[gimp-gap] movepath transformation support in storyboard and modify frames



commit f67c8d085df325d6120bc37e7e93faa29760c5ab
Author: Wolfgang Hofer <wolfgangh svn gnome org>
Date:   Tue Apr 26 20:33:21 2011 +0200

    movepath transformation support in storyboard and modify frames

 ChangeLog                                  |   64 ++
 NEWS                                       |    8 +-
 docs/STORYBOARD_FILE_DOC.txt               |  711 ------------
 docs/reference/txt/STORYBOARD_FILE_DOC.txt |  247 ++++-
 gap/Makefile.am                            |   33 +-
 gap/gap_bluebox.h                          |    5 +-
 gap/gap_main.c                             |  377 +-------
 gap/gap_mov_dialog.c                       |  465 +++++++--
 gap/gap_mov_dialog.h                       |   64 +-
 gap/gap_mov_exec.c                         | 1386 +++++++++++++++++++----
 gap/gap_mov_exec.h                         |   23 +
 gap/gap_mov_main.c                         |  713 ++++++++++++
 gap/gap_mov_render.c                       |  596 +++++++++-
 gap/gap_mov_xml_par.c                      | 1651 ++++++++++++++++++++++++++++
 gap/gap_mov_xml_par.h                      |   52 +
 gap/gap_story_att_trans_dlg.c              |  756 ++++++++++++-
 gap/gap_story_file.c                       |  145 +++-
 gap/gap_story_file.h                       |    5 +-
 gap/gap_story_main.h                       |    3 +
 gap/gap_story_properties.c                 |   26 +-
 gap/gap_story_render_lossless.c            |   83 +-
 gap/gap_story_render_processor.c           | 1274 ++++++++++++++++------
 gap/gap_story_render_processor.h           |   18 +-
 gap/gap_story_render_types.h               |   11 +
 gap/gap_story_syntax.c                     |    9 +
 gap/gap_story_syntax.h                     |    1 +
 gap/gap_story_vthumb.c                     |    5 +-
 gap/gap_xml_util.c                         |  347 ++++++
 gap/gap_xml_util.h                         |   56 +
 po/POTFILES.in                             |    1 +
 30 files changed, 7188 insertions(+), 1947 deletions(-)
---
diff --git a/ChangeLog b/ChangeLog
index ebfd90e..eaea9f0 100755
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,69 @@
 2011-04-26 Wolfgang Hofer <hof gimp org>
 
+- Storyboard render processor now supports processing
+  of new transition attribute record VID_MOVE_PATH
+   (based on the new singleframe mode of the move path feature)
+
+- Move Path now supports save of all settings (controlpoints and other relevant settings)
+  using a new XML stuctured fileformat.
+  Note that the old controlpoint file format is still supported both for load and save.
+  The new format is selected implicite by finename extension .xml 
+
+- New plug-in to call the MovePath for rendering only one frame where the
+  moving object is an already existing layer of the frame and all parameters
+  are provided via xml parameterfile
+  (this new feature is intended to be called as filter with the modify frames
+  feature)
+
+- The MovePath feature is still limited to process image types RGB 
+  (both for the frame and the moving object)
+  Therefore the PDB requstration was changed  
+   from "RGB*, INDEXED*, GRAY*"
+   to   "RGB*"
+
+  to disable the MovePath feature on unsupported frame types. 
+  (see also remarks at bugzilla #645303)
+  
+  
+- the MovePath feature now is built as separate plug-in with its own main
+  procedure to handle run and query.
+  
+- added a new gimprc parameter that enables logging of the
+  relevant render parameters each time the movePath feature
+  renders a frame. This parameter defaults to "no" in productive environment
+  and is intended for debug and analyse purpose.
+  This logging was used as base for regression tests on the current changes.
+  
+  (video-move-path-log-render-params "yes")
+
+
+ * NEWS  
+ * po/POTFILES.in
+ * gap/Makefile.am
+ * gap/gap_bluebox.h
+ * gap/gap_main.c
+ * gap/gap_mov_dialog.c  [.h]
+ * gap/gap_mov_exec.c    [.h]
+ * gap/gap_mov_main.c          # NEW FILE   
+ * gap/gap_mov_render.c  [.h]
+ * gap/gap_mov_xml_par.c [.h]  # NEW FILES    
+ * gap/gap_xml_util.c    [.h]  # NEW FILES    
+
+ * gap/gap_story_main.h
+ * gap/gap_story_file.c   [.h]
+ * gap/gap_story_render_processor.c [.h]
+ * gap/gap_story_render_lossless.c
+ * gap/gap_story_render_types.h
+ * gap/gap_story_syntax.c  [.h]
+ * gap/gap_story_vthumb.c
+ * gap/gap_story_properties.c
+ 
+ * gap/gap_story_att_trans_dlg.c
+ * docs/reference/txt/STORYBOARD_FILE_DOC.txt
+
+ 
+2011-04-26 Wolfgang Hofer <hof gimp org>
+
 - applied patch that fixes string typos provided by Christian Kirbach at #648607
 
  * gap/gap_morph_tween_dialog.c
diff --git a/NEWS b/NEWS
index e7f324d..fe02224 100644
--- a/NEWS
+++ b/NEWS
@@ -18,6 +18,10 @@ Here is a short overview whats new in GIMP-GAP-2.7.0:
   - support to run gimp_color_balance tool as animated filter 
     (added wrapper plug-in).
 
+  - new plug-in to apply the MovePath functionality 
+    (transitions and move object along path)
+    in a "process one frame per call" style, intended to be called as filter
+    with the modify frames feature.
 
 - GIMP-GAP now supports speed control of movements and other transitions
   via Acceleration characteristic presets. Those presets are available
@@ -32,7 +36,9 @@ Here is a short overview whats new in GIMP-GAP-2.7.0:
     (that replaces the "Apply Varying" button of older GIMP_GAP releases)
 
 
-- The Storyboard now supports rotatation of the processed clips by any angle.
+- The Storyboard now supports rotatation of the processed clips by any angle
+  and more complex transistions similar to the move path feature
+  (based on settings saved with the Movepath dialog as xml parameter file)
 
 - A new storyboard processing feature allows adding external transparency 
   for clip type movie. This is done via format string that refers to
diff --git a/docs/reference/txt/STORYBOARD_FILE_DOC.txt b/docs/reference/txt/STORYBOARD_FILE_DOC.txt
index 9189749..2f97d75 100644
--- a/docs/reference/txt/STORYBOARD_FILE_DOC.txt
+++ b/docs/reference/txt/STORYBOARD_FILE_DOC.txt
@@ -1,4 +1,4 @@
-STORYBOARD_FILES           2010.11.02:
+STORYBOARD_FILES           2011.04.14:
 
 
 General
@@ -539,7 +539,7 @@ VID_SILENCE
                             DEFAULT: 0
 
 VID_ROTATE
-  This record is used to define rotate treansitions,
+  This record is used to define rotate transitions,
   by changing the rotation angle of the processed frames in the specified Videotrack slightly from one value
   to another.
   
@@ -695,8 +695,13 @@ VID_MOVE_X and VID_MOVE_Y
 
 
 VID_FIT_SIZE
-  Define how to match input framesize with the size of
-  the resulting video (VID_MASTER_SIZE).
+  Define how to match input framesize with the target size.
+  The target size is typically the size of the resulting video (VID_MASTER_SIZE).
+  except in scenarios where a movepath transistion is active.
+  In this special case the input frame is processed as moving object
+  of this transition and the target size refers to the scaled size 
+  (preScaleWidth, preScaleHeight) of the recorded moving object.
+  
 
   Notes:
   - The Input Frame is placed in the center of the 
@@ -705,6 +710,29 @@ VID_FIT_SIZE
   - Zooming may cause additional Scaling. The VID_FIT_SIZE record
     describes the normal Size (where zoom is set to 1.0)
 
+  - In case the input Frame is processed by a movepath transistion
+    the target size depends on the size of the moving object and the framesize
+    in the xml_paramfile and on the VID_MASTER_SIZE.
+    (see VID_MOVE_PATH below)
+        
+    Example:
+    in the xml_paramfile the frame size 640x400 pixels and the 
+    moving object size was 320x240 pixels.
+    
+       <frame_description width="640" height="400"
+       <moving_object     width="320" height="240" 
+
+    The actual rendering shall be done at double frame size VID_MASTER_SIZE 1280 x 800 pixels.
+    In this example the target size of the pre-scaled moving object is 640 x 480 pixels
+    
+    target size calculation:
+       preScaleWidth  = recordedMovingObjWidth * masterWidth / recordedFrameWidth
+           640        = 320  * 1280  / 640
+       preScaleHeight = recordedMovingObjHeight * masterHeight / recordedFrameWidth
+           480        = 240  * 800   / 400
+    
+
+
   - WARNING: settings that disable scaling 
     (e.g. use fixed width or height at original source image size)
     do NOT follow changes of the resulting VID_MASTER_SIZE.
@@ -727,9 +755,15 @@ VID_FIT_SIZE
   (2) track             ... integer tracknumer
   [3] mode              ... One of the Keywords "width" "height" "none" or "both"
                             width:  Scale that only width does exactly
-                                    fit to the resulting video, height is unchanged
+                                    fit to the resulting video,
+                                    height is unchanged original input frame height,
+                                    or adjusted to keep the proportions of the input frame
+                                    (depends on the proportions setting)
                             height: Scale that only height does exactly
-                                    fit to the resulting video, width  is unchanged
+                                    fit to the resulting video,
+                                    width  is unchanged original input frame width,
+                                    or adjusted to keep the proportions of the input frame
+                                    (depends on the proportions setting)
                             both:   Scale that both width and height do exactly
                                     fit to the resulting video 
                             none:   Do not Scale the input frame at all
@@ -747,11 +781,182 @@ VID_FIT_SIZE
                                     background if there no such frames.
                             change_proportions:
                                     allow proportion changes at scaling.
-                                    Stretch the image to fit the resulting
-                                    video.
                                     
                             DEFAULT: "change_proportions"
 
+
+   This Example shows how an Input Frame of size 400x400 pixel is automatically scaled
+   to fit into a video at size 1280 x 800
+
+       +------+
+       |######|   Input Frame width:      400
+       |######|   Input Frame height:     400
+       |######|
+       |######|
+       +------+
+ 
+ 
+  ==== scenarios that allow changing proportions of the moving object =====
+ 
+  o) VID_FIT_SIZE mode=both proportions=change_proportions
+ 
+    +-------------------------------------+
+    |#####################################|  scaled copy of input frame:  1280 x 800
+    |#####################################|  video size:                  1280 x 800                  
+    |#####################################| 
+    |#####################################|
+    |#####################################|
+    |#####################################|
+    |#####################################|     
+    |#####################################|
+    |#####################################|
+    |#####################################|
+    |#####################################|
+    |#####################################|
+    +-------------------------------------+
+
+  o) VID_FIT_SIZE mode=width proportions=change_proportions
+ 
+    +-------------------------------------+
+    |                                     |  scaled copy of input frame:  1280 x 400
+    |                                     |  video size:                  1280 x 800                  
+    |                                     | 
+    +-------------------------------------+
+    |#####################################|
+    |#####################################|
+    |#####################################|     
+    |#####################################|
+    +-------------------------------------+
+    |                                     |
+    |                                     |
+    |                                     |
+    +-------------------------------------+
+ 
+
+  o) VID_FIT_SIZE mode=height proportions=change_proportions
+ 
+    +--------------+------+---------------+
+    |              |######|               |  scaled copy of input frame:   400 x 800
+    |              |######|               |  video size:                  1280 x 800                  
+    |              |######|               | 
+    |              |######|               | 
+    |              |######|               | 
+    |              |######|               | 
+    |              |######|               | 
+    |              |######|               | 
+    |              |######|               | 
+    |              |######|               | 
+    |              |######|               | 
+    |              |######|               | 
+    +--------------+------+---------------+
+
+ 
+  ==== scenarios that keep proportions of the moving object =====
+  
+  o) VID_FIT_SIZE mode=both proportions=keep_proportions
+  
+     The input frame is scaled to fit into video size rectangle
+     since the input frame has different proportions than the video size
+     there will be transparent borders. (where the black background
+     or clips of other tracks on lower stack position shows through)
+ 
+    +-------+---------------------+-------+
+    |       |#####################|       |  scaled copy of input frame:   800 x 800
+    |       |#####################|       |  video size:                  1280 x 800                  
+    |       |#####################|       | 
+    |       |#####################|       | 
+    |       |#####################|       | 
+    |       |#####################|       | 
+    |       |#####################|       | 
+    |       |#####################|       | 
+    |       |#####################|       | 
+    |       |#####################|       | 
+    |       |#####################|       | 
+    |       |#####################|       | 
+    +-------+---------------------+-------+
+
+ 
+  o) VID_FIT_SIZE mode=width proportions=keep_proportions
+    
+    The input frame is scaled to width 1280 pixels
+    and height 1280 pixels to keep the proportions.
+    But this cuts off the parts that do not fit into
+    the video height of 800 pixels.
+    
+
+
+    +-------------------------------------+
+    |.....................................| 
+    |.....................................| 
+    +-------------------------------------+
+    |#####################################|  scaled copy of input frame:  1280 x 1280
+    |#####################################|  video size:                  1280 x  800                  
+    |#####################################| 
+    |#####################################|
+    |#####################################|
+    |#####################################|
+    |#####################################|     
+    |#####################################|
+    |#####################################|
+    |#####################################|
+    |#####################################|
+    |#####################################|
+    +-------------------------------------+
+    |.....................................| 
+    |.....................................| 
+    +-------------------------------------+
+
+  o) VID_FIT_SIZE mode=height proportions=keep_proportions
+
+     The input frame is scaled to height 800 pixels.
+     keeping the proportions of the input frame results in width of 800 pixels
+     that gives transparent stripes left and right.
+     
+ 
+    +-------+---------------------+-------+
+    |       |#####################|       |  scaled copy of input frame:   800 x 800
+    |       |#####################|       |  video size:                  1280 x 800                  
+    |       |#####################|       | 
+    |       |#####################|       | 
+    |       |#####################|       | 
+    |       |#####################|       | 
+    |       |#####################|       | 
+    |       |#####################|       | 
+    |       |#####################|       | 
+    |       |#####################|       | 
+    |       |#####################|       | 
+    |       |#####################|       | 
+    +-------+---------------------+-------+
+
+
+
+  o) VID_FIT_SIZE mode=none
+   
+    No automatic scaling is done to fit into video size.
+    (the proportions seting is not relevant in this case)
+ 
+    +-------------------------------------+
+    |                                     |  scaled copy of input frame:   400 x 400
+    |                                     |  video size:                  1280 x 800                  
+    |                                     | 
+    |              +------+               |
+    |              |######|               |
+    |              |######|               |
+    |              |######|               |     
+    |              |######|               |
+    |              +------+               |
+    |                                     |
+    |                                     |
+    |                                     |
+    +-------------------------------------+
+ 
+
+  
+
+
+
+
+
 VID_OVERLAP
   Define overlapping frames within one track.
   The specified number of frames will overlap previous frames of the same track.
@@ -800,6 +1005,32 @@ VID_OVERLAP
   00195        B.mpg 000100 
 
 
+      
+VID_MOVE_PATH
+  This record is used to define a set of complex transformations on the current video track (layer)
+  Those transformations can include movement along a path, scaling, rotation, perspective transformation,
+  opacity changes ... (see the GIMP-GAP MovePath feature for more details)
+  
+  to another.
+  
+  (1) Record Key        ... VID_MOVE_PATH
+  (2) track             ... integer tracknumer
+  (3) frame_from        ... Start frame number specifies the phase
+                            where to start in the move path (typically start at 1)
+  (4) frame_to          ... End frame number  specifies the phase
+                            where to end in the move path
+  (5) nframes           ... duration of the effect
+                            in number of frames (integer)
+                            In case nframes is greater than total_frames
+                            the transistions of the end
+                            The duration Value 0 disables move transitions.
+  (6) accel             ... an integer value specifiying acceleration characteristic
+                            DEFAULT: 0  ** other values are currently ignored 
+                            
+  (7) xml_paramfile     ... name of the paramterfile for the move path plugin.
+
+
+
 AUD_PLAY_SOUND
   This record is used for playback of portions of an audiofile,
   with optional fade effects. Please note that all audiotracks
diff --git a/gap/Makefile.am b/gap/Makefile.am
old mode 100644
new mode 100755
index 4eedebb..26620f1
--- a/gap/Makefile.am
+++ b/gap/Makefile.am
@@ -79,13 +79,28 @@ BASE_SOURCES = \
 	gap_timeconv.h		\
 	gap_stock.c		\
 	gap_stock.h		\
+	gap_xml_util.c		\
+	gap_xml_util.h		\
 	gap_vin.c		\
 	gap_vin.h
 
+
+MOVEPATH_SOURCES = \
+	gap_bluebox.c		\
+	gap_bluebox.h		\
+	gap_mov_dialog.c	\
+	gap_mov_dialog.h	\
+	gap_mov_exec.c		\
+	gap_mov_exec.h		\
+	gap_mov_render.c	\
+	gap_mov_render.h	\
+	gap_mov_xml_par.c	\
+	gap_mov_xml_par.h
+
 libgimpgap_a_SOURCES = $(BASE_SOURCES)
 
 
-libgapstory_a_SOURCES = $(BASE_SOURCES)	\
+libgapstory_a_SOURCES = $(BASE_SOURCES)	$(MOVEPATH_SOURCES) \
 	gap_frame_fetcher.c		\
 	gap_frame_fetcher.h		\
 	gap_fmac_name.c		\
@@ -108,6 +123,7 @@ libexec_PROGRAMS = \
 	gap_bluebox		\
 	gap_colormask		\
 	gap_plugins		\
+	gap_movepath		\
 	gap_filter		\
 	gap_fmac		\
 	gap_fmac_varying	\
@@ -164,12 +180,6 @@ gap_plugins_SOURCES = \
 	gap_mod_layer.h		\
 	gap_mod_layer_dialog.c	\
 	gap_mod_layer_dialog.h	\
-	gap_mov_dialog.c	\
-	gap_mov_dialog.h	\
-	gap_mov_exec.c		\
-	gap_mov_exec.h		\
-	gap_mov_render.c	\
-	gap_mov_render.h	\
 	gap_navi_activtable.c	\
 	gap_navi_activtable.h	\
 	gap_range_ops.c		\
@@ -180,6 +190,14 @@ gap_plugins_SOURCES = \
 	gap_split.h		\
 	gap_libgimpgap.h	
 
+gap_movepath_SOURCES =	$(MOVEPATH_SOURCES) \
+	gap_base_ops.c		\
+	gap_base_ops.h		\
+	gap_mov_main.c		\
+	gap_lastvaldesc.c	\
+	gap_lastvaldesc.h	\
+	gap_libgimpgap.h	
+
 gap_filter_SOURCES = \
 	gap_dbbrowser_utils.c	\
 	gap_dbbrowser_utils.h	\
@@ -438,6 +456,7 @@ LDADD = $(GIMP_LIBS)
 
 
 gap_plugins_LDADD =          $(LIBGIMPGAP)  $(LIBGAPBASE) $(GIMP_LIBS)
+gap_movepath_LDADD =         $(LIBGIMPGAP)  $(LIBGAPBASE) $(GIMP_LIBS)
 gap_bluebox_LDADD =          $(LIBGIMPGAP)  $(LIBGAPBASE) $(GIMP_LIBS)
 gap_colormask_LDADD =        $(LIBGIMPGAP)  $(LIBGAPBASE) $(GIMP_LIBS)
 gap_filter_LDADD =           $(GAPVIDEOAPI) $(LIBGIMPGAP)  $(LIBGAPBASE) $(GIMP_LIBS)
diff --git a/gap/gap_bluebox.h b/gap/gap_bluebox.h
index 6e03598..8611535 100644
--- a/gap/gap_bluebox.h
+++ b/gap/gap_bluebox.h
@@ -32,6 +32,10 @@
 #define GAP_BLUEBOX_DATA_KEY_VALS  "plug_in_bluebox"
 #define GAP_BLUEBOX_HELP_ID        "plug-in-bluebox"
 
+
+#include <gtk/gtk.h>
+#include "libgimp/gimp.h"
+
 typedef enum
 {
    GAP_BLUBOX_THRES_RGB        
@@ -40,7 +44,6 @@ typedef enum
   ,GAP_BLUBOX_THRES_ALL
 } GapBlueboxThresMode;
 
-#include "libgimp/gimp.h"
 typedef struct GapBlueboxVals {
   GimpRGB               keycolor;
   GapBlueboxThresMode   thres_mode;
diff --git a/gap/gap_main.c b/gap/gap_main.c
index 75f267a..c271c5d 100644
--- a/gap/gap_main.c
+++ b/gap/gap_main.c
@@ -39,6 +39,7 @@
  */
 
 /* revision history:
+ *                  2011/03/09  hof: - moved code for Move Path features to gap_mov_main.c module
  * gimp    2.1.0a;  2004/04/05  hof: - Move Path added option to keep the original paintmode of the src_layer
  * gimp    1.3.24a; 2004/01/17  hof: - get main version from config.h, fixed PDB docs for plug_in_gap_modify
  * gimp    1.3.23b; 2003/12/06  hof: - updated main version
@@ -116,7 +117,6 @@
 #include "gap_match.h"
 #include "gap_range_ops.h"
 #include "gap_split.h"
-#include "gap_mov_exec.h"
 #include "gap_mod_layer.h"
 #include "gap_arr_dialog.h"
 #include "gap_pdb_calls.h"
@@ -145,9 +145,6 @@ int gap_debug = 0;
 #define PLUGIN_NAME_GAP_DUP                  "plug_in_gap_dup"
 #define PLUGIN_NAME_GAP_DENSITY              "plug_in_gap_density"
 #define PLUGIN_NAME_GAP_EXCHG                "plug_in_gap_exchg"
-#define PLUGIN_NAME_GAP_MOVE                 "plug_in_gap_move"
-#define PLUGIN_NAME_GAP_MOVE_PATH_EXT        "plug_in_gap_move_path_ext"
-#define PLUGIN_NAME_GAP_MOVE_PATH_EXT2       "plug_in_gap_move_path_ext2"
 #define PLUGIN_NAME_GAP_RANGE_TO_MULTILAYER  "plug_in_gap_range_to_multilayer"
 #define PLUGIN_NAME_GAP_RANGE_FLATTEN        "plug_in_gap_range_flatten"
 #define PLUGIN_NAME_GAP_RANGE_LAYER_DEL      "plug_in_gap_range_layer_del"
@@ -255,138 +252,6 @@ GimpPlugInInfo PLUG_IN_INFO =
   };
   static int nargs_exchg = G_N_ELEMENTS (args_exchg);
 
-  static GimpParamDef args_mov[] =
-  {
-    {GIMP_PDB_INT32, "run_mode", "Interactive"},
-    {GIMP_PDB_IMAGE, "image", "Input image (one of the video frames)"},
-    {GIMP_PDB_DRAWABLE, "drawable", "Input drawable (unused)"},
-  };
-
-  static GimpParamDef args_mov_path_ext[] =
-  {
-    {GIMP_PDB_INT32,        "run_mode",   "non-interactive"},
-    {GIMP_PDB_IMAGE,        "dst_image",  "Destination image (one of the video frames), where to insert the animated source layers"},
-    {GIMP_PDB_DRAWABLE,     "drawable",   "drawable (unused)"},
-    {GIMP_PDB_INT32,        "range_from", "destination frame nr to start"},
-    {GIMP_PDB_INT32,        "range_to",   "destination frame nr to stop (can be lower than range_from)"},
-    {GIMP_PDB_INT32,        "nr",         "layerstack position where to insert source layer (0 == on top)"},
-    /* source specs */
-    { GIMP_PDB_LAYER,      "src_layer_id",      "starting LayerID of SourceObject. (use any Multilayeranimated Image, or a video frame of anoter Animation)"},
-    { GIMP_PDB_INT32,      "src_stepmode",      "0-5     derive inserted object as copy of one layer from a multilayer src_image \n"
-                                                "100-105 derive inserted object as copy of merged visible layers of a source video frame \n"
-                                                "0:  Layer Loop  1: Layer Loop reverse  2: Layer Once  3: Layer Once reverse  4: Layer PingPong \n"
-                                                "5: None (use onle the selected src_layer)\n"
-                                                "100: Frame Loop  101: Frame Loop reverse  102: Frame Once  103: Frame Once reverse  104: Frame PingPong \n"
-                                                "105: Frame None (use onle the flat copy of the selected frame)\n"
-                                                },
-    { GIMP_PDB_INT32,      "src_handle",        "0: handle left top   1: handle left bottom \n"
-                                                "2: handle right top  3: handle right bottom \n"
-                                                "4: handle center"},
-    { GIMP_PDB_INT32,      "src_paintmode",     "4444: keep original paintmode of src_layer 0: GIMP_NORMAL_MODE (see GimpLayerModeEffects -- libgimp/gimpenums.h -- for more information)"},
-    { GIMP_PDB_INT32,      "src_force_visible", "1: Set inserted layres visible, 0: insert layers as is"},
-    { GIMP_PDB_INT32,      "clip_to_img",       "1: Clip inserted layers to Image size of the destination video frame, 0: dont clip"},
-    /* extras */
-    { GIMP_PDB_INT32,      "rotation_follow",   "0: NO automatic calculation (use the rotation array parameters as it is) \n"
-                                                "1: Automatic calculation of rotation, following the path vectors, (Ignore rotation array parameters)\n"},
-    { GIMP_PDB_FLOAT,      "startangle",        "start angle for the first contolpoint (only used if rotation-follow is on)"},
-
-    /* new features of the _ext[ended] API */
-    {GIMP_PDB_FLOAT,        "step_speed_factor",       "Allows stepping Source and Destination at different speed. (0.1 upto 50 where 1.0 does step snychron, 2.0 Src makes 2 Steps while Destination makes 1 step) "},
-    {GIMP_PDB_INT32,        "tween_steps",             "0 upto 50, Number of virtual Frames to calculate between 2 destination Frames. (use value 0 if no tween processing should be done)"},
-    {GIMP_PDB_FLOAT,        "tween_opacity_initial",   "opacity 0.0 upto 100.0 for the tween step that is nearest to the (next) real frame"},
-    {GIMP_PDB_FLOAT,        "tween_opacity_desc",      "descending opacity 0.0 upto 100.0  for the othertween steps"},
-    {GIMP_PDB_INT32,        "tracelayer_enable",       "TRUE: calculate a tracelayer (with all steps of the moving objects since first step)"},
-    {GIMP_PDB_FLOAT,        "trace_opacity_initial",   "opacity 0.0 upto 100.0 for the nearest tracestep to the actual destination frame"},
-    {GIMP_PDB_FLOAT,        "trace_opacity_desc",      "descending opacity 0.0 upto 100.0 for fading out older positions (that are done before the actual step)"},
-    {GIMP_PDB_INT32,        "apply_bluebox",           "TRUE: apply blubox filter (using bluebox param VALUES of last successful bluebox run)"},
-    {GIMP_PDB_INT32,        "src_selmode",      "0: ignore selections in all source images\n"
-                                                "1: use one selection (from the inital source image) for all handled src layers \n"
-                                                "2: use selections in all source images (for stepmodes 0-5 there is only one source image)"},
-
-
-    /* CONTROLPOINT Arrays (the _ext API uses FLOAT arrays for more precision) */
-    { GIMP_PDB_INT32,      "argc_p_x",          "number of controlpoints"},
-    { GIMP_PDB_INT32ARRAY, "p_x",               "Controlpoint x-koordinate"},
-    { GIMP_PDB_INT32,      "argc_p_y",          "number of controlpoints"},
-    { GIMP_PDB_INT32ARRAY, "p_y",               "Controlpoint y-koordinate"},
-    { GIMP_PDB_INT32,      "argc_opacity",      "number of controlpoints"},
-    { GIMP_PDB_FLOATARRAY, "opacity",           "Controlpoint opacity value 0 <= value <= 100"},
-    { GIMP_PDB_INT32,      "argc_w_resize",     "number of controlpoints"},
-    { GIMP_PDB_FLOATARRAY, "w_resize",          "width scaling in percent"},
-    { GIMP_PDB_INT32,      "argc_h_resize",     "number of controlpoints"},
-    { GIMP_PDB_FLOATARRAY, "h_resize",          "height scaling in percent"},
-    { GIMP_PDB_INT32,      "argc_rotation",     "number of controlpoints"},
-    { GIMP_PDB_FLOATARRAY, "rotation",          "rotation in degrees"},
-    { GIMP_PDB_INT32,      "argc_keyframe_abs", "number of controlpoints"},
-    { GIMP_PDB_INT32ARRAY, "keyframe_abs",      "n: fix controlpoint to this frame number, 0: for controlpoints that are not fixed to a frame."},
-
-    /* new CONTROLPOINT ARRAY items of the _ext[ended] API */
-    { GIMP_PDB_INT32,      "argc_ttlx",     "number of controlpoints"},
-    { GIMP_PDB_FLOATARRAY, "ttlx",          "perspective transformfactor for top left X Coordinate (0.0 upto 5.0, value 1.0 does no trasformation)"},
-    { GIMP_PDB_INT32,      "argc_ttly",     "number of controlpoints"},
-    { GIMP_PDB_FLOATARRAY, "ttly",          "perspective transformfactor for top left Y Coordinate (0.0 upto 5.0, value 1.0 does no trasformation)"},
-    { GIMP_PDB_INT32,      "argc_ttrx",     "number of controlpoints"},
-    { GIMP_PDB_FLOATARRAY, "ttrx",          "perspective transformfactor for top right X Coordinate (0.0 upto 5.0, value 1.0 does no trasformation)"},
-    { GIMP_PDB_INT32,      "argc_ttry",     "number of controlpoints"},
-    { GIMP_PDB_FLOATARRAY, "ttry",          "perspective transformfactor for top right Y Coordinate (0.0 upto 5.0, value 1.0 does no trasformation)"},
-    { GIMP_PDB_INT32,      "argc_tblx",     "number of controlpoints"},
-    { GIMP_PDB_FLOATARRAY, "tblx",          "perspective transformfactor for bottom left X Coordinate (0.0 upto 5.0, value 1.0 does no trasformation)"},
-    { GIMP_PDB_INT32,      "argc_tbly",     "number of controlpoints"},
-    { GIMP_PDB_FLOATARRAY, "tbly",          "perspective transformfactor for bottom left Y Coordinate (0.0 upto 5.0, value 1.0 does no trasformation)"},
-    { GIMP_PDB_INT32,      "argc_tbrx",     "number of controlpoints"},
-    { GIMP_PDB_FLOATARRAY, "tbrx",          "perspective transformfactor for bottom right X Coordinate (0.0 upto 5.0, value 1.0 does no trasformation)"},
-    { GIMP_PDB_INT32,      "argc_tbry",     "number of controlpoints"},
-    { GIMP_PDB_FLOATARRAY, "tbry",          "perspective transformfactor for bottom right Y Coordinate (0.0 upto 5.0, value 1.0 does no trasformation)"},
-    { GIMP_PDB_INT32,      "argc_sel",      "number of controlpoints"},
-    { GIMP_PDB_FLOATARRAY, "sel_feather_radius", "feather radius for selections"},
-  };
-  static int nargs_mov_path_ext = G_N_ELEMENTS (args_mov_path_ext);
-
-  static GimpParamDef args_mov_path_ext2[] =
-  {
-    {GIMP_PDB_INT32,        "run_mode",   "non-interactive"},
-    {GIMP_PDB_IMAGE,        "dst_image",  "Destination image (one of the video frames), where to insert the animated source layers"},
-    {GIMP_PDB_DRAWABLE,     "drawable",   "drawable (unused)"},
-    {GIMP_PDB_INT32,        "range_from", "destination frame nr to start"},
-    {GIMP_PDB_INT32,        "range_to",   "destination frame nr to stop (can be lower than range_from)"},
-    {GIMP_PDB_INT32,        "nr",         "layerstack position where to insert source layer (0 == on top)"},
-    /* source specs */
-    { GIMP_PDB_LAYER,      "src_layer_id",      "starting LayerID of SourceObject. (use any Multilayeranimated Image, or an video frame of anoter Animation)"},
-    { GIMP_PDB_INT32,      "src_stepmode",      "0-5     derive inserted object as copy of one layer from a multilayer src_image \n"
-                                                "100-105 derive inserted object as copy of merged visible layers of a source video frame \n"
-                                                "0:  Layer Loop  1: Layer Loop reverse  2: Layer Once  3: Layer Once reverse  4: Layer PingPong \n"
-                                                "5: None (use onle the selected src_layer)\n"
-                                                "100: Frame Loop  101: Frame Loop reverse  102: Frame Once  103: Frame Once reverse  104: Frame PingPong \n"
-                                                "105: Frame None (use onle the flat copy of the selected frame)\n"
-                                                },
-    { GIMP_PDB_INT32,      "src_handle",        "0: handle left top   1: handle left bottom \n"
-                                                "2: handle right top  3: handle right bottom \n"
-                                                "4: handle center"},
-    { GIMP_PDB_INT32,      "src_paintmode",     "4444: keep original paintmode of src_layer 0: GIMP_NORMAL_MODE (see GimpLayerModeEffects -- libgimp/gimpenums.h -- for more information)"},
-    { GIMP_PDB_INT32,      "src_force_visible", "1: Set inserted layres visible, 0: insert layers as is"},
-    { GIMP_PDB_INT32,      "clip_to_img",       "1: Clip inserted layers to Image size of the destination video frame, 0: dont clip"},
-    /* extras */
-    { GIMP_PDB_INT32,      "rotation_follow",   "0: NO automatic calculation (use the rotation array parameters as it is) \n"
-                                                "1: Automatic calculation of rotation, following the path vectors, (Ignore rotation array parameters)\n"},
-    { GIMP_PDB_FLOAT,      "startangle",        "start angle for the first contolpoint (only used if rotation-follow is on)"},
-
-    /* new features of the _ext[ended] API */
-    {GIMP_PDB_FLOAT,        "step_speed_factor",       "Allows stepping Source and Destination at different speed. (0.1 upto 50 where 1.0 does step snychron, 2.0 Src makes 2 Steps while Destination makes 1 step) "},
-    {GIMP_PDB_INT32,        "tween_steps",             "0 upto 50, Number of virtual Frames to calculate between 2 destination Frames. (use value 0 if no tween processing should be done)"},
-    {GIMP_PDB_FLOAT,        "tween_opacity_initial",   "opacity 0.0 upto 100.0 for the tween step that is nearest to the (next) real frame"},
-    {GIMP_PDB_FLOAT,        "tween_opacity_desc",      "descending opacity 0.0 upto 100.0  for the othertween steps"},
-    {GIMP_PDB_INT32,        "tracelayer_enable",       "TRUE: calculate a tracelayer (with all steps of the moving objects since first step)"},
-    {GIMP_PDB_FLOAT,        "trace_opacity_initial",   "opacity 0.0 upto 100.0 for the nearest tracestep to the actual destination frame"},
-    {GIMP_PDB_FLOAT,        "trace_opacity_desc",      "descending opacity 0.0 upto 100.0 for fading out older positions (that are done before the actual step)"},
-    {GIMP_PDB_INT32,        "apply_bluebox",           "TRUE: apply blubox filter (using bluebox param VALUES of last successful bluebox run)"},
-
-    /* CONTROLPOINTs from file */
-    { GIMP_PDB_STRING,     "pointfile",         "a file with contolpoints (readable text file with one line per controlpoint)"},
-  };
-  static int nargs_mov_path_ext2 = G_N_ELEMENTS (args_mov_path_ext2);
-
-
-
   static GimpParamDef args_f2multi[] =
   {
     {GIMP_PDB_INT32, "run_mode", "Interactive, non-interactive"},
@@ -807,85 +672,6 @@ query ()
                          nargs_exchg, nreturn_std,
                          args_exchg, return_std);
 
-  gimp_install_procedure(PLUGIN_NAME_GAP_MOVE,
-                         "This plugin copies layer(s) from one sourceimage to multiple frames on disk, varying position, size and opacity.",
-                         "For NONINTERACTIVE PDB interfaces see also (plug_in_gap_move_path_ext, plug_in_gap_move_path_ext2)",
-                         "Wolfgang Hofer (hof gimp org)",
-                         "Wolfgang Hofer",
-                         GAP_VERSION_WITH_DATE,
-                         N_("Move Path..."),
-                         "RGB*, INDEXED*, GRAY*",
-                         GIMP_PLUGIN,
-                         G_N_ELEMENTS (args_mov), nreturn_std,
-                         args_mov, return_std);
-
-  l_help_str = g_strdup_printf(
-                         "This plugin inserts one layer in each frame of the selected frame range of an Animation\n"
-                         " (specified by the dst_image parameter).\n"
-                         " An Animation is a series of numbered video frame images on disk where only the current\n"
-                         " Frame is opened in the gimp\n"
-                         " The inserted layer is derived from another (multilayer)image\n"
-                         " or from another Animation (as merged copy of the visible layers in a source frame)\n"
-                         " the affected destination frame range is selected by the range_from and range_to parameters\n"
-                         " the src_stepmode parameter controls how to derive the layer that is to be inserted.\n"
-                         " With the Controlpoint Parameters you can control position (coordinates),\n"
-                         " size, rotation, perspective and opacity values of the inserted layer\n"
-                         " If you want to move an Object from position AX/AY to BX/BY in a straight line within the range of 24 frames\n"
-                         " you need 2 Contolpoints, if you want the object to move following a path\n"
-                         " you need some more Controlpoints to do that.\n"
-                         " With the rotation_follow Parameter you can force automatic calculation of the rotation for the inserted\n"
-                         " layer according to the path vectors it is moving along.\n"
-                         " A controlpoint can be fixed to a special framenumber using the keyframe_abs controlpoint-parameter.\n"
-                         " Restrictions:\n"
-                         " - keyframe_abs numbers must be 0 (== not fixed) or a frame_number within the affected frame range\n"
-                         " - keyframes_abs must be in sequence (ascending or descending)\n"
-                         " - the first and last controlpoint are always implicit keyframes, and should be passed with keyframe_abs = 0\n"
-                         " - the number of controlpoints is limited to a maximum of %d.\n"
-                         "   the number of controlpoints must be passed in all argc_* parameters\n"
-                         "If the TraceLayer feature is turned on, an additional layer\n"
-                         "  is inserted below the moving object. This Tracelayer shows all steps\n"
-                         "  of the moving object since the 1st Frame.\n"
-                         "With TweenSteps you can calculate virtual Frames between 2 destination frames\n"
-                         "  all these Steps are collected in another additional Layer.\n"
-                         "  this Tweenlayer is added below the moving Object in all handled destination Frames\n"
-                         "See also (plug_in_gap_move_path, plug_in_gap_move)",
-                         (int)GAP_MOV_MAX_POINT);
-
-  gimp_install_procedure(PLUGIN_NAME_GAP_MOVE_PATH_EXT,
-                         "This plugin copies layer(s) from one sourceimage or source animation to multiple frames on disk,\n"
-                         "with varying position, size, perspective and opacity.\n"
-                         ,
-                         l_help_str,
-                         "Wolfgang Hofer (hof gimp org)",
-                         "Wolfgang Hofer",
-                         GAP_VERSION_WITH_DATE,
-                         NULL,                      /* do not appear in menus */
-                         "RGB*, INDEXED*, GRAY*",
-                         GIMP_PLUGIN,
-                         nargs_mov_path_ext, nreturn_std,
-                         args_mov_path_ext, return_std);
-  g_free(l_help_str);
-
-  gimp_install_procedure(PLUGIN_NAME_GAP_MOVE_PATH_EXT2,
-                         "This plugin copies layer(s) from one sourceimage or source animation to multiple frames on disk,\n"
-                         "with varying position, size, perspective and opacity.\n"
-                         ,
-                         "This plugin is just another Interface for the MovePath (plug_in_gap_move_path_ext)\n"
-                         " using a File to specify Controlpoints (rather than Array parameters).\n"
-                         " Notes:\n"
-                         " - you can create a controlpoint file with in the MovePath Dialog (interactive call of plug_in_gap_move)\n"
-                         " - for more infos about controlpoints see help of (plug_in_gap_move_path)\n"
-                         ,
-                         "Wolfgang Hofer (hof gimp org)",
-                         "Wolfgang Hofer",
-                         GAP_VERSION_WITH_DATE,
-                         NULL,                      /* do not appear in menus */
-                         "RGB*, INDEXED*, GRAY*",
-                         GIMP_PLUGIN,
-                         nargs_mov_path_ext2, nreturn_std,
-                         args_mov_path_ext2, return_std);
-
-
 
   gimp_install_procedure(PLUGIN_NAME_GAP_RANGE_TO_MULTILAYER,
                          "This plugin creates a new image from the given range of frame-images. Each frame is converted to one layer in the new image, according to flatten_mode. (the frames on disk are not changed).",
@@ -1145,7 +931,6 @@ query ()
      gimp_plugin_menu_register (PLUGIN_NAME_GAP_DUP, menupath_image_video);
      gimp_plugin_menu_register (PLUGIN_NAME_GAP_DENSITY, menupath_image_video);
      gimp_plugin_menu_register (PLUGIN_NAME_GAP_EXCHG, menupath_image_video);
-     gimp_plugin_menu_register (PLUGIN_NAME_GAP_MOVE, menupath_image_video);
      gimp_plugin_menu_register (PLUGIN_NAME_GAP_RANGE_TO_MULTILAYER, menupath_image_video);
      gimp_plugin_menu_register (PLUGIN_NAME_GAP_RANGE_FLATTEN, menupath_image_video);
      gimp_plugin_menu_register (PLUGIN_NAME_GAP_RANGE_LAYER_DEL, menupath_image_video);
@@ -1537,166 +1322,6 @@ run (const gchar *name
         l_rc_image = gap_base_exchg(run_mode, image_id, nr);
       }
   }
-  else if (strcmp (name, PLUGIN_NAME_GAP_MOVE) == 0)
-  {
-      GapMovValues *pvals;
-
-      pvals = g_new (GapMovValues, 1);
-      if (run_mode == GIMP_RUN_NONINTERACTIVE)
-      {
-          status = GIMP_PDB_CALLING_ERROR;
-      }
-
-      if (status == GIMP_PDB_SUCCESS)
-      {
-        l_rc_image = gap_mov_exec_move_path(run_mode, image_id, pvals, NULL, 0, 0);
-      }
-      g_free(pvals);
-  }
-  else if ((strcmp (name, PLUGIN_NAME_GAP_MOVE_PATH_EXT) == 0)
-       ||  (strcmp (name, PLUGIN_NAME_GAP_MOVE_PATH_EXT2) == 0))
-  {
-      GapMovValues *pvals;
-      gchar        *pointfile;
-      gint          l_idx;
-      gint          l_numpoints;
-      gint          l_rotation_follow;
-      gint32        l_startangle;
-
-      pointfile = NULL;
-      pvals = g_new (GapMovValues, 1);
-      l_rotation_follow = 0;
-      l_startangle = 0;
-
-      pvals->dst_image_id = image_id;
-      pvals->tmp_image_id = -1;
-      pvals->tmpsel_image_id = -1;
-      pvals->tmpsel_channel_id = -1;
-      pvals->apv_mode = 0;
-      pvals->apv_src_frame = -1;
-      pvals->apv_mlayer_image =  -1;
-      pvals->apv_gap_paste_buff = NULL;
-      pvals->apv_framerate = 24;
-      pvals->apv_scalex = 100.0;
-      pvals->apv_scaley = 100.0;
-      pvals->cache_src_image_id = -1;
-      pvals->cache_tmp_image_id = -1;
-      pvals->cache_tmp_layer_id = -1;
-      pvals->cache_frame_number = -1;
-      pvals->cache_ainfo_ptr = NULL;
-      pvals->point_idx = 0;
-      pvals->point_idx_max = 0;
-      pvals->src_apply_bluebox  = 0;
-      pvals->bbp  = NULL;
-
-      pvals->step_speed_factor = 1.0;
-      pvals->tracelayer_enable = FALSE;
-      pvals->trace_opacity_initial = 100.0;
-      pvals->trace_opacity_desc = 80.0;
-      pvals->tween_steps = 0;
-      pvals->tween_opacity_initial = 80.0;
-      pvals->tween_opacity_desc = 80.0;
-
-      if (run_mode == GIMP_RUN_NONINTERACTIVE)
-      {
-        if ( ((n_params != nargs_mov_path_ext)  && (strcmp (name, PLUGIN_NAME_GAP_MOVE_PATH_EXT)  == 0))
-        ||   ((n_params != nargs_mov_path_ext2) && (strcmp (name, PLUGIN_NAME_GAP_MOVE_PATH_EXT2) == 0)))
-        {
-          status = GIMP_PDB_CALLING_ERROR;
-        }
-        else
-        {
-           pvals->dst_range_start   = param[3].data.d_int32;
-           pvals->dst_range_end     = param[4].data.d_int32;
-           pvals->dst_layerstack    = param[5].data.d_int32;
-
-           pvals->src_layer_id      = param[6].data.d_layer;
-           pvals->src_stepmode      = param[7].data.d_int32;
-           pvals->src_handle        = param[8].data.d_int32;
-           pvals->src_paintmode     = param[9].data.d_int32;
-           pvals->src_force_visible = param[10].data.d_int32;
-           pvals->clip_to_img       = param[11].data.d_int32;
-
-           l_rotation_follow        = param[12].data.d_int32;
-           l_startangle             = param[13].data.d_float;
-
-           pvals->step_speed_factor      = param[14].data.d_float;
-           pvals->tween_steps            = param[15].data.d_int32;
-           pvals->tween_opacity_initial  = param[16].data.d_float;
-           pvals->tween_opacity_desc     = param[17].data.d_float;
-           pvals->tracelayer_enable      = param[18].data.d_int32;
-           pvals->trace_opacity_initial  = param[19].data.d_float;
-           pvals->trace_opacity_desc     = param[20].data.d_float;
-           pvals->src_apply_bluebox      = param[21].data.d_int32;
-           pvals->src_selmode            = param[22].data.d_int32;
-
-           if (strcmp (name, PLUGIN_NAME_GAP_MOVE_PATH_EXT)  == 0)
-           {
-              /* PLUGIN_NAME_GAP_MOVE_PATH_EXT passes controlpoints as array parameters */
-              l_numpoints = param[23].data.d_int32;
-              if ((l_numpoints != param[25].data.d_int32)
-              ||  (l_numpoints != param[27].data.d_int32)
-              ||  (l_numpoints != param[29].data.d_int32)
-              ||  (l_numpoints != param[31].data.d_int32)
-              ||  (l_numpoints != param[33].data.d_int32)
-              ||  (l_numpoints != param[35].data.d_int32)
-              ||  (l_numpoints != param[37].data.d_int32)
-              ||  (l_numpoints != param[39].data.d_int32)
-              ||  (l_numpoints != param[41].data.d_int32)
-              ||  (l_numpoints != param[43].data.d_int32)
-              ||  (l_numpoints != param[45].data.d_int32)
-              ||  (l_numpoints != param[47].data.d_int32)
-              ||  (l_numpoints != param[49].data.d_int32)
-              ||  (l_numpoints != param[51].data.d_int32)
-              ||  (l_numpoints != param[53].data.d_int32))
-              {
-                printf("plug_in_gap_move_path_ext: CallingError: different numbers in the controlpoint array argc parameters\n");
-                status = GIMP_PDB_CALLING_ERROR;
-              }
-              else
-              {
-                pvals->point_idx_max = l_numpoints -1;
-                for(l_idx = 0; l_idx < l_numpoints; l_idx++)
-                {
-                   pvals->point[l_idx].p_x = param[24].data.d_int32array[l_idx];
-                   pvals->point[l_idx].p_y = param[26].data.d_int32array[l_idx];
-                   pvals->point[l_idx].opacity  = param[28].data.d_floatarray[l_idx];
-                   pvals->point[l_idx].w_resize = param[30].data.d_floatarray[l_idx];
-                   pvals->point[l_idx].h_resize = param[32].data.d_floatarray[l_idx];
-                   pvals->point[l_idx].rotation = param[34].data.d_floatarray[l_idx];
-                   pvals->point[l_idx].keyframe_abs = param[36].data.d_int32array[l_idx];
-                   /* pvals->point[l_idx].keyframe = ; */ /* relative keyframes are calculated later */
-                   pvals->point[l_idx].ttlx = param[38].data.d_floatarray[l_idx];
-                   pvals->point[l_idx].ttly = param[40].data.d_floatarray[l_idx];
-                   pvals->point[l_idx].ttrx = param[42].data.d_floatarray[l_idx];
-                   pvals->point[l_idx].ttry = param[44].data.d_floatarray[l_idx];
-                   pvals->point[l_idx].tblx = param[46].data.d_floatarray[l_idx];
-                   pvals->point[l_idx].tbly = param[48].data.d_floatarray[l_idx];
-                   pvals->point[l_idx].tbrx = param[50].data.d_floatarray[l_idx];
-                   pvals->point[l_idx].tbry = param[52].data.d_floatarray[l_idx];
-                   pvals->point[l_idx].sel_feather_radius = param[54].data.d_floatarray[l_idx];
-                }
-              }
-           }
-           else
-           {
-              /* PLUGIN_NAME_GAP_MOVE_PATH_EXT2 operates with controlpoint file */
-              if(param[23].data.d_string != NULL)
-              {
-                 pointfile = g_strdup(param[23].data.d_string);
-              }
-           }
-        }
-
-      }
-
-      if (status == GIMP_PDB_SUCCESS)
-      {
-        l_rc_image = gap_mov_exec_move_path(run_mode, image_id, pvals, pointfile, l_rotation_follow, (gdouble)l_startangle);
-      }
-      g_free(pvals);
-      if(pointfile != NULL) g_free(pointfile);
-  }
   else if (strcmp (name, PLUGIN_NAME_GAP_RANGE_TO_MULTILAYER) == 0)
   {
       *nreturn_vals = nreturn_f2multi + 1;
diff --git a/gap/gap_mov_dialog.c b/gap/gap_mov_dialog.c
index 035fd44..d674249 100644
--- a/gap/gap_mov_dialog.c
+++ b/gap/gap_mov_dialog.c
@@ -118,6 +118,7 @@
 #include "gap_lib.h"
 #include "gap_image.h"
 #include "gap_mov_exec.h"
+#include "gap_mov_xml_par.h"
 #include "gap_mov_dialog.h"
 #include "gap_mov_render.h"
 #include "gap_pdb_calls.h"
@@ -205,7 +206,20 @@ typedef struct
   GtkAdjustment *keyframe_adj;
   GtkAdjustment *preview_frame_nr_adj;
 
+  GimpColorButton  *bluebox_keycolor_color_button;
+  GtkAdjustment *dst_range_start_adj;
+  GtkAdjustment *dst_range_end_adj;
+  GtkAdjustment *dst_layerstack_adj;
+  GtkWidget     *src_force_visible_check_button;
+  GtkWidget     *clip_to_img_check_button;
+  GtkWidget     *tracelayer_enable_check_button;
+  GtkWidget     *src_apply_bluebox_check_button;
+  GtkWidget     *paintmode_combo;
+  GtkWidget     *stepmode_combo;
+  GtkWidget     *handlemode_combo;
+  GtkWidget     *src_selmode_combo;
   GtkWidget     *src_layer_combo;
+
   GtkWidget     *constrain;       /* scale width/height keeps ratio constant */
   GtkAdjustment *ttlx_adj;
   GtkAdjustment *ttly_adj;
@@ -222,7 +236,7 @@ typedef struct
   GtkAdjustment *trace_opacity_initial_adj;
   GtkAdjustment *trace_opacity_desc_adj;
   GtkAdjustment *tween_steps_adj;
-  
+
   GtkAdjustment *accPosition_adj;
   GtkAdjustment *accOpacity_adj;
   GtkAdjustment *accSize_adj;
@@ -276,12 +290,12 @@ typedef struct
   GdkCursor     *cursor_wait;
   GdkCursor     *cursor_acitve;
   GimpRGB               pathcolor;
-  
-  
+
+
   GtkWidget            *segNumberLabel;
   GtkWidget            *segLengthLabel;
   GtkWidget            *segSpeedLabel;
-  
+
 } t_mov_gui_stuff;
 
 
@@ -299,6 +313,7 @@ static void        p_pick_nearest_point      (gint px, gint py);
 static void        p_reset_points            ();
 static void        p_clear_one_point         (gint idx);
 static void        p_mix_one_point(gint idx, gint ref1, gint ref2, gdouble mix_factor);
+static void        p_refresh_widgets_after_load(t_mov_gui_stuff *mgp);
 static void        p_load_points             (char *filename);
 static void        p_save_points             (char *filename, t_mov_gui_stuff *mgp);
 
@@ -511,7 +526,22 @@ long      gap_mov_dlg_move_dialog    (GapMovData *mov_ptr)
   mgp->segNumberLabel = NULL;
   mgp->segLengthLabel = NULL;
   mgp->segSpeedLabel = NULL;
-  
+
+  mgp->dst_range_start_adj = NULL;
+  mgp->dst_range_end_adj = NULL;
+  mgp->dst_layerstack_adj = NULL;
+  mgp->src_force_visible_check_button = NULL;
+  mgp->clip_to_img_check_button = NULL;
+  mgp->tracelayer_enable_check_button = NULL;
+  mgp->src_apply_bluebox_check_button = NULL;
+  mgp->bluebox_keycolor_color_button = NULL;
+  mgp->paintmode_combo = NULL;
+  mgp->stepmode_combo = NULL;
+  mgp->handlemode_combo = NULL;
+  mgp->src_selmode_combo = NULL;
+  mgp->src_layer_combo = NULL;
+
+
   pvals = mov_ptr->val_ptr;
 
   l_str = gap_base_strdup_del_underscore(mov_ptr->dst_ainfo_ptr->basename);
@@ -1285,7 +1315,7 @@ mov_grab_bezier_path(t_mov_gui_stuff *mgp, gint32 vectors_id, gint32 stroke_id,
     gdouble  slope;
     gboolean valid;
     gboolean success;
-    
+
 
     success = gimp_vectors_stroke_get_point_at_dist(vectors_id
                                          , stroke_id
@@ -1383,7 +1413,7 @@ mov_grab_anchorpoints_path(t_mov_gui_stuff *mgp,
       */
       switch (l_ti)
       {
-        case GAP_BEZIER_ANCHOR_X_INDEX:  point_x = (gint)points_details[l_ii]; break; 
+        case GAP_BEZIER_ANCHOR_X_INDEX:  point_x = (gint)points_details[l_ii]; break;
         case GAP_BEZIER_ANCHOR_Y_INDEX:  point_y = (gint)points_details[l_ii]; break;
         default:  break;
       }
@@ -1464,7 +1494,7 @@ mov_pgrab_callback (GtkWidget *widget,
     {
       printf("vectorname :%s\n", vectorname);
     }
-    
+
     stroke_ids = gimp_vectors_get_strokes(vectors_id, &num_stroke_ids);
 
     if(gap_debug)
@@ -1473,7 +1503,7 @@ mov_pgrab_callback (GtkWidget *widget,
             , (int)num_stroke_ids
             );
     }
-    
+
     if (num_stroke_ids < 1)
     {
       g_message(_("No stroke ids found in path:\n"
@@ -1576,7 +1606,7 @@ mov_pgrab_callback (GtkWidget *widget,
  * mov_upd_seg_labels
  * --------------------------------
  * update information about max speed and path segment length
- * 
+ *
  */
 static void
 mov_upd_seg_labels(GtkWidget *widget, t_mov_gui_stuff *mgp)
@@ -1594,7 +1624,7 @@ mov_upd_seg_labels(GtkWidget *widget, t_mov_gui_stuff *mgp)
     {
        GapMovQuery mov_query;
        char *numString;
-       
+
        mov_query.pointIndexToQuery = pvals->point_idx;
 
       if(gap_debug)
@@ -1604,10 +1634,10 @@ mov_upd_seg_labels(GtkWidget *widget, t_mov_gui_stuff *mgp)
               , (int)pvals->dst_range_end
               );
       }
-       
+
        /* query path segment length and max speed per frame values */
        gap_mov_exec_query(pvals, mgp->ainfo_ptr, &mov_query);
-       
+
        numString = g_strdup_printf("%d"
                                   , (int)mov_query.segmentNumber
                                   );
@@ -1619,15 +1649,15 @@ mov_upd_seg_labels(GtkWidget *widget, t_mov_gui_stuff *mgp)
                                   );
        gtk_label_set_text( GTK_LABEL(mgp->segLengthLabel), numString);
        g_free(numString);
-       
-       
+
+
        numString = g_strdup_printf("%.1f / %.1f"
                                   , (float)mov_query.minSpeedInPixelsPerFrame
                                   , (float)mov_query.maxSpeedInPixelsPerFrame
                                   );
        gtk_label_set_text( GTK_LABEL(mgp->segSpeedLabel), numString);
        g_free(numString);
-       
+
     }
   }
 }
@@ -2041,7 +2071,130 @@ mov_psave_callback (GtkWidget *widget,
                     mgp);
 }
 
+/* --------------------------------
+ * p_refresh_widgets_after_load
+ * --------------------------------
+ * refresh widgets according to the new values
+ * after loading settings from an xml parameter file.
+ * This includes all widgets representing controlpoint data
+ * and other render relevant parameter data loaded from the xml file.
+ * except:
+ * - the src_layer (the moving object)
+ *   (the layer id of the object saved to the parameter file
+ *    may no longer exist at load time)
+ *
+ * Note that not render relvant widgets are not
+ * included in the xml paramter file and are not refreshed here.
+ */
+static void
+p_refresh_widgets_after_load(t_mov_gui_stuff *mgp)
+{
+  if(mgp == NULL)
+  {
+    return;
+  }
+
+  p_point_refresh(mgp);
+
+  if(mgp->paintmode_combo != NULL)
+  {
+    gimp_int_combo_box_set_active (GIMP_INT_COMBO_BOX (mgp->paintmode_combo), pvals->src_paintmode);
+  }
+  if (mgp->stepmode_combo != NULL)
+  {
+    gimp_int_combo_box_set_active (GIMP_INT_COMBO_BOX (mgp->stepmode_combo), pvals->src_stepmode);
+  }
+  if (mgp->handlemode_combo != NULL)
+  {
+    gimp_int_combo_box_set_active (GIMP_INT_COMBO_BOX (mgp->handlemode_combo), pvals->src_handle);
+  }
+  if (mgp->src_selmode_combo != NULL)
+  {
+    gimp_int_combo_box_set_active (GIMP_INT_COMBO_BOX (mgp->src_selmode_combo), pvals->src_selmode);
+  }
+
+  if(mgp->step_speed_factor_adj != NULL)
+  {
+    gtk_adjustment_set_value(mgp->step_speed_factor_adj,  pvals->step_speed_factor);
+  }
+
+
+  if(mgp->dst_range_start_adj != NULL)
+  {
+    gtk_adjustment_set_value(mgp->dst_range_start_adj,  pvals->dst_range_start);
+  }
+  if(mgp->dst_range_end_adj != NULL)
+  {
+    gtk_adjustment_set_value(mgp->dst_range_end_adj,  pvals->dst_range_end);
+  }
+  if(mgp->dst_layerstack_adj != NULL)
+  {
+    gtk_adjustment_set_value(mgp->dst_layerstack_adj,  pvals->dst_layerstack);
+  }
+
+
+  if(mgp->tween_steps_adj != NULL)
+  {
+    gtk_adjustment_set_value(mgp->tween_steps_adj,  pvals->tween_steps);
+  }
+  if(mgp->tween_opacity_initial_adj != NULL)
+  {
+    gtk_adjustment_set_value(mgp->tween_opacity_initial_adj,  pvals->tween_opacity_initial);
+  }
+  if(mgp->tween_opacity_desc_adj != NULL)
+  {
+    gtk_adjustment_set_value(mgp->tween_opacity_desc_adj,  pvals->tween_opacity_desc);
+  }
 
+  if(mgp->trace_opacity_initial_adj != NULL)
+  {
+    gtk_adjustment_set_value(mgp->trace_opacity_initial_adj,  pvals->trace_opacity_initial);
+  }
+  if(mgp->trace_opacity_desc_adj != NULL)
+  {
+    gtk_adjustment_set_value(mgp->trace_opacity_desc_adj,  pvals->trace_opacity_desc);
+  }
+  
+  if(mgp->tracelayer_enable_check_button != NULL)
+  {
+    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (mgp->tracelayer_enable_check_button),
+                                  pvals->tracelayer_enable);
+  }
+
+  if (mgp->src_force_visible_check_button != NULL)
+  {
+    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (mgp->src_force_visible_check_button),
+                                  pvals->src_force_visible);
+  }
+  
+  if (mgp->clip_to_img_check_button != NULL)
+  {
+    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (mgp->clip_to_img_check_button),
+                                  pvals->clip_to_img);
+  }
+  
+  if (mgp->src_apply_bluebox_check_button != NULL)
+  {
+    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (mgp->src_apply_bluebox_check_button),
+                                  pvals->src_apply_bluebox);
+  }
+  
+  if (mgp->bluebox_keycolor_color_button != NULL)
+  {
+    if(pvals->bbp != NULL)
+    {
+      gimp_color_button_set_color(mgp->bluebox_keycolor_color_button, &pvals->bbp->vals.keycolor);
+    }
+  }
+
+}  /* end p_refresh_widgets_after_load */
+
+
+/* ---------------------------------
+ * p_points_load_from_file
+ * ---------------------------------
+ *
+ */
 static void
 p_points_load_from_file (GtkWidget *widget,
                       t_mov_gui_stuff *mgp)
@@ -2064,11 +2217,15 @@ p_points_load_from_file (GtkWidget *widget,
   mgp->filesel = NULL;
 
   p_load_points(mgp->pointfile_name);
-  p_point_refresh(mgp);
+  p_refresh_widgets_after_load(mgp);
   mov_set_instant_apply_request(mgp);
 }  /* end p_points_load_from_file */
 
-
+/* ---------------------------------
+ * p_points_save_to_file
+ * ---------------------------------
+ *
+ */
 static void
 p_points_save_to_file (GtkWidget *widget,
                       t_mov_gui_stuff *mgp)
@@ -2163,7 +2320,7 @@ p_point_refresh(t_mov_gui_stuff *mgp)
                             (gdouble)mgp->accSelFeatherRadius);
 
   mov_upd_seg_labels(NULL, mgp);
-  
+
   mgp->in_call = FALSE;
 }       /* end p_point_refresh */
 
@@ -2611,7 +2768,7 @@ p_set_sensitivity_by_adjustment(GtkAdjustment *adj, gboolean sensitive)
   {
     GtkWidget *spinbutton;
     GtkWidget *scale;
-    
+
     spinbutton = GTK_WIDGET(g_object_get_data (G_OBJECT (adj), "spinbutton"));
     if(spinbutton)
     {
@@ -2629,7 +2786,7 @@ p_set_sensitivity_by_adjustment(GtkAdjustment *adj, gboolean sensitive)
 /* ----------------------------------
  * p_accel_widget_sensitivity
  * ----------------------------------
- * set sensitivity for all acceleration characteristic widgets 
+ * set sensitivity for all acceleration characteristic widgets
  * Those widgets are sensitive for the first conrolpoint
  * and for keframes that are NOT the last controlpoint.
  */
@@ -2637,15 +2794,15 @@ static void
 p_accel_widget_sensitivity(t_mov_gui_stuff *mgp)
 {
   gboolean sensitive;
-    
+
   sensitive = FALSE;
-  if(pvals->point_idx == 0) 
+  if(pvals->point_idx == 0)
   {
     sensitive = TRUE;
   }
   else
   {
-    if ((pvals->point_idx != pvals->point_idx_max) 
+    if ((pvals->point_idx != pvals->point_idx_max)
     && ((pvals->point[pvals->point_idx].keyframe > 0) || (mgp->keyframe_abs > 0)))
     {
       sensitive = TRUE;
@@ -2700,16 +2857,16 @@ p_points_from_tab(t_mov_gui_stuff *mgp)
   if(( mgp->keyframe_adj != NULL) && (mgp->startup != TRUE))
   {
     gboolean sensitive;
-    
+
     sensitive = TRUE;
     if((pvals->point_idx == 0) || (pvals->point_idx == pvals->point_idx_max))
     {
       sensitive = FALSE;
     }
     p_set_sensitivity_by_adjustment(mgp->keyframe_adj, sensitive);
-    
+
     p_accel_widget_sensitivity(mgp);
-    
+
   }
 }
 
@@ -2734,7 +2891,7 @@ p_points_to_tab(t_mov_gui_stuff *mgp)
   pvals->point[pvals->point_idx].tbry      = mgp->tbry;
   pvals->point[pvals->point_idx].sel_feather_radius  = mgp->sel_feather_radius;
   pvals->point[pvals->point_idx].keyframe_abs  = mgp->keyframe_abs;
-  
+
   pvals->point[pvals->point_idx].accPosition         = mgp->accPosition;
   pvals->point[pvals->point_idx].accOpacity          = mgp->accOpacity;
   pvals->point[pvals->point_idx].accSize             = mgp->accSize;
@@ -2795,7 +2952,7 @@ p_clear_one_point(gint idx)
     pvals->point[idx].sel_feather_radius = 0.0;
     pvals->point[idx].keyframe = 0;   /* 0: controlpoint is not fixed to keyframe */
     pvals->point[idx].keyframe_abs = 0;   /* 0: controlpoint is not fixed to keyframe */
-    
+
     pvals->point[idx].accPosition = 0;           /* 0: linear (e.g NO acceleration) is default */
     pvals->point[idx].accOpacity = 0;            /* 0: linear (e.g NO acceleration) is default */
     pvals->point[idx].accSize = 0;               /* 0: linear (e.g NO acceleration) is default */
@@ -2872,19 +3029,92 @@ void p_reset_points()
   pvals->point[0].p_y = 0;
 }       /* end p_reset_points */
 
-/* ============================================================================
+/* ---------------------------------
+ * p_filename_ends_with_etension_xml
+ * ---------------------------------
+ */
+static gboolean
+p_filename_ends_with_etension_xml(const char *filename)
+{
+  int l_len;
+  gboolean l_xml;
+
+  l_xml = FALSE;
+  l_len = strlen(filename);
+  if(l_len >= 3)
+  {
+    const char *l_extension;
+    l_extension = &filename[l_len -3];
+    if (strcmp("xml", l_extension) == 0)
+    {
+      l_xml = TRUE;
+    }
+    if (strcmp("XML", l_extension) == 0)
+    {
+      l_xml = TRUE;
+    }
+  }
+
+  return(l_xml);
+
+}
+
+/* -----------------------------
  * p_load_points
+ * -----------------------------
+ * supports pointfile format(s) of older gimp-gap releases that
+ * only contains the path controlpoints table
+ * and also loads from the newer move path parameterfile in xml format
+ * that can contain all parameter settings.
+ *
+ * old pointfile:
  *   load point table (from named file into global pvals)
  *   (reset points if load failed)
+ * new xml file:
+ *   load all settings into global pvals including the point table.
+ *   Note that the xml load affects all settings (except the src_layer_id that represents
+ *   the moving object)
+ *   in case some settings (such as perspective settings, bluebox settings ...)
+ *   are not present in the xml file
+ *   the missing settings are replaced by default values.
  * ============================================================================
  */
-
 void
 p_load_points(char *filename)
 {
   gint l_rc;
   gint l_errno;
 
+  if (p_filename_ends_with_etension_xml(filename))
+  {
+    gboolean l_xmlOk;
+
+    l_xmlOk = gap_mov_xml_par_load(filename, pvals
+                                  ,gimp_image_width(pvals->dst_image_id)
+                                  ,gimp_image_height(pvals->dst_image_id)
+                                  );
+    if (!l_xmlOk)
+    {
+
+      if(l_errno != 0)
+      {
+        g_message(_("ERROR: Could not open xml parameterfile\n"
+                "filename: '%s'\n%s")
+               ,filename, g_strerror (l_errno));
+      }
+      else
+      {
+        g_message(_("ERROR: Could not read parameterfile\n"
+                "filename: '%s'\n(Is not a valid move path xml parameterfile file)")
+               ,filename);
+      }
+
+
+    }
+
+    return;
+  }
+
   l_rc = gap_mov_exec_gap_load_pointfile(filename, pvals);
   l_errno = errno;
 
@@ -2909,10 +3139,14 @@ p_load_points(char *filename)
   }
 }  /* end p_load_points */
 
-/* ============================================================================
+
+
+/* ----------------------------
  * p_save_points
+ * ----------------------------
+ * depending on the filename extension (.xml)
  *   save point table (from global pvals into named file)
- * ============================================================================
+ *
  */
 static void
 p_save_points(char *filename, t_mov_gui_stuff *mgp)
@@ -2928,7 +3162,14 @@ p_save_points(char *filename, t_mov_gui_stuff *mgp)
 
   if(l_wr_permission)
   {
-    l_rc = gap_mov_exec_gap_save_pointfile(filename, pvals);
+    if (p_filename_ends_with_etension_xml(filename))
+    {
+      l_rc = gap_mov_xml_par_save(filename, pvals);
+    }
+    else
+    {
+      l_rc = gap_mov_exec_gap_save_pointfile(filename, pvals);
+    }
     l_errno = errno;
 
     if(l_rc != 0)
@@ -3005,29 +3246,31 @@ mov_src_sel_create(t_mov_gui_stuff *mgp)
   gtk_table_attach(GTK_TABLE(table), label, 2, 3, 0, 1, GTK_FILL, 0, 4, 0);
   gtk_widget_show(label);
 
-  combo = gimp_int_combo_box_new (_("Normal"),         GIMP_NORMAL_MODE,
-                                  _("Dissolve"),       GIMP_DISSOLVE_MODE,
-                                  _("Multiply"),       GIMP_MULTIPLY_MODE,
-                                  _("Divide"),         GIMP_DIVIDE_MODE,
-                                  _("Screen"),         GIMP_SCREEN_MODE,
-                                  _("Overlay"),        GIMP_OVERLAY_MODE,
-                                  _("Dodge"),          GIMP_DODGE_MODE,
-                                  _("Burn"),           GIMP_BURN_MODE,
-                                  _("Hard Light"),     GIMP_HARDLIGHT_MODE,
-                                  _("Soft Light"),     GIMP_SOFTLIGHT_MODE,
-                                  _("Grain Extract"),  GIMP_GRAIN_EXTRACT_MODE,
-                                  _("Grain Merge"),    GIMP_GRAIN_MERGE_MODE,
-                                  _("Difference"),     GIMP_DIFFERENCE_MODE,
-                                  _("Addition"),       GIMP_ADDITION_MODE,
-                                  _("Subtract"),       GIMP_SUBTRACT_MODE,
-                                  _("Darken Only"),    GIMP_DARKEN_ONLY_MODE,
-                                  _("Lighten Only"),   GIMP_LIGHTEN_ONLY_MODE,
-                                  _("Hue"),            GIMP_HUE_MODE,
-                                  _("Saturation"),     GIMP_SATURATION_MODE,
-                                  _("Color"),          GIMP_COLOR_MODE,
-                                  _("Value"),          GIMP_VALUE_MODE,
-                                  _("Keep Paintmode"), GAP_MOV_KEEP_SRC_PAINTMODE,
-                                  NULL);
+   combo = gimp_int_combo_box_new (_("Normal"),         GIMP_NORMAL_MODE,
+                                   _("Dissolve"),       GIMP_DISSOLVE_MODE,
+                                   _("Behind"),         GIMP_BEHIND_MODE,
+                                   _("Multiply"),       GIMP_MULTIPLY_MODE,
+                                   _("Divide"),         GIMP_DIVIDE_MODE,
+                                   _("Screen"),         GIMP_SCREEN_MODE,
+                                   _("Overlay"),        GIMP_OVERLAY_MODE,
+                                   _("Dodge"),          GIMP_DODGE_MODE,
+                                   _("Burn"),           GIMP_BURN_MODE,
+                                   _("Hard Light"),     GIMP_HARDLIGHT_MODE,
+                                   _("Soft Light"),     GIMP_SOFTLIGHT_MODE,
+                                   _("Grain Extract"),  GIMP_GRAIN_EXTRACT_MODE,
+                                   _("Grain Merge"),    GIMP_GRAIN_MERGE_MODE,
+                                   _("Difference"),     GIMP_DIFFERENCE_MODE,
+                                   _("Addition"),       GIMP_ADDITION_MODE,
+                                   _("Subtract"),       GIMP_SUBTRACT_MODE,
+                                   _("Darken Only"),    GIMP_DARKEN_ONLY_MODE,
+                                   _("Lighten Only"),   GIMP_LIGHTEN_ONLY_MODE,
+                                   _("Hue"),            GIMP_HUE_MODE,
+                                   _("Saturation"),     GIMP_SATURATION_MODE,
+                                   _("Color"),          GIMP_COLOR_MODE,
+                                   _("Color Erase"),    GIMP_COLOR_ERASE_MODE,
+                                   _("Value"),          GIMP_VALUE_MODE,
+                                   _("Keep Paintmode"), GAP_MOV_KEEP_SRC_PAINTMODE,
+                                   NULL);
 
   gimp_int_combo_box_connect (GIMP_INT_COMBO_BOX (combo),
                               GIMP_NORMAL_MODE,              /* initial int value */
@@ -3039,6 +3282,7 @@ mov_src_sel_create(t_mov_gui_stuff *mgp)
                        _("Paintmode")
                        , NULL);
   gtk_widget_show(combo);
+  mgp->paintmode_combo = combo;
 
 
 
@@ -3108,6 +3352,7 @@ mov_src_sel_create(t_mov_gui_stuff *mgp)
                        _("How to fetch the next source layer at the next handled frame")
                        , NULL);
   gtk_widget_show(combo);
+  mgp->stepmode_combo = combo;
 
 
   /* Source Image Handle menu */
@@ -3136,6 +3381,7 @@ mov_src_sel_create(t_mov_gui_stuff *mgp)
                        _("How to place the Source layer at controlpoint coordinates")
                        , NULL);
   gtk_widget_show(combo);
+  mgp->handlemode_combo = combo;
 
   gtk_widget_show( table );
 
@@ -3182,6 +3428,7 @@ mov_advanced_tab_create(t_mov_gui_stuff *mgp)
 
     /* toggle bluebox */
     check_button = gtk_check_button_new_with_label ( _("Bluebox"));
+    mgp->src_apply_bluebox_check_button = check_button;
     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (check_button),
                                   pvals->src_apply_bluebox);
     gimp_help_set_help_data(check_button,
@@ -3213,6 +3460,7 @@ mov_advanced_tab_create(t_mov_gui_stuff *mgp)
                                   25, 12,                     /* WIDTH, HEIGHT, */
                                   &pvals->bbp_pv->vals.keycolor,
                                   GIMP_COLOR_AREA_FLAT);
+    mgp->bluebox_keycolor_color_button = (GimpColorButton *)color_button;
 
     /* dont know if it is possible to remove the signal handler for the "clicked" signal
      * on the gimp_color_button.
@@ -3244,6 +3492,7 @@ mov_advanced_tab_create(t_mov_gui_stuff *mgp)
 
   /* toggle Tracelayer */
   check_button = gtk_check_button_new_with_label ( _("Tracelayer"));
+  mgp->tracelayer_enable_check_button = check_button;
   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (check_button),
                                   pvals->tracelayer_enable);
   gimp_help_set_help_data(check_button,
@@ -3653,7 +3902,8 @@ mov_path_framerange_box_create(t_mov_gui_stuff *mgp
 {
   GtkWidget *master_table;
   GtkWidget *table;
-  GtkObject *adj;
+  //GtkObject *adj;
+  GtkAdjustment *adj;
   GtkWidget *check_button;
   gint  master_rows;
   gint  master_cols;
@@ -3711,6 +3961,7 @@ mov_path_framerange_box_create(t_mov_gui_stuff *mgp
   g_signal_connect (adj, "value-changed",
                             G_CALLBACK (mov_upd_seg_labels),
                             mgp);
+  mgp->dst_range_start_adj = adj;
 
   /* the end frame scale_entry */
   adj = gimp_scale_entry_new( GTK_TABLE (table), 0, 1,          /* table col, row */
@@ -3732,6 +3983,7 @@ mov_path_framerange_box_create(t_mov_gui_stuff *mgp
   g_signal_connect (adj, "value-changed",
                             G_CALLBACK (mov_upd_seg_labels),
                             mgp);
+  mgp->dst_range_end_adj = adj;
 
   /* the Layerstack scale_entry */
   adj = gimp_scale_entry_new( GTK_TABLE (table), 0, 2,          /* table col, row */
@@ -3751,6 +4003,7 @@ mov_path_framerange_box_create(t_mov_gui_stuff *mgp
   g_signal_connect (G_OBJECT (adj), "value_changed",
                     G_CALLBACK (mov_instant_int_adjustment_update),
                     &pvals->dst_layerstack);
+  mgp->dst_layerstack_adj = adj;
 
   /* the table for checkbuttons and info labels */
   table = gtk_table_new (3, 3, FALSE);
@@ -3772,7 +4025,7 @@ mov_path_framerange_box_create(t_mov_gui_stuff *mgp
                     &pvals->src_force_visible);
   gtk_table_attach(GTK_TABLE(table), check_button, 0, 1, row, row+1
                   , GTK_FILL, GTK_FILL, 4, 0);
-
+  mgp->src_force_visible_check_button = check_button;
 
   row = 1;
 
@@ -3789,8 +4042,7 @@ mov_path_framerange_box_create(t_mov_gui_stuff *mgp
                     &pvals->clip_to_img);
   gtk_table_attach(GTK_TABLE(table), check_button, 0, 1, row, row+1
                   , GTK_FILL, GTK_FILL, 4, 0);
-
-
+  mgp->clip_to_img_check_button = check_button;
 
 
 
@@ -4299,6 +4551,7 @@ mov_selection_handling_tab_create (t_mov_gui_stuff *mgp)
                        _("How to handle selections in the source image")
                        , NULL);
   gtk_widget_show(combo);
+  mgp->src_selmode_combo = combo;
 
   /* Feather Radius */
   adj = gimp_scale_entry_new( GTK_TABLE (table), 0, 1,        /* table col, row */
@@ -4339,8 +4592,10 @@ mov_selection_handling_tab_create (t_mov_gui_stuff *mgp)
  *   - 2 spinbuttons X/Y, used for positioning
  *   - Keyframe     spinbutton integer (0 to max_frame)
  *   - Notebook  with following sub tables:
- *      - transform SubTable  4-point perspective transformation
- *      - modify    SubTable  for Resize(Scaling), Opacity and Rotation
+ *      - transform    SubTable  4-point perspective transformation
+ *      - modify       SubTable  for Resize(Scaling), Opacity and Rotation
+ *      - selection    SubTable  for selection handling (mode and feather radius)
+ *      - acceleration SubTable  for acceleration characteristics
  * ============================================================================
  */
 static void
@@ -4470,7 +4725,7 @@ mov_path_prevw_create ( GimpDrawable *drawable, t_mov_gui_stuff *mgp, gboolean v
 
     /* set of perspective transformation widgets for the current controlpoint */
     transform_table = mov_trans_tab_create(mgp);
-    
+
     /* set of acceleration characteristic widgets for the current controlpoint */
     acceleration_table = mov_acc_tab_create(mgp);
 
@@ -4602,14 +4857,14 @@ mov_path_prevw_create ( GimpDrawable *drawable, t_mov_gui_stuff *mgp, gboolean v
   }
 
 
-  /* segmnt information labels */
+  /* segment information labels (to show min/max speed in pixels per frame) */
   {
     GtkWidget *label;
     GtkWidget *seg_table;
     gint seg_row;
-    
+
     seg_row = 0;
-    
+
     /* the preview sub table (1 row) */
     seg_table = gtk_table_new ( 1, 6, FALSE );
     gtk_widget_show (seg_table);
@@ -4619,22 +4874,22 @@ mov_path_prevw_create ( GimpDrawable *drawable, t_mov_gui_stuff *mgp, gboolean v
     gtk_widget_show (label);
     gtk_table_attach(GTK_TABLE(seg_table), label, 0, 1, seg_row, seg_row+1
                   , GTK_FILL, GTK_FILL, 4, 0);
- 
+
     label = gtk_label_new("0");
     gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
     gtk_widget_show (label);
     mgp->segNumberLabel = label;
     gtk_table_attach(GTK_TABLE(seg_table), label, 1, 2, seg_row, seg_row+1
                     , GTK_FILL, GTK_FILL, 4, 0);
-  
+
 
     label = gtk_label_new(_("Length:"));
     gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
     gtk_widget_show (label);
     gtk_table_attach(GTK_TABLE(seg_table), label, 2, 3, seg_row, seg_row+1
                   , GTK_FILL, GTK_FILL, 4, 0);
-  
- 
+
+
     label = gtk_label_new("0.0");
     gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
     gtk_widget_show (label);
@@ -4658,7 +4913,7 @@ mov_path_prevw_create ( GimpDrawable *drawable, t_mov_gui_stuff *mgp, gboolean v
 
     gtk_table_attach(GTK_TABLE(pv_table), seg_table, 0, 1, 1, 2,
                      GTK_FILL|GTK_EXPAND, 0, 0, 0);
-  
+
   }
 
 
@@ -5569,6 +5824,7 @@ p_get_prevw_drawable (t_mov_gui_stuff *mgp)
   GapMovCurrent l_curr;
   gint      l_nlayers;
 
+  l_curr.isSingleFrame = FALSE;
 
   /* check if we have a source layer (to add to the preview) */
   if((pvals->src_layer_id >= 0) && (pvals->src_image_id >= 0))
@@ -5801,7 +6057,7 @@ p_mov_acc_spinbutton_new(GtkTable *table
   GtkObject       *adj;
   GapAccelWidget  *accel_wgt;
   gint32           accelerationCharacteristic;
-  
+
 #define ACC_WGT_WIDTH 28
 #define ACC_WGT_HEIGHT 26
 
@@ -6008,3 +6264,66 @@ mov_pview_size_allocate_callback(GtkWidget *widget
   }
 
 }  /* end mov_pview_size_allocate_callback */
+
+
+
+/* -----------------------------------
+ * gap_mov_dlg_move_dialog_singleframe
+ * -----------------------------------
+ * return 0 : OK, got params from the dialog
+ *        -1: user has cancelled the dialog
+ */
+gint
+gap_mov_dlg_move_dialog_singleframe(GapMovSingleFrame *singleFramePtr)
+{
+  static GapArrArg  argv[3];
+  gint   l_ii;
+  gint   l_ii_frame_phase;
+  gint   l_ii_total_frames;
+
+  l_ii = 0;
+  gap_arr_arg_init(&argv[l_ii], GAP_ARR_WGT_FILESEL);
+  argv[l_ii].label_txt = _("MovePath xmlfile:");
+  argv[l_ii].entry_width = 400;
+  argv[l_ii].help_txt  = _("Name of the file containing move path paramters and controlpoints in XML format");
+  argv[l_ii].text_buf_len = sizeof(singleFramePtr->xml_paramfile);
+  argv[l_ii].text_buf_ret = singleFramePtr->xml_paramfile;
+
+  l_ii++;
+  l_ii_total_frames = l_ii;
+  gap_arr_arg_init(&argv[l_ii], GAP_ARR_WGT_INT_PAIR);
+  argv[l_ii].constraint = TRUE;
+  argv[l_ii].label_txt = _("Total Frames:");
+  argv[l_ii].help_txt  = _("Total number of frames");
+  argv[l_ii].int_min   = (gint)1;
+  argv[l_ii].int_max   = (gint)MAX(10000, singleFramePtr->total_frames);
+  argv[l_ii].int_ret   = (gint)singleFramePtr->total_frames;
+  argv[l_ii].has_default = TRUE;
+  argv[l_ii].int_default = (gint)50;
+
+  l_ii++;
+  l_ii_frame_phase = l_ii;
+  gap_arr_arg_init(&argv[l_ii], GAP_ARR_WGT_INT_PAIR);
+  argv[l_ii].constraint = TRUE;
+  argv[l_ii].label_txt = _("Current Frame:");
+  argv[l_ii].help_txt  = _("Curent Frame number (e.g. phase to be phase Total number of frames");
+  argv[l_ii].int_min   = (gint)1;
+  argv[l_ii].int_max   = (gint)MAX(10000, singleFramePtr->total_frames);
+  argv[l_ii].int_ret   = (gint)CLAMP(singleFramePtr->frame_phase, 1, argv[l_ii].int_max);
+  argv[l_ii].has_default = TRUE;
+  argv[l_ii].int_default = argv[l_ii].int_ret;
+
+
+  if(TRUE == gap_arr_ok_cancel_dialog( _("Copy Audiofile as Wavefile"),
+                                 _("Settings"),
+                                  G_N_ELEMENTS(argv), argv))
+  {
+     singleFramePtr->total_frames = (gint32)(argv[l_ii_total_frames].int_ret);
+     singleFramePtr->frame_phase  = (gint32)(argv[l_ii_frame_phase].int_ret);
+
+     return (0);  /* OK */
+  }
+
+  return (-1);  /*  dialog cancelled */
+
+}  /* end gap_mov_dlg_move_dialog_singleframe */
diff --git a/gap/gap_mov_dialog.h b/gap/gap_mov_dialog.h
index 6c996eb..7b73e2b 100644
--- a/gap/gap_mov_dialog.h
+++ b/gap/gap_mov_dialog.h
@@ -32,7 +32,7 @@
  * gimp    1.3.20c; 2003/09/29  hof: new features: perspective transformation, tween_layer and trace_layer
  *                                   changed opacity, rotation and resize from int to gdouble
  * gimp    1.3.14a; 2003/05/24  hof: moved render procedures to module gap_mov_render
- * gimp    1.1.29b; 2000/11/19  hof: new feature: FRAME based Stepmodes, 
+ * gimp    1.1.29b; 2000/11/19  hof: new feature: FRAME based Stepmodes,
  *                                   increased controlpoint Limit GAP_MOV_MAX_POINT (from 256 -> 1024)
  * gimp    1.1.20a; 2000/04/25  hof: support for keyframes, anim_preview
  * version 0.96.02; 1998.07.25  hof: added clip_to_img
@@ -42,6 +42,12 @@
 
 #define GAP_MOV_KEEP_SRC_PAINTMODE 4444
 
+#define GAP_MOV_INT_VERSION 2
+
+#define   GAP_MOVPATH_XML_FILENAME_MAX_LENGTH     1024
+#define   GAP_MOVEPATH_GIMPRC_LOG_RENDER_PARAMS "video-move-path-log-render-params"
+
+
 typedef enum
 {
   GAP_STEP_LOOP      = 0,
@@ -95,7 +101,7 @@ typedef struct {
         gdouble currX,  currY;
         gint    l_handleX;
         gint    l_handleY;
-        
+
         gdouble currOpacity;
         gdouble currWidth;
         gdouble currHeight;
@@ -109,7 +115,7 @@ typedef struct {
         gdouble currTBLY;     /*  transform y bot left */
         gdouble currTBRX;     /*  transform x bot right */
         gdouble currTBRY;     /*  transform y bot right */
-        
+
         gdouble currSelFeatherRadius;
 
         /* acceleration characteristics */
@@ -120,6 +126,15 @@ typedef struct {
         gint accPerspective;
         gint accSelFeatherRadius;
 
+        gboolean isSingleFrame;      /* TRUE when processing in single frame mode */
+        gboolean keep_proportions;
+	gboolean fit_width;
+	gboolean fit_height;
+        gint32   singleMovObjLayerId;
+        gint32   singleMovObjImageId;
+        
+        gint32   processedLayerId;   /* id of the layer that was processed in the current render step */
+
 } GapMovCurrent;
 
 
@@ -129,10 +144,10 @@ typedef struct {
         gdouble w_resize;   /* width resize 10 upto 300% */
         gdouble h_resize;   /* height resize 10 upto 300% */
         gdouble rotation;   /* rotatation +- degrees */
-        
+
         gint    keyframe_abs;
         gint    keyframe;
-        
+
         /* 4-point transform distortion (perspective) */
         gdouble ttlx;     /* 0.0 upto 10.0 transform x top left */
         gdouble ttly;     /* 0.0 upto 10.0 transform y top left */
@@ -145,7 +160,7 @@ typedef struct {
 
         /* feather radius for selection handling */
         gdouble sel_feather_radius;
-        
+
         /* acceleration characteristics */
 	gint accPosition;
 	gint accOpacity;
@@ -165,6 +180,16 @@ typedef struct {
  */
 
 typedef struct {
+        gint32   version;
+        gint32   recordedObjWidth;       /* witdh of the moving object layer (at recording time of the move path settings) */
+        gint32   recordedObjHeight;      /* height of the moving object layer (at recording time of the move path settings) */
+        gint32   recordedFrameWidth;     /* witdh of the frame (at recording time of the move path settings) */
+        gint32   recordedFrameHeight;    /* height of the frame (at recording time of the move path settings) */
+        gint32   total_frames;
+        gint32   src_layerstack;
+        gchar   *src_filename;
+
+
         gint32         src_image_id;   /* source image */
         gint32         src_layer_id;   /* id of layer (to begin with) */
         GapMovHandle   src_handle;
@@ -194,7 +219,7 @@ typedef struct {
         gint    dst_range_end;
         gint    dst_layerstack;
 
-        /* for dialog only */   
+        /* for dialog only */
         gint32  dst_image_id;      /* frame image */
         gint32  tmp_image_id;      /* temp. flattened preview image */
         gint32  tmp_alt_image_id;  /* temp. preview image (preloaded preview frame) */
@@ -210,7 +235,7 @@ typedef struct {
                                      * -1 if we are not in anim_preview mode
                                      */
         gchar   *apv_gap_paste_buff;  /* Optional PasteBuffer (to store preview frames)
-                                       * "/tmp/gimp_video_paste_buffer/gap_pasteframe_" 
+                                       * "/tmp/gimp_video_paste_buffer/gap_pasteframe_"
                                        * NULL if we do not copy frames to a paste_buffer
                                        */
 
@@ -219,7 +244,7 @@ typedef struct {
         gdouble  apv_scaley;
 
         /* for FRAME based stepmodes */
-        gint32   cache_src_image_id;    /* id of the source image (from where cache image was copied) */ 
+        gint32   cache_src_image_id;    /* id of the source image (from where cache image was copied) */
         gint32   cache_tmp_image_id;    /* id of a cached flattened copy of the src image */
         gint32   cache_tmp_layer_id;    /* the only visible layer in the cached image */
         gint32   cache_frame_number;
@@ -235,23 +260,36 @@ typedef struct {
         /* for the bluebox filter */
         GapBlueboxGlobalParams *bbp;
         GapBlueboxGlobalParams *bbp_pv;
-        
+
 
 } GapMovValues;
 
 typedef struct {
-        GapAnimInfo  *dst_ainfo_ptr;      /* destination frames */
+        gint32       drawable_id;      /* the layer in the frame image to be processed (e.g. moved and transofrmed) */
+        gint32       frame_phase;      /* current frame number starting at 1 */
+        gint32       total_frames;
+        gboolean     keep_proportions;
+	gboolean     fit_width;
+	gboolean     fit_height;
+        gchar        xml_paramfile[GAP_MOVPATH_XML_FILENAME_MAX_LENGTH];
+} GapMovSingleFrame;
+
+
+typedef struct {
+        GapAnimInfo  *dst_ainfo_ptr;        /* destination frames */
+        GapMovSingleFrame *singleFramePtr;  /* NULL when processing multiple frames */
         GapMovValues *val_ptr;
 } GapMovData;
 
 
+
 typedef struct {
     /* IN */
    gint  pointIndexToQuery;
    gint  startOfSegmentIndexToQuery;
    gint  endOfSegmentIndexToQuery;
    gint  tweenCount;
-   
+
    /* OUT */
    gint    segmentNumber;
    gdouble pathSegmentLengthInPixels;
@@ -261,5 +299,7 @@ typedef struct {
 
 
 long  gap_mov_dlg_move_dialog (GapMovData *mov_ptr);
+gint  gap_mov_dlg_move_dialog_singleframe(GapMovSingleFrame *singleFramePtr);
+
 
 #endif
diff --git a/gap/gap_mov_exec.c b/gap/gap_mov_exec.c
index 98fc37f..282bb1f 100644
--- a/gap/gap_mov_exec.c
+++ b/gap/gap_mov_exec.c
@@ -74,6 +74,7 @@
 #include "gap_pdb_calls.h"
 #include "gap_arr_dialog.h"
 #include "gap_accel_char.h"
+#include "gap_mov_xml_par.h"
 
 extern      int gap_debug; /* ==0  ... dont print debug infos */
 
@@ -81,8 +82,19 @@ static void p_add_tween_and_trace(gint32 dest_image_id, GapMovData *mov_ptr, Gap
 static gint p_mov_call_render(GapMovData *mov_ptr, GapMovCurrent *cur_ptr, gint apv_layerstack);
 static void p_mov_advance_src_layer(GapMovCurrent *cur_ptr, GapMovValues  *pvals);
 static void p_mov_advance_src_frame(GapMovCurrent *cur_ptr, GapMovValues  *pvals);
-static long   p_mov_execute_or_query(GapMovData *mov_ptr, GapMovQuery *mov_query);
-static long   p_mov_execute(GapMovData *mov_ptr);
+
+static void     p_log_current_render_params(GapMovData *mov_ptr, GapMovCurrent *cur_ptr);
+static void     p_printf_log_parameters(GapMovData *mov_ptr);
+static void     gap_mov_exec_set_handle_offsets_singleframe(GapMovValues *val_ptr, GapMovCurrent *cur_ptr);
+static void     p_add_2nd_controlpoint(GapMovValues *val_ptr);
+static void     p_init_curr_ptr_with_1st_controlpoint(GapMovCurrent *cur_ptr, GapMovValues *val_ptr, GapMovSingleFrame *singleFramePtr);
+static gint32   p_duplicate_layer(gint32 layerId);
+
+static long     p_mov_execute_or_query(GapMovData *mov_ptr, GapMovQuery *mov_query);
+static gint32   p_mov_execute_singleframe(GapMovData *mov_ptr);
+
+
+static long     p_mov_execute(GapMovData *mov_ptr);
 static gdouble  p_calc_angle(gint p1x, gint p1y, gint p2x, gint p2y);
 static gdouble  p_rotatate_less_than_180(gdouble angle, gdouble angle_new, gint *turns);
 
@@ -120,7 +132,10 @@ static gint     p_calculate_settings_for_current_FrameTween(
                    , gint     endOfSegmentIndex
                    , GapMovQuery *mov_query
                    );
-                    
+
+
+
+
 
 
 /* ============================================================================
@@ -243,11 +258,16 @@ p_mov_call_render(GapMovData *mov_ptr, GapMovCurrent *cur_ptr, gint apv_layersta
   int     l_rc;
   char    *l_fname;
   char    *l_name;
+  GapMovSingleFrame *singleFramePtr;
 
   l_rc = 0;
   ainfo_ptr = mov_ptr->dst_ainfo_ptr;
+  singleFramePtr = mov_ptr->singleFramePtr;
 
-  if(mov_ptr->val_ptr->twix > 0)
+  p_log_current_render_params(mov_ptr, cur_ptr);
+
+  if((mov_ptr->val_ptr->twix > 0)
+  && (singleFramePtr == NULL))
   {
     /* We are rendering a virtual frame (a tween) */
     /* ------------------------------------------ */
@@ -312,28 +332,44 @@ p_mov_call_render(GapMovData *mov_ptr, GapMovCurrent *cur_ptr, gint apv_layersta
         gimp_image_add_layer (mov_ptr->val_ptr->tween_image_id, l_new_layer_id, 0 /* top of layerstack */ );
       }
     }
-    else l_rc = -1;
+    else
+    {
+      l_rc = -1;
+    }
 
   }
   else
   {
     if(mov_ptr->val_ptr->apv_mlayer_image < 0)
     {
-      /* We are generating the Animation on the ORIGINAL FRAMES */
-      /* ------------------------------------------------------ */
-      if(ainfo_ptr->new_filename != NULL) g_free(ainfo_ptr->new_filename);
-      ainfo_ptr->new_filename = gap_lib_alloc_fname(ainfo_ptr->basename,
-                                        cur_ptr->dst_frame_nr,
-                                        ainfo_ptr->extension);
-      if(ainfo_ptr->new_filename == NULL)
-         return -1;
+      if(singleFramePtr == NULL)
+      {
+        /* We are generating the Animation on the ORIGINAL FRAMES */
+        /* ------------------------------------------------------ */
+        if(ainfo_ptr->new_filename != NULL) g_free(ainfo_ptr->new_filename);
+        ainfo_ptr->new_filename = gap_lib_alloc_fname(ainfo_ptr->basename,
+                                          cur_ptr->dst_frame_nr,
+                                          ainfo_ptr->extension);
+        if(ainfo_ptr->new_filename == NULL)
+           return -1;
 
-      /* load next frame to render */
-      l_tmp_image_id = gap_lib_load_image(ainfo_ptr->new_filename);
-      if(l_tmp_image_id < 0)
-        return -1;
+        /* load next frame to render */
+        l_tmp_image_id = gap_lib_load_image(ainfo_ptr->new_filename);
+        if(l_tmp_image_id < 0)
+        {
+          return -1;
+        }
 
-      gimp_image_undo_disable (l_tmp_image_id);
+        gimp_image_undo_disable (l_tmp_image_id);
+      }
+      else
+      {
+        l_tmp_image_id = mov_ptr->val_ptr->dst_image_id;
+	if (l_tmp_image_id < 0)
+	{
+          l_tmp_image_id = gimp_drawable_get_image(singleFramePtr->drawable_id);
+	}
+      }
 
 
       /* call render procedure for current image */
@@ -344,11 +380,19 @@ p_mov_call_render(GapMovData *mov_ptr, GapMovCurrent *cur_ptr, gint apv_layersta
          */
         p_add_tween_and_trace(l_tmp_image_id, mov_ptr, cur_ptr);
 
-        /* if OK: save the rendered frame back to disk */
-        if(gap_lib_save_named_frame(l_tmp_image_id, ainfo_ptr->new_filename) < 0)
-          l_rc = -1;
+        if(singleFramePtr == NULL)
+        {
+          /* if OK: save the rendered frame back to disk (but not in singleframes mode) */
+          if(gap_lib_save_named_frame(l_tmp_image_id, ainfo_ptr->new_filename) < 0)
+          {
+            l_rc = -1;
+          }
+        }
+      }
+      else 
+      {
+        l_rc = -1;
       }
-      else l_rc = -1;
     }
     else
     {
@@ -369,8 +413,10 @@ p_mov_call_render(GapMovData *mov_ptr, GapMovCurrent *cur_ptr, gint apv_layersta
                                            ainfo_ptr->extension);
          l_tmp_image_id = gap_lib_load_image(ainfo_ptr->new_filename);
          if(l_tmp_image_id < 0)
+         {
            return -1;
-
+         }
+         
          gimp_image_undo_disable (l_tmp_image_id);
 
          if((mov_ptr->val_ptr->apv_scalex != 100.0) || (mov_ptr->val_ptr->apv_scaley != 100.0))
@@ -462,9 +508,14 @@ p_mov_call_render(GapMovData *mov_ptr, GapMovCurrent *cur_ptr, gint apv_layersta
     }
   }
 
-
-  /* destroy the tmp image */
-  gimp_image_delete(l_tmp_image_id);
+  if(singleFramePtr == NULL)
+  {
+    /* destroy the tmp image 
+     * (but not in singleframes mode. Note that in singleframe mode l_tmp_image_id
+     * referes to the image from where we were invoked)
+     */
+    gimp_image_delete(l_tmp_image_id);
+  }
 
   return l_rc;
 }       /* end p_mov_call_render */
@@ -686,7 +737,7 @@ p_mov_advance_src_frame(GapMovCurrent *cur_ptr, GapMovValues  *pvals)
  * IN points is the number of processing relevant controlpoints
  * returns the index of the last controlpoint in the path segment that starts at specified index
  * Note that path segment includes all controlpoints up to the next keyframe inclusive. If no keyframe
- * present ther is only one big segment from first to last controlpoint.
+ * present there is only one big segment from first to last controlpoint.
  */
 static gint
 p_calculate_relframe_nr_at_index(GapMovValues *val_ptr, gint index, gint frames)
@@ -695,7 +746,7 @@ p_calculate_relframe_nr_at_index(GapMovValues *val_ptr, gint index, gint frames)
   {
     return (1);
   }
-  
+
   if(index  < val_ptr->point_idx_max )
   {
     if (val_ptr->point[index].keyframe > 0)
@@ -713,7 +764,7 @@ p_calculate_relframe_nr_at_index(GapMovValues *val_ptr, gint index, gint frames)
  * p_findEndOfSegmentIndex
  * -----------------------------------
  * IN points is the number of processing relevant controlpoints
- * returns the index of the last controlpoint in the segment that starts 
+ * returns the index of the last controlpoint in the segment that starts
  * at specified startOfSegmentIndex.
  * the segment ends at next KEYFRAME or at the last controlpoint (that is an implicite keyframe)
  */
@@ -721,7 +772,7 @@ static gint
 p_findEndOfSegmentIndex(GapMovValues *val_ptr, gint startOfSegmentIndex, gint points)
 {
   gint ii;
-  
+
   for (ii= startOfSegmentIndex +1; ii < points; ii++)
   {
     if (val_ptr->point[ii].keyframe > 0)
@@ -730,7 +781,7 @@ p_findEndOfSegmentIndex(GapMovValues *val_ptr, gint startOfSegmentIndex, gint po
       return (ii);
     }
   }
-  
+
   return (points -1);
 }  /* end p_findEndOfSegmentIndex */
 
@@ -753,14 +804,14 @@ p_calculate_LineLength(GapMovValues *val_ptr, gint idx)
   {
     return (0.0);
   }
-  
+
   dx = abs (val_ptr->point[idx].p_x - val_ptr->point[idx +1].p_x);
   dy = abs (val_ptr->point[idx].p_y - val_ptr->point[idx +1].p_y);
-  
+
   len = sqrt((dx * dx) + (dy * dy));
-  
+
   return (len);
-  
+
 }  /* end p_calculate_LineLength */
 
 
@@ -775,7 +826,7 @@ p_calculate_path_segment_length(GapMovValues  *val_ptr
 {
   gint idx;
   gdouble lenSum;
-  
+
   lenSum = 0;
   for(idx=startOfSegmentIndex; idx < endOfSegmentIndex; idx++)
   {
@@ -788,14 +839,14 @@ p_calculate_path_segment_length(GapMovValues  *val_ptr
 /* -----------------------------------
  * p_pick_controlpoint_at_curr_length
  * -----------------------------------
- * returns the relevant controlpoint index 
+ * returns the relevant controlpoint index
  * at specified currentLen in pixels (eg. current position length within the specified
  * path segment that starts at controlpoint startOfSegmentIndex)
  *
  * IN:  startOfSegmentIndex
  * IN:  endOfSegmentIndex
  * IN:  pathSegmentLength length in pixels (0 at stastOfSegment, upto segment length)
- * IN:  
+ * IN:
  * OUT: flt_posfactor
  */
 static gint
@@ -814,20 +865,20 @@ p_pick_controlpoint_at_curr_length(GapMovValues  *val_ptr
    /* calculate length factor (0.0 to 1.0) respecting position specific acceleartion characteristic */
    lengthFactorAcc = gap_accelMixFactor(lengthFactorLinear, accelerationCharacteristic);
    currentLen = pathSegmentLength * lengthFactorAcc;
-   
-   
-   
-   
+
+
+
+
    lenSum = 0;
    lenSumPrev = 0;
-   
+
    for(idx = startOfSegmentIndex; idx <= endOfSegmentIndex; idx++)
    {
      gdouble lineLen;
-     
+
      lineLen = p_calculate_LineLength(val_ptr, idx);
      lenSum += lineLen;
- 
+
      if(lenSum >= currentLen)
      {
        gdouble l_flt_posfactor;
@@ -842,14 +893,14 @@ p_pick_controlpoint_at_curr_length(GapMovValues  *val_ptr
             ,(float) lengthFactorAcc
             );
        }
-      
-       
+
+
        *flt_posfactor =  CLAMP (l_flt_posfactor, 0.0, 1.0);
        return (idx);
      }
     lenSumPrev = lenSum;
   }
-   
+
   *flt_posfactor = 0;
   return (endOfSegmentIndex);
 
@@ -858,9 +909,9 @@ p_pick_controlpoint_at_curr_length(GapMovValues  *val_ptr
 /* --------------------------------------
  * p_calculate_posFactor_from_FrameTweens
  * --------------------------------------
- * returns the relevant positionFactor for the 
+ * returns the relevant positionFactor for the
  * specified currFrameTweenInSegment and frameTweensInSegment
- * respecting the specified acceleration characteristic 
+ * respecting the specified acceleration characteristic
  *
  */
 static gdouble
@@ -874,11 +925,11 @@ p_calculate_posFactor_from_FrameTweens(gdouble frameTweensInSegment
 
   factorLinear = currFrameTweenInSegment / (MAX (1.0, frameTweensInSegment));
   factorLinear = CLAMP (factorLinear, 0.0, 1.0);
-  
+
   /* calculate length factor (0.0 to 1.0) respecting position specific acceleartion characteristic */
   posFactorAcc = gap_accelMixFactor(factorLinear, accelerationCharacteristic);
-   
-   
+
+
   return (posFactorAcc);
 
 }  /* end p_calculate_posFactor_from_FrameTweens */
@@ -889,7 +940,7 @@ p_calculate_posFactor_from_FrameTweens(gdouble frameTweensInSegment
  * -------------------------------------------
  * calculate settings for the currently processed
  * Frame (or tween) according to the controlpoints.
- * supports older behavior of GIMP-GAP-2.6 and prior 
+ * supports older behavior of GIMP-GAP-2.6 and prior
  * and the extended variant with accerlaration characteristics
  * per path segment.
  * mode without acceleration characteristic (GIMP-GAP-2.6 behvior)
@@ -897,7 +948,7 @@ p_calculate_posFactor_from_FrameTweens(gdouble frameTweensInSegment
  *   This mode is selected by acceleration characteristic value 0.
  *   In this mode the flt_posfactor specifies the position within one line
  *   between the controlpoints with index [l_ptidx -1] and [l_ptidx]
- *  
+ *
  * Mode with acceleration characteristic:
  * ========================================
  *   a Path segment includes all controlpoints between two keyframes
@@ -914,7 +965,7 @@ p_calculate_posFactor_from_FrameTweens(gdouble frameTweensInSegment
  *
  *   in case acceleration characteristic values != 0 are used for any other settings than position,
  *   then all controlpoints that are NON keyframes are ignored for other settings than position (x,y).
- *   Those settings (opacity, size ...) are then calculated from the 
+ *   Those settings (opacity, size ...) are then calculated from the
  *   controlpoint at start and end of the path segment.
  *   (note that first and last controlpoint are
  *    implicite keyframes and therefore always relevant)
@@ -945,7 +996,7 @@ p_calculate_settings_for_current_FrameTween(
   gdouble tweenMultiplicator;
   gdouble lengthFactorLinear;      /* 0.0 at begin of segment 1.0 at end of segment position */
   gdouble frameTweensInSegment;
-  gdouble currFrameTweenInSegment; /* frame number relative to 0 at each starting point of a new segment 
+  gdouble currFrameTweenInSegment; /* frame number relative to 0 at each starting point of a new segment
                                     * tweens can have non integer frame number like 1.5 or 1.3333 or 1.25 etc....
                                     */
   gdouble pathSegmentLength;
@@ -975,13 +1026,13 @@ p_calculate_settings_for_current_FrameTween(
                              - p_calculate_relframe_nr_at_index(val_ptr, startOfSegmentIndex, affectedFrames)
                              ) + 1;
   frameTweensInSegment *= tweenMultiplicator;
-  
-  
-  
-  currFrameTweenInSegment = 
-     tweenMultiplicator * (abs (currFrameIndex - val_ptr->point[startOfSegmentIndex].keyframe)); /// TODO check for reverse order processing ???
+
+
+
+  currFrameTweenInSegment =
+     tweenMultiplicator * (abs (currFrameIndex - val_ptr->point[startOfSegmentIndex].keyframe)); 
   currFrameTweenInSegment += (val_ptr->tween_steps - val_ptr->twix);
-  
+
   /* calculate length factor respecting position in the path segment where 0 is at begin 1 at end */
   lengthFactorLinear = currFrameTweenInSegment / MAX(frameTweensInSegment, 1);
 
@@ -997,7 +1048,13 @@ p_calculate_settings_for_current_FrameTween(
           );
   }
 
-  
+  cur_ptr->accPosition         = val_ptr->point[startOfSegmentIndex].accPosition;
+  cur_ptr->accOpacity          = val_ptr->point[startOfSegmentIndex].accOpacity;
+  cur_ptr->accSize             = val_ptr->point[startOfSegmentIndex].accSize;
+  cur_ptr->accRotation         = val_ptr->point[startOfSegmentIndex].accRotation;
+  cur_ptr->accPerspective      = val_ptr->point[startOfSegmentIndex].accPerspective;
+  cur_ptr->accSelFeatherRadius = val_ptr->point[startOfSegmentIndex].accSelFeatherRadius;
+
   /* calculate Movement settings for the currently processed Frame (or tween)
    * position dependent acceleration processing requires a path segment length > 0
    * AND accPosition != 0
@@ -1052,7 +1109,7 @@ p_calculate_settings_for_current_FrameTween(
   else
   {
     /* No acceleration characteristic specified for movement (compatible to GAP 2.6.x release behavior) */
-    if(gap_debug) 
+    if(gap_debug)
     {
       printf("p_mov_execute: framesPerLine=%f, flt_posfactor=%f\n"
            , (float)framesPerLine
@@ -1084,7 +1141,7 @@ p_calculate_settings_for_current_FrameTween(
       refPtidx = segmPtidx +1;
     }
 
-    if(gap_debug) 
+    if(gap_debug)
     {
       printf("p_mov_execute: pixelLengthInOneTweenStep=%f  pointIndexToQuery:%d  currPtidx:%d refPtidx:%d StartQuery:%d EndQuery:%d\n"
            , (float)pixelLengthInOneTweenStep
@@ -1102,7 +1159,7 @@ p_calculate_settings_for_current_FrameTween(
       if(mov_query->tweenCount == 0)
       {
         mov_query->tweenCount++;
-        if(gap_debug) 
+        if(gap_debug)
         {
           printf("p_mov_execute: SKIP max speed calculation at first frame of segment\n");
         }
@@ -1123,10 +1180,8 @@ p_calculate_settings_for_current_FrameTween(
     }
 
 
-      
-  }
-
 
+  }
 
 
   /* calculate Opacity settings for the currently processed Frame (or tween) */
@@ -1157,7 +1212,6 @@ p_calculate_settings_for_current_FrameTween(
   }
 
 
-
   /* calculate Zoom (e.g. Size) settings for the currently processed Frame (or tween) */
   if ((val_ptr->point[startOfSegmentIndex].accSize != 0)
   && (frameTweensInSegment > 0))
@@ -1227,7 +1281,7 @@ p_calculate_settings_for_current_FrameTween(
     else
     {
       cur_ptr->currRotation = GAP_BASE_MIX_VALUE(flt_posfactor, (gdouble)val_ptr->point[currPtidx -1].rotation, (gdouble)val_ptr->point[currPtidx].rotation);
- 
+
       if(gap_debug)
       {
         printf("p_mov_execute: framesPerLine=%f, flt_posfactor=%f\n"
@@ -1310,22 +1364,315 @@ p_calculate_settings_for_current_FrameTween(
 }  /* end p_calculate_settings_for_current_FrameTween */
 
 
+/* --------------------------------
+ * p_log_current_render_params
+ * --------------------------------
+ * log current render parameters to stdout (for test and analyse purpose)
+ */
+static void
+p_log_current_render_params(GapMovData *mov_ptr, GapMovCurrent *cur_ptr)
+{
+  gboolean defaultFlag;
 
-/* ============================================================================
+  defaultFlag = FALSE;
+  if(gap_debug)
+  {
+    defaultFlag = TRUE;
+  }
+
+  if(gap_base_get_gimprc_gboolean_value(GAP_MOVEPATH_GIMPRC_LOG_RENDER_PARAMS, defaultFlag))
+  {
+    GapMovValues *val_ptr;
+
+    val_ptr = mov_ptr->val_ptr;
+
+    printf("\nCurrent Render Params: dst_frame_nr:%ld tweenIndex:%d src_layer_idx:%ld\n"
+                "       currX:%f currY:%f\n"
+                "       Width:%f Height:%f\n"
+                "       Opacity:%f  Rotate:%f  clip_to_img:%d force_visibility:%d\n"
+                "       src_stepmode:%d handleX:%d handleY:%d currSelFeatherRadius:%f\n",
+                     cur_ptr->dst_frame_nr, (int)val_ptr->twix, cur_ptr->src_layer_idx,
+                     cur_ptr->currX, cur_ptr->currY,
+                     cur_ptr->currWidth,
+                     cur_ptr->currHeight,
+                     cur_ptr->currOpacity,
+                     cur_ptr->currRotation,
+                     val_ptr->clip_to_img,
+                     val_ptr->src_force_visible,
+                     val_ptr->src_stepmode,
+                     cur_ptr->l_handleX,
+                     cur_ptr->l_handleY,
+                     cur_ptr->currSelFeatherRadius
+                     );
+
+    printf("       Perspective Factors: [0] %.3f %.3f  [1] %.3f %.3f  [2] %.3f %.3f  [3] %.3f %.3f\n"
+          ,(float)cur_ptr->currTTLX
+          ,(float)cur_ptr->currTTLY
+          ,(float)cur_ptr->currTTRX
+          ,(float)cur_ptr->currTTRY
+          ,(float)cur_ptr->currTBLX
+          ,(float)cur_ptr->currTBLY
+          ,(float)cur_ptr->currTBRX
+          ,(float)cur_ptr->currTBRY
+          );
+    printf("       accPosition:%d accOpacity:%d accSize:%d accRotation:%d accPerspective:%d accSelFeatherRadius:%d\n"
+          ,(int)cur_ptr->accPosition
+          ,(int)cur_ptr->accOpacity
+          ,(int)cur_ptr->accSize
+          ,(int)cur_ptr->accRotation
+          ,(int)cur_ptr->accPerspective
+          ,(int)cur_ptr->accSelFeatherRadius
+          );
+  }
+
+}  /* end p_log_current_render_params */
+
+
+/* --------------------------------
+ * p_printf_log_parameters
+ * --------------------------------
+ *
+ */
+static void
+p_printf_log_parameters(GapMovData *mov_ptr)
+{
+  gint l_idx;
+  
+  printf("apv_mlayer_image: %ld\n", (long)mov_ptr->val_ptr->apv_mlayer_image);
+  printf("apv_mode: %ld\n", (long)mov_ptr->val_ptr->apv_mode);
+  printf("apv_scale x: %f y:%f\n", (float)mov_ptr->val_ptr->apv_scalex, (float)mov_ptr->val_ptr->apv_scaley);
+  if(mov_ptr->val_ptr->apv_gap_paste_buff)
+  {
+    printf("apv_gap_paste_buf: %s\n", mov_ptr->val_ptr->apv_gap_paste_buff);
+  }
+  else
+  {
+    printf("apv_gap_paste_buf: ** IS NULL ** (do not copy to paste buffer)\n");
+  }
+  printf("src_image_id :%ld\n", (long)mov_ptr->val_ptr->src_image_id);
+  printf("src_layer_id :%ld\n", (long)mov_ptr->val_ptr->src_layer_id);
+  printf("src_handle :%d\n", mov_ptr->val_ptr->src_handle);
+  printf("src_stepmode :%d\n", mov_ptr->val_ptr->src_stepmode);
+  printf("src_paintmode :%d\n", mov_ptr->val_ptr->src_paintmode);
+  printf("clip_to_img :%d\n", mov_ptr->val_ptr->clip_to_img);
+  printf("dst_range_start :%d\n", (int)mov_ptr->val_ptr->dst_range_start);
+  printf("dst_range_end :%d\n", (int)mov_ptr->val_ptr->dst_range_end);
+  printf("dst_layerstack :%d\n", (int)mov_ptr->val_ptr->dst_layerstack);
+  for(l_idx = 0; l_idx <= mov_ptr->val_ptr->point_idx_max; l_idx++)
+  {
+    printf("p_x[%d] :%d\n", l_idx, mov_ptr->val_ptr->point[l_idx].p_x);
+    printf("p_y[%d] :%d\n", l_idx, mov_ptr->val_ptr->point[l_idx].p_y);
+    printf("opacity[%d] :%.3f\n", l_idx, (float)mov_ptr->val_ptr->point[l_idx].opacity);
+    printf("w_resize[%d] :%.3f\n", l_idx, (float)mov_ptr->val_ptr->point[l_idx].w_resize);
+    printf("h_resize[%d] :%.3f\n", l_idx, (float)mov_ptr->val_ptr->point[l_idx].h_resize);
+    printf("rotation[%d] :%.3f\n", l_idx, (float)mov_ptr->val_ptr->point[l_idx].rotation);
+    printf("ttlx[%d] :%.3f\n", l_idx, (float)mov_ptr->val_ptr->point[l_idx].ttlx);
+    printf("ttly[%d] :%.3f\n", l_idx, (float)mov_ptr->val_ptr->point[l_idx].ttly);
+    printf("ttrx[%d] :%.3f\n", l_idx, (float)mov_ptr->val_ptr->point[l_idx].ttrx);
+    printf("ttry[%d] :%.3f\n", l_idx, (float)mov_ptr->val_ptr->point[l_idx].ttry);
+    printf("tblx[%d] :%.3f\n", l_idx, (float)mov_ptr->val_ptr->point[l_idx].tblx);
+    printf("tbly[%d] :%.3f\n", l_idx, (float)mov_ptr->val_ptr->point[l_idx].tbly);
+    printf("tbrx[%d] :%.3f\n", l_idx, (float)mov_ptr->val_ptr->point[l_idx].tbrx);
+    printf("tbry[%d] :%.3f\n", l_idx, (float)mov_ptr->val_ptr->point[l_idx].tbry);
+
+    printf("accPosition        [%d] :%d\n", l_idx, (int)mov_ptr->val_ptr->point[l_idx].accPosition);
+    printf("accOpacity         [%d] :%d\n", l_idx, (int)mov_ptr->val_ptr->point[l_idx].accOpacity);
+    printf("accSize            [%d] :%d\n", l_idx, (int)mov_ptr->val_ptr->point[l_idx].accSize);
+    printf("accRotation        [%d] :%d\n", l_idx, (int)mov_ptr->val_ptr->point[l_idx].accRotation);
+    printf("accPerspective     [%d] :%d\n", l_idx, (int)mov_ptr->val_ptr->point[l_idx].accPerspective);
+    printf("accSelFeatherRadius[%d] :%d\n", l_idx, (int)mov_ptr->val_ptr->point[l_idx].accSelFeatherRadius);
+
+    printf("keyframe[%d] :%d\n", l_idx, mov_ptr->val_ptr->point[l_idx].keyframe);
+    printf("keyframe_abs[%d] :%d\n", l_idx, mov_ptr->val_ptr->point[l_idx].keyframe_abs);
+  }
+  printf("\n");
+
+}  /* end p_printf_log_parameters */
+
+
+/* -------------------------------------------
+ * gap_mov_exec_set_handle_offsets_singleframe
+ * -------------------------------------------
+ *
+ */
+static void
+gap_mov_exec_set_handle_offsets_singleframe(GapMovValues *val_ptr, GapMovCurrent *cur_ptr)
+{
+  guint    l_src_width, l_src_height;         /* dimensions of the source image */
+
+   /* get dimensions of source image (in single frame mode: same as frame image)  */
+   l_src_width  = gimp_image_width(cur_ptr->singleMovObjImageId);
+   l_src_height = gimp_image_height(cur_ptr->singleMovObjImageId);
+
+   cur_ptr->l_handleX = 0.0;
+   cur_ptr->l_handleY = 0.0;
+   switch(val_ptr->src_handle)
+   {
+      case GAP_HANDLE_LEFT_BOT:
+         cur_ptr->l_handleY += l_src_height;
+         break;
+      case GAP_HANDLE_RIGHT_TOP:
+         cur_ptr->l_handleX += l_src_width;
+         break;
+      case GAP_HANDLE_RIGHT_BOT:
+         cur_ptr->l_handleX += l_src_width;
+         cur_ptr->l_handleY += l_src_height;
+         break;
+      case GAP_HANDLE_CENTER:
+         cur_ptr->l_handleX += (l_src_width  / 2);
+         cur_ptr->l_handleY += (l_src_height / 2);
+         break;
+      case GAP_HANDLE_LEFT_TOP:
+      default:
+         break;
+   }
+}  /* end gap_mov_exec_set_handle_offsets_singleframe */
+
+
+
+/* --------------------------------
+ * p_add_2nd_controlpoint
+ * --------------------------------
+ *
+ */
+static void
+p_add_2nd_controlpoint(GapMovValues *val_ptr)
+{
+  /* copy point[0] to point [1] because we need at least 2
+   * points for the algorithms below to work.
+   * (simulates a line with length 0, to move along)
+   */
+  if(gap_debug)
+  {
+    printf("p_mov_execute: added a 2nd Point\n");
+  }
+  val_ptr->point[1].p_x = val_ptr->point[0].p_x;
+  val_ptr->point[1].p_y = val_ptr->point[0].p_y;
+  val_ptr->point[1].opacity = val_ptr->point[0].opacity;
+  val_ptr->point[1].w_resize = val_ptr->point[0].w_resize;
+  val_ptr->point[1].h_resize = val_ptr->point[0].h_resize;
+  val_ptr->point[1].rotation = val_ptr->point[0].rotation;
+  val_ptr->point[1].ttlx = val_ptr->point[0].ttlx;
+  val_ptr->point[1].ttly = val_ptr->point[0].ttly;
+  val_ptr->point[1].ttrx = val_ptr->point[0].ttrx;
+  val_ptr->point[1].ttry = val_ptr->point[0].ttry;
+  val_ptr->point[1].tblx = val_ptr->point[0].tblx;
+  val_ptr->point[1].tbly = val_ptr->point[0].tbly;
+  val_ptr->point[1].tbrx = val_ptr->point[0].tbrx;
+  val_ptr->point[1].tbry = val_ptr->point[0].tbry;
+  val_ptr->point[1].sel_feather_radius = val_ptr->point[0].sel_feather_radius;
+
+  val_ptr->point[1].accPosition         = val_ptr->point[0].accPosition;
+  val_ptr->point[1].accOpacity          = val_ptr->point[0].accOpacity;
+  val_ptr->point[1].accSize             = val_ptr->point[0].accSize;
+  val_ptr->point[1].accRotation         = val_ptr->point[0].accRotation;
+  val_ptr->point[1].accPerspective      = val_ptr->point[0].accPerspective;
+  val_ptr->point[1].accSelFeatherRadius = val_ptr->point[0].accSelFeatherRadius;
+
+}  /* end p_add_2nd_controlpoint */
+
+
+/* -------------------------------------
+ * p_init_curr_ptr_with_1st_controlpoint
+ * -------------------------------------
+ * init current values with settings from the 1st controlpoint
+ * in case of single frame processing mode (singleFramePtr != NULL)
+ * init the id of the relevant layer (singleMovObjLayerId) that may be
+ * already part of the processed frame or may be a layer in another image.
+ */
+static void
+p_init_curr_ptr_with_1st_controlpoint(GapMovCurrent *cur_ptr, GapMovValues *val_ptr, GapMovSingleFrame *singleFramePtr)
+{
+  cur_ptr->currX   = (gdouble)val_ptr->point[0].p_x;
+  cur_ptr->currY   = (gdouble)val_ptr->point[0].p_y;
+  cur_ptr->currOpacity  = (gdouble)val_ptr->point[0].opacity;
+  cur_ptr->currWidth    = (gdouble)val_ptr->point[0].w_resize;
+  cur_ptr->currHeight   = (gdouble)val_ptr->point[0].h_resize;
+  cur_ptr->currRotation = (gdouble)val_ptr->point[0].rotation;
+  cur_ptr->currTTLX = (gdouble)val_ptr->point[0].ttlx;
+  cur_ptr->currTTLY = (gdouble)val_ptr->point[0].ttly;
+  cur_ptr->currTTRX = (gdouble)val_ptr->point[0].ttrx;
+  cur_ptr->currTTRY = (gdouble)val_ptr->point[0].ttry;
+  cur_ptr->currTBLX = (gdouble)val_ptr->point[0].tblx;
+  cur_ptr->currTBLY = (gdouble)val_ptr->point[0].tbly;
+  cur_ptr->currTBRX = (gdouble)val_ptr->point[0].tbrx;
+  cur_ptr->currTBRY = (gdouble)val_ptr->point[0].tbry;
+  cur_ptr->currSelFeatherRadius = (gdouble)val_ptr->point[0].sel_feather_radius;
+
+  cur_ptr->accPosition         = val_ptr->point[0].accPosition;
+  cur_ptr->accOpacity          = val_ptr->point[0].accOpacity;
+  cur_ptr->accSize             = val_ptr->point[0].accSize;
+  cur_ptr->accRotation         = val_ptr->point[0].accRotation;
+  cur_ptr->accPerspective      = val_ptr->point[0].accPerspective;
+  cur_ptr->accSelFeatherRadius = val_ptr->point[0].accSelFeatherRadius;
+
+
+  cur_ptr->isSingleFrame = FALSE;
+  cur_ptr->processedLayerId = -1;
+  cur_ptr->singleMovObjLayerId = -1;
+  cur_ptr->singleMovObjImageId = -1;
+  cur_ptr->keep_proportions = FALSE;
+  cur_ptr->fit_width = FALSE;
+  cur_ptr->fit_height = FALSE;
+
+  if (singleFramePtr != NULL)
+  {
+    cur_ptr->isSingleFrame = TRUE;
+    cur_ptr->singleMovObjLayerId = singleFramePtr->drawable_id;
+    cur_ptr->singleMovObjImageId = gimp_drawable_get_image(cur_ptr->singleMovObjLayerId);
+    cur_ptr->keep_proportions = singleFramePtr->keep_proportions;
+    cur_ptr->fit_width = singleFramePtr->fit_width;
+    cur_ptr->fit_height = singleFramePtr->fit_height;
+  }
+
+  val_ptr->tween_image_id = -1;
+  val_ptr->tween_layer_id = -1;
+  val_ptr->trace_image_id = -1;
+  val_ptr->trace_layer_id = -1;
+
+}  /* end p_init_curr_ptr_with_1st_controlpoint */
+
+
+/* --------------------------------
+ * p_duplicate_layer
+ * --------------------------------
+ *
+ */
+static gint32
+p_duplicate_layer(gint32 layerId)
+{
+  gint32 dupLayerId;
+  gint32 imageId;
+
+
+  imageId = gimp_drawable_get_image(layerId);
+  gimp_image_set_active_layer(imageId, layerId);
+  dupLayerId = gimp_layer_copy(layerId);
+  gimp_image_add_layer(imageId, dupLayerId, -1 /* -1 place above active layer */);
+  
+  return(dupLayerId);
+  
+}  /* end p_duplicate_layer */
+
+
+
+
+/* -----------------------
  * p_mov_execute_or_query
+ * -----------------------
  * Copy layer(s) from Sourceimage to given destination frame range,
- * varying koordinates and opacity of the copied layer.
- * To each affected destination frame exactly one copy of a source layer is added.
+ * varying koordinates and opacity and perform other transformations
+ * according to controlpoint settings on the copied layer.
+ * To each affected destination frame one copy of a source layer is added.
+ * (more than one layer is added in case of tween processing and tracelayer processing)
  * The source layer is iterated through all layers of the sourceimage
  * according to stemmode parameter.
  * For the placement the layers act as if their size is equal to their
  * Sourceimages size.
- * ============================================================================
  */
 long
 p_mov_execute_or_query(GapMovData *mov_ptr, GapMovQuery *mov_query)
 {
-   gint l_idx;
    GapMovCurrent l_current_data;
    GapMovCurrent *cur_ptr;
    GapMovValues  *val_ptr;
@@ -1380,54 +1727,7 @@ p_mov_execute_or_query(GapMovData *mov_ptr, GapMovQuery *mov_query)
   if(gap_debug)
   {
     printf("p_mov_execute: values got from dialog:\n");
-    printf("apv_mlayer_image: %ld\n", (long)mov_ptr->val_ptr->apv_mlayer_image);
-    printf("apv_mode: %ld\n", (long)mov_ptr->val_ptr->apv_mode);
-    printf("apv_scale x: %f y:%f\n", (float)mov_ptr->val_ptr->apv_scalex, (float)mov_ptr->val_ptr->apv_scaley);
-    if(mov_ptr->val_ptr->apv_gap_paste_buff)
-    {
-      printf("apv_gap_paste_buf: %s\n", mov_ptr->val_ptr->apv_gap_paste_buff);
-    }
-    else
-    {
-      printf("apv_gap_paste_buf: ** IS NULL ** (do not copy to paste buffer)\n");
-    }
-    printf("src_image_id :%ld\n", (long)mov_ptr->val_ptr->src_image_id);
-    printf("src_layer_id :%ld\n", (long)mov_ptr->val_ptr->src_layer_id);
-    printf("src_handle :%d\n", mov_ptr->val_ptr->src_handle);
-    printf("src_stepmode :%d\n", mov_ptr->val_ptr->src_stepmode);
-    printf("src_paintmode :%d\n", mov_ptr->val_ptr->src_paintmode);
-    printf("clip_to_img :%d\n", mov_ptr->val_ptr->clip_to_img);
-    printf("dst_range_start :%d\n", (int)mov_ptr->val_ptr->dst_range_start);
-    printf("dst_range_end :%d\n", (int)mov_ptr->val_ptr->dst_range_end);
-    printf("dst_layerstack :%d\n", (int)mov_ptr->val_ptr->dst_layerstack);
-    for(l_idx = 0; l_idx <= mov_ptr->val_ptr->point_idx_max; l_idx++)
-    {
-      printf("p_x[%d] :%d\n", l_idx, mov_ptr->val_ptr->point[l_idx].p_x);
-      printf("p_y[%d] :%d\n", l_idx, mov_ptr->val_ptr->point[l_idx].p_y);
-      printf("opacity[%d] :%.3f\n", l_idx, (float)mov_ptr->val_ptr->point[l_idx].opacity);
-      printf("w_resize[%d] :%.3f\n", l_idx, (float)mov_ptr->val_ptr->point[l_idx].w_resize);
-      printf("h_resize[%d] :%.3f\n", l_idx, (float)mov_ptr->val_ptr->point[l_idx].h_resize);
-      printf("rotation[%d] :%.3f\n", l_idx, (float)mov_ptr->val_ptr->point[l_idx].rotation);
-      printf("ttlx[%d] :%.3f\n", l_idx, (float)mov_ptr->val_ptr->point[l_idx].ttlx);
-      printf("ttly[%d] :%.3f\n", l_idx, (float)mov_ptr->val_ptr->point[l_idx].ttly);
-      printf("ttrx[%d] :%.3f\n", l_idx, (float)mov_ptr->val_ptr->point[l_idx].ttrx);
-      printf("ttry[%d] :%.3f\n", l_idx, (float)mov_ptr->val_ptr->point[l_idx].ttry);
-      printf("tblx[%d] :%.3f\n", l_idx, (float)mov_ptr->val_ptr->point[l_idx].tblx);
-      printf("tbly[%d] :%.3f\n", l_idx, (float)mov_ptr->val_ptr->point[l_idx].tbly);
-      printf("tbrx[%d] :%.3f\n", l_idx, (float)mov_ptr->val_ptr->point[l_idx].tbrx);
-      printf("tbry[%d] :%.3f\n", l_idx, (float)mov_ptr->val_ptr->point[l_idx].tbry);
-
-      printf("accPosition        [%d] :%d\n", l_idx, (int)mov_ptr->val_ptr->point[l_idx].accPosition);
-      printf("accOpacity         [%d] :%d\n", l_idx, (int)mov_ptr->val_ptr->point[l_idx].accOpacity);
-      printf("accSize            [%d] :%d\n", l_idx, (int)mov_ptr->val_ptr->point[l_idx].accSize);
-      printf("accRotation        [%d] :%d\n", l_idx, (int)mov_ptr->val_ptr->point[l_idx].accRotation);
-      printf("accPerspective     [%d] :%d\n", l_idx, (int)mov_ptr->val_ptr->point[l_idx].accPerspective);
-      printf("accSelFeatherRadius[%d] :%d\n", l_idx, (int)mov_ptr->val_ptr->point[l_idx].accSelFeatherRadius);
-      
-      printf("keyframe[%d] :%d\n", l_idx, mov_ptr->val_ptr->point[l_idx].keyframe);
-      printf("keyframe_abs[%d] :%d\n", l_idx, mov_ptr->val_ptr->point[l_idx].keyframe_abs);
-    }
-    printf("\n");
+    p_printf_log_parameters(mov_ptr);
   }
 
    l_rc    = 0;
@@ -1444,6 +1744,7 @@ p_mov_execute_or_query(GapMovData *mov_ptr, GapMovQuery *mov_query)
 
    val_ptr->tmpsel_image_id = -1;
    val_ptr->tmpsel_channel_id = -1;
+   val_ptr->twix = 0;
 
    /* set offsets (in cur_ptr)  according to handle mode and src_img dimension */
    gap_mov_exec_set_handle_offsets(val_ptr, cur_ptr);
@@ -1473,35 +1774,8 @@ p_mov_execute_or_query(GapMovData *mov_ptr, GapMovQuery *mov_query)
 
    if(l_points < 2)
    {
-      /* copy point[0] to point [1] because we need at least 2
-       * points for the algorithms below to work.
-       * (simulates a line with length 0, to move along)
-       */
-      if(gap_debug) printf("p_mov_execute: added a 2nd Point\n");
-      val_ptr->point[1].p_x = val_ptr->point[0].p_x;
-      val_ptr->point[1].p_y = val_ptr->point[0].p_y;
-      val_ptr->point[1].opacity = val_ptr->point[0].opacity;
-      val_ptr->point[1].w_resize = val_ptr->point[0].w_resize;
-      val_ptr->point[1].h_resize = val_ptr->point[0].h_resize;
-      val_ptr->point[1].rotation = val_ptr->point[0].rotation;
-      val_ptr->point[1].ttlx = val_ptr->point[0].ttlx;
-      val_ptr->point[1].ttly = val_ptr->point[0].ttly;
-      val_ptr->point[1].ttrx = val_ptr->point[0].ttrx;
-      val_ptr->point[1].ttry = val_ptr->point[0].ttry;
-      val_ptr->point[1].tblx = val_ptr->point[0].tblx;
-      val_ptr->point[1].tbly = val_ptr->point[0].tbly;
-      val_ptr->point[1].tbrx = val_ptr->point[0].tbrx;
-      val_ptr->point[1].tbry = val_ptr->point[0].tbry;
-      val_ptr->point[1].sel_feather_radius = val_ptr->point[0].sel_feather_radius;
-
-      val_ptr->point[1].accPosition         = val_ptr->point[0].accPosition;
-      val_ptr->point[1].accOpacity          = val_ptr->point[0].accOpacity;
-      val_ptr->point[1].accSize             = val_ptr->point[0].accSize;
-      val_ptr->point[1].accRotation         = val_ptr->point[0].accRotation;
-      val_ptr->point[1].accPerspective      = val_ptr->point[0].accPerspective;
-      val_ptr->point[1].accSelFeatherRadius = val_ptr->point[0].accSelFeatherRadius;
-
-      l_points = 2;
+     p_add_2nd_controlpoint(val_ptr);
+     l_points = 2;
    }
 
    startOfSegmentIndex = 0;
@@ -1509,7 +1783,7 @@ p_mov_execute_or_query(GapMovData *mov_ptr, GapMovQuery *mov_query)
 
    cur_ptr->dst_frame_nr = val_ptr->dst_range_start;
    cur_ptr->src_layers = NULL;
- 
+
    if(mov_query == NULL)
    {
      if(mov_ptr->val_ptr->src_stepmode < GAP_STEP_FRAME)
@@ -1574,35 +1848,8 @@ p_mov_execute_or_query(GapMovData *mov_ptr, GapMovQuery *mov_query)
      }
    }
 
-   cur_ptr->currX   = (gdouble)val_ptr->point[0].p_x;
-   cur_ptr->currY   = (gdouble)val_ptr->point[0].p_y;
-   cur_ptr->currOpacity  = (gdouble)val_ptr->point[0].opacity;
-   cur_ptr->currWidth    = (gdouble)val_ptr->point[0].w_resize;
-   cur_ptr->currHeight   = (gdouble)val_ptr->point[0].h_resize;
-   cur_ptr->currRotation = (gdouble)val_ptr->point[0].rotation;
-   cur_ptr->currTTLX = (gdouble)val_ptr->point[0].ttlx;
-   cur_ptr->currTTLY = (gdouble)val_ptr->point[0].ttly;
-   cur_ptr->currTTRX = (gdouble)val_ptr->point[0].ttrx;
-   cur_ptr->currTTRY = (gdouble)val_ptr->point[0].ttry;
-   cur_ptr->currTBLX = (gdouble)val_ptr->point[0].tblx;
-   cur_ptr->currTBLY = (gdouble)val_ptr->point[0].tbly;
-   cur_ptr->currTBRX = (gdouble)val_ptr->point[0].tbrx;
-   cur_ptr->currTBRY = (gdouble)val_ptr->point[0].tbry;
-   cur_ptr->currSelFeatherRadius = (gdouble)val_ptr->point[0].sel_feather_radius;
-
-   cur_ptr->accPosition         = val_ptr->point[0].accPosition;
-   cur_ptr->accOpacity          = val_ptr->point[0].accOpacity;
-   cur_ptr->accSize             = val_ptr->point[0].accSize;
-   cur_ptr->accRotation         = val_ptr->point[0].accRotation;
-   cur_ptr->accPerspective      = val_ptr->point[0].accPerspective;
-   cur_ptr->accSelFeatherRadius = val_ptr->point[0].accSelFeatherRadius;
-
-
-
-   val_ptr->tween_image_id = -1;
-   val_ptr->tween_layer_id = -1;
-   val_ptr->trace_image_id = -1;
-   val_ptr->trace_layer_id = -1;
+   p_init_curr_ptr_with_1st_controlpoint(cur_ptr, val_ptr, NULL);
+
 
    /* create temp images for tween processing and object tracing */
    if(mov_query == NULL)
@@ -1701,7 +1948,7 @@ p_mov_execute_or_query(GapMovData *mov_ptr, GapMovQuery *mov_query)
           (int)l_ptidx, (float)l_fpl);
      }
    }
-   
+
    /* in query mode: find start end end of segment that contains the pointIndexToQuery */
    if(mov_query != NULL)
    {
@@ -1717,16 +1964,16 @@ p_mov_execute_or_query(GapMovData *mov_ptr, GapMovQuery *mov_query)
            mov_query->segmentNumber++;
            mov_query->startOfSegmentIndexToQuery = l_ptidx;
          }
-         
+
          if (l_ptidx > mov_query->pointIndexToQuery)
          {
            mov_query->endOfSegmentIndexToQuery = l_ptidx;
            break;
          }
        }
-       
+
      }
-      
+
    }
 
    if(gap_debug)
@@ -1738,7 +1985,7 @@ p_mov_execute_or_query(GapMovData *mov_ptr, GapMovQuery *mov_query)
      }
    }
 
-  
+
   /* loop for each frame within the range (may step up or down) */
   l_ptidx = 1;
   cur_ptr->dst_frame_nr = val_ptr->dst_range_start;
@@ -1769,12 +2016,12 @@ p_mov_execute_or_query(GapMovData *mov_ptr, GapMovQuery *mov_query)
              startOfSegmentIndex = endOfSegmentIndex;
            }
          }
- 
+
          /* change deltas for next line of the move path */
          if(l_ptidx < l_points-1)
          {
            l_ptidx++;
-           
+
            if(gap_debug)
            {
               printf("p_mov_execute: advance to controlpoint l_ptidx=%d, l_flt_timing[l_ptidx]=%f  startOfSegmentIndex:%d endOfSegmentIndex:%d\n"
@@ -1805,7 +2052,7 @@ p_mov_execute_or_query(GapMovData *mov_ptr, GapMovQuery *mov_query)
       for (val_ptr->twix = val_ptr->tween_steps; val_ptr->twix >= 0; val_ptr->twix--)
       {
         gdouble l_flt_posfactor;
-        
+
         if(l_fpl != 0.0)
         {
             l_flt_posfactor  = (   (((gdouble)l_fridx * l_tw_cnt) - (gdouble)val_ptr->twix)
@@ -1817,9 +2064,9 @@ p_mov_execute_or_query(GapMovData *mov_ptr, GapMovQuery *mov_query)
             l_flt_posfactor = 1.0;
             if(gap_debug) printf("p_mov_execute: ** ERROR l_fpl is 0.0 frames per line\n");
         }
-        
+
         l_flt_posfactor = CLAMP (l_flt_posfactor, 0.0, 1.0);
-      
+
         endOfSegmentIndex = p_findEndOfSegmentIndex(val_ptr, startOfSegmentIndex, l_points);
         frameNrAtEndOfSegment = p_calculate_relframe_nr_at_index(val_ptr, endOfSegmentIndex, l_frames);
 
@@ -1836,20 +2083,20 @@ p_mov_execute_or_query(GapMovData *mov_ptr, GapMovQuery *mov_query)
              );
 
 
-        
+
         if(mov_query != NULL)
         {
           /* we run in query mode, just check if controlpoint for query is reached
            * and stop (without rendering)
            */
-          if(gap_debug) 
+          if(gap_debug)
           {
             printf("BREAK check: endOfSegmentIndexToQuery:%d  l_ptidx:%d\n"
                  , (int)mov_query->endOfSegmentIndexToQuery
                  , (int)l_ptidx
                  );
           }
-          
+
           if(l_ptidx > mov_query->endOfSegmentIndexToQuery)
           {
             return (l_rc);
@@ -1883,7 +2130,7 @@ p_mov_execute_or_query(GapMovData *mov_ptr, GapMovQuery *mov_query)
         }
 
       }  /* end tweenindex subloop */
-      
+
       /* advance to next path segment */
       if(l_fridx >= frameNrAtEndOfSegment)
       {
@@ -1935,34 +2182,457 @@ p_mov_execute_or_query(GapMovData *mov_ptr, GapMovQuery *mov_query)
 
 }       /* end p_mov_execute_or_query */
 
-/* ============================================================================
- * p_mov_execute
- * Copy layer(s) from Sourceimage to given destination frame range,
- * varying koordinates and opacity of the copied layer.
- * To each affected destination frame exactly one copy of a source layer is added.
- * The source layer is iterated through all layers of the sourceimage
- * according to stemmode parameter.
- * For the placement the layers act as if their size is equal to their
- * Sourceimages size.
- * ============================================================================
+
+
+/* ---------------------------
+ * p_mov_execute_singleframe
+ * ---------------------------
+ * transform and move layer (specified by mov_ptr->singleFramePtr->drawable_id)
+ * according to move path controlpoints in one destination frame
+ * at the specified pahse (e.g. frame number within a frame range)
+ *
+ * in case the  mov_ptr->singleFramePtr->drawable_id is NOT already part
+ * of the processed frame it will be copied to the frame and
+ * the transformation is done on the copy 
+ *  -- typical secnario when called from storyboard processor --
+ * Otherwise transformation is done on the original
+ *  -- typical scenario when called via PDB (as filter in the modify frames feature)
+ *
+ * return the layer_id of the processed resulting layer or -1 on error
+ * (note that additional layers will be created when tween processing is active)
  */
-static long
-p_mov_execute(GapMovData *mov_ptr)
+static gint32
+p_mov_execute_singleframe(GapMovData *mov_ptr)
 {
-  GapMovQuery *mov_query;
+   GapMovCurrent l_current_data;
+   GapMovCurrent *cur_ptr;
+   GapMovValues  *val_ptr;
 
-  mov_query = NULL;
-  return(p_mov_execute_or_query(mov_ptr, mov_query));
-}
+   gdouble  l_percentage;
+   gdouble  l_fpl;             /* frames_per_line */
+   long     l_frame_step;
+   gdouble  l_frames;
+   long     l_cnt;
+   long     l_points;
+   long     l_ptidx;
+   long     l_prev_keyptidx;
+   long     l_fridx;
+   gdouble  l_flt_count;
+   gint32   l_rc;
+   gint     l_idk;
+   gint     l_prev_keyframe;
+   gint     l_apv_layerstack;
+   gdouble  l_flt_timing[GAP_MOV_MAX_POINT];   /* timing table in relative frame numbers (0.0 == the first handled frame) */
 
+   gint startOfSegmentIndex;
+   gint endOfSegmentIndex;
+   gint frameNrAtEndOfSegment;
+   GapMovSingleFrame *singleFramePtr;
+   gint32            *tweenLayerIdTable;
 
 
+  tweenLayerIdTable = NULL;
+  singleFramePtr = mov_ptr->singleFramePtr;
+  if(singleFramePtr == NULL)
+  {
+    printf("ERROR p_mov_execute_singleframe must not be called wit singleFramePtr NULL!\n");
+  }
 
-/* ============================================================================
- * gap_mov_exec_anim_preview
- *   Generate an animated preview for the move path
- * ============================================================================
- */
+
+  frameNrAtEndOfSegment = 0;
+  l_apv_layerstack = 0;
+  l_percentage = 0.0;
+
+  if(mov_ptr->dst_ainfo_ptr->run_mode == GIMP_RUN_INTERACTIVE)
+  {
+    gimp_progress_init( _("Transforming layer according to move path frame_phase..."));
+  }
+
+  if(gap_debug)
+  {
+    printf("p_mov_execute_singleframe: values got from dialog:\n");
+    p_printf_log_parameters(mov_ptr);
+  }
+
+   l_rc    = 0;
+   cur_ptr = &l_current_data;
+   val_ptr = mov_ptr->val_ptr;
+
+   if(gap_image_is_alive(val_ptr->tmpsel_image_id))
+   {
+     gimp_image_delete(val_ptr->tmpsel_image_id);
+   }
+
+   val_ptr->tmpsel_image_id = -1;
+   val_ptr->tmpsel_channel_id = -1;
+   val_ptr->twix = 0;
+
+
+   /* only ascending range processing in single frames mode */
+   l_frame_step = 1;
+   if(singleFramePtr->total_frames > 0)
+   {
+     l_cnt = singleFramePtr->total_frames;
+   }
+   else
+   {
+     l_cnt = 1 + abs(val_ptr->dst_range_end - val_ptr->dst_range_start);
+   }
+
+   l_frames = (gdouble)l_cnt;              /* nr. of affected frames */
+   l_points = val_ptr->point_idx_max +1;   /* nr. of available points */
+
+   if(l_points > l_frames)
+   {
+      /* cut off some points if we got more than frames */
+      l_points = l_cnt;
+   }
+
+   if(l_points < 2)
+   {
+     p_add_2nd_controlpoint(val_ptr);
+     l_points = 2;
+   }
+
+   startOfSegmentIndex = 0;
+   endOfSegmentIndex = l_points -1;  /* initial value in case all points are in only 1 segment */
+
+   cur_ptr->dst_frame_nr = 1;
+   cur_ptr->src_layers = NULL;
+   cur_ptr->src_last_layer = -1;
+   p_init_curr_ptr_with_1st_controlpoint(cur_ptr, val_ptr, singleFramePtr);
+
+   /* set offsets (in cur_ptr)  according to handle mode and src_img dimension */
+   gap_mov_exec_set_handle_offsets_singleframe(val_ptr, cur_ptr);
+
+   /* mov_ptr->val_ptr->src_stepmode is ignored in singleframes mode
+    * (e.g. behaves like GAP_STEP_FRAME_NONE)
+    */
+   {
+     gint32        l_sel_channel_id;
+     gboolean      l_all_empty;
+
+     if(val_ptr->src_selmode != GAP_MOV_SEL_IGNORE)
+     {
+       l_all_empty = FALSE;
+       if(gimp_selection_is_empty(cur_ptr->singleMovObjImageId))
+       {
+         l_all_empty = TRUE;
+       }
+       l_sel_channel_id = gimp_image_get_selection(cur_ptr->singleMovObjImageId);
+       gap_mov_render_create_or_replace_tempsel_image(l_sel_channel_id, val_ptr, l_all_empty);
+     }
+
+   }
+
+
+   /* create duplicate layers for tween processing (or render first frame) */
+   {
+     gint32 iTween;
+
+
+     /* optional create tweens (n copies of the singleMovObjLayerId) */
+     if (singleFramePtr->frame_phase > 1)
+     {
+       tweenLayerIdTable = g_new(gint32, val_ptr->tween_steps +1);
+       tweenLayerIdTable[0] = cur_ptr->singleMovObjLayerId;
+       for(iTween = 1; iTween <= val_ptr->tween_steps; iTween++)
+       {
+         /* make copies of the cur_ptr->singleMovObjLayerId (one copy foreach tween) */
+         tweenLayerIdTable[iTween] = p_duplicate_layer(cur_ptr->singleMovObjLayerId);
+       }
+     }
+     else
+     {
+       /* RENDER the 1.st frame outside the frameindex loop,
+        * ------     ----
+        * without care about tweens
+        */
+       l_rc = p_mov_call_render(mov_ptr, cur_ptr, l_apv_layerstack);
+       if(l_rc >= 0)
+       {
+         l_rc = cur_ptr->processedLayerId;
+       }
+       return (l_rc);
+     }
+
+   }
+
+
+   /* how many frames are affected from one line of the moving path */
+   l_fpl = ((gdouble)l_frames - 1.0) / ((gdouble)(l_points -1));
+   if(gap_debug)
+   {
+     printf("p_mov_execute_singleframe: initial l_fpl=%f\n", l_fpl);
+   }
+
+   /* calculate l_flt_timing controlpoint timing table considering keyframes */
+   l_prev_keyptidx = 0;
+   l_prev_keyframe = 0;
+   l_flt_timing[0] = 0.0;
+   l_flt_timing[l_points -1] = l_frames -1;
+   l_flt_count = 0.0;
+   for(l_ptidx=1;  l_ptidx < l_points - 1; l_ptidx++)
+   {
+     /* search for keyframes */
+     if(l_ptidx > l_prev_keyptidx)
+     {
+       for(l_idk = l_ptidx; l_idk < l_points; l_idk++)
+       {
+          if(l_idk == l_points -1)
+          {
+            /* last point is always an implicite  keyframe */
+            l_fpl = ((gdouble)((l_frames -1) - l_prev_keyframe)) / ((gdouble)((l_idk -  l_ptidx) +1));
+            l_prev_keyframe = l_frames -1;
+
+            l_prev_keyptidx = l_idk;
+            if(gap_debug) printf("p_mov_execute_singleframe: last point is implicite keyframe l_fpl=%f\n", l_fpl);
+            break;
+          }
+          else
+          {
+            if (val_ptr->point[l_idk].keyframe > 0)
+            {
+              /* found a keyframe, have to recalculate frames_per_line */
+              l_fpl = ((gdouble)(val_ptr->point[l_idk].keyframe - l_prev_keyframe)) / ((gdouble)((l_idk -  l_ptidx) +1));
+              l_prev_keyframe = val_ptr->point[l_idk].keyframe;
+
+              l_prev_keyptidx = l_idk;
+              if(gap_debug) printf("p_mov_execute_singleframe: keyframe l_fpl=%f\n", l_fpl);
+              break;
+            }
+          }
+       }
+     }
+     l_flt_count += l_fpl;
+     l_flt_timing[l_ptidx] = l_flt_count;
+
+     if(l_fpl < 1.0)
+     {
+        printf("p_mov_execute_singleframe: ** Error frames per line at point[%d] = %f  (is less than 1.0 !!)\n",
+          (int)l_ptidx, (float)l_fpl);
+     }
+   }
+
+   if(gap_debug)
+   {
+     printf("p_mov_execute_singleframe: --- CONTROLPOINT relative frametiming TABLE -----\n");
+     for(l_ptidx=0;  l_ptidx < l_points; l_ptidx++)
+     {
+       printf("p_mov_execute_singleframe: l_flt_timing[%02d] = %f\n", (int)l_ptidx, (float)l_flt_timing[l_ptidx]);
+     }
+   }
+
+
+  /* loop for each frame within the range (may step up or down) */
+  l_ptidx = 1;
+  cur_ptr->dst_frame_nr = 1;
+
+  /* frameindex loop */
+  for(l_fridx = 1; l_fridx < l_cnt; l_fridx++)
+  {
+     gdouble  l_tw_cnt;   /* number of tweens (including the real frame) 1 if no tweens present */
+     gboolean isProcessingFramePhase;
+
+     isProcessingFramePhase = FALSE;
+     if((singleFramePtr->frame_phase == l_fridx +1)
+     || (l_fridx +1 == l_cnt))
+     {
+       isProcessingFramePhase = TRUE;
+     }
+
+     if(gap_debug)
+     {
+       printf("\np_mov_execute_singleframe: l_fridx=%ld, l_flt_timing[l_ptidx]=%f, l_rc=%d l_ptidx=%d, l_prev_keyptidx=%d\n",
+                           l_fridx, (float)l_flt_timing[l_ptidx], (int)l_rc, (int)l_ptidx, (int)l_prev_keyptidx);
+       if (isProcessingFramePhase)
+       {
+         printf("Now l_fridx is the frame index that triggers processing relevant Single Frame Phase\n");
+       }
+     }
+     if(l_rc != 0)
+     {
+       break;
+     }
+
+      /* advance frame_nr, (1st frame was done outside this loop) */
+      cur_ptr->dst_frame_nr += l_frame_step;  /* +1  or -1 */
+
+      if((gdouble)l_fridx > l_flt_timing[l_ptidx])
+      {
+         /*  fix for object jumps forth and back as reported in #607927 */
+         if(val_ptr->point[l_ptidx].keyframe > 0)
+         {
+           if((endOfSegmentIndex != l_points -1) && (endOfSegmentIndex > 0))
+           {
+             startOfSegmentIndex = endOfSegmentIndex;
+           }
+         }
+
+         /* change deltas for next line of the move path */
+         if(l_ptidx < l_points-1)
+         {
+           l_ptidx++;
+
+           if(gap_debug)
+           {
+              printf("p_mov_execute_singleframe: advance to controlpoint l_ptidx=%d, l_flt_timing[l_ptidx]=%f  startOfSegmentIndex:%d endOfSegmentIndex:%d\n"
+                     , (int)l_ptidx
+                     , (float)l_flt_timing[l_ptidx]
+                     , (int)startOfSegmentIndex
+                     , (int)endOfSegmentIndex
+                     );
+           }
+         }
+         else
+         {
+           if(gap_debug)
+           {
+             printf("p_mov_execute_singleframe: ** ERROR overflow l_ptidx=%d\n", (int)l_ptidx);
+           }
+         }
+      }
+
+      l_fpl = (l_flt_timing[l_ptidx] - l_flt_timing[l_ptidx -1]); /* float frames per line */
+
+      l_tw_cnt = (gdouble)(val_ptr->tween_steps +1);
+
+      /* loop for the tweens
+       * (tweens are virtual frames between the previous and the current frame)
+       * when val_ptr->twix is down to 0, the current real frame is reached
+       */
+      for (val_ptr->twix = val_ptr->tween_steps; val_ptr->twix >= 0; val_ptr->twix--)
+      {
+        gdouble l_flt_posfactor;
+
+        if(l_fpl != 0.0)
+        {
+            l_flt_posfactor  = (   (((gdouble)l_fridx * l_tw_cnt) - (gdouble)val_ptr->twix)
+                               - (l_flt_timing[l_ptidx -1] * l_tw_cnt)
+                             ) / (l_fpl * l_tw_cnt);
+        }
+        else
+        {
+            l_flt_posfactor = 1.0;
+            if(gap_debug) printf("p_mov_execute_singleframe: ** ERROR l_fpl is 0.0 frames per line\n");
+        }
+
+        l_flt_posfactor = CLAMP (l_flt_posfactor, 0.0, 1.0);
+
+        endOfSegmentIndex = p_findEndOfSegmentIndex(val_ptr, startOfSegmentIndex, l_points);
+        frameNrAtEndOfSegment = p_calculate_relframe_nr_at_index(val_ptr, endOfSegmentIndex, l_frames);
+
+        frameNrAtEndOfSegment = p_calculate_settings_for_current_FrameTween(val_ptr, cur_ptr
+             , l_fpl
+             , l_fridx
+             , l_ptidx
+             , l_flt_posfactor
+             , l_frames
+             , l_points
+             , startOfSegmentIndex
+             , endOfSegmentIndex
+             , NULL  /* mov_query */
+             );
+
+
+
+        if(l_frame_step < 0)
+        {
+            /* if we step down, we have to insert the layer
+             * as lowest layer in the existing layerstack
+             * of the animated preview multilayer image.
+             * (if we step up, we always use 0 as l_apv_layerstack,
+             *  that means always insert on top of the layerstack)
+             */
+            l_apv_layerstack++;
+        }
+
+        if (isProcessingFramePhase)
+        {
+          cur_ptr->singleMovObjLayerId = tweenLayerIdTable[val_ptr->twix];
+
+
+          /* RENDER add current src_layer to current frame */
+          l_rc = p_mov_call_render(mov_ptr, cur_ptr, l_apv_layerstack);
+        }
+
+      }  /* end tweenindex subloop */
+
+      if (isProcessingFramePhase)
+      {
+        /* returncode handling (id of the resulting processed layer) */
+        if(l_rc >= 0)
+        {
+          l_rc = cur_ptr->processedLayerId;
+        }
+        /* stop after the relevant single frame (and its tweens) was already processed */
+        break;
+      }
+
+      /* advance to next path segment */
+      if(l_fridx >= frameNrAtEndOfSegment)
+      {
+         startOfSegmentIndex = endOfSegmentIndex;
+      }
+
+      ///* show progress */  // TODO
+      //if((mov_ptr->dst_ainfo_ptr->run_mode == GIMP_RUN_INTERACTIVE)
+      //&& (mov_query == NULL))
+      //{
+      //  l_percentage = (gdouble)l_fridx / (gdouble)(l_cnt -1);
+      //  gimp_progress_update (l_percentage);
+      //}
+
+   }  /* end frameindex loop */
+
+   /* delete the temp selection image */
+   if(gap_image_is_alive(val_ptr->tmpsel_image_id))
+   {
+     gimp_image_delete(val_ptr->tmpsel_image_id);
+   }
+
+   val_ptr->tmpsel_image_id = -1;
+   val_ptr->tmpsel_channel_id = -1;
+
+   if (tweenLayerIdTable != NULL)
+   {
+     g_free(tweenLayerIdTable);
+   }
+
+   return l_rc;
+
+}       /* end p_mov_execute_singleframe */
+
+
+
+/* ============================================================================
+ * p_mov_execute
+ * Copy layer(s) from Sourceimage to given destination frame range,
+ * varying koordinates and opacity of the copied layer.
+ * To each affected destination frame exactly one copy of a source layer is added.
+ * The source layer is iterated through all layers of the sourceimage
+ * according to stemmode parameter.
+ * For the placement the layers act as if their size is equal to their
+ * Sourceimages size.
+ * ============================================================================
+ */
+static long
+p_mov_execute(GapMovData *mov_ptr)
+{
+  GapMovQuery *mov_query;
+
+  mov_query = NULL;
+  return(p_mov_execute_or_query(mov_ptr, mov_query));
+}
+
+
+
+
+/* ============================================================================
+ * gap_mov_exec_anim_preview
+ *   Generate an animated preview for the move path
+ * ============================================================================
+ */
 gint32
 gap_mov_exec_anim_preview(GapMovValues *pvals_orig, GapAnimInfo *ainfo_ptr, gint preview_frame_nr)
 {
@@ -2275,7 +2945,7 @@ gap_mov_exec_gap_save_pointfile(char *filename, GapMovValues *pvals)
        */
       if (pvals->point[l_idx].accPosition != 0)
       {
-        if (l_idx == 0) 
+        if (l_idx == 0)
         {
           writeAccelerationCharacteristics = TRUE;
         }
@@ -2287,7 +2957,7 @@ gap_mov_exec_gap_save_pointfile(char *filename, GapMovValues *pvals)
             writeAccelerationCharacteristics = TRUE;
           }
         }
-        
+
         if (writeAccelerationCharacteristics == TRUE)
         {
           optional_params_indicator += 6;
@@ -2327,7 +2997,7 @@ gap_mov_exec_gap_save_pointfile(char *filename, GapMovValues *pvals)
       if(writeAccelerationCharacteristics == TRUE)
       {
         fprintf(l_fp, " %02d %02d %02d %02d %02d %02d"
-           , (int)pvals->point[l_idx].accPosition 
+           , (int)pvals->point[l_idx].accPosition
            , (int)pvals->point[l_idx].accOpacity
            , (int)pvals->point[l_idx].accSize
            , (int)pvals->point[l_idx].accRotation
@@ -2395,11 +3065,11 @@ gap_mov_exec_gap_load_pointfile(char *filename, GapMovValues *pvals)
          l_cnt = gap_base_sscan_flt_numbers(l_ptr, &l_farr[0], MAX_NUMVALUES_PER_LINE);
          l_i1 = (gint)l_farr[0];
          l_i2 = (gint)l_farr[1];
-         
+
          if(gap_debug)
          {
             gint ii;
-            
+
             printf("scanned %d numbers\n", l_cnt);
             for(ii=0; ii < l_cnt; ii++)
             {
@@ -2503,7 +3173,7 @@ gap_mov_exec_gap_load_pointfile(char *filename, GapMovValues *pvals)
                    acc_idx = 8;
                    break;
                case 7:
-                   /* have six accelerate characteristic values 
+                   /* have six accelerate characteristic values
                     * and one keyframe value
                     */
                    acc_idx = 8;
@@ -2948,6 +3618,7 @@ gap_mov_exec_move_path(GimpRunMode run_mode, gint32 image_id, GapMovValues *pval
   if(ainfo_ptr != NULL)
   {
     l_mov_data.val_ptr = pvals;
+    l_mov_data.singleFramePtr = NULL;
     if(NULL != l_mov_data.val_ptr)
     {
       if (0 == gap_lib_dir_ainfo(ainfo_ptr))
@@ -3055,7 +3726,7 @@ gap_mov_exec_query(GapMovValues *val_ptr, GapAnimInfo *ainfo_ptr, GapMovQuery *m
 
   l_mov_ptr = &l_mov_data;
   l_pvals = &l_mov_vals;
-  
+
   if((mov_query != NULL) && (val_ptr != NULL))
   {
     /* init query results */
@@ -3077,7 +3748,7 @@ gap_mov_exec_query(GapMovValues *val_ptr, GapAnimInfo *ainfo_ptr, GapMovQuery *m
     l_pvals->cache_tmp_layer_id  = -1;
     l_pvals->cache_frame_number  = -1;
     l_pvals->cache_ainfo_ptr = NULL;
-    
+
     p_mov_execute_or_query(l_mov_ptr, mov_query);
     if(mov_query->minSpeedInPixelsPerFrame < 0)
     {
@@ -3087,6 +3758,9 @@ gap_mov_exec_query(GapMovValues *val_ptr, GapAnimInfo *ainfo_ptr, GapMovQuery *m
 }
 
 
+
+
+
 /* ============================================================================
  * gap_mov_exec_set_handle_offsets
  *  set handle offsets according to handle mode and src image dimensions
@@ -3134,3 +3808,261 @@ void gap_mov_exec_set_handle_offsets(GapMovValues *val_ptr, GapMovCurrent *cur_p
    }
 }       /* end gap_mov_exec_set_handle_offsets */
 
+
+
+/* ------------------------------------
+ * gap_mov_exec_new_GapMovValues
+ * ------------------------------------
+ */
+GapMovValues *gap_mov_exec_new_GapMovValues()
+{
+  GapMovValues *pvals;
+
+  pvals = g_new (GapMovValues, 1);
+
+  pvals->version = GAP_MOV_INT_VERSION;
+  pvals->recordedFrameWidth = 0;     /* witdh of the frame (at recording time of the move path settings) */
+  pvals->recordedFrameHeight = 0;    /* height of the frame (at recording time of the move path settings) */
+  pvals->recordedObjWidth = 0;
+  pvals->recordedObjHeight = 0;
+  pvals->total_frames = 0;
+  pvals->src_layerstack = 0;
+  pvals->src_filename = NULL;
+
+
+
+  pvals->dst_image_id = -1;
+  pvals->tmp_image_id = -1;
+  pvals->tmpsel_image_id = -1;
+  pvals->tmpsel_channel_id = -1;
+  pvals->apv_mode = 0;
+  pvals->apv_src_frame = -1;
+  pvals->apv_mlayer_image =  -1;
+  pvals->apv_gap_paste_buff = NULL;
+  pvals->apv_framerate = 24;
+  pvals->apv_scalex = 100.0;
+  pvals->apv_scaley = 100.0;
+  pvals->cache_src_image_id = -1;
+  pvals->cache_tmp_image_id = -1;
+  pvals->cache_tmp_layer_id = -1;
+  pvals->cache_frame_number = -1;
+  pvals->cache_ainfo_ptr = NULL;
+  pvals->point_idx = 0;
+  pvals->point_idx_max = 0;
+  pvals->src_apply_bluebox  = 0;
+  pvals->bbp  = NULL;
+
+  pvals->step_speed_factor = 1.0;
+  pvals->tracelayer_enable = FALSE;
+  pvals->trace_opacity_initial = 100.0;
+  pvals->trace_opacity_desc = 80.0;
+  pvals->tween_steps = 0;
+  pvals->tween_opacity_initial = 80.0;
+  pvals->tween_opacity_desc = 80.0;
+
+  return(pvals);
+
+}  /* end gap_mov_exec_new_GapMovValues */
+
+
+/* ----------------------------------
+ * gap_mov_exec_move_path_singleframe
+ * ----------------------------------
+ * return image_id (of the new loaded current frame) on success
+ *        or -1 on errors
+ */
+gint32
+gap_mov_exec_move_path_singleframe(GimpRunMode run_mode, gint32 image_id
+   , GapMovValues *pvals, GapMovSingleFrame *singleFramePtr)
+{
+  gint32 l_rc;
+  GapAnimInfo *ainfo_ptr;
+  GapMovData  l_mov_data;
+
+  l_rc = -1;
+  ainfo_ptr = gap_lib_alloc_ainfo(image_id, run_mode);
+  if(ainfo_ptr != NULL)
+  {
+    l_mov_data.val_ptr = pvals;
+    l_mov_data.singleFramePtr = singleFramePtr;
+
+    if(NULL != l_mov_data.val_ptr)
+    {
+      l_mov_data.val_ptr->cache_src_image_id = -1;
+      l_mov_data.dst_ainfo_ptr = ainfo_ptr;
+
+      if (run_mode == GIMP_RUN_INTERACTIVE)
+      {
+         /* Dialog for singleframes mode
+          */
+         l_rc = gap_mov_dlg_move_dialog_singleframe (singleFramePtr);
+      }
+      else
+      {
+         l_rc = 0;
+      }
+
+
+      if(l_rc >= 0)
+      {
+         /* get parameters and controlpoints from xml_paramfile */
+         if (singleFramePtr->xml_paramfile != NULL)
+         {
+           gboolean  isXmlLoadOk;
+
+           isXmlLoadOk =  gap_mov_xml_par_load(singleFramePtr->xml_paramfile
+                                   , pvals
+                                     , gimp_image_width(image_id)
+                                     , gimp_image_height(image_id)
+                                     );
+
+
+           if (!isXmlLoadOk)
+           {
+             l_rc = -1;
+             if (run_mode != GIMP_RUN_NONINTERACTIVE)
+             {
+               g_message(("could not load MovePath settings from file: %s")
+                        , singleFramePtr->xml_paramfile);
+             }
+             printf("Execution Error: could not load MovePath settings from file: %s\n",
+                     singleFramePtr->xml_paramfile);
+           }
+           
+           //if(gap_debug)
+           //{
+           //  gap_mov_xml_par_save("pvals_after_load.xml", pvals);
+           //}
+
+         }
+
+      }
+
+
+      if(l_rc >= 0)
+      {
+          /* START of the singleframe PROCESSING */
+          l_rc = p_mov_execute_singleframe(&l_mov_data);
+      }
+
+      if(l_mov_data.val_ptr->cache_tmp_image_id >= 0)
+      {
+         if(gap_debug)
+         {
+            printf("gap_move: DELETE cache_tmp_image_id:%d\n",
+                     (int)l_mov_data.val_ptr->cache_tmp_image_id);
+         }
+         /* destroy the cached frame image */
+         gimp_image_delete(l_mov_data.val_ptr->cache_tmp_image_id);
+      }
+
+    }
+
+    gap_lib_free_ainfo(&ainfo_ptr);
+  }
+
+  if (l_rc < 0)
+  {
+    return -1;
+  }
+
+  return(l_rc);
+}       /* end gap_mov_exec_move_path_singleframe */
+
+
+/* --------------------------------------
+ * gap_mov_exec_check_valid_xml_paramfile
+ * --------------------------------------
+ * return TRUE on valid movepat xml parameterfile
+ */
+gboolean 
+gap_mov_exec_check_valid_xml_paramfile(const char *filename)
+{
+  gboolean  isXmlLoadOk;
+  GapMovValues *pvals;
+
+  isXmlLoadOk = FALSE;
+  pvals = gap_mov_exec_new_GapMovValues();
+  
+  if(filename != NULL)
+  {
+    if(*filename != '\0')
+    {
+      isXmlLoadOk =  gap_mov_xml_par_load(filename
+                                     , pvals
+                                     , 640 /* fake actual width */
+                                     , 480 /* fake actual height */
+                                     );
+    }
+  }
+  g_free(pvals);
+
+  return (isXmlLoadOk);
+
+}  /* end gap_mov_exec_check_valid_xml_paramfile */
+
+
+/* ---------------------------------------------
+ * gap_mov_exec_move_path_singleframe_directcall
+ * ---------------------------------------------
+ * this procedure renders one frame of a movepath sequence.
+ * it is typically called by the storyboard processor.
+ * return the processed layer id
+ */
+gint32
+gap_mov_exec_move_path_singleframe_directcall(gint32 frame_image_id
+       , gint32 drawable_id
+       , gboolean keep_proportions
+       , gboolean fit_width
+       , gboolean fit_height
+       , gint32 frame_phase
+       , const char *xml_paramfile)
+{
+  GapMovValues *pvals;
+  gint32        result_layer_id;
+  GapMovSingleFrame singleframevals;
+
+
+  result_layer_id = -1;
+
+  /* init pvals with default values (to provide defined settings
+   * for optional data that may not be present in the xml parameter file)
+   */
+  pvals = gap_mov_exec_new_GapMovValues();
+  pvals->dst_image_id = frame_image_id;
+  
+  
+  singleframevals.drawable_id = drawable_id;
+  singleframevals.frame_phase = frame_phase;
+  singleframevals.total_frames = -1;          /* get path length (total frames) from xml paramfile */
+  singleframevals.keep_proportions = keep_proportions;
+  singleframevals.fit_width = fit_width;
+  singleframevals.fit_height = fit_height;
+  g_snprintf(&singleframevals.xml_paramfile[0], sizeof(singleframevals.xml_paramfile), "%s", xml_paramfile);
+
+  if(gap_debug)
+  {
+    printf("\ngap_mov_exec_move_path_singleframe_directcall:"
+           " drawable_id:%d frame_image_id:%d frame_phase:%d\n"
+       ,(int)singleframevals.drawable_id
+       ,(int)frame_image_id
+       ,(int)singleframevals.frame_phase
+       );
+  }
+
+
+  result_layer_id = gap_mov_exec_move_path_singleframe(GIMP_RUN_NONINTERACTIVE
+                            , frame_image_id, pvals, &singleframevals);
+  if(gap_debug)
+  {
+    printf("gap_mov_exec_move_path_singleframe_directcall:"
+           " result_layer_id:%d orig_drawable_id:%d\n"
+       ,(int)result_layer_id
+       ,(int)drawable_id
+       );
+  }
+  
+  g_free(pvals);
+  return (result_layer_id);
+
+}  /* end gap_mov_exec_move_path_singleframe_directcall  */
diff --git a/gap/gap_mov_exec.h b/gap/gap_mov_exec.h
index 81dca03..53e1948 100644
--- a/gap/gap_mov_exec.h
+++ b/gap/gap_mov_exec.h
@@ -43,6 +43,9 @@
 
 gint32  gap_mov_exec_move_path(GimpRunMode run_mode, gint32 image_id, GapMovValues *pvals, gchar *pointfile, gint rotation_follow, gdouble startangle);
 gint32  gap_mov_exec_anim_preview(GapMovValues *pvals_orig, GapAnimInfo *ainfo_ptr, gint preview_frame_nr);
+gint32  gap_mov_exec_move_path_singleframe(GimpRunMode run_mode, gint32 image_id
+              , GapMovValues *pvals, GapMovSingleFrame *singleFramePtr);
+
 
 gchar  *gap_mov_exec_chk_keyframes(GapMovValues *pvals);
 gint    gap_mov_exec_conv_keyframe_to_rel(gint abs_keyframe, GapMovValues *pvals);
@@ -53,6 +56,26 @@ void    gap_mov_exec_calculate_rotate_follow(GapMovValues *pvals, gdouble starta
 void    gap_mov_exec_set_handle_offsets(GapMovValues *val_ptr, GapMovCurrent *cur_ptr);
 void    gap_mov_exec_query(GapMovValues *val_ptr, GapAnimInfo *ainfo_ptr, GapMovQuery *mov_query);
 
+GapMovValues *gap_mov_exec_new_GapMovValues();
+
+gboolean  gap_mov_exec_check_valid_xml_paramfile(const char *filename);
+
+/* ---------------------------------------------
+ * gap_mov_exec_move_path_singleframe_directcall
+ * ---------------------------------------------
+ * this procedure renders one frame of a movepath sequence.
+ * it is typically called by the storyboard processor.
+ * return the processed layer id
+ */
+gint32  gap_mov_exec_move_path_singleframe_directcall(gint32 frame_image_id
+          , gint32 drawable_id
+          , gboolean keep_proportions
+          , gboolean fit_width
+          , gboolean fit_height
+          , gint32 frame_phase
+          , const char *xml_paramfile
+          );
+
 #endif
 
 
diff --git a/gap/gap_mov_main.c b/gap/gap_mov_main.c
new file mode 100644
index 0000000..fa3e74c
--- /dev/null
+++ b/gap/gap_mov_main.c
@@ -0,0 +1,713 @@
+/* gap_mov_main.c
+ * 2011.03.09 hof (Wolfgang Hofer)
+ *
+ * GAP ... Gimp Animation Package
+ *
+ * This Module contains:
+ * - MAIN of the GAP Move Path Plugin (and its non interactive variants)
+ * - query   registration of GAP Procedures (Video Menu) in the PDB
+ * - run     invoke the selected GAP procedure by its PDB name
+ *
+ */
+/* 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:
+ * 2011/03/09  hof: created (moved already existing code from gap_main.c to this new module)
+ */
+
+
+#include "config.h"
+
+/* SYTEM (UNIX) includes */
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+/* GIMP includes */
+#include "gtk/gtk.h"
+#include "libgimp/gimp.h"
+#include "libgimp/gimpui.h"
+
+/* GAP includes */
+#include "gap_lib.h"
+#include "gimplastvaldesc.h"
+#include "gap_image.h"
+#include "gap_base_ops.h"
+#include "gap_lock.h"
+#include "gap_mov_exec.h"
+
+#include "gap-intl.h"
+
+
+/* ------------------------
+ * global gap DEBUG switch
+ * ------------------------
+ */
+
+/* int gap_debug = 1; */    /* print debug infos */
+/* int gap_debug = 0; */    /* 0: dont print debug infos */
+
+int gap_debug = 0;
+
+#define PLUGIN_NAME_GAP_MOVE                 "plug_in_gap_move"
+#define PLUGIN_NAME_GAP_MOVE_PATH_EXT        "plug_in_gap_move_path_ext"
+#define PLUGIN_NAME_GAP_MOVE_PATH_EXT2       "plug_in_gap_move_path_ext2"
+#define PLUGIN_NAME_GAP_MOVE_SINGLEFRAME     "plug-in-move-path-singleframe"
+
+
+static void query(void);
+static void run(const gchar *name
+              , gint n_params
+              , const GimpParam *param
+              , gint *nreturn_vals
+              , GimpParam **return_vals);
+
+GimpPlugInInfo PLUG_IN_INFO =
+{
+  NULL,  /* init_proc */
+  NULL,  /* quit_proc */
+  query, /* query_proc */
+  run,   /* run_proc */
+};
+
+  static GapMovSingleFrame singleframevals;
+
+  static GimpParamDef return_std[] =
+  {
+    { GIMP_PDB_IMAGE, "curr_frame_image", "the resulting current frame image id" }
+  };
+  static int nreturn_std = G_N_ELEMENTS(return_std) ;
+
+  static GimpParamDef return_single[] =
+  {
+    { GIMP_PDB_DRAWABLE, "resulting_layer", "the resulting processed layer id" }
+  };
+  static int nreturn_single = G_N_ELEMENTS(return_single) ;
+
+
+  static GimpParamDef args_mov[] =
+  {
+    {GIMP_PDB_INT32, "run_mode", "Interactive"},
+    {GIMP_PDB_IMAGE, "image", "Input image (one of the video frames)"},
+    {GIMP_PDB_DRAWABLE, "drawable", "Input drawable (unused)"},
+  };
+
+  static GimpParamDef args_mov_path_ext[] =
+  {
+    {GIMP_PDB_INT32,        "run_mode",   "non-interactive"},
+    {GIMP_PDB_IMAGE,        "dst_image",  "Destination image (one of the video frames), where to insert the animated source layers"},
+    {GIMP_PDB_DRAWABLE,     "drawable",   "drawable (unused)"},
+    {GIMP_PDB_INT32,        "range_from", "destination frame nr to start"},
+    {GIMP_PDB_INT32,        "range_to",   "destination frame nr to stop (can be lower than range_from)"},
+    {GIMP_PDB_INT32,        "nr",         "layerstack position where to insert source layer (0 == on top)"},
+    /* source specs */
+    { GIMP_PDB_LAYER,      "src_layer_id",      "starting LayerID of SourceObject. (use any Multilayeranimated Image, or a video frame of anoter Animation)"},
+    { GIMP_PDB_INT32,      "src_stepmode",      "0-5     derive inserted object as copy of one layer from a multilayer src_image \n"
+                                                "100-105 derive inserted object as copy of merged visible layers of a source video frame \n"
+                                                "0:  Layer Loop  1: Layer Loop reverse  2: Layer Once  3: Layer Once reverse  4: Layer PingPong \n"
+                                                "5: None (use onle the selected src_layer)\n"
+                                                "100: Frame Loop  101: Frame Loop reverse  102: Frame Once  103: Frame Once reverse  104: Frame PingPong \n"
+                                                "105: Frame None (use onle the flat copy of the selected frame)\n"
+                                                },
+    { GIMP_PDB_INT32,      "src_handle",        "0: handle left top   1: handle left bottom \n"
+                                                "2: handle right top  3: handle right bottom \n"
+                                                "4: handle center"},
+    { GIMP_PDB_INT32,      "src_paintmode",     "4444: keep original paintmode of src_layer 0: GIMP_NORMAL_MODE (see GimpLayerModeEffects -- libgimp/gimpenums.h -- for more information)"},
+    { GIMP_PDB_INT32,      "src_force_visible", "1: Set inserted layres visible, 0: insert layers as is"},
+    { GIMP_PDB_INT32,      "clip_to_img",       "1: Clip inserted layers to Image size of the destination video frame, 0: dont clip"},
+    /* extras */
+    { GIMP_PDB_INT32,      "rotation_follow",   "0: NO automatic calculation (use the rotation array parameters as it is) \n"
+                                                "1: Automatic calculation of rotation, following the path vectors, (Ignore rotation array parameters)\n"},
+    { GIMP_PDB_FLOAT,      "startangle",        "start angle for the first contolpoint (only used if rotation-follow is on)"},
+
+    /* new features of the _ext[ended] API */
+    {GIMP_PDB_FLOAT,        "step_speed_factor",       "Allows stepping Source and Destination at different speed. (0.1 upto 50 where 1.0 does step snychron, 2.0 Src makes 2 Steps while Destination makes 1 step) "},
+    {GIMP_PDB_INT32,        "tween_steps",             "0 upto 50, Number of virtual Frames to calculate between 2 destination Frames. (use value 0 if no tween processing should be done)"},
+    {GIMP_PDB_FLOAT,        "tween_opacity_initial",   "opacity 0.0 upto 100.0 for the tween step that is nearest to the (next) real frame"},
+    {GIMP_PDB_FLOAT,        "tween_opacity_desc",      "descending opacity 0.0 upto 100.0  for the othertween steps"},
+    {GIMP_PDB_INT32,        "tracelayer_enable",       "TRUE: calculate a tracelayer (with all steps of the moving objects since first step)"},
+    {GIMP_PDB_FLOAT,        "trace_opacity_initial",   "opacity 0.0 upto 100.0 for the nearest tracestep to the actual destination frame"},
+    {GIMP_PDB_FLOAT,        "trace_opacity_desc",      "descending opacity 0.0 upto 100.0 for fading out older positions (that are done before the actual step)"},
+    {GIMP_PDB_INT32,        "apply_bluebox",           "TRUE: apply blubox filter (using bluebox param VALUES of last successful bluebox run)"},
+    {GIMP_PDB_INT32,        "src_selmode",      "0: ignore selections in all source images\n"
+                                                "1: use one selection (from the inital source image) for all handled src layers \n"
+                                                "2: use selections in all source images (for stepmodes 0-5 there is only one source image)"},
+
+
+    /* CONTROLPOINT Arrays (the _ext API uses FLOAT arrays for more precision) */
+    { GIMP_PDB_INT32,      "argc_p_x",          "number of controlpoints"},
+    { GIMP_PDB_INT32ARRAY, "p_x",               "Controlpoint x-koordinate"},
+    { GIMP_PDB_INT32,      "argc_p_y",          "number of controlpoints"},
+    { GIMP_PDB_INT32ARRAY, "p_y",               "Controlpoint y-koordinate"},
+    { GIMP_PDB_INT32,      "argc_opacity",      "number of controlpoints"},
+    { GIMP_PDB_FLOATARRAY, "opacity",           "Controlpoint opacity value 0 <= value <= 100"},
+    { GIMP_PDB_INT32,      "argc_w_resize",     "number of controlpoints"},
+    { GIMP_PDB_FLOATARRAY, "w_resize",          "width scaling in percent"},
+    { GIMP_PDB_INT32,      "argc_h_resize",     "number of controlpoints"},
+    { GIMP_PDB_FLOATARRAY, "h_resize",          "height scaling in percent"},
+    { GIMP_PDB_INT32,      "argc_rotation",     "number of controlpoints"},
+    { GIMP_PDB_FLOATARRAY, "rotation",          "rotation in degrees"},
+    { GIMP_PDB_INT32,      "argc_keyframe_abs", "number of controlpoints"},
+    { GIMP_PDB_INT32ARRAY, "keyframe_abs",      "n: fix controlpoint to this frame number, 0: for controlpoints that are not fixed to a frame."},
+
+    /* new CONTROLPOINT ARRAY items of the _ext[ended] API */
+    { GIMP_PDB_INT32,      "argc_ttlx",     "number of controlpoints"},
+    { GIMP_PDB_FLOATARRAY, "ttlx",          "perspective transformfactor for top left X Coordinate (0.0 upto 5.0, value 1.0 does no trasformation)"},
+    { GIMP_PDB_INT32,      "argc_ttly",     "number of controlpoints"},
+    { GIMP_PDB_FLOATARRAY, "ttly",          "perspective transformfactor for top left Y Coordinate (0.0 upto 5.0, value 1.0 does no trasformation)"},
+    { GIMP_PDB_INT32,      "argc_ttrx",     "number of controlpoints"},
+    { GIMP_PDB_FLOATARRAY, "ttrx",          "perspective transformfactor for top right X Coordinate (0.0 upto 5.0, value 1.0 does no trasformation)"},
+    { GIMP_PDB_INT32,      "argc_ttry",     "number of controlpoints"},
+    { GIMP_PDB_FLOATARRAY, "ttry",          "perspective transformfactor for top right Y Coordinate (0.0 upto 5.0, value 1.0 does no trasformation)"},
+    { GIMP_PDB_INT32,      "argc_tblx",     "number of controlpoints"},
+    { GIMP_PDB_FLOATARRAY, "tblx",          "perspective transformfactor for bottom left X Coordinate (0.0 upto 5.0, value 1.0 does no trasformation)"},
+    { GIMP_PDB_INT32,      "argc_tbly",     "number of controlpoints"},
+    { GIMP_PDB_FLOATARRAY, "tbly",          "perspective transformfactor for bottom left Y Coordinate (0.0 upto 5.0, value 1.0 does no trasformation)"},
+    { GIMP_PDB_INT32,      "argc_tbrx",     "number of controlpoints"},
+    { GIMP_PDB_FLOATARRAY, "tbrx",          "perspective transformfactor for bottom right X Coordinate (0.0 upto 5.0, value 1.0 does no trasformation)"},
+    { GIMP_PDB_INT32,      "argc_tbry",     "number of controlpoints"},
+    { GIMP_PDB_FLOATARRAY, "tbry",          "perspective transformfactor for bottom right Y Coordinate (0.0 upto 5.0, value 1.0 does no trasformation)"},
+    { GIMP_PDB_INT32,      "argc_sel",      "number of controlpoints"},
+    { GIMP_PDB_FLOATARRAY, "sel_feather_radius", "feather radius for selections"},
+  };
+  static int nargs_mov_path_ext = G_N_ELEMENTS (args_mov_path_ext);
+
+  static GimpParamDef args_mov_path_ext2[] =
+  {
+    {GIMP_PDB_INT32,        "run_mode",   "non-interactive"},
+    {GIMP_PDB_IMAGE,        "dst_image",  "Destination image (one of the video frames), where to insert the animated source layers"},
+    {GIMP_PDB_DRAWABLE,     "drawable",   "drawable (unused)"},
+    {GIMP_PDB_INT32,        "range_from", "destination frame nr to start"},
+    {GIMP_PDB_INT32,        "range_to",   "destination frame nr to stop (can be lower than range_from)"},
+    {GIMP_PDB_INT32,        "nr",         "layerstack position where to insert source layer (0 == on top)"},
+    /* source specs */
+    { GIMP_PDB_LAYER,      "src_layer_id",      "starting LayerID of SourceObject. (use any Multilayeranimated Image, or an video frame of anoter Animation)"},
+    { GIMP_PDB_INT32,      "src_stepmode",      "0-5     derive inserted object as copy of one layer from a multilayer src_image \n"
+                                                "100-105 derive inserted object as copy of merged visible layers of a source video frame \n"
+                                                "0:  Layer Loop  1: Layer Loop reverse  2: Layer Once  3: Layer Once reverse  4: Layer PingPong \n"
+                                                "5: None (use onle the selected src_layer)\n"
+                                                "100: Frame Loop  101: Frame Loop reverse  102: Frame Once  103: Frame Once reverse  104: Frame PingPong \n"
+                                                "105: Frame None (use onle the flat copy of the selected frame)\n"
+                                                },
+    { GIMP_PDB_INT32,      "src_handle",        "0: handle left top   1: handle left bottom \n"
+                                                "2: handle right top  3: handle right bottom \n"
+                                                "4: handle center"},
+    { GIMP_PDB_INT32,      "src_paintmode",     "4444: keep original paintmode of src_layer 0: GIMP_NORMAL_MODE (see GimpLayerModeEffects -- libgimp/gimpenums.h -- for more information)"},
+    { GIMP_PDB_INT32,      "src_force_visible", "1: Set inserted layres visible, 0: insert layers as is"},
+    { GIMP_PDB_INT32,      "clip_to_img",       "1: Clip inserted layers to Image size of the destination video frame, 0: dont clip"},
+    /* extras */
+    { GIMP_PDB_INT32,      "rotation_follow",   "0: NO automatic calculation (use the rotation array parameters as it is) \n"
+                                                "1: Automatic calculation of rotation, following the path vectors, (Ignore rotation array parameters)\n"},
+    { GIMP_PDB_FLOAT,      "startangle",        "start angle for the first contolpoint (only used if rotation-follow is on)"},
+
+    /* new features of the _ext[ended] API */
+    {GIMP_PDB_FLOAT,        "step_speed_factor",       "Allows stepping Source and Destination at different speed. (0.1 upto 50 where 1.0 does step snychron, 2.0 Src makes 2 Steps while Destination makes 1 step) "},
+    {GIMP_PDB_INT32,        "tween_steps",             "0 upto 50, Number of virtual Frames to calculate between 2 destination Frames. (use value 0 if no tween processing should be done)"},
+    {GIMP_PDB_FLOAT,        "tween_opacity_initial",   "opacity 0.0 upto 100.0 for the tween step that is nearest to the (next) real frame"},
+    {GIMP_PDB_FLOAT,        "tween_opacity_desc",      "descending opacity 0.0 upto 100.0  for the othertween steps"},
+    {GIMP_PDB_INT32,        "tracelayer_enable",       "TRUE: calculate a tracelayer (with all steps of the moving objects since first step)"},
+    {GIMP_PDB_FLOAT,        "trace_opacity_initial",   "opacity 0.0 upto 100.0 for the nearest tracestep to the actual destination frame"},
+    {GIMP_PDB_FLOAT,        "trace_opacity_desc",      "descending opacity 0.0 upto 100.0 for fading out older positions (that are done before the actual step)"},
+    {GIMP_PDB_INT32,        "apply_bluebox",           "TRUE: apply blubox filter (using bluebox param VALUES of last successful bluebox run)"},
+
+    /* CONTROLPOINTs from file */
+    { GIMP_PDB_STRING,     "pointfile",         "a file with contolpoints (readable text file with one line per controlpoint)"},
+  };
+  static int nargs_mov_path_ext2 = G_N_ELEMENTS (args_mov_path_ext2);
+
+
+  static GimpParamDef args_mov_path_single_frame[] =
+  {
+    {GIMP_PDB_INT32,        "run_mode",      "non-interactive"},
+    {GIMP_PDB_IMAGE,        "dst_image",     "Destination image (one of the video frames), where to insert the animated source layers"},
+    {GIMP_PDB_DRAWABLE,     "drawable",      "drawable to be transfromed and moved according to current phase"},
+    {GIMP_PDB_INT32,        "frame_phase",   "current frame nr starting at 1 (e.g. phase of movent and transformation along path)"},
+    {GIMP_PDB_INT32,        "total_frames",  "number of frames for the full movement/transformation. (value 0 uses the recorded number of frames from the xml file)"},
+    {GIMP_PDB_STRING,       "xml_paramfile", "a file with move path parameter settings in XML format "},
+  };
+  static int nargs_mov_path_single_frame = G_N_ELEMENTS (args_mov_path_single_frame);
+
+
+
+
+
+
+MAIN ()
+
+static void
+query ()
+{
+  gchar *l_help_str;
+
+  static GimpLastvalDef lastvals[] =
+  {
+    GIMP_LASTVALDEF_GINT32      (GIMP_ITER_FALSE,  singleframevals.drawable_id,             "drawable_id"),
+    GIMP_LASTVALDEF_GINT32      (GIMP_ITER_TRUE,   singleframevals.frame_phase,             "frame_phase"),
+    GIMP_LASTVALDEF_GINT32      (GIMP_ITER_FALSE,  singleframevals.total_frames,            "total_frames"),
+    GIMP_LASTVALDEF_GCHAR       (GIMP_ITER_FALSE,  singleframevals.xml_paramfile[0],        "xml_paramfile"),
+  };
+
+
+  gimp_plugin_domain_register (GETTEXT_PACKAGE, LOCALEDIR);
+
+  /* registration for last values buffer structure (for animated filter apply) */
+  gimp_lastval_desc_register(PLUGIN_NAME_GAP_MOVE_SINGLEFRAME,
+                             &singleframevals,
+                             sizeof(singleframevals),
+                             G_N_ELEMENTS (lastvals),
+                             lastvals);
+
+
+  gimp_install_procedure(PLUGIN_NAME_GAP_MOVE,
+                         "This plugin copies layer(s) from one sourceimage to multiple frames on disk, varying position, size and opacity.",
+                         "For NONINTERACTIVE PDB interfaces see also (plug_in_gap_move_path_ext, plug_in_gap_move_path_ext2)",
+                         "Wolfgang Hofer (hof gimp org)",
+                         "Wolfgang Hofer",
+                         GAP_VERSION_WITH_DATE,
+                         N_("Move Path..."),
+                         "RGB*",
+                         GIMP_PLUGIN,
+                         G_N_ELEMENTS (args_mov), nreturn_std,
+                         args_mov, return_std);
+
+  l_help_str = g_strdup_printf(
+                         "This plugin inserts one layer in each frame of the selected frame range of an Animation\n"
+                         " (specified by the dst_image parameter).\n"
+                         " An Animation is a series of numbered video frame images on disk where only the current\n"
+                         " Frame is opened in the gimp\n"
+                         " The inserted layer is derived from another (multilayer)image\n"
+                         " or from another Animation (as merged copy of the visible layers in a source frame)\n"
+                         " the affected destination frame range is selected by the range_from and range_to parameters\n"
+                         " the src_stepmode parameter controls how to derive the layer that is to be inserted.\n"
+                         " With the Controlpoint Parameters you can control position (coordinates),\n"
+                         " size, rotation, perspective and opacity values of the inserted layer\n"
+                         " If you want to move an Object from position AX/AY to BX/BY in a straight line within the range of 24 frames\n"
+                         " you need 2 Contolpoints, if you want the object to move following a path\n"
+                         " you need some more Controlpoints to do that.\n"
+                         " With the rotation_follow Parameter you can force automatic calculation of the rotation for the inserted\n"
+                         " layer according to the path vectors it is moving along.\n"
+                         " A controlpoint can be fixed to a special framenumber using the keyframe_abs controlpoint-parameter.\n"
+                         " Restrictions:\n"
+                         " - keyframe_abs numbers must be 0 (== not fixed) or a frame_number within the affected frame range\n"
+                         " - keyframes_abs must be in sequence (ascending or descending)\n"
+                         " - the first and last controlpoint are always implicit keyframes, and should be passed with keyframe_abs = 0\n"
+                         " - the number of controlpoints is limited to a maximum of %d.\n"
+                         "   the number of controlpoints must be passed in all argc_* parameters\n"
+                         "If the TraceLayer feature is turned on, an additional layer\n"
+                         "  is inserted below the moving object. This Tracelayer shows all steps\n"
+                         "  of the moving object since the 1st Frame.\n"
+                         "With TweenSteps you can calculate virtual Frames between 2 destination frames\n"
+                         "  all these Steps are collected in another additional Layer.\n"
+                         "  this Tweenlayer is added below the moving Object in all handled destination Frames\n"
+                         "See also (plug_in_gap_move_path, plug_in_gap_move)",
+                         (int)GAP_MOV_MAX_POINT);
+
+  gimp_install_procedure(PLUGIN_NAME_GAP_MOVE_PATH_EXT,
+                         "This plugin copies layer(s) from one sourceimage or source animation to multiple frames on disk,\n"
+                         "with varying position, size, perspective and opacity.\n"
+                         ,
+                         l_help_str,
+                         "Wolfgang Hofer (hof gimp org)",
+                         "Wolfgang Hofer",
+                         GAP_VERSION_WITH_DATE,
+                         NULL,                      /* do not appear in menus */
+                         "RGB*",
+                         GIMP_PLUGIN,
+                         nargs_mov_path_ext, nreturn_std,
+                         args_mov_path_ext, return_std);
+  g_free(l_help_str);
+
+  gimp_install_procedure(PLUGIN_NAME_GAP_MOVE_PATH_EXT2,
+                         "This plugin copies layer(s) from one sourceimage or source animation to multiple frames on disk,\n"
+                         "with varying position, size, perspective and opacity.\n"
+                         ,
+                         "This plugin is just another Interface for the MovePath (plug_in_gap_move_path_ext)\n"
+                         " using a File to specify Controlpoints (rather than Array parameters).\n"
+                         " Notes:\n"
+                         " - you can create a controlpoint file with in the MovePath Dialog (interactive call of plug_in_gap_move)\n"
+                         " - for more infos about controlpoints see help of (plug_in_gap_move_path)\n"
+                         ,
+                         "Wolfgang Hofer (hof gimp org)",
+                         "Wolfgang Hofer",
+                         GAP_VERSION_WITH_DATE,
+                         NULL,                      /* do not appear in menus */
+                         "RGB*",
+                         GIMP_PLUGIN,
+                         nargs_mov_path_ext2, nreturn_std,
+                         args_mov_path_ext2, return_std);
+
+
+  gimp_install_procedure(PLUGIN_NAME_GAP_MOVE_SINGLEFRAME,
+                         "This plugin transforms and moves the specified layer according to the settings of the \n"
+                         "specified move path xml parameterfile and to the specified frame_phase parameter.\n"
+                         ,
+                         "This plugin is intended to run as filter in the gimp-gap modify frames feature\n"
+                         " to transform and move an already existng layer along a path where each frame \n"
+                         " is processed in a separate call of this plug-in. The frame_phase parameter \n"
+                         " shall start at 1 in the 1st call and shall count up to total_frames in the other calls.\n"
+                         " Notes:\n"
+                         " - you can create the xml parameterfile with in the MovePath Dialog (interactive call of plug_in_gap_move)\n"
+                         ,
+                         "Wolfgang Hofer (hof gimp org)",
+                         "Wolfgang Hofer",
+                         GAP_VERSION_WITH_DATE,
+                         N_("Move Path Singleframe..."),
+                         "RGB*",
+                         GIMP_PLUGIN,
+                         nargs_mov_path_single_frame, nreturn_single,
+                         args_mov_path_single_frame, return_single);
+
+
+
+
+  {
+     /* Menu names */
+     const char *menupath_image_video = N_("<Image>/Video/");
+
+     //gimp_plugin_menu_branch_register("<Image>", "Video");
+
+     gimp_plugin_menu_register (PLUGIN_NAME_GAP_MOVE, menupath_image_video);
+     gimp_plugin_menu_register (PLUGIN_NAME_GAP_MOVE_SINGLEFRAME, menupath_image_video);
+  }
+}       /* end query */
+
+
+
+static void
+run (const gchar *name
+    , gint n_params
+    , const GimpParam *param
+    , gint *nreturn_vals
+    , GimpParam **return_vals)
+{
+  const char *l_env;
+
+  static GimpParam values[20];
+  GimpRunMode run_mode;
+  GimpRunMode lock_run_mode;
+  GimpPDBStatusType status;
+  gint32     image_id;
+  gint32     lock_image_id;
+
+
+  gint32     l_rc_image;
+
+  /* init std return values status and image (as used in most of the gap plug-ins) */
+  *nreturn_vals = 2;
+  *return_vals = values;
+  status = GIMP_PDB_SUCCESS;
+  values[0].type = GIMP_PDB_STATUS;
+  values[0].data.d_status = GIMP_PDB_EXECUTION_ERROR;
+  values[1].type = GIMP_PDB_IMAGE;
+  values[1].data.d_int32 = -1;
+
+  l_rc_image = -1;
+
+
+  l_env = g_getenv("GAP_DEBUG");
+  if(l_env != NULL)
+  {
+    if((*l_env != 'n') && (*l_env != 'N')) gap_debug = 1;
+  }
+
+  run_mode = param[0].data.d_int32;
+  lock_run_mode = run_mode;
+
+  if(gap_debug)
+  {
+    printf("\ngap_mov_main: debug name = %s\n", name);
+  }
+
+  /* gimp_ui_init is sometimes needed even in NON-Interactive
+   * runmodes.
+   * because thumbnail handling uses the procedure gdk_pixbuf_new_from_file
+   * that will crash if not initialized
+   * so better init always, just to be on the save side.
+   * (most diaolgs do init a 2.nd time but this worked well)
+   */
+  gimp_ui_init ("gap_mov_main", FALSE);
+
+  image_id = param[1].data.d_image;
+  if(!gap_image_is_alive(image_id))
+  {
+     printf("GAP plug-in was called on INVALID IMAGE_ID:%d (terminating)\n",
+                  (int)image_id);
+     status = GIMP_PDB_EXECUTION_ERROR;
+     values[0].data.d_status = status;
+     return ;
+  }
+  lock_image_id = image_id;
+
+
+  /* ---------------------------
+   * check for LOCKS
+   * ---------------------------
+   */
+  if(gap_lock_check_for_lock(lock_image_id, lock_run_mode))
+  {
+       status = GIMP_PDB_EXECUTION_ERROR;
+       values[0].data.d_status = status;
+       return ;
+  }
+
+
+  /* set LOCK on current image (for all gap_plugins) */
+  gap_lock_set_lock(lock_image_id);
+
+  INIT_I18N();
+
+  if (strcmp (name, PLUGIN_NAME_GAP_MOVE) == 0)
+  {
+      GapMovValues *pvals;
+
+      pvals = gap_mov_exec_new_GapMovValues();
+      pvals->dst_image_id = image_id;
+      if (run_mode == GIMP_RUN_NONINTERACTIVE)
+      {
+          status = GIMP_PDB_CALLING_ERROR;
+      }
+
+      if (status == GIMP_PDB_SUCCESS)
+      {
+        l_rc_image = gap_mov_exec_move_path(run_mode, image_id, pvals, NULL, 0, 0);
+      }
+      g_free(pvals);
+  }
+  else if (strcmp (name, PLUGIN_NAME_GAP_MOVE_SINGLEFRAME) == 0)
+  {
+      GapMovValues *pvals;
+      gint          l_dataSize;
+
+      values[1].type = GIMP_PDB_DRAWABLE;
+
+      /* init pvals with default values (to provide defined settings
+       * for optional data that may not be present in the xml parameter file)
+       */
+      pvals = gap_mov_exec_new_GapMovValues();
+      pvals->dst_image_id = image_id;
+
+      /* Possibly retrieve data from a previous run */
+      l_dataSize = gimp_get_data_size(PLUGIN_NAME_GAP_MOVE_SINGLEFRAME);
+      if(l_dataSize == sizeof(singleframevals))
+      {
+        gimp_get_data (PLUGIN_NAME_GAP_MOVE_SINGLEFRAME, &singleframevals);
+      }
+
+      singleframevals.drawable_id = param[2].data.d_drawable;
+      singleframevals.keep_proportions = FALSE;
+      singleframevals.fit_width = TRUE;
+      singleframevals.fit_height = TRUE;
+
+      if (run_mode == GIMP_RUN_NONINTERACTIVE)
+      {
+        if (n_params != nargs_mov_path_single_frame)
+        {
+          status = GIMP_PDB_CALLING_ERROR;
+        }
+        else
+        {
+           singleframevals.frame_phase   = param[3].data.d_int32;
+           singleframevals.total_frames  = param[4].data.d_int32;
+           if(param[5].data.d_string != NULL)
+           {
+              if(param[23].data.d_string != NULL)
+              {
+                 g_snprintf(&singleframevals.xml_paramfile[0], sizeof(singleframevals.xml_paramfile)
+                          , "%s"
+                          , param[5].data.d_string
+                          );
+              }
+           }
+           else
+           {
+             status = GIMP_PDB_CALLING_ERROR;
+           }
+        }
+
+      }
+
+      if (status == GIMP_PDB_SUCCESS)
+      {
+        gint32 l_rc_layer_id;
+        
+        l_rc_layer_id = gap_mov_exec_move_path_singleframe(run_mode, image_id, pvals, &singleframevals);
+        values[1].data.d_int32 = l_rc_layer_id;  /* return layer id  of the resulting (processed) layer */
+        if(l_rc_image >= 0)
+        {
+          gimp_set_data(PLUGIN_NAME_GAP_MOVE_SINGLEFRAME, &singleframevals, sizeof(singleframevals));
+        }
+      }
+      g_free(pvals);
+
+  }
+  else if ((strcmp (name, PLUGIN_NAME_GAP_MOVE_PATH_EXT) == 0)
+       ||  (strcmp (name, PLUGIN_NAME_GAP_MOVE_PATH_EXT2) == 0))
+  {
+      GapMovValues *pvals;
+      gchar        *pointfile;
+      gint          l_idx;
+      gint          l_numpoints;
+      gint          l_rotation_follow;
+      gint32        l_startangle;
+
+      pointfile = NULL;
+      pvals = gap_mov_exec_new_GapMovValues();
+      pvals->dst_image_id = image_id;
+      l_rotation_follow = 0;
+      l_startangle = 0;
+
+
+      if (run_mode == GIMP_RUN_NONINTERACTIVE)
+      {
+        if ( ((n_params != nargs_mov_path_ext)  && (strcmp (name, PLUGIN_NAME_GAP_MOVE_PATH_EXT)  == 0))
+        ||   ((n_params != nargs_mov_path_ext2) && (strcmp (name, PLUGIN_NAME_GAP_MOVE_PATH_EXT2) == 0)))
+        {
+          status = GIMP_PDB_CALLING_ERROR;
+        }
+        else
+        {
+           pvals->dst_range_start   = param[3].data.d_int32;
+           pvals->dst_range_end     = param[4].data.d_int32;
+           pvals->dst_layerstack    = param[5].data.d_int32;
+
+           pvals->src_layer_id      = param[6].data.d_layer;
+           pvals->src_stepmode      = param[7].data.d_int32;
+           pvals->src_handle        = param[8].data.d_int32;
+           pvals->src_paintmode     = param[9].data.d_int32;
+           pvals->src_force_visible = param[10].data.d_int32;
+           pvals->clip_to_img       = param[11].data.d_int32;
+
+           l_rotation_follow        = param[12].data.d_int32;
+           l_startangle             = param[13].data.d_float;
+
+           pvals->step_speed_factor      = param[14].data.d_float;
+           pvals->tween_steps            = param[15].data.d_int32;
+           pvals->tween_opacity_initial  = param[16].data.d_float;
+           pvals->tween_opacity_desc     = param[17].data.d_float;
+           pvals->tracelayer_enable      = param[18].data.d_int32;
+           pvals->trace_opacity_initial  = param[19].data.d_float;
+           pvals->trace_opacity_desc     = param[20].data.d_float;
+           pvals->src_apply_bluebox      = param[21].data.d_int32;
+           pvals->src_selmode            = param[22].data.d_int32;
+
+           if (strcmp (name, PLUGIN_NAME_GAP_MOVE_PATH_EXT)  == 0)
+           {
+              /* PLUGIN_NAME_GAP_MOVE_PATH_EXT passes controlpoints as array parameters */
+              l_numpoints = param[23].data.d_int32;
+              if ((l_numpoints != param[25].data.d_int32)
+              ||  (l_numpoints != param[27].data.d_int32)
+              ||  (l_numpoints != param[29].data.d_int32)
+              ||  (l_numpoints != param[31].data.d_int32)
+              ||  (l_numpoints != param[33].data.d_int32)
+              ||  (l_numpoints != param[35].data.d_int32)
+              ||  (l_numpoints != param[37].data.d_int32)
+              ||  (l_numpoints != param[39].data.d_int32)
+              ||  (l_numpoints != param[41].data.d_int32)
+              ||  (l_numpoints != param[43].data.d_int32)
+              ||  (l_numpoints != param[45].data.d_int32)
+              ||  (l_numpoints != param[47].data.d_int32)
+              ||  (l_numpoints != param[49].data.d_int32)
+              ||  (l_numpoints != param[51].data.d_int32)
+              ||  (l_numpoints != param[53].data.d_int32))
+              {
+                printf("plug_in_gap_move_path_ext: CallingError: different numbers in the controlpoint array argc parameters\n");
+                status = GIMP_PDB_CALLING_ERROR;
+              }
+              else
+              {
+                pvals->point_idx_max = l_numpoints -1;
+                for(l_idx = 0; l_idx < l_numpoints; l_idx++)
+                {
+                   pvals->point[l_idx].p_x = param[24].data.d_int32array[l_idx];
+                   pvals->point[l_idx].p_y = param[26].data.d_int32array[l_idx];
+                   pvals->point[l_idx].opacity  = param[28].data.d_floatarray[l_idx];
+                   pvals->point[l_idx].w_resize = param[30].data.d_floatarray[l_idx];
+                   pvals->point[l_idx].h_resize = param[32].data.d_floatarray[l_idx];
+                   pvals->point[l_idx].rotation = param[34].data.d_floatarray[l_idx];
+                   pvals->point[l_idx].keyframe_abs = param[36].data.d_int32array[l_idx];
+                   /* pvals->point[l_idx].keyframe = ; */ /* relative keyframes are calculated later */
+                   pvals->point[l_idx].ttlx = param[38].data.d_floatarray[l_idx];
+                   pvals->point[l_idx].ttly = param[40].data.d_floatarray[l_idx];
+                   pvals->point[l_idx].ttrx = param[42].data.d_floatarray[l_idx];
+                   pvals->point[l_idx].ttry = param[44].data.d_floatarray[l_idx];
+                   pvals->point[l_idx].tblx = param[46].data.d_floatarray[l_idx];
+                   pvals->point[l_idx].tbly = param[48].data.d_floatarray[l_idx];
+                   pvals->point[l_idx].tbrx = param[50].data.d_floatarray[l_idx];
+                   pvals->point[l_idx].tbry = param[52].data.d_floatarray[l_idx];
+                   pvals->point[l_idx].sel_feather_radius = param[54].data.d_floatarray[l_idx];
+                }
+              }
+           }
+           else
+           {
+              /* PLUGIN_NAME_GAP_MOVE_PATH_EXT2 operates with controlpoint file */
+              if(param[23].data.d_string != NULL)
+              {
+                 pointfile = g_strdup(param[23].data.d_string);
+              }
+           }
+        }
+
+      }
+
+      if (status == GIMP_PDB_SUCCESS)
+      {
+        l_rc_image = gap_mov_exec_move_path(run_mode, image_id, pvals, pointfile, l_rotation_follow, (gdouble)l_startangle);
+      }
+      g_free(pvals);
+      if(pointfile != NULL)
+      {
+        g_free(pointfile);
+      }
+  }
+
+  /* ---------- return handling --------- */
+
+ if(l_rc_image < 0)
+ {
+    if(gap_debug)
+    {
+      printf("gap_mov_main: return GIMP_PDB_EXECUTION_ERROR\n");
+    }
+    status = GIMP_PDB_EXECUTION_ERROR;
+ }
+ else
+ {
+    if(gap_debug) printf("gap_mov_main: return OK\n");
+    /* most gap_plug-ins return an image_id in values[1] */
+    if (values[1].type == GIMP_PDB_IMAGE)
+    {
+      if(gap_debug)
+      {
+        printf("gap_mov_main: return image_id:%d\n", (int)l_rc_image);
+      }
+      values[1].data.d_int32 = l_rc_image;  /* return image id  of the resulting (current frame) image */
+    }
+ }
+
+ if (run_mode != GIMP_RUN_NONINTERACTIVE)
+ {
+    gimp_displays_flush();
+ }
+
+ values[0].data.d_status = status;
+
+ /* remove LOCK on this image for all gap_plugins */
+ gap_lock_remove_lock(lock_image_id);
+}
diff --git a/gap/gap_mov_render.c b/gap/gap_mov_render.c
index 4d594d0..e0173e6 100644
--- a/gap/gap_mov_render.c
+++ b/gap/gap_mov_render.c
@@ -78,6 +78,11 @@ static void  p_mov_transform_perspective(gint32 layer_id
                   , guint        *new_height
                   );
 
+static void  p_mov_calculate_scale_factors(gint32 image_id, GapMovValues *val_ptr, GapMovCurrent *cur_ptr
+                  , guint orig_width, guint orig_height
+                  , gdouble *retScaleWidthPercent
+                  , gdouble *retScaleheightPercent
+                  );
 
 
 #define BOUNDS(a,x,y)  ((a < x) ? x : ((a > y) ? y : a))
@@ -93,7 +98,7 @@ p_get_paintmode(int mode, gint32 src_layer_id)
   if(mode == GAP_MOV_KEEP_SRC_PAINTMODE)
   {
     GimpLayerModeEffects l_mode;
-    
+
     l_mode = gimp_layer_get_mode(src_layer_id);
     return (l_mode);
   }
@@ -346,11 +351,367 @@ p_mov_transform_perspective(gint32 layer_id
 
 }  /* end p_mov_transform_perspective  */
 
-/* ============================================================================
+
+
+
+/* ----------------------------------------
+ * p_mov_calculate_scale_factors
+ * ----------------------------------------
+ * In case of single frame mode rendering is done with
+ * settings that were saved (recorded) based on frame and moving object size(s)
+ * that may differ from actual sizes at render time.
+ * The scaling factors are adjusted for automatically
+ * pre-scaling depending on scenarios that are controled by 3 flags.
+ *
+ *
+ * Example:
+ * recorded sizes of the frame and the moving object:
+ *
+ *   +---------------------+    recordedFrameWidth:  640
+ *   |                     |    recordedFrameHeight: 400
+ *   |  +-------+          |
+ *   |  |#######|          |    recordedObjWidth:    320
+ *   |  |#######|          |    recordedObjHeight:   240
+ *   |  +-------+          |
+ *   |                     |
+ *   |                     |
+ *   +---------------------+
+ *
+ * actual rendering shall be done on frame size 1280x400
+ * on a moving object of size 400 x 400.
+ *
+ *      +------+
+ *      |######|   orig_width:      400
+ *      |######|   orig_height:     400
+ *      |######|
+ *      |######|
+ *      +------+
+ *
+ * Prescaling will depending on 3 flags:
+ *
+ * ==== scenarios that allow changing proportions of the moving object =====
+ *
+ * o) fit_width = TRUE, fit_height = TRUE, keep_proportions = FALSE
+ *    In this scenario the moving object is pre-scaled to
+ *    preScaleWidth and preScaleHeight. This gives same relations
+ *    of the moving object / frame as the relations were at recording time,
+ *    but deform proportions of the moving object from square to rectangle shape.
+ *
+ *   +-------------------------------------+    renderImageWidth:  1280
+ *   |                                     |    renderImageHeight:  800
+ *   |                                     |
+ *   |  +--------------+                   |
+ *   |  |##############|                   |    preScaleWidth:      640
+ *   |  |##############|                   |    preScaleHeight:     480
+ *   |  |##############|                   |
+ *   |  |##############|                   |
+ *   |  +--------------+                   |    renderObjWidth:     640
+ *   |                                     |    renderObjHeight:    480
+ *   |                                     |
+ *   |                                     |
+ *   |                                     |
+ *   +-------------------------------------+
+ *
+ * o) fit_width = TRUE, fit_height = FALSE, keep_proportions = FALSE
+ *    In this scenario the moving object is scaled to preScaleWidth
+ *    and keeps its original height.
+ *
+ *   +-------------------------------------+    renderImageWidth:  1280
+ *   |                                     |    renderImageHeight:  800
+ *   |                                     |
+ *   |  +--------------+                   |
+ *   |  +--------------+                   |    preScaleWidth:      640
+ *   |  |##############|                   |    preScaleHeight:     480
+ *   |  |##############|                   |
+ *   |  +--------------+                   |
+ *   |  +--------------+                   |    renderObjWidth:     640
+ *   |                                     |    renderObjHeight:    400
+ *   |                                     |
+ *   |                                     |
+ *   |                                     |
+ *   +-------------------------------------+
+ *
+ * o) fit_width = FALSE, fit_height = TRUE, keep_proportions = FALSE
+ *    In this scenario the moving object is scaled to preScaleHeight
+ *    and keeps its original width.
+ *
+ *   +-------------------------------------+    renderImageWidth:  1280
+ *   |                                     |    renderImageHeight:  800
+ *   |                                     |
+ *   |  +---+------+---+                   |
+ *   |  |   |######|   |                   |    preScaleWidth:      640
+ *   |  |   |######|   |                   |    preScaleHeight:     480
+ *   |  |   |######|   |                   |
+ *   |  |   |######|   |                   |
+ *   |  +---+------+---+                   |    renderObjWidth:     400
+ *   |                                     |    renderObjHeight:    480
+ *   |                                     |
+ *   |                                     |
+ *   |                                     |
+ *   +-------------------------------------+
+ *
+ *
+ *
+ * ==== scenarios that keep proportions of the moving object =====
+ *
+ *
+ * o) fit_width = TRUE, fit_height = TRUE, keep_proportions = TRUE
+ *    In this scenario the moving object is pre-scaled to fit into a  rectangle
+ *    of size preScaleWidth x preScaleHeight, keeping its original proportions.
+ *
+ *   +-------------------------------------+    renderImageWidth:  1280
+ *   |                                     |    renderImageHeight:  800
+ *   |                                     |
+ *   |  +--+--------+--+                   |
+ *   |  |  |########|  |                   |    preScaleWidth:      640
+ *   |  |  |########|  |                   |    preScaleHeight:     480
+ *   |  |  |########|  |                   |
+ *   |  |  |########|  |                   |
+ *   |  +--+--------+--+                   |    renderObjWidth:     480
+ *   |                                     |    renderObjHeight:    480
+ *   |                                     |
+ *   |                                     |
+ *   |                                     |
+ *   +-------------------------------------+
+ *
+ * o) fit_width = TRUE, fit_height = FALSE, keep_proportions = TRUE
+ *    In this scenario the moving object is pre-scaled to preScaleWidth
+ *    keeping its original proportions.
+ *
+ *   +-------------------------------------+    renderImageWidth:  1280
+ *   |                                     |    renderImageHeight:  800
+ *   |  +--------------+                   |
+ *   |  +##############+                   |
+ *   |  |##############|                   |    preScaleWidth:      640
+ *   |  |##############|                   |    preScaleHeight:     480
+ *   |  |##############|                   |
+ *   |  |##############|                   |
+ *   |  +##############+                   |    renderObjWidth:     640
+ *   |  +--------------+                   |    renderObjHeight:    640
+ *   |                                     |
+ *   |                                     |
+ *   |                                     |
+ *   +-------------------------------------+
+ *
+ * o) fit_width = FALSE, fit_height = TRUE, keep_proportions = TRUE
+ *    In this scenario the moving object is pre-scaled to preScaleHeight
+ *    keeping its original proportions.
+ *
+ *   +-------------------------------------+    renderImageWidth:  1280
+ *   |                                     |    renderImageHeight:  800
+ *   |                                     |
+ *   |  +--+--------+--+                   |
+ *   |  |  |########|  |                   |    preScaleWidth:      640
+ *   |  |  |########|  |                   |    preScaleHeight:     480
+ *   |  |  |########|  |                   |
+ *   |  |  |########|  |                   |
+ *   |  +--+--------+--+                   |    renderObjWidth:     480
+ *   |                                     |    renderObjHeight:    480
+ *   |                                     |
+ *   |                                     |
+ *   |                                     |
+ *   +-------------------------------------+
+ *
+ * o) fit_width = FALSE, fit_height = FALSE, (keep_proportions NOT relevant)
+ *    In this scenario the moving object is not pre-scaled scaled
+ *    (therefore propotions are unchanged even in case keep_proportions flag is FALSE)
+ *
+ *   +-------------------------------------+    renderImageWidth:  1280
+ *   |                                     |    renderImageHeight:  800
+ *   |                                     |
+ *   |  +--------------+                   |
+ *   |  |   +######+   |                   |    preScaleWidth:      640
+ *   |  |   |######|   |                   |    preScaleHeight:     480
+ *   |  |   |######|   |                   |
+ *   |  |   +######+   |                   |
+ *   |  +--------------+                   |    renderObjWidth:     400
+ *   |                                     |    renderObjHeight:    400
+ *   |                                     |
+ *   |                                     |
+ *   |                                     |
+ *   +-------------------------------------+
+ *
+ *
+ * Note: the examples above show frame/moving object scenarios with handle object
+ * at center settings and with current scale 100%
+ *  (cur_ptr->currWidth == 100  cur_ptr->currHeight == 100)
+ *
+ */
+static void
+p_mov_calculate_scale_factors(gint32 image_id, GapMovValues *val_ptr, GapMovCurrent *cur_ptr
+   , guint orig_width, guint orig_height
+   , gdouble *retScaleWidthPercent
+   , gdouble *retScaleheightPercent
+   )
+{
+  gdouble              scaleWidthPercent;
+  gdouble              scaleHeightPercent;
+
+
+  scaleWidthPercent = cur_ptr->currWidth;
+  scaleHeightPercent = cur_ptr->currHeight;
+
+
+  if((cur_ptr->isSingleFrame)
+  && (orig_width != 0)
+  && (orig_height != 0))
+  {
+    gint32  renderImageWidth;
+    gint32  renderImageHeight;
+    gdouble preScaleWidth;
+    gdouble preScaleHeight;
+    gdouble preScaleWidthFactor;
+    gdouble preScaleHeightFactor;
+
+    gdouble result_width;         /* resulting width at unscaled size (e.g. 100%) */
+    gdouble result_height;        /* resulting height at unscaled size (e.g. 100%) */
+    gdouble origWidth;
+    gdouble origHeight;
+
+    origWidth = orig_width;
+    origHeight = orig_height;
+
+
+    renderImageWidth = gimp_image_width(image_id);
+    renderImageHeight = gimp_image_height(image_id);
+    preScaleWidth = origWidth;
+    preScaleHeight = origHeight;
+    preScaleWidthFactor = 1.0;
+    preScaleHeightFactor = 1.0;
+
+    /* calculate preScaleWidth */
+    if((val_ptr->recordedFrameWidth != renderImageWidth)
+    || (val_ptr->recordedObjWidth != orig_width))
+    {
+      if(val_ptr->recordedFrameWidth != 0)
+      {
+        preScaleWidth  = (gdouble)val_ptr->recordedObjWidth * (gdouble)renderImageWidth / (gdouble)val_ptr->recordedFrameWidth;
+      }
+    }
+
+    /* calculate preScaleHeight */
+    if((val_ptr->recordedFrameHeight != renderImageHeight)
+    || (val_ptr->recordedObjHeight != orig_height))
+    {
+      if(val_ptr->recordedFrameHeight != 0)
+      {
+        preScaleHeight = (gdouble)val_ptr->recordedObjHeight * (gdouble)renderImageHeight / (gdouble)val_ptr->recordedFrameHeight;
+      }
+    }
+
+
+
+    /* calculate (unscaled) result sizes according to flags */
+    result_width = orig_width;
+    result_height = orig_height;
+
+    if(cur_ptr->keep_proportions)
+    {
+      gdouble actualObjProportion;
+
+      actualObjProportion = origWidth / origHeight;
+
+
+      if((cur_ptr->fit_width) && (cur_ptr->fit_height))
+      {
+        result_width = preScaleHeight * actualObjProportion;
+        result_height = preScaleHeight;
+
+        if(result_width > preScaleWidth)
+        {
+          result_width = preScaleWidth;
+          result_height = preScaleWidth / actualObjProportion;
+        }
+      }
+      else
+      {
+        if(cur_ptr->fit_height)
+        {
+           result_height = preScaleHeight;
+           result_width = (gdouble)preScaleHeight * actualObjProportion;
+        }
+        if(cur_ptr->fit_width)
+        {
+           result_width = preScaleWidth;
+           result_height = (gdouble)preScaleHeight / actualObjProportion;
+        }
+      }
+
+    }
+    else
+    {
+      if(cur_ptr->fit_height)
+      {
+         result_height = preScaleHeight;
+      }
+      if(cur_ptr->fit_width)
+      {
+         result_width = preScaleWidth;
+      }
+    }
+
+    preScaleWidthFactor = result_width / origWidth;
+    preScaleHeightFactor = result_height / origHeight;
+
+    scaleWidthPercent = cur_ptr->currWidth * preScaleWidthFactor;
+    scaleHeightPercent = cur_ptr->currHeight * preScaleHeightFactor;
+
+
+
+    if(gap_debug)
+    {
+      printf("p_mov_calculate_scale_factors RESULTS:\n");
+      printf("  FrameSize Recorded:(%d x %d) Actual:(%d x %d) MovObjSize Recorded:(%d x %d) Actual:(%d x %d)\n"
+        ,(int)val_ptr->recordedFrameWidth
+        ,(int)val_ptr->recordedFrameHeight
+        ,(int)renderImageWidth
+        ,(int)renderImageHeight
+        ,(int)val_ptr->recordedObjWidth
+        ,(int)val_ptr->recordedObjHeight
+        ,(int)orig_width
+        ,(int)orig_height
+        );
+      printf("  Prescale flags fit_width:%d fit_height:%d keep_proportions:%d  result_width:%.4f result_height:%.4f\n"
+        ,cur_ptr->fit_width
+        ,cur_ptr->fit_height
+        ,cur_ptr->keep_proportions
+        ,(float)result_width
+        ,(float)result_height
+        );
+      printf("  Prescale WxH: (%.3f x %.3f) factors:(%.3f %.3f) scaleWidthPercent:%.3f scaleHeightPercent:%.3f\n"
+        ,(float)preScaleWidth
+        ,(float)preScaleHeight
+        ,(float)preScaleWidthFactor
+        ,(float)preScaleHeightFactor
+        ,(float)scaleWidthPercent
+        ,(float)scaleHeightPercent
+        );
+    }
+  }
+
+
+  /* deliver result values */
+  *retScaleWidthPercent = scaleWidthPercent;
+  *retScaleheightPercent = scaleHeightPercent;
+
+}  /* end p_mov_calculate_scale_factors */
+
+
+
+/* -----------------------------------
  * gap_mov_render_render
- * insert current source layer into image
- *    at current settings (position, size opacity ...)
- * ============================================================================
+ * -----------------------------------
+ * process transformations (current settings position, size opacity ...)
+ * for the current source layer.
+ *
+ * In case current source layer is not already part of the processed frame (image_id)
+ * insert a copy of the current source layer into the processed frame.
+ * and do perform the transformations on the inserted copy.
+ *
+ * Note: in singleframe mode the current source layer may be already
+ * part of the processed frame. In this special case perform the
+ * transformations directly on the source layer.
+ *
  */
 gint
 gap_mov_render_render(gint32 image_id, GapMovValues *val_ptr, GapMovCurrent *cur_ptr)
@@ -361,6 +722,8 @@ gap_mov_render_render(gint32 image_id, GapMovValues *val_ptr, GapMovCurrent *cur
   gint         l_src_offset_x, l_src_offset_y;    /* layeroffsets as they were in src_image */
   guint        l_new_width;
   guint        l_new_height;
+  guint        l_potential_new_width;
+  guint        l_potential_new_height;
   guint        l_orig_width;
   guint        l_orig_height;
   gint         l_resized_flag;
@@ -368,13 +731,16 @@ gap_mov_render_render(gint32 image_id, GapMovValues *val_ptr, GapMovCurrent *cur
   guint        l_image_width;
   guint        l_image_height;
   GimpLayerModeEffects l_mode;
+  gdouble              scaleWidthPercent;
+  gdouble              scaleHeightPercent;
 
-  if(gap_debug) 
+  if(gap_debug)
   {
     printf("gap_mov_render_render: frame/layer: %ld/%ld  X=%f, Y=%f\n"
                 "       Width=%f Height=%f\n"
                 "       Opacity=%f  Rotate=%f  clip_to_img = %d force_visibility = %d\n"
-                "       src_stepmode = %d\n",
+                "       src_stepmode = %d\n"
+		"       singleMovObjLayerId=%d singleMovObjIMAGEId=%d (frame)image_id=%d\n",
                      cur_ptr->dst_frame_nr, cur_ptr->src_layer_idx,
                      cur_ptr->currX, cur_ptr->currY,
                      cur_ptr->currWidth,
@@ -383,60 +749,121 @@ gap_mov_render_render(gint32 image_id, GapMovValues *val_ptr, GapMovCurrent *cur
                      cur_ptr->currRotation,
                      val_ptr->clip_to_img,
                      val_ptr->src_force_visible,
-                     val_ptr->src_stepmode);
+                     val_ptr->src_stepmode,
+		     cur_ptr->singleMovObjLayerId,
+		     gimp_drawable_get_image(cur_ptr->singleMovObjLayerId),
+		     image_id
+		     );
   }
-  
-  if(val_ptr->src_stepmode < GAP_STEP_FRAME)
+
+  if(cur_ptr->isSingleFrame)
   {
-    if(gap_debug)
-    {
-      printf("gap_mov_render_render: Before gap_layer_copy_to_dest_image image_id:%d src_layer_id:%d\n"
-              ,(int)image_id, (int)cur_ptr->src_layers[cur_ptr->src_layer_idx]);
-    }
     l_mode = p_get_paintmode(val_ptr->src_paintmode
-                            ,cur_ptr->src_layers[cur_ptr->src_layer_idx]
+                            ,cur_ptr->singleMovObjLayerId
                             );
-    /* make a copy of the current source layer
-     * (using current opacity  & paintmode values)
-     */
-     l_cp_layer_id = gap_layer_copy_to_dest_image(image_id,
-                                   cur_ptr->src_layers[cur_ptr->src_layer_idx],
-                                   cur_ptr->currOpacity,
-                                   l_mode,
-                                   &l_src_offset_x,
-                                   &l_src_offset_y);
+    if(gimp_drawable_get_image(cur_ptr->singleMovObjLayerId) == image_id)
+    {
+      /* the moving object layer id is already part of the processed frame image */
+      l_cp_layer_id = cur_ptr->singleMovObjLayerId;
+      /* findout the offsets of the original layer within the source Image */
+      gimp_drawable_offsets(l_cp_layer_id, &l_src_offset_x, &l_src_offset_y );
+    }
+    else
+    {
+      /* the moving object layer id must be coped to the processed frame image
+       * (this is typically used when called from the storyboard processor
+       * where the frame image is just an empty temporary image without any layers)
+       */
+      if(val_ptr->src_stepmode >= GAP_STEP_FRAME)
+      {
+        gimp_layer_resize_to_image_size(cur_ptr->singleMovObjLayerId);
+      }
+
+      /* make a copy of the moving object layer
+       * (using current opacity  & paintmode values at initial unscaled size)
+       * and add the copied layer at dst_layerstack
+       * (layerstack position is not important on an empty image)
+       */
+      l_cp_layer_id = gap_layer_copy_to_dest_image(image_id,
+                                     cur_ptr->singleMovObjLayerId,
+                                     cur_ptr->currOpacity,
+                                     l_mode,
+                                     &l_src_offset_x,
+                                     &l_src_offset_y);
+      if(l_cp_layer_id < 0)
+      {
+         cur_ptr->processedLayerId = -1;
+         return -1;
+      }
+      gimp_image_add_layer(image_id, l_cp_layer_id, val_ptr->dst_layerstack);
+
+    }
+
+
   }
   else
   {
+    if(val_ptr->src_stepmode < GAP_STEP_FRAME)
+    {
+      if(gap_debug)
+      {
+        printf("gap_mov_render_render: Before gap_layer_copy_to_dest_image image_id:%d src_layer_id:%d\n"
+                ,(int)image_id, (int)cur_ptr->src_layers[cur_ptr->src_layer_idx]);
+      }
+      l_mode = p_get_paintmode(val_ptr->src_paintmode
+                              ,cur_ptr->src_layers[cur_ptr->src_layer_idx]
+                              );
+      /* make a copy of the current source layer
+       * (using current opacity  & paintmode values)
+       */
+       l_cp_layer_id = gap_layer_copy_to_dest_image(image_id,
+                                     cur_ptr->src_layers[cur_ptr->src_layer_idx],
+                                     cur_ptr->currOpacity,
+                                     l_mode,
+                                     &l_src_offset_x,
+                                     &l_src_offset_y);
+    }
+    else
+    {
+      if(gap_debug)
+      {
+        printf("gap_mov_render_render: Before gap_layer_copy_to_dest_image image_id:%d cache_tmp_layer_id:%d\n"
+                ,(int)image_id, (int)val_ptr->cache_tmp_layer_id);
+      }
+      l_mode = p_get_paintmode(val_ptr->src_paintmode
+                              ,val_ptr->cache_tmp_layer_id
+                              );
+       /* for FRAME based stepmodes use the flattened layer in the cached frame image */
+       l_cp_layer_id = gap_layer_copy_to_dest_image(image_id,
+                                     val_ptr->cache_tmp_layer_id,
+                                     cur_ptr->currOpacity,
+                                     l_mode,
+                                     &l_src_offset_x,
+                                     &l_src_offset_y);
+    }
+    /* add the copied layer to current destination image */
     if(gap_debug)
     {
-      printf("gap_mov_render_render: Before gap_layer_copy_to_dest_image image_id:%d cache_tmp_layer_id:%d\n"
-              ,(int)image_id, (int)val_ptr->cache_tmp_layer_id);
+      printf("gap_mov_render_render: after layer copy layer_id=%d\n", (int)l_cp_layer_id);
+    }
+    if(l_cp_layer_id < 0)
+    {
+       cur_ptr->processedLayerId = -1;
+       return -1;
     }
-    l_mode = p_get_paintmode(val_ptr->src_paintmode
-                            ,val_ptr->cache_tmp_layer_id
-                            );
-     /* for FRAME based stepmodes use the flattened layer in the cahed frame image */
-     l_cp_layer_id = gap_layer_copy_to_dest_image(image_id,
-                                   val_ptr->cache_tmp_layer_id,
-                                   cur_ptr->currOpacity,
-                                   l_mode,
-                                   &l_src_offset_x,
-                                   &l_src_offset_y);
-  }
-
 
-  /* add the copied layer to current destination image */
-  if(gap_debug) printf("gap_mov_render_render: after layer copy layer_id=%d\n", (int)l_cp_layer_id);
-  if(l_cp_layer_id < 0)
-  {
-     return -1;
+    gimp_image_add_layer(image_id, l_cp_layer_id,
+                         val_ptr->dst_layerstack);
+    if(gap_debug)
+    {
+      printf("gap_mov_render_render: after add layer\n");
+    }
   }
 
-  gimp_image_add_layer(image_id, l_cp_layer_id,
-                       val_ptr->dst_layerstack);
 
-  if(gap_debug) printf("gap_mov_render_render: after add layer\n");
+  /* set processedLayerId (for return handling in singleframe mode) */
+  cur_ptr->processedLayerId = l_cp_layer_id;
+
 
   if(val_ptr->src_force_visible)
   {
@@ -483,17 +910,50 @@ gap_mov_render_render(gint32 image_id, GapMovValues *val_ptr, GapMovCurrent *cur
                              );
   }
 
-  if((cur_ptr->currWidth * cur_ptr->currHeight) > (100.0 * 100.0))
+
+  /* scale percentage values (where 100.0 is original size without scaling) */
+  scaleWidthPercent = cur_ptr->currWidth;
+  scaleHeightPercent = cur_ptr->currHeight;
+
+  /* re-calculate scale percentage values
+   * (for automatically pre-scaling in case of singleframes processing)  */
+  p_mov_calculate_scale_factors(image_id, val_ptr, cur_ptr
+                          , l_orig_width, l_orig_height
+                          , &scaleWidthPercent
+                          , &scaleHeightPercent
+                          );
+
+
+
+
+
+  if((scaleWidthPercent * scaleHeightPercent) > (100.0 * 100.0))
   {
-     /* have to scale layer (enlarge) */
-     l_resized_flag = 1;
+    l_potential_new_width  = (l_orig_width  * scaleWidthPercent) / 100;
+    l_potential_new_height = (l_orig_height * scaleHeightPercent) / 100;
+
+    if((l_potential_new_width != l_new_width)
+    || (l_potential_new_height != l_new_height))
+    {
+       /* have to scale layer (enlarge)  */
+       l_resized_flag = 1;
+
+       l_new_width  = l_potential_new_width;
+       l_new_height = l_potential_new_height;
 
-     l_new_width  = (l_orig_width  * cur_ptr->currWidth) / 100;
-     l_new_height = (l_orig_height * cur_ptr->currHeight) / 100;
-     gimp_layer_scale(l_cp_layer_id, l_new_width, l_new_height, 0);
+       if(gap_debug)
+       {
+         printf("SCALING-1 to size (%d x %d)\n"
+           ,(int)l_new_width
+           ,(int)l_new_height
+           );
+       }
+
+       gimp_layer_scale(l_cp_layer_id, l_new_width, l_new_height, 0);
+    }
 
-     /* do 4-point perspective stuff (after enlarge) */
-     p_mov_transform_perspective(l_cp_layer_id
+    /* do 4-point perspective stuff (after enlarge) */
+    p_mov_transform_perspective(l_cp_layer_id
                        , val_ptr
                        , cur_ptr
                        , &l_resized_flag
@@ -512,14 +972,24 @@ gap_mov_render_render(gint32 image_id, GapMovValues *val_ptr, GapMovCurrent *cur
                        , &l_new_height
                        );
 
-    if((cur_ptr->currWidth  > 100.01) || (cur_ptr->currWidth < 99.99)
-    || (cur_ptr->currHeight > 100.01) || (cur_ptr->currHeight < 99.99))
+    l_potential_new_width  = (l_new_width  * scaleWidthPercent) / 100;
+    l_potential_new_height = (l_new_height * scaleHeightPercent) / 100;
+
+    if((l_potential_new_width != l_new_width)
+    || (l_potential_new_height != l_new_height))
     {
        /* have to scale layer */
        l_resized_flag = 1;
 
-       l_new_width  = (l_new_width  * cur_ptr->currWidth) / 100;
-       l_new_height = (l_new_height * cur_ptr->currHeight) / 100;
+       l_new_width  = l_potential_new_width;
+       l_new_height = l_potential_new_height;
+       if(gap_debug)
+       {
+         printf("SCALING-2 to size (%d x %d)\n"
+           ,(int)l_new_width
+           ,(int)l_new_height
+           );
+       }
        gimp_layer_scale(l_cp_layer_id, l_new_width, l_new_height, 0);
     }
   }
@@ -640,16 +1110,20 @@ gap_mov_render_render(gint32 image_id, GapMovValues *val_ptr, GapMovCurrent *cur
      val_ptr->trace_layer_id = gap_image_merge_visible_layers(val_ptr->trace_image_id, l_mergemode);
   }
 
-  if(gap_debug) printf("GAP gap_mov_render_render: exit OK\n");
+  if(gap_debug)
+  {
+    printf("GAP gap_mov_render_render: exit OK\n");
+  }
 
   return 0;
 }       /* end gap_mov_render_render */
 
+
 /* ============================================================================
  * gap_mov_render_fetch_src_frame
  *   fetch the requested video frame SourceImage into cache_tmp_image_id
  *   and
- *    - reduce all visible layer to one layer (cache_tmp_layer_id)
+ *    - reduce all visible layers to one layer (cache_tmp_layer_id)
  *    - (scale to animated preview size if called for AnimPreview )
  *    - reuse cached image (for subsequent calls for the same framenumber
  *      of the same source image -- for  GAP_STEP_FRAME_NONE
diff --git a/gap/gap_mov_xml_par.c b/gap/gap_mov_xml_par.c
new file mode 100644
index 0000000..eaf39e4
--- /dev/null
+++ b/gap/gap_mov_xml_par.c
@@ -0,0 +1,1651 @@
+/* gap_mov_xml_par.c
+ * 2011.03.09 hof (Wolfgang Hofer)
+ *
+ * GAP ... Gimp Animation Plugins
+ *
+ * Move Path : XML parameterfile load and save procedures.
+ * The XML parameterfile contains full information of all parameters
+ * available in the move path feature including:
+ *  version
+ *  frame description, moving object description
+ *  tweens, trace layer, bluebox settings and controlpoints.
+ *
+ * Note that the old pointfile format is still supported
+ * but not handled here.
+ * (the old pointfile format is limited to information about the controlpoints that build the path
+ * see gap_mov_exec module for load/save support of the old pointfile format)
+ *
+ */
+/* 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:
+ * 2011.03.09  hof: created.
+ */
+
+#include "config.h"
+
+/* SYTEM (UNIX) includes */
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <errno.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include <glib/gstdio.h>
+
+/* GIMP includes */
+#include "gtk/gtk.h"
+#include "libgimp/gimp.h"
+
+/* GAP includes */
+#include "gap_libgapbase.h"
+#include "gap-intl.h"
+#include "gap_lib.h"
+#include "gap_mov_dialog.h"
+#include "gap_mov_exec.h"
+#include "gap_mov_render.h"
+#include "gap_mov_xml_par.h"
+#include "gap_accel_char.h"
+#include "gap_bluebox.h"
+#include "gap_xml_util.h"
+
+
+/* The xml tokens (element and attribute names) */
+#define GAP_MOVPATH_XML_TOKEN_ROOT                   "gimp_gap_move_path_parameters"
+#define GAP_MOVPATH_XML_TOKEN_VERSION                "version"
+#define GAP_MOVPATH_XML_TOKEN_FRAME_DESCRIPTION      "frame_description"
+#define GAP_MOVPATH_XML_TOKEN_WIDTH                  "width"
+#define GAP_MOVPATH_XML_TOKEN_HEIGHT                 "height"
+#define GAP_MOVPATH_XML_TOKEN_RANGE_FROM             "range_from"
+#define GAP_MOVPATH_XML_TOKEN_RANGE_TO               "range_to"
+#define GAP_MOVPATH_XML_TOKEN_TOTAL_FRAMES           "total_frames"
+#define GAP_MOVPATH_XML_TOKEN_TWEEN                  "tween"
+#define GAP_MOVPATH_XML_TOKEN_TWEEN_STEPS            "tween_steps"
+#define GAP_MOVPATH_XML_TOKEN_TWEEN_OPACITY_INITIAL  "tween_opacity_initial"
+#define GAP_MOVPATH_XML_TOKEN_TWEEN_OPACITY_DESC     "tween_opacity_desc"
+#define GAP_MOVPATH_XML_TOKEN_TRACE                  "trace"
+#define GAP_MOVPATH_XML_TOKEN_TRACELAYER_ENABLE      "tracelayer_enable"
+#define GAP_MOVPATH_XML_TOKEN_TRACE_OPACITY_INITIAL  "trace_opacity_initial"
+#define GAP_MOVPATH_XML_TOKEN_TRACE_OPACITY_DESC     "trace_opacity_desc"
+#define GAP_MOVPATH_XML_TOKEN_MOVING_OBJECT          "moving_object"
+#define GAP_MOVPATH_XML_TOKEN_SRC_STEPMODE           "src_stepmode"
+#define GAP_MOVPATH_XML_TOKEN_SRC_HANDLE             "src_handle"
+#define GAP_MOVPATH_XML_TOKEN_SRC_SELMODE            "src_selmode"
+#define GAP_MOVPATH_XML_TOKEN_SRC_PAINTMODE          "src_paintmode"
+#define GAP_MOVPATH_XML_TOKEN_DST_LAYERSTACK         "dst_layerstack"
+#define GAP_MOVPATH_XML_TOKEN_STEP_SPEED_FACTOR      "step_speed_factor"
+#define GAP_MOVPATH_XML_TOKEN_SRC_FORCE_VISIBLE      "src_force_visible"
+#define GAP_MOVPATH_XML_TOKEN_CLIP_TO_IMG            "clip_to_img"
+#define GAP_MOVPATH_XML_TOKEN_SRC_APPLY_BLUEBOX      "src_apply_bluebox"
+#define GAP_MOVPATH_XML_TOKEN_SRC_LAYER_ID           "src_layer_id"
+#define GAP_MOVPATH_XML_TOKEN_SRC_LAYERSTACK         "src_layerstack"
+#define GAP_MOVPATH_XML_TOKEN_SRC_FILENAME           "src_filename"
+#define GAP_MOVPATH_XML_TOKEN_BLUEBOX_PARAMETERS     "gimp_gap_bluebox_parameters"
+#define GAP_MOVPATH_XML_TOKEN_THRES_MODE             "thres_mode"
+#define GAP_MOVPATH_XML_TOKEN_THRES_R                "thres_r"
+#define GAP_MOVPATH_XML_TOKEN_THRES_G                "thres_g"
+#define GAP_MOVPATH_XML_TOKEN_THRES_B                "thres_b"
+#define GAP_MOVPATH_XML_TOKEN_THRES_H                "thres_h"
+#define GAP_MOVPATH_XML_TOKEN_THRES_S                "thres_s"
+#define GAP_MOVPATH_XML_TOKEN_THRES_V                "thres_v"
+#define GAP_MOVPATH_XML_TOKEN_THRES                  "thres"
+#define GAP_MOVPATH_XML_TOKEN_TOLERANCE              "tolerance"
+#define GAP_MOVPATH_XML_TOKEN_GROW                   "grow"
+#define GAP_MOVPATH_XML_TOKEN_FEATHER_EDGES          "feather_edges"
+#define GAP_MOVPATH_XML_TOKEN_FEATHER_RADIUS         "feather_radius"
+#define GAP_MOVPATH_XML_TOKEN_SOURCE_ALPHA           "source_alpha"
+#define GAP_MOVPATH_XML_TOKEN_TARGET_ALPHA           "target_alpha"
+#define GAP_MOVPATH_XML_TOKEN_KEYCOLOR_R             "keycolor_r"
+#define GAP_MOVPATH_XML_TOKEN_KEYCOLOR_G             "keycolor_g"
+#define GAP_MOVPATH_XML_TOKEN_KEYCOLOR_B             "keycolor_b"
+#define GAP_MOVPATH_XML_TOKEN_CONTROLPOINTS          "controlpoints"
+#define GAP_MOVPATH_XML_TOKEN_CURRENT_POINT          "current_point"
+#define GAP_MOVPATH_XML_TOKEN_NUMBER_OF_POINTS       "number_of_points"
+#define GAP_MOVPATH_XML_TOKEN_CONTROLPOINT           "controlpoint"
+#define GAP_MOVPATH_XML_TOKEN_PX                     "px"
+#define GAP_MOVPATH_XML_TOKEN_PY                     "py"
+#define GAP_MOVPATH_XML_TOKEN_KEYFRAME               "keyframe"
+#define GAP_MOVPATH_XML_TOKEN_SEL_FEATHER_RADIUS     "sel_feather_radius"
+#define GAP_MOVPATH_XML_TOKEN_W_RESIZE               "width_resize"
+#define GAP_MOVPATH_XML_TOKEN_H_RESIZE               "height_resize"
+#define GAP_MOVPATH_XML_TOKEN_OPACITY                "opacity"
+#define GAP_MOVPATH_XML_TOKEN_ROTATION               "rotation"
+#define GAP_MOVPATH_XML_TOKEN_TTLX                   "ttlx"
+#define GAP_MOVPATH_XML_TOKEN_TTLY                   "ttly"
+#define GAP_MOVPATH_XML_TOKEN_TTRX                   "ttrx"
+#define GAP_MOVPATH_XML_TOKEN_TTRY                   "ttry"
+#define GAP_MOVPATH_XML_TOKEN_TBLX                   "tblx"
+#define GAP_MOVPATH_XML_TOKEN_TBLY                   "tbly"
+#define GAP_MOVPATH_XML_TOKEN_TBRX                   "tbrx"
+#define GAP_MOVPATH_XML_TOKEN_TBRY                   "tbry"
+#define GAP_MOVPATH_XML_TOKEN_ACC_POSITION           "acc_position"
+#define GAP_MOVPATH_XML_TOKEN_ACC_OPACITY            "acc_opacity"
+#define GAP_MOVPATH_XML_TOKEN_ACC_SIZE               "acc_size"
+#define GAP_MOVPATH_XML_TOKEN_ACC_ROTATION           "acc_rotation"
+#define GAP_MOVPATH_XML_TOKEN_ACC_PERSPECTIVE        "acc_perspective"
+#define GAP_MOVPATH_XML_TOKEN_ACC_SEL_FEATHER_RADIUS "acc_sel_feather_radius"
+
+
+
+
+/* user data passed to parser fuctions */
+typedef struct {
+  GapMovValues *pvals;
+  gboolean      isScopeValid;
+  gboolean      isParseOk;
+  gint          errorLineNumber;
+} GapMovXmlUserData;
+
+/* Function signatures for parsing xml elements and attributes
+ */
+typedef void (*GapMovXmlElementParseFunctionType) (const gchar         *element_name,
+    const gchar        **attribute_names,
+    const gchar        **attribute_values,
+    GapMovXmlUserData   *userDataPtr,
+    gint                count
+    );
+
+
+/* functions for parsing the XML elements */
+
+static void  p_xml_parse_element_root(const gchar         *element_name,
+    const gchar        **attribute_names,
+    const gchar        **attribute_values,
+    GapMovXmlUserData   *userDataPtr,
+    gint                 count);
+static void  p_xml_parse_element_frame_description(const gchar         *element_name,
+    const gchar        **attribute_names,
+    const gchar        **attribute_values,
+    GapMovXmlUserData   *userDataPtr,
+    gint                 count);
+static void  p_xml_parse_element_tween(const gchar         *element_name,
+    const gchar        **attribute_names,
+    const gchar        **attribute_values,
+    GapMovXmlUserData   *userDataPtr,
+    gint                 count);
+static void  p_xml_parse_element_trace(const gchar         *element_name,
+    const gchar        **attribute_names,
+    const gchar        **attribute_values,
+    GapMovXmlUserData   *userDataPtr,
+    gint                 count);
+static void  p_xml_parse_element_moving_object(const gchar         *element_name,
+    const gchar        **attribute_names,
+    const gchar        **attribute_values,
+    GapMovXmlUserData   *userDataPtr,
+    gint                 count);
+static void  p_xml_parse_element_trace(const gchar         *element_name,
+    const gchar        **attribute_names,
+    const gchar        **attribute_values,
+    GapMovXmlUserData   *userDataPtr,
+    gint                 count);
+static void  p_xml_parse_element_bluebox_parameters(const gchar         *element_name,
+    const gchar        **attribute_names,
+    const gchar        **attribute_values,
+    GapMovXmlUserData   *userDataPtr,
+    gint                 count);
+static void  p_xml_parse_element_controlpoints(const gchar         *element_name,
+    const gchar        **attribute_names,
+    const gchar        **attribute_values,
+    GapMovXmlUserData   *userDataPtr,
+    gint                 count);
+static void  p_xml_parse_element_controlpoint(const gchar         *element_name,
+    const gchar        **attribute_names,
+    const gchar        **attribute_values,
+    GapMovXmlUserData   *userDataPtr,
+    gint                 count);
+    
+
+
+
+typedef struct JmpTableElement
+{
+  gint                                 count;
+  gboolean                             isRequired;
+  const gchar                         *name;
+  GapMovXmlElementParseFunctionType    func_ptr;
+} JmpTableElement;
+
+
+/* ------------------------
+ * STATIC DATA
+ * ------------------------
+ */
+
+
+extern      int gap_debug; /* ==0  ... dont print debug infos */
+
+
+/* jump table describes the supported xml element names and their parsing functions */
+static  JmpTableElement    jmpTableElement[] = {
+  {0, TRUE,  GAP_MOVPATH_XML_TOKEN_ROOT,                    p_xml_parse_element_root }
+ ,{0, TRUE,  GAP_MOVPATH_XML_TOKEN_FRAME_DESCRIPTION,       p_xml_parse_element_frame_description }
+ ,{0, FALSE, GAP_MOVPATH_XML_TOKEN_TWEEN,                   p_xml_parse_element_tween }
+ ,{0, FALSE, GAP_MOVPATH_XML_TOKEN_TRACE,                   p_xml_parse_element_trace }
+ ,{0, TRUE,  GAP_MOVPATH_XML_TOKEN_MOVING_OBJECT,           p_xml_parse_element_moving_object }
+ ,{0, FALSE, GAP_MOVPATH_XML_TOKEN_BLUEBOX_PARAMETERS,      p_xml_parse_element_bluebox_parameters }
+ ,{0, TRUE,  GAP_MOVPATH_XML_TOKEN_CONTROLPOINTS,           p_xml_parse_element_controlpoints }
+ ,{0, TRUE,  GAP_MOVPATH_XML_TOKEN_CONTROLPOINT,            p_xml_parse_element_controlpoint }
+ ,{0, FALSE, NULL,                                          NULL }
+};
+
+
+static const GEnumValue valuesGapMovHandle[] =
+{
+  { GAP_HANDLE_LEFT_TOP,         "GAP_HANDLE_LEFT_TOP", NULL },
+  { GAP_HANDLE_LEFT_BOT,         "GAP_HANDLE_LEFT_BOT", NULL },
+  { GAP_HANDLE_RIGHT_TOP,        "GAP_HANDLE_RIGHT_TOP", NULL },
+  { GAP_HANDLE_RIGHT_BOT,        "GAP_HANDLE_RIGHT_BOT", NULL },
+  { GAP_HANDLE_CENTER,           "GAP_HANDLE_CENTER", NULL },
+  { 0,                           NULL, NULL }
+};
+
+static const GEnumValue valuesGapMovStepMode[] =
+{
+  { GAP_STEP_LOOP,              "GAP_STEP_LOOP", NULL },
+  { GAP_STEP_LOOP_REV,          "GAP_STEP_LOOP_REV", NULL },
+  { GAP_STEP_ONCE,              "GAP_STEP_ONCE", NULL },
+  { GAP_STEP_ONCE_REV,          "GAP_STEP_ONCE_REV", NULL },
+  { GAP_STEP_PING_PONG,         "GAP_STEP_PING_PONG", NULL },
+  { GAP_STEP_NONE,              "GAP_STEP_NONE", NULL },
+  { GAP_STEP_FRAME_LOOP,        "GAP_STEP_FRAME_LOOP", NULL },
+  { GAP_STEP_FRAME_LOOP_REV,    "GAP_STEP_FRAME_LOOP_REV", NULL },
+  { GAP_STEP_FRAME_ONCE,        "GAP_STEP_FRAME_ONCE", NULL },
+  { GAP_STEP_FRAME_ONCE_REV,    "GAP_STEP_FRAME_ONCE_REV", NULL },
+  { GAP_STEP_FRAME_PING_PONG,   "GAP_STEP_FRAME_PING_PONG", NULL },
+  { GAP_STEP_FRAME_NONE,        "GAP_STEP_FRAME_NONE", NULL },
+  { 0,                          NULL, NULL }
+};
+
+static const GEnumValue valuesGapMovSelMode[] =
+{
+  { GAP_MOV_SEL_IGNORE,         "GAP_MOV_SEL_IGNORE", NULL },
+  { GAP_MOV_SEL_INITIAL,        "GAP_MOV_SEL_INITIAL", NULL },
+  { GAP_MOV_SEL_FRAME_SPECIFIC, "GAP_MOV_SEL_FRAME_SPECIFIC", NULL },
+  { 0,                          NULL, NULL }
+};
+
+static const GEnumValue valuesGimpPaintmode[] =
+{
+  { GIMP_NORMAL_MODE,           "GIMP_NORMAL_MODE", "normal-mode" },
+  { GIMP_DISSOLVE_MODE,         "GIMP_DISSOLVE_MODE", "dissolve-mode" },
+  { GIMP_BEHIND_MODE,           "GIMP_BEHIND_MODE", "behind-mode" },
+  { GIMP_MULTIPLY_MODE,         "GIMP_MULTIPLY_MODE", "multiply-mode" },
+  { GIMP_SCREEN_MODE,           "GIMP_SCREEN_MODE", "screen-mode" },
+  { GIMP_OVERLAY_MODE,          "GIMP_OVERLAY_MODE", "overlay-mode" },
+  { GIMP_DIFFERENCE_MODE,       "GIMP_DIFFERENCE_MODE", "difference-mode" },
+  { GIMP_ADDITION_MODE,         "GIMP_ADDITION_MODE", "addition-mode" },
+  { GIMP_SUBTRACT_MODE,         "GIMP_SUBTRACT_MODE", "subtract-mode" },
+  { GIMP_DARKEN_ONLY_MODE,      "GIMP_DARKEN_ONLY_MODE", "darken-only-mode" },
+  { GIMP_LIGHTEN_ONLY_MODE,     "GIMP_LIGHTEN_ONLY_MODE", "lighten-only-mode" },
+  { GIMP_HUE_MODE,              "GIMP_HUE_MODE", "hue-mode" },
+  { GIMP_SATURATION_MODE,       "GIMP_SATURATION_MODE", "saturation-mode" },
+  { GIMP_COLOR_MODE,            "GIMP_COLOR_MODE", "color-mode" },
+  { GIMP_VALUE_MODE,            "GIMP_VALUE_MODE", "value-mode" },
+  { GIMP_DIVIDE_MODE,           "GIMP_DIVIDE_MODE", "divide-mode" },
+  { GIMP_DODGE_MODE,            "GIMP_DODGE_MODE", "dodge-mode" },
+  { GIMP_BURN_MODE,             "GIMP_BURN_MODE", "burn-mode" },
+  { GIMP_HARDLIGHT_MODE,        "GIMP_HARDLIGHT_MODE", "hardlight-mode" },
+  { GIMP_SOFTLIGHT_MODE,        "GIMP_SOFTLIGHT_MODE", "softlight-mode" },
+  { GIMP_GRAIN_EXTRACT_MODE,    "GIMP_GRAIN_EXTRACT_MODE", "grain-extract-mode" },
+  { GIMP_GRAIN_MERGE_MODE,      "GIMP_GRAIN_MERGE_MODE", "grain-merge-mode" },
+  { GIMP_COLOR_ERASE_MODE,      "GIMP_COLOR_ERASE_MODE", "color-erase-mode" },
+  { GAP_MOV_KEEP_SRC_PAINTMODE, "GAP_MOV_KEEP_SRC_PAINTMODE", "keep-paintmode" },
+  { 0,                          NULL, NULL }
+};
+
+
+
+static const GEnumValue valuesGapBlueboxThresMode[] =
+{
+  { GAP_BLUBOX_THRES_RGB,          "GAP_BLUBOX_THRES_RGB", NULL },
+  { GAP_BLUBOX_THRES_HSV,          "GAP_BLUBOX_THRES_HSV", NULL },
+  { GAP_BLUBOX_THRES_VAL,          "GAP_BLUBOX_THRES_VAL", NULL },
+  { GAP_BLUBOX_THRES_ALL,          "GAP_BLUBOX_THRES_ALL", NULL },
+  { 0,                             NULL, NULL }
+};
+
+
+
+/* 
+ * XML PARSER procedures
+ */
+
+
+
+/* --------------------------------------
+ * p_xml_parse_value_GapMovHandle
+ * --------------------------------------
+ */
+static gboolean
+p_xml_parse_value_GapMovHandle(const gchar *attribute_value, GapMovHandle *valDestPtr)
+{
+  gboolean isOk;
+  gint     value;
+
+  isOk = gap_xml_parse_EnumValue_as_gint(attribute_value, &value, &valuesGapMovHandle[0]);
+  if(isOk)
+  {
+    *valDestPtr = value;
+  }
+  return (isOk);
+  
+}  /* end p_xml_parse_value_GapMovHandle */
+
+
+
+/* --------------------------------------
+ * p_xml_parse_value_GapMovStepMode
+ * --------------------------------------
+ */
+static gboolean
+p_xml_parse_value_GapMovStepMode(const gchar *attribute_value, GapMovStepMode *valDestPtr)
+{
+  gboolean isOk;
+  gint     value;
+
+  isOk = gap_xml_parse_EnumValue_as_gint(attribute_value, &value, &valuesGapMovStepMode[0]);
+  if(isOk)
+  {
+    *valDestPtr = value;
+  }
+  return (isOk);
+
+}  /* end p_xml_parse_value_GapMovStepMode */
+
+
+/* --------------------------------------
+ * p_xml_parse_value_GapMovSelMode
+ * --------------------------------------
+ */
+static gboolean
+p_xml_parse_value_GapMovSelMode(const gchar *attribute_value, GapMovSelMode *valDestPtr)
+{
+  gboolean isOk;
+  gint     value;
+
+  isOk = gap_xml_parse_EnumValue_as_gint(attribute_value, &value, &valuesGapMovSelMode[0]);
+  if(isOk)
+  {
+    *valDestPtr = value;
+  }
+  return (isOk);
+  
+}  /* end p_xml_parse_value_GapMovSelMode */
+
+
+/* ---------------------------------------
+ * p_xml_parse_value_GimpPaintmode_as_gint
+ * ---------------------------------------
+ */
+static gboolean
+p_xml_parse_value_GimpPaintmode_as_gint(const gchar *attribute_value, gint *valDestPtr)
+{
+  gboolean isOk;
+  gint     value;
+
+  isOk = gap_xml_parse_EnumValue_as_gint(attribute_value, &value, &valuesGimpPaintmode[0]);
+  if(isOk)
+  {
+    *valDestPtr = value;
+  }
+  return (isOk);
+  
+}  /* end gap_xml_parse_value_GimpPaintmode */
+
+
+/* ----------------------------------------
+ * p_xml_parse_value_GapBlueboxThresMode
+ * ----------------------------------------
+ */
+static gboolean
+p_xml_parse_value_GapBlueboxThresMode(const gchar *attribute_value, GapBlueboxThresMode *valDestPtr)
+{
+  gboolean isOk;
+  gint     value;
+
+  isOk = gap_xml_parse_EnumValue_as_gint(attribute_value, &value, &valuesGapBlueboxThresMode[0]);
+  if(isOk)
+  {
+    *valDestPtr = value;
+  }
+  return (isOk);
+  
+}  /* end p_xml_parse_value_GapBlueboxThresMode */
+
+
+
+
+/* --------------------------------------
+ * p_xml_parse_element_root
+ * --------------------------------------
+ */
+static void 
+p_xml_parse_element_root(const gchar         *element_name,
+    const gchar        **attribute_names,
+    const gchar        **attribute_values,
+    GapMovXmlUserData   *userDataPtr,
+    gint                 count)
+{
+  const gchar **name_cursor = attribute_names;
+  const gchar **value_cursor = attribute_values;
+
+  if(count > 0)
+  {
+    userDataPtr->isParseOk = FALSE;
+  }
+
+  while ((*name_cursor) && (userDataPtr->isParseOk))
+  {
+    if (strcmp (*name_cursor, GAP_MOVPATH_XML_TOKEN_VERSION) == 0)
+    {
+      userDataPtr->isParseOk = gap_xml_parse_value_gint32(*value_cursor, &userDataPtr->pvals->version);
+    }
+    name_cursor++;
+    value_cursor++;
+  }
+}   /* end  p_xml_parse_element_root */
+
+
+
+
+/* --------------------------------------
+ * p_xml_parse_element_frame_description
+ * --------------------------------------
+ */
+static void 
+p_xml_parse_element_frame_description(const gchar         *element_name,
+    const gchar        **attribute_names,
+    const gchar        **attribute_values,
+    GapMovXmlUserData   *userDataPtr,
+    gint                 count)
+{
+  const gchar **name_cursor = attribute_names;
+  const gchar **value_cursor = attribute_values;
+
+  if(count > 0)
+  {
+    userDataPtr->isParseOk = FALSE;
+  }
+
+  while ((*name_cursor) && (userDataPtr->isParseOk))
+  {
+    if (strcmp (*name_cursor, GAP_MOVPATH_XML_TOKEN_WIDTH) == 0)
+    {
+      userDataPtr->isParseOk = gap_xml_parse_value_gint32(*value_cursor, &userDataPtr->pvals->recordedFrameWidth);
+    }
+    else if (strcmp (*name_cursor, GAP_MOVPATH_XML_TOKEN_HEIGHT) == 0)
+    {
+      userDataPtr->isParseOk = gap_xml_parse_value_gint32(*value_cursor, &userDataPtr->pvals->recordedFrameHeight);
+    }
+    else if (strcmp (*name_cursor, GAP_MOVPATH_XML_TOKEN_RANGE_FROM) == 0)
+    {
+      userDataPtr->isParseOk = gap_xml_parse_value_gint(*value_cursor, &userDataPtr->pvals->dst_range_start);
+    }
+    else if (strcmp (*name_cursor, GAP_MOVPATH_XML_TOKEN_RANGE_TO) == 0)
+    {
+      userDataPtr->isParseOk = gap_xml_parse_value_gint(*value_cursor, &userDataPtr->pvals->dst_range_end);
+    }
+    else if (strcmp (*name_cursor, GAP_MOVPATH_XML_TOKEN_RANGE_TO) == 0)
+    {
+      userDataPtr->isParseOk = gap_xml_parse_value_gint32(*value_cursor, &userDataPtr->pvals->total_frames);
+    }
+    
+    name_cursor++;
+    value_cursor++;
+  }
+}   /* end  p_xml_parse_element_frame_description */
+
+
+/* --------------------------------------
+ * p_xml_parse_element_tween
+ * --------------------------------------
+ */
+static void 
+p_xml_parse_element_tween(const gchar         *element_name,
+    const gchar        **attribute_names,
+    const gchar        **attribute_values,
+    GapMovXmlUserData   *userDataPtr,
+    gint                 count)
+{
+  const gchar **name_cursor = attribute_names;
+  const gchar **value_cursor = attribute_values;
+
+  if(count > 0)
+  {
+    userDataPtr->isParseOk = FALSE;
+  }
+
+  while ((*name_cursor) && (userDataPtr->isParseOk))
+  {
+    if (strcmp (*name_cursor, GAP_MOVPATH_XML_TOKEN_TWEEN_STEPS) == 0)
+    {
+      userDataPtr->isParseOk = gap_xml_parse_value_gint(*value_cursor, &userDataPtr->pvals->tween_steps);
+    }
+    else if (strcmp (*name_cursor, GAP_MOVPATH_XML_TOKEN_TWEEN_OPACITY_INITIAL) == 0)
+    {
+      userDataPtr->isParseOk = gap_xml_parse_value_gdouble(*value_cursor, &userDataPtr->pvals->tween_opacity_initial);
+    }
+    else if (strcmp (*name_cursor, GAP_MOVPATH_XML_TOKEN_TWEEN_OPACITY_DESC) == 0)
+    {
+      userDataPtr->isParseOk = gap_xml_parse_value_gdouble(*value_cursor, &userDataPtr->pvals->tween_opacity_desc);
+    }
+    name_cursor++;
+    value_cursor++;
+  }
+}   /* end  p_xml_parse_element_tween */
+
+
+
+
+/* --------------------------------------
+ * p_xml_parse_element_trace
+ * --------------------------------------
+ */
+static void 
+p_xml_parse_element_trace(const gchar         *element_name,
+    const gchar        **attribute_names,
+    const gchar        **attribute_values,
+    GapMovXmlUserData   *userDataPtr,
+    gint                 count)
+{
+  const gchar **name_cursor = attribute_names;
+  const gchar **value_cursor = attribute_values;
+
+  if(count > 0)
+  {
+    userDataPtr->isParseOk = FALSE;
+  }
+
+  while ((*name_cursor) && (userDataPtr->isParseOk))
+  {
+    if (strcmp (*name_cursor, GAP_MOVPATH_XML_TOKEN_TRACELAYER_ENABLE) == 0)
+    {
+      userDataPtr->isParseOk = gap_xml_parse_value_gboolean_as_gint(*value_cursor, &userDataPtr->pvals->tracelayer_enable);
+    }
+    else if (strcmp (*name_cursor, GAP_MOVPATH_XML_TOKEN_TRACE_OPACITY_INITIAL) == 0)
+    {
+      userDataPtr->isParseOk = gap_xml_parse_value_gdouble(*value_cursor, &userDataPtr->pvals->trace_opacity_initial);
+    }
+    else if (strcmp (*name_cursor, GAP_MOVPATH_XML_TOKEN_TRACE_OPACITY_DESC) == 0)
+    {
+      userDataPtr->isParseOk = gap_xml_parse_value_gdouble(*value_cursor, &userDataPtr->pvals->trace_opacity_desc);
+    }
+    name_cursor++;
+    value_cursor++;
+  }
+}   /* end  p_xml_parse_element_trace */
+
+
+
+
+/* --------------------------------------
+ * p_xml_parse_element_moving_object
+ * --------------------------------------
+ */
+static void 
+p_xml_parse_element_moving_object(const gchar         *element_name,
+    const gchar        **attribute_names,
+    const gchar        **attribute_values,
+    GapMovXmlUserData   *userDataPtr,
+    gint                 count)
+{
+  const gchar **name_cursor = attribute_names;
+  const gchar **value_cursor = attribute_values;
+
+  if(count > 0)
+  {
+    userDataPtr->isParseOk = FALSE;
+  }
+
+  while ((*name_cursor) && (userDataPtr->isParseOk))
+  {
+    if (strcmp (*name_cursor, GAP_MOVPATH_XML_TOKEN_SRC_STEPMODE) == 0)
+    {
+      userDataPtr->isParseOk = p_xml_parse_value_GapMovStepMode(*value_cursor, &userDataPtr->pvals->src_stepmode);
+    }
+    else if (strcmp (*name_cursor, GAP_MOVPATH_XML_TOKEN_WIDTH) == 0)
+    {
+      userDataPtr->isParseOk = gap_xml_parse_value_gint32(*value_cursor, &userDataPtr->pvals->recordedObjWidth);
+    }
+    else if (strcmp (*name_cursor, GAP_MOVPATH_XML_TOKEN_HEIGHT) == 0)
+    {
+      userDataPtr->isParseOk = gap_xml_parse_value_gint32(*value_cursor, &userDataPtr->pvals->recordedObjHeight);
+    }
+    else if (strcmp (*name_cursor, GAP_MOVPATH_XML_TOKEN_SRC_HANDLE) == 0)
+    {
+      userDataPtr->isParseOk = p_xml_parse_value_GapMovHandle(*value_cursor, &userDataPtr->pvals->src_handle);
+    }
+    else if (strcmp (*name_cursor, GAP_MOVPATH_XML_TOKEN_SRC_SELMODE) == 0)
+    {
+      userDataPtr->isParseOk = p_xml_parse_value_GapMovSelMode(*value_cursor, &userDataPtr->pvals->src_selmode);
+    }
+    else if (strcmp (*name_cursor, GAP_MOVPATH_XML_TOKEN_SRC_PAINTMODE) == 0)
+    {
+      userDataPtr->isParseOk = p_xml_parse_value_GimpPaintmode_as_gint(*value_cursor, &userDataPtr->pvals->src_paintmode);
+    }
+    else if (strcmp (*name_cursor, GAP_MOVPATH_XML_TOKEN_DST_LAYERSTACK) == 0)
+    {
+      userDataPtr->isParseOk = gap_xml_parse_value_gint(*value_cursor, &userDataPtr->pvals->dst_layerstack);
+    }
+    else if (strcmp (*name_cursor, GAP_MOVPATH_XML_TOKEN_STEP_SPEED_FACTOR) == 0)
+    {
+      userDataPtr->isParseOk = gap_xml_parse_value_gdouble(*value_cursor, &userDataPtr->pvals->step_speed_factor);
+    }
+    else if (strcmp (*name_cursor, GAP_MOVPATH_XML_TOKEN_SRC_FORCE_VISIBLE) == 0)
+    {
+      userDataPtr->isParseOk = gap_xml_parse_value_gboolean_as_gint(*value_cursor, &userDataPtr->pvals->src_force_visible);
+    }
+    else if (strcmp (*name_cursor, GAP_MOVPATH_XML_TOKEN_CLIP_TO_IMG) == 0)
+    {
+      userDataPtr->isParseOk = gap_xml_parse_value_gboolean_as_gint(*value_cursor, &userDataPtr->pvals->clip_to_img);
+    }
+    else if (strcmp (*name_cursor, GAP_MOVPATH_XML_TOKEN_SRC_APPLY_BLUEBOX) == 0)
+    {
+      userDataPtr->isParseOk = gap_xml_parse_value_gboolean_as_gint(*value_cursor, &userDataPtr->pvals->src_apply_bluebox);
+    }
+    else if (strcmp (*name_cursor, GAP_MOVPATH_XML_TOKEN_SRC_LAYERSTACK) == 0)
+    {
+      userDataPtr->isParseOk = gap_xml_parse_value_gint32(*value_cursor, &userDataPtr->pvals->src_layerstack);
+    }
+    else if (strcmp (*name_cursor, GAP_MOVPATH_XML_TOKEN_SRC_FILENAME) == 0)
+    {
+      if(userDataPtr->pvals->src_filename != NULL)
+      {
+        g_free(userDataPtr->pvals->src_filename);
+        userDataPtr->pvals->src_filename = NULL;
+      }
+      if(*value_cursor != NULL)
+      {
+        userDataPtr->pvals->src_filename = g_strdup(*value_cursor);
+      }
+    }
+
+    name_cursor++;
+    value_cursor++;
+  }
+}   /* end  p_xml_parse_element_moving_object */
+
+
+/* --------------------------------------
+ * p_xml_parse_element_bluebox_parameters
+ * --------------------------------------
+ */
+static void 
+p_xml_parse_element_bluebox_parameters(const gchar         *element_name,
+    const gchar        **attribute_names,
+    const gchar        **attribute_values,
+    GapMovXmlUserData   *userDataPtr,
+    gint                 count)
+{
+  const gchar **name_cursor = attribute_names;
+  const gchar **value_cursor = attribute_values;
+  
+  GapBlueboxGlobalParams *bbp;
+
+  if(count > 0)
+  {
+    userDataPtr->isParseOk = FALSE;
+  }
+
+  if(userDataPtr->pvals->bbp == NULL)
+  {
+    gint32 layer_id;
+   
+    layer_id = -1;
+    userDataPtr->pvals->bbp = gap_bluebox_bbp_new(layer_id);
+    gap_bluebox_init_default_vals(userDataPtr->pvals->bbp);
+  }
+  bbp = userDataPtr->pvals->bbp;
+
+
+  while ((*name_cursor) && (userDataPtr->isParseOk))
+  {
+    if (strcmp (*name_cursor, GAP_MOVPATH_XML_TOKEN_THRES_MODE) == 0)
+    {
+      userDataPtr->isParseOk = p_xml_parse_value_GapBlueboxThresMode(*value_cursor, &bbp->vals.thres_mode);
+    }
+    else if (strcmp (*name_cursor, GAP_MOVPATH_XML_TOKEN_THRES_R) == 0)
+    {
+      userDataPtr->isParseOk = gap_xml_parse_value_gdouble(*value_cursor, &bbp->vals.thres_r);
+    }
+    else if (strcmp (*name_cursor, GAP_MOVPATH_XML_TOKEN_THRES_G) == 0)
+    {
+      userDataPtr->isParseOk = gap_xml_parse_value_gdouble(*value_cursor, &bbp->vals.thres_g);
+    }
+    else if (strcmp (*name_cursor, GAP_MOVPATH_XML_TOKEN_THRES_B) == 0)
+    {
+      userDataPtr->isParseOk = gap_xml_parse_value_gdouble(*value_cursor, &bbp->vals.thres_b);
+    }
+    else if (strcmp (*name_cursor, GAP_MOVPATH_XML_TOKEN_THRES_H) == 0)
+    {
+      userDataPtr->isParseOk = gap_xml_parse_value_gdouble(*value_cursor, &bbp->vals.thres_h);
+    }
+    else if (strcmp (*name_cursor, GAP_MOVPATH_XML_TOKEN_THRES_S) == 0)
+    {
+      userDataPtr->isParseOk = gap_xml_parse_value_gdouble(*value_cursor, &bbp->vals.thres_s);
+    }
+    else if (strcmp (*name_cursor, GAP_MOVPATH_XML_TOKEN_THRES_V) == 0)
+    {
+      userDataPtr->isParseOk = gap_xml_parse_value_gdouble(*value_cursor, &bbp->vals.thres_v);
+    }
+    else if (strcmp (*name_cursor, GAP_MOVPATH_XML_TOKEN_THRES) == 0)
+    {
+      userDataPtr->isParseOk = gap_xml_parse_value_gdouble(*value_cursor, &bbp->vals.thres);
+    }
+    else if (strcmp (*name_cursor, GAP_MOVPATH_XML_TOKEN_TOLERANCE) == 0)
+    {
+      userDataPtr->isParseOk = gap_xml_parse_value_gdouble(*value_cursor, &bbp->vals.tolerance);
+    }
+    else if (strcmp (*name_cursor, GAP_MOVPATH_XML_TOKEN_GROW) == 0)
+    {
+      userDataPtr->isParseOk = gap_xml_parse_value_gdouble(*value_cursor, &bbp->vals.grow);
+    }
+    else if (strcmp (*name_cursor, GAP_MOVPATH_XML_TOKEN_FEATHER_EDGES) == 0)
+    {
+      userDataPtr->isParseOk = gap_xml_parse_value_gboolean_as_gint(*value_cursor, &bbp->vals.feather_edges);
+    }
+    else if (strcmp (*name_cursor, GAP_MOVPATH_XML_TOKEN_FEATHER_RADIUS) == 0)
+    {
+      userDataPtr->isParseOk = gap_xml_parse_value_gdouble(*value_cursor, &bbp->vals.feather_radius);
+    }
+    else if (strcmp (*name_cursor, GAP_MOVPATH_XML_TOKEN_SOURCE_ALPHA) == 0)
+    {
+      userDataPtr->isParseOk = gap_xml_parse_value_gdouble(*value_cursor, &bbp->vals.source_alpha);
+    }
+    else if (strcmp (*name_cursor, GAP_MOVPATH_XML_TOKEN_TARGET_ALPHA) == 0)
+    {
+      userDataPtr->isParseOk = gap_xml_parse_value_gdouble(*value_cursor, &bbp->vals.target_alpha);
+    }
+    else if (strcmp (*name_cursor, GAP_MOVPATH_XML_TOKEN_KEYCOLOR_R) == 0)
+    {
+      userDataPtr->isParseOk = gap_xml_parse_value_gdouble(*value_cursor, &bbp->vals.keycolor.r);
+    }
+    else if (strcmp (*name_cursor, GAP_MOVPATH_XML_TOKEN_KEYCOLOR_G) == 0)
+    {
+      userDataPtr->isParseOk = gap_xml_parse_value_gdouble(*value_cursor, &bbp->vals.keycolor.g);
+    }
+    else if (strcmp (*name_cursor, GAP_MOVPATH_XML_TOKEN_KEYCOLOR_B) == 0)
+    {
+      userDataPtr->isParseOk = gap_xml_parse_value_gdouble(*value_cursor, &bbp->vals.keycolor.b);
+    }
+
+    name_cursor++;
+    value_cursor++;
+  }
+}   /* end  p_xml_parse_element_bluebox_parameters */
+
+
+/* --------------------------------------
+ * p_xml_parse_element_controlpoints
+ * --------------------------------------
+ */
+static void 
+p_xml_parse_element_controlpoints(const gchar         *element_name,
+    const gchar        **attribute_names,
+    const gchar        **attribute_values,
+    GapMovXmlUserData   *userDataPtr,
+    gint                 count)
+{
+  const gchar **name_cursor = attribute_names;
+  const gchar **value_cursor = attribute_values;
+  
+  if(count > 0)
+  {
+    userDataPtr->isParseOk = FALSE;
+  }
+  
+  while ((*name_cursor) && (userDataPtr->isParseOk))
+  {
+    if (strcmp (*name_cursor, GAP_MOVPATH_XML_TOKEN_CURRENT_POINT) == 0)
+    {
+      userDataPtr->isParseOk = gap_xml_parse_value_gint(*value_cursor, &userDataPtr->pvals->point_idx);
+    }
+    else if (strcmp (*name_cursor, GAP_MOVPATH_XML_TOKEN_NUMBER_OF_POINTS) == 0)
+    {
+      gint numberOfPoints;
+      userDataPtr->isParseOk = gap_xml_parse_value_gint(*value_cursor, &numberOfPoints);
+      
+      if(userDataPtr->isParseOk)
+      {
+        if((numberOfPoints < GAP_MOV_MAX_POINT) && (numberOfPoints > 0))
+        {
+          userDataPtr->pvals->point_idx_max = numberOfPoints -1;
+        }
+        else
+        {
+          userDataPtr->isParseOk = FALSE;
+        }
+      }
+      
+    }
+
+    name_cursor++;
+    value_cursor++;
+  }
+}   /* end  p_xml_parse_element_controlpoints */
+
+
+
+/* ----------------------------------------
+ * p_set_load_defaults_for_one_controlpoint
+ * ----------------------------------------
+ */
+static void
+p_set_load_defaults_for_one_controlpoint(GapMovValues *pvals, gint idx)
+{
+  if((idx >= 0) && (idx < GAP_MOV_MAX_POINT))
+  {
+    pvals->point[idx].p_x  = 0;
+    pvals->point[idx].p_y  = 0;
+    pvals->point[idx].opacity  = 100.0; /* 100 percent (no transparecy) */
+    pvals->point[idx].w_resize = 100.0; /* 100%  no resizize (1:1) */
+    pvals->point[idx].h_resize = 100.0; /* 100%  no resizize (1:1) */
+    pvals->point[idx].rotation = 0.0;   /* no rotation (0 degree) */
+    /* 1.0 for all perspective transform factors (== no perspective transformation) */
+    pvals->point[idx].ttlx      = 1.0;
+    pvals->point[idx].ttly      = 1.0;
+    pvals->point[idx].ttrx      = 1.0;
+    pvals->point[idx].ttry      = 1.0;
+    pvals->point[idx].tblx      = 1.0;
+    pvals->point[idx].tbly      = 1.0;
+    pvals->point[idx].tbrx      = 1.0;
+    pvals->point[idx].tbry      = 1.0;
+    pvals->point[idx].sel_feather_radius = 0.0;
+    pvals->point[idx].keyframe = 0;   /* 0: controlpoint is not fixed to keyframe */
+    pvals->point[idx].keyframe_abs = 0;   /* 0: controlpoint is not fixed to keyframe */
+    
+    pvals->point[idx].accPosition = 0;           /* 0: linear (e.g NO acceleration) is default */
+    pvals->point[idx].accOpacity = 0;            /* 0: linear (e.g NO acceleration) is default */
+    pvals->point[idx].accSize = 0;               /* 0: linear (e.g NO acceleration) is default */
+    pvals->point[idx].accRotation = 0;           /* 0: linear (e.g NO acceleration) is default */
+    pvals->point[idx].accPerspective = 0;        /* 0: linear (e.g NO acceleration) is default */
+    pvals->point[idx].accSelFeatherRadius = 0;   /* 0: linear (e.g NO acceleration) is default */
+
+  }
+  
+}  /* end p_set_load_defaults_for_one_controlpoint  */
+
+
+
+/* --------------------------------------
+ * p_xml_parse_element_controlpoint
+ * --------------------------------------
+ */
+static void 
+p_xml_parse_element_controlpoint(const gchar         *element_name,
+    const gchar        **attribute_names,
+    const gchar        **attribute_values,
+    GapMovXmlUserData   *userDataPtr,
+    gint                 count)
+{
+  const gchar **name_cursor = attribute_names;
+  const gchar **value_cursor = attribute_values;
+  
+  if(count >= GAP_MOV_MAX_POINT)
+  {
+    userDataPtr->isParseOk = FALSE;
+  }
+  
+  p_set_load_defaults_for_one_controlpoint(userDataPtr->pvals, count);
+  
+  while ((*name_cursor) && (userDataPtr->isParseOk))
+  {
+    if (strcmp (*name_cursor, GAP_MOVPATH_XML_TOKEN_PX) == 0)
+    {
+      userDataPtr->isParseOk = gap_xml_parse_value_gint(*value_cursor, &userDataPtr->pvals->point[count].p_x);
+    }
+    else if (strcmp (*name_cursor, GAP_MOVPATH_XML_TOKEN_PY) == 0)
+    {
+      userDataPtr->isParseOk = gap_xml_parse_value_gint(*value_cursor, &userDataPtr->pvals->point[count].p_y);
+    }
+    else if (strcmp (*name_cursor, GAP_MOVPATH_XML_TOKEN_KEYFRAME) == 0)
+    {
+      gint keyframe;
+      
+      userDataPtr->isParseOk = gap_xml_parse_value_gint(*value_cursor, &keyframe);
+      userDataPtr->pvals->point[count].keyframe = keyframe;
+      userDataPtr->pvals->point[count].keyframe_abs = gap_mov_exec_conv_keyframe_to_abs(keyframe, userDataPtr->pvals);
+
+    }
+    else if (strcmp (*name_cursor, GAP_MOVPATH_XML_TOKEN_SEL_FEATHER_RADIUS) == 0)
+    {
+      userDataPtr->isParseOk = gap_xml_parse_value_gdouble(*value_cursor, &userDataPtr->pvals->point[count].sel_feather_radius);
+    }
+    else if (strcmp (*name_cursor, GAP_MOVPATH_XML_TOKEN_W_RESIZE) == 0)
+    {
+      userDataPtr->isParseOk = gap_xml_parse_value_gdouble(*value_cursor, &userDataPtr->pvals->point[count].w_resize);
+    }
+    else if (strcmp (*name_cursor, GAP_MOVPATH_XML_TOKEN_H_RESIZE) == 0)
+    {
+      userDataPtr->isParseOk = gap_xml_parse_value_gdouble(*value_cursor, &userDataPtr->pvals->point[count].h_resize);
+    }
+    else if (strcmp (*name_cursor, GAP_MOVPATH_XML_TOKEN_OPACITY) == 0)
+    {
+      userDataPtr->isParseOk = gap_xml_parse_value_gdouble(*value_cursor, &userDataPtr->pvals->point[count].opacity);
+    }
+    else if (strcmp (*name_cursor, GAP_MOVPATH_XML_TOKEN_ROTATION) == 0)
+    {
+      userDataPtr->isParseOk = gap_xml_parse_value_gdouble(*value_cursor, &userDataPtr->pvals->point[count].rotation);
+    }
+    else if (strcmp (*name_cursor, GAP_MOVPATH_XML_TOKEN_TTLX) == 0)
+    {
+      userDataPtr->isParseOk = gap_xml_parse_value_gdouble(*value_cursor, &userDataPtr->pvals->point[count].ttlx);
+    }
+    else if (strcmp (*name_cursor, GAP_MOVPATH_XML_TOKEN_TTLY) == 0)
+    {
+      userDataPtr->isParseOk = gap_xml_parse_value_gdouble(*value_cursor, &userDataPtr->pvals->point[count].ttly);
+    }
+    else if (strcmp (*name_cursor, GAP_MOVPATH_XML_TOKEN_TTRX) == 0)
+    {
+      userDataPtr->isParseOk = gap_xml_parse_value_gdouble(*value_cursor, &userDataPtr->pvals->point[count].ttrx);
+    }
+    else if (strcmp (*name_cursor, GAP_MOVPATH_XML_TOKEN_TTRY) == 0)
+    {
+      userDataPtr->isParseOk = gap_xml_parse_value_gdouble(*value_cursor, &userDataPtr->pvals->point[count].ttry);
+    }
+    else if (strcmp (*name_cursor, GAP_MOVPATH_XML_TOKEN_TBLX) == 0)
+    {
+      userDataPtr->isParseOk = gap_xml_parse_value_gdouble(*value_cursor, &userDataPtr->pvals->point[count].tblx);
+    }
+    else if (strcmp (*name_cursor, GAP_MOVPATH_XML_TOKEN_TBLY) == 0)
+    {
+      userDataPtr->isParseOk = gap_xml_parse_value_gdouble(*value_cursor, &userDataPtr->pvals->point[count].tbly);
+    }
+    else if (strcmp (*name_cursor, GAP_MOVPATH_XML_TOKEN_TBRX) == 0)
+    {
+      userDataPtr->isParseOk = gap_xml_parse_value_gdouble(*value_cursor, &userDataPtr->pvals->point[count].tbrx);
+    }
+    else if (strcmp (*name_cursor, GAP_MOVPATH_XML_TOKEN_TBRY) == 0)
+    {
+      userDataPtr->isParseOk = gap_xml_parse_value_gdouble(*value_cursor, &userDataPtr->pvals->point[count].tbry);
+    }
+    else if (strcmp (*name_cursor, GAP_MOVPATH_XML_TOKEN_ACC_POSITION) == 0)
+    {
+      userDataPtr->isParseOk = gap_xml_parse_value_gint(*value_cursor, &userDataPtr->pvals->point[count].accPosition);
+    }
+    else if (strcmp (*name_cursor, GAP_MOVPATH_XML_TOKEN_ACC_OPACITY) == 0)
+    {
+      userDataPtr->isParseOk = gap_xml_parse_value_gint(*value_cursor, &userDataPtr->pvals->point[count].accOpacity);
+    }
+    else if (strcmp (*name_cursor, GAP_MOVPATH_XML_TOKEN_ACC_SIZE) == 0)
+    {
+      userDataPtr->isParseOk = gap_xml_parse_value_gint(*value_cursor, &userDataPtr->pvals->point[count].accSize);
+    }
+    else if (strcmp (*name_cursor, GAP_MOVPATH_XML_TOKEN_ACC_ROTATION) == 0)
+    {
+      userDataPtr->isParseOk = gap_xml_parse_value_gint(*value_cursor, &userDataPtr->pvals->point[count].accRotation);
+    }
+    else if (strcmp (*name_cursor, GAP_MOVPATH_XML_TOKEN_ACC_PERSPECTIVE) == 0)
+    {
+      userDataPtr->isParseOk = gap_xml_parse_value_gint(*value_cursor, &userDataPtr->pvals->point[count].accPerspective);
+    }
+    else if (strcmp (*name_cursor, GAP_MOVPATH_XML_TOKEN_ACC_SEL_FEATHER_RADIUS) == 0)
+    {
+      userDataPtr->isParseOk = gap_xml_parse_value_gint(*value_cursor, &userDataPtr->pvals->point[count].accSelFeatherRadius);
+    }
+
+    name_cursor++;
+    value_cursor++;
+  }
+}   /* end  p_xml_parse_element_controlpoint */
+
+
+/* --------------------------------------
+ * p_start_xml_element
+ * --------------------------------------
+ * handler function to be called by the GMarkupParser
+ * this handler is called each time the parser recognizes
+ * the start event of an xml element.
+ */
+static void 
+p_start_xml_element (GMarkupParseContext *context,
+    const gchar         *element_name,
+    const gchar        **attribute_names,
+    const gchar        **attribute_values,
+    GapMovXmlUserData   *userDataPtr,
+    GError             **error) 
+{
+  gint jj;
+
+  if(gap_debug)
+  {
+    printf("p_start_xml_element: %s\n", element_name);
+  }
+
+  if(userDataPtr == NULL)
+  {
+    return;
+  }
+
+  for(jj=0; jmpTableElement[jj].name != NULL; jj++)
+  {
+    if(strcmp(jmpTableElement[jj].name, element_name) == 0)
+    {
+      if ((jj==0) && (jmpTableElement[jj].count == 0))
+      {
+        userDataPtr->isScopeValid = TRUE;
+      }
+      if(!userDataPtr->isScopeValid)
+      {
+        /* stop parsing when outsided of known namespace 
+         * (and stop on duplicate root element too) 
+         */
+        return;
+      }
+
+      /* call token specific parse function */
+      jmpTableElement[jj].func_ptr(element_name
+                                 , attribute_names
+                                 , attribute_values
+                                 , userDataPtr
+                                 , jmpTableElement[jj].count
+                                 );
+    }
+    if(!userDataPtr->isParseOk)
+    {
+      return;
+    }
+  }
+
+}  /* end p_start_xml_element */
+
+
+
+/* --------------------------------------
+ * p_end_xml_element
+ * --------------------------------------
+ * handler function to be called by the GMarkupParser
+ * this handler is called each time the parser recognizes
+ * the end event of an xml element
+ */
+static void 
+p_end_xml_element (GMarkupParseContext *context,
+    const gchar         *element_name,
+    GapMovXmlUserData   *userDataPtr,
+    GError             **error)
+{
+  if(gap_debug)
+  {
+    printf("p_end_xml_element: %s\n", element_name);
+  }
+  if(userDataPtr == NULL)
+  {
+    return;
+  }
+
+  if(userDataPtr->isScopeValid)
+  {
+    gint jj;
+    
+    for(jj=0; jmpTableElement[jj].name != NULL; jj++)
+    {
+      if(strcmp(jmpTableElement[jj].name, element_name) == 0)
+      {
+        jmpTableElement[jj].count++;
+      }
+    }
+    
+  }
+  
+}  /* end p_end_xml_element */
+
+
+
+/* ------------------------------------------
+ * p_transform_path_coordinate
+ * ------------------------------------------
+ * transforms path coordinate value, typically from recorded size
+ * to actual size.
+ * Note that the path coordinates in the XML file are stored in unit pixels,
+ * refering to the frame image (recordedFrameWidth / recordedFrameHeight) where the move path dialog
+ * was invoked from and that typically did write the XML file when
+ * move path settings were saved in XML format.
+ *
+ * This procedure is used to transform the path coordinates to actual frame
+ * size at loading time.
+ */
+static gint
+p_transform_path_coordinate(gint value, gint32 recordedSize, gint32 actualSize)
+{
+  
+  if((recordedSize != 0) && (actualSize != recordedSize))
+  {
+    gdouble newValue;
+    gint    newIntValue;
+    
+    newValue = ((gdouble)value * (gdouble)actualSize) / (gdouble)recordedSize;
+    newIntValue = rint(newValue);
+    
+    return (newIntValue);
+  }
+
+  return(value);
+  
+}
+
+
+/* ------------------------------------------
+ * p_copy_transformed_values
+ * ------------------------------------------
+ *
+ */
+static void
+p_copy_transformed_values(GapMovValues *dstValues, GapMovValues *srcValues
+   , gint32 actualFrameWidth, gint32 actualFrameHeight)
+{
+  gint ii;
+  
+  dstValues->version = srcValues->version;
+  dstValues->recordedFrameWidth = srcValues->recordedFrameWidth;
+  dstValues->recordedFrameHeight = srcValues->recordedFrameHeight;
+  dstValues->recordedObjWidth = srcValues->recordedObjWidth;
+  dstValues->recordedObjHeight = srcValues->recordedObjHeight;
+  dstValues->dst_range_start = srcValues->dst_range_start;
+  dstValues->dst_range_end = srcValues->dst_range_end;
+  dstValues->total_frames = srcValues->total_frames;
+  dstValues->tween_steps = srcValues->tween_steps;
+  dstValues->tween_opacity_initial = srcValues->tween_opacity_initial;
+  dstValues->tween_opacity_desc = srcValues->tween_opacity_desc;
+  dstValues->tracelayer_enable = srcValues->tracelayer_enable;
+  dstValues->trace_opacity_initial = srcValues->trace_opacity_initial;
+  dstValues->trace_opacity_desc = srcValues->trace_opacity_desc;
+  dstValues->src_stepmode = srcValues->src_stepmode;
+  dstValues->src_handle = srcValues->src_handle;
+  dstValues->src_selmode = srcValues->src_selmode;
+  dstValues->src_paintmode = srcValues->src_paintmode;
+  dstValues->dst_layerstack = srcValues->dst_layerstack;
+  dstValues->step_speed_factor = srcValues->step_speed_factor;
+  dstValues->src_force_visible = srcValues->src_force_visible;
+  dstValues->clip_to_img = srcValues->clip_to_img;
+  dstValues->src_apply_bluebox = srcValues->src_apply_bluebox;
+  dstValues->src_layerstack = srcValues->src_layerstack;
+
+  if(dstValues->src_filename != NULL)
+  {
+    g_free(dstValues->src_filename);
+    dstValues->src_filename = NULL;
+  }
+  if(srcValues->src_filename != NULL)
+  {
+    dstValues->src_filename = g_strdup(srcValues->src_filename);
+  }
+
+  if(dstValues->bbp != NULL)
+  {
+    g_free(dstValues->bbp);
+    dstValues->bbp = NULL;
+  }
+  if(srcValues->bbp != NULL)
+  {
+    dstValues->bbp = g_new(GapBlueboxGlobalParams, 1);
+    memcpy(dstValues->bbp, srcValues->bbp, sizeof(GapBlueboxGlobalParams));
+  }
+  
+  
+  dstValues->point_idx = srcValues->point_idx;
+  dstValues->point_idx_max = srcValues->point_idx_max;
+
+  /* copy controlpoint data for all points and transform coordinates */
+  for(ii=0; ii <= srcValues->point_idx_max; ii++)
+  {
+    memcpy(&dstValues->point[ii], &srcValues->point[ii], sizeof(GapMovPoint));
+    
+    dstValues->point[ii].p_x = p_transform_path_coordinate(srcValues->point[ii].p_x
+                                    , srcValues->recordedFrameWidth
+                                    , actualFrameWidth
+                                    );
+    dstValues->point[ii].p_y = p_transform_path_coordinate(srcValues->point[ii].p_y
+                                    , srcValues->recordedFrameHeight
+                                    , actualFrameHeight
+                                    );
+    
+  }
+
+}  /* end p_copy_transformed_values */
+
+
+/* ------------------------------------------
+ * gap_mov_xml_par_load
+ * ------------------------------------------
+ * (use  actualFrameWidth and actualFrameHeight value 0 in case no transformation
+ * is desired)
+ */
+gboolean 
+gap_mov_xml_par_load(const char *filename, GapMovValues *productiveValues
+    ,gint32 actualFrameWidth, gint32 actualFrameHeight)
+{
+  /* The list of what handler does what.
+   * this implementation uses only start and end element events
+   */
+  static GMarkupParser parserFuctions = {
+    p_start_xml_element,
+    p_end_xml_element,
+    NULL,
+    NULL,
+    NULL
+  };
+
+  gint jj;
+  char *textBuffer;
+  gsize lengthTextBuffer;
+  gboolean isOk;
+  GapMovValues   *tmpValues;
+  GapMovXmlUserData *userDataPtr;
+  
+  isOk = TRUE;
+  tmpValues = gap_mov_exec_new_GapMovValues();
+  tmpValues->dst_image_id = productiveValues->dst_image_id;
+  userDataPtr = g_new(GapMovXmlUserData, 1);
+  userDataPtr->pvals = tmpValues;
+
+  ///p_init_default_values(tmpValues);   /// (?) TODO 
+  
+  userDataPtr->isScopeValid = FALSE;
+  userDataPtr->isParseOk = TRUE;
+  
+  for(jj=0; jmpTableElement[jj].name != NULL; jj++)
+  {
+    jmpTableElement[jj].count = 0;
+  }
+  
+  GMarkupParseContext *context = g_markup_parse_context_new (
+        &parserFuctions  /* GMarkupParser */
+      , 0                /* GMarkupParseFlags flags */
+      , userDataPtr      /* gpointer user_data */
+      , NULL             /* GDestroyNotify user_data_dnotify */
+      );
+
+  if (g_file_get_contents (filename, &textBuffer, &lengthTextBuffer, NULL) != TRUE) 
+  {
+    printf("Couldn't load XML file:%s\n", filename);
+    return(FALSE);
+  }
+  
+
+  if (g_markup_parse_context_parse (context, textBuffer, lengthTextBuffer, NULL) != TRUE)
+  {
+    printf("Parse failed of file: %s\n", filename);
+    // g_markup_parse_context_parse returns FALSE even when parsing seems to be OK
+    // TODO: findout what makes g_markup_parse_context_parse work with proper returncode...
+    // return(FALSE);
+  }
+  
+  /* check for mandatory elements */
+  if(userDataPtr->isParseOk)
+  {
+    for(jj=0; jmpTableElement[jj].name != NULL; jj++)
+    {
+      if(jmpTableElement[jj].isRequired)
+      {
+        if(jmpTableElement[jj].count < 1)
+        {
+          printf("required XML element:%s was not found in file:%s\n"
+            , jmpTableElement[jj].name
+            , filename
+            );
+          userDataPtr->isParseOk = FALSE;
+        }
+      }
+    }
+    
+  }
+
+  if(userDataPtr->isParseOk)
+  {
+      /* copy loaded values and transform coordinates from recorded frame size
+       * to actual frame size 
+       */
+      p_copy_transformed_values(productiveValues, tmpValues, actualFrameWidth, actualFrameHeight);
+  }  
+  
+  isOk = userDataPtr->isParseOk;
+  
+  g_free(textBuffer);
+  g_markup_parse_context_free (context);
+
+  if(tmpValues->bbp != NULL)
+  {
+    g_free(tmpValues->bbp);
+  }
+  if(tmpValues->src_filename != NULL)
+  {
+    g_free(tmpValues->src_filename);
+  }
+  g_free(tmpValues);
+
+  g_free(userDataPtr);
+  
+  
+  return (isOk);
+
+}  /* end gap_mov_xml_par_load */
+
+
+
+
+
+/* 
+ * XML WRITER procedure
+ */
+
+
+/* --------------------------------------
+ * gap_mov_xml_par_save
+ * --------------------------------------
+ * save all move path parameter settings to file in XML notation.
+ */
+gint
+gap_mov_xml_par_save(char *filename, GapMovValues *pvals)
+{
+  FILE *l_fp;
+  gint l_idx;
+
+  if(filename == NULL) return -1;
+
+  l_fp = g_fopen(filename, "w+");
+  if(l_fp != NULL)
+  {
+    gboolean isTraceLayerEnabled;
+    gboolean writeResizeValues;
+    gboolean writeRotateValues;
+    gboolean writeOpacityValues;
+    gboolean writeFeatherRadiusValues;
+    
+    fprintf(l_fp, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
+    
+    /* root */
+    fprintf(l_fp, "<%s ", GAP_MOVPATH_XML_TOKEN_ROOT);
+    gap_xml_write_int_value(l_fp, GAP_MOVPATH_XML_TOKEN_VERSION, pvals->version);
+    fprintf(l_fp, "/>\n");
+    
+    /* attributes for description of the processed frames */
+    {
+      gint32 total_frames;
+      
+      total_frames = 1 + abs(pvals->dst_range_end - pvals->dst_range_start);
+    
+      fprintf(l_fp, "  <%s ", GAP_MOVPATH_XML_TOKEN_FRAME_DESCRIPTION);
+      gap_xml_write_int_value(l_fp, GAP_MOVPATH_XML_TOKEN_WIDTH, gimp_image_width(pvals->dst_image_id));
+      gap_xml_write_int_value(l_fp, GAP_MOVPATH_XML_TOKEN_HEIGHT, gimp_image_height(pvals->dst_image_id));
+      gap_xml_write_int_value(l_fp, GAP_MOVPATH_XML_TOKEN_RANGE_FROM, pvals->dst_range_start);
+      gap_xml_write_int_value(l_fp, GAP_MOVPATH_XML_TOKEN_RANGE_TO, pvals->dst_range_end);
+      gap_xml_write_int_value(l_fp, GAP_MOVPATH_XML_TOKEN_TOTAL_FRAMES, total_frames);
+      fprintf(l_fp, "/>\n");
+    }
+    
+           
+    /* attributes for tween processing */
+    fprintf(l_fp, "  <%s ", GAP_MOVPATH_XML_TOKEN_TWEEN);
+    gap_xml_write_int_value(l_fp, GAP_MOVPATH_XML_TOKEN_TWEEN_STEPS, pvals->tween_steps);
+    if(pvals->tween_steps != 0)
+    {
+      gap_xml_write_gdouble_value(l_fp, GAP_MOVPATH_XML_TOKEN_TWEEN_OPACITY_INITIAL, pvals->tween_opacity_initial, 1, 3);
+      gap_xml_write_gdouble_value(l_fp, GAP_MOVPATH_XML_TOKEN_TWEEN_OPACITY_DESC, pvals->tween_opacity_desc, 1, 3);
+    }
+    fprintf(l_fp, "/>\n");
+    
+    /* attributes for trace layer generation */
+    isTraceLayerEnabled = (pvals->tracelayer_enable != 0);
+    fprintf(l_fp, "  <%s ", GAP_MOVPATH_XML_TOKEN_TRACE);
+    gap_xml_write_gint_as_gboolean_value(l_fp, GAP_MOVPATH_XML_TOKEN_TRACELAYER_ENABLE, isTraceLayerEnabled);
+    if(isTraceLayerEnabled)
+    {
+      gap_xml_write_gdouble_value(l_fp, GAP_MOVPATH_XML_TOKEN_TRACE_OPACITY_INITIAL, pvals->trace_opacity_initial, 1, 3);
+      gap_xml_write_gdouble_value(l_fp, GAP_MOVPATH_XML_TOKEN_TRACE_OPACITY_DESC, pvals->trace_opacity_desc, 1, 3);
+    }
+    fprintf(l_fp, "/>\n");
+    
+    /* attributes of the moving_object */
+    {
+      char   *src_filename;
+      gint32  src_image_id;
+ 
+      src_image_id = gimp_drawable_get_image(pvals->src_layer_id);
+
+      fprintf(l_fp, "  <%s ", GAP_MOVPATH_XML_TOKEN_MOVING_OBJECT);
+      gap_xml_write_int_value(l_fp, GAP_MOVPATH_XML_TOKEN_SRC_LAYER_ID, pvals->src_layer_id);
+      gap_xml_write_int_value(l_fp, GAP_MOVPATH_XML_TOKEN_SRC_LAYERSTACK, pvals->src_layerstack);
+      gap_xml_write_int_value(l_fp, GAP_MOVPATH_XML_TOKEN_WIDTH, gimp_image_width(src_image_id));
+      gap_xml_write_int_value(l_fp, GAP_MOVPATH_XML_TOKEN_HEIGHT, gimp_image_height(src_image_id));
+
+      src_filename = gimp_image_get_filename(src_image_id);
+      if(src_filename != NULL)
+      {
+        gap_xml_write_string_value(l_fp, GAP_MOVPATH_XML_TOKEN_SRC_FILENAME, src_filename);
+        g_free(src_filename);
+      }
+      fprintf(l_fp, "\n    ");
+      gap_xml_write_EnumValue(l_fp, GAP_MOVPATH_XML_TOKEN_SRC_HANDLE, pvals->src_handle, &valuesGapMovHandle[0]);
+      fprintf(l_fp, "\n    ");
+      gap_xml_write_EnumValue(l_fp, GAP_MOVPATH_XML_TOKEN_SRC_STEPMODE, pvals->src_stepmode, &valuesGapMovStepMode[0]);
+      gap_xml_write_gdouble_value(l_fp, GAP_MOVPATH_XML_TOKEN_STEP_SPEED_FACTOR, pvals->step_speed_factor, 1, 5);
+      fprintf(l_fp, "\n    ");
+      gap_xml_write_EnumValue(l_fp, GAP_MOVPATH_XML_TOKEN_SRC_SELMODE, pvals->src_selmode, &valuesGapMovSelMode[0]);
+      fprintf(l_fp, "\n    ");
+      gap_xml_write_EnumValue(l_fp, GAP_MOVPATH_XML_TOKEN_SRC_PAINTMODE, pvals->src_paintmode, &valuesGimpPaintmode[0]);
+      fprintf(l_fp, "\n    ");
+      gap_xml_write_int_value(l_fp, GAP_MOVPATH_XML_TOKEN_DST_LAYERSTACK, pvals->dst_layerstack);
+      gap_xml_write_gint_as_gboolean_value(l_fp, GAP_MOVPATH_XML_TOKEN_SRC_FORCE_VISIBLE, pvals->src_force_visible);
+      gap_xml_write_gint_as_gboolean_value(l_fp, GAP_MOVPATH_XML_TOKEN_CLIP_TO_IMG, pvals->clip_to_img);
+      gap_xml_write_gint_as_gboolean_value(l_fp, GAP_MOVPATH_XML_TOKEN_SRC_APPLY_BLUEBOX, pvals->src_apply_bluebox);
+      fprintf(l_fp, "\n");
+      
+      /* attributes for applying bluebox transparency processing */
+      if((pvals->src_apply_bluebox) && (pvals->bbp != NULL))
+      {
+        fprintf(l_fp, "    <%s ", GAP_MOVPATH_XML_TOKEN_BLUEBOX_PARAMETERS);
+        fprintf(l_fp, "\n      ");
+        gap_xml_write_EnumValue(l_fp, GAP_MOVPATH_XML_TOKEN_THRES_MODE, pvals->bbp->vals.thres_mode, &valuesGapBlueboxThresMode[0]);
+        fprintf(l_fp, "\n      ");
+        gap_xml_write_gdouble_value(l_fp, GAP_MOVPATH_XML_TOKEN_THRES_R, pvals->bbp->vals.thres_r, 1, 5);
+        gap_xml_write_gdouble_value(l_fp, GAP_MOVPATH_XML_TOKEN_THRES_G, pvals->bbp->vals.thres_g, 1, 5);
+        gap_xml_write_gdouble_value(l_fp, GAP_MOVPATH_XML_TOKEN_THRES_B, pvals->bbp->vals.thres_b, 1, 5);
+        fprintf(l_fp, "\n      ");
+        gap_xml_write_gdouble_value(l_fp, GAP_MOVPATH_XML_TOKEN_THRES_H, pvals->bbp->vals.thres_h, 1, 5);
+        gap_xml_write_gdouble_value(l_fp, GAP_MOVPATH_XML_TOKEN_THRES_S, pvals->bbp->vals.thres_s, 1, 5);
+        gap_xml_write_gdouble_value(l_fp, GAP_MOVPATH_XML_TOKEN_THRES_V, pvals->bbp->vals.thres_v, 1, 5);
+        fprintf(l_fp, "\n      ");
+        gap_xml_write_gdouble_value(l_fp, GAP_MOVPATH_XML_TOKEN_THRES, pvals->bbp->vals.thres, 1, 5);
+        gap_xml_write_gdouble_value(l_fp, GAP_MOVPATH_XML_TOKEN_TOLERANCE, pvals->bbp->vals.tolerance, 1, 5);
+        gap_xml_write_gdouble_value(l_fp, GAP_MOVPATH_XML_TOKEN_GROW, pvals->bbp->vals.grow, 1, 5);
+        fprintf(l_fp, "\n      ");
+        gap_xml_write_gint_as_gboolean_value(l_fp, GAP_MOVPATH_XML_TOKEN_FEATHER_EDGES, pvals->bbp->vals.feather_edges);
+        gap_xml_write_gdouble_value(l_fp, GAP_MOVPATH_XML_TOKEN_FEATHER_RADIUS, pvals->bbp->vals.feather_radius, 1, 3);
+        fprintf(l_fp, "\n      ");
+        gap_xml_write_gdouble_value(l_fp, GAP_MOVPATH_XML_TOKEN_SOURCE_ALPHA, pvals->bbp->vals.source_alpha, 1, 3);
+        gap_xml_write_gdouble_value(l_fp, GAP_MOVPATH_XML_TOKEN_TARGET_ALPHA, pvals->bbp->vals.target_alpha, 1, 3);
+        fprintf(l_fp, "\n      ");
+        gap_xml_write_gdouble_value(l_fp, GAP_MOVPATH_XML_TOKEN_KEYCOLOR_R, pvals->bbp->vals.keycolor.r, 1, 3);
+        gap_xml_write_gdouble_value(l_fp, GAP_MOVPATH_XML_TOKEN_KEYCOLOR_G, pvals->bbp->vals.keycolor.g, 1, 3);
+        gap_xml_write_gdouble_value(l_fp, GAP_MOVPATH_XML_TOKEN_KEYCOLOR_B, pvals->bbp->vals.keycolor.b, 1, 3);
+  
+        fprintf(l_fp, "\n");
+        fprintf(l_fp, "      >\n");
+        fprintf(l_fp, "    </%s>\n", GAP_MOVPATH_XML_TOKEN_BLUEBOX_PARAMETERS);
+      
+      }
+      
+      fprintf(l_fp, "    >\n");
+      fprintf(l_fp, "  </%s>\n", GAP_MOVPATH_XML_TOKEN_MOVING_OBJECT);
+    }
+
+    fprintf(l_fp, "\n");
+
+    /* controlpoints */
+
+    fprintf(l_fp, "  <%s ", GAP_MOVPATH_XML_TOKEN_CONTROLPOINTS);
+    gap_xml_write_int_value(l_fp, GAP_MOVPATH_XML_TOKEN_CURRENT_POINT, pvals->point_idx);
+    gap_xml_write_int_value(l_fp, GAP_MOVPATH_XML_TOKEN_NUMBER_OF_POINTS, pvals->point_idx_max +1);
+    fprintf(l_fp, " >\n");
+
+    /* check for conditonal write 
+     * (to keep files smaller and more readable we skip writing of some information
+     * that is equal to the default value in all controlpoints)
+     */
+    writeResizeValues = FALSE;
+    writeRotateValues = FALSE;
+    writeOpacityValues = FALSE;
+    writeFeatherRadiusValues = FALSE;
+    for(l_idx = 0; l_idx <= pvals->point_idx_max; l_idx++)
+    {
+      if((pvals->point[l_idx].w_resize != 100.0)
+      || (pvals->point[l_idx].h_resize != 100.0))
+      {
+        writeResizeValues = TRUE;
+      }
+      
+      if(pvals->point[l_idx].opacity != 100.0)
+      {
+        writeOpacityValues = TRUE;
+      }
+      
+      if(pvals->point[l_idx].rotation != 0.0)
+      {
+        writeRotateValues = TRUE;
+      }
+      
+      if(pvals->point[l_idx].sel_feather_radius != 0.0)
+      {
+        writeFeatherRadiusValues = TRUE;
+      }
+      
+    }
+
+    /* write controlpoints loop */
+    for(l_idx = 0; l_idx <= pvals->point_idx_max; l_idx++)
+    {
+      gdouble  px;
+      gdouble  py;
+      gboolean writeAccelerationCharacteristics;
+      
+      px = pvals->point[l_idx].p_x;
+      py = pvals->point[l_idx].p_y;
+      
+      /* write basic attributes of the controlpoint */
+      
+      fprintf(l_fp, "    <%s ", GAP_MOVPATH_XML_TOKEN_CONTROLPOINT);
+      gap_xml_write_gdouble_value(l_fp, GAP_MOVPATH_XML_TOKEN_PX, px, 5, 0);
+      gap_xml_write_gdouble_value(l_fp, GAP_MOVPATH_XML_TOKEN_PY, py, 5, 0);
+      if(writeResizeValues)
+      {
+        gap_xml_write_gdouble_value(l_fp, GAP_MOVPATH_XML_TOKEN_W_RESIZE, pvals->point[l_idx].w_resize, 3, 3);
+        gap_xml_write_gdouble_value(l_fp, GAP_MOVPATH_XML_TOKEN_H_RESIZE, pvals->point[l_idx].h_resize, 3, 3);
+      }
+      if(writeOpacityValues)
+      {
+        gap_xml_write_gdouble_value(l_fp, GAP_MOVPATH_XML_TOKEN_OPACITY, pvals->point[l_idx].opacity, 3, 1);
+      }
+      if(writeRotateValues)
+      {
+        gap_xml_write_gdouble_value(l_fp, GAP_MOVPATH_XML_TOKEN_ROTATION, pvals->point[l_idx].rotation, 4, 1);
+      }
+      if(writeFeatherRadiusValues)
+      {
+        gap_xml_write_gdouble_value(l_fp, GAP_MOVPATH_XML_TOKEN_SEL_FEATHER_RADIUS, pvals->point[l_idx].sel_feather_radius, 3, 1);
+      }
+      
+      /* conditional write perspective transformation (only if there is any) */
+      if(pvals->point[l_idx].ttlx != 1.0
+            || pvals->point[l_idx].ttly != 1.0
+            || pvals->point[l_idx].ttrx != 1.0
+            || pvals->point[l_idx].ttry != 1.0
+            || pvals->point[l_idx].tblx != 1.0
+            || pvals->point[l_idx].tbly != 1.0
+            || pvals->point[l_idx].tbrx != 1.0
+            || pvals->point[l_idx].tbry != 1.0
+            )
+      {
+        fprintf(l_fp, "\n      ");
+        gap_xml_write_gdouble_value(l_fp, GAP_MOVPATH_XML_TOKEN_TTLX, pvals->point[l_idx].ttlx, 2, 3);
+        gap_xml_write_gdouble_value(l_fp, GAP_MOVPATH_XML_TOKEN_TTLY, pvals->point[l_idx].ttly, 2, 3);
+        gap_xml_write_gdouble_value(l_fp, GAP_MOVPATH_XML_TOKEN_TTRX, pvals->point[l_idx].ttrx, 2, 3);
+        gap_xml_write_gdouble_value(l_fp, GAP_MOVPATH_XML_TOKEN_TTRY, pvals->point[l_idx].ttry, 2, 3);
+        gap_xml_write_gdouble_value(l_fp, GAP_MOVPATH_XML_TOKEN_TBLX, pvals->point[l_idx].tblx, 2, 3);
+        gap_xml_write_gdouble_value(l_fp, GAP_MOVPATH_XML_TOKEN_TBLY, pvals->point[l_idx].tbly, 2, 3);
+        gap_xml_write_gdouble_value(l_fp, GAP_MOVPATH_XML_TOKEN_TBRX, pvals->point[l_idx].tbrx, 2, 3);
+        gap_xml_write_gdouble_value(l_fp, GAP_MOVPATH_XML_TOKEN_TBRY, pvals->point[l_idx].tbry, 2, 3);
+      }
+
+      writeAccelerationCharacteristics = FALSE;
+      /* conditional write acceleration characteristics
+       * relevant information can occur at first controlpoint
+       * or on keyframes but never for the last controlpoint)
+       */
+      if ((pvals->point[l_idx].accPosition != 0)
+      ||  (pvals->point[l_idx].accOpacity != 0)
+      ||  (pvals->point[l_idx].accSize != 0)
+      ||  (pvals->point[l_idx].accRotation != 0)
+      ||  (pvals->point[l_idx].accPerspective != 0)
+      ||  (pvals->point[l_idx].accSelFeatherRadius != 0))
+      {
+        if (l_idx == 0) 
+        {
+          writeAccelerationCharacteristics = TRUE;
+        }
+        else
+        {
+          if ((l_idx < pvals->point_idx_max)
+          && ((int)pvals->point[l_idx].keyframe > 0))
+          {
+            writeAccelerationCharacteristics = TRUE;
+          }
+        }
+        
+      }
+      
+      if (writeAccelerationCharacteristics == TRUE)
+      {
+        fprintf(l_fp, "\n      ");
+        gap_xml_write_int_value(l_fp, GAP_MOVPATH_XML_TOKEN_ACC_POSITION, pvals->point[l_idx].accPosition);
+        gap_xml_write_int_value(l_fp, GAP_MOVPATH_XML_TOKEN_ACC_OPACITY, pvals->point[l_idx].accOpacity);
+        gap_xml_write_int_value(l_fp, GAP_MOVPATH_XML_TOKEN_ACC_SIZE, pvals->point[l_idx].accSize);
+        gap_xml_write_int_value(l_fp, GAP_MOVPATH_XML_TOKEN_ACC_ROTATION, pvals->point[l_idx].accRotation);
+        gap_xml_write_int_value(l_fp, GAP_MOVPATH_XML_TOKEN_ACC_PERSPECTIVE, pvals->point[l_idx].accPerspective);
+        gap_xml_write_int_value(l_fp, GAP_MOVPATH_XML_TOKEN_ACC_SEL_FEATHER_RADIUS, pvals->point[l_idx].accSelFeatherRadius);
+      }
+      
+      /* check for writing keyframe
+       * (the implicite keyframes at first and last controlpoints are not written to file)
+       */
+      if((l_idx > 0)
+      && (l_idx < pvals->point_idx_max)
+      && ((int)pvals->point[l_idx].keyframe > 0))
+      {
+        int keyframe;
+        
+        fprintf(l_fp, "\n      ");
+        keyframe = gap_mov_exec_conv_keyframe_to_rel(pvals->point[l_idx].keyframe_abs, pvals);
+        gap_xml_write_int_value(l_fp, GAP_MOVPATH_XML_TOKEN_KEYFRAME, keyframe);
+      
+      }
+
+      fprintf(l_fp, "/>\n"); /* end of GAP_MOVPATH_XML_TOKEN_CONTROLPOINT */
+      
+    }    
+    
+    fprintf(l_fp, "  </%s>\n", GAP_MOVPATH_XML_TOKEN_CONTROLPOINTS);
+    fprintf(l_fp, "</%s>", GAP_MOVPATH_XML_TOKEN_ROOT);
+    
+    fclose(l_fp);
+    return 0;
+  }
+  return -1;
+}  /* end gap_mov_xml_par_save */
diff --git a/gap/gap_mov_xml_par.h b/gap/gap_mov_xml_par.h
new file mode 100644
index 0000000..b2d0a0d
--- /dev/null
+++ b/gap/gap_mov_xml_par.h
@@ -0,0 +1,52 @@
+/* gap_mov_xml_par.h
+ * 2011.03.09 hof (Wolfgang Hofer)
+ *
+ * GAP ... Gimp Animation Plugins
+ *
+ * Move Path : XML parameterfile load and save procedures.
+ * The XML parameterfile contains full information of all parameters
+ * available in the move path feature including:
+ *  version
+ *  frame description, moving object description
+ *  tweens, trace layer, bluebox settings and controlpoints.
+ *
+ * Note that the old pointfile format is still supported
+ * but not handled here.
+ * (the old pointfile format is limited to information about the controlpoints that build the path
+ * see gap_mov_exec module for load/save support of the old pointfile format)
+ *
+ */
+/* 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:
+ * 2011.03.09  hof: created.
+ */
+#ifndef _GAP_MOV_XML_PAR_H
+#define _GAP_MOV_XML_PAR_H
+
+#include "libgimp/gimp.h"
+#include "gap_mov_dialog.h"
+
+
+gboolean  gap_mov_xml_par_load(const char *filename, GapMovValues *productiveValues
+                              ,gint32 actualFrameWidth, gint32 actualFrameHeight);
+gint      gap_mov_xml_par_save(char *filename, GapMovValues *pvals);
+
+#endif
+
diff --git a/gap/gap_story_att_trans_dlg.c b/gap/gap_story_att_trans_dlg.c
index e75f3d2..fdb9990 100644
--- a/gap/gap_story_att_trans_dlg.c
+++ b/gap/gap_story_att_trans_dlg.c
@@ -43,6 +43,7 @@
 #include "gap_story_undo.h"
 #include "gap_story_dialog.h"
 #include "gap_story_file.h"
+#include "gap_story_render_processor.h"
 #include "gap_story_att_trans_dlg.h"
 #include "gap_pview_da.h"
 #include "gap_stock.h"
@@ -145,11 +146,27 @@ static gint32   p_create_deco_layer(GapStbAttrWidget *attw, gint32 image_id);
 static void     p_create_gfx_image(GapStbAttrWidget *attw, gint img_idx);
 static void     p_delete_gfx_images(GapStbAttrWidget *attw);
 static void     p_adjust_stackposition(gint32 image_id, gint32 layer_id, gint position);
+
+
+static gboolean p_create_transformed_layer_movepath(gint32 image_id
+                                                  , gint32 origsize_layer_id
+                                                  , gint32 *layer_id_ptr
+                                                  , GapStoryCalcAttr  *calculated
+                                                  , gint32 stackposition, const char *layername
+                                                  , GapStbAttrWidget *attw
+                                                  , gint img_idx
+                                                  );
+
 static void     p_create_transformed_layer(gint32 image_id
                     , gint32 origsize_layer_id
                     , gint32 *layer_id_ptr
                     , GapStoryCalcAttr  *calculated
-                    , gint32 stackposition, const char *layername);
+                    , gint32 stackposition
+                    , const char *layername
+                    , GapStbAttrWidget *attw
+		    , gint img_idx
+                    , gboolean enableMovepath
+                    );
 static gboolean p_calculate_prefetch_visibility(GapStbAttrWidget *attw, gint img_idx);
 
 static void     p_render_gfx(GapStbAttrWidget *attw, gint img_idx);
@@ -196,8 +213,18 @@ static gint32   p_fetch_video_frame_as_layer(GapStbMainGlobalParams *sgpp
 static gint32   p_fetch_imagefile_as_layer (const char *img_filename
                    , gint32 image_id
                    );
+static gint32   p_fetch_layer_from_animimage (const char *img_filename
+                   , gint32 localframe_index, gint32 image_id
+                   );
 
-
+static void     p_attw_movepath_filesel_pw_ok_cb (GtkWidget *widget
+                   ,GapStbAttrWidget *attw);
+static void     p_attw_movepath_filesel_pw_close_cb ( GtkWidget *widget
+                      , GapStbAttrWidget *attw);
+static void     p_attw_movepath_filesel_button_cb ( GtkWidget *w
+                       , GapStbAttrWidget *attw);
+static void     p_attw_movepath_file_validity_check(GapStbAttrWidget *attw);
+static void     p_attw_movepath_file_entry_update_cb(GtkWidget *widget, GapStbAttrWidget *attw);
 static void     p_attw_comment_entry_update_cb(GtkWidget *widget, GapStbAttrWidget *attw);
 
 static void     p_init_layer_info(GapStbAttrLayerInfo *linfo);
@@ -237,13 +264,30 @@ static void     p_create_and_attach_att_arr_widgets(const char *row_title
 static gdouble
 p_getConvertFactor(gint att_type_idx)
 {
-  if (att_type_idx == GAP_STB_ATT_TYPE_ROTATE)
+  if ((att_type_idx == GAP_STB_ATT_TYPE_ROTATE)
+  ||  (att_type_idx == GAP_STB_ATT_TYPE_MOVEPATH))
   {
     return 1.0;
   }
   return (CONVERT_TO_100PERCENT);
 }
 
+/* ----------------------------------------------------
+ * p_debug_dup_image
+ * ----------------------------------------------------
+ * Duplicate image, and open a display for the duplicate
+ * (Procedure is used for debug only
+ */
+static void
+p_debug_dup_image(gint32 image_id)
+{
+  gint32 l_dup_image_id;
+
+  l_dup_image_id = gimp_image_duplicate(image_id);
+  gimp_display_new(l_dup_image_id);
+}  /* end p_debug_dup_image */
+
+
 /* ---------------------------------
  * p_attw_prop_response
  * ---------------------------------
@@ -275,6 +319,12 @@ p_attw_prop_response(GtkWidget *widget
       dlg = attw->attw_prop_dialog;
       if(dlg)
       {
+        if(attw->movepath_filesel != NULL)
+        {
+          /* force close in case file selection dialog is still open */
+          p_attw_movepath_filesel_pw_close_cb(attw->movepath_filesel, attw);
+        }
+      
         p_delete_gfx_images(attw);
         if(attw->go_timertag >= 0)
         {
@@ -290,12 +340,13 @@ p_attw_prop_response(GtkWidget *widget
   }
 }  /* end p_attw_prop_response */
 
+
 /* --------------------------------------
  * p_attw_push_undo_and_set_unsaved_changes
  * --------------------------------------
  * this procedure is called in most cases when
  * a transition attribute property has changed.
- * (note that the Undo push implementation filter out
+ * (note that the Undo push implementation filters out
  * multiple attribute property changes on the same object in sequence)
  */
 static void
@@ -339,6 +390,16 @@ p_attw_prop_reset_all(GapStbAttrWidget *attw)
 
       gap_story_elem_copy(attw->stb_elem_refptr, attw->stb_elem_bck);
 
+      if(attw->stb_elem_refptr->att_movepath_file_xml)
+      {
+        gtk_entry_set_text(GTK_ENTRY(attw->movepath_file_entry), attw->stb_elem_refptr->att_movepath_file_xml);
+      }
+      else
+      {
+        gtk_entry_set_text(GTK_ENTRY(attw->movepath_file_entry), "");
+      }
+
+
       comment_set = FALSE;
       if(attw->stb_elem_refptr->comment)
       {
@@ -483,8 +544,10 @@ p_attw_update_properties(GapStbAttrWidget *attw)
 /* ------------------------------------
  * p_attw_update_sensitivity
  * ------------------------------------
- * set sensitivity of all 5 from/to, dur attribute widgets
+ * set sensitivity of all from/to, dur attribute widgets
  * according to their enable flag.
+ * the row with widget for the movepath settings depends
+ * on the movepath_file_xml_is_valid flag
  */
 static void
 p_attw_update_sensitivity(GapStbAttrWidget *attw)
@@ -504,6 +567,14 @@ p_attw_update_sensitivity(GapStbAttrWidget *attw)
   for(ii=0; ii < GAP_STB_ATT_TYPES_ARRAY_MAX; ii++)
   {
     sensitive = attw->stb_elem_refptr->att_arr_enable[ii];
+    if (ii == GAP_STB_ATT_TYPE_MOVEPATH)
+    {
+      gtk_widget_set_sensitive(attw->att_rows[ii].enable_toggle, attw->movepath_file_xml_is_valid);
+      if(attw->movepath_file_xml_is_valid != TRUE)
+      {
+        sensitive = FALSE;
+      }
+    }
 
     gtk_widget_set_sensitive(attw->att_rows[ii].spinbutton_from, sensitive);
     gtk_widget_set_sensitive(attw->att_rows[ii].spinbutton_to, sensitive);
@@ -752,7 +823,7 @@ p_attw_gdouble_adjustment_callback(GtkObject *obj, gdouble *val)
   gdouble l_val;
   gint    att_type_idx;
 
-  att_type_idx = g_object_get_data( G_OBJECT(obj), "att_type_idx" );
+  att_type_idx = (gint) g_object_get_data( G_OBJECT(obj), "att_type_idx" );
   attw = g_object_get_data( G_OBJECT(obj), OBJ_DATA_KEY_ATTW );
   if(attw)
   {
@@ -968,10 +1039,7 @@ p_attw_preview_events_cb (GtkWidget *widget
       /* debug code to display a copy of the image */
       if(1==0)
       {
-          gint32 dup_id;
-
-          dup_id = gimp_image_duplicate(attw->gfx_tab[img_idx].image_id);
-          gimp_display_new(dup_id);
+        p_debug_dup_image(attw->gfx_tab[img_idx].image_id);
       }
 
       return FALSE;
@@ -1413,25 +1481,248 @@ p_adjust_stackposition(gint32 image_id, gint32 layer_id, gint position)
 
 
 /* -----------------------------------------
+ * p_is_width_or_height_fixed
+ * -----------------------------------------
+ * return TRUE in case either width or height is fixed
+ * (e.g. is disabled to be scaled to fit target size)
+ */
+static gboolean
+p_is_width_or_height_fixed(gboolean fit_width, gboolean fit_height, gboolean keep_proportions)
+{
+  if ((fit_width == FALSE) && (fit_height == FALSE))
+  {
+    return (TRUE);
+  }
+  
+  if ((fit_width != fit_height) && (keep_proportions == FALSE))
+  {
+    return (TRUE);
+  }
+  
+  return (FALSE);
+  
+}  /* end p_is_width_or_height_fixed */
+
+
+
+/* -----------------------------------------
+ * p_create_transformed_layer_movepath
+ * -----------------------------------------
+ * create transformed copy of the specified origsize_layer_id,
+ * according to movepath rendering and current transformation settings.
+ */
+static gboolean
+p_create_transformed_layer_movepath(gint32 image_id
+  , gint32 origsize_layer_id
+  , gint32 *layer_id_ptr
+  , GapStoryCalcAttr  *calculated
+  , gint32 stackposition, const char *layername
+  , GapStbAttrWidget *attw
+  , gint img_idx
+  )
+{
+  char    *movepath_file_xml;
+  gboolean keep_proportions;
+  gboolean fit_width;
+  gboolean fit_height;
+  gint     master_width;
+  gint     master_height;
+  gint32   new_layer_id;
+  gint32   mov_obj_image_id;
+  gint32   mov_obj_layer_id;
+  gdouble  att_tab[GAP_STB_ATT_GFX_ARRAY_MAX][GAP_STB_ATT_TYPES_ARRAY_MAX];
+  gint     ii;
+  
+  if(attw == NULL)
+  {
+    printf("** INTERNAL ERROR p_create_transformed_layer_movepath called with attw is NULL\n");
+    return (FALSE);
+  }
+  if(attw->stb_refptr == NULL)
+  {
+    printf("** INTERNAL ERROR p_create_transformed_layer_movepath called with stb_refptr is NULL\n");
+    return (FALSE);
+  }
+  
+  if(attw->movepath_file_xml_is_valid != TRUE)
+  {
+    return (FALSE);
+  }
+  
+  master_width = attw->stb_refptr->master_width;
+  master_height = attw->stb_refptr->master_height;
+  keep_proportions = attw->stb_elem_refptr->att_keep_proportions;
+  fit_width = attw->stb_elem_refptr->att_fit_width;
+  fit_height = attw->stb_elem_refptr->att_fit_height;
+  movepath_file_xml = attw->stb_elem_refptr->att_movepath_file_xml;
+  for(ii=0; ii < GAP_STB_ATT_TYPES_ARRAY_MAX; ii++)
+  {
+    if(attw->stb_elem_refptr->att_arr_enable[ii] == TRUE)
+    {
+      att_tab[0][ii] =  attw->stb_elem_refptr->att_arr_value_from[ii];
+      att_tab[1][ii] =  attw->stb_elem_refptr->att_arr_value_to[ii];
+    }
+    else
+    {
+      att_tab[0][ii] =  gap_story_get_default_attribute(ii);
+      att_tab[1][ii] =  gap_story_get_default_attribute(ii);
+    }
+  }
+
+  if(movepath_file_xml == NULL)
+  {
+    return (FALSE);
+  }
+  if(origsize_layer_id < 0)
+  {
+    return (FALSE);
+  }
+
+  
+  /* recreate the current layer at stackposition 2 */
+
+  /* create image with 1:1 copy of the origsize_layer_id */
+  mov_obj_image_id = gimp_image_new( gimp_drawable_width(origsize_layer_id)
+                       , gimp_drawable_height(origsize_layer_id)
+                       , GIMP_RGB
+                       );
+  gimp_image_undo_disable (mov_obj_image_id);
+  mov_obj_layer_id = gimp_layer_new_from_drawable(origsize_layer_id, mov_obj_image_id);
+
+
+  if(mov_obj_layer_id >= 0)
+  {
+    gimp_drawable_set_visible(mov_obj_layer_id, TRUE);
+    if(! gimp_drawable_has_alpha(mov_obj_layer_id))
+    {
+       /* have to add alpha channel */
+       gimp_layer_add_alpha(mov_obj_layer_id);
+    }
+    gimp_image_add_layer(mov_obj_image_id, mov_obj_layer_id, 0);
+    gimp_layer_set_offsets(mov_obj_layer_id, 0, 0);
+  }
+ 
+  //if(img_idx == 0)
+  //{
+  //  p_debug_dup_image(mov_obj_image_id);
+  //}
+
+  /* prescale to preview size in case fixed witdh or height
+   * (we can skip the prescale in other modes, because the non fixed modes
+   * allow scaling to render size in the movepath render processing)
+   */
+  if (p_is_width_or_height_fixed(fit_width, fit_height, keep_proportions) == TRUE)
+  {
+    gint scaled_width;
+    gint scaled_height;
+    
+    scaled_width = gimp_image_width(mov_obj_image_id) * gimp_image_width(image_id) / master_width;
+    scaled_height = gimp_image_height(mov_obj_image_id) * gimp_image_height(image_id) / master_height;
+    gimp_image_scale(mov_obj_image_id, scaled_width, scaled_height);
+  }
+
+  /* call storyboard processor that does movepath and other transformations
+   * (note that processing is done at size of the preview image that is 2 times larger
+   * than the wanted result because 
+   */
+  new_layer_id = gap_story_render_transform_with_movepath_processing(image_id
+                              , mov_obj_image_id
+                              , mov_obj_layer_id
+                              , keep_proportions
+                              , fit_width
+                              , fit_height
+                              , att_tab [img_idx][GAP_STB_ATT_TYPE_ROTATE]   /* rotation in degree */
+                              , att_tab [img_idx][GAP_STB_ATT_TYPE_OPACITY]   /* 0.0 upto 1.0 */
+                              , att_tab [img_idx][GAP_STB_ATT_TYPE_ZOOM_X]    /* 0.0 upto 10.0 where 1.0 = 1:1 */
+                              , att_tab [img_idx][GAP_STB_ATT_TYPE_ZOOM_Y]    /* 0.0 upto 10.0 where 1.0 = 1:1 */
+                              , att_tab [img_idx][GAP_STB_ATT_TYPE_MOVE_X]    /* -1.0 upto +1.0 where 0 = no move, -1 is left outside */
+                              , att_tab [img_idx][GAP_STB_ATT_TYPE_MOVE_Y]     /* -1.0 upto +1.0 where 0 = no move, -1 is top outside */
+                              , movepath_file_xml
+                              , att_tab [img_idx][GAP_STB_ATT_TYPE_MOVEPATH]    /* movepath_framePhase */
+                           );
+
+  gimp_layer_resize_to_image_size(new_layer_id);
+
+  gimp_layer_scale(new_layer_id
+                  , gimp_drawable_width(new_layer_id) * PVIEW_TO_MASTER_SCALE
+                  , gimp_drawable_height(new_layer_id) * PVIEW_TO_MASTER_SCALE
+                  , TRUE  /*  TRUE: centered local on layer */
+                  );
+
+
+  if(gap_debug)
+  {
+    printf("p_create_transformed_layer_movepath: "
+      "new_layer_id:%d new_layers_image_id:%d  mov_obj_image_id:%d (preview)image_id:%d\n"
+      ,(int)new_layer_id
+      ,(int)gimp_drawable_get_image(new_layer_id)
+      ,(int)mov_obj_image_id
+      ,(int)image_id
+      );
+  }
+  if(mov_obj_image_id >= 0)
+  {
+    gap_image_delete_immediate(mov_obj_image_id);
+  }
+
+
+  gimp_drawable_set_visible(new_layer_id, TRUE);
+
+  p_adjust_stackposition(image_id, new_layer_id, stackposition);
+
+  gimp_layer_set_opacity(new_layer_id
+                        , calculated->opacity
+                        );
+  *layer_id_ptr = new_layer_id;
+
+  return (TRUE);
+
+}  /* end p_create_transformed_layer_movepath */
+
+
+/* -----------------------------------------
  * p_create_transformed_layer
  * -----------------------------------------
  * create transformed copy of the specified origsize_layer_id,
  * according to calculated transformation settings.
+ * 
  */
 static void
 p_create_transformed_layer(gint32 image_id
   , gint32 origsize_layer_id
   , gint32 *layer_id_ptr
   , GapStoryCalcAttr  *calculated
-  , gint32 stackposition, const char *layername)
+  , gint32 stackposition
+  , const char *layername
+  , GapStbAttrWidget *attw
+  , gint img_idx
+  , gboolean enableMovepath
+  )
 {
-  /* if size is not equal calculated size remove curr layer
+  char    *movepath_file_xml;
+
+  movepath_file_xml = NULL;
+  if((attw != NULL)
+  && (enableMovepath))
+  {
+    if(attw->stb_elem_refptr != NULL)
+    {
+      if (attw->stb_elem_refptr->att_arr_enable[GAP_STB_ATT_TYPE_MOVEPATH] == TRUE)
+      {
+        movepath_file_xml = attw->stb_elem_refptr->att_movepath_file_xml;
+      }
+    }
+  }
+  
+  /* if size is not equal calculated size 
+   * or movepath rendering necessary then remove curr layer
    * to force recreation in desired size
    */
   if (*layer_id_ptr >= 0)
   {
      if ((gimp_drawable_width(*layer_id_ptr)  != calculated->width)
-     ||  (gimp_drawable_height(*layer_id_ptr) != calculated->height))
+     ||  (gimp_drawable_height(*layer_id_ptr) != calculated->height)
+     ||  (movepath_file_xml != NULL))
      {
         gimp_image_remove_layer( image_id
                                , *layer_id_ptr);
@@ -1444,11 +1735,34 @@ p_create_transformed_layer(gint32 image_id
   {
     gint32 new_layer_id;
 
+    if(movepath_file_xml != NULL)
+    {
+      gboolean movepathOk;
+      
+      movepathOk = p_create_transformed_layer_movepath(image_id
+                 , origsize_layer_id
+                 , layer_id_ptr
+                 , calculated
+                 , stackposition
+                 , layername
+                 , attw
+                 , img_idx
+                 );
+      if(movepathOk == TRUE)
+      {
+        return;
+      }
+      /* else fallback to rendering without movepath transformations */
+    }
 
     new_layer_id = gimp_layer_copy(origsize_layer_id);
     gimp_image_add_layer (image_id, new_layer_id, stackposition);
     gimp_drawable_set_name(new_layer_id, layername);
+    
     gimp_layer_scale(new_layer_id, calculated->width, calculated->height, 0);
+
+
+
     gimp_drawable_set_visible(new_layer_id, TRUE);
 
     *layer_id_ptr = new_layer_id;
@@ -1464,7 +1778,6 @@ p_create_transformed_layer(gint32 image_id
 
   gap_story_transform_rotate_layer(image_id, *layer_id_ptr, calculated->rotate);
 
-
   gimp_layer_set_opacity(*layer_id_ptr
                         , calculated->opacity
                         );
@@ -1534,6 +1847,9 @@ p_render_gfx(GapStbAttrWidget *attw, gint img_idx)
       , &calculate_pref_attributes
       , LAYERSTACK_PREF
       , LAYERNAME_PREF
+      , attw
+      , img_idx
+      , FALSE     /* do not enableMovepath */
       );
 
 
@@ -1548,6 +1864,9 @@ p_render_gfx(GapStbAttrWidget *attw, gint img_idx)
       , &calculate_curr_attributes
       , LAYERSTACK_CURR
       , LAYERNAME_CURR
+      , attw
+      , img_idx
+      , TRUE     /* enableMovepath */
       );
 
   prefetch_visible = p_calculate_prefetch_visibility(attw, img_idx);
@@ -1685,8 +2004,7 @@ p_stb_req_equals_layer_info(GapStbAttrLayerInfo *linfo
                          , GapStoryLocateRet *stb_ret
                          , const char *filename)
 {
-  if (stb_ret->stb_elem->record_type
-  != linfo->layer_record_type)
+  if (stb_ret->stb_elem->record_type != linfo->layer_record_type)
   {
     return(FALSE);
   }
@@ -1820,7 +2138,6 @@ p_orig_layer_frame_fetcher(GapStbAttrWidget *attw
    l_layer_id = -1;
    l_filename = NULL;
 
-
    {
      GapStoryLocateRet *stb_ret;
      GapStorySection   *section;
@@ -1837,9 +2154,10 @@ p_orig_layer_frame_fetcher(GapStbAttrWidget *attw
        {
          gboolean is_up_to_date;
 
-         l_filename = gap_story_get_filename_from_elem_nr(stb_ret->stb_elem
+         l_filename = gap_story_get_filename_from_elem_nr_anim(stb_ret->stb_elem
                                                 , stb_ret->ret_framenr
                                                );
+
          is_up_to_date = p_check_orig_layer_up_to_date(image_id
                              ,orig_layer_id_ptr
                              ,derived_layer_id_ptr
@@ -1847,6 +2165,7 @@ p_orig_layer_frame_fetcher(GapStbAttrWidget *attw
                              ,stb_ret
                              ,l_filename
                              );
+                             
          if(is_up_to_date)
          {
            g_free(stb_ret);
@@ -1854,6 +2173,7 @@ p_orig_layer_frame_fetcher(GapStbAttrWidget *attw
            {
              g_free(l_filename);
            }
+        
            return;  /* orig_layer is still up to date, done */
          }
 
@@ -1886,10 +2206,67 @@ p_orig_layer_frame_fetcher(GapStbAttrWidget *attw
              }
            }
          }
+         else if(stb_ret->stb_elem->record_type == GAP_STBREC_VID_COLOR)
+         {
+           l_layer_id = gimp_layer_new(image_id, "color"
+                                 ,gimp_image_width(image_id)
+                                 ,gimp_image_height(image_id)
+                                 ,GIMP_RGBA_IMAGE
+                                 ,100.0     /* Opacity */
+                                 ,GIMP_NORMAL_MODE
+                                 );
+           gimp_image_add_layer(image_id, l_layer_id, LAYERSTACK_TOP);
+           gap_layer_clear_to_color(l_layer_id
+                                   , stb_ret->stb_elem->color_red
+                                   , stb_ret->stb_elem->color_green
+                                   , stb_ret->stb_elem->color_blue
+                                   , stb_ret->stb_elem->color_alpha 
+                                   );
+         
+           linfo->layer_record_type = stb_ret->stb_elem->record_type;
+           linfo->layer_local_framenr = 0;
+           linfo->layer_seltrack = 1;
+           if (linfo->layer_filename != NULL)
+           {
+             g_free(linfo->layer_filename);
+             linfo->layer_filename = NULL;
+           }
+
+         }
+         else if(stb_ret->stb_elem->record_type == GAP_STBREC_VID_ANIMIMAGE)
+         {
+           if(gap_debug)
+           {
+             printf("GAP_STBREC_VID_ANIMIMAGE: l_filename:%s ret_framenr:%d\n"
+                 , l_filename
+                 , stb_ret->ret_framenr
+                 );
+           }
+           if(l_filename)
+           {
+             /* fetch_full_sized_frame */
+             l_layer_id = p_fetch_layer_from_animimage(l_filename
+                ,stb_ret->ret_framenr
+                ,image_id
+                );
+             if(l_layer_id > 0)
+             {
+                l_layer_id = gap_layer_flip(l_layer_id, stb_ret->stb_elem->flip_request);
+
+                linfo->layer_record_type = stb_ret->stb_elem->record_type;
+                linfo->layer_local_framenr = stb_ret->ret_framenr;
+                linfo->layer_seltrack = 1;
+                if (linfo->layer_filename != NULL)
+                {
+                  g_free(linfo->layer_filename);
+                  linfo->layer_filename = g_strdup(l_filename);
+                }
+             }
+           }
+         }
          else
          {
            /* record type is IMAGE or FRAME, fetch full size image
-            * TODO: anim frame and COLOR not handled
             */
            l_layer_id = p_fetch_imagefile_as_layer(l_filename
                             ,image_id
@@ -2163,6 +2540,8 @@ p_fetch_video_frame_as_layer(GapStbMainGlobalParams *sgpp
 /* ---------------------------------
  * p_fetch_imagefile_as_layer
  * ---------------------------------
+ * add the specified image filename as new layer on top of layerstack in the specified image_id.
+ * The newly added layer is a copy of the composite view (e.g. a merge of its visible layers)
  */
 static gint32
 p_fetch_imagefile_as_layer (const char *img_filename
@@ -2187,20 +2566,13 @@ p_fetch_imagefile_as_layer (const char *img_filename
     gint    l_src_offset_x, l_src_offset_y;
 
     gimp_image_undo_disable (l_tmp_image_id);
-    l_layer_id = gimp_image_flatten(l_tmp_image_id);
-    if(l_layer_id < 0)
+    l_layer_id = gap_image_merge_visible_layers(l_tmp_image_id, GIMP_CLIP_TO_IMAGE);
+    
+    if(!gimp_drawable_has_alpha(l_layer_id))
     {
-      l_layer_id = gimp_layer_new(l_tmp_image_id, LAYERNAME_ORIG,
-                             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_layer_id, LAYERSTACK_TOP);
-      gimp_layer_set_offsets(l_layer_id, -1, -1);
-      l_layer_id = gimp_image_flatten(l_tmp_image_id);
+      gimp_layer_add_alpha(l_layer_id);
     }
-    gimp_layer_add_alpha(l_layer_id);
+    gimp_layer_resize_to_image_size(l_layer_id);
 
     /* copy the layer from the temp image to the preview multilayer image */
     l_new_layer_id = gap_layer_copy_to_dest_image(image_id,
@@ -2217,11 +2589,246 @@ p_fetch_imagefile_as_layer (const char *img_filename
     gimp_image_delete(l_tmp_image_id);
   }
 
-
   return(l_new_layer_id);
+  
 }  /* end p_fetch_imagefile_as_layer */
 
 
+/* ---------------------------------
+ * p_fetch_layer_from_animimage
+ * ---------------------------------
+ * pick layer at specified localframe_index (e.g. layerstack_position) from specified 
+ * multilayer animimage on disk (specified via filename)
+ * and add a copy of this layer as new layer on top of layerstack in the specified image_id.
+ */
+static gint32
+p_fetch_layer_from_animimage (const char *img_filename
+                   , gint32 localframe_index, gint32 image_id
+                   )
+{
+  gint32 l_tmp_image_id;
+  gint32 l_new_layer_id;
+
+  l_new_layer_id  = -1;
+
+  if(gap_debug)
+  {
+    printf("p_fetch_layer_from_animimage START localframe_index:%d img_filename:%s\n"
+         ,(int)localframe_index
+         ,img_filename
+       );
+  }
+
+
+
+  {
+    char  *l_filename;
+    l_filename = g_strdup(img_filename);
+    l_tmp_image_id = gap_lib_load_image(l_filename);
+    g_free(l_filename);
+  }
+
+  if(l_tmp_image_id >= 0)
+  {
+    gint    l_src_offset_x, l_src_offset_y;
+    gint          l_nlayers;
+    gint32       *l_layers_list;
+
+    gimp_image_undo_disable (l_tmp_image_id);
+    
+    l_layers_list = gimp_image_get_layers(l_tmp_image_id, &l_nlayers);
+
+    if(gap_debug)
+    {
+      printf("p_fetch_layer_from_animimage l_nlayers:%d localframe_index:%d img_filename:%s\n"
+         ,(int)l_nlayers
+         ,(int)localframe_index
+         ,img_filename
+         );
+    }
+
+
+    if(l_layers_list != NULL)
+    {
+       if((localframe_index < l_nlayers)
+       && (localframe_index >= 0))
+       {
+          gimp_drawable_set_visible(l_layers_list[localframe_index], TRUE);
+          if (0 != gimp_layer_get_apply_mask(l_layers_list[localframe_index]))
+          {
+            /* the layer has an active mask, apply the mask now
+             * because copying from the layer ignores the mask
+             */
+            gimp_layer_remove_mask(l_layers_list[localframe_index], GIMP_MASK_APPLY);
+          }
+          gimp_layer_resize_to_image_size(l_layers_list[localframe_index]);
+          
+          /* copy the picked layer from the temp image to the preview multilayer image */
+          l_new_layer_id = gap_layer_copy_to_dest_image(image_id,
+                                   l_layers_list[localframe_index],
+                                   100.0,       /* opacity full */
+                                   0,           /* NORMAL */
+                                   &l_src_offset_x,
+                                   &l_src_offset_y
+                                   );
+
+          gimp_image_add_layer (image_id, l_new_layer_id, LAYERSTACK_TOP);
+          
+       }
+       g_free (l_layers_list);
+    }
+    
+    /* destroy the tmp image */
+    gimp_image_delete(l_tmp_image_id);
+  }
+
+
+  return(l_new_layer_id);
+}  /* end p_fetch_layer_from_animimage */
+
+
+/* ==================================================== START MOVEPATH FILESEL stuff ======  */
+/* --------------------------------
+ * p_attw_movepath_filesel_pw_ok_cb
+ * --------------------------------
+ */
+static void
+p_attw_movepath_filesel_pw_ok_cb (GtkWidget *widget
+                   ,GapStbAttrWidget *attw)
+{
+  const gchar *movepathFilename;
+  gchar *dup_movepathFilename;
+
+  if(attw == NULL) return;
+  if(attw->movepath_filesel == NULL) return;
+
+  dup_movepathFilename = NULL;
+  movepathFilename = gtk_file_selection_get_filename (GTK_FILE_SELECTION (attw->movepath_filesel));
+  if(movepathFilename)
+  {
+    dup_movepathFilename = g_strdup(movepathFilename);
+  }
+
+  gtk_widget_destroy(GTK_WIDGET(attw->movepath_filesel));
+
+  if(dup_movepathFilename)
+  {
+    gtk_entry_set_text(GTK_ENTRY(attw->movepath_file_entry), dup_movepathFilename);
+    g_free(dup_movepathFilename);
+  }
+
+  attw->movepath_filesel = NULL;
+}  /* end p_attw_movepath_filesel_pw_ok_cb */
+
+
+/* -----------------------------------
+ * p_attw_movepath_filesel_pw_close_cb
+ * -----------------------------------
+ */
+static void
+p_attw_movepath_filesel_pw_close_cb ( GtkWidget *widget
+                      , GapStbAttrWidget *attw)
+{
+  if(attw->movepath_filesel == NULL) return;
+
+  gtk_widget_destroy(GTK_WIDGET(attw->movepath_filesel));
+  attw->movepath_filesel = NULL;   /* indicate that filesel is closed */
+
+}  /* end p_attw_movepath_filesel_pw_close_cb */
+
+
+
+/* ---------------------------------
+ * p_attw_movepath_filesel_button_cb
+ * ---------------------------------
+ */
+static void
+p_attw_movepath_filesel_button_cb ( GtkWidget *w
+                       , GapStbAttrWidget *attw)
+{
+  GtkWidget *filesel = NULL;
+
+  if(attw->attw_prop_dialog == NULL)
+  {
+     return;
+  }
+
+  if(attw->movepath_filesel != NULL)
+  {
+     gtk_window_present(GTK_WINDOW(attw->movepath_filesel));
+     return;   /* filesel is already open */
+  }
+  if(attw->stb_elem_refptr == NULL) { return; }
+
+  filesel = gtk_file_selection_new ( _("Set Movepath Parameterfile (XML)"));
+  attw->movepath_filesel = filesel;
+
+  gtk_window_set_position (GTK_WINDOW (filesel), GTK_WIN_POS_MOUSE);
+  g_signal_connect (GTK_FILE_SELECTION (filesel)->ok_button,
+                    "clicked", G_CALLBACK (p_attw_movepath_filesel_pw_ok_cb),
+                    attw);
+  g_signal_connect (GTK_FILE_SELECTION (filesel)->cancel_button,
+                    "clicked", G_CALLBACK (p_attw_movepath_filesel_pw_close_cb),
+                    attw);
+  g_signal_connect (filesel, "destroy",
+                    G_CALLBACK (p_attw_movepath_filesel_pw_close_cb),
+                    attw);
+
+
+  if(attw->stb_elem_refptr->att_movepath_file_xml)
+  {
+    gtk_file_selection_set_filename (GTK_FILE_SELECTION (filesel),
+                                     attw->stb_elem_refptr->att_movepath_file_xml);
+  }
+  gtk_widget_show (filesel);
+
+}  /* end p_attw_movepath_filesel_button_cb */
+
+
+/* ==================================================== END MOVEPATH FILESEL stuff ======  */
+
+/* ------------------------------------
+ * p_attw_movepath_file_validity_check
+ * ------------------------------------
+ */
+static void
+p_attw_movepath_file_validity_check(GapStbAttrWidget *attw)
+{
+  if(attw == NULL) { return; }
+  attw->movepath_file_xml_is_valid = FALSE;
+  
+  if(attw->stb_elem_refptr == NULL) { return; }
+
+
+  attw->movepath_file_xml_is_valid = gap_mov_exec_check_valid_xml_paramfile(attw->stb_elem_refptr->att_movepath_file_xml);
+
+  p_attw_update_sensitivity(attw);
+  p_attw_update_properties(attw);
+
+}  /* end p_attw_movepath_file_validity_check */
+
+
+/* ------------------------------------
+ * p_attw_movepath_file_entry_update_cb
+ * ------------------------------------
+ */
+static void
+p_attw_movepath_file_entry_update_cb(GtkWidget *widget, GapStbAttrWidget *attw)
+{
+  if(attw == NULL) { return; }
+  if(attw->stb_elem_refptr == NULL) { return; }
+
+  p_attw_push_undo_and_set_unsaved_changes(attw);
+
+  if(attw->stb_elem_refptr->att_movepath_file_xml)
+  {
+    g_free(attw->stb_elem_refptr->att_movepath_file_xml);
+  }
+  attw->stb_elem_refptr->att_movepath_file_xml = g_strdup(gtk_entry_get_text(GTK_ENTRY(widget)));
+
+  p_attw_movepath_file_validity_check(attw);
+
+}  /* end p_attw_movepath_file_entry_update_cb */
 
 
 /* ------------------------------
@@ -2233,10 +2840,8 @@ p_attw_comment_entry_update_cb(GtkWidget *widget, GapStbAttrWidget *attw)
 {
   if(attw == NULL) { return; }
   if(attw->stb_elem_refptr == NULL) { return; }
-  if(attw->stb_refptr)
-  {
-    attw->stb_refptr->unsaved_changes = TRUE;
-  }
+
+  p_attw_push_undo_and_set_unsaved_changes(attw);
 
   if(attw->stb_elem_refptr->comment == NULL)
   {
@@ -2253,6 +2858,7 @@ p_attw_comment_entry_update_cb(GtkWidget *widget, GapStbAttrWidget *attw)
   }
 }  /* end p_attw_comment_entry_update_cb */
 
+
 /* ---------------------------------------
  * p_init_layer_info
  * ---------------------------------------
@@ -2506,7 +3112,7 @@ p_create_and_attach_att_arr_widgets(const char *row_title
   gtk_widget_set_size_request (spinbutton, 80, -1);
   gimp_help_set_help_data (spinbutton, from_tooltip, NULL);
 
-  g_object_set_data(G_OBJECT(adj), "att_type_idx", att_type_idx);
+  g_object_set_data(G_OBJECT(adj), "att_type_idx", (gpointer)att_type_idx);
   g_object_set_data(G_OBJECT(adj), OBJ_DATA_KEY_ATTW, attw);
   g_signal_connect (G_OBJECT (adj), "value_changed",
                       G_CALLBACK (p_attw_gdouble_adjustment_callback),
@@ -2549,7 +3155,7 @@ p_create_and_attach_att_arr_widgets(const char *row_title
   gtk_widget_set_size_request (spinbutton, 80, -1);
   gimp_help_set_help_data (spinbutton, to_tooltip, NULL);
 
-  g_object_set_data(G_OBJECT(adj), "att_type_idx", att_type_idx);
+  g_object_set_data(G_OBJECT(adj), "att_type_idx", (gpointer)att_type_idx);
   g_object_set_data(G_OBJECT(adj), OBJ_DATA_KEY_ATTW, attw);
   g_signal_connect (G_OBJECT (adj), "value_changed",
                       G_CALLBACK (p_attw_gdouble_adjustment_callback),
@@ -2676,6 +3282,8 @@ gap_story_attw_properties_dialog (GapStbAttrWidget *attw)
   tabw = (GapStbTabWidgets *)attw->tabw;
   if(tabw == NULL) { return (NULL); }
 
+  attw->movepath_filesel = NULL;
+
   if(attw->stb_elem_bck)
   {
     dlg = gimp_dialog_new (_("Transition Attributes"), "gap_story_attr_properties"
@@ -3014,6 +3622,36 @@ gap_story_attw_properties_dialog (GapStbAttrWidget *attw)
       , &attw->stb_elem_refptr->att_arr_value_accel[att_type_idx]
       );
 
+    row++;
+    att_type_idx = GAP_STB_ATT_TYPE_MOVEPATH;
+    p_create_and_attach_att_arr_widgets(_("Move Path:")
+      , attw
+      , table
+      , row
+      , col
+      , att_type_idx
+      ,  1.0       /* lower constraint for the from/to values */
+      ,  10000.0   /* upper constraint for the from/to values */
+      , 1.0        /* step increment   for the from/to values  */
+      , 10.0       /* page increment   for the from/to values */
+      , 0.0        /* page size        for the from/to values */
+      , 0          /* digits for the from/to values */
+      , _("ON: Enable move path transistions using settings provided via a movepath parameter file")
+      , &attw->stb_elem_refptr->att_arr_enable[att_type_idx]
+      , _("frame number (phase) of the movement/transition along path for the first handled frame"
+          " where 1 is the begin of the path using settings of the 1st controlpoint"
+          " in the movepath parameter file")
+      , &attw->stb_elem_refptr->att_arr_value_from[att_type_idx]
+      , _("frame number (phase) of the movement/transition along path for the last handled frame."
+          " note that frame numbers higher than (or equal to) total frames in the movepath parameter file"
+          " uses settings of the last controlpoint in this file.")
+      , &attw->stb_elem_refptr->att_arr_value_to[att_type_idx]
+      , _("number of frames")
+      , &attw->stb_elem_refptr->att_arr_value_dur[att_type_idx]
+      , _("acceleration characteristic (currently ignored)")
+      , &attw->stb_elem_refptr->att_arr_value_accel[att_type_idx]
+      );
+
   }
 
 
@@ -3048,6 +3686,46 @@ gap_story_attw_properties_dialog (GapStbAttrWidget *attw)
       );
   }
 
+  row++;
+
+
+  {
+    GtkWidget *button;
+
+
+    /* the movepath label */
+    label = gtk_label_new (_("Movepath File:"));
+    gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
+    gtk_table_attach_defaults (GTK_TABLE(table), label, 0, 1, row, row+1);
+    gtk_widget_show (label);
+
+
+    /* the movepath entry */
+    entry = gtk_entry_new ();
+    gtk_widget_set_size_request(entry, ATTW_COMMENT_WIDTH, -1);
+    if(attw->stb_elem_refptr)
+    {
+      if(attw->stb_elem_refptr->att_movepath_file_xml)
+      {
+        gtk_entry_set_text(GTK_ENTRY(entry), attw->stb_elem_refptr->att_movepath_file_xml);
+      }
+    }
+    gtk_table_attach_defaults (GTK_TABLE(table), entry, 1, 8, row, row+1);
+    g_signal_connect(G_OBJECT(entry), "changed",
+                     G_CALLBACK(p_attw_movepath_file_entry_update_cb),
+                     attw);
+    gtk_widget_show (entry);
+    attw->movepath_file_entry = entry;
+    
+    /* the filesel invoker button */
+    button = gtk_button_new_with_label ("...");
+    gtk_table_attach_defaults (GTK_TABLE(table), button, 8, 10, row, row+1);
+    g_signal_connect(G_OBJECT(button), "clicked",
+                     G_CALLBACK(p_attw_movepath_filesel_button_cb),
+                     attw);
+    gtk_widget_show (button);
+    
+  }
 
   row++;
 
@@ -3079,7 +3757,7 @@ gap_story_attw_properties_dialog (GapStbAttrWidget *attw)
   gtk_widget_show (entry);
   attw->comment_entry = entry;
 
-
+  p_attw_movepath_file_validity_check(attw);
   p_attw_update_sensitivity(attw);
 
   /*  Show the main containers  */
diff --git a/gap/gap_story_file.c b/gap/gap_story_file.c
index 69637b6..f8f6dea 100644
--- a/gap/gap_story_file.c
+++ b/gap/gap_story_file.c
@@ -82,6 +82,7 @@ static const char *gtab_att_transition_key_words[GAP_STB_ATT_TYPES_ARRAY_MAX] =
        , GAP_STBKEY_VID_ZOOM_X
        , GAP_STBKEY_VID_ZOOM_Y
        , GAP_STBKEY_VID_ROTATE
+       , GAP_STBKEY_VID_MOVEPATH
        };
 
 
@@ -274,6 +275,10 @@ gap_story_debug_print_elem(GapStoryElem *stb_elem)
             );
         }
       }
+      if(stb_elem->att_movepath_file_xml != NULL)
+      {
+        printf("   att_movepath_file_xml: %s\n", stb_elem->att_movepath_file_xml);
+      }
       printf("   overlap: %d\n", (int)stb_elem->att_overlap);
     }
 
@@ -758,7 +763,9 @@ gap_story_new_elem(GapStoryRecordType record_type)
         stb_elem->att_arr_value_dur[ii] = 1;        /* number of frames to change from -> to value */
         stb_elem->att_arr_value_accel[ii] = 0;      /* per default use no acceleration characteristic */
       }
+      stb_elem->att_arr_value_to[GAP_STB_ATT_TYPE_MOVEPATH] = 100.0;
     }
+    stb_elem->att_movepath_file_xml = NULL;
 
     /* init members for Audio Record types */
     stb_elem->aud_filename = NULL;
@@ -1301,14 +1308,15 @@ gap_story_upd_elem_from_filename(GapStoryElem *stb_elem,  const char *filename)
 static void
 p_free_stb_elem(GapStoryElem *stb_elem)
 {
-  if(stb_elem->orig_filename)     { g_free(stb_elem->orig_filename);}
-  if(stb_elem->orig_src_line)     { g_free(stb_elem->orig_src_line);}
-  if(stb_elem->basename)          { g_free(stb_elem->basename);}
-  if(stb_elem->ext)               { g_free(stb_elem->ext);}
-  if(stb_elem->filtermacro_file)  { g_free(stb_elem->filtermacro_file);}
-  if(stb_elem->colormask_file)    { g_free(stb_elem->colormask_file);}
-  if(stb_elem->preferred_decoder) { g_free(stb_elem->preferred_decoder);}
-  if(stb_elem->mask_name)         { g_free(stb_elem->mask_name);}
+  if(stb_elem->orig_filename)          { g_free(stb_elem->orig_filename);}
+  if(stb_elem->orig_src_line)          { g_free(stb_elem->orig_src_line);}
+  if(stb_elem->basename)               { g_free(stb_elem->basename);}
+  if(stb_elem->ext)                    { g_free(stb_elem->ext);}
+  if(stb_elem->filtermacro_file)       { g_free(stb_elem->filtermacro_file);}
+  if(stb_elem->colormask_file)         { g_free(stb_elem->colormask_file);}
+  if(stb_elem->preferred_decoder)      { g_free(stb_elem->preferred_decoder);}
+  if(stb_elem->mask_name)              { g_free(stb_elem->mask_name);}
+  if(stb_elem->att_movepath_file_xml)  { g_free(stb_elem->att_movepath_file_xml);}
 }  /* end p_free_stb_elem */
 
 
@@ -3327,13 +3335,14 @@ p_story_parse_line(GapStoryBoard *stb, char *longline
   ||  (strcmp(l_record_key, GAP_STBKEY_VID_MOVE_X)   == 0)
   ||  (strcmp(l_record_key, GAP_STBKEY_VID_MOVE_Y)   == 0)
   ||  (strcmp(l_record_key, GAP_STBKEY_VID_FIT_SIZE) == 0)
+  ||  (strcmp(l_record_key, GAP_STBKEY_VID_MOVEPATH) == 0)
   ||  (strcmp(l_record_key, GAP_STBKEY_VID_OVERLAP)  == 0))
   {
-    char *l_track_ptr      = l_wordval[1];
-    char *l_from_ptr       = l_wordval[2];
-    char *l_to_ptr         = l_wordval[3];
-    char *l_dur_ptr        = l_wordval[4];
-    char *l_accel_ptr      = l_wordval[5];
+    char *l_track_ptr         = l_wordval[1];
+    char *l_from_ptr          = l_wordval[2];
+    char *l_to_ptr            = l_wordval[3];
+    char *l_dur_ptr           = l_wordval[4];
+    char *l_accel_ptr         = l_wordval[5];
 
     gint32 l_track_nr;
 
@@ -3344,6 +3353,38 @@ p_story_parse_line(GapStoryBoard *stb, char *longline
     }
 
 
+    /* ATTRIBUTE: GAP_STBKEY_VID_MOVEPATH */
+    if (strcmp(l_record_key, GAP_STBKEY_VID_MOVEPATH) == 0)
+    {
+      char *l_xml_ptr = l_wordval[6];
+      gboolean elem_already_in_the_list = FALSE;
+
+      ii = GAP_STB_ATT_TYPE_MOVEPATH;
+
+      stb_elem =  p_get_att_transition_elem(stb, &elem_already_in_the_list, l_track_nr);
+      if(stb_elem)
+      {
+        stb_elem->att_arr_enable[ii] = TRUE;
+        stb_elem->file_line_nr = longlinenr;
+        stb_elem->orig_src_line = g_strdup(multi_lines);
+        stb_elem->track = l_track_nr;
+        if(*l_from_ptr)     { stb_elem->att_arr_value_from[ii]  = p_scan_gdouble(l_from_ptr, 1.0, 10000.0, stb); }
+        if(*l_to_ptr)       { stb_elem->att_arr_value_to[ii]    = p_scan_gdouble(l_to_ptr,   0.0, 10000.0, stb); }
+        else                { stb_elem->att_arr_value_to[ii]    = stb_elem->att_arr_value_from[ii]; }
+        if(*l_dur_ptr)      { stb_elem->att_arr_value_dur[ii]   = p_scan_gint32(l_dur_ptr,  0, 10000, stb); }
+        if(*l_xml_ptr)      { stb_elem->att_movepath_file_xml   = g_strdup(l_xml_ptr); }
+        else                { stb_elem->att_arr_enable[ii] = FALSE; }
+
+        p_assign_accel_attr(stb_elem, stb, l_accel_ptr, ii);
+
+        if(elem_already_in_the_list != TRUE)
+        {
+          gap_story_list_append_elem(stb, stb_elem);
+        }
+      }
+      goto cleanup;
+    }
+
     /* ATTRIBUTE: GAP_STBKEY_VID_OVERLAP */
     if (strcmp(l_record_key, GAP_STBKEY_VID_OVERLAP) == 0)
     {
@@ -4848,7 +4889,20 @@ p_story_save_att_transition(FILE *fp, GapStoryElem *stb_elem)
 
   for(ii=0; ii < GAP_STB_ATT_TYPES_ARRAY_MAX; ii++)
   {
-    if(stb_elem->att_arr_enable[ii])
+    gboolean writeEnable;
+    
+    writeEnable = stb_elem->att_arr_enable[ii];
+    if((ii == GAP_STB_ATT_TYPE_MOVEPATH)
+    && (stb_elem->att_movepath_file_xml != NULL))
+    {
+      if(stb_elem->att_movepath_file_xml[0] == '\0')
+      {
+        /* disable movepath in case of empty filename */
+        writeEnable = FALSE;
+      }
+    }
+  
+    if(writeEnable)
     {
       gchar l_from_str[G_ASCII_DTOSTR_BUF_SIZE];
       gchar l_to_str[G_ASCII_DTOSTR_BUF_SIZE];
@@ -4866,16 +4920,20 @@ p_story_save_att_transition(FILE *fp, GapStoryElem *stb_elem)
                      ,stb_elem->att_arr_value_to[ii]
                      );
 
-      fprintf(fp, "%s         "
+      fprintf(fp, "%s        "
                  , gtab_att_transition_key_words[ii]
                  );
-      if(ii !=0 )
+      if(ii != 6)
       {
-        /* print one extra space
-         * VID_MOVE_X, VID_MOVE_Y, VID_ZOOM_X, VID_ZOOM_Y VID_ROTATE
-         * are 1 character shorter than VID_OPACITY
-         */
         fprintf(fp, " ");
+        if(ii != 0 )
+        {
+          /* print one extra space
+           * VID_MOVE_X, VID_MOVE_Y, VID_ZOOM_X, VID_ZOOM_Y VID_ROTATE
+           * are 1 character shorter than VID_OPACITY
+           */
+          fprintf(fp, " ");
+        }
       }
 
       fprintf(fp, "%s:%d %s:%s %s:%s %s:%d"
@@ -4890,6 +4948,13 @@ p_story_save_att_transition(FILE *fp, GapStoryElem *stb_elem)
                  , l_parnam_tab.parname[5]              , stb_elem->att_arr_value_accel[ii]
                  );
       }
+      if((ii == GAP_STB_ATT_TYPE_MOVEPATH)
+      && (stb_elem->att_movepath_file_xml != NULL))
+      {
+        fprintf(fp, " %s:\"%s\""
+                 , l_parnam_tab.parname[6]              , stb_elem->att_movepath_file_xml
+                 );
+      }
       fprintf(fp, "\n");
     }
   }
@@ -6119,6 +6184,7 @@ p_story_get_filename_from_elem (GapStoryElem *stb_elem, gint32 in_framenr)
 /* --------------------------------
  * gap_story_get_filename_from_elem
  * gap_story_get_filename_from_elem_nr
+ * gap_story_get_filename_from_elem_nr_anim
  * --------------------------------
  */
 char *
@@ -6133,6 +6199,39 @@ gap_story_get_filename_from_elem_nr (GapStoryElem *stb_elem, gint32 in_framenr)
   return(p_story_get_filename_from_elem(stb_elem, in_framenr));
 }  /* end gap_story_get_filename_from_elem_nr */
 
+char *
+gap_story_get_filename_from_elem_nr_anim (GapStoryElem *stb_elem, gint32 in_framenr)
+{
+  char *filename;
+
+  filename = NULL;
+  if(stb_elem)
+  {
+    switch(stb_elem->record_type)
+    {
+      case GAP_STBREC_VID_MOVIE:
+      case GAP_STBREC_VID_ANIMIMAGE:
+      case GAP_STBREC_VID_IMAGE:
+        filename = g_strdup(stb_elem->orig_filename);
+        break;
+      case GAP_STBREC_VID_FRAMES:
+        if(in_framenr < 0)
+        {
+          in_framenr = stb_elem->from_frame;
+        }
+        filename = gap_lib_alloc_fname(stb_elem->basename
+                                      , in_framenr
+                                      , stb_elem->ext
+                                      );
+
+        break;
+      default:
+        break;
+    }
+  }
+  return(filename);
+}  /* end gap_story_get_filename_from_elem_nr_anim */
+
 
 
 /* ---------------------------------
@@ -6876,6 +6975,7 @@ gap_story_elem_duplicate(GapStoryElem      *stb_elem)
     if(stb_elem->filtermacro_file)  stb_elem_dup->filtermacro_file  = g_strdup(stb_elem->filtermacro_file);
     if(stb_elem->colormask_file)    stb_elem_dup->colormask_file    = g_strdup(stb_elem->colormask_file);
     if(stb_elem->preferred_decoder) stb_elem_dup->preferred_decoder = g_strdup(stb_elem->preferred_decoder);
+    if(stb_elem->att_movepath_file_xml) stb_elem_dup->att_movepath_file_xml = g_strdup(stb_elem->att_movepath_file_xml);
     stb_elem_dup->seltrack        = stb_elem->seltrack;
     stb_elem_dup->exact_seek      = stb_elem->exact_seek;
     stb_elem_dup->delace          = stb_elem->delace;
@@ -7031,6 +7131,8 @@ gap_story_elem_copy(GapStoryElem *stb_elem_dst, GapStoryElem *stb_elem)
         stb_elem_dst->att_arr_value_accel[ii] = stb_elem->att_arr_value_accel[ii];
       }
     }
+    stb_elem_dst->att_movepath_file_xml  = NULL;
+    if(stb_elem->att_movepath_file_xml)  stb_elem_dst->att_movepath_file_xml  = g_strdup(stb_elem->att_movepath_file_xml);
 
     stb_elem_dst->flip_request            = stb_elem->flip_request;
     stb_elem_dst->att_overlap             = stb_elem->att_overlap;
@@ -8922,10 +9024,11 @@ gdouble
 gap_story_get_default_attribute(gint att_typ_idx)
 {
    if ((att_typ_idx == GAP_STB_ATT_TYPE_OPACITY)
+   ||  (att_typ_idx == GAP_STB_ATT_TYPE_MOVEPATH)
    ||  (att_typ_idx == GAP_STB_ATT_TYPE_ZOOM_X)
    ||  (att_typ_idx == GAP_STB_ATT_TYPE_ZOOM_Y))
    {
-     return (1.0);  /* 1.0 indicates fully opaque, or no scaling */
+     return (1.0);  /* 1.0 indicates fully opaque, or no scaling or frame 1 for movepath */
    }
    return (0.0);  /* indicates centerd positioning or rotate 0 degree */
 }  /* end gap_story_get_default_attribute */
diff --git a/gap/gap_story_file.h b/gap/gap_story_file.h
index d3e4ae2..467f1eb 100644
--- a/gap/gap_story_file.h
+++ b/gap/gap_story_file.h
@@ -39,13 +39,14 @@
 /* transition attribute types
  * (values are used as index for look-up tables)
  */
-#define GAP_STB_ATT_TYPES_ARRAY_MAX 6
+#define GAP_STB_ATT_TYPES_ARRAY_MAX 7
 #define GAP_STB_ATT_TYPE_OPACITY  0
 #define GAP_STB_ATT_TYPE_MOVE_X   1
 #define GAP_STB_ATT_TYPE_MOVE_Y   2
 #define GAP_STB_ATT_TYPE_ZOOM_X   3
 #define GAP_STB_ATT_TYPE_ZOOM_Y   4
 #define GAP_STB_ATT_TYPE_ROTATE   5
+#define GAP_STB_ATT_TYPE_MOVEPATH 6
 
 
 #define GAP_STB_MASK_SECTION_NAME  "Masks"
@@ -186,6 +187,7 @@
     gint32   att_arr_value_dur[GAP_STB_ATT_TYPES_ARRAY_MAX];        /* number of frames to change from -> to value */
     gint32   att_arr_value_accel[GAP_STB_ATT_TYPES_ARRAY_MAX];      /* acceleration characteristics */
     gint32   att_overlap;  /* number of overlapping frames (value > 0 will generate a shadow track) */
+    gchar   *att_movepath_file_xml;
 
     /* new members for Audio Record types */
     char     *aud_filename;
@@ -388,6 +390,7 @@ gint32              gap_story_get_framenr_by_story_id(GapStorySection  *section,
 gint32              gap_story_get_expanded_framenr_by_story_id(GapStorySection *section, gint32 story_id, gint32 in_track);
 char *              gap_story_get_filename_from_elem(GapStoryElem *stb_elem);
 char *              gap_story_get_filename_from_elem_nr(GapStoryElem *stb_elem, gint32 in_framenr);
+char *              gap_story_get_filename_from_elem_nr_anim(GapStoryElem *stb_elem, gint32 in_framenr);
 GapStoryElem *      gap_story_fetch_nth_active_elem(GapStoryBoard *stb
                                                      , gint32 seq_nr
                                                      , gint32 in_track
diff --git a/gap/gap_story_main.h b/gap/gap_story_main.h
index 8f8c95f..48a908a 100644
--- a/gap/gap_story_main.h
+++ b/gap/gap_story_main.h
@@ -322,6 +322,9 @@ typedef struct GapStbAttrWidget  /* nickname: attw */
   GtkWidget  *button_overlap_dur;
 
   GtkWidget  *comment_entry;
+  GtkWidget  *movepath_file_entry;
+  GtkWidget  *movepath_filesel;
+  gboolean    movepath_file_xml_is_valid;
 
   struct GapStbAttrWidget *next;
 } GapStbAttrWidget;
diff --git a/gap/gap_story_properties.c b/gap/gap_story_properties.c
index 0a2a5ae..1b811ed 100644
--- a/gap/gap_story_properties.c
+++ b/gap/gap_story_properties.c
@@ -1694,13 +1694,15 @@ p_pw_check_ainfo_range(GapStbPropWidget *pw, char *filename)
                              ,pw->stb_refptr
                              ,pw->stb_elem_refptr
                              );
+        if(pw->stb_elem_refptr->record_type == GAP_STBREC_VID_ANIMIMAGE)
+        {
+          l_lower = 0;
+        }
         if(velem)
         {
-          l_upper = velem->total_frames;
-          if(pw->stb_elem_refptr->record_type == GAP_STBREC_VID_ANIMIMAGE)
+          if(pw->stb_elem_refptr->record_type == GAP_STBREC_VID_SECTION)
           {
-            l_lower = 0;
-            l_upper--;
+            l_upper = velem->total_frames;
           }
         }
       } 
@@ -2816,7 +2818,8 @@ p_pw_update_framenr_labels(GapStbPropWidget *pw, gint32 framenr)
 {
   char    txt_buf[100];
   gdouble l_speed_fps;
-
+  gint32  l_framenr_zero;
+  
   if(pw == NULL) { return; }
   if(pw->stb_elem_refptr == NULL) { return; }
 
@@ -2827,6 +2830,15 @@ p_pw_update_framenr_labels(GapStbPropWidget *pw, gint32 framenr)
   gtk_label_set_text ( GTK_LABEL(pw->pw_framenr_label), txt_buf);
 
   l_speed_fps = GAP_STORY_DEFAULT_FRAMERATE;
+  l_framenr_zero = framenr -1;
+  if(pw->stb_elem_refptr->record_type == GAP_STBREC_VID_ANIMIMAGE)
+  {
+    /* animimage framenr (e.g. layerstack position) starts at 0
+     * (other clips typically start with framenr 1)
+     */
+    l_framenr_zero = framenr;
+  }
+
   if(pw->stb_refptr)
   {
     if(pw->stb_refptr->master_framerate > 0)
@@ -2838,7 +2850,9 @@ p_pw_update_framenr_labels(GapStbPropWidget *pw, gint32 framenr)
   // TODO: can not show the exact frametime for
   // clips that do not start at frame 1
 
-  gap_timeconv_framenr_to_timestr(framenr -1
+  
+
+  gap_timeconv_framenr_to_timestr(l_framenr_zero
                          , l_speed_fps
                          , txt_buf
                          , sizeof(txt_buf)
diff --git a/gap/gap_story_render_lossless.c b/gap/gap_story_render_lossless.c
index 9913f2d..d951884 100644
--- a/gap/gap_story_render_lossless.c
+++ b/gap/gap_story_render_lossless.c
@@ -339,31 +339,15 @@ p_check_chunk_fetch_possible(GapStoryRenderVidHandle *vidhand
                     , GapStoryRenderFrameRangeElem **frn_elem  /* OUT: pointer to relevant frame range element */
                     )
 {
+  GapStbFetchData gapStbFetchData;
+  GapStbFetchData *gfd;
   gint32    l_track;
   gchar  *l_framename;
   gchar  *l_videofile;
-  gdouble l_rotate;
-  gdouble l_opacity;
-  gdouble l_scale_x;
-  gdouble l_scale_y;
-  gdouble l_move_x;
-  gdouble l_move_y;
-  GapStoryRenderFrameRangeElem *l_frn_elem_2;
 
-  gint32        l_localframe_index;
-  gint32        l_local_stepcount;
-  gdouble       l_localframe_tween_rest;
-  gboolean      l_keep_proportions;
-  gboolean      l_fit_width;
-  gboolean      l_fit_height;
-  GapStoryRenderFrameType   l_frn_type;
-  char            *l_trak_filtermacro_file;
-  gdouble l_red_f;
-  gdouble l_green_f;
-  gdouble l_blue_f;
-  gdouble l_alpha_f;
   gint    l_cnt_active_tracks;
 
+  gfd = &gapStbFetchData;
 
   *video_frame_nr   = -1;
   *frn_elem = NULL;
@@ -380,68 +364,51 @@ p_check_chunk_fetch_possible(GapStoryRenderVidHandle *vidhand
     l_framename = p_fetch_framename(vidhand->frn_list
                  , master_frame_nr /* starts at 1 */
                  , l_track
-                 , &l_frn_type
-                 , &l_trak_filtermacro_file
-                 , &l_localframe_index   /* used for ANIMIMAGE and Videoframe Number, -1 for all other types */
-                 , &l_local_stepcount
-                 , &l_localframe_tween_rest
-                 , &l_keep_proportions
-                 , &l_fit_width
-                 , &l_fit_height
-                 , &l_red_f
-                 , &l_green_f
-                 , &l_blue_f
-                 , &l_alpha_f
-		 , &l_rotate
-                 , &l_opacity       /* output opacity 0.0 upto 1.0 */
-                 , &l_scale_x       /* output 0.0 upto 10.0 where 1.0 is 1:1 */
-                 , &l_scale_y       /* output 0.0 upto 10.0 where 1.0 is 1:1 */
-                 , &l_move_x        /* output -1.0 upto 1.0 where 0.0 is centered */
-                 , &l_move_y        /* output -1.0 upto 1.0 where 0.0 is centered */
-                 , &l_frn_elem_2    /* output selected to the relevant framerange element */
+                 , gfd
                  );
 
      if(gap_debug)
      {
-       printf("l_track:%d  l_frn_type:%d\n", (int)l_track, (int)l_frn_type);
+       printf("l_track:%d  gfd->frn_type:%d\n", (int)l_track, (int)gfd->frn_type);
      }
 
-     if(l_frn_type != GAP_FRN_SILENCE)
+     if(gfd->frn_type != GAP_FRN_SILENCE)
      {
        l_cnt_active_tracks++;
      }
 
-     if((l_framename) || (l_frn_type == GAP_FRN_COLOR))
+     if((l_framename) || (gfd->frn_type == GAP_FRN_COLOR))
      {
        if(l_framename)
        {
-         if((l_frn_type == GAP_FRN_MOVIE)
-         || (l_frn_type == GAP_FRN_IMAGE)
-         || (l_frn_type == GAP_FRN_FRAMES))
+         if((gfd->frn_type == GAP_FRN_MOVIE)
+         || (gfd->frn_type == GAP_FRN_IMAGE)
+         || (gfd->frn_type == GAP_FRN_FRAMES))
          {
            if(l_cnt_active_tracks == 1)
            {
              /* check for transformations */
-             if((l_opacity == 1.0)
-             && (l_rotate == 0.0)
-             && (l_scale_x == 1.0)
-             && (l_scale_y == 1.0)
-             && (l_move_x == 0.0)
-             && (l_move_y == 0.0)
-             && (l_fit_width)
-             && (l_fit_height)
-             && (!l_keep_proportions)
-             && (l_frn_elem_2->flip_request == GAP_STB_FLIP_NONE)
-             && (l_frn_elem_2->mask_name == NULL)
-             && (l_trak_filtermacro_file == NULL))
+             if((gfd->opacity == 1.0)
+             && (gfd->rotate == 0.0)
+             && (gfd->scale_x == 1.0)
+             && (gfd->scale_y == 1.0)
+             && (gfd->move_x == 0.0)
+             && (gfd->move_y == 0.0)
+             && (gfd->fit_width)
+             && (gfd->fit_height)
+             && (!gfd->keep_proportions)
+             && (gfd->frn_elem->flip_request == GAP_STB_FLIP_NONE)
+             && (gfd->frn_elem->mask_name == NULL)
+             && (gfd->trak_filtermacro_file == NULL)
+             && (gfd->movepath_file_xml == NULL))
              {
                if(gap_debug)
                {
                  printf("gap_story_render_fetch_composite_image_or_chunk:  video:%s\n", l_framename);
                }
                l_videofile = g_strdup(l_framename);
-               *video_frame_nr = l_localframe_index;
-               *frn_elem = l_frn_elem_2;
+               *video_frame_nr = gfd->localframe_index;
+               *frn_elem = gfd->frn_elem;
              }
              else
              {
diff --git a/gap/gap_story_render_processor.c b/gap/gap_story_render_processor.c
index 1f5d088..f7a98d8 100644
--- a/gap/gap_story_render_processor.c
+++ b/gap/gap_story_render_processor.c
@@ -79,6 +79,7 @@
 #include "gap_fmac_name.h"
 #include "gap_frame_fetcher.h"
 #include "gap_accel_char.h"
+#include "gap_mov_exec.h"
 
 
 /* data for the storyboard proceesor frame fetching
@@ -104,7 +105,15 @@ typedef struct GapStbFetchData {   /* nick: gfd */
   gboolean      fit_width;
   gboolean      fit_height;
   GapStoryRenderFrameType   frn_type;
-  char            *trak_filtermacro_file;
+  const char      *trak_filtermacro_file;   /* dont g_free this ! */
+
+  gdouble          red_f;
+  gdouble          green_f;
+  gdouble          blue_f;
+  gdouble          alpha_f;
+  const char      *movepath_file_xml;      /* dont g_free this ! */
+  gdouble          movepath_framePhase;
+
 
   /* performance stuff for bypass render engine (where possible) */
   GapStoryFetchResult *gapStoryFetchResult;
@@ -219,29 +228,11 @@ static gdouble  p_attribute__at_step(gint32 frame_step /* current frame (since s
 static void     p_select_section_by_name(GapStoryRenderVidHandle *vidhand
                   , const char *section_name);
 
-static char *   p_fetch_framename   (GapStoryRenderFrameRangeElem *frn_list
+static char*    p_fetch_framename   (GapStoryRenderFrameRangeElem *frn_list
                             , gint32 master_frame_nr                   /* starts at 1 */
                             , gint32 track
-                            , GapStoryRenderFrameType *frn_type
-                            , char **filtermacro_file
-                            , gint32   *localframe_index  /* used only for ANIMIMAGE and videoclip, -1 for all other types */
-                            , gint32   *local_stepcount   /* nth frame within this clip, starts with 0 */
-                            , gdouble  *localframe_tween_rest /* non-integer part of the position. value < 1.0 for tween fetching */
-                            , gboolean *keep_proportions
-                            , gboolean *fit_width
-                            , gboolean *fit_height
-                            , gdouble  *red_f
-                            , gdouble  *green_f
-                            , gdouble  *blue_f
-                            , gdouble  *alpha_f
-                            , gdouble *rotate        /* output rotate in degree */
-                            , gdouble *opacity       /* output opacity 0.0 upto 1.0 */
-                            , gdouble *scale_x       /* output 0.0 upto 10.0 where 1.0 is 1:1 */
-                            , gdouble *scale_y       /* output 0.0 upto 10.0 where 1.0 is 1:1 */
-                            , gdouble *move_x        /* output -1.0 upto 1.0 where 0.0 is centered */
-                            , gdouble *move_y        /* output -1.0 upto 1.0 where 0.0 is centered */
-                            , GapStoryRenderFrameRangeElem **frn_elem_ptr  /* OUT pointer to the relevant framerange element */
-                           );
+                            , GapStbFetchData *gfd        /* out: result structure of the fetch */
+                            );
 static void   p_calculate_frames_to_handle(GapStoryRenderFrameRangeElem *frn_elem);
 
 static GapStoryRenderFrameRangeElem *  p_new_framerange_element(
@@ -381,10 +372,36 @@ static gint32     p_exec_filtermacro(gint32 image_id
                          , gint32 total_steps
                          , gint accelerationCharacteristic
                          );
-static gboolean p_transform_operate_on_full_layer(GapStoryCalcAttr *calculated
+static gboolean   p_transform_operate_on_full_layer(GapStoryCalcAttr *calculated
                          , gint32 comp_image_id, gint32 tmp_image_id
                          , GapStoryRenderFrameRangeElem *frn_elem
                          );
+static void       p_transform_rotate_layer_at(gint32 image_id, gint32 layer_id, gdouble rotate
+                         , gint image_offset_x, gint image_offset_y);
+static gint32     p_transform_with_movepath_processing( gint32 comp_image_id
+                         , gint32 tmp_image_id
+                         , gint32 layer_id
+                         , gboolean keep_proportions
+                         , gboolean fit_width
+                         , gboolean fit_height
+                         , gdouble rotate     /* rotation in degree */
+                         , gdouble opacity    /* 0.0 upto 1.0 */
+                         , gdouble scale_x    /* 0.0 upto 10.0 where 1.0 = 1:1 */
+                         , gdouble scale_y    /* 0.0 upto 10.0 where 1.0 = 1:1 */
+                         , gdouble move_x     /* -1.0 upto +1.0 where 0 = no move, -1 is left outside */
+                         , gdouble move_y     /* -1.0 upto +1.0 where 0 = no move, -1 is top outside */
+                         , GapStoryRenderFrameRangeElem *frn_elem
+                         , GapStoryRenderVidHandle *vidhand
+                         , gint32 local_stepcount
+                         , const char *movepath_file_xml
+                         , gdouble movepath_framePhase
+                         );
+static void       p_transform_postprocessing(gint32 new_layer_id
+                         , GapStoryRenderFrameRangeElem *frn_elem
+                         , GapStoryRenderVidHandle *vidhand
+                         , gdouble opacity
+                         , gint32 local_stepcount
+                         );
 static gint32     p_transform_and_add_layer( gint32 comp_image_id
                          , gint32 tmp_image_id
                          , gint32 layer_id
@@ -397,11 +414,13 @@ static gint32     p_transform_and_add_layer( gint32 comp_image_id
                          , gdouble scale_y    /* 0.0 upto 10.0 where 1.0 = 1:1 */
                          , gdouble move_x     /* -1.0 upto +1.0 where 0 = no move, -1 is left outside */
                          , gdouble move_y     /* -1.0 upto +1.0 where 0 = no move, -1 is top outside */
-                         , char *filtermacro_file
+                         , const char *filtermacro_file
                          , gint32 flip_request
                          , GapStoryRenderFrameRangeElem *frn_elem
                          , GapStoryRenderVidHandle *vidhand
                          , gint32 local_stepcount
+                         , const char *movepath_file_xml
+                         , gdouble movepath_framePhase
                          );
 static gint32     p_create_unicolor_image(gint32 *layer_id, gint32 width , gint32 height
                        , gdouble r_f, gdouble g_f, gdouble b_f, gdouble a_f);
@@ -753,6 +772,12 @@ gap_story_render_debug_print_frame_elem(GapStoryRenderFrameRangeElem *frn_elem,
       printf("  [%d] move_y_dur           : %d\n", (int)l_idx, (int)frn_elem->move_y_dur );
       printf("  [%d] move_y_accel         : %d\n", (int)l_idx, (int)frn_elem->move_y_accel );
       printf("  [%d] move_y_frames_done   : %d\n", (int)l_idx, (int)frn_elem->move_y_frames_done );
+
+      printf("  [%d] movepath_from        : %f\n", (int)l_idx, (float)frn_elem->movepath_from );
+      printf("  [%d] movepath_to          : %f\n", (int)l_idx, (float)frn_elem->movepath_to );
+      printf("  [%d] movepath_dur         : %d\n", (int)l_idx, (int)frn_elem->movepath_dur );
+      printf("  [%d] movepath_frames_done : %d\n", (int)l_idx, (int)frn_elem->movepath_frames_done );
+
   }
 }  /* end gap_story_render_debug_print_frame_elem */
 
@@ -1301,44 +1326,32 @@ p_select_section_by_name(GapStoryRenderVidHandle *vidhand, const char *section_n
 }  /* end p_select_section_by_name */
 
 
+
 /* ----------------------------------------------------
  * p_fetch_framename
  * ----------------------------------------------------
- * fetch framename for a given master_frame_nr in the given video track
+ * fetch frame access (framename or videofilename framenumber)
+ * and transition values relevant for a given master_frame_nr in the given video track
  * within a storyboard framerange list.
  * (simple animations without a storyboard file
  *  are represented by a short storyboard framerange list that has
  *  just one element entry at track 1).
  *
- * output gduoble attribute values (opacity, scale, move) for the frame
+ * output is written to the specified GapStbFetchData structure
+ * that contains gdouble attribute values (opacity, scale, move) for the frame
  * at track and master_frame_nr position.
  *
- * return the name of the frame (that is to be displayed at position master_frame_nr).
- * return NULL if there is no frame at desired track and master_frame_nr
+ * Note gfd->framename can refere to the relevant frame image that is one of a series of frames
+ * (imagefiles) on disc. But it may also refere to a videofilename or to a multilayer image.
+ * (in this cases localframe_index referes to the relevant framenumber in the videofile
+ * or to the layerstack position in the multilayer image)
+ * gfd->framename is set to NULL if there is no frame at desired track and master_frame_nr
  */
 static char *
 p_fetch_framename(GapStoryRenderFrameRangeElem *frn_list
                  , gint32 master_frame_nr      /* starts at 1 */
                  , gint32 track
-                 , GapStoryRenderFrameType *frn_type
-                 , char **filtermacro_file
-                 , gint32   *localframe_index  /* starts at 1, used for ANIMIMAGE and VIDEOFILES, -1 for all other types */
-                 , gint32   *local_stepcount   /* nth frame within this clip, starts with 0 */
-                 , gdouble  *localframe_tween_rest /* non-integer part of the position. value < 1.0 for tween fetching */
-                 , gboolean *keep_proportions
-                 , gboolean *fit_width
-                 , gboolean *fit_height
-                 , gdouble  *red_f
-                 , gdouble  *green_f
-                 , gdouble  *blue_f
-                 , gdouble  *alpha_f
-                 , gdouble *rotate        /* output rotate in degree */
-                 , gdouble *opacity       /* output opacity 0.0 upto 1.0 */
-                 , gdouble *scale_x       /* output 0.0 upto 10.0 where 1.0 is 1:1 */
-                 , gdouble *scale_y       /* output 0.0 upto 10.0 where 1.0 is 1:1 */
-                 , gdouble *move_x        /* output -1.0 upto 1.0 where 0.0 is centered */
-                 , gdouble *move_y        /* output -1.0 upto 1.0 where 0.0 is centered */
-                 , GapStoryRenderFrameRangeElem **frn_elem_ptr  /* OUT pointer to the relevant framerange element */
+                 , GapStbFetchData *gfd        /* out: result structure of the fetch */
                  )
 {
   GapStoryRenderFrameRangeElem *frn_elem;
@@ -1353,25 +1366,27 @@ p_fetch_framename(GapStoryRenderFrameRangeElem *frn_list
   l_framename = NULL;
 
   /* default attributes (used if storyboard does not define settings) */
-  *rotate  = 0.0;
-  *opacity = 1.0;
-  *scale_x = 1.0;
-  *scale_y = 1.0;
-  *move_x  = 0.0;
-  *move_y  = 0.0;
-  *localframe_index = -1;
-  *local_stepcount = 0;
-  *localframe_tween_rest = 0.0;
-  *frn_type = GAP_FRN_SILENCE;
-  *keep_proportions = FALSE;
-  *fit_width        = TRUE;
-  *fit_height       = TRUE;
-  *red_f            = 0.0;
-  *green_f          = 0.0;
-  *blue_f           = 0.0;
-  *alpha_f          = 1.0;
-  *filtermacro_file = NULL;
-  *frn_elem_ptr      = NULL;
+  gfd->rotate  = 0.0;
+  gfd->opacity = 1.0;
+  gfd->scale_x = 1.0;
+  gfd->scale_y = 1.0;
+  gfd->move_x  = 0.0;
+  gfd->move_y  = 0.0;
+  gfd->localframe_index = -1;
+  gfd->local_stepcount = 0;
+  gfd->localframe_tween_rest = 0.0;
+  gfd->frn_type = GAP_FRN_SILENCE;
+  gfd->keep_proportions = FALSE;
+  gfd->fit_width        = TRUE;
+  gfd->fit_height       = TRUE;
+  gfd->red_f            = 0.0;
+  gfd->green_f          = 0.0;
+  gfd->blue_f           = 0.0;
+  gfd->alpha_f          = 1.0;
+  gfd->trak_filtermacro_file = NULL;
+  gfd->frn_elem              = NULL;
+  gfd->movepath_file_xml     = NULL;
+  gfd->movepath_framePhase   = 0;
 
   l_found_at_idx=0;
   for (frn_elem = frn_list; frn_elem != NULL; frn_elem = (GapStoryRenderFrameRangeElem *)frn_elem->next)
@@ -1399,20 +1414,20 @@ p_fetch_framename(GapStoryRenderFrameRangeElem *frn_list
 
           fnrInt = fnr;  /* truncate to integer */
 
-          *localframe_tween_rest = fnr - fnrInt;
+          gfd->localframe_tween_rest = fnr - fnrInt;
 
           if(gap_debug)
           {
             printf("fnr:%.4f, fnrInt:%d localframe_tween_rest:%.4f\n"
                      ,(float)fnr
                      ,(int)fnrInt
-                     ,(float)*localframe_tween_rest
+                     ,(float)gfd->localframe_tween_rest
                      );
           }
         }
 
-        *local_stepcount = master_frame_nr - l_frame_group_count;
-        *local_stepcount -= 1;
+        gfd->local_stepcount = master_frame_nr - l_frame_group_count;
+        gfd->local_stepcount -= 1;
 
         switch(frn_elem->frn_type)
         {
@@ -1425,12 +1440,12 @@ p_fetch_framename(GapStoryRenderFrameRangeElem *frn_list
             break;
           case GAP_FRN_ANIMIMAGE:
             l_framename = g_strdup(frn_elem->basename);   /* use 1:1 basename for ainimated single images */
-            *localframe_index = l_fnr;                    /* local frame number is index in the layerstack */
+            gfd->localframe_index = l_fnr;                    /* local frame number is index in the layerstack */
             break;
           case GAP_FRN_MOVIE:
             /* video file frame numners start at 1 */
             l_framename = g_strdup(frn_elem->basename);   /* use 1:1 basename for videofiles */
-            *localframe_index = l_fnr;                    /* local frame number is the wanted video frame number */
+            gfd->localframe_index = l_fnr;                    /* local frame number is the wanted video frame number */
             break;
           case GAP_FRN_FRAMES:
             l_framename = gap_lib_alloc_fname(frn_elem->basename
@@ -1441,25 +1456,25 @@ p_fetch_framename(GapStoryRenderFrameRangeElem *frn_list
           case GAP_FRN_SECTION:
             /* frame numners in storyboard sections start at 1 */
             l_framename = g_strdup(frn_elem->basename);   /* section_name 1:1 for STB sections */
-            *localframe_index = l_fnr;                    /* local frame number is the wanted video frame number */
+            gfd->localframe_index = l_fnr;                    /* local frame number is the wanted video frame number */
             break;
         }
 
          /* return values for current fixed attribute settings
           */
-         *frn_type         = frn_elem->frn_type;
-         *keep_proportions = frn_elem->keep_proportions;
-         *fit_width        = frn_elem->fit_width;
-         *fit_height       = frn_elem->fit_height;
-         *red_f            = frn_elem->red_f;
-         *green_f          = frn_elem->green_f;
-         *blue_f           = frn_elem->blue_f;
-         *alpha_f          = frn_elem->alpha_f;
-         *filtermacro_file = frn_elem->filtermacro_file;
+         gfd->frn_type              = frn_elem->frn_type;
+         gfd->keep_proportions      = frn_elem->keep_proportions;
+         gfd->fit_width             = frn_elem->fit_width;
+         gfd->fit_height            = frn_elem->fit_height;
+         gfd->red_f                 = frn_elem->red_f;
+         gfd->green_f               = frn_elem->green_f;
+         gfd->blue_f                = frn_elem->blue_f;
+         gfd->alpha_f               = frn_elem->alpha_f;
+         gfd->trak_filtermacro_file = frn_elem->filtermacro_file;
 
          frn_elem->last_master_frame_access = master_frame_nr;
 
-         *frn_elem_ptr     = frn_elem;  /* deliver pointer to the current frn_elem */
+         gfd->frn_elem = frn_elem;  /* deliver pointer to the current frn_elem */
 
 
          /* calculate effect attributes for the current step
@@ -1468,48 +1483,89 @@ p_fetch_framename(GapStoryRenderFrameRangeElem *frn_list
          l_step = (master_frame_nr - 1) - l_frame_group_count;
 
 
-         *rotate = p_attribute_query_at_step(l_step
+         gfd->rotate = p_attribute_query_at_step(l_step
                                     , frn_elem->rotate_from
                                     , frn_elem->rotate_to
                                     , frn_elem->rotate_dur
                                     , frn_elem->rotate_frames_done
                                     , frn_elem->rotate_accel
                                     );
-         *opacity = p_attribute_query_at_step(l_step
+         gfd->opacity = p_attribute_query_at_step(l_step
                                     , frn_elem->opacity_from
                                     , frn_elem->opacity_to
                                     , frn_elem->opacity_dur
                                     , frn_elem->opacity_frames_done
                                     , frn_elem->opacity_accel
                                     );
-         *scale_x = p_attribute_query_at_step(l_step
+         gfd->scale_x = p_attribute_query_at_step(l_step
                                     , frn_elem->scale_x_from
                                     , frn_elem->scale_x_to
                                     , frn_elem->scale_x_dur
                                     , frn_elem->scale_x_frames_done
                                     , frn_elem->scale_x_accel
                                     );
-         *scale_y = p_attribute_query_at_step(l_step
+         gfd->scale_y = p_attribute_query_at_step(l_step
                                     , frn_elem->scale_y_from
                                     , frn_elem->scale_y_to
                                     , frn_elem->scale_y_dur
                                     , frn_elem->scale_y_frames_done
                                     , frn_elem->scale_y_accel
                                     );
-         *move_x  = p_attribute_query_at_step(l_step
+         gfd->move_x  = p_attribute_query_at_step(l_step
                                     , frn_elem->move_x_from
                                     , frn_elem->move_x_to
                                     , frn_elem->move_x_dur
                                     , frn_elem->move_x_frames_done
                                     , frn_elem->move_x_accel
                                     );
-         *move_y  = p_attribute_query_at_step(l_step
+         gfd->move_y  = p_attribute_query_at_step(l_step
                                     , frn_elem->move_y_from
                                     , frn_elem->move_y_to
                                     , frn_elem->move_y_dur
                                     , frn_elem->move_y_frames_done
                                     , frn_elem->move_y_accel
                                     );
+
+         /* movepath transition handling */                                   
+         if(frn_elem->movepath_file_xml != NULL)
+         {
+           gint32  l_steps_since_transition_start;
+           gdouble phase;
+
+           l_steps_since_transition_start = frn_elem->movepath_frames_done + l_step;
+           phase = 1.0;
+           
+           
+           if (l_steps_since_transition_start < frn_elem->movepath_dur)
+           {
+             gint32 duration;
+             gint   accel;
+             
+             accel = 0;
+             duration = abs(frn_elem->movepath_to - frn_elem->movepath_from);
+             gfd->movepath_file_xml = frn_elem->movepath_file_xml;
+             phase = p_attribute_query_at_step(l_step
+                                                 , frn_elem->movepath_from
+                                                 , frn_elem->movepath_to
+                                                 , duration
+                                                 , frn_elem->movepath_frames_done
+                                                 , accel
+                                                 );
+             gfd->movepath_framePhase = rint(phase);
+           }
+           if(gap_debug)
+           {
+             printf("FETCH: l_steps_since_transition_start:%d movepath_dur:%d movepath_framePhase:%d (phase:%.4f)\n"
+               , (int)l_steps_since_transition_start
+               , (int)frn_elem->movepath_dur
+               , (int)gfd->movepath_framePhase
+               , (float)phase
+               );
+           }
+           
+         }
+
+
         break;
       }
       l_frame_group_count += l_frames_to_handle;
@@ -1519,24 +1575,32 @@ p_fetch_framename(GapStoryRenderFrameRangeElem *frn_list
 
   if(gap_debug)
   {
-    printf("p_fetch_framename: track:%d master_frame_nr:%d framename:%s: found_at_idx:%d rotate:%f opa:%f scale:%f %f move:%f %f layerstack_idx:%d\n"
+    printf("p_fetch_framename: track:%d master_frame_nr:%d framename:%s: found_at_idx:%d rotate:%f opa:%f scale:%f %f move:%f %f layerstack_idx:%d"
        , (int)track
        , (int)master_frame_nr
        , l_framename
        , (int)l_found_at_idx
-       , (float)*rotate
-       , (float)*opacity
-       , (float)*scale_x
-       , (float)*scale_y
-       , (float)*move_x
-       , (float)*move_y
-       , (int)*localframe_index
+       , (float)gfd->rotate
+       , (float)gfd->opacity
+       , (float)gfd->scale_x
+       , (float)gfd->scale_y
+       , (float)gfd->move_x
+       , (float)gfd->move_y
+       , (int)gfd->localframe_index
        );
+     if(gfd->movepath_file_xml != NULL)
+     {
+       printf(" movepath_framePhase:%f XML:%s"
+         ,(float)gfd->movepath_framePhase
+         ,gfd->movepath_file_xml
+         );
+     }
+     printf("\n");
   }
 
-  return l_framename;
-}       /* end p_fetch_framename */
+  return (l_framename);
 
+}       /* end p_fetch_framename */
 
 
 /* ---------------------------------
@@ -1698,7 +1762,17 @@ p_new_framerange_element(GapStoryRenderFrameType  frn_type
   frn_elem->seltrack           = seltrack;
   frn_elem->exact_seek         = exact_seek;
   frn_elem->delace             = delace;
+  
+  frn_elem->movepath_from      = 0.0;
+  frn_elem->movepath_to        = 0.0;
+  frn_elem->movepath_frames_done = 0;
+  frn_elem->movepath_dur         = 0;
+  frn_elem->movepath_file_xml    = NULL;
+
+  
   frn_elem->next               = NULL;
+  
+  
 
 
   if(ext)
@@ -1850,6 +1924,7 @@ p_step_all_vtrack_attributes(gint32 track
   vtarr->attr[track].scale_y_frames_done += frames_to_handle;
   vtarr->attr[track].move_x_frames_done += frames_to_handle;
   vtarr->attr[track].move_y_frames_done += frames_to_handle;
+  vtarr->attr[track].movepath_frames_done += frames_to_handle;
 
 }  /* end p_step_all_vtrack_attributes */
 
@@ -1930,6 +2005,18 @@ p_set_vtrack_attributes(GapStoryRenderFrameRangeElem *frn_elem
   frn_elem->move_y_frames_done   = vtarr->attr[track].move_y_frames_done;
 
 
+  frn_elem->movepath_from        = vtarr->attr[track].movepath_from;
+  frn_elem->movepath_to          = vtarr->attr[track].movepath_to;
+  frn_elem->movepath_dur         = vtarr->attr[track].movepath_dur;
+  /// frn_elem->movepath_accel       = vtarr->attr[track].movepath_accel; // (?)
+  frn_elem->movepath_frames_done = vtarr->attr[track].movepath_frames_done;
+  frn_elem->movepath_file_xml    = NULL;
+  if ((vtarr->attr[track].movepath_file_xml != NULL)
+  &&  (vtarr->attr[track].movepath_frames_done <= vtarr->attr[track].movepath_dur))
+  {
+    frn_elem->movepath_file_xml    = g_strdup(vtarr->attr[track].movepath_file_xml);
+  }
+
   /* advance mask_framecount */
   vtarr->attr[track].mask_framecount += frn_elem->frames_to_handle;
 
@@ -2181,6 +2268,12 @@ p_copy_vattr_values(gint32 src_track
   vtarr->attr[dst_track].move_y_accel        = vtarr->attr[src_track].move_y_accel;
   vtarr->attr[dst_track].move_y_frames_done  = vtarr->attr[src_track].move_y_frames_done;
 
+  vtarr->attr[dst_track].movepath_from        = vtarr->attr[src_track].movepath_from;
+  vtarr->attr[dst_track].movepath_to          = vtarr->attr[src_track].movepath_to;
+  vtarr->attr[dst_track].movepath_dur         = vtarr->attr[src_track].movepath_dur;
+  vtarr->attr[dst_track].movepath_frames_done = vtarr->attr[src_track].movepath_frames_done;
+  vtarr->attr[dst_track].movepath_file_xml    = vtarr->attr[src_track].movepath_file_xml;
+
 }  /* end p_copy_vattr_values */
 
 
@@ -2454,6 +2547,13 @@ p_clear_vattr_array(GapStoryRenderVTrackArray *vtarr)
     vtarr->attr[l_idx].move_y_dur           = 0;
     vtarr->attr[l_idx].move_y_accel         = 0;
     vtarr->attr[l_idx].move_y_frames_done   = 0;
+    
+    vtarr->attr[l_idx].movepath_from        = 0.0;
+    vtarr->attr[l_idx].movepath_to          = 0.0;
+    vtarr->attr[l_idx].movepath_dur         = 0;
+    vtarr->attr[l_idx].movepath_frames_done = 0;
+    vtarr->attr[l_idx].movepath_file_xml    = NULL;
+
   }
 }  /* end p_clear_vattr_array */
 
@@ -2790,6 +2890,33 @@ p_storyboard_analyze(GapStoryBoard *stb
                     vtarr->attr[l_track].scale_y_accel = stb_elem->att_arr_value_accel[ii];
                     vtarr->attr[l_track].scale_y_frames_done = 0;
                     break;
+                  case GAP_STB_ATT_TYPE_MOVEPATH:
+                    vtarr->attr[l_track].movepath_from   = stb_elem->att_arr_value_from[ii];
+                    vtarr->attr[l_track].movepath_to     = stb_elem->att_arr_value_to[ii];
+                    vtarr->attr[l_track].movepath_dur    = stb_elem->att_arr_value_dur[ii];
+                    vtarr->attr[l_track].movepath_frames_done = 0;
+                    /* no need to strdup for the vattr table here.
+                     * becaue we use the reference to the original data in this table
+                     * (therefore dont g_free 
+                     * the g_strdup is done later when the movepath_file_xml is
+                     * set in the individual GapStoryRenderFrameRangeElem frn_elem
+                     * (see p_set_vtrack_attributes)
+                     */
+                    vtarr->attr[l_track].movepath_file_xml  = NULL;
+                    if(stb_elem->att_movepath_file_xml != NULL)
+                    {
+                      if(stb_elem->att_movepath_file_xml[0] != '\0')
+                      {
+                        gboolean is_valid;
+                        
+                        is_valid = gap_mov_exec_check_valid_xml_paramfile(stb_elem->att_movepath_file_xml);
+                        if (is_valid)
+                        {
+                          vtarr->attr[l_track].movepath_file_xml = stb_elem->att_movepath_file_xml;
+                        }
+                      }
+                    }
+                    break;
                 }
               }
             }
@@ -3311,6 +3438,7 @@ p_free_framerange_list(GapStoryRenderFrameRangeElem * frn_list)
     if(frn_elem->basename)          { g_free(frn_elem->basename);}
     if(frn_elem->ext)               { g_free(frn_elem->ext);}
     if(frn_elem->filtermacro_file)  { g_free(frn_elem->filtermacro_file);}
+    if(frn_elem->movepath_file_xml) { g_free(frn_elem->movepath_file_xml);}
 
 #ifdef GAP_ENABLE_VIDEOAPI_SUPPORT
     if(frn_elem->gvahand)           { p_call_GVA_close(frn_elem->gvahand);}
@@ -4046,8 +4174,7 @@ p_open_video_handle_private(    gboolean ignore_audio
   && (input_mode == GAP_RNGTYPE_LAYER)
   && (imagename))
   {
-      gint32 l_from;      render_section->frn_list = frn_elem;
-
+      gint32 l_from;
       gint32 l_to;
 
       l_from = frame_from;
@@ -4697,6 +4824,429 @@ p_transform_operate_on_full_layer(GapStoryCalcAttr *calculated, gint32 comp_imag
 }  /* end p_transform_operate_on_full_layer */
 
 
+/* ----------------------------------------------------
+ * p_transform_rotate_layer_at
+ * ----------------------------------------------------
+ * rotate layer by the specified angle in degree
+ * the center of the rotation is specified in image coordinates
+ * via image_offset_x and image_offset_y.
+ */
+void
+p_transform_rotate_layer_at(gint32 image_id, gint32 layer_id, gdouble rotate
+   , gint image_offset_x, gint image_offset_y)
+{
+  gint32       l_mask_id;
+  gint32       l_center_x;
+  gint32       l_center_y;
+  gdouble      l_angle_rad;
+  gdouble      l_angle_deg;
+  gint         l_layer_offset_x;
+  gint         l_layer_offset_y;
+
+  l_angle_deg = rotate;
+  while(l_angle_deg >= 360.0)
+  {
+    l_angle_deg -=360; 
+  }
+  while(l_angle_deg <= -360.0)
+  {
+    l_angle_deg +=360; 
+  }
+  
+  if ((l_angle_deg < 0.05) && (l_angle_deg > -0.05))
+  {
+    /* ignore very small rotations */
+    return;
+  }
+  l_angle_rad = (l_angle_deg * G_PI) / 180.0;
+
+  /* check for layermask */
+  l_mask_id = gimp_layer_get_mask(layer_id);
+  if(l_mask_id >= 0)
+  {
+     /* apply the layermask
+      *   some transitions (especially rotate) can't operate proper on
+      *   layers with masks !
+      *   (tests with gimp_rotate resulted in trashed images,
+      *    even if the mask was rotated too)
+      */
+      gimp_layer_remove_mask (layer_id, GIMP_MASK_APPLY);
+  }
+
+  /* remove selection (if there is one)
+   *   if there is a selection transitions (gimp_rotate)
+   *   will create new layer and do not operate on l_cp_layer_id
+   */
+  gimp_selection_none(image_id);
+
+  gimp_drawable_offsets(layer_id, &l_layer_offset_x, &l_layer_offset_y );
+
+  /* calculate rotation center in layer coordinates */
+  l_center_x  = image_offset_x + l_layer_offset_x;
+  l_center_y  = image_offset_y + l_layer_offset_y;
+
+  if(gap_debug)
+  {
+    printf("p_transform_rotate_layer_at: called at layer_id:%d l_angle_deg:%.4f\n"
+           "  image_offset_x:%d image_offset_y:%d\n"
+           "  l_layer_offset_x:%d l_layer_offset_y:%d  l_center_x:%d l_center_y:%d"
+      , (int)layer_id
+      , (float)l_angle_deg
+      , (int)image_offset_x
+      , (int)image_offset_y
+      , (int)l_layer_offset_x
+      , (int)l_layer_offset_y
+      , (int)l_center_x
+      , (int)l_center_y
+      );
+  }
+
+
+  /* perform rotation of the layer (rotation also changes size as needed) */
+  gimp_drawable_transform_rotate_default(layer_id
+                                        , l_angle_rad
+                                        , FALSE            /* auto_center */
+                                        , l_center_x
+                                        , l_center_y
+                                        , TRUE             /* interpolation */
+                                        , FALSE            /* clip_results */
+                                        );
+
+
+}  /* end p_transform_rotate_layer_at */
+
+
+
+
+
+
+/* ----------------------------------------------------
+ * p_transform_with_movepath_processing
+ * ----------------------------------------------------
+ * - copy the layer (layer_id) from tmp_image to the composite image
+ *   and apply movepath processing for complex transformations on the copied layer.
+ * - the source Layer (layer_id) must be part of tmp_image_id
+ * - additionally to the movepath transitions
+ *   also set opacity, move, scale and rotate the result layer of movepath processing
+ *   according to video attributes
+ * - optional apply transparency in case mask is attached (anchored to clip or master)
+ *   In case mask is anchored to clip, it is applied BEFORE movepath transformations
+ *
+ * return the new created layer id in the comp_image_id
+ *   (that is already added to the composte image on top of layerstack
+ *    and is already transformed 
+ *    -- except postprocessing for mask anchor master and opacity )
+ */
+static gint32
+p_transform_with_movepath_processing( gint32 comp_image_id
+                         , gint32 tmp_image_id
+                         , gint32 layer_id
+                         , gboolean keep_proportions
+                         , gboolean fit_width
+                         , gboolean fit_height
+                         , gdouble rotate     /* rotation in degree */
+                         , gdouble opacity    /* 0.0 upto 1.0 */
+                         , gdouble scale_x    /* 0.0 upto 10.0 where 1.0 = 1:1 */
+                         , gdouble scale_y    /* 0.0 upto 10.0 where 1.0 = 1:1 */
+                         , gdouble move_x     /* -1.0 upto +1.0 where 0 = no move, -1 is left outside */
+                         , gdouble move_y     /* -1.0 upto +1.0 where 0 = no move, -1 is top outside */
+                         , GapStoryRenderFrameRangeElem *frn_elem
+                         , GapStoryRenderVidHandle *vidhand
+                         , gint32 local_stepcount
+                         , const char *movepath_file_xml
+                         , gdouble movepath_framePhase
+                         )
+{
+  gint32 vid_width;
+  gint32 vid_height;
+  gint32 l_new_layer_id;
+  gint32 l_movepath_layer_id;
+  gint32 l_tmp_movpath_image_id;
+  gint32 l_empty_layer_id;
+  
+  gint   l_base_offset_x;
+  gint   l_base_offset_y;
+  gint   l_mov_dx;
+  gint   l_mov_dy;
+  gint   result_width;
+  gint   result_height;
+    
+  GapStoryCalcAttr  calculate_attributes;
+  GapStoryCalcAttr  *calculated;
+
+
+
+  if(gap_debug)
+  {
+    printf("\n--\np_transform_with_movepath_processing: called at layer_id: %d, tmp_image_id:%d comp_image_id:%d\n"
+      , (int)layer_id
+      , (int)tmp_image_id
+      , (int)comp_image_id
+      );
+    if(frn_elem != NULL) 
+    {
+      gap_story_render_debug_print_frame_elem(frn_elem, -77); 
+    }
+  }
+
+  l_tmp_movpath_image_id = -1;
+  
+
+  vid_width = gimp_image_width(comp_image_id);
+  vid_height = gimp_image_height(comp_image_id);
+
+  if(frn_elem != NULL)
+  {
+    /* process mask before the movepath transitions
+     * (but only in case when not achored to master, e.g anchored to clip)
+     */
+    if((frn_elem->mask_name != NULL)
+    && (frn_elem->mask_anchor != GAP_MSK_ANCHOR_MASTER))
+    {
+      /* fetch and add mask at calculated scaled size of layer_id */
+      p_fetch_and_add_layermask(vidhand
+                , frn_elem
+                , local_stepcount
+                , tmp_image_id
+                , layer_id
+                , frn_elem->mask_anchor
+                );
+      /* apply the mask (necessary because some of the following transformations on this layer
+       * would ignore the layer mask)
+       */
+      gimp_layer_remove_mask(layer_id, GIMP_MASK_APPLY);
+    }
+  }
+
+
+  /* create a temporary image in composite video size with a transparent layer
+   * (this image acts as frame for the movepath processing,
+   * and the layer_id acts as the moving object -- to be transformed --)
+   */
+  l_tmp_movpath_image_id = gimp_image_new(vid_width, vid_height, GIMP_RGB);
+  gimp_image_undo_disable(l_tmp_movpath_image_id);
+  
+  
+  l_empty_layer_id = gimp_layer_new(l_tmp_movpath_image_id, "empty",
+                        vid_width, vid_height,
+                        GIMP_RGBA_IMAGE,
+                        100.0,     /* Opacity */
+                        GIMP_NORMAL_MODE);
+  gimp_image_add_layer(l_tmp_movpath_image_id, l_empty_layer_id, 0);
+  gap_layer_clear_to_color(l_empty_layer_id
+                          , 0.0, 0.0, 0.0, 0.0  /* r,g,b,a (black full transparent) */
+                          );
+
+  gap_mov_exec_move_path_singleframe_directcall(l_tmp_movpath_image_id
+     , layer_id
+     , keep_proportions
+     , fit_width
+     , fit_height
+     , movepath_framePhase
+     , movepath_file_xml
+     );
+
+  l_movepath_layer_id = gap_image_merge_visible_layers(l_tmp_movpath_image_id
+                         , GIMP_EXPAND_AS_NECESSARY
+                         );
+
+  /* at this point the complex movepath transformation is done
+   * and the result is available as layer l_movepath_layer_id
+   * note that fit size flags were already handled in the movepath transformation.
+   *
+   * further processing continues with the result of movepath processing
+   * (the image l_tmp_movpath_image_id) 
+   * note that the resulting layer (l_movepath_layer_id) may be oversized,
+   * e.g may overlap image boudaries due to the movepath transformations
+   * (when the clip to image flag was not active in movepath processing)
+   */
+  if(gap_debug)
+  {
+    printf("p_transform_and_add_layer: MOVEPATH DONE at layer_id: %d (l_movepath_layer_id:%d), tmp_image_id:%d\n"
+      , (int)layer_id
+      , (int)l_movepath_layer_id
+      , (int)tmp_image_id
+      );
+  }
+
+  /* findout the offsets of the  layer within the Image */
+  gimp_drawable_offsets(l_movepath_layer_id, &l_base_offset_x, &l_base_offset_y );
+  
+  /* handle movement */
+  l_mov_dx = rint((gdouble)vid_width * move_x);
+  l_mov_dy = rint((gdouble)vid_height * move_y);
+  if((l_mov_dx != 0) || (l_mov_dy != 0))
+  {
+    gimp_layer_set_offsets(l_movepath_layer_id
+                         , l_base_offset_x + l_mov_dx
+                         , l_base_offset_y + l_mov_dy
+                         );
+  }
+
+  /* handle rotation where the center of the rotation
+   * is center of the frame image + movment offset (l_mov_dx / l_mov_dy)
+   */
+  p_transform_rotate_layer_at(l_tmp_movpath_image_id
+                            , l_movepath_layer_id
+                            , rotate
+                            , (vid_width / 2.0) + l_mov_dx
+                            , (vid_height / 2.0) + l_mov_dy
+                            );
+
+  /* handle scaling */
+  result_width  = MAX(1, rint(((gdouble)vid_width  * scale_x)));
+  result_height = MAX(1, rint(((gdouble)vid_height * scale_y)));
+  
+  if((result_width != vid_width)
+  || (result_height != vid_height))
+  {
+    gint offs_x;
+    gint offs_y;
+
+    if(gap_debug)
+    {
+      printf("DEBUG: p_transform_with_movepath_processing scaling tmp image from %dx%d to %dx%d\n"
+                            , (int)gimp_image_width(l_tmp_movpath_image_id)
+                            , (int)gimp_image_height(l_tmp_movpath_image_id)
+                            , (int)result_width
+                            , (int)result_width
+                            );
+
+    }
+
+
+    if((result_width >= vid_width)
+    && (result_height >= vid_height))
+    {
+      /* handle enlarge image in both dimensions scenario */
+      gimp_image_scale(l_tmp_movpath_image_id, result_width, result_height);
+      offs_x = rint((result_width - vid_width) / 2.0);
+      offs_y = rint((result_height - vid_height) / 2.0);
+      gimp_image_crop(l_tmp_movpath_image_id, vid_width, vid_height, offs_x, offs_y);
+    }
+    else if ((result_width <= vid_width)
+    && (result_height <= vid_height))
+    {
+      /* handle shrink image in both dimensions scenario */
+      gimp_image_scale(l_tmp_movpath_image_id, result_width, result_height);
+      offs_x = rint((vid_width - result_width) / 2.0);
+      offs_y = rint((vid_height - result_height) / 2.0);
+
+      /* resize image canvas back to coposite videoframe size */
+      gimp_image_resize(l_tmp_movpath_image_id, vid_width, vid_height, offs_x, offs_y);
+    }
+    else if ((result_width > vid_width)
+    && (result_height <= vid_height))
+    {
+      /* handle enlarge width but shrink height scenario */
+      /* 1. enlarge width, keep same image height */
+      gimp_image_scale(l_tmp_movpath_image_id, result_width, vid_height);
+      offs_x = rint((result_width - vid_width) / 2.0);
+      offs_y = 0;
+      gimp_image_crop(l_tmp_movpath_image_id, vid_width, vid_height, offs_x, offs_y);
+
+      /* 2. shrink height, keep same image width */
+      gimp_image_scale(l_tmp_movpath_image_id, vid_width, result_height);
+      offs_x = 0;
+      offs_y = rint((vid_height - result_height) / 2.0);
+
+      /* resize image canvas back to coposite videoframe size */
+      gimp_image_resize(l_tmp_movpath_image_id, vid_width, vid_height, offs_x, offs_y);
+    
+    }
+    else if ((result_width < vid_width)
+    && (result_height >= vid_height))
+    {
+      /* handle enlarge height but shrink width scenario */
+      /* 1. enlarge height, keep same image width */
+      gimp_image_scale(l_tmp_movpath_image_id, vid_width, result_height);
+      offs_x = 0;
+      offs_y = rint((result_height - vid_height) / 2.0);
+      gimp_image_crop(l_tmp_movpath_image_id, vid_width, vid_height, offs_x, offs_y);
+
+      /* 2. shrink width, keep same image height */
+      gimp_image_scale(l_tmp_movpath_image_id, result_width, vid_height);
+      offs_x = rint((vid_width - result_width) / 2.0);
+      offs_y = 0;
+
+      /* resize image canvas back to coposite videoframe size */
+      gimp_image_resize(l_tmp_movpath_image_id, vid_width, vid_height, offs_x, offs_y);
+    
+    }
+
+
+  
+  }
+
+  /* trim resulting layer to imagesize after all transformations
+   * (move, rotate, and scale) are processed
+   * This includes both extesion to image size and crop parts outside boundaries.
+   */
+  gimp_layer_resize_to_image_size(l_movepath_layer_id);
+
+  /* make 1:1 copy of the l_movepath_layer_id (that has already same size as
+   * the composite image and is already transformed)
+   * and add to the composite image (at top) 
+   */
+  l_new_layer_id = gimp_layer_new_from_drawable(l_movepath_layer_id, comp_image_id);
+  if(l_new_layer_id >= 0)
+  {
+    if(! gimp_drawable_has_alpha(l_new_layer_id))
+    {
+       /* have to add alpha channel */
+       gimp_layer_add_alpha(l_new_layer_id);
+    }
+    gimp_image_add_layer(comp_image_id, l_new_layer_id, 0);
+    gimp_layer_set_offsets(l_new_layer_id, 0, 0);
+  }
+  
+//p_debug_dup_image(l_tmp_movpath_image_id);
+
+
+  if(l_tmp_movpath_image_id >= 0)
+  {
+    gap_image_delete_immediate(l_tmp_movpath_image_id);
+  }
+
+  return(l_new_layer_id);
+  
+}   /* end p_transform_with_movepath_processing */
+
+
+/* ----------------------------------
+ * p_transform_postprocessing
+ * ----------------------------------
+ * perform the final processing steps on the transformed
+ * layer. This includes applying mask (but only in case mask is anchored to master)
+ * and setting the opacity.
+ */
+static void
+p_transform_postprocessing(gint32 new_layer_id
+  , GapStoryRenderFrameRangeElem *frn_elem
+  , GapStoryRenderVidHandle *vidhand
+  , gdouble opacity
+  , gint32 local_stepcount
+  )
+{
+
+  if((frn_elem->mask_name != NULL)
+  && (frn_elem->mask_anchor == GAP_MSK_ANCHOR_MASTER))
+  {
+     p_fetch_and_add_layermask(vidhand
+                  , frn_elem
+                  , local_stepcount
+                  , gimp_drawable_get_image(new_layer_id)
+                  , new_layer_id
+                  , frn_elem->mask_anchor
+                  );
+
+  }
+
+  gimp_layer_set_opacity(new_layer_id, opacity);
+
+}  /* end p_transform_postprocessing */
+
+
 
 /* ----------------------------------------------------
  * p_transform_and_add_layer
@@ -4723,11 +5273,13 @@ p_transform_and_add_layer( gint32 comp_image_id
                          , gdouble scale_y    /* 0.0 upto 10.0 where 1.0 = 1:1 */
                          , gdouble move_x     /* -1.0 upto +1.0 where 0 = no move, -1 is left outside */
                          , gdouble move_y     /* -1.0 upto +1.0 where 0 = no move, -1 is top outside */
-                         , char *filtermacro_file
+                         , const char *filtermacro_file
                          , gint32 flip_request
                          , GapStoryRenderFrameRangeElem *frn_elem
                          , GapStoryRenderVidHandle *vidhand
                          , gint32 local_stepcount
+                         , const char *movepath_file_xml
+                         , gdouble movepath_framePhase
                          )
 {
   gint32 vid_width;
@@ -4735,10 +5287,14 @@ p_transform_and_add_layer( gint32 comp_image_id
   gint32 l_new_layer_id;
   gint32 l_fsel_layer_id;
   gint32 l_fmac_layer_id;
+  gint32 l_movepath_layer_id;
+  gint32 l_tmp_image_id;
+    
   GapStoryCalcAttr  calculate_attributes;
   GapStoryCalcAttr  *calculated;
 
   static gint32 funcId = -1;
+  static gint32 funcIdPath = -1;
   static gint32 funcIdFull = -1;
   static gint32 funcIdScale = -1;
   static gint32 funcIdRotate = -1;
@@ -4746,6 +5302,7 @@ p_transform_and_add_layer( gint32 comp_image_id
   static gint32 funcIdClipScale = -1;
   
   GAP_TIMM_GET_FUNCTION_ID(funcId,          "p_transform_and_add_layer");
+  GAP_TIMM_GET_FUNCTION_ID(funcIdPath,      "p_transform_and_add_layer.PathFullsize");
   GAP_TIMM_GET_FUNCTION_ID(funcIdFull,      "p_transform_and_add_layer.Fullsize");
   GAP_TIMM_GET_FUNCTION_ID(funcIdScale,     "p_transform_and_add_layer.ScaleFullsize");
   GAP_TIMM_GET_FUNCTION_ID(funcIdRotate,    "p_transform_and_add_layer.RotateFullsize");
@@ -4764,12 +5321,14 @@ p_transform_and_add_layer( gint32 comp_image_id
     gap_story_render_debug_print_frame_elem(frn_elem, -77);
   }
 
+  l_tmp_image_id = tmp_image_id;
+  
   /* execute input track specific  filtermacro (optional if supplied)
    * local_stepcount  (usually delivered by procedure p_fetch_framename)
    *  is used to define fmac_current_step
    * (starts at 0)
    */
-  l_fmac_layer_id = p_exec_filtermacro(tmp_image_id
+  l_fmac_layer_id = p_exec_filtermacro(l_tmp_image_id
                       , layer_id
                       , filtermacro_file
                       , frn_elem->filtermacro_file_to
@@ -4780,283 +5339,353 @@ p_transform_and_add_layer( gint32 comp_image_id
 
   if(gap_debug)
   {
-    printf("p_transform_and_add_layer: FILTERMACRO DONE at layer_id: %d (l_fmac_layer_id:%d), tmp_image_id:%d\n"
+    printf("p_transform_and_add_layer: FILTERMACRO DONE at layer_id: %d (l_fmac_layer_id:%d), l_tmp_image_id:%d\n"
       , (int)layer_id
       , (int)l_fmac_layer_id
-      , (int)tmp_image_id
+      , (int)l_tmp_image_id
       );
   }
 
+
+
+  calculated = &calculate_attributes;
+
   layer_id = gap_layer_flip(l_fmac_layer_id, flip_request);
 
+  /* execute complex move path transformation (if movepath xml settings present) */
+  if(movepath_file_xml != NULL)
+  {
+    GAP_TIMM_START_FUNCTION(funcIdPath);
+    
+    l_new_layer_id = p_transform_with_movepath_processing(comp_image_id
+                            , tmp_image_id
+                            , layer_id
+                            , keep_proportions
+                            , fit_width
+                            , fit_height
+                            , rotate     /* rotation in degree */
+                            , opacity    /* 0.0 upto 1.0 */
+                            , scale_x    /* 0.0 upto 10.0 where 1.0 = 1:1 */
+                            , scale_y    /* 0.0 upto 10.0 where 1.0 = 1:1 */
+                            , move_x     /* -1.0 upto +1.0 where 0 = no move, -1 is left outside */
+                            , move_y     /* -1.0 upto +1.0 where 0 = no move, -1 is top outside */
+                            , frn_elem
+                            , vidhand
+                            , local_stepcount
+                            , movepath_file_xml
+                            , movepath_framePhase
+                         );
 
-  /* expand layer to tmp image size (before applying any scaling) */
-  gimp_layer_resize_to_image_size(layer_id);
+      calculated->opacity = CLAMP(opacity * 100.0, 0.0, 100.0);
+      GAP_TIMM_STOP_FUNCTION(funcIdPath);
+  }
+  else
+  {
 
-  vid_width = gimp_image_width(comp_image_id);
-  vid_height = gimp_image_height(comp_image_id);
+    vid_width = gimp_image_width(comp_image_id);
+    vid_height = gimp_image_height(comp_image_id);
 
-  /* calculate scaling, offsets and opacity  according to current attributes */
-  gap_story_file_calculate_render_attributes(&calculate_attributes
-    , vid_width
-    , vid_height
-    , vid_width
-    , vid_height
-    , gimp_image_width(tmp_image_id)
-    , gimp_image_height(tmp_image_id)
-    , keep_proportions
-    , fit_width
-    , fit_height
-    , rotate
-    , opacity
-    , scale_x
-    , scale_y
-    , move_x
-    , move_y
-    );
+    /* expand layer to tmp image size (before applying any scaling) */
+    gimp_layer_resize_to_image_size(layer_id);
+  
+    /* calculate scaling, offsets and opacity  according to current attributes */
+    gap_story_file_calculate_render_attributes(&calculate_attributes
+      , vid_width
+      , vid_height
+      , vid_width
+      , vid_height
+      , gimp_image_width(l_tmp_image_id)
+      , gimp_image_height(l_tmp_image_id)
+      , keep_proportions
+      , fit_width
+      , fit_height
+      , rotate
+      , opacity
+      , scale_x
+      , scale_y
+      , move_x
+      , move_y
+      );
 
-  calculated = &calculate_attributes;
+    calculated = &calculate_attributes;
 
 
-  /* add a new transparent layer to composite image */
-  l_new_layer_id = gimp_layer_new(comp_image_id
+    /* add a new transparent layer to the composite image */
+    l_new_layer_id = gimp_layer_new(comp_image_id
                               , "pasted_track"
                               , vid_width
                               , vid_height
                               , GIMP_RGBA_IMAGE
                               , 0.0         /* Opacity full transparent */
                               ,GIMP_NORMAL_MODE);
-  gimp_image_add_layer(comp_image_id, l_new_layer_id, 0);
-  gap_layer_clear_to_color(l_new_layer_id, 0.0, 0.0, 0.0, 0.0);
-
-
-  if(TRUE == p_transform_operate_on_full_layer(calculated, comp_image_id, tmp_image_id, frn_elem))
-  {
-    GAP_TIMM_START_FUNCTION(funcIdFull);
+    gimp_image_add_layer(comp_image_id, l_new_layer_id, 0);
+    gap_layer_clear_to_color(l_new_layer_id, 0.0, 0.0, 0.0, 0.0);
 
-    /* operate on layer in full calculated size */
-    if(gap_debug)
-    {
-      printf("p_transform operate on FULL size layer\n");
-    }
-    /* check size and scale source layer_id to calculated size
-     */
-    if ((gimp_image_width(tmp_image_id) != calculated->width)
-    ||  (gimp_image_height(tmp_image_id) != calculated->height) )
+    if (TRUE == p_transform_operate_on_full_layer(calculated, comp_image_id, l_tmp_image_id, frn_elem))
     {
+      GAP_TIMM_START_FUNCTION(funcIdFull);
+
+      /* operate on layer in full calculated size */
+      /* ---------------------------------------- */
       if(gap_debug)
       {
-        printf("DEBUG: p_transform_and_add_layer scaling layer from %dx%d to %dx%d\n"
-                            , (int)gimp_image_width(tmp_image_id)
-                            , (int)gimp_image_height(tmp_image_id)
+        printf("p_transform operate on FULL size layer\n");
+      }
+      /* check size and scale source layer_id to calculated size
+       */
+      if ((gimp_image_width(l_tmp_image_id) != calculated->width)
+      ||  (gimp_image_height(l_tmp_image_id) != calculated->height) )
+      {
+        if(gap_debug)
+        {
+          printf("DEBUG: p_transform_and_add_layer scaling tmp image from %dx%d to %dx%d\n"
+                            , (int)gimp_image_width(l_tmp_image_id)
+                            , (int)gimp_image_height(l_tmp_image_id)
                             , (int)calculated->width
                             , (int)calculated->height
                             );
 
+        }
+        GAP_TIMM_START_FUNCTION(funcIdScale);
+        gimp_layer_scale(layer_id, calculated->width, calculated->height
+                        , FALSE  /* FALSE: centered at image TRUE: centered local on layer */
+                        );
+        GAP_TIMM_STOP_FUNCTION(funcIdScale);
       }
-      GAP_TIMM_START_FUNCTION(funcIdScale);
-      gimp_layer_scale(layer_id, calculated->width, calculated->height
-                      , FALSE  /* FALSE: centered at image TRUE: centered local on layer */
-                      );
-      GAP_TIMM_STOP_FUNCTION(funcIdScale);
-    }
 
 
 
-    if((frn_elem->mask_name != NULL)
-    && (frn_elem->mask_anchor != GAP_MSK_ANCHOR_MASTER))
-    {
-       /* fetch and add mask at calculated scaled size of layer_id */
-       p_fetch_and_add_layermask(vidhand
+      if((frn_elem->mask_name != NULL)
+      && (frn_elem->mask_anchor != GAP_MSK_ANCHOR_MASTER))
+      {
+         /* fetch and add mask at calculated scaled size of layer_id */
+         p_fetch_and_add_layermask(vidhand
                   , frn_elem
                   , local_stepcount
-                  , tmp_image_id
+                  , l_tmp_image_id
                   , layer_id
                   , frn_elem->mask_anchor
                   );
-       /* apply the mask (necessary because the following copy with this layer
-        * as source would ignore the layer mask)
-        */
-       gimp_layer_remove_mask(layer_id, GIMP_MASK_APPLY);
-
-    }
+        /* apply the mask (necessary because the following copy with this layer
+         * as source would ignore the layer mask)
+         */
+        gimp_layer_remove_mask(layer_id, GIMP_MASK_APPLY);
+      }
 
-    if((rotate  > 0.05) || (rotate < -0.05))
-    {
-      gint32       l_orig_width;
-      gint32       l_orig_height;
+      if((rotate  > 0.05) || (rotate < -0.05))
+      {
+        gint32       l_orig_width;
+        gint32       l_orig_height;
 
-      l_orig_width  = gimp_drawable_width(layer_id);
-      l_orig_height  = gimp_drawable_height(layer_id);
+        l_orig_width  = gimp_drawable_width(layer_id);
+        l_orig_height  = gimp_drawable_height(layer_id);
 
-      GAP_TIMM_START_FUNCTION(funcIdRotate);
+        GAP_TIMM_START_FUNCTION(funcIdRotate);
 
-      gap_story_transform_rotate_layer(tmp_image_id, layer_id, rotate);
+        gap_story_transform_rotate_layer(l_tmp_image_id, layer_id, rotate);
 
 
-      GAP_TIMM_STOP_FUNCTION(funcIdRotate);
+        GAP_TIMM_STOP_FUNCTION(funcIdRotate);
 
-      /* recalculate offests to compensate size changes caused by rotation */
-      calculated->x_offs = calculated->x_offs + (l_orig_width / 2.0) - (gimp_drawable_width(layer_id) / 2.0);
-      calculated->y_offs = calculated->y_offs + (l_orig_height / 2.0) - (gimp_drawable_height(layer_id) / 2.0);
-    }
+        /* recalculate offests to compensate size changes caused by rotation */
+        calculated->x_offs = calculated->x_offs + (l_orig_width / 2.0) - (gimp_drawable_width(layer_id) / 2.0);
+        calculated->y_offs = calculated->y_offs + (l_orig_height / 2.0) - (gimp_drawable_height(layer_id) / 2.0);
+      }
 
-    /* copy from tmp_image and paste to composite_image
-     * force copying of the complete layer by clearing the selection
-     */
-    gimp_selection_none(tmp_image_id);
-    gimp_edit_copy(layer_id);
-    l_fsel_layer_id = gimp_edit_paste(l_new_layer_id, FALSE);  /* FALSE paste clear selection */
+      /* copy from tmp_image and paste to composite_image
+       * force copying of the complete layer by clearing the selection
+       */
+      gimp_selection_none(l_tmp_image_id);
+      gimp_edit_copy(layer_id);
+      l_fsel_layer_id = gimp_edit_paste(l_new_layer_id, FALSE);  /* FALSE paste clear selection */
 
-    gimp_layer_set_offsets(l_fsel_layer_id
+      gimp_layer_set_offsets(l_fsel_layer_id
                         , calculated->x_offs
                         , calculated->y_offs
                         );
 
-    gimp_floating_sel_anchor(l_fsel_layer_id);
-
-    GAP_TIMM_STOP_FUNCTION(funcIdFull);
-  }
-  else
-  {
-    GAP_TIMM_START_FUNCTION(funcIdClipped);
+      gimp_floating_sel_anchor(l_fsel_layer_id);
 
-    /* operate on clipped rectangle size (rotation not handled in this case) */
-    if(gap_debug)
-    {
-      printf("p_transform operate on CLIPPED RECTANGLE\n");
-    }
-
-    if ((calculated->visible_width <= 0) || (calculated->visible_height <= 0))
-    {
-      /* nothing will be visible (width or height is 0), so we can skip copying and scaling */
-      return (l_new_layer_id);  /* that is still fully transparent */
-    }
-
-    if((frn_elem->mask_name != NULL)
-    && (frn_elem->mask_anchor != GAP_MSK_ANCHOR_MASTER))
-    {
-      /* add and apply layermask at original unscaled tmp_image size */
-      p_fetch_and_add_layermask(vidhand
-                  , frn_elem
-                  , local_stepcount
-                  , tmp_image_id
-                  , layer_id
-                  , frn_elem->mask_anchor
-                  );
-      /* apply the mask (necessary because the following copy with this layer
-       * as source would ignore the layer mask)
-       */
-      gimp_layer_remove_mask(layer_id, GIMP_MASK_APPLY);
+      GAP_TIMM_STOP_FUNCTION(funcIdFull);
     }
-
-
-
-    /* copy selected clipped source rectangle from tmp_image to composite image
-     * as floating selection attached to l_new_layer_id (visble part at source size)
-     */
+    else
     {
-      gdouble sx;
-      gdouble sy;
-      gdouble swidth;
-      gdouble sheight;
+      GAP_TIMM_START_FUNCTION(funcIdClipped);
 
-      sx = 0;
-      if (calculated->x_offs < 0)
+      /* operate on clipped rectangle size (rotation not handled in this case) */
+      /* --------------------------------------------------------------------- */
+      if(gap_debug)
       {
-        sx = (0 - calculated->x_offs) *
-            ((gdouble)gimp_image_width(tmp_image_id) / MAX((gdouble)calculated->width, 1.0));
+        printf("p_transform operate on CLIPPED RECTANGLE\n");
       }
 
-      sy = 0;
-      if (calculated->y_offs < 0)
+      if ((calculated->visible_width <= 0) || (calculated->visible_height <= 0))
       {
-        sy = (0 - calculated->y_offs) *
-            ((gdouble)gimp_image_height(tmp_image_id) / MAX((gdouble)calculated->height, 1.0));
+        /* nothing will be visible (width or height is 0), so we can skip copying and scaling */
+        return (l_new_layer_id);  /* that is still fully transparent */
       }
 
-      swidth = calculated->visible_width * gimp_image_width(tmp_image_id) / MAX(calculated->width, 1);
-      sheight = calculated->visible_height * gimp_image_height(tmp_image_id) / MAX(calculated->height, 1);
-
-      if(gap_debug)
+      if((frn_elem->mask_name != NULL)
+      && (frn_elem->mask_anchor != GAP_MSK_ANCHOR_MASTER))
       {
-        printf("p_transform source rectangle offset sx:%.4f, sy:%.4f, swidth:%.4f, sheight:%.4f\n"
-           , (float)sx
-           , (float)sy
-           , (float)swidth
-           , (float)sheight
-           );
+        /* add and apply layermask at original unscaled tmp_image size */
+        p_fetch_and_add_layermask(vidhand
+                    , frn_elem
+                    , local_stepcount
+                    , l_tmp_image_id
+                    , layer_id
+                    , frn_elem->mask_anchor
+                    );
+        /* apply the mask (necessary because the following copy with this layer
+         * as source would ignore the layer mask)
+         */
+        gimp_layer_remove_mask(layer_id, GIMP_MASK_APPLY);
       }
 
-      gimp_rect_select(tmp_image_id
+      /* copy selected clipped source rectangle from tmp_image to composite image
+       * as floating selection attached to l_new_layer_id (visble part at source size)
+       */
+      {
+        gdouble sx;
+        gdouble sy;
+        gdouble swidth;
+        gdouble sheight;
+
+        sx = 0;
+        if (calculated->x_offs < 0)
+        {
+          sx = (0 - calculated->x_offs) *
+              ((gdouble)gimp_image_width(l_tmp_image_id) / MAX((gdouble)calculated->width, 1.0));
+        }
+
+        sy = 0;
+        if (calculated->y_offs < 0)
+        {
+          sy = (0 - calculated->y_offs) *
+              ((gdouble)gimp_image_height(l_tmp_image_id) / MAX((gdouble)calculated->height, 1.0));
+        }
+
+        swidth = calculated->visible_width * gimp_image_width(l_tmp_image_id) / MAX(calculated->width, 1);
+        sheight = calculated->visible_height * gimp_image_height(l_tmp_image_id) / MAX(calculated->height, 1);
+
+        if(gap_debug)
+        {
+          printf("p_transform source rectangle offset sx:%.4f, sy:%.4f, swidth:%.4f, sheight:%.4f\n"
+             , (float)sx
+             , (float)sy
+             , (float)swidth
+             , (float)sheight
+             );
+        }
+
+        gimp_rect_select(l_tmp_image_id
                       , sx, sy
                       , swidth, sheight
                       , GIMP_CHANNEL_OP_REPLACE
                       , FALSE  /* feather flag */
                       , 0.0    /* feather_radius */
                       );
-    }
+      }
 
-    gimp_edit_copy(layer_id);
-    l_fsel_layer_id = gimp_edit_paste(l_new_layer_id, FALSE);  /* FALSE paste clear selection */
+      gimp_edit_copy(layer_id);
+      l_fsel_layer_id = gimp_edit_paste(l_new_layer_id, FALSE);  /* FALSE paste clear selection */
 
 
-    /* scale floating selection to visible target size */
-    if ((gimp_drawable_width(l_fsel_layer_id) != calculated->visible_width)
-    ||  (gimp_drawable_height(l_fsel_layer_id) != calculated->visible_height) )
-    {
-      GAP_TIMM_START_FUNCTION(funcIdClipScale);
+      /* scale floating selection to visible target size */
+      if ((gimp_drawable_width(l_fsel_layer_id) != calculated->visible_width)
+      ||  (gimp_drawable_height(l_fsel_layer_id) != calculated->visible_height) )
+      {
+        GAP_TIMM_START_FUNCTION(funcIdClipScale);
 
-      gimp_layer_scale(l_fsel_layer_id, calculated->visible_width, calculated->visible_height
+        gimp_layer_scale(l_fsel_layer_id, calculated->visible_width, calculated->visible_height
                       , FALSE  /* FALSE: centered at image TRUE: centered local on layer */
                       );
 
-      GAP_TIMM_STOP_FUNCTION(funcIdClipScale);
-    }
+        GAP_TIMM_STOP_FUNCTION(funcIdClipScale);
+      }
 
-    /* move floating selection according to target offsets
-     * (negative offsets are truncated to 0
-     * because this case is already handled by selecting only the visible clipped rectangle)
-     */
-    gimp_layer_set_offsets(l_fsel_layer_id
-                        , MAX(0, calculated->x_offs)
-                        , MAX(0, calculated->y_offs)
-                        );
+      /* move floating selection according to target offsets
+       * (negative offsets are truncated to 0
+       * because this case is already handled by selecting only the visible clipped rectangle)
+       */
+      gimp_layer_set_offsets(l_fsel_layer_id
+                          , MAX(0, calculated->x_offs)
+                          , MAX(0, calculated->y_offs)
+                          );
+
+      gimp_floating_sel_anchor(l_fsel_layer_id);
 
-    gimp_floating_sel_anchor(l_fsel_layer_id);
 
+      GAP_TIMM_STOP_FUNCTION(funcIdClipped);
 
-    GAP_TIMM_STOP_FUNCTION(funcIdClipped);
+    }    /* end processing on CLIPPED part */
 
   }
 
 
-//   if((rotate  > 0.05) || (rotate < -0.05))
-//   {
-//     gap_story_transform_rotate_layer(comp_image_id, l_new_layer_id, rotate);
-//   }
+  /* final common processing to be applied on the newly added layer */
+  p_transform_postprocessing(l_new_layer_id
+     , frn_elem
+     , vidhand
+     , calculated->opacity
+     , local_stepcount
+     );
 
 
+  GAP_TIMM_STOP_FUNCTION(funcId);
 
-  if((frn_elem->mask_name != NULL)
-  && (frn_elem->mask_anchor == GAP_MSK_ANCHOR_MASTER))
-  {
-     p_fetch_and_add_layermask(vidhand
-                  , frn_elem
-                  , local_stepcount
-                  , comp_image_id
-                  , l_new_layer_id
-                  , frn_elem->mask_anchor
-                  );
+  return(l_new_layer_id);
+}   /* end p_transform_and_add_layer */
 
-  }
 
-  gimp_layer_set_opacity(l_new_layer_id, calculated->opacity);
 
-  GAP_TIMM_STOP_FUNCTION(funcId);
+/* ---------------------------------------------------
+ * gap_story_render_transform_with_movepath_processing
+ * ---------------------------------------------------
+ * move path processing variant for render preview purpose
+ * (without mask handling)
+ */
+gint32
+gap_story_render_transform_with_movepath_processing(gint32 comp_image_id
+                         , gint32 tmp_image_id  /* must contain layer_id */
+                         , gint32 layer_id
+                         , gboolean keep_proportions
+                         , gboolean fit_width
+                         , gboolean fit_height
+                         , gdouble rotate     /* rotation in degree */
+                         , gdouble opacity    /* 0.0 upto 1.0 */
+                         , gdouble scale_x    /* 0.0 upto 10.0 where 1.0 = 1:1 */
+                         , gdouble scale_y    /* 0.0 upto 10.0 where 1.0 = 1:1 */
+                         , gdouble move_x     /* -1.0 upto +1.0 where 0 = no move, -1 is left outside */
+                         , gdouble move_y     /* -1.0 upto +1.0 where 0 = no move, -1 is top outside */
+                         , const char *movepath_file_xml
+                         , gdouble movepath_framePhase
+                         )
+{
+  gint32 l_new_layer_id;
+  
+  l_new_layer_id = p_transform_with_movepath_processing(comp_image_id
+                            , tmp_image_id
+                            , layer_id
+                            , keep_proportions
+                            , fit_width
+                            , fit_height
+                            , rotate     /* rotation in degree */
+                            , opacity    /* 0.0 upto 1.0 */
+                            , scale_x    /* 0.0 upto 10.0 where 1.0 = 1:1 */
+                            , scale_y    /* 0.0 upto 10.0 where 1.0 = 1:1 */
+                            , move_x     /* -1.0 upto +1.0 where 0 = no move, -1 is left outside */
+                            , move_y     /* -1.0 upto +1.0 where 0 = no move, -1 is top outside */
+                            , NULL       /* frn_elem */
+                            , NULL       /* vidhand */
+                            , 0          /* local_stepcount */
+                            , movepath_file_xml
+                            , movepath_framePhase
+                         );
+  return (l_new_layer_id);
+
+}  /* end gap_story_render_transform_with_movepath_processing */
 
-  return(l_new_layer_id);
-}   /* end p_transform_and_add_layer */
 
 
 /* ----------------------------------------------------
@@ -7775,10 +8404,6 @@ p_story_render_bypass_where_possible(GapStoryRenderVidHandle *vidhand
   gchar           *videofileName;
   gboolean         isByPassRenderEngine;
   gint32           l_track;
-  gdouble l_red_f;
-  gdouble l_green_f;
-  gdouble l_blue_f;
-  gdouble l_alpha_f;
 
   isByPassRenderEngine = FALSE;
   gfd = &gapStbFetchData;
@@ -7805,25 +8430,7 @@ p_story_render_bypass_where_possible(GapStoryRenderVidHandle *vidhand
     gfd->framename = p_fetch_framename(vidhand->frn_list
                  , master_frame_nr /* starts at 1 */
                  , l_track
-                 , &gfd->frn_type
-                 , &gfd->trak_filtermacro_file
-                 , &gfd->localframe_index   /* used only for ANIMIMAGE, SECTION and Videoframe Number, -1 for all other types */
-                 , &gfd->local_stepcount    /* nth frame within this clip */
-                 , &gfd->localframe_tween_rest  /* non integer part of local position (in case stepsize != 1) */
-                 , &gfd->keep_proportions
-                 , &gfd->fit_width
-                 , &gfd->fit_height
-                 , &l_red_f
-                 , &l_green_f
-                 , &l_blue_f
-                 , &l_alpha_f
-                 , &gfd->rotate        /* output rotateion in degree */
-                 , &gfd->opacity       /* output opacity 0.0 upto 1.0 */
-                 , &gfd->scale_x       /* output 0.0 upto 10.0 where 1.0 is 1:1 */
-                 , &gfd->scale_y       /* output 0.0 upto 10.0 where 1.0 is 1:1 */
-                 , &gfd->move_x        /* output -1.0 upto 1.0 where 0.0 is centered */
-                 , &gfd->move_y        /* output -1.0 upto 1.0 where 0.0 is centered */
-                 , &gfd->frn_elem      /* output selected to the relevant framerange element */
+                 , gfd
                  );
 
     if(gap_debug)
@@ -7854,6 +8461,8 @@ p_story_render_bypass_where_possible(GapStoryRenderVidHandle *vidhand
           && (gfd->frn_elem->flip_request == GAP_STB_FLIP_NONE)
           && (gfd->frn_elem->mask_name == NULL)
           && (p_isFiltermacroActive(gfd->trak_filtermacro_file) != TRUE)
+          && (gfd->movepath_file_xml == NULL)
+          && (gfd->movepath_framePhase = 0.0)
           // Fit options are not relevant when clip and master size are exact the same
           // and no scaling is done.
           //     && (gfd->fit_width)
@@ -7865,7 +8474,7 @@ p_story_render_bypass_where_possible(GapStoryRenderVidHandle *vidhand
             
             isAutoInsertActive = FALSE;
             
-            if(vidhand->master_insert_alpha_format == TRUE)
+            if(vidhand->master_insert_alpha_format != NULL)
             {
               char *alpha_imagename;
               alpha_imagename = p_get_insert_alpha_filename(gfd, vidhand);
@@ -7877,7 +8486,7 @@ p_story_render_bypass_where_possible(GapStoryRenderVidHandle *vidhand
               g_free(alpha_imagename);
             }
 
-            if((vidhand->master_insert_area_format == TRUE)
+            if((vidhand->master_insert_area_format != NULL)
             && (isAutoInsertActive != TRUE))
             {
               char *logo_imagename;
@@ -8020,11 +8629,6 @@ p_story_render_fetch_composite_image_private(GapStoryRenderVidHandle *vidhand
 
   gint32  l_track;
 
-  gdouble l_red_f;
-  gdouble l_green_f;
-  gdouble l_blue_f;
-  gdouble l_alpha_f;
-
   static gint32 funcId = -1;
   static gint32 funcIdDirect = -1;
   static gint32 funcIdDirectScale = -1;
@@ -8123,25 +8727,7 @@ p_story_render_fetch_composite_image_private(GapStoryRenderVidHandle *vidhand
     gfd->framename = p_fetch_framename(vidhand->frn_list
                  , master_frame_nr /* starts at 1 */
                  , l_track
-                 , &gfd->frn_type
-                 , &gfd->trak_filtermacro_file
-                 , &gfd->localframe_index   /* used only for ANIMIMAGE, SECTION and Videoframe Number, -1 for all other types */
-                 , &gfd->local_stepcount    /* nth frame within this clip */
-                 , &gfd->localframe_tween_rest  /* non integer part of local position (in case stepsize != 1) */
-                 , &gfd->keep_proportions
-                 , &gfd->fit_width
-                 , &gfd->fit_height
-                 , &l_red_f
-                 , &l_green_f
-                 , &l_blue_f
-                 , &l_alpha_f
-                 , &gfd->rotate        /* output rotateion in degree */
-                 , &gfd->opacity       /* output opacity 0.0 upto 1.0 */
-                 , &gfd->scale_x       /* output 0.0 upto 10.0 where 1.0 is 1:1 */
-                 , &gfd->scale_y       /* output 0.0 upto 10.0 where 1.0 is 1:1 */
-                 , &gfd->move_x        /* output -1.0 upto 1.0 where 0.0 is centered */
-                 , &gfd->move_y        /* output -1.0 upto 1.0 where 0.0 is centered */
-                 , &gfd->frn_elem      /* output selected to the relevant framerange element */
+                 , gfd
                  );
 
 
@@ -8152,10 +8738,10 @@ p_story_render_fetch_composite_image_private(GapStoryRenderVidHandle *vidhand
            gfd->tmp_image_id = p_create_unicolor_image(&gfd->layer_id
                                                 , vid_width
                                                 , vid_height
-                                                , l_red_f
-                                                , l_green_f
-                                                , l_blue_f
-                                                , l_alpha_f
+                                                , gfd->red_f
+                                                , gfd->green_f
+                                                , gfd->blue_f
+                                                , gfd->alpha_f
                                                 );
 
        }
@@ -8303,6 +8889,8 @@ p_story_render_fetch_composite_image_private(GapStoryRenderVidHandle *vidhand
                                   ,gfd->frn_elem
                                   ,vidhand
                                   ,gfd->local_stepcount
+                                  ,gfd->movepath_file_xml
+                                  ,gfd->movepath_framePhase
                                    );
          gap_image_delete_immediate(gfd->tmp_image_id);
        }
diff --git a/gap/gap_story_render_processor.h b/gap/gap_story_render_processor.h
index f9523a7..891ca9b 100644
--- a/gap/gap_story_render_processor.h
+++ b/gap/gap_story_render_processor.h
@@ -129,7 +129,7 @@ typedef struct GapStoryFetchResult {
   gboolean  force_keyframe;                 /* the calling encoder should encode an I-Frame when true */
   gint32    video_frame_chunk_size;         /* total size of frame (may include a videoformat specific frameheader) */
   gint32    video_frame_chunk_hdr_size;     /* size of videoformat specific frameheader (0 if has no hdr) */
-  unsigned  char *video_frame_chunk_data    /* copy of the already compressed video frame from source video */
+  unsigned  char *video_frame_chunk_data;   /* copy of the already compressed video frame from source video */
 
 } GapStoryFetchResult;
 
@@ -358,4 +358,20 @@ guchar *  gap_story_render_fetch_composite_vthumb(GapStoryRenderVidHandle *stb_c
                             , gint32 width, gint32 height
                             );
 
+gint32    gap_story_render_transform_with_movepath_processing(gint32 comp_image_id
+                         , gint32 tmp_image_id  /* must contain layer_id */
+                         , gint32 layer_id
+                         , gboolean keep_proportions
+                         , gboolean fit_width
+                         , gboolean fit_height
+                         , gdouble rotate     /* rotation in degree */
+                         , gdouble opacity    /* 0.0 upto 1.0 */
+                         , gdouble scale_x    /* 0.0 upto 10.0 where 1.0 = 1:1 */
+                         , gdouble scale_y    /* 0.0 upto 10.0 where 1.0 = 1:1 */
+                         , gdouble move_x     /* -1.0 upto +1.0 where 0 = no move, -1 is left outside */
+                         , gdouble move_y     /* -1.0 upto +1.0 where 0 = no move, -1 is top outside */
+                         , const char *movepath_file_xml
+                         , gdouble movepath_framePhase
+                         );
+
 #endif        /* GAP_STORY_RENDER_PROCESSOR_H */
diff --git a/gap/gap_story_render_types.h b/gap/gap_story_render_types.h
index dc0414f..c6c1cc0 100644
--- a/gap/gap_story_render_types.h
+++ b/gap/gap_story_render_types.h
@@ -204,6 +204,11 @@ typedef struct GapStoryRenderFrameRangeElem   /* nick: frn_elem */
    gint    rotate_accel;        /* acceleration characteristic for opacity transformation */
    gint32  rotate_frames_done;  /* already processed frames since begin of transition */
 
+   gdouble movepath_from;        /* 1.0 to path length in frames */
+   gdouble movepath_to;          /* 1.0 to path length in frames */
+   gint32  movepath_frames_done;
+   gint32  movepath_dur;         /* number of frames to change from -> to value */
+   char   *movepath_file_xml;    /* XML parameter file for movepath transitions */
 
    void   *next;
 } GapStoryRenderFrameRangeElem;  /* used for storyboard processing */
@@ -299,6 +304,12 @@ typedef struct GapStoryRenderVTrackAttrElem
    gint    scale_y_accel;
    gint32  scale_y_frames_done;
 
+   gdouble movepath_from;        /* 1.0 to path length in frames */
+   gdouble movepath_to;          /* 1.0 to path length in frames */
+   gint32  movepath_frames_done;
+   gint32  movepath_dur;         /* number of frames to change from -> to value */
+   char   *movepath_file_xml;    /* (dont g_free this) XML parameter file for movepath transitions */
+
 } GapStoryRenderVTrackAttrElem;  /* Video track attributes used for storyboard processing */
 
 typedef struct GapStoryRenderVTrackArray
diff --git a/gap/gap_story_syntax.c b/gap/gap_story_syntax.c
index d1d5312..63594ab 100644
--- a/gap/gap_story_syntax.c
+++ b/gap/gap_story_syntax.c
@@ -494,6 +494,15 @@ p_create_syntax_list(void)
                ,"nframes"
                ,NULL
                );
+  p_add_keyword(GAP_STBKEY_VID_MOVEPATH
+               ,"track"
+               ,"frame_from"
+               ,"frame_to"
+               ,"nframes"
+               ,"accel"
+               ,"xml_paramfile"
+               ,NULL
+               );
   p_add_keyword(GAP_STBKEY_MASK_MOVIE
                ,"mask_name"
                ,"file"
diff --git a/gap/gap_story_syntax.h b/gap/gap_story_syntax.h
index 44a630d..fe1750d 100644
--- a/gap/gap_story_syntax.h
+++ b/gap/gap_story_syntax.h
@@ -65,6 +65,7 @@
 #define GAP_STBKEY_VID_MOVE_Y               "VID_MOVE_Y"
 #define GAP_STBKEY_VID_FIT_SIZE             "VID_FIT_SIZE"
 #define GAP_STBKEY_VID_OVERLAP              "VID_OVERLAP"
+#define GAP_STBKEY_VID_MOVEPATH             "VID_MOVEPATH"
 
 #define GAP_STBKEY_MASK_IMAGE               "MASK_IMAGE"
 #define GAP_STBKEY_MASK_ANIMIMAGE           "MASK_ANIMIMAGE"
diff --git a/gap/gap_story_vthumb.c b/gap/gap_story_vthumb.c
index ef9a24f..3a70f30 100644
--- a/gap/gap_story_vthumb.c
+++ b/gap/gap_story_vthumb.c
@@ -658,9 +658,12 @@ gap_story_vthumb_get_velem_no_movie(GapStbMainGlobalParams *sgpp
   velem->section_id = section_id;
   if(stb_elem->record_type == GAP_STBREC_VID_SECTION)
   {
-    velem->total_frames = stb_elem->nframes;
     velem->total_frames = gap_story_count_total_frames_in_section(referenced_section);
   }
+  else
+  {
+    velem->total_frames = stb_elem->nframes;
+  }
 
   velem->next = sgpp->video_list;
   sgpp->video_list = velem;
diff --git a/gap/gap_xml_util.c b/gap/gap_xml_util.c
new file mode 100644
index 0000000..08c04e9
--- /dev/null
+++ b/gap/gap_xml_util.c
@@ -0,0 +1,347 @@
+/* gap_xml_util.c
+ * 2011.03.09 hof (Wolfgang Hofer)
+ *
+ * This module contains procedures to parse basic datatypes from string and write to file.
+ * Intended for use in parser functions called by GMarkupParser
+ * to read data from attributes in XML elements.
+ * 
+ */
+/* 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:
+ * 2011.03.09  hof: created.
+ */
+ 
+#include "config.h"
+#include "gap_base.h"
+#include "gap_xml_util.h"
+
+/* --------------------------------------
+ * gap_xml_parse_value_gdouble
+ * --------------------------------------
+ */
+gboolean
+gap_xml_parse_value_gdouble(const gchar *attribute_value, gdouble *valDestPtr)
+{
+  gchar *endptr;
+  
+  *valDestPtr = g_ascii_strtod(attribute_value, &endptr);
+  if(attribute_value == endptr)
+  {
+    /* pointer was not advanced (no float number could be scanned */
+    return (FALSE);
+  }
+  return(TRUE);
+}
+
+/* --------------------------------------
+ * gap_xml_parse_value_gint32
+ * --------------------------------------
+ */
+gboolean
+gap_xml_parse_value_gint32(const gchar *attribute_value, gint32 *valDestPtr)
+{
+  gint64 value64;
+  gchar *endptr;
+  
+  value64 = g_ascii_strtoll(attribute_value, &endptr, 10);
+  if(attribute_value == endptr)
+  {
+    /* pointer was not advanced (no int number could be scanned */
+    return (FALSE);
+  }
+  *valDestPtr = value64;
+  return (TRUE);
+}
+
+/* --------------------------------------
+ * gap_xml_parse_value_gint
+ * --------------------------------------
+ */
+gboolean
+gap_xml_parse_value_gint(const gchar *attribute_value, gint *valDestPtr)
+{
+  gint64 value64;
+  gchar *endptr;
+  
+  value64 = g_ascii_strtoll(attribute_value, &endptr, 10);
+  if(attribute_value == endptr)
+  {
+    /* pointer was not advanced (no int number could be scanned */
+    return (FALSE);
+  }
+  *valDestPtr = value64;
+  return (TRUE);
+}
+
+
+
+/* --------------------------------------
+ * gap_xml_parse_value_gboolean
+ * --------------------------------------
+ */
+gboolean
+gap_xml_parse_value_gboolean(const gchar *attribute_value, gboolean *valDestPtr)
+{
+  gboolean isOk = TRUE;
+
+  *valDestPtr = FALSE;
+  if (strcmp("TRUE", attribute_value) == 0)
+  {
+    *valDestPtr = TRUE;
+  }
+  else if(strcmp("FALSE", attribute_value) == 0)
+  {
+    *valDestPtr = FALSE;
+  }
+  else
+  {
+    gint intVal;
+    
+    isOk = gap_xml_parse_value_gint(attribute_value, &intVal);
+    if(isOk)
+    {
+      *valDestPtr = (intVal != 0);
+    }
+  }
+  
+  return (isOk);
+  
+}  /* end gap_xml_parse_value_gboolean */
+
+
+
+/* --------------------------------------
+ * gap_xml_parse_value_gboolean_as_gint
+ * --------------------------------------
+ */
+gboolean
+gap_xml_parse_value_gboolean_as_gint(const gchar *attribute_value, gint *valDestPtr)
+{
+  gboolean val;
+  gboolean isOk;
+  
+  val = FALSE;
+  isOk = gap_xml_parse_value_gboolean(attribute_value, &val);
+  if(val)
+  {
+    *valDestPtr = 1;
+  }
+  else
+  {
+    *valDestPtr = 0;
+  }
+    
+  return (isOk);
+
+}  /* end gap_xml_parse_value_gboolean_as_gint */
+
+
+/* --------------------------------------
+ * gap_xml_parse_EnumValue_as_gint
+ * --------------------------------------
+ */
+gboolean
+gap_xml_parse_EnumValue_as_gint(const gchar *attribute_value, gint *valDestPtr,
+const GEnumValue *enumValuesTable)
+{
+  gboolean isOk = FALSE;
+  gint     ii;
+
+  for(ii=0; enumValuesTable[ii].value_name != NULL; ii++)
+  {
+    if (strcmp(enumValuesTable[ii].value_name, attribute_value) == 0)
+    {
+      *valDestPtr = enumValuesTable[ii].value;
+      isOk = TRUE;
+      break;
+    }
+    if(enumValuesTable[ii].value_nick != NULL)
+    {
+      if (strcmp(enumValuesTable[ii].value_nick, attribute_value) == 0)
+      {
+        *valDestPtr = enumValuesTable[ii].value;
+        isOk = TRUE;
+        break;
+      }
+    }
+  }
+  
+  return (isOk);
+  
+}  /* end gap_xml_parse_EnumValue_as_gint */
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+/* --------------------------------------
+ * gap_xml_write_gboolean_value
+ * --------------------------------------
+ */
+void
+gap_xml_write_gboolean_value(FILE *fp, const char *name, gboolean value)
+{
+  if(value)
+  {
+    fprintf(fp, "%s=\"TRUE\" ", name);
+  }
+  else
+  {
+    fprintf(fp, "%s=\"FALSE\" ", name);
+  }
+}
+
+/* --------------------------------------
+ * gap_xml_write_gint_as_gboolean_value
+ * --------------------------------------
+ */
+void
+gap_xml_write_gint_as_gboolean_value(FILE *fp, const char *name, gint value)
+{
+  if(value)
+  {
+    fprintf(fp, "%s=\"TRUE\" ", name);
+  }
+  else
+  {
+    fprintf(fp, "%s=\"FALSE\" ", name);
+  }
+}
+
+/* --------------------------------------
+ * gap_xml_write_int_value
+ * --------------------------------------
+ */
+void
+gap_xml_write_int_value(FILE *fp, const char *name, gint32 value)
+{
+  fprintf(fp, "%s=\"%d\" ", name, (int)value);
+}
+
+/* --------------------------------------
+ * gap_xml_write_gdouble_value
+ * --------------------------------------
+ */
+void
+gap_xml_write_gdouble_value(FILE *fp, const char *name, gdouble value, gint digits, gint precision_digits)
+{
+  fprintf(fp, "%s=\"", name);
+  gap_base_fprintf_gdouble(fp, value, digits, precision_digits, "");
+  fprintf(fp, "\" ");
+}
+
+
+/* --------------------------------------
+ * gap_xml_write_string_value
+ * --------------------------------------
+ * write escaped string value.
+ * Note that utf8 compliant strings are written unchanged
+ * but utf8 non compliant strings are changed where all 
+ * characters that are not 7-bit ascii encoded will be replaced by '_'
+ * (to force utf8 compatibility)
+ */
+void
+gap_xml_write_string_value(FILE *fp, const char *name, const char *value)
+{
+  gchar *utf8String;
+  gchar *escapedUtf8String;
+  gboolean    utf8_compliant;
+
+
+  if(value == NULL)
+  {
+    fprintf(fp, "%s=\"\" ", name);
+    return;
+  }
+
+  utf8_compliant = g_utf8_validate(value, -1, NULL);
+  if(utf8_compliant)
+  {
+    utf8String = g_strdup(value);
+  }
+  else
+  {
+    gint ii;
+    gint len;
+    
+    len = strlen(value);
+    
+    utf8String = g_malloc(len +1);
+    
+    /* copy 7-bit ascii characters and replace other characters by "_" */
+    for(ii=0; ii < len; ii++)
+    {
+      if(value[ii] <= 0x7f)
+      {
+        utf8String[ii] = value[ii];
+      }
+      else
+      {
+        utf8String[ii] = '_';
+      }
+    }
+    utf8String[ii] = '\0';
+  }
+  
+  escapedUtf8String = g_markup_printf_escaped("%s", utf8String);
+
+  fprintf(fp, "%s=\"%s\" ", name, escapedUtf8String);
+  
+  g_free(utf8String);
+  g_free(escapedUtf8String);
+
+}
+
+
+/* --------------------------------------
+ * gap_xml_write_EnumValue
+ * --------------------------------------
+ */
+void
+gap_xml_write_EnumValue(FILE *fp, const char *name, gint value, 
+  const GEnumValue *enumValuesTable)
+{
+  gint     ii;
+
+  for(ii=0; enumValuesTable[ii].value_name != NULL; ii++)
+  {
+    if (enumValuesTable[ii].value == value)
+    {
+      gap_xml_write_string_value(fp, name,enumValuesTable[ii].value_name);
+      return;
+    }
+  }
+  
+  /* fallback: write unknown value as int */
+  gap_xml_write_int_value(fp, name, value);
+  
+}  /* end p_parse_xml_value_GimpPaintmode */
+
diff --git a/gap/gap_xml_util.h b/gap/gap_xml_util.h
new file mode 100644
index 0000000..82230c8
--- /dev/null
+++ b/gap/gap_xml_util.h
@@ -0,0 +1,56 @@
+/* gap_xml_util.h
+ * 2011.03.09 hof (Wolfgang Hofer)
+ *
+ * GAP ... Gimp Animation Plugins
+ *
+ * functions to parse and write XML attributes for basic data types.
+ *
+ */
+/* 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:
+ * 2011.03.09  hof: created.
+ */
+#ifndef _GAP_XML_UTIL_H
+#define _GAP_XML_UTIL_H
+
+#include <glib.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "libgimp/gimp.h"
+
+gboolean    gap_xml_parse_value_gdouble(const gchar *attribute_value, gdouble *valDestPtr);
+gboolean    gap_xml_parse_value_gint32(const gchar *attribute_value, gint32 *valDestPtr);
+gboolean    gap_xml_parse_value_gint(const gchar *attribute_value, gint *valDestPtr);
+gboolean    gap_xml_parse_value_gboolean(const gchar *attribute_value, gboolean *valDestPtr);
+gboolean    gap_xml_parse_value_gboolean_as_gint(const gchar *attribute_value, gint *valDestPtr);
+gboolean    gap_xml_parse_EnumValue_as_gint(const gchar *attribute_value, gint
+*valDestPtr, const GEnumValue *enumValuesTable);
+
+void        gap_xml_write_gboolean_value(FILE *fp, const char *name, gboolean value);
+void        gap_xml_write_gint_as_gboolean_value(FILE *fp, const char *name, gint value);
+void        gap_xml_write_int_value(FILE *fp, const char *name, gint32 value);
+void        gap_xml_write_gdouble_value(FILE *fp, const char *name, gdouble value, gint digits, gint precision_digits);
+void        gap_xml_write_string_value(FILE *fp, const char *name, const char *value);
+void        gap_xml_write_EnumValue(FILE *fp, const char *name, gint value, const GEnumValue *enumValuesTable);
+
+#endif
+ 
+ 
diff --git a/po/POTFILES.in b/po/POTFILES.in
index c193302..ed11178 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -30,6 +30,7 @@ gap/gap_morph_exec.c
 gap/gap_morph_main.c
 gap/gap_morph_shape.c
 gap/gap_morph_tween_dialog.c
+gap/gap_mov_main.c
 gap/gap_mov_dialog.c
 gap/gap_mov_exec.c
 gap/gap_mpege.c



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]