[gimp-gap] added new fetures (morph, storyboard wrapper color-balance) some internal bugfixes, applied patch #6



commit 63e70e2d653be55db477d8070b52c3dc11d3e1a8
Author: Wolfgang Hofer <wolfgangh svn gnome org>
Date:   Thu Sep 9 10:37:35 2010 +0200

    added new fetures (morph, storyboard wrapper color-balance) some internal bugfixes, applied patch #625667 (various spelling fixes)

 ChangeLog                                  |  298 ++
 NEWS                                       |   38 +-
 configure.in                               |   14 +-
 docs/reference/txt/INTRODUCTION.txt        |    9 +-
 docs/reference/txt/STORYBOARD_FILE_DOC.txt |  103 +-
 docs/reference/txt/gap_gimprc_params.txt   |   44 +-
 docs/reference/txt/plug-in-colormask.txt   |   31 +
 extern_libs/README_extern_libs             |   17 +-
 gap/Makefile.am                            |   27 +
 gap/TESTPROT_iter_ALT                      |    2 +-
 gap/gap_colordiff.c                        |  363 +++
 gap/gap_colordiff.h                        |  142 +
 gap/gap_colormask_dialog.c                 | 1848 +++++++++++
 gap/gap_colormask_dialog.h                 |   45 +
 gap/gap_colormask_exec.c                   | 4650 ++++++++++++++++++++++++++++
 gap/gap_colormask_exec.h                   |   62 +
 gap/gap_colormask_file.c                   |  166 +
 gap/gap_colormask_file.h                   |   91 +
 gap/gap_colormask_main.c                   |  467 +++
 gap/gap_decode_mplayer.c                   |    2 +-
 gap/gap_edge_detection.c                   |  421 +++
 gap/gap_edge_detection.h                   |   63 +
 gap/gap_fmac_base.c                        |    4 +-
 gap/gap_frame_fetcher.c                    |  150 +-
 gap/gap_frame_fetcher.h                    |   11 +
 gap/gap_layer_copy.c                       |   38 +
 gap/gap_layer_copy.h                       |    2 +
 gap/gap_lib.c                              |   41 +-
 gap/gap_lib.h                              |    1 +
 gap/gap_libgimpgap.h                       |    5 +
 gap/gap_locate.c                           |  843 +++++
 gap/gap_locate.h                           |   83 +
 gap/gap_main.c                             |   10 +-
 gap/gap_match.c                            |    2 +-
 gap/gap_mod_layer.c                        |   76 +-
 gap/gap_mod_layer.h                        |   12 +
 gap/gap_mod_layer_dialog.c                 |   63 +-
 gap/gap_mod_layer_dialog.h                 |    4 +
 gap/gap_morph_dialog.c                     |  512 ++--
 gap/gap_morph_dialog.h                     |    7 +
 gap/gap_morph_exec.c                       |  682 ++++-
 gap/gap_morph_exec.h                       |    2 +-
 gap/gap_morph_main.c                       |  203 +-
 gap/gap_morph_main.h                       |   26 +
 gap/gap_morph_shape.c                      | 1312 ++++++++
 gap/gap_morph_shape.h                      |  102 +
 gap/gap_morph_tween_dialog.c               |  848 +++++-
 gap/gap_morph_tween_dialog.h               |   11 +-
 gap/gap_mov_dialog.h                       |    1 +
 gap/gap_mov_exec.c                         |    8 +
 gap/gap_player_dialog.c                    |   20 +-
 gap/gap_stock.c                            |   69 +
 gap/gap_stock.h                            |    6 +-
 gap/gap_story_att_trans_dlg.c              |   90 +-
 gap/gap_story_dialog.c                     |  104 +-
 gap/gap_story_file.c                       |  560 +++--
 gap/gap_story_file.h                       |   62 +-
 gap/gap_story_main.h                       |   64 +-
 gap/gap_story_properties.c                 |  288 ++-
 gap/gap_story_render_audio.c               |    2 +-
 gap/gap_story_render_lossless.c            |    3 +
 gap/gap_story_render_processor.c           |  598 +++-
 gap/gap_story_render_types.h               |   32 +-
 gap/gap_story_section_properties.c         |    2 +-
 gap/gap_story_syntax.c                     |   19 +
 gap/gap_story_syntax.h                     |    2 +
 gap/gap_story_vthumb.c                     |   16 +-
 gap/gap_thumbnail.c                        |    4 +-
 gap/gap_vex_dialog.c                       |    2 +-
 gap/gap_video_index_creator.c              |    6 +-
 gap/gap_wr_color_balance.c                 |  749 +++++
 libgapbase/gap_val_file.c                  |   23 +-
 libgapbase/gap_val_file.h                  |    1 +
 libgapvidapi/gap_vid_api_ffmpeg.c          |  543 ++--
 libgapvidapi/gap_vid_api_mpeg3.c           |   20 +-
 libgapvidapi/gap_vid_api_mpeg3toc.c        |    6 +-
 libgapvidutil/gap_gve_raw.c                |    4 +-
 po/POTFILES.in                             |    5 +
 vid_enc_ffmpeg/gap_enc_ffmpeg_gui.c        |    6 +-
 vid_enc_ffmpeg/gap_enc_ffmpeg_main.c       |  266 ++-
 vid_enc_ffmpeg/gap_enc_ffmpeg_main.h       |   40 +
 vid_enc_ffmpeg/gap_enc_ffmpeg_par.c        |   19 +
 82 files changed, 16450 insertions(+), 1143 deletions(-)
---
diff --git a/ChangeLog b/ChangeLog
index 046acf4..e9a41d2 100755
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,301 @@
+2010-09-09 Wolfgang Hofer <hof gimp org>
+
+- applied patch #625667 that fixed various spelling mistakes.
+  (most of them occured in comments, some in message texts)
+
+
+ * gap/gap_decode_mplayer.c
+ * gap/gap_fmac_base.c  
+ * gap/gap_lib.c
+ * gap/gap_main.c  
+ * gap/gap_match.c  
+ * gap/gap_mod_layer.c  
+ * gap/gap_morph_dialog.c  
+ * gap/gap_morph_main.c  
+ * gap/gap_story_att_trans_dlg.c  
+ * gap/gap_story_dialog.c  
+ * gap/gap_story_file.c  
+ * gap/gap_story_properties.c  
+ * gap/gap_story_render_audio.c  
+ * gap/gap_story_section_properties.c  
+ * gap/gap_story_vthumb.c  
+ * gap/gap_thumbnail.c  
+ * gap/gap_vex_dialog.c  
+ * gap/gap_video_index_creator.c  
+ * gap/TESTPROT_iter_ALT  
+ * libgapbase/gap_val_file.c  
+ * libgapvidapi/gap_vid_api_mpeg3.c  
+ * libgapvidapi/gap_vid_api_mpeg3toc.c  
+
+
+--- 2010-08-23
+
+- added wrapper plug-in to run gimp_color_balance tool as animated filter.
+
+   
+ * gap/Makefile.am
+ * gap/gap_wr_color_balance.c  # NEW file
+ * po/POTFILES.in
+
+--- 2010-08-15 (Morph features)
+   
+- Added features to the Morph plug-in to support
+  more convenient creation of workpoints and lead to better
+  results (especially for morping tweens of a video)
+
+- Created new plug-in "Morph Workpoint Generator" 
+  (in the menupath Video/Morph/)
+  Generates workpointfiles for each frame in the specified frame range.
+
+- added new feature to morph plug-in "Create Tween Frames"
+   that creates a directory with (renumbered) copies of the processed frames
+   and additionally generated tween frames that are rendered via fade or morphing.
+   individual workpointfiles per processed frame are located via
+   name convention (extension .morphpoints) and used for morphing when they exist.
+   (such files can be created with the help of the plug-in "Morph Workpoint Generator").
+   
+ 
+   example1:
+     generate 2 tween frames between each handled frame.
+     master_tween_steps = 2, tween_subdir = "with_tweens"
+    
+    frame_000001.xcf     == copied to     ==>  with_tweens/frame_000001.xcf
+                         render via morph ==>  with_tweens/frame_000002.xcf
+                         render via morph ==>  with_tweens/frame_000003.xcf
+
+    frame_000002.xcf     == copied to     ==>  with_tweens/frame_000004.xcf
+                         render via morph ==>  with_tweens/frame_000005.xcf
+                         render via morph ==>  with_tweens/frame_000006.xcf
+
+
+    frame_000003.xcf     == copied to     ==>  with_tweens/frame_000007.xcf
+
+   
+   example2:
+     generate 1 tween frame between each handled frame
+     master_tween_steps = 1, tween_subdir = "with_tweens"
+     in this example frame 2 is missing, therefore 3 tweens
+     are generates between frame 1 and 3.
+    
+    frame_000001.xcf     == copied to     ==>  with_tweens/frame_000001.xcf
+                         render via morph ==>  with_tweens/frame_000002.xcf
+                         render via morph ==>  with_tweens/frame_000003.xcf
+                         render via morph ==>  with_tweens/frame_000004.xcf
+
+    frame_000003.xcf     == copied to     ==>  with_tweens/frame_000005.xcf
+                         render via morph ==>  with_tweens/frame_000006.xcf
+   
+    frame_000004.xcf     == copied to     ==>  with_tweens/frame_000007.xcf
+
+- Morph dialog provides new features
+  for edge detection based automatic workpoint generation.
+  This feature is available when CTRL modifier key is hold down while
+  the Shape button is pressed.
+  
+
+
+- morph dialog now supports a special way to set morph workpoints
+  that automatically tries to find the corresponding point in the other image
+  within a specified locate radius.
+  This feature is useful when both images contain similar data.
+
+  This new feature is available via CTRL modifier key when setting morph points
+  (in the modes set and move)
+
+
+ * gap/Makefile.am
+ * gap/gap_colordiff.c [.h]          # NEW files
+ * gap/gap_locate.c [.h]             # NEW files
+ * gap/gap_edge_detection.c [.h]     # NEW files
+ * gap/gap_morph_shape.c [.h]        # NEW files
+ 
+ * gap/gap_morph_dialog.c [.h]
+ * gap/gap_morph_main.c [.h]
+ * gap/gap_libgimpgap.h
+ * gap/gap_morph_tween_dialog.c [.h]
+ 
+ * docs/reference/txt/INTRODUCTION.txt
+ 
+
+-- 2010-07-30 (started update to ffmpeg release ffmpeg 0.6)
+  
+  status: partly works, but problems to read some videoformats
+      - mjpeg (my Olympus SP-560UZ digicam uses this format)
+      - RealMedia
+
+  switch to ffmpeg-0.6 shall not be done until fixing this,
+  because update to new release shall not break 
+  compatibility with videoformats that already worked fine with the 
+  ffmpeg-0.5 based gimp-gap video api.
+
+- fixed gap_gve_raw_RGB_drawable_encode  
+  that method did convert to BGR colormodel (and not to RGB as it should)
+  but this was compensated by another bug when using ffmpeg conversion via deprecated
+  img_convert.
+
+- ffmpeg API no longer supports the procedure img_convert.
+  gimp-gap now uses sws_scale (that was already part of ffmpeg-0.5) and provides
+  the colormodel conversions required to feed the codecs of libavcodec.
+
+- replaced calls to deprecated video/audio decoder procedures
+       avcodec_decode_video  ==> avcodec_decode_video2
+       avcodec_decode_audio2 ==> avcodec_decode_audio3
+
+- ffmpeg-0.6 configure now requires --enable-nonfree when built with
+  non free libs (gimp-gap configure now adds this option when
+  building with the non free libfaac)
+  
+- support to compile gimp-gap with ffmpeg-0.6,
+  but keep older ffmpeg-0.5 release as default 
+
+- build gimp-gap with latest versions of xvid and x264 libraries is currently not possible.
+  note that the gap configure script checks the same way as the included ffmpeg 0.5 version
+  which does not accept new versions of those libraries.
+  therefore a change in the gap configure.in is not sufficient !
+
+  ==> for support of newer xvid and x264 libs porting gimp-gap to newer ffmpeg
+      is also required
+
+
+
+  * configure.in
+  * libgapvidapi/gap_vid_api_ffmpeg.c
+  * extern_libs/README_extern_libs
+  * extern_libs/ffmpeg.tar.gz
+  * vid_enc_ffmpeg/gap_enc_ffmpeg_par.c
+  * vid_enc_ffmpeg/gap_enc_ffmpeg_main.c
+  * vid_enc_ffmpeg/gap_enc_ffmpeg_par.c
+  * libgapvidutil/gap_gve_raw.c  
+
+
+--- 2010-05-xx
+
+- workaround for gtk_button_new_from_stock (); problem.
+
+  Short description of the problem:
+      buttons created with gtk_button_new_from_stock sometimes do not show the icon
+      (at least in case the theme is configured as "Show symbols in menus" = false")
+      
+      Some of the gimp-gap buttons do not have any text
+      (For example the play, pause and reverse buttons in the playback dialog)
+      and appear as very small completely empty buttons
+      that makes it very hard to use with such a theme configuration.
+  
+
+      Here are some examples of stock_ids where the button does NOT show the image:
+        GAP_STOCK_PLAY
+        GAP_STOCK_PAUSE
+        GAP_STOCK_PLAY_REVERSE
+
+        GIMP_STOCK_RESET
+
+        GTK_STOCK_SAVE
+        GTK_STOCK_CLOSE
+
+      Note that other plug-ins (map object, lighting, gfig) that are using stock images
+      in similar way as gimp-gap have the same problem.
+  
+  The workaround uses the new procedure gap_stock_button_new
+  instead of gtk_button_new_from_stock to create buttons with stock images
+  and is used only where it makes no sense to disable the images via themes configuration.
+
+- support for configuration of the gap frame fetcher cache by new gimprc parameters:
+
+  (gap_ffetch_max_img_cache_elements "18")
+  (gap_ffetch_max_gvc_cache_elements "6")
+  (gap_ffetch_gva_frames_to_keep_cached "36")
+  
+  the gap frame fetcher cache settings are relevant for storyboard
+  and filtermacro processing.
+  
+
+
+- fixed sporadic crash in the storyboard render processor.
+  work copy duplicates of images that are cached as gimp image in the gap frame fetcher cache
+  now do detach the image parasite "GAP-IMAGE-CACHE-PARASITE" that marks the image as member of the cache.
+  In case rendering of one frame requires more than one image fetch operations this parasite
+  did sometimes cause an unexpected delete of the duplicate during fetch
+  when the maximum number of cached images is already reached.
+
+  * gap/gap_player_dialog.c  
+  * gap/gap_stock.c [.h]
+  * gap/gap_frame_fetcher.c [.h]
+  * gap/gap_story_render_processor.c
+  * docs/refernce/txt/gap_gimprc_params.txt
+  
+
+----- 2010-04-xx ----
+
+- modify frames plug-in now keeps dialog window open to show progress while processing frames
+  and provides button to cancel processing.
+  The old behavior based on gimp_progress was not sufficient, especially in case
+  when a filter is applied and the called filter also uses gimp_progresss
+  for local progress. This resulted in confusing concurrent progress updates...
+
+- New colormask plug-in added
+  The colormask filter can apply transparency for pixels matching the colormask image.
+  intended for processing frames where moving objects can be isolated
+  from the non-moving constant background by applying the constant background
+  as colormask on all frames.
+  
+  NOTICE:
+  The current implementation is NOT YET READY for use.
+  There are more implementation variants, most of them are experimental
+  some of them will be removed after testing.
+  The dialog window currently hides the options for those experimental variants
+  (until implentation is finished, those options are only visible for USERNAME=hof).
+  
+  
+
+- New storyboard processing feature mask anchor mode CLIP_COLOR
+  applies the selected mask as colormask
+
+- Storyboard support rotate transitions
+
+- New storyboard processing feature to add external transparency 
+  for clip type movie. This is done via format string that refers to
+  the external frames (typical gray image frames)
+  the format string has placeholder %s (replaced by basename of the video)
+  and %06d (replaced by the currently processed frame number)
+  
+  The format string can be specified in the storyboard master properties dialog
+  (similar to the area replacement for logo processing)
+  
+  * docs/reference/txt/plug-in-gap-move-path.txt
+  * libgapbase/gap_val_file.c [.h]
+  * gap/gap_mod_*         
+
+  * NEWS
+  * docs/reference/txt/INTRODUCTION.txt
+  * docs/reference/txt/STORYBOARD_FILE_DOC.txt
+  * docs/reference/txt/plug-in-colormask.txt    # NEW FILE
+
+  * gap/gap_colormask_main.c          # NEW FILE
+  * gap/gap_colormask_exec.c [.h]     # NEW FILES
+  * gap/gap_colormask_file.c [.h]     # NEW FILES
+  * gap/gap_colormask_dialog.c [.h]   # NEW FILES
+  * gap/Makefile.am
+  * gap/gap_libgimpgap.h
+  * gap/gap_layer_copy.c [.h]
+
+  * gap/gap_morph_dialog.c
+
+
+  * gap/gap_lib.c
+  * gap/gap_lib.h
+  * gap/gap_story_att_trans_dlg.c
+  * gap/gap_story_dialog.c
+  * gap/gap_story_file.c
+  * gap/gap_story_file.h
+  * gap/gap_story_main.h
+  * gap/gap_story_properties.c
+  * gap/gap_story_render_lossless.c
+  * gap/gap_story_render_processor.c
+  * gap/gap_story_render_types.h
+  * gap/gap_story_syntax.c
+  * gap/gap_story_syntax.h
+
+
 2010-07-04 Wolfgang Hofer <hof gimp org>
 
 - added gitignore files (applied patch provided by Carlo Baldassi #623050)
diff --git a/NEWS b/NEWS
index d395b06..ca549e5 100644
--- a/NEWS
+++ b/NEWS
@@ -19,7 +19,43 @@ Here is a short overview whats new in GIMP-GAP-2.7.0:
     Th GAP dbbrowser now supports acceleration characteristic graph and spinbutton
     (that replaces the "Apply Varying" button of older GIMP_GAP releases)
 
-- some other bugfixes (See ChangeLog for details)
+- New colormask plug-in added
+  The colormask filter can apply transparency for pixels matching the colormask image.
+  intended for processing frames where moving objects can be isolated
+  from the non-moving constant background by aplying the constant background
+  as colormask on all frames.
+  the colormask feature is also available as new mask anchor mode 
+  when processing stroyboard clips.
+  (anchor modes are: ClipColormask, Clip, Master)
+
+- The Storyboard now supports rotatation of the prcessed clips by any angle.
+
+- A new storyboard processing feature allows adding external transparency 
+  for clip type movie. This is done via format string that refers to
+  the external frames (typical gray image frames)
+  the format string has placeholder %s (replaced by basename of the video)
+  and %06d (replaced by the currently processed frame number)
+  
+  The format string can be specified in the storyboard master properties dialog
+
+- Added features to the Morph plug-in to support
+  more convenient creation of workpoints and lead to better
+  results (especially for morping tweens of a video)
+  
+  new plug-in "Video/Morph/Morph Workpoint Generator" 
+        Generates workpointfiles for each frame in the specified frame range.
+
+  new feature for the morph plug-in "Video/Morph/Create Tween Frames"
+   that creates a directory with (renumbered) copies of the processed frames
+   and additionally generated tween frames that are rendered via fade or morphing.
+   individual workpointfiles per processed frame are located via
+   name convention (extension .morphpoints) and used for morphing when they exist.
+   (such files can be created with the help of the plug-in "Morph Workpoint Generator").
+
+- support to run gimp_color_balance tool as animated filter 
+  (added wrapper plug-in).
+
+- some bugfixes (See ChangeLog for details)
 
 
 Here is a short overview whats new in GIMP-GAP-2.6.0:
diff --git a/configure.in b/configure.in
old mode 100644
new mode 100755
index f9c7a98..a5b8998
--- a/configure.in
+++ b/configure.in
@@ -405,6 +405,7 @@ INFORMATION: old ffmpeg was moved to $FFMPEG_DIR-OLD
       FFMPEG_LIBAVCODEC_A="$FFMPEG_DIR/libavcodec/${LIBPREF}avcodec${LIBSUF}"
       FFMPEG_LIBAVUTIL_A="$FFMPEG_DIR/libavutil/${LIBPREF}avutil${LIBSUF}"
       FFMPEG_LIBAVDEVICE_A="$FFMPEG_DIR/libavdevice/${LIBPREF}avdevice${LIBSUF}"
+      FFMPEG_LIBSWSCALE_A="$FFMPEG_DIR/libswscale/${LIBPREF}swscale${LIBSUF}"
       dnl
       dnl ffmpeg can be configured to use external codec libs x264, xvid ....
       dnl options for ffmpeg ext libs configuration will be passed to ffmpeg/configure
@@ -415,8 +416,8 @@ INFORMATION: old ffmpeg was moved to $FFMPEG_DIR-OLD
         FFMPEG_EXTLIBS="$FFMPEG_EXTLIBS -lws2_32"
       fi
 
-      GAP_VLIBS_FFMPEG="  $FFMPEG_LIBAVDEVICE_A $FFMPEG_LIBAVFORMAT_A $FFMPEG_LIBAVCODEC_A $FFMPEG_LIBAVUTIL_A $FFMPEG_EXTLIBS "
-      GAP_VINCS_FFMPEG=" -I$FFMPEG_DIR -I$FFMPEG_DIR/libavcodec -I$FFMPEG_DIR/libavformat -I$FFMPEG_DIR/libavutil -I$FFMPEG_DIR/libavdevice "
+      GAP_VLIBS_FFMPEG="  $FFMPEG_LIBAVDEVICE_A $FFMPEG_LIBAVFORMAT_A $FFMPEG_LIBAVCODEC_A $FFMPEG_LIBAVUTIL_A $FFMPEG_LIBSWSCALE_A $FFMPEG_EXTLIBS "
+      GAP_VINCS_FFMPEG=" -I$FFMPEG_DIR -I$FFMPEG_DIR/libavcodec -I$FFMPEG_DIR/libavformat -I$FFMPEG_DIR/libavutil -I$FFMPEG_DIR/libavdevice  -I$FFMPEG_DIR/libswscale"
 
       vid_ffmpeg_warning="
           $x264_warn
@@ -450,9 +451,9 @@ INFORMATION: old ffmpeg was moved to $FFMPEG_DIR-OLD
       	 ff_pass_through_flags=" $ff_pass_through_flags --cc=$CC "
       fi
       
-      FFMPEG_CONFIGURE_OPTIONS=" $ff_pass_through_flags $FFMPEG_CONFIGURE_OPTIONS"
- 
+      FFMPEG_CONFIGURE_OPTIONS=" $ff_pass_through_flags $FFMPEG_CONFIGURE_OPTIONS --enable-swscale"
  
+      FFMPEG_ENABLE_NONFREE=""
 
       dnl configure ffmpeg bzip2 usage
       if test "x$FF_BZIP2" != "x"; then
@@ -467,6 +468,7 @@ INFORMATION: old ffmpeg was moved to $FFMPEG_DIR-OLD
       fi
       if test "x$FF_LIBFAAC" != "x"; then
       	 FFMPEG_CONFIGURE_OPTIONS="$FFMPEG_CONFIGURE_OPTIONS --enable-libfaac"
+         FFMPEG_ENABLE_NONFREE="--enable-nonfree"
       fi
       if test "x$FF_LIBFAAD" != "x"; then
       	 FFMPEG_CONFIGURE_OPTIONS="$FFMPEG_CONFIGURE_OPTIONS --enable-libfaad"
@@ -481,6 +483,9 @@ INFORMATION: old ffmpeg was moved to $FFMPEG_DIR-OLD
       if test "x$FF_LIBXVID" != "x"; then
       	 FFMPEG_CONFIGURE_OPTIONS="$FFMPEG_CONFIGURE_OPTIONS --enable-libxvid"
       fi
+
+      dnl in case nonfree libs are used ffmeg 0.6 configure requires --enable-nonfree
+      FFMPEG_CONFIGURE_OPTIONS="$FFMPEG_CONFIGURE_OPTIONS $FFMPEG_ENABLE_NONFREE"
       
       if  test "x$os_win32" = "xyes"; then
          dnl here we could check for required win32 api version on MonGW Win32 Systems.
@@ -934,6 +939,7 @@ AC_SUBST(FFMPEG_DIR)
 AC_SUBST(FFMPEG_LIBAVFORMAT_A)
 AC_SUBST(FFMPEG_LIBAVCODEC_A)
 AC_SUBST(FFMPEG_LIBAVUTIL_A)
+AC_SUBST(FFMPEG_LIBSWSCALE_A)
 AC_SUBST(LMPEG3_DIR)
 AC_SUBST(LMPEG3_A)
 
diff --git a/docs/reference/txt/INTRODUCTION.txt b/docs/reference/txt/INTRODUCTION.txt
index fefc8fe..8195f41 100644
--- a/docs/reference/txt/INTRODUCTION.txt
+++ b/docs/reference/txt/INTRODUCTION.txt
@@ -99,7 +99,7 @@ Output to videofile:
         GIMP-GAP provides video encoder plug-ins
         to save a series of input frame images or videoclips
         as videofile.
-        This process is called encoding an usually 
+        This process is called encoding and usually 
         does compresses the processed frames and audiodata.
 
         GIMP-GAP encoder plug-ins can operate on
@@ -304,6 +304,9 @@ Internal PDB names and menu names of GIMP-GAP plug-ins
   plug_in_gap_exchg                   "<Image>/Video/Exchange Frame"
   plug_in_gap_modify                  "<Image>/Video/Frames Modify"
   plug-in-gap-morph-layers            "<Image>/Video/Morph"
+  plug_in_gap_morph_tween             "<Image>/Video/Morph Tweenframes"
+  plug_in_gap_morph_one_tween         "<Image>/Video/Morph One Tween"
+  plug_in_gap_morph_workpoints        "<Image>/Video/Morph Workpoint Generator"
   plug_in_gap_move                    "<Image>/Video/Move Path"
   plug_in_gap_navigator               "<Image>/Video/VCR Navigator"
   plug_in_gap_range_convert           "<Image>/Video/Frames Convert"
@@ -317,10 +320,12 @@ Internal PDB names and menu names of GIMP-GAP plug-ins
   plug-in-gap-storyboard-edit         "<Image>/Video/Storyboard"
   plug_in_gap_videoframes_player      "<Image>/Video/Playback"
 
+  plug-in-layer-set-alpha-by-colormask "<Image>/Video/Layer/Attributes/"
+
+
   plug-in-gap-vid-encode-master       "<Image>/Video/Encode/Master Videoencoder"
   plug_in_gap_mpeg2encode             "<Image>/Video/Encode/MPEG2 (mpeg2encode)"
   plug_in_gap_mpeg_encode             "<Image>/Video/Encode/MPEG1 (mpeg_encode)"
-
   plug_in_gap_goto                    "<Image>/Video/Go To/Any Frame"
   plug_in_gap_first                   "<Image>/Video/Go To/First Frame"
   plug_in_gap_last                    "<Image>/Video/Go To/Last Frame"
diff --git a/docs/reference/txt/STORYBOARD_FILE_DOC.txt b/docs/reference/txt/STORYBOARD_FILE_DOC.txt
index 654d8cb..6d0660a 100644
--- a/docs/reference/txt/STORYBOARD_FILE_DOC.txt
+++ b/docs/reference/txt/STORYBOARD_FILE_DOC.txt
@@ -1,4 +1,4 @@
-STORYBOARD_FILES           2010.02.15:
+STORYBOARD_FILES           2010.03.20:
 
 
 General
@@ -74,6 +74,7 @@ VID_MASTER_FRAME_ASPECT width:16 height:9
 VID_MASTER_SIZE         width:320  height:200
 VID_PREFERRED_DECODER   libavformat
 VID_MASTER_LAYERSTACK   track1:Background
+VID_MASTER_INSERT_ALPHA format:"/path/file_%s_%06d.xcf"
 
 AUD_MASTER_VOLUME      volume:1.0
 AUD_MASTER_SAMPLERATE  samples_per_sec:44100
@@ -143,6 +144,7 @@ VID_PLAY_COLOR     track:1  red:0.0 green:1.0 blue:0.0 alpha:1.0   nloops:1 \
 VID_SILENCE        track:1                                         nloops:1  wait_until_sec:0.0
 
 # video attributes
+VID_ROTATE         track:1  rotate_from:0.0      rotate_to:360.0    nframes:10
 VID_OPACITY        track:1  opacity_from:0.0     opacity_to:1.0     nframes:10
 VID_ZOOM_X         track:1  zoom_x_from:0.1      zoom_x_to:10.0     nframes:10
 VID_ZOOM_Y         track:1  zoom_y_from:0.1      zoom_y_to:10.0     nframes:10
@@ -243,6 +245,65 @@ VID_MASTER_LAYERSTACK   track1:[Top | Background]
 			      Background .... (track 1 is the background)
                             DEFAULT: Top 
 
+
+VID_MASTER_INSERT_ALPHA
+  Specifies a format string that referes to an image or to a series of frame images
+  to provide transparency for the corresponding video frame.
+  This format string shall contain placeholders
+     %s    for the basename of videoclips and 
+     %06d  for the 6 digit framenumber.
+     
+  This format string will be checked each time when storyboard processing
+  fetches a video frame (from clip type VID_PLAY_MOVIE)
+  and will be evaluated to a corresponding frame/image filename.
+  If a frame image with this evaluated filename exists,
+  that image will be automatically loaded,
+  scaled to frame size and attached as alpha channel
+  to the fetched frame.
+  This feature offers individual transparency per video clip and frame.
+
+  Example1:
+    Have a Storyboard with:
+      VID_MASTER_INSERT_ALPHA  format="/home/storyboards/video_alpha_frame/alpha_%s_%06d.png"
+      VID_PLAY_MOVIE track=2 file=my_clip.AVI  from=12 to=20
+    
+    When processing fetches frame number 14 of my_clip.AVI
+    it evaluates the alpha imagename by substitution
+    where 
+        %s    is replaced by "my_clip"
+        %06d  is replaced by "000014"
+    and checks if the resulting alpha imagename exists:
+      
+        /home/storyboards/video_alpha_frame/alpha_my_clip_000014.png
+    
+    If there is such an image it will be added as alpha channel
+    to add transparency for the fetched video frame.
+
+  Example2:
+    Have a Storyboard with:
+      VID_MASTER_INSERT_ALPHA  format="/home/storyboards/video_alpha_frame/alpha_%s.png"
+      VID_PLAY_MOVIE track=2 file=my_clip.AVI  from=12 to=20
+    
+    In this case there is just the placeholder for the videofile but no
+    placeholder for the framenumber.
+    In this case the alpha imagename evaluates to
+
+       /home/storyboards/video_alpha_frame/alpha_my_clip.png
+
+    for all frames of this video clip. If such a file exists it
+    is applied as alpha channel on all frames of my_clip.AVI
+    
+
+
+  (1) Record Key        ... VID_MASTER_INSERT_ALPHA
+  (2) format            ... the format string
+                            shall include 
+                            %s     as placeholder for
+                                   videofile basename and
+                            %06d   as placeholder for the framenumber.
+  
+
+
 VID_PLAY_MOVIE
   fetch frames from a videofile. (such as MPEG, AVI, ...
   see GAP_VID_API description for Informations about 
@@ -318,6 +379,13 @@ VID_PLAY_MOVIE
                                    In this mode the mask is fixed to the master frame.
                                    and does not follow transformations and movements
                                    of the input frame.
+                            "xcolor" scale mask to resulting layer size.
+                                   and apply as color mask.
+                                   In this case the image that is referred via mask_name
+                                   should be a color image.
+                                   the mask scales and moves according
+                                   to transformations of the input frame
+                                   where it is attached to.
  [17] mask_stepsize:    ... stepsize factor for the mask-frames.
                             a value of 2.0 speeds up the mask effect (by skipping
                             every 2nd frame in the assigned mask clip)
@@ -335,6 +403,13 @@ VID_PLAY_MOVIE
                             other positive values result in increasing speed, negative values
                             result in decreasing speed.
 
+ [21] colormask_file:   ... a filename referring to a file with parameters for
+                            applying a mask with the colormask filter.
+                            the colormask filter makes pixels of the processed frame
+                            transparent when they are equal or similar to the corresponding
+                            pixel in the colormask.
+                            typically the colormask represents the non-moving backround
+                            that can be set transparent while moving objects are opaque.
 
 
 
@@ -367,6 +442,7 @@ VID_PLAY_ANIMIMAGE
  [14] mask_disable:     ... Same as described for VID_PLAY_MOVIE (see above)
  [15] macsteps:         ... Same as described for VID_PLAY_MOVIE (see above)
  [16] macaccel:         ... Same as described for VID_PLAY_MOVIE (see above)
+ [17] colormask_file:   ... Same as described for VID_PLAY_MOVIE (see above)
 
 
 
@@ -403,6 +479,7 @@ VID_PLAY_FRAMES
  [15] mask_disable:     ... Same as described for VID_PLAY_MOVIE (see above)
  [16] macsteps:         ... Same as described for VID_PLAY_MOVIE (see above)
  [17] macaccel:         ... Same as described for VID_PLAY_MOVIE (see above)
+ [18] colormask_file:   ... Same as described for VID_PLAY_MOVIE (see above)
 
 VID_PLAY_IMAGE
   fetch frame from a single imagefile. (Multilayer Images are handled as
@@ -422,6 +499,7 @@ VID_PLAY_IMAGE
  [10] mask_disable:     ... Same as described for VID_PLAY_MOVIE (see above)
  [11] macsteps:         ... Same as described for VID_PLAY_MOVIE (see above)
  [12] macaccel:         ... Same as described for VID_PLAY_MOVIE (see above)
+ [13] colormask_file:   ... Same as described for VID_PLAY_MOVIE (see above)
 
 
 
@@ -445,6 +523,7 @@ VID_PLAY_COLOR
  [11] mask_disable:     ... Same as described for VID_PLAY_MOVIE (see above)
  [12] macsteps:         ... Same as described for VID_PLAY_MOVIE (see above)
  [13] macaccel:         ... Same as described for VID_PLAY_MOVIE (see above)
+ [14] colormask_file:   ... Same as described for VID_PLAY_MOVIE (see above)
 
 VID_SILENCE
   This record is used to define Pauses in a videotrack. 
@@ -459,6 +538,28 @@ VID_SILENCE
                             timestamp can be a framenumber (integer) or seconds (float)
                             DEFAULT: 0
 
+VID_ROTATE
+  This record is used to define rotate treansitions,
+  by changing the rotation angle of the processed frames in the specified Videotrack slightly from one value
+  to another.
+  
+  (1) Record Key        ... VID_ROTATE
+  (2) track             ... integer tracknumer
+  (3) rotate_from       ... Start rotation with this value
+                            where 0.0 is and 360.0 is one full rotation
+  [4] rotate_to         ... Change rotation to this value
+                            DEFAULT: use same value as rotate_from
+  [5] nframes           ... optional duration of the effect
+                            in number of frames (integer) or seconds (float) 
+                            The duration Value 0 applies the TO value immediate.
+                            DEFAULT: 0
+  [6] accel             ... an integer value specifiying acceleration characteristic
+                            for speed behavior of rotation changes.
+                            where 1 (and 0) rotates the object with constant speed.
+                            other positive values result in increasing rotation speed, negative values
+                            result in decreasing speed.
+                            DEFAULT: 0
+
 VID_OPACITY
   This record is used to define Fade Effects,
   by changing the Opacity of the Videotrack slightly from one value
diff --git a/docs/reference/txt/gap_gimprc_params.txt b/docs/reference/txt/gap_gimprc_params.txt
index 89cc40b..a5071e0 100644
--- a/docs/reference/txt/gap_gimprc_params.txt
+++ b/docs/reference/txt/gap_gimprc_params.txt
@@ -4,8 +4,8 @@ Video Configuration (gimprc)
 --------------------------------------------------------------------
 
 There are optional configuration values for the GIMP-GAP video plug-ins.
-Those values are stored in your personal gimprc file (located in $HOME/.gimp-2.2/gimprc)
-or in the system-wide gimprc file (usually located at /usr/local/etc/gimp/2.2/gimprc)
+Those values are stored in your personal gimprc file (located in $HOME/.gimp-2.6/gimprc)
+or in the system-wide gimprc file (usually located at /usr/local/etc/gimp/2.6/gimprc)
 
 If you edit gimprc files by hand, you must do this before you startup GIMP.
 
@@ -253,3 +253,43 @@ If you edit gimprc files by hand, you must do this before you startup GIMP.
 # anaylse results files (even if they are still valid)
 # 
 (video-gva-libavformat-video-analyse-persistent "yes")
+
+
+# gimp_gap frame fetcher configuration
+# ------------------------------------
+#
+# the gap frame fetcher cache is currently relevant for storyboard processing
+# and filtermacro processing. it is used to keep the configured
+# number of images, videohandles and videoframes in cache for potential
+# reuse in further processing steps to avoid multiple load or open/close
+# operations.
+#
+# images are cached as gimp images that are marked with a temporary image parasite
+# "GAP-IMAGE-CACHE-PARASITE"
+# those parasite identifies an image as member of the gimp_gap frame fetcher cache. 
+# (gap does not open a display on such images)
+# setting the gap_ffetch_max_img_cache_elements too high can result in
+# slow processing because the gimp core starts to swap when too much
+# or too large images are loaded.
+#
+# using values up to 100 or more may speed up storyboard processing
+# on machines with large memory (4G or more).
+# (note: expect speed up only in case the storyboard has multiple references
+# to images, such as applying the same mask image to more frames
+# or using the same image to render multiple frames)
+#
+# default value is 18.
+#
+(gap_ffetch_max_img_cache_elements "18")
+
+# number of video handles to keep cached.
+# default value is 6
+(gap_ffetch_max_gvc_cache_elements "6")
+
+# number of video frames per video handle to keep cached.
+# note that video frames are not cached as gimp image.
+# video frames are cached by the gimp gap video api that allocates
+# the memory (and will fail in case not enough memory is available to hold the configured
+# number of video frames)
+# default value is 36
+(gap_ffetch_gva_frames_to_keep_cached "36")
diff --git a/docs/reference/txt/plug-in-colormask.txt b/docs/reference/txt/plug-in-colormask.txt
new file mode 100644
index 0000000..408ba22
--- /dev/null
+++ b/docs/reference/txt/plug-in-colormask.txt
@@ -0,0 +1,31 @@
+"plug-in-colormask"
+
+Colormask Filter
+
+    Start from Menu:
+       <Image>/Video/Layer/Attributes/Apply Colormask
+       
+    The colormask filter can apply transparency for pixels matching the colormask image.
+    It is intended for processing frames where moving objects can be isolated
+    from the non-moving constant background by aplying the constant background
+    as colormask on all frames.
+    
+    The colormask provides more flexibility than the bluebox filter
+    because it can operate on frames where the background is not just made of shades
+    of the same color.
+    But for good results the moving object shall not contain the same colors as the background.
+    
+    In case the colormask layer has an alpha channel it is used to
+    define protected areas, e.g the alpha channel of the colormask controls 
+    where the filter shall be applied (opaque) or not (transparent pixels are protected).
+    Pixels where the corresponding alpha channel in the colormask is below the specifed
+    triggerAlpha value are not affected by the colormask filter.
+    This is useful when the colormask filter runs non-interactive
+    under control of storyboard render processing
+    on frame images that are created internally where the image has no selction.
+    
+    
+    
+    The colormask filter is not yet fully implemented.
+    
+    TODO: add more detailed feature description here when implementation is finished.
diff --git a/extern_libs/README_extern_libs b/extern_libs/README_extern_libs
old mode 100644
new mode 100755
index f1b9a0e..5d69127
--- a/extern_libs/README_extern_libs
+++ b/extern_libs/README_extern_libs
@@ -3,7 +3,7 @@ as sourcecode for convenient static linking.
 
 CURRENT FFMPEG version is:
 
-- ffmpeg-0.5.tar.bz2
+- ffmpeg 0.5
 
 CURRENT LIBMPEG3 version is:
        
@@ -20,9 +20,9 @@ of those libs.
 ffmpeg
 --------------
 
-GIMP-GAP uses the libraries libavformat and libavcodec
+GIMP-GAP uses the libraries libavformat, libavcodec, libavutil, libavdevice and libswscale
 that are part of ffmpeg. 
-libavformat and libavcodec build up the basic videofile support for 
+Those libs build up the basic videofile support for 
 many MPEG based fileformats, both for read and write access 
 (frame and audio extraction, and for the powerful video encoder plug-in
  "FFMPEG" in the Master Videoencoder dialog).
@@ -45,10 +45,13 @@ this GIMP-GAP distribution.
   of both GIMP-GAP and ffmpeg internals to get new ffmpeg versions
   working. 
   
-  Therefore i decided to include the snapshot of the ffmpeg sourcetree.
-  that was tested with GIMP-GAP during Mar. 2009
+  GIMP-GAP currently supports 
+  o) ffmpeg-0.5 successfully tested with many videoformats
+     and
+  o) ffmpeg-0.6 basically works, but fails on some videoformats like MJPEG encdoded AVI files
+       (my Olympus SP560UZ Camera records videos in this format). 
 
-  newer ffmpeg SVN snapshots
+  newer ffmpeg GIT (or SVN) snapshots
   may or may not compile, link and run with this GIMP-GAP release.
 
 
@@ -82,7 +85,7 @@ NOTE: Older ffmpeg versions that once worked with older GIMP-GAP
 
    ffmpeg-CVS-2005.04.08  (Support dropped)
    ffmpeg-CVS-2005.03.02  (Support dropped)
-   ffmpeg-0.4.9pre1       (latest, but outdated official release is NOT SUPPORTED)
+   ffmpeg-0.4.9pre1       (outdated official release is NOT SUPPORTED)
    ffmpeg 0.4.8           (outdated stable release is NOT SUPPORTED) 
    
 
diff --git a/gap/Makefile.am b/gap/Makefile.am
index 0a8bb9a..b4b4387 100644
--- a/gap/Makefile.am
+++ b/gap/Makefile.am
@@ -44,6 +44,14 @@ BASE_SOURCES = \
 	gap_audio_util.h	\
 	gap_audio_wav.c		\
 	gap_audio_wav.h		\
+	gap_colordiff.c	        \
+	gap_colordiff.h 	\
+	gap_colormask_exec.c	\
+	gap_colormask_exec.h 	\
+	gap_colormask_file.c	\
+	gap_colormask_file.h 	\
+        gap_edge_detection.c    \
+        gap_edge_detection.h    \
 	gap_image.c		\
 	gap_image.h		\
 	gap_layer_copy.c	\
@@ -51,6 +59,8 @@ BASE_SOURCES = \
 	gap_lib.c		\
 	gap_lib.h		\
 	gap_lib_common_defs.h	\
+	gap_locate.c		\
+	gap_locate.h		\
 	gap_lock.c		\
 	gap_lock.h		\
 	gap_navi_activtable.c	\
@@ -96,6 +106,7 @@ libgapstory_a_SOURCES = $(BASE_SOURCES)	\
 
 libexec_PROGRAMS = \
 	gap_bluebox		\
+	gap_colormask		\
 	gap_plugins		\
 	gap_filter		\
 	gap_fmac		\
@@ -113,6 +124,7 @@ libexec_PROGRAMS = \
 	gap_wr_color_curve	\
 	gap_wr_color_levels	\
 	gap_wr_color_huesat	\
+	gap_wr_color_balance	\
 	gap_wr_trans		\
 	gap_wr_resynth		\
 	gap_wr_opacity
@@ -125,6 +137,13 @@ gap_bluebox_SOURCES = \
 	gap_bluebox.h		\
 	gap_libgimpgap.h	
 
+gap_colormask_SOURCES = \
+	gap_lastvaldesc.c	\
+	gap_lastvaldesc.h	\
+	gap_colormask_dialog.c	\
+	gap_colormask_dialog.h 	\
+	gap_colormask_main.c	\
+	gap_libgimpgap.h	
 
 gap_plugins_SOURCES = \
 	gap_base_ops.c		\
@@ -246,6 +265,8 @@ gap_morph_SOURCES = \
 	gap_morph_main.h	\
 	gap_morph_exec.c	\
 	gap_morph_exec.h	\
+	gap_morph_shape.c	\
+	gap_morph_shape.h	\
 	gap_morph_dialog.c	\
 	gap_morph_dialog.h	\
 	gap_morph_tween_dialog.c	\
@@ -369,6 +390,10 @@ gap_wr_color_huesat_SOURCES = \
 	gap_wr_color_huesat.c	\
 	gap_libgimpgap.h
 
+gap_wr_color_balance_SOURCES = \
+	gap_wr_color_balance.c	\
+	gap_libgimpgap.h
+
 gap_wr_resynth_SOURCES = \
 	gap_wr_resynth.c	\
 	gap_lastvaldesc.c	\
@@ -400,6 +425,7 @@ LDADD = $(GIMP_LIBS)
 
 gap_plugins_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)
 gap_fmac_LDADD =             $(GAPVIDEOAPI) $(LIBGIMPGAP)  $(LIBGAPBASE) $(GIMP_LIBS)
 gap_fmac_varying_LDADD =     $(GAPVIDEOAPI) $(LIBGIMPGAP)  $(LIBGAPBASE) $(GIMP_LIBS)
@@ -418,6 +444,7 @@ gap_wr_trans_LDADD =         $(LIBGIMPGAP)  $(LIBGAPBASE) $(GIMP_LIBS)
 gap_wr_color_curve_LDADD =   $(LIBGIMPGAP)  $(LIBGAPBASE) $(GIMP_LIBS)
 gap_wr_color_levels_LDADD =  $(LIBGIMPGAP)  $(LIBGAPBASE) $(GIMP_LIBS)
 gap_wr_color_huesat_LDADD =  $(LIBGIMPGAP)  $(LIBGAPBASE) $(GIMP_LIBS)
+gap_wr_color_balance_LDADD = $(LIBGIMPGAP)  $(LIBGAPBASE) $(GIMP_LIBS)
 gap_wr_resynth_LDADD =       $(LIBGIMPGAP)  $(LIBGAPBASE) $(GIMP_LIBS)
 
 EXTRA_DIST = \
diff --git a/gap/TESTPROT_iter_ALT b/gap/TESTPROT_iter_ALT
index 56d1627..9daef2e 100644
--- a/gap/TESTPROT_iter_ALT
+++ b/gap/TESTPROT_iter_ALT
@@ -204,7 +204,7 @@ Additional notes:
 
 plug_in_map_object old Testreport: (did not verfy that at the latest test)
    
-   - The MapObject has an implicite feature:
+   - The MapObject has an implicit feature:
      If the handled layer has NO ALPHA Channel (as backgrounds often do)
      and the "Transparent background" is ON
      it forces the creation of a new image.
diff --git a/gap/gap_colordiff.c b/gap/gap_colordiff.c
new file mode 100644
index 0000000..ddd2139
--- /dev/null
+++ b/gap/gap_colordiff.c
@@ -0,0 +1,363 @@
+/* gap_colordiff.c
+ *    by hof (Wolfgang Hofer)
+ *    color difference procedures
+ *  2010/08/06
+ *
+ */
+/* 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:
+ * version 2.7.0;             hof: created
+ */
+
+/* SYTEM (UNIX) includes */
+#include "string.h"
+/* GIMP includes */
+/* GAP includes */
+#include "gap_lib_common_defs.h"
+#include "gap_pdb_calls.h"
+#include "gap_colordiff.h"
+#include "gap_lib.h"
+#include "gap_image.h"
+#include "gap_layer_copy.h"
+#include "gap_libgapbase.h"
+
+
+#define TO_LONG_FACTOR     100000
+#define TO_GDOUDBLE_FACTOR 100000.0
+
+extern      int gap_debug; /* ==0  ... dont print debug infos */
+
+
+
+static long p_gdouble_to_long(gdouble value)
+{
+  long lval;
+  
+  lval = value * TO_LONG_FACTOR;
+  return (lval);
+}
+static gdouble p_long_to_gdouble(long value)
+{
+  gdouble dval;
+  
+  dval = (gdouble)value / TO_GDOUDBLE_FACTOR;
+  return (dval);
+}
+
+
+
+
+
+/* ---------------------------------
+ * gap_colordiff_GimpHSV
+ * ---------------------------------
+ * returns difference of 2 colors as gdouble value
+ * in range 0.0 (exact match) to 1.0 (maximal difference)
+ */
+gdouble
+gap_colordiff_GimpHSV(GimpHSV *aHsvPtr
+                  , GimpHSV *bHsvPtr
+                  , gdouble colorSensitivity
+                  , gboolean debugPrint)
+{
+  gdouble colorDiff;
+  gdouble hDif;
+  gdouble sDif;
+  gdouble vDif;
+  gdouble vMax;
+  gdouble sMax;
+  gdouble weight;
+
+
+  hDif = fabs(aHsvPtr->h - bHsvPtr->h);
+  /* normalize hue difference.
+   * hue values represents an angle
+   * where value 0.5 equals 180 degree
+   * and value 1.0 stands for 360 degree that is
+   * equal to 0.0
+   * Hue is maximal different at 180 degree.
+   *
+   * after normalizing, the difference
+   * hDiff value 1.0 represents angle difference of 180 degree
+   */
+  if(hDif > 0.5)
+  {
+    hDif = (1.0 - hDif) * 2.0;
+  }
+  else
+  {
+    hDif = hDif * 2.0;
+
+  }
+  sDif = fabs(aHsvPtr->s - bHsvPtr->s);
+  vDif = fabs(aHsvPtr->v - bHsvPtr->v);
+  
+
+  vMax = MAX(aHsvPtr->v, bHsvPtr->v);
+
+  if(vMax <= 0.25)
+  {
+    /* reduce weight of hue and saturation
+     * when comparing 2 dark colors
+     */
+    weight = vMax / 0.25;
+    weight *= (weight * weight);
+    colorDiff = 1.0 - ((1.0 - pow((hDif * weight), colorSensitivity)) * (1.0 -  pow((sDif * weight), colorSensitivity)) * (1.0 -  pow(vDif, colorSensitivity)));
+  }
+  else
+  {
+    sMax = MAX(aHsvPtr->s, bHsvPtr->s);
+    if (sMax <= 0.25)
+    {
+      /* reduce weight of hue and saturation
+       * when comparing 2 gray colors
+       */
+      weight = sMax / 0.25;
+      weight *= (weight * weight);
+      colorDiff = 1.0 - ((1.0 - pow((hDif * weight), colorSensitivity)) * (1.0 -  pow((sDif * weight), colorSensitivity)) * (1.0 -  pow(vDif, colorSensitivity)));
+    }
+    else
+    {
+      weight = 1.0;
+      colorDiff = 1.0 - ((1.0 - pow(hDif, colorSensitivity)) * (1.0 -  pow(sDif, colorSensitivity)) * (1.0 -  pow(vDif, colorSensitivity)));
+    }
+  }
+
+
+
+  if(debugPrint)
+  {
+    printf("HSV: hsv 1/2 (%.3f %.3f %.3f) / (%.3f %.3f %.3f) vMax:%f\n"
+       , aHsvPtr->h
+       , aHsvPtr->s
+       , aHsvPtr->v
+       , bHsvPtr->h
+       , bHsvPtr->s
+       , bHsvPtr->v
+       , vMax
+       );
+    printf("diffHSV:  (%.3f %.3f %.3f)  weight:%.3f colorSensitivity:%.3f colorDiff:%.5f\n"
+       , hDif
+       , sDif
+       , vDif
+       , weight
+       , colorSensitivity
+       , colorDiff
+       );
+  }
+
+  return (colorDiff);
+}  /* end gap_colordiff_GimpHSV */
+
+
+
+/* ---------------------------------
+ * gap_colordiff_guchar_GimpHSV
+ * ---------------------------------
+ * returns difference of 2 colors as gdouble value
+ * in range 0.0 (exact match) to 1.0 (maximal difference)
+ */
+gdouble
+gap_colordiff_guchar_GimpHSV(GimpHSV *aHsvPtr
+                  , guchar *pixel
+                  , gdouble colorSensitivity
+                  , gboolean debugPrint)
+{
+  GimpRGB bRgb;
+  GimpHSV bHsv;
+  gdouble colordiff;
+
+  gimp_rgba_set_uchar (&bRgb, pixel[0], pixel[1], pixel[2], 255);
+  gimp_rgb_to_hsv(&bRgb, &bHsv);
+
+  colordiff = gap_colordiff_GimpHSV(aHsvPtr, &bHsv, colorSensitivity, debugPrint);
+  return (colordiff);
+
+}  /* end gap_colordiff_guchar_GimpHSV */
+
+
+
+
+/* ---------------------------------
+ * gap_colordiff_GimpRGB
+ * ---------------------------------
+ * returns difference of 2 colors as gdouble value
+ * in range 0.0 (exact match) to 1.0 (maximal difference)
+ * Note: 
+ * this procedure uses the same HSV colormodel based
+ * Algorithm as the HSV specific procedures.
+ */
+gdouble
+gap_colordiff_GimpRGB(GimpRGB *aRgbPtr
+                  , GimpRGB *bRgbPtr
+                  , gdouble colorSensitivity
+                  , gboolean debugPrint)
+{
+  GimpHSV aHsv;
+  GimpHSV bHsv;
+  gdouble colordiff;
+
+  gimp_rgb_to_hsv(aRgbPtr, &aHsv);
+  gimp_rgb_to_hsv(bRgbPtr, &bHsv);
+
+
+  colordiff = gap_colordiff_GimpHSV(&aHsv, &bHsv, colorSensitivity, debugPrint);
+  if(debugPrint)
+  {
+    printf("RGB 1/2 (%.3g %.3g %.3g) / (%.3g %.3g %.3g) colordiff:%f\n"
+       , aRgbPtr->r
+       , aRgbPtr->g
+       , aRgbPtr->b
+       , bRgbPtr->r
+       , bRgbPtr->g
+       , bRgbPtr->b
+       , colordiff
+       );
+  }
+  return (colordiff);
+
+}  /* end gap_colordiff_GimpRGB */
+
+
+
+/* ---------------------------------
+ * gap_colordiff_guchar
+ * ---------------------------------
+ * returns difference of 2 colors as gdouble value
+ * in range 0.0 (exact match) to 1.0 (maximal difference)
+ * Note: 
+ * this procedure uses the same HSV colormodel based
+ * Algorithm as the HSV specific procedures.
+ */
+gdouble
+gap_colordiff_guchar(guchar *aPixelPtr
+                   , guchar *bPixelPtr
+                   , gdouble colorSensitivity
+                   , gboolean debugPrint
+                   )
+{
+  GimpRGB aRgb;
+  GimpRGB bRgb;
+
+  gimp_rgba_set_uchar (&aRgb, aPixelPtr[0], aPixelPtr[1], aPixelPtr[2], 255);
+  gimp_rgba_set_uchar (&bRgb, bPixelPtr[0], bPixelPtr[1], bPixelPtr[2], 255);
+  return (gap_colordiff_GimpRGB(&aRgb
+                            , &bRgb
+                            , colorSensitivity
+                            , debugPrint
+                            ));
+
+}  /* end gap_colordiff_guchar */
+
+
+/* ---------------------------------
+ * gap_colordiff_simple_GimpRGB
+ * ---------------------------------
+ * returns difference of 2 colors as gdouble value
+ * in range 0.0 (exact match) to 1.0 (maximal difference)
+ * Note: 
+ * this procedure uses a simple (and faster) algorithm
+ * that is based on the RGB colorspace
+ */
+gdouble
+gap_colordiff_simple_GimpRGB(GimpRGB *aRgbPtr
+                   , GimpRGB *bRgbPtr
+                   , gboolean debugPrint
+                   )
+{
+  gdouble rDif;
+  gdouble gDif;
+  gdouble bDif;
+  gdouble colorDiff;
+
+  rDif = fabs(aRgbPtr->r - bRgbPtr->r);
+  gDif = fabs(aRgbPtr->g - bRgbPtr->g);
+  bDif = fabs(aRgbPtr->b - bRgbPtr->b);
+
+  colorDiff = (rDif + gDif + bDif) / 3.0;
+
+  if(debugPrint)
+  {
+    printf("RGB 1/2 (%.3g %.3g %.3g) / (%.3g %.3g %.3g) rDif:%.3g gDif:%.3g bDif:%.3g colorDiff:%f\n"
+       , aRgbPtr->r
+       , aRgbPtr->g
+       , aRgbPtr->b
+       , bRgbPtr->r
+       , bRgbPtr->g
+       , bRgbPtr->b
+       , rDif
+       , gDif
+       , bDif
+       , colorDiff
+       );
+  }
+  return (colorDiff);
+ 
+
+}  /* end gap_colordiff_simple_GimpRGB */
+
+
+
+
+/* ---------------------------------
+ * gap_colordiff_simple_guchar
+ * ---------------------------------
+ * returns difference of 2 colors as gdouble value
+ * in range 0.0 (exact match) to 1.0 (maximal difference)
+ * Note: 
+ * this procedure uses a simple (and faster) algorithm
+ * that is based on the RGB colorspace
+ * in 1 byte per channel representation.
+ */
+gdouble
+gap_colordiff_simple_guchar(guchar *aPixelPtr
+                   , guchar *bPixelPtr
+                   , gboolean debugPrint
+                   )
+{
+#define MAX_CHANNEL_SUM 765.0
+  int rDif;
+  int gDif;
+  int bDif;
+  gdouble colorDiff;
+
+  rDif = abs(aPixelPtr[0] - bPixelPtr[0]);
+  gDif = abs(aPixelPtr[1] - bPixelPtr[1]);
+  bDif = abs(aPixelPtr[2] - bPixelPtr[2]);
+
+  colorDiff = (gdouble)(rDif + gDif + bDif) / MAX_CHANNEL_SUM;
+  if(debugPrint)
+  {
+    printf("rgb 1/2 (%.3g %.3g %.3g) / (%.3g %.3g %.3g) rDif:%.3g gDif:%.3g bDif:%.3g colorDiff:%f\n"
+       , (float)(aPixelPtr[0]) / 255.0
+       , (float)(aPixelPtr[1]) / 255.0
+       , (float)(aPixelPtr[2]) / 255.0
+       , (float)(bPixelPtr[0]) / 255.0
+       , (float)(bPixelPtr[1]) / 255.0
+       , (float)(bPixelPtr[2]) / 255.0
+       , (float)(rDif) / 255.0
+       , (float)(gDif) / 255.0
+       , (float)(bDif) / 255.0
+       , colorDiff
+       );
+  }
+  return (colorDiff);
+
+}  /* end gap_colordiff_simple_guchar */
diff --git a/gap/gap_colordiff.h b/gap/gap_colordiff.h
new file mode 100644
index 0000000..e0c6de5
--- /dev/null
+++ b/gap/gap_colordiff.h
@@ -0,0 +1,142 @@
+/* gap_colordiff.h
+ *    by hof (Wolfgang Hofer)
+ *    color difference procedures
+ *  2010/08/06
+ *
+ */
+/* 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:
+ * version 2.7.0;             hof: created
+ */
+
+#ifndef _GAP_COLORDIFF_H
+#define _GAP_COLORDIFF_H
+
+/* SYTEM (UNIX) includes */
+#include <stdio.h>
+#include <stdlib.h>
+
+/* GIMP includes */
+#include "gtk/gtk.h"
+#include "libgimp/gimp.h"
+
+
+#define GAP_COLORDIFF_DEFAULT_SENSITIVITY 1.35
+
+/* ---------------------------------
+ * gap_colordiff_GimpHSV
+ * ---------------------------------
+ * returns difference of 2 colors as gdouble value
+ * in range 0.0 (exact match) to 1.0 (maximal difference)
+ *
+ * use this procedure in case both colors are already availabe in GimpHSV color.
+ */
+gdouble
+gap_colordiff_GimpHSV(GimpHSV *aHsvPtr
+                  , GimpHSV *bHsvPtr
+                  , gdouble colorSensitivity
+                  , gboolean debugPrint);
+
+
+/* ---------------------------------
+ * gap_colordiff_guchar_GimpHSV
+ * ---------------------------------
+ * returns difference of 2 colors as gdouble value
+ * in range 0.0 (exact match) to 1.0 (maximal difference)
+ */
+gdouble
+gap_colordiff_guchar_GimpHSV(GimpHSV *aHsvPtr
+                  , guchar *pixel
+                  , gdouble colorSensitivity
+                  , gboolean debugPrint);
+
+
+/* ---------------------------------
+ * gap_colordiff_GimpRGB
+ * ---------------------------------
+ * returns difference of 2 colors as gdouble value
+ * in range 0.0 (exact match) to 1.0 (maximal difference)
+ *
+ * use this procedure in case both colors are availabe in GimpRGB color.
+ * Note: 
+ * this procedure uses the same HSV colormodel based
+ * Algorithm as the HSV specific procedures.
+ */
+gdouble  gap_colordiff_GimpRGB(GimpRGB *color1
+                            , GimpRGB *color2
+			    , gdouble sensitivity
+                            , gboolean debugPrint
+                            );
+
+/* ---------------------------------
+ * gap_colordiff_guchar
+ * ---------------------------------
+ * returns difference of 2 colors as gdouble value
+ * in range 0.0 (exact match) to 1.0 (maximal difference)
+ *
+ * use this procedure in case where both colors are available
+ * as rgb byte value components in the range 0 to 255.
+ * Note: 
+ * this procedure uses the same HSV colormodel based
+ * Algorithm as the HSV specific procedures.
+ */
+gdouble
+gap_colordiff_guchar(guchar *aPixelPtr
+                   , guchar *bPixelPtr
+                   , gdouble colorSensitivity
+                   , gboolean debugPrint
+                   );
+
+
+/* ---------------------------------
+ * gap_colordiff_simple_GimpRGB
+ * ---------------------------------
+ * returns difference of 2 colors as gdouble value
+ * in range 0.0 (exact match) to 1.0 (maximal difference)
+ * Note: 
+ * this procedure uses a simple (and faster) algorithm
+ * that is based on the RGB colorspace
+ */
+gdouble
+gap_colordiff_simple_GimpRGB(GimpRGB *aRgbPtr
+                   , GimpRGB *bRgbPtr
+                   , gboolean debugPrint
+                   );
+
+
+/* ---------------------------------
+ * gap_colordiff_simple_guchar
+ * ---------------------------------
+ * returns difference of 2 colors as gdouble value
+ * in range 0.0 (exact match) to 1.0 (maximal difference)
+ * Note: 
+ * this procedure uses a simple (and faster) algorithm
+ * that is based on the RGB colorspace
+ * PixelPtr must point to guchar data of at least 3 bytes size
+ * sequence RGB
+ */
+gdouble
+gap_colordiff_simple_guchar(guchar *aPixelPtr
+                   , guchar *bPixelPtr
+                   , gboolean debugPrint
+                   );
+
+
+#endif
diff --git a/gap/gap_colormask_dialog.c b/gap/gap_colormask_dialog.c
new file mode 100644
index 0000000..10bc58d
--- /dev/null
+++ b/gap/gap_colormask_dialog.c
@@ -0,0 +1,1848 @@
+/* gap_colormask_dialog.c
+ *    by hof (Wolfgang Hofer)
+ *    colormask filter dialog handling procedures
+ *  2010/02/25
+ *
+ */
+/* 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:
+ * version 2.7.0;             hof: created
+ */
+
+#include "config.h"
+
+/* SYTEM (UNIX) includes */
+#include "string.h"
+
+/* GIMP includes */
+#include <gtk/gtk.h>
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+
+/* GAP includes */
+#include "gap-intl.h"
+#include "gap_colordiff.h"
+#include "gap_lib_common_defs.h"
+#include "gap_pdb_calls.h"
+#include "gap_colormask_file.h"
+#include "gap_colormask_exec.h"
+#include "gap_libgapbase.h"
+#include "gap_colormask_dialog.h"
+
+#define PLUG_IN_BINARY      "gapcolormask"
+#define PLUG_IN_NAME_HELP   "plug-in-layer-set-alpha-by-colormask"
+
+#define SCALE_WIDTH       140
+
+
+typedef struct {
+   char            *paramFilename;
+   GimpDrawable    *layer_drawable;        /* the layer to be processed */
+   GimpRGB          layercolor;
+   GimpRGB          maskcolor;
+   GimpRGB          nbcolor;
+   gboolean         showExpertOptions;
+   GtkWidget       *baseFrame;
+   GtkWidget       *filterFrame;
+   GtkWidget       *opacityFrame;
+   GtkWidget       *expertFrame;
+   GtkWidget       *debugFrame;
+   GtkWidget       *keycolorButton;
+   GtkWidget       *layercolorButton;
+   GtkWidget       *maskcolorButton;
+   GtkWidget       *nbcolorButton;
+   GtkWidget       *colordiffLabel;
+   GtkWidget       *nbColordiffLabel;
+   GimpPixelFetcher *pftMask;
+   GimpPixelFetcher *pftDest;
+
+
+  gint               mouse_x;
+  gint               mouse_y;
+  //gint               drag_mode;
+
+  GtkObject         *offset_adj_x;
+  GtkObject         *offset_adj_y;
+
+  guchar           **src_rows;
+  guchar           **bm_rows;
+
+  GimpDrawable      *colormask_drawable;        /* the colormask to be applied */
+  gint               bm_width;
+  gint               bm_height;
+  gint               bm_bpp;
+  gboolean           bm_has_alpha;
+
+  GimpPixelRgn       src_rgn;
+  GimpPixelRgn       colormask_rgn;
+
+  GimpPreview       *preview;
+  gboolean           applyImmediate;
+  GapColormaskValues *cmaskvalsPtr;
+
+//  GapColormaskValues params;
+///  gint run;
+} CmaskInterface;
+
+
+extern      int gap_debug; /* ==0  ... dont print debug infos */
+
+static CmaskInterface cmaskint =
+{
+  NULL,         /* paramFilename */
+  NULL,         /* GimpDrawable    *layer_drawable */
+  { 0,0,0,0 },  /* GimpRGB layercolor */
+  { 0,0,0,0 },  /* GimpRGB maskcolor */
+  { 0,0,0,0 },  /* GimpRGB nbcolor */
+  FALSE,        /* showExpertOptions */
+  NULL,         /*   GtkWidget       *baseFrame; */
+  NULL,         /*   GtkWidget       *filterFrame; */
+  NULL,         /*   GtkWidget       *opacityFrame; */
+  NULL,         /*   GtkWidget       *expertFrame; */
+  NULL,         /*   GtkWidget       *debugFrame; */
+  NULL,         /* GtkWidget  *keycolorButton */
+  NULL,         /* GtkWidget  *layercolorButton */
+  NULL,         /* GtkWidget  *maskcolorButton */
+  NULL,         /* GtkWidget  *nbcolorButton */
+  NULL,         /* GtkWidget  *colordiffLabel */
+  NULL,         /* GtkWidget  *nbColordiffLabel */
+  NULL,         /* GimpPixelFetcher *pftMask */
+  NULL,         /* GimpPixelFetcher *pftDest */
+
+
+  0,         /* mouse_x */
+  0,         /* mouse_y */
+  // DRAG_NONE, /* drag_mode */
+  NULL,      /* offset_adj_x */
+  NULL,      /* offset_adj_y */
+  NULL,      /* src_rows */
+  NULL,      /* bm_rows */
+  NULL,      /* colormask_drawable */
+  0,         /* bm_width */
+  0,         /* bm_height */
+  0,         /* bm_bpp */
+  FALSE,     /* bm_has_alpha */
+  { 0, },    /* src_rgn */
+  { 0, },    /* colormask_rgn */
+  //{ 0, }     /* params */
+  NULL,      /* preview */
+  FALSE,
+  NULL       /*  cmaskvalsPtr */
+};
+
+static void     p_keycolor_update_callback(GtkWidget *widget, gpointer val);
+static void     p_color_update_callback(GtkWidget *widget, gpointer val);
+static void     p_fetch_layercolor_and_maskcolor(gint x, gint y);
+
+static void     p_dialog_new_colormask     (gboolean       init_offsets);
+static void     dialog_update_preview      (GimpPreview   *preview);
+static gint     dialog_preview_events      (GtkWidget     *area,
+                                            GdkEvent      *event,
+                                            GimpPreview   *preview);
+static void     dialog_get_rows            (GimpPixelRgn  *pr,
+                                            guchar       **rows,
+                                            gint           x,
+                                            gint           y,
+                                            gint           width,
+                                            gint           height);
+static void     dialog_fill_src_rows       (gint           start,
+                                            gint           how_many,
+                                            gint           yofs);
+static void     dialog_fill_bumpmap_rows   (gint           start,
+                                            gint           how_many,
+                                            gint           yofs);
+static gint     dialog_constrain           (gint32         image_id,
+                                            gint32         drawable_id,
+                                            gpointer       data);
+static void     dialog_colormask_callback  (GtkWidget     *widget,
+                                            GimpPreview   *preview);
+
+static void     p_update_colorfiff_labels(GtkWidget *widget, gpointer val);
+static void     p_update_option_frames_visiblity_callback(GtkWidget     *widget, gpointer      data);
+
+/* -----------------------------------------------
+ * p_set_widget_sensitivity_dependent_to_algorithm
+ * -----------------------------------------------
+ */
+static void
+p_set_widget_sensitivity_dependent_to_algorithm(GtkWidget *widget, gpointer val)
+{
+  printf("TODO p_set_widget_sensitivity_dependent_to_algorithm\n");  // TODO
+
+}  /* end p_set_widget_sensitivity_dependent_to_algorithm */
+
+
+
+
+/* -------------------------------
+ * p_create_base_options
+ * -------------------------------
+ *
+ */
+static GtkWidget*
+p_create_base_options(GapColormaskValues *cmaskvals, GtkWidget *preview)
+{
+  GtkWidget *frame;
+  GtkWidget *vbox;
+  GtkWidget *table;
+  GtkWidget *combo;
+  GtkWidget *button;
+  GtkObject *adj;
+  gint       row = 0;
+
+  /* the frame */
+  frame = gimp_frame_new (_("Base Options"));
+
+  gtk_widget_show (frame);
+  gtk_container_set_border_width (GTK_CONTAINER (frame), 2);
+
+  vbox = gtk_vbox_new (FALSE, 0);
+  gtk_container_add (GTK_CONTAINER (frame), vbox);
+  gtk_widget_show (vbox);
+
+
+
+  /* table */
+  table = gtk_table_new (4, 3, FALSE);
+  gtk_table_set_col_spacings (GTK_TABLE (table), 4);
+  gtk_table_set_row_spacings (GTK_TABLE (table), 4);
+  gtk_widget_show (table);
+  gtk_box_pack_start (GTK_BOX (vbox), table, TRUE, TRUE, 0);
+  gtk_container_set_border_width (GTK_CONTAINER (table), 4);
+
+
+
+
+
+
+  /* menu to select the colormask drawable */
+  combo = gimp_drawable_combo_box_new (dialog_constrain, NULL);
+  gimp_int_combo_box_connect (GIMP_INT_COMBO_BOX (combo), cmaskvals->colormask_id,
+                              G_CALLBACK (dialog_colormask_callback),
+                              preview);
+
+  gimp_table_attach_aligned (GTK_TABLE (table), 0, row++,
+                             _("Color mask:"), 0.0, 0.5, combo, 2, FALSE);
+
+
+
+  /* loColorThreshold for colordiffence range */
+  adj = gimp_scale_entry_new (GTK_TABLE (table), 0, row++,
+                              _("Colordiff threshold:"), SCALE_WIDTH, 6,
+                              cmaskvals->loColorThreshold, 0.0, 1.0, 0.01, 0.1, 4,
+                              TRUE, 0, 0,
+                              _("Colordiff lower threshold. "
+                                "pixels that differ in color less than this value "
+                                "(compared with the corresponding pixel in the colormask) "
+                                "are set to lower opacity value (usually transparent)"),
+                               NULL);
+  g_signal_connect (adj, "value-changed",
+                    G_CALLBACK (gimp_double_adjustment_update),
+                    &cmaskvals->loColorThreshold);
+  g_signal_connect_swapped (adj, "value-changed",
+                            G_CALLBACK (gimp_preview_invalidate),
+                            preview);
+
+  /* hiColorThreshold for colordiffence range */
+  adj = gimp_scale_entry_new (GTK_TABLE (table), 0, row++,
+                              _("_HiColordiff threshold:"), SCALE_WIDTH, 6,
+                              cmaskvals->hiColorThreshold, 0.0, 1.0, 0.01, 0.1, 4,
+                              TRUE, 0, 0,
+                              _("Colordiff upper threshold. "
+                                "pixels that differ in color more than this value "
+                                "(compared with the corresponding pixel in the colormask) "
+                                "are set to upper opacity value (usually opaque)"),
+                              NULL);
+  g_signal_connect (adj, "value-changed",
+                    G_CALLBACK (gimp_double_adjustment_update),
+                    &cmaskvals->hiColorThreshold);
+  g_signal_connect_swapped (adj, "value-changed",
+                            G_CALLBACK (gimp_preview_invalidate),
+                            preview);
+
+
+
+
+  /* keep layermask checkbutton */
+  button = gtk_check_button_new_with_mnemonic (_("Keep layermask"));
+  gtk_table_attach_defaults (GTK_TABLE (table), button, 0, 3, row, row + 1);
+  gtk_widget_show (button);
+  row++;
+
+  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), cmaskvals->keepLayerMask);
+  g_signal_connect (button, "toggled",
+                    G_CALLBACK (gimp_toggle_button_update),
+                    &cmaskvals->keepLayerMask);
+  g_signal_connect_swapped (button, "toggled",
+                            G_CALLBACK (gimp_preview_invalidate),
+                            preview);
+
+
+
+  /* apply immediate checkbutton */
+  button = gtk_check_button_new_with_mnemonic (_("Apply Immediate"));
+  gtk_table_attach_defaults (GTK_TABLE (table), button, 0, 3, row, row + 1);
+  gtk_widget_show (button);
+  row++;
+
+  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), cmaskint.applyImmediate);
+  g_signal_connect (button, "toggled",
+                    G_CALLBACK (gimp_toggle_button_update),
+                    &cmaskint.applyImmediate);
+  g_signal_connect_swapped (button, "toggled",
+                            G_CALLBACK (gimp_preview_invalidate),
+                            preview);
+
+
+  /* show expert options checkbutton */
+  button = gtk_check_button_new_with_mnemonic (_("Show All Options"));
+  gtk_table_attach_defaults (GTK_TABLE (table), button, 0, 3, row, row + 1);
+  gtk_widget_show (button);
+  row++;
+
+  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), cmaskint.showExpertOptions);
+  g_signal_connect (button, "toggled",
+                     G_CALLBACK (gimp_toggle_button_update),
+                     &cmaskint.showExpertOptions);
+  g_signal_connect_swapped (button, "toggled",
+                            G_CALLBACK (p_update_option_frames_visiblity_callback),
+                            NULL);
+
+  return (frame);
+
+}  /* end p_create_base_options */
+
+
+/* -------------------------------
+ * p_create_filter_options
+ * -------------------------------
+ *
+ */
+static GtkWidget*
+p_create_filter_options(GapColormaskValues *cmaskvals, GtkWidget *preview)
+{
+  GtkWidget *frame;
+  GtkWidget *vbox;
+  GtkWidget *table;
+  GtkObject *adj;
+  gint       row = 0;
+
+  /* the frame */
+  frame = gimp_frame_new (_("Filter Options"));
+
+  gtk_widget_show (frame);
+  gtk_container_set_border_width (GTK_CONTAINER (frame), 2);
+
+  vbox = gtk_vbox_new (FALSE, 0);
+  gtk_container_add (GTK_CONTAINER (frame), vbox);
+  gtk_widget_show (vbox);
+
+
+
+  /* table  */
+  table = gtk_table_new (4, 3, FALSE);
+  gtk_table_set_col_spacings (GTK_TABLE (table), 4);
+  gtk_table_set_row_spacings (GTK_TABLE (table), 4);
+  gtk_widget_show (table);
+  gtk_box_pack_start (GTK_BOX (vbox), table, TRUE, TRUE, 0);
+  gtk_container_set_border_width (GTK_CONTAINER (table), 4);
+
+
+
+
+
+
+  /* isleRadius in pixels (for removing isolated pixels) */
+  adj = gimp_scale_entry_new (GTK_TABLE (table), 0, row++,
+                              _("Isle Radius:"), SCALE_WIDTH, 6,
+                              cmaskvals->isleRadius, 0.0, 15.0, 1.0, 1.0, 0,
+                              TRUE, 0, 0,
+                              _("Isle removal radius in pixels (use value 0 to disable removal of isolated pixels)"),
+                              NULL);
+  g_signal_connect (adj, "value-changed",
+                    G_CALLBACK (gimp_double_adjustment_update),
+                    &cmaskvals->isleRadius);
+  g_signal_connect_swapped (adj, "value-changed",
+                            G_CALLBACK (gimp_preview_invalidate),
+                            preview);
+
+  /* isleAreaPixelLimit in pixels (for removing isolated pixels) */
+  adj = gimp_scale_entry_new (GTK_TABLE (table), 0, row++,
+                              _("Isle Area:"), SCALE_WIDTH, 6,
+                              cmaskvals->isleAreaPixelLimit, 0.0, 30.0, 1.0, 1.0, 0,
+                              TRUE, 0, 0,
+                              _("Isle Area size in pixels. "
+                                "small isolated opaque or transparent pixel areas below that size "
+                                "are removed (e.g toggled from opaque to transparent and vice versa)"),
+                              NULL);
+  g_signal_connect (adj, "value-changed",
+                    G_CALLBACK (gimp_double_adjustment_update),
+                    &cmaskvals->isleAreaPixelLimit);
+  g_signal_connect_swapped (adj, "value-changed",
+                            G_CALLBACK (gimp_preview_invalidate),
+                            preview);
+
+
+
+  /* featherRadius in pixels */
+  adj = gimp_scale_entry_new (GTK_TABLE (table), 0, row++,
+                              _("Feather Radius:"), SCALE_WIDTH, 6,
+                              cmaskvals->featherRadius, 0.0, 10.0, 1.0, 1.0, 0,
+                              TRUE, 0, 0,
+                              _("feather radius in pixels (use value 0 to disable feathering)"),
+                              NULL);
+  g_signal_connect (adj, "value-changed",
+                    G_CALLBACK (gimp_double_adjustment_update),
+                    &cmaskvals->featherRadius);
+  g_signal_connect_swapped (adj, "value-changed",
+                            G_CALLBACK (gimp_preview_invalidate),
+                            preview);
+
+
+
+  return (frame);
+
+}  /* end p_create_filter_options */
+
+
+
+
+/* -------------------------------
+ * p_create_opacity_options
+ * -------------------------------
+ *
+ */
+static GtkWidget*
+p_create_opacity_options(GapColormaskValues *cmaskvals, GtkWidget *preview)
+{
+  GtkWidget *frame;
+  GtkWidget *vbox;
+  GtkWidget *table;
+  GtkWidget *button;
+  GtkWidget *label;
+  GtkObject *adj;
+  gint       row = 0;
+
+  /* the frame */
+  frame = gimp_frame_new (_("Opacity Options"));
+
+  gtk_widget_show (frame);
+  gtk_container_set_border_width (GTK_CONTAINER (frame), 2);
+
+  vbox = gtk_vbox_new (FALSE, 0);
+  gtk_container_add (GTK_CONTAINER (frame), vbox);
+  gtk_widget_show (vbox);
+
+
+
+  /* table  */
+  table = gtk_table_new (4, 3, FALSE);
+  gtk_table_set_col_spacings (GTK_TABLE (table), 4);
+  gtk_table_set_row_spacings (GTK_TABLE (table), 4);
+  gtk_widget_show (table);
+  gtk_box_pack_start (GTK_BOX (vbox), table, TRUE, TRUE, 0);
+  gtk_container_set_border_width (GTK_CONTAINER (table), 4);
+
+
+
+  /* lowerOpacity for colordiffence range */
+  adj = gimp_scale_entry_new (GTK_TABLE (table), 0, row,
+                              _("Lower opacity:"), SCALE_WIDTH, 6,
+                              cmaskvals->lowerOpacity, 0.0, 1.0, 0.01, 0.1, 4,
+                              TRUE, 0, 0,
+                              _("Lower opacity value is set for pixels with color difference "
+                                "less than Colordiff threshold (use value 0 for transparency)"),
+                              NULL);
+  g_signal_connect (adj, "value-changed",
+                    G_CALLBACK (gimp_double_adjustment_update),
+                    &cmaskvals->lowerOpacity);
+  g_signal_connect_swapped (adj, "value-changed",
+                            G_CALLBACK (gimp_preview_invalidate),
+                            preview);
+
+  row++;
+
+  /* upperOpacity for colordiffence range */
+  adj = gimp_scale_entry_new (GTK_TABLE (table), 0, row,
+                              _("Upper opacity:"), SCALE_WIDTH, 6,
+                              cmaskvals->upperOpacity, 0.0, 1.0, 0.01, 0.1, 4,
+                              TRUE, 0, 0,
+                              _("Upper opacity is set for pixels with color difference "
+                                "greater than High Colordiff threshold (use value 1 for opacity)"),
+                              NULL);
+  g_signal_connect (adj, "value-changed",
+                    G_CALLBACK (gimp_double_adjustment_update),
+                    &cmaskvals->upperOpacity);
+  g_signal_connect_swapped (adj, "value-changed",
+                            G_CALLBACK (gimp_preview_invalidate),
+                            preview);
+
+
+  row++;
+
+  /* trigger alpha (only relevant if colormask has alpha channel) */
+  adj = gimp_scale_entry_new (GTK_TABLE (table), 0, row,
+                              _("Trigger alpha:"), SCALE_WIDTH, 6,
+                              cmaskvals->triggerAlpha, 0.0, 1.0, 0.1, 0.1, 4,
+                              TRUE, 0, 0,
+                              _("Trigger alpha is only relevant in case the color mask has an alpha channel. "
+                                "All pixels where the alpha channel of the corresponding pixel "
+                                "in the color mask is below this trigger value "
+                                "are not changed (e.g. keep their original opacity)"),
+                              NULL);
+  g_signal_connect (adj, "value-changed",
+                    G_CALLBACK (gimp_double_adjustment_update),
+                    &cmaskvals->triggerAlpha);
+  g_signal_connect_swapped (adj, "value-changed",
+                            G_CALLBACK (gimp_preview_invalidate),
+                            preview);
+
+
+  return (frame);
+
+}  /* end p_create_opacity_options */
+
+
+/* -------------------------------
+ * p_create_expert_options
+ * -------------------------------
+ * 
+ */
+static GtkWidget*
+p_create_expert_options(GapColormaskValues *cmaskvals, GtkWidget *preview)
+{
+  GtkWidget *frame;
+  GtkWidget *vbox;
+  GtkWidget *table;
+  GtkWidget *button;
+  GtkWidget *label;
+  GtkObject *adj;
+  gint       row = 0;
+
+  /* the frame */
+  frame = gimp_frame_new (_("Expert Options"));
+
+  gtk_widget_show (frame);
+  gtk_container_set_border_width (GTK_CONTAINER (frame), 2);
+
+  vbox = gtk_vbox_new (FALSE, 0);
+  gtk_container_add (GTK_CONTAINER (frame), vbox);
+  gtk_widget_show (vbox);
+
+
+
+  /* table  */
+  table = gtk_table_new (4, 3, FALSE);
+  gtk_table_set_col_spacings (GTK_TABLE (table), 4);
+  gtk_table_set_row_spacings (GTK_TABLE (table), 4);
+  gtk_widget_show (table);
+  gtk_box_pack_start (GTK_BOX (vbox), table, TRUE, TRUE, 0);
+  gtk_container_set_border_width (GTK_CONTAINER (table), 4);
+
+
+
+
+
+
+  /* algorithm */
+  adj = gimp_scale_entry_new (GTK_TABLE (table), 0, row++,
+                              _("_Algorithm:"), SCALE_WIDTH, 6,
+                              cmaskvals->algorithm, 0.0, GAP_COLORMASK_ALGO_MAX, 1.0, 1.0, 0,
+                              TRUE, 0, 0,
+                              NULL, NULL);
+  g_signal_connect (adj, "value-changed",
+                    G_CALLBACK (gimp_int_adjustment_update),
+                    &cmaskvals->algorithm);
+  g_signal_connect_swapped (adj, "value-changed",
+                            G_CALLBACK (gimp_preview_invalidate),
+                            preview);
+  g_signal_connect_swapped (adj, "value-changed",
+                            G_CALLBACK (p_set_widget_sensitivity_dependent_to_algorithm),
+                            preview);
+
+
+
+  /* --------------- start of keycolor widgets  ------------- */
+  
+  /* enableKeyColorThreshold checkbutton */
+  button = gtk_check_button_new_with_mnemonic (_("Enable individual color threshold for the key color"));
+  gtk_table_attach_defaults (GTK_TABLE (table), button, 0, 3, row, row + 1);
+  gtk_widget_show (button);
+
+  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), cmaskvals->enableKeyColorThreshold);
+  g_signal_connect (button, "toggled",
+                    G_CALLBACK (gimp_toggle_button_update),
+                    &cmaskvals->enableKeyColorThreshold);
+  g_signal_connect_swapped (button, "toggled",
+                            G_CALLBACK (gimp_preview_invalidate),
+                            cmaskint.preview);
+  // TODO add another callback with g_signal_connect_swapped
+  // to update widget sensitivity (en/disable keycolorButton, keyColorThreshold and keyColorSensitivity
+
+  row++;
+
+
+  /* the keycolor label */
+  label = gtk_label_new (_("Key color:"));
+  gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.0);
+  gtk_table_attach_defaults (GTK_TABLE(table), label, 0, 1, row, row+1);
+  gtk_widget_show (label);
+
+  /* the keycolor button */
+  button = gimp_color_button_new (_("Key color"),
+                                  40, 20,                     /* WIDTH, HEIGHT, */
+                                  &cmaskvals->keycolor,
+                                  GIMP_COLOR_AREA_FLAT);
+  gtk_table_attach (GTK_TABLE (table), button, 1, 3, row, row+1,
+                    GTK_FILL | GTK_EXPAND, GTK_SHRINK, 0, 0) ;
+  gtk_widget_show (button);
+  cmaskint.keycolorButton = button;
+
+  g_signal_connect (button, "color_changed",
+                    G_CALLBACK (p_keycolor_update_callback),
+                    &cmaskvals->keycolor);
+
+  g_signal_connect_swapped (button, "color_changed",
+                             G_CALLBACK (gimp_preview_invalidate),
+                             cmaskint.preview);
+
+  row++;
+
+  /* keyColorThreshold (individual lo threshold for the key color) */
+  adj = gimp_scale_entry_new (GTK_TABLE (table), 0, row,
+                              _("Key Colordiff threshold:"), SCALE_WIDTH, 6,
+                              cmaskvals->loKeyColorThreshold, 0.0, 1.0, 0.01, 0.1, 4,
+                              TRUE, 0, 0,
+                              NULL, NULL);
+  g_signal_connect (adj, "value-changed",
+                    G_CALLBACK (gimp_double_adjustment_update),
+                    &cmaskvals->loKeyColorThreshold);
+  g_signal_connect_swapped (adj, "value-changed",
+                            G_CALLBACK (gimp_preview_invalidate),
+                            cmaskint.preview);
+
+  row++;
+
+  /* keyColorThreshold (individual lo color threshold for the key color) */
+  adj = gimp_scale_entry_new (GTK_TABLE (table), 0, row,
+                              _("Key Color Sensitivity:"), SCALE_WIDTH, 6,
+                              cmaskvals->keyColorSensitivity, 0.0, 10.0, 0.1, 1.0, 4,
+                              TRUE, 0, 0,
+                              NULL, NULL);
+  g_signal_connect (adj, "value-changed",
+                    G_CALLBACK (gimp_double_adjustment_update),
+                    &cmaskvals->keyColorSensitivity);
+  g_signal_connect_swapped (adj, "value-changed",
+                            G_CALLBACK (gimp_preview_invalidate),
+                            cmaskint.preview);
+
+
+  /* --------------- end of keycolor widgets  ------------- */
+
+  row++;
+
+
+
+  /* significant colordiff threshold */
+  adj = gimp_scale_entry_new (GTK_TABLE (table), 0, row,
+                              _("Sig Colordiff Threshold:"), SCALE_WIDTH, 6,
+                              cmaskvals->significantColordiff, 0.0, 1.0, 0.1, 1.0, 4,
+                              TRUE, 0, 0,
+                              NULL, NULL);
+  g_signal_connect (adj, "value-changed",
+                    G_CALLBACK (gimp_double_adjustment_update),
+                    &cmaskvals->significantColordiff);
+  g_signal_connect_swapped (adj, "value-changed",
+                            G_CALLBACK (gimp_preview_invalidate),
+                            cmaskint.preview);
+  row++;
+
+  /* significant brightness difference threshold */
+  adj = gimp_scale_entry_new (GTK_TABLE (table), 0, row,
+                              _("Sig Brightness Threshold:"), SCALE_WIDTH, 6,
+                              cmaskvals->significantBrightnessDiff, 0.0, 1.0, 0.1, 1.0, 4,
+                              TRUE, 0, 0,
+                              NULL, NULL);
+  g_signal_connect (adj, "value-changed",
+                    G_CALLBACK (gimp_double_adjustment_update),
+                    &cmaskvals->significantBrightnessDiff);
+  g_signal_connect_swapped (adj, "value-changed",
+                            G_CALLBACK (gimp_preview_invalidate),
+                            cmaskint.preview);
+
+  row++;
+
+  /* significant brightness difference threshold */
+  adj = gimp_scale_entry_new (GTK_TABLE (table), 0, row,
+                              _("Sig Radius:"), SCALE_WIDTH, 6,
+                              cmaskvals->significantRadius, 0.0, 10.0, 1.0, 1.0, 0,
+                              TRUE, 0, 0,
+                              NULL, NULL);
+  g_signal_connect (adj, "value-changed",
+                    G_CALLBACK (gimp_double_adjustment_update),
+                    &cmaskvals->significantRadius);
+  g_signal_connect_swapped (adj, "value-changed",
+                            G_CALLBACK (gimp_preview_invalidate),
+                            cmaskint.preview);
+
+
+  row++;
+
+
+  /* edgeColorThreshold for colordiffence range */
+  adj = gimp_scale_entry_new (GTK_TABLE (table), 0, row,
+                              _("_Edge Colordiff threshold:"), SCALE_WIDTH, 6,
+                              cmaskvals->edgeColorThreshold, 0.0, 1.0, 0.01, 0.1, 4,
+                              TRUE, 0, 0,
+                              NULL, NULL);
+  g_signal_connect (adj, "value-changed",
+                    G_CALLBACK (gimp_double_adjustment_update),
+                    &cmaskvals->edgeColorThreshold);
+  g_signal_connect_swapped (adj, "value-changed",
+                            G_CALLBACK (gimp_preview_invalidate),
+                            preview);
+
+  row++;
+
+  /* thresholdColorArea for colordiffence range */
+  adj = gimp_scale_entry_new (GTK_TABLE (table), 0, row,
+                              _("_Area Colordiff threshold:"), SCALE_WIDTH, 6,
+                              cmaskvals->thresholdColorArea, 0.0, 1.0, 0.01, 0.1, 4,
+                              TRUE, 0, 0,
+                              NULL, NULL);
+  g_signal_connect (adj, "value-changed",
+                    G_CALLBACK (gimp_double_adjustment_update),
+                    &cmaskvals->thresholdColorArea);
+  g_signal_connect_swapped (adj, "value-changed",
+                            G_CALLBACK (gimp_preview_invalidate),
+                            preview);
+
+  row++;
+
+  /* pixelDiagonal in pixels (for small area detection) */
+  adj = gimp_scale_entry_new (GTK_TABLE (table), 0, row,
+                              _("Small Area Diagonal:"), SCALE_WIDTH, 6,
+                              cmaskvals->pixelDiagonal, 0.0, 50.0, 1.0, 1.0, 0,
+                              TRUE, 0, 0,
+                              NULL, NULL);
+  g_signal_connect (adj, "value-changed",
+                    G_CALLBACK (gimp_double_adjustment_update),
+                    &cmaskvals->pixelDiagonal);
+  g_signal_connect_swapped (adj, "value-changed",
+                            G_CALLBACK (gimp_preview_invalidate),
+                            preview);
+  row++;
+
+  /* pixelAreaLimit in pixels (for small area detection) */
+  adj = gimp_scale_entry_new (GTK_TABLE (table), 0, row,
+                              _("Small Area Pixelsize:"), SCALE_WIDTH, 6,
+                              cmaskvals->pixelAreaLimit, 0.0, 1000.0, 1.0, 1.0, 0,
+                              TRUE, 0, 0,
+                              NULL, NULL);
+  g_signal_connect (adj, "value-changed",
+                    G_CALLBACK (gimp_double_adjustment_update),
+                    &cmaskvals->pixelAreaLimit);
+  g_signal_connect_swapped (adj, "value-changed",
+                            G_CALLBACK (gimp_preview_invalidate),
+                            preview);
+
+
+  return (frame);
+
+}  /* end p_create_expert_options */
+
+
+/* -------------------------------
+ * p_create_debug_options
+ * -------------------------------
+ *
+ */
+static GtkWidget*
+p_create_debug_options(GapColormaskValues *cmaskvals, GtkWidget *preview)
+{
+  GtkWidget *frame;
+  GtkWidget *vbox;
+  GtkWidget *table;
+  GtkWidget *button;
+  GtkWidget *label;
+  GtkObject *adj;
+  gint       row = 0;
+
+  /* the frame */
+  frame = gimp_frame_new (_("DEBUG Options"));
+
+  gtk_widget_show (frame);
+  gtk_container_set_border_width (GTK_CONTAINER (frame), 2);
+
+  vbox = gtk_vbox_new (FALSE, 0);
+  gtk_container_add (GTK_CONTAINER (frame), vbox);
+  gtk_widget_show (vbox);
+
+
+
+  /* table  */
+  table = gtk_table_new (4, 3, FALSE);
+  gtk_table_set_col_spacings (GTK_TABLE (table), 4);
+  gtk_table_set_row_spacings (GTK_TABLE (table), 4);
+  gtk_widget_show (table);
+  gtk_box_pack_start (GTK_BOX (vbox), table, TRUE, TRUE, 0);
+  gtk_container_set_border_width (GTK_CONTAINER (table), 4);
+
+
+
+
+
+
+
+  /* colorSensitivity  */
+  adj = gimp_scale_entry_new (GTK_TABLE (table), 0, row++,
+                              _("DiffSensitvity:"), SCALE_WIDTH, 6,
+                              cmaskvals->colorSensitivity, 1.0, 2.0, 0.01, 0.1, 4,
+                              TRUE, 0, 0,
+                              NULL, NULL);
+  g_signal_connect (adj, "value-changed",
+                    G_CALLBACK (gimp_double_adjustment_update),
+                    &cmaskvals->colorSensitivity);
+  g_signal_connect_swapped (adj, "value-changed",
+                            G_CALLBACK (gimp_preview_invalidate),
+                            preview);
+  g_signal_connect_swapped (adj, "value-changed",
+                            G_CALLBACK (p_update_colorfiff_labels),
+                            NULL);
+
+
+
+
+  /* connectByCorner checkbutton */
+  button = gtk_check_button_new_with_mnemonic (_("DEBUG Connect by corner (+ use RGB colordiff)"));
+  gtk_table_attach_defaults (GTK_TABLE (table), button, 0, 3, row, row + 1);
+  gtk_widget_show (button);
+  row++;
+
+  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), cmaskvals->connectByCorner);
+  g_signal_connect (button, "toggled",
+                    G_CALLBACK (gimp_toggle_button_update),
+                    &cmaskvals->connectByCorner);
+  g_signal_connect_swapped (button, "toggled",
+                            G_CALLBACK (gimp_preview_invalidate),
+                            preview);
+
+
+
+  /* keep worklayer checkbutton */
+  button = gtk_check_button_new_with_mnemonic (_("DEBUG Keep worklayer"));
+  gtk_table_attach_defaults (GTK_TABLE (table), button, 0, 3, row, row + 1);
+  gtk_widget_show (button);
+  row++;
+
+  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), cmaskvals->keepWorklayer);
+  g_signal_connect (button, "toggled",
+                    G_CALLBACK (gimp_toggle_button_update),
+                    &cmaskvals->keepWorklayer);
+  g_signal_connect_swapped (button, "toggled",
+                            G_CALLBACK (gimp_preview_invalidate),
+                            preview);
+
+
+
+
+
+
+
+  row++;
+
+  /* the layercolor label */
+  label = gtk_label_new (_("Layer color:"));
+  gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.0);
+  gtk_table_attach_defaults (GTK_TABLE(table), label, 0, 1, row, row+1);
+  gtk_widget_show (label);
+
+  /* the layercolor button */
+  button = gimp_color_button_new (_("Layer color (Colormask)"),
+                                  40, 20,                     /* WIDTH, HEIGHT, */
+                                  &cmaskint.layercolor,
+                                  GIMP_COLOR_AREA_FLAT);
+  gtk_table_attach (GTK_TABLE (table), button, 1, 3, row, row+1,
+                    GTK_FILL | GTK_EXPAND, GTK_SHRINK, 0, 0) ;
+  gtk_widget_show (button);
+  cmaskint.layercolorButton = button;
+
+  g_signal_connect (button, "color_changed",
+                    G_CALLBACK (p_color_update_callback),
+                    &cmaskint.layercolor);
+ 
+  row++;
+
+  /* the maskcolor label */
+  label = gtk_label_new (_("Mask color:"));
+  gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.0);
+  gtk_table_attach_defaults (GTK_TABLE(table), label, 0, 1, row, row+1);
+  gtk_widget_show (label);
+
+  /* the maskcolor button */
+  button = gimp_color_button_new (_("Mask color (Colormask)"),
+                                  40, 20,                     /* WIDTH, HEIGHT, */
+                                  &cmaskint.maskcolor,
+                                  GIMP_COLOR_AREA_FLAT);
+  gtk_table_attach (GTK_TABLE (table), button, 1, 3, row, row+1,
+                    GTK_FILL | GTK_EXPAND, GTK_SHRINK, 0, 0) ;
+  gtk_widget_show (button);
+  cmaskint.maskcolorButton = button;
+
+  g_signal_connect (button, "color_changed",
+                    G_CALLBACK (p_color_update_callback),
+                    &cmaskint.maskcolor);
+
+  row++;
+ 
+
+  /* the maskcolor label */
+  label = gtk_label_new (_("Nb color:"));
+  gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.0);
+  gtk_table_attach_defaults (GTK_TABLE(table), label, 0, 1, row, row+1);
+  gtk_widget_show (label);
+
+  /* the maskcolor button */
+  button = gimp_color_button_new (_("Left Neighbor color"),
+                                  40, 20,                     /* WIDTH, HEIGHT, */
+                                  &cmaskint.nbcolor,
+                                  GIMP_COLOR_AREA_FLAT);
+  gtk_table_attach (GTK_TABLE (table), button, 1, 3, row, row+1,
+                    GTK_FILL | GTK_EXPAND, GTK_SHRINK, 0, 0) ;
+  gtk_widget_show (button);
+  cmaskint.nbcolorButton = button;
+
+  g_signal_connect (button, "color_changed",
+                    G_CALLBACK (p_color_update_callback),
+                    &cmaskint.nbcolor);
+
+  row++;
+ 
+  /* the colordiff label */
+  label = gtk_label_new (_("Colordiff:"));
+  gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.0);
+  gtk_table_attach_defaults (GTK_TABLE(table), label, 0, 1, row, row+1);
+  gtk_widget_show (label);
+
+  label = gtk_label_new (_("#.#####"));
+  gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.0);
+  gtk_table_attach_defaults (GTK_TABLE(table), label, 1, 3, row, row+1);
+  gtk_widget_show (label);
+  cmaskint.colordiffLabel = label;
+
+  row++;
+
+  /* the neighbor colordiff label */
+  label = gtk_label_new (_("NbDiff:"));
+  gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.0);
+  gtk_table_attach_defaults (GTK_TABLE(table), label, 0, 1, row, row+1);
+  gtk_widget_show (label);
+
+  label = gtk_label_new (_("#.#####"));
+  gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.0);
+  gtk_table_attach_defaults (GTK_TABLE(table), label, 1, 3, row, row+1);
+  gtk_widget_show (label);
+  cmaskint.nbColordiffLabel = label;
+
+
+  return (frame);
+
+}  /* end p_create_debug_options */
+
+
+/* ---------------------------------
+ * p_update_colorfiff_labels
+ * ---------------------------------
+ */
+static void
+p_update_colorfiff_labels(GtkWidget *widget, gpointer val)
+{
+  if((cmaskint.colordiffLabel != NULL) && (cmaskint.nbColordiffLabel != NULL))
+  {
+    gdouble  colorDiff;
+    gdouble  colorDiffSimple;
+    char    *valueAsText;
+    gdouble sensitivity;
+
+    sensitivity = cmaskint.cmaskvalsPtr->colorSensitivity;
+    
+    colorDiff = gap_colordiff_GimpRGB(&cmaskint.maskcolor
+                      , &cmaskint.layercolor
+		      , sensitivity, TRUE);
+
+    colorDiffSimple = gap_colordiff_simple_GimpRGB(&cmaskint.maskcolor
+                      , &cmaskint.layercolor
+		      , TRUE);
+                      
+printf("colorDiff:%f   %f\n", colorDiff, (float)colorDiff);
+printf("colorDiffSimple:%f\n", colorDiffSimple);
+
+    valueAsText = g_strdup_printf("%.5f", (float)colorDiff);
+    gtk_label_set_text ( GTK_LABEL(cmaskint.colordiffLabel), valueAsText);
+    g_free(valueAsText);
+
+    colorDiff = gap_colordiff_GimpRGB(&cmaskint.nbcolor
+                       , &cmaskint.layercolor
+		       , sensitivity, TRUE);
+
+    valueAsText = g_strdup_printf("%.5f", (float)colorDiff);
+    gtk_label_set_text ( GTK_LABEL(cmaskint.nbColordiffLabel), valueAsText);
+    g_free(valueAsText);
+   
+  }
+
+}  /* end p_update_colorfiff_labels */
+
+
+
+
+/* -----------------------------------------
+ * p_update_option_frames_visiblity_callback
+ * -----------------------------------------
+ *
+ */
+static void
+p_update_option_frames_visiblity_callback(GtkWidget     *widget,
+                                          gpointer      data)
+{
+  if(cmaskint.showExpertOptions)
+  {
+    const gchar *l_env;
+    gtk_widget_show (cmaskint.filterFrame);
+    gtk_widget_show (cmaskint.opacityFrame);
+
+    l_env = g_getenv("USERNAME");
+    if(l_env != NULL)
+    {
+      if(strcmp(l_env, "hof") == 0)
+      {
+        gtk_widget_show (cmaskint.expertFrame);
+        gtk_widget_show (cmaskint.debugFrame);
+        return;
+      }
+    }
+    
+    // Note that the colormask filter is not finished yet
+    // the experimental expert and debug options will be hidden
+    // from the public (all users != hof) until the code is ready.
+    // Expect some of those options to be removed and/or changed
+    // in the future....
+    
+    gtk_widget_hide (cmaskint.expertFrame);
+    gtk_widget_hide (cmaskint.debugFrame);
+  }
+  else
+  {
+    gtk_widget_hide (cmaskint.filterFrame);
+    gtk_widget_hide (cmaskint.opacityFrame);
+    gtk_widget_hide (cmaskint.expertFrame);
+    gtk_widget_hide (cmaskint.debugFrame);
+  }
+
+
+}  /* end p_update_option_frames_visiblity_callback */
+
+
+
+
+/* ---------------------------------
+ * p_keycolor_update_callback
+ * ---------------------------------
+ */
+static void
+p_keycolor_update_callback(GtkWidget *widget, gpointer val)
+{
+  gimp_color_button_get_color((GimpColorButton *)widget, (GimpRGB *)val);
+  //
+
+}  /* end p_keycolor_update_callback */
+
+
+/* ---------------------------------
+ * p_color_update_callback
+ * ---------------------------------
+ */
+static void
+p_color_update_callback(GtkWidget *widget, gpointer val)
+{
+  gimp_color_button_get_color((GimpColorButton *)widget, (GimpRGB *)val);
+  p_update_colorfiff_labels(widget, val);
+
+}  /* end p_color_update_callback */
+
+/* ---------------------------------
+ * p_fetch_layercolor_and_maskcolor
+ * ---------------------------------
+ * fetch colors at coordinate x/y from
+ * drawable and from the colormask
+ * and update both colorbuttons
+ * (the colordiff label shall update due to value change in the colorbuttons)
+ */
+static void
+p_fetch_layercolor_and_maskcolor(gint x, gint y)
+{
+  gint x1 = 0;
+  gint y1 = 0;
+ 
+  if(cmaskint.preview != NULL)
+  {
+    gimp_preview_get_position (cmaskint.preview, &x1, &y1);
+  }
+
+
+  //if(gap_debug)
+  {
+    printf("p_fetch_layercolor_and_maskcolor x:%d  y:%d  x1:%d y1:%d\n"
+          ,(int)x
+          ,(int)y
+          ,(int)x1
+          ,(int)y1
+          );
+  }
+
+  if ((cmaskint.pftDest != NULL) && (cmaskint.pftMask != NULL))
+  {
+    guchar  pixels[3][4];
+
+    gimp_pixel_fetcher_get_pixel (cmaskint.pftMask
+                                     , x1 + x
+                                     , y1 + y
+                                     , pixels[0]
+                                     );
+    gimp_pixel_fetcher_get_pixel (cmaskint.pftDest
+                                     , x1 + x
+                                     , y1 + y
+                                     , pixels[1]
+                                     );
+    gimp_pixel_fetcher_get_pixel (cmaskint.pftDest
+                                     , x1 + x -1
+                                     , y1 + y
+                                     , pixels[2]
+                                     );
+
+
+    //if(gap_debug)
+    {
+      printf("\npixel[0] r:%d  g:%d  b:%d  a:%d pixel[1] r:%d  g:%d  b:%d a:%d\n"
+          ,(int)pixels[0][0]
+          ,(int)pixels[0][1]
+          ,(int)pixels[0][2]
+          ,(int)pixels[0][3]
+          ,(int)pixels[1][0]
+          ,(int)pixels[1][1]
+          ,(int)pixels[1][2]
+          ,(int)pixels[1][3]
+          );
+    }
+
+
+    gimp_rgba_set_uchar (&cmaskint.maskcolor, pixels[0][0], pixels[0][1], pixels[0][2], pixels[0][3]);
+    gimp_rgba_set_uchar (&cmaskint.layercolor, pixels[1][0], pixels[1][1], pixels[1][2], pixels[1][3]);
+    gimp_rgba_set_uchar (&cmaskint.nbcolor, pixels[2][0], pixels[2][1], pixels[2][2], pixels[2][3]);
+
+    gimp_color_button_set_color ((GimpColorButton *)cmaskint.maskcolorButton, &cmaskint.maskcolor);
+    gimp_color_button_set_color ((GimpColorButton *)cmaskint.layercolorButton, &cmaskint.layercolor);
+    gimp_color_button_set_color ((GimpColorButton *)cmaskint.nbcolorButton, &cmaskint.nbcolor);
+
+  }
+}  /* end p_fetch_layercolor_and_maskcolor */
+
+
+
+
+/* ---------------------------------
+ * 
+ * ---------------------------------
+ */
+static gint
+dialog_preview_events (GtkWidget   *area,
+                       GdkEvent    *event,
+                       GimpPreview *preview)
+{
+  gint            x, y;
+  gint            dx, dy;
+  GdkEventButton *bevent;
+
+  gtk_widget_get_pointer (area, &x, &y);
+
+  bevent = (GdkEventButton *) event;
+
+  switch (event->type)
+    {
+    case GDK_BUTTON_PRESS:
+      switch (bevent->button)
+        {
+        case 2:
+          //cmaskint.drag_mode = DRAG_BUMPMAP;
+          break;
+
+        default:
+          p_fetch_layercolor_and_maskcolor(x, y);
+          return FALSE;
+        }
+
+      cmaskint.mouse_x = x;
+      cmaskint.mouse_y = y;
+
+      gtk_grab_add (area);
+
+      return TRUE;
+      break;
+
+    case GDK_BUTTON_RELEASE:
+//       if (cmaskint.drag_mode != DRAG_NONE)
+//         {
+//           gtk_grab_remove (area);
+//           cmaskint.drag_mode = DRAG_NONE;
+//           gimp_preview_invalidate (preview);
+// 
+//           return TRUE;
+//         }
+
+      break;
+
+    case GDK_MOTION_NOTIFY:
+      dx = x - cmaskint.mouse_x;
+      dy = y - cmaskint.mouse_y;
+
+      cmaskint.mouse_x = x;
+      cmaskint.mouse_y = y;
+
+      if ((dx == 0) && (dy == 0))
+        break;
+
+//       switch (cmaskint.drag_mode)
+//         {
+//         case DRAG_BUMPMAP:
+//           cmaskvals->xofs = CLAMP (cmaskvals->xofs - dx, -1000, 1000);
+//           g_signal_handlers_block_by_func (cmaskint.offset_adj_x,
+//                                            gimp_int_adjustment_update,
+//                                            &cmaskvals->xofs);
+//           gtk_adjustment_set_value (GTK_ADJUSTMENT (cmaskint.offset_adj_x),
+//                                     cmaskvals->xofs);
+//           g_signal_handlers_unblock_by_func (cmaskint.offset_adj_x,
+//                                              gimp_int_adjustment_update,
+//                                              &cmaskvals->xofs);
+// 
+//           cmaskvals->yofs = CLAMP (cmaskvals->yofs - dy, -1000, 1000);
+//           g_signal_handlers_block_by_func (cmaskint.offset_adj_y,
+//                                            gimp_int_adjustment_update,
+//                                            &cmaskvals->yofs);
+//           gtk_adjustment_set_value (GTK_ADJUSTMENT (cmaskint.offset_adj_y),
+//                                     cmaskvals->yofs);
+//           g_signal_handlers_unblock_by_func (cmaskint.offset_adj_y,
+//                                              gimp_int_adjustment_update,
+//                                              &cmaskvals->yofs);
+//           break;
+// 
+//         default:
+//           return FALSE;
+//         }
+
+      gimp_preview_invalidate (preview);
+
+      return TRUE;
+      break;
+
+    default:
+      break;
+    }
+
+  return FALSE;
+}
+
+/* -------------------------
+ * p_dialog_new_colormask
+ * -------------------------
+ */
+static void
+p_dialog_new_colormask (gboolean init_offsets)
+{
+  if(cmaskint.pftDest != NULL)
+  {
+    gimp_pixel_fetcher_destroy (cmaskint.pftDest);
+    cmaskint.pftDest = NULL;
+  }
+
+  /* Get drawable */
+  if (cmaskint.colormask_drawable != NULL)
+  { 
+    if (cmaskint.colormask_drawable != cmaskint.layer_drawable)
+    {
+      gimp_drawable_detach (cmaskint.colormask_drawable);
+    }
+  }
+  
+  if(cmaskint.pftMask != NULL)
+  {
+    gimp_pixel_fetcher_destroy (cmaskint.pftMask);
+    cmaskint.pftMask = NULL;
+  }
+  
+  if (cmaskint.cmaskvalsPtr->colormask_id != -1)
+  {
+    cmaskint.colormask_drawable = gimp_drawable_get (cmaskint.cmaskvalsPtr->colormask_id);
+  }
+  else
+  {
+    cmaskint.colormask_drawable = cmaskint.layer_drawable;
+  }
+
+  if (cmaskint.colormask_drawable == NULL)
+  {
+    return;
+  }
+
+  cmaskint.pftMask = gimp_pixel_fetcher_new (cmaskint.colormask_drawable, FALSE /* shadow */);
+  gimp_pixel_fetcher_set_edge_mode (cmaskint.pftMask, GIMP_PIXEL_FETCHER_EDGE_BLACK);
+
+  if(cmaskint.layer_drawable != NULL)
+  {
+    cmaskint.pftDest = gimp_pixel_fetcher_new (cmaskint.layer_drawable, FALSE /* shadow */);
+    gimp_pixel_fetcher_set_edge_mode (cmaskint.pftDest, GIMP_PIXEL_FETCHER_EDGE_BLACK);
+  }
+  
+  /* Get sizes */
+  cmaskint.bm_width     = gimp_drawable_width (cmaskint.colormask_drawable->drawable_id);
+  cmaskint.bm_height    = gimp_drawable_height (cmaskint.colormask_drawable->drawable_id);
+  cmaskint.bm_bpp       = gimp_drawable_bpp (cmaskint.colormask_drawable->drawable_id);
+  cmaskint.bm_has_alpha = gimp_drawable_has_alpha (cmaskint.colormask_drawable->drawable_id);
+
+//   if (init_offsets)
+//     {
+//       GtkAdjustment  *adj;
+//       gint            bump_offset_x;
+//       gint            bump_offset_y;
+//       gint            draw_offset_y;
+//       gint            draw_offset_x;
+// 
+//       gimp_drawable_offsets (cmaskint.colormask_drawable->drawable_id,
+//                              &bump_offset_x, &bump_offset_y);
+//       gimp_drawable_offsets (cmaskint.layer_drawable->drawable_id,
+//                              &draw_offset_x, &draw_offset_y);
+// 
+//       cmaskvals->xofs = draw_offset_x - bump_offset_x;
+//       cmaskvals->yofs = draw_offset_y - bump_offset_y;
+// 
+//       adj = (GtkAdjustment *) cmaskint.offset_adj_x;
+//       if (adj)
+//         {
+//           adj->value = cmaskvals->xofs;
+//           g_signal_handlers_block_by_func (adj,
+//                                            gimp_int_adjustment_update,
+//                                            &cmaskvals->xofs);
+//           gtk_adjustment_value_changed (adj);
+//           g_signal_handlers_unblock_by_func (adj,
+//                                              gimp_int_adjustment_update,
+//                                              &cmaskvals->xofs);
+//         }
+// 
+//       adj = (GtkAdjustment *) cmaskint.offset_adj_y;
+//       if (adj)
+//         {
+//           adj->value = cmaskvals->yofs;
+//           g_signal_handlers_block_by_func (adj,
+//                                            gimp_int_adjustment_update,
+//                                            &cmaskvals->yofs);
+//           gtk_adjustment_value_changed (adj);
+//           g_signal_handlers_unblock_by_func (adj,
+//                                              gimp_int_adjustment_update,
+//                                              &cmaskvals->yofs);
+//         }
+//     }
+
+  /* Initialize pixel region */
+  gimp_pixel_rgn_init (&cmaskint.colormask_rgn, cmaskint.colormask_drawable,
+                       0, 0, cmaskint.bm_width, cmaskint.bm_height, FALSE, FALSE);
+
+}  /* end p_dialog_new_colormask */
+
+static void
+dialog_update_preview (GimpPreview *preview)
+{
+   guchar *dest_row;
+   gint    y;
+   gint    x1, y1;
+   gint    width, height;
+   gint    bytes;
+ 
+   gimp_preview_get_position (preview, &x1, &y1);
+
+// if(gap_debug)
+   {
+     printf("dialog_update_preview x1:%d y1:%d\n"
+           ,(int)x1
+           ,(int)y1
+           );
+   }
+
+   if ((cmaskint.colormask_drawable != cmaskint.layer_drawable)
+   && (cmaskint.layer_drawable != NULL)
+   && (cmaskint.applyImmediate == TRUE)
+   && (cmaskint.cmaskvalsPtr->keepLayerMask == TRUE))
+   {
+     gint32 drawableId;
+     
+     drawableId = cmaskint.layer_drawable->drawable_id;
+     
+     gap_colormask_apply_to_layer_of_same_size (drawableId
+                        , cmaskint.cmaskvalsPtr
+                        , TRUE   // doProgress
+                        );
+     gimp_drawable_flush (cmaskint.layer_drawable);
+     gimp_displays_flush ();
+   }
+
+
+//   gimp_preview_get_size (preview, &width, &height);
+//   bytes = cmaskint.layer_drawable->bpp;
+// 
+//   /* Initialize source rows */
+//   gimp_pixel_rgn_init (&cmaskint.src_rgn, cmaskint.layer_drawable,
+//                        sel_x1, sel_y1, sel_width, sel_height, FALSE, FALSE);
+// 
+//   cmaskint.src_rows = g_new (guchar *, height);
+// 
+//   for (y = 0; y < height; y++)
+//     cmaskint.src_rows[y]  = g_new (guchar, sel_width * 4);
+// 
+//   dialog_fill_src_rows (0, height, y1);
+// 
+//   /* Initialize bumpmap rows */
+//   cmaskint.bm_rows = g_new (guchar *, height + 2);
+// 
+//   for (y = 0; y < height + 2; y++)
+//     cmaskint.bm_rows[y] = g_new (guchar, cmaskint.bm_width * cmaskint.bm_bpp);
+// 
+//   bumpmap_init_params (&cmaskint.params);
+// 
+//   dialog_fill_bumpmap_rows (0, height, cmaskvals->yofs + y1);
+// 
+//   dest_row = g_new (guchar, width * height * 4);
+// 
+//   /* Bumpmap */
+// 
+//   for (y = 0; y < height; y++)
+//     {
+//       gint isfirst = ((y == - cmaskvals->yofs - y1)
+//                       && ! cmaskvals->tiled) ? 1 : 0;
+//       gint islast = (y == (- cmaskvals->yofs - y1
+//                            + cmaskint.bm_height - 1) && ! cmaskvals->tiled) ? 1 : 0;
+//       bumpmap_row (cmaskint.src_rows[y] + 4 * x1,
+//                    dest_row + 4 * width * y,
+//                    width, 4, TRUE,
+//                    cmaskint.bm_rows[y + isfirst],
+//                    cmaskint.bm_rows[y + 1],
+//                    cmaskint.bm_rows[y + 2 - islast],
+//                    cmaskint.bm_width, cmaskvals->xofs + x1,
+//                    cmaskvals->tiled,
+//                    y >= - cmaskvals->yofs - y1 &&
+//                    y < (- cmaskvals->yofs - y1 + cmaskint.bm_height),
+//                    &cmaskint.params);
+// 
+//     }
+// 
+//   gimp_preview_area_draw (GIMP_PREVIEW_AREA (preview->area),
+//                           0, 0, width, height,
+//                           GIMP_RGBA_IMAGE,
+//                           dest_row,
+//                           4 * width);
+// 
+//   g_free (dest_row);
+// 
+//   for (y = 0; y < height + 2; y++)
+//     g_free (cmaskint.bm_rows[y]);
+//   g_free (cmaskint.bm_rows);
+// 
+//   for (y = 0; y < height; y++)
+//     g_free (cmaskint.src_rows[y]);
+//   g_free (cmaskint.src_rows);
+}
+
+
+// static void
+// dialog_get_rows (GimpPixelRgn  *pr,
+//                  guchar       **rows,
+//                  gint           x,
+//                  gint           y,
+//                  gint           width,
+//                  gint           height)
+// {
+//   /* This is shamelessly ripped off from gimp_pixel_rgn_get_rect().
+//    * Its function is exactly the same, but it can fetch an image
+//    * rectangle to a sparse buffer which is defined as separate
+//    * rows instead of one big linear region.
+//    */
+// 
+//   gint xstart, ystart;
+//   gint xend, yend;
+//   gint xboundary;
+//   gint yboundary;
+//   gint xstep, ystep;
+//   gint b, bpp;
+//   gint tx, ty;
+//   gint tile_width  = gimp_tile_width();
+//   gint tile_height = gimp_tile_height();
+// 
+//   bpp = pr->bpp;
+// 
+//   xstart = x;
+//   ystart = y;
+//   xend   = x + width;
+//   yend   = y + height;
+//   ystep  = 0; /* Shut up -Wall */
+// 
+//   while (y < yend)
+//     {
+//       x = xstart;
+// 
+//       while (x < xend)
+//         {
+//           GimpTile *tile;
+// 
+//           tile = gimp_drawable_get_tile2 (pr->drawable, pr->shadow, x, y);
+//           gimp_tile_ref (tile);
+// 
+//           xstep     = tile->ewidth - (x % tile_width);
+//           ystep     = tile->eheight - (y % tile_height);
+//           xboundary = x + xstep;
+//           yboundary = y + ystep;
+//           xboundary = MIN (xboundary, xend);
+//           yboundary = MIN (yboundary, yend);
+// 
+//           for (ty = y; ty < yboundary; ty++)
+//             {
+//               const guchar *src;
+//               guchar       *dest;
+// 
+//               src = tile->data + tile->bpp * (tile->ewidth *
+//                                               (ty % tile_height) +
+//                                                (x % tile_width));
+//               dest = rows[ty - ystart] + bpp * (x - xstart);
+// 
+//               for (tx = x; tx < xboundary; tx++)
+//                 for (b = bpp; b; b--)
+//                   *dest++ = *src++;
+//             }
+// 
+//           gimp_tile_unref (tile, FALSE);
+// 
+//           x += xstep;
+//         }
+// 
+//       y += ystep;
+//     }
+// }
+// 
+// static void
+// dialog_fill_src_rows (gint start,
+//                       gint how_many,
+//                       gint yofs)
+// {
+//   gint x;
+//   gint y;
+// 
+//   dialog_get_rows (&cmaskint.src_rgn,
+//                    cmaskint.src_rows + start,
+//                    0/*sel_x1*/,
+//                    yofs,
+//                    sel_width,
+//                    how_many);
+// 
+//   /* Convert to RGBA.  We move backwards! */
+// 
+//   for (y = start; y < (start + how_many); y++)
+//     {
+//       const guchar *sp = cmaskint.src_rows[y] + img_bpp * sel_width - 1;
+//       guchar       *p  = cmaskint.src_rows[y] + 4 * sel_width - 1;
+// 
+//       for (x = 0; x < sel_width; x++)
+//         {
+//           if (img_has_alpha)
+//             *p-- = *sp--;
+//           else
+//             *p-- = 255;
+// 
+//           if (img_bpp < 3)
+//             {
+//               *p-- = *sp;
+//               *p-- = *sp;
+//               *p-- = *sp--;
+//             }
+//           else
+//             {
+//               *p-- = *sp--;
+//               *p-- = *sp--;
+//               *p-- = *sp--;
+//             }
+//         }
+//     }
+// }
+// 
+// static void
+// dialog_fill_bumpmap_rows (gint start,
+//                           gint how_many,
+//                           gint yofs)
+// {
+//   gint buf_row_ofs;
+//   gint remaining;
+//   gint this_pass;
+// 
+//   /* Adapt to offset of selection */
+//   yofs = MOD (yofs + sel_y1, cmaskint.bm_height);
+// 
+//   buf_row_ofs = start;
+//   remaining   = how_many;
+// 
+//   while (remaining > 0)
+//     {
+//       this_pass = MIN (remaining, cmaskint.bm_height - yofs);
+// 
+//       dialog_get_rows (&cmaskint.colormask_rgn,
+//                        cmaskint.bm_rows + buf_row_ofs,
+//                        0,
+//                        yofs,
+//                        cmaskint.bm_width,
+//                        this_pass);
+// 
+//       yofs         = (yofs + this_pass) % cmaskint.bm_height;
+//       remaining   -= this_pass;
+//       buf_row_ofs += this_pass;
+//     }
+// 
+//   /* Convert rows */
+// 
+//   for (; how_many; how_many--)
+//     {
+//       bumpmap_convert_row (cmaskint.bm_rows[start],
+//                            cmaskint.bm_width,
+//                            cmaskint.bm_bpp,
+//                            cmaskint.bm_has_alpha,
+//                            cmaskint.params.lut);
+// 
+//       start++;
+//     }
+// }
+
+
+
+
+
+static gboolean
+dialog_constrain (gint32   image_id,
+                  gint32   drawable_id,
+                  gpointer data)
+{
+  return (gimp_drawable_is_rgb (drawable_id));
+}
+
+static void
+dialog_colormask_callback (GtkWidget   *widget,
+                         GimpPreview *preview)
+{
+  gint32  drawable_id;
+
+  gimp_int_combo_box_get_active (GIMP_INT_COMBO_BOX (widget), &drawable_id);
+
+  if (cmaskint.cmaskvalsPtr->colormask_id != drawable_id)
+    {
+      cmaskint.cmaskvalsPtr->colormask_id = drawable_id;
+      p_dialog_new_colormask (TRUE);
+      gimp_preview_invalidate (preview);
+    }
+}
+
+
+/* ---------------------------
+ * gap_colormask_create_dialog
+ * ---------------------------
+ */
+GtkWidget*  
+gap_colormask_create_dialog (GapColormaskValues *cmaskvals)
+{
+  GtkWidget *dialog;
+  GtkWidget *paned;
+  GtkWidget *hbox;
+  GtkWidget *vbox;
+  GtkWidget *preview;
+  GtkWidget *frame;
+  GtkObject *adj;
+
+  gimp_ui_init (PLUG_IN_BINARY, TRUE);
+
+  dialog = gimp_dialog_new (_("Color Mask"), PLUG_IN_BINARY,
+                            NULL, 0,
+                            gimp_standard_help_func, PLUG_IN_NAME_HELP,
+
+                            GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+                            GTK_STOCK_OK,     GTK_RESPONSE_OK,
+
+                            NULL);
+
+  gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
+                                           GTK_RESPONSE_OK,
+                                           GTK_RESPONSE_CANCEL,
+                                           -1);
+
+  gimp_window_set_transient (GTK_WINDOW (dialog));
+
+  cmaskint.pftDest = NULL;
+  cmaskint.pftMask = NULL;
+
+  paned = gtk_hpaned_new ();
+  gtk_container_set_border_width (GTK_CONTAINER (paned), 12);
+  gtk_container_add (GTK_CONTAINER (GTK_DIALOG (dialog)->vbox), paned);
+  gtk_widget_show (paned);
+
+  hbox = gtk_hbox_new (FALSE, 0);
+  gtk_paned_pack1 (GTK_PANED (paned), hbox, TRUE, FALSE);
+  gtk_widget_show (hbox);
+
+  vbox = gtk_vbox_new (FALSE, 0);
+  gtk_container_set_border_width (GTK_CONTAINER (vbox), 4);
+  gtk_box_pack_end (GTK_BOX (hbox), vbox, FALSE, FALSE, 0);
+  gtk_widget_show (vbox);
+
+  preview = gimp_drawable_preview_new (cmaskint.layer_drawable, NULL);
+  gtk_container_add (GTK_CONTAINER (hbox), preview);
+  gtk_widget_show (preview);
+
+  g_signal_connect (preview, "invalidated",
+                    G_CALLBACK (dialog_update_preview),
+                    NULL);
+  g_signal_connect (GIMP_PREVIEW (preview)->area, "event",
+                    G_CALLBACK (dialog_preview_events), preview);
+
+  cmaskint.preview = preview;
+
+  hbox = gtk_hbox_new (FALSE, 0);
+  gtk_paned_pack2 (GTK_PANED (paned), hbox, FALSE, FALSE);
+  gtk_widget_show (hbox);
+
+  vbox = gtk_vbox_new (FALSE, 0);
+  gtk_container_set_border_width (GTK_CONTAINER (vbox), 4);
+  gtk_box_pack_start (GTK_BOX (hbox), vbox, FALSE, FALSE, 0);
+  gtk_widget_show (vbox);
+
+
+  /* Base options */
+  frame = p_create_base_options(cmaskvals, preview);
+  cmaskint.baseFrame = frame;
+  gtk_widget_show (frame);
+  gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
+
+  /* Expert options */
+  frame = p_create_expert_options(cmaskvals, preview);
+  cmaskint.expertFrame = frame;
+  gtk_widget_show (frame);
+  gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
+
+
+  vbox = gtk_vbox_new (FALSE, 0);
+  gtk_container_add (GTK_CONTAINER (hbox), vbox);
+  gtk_widget_show (vbox);
+
+  /* Filter options */
+  frame = p_create_filter_options(cmaskvals, preview);
+  cmaskint.filterFrame = frame;
+  gtk_widget_hide (frame);
+  gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
+
+  /* Opacity options */
+  frame = p_create_opacity_options(cmaskvals, preview);
+  cmaskint.opacityFrame = frame;
+  gtk_widget_show (frame);
+  gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
+
+  /* Debug options */
+  frame = p_create_debug_options(cmaskvals, preview);
+  cmaskint.debugFrame = frame;
+  gtk_widget_hide (frame);
+  gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
+
+  p_update_option_frames_visiblity_callback(NULL, NULL);
+
+  /* Initialise drawable
+   * (don't initialize offsets if colormask_id is already known)
+   */
+  if (cmaskvals->colormask_id == -1)
+  {
+    p_dialog_new_colormask (TRUE);
+  }
+  else
+  {
+    p_dialog_new_colormask (FALSE);
+  }
+
+  return (dialog);
+}  /* end gap_colormask_create_dialog */
+
+
+
+/* --------------------
+ * gap_colormask_dialog
+ * --------------------
+ */
+gboolean
+gap_colormask_dialog (GapColormaskValues *cmaskvals, GimpDrawable *layer_drawable)
+{
+  GtkWidget *dialog;
+  gboolean   run;
+
+  cmaskint.applyImmediate = FALSE;
+  cmaskint.layer_drawable = layer_drawable;
+  cmaskint.paramFilename = g_strdup("colormask.params");
+  cmaskint.cmaskvalsPtr = cmaskvals;
+  
+
+  dialog = gap_colormask_create_dialog(cmaskvals);
+
+  /* Done */
+
+  gtk_widget_show (dialog);
+
+  run = FALSE;
+  if (gimp_dialog_run (GIMP_DIALOG (dialog)) == GTK_RESPONSE_OK)
+  {
+    // TODO param file save shall be done in the procedure that
+    // creates the dialog as pop-up for Storybord clip properties.
+    // for testpurpose saving is done here until the rest is implemented...
+    gap_colormask_file_save (cmaskint.paramFilename, cmaskvals);
+    run = TRUE;
+  }
+
+  gtk_widget_destroy (dialog);
+
+  if (cmaskint.colormask_drawable != cmaskint.layer_drawable)
+  {
+    gimp_drawable_detach (cmaskint.colormask_drawable);
+  }
+
+  //if(gap_debug)
+  {
+    printf("gap_colormask_dialog  run:%d\n", (int)run);
+  }
+
+  g_free(cmaskint.paramFilename);
+  
+  return run;
+  
+}  /* end gap_colormask_dialog */
diff --git a/gap/gap_colormask_dialog.h b/gap/gap_colormask_dialog.h
new file mode 100644
index 0000000..407aee9
--- /dev/null
+++ b/gap/gap_colormask_dialog.h
@@ -0,0 +1,45 @@
+/* gap_colormask_dialog.h
+ *    by hof (Wolfgang Hofer)
+ *    colormask filter dialog handling procedures
+ *  2010/02/25
+ *
+ */
+/* 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:
+ * version 2.7.0;             hof: created
+ */
+
+#ifndef _GAP_COLORMASK_DIALOG_H
+#define _GAP_COLORMASK_DIALOG_H
+
+/* SYTEM (UNIX) includes */ 
+#include <stdio.h>
+#include <stdlib.h>
+
+/* GIMP includes */
+#include "gtk/gtk.h"
+#include "libgimp/gimp.h"
+
+
+GtkWidget*  gap_colormask_create_dialog (GapColormaskValues *cmaskvals);
+
+gboolean    gap_colormask_dialog (GapColormaskValues *cmaskvals, GimpDrawable *layer_drawable);
+
+#endif
diff --git a/gap/gap_colormask_exec.c b/gap/gap_colormask_exec.c
new file mode 100644
index 0000000..63d3cb3
--- /dev/null
+++ b/gap/gap_colormask_exec.c
@@ -0,0 +1,4650 @@
+/* gap_colormask_exec.c
+ *    by hof (Wolfgang Hofer)
+ *    color mask filter worker procedures
+ *    to set alpha channel for a layer according to matching colors
+ *    of color mask (image)
+ *  2010/03/21
+ *
+ */
+/* 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:
+ * version 2.7.0;             hof: created
+ */
+
+/* SYTEM (UNIX) includes */
+#include "string.h"
+/* GIMP includes */
+/* GAP includes */
+#include "gap_lib_common_defs.h"
+#include "gap_pdb_calls.h"
+#include "gap_colordiff.h"
+#include "gap_colormask_file.h"
+#include "gap_colormask_exec.h"
+#include "gap_lib.h"
+#include "gap_image.h"
+#include "gap_layer_copy.h"
+#include "gap_libgapbase.h"
+
+
+#define WORK_PROTECTED             255     /* marker for protected pixels */
+#define WORK_PART_OF_CURRENT_AREA  250     /* marker for pixels that are part of current processed area */
+#define WORK_PART_OF_BIG_AREA      245     /* marker for pixels that are part of another already processed area */
+#define WORK_PART_OF_SMALL_AREA    240     /* marker for pixels that are part of another already processed area */
+#define WORK_VISITED_BUT_NOT_PART_OF_CURRENT_AREA  1
+#define WORK_UNCHECKED             0       /* marker for unchecked pixels */
+
+#define IDX_ALPHA  3                       /* in RGBA apha channel has index 3 */
+
+#define COLORMASK_LAYER_NAME "colormask"
+#define COLORMASK_DIFF_LAYER_NAME "diffLayer"
+
+#define CLIP_AREA_MAX_RADIUS 3
+#define CLIP_AREA_MAX_SIZE   (1 + (2 * CLIP_AREA_MAX_RADIUS))
+
+#define MI_IMAGE 0
+#define MI_MASK  1
+#define MI_MAX   2
+
+#define ISLE_AREA_MAX_RADIUS 10
+#define ISLE_AREA_MAX_SIZE   (1 + (2 * ISLE_AREA_MAX_RADIUS))
+
+
+#define MATCHTYPE_UNDEFINED     0
+#define MATCHTYPE_MATCHING      1
+#define MATCHTYPE_NOT_MATCHING  2
+
+
+
+extern      int gap_debug; /* ==0  ... dont print debug infos */
+
+
+typedef struct GapAreaCoordPoint {
+     gint            x;
+     gint            y;
+     void           *next;
+} GapAreaCoordPoint;
+
+
+typedef struct GapColorMaskParams { /* nickname: cmaskParPtr */
+     gint32     dst_layer_id;
+     gint32     cmask_drawable_id;
+     gdouble    colorThreshold;      /* dynamic keycolor dependent color difference lower threshold 0.0 to 1.0 */
+     gdouble    loColorThreshold;    /* color difference lower threshold 0.0 to 1.0 */
+     gdouble    hiColorThreshold;    /* color difference upper threshold 0.0 to 1.0 */
+     gdouble    colorSensitivity;    /* 1.0 to 2.0 sensitivity for colr diff algorithm */
+     gdouble    lowerOpacity;        /* opacity for matching colors 0.0 to 1.0 */
+     gdouble    upperOpacity;        /* opacity for non matching colors 0.0 to 1.0 */
+     gdouble    triggerAlpha;        /* 0.0 to 1.0 mask opacity greater than this alpha value trigger color masking */
+     gdouble    isleRadius;          /* radius in pixels for checking isolated pixels */
+     gdouble    isleAreaPixelLimit;  /* size of area in pixels for remove isaolated pixel(area)s */
+     gdouble    featherRadius;       /* radius in pixels for feathering edges */
+     gdouble    thresholdColorArea;
+     gboolean   connectByCorner;
+     gdouble    pixelDiagonal;
+     gdouble    pixelAreaLimit;
+     gboolean   keepLayerMask;
+     gboolean   keepWorklayer;
+     gdouble    edgeColorThreshold;
+     gint       algorithm;
+
+     /* stuff for dynamic keycolor dependent threshold handling */
+     GimpHSV    keyColorHsv;
+     gdouble    loKeyColorThreshold;      /* color difference lower threshold for keycolor 0.0 to 1.0 */
+     gdouble    keyColorSensitivity;      /* 0.1 upto 10.0 default 1.0 */
+     gboolean   enableKeyColorThreshold;  
+
+     /* internal and precalculated values */
+     guchar adjustOpacityLevel;
+     gint   adjustLoopCount;
+
+     gdouble    shadeRange;
+     guchar     triggerAlpha255;
+     guchar     lowerAlpha255;
+     guchar     upperAlpha255;
+     guchar     transparencyLevel255;
+     gdouble    opacityFactor;        /* 1.0 in case opacity lower/upper use the full range */
+     gint32     dst_image_id;
+     gboolean   debugCoordinate;
+     gint       guideRow;
+     gint       guideCol;
+     gint       x;                  /* current processed coordinate */
+     gint       y;
+
+     gint       width;              /* current processed drawable width */
+     gint       height;
+
+     GimpPixelFetcher *pftMask;
+     GimpPixelFetcher *pftDest;
+     GimpPixelFetcher *pftDestLayerMask;
+     gint32            dstLayerMaskId;
+
+     gint jaggedRadius;
+     gint jeggedLength;
+
+     /* area context stuff */
+
+     GapAreaCoordPoint   *pointList;
+     gint32    pointCount;
+     gint32    pixelCount;
+     gint32    max_ix;
+     gint32    max_iy;
+     gint32    min_ix;
+     gint32    min_iy;
+     gint32    seed_ix;
+     gint32    seed_iy;
+     guchar    marker;
+     gint      currentAreaPass;
+
+     gint      sel_x1;
+     gint      sel_y1;
+     gint      sel_x2;
+     gint      sel_y2;
+
+     gint32            worklayerId;
+     guchar            seedPixel[4];
+     GimpHSV           seedHsv;
+     GimpPixelFetcher *pftWork;
+     gdouble           sumColorDiff;
+     gdouble           avgColorDiff;
+     guchar            areaPrecalculatedOpacity;
+     gboolean          usePrecalculatedOpacity;
+
+
+     /* stuff for average algorithm handling */
+     gint    checkRadius;
+     gint    checkMatchRadius;
+     gdouble *colordiffTable;          // big colordiff lookup table in full image size
+     gdouble avgFactor;                // == 0.5  // 0.0 to 1.0
+     gdouble avgColorThreshold;        // ==  colorThreshold + ((hiColorThreshold - colorThreshold) *  avgFactor)
+
+
+     gint32 areaMinimumPixels;   // GUI Parameter 5 to     (0.9 * CLIP_AREA_MAX_SIZE * CLIP_AREA_MAX_SIZE)
+
+     gint32 maskAreaSize;  //     ... area of similar color to current image pixel in the colormask
+     gint32 imgAreaSize;   //     ... area of similar color to current
+     gint32 maskOnlyAreaSize;  //     ... area pixels that are part of the mask area but NOT part of the image area
+     gint32 imgOnlyAreaSize;   //     ... area pixels that are part of the image area but NOT part of the mask area
+     gint32 commonAreaSize;    //     ... common pixels that are member in both image and mask area
+
+     gdouble sumMatch[MI_MAX];
+     gdouble sumRange[MI_MAX];
+     gdouble sumNoMatch[MI_MAX];
+     gdouble countMatch[MI_MAX];
+     gdouble countRange[MI_MAX];
+     gdouble countNoMatch[MI_MAX];
+
+     gdouble avgColordiffMaskArea;
+     gdouble avgColordiffImgArea;
+     gdouble avgColordiffMaskOnlyArea;
+     gdouble avgColordiffImgOnlyArea;
+     gdouble avgColordiffCommonArea;
+     gdouble sumColordiffMaskOnlyArea;
+     gdouble sumColordiffImgOnlyArea;
+     gdouble sumColordiffCommonArea;
+     
+     /* stuff for GAP_COLORMASK_ALGO_AVG_CHANGE */
+     gint    significantRadius;              /* radius to limit search for significant brightness/colorchanges */
+     gdouble significantColordiff;           /* threshold to define when colors are considered as significant different */
+     gdouble significantBrightnessDiff;      /* threshold to define significant brightness changes */
+
+
+     gint32 seedOffsetX;   /* for convert image coords to clipAreaTable coords */
+     gint32 seedOffsetY;
+     guchar clipAreaTable[CLIP_AREA_MAX_SIZE][CLIP_AREA_MAX_SIZE][MI_MAX];
+     guchar isleAreaTable[ISLE_AREA_MAX_SIZE][ISLE_AREA_MAX_SIZE];
+
+
+
+     /* stuff for progress handling */
+     gboolean   doProgress;
+     gdouble    pixelsToProcessInAllPasses;
+     gdouble    pixelsDoneAllPasses;
+
+  } GapColorMaskParams;
+
+static  inline void  p_set_dynamic_threshold_by_KeyColor(guchar *pixel
+                        , GapColorMaskParams *cmaskParPtr);
+
+
+static void          p_colormask_avg_rgn_render_region (const GimpPixelRgn *maskPR
+                        ,const GimpPixelRgn *destPR
+                        ,const GimpPixelRgn *lmskPR
+                        ,GapColorMaskParams *cmaskParPtr);
+
+static gboolean      p_check_matching_pixel_isolation(GapColorMaskParams *cmaskParPtr);
+static guchar        p_calc_opacity_for_matching_color(GapColorMaskParams *cmaskParPtr);
+static guchar        p_calc_opacity_for_range_color(GapColorMaskParams *cmaskParPtr);
+static void          p_check_range_neighbour_sum(GapColorMaskParams *cmaskParPtr, gint dx, gint dy
+                         , gint radius, gdouble *sumNbColorDiff, gdouble *countNbPixels);
+
+
+static void          p_countPerColordiff(GapColorMaskParams *cmaskParPtr, gdouble colordiff, gint mi);
+static void          p_avg_check_and_mark_nb_pixel(GapColorMaskParams *cmaskParPtr, GimpPixelFetcher *pft, gint nx, gint ny, gint mi);
+static void          p_find_pixel_area_of_similar_color(GapColorMaskParams *cmaskParPtr, GimpPixelFetcher *pft, gint mi);
+static void          p_calculate_clip_area_average_values(GapColorMaskParams *cmaskParPtr);
+
+
+static gboolean      p_check_average_colordiff_incl_neighbours(GapColorMaskParams *cmaskParPtr);
+
+static gboolean      p_check_significant_diff(guchar *aPixelPtr
+                       , guchar *bPixelPtr
+		       , gint xx, gint yy
+                       , GapColorMaskParams *cmaskParPtr);
+static guchar        p_check_significant_changes_in_one_direction(GapColorMaskParams *cmaskParPtr, gint dx, gint dy);
+
+static inline void   p_check_avg_add_one_pixel(GapColorMaskParams *cmaskParPtr, gint xx, gint yy    // DEPRECATED
+                           , gdouble *sumNbColorDiff, gdouble *countNbPixels);
+static gdouble       p_check_avg_diff_within_radius(GapColorMaskParams *cmaskParPtr, gint radius    // DEPRECATED
+                           , gdouble *sumNbColorDiff, gdouble *countNbPixels);
+
+
+static void          p_init_colordiffTable (const GimpPixelRgn *maskPR
+                        ,const GimpPixelRgn *destPR
+                        ,GapColorMaskParams *cmaskParPtr);
+static inline gint32 p_getColordiffTableIndex(GapColorMaskParams *cmaskParPtr, gint x, gint y);
+
+
+static void          p_difflayer_rgn_render (const GimpPixelRgn *diffPR
+                         , const GimpPixelRgn *destPR, GapColorMaskParams *cmaskParPtr);
+
+
+static void          p_create_colordiffTable_Layer(GapColorMaskParams *cmaskParPtr, GimpDrawable *dst_drawable);
+
+
+
+
+
+
+
+/* ---------------------------------
+ * p_handle_progress
+ * ---------------------------------
+ * handle progress based on the specified amount of processed pixels
+ */
+static void
+p_handle_progress(GapColorMaskParams *cmaskParPtr, gint pixels, const char *caller)
+{
+  gdouble progress;
+
+  cmaskParPtr->pixelsDoneAllPasses += pixels;
+  progress = cmaskParPtr->pixelsDoneAllPasses / cmaskParPtr->pixelsToProcessInAllPasses;
+  gimp_progress_update(progress);
+
+  return;
+
+  if(gap_debug)
+  {
+    printf("Progress: %s (%.1f / %.1f) pixels:%d %.3f\n"
+      , caller
+      , (float)cmaskParPtr->pixelsDoneAllPasses
+      , (float)cmaskParPtr->pixelsToProcessInAllPasses
+      , (int)pixels
+      , (float)progress
+      );
+  }
+}  /* end p_handle_progress */
+
+/* --------------------------------------------
+ * p_create_empty_layer
+ * --------------------------------------------
+ * create an empty layer (the worklayer) and add it to the specified image
+ *
+ */
+static gint32
+p_create_empty_layer(gint32 image_id
+                       , gint32 width
+                       , gint32 height
+                       , const char *name
+                        )
+{
+  gint32 layer_id;
+  GimpImageBaseType l_basetype;
+
+  l_basetype   = gimp_image_base_type(image_id);
+  if(l_basetype != GIMP_RGB)
+  {
+    printf("** ERROR image != RGB is not supported\n");
+    return -1;
+  }
+
+  layer_id = gimp_layer_new(image_id
+                , name
+                , width
+                , height
+                , GIMP_RGBA_IMAGE
+                , 100.0   /* full opacity */
+                , 0       /* normal mode */
+                );
+
+  gimp_image_add_layer (image_id, layer_id, 0 /* stackposition */ );
+
+  return (layer_id);
+
+}  /* end p_create_empty_layer */
+
+
+
+
+
+/* ---------------------------------
+ * p_colordiff_guchar_cmask
+ * ---------------------------------
+ * returns difference of 2 colors as gdouble value
+ * in range 0.0 (exact match) to 1.0 (maximal difference)
+ */
+gdouble
+p_colordiff_guchar_cmask(guchar *aPixelPtr
+                   , guchar *bPixelPtr
+                   , GapColorMaskParams *cmaskParPtr)
+{
+  gdouble colordiffRgb;
+  gdouble colordiffHsv;
+  
+  colordiffRgb = 0.0;
+  if (cmaskParPtr->connectByCorner == TRUE) ///// #### TODO
+  {
+    colordiffRgb = gap_colordiff_simple_guchar(aPixelPtr
+                   , bPixelPtr
+                   , cmaskParPtr->debugCoordinate
+                   );
+  }
+
+  colordiffHsv = gap_colordiff_guchar(aPixelPtr
+                   , bPixelPtr
+                   , cmaskParPtr->colorSensitivity
+                   , cmaskParPtr->debugCoordinate
+                   );
+
+  return (MAX(colordiffRgb, colordiffHsv));
+  
+}  /* end p_colordiff_guchar_cmask */
+
+
+/* ---------------------------------
+ * p_colordiff_guchar_cmask_RGBorHSV
+ * ---------------------------------
+ * returns difference of 2 colors as gdouble value
+ * in range 0.0 (exact match) to 1.0 (maximal difference)
+ */
+gdouble
+p_colordiff_guchar_cmask_RGBorHSV(GimpHSV *aHsvPtr, guchar *aPixelPtr
+                   , guchar *bPixelPtr
+                   , GapColorMaskParams *cmaskParPtr)
+{
+  gdouble colordiffRgb;
+  gdouble colordiffHsv;
+  
+  colordiffRgb = 0.0;
+  if (cmaskParPtr->connectByCorner == TRUE) ///// #### TODO
+  {
+    colordiffRgb = gap_colordiff_simple_guchar(aPixelPtr
+                   , bPixelPtr
+                   , cmaskParPtr->debugCoordinate
+                   );
+  }
+  colordiffHsv = gap_colordiff_guchar_GimpHSV(aHsvPtr
+                  , bPixelPtr
+                   , cmaskParPtr->colorSensitivity
+                   , cmaskParPtr->debugCoordinate
+                   );
+
+  return (MAX(colordiffRgb, colordiffHsv));
+
+}  /* end p_colordiff_guchar_cmask_RGBorHSV */
+
+
+
+/* ----------------------------------
+ * p_get_guides
+ * ----------------------------------
+ * get 1st horizontal and vertical guide.
+ *
+ * note that guides are not relevant for the productive processing
+ * but the 1st guide crossing is used to specify
+ * a coordinate where debug output shall be printed.
+ */
+static void
+p_get_guides(GapColorMaskParams *cmaskParPtr)
+{
+  gint32  guide_id;
+
+  guide_id = 0;
+
+  cmaskParPtr->guideRow = -1;
+  cmaskParPtr->guideCol = -1;
+
+  if(cmaskParPtr->dst_image_id < 0)
+  {
+     return;
+  }
+
+
+  while(TRUE)
+  {
+    guide_id = gimp_image_find_next_guide(cmaskParPtr->dst_image_id, guide_id);
+
+    if (guide_id < 1)
+    {
+       break;
+    }
+    else
+    {
+       gint32 orientation;
+
+       orientation = gimp_image_get_guide_orientation(cmaskParPtr->dst_image_id, guide_id);
+       if(orientation != 0)
+       {
+         if(cmaskParPtr->guideCol < 0)
+         {
+           cmaskParPtr->guideCol = gimp_image_get_guide_position(cmaskParPtr->dst_image_id, guide_id);
+         }
+       }
+       else
+       {
+         if(cmaskParPtr->guideRow < 0)
+         {
+           cmaskParPtr->guideRow = gimp_image_get_guide_position(cmaskParPtr->dst_image_id, guide_id);
+         }
+       }
+    }
+
+  }
+
+  if(gap_debug)
+  {
+    printf("dst_image_id:%d  guideCol:%d :%d\n"
+       ,(int)cmaskParPtr->dst_image_id
+       ,(int)cmaskParPtr->guideCol
+       ,(int)cmaskParPtr->guideRow
+       );
+  }
+
+}  /* end p_get_guides */
+
+/* ----------------------------------
+ * p_is_debug_active_on_coordinate
+ * ----------------------------------
+ * check if current processed coords (x/y) are on first guide crossing
+ * (for debug purpuse to force logging at koordinates selected via guides)
+ */
+static gboolean
+p_is_debug_active_on_coordinate(GapColorMaskParams *cmaskParPtr)
+{
+  if(cmaskParPtr->x == cmaskParPtr->guideCol)
+  {
+    if(cmaskParPtr->y == cmaskParPtr->guideRow)
+    {
+      return(TRUE);
+    }
+    if (cmaskParPtr->guideRow == -1)
+    {
+      /* there is no vertical guide available, in this case match all rows */
+      return(TRUE);
+    }
+  }
+  else
+  {
+    if((cmaskParPtr->y == cmaskParPtr->guideRow) && (cmaskParPtr->guideCol == -1))
+    {
+      /* there is no horizontal guide available, in this case match all coloumns */
+      return(TRUE);
+    }
+  }
+  return (FALSE);
+
+}  /* end p_is_debug_active_on_coordinate */
+
+
+
+
+/* --------------------------------------
+ * p_calculate_alpha_from_colormask_pixel  (SIMPLE VARIANT)
+ * --------------------------------------
+ * calculates transparency according to
+ * color difference of individual pixel in the processed
+ * layer and its corresponding pixel in the colormask.
+ *
+ * returns the alpha channel as byte value.
+ */
+static inline guchar
+p_calculate_alpha_from_colormask_pixel(gint maskBpp
+     , guchar *maskPtr
+     , guchar *origPtr
+     , GapColorMaskParams *cmaskParPtr
+     )
+{
+  gdouble colorDiff;
+
+  if (maskBpp > 3)
+  {
+    /* colormask has an alpha channel */
+    if (maskPtr[3] <= cmaskParPtr->triggerAlpha255)
+    {
+      /* transparent mask (below or equal trigger value)
+       * switches off color masking for that pixel
+       * return 255 (full opaque) as opacity value
+       * for the layermask.
+       * Note that an opaque layermask pixel
+       * preserves opacity as set via alpha channel unchanged when the layermask is applied.
+       * (no need to check color differences in that case)
+       */
+      return (255);
+    }
+  }
+
+  colorDiff = p_colordiff_guchar_cmask(maskPtr
+                                 , origPtr
+                                 , cmaskParPtr
+                                 );
+
+  p_set_dynamic_threshold_by_KeyColor(origPtr, cmaskParPtr);
+  
+  if(colorDiff <= cmaskParPtr->colorThreshold)
+  {
+    /* color matches good */
+    return (cmaskParPtr->lowerAlpha255);
+  }
+
+  if ((colorDiff < cmaskParPtr->hiColorThreshold)
+  &&  (cmaskParPtr->shadeRange > 0.0))
+  {
+    /* color matches extended range (calculate corresponding alpha shade value) */
+    gdouble opacity255;  /* range 0.0 to 255.0 */
+    guchar  alpha;
+    gdouble shadeFactor;
+
+    shadeFactor = (colorDiff - cmaskParPtr->colorThreshold) / cmaskParPtr->shadeRange;
+
+
+     opacity255 = GAP_BASE_MIX_VALUE(shadeFactor
+                                   , (gdouble)cmaskParPtr->lowerAlpha255
+                                   , (gdouble)cmaskParPtr->upperAlpha255
+                                   );
+    alpha = CLAMP((guchar)opacity255, 0, 255);
+
+    if (cmaskParPtr->debugCoordinate)
+    {
+      printf("p_calculate_alpha x:%03d y:%03d shadeFactor:%.4f shadeRange:%.4f colorDiff:%.6f  ALPHA:%d \n"
+             ,(int)cmaskParPtr->x
+             ,(int)cmaskParPtr->y
+             ,(float)shadeFactor
+             ,(float)cmaskParPtr->shadeRange
+             ,(float)colorDiff
+             ,(int)alpha
+             );
+    }
+
+    return (alpha);
+  }
+
+  /* color does not match */
+  return (cmaskParPtr->upperAlpha255);
+
+}  /* end p_calculate_alpha_from_colormask_pixel */
+
+
+
+
+/* ---------------------------------
+ * p_colormask_rgn_render_region
+ * ---------------------------------
+ * render transparency according to colormask
+ * to the layermask of the destination drawable.
+ */
+static void
+p_colormask_rgn_render_region (const GimpPixelRgn *maskPR
+                    ,const GimpPixelRgn *destPR
+                    ,const GimpPixelRgn *lmskPR
+                    ,GapColorMaskParams *cmaskParPtr)
+{
+  guint    row;
+  guchar* mask = maskPR->data;   /* the colormask */
+  guchar* dest = destPR->data;   /* the destination drawable */
+  guchar* lmsk = lmskPR->data;   /* the layermask of the destination drawable */
+
+
+  for (row = 0; row < destPR->h; row++)
+  {
+    guint  col;
+    guint  idxMask;
+    guint  idxDest;
+    guint  idxLmsk;
+
+    idxMask = 0;
+    idxDest = 0;
+    idxLmsk = 0;
+    for(col = 0; col < destPR->w; col++)
+    {
+      cmaskParPtr->x = destPR->x + col;
+      cmaskParPtr->y = destPR->y + row;
+      lmsk[idxLmsk] = p_calculate_alpha_from_colormask_pixel(maskPR->bpp
+                          , &mask[idxMask]     /* maskPtr */
+                          , &dest[idxDest]     /* origPtr */
+                          , cmaskParPtr
+                          );
+
+      /* ------ start debug code block -----*/
+      //if(gap_debug)
+      {
+        cmaskParPtr->debugCoordinate = p_is_debug_active_on_coordinate(cmaskParPtr);
+        if (cmaskParPtr->debugCoordinate)
+        {
+           gdouble colorDiff;
+           colorDiff = p_colordiff_guchar_cmask( &mask[idxMask]
+                               , &dest[idxDest]
+                               , cmaskParPtr
+                               );
+
+           printf("colormask x:%03d y:%03d  ALPHA:%d colorDiff:%.6f (colorThreshold:%.6f)"
+             ,(int)cmaskParPtr->x
+             ,(int)cmaskParPtr->y
+             ,(int)lmsk[idxLmsk]
+             ,(float)colorDiff
+             ,(float)cmaskParPtr->colorThreshold
+             );
+
+
+           if (maskPR->bpp > 3)
+           {
+             printf(" TRIGGER_ALPHA:%d MASK_ALPHA:%d"
+               ,(int)cmaskParPtr->triggerAlpha255
+               ,(int)mask[idxMask +3]
+               );
+           }
+
+           printf("\n");
+        }
+      }
+      /* ------ end debug code block -----*/
+
+
+      idxDest += destPR->bpp;
+      idxMask += maskPR->bpp;
+      idxLmsk += lmskPR->bpp;
+    }
+
+    mask += maskPR->rowstride;
+    dest += destPR->rowstride;
+    lmsk += lmskPR->rowstride;
+
+    if(cmaskParPtr->doProgress)
+    {
+      p_handle_progress(cmaskParPtr, maskPR->w, "p_colormask_rgn_render_region");
+    }
+  }
+
+}  /* end p_colormask_rgn_render_region */
+
+
+
+
+
+
+
+
+
+
+/* --------------------------------
+ * p_add_point
+ * --------------------------------
+ * add specified coords as new element to the begin of the pointList
+ */
+static inline void
+p_add_point(GapColorMaskParams *cmaskParPtr, gint32 ix, gint32 iy)
+{
+  GapAreaCoordPoint *point;
+//  static gint32 highWaterMark = 0;
+
+  point = g_new ( GapAreaCoordPoint, 1 );
+
+  point->x = ix;
+  point->y = iy;
+
+  point->next = cmaskParPtr->pointList;
+
+  cmaskParPtr->pointList = point;
+
+  cmaskParPtr->pointCount++;
+
+//   //if(gap_debug)
+//   {
+//     if(cmaskParPtr->pointCount > highWaterMark)
+//     {
+//        highWaterMark = cmaskParPtr->pointCount;
+//        printf("(%d) add pointCount:%d Seed x/y: (%d/%d) x/y: (%d/%d)\n"
+//          ,(int)cmaskParPtr->currentAreaPass
+//          ,(int)cmaskParPtr->pointCount
+//          ,(int)cmaskParPtr->seed_ix
+//          ,(int)cmaskParPtr->seed_iy
+//          ,(int)ix
+//          ,(int)iy
+//          );
+//     }
+//   }
+//
+//   if(cmaskParPtr->pointCount > (cmaskParPtr->width * cmaskParPtr->height))
+//   {
+//      printf("(%d) internal error... pointCount:%d seed x/y: (%d/%d)\n"
+//          ,(int)cmaskParPtr->currentAreaPass
+//          ,(int)cmaskParPtr->pointCount
+//          ,(int)cmaskParPtr->seed_ix
+//          ,(int)cmaskParPtr->seed_iy
+//        );
+//      exit(44);
+//   }
+
+}  /* end p_add_point */
+
+
+/* --------------------------------
+ * p_remove_first_point
+ * --------------------------------
+ * remove first element from the pointList
+ */
+static inline void
+p_remove_first_point(GapColorMaskParams *cmaskParPtr)
+{
+  GapAreaCoordPoint *point;
+
+  point = cmaskParPtr->pointList;
+
+  if (point != NULL)
+  {
+//     if(gap_debug)
+//     {
+//        printf("(%d) remove pointCount:%d point x/y: (%d/%d)\n"
+//          ,(int)cmaskParPtr->currentAreaPass
+//          ,(int)cmaskParPtr->pointCount
+//          ,(int)point->x
+//          ,(int)point->y
+//          );
+//     }
+    cmaskParPtr->pointCount--;
+    cmaskParPtr->pointList = (GapAreaCoordPoint *)point->next;
+    g_free(point);
+  }
+  else
+  {
+    //if(gap_debug)
+    {
+       printf("(%d) remove pointCount:%d seed x/y: (%d/%d)  pointList is NULL!\n"
+         ,(int)cmaskParPtr->currentAreaPass
+         ,(int)cmaskParPtr->pointCount
+         ,(int)cmaskParPtr->seed_ix
+         ,(int)cmaskParPtr->seed_iy
+         );
+    }
+  }
+
+}  /* end p_remove_first_point */
+
+
+/* --------------------------------------------
+ * p_free_point_list
+ * --------------------------------------------
+ */
+static void
+p_free_point_list(GapColorMaskParams *cmaskParPtr)
+{
+  while(cmaskParPtr->pointList != NULL)
+  {
+      p_remove_first_point(cmaskParPtr);
+  }
+}  /* end p_free_point_list */
+
+
+
+/* -------------------------------------------------------------------- */
+/* ------------------ stuff for EDGE ALGORITHM  ----- START ------------ */
+/* -------------------------------------------------------------------- */
+
+/* --------------------------------------
+ * p_edge_calculate_pixel_opacity  (EDGE VARIANT)
+ * --------------------------------------
+ * calculates transparency according to
+ * color difference of individual pixel in the processed
+ * layer and its corresponding pixel in the colormask.
+ *
+ * returns the calculated opacity as byte value.
+ */
+static inline guchar
+p_edge_calculate_pixel_opacity(gint maskBpp
+     , guchar *maskPtr
+     , guchar *origPtr
+     , guchar *origUpper
+     , guchar *origLeft
+     , guchar *lmskUpper
+     , guchar *lmskLeft
+     , GapColorMaskParams *cmaskParPtr
+     )
+{
+  gdouble colorDiff;
+  guchar  nbAlpha;
+  guchar          pixel[4];
+  guchar          layermaskPixel[4];
+
+  if (maskBpp > 3)
+  {
+    /* colormask has an alpha channel */
+    if (maskPtr[3] <= cmaskParPtr->triggerAlpha255)
+    {
+      /* transparent mask (below or equal trigger value)
+       * switches off color masking for that pixel
+       * return 255 (full opaque) as opacity value
+       * for the layermask.
+       * Note that an opaque layermask pixel
+       * preserves opacity as set via alpha channel unchanged when the layermask is applied.
+       * (no need to check color differences in that case)
+       */
+      return (255);
+    }
+  }
+
+  colorDiff = p_colordiff_guchar_cmask(maskPtr
+                                 , origPtr
+                                 , cmaskParPtr
+                                 );
+
+  p_set_dynamic_threshold_by_KeyColor(origPtr, cmaskParPtr);
+
+  if(colorDiff <= cmaskParPtr->colorThreshold)
+  {
+    /* color matches good */
+    return (cmaskParPtr->lowerAlpha255);
+  }
+
+  if (colorDiff >= cmaskParPtr->hiColorThreshold)
+  {
+    /* color does not match */
+    return (cmaskParPtr->upperAlpha255);
+  }
+
+
+///////////////////////////// DEBUG
+//   if(TRUE)
+//   {
+//     gdouble shadeFactor;
+//     gdouble green;
+//
+//     shadeFactor = (colorDiff - cmaskParPtr->colorThreshold) / cmaskParPtr->shadeRange;
+//     green = GAP_BASE_MIX_VALUE(shadeFactor
+//                               , (gdouble)128.0
+//                               , (gdouble)255.0
+//                               );
+//
+//     origPtr[0] = 32;    /* origRed   */
+//     origPtr[1] = CLAMP((guchar)green, 0, 255);;   /* origGreen */
+//     origPtr[2] = 32;    /* origBlue  */
+//
+//     return (cmaskParPtr->lowerAlpha255);
+//   }
+///////////////////////////// DEBUG
+
+
+
+  /* color matches range between the thresholds
+   * in this case check if the current pixel is an edge pixel
+   * for edge pixels use lower alpha,
+   * for non-edge pixels use same alpha as the already processed left (or upper) neighbour pixel
+   */
+
+  nbAlpha = cmaskParPtr->lowerAlpha255;
+
+  /* check upper neigbour */
+//   if(gap_debug)
+//   {
+//     printf("X/Y: %d/%d origUpper:%d lmskUpper:%d\n"
+//       , (int)cmaskParPtr->x
+//       , (int)cmaskParPtr->y
+//       , (int)origUpper
+//       , (int)lmskUpper
+//       );
+//   }
+
+  if (origUpper != NULL)
+  {
+    colorDiff = p_colordiff_guchar_cmask(origUpper
+                                 , origPtr
+                                 , cmaskParPtr
+                                 );
+    nbAlpha = lmskUpper[0];
+  }
+  else
+  {
+    /* current pixel is first in tile column
+     * in this special case use pixel fether to access neighbour pixel
+     */
+    if(cmaskParPtr->y > 0)
+    {
+      gimp_pixel_fetcher_get_pixel (cmaskParPtr->pftDest
+                                , cmaskParPtr->x
+                                , cmaskParPtr->y-1
+                                , &pixel[0]
+                                );
+      gimp_pixel_fetcher_get_pixel (cmaskParPtr->pftDestLayerMask
+                                , cmaskParPtr->x
+                                , cmaskParPtr->y-1
+                                , &layermaskPixel[0]
+                                );
+      nbAlpha = layermaskPixel[0];
+      colorDiff = p_colordiff_guchar_cmask(&pixel[0]
+                                   , origPtr
+                                   , cmaskParPtr
+                                   );
+    }
+    else
+    {
+      colorDiff = 1.0;  /* coordinate x == 0 is considered as edge pixel */
+    }
+  }
+
+  if (colorDiff >= cmaskParPtr->edgeColorThreshold)
+  {
+    return (cmaskParPtr->lowerAlpha255);
+  }
+
+
+
+  /* check left neigbour */
+
+//   if(gap_debug)
+//   {
+//     printf("X/Y: %d/%d origLeft:%d lmskLeft:%d\n"
+//       , (int)cmaskParPtr->x
+//       , (int)cmaskParPtr->y
+//       , (int)origLeft
+//       , (int)lmskLeft
+//       );
+//   }
+
+  if (origLeft != NULL)
+  {
+    colorDiff = p_colordiff_guchar_cmask(origLeft
+                                 , origPtr
+                                 , cmaskParPtr
+                                 );
+    nbAlpha = lmskLeft[0];
+  }
+  else
+  {
+    /* current pixel is first in tile row
+     * in this special case use pixel fether to access neighbour pixel
+     */
+    if(cmaskParPtr->x > 0)
+    {
+      gimp_pixel_fetcher_get_pixel (cmaskParPtr->pftDest
+                                , cmaskParPtr->x-1
+                                , cmaskParPtr->y
+                                , &pixel[0]
+                                );
+      gimp_pixel_fetcher_get_pixel (cmaskParPtr->pftDestLayerMask
+                                , cmaskParPtr->x-1
+                                , cmaskParPtr->y
+                                , &layermaskPixel[0]
+                                );
+      nbAlpha = layermaskPixel[0];
+      colorDiff = p_colordiff_guchar_cmask(&pixel[0]
+                                   , origPtr
+                                   , cmaskParPtr
+                                   );
+    }
+    else
+    {
+      colorDiff = 1.0;  /* coordinate x == 0 is considered as edge pixel */
+    }
+  }
+
+  if (colorDiff >= cmaskParPtr->edgeColorThreshold)
+  {
+    return (cmaskParPtr->lowerAlpha255);
+  }
+
+
+  return (nbAlpha);
+}  /* end p_edge_calculate_pixel_opacity */
+
+
+
+/* ---------------------------------
+ * p_colormask_edge_rgn_render_region
+ * ---------------------------------
+ * render transparency according to colormask
+ * to the layermask of the destination drawable.
+ */
+static void
+p_colormask_edge_rgn_render_region (const GimpPixelRgn *maskPR
+                    ,const GimpPixelRgn *origPR
+                    ,const GimpPixelRgn *lmskPR
+                    ,GapColorMaskParams *cmaskParPtr)
+{
+  guint    row;
+  guchar* mask = maskPR->data;   /* the colormask */
+  guchar* orig = origPR->data;   /* the drawable */
+  guchar* lmsk = lmskPR->data;   /* the layermask of the drawable */
+
+  guchar* origUpper = NULL;      /* the upper pixelrow of the drawable */
+  guchar* lmskUpper = NULL;      /* the upper pixelrow of the layermask of the drawable */
+
+  for (row = 0; row < origPR->h; row++)
+  {
+    guchar* origLeft;      /* the left neighbor pixel of the drawable */
+    guchar* lmskLeft;      /* the upper pixelrow of the layermask of the drawable */
+    guint  col;
+    guint  idxMask;
+    guint  idxOrig;
+    guint  idxLmsk;
+
+    idxMask = 0;
+    idxOrig = 0;
+    idxLmsk = 0;
+    origLeft = NULL;
+    lmskLeft = NULL;
+
+    cmaskParPtr->y = origPR->y + row;
+
+//     if(gap_debug)
+//     {
+//       printf("ROW:%d Y: %d origUpper:%d lmskUpper:%d\n"
+//       , (int)row
+//       , (int)cmaskParPtr->y
+//       , (int)origUpper
+//       , (int)lmskUpper
+//       );
+//     }
+
+    for(col = 0; col < origPR->w; col++)
+    {
+      cmaskParPtr->x = origPR->x + col;
+      if(origUpper != NULL)
+      {
+        lmsk[idxLmsk] = p_edge_calculate_pixel_opacity(maskPR->bpp
+                          , &mask[idxMask]     /* maskPtr */
+                          , &orig[idxOrig]     /* origPtr */
+                          , &origUpper[idxOrig]
+                          , origLeft
+                          , &lmskUpper[idxLmsk]
+                          , lmskLeft
+                          , cmaskParPtr
+                          );
+      }
+      else
+      {
+        lmsk[idxLmsk] = p_edge_calculate_pixel_opacity(maskPR->bpp
+                          , &mask[idxMask]     /* maskPtr */
+                          , &orig[idxOrig]     /* origPtr */
+                          , NULL
+                          , origLeft
+                          , NULL
+                          , lmskLeft
+                          , cmaskParPtr
+                          );
+      }
+
+      origLeft = &orig[idxOrig];
+      lmskLeft = &lmsk[idxLmsk];
+
+      idxOrig += origPR->bpp;
+      idxMask += maskPR->bpp;
+      idxLmsk += lmskPR->bpp;
+    }
+    lmskUpper = lmsk;
+    origUpper = orig;
+
+
+    mask += maskPR->rowstride;
+    orig += origPR->rowstride;
+    lmsk += lmskPR->rowstride;
+
+    if(cmaskParPtr->doProgress)
+    {
+      p_handle_progress(cmaskParPtr, maskPR->w, "p_colormask_edge_rgn_render_region");
+    }
+  }
+
+}  /* end p_colormask_edge_rgn_render_region */
+
+
+/* -------------------------------------------------------------------- */
+/* ------------------ stuff for EDGE ALGORITHM  ----- END ------------- */
+/* -------------------------------------------------------------------- */
+
+
+
+
+
+
+/* ---------------------------------------------
+ * p_get_distances_to_nearest_transparent_pixels
+ * ---------------------------------------------
+ * check distances to next transparent neghbour pixels (where opacity value in the layermask
+ * is lower or equal to the specified transparencyLevel value)
+ * in all 4 directions.
+ *   [0]  right neigbour
+ *   [1]  left neigbour
+ *   [2]  lower neigbour
+ *   [3]  upper neigbour
+ *
+ * Note: in case there is no transparent pixel found within featherRadius
+ * the nbDistance value for that direction is set to 100 + featherRadius
+ * to indicate that no transparent pixel is near.
+ */
+static inline void
+p_get_distances_to_nearest_transparent_pixels(GapColorMaskParams *cmaskParPtr, gdouble *nbDistance)
+{
+  guchar  nbOrigPixels[4][4];
+  gint    ii;
+  gint    xx;
+  gint    yy;
+
+  nbDistance[0] = 100 + cmaskParPtr->featherRadius;
+  nbDistance[1] = 100 + cmaskParPtr->featherRadius;
+  nbDistance[2] = 100 + cmaskParPtr->featherRadius;
+  nbDistance[3] = 100 + cmaskParPtr->featherRadius;
+
+
+  for(ii=1; ii <= cmaskParPtr->featherRadius; ii++)
+  {
+    xx = cmaskParPtr->x + ii;
+    if (xx >= cmaskParPtr->width)
+    {
+      break;
+    }
+    gimp_pixel_fetcher_get_pixel (cmaskParPtr->pftDestLayerMask
+                                 , xx
+                                 , cmaskParPtr->y
+                                 , nbOrigPixels[0]
+                                 );
+    if(nbOrigPixels[0][0] <= cmaskParPtr->transparencyLevel255)
+    {
+      nbDistance[0] = ii;
+      break;
+    }
+  }
+
+  for(ii=1; ii <= cmaskParPtr->featherRadius; ii++)
+  {
+    xx = cmaskParPtr->x - ii;
+    if (xx < 0)
+    {
+      break;
+    }
+    gimp_pixel_fetcher_get_pixel (cmaskParPtr->pftDestLayerMask
+                                 , xx
+                                 , cmaskParPtr->y
+                                 , nbOrigPixels[1]
+                                 );
+    if(nbOrigPixels[1][0] <= cmaskParPtr->transparencyLevel255)
+    {
+      nbDistance[1] = ii;
+      break;
+    }
+  }
+
+  for(ii=1; ii <= cmaskParPtr->featherRadius; ii++)
+  {
+    yy = cmaskParPtr->y + ii;
+    if (yy >= cmaskParPtr->height)
+    {
+      break;
+    }
+    gimp_pixel_fetcher_get_pixel (cmaskParPtr->pftDestLayerMask
+                                 , cmaskParPtr->x
+                                 , yy
+                                 , nbOrigPixels[2]
+                                 );
+    if(nbOrigPixels[2][0] <= cmaskParPtr->transparencyLevel255)
+    {
+      nbDistance[2] = ii;
+      break;
+    }
+  }
+
+  for(ii=1; ii <= cmaskParPtr->featherRadius; ii++)
+  {
+    yy = cmaskParPtr->y - ii;
+    if (yy < 0)
+    {
+      break;
+    }
+    gimp_pixel_fetcher_get_pixel (cmaskParPtr->pftDestLayerMask
+                                 , cmaskParPtr->x
+                                 , yy
+                                 , nbOrigPixels[3]
+                                 );
+    if(nbOrigPixels[3][0] <= cmaskParPtr->transparencyLevel255)
+    {
+      nbDistance[3] = ii;
+      break;
+    }
+  }
+
+
+}  /* end p_get_distances_to_nearest_transparent_pixels */
+
+
+
+/* ---------------------------------
+ * p_blur_alpha_at_edge_pixels
+ * ---------------------------------
+ * calculate blurred alpha value depentent to distance to next
+ * transparent pixel (whre transparency is below level)
+ * to the layermask of the destination drawable.
+ */
+static inline guchar
+p_blur_alpha_at_edge_pixels(guchar currentAlpha, GapColorMaskParams *cmaskParPtr)
+{
+  gdouble distance[4];
+  gdouble minDistance;
+  gdouble opacity255;  /* range 0.0 to 255.0 */
+  guchar  alpha;
+  gdouble radiusFactor;
+  gint    ii;
+
+
+  p_get_distances_to_nearest_transparent_pixels(cmaskParPtr, &distance[0]);
+
+  minDistance = 100 + cmaskParPtr->featherRadius;
+  for(ii=0; ii < 4; ii++)
+  {
+     if (distance[ii] < minDistance)
+     {
+       minDistance = distance[ii];
+     }
+  }
+
+  if (cmaskParPtr->debugCoordinate)
+  {
+    printf("p_smooth_alpha_at_edges distances: %.1f  %.1f %.1f %.1f  minDistance %.1f\n"
+      ,(float)distance[0]
+      ,(float)distance[1]
+      ,(float)distance[2]
+      ,(float)distance[3]
+      ,(float)minDistance
+      );
+  }
+
+  if(minDistance > cmaskParPtr->featherRadius)
+  {
+    /* no transparent pixel is near, return the unchanged alpha value */
+    return (currentAlpha);
+  }
+
+  /* one of the neigbours within featherRadius is transparent (below transparencyLevel)
+   * now calculate the fading opacity for this current edge pixel
+   * (the bigger the distande, the more opaque)
+   * note that fading is done within the range of specified upper and lower opacity parameters
+   */
+  radiusFactor = minDistance / (1 + cmaskParPtr->featherRadius);
+  opacity255 = (gdouble)(currentAlpha - cmaskParPtr->transparencyLevel255);
+  opacity255 *= radiusFactor;
+  opacity255 *= cmaskParPtr->opacityFactor;
+
+  alpha = CLAMP(((guchar)opacity255 + cmaskParPtr->transparencyLevel255), 0, 255);
+
+  if (cmaskParPtr->debugCoordinate)
+  {
+    printf("p_blur_alpha_at_edge_pixels at xy:(%d / %d) radiusFactor:%.4f opacityFactor:%.4f"
+           " transparencyLevel:%d  opacity255: %.1f  blured_ALPHA %d\n"
+      ,(int)cmaskParPtr->x
+      ,(int)cmaskParPtr->y
+      ,(float)radiusFactor
+      ,(float)cmaskParPtr->opacityFactor
+      ,(int)cmaskParPtr->transparencyLevel255
+      ,(float)opacity255
+      ,(int)alpha
+      );
+  }
+
+  return (alpha);
+
+}  /* end p_blur_alpha_at_edge_pixels */
+
+
+
+/* ---------------------------------
+ * p_smooth_edges_rgn_render_region
+ * ---------------------------------
+ * render transparency according to colormask
+ * to the layermask of the destination drawable.
+ */
+static void
+p_smooth_edges_rgn_render_region (const GimpPixelRgn *maskPR
+                    ,const GimpPixelRgn *lmskPR
+                    ,GapColorMaskParams *cmaskParPtr)
+{
+  guint    row;
+  guchar* mask = maskPR->data;   /* the colormask */
+  guchar* lmsk = lmskPR->data;   /* the layermask of the destination drawable */
+
+
+  for (row = 0; row < lmskPR->h; row++)
+  {
+    guint  col;
+    guint  idxMask;
+    guint  idxLmsk;
+
+    idxMask = 0;
+    idxLmsk = 0;
+    for(col = 0; col < lmskPR->w; col++)
+    {
+      /* process only non transparent pixels in the layermask */
+      if (lmsk[idxLmsk] > cmaskParPtr->transparencyLevel255)
+      {
+        gboolean isProtected;
+
+        isProtected = FALSE;
+        if (maskPR->bpp > 3)
+        {
+          if (mask[idxMask +3] <= cmaskParPtr->triggerAlpha255)
+          {
+            isProtected = TRUE;
+          }
+        }
+
+        if (!isProtected)
+        {
+          cmaskParPtr->x = maskPR->x + col;
+          cmaskParPtr->y = maskPR->y + row;
+
+          //if(gap_debug)
+          {
+            cmaskParPtr->debugCoordinate = p_is_debug_active_on_coordinate(cmaskParPtr);
+          }
+
+          /* re-calculate opacity respecting featherRadius near edges */
+          lmsk[idxLmsk] = p_blur_alpha_at_edge_pixels(lmsk[idxLmsk], cmaskParPtr);
+        }
+      }
+
+      idxMask += maskPR->bpp;
+      idxLmsk += lmskPR->bpp;
+    }
+
+    mask += maskPR->rowstride;
+    lmsk += lmskPR->rowstride;
+
+    if(cmaskParPtr->doProgress)
+    {
+      p_handle_progress(cmaskParPtr, maskPR->w, "p_smooth_edges_rgn_render_region");
+    }
+  }
+
+}  /* end p_smooth_edges_rgn_render_region */
+
+
+
+/* -------------------------------------------------------------------- */
+/* ------------------ stuff for remove ISOLATED PIXELS ---------------- */
+/* -------------------------------------------------------------------- */
+
+/* --------------------------------------
+ * p_map_alpha
+ * --------------------------------------
+ * returns 0 if alpha considerd as transparent
+ *           otherwise 255
+ */
+static inline guchar
+p_map_alpha(GapColorMaskParams *cmaskParPtr, guchar value)
+{
+  if (value <=  cmaskParPtr->transparencyLevel255)
+  {
+    return (0);
+  }
+  return (255);
+} /* end p_map_alpha */
+
+
+
+
+/* ---------------------------------------------
+ * p_is_isolated_pixel
+ * ---------------------------------------------
+ * returns TRUE in case pixel is isolated
+ * the isolation check is done by comparing the currentAlpha value
+ * against neighbour pixels in the layermask.
+ * In case there are no (or too few) neigbours with matching opacity
+ * the pixel is considered as isolated pixel
+ *
+ */
+static inline gboolean
+p_is_isolated_pixel(GapColorMaskParams *cmaskParPtr, guchar currentAlpha)
+{
+  static gboolean nbCountLookup[5][5] = {
+  /*  0      1      2     3     4        countCornerNb
+   *
+   *  ...    #..    #..    #.#    #.#
+   *  .o.    .o.    .o.    .o.    .o.
+   *  ...    ...    ..#    ..#    #.#
+   */
+  {   TRUE,  TRUE,  TRUE,  TRUE,  TRUE  }  /* countEdgeNb == 0 */
+
+
+  /*  0      1      2     3     4        countCornerNb
+   *
+   *  .#.    ##.    ##.    ###    ###
+   *  .o.    .o.    .o.    .o.    .o.
+   *  ...    ...    ..#    ..#    #.#
+   */
+ ,{   TRUE,  TRUE,  TRUE,  FALSE, FALSE  }  /* countEdgeNb == 1 */
+
+
+
+  /*  0      1      2     3     4        countCornerNb
+   *
+   *  .#.    ##.    ##.    ###    ###
+   *  .o.    .o.    .o.    .o.    .o.
+   *  .#.    .#.    .##    .##    ###
+   */
+ ,{   TRUE,  FALSE, FALSE, FALSE, FALSE  }  /* countEdgeNb == 2 */
+
+
+  /*  0      1      2     3     4        countCornerNb
+   *
+   *  .#.    ##.    ##.    ###    ###
+   *  .o#    .o#    .o#    .o#    .o#
+   *  .#.    .#.    .##    .##    ###
+   */
+ ,{   FALSE, FALSE, FALSE,  FALSE, FALSE }  /* countEdgeNb == 3 */
+
+
+
+  /*  0      1      2     3     4        countCornerNb
+   *
+   *  .#.    ##.    ##.    ###    ###
+   *  #o#    #o#    #o#    #o#    #o#
+   *  .#.    .#.    .##    .##    ###
+   */
+ ,{   FALSE, FALSE, FALSE, FALSE, FALSE }  /* countEdgeNb == 4 */
+  };
+
+  gboolean isIsolated;
+  gint     countCornerNb;
+  gint     countEdgeNb;
+
+  guchar  nbLayermaskPixel[4];
+  guchar  refAlphaValue;
+  gint    xx;
+  gint    yy;
+
+  refAlphaValue = p_map_alpha(cmaskParPtr, currentAlpha);
+  countEdgeNb = 0;
+
+  /* right neighbor */
+  xx = cmaskParPtr->x + 1;
+  if (xx < cmaskParPtr->width)
+  {
+    gimp_pixel_fetcher_get_pixel (cmaskParPtr->pftDestLayerMask
+                                 , xx
+                                 , cmaskParPtr->y
+                                 , &nbLayermaskPixel[0]
+                                 );
+
+    if(p_map_alpha(cmaskParPtr, nbLayermaskPixel[0]) == refAlphaValue)
+    {
+      countEdgeNb++;
+    }
+  }
+
+  /* left neighbor */
+  xx = cmaskParPtr->x - 1;
+  if (xx >= 0)
+  {
+    gimp_pixel_fetcher_get_pixel (cmaskParPtr->pftDestLayerMask
+                                 , xx
+                                 , cmaskParPtr->y
+                                 , &nbLayermaskPixel[0]
+                                 );
+
+    if(p_map_alpha(cmaskParPtr, nbLayermaskPixel[0]) == refAlphaValue)
+    {
+      countEdgeNb++;
+    }
+  }
+
+
+  /* lower neighbor */
+  yy = cmaskParPtr->y + 1;
+  if (yy < cmaskParPtr->height)
+  {
+    gimp_pixel_fetcher_get_pixel (cmaskParPtr->pftDestLayerMask
+                                 , cmaskParPtr->x
+                                 , yy
+                                 , &nbLayermaskPixel[0]
+                                 );
+
+    if(p_map_alpha(cmaskParPtr, nbLayermaskPixel[0]) == refAlphaValue)
+    {
+      countEdgeNb++;
+    }
+  }
+
+  if(countEdgeNb > 2)
+  {
+    return (FALSE);
+  }
+
+  /* upper neighbor */
+  yy = cmaskParPtr->y - 1;
+  if (yy >= 0)
+  {
+    gimp_pixel_fetcher_get_pixel (cmaskParPtr->pftDestLayerMask
+                                 , cmaskParPtr->x
+                                 , yy
+                                 , &nbLayermaskPixel[0]
+                                 );
+
+    if(p_map_alpha(cmaskParPtr, nbLayermaskPixel[0]) == refAlphaValue)
+    {
+      countEdgeNb++;
+    }
+  }
+
+  if(countEdgeNb > 2)
+  {
+    return (FALSE);
+  }
+
+  /* there are 2 or less neighbours
+   * check corner neighbours in that case
+   */
+
+  countCornerNb = 0;
+
+  /* right lower neighbor */
+  xx = cmaskParPtr->x + 1;
+  yy = cmaskParPtr->y + 1;
+  if ((xx < cmaskParPtr->width) && (yy < cmaskParPtr->height))
+  {
+    gimp_pixel_fetcher_get_pixel (cmaskParPtr->pftDestLayerMask
+                                 , xx
+                                 , yy
+                                 , &nbLayermaskPixel[0]
+                                 );
+
+    if(p_map_alpha(cmaskParPtr, nbLayermaskPixel[0]) == refAlphaValue)
+    {
+      countCornerNb++;
+    }
+  }
+
+  /* left upper neighbor */
+  xx = cmaskParPtr->x - 1;
+  yy = cmaskParPtr->y - 1;
+  if ((xx >= 0) && (yy >= 0))
+  {
+    gimp_pixel_fetcher_get_pixel (cmaskParPtr->pftDestLayerMask
+                                 , xx
+                                 , yy
+                                 , &nbLayermaskPixel[0]
+                                 );
+
+    if(p_map_alpha(cmaskParPtr, nbLayermaskPixel[0]) == refAlphaValue)
+    {
+      countCornerNb++;
+    }
+  }
+
+
+  /* left lower neighbor */
+  xx = cmaskParPtr->x - 1;
+  yy = cmaskParPtr->y + 1;
+  if ((xx >= 0) && (yy < cmaskParPtr->height))
+  {
+    gimp_pixel_fetcher_get_pixel (cmaskParPtr->pftDestLayerMask
+                                 , xx
+                                 , yy
+                                 , &nbLayermaskPixel[0]
+                                 );
+
+    if(p_map_alpha(cmaskParPtr, nbLayermaskPixel[0]) == refAlphaValue)
+    {
+      countCornerNb++;
+    }
+  }
+
+
+  /* right upper neighbor */
+  xx = cmaskParPtr->x + 1;
+  yy = cmaskParPtr->y - 1;
+  if ((xx < cmaskParPtr->width) && (yy >= 0))
+  {
+    gimp_pixel_fetcher_get_pixel (cmaskParPtr->pftDestLayerMask
+                                 , xx
+                                 , yy
+                                 , &nbLayermaskPixel[0]
+                                 );
+
+    if(p_map_alpha(cmaskParPtr, nbLayermaskPixel[0]) == refAlphaValue)
+    {
+      countCornerNb++;
+    }
+  }
+
+  isIsolated = nbCountLookup[countEdgeNb][countCornerNb];
+
+  if (cmaskParPtr->debugCoordinate)
+  {
+    printf("countEdgeNb:%d countCornerNb:%d isIsolated:%d\n"
+       ,(int)countEdgeNb
+       ,(int)countCornerNb
+       ,(int)isIsolated
+       );
+  }
+
+  return (isIsolated);
+
+}  /* end p_is_isolated_pixel */
+
+
+
+/* --------------------------------------------
+ * p_isle_check_and_mark_nb_pixel
+ * --------------------------------------------
+ * checks if the specified neighbor pixel at coords nx, ny
+ * has same opacity value as refOpacity (e.g the same opacity as the seed pixel).
+ * If this is the case increment pixel count and
+ * and add nx/ny to the point list (to initiate check of further neighbour pixels)
+ *
+ * The isleAreaTable is updated to mark already processed pixels
+ * (either as WORK_VISITED_BUT_NOT_PART_OF_CURRENT_AREA or as WORK_PART_OF_CURRENT_AREA)
+ *
+ */
+static void
+p_isle_check_and_mark_nb_pixel(GapColorMaskParams *cmaskParPtr, gint nx, gint ny, guchar refOpacity)
+{
+  guchar          pixel[4];
+  gint32 ax;
+  gint32 ay;
+
+
+  ax = nx + cmaskParPtr->seedOffsetX;
+  ay = ny + cmaskParPtr->seedOffsetY;
+
+  if (cmaskParPtr->isleAreaTable[ax][ay] != WORK_UNCHECKED)
+  {
+     /* this neighbor pixel is already processed (nothing to be done in that case) */
+     return;
+  }
+
+
+  gimp_pixel_fetcher_get_pixel (cmaskParPtr->pftDestLayerMask
+                                , nx
+                                , ny
+                                , &pixel[0]
+                               );
+
+  if (pixel[0] != refOpacity)
+  {
+    /* currently processed layermask pixel has other opacity than current seed
+     * and is not member of the checked isle area
+     */
+    cmaskParPtr->isleAreaTable[ax][ay] = WORK_VISITED_BUT_NOT_PART_OF_CURRENT_AREA;
+    return;
+  }
+
+  /* mark this neighbor pixel */
+  cmaskParPtr->isleAreaTable[ax][ay] = WORK_PART_OF_CURRENT_AREA;
+  cmaskParPtr->pixelCount++;
+  p_add_point(cmaskParPtr, nx, ny);
+
+  if (cmaskParPtr->debugCoordinate)
+  {
+    printf("ClipArea Check MATCH at nx:%d ny:%d pixelCount:%d refOpacity:%d opacity:%d\n"
+      ,(int)nx
+      ,(int)ny
+      ,(int)cmaskParPtr->pixelCount
+      ,(int)refOpacity
+      ,(int)pixel[0]
+      );
+  }
+
+}  /* p_isle_check_and_mark_nb_pixel */
+
+
+
+/* --------------------------------------------
+ * p_is_pixel_a_small_isolated_area
+ * --------------------------------------------
+ *
+ * find pixels of same opacity as the specified refOpacity of the current processed seed pixel (cmaskParPtr->x / y)
+ * and count neighbor pixel (connected at edges) with same opacity value.
+ * Note that the search is limited to a small clipping area that fits into isleAreaTable size
+ * This is done for performance reasons.
+ *
+ * return TRUE  in case the seed pixel is isolated 
+ * return FALSE in case seed pixel is part of an area with same opacity
+ *                      that is larger than the specified isleAreaPixelLimit,
+ *                      OR in case the area touches the border of the checked clipping area rectangle.
+ *
+ *
+ */
+static gboolean
+p_is_pixel_a_small_isolated_area(GapColorMaskParams *cmaskParPtr, guchar refOpacity)
+{
+  gint32          ix;
+  gint32          iy;
+  gint32          ax;
+  gint32          ay;
+
+  gint            clipSelX1;
+  gint            clipSelX2;
+  gint            clipSelY1;
+  gint            clipSelY2;
+  gint            maxClipRadius;
+  gint            usedArraySize;
+  gboolean        isBorderReached;
+  
+  
+  maxClipRadius = MIN(ISLE_AREA_MAX_RADIUS, cmaskParPtr->isleRadius);
+  usedArraySize = (1 + (2 * maxClipRadius));
+
+  cmaskParPtr->pointCount = 0;
+
+  cmaskParPtr->seedOffsetX = maxClipRadius - cmaskParPtr->x;
+  cmaskParPtr->seedOffsetY =  maxClipRadius - cmaskParPtr->y;
+
+
+  /* clipping area boundaries to limit area checks
+   * for a small rectangular area +- maxClipRadius around current pixel
+   */
+  clipSelX1 = MAX(cmaskParPtr->sel_x1, (cmaskParPtr->x - maxClipRadius));
+  clipSelY1 = MAX(cmaskParPtr->sel_y1, (cmaskParPtr->y - maxClipRadius));
+
+  clipSelX2 = MIN(cmaskParPtr->sel_x2, (cmaskParPtr->x + maxClipRadius));
+  clipSelY2 = MIN(cmaskParPtr->sel_y2, (cmaskParPtr->y + maxClipRadius));
+
+
+  /* reset area context, clear used part of the isleAreaTable */
+  for(ax=0; ax < usedArraySize; ax++)
+  {
+    for(ay=0; ay < usedArraySize; ay++)
+    {
+      cmaskParPtr->isleAreaTable[ax][ay] = WORK_UNCHECKED;
+    }
+  }
+
+  cmaskParPtr->pixelCount = 1;
+  cmaskParPtr->pointList = NULL;
+
+  /* mark seed pixel */
+  ax = cmaskParPtr->x + cmaskParPtr->seedOffsetX;
+  ay = cmaskParPtr->y + cmaskParPtr->seedOffsetY;
+  cmaskParPtr->isleAreaTable[ax][ay] = WORK_PART_OF_CURRENT_AREA +1;
+
+  ix = cmaskParPtr->x;
+  iy = cmaskParPtr->y;
+  
+  isBorderReached = FALSE;
+  
+  while (TRUE)
+  {
+    /* check of the neighbours */
+    if (ix > clipSelX1)
+    {
+      p_isle_check_and_mark_nb_pixel(cmaskParPtr, ix-1, iy, refOpacity);
+
+      if (ix < clipSelX2)
+      {
+        p_isle_check_and_mark_nb_pixel(cmaskParPtr, ix+1, iy, refOpacity);
+
+        if (iy > clipSelY1)
+        {
+          p_isle_check_and_mark_nb_pixel(cmaskParPtr, ix,   iy-1, refOpacity);
+
+          if (iy < clipSelY2)
+          {
+            p_isle_check_and_mark_nb_pixel(cmaskParPtr, ix,   iy+1, refOpacity);
+          }
+          else { isBorderReached = TRUE; }
+
+        }
+        else { isBorderReached = TRUE; }
+
+      }
+      else { isBorderReached = TRUE; }
+    } 
+    else { isBorderReached = TRUE; }
+    
+
+
+
+
+    if((cmaskParPtr->pixelCount > cmaskParPtr->isleAreaPixelLimit)
+    || (isBorderReached == TRUE))
+    {
+      if (cmaskParPtr->debugCoordinate)
+      {
+        printf("NOT_ISOLATED_AREA  x:%d y:%d ix:%d iy:%d pixelCount:%d isleAreaPixelLimit:%d isBorderReached:%d\n"
+          ,(int)cmaskParPtr->x
+          ,(int)cmaskParPtr->y
+          ,(int)ix
+          ,(int)iy
+          ,(int)cmaskParPtr->pixelCount
+          ,(int)cmaskParPtr->isleAreaPixelLimit
+          ,(int)isBorderReached
+          );
+      }
+      /* break because at least one of the non-isolation conditions was detected */
+      p_free_point_list(cmaskParPtr);
+      return (FALSE);
+    }
+
+    if (cmaskParPtr->pointList == NULL)
+    {
+      if (cmaskParPtr->debugCoordinate)
+      {
+        printf("ISOLATED_AREA  x:%d y:%d ix:%d iy:%d pixelCount:%d isleAreaPixelLimit:%d isBorderReached:%d\n"
+          ,(int)cmaskParPtr->x
+          ,(int)cmaskParPtr->y
+          ,(int)ix
+          ,(int)iy
+          ,(int)cmaskParPtr->pixelCount
+          ,(int)cmaskParPtr->isleAreaPixelLimit
+          ,(int)isBorderReached
+          );
+      }
+      /* area completely checked and pixelCount is bleow isleAreaPixelLimit */
+      return (TRUE);
+    }
+
+    /* continue checking neigbor pixels of same opacity in the point list */
+    ix = cmaskParPtr->pointList->x;
+    iy = cmaskParPtr->pointList->y;
+    p_remove_first_point(cmaskParPtr);
+
+
+
+  }
+
+  return (FALSE);  /* never reached code */
+
+
+}  /* end p_is_pixel_a_small_isolated_area */
+
+
+
+
+/* ---------------------------------
+ * p_remove_isolated_pixels
+ * ---------------------------------
+ * calculate new alpha value in case the current processed pixel is an
+ * isolated pixel
+ * return the (possibly changed) alpha value.
+ *
+ */
+static inline guchar
+p_remove_isolated_pixels(guchar currentAlpha, GapColorMaskParams *cmaskParPtr)
+{
+  guchar   invertedAlpha;
+  gboolean isIsolated;
+
+  isIsolated = p_is_isolated_pixel(cmaskParPtr, currentAlpha);
+  if((!isIsolated) && (cmaskParPtr->isleAreaPixelLimit > 1))
+  {
+    isIsolated = p_is_pixel_a_small_isolated_area(cmaskParPtr, currentAlpha);
+  }
+
+  /* check distance  >= isleRadius
+   * return currentAlpha unchanged
+   * in case extension of area with same value is large enough in any of the 4 directions
+   */
+  if(!isIsolated)
+  {
+    if (cmaskParPtr->debugCoordinate)
+    {
+      printf("p_remove_isolated_pixels NOT isolated   currentAlpha:%d\n"
+        ,(int)currentAlpha
+        );
+    }
+    return (currentAlpha);
+  }
+
+
+  /* the current pixel is an isolated pixel
+   * therefore invert its opacity to join with neighbor pixel environment
+   */
+  if (currentAlpha == cmaskParPtr->lowerAlpha255)
+  {
+    invertedAlpha = cmaskParPtr->upperAlpha255;
+  }
+  else
+  {
+    invertedAlpha = cmaskParPtr->lowerAlpha255;
+  }
+
+  if (cmaskParPtr->debugCoordinate)
+  {
+    printf("p_remove_isolated_pixels ISOLATED  currentAlpha:%d inverted:%d\n"
+      ,(int)currentAlpha
+      ,(int)invertedAlpha
+      );
+  }
+
+  return (invertedAlpha);
+
+}  /* end p_remove_isolated_pixels */
+
+
+
+/* ------------------------------------------
+ * p_remove_isolates_pixels_rgn_render_region
+ * ------------------------------------------
+ * remove isolated pixels (e.g. areas smaller than isleRadius)
+ * from the layermask
+ * Note that protected pixels (that are transparent in the colormask)
+ * are not affected.
+ */
+static void
+p_remove_isolates_pixels_rgn_render_region (const GimpPixelRgn *maskPR
+                    ,const GimpPixelRgn *lmskPR
+                    ,GapColorMaskParams *cmaskParPtr)
+{
+  guint    row;
+  guchar* mask = maskPR->data;   /* the colormask (RGB or RGBA) */
+  guchar* lmsk = lmskPR->data;   /* the layermask of the destination drawable */
+
+
+  for (row = 0; row < lmskPR->h; row++)
+  {
+    guint  col;
+    guint  idxMask;
+    guint  idxLmsk;
+
+    idxMask = 0;
+    idxLmsk = 0;
+    for(col = 0; col < lmskPR->w; col++)
+    {
+      gboolean isProtected;
+
+      isProtected = FALSE;
+      if (maskPR->bpp > 3)
+      {
+        if (mask[idxMask + 3] <= cmaskParPtr->triggerAlpha255)
+        {
+          isProtected = TRUE;
+        }
+      }
+
+      if (!isProtected)
+      {
+        cmaskParPtr->x = maskPR->x + col;
+        cmaskParPtr->y = maskPR->y + row;
+
+        //if(gap_debug)
+        {
+          cmaskParPtr->debugCoordinate = p_is_debug_active_on_coordinate(cmaskParPtr);
+        }
+
+        /* re-calculate opacity respecting isolated pixels */
+        lmsk[idxLmsk] = p_remove_isolated_pixels(lmsk[idxLmsk], cmaskParPtr);
+      }
+
+      idxMask += maskPR->bpp;
+      idxLmsk += lmskPR->bpp;
+    }
+
+    mask += maskPR->rowstride;
+    lmsk += lmskPR->rowstride;
+
+    if(cmaskParPtr->doProgress)
+    {
+      p_handle_progress(cmaskParPtr, maskPR->w, "p_remove_isolates_pixels_rgn_render_region");
+    }
+  }
+
+}  /* end p_remove_isolates_pixels_rgn_render_region */
+
+
+
+
+
+
+
+
+
+
+/* ============================ Stuff for average algorithm START ======= */
+/* -------------------------------------------------------------------- */
+/* ------------------ stuff for average algorithm START    ------------ */
+/* -------------------------------------------------------------------- */
+
+
+/* ------------------------------------------
+ * p_set_dynamic_threshold_by_KeyColor
+ * ------------------------------------------
+ * in case enableKeyColorThreshold is true
+ * set colorThreshold and avgColorThreshold
+ * dynamic by colrdifference of current color versus KeyColor
+ */
+static inline void
+p_set_dynamic_threshold_by_KeyColor(guchar *pixel
+                , GapColorMaskParams *cmaskParPtr)
+{
+  GimpRGB bRgb;
+  GimpHSV bHsv;
+  gdouble colordiff;
+  gdouble factor;
+
+  if(!cmaskParPtr->enableKeyColorThreshold)
+  {
+    return; /* dynamic threshold for keycolor feature is disabled */
+  }
+  
+  gimp_rgba_set_uchar (&bRgb, pixel[0], pixel[1], pixel[2], 255);
+  gimp_rgb_to_hsv(&bRgb, &bHsv);
+
+  colordiff = gap_colordiff_GimpHSV(&cmaskParPtr->keyColorHsv, &bHsv, cmaskParPtr->colorSensitivity, cmaskParPtr->debugCoordinate);
+
+  factor = CLAMP(colordiff * cmaskParPtr->keyColorSensitivity, 0.0, 1.0);
+  cmaskParPtr->colorThreshold = GAP_BASE_MIX_VALUE(factor
+                                     , cmaskParPtr->loKeyColorThreshold
+                                     , cmaskParPtr->loColorThreshold
+                                     );
+
+  cmaskParPtr->avgColorThreshold = cmaskParPtr->colorThreshold
+                                 + ((cmaskParPtr->hiColorThreshold - cmaskParPtr->colorThreshold) *  cmaskParPtr->avgFactor);
+
+
+  if(cmaskParPtr->debugCoordinate)
+  {
+    printf("colordiff at x:%d y:%d (compared to Keycolor hue:%.3f sat:%.3f val:%.3f):%.4f factor:%.4f colorThreshold:%.4f avgColorThreshold:%.4f\n"
+         ,(int)cmaskParPtr->x
+         ,(int)cmaskParPtr->y
+         ,(float)cmaskParPtr->keyColorHsv.h
+         ,(float)cmaskParPtr->keyColorHsv.s
+         ,(float)cmaskParPtr->keyColorHsv.v
+         ,(float)colordiff
+         ,(float)factor
+         ,(float)cmaskParPtr->colorThreshold
+         ,(float)cmaskParPtr->avgColorThreshold
+         );
+  }
+
+}  /* end p_set_dynamic_threshold_by_KeyColor */
+
+
+/* ---------------------------------
+ * p_colormask_avg_rgn_render_region
+ * ---------------------------------
+ * render transparency according to colormask
+ * to the layermask of the destination drawable.
+ */
+static void
+p_colormask_avg_rgn_render_region (const GimpPixelRgn *maskPR
+                    ,const GimpPixelRgn *destPR
+                    ,const GimpPixelRgn *lmskPR
+                    ,GapColorMaskParams *cmaskParPtr)
+{
+  guint    row;
+  guchar* mask = maskPR->data;   /* the colormask */
+  guchar* dest = destPR->data;   /* the destination drawable */
+  guchar* lmsk = lmskPR->data;   /* the layermask of the destination drawable */
+
+
+  for (row = 0; row < destPR->h; row++)
+  {
+    guint  col;
+    guint  idxMask;
+    guint  idxDest;
+    guint  idxLmsk;
+
+    idxMask = 0;
+    idxDest = 0;
+    idxLmsk = 0;
+    for(col = 0; col < destPR->w; col++)
+    {
+      gint32 iTab;
+      gboolean isPixelProtected;
+
+      isPixelProtected = FALSE;
+      cmaskParPtr->x = destPR->x + col;
+      cmaskParPtr->y = destPR->y + row;
+
+
+      /* ------ start debug code block -----*/
+      //if(gap_debug)
+      {
+        cmaskParPtr->debugCoordinate = p_is_debug_active_on_coordinate(cmaskParPtr);
+      }
+      /* ------ end debug code block -----*/
+
+      if (maskPR->bpp > 3)
+      {
+        /* colormask has an alpha channel */
+        if (mask[idxMask +3] <= cmaskParPtr->triggerAlpha255)
+        {
+          /* transparent mask (below or equal trigger value)
+           * switches off color masking for that pixel
+           * Note that an opaque layermask pixel
+           * preserves opacity as set via alpha channel unchanged when the layermask is applied.
+           * (no need to check color differences in that case)
+           */
+          isPixelProtected = TRUE;
+        }
+      }
+
+
+      if (isPixelProtected)
+      {
+        /* protected pixels are set full opaque in the layermask
+         * (e.g. they keep their original opacity when layermask is applied)
+         */
+        lmsk[idxLmsk] = 255;
+        if(cmaskParPtr->debugCoordinate)
+        {
+          printf("ALGO_AVG: x:%d y:%d pixel IS PROTECTED alpha:%d triggerAlpha255:%d\n"
+            ,(int)cmaskParPtr->x
+            ,(int)cmaskParPtr->y
+            ,(int)mask[idxMask +4]
+            ,(int)cmaskParPtr->triggerAlpha255
+            );
+        }
+      }
+      else
+      {
+        iTab = p_getColordiffTableIndex(cmaskParPtr, cmaskParPtr->x, cmaskParPtr->y);
+
+        if(cmaskParPtr->debugCoordinate)
+        {
+          printf("ALGO_AVG: x:%d y:%d iTab:%d  colordiff:%.5f \n"
+            ,(int)cmaskParPtr->x
+            ,(int)cmaskParPtr->y
+            ,(int)iTab
+            ,(float)cmaskParPtr->colordiffTable[iTab]
+            );
+        }
+
+
+        if (cmaskParPtr->colordiffTable[iTab] >= cmaskParPtr->hiColorThreshold)
+        {
+          /* NOMATCH pixel color differs significant from colormask pixelcolor
+           * set pixel to upper alpha (typically full opaque)
+           */
+          lmsk[idxLmsk] = cmaskParPtr->upperAlpha255;
+        }
+        else
+        {
+          p_set_dynamic_threshold_by_KeyColor(&dest[idxDest], cmaskParPtr);
+          if (cmaskParPtr->colordiffTable[iTab] < cmaskParPtr->colorThreshold)
+          {
+            /* MATCH pixel color equal or nearly equal to colormask pixelcolor */
+            lmsk[idxLmsk] = p_calc_opacity_for_matching_color(cmaskParPtr);
+          }
+          else
+          {
+            /* RANGE pixel color falls in the range of similar but not best matching colors
+             */
+            lmsk[idxLmsk] = p_calc_opacity_for_range_color(cmaskParPtr);
+          }
+        }
+      }
+
+
+
+      idxDest += destPR->bpp;
+      idxMask += maskPR->bpp;
+      idxLmsk += lmskPR->bpp;
+    }
+
+    mask += maskPR->rowstride;
+    dest += destPR->rowstride;
+    lmsk += lmskPR->rowstride;
+
+    if(cmaskParPtr->doProgress)
+    {
+      p_handle_progress(cmaskParPtr, maskPR->w, "p_colormask_avg_rgn_render_region");
+    }
+  }
+
+}  /* end p_colormask_avg_rgn_render_region */
+
+
+/* -----------------------------------------
+ * p_check_matching_pixel_isolation
+ * -----------------------------------------
+ * check if matching pixel is embedded into non matching pixels
+ * and return TRUE in this case.
+ * if there are more matching or range pixels in one horizontal or vertical row,
+ * the pixel is considered as not isolated and FALSE is returned.
+ */
+static gboolean
+p_check_matching_pixel_isolation(GapColorMaskParams *cmaskParPtr)
+{
+  gdouble sumNbColorDiff;
+  gdouble countNbPixels;
+  gdouble matchWidth;
+  gdouble matchHeight;
+
+  /* check matching width */
+  p_check_range_neighbour_sum(cmaskParPtr
+                             , -1  /* dx */
+                             , 0   /* dy */
+                             , cmaskParPtr->checkMatchRadius
+                             , &sumNbColorDiff
+                             , &countNbPixels
+                             );
+  if (countNbPixels >= cmaskParPtr->checkMatchRadius)
+  {
+    return (FALSE);
+  }
+
+  matchWidth = countNbPixels;
+
+  p_check_range_neighbour_sum(cmaskParPtr
+                             , 1   /* dx */
+                             , 0   /* dy */
+                             , cmaskParPtr->checkMatchRadius
+                             , &sumNbColorDiff
+                             , &countNbPixels
+                             );
+  matchWidth += (countNbPixels -1);
+  if (matchWidth >= cmaskParPtr->checkMatchRadius)
+  {
+    return (FALSE);
+  }
+
+  /* check matching height */
+  p_check_range_neighbour_sum(cmaskParPtr
+                             , 0    /* dx */
+                             , -1   /* dy */
+                             , cmaskParPtr->checkMatchRadius
+                             , &sumNbColorDiff
+                             , &countNbPixels
+                             );
+  if (countNbPixels >= cmaskParPtr->checkMatchRadius)
+  {
+    return (FALSE);
+  }
+
+  matchHeight = countNbPixels;
+
+  p_check_range_neighbour_sum(cmaskParPtr
+                             , 0   /* dx */
+                             , 1   /* dy */
+                             , cmaskParPtr->checkMatchRadius
+                             , &sumNbColorDiff
+                             , &countNbPixels
+                             );
+  matchHeight += (countNbPixels -1);
+  if (matchHeight >= cmaskParPtr->checkMatchRadius)
+  {
+    return (FALSE);
+  }
+
+  if (cmaskParPtr->debugCoordinate)
+  {
+    printf("matching pixel is isolated matchWidth:%d matchHeight:%d radius:%d\n"
+      , (int)matchWidth
+      , (int)matchHeight
+      , (int)cmaskParPtr->checkMatchRadius
+      );
+  }
+
+  return (TRUE);
+}  /* end p_check_matching_pixel_isolation */
+
+
+/* ---------------------------------
+ * p_calc_opacity_for_matching_color
+ * ---------------------------------
+ * in case all or nearly all pixels within radius do NOT MATCH set
+ * current pixel opaque
+ */
+static guchar
+p_calc_opacity_for_matching_color(GapColorMaskParams *cmaskParPtr)
+{
+  if (p_check_matching_pixel_isolation(cmaskParPtr) == TRUE)
+  {
+    return (cmaskParPtr->upperAlpha255);
+  }
+
+  return (cmaskParPtr->lowerAlpha255);
+}  /* end p_calc_opacity_for_matching_color */
+
+
+/* ---------------------------------
+ * p_calc_opacity_for_range_color
+ * ---------------------------------
+ * range pixels are set to matching lower alpha (transparent) in case where 
+ * the configured average algorithm check returns TRUE (considered as matching)
+ * otherwise range pixels are set to upper alpha (opaque)
+ */
+static guchar
+p_calc_opacity_for_range_color(GapColorMaskParams *cmaskParPtr)
+{
+  if(p_check_average_colordiff_incl_neighbours(cmaskParPtr) == TRUE)
+  {
+    /* average matches good */
+    return (cmaskParPtr->lowerAlpha255);
+  }
+
+
+
+  return (cmaskParPtr->upperAlpha255);
+
+}  /* end p_calc_opacity_for_range_color */
+
+
+/* ---------------------------------
+ * p_check_range_neighbour_sum
+ * ---------------------------------
+ */
+static void
+p_check_range_neighbour_sum(GapColorMaskParams *cmaskParPtr, gint dx, gint dy, gint radius, gdouble *sumNbColorDiff, gdouble *countNbPixels)
+{
+  gint iTab;
+  gint ii;
+
+  iTab = p_getColordiffTableIndex(cmaskParPtr, cmaskParPtr->x, cmaskParPtr->y);
+  *sumNbColorDiff = cmaskParPtr->colordiffTable[iTab];
+  *countNbPixels = 1.0;
+
+  for(ii=1; ii < radius; ii++)
+  {
+    gint xx;
+    gint yy;
+
+    xx = cmaskParPtr->x + (ii * dx);
+    yy = cmaskParPtr->y + (ii * dy);
+
+    if (cmaskParPtr->debugCoordinate)
+    {
+      printf("CHK ii:%d radius:%d\n"
+        , (int)ii
+        , (int)radius
+        );
+    }
+
+
+    /* clipping check */
+    if ((xx < 0) || (yy < 0) || (xx >= cmaskParPtr->width) || (yy >= cmaskParPtr->height))
+    {
+      return;
+    }
+
+    iTab = p_getColordiffTableIndex(cmaskParPtr, xx, yy);
+
+    if (cmaskParPtr->debugCoordinate)
+    {
+      printf("CHK xx:%d yy:%d countNbPixels:%.1f cmaskParPtr:%d hiColorThreshold:%.4f colordiffTab:%.4f\n"
+        , (int)xx
+        , (int)yy
+        , *countNbPixels
+        , (int)cmaskParPtr
+        , (float)cmaskParPtr->hiColorThreshold
+        , (float)cmaskParPtr->colordiffTable[iTab]
+        );
+    }
+
+    if (cmaskParPtr->colordiffTable[iTab] >= cmaskParPtr->hiColorThreshold)
+    {
+      /* stop checking because we have reached a non matching pixel */
+      return;
+    }
+
+    *sumNbColorDiff += cmaskParPtr->colordiffTable[iTab];
+    *countNbPixels  += 1.0;
+  }
+
+}  /* end p_check_range_neighbour_sum */
+
+
+
+
+/* --------------------------------------------
+ * p_countPerColordiff
+ * --------------------------------------------
+ * count and update colordiff statistics for matching, range and not matching pixels
+ * within the checked area. (typical used to analyze a small area of same or similar
+ * color within a clipping rectangle)
+ */
+static void
+p_countPerColordiff(GapColorMaskParams *cmaskParPtr, gdouble colordiff, gint mi)
+{
+  if (colordiff < cmaskParPtr->avgColorThreshold) ////## ?? loColorThreshold)
+  {
+    cmaskParPtr->sumMatch[mi] += colordiff;
+    cmaskParPtr->countMatch[mi]++;
+    return;
+  }
+
+  if (colordiff >= cmaskParPtr->hiColorThreshold)
+  {
+    cmaskParPtr->sumNoMatch[mi] += colordiff;
+    cmaskParPtr->countNoMatch[mi]++;
+    return;
+  }
+
+  cmaskParPtr->sumRange[mi] += colordiff;
+  cmaskParPtr->countRange[mi]++;
+
+}  /* end p_countPerColordiff */
+
+
+
+
+
+/* --------------------------------------------
+ * p_avg_check_and_mark_nb_pixel
+ * --------------------------------------------
+ * checks if the specified neighbor pixel at coords nx, ny
+ * has similar color as the seed pixel.
+ * If this is the case increment pixel count and summary colordiffrenece
+ * and add nx/ny to the point list (to initiate check of further neighbour pixels)
+ *
+ * The clipAreaTable is updated to mark already processed pixels
+ * (either as WORK_VISITED_BUT_NOT_PART_OF_CURRENT_AREA or as WORK_PART_OF_CURRENT_AREA)
+ */
+static void
+p_avg_check_and_mark_nb_pixel(GapColorMaskParams *cmaskParPtr, GimpPixelFetcher *pft, gint nx, gint ny, gint mi)
+{
+  guchar          pixel[4];
+  gdouble         colorDiff;
+
+  gint32 ax;
+  gint32 ay;
+  gint32 iTab;
+
+  ax = nx + cmaskParPtr->seedOffsetX;
+  ay = ny + cmaskParPtr->seedOffsetY;
+
+  if (cmaskParPtr->clipAreaTable[ax][ay][mi] != WORK_UNCHECKED)
+  {
+     /* this neighbor pixel is already processed (nothing to be done in that case) */
+     return;
+  }
+
+  gimp_pixel_fetcher_get_pixel (pft, nx, ny, &pixel[0]);
+
+  /* check if color matches with currently processed area */
+  colorDiff = p_colordiff_guchar_cmask_RGBorHSV(&cmaskParPtr->seedHsv
+                    , &cmaskParPtr->seedPixel[0]
+                    , &pixel[0]
+                    , cmaskParPtr
+                    );
+
+  if (cmaskParPtr->debugCoordinate)
+  {
+    printf("ClipArea neighbor Check ax:%d ay:%d  nx:%d ny:%d  thresholdColorArea:%.4f colorDiff:%.4f\n"
+      ,(int)ax
+      ,(int)ay
+      ,(int)nx
+      ,(int)ny
+      ,(float)cmaskParPtr->thresholdColorArea
+      ,(float)colorDiff
+      );
+  }
+  if (colorDiff > cmaskParPtr->thresholdColorArea)
+  {
+    /* currently processed pixel has other color than current seed
+     * and is not member of the same color-area
+     */
+    cmaskParPtr->clipAreaTable[ax][ay][mi] = WORK_VISITED_BUT_NOT_PART_OF_CURRENT_AREA;
+    return;
+  }
+
+  /* mark this neigbor pixel */
+  cmaskParPtr->clipAreaTable[ax][ay][mi] = WORK_PART_OF_CURRENT_AREA;
+
+  iTab = p_getColordiffTableIndex(cmaskParPtr, nx, ny);
+
+  cmaskParPtr->pixelCount++;
+  cmaskParPtr->sumColorDiff += cmaskParPtr->colordiffTable[iTab];
+
+  p_countPerColordiff(cmaskParPtr, cmaskParPtr->colordiffTable[iTab], mi); /// #### 
+
+  p_add_point(cmaskParPtr, nx, ny);
+
+  if (cmaskParPtr->debugCoordinate)
+  {
+    printf("ClipArea Check MATCH at nx:%d ny:%d sumColorDiff:%.4f count:%d avgDiff:%.4f iTab:%d\n"
+      ,(int)nx
+      ,(int)ny
+      ,(float)cmaskParPtr->sumColorDiff
+      ,(int)cmaskParPtr->pixelCount
+      ,(float)cmaskParPtr->sumColorDiff / cmaskParPtr->pixelCount
+      ,(int)iTab
+      );
+  }
+
+}  /* p_avg_check_and_mark_nb_pixel */
+
+
+/* --------------------------------------------
+ * p_find_pixel_area_of_similar_color
+ * --------------------------------------------
+ *
+ * find pixels of same or similar color to the current processed seed pixel (cmaskParPtr->x / y)
+ * and count pixels that are part of similar color area and calculate summary colordiff (dest layer versus colormask)
+ * for all the pixels in the similar color area.
+ * Note that the search is limited to a small clipping area that fits into clipAreaTable size
+ * This is done both for performance reasons, and to limit results to local area near the current pixel.
+ *
+ */
+static void
+p_find_pixel_area_of_similar_color(GapColorMaskParams *cmaskParPtr, GimpPixelFetcher *pft, gint mi)
+{
+  gint32          ix;
+  gint32          iy;
+  gint32          ax;
+  gint32          ay;
+  gint32          iTab;
+  GimpRGB         rgb;
+
+  gint            clipSelX1;
+  gint            clipSelX2;
+  gint            clipSelY1;
+  gint            clipSelY2;
+
+  cmaskParPtr->pointCount = 0;
+  cmaskParPtr->seed_ix = cmaskParPtr->x;
+  cmaskParPtr->seed_iy = cmaskParPtr->y;
+
+  cmaskParPtr->seedOffsetX = CLIP_AREA_MAX_RADIUS - cmaskParPtr->x;
+  cmaskParPtr->seedOffsetY =  CLIP_AREA_MAX_RADIUS - cmaskParPtr->y;
+
+
+  /* clipping area boundaries to limit area checks
+   * for a small rectangular area +- CLIP_AREA_MAX_RADIUS around current pixel
+   */
+  clipSelX1 = MAX(cmaskParPtr->sel_x1, (cmaskParPtr->x - CLIP_AREA_MAX_RADIUS));
+  clipSelY1 = MAX(cmaskParPtr->sel_y1, (cmaskParPtr->y - CLIP_AREA_MAX_RADIUS));
+
+  clipSelX2 = MIN(cmaskParPtr->sel_x2, (cmaskParPtr->x + CLIP_AREA_MAX_RADIUS));
+  clipSelY2 = MIN(cmaskParPtr->sel_y2, (cmaskParPtr->y + CLIP_AREA_MAX_RADIUS));
+
+
+  /* reset area context, clear clipAreaTable */
+  for(ax=0; ax < CLIP_AREA_MAX_SIZE; ax++)
+  {
+    for(ay=0; ay < CLIP_AREA_MAX_SIZE; ay++)
+    {
+      cmaskParPtr->clipAreaTable[ax][ay][mi] = WORK_UNCHECKED;
+    }
+  }
+
+  /* fetch the seed pixel color */
+  gimp_pixel_fetcher_get_pixel (pft, cmaskParPtr->x, cmaskParPtr->y, &cmaskParPtr->seedPixel[0]);
+  gimp_rgba_set_uchar (&rgb
+                      , cmaskParPtr->seedPixel[0]
+                      , cmaskParPtr->seedPixel[1]
+                      , cmaskParPtr->seedPixel[2]
+                      , 255
+                      );
+  gimp_rgb_to_hsv(&rgb, &cmaskParPtr->seedHsv);
+
+
+  iTab = p_getColordiffTableIndex(cmaskParPtr, cmaskParPtr->x, cmaskParPtr->y);
+  cmaskParPtr->sumColorDiff = cmaskParPtr->colordiffTable[iTab];
+  cmaskParPtr->pixelCount = 1;
+  
+  p_countPerColordiff(cmaskParPtr, cmaskParPtr->colordiffTable[iTab], mi); /// #### 
+  
+  
+  
+  
+  
+  cmaskParPtr->pointList = NULL;
+
+  /* mark seed pixel */
+  ax = cmaskParPtr->x + cmaskParPtr->seedOffsetX;
+  ay = cmaskParPtr->y + cmaskParPtr->seedOffsetY;
+  cmaskParPtr->clipAreaTable[ax][ay][mi] = WORK_PART_OF_CURRENT_AREA +1;
+
+  if (cmaskParPtr->debugCoordinate)
+  {
+    printf("Seed at x:%d y:%d (ax:%d ay:%d seedOffsetX:%d Y:%d) sel_y1:%d clipSelX1:%d X2:%d Y1:%d Y2:%d  initial sumColorDiff:%.4f iTab:%d RGB: %03d %03d %03d \n"
+      ,(int)cmaskParPtr->x
+      ,(int)cmaskParPtr->y
+      ,(int)ax
+      ,(int)ay
+      ,(int)cmaskParPtr->seedOffsetX
+      ,(int)cmaskParPtr->seedOffsetY
+      ,(int)cmaskParPtr->sel_y1
+      ,(int)clipSelX1
+      ,(int)clipSelX2
+      ,(int)clipSelY1
+      ,(int)clipSelY2
+      ,(float)cmaskParPtr->sumColorDiff
+      ,(int)iTab
+      ,(int)cmaskParPtr->seedPixel[0]
+      ,(int)cmaskParPtr->seedPixel[1]
+      ,(int)cmaskParPtr->seedPixel[2]
+      );
+  }
+
+
+  ix = cmaskParPtr->x;
+  iy = cmaskParPtr->y;
+
+  while (TRUE)
+  {
+    /* check of the neighbours */
+    if (ix > clipSelX1)
+    {
+      p_avg_check_and_mark_nb_pixel(cmaskParPtr, pft, ix-1, iy, mi);
+    }
+
+    if (ix < clipSelX2)
+    {
+      p_avg_check_and_mark_nb_pixel(cmaskParPtr, pft, ix+1, iy, mi);
+    }
+
+    if (iy > clipSelY1)
+    {
+      p_avg_check_and_mark_nb_pixel(cmaskParPtr, pft, ix,   iy-1, mi);
+    }
+
+    if (iy < clipSelY2)
+    {
+      p_avg_check_and_mark_nb_pixel(cmaskParPtr, pft, ix,   iy+1, mi);
+    }
+
+
+    if (cmaskParPtr->pointList == NULL)
+    {
+      /* area completely done (all connected neigbour pixels of the seed are processed */
+      break;
+    }
+
+    /* continue checking neigbor pixels of similar color in the point list */
+    ix = cmaskParPtr->pointList->x;
+    iy = cmaskParPtr->pointList->y;
+    p_remove_first_point(cmaskParPtr);
+
+
+
+  }
+
+
+
+}  /* end p_find_pixel_area_of_similar_color */
+
+
+
+/* ------------------------------------
+ * p_calculate_clip_area_average_values
+ * ------------------------------------
+ *
+ */
+static void
+p_calculate_clip_area_average_values(GapColorMaskParams *cmaskParPtr)
+{
+  cmaskParPtr->sumMatch[MI_IMAGE] = 0.0;
+  cmaskParPtr->sumRange[MI_IMAGE] = 0.0;
+  cmaskParPtr->sumNoMatch[MI_IMAGE] = 0.0;
+  cmaskParPtr->countMatch[MI_IMAGE] = 0.0;
+  cmaskParPtr->countRange[MI_IMAGE] = 0.0;
+  cmaskParPtr->countNoMatch[MI_IMAGE] = 0.0;
+
+  
+  p_find_pixel_area_of_similar_color(cmaskParPtr, cmaskParPtr->pftDest, MI_IMAGE);
+
+  cmaskParPtr->imgAreaSize = cmaskParPtr->pixelCount;
+  cmaskParPtr->avgColordiffImgArea = cmaskParPtr->sumColorDiff / ((gdouble)cmaskParPtr->pixelCount);
+
+
+  /* debug code to print the clipAreaTable and results */
+  if (cmaskParPtr->debugCoordinate)
+  {
+    gint mi;
+    gint ax;
+    gint ay;
+    
+    for(mi=0; mi < 1; mi++)
+    {
+      if(mi==0)
+      {
+        printf("Image clipAreaTable\n");
+      }
+      if(mi==1)
+      {
+        printf("Mask clipAreaTable\n");
+      }
+      for(ay=0; ay < CLIP_AREA_MAX_SIZE; ay++)
+      {
+        printf(" clipAreaTable_%d[%02d]: ", (int)mi, (int)ay);
+        for(ax=0; ax < CLIP_AREA_MAX_SIZE; ax++)
+        {
+          printf(" %03d", (int)cmaskParPtr->clipAreaTable[ax][ay][mi]);
+        }
+        printf("\n");
+      }
+    }
+
+    printf("ClipArea RESULTS x:%d y:%d : avgColordiffImgArea:%.4f imgAreaSize:%d \n"
+           "   avgColorThreshold:%.4f \n"
+           "   countMatch:%.0f countRange:%.0f countNoMatch:%.0f\n"
+      ,(int)cmaskParPtr->x
+      ,(int)cmaskParPtr->y
+      ,(float)cmaskParPtr->avgColordiffImgArea
+      ,(int)cmaskParPtr->imgAreaSize
+      ,(float)cmaskParPtr->avgColorThreshold
+      ,(float)cmaskParPtr->countMatch[MI_IMAGE]
+      ,(float)cmaskParPtr->countRange[MI_IMAGE]
+      ,(float)cmaskParPtr->countNoMatch[MI_IMAGE]
+      );
+
+    if(cmaskParPtr->countMatch[MI_IMAGE] > 0.0)
+    {
+      printf("   avgMatch: %.5f\n"
+         , (float)(cmaskParPtr->sumMatch[MI_IMAGE] / cmaskParPtr->countMatch[MI_IMAGE])
+         );
+    }
+    if(cmaskParPtr->countRange[MI_IMAGE] > 0.0)
+    {
+      printf("   avgRange: %.5f\n"
+         , (float)(cmaskParPtr->sumRange[MI_IMAGE] / cmaskParPtr->countRange[MI_IMAGE])
+         );
+    }
+    if(cmaskParPtr->countNoMatch[MI_IMAGE] > 0.0)
+    {
+      printf("   avgNoMatch: %.5f\n"
+         , (float)(cmaskParPtr->sumNoMatch[MI_IMAGE] / cmaskParPtr->countNoMatch[MI_IMAGE])
+         );
+    }
+
+  }
+
+}  /* end p_calculate_clip_area_average_values */
+
+
+
+
+/* -----------------------------------------
+ * p_check_average_colordiff_incl_neighbours
+ * -----------------------------------------
+ * decide if color is matching by checking neighbor pixels
+ * using the configured type of average algorithm
+ *
+ * GAP_COLORMASK_ALGO_AVG_CHANGE_1
+ *   check for significant brightness and colorchanges
+ *   in 4 directions, starting at current pixel up to a distance of 
+ *   the configured significantRadius.
+ *   
+ *   (MATCHTYPE_MATCHING)
+ *     in case the check finds 2 good matching pixels (image layer versus colormask) 
+ *     and there was no significant brightness or colorchange
+ *     (checked image pixel versus previous image pixel in processed direction)
+ *     then consider the starting pixel as good matching.
+ *
+ *   (MATCHTYPE_NOT_MATCHING)
+ *     in case the check finds 2 NOT-matching pixels (image layer versus colormask)
+ *     and there was no significant brightness or colorchange 
+ *     (checked image pixel versus previous image pixel in processed direction)
+ *     then consider the starting pixel as not matching.
+ *
+ *   (MATCHTYPE_UNDEFINED)
+ *     this undefined result occurs in case the check in the current direction 
+ *     o) has stopped at the configured significantRadius or image borders
+ *     o) has found a significant brightness or colorchange
+ *     continue with GAP_COLORMASK_ALGO_AVG_SMART in case
+ *     MATCHTYPE_UNDEFINED is the result in all 4 directions.
+ *     
+ *
+ * GAP_COLORMASK_ALGO_AVG_CHANGE_2
+ *   similar to GAP_COLORMASK_ALGO_AVG_CHANGE_1
+ *   but use results of all 4 directions
+ *   (while variant 1 stops check at the first direction
+ *   that delivers a result != MATCHTYPE_UNDEFINED)
+ *
+ * GAP_COLORMASK_ALGO_AVG_AREA:
+ *   check if the pixel is part of an area of similar colors
+ *   within the the processed image layer.
+ *   in case the pixel is part of such an area then use the
+ *   most significant type of colordiff (matching, NotMatching)
+ *   within the area.
+ *   In case colordiff type 'Range' is most significant
+ *   (or number of pixels with colordiff type matching and NotMatching are equal)
+ *   use the average colordiff of the area(s) or to decide.
+ *
+ * GAP_COLORMASK_ALGO_AVG_SMART:
+ *   check average colordiff of neighbor pixels
+ *   in upper, lower, left and right direction.
+ *   in case the average colordiff is below the threshold
+ *   return TRUE (e.g. the pixel is considered as matching good enough
+ *   with the colormask)
+ *   Otherwise return FALSE.
+ *   This algorithm is also used as fallback strategy
+ *   in case the other algortihms could not deliver a valid result.
+ */
+static gboolean
+p_check_average_colordiff_incl_neighbours(GapColorMaskParams *cmaskParPtr)
+{
+  gdouble sumNbColorDiff;
+  gdouble countNbPixels;
+  gdouble avgColordiff[4];
+
+  gint32 iTab;
+
+  if ((cmaskParPtr->algorithm == GAP_COLORMASK_ALGO_AVG_CHANGE_1)
+  || (cmaskParPtr->algorithm == GAP_COLORMASK_ALGO_AVG_CHANGE_2))
+  {
+    static gint dxTab[4] = { 1, -1,  0,  0 };
+    static gint dyTab[4] = { 0,  0,  1, -1 };
+    gint   direction;
+    gint   matchCount;
+    gint   notMatchCount;
+
+
+    if (cmaskParPtr->algorithm == GAP_COLORMASK_ALGO_AVG_CHANGE_1) 
+    {
+      /* simple varaiant based on the first defined result */
+      for (direction=0; direction < 4; direction++)
+      {
+        guchar rc;
+     
+        rc = p_check_significant_changes_in_one_direction(cmaskParPtr
+                   , dxTab[direction], dyTab[direction]);
+
+        if(rc == MATCHTYPE_MATCHING)
+        {
+          return (TRUE);  /* good matching */
+        }
+      
+        if(rc == MATCHTYPE_NOT_MATCHING)
+        {
+          return (FALSE);  /* NOT matching */
+        }
+      }
+    }
+
+    /* full varaiant based on all defined results */
+
+    matchCount = 0;
+    notMatchCount = 0;
+
+
+    for (direction=0; direction < 4; direction++)
+    {
+      guchar rc;
+     
+      rc = p_check_significant_changes_in_one_direction(cmaskParPtr
+             , dxTab[direction], dyTab[direction]);
+
+      if(rc == MATCHTYPE_MATCHING)
+      {
+        matchCount++;
+      }
+      else if(rc == MATCHTYPE_NOT_MATCHING)
+      {
+        notMatchCount++;
+      }
+    }
+
+    if (cmaskParPtr->debugCoordinate)
+    {
+          printf("SIG2 result: x:%d y:%d matchCount:%d  notMatchCount:%d\n"
+            , (int)cmaskParPtr->x
+            , (int)cmaskParPtr->y
+            , matchCount
+            , notMatchCount
+            );
+    }
+
+    if(matchCount > notMatchCount)
+    {
+      return (TRUE);  /* good matching */
+    }
+    else if (notMatchCount > 0)
+    {
+      return (FALSE);  /* NOT matching */
+    }
+
+    /* pixel is not part of an area of similar colors (or area too small)
+     * continue with GAP_COLORMASK_ALGO_AVG_SMART algorithm
+     */
+    if (cmaskParPtr->debugCoordinate)
+    {
+      printf("continue with standard AVG_SMART algorithm: ");
+    }
+  }
+
+
+
+  //iTab = p_getColordiffTableIndex(cmaskParPtr, cmaskParPtr->x, cmaskParPtr->y);
+
+  //if ((cmaskParPtr->algorithm == GAP_COLORMASK_ALGO_AVG_AREA)
+  //&& (cmaskParPtr->colordiffTable[iTab] >= cmaskParPtr->avgColorThreshold))
+
+  if (cmaskParPtr->algorithm == GAP_COLORMASK_ALGO_AVG_AREA)
+  {
+    /* handle GAP_COLORMASK_ALGO_AVG_AREA algorithm */
+
+    p_calculate_clip_area_average_values(cmaskParPtr);
+
+
+    if (cmaskParPtr->imgAreaSize >= cmaskParPtr->areaMinimumPixels)
+    {
+      if((cmaskParPtr->countMatch[MI_IMAGE] > cmaskParPtr->countRange[MI_IMAGE])
+      && (cmaskParPtr->countMatch[MI_IMAGE] > cmaskParPtr->countNoMatch[MI_IMAGE]))
+      {
+        /* most pixels of the similar colored area are good matching */
+        if (cmaskParPtr->debugCoordinate)
+        {
+          printf("MATCHING is most significant\n");
+        }
+        return (TRUE); /* good matching */
+      }
+
+      if((cmaskParPtr->countNoMatch[MI_IMAGE] > cmaskParPtr->countRange[MI_IMAGE])
+      && (cmaskParPtr->countNoMatch[MI_IMAGE] > cmaskParPtr->countMatch[MI_IMAGE]))
+      {
+        /* most pixels of the similar colored area are NOT matching */
+        if (cmaskParPtr->debugCoordinate)
+        {
+          printf("NOT_MATCHING is most significant\n");
+        }
+        return (FALSE); /* not matching */
+      }
+      
+      /* most pixels of the similar colored area within range or 
+       * there are exactly the same number of good and non matching pixels within this area.
+       * therfore next check the overall average colordiff of the area.
+       */
+      if (cmaskParPtr->avgColordiffImgArea < cmaskParPtr->avgColorThreshold)
+      {
+        if (cmaskParPtr->debugCoordinate)
+        {
+          printf("MATCHING due to overall avgColordiffImgArea:%.5f avgColorThreshold:%.5f\n"
+              , (float)cmaskParPtr->avgColordiffImgArea
+              , (float)cmaskParPtr->avgColorThreshold
+              );
+        }
+        return (TRUE); /* good matching */
+      }
+      if (cmaskParPtr->avgColordiffImgArea >= cmaskParPtr->hiColorThreshold)
+      {
+        if (cmaskParPtr->debugCoordinate)
+        {
+          printf("NOT_MATCHING due to overall avgColordiffImgArea:%.5f avgColorThreshold:%.5f\n"
+              , (float)cmaskParPtr->avgColordiffImgArea
+              , (float)cmaskParPtr->avgColorThreshold
+              );
+        }
+        return (FALSE); /* not matching */
+      }
+      
+      if (cmaskParPtr->countRange[MI_IMAGE] > 1)
+      {
+        gdouble avgRangeColordiff;
+        gdouble avgMixColordiff;
+
+        /* check average colordiff limited to just those similar color area pixels
+         * that match within range
+         */
+        avgRangeColordiff = cmaskParPtr->sumRange[MI_IMAGE] / cmaskParPtr->countRange[MI_IMAGE];
+        
+        if (avgRangeColordiff < cmaskParPtr->avgColorThreshold)
+        {
+          if (cmaskParPtr->debugCoordinate)
+          {
+            printf("MATCHING due to range avgRangeColordiff:%.5f avgColorThreshold:%.5f\n"
+                , (float)avgRangeColordiff
+                , (float)cmaskParPtr->avgColorThreshold
+                );
+          }
+          return (TRUE); /* good matching */
+        }
+        if (avgRangeColordiff >= cmaskParPtr->hiColorThreshold)
+        {
+          if (cmaskParPtr->debugCoordinate)
+          {
+            printf("NOT_MATCHING due to range avgRangeColordiff:%.5f avgColorThreshold:%.5f\n"
+                , (float)avgRangeColordiff
+                , (float)cmaskParPtr->avgColorThreshold
+                );
+          }
+          return (FALSE); /* not matching */
+        }
+        
+        if(cmaskParPtr->countMatch[MI_IMAGE] >= cmaskParPtr->countNoMatch[MI_IMAGE])
+        {
+          avgMixColordiff = (cmaskParPtr->sumRange[MI_IMAGE] + cmaskParPtr->sumMatch[MI_IMAGE])
+                          / (cmaskParPtr->countRange[MI_IMAGE] + cmaskParPtr->countMatch[MI_IMAGE]);
+          if (avgMixColordiff < cmaskParPtr->avgColorThreshold)
+          {
+            if (cmaskParPtr->debugCoordinate)
+            {
+              printf("MATCHING due to mix range+match avgMixColordiff:%.5f avgColorThreshold:%.5f\n"
+                  , (float)avgMixColordiff
+                  , (float)cmaskParPtr->avgColorThreshold
+                  );
+            }
+            return (TRUE); /* good matching */
+          }
+        }
+        
+        if(cmaskParPtr->countNoMatch[MI_IMAGE] >= cmaskParPtr->countMatch[MI_IMAGE])
+        {
+          avgMixColordiff = (cmaskParPtr->sumRange[MI_IMAGE] + cmaskParPtr->sumNoMatch[MI_IMAGE])
+                          / (cmaskParPtr->countRange[MI_IMAGE] + cmaskParPtr->countNoMatch[MI_IMAGE]);
+          if (avgMixColordiff >= cmaskParPtr->hiColorThreshold)
+          {
+            if (cmaskParPtr->debugCoordinate)
+            {
+              printf("NOT MATCHING due to mix range+NoMatch avgMixColordiff:%.5f avgColorThreshold:%.5f\n"
+                  , (float)avgMixColordiff
+                  , (float)cmaskParPtr->avgColorThreshold
+                  );
+            }
+            return (FALSE); /* not matching */
+          }
+        }
+        
+        
+        
+        
+        
+      }
+     
+      if(cmaskParPtr->countNoMatch[MI_IMAGE] > cmaskParPtr->countMatch[MI_IMAGE])
+      {
+        if (cmaskParPtr->debugCoordinate)
+        {
+          printf("NOT MATCHING more NoMatch pixels than Match pixels\n");
+        }
+        return (FALSE); /* not matching */
+      }
+      if(cmaskParPtr->countMatch[MI_IMAGE] > cmaskParPtr->countNoMatch[MI_IMAGE])
+      {
+        if (cmaskParPtr->debugCoordinate)
+        {
+          printf("MATCHING more Match pixels than NoMatch pixels\n");
+        }
+        return (TRUE); /* matching */
+      }
+
+    }
+
+
+    /* pixel is not part of an area of similar colors (or area too small)
+     * continue with GAP_COLORMASK_ALGO_AVG_SMART algorithm
+     */
+    if (cmaskParPtr->debugCoordinate)
+    {
+      printf("AREA algorithm could not decide, continue with AVG_SMART algorithm: ");
+    }
+
+  }
+
+  /* following code handles the algorithm
+   * GAP_COLORMASK_ALGO_AVG_SMART and
+   * GAP_COLORMASK_ALGO_AVG_AREA (part2 that is ident to the smart algorithm )
+   */
+
+  p_check_range_neighbour_sum(cmaskParPtr
+                             , 0   /* dx */
+                             , 1   /* dy */
+                             , cmaskParPtr->checkRadius
+                             , &sumNbColorDiff, &countNbPixels);
+  avgColordiff[0] = sumNbColorDiff / countNbPixels;
+  if ((avgColordiff[0] < cmaskParPtr->avgColorThreshold) && (countNbPixels > 2))
+  {
+    if (cmaskParPtr->debugCoordinate)
+    {
+      printf("x:%d y:%d countNbPixels:%.1f avgColordiff:[%.4f] is below Thres:%.4f\n"
+          ,(int)cmaskParPtr->x
+          ,(int)cmaskParPtr->y
+          ,(float)countNbPixels
+          ,(float)avgColordiff[0]
+          ,(float)cmaskParPtr->avgColorThreshold
+          );
+    }
+    /* average matches good */
+    return (TRUE);
+  }
+
+  p_check_range_neighbour_sum(cmaskParPtr
+                             , 0   /* dx */
+                             ,-1   /* dy */
+                             , cmaskParPtr->checkRadius
+                             , &sumNbColorDiff, &countNbPixels);
+  avgColordiff[1] = sumNbColorDiff / countNbPixels;
+  if ((avgColordiff[1] < cmaskParPtr->avgColorThreshold) && (countNbPixels > 2))
+  {
+    if (cmaskParPtr->debugCoordinate)
+    {
+      printf("x:%d y:%d countNbPixels:%.1f avgColordiff:[%.4f] is below Thres:%.4f\n"
+          ,(int)cmaskParPtr->x
+          ,(int)cmaskParPtr->y
+          ,(float)countNbPixels
+          ,(float)avgColordiff[1]
+          ,(float)cmaskParPtr->avgColorThreshold
+          );
+    }
+    /* average matches good */
+    return (TRUE);
+  }
+
+
+
+  /* horizontal */
+
+  p_check_range_neighbour_sum(cmaskParPtr
+                             , 1   /* dx */
+                             , 0   /* dy */
+                             , cmaskParPtr->checkRadius
+                             , &sumNbColorDiff, &countNbPixels);
+  avgColordiff[2] = sumNbColorDiff / countNbPixels;
+  if ((avgColordiff[2] < cmaskParPtr->avgColorThreshold) && (countNbPixels > 2))
+  {
+    if (cmaskParPtr->debugCoordinate)
+    {
+      printf("x:%d y:%d countNbPixels:%.1f avgColordiff:[%.4f] is below Thres:%.4f\n"
+          ,(int)cmaskParPtr->x
+          ,(int)cmaskParPtr->y
+          ,(float)countNbPixels
+          ,(float)avgColordiff[2]
+          ,(float)cmaskParPtr->avgColorThreshold
+          );
+    }
+    /* average matches good */
+    return (TRUE);
+  }
+
+
+  p_check_range_neighbour_sum(cmaskParPtr
+                             ,-1   /* dx */
+                             , 0   /* dy */
+                             , cmaskParPtr->checkRadius
+                             , &sumNbColorDiff, &countNbPixels);
+  avgColordiff[3] = sumNbColorDiff / countNbPixels;
+  if ((avgColordiff[3] < cmaskParPtr->avgColorThreshold) && (countNbPixels > 2))
+  {
+    if (cmaskParPtr->debugCoordinate)
+    {
+      printf("x:%d y:%d countNbPixels:%.1f avgColordiff:[%.4f] is below Thres:%.4f\n"
+          ,(int)cmaskParPtr->x
+          ,(int)cmaskParPtr->y
+          ,(float)countNbPixels
+          ,(float)avgColordiff[3]
+          ,(float)cmaskParPtr->avgColorThreshold
+          );
+    }
+    /* average matches good */
+    return (TRUE);
+  }
+
+
+
+  if (cmaskParPtr->debugCoordinate)
+  {
+    printf("x:%d y:%d avgColordiff:[%.4f %.4f %.4f %.4f] at least one is above Thres:%.4f\n"
+        ,(int)cmaskParPtr->x
+        ,(int)cmaskParPtr->y
+        ,(float)avgColordiff[0]
+        ,(float)avgColordiff[1]
+        ,(float)avgColordiff[2]
+        ,(float)avgColordiff[3]
+        ,(float)cmaskParPtr->avgColorThreshold
+        );
+  }
+
+  return (FALSE);
+}  /* end p_check_average_colordiff_incl_neighbours */
+
+
+
+
+
+
+   
+/* ---------------------------------
+ * p_check_significant_diff
+ * ---------------------------------
+ * returns TRUE in case the specified colors differ significant
+ *              (according to curent parameter settings)
+ */
+static gboolean
+p_check_significant_diff(guchar *aPixelPtr
+                   , guchar *bPixelPtr
+		   , gint xx, gint yy
+                   , GapColorMaskParams *cmaskParPtr)
+{
+  gdouble nbColorDiff;
+  gdouble nbBrightnessDiff;
+  gdouble lum1;
+  gdouble lum2;
+                    
+  lum1 = (gdouble)(aPixelPtr[0] + aPixelPtr[1] + aPixelPtr[2]) / 765.0;  // (255 * 3);
+  lum2 = (gdouble)(bPixelPtr[0] + bPixelPtr[1] + bPixelPtr[2]) / 765.0;  // (255 * 3);
+  
+  nbBrightnessDiff = fabs(lum1 - lum2);
+  if(nbBrightnessDiff > cmaskParPtr->significantBrightnessDiff)
+  {
+    if (cmaskParPtr->debugCoordinate)
+    {
+        printf("SIG Brightnessdiff x:%d y:%d  xx:%d yy:%d lum1:%.3f lum2:%.3f nbBrightnessDiff:%.4f  significantBrightnessDiff:%.4f\n"
+          , (int)cmaskParPtr->x
+          , (int)cmaskParPtr->y
+	  , (int)xx
+	  , (int)yy
+	  , (float)lum1
+	  , (float)lum2
+          , (float)nbBrightnessDiff
+          , (float)cmaskParPtr->significantBrightnessDiff
+          );
+    }
+  
+    return (TRUE);
+  }
+  
+  nbColorDiff = p_colordiff_guchar_cmask(aPixelPtr
+                             , bPixelPtr
+                             , cmaskParPtr
+                             );
+
+  if(nbColorDiff > cmaskParPtr->significantColordiff)
+  {
+    if (cmaskParPtr->debugCoordinate)
+    {
+        printf("SIG Colordiff x:%d y:%d nbColorDiff:%.4f  significantColordiff:%.4f\n"
+          , (int)cmaskParPtr->x
+          , (int)cmaskParPtr->y
+          , (float)nbColorDiff
+          , (float)cmaskParPtr->significantColordiff
+          );
+    }
+
+    return (TRUE);
+  }
+
+  if (cmaskParPtr->debugCoordinate)
+  {
+    printf("SIG similar color x:%d y:%d  xx:%d yy:%d lum1:%.3f lum2:%.3f nbBrightnessDiff:%.4f  significantBrightnessDiff:%.4f"
+           "  nbColorDiff:%.4f  significantColordiff:%.4f\n"
+          , (int)cmaskParPtr->x
+          , (int)cmaskParPtr->y
+	  , (int)xx
+	  , (int)yy
+	  , (float)lum1
+	  , (float)lum2
+          , (float)nbBrightnessDiff
+          , (float)cmaskParPtr->significantBrightnessDiff
+          , (float)nbColorDiff
+          , (float)cmaskParPtr->significantColordiff
+          );
+  }
+  
+
+  return (FALSE);
+}  /* end p_check_significant_diff */
+
+
+
+/* --------------------------------------------
+ * p_check_significant_changes_in_one_direction
+ * --------------------------------------------
+ * returns TRUE in case the specified colors differ significant
+ *              (according to curent parameter settings)
+ */
+static guchar
+p_check_significant_changes_in_one_direction(GapColorMaskParams *cmaskParPtr, gint dx, gint dy)
+{
+  guchar  currPixel[4];
+  guchar  prevPixel[4];
+  gint    distance;
+  gdouble prevColordiff;
+  
+  
+  gimp_pixel_fetcher_get_pixel (cmaskParPtr->pftDest, cmaskParPtr->x, cmaskParPtr->y, &prevPixel[0]);
+  prevColordiff = 0.0;
+
+  for(distance = 1; distance < cmaskParPtr->significantRadius; distance++)
+  {
+    gboolean significantDiff;
+    gdouble  currColordiff;
+    gint     xx;
+    gint     yy;
+    gint32   iTab;
+
+    xx = cmaskParPtr->x + (distance * dx);
+    yy = cmaskParPtr->y + (distance * dy);
+
+    
+    /* clipping check test position outside image boudaries */
+    if ((xx < 0) || (yy < 0) || (xx >= cmaskParPtr->width) || (yy >= cmaskParPtr->height))
+    {
+      return (MATCHTYPE_UNDEFINED);
+    }
+
+    gimp_pixel_fetcher_get_pixel (cmaskParPtr->pftDest, xx, yy, &currPixel[0]);
+    
+    significantDiff = p_check_significant_diff(&prevPixel[0]
+                             , &currPixel[0]
+			     , xx
+			     , yy
+                             , cmaskParPtr
+                             );
+    if(significantDiff)
+    {
+      return (MATCHTYPE_UNDEFINED);
+    }
+
+
+    iTab = p_getColordiffTableIndex(cmaskParPtr, xx, yy);
+    currColordiff = cmaskParPtr->colordiffTable[iTab];
+    
+    if (distance > 1)
+    {
+      if ((currColordiff > cmaskParPtr->hiColorThreshold)
+      &&  (prevColordiff > cmaskParPtr->hiColorThreshold))
+      {
+        /* found "NN" pattern (2 Non matching pixels in series)  */
+        return (MATCHTYPE_NOT_MATCHING);
+      }
+
+   
+   
+//       if ((currColordiff < cmaskParPtr->colorThreshold)
+//       &&  (prevColordiff < cmaskParPtr->colorThreshold))
+//       {
+//         /* found "mm" pattern (2 matching pixels in series)  */
+//         return (MATCHTYPE_MATCHING);
+//       }
+    }
+    
+    prevPixel[0] = currPixel[0];
+    prevPixel[1] = currPixel[1];
+    prevPixel[2] = currPixel[2];
+    
+    prevColordiff = currColordiff;
+  }
+
+  return (MATCHTYPE_UNDEFINED);
+  
+}  /* end p_check_significant_changes_in_one_direction */
+
+
+
+
+
+
+
+
+
+
+/* ---------------------------------
+ * p_check_avg_add_one_pixel             DEPRECATED
+ * ---------------------------------
+ *
+ */
+static inline void
+p_check_avg_add_one_pixel(GapColorMaskParams *cmaskParPtr, gint xx, gint yy
+   , gdouble *sumNbColorDiff, gdouble *countNbPixels)
+{
+  gint iTab;
+
+  /* clipping check */
+  if ((xx < 0) || (yy < 0) || (xx >= cmaskParPtr->width) || (yy >= cmaskParPtr->height))
+  {
+    return;
+  }
+
+  iTab = p_getColordiffTableIndex(cmaskParPtr, xx, yy);
+  *sumNbColorDiff += cmaskParPtr->colordiffTable[iTab];
+  *countNbPixels  += 1.0;
+
+  if (cmaskParPtr->debugCoordinate)
+  {
+    printf("AVCHK xx:%d yy:%d countNbPixels:%.1f colordiffTab:%.4f\n"
+      , (int)xx
+      , (int)yy
+      , *countNbPixels
+      , (float)cmaskParPtr->colordiffTable[iTab]
+      );
+  }
+
+}  /* end p_check_avg_add_one_pixel */
+
+
+
+
+/* ---------------------------------
+ * p_init_colordiffTable
+ * ---------------------------------
+ * initialize colordiffTable at full image size
+ * with calculate color differences for all pixels.
+ * (this table will not work for huge images on machines
+ * with limited memory resouces but allows fast
+ * processing by avoiding multiple colordiff processing
+ * and pixel fetch steps and shall do it for
+ * typical video or HD video image sizes.
+ */
+static void
+p_init_colordiffTable (const GimpPixelRgn *maskPR
+                    ,const GimpPixelRgn *destPR
+                    ,GapColorMaskParams *cmaskParPtr)
+{
+  guint    row;
+  guchar* mask = maskPR->data;   /* the colormask */
+  guchar* dest = destPR->data;   /* the destination drawable */
+
+
+  for (row = 0; row < destPR->h; row++)
+  {
+    guint  col;
+    guint  idxMask;
+    guint  idxDest;
+
+    idxMask = 0;
+    idxDest = 0;
+    for(col = 0; col < destPR->w; col++)
+    {
+      gint32 iTab;
+
+      cmaskParPtr->x = destPR->x + col;
+      cmaskParPtr->y = destPR->y + row;
+      iTab = p_getColordiffTableIndex(cmaskParPtr, cmaskParPtr->x, cmaskParPtr->y);
+
+
+      {
+        cmaskParPtr->colordiffTable[iTab] =
+            p_colordiff_guchar_cmask( &mask[idxMask]
+                             , &dest[idxDest]
+                             , cmaskParPtr
+                             );
+
+      }
+
+
+
+      idxDest += destPR->bpp;
+      idxMask += maskPR->bpp;
+    }
+
+    mask += maskPR->rowstride;
+    dest += destPR->rowstride;
+
+    if(cmaskParPtr->doProgress)
+    {
+      p_handle_progress(cmaskParPtr, maskPR->w, "p_init_colordiffTable");
+    }
+  }
+
+}  /* end p_init_colordiffTable */
+
+
+/* ---------------------------------
+ * p_getColordiffTableIndex
+ * ---------------------------------
+ */
+static inline gint32
+p_getColordiffTableIndex(GapColorMaskParams *cmaskParPtr, gint x, gint y)
+{
+  gint32 iTab;
+
+  iTab = (y * cmaskParPtr->width) + x;
+  return (iTab);
+}
+
+
+/* --------------------------------------------
+ * p_difflayer_rgn_render
+ * --------------------------------------------
+ * render difflayer to visualize
+ * the colordiffTable in shades of green (MATCHING pixels) yellow (tolrated RANGE) and red. (bad matching RANGE pixels)
+ * or transparent (NON-MATCHING pixels)
+ * The difflayer is not required for productive processing,
+ * this layer is for debug and tuning purpose while development.
+ */
+static void
+p_difflayer_rgn_render (const GimpPixelRgn *diffPR
+                    , const GimpPixelRgn *destPR, GapColorMaskParams *cmaskParPtr)
+{
+  guint    row;
+  guchar* diff = diffPR->data;
+  guchar* dest = destPR->data;
+
+
+  for (row = 0; row < diffPR->h; row++)
+  {
+    guint  col;
+    guint  idxDiff;
+    guint  idxDest;
+
+    idxDiff = 0;
+    idxDest = 0;
+    for(col = 0; col < diffPR->w; col++)
+    {
+      gint32 iTab;
+      guchar red, green, blue, alpha;
+
+      cmaskParPtr->x = diffPR->x + col;
+      cmaskParPtr->y = diffPR->y + row;
+      iTab = p_getColordiffTableIndex(cmaskParPtr, cmaskParPtr->x, cmaskParPtr->y);
+
+      red   = 0;
+      green = 0;
+      blue  = 0;
+      alpha = 255;
+
+      if (cmaskParPtr->colordiffTable[iTab] >= cmaskParPtr->hiColorThreshold)
+      {
+        /* NOMATCH pixel color differs significant from colormask pixelcolor
+         * render those pixels fully transparent.
+         */
+        alpha = 0;
+      }
+      else
+      {
+        gdouble intensity;
+
+        intensity = 1.0;
+        
+        if(cmaskParPtr->connectByCorner)   //// DEBUG
+        {
+          p_set_dynamic_threshold_by_KeyColor(&dest[idxDest], cmaskParPtr);
+        }
+
+        if (cmaskParPtr->colordiffTable[iTab] < cmaskParPtr->colorThreshold)
+        {
+          /* MATCH pixel color equal or nearly equal to colormask pixelcolor
+           * render those pixels in shades of green
+           */
+          if (cmaskParPtr->colorThreshold != 0)
+          {
+            intensity = cmaskParPtr->colordiffTable[iTab] / cmaskParPtr->colorThreshold;
+          }
+          intensity *= 127.0;
+          green = 127 + intensity;
+        }
+        else if (cmaskParPtr->colordiffTable[iTab] <= cmaskParPtr->avgColorThreshold)
+        {
+          gdouble loThres, hiThres;
+
+          loThres = MIN(cmaskParPtr->colorThreshold , cmaskParPtr->avgColorThreshold);
+          hiThres = MAX(cmaskParPtr->colorThreshold , cmaskParPtr->avgColorThreshold);
+
+          /* RANGE pixel color falls in the range of matching colors (below the tolerated avgColorThreshold level)
+           * render those pixels in shades of blue
+           */
+          if (hiThres - loThres)
+          {
+            intensity = (cmaskParPtr->colordiffTable[iTab] - loThres)
+                      / (hiThres - loThres);
+          }
+          intensity *= 127.0;
+          blue = 127 + intensity;
+        }
+        else
+        {
+          gdouble loThres, hiThres;
+
+          loThres = MIN(cmaskParPtr->avgColorThreshold , cmaskParPtr->hiColorThreshold);
+          hiThres = MAX(cmaskParPtr->avgColorThreshold , cmaskParPtr->hiColorThreshold);
+
+          /* RANGE pixel color falls in the range of similar but not good matching colors
+           * render those pixels in shades of red
+           */
+          if (hiThres - loThres)
+          {
+            intensity = (cmaskParPtr->colordiffTable[iTab] - loThres)
+                      / (hiThres - loThres);
+          }
+          intensity *= 127.0;
+          red = 127 + intensity;
+        }
+      }
+
+
+      diff[idxDiff]    = red;
+      diff[idxDiff +1] = green;
+      diff[idxDiff +2] = blue;
+      diff[idxDiff +3] = alpha;
+
+
+      idxDiff += diffPR->bpp;
+      idxDest += destPR->bpp;
+    }
+
+    diff += diffPR->rowstride;
+    dest += destPR->rowstride;
+
+    if(cmaskParPtr->doProgress)
+    {
+      p_handle_progress(cmaskParPtr, diffPR->w, "p_difflayer_rgn_render");
+    }
+  }
+
+}  /* end p_difflayer_rgn_render */
+
+
+/* --------------------------------------------
+ * p_create_colordiffTable_Layer
+ * --------------------------------------------
+ * create difflayer to visualize the colordiffTable
+ * The difflayer is not required for productive processing,
+ * this layer is for debug and tuning purpose while development.
+ */
+static void
+p_create_colordiffTable_Layer(GapColorMaskParams *cmaskParPtr, GimpDrawable *dst_drawable)
+{
+  GimpDrawable *diffLayerDrawable;
+  gpointer  pr;
+  GimpPixelRgn diffPR;
+  GimpPixelRgn destPR;
+  gint32 diffLayerId;
+
+  diffLayerId = gap_layer_find_by_name(cmaskParPtr->dst_image_id, COLORMASK_DIFF_LAYER_NAME);
+  if (diffLayerId < 0)
+  {
+    diffLayerId = p_create_empty_layer(cmaskParPtr->dst_image_id
+                       , cmaskParPtr->width
+                       , cmaskParPtr->height
+                       , COLORMASK_DIFF_LAYER_NAME);
+  }
+
+  if (diffLayerId < 0)
+  {
+    return;
+  }
+
+
+  diffLayerDrawable = gimp_drawable_get(diffLayerId);
+
+  gimp_pixel_rgn_init (&diffPR, diffLayerDrawable, 0, 0
+                      , diffLayerDrawable->width, diffLayerDrawable->height
+                      , TRUE     /* dirty */
+                      , FALSE     /* shadow */
+                       );
+
+  gimp_pixel_rgn_init (&destPR, dst_drawable, 0, 0
+                      , dst_drawable->width, dst_drawable->height
+                      , TRUE      /* dirty */
+                      , FALSE     /* shadow */
+                       );
+
+
+
+  /* init the the worklayer (copy content and setup alpha channel) */
+  for (pr = gimp_pixel_rgns_register (2, &diffPR, &destPR);
+       pr != NULL;
+       pr = gimp_pixel_rgns_process (pr))
+  {
+    p_difflayer_rgn_render (&diffPR, &destPR, cmaskParPtr);
+  }
+
+  gimp_drawable_detach(diffLayerDrawable);
+
+
+}  /* end p_create_colordiffTable_Layer */
+
+
+
+
+/* -------------------------------------------------------------------- */
+/* ------------------ stuff for average algorithm END      ------------ */
+/* -------------------------------------------------------------------- */
+
+/* ============================ Stuff for average algorithm END ======= */
+
+
+
+
+/* ============================ Stuff for create or replace individual per frame colormask layer ======= */
+
+/* --------------------------------------------
+ * p_mix_newmask_rgn_render
+ * --------------------------------------------
+ * create individual (per frame) colormask in the work drawable.
+ * as mix of the previous colormask the initial reference colormask and the original layer
+ */
+static void
+p_mix_newmask_rgn_render(const GimpPixelRgn *pmskPR
+                 ,const GimpPixelRgn *maskPR
+                 ,const GimpPixelRgn *origPR
+                 ,const GimpPixelRgn *workPR
+                 ,GapColorMaskParams *cmaskParPtr)
+{
+  guint    row;
+  guchar*  pmsk = pmskPR->data;   /* the colormask from previous frame  */
+  guchar*  mask = maskPR->data;   /* the initial reference colormask */
+  guchar*  orig = origPR->data;   /* the current frame original */
+  guchar*  work = workPR->data;   /* the destination drawable (e.g. new colormask) */
+
+
+  for (row = 0; row < workPR->h; row++)
+  {
+    guint  col;
+    guint  idxPmsk;
+    guint  idxMask;
+    guint  idxOrig;
+    guint  idxWork;
+
+    idxPmsk = 0;
+    idxMask = 0;
+    idxOrig = 0;
+    idxWork = 0;
+    for(col = 0; col < workPR->w; col++)
+    {
+      gboolean useNewIndividualPixel;
+      gboolean usePrevMaskPixel;
+      gboolean isProtected;
+
+      //if(gap_debug)
+      {
+        cmaskParPtr->x = workPR->x + col;
+        cmaskParPtr->y = workPR->y + row;
+        cmaskParPtr->debugCoordinate = p_is_debug_active_on_coordinate(cmaskParPtr);
+      }
+
+
+      useNewIndividualPixel = FALSE;
+      usePrevMaskPixel = FALSE;
+      isProtected = FALSE;
+      if (maskPR->bpp > 3)
+      {
+        if (mask[idxMask + 3] <= cmaskParPtr->triggerAlpha255)
+        {
+          isProtected = TRUE;
+        }
+      }
+
+
+      if (!isProtected)
+      {
+        gdouble colorDiff;
+
+        colorDiff = p_colordiff_guchar_cmask(&mask[idxMask]
+                 , &orig[idxOrig]
+                 , cmaskParPtr);
+
+        if(colorDiff < cmaskParPtr->hiColorThreshold)
+        {
+          colorDiff = p_colordiff_guchar_cmask(&pmsk[idxPmsk]
+                 , &orig[idxOrig]
+                 , cmaskParPtr);
+          if(colorDiff < cmaskParPtr->colorThreshold)
+          {
+            useNewIndividualPixel = TRUE;
+          }
+          else if(colorDiff < cmaskParPtr->edgeColorThreshold)
+          {
+            usePrevMaskPixel = TRUE;
+          }
+        }
+        else if(colorDiff < cmaskParPtr->colorThreshold)
+        {
+            useNewIndividualPixel = TRUE;
+        }
+      }
+
+      if(useNewIndividualPixel)
+      {
+        work[idxWork]    = orig[idxOrig];
+        work[idxWork +1] = orig[idxOrig +1];
+        work[idxWork +2] = orig[idxOrig +2];
+      }
+      else
+      {
+        if (usePrevMaskPixel)
+        {
+          /* for this pixel copy from previous colormask rgb values */
+          work[idxWork]    = pmsk[idxPmsk];
+          work[idxWork +1] = pmsk[idxPmsk +1];
+          work[idxWork +2] = pmsk[idxPmsk +2];
+        }
+        else
+        {
+          /* for this pixel copy from initial colormask rgb values */
+          work[idxWork]    = mask[idxMask];
+          work[idxWork +1] = mask[idxMask +1];
+          work[idxWork +2] = mask[idxMask +2];
+        }
+      }
+
+      if (maskPR->bpp > 3)
+      {
+        work[idxWork +3] = mask[idxMask + 3];  /* copy alpha channel from original colormask */
+      }
+      else
+      {
+        work[idxWork +3] = 255;
+      }
+
+
+      idxPmsk += pmskPR->bpp;
+      idxMask += maskPR->bpp;
+      idxOrig += origPR->bpp;
+      idxWork += workPR->bpp;
+    }
+
+    pmsk += pmskPR->rowstride;
+    mask += maskPR->rowstride;
+    orig += origPR->rowstride;
+    work += workPR->rowstride;
+
+    if(cmaskParPtr->doProgress)
+    {
+      p_handle_progress(cmaskParPtr, maskPR->w, "p_mix_newmask_rgn_render");
+    }
+  }
+
+}  /* end p_mix_newmask_rgn_render */
+
+
+/* --------------------------------------------
+ * p_fetch_colormask_in_previous_frame
+ * --------------------------------------------
+ */
+static gint32
+p_fetch_colormask_in_previous_frame(gint32 orig_layer_id)
+{
+  gint32 image_id;
+  gint32 prevCmskId;
+  GapAnimInfo *ainfo_ptr;
+
+  prevCmskId = -1;
+  image_id = gimp_drawable_get_image(orig_layer_id);
+  ainfo_ptr = gap_lib_alloc_ainfo(image_id, GIMP_RUN_NONINTERACTIVE);
+  if(ainfo_ptr != NULL)
+  {
+    char *prevFrameName;
+
+    ainfo_ptr->frame_nr = ainfo_ptr->curr_frame_nr - 1;
+
+    prevFrameName = gap_lib_alloc_fname(ainfo_ptr->basename,
+                                      ainfo_ptr->frame_nr,
+                                      ainfo_ptr->extension);
+    if(0 == gap_lib_file_exists(prevFrameName))
+    {
+      //if(gap_debug)
+      {
+        printf("previous frame %s does not exist (or is empty)\n"
+              , prevFrameName
+              );
+      }
+      prevCmskId = -1;
+    }
+    else
+    {
+       gint32 image_id;
+
+       image_id = gap_lib_load_image(prevFrameName);
+       if (image_id >= 0)
+       {
+         prevCmskId = gap_layer_find_by_name(image_id, COLORMASK_LAYER_NAME);
+       }
+
+    }
+
+    gap_lib_free_ainfo(&ainfo_ptr);
+  }
+
+  return(prevCmskId);
+
+}  /* end p_fetch_colormask_in_previous_frame */
+
+
+/* -----------------------------------------
+ * p_remove_layer_by_name
+ * -----------------------------------------
+ * remove layer from the specified image_id that has
+ * the specified name, AND has a layer_id that is different from orig_layer_id
+ */
+static void
+p_remove_layer_by_name(gint32 image_id, gint32 orig_layer_id, const char *name)
+{
+  gint32 layer_id;
+
+  layer_id = gap_layer_find_by_name(image_id, name);
+  if(layer_id < 0)
+  {
+    return;
+  }
+  if (layer_id != orig_layer_id)
+  {
+    gimp_image_remove_layer(image_id, layer_id);
+  }
+}  /* end p_remove_layer_by_name */
+
+/* ============================ Stuff for create or replace individual perf frame colormask layer ======= */
+
+
+/* ------------------------------------
+ * p_gdouble_to_uchar255
+ * ------------------------------------
+ * convert from range 0.0 .. 1.0   to 0 .. 255
+ */
+static guchar
+p_gdouble_to_uchar255(gdouble value)
+{
+  gdouble   value255;
+  guchar    ret;
+
+  value255 = value * 255.0;
+  ret = CLAMP((guchar)value255, 0, 255);
+
+  if(gap_debug)
+  {
+    printf("value:%f val255:%f  ret:%d\n"
+      , (float)value
+      , (float)value255
+      , (int)ret
+      );
+  }
+  return (ret);
+}
+
+/* -----------------------------------------
+ * p_copy_cmaskvals_to_colorMaskParams
+ * -----------------------------------------
+ */
+static void
+p_copy_cmaskvals_to_colorMaskParams(GapColormaskValues *cmaskvals, GapColorMaskParams *cmaskParPtr, gint32 dst_layer_id, gboolean doProgress)
+{
+  //if(gap_debug)
+  {
+    printf("algorithm: %d\n", (int)cmaskvals->algorithm);
+    printf("colormask_id: %d\n", (int)cmaskvals->colormask_id);
+    printf("loColorThreshold: %f\n", (float)cmaskvals->loColorThreshold);
+    printf("hiColorThreshold: %f\n", (float)cmaskvals->hiColorThreshold);
+    printf("colorSensitivity: %f\n", (float)cmaskvals->colorSensitivity);
+    printf("lowerOpacity: %f\n", (float)cmaskvals->lowerOpacity);
+    printf("upperOpacity: %f\n", (float)cmaskvals->upperOpacity);
+    printf("triggerAlpha: %f\n", (float)cmaskvals->triggerAlpha);
+    printf("isleRadius: %f\n", (float)cmaskvals->isleRadius);
+    printf("isleAreaPixelLimit: %f\n", (float)cmaskvals->isleAreaPixelLimit);
+    printf("featherRadius: %f\n", (float)cmaskvals->featherRadius);
+
+    printf("edgeColorThreshold: %f\n", (float)cmaskvals->edgeColorThreshold);
+    printf("thresholdColorArea: %f\n", (float)cmaskvals->thresholdColorArea);
+    printf("pixelDiagonal: %f\n", (float)cmaskvals->pixelDiagonal);
+    printf("pixelAreaLimit: %f\n", (float)cmaskvals->pixelAreaLimit);
+    printf("connectByCorner: %d\n", (int)cmaskvals->connectByCorner);
+    printf("keepLayerMask: %d\n", (int)cmaskvals->keepLayerMask);
+    printf("keepWorklayer: %d\n", (int)cmaskvals->keepWorklayer);
+    printf("enableKeyColorThreshold: %d\n", (int)cmaskvals->enableKeyColorThreshold);
+    printf("loKeyColorThreshold: %f\n", (float)cmaskvals->loKeyColorThreshold);
+    printf("keyColorSensitivity: %f\n", (float)cmaskvals->keyColorSensitivity);
+
+    printf("jaggedRadius: %f\n", (float)cmaskvals->pixelDiagonal);   /// TODO
+    printf("jeggedLength: %f\n", (float)cmaskvals->pixelAreaLimit);   /// TODO
+
+    printf("avgFactor: %f\n", (float)cmaskvals->edgeColorThreshold);      /// TODO
+    printf("checkRadius: %f\n", (float)cmaskvals->pixelDiagonal);         /// TODO
+    printf("checkMatchRadius: %f\n", (float)cmaskvals->pixelDiagonal);    /// TODO
+    printf("areaMinimumPixels: %f\n", (float)cmaskvals->pixelAreaLimit);  /// TODO
+  }
+
+
+  cmaskParPtr->algorithm = cmaskvals->algorithm;
+  cmaskParPtr->dst_layer_id = dst_layer_id;
+  cmaskParPtr->dst_image_id = gimp_drawable_get_image(dst_layer_id);
+  cmaskParPtr->cmask_drawable_id = cmaskvals->colormask_id;
+  cmaskParPtr->colorThreshold = cmaskvals->loColorThreshold;    /* use loColorThreshold as default for  colorThreshold */
+  cmaskParPtr->loColorThreshold = cmaskvals->loColorThreshold;
+  cmaskParPtr->hiColorThreshold = cmaskvals->hiColorThreshold;
+  cmaskParPtr->colorSensitivity = CLAMP(cmaskvals->colorSensitivity, 1.0, 2.0);
+  
+  cmaskParPtr->enableKeyColorThreshold = cmaskvals->enableKeyColorThreshold;
+  cmaskParPtr->loKeyColorThreshold = cmaskvals->loKeyColorThreshold;
+  cmaskParPtr->keyColorSensitivity = cmaskvals->keyColorSensitivity;
+  gimp_rgb_to_hsv(&cmaskvals->keycolor, &cmaskParPtr->keyColorHsv);
+  
+
+  cmaskParPtr->lowerOpacity = cmaskvals->lowerOpacity;
+  cmaskParPtr->upperOpacity = cmaskvals->upperOpacity;
+  cmaskParPtr->triggerAlpha = cmaskvals->triggerAlpha;
+  cmaskParPtr->isleRadius = cmaskvals->isleRadius;
+  cmaskParPtr->isleAreaPixelLimit = cmaskvals->isleAreaPixelLimit;
+  cmaskParPtr->featherRadius = cmaskvals->featherRadius;
+
+  cmaskParPtr->edgeColorThreshold = cmaskParPtr->edgeColorThreshold;
+  cmaskParPtr->thresholdColorArea = cmaskvals->thresholdColorArea;
+  cmaskParPtr->pixelDiagonal = cmaskvals->pixelDiagonal;
+  cmaskParPtr->pixelAreaLimit = cmaskvals->pixelAreaLimit;
+  cmaskParPtr->connectByCorner = cmaskvals->connectByCorner;
+  cmaskParPtr->keepLayerMask = cmaskvals->keepLayerMask;
+  cmaskParPtr->keepWorklayer = cmaskvals->keepWorklayer;
+  cmaskParPtr->doProgress = doProgress;
+
+
+  /* precalculate values for per-pixel calculations (performance) */
+  cmaskParPtr->dstLayerMaskId = -1;
+  cmaskParPtr->triggerAlpha255 = p_gdouble_to_uchar255(cmaskParPtr->triggerAlpha);
+  cmaskParPtr->lowerAlpha255 = p_gdouble_to_uchar255(cmaskParPtr->lowerOpacity);
+  cmaskParPtr->upperAlpha255 = p_gdouble_to_uchar255(cmaskParPtr->upperOpacity);
+  cmaskParPtr->transparencyLevel255 = MIN(cmaskParPtr->lowerAlpha255, cmaskParPtr->upperAlpha255);
+  cmaskParPtr->opacityFactor = MAX(cmaskParPtr->lowerOpacity, cmaskParPtr->upperOpacity)
+                             - MIN(cmaskParPtr->lowerOpacity, cmaskParPtr->upperOpacity);
+  cmaskParPtr->shadeRange = fabs(cmaskParPtr->hiColorThreshold - cmaskParPtr->loColorThreshold);
+
+  cmaskParPtr->jaggedRadius = (gint)cmaskvals->pixelDiagonal;   /// TODO
+  cmaskParPtr->jeggedLength = (gint)cmaskvals->pixelAreaLimit;   /// TODO
+
+
+
+  cmaskParPtr->checkRadius = (gint)cmaskvals->pixelDiagonal;         /// TODO
+  cmaskParPtr->checkMatchRadius = (gint)cmaskvals->pixelDiagonal;    /// TODO
+  cmaskParPtr->areaMinimumPixels = (gint)cmaskvals->pixelAreaLimit;  /// TODO
+  cmaskParPtr->avgFactor = cmaskvals->edgeColorThreshold;            // TODO
+  cmaskParPtr->avgColorThreshold = cmaskParPtr->colorThreshold
+                                 + ((cmaskParPtr->hiColorThreshold - cmaskParPtr->colorThreshold) *  cmaskParPtr->avgFactor);
+
+
+  cmaskParPtr->significantRadius = (gint)cmaskvals->significantRadius;         /// TODO
+  cmaskParPtr->significantColordiff = cmaskvals->significantColordiff;         /// TODO
+  cmaskParPtr->significantBrightnessDiff = cmaskvals->significantBrightnessDiff;         /// TODO
+
+
+  //if(gap_debug)
+  {
+
+    printf("significantRadius: %d\n", (int)cmaskParPtr->significantRadius);
+    printf("significantColordiff: %f\n", (float)cmaskParPtr->significantColordiff);
+    printf("significantBrightnessDiff: %f\n", (float)cmaskParPtr->significantBrightnessDiff);
+    printf("checkRadius: %f\n", (float)cmaskParPtr->checkRadius);
+    printf("checkMatchRadius: %f\n", (float)cmaskParPtr->checkMatchRadius);
+    printf("areaMinimumPixels: %f\n", (float)cmaskParPtr->areaMinimumPixels);
+    printf("avgFactor: %f\n", (float)cmaskParPtr->avgFactor);
+    printf("avgColorThreshold: %f\n", (float)cmaskParPtr->avgColorThreshold);
+  }
+
+  cmaskParPtr->debugCoordinate = FALSE;
+  p_get_guides(cmaskParPtr);
+
+}  /* end p_copy_cmaskvals_to_colorMaskParams */
+
+
+
+/* -----------------------------------------
+ * gap_colormask_apply_to_layer_of_same_size
+ * -----------------------------------------
+ *
+ * handles transparency by applying a color image (RGB) or (RGBA) as mask,
+ * The pixels that are transparent (below or equal triggerAlpha) in
+ * the alpha channel of the colormask are considered as PROTECTED
+ * e.g are not affected by this filter.
+ * (those pixels keep their original alpha channel)
+ *
+ * The resulting transparency is generated as new layermask
+ * that can be optionally applied at end of processing (keepLayerMask FALSE)
+ * Note that an already existing layermask will be applied before processing
+ * and is replaced by the new generated one, even if keepLayerMask FALSE is used.
+ *
+ * current implementation provides 2 algorithms
+ * a) by checking indiviual pixels
+ * b) by checking areas of similar colors (triggered by thresholdColorArea > 0.0
+ *
+ * preconditions:
+ *  destination layer (dst_layer_id) and colormask (cmask_drawable_id)
+ *  must be of same size.
+ *  destination layer must be of type RGBA.
+ *  colormask drawable must be of type RGB or RGBA.
+ *
+ * returns -1 on failure or dst_layer_id on success
+ *
+ */
+gint32
+gap_colormask_apply_to_layer_of_same_size (gint32 dst_layer_id
+                        , GapColormaskValues     *cmaskvals
+                        , gboolean                doProgress
+                        )
+{
+  GapColorMaskParams colorMaskParams;
+  GapColorMaskParams *cmaskParPtr;
+  GimpPixelRgn colormaskPR, dstPR, lmskPR;
+  GimpDrawable *colormask_drawable;
+  GimpDrawable *dst_drawable;
+  GimpDrawable *dstLayerMask_drawable;
+  gint32        oldLayerMaskId;
+  gpointer  pr;
+  GimpRGB   background;
+  gint32    retLayerId;
+
+
+  retLayerId = dst_layer_id;
+
+  cmaskParPtr = &colorMaskParams;
+  p_copy_cmaskvals_to_colorMaskParams(cmaskvals, cmaskParPtr, dst_layer_id, doProgress);
+
+  dst_drawable = gimp_drawable_get (dst_layer_id);
+  colormask_drawable = gimp_drawable_get (cmaskParPtr->cmask_drawable_id);
+  //if(gap_debug)
+  {
+    printf("gap_colormask_apply_to_layer_of_same_size checking preconditions \n"
+           "  DST: width:%d height: %d  BPP:%d \n"
+           "  MSK: width:%d height: %d  BPP:%d \n"
+           "  triggerAlpha:%d lowerAlpha:%d upperAlpha:%d algorithm:%d loColorThreshold:%f\n"
+           "  cmaskParPtr:%d  hiColorThreshold: %f\n"
+          , (int)dst_drawable->width
+          , (int)dst_drawable->height
+          , (int)dst_drawable->bpp
+          , (int)colormask_drawable->width
+          , (int)colormask_drawable->height
+          , (int)colormask_drawable->bpp
+          , (int)cmaskParPtr->triggerAlpha255
+          , (int)cmaskParPtr->lowerAlpha255
+          , (int)cmaskParPtr->upperAlpha255
+          , (int)cmaskParPtr->algorithm
+          , (float)cmaskParPtr->loColorThreshold
+          , (int)cmaskParPtr
+          , (float)cmaskParPtr->hiColorThreshold
+          );
+  }
+
+  if((colormask_drawable->width  != dst_drawable->width)
+  || (colormask_drawable->height != dst_drawable->height)
+  || (dst_drawable->bpp    != 4)
+  || (colormask_drawable->bpp    < 3))
+  {
+    if(gap_debug)
+    {
+      printf("gap_colormask_apply_to_layer_of_same_size preconditions not ok, operation NOT performed\n");
+    }
+    gimp_drawable_detach(colormask_drawable);
+    gimp_drawable_detach(dst_drawable);
+    return (-1);
+  }
+
+  gimp_image_undo_group_start (cmaskParPtr->dst_image_id);
+
+
+
+  cmaskParPtr->width = dst_drawable->width;
+  cmaskParPtr->height = dst_drawable->height;
+  /* setup selected area
+   * current implementation operates always on full layer size
+   */
+  cmaskParPtr->sel_x1 = 0;
+  cmaskParPtr->sel_y1 = 0;
+  cmaskParPtr->sel_x2 = cmaskParPtr->width -1;
+  cmaskParPtr->sel_y2 = cmaskParPtr->height -1;
+
+  /* progress init */
+  {
+    gint pixelSize;
+
+    pixelSize = cmaskParPtr->height * cmaskParPtr->width;
+
+    cmaskParPtr->pixelsDoneAllPasses = 0.0;
+    cmaskParPtr->pixelsToProcessInAllPasses = pixelSize;
+
+    if((cmaskParPtr->algorithm == GAP_COLORMASK_ALGO_AVG_SMART)
+    || (cmaskParPtr->algorithm == GAP_COLORMASK_ALGO_AVG_AREA))
+    {
+      /* those algorithms have an extra pass to create the colordiffTable */
+      cmaskParPtr->pixelsToProcessInAllPasses += pixelSize;
+    }
+
+
+
+    if(cmaskParPtr->featherRadius >= 1.0)
+    {
+      cmaskParPtr->pixelsToProcessInAllPasses += pixelSize;
+    }
+
+    if((cmaskParPtr->isleRadius >= 1) && (cmaskParPtr->isleAreaPixelLimit >= 1))
+    {
+      cmaskParPtr->pixelsToProcessInAllPasses += pixelSize;
+    }
+  }
+
+  /* remove old layermask if there is one */
+  oldLayerMaskId = gimp_layer_get_mask(cmaskParPtr->dst_layer_id);
+  if (oldLayerMaskId >= 0)
+  {
+     gimp_layer_remove_mask (cmaskParPtr->dst_layer_id, GIMP_MASK_DISCARD);
+  }
+
+
+  /* create a new layermask (white is full opaque */
+  cmaskParPtr->dstLayerMaskId = gimp_layer_create_mask(cmaskParPtr->dst_layer_id, GIMP_ADD_WHITE_MASK);
+  gimp_layer_add_mask(cmaskParPtr->dst_layer_id, cmaskParPtr->dstLayerMaskId);
+  dstLayerMask_drawable = gimp_drawable_get(cmaskParPtr->dstLayerMaskId);
+
+
+  /* create and init pixelfetchers */
+  cmaskParPtr->pftDest = gimp_pixel_fetcher_new (dst_drawable, FALSE /* shadow */);
+  cmaskParPtr->pftMask = gimp_pixel_fetcher_new (colormask_drawable, FALSE /* shadow */);
+  cmaskParPtr->pftDestLayerMask = gimp_pixel_fetcher_new (dstLayerMask_drawable, FALSE /* shadow */);
+
+  gimp_context_get_background (&background);
+  gimp_pixel_fetcher_set_bg_color (cmaskParPtr->pftDest, &background);
+  gimp_pixel_fetcher_set_bg_color (cmaskParPtr->pftMask, &background);
+  gimp_pixel_fetcher_set_bg_color (cmaskParPtr->pftDestLayerMask, &background);
+  gimp_pixel_fetcher_set_edge_mode (cmaskParPtr->pftDest, GIMP_PIXEL_FETCHER_EDGE_BLACK);
+  gimp_pixel_fetcher_set_edge_mode (cmaskParPtr->pftMask, GIMP_PIXEL_FETCHER_EDGE_BLACK);
+  gimp_pixel_fetcher_set_edge_mode (cmaskParPtr->pftDestLayerMask, GIMP_PIXEL_FETCHER_EDGE_BLACK);
+
+
+  gimp_pixel_rgn_init (&colormaskPR, colormask_drawable, 0, 0
+                      , colormask_drawable->width, colormask_drawable->height
+                      , FALSE     /* dirty */
+                      , FALSE     /* shadow */
+                       );
+  gimp_pixel_rgn_init (&dstPR, dst_drawable, 0, 0
+                      , dst_drawable->width, dst_drawable->height
+                      , TRUE      /* dirty */
+                      , FALSE     /* shadow */
+                       );
+  gimp_pixel_rgn_init (&lmskPR, dstLayerMask_drawable, 0, 0
+                      , dstLayerMask_drawable->width, dstLayerMask_drawable->height
+                      , TRUE     /* dirty */
+                      , FALSE     /* shadow */
+                       );
+
+
+
+  if((cmaskParPtr->algorithm == GAP_COLORMASK_ALGO_AVG_SMART)
+  || (cmaskParPtr->algorithm == GAP_COLORMASK_ALGO_AVG_CHANGE_1)
+  || (cmaskParPtr->algorithm == GAP_COLORMASK_ALGO_AVG_CHANGE_2)
+  || (cmaskParPtr->algorithm == GAP_COLORMASK_ALGO_AVG_AREA))
+  {
+    /* code for the average colordiff based algorithms
+     * (all of them are using a colordiffTable) */
+
+    //if(gap_debug)
+    {
+      printf("BEFORE alloc colordiffTable\n");
+    }
+
+    cmaskParPtr->colordiffTable = g_malloc(cmaskParPtr->width * cmaskParPtr->height * sizeof(gdouble));
+
+
+    /* 1.st pass to create a ColordiffTable and initialize with color differences foreach pixel
+     */
+    for (pr = gimp_pixel_rgns_register (2, &colormaskPR, &dstPR);
+         pr != NULL;
+         pr = gimp_pixel_rgns_process (pr))
+    {
+      p_init_colordiffTable (&colormaskPR, &dstPR, cmaskParPtr);
+    }
+
+
+    gimp_pixel_rgn_init (&colormaskPR, colormask_drawable, 0, 0
+                      , colormask_drawable->width, colormask_drawable->height
+                      , FALSE     /* dirty */
+                      , FALSE     /* shadow */
+                       );
+    gimp_pixel_rgn_init (&dstPR, dst_drawable, 0, 0
+                    , dst_drawable->width, dst_drawable->height
+                    , TRUE      /* dirty */
+                    , FALSE     /* shadow */
+                     );
+
+
+    /* 2.nd pass to render layermask (by average colordiff within radius)
+     */
+    for (pr = gimp_pixel_rgns_register (3, &colormaskPR, &dstPR, &lmskPR);
+         pr != NULL;
+         pr = gimp_pixel_rgns_process (pr))
+    {
+      p_colormask_avg_rgn_render_region (&colormaskPR, &dstPR, &lmskPR, cmaskParPtr);
+    }
+
+
+    if(cmaskParPtr->keepWorklayer)
+    {
+      p_create_colordiffTable_Layer(cmaskParPtr, dst_drawable);
+    }
+
+    g_free(cmaskParPtr->colordiffTable);
+
+
+  }
+  else if(cmaskParPtr->algorithm == GAP_COLORMASK_ALGO_EDGE)
+  {
+    /* edge algorithm basic pass to render layermask (by compare colormask and destination layer colors)
+     */
+    for (pr = gimp_pixel_rgns_register (3, &colormaskPR, &dstPR, &lmskPR);
+         pr != NULL;
+         pr = gimp_pixel_rgns_process (pr))
+    {
+      p_colormask_edge_rgn_render_region (&colormaskPR, &dstPR, &lmskPR, cmaskParPtr);
+    }
+  }
+  else  /* handles GAP_COLORMASK_ALGO_SIMPLE */
+  {
+    /* simple basic pass to render layermask (by compare colormask and destination layer colors)
+     */
+    for (pr = gimp_pixel_rgns_register (3, &colormaskPR, &dstPR, &lmskPR);
+         pr != NULL;
+         pr = gimp_pixel_rgns_process (pr))
+    {
+      p_colormask_rgn_render_region (&colormaskPR, &dstPR, &lmskPR, cmaskParPtr);
+    }
+  }
+
+  /* optional pass to remove isolated pixels in the layermask */
+  if((cmaskParPtr->isleRadius >= 1) && (cmaskParPtr->isleAreaPixelLimit >= 1))
+  {
+    gimp_pixel_rgn_init (&colormaskPR, colormask_drawable, 0, 0
+                      , colormask_drawable->width, colormask_drawable->height
+                      , FALSE     /* dirty */
+                      , FALSE     /* shadow */
+                       );
+    gimp_pixel_rgn_init (&lmskPR, dstLayerMask_drawable, 0, 0
+                      , dstLayerMask_drawable->width, dstLayerMask_drawable->height
+                      , TRUE      /* dirty */
+                      , FALSE     /* shadow */
+                       );
+
+
+    for (pr = gimp_pixel_rgns_register (2, &colormaskPR, &lmskPR);
+         pr != NULL;
+         pr = gimp_pixel_rgns_process (pr))
+    {
+        p_remove_isolates_pixels_rgn_render_region (&colormaskPR, &lmskPR, cmaskParPtr);
+    }
+  }
+
+
+
+  /* final optional pass to render smooth edges in the layermask */
+  if(cmaskParPtr->featherRadius >= 1.0)
+  {
+    gimp_pixel_rgn_init (&colormaskPR, colormask_drawable, 0, 0
+                      , colormask_drawable->width, colormask_drawable->height
+                      , FALSE     /* dirty */
+                      , FALSE     /* shadow */
+                       );
+    gimp_pixel_rgn_init (&lmskPR, dstLayerMask_drawable, 0, 0
+                      , dstLayerMask_drawable->width, dstLayerMask_drawable->height
+                      , TRUE      /* dirty */
+                      , FALSE     /* shadow */
+                       );
+
+
+    for (pr = gimp_pixel_rgns_register (2, &colormaskPR, &lmskPR);
+         pr != NULL;
+         pr = gimp_pixel_rgns_process (pr))
+    {
+        p_smooth_edges_rgn_render_region (&colormaskPR, &lmskPR, cmaskParPtr);
+    }
+  }
+
+
+  gimp_pixel_fetcher_destroy (cmaskParPtr->pftDestLayerMask);
+  gimp_pixel_fetcher_destroy (cmaskParPtr->pftDest);
+  gimp_pixel_fetcher_destroy (cmaskParPtr->pftMask);
+
+  gimp_drawable_detach(dst_drawable);
+  gimp_drawable_detach(colormask_drawable);
+  gimp_drawable_detach(dstLayerMask_drawable);
+
+
+  if(cmaskParPtr->dstLayerMaskId >= 0)
+  {
+    if(!cmaskParPtr->keepLayerMask)
+    {
+      gimp_layer_remove_mask (cmaskParPtr->dst_layer_id, GIMP_MASK_APPLY);
+    }
+  }
+
+  gimp_image_undo_group_end (cmaskParPtr->dst_image_id);
+
+  return (retLayerId);
+
+}  /* end gap_colormask_apply_to_layer_of_same_size */
+
+
+/* ---------------------------------------------------
+ * gap_colormask_apply_to_layer_of_same_size_from_file
+ * ---------------------------------------------------
+ * call gap_colormask_apply_to_layer_of_same_size
+ * with parameters loaded from specified filename
+ */
+gint32
+gap_colormask_apply_to_layer_of_same_size_from_file (gint32 dst_layer_id
+                        , gint32                  colormask_id
+                        , const char              *filename
+                        , gboolean                keepLayerMask
+                        , gboolean                doProgress
+                        )
+{
+  GapColormaskValues      cmaskvals;
+  gboolean                isLoadOk;
+
+  if(filename != NULL)
+  {
+    isLoadOk = gap_colormask_file_load (filename, &cmaskvals);
+  }
+  else
+  {
+    /* set default values */
+    cmaskvals.loColorThreshold = 0.11;
+    cmaskvals.hiColorThreshold = 0.11;
+    cmaskvals.colorSensitivity = 1.35;
+    cmaskvals.lowerOpacity = 0.0;
+    cmaskvals.upperOpacity = 1.0;
+    cmaskvals.triggerAlpha = 0.8;
+    cmaskvals.isleRadius = 1.0;
+    cmaskvals.isleAreaPixelLimit = 7.0;
+    cmaskvals.featherRadius = 3.0;
+    cmaskvals.thresholdColorArea = 0.0;
+    cmaskvals.pixelDiagonal = 10.0;
+    cmaskvals.pixelAreaLimit = 25.0;
+
+    isLoadOk = TRUE;
+  }
+
+  cmaskvals.colormask_id = colormask_id;
+  cmaskvals.keepLayerMask = keepLayerMask;
+  cmaskvals.keepWorklayer = FALSE;         /* dont keep debug worklayer when using the param file interface */
+  if (isLoadOk)
+  {
+    return (gap_colormask_apply_to_layer_of_same_size (dst_layer_id
+                        , &cmaskvals
+                        , doProgress
+                        ));
+  }
+
+  return -1;
+
+}  /* end gap_colormask_apply_to_layer_of_same_size_from_file */
+
+
+
+
+/* ======= */
+
+
+/* -----------------------------------------
+ * gap_create_or_replace_colormask_layer
+ * -----------------------------------------
+ * this procedure creates (or replaces) a layer named "colormask" and adds
+ * it on top of the layerstack.
+ * the new colormask layer is made as mix of the specified dst_layer_id
+ * and the colormask layer of the previous frame.
+ * (therefore the frame image with the previous frame number
+ * is loladed and searched for a layer with the name "colormask"
+ *
+ */
+gint32
+gap_create_or_replace_colormask_layer (gint32 orig_layer_id
+                        , GapColormaskValues     *cmaskvals
+                        , gboolean                doProgress
+                        )
+{
+  GapColorMaskParams colorMaskParams;
+  GapColorMaskParams *cmaskParPtr;
+  GimpPixelRgn prevCmaskPR, colormaskPR, origPR, workPR;
+  GimpDrawable *prevCmask_drawable;
+  GimpDrawable *colormask_drawable;
+  GimpDrawable *orig_drawable;
+  GimpDrawable *work_drawable;
+  gpointer  pr;
+  gint32    workLayerId;
+  gint32    prevCmaskId;
+
+
+
+  prevCmaskId = p_fetch_colormask_in_previous_frame(orig_layer_id);
+  //if(gap_debug)
+  {
+    printf("gap_create_or_replace_colormask_layer prevCmaskId:%d\n"
+      , (int)prevCmaskId
+      );
+  }
+
+  if (prevCmaskId < 0)
+  {
+    prevCmaskId = cmaskvals->colormask_id;
+  }
+  cmaskParPtr = &colorMaskParams;
+  p_copy_cmaskvals_to_colorMaskParams(cmaskvals, cmaskParPtr, orig_layer_id, doProgress);
+
+  orig_drawable = gimp_drawable_get (orig_layer_id);
+  colormask_drawable = gimp_drawable_get (cmaskParPtr->cmask_drawable_id);
+  if(gap_debug)
+  {
+    printf("gap_create_or_replace_colormask_layer checking preconditions \n"
+           "  DST: width:%d height: %d  BPP:%d \n"
+           "  MSK: width:%d height: %d  BPP:%d \n"
+           "  triggerAlpha:%d lowerAlpha:%d upperAlpha:%d colorThreshold:%f\n"
+          , (int)orig_drawable->width
+          , (int)orig_drawable->height
+          , (int)orig_drawable->bpp
+          , (int)colormask_drawable->width
+          , (int)colormask_drawable->height
+          , (int)colormask_drawable->bpp
+          , (int)cmaskParPtr->triggerAlpha255
+          , (int)cmaskParPtr->lowerAlpha255
+          , (int)cmaskParPtr->upperAlpha255
+          , (float)cmaskParPtr->colorThreshold
+          );
+  }
+
+  if((colormask_drawable->width  != orig_drawable->width)
+  || (colormask_drawable->height != orig_drawable->height)
+  || (orig_drawable->bpp    != 4)
+  || (colormask_drawable->bpp    < 3))
+  {
+    //if(gap_debug)
+    {
+      printf("gap_create_or_replace_colormask_layer preconditions not ok, operation NOT performed\n");
+    }
+    gimp_drawable_detach(colormask_drawable);
+    gimp_drawable_detach(orig_drawable);
+    return (-1);
+  }
+
+  gimp_image_undo_group_start (cmaskParPtr->dst_image_id);
+
+
+
+  cmaskParPtr->width = orig_drawable->width;
+  cmaskParPtr->height = orig_drawable->height;
+  /* progress init */
+  {
+    gint pixelSize;
+
+    pixelSize = cmaskParPtr->height * cmaskParPtr->width;
+
+    cmaskParPtr->pixelsDoneAllPasses = 0.0;
+    cmaskParPtr->pixelsToProcessInAllPasses = pixelSize;
+  }
+
+
+  p_remove_layer_by_name(cmaskParPtr->dst_image_id, orig_layer_id, COLORMASK_LAYER_NAME);
+  workLayerId = p_create_empty_layer(cmaskParPtr->dst_image_id
+                       , cmaskParPtr->width
+                       , cmaskParPtr->height
+                       , COLORMASK_LAYER_NAME
+                       );
+
+  work_drawable = gimp_drawable_get (workLayerId);
+  prevCmask_drawable = gimp_drawable_get (prevCmaskId);
+
+
+  gimp_pixel_rgn_init (&prevCmaskPR, prevCmask_drawable, 0, 0
+                      , prevCmask_drawable->width, prevCmask_drawable->height
+                      , FALSE     /* dirty */
+                      , FALSE     /* shadow */
+                       );
+  gimp_pixel_rgn_init (&colormaskPR, colormask_drawable, 0, 0
+                      , colormask_drawable->width, colormask_drawable->height
+                      , FALSE     /* dirty */
+                      , FALSE     /* shadow */
+                       );
+  gimp_pixel_rgn_init (&origPR, orig_drawable, 0, 0
+                      , orig_drawable->width, orig_drawable->height
+                      , FALSE     /* dirty */
+                      , FALSE     /* shadow */
+                       );
+  gimp_pixel_rgn_init (&workPR, work_drawable, 0, 0
+                      , work_drawable->width, work_drawable->height
+                      , TRUE      /* dirty */
+                      , FALSE     /* shadow */
+                       );
+
+
+  for (pr = gimp_pixel_rgns_register (4, &prevCmaskPR, &colormaskPR, &origPR, &workPR);
+       pr != NULL;
+       pr = gimp_pixel_rgns_process (pr))
+  {
+      p_mix_newmask_rgn_render (&prevCmaskPR, &colormaskPR, &origPR, &workPR, cmaskParPtr);
+  }
+
+
+  gimp_drawable_detach(prevCmask_drawable);
+  gimp_drawable_detach(colormask_drawable);
+  gimp_drawable_detach(orig_drawable);
+  gimp_drawable_detach(work_drawable);
+
+  gimp_image_undo_group_end (cmaskParPtr->dst_image_id);
+
+  if(prevCmaskId != cmaskParPtr->cmask_drawable_id)
+  {
+    /* delete (e.g. close) the previous frame image (form memory not from disk)
+     */
+    gap_image_delete_immediate(gimp_drawable_get_image(prevCmaskId));
+  }
+  return (workLayerId);
+
+}  /* end gap_create_or_replace_colormask_layer */
+
+
+/* -----------------------------------------
+ * gap_colormask_apply_by_name
+ * -----------------------------------------
+ * find colormask in the layerstack by name
+ * and apply to dst_layer_id if found.
+ * return -1 if not found
+ *
+ */
+gint32
+gap_colormask_apply_by_name (gint32 dst_layer_id
+                        , GapColormaskValues     *cmaskvals
+                        , gboolean                doProgress
+                        )
+{
+  gint32    colormaskId;
+
+
+  colormaskId = gap_layer_find_by_name(gimp_drawable_get_image(dst_layer_id), COLORMASK_LAYER_NAME);
+  if (colormaskId < 0)
+  {
+    return (-1);
+  }
+
+  cmaskvals->colormask_id = colormaskId;
+  return (gap_colormask_apply_to_layer_of_same_size (dst_layer_id
+                          , cmaskvals
+                          , doProgress
+                          ));
+
+}  /* end gap_colormask_apply_by_name */
+
+
diff --git a/gap/gap_colormask_exec.h b/gap/gap_colormask_exec.h
new file mode 100644
index 0000000..b8d0ac5
--- /dev/null
+++ b/gap/gap_colormask_exec.h
@@ -0,0 +1,62 @@
+/* gap_colormask_exec.h
+ *    by hof (Wolfgang Hofer)
+ *    color mask filter worker procedures
+ *    to set alpha channel for a layer according to matching colors
+ *    of color mask (image)
+ *  2010/02/21
+ *
+ */
+/* 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:
+ * version 2.7.0;             hof: created
+ */
+
+#ifndef _GAP_COLORMASK_EXEC_H
+#define _GAP_COLORMASK_EXEC_H
+
+/* SYTEM (UNIX) includes */
+#include <stdio.h>
+#include <stdlib.h>
+
+/* GIMP includes */
+#include "gtk/gtk.h"
+#include "libgimp/gimp.h"
+
+gint32   gap_colormask_apply_to_layer_of_same_size (gint32          dst_layer_id
+                                             ,GapColormaskValues *cmaskvals
+                                             ,gboolean            doProgress
+                                             );
+gint32   gap_colormask_apply_to_layer_of_same_size_from_file (gint32 dst_layer_id
+                            , gint32                  colormask_id
+                            , const char              *filename
+                            , gboolean                keepLayerMask
+                            , gboolean                doProgress
+                            );
+
+gint32   gap_create_or_replace_colormask_layer (gint32 orig_layer_id
+                        , GapColormaskValues     *cmaskvals
+                        , gboolean                doProgress
+                        );
+gint32   gap_colormask_apply_by_name (gint32 dst_layer_id
+                        , GapColormaskValues     *cmaskvals
+                        , gboolean                doProgress
+                        );
+
+#endif
diff --git a/gap/gap_colormask_file.c b/gap/gap_colormask_file.c
new file mode 100644
index 0000000..0db7bdc
--- /dev/null
+++ b/gap/gap_colormask_file.c
@@ -0,0 +1,166 @@
+/* gap_colormask_file.c
+ *    by hof (Wolfgang Hofer)
+ *    colormask filter parameter file handling procedures (load/save)
+ *  2010/02/25
+ *
+ */
+/* 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:
+ * version 2.7.0;             hof: created
+ */
+
+/* SYTEM (UNIX) includes */ 
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+
+/* GIMP includes */
+#include "gtk/gtk.h"
+#include "libgimp/gimp.h"
+
+/* GAP includes */
+#include "config.h"
+#include "gap_lib_common_defs.h"
+#include "gap_libgimpgap.h"
+#include "gap_colormask_file.h"
+
+extern int gap_debug;
+
+
+/* --------------------------
+ * p_set_master_keywords
+ * --------------------------
+ * set keywords for the colordiff filter parameter file format
+ * adresspointers to the relevant parameter, datatype (how to scan value when parameter are read from file)
+ * and comment string (that is added to the parameter when saved to file)
+ */
+static void
+p_set_master_keywords(GapValKeyList *keylist, GapColormaskValues *cmaskvals)
+{
+   gap_val_set_keyword(keylist, "(algorithm ",            &cmaskvals->algorithm,           GAP_VAL_GINT,    0, "# 0 simple algorithm");
+   gap_val_set_keyword(keylist, "(loColorThreshold ",     &cmaskvals->loColorThreshold,    GAP_VAL_GDOUBLE, 0, "# lower color threshold (0.0 to 1.0)");
+   gap_val_set_keyword(keylist, "(hiColorThreshold ",     &cmaskvals->hiColorThreshold,    GAP_VAL_GDOUBLE, 0, "# upper color threshold (0.0 to 1.0)");
+   gap_val_set_keyword(keylist, "(colorSensitivity ",     &cmaskvals->colorSensitivity,    GAP_VAL_GDOUBLE, 0, "# gamma valid range is (1.0 to 2.0)");
+   gap_val_set_keyword(keylist, "(lowerOpacity ",         &cmaskvals->lowerOpacity,        GAP_VAL_GDOUBLE, 0, "# color threshold (0.0 to 1.0)");
+   gap_val_set_keyword(keylist, "(upperOpacity ",         &cmaskvals->upperOpacity,        GAP_VAL_GDOUBLE, 0, "# color threshold (0.0 to 1.0)");
+   gap_val_set_keyword(keylist, "(triggerAlpha ",         &cmaskvals->triggerAlpha,        GAP_VAL_GDOUBLE, 0, "# opacity protection threshold (0.0 to 1.0)");
+   gap_val_set_keyword(keylist, "(isleRadius ",           &cmaskvals->isleRadius,          GAP_VAL_GDOUBLE, 0, "# radius for isolated pixel removal. (0.0 to 10.0)");
+   gap_val_set_keyword(keylist, "(isleAreaPixelLimit ",   &cmaskvals->isleAreaPixelLimit,  GAP_VAL_GDOUBLE, 0, "# size of area in pixels for remove isaolated pixel(area)s (0.0 to 100.0)");
+   gap_val_set_keyword(keylist, "(featherRadius ",        &cmaskvals->featherRadius,       GAP_VAL_GDOUBLE, 0, "# radius for feathering edges (0.0 to 10.0)");
+
+   gap_val_set_keyword(keylist, "(edgeColorThreshold ",   &cmaskvals->edgeColorThreshold,  GAP_VAL_GDOUBLE,  0, "# threshold for edge detection (relevant for algorithm 1)");
+   gap_val_set_keyword(keylist, "(thresholdColorArea ",   &cmaskvals->thresholdColorArea,  GAP_VAL_GDOUBLE,  0, "# threshold for color difference colors belonging to same area.(algorithm 2)");
+   gap_val_set_keyword(keylist, "(pixelDiagonal ",        &cmaskvals->pixelDiagonal,       GAP_VAL_GDOUBLE,  0, "# areas with smaller diagonale are considered small isles (algorithm 2, 0.0 to 100.0)");
+   gap_val_set_keyword(keylist, "(pixelAreaLimit ",       &cmaskvals->pixelAreaLimit,      GAP_VAL_GDOUBLE,  0, "# areas with less pixels are considered as small isles (algorithm 2, 0.0 to 1000.0)");
+   gap_val_set_keyword(keylist, "(connectByCorner ",      &cmaskvals->connectByCorner,     GAP_VAL_GBOOLEAN, 0, "# pixels touching only at corners are of the area. (algorithm 2, 0 to 1)");
+
+   gap_val_set_keyword(keylist, "(enableKeyColorThreshold ",  &cmaskvals->enableKeyColorThreshold,     GAP_VAL_GBOOLEAN, 0, "# enable individual low threshold for key color");
+   gap_val_set_keyword(keylist, "(loKeyColorThreshold ",   &cmaskvals->loKeyColorThreshold,  GAP_VAL_GDOUBLE,  0, "# the individual threshold for keycolor (relevant when enableKeyColorThreshold == 1)");
+   gap_val_set_keyword(keylist, "(keyColorSensitivity ",   &cmaskvals->keyColorSensitivity,  GAP_VAL_GDOUBLE,  0, "# sensitivity for keycolor (relevant when enableKeyColorThreshold == 1 0.0 to 10.0)");
+   gap_val_set_keyword(keylist, "(keyColor.r ",            &cmaskvals->keycolor.r,  GAP_VAL_GDOUBLE,  0, "# keycolor red channel (0.0 to 1.0)");
+   gap_val_set_keyword(keylist, "(keyColor.g ",            &cmaskvals->keycolor.g,  GAP_VAL_GDOUBLE,  0, "# keycolor green channel (0.0 to 1.0)");
+   gap_val_set_keyword(keylist, "(keyColor.b ",            &cmaskvals->keycolor.b,  GAP_VAL_GDOUBLE,  0, "# keycolor blue channel (0.0 to 1.0)");
+
+}  /* end p_set_master_keywords */
+
+
+/* --------------------------
+ * gap_colormask_file_load
+ * --------------------------
+ * load parameters from parameter file.
+ * return TRUE if ok, FALSE if not
+ */
+gboolean
+gap_colormask_file_load (const char *filename, GapColormaskValues *cmaskvals)
+{
+  GapValKeyList    *keylist;
+  gint              cnt_scanned_items;
+
+  /* TODO maybe init with defaults the case where no value available ?
+   */
+
+  keylist = gap_val_new_keylist();
+  p_set_master_keywords(keylist, cmaskvals);
+
+  cnt_scanned_items = gap_val_scann_filevalues(keylist, filename);
+  gap_val_free_keylist(keylist);
+
+  //if(gap_debug)
+  {
+    printf("gap_colormask_file_load: params loaded: cmaskvals:%d cnt_scanned_items:%d\n"
+       , (int)cmaskvals
+       , (int)cnt_scanned_items
+       );
+  }
+
+  if(cnt_scanned_items <= 0)
+  {
+    g_message(_("Could not read colormask parameters from file:\n%s"), filename);
+    return (FALSE);
+  }
+
+  return(TRUE);
+  
+}  /* end gap_colormask_file_load */
+
+
+/* --------------------------
+ * gap_colormask_file_save
+ * --------------------------
+ * (re)write the specified ffmpeg videoencode parameter file
+ */
+gboolean
+gap_colormask_file_save (const char *filename, GapColormaskValues *cmaskvals)
+{
+  GapValKeyList    *keylist;
+  int               l_rc;
+
+  keylist = gap_val_new_keylist();
+
+  //if(gap_debug)
+  {
+    printf("gap_colormask_file_save: now saving parameters: to file:%s\n", filename);
+  }
+
+  p_set_master_keywords(keylist, cmaskvals);
+  l_rc = gap_val_rewrite_file(keylist
+                          , filename
+                          , "# GIMP / GAP COLORMASK parameter file"   /*  hdr_text */
+                          , ")"                                       /* terminate char */
+                          );
+
+  gap_val_free_keylist(keylist);
+
+  if(l_rc != 0)
+  {
+    gint l_errno;
+
+    l_errno = errno;
+    g_message(_("Could not save colormask parameterfile:"
+               "'%s'"
+               "%s")
+               ,filename
+               ,g_strerror (l_errno) );
+    return (FALSE);
+  }
+
+  return(TRUE);
+}  /* end gap_colormask_file_save */
+
diff --git a/gap/gap_colormask_file.h b/gap/gap_colormask_file.h
new file mode 100644
index 0000000..8fc3a5a
--- /dev/null
+++ b/gap/gap_colormask_file.h
@@ -0,0 +1,91 @@
+/* gap_colormask_file.h
+ *    by hof (Wolfgang Hofer)
+ *    colormask filter parameter file handling procedures (load/save)
+ *  2010/02/25
+ *
+ */
+/* 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:
+ * version 2.7.0;             hof: created
+ */
+
+#ifndef _GAP_COLORMASK_FILE_H
+#define _GAP_COLORMASK_FILE_H
+
+/* SYTEM (UNIX) includes */
+#include <stdio.h>
+#include <stdlib.h>
+
+/* GIMP includes */
+#include "gtk/gtk.h"
+#include "libgimp/gimp.h"
+
+#define GAP_COLORMASK_ALGO_SIMPLE        0
+#define GAP_COLORMASK_ALGO_AVG_CHANGE_1  1
+#define GAP_COLORMASK_ALGO_AVG_CHANGE_2  2
+#define GAP_COLORMASK_ALGO_AVG_SMART     3
+#define GAP_COLORMASK_ALGO_AVG_AREA      4
+
+#define GAP_COLORMASK_ALGO_EDGE          5
+#define GAP_COLORMASK_ALGO_MAX           5.0
+
+typedef struct {
+     gint32     colormask_id;
+     gdouble    loColorThreshold;      /* color difference threshold 0.0 to 1.0 */
+     gdouble    hiColorThreshold;    /* color difference threshold 0.0 to 1.0 */
+     gdouble    colorSensitivity;    /* 1.0 to 2.0 sensitivity for colr diff algorithm */
+     gdouble    lowerOpacity;        /* opacity for matching pixels 0.0 to 1.0 */
+     gdouble    upperOpacity;        /* opacity for non matching pixels 0.0 to 1.0 */
+     gdouble    triggerAlpha;        /* 0.0 to 1.0 mask opacity greater than this alpha value trigger color masking */
+     gdouble    isleRadius;          /* radius in pixels for remove isaolated pixels */
+     gdouble    isleAreaPixelLimit;  /* size of area in pixels for remove isaolated pixel(area)s */
+     gdouble    featherRadius;       /* radius in pixels for feathering edges */
+
+     gdouble    edgeColorThreshold;
+     gdouble    thresholdColorArea;
+     gdouble    pixelDiagonal;
+     gdouble    pixelAreaLimit;
+     gboolean   connectByCorner;
+
+     gboolean   keepLayerMask;
+     gboolean   keepWorklayer;       /* shall be FALSE for productive usage
+                                      * TRUE: debug feature for visible verification of the area algorithm processing
+                                      */
+     gboolean   enableKeyColorThreshold;
+     GimpRGB    keycolor;
+     gdouble    loKeyColorThreshold;    /* color difference lower threshold for keycolor 0.0 to 1.0 */
+     gdouble    keyColorSensitivity;    /* 0.1 upto 10.0 default 1.0 */
+
+     gdouble significantRadius;              /* radius to limit search for significant brightness/colorchanges */
+     gdouble significantColordiff;           /* threshold to define when colors are considered as significant different */
+     gdouble significantBrightnessDiff;      /* threshold to define significant brightness changes */
+
+
+     gint       algorithm;           /* 0 = simple, 1 = edge, 2= area */
+
+} GapColormaskValues;
+
+
+gboolean gap_colormask_file_load (const char *filename, GapColormaskValues *cmaskvals);
+
+gboolean gap_colormask_file_save (const char *filename, GapColormaskValues *cmaskvals);
+
+
+#endif
diff --git a/gap/gap_colormask_main.c b/gap/gap_colormask_main.c
new file mode 100644
index 0000000..0794d0d
--- /dev/null
+++ b/gap/gap_colormask_main.c
@@ -0,0 +1,467 @@
+/*  gap_colormask_main.c
+ *    color mask filter main and dialog procedures
+ *    to set alpha channel for a layer according to matching colors
+ *    of a color mask (image)  by Wolfgang Hofer
+ *  2010/02/21
+ */
+
+/* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* Revision history
+ *  (2010/02/21)  2.7.0       hof: created
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include <gtk/gtk.h>
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "gap_lastvaldesc.h"
+#include "gap_colormask_file.h"
+#include "gap_colormask_dialog.h"
+#include "gap_colormask_exec.h"
+
+#include "gap-intl.h"
+
+/* Defines */
+#define PLUG_IN_NAME        "plug-in-layer-set-alpha-by-colormask"
+#define PLUG_IN_NAME_2      "plug-in-layer-set-alpha-by-colormask-in-frame"
+#define PLUG_IN_NAME_3      "plug-in-layer-create-colormask-in-frame"
+#define PLUG_IN_PRINT_NAME  "Name to Layer"
+#define PLUG_IN_IMAGE_TYPES "RGB*, INDEXED*, GRAY*"
+#define PLUG_IN_AUTHOR      "Wolfgang Hofer (hof gimp org)"
+#define PLUG_IN_COPYRIGHT   "Wolfgang Hofer"
+
+
+int gap_debug = 1;  /* 1 == print debug infos , 0 dont print debug infos */
+
+
+
+static GapColormaskValues cmaskvals =
+{
+   -1    /* colormask_id drawable id */
+ , 0.03  /* loColorThreshold */
+ , 0.12  /* hiColorThreshold */
+ , 1.35  /* colorSensitivity   */
+ , 0.0   /* lowerOpacity */
+ , 1.0   /* upperOpacity */
+ , 0.8   /* triggerAlpha */
+ , 1.0   /* isleRadius */
+ , 7.0   /* isleAreaPixelLimit */
+ , 2.0   /* featherRadius */
+ , 0.1   /* edgeColorThreshold */
+ , 0.0   /* thresholdColorArea */
+ , 6.0   /* pixelDiagonal      */
+ , 10.0  /* pixelAreaLimit     */
+ , 0     /* connectByCorner    */
+
+ , 1     /* keepLayerMask */
+ , 0     /* keepWorklayer */
+
+
+ , 0            /* gboolean   enableKeyColorThreshold */
+ , { 0,0,0,0 }  /* GimpRGB keycolor */
+ , 0.06         /* gdouble    loKeyColorThreshold */
+ , 1.0          /* gdouble    keyColorSensitivity */
+
+
+ , GAP_COLORMASK_ALGO_AVG_SMART
+};
+
+
+
+
+static GimpDrawable *globalDrawable = NULL;
+
+static void  query (void);
+static void  run (const gchar *name,          /* name of plugin */
+     gint nparams,               /* number of in-paramters */
+     const GimpParam * param,    /* in-parameters */
+     gint *nreturn_vals,         /* number of out-parameters */
+     GimpParam ** return_vals);  /* out-parameters */
+
+static gint  p_colormask_apply_run (gint32 image_id, gint32 drawable_id, gboolean doProgress, const char *name);
+
+
+
+
+/* Global Variables */
+GimpPlugInInfo PLUG_IN_INFO =
+{
+  NULL,   /* init_proc  */
+  NULL,   /* quit_proc  */
+  query,  /* query_proc */
+  run     /* run_proc   */
+};
+
+static GimpParamDef in_args[] = {
+                  { GIMP_PDB_INT32,    "run_mode", "Interactive, non-interactive"},
+                  { GIMP_PDB_IMAGE,    "image", "Input image" },
+                  { GIMP_PDB_DRAWABLE, "drawable", "layer with alpha channel to be processed"},
+                  { GIMP_PDB_DRAWABLE, "colormaskDrawable", "rgb drawable (optional with with alpha channel) to be applied"},
+                  { GIMP_PDB_FLOAT,    "loColorThreshold", "0.0 to 1.0 color difference lower threshold value (diff below this is considered as matching color)"},
+                  { GIMP_PDB_FLOAT,    "hiColorThreshold", "0.0 to 1.0 color difference upper threshold value (diff above this is considered as not matching color)"},
+                  { GIMP_PDB_FLOAT,    "colorSensitivity", "1 .. 2 sensitivity for color dff algorithm"},
+                  { GIMP_PDB_FLOAT,    "lowerOpacity", "0.0 to 1.0 opacity value for matching pixels"},
+                  { GIMP_PDB_FLOAT,    "upperOpacity", "0.0 to 1.0 opacity value for non matching pixels"},
+                  { GIMP_PDB_FLOAT,    "triggerAlpha", "0.0 to 1.0 in case the colormask has alpha, greater values than the specifiedone trigger colormasking "},
+                  { GIMP_PDB_FLOAT,    "isleRadius",   "radius in pixels for remove isolated pixels"},
+                  { GIMP_PDB_FLOAT,    "isleAreaPixelLimit",   "area size in pixels for removal of isolated pixel areas )"},
+                  { GIMP_PDB_FLOAT,    "featherRadius", "radius in pixels for smoothing edges "},
+                  { GIMP_PDB_FLOAT,    "edgeColorThreshold", "color difference for edge detection (relevant for algorthm 1, range 0.0 to 1.0) "},
+                  { GIMP_PDB_FLOAT,    "thresholdColorArea", "threshold for color difference colors belonging to same area. (relevant for algorithm 2 range .0 to 1.0) "},
+                  { GIMP_PDB_FLOAT,    "pixelDiagonal", "areas with smaller diagonale are considered small isles (relevant for algorithm 2)"},
+                  { GIMP_PDB_FLOAT,    "pixelAreaLimit", "areas with less pixels are considered as small isles  (relevant for algorithm 2)"},
+                  { GIMP_PDB_INT32,    "connectByCorner", "pixels toching only at corners ar pat/not part of the area valid range is (relevant for algorithm 2) "},
+                  { GIMP_PDB_INT32,    "keepLayerMask", "0 apply the generated layerMask, 1 keep the generated layermask"},
+                  { GIMP_PDB_INT32,    "keepWorklayer", "0 delete internal worklayer, 1 keep the generated layermask (relevant for algorithm 2 for DEBUG purpose)"},
+                  { GIMP_PDB_INT32,    "enableKeyColorThreshold", "0 disable, 1 enable individual threshold for keycolor"},
+                  { GIMP_PDB_COLOR,    "keycolor", "KeyColor" },
+                  { GIMP_PDB_FLOAT,    "loKeyColorThreshold", "0.0 to 1.0 color difference lower individual threshold value for the key color"},
+                  { GIMP_PDB_FLOAT,    "keyColorSensitivity", "0.0 to 10.0 "},
+                  { GIMP_PDB_INT32,    "algorithm", "0 simple, 1 edge, 2 area"}
+  };
+
+static GimpParamDef return_vals[] = {
+    { GIMP_PDB_DRAWABLE, "the_drawable", "the handled drawable" }
+};
+
+static gint global_number_in_args = G_N_ELEMENTS (in_args);
+static gint global_number_out_args = G_N_ELEMENTS (return_vals);
+
+
+/* Functions */
+
+MAIN ()
+
+static void query (void)
+{
+  static GimpLastvalDef lastvals[] =
+  {
+    GIMP_LASTVALDEF_DRAWABLE        (GIMP_ITER_FALSE,  cmaskvals.colormask_id,            "colormaskDrawable"),
+    GIMP_LASTVALDEF_GDOUBLE         (GIMP_ITER_FALSE,  cmaskvals.loColorThreshold,        "loColorThreshold"),
+    GIMP_LASTVALDEF_GDOUBLE         (GIMP_ITER_FALSE,  cmaskvals.hiColorThreshold,        "hiColorThreshold"),
+    GIMP_LASTVALDEF_GDOUBLE         (GIMP_ITER_FALSE,  cmaskvals.colorSensitivity,        "colorSensitivity"),
+    GIMP_LASTVALDEF_GDOUBLE         (GIMP_ITER_TRUE,   cmaskvals.lowerOpacity,            "lowerOpacity"),
+    GIMP_LASTVALDEF_GDOUBLE         (GIMP_ITER_TRUE,   cmaskvals.upperOpacity,            "upperOpacity"),
+    GIMP_LASTVALDEF_GDOUBLE         (GIMP_ITER_TRUE,   cmaskvals.triggerAlpha,            "triggerAlpha"),
+    GIMP_LASTVALDEF_GDOUBLE         (GIMP_ITER_TRUE,   cmaskvals.isleRadius,              "isleRadius"),
+    GIMP_LASTVALDEF_GDOUBLE         (GIMP_ITER_TRUE,   cmaskvals.isleAreaPixelLimit,      "isleAreaPixelLimit"),
+    GIMP_LASTVALDEF_GDOUBLE         (GIMP_ITER_TRUE,   cmaskvals.featherRadius,           "featherRadius"),
+    GIMP_LASTVALDEF_GDOUBLE         (GIMP_ITER_TRUE,   cmaskvals.edgeColorThreshold,      "edgeColorThreshold"),
+    GIMP_LASTVALDEF_GDOUBLE         (GIMP_ITER_TRUE,   cmaskvals.thresholdColorArea,      "thresholdColorArea"),
+    GIMP_LASTVALDEF_GDOUBLE         (GIMP_ITER_TRUE,   cmaskvals.pixelDiagonal,           "pixelDiagonal"),
+    GIMP_LASTVALDEF_GDOUBLE         (GIMP_ITER_TRUE,   cmaskvals.pixelAreaLimit,          "pixelAreaLimit"),
+    GIMP_LASTVALDEF_GINT32          (GIMP_ITER_FALSE,  cmaskvals.connectByCorner,         "connectByCorner"),
+    GIMP_LASTVALDEF_GINT32          (GIMP_ITER_FALSE,  cmaskvals.keepLayerMask,           "keepLayerMask"),
+    GIMP_LASTVALDEF_GINT32          (GIMP_ITER_FALSE,  cmaskvals.keepWorklayer,           "keepWorklayer"),
+    GIMP_LASTVALDEF_GINT32          (GIMP_ITER_FALSE,  cmaskvals.enableKeyColorThreshold, "enableKeyColorThreshold"),
+    GIMP_LASTVALDEF_GIMPRGB         (GIMP_ITER_TRUE,   cmaskvals.keycolor,                "keycolor"),
+    GIMP_LASTVALDEF_GDOUBLE         (GIMP_ITER_TRUE,   cmaskvals.loKeyColorThreshold,     "loKeyColorThreshold"),
+    GIMP_LASTVALDEF_GDOUBLE         (GIMP_ITER_TRUE,   cmaskvals.keyColorSensitivity,     "keyColorSensitivity"),
+    GIMP_LASTVALDEF_GINT            (GIMP_ITER_FALSE,  cmaskvals.algorithm,               "algorithm")
+  };
+
+
+
+  gimp_plugin_domain_register (GETTEXT_PACKAGE, LOCALEDIR);
+
+  /* registration for last values buffer structure (useful for animated filter apply) */
+  gimp_lastval_desc_register(PLUG_IN_NAME,
+                             &cmaskvals,
+                             sizeof(cmaskvals),
+                             G_N_ELEMENTS (lastvals),
+                             lastvals);
+//   gimp_lastval_desc_register(PLUG_IN_NAME_2,
+//                              &cmaskvals,
+//                              sizeof(cmaskvals),
+//                              G_N_ELEMENTS (lastvals),
+//                              lastvals);
+//   gimp_lastval_desc_register(PLUG_IN_NAME_3,
+//                              &cmaskvals,
+//                              sizeof(cmaskvals),
+//                              G_N_ELEMENTS (lastvals),
+//                              lastvals);
+
+  /* the actual installation of the plugin */
+  gimp_install_procedure (PLUG_IN_NAME,
+                          "Set layer opacity by applying another color drawable as colormask",
+                          "This plug-in sets the opacity for the specified Input drawable (a layer), "
+                          "according to matching colors of the specified colormask drawable. "
+                          "The resulting opacity is created as layermask. "
+                          "This newly created layermask is applied optionally (in case parameter keepLayerMask is FALSE)."
+                          "Note that the colormask drawable may have an alpha channel."
+                          "in this case the transparent parts of the colormask are protected areas, "
+                          "that are not affected by this filter. "
+                          "(e.g the corresponding pixels in processed layer keep their original opacity) "
+                          " ",
+                          PLUG_IN_AUTHOR,
+                          PLUG_IN_COPYRIGHT,
+                          GAP_VERSION_WITH_DATE,
+                          N_("Apply Colormask..."),
+                          PLUG_IN_IMAGE_TYPES,
+                          GIMP_PLUGIN,
+                          global_number_in_args,
+                          global_number_out_args,
+                          in_args,
+                          return_vals);
+//   gimp_install_procedure (PLUG_IN_NAME_2,
+//                           "Set Layer Opacity by applying the colormask layer colormask",
+//                           "This plug-in sets the alpha channel for the specified Input drawable (a layer), "
+//                           "according to matching colors of the colormask. The colormask must be a layer in the same image "
+//                           "with the layer name colormask"
+//                           "this procedure is intended for processing with individual per frame colormask "
+//                           " ",
+//                           PLUG_IN_AUTHOR,
+//                           PLUG_IN_COPYRIGHT,
+//                           GAP_VERSION_WITH_DATE,
+//                           N_("Apply Colormask in Frame..."),
+//                           PLUG_IN_IMAGE_TYPES,
+//                           GIMP_PLUGIN,
+//                           global_number_in_args,
+//                           global_number_out_args,
+//                           in_args,
+//                           return_vals);
+//   gimp_install_procedure (PLUG_IN_NAME_3,
+//                           "Create or replace a individual per frame colormask as top layer",
+//                           "The colormask is created as mix of the processed original layer and, "
+//                           "an initial colormask and /or the colormask from the previous frame. "
+//                           "(the processed drawable shall be a layer of a frame image)"
+//                           "xxxxxxx "
+//                           " ",
+//                           PLUG_IN_AUTHOR,
+//                           PLUG_IN_COPYRIGHT,
+//                           GAP_VERSION_WITH_DATE,
+//                           N_("Create Colormask..."),
+//                           PLUG_IN_IMAGE_TYPES,
+//                           GIMP_PLUGIN,
+//                           global_number_in_args,
+//                           global_number_out_args,
+//                           in_args,
+//                           return_vals);
+
+  {
+    /* Menu names */
+    const char *menupath_image_video_layer_attr = N_("<Image>/Video/Layer/Attributes/");
+
+    //gimp_plugin_menu_branch_register("<Image>", "Video");
+    //gimp_plugin_menu_branch_register("<Image>/Video", "Layer");
+
+    gimp_plugin_menu_register (PLUG_IN_NAME, menupath_image_video_layer_attr);
+//     gimp_plugin_menu_register (PLUG_IN_NAME_2, menupath_image_video_layer_attr);
+//     gimp_plugin_menu_register (PLUG_IN_NAME_3, menupath_image_video_layer_attr);
+  }
+
+}  /* end query */
+
+static void
+run (const gchar *name,          /* name of plugin */
+     gint nparams,               /* number of in-paramters */
+     const GimpParam * param,    /* in-parameters */
+     gint *nreturn_vals,         /* number of out-parameters */
+     GimpParam ** return_vals)   /* out-parameters */
+{
+  const gchar *l_env;
+  gint32       image_id = -1;
+  gboolean doProgress;
+
+
+
+  /* Get the runmode from the in-parameters */
+  GimpRunMode run_mode = param[0].data.d_int32;
+
+  /* status variable, use it to check for errors in invocation usualy only
+     during non-interactive calling */
+  GimpPDBStatusType status = GIMP_PDB_SUCCESS;
+
+  /* always return at least the status to the caller. */
+  static GimpParam values[2];
+
+  INIT_I18N();
+
+  l_env = g_getenv("GAP_DEBUG");
+  if(l_env != NULL)
+  {
+    if((*l_env != 'n') && (*l_env != 'N')) gap_debug = 1;
+  }
+
+  if(gap_debug) printf("\n\nDEBUG: run %s\n", name);
+
+
+  doProgress = FALSE;
+
+  /* initialize the return of the status */
+  values[0].type = GIMP_PDB_STATUS;
+  values[0].data.d_status = status;
+  values[1].type = GIMP_PDB_DRAWABLE;
+  values[1].data.d_drawable = -1;
+  *nreturn_vals = 2;
+  *return_vals = values;
+
+
+  /* get image and drawable */
+  image_id = param[1].data.d_int32;
+
+  /* Get drawable information */
+  globalDrawable = gimp_drawable_get (param[2].data.d_drawable);
+
+
+  /* how are we running today? */
+  switch (run_mode)
+  {
+    case GIMP_RUN_INTERACTIVE:
+      /* Possibly retrieve data from a previous run */
+      gimp_get_data (name, &cmaskvals);
+
+      /* Get information from the dialog */
+      if (!gap_colormask_dialog(&cmaskvals, globalDrawable))
+      {
+        return;
+      }
+      doProgress = TRUE;
+      break;
+
+    case GIMP_RUN_NONINTERACTIVE:
+      /* check to see if invoked with the correct number of parameters */
+      if (nparams == global_number_in_args)
+      {
+          cmaskvals.colormask_id        = (gint32)  param[3].data.d_drawable;
+          cmaskvals.loColorThreshold    = (gdouble) param[4].data.d_float;
+          cmaskvals.hiColorThreshold    = (gdouble) param[5].data.d_float;
+          cmaskvals.colorSensitivity    = (gdouble) param[6].data.d_float;
+          cmaskvals.lowerOpacity        = (gdouble) param[7].data.d_float;
+          cmaskvals.upperOpacity        = (gdouble) param[8].data.d_float;
+          cmaskvals.triggerAlpha        = (gdouble) param[9].data.d_float;
+          cmaskvals.isleRadius          = (gdouble) param[10].data.d_float;
+          cmaskvals.isleAreaPixelLimit  = (gdouble) param[11].data.d_float;
+          cmaskvals.featherRadius       = (gdouble) param[12].data.d_float;
+
+          cmaskvals.thresholdColorArea  = (gdouble) param[13].data.d_float;
+          cmaskvals.pixelDiagonal       = (gdouble) param[14].data.d_float;
+          cmaskvals.pixelAreaLimit      = (gdouble) param[15].data.d_float;
+          cmaskvals.connectByCorner     = (param[16].data.d_int32 == 0) ? FALSE : TRUE;
+          cmaskvals.keepLayerMask       = (param[17].data.d_int32 == 0) ? FALSE : TRUE;
+          cmaskvals.keepWorklayer       = (param[18].data.d_int32 == 0) ? FALSE : TRUE;
+
+          cmaskvals.enableKeyColorThreshold       = (param[19].data.d_int32 == 0) ? FALSE : TRUE;
+          cmaskvals.keycolor                      = param[20].data.d_color;
+          cmaskvals.loKeyColorThreshold           = (gdouble) param[21].data.d_float;
+          cmaskvals.keyColorSensitivity           = (gdouble) param[22].data.d_float;
+
+          cmaskvals.algorithm           =  param[23].data.d_int32;
+      }
+      else
+      {
+        status = GIMP_PDB_CALLING_ERROR;
+      }
+
+      break;
+
+    case GIMP_RUN_WITH_LAST_VALS:
+      /* Possibly retrieve data from a previous run */
+      gimp_get_data (name, &cmaskvals);
+
+      break;
+
+    default:
+      break;
+  }
+
+  if (status == GIMP_PDB_SUCCESS)
+  {
+    /* Run the main function */
+    values[1].data.d_drawable = p_colormask_apply_run(image_id, param[2].data.d_drawable, doProgress, name);
+    if (values[1].data.d_drawable < 0)
+    {
+       status = GIMP_PDB_CALLING_ERROR;
+    }
+
+    /* If run mode is interactive, flush displays, else (script) don't
+       do it, as the screen updates would make the scripts slow */
+    if (run_mode != GIMP_RUN_NONINTERACTIVE)
+      gimp_displays_flush ();
+
+    /* Store variable states for next run */
+    if (run_mode == GIMP_RUN_INTERACTIVE)
+      gimp_set_data (name, &cmaskvals, sizeof (GapColormaskValues));
+
+    if(run_mode != GIMP_RUN_NONINTERACTIVE)
+      gimp_drawable_flush (globalDrawable);
+  }
+  values[0].data.d_status = status;
+
+
+  gimp_drawable_detach (globalDrawable);
+}       /* end run */
+
+
+
+/* ----------------------
+ * p_colormask_apply_run
+ * ----------------------
+ * The processing functions
+ *
+ */
+static gint32
+p_colormask_apply_run (gint32 image_id, gint32 drawable_id, gboolean doProgress, const char *name)
+{
+  gint32     retLayerId;
+
+  if (!gimp_drawable_is_layer(drawable_id))
+  {
+    return (-1);  /* dont operate on other drawables than layers */
+  }
+  if (!gimp_drawable_has_alpha(drawable_id))
+  {
+    return (-1);  /* dont operate on layers without alpha channel */
+  }
+
+  if (strcmp(name, PLUG_IN_NAME_3) == 0)
+  {
+    retLayerId = gap_create_or_replace_colormask_layer (drawable_id
+                        , &cmaskvals
+                        , doProgress
+                        );
+  }
+  else if (strcmp(name, PLUG_IN_NAME_2) == 0)
+  {
+    retLayerId = gap_colormask_apply_by_name (drawable_id
+                        , &cmaskvals
+                        , doProgress
+                        );
+  }
+  else
+  {
+    retLayerId = gap_colormask_apply_to_layer_of_same_size (drawable_id
+                        , &cmaskvals
+                        , doProgress
+                        );
+  }
+
+  return (retLayerId);
+}       /* end p_colormask_apply_run */
+
+
+
+
diff --git a/gap/gap_decode_mplayer.c b/gap/gap_decode_mplayer.c
index 7376a7f..5243241 100644
--- a/gap/gap_decode_mplayer.c
+++ b/gap/gap_decode_mplayer.c
@@ -296,7 +296,7 @@ p_mplayer_info(char *errlist)
 /* -----------------------
  * p_scann_start_time
  * -----------------------
- * scann hour, minute, second from a string
+ * scan hour, minute, second from a string
  * that has the format "HH:MM:SS"
  * where any other non-digit characters are treated same as ":"
  * strings with format "HHMMSS" are also accepted.
diff --git a/gap/gap_edge_detection.c b/gap/gap_edge_detection.c
new file mode 100644
index 0000000..6d198e8
--- /dev/null
+++ b/gap/gap_edge_detection.c
@@ -0,0 +1,421 @@
+/* gap_edge_detection.c
+ *    by hof (Wolfgang Hofer)
+ *  2010/08/10
+ *
+ */
+/* 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:
+ * version 2.7.0;             hof: created
+ */
+
+/* SYTEM (UNIX) includes */
+#include "string.h"
+/* GIMP includes */
+/* GAP includes */
+#include "gap_lib_common_defs.h"
+#include "gap_edge_detection.h"
+#include "gap_colordiff.h"
+#include "gap_lib.h"
+#include "gap_image.h"
+#include "gap_layer_copy.h"
+#include "gap_libgapbase.h"
+
+#define OPACITY_LEVEL_UCHAR 50
+
+
+extern      int gap_debug; /* ==0  ... dont print debug infos */
+
+typedef struct GapEdgeContext { /* nickname: ectx */
+     GimpDrawable *refDrawable;
+     GimpDrawable *edgeDrawable;
+     gdouble       edgeColordiffThreshold;
+     gint32        edgeOpacityThreshold255;
+     gint32        edgeImageId;
+     gint32        edgeDrawableId;
+     gint32        countEdgePixels;
+
+     GimpPixelFetcher *pftRef;
+
+  } GapEdgeContext;
+
+
+
+/* ---------------------------------
+ * p_edgeProcessingForOneRegion
+ * ---------------------------------
+ * render edge drawable for the current processed pixel region.
+ * (use a pixelfetcher on region boundaries)
+ */
+static void
+p_edgeProcessingForOneRegion (const GimpPixelRgn *refPR
+                    ,const GimpPixelRgn *edgePR
+                    ,GapEdgeContext *ectx)
+{
+  guint    row;
+  guchar* ref = refPR->data;
+  guchar* edge = edgePR->data;
+  guchar  rightPixel[4];
+  guchar  botPixel[4];
+  guchar  rbPixel[4];
+  guchar* rightPixelPtr;
+  guchar* botPixelPtr;
+  guchar* rbPixelPtr;
+  gint32   rx; 
+  gint32   ry; 
+  gboolean debugPrint;
+        
+  
+  
+  if(gap_debug)
+  {
+    printf("p_edgeProcessingForOneRegion START\n");
+  }
+  debugPrint = FALSE;
+  
+  for (row = 0; row < edgePR->h; row++)
+  {
+    guint  col;
+    guint  idxref;
+    guint  idxedge;
+
+    ry = refPR->y + row;
+
+    idxref = 0;
+    idxedge = 0;
+    for(col = 0; col < edgePR->w; col++)
+    {
+      gdouble  colordiff1;
+      gdouble  colordiff2;
+      gdouble  colordiff3;
+      gdouble  maxColordiff;
+      gboolean isColorCompareRequired;
+
+      rbPixelPtr = &ref[idxref];
+      
+      rx = refPR->x + col;
+      
+      isColorCompareRequired = TRUE;
+        
+/*
+ *       if(gap_debug)
+ *       {
+ *         debugPrint = FALSE;
+ *         if((rx == 596) || (rx == 597))
+ *         {
+ *           if((ry==818) ||(ry==818))
+ *           {
+ *             debugPrint = TRUE;
+ *           }
+ *         }
+ *       }
+ */
+      
+      if(col < edgePR->w -1)
+      {
+        rightPixelPtr = &ref[idxref + refPR->bpp];
+
+        if(row < edgePR->h -1)
+        {
+          rbPixelPtr = &ref[idxref + refPR->bpp + refPR->rowstride];
+        }
+      }
+      else if(rx >= ectx->refDrawable->width -1)
+      {
+         /* drawable border is not considered as edge */
+        rightPixelPtr = &ref[idxref];
+      }
+      else
+      {
+        rightPixelPtr = &rightPixel[0];
+        gimp_pixel_fetcher_get_pixel (ectx->pftRef, rx +1, ry, rightPixelPtr);
+
+        if(ry >= ectx->refDrawable->height -1)
+        {
+          rbPixelPtr = &rbPixel[0];
+          gimp_pixel_fetcher_get_pixel (ectx->pftRef, rx +1, ry +1, rbPixelPtr);
+        }
+      }
+      
+      if(row < edgePR->h -1)
+      {
+        botPixelPtr = &ref[idxref + refPR->rowstride];
+      }
+      else if(ry >= ectx->refDrawable->height -1)
+      {
+         /* drawable border is not considered as edge */
+        botPixelPtr = &ref[idxref];
+      }
+      else
+      {
+        botPixelPtr = &botPixel[0];
+        gimp_pixel_fetcher_get_pixel (ectx->pftRef, rx, ry +1, botPixelPtr);
+      }
+
+      if(refPR->bpp > 3)
+      {
+        /* reference drawable has alpha channel
+         * in this case significant changes of opacity shall detect edge
+         */
+        gint32 maxAlphaDiff;
+        
+        maxAlphaDiff = MAX(abs(ref[idxref +3] - rightPixelPtr[3])
+                          ,abs(ref[idxref +3] - botPixelPtr[3]));
+        maxAlphaDiff = MAX(maxAlphaDiff
+                          ,abs(ref[idxref +3] - rbPixelPtr[3]));
+        if(debugPrint)
+        {
+          printf("rx:%d ry:%d idxref:%d idxedge:%d (maxAlphaDiff):%d  Thres:%d  Alpha ref:%d right:%d bot:%d rb:%d\n"
+               , (int)rx
+               , (int)ry
+               , (int)idxref
+               , (int)idxedge
+               , (int)maxAlphaDiff
+               , (int)ectx->edgeOpacityThreshold255
+               , (int)ref[idxref +3]
+               , (int)rightPixelPtr[3]
+               , (int)botPixelPtr[3]
+               , (int)rbPixelPtr[3]
+               );
+        }
+        
+        if(maxAlphaDiff > ectx->edgeOpacityThreshold255)
+        {
+           ectx->countEdgePixels++;
+           edge[idxedge] = maxAlphaDiff;
+           isColorCompareRequired = FALSE;
+        }
+        else if(ref[idxref +3] < OPACITY_LEVEL_UCHAR)
+        {
+          /* transparent pixel is not considered as edge */
+          edge[idxedge] = 0;
+          isColorCompareRequired = FALSE;
+        }
+        
+      }
+      
+      
+      if(isColorCompareRequired == TRUE)
+      {
+        
+        colordiff1 = gap_colordiff_simple_guchar(&ref[idxref]
+                     , &rightPixelPtr[0]
+                     , debugPrint    /* debugPrint */
+                     );
+
+        colordiff2 = gap_colordiff_simple_guchar(&ref[idxref]
+                     , &botPixelPtr[0]
+                     , debugPrint    /* debugPrint */
+                     );
+
+        colordiff3 = gap_colordiff_simple_guchar(&ref[idxref]
+                     , &rbPixelPtr[0]
+                     , debugPrint    /* debugPrint */
+                     );
+        maxColordiff = MAX(colordiff1, colordiff2);
+        maxColordiff = MAX(maxColordiff, colordiff3);
+
+        if(debugPrint)
+        {
+          printf("rx:%d ry:%d colordiff(1): %.5f (2):%.5f (3):%.5f (max):%.5f Thres:%.5f\n"
+            , (int)rx
+            , (int)ry
+            , (float)colordiff1
+            , (float)colordiff2
+            , (float)colordiff3
+            , (float)maxColordiff
+            , (float)ectx->edgeColordiffThreshold
+            );
+        }
+
+        if (maxColordiff < ectx->edgeColordiffThreshold)
+        {
+            edge[idxedge] = 0;
+        }
+        else
+        {
+            gdouble value;
+
+            value = maxColordiff * 255.0;
+            edge[idxedge] = CLAMP(value, 1, 255);
+            ectx->countEdgePixels++;
+        }
+      }
+       
+
+
+      idxref += refPR->bpp;
+      idxedge += edgePR->bpp;
+    }
+
+    ref += refPR->rowstride;
+    edge += edgePR->rowstride;
+
+  }
+
+}  /* end p_edgeProcessingForOneRegion */
+
+
+
+/* ----------------------------------------
+ * p_createEmptyEdgeDrawable
+ * ----------------------------------------
+ * create the (empty) edge drawable as layer of a new image
+ *
+ */
+static void
+p_createEmptyEdgeDrawable(GapEdgeContext *ectx)
+{
+  ectx->edgeImageId = gimp_image_new(ectx->refDrawable->width
+                                   , ectx->refDrawable->height
+                                   , GIMP_GRAY
+                                   );
+  ectx->edgeDrawableId = gimp_layer_new(ectx->edgeImageId
+                , "edge"
+                , ectx->refDrawable->width
+                , ectx->refDrawable->height
+                , GIMP_GRAY_IMAGE
+                , 100.0   /* full opacity */
+                , 0       /* normal mode */
+                );
+
+  gimp_image_add_layer (ectx->edgeImageId, ectx->edgeDrawableId, 0 /* stackposition */ );
+
+  ectx->edgeDrawable = gimp_drawable_get(ectx->edgeDrawableId);
+  
+}  /* end p_createEmptyEdgeDrawable */
+
+
+/* ----------------------------------------
+ * p_edgeDetection
+ * ----------------------------------------
+ * setup pixel regions and perform edge detection processing per region.
+ * as result of this processing  the edgeDrawable is created and rendered.
+ */
+static void
+p_edgeDetection(GapEdgeContext *ectx)
+{
+  GimpPixelRgn refPR;
+  GimpPixelRgn edgePR;
+  gpointer  pr;
+
+  p_createEmptyEdgeDrawable(ectx);
+  if(ectx->edgeDrawable == NULL)
+  {
+    return;
+  }
+  
+
+  gimp_pixel_rgn_init (&refPR, ectx->refDrawable, 0, 0
+                      , ectx->refDrawable->width, ectx->refDrawable->height
+                      , FALSE     /* dirty */
+                      , FALSE     /* shadow */
+                       );
+
+  gimp_pixel_rgn_init (&edgePR, ectx->edgeDrawable, 0, 0
+                      , ectx->edgeDrawable->width, ectx->edgeDrawable->height
+                      , TRUE     /* dirty */
+                      , FALSE    /* shadow */
+                       );
+
+  /* compare pixel areas in tiled portions via pixel region processing loops.
+   */
+  for (pr = gimp_pixel_rgns_register (2, &refPR, &edgePR);
+       pr != NULL;
+       pr = gimp_pixel_rgns_process (pr))
+  {
+    p_edgeProcessingForOneRegion (&refPR, &edgePR, ectx);
+  }
+  gimp_drawable_flush (ectx->edgeDrawable);
+}
+
+
+
+/* ----------------------------------------
+ * gap_edgeDetection
+ * ----------------------------------------
+ *
+ * returns the drawable id of a newly created channel
+ * representing edges of the specified image.
+ *
+ * black pixels indicate areas of same or similar colors,
+ * white indicates sharp edges.
+ *
+ */
+gint32 gap_edgeDetection(gint32  refDrawableId
+  , gdouble edgeColordiffThreshold
+  , gint32 *countEdgePixels
+  )
+{
+  GapEdgeContext  edgeContext;
+  GapEdgeContext *ectx;
+  gdouble         edgeOpacityThreshold;
+
+  /* init context */  
+  ectx = &edgeContext;
+  ectx->refDrawable = gimp_drawable_get(refDrawableId);
+  ectx->edgeDrawable = NULL;
+  ectx->edgeColordiffThreshold = CLAMP(edgeColordiffThreshold, 0.0, 1.0);
+  edgeOpacityThreshold = CLAMP((edgeColordiffThreshold * 255), 0, 255);
+  ectx->edgeOpacityThreshold255 = edgeOpacityThreshold;
+  ectx->edgeDrawableId = -1;
+  ectx->countEdgePixels = 0;
+
+  if(gap_debug)
+  {
+    printf("gap_edgeDetection START edgeColordiffThreshold:%.5f refDrawableId:%d\n"
+       , (float)ectx->edgeColordiffThreshold
+       , (int)refDrawableId
+       );
+  }
+ 
+  if(ectx->refDrawable != NULL)
+  {
+    ectx->pftRef = gimp_pixel_fetcher_new (ectx->refDrawable, FALSE /* shadow */);
+    gimp_pixel_fetcher_set_edge_mode (ectx->pftRef, GIMP_PIXEL_FETCHER_EDGE_BLACK);
+
+    p_edgeDetection(ectx);
+
+    gimp_pixel_fetcher_destroy (ectx->pftRef);
+  }
+
+  if(ectx->refDrawable != NULL)
+  {
+    gimp_drawable_detach(ectx->refDrawable);
+    ectx->refDrawable = NULL;
+  }
+  if(ectx->edgeDrawable != NULL)
+  {
+    gimp_drawable_detach(ectx->edgeDrawable);
+    ectx->edgeDrawable = NULL;
+  }
+
+  *countEdgePixels = ectx->countEdgePixels;
+
+  if(gap_debug)
+  {
+    printf("gap_edgeDetection END resulting edgeDrawableId:%d countEdgePixels:%d\n"
+       , (int)ectx->edgeDrawableId
+       , (int)*countEdgePixels
+       );
+  }
+  
+  return (ectx->edgeDrawableId);
+  
+}  /* end gap_edgeDetection */
diff --git a/gap/gap_edge_detection.h b/gap/gap_edge_detection.h
new file mode 100644
index 0000000..3379f13
--- /dev/null
+++ b/gap/gap_edge_detection.h
@@ -0,0 +1,63 @@
+// TODOS: levels of non-black edge pixels are too dark (stretch range...)
+//        check colordiff (that seems not work properly on draft locate test image coords x:25 y:23  and x:25 y:24 !!!
+
+/* gap_edge_detection.h
+ *    by hof (Wolfgang Hofer)
+ *  2010/08/08
+ *
+ */
+/* 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:
+ * version 2.7.0;             hof: created
+ */
+
+#ifndef _GAP_EDGE_DETECTION_H
+#define _GAP_EDGE_DETECTION_H
+
+/* SYTEM (UNIX) includes */
+#include <stdio.h>
+#include <stdlib.h>
+
+/* GIMP includes */
+#include "gtk/gtk.h"
+#include "libgimp/gimp.h"
+
+
+
+/* ----------------------------------------
+ * gap_edgeDetection
+ * ----------------------------------------
+ *
+ * returns the drawable id of a newly created channel
+ * representing edges of the specified image.
+ *
+ * black pixels indicate areas of same or similar colors,
+ * white indicates sharp edges.
+ *
+ */
+gint32 gap_edgeDetection(gint32  refDrawableId
+  , gdouble edgeColordiffThreshold
+  , gint32 *countEdgePixels
+  );
+
+
+
+
+#endif
diff --git a/gap/gap_fmac_base.c b/gap/gap_fmac_base.c
index 09ed38a..d839cc1 100644
--- a/gap/gap_fmac_base.c
+++ b/gap/gap_fmac_base.c
@@ -277,7 +277,7 @@ p_build_fmac_list(const char *filtermacro_file, GimpRunMode run_mode)
       return NULL;
   }
 
-  /* process filtermacro file (scann line by line and add filtername
+  /* process filtermacro file (scan line by line and add filtername
    * and params to the fmac_root list) 
    */
   txf_ptr_root = gap_val_load_textfile(filtermacro_file);
@@ -380,7 +380,7 @@ p_merge_fmac_list(FMacElem *fmac_root, const char *filtermacro_file, GimpRunMode
   }
 
 
-  /* process filtermacro file (scann line by line and overwrite
+  /* process filtermacro file (scan line by line and overwrite
    * the 2nd parameter set where filtername matches
    */
   txf_ptr_root = gap_val_load_textfile(filtermacro_file);
diff --git a/gap/gap_frame_fetcher.c b/gap/gap_frame_fetcher.c
index 3d66a66..d43bc09 100644
--- a/gap/gap_frame_fetcher.c
+++ b/gap/gap_frame_fetcher.c
@@ -89,6 +89,11 @@
 #define GAP_FFETCH_MAX_GVC_CACHE_ELEMENTS 6
 #define GAP_FFETCH_GVA_FRAMES_TO_KEEP_CACHED 36
 
+#define GAP_FFETCH_MAX_IMG_CACHE_ELEMENTS_GIMPRC_KEY     "gap_ffetch_max_img_cache_elements"
+#define GAP_FFETCH_MAX_GVC_CACHE_ELEMENTS_GIMPRC_KEY     "gap_ffetch_max_gvc_cache_elements" 
+#define GAP_FFETCH_GVA_FRAMES_TO_KEEP_CACHED_GIMPRC_KEY  "gap_ffetch_gva_frames_to_keep_cached"
+
+
 /* the lists of cached images and duplicates are implemented via GIMP image parasites,
  * where images are simply loaded by GIMP without adding a display and marked with a non persistent parasite.
  * the GAP_IMAGE_CACHE_PARASITE holds the modification timestamp (mtime) and full filename (inclusive terminating 0)
@@ -137,6 +142,7 @@ static GapFFetchGvahandCache *global_gvcache = NULL;
 
 static GapFFetchResourceUserElem *global_rsource_users = NULL;
 
+
 /*************************************************************
  *         FRAME FETCHER procedures                          *
  *************************************************************
@@ -153,9 +159,114 @@ static t_GVA_Handle*  p_ffetch_get_open_gvahand(const char* filename, gint32 sel
 
 static void           p_add_image_to_list_of_duplicated_images(gint32 image_id, gint32 ffetch_user_id);
 
+static gint32 p_get_ffetch_max_img_cache_elements();
+static gint32 p_get_ffetch_max_gvc_cache_elements();
+static gint32 ffetch_gva_frames_to_keep_cached();
 
 
 /* ----------------------------------------------------
+ * p_get_ffetch_max_img_cache_elements
+ * ----------------------------------------------------
+ */
+static gint32
+p_get_ffetch_max_img_cache_elements()
+{
+  static gint32 value = -1;
+  static char *gimprc_key = GAP_FFETCH_MAX_IMG_CACHE_ELEMENTS_GIMPRC_KEY;
+  
+  if(value == -1)
+  {
+    if(gap_debug)
+    {
+      printf("get gimprc value for %s\n"
+        , gimprc_key
+        );
+    }
+    value = gap_base_get_gimprc_int_value (gimprc_key
+        , GAP_FFETCH_MAX_IMG_CACHE_ELEMENTS /* default_value*/
+        , 1                                 /* min_value */
+        , 2000                              /* max_value */
+        );
+  }
+  if(gap_debug)
+  {
+    printf("value for %s is:%d\n"
+        , gimprc_key
+        ,(int)value
+        );
+  }
+  return (value);
+}  /* end p_get_ffetch_max_img_cache_elements */
+
+/* ----------------------------------------------------
+ * p_get_ffetch_max_gvc_cache_elements
+ * ----------------------------------------------------
+ */
+static gint32
+p_get_ffetch_max_gvc_cache_elements()
+{
+  static gint32 value = -1;
+  static char *gimprc_key = GAP_FFETCH_MAX_GVC_CACHE_ELEMENTS_GIMPRC_KEY;
+  
+  if(value == -1)
+  {
+    if(gap_debug)
+    {
+      printf("get gimprc value for %s\n"
+        , gimprc_key
+        );
+    }
+    value = gap_base_get_gimprc_int_value (gimprc_key
+        , GAP_FFETCH_MAX_GVC_CACHE_ELEMENTS /* default_value*/
+        , 1                                 /* min_value */
+        , 80                              /* max_value */
+        );
+  }
+  if(gap_debug)
+  {
+    printf("value for %s is:%d\n"
+        , gimprc_key
+        ,(int)value
+        );
+  }
+  return (value);
+}  /* end p_get_ffetch_max_gvc_cache_elements */
+
+/* ----------------------------------------------------
+ * p_get_ffetch_gva_frames_to_keep_cached
+ * ----------------------------------------------------
+ */
+static gint32
+p_get_ffetch_gva_frames_to_keep_cached()
+{
+  static gint32 value = -1;
+  static char *gimprc_key = GAP_FFETCH_GVA_FRAMES_TO_KEEP_CACHED_GIMPRC_KEY;
+  
+  if(value == -1)
+  {
+    if(gap_debug)
+    {
+      printf("get gimprc value for %s\n"
+        , gimprc_key
+        );
+    }
+    value = gap_base_get_gimprc_int_value (gimprc_key
+        , GAP_FFETCH_GVA_FRAMES_TO_KEEP_CACHED /* default_value*/
+        , 1                                 /* min_value */
+        , 1000                              /* max_value */
+        );
+  }
+  if(gap_debug)
+  {
+    printf("value for %s is:%d\n"
+        , gimprc_key
+        ,(int)value
+        );
+  }
+  return (value);
+}  /* end p_get_ffetch_gva_frames_to_keep_cached */
+
+/* ----------------------------------------------------
  * p_load_cache_image
  * ----------------------------------------------------
  */
@@ -264,7 +375,7 @@ p_load_cache_image(const char* filename, gboolean addToCache)
     gchar  *parasite_filename_ptr;
     gint32  len_filename0;           /* filename length including the terminating 0 */
   
-    if (l_number_of_cached_images > GAP_FFETCH_MAX_IMG_CACHE_ELEMENTS)
+    if (l_number_of_cached_images > p_get_ffetch_max_img_cache_elements())
     {
       /* the image cache already has more elements than desired,
        * drop the 1st cached image
@@ -273,7 +384,7 @@ p_load_cache_image(const char* filename, gboolean addToCache)
       {
         printf("FrameFetcher: DELETE because cache is full: (image_id:%d)  name:%s number_of_cached_images:%d pid:%d\n"
               , (int)l_first_chached_image_id
-              , gimp_image_get_filename(images[l_idi])
+              , gimp_image_get_filename(l_first_chached_image_id)
               , (int)l_number_of_cached_images
               , (int)gap_base_getpid()
               );
@@ -473,7 +584,7 @@ p_ffetch_get_open_gvahand(const char* filename, gint32 seltrack, const char *pre
     /* init the global_image cache */
     global_gvcache = g_malloc0(sizeof(GapFFetchGvahandCache));
     global_gvcache->gvc_list = NULL;
-    global_gvcache->max_vid_cache = GAP_FFETCH_MAX_GVC_CACHE_ELEMENTS;
+    global_gvcache->max_vid_cache = p_get_ffetch_max_gvc_cache_elements();
   }
 
   gvcache = global_gvcache;
@@ -510,7 +621,7 @@ p_ffetch_get_open_gvahand(const char* filename, gint32 seltrack, const char *pre
   
   if(l_gvahand)
   {
-    GVA_set_fcache_size(l_gvahand, GAP_FFETCH_GVA_FRAMES_TO_KEEP_CACHED);
+    GVA_set_fcache_size(l_gvahand, p_get_ffetch_gva_frames_to_keep_cached());
 
     gvc_new = g_malloc0(sizeof(GapFFetchGvahandCacheElem));
     gvc_new->filename = g_strdup(filename);
@@ -676,6 +787,8 @@ gap_frame_fetch_dup_image(gint32 ffetch_user_id
   if (stackpos < 0)
   {
     dup_image_id = gimp_image_duplicate(image_id);
+
+    gap_frame_fetch_remove_parasite(dup_image_id);
     resulting_layer = gap_image_merge_visible_layers(dup_image_id, GIMP_CLIP_TO_IMAGE);
   }
   else
@@ -779,7 +892,7 @@ gap_frame_fetch_dup_video(gint32 ffetch_user_id
     /* if no success, we try explicite read that frame  */
     if(gvahand->current_seek_nr != framenr)
     {
-      if(((gvahand->current_seek_nr + GAP_FFETCH_GVA_FRAMES_TO_KEEP_CACHED) > framenr)
+      if(((gvahand->current_seek_nr + p_get_ffetch_gva_frames_to_keep_cached()) > framenr)
       &&  (gvahand->current_seek_nr < framenr ) )
       {
         /* near forward seek is performed by dummyreads to fill up the 
@@ -949,3 +1062,30 @@ gap_frame_fetch_unregister_user(gint32 ffetch_user_id)
   }
 
 }  /* end gap_frame_fetch_unregister_user */
+
+
+/* -------------------------------
+ * gap_frame_fetch_remove_parasite
+ * -------------------------------
+ * removes the image parasite that marks the image as member
+ * of the gap frame fetcher cache.
+ */
+void
+gap_frame_fetch_remove_parasite(gint32 image_id)
+{
+  GimpParasite  *l_parasite;
+ 
+  l_parasite = gimp_image_parasite_find(image_id, GAP_IMAGE_CACHE_PARASITE);
+
+  if(l_parasite)
+  {
+    gimp_image_parasite_detach(image_id, GAP_IMAGE_CACHE_PARASITE);
+    if(gap_debug)
+    {
+      printf("FrameFetcher: removed parasite from (image_id:%d) pid:%d\n"
+        , (int)image_id, (int)gap_base_getpid());
+    }
+    gimp_parasite_free(l_parasite);
+  }
+
+}  /* end gap_frame_fetch_remove_parasite */
diff --git a/gap/gap_frame_fetcher.h b/gap/gap_frame_fetcher.h
index 8fce9b7..2c7c553 100644
--- a/gap/gap_frame_fetcher.h
+++ b/gap/gap_frame_fetcher.h
@@ -92,6 +92,9 @@ gap_frame_fetch_delete_list_of_duplicated_images(gint32 ffetch_user_id);
  * ----------------------------
  * returns image_id of the original cached image.
  *    RESTRICTION: the Caller must NOT not modify that image and shall not open a display for it!
+ *    In case this image is duplicated, the parasite that marks an image as member of the gap frame fetcher cache
+ *    must be removed (by calling procedure gap_frame_fetch_remove_parasite on the duplicate)
+ *    otherwise the duplicate might be unexpectedly deleted  when the frame fetcher cache is full.
  */
 gint32
 gap_frame_fetch_orig_image(gint32 ffetch_user_id
@@ -133,5 +136,13 @@ gap_frame_fetch_dup_video(gint32 ffetch_user_id
     );
 
 
+/* -------------------------------
+ * gap_frame_fetch_remove_parasite
+ * -------------------------------
+ * removes the image parasite that marks the image as member
+ * of the gap frame fetcher cache.
+ */
+void
+gap_frame_fetch_remove_parasite(gint32 image_id);
 
 #endif
diff --git a/gap/gap_layer_copy.c b/gap/gap_layer_copy.c
index 7567449..f8a2fe4 100644
--- a/gap/gap_layer_copy.c
+++ b/gap/gap_layer_copy.c
@@ -847,3 +847,41 @@ gap_layer_create_layer_from_alpha(gint32 src_layer_id, gint32 image_id
 
 }  /* end gap_layer_create_layer_from_alpha  */
 
+/* ---------------------------------
+ * gap_layer_find_by_name
+ * ---------------------------------
+ * return -1 if the specified image has no layer with the specified name
+ */
+gint32
+gap_layer_find_by_name(gint32 image_id, const char *name)
+{
+  gint          l_nlayers;
+  gint32       *l_layers_list;
+  gint32        l_layer_id;
+
+  l_layer_id = -1;
+  l_layers_list = gimp_image_get_layers(image_id, &l_nlayers);
+  if(l_layers_list != NULL)
+  {
+    gint ii;
+    for(ii=0; ii < l_nlayers; ii++)
+    {
+      char *layername;
+      gboolean isEqual;
+
+      layername = gimp_drawable_get_name(l_layers_list[ii]);
+      isEqual = (strcmp(layername, name) == 0);
+      g_free(layername);
+      
+      if (isEqual)
+      {
+        l_layer_id = l_layers_list[ii];
+        break;
+      }
+    }
+    g_free (l_layers_list);
+  }
+
+  return (l_layer_id);
+
+}  /* end gap_layer_find_by_name */
diff --git a/gap/gap_layer_copy.h b/gap/gap_layer_copy.h
index de2318d..3e04bf2 100644
--- a/gap/gap_layer_copy.h
+++ b/gap/gap_layer_copy.h
@@ -93,4 +93,6 @@ gint32  gap_layer_create_layer_from_alpha(gint32 src_layer_id, gint32 image_id
                                , const char *name_prefix, const char *name_suffix
                                , gboolean applyExistingLayermask, gboolean useTransferAlpha);
 
+gint32  gap_layer_find_by_name(gint32 image_id, const char *name);
+
 #endif
diff --git a/gap/gap_lib.c b/gap/gap_lib.c
index 76fe9df..04fcf9d 100644
--- a/gap/gap_lib.c
+++ b/gap/gap_lib.c
@@ -528,7 +528,7 @@ p_do_active_layer_tracking(gint32 image_id
 /* ============================================================================
  * gap_lib_file_exists
  *
- * return 0  ... file does not exist, or is not accessable by user,
+ * return 0  ... file does not exist, or is not accessible by user,
  *               or is empty,
  *               or is not a regular file
  *        1  ... file exists
@@ -883,6 +883,39 @@ gap_lib_alloc_extension(const char *imagename)
   return(l_ext);
 }
 
+/* -------------------------------------
+ * gap_lib_build_basename_without_ext
+ * -------------------------------------
+ * return a duplicate of the basename part of the specified filename without extension.
+ *        leading directory path and drive letter (for WinOS) is cut off
+ * the caller is responsible to g_free the returned string.
+ */
+char *
+gap_lib_build_basename_without_ext(const char *filename)
+{
+  char *basename;
+  gint  idx;
+  
+  basename = g_filename_display_basename(filename);
+  idx = strlen(basename) -1;
+  while(idx > 0)
+  {
+    if(basename[idx] == '.')
+    {
+      basename[idx] = '\0';
+      break;
+    }
+    if((basename[idx] == G_DIR_SEPARATOR) || (basename[idx] == DIR_ROOT))
+    {
+      break;    /* dont run into dir part */
+    }
+
+    idx--;
+  }
+  return(basename);
+}  /* end gap_lib_build_basename_without_ext */
+
+
 
 /* ----------------------------------
  * gap_lib_alloc_fname_fixed_digits
@@ -2611,14 +2644,14 @@ gap_lib_load_named_frame (gint32 old_image_id, char *lod_name)
   
   if (gap_pdb_gimp_displays_reconnect(old_image_id, l_new_image_id))
   {
-      /* deleteing the old image if it is still alive
+      /* deleting the old image if it is still alive
        * (maybe still required gimp-2.2.x prior to gimp-2.2.11
        * gimp-2.2.11 still accepts the delete, but the old image becomes invalid after
        * reconnecting the display.
        * gimp-2.3.8 even complains and breaks gimp-gap if we attempt to delete
        * the old image. (see #339840)
-       * GAP has no more chance for explicite delete the old image
-       * (hope that this is already done implicite by gimp_reconnect_displays ?)
+       * GAP has no more chance of explicitly deleting the old image
+       * (hope that this is already done implicitly by gimp_reconnect_displays ?)
        */
        
       if(gap_image_is_alive(old_image_id))
diff --git a/gap/gap_lib.h b/gap/gap_lib.h
index d92d093..8666384 100644
--- a/gap/gap_lib.h
+++ b/gap/gap_lib.h
@@ -66,6 +66,7 @@ GapAnimInfo* gap_lib_alloc_ainfo(gint32 image_id, GimpRunMode run_mode);
 int          gap_lib_dir_ainfo(GapAnimInfo *ainfo_ptr);
 int          gap_lib_chk_framerange(GapAnimInfo *ainfo_ptr);
 int          gap_lib_chk_framechange(GapAnimInfo *ainfo_ptr);
+char*        gap_lib_build_basename_without_ext(const char *filename);
 
 int    gap_lib_save_named_frame (gint32 image_id, char *sav_name);
 int    gap_lib_load_named_frame (gint32 image_id, char *lod_name);
diff --git a/gap/gap_libgimpgap.h b/gap/gap_libgimpgap.h
index 74c2121..acf7882 100644
--- a/gap/gap_libgimpgap.h
+++ b/gap/gap_libgimpgap.h
@@ -36,6 +36,11 @@
 
 #include "gap-intl.h"
 #include "gap_arr_dialog.h"
+#include "gap_colordiff.h"
+#include "gap_locate.h"
+#include "gap_edge_detection.h"
+#include "gap_colormask_file.h"
+#include "gap_colormask_exec.h"
 #include "gap_image.h"
 #include "gap_layer_copy.h"
 #include "gap_lib.h"
diff --git a/gap/gap_locate.c b/gap/gap_locate.c
new file mode 100644
index 0000000..7694dfa
--- /dev/null
+++ b/gap/gap_locate.c
@@ -0,0 +1,843 @@
+/* gap_locate.c
+ *    by hof (Wolfgang Hofer)
+ *  2010/08/06
+ *
+ */
+/* 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:
+ * version 2.7.0;             hof: created
+ */
+
+/* SYTEM (UNIX) includes */
+#include "string.h"
+/* GIMP includes */
+#include "gtk/gtk.h"
+#include "libgimp/gimp.h"
+
+/* GAP includes */
+#include "gap_lib_common_defs.h"
+#include "gap_locate.h"
+#include "gap_colordiff.h"
+#include "gap_lib.h"
+#include "gap_image.h"
+#include "gap_layer_copy.h"
+#include "gap_libgapbase.h"
+
+#define MAX_SHAPE_TABLE_SIZE (1 + (GAP_LOCATE_MAX_REF_SHAPE_RADIUS * 2))
+#define OPACITY_LEVEL_UCHAR 50
+#define COLORDIFF_BREAK_THRESHOLD_FACTOR 1.1
+
+extern      int gap_debug; /* ==0  ... dont print debug infos */
+
+typedef struct GapLocateColor {
+  guchar rgba[4];   /* the color channels */
+  } GapLocateColor;
+
+
+typedef struct GapLocateContext { /* nickname: lctx */
+     GimpDrawable *refDrawable;
+     GimpDrawable *targetDrawable;
+     gint32        refX;
+     gint32        refY;
+     gint32        refShapeRadius;
+     gint32        targetMoveRadius;
+     gint32        targetX;
+     gint32        targetY;
+     gdouble       minColordiff;
+     gdouble       breakAtColordiff;
+     gdouble       refPixelColordiffThreshold;
+     gdouble       edgeColordiffThreshold;
+     gdouble       tmpColordiffBreakThreshold;  /* cancel area compare if average colordiff goes above this threshold */
+     
+
+     /* common clip rectangle offset in the tareget drawable */
+     gint32        targetOffX;
+     gint32        targetOffY;
+
+     /* transformation offsets to convert target coordinates (tx/ty) to indexes for refColorTab */
+     gint32        transformOffX;
+     gint32        transformOffY;
+   
+     /* common clip rectangle sizes */
+     gint32        commonAreaWidth;  
+     gint32        commonAreaHeight;
+
+     gdouble       commonAreaSumColordiff;
+     gint32        commonAreaComparedPixelCount;
+
+     GimpPixelFetcher *pftRef;
+     GimpPixelFetcher *pftTarget;
+     GapLocateColor   *refColorPtr;
+     GapLocateColor    refColorTab[MAX_SHAPE_TABLE_SIZE][MAX_SHAPE_TABLE_SIZE];
+
+     gint32            pickedTryNumber;
+     gint32            pickedPixelCount;
+     gint32            countTries;
+     gint32            countAreaCompares;
+     
+     gboolean          cancelAttemptFlag;
+
+  } GapLocateContext;
+
+
+
+
+/* ---------------------------------
+ * p_debugPrintReferenceTable
+ * ---------------------------------
+ */
+static void
+p_debugPrintReferenceTable(GapLocateContext *lctx)
+{
+  gint32   rx;
+  gint32   ry;
+  gint32   tabSizeUsed;
+  
+  tabSizeUsed = 1 +(2 * lctx->refShapeRadius);
+  printf("\nopacity of refColorTab (usedSize)\n+");
+  for(rx = 0; rx < tabSizeUsed; rx++)
+  {
+    if(rx == lctx->refShapeRadius)
+    {
+      printf("+");
+    }
+    else
+    {
+      printf("-");
+    }
+  }
+  printf("\n");
+  
+  
+  for(ry = 0; ry < tabSizeUsed; ry++)
+  {
+    if(ry == lctx->refShapeRadius)
+    {
+      printf("+");
+    }
+    else
+    {
+      printf("|");
+    }
+    for(rx = 0; rx < tabSizeUsed; rx++)
+    {
+      GapLocateColor *currColorPtr;
+      currColorPtr = &lctx->refColorTab[rx][ry];
+    
+      if(currColorPtr->rgba[3] < OPACITY_LEVEL_UCHAR)
+      {
+        printf(".");
+      }
+      else
+      {
+        if((ry == lctx->refShapeRadius) && (rx == lctx->refShapeRadius))
+        {
+          printf("o");
+        }
+        else
+        {
+          printf("x");
+        }
+      }
+      
+    }
+    printf("\n");
+  }
+}  /* end p_debugPrintReferenceTable */
+
+/* ---------------------------------
+ * p_GapLocateColor_to_GimpHsv
+ * ---------------------------------
+ * check if the reference shape has more than one single color
+ * and has sufficient number of non transparent pixels
+ * (otherwise area compare loops have no chance to deliver
+ *  acceptable locating results)
+ */
+static void
+p_GapLocateColor_to_GimpHsv(GapLocateColor *colorPtr, GimpHSV *hsvPtr)
+{
+  GimpRGB rgb;
+  
+  gimp_rgba_set_uchar (&rgb, colorPtr->rgba[0], colorPtr->rgba[1], colorPtr->rgba[2], 255);
+  gimp_rgb_to_hsv(&rgb, hsvPtr);
+
+}
+
+
+/* ---------------------------------
+ * p_trimReferenceShape
+ * ---------------------------------
+ * check if the reference shape has more than one single color
+ * and has sufficient number of non transparent pixels
+ * (otherwise area compare loops have no chance to deliver
+ *  acceptable locating results)
+ */
+static void
+p_trimReferenceShape(GapLocateContext *lctx)
+{
+  GimpHSV refHsv;
+  gint32 tabSizeUsed;
+  gint32 rx;
+  gint32 ry;
+  gboolean isRefPixelColor;
+  
+  p_GapLocateColor_to_GimpHsv(lctx->refColorPtr, &refHsv);
+  isRefPixelColor = ((refHsv.s >= 0.25) && (refHsv.v >= 0.1));
+  
+  tabSizeUsed = 1 +(2 * lctx->refShapeRadius);
+  for(ry = 0; ry < tabSizeUsed; ry++)
+  {
+    for(rx = 0; rx < tabSizeUsed; rx++)
+    {
+      GimpHSV currentHsv;
+      GapLocateColor *currColorPtr;
+      gdouble vDif;
+      
+      currColorPtr = &lctx->refColorTab[rx][ry];
+      p_GapLocateColor_to_GimpHsv(currColorPtr, &currentHsv);
+
+      vDif = fabs(refHsv.v - currentHsv.v);
+      
+      if(isRefPixelColor)
+      {
+        gdouble hDif;
+        gdouble sDif;
+
+        hDif = fabs(refHsv.h - currentHsv.h);
+        /* normalize hue difference.
+         * hue values represents an angle
+         * where value 0.5 equals 180 degree
+         * and value 1.0 stands for 360 degree that is
+         * equal to 0.0
+         * Hue is maximal different at 180 degree.
+         *
+         * after normalizing, the difference
+         * hDiff value 1.0 represents angle difference of 180 degree
+         */
+        if(hDif > 0.5)
+        {
+          hDif = (1.0 - hDif) * 2.0;
+        }
+        else
+        {
+          hDif = hDif * 2.0;
+        }
+
+        if((hDif > 0.5) || (vDif > 0.6))
+        {
+          currColorPtr->rgba[3] = 0;
+        }
+        
+      }
+      else
+      {
+        if(vDif > 0.5)
+        {
+          currColorPtr->rgba[3] = 0;
+        }
+      }
+
+
+    }
+  }
+}  /* end p_trimReferenceShape */
+
+
+/* ---------------------------------
+ * p_initReferenceTable
+ * ---------------------------------
+ * init the reference table refColorTab that represents
+ * the reference shape as array of colors with alpha channel.
+ *
+ * the used size of the reference table depends on the refShapeRadius,
+ * but not on the reference coordinates lctx->refX/Y
+ * if the reference coordinate is near the edges some pixels that refere
+ * to coordinates outside the reference image boundaries will be initialized as transparent
+ */
+static void
+p_initReferenceTable(GapLocateContext *lctx)
+{
+  gint32 tabSizeUsed;
+  gint32 rx;
+  gint32 ry;
+  gint32 refOffX;
+  gint32 refOffY;
+  
+  tabSizeUsed = 1 +(2 * lctx->refShapeRadius);
+  refOffX = lctx->refX - lctx->refShapeRadius;
+  refOffY = lctx->refY - lctx->refShapeRadius;
+ 
+  for(ry = 0; ry < tabSizeUsed; ry++)
+  {
+    for(rx = 0; rx < tabSizeUsed; rx++)
+    {
+      GapLocateColor *currColorPtr;
+      gint32          xx;
+      gint32          yy;
+      
+      currColorPtr = &lctx->refColorTab[rx][ry];
+      xx = refOffX + rx;
+      yy = refOffY + ry;
+
+      if ((xx < 0) || (xx >= lctx->refDrawable->width)
+      ||  (yy < 0) || (yy >= lctx->refDrawable->height))
+      {
+        /* use full transparent black pixel for coordinates outside boundaries */
+        currColorPtr->rgba[0] = 0;
+        currColorPtr->rgba[1] = 0;
+        currColorPtr->rgba[2] = 0;
+        currColorPtr->rgba[3] = 0;
+      }
+      else
+      {
+        gimp_pixel_fetcher_get_pixel (lctx->pftRef
+                        , refOffX + rx
+                        , refOffY + ry
+                        , &currColorPtr->rgba[0]
+                        );
+      }
+      
+    }
+  }
+
+  p_trimReferenceShape(lctx);
+  
+}  /* end p_initReferenceTable */
+   
+
+
+
+
+/* ---------------------------------
+ * p_compareAreaRegion
+ * ---------------------------------
+ * calculate summaryColordiff for all opaque pixels
+ * in the compared area region.
+ */
+static void
+p_compareAreaRegion (const GimpPixelRgn *targetPR
+                    ,GapLocateContext *lctx)
+{
+  guint    row;
+  guchar* target = targetPR->data;   /* the target drawable */
+
+  if(lctx->cancelAttemptFlag == TRUE)
+  {
+    return;
+  }
+  for (row = 0; row < targetPR->h; row++)
+  {
+    guint  col;
+    guint  idxtarget;
+
+    idxtarget = 0;
+    for(col = 0; col < targetPR->w; col++)
+    {
+      GapLocateColor *referenceColorPtr;
+      gint32   rx; 
+      gint32   ry; 
+      gint32   tx; 
+      gint32   ty; 
+      
+      if(targetPR->bpp > 3)
+      {
+        if(target[idxtarget +3] < OPACITY_LEVEL_UCHAR)
+        {
+          /* transparent target pixel is not compared */
+          idxtarget += targetPR->bpp;
+          continue;
+        }
+      }
+      
+      tx = targetPR->x + col;
+      ty = targetPR->y + row;
+
+      rx = CLAMP(tx - lctx->transformOffX, 0, MAX_SHAPE_TABLE_SIZE -1);
+      ry = CLAMP(ty - lctx->transformOffY, 0, MAX_SHAPE_TABLE_SIZE -1);
+
+      referenceColorPtr = &lctx->refColorTab[rx][ry];
+      
+      if(referenceColorPtr->rgba[3] >= OPACITY_LEVEL_UCHAR)
+      {
+        gdouble colordiff;
+        
+        colordiff = gap_colordiff_simple_guchar(&referenceColorPtr->rgba[0]
+                  , &target[idxtarget +0]
+                  , FALSE                    /*gboolean debugPrint*/
+                  );
+        lctx->commonAreaComparedPixelCount += 1;
+        lctx->commonAreaSumColordiff += colordiff;
+        
+        
+
+        if(gap_debug)
+        {
+          printf("try:(%d)  pixel at ref: %d/%d target:%d/%d   colordiff:%.5f\n"
+              , (int)lctx->countTries
+              , (int)rx
+              , (int)ry
+              , (int)tx
+              , (int)ty
+              , (float)colordiff
+              );
+        }
+        
+        if(((lctx->commonAreaComparedPixelCount >> 3) & 7) == 7)
+        {
+          gdouble tmpAvgColordiff;
+          
+          tmpAvgColordiff = lctx->commonAreaSumColordiff / (gdouble)lctx->commonAreaComparedPixelCount;
+          if(tmpAvgColordiff > lctx->tmpColordiffBreakThreshold)
+          {
+            lctx->cancelAttemptFlag = TRUE;
+            if(gap_debug)
+            {
+              printf("try:(%d)  cancelAttemptFlag set TRUE\n"
+                , (int)lctx->countTries
+                );
+            }
+            return;
+          }
+        }
+
+
+      }
+
+      idxtarget += targetPR->bpp;
+    }
+
+    target += targetPR->rowstride;
+
+  }
+
+}  /* end p_compareAreaRegion */
+
+
+
+
+/* ----------------------------------------
+ * p_calculateCommonAreaClipping
+ * ----------------------------------------
+ * check clipping and calculate commonAreaWidth and Height
+ * and offsets in target drawable 
+ *
+ *  <--- leftRadius --->o<------- rightRadius ------->
+ *
+ */
+static void 
+p_calculateCommonAreaClipping(GapLocateContext *lctx, gint32 offX, gint32 offY
+    , gint32  targetX, gint32  targetY
+    )
+{
+  gint32  commonWidth;
+  gint32  commonHeight;
+  gint32  leftRefRadius;
+  gint32  topRefRadius;
+  gint32  rightRefRadius;
+  gint32  botRefRadius;
+  gint32  leftTargetX;
+  gint32  topTargetY;
+  gint32  rightTargetX;
+  gint32  botTargetY;
+  gint32  leftTargetRadius;
+  gint32  topTargetRadius;
+  gint32  rightTargetRadius;
+  gint32  botTargetRadius;
+
+  commonWidth = MIN(lctx->refDrawable->width, lctx->targetDrawable->width);
+  commonHeight = MIN(lctx->refDrawable->height, lctx->targetDrawable->height);
+
+  leftRefRadius = lctx->refShapeRadius;
+  topRefRadius = lctx->refShapeRadius;
+
+  rightRefRadius = lctx->refShapeRadius;
+  botRefRadius = lctx->refShapeRadius;
+
+
+ 
+  leftTargetX = CLAMP((targetX - leftRefRadius), 0, commonWidth -1);
+  topTargetY = CLAMP((targetY - topRefRadius), 0, commonHeight -1);
+
+  rightTargetX = CLAMP((targetX + rightRefRadius), 0, commonWidth -1);
+  botTargetY = CLAMP((targetY + botRefRadius), 0, commonHeight -1);
+
+  leftTargetRadius = targetX - leftTargetX;
+  topTargetRadius = targetY - topTargetY;
+
+  rightTargetRadius = rightTargetX - targetX;
+  botTargetRadius = botTargetY - targetY;
+
+  
+  lctx->targetOffX = leftTargetX;
+  lctx->targetOffY = topTargetY;
+  lctx->commonAreaWidth = 1 + leftTargetRadius + rightTargetRadius;
+  lctx->commonAreaHeight = 1 + topTargetRadius + botTargetRadius;
+
+  lctx->transformOffX = targetX - lctx->refShapeRadius;
+  lctx->transformOffY = targetY - lctx->refShapeRadius;
+
+  if(gap_debug)
+  {
+    printf("refX/Y: %d / %d  offX/Y: %d / %d  targetX/Y %d / %d  leftTargetX:%d topTargetY:%d rightTargetX:%d botTargetY:%d"
+           " leftTargetRadius:%d topTargetRadius:%d rightTargetRadius:%d botTargetRadius %d\n"
+      , (int)  lctx->refX
+      , (int)  lctx->refY
+      , (int)  offX
+      , (int)  offY
+      , (int)  targetX
+      , (int)  targetY
+      , (int)  leftTargetX
+      , (int)  topTargetY
+      , (int)  rightTargetX
+      , (int)  botTargetY
+      , (int)  leftTargetRadius
+      , (int)  topTargetRadius
+      , (int)  rightTargetRadius
+      , (int)  botTargetRadius
+      );
+  
+    printf("transformOffX:%d transformOffY:%d targetOffX/Y: %d / %d commonArea W/H: %d x %d\n"
+      , (int)lctx->transformOffX
+      , (int)lctx->transformOffY
+      , (int)lctx->targetOffX
+      , (int)lctx->targetOffY
+      , (int)lctx->commonAreaWidth
+      , (int)lctx->commonAreaHeight
+      );
+  }
+
+}  /* end p_calculateCommonAreaClipping */
+
+
+/* ----------------------------------------
+ * p_compareArea
+ * ----------------------------------------
+ *
+ *  +---+---+
+ *  |   |   |
+ *  | ..|...|
+ *  | ..|...|
+ *  +---o---+  ----------
+ *  | ..|...|    ^
+ *  | ..|...|    |  refShapeRadius
+ *  | ..|...|    v
+ *  +---+---+  -----------
+ *
+ *
+ */
+static void p_compareArea(GapLocateContext *lctx, gint32 offX, gint32 offY)
+{
+#define COMMON_AREA_MIN_EDGE_LENGTH 4
+#define COMMON_AREA_MIN_PIXELS_COMPARED 12
+
+  GimpPixelRgn targetPR;
+  gpointer  pr;
+  gint32 targetX;
+  gint32 targetY;
+  guchar          pixel[4];
+  gdouble         colordiff;
+
+  lctx->countTries++;
+
+  targetX = lctx->refX + offX;
+  if(targetX < 0)
+  {
+    return;
+  }
+  if(targetX >  lctx->targetDrawable->width -1)
+  {
+    return;
+  }
+  targetY = lctx->refY + offY;
+  if(targetY < 0)
+  {
+    return;
+  }
+  if(targetY > lctx->targetDrawable->height -1)
+  {
+    return;
+  }
+  gimp_pixel_fetcher_get_pixel (lctx->pftTarget, targetX, targetY, &pixel[0]);
+  colordiff = gap_colordiff_simple_guchar(&lctx->refColorPtr->rgba[0]
+                  , &pixel[0]
+                  , FALSE                    /*gboolean debugPrint*/
+                  );
+  if(colordiff > lctx->refPixelColordiffThreshold)
+  {
+    /* the coresponding pixel in the target drawable at 
+     * current offsets differs more than tolerated threshold.
+     * in this case skip full area comare for performance reasons
+     */
+    if(gap_debug)
+    {
+      printf("p_compareArea ref pixel too much Different, SKIP AREA compare at offsets offX:%d offY:%d refPixelColordiffThreshold:%.5f colordiff:%.5f\n"
+         ,(int)offX
+         ,(int)offY
+         ,(float)lctx->refPixelColordiffThreshold
+         ,(float)colordiff
+         );
+    }
+    return;
+  }
+  
+  
+  p_calculateCommonAreaClipping(lctx, offX, offY, targetX, targetY);
+  
+  if((lctx->commonAreaWidth < COMMON_AREA_MIN_EDGE_LENGTH)
+  || (lctx->commonAreaHeight < COMMON_AREA_MIN_EDGE_LENGTH))
+  {
+    /* common area is too small to locate image details
+     * (comparing very few or single pixels does not make sense
+     *  it would indicate matching area at unexpected coordinates)
+     * 
+     */
+    if(gap_debug)
+    {
+      printf("p_compareArea is too small at offsets offX:%d offY:%d\n"
+         ,(int)offX
+         ,(int)offY
+         );
+    }
+    return;
+  }
+
+
+  lctx->commonAreaSumColordiff = 0.0;
+  lctx->commonAreaComparedPixelCount = 0;
+  lctx->cancelAttemptFlag = FALSE;
+
+  gimp_pixel_rgn_init (&targetPR, lctx->targetDrawable, lctx->targetOffX, lctx->targetOffY
+                      , lctx->commonAreaWidth, lctx->commonAreaHeight
+                      , FALSE     /* dirty */
+                      , FALSE     /* shadow */
+                       );
+
+  /* compare pixel areas in tiled portions via pixel region processing loops.
+   */
+  for (pr = gimp_pixel_rgns_register (1, &targetPR);
+       pr != NULL;
+       pr = gimp_pixel_rgns_process (pr))
+  {
+    p_compareAreaRegion (&targetPR, lctx);
+  }
+
+  lctx->countAreaCompares++;
+
+  if ((lctx->commonAreaComparedPixelCount >= COMMON_AREA_MIN_PIXELS_COMPARED)
+  &&  (lctx->cancelAttemptFlag != TRUE))
+  {
+    gdouble commonAreaAvgColordiff;
+    
+    commonAreaAvgColordiff = lctx->commonAreaSumColordiff / (gdouble)lctx->commonAreaComparedPixelCount;
+
+    if(gap_debug)
+    {
+      printf("p_compareArea try:(%d) done at offsets offX:%d offY:%d  sum:%.5f, count:%.0f commonAreaAvgColordiff:%.5f\n"
+         ,(int)lctx->countTries
+         ,(int)offX
+         ,(int)offY
+         ,(float)lctx->commonAreaSumColordiff
+         ,(float)lctx->commonAreaComparedPixelCount
+         ,(float)commonAreaAvgColordiff
+         );
+    }
+
+    
+    if ((commonAreaAvgColordiff < lctx->minColordiff)
+    ||  ((commonAreaAvgColordiff == lctx->minColordiff) && (lctx->commonAreaComparedPixelCount > lctx->pickedPixelCount)))
+    {
+      /* found best matching common area (so far) at current offests
+       * set minColordiff and result targertX/Y coordinates.
+       * (processing may continue to find better matching result
+       *  at other offsets later on..)
+       */
+      lctx->minColordiff = commonAreaAvgColordiff;
+      lctx->tmpColordiffBreakThreshold = lctx->minColordiff * COLORDIFF_BREAK_THRESHOLD_FACTOR;
+      lctx->targetX = targetX;
+      lctx->targetY = targetY;
+      lctx->pickedTryNumber = lctx->countTries;
+      lctx->pickedPixelCount = lctx->commonAreaComparedPixelCount;
+      
+    }
+
+  }
+
+}  /* end p_compareArea */
+
+
+
+/* ----------------------------------------
+ * p_locateDetailLoop
+ * ----------------------------------------
+ * calls area comarison by varying offsets within targetMoveRadius
+ * starting at offsets 0/0 to find the offsets where reference matches best with target.
+ * break the loop at first perfect match (where minColordiff <= breakAtColordiff level)
+ */
+static void p_locateDetailLoop(GapLocateContext *lctx)
+{
+  gint32 offX;
+  gint32 offY;
+  
+  for(offX = 0; offX < lctx->targetMoveRadius; offX++)
+  {
+    for(offY = 0; offY < lctx->targetMoveRadius; offY++)
+    {
+      p_compareArea(lctx, offX, offY);
+      if(lctx->minColordiff <= lctx->breakAtColordiff)
+      {
+        return;
+      }
+
+      if(offX != 0)
+      {
+        p_compareArea(lctx, 0 - offX, offY);
+        if(lctx->minColordiff <= lctx->breakAtColordiff)
+        {
+          return;
+        }
+        if(offY != 0)
+        {
+          p_compareArea(lctx, 0 - offX, 0 - offY);
+          if(lctx->minColordiff <= lctx->breakAtColordiff)
+          {
+            return;
+          }
+        }
+      }
+
+      if(offY != 0)
+      {
+        p_compareArea(lctx, offX, 0 - offY);
+        if(lctx->minColordiff <= lctx->breakAtColordiff)
+        {
+          return;
+        }
+      }
+
+    }
+  }
+}  /* end p_locateDetailLoop */
+
+
+
+/* ----------------------------------------
+ * gap_locateDetailWithinRadius
+ * ----------------------------------------
+ *
+ * the locateDetailWithinRadius procedure takes a referenced detail
+ * specified via refX/Y coordinate, refShapeRadius within a
+ * reference drawable
+ * and tries to locate the same (or similar) detail coordinates
+ * in the target Drawable.
+ *
+ * This is done by comparing pixel areas at refShapeRadius
+ * in the corresponding target Drawable in a loop while
+ * varying offsets within targetMoveRadius.
+ * the targetX/Y koords are picked at those offsets where the compared areas
+ * are best matching (e.g with minimun color difference)
+ * the return value is the minimum colordifference value
+ * (in range 0.0 to 1.0 where 0.0 indicates that the compared area is exactly equal)
+ */
+gdouble gap_locateDetailWithinRadius(gint32  refDrawableId
+  , gint32  refX
+  , gint32  refY
+  , gint32  refShapeRadius
+  , gint32  targetDrawableId
+  , gint32  targetMoveRadius
+  , gdouble requiredColordiffThreshold
+  , gint32  *targetX
+  , gint32  *targetY
+  )
+{
+  GapLocateContext  locateContext;
+  GapLocateContext *lctx;
+
+  /* init context */  
+  lctx = &locateContext;
+  lctx->refDrawable = NULL;
+  lctx->targetDrawable = NULL;
+  lctx->refX = refX;
+  lctx->refY = refY;
+  lctx->refShapeRadius = CLAMP(refShapeRadius, 1, GAP_LOCATE_MAX_REF_SHAPE_RADIUS);
+  lctx->targetMoveRadius = targetMoveRadius;
+  lctx->targetX = 0;
+  lctx->targetY = 0;
+  lctx->minColordiff = 1.0;
+  lctx->tmpColordiffBreakThreshold = CLAMP(requiredColordiffThreshold, 0.0, 1.0) * COLORDIFF_BREAK_THRESHOLD_FACTOR;
+  lctx->breakAtColordiff = 0.0;
+  lctx->refPixelColordiffThreshold = 0.11;
+  lctx->edgeColordiffThreshold = 0.14;
+  lctx->countTries = 0;
+  lctx->countAreaCompares = 0;
+  lctx->pickedPixelCount = 0;
+
+  lctx->refDrawable = gimp_drawable_get(refDrawableId);
+  lctx->targetDrawable = gimp_drawable_get(targetDrawableId);
+
+  if((lctx->refDrawable != NULL) 
+  && (lctx->targetDrawable != NULL))
+  {
+    /* create and init pixelfetchers (deliver black transparent pixel outside boundaries) */
+    lctx->pftRef = gimp_pixel_fetcher_new (lctx->refDrawable, FALSE /* shadow */);
+    lctx->pftTarget = gimp_pixel_fetcher_new (lctx->targetDrawable, FALSE /* shadow */);
+    gimp_pixel_fetcher_set_edge_mode (lctx->pftRef, GIMP_PIXEL_FETCHER_EDGE_NONE);
+    gimp_pixel_fetcher_set_edge_mode (lctx->pftTarget, GIMP_PIXEL_FETCHER_EDGE_BLACK);
+
+    /* init reference pixel color (in center of used area in refColorTab) */
+    lctx->refColorPtr = &lctx->refColorTab[lctx->refShapeRadius][lctx->refShapeRadius];
+    gimp_pixel_fetcher_get_pixel (lctx->pftRef, refX, refY, &lctx->refColorPtr->rgba[0]);
+    
+    p_initReferenceTable(lctx);
+    p_locateDetailLoop(lctx);
+
+    gimp_pixel_fetcher_destroy (lctx->pftRef);
+    gimp_pixel_fetcher_destroy (lctx->pftTarget);
+  }
+
+  
+  if(lctx->refDrawable != NULL)
+  {
+    gimp_drawable_detach(lctx->refDrawable);
+  }
+  
+  if(lctx->targetDrawable != NULL)
+  {
+    gimp_drawable_detach(lctx->targetDrawable);
+  }
+  
+  
+  *targetX = lctx->targetX;
+  *targetY = lctx->targetY;
+  
+  if(gap_debug)
+  {
+    p_debugPrintReferenceTable(lctx);
+    printf("gap_locateDetailWithinRadius: tries:%d areaCompares:%d pickedTryNumber:%d pickedPixelCount:%d resulting colordiff:%.5f\n"
+       ,(int)lctx->countTries
+       ,(int)lctx->countAreaCompares
+       ,(int)lctx->pickedTryNumber
+       ,(int)lctx->pickedPixelCount
+       ,(float)lctx->minColordiff
+       );
+  }
+  
+  return (lctx->minColordiff);
+  
+}  /* end gap_locateDetailWithinRadius */
diff --git a/gap/gap_locate.h b/gap/gap_locate.h
new file mode 100644
index 0000000..e602958
--- /dev/null
+++ b/gap/gap_locate.h
@@ -0,0 +1,83 @@
+/* gap_locate.h
+ *    by hof (Wolfgang Hofer)
+ *  2010/08/06
+ *
+ */
+/* 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:
+ * version 2.7.0;             hof: created
+ */
+
+#ifndef _GAP_LOCATE_H
+#define _GAP_LOCATE_H
+
+/* SYTEM (UNIX) includes */
+#include <stdio.h>
+#include <stdlib.h>
+
+/* GIMP includes */
+#include "gtk/gtk.h"
+#include "libgimp/gimp.h"
+
+#define GAP_LOCATE_MIN_REF_SHAPE_RADIUS 3
+#define GAP_LOCATE_MAX_REF_SHAPE_RADIUS 16
+
+
+/* ----------------------------------------
+ * gap_locateDetailWithinRadius
+ * ----------------------------------------
+ *
+ * the locateDetailWithinRadius procedure takes a referenced detail
+ * specified via refX/Y coordinate, refShapeRadius within a
+ * reference drawable
+ * and tries to locate the same (or similar) detail coordinates
+ * in the target Drawable.
+ *
+ * This is done by comparing pixel areas at refShapeRadius
+ * in the corresponding target Drawable in a loop while
+ * varying offsets within targetMoveRadius.
+ * the targetX/Y koords are picked at those offsets where the compared areas
+ * are best matching (e.g with minimun color difference)
+ * the return value is the minimum colrdifference value
+ * (in range 0.0 to 1.0 where 0.0 indicates that the compared area is exactly equal)
+ * 
+ * requiredColordiffThreshold has valid range 0.0 to 1.0
+ *                            values < 1.0 can speed up processing because area compare operations
+ *                            are cancelled on offesets where colordiff is above this threshold
+ *                            (e.g. when further analyse has no chance to deliver result 
+ *                             below the required matching quality)
+ *                            use value 1.0 if you are interested in weak matching results too.
+ *                           
+ */
+gdouble gap_locateDetailWithinRadius(gint32  refDrawableId
+  , gint32  refX
+  , gint32  refY
+  , gint32  refShapeRadius
+  , gint32  targetDrawableId
+  , gint32  targetMoveRadius
+  , gdouble requiredColordiffThreshold
+  , gint32  *targetX
+  , gint32  *targetY
+  );
+
+
+
+
+#endif
diff --git a/gap/gap_main.c b/gap/gap_main.c
index 985b5a8..bb224a6 100644
--- a/gap/gap_main.c
+++ b/gap/gap_main.c
@@ -831,23 +831,23 @@ query ()
                          " 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 folowing a path\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"
-                         " Restictions:\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 implicite keyframes, and should be passed with keyframe_abs = 0\n"
-                         " - the number of controlpoints is limitied to a maximum of %d.\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 Obect in all handled destination Frames\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);
 
diff --git a/gap/gap_match.c b/gap/gap_match.c
index 9b79203..83c5bb0 100644
--- a/gap/gap_match.c
+++ b/gap/gap_match.c
@@ -228,7 +228,7 @@ gap_match_number(gint32 layer_idx, const char *pattern)
          {
             /* now we are one character past a number */
             l_digit_buff[l_idx] = '\0';
-            l_num = atol(l_digit_buff);  /* scann the number */
+            l_num = atol(l_digit_buff);  /* scan the number */
 
             if(l_num == layer_idx)
             {
diff --git a/gap/gap_mod_layer.c b/gap/gap_mod_layer.c
index ccb7c7d..9fb4bbc 100644
--- a/gap/gap_mod_layer.c
+++ b/gap/gap_mod_layer.c
@@ -283,7 +283,7 @@ p_raise_layer (gint32 image_id, gint32 layer_id, GapModLayliElem * layli_ptr, gi
 
   if(! gimp_drawable_has_alpha (layer_id))
   {
-    /* implicite add an alpha channel before we try to raise */
+    /* implicitly add an alpha channel before we try to raise */
     gimp_layer_add_alpha(layer_id);
   }
   gimp_image_raise_layer(image_id, layer_id);
@@ -296,7 +296,7 @@ p_lower_layer (gint32 image_id, gint32 layer_id, GapModLayliElem * layli_ptr, gi
 
   if(! gimp_drawable_has_alpha (layer_id))
   {
-    /* implicite add an alpha channel before we try to lower */
+    /* implicitly add an alpha channel before we try to lower */
     gimp_layer_add_alpha(layer_id);
   }
 
@@ -306,7 +306,7 @@ p_lower_layer (gint32 image_id, gint32 layer_id, GapModLayliElem * layli_ptr, gi
     && (! gimp_drawable_has_alpha (layli_ptr[nlayers-1].layer_id)))
     {
       /* the layer is one step above a "bottom-layer without alpha" */
-      /* implicite add an alpha channel before we try to lower */
+      /* implicitly add an alpha channel before we try to lower */
       gimp_layer_add_alpha(layli_ptr[nlayers-1].layer_id);
     }
   }
@@ -1218,7 +1218,7 @@ cleanup:
 
 
 /* ============================================================================
- * p_frames_modify
+ * gap_mod_frames_modify
  *
  *   foreach frame of the range (given by range_from and range_to)
  *   perform function defined by action_mode
@@ -1228,12 +1228,14 @@ cleanup:
  *           (or -1 on error or cancel)
  * ============================================================================
  */
-static gint32
-p_frames_modify(GapAnimInfo *ainfo_ptr,
+gint32
+gap_mod_frames_modify(GapAnimInfo *ainfo_ptr,
                    long range_from, long range_to,
                    gint32 action_mode, gint32 sel_mode,
                    gint32 sel_case, gint32 sel_invert,
-                   char *sel_pattern, char *new_layername)
+                   char *sel_pattern, char *new_layername,
+                   GtkWidget *progress_bar,
+                   gboolean *run_flag)
 {
   long    l_cur_frame_nr;
   long    l_step, l_begin, l_end;
@@ -1264,7 +1266,7 @@ p_frames_modify(GapAnimInfo *ainfo_ptr,
 
   if(gap_debug)
   { 
-    printf("gap: p_frames_modify START, action_mode=%d  sel_mode=%d case=%d, invert=%d patt:%s:\n",
+    printf("gap: gap_mod_frames_modify START, action_mode=%d  sel_mode=%d case=%d, invert=%d patt:%s:\n",
         (int)action_mode, (int)sel_mode, (int)sel_case, (int)sel_invert, sel_pattern);
   }
   
@@ -1324,7 +1326,7 @@ p_frames_modify(GapAnimInfo *ainfo_ptr,
   {
     if(gap_debug)
     {
-      printf("p_frames_modify While l_cur_frame_nr = %d\n", (int)l_cur_frame_nr);
+      printf("gap_mod_frames_modify While l_cur_frame_nr = %d\n", (int)l_cur_frame_nr);
     }
 
     /* build the frame name */
@@ -1356,7 +1358,7 @@ p_frames_modify(GapAnimInfo *ainfo_ptr,
 
     if(l_layli_ptr == NULL)
     {
-       printf("gap: p_frames_modify: cant alloc layer info list\n");
+       printf("gap: gap_mod_frames_modify: cant alloc layer info list\n");
        goto error;
     }
 
@@ -1460,7 +1462,7 @@ p_frames_modify(GapAnimInfo *ainfo_ptr,
                    );
     if(l_rc != 0)
     {
-      if(gap_debug) printf("gap: p_frames_modify p_apply-action failed. rc=%d\n", (int)l_rc);
+      if(gap_debug) printf("gap: gap_mod_frames_modify p_apply-action failed. rc=%d\n", (int)l_rc);
       goto error;
     }
 
@@ -1478,7 +1480,7 @@ p_frames_modify(GapAnimInfo *ainfo_ptr,
     l_rc = gap_lib_save_named_frame(l_tmp_image_id, ainfo_ptr->new_filename);
     if(l_rc < 0)
     {
-      printf("gap: p_frames_modify save frame %d failed.\n", (int)l_cur_frame_nr);
+      printf("gap: gap_mod_frames_modify save frame %d failed.\n", (int)l_cur_frame_nr);
       goto error;
     }
     else l_rc = 0;
@@ -1558,7 +1560,29 @@ p_frames_modify(GapAnimInfo *ainfo_ptr,
     if(ainfo_ptr->run_mode == GIMP_RUN_INTERACTIVE)
     {
       l_percentage += l_percentage_step;
-      gimp_progress_update (l_percentage);
+     
+      if(progress_bar != NULL)
+      {
+        guchar *progressText;
+        progressText = g_strdup_printf("frame:%d (%d)", (int)l_cur_frame_nr, (int)l_end);
+        
+        gtk_progress_bar_set_text(GTK_PROGRESS_BAR(progress_bar), progressText);
+        gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(progress_bar), l_percentage);
+        g_free(progressText);
+        while (gtk_events_pending ())
+        {
+          gtk_main_iteration ();
+        }
+        if(*run_flag != TRUE)
+        {
+          /* cancel button was pressed in interactive mode */
+          break;
+        }
+      }
+      else
+      {
+        gimp_progress_update (l_percentage);
+      }
     }
 
     /* advance to next frame */
@@ -1597,12 +1621,12 @@ p_frames_modify(GapAnimInfo *ainfo_ptr,
     }
   }
 
-  if(gap_debug) printf("p_frames_modify End OK\n");
+  if(gap_debug) printf("gap_mod_frames_modify End OK\n");
 
   return 0;
 
 error:
-  if(gap_debug) printf("gap: p_frames_modify exit with Error\n");
+  if(gap_debug) printf("gap: gap_mod_frames_modify exit with Error\n");
 
   if((l_tmp_image_id >= 0) && (l_operating_on_current_image == FALSE))
   {
@@ -1617,7 +1641,7 @@ error:
   if(l_plugin_iterator != NULL)  g_free(l_plugin_iterator);
   return -1;
 
-}               /* end p_frames_modify */
+}               /* end gap_mod_frames_modify */
 
 
 /* ============================================================================
@@ -1639,12 +1663,15 @@ gint gap_mod_layer(GimpRunMode run_mode, gint32 image_id,
   gint32    l_sel_mode;
   gint32    l_sel_case;
   gint32    l_sel_invert;
+  GtkWidget *progress_bar;
+  GtkWidget *dlg;
 
   char      l_sel_pattern[MAX_LAYERNAME];
   char      l_new_layername[MAX_LAYERNAME];
 
   l_rc = 0;
-
+  progress_bar = NULL;
+  dlg = NULL;
 
   ainfo_ptr = gap_lib_alloc_ainfo(image_id, run_mode);
   if(ainfo_ptr != NULL)
@@ -1654,10 +1681,15 @@ gint gap_mod_layer(GimpRunMode run_mode, gint32 image_id,
     {
       if(run_mode == GIMP_RUN_INTERACTIVE)
       {
+         /* note: for interactive call the processing is already done
+          * as callback of the dialog
+          */
          l_rc = gap_mod_frames_dialog (ainfo_ptr, &l_from, &l_to,
                                        &l_action_mode,
                                        &l_sel_mode, &sel_case, &sel_invert,
                                        &l_sel_pattern[0], &l_new_layername[0]);
+         gap_lib_free_ainfo(&ainfo_ptr);
+         return (l_rc);
       }
       else
       {
@@ -1676,16 +1708,20 @@ gint gap_mod_layer(GimpRunMode run_mode, gint32 image_id,
 
       if(l_rc >= 0)
       {
+        gboolean run_flag;
+         
+        run_flag = TRUE;
         /* no need to save the current image before processing
-         * because the p_frames_modify procedure operates directly on the current frame
+         * because the gap_mod_frames_modify procedure operates directly on the current frame
          * and loads all other frames from disc.
          * futher all successful processed frames are saved back to disk
          * (including the current one)
          */
-           l_rc = p_frames_modify(ainfo_ptr, l_from, l_to,
+           l_rc = gap_mod_frames_modify(ainfo_ptr, l_from, l_to,
                                   l_action_mode,
                                   l_sel_mode, sel_case, sel_invert,
-                                  &l_sel_pattern[0], &l_new_layername[0]
+                                  &l_sel_pattern[0], &l_new_layername[0],
+                                  progress_bar, &run_flag
                                  );
       }
 
diff --git a/gap/gap_mod_layer.h b/gap/gap_mod_layer.h
index 0aa787b..fd7439e 100644
--- a/gap/gap_mod_layer.h
+++ b/gap/gap_mod_layer.h
@@ -36,6 +36,10 @@
 #ifndef _GAP_MOD_LAYER_H
 #define _GAP_MOD_LAYER_H
 
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+#include "gap_libgapbase.h"
+
 #define MAX_LAYERNAME 128
 
 /* action_mode values */
@@ -130,4 +134,12 @@ gint gap_mod_layer(GimpRunMode run_mode, gint32 image_id,
                    gint32 sel_case, gint32 sel_invert,
                    char *sel_pattern, char *new_layername);
 
+gint32  gap_mod_frames_modify(GapAnimInfo *ainfo_ptr,
+                   long range_from, long range_to,
+                   gint32 action_mode, gint32 sel_mode,
+                   gint32 sel_case, gint32 sel_invert,
+                   char *sel_pattern, char *new_layername,
+                   GtkWidget *progress_bar,
+                   gboolean *run_flag);
+
 #endif
diff --git a/gap/gap_mod_layer_dialog.c b/gap/gap_mod_layer_dialog.c
index fdaf946..d09d355 100644
--- a/gap/gap_mod_layer_dialog.c
+++ b/gap/gap_mod_layer_dialog.c
@@ -111,24 +111,47 @@ p_mod_frames_response (GtkWidget *widget,
                  gint       response_id,
                  GapModFramesGlobalParams *gmop)
 {
-  switch (response_id)
+  if (response_id == GTK_RESPONSE_CANCEL)
   {
-    case GTK_RESPONSE_OK:
-      gmop->run_flag = TRUE;
-
-    default:
-      if(gmop->shell)
-      {
-        GtkWidget *l_shell;
+    gmop->retcode = -1; 
+    if(gmop->run_flag == TRUE)
+    {
+      gmop->run_flag = FALSE;
+      return;
+    }
+  }
+  else if (response_id == GTK_RESPONSE_OK)
+  {
+    if(gmop->run_flag == TRUE)
+    {
+      return;
+    }
+    gtk_widget_set_sensitive(gmop->main_vbox, FALSE);
+
+    gmop->run_flag = TRUE;
+    gmop->retcode = gap_mod_frames_modify(gmop->ainfo_ptr
+                                        , gmop->range_from
+                                        , gmop->range_to
+                                        , gmop->action_mode
+                                        , gmop->sel_mode
+                                        , gmop->sel_case
+                                        , gmop->sel_invert
+                                        , gmop->sel_pattern
+                                        , gmop->new_layername
+                                        , gmop->progress_bar
+                                        , &gmop->run_flag
+                                        );
+  }
 
-        l_shell = gmop->shell;
-        gmop->shell = NULL;
+  if(gmop->shell)
+  {
+    GtkWidget *l_shell;
 
-        gtk_widget_destroy(l_shell);
-        gtk_main_quit();
-      }
+    l_shell = gmop->shell;
+    gmop->shell = NULL;
 
-      break;
+    gtk_widget_destroy(l_shell);
+    gtk_main_quit();
   }
 }  /* end p_mod_frames_response */
 
@@ -960,6 +983,7 @@ p_create_mod_frames_dialog(GapModFramesGlobalParams *gmop)
 {
   GtkWidget *dlg;
   GtkWidget *main_vbox;
+  GtkWidget *active_vbox;
   GtkWidget *hbox;
   GtkWidget *frame;
   GtkWidget *entry;
@@ -972,6 +996,7 @@ p_create_mod_frames_dialog(GapModFramesGlobalParams *gmop)
   GtkWidget *menu_bar;
   GtkWidget *menu_item;
   GtkWidget *master_menu;
+  GtkWidget *progress_bar;
   gint       row;
   GtkObject *adj;
 
@@ -997,6 +1022,7 @@ p_create_mod_frames_dialog(GapModFramesGlobalParams *gmop)
 
 
   main_vbox = gtk_vbox_new (FALSE, 4);
+  gmop->main_vbox = main_vbox;
   gtk_container_set_border_width (GTK_CONTAINER (main_vbox), 6);
   gtk_container_add (GTK_CONTAINER (GTK_DIALOG (dlg)->vbox), main_vbox);
 
@@ -1389,6 +1415,14 @@ p_create_mod_frames_dialog(GapModFramesGlobalParams *gmop)
                     G_CALLBACK (gimp_int_adjustment_update),
                     &gmop->range_to);
 
+  row++;
+
+  progress_bar = gtk_progress_bar_new ();
+  gmop->progress_bar = progress_bar;
+  gtk_progress_bar_set_text(GTK_PROGRESS_BAR(progress_bar), " ");
+  gtk_widget_show (progress_bar);
+
+  gtk_container_add (GTK_CONTAINER (GTK_DIALOG (dlg)->vbox), progress_bar);
 
 
   gtk_widget_show(main_vbox);
@@ -1417,6 +1451,7 @@ gap_mod_frames_dialog(GapAnimInfo *ainfo_ptr,
   gap_stock_init();
 
   gmop->run_flag = FALSE;
+  gmop->retcode = -1;
   gmop->ainfo_ptr = ainfo_ptr;
   gmop->range_from = gmop->ainfo_ptr->first_frame_nr;
   gmop->range_to = gmop->ainfo_ptr->last_frame_nr;
diff --git a/gap/gap_mod_layer_dialog.h b/gap/gap_mod_layer_dialog.h
index 6f40400..e744897 100644
--- a/gap/gap_mod_layer_dialog.h
+++ b/gap/gap_mod_layer_dialog.h
@@ -69,9 +69,13 @@ typedef struct GapModFramesGlobalParams {  /* nick: gmop */
   GtkWidget *case_sensitive_check_button;  
   GtkWidget *invert_check_button;
   GtkWidget *layer_selection_frame;
+  GtkWidget *progress_bar;
+  GtkWidget *main_vbox;  
 
   GtkObject *frame_from_adj;
   GtkObject *frame_to_adj;
+
+  gint32       retcode;
   
 } GapModFramesGlobalParams;
 
diff --git a/gap/gap_morph_dialog.c b/gap/gap_morph_dialog.c
index 2e6f379..3b97bda 100644
--- a/gap/gap_morph_dialog.c
+++ b/gap/gap_morph_dialog.c
@@ -44,8 +44,11 @@
 
 #include "gap_base.h"
 
+#include "gap_colordiff.h"
+#include "gap_locate.h"
 #include "gap_morph_main.h"
 #include "gap_morph_dialog.h"
+#include "gap_morph_shape.h"
 #include "gap_morph_exec.h"
 #include "gap_pdb_calls.h"
 #include "gap_pview_da.h"
@@ -112,24 +115,11 @@ static gint32 global_morph_pv_height = GAP_MORPH_PV_HEIGHT_DEFAULT;
 static void         p_morph_response(GtkWidget *w, gint response_id, GapMorphGUIParams *mgup);
 static void         p_upd_widget_values(GapMorphGUIParams *mgup);
 static void         p_upd_warp_info_label(GapMorphGUIParams *mgup);
-static gboolean     p_pixel_check_opaque(GimpPixelFetcher *pft
-                                     , gint bpp
-                                     , gdouble needx
-                                     , gdouble needy
-                                     );
-static void         p_find_outmost_opaque_pixel(GimpPixelFetcher *pft
-                           ,gint bpp
-                           ,gdouble alpha_rad
-                           ,gint32 width
-                           ,gint32 height
-                           ,gdouble *px
-                           ,gdouble *py
-                           );
 static void         p_generate_outline_shape_workpoints(GapMorphGUIParams *mgup);
 static void         p_add_4corner_workpoints(GapMorphGUIParams *mgup);
 
-static void         p_zoom_in(GapMorphSubWin  *swp, gdouble l_x, gdouble l_y);   //XXX unfinished
-static void         p_zoom_out(GapMorphSubWin  *swp);   //XXX unfinished
+static void         p_zoom_in(GapMorphSubWin  *swp, gdouble l_x, gdouble l_y);
+static void         p_zoom_out(GapMorphSubWin  *swp);
 static void         p_fit_zoom_into_pview_size(GapMorphSubWin  *swp);
 static void         on_swap_button_pressed_callback(GtkWidget *wgt, GapMorphGUIParams *mgup);
 static void         on_fit_zoom_pressed_callback(GtkWidget *wgt, GapMorphSubWin *swp);
@@ -146,8 +136,13 @@ static void         p_set_nearest_current_workpoint(GapMorphSubWin  *swp
                                                   );
 static void         p_set_current_workpoint_no_refresh(GapMorphSubWin  *swp, GapMorphWorkPoint *wp);
 static void         p_set_current_workpoint(GapMorphSubWin  *swp, GapMorphWorkPoint *wp);
-static GapMorphWorkPoint * p_add_new_point(GapMorphSubWin  *swp, gdouble in_x, gdouble in_y);
-static void                p_add_new_point_refresh(GapMorphSubWin  *swp, gdouble in_x, gdouble in_y);
+
+static void         p_locate_point(gint32  refDrawableId, gint32  targetDrawableId
+                               , GapMorphGUIParams *mgup
+                               , gdouble rx, gdouble ry
+                               , gdouble *tx, gdouble *ty);
+static GapMorphWorkPoint * p_add_new_point(GapMorphSubWin  *swp, gdouble in_x, gdouble in_y, gboolean locate_detail);
+static void                p_add_new_point_refresh(GapMorphSubWin  *swp, gdouble in_x, gdouble in_y, gboolean locate_detail);
 static void                p_refresh_total_points_label(GapMorphGUIParams *mgup);
 static GapMorphWorkPoint * p_find_nearest_point(GapMorphSubWin  *swp
                                                , gdouble in_x
@@ -213,6 +208,8 @@ static GtkWidget *  p_create_subwin(GapMorphSubWin *swp
 static void         gap_morph_create_dialog(GapMorphGUIParams *mgup);
 
 
+
+
 /* -----------------------------
  * p_morph_response
  * -----------------------------
@@ -225,6 +222,7 @@ p_morph_response(GtkWidget *w, gint response_id, GapMorphGUIParams *mgup)
   switch (response_id)
   {
     case GAP_MORPH_RESPONSE_RESET:
+      mgup->cancelWorkpointGeneration = TRUE;
       gap_morph_exec_free_workpoint_list(&mgup->mgpp->master_wp_list);
       p_add_4corner_workpoints(mgup);
       p_set_upd_timer_request(mgup
@@ -254,6 +252,7 @@ p_morph_response(GtkWidget *w, gint response_id, GapMorphGUIParams *mgup)
     case GTK_RESPONSE_OK:
       if(mgup)
       {
+        mgup->cancelWorkpointGeneration = TRUE;
         if (GTK_WIDGET_VISIBLE (mgup->shell))
         {
           gtk_widget_hide (mgup->shell);
@@ -265,6 +264,7 @@ p_morph_response(GtkWidget *w, gint response_id, GapMorphGUIParams *mgup)
       dialog = NULL;
       if(mgup)
       {
+        mgup->cancelWorkpointGeneration = TRUE;
         dialog = mgup->shell;
         if(dialog)
         {
@@ -297,6 +297,15 @@ p_upd_widget_values(GapMorphGUIParams *mgup)
             GTK_ADJUSTMENT(mgup->affect_radius_spinbutton_adj)
            ,mgup->mgpp->affect_radius);
        gtk_adjustment_set_value(
+            GTK_ADJUSTMENT(mgup->locate_edge_threshold_spinbutton_adj)
+           ,mgup->mgpp->edgeColordiffThreshold);
+       gtk_adjustment_set_value(
+            GTK_ADJUSTMENT(mgup->locate_radius_spinbutton_adj)
+           ,mgup->mgpp->locateDetailMoveRadius);
+       gtk_adjustment_set_value(
+            GTK_ADJUSTMENT(mgup->locate_shape_radius_spinbutton_adj)
+           ,mgup->mgpp->locateDetailShapeRadius);
+       gtk_adjustment_set_value(
             GTK_ADJUSTMENT(mgup->gravity_intensity_spinbutton_adj)
            ,mgup->mgpp->gravity_intensity);
        gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (mgup->use_quality_wp_selection_checkbutton)
@@ -345,127 +354,6 @@ p_upd_warp_info_label(GapMorphGUIParams *mgup)
 
 
 
-/* -----------------------------
- * p_pixel_check_opaque
- * -----------------------------
- * check average opacity in an area
- * of 2x2 pixels
- * return TRUE if average alpha is 50% or more opaque
- */
-static gboolean
-p_pixel_check_opaque(GimpPixelFetcher *pft, gint bpp, gdouble needx, gdouble needy)
-{
-  guchar  pixel[4][4];
-  gdouble alpha_val;
-
-  gint    xi, yi;
-  gint alpha_idx;
-
-
-  if (needx >= 0.0)
-    xi = (int) needx;
-  else
-    xi = -((int) -needx + 1);
-
-  if (needy >= 0.0)
-    yi = (int) needy;
-  else
-    yi = -((int) -needy + 1);
-
-  gimp_pixel_fetcher_get_pixel (pft, xi, yi, pixel[0]);
-  gimp_pixel_fetcher_get_pixel (pft, xi + 1, yi, pixel[1]);
-  gimp_pixel_fetcher_get_pixel (pft, xi, yi + 1, pixel[2]);
-  gimp_pixel_fetcher_get_pixel (pft, xi + 1, yi + 1, pixel[3]);
-
-  alpha_idx = bpp -1;
-  /* average aplha channel normalized to range 0.0 upto 1.0 */
-  alpha_val = (
-               (gdouble)pixel[0][alpha_idx] / 255.0
-            +  (gdouble)pixel[1][alpha_idx] / 255.0
-            +  (gdouble)pixel[2][alpha_idx] / 255.0
-            +  (gdouble)pixel[3][alpha_idx] / 255.0
-            ) /  4.0;
-
-  if (alpha_val > 0.5)  /* fix THRESHOLD half or more opaque */
-  {
-    return (TRUE);
-  }
-  return (FALSE);
-}  /* end p_pixel_check_opaque */
-
-
-/* -----------------------------
- * p_find_outmost_opaque_pixel
- * -----------------------------
- * returns koords in paramters px, py
- */
-static void
-p_find_outmost_opaque_pixel(GimpPixelFetcher *pft
-                           ,gint bpp
-                           ,gdouble alpha_rad
-                           ,gint32 width
-                           ,gint32 height
-                           ,gdouble *px
-                           ,gdouble *py
-                           )
-{
-  gdouble center_x;
-  gdouble center_y;
-  gdouble cos_alpha;
-  gdouble sin_alpha;
-  gdouble l_x, l_y, l_r;
-  gdouble half_width;
-  gdouble half_height;
-
-  l_x = 0;
-  l_y = 0;
-  cos_alpha = cos(alpha_rad);
-  sin_alpha = sin(alpha_rad);
-
-  /* printf("sin: %.5f cos:%.5f\n", sin_alpha, cos_alpha); */
-
-  half_width = (gdouble)(width /2.0);
-  half_height = (gdouble)(height /2.0);
-  center_x = half_width;
-  center_y = half_height;
-  l_r = MAX(half_width, half_height);
-  l_r *= l_r;
-
-  /* start at the out-most point
-   * (may be out of the layer in most cases)
-   * and search towards the center along
-   * the line with angle alpha
-   */
-  while(l_r > 0)
-  {
-    l_y = l_r * sin_alpha;
-    l_x = l_r * cos_alpha;
-    if((l_x <= half_width)
-    && (l_y <= half_height))
-    {
-      if (((center_x + l_x) >= 0)
-      &&  ((center_y + l_y) >= 0))
-      {
-        /* now we are inside the layer area */
-        if (p_pixel_check_opaque(pft
-                          ,bpp
-                          ,center_x + l_x
-                          ,center_y + l_y
-                          ))
-        {
-          break;
-        }
-      }
-    }
-    l_r--;
-  }
-
-  *px = center_x + l_x;
-  *py = center_y + l_y;
-
-}  /* end p_find_outmost_opaque_pixel */
-
-
 /* -----------------------------------
  * p_generate_outline_shape_workpoints
  * -----------------------------------
@@ -473,59 +361,8 @@ p_find_outmost_opaque_pixel(GimpPixelFetcher *pft
 static void
 p_generate_outline_shape_workpoints(GapMorphGUIParams *mgup)
 {
-  GapMorphWorkPoint *wp;
-  GimpPixelFetcher *src_pixfet;
-  GimpPixelFetcher *dst_pixfet;
-  GimpDrawable *dst_drawable;
-  GimpDrawable *src_drawable;
-  gdouble alpha_rad;
-  gdouble step_rad;
-  gint  ii;
-
-  src_drawable = gimp_drawable_get (mgup->mgpp->osrc_layer_id);
-  dst_drawable = gimp_drawable_get (mgup->mgpp->fdst_layer_id);
-
-  src_pixfet = gimp_pixel_fetcher_new (src_drawable, FALSE /*shadow*/);
-  dst_pixfet = gimp_pixel_fetcher_new (dst_drawable, FALSE /*shadow*/);
-
-  step_rad =  (2.0 * G_PI) / MAX(1, mgup->num_shapepoints);
-  alpha_rad = 0.0;
-
-  /* loop from 0 to 360 degree */
-  for(ii=0; ii < mgup->num_shapepoints; ii++)
-  {
-     gdouble sx, sy, dx, dy;
-
-     p_find_outmost_opaque_pixel(src_pixfet
-                                ,src_drawable->bpp
-                                ,alpha_rad
-                                ,src_drawable->width
-                                ,src_drawable->height
-                                ,&sx
-                                ,&sy
-                                );
-     p_find_outmost_opaque_pixel(dst_pixfet
-                                ,dst_drawable->bpp
-                                ,alpha_rad
-                                ,dst_drawable->width
-                                ,dst_drawable->height
-                                ,&dx
-                                ,&dy
-                                );
-
-     /* create a new workpoint with sx,sy, dx, dy coords */
-     wp = gap_morph_dlg_new_workpont(sx ,sy ,dx ,dy);
-     wp->next = mgup->mgpp->master_wp_list;
-     mgup->mgpp->master_wp_list = wp;
-
-     alpha_rad += step_rad;
-  }
-  gimp_pixel_fetcher_destroy (src_pixfet);
-  gimp_pixel_fetcher_destroy (dst_pixfet);
-
-  gimp_drawable_detach(src_drawable);
-  gimp_drawable_detach(dst_drawable);
-
+  mgup->mgpp->numOutlinePoints = mgup->num_shapepoints;
+  gap_morph_shape_generate_outline_workpoints(mgup->mgpp);
 
 }  /* end p_generate_outline_shape_workpoints */
 
@@ -741,6 +578,10 @@ on_swap_button_pressed_callback(GtkWidget *wgt, GapMorphGUIParams *mgup)
   {
     return;
   }
+  if(mgup->workpointGenerationBusy == TRUE)
+  {
+    return;
+  }
 
   src_layer_width  = gimp_drawable_width(mgup->mgpp->osrc_layer_id);
   src_layer_height = gimp_drawable_height(mgup->mgpp->osrc_layer_id);
@@ -1004,7 +845,6 @@ on_koord_spinbutton_changed             (GtkObject       *obj,
   if(value != *val_ptr)
   {
     gimp_double_adjustment_update(GTK_ADJUSTMENT(obj), val_ptr);
-    //  *val_ptr = value;
     if(swp)
     {
       /* setup a timer request for pview redraw after 16 millisec
@@ -1082,19 +922,9 @@ gap_morph_dlg_new_workpont(gdouble srcx, gdouble srcy, gdouble dstx, gdouble dst
 {
   GapMorphWorkPoint *wp;
 
-  wp = g_new(GapMorphWorkPoint, 1);
-  wp->next = NULL;
-  wp->fdst_x = dstx;
-  wp->fdst_y = dsty;
-  wp->osrc_x = srcx;
-  wp->osrc_y = srcy;
-
-  wp->dst_x = wp->fdst_x;
-  wp->dst_y = wp->fdst_y;
-  wp->src_x = wp->osrc_x;
-  wp->src_y = wp->osrc_y;
-
+  wp = gap_morph_shape_new_workpont(srcx, srcy, dstx, dsty);
   return(wp);
+
 }  /* end gap_morph_dlg_new_workpont */
 
 /* -----------------------------
@@ -1290,15 +1120,81 @@ p_set_current_workpoint(GapMorphSubWin  *swp, GapMorphWorkPoint *wp_cur)
 }  /* end p_set_current_workpoint */
 
 /* -----------------------------
+ * p_locate_point
+ * -----------------------------
+ * locate point coordinate in the other drawable
+ * (by searching for best matching shape within move radius)
+ */
+static void
+p_locate_point(gint32  refDrawableId, gint32  targetDrawableId
+  , GapMorphGUIParams *mgup
+  , gdouble rx, gdouble ry
+  , gdouble *tx, gdouble *ty)
+{
+  gint32  refX;
+  gint32  refY;
+  gint32  targetX;
+  gint32  targetY;
+  gdouble colordiff;
+
+  refX = rx;
+  refY = ry;
+
+ 
+  colordiff = gap_locateDetailWithinRadius(refDrawableId
+                  , refX
+                  , refY
+                  , mgup->mgpp->locateDetailShapeRadius
+                  , targetDrawableId
+                  , mgup->mgpp->locateDetailMoveRadius
+                  , mgup->mgpp->locateColordiffThreshold
+                  , &targetX
+                  , &targetY
+                  );
+  if(gap_debug)
+  {
+    printf("p_locate_point: x/y: %d / %d MoveRadius:%d ShapeRadius:%d Threshold:%.5f colordiff:%.5f\n"
+          , (int)refX
+          , (int)refY
+          , (int)mgup->mgpp->locateDetailMoveRadius
+          , (int)mgup->mgpp->locateDetailShapeRadius
+          , (float)mgup->mgpp->locateColordiffThreshold
+          , (float)colordiff
+          );
+          
+  }
+
+  if(colordiff < mgup->mgpp->locateColordiffThreshold)
+  {
+    /* found a sufficient matching shape in the target drawable
+     * at targetX/Y coordinate. Overwrite tx and ty with this matching point coordinate
+     */
+    *tx = targetX;
+    *ty = targetY;
+
+
+    if(gap_debug)
+    {
+      printf("p_locate_point: SUCCESS, found matching shape! tx:%d ty:%d\n"
+         ,(int)targetX
+         ,(int)targetY
+         );
+    }
+  }
+
+}  /* end p_locate_point */
+
+
+/* -----------------------------
  * p_add_new_point
  * -----------------------------
  * add a new point at in_x/in_y
- * and calculate the other koordianates
- * (using the offests of the nearest existing point
+ * and calculate the other coordinates
+ * (using the offsets of the nearest existing point
  *  as guess)
  */
 static GapMorphWorkPoint *
-p_add_new_point(GapMorphSubWin  *swp, gdouble in_x, gdouble in_y)
+p_add_new_point(GapMorphSubWin  *swp, gdouble in_x, gdouble in_y, gboolean locate_detail)
 {
   GapMorphGUIParams *mgup;
   GapMorphSubWin    *swp_other;
@@ -1333,6 +1229,8 @@ p_add_new_point(GapMorphSubWin  *swp, gdouble in_x, gdouble in_y)
 
     srcx = in_x;
     srcy = in_y;
+    
+    
     if(mgup->op_mode == GAP_MORPH_OP_MODE_MOVE)
     {
       dstx = (srcx) * w / MAX(wo,1);
@@ -1343,6 +1241,18 @@ p_add_new_point(GapMorphSubWin  *swp, gdouble in_x, gdouble in_y)
       dstx = (srcx + (wp_near->fdst_x - wp_near->osrc_x)) * w / MAX(wo,1);
       dsty = (srcy + (wp_near->fdst_y - wp_near->osrc_y)) * h / MAX(ho,1);
     }
+
+    if(locate_detail)
+    {
+      p_locate_point(*swp->layer_id_ptr          /* reference DrawableId  */
+                    , *swp_other->layer_id_ptr   /* target DrawableId  */
+                    , mgup
+                    , srcx, srcy                 /* reference coordinates */
+                    , &dstx, &dsty               /* OUT: target coordinates */
+                    );
+    }
+
+
   }
   else
   {
@@ -1366,6 +1276,16 @@ p_add_new_point(GapMorphSubWin  *swp, gdouble in_x, gdouble in_y)
       srcx = (dstx + (wp_near->osrc_x - wp_near->fdst_x)) * w / MAX(wo,1);
       srcy = (dsty + (wp_near->osrc_y - wp_near->fdst_y)) * h / MAX(ho,1);
     }
+
+    if(locate_detail)
+    {
+      p_locate_point(*swp->layer_id_ptr          /* reference DrawableId  */
+                    , *swp_other->layer_id_ptr   /* target DrawableId  */
+                    , mgup
+                    , dstx,  dsty                 /* reference coordinates */
+                    , &srcx, &srcy                /* OUT: target coordinates */
+                    );
+    }
   }
 
   wp = gap_morph_dlg_new_workpont(srcx, srcy, dstx, dsty);
@@ -1386,12 +1306,12 @@ p_add_new_point(GapMorphSubWin  *swp, gdouble in_x, gdouble in_y)
  * (this also sets refresh request)
  */
 static void
-p_add_new_point_refresh(GapMorphSubWin  *swp, gdouble in_x, gdouble in_y)
+p_add_new_point_refresh(GapMorphSubWin  *swp, gdouble in_x, gdouble in_y, gboolean locate_detail)
 {
   GapMorphGUIParams *mgup;
   GapMorphWorkPoint *wp;
 
-  wp = p_add_new_point(swp, in_x, in_y);
+  wp = p_add_new_point(swp, in_x, in_y, locate_detail);
   p_set_current_workpoint(swp, wp);
 
   mgup = (GapMorphGUIParams *)swp->mgup;
@@ -1432,7 +1352,7 @@ p_refresh_total_points_label(GapMorphGUIParams *mgup)
  * p_find_nearest_point
  * ---------------------------------
  * saerch the workpoint list for the point that is the nearest
- * to position in_x/in_y in the osrc or fdst koord system.
+ * to position in_x/in_y in the osrc or fdst coord system.
  * (depending on the swp->src_flag)
  */
 static GapMorphWorkPoint *
@@ -1940,10 +1860,10 @@ on_pview_events (GtkWidget *widget
   GdkEventButton *bevent;
   GdkEventMotion *mevent;
   gint mouse_button;
-  gdouble    curx;        /* current mouse position koordinate */
+  gdouble    curx;        /* current mouse position coordinate */
   gdouble    cury;
 
-  static gdouble    prevx;        /* prev mouse position koordinate */
+  static gdouble    prevx;        /* prev mouse position coordinate */
   static gdouble    prevy;
   static gboolean   drag_disabled = FALSE;  /* ALT or CTRL pressed */
 
@@ -1991,6 +1911,9 @@ on_pview_events (GtkWidget *widget
         gdouble l_x;
         gdouble l_y;
         gboolean make_new_point;
+        gboolean locate_detail;
+
+        locate_detail = FALSE;
 
          /* Picking of pathpoints is done when
           *   the left mousebutton goes down (mouse_button == 1)
@@ -2062,6 +1985,15 @@ on_pview_events (GtkWidget *widget
            return (FALSE);
          }
 
+         if(bevent->state & GDK_CONTROL_MASK)
+         {
+           if(mgup->op_mode == GAP_MORPH_OP_MODE_MOVE)
+           {
+             make_new_point = TRUE;
+           }
+           locate_detail = TRUE;
+         }
+
          if(bevent->state & GDK_SHIFT_MASK)
          {
            /* SHIFT-Click: force creation of new point
@@ -2114,7 +2046,7 @@ on_pview_events (GtkWidget *widget
          if(make_new_point)
          {
            /* add the new point and handle refresh for both src and dst view */
-           p_add_new_point_refresh(swp, l_x, l_y);
+           p_add_new_point_refresh(swp, l_x, l_y, locate_detail);
            return(FALSE);
          }
       }
@@ -2179,6 +2111,7 @@ on_pview_events (GtkWidget *widget
 
           gtk_adjustment_set_value (GTK_ADJUSTMENT(swp->x_spinbutton_adj), *swp->wpx_ptr);
           gtk_adjustment_set_value (GTK_ADJUSTMENT(swp->y_spinbutton_adj), *swp->wpy_ptr);
+          
         }
       }
       break;
@@ -2448,7 +2381,6 @@ on_pointcolor_button_changed(GimpColorButton *widget,
                          ,GAP_MORPH_DLG_UPD_REQUEST_REDRAW
                          );
 
-
   }
 }  /* end on_pointcolor_button_changed */
 
@@ -2673,6 +2605,7 @@ on_wp_save_button_clicked (GtkButton         *button
                           ,GdkEventButton    *bevent
                           ,GapMorphGUIParams *mgup)
 {
+  mgup->cancelWorkpointGeneration = TRUE;
   p_create_wp_filesel(mgup
                      ,bevent
                      ,TRUE  /* save_mode */
@@ -2688,12 +2621,15 @@ on_wp_load_button_clicked (GtkButton         *button
                           ,GdkEventButton    *bevent
                           ,GapMorphGUIParams *mgup)
 {
+  mgup->cancelWorkpointGeneration = TRUE;
   p_create_wp_filesel(mgup
                      ,bevent
                      ,FALSE  /* save_mode */
                      );
 }  /* end on_wp_load_button_clicked */
 
+
+
 /* --------------------------------
  * on_wp_shape_button_clicked
  * --------------------------------
@@ -2707,22 +2643,47 @@ on_wp_shape_button_clicked (GtkButton *button
   if(mgup)
   {
     gboolean clear_old_workpoint_set;
+    gboolean autoEdgeShape;
+
+    if(mgup->workpointGenerationBusy == TRUE)
+    {
+      return;
+    }
 
     clear_old_workpoint_set = TRUE;
+    autoEdgeShape = FALSE;
     if(bevent)
     {
       if(bevent->state & GDK_SHIFT_MASK)
       {
         clear_old_workpoint_set = FALSE;
       }
+      if(bevent->state & GDK_CONTROL_MASK)
+      {
+        autoEdgeShape = TRUE;
+      }
     }
 
     if(clear_old_workpoint_set)
     {
       gap_morph_exec_free_workpoint_list(&mgup->mgpp->master_wp_list);
     }
-    mgup->num_shapepoints = (gint32)GTK_ADJUSTMENT(mgup->num_shapepoints_adj)->value;;
-    p_generate_outline_shape_workpoints(mgup);
+    mgup->num_shapepoints = (gint32)GTK_ADJUSTMENT(mgup->num_shapepoints_adj)->value;
+    
+    if(autoEdgeShape == TRUE)
+    {
+      gtk_widget_show (mgup->progressBar);
+      mgup->cancelWorkpointGeneration = FALSE;
+      mgup->mgpp->numWorkpoints = mgup->num_shapepoints;
+      mgup->mgpp->numOutlinePoints = 0;
+      gap_moprhShapeDetectionEdgeBased(mgup, &mgup->cancelWorkpointGeneration);
+      gtk_widget_hide (mgup->progressBar);
+    }
+    else
+    {
+      p_generate_outline_shape_workpoints(mgup);
+    }
+    
     p_set_upd_timer_request(mgup
                        ,GAP_MORPH_DLG_UPD_REQUEST_FULL_REFRESH
                        ,GAP_MORPH_DLG_UPD_REQUEST_FULL_REFRESH
@@ -3195,7 +3156,7 @@ p_create_subwin(GapMorphSubWin *swp
 
   row = 0;
 
-  /* the layer seletion combobox */
+  /* the layer selection combobox */
 //   label = gtk_label_new( _("Layer:"));
 // 
 //   gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
@@ -3229,7 +3190,7 @@ p_create_subwin(GapMorphSubWin *swp
   row++;
 
 
-  /* the x koordinate label */
+  /* the x coordinate label */
   label = gtk_label_new (_("X:"));
   gtk_widget_show (label);
   gtk_table_attach (GTK_TABLE (table), label, 0, 1, row, row+1,
@@ -3237,7 +3198,7 @@ p_create_subwin(GapMorphSubWin *swp
                     (GtkAttachOptions) (0), 4, 0);
   gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
 
-  /* the x koordinate spinbutton */
+  /* the x coordinate spinbutton */
   adj = gtk_adjustment_new ( 0
                            , 0
                            , 10000       /* constrain to image width is done later */
@@ -3260,7 +3221,7 @@ p_create_subwin(GapMorphSubWin *swp
 
 
 
-  /* the y koordinate label */
+  /* the y coordinate label */
   label = gtk_label_new (_("Y:"));
   gtk_widget_show (label);
   gtk_table_attach (GTK_TABLE (table), label, 2, 3, row, row+1,
@@ -3269,7 +3230,7 @@ p_create_subwin(GapMorphSubWin *swp
   gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
 
 
-  /* the y koordinate spinbutton */
+  /* the y coordinate spinbutton */
   adj = gtk_adjustment_new ( 0
                            , 0
                            , 10000       /* constrain to image height is done later */
@@ -3652,7 +3613,9 @@ gap_morph_create_dialog(GapMorphGUIParams *mgup)
                     GTK_FILL, 0, 0, 0 );
   gimp_help_set_help_data(button,
                        _("Create N workpoints following the outline shape of the layer."
-                         "(the shape detection is looking for non-transparent pixels)."
+                         "the simple shape detection is looking for non-transparent pixels."
+                         "CTRL-click uses an edge detection based shape detection algorithm "
+                         "that is capable to operate on opaque images."
                          "SHIFT-click: adds the new points and keeps the old points")
                        , NULL);
   gtk_widget_show (button);
@@ -3712,7 +3675,6 @@ gap_morph_create_dialog(GapMorphGUIParams *mgup)
                    , G_CALLBACK (gimp_double_adjustment_update)
                    , &mgup->mgpp->affect_radius);
 
-
   /* the deform intensity label */
   label = gtk_label_new (_("Intensity:"));
   gtk_widget_show (label);
@@ -3731,12 +3693,12 @@ gap_morph_create_dialog(GapMorphGUIParams *mgup)
   mgup->gravity_intensity_spinbutton = spinbutton;
   gtk_widget_show (spinbutton);
 
+  gtk_widget_set_size_request (spinbutton, 80, -1);
   gtk_table_attach (GTK_TABLE (table), spinbutton, 5, 7, row, row+1,
                     (GtkAttachOptions) (GTK_FILL),
                     (GtkAttachOptions) (0),
                      0, 0);
 
-  gtk_widget_set_size_request (spinbutton, 80, -1);
   gimp_help_set_help_data (spinbutton, _("Deform intensity.")
                                          , NULL);
   g_signal_connect (G_OBJECT (adj), "value_changed"
@@ -3890,6 +3852,8 @@ gap_morph_create_dialog(GapMorphGUIParams *mgup)
 
   row++;
 
+
+
   /* the number_of_points label */
   label = gtk_label_new ("---");
   mgup->warp_info_label = label;
@@ -3939,12 +3903,96 @@ gap_morph_create_dialog(GapMorphGUIParams *mgup)
 
   row++;
 
+  /* the LOCATE label */
+  label = gtk_label_new (_("Locate:"));
+  gtk_widget_show (label);
+  gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
+  gtk_table_attach (GTK_TABLE (table), label, 0, 1, row, row+1,
+                    (GtkAttachOptions) (GTK_FILL),
+                    (GtkAttachOptions) (0), 0, 0);
+
+  /* locate move radius spinbutton */
+  adj = gtk_adjustment_new ( mgup->mgpp->locateDetailMoveRadius
+                           , 0
+                           , 200
+                           , 1, 10, 0);
+  mgup->locate_radius_spinbutton_adj = adj;
+  spinbutton = gtk_spin_button_new (GTK_ADJUSTMENT (adj), 1, 0);
+  gtk_widget_show (spinbutton);
+
+  gtk_table_attach (GTK_TABLE (table), spinbutton, 1, 2, row, row+1,
+                    (GtkAttachOptions) (0),
+                    (GtkAttachOptions) (0),
+                     0, 0);
+
+  gtk_widget_set_size_request (spinbutton, 80, -1);
+  gimp_help_set_help_data (spinbutton, _("Locate radius in pixels."
+                                         " radius for automatically point locate feature "
+                                         " triggered by CTRL when setting workpoints."
+                                         "(Not relevant for rendering)")
+                                         , NULL);
+  g_signal_connect (G_OBJECT (adj), "value_changed"
+                   , G_CALLBACK (gimp_int_adjustment_update)
+                   , &mgup->mgpp->locateDetailMoveRadius);
+
+  /* locate shape radius spinbutton */
+  adj = gtk_adjustment_new ( mgup->mgpp->locateDetailShapeRadius
+                           , GAP_LOCATE_MIN_REF_SHAPE_RADIUS
+                           , GAP_LOCATE_MAX_REF_SHAPE_RADIUS
+                           , 1, 10, 0);
+  mgup->locate_shape_radius_spinbutton_adj = adj;
+  spinbutton = gtk_spin_button_new (GTK_ADJUSTMENT (adj), 1, 0);
+  gtk_widget_show (spinbutton);
+
+  gtk_table_attach (GTK_TABLE (table), spinbutton, 2, 3, row, row+1,
+                    (GtkAttachOptions) (0),
+                    (GtkAttachOptions) (0),
+                     0, 0);
+
+  gtk_widget_set_size_request (spinbutton, 80, -1);
+  gimp_help_set_help_data (spinbutton, _("Locate Shaperadius in pixels."
+                                         " Defines shape size as area around workpoint to be compared "
+                                         " when loacting corresponding coordinate"
+                                         "(Not relevant for rendering)")
+                                         , NULL);
+  g_signal_connect (G_OBJECT (adj), "value_changed"
+                   , G_CALLBACK (gimp_int_adjustment_update)
+                   , &mgup->mgpp->locateDetailShapeRadius);
+
+
+
+  /* locate edge detection threshold spinbutton */
+  adj = gtk_adjustment_new ( mgup->mgpp->edgeColordiffThreshold
+                           , 0.01
+                           , 0.35
+                           , 0.01, 0.1, 0.0);
+  mgup->locate_edge_threshold_spinbutton_adj = adj;
+  spinbutton = gtk_spin_button_new (GTK_ADJUSTMENT (adj), 0.01, 2);
+  gtk_widget_show (spinbutton);
+
+  gtk_table_attach (GTK_TABLE (table), spinbutton, 3, 4, row, row+1,
+                    (GtkAttachOptions) (0),
+                    (GtkAttachOptions) (0),
+                     0, 0);
+
+  gtk_widget_set_size_request (spinbutton, 80, -1);
+  gimp_help_set_help_data (spinbutton, _("Edge detection threshold"
+                                         " for automatically point locate feature "
+                                         " triggered by CTRL when setting workpoints."
+                                         "(Not relevant for rendering)")
+                                         , NULL);
+  g_signal_connect (G_OBJECT (adj), "value_changed"
+                   , G_CALLBACK (gimp_double_adjustment_update)
+                   , &mgup->mgpp->edgeColordiffThreshold);
+
+
+
   /* the create tween checkbutton */
   checkbutton = gtk_check_button_new_with_label ( _("Create Layers"));
   mgup->create_tween_layers_checkbutton = checkbutton;
   gtk_widget_show (checkbutton);
   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (checkbutton), mgup->mgpp->create_tween_layers);
-  gtk_table_attach( GTK_TABLE(table), checkbutton, 0, 2, row, row+1,
+  gtk_table_attach( GTK_TABLE(table), checkbutton, 5, 6, row, row+1,
                     GTK_FILL, 0, 0, 0 );
   g_signal_connect (checkbutton, "toggled",
                     G_CALLBACK (on_create_tween_layers_toggled_callback),
@@ -3958,7 +4006,7 @@ gap_morph_create_dialog(GapMorphGUIParams *mgup)
   checkbutton = gtk_check_button_new_with_label ( _("Quality"));
   mgup->use_quality_wp_selection_checkbutton = checkbutton;
   gtk_widget_show (checkbutton);
-  gtk_table_attach( GTK_TABLE(table), checkbutton, 4, 5, row, row+1,
+  gtk_table_attach( GTK_TABLE(table), checkbutton, 6, 7, row, row+1,
                     GTK_FILL, 0, 0, 0 );
   g_signal_connect (checkbutton, "toggled",
                     G_CALLBACK (on_use_quality_wp_selection_toggled_callback),
@@ -3973,7 +4021,7 @@ gap_morph_create_dialog(GapMorphGUIParams *mgup)
   checkbutton = gtk_check_button_new_with_label ( _("Lines"));
   mgup->show_lines_checkbutton = checkbutton;
   gtk_widget_show (checkbutton);
-  gtk_table_attach( GTK_TABLE(table), checkbutton, 6, 7, row, row+1,
+  gtk_table_attach( GTK_TABLE(table), checkbutton, 7, 8, row, row+1,
                     GTK_FILL, 0, 0, 0 );
   g_signal_connect (checkbutton, "toggled",
                     G_CALLBACK (on_show_lines_toggled_callback),
@@ -3983,6 +4031,12 @@ gap_morph_create_dialog(GapMorphGUIParams *mgup)
                        , NULL);
 
 
+  /* the progress bar */
+  mgup->progressBar = gtk_progress_bar_new ();
+  gtk_progress_bar_set_text(GTK_PROGRESS_BAR(mgup->progressBar), " ");
+  gtk_widget_hide (mgup->progressBar);
+  gtk_table_attach( GTK_TABLE(table), mgup->progressBar, 8, 10, row, row+1,
+                    GTK_FILL, 0, 0, 0 );
 
 
 
@@ -4019,6 +4073,8 @@ gap_morph_dialog(GapMorphGlobalParams *mgpp)
 
   mgup = &morph_gui_params;
   mgup->mgpp = mgpp;
+  mgup->workpointGenerationBusy = FALSE;
+  mgup->cancelWorkpointGeneration = FALSE;
 
   if(gap_debug) printf("gap_morph_dialog: START mgpp->master_wp_list: %d\n", (int)mgpp->master_wp_list);
 
@@ -4033,7 +4089,7 @@ gap_morph_dialog(GapMorphGlobalParams *mgpp)
   gtk_widget_show (mgup->shell);
 
   /* fit both src and dst into preview size
-   * (this implicite forces full refresh)
+   * (this implicitly forces full refresh)
    */
   p_scale_wp_list(mgup);
   p_fit_zoom_into_pview_size(&mgup->src_win);
diff --git a/gap/gap_morph_dialog.h b/gap/gap_morph_dialog.h
index 75dbce8..20f3670 100644
--- a/gap/gap_morph_dialog.h
+++ b/gap/gap_morph_dialog.h
@@ -78,6 +78,9 @@ typedef struct GapMorphGUIParams  { /* nickname: mgup */
 
   GtkObject  *tween_steps_spinbutton_adj;
   GtkObject  *affect_radius_spinbutton_adj;
+  GtkObject  *locate_edge_threshold_spinbutton_adj;
+  GtkObject  *locate_radius_spinbutton_adj;
+  GtkObject  *locate_shape_radius_spinbutton_adj;
   GtkObject  *gravity_intensity_spinbutton_adj;
   GtkWidget  *gravity_intensity_spinbutton;
   GtkWidget  *use_gravity_checkbutton;
@@ -121,6 +124,10 @@ typedef struct GapMorphGUIParams  { /* nickname: mgup */
   GtkWidget *workpoint_file_upper_label;  
   GtkWidget *workpoint_lower_label;  
   GtkWidget *workpoint_upper_label;
+
+  GtkWidget *progressBar;
+  gboolean   workpointGenerationBusy;
+  gboolean   cancelWorkpointGeneration;
   
 } GapMorphGUIParams;
 
diff --git a/gap/gap_morph_exec.c b/gap/gap_morph_exec.c
index cc4969a..45d37ca 100644
--- a/gap/gap_morph_exec.c
+++ b/gap/gap_morph_exec.c
@@ -64,7 +64,6 @@
  * (gives bad quality results, you should NOT enable this option)
  */
 #undef  GAP_MORPH_USE_SIMPLE_PIXEL_WARP
-//#define  GAP_MORPH_USE_SIMPLE_PIXEL_WARP
 
 #define GAP_MORPH_NEAR_SQR_RADIUS  64
 #define GAP_MORPH_NEAR_RADIUS  4
@@ -1732,7 +1731,6 @@ p_calculate_work_point_movement(gdouble total_steps
   gdouble            scaley;
 
 
-  if(gap_debug) printf ("p_calculate_work_point_movement: START\n");
 
   if(forward_move)
   {
@@ -1744,10 +1742,31 @@ p_calculate_work_point_movement(gdouble total_steps
     scalex = (gdouble)curr_width  / (gdouble)gimp_drawable_width(dst_layer_id);
     scaley = (gdouble)curr_height / (gdouble)gimp_drawable_height(dst_layer_id);
   }
+
+  if(gap_debug)
+  {
+    gint32  countPoints;
+
+    countPoints = 0;
+    for(wp_elem_orig = master_list; wp_elem_orig != NULL; wp_elem_orig = (GapMorphWorkPoint *)wp_elem_orig->next)
+    {
+      countPoints++;
+    }
+    printf ("p_calculate_work_point_movement: START total_steps:%.2f current_step:%.2f forward_move:%d scalex:%.3f scaley:%.3f master_list:%d countPoints:%d\n"
+      ,(float)total_steps
+      ,(float)current_step
+      ,(int)forward_move
+      ,(float)scalex
+      ,(float)scaley
+      ,(int)master_list
+      ,(int)countPoints
+      );
+  }
   
   wp_list = NULL;
   wp_elem_prev = NULL;
   
+  
   for(wp_elem_orig = master_list; wp_elem_orig != NULL; wp_elem_orig = (GapMorphWorkPoint *)wp_elem_orig->next)
   {
     wp_elem_dup = g_new(GapMorphWorkPoint, 1);
@@ -1964,8 +1983,8 @@ p_layer_warp_move (GapMorphWorkPoint     *wp_list_1
            if(mgpp->have_workpointsets)
            {
              /* pick based on 2 sets of workpoints */
-             p_pixel_warp_multipick(wcap_1  /// XXXXX list1
-                   , wcap_2                 /// XXXXX list2
+             p_pixel_warp_multipick(wcap_1  /*  list1 */
+                   , wcap_2                 /*  list2 */
                    , wp_mix_factor
                    , l_col
                    , l_row
@@ -2014,7 +2033,14 @@ p_layer_warp_move (GapMorphWorkPoint     *wp_list_1
         * }
         */
         
-       gimp_progress_update(l_total_progress);
+       if(mgpp->progress_callback_fptr == NULL)
+       {
+         gimp_progress_update(l_total_progress);
+       }
+       else
+       {
+         (*mgpp->progress_callback_fptr)(l_total_progress, mgpp->callback_data_ptr);
+       }
      }
 
   }
@@ -2405,6 +2431,16 @@ p_create_morph_tween_frame(gint32 total_steps
      }
      else
      {
+       if(gap_debug)
+       {
+         printf("simple workpoint setting for BG LAYER total_steps:%.2f current_step:%.2f forward_move:%d  wp_mix_factor:%.3f\n"
+            , (float)total_steps
+            , (float)current_step
+            , (int)forward_move
+            , (float)wp_mix_factor
+            );
+       }
+     
        /* simple workpoint settings for src_layer */
        curr_wp_list_1 = p_calculate_work_point_movement((gdouble)total_steps
                                                      ,(gdouble)current_step
@@ -2472,6 +2508,16 @@ p_create_morph_tween_frame(gint32 total_steps
    }
    else
    {
+     if(gap_debug)
+     {
+       printf("simple workpoint setting for TOP LAYER total_steps:%.2f current_step:%.2f forward_move:%d  wp_mix_factor:%.3f\n"
+          , (float)total_steps
+          , (float)current_step
+          , (int)forward_move
+          , (float)wp_mix_factor
+          );
+     }
+
      /* simple workpoint settings for sc_layer */
      curr_wp_list_1 = p_calculate_work_point_movement((gdouble)total_steps
                                                    ,(gdouble)current_step
@@ -2506,11 +2552,7 @@ p_create_morph_tween_frame(gint32 total_steps
    }
 
    /* merge BG and TOP Layer (does mix opacity according to current step) */
-//   merged_layer_id = 
-//   gap_image_merge_visible_layers(curr_image_id, GIMP_EXPAND_AS_NECESSARY);
-
-   merged_layer_id = 
-   p_mix_layers(curr_image_id
+   merged_layer_id = p_mix_layers(curr_image_id
                ,bg_layer_id
                ,top_layer_id
                ,curr_width
@@ -2601,7 +2643,10 @@ p_get_tween_steps_and_layerstacks(GapMorphGlobalParams *mgpp, GapMorphExeLayerst
     for(ii=0; ii < mlayers->dst_nlayers; ii++)
     {
 
-      if(gap_debug) printf("dst: id[%d]: %d\n", (int)ii, (int)mlayers->dst_layers[ii] );
+      if(gap_debug)
+      {
+        printf("dst: id[%d]: %d\n", (int)ii, (int)mlayers->dst_layers[ii] );
+      }
 
       if(mlayers->dst_layers[ii] == mgpp->fdst_layer_id)
       {
@@ -2716,7 +2761,7 @@ gap_morph_execute(GapMorphGlobalParams *mgpp)
     mgpp->render_mode = GAP_MORPH_RENDER_MODE_WARP;
   }
 
-  if(mgpp->do_progress)
+  if((mgpp->do_progress) && (mgpp->progress_callback_fptr == NULL))
   {
     if(mgpp->render_mode == GAP_MORPH_RENDER_MODE_MORPH)
     {
@@ -2825,7 +2870,14 @@ gap_morph_execute(GapMorphGlobalParams *mgpp)
     if(mgpp->do_progress)
     {
       mgpp->master_progress = (gdouble)current_step / (gdouble)(MAX(1, last_step));
-      gimp_progress_update(mgpp->master_progress);
+      if(mgpp->progress_callback_fptr == NULL)
+      {
+        gimp_progress_update(mgpp->master_progress);
+      }
+      else
+      {
+        (*mgpp->progress_callback_fptr)(mgpp->master_progress, mgpp->callback_data_ptr);
+      }
     }
 
     
@@ -2912,6 +2964,13 @@ p_create_simple_fade_tween_frame(gint32 total_steps
                                 ,(gdouble)0.0
                                 ,(gdouble)100.0
                                 );
+   if(mgpp->do_progress)
+   {
+     if(mgpp->progress_callback_fptr != NULL)
+     {
+         (*mgpp->progress_callback_fptr)(0.05, mgpp->callback_data_ptr);
+     }
+   }
 
    merged_layer_id = 
    p_mix_layers(curr_image_id
@@ -2922,8 +2981,16 @@ p_create_simple_fade_tween_frame(gint32 total_steps
                ,(gdouble)(curr_opacity / 100.0)
                );
  
+   if(mgpp->do_progress)
+   {
+     if(mgpp->progress_callback_fptr != NULL)
+     {
+         (*mgpp->progress_callback_fptr)(1.0, mgpp->callback_data_ptr);
+     }
+   }
    mgpp->master_progress += mgpp->layer_progress_step;
 
+
    /* DEBUG code: show duplicate of the temporary tween image */
    if(FALSE)
    {
@@ -2956,6 +3023,8 @@ gap_morph_render_one_of_n_tweens(GapMorphGlobalParams *mgpp, gdouble total_steps
   gint32 new_layer_id;
   GapMorphExeLayerstack mlayers_struct;
   GapMorphExeLayerstack *mlayers;
+  gint32              bck_tween_steps;
+  gint32              ret_layer_id;
   
   mlayers = &mlayers_struct;
   mlayers->tab_wp_sets = NULL;
@@ -2971,21 +3040,29 @@ gap_morph_render_one_of_n_tweens(GapMorphGlobalParams *mgpp, gdouble total_steps
   {
     if(*mgpp->workpoint_file_lower != '\0')
     {
-      /* load a single workpointfile
-       * (note: we use general procedure p_build_wp_set_table and handle the single workpointfile
-       *  as mix of upper and lower set that both refere to the same single workpointfile)
-       */
-      p_build_wp_set_table(mgpp ,mlayers);
+      /* load a single workpointfile */
+      mlayers->available_wp_sets = 1;
+      mlayers->tab_wp_sets = g_new(GapMorphWarpCoreAPI*, mlayers->available_wp_sets);
+      
+      mlayers->tab_wp_sets[0] = p_load_workpoint_set(mgpp->workpoint_file_lower, mgpp, mlayers);
+      mgpp->master_wp_list = mlayers->tab_wp_sets[0]->wp_list;
+      
       mgpp->have_workpointsets = FALSE;
       mlayers->available_wp_sets = 0;
     }
   }
   
   /* overrule the number of steps with the explicite parameter value */
+  bck_tween_steps = mgpp->tween_steps;
   mgpp->tween_steps = total_steps;
 
   if(mgpp->do_simple_fade)
   {
+    if(gap_debug)
+    {
+      printf("do_simple_fade == TRUE, skip morph and do fade operation instead\n");
+    }
+
     /* we have no workpoints available
      * in this case make an attempt with a fast fade operation
      *  (but restricted to frames of same size and type)
@@ -3012,7 +3089,13 @@ gap_morph_render_one_of_n_tweens(GapMorphGlobalParams *mgpp, gdouble total_steps
                           ,mlayers
                           );
   }
-  return (gap_image_merge_to_specified_layer(new_layer_id, GIMP_CLIP_TO_IMAGE));
+  ret_layer_id = gap_image_merge_to_specified_layer(new_layer_id, GIMP_CLIP_TO_IMAGE);
+
+  /* restore tween_steps value */
+  mgpp->tween_steps = bck_tween_steps;
+  
+  return(ret_layer_id);
+  
 }  /* end gap_morph_render_one_of_n_tweens */
 
 
@@ -3045,6 +3128,505 @@ gap_morph_render_one_tween(GapMorphGlobalParams *mgpp)
 
 
 
+
+/* -----------------------------------------------
+ * p_buildTargetTweenFilename
+ * -----------------------------------------------
+ * build filename for the target Tween frame with the specified targetFrameNr number part
+ * with full directory path (specified by tweenDirectory)
+ *
+ * the caller is rsponsible to g_free the returned filename.
+ */
+static char*
+p_buildTargetTweenFilename(GapAnimInfo *ainfo_ptr, const char *tweenDirectory, gint32 targetFrameNr)
+{
+  char *fileBasenameWithoutDir;
+  char *fileNameTarget;
+  char *fileNameTargetWithDir;
+  char *checkName;
+  
+  
+  fileBasenameWithoutDir = g_path_get_basename(ainfo_ptr->basename);
+  fileNameTarget = g_strdup_printf("%s%06d%s"
+                          , fileBasenameWithoutDir
+                          , (int)targetFrameNr
+                          , ainfo_ptr->extension
+                          );
+
+  checkName = g_build_filename(tweenDirectory, fileBasenameWithoutDir, NULL);
+  
+  if((strcmp(checkName, ainfo_ptr->basename) == 0)
+  || (*tweenDirectory == '.'))
+  {
+    if(gap_debug)
+    {
+      printf("WARNING: create tween in directory: %s\n"
+           "  with additional prefix tween_"
+           "  to prevent overwrite of source frames in the same directory\n"
+        , tweenDirectory
+        );
+    }
+    fileNameTarget = g_strdup_printf("tween_%s%06d%s"
+                          , fileBasenameWithoutDir
+                          , (int)targetFrameNr
+                          , ainfo_ptr->extension
+                          );
+  }
+  else
+  {
+    fileNameTarget = g_strdup_printf("%s%06d%s"
+                          , fileBasenameWithoutDir
+                          , (int)targetFrameNr
+                          , ainfo_ptr->extension
+                          );
+  }
+
+  fileNameTargetWithDir = g_build_filename(tweenDirectory, fileNameTarget, NULL);
+
+
+  if(gap_debug)
+  {
+    printf("checkName: %s\n", checkName);
+    printf("fileBasenameWithoutDir: %s\n", fileBasenameWithoutDir);
+    printf("fileNameTarget: %s\n", fileNameTarget);
+    printf("fileNameTargetWithDir: %s\n", fileNameTargetWithDir);
+  }
+  
+  if(fileBasenameWithoutDir)    { g_free(fileBasenameWithoutDir); }
+  if(fileNameTarget)            { g_free(fileNameTarget); }
+  if(checkName)                 { g_free(checkName); }
+
+  return(fileNameTargetWithDir);
+
+}  /* end p_buildTargetTweenFilename */
+
+
+/* -----------------------------------------------
+ * p_copyAndGetMergedFrameImageLayer
+ * -----------------------------------------------
+ * load the specified frame as temporary image, merge to one layer and return this layer's id.
+ * further copy the original image to the directory specified via tweenDirectory
+ * the filename of the copy is built with original filename and extension but the number part is replaced
+ * by the specified targetFrameNr.
+ *
+ * return -1 if FAILED, or layerId (positive integer) on success
+ */
+gint32
+p_copyAndGetMergedFrameImageLayer(GapAnimInfo *ainfo_ptr, gint32 frameNr
+   , const char *tweenDirectory, gint32 targetFrameNr, gboolean overwrite_flag)
+{
+  gint32 imageId;
+  gint32 layerId;
+
+  layerId = -1;
+  if(ainfo_ptr->new_filename != NULL)
+  {
+    g_free(ainfo_ptr->new_filename);
+  }
+  ainfo_ptr->new_filename = gap_lib_alloc_fname(ainfo_ptr->basename,
+                                      frameNr,
+                                      ainfo_ptr->extension);
+  if(ainfo_ptr->new_filename == NULL)
+  {
+     printf("could not create frame filename for frameNr:%d\n", (int)frameNr);
+     return -1;
+  }
+
+  if(!g_file_test(ainfo_ptr->new_filename, G_FILE_TEST_EXISTS))
+  {
+     printf("file not found: %s\n", ainfo_ptr->new_filename);
+     return -1;
+  }
+
+  /* load current frame */
+  imageId = gap_lib_load_image(ainfo_ptr->new_filename);
+  if(imageId >= 0)
+  {
+    char *targetTweenFilename;
+    
+    layerId = gap_image_merge_visible_layers(imageId, GIMP_CLIP_TO_IMAGE);
+    
+    targetTweenFilename = p_buildTargetTweenFilename(ainfo_ptr, tweenDirectory, targetFrameNr);
+    
+    if(gap_debug)
+    {
+      printf("COPY: %s ==> %s\n", ainfo_ptr->new_filename, targetTweenFilename);
+    }
+
+
+    if((!g_file_test(targetTweenFilename, G_FILE_TEST_EXISTS))
+    || (overwrite_flag == TRUE))
+    {
+      gap_lib_file_copy(ainfo_ptr->new_filename /* the original file */
+                     ,targetTweenFilename     /* the copy */
+                     );
+    }
+    
+
+  }
+  
+  return(layerId);
+
+}  /* end p_copyAndGetMergedFrameImageLayer */
+
+
+
+
+/* -------------------------------------
+ * p_morph_render_frame_tweens_in_subdir
+ * -------------------------------------
+ *
+ * return one of the newly created morphed tween frame layers
+ *       (the one that was created last is picked)
+ *
+ */
+gint32
+p_morph_render_frame_tweens_in_subdir(GapAnimInfo *ainfo_ptr, GapMorphGlobalParams *mgpp, gboolean *cancelFlagPtr)
+{
+  char   *tweenDirectory;
+  char   *targetTweenFrameFilename;
+  gint32 l_tween_layer_id;
+  gint32 l_rc;
+  gint   l_errno;
+  gint32 frameCount;
+  gint32 currFrameNr;
+  gint32 nextFrameNr;
+  gint32 nextTargetFrameNr;
+  gint32 baseTargetFrameNr;
+  gint32 currTargetFrameNr;
+  gint32 currLayerId;
+  gint32 nextLayerId;
+  gint32 firstFrameNr;
+  gint32 lastFrameNr;
+  gdouble framesToProcess;
+  gdouble nTweenFramesTotal;   /* for outer progress (frames that are just copied are not included in progress) */
+  gdouble nTweenFramesDone;    /* rendered tween frames so far (frames that are just copied are not included) */
+
+
+
+  l_tween_layer_id = -1;
+  l_errno = 0;
+  targetTweenFrameFilename = NULL;
+
+  if(gap_debug)
+  {
+    printf("p_morph_render_frame_tweens_in_subdir  START\n");
+  }
+  
+  if((mgpp->tween_subdir[0] == '\0') || (mgpp->tween_subdir[0] == '.'))
+  {
+    /* use same directory as source frames
+     * (in this case tween frame filenames are automatically prefixed with "tween_"
+     * to prevent overwrite of the source frames while processing)
+     */
+    tweenDirectory = g_path_get_dirname(ainfo_ptr->basename);
+  }
+  else
+  {
+    tweenDirectory = gap_file_make_abspath_filename(&mgpp->tween_subdir[0]
+                     , ainfo_ptr->basename
+                     );
+  }
+  
+
+  if(gap_debug)
+  {
+    printf("p_morph_render_frame_tweens_in_subdir  tweenDirectory:%s\n"
+       ,tweenDirectory
+       );
+  }
+
+
+
+  if (!g_file_test(tweenDirectory, G_FILE_TEST_IS_DIR))
+  {
+    gap_file_mkdir (tweenDirectory, GAP_FILE_MKDIR_MODE);
+    l_errno = errno;
+  }
+
+  if (!g_file_test(tweenDirectory, G_FILE_TEST_IS_DIR))
+  {
+    g_message (_("Failed to create tween subdirectory: '%s':\n%s")
+                   , tweenDirectory
+                   , g_strerror (l_errno)
+                   );
+    return (-1);
+  }
+
+
+  frameCount = 0;
+  nextLayerId = -1;
+ 
+  if(gap_lib_chk_framerange(ainfo_ptr) != 0)
+  {
+    return(0);
+  }
+  
+  
+  firstFrameNr = CLAMP(mgpp->range_from, ainfo_ptr->first_frame_nr, ainfo_ptr->last_frame_nr);
+  currFrameNr = firstFrameNr;
+  currTargetFrameNr = firstFrameNr;
+  nextTargetFrameNr = firstFrameNr;
+  baseTargetFrameNr = firstFrameNr;
+
+  currLayerId = p_copyAndGetMergedFrameImageLayer(ainfo_ptr
+                      , currTargetFrameNr
+                      , tweenDirectory
+                      , currTargetFrameNr
+                      , mgpp->overwrite_flag
+                      );
+  nextFrameNr = currFrameNr + 1;
+
+
+  lastFrameNr = MIN(mgpp->range_to, ainfo_ptr->last_frame_nr);
+  nTweenFramesTotal = MAX(1.0, (lastFrameNr - firstFrameNr) * mgpp->master_tween_steps);
+  nTweenFramesDone = 0;
+
+  /* loop to process all frames within the specified frame range */
+  while(TRUE)
+  {
+    gdouble nFrames;
+    gint32  tweenFramesToBeCreated;
+    gdouble l_current_step;
+
+    /* recalculate (in case range_to is changed on GUI while processing is already running) */
+    lastFrameNr = MIN(mgpp->range_to, ainfo_ptr->last_frame_nr);
+    framesToProcess = MAX(1, (lastFrameNr - currFrameNr) -1);
+    nTweenFramesTotal = MAX(1.0, (lastFrameNr - firstFrameNr) * mgpp->master_tween_steps);
+   
+    if (nextFrameNr > lastFrameNr)
+    {
+      if(gap_debug)
+      {
+        printf("BREAK nextFrameNr:%d lastFrameNr:%d\n"
+          ,(int)nextFrameNr
+          ,(int)lastFrameNr
+          );
+      }
+     
+      break;
+    }
+
+    nFrames = nextFrameNr - currFrameNr;
+    tweenFramesToBeCreated = (nFrames * mgpp->master_tween_steps) + (nFrames -1);
+
+    nextTargetFrameNr = baseTargetFrameNr + (1 + tweenFramesToBeCreated);
+    
+    if(gap_debug)
+    {
+      printf("ATTR currFrameNr:%d  nextFrameNr:%d  master_tween_steps:%d  tweenFramesToBeCreated:%d  baseTargetFrameNr:%d  nextTargetFrameNr:%d\n"
+        ,(int)currFrameNr
+        ,(int)nextFrameNr
+        ,(int)mgpp->master_tween_steps
+        ,(int)tweenFramesToBeCreated
+        ,(int)baseTargetFrameNr
+        ,(int)nextTargetFrameNr
+        );
+    }
+    nextLayerId = p_copyAndGetMergedFrameImageLayer(ainfo_ptr
+                         , nextFrameNr
+                         , tweenDirectory
+                         , nextTargetFrameNr
+                         , mgpp->overwrite_flag
+                         );
+    
+    if((nextLayerId >= 0) && (currLayerId >= 0))
+    {
+      char *workpointFileName;
+      gboolean success;
+        
+   
+      success = TRUE;
+      baseTargetFrameNr = nextTargetFrameNr;
+
+      workpointFileName = gap_lib_alloc_fname(ainfo_ptr->basename,
+                                      currFrameNr,
+                                      "." GAP_MORPH_WORKPOINT_EXTENSION);      
+      if(g_file_test(workpointFileName, G_FILE_TEST_EXISTS))
+      {
+        if(gap_debug)
+        {
+          printf("USING workpointfile: %s\n"
+                 , workpointFileName
+                 );
+        }
+        g_snprintf(&mgpp->workpoint_file_lower[0]
+                , sizeof(mgpp->workpoint_file_lower)
+                , "%s"
+                , workpointFileName
+                );
+        g_snprintf(&mgpp->workpoint_file_upper[0]
+                , sizeof(mgpp->workpoint_file_upper)
+                , "%s"
+                , workpointFileName
+                );
+        
+        mgpp->do_simple_fade = FALSE;
+      }
+      else
+      {
+        if(gap_debug)
+        {
+          printf("have no valid frame specific workpointfile: %s\n"
+                 "  (reset names to force simple fade operaton\n"
+                 , workpointFileName
+                 );
+        }
+        mgpp->workpoint_file_lower[0] = '\0';
+        mgpp->workpoint_file_upper[0] = '\0';
+        mgpp->do_simple_fade = TRUE;
+      }
+
+      
+       mgpp->osrc_layer_id = currLayerId;
+       mgpp->fdst_layer_id = nextLayerId;
+
+      /* loop to generate tween frames between currentFrameNr and nextFrameNr */
+      for (l_current_step = 1; l_current_step <= tweenFramesToBeCreated; l_current_step++)
+      {
+        gint32  l_tween_tmp_image_id;
+        
+        currTargetFrameNr++;
+
+        mgpp->master_progress = 0.0;
+        mgpp->layer_progress_step = 0.5;
+
+        if(l_tween_layer_id >= 0)
+        {
+          /* delete the previous handled tween frame image in memory. 
+           * (that is already saved to disk)
+           * we keep only the last one opened in gimp.
+           */
+          gap_image_delete_immediate(gimp_drawable_get_image(l_tween_layer_id));
+        }
+
+        if(targetTweenFrameFilename != NULL)
+        {
+          g_free(targetTweenFrameFilename);
+        }
+        targetTweenFrameFilename = p_buildTargetTweenFilename(ainfo_ptr
+                                            , tweenDirectory
+                                            , currTargetFrameNr);
+        if(targetTweenFrameFilename == NULL)
+        {
+          success = FALSE;
+          break;
+        }
+        
+        if(gap_debug)
+        {
+          printf("p_morph_render_frame_tweens_in_subdir creating tween:%s\n"
+             , targetTweenFrameFilename
+             );
+        }
+
+        /* progress and cancel handling */
+        if(mgpp->do_progress)
+        {
+           gdouble percentage;
+           percentage = nTweenFramesDone / nTweenFramesTotal;
+           if(gap_debug)
+           {
+             printf("\nTWEEN_PROGRESS: nTweenFramesDone:%.0f nTweenFramesTotal:%.0f percentage:%.5f\n\n"
+               , (float)nTweenFramesDone
+               , (float)nTweenFramesTotal
+               , (float)percentage
+               );
+           }
+           if(mgpp->master_progress_callback_fptr != NULL)
+           {
+             (*mgpp->master_progress_callback_fptr)( nTweenFramesDone
+                                                   , nTweenFramesTotal
+                                                   , targetTweenFrameFilename
+                                                   , mgpp->callback_data_ptr
+                                                   );           
+           }
+           if(cancelFlagPtr != NULL)
+           {
+             if (*cancelFlagPtr == TRUE)
+             {
+                success = FALSE;
+                break;
+             }           
+           }
+        }
+
+        if (mgpp->overwrite_flag != TRUE)
+        {
+          if(g_file_test(targetTweenFrameFilename, G_FILE_TEST_EXISTS))
+          {
+            l_tween_layer_id = -1;
+            if(gap_debug)
+            {
+              printf("SKIP tween generation because file: %s already exists"
+                 , targetTweenFrameFilename
+                 );
+            }
+            continue;
+          }
+        }
+
+
+        /* CALL of the morphing processor */
+
+        l_tween_layer_id = gap_morph_render_one_of_n_tweens(mgpp
+                                                           , tweenFramesToBeCreated +1
+                                                           , l_current_step
+                                                           );
+        l_tween_tmp_image_id = gimp_drawable_get_image(l_tween_layer_id);
+        if(gap_debug)
+        {
+          printf("p_morph_render_frame_tweens_in_subdir saving tween:%s :%d\n"
+             ,targetTweenFrameFilename
+             ,(int)l_tween_tmp_image_id
+             );
+        }
+        l_rc = gap_lib_save_named_frame(l_tween_tmp_image_id, targetTweenFrameFilename);
+        if(targetTweenFrameFilename != NULL)
+        {
+          g_free(targetTweenFrameFilename);
+          targetTweenFrameFilename = NULL;
+        }
+        if (l_rc < 0)
+        {
+          l_tween_layer_id = -1;
+          p_error_message_with_filename(mgpp->run_mode, _("file: %s save failed"), targetTweenFrameFilename);
+          success = FALSE;
+          break;
+        }
+
+        nTweenFramesDone++;
+
+      }
+
+      g_free(workpointFileName);
+      gap_image_delete_immediate(gimp_drawable_get_image(currLayerId));
+      if(!success)
+      {
+        break;
+      }
+
+      currFrameNr = nextFrameNr;
+      currLayerId = nextLayerId;
+      currTargetFrameNr++;
+
+    }
+    nextFrameNr++;
+
+  }
+  
+  if(nextLayerId >= 0)
+  {
+    gap_image_delete_immediate(gimp_drawable_get_image(nextLayerId));
+  }
+
+  return(l_tween_layer_id);
+
+}  /* end p_morph_render_frame_tweens_in_subdir */
+
+
+
+
 /* ----------------------------------
  * gap_morph_render_frame_tweens
  * ----------------------------------
@@ -3053,24 +3635,27 @@ gap_morph_render_one_tween(GapMorphGlobalParams *mgpp)
  *
  */
 gint32
-gap_morph_render_frame_tweens(GapAnimInfo *ainfo_ptr, GapMorphGlobalParams *mgpp)
+gap_morph_render_frame_tweens(GapAnimInfo *ainfo_ptr, GapMorphGlobalParams *mgpp, gboolean *cancelFlagPtr)
 {
   gint32 l_tween_layer_id;
   gint32 l_tween_frame_nr;
   gint32 l_rc;
   gdouble total_steps;
 
+
+  if(mgpp->create_tweens_in_subdir == TRUE)
+  {
+    return(p_morph_render_frame_tweens_in_subdir(ainfo_ptr, mgpp, cancelFlagPtr));
+  }
+
+
   l_tween_layer_id = -1;
 
   total_steps = (mgpp->range_to -  mgpp->range_from);
-  mgpp->tween_steps = (mgpp->range_to -  mgpp->range_from); /// ??? - 1;
+  mgpp->tween_steps = (mgpp->range_to -  mgpp->range_from);
 
   mgpp->master_progress = 0.0;
-  mgpp->layer_progress_step = 0.5 / (gdouble)(MAX(1.0, (total_steps -1.0)));
-  if(mgpp->do_progress)
-  {
-    gimp_progress_init(_("creating morph tween frames..."));
-  }
+  mgpp->layer_progress_step = 0.5;
   
   if (mgpp->tween_steps > 0)
   {
@@ -3098,6 +3683,8 @@ gap_morph_render_frame_tweens(GapAnimInfo *ainfo_ptr, GapMorphGlobalParams *mgpp
        return -1;
     }
 
+
+
     /* load current frame */
     l_tmp_image_id = gap_lib_load_image(ainfo_ptr->new_filename);
 
@@ -3126,7 +3713,9 @@ gap_morph_render_frame_tweens(GapAnimInfo *ainfo_ptr, GapMorphGlobalParams *mgpp
       gint32  l_tween_tmp_image_id;
       gdouble l_current_step;
       
-      mgpp->master_progress = 2.0 * mgpp->layer_progress_step * (l_tween_frame_nr - (mgpp->range_from +1));
+      mgpp->master_progress = 0.0;
+      mgpp->layer_progress_step = 0.5;
+
 
       if(l_tween_layer_id >= 0)
       {
@@ -3144,13 +3733,44 @@ gap_morph_render_frame_tweens(GapAnimInfo *ainfo_ptr, GapMorphGlobalParams *mgpp
       ainfo_ptr->new_filename = gap_lib_alloc_fname(ainfo_ptr->basename,
                                           l_tween_frame_nr,
                                           ainfo_ptr->extension);
+      /* progress and cancel handling */
       if(mgpp->do_progress)
       {
-        char *l_msg;
-        l_msg = g_strdup_printf(_("creating morph tween frame: %d"), (int)l_tween_frame_nr);
-        gimp_progress_init(l_msg);
-        g_free(l_msg);
+         gdouble percentage;
+         gdouble nTweenFramesDone;
+         gdouble nTweenFramesTotal;
+         
+         nTweenFramesDone = l_tween_frame_nr - mgpp->range_from;
+         nTweenFramesTotal = mgpp->range_to - mgpp->range_from;
+         
+         percentage = nTweenFramesDone / nTweenFramesTotal;
+         if(gap_debug)
+         {
+           printf("\nTWEEN_PROGRESS: nTweenFramesDone:%.0f nTweenFramesTotal:%.0f percentage:%.5f\n\n"
+             , (float)nTweenFramesDone
+             , (float)nTweenFramesTotal
+             , (float)percentage
+             );
+         }
+         if(mgpp->master_progress_callback_fptr != NULL)
+         {
+           (*mgpp->master_progress_callback_fptr)( nTweenFramesDone
+                                                 , nTweenFramesTotal
+                                                 , ainfo_ptr->new_filename
+                                                 , mgpp->callback_data_ptr
+                                                 );           
+         }
+         if(cancelFlagPtr != NULL)
+         {
+           if (*cancelFlagPtr == TRUE)
+           {
+              break;
+           }           
+         }
       }
+
+
+
       if(gap_debug)
       {
         printf("gap_morph_render_frame_tweens creating tween:%s\n"
diff --git a/gap/gap_morph_exec.h b/gap/gap_morph_exec.h
index b0ca8b8..e973010 100644
--- a/gap/gap_morph_exec.h
+++ b/gap/gap_morph_exec.h
@@ -56,7 +56,7 @@ void    gap_morph_exec_get_warp_pick_koords(GapMorphWorkPoint *wp_list
                                       );
 gint32  gap_morph_execute(GapMorphGlobalParams *mgpp);
 gint32  gap_morph_render_one_tween(GapMorphGlobalParams *mgpp);
-gint32  gap_morph_render_frame_tweens(GapAnimInfo *ainfo_ptr, GapMorphGlobalParams *mgpp);
+gint32  gap_morph_render_frame_tweens(GapAnimInfo *ainfo_ptr, GapMorphGlobalParams *mgpp, gboolean *cancelFlagPtr);
 
 #endif
 
diff --git a/gap/gap_morph_main.c b/gap/gap_morph_main.c
index fb2f7cd..e22b8eb 100644
--- a/gap/gap_morph_main.c
+++ b/gap/gap_morph_main.c
@@ -36,6 +36,7 @@
 
 #include "gap_morph_main.h"
 #include "gap_morph_exec.h"
+#include "gap_morph_shape.h"
 #include "gap_morph_dialog.h"
 #include "gap_morph_tween_dialog.h"
 #include "gap_pview_da.h"
@@ -52,11 +53,13 @@
 #define PLUG_IN_NAME            "plug_in_gap_morph_layers"
 #define PLUG_IN_NAME_TWEEN      "plug_in_gap_morph_tween"      /* render missing tween(s) between frames */
 #define PLUG_IN_NAME_ONE_TWEEN  "plug_in_gap_morph_one_tween"  /* single tween rendering  */
+#define PLUG_IN_NAME_WORKPOINTS "plug_in_gap_morph_workpoints"   /* workpoint generation */
 #define PLUG_IN_PRINT_NAME      "Morph Layers"
 #define PLUG_IN_IMAGE_TYPES     "RGBA, GRAYA"
 #define PLUG_IN_AUTHOR          "Wolfgang Hofer (hof gimp org)"
 #define PLUG_IN_COPYRIGHT       "Wolfgang Hofer"
 
+#define LOCATE_COLORDIFF_THRESHOLD 0.05;
 
 int gap_debug = 0;  /* 1 == print debug infos , 0 dont print debug infos */
 
@@ -86,7 +89,20 @@ static GapMorphGlobalParams global_params =
 , -1          /*  long                range_from */
 , -1          /*  long                range_to */
 , FALSE       /* gboolean            overwrite_flag */
+, FALSE       /* gboolean            append_flag */
 , FALSE       /* gboolean            do_simple_fade */
+, 0.08        /* gdouble             edgeColordiffThreshold */
+, 0.05        /* gdouble             locateColordiffThreshold */
+, 5           /* gint32              locateDetailShapeRadius */
+, 70          /* gint32              locateDetailMoveRadius */
+, 150         /* gint32              numWorkpoints */
+, 32          /* gint32              numOutlinePoints */
+, 1           /* gint32              master_tween_steps */
+, TRUE        /* gboolean            create_tweens_in_subdir */
+, "tween_frames\0"        /* char                tween_subdir[1024] */
+, NULL        /* master_progress_callback_fptr */
+, NULL        /* progress_callback_fptr */
+, NULL        /* gpointer            callback_data_ptr */
 };
 
 
@@ -97,7 +113,7 @@ static void  run (const gchar *name,
                   gint *nreturn_vals,        /* number of parameters returned */
                   GimpParam ** return_vals); /* parameters to be returned */
 
-static gint32   p_handle_PLUG_IN_NAME_TWEEN(GapMorphGlobalParams *mgpp);
+static gint32   p_handle_tween_processing_pug_ins(GapMorphGlobalParams *mgpp, const char *name);
 
 /* Global Variables */
 GimpPlugInInfo PLUG_IN_INFO =
@@ -158,6 +174,22 @@ static GimpParamDef out_one_tween_args[] = {
                   { GIMP_PDB_DRAWABLE, "tween_drawable", "newly created tween (a layer in a newly created image)"},
   };
 
+static GimpParamDef in_workpoint_args[] = {
+                  { GIMP_PDB_INT32,    "run_mode", "Interactive, non-interactive."},
+                  { GIMP_PDB_IMAGE,    "start_image",    "from frame image (must be a frame with GIMP-GAP typical number part in the imagefilename)" },
+                  { GIMP_PDB_DRAWABLE, "drawable", "ignored"},
+                  { GIMP_PDB_INT32,    "to_frame_nr", "frame number of the next available frame"},
+                  { GIMP_PDB_INT32,    "numWorkpoints", "number of workpoints to be generated (per workpoint file)"},
+                  { GIMP_PDB_INT32,    "numOutlinePoints", "number of additional workpoint on outline (per workpoint file)"},
+                  { GIMP_PDB_INT32,    "locateDetailMoveRadius", "radius for locating corresponding coordinate"},
+                  { GIMP_PDB_INT32,    "locateDetailShapeRadius", "shape radius in pixels (3 to 16) for locating corresponding coordinate"},
+                  { GIMP_PDB_FLOAT,    "locateColordiffThreshold", "0.05 (0.01 - 0.1)  "},
+                  { GIMP_PDB_FLOAT,    "edgeColordiffThreshold", "0.05 (0.0 - 1.0) edge detection threshold (the generated workpoints are picked at detected edges) "},
+  };
+static GimpParamDef out_workpoint_args[] = {
+                  { GIMP_PDB_INT32, "num_workpointfiles", "number of successfully generated workpoint files"},
+  };
+
 MAIN ()
 
 static void query (void)
@@ -175,12 +207,12 @@ static void query (void)
                           "(or how much layers to modify depending on the create_tween_layers parameter) "
                           "source and destination may differ in size and can be in different images. "
                           "for WARP render_mode (1) there will be just Move Deformation. "
-                          "Deformation is controled by workpoints. Workpoints are created and edited with the help "
+                          "Deformation is controlled by workpoints. Workpoints are created and edited with the help "
                           "of the INTERACTIVE GUI and saved to file(s). "
                           "For the NON-INTERACTIVE runmode you must provide the filename of such a file. "
                           "Provide 2 filenames if you want to operate with multiple workpoint sets. "
                           "In that case your workpoint files can have a 2 digit numberpart. "
-                          "This will implicite select all filenames with numbers inbetween as well."
+                          "This will implicitly select all filenames with numbers in between as well."
                           ,
                           PLUG_IN_AUTHOR,
                           PLUG_IN_COPYRIGHT,
@@ -196,7 +228,7 @@ static void query (void)
 
   /* the actual installation of the plugin */
   gimp_install_procedure (PLUG_IN_NAME_TWEEN,
-                          "Render tween frames via morhing",
+                          "Render tween frames via morphing",
                           "This plug-in creates and saves image frames that are a mix of the specified image frame and the frame with to_frame_nr, "
                           "The typical usage is to create the frame images of missing frame numbers in a series of anim frame images. "
                           "the overwrite flag allows overwriting of already existing frames between the start frame image "
@@ -220,12 +252,12 @@ static void query (void)
 
   /* the actual installation of the plugin */
   gimp_install_procedure (PLUG_IN_NAME_ONE_TWEEN,
-                          "Render one tween via morhing",
+                          "Render one tween via morphing",
                           "This plug-in creates a new image that is a mix of the specified src_drawable and dst_drawable, "
                           "the mixing is done based on a morphing transformation where the tween_mix_factor "
                           "determines how much the result looks like source or destination. "
                           "source and destination may differ in size and can be in different images. "
-                          "Morphing is controled by workpoints. A Workpoint file can be created and edited with the help "
+                          "Morphing is controlled by workpoints. A Workpoint file can be created and edited with the help "
                           "of the Morph feature in the Video menu. "
                           "Note: without workpoints the resulting tween is calculated as simple fade operation. "
                           ,
@@ -242,54 +274,81 @@ static void query (void)
                           );
 
 
+  /* the actual installation of the plugin */
+  gimp_install_procedure (PLUG_IN_NAME_WORKPOINTS,
+                          "Generate workpoint files for tween frame morphing",
+                          "This plug-in generates workpoint files for the specifed frame range."
+                          "The generated files are saved with the same name as the frame name(s) "
+                          "but with extension .morphpoints "
+                          ,
+                          PLUG_IN_AUTHOR,
+                          PLUG_IN_COPYRIGHT,
+                          GAP_VERSION_WITH_DATE,
+                          N_("Morph Workpoint Generator..."),
+                          PLUG_IN_IMAGE_TYPES,
+                          GIMP_PLUGIN,
+                          G_N_ELEMENTS (in_workpoint_args),
+                          G_N_ELEMENTS (out_workpoint_args),
+                          in_workpoint_args,
+                          out_workpoint_args
+                          );
+
   {
     /* Menu names */
     const char *menupath_image_video_morph = N_("<Image>/Video/Morph/");
 
-    //gimp_plugin_menu_branch_register("<Image>", "Video/Morph");
-  
     gimp_plugin_menu_register (PLUG_IN_NAME,           menupath_image_video_morph);
     gimp_plugin_menu_register (PLUG_IN_NAME_TWEEN,     menupath_image_video_morph);
     gimp_plugin_menu_register (PLUG_IN_NAME_ONE_TWEEN, menupath_image_video_morph);
+    gimp_plugin_menu_register (PLUG_IN_NAME_WORKPOINTS, menupath_image_video_morph);
   }
 }
 
 /* ----------------------------------
- * p_handle_PLUG_IN_NAME_TWEEN
+ * p_handle_tween_processing_pug_ins
  * ----------------------------------
  *
  * return the newly created tween morphed layer
  *
  */
 static gint32
-p_handle_PLUG_IN_NAME_TWEEN(GapMorphGlobalParams *mgpp)
+p_handle_tween_processing_pug_ins(GapMorphGlobalParams *mgpp, const char *name)
 {
   gint32 l_tween_layer_id;
   GapAnimInfo *ainfo_ptr;
 
-  gboolean       l_rc;
   gboolean       l_run_flag;
 
   l_tween_layer_id = -1;
-  l_rc = FALSE;
   l_run_flag = TRUE;
 
+  if(gap_debug)
+  {
+    printf("p_handle_tween_processing_pug_ins name:%s\n", name);
+  }
+
   ainfo_ptr = gap_lib_alloc_ainfo(mgpp->image_id, mgpp->run_mode);
   if(ainfo_ptr != NULL)
   {
     if (0 == gap_lib_dir_ainfo(ainfo_ptr))
     {
       mgpp->range_from = ainfo_ptr->curr_frame_nr;
-      /* mgpp->range_to is already set at noninteractive call. for interacive cals this is set in the following dialog
+      /* mgpp->range_to is already set at non-interactive call. for interactive calls this is set in the following dialog
        */
       if(mgpp->run_mode == GIMP_RUN_INTERACTIVE)
       {
+         if(gap_lib_chk_framerange(ainfo_ptr) != 0)
+         {
+           return(-1);
+         }
+         
          if(0 != gap_lib_chk_framechange(ainfo_ptr)) { l_run_flag = FALSE; }
          else
          {
-           if(*ainfo_ptr->extension == '\0' && ainfo_ptr->frame_cnt == 0)
+           if((*ainfo_ptr->extension == '\0') 
+           || (ainfo_ptr->frame_cnt == 0))
            {
-             /* plugin was called on a frame without extension and without framenumer in its name
+             /* plugin was called on a frame without extension and without framenumber in its name
               * (typical for new created images named like 'Untitled' 
               */
                g_message(_("Operation cancelled.\n"
@@ -298,11 +357,28 @@ p_handle_PLUG_IN_NAME_TWEEN(GapMorphGlobalParams *mgpp)
                          "==> Rename your image, then try again."));
                return -1;
            }
-           l_rc = gap_morph_frame_tweens_dialog(ainfo_ptr, mgpp);
+           if (strcmp(name, PLUG_IN_NAME_TWEEN) == 0)
+           {
+             l_tween_layer_id = gap_morph_frame_tweens_dialog(ainfo_ptr, mgpp);
+             /* this dialog also perform processing while dialog window is open
+              * (no need to run non-interactive when dialog returns) */
+             l_run_flag = FALSE;
+           }
+           else if (strcmp(name, PLUG_IN_NAME_WORKPOINTS) == 0)
+           {
+             l_tween_layer_id = gap_morph_generate_frame_tween_workpoints_dialog(ainfo_ptr, mgpp);
+             /* this dialog also perform processing while dialog window is open
+              * (no need to run non-interactive when dialog returns) */
+             l_run_flag = FALSE;
+           }
+           else
+           {
+               return -1;
+           }
            mgpp->do_progress = TRUE;
          }
 
-         if((0 != gap_lib_chk_framechange(ainfo_ptr)) || (!l_rc))
+         if(0 != gap_lib_chk_framechange(ainfo_ptr))
          {
             l_run_flag = FALSE;
          }
@@ -311,8 +387,39 @@ p_handle_PLUG_IN_NAME_TWEEN(GapMorphGlobalParams *mgpp)
 
       if(l_run_flag == TRUE)
       {
-         /* render tween frames and write them as framefiles to disc () */
-         l_tween_layer_id = gap_morph_render_frame_tweens(ainfo_ptr, mgpp);
+        gboolean cancelFlag;
+             
+        cancelFlag = FALSE;
+        if(gap_debug)
+        {
+          printf("l_run_flag is TRUE, running...%s\n", name);
+        }
+
+        if (strcmp(name, PLUG_IN_NAME_TWEEN) == 0)
+        {
+          /* render tween frames and write them as framefiles to disc () */
+          l_tween_layer_id = gap_morph_render_frame_tweens(ainfo_ptr
+                                 , mgpp
+                                 , &cancelFlag
+                                 );
+        }
+        else if (strcmp(name, PLUG_IN_NAME_WORKPOINTS) == 0)
+        {
+          /* non-interactive workpoint generation (progressBar is NULL) */
+          l_tween_layer_id = gap_morph_shape_generate_frame_tween_workpoints(ainfo_ptr
+                                 , mgpp
+                                 , NULL           /* masterProgressBar */
+                                 , NULL           /* progressBar */
+                                 , &cancelFlag
+                                 );
+        }
+      }
+      else
+      {
+        if(gap_debug)
+        {
+          printf("l_run_flag is FALSE, terminating...%s\n", name);
+        }
       }
 
     }
@@ -321,7 +428,7 @@ p_handle_PLUG_IN_NAME_TWEEN(GapMorphGlobalParams *mgpp)
 
   return(l_tween_layer_id);
 
-}  /* end p_handle_PLUG_IN_NAME_TWEEN */
+}  /* end p_handle_tween_processing_pug_ins */
 
 /* -------------------------------------
  * run
@@ -329,7 +436,7 @@ p_handle_PLUG_IN_NAME_TWEEN(GapMorphGlobalParams *mgpp)
  */
 static void
 run (const gchar *name,          /* name of plugin */
-     gint nparams,               /* number of in-paramters */
+     gint nparams,               /* number of in-parameters */
      const GimpParam * param,    /* in-parameters */
      gint *nreturn_vals,         /* number of out-parameters */
      GimpParam ** return_vals)   /* out-parameters */
@@ -343,7 +450,7 @@ run (const gchar *name,          /* name of plugin */
   /* Get the runmode from the in-parameters */
   GimpRunMode run_mode = param[0].data.d_int32;
 
-  /* status variable, use it to check for errors in invocation usualy only
+  /* status variable, use it to check for errors in invocation usually only
    * during non-interactive calling
    */
   GimpPDBStatusType status = GIMP_PDB_SUCCESS;
@@ -365,7 +472,8 @@ run (const gchar *name,          /* name of plugin */
   }
 
   run_flag = FALSE;
-  if (strcmp(name, PLUG_IN_NAME_TWEEN) == 0)
+  if ((strcmp(name, PLUG_IN_NAME_TWEEN) == 0)
+  ||  (strcmp(name, PLUG_IN_NAME_WORKPOINTS) == 0))
   {
     run_flag = TRUE;
   }
@@ -396,6 +504,8 @@ run (const gchar *name,          /* name of plugin */
         mgpp->fdst_layer_id          = gap_morph_exec_find_dst_drawable(image_id, drawable_id);
         run_flag = gap_morph_dialog(mgpp);
         mgpp->do_progress            = TRUE;
+        mgpp->master_progress_callback_fptr     = NULL;
+        mgpp->progress_callback_fptr            = NULL;
       }
       else if (strcmp(name, PLUG_IN_NAME_ONE_TWEEN) == 0)
       {
@@ -406,7 +516,12 @@ run (const gchar *name,          /* name of plugin */
         mgpp->fdst_layer_id          = gap_morph_exec_find_dst_drawable(image_id, drawable_id);
         run_flag = gap_morph_one_tween_dialog(mgpp);
         mgpp->do_progress            = TRUE;
+        mgpp->master_progress_callback_fptr     = NULL;
+        mgpp->progress_callback_fptr            = NULL;
       }
+      /* dialog for other frame tween processing plug ins see 
+       * p_handle_tween_processing_pug_ins
+       */
       break;
 
     case GIMP_RUN_NONINTERACTIVE:
@@ -442,7 +557,7 @@ run (const gchar *name,          /* name of plugin */
             }
             else
             {
-              printf("%s: noninteractive call requires a not-empty workpoint_file_1 parameter\n"
+              printf("%s: non-interactive call requires a not-empty workpoint_file_1 parameter\n"
                     , PLUG_IN_NAME
                     );
               status = GIMP_PDB_CALLING_ERROR;
@@ -450,7 +565,7 @@ run (const gchar *name,          /* name of plugin */
           }
           else
           {
-            printf("%s: noninteractive call requires a not-NULL workpoint_file_1 parameter\n"
+            printf("%s: non-interactive call requires a not-NULL workpoint_file_1 parameter\n"
                     , PLUG_IN_NAME
                     );
             status = GIMP_PDB_CALLING_ERROR;
@@ -506,7 +621,7 @@ run (const gchar *name,          /* name of plugin */
           }
           else
           {
-            printf("%s: noninteractive call requires a not-NULL workpoint_file parameter\n"
+            printf("%s: non-interactive call requires a not-NULL workpoint_file parameter\n"
                     , PLUG_IN_NAME_ONE_TWEEN
                     );
             status = GIMP_PDB_CALLING_ERROR;
@@ -554,16 +669,30 @@ run (const gchar *name,          /* name of plugin */
           }
           else
           {
-            printf("%s: noninteractive call requires a not-NULL workpoint_file parameter\n"
+            printf("%s: non-interactive call requires a not-NULL workpoint_file parameter\n"
                     , PLUG_IN_NAME_TWEEN
                     );
             status = GIMP_PDB_CALLING_ERROR;
           }
           
       }
+      else if ((nparams == G_N_ELEMENTS (in_workpoint_args))
+      && (strcmp(name, PLUG_IN_NAME_WORKPOINTS) == 0))
+      {
+          mgpp->osrc_layer_id = -1;  /* is created later as merged copy of the specified image */
+          mgpp->fdst_layer_id = -1;   /* is created later as merged copy of the frame rfered by to_frame_nr parameter  */        
+          mgpp->range_to = param[3].data.d_int32;
+          mgpp->numWorkpoints = param[4].data.d_int32;
+          mgpp->numOutlinePoints = param[5].data.d_int32;
+          mgpp->locateDetailMoveRadius = param[6].data.d_int32;
+          mgpp->locateDetailShapeRadius = param[7].data.d_int32;
+          mgpp->locateColordiffThreshold = param[8].data.d_float;
+          mgpp->edgeColordiffThreshold = param[9].data.d_float;
+          run_flag = TRUE;
+      }
       else
       {
-        printf("%s: noninteractive call wrong nuber %d of params were passed. (%d params are required)\n"
+        printf("%s: non-interactive call wrong nuber %d of params were passed. (%d params are required)\n"
               , PLUG_IN_NAME
               , (int)nparams
               , (int)G_N_ELEMENTS (in_args)
@@ -581,6 +710,8 @@ run (const gchar *name,          /* name of plugin */
         mgpp->fdst_layer_id          = gap_morph_exec_find_dst_drawable(image_id, drawable_id);
         run_flag = gap_morph_dialog(mgpp);
         mgpp->do_progress            = TRUE;
+        mgpp->master_progress_callback_fptr     = NULL;
+        mgpp->progress_callback_fptr            = NULL;
       }
       break;
 
@@ -600,7 +731,9 @@ run (const gchar *name,          /* name of plugin */
         gimp_set_data (PLUG_IN_NAME, mgpp, sizeof (GapMorphGlobalParams));
       }
     }
-    else if ((strcmp(name, PLUG_IN_NAME_ONE_TWEEN) == 0) || (strcmp(name, PLUG_IN_NAME_TWEEN) == 0))
+    else if ((strcmp(name, PLUG_IN_NAME_ONE_TWEEN) == 0) 
+         || (strcmp(name, PLUG_IN_NAME_TWEEN) == 0)
+         || (strcmp(name, PLUG_IN_NAME_WORKPOINTS) == 0))
     {
       gint32 tween_layer_id;
       
@@ -608,9 +741,10 @@ run (const gchar *name,          /* name of plugin */
       {
         tween_layer_id = gap_morph_render_one_tween(mgpp);
       }
-      else if (strcmp(name, PLUG_IN_NAME_TWEEN) == 0)
+      else
       {
-        tween_layer_id = p_handle_PLUG_IN_NAME_TWEEN(mgpp);
+        /* handle processing for PLUG_IN_NAME_TWEEN or PLUG_IN_NAME_WORKPOINTS */
+        tween_layer_id = p_handle_tween_processing_pug_ins(mgpp, name);
       }
 
 
@@ -628,12 +762,15 @@ run (const gchar *name,          /* name of plugin */
          {
            /* Store variable states for next run */
            gimp_set_data (name, mgpp, sizeof (GapMorphGlobalParams));
-           gimp_display_new(gimp_drawable_get_image(tween_layer_id));
+           if(strcmp(name, PLUG_IN_NAME_WORKPOINTS) != 0)
+           {
+             gimp_display_new(gimp_drawable_get_image(tween_layer_id));
+           }
          }
       }
     }
-
   }
+
   values[0].data.d_status = status;
 }       /* end run */
 
diff --git a/gap/gap_morph_main.h b/gap/gap_morph_main.h
index 5cbeb7c..4c9cfa6 100644
--- a/gap/gap_morph_main.h
+++ b/gap/gap_morph_main.h
@@ -41,6 +41,12 @@
 
 #define   GAP_MORPH_WORKPOINT_FILENAME_MAX_LENGTH     1024 
 
+#define   GAP_MORPH_WORKPOINT_EXTENSION "morphpoints"
+
+/* Function Typedefs */
+typedef  void       (*t_progress_callback_fptr)(gdouble percentage, gpointer data);
+typedef  void       (*t_master_progress_callback_fptr)(gdouble numDone, gdouble numTotal, const char *filename, gpointer data);
+
 typedef struct GapMorphWorkPoint { /* nickname: wp */
      gdouble fdst_x;   /* final dest koord (as set by user for last dest. frame) */
      gdouble fdst_y;
@@ -66,6 +72,9 @@ typedef struct GapMorphWorkPoint { /* nickname: wp */
      gboolean is_alive;
      void    *next_selected;
      void    *next_sek;
+
+     /* for automatically workpoint genration */
+     gdouble  locateColordiff; /* 0.0 exact match up to theoretical 1.0 */
      
   } GapMorphWorkPoint;
 
@@ -106,7 +115,24 @@ typedef struct GapMorphGlobalParams  { /* nickname: mgpp */
   gint32              range_from;
   gint32              range_to;
   gboolean            overwrite_flag;
+  gboolean            append_flag;
   gboolean            do_simple_fade;   /* bypass morph algortihm when renderiing tweens and use simple fade instead */
+  
+  gdouble             edgeColordiffThreshold;
+  gdouble             locateColordiffThreshold;
+  gint32              locateDetailShapeRadius;
+  gint32              locateDetailMoveRadius;
+  gint32              numWorkpoints;
+  gint32              numOutlinePoints;
+
+  /* additional stuff (only relevant for tween frame rendering) */
+  gint32              master_tween_steps;
+  gboolean            create_tweens_in_subdir;
+  char                tween_subdir[GAP_MORPH_WORKPOINT_FILENAME_MAX_LENGTH];
+
+  t_master_progress_callback_fptr master_progress_callback_fptr;
+  t_progress_callback_fptr        progress_callback_fptr;
+  gpointer                        callback_data_ptr;
 } GapMorphGlobalParams;
 
 typedef struct GapMorphWarpCoreAPI  { /* nickname: wcap */
diff --git a/gap/gap_morph_shape.c b/gap/gap_morph_shape.c
new file mode 100644
index 0000000..4c6acb8
--- /dev/null
+++ b/gap/gap_morph_shape.c
@@ -0,0 +1,1312 @@
+/* gap_morph_shape.c
+ *    image shape dependent morph workpoint creation
+ *    by hof (Wolfgang Hofer)
+ *  2010/08/10
+ *
+ */
+/* 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:
+ * version 2.7.0;             hof: created
+ */
+
+#include "config.h"
+
+
+/* SYTEM (UNIX) includes */
+#include "string.h"
+#include <errno.h>
+
+/* GIMP includes */
+#include <gtk/gtk.h>
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+/* GAP includes */
+#include "gap_lib_common_defs.h"
+#include "gap_colordiff.h"
+#include "gap_locate.h"
+#include "gap_edge_detection.h"
+#include "gap_colordiff.h"
+#include "gap_morph_shape.h"
+#include "gap_morph_main.h"
+#include "gap_morph_dialog.h"
+#include "gap_morph_exec.h"
+#include "gap_lib.h"
+#include "gap_image.h"
+#include "gap_libgapbase.h"
+
+#include "gap-intl.h"
+
+#define MAX_TOLERATED_ANGLE_DIFF_DEGREE 22.0
+#define MAX_TOLERATED_LENGTH_DIFF_PIXELS 33.0
+
+
+extern      int gap_debug; /* ==0  ... dont print debug infos */
+
+typedef struct GapMorphShapeContext { /* nickname: msctx */
+     gint32 edgeImageId;
+     gint32 edgeLayerId;
+     gint32 refLayerId;
+     gint32 targetLayerId;
+     GimpDrawable *edgeDrawable;
+     GimpPixelFetcher *pftEdge;
+     
+     gdouble locateColordiffThreshold;
+     gint32  locateDetailMoveRadius;
+     gint32  locateDetailShapeRadius;
+     gdouble edgeColordiffThreshold;
+     gdouble colorSensitivity;
+     gint32  countEdgePixels;
+
+     gint32  numShapePoints;
+     gint32  countGeneratedPoints;
+     
+     /* generate points within the clipping boundary sel1/sel2 coordinate */
+     gint32  sel1X;
+     gint32  sel1Y;
+     gint32  sel2X;
+     gint32  sel2Y;
+     
+     
+     gboolean doProgress;
+     GtkWidget *progressBar;
+     gboolean  *cancelWorkpointGenerationPtr;
+
+  } GapMorphShapeContext;
+
+static gboolean     p_pixel_check_opaque(GimpPixelFetcher *pft
+                                     , gint bpp
+                                     , gdouble needx
+                                     , gdouble needy
+                                     );
+static void         p_find_outmost_opaque_pixel(GimpPixelFetcher *pft
+                           ,gint bpp
+                           ,gdouble alpha_rad
+                           ,gint32 width
+                           ,gint32 height
+                           ,gdouble *px
+                           ,gdouble *py
+                           );
+
+
+/* ------------------------------------
+ * gap_morph_shape_calculateAngleDegree
+ * ------------------------------------
+ */
+gdouble
+gap_morph_shape_calculateAngleDegree(GapMorphWorkPoint *wp, gdouble *length)
+{
+  gdouble angleRad;
+  gdouble angleDeg;
+  gdouble sqrLen;
+  gdouble dx;
+  gdouble dy;
+  gdouble tanAlpha;
+ 
+  dx = wp->fdst_x - wp->osrc_x;
+  dy = wp->fdst_y - wp->osrc_y;
+
+  sqrLen = (dx * dx) + (dy * dy);
+
+  if (dx > 0)
+  {
+    tanAlpha = dy / dx;
+    angleRad = atan(tanAlpha);
+    if(angleRad < 0)
+    {
+      angleRad += (2.0 * G_PI);
+    }
+  } else if (dx < 0)
+  {
+    tanAlpha = dy / (0.0 - dx);
+    angleRad = atan(tanAlpha);
+    angleRad = G_PI - angleRad;
+  } else
+  {
+    /* dx == 0 */
+    if(dy > 0)
+    {
+      /* + 90 degree */
+      angleRad = G_PI / 2.0;
+    } else if(dy < 0)
+    {
+      /* 270 degree */
+      angleRad = G_PI + (G_PI / 2.0);
+    } else
+    {
+      /* undefined angle */
+      angleRad = 0;
+    }
+
+  }
+
+  *length = sqrt(sqrLen);
+  angleDeg = (angleRad * 180.0) / G_PI;
+  
+  if(gap_debug)
+  {
+    printf("WP: dx:%.1f dy:%.1f  len:%.3f   angleDeg:%.5f\n"
+       ,(float)dx
+       ,(float)dy
+       ,(float)*length
+       ,(float)angleDeg
+       );
+  }
+  
+  return(angleDeg);
+
+}  /* end gap_morph_shape_calculateAngleDegree */
+
+
+
+
+/* -----------------------------
+ * gap_morph_shape_new_workpont
+ * -----------------------------
+ */
+GapMorphWorkPoint *
+gap_morph_shape_new_workpont(gdouble srcx, gdouble srcy, gdouble dstx, gdouble dsty)
+{
+  GapMorphWorkPoint *wp;
+
+  wp = g_new(GapMorphWorkPoint, 1);
+  wp->next = NULL;
+  wp->fdst_x = dstx;
+  wp->fdst_y = dsty;
+  wp->osrc_x = srcx;
+  wp->osrc_y = srcy;
+
+  wp->dst_x = wp->fdst_x;
+  wp->dst_y = wp->fdst_y;
+  wp->src_x = wp->osrc_x;
+  wp->locateColordiff = 0.0;
+
+
+  wp->src_y = wp->osrc_y;
+  
+  return(wp);
+}  /* end gap_morph_shape_new_workpont */
+
+
+/* ---------------------------------
+ * p_doProgress
+ * ---------------------------------
+ */
+static void
+p_doProgress(GapMorphShapeContext *msctx)
+{
+  guchar *progressText;
+  gdouble percentage;
+
+  if(msctx->doProgress != TRUE)
+  {
+    return;
+  }
+  percentage = (gdouble)msctx->countGeneratedPoints / MAX(1.0, (gdouble)msctx->numShapePoints);
+  
+  progressText = g_strdup_printf(_("generating workpoint:%d (%d)")
+                     , (int)msctx->countGeneratedPoints
+                     , (int)msctx->numShapePoints
+                     );
+  gtk_progress_bar_set_text(GTK_PROGRESS_BAR(msctx->progressBar), progressText);
+  gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(msctx->progressBar), percentage);
+  g_free(progressText);
+
+  while (gtk_events_pending ())
+  {
+    gtk_main_iteration ();
+  }
+}  /* end p_doProgress */
+
+
+/* ---------------------------------
+ * p_find_nearest_point
+ * ---------------------------------
+ * saerch the workpoint list for the point that is the nearest
+ * to position in_x/in_y in the osrc or fdst koord system.
+ * (depending on the swp->src_flag)
+ */
+static GapMorphWorkPoint *
+p_find_workpoint_near_src_coords(GapMorphGlobalParams *mgpp
+                    , gdouble in_x
+                    , gdouble in_y
+                    , gint32  nearRadius
+                    )
+{
+  GapMorphWorkPoint *wp;
+  GapMorphWorkPoint *wp_list;
+  gdouble            sqr_distance;
+  gdouble            near_sqr_dist;
+  
+  near_sqr_dist = (nearRadius * nearRadius) * 2;
+  wp_list = mgpp->master_wp_list;
+
+  for(wp = wp_list; wp != NULL; wp = (GapMorphWorkPoint *)wp->next)
+  {
+    gdouble  adx;
+    gdouble  ady;
+
+    if((wp->osrc_x == in_x)
+    && (wp->osrc_y == in_y))
+    {
+      return(wp);
+    }
+
+    adx = abs(wp->osrc_x - in_x);
+    ady = abs(wp->osrc_y - in_y);
+
+    sqr_distance = (adx * adx) + (ady * ady);
+
+    if(sqr_distance <= near_sqr_dist)
+    {
+      return(wp);
+    }
+  }
+
+  return(NULL);
+}  /* end p_find_nearest_point */
+
+
+
+/* -----------------------------
+ * p_locate_workpoint
+ * -----------------------------
+ * locate point coordinate in the other drawable
+ * (by searching for best matching shape within move radius)
+ */
+static gboolean
+p_locate_workpoint(GapMorphShapeContext *msctx
+  , gdouble rx, gdouble ry
+  , gdouble *tx, gdouble *ty
+  , gdouble *locateColordiff)
+{
+  gint32    refX;
+  gint32    refY;
+  gint32    targetX;
+  gint32    targetY;
+  gboolean  success;
+  
+  refX = rx;
+  refY = ry;
+
+  *locateColordiff = gap_locateDetailWithinRadius(msctx->refLayerId
+                  , refX
+                  , refY
+                  , msctx->locateDetailShapeRadius
+                  , msctx->targetLayerId
+                  , msctx->locateDetailMoveRadius
+                  , msctx->locateColordiffThreshold
+                  , &targetX
+                  , &targetY
+                  );
+  if(gap_debug)
+  {
+    printf("p_locate_workpoint: x/y: %d / %d MoveRadius:%d ShapeRadius:%d Threshold:%.5f colordiff:%.5f\n"
+          , (int)refX
+          , (int)refY
+          , (int)msctx->locateDetailMoveRadius
+          , (int)msctx->locateDetailShapeRadius
+          , (float)msctx->locateColordiffThreshold
+          , (float)*locateColordiff
+          );
+          
+  }
+
+  if(*locateColordiff < msctx->locateColordiffThreshold)
+  {
+    /* found a sufficient matching shape in the target drawable
+     * at targetX/Y coordinate. Overwrite tx and ty with this matching point coordinate
+     */
+    *tx = targetX;
+    *ty = targetY;
+    success = TRUE;
+
+    if(gap_debug)
+    {
+      printf("p_locate_workpoint: SUCCESS, found matching shape! tx:%d ty:%d colordiff:%.4f colordiffThres:%.4f\n"
+         ,(int)targetX
+         ,(int)targetY
+         ,(float)*locateColordiff
+         ,(float)msctx->locateColordiffThreshold
+         );
+    }
+  }
+  else
+  {
+    success = FALSE;
+  }
+
+  return(success);
+  
+}  /* end p_locate_workpoint */
+
+
+
+
+
+
+
+
+
+
+
+
+/* -----------------------------------------------
+ * p_find_workpoint_nearest_to_src_coords
+ * -----------------------------------------------
+ * find workpoint that is nearest (but not equal)
+ * to specified coords.
+ * return the nearest workpoint within check radius
+ *        (and set wp->sqr_dist  to the square distance)
+ * or NULLin case no workpoint is near.
+ */
+static GapMorphWorkPoint*
+p_find_workpoint_nearest_to_src_coords(GapMorphGlobalParams *mgpp
+                    , gdouble in_x
+                    , gdouble in_y
+                    , gint32  checkRadius
+                    )
+{
+  GapMorphWorkPoint *wp;
+  GapMorphWorkPoint *wp_ret;
+  GapMorphWorkPoint *wp_list;
+  gdouble            sqr_distance;
+  gdouble            check_sqr_dist;
+  
+  check_sqr_dist = (checkRadius * checkRadius) * 2;
+  wp_list = mgpp->master_wp_list;
+  wp_ret = NULL;
+
+  for(wp = wp_list; wp != NULL; wp = (GapMorphWorkPoint *)wp->next)
+  {
+    gdouble  adx;
+    gdouble  ady;
+
+    if((wp->osrc_x == in_x)
+    && (wp->osrc_y == in_y))
+    {
+      continue;
+    }
+
+    adx = abs(wp->osrc_x - in_x);
+    ady = abs(wp->osrc_y - in_y);
+
+    sqr_distance = (adx * adx) + (ady * ady);
+
+    if(sqr_distance < check_sqr_dist)
+    {
+      check_sqr_dist = sqr_distance;
+      wp_ret = wp;
+    }
+  }
+  
+  if(wp_ret)
+  {
+    wp_ret->sqr_dist = check_sqr_dist;
+  }
+
+  return(wp_ret);
+  
+}  /* end p_find_nearest_point */
+
+
+
+/* -----------------------------------------------
+ * p_compare_workpoint_direction
+ * -----------------------------------------------
+ * return FALSE in case the specified workpoint
+ * movement vector differs significant from specified angle/length.
+ * in case the vector length is 0 (just a point)
+ * TRUE is returned.
+ */
+static gboolean
+p_compare_workpoint_direction(GapMorphWorkPoint *wpRef
+  , gdouble angleDeg
+  , gdouble length
+  )
+{
+  gdouble lengthRef;
+  gdouble angleDegRef;
+  gdouble angleDiffDeg;
+  gdouble lengthDiffPixels;
+
+  angleDegRef = gap_morph_shape_calculateAngleDegree(wpRef, &lengthRef);
+  lengthDiffPixels = fabs(length - lengthRef);
+
+  if((lengthRef > 0) && (length > 0))
+  {
+    lengthDiffPixels = fabs(length - lengthRef);
+    if(lengthDiffPixels > MAX_TOLERATED_LENGTH_DIFF_PIXELS)
+    {
+      return (FALSE);
+    }
+
+
+    angleDiffDeg = fabs(angleDeg - angleDegRef);
+    if(angleDiffDeg > 180)
+    {
+      angleDiffDeg = 360 - angleDiffDeg;
+    }
+
+    if(angleDiffDeg > MAX_TOLERATED_ANGLE_DIFF_DEGREE)
+    {
+      return (FALSE);
+    }
+  }
+  return (TRUE);
+  
+}  /* end p_compare_workpoint_direction */
+
+
+/* -----------------------------------------------
+ * p_copy_workpoint_attributes
+ * -----------------------------------------------
+ * copy relevant attributes
+ * (but keep next pointer unchanged)
+ */
+static void
+p_copy_workpoint_attributes(GapMorphWorkPoint *wpSrc, GapMorphWorkPoint *wpDst)
+{
+   wpDst->fdst_x = wpSrc->fdst_x;   /* final dest koord (as set by user for last dest. frame) */
+   wpDst->fdst_y = wpSrc->fdst_y;
+   wpDst->osrc_x = wpSrc->osrc_x;   /* start source koord (as set by user for the 1.st frame) */
+   wpDst->osrc_y = wpSrc->osrc_y;
+
+   wpDst->dst_x = wpSrc->dst_x;    /* koord trans */
+   wpDst->dst_y = wpSrc->dst_y;
+   wpDst->src_x = wpSrc->src_x;    /* osrc_x scaled to koords of current (dest) frame */
+   wpDst->src_y = wpSrc->src_y;
+
+   wpDst->locateColordiff = wpSrc->locateColordiff;
+}
+
+/* -----------------------------------------------
+ * p_workpoint_plausiblity_filter
+ * -----------------------------------------------
+ * return FALSE in case the specified workpoint
+ * has untypical movement that differs significant from near workpoints.
+ */
+static gboolean
+p_workpoint_plausiblity_filter(GapMorphShapeContext *msctx
+                    , GapMorphGlobalParams *mgpp
+                    , GapMorphWorkPoint *wp
+                    )
+{
+#define NEAR_CHECK_RADIUS 20.0
+#define OUTER_CHECK_RADIUS (30.0 + NEAR_CHECK_RADIUS)
+  GapMorphWorkPoint *wpRef;
+  gdouble length;
+  gdouble angleDeg;
+  gboolean isWorkpointOk;
+  
+  isWorkpointOk = TRUE;
+  angleDeg = gap_morph_shape_calculateAngleDegree(wp, &length);
+
+  wpRef = p_find_workpoint_nearest_to_src_coords(mgpp
+                    , wp->osrc_x   /* gdouble in_x */
+                    , wp->osrc_y   /* gdouble in_y */
+                    , OUTER_CHECK_RADIUS
+                    );
+  if(wpRef != NULL)
+  {
+    /* found a near workpoint that is either already checked
+     * or was already present before start of workpoint generation
+     */ 
+    isWorkpointOk = p_compare_workpoint_direction(wpRef, angleDeg, length);
+
+    if(gap_debug)
+    {
+      printf("NEAREST Ref workpoint found x:%d y:%d xRef:%d yRef:%d OK:%d colordiff:%.4f colordiffRef:%.4f dist:%.4f DIST_LIMIT:%.4f\n"
+          , (int)wp->osrc_x
+          , (int)wp->osrc_y
+          , (int)wpRef->osrc_x
+          , (int)wpRef->osrc_y
+          , (int)isWorkpointOk
+          , (float)wp->locateColordiff
+          , (float)wpRef->locateColordiff
+          , (float)sqrt(wpRef->sqr_dist)
+          , (float)NEAR_CHECK_RADIUS
+          );
+    }
+
+
+    if (isWorkpointOk != TRUE)
+    {
+      if(wp->locateColordiff < wpRef->locateColordiff)
+      {
+        if(sqrt(wpRef->sqr_dist) <= NEAR_CHECK_RADIUS)
+        {
+          /* overwrite wpRef attributes because the new workpoint wp
+           */
+          p_copy_workpoint_attributes(wp, wpRef);
+          if(gap_debug)
+          {
+            printf("REPLACE Ref workpoint x:%d y:%d xRef:%d yRef:%d\n"
+                , (int)wp->osrc_x
+                , (int)wp->osrc_y
+                , (int)wpRef->osrc_x
+                , (int)wpRef->osrc_y
+                );
+          }
+        }
+      }
+    }
+
+  }
+
+
+
+  if(gap_debug)
+  {
+    if(isWorkpointOk != TRUE)
+    {
+      printf("DISCARD non-plausible workpoint at x:%d y:%d\n"
+        , (int)wp->osrc_x
+        , (int)wp->osrc_y
+        );
+    }
+  }
+
+  return(isWorkpointOk);
+  
+}  /* end p_workpoint_plausiblity_filter */
+
+
+
+
+
+/* --------------------------------
+ * p_generateWorkpoints
+ * --------------------------------
+ * scan the edge drawable with descending grid size (defined stepSizeTab)
+ * and descending edge threshold. This shall spread the generated
+ * workpoints all over the whole area where bright edge points
+ * are picked first.
+ */
+static void
+p_generateWorkpoints(GapMorphShapeContext *msctx, GapMorphGlobalParams *mgpp)
+{
+#define LOOP_LOOKUP_TAB_SIZE 5
+  static gint32  edgeThresTab[LOOP_LOOKUP_TAB_SIZE]   = { 120, 60, 30, 10,  1 };
+  static gint32  nearRadiusTab[LOOP_LOOKUP_TAB_SIZE]  = {  20, 14, 10,  6,  2 };
+  static gint32  stepSizeTab[LOOP_LOOKUP_TAB_SIZE]    = {  10,  8,  6,  2,  1 };
+  gint32  ex;
+  gint32  ey;
+  gdouble srcX;
+  gdouble srcY;
+  gint32  nearRadius;
+  gint32  edgePixelThreshold255;
+  gint32  stepSize;
+  gint32  ii;
+
+  for(ii=0; ii < LOOP_LOOKUP_TAB_SIZE; ii++)
+  {
+    edgePixelThreshold255 = edgeThresTab[ii];
+    nearRadius = nearRadiusTab[ii];
+    stepSize = stepSizeTab[ii];
+    
+    for(ey=msctx->sel1Y; ey < msctx->sel2Y; ey += stepSize)
+    {
+      srcY = ey +1;
+      for(ex=msctx->sel1X; ex < msctx->sel2X; ex += stepSize)
+      {
+        guchar pixel[4];
+  
+        if(*msctx->cancelWorkpointGenerationPtr == TRUE)
+        {
+          return;
+        }
+
+        gimp_pixel_fetcher_get_pixel (msctx->pftEdge, ex, ey, &pixel[0]);
+
+        if(pixel[0] >= edgePixelThreshold255)
+        {
+          /* found an edge pixel */
+          GapMorphWorkPoint *wp;
+
+          srcX = ex +1;
+
+
+          /* chek if workpoint with this (or very near) coords does already exist */
+          wp = p_find_workpoint_near_src_coords(mgpp
+                      , srcX
+                      , srcY
+                      , nearRadius
+                      );
+          if(wp == NULL)
+          {
+            gboolean success;
+            gdouble dstX;
+            gdouble dstY;
+            gdouble locateColordiff;
+
+            success = p_locate_workpoint(msctx
+                                        , srcX,  srcY
+                                        , &dstX, &dstY
+                                        , &locateColordiff
+                                        );
+            if(success)
+            {
+              wp = gap_morph_shape_new_workpont(srcX, srcY, dstX, dstY);
+              wp->locateColordiff = locateColordiff;
+              
+              if(TRUE == p_workpoint_plausiblity_filter(msctx
+                    , mgpp
+                    , wp
+                    ))
+              {
+                msctx->countGeneratedPoints++; 
+                /* add new workpoint as 1st listelement */
+                wp->next = mgpp->master_wp_list;
+                mgpp->master_wp_list = wp;
+
+                p_doProgress(msctx);
+
+                if(msctx->countGeneratedPoints >= msctx->numShapePoints)
+                {
+                  return;
+                }
+              }
+              else
+              {
+                g_free(wp);
+              }
+
+            }
+
+          }
+
+        }
+
+      }
+    }
+  }
+  
+}  /* end p_generateWorkpoints */
+
+
+/* --------------------------------
+ * gap_moprhShapeDetectionEdgeBased
+ * --------------------------------
+ *
+ * generates morph workpoints via edge based shape detection.
+ * This is done by edge detection in the source image
+ *  (specified via mgup->mgpp->osrc_layer_id)
+ *
+ * and picking workpoints on the detected edges
+ * and locating the corresponding point in the destination image.
+ *
+ * IN: mgup->num_shapepoints  specifies the number of workpoints to be generated.
+ */
+void
+gap_moprhShapeDetectionEdgeBased(GapMorphGUIParams *mgup, gboolean *cancelFlagPtr)
+{
+  GapMorphShapeContext  morphShapeContext;
+  GapMorphShapeContext *msctx;
+  gboolean deleteEdgeImage;
+  GapMorphGlobalParams *mgpp;
+
+  mgpp = mgup->mgpp;
+
+  if(mgup->workpointGenerationBusy == TRUE)
+  {
+    return;
+  }
+  mgup->workpointGenerationBusy = TRUE;
+  deleteEdgeImage = TRUE;
+  
+  /* init context */  
+  msctx = &morphShapeContext;
+  msctx->edgeColordiffThreshold = CLAMP(mgpp->edgeColordiffThreshold, 0.0, 1.0);
+  msctx->locateColordiffThreshold = CLAMP(mgpp->locateColordiffThreshold, 0.0, 1.0);
+  msctx->locateDetailShapeRadius = mgpp->locateDetailShapeRadius;
+  msctx->locateDetailMoveRadius = mgpp->locateDetailMoveRadius;
+  msctx->colorSensitivity = GAP_COLORDIFF_DEFAULT_SENSITIVITY;
+  msctx->refLayerId = mgpp->osrc_layer_id;
+  msctx->targetLayerId = mgpp->fdst_layer_id;
+  msctx->numShapePoints = mgpp->numWorkpoints;
+  msctx->countGeneratedPoints = 0;
+
+  msctx->doProgress = TRUE;
+  msctx->progressBar = mgup->progressBar;
+  if(msctx->progressBar == NULL)
+  {
+    msctx->doProgress = FALSE;
+  }
+  msctx->cancelWorkpointGenerationPtr = cancelFlagPtr;
+
+  if(gap_debug)
+  {
+    printf("gap_moprhShapeDetectionEdgeBased START edgeThres:%.5f  locateThres:%.5f locateRadius:%d\n"
+       , (float)msctx->edgeColordiffThreshold
+       , (float)msctx->locateDetailMoveRadius
+       , (int)msctx->locateDetailMoveRadius
+       );
+  }
+
+
+  msctx->edgeLayerId = gap_edgeDetection(mgup->mgpp->osrc_layer_id   /* refDrawableId */
+                                 ,mgup->mgpp->edgeColordiffThreshold
+                                 ,&msctx->countEdgePixels
+                                 );
+
+  msctx->edgeImageId = gimp_drawable_get_image(msctx->edgeLayerId);
+
+  if(gap_debug)
+  {
+    /* show the edge image for debug purpose
+     * (this image is intended for internal processing usage)
+     */
+    gimp_display_new(msctx->edgeImageId);
+    deleteEdgeImage = FALSE;
+  }
+  msctx->edgeDrawable = gimp_drawable_get(msctx->edgeLayerId);
+
+ 
+  if(msctx->edgeDrawable != NULL)
+  {
+    gint      maxWidth;
+    gint      maxHeight;
+    
+    maxWidth = msctx->edgeDrawable->width-1;
+    maxHeight = msctx->edgeDrawable->height-1;
+    
+    if((mgup->src_win.zoom < 1.0) && (mgup->src_win.zoom > 0.0))
+    {
+      gdouble   fwidth;
+      gdouble   fheight;
+      gint      width;
+      gint      height;
+      
+      fwidth  = mgup->src_win.zoom * (gdouble)mgup->src_win.pv_ptr->pv_width;
+      fheight = mgup->src_win.zoom * (gdouble)mgup->src_win.pv_ptr->pv_height;
+      width  = CLAMP((gint)fwidth,  1, maxWidth);
+      height = CLAMP((gint)fheight, 1, maxHeight);
+      msctx->sel1X = CLAMP(mgup->src_win.offs_x, 0, maxWidth);
+      msctx->sel1Y = CLAMP(mgup->src_win.offs_y, 0, maxHeight);
+      msctx->sel2X = CLAMP((msctx->sel1X + width), 0, maxWidth);
+      msctx->sel2Y = CLAMP((msctx->sel1Y + height), 0, maxHeight);
+    }
+    else
+    {
+      msctx->sel1X = 0;
+      msctx->sel1Y = 0;
+      msctx->sel2X = maxWidth;
+      msctx->sel2Y = maxHeight;
+    }
+   
+
+    if(gap_debug)
+    {
+      printf("Boundaries: sel1: %d / %d sel2: %d / %d  zoom:%.5f\n"
+        , (int)msctx->sel1X
+        , (int)msctx->sel1Y
+        , (int)msctx->sel2X
+        , (int)msctx->sel2Y
+        , (float)mgup->src_win.zoom
+        );
+    }
+
+
+    msctx->pftEdge = gimp_pixel_fetcher_new (msctx->edgeDrawable, FALSE /* shadow */);
+    gimp_pixel_fetcher_set_edge_mode (msctx->pftEdge, GIMP_PIXEL_FETCHER_EDGE_BLACK);
+
+    p_generateWorkpoints(msctx, mgpp);
+
+    gimp_pixel_fetcher_destroy (msctx->pftEdge);
+  }
+
+  if(msctx->edgeDrawable != NULL)
+  {
+    gimp_drawable_detach(msctx->edgeDrawable);
+    msctx->edgeDrawable = NULL;
+  }
+  
+  if(deleteEdgeImage == TRUE)
+  {
+    gap_image_delete_immediate(msctx->edgeImageId);
+  }
+
+  if(gap_debug)
+  {
+    printf("gap_moprhShapeDetectionEdgeBased END\n");
+  }
+  mgup->workpointGenerationBusy = FALSE;
+  
+  return ;
+
+}  /* end gap_moprhShapeDetectionEdgeBased */
+
+
+
+/* -----------------------------------------------
+ * p_generateWorkpointFileForFramePair
+ * -----------------------------------------------
+ * generate workpoint file for morphing
+ * tweens between currFrameNr and nextFrameNr
+ *
+ * return TRUE on success, FALSE when Failed
+ */
+static gboolean
+p_generateWorkpointFileForFramePair(const char *workpointFileName, GapMorphGUIParams *mgup
+     , gboolean *cancelFlagPtr, gint32 currLayerId, gint32 nextLayerId)
+{
+  /* startup with empty workpoint list
+   */
+  if(mgup->mgpp->master_wp_list != NULL)
+  {
+    gap_morph_exec_free_workpoint_list(&mgup->mgpp->master_wp_list);
+  }
+  mgup->mgpp->master_wp_list = NULL;
+  
+  mgup->mgpp->osrc_layer_id = currLayerId;
+  mgup->mgpp->fdst_layer_id = nextLayerId;
+
+  /* if workpointfile already exists (and overwrite flag is not specified)
+   * load and append newly generated points 
+   */
+  if(g_file_test(workpointFileName, G_FILE_TEST_EXISTS))
+  {
+    if(mgup->mgpp->append_flag != TRUE)
+    {
+      if(gap_debug)
+      {
+        printf("SKIP processing for already existing workpointfile:%s\n"
+            , workpointFileName
+            );
+      }
+      return (TRUE);
+    }
+    if(mgup->mgpp->overwrite_flag != TRUE)
+    {
+      if(gap_debug)
+      {
+        printf("ADD newly generated workpoints to already existing workpointfile:%s\n"
+            , workpointFileName
+            );
+      }
+      GapMorphGUIParams morphParamsInFile;
+      GapMorphGUIParams *mgupFile;
+      GapMorphGlobalParams  dummyParams;
+      GapMorphGlobalParams *mgppFile;
+
+      mgppFile = &dummyParams;
+      mgppFile->osrc_layer_id = mgup->mgpp->osrc_layer_id;
+      mgppFile->fdst_layer_id = mgup->mgpp->fdst_layer_id;
+     
+      mgupFile = &morphParamsInFile;
+      mgupFile->mgpp = mgppFile;
+     
+      mgup->mgpp->master_wp_list = gap_moprh_exec_load_workpointfile(workpointFileName
+                                  ,mgupFile
+                                  );
+    }
+  }
+
+  if(mgup->mgpp->numOutlinePoints > 0)
+  {
+    gap_morph_shape_generate_outline_workpoints(mgup->mgpp);
+  }
+  
+  
+  gap_moprhShapeDetectionEdgeBased(mgup, cancelFlagPtr);
+  if(!gap_moprh_exec_save_workpointfile(workpointFileName, mgup))
+  {
+   gint   l_errno;
+   
+   l_errno = errno;
+   g_message (_("Failed to write morph workpointfile\n"
+                "filename: '%s':\n%s")
+             , workpointFileName
+             , g_strerror (l_errno)
+             );
+    return (FALSE);
+  }
+
+  return (TRUE);
+
+}  /* end p_generateWorkpointFileForFramePair */
+
+
+
+/* -----------------------------------------------
+ * p_getMergedFrameImageLayer
+ * -----------------------------------------------
+ *
+ * return -1 if FAILED, or layerId (positive integer) on success
+ */
+gint32
+p_getMergedFrameImageLayer(GapAnimInfo *ainfo_ptr, gint32 frameNr)
+{
+  gint32 imageId;
+  gint32 layerId;
+
+  layerId = -1;
+  if(ainfo_ptr->new_filename != NULL)
+  {
+    g_free(ainfo_ptr->new_filename);
+  }
+  ainfo_ptr->new_filename = gap_lib_alloc_fname(ainfo_ptr->basename,
+                                      frameNr,
+                                      ainfo_ptr->extension);
+  if(ainfo_ptr->new_filename == NULL)
+  {
+     printf("could not create frame filename for frameNr:%d\n", (int)frameNr);
+     return -1;
+  }
+
+  if(!g_file_test(ainfo_ptr->new_filename, G_FILE_TEST_EXISTS))
+  {
+     printf("file not found: %s\n", ainfo_ptr->new_filename);
+     return -1;
+  }
+
+  /* load current frame */
+  imageId = gap_lib_load_image(ainfo_ptr->new_filename);
+  if(imageId >= 0)
+  {
+    layerId = gap_image_merge_visible_layers(imageId, GIMP_CLIP_TO_IMAGE);
+  }
+  
+  return(layerId);
+}
+
+/* -----------------------------------------------
+ * p_do_master_progress
+ * -----------------------------------------------
+ */
+static void
+p_do_master_progress(GtkWidget *progressBar
+   , const char *workpointFileName,  gdouble framesToProcess, gdouble frameNr)
+{
+  if(progressBar != NULL)
+  {
+    char *msg;
+    gdouble percentage;
+    
+    percentage = frameNr / MAX(1.0, framesToProcess);
+    msg = gap_base_shorten_filename(NULL       /* prefix */
+                        ,workpointFileName     /* filenamepart */
+                        ,NULL                  /* suffix */
+                        ,130                   /* l_max_chars */
+                        );
+    gtk_progress_bar_set_text(GTK_PROGRESS_BAR(progressBar), msg);
+    gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(progressBar), percentage);
+    g_free(msg);
+
+    while (gtk_events_pending ())
+    {
+      gtk_main_iteration ();
+    }
+  }
+}
+
+
+
+/* -----------------------------
+ * p_pixel_check_opaque
+ * -----------------------------
+ * check average opacity in an area
+ * of 2x2 pixels
+ * return TRUE if average alpha is 50% or more opaque
+ */
+static gboolean
+p_pixel_check_opaque(GimpPixelFetcher *pft, gint bpp, gdouble needx, gdouble needy)
+{
+  guchar  pixel[4][4];
+  gdouble alpha_val;
+
+  gint    xi, yi;
+  gint alpha_idx;
+
+
+  if (needx >= 0.0)
+    xi = (int) needx;
+  else
+    xi = -((int) -needx + 1);
+
+  if (needy >= 0.0)
+    yi = (int) needy;
+  else
+    yi = -((int) -needy + 1);
+
+  gimp_pixel_fetcher_get_pixel (pft, xi, yi, pixel[0]);
+  gimp_pixel_fetcher_get_pixel (pft, xi + 1, yi, pixel[1]);
+  gimp_pixel_fetcher_get_pixel (pft, xi, yi + 1, pixel[2]);
+  gimp_pixel_fetcher_get_pixel (pft, xi + 1, yi + 1, pixel[3]);
+
+  alpha_idx = bpp -1;
+  /* average aplha channel normalized to range 0.0 upto 1.0 */
+  alpha_val = (
+               (gdouble)pixel[0][alpha_idx] / 255.0
+            +  (gdouble)pixel[1][alpha_idx] / 255.0
+            +  (gdouble)pixel[2][alpha_idx] / 255.0
+            +  (gdouble)pixel[3][alpha_idx] / 255.0
+            ) /  4.0;
+
+  if (alpha_val > 0.5)  /* fix THRESHOLD half or more opaque */
+  {
+    return (TRUE);
+  }
+  return (FALSE);
+}  /* end p_pixel_check_opaque */
+
+
+/* -----------------------------
+ * p_find_outmost_opaque_pixel
+ * -----------------------------
+ * returns koords in paramters px, py
+ */
+static void
+p_find_outmost_opaque_pixel(GimpPixelFetcher *pft
+                           ,gint bpp
+                           ,gdouble alpha_rad
+                           ,gint32 width
+                           ,gint32 height
+                           ,gdouble *px
+                           ,gdouble *py
+                           )
+{
+  gdouble center_x;
+  gdouble center_y;
+  gdouble cos_alpha;
+  gdouble sin_alpha;
+  gdouble l_x, l_y, l_r;
+  gdouble half_width;
+  gdouble half_height;
+
+  l_x = 0;
+  l_y = 0;
+  cos_alpha = cos(alpha_rad);
+  sin_alpha = sin(alpha_rad);
+
+  /* printf("sin: %.5f cos:%.5f\n", sin_alpha, cos_alpha); */
+
+  half_width = (gdouble)(width /2.0);
+  half_height = (gdouble)(height /2.0);
+  center_x = half_width;
+  center_y = half_height;
+  l_r = MAX(half_width, half_height);
+  l_r *= l_r;
+
+  /* start at the out-most point
+   * (may be out of the layer in most cases)
+   * and search towards the center along
+   * the line with angle alpha
+   */
+  while(l_r > 0)
+  {
+    l_y = l_r * sin_alpha;
+    l_x = l_r * cos_alpha;
+    if((l_x <= half_width)
+    && (l_y <= half_height))
+    {
+      if (((center_x + l_x) >= 0)
+      &&  ((center_y + l_y) >= 0))
+      {
+        /* now we are inside the layer area */
+        if (p_pixel_check_opaque(pft
+                          ,bpp
+                          ,center_x + l_x
+                          ,center_y + l_y
+                          ))
+        {
+          break;
+        }
+      }
+    }
+    l_r--;
+  }
+
+  *px = center_x + l_x;
+  *py = center_y + l_y;
+
+}  /* end p_find_outmost_opaque_pixel */
+
+
+
+/* -------------------------------------------
+ * gap_morph_shape_generate_outline_workpoints
+ * -------------------------------------------
+ */
+void
+gap_morph_shape_generate_outline_workpoints(GapMorphGlobalParams *mgpp)
+{
+  GapMorphWorkPoint *wp;
+  GimpPixelFetcher *src_pixfet;
+  GimpPixelFetcher *dst_pixfet;
+  GimpDrawable *dst_drawable;
+  GimpDrawable *src_drawable;
+  gdouble alpha_rad;
+  gdouble step_rad;
+  gint  ii;
+
+  src_drawable = gimp_drawable_get (mgpp->osrc_layer_id);
+  dst_drawable = gimp_drawable_get (mgpp->fdst_layer_id);
+
+  src_pixfet = gimp_pixel_fetcher_new (src_drawable, FALSE /*shadow*/);
+  dst_pixfet = gimp_pixel_fetcher_new (dst_drawable, FALSE /*shadow*/);
+
+  step_rad =  (2.0 * G_PI) / MAX(1, mgpp->numOutlinePoints);
+  alpha_rad = 0.0;
+
+  /* loop from 0 to 360 degree */
+  for(ii=0; ii < mgpp->numOutlinePoints; ii++)
+  {
+     gdouble sx, sy, dx, dy;
+
+     p_find_outmost_opaque_pixel(src_pixfet
+                                ,src_drawable->bpp
+                                ,alpha_rad
+                                ,src_drawable->width
+                                ,src_drawable->height
+                                ,&sx
+                                ,&sy
+                                );
+     p_find_outmost_opaque_pixel(dst_pixfet
+                                ,dst_drawable->bpp
+                                ,alpha_rad
+                                ,dst_drawable->width
+                                ,dst_drawable->height
+                                ,&dx
+                                ,&dy
+                                );
+
+     /* create a new workpoint with sx,sy, dx, dy coords */
+     wp = gap_morph_shape_new_workpont(sx ,sy ,dx ,dy);
+     wp->next = mgpp->master_wp_list;
+     mgpp->master_wp_list = wp;
+
+     alpha_rad += step_rad;
+  }
+  gimp_pixel_fetcher_destroy (src_pixfet);
+  gimp_pixel_fetcher_destroy (dst_pixfet);
+
+  gimp_drawable_detach(src_drawable);
+  gimp_drawable_detach(dst_drawable);
+
+}  /* end gap_morph_shape_generate_outline_workpoints */
+
+
+/* -----------------------------------------------
+ * gap_morph_shape_generate_frame_tween_workpoints
+ * -----------------------------------------------
+ * generate workpoint files (one per frame)
+ * for the specified frame range.
+ * 
+ */
+gint32
+gap_morph_shape_generate_frame_tween_workpoints(GapAnimInfo *ainfo_ptr
+   , GapMorphGlobalParams *mgpp, GtkWidget *masterProgressBar, GtkWidget *progressBar, gboolean *cancelFlagPtr)
+{
+  GapMorphGUIParams morph_gui_params;
+  GapMorphGUIParams *mgup;
+  gint32 frameCount;
+  gint32 currFrameNr;
+  gint32 nextFrameNr;
+  gint32 currLayerId;
+  gint32 nextLayerId;
+  gint32 firstFrameNr;
+  gint32 lastFrameNr;
+  gdouble framesToProcess;
+  
+
+  mgup = &morph_gui_params;
+  mgup->mgpp = mgpp;
+  mgup->src_win.zoom = 1.0;  /* no zoom available here (use full drawable size for workpoint generation */
+  mgup->cancelWorkpointGeneration = FALSE;
+  mgup->progressBar = progressBar;  
+  mgpp->master_wp_list = NULL;
+
+  gimp_rgb_set(&mgup->pointcolor, 0.1, 1.0, 0.1); /* startup with GREEN pointcolor */
+  gimp_rgb_set_alpha(&mgup->pointcolor, 1.0);
+  gimp_rgb_set(&mgup->curr_pointcolor, 1.0, 1.0, 0.1); /* startup with YELLOW color */
+  gimp_rgb_set_alpha(&mgup->curr_pointcolor, 1.0);
+
+  frameCount = 0;
+  nextLayerId = -1;
+ 
+  if(gap_lib_chk_framerange(ainfo_ptr) != 0)
+  {
+    return(0);
+  }
+
+  
+  firstFrameNr = CLAMP(mgpp->range_from, ainfo_ptr->first_frame_nr, ainfo_ptr->last_frame_nr);
+  currFrameNr = firstFrameNr;
+  currLayerId = p_getMergedFrameImageLayer(ainfo_ptr, currFrameNr);
+  nextFrameNr = currFrameNr + 1;
+  while(TRUE)
+  {
+    lastFrameNr = MIN(mgpp->range_to, ainfo_ptr->last_frame_nr);
+    framesToProcess = MAX(1, (lastFrameNr - currFrameNr) -1);
+    
+    if (nextFrameNr > lastFrameNr)
+    {
+      break;
+    }
+    nextLayerId = p_getMergedFrameImageLayer(ainfo_ptr, nextFrameNr);
+    
+    if((nextLayerId >= 0) && (currLayerId >= 0))
+    {
+      char *workpointFileName;
+      gboolean success;
+
+      workpointFileName = gap_lib_alloc_fname(ainfo_ptr->basename,
+                                      currFrameNr,
+                                      "." GAP_MORPH_WORKPOINT_EXTENSION);      
+
+      p_do_master_progress(masterProgressBar
+                          , workpointFileName
+                          , framesToProcess
+                          , (currFrameNr - firstFrameNr)
+                          );
+   
+      success = p_generateWorkpointFileForFramePair(workpointFileName
+                              , mgup
+                              , cancelFlagPtr
+                              , currLayerId
+                              , nextLayerId
+                              );
+
+      g_free(workpointFileName);
+      gap_image_delete_immediate(gimp_drawable_get_image(currLayerId));
+      if(!success)
+      {
+        break;
+      }
+
+      currFrameNr = nextFrameNr;
+      currLayerId = nextLayerId;
+    }
+    nextFrameNr++;
+
+  }
+  
+  if(nextLayerId >= 0)
+  {
+    gap_image_delete_immediate(gimp_drawable_get_image(nextLayerId));
+  }
+
+  return (frameCount);
+}  /* end gap_morph_shape_generate_frame_tween_workpoints */
+
+
+
diff --git a/gap/gap_morph_shape.h b/gap/gap_morph_shape.h
new file mode 100644
index 0000000..02566c4
--- /dev/null
+++ b/gap/gap_morph_shape.h
@@ -0,0 +1,102 @@
+/* gap_morph_shape.h
+ *    image shape dependent morph workpoint creation
+ *    by hof (Wolfgang Hofer)
+ *  2010/08/10
+ *
+ */
+/* 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:
+ * version 2.7.0;             hof: created
+ */
+
+#ifndef _GAP_MORPH_SHAPE_H
+#define _GAP_MORPH_SHAPE_H
+
+/* SYTEM (UNIX) includes */
+#include <stdio.h>
+#include <stdlib.h>
+
+/* GIMP includes */
+#include "gtk/gtk.h"
+#include "libgimp/gimp.h"
+
+/* GAP includes */
+#include "gap_morph_main.h"
+#include "gap_morph_dialog.h"
+#include "gap_lib_common_defs.h"
+
+/* -----------------------------
+ * gap_morph_shape_new_workpont
+ * -----------------------------
+ */
+GapMorphWorkPoint *
+gap_morph_shape_new_workpont(gdouble srcx, gdouble srcy, gdouble dstx, gdouble dsty);
+
+
+
+/* --------------------------------
+ * gap_moprhShapeDetectionEdgeBased
+ * --------------------------------
+ *
+ * generates morph workpoints via edge based shape detection.
+ * This is done by edge detection in the source image
+ *  (specified via mgup->mgpp->osrc_layer_id)
+ *
+ * and picking workpoints on the detected edges
+ * and locating the corresponding point in the destination image.
+ *
+ * IN: mgup->num_shapepoints  specifies the number of workpoints to be generated.
+ */
+void
+gap_moprhShapeDetectionEdgeBased(GapMorphGUIParams *mgup, gboolean *cancelFlagPtr);
+
+
+
+/* -----------------------------------------------
+ * gap_morph_shape_generate_frame_tween_workpoints
+ * -----------------------------------------------
+ * call with progressBar in non-interactive mode
+ * or with progressBar when progress handling is required.
+ */
+gint32
+gap_morph_shape_generate_frame_tween_workpoints(GapAnimInfo *ainfo_ptr
+   , GapMorphGlobalParams *mgpp, GtkWidget *masterProgressBar, GtkWidget *progressBar, gboolean *cancelFlagPtr);
+
+
+/* -------------------------------------------
+ * gap_morph_shape_generate_outline_workpoints
+ * -------------------------------------------
+ */
+void
+gap_morph_shape_generate_outline_workpoints(GapMorphGlobalParams *mgpp);
+
+
+/* -------------------------------------------
+ * gap_morph_shape_calculateAngleDegree
+ * -------------------------------------------
+ * return angle of the workpoint movement vektor in degree
+ * Out: length of this vector
+ */
+gdouble
+gap_morph_shape_calculateAngleDegree(GapMorphWorkPoint *wp, gdouble *length);
+
+
+
+#endif
diff --git a/gap/gap_morph_tween_dialog.c b/gap/gap_morph_tween_dialog.c
index a6aa5cd..4cd809a 100644
--- a/gap/gap_morph_tween_dialog.c
+++ b/gap/gap_morph_tween_dialog.c
@@ -1,6 +1,9 @@
-/*  gap_morph_one_tween_dialog.c
+/*  gap_morph_tween_dialog.c
  *
- *  This module handles the GAP morph tween dialog
+ *  This module handles the GAP morph tween dialogs for
+ *     o) gap_morph_one_tween_dialog
+ *     o) gap_morph_frame_tweens_dialog
+ *     o) gap_morph_generate_frame_tween_workpoints_dialog  (Workpointfile Generator)
  */
 /* The GIMP -- an image manipulation program
  * Copyright (C) 1995 Spencer Kimball and Peter Mattis
@@ -28,6 +31,9 @@
 
 #include "gap_morph_main.h"
 #include "gap_morph_tween_dialog.h"
+#include "gap_morph_shape.h"
+#include "gap_base.h"
+
 #include "gap-intl.h"
 
 extern int gap_debug;  /* 1 == print debug infos , 0 dont print debug infos */
@@ -39,6 +45,21 @@ typedef struct MorphTweenGui { /* nickname: mtg */
      GapMorphGlobalParams *mgpp;
      GtkWidget *morph_filesel;
      GtkWidget *morph_entry;
+     GtkWidget *tween_subdir_entry;
+     GapAnimInfo *ainfo_ptr;
+     
+     GtkWidget *shell;
+     GtkWidget *masterProgressBar;
+     GtkWidget *progressBar;
+     gboolean   processingBusy;
+     gboolean   cancelProcessing;
+     gint32     ret;
+
+     GtkWidget *from_scale;
+     GtkWidget *from_spinbutton;
+     GtkWidget *to_scale;
+     GtkWidget *to_spinbutton;
+     
   } MorphTweenGui;
   
   
@@ -94,6 +115,108 @@ p_layerSelectionComboCallback (GtkWidget *widget)
 }  /* end p_layerSelectionComboCallback */
 
 
+/* --------------------------
+ * p_master_progress_callback
+ * --------------------------
+ */
+static void
+p_master_progress_callback(gdouble numDone, gdouble numTotal, const char *filename, gpointer data)
+{
+  MorphTweenGui *mtg;
+  
+  mtg = (MorphTweenGui *) data;
+  if(mtg)
+  {
+    if(mtg->masterProgressBar)
+    {
+      guchar *progressText;
+      gdouble percentage;
+      
+      percentage = numDone / MAX(1.0, numTotal);
+      
+      if(filename != NULL)
+      {
+        char *shortFileName;
+    
+        shortFileName = gap_base_shorten_filename(NULL       /* prefix */
+                        ,filename     /* filenamepart */
+                        ,NULL                  /* suffix */
+                        ,130                   /* l_max_chars */
+                        );
+        progressText = g_strdup_printf("%s  (%.0f / %.0f)"
+                     , shortFileName
+                     , (float)(MIN(numDone +1.0, numTotal))
+                     , (float)(numTotal)
+                     );
+        g_free(shortFileName);
+      }
+      else
+      {
+        progressText = g_strdup_printf(_("Tween %.0f / %.0f")
+                     , (float)(MIN(numDone +1.0, numTotal))
+                     , (float)(numTotal)
+                     );
+      }
+  
+      gtk_progress_bar_set_text(GTK_PROGRESS_BAR(mtg->masterProgressBar), progressText);
+      gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(mtg->masterProgressBar), percentage);
+      g_free(progressText);
+
+      while (gtk_events_pending ())
+      {
+        gtk_main_iteration ();
+      }
+      
+    }
+  
+  }
+}  /* end p_master_progress_callback */
+
+
+/* --------------------------
+ * p_progress_callback
+ * --------------------------
+ */
+static void
+p_progress_callback(gdouble percentage, gpointer data)
+{
+  MorphTweenGui *mtg;
+
+  mtg = (MorphTweenGui *) data;
+  if(mtg)
+  {
+    if(mtg->progressBar)
+    {
+      guchar *progressText;
+
+      if(mtg->mgpp->do_simple_fade == TRUE)
+      {
+        progressText = g_strdup_printf(_("render tween via fade algorithm %.2f%%")
+                     , (float)(percentage * 100)
+                     );
+      }
+      else
+      {
+        progressText = g_strdup_printf(_("render tween via morphing algorithm %.2f%%")
+                     , (float)(percentage * 100)
+                     );
+      }
+      gtk_progress_bar_set_text(GTK_PROGRESS_BAR(mtg->progressBar), progressText);
+      gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(mtg->progressBar), percentage);
+      g_free(progressText);
+
+      while (gtk_events_pending ())
+      {
+        gtk_main_iteration ();
+      }
+      
+    }
+  }
+  
+  
+}  /* end p_progress_callback */
+
+
 
 
 /* --------------------------
@@ -180,6 +303,25 @@ p_check_workpoint_file_and_use_single_fade_if_missing(GapMorphGlobalParams *mgpp
    }
 }
 
+
+/* --------------------------------------
+ * on_gboolean_button_update
+ * --------------------------------------
+ */
+static void
+on_gboolean_button_update (GtkWidget *widget,
+                           gpointer   data)
+{
+  gint *toggle_val = (gint *) data;
+
+  if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)))
+    *toggle_val = TRUE;
+  else
+    *toggle_val = FALSE;
+
+  gimp_toggle_button_sensitive_update (GTK_TOGGLE_BUTTON (widget));
+}
+
 /* --------------------------------------
  * p_morph_workpoint_entry_update_cb
  * --------------------------------------
@@ -207,6 +349,25 @@ p_morph_workpoint_entry_update_cb(GtkWidget *widget, GapMorphGlobalParams *mgpp)
    
 }  /* end p_morph_workpoint_entry_update_cb */
 
+/* --------------------------------------
+ * p_tween_subdir_entry_update_cb
+ * --------------------------------------
+ */
+static void
+p_tween_subdir_entry_update_cb(GtkWidget *widget, GapMorphGlobalParams *mgpp)
+{
+   g_snprintf(&mgpp->tween_subdir[0]
+           , sizeof(mgpp->tween_subdir)
+           , "%s"
+           , gtk_entry_get_text(GTK_ENTRY(widget))
+           );
+   if(gap_debug)
+   {
+     printf("p_tween_subdir_entry_update_cb  tween_subdir: %s\n"
+       , &mgpp->tween_subdir[0]);
+   }
+  
+}  /* end p_tween_subdir_entry_update_cb */
 
 
 /* --------------------------------------
@@ -236,7 +397,7 @@ p_create_morph_workpoint_entry(GapMorphGlobalParams *mgpp, MorphTweenGui *mtg, g
   }
   gtk_table_attach(GTK_TABLE(table), entry, 1, 2, row, row + 1, GTK_FILL, GTK_FILL | GTK_EXPAND, 4, 0);
   gimp_help_set_help_data(entry, _("Name of a Workpointfile created with the Morph feature\n"
-                                   "(note that tweens are created via simple fade operations when no workpointfile is available))"), NULL);
+                                   "(note that tweens are created via simple fade operations when no workpointfile is available)"), NULL);
   gtk_widget_show(entry);
 
   g_signal_connect(G_OBJECT(entry), "changed",
@@ -281,7 +442,7 @@ gap_morph_one_tween_dialog(GapMorphGlobalParams *mgpp)
 
   gimp_ui_init (GAP_MORPH_TWEEN_PLUGIN_NAME, TRUE);
 
-  dialog = gimp_dialog_new (_("Create one tween"), GAP_MORPH_TWEEN_PLUGIN_NAME,
+  dialog = gimp_dialog_new (_("Create one tween as Layer"), GAP_MORPH_TWEEN_PLUGIN_NAME,
                             NULL, 0,
                             gimp_standard_help_func, GAP_MORPH_TWEEN_HELP_ID,
 
@@ -380,11 +541,508 @@ gap_morph_one_tween_dialog(GapMorphGlobalParams *mgpp)
 } /* end gap_morph_one_tween_dialog */
 
 
+
+
+/* -------------------------------
+ * on_workpoint_generator_response
+ * -------------------------------
+ */
+static void
+on_workpoint_generator_response(GtkWidget *w, gint response_id, MorphTweenGui *mtg)
+{
+  GtkWidget *dialog;
+
+  switch (response_id)
+  {
+    case GTK_RESPONSE_OK:
+      if(mtg == NULL)
+      {
+        return;
+      }
+      if((mtg->cancelProcessing == FALSE))
+      {
+        if(mtg->processingBusy == TRUE)
+        {
+          return;
+        }
+        mtg->processingBusy = TRUE;
+        gtk_widget_set_sensitive(mtg->from_scale, FALSE);
+        gtk_widget_set_sensitive(mtg->from_spinbutton, FALSE);
+        
+        
+        mtg->mgpp->use_gravity = FALSE;
+        if(mtg->mgpp->gravity_intensity >= 1.0)
+        {
+          mtg->mgpp->use_gravity = TRUE;
+        }
+        mtg->ret = gap_morph_shape_generate_frame_tween_workpoints(mtg->ainfo_ptr
+                                , mtg->mgpp
+                                , mtg->masterProgressBar
+                                , mtg->progressBar
+                                , &mtg->cancelProcessing
+                                );
+        mtg->cancelProcessing = TRUE;
+        mtg->processingBusy = FALSE;
+      }
+      /* fall through (close dialog window when cancel or done) */
+    default:
+      dialog = NULL;
+      if(mtg)
+      {
+        if((mtg->processingBusy == TRUE) && (mtg->cancelProcessing == FALSE))
+        {
+          mtg->cancelProcessing = TRUE;
+          return;
+        }
+        /* close (or terminate) dialog window */
+        dialog = mtg->shell;
+        if(dialog)
+        {
+          mtg->shell = NULL;
+          gtk_widget_destroy (dialog);
+        }
+      }
+      gtk_main_quit ();
+      break;
+  }
+}  /* end on_workpoint_generator_response */
+
+
+/* ------------------------------------------------
+ * gap_morph_generate_frame_tween_workpoints_dialog
+ * ------------------------------------------------
+ */
+gint32
+gap_morph_generate_frame_tween_workpoints_dialog(GapAnimInfo *ainfo_ptr, GapMorphGlobalParams *mgpp)
+{
+  MorphTweenGui  morphTweenGui;
+  MorphTweenGui  *mtg;
+  GtkWidget *dialog;
+  GtkWidget *main_vbox;
+  GtkWidget *table;
+  GtkObject *adj;
+  GtkWidget *scale;
+  GtkWidget *spinbutton;
+  GtkWidget *label;
+  GtkWidget *checkbutton;
+  GtkWidget *progressBar;
+  gint       row;
+
+  mgpp->range_from = ainfo_ptr->curr_frame_nr;
+  mgpp->range_to = ainfo_ptr->last_frame_nr;
+  mtg = &morphTweenGui;
+  mtg->mgpp = mgpp;
+  mtg->ainfo_ptr = ainfo_ptr;
+  mtg->morph_filesel = NULL;
+  mtg->cancelProcessing = FALSE;
+  mtg->processingBusy = FALSE;
+  mtg->ret = 0;
+  
+  
+  p_check_workpoint_file_and_use_single_fade_if_missing(mgpp);
+
+
+  if(gap_debug)
+  {
+    printf("gap_morph_generate_frame_tween_workpoints_dialog: mgpp->range_from%d  mgpp->range_to:%d  first:%d, curr:%d, last:%d after_curr:%d\n"
+      , (int)mgpp->range_from
+      , (int)mgpp->range_to
+      , (int)ainfo_ptr->first_frame_nr
+      , (int)ainfo_ptr->curr_frame_nr
+      , (int)ainfo_ptr->last_frame_nr
+      , (int)ainfo_ptr->frame_nr_after_curr_frame_nr
+      );
+  }
+
+
+  gimp_ui_init (GAP_MORPH_WORKPOINTS_PLUGIN_NAME, TRUE);
+
+  dialog = gimp_dialog_new (_("Generate Workpointfiles"), GAP_MORPH_WORKPOINTS_PLUGIN_NAME,
+                            NULL, 0,
+                            gimp_standard_help_func, GAP_MORPH_WORKPOINTS_HELP_ID,
+
+                            GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+                            GTK_STOCK_OK,     GTK_RESPONSE_OK,
+
+                            NULL);
+
+  mtg->shell = dialog;
+
+  gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
+                                           GTK_RESPONSE_OK,
+                                           GTK_RESPONSE_CANCEL,
+                                           -1);
+
+  gimp_window_set_transient (GTK_WINDOW (dialog));
+  gtk_window_set_type_hint (dialog, GDK_WINDOW_TYPE_HINT_NORMAL);
+
+  g_signal_connect (G_OBJECT (dialog), "response",
+                    G_CALLBACK (on_workpoint_generator_response),
+                    mtg);
+
+  main_vbox = gtk_vbox_new (FALSE, 12);
+  gtk_container_set_border_width (GTK_CONTAINER (main_vbox), 12);
+  gtk_container_add (GTK_CONTAINER (GTK_DIALOG (dialog)->vbox), main_vbox);
+  gtk_widget_show (main_vbox);
+
+
+  /* Controls */
+  table = gtk_table_new (3, 3, FALSE);
+  gtk_table_set_col_spacings (GTK_TABLE (table), 6);
+  gtk_table_set_row_spacings (GTK_TABLE (table), 6);
+  gtk_box_pack_start (GTK_BOX (main_vbox), table, FALSE, FALSE, 0);
+  gtk_widget_show (table);
+
+  row = 0;
+
+  adj = gimp_scale_entry_new (GTK_TABLE (table), 0, row,
+                              _("From:"), SCALE_WIDTH, 7,
+                              mgpp->range_from, ainfo_ptr->first_frame_nr, ainfo_ptr->last_frame_nr, 1.0, 10.0, 0,
+                              TRUE, 0, 0,
+                              _("First processed frame"), NULL);
+  scale = g_object_get_data(G_OBJECT (adj), "scale");
+  spinbutton = g_object_get_data(G_OBJECT (adj), "spinbutton");
+  mtg->from_scale = scale;
+  mtg->from_spinbutton = spinbutton;
+  g_signal_connect (adj, "value-changed",
+                    G_CALLBACK (gimp_int_adjustment_update),
+                    &mgpp->range_from);
+
+
+  row++;
+
+  adj = gimp_scale_entry_new (GTK_TABLE (table), 0, row,
+                              _("To:"), SCALE_WIDTH, 7,
+                              mgpp->range_to, mgpp->range_from +1, ainfo_ptr->last_frame_nr, 1.0, 10.0, 0,
+                              TRUE, 0, 0,
+                              _("Last processed frame"), NULL);
+  scale = g_object_get_data(G_OBJECT (adj), "scale");
+  spinbutton = g_object_get_data(G_OBJECT (adj), "spinbutton");
+  mtg->to_scale = scale;
+  mtg->to_spinbutton = spinbutton;
+  g_signal_connect (adj, "value-changed",
+                    G_CALLBACK (gimp_int_adjustment_update),
+                    &mgpp->range_to);
+
+  row++;
+
+  adj = gimp_scale_entry_new (GTK_TABLE (table), 0, row,
+                              _("Num Workpoints:"), SCALE_WIDTH, 7,
+                              mgpp->numWorkpoints, 1, 5000, 1.0, 10.0, 0,
+                              TRUE, 0, 0,
+                              _("Number of workpoints to be generated par processed frame"),
+                              NULL);
+  g_signal_connect (adj, "value-changed",
+                    G_CALLBACK (gimp_int_adjustment_update),
+                    &mgpp->numWorkpoints);
+
+  row++;
+
+  adj = gimp_scale_entry_new (GTK_TABLE (table), 0, row,
+                              _("Num Outlinepoints:"), SCALE_WIDTH, 7,
+                              mgpp->numOutlinePoints, 1, 100, 1.0, 10.0, 0,
+                              TRUE, 0, 0,
+                              _("Number of additional workpoints on the outline of opaque image area")
+                              , NULL);
+  g_signal_connect (adj, "value-changed",
+                    G_CALLBACK (gimp_int_adjustment_update),
+                    &mgpp->numOutlinePoints);
+
+
+  row++;
+
+  adj = gimp_scale_entry_new (GTK_TABLE (table), 0, row,
+                              _("Tween Steps:"), SCALE_WIDTH, 7,
+                              mgpp->tween_steps, 1, 100, 1.0, 10.0, 0,
+                              TRUE, 0, 0,
+                              _("TWEEN-STEPS attribute value to be written to the generated workpoint file."
+                                " (Number of tweens to be inserted between 2 frames at tween morphprocessing) "),
+                              NULL);
+  g_signal_connect (adj, "value-changed",
+                    G_CALLBACK (gimp_int_adjustment_update),
+                    &mgpp->tween_steps);
+  
+  row++;
+
+
+  adj = gimp_scale_entry_new (GTK_TABLE (table), 0, row,
+                              _("Deform Radius:"), SCALE_WIDTH, 7,
+                              mgpp->affect_radius, 0, 10000, 1.0, 10.0, 0,
+                              TRUE, 0, 0,
+                              _("AFFECT-RADIUS attribute value to be written to the generated workpoint file."),
+                              NULL);
+  g_signal_connect (adj, "value-changed",
+                    G_CALLBACK (gimp_int_adjustment_update),
+                    &mgpp->affect_radius);
+  
+  row++;
+
+  if(!mgpp->use_gravity)
+  {
+    mgpp->gravity_intensity = 0.0;
+  }
+
+  adj = gimp_scale_entry_new (GTK_TABLE (table), 0, row,
+                              _("Intensity:"), SCALE_WIDTH, 7,
+                              mgpp->gravity_intensity, 0.0, 5.0, 0.1, 0.1, 1,
+                              TRUE, 0, 0,
+                              _("INTENSITY attribute value to be written to the generated workpoint file. "
+                                "value 0 turns off intensity desceding deformation, "
+                                "morph processing will use linear deform action inside the deform radius"),
+                              NULL);
+  g_signal_connect (adj, "value-changed",
+                    G_CALLBACK (gimp_double_adjustment_update),
+                    &mgpp->gravity_intensity);
+
+
+
+
+
+  row++;
+
+  adj = gimp_scale_entry_new (GTK_TABLE (table), 0, row,
+                              _("Locate Move Radius:"), SCALE_WIDTH, 7,
+                              mgpp->locateDetailMoveRadius, 1, 500, 1.0, 10.0, 0,
+                              TRUE, 0, 0,
+                              _("Locate radius in pixels. "
+                                "The workpoint generation searches for corresponding points "
+                                "in the next frame within this radius"),
+                              NULL);
+  g_signal_connect (adj, "value-changed",
+                    G_CALLBACK (gimp_int_adjustment_update),
+                    &mgpp->locateDetailMoveRadius);
+
+  row++;
+
+  adj = gimp_scale_entry_new (GTK_TABLE (table), 0, row,
+                              _("Locate Shape Radius:"), SCALE_WIDTH, 7,
+                              mgpp->locateDetailShapeRadius,
+                               GAP_LOCATE_MIN_REF_SHAPE_RADIUS, 
+                               GAP_LOCATE_MAX_REF_SHAPE_RADIUS,
+                               1.0, 10.0, 0,
+                              TRUE, 0, 0,
+                              _("Locate Shaperadius in pixels. "
+                                "Defines shape size as area around workpoint to be compared "
+                                " when loacting corresponding coordinate in the next frame."),
+                              NULL);
+  g_signal_connect (adj, "value-changed",
+                    G_CALLBACK (gimp_int_adjustment_update),
+                    &mgpp->locateDetailShapeRadius);
+  row++;
+  
+  adj = gimp_scale_entry_new (GTK_TABLE (table), 0, row,
+                                _("Edge Threshold:"), SCALE_WIDTH, 7,
+                                mgpp->edgeColordiffThreshold, 0.01, 0.35, 0.01, 0.1, 2,
+                                TRUE, 0, 0,
+                                _("Edge detection threshold. "
+                                  "Workpoints are generated on detected edges. "
+                                  "Edges are detected on pixels where color or opacity differs significant "
+                                  "from the neighbor pixel."
+                                  "(e.g. more than the specified edge detection threshold)."),
+                                NULL);
+  g_signal_connect (adj, "value-changed",
+                      G_CALLBACK (gimp_double_adjustment_update),
+                      &mgpp->edgeColordiffThreshold);
+  if(gap_debug)
+  {
+
+    row++;
+
+    adj = gimp_scale_entry_new (GTK_TABLE (table), 0, row,
+                                _("Locate ColordiffEdge Threshold:"), SCALE_WIDTH, 7,
+                                mgpp->locateColordiffThreshold, 0.01, 0.1, 0.01, 0.1, 3,
+                                TRUE, 0, 0,
+                                NULL, NULL);
+    g_signal_connect (adj, "value-changed",
+                      G_CALLBACK (gimp_double_adjustment_update),
+                      &mgpp->locateColordiffThreshold);
+ 
+  }
+
+  row++;
+
+  /* the use_quality_wp_selection checkbutton */
+  checkbutton = gtk_check_button_new_with_label ( _("Quality"));
+  gtk_widget_show (checkbutton);
+  gtk_table_attach( GTK_TABLE(table), checkbutton, 2, 3, row, row+1,
+                    GTK_FILL, 0, 0, 0 );
+  g_signal_connect (checkbutton, "toggled",
+                    G_CALLBACK (on_gboolean_button_update),
+                    &mgpp->use_quality_wp_selection);
+  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (checkbutton), mgpp->use_quality_wp_selection);
+  gimp_help_set_help_data(checkbutton,
+                       _("ON: Use quality workpoint selection algorithm."
+                         "OFF: Use fast workpoint selection algorithm.")
+                       , NULL);
+  row++;
+
+
+  /* the overwrite checkbutton */
+  checkbutton = gtk_check_button_new_with_label ( _("Overwrite"));
+  gtk_widget_show (checkbutton);
+  gtk_table_attach( GTK_TABLE(table), checkbutton, 1, 2, row, row+1,
+                    GTK_FILL, 0, 0, 0 );
+  g_signal_connect (checkbutton, "toggled",
+                    G_CALLBACK (on_gboolean_button_update),
+                    &mgpp->overwrite_flag);
+  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (checkbutton), mgpp->overwrite_flag);
+  gimp_help_set_help_data(checkbutton,
+                       _("ON: overwrite existing workpointfiles."
+                         "OFF: Skip workpoint generation or add new generated workpoints (see append checkbutton).")
+                       , NULL);
+
+
+  /* the overwrite checkbutton */
+  checkbutton = gtk_check_button_new_with_label ( _("Append"));
+  gtk_widget_show (checkbutton);
+  gtk_table_attach( GTK_TABLE(table), checkbutton, 2, 3, row, row+1,
+                    GTK_FILL, 0, 0, 0 );
+  g_signal_connect (checkbutton, "toggled",
+                    G_CALLBACK (on_gboolean_button_update),
+                    &mgpp->append_flag);
+  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (checkbutton), mgpp->append_flag);
+  gimp_help_set_help_data(checkbutton,
+                       _("ON: add newly generated workpoints to existing workpointfiles."
+                         "OFF: Skip workpoint generation for frames where workpointfile already exists.")
+                       , NULL);
+
+  row++;
+
+  /* the master progress bar */
+
+  /* master progress */
+  label = gtk_label_new (_("Create File(s):"));
+  gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
+  gtk_table_attach (GTK_TABLE (table), label, 0, 1, row, row + 1,
+                    GTK_FILL, GTK_FILL, 4, 0);
+  gtk_widget_show (label);
+
+  /* the master progress bar */
+  progressBar = gtk_progress_bar_new ();
+  mtg->masterProgressBar = progressBar;
+  {
+    char *suffix;
+    char *msg;
+    suffix = g_strdup_printf("%s.%s"
+                         ,"######"
+                         ,GAP_MORPH_WORKPOINT_EXTENSION
+                         );
+    msg = gap_base_shorten_filename(NULL        /* prefix */
+                        ,ainfo_ptr->basename    /* filenamepart */
+                        ,suffix                 /* suffix */
+                        ,130                     /* l_max_chars */
+                        );
+    gtk_progress_bar_set_text(GTK_PROGRESS_BAR(progressBar), msg);
+    g_free(msg);
+    g_free(suffix);
+  }
+
+  gtk_table_attach (GTK_TABLE (table), progressBar, 1, 3, row, row + 1,
+                    GTK_FILL, GTK_FILL, 4, 0);
+  gtk_widget_show (progressBar);
+
+  row++;
+
+  label = gtk_label_new (_("Create Points:"));
+  gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
+  gtk_table_attach (GTK_TABLE (table), label, 0, 1, row, row + 1,
+                    GTK_FILL, GTK_FILL, 4, 0);
+  gtk_widget_show (label);
+
+
+  /* the progress bar */
+  progressBar = gtk_progress_bar_new ();
+  mtg->progressBar = progressBar;
+  gtk_progress_bar_set_text(GTK_PROGRESS_BAR(progressBar), " ");
+  gtk_widget_show (progressBar);
+  gtk_table_attach( GTK_TABLE(table), progressBar, 1, 3, row, row+1,
+                    GTK_FILL, 0, 0, 0 );
+  
+  /* Done */
+
+  gtk_widget_show (dialog);
+
+  gtk_main ();
+  gdk_flush ();
+
+
+  return (mtg->ret);
+} /* end gap_morph_generate_frame_tween_workpoints_dialog */
+
+
+
+/* -------------------------------
+ * on_morph_frame_tweens_response
+ * -------------------------------
+ */
+static void
+on_morph_frame_tweens_response(GtkWidget *w, gint response_id, MorphTweenGui *mtg)
+{
+  GtkWidget *dialog;
+
+  switch (response_id)
+  {
+    case GTK_RESPONSE_OK:
+      if(mtg == NULL)
+      {
+        return;
+      }
+      if((mtg->cancelProcessing == FALSE))
+      {
+        if(mtg->processingBusy == TRUE)
+        {
+          return;
+        }
+        mtg->processingBusy = TRUE;
+        mtg->mgpp->do_progress = TRUE;
+        mtg->mgpp->master_progress_callback_fptr = p_master_progress_callback;
+        mtg->mgpp->progress_callback_fptr = p_progress_callback;
+        mtg->mgpp->callback_data_ptr = mtg;
+        gtk_widget_set_sensitive(mtg->from_scale, FALSE);
+        gtk_widget_set_sensitive(mtg->from_spinbutton, FALSE);
+        
+        mtg->ret = gap_morph_render_frame_tweens(mtg->ainfo_ptr
+                                , mtg->mgpp
+                                , &mtg->cancelProcessing
+                                );
+        mtg->cancelProcessing = TRUE;
+        mtg->processingBusy = FALSE;
+      }
+      /* fall through (close dialog window when cancel or done) */
+    default:
+      dialog = NULL;
+      if(mtg)
+      {
+        if((mtg->processingBusy == TRUE) && (mtg->cancelProcessing == FALSE))
+        {
+          mtg->cancelProcessing = TRUE;
+          return;
+        }
+        /* close (or terminate) dialog window */
+        dialog = mtg->shell;
+        if(dialog)
+        {
+          mtg->shell = NULL;
+          gtk_widget_destroy (dialog);
+        }
+      }
+      gtk_main_quit ();
+      break;
+  }
+}  /* end on_morph_frame_tweens_response */
+
+
+
+
+
+
+
+
 /* --------------------------------------
  * gap_morph_frame_tweens_dialog
  * --------------------------------------
  */
-gboolean
+gint32
 gap_morph_frame_tweens_dialog(GapAnimInfo *ainfo_ptr, GapMorphGlobalParams *mgpp)
 {
   MorphTweenGui  morphTweenGui;
@@ -396,18 +1054,28 @@ gap_morph_frame_tweens_dialog(GapAnimInfo *ainfo_ptr, GapMorphGlobalParams *mgpp
   GtkWidget *scale;
   GtkWidget *spinbutton;
   GtkWidget *label;
+  GtkWidget *entry;
+  GtkWidget *checkbutton;
+  GtkWidget *progressBar;
   gint       row;
-  gboolean   run;
   gboolean   isFrameMissing;
 
   mgpp->range_from = ainfo_ptr->curr_frame_nr;
   mgpp->range_to = ainfo_ptr->frame_nr_after_curr_frame_nr;
   mtg = &morphTweenGui;
   mtg->mgpp = mgpp;
+  mtg->ainfo_ptr = ainfo_ptr;
   mtg->morph_filesel = NULL;
+  mtg->ret = -1;
   isFrameMissing = FALSE;
+
   p_check_workpoint_file_and_use_single_fade_if_missing(mgpp);
 
+  isFrameMissing = (ainfo_ptr->curr_frame_nr +1) != ainfo_ptr->frame_nr_after_curr_frame_nr;
+  if(!isFrameMissing)
+  {
+    mgpp->range_to = ainfo_ptr->last_frame_nr;
+  }
 
   if(gap_debug)
   {
@@ -421,7 +1089,6 @@ gap_morph_frame_tweens_dialog(GapAnimInfo *ainfo_ptr, GapMorphGlobalParams *mgpp
       );
   }
 
-  isFrameMissing = (ainfo_ptr->curr_frame_nr +1) != ainfo_ptr->frame_nr_after_curr_frame_nr;
 
   gimp_ui_init (GAP_MORPH_TWEEN_PLUGIN_NAME, TRUE);
 
@@ -434,6 +1101,8 @@ gap_morph_frame_tweens_dialog(GapAnimInfo *ainfo_ptr, GapMorphGlobalParams *mgpp
 
                             NULL);
 
+  mtg->shell = dialog;
+
 
   gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
                                            GTK_RESPONSE_OK,
@@ -441,6 +1110,11 @@ gap_morph_frame_tweens_dialog(GapAnimInfo *ainfo_ptr, GapMorphGlobalParams *mgpp
                                            -1);
 
   gimp_window_set_transient (GTK_WINDOW (dialog));
+  gtk_window_set_type_hint (dialog, GDK_WINDOW_TYPE_HINT_NORMAL);
+
+  g_signal_connect (G_OBJECT (dialog), "response",
+                    G_CALLBACK (on_morph_frame_tweens_response),
+                    mtg);
 
   main_vbox = gtk_vbox_new (FALSE, 12);
   gtk_container_set_border_width (GTK_CONTAINER (main_vbox), 12);
@@ -456,7 +1130,8 @@ gap_morph_frame_tweens_dialog(GapAnimInfo *ainfo_ptr, GapMorphGlobalParams *mgpp
   gtk_widget_show (table);
 
   row = 0;
-  /* morph workpoint entry */
+
+  /* label */
   label = gtk_label_new (_("Information:"));
   gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
   gtk_table_attach (GTK_TABLE (table), label, 0, 1, row, row + 1,
@@ -477,7 +1152,11 @@ gap_morph_frame_tweens_dialog(GapAnimInfo *ainfo_ptr, GapMorphGlobalParams *mgpp
   }
   else
   {
-    label = gtk_label_new (_("WARNING this operation will overwrite all frames between the specified frame range"));
+    label = gtk_label_new (_("this operation creates copies of all frames in the specified range\n"
+                             "and the specifed number of tweens as additional tween frames\n"
+                             "between all the processed frames in the specified subdirectory.\n"
+                             "Provide workpointfiles (one per frame) for morphing based tween rendering\n"
+                             "(this can be done with the Morph Workpoint Generator)"));
     mgpp->overwrite_flag = TRUE;
   }
   gtk_misc_set_alignment (GTK_MISC (label), 0.5, 0.5);
@@ -489,25 +1168,30 @@ gap_morph_frame_tweens_dialog(GapAnimInfo *ainfo_ptr, GapMorphGlobalParams *mgpp
 
   adj = gimp_scale_entry_new (GTK_TABLE (table), 0, row,
                               _("From:"), SCALE_WIDTH, 7,
-                              mgpp->range_from, ainfo_ptr->first_frame_nr, ainfo_ptr->last_frame_nr, 1.0, 10.0, 0,
+                              mgpp->range_from, ainfo_ptr->first_frame_nr, ainfo_ptr->last_frame_nr-1, 1.0, 10.0, 0,
                               TRUE, 0, 0,
-                              NULL, NULL);
+                              _("First processed frame"), NULL);
   g_signal_connect (adj, "value-changed",
                     G_CALLBACK (gimp_int_adjustment_update),
                     &mgpp->range_from);
 
   scale = g_object_get_data(G_OBJECT (adj), "scale");
   spinbutton = g_object_get_data(G_OBJECT (adj), "spinbutton");
-  gtk_widget_set_sensitive(scale, FALSE);
-  gtk_widget_set_sensitive(spinbutton, FALSE);
-
+  mtg->from_scale = scale;
+  mtg->from_spinbutton = spinbutton;
+  if(isFrameMissing)
+  {
+    gtk_widget_set_sensitive(scale, FALSE);
+    gtk_widget_set_sensitive(spinbutton, FALSE);
+  }
+  
   row++;
 
   adj = gimp_scale_entry_new (GTK_TABLE (table), 0, row,
                               _("To:"), SCALE_WIDTH, 7,
                               mgpp->range_to, mgpp->range_from +1, ainfo_ptr->last_frame_nr, 1.0, 10.0, 0,
                               TRUE, 0, 0,
-                              NULL, NULL);
+                              _("Last processed frame"), NULL);
   g_signal_connect (adj, "value-changed",
                     G_CALLBACK (gimp_int_adjustment_update),
                     &mgpp->range_to);
@@ -519,18 +1203,142 @@ gap_morph_frame_tweens_dialog(GapAnimInfo *ainfo_ptr, GapMorphGlobalParams *mgpp
     gtk_widget_set_sensitive(spinbutton, FALSE);
   }
 
+
+  if (isFrameMissing)
+  {
+    row++;
+
+    p_create_morph_workpoint_entry(mgpp, mtg, row, table);
+
+    mgpp->master_tween_steps = 0;
+    mgpp->create_tweens_in_subdir = FALSE;
+  }
+  else
+  {
+    row++;
+
+    mgpp->create_tweens_in_subdir = TRUE;
+    
+    adj = gimp_scale_entry_new (GTK_TABLE (table), 0, row,
+                                _("Number of Tweens:"), SCALE_WIDTH, 7,
+                                mgpp->master_tween_steps, 0, 100, 1.0, 10.0, 0,
+                                TRUE, 0, 0,
+                                _("Number of tweens to be inserted between 2 frames. "
+                                  "Value 0 renderes missing frames (via morping or fade)"
+                                  "but does not create tweens where the "
+                                  "next frame number is equal to the current processed frame number +1"),
+                                NULL);
+    g_signal_connect (adj, "value-changed",
+                      G_CALLBACK (gimp_int_adjustment_update),
+                      &mgpp->master_tween_steps);
+
+    row++;
+
+    /* the create_tweens_in_subdir checkbutton */
+//     checkbutton = gtk_check_button_new_with_label ( _("Subdirectory:"));
+//     gtk_widget_show (checkbutton);
+//     gtk_table_attach( GTK_TABLE(table), checkbutton, 0, 1, row, row+1,
+//                       GTK_FILL, 0, 0, 0 );
+//     g_signal_connect (checkbutton, "toggled",
+//                       G_CALLBACK (on_gboolean_button_update),
+//                       &mgpp->create_tweens_in_subdir);
+//     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (checkbutton), mgpp->create_tweens_in_subdir);
+//     gimp_help_set_help_data(checkbutton,
+//                          _("ON: copy processed frames to a subdirectory "
+//                            "and create tween frames in this subdirectory via morping."
+//                            "OFF: Render missing frames via moprhing. ")
+//                          , NULL);
+
+    /* label */
+    label = gtk_label_new (_("Subdirectory:"));
+    gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
+    gtk_table_attach (GTK_TABLE (table), label, 0, 1, row, row + 1,
+                      GTK_FILL, GTK_FILL, 4, 0);
+    gtk_widget_show (label);
+
+    /* subdirectory entry */
+    entry = gtk_entry_new();
+    mtg->tween_subdir_entry = entry;
+    gtk_widget_set_size_request(entry, SCALE_WIDTH, -1);
+    if(mgpp->tween_subdir[0] != '\0')
+    {
+      gtk_entry_set_text(GTK_ENTRY(entry), &mgpp->tween_subdir[0]);
+    }
+    gtk_table_attach(GTK_TABLE(table), entry, 1, 2, row, row + 1, GTK_FILL, GTK_FILL | GTK_EXPAND, 4, 0);
+    gimp_help_set_help_data(entry, _("Name of a (Sub)directoy to save copies of processed frames "
+                                     "and generated tweens. "
+                                     "Note that tweens are created via simple fade operations "
+                                     "when no workpointfile for the processed frame is available. "
+                                     "(individual workpointfiles per frame are refered by extension .morphpoints)")
+                           , NULL);
+    gtk_widget_show(entry);
+
+    g_signal_connect(G_OBJECT(entry), "changed",
+                     G_CALLBACK (p_tween_subdir_entry_update_cb),
+                     mgpp);
+
+    row++;
+
+    /* the overwrite checkbutton */
+    checkbutton = gtk_check_button_new_with_label ( _("Overwrite"));
+    gtk_widget_show (checkbutton);
+    gtk_table_attach( GTK_TABLE(table), checkbutton, 0, 1, row, row+1,
+                      GTK_FILL, 0, 0, 0 );
+    g_signal_connect (checkbutton, "toggled",
+                      G_CALLBACK (on_gboolean_button_update),
+                      &mgpp->overwrite_flag);
+    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (checkbutton), mgpp->overwrite_flag);
+    gimp_help_set_help_data(checkbutton,
+                         _("ON: overwrite existing frames."
+                           "OFF: skip processing when target frame/tween already exists.")
+                         , NULL);
+  }
+
+
   row++;
 
-  p_create_morph_workpoint_entry(mgpp, mtg, row, table);
+
+  /* the master progress bar */
+
+  /* master progress */
+  label = gtk_label_new (_("Create Tweenfame(s):"));
+  gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
+  gtk_table_attach (GTK_TABLE (table), label, 0, 1, row, row + 1,
+                    GTK_FILL, GTK_FILL, 4, 0);
+  gtk_widget_show (label);
+
+  /* the master progress bar */
+  progressBar = gtk_progress_bar_new ();
+  mtg->masterProgressBar = progressBar;
+  gtk_table_attach (GTK_TABLE (table), progressBar, 1, 3, row, row + 1,
+                    GTK_FILL, GTK_FILL, 4, 0);
+  gtk_widget_show (progressBar);
+
+  row++;
+
+  label = gtk_label_new (_("Local Progress:"));
+  gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
+  gtk_table_attach (GTK_TABLE (table), label, 0, 1, row, row + 1,
+                    GTK_FILL, GTK_FILL, 4, 0);
+  gtk_widget_show (label);
+
+
+  /* the progress bar */
+  progressBar = gtk_progress_bar_new ();
+  mtg->progressBar = progressBar;
+  gtk_progress_bar_set_text(GTK_PROGRESS_BAR(progressBar), " ");
+  gtk_widget_show (progressBar);
+  gtk_table_attach( GTK_TABLE(table), progressBar, 1, 3, row, row+1,
+                    GTK_FILL, 0, 0, 0 );
   
   /* Done */
 
   gtk_widget_show (dialog);
 
-  run = (gimp_dialog_run (GIMP_DIALOG (dialog)) == GTK_RESPONSE_OK);
+  gtk_main ();
+  gdk_flush ();
 
-  gtk_widget_destroy (dialog);
 
-  return run;
-} /* end gap_morph_frame_tweens_dialog */
+  return (mtg->ret);
 
+} /* end gap_morph_frame_tweens_dialog */
diff --git a/gap/gap_morph_tween_dialog.h b/gap/gap_morph_tween_dialog.h
index 13002bc..54e9036 100644
--- a/gap/gap_morph_tween_dialog.h
+++ b/gap/gap_morph_tween_dialog.h
@@ -1,4 +1,4 @@
-/*  gap_morph_one_tween_dialog.h
+/*  gap_morph_tween_dialog.h
  *
  *  This module handles the GAP morph tween dialog
  */
@@ -30,6 +30,7 @@
 #include "libgimp/gimp.h"
 #include "gap_libgimpgap.h"
 #include "gap_morph_main.h"
+#include "gap_locate.h"
 #include "gap_libgimpgap.h"
 
 #define GAP_MORPH_ONE_TWEEN_PLUGIN_NAME    "plug_in_gap_morph_one_tween"
@@ -38,7 +39,11 @@
 #define GAP_MORPH_TWEEN_PLUGIN_NAME    "plug_in_gap_morph_tween"
 #define GAP_MORPH_TWEEN_HELP_ID        "plug-in-gap-morph-tween"
 
-gboolean   gap_morph_one_tween_dialog(GapMorphGlobalParams *mgpp);
-gboolean   gap_morph_frame_tweens_dialog(GapAnimInfo *ainfo_ptr, GapMorphGlobalParams *mgpp);
+#define GAP_MORPH_WORKPOINTS_PLUGIN_NAME   "plug_in_gap_morph_workpoints"
+#define GAP_MORPH_WORKPOINTS_HELP_ID       "plug-in-gap-morph-workpoints"
 
+gboolean   gap_morph_one_tween_dialog(GapMorphGlobalParams *mgpp);
+gint32     gap_morph_frame_tweens_dialog(GapAnimInfo *ainfo_ptr, GapMorphGlobalParams *mgpp);
+gint32     gap_morph_generate_frame_tween_workpoints_dialog(GapAnimInfo *ainfo_ptr
+                   , GapMorphGlobalParams *mgpp);
 #endif
diff --git a/gap/gap_mov_dialog.h b/gap/gap_mov_dialog.h
index c3be9a2..6c996eb 100644
--- a/gap/gap_mov_dialog.h
+++ b/gap/gap_mov_dialog.h
@@ -235,6 +235,7 @@ typedef struct {
         /* for the bluebox filter */
         GapBlueboxGlobalParams *bbp;
         GapBlueboxGlobalParams *bbp_pv;
+        
 
 } GapMovValues;
 
diff --git a/gap/gap_mov_exec.c b/gap/gap_mov_exec.c
index 19b45ef..98fc37f 100644
--- a/gap/gap_mov_exec.c
+++ b/gap/gap_mov_exec.c
@@ -1157,6 +1157,7 @@ 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))
@@ -1186,6 +1187,13 @@ p_calculate_settings_for_current_FrameTween(
     }
   }
 
+  if(gap_debug)
+  {
+     printf("currWidth:%.4f\n"
+       ,(float)cur_ptr->currWidth
+       );
+  }
+
 
   /* calculate Rotation settings for the currently processed Frame (or tween) */
   if ((val_ptr->point[startOfSegmentIndex].accRotation != 0)
diff --git a/gap/gap_player_dialog.c b/gap/gap_player_dialog.c
index 3e255a7..cb05123 100644
--- a/gap/gap_player_dialog.c
+++ b/gap/gap_player_dialog.c
@@ -7585,7 +7585,7 @@ p_create_player_window (GapPlayerMainGlobalParams *gpp)
                       (GtkAttachOptions) (0), 0, 0);
 
     /* the framenr 1 button (does set Begin of range) */
-    framenr_1_button = gtk_button_new_from_stock (GAP_STOCK_SET_RANGE_START);
+    framenr_1_button = gap_stock_button_new (GAP_STOCK_SET_RANGE_START);
     gtk_widget_show (framenr_1_button);
     gtk_box_pack_start (GTK_BOX (fnr_hbox), framenr_1_button, FALSE, FALSE, 0);
     g_object_set_data (G_OBJECT (framenr_1_button), KEY_FRAMENR_BUTTON_TYPE, (gpointer)FRAMENR_BUTTON_BEGIN);
@@ -7612,7 +7612,7 @@ p_create_player_window (GapPlayerMainGlobalParams *gpp)
     }
 
     /* the framenr 2 button (does set End of range) */
-    framenr_2_button = gtk_button_new_from_stock (GAP_STOCK_SET_RANGE_END);
+    framenr_2_button = gap_stock_button_new (GAP_STOCK_SET_RANGE_END);
     gtk_widget_show (framenr_2_button);
     gtk_box_pack_start (GTK_BOX (fnr_hbox), framenr_2_button, TRUE, TRUE, 0);
     g_object_set_data (G_OBJECT (framenr_2_button), KEY_FRAMENR_BUTTON_TYPE, (gpointer)FRAMENR_BUTTON_END);
@@ -7661,7 +7661,7 @@ p_create_player_window (GapPlayerMainGlobalParams *gpp)
   row++;
 
   /* the from button */
-  from_button = gtk_button_new_from_stock (GAP_STOCK_RANGE_START);
+  from_button = gap_stock_button_new (GAP_STOCK_RANGE_START);
   gpp->from_button = from_button;
   /* the from button */
   gtk_widget_show (from_button);
@@ -7711,7 +7711,7 @@ p_create_player_window (GapPlayerMainGlobalParams *gpp)
   row++;
 
   /* the to button */
-  to_button = gtk_button_new_from_stock (GAP_STOCK_RANGE_END);
+  to_button = gap_stock_button_new (GAP_STOCK_RANGE_END);
   gpp->to_button = to_button;
   gtk_widget_show (to_button);
   gtk_table_attach (GTK_TABLE (table2), to_button, colbutton, colbutton+1, row, row+1
@@ -7765,7 +7765,7 @@ p_create_player_window (GapPlayerMainGlobalParams *gpp)
   row++;
 
   /* the origspeed_button */
-  origspeed_button = gtk_button_new_from_stock (GAP_STOCK_SPEED);
+  origspeed_button = gap_stock_button_new (GAP_STOCK_SPEED);
   gtk_widget_show (origspeed_button);
   gtk_table_attach (GTK_TABLE (table2), origspeed_button, colbutton, colbutton+1, row, row+1,
                     (GtkAttachOptions) (GTK_FILL),
@@ -8036,7 +8036,7 @@ p_create_player_window (GapPlayerMainGlobalParams *gpp)
 
 
   /* the PLAY button */
-  play_button = gtk_button_new_from_stock (GAP_STOCK_PLAY);
+  play_button = gap_stock_button_new_with_label (GAP_STOCK_PLAY, _("Play"));
   gtk_widget_show (play_button);
   gtk_box_pack_start (GTK_BOX (play_n_stop_hbox), play_button, FALSE, TRUE, 0);
   gimp_help_set_help_data (play_button, _("Start playback. "
@@ -8048,7 +8048,7 @@ p_create_player_window (GapPlayerMainGlobalParams *gpp)
                       gpp);
 
   /* the PAUSE button */
-  pause_button = gtk_button_new_from_stock (GAP_STOCK_PAUSE);
+  pause_button = gap_stock_button_new_with_label (GAP_STOCK_PAUSE, _("Pause"));
   gtk_widget_show (pause_button);
   gtk_widget_set_events(pause_button, GDK_BUTTON_PRESS_MASK);
   gtk_box_pack_start (GTK_BOX (play_n_stop_hbox), pause_button, FALSE, TRUE, 0);
@@ -8059,7 +8059,7 @@ p_create_player_window (GapPlayerMainGlobalParams *gpp)
                       gpp);
 
   /* the PLAY_REVERSE button */
-  back_button = gtk_button_new_from_stock (GAP_STOCK_PLAY_REVERSE);
+  back_button = gap_stock_button_new_with_label (GAP_STOCK_PLAY_REVERSE, _("Reverse"));
   gtk_widget_show (back_button);
   gtk_box_pack_start (GTK_BOX (play_n_stop_hbox), back_button, FALSE, TRUE, 0);
   gimp_help_set_help_data (back_button, _("Start reverse playback. "
@@ -8727,3 +8727,7 @@ gap_player_dlg_playback_dialog(GapPlayerMainGlobalParams *gpp)
 
 
 }  /* end gap_player_dlg_playback_dialog */
+
+
+
+
diff --git a/gap/gap_stock.c b/gap/gap_stock.c
index c8d9aa5..afb1395 100644
--- a/gap/gap_stock.c
+++ b/gap/gap_stock.c
@@ -35,6 +35,7 @@
 
 #include "images/gap-stock-pixbufs.h"
 
+extern int gap_debug;  /* 1 == print debug infos , 0 dont print debug infos */
 
 static GtkIconFactory *gap_icon_factory = NULL;
 
@@ -83,6 +84,16 @@ add_stock_icon (const gchar  *stock_id,
 
   pixbuf = gdk_pixbuf_new_from_inline (-1, inline_data, FALSE, NULL);
 
+  if(gap_debug)
+  {
+    printf("add_stock_icon stock_id:%s size:%d inline_data:%d pixbuf:%d\n"
+       ,stock_id
+       ,(int)size
+       ,(int)inline_data
+       ,(int)pixbuf
+       );
+  }
+
   gtk_icon_source_set_pixbuf (source, pixbuf);
   g_object_unref (pixbuf);
 
@@ -101,6 +112,11 @@ gap_stock_init (void)
 {
   static gboolean initialized = FALSE;
 
+  if(gap_debug)
+  {
+    printf("gap_stock_init START\n");
+  }
+
   if (initialized)
     return;
 
@@ -137,4 +153,57 @@ gap_stock_init (void)
   gtk_stock_add_static (gap_stock_items, G_N_ELEMENTS (gap_stock_items));
 
   initialized = TRUE;
+
+  if(gap_debug)
+  {
+    printf("gap_stock_init DONE\n");
+  }
+}
+
+
+
+GtkWidget *  
+gap_stock_button_new_with_label(const char *stock_id, const char *optional_label)
+{
+  GtkWidget *button;
+  GtkWidget *image;
+
+  button = gtk_button_new ();
+
+  image = gtk_image_new_from_stock (stock_id, GTK_ICON_SIZE_BUTTON);
+  gtk_widget_show (image);
+  
+
+  if (optional_label != NULL)
+  {
+    GtkWidget *hbox;
+    GtkWidget *label;
+
+    hbox = gtk_hbox_new (FALSE, 2);
+    gtk_widget_show (hbox);
+  
+    gtk_container_set_border_width (GTK_CONTAINER (hbox), 2);
+    gtk_box_pack_start (GTK_BOX (hbox), image, FALSE, FALSE, 0);
+    
+    label = gtk_label_new (optional_label);
+    gtk_widget_show (label);
+
+    gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 4);
+
+    gtk_container_add (GTK_CONTAINER (button), hbox);
+
+  }
+  else
+  {
+    gtk_container_add (GTK_CONTAINER (button), image);
+  }
+
+  return (button);
 }
+
+GtkWidget *  
+gap_stock_button_new(const char *stock_id)
+{
+  return(gap_stock_button_new_with_label(stock_id, NULL));
+}
+
diff --git a/gap/gap_stock.h b/gap/gap_stock.h
index 101a0af..2ce22f7 100644
--- a/gap/gap_stock.h
+++ b/gap/gap_stock.h
@@ -49,7 +49,11 @@
 #define GAP_STOCK_SET_RANGE_START    "gap-set-range-start"
 #define GAP_STOCK_SPEED              "gap-speed"
 
-void  gap_stock_init (void);
 
+#include <gtk/gtk.h>
+
+void  gap_stock_init (void);
+GtkWidget *  gap_stock_button_new(const char *stock_id);
+GtkWidget *  gap_stock_button_new_with_label(const char *stock_id, const char *optional_label);
 
 #endif /* __GAP_STOCK_H__ */
diff --git a/gap/gap_story_att_trans_dlg.c b/gap/gap_story_att_trans_dlg.c
index 3171627..e75f3d2 100644
--- a/gap/gap_story_att_trans_dlg.c
+++ b/gap/gap_story_att_trans_dlg.c
@@ -84,6 +84,7 @@
 
 extern int gap_debug;  /* 1 == print debug infos , 0 dont print debug infos */
 
+static gdouble  p_getConvertFactor(gint att_type_idx);
 static void     p_attw_prop_response(GtkWidget *widget
                   , gint       response_id
                   , GapStbAttrWidget *attw
@@ -232,6 +233,17 @@ static void     p_create_and_attach_att_arr_widgets(const char *row_title
                  , gint32     *att_arr_value_accel_ptr
                  );
 
+
+static gdouble
+p_getConvertFactor(gint att_type_idx)
+{
+  if (att_type_idx == GAP_STB_ATT_TYPE_ROTATE)
+  {
+    return 1.0;
+  }
+  return (CONVERT_TO_100PERCENT);
+}
+
 /* ---------------------------------
  * p_attw_prop_response
  * ---------------------------------
@@ -347,9 +359,11 @@ p_attw_prop_reset_all(GapStbAttrWidget *attw)
                                 , attw->stb_elem_refptr->att_arr_enable[ii]);
 
         gtk_adjustment_set_value(GTK_ADJUSTMENT(attw->att_rows[ii].spinbutton_from_adj)
-                                , attw->stb_elem_refptr->att_arr_value_from[ii] * CONVERT_TO_100PERCENT);
+                                , attw->stb_elem_refptr->att_arr_value_from[ii] *
+				p_getConvertFactor(ii));
         gtk_adjustment_set_value(GTK_ADJUSTMENT(attw->att_rows[ii].spinbutton_to_adj)
-                                , attw->stb_elem_refptr->att_arr_value_to[ii] * CONVERT_TO_100PERCENT);
+                                , attw->stb_elem_refptr->att_arr_value_to[ii] *
+				p_getConvertFactor(ii));
         gtk_adjustment_set_value(GTK_ADJUSTMENT(attw->att_rows[ii].spinbutton_dur_adj)
                                 , attw->stb_elem_refptr->att_arr_value_dur[ii]);
 
@@ -446,7 +460,7 @@ p_attw_timer_job(GapStbAttrWidget *attw)
  * p_attw_update_properties
  * ------------------------------------
  * render graphical view respecting all enabled attributes
- * opacity, move X/Y and Zoom X/Y
+ * rotate, opacity, move X/Y and Zoom X/Y
  */
 static void
 p_attw_update_properties(GapStbAttrWidget *attw)
@@ -540,6 +554,10 @@ p_get_default_attribute(GapStbAttrWidget *attw
       {
         return (0.5);  /* 1.0 scale to half size */
       }
+      if (att_type_idx == GAP_STB_ATT_TYPE_ROTATE)
+      {
+        return (180.0);
+      }
       return (0.5);    /* indicates 50% opacity */
     }
 
@@ -555,6 +573,10 @@ p_get_default_attribute(GapStbAttrWidget *attw
       {
         return (2.0);  /* 1.0 scale to doble size */
       }
+      if (att_type_idx == GAP_STB_ATT_TYPE_ROTATE)
+      {
+        return (360.0);
+      }
       return (0.75);  /* indicates 75% opacity */
     }
 
@@ -593,7 +615,7 @@ p_attw_start_button_clicked_callback(GtkWidget *widget
       gdouble attr_value;
 
       p_attw_push_undo_and_set_unsaved_changes(attw);
-      attr_value = CONVERT_TO_100PERCENT * p_get_default_attribute(attw
+      attr_value = p_getConvertFactor(att_type_idx) * p_get_default_attribute(attw
                         , bevent
                         , att_type_idx
                         , TRUE   /* use from value for reset */
@@ -624,7 +646,7 @@ p_attw_end_button_clicked_callback(GtkWidget *widget
       gdouble attr_value;
 
       p_attw_push_undo_and_set_unsaved_changes(attw);
-      attr_value = CONVERT_TO_100PERCENT * p_get_default_attribute(attw
+      attr_value = p_getConvertFactor(att_type_idx) * p_get_default_attribute(attw
                         , bevent
                         , att_type_idx
                         , FALSE   /* use to value for reset */
@@ -728,14 +750,15 @@ p_attw_gdouble_adjustment_callback(GtkObject *obj, gdouble *val)
 {
   GapStbAttrWidget *attw;
   gdouble l_val;
+  gint    att_type_idx;
 
-
+  att_type_idx = g_object_get_data( G_OBJECT(obj), "att_type_idx" );
   attw = g_object_get_data( G_OBJECT(obj), OBJ_DATA_KEY_ATTW );
   if(attw)
   {
     if(attw->stb_elem_refptr)
     {
-      l_val = (GTK_ADJUSTMENT(obj)->value) / CONVERT_TO_100PERCENT;
+      l_val = (GTK_ADJUSTMENT(obj)->value) / p_getConvertFactor(att_type_idx);
       if(gap_debug)
       {
         printf("gdouble_adjustment_callback: old_val:%f val:%f\n"
@@ -1031,6 +1054,7 @@ p_calculate_prefetch_render_attributes(GapStbAttrWidget *attw
     , attw->stb_elem_refptr->att_keep_proportions
     , attw->stb_elem_refptr->att_fit_width
     , attw->stb_elem_refptr->att_fit_height
+    , att_tab [img_idx][GAP_STB_ATT_TYPE_ROTATE]
     , att_tab [img_idx][GAP_STB_ATT_TYPE_OPACITY]
     , att_tab [img_idx][GAP_STB_ATT_TYPE_ZOOM_X]
     , att_tab [img_idx][GAP_STB_ATT_TYPE_ZOOM_Y]
@@ -1096,6 +1120,7 @@ p_calculate_render_attributes(GapStbAttrWidget *attw
     , attw->stb_elem_refptr->att_keep_proportions
     , attw->stb_elem_refptr->att_fit_width
     , attw->stb_elem_refptr->att_fit_height
+    , att_tab [img_idx][GAP_STB_ATT_TYPE_ROTATE]
     , att_tab [img_idx][GAP_STB_ATT_TYPE_OPACITY]
     , att_tab [img_idx][GAP_STB_ATT_TYPE_ZOOM_X]
     , att_tab [img_idx][GAP_STB_ATT_TYPE_ZOOM_Y]
@@ -1436,6 +1461,10 @@ p_create_transformed_layer(gint32 image_id
                         , calculated->x_offs
                         , calculated->y_offs
                         );
+
+  gap_story_transform_rotate_layer(image_id, *layer_id_ptr, calculated->rotate);
+
+
   gimp_layer_set_opacity(*layer_id_ptr
                         , calculated->opacity
                         );
@@ -2117,7 +2146,7 @@ p_fetch_video_frame_as_layer(GapStbMainGlobalParams *sgpp
 
     if(! gimp_drawable_has_alpha (l_new_layer_id))
     {
-      /* implicite add an alpha channel before we try to raise */
+      /* implicitly add an alpha channel before we try to raise */
       gimp_layer_add_alpha(l_new_layer_id);
     }
 
@@ -2410,9 +2439,10 @@ p_create_and_attach_att_arr_widgets(const char *row_title
   GtkObject *adj;
   GtkWidget *spinbutton;
   gint      col;
+  gdouble   convertFactor;
 
   col = column;
-
+  convertFactor = p_getConvertFactor(att_type_idx);
   /* enable label */
   label = gtk_label_new(row_title);
   gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
@@ -2458,7 +2488,7 @@ p_create_and_attach_att_arr_widgets(const char *row_title
   col++;
 
   /* From (Start value of transition) button */
-  adj = gtk_adjustment_new ( *att_arr_value_from_ptr * CONVERT_TO_100PERCENT
+  adj = gtk_adjustment_new ( *att_arr_value_from_ptr * convertFactor
                            , lower_constraint
                            , upper_constraint
                            , step_increment
@@ -2476,6 +2506,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), OBJ_DATA_KEY_ATTW, attw);
   g_signal_connect (G_OBJECT (adj), "value_changed",
                       G_CALLBACK (p_attw_gdouble_adjustment_callback),
@@ -2500,7 +2531,7 @@ p_create_and_attach_att_arr_widgets(const char *row_title
   col++;
 
   /* the To value spinbutton */
-  adj = gtk_adjustment_new ( *att_arr_value_to_ptr * CONVERT_TO_100PERCENT
+  adj = gtk_adjustment_new ( *att_arr_value_to_ptr * convertFactor
                            , lower_constraint
                            , upper_constraint
                            , step_increment
@@ -2518,6 +2549,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), OBJ_DATA_KEY_ATTW, attw);
   g_signal_connect (G_OBJECT (adj), "value_changed",
                       G_CALLBACK (p_attw_gdouble_adjustment_callback),
@@ -2814,6 +2846,33 @@ gap_story_attw_properties_dialog (GapStbAttrWidget *attw)
     gint att_type_idx;
     gint col = 0;
 
+
+    att_type_idx = GAP_STB_ATT_TYPE_ROTATE;
+    p_create_and_attach_att_arr_widgets(_("Rotate:")
+      , attw
+      , table
+      , row
+      , col
+      , att_type_idx
+      , -36000.0    /* lower constraint for the from/to values */
+      ,  36000.0    /* upper constraint for the from/to values */
+      , 1.0        /* step increment   for the from/to values  */
+      , 45.0       /* page increment   for the from/to values */
+      , 0.0        /* page size        for the from/to values */
+      , 1          /* digits for the from/to values */
+      , _("ON: Enable rotation settings")
+      , &attw->stb_elem_refptr->att_arr_enable[att_type_idx]
+      , _("rotation value in degree for the first handled frame ")
+      , &attw->stb_elem_refptr->att_arr_value_from[att_type_idx]
+      , _("rotation value in degree for the last handled frame ")
+      , &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 for rotation (1 for constant speed, positive: acceleration, negative: deceleration)")
+      , &attw->stb_elem_refptr->att_arr_value_accel[att_type_idx]
+      );
+
+    row++;
     att_type_idx = GAP_STB_ATT_TYPE_OPACITY;
     p_create_and_attach_att_arr_widgets(_("Opacity:")
       , attw
@@ -2841,6 +2900,7 @@ 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_MOVE_X;
     p_create_and_attach_att_arr_widgets(_("Move X:")
@@ -2858,10 +2918,10 @@ gap_story_attw_properties_dialog (GapStbAttrWidget *attw)
       , _("ON: Enable move horizontal settings")
       , &attw->stb_elem_refptr->att_arr_enable[att_type_idx]
       , _("move horizontal value for the first handled frame "
-          " where 0.0 is centered, 100.0 is outside right, -100.0 is outside left)")
+          "where 0.0 is centered, 100.0 is outside right, -100.0 is outside left")
       , &attw->stb_elem_refptr->att_arr_value_from[att_type_idx]
       , _("move horizontal value for the last handled frame "
-          " where 0.0 is centered, 100.0 is outside right, -100.0 is outside left)")
+          "where 0.0 is centered, 100.0 is outside right, -100.0 is outside left")
       , &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]
@@ -2887,10 +2947,10 @@ gap_story_attw_properties_dialog (GapStbAttrWidget *attw)
       , _("ON: Enable move vertical settings")
       , &attw->stb_elem_refptr->att_arr_enable[att_type_idx]
       , _("move vertical value for the first handled frame "
-          " where 0.0 is centered, 100.0 is outside at bottom, -100.0 is outside at top)")
+          "where 0.0 is centered, 100.0 is outside at bottom, -100.0 is outside at top")
       , &attw->stb_elem_refptr->att_arr_value_from[att_type_idx]
       , _("move vertical value for the last handled frame "
-          " where 0.0 is centered, 100.0 is outside at bottom, -100.0 is outside at top)")
+          "where 0.0 is centered, 100.0 is outside at bottom, -100.0 is outside at top")
       , &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]
diff --git a/gap/gap_story_dialog.c b/gap/gap_story_dialog.c
index a9b9bd3..6ca7576 100644
--- a/gap/gap_story_dialog.c
+++ b/gap/gap_story_dialog.c
@@ -23,7 +23,7 @@
  * version 2.2.1;   2006/02/07  hof: support drag&drop  destination for image/videofilenames
  * version 2.1.0a;  2005/01/22  hof: copy/cut/paste handling via keys (ctrl-c, ctrl-x, ctrl-v)
  * version 2.1.0a;  2004/12/05  hof: added global layout properties dialog
- * version 1.3.27a; 2004/03/15  hof: videothumbnails are kept in memory
+ * version 1.3.27a; 2004/03/15  hof: video thumbnails are kept in memory
  *                                   for the startframes in all MOVIE clips
  *                                   (common list for both storyboard and cliplist)
  *                                   Need define GAP_ENABLE_VIDEOAPI_SUPPORT
@@ -523,17 +523,17 @@ p_get_begin_and_end_for_single_clip_playback(gint32 *begin_frame, gint32 *end_fr
 {
   *begin_frame = stb_elem->from_frame;
   *end_frame = stb_elem->to_frame;
- 
+
   if(stb_elem->record_type == GAP_STBREC_VID_IMAGE)
   {
     char *l_basename;
     long l_number;
-    
+
     l_basename = gap_lib_alloc_basename(stb_elem->orig_filename, &l_number);
     g_free(l_basename);
     *begin_frame = l_number;
     *end_frame = l_number;
-    
+
   }
 }    /* end p_get_begin_and_end_for_single_clip_playback */
 
@@ -1601,7 +1601,7 @@ gap_story_fw_abstract_properties_dialog (GapStbFrameWidget *fw)
  *   in case a valid (positive) vtrack number is specified.
  *   (this allows quick direct frame fetch operations without
  *    the use of the storyboard render processor engine,
- *    VID_PLAY_SECTION elemnts are rendered as black frames in this
+ *    VID_PLAY_SECTION elements are rendered as black frames in this
  *    playback mode)
  *
  *   a negative vtrack number (-1) triggers composite video playback
@@ -1679,7 +1679,7 @@ p_story_call_player(GapStbMainGlobalParams *sgpp
     /* make a copy of the storyboard list
      * for the internal use in the player
      * (note that section clip playback is only supported
-     *  if the main section is active and composite video playback is selectd)
+     *  if the main section is active and composite video playback is selected)
      */
     if(play_all)
     {
@@ -1700,7 +1700,7 @@ p_story_call_player(GapStbMainGlobalParams *sgpp
         if(stb->active_section == gap_story_find_main_section(stb))
         {
            /* for composite video playback of "only selected elements" in
-            * the main section, we have to copy implictely
+            * the main section, we have to copy implicitly
             * all referenced sub sections.
             * (but we simply add all available subsection)
             */
@@ -1948,7 +1948,7 @@ p_call_master_encoder(GapStbMainGlobalParams *sgpp
   {
     gtk_main_iteration ();
   }
-   
+
   dummy_layer_id = gap_image_get_any_layer(sgpp->image_id);
   /* generic call of GAP master video encoder plugin */
   l_params = gimp_run_procedure (GAP_PLUG_IN_MASTER_ENCODER,
@@ -2493,7 +2493,7 @@ p_cancel_button_cb (GtkWidget *w,
     }
     else
     {
-      printf("VIDEO-CANCEL occured implicite\n");
+      printf("VIDEO-CANCEL occurred implicitly\n");
     }
   }
 
@@ -2512,7 +2512,7 @@ p_cancel_button_cb (GtkWidget *w,
     sgpp->vthumb_prefetch_in_progress = GAP_VTHUMB_PREFETCH_NOT_ACTIVE;
     if(sgpp->menu_item_win_vthumbs != NULL)
     {
-      /* implicite switch off auto_vthumb  (by setting the relevant menu item FALSE)
+      /* implicitly switch off auto_vthumb  (by setting the relevant menu item FALSE)
        */
       gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(sgpp->menu_item_win_vthumbs), FALSE);
     }
@@ -3517,7 +3517,7 @@ p_single_clip_playback(GapStbFrameWidget *fw)
         {
           gint32 stb_in_track;
 
-          /* select the secetion clip
+          /* select the section clip
            * (because section playback is only possible in composite
            * mode (using the storyboard render processor)
            * an composite playback operates on mapping for selected clips)
@@ -3548,17 +3548,17 @@ p_single_clip_playback(GapStbFrameWidget *fw)
         {
           gint32 l_begin_frame;
           gint32 l_end_frame;
-          
+
           p_get_begin_and_end_for_single_clip_playback(&l_begin_frame, &l_end_frame, fw->stb_elem_refptr);
           imagename = gap_story_get_filename_from_elem(fw->stb_elem_refptr);
- 
+
           if(gap_debug)
           {
             printf("CALLING Player from single clip imagename:%s\n  from:%d (%d) to:%d (%d) type:%d\n  orig_filename:%s\n\n"
                 , imagename
                 ,(int)fw->stb_elem_refptr->from_frame
                 ,(int)l_begin_frame
-                ,(int)fw->stb_elem_refptr->to_frame 
+                ,(int)fw->stb_elem_refptr->to_frame
                 ,(int)l_end_frame
                 ,(int)fw->stb_elem_refptr->record_type
                 , fw->stb_elem_refptr->orig_filename
@@ -3755,7 +3755,7 @@ gap_story_dlg_pw_update_mask_references(GapStbTabWidgets *tabw)
  * ---------------------------------
  * set specified section as new active section,
  * refresh the section combo box.
- * (this causes an implicite refresh in case
+ * (this causes an implicit refresh in case
  * where the active_section has changed)
  */
 void
@@ -5933,7 +5933,7 @@ p_make_menu_global(GapStbMainGlobalParams *sgpp, GtkWidget *menu_bar)
                           );
 
    sgpp->menu_item_win_vthumbs =
-   p_make_check_item_with_label(file_menu, _("Videothumbnails")
+   p_make_check_item_with_label(file_menu, _("Video thumbnails")
                           , p_menu_win_vthumbs_toggle_cb
                           , sgpp
                           , sgpp->auto_vthumb
@@ -6627,12 +6627,12 @@ p_prefetch_vthumbs (GapStbMainGlobalParams *sgpp, GapStoryBoard *stb)
           if(sgpp->progress_bar_master)
           {
             gtk_progress_bar_set_text(GTK_PROGRESS_BAR(sgpp->progress_bar_master),
-                                     _("videothumbnail cancelled"));
+                                     _("video thumbnail cancelled"));
             gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(sgpp->progress_bar_master), 0);
           }
           if(gap_debug)
           {
-            printf("p_prefetch_vthumbs : (1) videothumbnail cancelled: cancel_video_api: %d auto_vthumb_refresh_canceled:%d auto_vthumb:%d\n"
+            printf("p_prefetch_vthumbs : (1) video thumbnail cancelled: cancel_video_api: %d auto_vthumb_refresh_canceled:%d auto_vthumb:%d\n"
                ,(int)sgpp->cancel_video_api
                ,(int)sgpp->auto_vthumb_refresh_canceled
                ,(int)sgpp->auto_vthumb
@@ -6653,7 +6653,7 @@ p_prefetch_vthumbs (GapStbMainGlobalParams *sgpp, GapStoryBoard *stb)
         if(sgpp->progress_bar_master)
         {
 
-           msg = g_strdup_printf(_("Fetching videothumbnail for clip: %d (out of %d)")
+           msg = g_strdup_printf(_("Fetching video thumbnail for clip: %d (out of %d)")
                                 ,(int)l_count
                                 ,(int)l_total
                                 );
@@ -6778,7 +6778,7 @@ p_optimized_prefetch_vthumbs (GapStbMainGlobalParams *sgpp)
            *    note that prefetch will be very quick for all clips where vthumb is already present
            *    from the cancelled previous prefetch cycle)
            *    (currently this attempt leads to crashes that i could not locate yet)
-           * - the other (currently implemented) option is to cancel prefetch and implicite turn off auto_vthumb mode
+           * - the other (currently implemented) option is to cancel prefetch and implicitly turn off auto_vthumb mode
            */
           option_restart = TRUE;
 
@@ -6799,7 +6799,7 @@ p_optimized_prefetch_vthumbs (GapStbMainGlobalParams *sgpp)
 //               if(sgpp->progress_bar_master)
 //               {
 //                 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(sgpp->progress_bar_master),
-//                                          _("videothumbnail cancelled"));
+//                                          _("video thumbnail cancelled"));
 //                 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(sgpp->progress_bar_master), 0);
 //               }
 //               p_cancel_button_cb(NULL, sgpp);
@@ -7450,7 +7450,7 @@ p_activate_section_by_combo_index(gint target_index
        {
          /* the callback p_vtrack_spinbutton_cb is normally triggered automatically
           * if the current vtrack number is set to the another number.
-          * this explicite call is for the case where the track number
+          * this explicit call is for the case where the track number
           * did not change. (but same track number in another section requires
           * always a refresh, that is triggered here)
           */
@@ -7677,7 +7677,7 @@ p_create_button_bar(GapStbTabWidgets *tabw
                   G_CALLBACK (p_tabw_section_properties_cut_cb),
                   tabw);
   gimp_help_set_help_data (button,
-                           _("Show Section properites window"),
+                           _("Show Section properties window"),
                            NULL);
   gtk_widget_show (button);
 
@@ -8428,7 +8428,7 @@ gap_storyboard_dialog(GapStbMainGlobalParams *sgpp)
     gtk_box_pack_start (GTK_BOX (hbox_stb_frame), button, FALSE, FALSE, 0);
     gtk_widget_show (button);
     gimp_help_set_help_data (button
-                   , _("Cancel video access if in progress and disable automatic videothumbnails")
+                   , _("Cancel video access if in progress and disable automatic video thumbnails")
                    , NULL);
     g_signal_connect (G_OBJECT (button), "clicked",
                     G_CALLBACK (p_cancel_button_cb),
@@ -8680,7 +8680,7 @@ p_parse_aspect_width_and_height(const char *buff, gint32 *aspect_width, gint32 *
 static void
 p_tabw_master_prop_dialog(GapStbTabWidgets *tabw, gboolean new_flag)
 {
-  GapArrArg  argv[12];
+  GapArrArg  argv[13];
   static char *radio_args[4]  = { N_("automatic"), "libmpeg3", "libavformat", "quicktime4linux" };
   static char *radio_aspect_args[4]  = { N_("none"), "4:3", "16:9", "3:2"};
   gint   l_ii;
@@ -8701,6 +8701,7 @@ p_tabw_master_prop_dialog(GapStbTabWidgets *tabw, gboolean new_flag)
   gchar    buf_preferred_decoder[60];
   gchar    buf_aspect_string[40];
   gchar   *label_txt;
+  gchar    l_master_insert_alpha_format[GAP_STORY_MAX_STORYFILENAME_LEN];
   gchar    l_master_insert_area_format[GAP_STORY_MAX_STORYFILENAME_LEN];
 
   gboolean l_rc;
@@ -8929,6 +8930,30 @@ p_tabw_master_prop_dialog(GapStbTabWidgets *tabw, gboolean new_flag)
   argv[l_ii].has_default = TRUE;
   argv[l_ii].flt_default = argv[l_ii].flt_ret;
 
+
+
+
+  l_ii++;
+  l_master_insert_alpha_format[0] = '\0';
+  if (stb_dst->master_insert_alpha_format)
+  {
+    g_snprintf(&l_master_insert_alpha_format[0], sizeof(l_master_insert_alpha_format), "%s"
+            , stb_dst->master_insert_alpha_format
+            );
+  }
+  gap_arr_arg_init(&argv[l_ii], GAP_ARR_WGT_FILESEL);
+  argv[l_ii].label_txt = _("Ext. Transparency Format:");
+  argv[l_ii].entry_width = 250;       /* pixel */
+  argv[l_ii].help_txt  = _("Format string to provide external tranparency in movie clips. "
+                           "(e.g automatic alpha channel insertation)"
+                           "this string shall contain \%s as placeholder for the basename of a videoclip and "
+                           "optional \%06d as placeholder for the framenumber.");
+  argv[l_ii].text_buf_len = sizeof(l_master_insert_alpha_format);
+  argv[l_ii].text_buf_ret = &l_master_insert_alpha_format[0];
+
+
+
+
   l_ii++;
   l_master_insert_area_format[0] = '\0';
   if (stb_dst->master_insert_area_format)
@@ -8947,7 +8972,6 @@ p_tabw_master_prop_dialog(GapStbTabWidgets *tabw, gboolean new_flag)
   argv[l_ii].text_buf_ret = &l_master_insert_area_format[0];
 
 
-
   l_ii++;
   gap_arr_arg_init(&argv[l_ii], GAP_ARR_WGT_DEFAULT_BUTTON);
   argv[l_ii].label_txt =  _("Reset");                /* should use GIMP_STOCK_RESET if possible */
@@ -9038,6 +9062,34 @@ p_tabw_master_prop_dialog(GapStbTabWidgets *tabw, gboolean new_flag)
      }
 
 
+     if (l_master_insert_alpha_format[0] != '\0')
+     {
+       if (stb_dst->master_insert_alpha_format)
+       {
+         if (strcmp(stb_dst->master_insert_alpha_format, &l_master_insert_alpha_format[0]) != 0)
+         {
+           stb_dst->unsaved_changes = TRUE;
+         }
+         g_free(stb_dst->master_insert_alpha_format);
+       }
+       else
+       {
+           stb_dst->unsaved_changes = TRUE;
+       }
+       stb_dst->master_insert_alpha_format = g_strdup(&l_master_insert_alpha_format[0]);
+     }
+     else
+     {
+       if (stb_dst->master_insert_alpha_format)
+       {
+         stb_dst->unsaved_changes = TRUE;
+         g_free(stb_dst->master_insert_alpha_format);
+         stb_dst->master_insert_alpha_format = NULL;
+       }
+     }
+
+
+
      if (l_master_insert_area_format[0] != '\0')
      {
        if (stb_dst->master_insert_area_format)
diff --git a/gap/gap_story_file.c b/gap/gap_story_file.c
index 4bf1869..de8b7f4 100644
--- a/gap/gap_story_file.c
+++ b/gap/gap_story_file.c
@@ -26,7 +26,7 @@
 /* revision history:
  * version 2.3.0;   2006/06/14  hof: added storyboard support for layer_masks,
  *                                   overlapping frames (are converted to shadow tracks at processing)
- *                                   and image filpping
+ *                                   and image flipping
  * version 1.3.25b; 2004/01/24  hof: created
  */
 
@@ -80,6 +80,7 @@ static const char *gtab_att_transition_key_words[GAP_STB_ATT_TYPES_ARRAY_MAX] =
        , GAP_STBKEY_VID_MOVE_Y
        , GAP_STBKEY_VID_ZOOM_X
        , GAP_STBKEY_VID_ZOOM_Y
+       , GAP_STBKEY_VID_ROTATE
        };
 
 
@@ -234,6 +235,15 @@ gap_story_debug_print_elem(GapStoryElem *stb_elem)
     {
       printf("  filtermacro_file: (NULL)\n");
     }
+
+    if(stb_elem->colormask_file)
+    {
+      printf("  colormask_file :%s:\n", stb_elem->colormask_file);
+    }
+    else
+    {
+      printf("  colormask_file: (NULL)\n");
+    }
     printf("  fmac_total_steps: %d\n", (int)stb_elem->fmac_total_steps);
     printf("  fmac_accel:       %d\n", (int)stb_elem->fmac_accel);
 
@@ -315,6 +325,10 @@ gap_story_debug_print_list(GapStoryBoard *stb)
             , (int)stb->master_aspect_height
             );
   printf("master_1is_toplayer       : %d\n", (int)stb->master_vtrack1_is_toplayer );
+  if (stb->master_insert_alpha_format)
+  {
+    printf("master_insert_alpha_format :%s", stb->master_insert_alpha_format);
+  }
   if (stb->master_insert_area_format)
   {
     printf("master_insert_area_format :%s", stb->master_insert_area_format);
@@ -327,7 +341,7 @@ gap_story_debug_print_list(GapStoryBoard *stb)
   printf("stb_unique_id             : %d\n", (int)stb->stb_unique_id );
 
   GapStorySection *active_section;
-  
+
   for(active_section = stb->stb_section; active_section != NULL; active_section=active_section->next)
   {
     if(active_section->section_name == NULL)
@@ -624,6 +638,7 @@ gap_story_new_story_board(const char *filename)
     stb->master_aspect_ratio = 0.0;  /* 0.0 for none */
     stb->master_aspect_width = 0;
     stb->master_aspect_height = 0;
+    stb->master_insert_alpha_format = NULL;
     stb->master_insert_area_format = NULL;
 
     stb->layout_cols = -1;
@@ -654,11 +669,11 @@ gap_story_new_story_board(const char *filename)
 
     stb->mask_section = gap_story_create_or_find_section_by_name(stb, GAP_STB_MASK_SECTION_NAME);
     stb->active_section = gap_story_create_or_find_section_by_name(stb, NULL);
-    
+
     stb->edit_settings = g_new(GapStoryEditSettings, 1);
     if (stb->edit_settings)
     {
-       /* init default edit settings 
+       /* init default edit settings
         * (show main section track 1 first page)
         */
        stb->edit_settings->section_name = NULL;
@@ -702,6 +717,7 @@ gap_story_new_elem(GapStoryRecordType record_type)
     stb_elem->exact_seek = 0;
     stb_elem->delace = 0.0;
     stb_elem->filtermacro_file = NULL;
+    stb_elem->colormask_file = NULL;
     stb_elem->fmac_total_steps = 1;
     stb_elem->fmac_accel = 0;              /* default: use no acceleration */
     stb_elem->preferred_decoder = NULL;
@@ -797,7 +813,7 @@ GapStorySection *
 gap_story_new_section()
 {
    GapStorySection *new_section;
-   
+
    new_section = g_new(GapStorySection, 1);
    new_section->stb_elem = NULL;
    new_section->section_name = NULL;
@@ -883,12 +899,12 @@ GapStorySection *
 gap_story_find_section_by_story_id(GapStoryBoard *stb, gint32 story_id)
 {
   GapStorySection *section;
-  
+
   if (stb == NULL)
   {
     return (NULL);
   }
-  
+
 
   /* 1st attempt searches only in the active section */
   if (gap_story_elem_find_in_section_by_story_id(stb->active_section, story_id) != NULL)
@@ -896,7 +912,7 @@ gap_story_find_section_by_story_id(GapStoryBoard *stb, gint32 story_id)
     return (stb->active_section);
   }
 
-  /* 2nd attempt searches in all sections */ 
+  /* 2nd attempt searches in all sections */
   for(section = stb->stb_section; section != NULL; section = section->next)
   {
     if (gap_story_elem_find_in_section_by_story_id(section, story_id) != NULL)
@@ -904,9 +920,9 @@ gap_story_find_section_by_story_id(GapStoryBoard *stb, gint32 story_id)
       return(section);
     }
   }
-  
+
   return (NULL);
-  
+
 }  /* end gap_story_find_section_by_story_id */
 
 
@@ -925,7 +941,7 @@ gap_story_find_section_by_stb_elem(GapStoryBoard *stb, GapStoryElem      *stb_el
   {
     return (NULL);
   }
-  
+
   return (gap_story_find_section_by_story_id(stb, stb_elem->story_id));
 }
 
@@ -939,7 +955,7 @@ GapStorySection *
 gap_story_find_section_by_name(GapStoryBoard *stb, const char *section_name)
 {
   GapStorySection *section;
-  
+
   for(section = stb->stb_section; section != NULL; section = section->next)
   {
     if(p_null_strcmp(section->section_name, section_name))
@@ -975,20 +991,20 @@ gap_story_create_or_find_section_by_name(GapStoryBoard *stb, const char *section
 {
   GapStorySection *section;
   GapStorySection *new_section;
-  
+
   section = gap_story_find_section_by_name(stb, section_name);
   if (section != NULL)
   {
     return(section);
   }
-    
-    
+
+
   new_section = gap_story_new_section();
   if (section_name != NULL)
   {
     new_section->section_name = g_strdup(section_name);
   }
-    
+
   for(section = stb->stb_section; section != NULL; section = section->next)
   {
     if(section->next == NULL)
@@ -1074,15 +1090,15 @@ gap_story_remove_section(GapStoryBoard *stb, GapStorySection *del_section)
       }
     }
   }
-  
-  
+
+
   prev_section = NULL;
   for(section = stb->stb_section; section != NULL; section = section->next)
   {
     if(del_section == section)
     {
       stb->unsaved_changes = TRUE;
-      
+
       if(prev_section == NULL)
       {
         /* remove the 1st list element */
@@ -1093,15 +1109,15 @@ gap_story_remove_section(GapStoryBoard *stb, GapStorySection *del_section)
         /* remove list element that has a rprevious element */
         prev_section->next = section->next;
       }
-      
+
       p_free_section(section);
-      
+
       return(TRUE);
     }
     prev_section = section;
   }
   return (FALSE);
-  
+
 }  /* end gap_story_remove_section */
 
 /* ------------------------------------------
@@ -1111,7 +1127,7 @@ gap_story_remove_section(GapStoryBoard *stb, GapStorySection *del_section)
 gchar * gap_story_generate_new_unique_section_name(GapStoryBoard *stb)
 {
   gchar *new_name;
-  
+
   while (TRUE)
   {
     new_name = g_strdup_printf(_("section_%02d"), global_section_name_number);
@@ -1139,7 +1155,7 @@ gap_story_filename_is_videofile_by_ext(const char *filename)
   char  *l_ext;
   char  *l_extension;
   gboolean l_isKnownMovie;
-  
+
   l_isKnownMovie = FALSE;
   l_extension = gap_lib_alloc_extension(filename);
   if(l_extension)
@@ -1289,6 +1305,7 @@ p_free_stb_elem(GapStoryElem *stb_elem)
   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);}
 }  /* end p_free_stb_elem */
@@ -1370,7 +1387,7 @@ gap_story_free_stb_section(GapStorySection *stb_section)
       p_free_stb_elem_list(stb_section->stb_elem);
     }
     stb_section->stb_elem = NULL;
-    
+
     if (stb_section->section_name)
     {
       g_free(stb_section->section_name);
@@ -1394,7 +1411,7 @@ gap_story_free_selection_mapping(GapStoryFrameNumberMap *mapping)
     if(mapping->map_list)
     {
       GapStoryFrameNumberMappingElem *map_elem;
-      
+
       map_elem = mapping->map_list;
       while(map_elem != NULL)
       {
@@ -1408,7 +1425,7 @@ gap_story_free_selection_mapping(GapStoryFrameNumberMap *mapping)
       mapping->total_frames_selected = 0;
     }
   }
-  
+
 }  /* end gap_story_free_selection_mapping */
 
 /* ----------------------------------------------------
@@ -1437,7 +1454,7 @@ gap_story_free_storyboard(GapStoryBoard **stb_ptr)
       }
       stb->stb_section = NULL;
     }
-    
+
     /* free edit_settings */
     if(stb->edit_settings)
     {
@@ -1448,7 +1465,7 @@ gap_story_free_storyboard(GapStoryBoard **stb_ptr)
        g_free(stb->edit_settings);
        stb->edit_settings = NULL;
     }
-    
+
     /* free selction mapping */
     if(stb->mapping)
     {
@@ -1481,6 +1498,10 @@ gap_story_free_storyboard(GapStoryBoard **stb_ptr)
     {
       g_free(stb->warnline);
     }
+    if(stb->master_insert_alpha_format)
+    {
+      g_free(stb->master_insert_alpha_format);
+    }
     if(stb->master_insert_area_format)
     {
       g_free(stb->master_insert_area_format);
@@ -1549,7 +1570,7 @@ gap_story_list_append_elem_at_section(GapStoryBoard *stb, GapStoryElem *stb_elem
     gap_story_debug_print_elem(stb_elem);
     printf("\n\n");
   }
-  
+
   if (active_section == NULL)
   {
     printf("gap_story_list_append_elem: ERROR active_section is NULL\n");
@@ -1698,14 +1719,14 @@ gap_story_set_fit_size_attributes(GapStoryBoard *stb
  * this procedure is typically used when parsing a storyboard file transition element.
  * in the file there may be several different transition records
  * in continous sequence that are collected in only one transition element in memory
- * (the presence of a video clip record breaks continous sequence). 
+ * (the presence of a video clip record breaks continous sequence).
  * This procedure shall provide the one transition element that collects the
  * transition records read from file. Therefore the already existing list in the active section
  * is checked at the last NON comment element. If there is such an elem that is a transition,
  * then set *elem_already_in_the_list TRUE and return this element. Otherwise create a new
  * transition element.
  * if the list already contains previous, but NON continous Fit size attributes for the same track,
- * those attributes will be used to overwrite the defaults of a newly created transition attribute element. 
+ * those attributes will be used to overwrite the defaults of a newly created transition attribute element.
  * Note: at parsing, the active_section is managed by the line parser and set always
  * to the section where the currently parsed record shall be added.
  */
@@ -1910,7 +1931,7 @@ GapStoryElem *
 gap_story_find_maskdef_equal_to_ref_elem(GapStoryBoard *stb_ptr, GapStoryElem *stb_ref_elem)
 {
   GapStoryElem *stb_elem;
-  
+
   if(stb_ptr->mask_section == NULL)
   {
     return (NULL);
@@ -1949,7 +1970,7 @@ gap_story_find_maskdef_equal_to_ref_elem(GapStoryBoard *stb_ptr, GapStoryElem *s
  * where the elements of hidden mask definition tracks
  * are NOT included. (those elements are already processed in this procedure
  * and added to the destination storyboard if necessary.
- * -- for implicite copy of mask definitions --
+ * -- for implicit copy of mask definitions --
  * this includes the required updates in case of mask_name uniqueness conflicts)
  *
  * the elements in the returned storyboard all have set the
@@ -1984,8 +2005,8 @@ p_process_maskdefs_and_split_off_vtrack_elems(GapStoryBoard *stb_dst
   {
     /* we are pasting into a normal video track of a non mask section
      * check if there are maskdefinitions in the src storyboard that have to
-     * be pasted implicite (into mask definition track at end of destination storyboard)
-     * Note: implicite mask definitions are already present in the stb_src->mask_section
+     * be pasted implicitly (into mask definition track at end of destination storyboard)
+     * Note: implicit mask definitions are already present in the stb_src->mask_section
      * (the cut/copy processing has put them there, using a hidden track number
      * GAP_STB_HIDDEN_MASK_TRACK_NUMBER)
      */
@@ -2007,7 +2028,7 @@ p_process_maskdefs_and_split_off_vtrack_elems(GapStoryBoard *stb_dst
           if(p_story_is_maskdef_equal(stb_elem, stb_maskdef_elem))
           {
             /* the destination storyboard already includes this
-             * maskdefinition (same mask_name and mask same clip), therefore no implicite copy
+             * maskdefinition (same mask_name and mask same clip), therefore no implicit copy
              * is necessary
              */
             maskdef_copy_required = FALSE;
@@ -2024,7 +2045,7 @@ p_process_maskdefs_and_split_off_vtrack_elems(GapStoryBoard *stb_dst
          *
          *  TODO: check if destination already includes same mask_definition clip
          *  using another mask_name.
-         *   in such a case copy is not required, but have to 
+         *   in such a case copy is not required, but have to
          *   gap_story_update_mask_name_references(stb_src, another_mask_name, stb_elem->mask_name);
          */
 
@@ -2057,9 +2078,9 @@ p_process_maskdefs_and_split_off_vtrack_elems(GapStoryBoard *stb_dst
    * In case of explicite pasting elements into the mask section.
    * the elements require a new unique mask_name that is generated.
    * (existing mask_name references must be removed)
-   * 
+   *
    */
-   
+
   for(stb_elem = stb_src->active_section->stb_elem; stb_elem != NULL; stb_elem = stb_elem->next)
   {
     if(stb_elem->record_type == GAP_STBREC_ATT_TRANSITION)
@@ -2263,7 +2284,7 @@ p_list_insert_at_story_id(GapStorySection *dst_section
  *
  * Paste into non-mask section: main or sub_section:
  *   in this case stb_dst->active_section != stb_dst->mask_section
- *   all clips in non mask sections of stb_src (this is the  are copied into 
+ *   all clips in non mask sections of stb_src (this is the  are copied into
  */
 void
 gap_story_lists_merge(GapStoryBoard *stb_dst
@@ -2296,7 +2317,7 @@ gap_story_lists_merge(GapStoryBoard *stb_dst
                                    );
     stb_vtrack_main_section = gap_story_find_main_section(stb_vtrack_src);
     stb_dst_main_section = gap_story_find_main_section(stb_dst);
-    
+
     if (stb_dst->active_section != stb_dst_main_section)
     {
       /* other sections than main section do NOT support VID_PLAY_SECTION elements
@@ -2310,9 +2331,9 @@ gap_story_lists_merge(GapStoryBoard *stb_dst
       {
         /* convert VID_PLAY_SECTION to VID_PLAY_BLACKSECTION
          * because we copy from another storyboard into a main section.
-         * note: this is done because the more complex implicite copying
-         *  of all refered sections and references updates is NOT yet supported.
-         *  a future implemtation must take care to copy only those sections
+         * note: this is done because the more complex implicit copying
+         *  of all referred sections and references updates is NOT yet supported.
+         *  a future implementation must take care to copy only those sections
          *  that are not already present in the destination storyboard stb_dst.
          *  where checking must test if an adequate section is available
          *  where all list elements match.
@@ -2320,7 +2341,7 @@ gap_story_lists_merge(GapStoryBoard *stb_dst
          p_section_to_blacksection(stb_vtrack_main_section);
       }
     }
-        
+
     /* insert regular clips (non-mask clips) to the active section in the
      * destination storyboard. (e.g. paste explicte selected clips)
      */
@@ -2541,7 +2562,7 @@ p_fetch_string(char **scan_ptr
       {
         break;
       }
-      /* continue scann after doublequotes (until EOL or whitespace) */
+      /* continue scan after doublequotes (until EOL or whitespace) */
       l_termchar = ' ';
     }
     if(l_termchar != '"')
@@ -2764,9 +2785,9 @@ p_scan_gboolean(char *ptr, gboolean default_value,
 /* -----------------------------
  * p_assign_parsed_flip_value
  * -----------------------------
- * parse typical (optional) video parameters via predfined pointers.
+ * parse typical (optional) video parameters via predefined pointers.
  * if the relevant pointer points to a not null string
- * then scann the parameter and assign it the specified stb_elem
+ * then scan the parameter and assign it the specified stb_elem
  */
 static void
 p_assign_parsed_flip_value(GapStoryElem *stb_elem
@@ -2805,9 +2826,9 @@ p_assign_parsed_flip_value(GapStoryElem *stb_elem
 /* -----------------------------
  * p_assign_parsed_video_values
  * -----------------------------
- * parse typical (optional) video parameters via predfined pointers.
+ * parse typical (optional) video parameters via predefined pointers.
  * if the relevant pointer points to a not null string
- * then scann the parameter and assign it the specified stb_elem
+ * then scan the parameter and assign it the specified stb_elem
  */
 static void
 p_assign_parsed_video_values(GapStoryElem *stb_elem
@@ -2823,6 +2844,7 @@ p_assign_parsed_video_values(GapStoryElem *stb_elem
   , char *mask_disable_ptr
   , char *fmac_total_steps_ptr
   , char *fmac_accel_ptr
+  , char *colormask_file_ptr
   )
 {
   if(nloops_ptr)
@@ -2862,6 +2884,17 @@ p_assign_parsed_video_values(GapStoryElem *stb_elem
     }
   }
 
+
+  if(colormask_file_ptr)
+  {
+    if(*colormask_file_ptr)
+    {
+      stb_elem->colormask_file = g_strdup(colormask_file_ptr);
+      p_flip_dir_separators(stb_elem->colormask_file);
+    }
+  }
+
+
   p_assign_parsed_flip_value(stb_elem, flip_ptr);
 
   if(mask_name_ptr)
@@ -2886,6 +2919,10 @@ p_assign_parsed_video_values(GapStoryElem *stb_elem
         case 'M':
           stb_elem->mask_anchor = GAP_MSK_ANCHOR_MASTER;
           break;
+        case 'x':
+        case 'X':
+          stb_elem->mask_anchor = GAP_MSK_ANCHOR_XCOLOR;
+          break;
       }
     }
   }
@@ -2984,7 +3021,7 @@ static void
 p_story_parse_line(GapStoryBoard *stb, char *longline
    , gint32 longlinenr, char *multi_lines)
 {
-#define GAP_MAX_STB_PARAMS_PER_LINE 20
+#define GAP_MAX_STB_PARAMS_PER_LINE 25
   char *l_scan_ptr;
   char *l_record_key;
   char *l_parname;
@@ -3201,6 +3238,15 @@ p_story_parse_line(GapStoryBoard *stb, char *longline
       if(*l_wordval[1]) { stb->preferred_decoder = g_strdup(l_wordval[1]); }
       goto cleanup;  /* master RECORD does not create frame range elements */
   }
+  /* Master informations: GAP_STBKEY_VID_MASTER_INSERT_ALPHA */
+  if (strcmp(l_record_key, GAP_STBKEY_VID_MASTER_INSERT_ALPHA) == 0)
+  {
+      char *l_format_ptr   = l_wordval[1];
+      if(*l_format_ptr) {  p_flip_dir_separators(l_format_ptr);;
+                           stb->master_insert_alpha_format = g_strdup(l_format_ptr);
+                        }
+      goto cleanup;  /* master RECORD does not create frame range elements */
+  }
   /* Master informations: GAP_STBKEY_VID_MASTER_INSERT_AREA */
   if (strcmp(l_record_key, GAP_STBKEY_VID_MASTER_INSERT_AREA) == 0)
   {
@@ -3244,7 +3290,7 @@ p_story_parse_line(GapStoryBoard *stb, char *longline
     char *l_track_ptr        = l_wordval[2];
     char *l_page_ptr         = l_wordval[3];
     GapStoryEditSettings *edit_settings;
-    
+
     edit_settings = stb->edit_settings;
 
     if (edit_settings != NULL)
@@ -3262,18 +3308,19 @@ p_story_parse_line(GapStoryBoard *stb, char *longline
   {
     stb->active_section = gap_story_create_or_find_section_by_name(stb, NULL);
     goto cleanup;  /* main section RECORD does not create frame range elements */
-  }  
+  }
   if (strcmp(l_record_key, GAP_STBKEY_SUB_SECTION) == 0)
   {
     char *l_section_name_ptr      = l_wordval[1];
-    
+
     stb->active_section = gap_story_create_or_find_section_by_name(stb, l_section_name_ptr);
     goto cleanup;  /* sub section RECORD does not create frame range elements */
-  }  
+  }
 
 
   /* =========== Vid Attributes ===== start =========== */
   if ((strcmp(l_record_key, GAP_STBKEY_VID_OPACITY)  == 0)
+  ||  (strcmp(l_record_key, GAP_STBKEY_VID_ROTATE)   == 0)
   ||  (strcmp(l_record_key, GAP_STBKEY_VID_ZOOM_X)   == 0)
   ||  (strcmp(l_record_key, GAP_STBKEY_VID_ZOOM_Y)   == 0)
   ||  (strcmp(l_record_key, GAP_STBKEY_VID_MOVE_X)   == 0)
@@ -3340,7 +3387,35 @@ p_story_parse_line(GapStoryBoard *stb, char *longline
         if(*l_to_ptr)       { stb_elem->att_arr_value_to[ii]    = p_scan_gdouble(l_to_ptr,   0.0, 1.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, 999, stb); }
-        
+
+        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_ROTATE */
+    if (strcmp(l_record_key, GAP_STBKEY_VID_ROTATE) == 0)
+    {
+      gboolean elem_already_in_the_list = FALSE;
+      ii = GAP_STB_ATT_TYPE_ROTATE;
+
+      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, -36000.0, 36000.0, stb); }
+        if(*l_to_ptr)       { stb_elem->att_arr_value_to[ii]    = p_scan_gdouble(l_to_ptr,   -36000.0, 36000.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, 999, stb); }
+
         p_assign_accel_attr(stb_elem, stb, l_accel_ptr, ii);
 
         if(elem_already_in_the_list != TRUE)
@@ -3368,7 +3443,7 @@ p_story_parse_line(GapStoryBoard *stb, char *longline
         if(*l_to_ptr)       { stb_elem->att_arr_value_to[ii]    = p_scan_gdouble(l_to_ptr,   0.0001, 999.9, 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, 999, stb); }
-        
+
         p_assign_accel_attr(stb_elem, stb, l_accel_ptr, ii);
 
         if(elem_already_in_the_list != TRUE)
@@ -3396,7 +3471,7 @@ p_story_parse_line(GapStoryBoard *stb, char *longline
         if(*l_to_ptr)       { stb_elem->att_arr_value_to[ii]    = p_scan_gdouble(l_to_ptr,   0.0001, 999.9, 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, 999, stb); }
-        
+
         p_assign_accel_attr(stb_elem, stb, l_accel_ptr, ii);
 
         if(elem_already_in_the_list != TRUE)
@@ -3424,7 +3499,7 @@ p_story_parse_line(GapStoryBoard *stb, char *longline
         if(*l_to_ptr)       { stb_elem->att_arr_value_to[ii]    = p_scan_gdouble(l_to_ptr,   -99999.9, 99999.9, 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, 999, stb); }
-        
+
         p_assign_accel_attr(stb_elem, stb, l_accel_ptr, ii);
 
         if(elem_already_in_the_list != TRUE)
@@ -3452,7 +3527,7 @@ p_story_parse_line(GapStoryBoard *stb, char *longline
         if(*l_to_ptr)       { stb_elem->att_arr_value_to[ii]    = p_scan_gdouble(l_to_ptr,   -99999.9, 99999.9, 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, 999, stb); }
-        
+
         p_assign_accel_attr(stb_elem, stb, l_accel_ptr, ii);
 
         if(elem_already_in_the_list != TRUE)
@@ -3578,6 +3653,7 @@ p_story_parse_line(GapStoryBoard *stb, char *longline
     char *l_mask_disable_ptr  = l_wordval[9];
     char *l_fmac_total_steps_ptr  = l_wordval[10];
     char *l_fmac_accel_ptr        = l_wordval[11];
+    char *l_colormask_file_ptr    = l_wordval[12];
 
 
     stb_elem = gap_story_new_elem(GAP_STBREC_VID_IMAGE);
@@ -3602,6 +3678,7 @@ p_story_parse_line(GapStoryBoard *stb, char *longline
           , l_mask_disable_ptr
           , l_fmac_total_steps_ptr
           , l_fmac_accel_ptr
+          , l_colormask_file_ptr
           );
 
       p_check_image_numpart(stb_elem, l_filename_ptr);
@@ -3632,6 +3709,7 @@ p_story_parse_line(GapStoryBoard *stb, char *longline
     char *l_mask_disable_ptr  = l_wordval[14];
     char *l_fmac_total_steps_ptr  = l_wordval[15];
     char *l_fmac_accel_ptr        = l_wordval[16];
+    char *l_colormask_file_ptr    = l_wordval[17];
 
     stb_elem = gap_story_new_elem(GAP_STBREC_VID_FRAMES);
     if(stb_elem)
@@ -3661,6 +3739,7 @@ p_story_parse_line(GapStoryBoard *stb, char *longline
           , l_mask_disable_ptr
           , l_fmac_total_steps_ptr
           , l_fmac_accel_ptr
+          , l_colormask_file_ptr
           );
 
       if((*l_basename_ptr)
@@ -3702,6 +3781,7 @@ p_story_parse_line(GapStoryBoard *stb, char *longline
     char *l_mask_disable_ptr  = l_wordval[17];
     char *l_fmac_total_steps_ptr  = l_wordval[18];
     char *l_fmac_accel_ptr        = l_wordval[19];
+    char *l_colormask_file_ptr    = l_wordval[20];
 
     stb_elem = gap_story_new_elem(GAP_STBREC_VID_MOVIE);
     if(stb_elem)
@@ -3734,6 +3814,7 @@ p_story_parse_line(GapStoryBoard *stb, char *longline
           , l_mask_disable_ptr
           , l_fmac_total_steps_ptr
           , l_fmac_accel_ptr
+          , l_colormask_file_ptr
           );
 
 
@@ -3766,8 +3847,9 @@ p_story_parse_line(GapStoryBoard *stb, char *longline
     char *l_mask_disable_ptr  = l_wordval[13];
     char *l_fmac_total_steps_ptr  = l_wordval[14];
     char *l_fmac_accel_ptr        = l_wordval[15];
+    char *l_colormask_file_ptr    = l_wordval[16];
     GapStorySection *l_main_section;
-    
+
     l_main_section = gap_story_find_main_section(stb);
 
     if ((strcmp(l_record_key, GAP_STBKEY_VID_PLAY_SECTION) == 0)
@@ -3783,7 +3865,7 @@ p_story_parse_line(GapStoryBoard *stb, char *longline
        */
       stb_elem = gap_story_new_elem(GAP_STBREC_VID_BLACKSECTION);
     }
-    
+
     if(stb_elem)
     {
       stb_elem->file_line_nr = longlinenr;
@@ -3807,6 +3889,7 @@ p_story_parse_line(GapStoryBoard *stb, char *longline
           , l_mask_disable_ptr
           , l_fmac_total_steps_ptr
           , l_fmac_accel_ptr
+          , l_colormask_file_ptr
           );
 
       gap_story_list_append_elem(stb, stb_elem);
@@ -3834,6 +3917,7 @@ p_story_parse_line(GapStoryBoard *stb, char *longline
     char *l_mask_disable_ptr  = l_wordval[12];
     char *l_fmac_total_steps_ptr  = l_wordval[13];
     char *l_fmac_accel_ptr        = l_wordval[14];
+    char *l_colormask_file_ptr    = l_wordval[15];
 
     stb_elem = gap_story_new_elem(GAP_STBREC_VID_COLOR);
     if(stb_elem)
@@ -3859,6 +3943,7 @@ p_story_parse_line(GapStoryBoard *stb, char *longline
           , l_mask_disable_ptr
           , l_fmac_total_steps_ptr
           , l_fmac_accel_ptr
+          , l_colormask_file_ptr
           );
 
       gap_story_list_append_elem(stb, stb_elem);
@@ -3886,6 +3971,7 @@ p_story_parse_line(GapStoryBoard *stb, char *longline
     char *l_mask_disable_ptr  = l_wordval[13];
     char *l_fmac_total_steps_ptr  = l_wordval[14];
     char *l_fmac_accel_ptr        = l_wordval[15];
+    char *l_colormask_file_ptr    = l_wordval[16];
 
     stb_elem = gap_story_new_elem(GAP_STBREC_VID_ANIMIMAGE);
     if(stb_elem)
@@ -3911,6 +3997,7 @@ p_story_parse_line(GapStoryBoard *stb, char *longline
           , l_mask_disable_ptr
           , l_fmac_total_steps_ptr
           , l_fmac_accel_ptr
+          , l_colormask_file_ptr
           );
 
       gap_story_list_append_elem(stb, stb_elem);
@@ -4299,7 +4386,7 @@ p_story_parse_line(GapStoryBoard *stb, char *longline
 
 
 
-  /* accept lines with filenames as implicite VID_PLAY_IMAGE commands */
+  /* accept lines with filenames as implicit VID_PLAY_IMAGE commands */
   {
     char *l_filename_ptr;
 
@@ -4387,7 +4474,7 @@ p_stb_is_empty_in_all_sections(GapStoryBoard *stb)
       return(FALSE);
     }
   }
-  
+
   return (TRUE);
 }  /* end p_stb_is_empty_in_all_sections */
 
@@ -4784,7 +4871,7 @@ p_story_save_att_transition(FILE *fp, GapStoryElem *stb_elem)
       if(ii !=0 )
       {
         /* print one extra space
-         * VID_MOVE_X, VID_MOVE_Y, VID_ZOOM_X, VID_ZOOM_Y
+         * VID_MOVE_X, VID_MOVE_Y, VID_ZOOM_X, VID_ZOOM_Y VID_ROTATE
          * are 1 character shorter than VID_OPACITY
          */
         fprintf(fp, " ");
@@ -4904,7 +4991,7 @@ p_story_save_header(GapStoryBoard *stb, FILE *fp)
   {
      static gchar *l_bool_tab[2] = { GAP_STB_VTRACK1_TOP_TRUE, GAP_STB_VTRACK1_TOP_FALSE };
      gint l_bool_idx;
-     
+
      l_bool_idx = 0;
      if (stb->master_vtrack1_is_toplayer != TRUE)
      {
@@ -4993,7 +5080,7 @@ p_story_save_header(GapStoryBoard *stb, FILE *fp)
      char *section_name;
      int l_track;
      int l_page;
-     
+
      l_track = stb->edit_settings->track;
      l_page = stb->edit_settings->page;
      section_name = "\0";
@@ -5001,7 +5088,7 @@ p_story_save_header(GapStoryBoard *stb, FILE *fp)
      {
        section_name = stb->edit_settings->section_name;
      }
-     
+
      gap_stb_syntax_get_parname_tab(GAP_STBKEY_EDIT_SETTINGS
                                   ,&l_parnam_tab
                                   );
@@ -5032,6 +5119,21 @@ p_story_save_header(GapStoryBoard *stb, FILE *fp)
     }
   }
 
+  if (stb->master_insert_alpha_format)
+  {
+    if(*stb->master_insert_alpha_format != '\0')
+    {
+      gap_stb_syntax_get_parname_tab(GAP_STBKEY_VID_MASTER_INSERT_ALPHA
+                                    ,&l_parnam_tab
+                                    );
+      fprintf(fp, "%s  %s:\"%s\"\n"
+           , GAP_STBKEY_VID_MASTER_INSERT_ALPHA
+           , l_parnam_tab.parname[1]
+           , stb->master_insert_alpha_format
+           );
+    }
+  }
+
   if (stb->master_insert_area_format)
   {
     if(*stb->master_insert_area_format != '\0')
@@ -5047,6 +5149,7 @@ p_story_save_header(GapStoryBoard *stb, FILE *fp)
     }
   }
 
+
 }  /* end p_story_save_header */
 
 
@@ -5478,6 +5581,7 @@ p_story_save_flip_and_mask_params(FILE *fp, GapStoryElem *stb_elem
   , const char *parname_mask_anchor
   , const char *parname_mask_stepsize
   , const char *parname_mask_disable
+  , const char *parname_colormask_file
   )
 {
   p_story_save_flip_param(fp, stb_elem, parname_flip);
@@ -5501,6 +5605,11 @@ p_story_save_flip_and_mask_params(FILE *fp, GapStoryElem *stb_elem
                , parname_mask_anchor
                );
         break;
+      case GAP_MSK_ANCHOR_XCOLOR:
+        fprintf(fp, " %s:xcolor"
+               , parname_mask_anchor
+               );
+        break;
     }
 
     if(stb_elem->mask_stepsize != 1.0)
@@ -5527,6 +5636,14 @@ p_story_save_flip_and_mask_params(FILE *fp, GapStoryElem *stb_elem
 
   }
 
+  if(stb_elem->colormask_file)
+  {
+    fprintf(fp, " %s:\"%s\""
+           , parname_colormask_file
+           , stb_elem->colormask_file
+           );
+
+  }
 }  /* end p_story_save_flip_and_mask_params */
 
 /* ---------------------------------
@@ -5671,9 +5788,9 @@ p_story_save_video_list(GapStoryBoard *stb, FILE *fp, gint32 save_track
                );
 
           p_story_save_filtermacro_params(fp, stb_elem
-	      , l_parnam_tab.parname[9]   /* parname_fmac_file */
-	      , l_parnam_tab.parname[15]  /* parname_fmac_steps */
-	      , l_parnam_tab.parname[16]  /* parname_fmac_accel */
+          , l_parnam_tab.parname[9]   /* parname_fmac_file */
+          , l_parnam_tab.parname[15]  /* parname_fmac_steps */
+          , l_parnam_tab.parname[16]  /* parname_fmac_accel */
               );
 
           p_story_save_flip_and_mask_params(fp, stb_elem
@@ -5682,6 +5799,7 @@ p_story_save_video_list(GapStoryBoard *stb, FILE *fp, gint32 save_track
                   , l_parnam_tab.parname[12]  /* parname_mask_anchor */
                   , l_parnam_tab.parname[13]  /* parname_mask_stepsize */
                   , l_parnam_tab.parname[14]  /* parname_mask_disable */
+                  , l_parnam_tab.parname[17]  /* parname_colormask_file */
                   );
 
           fprintf(fp, "\n");
@@ -5705,9 +5823,9 @@ p_story_save_video_list(GapStoryBoard *stb, FILE *fp, gint32 save_track
                );
 
         p_story_save_filtermacro_params(fp, stb_elem
-	      , l_parnam_tab.parname[4]   /* parname_fmac_file */
-	      , l_parnam_tab.parname[10]  /* parname_fmac_steps */
-	      , l_parnam_tab.parname[11]  /* parname_fmac_accel */
+          , l_parnam_tab.parname[4]   /* parname_fmac_file */
+          , l_parnam_tab.parname[10]  /* parname_fmac_steps */
+          , l_parnam_tab.parname[11]  /* parname_fmac_accel */
               );
 
         p_story_save_flip_and_mask_params(fp, stb_elem
@@ -5716,6 +5834,7 @@ p_story_save_video_list(GapStoryBoard *stb, FILE *fp, gint32 save_track
                   , l_parnam_tab.parname[7]  /* parname_mask_anchor */
                   , l_parnam_tab.parname[8]  /* parname_mask_stepsize */
                   , l_parnam_tab.parname[9]  /* parname_mask_disable */
+                  , l_parnam_tab.parname[12] /* parname_colormask_file */
                   );
         fprintf(fp, "\n");
         if(gap_debug)
@@ -5750,9 +5869,9 @@ p_story_save_video_list(GapStoryBoard *stb, FILE *fp, gint32 save_track
                );
 
           p_story_save_filtermacro_params(fp, stb_elem
-	      , l_parnam_tab.parname[11]  /* parname_fmac_file */
-	      , l_parnam_tab.parname[18]  /* parname_fmac_steps */
-	      , l_parnam_tab.parname[19]  /* parname_fmac_accel */
+          , l_parnam_tab.parname[11]  /* parname_fmac_file */
+          , l_parnam_tab.parname[18]  /* parname_fmac_steps */
+          , l_parnam_tab.parname[19]  /* parname_fmac_accel */
               );
 
           if(stb_elem->preferred_decoder)
@@ -5768,6 +5887,7 @@ p_story_save_video_list(GapStoryBoard *stb, FILE *fp, gint32 save_track
                   , l_parnam_tab.parname[15]  /* parname_mask_anchor */
                   , l_parnam_tab.parname[16]  /* parname_mask_stepsize */
                   , l_parnam_tab.parname[17]  /* parname_mask_disable */
+                  , l_parnam_tab.parname[20]  /* parname_colormask_file */
                   );
           fprintf(fp, "\n");
         }
@@ -5795,7 +5915,7 @@ p_story_save_video_list(GapStoryBoard *stb, FILE *fp, gint32 save_track
           {
             l_gap_stb_key_vid_play_section = GAP_STBKEY_VID_PLAY_BLACKSECTION;
           }
-          
+
 
           gap_stb_syntax_get_parname_tab(GAP_STBKEY_VID_PLAY_SECTION
                                   ,&l_parnam_tab
@@ -5815,9 +5935,9 @@ p_story_save_video_list(GapStoryBoard *stb, FILE *fp, gint32 save_track
                );
 
           p_story_save_filtermacro_params(fp, stb_elem
-	      , l_parnam_tab.parname[8]   /* parname_fmac_file */
-	      , l_parnam_tab.parname[14]  /* parname_fmac_steps */
-	      , l_parnam_tab.parname[15]  /* parname_fmac_accel */
+          , l_parnam_tab.parname[8]   /* parname_fmac_file */
+          , l_parnam_tab.parname[14]  /* parname_fmac_steps */
+          , l_parnam_tab.parname[15]  /* parname_fmac_accel */
               );
 
           p_story_save_flip_and_mask_params(fp, stb_elem
@@ -5826,6 +5946,7 @@ p_story_save_video_list(GapStoryBoard *stb, FILE *fp, gint32 save_track
                   , l_parnam_tab.parname[11]  /* parname_mask_anchor */
                   , l_parnam_tab.parname[12]  /* parname_mask_stepsize */
                   , l_parnam_tab.parname[13]  /* parname_mask_disable */
+                  , l_parnam_tab.parname[16]  /* parname_colormask_file */
                   );
           fprintf(fp, "\n");
         }
@@ -5854,9 +5975,9 @@ p_story_save_video_list(GapStoryBoard *stb, FILE *fp, gint32 save_track
                );
 
           p_story_save_filtermacro_params(fp, stb_elem
-	      , l_parnam_tab.parname[8]   /* parname_fmac_file */
-	      , l_parnam_tab.parname[14]  /* parname_fmac_steps */
-	      , l_parnam_tab.parname[15]  /* parname_fmac_accel */
+          , l_parnam_tab.parname[8]   /* parname_fmac_file */
+          , l_parnam_tab.parname[14]  /* parname_fmac_steps */
+          , l_parnam_tab.parname[15]  /* parname_fmac_accel */
               );
 
           p_story_save_flip_and_mask_params(fp, stb_elem
@@ -5865,6 +5986,7 @@ p_story_save_video_list(GapStoryBoard *stb, FILE *fp, gint32 save_track
                   , l_parnam_tab.parname[11]  /* parname_mask_anchor */
                   , l_parnam_tab.parname[12]  /* parname_mask_stepsize */
                   , l_parnam_tab.parname[13]  /* parname_mask_disable */
+                  , l_parnam_tab.parname[16]  /* parname_colormask_file */
                   );
 
           fprintf(fp, "\n");
@@ -5914,7 +6036,7 @@ gap_story_save(GapStoryBoard *stb, const char *filename)
 
     p_story_save_header(stb, fp);
     p_story_save_mask_elem_list(stb, fp);
-    
+
 
     for(section = stb->stb_section; section != NULL; section = section->next)
     {
@@ -6217,7 +6339,7 @@ p_story_locate_framenr(GapStorySection   *section, gint32 in_framenr, gint32 in_
 /* ------------------------------
  * gap_story_locate_framenr
  * ------------------------------
- * IN: stb        The Storyboardfile 
+ * IN: stb        The Storyboardfile
  *                   NOTE: this procedure operates on the active_section of the
  *                         specified storyboard.
  * IN: in_framenr The Framenumber in sequence to locate
@@ -6297,7 +6419,7 @@ gap_story_count_total_frames_in_section(GapStorySection  *section)
   l_total_frames = 0;
   for(ii=0; ii < GAP_STB_MAX_VID_TRACKS; ii++)
   {
-    l_total_frames = MAX(l_framecount[ii], l_total_frames); 
+    l_total_frames = MAX(l_framecount[ii], l_total_frames);
   }
 
   return(l_total_frames);
@@ -6434,7 +6556,7 @@ gap_story_get_master_pixelsize(GapStoryBoard *stb
   {
     GapStoryLocateRet *stb_ret;
     GapStorySection   *section;
-    
+
     section = gap_story_find_main_section(stb);
 
     stb_ret = p_story_locate_framenr(section
@@ -6670,7 +6792,7 @@ gap_story_fake_ainfo_from_stb(GapStoryBoard *stb, gint32 in_track)
     l_ainfo_ptr->height = l_height;
 
     l_max_framecount = 0;
-    
+
     if (stb->mapping != NULL)
     {
       /* use framecount from selction mapping if
@@ -6678,7 +6800,7 @@ gap_story_fake_ainfo_from_stb(GapStoryBoard *stb, gint32 in_track)
        */
       l_max_framecount = stb->mapping->total_frames_selected;
     }
-    
+
     if(l_max_framecount == 0)
     {
       if(in_track < 0)
@@ -6699,7 +6821,7 @@ gap_story_fake_ainfo_from_stb(GapStoryBoard *stb, gint32 in_track)
         l_max_framecount = p_story_count_frames_in_track(stb->active_section ,in_track);
       }
     }
-    
+
 
     l_ainfo_ptr->curr_frame_nr = 1;
     l_ainfo_ptr->first_frame_nr = 1;
@@ -6751,6 +6873,7 @@ gap_story_elem_duplicate(GapStoryElem      *stb_elem)
     if(stb_elem->basename)          stb_elem_dup->basename          = g_strdup(stb_elem->basename);
     if(stb_elem->ext)               stb_elem_dup->ext               = g_strdup(stb_elem->ext);
     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);
     stb_elem_dup->seltrack        = stb_elem->seltrack;
     stb_elem_dup->exact_seek      = stb_elem->exact_seek;
@@ -6837,6 +6960,7 @@ gap_story_elem_copy(GapStoryElem *stb_elem_dst, GapStoryElem *stb_elem)
     if(stb_elem_dst->basename)          g_free(stb_elem_dst->basename);
     if(stb_elem_dst->ext)               g_free(stb_elem_dst->ext);
     if(stb_elem_dst->filtermacro_file)  g_free(stb_elem_dst->filtermacro_file);
+    if(stb_elem_dst->colormask_file)    g_free(stb_elem_dst->colormask_file);
     if(stb_elem_dst->preferred_decoder) g_free(stb_elem_dst->preferred_decoder);
     if(stb_elem_dst->mask_name)         g_free(stb_elem_dst->mask_name);
     if(stb_elem_dst->aud_filename)      g_free(stb_elem_dst->aud_filename);
@@ -6847,6 +6971,7 @@ gap_story_elem_copy(GapStoryElem *stb_elem_dst, GapStoryElem *stb_elem)
     stb_elem_dst->basename          = NULL;
     stb_elem_dst->ext               = NULL;
     stb_elem_dst->filtermacro_file  = NULL;
+    stb_elem_dst->colormask_file    = NULL;
     stb_elem_dst->preferred_decoder = NULL;
     stb_elem_dst->mask_name = NULL;
     stb_elem_dst->aud_filename = NULL;
@@ -6865,6 +6990,7 @@ gap_story_elem_copy(GapStoryElem *stb_elem_dst, GapStoryElem *stb_elem)
     if(stb_elem->basename)          stb_elem_dst->basename          = g_strdup(stb_elem->basename);
     if(stb_elem->ext)               stb_elem_dst->ext               = g_strdup(stb_elem->ext);
     if(stb_elem->filtermacro_file)  stb_elem_dst->filtermacro_file  = g_strdup(stb_elem->filtermacro_file);
+    if(stb_elem->colormask_file)    stb_elem_dst->colormask_file    = g_strdup(stb_elem->colormask_file);
     if(stb_elem->preferred_decoder) stb_elem_dst->preferred_decoder = g_strdup(stb_elem->preferred_decoder);
     if(stb_elem->mask_name)         stb_elem_dst->mask_name         = g_strdup(stb_elem->mask_name);
     if(stb_elem->aud_filename)      stb_elem_dst->aud_filename      = g_strdup(stb_elem->aud_filename);
@@ -6985,7 +7111,7 @@ gap_story_find_mask_reference_by_name(GapStoryBoard *stb_ptr, const char *mask_n
   {
     return (NULL);
   }
-  
+
   GapStorySection *section;
   for(section = stb_ptr->stb_section; section != NULL; section = section->next)
   {
@@ -6993,7 +7119,7 @@ gap_story_find_mask_reference_by_name(GapStoryBoard *stb_ptr, const char *mask_n
     {
       continue;
     }
-    
+
     for(stb_elem = section->stb_elem; stb_elem != NULL;  stb_elem = stb_elem->next)
     {
       if((stb_elem->track != GAP_STB_MASK_TRACK_NUMBER)
@@ -7019,13 +7145,13 @@ gap_story_find_mask_reference_by_name(GapStoryBoard *stb_ptr, const char *mask_n
  * copy mask definitions of the specified stb_src
  * as hidden mask definitions (track = GAP_STB_HIDDEN_MASK_TRACK_NUMBER)
  * to the mask_section of the specified destination storyboard stb_dest.
- * Only those mask definitions are copied, that are refered
+ * Only those mask definitions are copied, that are referred
  * in the destination storyboard.
  * NOTES: this procedure is often called in cut or copy operations,
  * where the destination (stb_dest) is the paste buffer that contains
  * already copies of all selected elements (where copy source was stb_src)
- * This procedure is now responsible, to add all refered mask definition
- * elements implicitely as hidden elements to the mask_section.
+ * This procedure is now responsible, to add all referred mask definition
+ * elements implicitly as hidden elements to the mask_section.
  *
  * the hidden mask definition elements are only relevant for
  * paste processing that may follow later on.
@@ -7045,7 +7171,7 @@ p_story_board_duplicate_refered_mask_definitions(GapStoryBoard *stb_dest
     printf("@@@@@ p_story_board_duplicate_refered_mask_definitions\n");
     gap_story_debug_print_list(stb_src);
   }
-  
+
   if (stb_src->mask_section == NULL)
   {
     printf("** ERROR p_story_board_duplicate_refered_mask_definitions src mask_section is NULL\n");
@@ -7103,12 +7229,12 @@ p_story_copy_edit_settings (GapStoryEditSettings *dst_edit_settings
       g_free(dst_edit_settings->section_name);
       dst_edit_settings->section_name = NULL;
     }
-    
+
     if (src_edit_settings->section_name)
     {
       dst_edit_settings->section_name = g_strdup(src_edit_settings->section_name);
     }
-    
+
     dst_edit_settings->track = src_edit_settings->track;
     dst_edit_settings->page = src_edit_settings->page;
   }
@@ -7140,7 +7266,7 @@ p_story_copy_edit_settings (GapStoryEditSettings *dst_edit_settings
  *                             (all those mask_definitions that are referred by the copied clips)
  * IN: all_masks   TRUE:  explicite include all mask definitions with regular
  *                             mask track number GAP_STB_MASK_TRACK_NUMBER in the duplicate.
- *                 FALSE: mask definitions (elements of the mask_section with 
+ *                 FALSE: mask definitions (elements of the mask_section with
  *                        track GAP_STB_MASK_TRACK_NUMBER)
  *                        are automatically included in the duplicate (as GAP_STB_HIDDEN_MASK_TRACK_NUMBER)
  *                        but only when the copied video elements refere to them.
@@ -7190,6 +7316,10 @@ p_story_board_duplicate(GapStoryBoard *stb_ptr, GapStoryDuplicateMode dup_mode
   {
     stb_dup->preferred_decoder = g_strdup(stb_ptr->preferred_decoder);
   }
+  if(stb_ptr->master_insert_alpha_format)
+  {
+    stb_dup->master_insert_alpha_format = g_strdup(stb_ptr->master_insert_alpha_format);
+  }
   if(stb_ptr->master_insert_area_format)
   {
     stb_dup->master_insert_area_format = g_strdup(stb_ptr->master_insert_area_format);
@@ -7226,7 +7356,7 @@ p_story_board_duplicate(GapStoryBoard *stb_ptr, GapStoryDuplicateMode dup_mode
       {
         /* mask_section is the active_section
          * this case handled copy from mask definition
-         * into main section 
+         * into main section
          * (the mask_section of the duplicate will be left empty in that case)
          */
         dup_to_section = gap_story_find_main_section(stb_dup);
@@ -7239,7 +7369,7 @@ p_story_board_duplicate(GapStoryBoard *stb_ptr, GapStoryDuplicateMode dup_mode
         /* handle only specified active section.
          *  the duplicate will include only clips from the active section
          *  in its MAIN section.
-         * 
+         *
          */
         if(section == active_section)
         {
@@ -7251,7 +7381,7 @@ p_story_board_duplicate(GapStoryBoard *stb_ptr, GapStoryDuplicateMode dup_mode
         /* handle all available sections
          * (the duplicate will include all the sections from the original with same names and structure)
          */
-        dup_to_section = 
+        dup_to_section =
            gap_story_create_or_find_section_by_name(stb_dup, section->section_name);
 
         /* handle current_vtrack settings of copied section
@@ -7264,15 +7394,15 @@ p_story_board_duplicate(GapStoryBoard *stb_ptr, GapStoryDuplicateMode dup_mode
                                      );
       }
     }
-    
-    
-    
+
+
+
     if(dup_to_section == NULL)
     {
       /* current section not relevant */
       continue;
     }
-    
+
     /* copy tracks of the currently processed section */
     for(stb_elem = section->stb_elem; stb_elem != NULL;  stb_elem = stb_elem->next)
     {
@@ -7302,7 +7432,7 @@ p_story_board_duplicate(GapStoryBoard *stb_ptr, GapStoryDuplicateMode dup_mode
           dup_flag = TRUE;
         }
       }
-  
+
       if(trackselect_flag)
       {
         if( ((stb_elem->selected) && (dup_mode == GAP_STB_DUPLICATE_ONLY_SELECTED_ELEMS))
@@ -7313,7 +7443,7 @@ p_story_board_duplicate(GapStoryBoard *stb_ptr, GapStoryDuplicateMode dup_mode
           dup_flag = TRUE;
         }
       }
-  
+
       if(dup_flag)
       {
           stb_elem_dup = gap_story_elem_duplicate(stb_elem);
@@ -7375,7 +7505,7 @@ p_story_board_duplicate(GapStoryBoard *stb_ptr, GapStoryDuplicateMode dup_mode
             }
 
           }
-          
+
           if(stb_elem_dup)
           {
             gap_story_list_append_elem_at_section(stb_dup, stb_elem_dup, dup_to_section);
@@ -7383,7 +7513,7 @@ p_story_board_duplicate(GapStoryBoard *stb_ptr, GapStoryDuplicateMode dup_mode
       }
     }
 
-    
+
   }  /* end for section loop */
 
 
@@ -7420,14 +7550,14 @@ p_story_board_duplicate(GapStoryBoard *stb_ptr, GapStoryDuplicateMode dup_mode
 /* -----------------------------
  * gap_story_duplicate_full
  * -----------------------------
- * make a full duplicate of all lists in all sections 
+ * make a full duplicate of all lists in all sections
  * (but without the comment sublists)
  */
 GapStoryBoard *
 gap_story_duplicate_full(GapStoryBoard *stb_ptr)
 {
   GapStoryBoard *stb_dup;
-  
+
   stb_dup = p_story_board_duplicate(stb_ptr
                                 , GAP_STB_DUPLICATE_ALL_ELEMS
                                 , -1         /* include all video tracks */
@@ -7443,7 +7573,7 @@ gap_story_duplicate_full(GapStoryBoard *stb_ptr)
 /* -------------------------------------------
  * gap_story_duplicate_active_and_mask_section
  * -------------------------------------------
- * make a duplicate of all list in all sections 
+ * make a duplicate of all list in all sections
  * (but without the comment sublists)
  */
 GapStoryBoard *
@@ -7472,7 +7602,7 @@ void
 gap_story_enable_hidden_maskdefinitions(GapStoryBoard *stb_ptr)
 {
   GapStoryElem  *stb_elem;
-  
+
   if (stb_ptr->mask_section == NULL)
   {
     return;
@@ -7516,7 +7646,7 @@ gap_story_duplicate_vtrack(GapStoryBoard *stb_ptr, gint32 in_vtrack)
  * gap_story_duplicate_sel_only
  * -----------------------------
  *
- * make a filtered duplicate of the storyboard where the element lists 
+ * make a filtered duplicate of the storyboard where the element lists
  * contains only elements of the
  * specified track in the active_section
  * with the selected flag set.
@@ -7548,7 +7678,7 @@ gap_story_duplicate_sel_only(GapStoryBoard *stb_ptr, gint32 in_vtrack)
  * gap_story_duplicate_one_elem_and_masks
  * ---------------------------------------
  *
- * make a filtered duplicate of the storyboard where the element lists 
+ * make a filtered duplicate of the storyboard where the element lists
  * contains only one element in the main section, that is specifed by story_id.
  * and must be in the active_section
  *
@@ -7576,7 +7706,7 @@ gap_story_duplicate_one_elem_and_masks(GapStoryBoard *stb_ptr
  * gap_story_duplicate_one_elem
  * ---------------------------------------
  *
- * make a filtered duplicate of the storyboard where the element lists 
+ * make a filtered duplicate of the storyboard where the element lists
  * contains only one element in the main section, that is specifed by story_id.
  * and must be in the active_section
  *
@@ -7611,7 +7741,7 @@ gap_story_duplicate_one_elem(GapStoryBoard *stb_ptr
  * and audio clips are ignored.
  *
  * typical usage at storyboard dialog startup,
- * to speed up videothumbnail creation.
+ * to speed up video thumbnail creation.
  * (minimize frame seek times)
  *
  * NOTE: mask definitions (elements of the track GAP_STB_MASK_TRACK_NUMBER)
@@ -7672,7 +7802,7 @@ gap_story_board_duplicate_distinct_sorted(GapStoryBoard  *stb_dup, GapStoryBoard
 
     stb_dup->unsaved_changes = TRUE;
   }
-  
+
   dup_main_section = gap_story_find_section_by_name(stb_dup, NULL);
 
   for(section = stb_ptr->stb_section; section != NULL; section = section->next)
@@ -7751,7 +7881,7 @@ gap_story_board_duplicate_distinct_sorted(GapStoryBoard  *stb_dup, GapStoryBoard
                     new_main_section =
                       gap_story_find_section_by_name(stb_new, NULL);
 
-                    
+
                     gap_story_list_append_elem_at_section(stb_new, stb_elem_dup, new_main_section);
 
                     /* insert before stb_elem_same */
@@ -7777,8 +7907,8 @@ gap_story_board_duplicate_distinct_sorted(GapStoryBoard  *stb_dup, GapStoryBoard
 
 
   }  /* end for section loop */
-  
-  
+
+
 
 
   if(gap_debug)
@@ -7805,7 +7935,7 @@ gap_story_board_duplicate_distinct_sorted(GapStoryBoard  *stb_dup, GapStoryBoard
  *   (If the destination storyboard already has subsection(s) with the same
  *    section_name(s) as the source storyboard, the elements are appended
  *    at end.)
- *   
+ *
  */
 void
 gap_story_copy_sub_sections(GapStoryBoard *stb_src, GapStoryBoard *stb_dst)
@@ -7835,7 +7965,7 @@ gap_story_copy_sub_sections(GapStoryBoard *stb_src, GapStoryBoard *stb_dst)
     {
       /* copy all available sub sections (except main and mask section)
        */
-      dup_to_section = 
+      dup_to_section =
          gap_story_create_or_find_section_by_name(stb_dst, section->section_name);
 
       /* copy tracks of the currently processed section */
@@ -7886,7 +8016,17 @@ gap_story_set_properties_like_sample_storyboard (GapStoryBoard *stb
       {
         stb->preferred_decoder  = g_strdup(stb_sample->preferred_decoder);
       }
-      
+
+      if(stb->master_insert_alpha_format)
+      {
+        g_free(stb->master_insert_alpha_format);
+        stb->master_insert_alpha_format  = NULL;
+      }
+      if(stb_sample->master_insert_alpha_format)
+      {
+        stb->master_insert_alpha_format = g_strdup(stb_sample->master_insert_alpha_format);
+      }
+
       if(stb->master_insert_area_format)
       {
         g_free(stb->master_insert_area_format);
@@ -7896,7 +8036,7 @@ gap_story_set_properties_like_sample_storyboard (GapStoryBoard *stb
       {
         stb->master_insert_area_format = g_strdup(stb_sample->master_insert_area_format);
       }
-      
+
     }
   }
 }  /* end gap_story_set_properties_like_sample_storyboard */
@@ -7926,7 +8066,7 @@ gap_story_remove_sel_elems(GapStoryBoard *stb)
 
       unsaved_changes_counter = 0;
       stb_elem_prev = NULL;
-      
+
       for(stb_elem = section->stb_elem; stb_elem != NULL;  stb_elem = stb_elem_next)
       {
         stb_elem_next = (GapStoryElem *)stb_elem->next;
@@ -8008,7 +8148,7 @@ gap_story_count_active_elements(GapStoryBoard *stb_ptr, gint32 in_track)
   {
     return (0);
   }
-  
+
   for(stb_elem = stb_ptr->active_section->stb_elem; stb_elem != NULL;  stb_elem = stb_elem->next)
   {
     if(stb_elem->track == in_track)
@@ -8049,7 +8189,7 @@ gap_story_fetch_nth_active_elem(GapStoryBoard *stb
   {
     return (NULL);
   }
-  
+
   for(stb_elem = stb->active_section->stb_elem; stb_elem != NULL;  stb_elem = stb_elem->next)
   {
     if(stb_elem->track == in_track)
@@ -8099,7 +8239,7 @@ gap_story_selection_all_set(GapStoryBoard *stb, gboolean sel_state)
  * gap_story_selection_by_story_id
  * ---------------------------------
  * set the element with specified story_id to the specified selection state.
- * Notes: 
+ * Notes:
  *  - searching for the element is done in all sections of the
  *    specified storyboard.
  *
@@ -8219,7 +8359,7 @@ gap_story_set_aud_movie_min_max(GapStoryBoard *stb)
 {
   GapStoryElem *stb_element;
   GapStoryElem *stb_elem;
-  
+
   if (stb->active_section == NULL)
   {
     return;
@@ -8786,7 +8926,7 @@ gap_story_get_default_attribute(gint att_typ_idx)
    {
      return (1.0);  /* 1.0 indicates fully opaque, or no scaling */
    }
-   return (0.0);  /* indicates centerd positioning */
+   return (0.0);  /* indicates centerd positioning or rotate 0 degree */
 }  /* end gap_story_get_default_attribute */
 
 
@@ -8823,6 +8963,7 @@ gap_story_file_calculate_render_attributes(GapStoryCalcAttr *result_attr
     , gboolean keep_proportions
     , gboolean fit_width
     , gboolean fit_height
+    , gdouble rotate
     , gdouble opacity
     , gdouble scale_x
     , gdouble scale_y
@@ -8849,6 +8990,7 @@ gap_story_file_calculate_render_attributes(GapStoryCalcAttr *result_attr
   result_height = -1;
   l_tmp_width = frame_width;
   l_tmp_height = frame_height;
+  result_attr->rotate = rotate;
 
   if(keep_proportions)
   {
@@ -8997,17 +9139,17 @@ gap_story_file_calculate_render_attributes(GapStoryCalcAttr *result_attr
  * get the current video track
  * for the specified section.
  * Typically used in the storyboard dialog
- * when switching sections. (for implicite switch
+ * when switching sections. (for implicit switch
  * to the video track that was active when the section
  * was edited the last time)
- * Note that the mask section has only one implicte track with
+ * Note that the mask section has only one implicit track with
  * GAP_STB_MASK_TRACK_NUMBER (0)
  */
 gint32
 gap_story_get_current_vtrack (GapStoryBoard *stb, GapStorySection *section)
 {
   gint32 current_vtrack;
-  
+
   if(section == stb->mask_section)
   {
     return (GAP_STB_MASK_TRACK_NUMBER);
@@ -9016,7 +9158,7 @@ gap_story_get_current_vtrack (GapStoryBoard *stb, GapStorySection *section)
   section->current_vtrack = current_vtrack;
 
   return (current_vtrack);
-  
+
 }  /* end gap_story_get_current_vtrack */
 
 
@@ -9038,7 +9180,7 @@ gap_story_set_current_vtrack (GapStoryBoard *stb, GapStorySection *section
   {
     return;
   }
-  
+
   if(section == stb->mask_section)
   {
     section->current_vtrack = GAP_STB_MASK_TRACK_NUMBER;
@@ -9083,10 +9225,10 @@ gap_story_set_current_vtrack (GapStoryBoard *stb, GapStorySection *section
  * and the 1st element must describe mapped_frame_number == 1
  * Mapping Example:
  *  the current vtrack has 5 clips
- *     clip1 has  7 unselected frames 
- *     clip2 has 10 SELECTED frames 
+ *     clip1 has  7 unselected frames
+ *     clip2 has 10 SELECTED frames
  *     clip3 has  4 unselected frames
- *     clip4 has  5 SELECTED frames 
+ *     clip4 has  5 SELECTED frames
  *     clip5 has  4 unselected frames
  *
  *     o .. unselected frame,
@@ -9116,12 +9258,12 @@ gap_story_get_mapped_master_frame_number(GapStoryFrameNumberMap *mapping
   GapStoryFrameNumberMappingElem *map_elem;
   gint32 mapped_nr;
   gint32 l_frame_number;
-  
+
   if(mapping == NULL)
   {
     return (frame_number);
   }
-  
+
   if (mapping->map_list == NULL)
   {
     return (frame_number);
@@ -9166,12 +9308,12 @@ p_story_add_selection_mapping_elem(GapStoryFrameNumberMap *mapping,
 {
   GapStoryFrameNumberMappingElem *new_map_elem;
   GapStoryFrameNumberMappingElem *map_elem;
-  
+
   new_map_elem = g_new(GapStoryFrameNumberMappingElem, 1);
   new_map_elem->next = NULL;
   new_map_elem->orig_frame_number = orig_frame_number;
   new_map_elem->mapped_frame_number = mapped_frame_number;
-  
+
 
   /* add new map element at and of the list */
   for(map_elem = mapping->map_list; map_elem != NULL; map_elem = map_elem->next)
@@ -9189,7 +9331,7 @@ p_story_add_selection_mapping_elem(GapStoryFrameNumberMap *mapping,
   {
     mapping->map_list = new_map_elem;
   }
-  
+
 }  /* end p_story_add_selection_mapping_elem */
 
 
@@ -9265,9 +9407,9 @@ gap_story_create_new_mapping_from_selection_OLD(GapStorySection *active_section
        */
       capture_maped_frame_number = TRUE;
       rest_frames = stb_elem->nframes;
-      
+
     }
-    
+
   }
 
   mapping->total_frames_selected = mapped_frame_number + rest_frames -1;
@@ -9293,20 +9435,20 @@ gap_story_create_new_mapping_from_selection_OLD(GapStorySection *active_section
  *
  * Example:
  * Storyboard track witch videoclip sequence:  a,b,(overlap 6),c,d,e
- * 
+ *
  *  1 2     5 6   8    11
  * +-+-+-+-+-+-+-+     +-+-+-+-+
  * | a=4   |b=3  |     | e=4   |
  * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  *   |c=2|     d=7     |
  *   +-+-+-+-+-+-+-+-+-+
- * 
+ *
  * Selected: a,b,e     total_frames_selected = (4 + 3 + 4) - (0) = 11
- * 
+ *
  * Selected: a,b,c     total_frames_selected = (4 + 3 + 2) - (2) = 7
- * 
+ *
  * Selected: a,b,c,d   total_frames_selected = (4 + 3 + 2 + 7) - ( 2 + 4) = 10
- * 
+ *
  * Selected: a,b,d     total_frames_selected = (4 + 3 + 7) - (4) = 10
  *                     in this case the 2 frames of clip 2 are not mapped (cause they are not selected)
  *                     but due to overlapping those frames are involved at rendering of selected frames
@@ -9315,7 +9457,7 @@ gap_story_create_new_mapping_from_selection_OLD(GapStorySection *active_section
  *                     but it reduces the configured overlap amount (from 6 to 4 frames).
  *                     the first 4 frames of clip d overlap frames of clip a and clip b,
  *                     therefore 4 frames are subtracted from the total_frames_selected sum.
- *                     
+ *
  */
 GapStoryFrameNumberMap *
 gap_story_create_new_mapping_from_selection(GapStorySection *active_section
@@ -9329,7 +9471,7 @@ gap_story_create_new_mapping_from_selection(GapStorySection *active_section
   gint32             sum_overlap_sel;
   gint32             curr_overlap;
   gint32             expect_next;
-  
+
   gboolean           capture_maped_frame_number;
 
   if (active_section == NULL)
@@ -9346,7 +9488,7 @@ gap_story_create_new_mapping_from_selection(GapStorySection *active_section
   sum_overlap_sel = 0;
   curr_overlap = 0;
   expect_next = 1;
-  
+
   orig_frame_number = 1;
   capture_maped_frame_number = FALSE;
 
@@ -9375,17 +9517,17 @@ gap_story_create_new_mapping_from_selection(GapStorySection *active_section
       {
         curr_overlap += expect_next - next_orig_frame_number;
       }
-      
-      
+
+
     }
 
     if( (stb_elem->track == vtrack ) && (gap_story_elem_is_video(stb_elem) == TRUE))
     {
       gint32 overlap_for_this_frame;
-      
+
       overlap_for_this_frame = MAX(0, MIN(curr_overlap, stb_elem->nframes));
       curr_overlap -= overlap_for_this_frame;
-      
+
       if (stb_elem->selected)
       {
         orig_frame_number = gap_story_get_framenr_by_story_id(active_section
@@ -9403,7 +9545,7 @@ gap_story_create_new_mapping_from_selection(GapStorySection *active_section
         expect_next = orig_frame_number + stb_elem->nframes;  /* expected frame without overlapping */
       }
     }
-    
+
   }
 
   mapping->total_frames_selected = sum_all_selected - sum_overlap_sel;
@@ -9417,7 +9559,7 @@ gap_story_create_new_mapping_from_selection(GapStorySection *active_section
   {
     GapStoryFrameNumberMap *old_mapping;
     old_mapping = gap_story_create_new_mapping_from_selection_OLD(active_section, vtrack);
-    
+
     if (old_mapping->total_frames_selected != mapping->total_frames_selected)
     {
       printf("\nOLD MAPPING\n");
@@ -9460,7 +9602,7 @@ gap_story_debug_print_mapping(GapStoryFrameNumberMap *mapping)
     }
   }
   printf("gap_story_debug_print_mapping END\n");
-  
+
 }  /* end gap_story_debug_print_mapping */
 
 
@@ -9479,7 +9621,7 @@ gap_story_free_GapStoryVideoFileRef(GapStoryVideoFileRef *vref_list)
   while(vref != NULL)
   {
     vref_next = vref->next;
-    
+
     if (vref->videofile != NULL)
     {
       g_free(vref->videofile);
@@ -9490,16 +9632,16 @@ gap_story_free_GapStoryVideoFileRef(GapStoryVideoFileRef *vref_list)
       g_free(vref->userdata);
       vref->userdata = NULL;
     }
-    
+
     if (vref->preferred_decoder != NULL)
     {
       g_free(vref->preferred_decoder);
       vref->preferred_decoder = NULL;
     }
-    
+
     g_free(vref);
     vref = vref_next;
-    
+
   }
 }  /* end gap_story_free_GapStoryVideoFileRef */
 
@@ -9515,7 +9657,7 @@ p_new_GapStoryVideoFileRef(const char *videofile
   , gint32          max_ref_framenr)
 {
   GapStoryVideoFileRef *vref;
-  
+
   vref = g_new(GapStoryVideoFileRef, 1);
   if(vref)
   {
@@ -9525,12 +9667,12 @@ p_new_GapStoryVideoFileRef(const char *videofile
     vref->preferred_decoder = NULL;
     vref->seltrack = seltrack;
     vref->max_ref_framenr = max_ref_framenr;
-    
+
     if (videofile != NULL)
     {
       vref->videofile = g_strdup(videofile);
     }
-    
+
     if (preferred_decoder != NULL)
     {
       vref->preferred_decoder = g_strdup(preferred_decoder);
@@ -9561,14 +9703,14 @@ gap_story_find_vref_by_name_and_seltrack(GapStoryVideoFileRef *vref_list
 
   }
   return (NULL);
-  
+
 }  /* end gap_story_find_vref_by_name_and_seltrack */
 
 
 /* ---------------------------------
  * gap_story_get_video_file_ref_list
  * ---------------------------------
- * IN: stb        The Storyboardfile 
+ * IN: stb        The Storyboardfile
  *                   NOTE: this procedure operates on all sections of the
  *                         specified storyboard.
  * OUT:
@@ -9584,7 +9726,7 @@ gap_story_get_video_file_ref_list(GapStoryBoard *stb)
 {
   GapStoryVideoFileRef *vref_list;
   GapStorySection *section;
-  
+
   vref_list = NULL;
 
   for(section = stb->stb_section; section != NULL; section = section->next)
@@ -9595,7 +9737,7 @@ gap_story_get_video_file_ref_list(GapStoryBoard *stb)
        if(stb_elem->record_type == GAP_STBREC_VID_MOVIE)
        {
          GapStoryVideoFileRef *vref;
-         
+
          vref = gap_story_find_vref_by_name_and_seltrack(vref_list
                                                         ,stb_elem->orig_filename
                                                         ,stb_elem->seltrack
@@ -9640,3 +9782,71 @@ gap_story_build_basename(const char *filename)
   basename = g_filename_display_basename(filename);
   return(basename);
 }  /* end gap_story_build_basename */
+
+
+
+/* ----------------------------------------------------
+ * gap_story_transform_rotate_layer
+ * ----------------------------------------------------
+ * rotate layer by the specified angle in degree
+ */
+void
+gap_story_transform_rotate_layer(gint32 image_id, gint32 layer_id, gdouble rotate)
+{
+  gint32       l_mask_id;
+  gint32       l_center_x;
+  gint32       l_center_y;
+  gdouble      l_angle_rad;
+  gdouble      l_angle_deg;
+
+  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);
+
+  l_center_x  = gimp_drawable_width(layer_id) / 2.0;
+  l_center_y  = gimp_drawable_height(layer_id) / 2.0;
+
+  /* have to rotate the layer (rotation also changes size as needed) */
+  gimp_drawable_transform_rotate_default(layer_id
+                                        , l_angle_rad
+                                        , TRUE            /* auto_center */
+                                        , l_center_x
+                                        , l_center_y
+                                        , TRUE             /* interpolation */
+                                        , FALSE            /* clip_results */
+                                        );
+
+
+}  /* end gap_story_transform_rotate_layer */
diff --git a/gap/gap_story_file.h b/gap/gap_story_file.h
index 3534641..5551e1a 100644
--- a/gap/gap_story_file.h
+++ b/gap/gap_story_file.h
@@ -1,6 +1,6 @@
 /*  gap_story_file.h
  *
- *  This module handles GAP storyboard file 
+ *  This module handles GAP storyboard file
  *  parsing of storyboard level1 files (load informations into a list)
  *  and (re)write storyboard files from the list (back to storyboard file)
  *
@@ -36,15 +36,16 @@
 #include "gap_story_syntax.h"
 #include "gap_story_render_types.h"
 
-/* transition attribute types 
+/* transition attribute types
  * (values are used as index for look-up tables)
  */
-#define GAP_STB_ATT_TYPES_ARRAY_MAX 5
+#define GAP_STB_ATT_TYPES_ARRAY_MAX 6
 #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_MASK_SECTION_NAME  "Masks"
@@ -57,11 +58,11 @@
   typedef enum
   {
      GAP_STBREC_VID_SILENCE
-    ,GAP_STBREC_VID_COLOR        
-    ,GAP_STBREC_VID_IMAGE        
-    ,GAP_STBREC_VID_ANIMIMAGE        
-    ,GAP_STBREC_VID_FRAMES       
-    ,GAP_STBREC_VID_MOVIE 
+    ,GAP_STBREC_VID_COLOR
+    ,GAP_STBREC_VID_IMAGE
+    ,GAP_STBREC_VID_ANIMIMAGE
+    ,GAP_STBREC_VID_FRAMES
+    ,GAP_STBREC_VID_MOVIE
     ,GAP_STBREC_VID_COMMENT
     ,GAP_STBREC_VID_UNKNOWN
 
@@ -88,14 +89,14 @@
   typedef enum
   {
      GAP_STB_PM_NORMAL
-    ,GAP_STB_PM_PINGPONG        
+    ,GAP_STB_PM_PINGPONG
   } GapStoryVideoPlaymode;
 
   typedef enum
   {
      GAP_STB_MASTER_TYPE_UNDEFINED
     ,GAP_STB_MASTER_TYPE_STORYBOARD
-    ,GAP_STB_MASTER_TYPE_CLIPLIST        
+    ,GAP_STB_MASTER_TYPE_CLIPLIST
   } GapStoryMasterType;
 
 
@@ -112,13 +113,13 @@
     GapStoryRecordType     record_type;
     GapStoryVideoPlaymode  playmode;
     gint32                 track;
-    
-    char  *orig_filename;   /* full filename use for IMAGE and MOVIE Files 
+
+    char  *orig_filename;   /* full filename use for IMAGE and MOVIE Files
                              * and SECTIONS (for section_name)
                              */
     char  *orig_src_line;   /* without \n, used to store header, comment and unknown lines */
-    
-                            /* basename + ext are used for FRAME range elements only */ 
+
+                            /* basename + ext are used for FRAME range elements only */
     gchar *basename;        /* path+filename (without number part and without extension */
     gchar *ext;             /* extenson ".xcf" ".jpg" ... including the dot */
     gint32     seltrack;    /* selected videotrack in a videofile (for GAP_FRN_MOVIE) */
@@ -130,6 +131,10 @@
                                * if track == GAP_STB_MASK_TRACK_NUMBER this atribute
                                * is the mandatory definition of the mask_name.
                                */
+    char   *colormask_file;   /* optional reference to a colormask parameter file
+                               * relevant for ancor mode GAP_MSK_ANCHOR_CLIPCOLOR
+                               * where mask is applied as colormask
+                               */
     gdouble mask_stepsize;
     GapStoryMaskAnchormode  mask_anchor;
     gboolean  mask_disable;
@@ -138,11 +143,11 @@
     gchar *filtermacro_file;
     gint32 fmac_total_steps;
     gint32 fmac_accel;
-    
+
     gint32 from_frame;
     gint32 to_frame;
     gint32 nloop;          /* 1 play one time */
-    
+
     gint32 nframes;        /* if playmode == normal
                             * then frames = nloop * (ABS(from_frame - to_frame) + 1);
                             * else frames = (nloop * 2 * ABS(from_frame - to_frame)) + 1;
@@ -160,7 +165,7 @@
     gdouble  color_green;
     gdouble  color_blue;
     gdouble  color_alpha;
-           
+
     /* members for attribute Record types */
     gboolean att_keep_proportions;
     gboolean att_fit_width;
@@ -188,7 +193,7 @@
     gdouble  aud_min_play_sec;  /* for optimzed audio extract from videofiles */
     gdouble  aud_max_play_sec;
     gdouble  aud_framerate;     /* framerate that is used to convert audio unit frame <-> secs */
- 
+
     struct GapStoryElem  *comment;
     struct GapStoryElem  *next;
   } GapStoryElem;
@@ -198,7 +203,7 @@
      GapStoryElem    *stb_elem;
      gchar           *section_name;  /* null refers to the main section */
      gint32          current_vtrack;
-     
+
      gint32          section_id;  /* unique ID, NOT persistent */
      gint32          version;     /* numer of changes while editing, NOT persistent */
 
@@ -216,9 +221,9 @@
   typedef struct GapStoryFrameNumberMappingElem {
     gint32 mapped_frame_number;
     gint32 orig_frame_number;
-    
+
     struct GapStoryFrameNumberMappingElem *next;
-    
+
   } GapStoryFrameNumberMappingElem;
 
 
@@ -251,7 +256,7 @@
      gint32         layout_thumbsize;
 
      gchar         *preferred_decoder;
-     
+
      /* for error handling while parsing */
      gchar         *errtext;
      gchar         *errline;
@@ -278,11 +283,12 @@
 
      gboolean       unsaved_changes;
      GapStoryEditSettings *edit_settings;
-     
+
+     gchar         *master_insert_alpha_format;
      gchar         *master_insert_area_format;
  }  GapStoryBoard;
 
- 
+
   typedef struct GapStoryLocateRet {
      GapStoryElem  *stb_elem;
      gint32        ret_framenr;
@@ -296,7 +302,8 @@
     gint32  x_offs;
     gint32  y_offs;
     gdouble opacity;
-    
+    gdouble rotate;
+
     gint32  visible_width;
     gint32  visible_height;
   } GapStoryCalcAttr;
@@ -308,7 +315,7 @@
      gchar           *preferred_decoder;
      gint32          seltrack;
      gint32          max_ref_framenr;
-     
+
      void            *next;
   } GapStoryVideoFileRef;
 
@@ -447,6 +454,7 @@ void                gap_story_file_calculate_render_attributes(GapStoryCalcAttr
                          , gboolean keep_proportions
                          , gboolean fit_width
                          , gboolean fit_height
+                         , gdouble rotate
                          , gdouble opacity
                          , gdouble scale_x
                          , gdouble scale_y
@@ -481,4 +489,6 @@ GapStoryVideoFileRef * p_new_GapStoryVideoFileRef(const char *videofile
 GapStoryVideoFileRef * gap_story_get_video_file_ref_list(GapStoryBoard *stb);
 char *                 gap_story_build_basename(const char *filename);
 
+void                   gap_story_transform_rotate_layer(gint32 image_id, gint32 layer_id, gdouble rotate);
+
 #endif
diff --git a/gap/gap_story_main.h b/gap/gap_story_main.h
index a59a528..ee3a554 100644
--- a/gap/gap_story_main.h
+++ b/gap/gap_story_main.h
@@ -65,8 +65,8 @@ typedef gpointer t_GVA_Handle;
 typedef enum
   {
      GAP_STB_EDMO_SEQUENCE_NUMBER
-    ,GAP_STB_EDMO_FRAME_NUMBER        
-    ,GAP_STB_EDMO_TIMECODE        
+    ,GAP_STB_EDMO_FRAME_NUMBER
+    ,GAP_STB_EDMO_TIMECODE
   } GapStoryElemDisplayMode;
 
 typedef enum
@@ -85,10 +85,10 @@ typedef enum
 typedef enum
   {
      GAP_VTHUMB_PREFETCH_NOT_ACTIVE
-    ,GAP_VTHUMB_PREFETCH_IN_PROGRESS        
-    ,GAP_VTHUMB_PREFETCH_RESTART_REQUEST        
-    ,GAP_VTHUMB_PREFETCH_CANCEL_REQUEST        
-    ,GAP_VTHUMB_PREFETCH_PENDING        
+    ,GAP_VTHUMB_PREFETCH_IN_PROGRESS
+    ,GAP_VTHUMB_PREFETCH_RESTART_REQUEST
+    ,GAP_VTHUMB_PREFETCH_CANCEL_REQUEST
+    ,GAP_VTHUMB_PREFETCH_PENDING
   } GapVThumbPrefetchProgressMode;
 
 /* video list element describes base resource that has
@@ -96,7 +96,7 @@ typedef enum
  * per resource.
  *
  * currently supported video resource types are defined via GapStoryVthumbEnum.
- * 
+ *
  * The storyboard dialog has a global video thumbnail list that keeps
  * all non-persistent thumbnails in memory, connected to the video list elem
  * by theis video_id.
@@ -113,18 +113,18 @@ typedef struct GapStoryVTResurceElem {
   gchar *video_filename;   /* filename of the video or anim image,
                             * or section_name
                             */
-  gint32 seltrack;         /* not relevant for 
-                            * GAP_STB_VLIST_SECTION and 
+  gint32 seltrack;         /* not relevant for
+                            * GAP_STB_VLIST_SECTION and
                             * GAP_STB_VLIST_ANIM_IMAGE
                             */
   gint32 video_id;         /* uniqie video resource id */
   gint32 total_frames;     /* total frames of the video resource */
 
-  
+
   void *next;
 }  GapStoryVTResurceElem;
 
-/* video thumbnail elemnt is used in the storyboard dialog 
+/* video thumbnail elemnt is used in the storyboard dialog
  * (and clip properties dialog)
  */
 typedef struct GapVThumbElem {
@@ -134,7 +134,7 @@ typedef struct GapVThumbElem {
   gint32 th_bpp;
   gint32 framenr;
   gint32 video_id;
-  
+
   void  *next;
 }  GapVThumbElem;
 
@@ -153,7 +153,7 @@ typedef struct GapStbPropWidget  /* nickname: pw */
   gint32   go_timertag;
   gboolean scene_detection_busy;
   gboolean close_flag;
-  
+
   gdouble     delace_threshold;
   gint32      delace_mode;
   gint32      flip_request;
@@ -174,6 +174,10 @@ typedef struct GapStbPropWidget  /* nickname: pw */
   GtkWidget  *comment_entry;
   GtkWidget  *pw_fmac_filesel;
   GtkWidget  *fmac_entry;
+  GtkWidget  *pw_colormask_file_filesel;
+  GtkWidget  *colormask_file_entry;
+  GtkWidget  *colormask_file_filesel_button;
+  GtkWidget  *colormask_file_label;
   GtkObject  *pw_spinbutton_from_adj;
   GtkObject  *pw_spinbutton_to_adj;
   GtkObject  *pw_spinbutton_loops_adj;
@@ -191,14 +195,14 @@ typedef struct GapStbPropWidget  /* nickname: pw */
   GtkWidget  *pw_mask_definition_name_label;
   gboolean   is_mask_definition;
   GtkWidget  *pw_mask_name_entry;      /* relevant to enter mask definition */
-  GtkWidget  *mask_name_combo;         /* selected mask_name reference */ 
+  GtkWidget  *mask_name_combo;         /* selected mask_name reference */
   gint32 mask_name_combo_elem_count;
   GtkWidget  *mask_anchor_label;
   GtkWidget  *pingpong_label;
   GtkWidget  *pw_mask_enable_toggle;
-  GtkWidget  *pw_mask_anchor_radio_button_arr[2];
+  GtkWidget  *pw_mask_anchor_radio_button_arr[3];
   GtkObject  *pw_spinbutton_mask_stepsize_adj;
-  
+
   /* for filermacro handling */
   GtkObject  *pw_spinbutton_fmac_accel_adj;
   GtkWidget  *pw_spinbutton_fmac_accel;
@@ -207,7 +211,7 @@ typedef struct GapStbPropWidget  /* nickname: pw */
   GtkWidget  *pw_label_alternate_fmac2;
   GtkWidget  *pw_label_alternate_fmac_file;
   GtkWidget  *pw_hbox_fmac2;
-  
+
 
   struct GapStbPropWidget *next;
 } GapStbPropWidget;
@@ -220,7 +224,7 @@ typedef struct GapStbAttrLayerInfo  /* nickname: linfo */
   gint32                 layer_local_framenr;
   gint32                 layer_seltrack;
   gchar                 *layer_filename;
-  
+
 } GapStbAttrLayerInfo;
 
 
@@ -231,7 +235,7 @@ typedef struct GapStbSecpropWidget  /* nickname: spw */
   void  *sgpp;               /* never g_free this one ! */
   void  *tabw;               /* never g_free this one ! (pointer to parent GapStbTabWidgets) */
 
-  
+
   GapPView   *typ_icon_pv_ptr;   /* for display of the section type */
   GtkWidget  *spw_prop_dialog;
   GtkWidget  *spw_section_name_entry;
@@ -252,13 +256,13 @@ typedef struct GapStbAttGfx
 
   gint32      orig_layer_id;   /* invisible frame at original image size */
   gint32      opre_layer_id;   /* invisible prefetch frame at original image size */
-  
+
   gint32      deco_layer_id;   /* decor layer (always on top of stack) */
   gint32      curr_layer_id;   /* copy of orig_layer_id, after transformations */
   gint32      pref_layer_id;   /* copy of opre_layer_id, after transformations (visible if overlapping is active) */
   gint32      base_layer_id;   /* decor layer (always BG) */
   gboolean    auto_update;
-  
+
   /* information about the orig layer */
   GapStbAttrLayerInfo  orig_info;
   GapStbAttrLayerInfo  opre_info;
@@ -302,14 +306,14 @@ typedef struct GapStbAttrWidget  /* nickname: attw */
   gint32   go_timertag;
   gboolean timer_full_update_request;
   gboolean close_flag;
-  
+
   GtkWidget  *attw_prop_dialog;
   GtkWidget  *master_table;
-  
+
   GtkWidget  *fit_width_toggle;
   GtkWidget  *fit_height_toggle;
   GtkWidget  *keep_proportions_toggle;
-  
+
   GapStbAttRow  att_rows[GAP_STB_ATT_TYPES_ARRAY_MAX];
   GapStbAttGfx  gfx_tab[GAP_STB_ATT_GFX_ARRAY_MAX];   /* 0 .. from, 1 .. to */
 
@@ -339,7 +343,7 @@ typedef struct GapStbFrameWidget  /* nickname: fw */
 
   gint32    seq_nr;          /* position in the storyboard list (starting at 1) */
   char      *frame_filename;
-  
+
   void  *sgpp;               /* never g_free this one ! */
   void  *tabw;               /* never g_free this one ! (pointer to parent GapStbTabWidgets) */
 } GapStbFrameWidget;
@@ -401,7 +405,7 @@ typedef struct GapStbTabWidgets  /* nickname: tabw */
   GapStbAttrWidget       *attw;
   GapStoryElemDisplayMode edmode;
 
-  GapStoryUndoElem  *undo_stack_list;     
+  GapStoryUndoElem  *undo_stack_list;
   GapStoryUndoElem  *undo_stack_ptr;
   gdouble            undo_stack_group_counter;
 
@@ -428,7 +432,7 @@ typedef struct GapStbMainGlobalParams  /* nickname: sgpp */
   /* GUI widget pointers */
   GapStbTabWidgets  *stb_widgets;
   GapStbTabWidgets  *cll_widgets;
-  
+
   GapStoryVTResurceElem *video_list;
   GapVThumbElem     *vthumb_list;
   t_GVA_Handle      *gvahand;
@@ -460,8 +464,8 @@ typedef struct GapStbMainGlobalParams  /* nickname: sgpp */
   gint32                  stb_thumbsize;
   /* end layout values */
 
-  GtkWidget *shell_window;  
-  GtkWidget *player_frame;  
+  GtkWidget *shell_window;
+  GtkWidget *player_frame;
 
   GtkWidget *menu_item_win_vthumbs;
 
@@ -490,7 +494,7 @@ typedef struct GapStbMainGlobalParams  /* nickname: sgpp */
   GtkWidget *menu_item_cll_undo;
   GtkWidget *menu_item_cll_redo;
   GtkWidget *menu_item_cll_close;
-  
+
   GdkPixbuf *dnd_pixbuf;
 } GapStbMainGlobalParams;
 
diff --git a/gap/gap_story_properties.c b/gap/gap_story_properties.c
index 0d032e1..0a2a5ae 100644
--- a/gap/gap_story_properties.c
+++ b/gap/gap_story_properties.c
@@ -149,6 +149,7 @@ static void     p_pw_mask_name_reference_update(GapStbPropWidget *pw, const char
 static void     p_pw_mask_name_entry_update_cb(GtkWidget *widget, GapStbPropWidget *pw);
 static void     p_pw_nullable_widget_set_sensitive(GtkWidget *widget, gboolean sensitive, gboolean show);
 static void     p_pw_nullable_gimp_scale_entry_set_sensitive(GtkObject *adj, gboolean sensitive, gboolean show);
+static void     p_pw_set_colormask_param_sensitivity (GapStbPropWidget *pw);
 static void     p_pw_set_mask_name_dependent_widget_sensitivity(GapStbPropWidget *pw, gint active_index);
 static void     p_pw_set_strings_for_mask_name_combo(GapStbPropWidget *pw);
 static void     p_pw_mask_name_combo_changed_cb( GtkWidget *widget, GapStbPropWidget *pw);
@@ -157,10 +158,14 @@ static void     p_pw_filename_changed(const char *filename, GapStbPropWidget *pw
 static void     p_filesel_pw_ok_cb (GtkWidget *widget, GapStbPropWidget *pw);
 static void     p_filesel_pw_close_cb ( GtkWidget *widget, GapStbPropWidget *pw);
 static void     p_pw_filesel_button_cb ( GtkWidget *w, GapStbPropWidget *pw);
+static void     p_pw_colormask_file_filesel_pw_ok_cb (GtkWidget *widget, GapStbPropWidget *pw);
+static void     p_pw_colormask_file_filesel_pw_close_cb ( GtkWidget *widget, GapStbPropWidget *pw);
+static void     p_pw_colormask_file_filesel_button_cb ( GtkWidget *w, GapStbPropWidget *pw);
 static void     p_pw_fmac_filesel_pw_ok_cb (GtkWidget *widget, GapStbPropWidget *pw);
 static void     p_pw_fmac_filesel_pw_close_cb ( GtkWidget *widget, GapStbPropWidget *pw);
 static void     p_pw_fmac_filesel_button_cb ( GtkWidget *w, GapStbPropWidget *pw);
 static void     p_pw_comment_entry_update_cb(GtkWidget *widget, GapStbPropWidget *pw);
+static void     p_pw_colormask_file_entry_update_cb(GtkWidget *widget, GapStbPropWidget *pw);
 static void     p_pw_fmac_entry_update_cb(GtkWidget *widget, GapStbPropWidget *pw);
 static void     p_pw_update_info_labels_and_cliptype_senstivity(GapStbPropWidget *pw);
 static void     p_pw_update_framenr_labels(GapStbPropWidget *pw, gint32 framenr);
@@ -306,7 +311,7 @@ p_pw_prop_reset_all(GapStbPropWidget *pw)
                                    , TRUE);
     }
     
-    idx = CLAMP((gint32)pw->stb_elem_refptr->mask_anchor, 0, (2 -1));
+    idx = CLAMP((gint32)pw->stb_elem_refptr->mask_anchor, 0, (3 -1));
     if(pw->pw_mask_anchor_radio_button_arr[idx])
     {
       gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (pw->pw_mask_anchor_radio_button_arr[idx])
@@ -706,7 +711,7 @@ p_pw_auto_scene_split(GapStbPropWidget *pw, gboolean all_scenes)
   &&  (sgpp->auto_vthumb == FALSE))
   {
     g_message(_("Scene detection depends on video thumbnails. "
-                "Please enable videothumbnails (in the Windows Menu)"));
+                "Please enable video thumbnails (in the Windows Menu)"));
     return;
   }
 
@@ -1282,7 +1287,7 @@ p_pw_timer_go_job(GapStbPropWidget *pw)
              * from_frame number at this point. (dont know exactly why)
              * the simple workaround is just render twice
              * and now it seems to work fine.
-             * (rendering twice should not be too slow, since the requested videothumbnails
+             * (rendering twice should not be too slow, since the requested video thumbnails
              *  are now cached in memory)
              */
             p_pv_pview_render_immediate(pw, pw->stb_elem_refptr, pw->pv_ptr);
@@ -1334,7 +1339,7 @@ p_pv_pview_render (GapStbPropWidget *pw)
    }
    pw->go_job_framenr = pw->stb_elem_refptr->from_frame;
 
-   /* videothumb is rendered deferred (16 millisec) via timer */
+   /* video thumb is rendered deferred (16 millisec) via timer */
    if(pw->go_timertag < 0)
    {
      pw->go_timertag = (gint32) g_timeout_add(16, (GtkFunction)p_pw_timer_go_job, pw);
@@ -1995,6 +2000,25 @@ p_pw_nullable_gimp_scale_entry_set_sensitive(GtkObject *adj, gboolean sensitive,
 }
 
 
+/* ------------------------------------
+ * p_pw_set_colormask_param_sensitivity
+ * ------------------------------------
+ */
+static void
+p_pw_set_colormask_param_sensitivity (GapStbPropWidget *pw)
+{
+  gint active_index;
+
+  if(pw == NULL) { return; }
+  if(pw->mask_name_combo == NULL) {return;}
+  if(pw->stb_elem_refptr == NULL) {return;}
+
+  active_index = gtk_combo_box_get_active(GTK_COMBO_BOX (pw->mask_name_combo));
+  p_pw_set_mask_name_dependent_widget_sensitivity(pw, active_index);
+
+
+}  /* end p_pw_set_colormask_param_sensitivity */
+
 /* ------------------------------------------------
  * p_pw_set_mask_name_dependent_widget_sensitivity
  * ------------------------------------------------
@@ -2017,13 +2041,22 @@ p_pw_set_mask_name_dependent_widget_sensitivity(GapStbPropWidget *pw, gint activ
   p_pw_nullable_widget_set_sensitive(pw->pw_mask_enable_toggle, l_sensitive, l_show);
   p_pw_nullable_widget_set_sensitive(pw->pw_mask_anchor_radio_button_arr[0], l_sensitive, l_show);
   p_pw_nullable_widget_set_sensitive(pw->pw_mask_anchor_radio_button_arr[1], l_sensitive, l_show);
-  
+  p_pw_nullable_widget_set_sensitive(pw->pw_mask_anchor_radio_button_arr[2], l_sensitive, l_show);
+ 
   p_pw_nullable_gimp_scale_entry_set_sensitive(pw->pw_spinbutton_mask_stepsize_adj
       , l_sensitive
       , l_show
       );
-  
-}  /* end p_pw_set_mask_name_dependent_widget_sensitivity */
+ 
+  if(pw->mask_anchor != GAP_MSK_ANCHOR_XCOLOR)
+  {
+    l_sensitive = FALSE;
+  } 
+
+  p_pw_nullable_widget_set_sensitive(pw->colormask_file_label, l_sensitive, l_show);
+  p_pw_nullable_widget_set_sensitive(pw->colormask_file_entry, l_sensitive, l_show);
+  p_pw_nullable_widget_set_sensitive(pw->colormask_file_filesel_button, l_sensitive, l_show);
+ }  /* end p_pw_set_mask_name_dependent_widget_sensitivity */
 
 /* ------------------------------------
  * p_pw_set_strings_for_mask_name_combo
@@ -2380,6 +2413,105 @@ p_pw_filesel_button_cb ( GtkWidget *w
 
 
 /* ==================================================== END FILESEL stuff ======  */
+/* ==================================================== START COLORMASK FILESEL stuff ======  */
+/* ------------------------------------
+ * p_pw_colormask_file_filesel_pw_ok_cb
+ * -----------------------------------
+ */
+static void
+p_pw_colormask_file_filesel_pw_ok_cb (GtkWidget *widget
+                   ,GapStbPropWidget *pw)
+{
+  const gchar *colormask_file_name;
+  gchar *dup_colormask_file_name;
+
+  if(pw == NULL) return;
+  if(pw->pw_colormask_file_filesel == NULL) return;
+
+  dup_colormask_file_name = NULL;
+  colormask_file_name = gtk_file_selection_get_filename (GTK_FILE_SELECTION (pw->pw_colormask_file_filesel));
+  if(colormask_file_name)
+  {
+    dup_colormask_file_name = g_strdup(colormask_file_name);
+  }
+
+  gtk_widget_destroy(GTK_WIDGET(pw->pw_colormask_file_filesel));
+
+  if(dup_colormask_file_name)
+  {
+    gtk_entry_set_text(GTK_ENTRY(pw->colormask_file_entry), dup_colormask_file_name);
+    g_free(dup_colormask_file_name);
+  }
+
+  pw->pw_colormask_file_filesel = NULL;
+}  /* end p_pw_colormask_file_filesel_pw_ok_cb */
+
+
+/* ---------------------------------------
+ * p_pw_colormask_file_filesel_pw_close_cb
+ * ---------------------------------------
+ */
+static void
+p_pw_colormask_file_filesel_pw_close_cb ( GtkWidget *widget
+                      , GapStbPropWidget *pw)
+{
+  if(pw->pw_colormask_file_filesel == NULL) return;
+
+  gtk_widget_destroy(GTK_WIDGET(pw->pw_colormask_file_filesel));
+  pw->pw_colormask_file_filesel = NULL;   /* indicate that filesel is closed */
+
+}  /* end p_pw_colormask_file_filesel_pw_close_cb */
+
+
+
+/* -------------------------------------
+ * p_pw_colormask_file_filesel_button_cb
+ * -------------------------------------
+ */
+static void
+p_pw_colormask_file_filesel_button_cb ( GtkWidget *w
+                       , GapStbPropWidget *pw)
+{
+  GtkWidget *filesel = NULL;
+
+  if(pw->scene_detection_busy)
+  {
+     return;
+  }
+
+  if(pw->pw_colormask_file_filesel != NULL)
+  {
+     gtk_window_present(GTK_WINDOW(pw->pw_colormask_file_filesel));
+     return;   /* filesel is already open */
+  }
+  if(pw->stb_elem_refptr == NULL) { return; }
+
+  filesel = gtk_file_selection_new ( _("Set Colormark Parmeter Filename"));
+  pw->pw_colormask_file_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_pw_colormask_file_filesel_pw_ok_cb),
+                    pw);
+  g_signal_connect (GTK_FILE_SELECTION (filesel)->cancel_button,
+                    "clicked", G_CALLBACK (p_pw_colormask_file_filesel_pw_close_cb),
+                    pw);
+  g_signal_connect (filesel, "destroy",
+                    G_CALLBACK (p_pw_colormask_file_filesel_pw_close_cb),
+                    pw);
+
+
+  if(pw->stb_elem_refptr->colormask_file)
+  {
+    gtk_file_selection_set_filename (GTK_FILE_SELECTION (filesel),
+                                     pw->stb_elem_refptr->colormask_file);
+  }
+  gtk_widget_show (filesel);
+
+}  /* end p_pw_colormask_file_filesel_button_cb */
+
+
+/* ==================================================== END COLORMASK FILESEL stuff ======  */
 
 /* ==================================================== START FMAC FILESEL stuff ======  */
 /* --------------------------------
@@ -2517,6 +2649,29 @@ p_pw_comment_entry_update_cb(GtkWidget *widget, GapStbPropWidget *pw)
   }
 }  /* end p_pw_comment_entry_update_cb */
 
+/* -----------------------------------
+ * p_pw_colormask_file_entry_update_cb
+ * -----------------------------------
+ */
+static void
+p_pw_colormask_file_entry_update_cb(GtkWidget *widget, GapStbPropWidget *pw)
+{
+  if(pw == NULL) { return; }
+  if(pw->stb_elem_refptr == NULL) { return; }
+  if(pw->stb_refptr)
+  {
+    p_pw_push_undo_and_set_unsaved_changes(pw);
+  }
+
+  if(pw->stb_elem_refptr->colormask_file)
+  {
+    g_free(pw->stb_elem_refptr->colormask_file);
+  }
+
+  pw->stb_elem_refptr->colormask_file = g_strdup(gtk_entry_get_text(GTK_ENTRY(widget)));
+
+}  /* end p_pw_colormask_file_entry_update_cb */
+
 
 /* ----------------------------
  * p_pw_fmac_entry_update_cb
@@ -2825,6 +2980,7 @@ p_radio_mask_anchor_clip_callback(GtkWidget *widget, GapStbPropWidget *pw)
     {
       p_pw_push_undo_and_set_unsaved_changes(pw);
       pw->stb_elem_refptr->mask_anchor = pw->mask_anchor;
+      p_pw_set_colormask_param_sensitivity(pw);
     }
   }
 }  /* end p_radio_mask_anchor_clip_callback */
@@ -2844,10 +3000,30 @@ p_radio_mask_anchor_master_callback(GtkWidget *widget, GapStbPropWidget *pw)
     {
       p_pw_push_undo_and_set_unsaved_changes(pw);
       pw->stb_elem_refptr->mask_anchor = pw->mask_anchor;
+      p_pw_set_colormask_param_sensitivity(pw);
     }
   }
 }  /* end p_radio_mask_anchor_master_callback */
 
+/* -----------------------------------
+ * p_radio_mask_anchor_xcolor_callback
+ * -----------------------------------
+ */
+static void
+p_radio_mask_anchor_xcolor_callback(GtkWidget *widget, GapStbPropWidget *pw)
+{
+  if((pw) && (GTK_TOGGLE_BUTTON (widget)->active))
+  {
+    pw->mask_anchor = GAP_MSK_ANCHOR_XCOLOR;
+    if(pw->stb_elem_refptr)
+    {
+      p_pw_push_undo_and_set_unsaved_changes(pw);
+      pw->stb_elem_refptr->mask_anchor = pw->mask_anchor;
+      p_pw_set_colormask_param_sensitivity(pw);
+    }
+  }
+}  /* end p_radio_mask_anchor_xcolor_callback */
+
 
 /* ---------------------------------
  * p_radio_delace_none_callback
@@ -3150,10 +3326,10 @@ p_radio_flip_both_callback(GtkWidget *widget, GapStbPropWidget *pw)
  *
  * The triggered action is to create (or replace) the mask_name reference
  * of the current property widget.
- * If the droped element was not a mask definition, then implicite
+ * If the dropped element was not a mask definition, then implicitly
  * add it to the mask definitions.
  *
- * NOTE: mask definition property windows can NOT act as drop destionation.
+ * NOTE: mask definition property windows can NOT act as drop destination.
  */
 static void
 on_clip_elements_dropped_as_mask_ref (GtkWidget        *widget,
@@ -3252,8 +3428,8 @@ on_clip_elements_dropped_as_mask_ref (GtkWidget        *widget,
             GapStoryElem *stb_elem_dup;
             
             mask_name_new = gap_story_generate_unique_maskname(pw->stb_refptr);
-            /* implicite create a new mask definition from the dropped
-             * clip and change properties of current pw to refere
+            /* implicitly create a new mask definition from the dropped
+             * clip and change properties of current pw to refer
              * to the newly created mask definition
              */
             stb_elem_dup = gap_story_elem_duplicate(fw_drop->stb_elem_refptr);
@@ -3303,7 +3479,7 @@ on_clip_elements_dropped_as_mask_ref (GtkWidget        *widget,
  * accept dropping of application internal storyboard elements
  * to add (or replace) the current mask_name (only for mask references)
  *
- * if the dropped element is NOT a mask definition, then implicite
+ * if the dropped element is NOT a mask definition, then implicitly
  * add it to the mask definitions.
  */
 static void
@@ -3345,13 +3521,13 @@ p_pw_dialog_init_dnd(GapStbPropWidget *pw)
  * -------------------------------
  * - set sensitivity for the filtermacro steps spinbutton.
  *   It is sensitive if both filtermacro_file and an 
- *   implicite alternative filtermacro_file (MACRO2) are existent and valid.
+ *   implicit alternative filtermacro_file (MACRO2) are existent and valid.
  *   In this case filter apply with varying values can be used
  *   by entering a value > 1 into the filtermacro steps spinbutton.
  *
  * - The presence of the alternative filtermacro_file is also
  *   displayed in the label pw_label_alternate_fmac_file
- *   via shortened version of the alternat filtermacro filename.
+ *   via shortened version of the alternate filtermacro filename.
  * - This label is set to space if no valid alternate filtermacro_file
  *   is available. This label also indicates if MACRO2 is active
  *   or not active by adding Suffix "(ON)" or "(OFF)"
@@ -3680,6 +3856,10 @@ p_pw_clear_widgets(GapStbPropWidget *pw)
   pw->comment_entry = NULL;
   pw->pw_fmac_filesel = NULL;
   pw->fmac_entry = NULL;
+  pw->pw_colormask_file_filesel = NULL;
+  pw->colormask_file_filesel_button = NULL;
+  pw->colormask_file_entry = NULL;
+  pw->colormask_file_label = NULL;
   pw->pw_spinbutton_from_adj = NULL;
   pw->pw_spinbutton_to_adj = NULL;
   pw->pw_spinbutton_loops_adj = NULL;
@@ -3699,6 +3879,8 @@ p_pw_clear_widgets(GapStbPropWidget *pw)
   pw->pw_mask_enable_toggle = NULL;
   pw->pw_mask_anchor_radio_button_arr[0] = NULL;
   pw->pw_mask_anchor_radio_button_arr[1] = NULL;
+  pw->pw_mask_anchor_radio_button_arr[2] = NULL;
+  
   pw->pw_spinbutton_mask_stepsize_adj = NULL;
   
   pw->pw_spinbutton_fmac_steps = NULL;
@@ -4499,7 +4681,7 @@ gap_story_pw_properties_dialog (GapStbPropWidget *pw)
     radio_table = gtk_table_new (1, 2, FALSE);
 
     l_idx = 0;
-    /* radio button mask_anchor None */
+    /* radio button mask_anchor Clip */
     radio_button = gtk_radio_button_new_with_label ( radio_group, _("Clip") );
     radio_group = gtk_radio_button_get_group ( GTK_RADIO_BUTTON (radio_button) );
     gtk_table_attach ( GTK_TABLE (radio_table), radio_button, l_idx, l_idx+1
@@ -4517,7 +4699,7 @@ gap_story_pw_properties_dialog (GapStbPropWidget *pw)
                        pw);
 
     l_idx++;
-    /* radio button mask_anchor odd */
+    /* radio button mask_anchor Master */
     radio_button = gtk_radio_button_new_with_label ( radio_group, _("Master") );
     radio_group = gtk_radio_button_get_group ( GTK_RADIO_BUTTON (radio_button) );
     gtk_table_attach ( GTK_TABLE (radio_table), radio_button, l_idx, l_idx+1
@@ -4534,12 +4716,82 @@ gap_story_pw_properties_dialog (GapStbPropWidget *pw)
                        G_CALLBACK (p_radio_mask_anchor_master_callback),
                        pw);
 
+    l_idx++;
+    /* radio button mask_anchor ColormaskClip */
+    radio_button = gtk_radio_button_new_with_label ( radio_group, _("ClipColormask") );
+    radio_group = gtk_radio_button_get_group ( GTK_RADIO_BUTTON (radio_button) );
+    gtk_table_attach ( GTK_TABLE (radio_table), radio_button, l_idx, l_idx+1
+                     , 0, 1, 0, 0, 0, 0);
+
+    pw->pw_mask_anchor_radio_button_arr[2] = radio_button;
+    l_radio_pressed = (pw->mask_anchor == GAP_MSK_ANCHOR_XCOLOR);
+    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (radio_button),
+                                     l_radio_pressed);
+    gimp_help_set_help_data(radio_button, _("Apply as colormask to clip at clip position in clip size"), NULL);
+
+    gtk_widget_show (radio_button);
+    g_signal_connect ( G_OBJECT (radio_button), "toggled",
+                       G_CALLBACK (p_radio_mask_anchor_xcolor_callback),
+                       pw);
+
+
 
     gtk_widget_show (radio_table);
     gtk_table_attach_defaults (GTK_TABLE(table_ptr), radio_table, 1, 2, row, row+1);
   }
 
 
+  row++;
+
+  /* the colormask parameter file label */
+  label = gtk_label_new (_("Mask Params:"));
+  pw->colormask_file_label = label;
+  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);
+
+
+
+  /* colormask parameter file entry and filesel invoker button */
+  {
+    GtkWidget *hbox_colormask_file;
+  
+    hbox_colormask_file = gtk_hbox_new (FALSE, 1);
+    gtk_widget_show (hbox_colormask_file);
+    gtk_table_attach_defaults (GTK_TABLE(table), hbox_colormask_file, 1, 2, row, row+1);
+
+    /* the filtermacro entry */
+    entry = gtk_entry_new ();
+    gtk_widget_set_size_request(entry, PW_ENTRY_WIDTH, -1);
+    if(pw->stb_elem_refptr)
+    {
+      if(pw->stb_elem_refptr->colormask_file)
+      {
+        gtk_entry_set_text(GTK_ENTRY(entry), pw->stb_elem_refptr->colormask_file);
+      }
+    }
+
+    gtk_box_pack_start (GTK_BOX (hbox_colormask_file), entry, TRUE, TRUE, 1);
+
+    g_signal_connect(G_OBJECT(entry), "changed",
+                     G_CALLBACK(p_pw_colormask_file_entry_update_cb),
+                     pw);
+    gtk_widget_show (entry);
+    gimp_help_set_help_data (entry, _("parameter file for the colormask filter")
+                            , NULL);
+    pw->colormask_file_entry = entry;
+
+
+    /* the filesel invoker button */
+    button = gtk_button_new_with_label ("...");
+    pw->colormask_file_filesel_button = button;
+    gtk_box_pack_start (GTK_BOX (hbox_colormask_file), button, FALSE, FALSE, 1);
+    g_signal_connect(G_OBJECT(button), "clicked",
+                     G_CALLBACK(p_pw_colormask_file_filesel_button_cb),
+                     pw);
+    gtk_widget_show (button);
+  }
+
   if(!pw->is_mask_definition)
   {
     row++;
@@ -4622,7 +4874,7 @@ gap_story_pw_properties_dialog (GapStbPropWidget *pw)
     gtk_widget_show (entry);
     gimp_help_set_help_data (entry, _("filter macro to be performed when frames "
                                     "of this clips are rendered. "
-                                    "A 2nd macrofile is implicite referenced by naming convetion "
+                                    "A 2nd macrofile is implicitly referenced by naming convention "
                                     "via the keyword .VARYING (as suffix or before the extension)")
                                            , NULL);
     pw->fmac_entry = entry;
@@ -4875,7 +5127,7 @@ gap_story_stb_elem_properties_dialog ( GapStbTabWidgets *tabw
   {
     pw->pw_flip_request_radio_button_arr[idx] = NULL;
   }
-  for(idx=0; idx < 2; idx++)
+  for(idx=0; idx < sizeof(pw->pw_mask_anchor_radio_button_arr); idx++)
   {
     pw->pw_mask_anchor_radio_button_arr[idx] = NULL;
   }
diff --git a/gap/gap_story_render_audio.c b/gap/gap_story_render_audio.c
index efad98d..d8220bd 100644
--- a/gap/gap_story_render_audio.c
+++ b/gap/gap_story_render_audio.c
@@ -356,7 +356,7 @@ p_find_min_max_aud_tracknumbers(GapStoryRenderAudioRangeElem *aud_list
 /* ---------------------------------
  * p_get_audio_sample
  * ---------------------------------
- * - scann the aud_list to findout
+ * - scan the aud_list to findout
  *   audiofile that is played at desired track
  *   at desired sample_index position.
  * - return the sample (amplitude) at desired sample_index position
diff --git a/gap/gap_story_render_lossless.c b/gap/gap_story_render_lossless.c
index d999ac5..25f07b8 100644
--- a/gap/gap_story_render_lossless.c
+++ b/gap/gap_story_render_lossless.c
@@ -344,6 +344,7 @@ p_check_chunk_fetch_possible(GapStoryRenderVidHandle *vidhand
   gint32    l_track_max;
   gchar  *l_framename;
   gchar  *l_videofile;
+  gdouble l_rotate;
   gdouble l_opacity;
   gdouble l_scale_x;
   gdouble l_scale_y;
@@ -395,6 +396,7 @@ p_check_chunk_fetch_possible(GapStoryRenderVidHandle *vidhand
                  , &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 */
@@ -425,6 +427,7 @@ p_check_chunk_fetch_possible(GapStoryRenderVidHandle *vidhand
            {
              /* 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)
diff --git a/gap/gap_story_render_processor.c b/gap/gap_story_render_processor.c
index 05b02c0..c7f9989 100644
--- a/gap/gap_story_render_processor.c
+++ b/gap/gap_story_render_processor.c
@@ -88,6 +88,7 @@ typedef struct GapStbFetchData {   /* nick: gfd */
   gint32        layer_id;
 
   gchar  *framename;
+  gdouble rotate;
   gdouble opacity;
   gdouble scale_x;
   gdouble scale_y;
@@ -168,6 +169,7 @@ static char *   p_fetch_framename   (GapStoryRenderFrameRangeElem *frn_list
                             , 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 */
@@ -200,6 +202,7 @@ static GapStoryRenderFrameRangeElem *  p_new_framerange_element(
                           ,gboolean mask_disable
                           ,gint32 fmac_total_steps
                           ,gint32 fmac_accel
+                          ,const char *colormask_file  /* IN: NULL, or name of the colormask parameter file */
                           );
 static void       p_add_frn_list(GapStoryRenderVidHandle *vidhand, GapStoryRenderFrameRangeElem *frn_elem);
 static void       p_step_all_vtrack_attributes(gint32 track, gint32 frames_to_handle
@@ -273,12 +276,14 @@ static gint32     p_mask_fetcher(GapStoryRenderVidHandle *vidhand
                     , gint32 mask_height
                     , gint32 *layer_id               /* OUT: Id of the only layer in the composite image */
                     , gboolean *was_last_maskframe   /* OUT: true if this was the last maskframe */
+                    , gboolean makeGrayFlattened     /* IN   true flatten and convert to GRAY, false keep color and alpha channel */
                     );
 static void       p_fetch_and_add_layermask(GapStoryRenderVidHandle *vidhand
                     , GapStoryRenderFrameRangeElem *frn_elem
                     , gint32 local_stepcount
                     , gint32 image_id
                     , gint32 layer_id
+                    , GapStoryMaskAnchormode mask_anchor
                     );
 
 static GapStoryRenderVidHandle * p_open_video_handle_private(    gboolean ignore_audio
@@ -321,6 +326,7 @@ static gint32     p_transform_and_add_layer( gint32 comp_image_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 */
@@ -384,8 +390,11 @@ static void       p_paste_logo_pattern(gint32 drawable_id
 
 static void       p_do_insert_area_processing(GapStbFetchData *gfd
                       , GapStoryRenderVidHandle *vidhand);
+static gint32     p_prepare_GRAY_image(gint32 image_id);
+static void       p_do_insert_alpha_processing(GapStbFetchData *gfd
+                      , GapStoryRenderVidHandle *vidhand);
+
 
-  
 static gint32     p_story_render_fetch_composite_image_private(GapStoryRenderVidHandle *vidhand
                     , gint32 master_frame_nr  /* starts at 1 */
                     , gint32  vid_width       /* desired Video Width in pixels */
@@ -530,6 +539,12 @@ gap_story_render_debug_print_frame_elem(GapStoryRenderFrameRangeElem *frn_elem,
       printf("  [%d] opacity_accel        : %d\n", (int)l_idx, (int)frn_elem->opacity_accel );
       printf("  [%d] opacity_frames_done  : %d\n", (int)l_idx, (int)frn_elem->opacity_frames_done );
 
+      printf("  [%d] rotate_from          : %f\n", (int)l_idx, (float)frn_elem->rotate_from );
+      printf("  [%d] rotate_to            : %f\n", (int)l_idx, (float)frn_elem->rotate_to );
+      printf("  [%d] rotate_dur           : %d\n", (int)l_idx, (int)frn_elem->rotate_dur );
+      printf("  [%d] rotate_accel         : %d\n", (int)l_idx, (int)frn_elem->rotate_accel );
+      printf("  [%d] rotate_frames_done   : %d\n", (int)l_idx, (int)frn_elem->rotate_frames_done );
+
       printf("  [%d] scale_x_from         : %f\n", (int)l_idx, (float)frn_elem->scale_x_from );
       printf("  [%d] scale_x_to           : %f\n", (int)l_idx, (float)frn_elem->scale_x_to );
       printf("  [%d] scale_x_dur          : %d\n", (int)l_idx, frn_elem->scale_x_dur );
@@ -957,10 +972,10 @@ p_find_min_max_vid_tracknumbers(GapStoryRenderFrameRangeElem *frn_list
 /* --------------------------------
  * p_attribute_query_at_step
  * --------------------------------
- * return 
- *  o) from_val (before and at start of transition) or 
+ * return
+ *  o) from_val (before and at start of transition) or
  *  o) to_val (at end and after transition) or
- *  o) a mixed value according to frame_step progress and acceleratin characteristics 
+ *  o) a mixed value according to frame_step progress and acceleratin characteristics
  *     when frame_step is a frame number within the transition.
  */
 static gdouble
@@ -975,15 +990,15 @@ p_attribute_query_at_step(gint32 frame_step /* current frame (since start of cur
   gdouble l_mixed_val;
   gdouble l_mixed_factor;
   gint32  l_steps_since_transition_start;
-  
+
 
   l_steps_since_transition_start = frames_done + frame_step;
-  
+
   if (l_steps_since_transition_start <= 0)
   {
     return (from_val);
   }
-  
+
   if (l_steps_since_transition_start >= frames_dur)
   {
     return (to_val);
@@ -1098,6 +1113,7 @@ p_fetch_framename(GapStoryRenderFrameRangeElem *frn_list
                  , 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 */
@@ -1118,6 +1134,7 @@ 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;
@@ -1160,11 +1177,11 @@ p_fetch_framename(GapStoryRenderFrameRangeElem *frn_list
 
         {
           gint32 fnrInt;
-          
+
           fnrInt = fnr;  /* truncate to integer */
-          
+
           *localframe_tween_rest = fnr - fnrInt;
-          
+
           if(gap_debug)
           {
             printf("fnr:%.4f, fnrInt:%d localframe_tween_rest:%.4f\n"
@@ -1232,6 +1249,13 @@ p_fetch_framename(GapStoryRenderFrameRangeElem *frn_list
          l_step = (master_frame_nr - 1) - l_frame_group_count;
 
 
+         *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
                                     , frn_elem->opacity_from
                                     , frn_elem->opacity_to
@@ -1276,11 +1300,12 @@ 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 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\n"
        , (int)track
        , (int)master_frame_nr
        , l_framename
        , (int)l_found_at_idx
+       , (float)*rotate
        , (float)*opacity
        , (float)*scale_x
        , (float)*scale_y
@@ -1354,6 +1379,7 @@ p_new_framerange_element(GapStoryRenderFrameType  frn_type
                       ,gboolean mask_disable
                       ,gint32 fmac_total_steps
                       ,gint32 fmac_accel
+                      ,const char *colormask_file      /* IN: NULL, or name of the colormask parameter file */
                       )
 {
   GapStoryRenderFrameRangeElem *frn_elem;
@@ -1369,6 +1395,7 @@ p_new_framerange_element(GapStoryRenderFrameType  frn_type
      if(ext)               printf("  ext:%s:\n", ext);
      if(storyboard_file)   printf("  storyboard_file:%s:\n", storyboard_file);
      if(preferred_decoder) printf("  preferred_decoder:%s:\n", preferred_decoder);
+     if(colormask_file)    printf("  colormask_file:%s:\n", colormask_file);
   }
 
 
@@ -1391,14 +1418,31 @@ p_new_framerange_element(GapStoryRenderFrameType  frn_type
    * by using a mask_name == NULL
    */
   frn_elem->mask_name     = NULL;
+  frn_elem->colormask_file     = NULL;
   if(!mask_disable)
   {
     if(mask_name)
     {
       frn_elem->mask_name = g_strdup(mask_name);
     }
+    if(colormask_file)
+    {
+      frn_elem->colormask_file = gap_file_make_abspath_filename(colormask_file, storyboard_file);
+
+      if(!g_file_test(frn_elem->colormask_file, G_FILE_TEST_EXISTS))
+      {
+         char *l_errtxt;
+
+         l_errtxt = g_strdup_printf("colormask_file not found:  %s", frn_elem->colormask_file);
+         p_set_stb_error(sterr, l_errtxt);
+         g_free(l_errtxt);
+         g_free(frn_elem->colormask_file);
+         frn_elem->colormask_file = NULL;
+      }
+    }
   }
 
+
   /* default attributes (used if storyboard does not define settings) */
   frn_elem->red_f              = 0.0;
   frn_elem->green_f            = 0.0;
@@ -1409,6 +1453,9 @@ p_new_framerange_element(GapStoryRenderFrameType  frn_type
   frn_elem->fit_height         = TRUE;
   frn_elem->wait_untiltime_sec = 0.0;
   frn_elem->wait_untilframes   = 0;
+  frn_elem->rotate_from        = 0.0;
+  frn_elem->rotate_to          = 0.0;
+  frn_elem->rotate_dur         = 0;
   frn_elem->opacity_from       = 1.0;
   frn_elem->opacity_to         = 1.0;
   frn_elem->opacity_dur        = 0;
@@ -1578,6 +1625,7 @@ p_step_all_vtrack_attributes(gint32 track
                        , GapStoryRenderVTrackArray *vtarr
                       )
 {
+  vtarr->attr[track].rotate_frames_done += frames_to_handle;
   vtarr->attr[track].opacity_frames_done += frames_to_handle;
   vtarr->attr[track].scale_x_frames_done += frames_to_handle;
   vtarr->attr[track].scale_y_frames_done += frames_to_handle;
@@ -1626,30 +1674,36 @@ p_set_vtrack_attributes(GapStoryRenderFrameRangeElem *frn_elem
   frn_elem->mask_framecount       = vtarr->attr[track].mask_framecount;
 
 
+  frn_elem->rotate_from          = vtarr->attr[track].rotate_from;
+  frn_elem->rotate_to            = vtarr->attr[track].rotate_to;
+  frn_elem->rotate_dur           = vtarr->attr[track].rotate_dur;
+  frn_elem->rotate_accel         = vtarr->attr[track].rotate_accel;
+  frn_elem->rotate_frames_done   = vtarr->attr[track].rotate_frames_done;
+
   frn_elem->opacity_from         = vtarr->attr[track].opacity_from;
   frn_elem->opacity_to           = vtarr->attr[track].opacity_to;
   frn_elem->opacity_dur          = vtarr->attr[track].opacity_dur;
   frn_elem->opacity_accel        = vtarr->attr[track].opacity_accel;
   frn_elem->opacity_frames_done  = vtarr->attr[track].opacity_frames_done;
-  
+
   frn_elem->scale_x_from         = vtarr->attr[track].scale_x_from;
   frn_elem->scale_x_to           = vtarr->attr[track].scale_x_to;
   frn_elem->scale_x_dur          = vtarr->attr[track].scale_x_dur;
   frn_elem->scale_x_accel        = vtarr->attr[track].scale_x_accel;
   frn_elem->scale_x_frames_done  = vtarr->attr[track].scale_x_frames_done;
-  
+
   frn_elem->scale_y_from         = vtarr->attr[track].scale_y_from;
   frn_elem->scale_y_to           = vtarr->attr[track].scale_y_to;
   frn_elem->scale_y_dur          = vtarr->attr[track].scale_y_dur;
   frn_elem->scale_y_accel        = vtarr->attr[track].scale_y_accel;
   frn_elem->scale_y_frames_done  = vtarr->attr[track].scale_y_frames_done;
-  
+
   frn_elem->move_x_from          = vtarr->attr[track].move_x_from;
   frn_elem->move_x_to            = vtarr->attr[track].move_x_to;
   frn_elem->move_x_dur           = vtarr->attr[track].move_x_dur;
   frn_elem->move_x_accel         = vtarr->attr[track].move_x_accel;
   frn_elem->move_x_frames_done   = vtarr->attr[track].move_x_frames_done;
-  
+
   frn_elem->move_y_from          = vtarr->attr[track].move_y_from;
   frn_elem->move_y_to            = vtarr->attr[track].move_y_to;
   frn_elem->move_y_dur           = vtarr->attr[track].move_y_dur;
@@ -1730,6 +1784,7 @@ p_vidclip_shadow_add_silence(gint32 shadow_track
                                      , TRUE                 /* mask_disable */
                                      , 1                    /* fmac_total_steps */
                                      , 0                    /* fmac_accel */
+                                     , NULL                 /* colormask_file */
                                      );
   if(frn_elem)
   {
@@ -1871,30 +1926,36 @@ p_copy_vattr_values(gint32 src_track
   vtarr->attr[dst_track].fit_height        = vtarr->attr[src_track].fit_height;
 
 
+  vtarr->attr[dst_track].rotate_from         = vtarr->attr[src_track].rotate_from;
+  vtarr->attr[dst_track].rotate_to           = vtarr->attr[src_track].rotate_to;
+  vtarr->attr[dst_track].rotate_dur          = vtarr->attr[src_track].rotate_dur;
+  vtarr->attr[dst_track].rotate_accel        = vtarr->attr[src_track].rotate_accel;
+  vtarr->attr[dst_track].rotate_frames_done  = vtarr->attr[src_track].rotate_frames_done;
+
   vtarr->attr[dst_track].opacity_from        = vtarr->attr[src_track].opacity_from;
   vtarr->attr[dst_track].opacity_to          = vtarr->attr[src_track].opacity_to;
   vtarr->attr[dst_track].opacity_dur         = vtarr->attr[src_track].opacity_dur;
   vtarr->attr[dst_track].opacity_accel       = vtarr->attr[src_track].opacity_accel;
   vtarr->attr[dst_track].opacity_frames_done = vtarr->attr[src_track].opacity_frames_done;
-  
+
   vtarr->attr[dst_track].scale_x_from        = vtarr->attr[src_track].scale_x_from;
   vtarr->attr[dst_track].scale_x_to          = vtarr->attr[src_track].scale_x_to;
   vtarr->attr[dst_track].scale_x_dur         = vtarr->attr[src_track].scale_x_dur;
   vtarr->attr[dst_track].scale_x_accel       = vtarr->attr[src_track].scale_x_accel;
   vtarr->attr[dst_track].scale_x_frames_done = vtarr->attr[src_track].scale_x_frames_done;
-  
+
   vtarr->attr[dst_track].scale_y_from        = vtarr->attr[src_track].scale_y_from;
   vtarr->attr[dst_track].scale_y_to          = vtarr->attr[src_track].scale_y_to;
   vtarr->attr[dst_track].scale_y_dur         = vtarr->attr[src_track].scale_y_dur;
   vtarr->attr[dst_track].scale_y_accel       = vtarr->attr[src_track].scale_y_accel;
   vtarr->attr[dst_track].scale_y_frames_done = vtarr->attr[src_track].scale_y_frames_done;
-  
+
   vtarr->attr[dst_track].move_x_from         = vtarr->attr[src_track].move_x_from;
   vtarr->attr[dst_track].move_x_to           = vtarr->attr[src_track].move_x_to;
   vtarr->attr[dst_track].move_x_dur          = vtarr->attr[src_track].move_x_dur;
   vtarr->attr[dst_track].move_x_accel        = vtarr->attr[src_track].move_x_accel;
   vtarr->attr[dst_track].move_x_frames_done  = vtarr->attr[src_track].move_x_frames_done;
-  
+
   vtarr->attr[dst_track].move_y_from         = vtarr->attr[src_track].move_y_from;
   vtarr->attr[dst_track].move_y_to           = vtarr->attr[src_track].move_y_to;
   vtarr->attr[dst_track].move_y_dur          = vtarr->attr[src_track].move_y_dur;
@@ -2017,6 +2078,7 @@ p_vidclip_split_and_add_frn_list(GapStoryRenderFrameRangeElem *frn_elem
                       ,FALSE     /* keep mask enabled if the element has one */
                       ,frn_elem->fmac_total_steps
                       ,frn_elem->fmac_accel
+                      ,frn_elem->colormask_file
                       );
 
 
@@ -2137,31 +2199,37 @@ p_clear_vattr_array(GapStoryRenderVTrackArray *vtarr)
     vtarr->attr[l_idx].keep_proportions     = FALSE;
     vtarr->attr[l_idx].fit_width            = TRUE;
     vtarr->attr[l_idx].fit_height           = TRUE;
-    
+
+    vtarr->attr[l_idx].rotate_from          = 0.0;
+    vtarr->attr[l_idx].rotate_to            = 0.0;
+    vtarr->attr[l_idx].rotate_dur           = 0;
+    vtarr->attr[l_idx].rotate_accel         = 0;
+    vtarr->attr[l_idx].rotate_frames_done   = 0;
+
     vtarr->attr[l_idx].opacity_from         = 1.0;
     vtarr->attr[l_idx].opacity_to           = 1.0;
     vtarr->attr[l_idx].opacity_dur          = 0;
     vtarr->attr[l_idx].opacity_accel        = 0;
     vtarr->attr[l_idx].opacity_frames_done  = 0;
-        
+
     vtarr->attr[l_idx].scale_x_from         = 1.0;
     vtarr->attr[l_idx].scale_x_to           = 1.0;
     vtarr->attr[l_idx].scale_x_dur          = 0;
     vtarr->attr[l_idx].scale_x_accel        = 0;
     vtarr->attr[l_idx].scale_x_frames_done  = 0;
-    
+
     vtarr->attr[l_idx].scale_y_from         = 1.0;
     vtarr->attr[l_idx].scale_y_to           = 1.0;
     vtarr->attr[l_idx].scale_y_dur          = 0;
     vtarr->attr[l_idx].scale_y_accel        = 0;
     vtarr->attr[l_idx].scale_y_frames_done  = 0;
-    
+
     vtarr->attr[l_idx].move_x_from          = 0.0;
     vtarr->attr[l_idx].move_x_to            = 0.0;
     vtarr->attr[l_idx].move_x_dur           = 0;
     vtarr->attr[l_idx].move_x_accel         = 0;
     vtarr->attr[l_idx].move_x_frames_done   = 0;
-    
+
     vtarr->attr[l_idx].move_y_from          = 0.0;
     vtarr->attr[l_idx].move_y_to            = 0.0;
     vtarr->attr[l_idx].move_y_dur           = 0;
@@ -2182,7 +2250,7 @@ p_fmt_string_has_framenumber_format(const char *fmt_string)
 {
   const char *ptr;
   gboolean l_found;
-  
+
   l_found = FALSE;
   ptr = fmt_string;
   while(ptr)
@@ -2191,7 +2259,7 @@ p_fmt_string_has_framenumber_format(const char *fmt_string)
     {
       break;
     }
-    
+
     if (ptr[0] == '%')
     {
       if(ptr[1] != '\0')
@@ -2215,7 +2283,7 @@ p_fmt_string_has_framenumber_format(const char *fmt_string)
     ptr++;
   }
   return (l_found);
-  
+
 }  /* end p_fmt_string_has_framenumber_format */
 
 /* -------------------------------------
@@ -2230,7 +2298,7 @@ p_fmt_string_has_videobasename_format(const char *fmt_string)
 {
   const char *ptr;
   gboolean l_found;
-  
+
   l_found = FALSE;
   ptr = fmt_string;
   while(ptr)
@@ -2239,7 +2307,7 @@ p_fmt_string_has_videobasename_format(const char *fmt_string)
     {
       break;
     }
-    
+
     if ((ptr[0] == '%') && (ptr[1] == 's'))
     {
       l_found = TRUE;
@@ -2248,7 +2316,7 @@ p_fmt_string_has_videobasename_format(const char *fmt_string)
     ptr++;
   }
   return (l_found);
-  
+
 }  /* end p_fmt_string_has_videobasename_format */
 
 /* ----------------------------------------------------
@@ -2310,6 +2378,16 @@ p_storyboard_analyze(GapStoryBoard *stb
     vidhand->preferred_decoder = g_strdup(stb->preferred_decoder);
   }
 
+  vidhand->master_insert_alpha_format = NULL;
+  if(stb->master_insert_alpha_format)
+  {
+    vidhand->master_insert_alpha_format = g_strdup(stb->master_insert_alpha_format);
+    vidhand->master_insert_alpha_format_has_framenumber =
+      p_fmt_string_has_framenumber_format(vidhand->master_insert_alpha_format);
+    vidhand->master_insert_alpha_format_has_videobasename =
+      p_fmt_string_has_videobasename_format(vidhand->master_insert_alpha_format);
+  }
+
   vidhand->master_insert_area_format = NULL;
   if(stb->master_insert_area_format)
   {
@@ -2449,6 +2527,13 @@ p_storyboard_analyze(GapStoryBoard *stb
               {
                 switch(ii)
                 {
+                  case GAP_STB_ATT_TYPE_ROTATE:
+                    vtarr->attr[l_track].rotate_from  = stb_elem->att_arr_value_from[ii];
+                    vtarr->attr[l_track].rotate_to    = stb_elem->att_arr_value_to[ii];
+                    vtarr->attr[l_track].rotate_dur   = stb_elem->att_arr_value_dur[ii];
+                    vtarr->attr[l_track].rotate_accel = stb_elem->att_arr_value_accel[ii];
+                    vtarr->attr[l_track].rotate_frames_done  = 0;
+                    break;
                   case GAP_STB_ATT_TYPE_OPACITY:
                     vtarr->attr[l_track].opacity_from  = stb_elem->att_arr_value_from[ii];
                     vtarr->attr[l_track].opacity_to    = stb_elem->att_arr_value_to[ii];
@@ -2516,6 +2601,7 @@ p_storyboard_analyze(GapStoryBoard *stb
                                                , TRUE                 /* mask_disable */
                                                , 1                    /* fmac_total_steps */
                                                , 0                    /* fmac_accel */
+                                               , NULL                 /* colormask_file */
                                                );
             if(frn_elem)
             {
@@ -2552,6 +2638,7 @@ p_storyboard_analyze(GapStoryBoard *stb
                                                , TRUE                 /* mask_disable */
                                                , 1                    /* fmac_total_steps */
                                                , 0                    /* fmac_accel */
+                                               , NULL                 /* colormask_file */
                                                );
             if(frn_elem)
             {
@@ -2585,6 +2672,7 @@ p_storyboard_analyze(GapStoryBoard *stb
                                                , stb_elem->mask_disable
                                                , stb_elem->fmac_total_steps
                                                , stb_elem->fmac_accel
+                                               , stb_elem->colormask_file
                                                );
             if(frn_elem)
             {
@@ -2623,6 +2711,7 @@ p_storyboard_analyze(GapStoryBoard *stb
                                                , stb_elem->mask_disable
                                                , stb_elem->fmac_total_steps
                                                , stb_elem->fmac_accel
+                                               , stb_elem->colormask_file
                                                );
             if(frn_elem)
             {
@@ -2709,6 +2798,7 @@ p_storyboard_analyze(GapStoryBoard *stb
                                                     , stb_elem->mask_disable
                                                     , stb_elem->fmac_total_steps
                                                     , stb_elem->fmac_accel
+                                                    , stb_elem->colormask_file
                                                     );
                  if(frn_elem)
                  {
@@ -3339,7 +3429,7 @@ p_find_maskdef_by_name(GapStoryRenderVidHandle *vidhand, const char *mask_name)
 /* ----------------------------------------------------
  * p_mask_image_fetcher
  * ----------------------------------------------------
- * fetch specified mask frame as gray image with only one composite layer.
+ * fetch specified mask frame with only one composite layer.
  * if the mask definition is not found,
  * then deliver -1.
  * if the mask definition has less frames than master_frame_nr, then deliver the
@@ -3354,6 +3444,7 @@ p_mask_fetcher(GapStoryRenderVidHandle *vidhand
    , gint32 mask_height
    , gint32 *layer_id_ptr           /* OUT: Id of the only layer in the composite image */
    , gboolean *was_last_maskframe   /* OUT: true if this was the last maskframe */
+   , gboolean makeGrayFlattened     /* IN   true flatten and convert to GRAY, false keep color and alpha channel */
    )
 {
   GapStoryRenderMaskDefElem *maskdef_elem;
@@ -3378,18 +3469,15 @@ p_mask_fetcher(GapStoryRenderVidHandle *vidhand
     l_framenr = MIN(master_frame_nr, maskdef_elem->frame_count);
 
 
-     if(gap_debug)
-     {
+    if(gap_debug)
+    {
        printf("\n############# MASK start FETCH ##########\n");
        printf("MASK relevant framenr:%d\n"
             , (int)l_framenr
             );
        gap_story_render_debug_print_maskdef_elem(maskdef_elem, -7);
-     }
+    }
 
-    /* the composite image fecther already converts to gray when called
-     * with a mask videohandle (marked with is_mask_handle flag)
-     */
     image_id = p_story_render_fetch_composite_image_private(maskdef_elem->mask_vidhand
                   , l_framenr        /* starts at 1 */
                   , mask_width       /* desired  Width in pixels */
@@ -3406,12 +3494,22 @@ p_mask_fetcher(GapStoryRenderVidHandle *vidhand
       printf("\n.........#### MASK end FETCH ####......\n");
     }
 
+    if(makeGrayFlattened == TRUE)
+    {
+      if(gimp_image_base_type(image_id) != GIMP_GRAY)
+      {
+        gimp_image_convert_grayscale(image_id);
+      }
+    }
 
     *layer_id_ptr = gap_layer_flip(*layer_id_ptr, maskdef_elem->flip_request);
 
-    if(gimp_drawable_has_alpha(*layer_id_ptr))
+    if(makeGrayFlattened == TRUE)
     {
-      *layer_id_ptr = gimp_image_flatten(image_id);
+      if(gimp_drawable_has_alpha(*layer_id_ptr))
+      {
+        *layer_id_ptr = gimp_image_flatten(image_id);
+      }
     }
 
     if(gap_debug)
@@ -3443,6 +3541,7 @@ p_fetch_and_add_layermask(GapStoryRenderVidHandle *vidhand
                   , gint32 local_stepcount
                   , gint32 image_id
                   , gint32 layer_id
+                  , GapStoryMaskAnchormode mask_anchor
                   )
 {
   gint32  l_tmp_mask_image_id;
@@ -3451,10 +3550,17 @@ p_fetch_and_add_layermask(GapStoryRenderVidHandle *vidhand
   gdouble l_framenr;
   gboolean l_found_in_cache;
   gboolean l_was_last_maskframe;
+  gboolean l_makeGrayFlattened;
 
   /* both local_stepcount and mask_framecount start with 0 for the 1st element */
   l_framenr = frn_elem->mask_stepsize * (gdouble)(frn_elem->mask_framecount + local_stepcount);
   l_master_framenr = 1 + (gint32)(l_framenr);
+  l_makeGrayFlattened = TRUE;
+
+  if (mask_anchor == GAP_MSK_ANCHOR_XCOLOR)
+  {
+    l_makeGrayFlattened = FALSE;
+  }
 
   if(gap_debug)
   {
@@ -3490,6 +3596,7 @@ p_fetch_and_add_layermask(GapStoryRenderVidHandle *vidhand
                               , gimp_drawable_height(layer_id)
                               ,&l_tmp_mask_layer_id
                               ,&l_was_last_maskframe
+                              , l_makeGrayFlattened
                               );
   }
 
@@ -3519,14 +3626,29 @@ p_fetch_and_add_layermask(GapStoryRenderVidHandle *vidhand
      {
        gimp_layer_add_alpha(layer_id);
      }
-     l_new_layer_mask_id = gimp_layer_create_mask(layer_id, GIMP_ADD_WHITE_MASK);
-     gimp_layer_add_mask(layer_id, l_new_layer_mask_id);
 
+     if (mask_anchor == GAP_MSK_ANCHOR_XCOLOR)
+     {
+       /* render the layermask by applying the mask as colormask */
+       gap_colormask_apply_to_layer_of_same_size_from_file (layer_id
+                                  , l_tmp_mask_layer_id       /* the colormask to be applied */
+                                  , frn_elem->colormask_file  /* colormask parameter file */
+                                  , TRUE                      /* keepLayerMask  */
+                                  , FALSE                     /* doProgress */
+                            );
+     }
+     else
+     {
+       l_new_layer_mask_id = gimp_layer_create_mask(layer_id, GIMP_ADD_WHITE_MASK);
+       gimp_layer_add_mask(layer_id, l_new_layer_mask_id);
+
+
+       /* overwrite the white layer mask with the fetched mask */
+       gap_layer_copy_content(l_new_layer_mask_id   /* dst_drawable_id */
+                             ,l_tmp_mask_layer_id     /* src_drawable_id */
+                             );
+     }
 
-     /* overwrite the white layer mask with the fetched mask */
-     gap_layer_copy_content(l_new_layer_mask_id   /* dst_drawable_id */
-                           ,l_tmp_mask_layer_id     /* src_drawable_id */
-                           );
 
 
      if(!l_found_in_cache)
@@ -3551,6 +3673,7 @@ p_fetch_and_add_layermask(GapStoryRenderVidHandle *vidhand
 }  /* end p_fetch_and_add_layermask */
 
 
+
 /* ----------------------------------------------------
  * p_open_video_handle_private
  * ----------------------------------------------------
@@ -3617,10 +3740,14 @@ p_open_video_handle_private(    gboolean ignore_audio
 
   vidhand->frn_list = NULL;
   vidhand->preferred_decoder = NULL;
+  vidhand->master_insert_alpha_format = NULL;
+  vidhand->master_insert_alpha_format_has_videobasename = FALSE;
+  vidhand->master_insert_alpha_format_has_framenumber = FALSE;
+
   vidhand->master_insert_area_format = NULL;
   vidhand->master_insert_area_format_has_videobasename = FALSE;
   vidhand->master_insert_area_format_has_framenumber = FALSE;
-  
+
   vidhand->do_gimp_progress = do_gimp_progress;
   *vidhand->progress = 0.0;
   vidhand->sterr = p_new_stb_error();
@@ -3722,6 +3849,7 @@ p_open_video_handle_private(    gboolean ignore_audio
                                          , TRUE                 /* mask_disable */
                                          , 1                    /* fmac_total_steps */
                                          , 0                    /* fmac_accel */
+                                         , NULL                 /* colormask_file */
                                          );
       if(frn_elem)
       {
@@ -3755,6 +3883,7 @@ p_open_video_handle_private(    gboolean ignore_audio
                                          , TRUE                 /* mask_disable */
                                          , 1                    /* fmac_total_steps */
                                          , 0                    /* fmac_accel */
+                                         , NULL                 /* colormask_file */
                                          );
         render_section->frn_list->frames_to_handle = l_from -1;
         render_section->frn_list->next = frn_elem;
@@ -3815,6 +3944,7 @@ p_open_video_handle_private(    gboolean ignore_audio
                                          , TRUE                 /* mask_disable */
                                          , 1                    /* fmac_total_steps */
                                          , 0                    /* fmac_accel */
+                                         , NULL                 /* colormask_file */
                                          );
       if(frn_elem) *frame_count = frn_elem->frames_to_handle;
 
@@ -3846,6 +3976,7 @@ p_open_video_handle_private(    gboolean ignore_audio
                                            , TRUE                 /* mask_disable */
                                            , 1                    /* fmac_total_steps */
                                            , 0                    /* fmac_accel */
+                                           , NULL                 /* colormask_file */
                                            );
         render_section->frn_list->frames_to_handle = l_from -1;
         render_section->frn_list->next = frn_elem;
@@ -3894,6 +4025,7 @@ p_open_video_handle_private(    gboolean ignore_audio
                                          , TRUE                 /* mask_disable */
                                          , 1                    /* fmac_total_steps */
                                          , 0                    /* fmac_accel */
+                                         , NULL                 /* colormask_file */
                                          );
       if(frn_elem)
       {
@@ -3932,6 +4064,7 @@ p_open_video_handle_private(    gboolean ignore_audio
                                          , TRUE                 /* mask_disable */
                                          , 1                    /* fmac_total_steps */
                                          , 0                    /* fmac_accel */
+                                         , NULL                 /* colormask_file */
                                          );
       if(frn_elem)
       {
@@ -4165,7 +4298,7 @@ p_exec_filtermacro(gint32 image_id, gint32 layer_id, const char *filtermacro_fil
                               , (int)total_steps
                               );
        }
-       
+
        if(! gimp_drawable_has_alpha (layer_id))
        {
          /* some filtermacros do not work with layer that do not have an alpha channel
@@ -4194,7 +4327,7 @@ p_exec_filtermacro(gint32 image_id, gint32 layer_id, const char *filtermacro_fil
        else
        {
            gdouble   current_accel_step;
-           
+
            current_accel_step = gap_calculate_current_step_with_acceleration(current_step
                                    , total_steps
                                    , accelerationCharacteristic
@@ -4254,11 +4387,15 @@ p_transform_operate_on_full_layer(GapStoryCalcAttr *calculated, gint32 comp_imag
   gboolean l_ret;
 
   l_ret = TRUE;
-  
+
+  if(calculated->rotate != 0.0)
+  {
+    return (l_ret);
+  }
   calculated_area = calculated->width * calculated->height;
   visible_on_composite_area = calculated->visible_width * calculated->visible_height;
   composite_img_area = gimp_image_width(comp_image_id) * gimp_image_height(comp_image_id);
- 
+
   if(gap_debug)
   {
       tmp_image_area = gimp_image_width(tmp_image_id) * gimp_image_height(tmp_image_id);
@@ -4269,15 +4406,15 @@ p_transform_operate_on_full_layer(GapStoryCalcAttr *calculated, gint32 comp_imag
          , (int)composite_img_area
          );
   }
-  
+
   if ((calculated_area > composite_img_area)
   ||  (visible_on_composite_area < composite_img_area))
   {
     if((frn_elem->mask_name != NULL)
-    && (frn_elem->mask_anchor == GAP_MSK_ANCHOR_CLIP))
+    && (frn_elem->mask_anchor != GAP_MSK_ANCHOR_MASTER))
     {
       tmp_image_area = gimp_image_width(tmp_image_id) * gimp_image_height(tmp_image_id);
-      
+
       /* operation on clipped rectangle requires creation of the mask already at
        * tmp_image_area size. in case the calculated area
        * is smaller than the original tmp image, it will be
@@ -4285,19 +4422,21 @@ p_transform_operate_on_full_layer(GapStoryCalcAttr *calculated, gint32 comp_imag
        */
       if (tmp_image_area < calculated_area)
       {
-        l_ret = FALSE; /* operate on visble rectanngle only */
+        l_ret = FALSE; /* operate on visble rectangle only */
       }
     }
     else
     {
-      l_ret = FALSE; /* operate on visble rectanngle only */
+      l_ret = FALSE; /* operate on visble rectangle only */
     }
   }
-  
+
   return (l_ret);
 
 }  /* end p_transform_operate_on_full_layer */
 
+
+
 /* ----------------------------------------------------
  * p_transform_and_add_layer
  * ----------------------------------------------------
@@ -4317,6 +4456,7 @@ p_transform_and_add_layer( gint32 comp_image_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 */
@@ -4338,9 +4478,10 @@ p_transform_and_add_layer( gint32 comp_image_id
 
   if(gap_debug)
   {
-    printf("p_transform_and_add_layer: called at layer_id: %d, tmp_image_id:%d\n"
+    printf("p_transform_and_add_layer: called at layer_id: %d, tmp_image_id:%d comp_image_id:%d\n"
       , (int)layer_id
-      ,(int)tmp_image_id );
+      , (int)tmp_image_id
+      , (int)comp_image_id);
     gap_story_render_debug_print_frame_elem(frn_elem, -77);
   }
 
@@ -4385,6 +4526,7 @@ p_transform_and_add_layer( gint32 comp_image_id
     , keep_proportions
     , fit_width
     , fit_height
+    , rotate
     , opacity
     , scale_x
     , scale_y
@@ -4438,7 +4580,7 @@ p_transform_and_add_layer( gint32 comp_image_id
 
 
     if((frn_elem->mask_name != NULL)
-    && (frn_elem->mask_anchor == GAP_MSK_ANCHOR_CLIP))
+    && (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
@@ -4446,6 +4588,7 @@ p_transform_and_add_layer( gint32 comp_image_id
                   , 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)
@@ -4454,6 +4597,21 @@ p_transform_and_add_layer( gint32 comp_image_id
 
     }
 
+    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);
+
+      gap_story_transform_rotate_layer(tmp_image_id, layer_id, rotate);
+
+      /* 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
      */
@@ -4470,20 +4628,20 @@ p_transform_and_add_layer( gint32 comp_image_id
   }
   else
   {
-    /* operate on clipped rectangle size */
+    /* 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_CLIP))
+    && (frn_elem->mask_anchor != GAP_MSK_ANCHOR_MASTER))
     {
       /* add and apply layermask at original unscaled tmp_image size */
       p_fetch_and_add_layermask(vidhand
@@ -4491,6 +4649,7 @@ p_transform_and_add_layer( gint32 comp_image_id
                   , 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)
@@ -4499,8 +4658,8 @@ p_transform_and_add_layer( gint32 comp_image_id
     }
 
 
-         
-    /* copy selected clipped source rectangle from tmp_image to composite image 
+
+    /* 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)
      */
     {
@@ -4508,21 +4667,21 @@ p_transform_and_add_layer( gint32 comp_image_id
       gdouble sy;
       gdouble swidth;
       gdouble sheight;
-      
+
       sx = 0;
       if (calculated->x_offs < 0)
       {
-        sx = (0 - calculated->x_offs) * 
+        sx = (0 - calculated->x_offs) *
             ((gdouble)gimp_image_width(tmp_image_id) / MAX((gdouble)calculated->width, 1.0));
       }
-      
+
       sy = 0;
       if (calculated->y_offs < 0)
       {
-        sy = (0 - calculated->y_offs) * 
+        sy = (0 - calculated->y_offs) *
             ((gdouble)gimp_image_height(tmp_image_id) / MAX((gdouble)calculated->height, 1.0));
       }
-      
+
       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);
 
@@ -4569,10 +4728,14 @@ p_transform_and_add_layer( gint32 comp_image_id
 
     gimp_floating_sel_anchor(l_fsel_layer_id);
 
-    
+
   }
 
 
+//   if((rotate  > 0.05) || (rotate < -0.05))
+//   {
+//     gap_story_transform_rotate_layer(comp_image_id, l_new_layer_id, rotate);
+//   }
 
 
 
@@ -4584,6 +4747,7 @@ p_transform_and_add_layer( gint32 comp_image_id
                   , local_stepcount
                   , comp_image_id
                   , l_new_layer_id
+                  , frn_elem->mask_anchor
                   );
 
   }
@@ -4717,13 +4881,13 @@ p_limit_open_videohandles(GapStoryRenderVidHandle *vidhand
                                                         , 100
                                                         );
   l_count_open_videohandles = currently_open_videohandles;
-  
+
   if (l_count_open_videohandles <= l_max_open_videohandles)
   {
     /* we are below the limit, nothing left to do in that case */
     return;
   }
-  
+
   for (frn_elem = vidhand->frn_list; frn_elem != NULL; frn_elem = (GapStoryRenderFrameRangeElem *)frn_elem->next)
   {
     if((frn_elem->last_master_frame_access < master_frame_nr)
@@ -4747,7 +4911,7 @@ p_limit_open_videohandles(GapStoryRenderVidHandle *vidhand
        {
          return;
        }
-       
+
     }
   }
 #endif
@@ -5038,8 +5202,8 @@ gap_story_render_fetch_composite_image(GapStoryRenderVidHandle *vidhand
 /* ------------------------------------------------
  * p_split_delace_value
  * ------------------------------------------------
- * split the specified delace value: 
- *    integer part is deinterlace mode, 
+ * split the specified delace value:
+ *    integer part is deinterlace mode,
  *      (0 NO,
  *       1 Odd,
  *       2 Even,
@@ -5053,7 +5217,7 @@ gap_story_render_fetch_composite_image(GapStoryRenderVidHandle *vidhand
  * between 2 framenumbers, where the value 0.5 is the middle.
  * An Interlaced frame contains 2 half-frames where one half-frame is represented by
  * the even, the other half-frame by the odd lines.
- * 
+ *
  * therfore localframe_tween_rest values >= 0.5 selects the other half-frame,
  * in case the enable_interlace_tween_pick option is enabled.
  *
@@ -5102,7 +5266,7 @@ p_split_delace_value(gdouble delace, gdouble localframe_tween_rest, gint32 *dein
 
 
 /* -------------------------------------------------------------------
- * p_conditional_delace_drawable 
+ * p_conditional_delace_drawable
  * -------------------------------------------------------------------
  * general deinterlace handling for frames, images an animimages
  * (except cliptype movie)
@@ -5155,12 +5319,22 @@ p_stb_render_image_or_animimage(GapStbFetchData *gfd
                         , gfd->framename            /* full filename of the image */
                         , TRUE /*  enable caching */
                        );
-  
+
   gimp_selection_none(l_orig_image_id);
   if(gfd->frn_type == GAP_FRN_IMAGE)
   {
-    gfd->layer_id = p_prepare_RGB_image(l_orig_image_id);
     gfd->tmp_image_id = gimp_image_duplicate(l_orig_image_id);
+    gfd->layer_id = p_prepare_RGB_image(gfd->tmp_image_id);
+    gap_frame_fetch_remove_parasite(gfd->tmp_image_id);
+    if(gap_debug)
+    {
+      printf("IMAGE fetch  master_frame_nr:%d  (dup)tmp_image_id:%d layer_id:%d l_orig_image_id:%d\n"
+         ,(int)master_frame_nr
+         ,(int)gfd->tmp_image_id
+         ,(int)gfd->layer_id
+         ,(int)l_orig_image_id
+         );
+    }
   }
   else
   {
@@ -5213,14 +5387,14 @@ p_stb_render_image_or_animimage(GapStbFetchData *gfd
        g_free (l_layers_list);
     }
   }
-  
+
 }  /* end p_stb_render_image_or_animimage */
 
 
 /* -------------------------------------------
  * p_stb_render_movie (GAP_FRN_MOVIE)
  * -------------------------------------------
- * fetch frame from a videofile (gfd->framename contains the videofile name) 
+ * fetch frame from a videofile (gfd->framename contains the videofile name)
  */
 static void
 p_stb_render_movie(GapStbFetchData *gfd
@@ -5417,7 +5591,7 @@ p_stb_render_section(GapStbFetchData *gfd
  * p_stb_render_frame_images (GAP_FRN_FRAMES)
  * -------------------------------------------
  * gfd->framename  is one single imagefile out of a series of numbered imagefiles.
- * (note that the gfd->framename is already full qualified 
+ * (note that the gfd->framename is already full qualified
  *  and includes path name, numberpart and extension)
  */
 static void
@@ -5440,7 +5614,7 @@ p_stb_render_frame_images(GapStbFetchData *gfd, gint32 master_frame_nr)
  * p_stb_render_composite_image_postprocessing
  * -------------------------------------------
  * perform postprocessing on the composite frame image.
- * this includes 
+ * this includes
  *  - convert to gray (only when fetching masks)
  *  - optional applying the global filtermacro
  *  - check size and scale (if filtermacro has changed size of the composite image)
@@ -5474,17 +5648,6 @@ p_stb_render_composite_image_postprocessing(GapStbFetchData *gfd
                          );
   }
 
-  if(vidhand->is_mask_handle == TRUE)
-  {
-    /* we are running as mask fetcher,
-     * therefore convert to GRAY image
-     */
-    if(gimp_image_base_type(gfd->comp_image_id) != GIMP_GRAY)
-    {
-      gimp_image_convert_grayscale(gfd->comp_image_id);
-    }
-  }
-
   /* debug: disabled code to display a copy of the image */
   if(1==0)
   {
@@ -5600,7 +5763,7 @@ p_stb_render_result_monitoring(GapStbFetchData *gfd, gint32 master_frame_nr)
 
 /* ------------------------
  * p_paste_logo_pattern
- * ------------------------  
+ * ------------------------
  * replace logo area with the specified logo pattern
  */
 static void
@@ -5646,7 +5809,7 @@ p_paste_logo_pattern(gint32 drawable_id
   gimp_layer_set_offsets(l_fsel_layer_id
                         , offsetX + l_src_offset_x
                         , offsetY + l_src_offset_y);
-  
+
   gimp_floating_sel_anchor(l_fsel_layer_id);
 
 } /* end p_copy_and_paste_replacement_pattern */
@@ -5656,6 +5819,7 @@ p_paste_logo_pattern(gint32 drawable_id
 /* -------------------------------------
  * p_do_insert_area_processing
  * -------------------------------------
+ * add logo area to video clip
  */
 static void
 p_do_insert_area_processing(GapStbFetchData *gfd
@@ -5663,10 +5827,10 @@ p_do_insert_area_processing(GapStbFetchData *gfd
 {
   char *logo_imagename;
   char *videofilename_without_path;
-  
-  
-  videofilename_without_path = gap_story_build_basename(gfd->framename);
-  
+
+
+  videofilename_without_path = gap_lib_build_basename_without_ext(gfd->framename);
+
   if (vidhand->master_insert_area_format_has_framenumber)
   {
     if (vidhand->master_insert_area_format_has_videobasename)
@@ -5714,7 +5878,7 @@ p_do_insert_area_processing(GapStbFetchData *gfd
   {
     gint32 logo_image_id;
     gint32 logo_layer_id;
-    
+
     if (vidhand->master_insert_area_format_has_framenumber)
     {
       logo_image_id = gap_lib_load_image(logo_imagename);
@@ -5727,14 +5891,14 @@ p_do_insert_area_processing(GapStbFetchData *gfd
                             , TRUE /*  enable caching */
                            );
     }
-    
+
     if(logo_image_id < 0)
     {
       printf("p_do_insert_area_processing: ERROR could not load logo_imagename:%s\n", logo_imagename);
       return;
     }
-    
-    
+
+
     gimp_selection_none(logo_image_id);
     logo_layer_id = p_prepare_RGB_image(logo_image_id);
 
@@ -5752,6 +5916,202 @@ p_do_insert_area_processing(GapStbFetchData *gfd
 
 }  /* end p_do_insert_area_processing */
 
+
+
+
+/* ----------------------------------------------------
+ * p_prepare_GRAY_image
+ * ----------------------------------------------------
+ * prepare image to be applied as transparency channel to a frame.
+ * - clear undo stack
+ * - convert to GREY
+ * - merge all visible layer to one layer that
+ *   fits the image size.
+ *
+ * return the resulting layer_id.
+ */
+static gint32
+p_prepare_GRAY_image(gint32 image_id)
+{
+  gint          l_nlayers;
+  gint32       *l_layers_list;
+  gint32 l_layer_id;
+
+  l_layer_id = -1;
+ /* dont waste time and memory for undo in noninteracive processing
+  * of the frames
+  */
+  /*  gimp_image_undo_enable(image_id); */ /* clear undo stack */
+  /* no more gimp_image_undo_enable, because this results in Warnings since gimp-2.1.6
+   * Gimp-Core-CRITICAL **: file gimpimage.c: line 1708 (gimp_image_undo_thaw): assertion `gimage->undo_freeze_count > 0' failed
+   */
+  gimp_image_undo_disable(image_id); /*  NO Undo */
+
+  l_layers_list = gimp_image_get_layers(image_id, &l_nlayers);
+  if(l_layers_list != NULL)
+  {
+    l_layer_id = l_layers_list[0];
+    g_free (l_layers_list);
+  }
+
+  if((l_nlayers > 1 ) || (gimp_layer_get_mask (l_layer_id) >= 0))
+  {
+     if(gap_debug) printf("DEBUG: p_prepare_image merge layers tmp image\n");
+
+     /* merge visible layers (reduce to single layer) */
+     l_layer_id = gap_image_merge_visible_layers(image_id, GIMP_CLIP_TO_IMAGE);
+  }
+
+  /* convert TO GREY if needed */
+  if(gimp_image_base_type(image_id) != GIMP_GRAY)
+  {
+     gimp_image_convert_grayscale(image_id);
+  }
+
+  if(l_layer_id >= 0)
+  {
+    gimp_layer_resize_to_image_size(l_layer_id);
+  }
+
+  return(l_layer_id);
+} /* end p_prepare_GRAY_image */
+
+
+
+
+/* -------------------------------------
+ * p_do_insert_alpha_processing
+ * -------------------------------------
+ * adds alpha channel for videoframes
+ * based on an image or series of frames
+ * matching the configured format string.
+ * (VID_MASTER_INSERT_ALPHA)
+ *
+ */
+static void
+p_do_insert_alpha_processing(GapStbFetchData *gfd
+  , GapStoryRenderVidHandle *vidhand)
+{
+  char *alpha_imagename;
+  char *videofilename_without_path;
+
+
+  videofilename_without_path =   gap_lib_build_basename_without_ext(gfd->framename);
+
+  if (vidhand->master_insert_alpha_format_has_framenumber)
+  {
+    if (vidhand->master_insert_alpha_format_has_videobasename)
+    {
+      alpha_imagename =
+         g_strdup_printf(vidhand->master_insert_alpha_format
+                       , videofilename_without_path
+                       , gfd->localframe_index   /* videoFrameNr */
+                       );
+    }
+    else
+    {
+      alpha_imagename =
+         g_strdup_printf(vidhand->master_insert_alpha_format
+                       , gfd->localframe_index   /* videoFrameNr */
+                       );
+    }
+  }
+  else
+  {
+    if (vidhand->master_insert_alpha_format_has_videobasename)
+    {
+      alpha_imagename =
+         g_strdup_printf(vidhand->master_insert_alpha_format
+                       , videofilename_without_path
+                       );
+    }
+    else
+    {
+      alpha_imagename = g_strdup(vidhand->master_insert_alpha_format);
+    }
+  }
+
+  if(gap_debug)
+  {
+    printf("p_do_insert_alpha_processing: format:%s\n video:%s\n alpha_imagename:%s\n"
+        , vidhand->master_insert_alpha_format
+        , videofilename_without_path
+        , alpha_imagename
+        );
+  }
+
+
+  if(g_file_test(alpha_imagename, G_FILE_TEST_EXISTS))
+  {
+    gint32 alpha_image_id;
+    gint32 alpha_layer_id;
+    gint   vid_width;
+    gint   vid_height;
+
+    if (vidhand->master_insert_alpha_format_has_framenumber)
+    {
+      alpha_image_id = gap_lib_load_image(alpha_imagename);
+    }
+    else
+    {
+      /* use framefetcher cache in case all frames shall get alpha from the same image */
+      alpha_image_id = gap_frame_fetch_orig_image(vidhand->ffetch_user_id
+                            , alpha_imagename
+                            , TRUE /*  enable caching */
+                           );
+    }
+
+    if(alpha_image_id < 0)
+    {
+      printf("p_do_insert_alpha_processing: ERROR could not load alpha_imagename:%s\n"
+            , alpha_imagename
+            );
+      return;
+    }
+
+
+    gimp_selection_none(alpha_image_id);
+    alpha_layer_id = p_prepare_GRAY_image(alpha_image_id);
+
+    vid_width = gimp_image_width(gfd->tmp_image_id);
+    vid_height = gimp_image_height(gfd->tmp_image_id);
+
+    /* scale alpha image to Videosize (if not already equal) */
+    if ((gimp_image_width(alpha_image_id) != vid_width)
+    ||  (gimp_image_height(alpha_image_id) != vid_height) )
+    {
+       if(gap_debug)
+       {
+         printf("DEBUG: p_do_insert_alpha_processing scaling alpha image\n");
+       }
+       gimp_image_scale(gfd->comp_image_id, vid_width, vid_height);
+    }
+
+    if(! gimp_drawable_has_alpha(gfd->layer_id))
+    {
+      /* have to add alpha channel */
+      gimp_layer_add_alpha(gfd->layer_id);
+    }
+
+
+    /* copy alpha_layer_id into the alpha channel of the current frame */
+    gap_layer_copy_picked_channel(gfd->layer_id, 3  /* dst_pick is the alpha channel */
+                               ,alpha_layer_id, 0   /* gray value */
+                               ,FALSE  /* shadow */
+                               );
+
+    if (vidhand->master_insert_alpha_format_has_framenumber)
+    {
+      /* do not keep individual per frame alpha images cached
+       */
+      gap_image_delete_immediate(alpha_image_id);
+    }
+  }
+
+}  /* end p_do_insert_alpha_processing */
+
+
+
 /* --------------------------------------------
  * p_story_render_fetch_composite_image_private
  * --------------------------------------------
@@ -5790,6 +6150,8 @@ p_story_render_fetch_composite_image_private(GapStoryRenderVidHandle *vidhand
   gfd->layer_id        = -1;
   *layer_id         = -1;
 
+
+
   if(gap_debug)
   {
     printf("p_story_render_fetch_composite_image_private START  master_frame_nr:%d  %dx%d vidhand:%d\n"
@@ -5814,7 +6176,6 @@ p_story_render_fetch_composite_image_private(GapStoryRenderVidHandle *vidhand
   {
     if((vidhand->is_mask_handle == FALSE)
     && (master_frame_nr == 1)
-    // && (section_name == NULL)
     )
     {
       printf("\n###\n###\nSTART rendering at master_frame_nr 1 with this list of elements:\n");
@@ -5845,6 +6206,7 @@ p_story_render_fetch_composite_image_private(GapStoryRenderVidHandle *vidhand
                  , &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 */
@@ -5908,11 +6270,19 @@ p_story_render_fetch_composite_image_private(GapStoryRenderVidHandle *vidhand
               return -1;
            }
            gfd->layer_id = p_prepare_RGB_image(gfd->tmp_image_id);
-           if((gfd->frn_type == GAP_FRN_MOVIE) && (vidhand->master_insert_area_format))
+
+           if(gfd->frn_type == GAP_FRN_MOVIE)
            {
-             p_do_insert_area_processing(gfd, vidhand);
+             if(vidhand->master_insert_alpha_format)
+             {
+               p_do_insert_alpha_processing(gfd, vidhand);
+             }
+             if(vidhand->master_insert_area_format)
+             {
+               p_do_insert_area_processing(gfd, vidhand);
+             }
            }
-           
+
            p_conditional_delace_drawable(gfd, gfd->layer_id);
            g_free(gfd->framename);
          }
@@ -5921,15 +6291,18 @@ p_story_render_fetch_composite_image_private(GapStoryRenderVidHandle *vidhand
 
        if(gap_debug)
        {
-         printf("p_prepare_RGB_image returned layer_id: %d, tmp_image_id:%d\n"
+         printf("p_prepare_RGB_image returned layer_id: %d, tmp_image_id:%d (master_frame_nr:%d, comp_image_id:%d)\n"
             , (int)gfd->layer_id
             , (int)gfd->tmp_image_id
+            , (int)master_frame_nr
+            , (int)gfd->comp_image_id
             );
-       }         
+       }
 
        if(gfd->comp_image_id  < 0)
        {
          if((gfd->opacity == 1.0)
+         && (gfd->rotate == 0.0)
          && (gfd->scale_x == 1.0)
          && (gfd->scale_y == 1.0)
          && (gfd->move_x == 0.0)
@@ -5978,6 +6351,7 @@ p_story_render_fetch_composite_image_private(GapStoryRenderVidHandle *vidhand
                                   ,gfd->keep_proportions
                                   ,gfd->fit_width
                                   ,gfd->fit_height
+                                  ,gfd->rotate
                                   ,gfd->opacity
                                   ,gfd->scale_x
                                   ,gfd->scale_y
@@ -6029,7 +6403,7 @@ p_story_render_fetch_composite_image_private(GapStoryRenderVidHandle *vidhand
 /* -------------------------------------------------------------------
  * gap_story_render_fetch_composite_image_or_chunk (see included file)
  * -------------------------------------------------------------------
- * 
+ *
  */
 
 #include "gap_story_render_lossless.c"
diff --git a/gap/gap_story_render_types.h b/gap/gap_story_render_types.h
index 80d6fa6..f208374 100644
--- a/gap/gap_story_render_types.h
+++ b/gap/gap_story_render_types.h
@@ -60,7 +60,8 @@ typedef gpointer t_GVA_Handle;
 typedef enum
 {
    GAP_MSK_ANCHOR_CLIP
-  ,GAP_MSK_ANCHOR_MASTER        
+  ,GAP_MSK_ANCHOR_MASTER
+  ,GAP_MSK_ANCHOR_XCOLOR   /* apply to clip as colormask */
 } GapStoryMaskAnchormode;
 
 typedef enum
@@ -137,7 +138,7 @@ typedef struct GapStoryRenderFrameRangeElem   /* nick: frn_elem */
    char   *filtermacro_file_to;  /* additional macro with 2nd parameterset(s) for varying apply */
    gint32  fmac_total_steps;     /* total steps for varying filtermacro apply */
    gint32  fmac_accel;           /* acceleration characteristic for filtermacro apply with varying values */
-   
+
    gdouble  frame_from;       /* internal frame number that is 1.st of range (float due to internal clip splitting) */
    gdouble  frame_to;         /* internal frame number that is the last handled frame of the range */
    gint32  frames_to_handle;
@@ -157,11 +158,19 @@ typedef struct GapStoryRenderFrameRangeElem   /* nick: frn_elem */
    char    *mask_name;          /* optional reference to a layer mask */
    gdouble  mask_stepsize;
    GapStoryMaskAnchormode  mask_anchor;
+   char   *colormask_file;    /* optional reference to a colormask parameter file
+                               * relevant for ancor mode GAP_MSK_ANCHOR_CLIPCOLOR
+                               * where mask is applied as colormask
+                               */
 
 
    gdouble  wait_untiltime_sec;
    gint32   wait_untilframes;
 
+   gdouble rotate_from;        /* -36000.0 upto 3600.0 degree */
+   gdouble rotate_to;          /*  -36000.0 upto 3600.0 degree */
+   gint32  rotate_dur;         /* number of frames to change from -> to value */
+
    gdouble opacity_from;       /* 0.0 upto 1.0 */
    gdouble opacity_to;         /* 0.0 upto 1.0 */
    gint32  opacity_dur;        /* number of frames to change from -> to value */
@@ -183,7 +192,7 @@ typedef struct GapStoryRenderFrameRangeElem   /* nick: frn_elem */
    gint32  move_y_dur;         /* number of frames to change from -> to value */
 
    gint    opacity_accel;        /* acceleration characteristic for opacity transformation */
-   gint32  opacity_frames_done;  /* already processed frames since begin of transition */ 
+   gint32  opacity_frames_done;  /* already processed frames since begin of transition */
    gint    move_x_accel;
    gint32  move_x_frames_done;
    gint    move_y_accel;
@@ -192,6 +201,8 @@ typedef struct GapStoryRenderFrameRangeElem   /* nick: frn_elem */
    gint32  scale_x_frames_done;
    gint    scale_y_accel;
    gint32  scale_y_frames_done;
+   gint    rotate_accel;        /* acceleration characteristic for opacity transformation */
+   gint32  rotate_frames_done;  /* already processed frames since begin of transition */
 
 
    void   *next;
@@ -251,6 +262,10 @@ typedef struct GapStoryRenderVTrackAttrElem
    gboolean fit_width;
    gboolean fit_height;
 
+   gdouble rotate_from;       /* rotation in degree */
+   gdouble rotate_to;         /* rotation in degree */
+   gint32  rotate_dur;        /* number of frames to change from -> to value */
+
    gdouble opacity_from;       /* 0.0 upto 1.0 */
    gdouble opacity_to;         /* 0.0 upto 1.0 */
    gint32  opacity_dur;        /* number of frames to change from -> to value */
@@ -271,8 +286,10 @@ typedef struct GapStoryRenderVTrackAttrElem
    gdouble move_y_to;          /* -1.0 upto 1.0 where 0 is center and -1.0 up outside */
    gint32  move_y_dur;         /* number of frames to change from -> to value */
 
+   gint    rotate_accel;        /* acceleration characteristic for opacity transformation */
+   gint32  rotate_frames_done;  /* already processed frames since begin of transition */
    gint    opacity_accel;        /* acceleration characteristic for opacity transformation */
-   gint32  opacity_frames_done;  /* already processed frames since begin of transition */ 
+   gint32  opacity_frames_done;  /* already processed frames since begin of transition */
    gint    move_x_accel;
    gint32  move_x_frames_done;
    gint    move_y_accel;
@@ -323,6 +340,11 @@ typedef struct GapStoryRenderVidHandle
   GapStoryRenderAudioRangeElem    *aud_list;
   GapStoryRenderErrors            *sterr;
   char                         *preferred_decoder;
+
+  char                         *master_insert_alpha_format;    /* Format for alpha channel inserting */
+  gboolean                      master_insert_alpha_format_has_videobasename;
+  gboolean                      master_insert_alpha_format_has_framenumber;
+
   char                         *master_insert_area_format;    /* Format for logo replacement */
   gboolean                      master_insert_area_format_has_videobasename;
   gboolean                      master_insert_area_format_has_framenumber;
@@ -372,7 +394,7 @@ typedef struct GapStoryRenderMaskDefElem  /* nick: maskdef_elem */
 
 
   GapStoryRenderVidHandle *mask_vidhand;
-  
+
   struct GapStoryRenderMaskDefElem *next;
 } GapStoryRenderMaskDefElem;
 
diff --git a/gap/gap_story_section_properties.c b/gap/gap_story_section_properties.c
index b51740c..4e97272 100644
--- a/gap/gap_story_section_properties.c
+++ b/gap/gap_story_section_properties.c
@@ -396,7 +396,7 @@ p_switch_to_section(GapStbSecpropWidget *spw, GapStorySection *target_section)
   
   /* make the section the active section
    * make the section the active section, refresh the sections combo box
-   * (this implicite triggers thumbnail rendering)
+   * (this implicitly triggers thumbnail rendering)
    */
   gap_story_dlg_spw_section_refresh(spw, target_section);
   
diff --git a/gap/gap_story_syntax.c b/gap/gap_story_syntax.c
index 2936b02..d1d5312 100644
--- a/gap/gap_story_syntax.c
+++ b/gap/gap_story_syntax.c
@@ -280,6 +280,10 @@ p_create_syntax_list(void)
                ,"decoder"
                ,NULL
                );
+  p_add_keyword(GAP_STBKEY_VID_MASTER_INSERT_ALPHA
+               ,"format"
+               ,NULL
+               );
   p_add_keyword(GAP_STBKEY_VID_MASTER_INSERT_AREA
                ,"format"
                ,NULL
@@ -307,6 +311,7 @@ p_create_syntax_list(void)
                ,"mask_disable"
                ,"macsteps"
                ,"macaccel"
+               ,"colormask_file"
                ,NULL
                );
   p_add_keyword(GAP_STBKEY_VID_PLAY_BLACKSECTION
@@ -325,6 +330,7 @@ p_create_syntax_list(void)
                ,"mask_disable"
                ,"macsteps"
                ,"macaccel"
+               ,"colormask_file"
                ,NULL
                );
   p_add_keyword(GAP_STBKEY_VID_PLAY_MOVIE
@@ -347,6 +353,7 @@ p_create_syntax_list(void)
                ,"mask_disable"
                ,"macsteps"
                ,"macaccel"
+               ,"colormask_file"
                ,NULL
                );
   p_add_keyword(GAP_STBKEY_VID_PLAY_FRAMES
@@ -366,6 +373,7 @@ p_create_syntax_list(void)
                ,"mask_disable"
                ,"macsteps"
                ,"macaccel"
+               ,"colormask_file"
                ,NULL
                );
   p_add_keyword(GAP_STBKEY_VID_PLAY_ANIMIMAGE
@@ -384,6 +392,7 @@ p_create_syntax_list(void)
                ,"mask_disable"
                ,"macsteps"
                ,"macaccel"
+               ,"colormask_file"
                ,NULL
                );
 
@@ -399,6 +408,7 @@ p_create_syntax_list(void)
                ,"mask_disable"
                ,"macsteps"
                ,"macaccel"
+               ,"colormask_file"
                ,NULL
                );
   p_add_keyword(GAP_STBKEY_VID_PLAY_COLOR
@@ -416,6 +426,7 @@ p_create_syntax_list(void)
                ,"mask_disable"
                ,"macsteps"
                ,"macaccel"
+               ,"colormask_file"
                ,NULL
                );
   p_add_keyword(GAP_STBKEY_VID_SILENCE
@@ -424,6 +435,14 @@ p_create_syntax_list(void)
                ,"wait_until_sec"
                ,NULL
                );
+  p_add_keyword(GAP_STBKEY_VID_ROTATE
+               ,"track"
+               ,"rotate_from"
+               ,"rotate_to"
+               ,"nframes"
+               ,"accel"
+               ,NULL
+               );
   p_add_keyword(GAP_STBKEY_VID_OPACITY
                ,"track"
                ,"opacity_from"
diff --git a/gap/gap_story_syntax.h b/gap/gap_story_syntax.h
index 6d72074..44a630d 100644
--- a/gap/gap_story_syntax.h
+++ b/gap/gap_story_syntax.h
@@ -43,6 +43,7 @@
 #define GAP_STBKEY_VID_MASTER_FRAME_ASPECT  "VID_MASTER_FRAME_ASPECT"
 #define GAP_STBKEY_VID_MASTER_LAYERSTACK    "VID_MASTER_LAYERSTACK"
 #define GAP_STBKEY_VID_PREFERRED_DECODER    "VID_PREFERRED_DECODER"
+#define GAP_STBKEY_VID_MASTER_INSERT_ALPHA  "VID_MASTER_INSERT_ALPHA"
 #define GAP_STBKEY_VID_MASTER_INSERT_AREA   "VID_MASTER_INSERT_AREA"
 
 #define GAP_STBKEY_MAIN_SECTION             "MAIN_SECTION"
@@ -56,6 +57,7 @@
 #define GAP_STBKEY_VID_PLAY_IMAGE           "VID_PLAY_IMAGE"
 #define GAP_STBKEY_VID_PLAY_COLOR           "VID_PLAY_COLOR"
 #define GAP_STBKEY_VID_SILENCE              "VID_SILENCE"
+#define GAP_STBKEY_VID_ROTATE               "VID_ROTATE"
 #define GAP_STBKEY_VID_OPACITY              "VID_OPACITY"
 #define GAP_STBKEY_VID_ZOOM_X               "VID_ZOOM_X"
 #define GAP_STBKEY_VID_ZOOM_Y               "VID_ZOOM_Y"
diff --git a/gap/gap_story_vthumb.c b/gap/gap_story_vthumb.c
index 58b1d0a..ef9a24f 100644
--- a/gap/gap_story_vthumb.c
+++ b/gap/gap_story_vthumb.c
@@ -839,7 +839,7 @@ p_story_vthumb_elem_fetch(GapStbMainGlobalParams *sgpp
   {
     if(sgpp->vthumb_prefetch_in_progress != GAP_VTHUMB_PREFETCH_NOT_ACTIVE)
     {
-      /* at this point an implicite cancel of video thumbnail prefetch
+      /* at this point an implicit cancel of video thumbnail prefetch
        * is detected.
        */
       if(gap_debug)
@@ -902,7 +902,7 @@ p_story_vthumb_elem_fetch(GapStbMainGlobalParams *sgpp
     return(NULL);
   }
   
-  /* Videothumbnail not known yet,
+  /* Video thumbnail not known yet,
    * we try to create it now
    */
   switch(velem->vt_type)
@@ -1001,7 +1001,7 @@ gap_story_vthumb_elem_fetch(GapStbMainGlobalParams *sgpp
 /* ------------------------------
  * gap_story_vthumb_fetch_thdata
  * ------------------------------
- * RETURN a copy of the videothumbnail data
+ * RETURN a copy of the video thumbnail data
  *        or NULL if fetch was not successful
  *        the caller is responsible to g_free the returned data
  *        after usage.
@@ -1052,7 +1052,7 @@ gap_story_vthumb_fetch_thdata(GapStbMainGlobalParams *sgpp
 /* --------------------------------------
  * gap_story_vthumb_fetch_thdata_no_store
  * --------------------------------------
- * RETURN a pointer of the videothumbnail data
+ * RETURN a pointer of the video thumbnail data
  *        or thumbnail data read from file (indicated by file_read_flag = TRUE)
  * the caller must g_free the returned data if file_read_flag = TRUE
  * but MUST NOT g_free the returned data if file_read_flag = FALSE
@@ -1124,20 +1124,20 @@ gap_story_vthumb_fetch_thdata_no_store(GapStbMainGlobalParams *sgpp
 
   if(sgpp->vthumb_prefetch_in_progress != GAP_VTHUMB_PREFETCH_NOT_ACTIVE)
   {
-      /* at this point an implicite cancel of video thumbnail prefetch
+      /* at this point an implicit cancel of video thumbnail prefetch
        * is detected.
-       * - one option is to render default icon, and restert the prefetch 
+       * - one option is to render default icon, and restart the prefetch 
        *   via GAP_VTHUMB_PREFETCH_RESTART_REQUEST
        *   (because the storyboard may have changed since prefetch was started
        *    note that prefetch will be very quick for all clips where vthumb is already present
        *    from the cancelled previous prefetch cycle)
-       * - an other (not implemented) option is to cancel prefetch and implicite turn off auto_vthumb mode
+       * - an other (not implemented) option is to cancel prefetch and implicitly turn off auto_vthumb mode
        */
       sgpp->vthumb_prefetch_in_progress = GAP_VTHUMB_PREFETCH_RESTART_REQUEST;
       return (NULL);
   }
 
-  /* Videothumbnail not known yet,
+  /* Video thumbnail not known yet,
    * we try to create it now
    * (but dont add it tho global vthumb list)
    */
diff --git a/gap/gap_thumbnail.c b/gap/gap_thumbnail.c
index 4e0f905..4f28efe 100644
--- a/gap/gap_thumbnail.c
+++ b/gap/gap_thumbnail.c
@@ -163,13 +163,13 @@ p_gap_filename_to_uri(const char *filename)
  * and save the pixbuf for the new filename_dst.
  *
  * The following Information Tags in the resulting copy are updated
- * to match filename_dst. (this is done implicite by the gimp_thumbnail_* procedures)
+ * to match filename_dst. (this is done implicitly by the gimp_thumbnail_* procedures)
  *   tEXt::Thumb::URI
  *   tEXt::Thumb::MTime
  *   tEXt::Description
  *   tEXt::Thumb::Size
  *
- * The following Tags are copied explicite from the source thumbnail:
+ * The following Tags are copied explicit from the source thumbnail:
  *   tEXt::Thumb::Image::Width
  *   tEXt::Thumb::Image::Height
  *   tEXt::Thumb::X-GIMP::Type
diff --git a/gap/gap_vex_dialog.c b/gap/gap_vex_dialog.c
index 4924e21..d2674c9 100644
--- a/gap/gap_vex_dialog.c
+++ b/gap/gap_vex_dialog.c
@@ -2533,7 +2533,7 @@ gap_vex_dlg_create_mw__main_window (GapVexMainGlobalParams *gpp)
   gtk_box_pack_start (GTK_BOX (hbox2), checkbutton, TRUE, TRUE, 0);
   gimp_help_set_help_data (checkbutton
                           , _("On: extract grayscale mask (generated by bluebox)\n"
-                              "Off: extract color frames 1.1")
+                              "Off: extract color frames 1:1")
                           , NULL);
   g_signal_connect (G_OBJECT (checkbutton), "toggled",
                       G_CALLBACK (on_mw__checkbutton_graymask_toggled),
diff --git a/gap/gap_video_index_creator.c b/gap/gap_video_index_creator.c
index ac0cdb3..92b01db 100644
--- a/gap/gap_video_index_creator.c
+++ b/gap/gap_video_index_creator.c
@@ -932,7 +932,7 @@ p_vindex_dialog(VindexValues *val_ptr)
                                               "and no critical timecode steps are detected in the probereads so far.\n"
                                               "\nWARNING: positioning via native seek may not work exact in case critical "
                                               "timecode steps were not detected in the probereads.")
-                                         , N_("Create video index. Requires unconditional full scann of all frames."
+                                         , N_("Create video index. Requires unconditional full scan of all frames."
                                               "Native seek is enabled only in case all timecodes are OK.")
                                          };
 
@@ -991,7 +991,7 @@ p_vindex_dialog(VindexValues *val_ptr)
   ii++; gap_arr_arg_init(&argv[ii], GAP_ARR_WGT_FLT); ii_percentage_smart_mode = ii;
   argv[ii].constraint = TRUE;
   argv[ii].label_txt = _("Percentage:");
-  argv[ii].help_txt  = _("stop scann after percentage reached and no unplausible timecode was detected so far (only relevant in smart mode)");
+  argv[ii].help_txt  = _("stop scan after percentage reached and no unplausible timecode was detected so far (only relevant in smart mode)");
   argv[ii].flt_min   =  1.0;
   argv[ii].flt_max   = (gint)100.0;
   argv[ii].flt_step  =  1.0;
@@ -1444,7 +1444,7 @@ p_create_progress_window(GapVideoIndexCreatorProgressParams *vipp)
     gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
     gtk_widget_show (button);
     gimp_help_set_help_data (button
-                   , _("Cancel video access if in progress and disable automatic videothumbnails")
+                   , _("Cancel video access if in progress and disable automatic video thumbnails")
                    , NULL);
     g_signal_connect (G_OBJECT (button), "clicked",
                     G_CALLBACK (p_cancel_button_cb),
diff --git a/gap/gap_wr_color_balance.c b/gap/gap_wr_color_balance.c
new file mode 100644
index 0000000..5fea509
--- /dev/null
+++ b/gap/gap_wr_color_balance.c
@@ -0,0 +1,749 @@
+/* gap_wr_color_balance.c
+ * 2010.08.23 hof (Wolfgang Hofer)
+ *
+ *  Wrapper Plugin for GIMP Hue Saturation tool
+ *
+ * Warning: This is just a QUICK HACK to enable
+ *          Animated Filterapply in conjunction with the
+ *          GIMP Hue Saturation Tool.
+ *
+ *  It provides a primitive Dialog Interface where you
+ *  can call the Hue Saturation Tool
+ *
+ *  Further it has an Interface to 'Run_with_last_values'
+ *  and an Iterator Procedure.
+ *  (This enables the 'Animated Filter Call' from
+ *   the GAP's Menu Filters->Filter all Layers)
+ *
+ */
+/* 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
+ *  (2004/01/15)  v1.3.24a  hof: adapted for gimp-1.3.x and gtk+2.2 API
+ *  (2002/10/27)  v1.03  hof: - appear in menu (for filtermacro)
+ *  (2002/01/01)  v1.02  hof: - created
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <gtk/gtk.h>
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "gap-intl.h"
+
+/* Defines */
+#define PLUG_IN_NAME        "plug-in-wr-colorbalance"
+#define PLUG_IN_IMAGE_TYPES "RGB*, GRAY*"
+#define PLUG_IN_AUTHOR      "Wolfgang Hofer (hof gimp org)"
+#define PLUG_IN_COPYRIGHT   "Wolfgang Hofer"
+#define PLUG_IN_DESCRIPTION "Wrapper call for GIMP Color Balance Tool"
+
+#define PLUG_IN_ITER_NAME       "plug-in-wr-colorbalance-Iterator"
+#define PLUG_IN_DATA_ITER_FROM  "plug-in-wr-colorbalance-ITER-FROM"
+#define PLUG_IN_DATA_ITER_TO    "plug-in-wr-colorbalance-ITER-TO"
+#define PLUG_IN_HELP_ID         "plug-in-wr-colorbalance"
+
+
+
+typedef struct
+{
+  gint32   transfer_mode;
+  gboolean preserve_lum;
+  gdouble  cyan_red;
+  gdouble  magenta_green;
+  gdouble  yellow_blue;
+} wr_color_balance_val_t;
+
+
+typedef struct _WrDialog WrDialog;
+
+struct _WrDialog
+{
+  gint          run;
+  gint          show_progress;
+  GtkWidget       *shell;
+  GtkWidget       *radio_highlights;
+  GtkWidget       *radio_midtones;
+  GtkWidget       *radio_shadows;
+  wr_color_balance_val_t *vals;
+};
+
+
+WrDialog *do_dialog (wr_color_balance_val_t *);
+static void  query (void);
+static void run(const gchar *name
+           , gint nparams
+           , const GimpParam *param
+           , gint *nreturn_vals
+           , GimpParam **return_vals);
+
+/* Global Variables */
+GimpPlugInInfo PLUG_IN_INFO =
+{
+  NULL,   /* init_proc  */
+  NULL,   /* quit_proc  */
+  query,  /* query_proc */
+  run     /* run_proc   */
+};
+
+
+gint  gap_debug = 0;  /* 0.. no debug, 1 .. print debug messages */
+
+/* --------------
+ * procedures
+ * --------------
+ */
+
+
+
+/*
+ * Delta Calculations for the iterator
+ */
+static void
+p_delta_gdouble (gdouble  *val,
+                 gdouble   val_from,
+                 gdouble   val_to,
+                 gint32   total_steps,
+                 gdouble  current_step)
+{
+    gdouble     delta;
+
+    if(total_steps < 1) return;
+
+    delta = ((gdouble)(val_to - val_from) / (gdouble)total_steps) * ((gdouble)total_steps - current_step);
+    *val  = val_from + delta;
+}
+
+static void
+p_delta_gint32 (gint32  *val,
+                 gint32   val_from,
+                 gint32   val_to,
+                 gint32   total_steps,
+                 gdouble  current_step)
+{
+    gdouble     delta;
+
+    if(total_steps < 1) return;
+
+    delta = ((gdouble)(val_to - val_from) / (gdouble)total_steps) * ((gdouble)total_steps - current_step);
+    *val  = val_from + delta;
+}
+
+
+static void
+p_run_color_balance_tool(gint32 drawable_id, wr_color_balance_val_t *cuvals)
+{
+  gboolean success;
+
+  if(gap_debug)
+  {
+     printf("p_run_color_balance_tool: drawable_id :%d\n", (int)drawable_id);
+     printf("p_run_color_balance_tool:  transfer_mode:%d\n", (int)cuvals->transfer_mode);
+     printf("p_run_color_balance_tool:  preserve_lum:%d\n", (int)cuvals->preserve_lum);
+     printf("p_run_color_balance_tool:  cyan_red:%f\n", (float)cuvals->cyan_red);
+     printf("p_run_color_balance_tool:  magenta_green:%f\n", (float)cuvals->magenta_green);
+     printf("p_run_color_balance_tool:  yellow_blue:%f\n", (float)cuvals->yellow_blue);
+  }
+
+  success = gimp_color_balance(drawable_id
+                             , cuvals->transfer_mode   /* GimpTransferMode */
+                             , cuvals->preserve_lum
+                             , cuvals->cyan_red
+                             , cuvals->magenta_green
+                             , cuvals->yellow_blue
+                             );
+
+
+}
+
+MAIN ()
+
+static void
+query (void)
+{
+  static GimpParamDef args[] = {
+                  { GIMP_PDB_INT32,      "run_mode", "Interactive, non-interactive"},
+                  { GIMP_PDB_IMAGE,      "image", "Input image" },
+                  { GIMP_PDB_DRAWABLE,   "drawable", "Input drawable (must be a layer without layermask)"},
+                  { GIMP_PDB_INT32,      "transfer_mode", "Transfer mode { SHADOWS (0), MIDTONES (1), HIGHLIGHTS (2) }"},
+                  { GIMP_PDB_INT32,      "preserve_lum", "Preserve luminosity values at each pixel (TRUE or FALSE)"},
+                  { GIMP_PDB_FLOAT,      "cyan_red", "Cyan-Red color balance (-100 <= cyan-red <= 100)"},
+                  { GIMP_PDB_FLOAT,      "magenta_green", "Magenta-Green color balance (-100 <= magenta-green <= 100)"},
+                  { GIMP_PDB_FLOAT,      "yellow_blue", "Yellow-Blue color balance (-100 <= yellow-blue <= 100)"},
+  };
+  static int nargs = sizeof(args) / sizeof(args[0]);
+
+  static GimpParamDef return_vals[] =
+  {
+    { GIMP_PDB_DRAWABLE, "the_drawable", "the handled drawable" }
+  };
+  static int nreturn_vals = sizeof(return_vals) / sizeof(return_vals[0]);
+
+
+  static GimpParamDef args_iter[] =
+  {
+    {GIMP_PDB_INT32, "run_mode", "non-interactive"},
+    {GIMP_PDB_INT32, "total_steps", "total number of steps (# of layers-1 to apply the related plug-in)"},
+    {GIMP_PDB_FLOAT, "current_step", "current (for linear iterations this is the layerstack position, otherwise some value inbetween)"},
+    {GIMP_PDB_INT32, "len_struct", "length of stored data structure with id is equal to the plug_in  proc_name"},
+  };
+  static int nargs_iter = sizeof(args_iter) / sizeof(args_iter[0]);
+
+  static GimpParamDef *return_iter = NULL;
+  static int nreturn_iter = 0;
+
+  gimp_plugin_domain_register (GETTEXT_PACKAGE, LOCALEDIR);
+
+  /* the actual installation of the bend plugin */
+  gimp_install_procedure (PLUG_IN_NAME,
+                          PLUG_IN_DESCRIPTION,
+                         "This Plugin is a wrapper to call the GIMP Color Balance Tool (gimp_color_balance)"
+                         " it has a simplified Dialog (without preview) where you can enter the parameters"
+                         " this wrapper is useful for animated filtercalls and provides "
+                         " a PDB interface that runs in GIMP_RUN_WITH_LAST_VALUES mode"
+                         " and also provides an Iterator Procedure for animated calls"
+                          ,
+                          PLUG_IN_AUTHOR,
+                          PLUG_IN_COPYRIGHT,
+                          GAP_VERSION_WITH_DATE,
+                          N_("Color Balance..."),
+                          PLUG_IN_IMAGE_TYPES,
+                          GIMP_PLUGIN,
+                          nargs,
+                          nreturn_vals,
+                          args,
+                          return_vals);
+
+
+  /* the installation of the Iterator extension for the bend plugin */
+  gimp_install_procedure (PLUG_IN_ITER_NAME,
+                          "This extension calculates the modified values for one iterationstep for the call of gimp_color_balance",
+                          "",
+                          PLUG_IN_AUTHOR,
+                          PLUG_IN_COPYRIGHT,
+                          GAP_VERSION_WITH_DATE,
+                          NULL,    /* do not appear in menus */
+                          NULL,
+                          GIMP_PLUGIN,
+                          nargs_iter, nreturn_iter,
+                          args_iter, return_iter);
+
+  {
+    /* Menu names */
+    const char *menupath_image_video_layer_colors = N_("<Image>/Video/Layer/Colors/");
+
+    gimp_plugin_menu_register (PLUG_IN_NAME, menupath_image_video_layer_colors);
+  }
+}
+
+
+
+static void
+run(const gchar *name
+           , gint nparams
+           , const GimpParam *param
+           , gint *nreturn_vals
+           , GimpParam **return_vals)
+{
+  wr_color_balance_val_t l_cuvals;
+  WrDialog   *wcd = NULL;
+
+  gint32    l_image_id = -1;
+  gint32    l_drawable_id = -1;
+  gint32    l_handled_drawable_id = -1;
+
+  /* Get the runmode from the in-parameters */
+  GimpRunMode run_mode = param[0].data.d_int32;
+
+  /* status variable, use it to check for errors in invocation usualy only
+     during non-interactive calling */
+  GimpPDBStatusType status = GIMP_PDB_SUCCESS;
+
+  /*always return at least the status to the caller. */
+  static GimpParam values[2];
+
+
+  INIT_I18N();
+
+  /* initialize the return of the status */
+  values[0].type = GIMP_PDB_STATUS;
+  values[0].data.d_status = status;
+  values[1].type = GIMP_PDB_DRAWABLE;
+  values[1].data.d_int32 = -1;
+  *nreturn_vals = 2;
+  *return_vals = values;
+
+  /* the Iterator Stuff */
+  if (strcmp (name, PLUG_IN_ITER_NAME) == 0)
+  {
+     gint32  len_struct;
+     gint32  total_steps;
+     gdouble current_step;
+     wr_color_balance_val_t   cval;                  /* current values while iterating */
+     wr_color_balance_val_t   cval_from, cval_to;    /* start and end values */
+
+     /* Iterator procedure for animated calls is usually called from
+      * "plug_in_gap_layers_run_animfilter"
+      * (always run noninteractive)
+      */
+     if ((run_mode == GIMP_RUN_NONINTERACTIVE) && (nparams == 4))
+     {
+       total_steps  =  param[1].data.d_int32;
+       current_step =  param[2].data.d_float;
+       len_struct   =  param[3].data.d_int32;
+
+       if(len_struct == sizeof(cval))
+       {
+         /* get _FROM and _TO data,
+          * This data was stored by plug_in_gap_layers_run_animfilter
+          */
+          gimp_get_data(PLUG_IN_DATA_ITER_FROM, &cval_from);
+          gimp_get_data(PLUG_IN_DATA_ITER_TO,   &cval_to);
+          memcpy(&cval, &cval_from, sizeof(cval));
+
+          p_delta_gint32 (&cval.transfer_mode,  cval_from.transfer_mode,  cval_to.transfer_mode,  total_steps, current_step);
+          p_delta_gdouble(&cval.cyan_red, cval_from.cyan_red, cval_to.cyan_red, total_steps, current_step);
+          p_delta_gdouble(&cval.magenta_green,  cval_from.magenta_green,  cval_to.magenta_green,  total_steps, current_step);
+          p_delta_gdouble(&cval.yellow_blue, cval_from.yellow_blue, cval_to.yellow_blue, total_steps, current_step);
+
+          gimp_set_data(PLUG_IN_NAME, &cval, sizeof(cval));
+       }
+       else status = GIMP_PDB_CALLING_ERROR;
+     }
+     else status = GIMP_PDB_CALLING_ERROR;
+
+     values[0].data.d_status = status;
+     return;
+  }
+
+
+
+  /* get image and drawable */
+  l_image_id = param[1].data.d_int32;
+  l_drawable_id = param[2].data.d_drawable;
+
+  if(status == GIMP_PDB_SUCCESS)
+  {
+    /* how are we running today? */
+    switch (run_mode)
+     {
+      case GIMP_RUN_INTERACTIVE:
+        /* Initial values */
+        l_cuvals.transfer_mode = 0;
+        l_cuvals.preserve_lum = FALSE;
+        l_cuvals.cyan_red = 0;
+        l_cuvals.magenta_green = 0;
+        l_cuvals.yellow_blue = 0;
+
+        /* Get information from the dialog */
+        wcd = do_dialog(&l_cuvals);
+        wcd->show_progress = TRUE;
+        break;
+
+      case GIMP_RUN_NONINTERACTIVE:
+        /* check to see if invoked with the correct number of parameters */
+        if (nparams >= 7)
+        {
+           wcd = g_malloc (sizeof (WrDialog));
+           wcd->run = TRUE;
+           wcd->show_progress = FALSE;
+
+           l_cuvals.transfer_mode  = param[3].data.d_int32;
+           l_cuvals.preserve_lum   = param[4].data.d_int32;
+           l_cuvals.cyan_red       = param[5].data.d_float;
+           l_cuvals.magenta_green  = param[6].data.d_float;
+           l_cuvals.yellow_blue    = param[7].data.d_float;
+        }
+        else
+        {
+          status = GIMP_PDB_CALLING_ERROR;
+        }
+        break;
+
+      case GIMP_RUN_WITH_LAST_VALS:
+        wcd = g_malloc (sizeof (WrDialog));
+        wcd->run = TRUE;
+        wcd->show_progress = TRUE;
+        /* Possibly retrieve data from a previous run */
+        gimp_get_data (PLUG_IN_NAME, &l_cuvals);
+        break;
+
+      default:
+        break;
+    }
+  }
+
+  if (wcd == NULL)
+  {
+    status = GIMP_PDB_EXECUTION_ERROR;
+  }
+
+  if (status == GIMP_PDB_SUCCESS)
+  {
+     /* Run the main function */
+     if(wcd->run)
+     {
+        gimp_image_undo_group_start (l_image_id);
+        p_run_color_balance_tool(l_drawable_id, &l_cuvals);
+        l_handled_drawable_id = l_drawable_id;
+        gimp_image_undo_group_end (l_image_id);
+
+        /* Store variable states for next run */
+        if (run_mode == GIMP_RUN_INTERACTIVE)
+        {
+          gimp_set_data(PLUG_IN_NAME, &l_cuvals, sizeof(l_cuvals));
+        }
+     }
+     else
+     {
+       status = GIMP_PDB_EXECUTION_ERROR;       /* dialog ended with cancel button */
+     }
+
+     /* If run mode is interactive, flush displays, else (script) don't
+        do it, as the screen updates would make the scripts slow */
+     if (run_mode != GIMP_RUN_NONINTERACTIVE)
+     {
+       gimp_displays_flush ();
+     }
+  }
+  values[0].data.d_status = status;
+  values[1].data.d_int32 = l_handled_drawable_id;   /* return the id of handled layer */
+
+}       /* end run */
+
+
+/*
+ * DIALOG and callback stuff
+ */
+
+
+static void
+radio_callback(GtkWidget *wgt, gpointer user_data)
+{
+  WrDialog *wcd;
+
+  if(gap_debug) printf("radio_callback: START\n");
+  wcd = (WrDialog*)user_data;
+  if(wcd != NULL)
+  {
+    if(wcd->vals != NULL)
+    {
+       if(wgt == wcd->radio_highlights)   { wcd->vals->transfer_mode = 2; }
+       if(wgt == wcd->radio_midtones)     { wcd->vals->transfer_mode = 1; }
+       if(wgt == wcd->radio_shadows)      { wcd->vals->transfer_mode = 0; }
+
+    }
+  }
+}
+
+
+/* --------------------------------------
+ * on_gboolean_button_update
+ * --------------------------------------
+ */
+static void
+on_gboolean_button_update (GtkWidget *widget,
+                           gpointer   data)
+{
+  gint *toggle_val = (gint *) data;
+
+  if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)))
+    *toggle_val = TRUE;
+  else
+    *toggle_val = FALSE;
+
+  gimp_toggle_button_sensitive_update (GTK_TOGGLE_BUTTON (widget));
+}
+
+
+/* ---------------------------------
+ * wr_color_balance_response
+ * ---------------------------------
+ */
+static void
+wr_color_balance_response (GtkWidget *widget,
+                 gint       response_id,
+                 WrDialog *wcd)
+{
+  GtkWidget *dialog;
+
+  switch (response_id)
+  {
+    case GTK_RESPONSE_OK:
+      if(wcd)
+      {
+        if (GTK_WIDGET_VISIBLE (wcd->shell))
+          gtk_widget_hide (wcd->shell);
+
+        wcd->run = TRUE;
+      }
+
+    default:
+      dialog = NULL;
+      if(wcd)
+      {
+        dialog = wcd->shell;
+        if(dialog)
+        {
+          wcd->shell = NULL;
+          gtk_widget_destroy (dialog);
+        }
+      }
+      gtk_main_quit ();
+      break;
+  }
+}  /* end wr_color_balance_response */
+
+
+WrDialog *
+do_dialog (wr_color_balance_val_t *cuvals)
+{
+  WrDialog *wcd;
+  GtkWidget  *vbox;
+
+  GtkWidget *dialog1;
+  GtkWidget *dialog_vbox1;
+  GtkWidget *frame1;
+  GtkWidget *hbox1;
+  GtkWidget *vbox1;
+  GtkWidget *label1;
+  GSList *vbox1_group = NULL;
+  GtkWidget *radiobutton1;
+  GtkWidget *radiobutton2;
+  GtkWidget *radiobutton3;
+  GtkWidget *table1;
+  GtkWidget *label2;
+  GtkWidget *label3;
+  GtkWidget *label4;
+  GtkObject *spinbutton_cyan_red_adj;
+  GtkWidget *spinbutton_cyan_red;
+  GtkObject *spinbutton_magenta_green_adj;
+  GtkWidget *spinbutton_magenta_green;
+  GtkObject *spinbutton_yellow_blue_adj;
+  GtkWidget *spinbutton_yellow_blue;
+  GtkWidget *dialog_action_area1;
+  GtkWidget *checkbutton;
+  gint       row;
+
+
+  /* Init UI  */
+  gimp_ui_init ("wr_color_balance", FALSE);
+
+
+  /*  The dialog1  */
+  wcd = g_malloc (sizeof (WrDialog));
+  wcd->run = FALSE;
+  wcd->vals = cuvals;
+
+  /*  The dialog1 and main vbox  */
+  dialog1 = gimp_dialog_new (_("Color-Balance"), "color_balance_wrapper",
+                               NULL, 0,
+                               gimp_standard_help_func, PLUG_IN_HELP_ID,
+
+                               GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+                               GTK_STOCK_OK,     GTK_RESPONSE_OK,
+                               NULL);
+
+  wcd->shell = dialog1;
+
+
+  /*
+   * g_object_set_data (G_OBJECT (dialog1), "dialog1", dialog1);
+   * gtk_window_set_title (GTK_WINDOW (dialog1), _("dialog1"));
+   */
+
+
+  g_signal_connect (G_OBJECT (dialog1), "response",
+                      G_CALLBACK (wr_color_balance_response),
+                      wcd);
+
+  /* the vbox */
+  vbox = gtk_vbox_new (FALSE, 2);
+  gtk_container_set_border_width (GTK_CONTAINER (vbox), 2);
+  gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog1)->vbox), vbox,
+                      TRUE, TRUE, 0);
+  gtk_widget_show (vbox);
+
+  dialog_vbox1 = GTK_DIALOG (dialog1)->vbox;
+  g_object_set_data (G_OBJECT (dialog1), "dialog_vbox1", dialog_vbox1);
+  gtk_widget_show (dialog_vbox1);
+
+
+
+  /* the frame */
+  frame1 = gimp_frame_new (_("Select Range to Adjust"));
+
+  gtk_widget_show (frame1);
+  gtk_box_pack_start (GTK_BOX (dialog_vbox1), frame1, TRUE, TRUE, 0);
+  gtk_container_set_border_width (GTK_CONTAINER (frame1), 2);
+
+  hbox1 = gtk_hbox_new (FALSE, 0);
+
+  gtk_widget_show (hbox1);
+  gtk_container_add (GTK_CONTAINER (frame1), hbox1);
+  gtk_container_set_border_width (GTK_CONTAINER (hbox1), 4);
+
+  vbox1 = gtk_vbox_new (FALSE, 0);
+
+  gtk_widget_show (vbox1);
+  gtk_box_pack_start (GTK_BOX (hbox1), vbox1, TRUE, TRUE, 0);
+
+
+  /* Transfer Mode the radio buttons */
+  radiobutton1 = gtk_radio_button_new_with_label (vbox1_group, _("Shadows"));
+  vbox1_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (radiobutton1));
+  gtk_widget_show (radiobutton1);
+  gtk_box_pack_start (GTK_BOX (vbox1), radiobutton1, FALSE, FALSE, 0);
+
+  radiobutton2 = gtk_radio_button_new_with_label (vbox1_group, _("Midtones"));
+  vbox1_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (radiobutton2));
+  gtk_widget_show (radiobutton2);
+  gtk_box_pack_start (GTK_BOX (vbox1), radiobutton2, FALSE, FALSE, 0);
+
+  radiobutton3 = gtk_radio_button_new_with_label (vbox1_group, _("Highlights"));
+  vbox1_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (radiobutton3));
+  gtk_widget_show (radiobutton3);
+  gtk_box_pack_start (GTK_BOX (vbox1), radiobutton3, FALSE, FALSE, 0);
+
+
+
+  /* the frame */
+  frame1 = gimp_frame_new (_("Adjust Color Levels"));
+
+  gtk_widget_show (frame1);
+  gtk_box_pack_start (GTK_BOX (dialog_vbox1), frame1, TRUE, TRUE, 0);
+  gtk_container_set_border_width (GTK_CONTAINER (frame1), 2);
+
+
+  vbox1 = gtk_vbox_new (FALSE, 0);
+  gtk_container_add (GTK_CONTAINER (frame1), vbox1);
+  gtk_widget_show (vbox1);
+
+
+
+  /* table1 for spinbuttons  */
+  table1 = gtk_table_new (4, 2, FALSE);
+  gtk_widget_show (table1);
+  gtk_box_pack_start (GTK_BOX (vbox1), table1, TRUE, TRUE, 0);
+  gtk_container_set_border_width (GTK_CONTAINER (table1), 4);
+
+
+
+  /* table1 for spinbuttons  */
+
+  row = 0;
+  
+  label2 = gtk_label_new (_("Cyan/Red:"));
+  gtk_widget_show (label2);
+  gtk_table_attach (GTK_TABLE (table1), label2, 0, 1, row, row+1,
+                    (GtkAttachOptions) (GTK_FILL),
+                    (GtkAttachOptions) (0), 0, 0);
+  gtk_misc_set_alignment (GTK_MISC (label2), 0, 0.5);
+  spinbutton_cyan_red_adj = gtk_adjustment_new (0, -100, 100, 1, 10, 0);
+  spinbutton_cyan_red = gtk_spin_button_new (GTK_ADJUSTMENT (spinbutton_cyan_red_adj), 1, 0);
+  gtk_widget_show (spinbutton_cyan_red);
+  gtk_table_attach (GTK_TABLE (table1), spinbutton_cyan_red, 1, 2, row, row+1,
+                    (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
+                    (GtkAttachOptions) (0), 0, 0);
+  row++;
+  
+  label3 = gtk_label_new (_("Magenta/Green:"));
+  gtk_widget_show (label3);
+  gtk_table_attach (GTK_TABLE (table1), label3, 0, 1, row, row+1,
+                    (GtkAttachOptions) (GTK_FILL),
+                    (GtkAttachOptions) (0), 0, 0);
+  gtk_misc_set_alignment (GTK_MISC (label3), 0, 0.5);
+
+
+  spinbutton_magenta_green_adj = gtk_adjustment_new (0, -100, 100, 1, 10, 0);
+  spinbutton_magenta_green = gtk_spin_button_new (GTK_ADJUSTMENT (spinbutton_magenta_green_adj), 1, 0);
+  gtk_widget_show (spinbutton_magenta_green);
+  gtk_table_attach (GTK_TABLE (table1), spinbutton_magenta_green, 1, 2, row, row+1,
+                    (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
+                    (GtkAttachOptions) (0), 0, 0);
+
+
+  row++;
+  
+  label4 = gtk_label_new (_("Yellow/Blue:"));
+  gtk_widget_show (label4);
+  gtk_table_attach (GTK_TABLE (table1), label4, 0, 1, row, row+1,
+                    (GtkAttachOptions) (GTK_FILL),
+                    (GtkAttachOptions) (0), 0, 0);
+  gtk_misc_set_alignment (GTK_MISC (label4), 0, 0.5);
+
+
+  spinbutton_yellow_blue_adj = gtk_adjustment_new (0, -100, 100, 1, 10, 0);
+  spinbutton_yellow_blue = gtk_spin_button_new (GTK_ADJUSTMENT (spinbutton_yellow_blue_adj), 1, 0);
+  gtk_widget_show (spinbutton_yellow_blue);
+  gtk_table_attach (GTK_TABLE (table1), spinbutton_yellow_blue, 1, 2, row, row+1,
+                    (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
+                    (GtkAttachOptions) (0), 0, 0);
+
+
+  row++;
+
+  checkbutton = gtk_check_button_new_with_label ( _("Preserve luminosity"));
+  gtk_widget_show (checkbutton);
+  gtk_table_attach( GTK_TABLE(table1), checkbutton, 0, 2, row, row+1,
+                    GTK_FILL, 0, 0, 0 );
+  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (checkbutton), cuvals->preserve_lum);
+  g_signal_connect (checkbutton, "toggled",
+                    G_CALLBACK (on_gboolean_button_update),
+                    &cuvals->preserve_lum);
+
+
+
+
+  dialog_action_area1 = GTK_DIALOG (dialog1)->action_area;
+  gtk_widget_show (dialog_action_area1);
+  gtk_container_set_border_width (GTK_CONTAINER (dialog_action_area1), 10);
+
+
+
+
+
+  wcd->radio_shadows     = radiobutton1;
+  wcd->radio_midtones    = radiobutton2;
+  wcd->radio_highlights  = radiobutton3;
+
+
+  /* signals */
+  g_signal_connect (G_OBJECT (wcd->radio_shadows),     "clicked",  G_CALLBACK (radio_callback), wcd);
+  g_signal_connect (G_OBJECT (wcd->radio_midtones),    "clicked",  G_CALLBACK (radio_callback), wcd);
+  g_signal_connect (G_OBJECT (wcd->radio_highlights),  "clicked",  G_CALLBACK (radio_callback), wcd);
+
+  g_signal_connect (G_OBJECT (spinbutton_cyan_red_adj),      "value_changed",  G_CALLBACK (gimp_double_adjustment_update), &cuvals->cyan_red);
+  g_signal_connect (G_OBJECT (spinbutton_magenta_green_adj), "value_changed",  G_CALLBACK (gimp_double_adjustment_update), &cuvals->magenta_green);
+  g_signal_connect (G_OBJECT (spinbutton_yellow_blue_adj),   "value_changed",  G_CALLBACK (gimp_double_adjustment_update), &cuvals->yellow_blue);
+
+
+  gtk_widget_show (dialog1);
+
+  gtk_main ();
+  gdk_flush ();
+
+  return wcd;
+}
diff --git a/libgapbase/gap_val_file.c b/libgapbase/gap_val_file.c
index c3e3a2b..811fdbd 100644
--- a/libgapbase/gap_val_file.c
+++ b/libgapbase/gap_val_file.c
@@ -238,6 +238,19 @@ p_write_keylist_value(FILE *fp, GapValKeyList *keyptr, const char *term_str)
   
   switch(keyptr->dataype)
   {
+    case GAP_VAL_GINT:
+      {
+        gint *val_ptr;
+      
+        val_ptr = (gint *)keyptr->val_ptr;
+        fprintf(fp, "%s%d%s %s\n"
+               , keyptr->keyword   /* "(keyword " */
+               , (int)*val_ptr          /* value */
+               , term_ptr
+               , keyptr->comment
+               );
+      }
+      break;
     case GAP_VAL_GINT32:
       {
         gint32 *val_ptr;
@@ -527,6 +540,14 @@ gap_val_scann_filevalues(GapValKeyList *keylist, const char *filename)
                l_cnt_keys++;
                switch(keyptr->dataype)
                {
+                 case GAP_VAL_GINT:
+                   {
+                      gint *val_ptr;
+                      
+                      val_ptr = (gint *)keyptr->val_ptr;
+                      *val_ptr = atol(&txf_ptr->line[l_len]);
+                   }
+                   break;
                  case GAP_VAL_GINT32:
                    {
                       gint32 *val_ptr;
@@ -641,7 +662,7 @@ gap_val_scann_filevalues(GapValKeyList *keylist, const char *filename)
                break;
              }
           }  /* end for keylist loop */
-      } /* end for text lines scann loop */
+      } /* end for text lines scan loop */
       if(txf_ptr_root)
       {
         gap_val_free_textfile_lines(txf_ptr_root);
diff --git a/libgapbase/gap_val_file.h b/libgapbase/gap_val_file.h
index e21a2bd..cb2d3e1 100644
--- a/libgapbase/gap_val_file.h
+++ b/libgapbase/gap_val_file.h
@@ -46,6 +46,7 @@ typedef enum
 , GAP_VAL_STRING
 , GAP_VAL_GBOOLEAN
 , GAP_VAL_G32BOOLEAN
+, GAP_VAL_GINT
 } GapValDataType;
 
 
diff --git a/libgapvidapi/gap_vid_api_ffmpeg.c b/libgapvidapi/gap_vid_api_ffmpeg.c
index b408a27..d38fe96 100644
--- a/libgapvidapi/gap_vid_api_ffmpeg.c
+++ b/libgapvidapi/gap_vid_api_ffmpeg.c
@@ -3,6 +3,7 @@
  * GAP Video read API implementation of libavformat/lbavcodec (also known as FFMPEG)
  * based wrappers to read various videofile formats
  *
+ * 2010.07.31   update to support both ffmpeg-0.5 and ffmpeg-0.6
  * 2007.11.04   update to ffmpeg svn snapshot 2007.10.31
  *                bugfix: selftest sometimes did not detect variable timecodes.
  * 2007.04.04   update to ffmpeg svn snapshot 2007.04.04,
@@ -11,7 +12,7 @@
  * 2007.03.12   update to ffmpeg svn snapshot 2007.03.12,
  * 2005.02.05   update to ffmpeg-0.4.9 (basically works now)
  * 2004.10.24   workaround initial frameread and reopen for detection of yuv_buff_pix_fmt
- *              of the active codec. (works only after the 1.st frame was read) 
+ *              of the active codec. (works only after the 1.st frame was read)
  * 2004.04.12   vindex bugfix seek high framnumbers sometimes used wrong (last index)
  * 2004.03.07   vindex
  * 2004.02.29   hof fptr_progress_callback
@@ -27,12 +28,37 @@
  */
 #ifdef ENABLE_GVA_LIBAVFORMAT
 #include "avformat.h"
+#include "swscale.h"
 #include "stdlib.h"
 #include "gap_val_file.h"
 #include "gap_base.h"
 #include "audioconvert.h"
 #include "imgconvert.h"
 
+
+/* start ffmpeg 0.5 / 0.6 support */
+#if LIBAVCODEC_VERSION_MAJOR < 52
+#define GAP_USES_OLD_FFMPEG_0_5
+#endif
+#if LIBAVCODEC_VERSION_MAJOR == 52
+#if LIBAVCODEC_VERSION_MINOR <= 20
+#define GAP_USES_OLD_FFMPEG_0_5
+#endif
+#endif
+
+
+#ifdef GAP_USES_OLD_FFMPEG_0_5
+/* defines to use older ffmpeg-0.5 compatible types */
+#define AVMEDIA_TYPE_UNKNOWN  CODEC_TYPE_UNKNOWN
+#define AVMEDIA_TYPE_VIDEO    CODEC_TYPE_VIDEO
+#define AVMEDIA_TYPE_AUDIO    CODEC_TYPE_AUDIO
+#define AV_PKT_FLAG_KEY       PKT_FLAG_KEY
+#endif
+
+/* end ffmpeg 0.5 / 0.6 support */
+
+
+
 #define READSTEPS_PROBE_TIMECODE 32
 
 #define DEFAULT_NAT_SEEK_GOPSIZE 24
@@ -155,10 +181,12 @@ typedef struct t_GVA_ffmpeg
  gboolean        prefere_native_seek;               /* prefere native seek if both vindex and native seek available */
  gboolean        all_timecodes_verified;
  gboolean        critical_timecodesteps_found;
- 
+
  unsigned char *  chunk_ptr;
  gint32           chunk_len;
- 
+
+ struct SwsContext *img_convert_ctx;
+
 } t_GVA_ffmpeg;
 
 
@@ -355,14 +383,15 @@ p_wrapper_ffmpeg_open_read(char *filename, t_GVA_Handle *gvahand)
   handle->key_frame_detection_works = FALSE;  /* assume a Codec with non working detection */
   handle->chunk_len = 0;
   handle->chunk_ptr = NULL;
-  
-  
+  handle->img_convert_ctx = NULL;          /* context for img_convert via sws_scale */
+
+
   p_reset_proberead_results(handle);
 
   handle->prefere_native_seek = FALSE;
   handle->all_timecodes_verified = FALSE;
   handle->critical_timecodesteps_found = FALSE;
-  
+
   /* open for the VIDEO part */
   if(FALSE == p_ff_open_input(filename, gvahand, handle, TRUE))
   {
@@ -473,7 +502,7 @@ p_wrapper_ffmpeg_open_read(char *filename, t_GVA_Handle *gvahand)
                 );
 
 
-  /* 
+  /*
    * vid_codec_context->pix_fmt may change later, and is sometimes
    * not recognized until the 1.st frame has been decoded.
    * therefore the follwing avpicture_fill is just a 1.st guess
@@ -484,7 +513,7 @@ p_wrapper_ffmpeg_open_read(char *filename, t_GVA_Handle *gvahand)
    *  - Quicktime codec (codec_id == 10)  uses PIX_FMT_YUV422P ( Olympus C4040Z Digicam MJPEG .mov files)
    */
   handle->yuv_buff_pix_fmt = handle->vid_codec_context->pix_fmt;   /* PIX_FMT_YUV420P */
-  
+
   avpicture_fill(handle->picture_yuv
                 ,handle->yuv_buffer
                 ,handle->yuv_buff_pix_fmt
@@ -497,9 +526,9 @@ p_wrapper_ffmpeg_open_read(char *filename, t_GVA_Handle *gvahand)
   gvahand->decoder_handle = (void *)handle;
 
 
-  /* workaround: initial frameread and reopen for detection of yuv_buff_pix_fmt */ 
+  /* workaround: initial frameread and reopen for detection of yuv_buff_pix_fmt */
   {
-    if(gap_debug) 
+    if(gap_debug)
     {
       printf("INITIAL call p_wrapper_ffmpeg_get_next_frame\n");
     }
@@ -510,7 +539,7 @@ p_wrapper_ffmpeg_open_read(char *filename, t_GVA_Handle *gvahand)
     p_ffmpeg_vid_reopen_read(handle, gvahand);
     gvahand->current_frame_nr = 0;
     gvahand->current_seek_nr = 1;
-    
+
   }
 
 
@@ -546,7 +575,7 @@ p_wrapper_ffmpeg_open_read(char *filename, t_GVA_Handle *gvahand)
     }
   }
 
-  if(gap_debug)  //####XXXXXXXXXXXXXx
+  if(gap_debug)
   {
     if(gvahand->vindex)
     {
@@ -567,7 +596,7 @@ p_wrapper_ffmpeg_open_read(char *filename, t_GVA_Handle *gvahand)
     {
       printf("IDX: p_wrapper_ffmpeg_open_read: vindex is NULL\n");
     }
-    
+
     printf("p_wrapper_ffmpeg_open_read: END, OK\n");
   }
 
@@ -623,6 +652,12 @@ p_wrapper_ffmpeg_close(t_GVA_Handle *gvahand)
     /* ??????? do not attempt to free handle->aud_codec_context (it points to &handle->aud_stream.codec) */
   }
 
+  if (handle->img_convert_ctx != NULL)
+  {
+    sws_freeContext(handle->img_convert_ctx);
+    handle->img_convert_ctx = NULL;
+  }
+
   /* Close a media file (but not its codecs) */
   if(gap_debug) printf("p_wrapper_ffmpeg_close: av_close_input_file\n");
   if(handle->vid_input_context) av_close_input_file(handle->vid_input_context);
@@ -670,7 +705,7 @@ p_wrapper_ffmpeg_get_codec_name(t_GVA_Handle  *gvahand
       return(g_strdup(handle->vcodec->name));
     }
   }
-  
+
   if (codec_type == GVA_AUDIO_CODEC)
   {
     if(handle->acodec)
@@ -715,7 +750,7 @@ p_wrapper_ffmpeg_get_video_chunk(t_GVA_Handle  *gvahand
     /* illegal frame_nr (first frame starts at Nr 1 */
     return(GVA_RET_ERROR);
   }
- 
+
   handle = (t_GVA_ffmpeg *)gvahand->decoder_handle;
   if(handle == NULL)
   {
@@ -726,7 +761,7 @@ p_wrapper_ffmpeg_get_video_chunk(t_GVA_Handle  *gvahand
   if (frame_nr != gvahand->current_seek_nr)
   {
     gdouble pos;
-    
+
     pos = frame_nr;
     l_rc = p_wrapper_ffmpeg_seek_frame(gvahand, pos, GVA_UPOS_FRAMES);
     if (l_rc != GVA_RET_OK)
@@ -735,7 +770,7 @@ p_wrapper_ffmpeg_get_video_chunk(t_GVA_Handle  *gvahand
     }
   }
 
-  
+
   l_rc = p_private_ffmpeg_get_next_frame(gvahand, TRUE);
   if(handle->chunk_len > max_size)
   {
@@ -750,11 +785,11 @@ p_wrapper_ffmpeg_get_video_chunk(t_GVA_Handle  *gvahand
     printf("CALLING ERROR p_wrapper_ffmpeg_get_video_chunk fetch raw frame failed, chunk_ptr is NULL\n");
     return(GVA_RET_ERROR);
   }
-  
+
   if(gap_debug)
   {
     char *vcodec_name;
-    
+
     vcodec_name = "NULL";
     if(handle->vcodec)
     {
@@ -771,7 +806,7 @@ p_wrapper_ffmpeg_get_video_chunk(t_GVA_Handle  *gvahand
       ,vcodec_name
       );
   }
-  
+
   *size =  handle->chunk_len;
   memcpy(chunk, handle->chunk_ptr, handle->chunk_len);
   return (l_rc);
@@ -839,7 +874,7 @@ p_url_ftell(ByteIOContext *s)
        ,(int)s->buf_end
        );
   }
- 
+
 
   /* ignore inplausible staart offsets */
   bufferStartOffsetInStream = 0;
@@ -913,7 +948,7 @@ p_private_ffmpeg_get_next_frame(t_GVA_Handle *gvahand, gboolean do_copy_raw_chun
   l_curr_url_offset = -1;
   l_url_seek_nr = gvahand->current_seek_nr;
   l_key_frame_detected = FALSE;
-  
+
   while(l_got_picture == 0)
   {
     int l_pktlen;
@@ -950,7 +985,7 @@ p_private_ffmpeg_get_next_frame(t_GVA_Handle *gvahand, gboolean do_copy_raw_chun
               );
          }
          l_record_url_offset = -1;
-         
+
          if (!gvahand->all_frames_counted)
          {
            gvahand->total_frames = gvahand->current_frame_nr;
@@ -963,13 +998,13 @@ p_private_ffmpeg_get_next_frame(t_GVA_Handle *gvahand, gboolean do_copy_raw_chun
              */
             gvahand->total_aud_samples = (gint32)GVA_frame_2_samples(gvahand->framerate, gvahand->samplerate, gvahand->total_frames);
          }
-  
+
          p_clear_inbuf_and_vid_packet(handle);
 
          l_rc = 1;
          break;
       }
-      
+
       /* if (gap_debug)  printf("vid_stream:%d pkt.stream_index #%d, pkt.size: %d\n", handle->vid_stream_index, handle->vid_pkt.stream_index, handle->vid_pkt.size); */
 
 
@@ -989,7 +1024,7 @@ p_private_ffmpeg_get_next_frame(t_GVA_Handle *gvahand, gboolean do_copy_raw_chun
 
       l_pkt_size = handle->vid_pkt.size;
       handle->frame_len += l_pkt_size;
-      
+
       if(gap_debug)
       {  printf("using Packet data:%d size:%d  handle->frame_len:%d  dts:%lld  pts:%lld  AV_NOPTS_VALUE:%lld\n"
                                  ,(int)handle->vid_pkt.data
@@ -999,7 +1034,7 @@ p_private_ffmpeg_get_next_frame(t_GVA_Handle *gvahand, gboolean do_copy_raw_chun
                                  , handle->vid_pkt.pts
                                  , AV_NOPTS_VALUE
                                  );
-      }   
+      }
     }
 
     if (gap_debug)
@@ -1046,12 +1081,22 @@ p_private_ffmpeg_get_next_frame(t_GVA_Handle *gvahand, gboolean do_copy_raw_chun
      * bytes used. If no frame could be decompressed, *got_picture_ptr is
      * zero. Otherwise, it is non zero.
      */
+#ifdef GAP_USES_OLD_FFMPEG_0_5
+    // printf("USING avcodec_decode_video FFMPEG-0.5\n");
     l_len = avcodec_decode_video(handle->vid_codec_context  /* AVCodecContext * */
                                ,&handle->big_picture_yuv
                                ,&l_got_picture
                                ,handle->inbuf_ptr
                                ,handle->inbuf_len
                                );
+#else
+    // printf("USING avcodec_decode_video2 FFMPEG-0.6\n");
+    l_len = avcodec_decode_video2(handle->vid_codec_context  /* AVCodecContext * */
+                               ,&handle->big_picture_yuv
+                               ,&l_got_picture
+                               ,&handle->vid_pkt
+                               );
+#endif
 
     /*if (gap_debug) printf("after avcodec_decode_video: l_len:%d got_pic:%d\n", (int)l_len, (int)l_got_picture);*/
 
@@ -1074,7 +1119,7 @@ p_private_ffmpeg_get_next_frame(t_GVA_Handle *gvahand, gboolean do_copy_raw_chun
                 ,gvahand->width
                 ,gvahand->height
                 );
-    } 
+    }
 
     if(l_len < 0)
     {
@@ -1090,8 +1135,8 @@ p_private_ffmpeg_get_next_frame(t_GVA_Handle *gvahand, gboolean do_copy_raw_chun
       l_frame_len = (l_len & 0xffff);
       handle->frame_len = (l_len - l_pkt_size);
       l_record_url_offset = l_curr_url_offset;
-      l_key_frame_detected = ((handle->vid_pkt.flags & PKT_FLAG_KEY) != 0);
-      
+      l_key_frame_detected = ((handle->vid_pkt.flags & AV_PKT_FLAG_KEY) != 0);
+
       if(gap_debug)
       {
         /* log information that could be relevant for redesign of VINDEX creation */
@@ -1099,7 +1144,7 @@ p_private_ffmpeg_get_next_frame(t_GVA_Handle *gvahand, gboolean do_copy_raw_chun
                   , (int)gvahand->current_seek_nr
                   , handle->prev_url_offset[MAX_PREV_OFFSET -1]
                   , l_record_url_offset
-                  ,  (handle->vid_pkt.flags & PKT_FLAG_KEY)
+                  ,  (handle->vid_pkt.flags & AV_PKT_FLAG_KEY)
                   , handle->vid_pkt.dts
                   ,(int)l_frame_len
                   ,(int)l_len
@@ -1125,9 +1170,9 @@ p_private_ffmpeg_get_next_frame(t_GVA_Handle *gvahand, gboolean do_copy_raw_chun
       /* 1.st frame_len may contain headers (size may be too large) */
       handle->max_frame_len = MAX(handle->max_frame_len, l_len);
     }
-    
+
     handle->got_frame_length16 = l_frame_len;
-    
+
     if(gap_debug)
     {
       printf("GOT PIC: current_seek_nr:%06d l_frame_len:%d got_pic:%d key:%d\n"
@@ -1140,20 +1185,43 @@ p_private_ffmpeg_get_next_frame(t_GVA_Handle *gvahand, gboolean do_copy_raw_chun
 
     if(handle->dummy_read == FALSE)
     {
-      if (gap_debug) printf("p_wrapper_ffmpeg_get_next_frame: before img_convert\n");
-
-      /* avcodec.img_convert  convert among pixel formats */
+      /* reuse the img_convert_ctx or create a new one (in case ctx is NULL or params have changed)
+       *
+       */
+      handle->img_convert_ctx = sws_getCachedContext(handle->img_convert_ctx
+	                                 , gvahand->width
+                                         , gvahand->height
+                                         , handle->yuv_buff_pix_fmt    /* src pixelformat */
+                                         , gvahand->width
+                                         , gvahand->height
+                                         , PIX_FMT_RGB24               /* dest pixelformat */
+                                         , SWS_BICUBIC                 /* int sws_flags */
+                                         , NULL, NULL, NULL
+                                         );
+      if (handle->img_convert_ctx == NULL)
+      {
+         printf("Cannot initialize the conversion context (sws_getCachedContext delivered NULL pointer)\n");
+         exit(1);
+      }
 
+      if (gap_debug)
+      {
+        printf("p_wrapper_ffmpeg_get_next_frame: before img_convert via sws_scale\n");
+      }
+      sws_scale(handle->img_convert_ctx
+               , handle->picture_yuv->data      /* srcSlice */
+               , handle->picture_yuv->linesize  /* srcStride the array containing the strides for each plane */
+               , 0                              /* srcSliceY starting at 0 */
+               , gvahand->height                /* srcSliceH the height of the source slice */
+               , handle->picture_rgb->data      /* dst */
+               , handle->picture_rgb->linesize  /* dstStride the array containing the strides for each plane */
+               );
 
-      l_rc = img_convert(handle->picture_rgb
-                        , PIX_FMT_RGB24                      /* dst */
-                        ,handle->picture_yuv
-                        ,handle->yuv_buff_pix_fmt            /* src */
-                        ,gvahand->width
-                        ,gvahand->height
-                        );
+      if (gap_debug)
+      {
+        printf("p_wrapper_ffmpeg_get_next_frame: after img_convert via sws_scale\n");
+      }
 
-      /* if (gap_debug) printf("p_wrapper_ffmpeg_get_next_frame: after img_convert l_rc:%d\n", (int)l_rc); */
     }
 
 #define GVA_LOW_GOP_LIMIT 24
@@ -1188,10 +1256,10 @@ p_private_ffmpeg_get_next_frame(t_GVA_Handle *gvahand, gboolean do_copy_raw_chun
 
     if(gap_debug)
     {
-      if( (gvahand->vindex) 
+      if( (gvahand->vindex)
           && (handle->capture_offset)
           )
-      {    
+      {
         printf("KeyFrame: %d  works:%d\n"
           ,(int)handle->big_picture_yuv.key_frame
           ,(int)handle->key_frame_detection_works
@@ -1199,7 +1267,7 @@ p_private_ffmpeg_get_next_frame(t_GVA_Handle *gvahand, gboolean do_copy_raw_chun
       }
     }
 
-    if((gvahand->vindex) 
+    if((gvahand->vindex)
     && (handle->capture_offset)
     && (l_potential_index_frame)
     )
@@ -1220,13 +1288,13 @@ p_private_ffmpeg_get_next_frame(t_GVA_Handle *gvahand, gboolean do_copy_raw_chun
 
         /* printf("GUESS_GOP: prev_key_seek_nr:%d  l_url_seek_nr:%d\n"
          *       , (int)handle->prev_key_seek_nr
-         *       , (int)l_url_seek_nr); 
+         *       , (int)l_url_seek_nr);
          */
         /* try to guess the GOP size (but use at least  GOPSIZE of 4 frames) */
         if(l_url_seek_nr > handle->prev_key_seek_nr)
         {
            gdouble l_gopsize;
-           
+
            l_gopsize = l_url_seek_nr - handle->prev_key_seek_nr;
            if(handle->guess_gop_size == 0)
            {
@@ -1268,10 +1336,10 @@ p_private_ffmpeg_get_next_frame(t_GVA_Handle *gvahand, gboolean do_copy_raw_chun
         }
         handle->prev_url_offset[0] = l_record_url_offset;
     }
-    
+
     gvahand->current_frame_nr = gvahand->current_seek_nr;
     gvahand->current_seek_nr++;
- 
+
     if (gap_debug) printf("p_wrapper_ffmpeg_get_next_frame: current_frame_nr :%d\n"
                          , (int)gvahand->current_frame_nr);
 
@@ -1306,7 +1374,7 @@ t_GVA_RetCode
 p_wrapper_ffmpeg_seek_frame(t_GVA_Handle *gvahand, gdouble pos, t_GVA_PosUnit pos_unit)
 {
   t_GVA_ffmpeg*  master_handle;
-  
+
   master_handle = (t_GVA_ffmpeg*)gvahand->decoder_handle;
 
   if (master_handle->timecode_proberead_done != TRUE)
@@ -1354,7 +1422,7 @@ static void p_reset_proberead_results(t_GVA_ffmpeg*  handle)
   handle->eof_timecode = AV_NOPTS_VALUE;
   handle->video_libavformat_seek_gopsize
     = gap_base_get_gimprc_int_value("video-libavformat-seek-gopsize", DEFAULT_NAT_SEEK_GOPSIZE, 0, MAX_NAT_SEEK_GOPSIZE);
-}  /* end p_reset_proberead_results */ 
+}  /* end p_reset_proberead_results */
 
 
 /* -------------------------------------
@@ -1379,8 +1447,8 @@ p_seek_timecode_reliability_self_test(t_GVA_Handle *gvahand)
   t_GVA_ffmpeg*  master_handle;
   int gap_debug_local;
   gboolean persitent_analyse_available;
-  
-  
+
+
   gap_debug_local = gap_debug;
 
 #ifdef GAP_DEBUG_FF_NATIVE_SEEK
@@ -1396,7 +1464,7 @@ p_seek_timecode_reliability_self_test(t_GVA_Handle *gvahand)
     persitent_analyse_available =
       p_get_video_analyse_results(gvahand);
   }
-  
+
   if(gap_debug_local)
   {
     printf("\n\n\n\n\n\n\n\n\n\n\n\n");
@@ -1413,10 +1481,10 @@ p_seek_timecode_reliability_self_test(t_GVA_Handle *gvahand)
         );
     }
   }
-  
+
   if(persitent_analyse_available != TRUE)
   {
-    bck_total_frames = gvahand->total_frames;  
+    bck_total_frames = gvahand->total_frames;
 
     gvahand->cancel_operation = FALSE;   /* reset cancel flag */
     p_ffmpeg_vid_reopen_read(master_handle, gvahand);
@@ -1425,8 +1493,8 @@ p_seek_timecode_reliability_self_test(t_GVA_Handle *gvahand)
     gvahand->current_seek_nr = 1;
     p_clear_inbuf_and_vid_packet(master_handle);
 
-    /* limit native seek attempts to only one loop while running the selftest 
-     */  
+    /* limit native seek attempts to only one loop while running the selftest
+     */
     master_handle->max_tries_native_seek = 1;
 
 
@@ -1546,7 +1614,7 @@ p_seek_timecode_reliability_self_test(t_GVA_Handle *gvahand)
  * and furthermore native timecode based seek operations are not reliable
  * and must be disabled.
  *
- * it seems that libavformat sets the current timecode 
+ * it seems that libavformat sets the current timecode
  * to timecode after last frame
  * when seeking to positions after EOF (and in case of errors after native seek).
  * This (undocumented ?) effect enables quick detection of the toatal_frames.
@@ -1564,18 +1632,18 @@ p_detect_total_frames_via_native_seek(t_GVA_Handle *gvahand)
   t_GVA_RetCode l_rc;
   t_GVA_ffmpeg*  master_handle;
   int gap_debug_local;
-  
+
   gap_debug_local = gap_debug;
 
 #ifdef GAP_DEBUG_FF_NATIVE_SEEK
   gap_debug_local = 1;
 #endif /* GAP_DEBUG_FF_NATIVE_SEEK */
-  
+
   master_handle = (t_GVA_ffmpeg*)gvahand->decoder_handle;
   master_handle->eof_timecode = AV_NOPTS_VALUE;
   totalGuess = p_guess_total_frames(gvahand);
   lastFrameNr = totalGuess;
-  
+
   totalPlausible = MAX((totalGuess / 4), 256);
   if(gap_debug_local)
   {
@@ -1585,7 +1653,7 @@ p_detect_total_frames_via_native_seek(t_GVA_Handle *gvahand)
   }
 
   /* first loop trys to seek after EOF until seek fails
-   * seek to 150 % until seek fails 
+   * seek to 150 % until seek fails
    */
   totalDetected = 0;
   frameNrLo = 1;
@@ -1620,7 +1688,7 @@ p_detect_total_frames_via_native_seek(t_GVA_Handle *gvahand)
   while(1==1)
   {
     gdouble delta;
-    
+
     delta = (gdouble)(frameNrHi - frameNrLo) / 2.0;
     frameNrMed = frameNrLo + delta;
     if(gap_debug_local)
@@ -1656,7 +1724,7 @@ p_detect_total_frames_via_native_seek(t_GVA_Handle *gvahand)
     }
 
   }
-  
+
   if(totalDetected >= totalPlausible)
   {
     l_rc = p_seek_native_timcode_based(gvahand, totalDetected);
@@ -1681,8 +1749,8 @@ p_detect_total_frames_via_native_seek(t_GVA_Handle *gvahand)
     gvahand->total_frames = lastFrameNr;
     gvahand->all_frames_counted = FALSE;
   }
-  
-  
+
+
   if(gap_debug_local)
   {
     printf("END: p_detect_total_frames_via_native_seek plausiblity limit:%d detected:%d total_frames:%d \n"
@@ -1703,8 +1771,8 @@ static void
 p_inc_native_timecode_seek_failcount(t_GVA_Handle *gvahand)
 {
   t_GVA_ffmpeg*  master_handle;
-  
-  master_handle = (t_GVA_ffmpeg*)gvahand->decoder_handle; 
+
+  master_handle = (t_GVA_ffmpeg*)gvahand->decoder_handle;
   if (master_handle->native_timecode_seek_failcount <= 0)
   {
     printf("\n##############################\n");
@@ -1714,7 +1782,7 @@ p_inc_native_timecode_seek_failcount(t_GVA_Handle *gvahand)
     printf("#  positioning to exact framenumber is not guaranteed\n");
     printf("#  you may create a videoindex to enable positioning to exact frame numbers\n");
     printf("##############################\n");
-    
+
   }
   master_handle->native_timecode_seek_failcount++;
 }  /* end p_inc_native_timecode_seek_failcount */
@@ -1766,17 +1834,17 @@ p_clear_inbuf_and_vid_packet(t_GVA_ffmpeg *handle)
  *    The outer Loop (l_nloops) repeats the sync loop attempt by using a lower seek offset
  *    from the previous video index entry and uses more read operations (l_synctries)
  *    trying to find the target frame. (This is necessary because byte oriented
- *    seek operations do not work exact and may position the video stream 
+ *    seek operations do not work exact and may position the video stream
  *    AFTER the target frame.
- *    
+ *
  *    The postioning to last Group of pictures is done
  *    by sequential read (l_readsteps). it should contain at least one keyframe (I-frame)
  *    that enables clean decoding of the following P and B frames
  *
- *                        video index table                              inner (synctries) and outer loop 
+ *                        video index table                              inner (synctries) and outer loop
  *
  *                       |-------------------------------------------|
- *                       | [10] seek_nr:000100  offset: len16:  DTS: |    
+ *                       | [10] seek_nr:000100  offset: len16:  DTS: |
  *                       |-------------------------------------------|
  *
  *
@@ -1784,7 +1852,7 @@ p_clear_inbuf_and_vid_packet(t_GVA_ffmpeg *handle)
  *                                                                                      to seek the video stream
  *                                                                                      to exact target position)
  *                       |-------------------------------------------|                 l_nloops = 2
- *                       | [11] seek_nr:000110  offset: len16:  DTS: |                      |  1  l_synctries             
+ *                       | [11] seek_nr:000110  offset: len16:  DTS: |                      |  1  l_synctries
  *                       |-------------------------------------------|                      |  2
  *                                                                                          |  3
  *                                                                                          |  4
@@ -1793,19 +1861,19 @@ p_clear_inbuf_and_vid_packet(t_GVA_ffmpeg *handle)
  *                                                                                          |  7
  *                                                                                          |  8
  *                                                                                          |  9
- *                       |-------------------------------------------|  l_nloops = 1        |  10    
+ *                       |-------------------------------------------|  l_nloops = 1        |  10
  *  l_idx_target ------> | [12] seek_nr:000120  offset: len16:  DTS: |   |  1 l_synctries   |  11
  *                       |-------------------------------------------|   |  2               |  12
- *                                                                       |  3               |  13 
- *                                                                       |  4               |  14 
- *                                                                       |  5               |  15 
- *                                                                       |  6               |  16 
- *                                                                       |  7               |  17 
- *                                                                       |  8               |  18 
+ *                                                                       |  3               |  13
+ *                                                                       |  4               |  14
+ *                                                                       |  5               |  15
+ *                                                                       |  6               |  16
+ *                                                                       |  7               |  17
+ *                                                                       |  8               |  18
  *                       |-------------------------------------------|   |  9               |  19
- *  l_idx_too_far -----> | [13] seek_nr:000130  offset: len16:  DTS: |   V  10              V  20 
+ *  l_idx_too_far -----> | [13] seek_nr:000130  offset: len16:  DTS: |   V  10              V  20
  *                       |-------------------------------------------|
- *                                  
+ *
  *
  *  - the fallback method emulates
  *    seek by pos-1 sequential read ops.
@@ -1814,7 +1882,7 @@ p_clear_inbuf_and_vid_packet(t_GVA_ffmpeg *handle)
  *
  *    seek operation to the start (the first few frames) always use sequential read
  *
- *                 
+ *
  */
 #define GVA_IDX_SYNC_STEPSIZE 1
 #define GVA_FRAME_NEAR_START 24
@@ -1833,7 +1901,7 @@ p_seek_private(t_GVA_Handle *gvahand, gdouble pos, t_GVA_PosUnit pos_unit)
 
   handle = (t_GVA_ffmpeg *)gvahand->decoder_handle;
   vindex = gvahand->vindex;
-  
+
   gvahand->cancel_operation = FALSE;   /* reset cancel flag */
 
   switch(pos_unit)
@@ -1852,7 +1920,7 @@ p_seek_private(t_GVA_Handle *gvahand, gdouble pos, t_GVA_PosUnit pos_unit)
       l_frame_pos = (gint32)pos;
       break;
   }
-  
+
   if(gvahand->all_frames_counted)
   {
     /* in case total number of frames is exactly known
@@ -1860,7 +1928,7 @@ p_seek_private(t_GVA_Handle *gvahand, gdouble pos, t_GVA_PosUnit pos_unit)
      */
     l_frame_pos = MIN(l_frame_pos, gvahand->total_frames);
   }
-  
+
   gvahand->percentage_done = 0.0;
 
   if(gap_debug)
@@ -1874,7 +1942,7 @@ p_seek_private(t_GVA_Handle *gvahand, gdouble pos, t_GVA_PosUnit pos_unit)
                            , gvahand->filename
                            );
   }
-  
+
 
   handle->dummy_read = TRUE;
   l_url_frame_pos = 0;
@@ -1920,7 +1988,7 @@ p_seek_private(t_GVA_Handle *gvahand, gdouble pos, t_GVA_PosUnit pos_unit)
        }
     }
 
-  
+
     /* check conditions and try native timecode based seek if conditions permit native seek.
      */
     if((l_vindex_is_usable != TRUE) || (handle->prefere_native_seek))
@@ -1941,9 +2009,9 @@ p_seek_private(t_GVA_Handle *gvahand, gdouble pos, t_GVA_PosUnit pos_unit)
           }
           return (l_rc_rd);
         }
-      }  
-    }    
-    
+      }
+    }
+
   }
 
   /* use video index where possible */
@@ -1960,8 +2028,8 @@ p_seek_private(t_GVA_Handle *gvahand, gdouble pos, t_GVA_PosUnit pos_unit)
          , (int)vindex->tabsize_used
          );
      }
-     
-     
+
+
      if(vindex->tabsize_used < 1)
      {
        printf("SEEK: index is >>> EMPTY <<<: vindex->tabsize_used: %d cannot use index!\n", (int)vindex->tabsize_used);
@@ -1987,13 +2055,13 @@ p_seek_private(t_GVA_Handle *gvahand, gdouble pos, t_GVA_PosUnit pos_unit)
          l_idx = vindex->tabsize_used -1;
        }
 
-       /* lower index adjustment (dont start seek with positions much smaller than l_frame_pos) */ 
+       /* lower index adjustment (dont start seek with positions much smaller than l_frame_pos) */
        if(l_idx > 0)
        {
          gint32 l_pos_min;
-         
+
          l_pos_min = l_frame_pos - (MAX(64, (2* vindex->stepsize)));
-         
+
          while(vindex->ofs_tab[l_idx].seek_nr < l_pos_min)
          {
            l_idx++;
@@ -2016,8 +2084,8 @@ p_seek_private(t_GVA_Handle *gvahand, gdouble pos, t_GVA_PosUnit pos_unit)
            }
          }
        }
-       
-       /* upper index adjustment (dont start seek with positions already graeter than l_frame_pos)*/ 
+
+       /* upper index adjustment (dont start seek with positions already graeter than l_frame_pos)*/
        if(l_idx > 0)
        {
          while(vindex->ofs_tab[l_idx].seek_nr >= l_frame_pos)
@@ -2038,7 +2106,7 @@ p_seek_private(t_GVA_Handle *gvahand, gdouble pos, t_GVA_PosUnit pos_unit)
            }
          }
        }
-       
+
        if(l_idx > 0)
        {
          gint32  l_idx_target;
@@ -2055,12 +2123,12 @@ p_seek_private(t_GVA_Handle *gvahand, gdouble pos, t_GVA_PosUnit pos_unit)
          {
            l_idx_too_far = -1;  /* mark as invalid */
          }
-         
+
          l_readsteps = l_frame_pos - gvahand->current_seek_nr;
          if((l_readsteps > vindex->stepsize)
          || (l_readsteps < 0))
          {
-           gint32  l_nloops;     /* number of seek attempts with different recorded videoindex entries 
+           gint32  l_nloops;     /* number of seek attempts with different recorded videoindex entries
                                   * (outer loop)
                                   */
            gint32  l_synctries;  /* number of frame read attempts until
@@ -2073,7 +2141,7 @@ p_seek_private(t_GVA_Handle *gvahand, gdouble pos, t_GVA_PosUnit pos_unit)
            while((l_idx >= 0) && (l_nloops < 12))
            {
              gboolean l_dts_timecode_usable;
-             
+
              l_dts_timecode_usable = FALSE;
 
              if((vindex->ofs_tab[l_idx].timecode_dts != AV_NOPTS_VALUE)
@@ -2083,7 +2151,7 @@ p_seek_private(t_GVA_Handle *gvahand, gdouble pos, t_GVA_PosUnit pos_unit)
              {
                l_dts_timecode_usable = TRUE;
              }
-             
+
              if(gap_debug)
              {
                printf("SEEK: USING_INDEX: ofs_tab[%d]: ofs64: %lld seek_nr:%d flen:%d chk:%d dts:%lld DTS_USABLE:%d NLOOPS:%d\n"
@@ -2097,7 +2165,7 @@ p_seek_private(t_GVA_Handle *gvahand, gdouble pos, t_GVA_PosUnit pos_unit)
                , (int)l_nloops
                );
              }
-             
+
              l_synctries = 4 + MAX_PREV_OFFSET + (vindex->stepsize * GVA_IDX_SYNC_STEPSIZE * l_nloops);
 
              /* SYNC READ loop
@@ -2130,7 +2198,7 @@ p_seek_private(t_GVA_Handle *gvahand, gdouble pos, t_GVA_PosUnit pos_unit)
                {
                  printf("USING DTS timecode based av_seek_frame\n");
                }
-               
+
                /* timecode based seek */
                ret_av_seek_frame = av_seek_frame(handle->vid_input_context, handle->vid_stream_index
                    , vindex->ofs_tab[l_idx].timecode_dts, AVSEEK_FLAG_BACKWARD);
@@ -2145,7 +2213,7 @@ p_seek_private(t_GVA_Handle *gvahand, gdouble pos, t_GVA_PosUnit pos_unit)
                /* byte position based seek  AVSEEK_FLAG_BACKWARD AVSEEK_FLAG_ANY*/
                ret_av_seek_frame = av_seek_frame(handle->vid_input_context, handle->vid_stream_index
                     ,seek_pos, AVSEEK_FLAG_BACKWARD | AVSEEK_FLAG_BYTE);
-               
+
              }
              p_clear_inbuf_and_vid_packet(handle);
 
@@ -2154,7 +2222,7 @@ p_seek_private(t_GVA_Handle *gvahand, gdouble pos, t_GVA_PosUnit pos_unit)
              while(l_synctries > 0)
              {
                gboolean l_potentialCanditate;
-               
+
                l_potentialCanditate = FALSE;
                l_rc_rd = p_wrapper_ffmpeg_get_next_frame(gvahand);
                l_summary_read_ops++;
@@ -2173,10 +2241,10 @@ p_seek_private(t_GVA_Handle *gvahand, gdouble pos, t_GVA_PosUnit pos_unit)
 //               printf("SEEK_SYNC_LOOP: idx_frame_len:%d  got_frame_length16:%d  Key:%d dts:%lld\n"
 //                    , (int)vindex->ofs_tab[l_idx_target].frame_length
 //                    , (int)handle->got_frame_length16
-//                    , (int)handle->big_picture_yuv.key_frame 
+//                    , (int)handle->big_picture_yuv.key_frame
 //                    , handle->vid_pkt.dts
 //                    );
-               
+
                if(l_dts_timecode_usable == TRUE)
                {
                  /* handling for index format with usable timecode
@@ -2198,7 +2266,7 @@ p_seek_private(t_GVA_Handle *gvahand, gdouble pos, t_GVA_PosUnit pos_unit)
                    l_potentialCanditate = TRUE;
                  }
                }
-               
+
                if (l_potentialCanditate == TRUE)
                {
                  guint16 l_checksum;
@@ -2275,11 +2343,11 @@ p_seek_private(t_GVA_Handle *gvahand, gdouble pos, t_GVA_PosUnit pos_unit)
                     }
                   }
                }
-               
+
                l_synctries--;
              }                     /* end while */
-             
-             
+
+
              if(l_target_found != TRUE)
              {
                /* index sync search failed, try with previous index in the table */
@@ -2310,7 +2378,7 @@ p_seek_private(t_GVA_Handle *gvahand, gdouble pos, t_GVA_PosUnit pos_unit)
                {
                  printf("SEEK: sync loop success remaining l_readsteps: %d\n", (int)l_readsteps);
                }
-  
+
                break;  /* OK, escape from outer loop, we are now at read position of the wanted keyframe */
              }
 
@@ -2331,7 +2399,7 @@ p_seek_private(t_GVA_Handle *gvahand, gdouble pos, t_GVA_PosUnit pos_unit)
              }
              l_nloops++;
            }                 /* end outer loop (l_nloops) */
-           
+
            if (l_target_found != TRUE)
            {
              p_ffmpeg_vid_reopen_read(handle, gvahand);
@@ -2341,22 +2409,22 @@ p_seek_private(t_GVA_Handle *gvahand, gdouble pos, t_GVA_PosUnit pos_unit)
              {
                 printf("SEEK: target frame not found, fallback to slow sequential read from begin\n");
              }
-             
+
            }
-           
+
          }
        }
      }
   }
-  
-  
+
+
   /* fallback to sequential read */
 
   if(l_url_frame_pos == 0)
   {
     /* emulate seek with N steps of read ops */
     if((l_frame_pos < gvahand->current_seek_nr)   /* position backwards ? */
-    || (gvahand->current_seek_nr < 0))            /* inplausible current position ? */ 
+    || (gvahand->current_seek_nr < 0))            /* inplausible current position ? */
     {
       /* reopen and read from start */
       p_ffmpeg_vid_reopen_read(handle, gvahand);
@@ -2427,11 +2495,11 @@ p_seek_private(t_GVA_Handle *gvahand, gdouble pos, t_GVA_PosUnit pos_unit)
  * p_seek_native_timcode_based
  * ------------------------------
  * positioning of the video stream before target_frame position.
- * 
+ *
  * the native timecode based seek operates on
  * timecode steps sample pattern that will be measured
  * by some initial probereads (p_probe_timecode_offset).
- * 
+ *
  * Most Videofiles have either constant timing (stepsize equal for all frames)
  * or use a repeating pattern of individual stepsizes within a group of pictures.
  * In both cases this procedure can calculate the correct timecode that
@@ -2475,13 +2543,13 @@ p_seek_native_timcode_based(t_GVA_Handle *gvahand, gint32 target_frame)
 #define NATIVE_SEEK_EXTRA_PREREADS_NEAR_END 22
 
   handle = (t_GVA_ffmpeg *)gvahand->decoder_handle;
- 
+
   l_retcode = GVA_RET_ERROR;
   if (handle->video_libavformat_seek_gopsize <= 0)
   {
       return(l_retcode);
   }
-  
+
   l_retry = TRUE;
   for(l_tries=0; l_tries < handle->max_tries_native_seek; l_tries++)
   {
@@ -2502,10 +2570,10 @@ p_seek_native_timcode_based(t_GVA_Handle *gvahand, gint32 target_frame)
         ,(int)l_pre_read_frames
         ,(int)gvahand->total_frames
         ,(int)target_frame
-        ); 
+        );
     }
 #endif /* GAP_DEBUG_FF_NATIVE_SEEK */
-    
+
 
 
     if (target_frame > l_pre_read_frames)
@@ -2515,8 +2583,8 @@ p_seek_native_timcode_based(t_GVA_Handle *gvahand, gint32 target_frame)
       int64_t  l_seek_timecode;
       gint32   seek_fnr;
       gint32   seek_fnr2;
-      
-  
+
+
       ret_av_seek_frame = -1;
       p_probe_timecode_offset(gvahand);
 
@@ -2535,7 +2603,7 @@ p_seek_native_timcode_based(t_GVA_Handle *gvahand, gint32 target_frame)
        */
       seek_fnr = target_frame - l_pre_read_frames;
       l_seek_timecode = p_frame_nr_to_timecode(handle, seek_fnr);
-      
+
       seek_fnr2 = seek_fnr;
       for(jj=0; jj < 2; jj++)
       {
@@ -2552,7 +2620,7 @@ p_seek_native_timcode_based(t_GVA_Handle *gvahand, gint32 target_frame)
       /* SEEK to preread position */
       gvahand->current_frame_nr = 0;
       gvahand->current_seek_nr = 1;
-      
+
       if(1==1 /*l_tries == 0*/)
       {
         /* seek variant based on stream specific dts timecode
@@ -2560,7 +2628,7 @@ p_seek_native_timcode_based(t_GVA_Handle *gvahand, gint32 target_frame)
         /* seek is done some (configurable number) frames less than wanted frame position.
          * it shall deliver position of nearest key frame
          * after seek, have to read/decode some few frames until
-         * wanted frame is reached 
+         * wanted frame is reached
          * in some rare cases (some older mpeg files) libavformat positioning may take us
          * to non-keyframes (even with AVSEEK_FLAG_BACKWARD set).
          * therefore read/decode some frames (l_pre_read_frames) shall ensure that
@@ -2575,8 +2643,8 @@ p_seek_native_timcode_based(t_GVA_Handle *gvahand, gint32 target_frame)
          */
         int64_t  target_pts;
         gdouble  secs;
-        
-        target_pts = 
+
+        target_pts =
           av_rescale_q(l_seek_timecode, handle->vid_stream->time_base, AV_TIME_BASE_Q);
 
         ret_av_seek_frame = av_seek_frame(handle->vid_input_context, -1
@@ -2591,7 +2659,7 @@ p_seek_native_timcode_based(t_GVA_Handle *gvahand, gint32 target_frame)
 #endif /* GAP_DEBUG_FF_NATIVE_SEEK */
       }
 
-    
+
 #ifdef GAP_DEBUG_FF_NATIVE_SEEK
       if(1==0)
       {
@@ -2601,13 +2669,13 @@ p_seek_native_timcode_based(t_GVA_Handle *gvahand, gint32 target_frame)
         gdouble secs;
         gdouble dframes;
         gint32 seek_fnr;
-        
+
         seek_fnr = target_frame - l_pre_read_frames;
         secs = (gdouble)seek_fnr / gvahand->framerate;
         target_pts = secs * AV_TIME_BASE;
         microsecs_start = handle->vid_input_context->start_time * handle->vid_stream->time_base.num
                           / handle->vid_stream->time_base.den;
-        
+
         seek_target= av_rescale_q(target_pts, AV_TIME_BASE_Q, handle->vid_stream->time_base);
         dframes = handle->vid_input_context->duration / (gdouble)AV_TIME_BASE * gvahand->framerate;
 
@@ -2619,7 +2687,7 @@ p_seek_native_timcode_based(t_GVA_Handle *gvahand, gint32 target_frame)
            , l_seek_timecode
            , (int)seek_fnr
            );
-        
+
 
       }
 
@@ -2634,12 +2702,12 @@ p_seek_native_timcode_based(t_GVA_Handle *gvahand, gint32 target_frame)
            );
       }
 #endif /* GAP_DEBUG_FF_NATIVE_SEEK */
-      
-      /* clear inbuf and packet 
+
+      /* clear inbuf and packet
        *(must remove old content after seek to enable clean reads)
        */
       p_clear_inbuf_and_vid_packet(handle);
-    
+
       if (ret_av_seek_frame < 0)
       {
         printf("** ERROR AVFORMAT: av_seek_frame failed retcode: %d \n"
@@ -2657,7 +2725,7 @@ p_seek_native_timcode_based(t_GVA_Handle *gvahand, gint32 target_frame)
          int64_t  l_prev_timecode;
          int64_t  l_pprev_timecode;
          gchar    l_debug_msg[500];
-  
+
          l_wanted_timecode = p_frame_nr_to_timecode(handle, target_frame -1);
          l_want_2_timecode = p_frame_nr_to_timecode(handle, target_frame -2);
          l_want_3_timecode = p_frame_nr_to_timecode(handle, target_frame -3);
@@ -2669,10 +2737,10 @@ p_seek_native_timcode_based(t_GVA_Handle *gvahand, gint32 target_frame)
          l_prev_timecode = AV_NOPTS_VALUE;
          l_pprev_timecode = AV_NOPTS_VALUE;
          l_read_err_count = 0;
-         
+
          l_progress_step =  1.0 / MAX((gdouble)l_pre_read_frames, 1.0);
          gvahand->percentage_done = 0.0;
-         /* native seek has (hopefully) set stream position to the nearest keyframe 
+         /* native seek has (hopefully) set stream position to the nearest keyframe
           * (before the wanted frame)
           * ??? For some video formats the native seek may set position AFTER the wanted
           *     frame in such a case it is worth to start a retry with larger
@@ -2748,7 +2816,7 @@ p_seek_native_timcode_based(t_GVA_Handle *gvahand, gint32 target_frame)
              fflush(stdout);
            }
 #endif /* GAP_DEBUG_FF_NATIVE_SEEK */
-           
+
            l_rc_timeread = p_wrapper_ffmpeg_get_next_frame(gvahand);
            l_curr_timecode = handle->vid_pkt.dts;
 
@@ -2771,13 +2839,13 @@ p_seek_native_timcode_based(t_GVA_Handle *gvahand, gint32 target_frame)
            if(l_rc_timeread != GVA_RET_OK)
            {
              gint32 l_fnr;
-           
+
              l_fnr = p_timecode_to_frame_nr(handle, l_curr_timecode);
              if(handle->eof_timecode == AV_NOPTS_VALUE)
              {
                handle->eof_timecode = l_curr_timecode;
              }
-     
+
              l_read_err_count++;
 
 #ifdef GAP_DEBUG_FF_NATIVE_SEEK
@@ -2793,7 +2861,7 @@ p_seek_native_timcode_based(t_GVA_Handle *gvahand, gint32 target_frame)
                 , (int)gvahand->all_frames_counted
                 );
 #endif /* GAP_DEBUG_FF_NATIVE_SEEK */
-             
+
              if (1==1)  /* (l_read_err_count > 1) */
              {
                l_retry = FALSE;
@@ -2804,7 +2872,7 @@ p_seek_native_timcode_based(t_GVA_Handle *gvahand, gint32 target_frame)
                l_prev_timecode = AV_NOPTS_VALUE;
              }
              break;
-             
+
            }
 
            l_debug_msg[0] = "\n";
@@ -2834,19 +2902,19 @@ p_seek_native_timcode_based(t_GVA_Handle *gvahand, gint32 target_frame)
               int64_t l_prev_absdiff;
               int64_t l_pprev_absdiff;
 
-              
+
               match_count = 0;
               must_match = 2;
               debug_match_string[0] = '-';
               debug_match_string[1] = '-';
               debug_match_string[2] = '-';
               debug_match_string[3] = '\0';
-              
+
               l_curr_absdiff  = llabs(l_curr_timecode  - l_wanted_timecode);
               l_prev_absdiff  = llabs(l_prev_timecode  - l_want_2_timecode);
               l_pprev_absdiff = llabs(l_pprev_timecode - l_want_3_timecode);
-              
-              
+
+
               if (l_curr_absdiff <= l_very_small_absdiff)
               {
                 debug_match_string[0] = 'x';
@@ -2871,13 +2939,13 @@ p_seek_native_timcode_based(t_GVA_Handle *gvahand, gint32 target_frame)
                 fflush(stdout);
               }
 #endif /* GAP_DEBUG_FF_NATIVE_SEEK */
-            
+
               if(match_count >= must_match)
               {
                 /* set attributes of gvahand to refelect wanted seek position reached. */
                 gvahand->current_seek_nr = target_frame;
                 gvahand->current_frame_nr = target_frame -1;
-               
+
                 /* if this is not the first try ( l_tries > 0)
                  * increase video_libavformat_seek_gopsize
                  * to the value where we had success with.
@@ -2908,7 +2976,7 @@ p_seek_native_timcode_based(t_GVA_Handle *gvahand, gint32 target_frame)
                 return (GVA_RET_OK);
               }
            } /* END of tolerant check if wanted timecode is reached */
-        
+
 
            /* timecode overflow check */
            if ((l_curr_timecode != AV_NOPTS_VALUE)
@@ -2943,15 +3011,15 @@ p_seek_native_timcode_based(t_GVA_Handle *gvahand, gint32 target_frame)
                l_retcode = GVA_RET_ERROR;
                break;
            }
-         
+
            l_pprev_timecode = l_prev_timecode;
            l_prev_timecode = l_curr_timecode;
-         
-         
+
+
          }
 
       }
-      
+
       if (l_retry != TRUE)
       {
         printf("** AVFORMAT: native seek failed (seek behind EOF, Seek Error, or Read error)\n");
@@ -2973,7 +3041,7 @@ p_seek_native_timcode_based(t_GVA_Handle *gvahand, gint32 target_frame)
   }  /* end for tries loop */
 
   return (l_retcode);
-  
+
 }  /* end p_seek_native_timcode_based */
 
 
@@ -3144,7 +3212,7 @@ p_wrapper_ffmpeg_count_frames(t_GVA_Handle *gvahand)
   {
     gvahand->vindex = GVA_load_videoindex(gvahand->filename, gvahand->vid_track, GVA_FFMPEG_DECODER_NAME);
   }
-  
+
   if(gvahand->vindex)
   {
      if((gvahand->vindex->total_frames > 0)
@@ -3207,7 +3275,7 @@ p_wrapper_ffmpeg_count_frames(t_GVA_Handle *gvahand)
     /* TIMCODE LOG INIT */
     FILE *fp_timecode_log;
     fp_timecode_log = p_init_timecode_log(gvahand);
-  
+
     if(vindex)
     {
       handle->capture_offset = TRUE;
@@ -3242,8 +3310,8 @@ p_wrapper_ffmpeg_count_frames(t_GVA_Handle *gvahand)
        }
        gvahand->frame_counter++;
        l_total_frames = MAX(l_total_frames, gvahand->frame_counter);
-       
-       
+
+
        /* TIMCODE check and LOG WHILE COUNTING */
        p_timecode_check_and_log(fp_timecode_log, gvahand->frame_counter, handle, master_handle, gvahand);
 
@@ -3281,15 +3349,15 @@ p_wrapper_ffmpeg_count_frames(t_GVA_Handle *gvahand)
 
     /* close the extra handle (that was opened for counting only) */
     p_wrapper_ffmpeg_close(copy_gvahand);
-    
-    
+
+
     /* TIMCODE LOG FINALLY */
     if (fp_timecode_log)
     {
       fclose(fp_timecode_log);
     }
-    
-    
+
+
   }
 
   g_free(copy_gvahand);
@@ -3307,7 +3375,7 @@ p_wrapper_ffmpeg_count_frames(t_GVA_Handle *gvahand)
     if(gvahand->cancel_operation)
     {
       /* because of cancel the total_frames is still unknown
-       * (vindex->total_frames == 0 is the indicator for incomplete index) 
+       * (vindex->total_frames == 0 is the indicator for incomplete index)
        */
       vindex->total_frames = 0;
     }
@@ -3317,7 +3385,7 @@ p_wrapper_ffmpeg_count_frames(t_GVA_Handle *gvahand)
       printf("p_wrapper_ffmpeg_count_frames: before GVA_save_videoindex\n");
     }
     GVA_save_videoindex(vindex, gvahand->filename, GVA_FFMPEG_DECODER_NAME);
-  
+
 
     if(gap_debug)
     {
@@ -3329,14 +3397,14 @@ p_wrapper_ffmpeg_count_frames(t_GVA_Handle *gvahand)
   {
      gvahand->total_frames = gvahand->frame_counter;
      gvahand->all_frames_counted = TRUE;
-     
+
      master_handle->all_timecodes_verified = TRUE;
   }
   else
   {
      gvahand->total_frames = MAX(gvahand->total_frames, gvahand->frame_counter);
      gvahand->cancel_operation = FALSE;  /* reset cancel flag */
-     
+
      if(gap_debug)
      {
        printf("p_wrapper_ffmpeg_count_frames: RETURN ERROR\n");
@@ -3405,7 +3473,7 @@ t_GVA_SeekSupport
 p_wrapper_ffmpeg_seek_support(t_GVA_Handle *gvahand)
 {
   t_GVA_ffmpeg*  master_handle;
- 
+
   master_handle = (t_GVA_ffmpeg*)gvahand->decoder_handle;
 
   if ((master_handle->timecode_proberead_done != TRUE)
@@ -3418,14 +3486,14 @@ p_wrapper_ffmpeg_seek_support(t_GVA_Handle *gvahand)
   {
     return(GVA_SEEKSUPP_NONE);
   }
-  
+
   if (master_handle->video_libavformat_seek_gopsize > 0)
   {
     return(GVA_SEEKSUPP_NATIVE);
   }
 
   return(GVA_SEEKSUPP_VINDEX);
-  
+
 }  /* end p_wrapper_ffmpeg_seek_support */
 
 
@@ -3591,7 +3659,7 @@ p_ff_open_input(char *filename, t_GVA_Handle *gvahand, t_GVA_ffmpeg*  handle, gb
 
     switch(acc->codec_type)
     {
-        case CODEC_TYPE_AUDIO:
+        case AVMEDIA_TYPE_AUDIO:
             gvahand->atracks++;  /* count all audiostraems as audiotrack */
             if(gap_debug) printf("\nInput Audio channels: %d\n", acc->channels);
             if((gvahand->atracks == gvahand->aud_track)
@@ -3609,14 +3677,14 @@ p_ff_open_input(char *filename, t_GVA_Handle *gvahand, t_GVA_ffmpeg*  handle, gb
               gvahand->samplerate = acc->sample_rate;
             }
             break;
-        case CODEC_TYPE_VIDEO:
+        case AVMEDIA_TYPE_VIDEO:
             gvahand->vtracks++; /* count all videostraems as videotrack */
             if((gvahand->vtracks == gvahand->vid_track)
             || (gvahand->vtracks == 1))
             {
               gdouble containerFramerate;
               gdouble codecFramerate;
-              
+
               if(vid_open)
               {
                 handle->vid_stream_index = ii;
@@ -3630,7 +3698,7 @@ p_ff_open_input(char *filename, t_GVA_Handle *gvahand, t_GVA_ffmpeg*  handle, gb
 
               /* Aspect Ratio handling */
               p_set_aspect_ratio(gvahand, handle);
-              
+
 
               rfps      = ic->streams[ii]->r_frame_rate.num;
               rfps_base = ic->streams[ii]->r_frame_rate.den;
@@ -3642,7 +3710,7 @@ p_ff_open_input(char *filename, t_GVA_Handle *gvahand, t_GVA_ffmpeg*  handle, gb
               acc->idct_algo= 0;
               /*
                * if(acc->codec->capabilities & CODEC_CAP_TRUNCATED)
-               *   acc->flags|= CODEC_FLAG_TRUNCATED; 
+               *   acc->flags|= CODEC_FLAG_TRUNCATED;
                */
               if(/*acc->codec_id==CODEC_ID_MPEG4 || */acc->codec_id==CODEC_ID_MPEG1VIDEO)
               {
@@ -3650,7 +3718,7 @@ p_ff_open_input(char *filename, t_GVA_Handle *gvahand, t_GVA_ffmpeg*  handle, gb
               }
 
 
-              /* attempt to get the framerate */              
+              /* attempt to get the framerate */
               containerFramerate = 1.0;
               codecFramerate = 1.0;
 
@@ -3658,7 +3726,7 @@ p_ff_open_input(char *filename, t_GVA_Handle *gvahand, t_GVA_ffmpeg*  handle, gb
               if (acc->time_base.num != 0)
               {
                  codecFramerate = (gdouble)acc->time_base.den / (gdouble)acc->time_base.num;
-                
+
                 if (acc->ticks_per_frame > 1)
                 {
                   /* videos with interlaced frames typically deliver framerates at double speed
@@ -3680,13 +3748,13 @@ p_ff_open_input(char *filename, t_GVA_Handle *gvahand, t_GVA_ffmpeg*  handle, gb
                 /* in case the framerate values of the codec and container are different
                  * pick the smaller value (but only if it is greater than plausibility threshold of 10 fps)
                  */
-              
+
                 if((containerFramerate > 10.0)
                 && (containerFramerate < codecFramerate))
                 {
                    gvahand->framerate = containerFramerate;
                 }
-              
+
                 printf("\nSeems stream %d codec frame rate differs from container frame rate: %2.2f (%d/%d) -> %2.2f (%d/%d) ticksPerFrame:%d\n"
                          ,ii
                          , (float)acc->time_base.den /  (float)acc->time_base.num
@@ -3795,9 +3863,9 @@ p_ff_open_input(char *filename, t_GVA_Handle *gvahand, t_GVA_ffmpeg*  handle, gb
  * -------------------------
  * set the gvahand->aspect_ratio variable to aspect ratio
  * typical values are
- * 1.777777  For 16:9 video 
+ * 1.777777  For 16:9 video
  * 1.333333  For 4:3  video
- * 
+ *
  * Note that the gvahand->aspect_ratio variable describes the ratio
  * for the full image (and not the ratio of a single pixel)
  *
@@ -3807,7 +3875,7 @@ static void
 p_set_aspect_ratio(t_GVA_Handle *gvahand, t_GVA_ffmpeg*  handle)
 {
   AVStream *video_st;
- 
+
   video_st = handle->vid_stream;
   gvahand->aspect_ratio = 0.0;
 
@@ -3824,7 +3892,7 @@ p_set_aspect_ratio(t_GVA_Handle *gvahand, t_GVA_ffmpeg*  handle)
   {
      gvahand->aspect_ratio = 1.0;
   }
-  
+
   if(gvahand->aspect_ratio != 0.0)
   {
      gvahand->aspect_ratio *= (gdouble)video_st->codec->width / video_st->codec->height;
@@ -3998,7 +4066,7 @@ p_ffmpeg_aud_reopen_read(t_GVA_ffmpeg *handle, t_GVA_Handle *gvahand)
  * p_audio_convert_to_s16
  * ------------------------------
  *
- * convert audio samples in specified data_length at handle->output_samples_ptr 
+ * convert audio samples in specified data_length at handle->output_samples_ptr
  * to signed 16 bit little endian audio format.
  * The original content of handle->output_samples_ptr is replaced by the conversion result
  * that may also change data length.
@@ -4020,7 +4088,7 @@ p_audio_convert_to_s16(t_GVA_ffmpeg *handle
   {
     handle->reformat_ctx = av_audio_convert_alloc(SAMPLE_FMT_S16, 1,
                                            dec->sample_fmt, 1, NULL, 0);
-    if (!handle->reformat_ctx) 
+    if (!handle->reformat_ctx)
     {
       printf("ERROR: Cannot convert %s sample format to %s sample format\n",
           avcodec_get_sample_fmt_name(dec->sample_fmt),
@@ -4028,15 +4096,15 @@ p_audio_convert_to_s16(t_GVA_ffmpeg *handle
       return (converted_data_size);
     }
   }
-  
+
   audio_convert_in_buffer = g_malloc(data_size);
-  if (audio_convert_in_buffer != NULL) 
+  if (audio_convert_in_buffer != NULL)
   {
     /* copy samples in a new buffer to be used as input for the conversion */
     memcpy(audio_convert_in_buffer, handle->output_samples_ptr, data_size);
-    
-    /* convert and write converted samples back to handle->output_samples_ptr */ 
-    if (handle->reformat_ctx) 
+
+    /* convert and write converted samples back to handle->output_samples_ptr */
+    if (handle->reformat_ctx)
     {
        const void *ibuf[6]= {audio_convert_in_buffer};
        void *obuf[6]= {handle->output_samples_ptr};
@@ -4336,12 +4404,21 @@ p_read_audio_packets( t_GVA_ffmpeg *handle, t_GVA_Handle *gvahand, gint32 max_sa
      * zero or negative.
      */
     data_size = handle->samples_buffer_size;
+#ifdef GAP_USES_OLD_FFMPEG_0_5
     l_len = avcodec_decode_audio2(handle->aud_codec_context  /* AVCodecContext * */
                                ,(int16_t *)handle->output_samples_ptr
                                ,&data_size
                                ,handle->abuf_ptr
                                ,handle->abuf_len
                                );
+#else
+    l_len = avcodec_decode_audio3(handle->aud_codec_context  /* AVCodecContext * */
+                               ,(int16_t *)handle->output_samples_ptr
+                               ,&data_size
+                               ,&handle->aud_pkt
+                               );
+#endif
+
 
     if (gap_debug)
     {
@@ -4484,7 +4561,7 @@ p_vindex_add_url_offest(t_GVA_Handle *gvahand
                          )
 {
   static gint64 s_last_seek_nr;
-  
+
   if(vindex->tabsize_used > 0)
   {
     if(seek_nr <= s_last_seek_nr)
@@ -4493,7 +4570,7 @@ p_vindex_add_url_offest(t_GVA_Handle *gvahand
     }
   }
   s_last_seek_nr = seek_nr;
-  
+
   if(vindex->tabsize_used >= vindex->tabsize_allocated -1)
   {
     t_GVA_IndexElem *redim_ofs_tab;
@@ -4564,7 +4641,7 @@ p_gva_checksum(AVPicture *picture_yuv, gint height)
 {
   guint16 l_checksum;
   gint ii;
-  
+
   l_checksum = 0;
   for(ii=0;ii<4;ii++)
   {
@@ -4572,7 +4649,7 @@ p_gva_checksum(AVPicture *picture_yuv, gint height)
     {
       guchar *buf;
       gint row;
-     
+
       buf = picture_yuv->data[ii];
       buf += picture_yuv->linesize[ii] / 2;
       for(row=0; row < height; row+=2)
@@ -4601,7 +4678,7 @@ p_init_timecode_log(t_GVA_Handle   *gvahand)
   FILE *fp_timecode_log;
   gint32 gimprc_timecode_log;
   gchar *timecode_logfile_name;
- 
+
   gimprc_timecode_log
     = gap_base_get_gimprc_int_value("video-libavformat-timecodelog", 0, 0, 1);
   fp_timecode_log = NULL;
@@ -4634,7 +4711,7 @@ p_init_timecode_log(t_GVA_Handle   *gvahand)
     {
       t_GVA_ffmpeg *master_handle;
       master_handle = (t_GVA_ffmpeg *)gvahand->decoder_handle;
-     
+
       fprintf(fp_timecode_log, "# TIMECODE log for video: %s\n"
         , gvahand->filename
         );
@@ -4652,7 +4729,7 @@ p_init_timecode_log(t_GVA_Handle   *gvahand)
     }
     g_free(timecode_logfile_name);
   }
-  
+
   return (fp_timecode_log);
 }  /* end p_init_timecode_log */
 
@@ -4662,7 +4739,7 @@ p_init_timecode_log(t_GVA_Handle   *gvahand)
  * p_timecode_check_and_log
  * ----------------------------------
  * write log entry for current frame to timecode log file.
- * the master_handle is used read only 
+ * the master_handle is used read only
  * (for converting framenr to timecode based on
  * proberead statistical data)
  */
@@ -4679,7 +4756,7 @@ p_timecode_check_and_log(FILE *fp_timecode_log, gint32 framenr, t_GVA_ffmpeg *ha
   int64_t diff_dts;
 
   expected_dts = p_frame_nr_to_timecode(master_handle, framenr);
-  
+
   if (abs(expected_dts - handle->vid_pkt.dts) > 10)
   {
     master_handle->critical_timecodesteps_found = TRUE;
@@ -4691,7 +4768,7 @@ p_timecode_check_and_log(FILE *fp_timecode_log, gint32 framenr, t_GVA_ffmpeg *ha
   {
     remark_ptr = ok_string;
   }
-  
+
   if (fp_timecode_log == NULL)
   {
     return;
@@ -4714,7 +4791,7 @@ p_timecode_check_and_log(FILE *fp_timecode_log, gint32 framenr, t_GVA_ffmpeg *ha
           , expected_dts - handle->vid_pkt.pts
           , handle->vid_pkt.pts - handle->vid_pkt.dts
           );
-          
+
     if (old_pts != AV_NOPTS_VALUE)
     {
       fprintf(fp_timecode_log, "; pts-oldpts:%lld"
@@ -4727,7 +4804,7 @@ p_timecode_check_and_log(FILE *fp_timecode_log, gint32 framenr, t_GVA_ffmpeg *ha
   {
     fprintf(fp_timecode_log, "; pts:NOPTS_VAL"
           );
-          
+
   }
 
   fprintf(fp_timecode_log, "%s\n"
@@ -4793,7 +4870,7 @@ p_save_video_analyse_results(t_GVA_Handle *gvahand)
 
   master_handle = (t_GVA_ffmpeg *)gvahand->decoder_handle;
   analysefile_name = p_create_analysefile_name(gvahand);
-  
+
   /* current videofile timestamp */
   master_handle->timestamp = gap_file_get_mtime(gvahand->filename);
   master_handle->readsteps_probe_timecode = READSTEPS_PROBE_TIMECODE;
@@ -4808,7 +4885,7 @@ p_save_video_analyse_results(t_GVA_Handle *gvahand)
     if (master_handle->self_test_detected_seek_bug == TRUE)
     {
         fprintf(fp_analyse, "NONE");
-    } 
+    }
     else if (master_handle->video_libavformat_seek_gopsize > 0)
     {
         fprintf(fp_analyse, "NATIVE");
@@ -4829,7 +4906,7 @@ p_save_video_analyse_results(t_GVA_Handle *gvahand)
         duration = master_handle->vid_input_context->duration;
         duration3 =
           av_rescale_q(duration, AV_TIME_BASE_Q, master_handle->vid_stream->time_base);
-                
+
         stt = master_handle->vid_input_context->start_time;
         /* scale stat_time from global AV_TIME_BASE to stream specific timecode */
         stt =
@@ -4856,7 +4933,7 @@ p_save_video_analyse_results(t_GVA_Handle *gvahand)
     fclose(fp_analyse);
 
     keylist = gap_val_new_keylist();
-    
+
     /* setup key/value descriptions */
     p_set_analysefile_master_keywords(keylist, gvahand, master_handle->count_timecode_steps);
 
@@ -4894,13 +4971,13 @@ p_get_video_analyse_results(t_GVA_Handle *gvahand)
   gint32 curr_mtime;
   gint ii;
   gboolean ret;
-  
-  
+
+
   analysefile_name = p_create_analysefile_name(gvahand);
   master_handle = (t_GVA_ffmpeg *)gvahand->decoder_handle;
   keylist = gap_val_new_keylist();
   p_set_analysefile_master_keywords(keylist, gvahand, READSTEPS_PROBE_TIMECODE);
-  
+
   /* init structures with some non-plausible values
    * (will be overwritten on sucessful fetch)
    */
@@ -4908,10 +4985,10 @@ p_get_video_analyse_results(t_GVA_Handle *gvahand)
   {
     master_handle->timecode_steps[ii] = UNDEFINED_TIMECODE_STEP_VALUE;
   }
-  
+
   min_expected_items = 10;
   master_handle->count_timecode_steps = -1;
-  
+
   scanned_items = gap_val_scann_filevalues(keylist, analysefile_name);
   gap_val_free_keylist(keylist);
   curr_mtime = gap_file_get_mtime(gvahand->filename);
@@ -4947,8 +5024,8 @@ p_get_video_analyse_results(t_GVA_Handle *gvahand)
       ,(int)scanned_items
       ,(int)master_handle->timestamp
       ,(int)curr_mtime
-      ); 
-  }  
+      );
+  }
 
   g_free(analysefile_name);
   return (ret);
@@ -4965,7 +5042,7 @@ static char*
 p_create_analysefile_name(t_GVA_Handle *gvahand)
 {
   char *vindex_name;
-  
+
   vindex_name = p_build_gvaidx_filename(gvahand->filename
                                        , gvahand->vid_track
                                        , GVA_FFMPEG_DECODER_NAME
@@ -5078,7 +5155,7 @@ p_frame_nr_to_timecode(t_GVA_ffmpeg *handle, gint32 frame_nr)
       , (int)handle->timecode_steps_sum
       );
   }
-  
+
   return(timecode);
 
 }  /* end p_frame_nr_to_timecode */
@@ -5099,7 +5176,7 @@ p_analyze_stepsize_pattern(gint max_idx, t_GVA_Handle *gvahand)
   gint jj;
   t_GVA_ffmpeg *master_handle;
   int gap_debug_local;
-  
+
   gap_debug_local = gap_debug;
 
 #ifdef GAP_DEBUG_FF_NATIVE_SEEK
@@ -5185,7 +5262,7 @@ p_analyze_stepsize_pattern(gint max_idx, t_GVA_Handle *gvahand)
  * Note that duration of frames may not be constant for all videofiles.
  * for videos with very individual frame durations it is not possible
  * to calculate correct timecode for a given frame number.
- * Typically most (DVD) videos use individual frame timing 
+ * Typically most (DVD) videos use individual frame timing
  * in cyclic pattern manner. This probe read analyzes the type of pattern
  * and enables correct frame to timestamp conversion
  * based on the measured resulting array timecode_steps[]
@@ -5217,7 +5294,7 @@ p_probe_timecode_offset(t_GVA_Handle *master_gvahand)
     l_countValidTimecodes = 0;
     master_handle->timecode_proberead_done = TRUE;
     master_handle->timecode_step_abs_min = 99999999;
-    
+
     /* calculate average timecode step per frame via framerate */
     avg_fstepsize = 100000.0 / master_gvahand->framerate;
     master_handle->timecode_steps[0] = avg_fstepsize;
@@ -5228,7 +5305,7 @@ p_probe_timecode_offset(t_GVA_Handle *master_gvahand)
             , READSTEPS_PROBE_TIMECODE
             );
     }
-    
+
     /* open an extra handle for the probe read */
     copy_gvahand = g_malloc0(sizeof(t_GVA_Handle));
     p_wrapper_ffmpeg_open_read(master_gvahand->filename
@@ -5262,7 +5339,7 @@ p_probe_timecode_offset(t_GVA_Handle *master_gvahand)
          MIN(abs(master_handle->timecode_steps[l_readsteps])
              , master_handle->timecode_step_abs_min);
       l_readsteps++;
-      master_handle->timecode_step_avg = 
+      master_handle->timecode_step_avg =
              (copy_handle->vid_pkt.dts - master_handle->timecode_offset_frame1) / l_readsteps;
 
       if(gap_debug)
@@ -5280,14 +5357,14 @@ p_probe_timecode_offset(t_GVA_Handle *master_gvahand)
 
     /* close the extra handle (that was opened for counting only) */
     p_wrapper_ffmpeg_close(copy_gvahand);
-    
+
     if (l_countValidTimecodes > 0)
     {
         p_analyze_stepsize_pattern(l_readsteps, master_gvahand);
     }
     else
     {
-       /* some older ffmpeg versions did always deliver valid dts timecodes, 
+       /* some older ffmpeg versions did always deliver valid dts timecodes,
         * even if not present in the video.
         * but unfortunately recent ffmpeg snapshots deliver AV_NOPTS_VALUE as dts
         * for such videos. In this case native seek must be disabled.
@@ -5296,7 +5373,7 @@ p_probe_timecode_offset(t_GVA_Handle *master_gvahand)
        master_handle->count_timecode_steps = 1;
        master_handle->video_libavformat_seek_gopsize = 0; /* DISABLE natvie seek */
        master_handle->prefere_native_seek = FALSE;
-    
+
        printf("WARNING: p_probe_timecode_offset no valid timecode found in video:%s\n"
           , master_gvahand->filename
           );
@@ -5305,7 +5382,7 @@ p_probe_timecode_offset(t_GVA_Handle *master_gvahand)
     }
 
   }
-    
+
 }  /* end p_probe_timecode_offset */
 
 
diff --git a/libgapvidapi/gap_vid_api_mpeg3.c b/libgapvidapi/gap_vid_api_mpeg3.c
index 1a5c155..4f900e8 100644
--- a/libgapvidapi/gap_vid_api_mpeg3.c
+++ b/libgapvidapi/gap_vid_api_mpeg3.c
@@ -334,7 +334,7 @@ p_wrapper_mpeg3_open_read(char *in_filename, t_GVA_Handle *gvahand)
         gvahand->height           = mpeg3_video_height(handle->main_handle, (int)gvahand->vid_track);
         gvahand->aspect_ratio     = mpeg3_aspect_ratio(handle->main_handle, (int)gvahand->vid_track);
 
-        /* libmpeg3-1.5.4 has a bug and did always deliver apect ratio value 0.0
+        /* libmpeg3-1.5.4 has a bug and did always deliver aspect ratio value 0.0
          * the aspect was not recognized when it was there 
          * tested with some DVD and MPEG1 videofiles
          */
@@ -853,7 +853,7 @@ p_wrapper_mpeg3_count_frames(t_GVA_Handle *gvahand)
     
     if(gvahand->cancel_operation)
     {
-      /* delete uncomplete TOC file
+      /* delete incomplete TOC file
        * (dont know if libmpeg3 can use unfinished TOC files)
        */
       g_remove(vindex->tocfile);
@@ -883,7 +883,7 @@ p_wrapper_mpeg3_count_frames(t_GVA_Handle *gvahand)
       /* because of cancel the total_frames is still unknown
        * (vindex->total_frames == 0 is the indicator for incomplete index)
        * For libmpeg3: videoindex is just used to store total_frames info
-       *               so we dont save uncomplete videoindex file
+       *               so we dont save incomplete videoindex file
        */
       vindex->total_frames = 0;
     }
@@ -1117,7 +1117,7 @@ p_mpeg3_new_dec_elem(void)
 /* -----------------------
  * p_mpeg3_emulate_seek
  * -----------------------
- * procedure to completly emulate frame seek by dummy reads
+ * procedure to completely emulate frame seek by dummy reads
  * (is very slow but sets position to exact frame position
  *  this is needed if we have no TOC file)
  */
@@ -1183,7 +1183,7 @@ p_mpeg3_emulate_seek(mpeg3_t*  handle, gint32 seekto_frame, t_GVA_Handle *gvahan
   if(l_clean_reads > 0)
   {
     /* after (re)open we must start with a clean read
-     * (never start with a dirty read (because ist does not advance position as expected)
+     * (never start with a dirty read (because it does not advance position as expected)
      */
      l_clean_reads--;
 
@@ -1226,7 +1226,7 @@ p_mpeg3_emulate_seek(mpeg3_t*  handle, gint32 seekto_frame, t_GVA_Handle *gvahan
 
     for(l_ii = 0; l_ii < l_dirty_reads; l_ii++)
     {
-      /* read one frame (we use the comressed chunk
+      /* read one frame (we use the compressed chunk
        * because this is a dummy read to skip frames only)
        */
       l_rc = mpeg3_read_video_chunk(seek_handle,
@@ -1354,8 +1354,8 @@ p_mpeg3_gopseek(mpeg3_t*  handle, gint32 seekto_frame, t_GVA_Handle *gvahand)
     ||   ((gvahand->current_seek_nr + GVA_GOPSEEKSIZE) <= seekto_frame)  )
     {
        if(gap_debug) printf("p_mpeg3_gopseek(F): (GOPSEEK NEEDED)\n");
-      /* perform (faster) seek if we have to do backstep
-       * or if we have to do a bigstep forward (greater than GVA_GOPSEEKSIZE)
+      /* perform (faster) seek if we have to do step back
+       * or if we have to do a big step forward (greater than GVA_GOPSEEKSIZE)
        */
       l_rc = mpeg3_set_frame(seek_handle, l_gopseek +1, (int)gvahand->vid_track);
       gvahand->percentage_done += l_progress_step;
@@ -1426,9 +1426,9 @@ p_mpeg3_gopseek(mpeg3_t*  handle, gint32 seekto_frame, t_GVA_Handle *gvahand)
  * the name of the toc file must then be passed to the 
  * the mpeg3_open procedure (rather than the original videofile)
  *
- * toc files can be created explicite with the commandline tool mpeg3toc
+ * toc files can be created explicitly with the commandline tool mpeg3toc
  * (that is part of the libmpeg3 distribution)
- * or implicite by the GVA video Api procedures.
+ * or implicitly by the GVA video Api procedures.
  */
 gboolean
 p_check_libmpeg3_toc_file(const char *filename)
diff --git a/libgapvidapi/gap_vid_api_mpeg3toc.c b/libgapvidapi/gap_vid_api_mpeg3toc.c
index 15e4fc4..7e23517 100644
--- a/libgapvidapi/gap_vid_api_mpeg3toc.c
+++ b/libgapvidapi/gap_vid_api_mpeg3toc.c
@@ -1,14 +1,14 @@
-/* this file is a modified variante
+/* this file is a modified variant
  * of the meg3toc.c Code that came with libmpeg3-1.8
  *
  * it is used to build the libmpeg3
- * compatible "Table of content" files implicite
+ * compatible "Table of content" files implicitly
  * when using the GIMP-GAP specific GVA video API.
  * .TOC files are the libmpeg3 specific pendant to gvaindexes
  * and enable fast and exact positioning in the MPEG videofile
  * (but need one initial full scan to create)
  *
- * This variante of the toc file genration is used in GIMP-GAP
+ * This variant of the toc file generation is used in GIMP-GAP
  * GUI Modules and provides Progress Feedback for the calling GUI
  * and has the option to STOP (cancel) TOC creation on GUI request.
  *
diff --git a/libgapvidutil/gap_gve_raw.c b/libgapvidutil/gap_gve_raw.c
index ea604d2..bf2a686 100644
--- a/libgapvidutil/gap_gve_raw.c
+++ b/libgapvidutil/gap_gve_raw.c
@@ -190,9 +190,9 @@ gap_gve_raw_RGB_drawable_encode(GimpDrawable *drawable, gint32 *RAW_size, gboole
                               , 1);
      for(l_idx=0;l_idx < l_rowstride; l_idx += drawable->bpp)
      {
-       *(RAW_ptr++) = pixelrow_data[l_idx + l_blue];
-       *(RAW_ptr++) = pixelrow_data[l_idx + l_green];
        *(RAW_ptr++) = pixelrow_data[l_idx + l_red];
+       *(RAW_ptr++) = pixelrow_data[l_idx + l_green];
+       *(RAW_ptr++) = pixelrow_data[l_idx + l_blue];
      }
   }
   g_free(pixelrow_data);
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 406a70c..6a0a80c 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -7,6 +7,9 @@ gap/gap_audio_wav.c
 gap/gap_base_ops.c
 gap/gap_bluebox.c
 gap/gap_bluebox_main.c
+gap/gap_colormask_dialog.c
+gap/gap_colormask_file.c
+gap/gap_colormask_main.c
 gap/gap_dbbrowser_utils.c
 gap/gap_decode_mplayer_main.c
 gap/gap_decode_mplayer.c
@@ -24,6 +27,7 @@ gap/gap_mod_layer_dialog.c
 gap/gap_morph_dialog.c
 gap/gap_morph_exec.c
 gap/gap_morph_main.c
+gap/gap_morph_shape.c
 gap/gap_morph_tween_dialog.c
 gap/gap_mov_dialog.c
 gap/gap_mov_exec.c
@@ -54,6 +58,7 @@ gap/gap_vex_dialog.c
 gap/gap_vex_exec.c
 gap/gap_video_index_creator.c
 gap/gap_vex_main.c
+gap/gap_wr_color_balance.c
 gap/gap_wr_color_curve.c
 gap/gap_wr_color_huesat.c
 gap/gap_wr_color_levels.c
diff --git a/vid_enc_ffmpeg/gap_enc_ffmpeg_gui.c b/vid_enc_ffmpeg/gap_enc_ffmpeg_gui.c
index aa75df3..88928d9 100644
--- a/vid_enc_ffmpeg/gap_enc_ffmpeg_gui.c
+++ b/vid_enc_ffmpeg/gap_enc_ffmpeg_gui.c
@@ -284,7 +284,7 @@ p_replace_combo_vid_codec(GapGveFFMpegGlobalParams *gpp)
   {
      char *menu_name;
      char *object_data;
-     if ((avcodec->encode) && (avcodec->type == CODEC_TYPE_VIDEO))
+     if ((avcodec->encode) && (avcodec->type == AVMEDIA_TYPE_VIDEO))
      {
        object_data = (char *)avcodec->name;
 
@@ -358,7 +358,7 @@ p_replace_combo_aud_codec(GapGveFFMpegGlobalParams *gpp)
   {
      char *menu_name;
      char *object_data;
-     if ((avcodec->encode) && (avcodec->type == CODEC_TYPE_AUDIO))
+     if ((avcodec->encode) && (avcodec->type == AVMEDIA_TYPE_AUDIO))
      {
        object_data = (char *)avcodec->name;
 
@@ -3736,7 +3736,7 @@ p_create_ffmpeg_dialog_shell (GapGveFFMpegGlobalParams *gpp)
   gpp->ff_codec_FLAG_GRAY_checkbutton = NULL;
   gpp->ff_codec_FLAG_EMU_EDGE_checkbutton = NULL;
   gpp->ff_codec_FLAG_TRUNCATED_checkbutton = NULL;
-  
+
   gpp->ff_codec_FLAG2_FAST_checkbutton = NULL;
   gpp->ff_codec_FLAG2_LOCAL_HEADER_checkbutton = NULL;
   gpp->ff_codec_FLAG2_BPYRAMID_checkbutton = NULL;
diff --git a/vid_enc_ffmpeg/gap_enc_ffmpeg_main.c b/vid_enc_ffmpeg/gap_enc_ffmpeg_main.c
index c95c256..eadbec1 100644
--- a/vid_enc_ffmpeg/gap_enc_ffmpeg_main.c
+++ b/vid_enc_ffmpeg/gap_enc_ffmpeg_main.c
@@ -76,7 +76,8 @@
 #include <libgimp/gimp.h>
 #include <libgimp/gimpui.h>
 
-#include "imgconvert.h"
+//#include "imgconvert.h"
+#include "swscale.h"
 
 #include "gap-intl.h"
 
@@ -204,6 +205,8 @@ typedef struct t_ffmpeg_handle
  gdouble pts_stepsize_per_frame;
  gint32 encode_frame_nr;          /* number of frame to encode always starts at 1 and inc by one (base for monotone timecode calculation) */
 
+ struct SwsContext *img_convert_ctx;
+
 } t_ffmpeg_handle;
 
 
@@ -230,6 +233,7 @@ static void run(const gchar *name
 
 
 static void   p_debug_print_dump_AVCodecContext(AVCodecContext *codecContext);
+static int    p_av_metadata_set(AVMetadata **pm, const char *key, const char *value, int flags);
 static void   p_set_flag(gint32 value_bool32, int *flag_ptr, int maskbit);
 
 
@@ -699,7 +703,7 @@ static void
 p_debug_print_dump_AVCodecContext(AVCodecContext *codecContext)
 {
   printf("AVCodecContext Settings START\n");
-  
+
   printf("(av_class (ptr AVClass*)      %d)\n", (int)    codecContext->av_class);
   printf("(bit_rate                     %d)\n", (int)    codecContext->bit_rate);
   printf("(bit_rate_tolerance           %d)\n", (int)    codecContext->bit_rate_tolerance);
@@ -901,12 +905,49 @@ p_debug_print_dump_AVCodecContext(AVCodecContext *codecContext)
   printf("(rc_max_available_vbv_use     %f)\n", (float)  codecContext->rc_max_available_vbv_use);
   printf("(rc_min_vbv_overflow_use      %f)\n", (float)  codecContext->rc_min_vbv_overflow_use);
 
+#ifndef GAP_USES_OLD_FFMPEG_0_5
+
+  printf("(hwaccel_context              %d)\n", (int)    codecContext->hwaccel_context);
+  printf("(color_primaries              %d)\n", (int)    codecContext->color_primaries);
+  printf("(color_trc                    %d)\n", (int)    codecContext->color_trc);
+  printf("(colorspace                   %d)\n", (int)    codecContext->colorspace);
+  printf("(color_range                  %d)\n", (int)    codecContext->color_range);
+  printf("(chroma_sample_location       %d)\n", (int)    codecContext->chroma_sample_location);
+  /* *func */
+  printf("(weighted_p_pred              %d)\n", (int)    codecContext->weighted_p_pred);
+  printf("(aq_mode                      %d)\n", (int)    codecContext->aq_mode);
+  printf("(aq_strength                  %f)\n", (float)  codecContext->aq_strength);
+  printf("(psy_rd                       %f)\n", (float)  codecContext->psy_rd);
+  printf("(psy_trellis                  %f)\n", (float)  codecContext->psy_trellis);
+  printf("(rc_lookahead                 %d)\n", (int)    codecContext->rc_lookahead);
+
+#endif
+
   printf("AVCodecContext Settings END\n");
 
 }  /* end p_debug_print_dump_AVCodecContext */
 
 
 /* --------------------------------
+ * p_av_metadata_set
+ * --------------------------------
+ */
+static int
+p_av_metadata_set(AVMetadata **pm, const char *key, const char *value, int flags)
+{
+  int ret;
+  
+#ifndef GAP_USES_OLD_FFMPEG_0_5
+  ret = av_metadata_set2(pm, key, value, flags);
+#else
+  ret = av_metadata_set(pm, key, value);
+#endif  
+  
+  return (ret);
+}  /* end p_av_metadata_set */
+
+
+/* --------------------------------
  * p_set_flag
  * --------------------------------
  */
@@ -1155,7 +1196,7 @@ gap_enc_ffmpeg_main_init_preset_params(GapGveFFMpegValues *epp, gint preset_idx)
 
 
   /* new parms 2009.01.31 */
-  
+
   epp->b_frame_strategy              = 0;
   epp->workaround_bugs               = FF_BUG_AUTODETECT;
   epp->error_recognition             = FF_ER_CAREFUL;
@@ -1177,7 +1218,7 @@ gap_enc_ffmpeg_main_init_preset_params(GapGveFFMpegValues *epp, gint preset_idx)
   epp->cqp                           = -1;
   epp->keyint_min                    = 25;   /* minimum GOP size */
   epp->refs                          = 1;
-  epp->chromaoffset                  = 0;        
+  epp->chromaoffset                  = 0;
   epp->bframebias                    = 0;
   epp->trellis                       = 0;
   epp->complexityblur                = 20.0;
@@ -1201,7 +1242,20 @@ gap_enc_ffmpeg_main_init_preset_params(GapGveFFMpegValues *epp, gint preset_idx)
   epp->channel_layout                = 0;
   epp->rc_max_available_vbv_use      = 1.0 / 3.0;
   epp->rc_min_vbv_overflow_use       = 3.0;
-  
+
+  /* new params of ffmpeg-0.6 2010.07.31 */
+  epp->color_primaries               =  0; // TODO  enum AVColorPrimaries color_primaries;
+  epp->color_trc                     =  0; //  enum AVColorTransferCharacteristic color_trc;
+  epp->colorspace                    =  0; //  enum AVColorSpace colorspace;
+  epp->color_range                   =  0; //  enum AVColorRange color_range;
+  epp->chroma_sample_location        =  0; //  enum AVChromaLocation chroma_sample_location;
+  epp->weighted_p_pred               = 0;
+  epp->aq_mode                       = 0;
+  epp->aq_strength                   = 0.0; 
+  epp->psy_rd                        = 0.0;
+  epp->psy_trellis                   = 0.0;
+  epp->rc_lookahead                  = 0;
+
   /* new flags/flags2 2009.01.31 */
   epp->codec_FLAG_GMC                   = 0; /* 0: FALSE */
   epp->codec_FLAG_INPUT_PRESERVED       = 0; /* 0: FALSE */
@@ -1226,6 +1280,11 @@ gap_enc_ffmpeg_main_init_preset_params(GapGveFFMpegValues *epp, gint preset_idx)
   epp->codec_FLAG2_NON_LINEAR_QUANT     = 0; /* 0: FALSE */
   epp->codec_FLAG2_BIT_RESERVOIR        = 0; /* 0: FALSE */
 
+  /* new flags/flags2 of ffmpeg-0.6 2010.07.31 */
+  epp->codec_FLAG2_MBTREE               = 0; /* 0: FALSE */
+  epp->codec_FLAG2_PSY                  = 0; /* 0: FALSE */
+  epp->codec_FLAG2_SSIM                 = 0; /* 0: FALSE */
+
 }   /* end gap_enc_ffmpeg_main_init_preset_params */
 
 
@@ -1621,7 +1680,7 @@ p_calculate_current_timecode(t_ffmpeg_handle *ffh)
 {
   gdouble dblTimecode;
   gint64  timecode64;
-  
+
   /*
    * gdouble seconds;
    * seconds = (ffh->encode_frame_nr  / gpp->val.framerate);
@@ -1629,7 +1688,7 @@ p_calculate_current_timecode(t_ffmpeg_handle *ffh)
 
   dblTimecode = ffh->encode_frame_nr * ffh->pts_stepsize_per_frame;
   timecode64 = dblTimecode;
-  
+
   return (timecode64);
 }
 
@@ -1669,19 +1728,19 @@ p_set_timebase_from_framerate(AVRational *time_base, gdouble framerate)
   {
     time_base->num = 1001;
     time_base->den = 60000;
-  
+
   }
   if ((framerate > 29.90) && (framerate < 29.99))
   {
     time_base->num = 1001;
     time_base->den = 30000;
-  
+
   }
   if ((framerate > 23.90) && (framerate < 23.99))
   {
     time_base->num = 1001;
     time_base->den = 24000;
-  
+
   }
 
 }  /* end p_set_timebase_from_framerate */
@@ -1729,10 +1788,10 @@ p_ffmpeg_open_init(t_ffmpeg_handle *ffh, GapGveFFMpegGlobalParams *gpp)
     ffh->ast[ii].audio_buffer = NULL;
     ffh->ast[ii].audio_buffer_size = 0;
   }
-  
+
   {
     AVRational l_time_base;
-    
+
     p_set_timebase_from_framerate(&l_time_base, gpp->val.framerate);
     ffh->pts_stepsize_per_frame = l_time_base.num;
 
@@ -1786,7 +1845,7 @@ p_init_video_codec(t_ffmpeg_handle *ffh
      g_free(ffh);
      return(FALSE); /* error */
   }
-  if(ffh->vst[ii].vid_codec->type != CODEC_TYPE_VIDEO)
+  if(ffh->vst[ii].vid_codec->type != AVMEDIA_TYPE_VIDEO)
   {
      printf("CODEC: %s is no VIDEO CODEC!\n", epp->vcodec_name);
      g_free(ffh);
@@ -1825,14 +1884,14 @@ p_init_video_codec(t_ffmpeg_handle *ffh
 
   video_enc = ffh->vst[ii].vid_codec_context;
 
-  video_enc->codec_type = CODEC_TYPE_VIDEO;
+  video_enc->codec_type = AVMEDIA_TYPE_VIDEO;
   video_enc->codec_id = ffh->vst[ii].vid_codec->id;
 
   video_enc->bit_rate = epp->video_bitrate * 1000;
   video_enc->bit_rate_tolerance = epp->bitrate_tol * 1000;
 
   p_set_timebase_from_framerate(&video_enc->time_base, gpp->val.framerate);
-      
+
   video_enc->width = gpp->val.vid_width;
   video_enc->height = gpp->val.vid_height;
 
@@ -2005,6 +2064,12 @@ p_init_video_codec(t_ffmpeg_handle *ffh
   p_set_flag(epp->codec_FLAG2_NON_LINEAR_QUANT,    &video_enc->flags2, CODEC_FLAG2_NON_LINEAR_QUANT);
   p_set_flag(epp->codec_FLAG2_BIT_RESERVOIR,       &video_enc->flags2, CODEC_FLAG2_BIT_RESERVOIR);
 
+#ifndef GAP_USES_OLD_FFMPEG_0_5
+  p_set_flag(epp->codec_FLAG2_MBTREE,              &video_enc->flags2, CODEC_FLAG2_MBTREE);
+  p_set_flag(epp->codec_FLAG2_PSY,                 &video_enc->flags2, CODEC_FLAG2_PSY);
+  p_set_flag(epp->codec_FLAG2_SSIM,                &video_enc->flags2, CODEC_FLAG2_SSIM);
+#endif
+
 
   if ((epp->b_frames > 0) && (!epp->intra))
   {
@@ -2036,7 +2101,7 @@ p_init_video_codec(t_ffmpeg_handle *ffh
         break;
     }
   }
-  
+
   video_enc->qmin      = epp->qmin;
   video_enc->qmax      = epp->qmax;
   video_enc->max_qdiff = epp->qdiff;
@@ -2069,8 +2134,8 @@ p_init_video_codec(t_ffmpeg_handle *ffh
   video_enc->workaround_bugs        = epp->workaround_bugs;
   video_enc->error_recognition      = epp->error_recognition;
   video_enc->mpeg_quant             = epp->mpeg_quant;
-  
-  
+
+
   video_enc->debug                  = 0;
   video_enc->mb_qmin                = epp->mb_qmin;
   video_enc->mb_qmax                = epp->mb_qmax;
@@ -2081,8 +2146,8 @@ p_init_video_codec(t_ffmpeg_handle *ffh
   video_enc->rc_qsquish             = epp->rc_qsquish;
   video_enc->rc_qmod_amp            = epp->rc_qmod_amp;
   video_enc->rc_qmod_freq           = epp->rc_qmod_freq;
-  
-  
+
+
   video_enc->luma_elim_threshold    = epp->video_lelim;
   video_enc->chroma_elim_threshold  = epp->video_celim;
 
@@ -2108,7 +2173,7 @@ p_init_video_codec(t_ffmpeg_handle *ffh
 
   video_enc->nsse_weight            = epp->nsse_weight;
   video_enc->me_subpel_quality      = epp->subpel_quality;
-  
+
   video_enc->dia_size               = epp->dia_size;
   video_enc->last_predictor_count   = epp->last_predictor_count;
   video_enc->pre_dia_size           = epp->pre_dia_size;
@@ -2130,7 +2195,7 @@ p_init_video_codec(t_ffmpeg_handle *ffh
   video_enc->cqp                      = epp->cqp;
   video_enc->keyint_min               = epp->keyint_min;
   video_enc->refs                     = epp->refs;
-  video_enc->chromaoffset             = epp->chromaoffset;        
+  video_enc->chromaoffset             = epp->chromaoffset;
   video_enc->bframebias               = epp->bframebias;
   video_enc->trellis                  = epp->trellis;
   video_enc->complexityblur           = (float) epp->complexityblur;
@@ -2153,6 +2218,21 @@ p_init_video_codec(t_ffmpeg_handle *ffh
   video_enc->rc_max_available_vbv_use = (float)epp->rc_max_available_vbv_use;
   video_enc->rc_min_vbv_overflow_use  = (float)epp->rc_min_vbv_overflow_use;
 
+#ifndef GAP_USES_OLD_FFMPEG_0_5
+  //video_enc->hwaccel_context == NULL;
+  video_enc->color_primaries = epp->color_primaries;
+  video_enc->color_trc = epp->color_trc;
+  video_enc->colorspace = epp->colorspace;
+  video_enc->color_range = epp->color_range;
+  video_enc->chroma_sample_location = epp->chroma_sample_location;
+  //video_enc->func = NULL;
+  video_enc->weighted_p_pred = (int)epp->weighted_p_pred;
+  video_enc->aq_mode = (int)epp->aq_mode;
+  video_enc->aq_strength = (float)epp->aq_strength;
+  video_enc->psy_rd = (float)epp->psy_rd;
+  video_enc->psy_trellis = (float)epp->psy_trellis;
+  video_enc->rc_lookahead = (int)epp->rc_lookahead;
+#endif
 
 
   if(epp->packet_size)
@@ -2167,19 +2247,19 @@ p_init_video_codec(t_ffmpeg_handle *ffh
 
   if (epp->title[0] != '\0')
   {
-      av_metadata_set(&ffh->output_context->metadata, "title",  &epp->title[0]);
+      p_av_metadata_set(&ffh->output_context->metadata, "title",  &epp->title[0], 0);
   }
   if (epp->author[0] != '\0')
   {
-      av_metadata_set(&ffh->output_context->metadata, "author",  &epp->author[0]);
+      p_av_metadata_set(&ffh->output_context->metadata, "author",  &epp->author[0], 0);
   }
   if (epp->copyright[0] != '\0')
   {
-      av_metadata_set(&ffh->output_context->metadata, "copyright",  &epp->copyright[0]);
+      p_av_metadata_set(&ffh->output_context->metadata, "copyright",  &epp->copyright[0], 0);
   }
   if (epp->comment[0] != '\0')
   {
-      av_metadata_set(&ffh->output_context->metadata, "comment",  &epp->comment[0]);
+      p_av_metadata_set(&ffh->output_context->metadata, "comment",  &epp->comment[0], 0);
   }
 
 
@@ -2265,7 +2345,7 @@ p_init_video_codec(t_ffmpeg_handle *ffh
 
 
   return (TRUE); /* OK */
-  
+
 }  /* end p_init_video_codec */
 
 
@@ -2291,7 +2371,7 @@ p_init_and_open_audio_codec(t_ffmpeg_handle *ffh
 
   audioOK = TRUE;
   msg = NULL;
-  
+
   /* ------------ Start Audio CODEC init -------   */
   if(audio_channels > 0)
   {
@@ -2305,7 +2385,7 @@ p_init_and_open_audio_codec(t_ffmpeg_handle *ffh
     }
     else
     {
-      if(ffh->ast[ii].aud_codec->type != CODEC_TYPE_AUDIO)
+      if(ffh->ast[ii].aud_codec->type != AVMEDIA_TYPE_AUDIO)
       {
          audioOK = FALSE;
          msg = g_strdup_printf(_("CODEC: %s is no AUDIO CODEC!"), epp->acodec_name);
@@ -2326,7 +2406,7 @@ p_init_and_open_audio_codec(t_ffmpeg_handle *ffh
         /*  ffh->ast[ii].aud_codec_context = avcodec_alloc_context();*/
         audio_enc = ffh->ast[ii].aud_codec_context;
 
-        audio_enc->codec_type = CODEC_TYPE_AUDIO;
+        audio_enc->codec_type = AVMEDIA_TYPE_AUDIO;
         audio_enc->codec_id = ffh->ast[ii].aud_codec->id;
 
         audio_enc->bit_rate = epp->audio_bitrate * 1000;
@@ -2340,7 +2420,7 @@ p_init_and_open_audio_codec(t_ffmpeg_handle *ffh
         audio_enc->workaround_bugs       = epp->workaround_bugs;
         audio_enc->error_recognition     = epp->error_recognition;
         audio_enc->cutoff                = epp->cutoff;
-        
+
         if (bits == 16)
         {
           audio_enc->sample_fmt = SAMPLE_FMT_S16;
@@ -2349,8 +2429,8 @@ p_init_and_open_audio_codec(t_ffmpeg_handle *ffh
         {
           audio_enc->sample_fmt = SAMPLE_FMT_U8;
         }
-        
-        
+
+
         switch (audio_channels)
         {
           case 1:
@@ -2362,14 +2442,14 @@ p_init_and_open_audio_codec(t_ffmpeg_handle *ffh
           default:
             audio_enc->channel_layout = epp->channel_layout;
             break;
-            
+
         }
 
         /* open audio codec */
         if (avcodec_open(ffh->ast[ii].aud_codec_context, ffh->ast[ii].aud_codec) < 0)
         {
           audioOK = FALSE;
-          
+
           msg = g_strdup_printf(_("could not open audio codec: %s\n"
                       "at audio_samplerate:%d channels:%d bits per channel:%d\n"
                       "(try to convert to 48 KHz, 44.1KHz or 32 kHz samplerate\n"
@@ -2381,7 +2461,7 @@ p_init_and_open_audio_codec(t_ffmpeg_handle *ffh
                      );
           ffh->ast[ii].aud_codec = NULL;
         }
-        
+
       }
     }
   }
@@ -2447,7 +2527,11 @@ p_ffmpeg_open(GapGveFFMpegGlobalParams *gpp
 
   /* ------------ File Format  -------   */
 
+#ifndef GAP_USES_OLD_FFMPEG_0_5
+  ffh->file_oformat = av_guess_format(epp->format_name, gpp->val.videoname, NULL);
+#else
   ffh->file_oformat = guess_format(epp->format_name, gpp->val.videoname, NULL);
+#endif
   if (!ffh->file_oformat)
   {
      printf("Unknown output format: %s\n", epp->format_name);
@@ -2558,7 +2642,7 @@ p_ffmpeg_open(GapGveFFMpegGlobalParams *gpp
    */
   ffh->ap->sample_rate = awp->awk[0].sample_rate;
   ffh->ap->channels = awp->awk[0].channels;
-  
+
   p_set_timebase_from_framerate(&ffh->ap->time_base, gpp->val.framerate);
 
   ffh->ap->width = gpp->val.vid_width;
@@ -2637,6 +2721,8 @@ p_ffmpeg_open(GapGveFFMpegGlobalParams *gpp
   ffh->output_context->preload     = (int)(epp->mux_preload*AV_TIME_BASE);
   ffh->output_context->max_delay   = (int)(epp->mux_max_delay*AV_TIME_BASE);
 
+  ffh->img_convert_ctx = NULL; /* will be allocated at first img conversion */
+
   if(gap_debug)
   {
     printf("\np_ffmpeg_open END\n");
@@ -2740,7 +2826,7 @@ p_ffmpeg_write_frame_chunk(t_ffmpeg_handle *ffh, gint32 encoded_size, gint vid_t
       chunk_frame_type = GVA_util_check_mpg_frame_type(ffh->vst[ii].video_buffer, encoded_size);
       if(chunk_frame_type == 1)  /* check for intra frame type */
       {
-        pkt.flags |= PKT_FLAG_KEY;
+        pkt.flags |= AV_PKT_FLAG_KEY;
       }
 
 
@@ -2758,14 +2844,14 @@ p_ffmpeg_write_frame_chunk(t_ffmpeg_handle *ffh, gint32 encoded_size, gint vid_t
       ///// pkt.pts = ffh->vst[ii].vid_codec_context->coded_frame->pts;  // OLD
       {
         AVCodecContext *c;
-        
+
         c = ffh->vst[ii].vid_codec_context;
         if (c->coded_frame->pts != AV_NOPTS_VALUE)
         {
           pkt.pts= av_rescale_q(c->coded_frame->pts, c->time_base, ffh->vst[ii].vid_stream->time_base);
         }
       }
-      
+
       pkt.dts = AV_NOPTS_VALUE;  /* let av_write_frame calculate the decompression timestamp */
       pkt.stream_index = ffh->vst[ii].video_stream_index;
       pkt.data = ffh->vst[ii].video_buffer;
@@ -2784,7 +2870,7 @@ p_ffmpeg_write_frame_chunk(t_ffmpeg_handle *ffh, gint32 encoded_size, gint vid_t
  * p_convert_colormodel
  * -----------------------
  * convert video frame specified in the rgb_buffer
- * from PIX_FMT_BGR24 to the colormodel that is required
+ * from PIX_FMT_RGB24 to the colormodel that is required
  * by the video codec.
  *
  * conversion is done based on ffmpegs img_convert procedure.
@@ -2796,7 +2882,7 @@ p_convert_colormodel(t_ffmpeg_handle *ffh, AVPicture *picture_codec, guchar *rgb
   AVPicture *picture_rgb;
   uint8_t   *l_convert_buffer;
   int        ii;
-  int        l_rc;
+  //int        l_rc;
 
   ii = ffh->vst[vid_track].video_stream_index;
 
@@ -2804,7 +2890,7 @@ p_convert_colormodel(t_ffmpeg_handle *ffh, AVPicture *picture_codec, guchar *rgb
   big_picture_rgb = avcodec_alloc_frame();
   picture_rgb = (AVPicture *)big_picture_rgb;
 
-    
+
   /* allocate buffer for image conversion large enough for for uncompressed RGBA32 colormodel */
   l_convert_buffer = g_malloc(4 * ffh->frame_width * ffh->frame_height);
 
@@ -2844,16 +2930,45 @@ p_convert_colormodel(t_ffmpeg_handle *ffh, AVPicture *picture_codec, guchar *rgb
         , (int)PIX_FMT_YUV420P);
   }
 
-  /* convert to pix_fmt needed by the codec */
-  l_rc = img_convert(picture_codec, ffh->vst[ii].vid_codec_context->pix_fmt  /* dst */
-               ,picture_rgb, PIX_FMT_BGR24                    /* src */
-               ,ffh->frame_width
-               ,ffh->frame_height
-               );
+  /* reuse the img_convert_ctx or create a new one
+   * (in case ctx is NULL or params have changed)
+   */
+  ffh->img_convert_ctx = sws_getCachedContext(ffh->img_convert_ctx
+                                         , ffh->frame_width
+                                         , ffh->frame_height
+                                         , PIX_FMT_RGB24               /* src pixelformat */
+                                         , ffh->frame_width
+                                         , ffh->frame_height
+                                         , ffh->vst[ii].vid_codec_context->pix_fmt    /* dest pixelformat */
+                                         , SWS_BICUBIC                 /* int sws_flags */
+                                         , NULL, NULL, NULL
+                                         );
+  if (ffh->img_convert_ctx == NULL)
+  {
+     printf("Cannot initialize the conversion context (sws_getCachedContext delivered NULL pointer)\n");
+     exit(1);
+  }
+
+  /* convert from RGB to pix_fmt needed by the codec */
+  sws_scale(ffh->img_convert_ctx
+           , picture_rgb->data        /* srcSlice */
+           , picture_rgb->linesize    /* srcStride the array containing the strides for each plane */
+           , 0                        /* srcSliceY starting at 0 */
+           , ffh->frame_height        /* srcSliceH the height of the source slice */
+           , picture_codec->data      /* dst */
+           , picture_codec->linesize  /* dstStride the array containing the strides for each plane */
+           );
+
+  // /* img_convert is no longer available since ffmpeg.0.6 */
+  //l_rc = img_convert(picture_codec, ffh->vst[ii].vid_codec_context->pix_fmt  /* dst */
+  //             ,picture_rgb, PIX_FMT_BGR24                    /* src */
+  //             ,ffh->frame_width
+  //             ,ffh->frame_height
+  //             );
 
   if(gap_debug)
   {
-    printf("after img_convert: l_rc:%d\n", l_rc);
+    printf("after sws_scale:\n");
   }
 
   g_free(big_picture_rgb);
@@ -2964,7 +3079,7 @@ p_ffmpeg_write_frame(t_ffmpeg_handle *ffh, GimpDrawable *drawable, gboolean forc
     {
       l_convert_buffer = p_convert_colormodel(ffh, picture_codec, rgb_buffer, vid_track);
     }
-    
+
 
     if(gap_debug)
     {
@@ -3021,26 +3136,26 @@ p_ffmpeg_write_frame(t_ffmpeg_handle *ffh, GimpDrawable *drawable, gboolean forc
 
 
         ///// pkt.pts = ffh->vst[ii].vid_codec_context->coded_frame->pts; // OLD
-        
+
         if (c->coded_frame->pts != AV_NOPTS_VALUE)
         {
           pkt.pts= av_rescale_q(c->coded_frame->pts, c->time_base, ffh->vst[ii].vid_stream->time_base);
         }
-        
+
 //        if ((pkt.pts == 0) || (pkt.pts == AV_NOPTS_VALUE))
 //        {
 //          /* WORKAROND calculate pts timecode for the current frame
-//           * because the codec did not deliver a valid timecode 
+//           * because the codec did not deliver a valid timecode
 //           */
 //          pkt.pts = p_calculate_current_timecode(ffh);
 //        }
 
-        
+
         if(c->coded_frame->key_frame)
         {
-          pkt.flags |= PKT_FLAG_KEY;
+          pkt.flags |= AV_PKT_FLAG_KEY;
         }
-        
+
         pkt.stream_index = ffh->vst[ii].video_stream_index;
         pkt.data = ffh->vst[ii].video_buffer;
         pkt.size = encoded_size;
@@ -3048,7 +3163,7 @@ p_ffmpeg_write_frame(t_ffmpeg_handle *ffh, GimpDrawable *drawable, gboolean forc
         if(gap_debug)
         {
           AVStream *st;
-          
+
           st = ffh->output_context->streams[pkt.stream_index];
 
           printf("before av_write_frame video encoded_size:%d\n"
@@ -3065,7 +3180,7 @@ p_ffmpeg_write_frame(t_ffmpeg_handle *ffh, GimpDrawable *drawable, gboolean forc
              ,st->pts.den
              ,st->pts.val
              );
-             
+
         }
         ret = av_write_frame(ffh->output_context, &pkt);
 
@@ -3130,7 +3245,7 @@ p_ffmpeg_write_audioframe(t_ffmpeg_handle *ffh, guchar *audio_buf, int frame_byt
 
 
 //      pkt.pts = ffh->ast[ii].aud_codec_context->coded_frame->pts;  // OLD
-      
+
       if (c->coded_frame->pts != AV_NOPTS_VALUE)
       {
         pkt.pts= av_rescale_q(c->coded_frame->pts, c->time_base, ffh->ast[ii].aud_stream->time_base);
@@ -3139,16 +3254,16 @@ p_ffmpeg_write_audioframe(t_ffmpeg_handle *ffh, guchar *audio_buf, int frame_byt
 //      if ((pkt.pts == 0) || (pkt.pts == AV_NOPTS_VALUE))
 //      {
 //        /* calculate pts timecode for the current frame
-//         * because the codec did not deliver a valid timecode 
+//         * because the codec did not deliver a valid timecode
 //         */
 //        pkt.pts = p_calculate_current_timecode(ffh);
 //      }
-      
-      
+
+
       pkt.stream_index = ffh->ast[ii].audio_stream_index;
       pkt.data = ffh->ast[ii].audio_buffer;
       pkt.size = encoded_size;
-      
+
       if(gap_debug)
       {
         printf("before av_write_frame audio  encoded_size:%d pkt.pts:%lld dts:%lld\n"
@@ -3157,7 +3272,7 @@ p_ffmpeg_write_audioframe(t_ffmpeg_handle *ffh, guchar *audio_buf, int frame_byt
           , pkt.dts
           );
       }
-      
+
       ret = av_write_frame(ffh->output_context, &pkt);
 
       if(gap_debug)
@@ -3175,13 +3290,13 @@ p_ffmpeg_write_audioframe(t_ffmpeg_handle *ffh, guchar *audio_buf, int frame_byt
  * my_url_fclose
  * ---------------
  * this procedure is a workaround that fixes a crash that happens on attempt to call the original
- * url_fclose procedure 
+ * url_fclose procedure
  * my private copy of (libavformat/aviobuf.c url_fclose)
  * just skips the crashing step "av_free(s);"  that frees up the ByteIOContext itself
  * (free(): invalid pointer: 0x0859f3b0)
  * The workaround is still required in ffmpeg snapshot from 2009.01.31
  */
-int 
+int
 my_url_fclose(ByteIOContext *s)
 {
     URLContext *h = s->opaque;
@@ -3312,7 +3427,7 @@ p_ffmpeg_close(t_ffmpeg_handle *ffh)
     }
 
   }
- 
+
   if(gap_debug)
   {
     printf("Closing AUDIO stuff\n");
@@ -3343,7 +3458,7 @@ p_ffmpeg_close(t_ffmpeg_handle *ffh)
         printf("before url_fclose\n");
       }
       my_url_fclose(&ffh->output_context->pb);
-      
+
       if(gap_debug)
       {
         printf("after url_fclose\n");
@@ -3355,6 +3470,11 @@ p_ffmpeg_close(t_ffmpeg_handle *ffh)
     }
   }
 
+  if(ffh->img_convert_ctx != NULL)
+  {
+    sws_freeContext(ffh->img_convert_ctx);
+    ffh->img_convert_ctx = NULL;
+  }
 
 }  /* end p_ffmpeg_close */
 
@@ -3543,7 +3663,7 @@ p_ffmpeg_encode_pass(GapGveFFMpegGlobalParams *gpp, gint32 current_pass, GapGveM
   l_cnt_encoded_frames = 0;
   l_cnt_reused_frames = 0;
   p_init_audio_workdata(awp);
-  
+
   l_check_flags = GAP_VID_CHCHK_FLAG_SIZE;
   l_vcodec_list = p_setup_check_flags(epp, &l_check_flags);
 
@@ -3594,7 +3714,7 @@ p_ffmpeg_encode_pass(GapGveFFMpegGlobalParams *gpp, gint32 current_pass, GapGveM
   }
 
   /* TODO check for overwrite (in case we are called non-interactive)
-   * overwrite check shall be done only if (current_pass < 2) 
+   * overwrite check shall be done only if (current_pass < 2)
    */
 
   if (gap_debug) printf("Creating ffmpeg file.\n");
@@ -3674,7 +3794,7 @@ p_ffmpeg_encode_pass(GapGveFFMpegGlobalParams *gpp, gint32 current_pass, GapGveM
     if(gap_debug)
     {
       printf("\nFFenc: after gap_story_render_fetch_composite_image_or_chunk image_id:%d layer_id:%d\n"
-        , (int)l_tmp_image_id 
+        , (int)l_tmp_image_id
         , (int) l_layer_id
         );
     }
@@ -3793,7 +3913,7 @@ p_ffmpeg_encode_pass(GapGveFFMpegGlobalParams *gpp, gint32 current_pass, GapGveM
     printf("1:1 copied    frames: %d\n", (int)l_cnt_reused_frames);
     printf("total handled frames: %d\n", (int)l_cnt_encoded_frames + l_cnt_reused_frames);
   }
-  
+
   return l_rc;
 }    /* end p_ffmpeg_encode_pass */
 
diff --git a/vid_enc_ffmpeg/gap_enc_ffmpeg_main.h b/vid_enc_ffmpeg/gap_enc_ffmpeg_main.h
index 777f5c0..3a872c5 100644
--- a/vid_enc_ffmpeg/gap_enc_ffmpeg_main.h
+++ b/vid_enc_ffmpeg/gap_enc_ffmpeg_main.h
@@ -38,6 +38,28 @@
 #include "avformat.h"
 #include "avcodec.h"
 
+/// start ffmpeg 0.5 / 0.6 support
+#if LIBAVCODEC_VERSION_MAJOR < 52
+#define GAP_USES_OLD_FFMPEG_0_5
+#endif
+#if LIBAVCODEC_VERSION_MAJOR == 52
+#if LIBAVCODEC_VERSION_MINOR <= 20
+#define GAP_USES_OLD_FFMPEG_0_5
+#endif
+#endif
+
+
+
+#ifdef GAP_USES_OLD_FFMPEG_0_5
+/* defines to use older ffmpeg-0.5 compatible types */
+#define AVMEDIA_TYPE_UNKNOWN  CODEC_TYPE_UNKNOWN
+#define AVMEDIA_TYPE_VIDEO    CODEC_TYPE_VIDEO
+#define AVMEDIA_TYPE_AUDIO    CODEC_TYPE_AUDIO
+#define AV_PKT_FLAG_KEY       PKT_FLAG_KEY
+#endif
+
+/// end ffmpeg 0.5 / 0.6 support
+
 
 #define GAP_HELP_ID_FFMPEG_PARAMS         "plug-in-gap-encpar-ffmpeg"
 #define GAP_PLUGIN_NAME_FFMPEG_PARAMS     "plug-in-gap-encpar-ffmpeg"
@@ -355,6 +377,20 @@ typedef struct {
   gdouble rc_max_available_vbv_use;
   gdouble rc_min_vbv_overflow_use;
 
+  gint32  color_primaries;        // enum AVColorPrimaries color_primaries;
+  gint32  color_trc;              // enum AVColorTransferCharacteristic color_trc;
+  gint32  colorspace;             // enum AVColorSpace colorspace;
+  gint32  color_range;            // enum AVColorRange color_range;
+  gint32  chroma_sample_location; // enum AVChromaLocation chroma_sample_location;
+  gint32  weighted_p_pred;   // int weighted_p_pred;
+  gint32  aq_mode;           // int aq_mode;
+  gdouble aq_strength;       // float aq_strength;
+  gdouble psy_rd;            // float psy_rd;
+  gdouble psy_trellis;       // float psy_trellis;
+  gint32  rc_lookahead;      // int rc_lookahead;
+
+
+
   gint32 codec_FLAG_GMC;
   gint32 codec_FLAG_INPUT_PRESERVED;
   gint32 codec_FLAG_GRAY;
@@ -377,6 +413,10 @@ typedef struct {
   gint32 codec_FLAG2_CHUNKS;
   gint32 codec_FLAG2_NON_LINEAR_QUANT;
   gint32 codec_FLAG2_BIT_RESERVOIR;
+  gint32 codec_FLAG2_MBTREE;
+  gint32 codec_FLAG2_PSY;
+  gint32 codec_FLAG2_SSIM;
+
 
 } GapGveFFMpegValues;
 
diff --git a/vid_enc_ffmpeg/gap_enc_ffmpeg_par.c b/vid_enc_ffmpeg/gap_enc_ffmpeg_par.c
index 2d38d3e..8855834 100644
--- a/vid_enc_ffmpeg/gap_enc_ffmpeg_par.c
+++ b/vid_enc_ffmpeg/gap_enc_ffmpeg_par.c
@@ -207,6 +207,18 @@ p_set_master_keywords(GapValKeyList *keylist, GapGveFFMpegValues *epp)
    gap_val_set_keyword(keylist, "(rc_max_available_vbv_use ",  &epp->rc_max_available_vbv_use,   GAP_VAL_GDOUBLE, 0, "# Ratecontrol attempt to use, at maximum, <value> of what can be used without an underflow");
    gap_val_set_keyword(keylist, "(rc_min_vbv_overflow_use ",   &epp->rc_min_vbv_overflow_use,    GAP_VAL_GDOUBLE, 0, "# Ratecontrol attempt to use, at least, <value> times the amount needed to prevent a vbv overflow");
 
+   /* new params (introduced with ffmpeg 0.6) */
+   gap_val_set_keyword(keylist, "(color_primaries ",          &epp->color_primaries,          GAP_VAL_GINT32, 0, "# Chromaticity coordinates of the source primaries.");
+   gap_val_set_keyword(keylist, "(color_trc ",                &epp->color_trc,                GAP_VAL_GINT32, 0, "# Color Transfer Characteristic.");
+   gap_val_set_keyword(keylist, "(colorspace ",               &epp->colorspace,               GAP_VAL_GINT32, 0, "# YUV colorspace type.");
+   gap_val_set_keyword(keylist, "(color_range ",              &epp->color_range,              GAP_VAL_GINT32, 0, "# MPEG vs JPEG YUV range.");
+   gap_val_set_keyword(keylist, "(chroma_sample_location ",   &epp->chroma_sample_location,   GAP_VAL_GINT32, 0, "# This defines the location of chroma samples.");
+   gap_val_set_keyword(keylist, "(weighted_p_pred ",          &epp->weighted_p_pred,          GAP_VAL_GINT32, 0, "# explicit P-frame weighted prediction analysis method 0:off, 1: fast blind weighting, 2:smart weighting (full fade detection analysis)");
+   gap_val_set_keyword(keylist, "(aq_mode ",                  &epp->aq_mode,                  GAP_VAL_GINT32, 0, "# AQ mode");
+   gap_val_set_keyword(keylist, "(aq_strength ",              &epp->aq_strength,              GAP_VAL_GDOUBLE, 0, "# AQ strength, Reduces blocking and blurring in flat and textured areas.");
+   gap_val_set_keyword(keylist, "(psy_rd ",                   &epp->psy_rd,                   GAP_VAL_GDOUBLE, 0, "# PSY RD Strength of psychovisual optimization ");
+   gap_val_set_keyword(keylist, "(psy_trellis ",              &epp->psy_trellis,              GAP_VAL_GDOUBLE, 0, "# PSY trellis Strength of psychovisual optimization");
+   gap_val_set_keyword(keylist, "(rc_lookahead ",             &epp->rc_lookahead,             GAP_VAL_GINT32, 0,  "# Number of frames for frametype and ratecontrol lookahead");
 
    /* codec flags */
 
@@ -254,6 +266,13 @@ p_set_master_keywords(GapValKeyList *keylist, GapGveFFMpegValues *epp)
    p_set_keyword_bool32(keylist, "(use_non_linear_quant ",    &epp->codec_FLAG2_NON_LINEAR_QUANT,       "# CODEC_FLAG2_NON_LINEAR_QUANT    Use MPEG-2 nonlinear quantizer");
    p_set_keyword_bool32(keylist, "(use_bit_reservoir ",       &epp->codec_FLAG2_BIT_RESERVOIR,          "# CODEC_FLAG2_BIT_RESERVOIR       Use a bit reservoir when encoding if possible");
 
+   /* codec flags new in ffmpeg-0.6 */
+
+   p_set_keyword_bool32(keylist, "(use_bit_reservoir ",       &epp->codec_FLAG2_BIT_RESERVOIR,          "# CODEC_FLAG2_BIT_RESERVOIR       Use a bit reservoir when encoding if possible");
+   p_set_keyword_bool32(keylist, "(use_bit_mbtree ",          &epp->codec_FLAG2_MBTREE,                 "# CODEC_FLAG2_MBTREE              Use macroblock tree ratecontrol (x264 only)");
+   p_set_keyword_bool32(keylist, "(use_bit_psy ",             &epp->codec_FLAG2_PSY,                    "# CODEC_FLAG2_PSY                 Use psycho visual optimizations.");
+   p_set_keyword_bool32(keylist, "(use_bit_ssim ",            &epp->codec_FLAG2_SSIM,                   "# CODEC_FLAG2_SSIM                Compute SSIM during encoding, error[] values are undefined");
+
 
 }  /* end p_set_master_keywords */
 



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