[gimp-gap] update to ffmpeg-0.7.11, bugfixes, new fetures detail_tracking, blend_fill



commit b5804921457ec3bba57f05f4d4592ff77e71960c
Author: Wolfgang Hofer <wolfgangh svn gnome org>
Date:   Sun Jan 29 10:35:33 2012 +0100

    update to ffmpeg-0.7.11, bugfixes, new fetures detail_tracking, blend_fill

 ChangeLog                                      |  166 ++
 NEWS                                           |   11 +-
 configure.in                                   |   78 +-
 docs/reference/txt/gap_gimprc_params.txt       |   27 +
 extern_libs/README_extern_libs                 |   19 +-
 extern_libs/configure_options_ffmpeg.txt       |    2 +-
 extern_libs/configure_options_ffmpeg_win32.txt |    2 +-
 extern_libs/ffmpeg.tar.gz                      |  Bin 4471231 -> 5415900 bytes
 gap/Makefile.am                                |   28 +
 gap/gap_blend_fill_main.c                      | 1988 ++++++++++++++++++++++++
 gap/gap_colordiff.c                            |  122 ++
 gap/gap_colordiff.h                            |   33 +
 gap/gap_detail_align_exec.c                    |  821 ++++++++++
 gap/gap_detail_align_exec.h                    |   74 +
 gap/gap_detail_tracking_exec.c                 | 1530 ++++++++++++++++++
 gap/gap_detail_tracking_exec.h                 |  119 ++
 gap/gap_detail_tracking_main.c                 |  719 +++++++++
 gap/gap_edge_detection.c                       |  383 +++++
 gap/gap_edge_detection.h                       |   17 +-
 gap/gap_image.c                                |   67 +-
 gap/gap_image.h                                |    2 +
 gap/gap_locate.c                               |   33 +-
 gap/gap_locate2.c                              |  604 +++++++
 gap/gap_locate2.h                              |   96 ++
 gap/gap_mov_dialog.h                           |    5 +-
 gap/gap_mov_exec.c                             |   37 +-
 gap/gap_mov_exec.h                             |    1 +
 gap/gap_mov_render.c                           |    6 +-
 gap/gap_mov_xml_par.c                          |    8 +
 gap/gap_pixelrgn.c                             |  151 ++
 gap/gap_pixelrgn.h                             |   36 +
 gap/gap_player_dialog.c                        |  258 +++-
 gap/gap_player_main.c                          |    4 +
 gap/gap_player_main.h                          |    9 +-
 libgapbase/gap_base.c                          |   68 +-
 libgapbase/gap_base.h                          |   26 +-
 libgapvidapi/gap_vid_api_ffmpeg.c              |  135 ++-
 po/POTFILES.in                                 |    4 +
 vid_enc_ffmpeg/gap_enc_ffmpeg_gui.c            |   18 +
 vid_enc_ffmpeg/gap_enc_ffmpeg_main.c           |   51 +-
 vid_enc_ffmpeg/gap_enc_ffmpeg_main.h           |   17 +-
 vid_enc_ffmpeg/gap_enc_ffmpeg_par.c            |   13 +
 42 files changed, 7711 insertions(+), 77 deletions(-)
---
diff --git a/ChangeLog b/ChangeLog
old mode 100755
new mode 100644
index d7c55d9..9d0e206
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,168 @@
+2012-01-29 Wolfgang Hofer <hof gimp org>
+
+- fixed a bug in the GAP video API that caused a crash when decoding audio.
+  This bug occured when GIMP-GAP was built with ffmpeg confiure options that enabled MMX
+  and ac3 or aac encoded audio was extracted from videofiles.
+  The fix uses an additional aligned buffer (required for proper operation of MMX based
+  audio format conversions)
+  Note that the current implementation of this fix eliminates the performance advantage 
+  of MMX for the audio conversion, but enabled MMX is still preferable for video processing.
+  
+  The fix is relevant for both ffmpeg-0.6.1 and ffmpeg-0.7.11
+
+- update to ffmpeg-0.7.11 "Peace" that was released on 2012-01-12
+  removed confiure "--enable-libfaad" (that is no longer supported by ffmpeg-0.7.11.)
+
+  The gimp-gap configure.in script now checks for the yasm assembler
+  and automatically adds --disable-yasm configure option when configuring
+  the ffmpeg libraries build.
+  It displays a warning that installation of yasm is recommanded
+  for the ffmpeg build in that case.
+
+
+  The ffmpeg configure call now uses the options:
+   --disable-ffmpeg  --disable-ffplay  --disable-ffprobe  --disable-ffserver 
+  because those binaries are not required to build GIMP-GAP
+  and may cause fail of the build in case different librariy versions
+  are available on different locations.
+
+      The ffmpeg build of those binaries failed in my environment with
+          older libx264 installed at /usr/lib 
+          and newer version at /usr/local/lib
+      but built successful when building just the static libs without the ffmpeg binaries.
+
+  - compile support for older ffmpeg 0.6 is still there.
+     replace extern_libs/ffmpeg.tar.gz with an older 0.6, or 0.6.1 tarball
+     and remove the extern_libs/ffmpeg directory before
+     running the autoconf.sh script to build gimp-gap with the older versions)
+
+  - #define GAP_USES_OLD_FFMPEG_0_6  when compiling against 0.6 or 0.6.1
+   
+  - support new encoder flags: (via preset parameterfile and GUI)
+     epp->codec_FLAG2_INTRA_REFRESH        = 0; /* 0: FALSE */
+
+  - new encoder codec parameters (supported via preset parameterfile):
+    gdouble crf_max;               // float crf_max;
+    gint32  log_level_offset;      // int log_level_offset;
+    gint32  slices;                // int slices;
+    gint32  thread_type;           // int thread_type;
+    gint32  active_thread_type;    //  int active_thread_type;
+    gint32  thread_safe_callbacks; // int thread_safe_callbacks;
+    gint64  vbv_delay;             // uint64_t vbv_delay;
+    gint32  audio_service_type;    // enum AVAudioServiceType  audio_service_type;
+  
+- new plug-in to fill selected (small) areas by blending surrounding
+  colors to cover pixel errors. Intended to fix videos
+  shot with (my) camera that has some defect sensor pixels.
+
+- new plug-in to for tracking a detail over a sequence of video frames.
+  This plug-in is typically triggered by the Player and records the movement
+  as XML file usable with the MovePath plug-in.
+  Intended to compensate unwanted camera movements.
+  
+  implemented alternative locate algorithm that
+  uses gimp regions more efficient than the old one.
+  (runs at least 10 times faster)
+  
+  per default all calls of the old procedure
+  are redirected to use the newer implementation.
+  With the gimprc parameter "
+  (gap-locate-details-use-old-algorithm "yes")
+  the old implementation can be enabled
+  -- for more test and compare purpose on further development cycles--) 
+
+- new plug-in inspired by the "exact aligner" script.
+
+  This plug-in can operate on XML files (recorded with the detail tracking feature)
+  to align multiple frames when called as filter with the frames modify feature.
+  
+
+- the module gap_pixelrgn implements the procedure 
+  gap_gimp_pixel_rgns_unref to enable clean escape
+  from loops based on gimp_pixel_rgns_register, gimp_pixel_rgns_process.
+
+  * Usage CODE example:
+  *
+  *  for (pr = gimp_pixel_rgns_register (2, &PR1, &PR2);
+  *      pr != NULL;
+  *      pr = gimp_pixel_rgns_process (pr))
+  *  {
+  *      ... evaluate pixel data in pixel regions PR1, PR2
+  *      ...
+  *      if (further evaluation of the remaining tiles is not necessary)
+  *      {
+  *        // escaping from the loop without the call
+  *        // to gap_gimp_pixel_rgns_unref
+  *        // leads to memory leaks because of unbalanced tile ref/unref calls.
+  *        break;
+  *      }
+  *  }
+  *  if (pr != NULL)
+  *  {
+  *    gap_gimp_pixel_rgns_unref(pr);
+  *  }
+  
+  NOTE:  A procedure like gap_gimp_pixel_rgns_unref
+  that deals with libgimp internal private structures
+  should better be part of libgimp.
+  (and should be generally available for plug-in development
+  in future gimp releases.)
+  BUT: The GIMP-GAP implementation would still be required for backwards compatibilty
+  with GIMP-2.6.x releases.
+  
+- fixed wrong dialog title in the MovePath single frame apply feature
+
+- MovePath rotation Threshold
+  The old move path implementation ignores rotate values between -0.5 and + 0.5 degree.
+  The threshold is intended for better render performance by skipping
+  rotate transformations when angle is nearly 0.
+  The hardcode value 0.5 is too high (especially for larger frame sizes introduced with HD video)
+  
+  The new implementation allows to configure this threshold 
+  in the range 0.0 upto 1.0 degree.
+  The new default threshold is configurable via gimprc parameter. "video-move-path-rotate-threshold"
+  and now the default is set to 0.015 degree that shall result in smoother rotations.
+  individual rotateThreshold configuration is supported via MovePath XML file.
+  The rotate_threshold value is not exposed in the Dialog.
+
+
+ * NEWS
+ * configure.in  
+ * po/POTFILES.in
+ * libgapbase/gap_base.c [.h]
+ * docs/reference/txt/gap_gimprc_params.txt  
+
+ * gap/Makefile.am
+ * gap/gap_mov_dialog.h
+ * gap/gap_mov_exec.c  [.h]
+ * gap/gap_mov_render.c
+ * gap/gap_mov_xml_par.c
+
+ * gap/gap_image.c [.h]
+ * gap/gap_player_main.c [.h]
+ * gap/gap_player_dialog.c 
+ * gap/gap_locate.c 
+ * gap/gap_colordiff.c [.h]
+ * gap/gap_edge_detection.c [.h]
+
+ * gap/gap_blend_fill_main.c             # new file
+ * gap/gap_detail_align_exec.c [.h]      # new files
+ * gap/gap_detail_tracking_exec.c [.h]   # new files
+ * gap/gap_detail_tracking_main.c        # new file
+ * gap/gap_locate2.c [.h]                # new files
+ * gap/gap_pixelrgn.c [.h]               # new files
+
+ * extern_libs/ffmpeg.tar.gz                # updated to ffmpeg release 0.7.11
+ * extern_libs/README_extern_libs  
+ * extern_libs/configure_options_ffmpeg_win32.txt
+ * extern_libs/configure_options_ffmpeg.txt
+
+ * vid_enc_ffmpeg/gap_enc_ffmpeg_gui.c
+ * vid_enc_ffmpeg/gap_enc_ffmpeg_main.c [.h]
+ * vid_enc_ffmpeg/gap_enc_ffmpeg_par.c
+ * libgapvidapi/gap_vid_api_ffmpeg.c
+
+
 2011-11-30 Wolfgang Hofer <hof gimp org>
 
 - added option to merge visible layers
@@ -50,6 +215,7 @@
 
 2011-11-23 Wolfgang Hofer <hof gimp org>
 
+ * gap/Makefile.am
 - Foreground selection: fixed parameters for non interactive calls.
 
 - new feature: foreground extraction from current selection.
diff --git a/NEWS b/NEWS
index ca7dfb3..bbe5bc4 100644
--- a/NEWS
+++ b/NEWS
@@ -29,6 +29,15 @@ Here is a short overview whats new in GIMP-GAP-2.7.0:
     in a "process one frame per call" style, intended to be called as filter
     with the modify frames feature.
 
+  - new plug-in to fill selected (small) areas by blending surrounding
+    colors to cover pixel errors. Intended to fix images and videos
+    shot with a camera that has some defect sensor pixels or dirty lens.
+
+  - new plug-in for tracking a significant detail in frames.
+    This plug-in is integrated in the Playback and can
+    record movents of the detail as XML parameters for the MovePath feature.
+    The recorded movements are useful to compensate unwanted shaking
+    of videos recorder without a stativ.
  
 - GIMP-GAP now supports speed control of movements and other transitions
   via Acceleration characteristic presets. Those presets are available
@@ -71,7 +80,7 @@ Here is a short overview whats new in GIMP-GAP-2.7.0:
 
 
 - updated gimp-gap video API and ffmpeg-based video encoder
-  to support the libraries provided with the ffmpeg-0.6.1 release
+  to support the libraries provided with the ffmpeg-0.7.11 release
   This includes various bugfixes related to video de/encode 
   (but breaks backwards compatibility when seeking positions by frame number
   in videofiles that do not start with a keyframe).
diff --git a/configure.in b/configure.in
index 9a0a681..8b70ac6 100644
--- a/configure.in
+++ b/configure.in
@@ -201,6 +201,15 @@ AC_ARG_ENABLE(ff_libbz2,
 
 
 
+dnl check for the yasm assembler program (recommanded for build of ffmpeg libraries avcodec, avformat,...)
+dnl note that ffmpeg-0.7.11 can be built without yasm when explicte configured with --disable-yasm
+dnl but at least display a warning in this case.
+yasm_warn=""
+AC_CHECK_PROG(HAVE_YASM_ASSEMBLER, yasm, yes, no)
+if test "x$HAVE_YASM_ASSEMBLER" = "xno"; then
+   yasm_warn=" the yasm assembler, recommanded to build libavformat, libavcodec, was not found. "
+fi
+
 
 
 dnl ## libmp3lame lame/lame.h lame_init -lmp3lame -lm
@@ -240,27 +249,31 @@ dnl check for faad library (useful for better ffmpeg audio support)
 dnl the check result does not matter unless libavformat is linked or built later on.
 FF_LIBFAAD=""
 faad_warn=""
-AC_ARG_ENABLE(ff_libfaad,
-              [  --disable-ff-libfaad          configure libavformat without libfaad])
-  if test "x$enable_ff_libfaad" != "xno"; then
-    AC_CHECK_LIB(faad, NeAACDecOpen,
-        [AC_CHECK_HEADER(faad.h,
-          FF_LIBFAAD="-lfaad",
-          faad_warn="$NEW_LINE ** faad header file (faad.h) not found (not critical)")],
-      faad_warn="faad library (libfaad) not found (not critical)")    
-
-    dnl additional check for faad1 library (that is also sufficient for building ffmpeg 0.5)
-
-    if test "x$FF_LIBFAAD" = "x"; then
-      AC_CHECK_LIB(faad, faacDecOpen,
-        [AC_CHECK_HEADER(faad.h,
-          FF_LIBFAAD="-lfaad",
-          faad_warn="$NEW_LINE ** faad header file (faad.h) not found (not critical)")],
-        faad_warn="faad library (libfaad) not found (not critical)")    
-    fi
 
-  fi
-dnl
+dnl ffmpeg-0.7.8 has dropped support for faad library
+dnl TODO remove faad specific stuff after succesful integration of ffmpeg-0.7.8
+
+dnl AC_ARG_ENABLE(ff_libfaad,
+dnl               [  --disable-ff-libfaad          configure libavformat without libfaad])
+dnl   if test "x$enable_ff_libfaad" != "xno"; then
+dnl     AC_CHECK_LIB(faad, NeAACDecOpen,
+dnl         [AC_CHECK_HEADER(faad.h,
+dnl           FF_LIBFAAD="-lfaad",
+dnl           faad_warn="$NEW_LINE ** faad header file (faad.h) not found (not critical)")],
+dnl       faad_warn="faad library (libfaad) not found (not critical)")    
+dnl 
+dnl     dnl additional check for faad1 library (that is also sufficient for building ffmpeg 0.5)
+dnl 
+dnl     if test "x$FF_LIBFAAD" = "x"; then
+dnl       AC_CHECK_LIB(faad, faacDecOpen,
+dnl         [AC_CHECK_HEADER(faad.h,
+dnl           FF_LIBFAAD="-lfaad",
+dnl           faad_warn="$NEW_LINE ** faad header file (faad.h) not found (not critical)")],
+dnl         faad_warn="faad library (libfaad) not found (not critical)")    
+dnl     fi
+dnl 
+dnl   fi
+dnl dnl
 
 
 
@@ -270,16 +283,22 @@ dnl ### set X264_REQUIRED_VERSION="0.65" to support ffmpeg-0.5 (condition "X264_
 dnl ### set X264_REQUIRED_VERSION="0.85" to support ffmpeg-0.6 (broken mjpeg decoder)
 dnl ### set X264_REQUIRED_VERSION="0.98" to support ffmpeg-2010-09-14 snapshot (for testing)
 dnl ### new libx264 versions (X264_BUILD >= 93)  require to include stdint.h (or inttypes.h)
-dnl ### before including tthe headerfile x264.h 
+dnl ### before including the headerfile x264.h 
 dnl ### therfore we provide this requirement as 4. parameter of the AC_CHECK_HEADER macro.
 dnl ### but AC_CHECK_LIB now fails when checking for procedure x264_encoder_open
 dnl ### because x264_encoder_open is a define constant that refers to x264_encoder_open_<X264_BUILD>
-dnl ### therefore therefore the check was changed to procedure x264_encoder_close
+dnl ### therefore the check was changed to procedure x264_encoder_close
 dnl ### (dont want to AC_CHECK_LIB that works only for one hardcoded version.)
+dnl ### set X264_REQUIRED_VERSION="0.120" to support ffmpeg-0.7.8
+dnl ###   note that ffmpeg configure script says that required libx264 version is >= 0.115
+dnl ###   but with libx264 0.115 version installed on my development system
+dnl ###   the ffmpeg-0.7.8 configure script complained:
+dnl ###   ERROR: libx264 version must be >= 0.115.
+dnl ###   After this test i updated to X264 0.120 and ffmpeg-0.7.8 configure worked fine without complaining...
 dnl the check result does not matter unless libavformat is linked or built later on.
 
 
-X264_REQUIRED_VERSION="0.85"
+X264_REQUIRED_VERSION="0.120"
 X264_REQUIRED_INC='#ifdef HAVE_STDINT_H
 #include <stdint.h>
 #endif'
@@ -299,10 +318,11 @@ dnl  fi
   if test "x$enable_ff_libx264" != "xno"; then
     if ! $PKG_CONFIG --atleast-version=$X264_REQUIRED_VERSION x264; then
         x264_warn="$NEW_LINE x264 library (libx264) required version $X264_REQUIRED_VERSION not found (not critical, but no open H264 video codec support)"
+        FF_LIBX264=""
     else
       AC_CHECK_LIB(x264, x264_encoder_close,
         [AC_CHECK_HEADER(x264.h,
-          FF_LIBX264="-lx264",
+          FF_LIBX264=`$PKG_CONFIG --libs x264`,
           x264_warn="$NEW_LINE x264 header file (x264.h) not found (not critical, but no open H264 video codec support)",
           $X264_REQUIRED_INC)],
         x264_warn="$NEW_LINE x264 library (libx264) not found (not critical, but no open H264 video codec support)")
@@ -548,6 +568,14 @@ INFORMATION: old ffmpeg was moved to $FFMPEG_DIR-OLD
  
       FFMPEG_ENABLE_NONFREE=""
 
+
+      dnl ffmpeg-0.7.11 recommands build with yasm assembler, but the
+      dnl configure script fails when yasm is not installed and yasm is not diasbled by explicite configure option.
+      if test "x$HAVE_YASM_ASSEMBLER" = "xno"; then
+      	 FFMPEG_CONFIGURE_OPTIONS="$FFMPEG_CONFIGURE_OPTIONS --disable-yasm"
+      	 yasm_warn="$yasm_warn  ffmpeg congigure option --disable-yasm was used to allow a crippled build. (Installation of yasm is RECOMMANDED)"
+      fi
+
       dnl configure ffmpeg bzip2 usage
       if test "x$FF_BZIP2" != "x"; then
       	 FFMPEG_CONFIGURE_OPTIONS="$FFMPEG_CONFIGURE_OPTIONS --enable-bzlib"
@@ -1187,4 +1215,4 @@ docs/reference/Makefile
 docs/reference/txt/Makefile
 ])
 
-AC_MSG_RESULT($frontends_warning $audio_warning $videoapi_warning $moved_old_ffmpeg_warn $moved_old_libmpeg3_warn $vid_ffmpeg_warning $vid_mpeg3_warning $vid_quicktime_warning $vid_xvidcore_warning)
+AC_MSG_RESULT($frontends_warning $audio_warning $videoapi_warning $moved_old_ffmpeg_warn $moved_old_libmpeg3_warn $vid_ffmpeg_warning $yasm_warn $vid_mpeg3_warning $vid_quicktime_warning $vid_xvidcore_warning)
diff --git a/docs/reference/txt/gap_gimprc_params.txt b/docs/reference/txt/gap_gimprc_params.txt
index 5eaebb4..41d0e62 100644
--- a/docs/reference/txt/gap_gimprc_params.txt
+++ b/docs/reference/txt/gap_gimprc_params.txt
@@ -51,6 +51,14 @@ If you edit gimprc files by hand, you must do this before you startup GIMP.
 # the cache size can be set in kilobytes (K) or megaytes (M)
 (video_playback_cache "100M")
 
+# the gap player supports caching of gimp tiles
+# note that frame playback does NOT use gimp_tiles (see video_playback_cache)
+# but caching of gimp tiles is relevant for other tile based processing features
+# (especially detail tracking that accesses the same region of gimp tiles in many loop iterations)
+# the cache size is specified in number of tiles.
+(video_player_cache_ntiles 200)
+
+
 # the gap player has several widgets to set the
 # position (e.g. the currently displayed framenumber)
 #
@@ -359,3 +367,22 @@ If you edit gimprc files by hand, you must do this before you startup GIMP.
 # number of video frames)
 # default value is 36
 (gap_ffetch_gva_frames_to_keep_cached "36")
+
+# gimp-gap has implemented 2 algorithms to locate small detail
+# of a layer in a corresponding layer.
+# the older implementation is slower but may may be activated
+# with this parameter for test purpose
+# default is no
+(gap-locate-details-use-old-algorithm "no")
+
+
+# Move Path debug feature to trigger logging current parameters
+# while rendering a frame to stdout.
+# default is no
+(video-move-path-log-render-params "no")
+
+# Move Path rotation threshold is a float number between 0.0 and 1.0
+# This threshold vaule allows Move path render engine to skip the time consuming
+# rotate transformations on very small angles.
+# default is 0.0125 degree
+(video-move-path-rotate-threshold "0.0125")
diff --git a/extern_libs/README_extern_libs b/extern_libs/README_extern_libs
index a590660..a7be7be 100755
--- 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.6.1
+- ffmpeg 0.7.11
 
 CURRENT LIBMPEG3 version is:
        
@@ -45,9 +45,16 @@ this GIMP-GAP distribution.
   will work with a new ffmpeg version.
   Typically it requires much testing and some knowledge
   of both GIMP-GAP and ffmpeg internals to get new ffmpeg versions
-  working. 
+  working.
+  
   
   GIMP-GAP currently supports 
+  o) ffmpeg-0.7.11  basically works, tests done 
+       minor incopatibility detected at frame exact positioning compared against ffmpeg-0.6.1
+       on dvd mpeg2 encoded videos that do not start with a keyframe.
+       0.7.11 delivers one extra gray frame as 1st and a black frame as 2nd frame
+       where 0.6.1 did deliver just one black frame as 1st frame. 
+  o) ffmpeg-0.7.8 Not fully tested....
   o) ffmpeg-0.6.1 tests repeated, same results as ffmpeg-0.6
   o) ffmpeg-0.6 basically works, tests done 
      incopatibility detected at frame exact positioning compared against ffmpeg-0.5 
@@ -60,11 +67,9 @@ this GIMP-GAP distribution.
      when compiling/linking with ffmpeg-0.5)
      
 
-
-  newer ffmpeg GIT (or SVN) snapshots
-  may or may not compile, link and run with this GIMP-GAP release.
-  (a compile/link test was done with ffmpeg-2010-09-14 snapshot
-  that has no more support for libfaad --disable-ff-libfaad)
+  the ffmpeg releases 0.8.x, 0.9.x and latest GIT repository
+  are NOT compatible due to new API versions and will NOT compile with
+  this GIMP-GAP version.
 
 
 GIMP-GAP can be configured to be compiled without ffmpeg (in this case 
diff --git a/extern_libs/configure_options_ffmpeg.txt b/extern_libs/configure_options_ffmpeg.txt
index 6fa8162..d558146 100644
--- a/extern_libs/configure_options_ffmpeg.txt
+++ b/extern_libs/configure_options_ffmpeg.txt
@@ -1,4 +1,4 @@
---disable-shared --enable-static --enable-gpl --enable-mmx --enable-mmx2 --disable-vaapi
+ --disable-ffmpeg  --disable-ffplay  --disable-ffprobe  --disable-ffserver --disable-shared --enable-static --enable-gpl --enable-mmx --enable-mmx2 --disable-vaapi
 #
 # This file provides configuration options for ffmpeg
 # and is included while GIMP-GAP configure script runs the ffmpeg configuration.
diff --git a/extern_libs/configure_options_ffmpeg_win32.txt b/extern_libs/configure_options_ffmpeg_win32.txt
index 1287434..96a69b6 100644
--- a/extern_libs/configure_options_ffmpeg_win32.txt
+++ b/extern_libs/configure_options_ffmpeg_win32.txt
@@ -1,4 +1,4 @@
---disable-shared --enable-static --enable-gpl --enable-mmx --enable-mmx2 --disable-vaapi  --disable-devices --disable-pthreads  --disable-altivec  --disable-sse --disable-neon
+ --disable-ffmpeg  --disable-ffplay  --disable-ffprobe  --disable-ffserver --disable-shared --enable-static --enable-gpl --enable-mmx --enable-mmx2 --disable-vaapi  --disable-devices --disable-pthreads  --disable-altivec  --disable-sse --disable-neon
 #
 # This file provides configuration options for ffmpeg
 # and is included while GIMP-GAP configure scriptruns the ffmpeg configuration on Windows environment.
diff --git a/extern_libs/ffmpeg.tar.gz b/extern_libs/ffmpeg.tar.gz
index a7d7914..7a3cb42 100644
Binary files a/extern_libs/ffmpeg.tar.gz and b/extern_libs/ffmpeg.tar.gz differ
diff --git a/gap/Makefile.am b/gap/Makefile.am
index acb99ee..1173ccc 100644
--- a/gap/Makefile.am
+++ b/gap/Makefile.am
@@ -59,8 +59,12 @@ BASE_SOURCES = \
 	gap_lib.c		\
 	gap_lib.h		\
 	gap_lib_common_defs.h	\
+	gap_detail_tracking_exec.c	\
+	gap_detail_tracking_exec.h 	\
 	gap_locate.c		\
 	gap_locate.h		\
+	gap_locate2.c		\
+	gap_locate2.h		\
 	gap_lock.c		\
 	gap_lock.h		\
 	gap_navi_activtable.c	\
@@ -69,6 +73,8 @@ BASE_SOURCES = \
 	gap_match.h		\
 	gap_onion_base.c	\
 	gap_onion_base.h	\
+	gap_pixelrgn.c		\
+	gap_pixelrgn.h		\
 	gap_pdb_calls.c		\
 	gap_pdb_calls.h		\
 	gap_pview_da.c		\
@@ -120,8 +126,10 @@ libgapstory_a_SOURCES = $(BASE_SOURCES)	$(MOVEPATH_SOURCES) \
 	gap_story_syntax.c
 
 libexec_PROGRAMS = \
+	gap_blend_fill		\
 	gap_bluebox		\
 	gap_colormask		\
+	gap_detail_tracking	\
 	gap_plugins		\
 	gap_movepath		\
 	gap_filter		\
@@ -148,6 +156,13 @@ libexec_PROGRAMS = \
 	gap_wr_resynth		\
 	gap_wr_opacity
 
+
+gap_blend_fill_SOURCES = \
+	gap_lastvaldesc.c	\
+	gap_lastvaldesc.h	\
+	gap_blend_fill_main.c   \
+	gap_libgimpgap.h	
+
 gap_bluebox_SOURCES = \
 	gap_lastvaldesc.c	\
 	gap_lastvaldesc.h	\
@@ -164,6 +179,15 @@ gap_colormask_SOURCES = \
 	gap_colormask_main.c	\
 	gap_libgimpgap.h	
 
+
+gap_detail_tracking_SOURCES = \
+	gap_lastvaldesc.c	\
+	gap_lastvaldesc.h	\
+	gap_detail_tracking_main.c	\
+	gap_detail_align_exec.c	\
+	gap_detail_align_exec.h	\
+	gap_libgimpgap.h	
+
 gap_plugins_SOURCES = \
 	gap_base_ops.c		\
 	gap_base_ops.h		\
@@ -415,6 +439,7 @@ gap_water_pattern_SOURCES = \
 	gap_water_pattern.c	\
 	gap_libgimpgap.h
 
+
 gap_wr_opacity_SOURCES = \
 	gap_lastvaldesc.c	\
 	gap_lastvaldesc.h	\
@@ -475,7 +500,9 @@ LDADD = $(GIMP_LIBS)
 gap_plugins_LDADD =          $(LIBGIMPGAP)  $(LIBGAPBASE) $(GIMP_LIBS) -lm
 gap_movepath_LDADD =         $(LIBGIMPGAP)  $(LIBGAPBASE) $(GIMP_LIBS)
 gap_bluebox_LDADD =          $(LIBGIMPGAP)  $(LIBGAPBASE) $(GIMP_LIBS)
+gap_blend_fill_LDADD =       $(LIBGIMPGAP)  $(LIBGAPBASE) $(GIMP_LIBS)
 gap_colormask_LDADD =        $(LIBGIMPGAP)  $(LIBGAPBASE) $(GIMP_LIBS)
+gap_detail_tracking_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)
@@ -500,6 +527,7 @@ 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 = \
 	README							\
 	README_developers					\
diff --git a/gap/gap_blend_fill_main.c b/gap/gap_blend_fill_main.c
new file mode 100644
index 0000000..c7b83d4
--- /dev/null
+++ b/gap/gap_blend_fill_main.c
@@ -0,0 +1,1988 @@
+/*  gap_blend_fill_main.c
+ *    This filter fills the selected area via blending opposite border colors
+ *    outside the selction into the selected area.
+ *    It was implemented for fixing small pixel defects of my video camera sensor
+ *    and is intended to be used as filter when processing video frames
+ *    that are shot by such faulty cameras and typically runs
+ *    as filtermacro. Therefore the selection can be provided via an external image
+ *    or as path vectors via an extrernal SVG file.
+ *
+ *  2011/11/22
+ */
+/* 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
+ *  (2011/11/22)  2.7.0       hof: created
+ */
+int gap_debug = 0;  /* 1 == print debug infos , 0 dont print debug infos */
+#define GAP_DEBUG_DECLARED 1
+
+#define PLUG_IN_NAME        "gap-blend-fill"
+#define PLUG_IN_BINARY      "gap_blend_fill"
+#define PLUG_IN_PRINT_NAME  "Blend Fill"
+#define PLUG_IN_IMAGE_TYPES "RGB*"
+#define PLUG_IN_AUTHOR      "Wolfgang Hofer (hof gimp org)"
+#define PLUG_IN_COPYRIGHT   "Wolfgang Hofer"
+#define PLUG_IN_HELP_ID     "gap-plug-in-blend-fill"
+
+
+#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_libgapbase.h"
+#include "gimplastvaldesc.h"
+#include "gap_image.h"
+#include "gap_arr_dialog.h"
+
+#include "gap-intl.h"
+
+#define SELECTION_FROM_VECTORS -2
+#define SELECTION_FROM_SVG_FILE -3
+
+#define ALPHA_NOT_SELECTED              0     /* marks pixel outside the selection */
+#define ALPHA_SELECTED_COLOR_UNDEFINED  128   /* marks pixel inside selection, color not (yet) calculated */
+#define ALPHA_SELECTED_COLOR_VALID      255   /* marks pixel inside selection, color already calculated */
+
+#define RED_IDX   0          /* index of red   channel in RGBA model */
+#define BLU_IDX   1          /* index of green channel in RGBA model */
+#define GRN_IDX   2          /* index of blue  channel in RGBA model */
+#define ALPHA_IDX 3          /* index of aplha channel in RGBA model */
+
+#define SCALE_MIN_WIDTH 300
+#define SPINBUTTON_MIN_WIDTH 50
+#define BUTTON_MIN_WIDTH     50
+#define MAX_SVG_SIZE     1600
+
+typedef struct FilterVals {
+  gboolean               horizontalBlendFlag;
+  gboolean               verticalBlendFlag;
+  gint32                 altSelection;
+  gint32                 borderRadius;
+  gchar                  selectionSVGFileName[MAX_SVG_SIZE]; /* contains small xml string or reference to SVG file */
+
+} FilterVals;
+
+
+
+typedef struct FilterContext {
+  gint32                 imageId;
+  gint32                 drawableId;
+  gint32                 workLayerId;
+  gint                   workLayerWidth;
+  gint                   workLayerHeight;
+  gint                   workLayerOffsX;
+  gint                   workLayerOffsY;
+
+  gint                   origIntersectX;
+  gint                   origIntersectY;
+  FilterVals             *valPtr;
+
+  gboolean               doProgress;
+  gboolean               doFlush;
+  gboolean               doClearSelection;
+  gdouble                progressStepsDone;
+  gdouble                progressStepsTotal;
+
+} FilterContext;
+
+
+
+
+typedef struct BorderPixel {
+   gint     pos;
+   guchar  *colorRGBAPtr;  /* NULL marks invalid color */
+} BorderPixel;
+
+typedef struct GuiStuff {
+  gint32      imageId;
+  GtkWidget  *ok_button;
+  GtkWidget  *msg_label;
+  GtkWidget  *svg_entry;
+  GtkWidget  *svg_filesel;
+  FilterVals *valPtr;
+} GuiStuff;
+
+
+static FilterVals fiVals;
+
+
+
+
+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 gint32   gap_blend_fill_apply_run(gint32 image_id, gint32 activeDrawableId, gboolean doProgress, gboolean doFlush, FilterVals *fiVals);
+static gboolean gap_blend_fill_dialog(FilterVals *fiVals, gint32 drawable_id);
+
+
+/* Global Variables */
+GimpPlugInInfo PLUG_IN_INFO =
+{
+  NULL,   /* init_proc  */
+  NULL,   /* quit_proc  */
+  query,  /* query_proc */
+  run     /* run_proc   */
+};
+
+static const GimpParamDef in_args[] =
+{
+    { GIMP_PDB_INT32,    "run-mode",      "Interactive, non-interactive" },
+    { GIMP_PDB_IMAGE,    "image",         "Input image"                  },
+    { GIMP_PDB_DRAWABLE, "drawable",      "Input layer (RGB or RGBA)"               },
+    { GIMP_PDB_INT32,    "horizontal",    "0 .. dont blend colors horizontal. "
+                                          "1 .. blend colors horizontal." },
+    { GIMP_PDB_INT32,    "vertical",      "0 .. dont blend colors vertical. "
+                                          "1 .. blend colors vertical." },
+    { GIMP_PDB_DRAWABLE, "altSelection",  "-1 use current selection, "
+                                          "-2 set selection by loading vectors from XML string (SVG XML string has to be provided in parameter selSVGFile) "
+                                          "-3 set selection by loading vectors from SVG file (name has to be provided in parameter selSVGFile) "
+                                          "or provide a valid positive drawable id of another layer (in another image) to copy selection from."
+                                          "(note: if the image where altSelection layer is part of has no selection, "
+                                          " then use a greayscale copy of the altSelection drawable as selection."
+                                          " and scale to current image size if necessary." },
+    { GIMP_PDB_INT32,    "borderRadius",  "radius for picking border colors (1 to 10)" },
+    { GIMP_PDB_STRING,   "selSVGFile",    "optional name of a file that contains the selection as vectors in SVG format. (set altSelection to -2)" }
+};
+
+
+static const GimpParamDef return_vals[] = {
+    { GIMP_PDB_DRAWABLE, "drawable",      "unused" }
+};
+
+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_GBOOLEAN        (GIMP_ITER_FALSE,  fiVals.horizontalBlendFlag,     "horizontalBlendFlag"),
+    GIMP_LASTVALDEF_GBOOLEAN        (GIMP_ITER_FALSE,  fiVals.verticalBlendFlag,       "verticalBlendFlag"),
+    GIMP_LASTVALDEF_DRAWABLE        (GIMP_ITER_TRUE,   fiVals.altSelection,            "altSelection"),
+    GIMP_LASTVALDEF_GINT32          (GIMP_ITER_TRUE,   fiVals.borderRadius,            "borderRadius"),
+    GIMP_LASTVALDEF_ARRAY           (GIMP_ITER_FALSE,  fiVals.selectionSVGFileName,    "svgFileNameArray"),
+    GIMP_LASTVALDEF_GCHAR           (GIMP_ITER_FALSE,  fiVals.selectionSVGFileName[0], "svgFilenameNameChar"),
+
+  };
+
+  /* registration for last values buffer structure (useful for animated filter apply) */
+  gimp_lastval_desc_register(PLUG_IN_NAME,
+                             &fiVals,
+                             sizeof(fiVals),
+                             G_N_ELEMENTS (lastvals),
+                             lastvals);
+
+
+  gimp_plugin_domain_register (GETTEXT_PACKAGE, LOCALEDIR);
+
+
+  /* the actual installation of the plugin */
+  gimp_install_procedure (PLUG_IN_NAME,
+                          "Fill the selected area via blending border colors outside the selection.",
+                          "Fill selected area by blending surrounding colors to cover it. "
+                          "The fill area can be represented by the current selection  "
+                          "or by an alternative selction (provided as parameter altSelection) "
+                          "If the image, that is refered by the altSelection drawable_id has a selection "
+                          "then the refered selection is used to identify the fill area. "
+                          "otherwise a grayscale copy of the altSelection drawable_id will be used "
+                          "to identify the area that shall be filled. "
+                          "altSelection value -1 indicates that the current selection of the input image shall be used. "
+                          "This plug-in renders colors for all pixels in the selected area "
+                          "This is done by blending colors of opposite border pixels outside the selection "
+                          "and replaces colors of the selected area by the calculated blend color values. "
+                          "The current implementation supports horizontal and/or vertical blend directions. "
+                          "This filter is intended to fix small areas in videos shot with a camera that has some defect sensor pixels. "
+                          " ",
+                          PLUG_IN_AUTHOR,
+                          PLUG_IN_COPYRIGHT,
+                          GAP_VERSION_WITH_DATE,
+                          N_("Blend Fill..."),
+                          PLUG_IN_IMAGE_TYPES,
+                          GIMP_PLUGIN,
+                          global_number_in_args,
+                          global_number_out_args,
+                          in_args,
+                          return_vals);
+
+
+  {
+    /* Menu names */
+    const char *menupath_image_layer_tranparency = N_("<Image>/Video/Layer/Enhance/");
+
+    gimp_plugin_menu_register (PLUG_IN_NAME, menupath_image_layer_tranparency);
+  }
+
+}  /* 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;
+  gint32       activeDrawableId = -1;
+  gboolean doProgress;
+  gboolean doFlush;
+  GapLastvalAnimatedCallInfo  animCallInfo;
+
+
+
+  /* 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;
+  doFlush = 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;
+
+  /* init default values */
+  fiVals.horizontalBlendFlag = TRUE;
+  fiVals.verticalBlendFlag   = TRUE;
+  fiVals.altSelection        = SELECTION_FROM_SVG_FILE;
+  fiVals.borderRadius        = 3;
+  fiVals.selectionSVGFileName[0] = '\0';
+  g_snprintf(fiVals.selectionSVGFileName
+             , sizeof(fiVals.selectionSVGFileName), "%s"
+             , _("selection.svg"));
+
+  /* get image and drawable */
+  image_id = param[1].data.d_int32;
+  activeDrawableId = param[2].data.d_drawable;
+
+  /* Possibly retrieve data from a previous run */
+  gimp_get_data (name, &fiVals);
+
+  /* how are we running today? */
+  switch (run_mode)
+  {
+    gboolean dialogOk;
+
+    dialogOk = FALSE;
+
+    case GIMP_RUN_INTERACTIVE:
+      /* Get information from the dialog */
+      dialogOk = gap_blend_fill_dialog(&fiVals, activeDrawableId);
+      if (!dialogOk)
+      {
+        /* return without processing in case the Dialog was cancelled */
+        return;
+      }
+      doProgress = TRUE;
+      doFlush =  TRUE;
+
+      break;
+
+    case GIMP_RUN_NONINTERACTIVE:
+      /* check to see if invoked with the correct number of parameters */
+      if (nparams == global_number_in_args)
+      {
+          fiVals.horizontalBlendFlag     = (param[3].data.d_int32 == 0) ? FALSE : TRUE;
+          fiVals.verticalBlendFlag       = (param[4].data.d_int32 == 0) ? FALSE : TRUE;
+          fiVals.altSelection  = (gint32)  param[5].data.d_drawable;
+          fiVals.borderRadius            = CLAMP(param[6].data.d_int32, 1, 10);
+
+          fiVals.selectionSVGFileName[0] = '\0';
+          if(param[7].data.d_string != NULL)
+          {
+            g_snprintf(fiVals.selectionSVGFileName, MAX_SVG_SIZE -1, "%s", param[7].data.d_string);
+          }
+
+      }
+      else
+      {
+        status = GIMP_PDB_CALLING_ERROR;
+      }
+
+      break;
+
+    case GIMP_RUN_WITH_LAST_VALS:
+      animCallInfo.animatedCallInProgress = FALSE;
+      gimp_get_data(GAP_LASTVAL_KEY_ANIMATED_CALL_INFO, &animCallInfo);
+
+      if(animCallInfo.animatedCallInProgress != TRUE)
+      {
+        doProgress = TRUE;
+        doFlush =  TRUE;
+      }
+      break;
+
+    default:
+      break;
+  }
+
+  if (status == GIMP_PDB_SUCCESS)
+  {
+    /* Run the main function */
+    values[1].data.d_drawable =
+          gap_blend_fill_apply_run(image_id, activeDrawableId, doProgress, doFlush, &fiVals);
+
+    /* Store variable states for next run */
+    if (run_mode == GIMP_RUN_INTERACTIVE)
+    {
+      gimp_set_data (name, &fiVals, sizeof (FilterVals));
+    }
+
+    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 (doFlush)
+    {
+      gimp_displays_flush ();
+    }
+
+
+  }
+  values[0].data.d_status = status;
+
+}       /* end run */
+
+
+
+/* ---------------------------------------------- */
+/* ---------------------------------------------- */
+/* ---------------------------------------------- */
+/* ---------------------------------------------- */
+
+/* --------------------------
+ * p_to_double
+ * --------------------------
+ */
+static inline gdouble
+p_to_double(guchar val)
+{
+  gdouble dblValue;
+
+  dblValue = val;
+  return (dblValue);
+}
+
+
+/* --------------------------
+ * p_do_progress_steps
+ * --------------------------
+ */
+static void
+p_do_progress_steps(FilterContext *context, gint steps)
+{
+  if(context->doProgress)
+  {
+    gdouble currentProgress;
+
+    context->progressStepsDone += steps;
+    currentProgress = CLAMP(context->progressStepsDone / context->progressStepsTotal, 0.0, 1.0);
+    gimp_progress_update (currentProgress);
+  }
+
+}  /* end p_do_progress_steps */
+
+
+/* --------------------------
+ * p_progress_init
+ * --------------------------
+ */
+static void
+p_progress_init(FilterContext *context)
+{
+  if(context->doProgress)
+  {
+    gdouble areaPixels;
+
+    areaPixels = context->workLayerWidth * context->workLayerHeight;
+    context->progressStepsDone = 0.0;
+
+    /* progress steps e.g. pixels to be handled for creating the work layer */
+    context->progressStepsTotal = areaPixels;
+
+    if (context->valPtr->horizontalBlendFlag)
+    {
+      context->progressStepsTotal += areaPixels;
+    }
+    if (context->valPtr->verticalBlendFlag)
+    {
+      context->progressStepsTotal += areaPixels;
+    }
+
+    gimp_progress_init( _("Blendfill ..."));
+
+  }
+
+}  /* end p_progress_init */
+
+
+/* -------------------------------------
+ * p_mergeValidColors
+ * -------------------------------------
+ * merge colorRGBA into resultRGBA
+ * both are expected as guchar array with 4 elements for the red,green, blue and
+ * selection/processing status information in the alpha channel.
+ * - in case resultRGBA is still undefined
+ *   the specified colorRGBA is copied to resultRGBA without merge.
+ * - in case both colors are undefined nothing is done.
+ */
+static void
+p_mergeValidColors(guchar *resultRGBAPtr, guchar *colorRGBAPtr)
+{
+  if ((colorRGBAPtr == NULL) || (resultRGBAPtr == NULL))
+  {
+    return;
+  }
+  if (resultRGBAPtr[ALPHA_IDX] != ALPHA_SELECTED_COLOR_VALID)
+  {
+    /* resultRGBAPtr is invalid, simply copy colorRGBAPtr because mix would give unwanted results */
+    resultRGBAPtr[RED_IDX] = colorRGBAPtr[RED_IDX];
+    resultRGBAPtr[BLU_IDX] = colorRGBAPtr[BLU_IDX];
+    resultRGBAPtr[GRN_IDX] = colorRGBAPtr[GRN_IDX];
+    resultRGBAPtr[ALPHA_IDX] = colorRGBAPtr[ALPHA_IDX];
+  }
+  else
+  {
+    gdouble r, g, b;
+
+    /* both resultRGBAPtr and colorRGBAPtr are valid, therefore merge colorRGBAPtr into result */
+    r = GAP_BASE_MIX_VALUE(0.5, p_to_double(resultRGBAPtr[0]), p_to_double(colorRGBAPtr[0]));
+    g = GAP_BASE_MIX_VALUE(0.5, p_to_double(resultRGBAPtr[1]), p_to_double(colorRGBAPtr[1]));
+    b = GAP_BASE_MIX_VALUE(0.5, p_to_double(resultRGBAPtr[2]), p_to_double(colorRGBAPtr[2]));
+
+    resultRGBAPtr[RED_IDX] = CLAMP(r, 0, 255);
+    resultRGBAPtr[BLU_IDX] = CLAMP(g, 0, 255);
+    resultRGBAPtr[GRN_IDX] = CLAMP(b, 0, 255);
+    resultRGBAPtr[ALPHA_IDX] = ALPHA_SELECTED_COLOR_VALID;
+  }
+
+}  /* end p_mergeValidColors */
+
+
+/* -------------------------------------
+ * p_mixColorsWeightenedByDistance
+ * -------------------------------------
+ * mix both border colors weightened by position within selection length
+ */
+static inline void
+p_mixColorsWeightenedByDistance(guchar *colorRGBAPtr
+   , BorderPixel *border1, BorderPixel *border2, gint pos)
+{
+  gdouble r, g, b;
+  gdouble mixfactor;
+  gdouble selLength;
+  gdouble deltaPos;
+
+  selLength = 1 + (border2->pos - border1->pos);
+  deltaPos = pos - border1->pos;
+
+  mixfactor = CLAMP(deltaPos / selLength, 0.0, 1.0);
+
+  r = GAP_BASE_MIX_VALUE(mixfactor, p_to_double(border1->colorRGBAPtr[0]), p_to_double(border2->colorRGBAPtr[0]));
+  g = GAP_BASE_MIX_VALUE(mixfactor, p_to_double(border1->colorRGBAPtr[1]), p_to_double(border2->colorRGBAPtr[1]));
+  b = GAP_BASE_MIX_VALUE(mixfactor, p_to_double(border1->colorRGBAPtr[2]), p_to_double(border2->colorRGBAPtr[2]));
+
+  colorRGBAPtr[RED_IDX] = CLAMP(r, 0, 255);
+  colorRGBAPtr[BLU_IDX] = CLAMP(g, 0, 255);
+  colorRGBAPtr[GRN_IDX] = CLAMP(b, 0, 255);
+  colorRGBAPtr[ALPHA_IDX] = ALPHA_SELECTED_COLOR_VALID;
+
+}  /* end p_mixColorsWeightenedByDistance */
+
+
+/* -------------------------------------
+ * p_copyBorderColor
+ * -------------------------------------
+ */
+static inline void
+p_copyBorderColor(guchar *colorRGBA, BorderPixel *border)
+{
+  colorRGBA[RED_IDX] = border->colorRGBAPtr[RED_IDX];
+  colorRGBA[BLU_IDX] = border->colorRGBAPtr[BLU_IDX];
+  colorRGBA[GRN_IDX] = border->colorRGBAPtr[GRN_IDX];
+  colorRGBA[ALPHA_IDX] = ALPHA_SELECTED_COLOR_VALID;
+
+}  /* end p_copyBorderColor */
+
+
+
+/* -------------------------------------
+ * p_color_processing_one_pixel
+ * -------------------------------------
+ */
+static void
+p_color_processing_one_pixel(guchar *resultRGBAPtr, BorderPixel *border1, BorderPixel *border2, gint pos)
+{
+  guchar colorRGBA[4];
+  guchar *colorRGBAPtr;
+
+  if (resultRGBAPtr == NULL)
+  {
+    return;
+  }
+
+  colorRGBAPtr = NULL;
+
+  if (border1->colorRGBAPtr)
+  {
+    colorRGBAPtr = &colorRGBA[0];
+    if (border2->colorRGBAPtr)
+    {
+      p_mixColorsWeightenedByDistance(colorRGBAPtr,  border1, border2, pos);
+    }
+    else
+    {
+      p_copyBorderColor(colorRGBAPtr, border1);
+    }
+  }
+  else if (border2->colorRGBAPtr)
+  {
+      colorRGBAPtr = &colorRGBA[0];
+      p_copyBorderColor(colorRGBAPtr, border2);
+  }
+  p_mergeValidColors(resultRGBAPtr, colorRGBAPtr);
+
+}  /* end p_color_processing_one_pixel */
+
+
+/* ----------------------------------------
+ * p_mix_border_colors
+ * ----------------------------------------
+ * mix unselected neighbour border colors up to specified borderRadius
+ * in case there is only one border pixel available, the border->colorRGBAPtr
+ * is kept unchanged an no mix can be done.
+ * if there are 2 or more unselected neighbour border pixels available
+ * the border colors are mixed into mixRGBAPtr and this resulting mixed color
+ * is set as new border->colorRGBAPtr.
+ * The mix is done 50:50 in a loop, therefore the nearest border pixel is processed
+ * as the last one and has typically more weight than the farest border pixel.
+ */
+static void
+p_mix_border_colors(guchar *mixRGBAPtr, guchar *pixLine, gint lineLength
+  , BorderPixel *border, FilterContext *context, gint step)
+{
+  gint startPos;
+  gint pos;
+  gint idx;
+  gint radius;
+
+  if (border->colorRGBAPtr == NULL)
+  {
+    return;
+  }
+
+  radius = 1;
+  startPos = pos;
+  for (pos = border->pos + step; ((pos < lineLength) && (pos >= 0)); pos += step)
+  {
+    idx = pos * 4;
+    if (pixLine[idx + ALPHA_IDX] != ALPHA_NOT_SELECTED)
+    {
+      break;
+    }
+    radius++;
+    startPos = pos;
+    if(radius >= context->valPtr->borderRadius)
+    {
+      break;
+    }
+  }
+
+  if (radius > 1)
+  {
+    gint cnt;
+
+    pos = startPos;
+    idx = pos * 4;
+
+    mixRGBAPtr[RED_IDX] = pixLine[idx + RED_IDX];
+    mixRGBAPtr[BLU_IDX] = pixLine[idx + BLU_IDX];
+    mixRGBAPtr[GRN_IDX] = pixLine[idx + GRN_IDX];
+    mixRGBAPtr[ALPHA_IDX] = ALPHA_SELECTED_COLOR_VALID;
+
+
+    for (cnt = 1; cnt < radius; cnt++)
+    {
+      pos -= step;
+      idx = pos * 4;
+
+      p_mergeValidColors(mixRGBAPtr, &pixLine[idx]);
+
+
+    }
+    border->colorRGBAPtr = mixRGBAPtr;
+
+  }
+
+}  /* end p_mix_border_colors */
+
+
+/* ----------------------------------------
+ * p_color_blend_pixel_line
+ * ----------------------------------------
+ * pixLine input is expected as RGBA8 pixel row (or column)
+ * where the Alpha channel byte is used as selection and processing state information
+ * (and does not represent transparency as normal RGBA colormodel would)
+ * The algorithm iterates along the line and fills all selected pixels
+ * (identified by Alpha != ALPHA_NOT_SELECTED) with color blend from
+ * last border pixel color to next border pixel.
+ * The original color of the pixels is ignored in case the pixel has
+ * the state ALPHA_SELECTED_COLOR_UNDEFINED but the state is set
+ * to ALPHA_SELECTED_COLOR_VALID when the new color is assigned to the pixel.
+ *
+ * If only one border pixel was found, all its selected neighbour pixels
+ * ill be filled with the border color without blending.
+ * If there is NO unselected border pixel at all, the selected pixel keeps
+ * its original color.
+ *
+ * in case the processed SELECTED pixel has a already reached state ALPHA_SELECTED_COLOR_VALID
+ * it is mixed 50:50 with the calculated blend (or border) color value.
+ * (this will occure when both horizontal and vertical blend is applied)
+ *
+ */
+static void
+p_color_blend_pixel_line(guchar *pixLine, gint lineLength, FilterContext *context)
+{
+  gint pos;
+  gint idx;
+  BorderPixel border1;
+  BorderPixel border2;
+  gboolean    insideSelecton;
+  guchar      mix1RGBA[4];
+  guchar      mix2RGBA[4];
+
+  border1.colorRGBAPtr = NULL;
+  border2.colorRGBAPtr = NULL;
+  insideSelecton = FALSE;
+  idx = 0;
+  for (pos = 0; pos < lineLength; pos++)
+  {
+    if (pixLine[idx + ALPHA_IDX] == ALPHA_NOT_SELECTED)
+    {
+      insideSelecton = FALSE;
+      border1.pos = pos;
+      border1.colorRGBAPtr = &pixLine[idx];
+      border2.colorRGBAPtr = NULL;
+    }
+    else
+    {
+      if (insideSelecton != TRUE)
+      {
+        gint pos2;
+        gint idx2;
+        insideSelecton = TRUE;
+
+        /* find (border2) the next pixel that is outside the selection */
+        idx2 = idx +4;
+        for (pos2 = pos +1; pos2 < lineLength; pos2++)
+        {
+          if (pixLine[idx2 + ALPHA_IDX] == ALPHA_NOT_SELECTED)
+          {
+            border2.pos = pos2;
+            border2.colorRGBAPtr = &pixLine[idx2];
+            break;
+          }
+          idx2 += 4;
+        }
+
+        if (context->valPtr->borderRadius > 1)
+        {
+          p_mix_border_colors(&mix1RGBA[0], pixLine, lineLength, &border1, context, -1);
+          p_mix_border_colors(&mix2RGBA[0], pixLine, lineLength, &border2, context, 1);
+        }
+
+      }
+      p_color_processing_one_pixel(&pixLine[idx], &border1, &border2, pos);
+    }
+
+    idx += 4;
+  }
+
+  p_do_progress_steps(context, lineLength);
+
+}  /* end p_color_blend_pixel_line */
+
+
+/* ----------------------------------------
+ * p_horizontal_color_blend
+ * ----------------------------------------
+ */
+static void
+p_horizontal_color_blend(FilterContext *context)
+{
+  GimpPixelRgn  workPR;
+  GimpDrawable *workDrawable;
+  guchar       *pixLine;
+  gint          row;
+
+  workDrawable = gimp_drawable_get (context->workLayerId);
+  gimp_pixel_rgn_init (&workPR, workDrawable
+                      , 0, 0
+                      , context->workLayerWidth, context->workLayerHeight
+                      , TRUE      /* dirty */
+                      , FALSE    /* shadow */
+                      );
+
+
+
+  /*  allocate buffer for one horizontal pixel line  */
+  pixLine = g_new (guchar, context->workLayerWidth * workDrawable->bpp);
+
+  for (row = 0; row < context->workLayerHeight; row++)
+  {
+    gimp_pixel_rgn_get_row (&workPR, pixLine, 0, row, context->workLayerWidth);
+    p_color_blend_pixel_line(pixLine, context->workLayerWidth, context);
+    gimp_pixel_rgn_set_row (&workPR, pixLine, 0, row, context->workLayerWidth);
+
+  }
+
+  gimp_drawable_flush (workDrawable);
+  gimp_drawable_update (context->workLayerId, 0, 0, context->workLayerWidth, context->workLayerHeight);
+  gimp_drawable_detach(workDrawable);
+
+  g_free (pixLine);
+
+}  /* end p_horizontal_color_blend */
+
+
+/* ----------------------------------------
+ * p_vertical_color_blend
+ * ----------------------------------------
+ */
+static void
+p_vertical_color_blend(FilterContext *context)
+{
+  GimpPixelRgn  workPR;
+  GimpDrawable *workDrawable;
+  guchar       *pixLine;
+  gint          col;
+
+  workDrawable = gimp_drawable_get (context->workLayerId);
+  gimp_pixel_rgn_init (&workPR, workDrawable
+                      , 0, 0
+                      , context->workLayerWidth, context->workLayerHeight
+                      , TRUE      /* dirty */
+                      , FALSE    /* shadow */
+                      );
+
+
+
+  /*  allocate buffer for one vertical pixel line  */
+  pixLine = g_new (guchar, context->workLayerHeight * workDrawable->bpp);
+
+  for (col = 0; col < context->workLayerWidth; col++)
+  {
+    gimp_pixel_rgn_get_col (&workPR, pixLine, col, 0, context->workLayerHeight);
+    p_color_blend_pixel_line(pixLine, context->workLayerHeight, context);
+    gimp_pixel_rgn_set_col (&workPR, pixLine, col, 0, context->workLayerHeight);
+
+  }
+
+  gimp_drawable_flush (workDrawable);
+  gimp_drawable_update (context->workLayerId, 0, 0, context->workLayerWidth, context->workLayerHeight);
+  gimp_drawable_detach(workDrawable);
+
+  g_free (pixLine);
+
+}  /* end p_vertical_color_blend */
+
+
+
+
+
+
+/* --------------------------
+ * p_set_altSelection
+ * --------------------------
+ * create selection as Grayscale copy of the specified altSelection layer
+ *  - operates on a duplicate of the image references by altSelection
+ *  - this duplicate is scaled to same size as specified image_id
+ *
+ * - if altSelection refers to an image that has a selection
+ *   then use this selection instead of the layer itself.
+ */
+static gboolean
+p_set_altSelection(FilterContext *context)
+{
+  gboolean l_rc;
+  if(gap_debug)
+  {
+    printf("p_set_altSelection: drawable_id:%d  altSelection:%d\n"
+      ,(int)context->drawableId
+      ,(int)context->valPtr->altSelection
+      );
+  }
+
+  if ((context->drawableId == context->valPtr->altSelection) || (context->drawableId < 0))
+  {
+    return (FALSE);
+  }
+  context->doClearSelection = TRUE;
+  l_rc = gap_image_set_selection_from_selection_or_drawable(context->imageId
+                                                          , context->valPtr->altSelection
+                                                          , FALSE);
+
+  return (l_rc);
+}
+
+
+
+/* ---------------------------------
+ * p_rgn_render_init_workregion
+ * ---------------------------------
+ */
+static void
+p_rgn_render_init_workregion (const GimpPixelRgn *origPR
+                    , const GimpPixelRgn *workPR
+                    , const GimpPixelRgn *selPR
+                    , FilterContext *context)
+{
+  guint    row;
+  guchar* orig  = origPR->data;
+  guchar* work  = workPR->data;
+  guchar* sel   = selPR->data;
+
+
+  if(gap_debug)
+  {
+    printf("p_rgn_render_init_workregion  orig W:%d H:%d  work W:%d H:%d  sel W:%d H:%d\n"
+       ,(int)origPR->w
+       ,(int)origPR->h
+       ,(int)workPR->w
+       ,(int)workPR->h
+       ,(int)selPR->w
+       ,(int)selPR->h
+       );
+  }
+
+
+  for (row = 0; row < MIN(origPR->h, workPR->h); row++)
+  {
+    guint   col;
+    guint   origIdx = 0;
+    guint   workIdx = 0;
+    guint   selIdx = 0;
+
+    for (col = 0; col < MIN(origPR->w, workPR->w); col++)
+    {
+        work[workIdx]    = orig[origIdx];
+        work[workIdx +1] = orig[origIdx +1];
+        work[workIdx +2] = orig[origIdx +2];
+
+        work[workIdx + ALPHA_IDX] = ALPHA_NOT_SELECTED;
+        if (col < selPR->w)
+        {
+          if (sel[selIdx] != 0)
+          {
+            work[workIdx + ALPHA_IDX] = ALPHA_SELECTED_COLOR_UNDEFINED;
+          }
+        }
+
+        origIdx += origPR->bpp;
+        workIdx += workPR->bpp;
+        selIdx  += selPR->bpp;
+    }
+
+    orig  += origPR->rowstride;
+    work  += workPR->rowstride;
+    sel   += selPR->rowstride;
+  }
+
+  p_do_progress_steps(context, workPR->h * workPR->w);
+
+}  /* end p_rgn_render_init_workregion */
+
+
+/* ----------------------------------
+ * p_set_selection_from_vectors_string
+ * ----------------------------------
+ * interpret the selectionSVGFileName buffer as XML svg content
+ * and load all path vectors from this string.
+ * (on errors keep current selection)
+ */
+static void
+p_set_selection_from_vectors_string(FilterContext *context)
+{
+  gboolean vectorsOk;
+  gint     num_vectors;
+  gint32  *vectors_ids;
+
+  vectorsOk = FALSE;
+  if (context->valPtr->selectionSVGFileName != '\0')
+  {
+    gint length;
+    
+    length = strlen(context->valPtr->selectionSVGFileName);
+    vectorsOk = gimp_vectors_import_from_string (context->imageId
+                                                ,context->valPtr->selectionSVGFileName
+                                                ,length
+                                                , TRUE  /* Merge paths into a single vectors object. */
+                                                , TRUE  /* Scale the SVG to image dimensions. */
+                                                , &num_vectors
+                                                , &vectors_ids
+                                                );
+  }
+
+  if ((vectorsOk) && (vectors_ids != NULL) && (num_vectors > 0))
+  {
+    gboolean       selOk;
+    gint32         vectorId;
+    GimpChannelOps operation;
+
+    vectorId = vectors_ids[0];
+    operation = GIMP_CHANNEL_OP_REPLACE;
+    selOk = gimp_vectors_to_selection(vectorId
+                                     , operation
+                                     , FALSE      /*  antialias */
+                                     , FALSE      /*  feather   */
+                                     , 0.0        /*  gdouble feather_radius_x */
+                                     , 0.0        /*  gdouble feather_radius_y */
+                                     );
+    gimp_image_remove_vectors(context->imageId, vectorId);
+    context->doClearSelection = TRUE;
+  }
+
+}  /* end p_set_selection_from_vectors_string */
+
+
+
+/* ----------------------------------
+ * p_set_selection_from_vectors_file
+ * ----------------------------------
+ * import selection from an SVG vectors file
+ * and replace the current selection on success.
+ * (on errors keep current selection)
+ */
+static void
+p_set_selection_from_vectors_file(FilterContext *context)
+{
+  gboolean vectorsOk;
+  gint     num_vectors;
+  gint32  *vectors_ids;
+
+  vectorsOk = FALSE;
+  if (context->valPtr->selectionSVGFileName != '\0')
+  {
+    if(g_file_test(context->valPtr->selectionSVGFileName, G_FILE_TEST_EXISTS))
+    {
+      vectorsOk = gimp_vectors_import_from_file   (context->imageId
+                                                  ,context->valPtr->selectionSVGFileName
+                                                  , TRUE  /* Merge paths into a single vectors object. */
+                                                  , TRUE  /* Scale the SVG to image dimensions. */
+                                                  , &num_vectors
+                                                  , &vectors_ids
+                                                  );
+    }
+  }
+
+
+  if ((vectorsOk) && (vectors_ids != NULL) && (num_vectors > 0))
+  {
+    gboolean       selOk;
+    gint32         vectorId;
+    GimpChannelOps operation;
+
+    vectorId = vectors_ids[0];
+    operation = GIMP_CHANNEL_OP_REPLACE;
+    selOk = gimp_vectors_to_selection(vectorId
+                                     , operation
+                                     , FALSE      /*  antialias */
+                                     , FALSE      /*  feather   */
+                                     , 0.0        /*  gdouble feather_radius_x */
+                                     , 0.0        /*  gdouble feather_radius_y */
+                                     );
+    gimp_image_remove_vectors(context->imageId, vectorId);
+    context->doClearSelection = TRUE;
+  }
+
+}  /* end p_set_selection_from_vectors_file */
+
+
+
+/* --------------------------
+ * p_render_initial_workLayer
+ * --------------------------
+ * Creates a worklayer in size of the interection of
+ * the current selection (expanded by borderRadius) and the input Layer (origDrawable).
+ * The worklayer has type RGBA, but the Alpha channel byte is not used for transparency.
+ * It represents selection and processing state information that is initalized with:
+ *    ALPHA_NOT_SELECTED                for all pixels outside the current (unexpanded) selection
+ *    ALPHA_SELECTED_COLOR_UNDEFINED    for all pixels outside the current (unexpanded) selection
+ * Note that this implicite sharpens the selection, e.g. weak selected
+ * pixels are considered as selected and will be processed too.
+ */
+static void
+p_render_initial_workLayer(FilterContext *context)
+{
+  GimpPixelRgn origPR, workPR, selPR;
+  GimpDrawable *origDrawable;
+  GimpDrawable *workDrawable;
+  GimpDrawable *selDrawable;
+  gpointer  pr;
+
+  origDrawable = gimp_drawable_get (context->drawableId);
+  workDrawable = gimp_drawable_get (context->workLayerId);
+  selDrawable  = gimp_drawable_get (gimp_image_get_selection(context->imageId));
+
+  gimp_pixel_rgn_init (&origPR, origDrawable
+                      , context->origIntersectX, context->origIntersectY
+                      , context->workLayerWidth, context->workLayerHeight
+                      , FALSE    /* dirty */
+                      , FALSE    /* shadow */
+                      );
+
+  gimp_pixel_rgn_init (&workPR, workDrawable
+                      , 0, 0
+                      , context->workLayerWidth, context->workLayerHeight
+                      , TRUE      /* dirty */
+                      , FALSE    /* shadow */
+                      );
+  gimp_pixel_rgn_init (&selPR, selDrawable
+                      , context->workLayerOffsX, context->workLayerOffsY
+                      , context->workLayerWidth, context->workLayerHeight
+                      , FALSE     /* dirty */
+                      , FALSE     /* shadow */
+                       );
+
+  for (pr = gimp_pixel_rgns_register (3, &origPR, &workPR, &selPR);
+       pr != NULL;
+       pr = gimp_pixel_rgns_process (pr))
+  {
+      p_rgn_render_init_workregion (&origPR, &workPR, &selPR, context);
+  }
+
+  /*  update the processed region  */
+  gimp_drawable_flush (workDrawable);
+
+  gimp_drawable_update (context->workLayerId, 0, 0, context->workLayerWidth, context->workLayerHeight);
+
+  gimp_drawable_detach(origDrawable);
+  gimp_drawable_detach(workDrawable);
+  gimp_drawable_detach(selDrawable);
+
+}  /* end p_render_initial_workLayer */
+
+
+
+
+
+
+/* ----------------------------------------
+ * p_create_workLayer
+ * ----------------------------------------
+ * create the workLayer as copy of the drawable rectangle area
+ * that intersects with the selection expanded by borderRadius and clipped
+ * to drawable boundaries.
+ * The alpha channel is copied from the selection and the rgb channels
+ * are copied from the drawable.
+ */
+static void
+p_create_workLayer(FilterContext *context)
+{
+  gboolean has_selection;
+  gboolean non_empty;
+  gboolean altSelection_success;
+  gint     x1, y1, x2, y2;
+  gint     ix, iy, ix1, iy1, ix2, iy2;
+  gint     iWidth, iHeight;
+  gint     borderRadius;
+
+
+  altSelection_success = FALSE;
+  borderRadius = context->valPtr->borderRadius;
+
+  if(context->valPtr->altSelection >= 0)
+  {
+    if(gap_debug)
+    {
+      printf("creating altSelection: %d\n", (int)context->valPtr->altSelection);
+    }
+    altSelection_success = p_set_altSelection(context);
+  }
+
+  if(context->valPtr->altSelection == SELECTION_FROM_VECTORS)
+  {
+    p_set_selection_from_vectors_string(context);
+  }
+  else if(context->valPtr->altSelection == SELECTION_FROM_SVG_FILE)
+  {
+    p_set_selection_from_vectors_file(context);
+  }
+
+  has_selection  = gimp_selection_bounds(context->imageId, &non_empty, &x1, &y1, &x2, &y2);
+
+  context->workLayerId = -1;
+  if (non_empty != TRUE)
+  {
+    return;
+  }
+
+  non_empty = gimp_drawable_mask_intersect(context->drawableId, &ix, &iy, &iWidth, &iHeight);
+  if((non_empty != TRUE)
+  || (iWidth <= 0)
+  || (iHeight <= 0))
+  {
+    return;
+  }
+
+  gimp_drawable_offsets(context->drawableId
+                       ,&context->workLayerOffsX
+                       ,&context->workLayerOffsY
+                       );
+
+  ix2 = MIN(gimp_drawable_width(context->drawableId), ix + iWidth + borderRadius);
+  iy2 = MIN(gimp_drawable_height(context->drawableId), iy + iHeight + borderRadius);
+
+  ix1 = MAX(0, ix - borderRadius);
+  iy1 = MAX(0, iy - borderRadius);
+
+  context->workLayerOffsX += ix1;
+  context->workLayerOffsY += iy1;
+  context->workLayerWidth = ix2 - ix1;
+  context->workLayerHeight = iy2 - iy1;
+
+  context->origIntersectX = ix1;
+  context->origIntersectY = iy1;
+
+  if(gap_debug)
+  {
+    printf("intersect ix:%d iy:%d iWidth:%d iHeight:%d  ix1:%d iy1:%d ix2:%d iy2:%d\n"
+       ,(int)ix
+       ,(int)iy
+       ,(int)iWidth
+       ,(int)iHeight
+       ,(int)ix1
+       ,(int)iy1
+       ,(int)ix2
+       ,(int)iy2
+       );
+    printf("workLayerOffsX:%d workLayerOffsY:%d workLayerWidth:%d workLayerHeight:%d\n"
+       ,(int)context->workLayerOffsX
+       ,(int)context->workLayerOffsY
+       ,(int)context->workLayerWidth
+       ,(int)context->workLayerHeight
+       );
+  }
+
+  p_progress_init(context);
+
+  context->workLayerId = gimp_layer_new(context->imageId
+                , "work"
+                , context->workLayerWidth
+                , context->workLayerHeight
+                , GIMP_RGBA_IMAGE
+                , 100.0   /* full opacity */
+                , 0       /* normal mode */
+                );
+  gimp_image_add_layer(context->imageId, context->workLayerId, 0);
+  gimp_layer_set_offsets(context->workLayerId
+                        , context->workLayerOffsX
+                        , context->workLayerOffsY
+                        );
+
+  p_render_initial_workLayer(context);
+
+}  /* end p_create_workLayer */
+
+
+
+
+
+/* ----------------------------------
+ * gap_blend_fill_apply_run
+ * ----------------------------------
+ */
+static gint32
+gap_blend_fill_apply_run(gint32 image_id, gint32 activeDrawableId, gboolean doProgress, gboolean doFlush, FilterVals *fiVals)
+{
+  FilterContext  filterContext;
+  FilterContext *context;
+  gint32         rc;
+
+  gimp_image_undo_group_start (image_id);
+
+  /* init the context */
+  context = &filterContext;
+  context->doProgress = doProgress;
+  context->doFlush = doFlush;
+  context->doClearSelection = FALSE;
+  context->imageId = image_id;
+  context->drawableId = activeDrawableId;
+  context->workLayerId = -1;
+  context->valPtr = fiVals;
+  fiVals->borderRadius = CLAMP(fiVals->borderRadius, 1, 10);
+
+  rc = -1;
+
+  if(gap_debug)
+  {
+    printf("context Svg:%s\n", context->valPtr->selectionSVGFileName);
+  }
+
+  p_create_workLayer(context);
+  if (context->workLayerId >= 0)
+  {
+    if (context->valPtr->horizontalBlendFlag)
+    {
+      p_horizontal_color_blend(context);
+    }
+
+    if (context->valPtr->verticalBlendFlag)
+    {
+      p_vertical_color_blend(context);
+    }
+
+    rc = gimp_image_merge_down(image_id, context->workLayerId, GIMP_EXPAND_AS_NECESSARY);
+    
+    if(context->doClearSelection)
+    {
+      gimp_selection_none(context->imageId);
+    }
+
+  }
+
+  gimp_image_undo_group_end (image_id);
+  return (rc);
+}  /* end gap_blend_fill_apply_run */
+
+
+
+/* --------------------------- DIALOG stuff ------------------- */
+/* --------------------------- DIALOG stuff ------------------- */
+/* --------------------------- DIALOG stuff ------------------- */
+/* --------------------------- DIALOG stuff ------------------- */
+/* --------------------------- DIALOG stuff ------------------- */
+/* --------------------------- DIALOG stuff ------------------- */
+/* --------------------------- DIALOG stuff ------------------- */
+
+
+
+/* --------------------------------------
+ * 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;
+  }
+}
+
+
+/* ----------------------------
+ * p_selectionConstraintFunc
+ * ----------------------------
+ *
+ */
+static gint
+p_selectionConstraintFunc (gint32   image_id,
+               gint32   drawable_id,
+               gpointer data)
+{
+  if (image_id < 0)
+  {
+    return FALSE;
+  }
+
+  /* dont accept layers from indexed images */
+  if (gimp_drawable_is_indexed (drawable_id))
+  {
+    return FALSE;
+  }
+
+  return TRUE;
+}  /* end p_selectionConstraintFunc */
+
+
+/* --------------------------------------------
+ * p_check_exec_condition_and_set_ok_sesitivity
+ * --------------------------------------------
+ */
+static void
+p_check_exec_condition_and_set_ok_sesitivity(GuiStuff *guiStuffPtr)
+{
+  gboolean okButtonSensitive;
+
+  okButtonSensitive = TRUE;
+  
+  if ((guiStuffPtr->valPtr == NULL)
+  ||  (guiStuffPtr->msg_label == NULL))
+  {
+    return;
+  }
+  gtk_label_set_text(GTK_LABEL(guiStuffPtr->msg_label), "");
+
+  if (guiStuffPtr->valPtr->altSelection == SELECTION_FROM_VECTORS)
+  {
+    gchar   *svgString;
+    gint    *allVectors;
+    gint     num_vectors;
+
+    svgString = NULL;
+    allVectors = gimp_image_get_vectors(guiStuffPtr->imageId, &num_vectors);
+    if(allVectors != NULL)
+    {
+      g_free(allVectors);
+    }
+    if(num_vectors > 0)
+    {
+      svgString = gimp_vectors_export_to_string (guiStuffPtr->imageId
+                                                 , 0  /* vectors_ID for all vectors merged */
+                                                );
+    }
+
+    if (svgString != NULL)
+    {
+      gint     length;
+
+      length = strlen(svgString);
+      if(length >= sizeof(guiStuffPtr->valPtr->selectionSVGFileName))
+      {
+        gchar *msg;
+        
+        msg = g_strdup_printf(_("Path Vectors too large to fit into buffersize:%d.")
+                              , sizeof(guiStuffPtr->valPtr->selectionSVGFileName));
+        gtk_label_set_text(GTK_LABEL(guiStuffPtr->msg_label), msg);
+        g_free(msg);
+        okButtonSensitive = FALSE;
+      }
+      g_free(svgString);
+    }
+    else
+    {
+      gtk_label_set_text(GTK_LABEL(guiStuffPtr->msg_label)
+                        , _("No Path Vectors available."));
+      okButtonSensitive = FALSE;
+    }
+  }
+
+  if (guiStuffPtr->valPtr->altSelection == SELECTION_FROM_SVG_FILE)
+  {
+    okButtonSensitive = FALSE;
+    if (guiStuffPtr->valPtr->selectionSVGFileName[0] != '\0')
+    {
+      if(g_file_test(guiStuffPtr->valPtr->selectionSVGFileName, G_FILE_TEST_EXISTS))
+      {
+        okButtonSensitive = TRUE;
+      }
+      else
+      {
+        gtk_label_set_text(GTK_LABEL(guiStuffPtr->msg_label)
+                        , _("SVG file does not exist (use Save Pats button to create)."));
+      }
+    }
+    else
+    {
+        gtk_label_set_text(GTK_LABEL(guiStuffPtr->msg_label)
+                        , _("please enter SVG filename"));
+    }
+  }
+  
+  if(guiStuffPtr->ok_button)
+  {
+    gtk_widget_set_sensitive(guiStuffPtr->ok_button, okButtonSensitive);
+      
+  }
+   
+}  /* end p_check_exec_condition_and_set_ok_sesitivity */
+
+/* ----------------------------
+ * p_selectionComboCallback
+ * ----------------------------
+ *
+ */
+static void
+p_selectionComboCallback (GtkWidget *widget, gint32 *layerId)
+{
+  gint value;
+
+  gimp_int_combo_box_get_active (GIMP_INT_COMBO_BOX (widget), &value);
+
+  if(gap_debug)
+  {
+    printf("p_selectionComboCallback: LayerAddr:%d value:%d\n"
+      ,(int)layerId
+      ,(int)value
+      );
+  }
+
+  if(layerId != NULL)
+  {
+    GuiStuff *guiStuffPtr;
+    
+    *layerId = value;
+    
+    guiStuffPtr = (GuiStuff *) g_object_get_data (G_OBJECT (widget), "guiStuffPtr");
+    if(guiStuffPtr != NULL)
+    {
+      p_check_exec_condition_and_set_ok_sesitivity(guiStuffPtr);
+    }
+  }
+
+}  /* end p_selectionComboCallback */
+
+
+
+
+
+/* ---------------------------------
+ * svg fileselct callbacks
+ * ---------------------------------
+ */
+
+static void
+on_svg_filesel_destroy          (GtkObject *object,
+                                 GuiStuff  *guiStuffPtr)
+{
+ if(gap_debug) printf("CB: on_svg_filesel_destroy\n");
+ if(guiStuffPtr == NULL) return;
+
+ guiStuffPtr->svg_filesel = NULL;
+}
+
+static void
+on_svg__button_cancel_clicked          (GtkButton *button,
+                                        GuiStuff  *guiStuffPtr)
+{
+ if(gap_debug) printf("CB: on_svg__button_cancel_clicked\n");
+ if(guiStuffPtr == NULL) return;
+
+ if(guiStuffPtr->svg_filesel)
+ {
+   gtk_widget_destroy(guiStuffPtr->svg_filesel);
+   guiStuffPtr->svg_filesel = NULL;
+ }
+}
+
+static void
+on_svg__button_OK_clicked       (GtkButton *button,
+                                 GuiStuff  *guiStuffPtr)
+{
+  const gchar *filename;
+  GtkEntry *entry;
+
+ if(gap_debug) printf("CB: on_svg__button_OK_clicked\n");
+ if(guiStuffPtr == NULL) return;
+
+ if(guiStuffPtr->svg_filesel)
+ {
+   filename =  gtk_file_selection_get_filename (GTK_FILE_SELECTION (guiStuffPtr->svg_filesel));
+   g_snprintf(guiStuffPtr->valPtr->selectionSVGFileName
+             , sizeof(guiStuffPtr->valPtr->selectionSVGFileName), "%s"
+             , filename);
+   entry = GTK_ENTRY(guiStuffPtr->svg_entry);
+   if(entry)
+   {
+      gtk_entry_set_text(entry, filename);
+   }
+   on_svg__button_cancel_clicked(NULL, (gpointer)guiStuffPtr);
+ }
+}
+
+
+
+
+/* ----------------------------------------
+ * p_create_fileselection
+ * ----------------------------------------
+ */
+static GtkWidget*
+p_create_fileselection (GuiStuff *guiStuffPtr)
+{
+  GtkWidget *svg_filesel;
+  GtkWidget *svg__button_OK;
+  GtkWidget *svg__button_cancel;
+
+  svg_filesel = gtk_file_selection_new (_("Select vectorfile name"));
+  gtk_container_set_border_width (GTK_CONTAINER (svg_filesel), 10);
+
+  svg__button_OK = GTK_FILE_SELECTION (svg_filesel)->ok_button;
+  gtk_widget_show (svg__button_OK);
+  GTK_WIDGET_SET_FLAGS (svg__button_OK, GTK_CAN_DEFAULT);
+
+  svg__button_cancel = GTK_FILE_SELECTION (svg_filesel)->cancel_button;
+  gtk_widget_show (svg__button_cancel);
+  GTK_WIDGET_SET_FLAGS (svg__button_cancel, GTK_CAN_DEFAULT);
+
+  g_signal_connect (G_OBJECT (svg_filesel), "destroy",
+                      G_CALLBACK (on_svg_filesel_destroy),
+                      guiStuffPtr);
+  g_signal_connect (G_OBJECT (svg__button_OK), "clicked",
+                      G_CALLBACK (on_svg__button_OK_clicked),
+                      guiStuffPtr);
+  g_signal_connect (G_OBJECT (svg__button_cancel), "clicked",
+                      G_CALLBACK (on_svg__button_cancel_clicked),
+                      guiStuffPtr);
+
+  gtk_widget_grab_default (svg__button_cancel);
+  return svg_filesel;
+}  /* end p_create_fileselection */
+
+
+
+/* ---------------------------------
+ * on_svg_entry_changed
+ * ---------------------------------
+ */
+static void
+on_svg_entry_changed              (GtkEditable     *editable,
+                                   GuiStuff *guiStuffPtr)
+{
+  GtkEntry *entry;
+
+ if(gap_debug)
+ {
+   printf("CB: on_svg_entry_changed\n");
+ }
+ if(guiStuffPtr == NULL)
+ {
+   return;
+ }
+
+ entry = GTK_ENTRY(guiStuffPtr->svg_entry);
+ if(entry)
+ {
+    g_snprintf(guiStuffPtr->valPtr->selectionSVGFileName
+              , sizeof(guiStuffPtr->valPtr->selectionSVGFileName), "%s"
+              , gtk_entry_get_text(entry));
+    p_check_exec_condition_and_set_ok_sesitivity(guiStuffPtr);
+ }
+}
+
+/* ---------------------------------
+ * on_filesel_button_clicked
+ * ---------------------------------
+ */
+static void
+on_filesel_button_clicked             (GtkButton *button,
+                                       GuiStuff  *guiStuffPtr)
+{
+ if(gap_debug)
+ {
+   printf("CB: on_filesel_button_clicked\n");
+ }
+ if(guiStuffPtr == NULL)
+ {
+   return;
+ }
+
+ if(guiStuffPtr->svg_filesel == NULL)
+ {
+   guiStuffPtr->svg_filesel = p_create_fileselection(guiStuffPtr);
+   gtk_file_selection_set_filename (GTK_FILE_SELECTION (guiStuffPtr->svg_filesel),
+                                    guiStuffPtr->valPtr->selectionSVGFileName);
+
+   gtk_widget_show (guiStuffPtr->svg_filesel);
+ }
+
+}  /* end on_filesel_button_clicked */
+
+
+/* ---------------------------------
+ * on_save_svg_clicked
+ * ---------------------------------
+ */
+static void
+on_save_svg_clicked(GtkButton  *button,
+                    GuiStuff   *guiStuffPtr)
+{
+  if (guiStuffPtr->valPtr->selectionSVGFileName != '\0')
+  {
+    gboolean l_wr_permission;
+
+    l_wr_permission = gap_arr_overwrite_file_dialog(guiStuffPtr->valPtr->selectionSVGFileName);
+
+    if(l_wr_permission)
+    {
+      gboolean svgExportOk;
+      gint32   vectors_ID;
+      
+      vectors_ID = 0;  /* 0 refers to all vectors in the image */
+      svgExportOk = gimp_vectors_export_to_file(guiStuffPtr->imageId
+                                          , guiStuffPtr->valPtr->selectionSVGFileName
+                                          , vectors_ID
+                                          );
+      if (svgExportOk != TRUE)
+      {
+        g_message(_("Failed to write SVG file: %s")
+                 , guiStuffPtr->valPtr->selectionSVGFileName);
+      }
+    }
+  }
+}  /* end on_save_svg_clicked */
+
+
+
+
+/* ----------------------------------
+ * p_save_vectors_to_string
+ * ----------------------------------
+ */
+static void
+p_save_vectors_to_string(GuiStuff *guiStuffPtr)
+{
+  gchar   *svgString;
+
+  svgString = gimp_vectors_export_to_string (guiStuffPtr->imageId, 0  /* vectors_ID for all vectors merged */);
+  if (svgString != NULL)
+  {
+    gint     length;
+
+    if(gap_debug)
+    {
+      printf("svgString:%s\n", svgString);
+    }
+    length = strlen(svgString);
+    if(length < sizeof(guiStuffPtr->valPtr->selectionSVGFileName))
+    {
+        g_snprintf(guiStuffPtr->valPtr->selectionSVGFileName
+             , sizeof(guiStuffPtr->valPtr->selectionSVGFileName), "%s"
+             , svgString);
+
+    }
+    else
+    {
+        g_message(_("Path Vectors too large to fit into buffersize:%d.")
+                 , sizeof(guiStuffPtr->valPtr->selectionSVGFileName));
+    }
+    g_free(svgString);
+  }
+  else
+  {
+      g_message(_("No Path Vectors available."));
+  }
+
+}  /* end p_save_vectors_to_string */
+
+
+/* --------------------------
+ * gap_blend_fill_dialog
+ * --------------------------
+ */
+static gboolean
+gap_blend_fill_dialog (FilterVals *fiVals, gint32 drawable_id)
+{
+  GuiStuff guiStuffRecord;
+  GuiStuff *guiStuffPtr;
+  GtkWidget *dialog;
+  GtkWidget *main_vbox;
+  GtkWidget *label;
+  GtkWidget *button;
+  GtkWidget *table;
+  GtkWidget *combo;
+  GtkWidget *entry;
+  GtkWidget *checkbutton;
+  GtkObject *adj;
+  gint       row;
+  gboolean   run;
+  gint       initalComboElem;
+
+
+  guiStuffPtr = &guiStuffRecord;
+  guiStuffPtr->imageId = gimp_drawable_get_image(drawable_id);
+  guiStuffPtr->msg_label = NULL;
+  guiStuffPtr->svg_entry = NULL;
+  guiStuffPtr->svg_filesel = NULL;
+  guiStuffPtr->valPtr = fiVals;
+
+
+  fiVals->altSelection = -1;
+
+  gimp_ui_init (PLUG_IN_BINARY, TRUE);
+
+  dialog = gimp_dialog_new (_("Blend Fill Selection"), PLUG_IN_BINARY,
+                            NULL, 0,
+                            gimp_standard_help_func, PLUG_IN_NAME,
+
+                            GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+
+                            NULL);
+
+  button = gimp_dialog_add_button (GIMP_DIALOG(dialog), GTK_STOCK_OK, GTK_RESPONSE_OK);
+  guiStuffPtr->ok_button = button;
+
+  gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
+                                           GTK_RESPONSE_OK,
+                                           GTK_RESPONSE_CANCEL,
+                                           -1);
+
+  gimp_window_set_transient (GTK_WINDOW (dialog));
+
+  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;
+
+  /* horizontalBlendFlag checkbutton  */
+  label = gtk_label_new (_("fills the selection by blending opposite border colors "
+                           "outside the selction to cover the selected area.\n"
+                           "Intended to fix small pixel errors"));
+  gtk_widget_show (label);
+  gtk_table_attach (GTK_TABLE (table), label, 0, 3, row, row+1,
+                    (GtkAttachOptions) (GTK_FILL),
+                    (GtkAttachOptions) (0), 0, 0);
+  gtk_misc_set_alignment (GTK_MISC (label), 0.5, 0.5);
+
+  row++;
+
+  /* horizontalBlendFlag checkbutton  */
+  label = gtk_label_new (_("Horizontal Blend:"));
+  gtk_widget_show (label);
+  gtk_table_attach (GTK_TABLE (table), label, 0, 1, row, row+1,
+                    (GtkAttachOptions) (GTK_FILL),
+                    (GtkAttachOptions) (0), 0, 0);
+  gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
+
+
+  checkbutton = gtk_check_button_new_with_label (" ");
+  gimp_help_set_help_data (checkbutton, _("ON: enable horizontal color blending. "
+                                          "OFF: disable horizontal color blending."), NULL);
+  gtk_widget_show (checkbutton);
+  gtk_table_attach( GTK_TABLE(table), checkbutton, 1, 2, row, row+1,
+                    GTK_FILL, 0, 0, 0 );
+  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (checkbutton), fiVals->horizontalBlendFlag);
+  g_signal_connect (checkbutton, "toggled",
+                    G_CALLBACK (on_gboolean_button_update),
+                    &fiVals->horizontalBlendFlag);
+
+  row++;
+
+  /* verticalBlendFlag checkbutton  */
+  label = gtk_label_new (_("Vertical Blend:"));
+  gtk_widget_show (label);
+  gtk_table_attach (GTK_TABLE (table), label, 0, 1, row, row+1,
+                    (GtkAttachOptions) (GTK_FILL),
+                    (GtkAttachOptions) (0), 0, 0);
+  gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
+
+
+  checkbutton = gtk_check_button_new_with_label (" ");
+  gimp_help_set_help_data (checkbutton, _("ON: enable vertical color blending. "
+                                          "OFF: disable vertical color blending."), NULL);
+  gtk_widget_show (checkbutton);
+  gtk_table_attach( GTK_TABLE(table), checkbutton, 1, 2, row, row+1,
+                    GTK_FILL, 0, 0, 0 );
+  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (checkbutton), fiVals->verticalBlendFlag);
+  g_signal_connect (checkbutton, "toggled",
+                    G_CALLBACK (on_gboolean_button_update),
+                    &fiVals->verticalBlendFlag);
+
+  row++;
+
+  adj = gimp_scale_entry_new (GTK_TABLE (table), 0, row,
+                              _("Border Radius:"), SCALE_MIN_WIDTH, SPINBUTTON_MIN_WIDTH,
+                              fiVals->borderRadius, 1.0, 10.0, 1.0, 10.0, 0,
+                              TRUE, 0, 0,
+                              _("radius for picking border colors"),
+                              NULL /* help_id */
+                              );
+  g_signal_connect (adj, "value-changed",
+                    G_CALLBACK (gimp_int_adjustment_update),
+                    &fiVals->borderRadius);
+
+  row++;
+
+
+  /* layer combo_box (altSelection) */
+  label = gtk_label_new (_("Set Selection:"));
+  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);
+
+  /* layer combo_box (Sample from where to pick the alternative selection */
+  combo = gimp_layer_combo_box_new (p_selectionConstraintFunc, NULL);
+  g_object_set_data (G_OBJECT (combo), "guiStuffPtr", guiStuffPtr);
+  gimp_int_combo_box_prepend (GIMP_INT_COMBO_BOX (combo),
+                              GIMP_INT_STORE_VALUE,    SELECTION_FROM_VECTORS,
+                              GIMP_INT_STORE_LABEL,    _("Selection From All Paths"),
+                              GIMP_INT_STORE_STOCK_ID, GIMP_STOCK_PATHS,
+                              -1);
+  gimp_int_combo_box_prepend (GIMP_INT_COMBO_BOX (combo),
+                              GIMP_INT_STORE_VALUE,    SELECTION_FROM_SVG_FILE,
+                              GIMP_INT_STORE_LABEL,    _("Selection From Vectors File"),
+                              GIMP_INT_STORE_STOCK_ID, GIMP_STOCK_PATH,
+                              -1);
+
+  initalComboElem = drawable_id;
+  if (fiVals->altSelection == SELECTION_FROM_VECTORS)
+  {
+    initalComboElem = SELECTION_FROM_VECTORS;
+  }
+  else if(fiVals->altSelection == SELECTION_FROM_SVG_FILE)
+  {
+    initalComboElem = SELECTION_FROM_SVG_FILE;
+  }
+  else if(gimp_drawable_is_valid(fiVals->altSelection) == TRUE)
+  {
+    initalComboElem = fiVals->altSelection;
+  }
+  gimp_int_combo_box_connect (GIMP_INT_COMBO_BOX (combo), initalComboElem,
+                              G_CALLBACK (p_selectionComboCallback),
+                              &fiVals->altSelection);
+  gimp_int_combo_box_set_active (GIMP_INT_COMBO_BOX (combo), initalComboElem);
+
+  gtk_table_attach (GTK_TABLE (table), combo, 1, 3, row, row + 1,
+                    GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0);
+  gtk_widget_show (combo);
+
+  row++;
+
+
+  /* grab vectors button */
+  button = gtk_button_new_with_label (_("Save Paths"));
+  gtk_widget_show (button);
+  gtk_table_attach (GTK_TABLE (table), button, 0, 1, row, row + 1,
+                    GTK_FILL, GTK_FILL, 4, 0);
+
+  gimp_help_set_help_data (button, _("Save all pathes as svg vector file."
+                          "(use svg file when large or many pathes shall be used)"), NULL);
+  g_signal_connect (G_OBJECT (button), "clicked",
+                      G_CALLBACK (on_save_svg_clicked),
+                      guiStuffPtr);
+
+  /* the (output) video entry */
+  entry = gtk_entry_new ();
+  guiStuffPtr->svg_entry = entry;
+  gtk_widget_show (entry);
+  gtk_table_attach (GTK_TABLE (table), entry, 1, 2, row, row + 1,
+                    GTK_FILL, GTK_FILL, 4, 0);
+  gimp_help_set_help_data (entry, _("Name of SVG vector file"), NULL);
+  if(strncmp("<?xml", guiStuffPtr->valPtr->selectionSVGFileName, 3) == 0)
+  {
+    g_snprintf(guiStuffPtr->valPtr->selectionSVGFileName
+             , sizeof(guiStuffPtr->valPtr->selectionSVGFileName), "%s"
+             , _("selection.svg"));
+  }
+  gtk_entry_set_text(GTK_ENTRY (entry), guiStuffPtr->valPtr->selectionSVGFileName);
+  g_signal_connect (G_OBJECT (entry), "changed",
+                      G_CALLBACK (on_svg_entry_changed),
+                      guiStuffPtr);
+
+
+
+  button = gtk_button_new_with_label (_("..."));
+  gtk_widget_set_size_request (button, BUTTON_MIN_WIDTH, -1);
+  gtk_widget_show (button);
+  gtk_table_attach (GTK_TABLE (table), button, 2, 3, row, row + 1,
+                    GTK_FILL, GTK_FILL, 4, 0);
+
+  gimp_help_set_help_data (button, _("Select output svg vector file via browser"), NULL);
+  g_signal_connect (G_OBJECT (button), "clicked",
+                      G_CALLBACK (on_filesel_button_clicked),
+                      guiStuffPtr);
+
+
+  row++;
+  label = gtk_label_new (" ");
+  guiStuffPtr->msg_label = label;
+  gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
+  gtk_table_attach (GTK_TABLE (table), label, 0, 3, row, row + 1,
+                    GTK_FILL, GTK_FILL, 4, 0);
+  gtk_widget_show (label);
+
+
+  p_check_exec_condition_and_set_ok_sesitivity(guiStuffPtr);
+
+  /* Done */
+
+  gtk_widget_show (dialog);
+
+  run = (gimp_dialog_run (GIMP_DIALOG (dialog)) == GTK_RESPONSE_OK);
+
+  if((run) && (fiVals->altSelection == SELECTION_FROM_VECTORS))
+  {
+    p_save_vectors_to_string(guiStuffPtr);
+  }
+
+  gtk_widget_destroy (dialog);
+
+  if(gap_debug)
+  {
+    printf("guiStuffPtr.2 Svg:%s\n", guiStuffPtr->valPtr->selectionSVGFileName);
+  }
+
+  return run;
+}  /* end gap_blend_fill_dialog */
diff --git a/gap/gap_colordiff.c b/gap/gap_colordiff.c
index ddd2139..963cccb 100644
--- a/gap/gap_colordiff.c
+++ b/gap/gap_colordiff.c
@@ -361,3 +361,125 @@ gap_colordiff_simple_guchar(guchar *aPixelPtr
   return (colorDiff);
 
 }  /* end gap_colordiff_simple_guchar */
+
+
+
+/* ---------------------------------
+ * gap_colordiff_hvmax_GimpHSV
+ * ---------------------------------
+ * returns difference of 2 colors as gdouble value
+ * in range 0.0 (exact match) to 1.0 (maximal difference)
+ */
+gdouble
+gap_colordiff_hvmax_GimpHSV(GimpHSV *aHsvPtr
+                  , GimpHSV *bHsvPtr
+                  , gboolean debugPrint)
+{
+  gdouble colorDiff;
+  gdouble hDif;
+  gdouble h2Dif;
+  gdouble sDif;
+  gdouble s2Dif;
+  gdouble vDif;
+  gdouble v2Dif;
+  gdouble vMax;
+  gdouble sMax;
+
+
+  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);
+  
+  sMax = MAX(aHsvPtr->s, bHsvPtr->s);
+  vMax = MAX(aHsvPtr->v, bHsvPtr->v);
+  
+  /* increase hue weight when comparing well saturated colors */
+  h2Dif = MIN(1.0, ((4.0 * sMax) * hDif));
+  v2Dif = vDif * vDif;
+  s2Dif = sDif * sDif * sDif;
+
+  colorDiff = MAX(MAX(v2Dif, h2Dif),s2Dif);
+
+
+
+  if(debugPrint)
+  {
+    printf("max 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:  (h2:%.3f h:%.3f s2:%.3f s:%.3f v2:%.3f v:%.3f)  max colorDiff:%.5f\n"
+       , h2Dif
+       , hDif
+       , s2Dif
+       , sDif
+       , v2Dif
+       , vDif
+       , colorDiff
+       );
+  }
+
+  return (colorDiff);
+
+}  /* end gap_colordiff_hvmax_GimpHSV */
+
+
+
+
+/* ---------------------------------
+ * gap_colordiff_hvmax_guchar
+ * ---------------------------------
+ * returns difference of 2 colors as gdouble value
+ * in range 0.0 (exact match) to 1.0 (maximal difference)
+ * Note: 
+ * this procedure uses an HSV colormodel based
+ * Algorithm and calculates difference as max HSV difference.
+ *
+ */
+gdouble
+gap_colordiff_hvmax_guchar(guchar *aPixelPtr
+                   , guchar *bPixelPtr
+                   , gboolean debugPrint
+                   )
+{
+  GimpRGB aRgb;
+  GimpRGB bRgb;
+  GimpHSV aHsv;
+  GimpHSV bHsv;
+
+  gimp_rgba_set_uchar (&aRgb, aPixelPtr[0], aPixelPtr[1], aPixelPtr[2], 255);
+  gimp_rgba_set_uchar (&bRgb, bPixelPtr[0], bPixelPtr[1], bPixelPtr[2], 255);
+
+  gimp_rgb_to_hsv(&aRgb, &aHsv);
+  gimp_rgb_to_hsv(&bRgb, &bHsv);
+
+  return (gap_colordiff_hvmax_GimpHSV(&aHsv
+                            , &bHsv
+                            , debugPrint
+                            ));
+
+}  /* end gap_colordiff_hvmax_guchar */
+
diff --git a/gap/gap_colordiff.h b/gap/gap_colordiff.h
index e0c6de5..755414d 100644
--- a/gap/gap_colordiff.h
+++ b/gap/gap_colordiff.h
@@ -139,4 +139,37 @@ gap_colordiff_simple_guchar(guchar *aPixelPtr
                    );
 
 
+
+
+
+
+/* ---------------------------------
+ * gap_colordiff_hvmax_guchar
+ * ---------------------------------
+ * returns difference of 2 colors as gdouble value
+ * in range 0.0 (exact match) to 1.0 (maximal difference)
+ * Note: 
+ * this procedure uses an HSV colormodel based
+ * Algorithm and calculates difference as max HSV difference.
+ *
+ */
+gdouble
+gap_colordiff_hvmax_guchar(guchar *aPixelPtr
+                   , guchar *bPixelPtr
+                   , gboolean debugPrint
+                   );
+
+
+/* ---------------------------------
+ * gap_colordiff_hvmax_GimpHSV
+ * ---------------------------------
+ * returns difference of 2 colors as gdouble value
+ * in range 0.0 (exact match) to 1.0 (maximal difference)
+ */
+gdouble
+gap_colordiff_hvmax_GimpHSV(GimpHSV *aHsvPtr
+                  , GimpHSV *bHsvPtr
+                  , gboolean debugPrint);
+                  
+
 #endif
diff --git a/gap/gap_detail_align_exec.c b/gap/gap_detail_align_exec.c
new file mode 100644
index 0000000..49fd2d2
--- /dev/null
+++ b/gap/gap_detail_align_exec.c
@@ -0,0 +1,821 @@
+/*  gap_detail_align_exec.c
+ *    This filter locates the position of a small 
+ *    outside the selction into the selected area.
+ *    It was implemented for fixing small pixel defects of my video camera sensor
+ *    and is intended to be used as filter when processing video frames
+ *    that are shot by such faulty cameras and typically runs
+ *    as filtermacro. Therefore the selection can be provided via an external image
+ *    or as path vectors via an extrernal SVG file.
+ *
+ *  2011/12/01
+ */
+/* 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
+ *  (2011/12/01)  2.7.0       hof: created
+ */
+extern int gap_debug;
+
+#include "config.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <glib.h>
+#include <glib/gstdio.h>
+
+#include <gtk/gtk.h>
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "gap_base.h"
+#include "gap_libgapbase.h"
+#include "gap_detail_align_exec.h"
+
+#include "gap-intl.h"
+
+
+typedef struct PixelCoords
+{
+  gboolean  valid;
+  gint32  px;
+  gint32  py;
+} PixelCoords;
+
+
+typedef struct AlingCoords
+{
+  PixelCoords  currCoords;   /* 1st coords in current frame */
+  PixelCoords  currCoords2;  /* 2nd detail coords in current frame */
+  PixelCoords  startCoords;  /* 1st coords of first processed (reference) frame */
+  PixelCoords  startCoords2; /* 2nd detail coords of first processed frame */
+} AlingCoords;
+
+
+typedef struct ParseContext {
+  char  *parsePtr;
+  gint32 frameNr;
+  AlingCoords *alingCoords;
+} ParseContext;
+
+
+
+#define DEFAULT_framePhase 1
+
+
+/* --------------------------------------
+ * p_parse_value_gint32
+ * --------------------------------------
+ */
+static gboolean
+p_parse_value_gint32(ParseContext *parseCtx, gint32 *valDestPtr, gint *itemCount)
+{
+  gint64 value64;
+  gchar *endptr;
+  
+  value64 = g_ascii_strtoll(parseCtx->parsePtr, &endptr, 10);
+  if(parseCtx->parsePtr == endptr)
+  {
+    /* pointer was not advanced (no int number could be scanned */
+    return (FALSE);
+  }
+  *valDestPtr = value64;
+  parseCtx->parsePtr = endptr;
+  *itemCount +=1;
+  return (TRUE);
+
+}  /* end p_parse_value_gint32 */
+
+
+/* --------------------------------
+ * p_parse_coords_p1_and_p2
+ * --------------------------------
+ * parse p1x, p1y, p2x, p2y values into p1 (mandatory) and p2 (ptional) coordinates
+ * and parse keyframe_abs value int *frameNrPtr (optional if present)
+ * multiple occurances are not tolerated.
+ * return TRUE on success.
+ */
+static gboolean
+p_parse_coords_p1_and_p2(ParseContext *parseCtx, PixelCoords *p1, PixelCoords *p2, gint32 *frameNrPtr)
+{
+  gboolean ok;
+  gint     px1Count;
+  gint     py1Count;
+  gint     px2Count;
+  gint     py2Count;
+  gint     frCount;
+  
+  ok = TRUE;
+  px1Count = 0;
+  py1Count = 0;
+  px2Count = 0;
+  py2Count = 0;
+  frCount  = 0;
+  while(*parseCtx->parsePtr != '\0')
+  {
+    if (strncmp(parseCtx->parsePtr, "keyframe_abs=\"", strlen("keyframe_abs=\"")) == 0)
+    {
+      parseCtx->parsePtr += strlen("keyframe_abs=\"");
+      ok = p_parse_value_gint32(parseCtx, frameNrPtr, &frCount);
+    }
+    else if (strncmp(parseCtx->parsePtr, "p1x=\"", strlen("p1x=\"")) == 0)
+    {
+      parseCtx->parsePtr += strlen("p1x=\"");
+      ok = p_parse_value_gint32(parseCtx, &p1->px, &px1Count);
+    }
+    else if (strncmp(parseCtx->parsePtr, "p1y=\"", strlen("p1y=\"")) == 0)
+    {
+      parseCtx->parsePtr += strlen("p1y=\"");
+      ok = p_parse_value_gint32(parseCtx, &p1->py, &py1Count);
+    }
+    else if (strncmp(parseCtx->parsePtr, "p2x=\"", strlen("p2x=\"")) == 0)
+    {
+      parseCtx->parsePtr += strlen("p2x=\"");
+      ok = p_parse_value_gint32(parseCtx, &p2->px, &px2Count);
+    }
+    else if (strncmp(parseCtx->parsePtr, "p2y=\"", strlen("p2y=\"")) == 0)
+    {
+      parseCtx->parsePtr += strlen("p2y=\"");
+      ok = p_parse_value_gint32(parseCtx, &p2->py, &py2Count);
+    }
+    else if (strncmp(parseCtx->parsePtr, "/>", strlen("/>")) == 0)
+    {
+      /* stop evaluate when current controlpoint ends */
+      parseCtx->parsePtr += strlen("/>");
+      break;
+    }
+    else if (strncmp(parseCtx->parsePtr, "<controlpoint ", strlen("<controlpoint ")) == 0)
+    {
+      /* stop evaluate when when we run into the next controlpoint */
+      break;
+    }
+    else
+    {
+      parseCtx->parsePtr++;
+    }
+    
+    if(ok != TRUE)
+    {
+     break;
+    }
+  }
+
+  if ((ok == TRUE)
+  && (px1Count == 1)
+  && (py1Count == 1)
+  && (frCount <= 1))
+  {
+    p1->valid = TRUE;
+    if ((px2Count == 1)
+    &&  (py2Count == 1))
+    {
+      p2->valid = TRUE;
+    }
+
+    return (TRUE);
+  }
+
+
+  
+  if(gap_debug)
+  {
+    printf("p_parse_coords_p1_and_p2 ok:%d px1Count:%d py1Count:%d px2Count:%d py2Count:%d frCount:%d\n"
+           " parsePtr:%.200s\n" 
+      ,(int)ok
+      ,(int)px1Count
+      ,(int)py1Count
+      ,(int)px2Count
+      ,(int)py2Count
+      ,(int)frCount
+      ,parseCtx->parsePtr 
+      );
+  }
+  return (FALSE);
+  
+}  /* end p_parse_coords_p1_and_p2 */
+
+
+static void
+p_find_attribute_start(ParseContext *parseCtx)
+{
+  while(*parseCtx->parsePtr != '\0')
+  {
+    if(*parseCtx->parsePtr == '<')
+    {
+      break;
+    }
+    parseCtx->parsePtr++;
+  }
+  
+  if(gap_debug)
+  {
+    printf("\nSTRING at parsePtr:%.200s\n"
+      , parseCtx->parsePtr
+      );
+  }
+}   /* end p_find_attribute_start */
+
+
+/* --------------------------------
+ * p_parse_xml_controlpoint_coords
+ * --------------------------------
+ *
+ */
+static gboolean
+p_parse_xml_controlpoint_coords(ParseContext *parseCtx)
+{
+  gboolean     ok;
+  gint32       frameNr;
+
+  ok = TRUE;
+  frameNr = -1;
+  
+  while(*parseCtx->parsePtr != '\0')
+  {
+    p_find_attribute_start(parseCtx);
+
+    if (strncmp(parseCtx->parsePtr, "<controlpoint ", strlen("<controlpoint ")) == 0)
+    {
+      parseCtx->parsePtr += strlen("<controlpoint ");
+      
+      if(parseCtx->alingCoords->startCoords.valid == FALSE)
+      {
+        ok = p_parse_coords_p1_and_p2(parseCtx
+                               , &parseCtx->alingCoords->startCoords
+                               , &parseCtx->alingCoords->startCoords2
+                               , &frameNr
+                               );
+      }
+      else
+      {
+        ok = p_parse_coords_p1_and_p2(parseCtx
+                               , &parseCtx->alingCoords->currCoords
+                               , &parseCtx->alingCoords->currCoords2
+                               , &frameNr
+                               );
+      }
+
+      if(gap_debug)
+      {
+        printf("p_parse_xml_controlpoint_coords: ok:%d, frameNr:%d parseCtx->frameNr:%d\n"
+          ,(int)ok
+          ,(int)frameNr
+          ,(int)parseCtx->frameNr
+          );
+      }
+
+      if(ok != TRUE)
+      {
+        return (FALSE);
+      }
+
+      if ((frameNr == parseCtx->frameNr)
+      &&  (parseCtx->alingCoords->currCoords2.valid == TRUE))
+      {
+        return(TRUE);
+      }
+    }
+    parseCtx->parsePtr++;
+  }
+
+
+  if ((ok == TRUE)
+  &&  (parseCtx->alingCoords->currCoords2.valid == TRUE))
+  {
+    /* accept the last controlpoint when no matching frameNr was found */
+    return(TRUE);
+  }
+  
+  return(FALSE);
+
+}  /* end p_parse_xml_controlpoint_coords */
+
+
+/* -----------------------------------------
+ * p_parse_xml_controlpoint_coords_from_file
+ * -----------------------------------------
+ * parse coords of 1st controlpoint and the controlpoint at frameNr
+ * from the xml file.
+ * return TRUE on success.
+ */
+static gboolean
+p_parse_xml_controlpoint_coords_from_file(const char *filename, gint32 frameNr, AlingCoords *alingCoords)
+{
+  char *textBuffer;
+  gsize lengthTextBuffer;
+  
+  ParseContext parseCtx;
+  gboolean     parseOk;
+  
+  textBuffer = NULL;
+  parseOk = FALSE;
+  if ((g_file_get_contents (filename, &textBuffer, &lengthTextBuffer, NULL) != TRUE) 
+  || (textBuffer == NULL))
+  {
+    printf("Couldn't load XML file:%s\n", filename);
+    return(FALSE);
+  }
+
+  parseCtx.parsePtr = textBuffer;
+  parseCtx.alingCoords = alingCoords;
+  parseCtx.frameNr = frameNr;
+
+  parseCtx.alingCoords->startCoords.valid  = FALSE;
+  parseCtx.alingCoords->startCoords2.valid = FALSE;
+  parseCtx.alingCoords->currCoords.valid   = FALSE;
+  parseCtx.alingCoords->currCoords2.valid  = FALSE;
+  
+  parseOk = p_parse_xml_controlpoint_coords(&parseCtx);
+    
+    
+  g_free(textBuffer);
+  
+  return (parseOk);
+
+}  /* end p_parse_xml_controlpoint_coords_from_file */
+
+
+/* -----------------------------------
+ * p_set_drawable_offsets
+ * -----------------------------------
+ * simple 2-point align via offsets (without rotate and scale)
+ */
+static gint32
+p_set_drawable_offsets(gint32 activeDrawableId, AlingCoords *alingCoords)
+{
+  gdouble px1, py1, px2, py2;
+  gdouble dx, dy;
+  gint    offset_x;
+  gint    offset_y;
+  
+
+  px1 = alingCoords->startCoords.px;
+  py1 = alingCoords->startCoords.py;
+  px2 = alingCoords->currCoords.px;
+  py2 = alingCoords->currCoords.py;
+
+  dx = px2 - px1;
+  dy = py2 - py1;
+
+  /* findout the offsets of the original layer within the source Image */
+  gimp_drawable_offsets(activeDrawableId, &offset_x, &offset_y );
+  gimp_layer_set_offsets(activeDrawableId, offset_x - dx, offset_y - dy);
+  
+  return (activeDrawableId);
+
+}  /* end p_set_drawable_offsets */
+
+
+/* -----------------------------------
+ * p_exact_align_drawable
+ * -----------------------------------
+ * 4-point alignment including necessary scale and rotate transformation
+ * to match 2 pairs of corresponding coordonates.
+ */
+static gint32
+p_exact_align_drawable(gint32 activeDrawableId, AlingCoords *alingCoords)
+{
+  gdouble px1, py1, px2, py2;
+  gdouble px3, py3, px4, py4;
+  gdouble dx1, dy1, dx2, dy2;
+  gdouble angle1Rad, angle2Rad, angleRad;
+  gdouble len1, len2;
+  gdouble scaleXY;
+  gint32  transformedDrawableId;
+  
+  px1 = alingCoords->startCoords.px;
+  py1 = alingCoords->startCoords.py;
+  px2 = alingCoords->startCoords2.px;
+  py2 = alingCoords->startCoords2.py;
+  
+  px3 = alingCoords->currCoords.px;
+  py3 = alingCoords->currCoords.py;
+  px4 = alingCoords->currCoords2.px;
+  py4 = alingCoords->currCoords2.py;
+
+  dx1 = px2 - px1;
+  dy1 = py2 - py1;
+  dx2 = px4 - px3;
+  dy2 = py4 - py3;
+  
+  /* the angle between the two lines. i.e., the angle layer2 must be clockwise rotatet
+   * in order to overlap with initial start layer1
+   */
+  angle1Rad = 0;
+  angle2Rad = 0;
+  if (dx1 != 0.0)
+  {
+    angle1Rad = atan(dy1 / dx1);
+  }
+  if (dx2 != 0.0)
+  {
+    angle2Rad = atan(dy2 / dx2);
+  }
+  angleRad = angle1Rad - angle2Rad;
+  
+  /* the scale factors current layer must be mulitplied by,
+   * in order to fit onto reference start layer.
+   * this is simply the ratio of the two line lenths from the path we created with the 4 points
+   */
+  
+  len1 = sqrt((dx1 * dx1) + (dy1 * dy1));
+  len2 = sqrt((dx2 * dx2) + (dy2 * dy2));
+
+  scaleXY = 1.0;
+  if ((len1 != len2)
+  &&  (len2 != 0.0))
+  {
+    scaleXY = len1 / len2;
+  }
+
+  transformedDrawableId = gimp_drawable_transform_2d(activeDrawableId
+                            , px3
+                            , py3
+                            , scaleXY 
+                            , scaleXY
+                            , angleRad
+                            , px1
+                            , py1
+                            , 0      /* FORWARD (0), TRANSFORM-BACKWARD (1) */
+                            , 2      /* INTERPOLATION-CUBIC (2) */
+                            , TRUE   /* supersample */
+                            , 1      /* Maximum recursion level used for supersampling */
+                            , 1      /* TRANSFORM-RESIZE-CLIP (1) */
+                            );
+
+  if(gap_debug)
+  {
+    printf("p_exact_align_drawable: activeDrawableId:%d transformedDrawableId:%d\n"
+           "    p1: %f %f\n"
+           "    p2: %f %f\n"
+           "    p3: %f %f\n"
+           "    p4: %f %f\n"
+           "    scale:%f  angleRad:%f (degree:%f)\n"
+      ,(int)activeDrawableId
+      ,(int)transformedDrawableId
+      ,(float)px1
+      ,(float)py1
+      ,(float)px2
+      ,(float)py2
+      ,(float)px3
+      ,(float)py3
+      ,(float)px4
+      ,(float)py4
+      ,(float)scaleXY
+      ,(float)angleRad
+      ,(float)(angleRad * 180.0) / G_PI
+      );
+  }
+  return (transformedDrawableId);  
+  
+}  /* end p_exact_align_drawable */
+
+/* -----------------------------------
+ * gap_detail_xml_align
+ * -----------------------------------
+ * This procedure transforms the specified drawable
+ * according controlpoints in an XML file that was recorded via detail tracking.
+ * it picks the relevant controlpoint at framePhase from the XML file
+ * and scales, rotates and aligns the specified drawableId (shall be a layer)
+ * in a way that it exactly matches with the 1st (reference) controlpoint in the XML file.
+ *
+ * returns the drawable id of the resulting transformed layer (or -1 on errors)s
+ */
+gint32
+gap_detail_xml_align(gint32 drawableId, XmlAlignValues *xaVals)
+{
+  gint32 newDrawableId;
+  
+  newDrawableId = drawableId;
+  if(gap_debug)
+  {
+      printf("gap_detail_xml_align: START\n"
+             "  framePhase:%d  moveLogFile:%s\n"
+            , (int)xaVals->framePhase
+             , xaVals->moveLogFile
+            );
+  }
+  
+  if(xaVals->framePhase > 1)
+  {
+    gboolean  parseOk;
+    AlingCoords alingCoords;
+    
+    parseOk =
+      p_parse_xml_controlpoint_coords_from_file(xaVals->moveLogFile
+         , xaVals->framePhase, &alingCoords);
+
+    if(parseOk)
+    {
+      if ((alingCoords.startCoords2.valid == TRUE)
+      &&  (alingCoords.currCoords2.valid == TRUE))
+      {
+        /* exact align transformation with 2 point pairs including rotation and scaling */
+        newDrawableId = p_exact_align_drawable(drawableId, &alingCoords);
+      }
+      else
+      {
+        /* simple move (to match current recorded point to recorded start point) */
+        newDrawableId = p_set_drawable_offsets(drawableId, &alingCoords);
+      }
+    }
+    else
+    {
+      newDrawableId = -1;
+    }
+
+  }
+ 
+  return(newDrawableId);
+  
+}  /* end gap_detail_xml_align */
+
+
+
+
+/* -----------------------------------
+ * gap_detail_xml_align_get_values
+ * -----------------------------------
+ * This procedure is typically called
+ * on the snapshot image created by the Player.
+ * This image has one layer at the first snapshot
+ * and each further snapshot adds one layer on top of the layerstack.
+ *
+ * The start is detected when the image has only one layer.
+ * optionally the numer of layers can be limted
+ * to 2 (or more) layers.
+ */
+void
+gap_detail_xml_align_get_values(XmlAlignValues *xaVals)
+{
+  int l_len;
+
+  /* init default values */
+  xaVals->framePhase                 = DEFAULT_framePhase;
+  xaVals->moveLogFile[0]             = '\0';
+
+  l_len = gimp_get_data_size (GAP_DETAIL_TRACKING_XML_ALIGNER_PLUG_IN_NAME);
+  if (l_len == sizeof(XmlAlignValues))
+  {
+    /* Possibly retrieve data from a previous interactive run */
+    gimp_get_data (GAP_DETAIL_TRACKING_XML_ALIGNER_PLUG_IN_NAME, xaVals);
+    
+    if(gap_debug)
+    {
+      printf("gap_detail_xml_align_get_values FOUND data for key:%s\n"
+        , GAP_DETAIL_TRACKING_XML_ALIGNER_PLUG_IN_NAME
+        );
+    }
+  }
+
+  if(gap_debug)
+  {
+    printf("gap_detail_xml_align_get_values:\n"
+           "  framePhase:%d  moveLogFile:%s\n"
+            , (int)xaVals->framePhase
+            , xaVals->moveLogFile
+            );
+  }
+  
+}  /* end gap_detail_xml_align_get_values */
+
+
+
+/* ---------------------------
+ * gap_detail_xml_align_dialog
+ * ---------------------------
+ *   return  TRUE.. OK
+ *           FALSE.. in case of Error or cancel
+ */
+gboolean
+gap_detail_xml_align_dialog(XmlAlignValues *xaVals)
+{
+#define SPINBUTTON_ENTRY_WIDTH 70
+#define DETAIL_ALIGN_XML_DIALOG_ARGC 3
+
+  static GapArrArg  argv[DETAIL_ALIGN_XML_DIALOG_ARGC];
+  gint ii;
+  gint ii_framePhase;
+
+  ii=0; gap_arr_arg_init(&argv[ii], GAP_ARR_WGT_INT_PAIR); ii_framePhase = ii;
+  argv[ii].label_txt = _("Frame Phase:");
+  argv[ii].help_txt  = _("Frame number (phase) to be rendered.");
+  argv[ii].constraint = FALSE;
+  argv[ii].int_min   = 1;
+  argv[ii].int_max   = 9999;
+  argv[ii].umin      = 1;
+  argv[ii].umax      = 999999;
+  argv[ii].int_ret   = xaVals->framePhase;
+  argv[ii].entry_width = SPINBUTTON_ENTRY_WIDTH;
+  argv[ii].has_default = TRUE;
+  argv[ii].int_default = DEFAULT_framePhase;
+
+
+  ii++; gap_arr_arg_init(&argv[ii], GAP_ARR_WGT_FILESEL);
+  argv[ii].label_txt = _("XML file:");
+  argv[ii].help_txt  = _("Name of the xml file that contains the tracked detail coordinates. "
+                        " (recorded with the detail tracking feature).");
+  argv[ii].text_buf_len = sizeof(xaVals->moveLogFile);
+  argv[ii].text_buf_ret = &xaVals->moveLogFile[0];
+  argv[ii].entry_width = 400;
+
+
+
+  ii++; gap_arr_arg_init(&argv[ii], GAP_ARR_WGT_DEFAULT_BUTTON);
+  argv[ii].label_txt = _("Default");
+  argv[ii].help_txt  = _("Reset all parameters to default values");
+
+  if(TRUE == gap_arr_ok_cancel_dialog(_("Detail Align via XML"),
+                            _("Settings :"),
+                            DETAIL_ALIGN_XML_DIALOG_ARGC, argv))
+  {
+      xaVals->framePhase               = (gint32)(argv[ii_framePhase].int_ret);
+      
+      gimp_set_data (GAP_DETAIL_TRACKING_XML_ALIGNER_PLUG_IN_NAME
+                    , xaVals, sizeof (XmlAlignValues));
+      return TRUE;
+  }
+  else
+  {
+      return FALSE;
+  }
+}               /* end gap_detail_xml_align_dialog */
+
+
+
+/* ---------------------------------- */
+/* ---------------------------------- */
+/* ---------------------------------- */
+/* ---------------------------------- */
+/* ---------------------------------- */
+/* ---------------------------------- */
+
+
+
+/* ------------------------------------------
+ * p_capture_4_vector_points
+ * ------------------------------------------
+ * capture the first 4 points of the 1st stroke in the active path vectors
+ */
+static gint
+p_capture_4_vector_points(gint32 imageId, AlingCoords *alingCoords)
+{
+  gint32  activeVectorsId;
+  PixelCoords *coordPtr[4];
+  gint         ii;
+  gint         countVaildPoints;
+  
+  
+  coordPtr[0] = &alingCoords->startCoords;
+  coordPtr[1] = &alingCoords->startCoords2;
+  coordPtr[2] = &alingCoords->currCoords;
+  coordPtr[3] = &alingCoords->currCoords2;
+  
+  countVaildPoints = 0;
+  for(ii=0; ii < 4; ii++)
+  {
+    coordPtr[ii]->px = -1;
+    coordPtr[ii]->py = -1;
+    coordPtr[ii]->valid = FALSE;
+
+  }
+
+  activeVectorsId = gimp_image_get_active_vectors(imageId);
+  if(activeVectorsId >= 0)
+  {
+    gint num_strokes;
+    gint *strokes;
+ 
+    strokes = gimp_vectors_get_strokes (activeVectorsId, &num_strokes);
+    if(strokes)
+    {
+      if(num_strokes > 0)
+      {
+        gdouble  *points;
+        gint      num_points;
+        gboolean  closed;
+        GimpVectorsStrokeType type;
+       
+        points = NULL;
+        type = gimp_vectors_stroke_get_points(activeVectorsId, strokes[0],
+                                   &num_points, &points, &closed);
+
+        if(gap_debug)
+        {
+          gint ii;
+          for(ii=0; ii < MIN(24, num_points); ii++)
+          {
+            printf ("point[%d] = %.3f\n", ii, points[ii]);
+          }
+        }
+        
+        if (type == GIMP_VECTORS_STROKE_TYPE_BEZIER)
+        {
+          if(num_points >= 6)
+          {
+            coordPtr[0]->px = points[0];
+            coordPtr[0]->py = points[1];
+            coordPtr[0]->valid = TRUE;
+            countVaildPoints++;
+          }
+          if(num_points >= 12)
+          {
+            coordPtr[1]->px = points[6];
+            coordPtr[1]->py = points[7];
+            coordPtr[1]->valid = TRUE;
+            countVaildPoints++;
+          }
+          if(num_points >= 18)
+          {
+            coordPtr[2]->px = points[12];
+            coordPtr[2]->py = points[13];
+            coordPtr[2]->valid = TRUE;
+            countVaildPoints++;
+          }
+          if(num_points >= 24)
+          {
+            coordPtr[3]->px = points[18];
+            coordPtr[3]->py = points[19];
+            coordPtr[3]->valid = TRUE;
+            countVaildPoints++;
+          }
+        }
+        if(points)
+        {
+          g_free(points);
+        }
+      
+      }
+      g_free(strokes);
+    }
+
+  }
+ 
+  return(countVaildPoints);
+  
+}  /* end p_capture_4_vector_points */
+
+
+
+/* ------------------------------------------
+ * gap_detail_exact_align_via_4point_path
+ * ------------------------------------------
+ *
+ */
+gint32
+gap_detail_exact_align_via_4point_path(gint32 image_id, gint32 activeDrawableId)
+{
+  AlingCoords  alingCoordinates;
+  AlingCoords *alingCoords;
+  gint         countVaildPoints;
+  gint32       ret;
+  
+  alingCoords = &alingCoordinates;
+  ret = -1;
+  
+  countVaildPoints = p_capture_4_vector_points(image_id, alingCoords);
+  if(countVaildPoints == 4)
+  {
+    ret = p_exact_align_drawable(activeDrawableId, alingCoords);
+
+  }
+  else if(countVaildPoints == 2)
+  {
+    alingCoords->currCoords.px = alingCoords->startCoords2.px;
+    alingCoords->currCoords.py = alingCoords->startCoords2.py;
+    alingCoords->currCoords.valid = alingCoords->startCoords2.valid;
+    ret = p_set_drawable_offsets(activeDrawableId, alingCoords);
+  }
+  else
+  {
+    g_message(_("This filter requires a current path with 4 points,"
+                "where point 1 and 2 mark reference positions "
+                "and point 3 and 4 mark postions in the target layer."
+                "It transforms the target layer in a way that "
+                "point3 is moved to point1 and point4 moves to point2."
+                "(this may include rotate an scale transforamtion).\n"
+                "A path with 2 points can be used to move point2 to point1."
+                "(via simple move operation without rotate and scale)"));
+  }
+  
+  return(ret);
+  
+  
+}  /* end  gap_detail_exact_align_via_4point_path */
+
diff --git a/gap/gap_detail_align_exec.h b/gap/gap_detail_align_exec.h
new file mode 100644
index 0000000..702bd9a
--- /dev/null
+++ b/gap/gap_detail_align_exec.h
@@ -0,0 +1,74 @@
+/*  gap_detail_align_exec.h
+ *    This filter locates the position of a small 
+ *    outside the selction into the selected area.
+ *    It was implemented for fixing small pixel defects of my video camera sensor
+ *    and is intended to be used as filter when processing video frames
+ *    that are shot by such faulty cameras and typically runs
+ *    as filtermacro. Therefore the selection can be provided via an external image
+ *    or as path vectors via an extrernal SVG file.
+ *
+ *  2011/12/01
+ */
+/* 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
+ *  (2011/12/01)  2.7.0       hof: created
+ */
+
+#ifndef _GAP_DETAIL_ALIGN_EXEC_H
+#define _GAP_DETAIL_ALIGN_EXEC_H
+
+
+#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_libgapbase.h"
+#include "gap_image.h"
+#include "gap_layer_copy.h"
+#include "gap_arr_dialog.h"
+
+#include "gap-intl.h"
+
+
+#define GAP_DETAIL_TRACKING_XML_ALIGNER_PLUG_IN_NAME      "gap-detail-tracking-xml-aligner"
+#define GAP_EXACT_ALIGNER_PLUG_IN_NAME                    "gap-exact-aligner"
+
+typedef struct XmlAlignValues {
+   gint32     framePhase;
+   char       moveLogFile[1600];
+} XmlAlignValues;
+
+
+
+gint32      gap_detail_xml_align(gint32 drawableId, XmlAlignValues *xaVals);
+void        gap_detail_xml_align_get_values(XmlAlignValues *xaVals);
+gboolean    gap_detail_xml_align_dialog(XmlAlignValues *xaVals);
+
+gint32      gap_detail_exact_align_via_4point_path(gint32 image_id, gint32 activeDrawableId);
+
+
+
+#endif
diff --git a/gap/gap_detail_tracking_exec.c b/gap/gap_detail_tracking_exec.c
new file mode 100644
index 0000000..2af7617
--- /dev/null
+++ b/gap/gap_detail_tracking_exec.c
@@ -0,0 +1,1530 @@
+/*  gap_detail_tracking_exec.c
+ *    This filter locates the position of one or 2 small areas
+ *    of a reference layer within a target layer and logs the coordinates
+ *    as XML file. It is intended to track details in a frame sequence.
+ *
+ *  2011/12/01
+ */
+/* 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
+ *  (2011/12/01)  2.7.0       hof: created
+ */
+extern int gap_debug;
+
+#include "config.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <glib.h>
+#include <glib/gstdio.h>
+
+#include <gtk/gtk.h>
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "gap_base.h"
+#include "gap_libgapbase.h"
+#include "gap_locate.h"
+#include "gap_locate2.h"
+#include "gap_colordiff.h"
+#include "gap_image.h"
+#include "gap_layer_copy.h"
+#include "gap_detail_tracking_exec.h"
+
+#include "gap-intl.h"
+
+#define DEFAULT_refShapeRadius            15
+#define DEFAULT_targetMoveRadius          70
+#define DEFAULT_loacteColodiffThreshold   0.08
+#define DEFAULT_coordsRelToFrame1         TRUE
+#define DEFAULT_offsX                     0
+#define DEFAULT_offsY                     0
+#define DEFAULT_offsRotate                0.0
+#define DEFAULT_enableScaling             TRUE
+#define DEFAULT_removeMidlayers           TRUE
+#define DEFAULT_bgLayerIsReference        TRUE
+
+/* -----------------------------------
+ * p_calculate_angle_in_degree
+ * -----------------------------------
+ * calculate angle of the line described by coordinates p1, p2
+ * returns the angle in degree.
+ */
+static gdouble
+p_calculate_angle_in_degree(gint p1x, gint p1y, gint p2x, gint p2y)
+{
+  /* calculate angle in degree
+   * how to rotate an object that follows the line between p1 and p2
+   */
+  gdouble l_a;
+  gdouble l_b;
+  gdouble l_angle_rad;
+  gdouble l_angle;
+
+  l_a = p2x - p1x;
+  l_b = (p2y - p1y) * (-1.0);
+
+  if(l_a == 0)
+  {
+    if(l_b < 0)  { l_angle = 90.0; }
+    else         { l_angle = 270.0; }
+  }
+  else
+  {
+    l_angle_rad = atan(l_b/l_a);
+    l_angle = (l_angle_rad * 180.0) / G_PI;
+
+    if(l_a < 0)
+    {
+      l_angle = 180 - l_angle;
+    }
+    else
+    {
+      l_angle = l_angle * (-1.0);
+    }
+  }
+
+  if(gap_debug)
+  {
+     printf("p_calc_angle: p1(%d/%d) p2(%d/%d)  a=%f, b=%f, angle=%f\n"
+         , (int)p1x, (int)p1y, (int)p2x, (int)p2y
+         , (float)l_a, (float)l_b, (float)l_angle);
+  }
+  return(l_angle);
+
+}  /* end p_calculate_angle_in_degree */
+
+
+/* -----------------------------------
+ * p_calculate_scale_factor
+ * -----------------------------------
+ * calculate angle of the line described by coordinates p1, p2
+ * returns the angle in degree.
+ */
+static gdouble
+p_calculate_scale_factor(gint p1x, gint p1y, gint p2x, gint p2y
+                       , gint p3x, gint p3y, gint p4x, gint p4y)
+{
+  /* calculate angle in degree
+   * how to rotate an object that follows the line between p1 and p2
+   */
+  gdouble l_a;
+  gdouble l_b;
+  gdouble scaleFactor;
+
+  scaleFactor = 1.0;
+
+  l_a = sqrt(((p2x - p1x) * (p2x - p1x)) + ((p2y - p1y) * (p2y - p1y)));
+  l_b = sqrt(((p4x - p3x) * (p4x - p3x)) + ((p4y - p3y) * (p4y - p3y)));
+
+  if ((l_a >= 0) &&(l_b >= 0))
+  {
+    scaleFactor = l_a / l_b;
+  }
+
+  return(scaleFactor);
+
+}  /* end p_calculate_scale_factor */
+
+
+
+
+/* ------------------------------------------
+ * p_capture_2_vector_points
+ * ------------------------------------------
+ * capture the first 2 points of the 1st stroke in the active path vectors
+ */
+static void
+p_capture_2_vector_points(gint32 imageId, PixelCoords *coordPtr, PixelCoords *coordPtr2) {
+  gint32  activeVectorsId;
+  gint32  gx1;
+  gint32  gy1;
+  gint32  gx2;
+  gint32  gy2;
+
+  gx1 = -1;
+  gy1 = -1;
+  gx2 = -1;
+  gy2 = -1;
+
+  activeVectorsId = gimp_image_get_active_vectors(imageId);
+  if(activeVectorsId >= 0)
+  {
+    gint num_strokes;
+    gint *strokes;
+
+    strokes = gimp_vectors_get_strokes (activeVectorsId, &num_strokes);
+    if(strokes)
+    {
+      if(num_strokes > 0)
+      {
+        gdouble  *points;
+        gint      num_points;
+        gboolean  closed;
+        GimpVectorsStrokeType type;
+
+        points = NULL;
+        type = gimp_vectors_stroke_get_points(activeVectorsId, strokes[0],
+                                   &num_points, &points, &closed);
+
+        if(gap_debug)
+        {
+          gint ii;
+          for(ii=0; ii < MIN(12, num_points); ii++)
+          {
+            printf ("point[%d] = %.3f\n", ii, points[ii]);
+          }
+        }
+
+        if (type == GIMP_VECTORS_STROKE_TYPE_BEZIER)
+        {
+          if(num_points >= 6)
+          {
+            gx1 = points[0];
+            gy1 = points[1];
+          }
+          if(num_points >= 12)
+          {
+            gx2 = points[6];
+            gy2 = points[7];
+          }
+        }
+        if(points)
+        {
+          g_free(points);
+        }
+
+      }
+      g_free(strokes);
+    }
+
+  }
+
+
+  coordPtr->valid = FALSE;
+  coordPtr2->valid = FALSE;
+
+  if((gx1 >= 0) && (gy1 >= 0))
+  {
+    if(gap_debug)
+    {
+      printf("\nPathPoints: x1=%d; y1=%d x2=%d; y2=%d\n"
+          , gx1
+          , gy1
+          , gx2
+          , gy2
+          );
+    }
+
+    coordPtr->px = gx1;
+    coordPtr->py = gy1;
+    coordPtr->valid = TRUE;
+
+    if((gx2 != gx1) || (gy2 != gy1))
+    {
+      coordPtr2->px = gx2;
+      coordPtr2->py = gy2;
+      coordPtr2->valid = TRUE;
+    }
+
+  }
+
+}  /* end p_capture_2_vector_points */
+
+
+/* ------------------------------------
+ * p_copy_src_to_dst_coords
+ * ------------------------------------
+ */
+static void
+p_copy_src_to_dst_coords(PixelCoords *srcCoords, PixelCoords *dstCoords)
+{
+  dstCoords->valid = srcCoords->valid;
+  dstCoords->px = srcCoords->px;
+  dstCoords->py = srcCoords->py;
+}
+
+/* ------------------------------------
+ * p_locate_target
+ * ------------------------------------
+ */
+static void
+p_locate_target(gint32 refLayerId, PixelCoords *refCoords
+   , gint32 targetLayerId, PixelCoords *targetCoords
+   , gint32 locateOffsetX, gint32 locateOffsetY
+   , FilterValues *valPtr, gint32 *lostTraceCount)
+{
+  gdouble colordiffLocate;
+  gint          ref_offset_x;
+  gint          ref_offset_y;
+  gint          target_offset_x;
+  gint          target_offset_y;
+  gint32        refX;
+  gint32        refY;
+  gint32        targetX;
+  gint32        targetY;
+
+
+  /* get offsets of the layers within the image */
+  gimp_drawable_offsets (refLayerId, &ref_offset_x, &ref_offset_y);
+  gimp_drawable_offsets (targetLayerId, &target_offset_x, &target_offset_y);
+
+  targetCoords->valid = FALSE;
+  refX = refCoords->px - ref_offset_x;
+  refY = refCoords->py - ref_offset_y;
+  colordiffLocate =
+       gap_locateAreaWithinRadiusWithOffset (refLayerId
+                                    , refX
+                                    , refY
+                                    , valPtr->refShapeRadius
+                                    , targetLayerId
+                                    , valPtr->targetMoveRadius
+                                    , &targetX
+                                    , &targetY
+                                    , locateOffsetX
+                                    , locateOffsetY
+                                    );
+
+  targetCoords->px = targetX + target_offset_x;
+  targetCoords->py = targetY + target_offset_y;
+
+
+  if (colordiffLocate < valPtr->loacteColodiffThreshold)
+  {
+    /* successful located the deatail in target layer
+     * set target coordinates valid.
+     */
+    targetCoords->valid = TRUE;
+  }
+  else
+  {
+    (*lostTraceCount) += 1;
+  }
+
+  if(gap_debug)
+  {
+    printf("p_locate_target: refX:%d refY:%d locateOffsetX:%d locateOffsetY:%d\n"
+            "                targetX:%d targetY:%d targetCoords->px:%d py:%d  avgColodiff:%.5f valid:%d\n"
+        ,(int)refX
+        ,(int)refY
+        ,(int)locateOffsetX
+        ,(int)locateOffsetY
+        ,(int)targetX
+        ,(int)targetY
+        ,(int)targetCoords->px
+        ,(int)targetCoords->py
+        ,(float)colordiffLocate
+        , targetCoords->valid
+        );
+  }
+
+
+}  /* end p_locate_target */
+
+
+/* -----------------------------------------
+ * p_write_xml_header
+ * -----------------------------------------
+ * write header for a MovePath XML file
+ */
+static void
+p_write_xml_header(FILE *l_fp, gboolean center, gint width, gint height, gint numFrames)
+{
+  fprintf(l_fp, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
+  fprintf(l_fp, "<gimp_gap_move_path_parameters version=\"2\" >\n");
+  fprintf(l_fp, "  <frame_description width=\"%d\" height=\"%d\" range_from=\"1\" range_to=\"%d\" total_frames=\"%d\" />\n"
+          , (int)width
+          , (int)height
+          , (int)numFrames
+          , (int)numFrames
+          );
+  fprintf(l_fp, "  <tween tween_steps=\"0\" />\n");
+  fprintf(l_fp, "  <trace tracelayer_enable=\"FALSE\" />\n");
+  fprintf(l_fp, "  <moving_object src_layer_id=\"0\" src_layerstack=\"0\" width=\"%d\" height=\"%d\"\n"
+          , (int)width
+          , (int)height
+          );
+  if(center)
+  {
+    fprintf(l_fp, "    src_handle=\"GAP_HANDLE_CENTER\"\n");
+  }
+  else
+  {
+    fprintf(l_fp, "    src_handle=\"GAP_HANDLE_LEFT_TOP\"\n");
+  }
+  fprintf(l_fp, "    src_stepmode=\"GAP_STEP_FRAME_ONCE\" step_speed_factor=\"1.00000\"\n");
+  fprintf(l_fp, "    src_selmode=\"GAP_MOV_SEL_IGNORE\"\n");
+  fprintf(l_fp, "    src_paintmode=\"GIMP_NORMAL_MODE\"\n");
+  fprintf(l_fp, "    dst_layerstack=\"0\" src_force_visible=\"TRUE\" clip_to_img=\"FALSE\" src_apply_bluebox=\"FALSE\"\n");
+  fprintf(l_fp, "    >\n");
+  fprintf(l_fp, "  </moving_object>\n");
+  fprintf(l_fp, "\n");
+  fprintf(l_fp, "  <controlpoints current_point=\"1\" number_of_points=\"%d\"  >\n"
+          , (int)numFrames
+          );
+
+}  /* end p_write_xml_header */
+
+
+/* -----------------------------------------
+ * p_write_xml_footer
+ * -----------------------------------------
+ */
+static void
+p_write_xml_footer(FILE *l_fp)
+{
+  fprintf(l_fp, "  </controlpoints>\n");
+  fprintf(l_fp, "</gimp_gap_move_path_parameters>");
+}  /* end p_write_xml_footer */
+
+
+/* -----------------------------------------
+ * p_log_to_file
+ * -----------------------------------------
+ * log controlpoint (logString) to XML file.
+ * An existing XML file is owerwritten, but the controlpoints
+ * of the old content are copied to the newly written XML file generation.
+ *
+ * return TRUE on success, FALSE on errors.
+ */
+static gboolean
+p_log_to_file(const char *filename, const char *logString
+   , gint32 frameNr, gboolean center, gint width, gint height)
+{
+  char *textBuffer;
+  gsize lengthTextBuffer;
+  gint  ii;
+  gint  beginPos;
+  gint  endPos;
+  gint  copyPos;
+  gint  copySize;
+  gboolean ok;
+  FILE *l_fp;
+
+
+
+  copySize = 0;
+  beginPos = 0;
+  endPos   = 0;
+  copyPos  = -1;
+  textBuffer = NULL;
+  if ((g_file_test (filename, G_FILE_TEST_EXISTS))
+  && (frameNr > 1))
+  {
+    /* get old content of the XML file and findout the block
+     * of controlpoint lines to be copied when (over)writing the next
+     * generation of the XML file
+     */
+
+    if ((g_file_get_contents (filename, &textBuffer, &lengthTextBuffer, NULL) != TRUE)
+    || (textBuffer == NULL))
+    {
+      printf("Could not load XML file:%s\n", filename);
+      return(FALSE);
+    }
+
+    for(ii=1; ii < lengthTextBuffer; ii++)
+    {
+
+      if (textBuffer[ii] == '\n')
+      {
+        beginPos = -1;
+      }
+
+      if ((textBuffer[ii-1] != ' ') && (textBuffer[ii] == ' '))
+      {
+        beginPos = ii;
+      }
+
+      /* find first controlpoint (as start position of block to copy) */
+      if (strncmp(&textBuffer[ii], "<controlpoint ", strlen("<controlpoint ")) == 0)
+      {
+        if (copyPos < 0)
+        {
+          if (beginPos >= 0)
+          {
+            copyPos = beginPos;
+          }
+          else
+          {
+            copyPos = ii -1;
+          }
+        }
+      }
+
+      /* check end position of the controlpoints block */
+      if (strncmp(&textBuffer[ii], "</controlpoints>", strlen("</controlpoints>")) == 0)
+      {
+        if (beginPos >= 0)
+        {
+          endPos = beginPos;
+        }
+        else
+        {
+          endPos = ii -1;
+        }
+        if (copyPos > 0)
+        {
+          copySize = endPos - copyPos;
+        }
+        break;
+      }
+    }
+    if ((endPos == 0) && (copyPos > 0))
+    {
+      copySize = lengthTextBuffer - copyPos;
+    }
+  }
+
+
+
+  ok = FALSE;
+
+  l_fp = g_fopen(filename, "w+");
+  if(l_fp != NULL)
+  {
+      p_write_xml_header(l_fp, center, width, height, frameNr);
+
+      if (copySize > 0)
+      {
+        fclose(l_fp);
+
+        /* append controlpoints (use binary mode to prevent additional line feeds in Windows environment)  */
+        l_fp = g_fopen(filename, "ab");
+        if(l_fp != NULL)
+        {
+          fwrite(&textBuffer[copyPos], copySize, 1, l_fp);
+          fclose(l_fp);
+          l_fp = g_fopen(filename, "a+");
+        }
+      }
+
+      if(l_fp != NULL)
+      {
+        fprintf(l_fp, "%s\n", logString);
+        p_write_xml_footer(l_fp);
+        fclose(l_fp);
+
+        ok = TRUE;
+      }
+  }
+  else
+  {
+    printf("Could not update file:%s", filename);
+  }
+
+  if(textBuffer != NULL)
+  {
+    g_free(textBuffer);
+  }
+
+  return (ok);
+
+}  /* end p_log_to_file */
+
+
+
+/* ----------------------------
+ * p_coords_logging
+ * ----------------------------
+ * log coordinates to stdout
+ * or to move-path controlpoint XML file.
+ *
+ */
+static void
+p_coords_logging(gint32 frameNr, PixelCoords *currCoords,  PixelCoords *currCoords2
+  , PixelCoords *startCoords, PixelCoords *startCoords2, FilterValues *valPtr
+  , gint32 imageId
+  )
+{
+  gint32  px;
+  gint32  py;
+  gint32  px1;
+  gint32  py1;
+  gint32  px2;
+  gint32  py2;
+  gdouble rotation;
+  gchar  *logString;
+  gdouble scaleFactor;
+  gboolean center;
+  gint     width;
+  gint     height;
+  gint     precision_digits;
+  gchar   *rotValueAsString;
+
+  if(currCoords->valid != TRUE)
+  {
+    /* do not record invalid coordinates */
+    return;
+  }
+
+  width = gimp_image_width(imageId);
+  height = gimp_image_height(imageId);
+  center = FALSE;
+  if ((valPtr->coordsRelToFrame1)
+  &&  (valPtr->offsX != 0)
+  &&  (valPtr->offsY != 0))
+  {
+    center = TRUE;
+  }
+
+  scaleFactor = 1.0;
+  rotation = 0.0;
+  px1 = currCoords->px;
+  py1 = currCoords->py;
+
+  px2 = currCoords2->px;
+  py2 = currCoords2->py;
+
+  if ((valPtr->coordsRelToFrame1)
+  &&  (startCoords->valid == TRUE))
+  {
+    px1 = startCoords->px -px1;
+    py1 = startCoords->py -py1;
+  }
+
+
+  px = px1 + valPtr->offsX;
+  py = py1 + valPtr->offsY;
+
+  if ((valPtr->coordsRelToFrame1)
+  &&  (startCoords2->valid == TRUE))
+  {
+    px2 = startCoords2->px -px2;
+    py2 = startCoords2->py -py2;
+
+  }
+
+  if((currCoords2->valid  == TRUE)
+  && (startCoords2->valid == TRUE)
+  && (startCoords->valid  == TRUE))
+  {
+    gdouble startAngle;
+    gdouble currAngle;
+
+    /* we have 2 valid coordinate points and can calculate rotate compensation
+     * in this case movement offest compensation is the average
+     * of both tracked coordinate points
+     */
+    px = ((px1 + px2) / 2) + valPtr->offsX;
+    py = ((py1 + py2) / 2) + valPtr->offsY;
+
+
+    startAngle = p_calculate_angle_in_degree( startCoords->px
+                                    , startCoords->py
+                                    , startCoords2->px
+                                    , startCoords2->py
+                                    );
+    currAngle = p_calculate_angle_in_degree(  currCoords->px
+                                    , currCoords->py
+                                    , currCoords2->px
+                                    , currCoords2->py
+                                    );
+    rotation = startAngle - currAngle;
+    if (rotation >= 360.0)
+    {
+      rotation = 360.0 - rotation;
+    }
+    if (rotation <= -360.0)
+    {
+      rotation += 360.0;
+    }
+    scaleFactor = p_calculate_scale_factor( startCoords->px
+                                    , startCoords->py
+                                    , startCoords2->px
+                                    , startCoords2->py
+                                    , currCoords->px
+                                    , currCoords->py
+                                    , currCoords2->px
+                                    , currCoords2->py
+                                    );
+  }
+
+  logString = NULL;
+
+  if(currCoords2->valid == TRUE)
+  {
+    /* double point detail coordinate tracking (allows calculation of rotate and scale factors) */
+    gchar *scaleValueAsString;
+
+
+    precision_digits = 7;
+    rotValueAsString = gap_base_gdouble_to_ascii_string(rotation + valPtr->offsRotate, precision_digits);
+    precision_digits = 5;
+    scaleValueAsString = gap_base_gdouble_to_ascii_string(scaleFactor * 100, precision_digits);
+
+    if(valPtr->enableScaling == TRUE)
+    {
+      logString = g_strdup_printf("    <controlpoint px=\"%04d\" py=\"%04d\" rotation=\"%s\" width_resize=\"%s\" height_resize=\"%s\" keyframe_abs=\"%d\" p1x=\"%04d\"  p1y=\"%04d\"  p2x=\"%04d\" p2y=\"%04d\"/>"
+       , px
+       , py
+       , rotValueAsString
+       , scaleValueAsString
+       , scaleValueAsString
+       , frameNr
+       , currCoords->px
+       , currCoords->py
+       , currCoords2->px
+       , currCoords2->py
+       );
+    }
+    else
+    {
+      logString = g_strdup_printf("    <controlpoint px=\"%04d\" py=\"%04d\" rotation=\"%s\" keyframe_abs=\"%d\" p1x=\"%04d\"  p1y=\"%04d\"  p2x=\"%04d\" p2y=\"%04d\"/>"
+       , px
+       , py
+       , rotValueAsString
+       , frameNr
+       , currCoords->px
+       , currCoords->py
+       , currCoords2->px
+       , currCoords2->py
+       );
+    }
+
+
+    g_free(rotValueAsString);
+    g_free(scaleValueAsString);
+  }
+  else
+  {
+    /* single point detail coordinate tracking */
+    if (valPtr->offsRotate == 0.0)
+    {
+      logString = g_strdup_printf("    <controlpoint px=\"%04d\" py=\"%04d\"  keyframe_abs=\"%d\" p1x=\"%04d\"  p1y=\"%04d\"/>"
+         , px
+         , py
+         , frameNr
+         , currCoords->px
+         , currCoords->py
+         );
+    }
+    else
+    {
+      rotValueAsString = gap_base_gdouble_to_ascii_string(valPtr->offsRotate, precision_digits);
+      precision_digits = 7;
+
+      logString = g_strdup_printf("    <controlpoint px=\"%04d\" py=\"%04d\"  rotation=\"%s\" keyframe_abs=\"%d\" p1x=\"%04d\"  p1y=\"%04d\"/>"
+         , px
+         , py
+         , rotValueAsString
+         , frameNr
+         , currCoords->px
+         , currCoords->py
+         );
+      g_free(rotValueAsString);
+    }
+
+  }
+
+  if ((valPtr->moveLogFile[0] == '\0')
+  ||  (valPtr->moveLogFile[0] == '-'))
+  {
+    printf("%s\n", logString);
+  }
+  else
+  {
+    p_log_to_file(&valPtr->moveLogFile[0], logString
+                , frameNr
+                , center
+                , width
+                , height
+                );
+  }
+
+  if (logString)
+  {
+    g_free(logString);
+  }
+
+
+}  /* end p_coords_logging */
+
+
+
+/* -------------------------------
+ * p_parse_frame_nr_from_layerId
+ * -------------------------------
+ */
+static gint32
+p_parse_frame_nr_from_layerId(gint32 layerId)
+{
+  char     *layername;
+  gint32    frameNr;
+  gint      len;
+  gint      ii;
+
+
+
+  frameNr = 0;
+
+  layername = gimp_drawable_get_name(layerId);
+  if(layername)
+  {
+    len = strlen(layername);
+    for(ii=1; ii < len; ii++)
+    {
+      if ((layername[ii-1] == '_')
+      &&  (layername[ii] <= '9')
+      &&  (layername[ii] >= '0'))
+      {
+        frameNr = g_ascii_strtod(&layername[ii], NULL);
+        break;
+      }
+    }
+    g_free(layername);
+  }
+
+  return (frameNr);
+
+}  /* end p_parse_frame_nr_from_layerId */
+
+
+/* -------------------------------
+ * p_get_frameHistInfo
+ * -------------------------------
+ */
+static void
+p_get_frameHistInfo(FrameHistInfo *frameHistInfo)
+{
+  int l_len;
+
+  frameHistInfo->workImageId = -1;
+  frameHistInfo->frameNr = 0;
+  frameHistInfo->startCoords.valid = FALSE;
+  frameHistInfo->startCoords.px = 0;
+  frameHistInfo->startCoords.py = 0;
+  frameHistInfo->lostTraceCount = 0;
+
+  l_len = gimp_get_data_size (GAP_DETAIL_FRAME_HISTORY_INFO);
+
+  if(gap_debug)
+  {
+    printf("p_get_frameHistInfo: %s len:%d sizeof(FrameHistInfo):%d\n"
+       , GAP_DETAIL_FRAME_HISTORY_INFO
+       , (int)l_len
+       , (int)sizeof(FrameHistInfo)
+       );
+  }
+
+
+  if (l_len == sizeof(FrameHistInfo))
+  {
+
+    gimp_get_data(GAP_DETAIL_FRAME_HISTORY_INFO, frameHistInfo);
+
+    if(gap_debug)
+    {
+      printf("p_get_frameHistInfo: %s  frameNr:%d px:%d py:%d valid:%d\n"
+             "                     prevPx:%d prevPy:%d prevValid:%d lostTraceCount:%d\n"
+        , GAP_DETAIL_FRAME_HISTORY_INFO
+        , (int)frameHistInfo->frameNr
+        , (int)frameHistInfo->startCoords.px
+        , (int)frameHistInfo->startCoords.py
+        , (int)frameHistInfo->startCoords.valid
+        , (int)frameHistInfo->prevCoords.px
+        , (int)frameHistInfo->prevCoords.py
+        , (int)frameHistInfo->prevCoords.valid
+        , (int)frameHistInfo->lostTraceCount
+        );
+    }
+
+  }
+
+}  /* end p_get_frameHistInfo */
+
+
+/* -------------------------------
+ * p_set_frameHistInfo
+ * -------------------------------
+ * store frame history information
+ * (for the next run in the same gimp session)
+ */
+static void
+p_set_frameHistInfo(FrameHistInfo *frameHistInfo)
+{
+  if(gap_debug)
+  {
+      printf("p_SET_frameHistInfo: %s  frameNr:%d px:%d py:%d valid:%d  prevPx:%d prevPy:%d prevValid:%d\n\n"
+        , GAP_DETAIL_FRAME_HISTORY_INFO
+        , (int)frameHistInfo->frameNr
+        , (int)frameHistInfo->startCoords.px
+        , (int)frameHistInfo->startCoords.py
+        , (int)frameHistInfo->startCoords.valid
+        , (int)frameHistInfo->prevCoords.px
+        , (int)frameHistInfo->prevCoords.py
+        , (int)frameHistInfo->prevCoords.valid
+
+        );
+  }
+
+  gimp_set_data(GAP_DETAIL_FRAME_HISTORY_INFO, frameHistInfo, sizeof(FrameHistInfo));
+
+}  /* end p_set_frameHistInfo */
+
+
+
+/* -------------------------------
+ * p_set_2_vector_points
+ * -------------------------------
+ * remove all strokes from the active path vectors
+ * and set a new stroke containing targetCoords (one or 2 depend on the valid flag)
+ * For better visualisation set guide lines crossing at the first target coords.
+ */
+static void
+p_set_2_vector_points(gint32 imageId, PixelCoords *targetCoords, PixelCoords *targetCoords2)
+{
+  gint32  activeVectorsId;
+  gint    newStrokeId;
+
+  gdouble  *points;
+  gint      num_points;
+  gboolean  closed;
+  GimpVectorsStrokeType type;
+
+
+  gimp_image_add_hguide(imageId, targetCoords->py);
+  gimp_image_add_vguide(imageId, targetCoords->px);
+
+
+  activeVectorsId = gimp_image_get_active_vectors(imageId);
+  if(activeVectorsId >= 0)
+  {
+    gint num_strokes;
+    gint *strokes;
+
+    strokes = gimp_vectors_get_strokes (activeVectorsId, &num_strokes);
+    if(strokes)
+    {
+      if(num_strokes > 0)
+      {
+        gint ii;
+        for(ii=0; ii < num_strokes; ii++)
+        {
+          gimp_vectors_remove_stroke(activeVectorsId, strokes[ii]);
+        }
+      }
+      g_free(strokes);
+
+    }
+
+    if (targetCoords->valid)
+    {
+      closed = FALSE;
+      num_points = 6;
+      if (targetCoords2->valid)
+      {
+        num_points = 12;
+      }
+      points = g_new (gdouble, num_points);
+      points[0] = targetCoords->px;
+      points[1] = targetCoords->py;
+      points[2] = targetCoords->px;
+      points[3] = targetCoords->py;
+      points[4] = targetCoords->px;
+      points[5] = targetCoords->py;
+      if(targetCoords2->valid)
+      {
+        points[6] = targetCoords2->px;
+        points[7] = targetCoords2->py;
+        points[8] = targetCoords2->px;
+        points[9] = targetCoords2->py;
+        points[10] = targetCoords2->px;
+        points[11] = targetCoords2->py;
+      }
+
+      type = GIMP_VECTORS_STROKE_TYPE_BEZIER;
+      newStrokeId = gimp_vectors_stroke_new_from_points (activeVectorsId
+                                     , type
+                                     , num_points
+                                     , points
+                                     , closed
+                                     );
+      g_free(points);
+    }
+
+
+  }
+
+}  /* end p_set_2_vector_points */
+
+
+/* -----------------------------------
+ * gap_track_detail_on_top_layers
+ * -----------------------------------
+ * This procedure is typically called
+ * on the snapshot image created by the Player.
+ * This image has one layer at the first snapshot
+ * and each further snapshot adds one layer on top of the layerstack.
+ *
+ * The start is detected when the image has only one layer.
+ * optionally the numer of layers can be limted
+ * to 2 (or more) layers.
+ */
+gint32
+gap_track_detail_on_top_layers(gint32 imageId, gboolean doProgress, FilterValues *valPtr)
+{
+  gint      l_nlayers;
+  gint32   *l_layers_list;
+
+  FrameHistInfo  frameHistInfoData;
+  FrameHistInfo *frameHistInfo;
+  PixelCoords  currCoords;
+  PixelCoords  currCoords2;
+  PixelCoords  targetCoords;
+  PixelCoords  targetCoords2;
+  gint32       locateOffsetX;
+  gint32       locateOffsetY;
+  gint32       locateOffsetX2;
+  gint32       locateOffsetY2;
+  gint32       lostTraceCount;
+
+  if(gap_debug)
+  {
+      printf("gap_track_detail_on_top_layers: START\n"
+             "  refShapeRadius:%d targetMoveRadius:%d locateColordiff:%.4f\n"
+             "  coordsRelToFrame1:%d  offsX:%d offsY:%d removeMidlayers:%d bgLayerIsReference:%d\n"
+             "  moveLogFile:%s\n"
+            , (int)valPtr->refShapeRadius
+            , (int)valPtr->targetMoveRadius
+            , (float)valPtr->loacteColodiffThreshold
+            , (int)valPtr->coordsRelToFrame1
+            , (int)valPtr->offsX
+            , (int)valPtr->offsY
+            , (int)valPtr->removeMidlayers
+            , (int)valPtr->bgLayerIsReference
+            , valPtr->moveLogFile
+            );
+  }
+
+  frameHistInfo = &frameHistInfoData;
+
+  currCoords.valid = FALSE;
+  targetCoords.valid = FALSE;
+
+  l_layers_list = gimp_image_get_layers(imageId, &l_nlayers);
+  if((l_layers_list != NULL)
+  && (l_nlayers > 0))
+  {
+    gint32 topLayerId;
+
+    topLayerId = l_layers_list[0];
+
+    frameHistInfo->frameNr += 1;
+
+    p_get_frameHistInfo(frameHistInfo);
+    locateOffsetX = 0;
+    locateOffsetY = 0;
+    locateOffsetX2 = 0;
+    locateOffsetY2 = 0;
+
+    if (l_nlayers == 1)
+    {
+      p_capture_2_vector_points(imageId, &currCoords, &currCoords2);
+
+      frameHistInfo->lostTraceCount = 0;
+      p_copy_src_to_dst_coords(&currCoords,  &frameHistInfo->startCoords);
+      p_copy_src_to_dst_coords(&currCoords2, &frameHistInfo->startCoords2);
+      p_copy_src_to_dst_coords(&currCoords,  &frameHistInfo->prevCoords);
+      p_copy_src_to_dst_coords(&currCoords2, &frameHistInfo->prevCoords2);
+
+      frameHistInfo->frameNr = 1;
+      p_coords_logging(frameHistInfo->frameNr
+                      , &currCoords
+                      , &currCoords2
+                      , &frameHistInfo->startCoords
+                      , &frameHistInfo->startCoords2
+                      , valPtr
+                      , imageId
+                      );
+    }
+    else
+    {
+      gint32 refLayerId;
+
+      refLayerId = l_layers_list[1];
+      if (valPtr->bgLayerIsReference == TRUE)
+      {
+        refLayerId = l_layers_list[l_nlayers -1];
+      }
+
+
+      if(frameHistInfo->startCoords.valid != TRUE)
+      {
+        p_capture_2_vector_points(imageId, &currCoords, &currCoords2);
+
+        frameHistInfo->lostTraceCount = 0;
+        p_copy_src_to_dst_coords(&currCoords,  &frameHistInfo->startCoords);
+        p_copy_src_to_dst_coords(&currCoords2, &frameHistInfo->startCoords2);
+        p_copy_src_to_dst_coords(&currCoords,  &frameHistInfo->prevCoords);
+        p_copy_src_to_dst_coords(&currCoords2, &frameHistInfo->prevCoords2);
+
+        frameHistInfo->frameNr = p_parse_frame_nr_from_layerId(refLayerId);
+        p_coords_logging(frameHistInfo->frameNr
+                      , &currCoords
+                      , &currCoords2
+                      , &frameHistInfo->startCoords
+                      , &frameHistInfo->startCoords2
+                      , valPtr
+                      , imageId
+                      );
+      }
+      else if (valPtr->bgLayerIsReference == TRUE)
+      {
+        /* when all trackings refere to initial BG layer (that is always kept as reference
+         * for all further frames), we do not capture currCoords
+         * but copy the initial start values.
+         */
+        p_copy_src_to_dst_coords(&frameHistInfo->startCoords, &currCoords);
+        p_copy_src_to_dst_coords(&frameHistInfo->startCoords2, &currCoords2);
+
+        /* locate shall start investigations at matching coordinates of the previous processed frame
+         * because the chance to find the detail near this postion is much greater than near
+         * the start coords in the initial frame.
+         * (note that locate does use the initial frame e.g. the BG layer in this mode,
+         * but without the locateOffsets we might loose track of the detail when it moves outside the targetRadius
+         * and increasing the targetRadius would also result in siginificant longer processing time)
+         */
+        if (frameHistInfo->prevCoords.valid)
+        {
+          locateOffsetX = frameHistInfo->prevCoords.px - frameHistInfo->startCoords.px;
+          locateOffsetY = frameHistInfo->prevCoords.py - frameHistInfo->startCoords.py;
+	    }
+        if (frameHistInfo->prevCoords2.valid)
+        {
+          locateOffsetX2 = frameHistInfo->prevCoords2.px - frameHistInfo->startCoords2.px;
+          locateOffsetY2 = frameHistInfo->prevCoords2.py - frameHistInfo->startCoords2.py;
+	    }
+      }
+      else
+      {
+	    /* tracking is done with reference to the previous layer
+	     * therefore refresh capture. (currCoords are set to the targetCoords
+	     * that were calculated in previous processing step)
+	     */
+        p_capture_2_vector_points(imageId, &currCoords, &currCoords2);
+      }
+
+      lostTraceCount = frameHistInfo->lostTraceCount;
+
+      if (currCoords.valid == TRUE)
+      {
+        p_locate_target(refLayerId
+                           , &currCoords
+                           , topLayerId
+                           , &targetCoords
+                           , locateOffsetX
+                           , locateOffsetY
+                           , valPtr
+                           , &frameHistInfo->lostTraceCount
+                           );
+      }
+
+      if (currCoords2.valid == TRUE)
+      {
+        p_locate_target(refLayerId
+                           , &currCoords2
+                           , topLayerId
+                           , &targetCoords2
+                           , locateOffsetX2
+                           , locateOffsetY2
+                           , valPtr
+                           , &frameHistInfo->lostTraceCount
+                           );
+      }
+
+
+    }
+
+
+
+    if (targetCoords.valid == TRUE)
+    {
+      gap_image_remove_all_guides(imageId);
+      p_set_2_vector_points(imageId, &targetCoords, &targetCoords2);
+
+      p_copy_src_to_dst_coords(&targetCoords,  &frameHistInfo->prevCoords);
+      p_copy_src_to_dst_coords(&targetCoords2, &frameHistInfo->prevCoords2);
+
+      frameHistInfo->frameNr = p_parse_frame_nr_from_layerId(topLayerId);
+      p_coords_logging(frameHistInfo->frameNr
+                      , &targetCoords
+                      , &targetCoords2
+                      , &frameHistInfo->startCoords
+                      , &frameHistInfo->startCoords2
+                      , valPtr
+                      , imageId
+                      );
+
+    }
+
+    if (valPtr->removeMidlayers == TRUE)
+    {
+      gap_image_limit_layers(imageId
+                            , 2   /* keepTopLayers */
+                            , 1   /* keepBgLayers */
+                            );
+    }
+
+    p_set_frameHistInfo(frameHistInfo);
+    g_free(l_layers_list);
+
+    if ((lostTraceCount == 0)
+    &&  (frameHistInfo->lostTraceCount > 0))
+    {
+      /* trace lost the 1st time, display warning */
+      gap_arr_msg_popup(GIMP_RUN_INTERACTIVE
+                       , _("Detail Tracking Stopped. (could not find corresponding detail)"));;
+    }
+  }
+
+  return(imageId);
+
+}  /* end gap_track_detail_on_top_layers */
+
+
+
+
+/* ---------------------------------------
+ * gap_track_detail_on_top_layers_lastvals
+ * ---------------------------------------
+ * processing based on last values used in the same gimp session.
+ * (uses defaults when called the 1.st time)
+ * Intended for use in the player
+ */
+gint32
+gap_track_detail_on_top_layers_lastvals(gint32 imageId)
+{
+  gint32       rc;
+  gboolean     doProgress;
+  FilterValues fiVals;
+
+  /* clear undo stack */
+  if (gimp_image_undo_is_enabled(imageId))
+  {
+    gimp_image_undo_disable(imageId);
+  }
+
+  doProgress = FALSE;
+  gap_detail_tracking_get_values(&fiVals);
+  rc = gap_track_detail_on_top_layers(imageId, doProgress, &fiVals);
+
+  gimp_image_undo_enable(imageId); /* clear undo stack */
+
+  return(rc);
+
+}  /* end gap_track_detail_on_top_layers_lastvals */
+
+
+/* -----------------------------------
+ * gap_detail_tracking_get_values
+ * -----------------------------------
+ * This procedure is typically called
+ * on the snapshot image created by the Player.
+ * This image has one layer at the first snapshot
+ * and each further snapshot adds one layer on top of the layerstack.
+ *
+ * The start is detected when the image has only one layer.
+ * optionally the numer of layers can be limted
+ * to 2 (or more) layers.
+ */
+void
+gap_detail_tracking_get_values(FilterValues *fiVals)
+{
+  int l_len;
+
+  /* init default values */
+  fiVals->refShapeRadius             = DEFAULT_refShapeRadius;
+  fiVals->targetMoveRadius           = DEFAULT_targetMoveRadius;
+  fiVals->loacteColodiffThreshold    = DEFAULT_loacteColodiffThreshold;
+  fiVals->coordsRelToFrame1          = DEFAULT_coordsRelToFrame1;
+  fiVals->offsX                      = DEFAULT_offsX;
+  fiVals->offsY                      = DEFAULT_offsY;
+  fiVals->offsRotate                 = DEFAULT_offsRotate;
+  fiVals->enableScaling              = DEFAULT_enableScaling;
+  fiVals->removeMidlayers            = DEFAULT_removeMidlayers;
+  fiVals->bgLayerIsReference         = DEFAULT_bgLayerIsReference;
+  fiVals->moveLogFile[0]             = '\0';
+
+  l_len = gimp_get_data_size (GAP_DETAIL_TRACKING_PLUG_IN_NAME);
+  if (l_len == sizeof(FilterValues))
+  {
+    /* Possibly retrieve data from a previous interactive run */
+    gimp_get_data (GAP_DETAIL_TRACKING_PLUG_IN_NAME, fiVals);
+
+    if(gap_debug)
+    {
+      printf("gap_detail_tracking_get_values FOUND data for key:%s\n"
+        , GAP_DETAIL_TRACKING_PLUG_IN_NAME
+        );
+    }
+  }
+
+  if(gap_debug)
+  {
+    printf("gap_detail_tracking_get_values:\n"
+           "  refShapeRadius:%d targetMoveRadius:%d locateColordiff:%.4f\n"
+           "  coordsRelToFrame1:%d  offsX:%d offsY:%d removeMidlayers:%d bgLayerIsReference:%d\n"
+           "  moveLogFile:%s\n"
+            , (int)fiVals->refShapeRadius
+            , (int)fiVals->targetMoveRadius
+            , (float)fiVals->loacteColodiffThreshold
+            , (int)fiVals->coordsRelToFrame1
+            , (int)fiVals->offsX
+            , (int)fiVals->offsY
+            , (int)fiVals->removeMidlayers
+            , (int)fiVals->bgLayerIsReference
+            , fiVals->moveLogFile
+            );
+  }
+
+}  /* end gap_detail_tracking_get_values */
+
+
+
+/* --------------------------
+ * gap_detail_tracking_dialog
+ * --------------------------
+ *   return  TRUE.. OK
+ *           FALSE.. in case of Error or cancel
+ */
+gboolean
+gap_detail_tracking_dialog(FilterValues *fiVals)
+{
+#define SPINBUTTON_ENTRY_WIDTH 80
+#define DETAIL_TRACKING_DIALOG_ARGC 12
+
+  static GapArrArg  argv[DETAIL_TRACKING_DIALOG_ARGC];
+  gint ii;
+  gint ii_loacteColodiffThreshold;
+  gint ii_refShapeRadius;
+  gint ii_targetMoveRadius;
+  gint ii_coordsRelToFrame1;
+  gint ii_offsX;
+  gint ii_offsY;
+  gint ii_offsRotate;
+  gint ii_enableScaling;
+  gint ii_removeMidlayers;
+  gint ii_bgLayerIsReference;
+
+
+  ii=0; gap_arr_arg_init(&argv[ii], GAP_ARR_WGT_LABEL);
+  argv[0].label_txt = _("This filter requires a current path with one or 2 anchor points\n"
+                        "to mark coordinate(s) to be tracked in the target frame(s)");
+
+
+  ii++; gap_arr_arg_init(&argv[ii], GAP_ARR_WGT_FLT_PAIR); ii_loacteColodiffThreshold = ii;
+  argv[ii].constraint = TRUE;
+  argv[ii].label_txt = _("Locate colordiff Thres:");
+  argv[ii].help_txt  = _("Colordiff threshold value. Locate fails when average color difference is below this value.");
+  argv[ii].flt_min   = 0.0;
+  argv[ii].flt_max   = 1.0;
+  argv[ii].flt_ret   = fiVals->loacteColodiffThreshold;
+  argv[ii].flt_step  =  0.01;
+  argv[ii].flt_digits =  4;
+  argv[ii].entry_width = SPINBUTTON_ENTRY_WIDTH;
+  argv[ii].has_default = TRUE;
+  argv[ii].flt_default = DEFAULT_loacteColodiffThreshold;
+
+
+  ii++; gap_arr_arg_init(&argv[ii], GAP_ARR_WGT_INT_PAIR); ii_refShapeRadius = ii;
+  argv[ii].label_txt = _("Locate Shape Radius:");
+  argv[ii].help_txt  = _("The quadratic area surrounding a marked detail coordinate +- this radius "
+                         "is considered as reference shape, to be tracked in the target frame(s).");
+  argv[ii].constraint = FALSE;
+  argv[ii].int_min   = 1;
+  argv[ii].int_max   = 50;
+  argv[ii].umin      = 1;
+  argv[ii].umax      = 100;
+  argv[ii].int_ret   = fiVals->refShapeRadius;
+  argv[ii].entry_width = SPINBUTTON_ENTRY_WIDTH;
+  argv[ii].has_default = TRUE;
+  argv[ii].int_default = DEFAULT_refShapeRadius;
+
+  ii++; gap_arr_arg_init(&argv[ii], GAP_ARR_WGT_INT_PAIR); ii_targetMoveRadius = ii;
+  argv[ii].label_txt = _("Locate Target Move Radius:");
+  argv[ii].help_txt  = _("Limits attempts to locate the Detail within this radius.");
+  argv[ii].constraint = FALSE;
+  argv[ii].int_min   = 1;
+  argv[ii].int_max   = 500;
+  argv[ii].umin      = 1;
+  argv[ii].umax      = 1000;
+  argv[ii].int_ret   = fiVals->targetMoveRadius;
+  argv[ii].entry_width = SPINBUTTON_ENTRY_WIDTH;
+  argv[ii].has_default = TRUE;
+  argv[ii].int_default = DEFAULT_targetMoveRadius;
+
+
+
+  ii++; gap_arr_arg_init(&argv[ii], GAP_ARR_WGT_TOGGLE); ii_coordsRelToFrame1 = ii;
+  argv[ii].label_txt = _("Log Relative Coords:");
+  argv[ii].help_txt  = _("ON: Coordinates are logged relative to the first coordinate.\n"
+                         "OFF: Coordinates are logged as absolute pixel coordinate values.");
+  argv[ii].constraint = FALSE;
+  argv[ii].int_ret   = fiVals->coordsRelToFrame1;
+  argv[ii].has_default = TRUE;
+  argv[ii].int_default = DEFAULT_coordsRelToFrame1;
+
+
+  ii++; gap_arr_arg_init(&argv[ii], GAP_ARR_WGT_TOGGLE); ii_enableScaling = ii;
+  argv[ii].label_txt = _("Log Scaling:");
+  argv[ii].help_txt  = _("ON: Calculate scaling and rotation when 2 detail Coordinates are tracked.\n"
+                         "OFF: Calculate only rotation and keep orignal size.");
+  argv[ii].int_ret   = fiVals->enableScaling;
+  argv[ii].has_default = TRUE;
+  argv[ii].int_default = DEFAULT_enableScaling;
+
+
+
+
+  ii++; gap_arr_arg_init(&argv[ii], GAP_ARR_WGT_TOGGLE); ii_bgLayerIsReference = ii;
+  argv[ii].label_txt = _("BG is Reference:");
+  argv[ii].help_txt  = _("ON: Use background layer as reference and foreground layer as target for tracking.\n"
+                         "OFF: Use foreground layer as target, and the layer below as reference\n.");
+  argv[ii].int_ret   = fiVals->bgLayerIsReference;
+  argv[ii].has_default = TRUE;
+  argv[ii].int_default = DEFAULT_bgLayerIsReference;
+
+
+  ii++; gap_arr_arg_init(&argv[ii], GAP_ARR_WGT_TOGGLE); ii_removeMidlayers = ii;
+  argv[ii].label_txt = _("Remove Middle Layers:");
+  argv[ii].help_txt  = _("ON: removes layers (except BG and 2 Layer on top) that are not relevant for detail tracking.\n"
+                         "OFF: Keep all layers.");
+  argv[ii].int_ret   = fiVals->removeMidlayers;
+  argv[ii].has_default = TRUE;
+  argv[ii].int_default = DEFAULT_removeMidlayers;
+
+
+
+
+
+  ii++; gap_arr_arg_init(&argv[ii], GAP_ARR_WGT_INT_PAIR); ii_offsX = ii;
+  argv[ii].label_txt = _("Const X Offset:");
+  argv[ii].help_txt  = _("This value is added when logging captured X coordinates.");
+  argv[ii].constraint = FALSE;
+  argv[ii].int_min   = 0;
+  argv[ii].int_max   = 2000;
+  argv[ii].umin      = 0;
+  argv[ii].umax      = 10000;
+  argv[ii].int_ret   = fiVals->offsX;
+  argv[ii].entry_width = SPINBUTTON_ENTRY_WIDTH;
+  argv[ii].has_default = TRUE;
+  argv[ii].int_default = DEFAULT_offsX;
+
+
+  ii++; gap_arr_arg_init(&argv[ii], GAP_ARR_WGT_INT_PAIR); ii_offsY = ii;
+  argv[ii].label_txt = _("Const Y Offset:");
+  argv[ii].help_txt  = _("This value is added when logging captured Y coordinates.");
+  argv[ii].constraint = FALSE;
+  argv[ii].int_min   = 0;
+  argv[ii].int_max   = 2000;
+  argv[ii].umin      = 0;
+  argv[ii].umax      = 10000;
+  argv[ii].int_ret   = fiVals->offsY;
+  argv[ii].entry_width = SPINBUTTON_ENTRY_WIDTH;
+  argv[ii].has_default = TRUE;
+  argv[ii].int_default = DEFAULT_offsY;
+
+  ii++; gap_arr_arg_init(&argv[ii], GAP_ARR_WGT_FLT_PAIR); ii_offsRotate = ii;
+  argv[ii].constraint = TRUE;
+  argv[ii].label_txt = _("Const Rotate Offset:");
+  argv[ii].help_txt  = _("This value is added when logging rotation values.");
+  argv[ii].flt_min   = -360.0;
+  argv[ii].flt_max   = 360.0;
+  argv[ii].flt_ret   = fiVals->offsRotate;
+  argv[ii].flt_step  =  0.1;
+  argv[ii].flt_digits =  4;
+  argv[ii].entry_width = SPINBUTTON_ENTRY_WIDTH;
+  argv[ii].has_default = TRUE;
+  argv[ii].flt_default = DEFAULT_offsRotate;
+
+
+  ii++; gap_arr_arg_init(&argv[ii], GAP_ARR_WGT_FILESEL);
+  argv[ii].label_txt = _("MovePath XML file:");
+  argv[ii].help_txt  = _("Name of the file to log the tracked detail coordinates "
+                        " as XML parameterfile for later use in the MovePath plug-in.");
+  argv[ii].text_buf_len = sizeof(fiVals->moveLogFile);
+  argv[ii].text_buf_ret = &fiVals->moveLogFile[0];
+  argv[ii].entry_width = 400;
+
+
+
+  ii++; gap_arr_arg_init(&argv[ii], GAP_ARR_WGT_DEFAULT_BUTTON);
+  argv[ii].label_txt = _("Default");
+  argv[ii].help_txt  = _("Reset all parameters to default values");
+
+  if(TRUE == gap_arr_ok_cancel_dialog(_("Detail Tracking"),
+                            _("Settings :"),
+                            DETAIL_TRACKING_DIALOG_ARGC, argv))
+  {
+      fiVals->refShapeRadius           = (gint32)(argv[ii_refShapeRadius].int_ret);
+      fiVals->targetMoveRadius         = (gint32)(argv[ii_targetMoveRadius].int_ret);
+      fiVals->loacteColodiffThreshold  = (gdouble)(argv[ii_loacteColodiffThreshold].flt_ret);
+      fiVals->coordsRelToFrame1        = (gint32)(argv[ii_coordsRelToFrame1].int_ret);
+      fiVals->offsX                    = (gint32)(argv[ii_offsX].int_ret);
+      fiVals->offsY                    = (gint32)(argv[ii_offsY].int_ret);
+      fiVals->offsRotate               = (gint32)(argv[ii_offsRotate].flt_ret);
+      fiVals->enableScaling            = (gint32)(argv[ii_enableScaling].int_ret);
+      fiVals->removeMidlayers          = (gint32)(argv[ii_removeMidlayers].int_ret);
+      fiVals->bgLayerIsReference       = (gint32)(argv[ii_bgLayerIsReference].int_ret);
+
+      gimp_set_data (GAP_DETAIL_TRACKING_PLUG_IN_NAME, fiVals, sizeof (FilterValues));
+      return TRUE;
+  }
+  else
+  {
+      return FALSE;
+  }
+}               /* end gap_detail_tracking_dialog */
+
+
+/* ---------------------------------------
+ * gap_detail_tracking_dialog_cfg_set_vals
+ * ---------------------------------------
+ *   return  TRUE.. OK
+ *           FALSE.. in case of Error or cancel
+ */
+gboolean
+gap_detail_tracking_dialog_cfg_set_vals(gint32 image_id)
+{
+  gboolean     rc;
+  FilterValues fiVals;
+
+  gap_detail_tracking_get_values(&fiVals);
+  if(image_id >= 0)
+  {
+    if (fiVals.coordsRelToFrame1)
+    {
+      if(gap_image_is_alive(image_id))
+      {
+        /* default offsets for handle at center */
+        fiVals.offsX = gimp_image_width(image_id) / 2.0;
+        fiVals.offsY = gimp_image_height(image_id) / 2.0;
+      }
+    }
+  }
+
+  rc = gap_detail_tracking_dialog(&fiVals);
+
+  return(rc);
+
+}  /* end gap_detail_tracking_dialog_cfg_set_vals */
+
diff --git a/gap/gap_detail_tracking_exec.h b/gap/gap_detail_tracking_exec.h
new file mode 100644
index 0000000..f006e48
--- /dev/null
+++ b/gap/gap_detail_tracking_exec.h
@@ -0,0 +1,119 @@
+/*  gap_detail_tracking_exec.h
+ *    This filter locates the position of one or 2 small areas
+ *    of a reference layer within a target layer and logs the coordinates
+ *    as XML file. It is intended to track details in a frame sequence.
+ *
+ *  2011/12/01
+ */
+/* 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
+ *  (2011/12/01)  2.7.0       hof: created
+ */
+
+#ifndef _GAP_DETAIL_TRACKING_EXEC_H
+#define _GAP_DETAIL_TRACKING_EXEC_H
+
+
+#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_libgapbase.h"
+#include "gap_locate.h"
+#include "gap_colordiff.h"
+#include "gap_image.h"
+#include "gap_layer_copy.h"
+#include "gap_arr_dialog.h"
+
+#include "gap-intl.h"
+
+
+
+#define GAP_DETAIL_FRAME_HISTORY_INFO     "GAP_DETAIL_FRAME_HISTORY_INFO"
+#define GAP_DETAIL_TRACKING_PLUG_IN_NAME  "gap-detail-tracking"
+
+typedef struct FilterValues {
+   gint32     refShapeRadius;
+   gint32     targetMoveRadius;
+   gdouble    loacteColodiffThreshold;
+
+   gboolean   coordsRelToFrame1;   /* subtract coords of frame 1 when logging coords */
+   gint32     offsX;               /* add this value when logging coords */
+   gint32     offsY;
+   gdouble    offsRotate;          /* additional rotation angle, to be added in all controlpoints */
+   gboolean   enableScaling;       /* on: use rotation and scaling  off: roate only  */
+   gboolean   bgLayerIsReference;
+   gboolean   removeMidlayers;     /* on: keep 2 top layers and Bg layer, remove other layers  off: keep all layers  */
+   char       moveLogFile[1600];
+} FilterValues;
+
+typedef struct PixelCoords
+{
+  gboolean  valid;
+  gint32  px;
+  gint32  py;
+} PixelCoords;
+
+
+typedef struct FrameHistInfo
+{
+  gint32       workImageId;
+  gint32       frameNr;      /* last handled frameNr */
+  PixelCoords  startCoords;  /* coords of first processed frame */
+  PixelCoords  startCoords2; /* 2nd detail coords of first processed frame */
+
+  PixelCoords  prevCoords;   /* coords of the previous processed frame */
+  PixelCoords  prevCoords2;  /* 2nd detail coords of the previous processed frame */
+
+  gint32       lostTraceCount;
+} FrameHistInfo;
+
+
+
+/* -----------------------------------
+ * gap_track_detail_on_top_layers
+ * -----------------------------------
+ * This procedure is typically called
+ * on the snapshot image created by the Player.
+ * This image has one layer at the first snapshot
+ * and each further snapshot adds one layer on top of the layerstack.
+ *
+ * The start is detected when the image has only one layer.
+ * optionally the numer of layers can be limted
+ * to 2 (or more) layers.
+ */
+gint32      gap_track_detail_on_top_layers(gint32 imageId, gboolean doProgress, FilterValues *valPtr);
+void        gap_detail_tracking_get_values(FilterValues *fiVals);
+gboolean    gap_detail_tracking_dialog(FilterValues *fiVals);
+
+
+/* procedure variants intended for use in the player plug-in */
+gint32      gap_track_detail_on_top_layers_lastvals(gint32 imageId);
+gboolean    gap_detail_tracking_dialog_cfg_set_vals(gint32 imageId);
+
+
+
+#endif
diff --git a/gap/gap_detail_tracking_main.c b/gap/gap_detail_tracking_main.c
new file mode 100644
index 0000000..551ae64
--- /dev/null
+++ b/gap/gap_detail_tracking_main.c
@@ -0,0 +1,719 @@
+/*  gap_detail_tracking_main.c
+ *    This filter locates the position of a small area of one layer
+ *    in another layer.
+ *    It was implemented for recording postions as XML input
+ *    for the MovePath tool by tracking a detail
+ *    in a series of video frames.
+ *    The recorded positions can also be used as XML input for the XML aligner
+ *    plug-in (available in this module) and can be used as filter
+ *    in the frames modify feature.
+ *
+ *
+ *    Applying the recorded position can compensate unwanted camera moves
+ *    when static scenes where shot without using a stativ.
+ *  Note that the recording of positions is usually triggered by the
+ *  Player's Snaphot feature where this filter runs on the 2 topmost layers
+ *  (or on top and BG layer)
+ *  in the snapshot image that is created and updated by the player.
+ *
+ *  2011/12/01
+ */
+/* 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
+ *  (2011/12/01)  2.7.0       hof: created
+ */
+int gap_debug = 0;  /* 1 == print debug infos , 0 dont print debug infos */
+#define GAP_DEBUG_DECLARED 1
+
+
+#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 "gimplastvaldesc.h"
+#include "gap_detail_tracking_exec.h"
+#include "gap_detail_align_exec.h"
+#include "gap_arr_dialog.h"
+
+#include "gap-intl.h"
+
+
+
+#define PLUG_IN_NAME        GAP_DETAIL_TRACKING_PLUG_IN_NAME
+#define PLUG_IN_NAME_CFG    "gap-detail-tracking-config"
+#define PLUG_IN_BINARY      "gap_detail_tracking"
+#define PLUG_IN_PRINT_NAME  "Detail Tracking"
+#define PLUG_IN_IMAGE_TYPES "RGB*"
+#define PLUG_IN_AUTHOR      "Wolfgang Hofer (hof gimp org)"
+#define PLUG_IN_COPYRIGHT   "Wolfgang Hofer"
+#define PLUG_IN_HELP_ID     "gap-plug-detail-tracking"
+
+
+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 */
+
+
+/* Global Variables */
+GimpPlugInInfo PLUG_IN_INFO =
+{
+  NULL,   /* init_proc  */
+  NULL,   /* quit_proc  */
+  query,  /* query_proc */
+  run     /* run_proc   */
+};
+
+
+FilterValues     fiVals;
+XmlAlignValues   xaVals;
+
+
+static const GimpParamDef in_args[] =
+{
+    { GIMP_PDB_INT32,    "run-mode",      "Interactive, non-interactive" },
+    { GIMP_PDB_IMAGE,    "image",         "Input image"                  },
+    { GIMP_PDB_DRAWABLE, "drawable",      "ignored"               },
+    { GIMP_PDB_INT32,    "refShapeRadius",      "radius in pixels to identify the reference shape." },
+    { GIMP_PDB_INT32,    "targetMoveRadius",    "maximal expected movement radius. "
+                                                "(the shape is searched in the target layer only within this radius." },
+    { GIMP_PDB_FLOAT,    "loacteColodiffThreshold",     "0.0 upto 1.0 threshold that defines tolerated average colordiff for successful detail tracking."
+                                                        " ." },
+    { GIMP_PDB_INT32,    "coordsRelToFrame1",    "1 .. substract coords of initial position from all recorded positions."
+                                                 "     (e.g. recording starts with px=0 py=0) "
+                                                 "0 .. record absolute positions" },
+    { GIMP_PDB_INT32,    "offsX",                "fix X offset (is added to all recorded positions)" },
+    { GIMP_PDB_INT32,    "offsY",                "fix Y offset (is added to all recorded positions)" },
+    { GIMP_PDB_FLOAT,    "offsRotate",           "fix rotation offset in degree (is added to all rotation values)" },
+    { GIMP_PDB_INT32,    "enableScaling",        "1: calculate scaling and rotation when 2 points are tracked."
+                                                 "0: calculate only rotation when 2 points are tracked." },
+    { GIMP_PDB_INT32,    "bgLayerIsReference",   "1: BG layer is used as reference layer for detail tracking, "
+                                                 "   (this is typically the 1st frame of the sequence)."
+                                                 "0: The layer below the foreground layer is used as reference."
+                                                 "   (this is typically the previous frame of the sequence)" },
+    { GIMP_PDB_INT32,    "removeMidlayers",      "1: delete all layers except BG layer and 2 layer on top of the layerstack, "
+                                                 "0: do not delete anything and keep all layers." },
+    { GIMP_PDB_STRING,   "moveLogFile",          "optional name of a move path controlpoint xml file. (use - to write to stdout) " }
+};
+
+static const GimpParamDef in_xml_args[] =
+{
+    { GIMP_PDB_INT32,    "run-mode",      "Interactive, non-interactive" },
+    { GIMP_PDB_IMAGE,    "image",         "Input image"                  },
+    { GIMP_PDB_DRAWABLE, "drawable",      "layer to be aligned"               },
+    { GIMP_PDB_INT32,    "framePhase",    "frame number e.g. phase to render (1 upto n recorded points in the xml file)." },
+    { GIMP_PDB_STRING,   "moveLogFile",          "optional name of a move path controlpoint xml file. (use - to write to stdout) " }
+};
+
+static const GimpParamDef in_exalign_args[] =
+{
+    { GIMP_PDB_INT32,    "run-mode",      "Interactive, non-interactive" },
+    { GIMP_PDB_IMAGE,    "image",         "Input image"                  },
+    { GIMP_PDB_DRAWABLE, "drawable",      "layer to be aligned"          }
+};
+
+
+
+
+static const GimpParamDef return_vals[] = {
+    { GIMP_PDB_DRAWABLE, "drawable",      "unused" }
+};
+
+static gint global_number_in_args = G_N_ELEMENTS (in_args);
+static gint global_number_out_args = G_N_ELEMENTS (return_vals);
+static gint global_number_in_xml_args = G_N_ELEMENTS (in_xml_args);
+static gint global_number_in_exalign_args = G_N_ELEMENTS (in_exalign_args);
+
+
+
+
+/* Functions */
+
+MAIN ()
+
+static void query (void)
+{
+  static GimpLastvalDef lastvals[] =
+  {
+    GIMP_LASTVALDEF_GINT32       (GIMP_ITER_FALSE,  fiVals.refShapeRadius,             "refShapeRadius"),
+    GIMP_LASTVALDEF_GINT32       (GIMP_ITER_FALSE,  fiVals.targetMoveRadius,           "targetMoveRadius"),
+    GIMP_LASTVALDEF_GDOUBLE      (GIMP_ITER_FALSE,  fiVals.loacteColodiffThreshold,    "loacteColodiffThreshold"),
+    GIMP_LASTVALDEF_GBOOLEAN     (GIMP_ITER_FALSE,  fiVals.coordsRelToFrame1,          "coordsRelToFrame1"),
+    GIMP_LASTVALDEF_GINT32       (GIMP_ITER_TRUE,   fiVals.offsX,                      "offsX"),
+    GIMP_LASTVALDEF_GINT32       (GIMP_ITER_TRUE,   fiVals.offsY,                      "offsY"),
+    GIMP_LASTVALDEF_GDOUBLE      (GIMP_ITER_TRUE,   fiVals.offsRotate,                 "offsRotate"),
+    GIMP_LASTVALDEF_GBOOLEAN     (GIMP_ITER_FALSE,  fiVals.enableScaling,              "enableScaling"),
+    GIMP_LASTVALDEF_GBOOLEAN     (GIMP_ITER_FALSE,  fiVals.bgLayerIsReference,         "bgLayerIsReference"),
+    GIMP_LASTVALDEF_GBOOLEAN     (GIMP_ITER_FALSE,  fiVals.removeMidlayers,            "removeMidlayers"),
+    GIMP_LASTVALDEF_ARRAY        (GIMP_ITER_FALSE,  fiVals.moveLogFile,                "moveLogFileArray"),
+    GIMP_LASTVALDEF_GCHAR        (GIMP_ITER_FALSE,  fiVals.moveLogFile[0],             "moveLogFileChar"),
+
+  };
+
+  static GimpLastvalDef xaLastvals[] =
+  {
+    GIMP_LASTVALDEF_GINT32       (GIMP_ITER_TRUE,   xaVals.framePhase,                 "framePhase"),
+    GIMP_LASTVALDEF_ARRAY        (GIMP_ITER_FALSE,  xaVals.moveLogFile,                "moveLogFileArray"),
+    GIMP_LASTVALDEF_GCHAR        (GIMP_ITER_FALSE,  xaVals.moveLogFile[0],             "moveLogFileChar"),
+
+  };
+
+  /* registration for last values buffer structure (useful for animated filter apply) */
+  gimp_lastval_desc_register(PLUG_IN_NAME,
+                             &fiVals,
+                             sizeof(fiVals),
+                             G_N_ELEMENTS (lastvals),
+                             lastvals);
+
+  gimp_lastval_desc_register(GAP_DETAIL_TRACKING_XML_ALIGNER_PLUG_IN_NAME,
+                             &xaVals,
+                             sizeof(xaVals),
+                             G_N_ELEMENTS (xaLastvals),
+                             xaLastvals);
+
+  gimp_plugin_domain_register (GETTEXT_PACKAGE, LOCALEDIR);
+
+
+  /* the actual installation of the plugin with configuration dialog */
+  gimp_install_procedure (PLUG_IN_NAME_CFG,
+                          "Locate the position of a small area of one layer in another layer.",
+                          "This filter operates on 2 layers on top of the layerstack, where "
+                          "the topmost layer is the target and the layer below acts as reference layer.  "
+                          "The position in the reference layer must be provided by the user as active path with one or 2 points. "
+                          "For proper operation, both reference and target layer must have exact image size."
+                          "The filter loactes the position of the corresponding detail within a specified radius in the target layer "
+                          "and adjusts the marked positions on the corresponding detail in the target layer. "
+                          "This new position is logged in XML format, suitable as input for the MovePath plug-in."
+                          "Note that this filter is typically invoked from the Player on the snapshot image, "
+                          "whenever the player puts the next frame on top of the snaphot image and detail tracking is enabled. "
+                          "Detail tracking can record the unwanted camera movements in a static scene of a video shot freehand (without a stativ) "
+                          "Applying the recorded movements with the MovePath feature can compensate such unwanted movements. "
+                          " ",
+                          PLUG_IN_AUTHOR,
+                          PLUG_IN_COPYRIGHT,
+                          GAP_VERSION_WITH_DATE,
+                          N_("DetailTracking Config..."),
+                          PLUG_IN_IMAGE_TYPES,
+                          GIMP_PLUGIN,
+                          global_number_in_args,
+                          global_number_out_args,
+                          in_args,
+                          return_vals);
+
+  gimp_install_procedure (PLUG_IN_NAME,
+                          "Non-Interactive Locate the position of a small area of one layer in another layer.",
+                          "This filter operates on 2 layers on top of the layerstack, where "
+                          "the topmost layer is the target and the layer below acts as reference layer.  "
+                          "The position in the reference layer must be provided by the user as active path with one or 2 points. "
+                          "For proper operation, both reference and target layer must have exact image size."
+                          "The filter loactes the position of the corresponding detail within a specified radius in the target layer "
+                          "and adjusts the marked positions on the corresponding detail in the target layer. "
+                          "This new position is logged in XML format, suitable as input for the MovePath plug-in."
+                          "Note that this filter is typically invoked from the Player on the snapshot image, "
+                          "whenever the player puts the next frame on top of the snaphot image and detail tracking is enabled. "
+                          "Detail tracking can record the unwanted camera movements in a static scene of a video shot freehand (without a stativ) "
+                          "Applying the recorded movements with the MovePath feature can compensate such unwanted movements. "
+                          " ",
+                          PLUG_IN_AUTHOR,
+                          PLUG_IN_COPYRIGHT,
+                          GAP_VERSION_WITH_DATE,
+                          N_("DetailTracking"),
+                          PLUG_IN_IMAGE_TYPES,
+                          GIMP_PLUGIN,
+                          global_number_in_args,
+                          global_number_out_args,
+                          in_args,
+                          return_vals);
+
+  /* the  installation of the xml based aligner plugin */
+  gimp_install_procedure (GAP_DETAIL_TRACKING_XML_ALIGNER_PLUG_IN_NAME,
+                          "Exact Align Layer via transformation according to current phase of detail tracking (recorded in XML file) .",
+                          "This filter tranforms the specified layer. "
+                          "It uses the relevant controlpoint (that matches the framePhase parameter) in the recorded XML file as input.  "
+                          "and calculates offsts, scaling and rotation to transform the layer in a way that the points p1x p1y p2x p2y "
+                          "will exactly match with the points p1x p1y p2x p2y of the 1st controlpoint in the XML file."
+                          "(calling this filter with framePhase 1 does no transformation) "
+                          "This filter is intended to run under control of the gimp-gap frames modify feature "
+                          "to align multiple frames according to the controlpoints recorde in an XML file (via Detail tracking feature)."
+                          " ",
+                          PLUG_IN_AUTHOR,
+                          PLUG_IN_COPYRIGHT,
+                          GAP_VERSION_WITH_DATE,
+                          N_("Align Transform via XML file..."),
+                          PLUG_IN_IMAGE_TYPES,
+                          GIMP_PLUGIN,
+                          global_number_in_xml_args,
+                          global_number_out_args,
+                          in_xml_args,
+                          return_vals);
+
+  /* the  installation of the 4-point path based aligner plugin */
+  gimp_install_procedure (GAP_EXACT_ALIGNER_PLUG_IN_NAME,
+                          "Exact Align Layer via transformation according 4 points specified in the current path.",
+                          "This filter expects a current path with 4 points as input where point 1 and 2 mark positions "
+                          "within a reference layer and points 3 and 4 mark 2 corresponding point in the target layer. "
+                          "The transformation is applied to the target layer and sets offsets, scaling and rotation "
+                          "in a way that point3 is placed on position of point1, and point4 is placed on position of point2."
+                          " "
+                          " ",
+                          PLUG_IN_AUTHOR,
+                          PLUG_IN_COPYRIGHT,
+                          GAP_VERSION_WITH_DATE,
+                          N_("Exact Align via 4-Point Path."),
+                          PLUG_IN_IMAGE_TYPES,
+                          GIMP_PLUGIN,
+                          global_number_in_exalign_args,
+                          global_number_out_args,
+                          in_exalign_args,
+                          return_vals);
+
+
+  {
+    /* Menu names */
+    const char *menupath_image_layer_enhance = N_("<Image>/Video/Layer/Enhance/");
+    const char *menupath_image_layer_transform = N_("<Image>/Layer/Transform/");
+
+    gimp_plugin_menu_register (PLUG_IN_NAME_CFG, menupath_image_layer_enhance);
+    gimp_plugin_menu_register (PLUG_IN_NAME, menupath_image_layer_enhance);
+    gimp_plugin_menu_register (GAP_DETAIL_TRACKING_XML_ALIGNER_PLUG_IN_NAME, menupath_image_layer_enhance);
+    gimp_plugin_menu_register (GAP_EXACT_ALIGNER_PLUG_IN_NAME, menupath_image_layer_transform);
+  }
+
+}  /* end query */
+
+
+static void
+runExactAlign (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 */
+{
+  gint32       image_id = -1;
+  gint32       activeDrawableId = -1;
+  gboolean     doFlush;
+
+  /* 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];
+
+  doFlush = TRUE;
+
+  /* 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;
+  activeDrawableId = param[2].data.d_drawable;
+
+
+
+  if (status == GIMP_PDB_SUCCESS)
+  {
+
+    gimp_image_undo_group_start (image_id);
+
+
+    /* Run the main function */
+    values[1].data.d_drawable =
+          gap_detail_exact_align_via_4point_path(image_id, activeDrawableId);
+
+    gimp_image_undo_group_end (image_id);
+
+    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 (doFlush)
+    {
+      gimp_displays_flush ();
+    }
+
+
+  }
+  values[0].data.d_status = status;
+
+}       /* end runExactAlign */
+
+
+
+static void
+runXmlAlign (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 */
+{
+  gint32       image_id = -1;
+  gint32       activeDrawableId = -1;
+  gboolean doProgress;
+  gboolean doFlush;
+  GapLastvalAnimatedCallInfo  animCallInfo;
+
+  /* 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];
+
+  doProgress = FALSE;
+  doFlush = 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;
+
+  /* init default values and Possibly retrieve data from a previous interactive run */
+  gap_detail_xml_align_get_values(&xaVals);
+
+  /* get image and drawable */
+  image_id = param[1].data.d_int32;
+  activeDrawableId = param[2].data.d_drawable;
+
+
+  /* how are we running today? */
+  switch (run_mode)
+  {
+    case GIMP_RUN_INTERACTIVE:
+      {
+        gboolean dialogOk;
+
+        dialogOk = gap_detail_xml_align_dialog(&xaVals);
+        if( dialogOk != TRUE)
+        {
+          status = GIMP_PDB_CALLING_ERROR;
+        }
+
+      }
+      doProgress = TRUE;
+      doFlush =  TRUE;
+      break;
+
+    case GIMP_RUN_NONINTERACTIVE:
+      /* check to see if invoked with the correct number of parameters */
+      if (nparams == global_number_in_args)
+      {
+          xaVals.framePhase               = param[3].data.d_int32;
+          xaVals.moveLogFile[0] = '\0';
+          if(param[4].data.d_string != NULL)
+          {
+            g_snprintf(xaVals.moveLogFile, sizeof(xaVals.moveLogFile) -1, "%s", param[4].data.d_string);
+          }
+
+      }
+      else
+      {
+        status = GIMP_PDB_CALLING_ERROR;
+      }
+
+      break;
+
+    case GIMP_RUN_WITH_LAST_VALS:
+      animCallInfo.animatedCallInProgress = FALSE;
+      gimp_get_data(GAP_LASTVAL_KEY_ANIMATED_CALL_INFO, &animCallInfo);
+
+      if(animCallInfo.animatedCallInProgress != TRUE)
+      {
+        doProgress = TRUE;
+        doFlush =  TRUE;
+      }
+      else
+      {
+        if(gap_debug)
+        {
+          // TODO never saw this in tests ...
+          printf("animCallInfo.total_steps: %d current_step:%f\n"
+            ,(int)animCallInfo.total_steps
+            ,(float)animCallInfo.current_step
+            );
+        }
+        if(xaVals.framePhase == 1)
+        {
+          /* apply with constant value framePhase 1 does not make sense
+           * Therefore use current_step as framePhase
+           */
+          xaVals.framePhase = animCallInfo.current_step;
+        }
+
+      }
+      break;
+
+    default:
+      break;
+  }
+
+  if (status == GIMP_PDB_SUCCESS)
+  {
+
+    gimp_image_undo_group_start (image_id);
+
+
+    /* Run the main function */
+    values[1].data.d_drawable =
+          gap_detail_xml_align(activeDrawableId, &xaVals);
+
+    gimp_image_undo_group_end (image_id);
+
+    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 (doFlush)
+    {
+      gimp_displays_flush ();
+    }
+
+
+  }
+  values[0].data.d_status = status;
+
+}       /* end runXmlAlign */
+
+
+
+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;
+  gint32       activeDrawableId = -1;
+  gboolean doProgress;
+  gboolean doFlush;
+  GapLastvalAnimatedCallInfo  animCallInfo;
+
+
+  /* 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);
+  }
+
+
+
+  if(strcmp(name, GAP_DETAIL_TRACKING_XML_ALIGNER_PLUG_IN_NAME) == 0)
+  {
+    runXmlAlign(name, nparams, param, nreturn_vals, return_vals);
+    return;
+  }
+
+  if(strcmp(name, GAP_EXACT_ALIGNER_PLUG_IN_NAME) == 0)
+  {
+    runExactAlign(name, nparams, param, nreturn_vals, return_vals);
+    return;
+  }
+
+
+  doProgress = FALSE;
+  doFlush = 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;
+
+  /* init default values and Possibly retrieve data from a previous interactive run */
+  gap_detail_tracking_get_values(&fiVals);
+
+  /* get image and drawable */
+  image_id = param[1].data.d_int32;
+  activeDrawableId = param[2].data.d_drawable;
+
+
+  /* how are we running today? */
+  switch (run_mode)
+  {
+    case GIMP_RUN_INTERACTIVE:
+      /* detail tracking primary feature is intended to work without dialog interaction
+       * when ivoked by menu or keyboard shortcut using PLUG_IN_NAME.
+       * This plug in also registers with a 2nd variant PLUG_IN_NAME_CFG
+       * where the user can configure the options (for one gimp session)
+       */
+      if(strcmp(name, PLUG_IN_NAME_CFG) ==0)
+      {
+        gboolean dialogOk;
+
+        if (fiVals.coordsRelToFrame1)
+        {
+          /* default offsets for handle at center */
+          fiVals.offsX = gimp_image_width(image_id) / 2.0;
+          fiVals.offsY = gimp_image_height(image_id) / 2.0;
+        }
+
+
+        dialogOk = gap_detail_tracking_dialog(&fiVals);
+        if( dialogOk != TRUE)
+        {
+          status = GIMP_PDB_CALLING_ERROR;
+        }
+
+      }
+      doProgress = TRUE;
+      doFlush =  TRUE;
+      break;
+
+    case GIMP_RUN_NONINTERACTIVE:
+      /* check to see if invoked with the correct number of parameters */
+      if (nparams == global_number_in_args)
+      {
+          fiVals.refShapeRadius               = param[3].data.d_int32;
+          fiVals.targetMoveRadius             = param[4].data.d_int32;
+          fiVals.loacteColodiffThreshold      = param[5].data.d_float;
+          fiVals.coordsRelToFrame1            = (param[6].data.d_int32 == 0) ? FALSE : TRUE;
+          fiVals.offsX                        = param[7].data.d_int32;
+          fiVals.offsY                        = param[8].data.d_int32;
+          fiVals.offsRotate                   = param[9].data.d_float;
+          fiVals.enableScaling                = (param[10].data.d_int32 == 0) ? FALSE : TRUE;
+          fiVals.bgLayerIsReference           = (param[11].data.d_int32 == 0) ? FALSE : TRUE;
+          fiVals.removeMidlayers              = (param[12].data.d_int32 == 0) ? FALSE : TRUE;
+
+          fiVals.moveLogFile[0] = '\0';
+          if(param[13].data.d_string != NULL)
+          {
+            g_snprintf(fiVals.moveLogFile, sizeof(fiVals.moveLogFile) -1, "%s", param[13].data.d_string);
+          }
+
+      }
+      else
+      {
+        status = GIMP_PDB_CALLING_ERROR;
+      }
+
+      break;
+
+    case GIMP_RUN_WITH_LAST_VALS:
+      animCallInfo.animatedCallInProgress = FALSE;
+      gimp_get_data(GAP_LASTVAL_KEY_ANIMATED_CALL_INFO, &animCallInfo);
+
+      if(animCallInfo.animatedCallInProgress != TRUE)
+      {
+        doProgress = TRUE;
+        doFlush =  TRUE;
+      }
+      break;
+
+    default:
+      break;
+  }
+
+  if (status == GIMP_PDB_SUCCESS)
+  {
+    gulong cache_ntiles;
+    gulong regionTileWidth;
+    gulong regionTileHeight;
+
+    gimp_image_undo_group_start (image_id);
+
+    /* this plug in repeatedly accesses the same tiles in the same pixel regionsarea
+     * therefore tile caching is essential for performance reason
+     * therefore calculate optimal tile cache size (but limit to 300 tiles that should be enogh
+     * in most practical use cases)
+     */
+    regionTileWidth = 1 + (2 * (fiVals.targetMoveRadius + fiVals.refShapeRadius))/ gimp_tile_width() ;
+    regionTileHeight = 1 + (2 * (fiVals.targetMoveRadius + fiVals.refShapeRadius))/ gimp_tile_height() ;
+
+    /* processing may track 2 details in different regions of same size */
+    cache_ntiles = (regionTileWidth * regionTileHeight) * 2;
+
+    gimp_tile_cache_ntiles (MAX(300, cache_ntiles));
+
+    /* Run the main function */
+    values[1].data.d_drawable =
+          gap_track_detail_on_top_layers(image_id, doProgress, &fiVals);
+
+    gimp_image_undo_group_end (image_id);
+
+    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 (doFlush)
+    {
+      gimp_displays_flush ();
+    }
+
+
+  }
+  values[0].data.d_status = status;
+
+}       /* end run */
+
diff --git a/gap/gap_edge_detection.c b/gap/gap_edge_detection.c
index 6d198e8..f37a808 100644
--- a/gap/gap_edge_detection.c
+++ b/gap/gap_edge_detection.c
@@ -36,6 +36,8 @@
 #include "gap_image.h"
 #include "gap_layer_copy.h"
 #include "gap_libgapbase.h"
+#include "gap_edge_detection.h"
+#include "gap_pdb_calls.h"
 
 #define OPACITY_LEVEL_UCHAR 50
 
@@ -56,6 +58,81 @@ typedef struct GapEdgeContext { /* nickname: ectx */
   } GapEdgeContext;
 
 
+/* ----------------------------------
+ * p_get_debug_coords_from_guides
+ * ----------------------------------
+ * get debug coordinaztes from 1st horizontal and vertical guide crossing.
+ *
+ * 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_debug_coords_from_guides(gint32 image_id, gint *cx, gint *cy)
+{
+  gint32  guide_id;
+  gint    guideRow;
+  gint    guideCol;
+
+  guide_id = 0;
+
+  guideRow = -1;
+  guideCol = -1;
+
+  if(image_id < 0)
+  {
+     return;
+  }
+
+
+  while(TRUE)
+  {
+    guide_id = gimp_image_find_next_guide(image_id, guide_id);
+
+    if (guide_id < 1)
+    {
+       break;
+    }
+    else
+    {
+       gint32 orientation;
+
+       orientation = gimp_image_get_guide_orientation(image_id, guide_id);
+       if(orientation != 0)
+       {
+         if(guideCol < 0)
+         {
+           guideCol = gimp_image_get_guide_position(image_id, guide_id);
+         }
+       }
+       else
+       {
+         if(guideRow < 0)
+         {
+           guideRow = gimp_image_get_guide_position(image_id, guide_id);
+         }
+       }
+    }
+
+  }
+
+  *cx = guideCol;
+  *cy = guideRow;
+
+  //if(gap_debug)
+  {
+    printf("image_id:%d  guideCol:%d :%d\n"
+       ,(int)image_id
+       ,(int)guideCol
+       ,(int)guideRow
+       );
+  }
+
+}  /* end p_get_debug_coords_from_guides */
+
+
+
+
 
 /* ---------------------------------
  * p_edgeProcessingForOneRegion
@@ -419,3 +496,309 @@ gint32 gap_edgeDetection(gint32  refDrawableId
   return (ectx->edgeDrawableId);
   
 }  /* end gap_edgeDetection */
+
+
+
+
+/*
+ * Stuff for the alternative algorithm:
+ *
+ * ----------------------------------------------
+ * Edge detection via Difference to Blurred Copy 
+ * ----------------------------------------------
+ * 
+ */
+ 
+ 
+ /* ---------------------------------
+  * p_colordiffProcessingForOneRegion
+  * ---------------------------------
+  * subtract RGB channels of the refPR from edgePR
+  * and set edgePR pixels to desaturated result of the subtraction.
+  * (desaturation is done by lightness)
+  */
+ static void
+ p_colordiffProcessingForOneRegion (const GimpPixelRgn *edgePR
+                     , const GimpPixelRgn *refPR
+                     , const GimpPixelRgn *ref2PR
+                     , gdouble threshold01f, gboolean invert
+                     , gint cx, gint cy
+                     )
+ {
+   guint    row;
+   guchar* ref = refPR->data;
+   guchar* ref2 = ref2PR->data;
+   guchar* edge = edgePR->data;
+   gdouble colordiff;
+   
+   if(gap_debug)
+   {
+     printf("p_colordiffProcessingForOneRegion START Edge:w:%d h:%d x:%d y:%d  Ref:w:%d h:%d x:%d y:%d \n"
+        ,edgePR->w
+        ,edgePR->h
+        ,edgePR->x
+        ,edgePR->y
+        ,refPR->w
+        ,refPR->h
+        ,refPR->x
+        ,refPR->y
+         );
+   }
+   
+   for (row = 0; row < edgePR->h; row++)
+   {
+     guint  col;
+     guint  idxref;
+     guint  idxref2;
+     guint  idxedge;
+ 
+     idxref = 0;
+     idxref2 = 0;
+     idxedge = 0;
+     for(col = 0; col < edgePR->w; col++)
+     {
+       gint value;
+       gboolean  debugPrint;
+       gdouble colordiff1;
+       gdouble colordiff2;
+ 
+       debugPrint = FALSE;
+       
+       if((cx == edgePR->x + col)
+       && (cy == edgePR->y + row))
+       {
+         debugPrint = TRUE;
+         printf("threshold01f:%.4f\n", (float)threshold01f);
+       }
+       
+//        colordiff = gap_colordiff_simple_guchar(&ref[idxref]
+//                      , &edge[idxref]
+//                      , debugPrint    /* debugPrint */
+//                      );
+//        colordiff = gap_colordiff_guchar(&ref[idxref]
+//                             ,  &edge[idxref]
+// 			    , 1.15            /* gdouble color sensitivity 1.0 to 2.0 */
+//                             , debugPrint
+//                             );
+       colordiff1 = gap_colordiff_hvmax_guchar(&ref[idxref]
+                   , &edge[idxref]
+                   , debugPrint
+                   );
+       colordiff2 = gap_colordiff_hvmax_guchar(&ref2[idxref]
+                   , &edge[idxref]
+                   , debugPrint
+                   );
+       colordiff = MAX(colordiff1, colordiff2);          
+       value = 0;
+       if(colordiff > threshold01f)
+       {
+         gdouble valuef;
+         
+         valuef = colordiff * 255.0;
+         value = CLAMP(valuef, 0, 255);
+       }
+
+       if (invert)
+       {
+         value = 255 - value;
+       }
+       
+       if(debugPrint)
+       {
+         printf("value: %d\n"
+               ,(int)value
+               );
+       }
+       
+       edge[idxedge]    = value;
+       edge[idxedge +1] = value;
+       edge[idxedge +2] = value;
+ 
+ 
+       idxref += refPR->bpp;
+       idxref2 += ref2PR->bpp;
+       idxedge += edgePR->bpp;
+     }
+ 
+     ref += refPR->rowstride;
+     ref2 += ref2PR->rowstride;
+     edge += edgePR->rowstride;
+ 
+   }
+ 
+ 
+ }  /* end p_colordiffProcessingForOneRegion */
+ 
+ 
+ /* ----------------------------------------
+  * p_subtract_ref_layer
+  * ----------------------------------------
+  * setup pixel regions and perform edge detection by subtracting RGB channels 
+  * of the orignal (refDrawable) from the blurred copy (edgeDrawable)
+  * and convert the rgb differences to lightness.
+  *
+  * as result of this processing in the edgeDrawable contains a desaturated
+  * colordifference of the original versus blured copy.
+  */
+ static void
+ p_subtract_ref_layer(gint32 image_id, GimpDrawable *edgeDrawable, GimpDrawable *refDrawable
+   , gdouble threshold, gint32 shift, gboolean invert)
+ {
+   GimpPixelRgn edgePR;
+   GimpPixelRgn refPR;
+   GimpPixelRgn ref2PR;
+   gpointer  pr;
+   gdouble   threshold01f;
+   gdouble   threshold255f;
+   gint      threshold255;
+   gint      cx;
+   gint      cy;
+   
+   threshold01f = CLAMP((threshold / 100.0), 0, 1);
+   threshold255f = 255.0 * threshold01f;
+   threshold255 = threshold255f;
+
+   p_get_debug_coords_from_guides(image_id, &cx, &cy);
+   
+   gimp_pixel_rgn_init (&edgePR, edgeDrawable, 0, 0
+                       , edgeDrawable->width - shift, edgeDrawable->height - shift
+                       , TRUE     /* dirty */
+                       , FALSE    /* shadow */
+                        );
+
+   /* start at shifted offset 0/+1 */ 
+   gimp_pixel_rgn_init (&refPR, refDrawable, 0, shift
+                       , refDrawable->width - shift, refDrawable->height - shift
+                       , FALSE     /* dirty */
+                       , FALSE     /* shadow */
+                        );
+   /* start at shifted offset +1/0 */ 
+   gimp_pixel_rgn_init (&ref2PR, refDrawable, shift, 0
+                       , refDrawable->width - shift, refDrawable->height - shift
+                       , FALSE     /* dirty */
+                       , FALSE     /* shadow */
+                        );
+ 
+   /* compare pixel areas in tiled portions via pixel region processing loops.
+    */
+   for (pr = gimp_pixel_rgns_register (3, &edgePR, &refPR, &ref2PR);
+        pr != NULL;
+        pr = gimp_pixel_rgns_process (pr))
+   {
+     p_colordiffProcessingForOneRegion (&edgePR, &refPR, &ref2PR, threshold01f, invert, cx, cy);
+   }
+ 
+   gimp_drawable_flush (edgeDrawable);
+   gimp_drawable_update (edgeDrawable->drawable_id
+                          , 0, 0
+                          , edgeDrawable->width, edgeDrawable->height
+                          );
+ 
+ }  /* end  p_subtract_ref_layer */
+ 
+ 
+ 
+ /* ---------------------------------
+  * p_call_plug_in_gauss_iir2
+  * ---------------------------------
+  */
+ gboolean
+ p_call_plug_in_gauss_iir2(gint32 imageId, gint32 edgeLayerId, gdouble radiusX, gdouble radiusY)
+ {
+    static char     *l_called_proc = "plug-in-gauss-iir2";
+    GimpParam       *return_vals;
+    int              nreturn_vals;
+ 
+    return_vals = gimp_run_procedure (l_called_proc,
+                                  &nreturn_vals,
+                                  GIMP_PDB_INT32,     GIMP_RUN_NONINTERACTIVE,
+                                  GIMP_PDB_IMAGE,     imageId,
+                                  GIMP_PDB_DRAWABLE,  edgeLayerId,
+                                  GIMP_PDB_FLOAT,     radiusX,
+                                  GIMP_PDB_FLOAT,     radiusY,
+                                  GIMP_PDB_END);
+ 
+    if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+    {
+       gimp_destroy_params(return_vals, nreturn_vals);
+       return (TRUE);   /* OK */
+    }
+    gimp_destroy_params(return_vals, nreturn_vals);
+    printf("GAP: Error: PDB call of %s failed, d_status:%d %s\n"
+       , l_called_proc
+       , (int)return_vals[0].data.d_status
+       , gap_status_to_string(return_vals[0].data.d_status)
+       );
+    return(FALSE);
+ }       /* end p_call_plug_in_gauss_iir2 */
+ 
+ 
+ 
+ /* ---------------------------------
+  * gap_edgeDetectionByBlurDiff
+  * ---------------------------------
+  */
+ gint32
+ gap_edgeDetectionByBlurDiff(gint32 activeDrawableId, gdouble blurRadius, gdouble blurResultRadius
+   , gdouble threshold, gint32 shift, gboolean doLevelsAutostretch
+   , gboolean invert)
+ {
+   gint32 blurLayerId;
+   gint32 edgeLayerId;
+   gint32 imageId;
+   GimpDrawable *edgeDrawable;
+   GimpDrawable *refDrawable;
+   GimpDrawable *blurDrawable;
+
+ 
+ 
+ 
+   imageId = gimp_drawable_get_image(activeDrawableId);
+ 
+   edgeLayerId = gimp_layer_copy(activeDrawableId);
+   gimp_image_add_layer (imageId, edgeLayerId, 0 /* stackposition */ );
+ 
+   edgeDrawable = gimp_drawable_get(edgeLayerId);
+   refDrawable = gimp_drawable_get(activeDrawableId);
+ 
+   if(blurRadius > 0.0)
+   {
+     p_call_plug_in_gauss_iir2(imageId, edgeLayerId, blurRadius, blurRadius);
+   }
+   
+   blurDrawable = NULL;
+//    blurLayerId = gimp_layer_copy(edgeLayerId);
+//    gimp_image_add_layer (imageId, blurLayerId, 0 /* stackposition */ );
+//    blurDrawable = gimp_drawable_get(blurLayerId);
+
+   p_subtract_ref_layer(imageId, edgeDrawable, refDrawable, threshold, shift, invert);
+   //p_subtract_ref_layer(imageId, edgeDrawable, blurDrawable, threshold, shift, invert);
+   if (doLevelsAutostretch)
+   {
+     gimp_levels_stretch(edgeLayerId);
+   }
+
+   if(blurResultRadius > 0.0)
+   {
+     p_call_plug_in_gauss_iir2(imageId, edgeLayerId, blurResultRadius, blurResultRadius);
+   }
+ 
+   if(refDrawable)
+   {
+     gimp_drawable_detach(refDrawable);
+   }
+   
+   if(edgeDrawable)
+   {
+     gimp_drawable_detach(edgeDrawable);
+   }
+   if(blurDrawable)
+   {
+     gimp_drawable_detach(blurDrawable);
+   }
+ 
+   return (edgeLayerId);
+   
+ }  /* end gap_edgeDetectionByBlurDiff */
+ 
+ 
diff --git a/gap/gap_edge_detection.h b/gap/gap_edge_detection.h
index 3379f13..f0676d1 100644
--- a/gap/gap_edge_detection.h
+++ b/gap/gap_edge_detection.h
@@ -1,6 +1,3 @@
-// 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
@@ -58,6 +55,20 @@ gint32 gap_edgeDetection(gint32  refDrawableId
   );
 
 
+/* ---------------------------------
+ * gap_edgeDetectionByBlurDiff
+ * ---------------------------------
+ * create a new layer representing edges of the specified activeDrawableId.
+ * The edge detection is done based on difference versus a blured
+ * copy of the activeDrawableId.
+ * (optionalyy with auto streched levels)
+ * returns the drawable id of a newly created layer
+ */
+gint32
+gap_edgeDetectionByBlurDiff(gint32 activeDrawableId, gdouble blurRadius, gdouble blurResultRadius
+   , gdouble threshold, gint32 shift, gboolean doLevelsAutostretch
+   , gboolean invert);
+
 
 
 #endif
diff --git a/gap/gap_image.c b/gap/gap_image.c
index ec3a021..90a49b7 100644
--- a/gap/gap_image.c
+++ b/gap/gap_image.c
@@ -293,8 +293,6 @@ gap_image_merge_to_specified_layer(gint32 ref_layer_id, GimpMergeType mergemode)
     {
       for(l_idx = 0; l_idx < l_nlayers; l_idx++)
       {
-        gboolean l_visible;
-        
         if (l_layers_list[l_idx] == ref_layer_id)
         {
           gimp_drawable_set_visible(l_layers_list[l_idx], TRUE);
@@ -454,3 +452,68 @@ gap_image_remove_invisble_layers(gint32 image_id)
     g_free (l_layers_list);
   }
 }  /* end gap_image_remove_invisble_layers */
+
+
+/* ---------------------------------------
+ * gap_image_remove_all_guides
+ * ---------------------------------------
+ */
+void
+gap_image_remove_all_guides(gint32 image_id)
+{
+  gint32  guide_id;
+
+  while(TRUE)
+  {
+    guide_id = 0;  /* 0 starts find at 1st guide */
+    guide_id = gimp_image_find_next_guide(image_id, guide_id);
+    
+    if (guide_id < 1)
+    {
+       break;
+    }
+    gimp_image_delete_guide(image_id, guide_id);
+  }
+
+  
+}  /* end gap_image_remove_all_guides */
+
+
+/* ---------------------------------------
+ * gap_image_limit_layers
+ * ---------------------------------------
+ * keepTopLayers  number of layers to keep on top of the layerstack (Foreground).
+ * keepBgLayers   number of layers to keep on bottom of the layerstack (Background).
+ *
+ * Note that gimp-2.7 or later versions supports layer groups.
+ * this procedure does only check for toplevel layers
+ * and ignores layers that are nested in groups.
+ * e.g. a top level group counts as one single layer
+ * no matter how many layers und subgroups are in the toplevel group.
+ *
+ */
+void
+gap_image_limit_layers(gint32 image_id, gint keepTopLayers,  gint keepBgLayers)
+{
+  gint      l_nlayers;
+  gint32   *l_layers_list;
+
+  l_layers_list = gimp_image_get_layers(image_id, &l_nlayers);
+  if(l_layers_list != NULL)
+  {
+    int ii;
+    
+    for(ii=0; ii < l_nlayers; ii++)
+    {
+      if ((ii >= keepTopLayers)
+      &&  ((l_nlayers -ii) > keepBgLayers))
+      {
+        gimp_image_remove_layer(image_id, l_layers_list[ii]);
+      }
+    }
+    g_free (l_layers_list);
+
+  }
+}  /* end gap_image_limit_layers */
+
+
diff --git a/gap/gap_image.h b/gap/gap_image.h
index 079f9af..f716d5c 100644
--- a/gap/gap_image.h
+++ b/gap/gap_image.h
@@ -54,6 +54,8 @@ gint32    gap_image_merge_to_specified_layer(gint32 ref_layer_id, GimpMergeType
 gboolean  gap_image_set_selection_from_selection_or_drawable(gint32 image_id, gint32 ref_drawable_id
                               , gboolean force_from_drawable);
 void      gap_image_remove_invisble_layers(gint32 image_id);
+void      gap_image_remove_all_guides(gint32 image_id);
+void      gap_image_limit_layers(gint32 image_id, gint keepTopLayers,  gint keepBgLayers);
 
 
 #endif
diff --git a/gap/gap_locate.c b/gap/gap_locate.c
index 7694dfa..8664d64 100644
--- a/gap/gap_locate.c
+++ b/gap/gap_locate.c
@@ -34,6 +34,7 @@
 /* GAP includes */
 #include "gap_lib_common_defs.h"
 #include "gap_locate.h"
+#include "gap_locate2.h"
 #include "gap_colordiff.h"
 #include "gap_lib.h"
 #include "gap_image.h"
@@ -218,7 +219,6 @@ p_trimReferenceShape(GapLocateContext *lctx)
       if(isRefPixelColor)
       {
         gdouble hDif;
-        gdouble sDif;
 
         hDif = fabs(refHsv.h - currentHsv.h);
         /* normalize hue difference.
@@ -754,6 +754,12 @@ static void p_locateDetailLoop(GapLocateContext *lctx)
  * 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)
+ *
+ * NOTE: this procedure is the old implementation and shall not be used in new code.
+ *       it calls the more efficient alternative implementation
+ *       unless the user explicte sets the gimprc parameter
+ *         gap-locate-details-use-old-algorithm yes
+ * 
  */
 gdouble gap_locateDetailWithinRadius(gint32  refDrawableId
   , gint32  refX
@@ -768,6 +774,31 @@ gdouble gap_locateDetailWithinRadius(gint32  refDrawableId
 {
   GapLocateContext  locateContext;
   GapLocateContext *lctx;
+ 
+  gboolean useGapLocateOldAlgo;
+  
+  
+  useGapLocateOldAlgo =
+    gap_base_get_gimprc_gboolean_value("gap-locate-details-use-old-algorithm", FALSE);
+
+  if(useGapLocateOldAlgo == FALSE)
+  {
+    gdouble avgColordiff;
+    
+    avgColordiff =
+      gap_locateAreaWithinRadius(refDrawableId
+                                ,refX
+                                ,refY
+                                ,refShapeRadius
+                                ,targetDrawableId
+                                ,targetMoveRadius
+                                ,targetX
+                                ,targetY
+                                );
+    
+    return(avgColordiff);
+  }
+
 
   /* init context */  
   lctx = &locateContext;
diff --git a/gap/gap_locate2.c b/gap/gap_locate2.c
new file mode 100644
index 0000000..c6fa3db
--- /dev/null
+++ b/gap/gap_locate2.c
@@ -0,0 +1,604 @@
+/* gap_locate2.c
+ *    alternative implementation for locating corresponding pattern in another layer.
+ *    by hof (Wolfgang Hofer)
+ *  2011/12/03
+ *
+ */
+/* 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"
+#include "stdlib.h"
+
+/* GIMP includes */
+#include "gtk/gtk.h"
+#include "libgimp/gimp.h"
+
+/* GAP includes */
+#include "gap_pixelrgn.h"
+#include "gap_locate2.h"
+
+#define MAX_DIFF_VALUE_PER_PIXEL  (255.0 + 255.0 + 255.0)
+#define OPACITY_LEVEL_UCHAR 50
+
+extern int gap_debug;
+
+typedef struct Context {
+  gint32    refShapeRadius;
+  gint32    refX;
+  gint32    refY;
+  gint32    bestX;
+  gint32    bestY;
+  gint32    px;
+  gint32    py;
+  gint32    cancelAttemptCount;     /* debug information (for development/debug purpose) */
+  gboolean  cancelAttemptFlag;      /* indicator to cancel current attempt */
+  gboolean  isFinishedFlag;         /* indicator to cancel all further evaluations */
+  gint32  requiredPixelCount;       /* number of pixels minimum required for plausible area comparison  (1/4 of full area size)*/
+  gint32  involvedPixelCount;       /* number of pixels involved in current comparison attempt */
+  gdouble sumDiffValue;             /* summ of the RGB channel differences of the involved pixels in the current attempt */
+  gdouble currentDistance;          /* square distance from reference coords to the offset of the current attempt */
+  gint32  bestMatchingPixelCount;   /* number of pixels involved in best matching attempt */
+  gdouble bestMatchingDistance;     /* square distance from reference coords to the offset of the best matching attempt */
+  gdouble bestMatchingSumDiffValue; /* summ of the RGB channel differences of the best matching attempt */
+  gdouble veryNearDistance;         /* square of near radius to stop evaluation when exactly matching area is detected */
+  gdouble bestMatchingAvgColordiff; /* average colordiff at best matching attempt */
+  GimpDrawable *refDrawable;
+  GimpDrawable *targetDrawable;
+  
+} Context;
+
+
+/* ---------------------------------
+ * p_calculate_average_colordiff
+ * ---------------------------------
+ * calculate the average color difference for given sum of value differnces and pixel count
+ */
+static inline gdouble
+p_calculate_average_colordiff(gdouble sumDiffValue, gint32 pixelCount)
+{
+  if (pixelCount > 0)
+  {
+    gdouble avgValue;
+    gdouble averageColorDiff;
+    
+    avgValue = sumDiffValue  / (gdouble)pixelCount;
+    averageColorDiff = avgValue / MAX_DIFF_VALUE_PER_PIXEL;
+    return (averageColorDiff);
+  }
+  return (1.0);
+  
+}  /* end p_calculate_average_colordiff */
+
+
+/* ---------------------------------
+ * p_compare_regions
+ * ---------------------------------
+ * calculate summary Colorvalues difference for all opaque pixels
+ * in the compared area region.
+ */
+static void
+p_compare_regions (const GimpPixelRgn *refPR
+                  ,const GimpPixelRgn *targetPR
+                  ,Context *context)
+{
+  guint    row;
+  guchar*  ref = refPR->data;   /* the reference drawable */
+  guchar*  target = targetPR->data;   /* the target drawable */
+
+//   if(gap_debug)
+//   {
+//     printf("region REF x:%d y:%d w:%d h:%d  TARGET x:%d y:%d w:%d h:%d   px:%d py:%d\n"
+//        , (int)refPR->x
+//        , (int)refPR->y
+//        , (int)refPR->w
+//        , (int)refPR->h
+//        , (int)targetPR->x
+//        , (int)targetPR->y
+//        , (int)targetPR->w
+//        , (int)targetPR->h
+//        , (int)context->px
+//        , (int)context->py
+//        );
+//   }
+
+
+  for (row = 0; row < targetPR->h; row++)
+  {
+    guint  col;
+    guint  idxref;
+    guint  idxtarget;
+    
+    if (row >= refPR->h)
+    {
+      continue;
+    }
+
+    idxref = 0;
+    idxtarget = 0;
+    for(col = 0; col < targetPR->w; col++)
+    {
+      gboolean isCompareable;
+      
+      isCompareable = TRUE;
+      
+      if (col < refPR->w)
+      {
+        if(refPR->bpp > 3)
+        {
+          if(ref[idxref +3] < OPACITY_LEVEL_UCHAR)
+          {
+            /* transparent reference pixel is not compared */
+            isCompareable = FALSE;
+          }
+        }
+      }
+      else
+      {
+        isCompareable = FALSE;
+      }
+      
+
+
+      if(targetPR->bpp > 3)
+      {
+        if(target[idxtarget +3] < OPACITY_LEVEL_UCHAR)
+        {
+          /* transparent target pixel is not compared */
+          isCompareable = FALSE;
+        }
+      }
+
+      if (isCompareable == TRUE)
+      {
+        context->involvedPixelCount += 1;
+        context->sumDiffValue += abs(ref[idxref]    - target[idxtarget]);
+        context->sumDiffValue += abs(ref[idxref +1] - target[idxtarget +1]);
+        context->sumDiffValue += abs(ref[idxref +2] - target[idxtarget +2]);
+
+        if (context->sumDiffValue > context->bestMatchingSumDiffValue)
+        {
+          gdouble avgColodiff;
+          
+          avgColodiff =
+              p_calculate_average_colordiff(context->sumDiffValue
+                                          , context->involvedPixelCount
+                                          );
+          if(avgColodiff > context->bestMatchingAvgColordiff)
+          {
+            /* stop evaluating area at current offset on worse results */
+            context->cancelAttemptFlag = TRUE;
+            context->cancelAttemptCount += 1;
+            return;
+          }                               
+        }
+
+        
+      }
+
+      idxref    += refPR->bpp;
+      idxtarget += targetPR->bpp;
+    }
+
+    ref += refPR->rowstride;
+    target += targetPR->rowstride;
+
+  }
+
+}  /* end p_compare_regions */
+
+
+/* ---------------------------------
+ * p_calculate_distance_to_ref_coord
+ * ---------------------------------
+ * calculate the (square of) the distance between reference coordinates and px/py
+ */
+static gdouble
+p_calculate_distance_to_ref_coord(Context *context, gint32 px, gint32 py)
+{
+  gdouble squareDistance;
+  
+  squareDistance =  (context->refX - px) * (context->refX - px);
+  squareDistance += (context->refY - py) * (context->refY - py);
+  
+  return(squareDistance);
+  
+}  /* end p_calculate_distance_to_ref_coord */
+
+
+/* --------------------------------------------
+ * p_attempt_locate_at_current_offset
+ * --------------------------------------------
+ */
+void
+p_attempt_locate_at_current_offset(Context *context, gint32 px, gint32 py)
+{
+  GimpPixelRgn refPR;
+  GimpPixelRgn targetPR;
+  gpointer  pr;
+  gint      rx1, ry1, rWidth, rHeight;
+  gint      tx1, ty1, tWidth, tHeight;
+  gint      commonAreaWidth, commonAreaHeight;
+  gboolean  isIntersect;
+
+  gint    leftShapeRadius;
+  gint    upperShapeRadius;
+
+
+  if (context->isFinishedFlag)
+  {
+    return;
+  }
+
+  /* calculate processing relevant interecting reference / target rectangles */  
+
+  isIntersect =
+   gimp_rectangle_intersect((context->refX - context->refShapeRadius)  /* origin1 */
+                          , (context->refY - context->refShapeRadius)
+                          , (2 * context->refShapeRadius)               /*  width1 */
+                          , (2 * context->refShapeRadius)               /* height1 */
+                          ,0
+                          ,0
+                          ,context->refDrawable->width
+                          ,context->refDrawable->height
+                          ,&rx1
+                          ,&ry1
+                          ,&rWidth
+                          ,&rHeight
+                          );
+  if (!isIntersect)
+  {
+    return;
+  }
+
+  leftShapeRadius = context->refX - rx1;
+  upperShapeRadius = context->refY - ry1;
+
+  isIntersect =
+   gimp_rectangle_intersect((px - leftShapeRadius)  /* origin1 */
+                          , (py - upperShapeRadius)
+                          , rWidth               /*  width1 */
+                          , rHeight               /* height1 */
+                          ,0
+                          ,0
+                          ,context->targetDrawable->width
+                          ,context->targetDrawable->height
+                          ,&tx1
+                          ,&ty1
+                          ,&tWidth
+                          ,&tHeight
+                          );
+  if (!isIntersect)
+  {
+    return;
+  }
+
+  commonAreaWidth = tWidth;
+  commonAreaHeight = tHeight;
+  
+
+//   if(gap_debug)
+//   {
+//     printf("p_attempt_locate_at: px: %04d py:%04d\n"
+//            "                     rx1:%04d ry1:%04d rWidth:%d rHeight:%d\n"
+//            "                     tx1:%04d ty1:%04d tWidth:%d tHeight:%d\n"
+//            "                     commonAreaWidth:%d commonAreaHeight:%d\n"
+//       ,(int)px
+//       ,(int)py
+//       ,(int)rx1
+//       ,(int)ry1
+//       ,(int)rWidth
+//       ,(int)rHeight
+//       ,(int)tx1
+//       ,(int)ty1
+//       ,(int)tWidth
+//       ,(int)tHeight
+//       ,(int)commonAreaWidth
+//       ,(int)commonAreaHeight
+//       );
+//   }
+
+  /* rest 'per offset' values in the context */
+  context->cancelAttemptFlag = FALSE;
+  context->sumDiffValue = 0;
+  context->involvedPixelCount = 0;
+  context->currentDistance = p_calculate_distance_to_ref_coord(context, px, py);
+  context->px = px;
+  context->py = py;
+
+  gimp_pixel_rgn_init (&refPR, context->refDrawable, rx1, ry1
+                      , commonAreaWidth, commonAreaHeight
+                      , FALSE     /* dirty */
+                      , FALSE     /* shadow */
+                       );
+
+  gimp_pixel_rgn_init (&targetPR, context->targetDrawable, tx1, ty1
+                      , commonAreaWidth, commonAreaHeight
+                      , FALSE     /* dirty */
+                      , FALSE     /* shadow */
+                       );
+
+  /* compare pixel areas in tiled portions via pixel region processing loops.
+   */
+  for (pr = gimp_pixel_rgns_register (2, &refPR, &targetPR);
+       pr != NULL;
+       pr = gimp_pixel_rgns_process (pr))
+  {
+    if (context->cancelAttemptFlag)
+    {
+      break;
+    }
+    else
+    {
+      p_compare_regions(&refPR, &targetPR, context);
+    }
+  }
+
+  if (pr != NULL)
+  {
+     /* NOTE:
+      * early escaping from the loop with pr != NULL
+      * leads to memory leaks due to unbalanced tile ref/unref calls.
+      * the call to gap_gimp_pixel_rgns_unref cals unref on the current tile
+      * (in th same way as gimp_pixel_rgns_process does)
+      * but does not ref another available tile.
+      */
+    gap_gimp_pixel_rgns_unref (pr);
+  
+  }
+  
+
+  
+  if ((context->involvedPixelCount >= context->requiredPixelCount)
+  &&  (context->sumDiffValue <= context->bestMatchingSumDiffValue))
+  {
+    if((context->sumDiffValue < context->bestMatchingSumDiffValue)
+    || ( context->currentDistance < context->bestMatchingDistance))
+    {
+      context->bestMatchingSumDiffValue = context->sumDiffValue;
+      context->bestMatchingDistance = context->currentDistance;
+      context->bestMatchingPixelCount = context->involvedPixelCount;
+      context->bestX = px;
+      context->bestY = py;
+      context->bestMatchingAvgColordiff = 
+        p_calculate_average_colordiff(context->bestMatchingSumDiffValue
+                                    , context->bestMatchingPixelCount
+                                    );
+
+      if(gap_debug)
+      {
+        printf("FOUND: bestX:%d bestY:%d squareDist:%d\n"
+               "             sumDiffValues:%d pixelCount:%d bestMatchingAvgColordiff:%.5f\n"
+          , (int)context->bestX
+          , (int)context->bestY
+          , (int)context->bestMatchingDistance
+          , (int)context->bestMatchingSumDiffValue
+          , (int)context->bestMatchingPixelCount
+          , (float)context->bestMatchingAvgColordiff
+          );
+      }
+       
+      if ((context->currentDistance <= context->veryNearDistance)
+      &&  (context->sumDiffValue == 0))
+      {
+        /* stop all further attempts on exact matching area when near reference origin */
+        context->isFinishedFlag = TRUE;
+      }
+    }
+  }
+
+  
+}  /* end p_attempt_locate_at_current_offset */
+
+
+
+/* --------------------------------------------
+ * gap_locateAreaWithinRadiusWithOffset
+ * --------------------------------------------
+ * processing starts at reference coords + offest
+ * and continues outwards upto targetMoveRadius for 4 quadrants.
+ * 
+ * returns average color difference (0.0 upto 1.0)
+ *    where 0.0 indicates exact matching area
+ *      and 1.0 indicates all pixel have maximum color diff (when comaring full white agains full black area)
+ */
+gdouble
+gap_locateAreaWithinRadiusWithOffset(gint32  refDrawableId
+  , gint32  refX
+  , gint32  refY
+  , gint32  refShapeRadius
+  , gint32  targetDrawableId
+  , gint32  targetMoveRadius
+  , gint32  *targetX
+  , gint32  *targetY
+  , gint32  offsetX
+  , gint32  offsetY
+  )
+{
+  Context contextData;
+  Context *context;
+  gdouble averageColorDiff;
+  gboolean isFinishedFlag;
+  gint    idx;
+  gint    idy;
+  gdouble maxPixelCount;
+  
+  
+  *targetX = refX;
+  *targetY = refY;
+  
+  /* init Context */
+  context = &contextData;
+  context->refShapeRadius = refShapeRadius;
+  context->refX = refX;
+  context->refY = refY;
+  context->bestX = refX;
+  context->bestY = refY;
+  context->cancelAttemptCount = 0;
+  context->cancelAttemptFlag = FALSE;
+  context->isFinishedFlag = FALSE;
+  context->requiredPixelCount = MAX(1, ((refShapeRadius * refShapeRadius) / 4));
+  context->involvedPixelCount = 0;
+  context->sumDiffValue = 0;
+  context->currentDistance = 0;
+  context->bestMatchingPixelCount = 0;
+  context->veryNearDistance = (2 * 2);
+
+  context->refDrawable = gimp_drawable_get(refDrawableId);
+  context->targetDrawable = gimp_drawable_get(targetDrawableId);
+  
+  maxPixelCount = MAX(context->refDrawable->width, context->targetDrawable->width)
+                * MAX(context->refDrawable->height, context->targetDrawable->height);
+                
+  context->bestMatchingSumDiffValue = maxPixelCount * MAX_DIFF_VALUE_PER_PIXEL;
+  context->bestMatchingDistance = maxPixelCount;
+  context->bestMatchingAvgColordiff = 1.0;
+  
+  averageColorDiff = 1.0;
+  
+  for(idx = 0; idx <= targetMoveRadius; idx ++)
+  {
+    if (context->isFinishedFlag) 
+    { 
+      break; 
+    }
+
+    for(idy = 0; idy <= targetMoveRadius; idy++)
+    {
+      gint32 dx;
+      gint32 dy;
+      
+      dx = idx;
+      dy = idy;
+      p_attempt_locate_at_current_offset(context, (offsetX + refX) + dx, (offsetY + refY) +dy);
+      if (isFinishedFlag)
+      { 
+        break;
+      }
+      
+      if (idx > 0)
+      {
+        p_attempt_locate_at_current_offset(context, (offsetX + refX) - dx, (offsetY + refY) +dy);
+        if (context->isFinishedFlag) 
+        {
+          break; 
+        }
+      }
+      
+      if (idy > 0)
+      {
+        p_attempt_locate_at_current_offset(context, (offsetX + refX) + dx, (offsetY + refY) -dy);
+        if (context->isFinishedFlag) 
+        {
+          break; 
+        }
+      }
+      
+      if ((idx > 0) && (idy > 0))
+      {
+        p_attempt_locate_at_current_offset(context, (offsetX + refX) - dx, (offsetY + refY) -dy);
+        if (context->isFinishedFlag) 
+        {
+          break; 
+        }
+      }
+      
+    }
+  }
+  
+  if (context->bestMatchingPixelCount > 0)
+  {
+    *targetX = context->bestX;
+    *targetY = context->bestY;
+    averageColorDiff = context->bestMatchingAvgColordiff;
+    
+    if(gap_debug)
+    {
+      printf("gap_locateAreaWithinRadiusWithOffset Result: bestX:%d bestY:%d averageColorDiff:%.5f\n"
+             "                           sumDiffValues:%d pixelCount:%d\n"
+             "                           refX:%d refY:%d  cancelAttemptCount:%d\n"
+        , (int)context->bestX
+        , (int)context->bestY
+        , (float)averageColorDiff
+        , (int)context->bestMatchingSumDiffValue
+        , (int)context->bestMatchingPixelCount
+        , (int)context->refX
+        , (int)context->refY
+        , (int)context->cancelAttemptCount
+        );
+    }
+  }
+
+
+  if(context->refDrawable != NULL)
+  {
+    gimp_drawable_detach(context->refDrawable);
+  }
+  if(context->targetDrawable != NULL)
+  {
+    gimp_drawable_detach(context->targetDrawable);
+  }
+
+  return (averageColorDiff);
+
+}  /* end gap_locateAreaWithinRadiusWithOffset */
+
+
+/* --------------------------------------------
+ * gap_locateAreaWithinRadius
+ * --------------------------------------------
+ * processing starts at reference coords and continues
+ * outwards upto targetMoveRadius for 4 quadrants.
+ * 
+ * returns average color difference (0.0 upto 1.0)
+ *    where 0.0 indicates exact matching area
+ *      and 1.0 indicates all pixel have maximum color diff (when comaring full white agains full black area)
+ */
+gdouble
+gap_locateAreaWithinRadius(gint32  refDrawableId
+  , gint32  refX
+  , gint32  refY
+  , gint32  refShapeRadius
+  , gint32  targetDrawableId
+  , gint32  targetMoveRadius
+  , gint32  *targetX
+  , gint32  *targetY
+  )
+{
+  gdouble avgColordiff;
+  
+  avgColordiff =
+    gap_locateAreaWithinRadiusWithOffset(refDrawableId
+       , refX
+       , refY
+       , refShapeRadius
+       , targetDrawableId
+       , targetMoveRadius
+       , targetX
+       , targetY
+       , 0
+       , 0
+       );
+  return (avgColordiff);
+  
+}  /* end gap_locateAreaWithinRadius */
diff --git a/gap/gap_locate2.h b/gap/gap_locate2.h
new file mode 100644
index 0000000..dccbda2
--- /dev/null
+++ b/gap/gap_locate2.h
@@ -0,0 +1,96 @@
+/* gap_locate.h
+ *    alternative implementation for locating corresponding pattern in another layer.
+ *    by hof (Wolfgang Hofer)
+ *  2011/12/03
+ *
+ */
+/* 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_LOCATE2_H
+#define _GAP_LOCATE2_H
+
+/* SYTEM (UNIX) includes */
+#include <stdio.h>
+#include <stdlib.h>
+
+/* GIMP includes */
+#include "gtk/gtk.h"
+#include "libgimp/gimp.h"
+
+
+/* ----------------------------------------
+ * gap_locateAreaWithinRadius
+ * ----------------------------------------
+ *
+ * the locateAreaWithinRadius 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 rgb pixel values of the 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)
+ * 
+ */
+gdouble
+gap_locateAreaWithinRadius(gint32  refDrawableId
+  , gint32  refX
+  , gint32  refY
+  , gint32  refShapeRadius
+  , gint32  targetDrawableId
+  , gint32  targetMoveRadius
+  , gint32  *targetX
+  , gint32  *targetY
+  );
+
+
+/* --------------------------------------------
+ * gap_locateAreaWithinRadiusWithOffset
+ * --------------------------------------------
+ * processing starts at reference coords + offest
+ * and continues outwards upto targetMoveRadius for 4 quadrants.
+ * 
+ * returns average color difference (0.0 upto 1.0)
+ *    where 0.0 indicates exact matching area
+ *      and 1.0 indicates all pixel have maximum color diff (when comaring full white agains full black area)
+ */
+gdouble
+gap_locateAreaWithinRadiusWithOffset(gint32  refDrawableId
+  , gint32  refX
+  , gint32  refY
+  , gint32  refShapeRadius
+  , gint32  targetDrawableId
+  , gint32  targetMoveRadius
+  , gint32  *targetX
+  , gint32  *targetY
+  , gint32  offsetX
+  , gint32  offsetY
+  );
+
+
+#endif
diff --git a/gap/gap_mov_dialog.h b/gap/gap_mov_dialog.h
index 23f0577..d52c2a6 100755
--- a/gap/gap_mov_dialog.h
+++ b/gap/gap_mov_dialog.h
@@ -46,7 +46,8 @@
 
 #define   GAP_MOVPATH_XML_FILENAME_MAX_LENGTH     1024
 #define   GAP_MOVEPATH_GIMPRC_LOG_RENDER_PARAMS "video-move-path-log-render-params"
-
+#define   GAP_MOVEPATH_GIMPRC_ROTATE_THRESHOLD  "video-move-path-rotate-threshold"
+#define   GAP_MOVEPATH_DEFAULT_ROTATE_THRESHOLD  0.015
 
 typedef enum
 {
@@ -261,6 +262,8 @@ typedef struct {
         GapBlueboxGlobalParams *bbp;
         GapBlueboxGlobalParams *bbp_pv;
 
+        gdouble  rotate_threshold;
+
 
 } GapMovValues;
 
diff --git a/gap/gap_mov_exec.c b/gap/gap_mov_exec.c
index f4de61e..af62297 100755
--- a/gap/gap_mov_exec.c
+++ b/gap/gap_mov_exec.c
@@ -1390,19 +1390,21 @@ p_log_current_render_params(GapMovData *mov_ptr, GapMovCurrent *cur_ptr)
                 "       currX:%f currY:%f\n"
                 "       Width:%f Height:%f\n"
                 "       Opacity:%f  Rotate:%f  clip_to_img:%d force_visibility:%d\n"
-                "       src_stepmode:%d handleX:%d handleY:%d currSelFeatherRadius:%f\n",
+                "       src_stepmode:%d handleX:%d handleY:%d currSelFeatherRadius:%f rotate_threshold:%f\n",
                      cur_ptr->dst_frame_nr, (int)val_ptr->twix, cur_ptr->src_layer_idx,
-                     cur_ptr->currX, cur_ptr->currY,
-                     cur_ptr->currWidth,
-                     cur_ptr->currHeight,
-                     cur_ptr->currOpacity,
-                     cur_ptr->currRotation,
+                     (float)cur_ptr->currX,
+                     (float)cur_ptr->currY,
+                     (float)cur_ptr->currWidth,
+                     (float)cur_ptr->currHeight,
+                     (float)cur_ptr->currOpacity,
+                     (float)cur_ptr->currRotation,
                      val_ptr->clip_to_img,
                      val_ptr->src_force_visible,
                      val_ptr->src_stepmode,
                      cur_ptr->l_handleX,
                      cur_ptr->l_handleY,
-                     cur_ptr->currSelFeatherRadius
+                     (float)cur_ptr->currSelFeatherRadius,
+                     (float)val_ptr->rotate_threshold
                      );
 
     printf("       Perspective Factors: [0] %.3f %.3f  [1] %.3f %.3f  [2] %.3f %.3f  [3] %.3f %.3f\n"
@@ -3854,6 +3856,26 @@ void gap_mov_exec_set_handle_offsets(GapMovValues *val_ptr, GapMovCurrent *cur_p
 }       /* end gap_mov_exec_set_handle_offsets */
 
 
+/* ------------------------------------
+ * gap_mov_exec_new_GapMovValues
+ * ------------------------------------
+ */
+gdouble
+gap_mov_exec_get_default_rotate_threshold()
+{
+  gdouble rotate_threshold;
+  
+  
+  rotate_threshold = 
+    gap_base_get_gimprc_gdouble_value (GAP_MOVEPATH_GIMPRC_ROTATE_THRESHOLD
+                                       , GAP_MOVEPATH_DEFAULT_ROTATE_THRESHOLD
+                                       , 0.0  /* gdouble min_value */
+                                       , 1.0  /* gdouble max_value */
+                                       );
+
+  return (rotate_threshold);
+}  /* end gap_mov_exec_get_default_rotate_threshold */
+
 
 /* ------------------------------------
  * gap_mov_exec_new_GapMovValues
@@ -3866,6 +3888,7 @@ GapMovValues *gap_mov_exec_new_GapMovValues()
   pvals = g_new (GapMovValues, 1);
 
   pvals->version = GAP_MOV_INT_VERSION;
+  pvals->rotate_threshold = gap_mov_exec_get_default_rotate_threshold();
   pvals->recordedFrameWidth = 0;     /* witdh of the frame (at recording time of the move path settings) */
   pvals->recordedFrameHeight = 0;    /* height of the frame (at recording time of the move path settings) */
   pvals->recordedObjWidth = 0;
diff --git a/gap/gap_mov_exec.h b/gap/gap_mov_exec.h
index 53e1948..0886eb0 100644
--- a/gap/gap_mov_exec.h
+++ b/gap/gap_mov_exec.h
@@ -55,6 +55,7 @@ gint    gap_mov_exec_gap_load_pointfile(char *filename, GapMovValues *pvals);
 void    gap_mov_exec_calculate_rotate_follow(GapMovValues *pvals, gdouble startangle);
 void    gap_mov_exec_set_handle_offsets(GapMovValues *val_ptr, GapMovCurrent *cur_ptr);
 void    gap_mov_exec_query(GapMovValues *val_ptr, GapAnimInfo *ainfo_ptr, GapMovQuery *mov_query);
+gdouble gap_mov_exec_get_default_rotate_threshold();
 
 GapMovValues *gap_mov_exec_new_GapMovValues();
 
diff --git a/gap/gap_mov_render.c b/gap/gap_mov_render.c
index e0173e6..ea49cb8 100644
--- a/gap/gap_mov_render.c
+++ b/gap/gap_mov_render.c
@@ -739,7 +739,7 @@ gap_mov_render_render(gint32 image_id, GapMovValues *val_ptr, GapMovCurrent *cur
     printf("gap_mov_render_render: frame/layer: %ld/%ld  X=%f, Y=%f\n"
                 "       Width=%f Height=%f\n"
                 "       Opacity=%f  Rotate=%f  clip_to_img = %d force_visibility = %d\n"
-                "       src_stepmode = %d\n"
+                "       src_stepmode = %d rotate_threshold=%.7f\n"
 		"       singleMovObjLayerId=%d singleMovObjIMAGEId=%d (frame)image_id=%d\n",
                      cur_ptr->dst_frame_nr, cur_ptr->src_layer_idx,
                      cur_ptr->currX, cur_ptr->currY,
@@ -750,6 +750,7 @@ gap_mov_render_render(gint32 image_id, GapMovValues *val_ptr, GapMovCurrent *cur
                      val_ptr->clip_to_img,
                      val_ptr->src_force_visible,
                      val_ptr->src_stepmode,
+                     val_ptr->rotate_threshold,
 		     cur_ptr->singleMovObjLayerId,
 		     gimp_drawable_get_image(cur_ptr->singleMovObjLayerId),
 		     image_id
@@ -995,7 +996,8 @@ gap_mov_render_render(gint32 image_id, GapMovValues *val_ptr, GapMovCurrent *cur
   }
 
 
-  if((cur_ptr->currRotation  > 0.5) || (cur_ptr->currRotation < -0.5))
+  if((cur_ptr->currRotation  > val_ptr->rotate_threshold) 
+  || (cur_ptr->currRotation <  (0.0 - val_ptr->rotate_threshold)))
   {
     gboolean     l_interpolation;
 
diff --git a/gap/gap_mov_xml_par.c b/gap/gap_mov_xml_par.c
index 0aa15d3..9fbf402 100755
--- a/gap/gap_mov_xml_par.c
+++ b/gap/gap_mov_xml_par.c
@@ -119,6 +119,7 @@
 #define GAP_MOVPATH_XML_TOKEN_CONTROLPOINTS          "controlpoints"
 #define GAP_MOVPATH_XML_TOKEN_CURRENT_POINT          "current_point"
 #define GAP_MOVPATH_XML_TOKEN_NUMBER_OF_POINTS       "number_of_points"
+#define GAP_MOVPATH_XML_TOKEN_ROTATE_THRESHOLD       "rotate_threshold"
 #define GAP_MOVPATH_XML_TOKEN_CONTROLPOINT           "controlpoint"
 #define GAP_MOVPATH_XML_TOKEN_PX                     "px"
 #define GAP_MOVPATH_XML_TOKEN_PY                     "py"
@@ -819,6 +820,10 @@ p_xml_parse_element_controlpoints(const gchar         *element_name,
     {
       userDataPtr->isParseOk = gap_xml_parse_value_gint(*value_cursor, &userDataPtr->pvals->point_idx);
     }
+    else if (strcmp (*name_cursor, GAP_MOVPATH_XML_TOKEN_ROTATE_THRESHOLD) == 0)
+    {
+      userDataPtr->isParseOk = gap_xml_parse_value_gdouble(*value_cursor, &userDataPtr->pvals->rotate_threshold);
+    }
     else if (strcmp (*name_cursor, GAP_MOVPATH_XML_TOKEN_NUMBER_OF_POINTS) == 0)
     {
       gint numberOfPoints;
@@ -1164,6 +1169,7 @@ p_copy_transformed_values(GapMovValues *dstValues, GapMovValues *srcValues
   gint ii;
   
   dstValues->version = srcValues->version;
+  dstValues->rotate_threshold = srcValues->rotate_threshold;
   dstValues->recordedFrameWidth = srcValues->recordedFrameWidth;
   dstValues->recordedFrameHeight = srcValues->recordedFrameHeight;
   dstValues->recordedObjWidth = srcValues->recordedObjWidth;
@@ -1315,6 +1321,7 @@ gap_mov_xml_par_load(const char *filename, GapMovValues *productiveValues
   gError = NULL;
   tmpValues = gap_mov_exec_new_GapMovValues();
   tmpValues->dst_image_id = productiveValues->dst_image_id;
+  tmpValues->rotate_threshold = productiveValues->rotate_threshold;
   userDataPtr = g_new(GapMovXmlUserData, 1);
   userDataPtr->pvals = tmpValues;
 
@@ -1557,6 +1564,7 @@ gap_mov_xml_par_save(char *filename, GapMovValues *pvals)
     fprintf(l_fp, "  <%s ", GAP_MOVPATH_XML_TOKEN_CONTROLPOINTS);
     gap_xml_write_int_value(l_fp, GAP_MOVPATH_XML_TOKEN_CURRENT_POINT, pvals->point_idx);
     gap_xml_write_int_value(l_fp, GAP_MOVPATH_XML_TOKEN_NUMBER_OF_POINTS, pvals->point_idx_max +1);
+    gap_xml_write_gdouble_value(l_fp, GAP_MOVPATH_XML_TOKEN_ROTATE_THRESHOLD, pvals->rotate_threshold, 1, 7);
     fprintf(l_fp, " >\n");
 
     /* check for conditonal write 
diff --git a/gap/gap_pixelrgn.c b/gap/gap_pixelrgn.c
new file mode 100644
index 0000000..2f08ce8
--- /dev/null
+++ b/gap/gap_pixelrgn.c
@@ -0,0 +1,151 @@
+/* gap_pixelrgn.c
+ *    extension for libgimp pixel region processing.
+ *    by hof (Wolfgang Hofer)
+ *  2011/12/07
+ *
+ * NOTE: This module deals with libgimp internal private structures
+ * and therefore should become part of libgimp in future versions.
+ * TODO: when future libgimp versions provide comparable functionality
+ *       use this module as wrapper to call libgimp
+ *
+ */
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimppixelrgn.c
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "libgimp/gimp.h"
+#include "gap_pixelrgn.h"
+
+typedef struct _GimpPixelRgnHolder    GimpPixelRgnHolder;
+typedef struct _GimpPixelRgnIterator  GimpPixelRgnIterator;
+
+struct _GimpPixelRgnHolder
+{
+  GimpPixelRgn *pr;
+  guchar       *original_data;
+  gint          startx;
+  gint          starty;
+  gint          count;
+};
+
+struct _GimpPixelRgnIterator
+{
+  GSList *pixel_regions;
+  gint    region_width;
+  gint    region_height;
+  gint    portion_width;
+  gint    portion_height;
+  gint    process_count;
+};
+
+/**
+ * gap_gimp_pixel_rgns_unref:
+ * @pri_ptr: a regions iterator returned by #gimp_pixel_rgns_register,
+ *           #gimp_pixel_rgns_register2 or #gimp_pixel_rgns_process.
+ *
+ * This function cleans up by unref tiles that were initialized in a
+ * loop constuct with gimp_pixel_rgns_register and gimp_pixel_rgns_process calls.
+ * It is intended for clean escape from such a loop at early time
+ * e.g. before all tiles are processed.
+ *
+ * Note: leaving such a loop construct with break or return
+ *        without clean up keeps tile references alive
+ *       and leads to memory leaks.
+ * Usage CODE example:
+ *
+ *  for (pr = gimp_pixel_rgns_register (2, &PR1, &PR2);
+ *      pr != NULL;
+ *      pr = gimp_pixel_rgns_process (pr))
+ *  {
+ *      ... evaluate pixel data in pixel regions PR1, PR2
+ *      ...
+ *      if (further evaluation of the remaining tiles is not necessary)
+ *      {
+ *        // escaping from the loop without the call
+ *        // to gap_gimp_pixel_rgns_unref
+ *        // leads to memory leaks because of unbalanced tile ref/unref calls.
+ *        break;
+ *      }
+ *  }
+ *  if (pr != NULL)
+ *  {
+ *    gap_gimp_pixel_rgns_unref(pr);
+ *  }
+ **/
+void
+gap_gimp_pixel_rgns_unref (gpointer pri_ptr)
+{
+  GimpPixelRgnIterator *pri;
+  GSList               *list;
+
+  g_return_val_if_fail (pri_ptr != NULL, NULL);
+
+  pri = (GimpPixelRgnIterator*) pri_ptr;
+  pri->process_count++;
+
+  /*  Unref all referenced tiles and increment the offsets  */
+
+  for (list = pri->pixel_regions; list; list = list->next)
+    {
+      GimpPixelRgnHolder *prh = list->data;
+
+      if ((prh->pr != NULL) && (prh->pr->process_count != pri->process_count))
+        {
+          /*  This eliminates the possibility of incrementing the
+           *  same region twice
+           */
+          prh->pr->process_count++;
+
+          /*  Unref the last referenced tile if the underlying region
+           *  is a tile manager
+           */
+          if (prh->pr->drawable)
+            {
+              GimpTile *tile = gimp_drawable_get_tile2 (prh->pr->drawable,
+                                                        prh->pr->shadow,
+                                                        prh->pr->x,
+                                                        prh->pr->y);
+              gimp_tile_unref (tile, prh->pr->dirty);
+            }
+
+          prh->pr->x += pri->portion_width;
+
+          if ((prh->pr->x - prh->startx) >= pri->region_width)
+            {
+              prh->pr->x  = prh->startx;
+              prh->pr->y += pri->portion_height;
+            }
+        }
+    }
+
+ 
+
+  /*  free the pixel regions list  */
+  for (list = pri->pixel_regions; list; list = list->next)
+    {
+      g_slice_free (GimpPixelRgnHolder, list->data);
+    }
+
+  g_slist_free (pri->pixel_regions);
+  g_slice_free (GimpPixelRgnIterator, pri);
+
+
+
+}
diff --git a/gap/gap_pixelrgn.h b/gap/gap_pixelrgn.h
new file mode 100644
index 0000000..6e54de7
--- /dev/null
+++ b/gap/gap_pixelrgn.h
@@ -0,0 +1,36 @@
+/* gap_pixelrgn.h
+ *    extension for libgimp pixel region processing.
+ *    by hof (Wolfgang Hofer)
+ *  2011/12/07
+ *
+ */
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimppixelrgn.c
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ */
+#ifndef _GAP_PIXELRGN_H
+#define _GAP_PIXELRGN_H
+
+#include "libgimp/gimp.h"
+
+void gap_gimp_pixel_rgns_unref (gpointer pri_ptr);
+
+#endif
+
diff --git a/gap/gap_player_dialog.c b/gap/gap_player_dialog.c
index e42f1b2..25bc2ce 100644
--- a/gap/gap_player_dialog.c
+++ b/gap/gap_player_dialog.c
@@ -112,6 +112,7 @@
 #include "gap_audio_extract.h"
 #include "gap_audio_extract.h"
 #include "gap_drawable_vref_parasite.h"
+#include "gap_detail_tracking_exec.h"
 
 #include "gap-intl.h"
 
@@ -346,6 +347,7 @@ static guchar * p_fetch_videoframe(GapPlayerMainGlobalParams *gpp
                    , gint32 *th_height
                    , gboolean isBackwards
                    );
+static void     p_init_tile_cache(GapPlayerMainGlobalParams *gpp);
 static void     p_init_video_playback_cache(GapPlayerMainGlobalParams *gpp);
 static void     p_init_layout_options(GapPlayerMainGlobalParams *gpp);
 static guchar * p_fetch_frame_via_cache(GapPlayerMainGlobalParams *gpp
@@ -1247,10 +1249,29 @@ p_mtrace_image_alive(GapPlayerMainGlobalParams *gpp
 
 
 /* ------------------------------
+ * p_conditional_detail_tracking
+ * ------------------------------
+ * optional call detail  tracking.
+ * that logs coordinates of a detail (typically marked via 2 path vector points)
+ * as XML input file for the MovePath feature.
+ */
+static void
+p_conditional_detail_tracking(GapPlayerMainGlobalParams *gpp, gint32 image_id)
+{
+  if(gpp->enableDetailTracking)
+  {
+    gap_track_detail_on_top_layers_lastvals(image_id);
+    /* flush display to force rendering of newly added layers and vectors */
+    gimp_displays_flush ();
+  }
+}  /* end p_conditional_detail_tracking */
+
+
+/* ------------------------------
  * p_conditional_attach_dvref
  * ------------------------------
  * check if there is a valid current videoreference.
- * if yes attach the reference as (temporary) videoreferenc parasite
+ * if yes attach the reference as (temporary) videoreference parasite
  * to the specified drawable_id 
  * (that is typically the layer in the mtrace image)
  */
@@ -1355,6 +1376,8 @@ p_mtrace_image( GapPlayerMainGlobalParams *gpp
       g_free(l_name);
     }
 
+    p_conditional_detail_tracking(gpp, gpp->mtrace_image_id);
+
     gimp_displays_flush();
   }
 }  /* end p_mtrace_image */
@@ -1423,6 +1446,9 @@ p_mtrace_tmpbuf( GapPlayerMainGlobalParams *gpp
       gimp_drawable_set_name(dst_layer_id, l_name);
       g_free(l_name);
     }
+
+    p_conditional_detail_tracking(gpp, gpp->mtrace_image_id);
+
     gimp_displays_flush();
   }
 }  /* end p_mtrace_tmpbuf */
@@ -3100,6 +3126,29 @@ p_fetch_videoframe(GapPlayerMainGlobalParams *gpp
 
 
 /* -----------------------------
+ * p_init_tile_cache
+ * -----------------------------
+ */
+static void
+p_init_tile_cache(GapPlayerMainGlobalParams *gpp)
+{
+  gchar *value_string;
+
+  gpp->cache_ntiles = GAP_PLAYER_MAIN_DEFAULT_CACHE_NTILES;
+
+  value_string = gimp_gimprc_query("video_player_cache_ntiles");
+  if(value_string)
+  {
+    gpp->cache_ntiles = MAX(0, atol(value_string));
+    
+    g_free(value_string);
+  }
+
+  gimp_tile_cache_ntiles(gpp->cache_ntiles);
+  
+}  /* end p_init_tile_cache */
+
+/* -----------------------------
  * p_init_video_playback_cache
  * -----------------------------
  */
@@ -3152,6 +3201,21 @@ p_init_layout_options(GapPlayerMainGlobalParams *gpp)
     g_free(value_string);
   }
 
+
+  value_string = gimp_gimprc_query("video_player_enable_detail_tracking");
+  if(value_string)
+  {
+    if ((*value_string == 'Y') || (*value_string == 'y'))
+    {
+      gpp->enableDetailTracking = TRUE;
+    }
+    else
+    {
+      gpp->enableDetailTracking = FALSE;
+    }
+
+    g_free(value_string);
+  }
 }  /* end p_init_layout_options */
 
 
@@ -5865,6 +5929,68 @@ on_show_positionscale_checkbutton_toggled(GtkToggleButton *togglebutton,
 }  /* end on_show_positionscale_checkbutton_toggled */
 
 
+/* ---------------------------------------------
+ * on_enable_detail_tracking_checkbutton_toggled
+ * ---------------------------------------------
+ */
+static void
+on_enable_detail_tracking_checkbutton_toggled(GtkToggleButton *togglebutton,
+                                        GapPlayerMainGlobalParams *gpp)
+{
+  if(gpp == NULL)
+  {
+    return;
+  }
+
+  if (togglebutton->active)
+  {
+       gpp->enableDetailTracking = TRUE;
+  }
+  else
+  {
+       gpp->enableDetailTracking = FALSE;
+  }
+
+}  /* end on_enable_detail_tracking_checkbutton_toggled */
+
+/* ---------------------------------
+ * on_detail_tracking_button_clicked
+ * ---------------------------------
+ */
+static void
+on_detail_tracking_button_clicked (GtkButton       *button,
+                                   GapPlayerMainGlobalParams *gpp)
+{
+  gboolean dtailTrackingOk;
+  if(gpp == NULL)
+  {
+    return;
+  }
+  p_stop_playback(gpp);
+  if(gpp->gva_lock)
+  {
+    gpp->request_cancel_video_api = TRUE;
+    return;
+  }
+
+  dtailTrackingOk =
+    gap_detail_tracking_dialog_cfg_set_vals(gpp->mtrace_image_id);
+  
+  if(dtailTrackingOk)
+  {
+    gpp->enableDetailTracking = TRUE;
+    if(gpp->detail_tracking_checkbutton)
+    {
+      gtk_toggle_button_set_active (
+         GTK_TOGGLE_BUTTON (gpp->detail_tracking_checkbutton), TRUE);
+    }
+  }
+
+}  /* end on_detail_tracking_button_clicked */
+
+
+
+
 /* -----------------------------
  * on_close_button_clicked
  * -----------------------------
@@ -6986,6 +7112,40 @@ on_cache_size_spinbutton_changed (GtkEditable     *editable,
 
 
 /* -----------------------------------------
+ * on_tile_cache_size_spinbutton_changed
+ * -----------------------------------------
+ */
+static void
+on_tile_cache_size_spinbutton_changed (GtkEditable     *editable,
+                                  GapPlayerMainGlobalParams *gpp)
+{
+  gdouble  nsize;
+
+  if(gpp == NULL)
+  {
+    return;
+  }
+  nsize = GTK_ADJUSTMENT(gpp->cache_ntiles_spinbutton_adj)->value;
+
+  if(gap_debug)
+  {
+      printf("on_tile_cache_size_spinbutton_changed: cache_ntiles:%d newvalue:%d\n"
+              , (int) gpp->cache_ntiles
+              , (int) nsize
+              );
+  }
+
+  if(gpp->cache_ntiles != (gulong)nsize)
+  {
+    gpp->cache_ntiles = (gulong)nsize;
+
+    gimp_tile_cache_ntiles(gpp->cache_ntiles);
+  }
+
+}  /* end on_tile_cache_size_spinbutton_changed */
+
+
+/* -----------------------------------------
  * on_cache_clear_button_clicked
  * -----------------------------------------
  */
@@ -7002,13 +7162,12 @@ on_cache_clear_button_clicked (GtkButton       *button,
 }  /* end on_cache_clear_button_clicked */
 
 
-
 /* -----------------------------------------
- * p_gimprc_save_boolen_option
+ * p_gimprc_save_option
  * -----------------------------------------
  */
 static void
-p_gimprc_save_boolen_option (const char *option_name, gboolean value)
+p_gimprc_save_boolean_option (const char *option_name, gboolean value)
 {
   if(value)
   {
@@ -7018,8 +7177,7 @@ p_gimprc_save_boolen_option (const char *option_name, gboolean value)
   {
     gimp_gimprc_set(option_name, "no");
   }
-}  /* end p_gimprc_save_boolen_option */
-
+}  /* end p_gimprc_save_boolean_option */
 
 /* -----------------------------------------
  * on_prefs_save_gimprc_button_clicked
@@ -7029,16 +7187,25 @@ static void
 on_prefs_save_gimprc_button_clicked (GtkButton       *button,
                                GapPlayerMainGlobalParams *gpp)
 {
+  gchar *valueAsString;
+  
   if(gpp == NULL)
   {
     return;
   }
   gap_player_cache_set_gimprc_bytesize(gpp->max_player_cache);
 
-  p_gimprc_save_boolen_option("video_player_show_go_buttons"
+  p_gimprc_save_boolean_option("video_player_show_go_buttons"
                              ,gpp->show_go_buttons);
-  p_gimprc_save_boolen_option("video_player_show_position_scale"
+  p_gimprc_save_boolean_option("video_player_show_position_scale"
                              ,gpp->show_position_scale);
+  p_gimprc_save_boolean_option("video_player_enable_detail_tracking"
+                             ,gpp->enableDetailTracking);
+
+  valueAsString = g_strdup_printf("%d", gpp->cache_ntiles);
+  gimp_gimprc_set("video_player_cache_ntiles", valueAsString);
+  g_free(valueAsString);
+  
 
 }  /* end on_prefs_save_gimprc_button_clicked */
 
@@ -7081,7 +7248,7 @@ p_new_configframe(GapPlayerMainGlobalParams *gpp)
 
   row = 0;
 
-  /* Cahe size label */
+  /* Cache size label */
   label = gtk_label_new (_("Cache Size (MB):"));
   gtk_widget_show (label);
   gtk_table_attach (GTK_TABLE (table1), label, 0, 1,  row, row+1,
@@ -7150,6 +7317,36 @@ p_new_configframe(GapPlayerMainGlobalParams *gpp)
   gtk_progress_bar_set_text(GTK_PROGRESS_BAR(progress_bar), "0.0 MB");
   gpp->progress_bar_cache_usage = progress_bar;
 
+
+  row++;
+
+  /* tile Chache  */
+  label = gtk_label_new(_("Tile Cache:"));
+  gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
+  gtk_table_attach(GTK_TABLE(table1), label, 0, 1, row, row + 1, GTK_FILL, GTK_FILL, 0, 0);
+  gtk_widget_show(label);
+
+
+  /* tile cache size spinutton */
+  spinbutton = gimp_spin_button_new (&adj,  /* return value */
+                        gpp->cache_ntiles,         /*   initial_val */
+                        0.0,   /* umin */
+                     9000.0,   /* umax */
+                        1.0,   /* sstep */
+                       10.0,   /* pagestep */
+                       0.0,                  /* page_size */
+                       10.0,                 /* climb_rate */
+                        0                    /* digits */
+                        );
+  gtk_widget_show (spinbutton);
+  gpp->cache_ntiles_spinbutton_adj = adj;
+  gtk_table_attach(GTK_TABLE(table1), spinbutton, 1, 2, row, row + 1, GTK_FILL, GTK_FILL, 4, 0);
+  gimp_help_set_help_data(spinbutton, _("gimp tile cache for the player process. (in tiles 64x64 pixel)"),NULL);
+  g_signal_connect (G_OBJECT (gpp->cache_ntiles_spinbutton_adj), "value_changed",
+                        G_CALLBACK (on_tile_cache_size_spinbutton_changed),
+                        gpp);
+
+
   row++;
 
   /* Layout Options label */
@@ -7200,6 +7397,46 @@ p_new_configframe(GapPlayerMainGlobalParams *gpp)
 
   row++;
 
+
+
+
+
+  /* configure Detail Tracking button */
+  button = gtk_button_new_with_label(_("Configure Tracking:"));
+
+  gtk_widget_show (button);
+  gimp_help_set_help_data(button, _("Configure detail tracking options"),NULL);
+  gtk_table_attach(GTK_TABLE(table1), button, 0, 1, row, row + 1,
+                    (GtkAttachOptions) GTK_FILL,
+                    (GtkAttachOptions) GTK_FILL, 4, 0);
+  g_signal_connect (G_OBJECT (button), "pressed",
+                      G_CALLBACK (on_detail_tracking_button_clicked),
+                      gpp);
+
+  checkbutton = gtk_check_button_new_with_label (_("Enable Detail Tracking"));
+  gpp->detail_tracking_checkbutton = checkbutton;
+  gtk_widget_show (checkbutton);
+  gtk_table_attach (GTK_TABLE (table1), checkbutton, 1, 3, row, row+1,
+                    (GtkAttachOptions) (GTK_FILL),
+                    (GtkAttachOptions) (0), 0, 0);
+  gimp_help_set_help_data (checkbutton, _("ON: Enable detail tracking in snapshot image."
+                                          " Mark coordinates of one (or 2) significant details in the snapshot image"
+                                          " using the current path with one or 2 points."
+                                          " Each further snapshot automatically moves the marked points"
+                                          " to the coordinates of the corresponding details "
+                                          " and logs the movement as XML parameters for the MovePath feature."
+                                          " In case 2 points are marked, the rotation is calculated too.\n"
+                                          "OFF: Disable detail tracking."), NULL);
+  if(gpp->enableDetailTracking)
+  {
+    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (checkbutton), TRUE);
+  }
+  g_signal_connect (G_OBJECT (checkbutton), "toggled",
+                      G_CALLBACK (on_enable_detail_tracking_checkbutton_toggled),
+                      gpp);
+
+  row++;
+
   /* Save Player Preferences label */
   label = gtk_label_new (_("Save Preferences:"));
   gtk_widget_show (label);
@@ -8512,11 +8749,14 @@ gap_player_dlg_create(GapPlayerMainGlobalParams *gpp)
   gpp->shell_initial_width = -1;
   gpp->shell_initial_height = -1;
 
+  gimp_tile_cache_ntiles(gpp->cache_ntiles);
+  p_init_tile_cache(gpp);
   p_init_video_playback_cache(gpp);
   p_init_layout_options(gpp);
 
   gpp->mtrace_image_id = -1;
   gpp->mtrace_mode = GAP_PLAYER_MTRACE_OFF;
+  gpp->detail_tracking_checkbutton = NULL;
 
   gpp->vindex_creation_is_running = FALSE;
   gpp->request_cancel_video_api = FALSE;
diff --git a/gap/gap_player_main.c b/gap/gap_player_main.c
index 339b5d6..1ef9809 100644
--- a/gap/gap_player_main.c
+++ b/gap/gap_player_main.c
@@ -223,6 +223,10 @@ static GapPlayerMainGlobalParams global_params =
 , NULL                /* GtkWidget *audio_enable_checkbutton */
 
 , NULL                /* GapDrawableVideoRef  *dvref_ptr */
+, FALSE               /* gboolean     enableDetailTracking */
+, GAP_PLAYER_MAIN_DEFAULT_CACHE_NTILES  /* gulong cache_ntiles */
+, NULL                /* GtkObject *cache_ntiles_spinbutton_adj */
+, NULL                /* GtkWidget   *detail_tracking_checkbutton */
 };
 
 
diff --git a/gap/gap_player_main.h b/gap/gap_player_main.h
index b6bb01e..3514526 100644
--- a/gap/gap_player_main.h
+++ b/gap/gap_player_main.h
@@ -221,7 +221,7 @@ typedef struct GapPlayerMainGlobalParams {
 
   gint32     max_player_cache;    /* max bytesize to use for caching frames 
                                    * (at pview widget size) 
-                                   * a value of 0 turns cahing OFF
+                                   * a value of 0 turns caching OFF
                                    */
   GapPlayerCacheCompressionType cache_compression;
   gdouble                       cache_jpeg_quality;
@@ -257,9 +257,16 @@ typedef struct GapPlayerMainGlobalParams {
   GtkWidget *audio_enable_checkbutton;
 
   GapDrawableVideoRef  *dvref_ptr;
+
+  gboolean     enableDetailTracking;
+  gulong       cache_ntiles;                 /* gimp tile cache size for the player process */
+  GtkObject   *cache_ntiles_spinbutton_adj;
+  GtkWidget   *detail_tracking_checkbutton;
   
 } GapPlayerMainGlobalParams;
 
+#define GAP_PLAYER_MAIN_DEFAULT_CACHE_NTILES   200
+
 #define GAP_PLAYER_MAIN_AUSTAT_UNCHECKED       -1
 #define GAP_PLAYER_MAIN_AUSTAT_NONE             0
 #define GAP_PLAYER_MAIN_AUSTAT_SERVER_STARTED   1
diff --git a/libgapbase/gap_base.c b/libgapbase/gap_base.c
index a2ebd63..caaeb0f 100644
--- a/libgapbase/gap_base.c
+++ b/libgapbase/gap_base.c
@@ -299,6 +299,32 @@ gap_base_dup_filename_and_replace_extension_by_underscore(const char *filename)
 }  /* end gap_base_dup_filename_and_replace_extension_by_underscore */
 
 
+
+
+/* --------------------------------
+ * gap_base_gdouble_to_ascii_string
+ * --------------------------------
+ * convert the specified gdouble value to ascci string.
+ * (always use "." as decimalpoint, independent of LOCALE language settings)
+ * return the converted string. (the caller is responsible to g_free this string after usage)
+ */
+gchar *
+gap_base_gdouble_to_ascii_string(gdouble value, gint precision_digits)
+{
+  gchar *retValueAsSting;
+  
+  gchar l_dbl_str[G_ASCII_DTOSTR_BUF_SIZE];
+  gchar l_fmt_str[20];
+  gint  l_len;
+
+  g_snprintf(l_fmt_str, sizeof(l_fmt_str), "%%.%df", (int)precision_digits);
+  g_ascii_formatd(l_dbl_str, sizeof(l_dbl_str), l_fmt_str, value);
+
+  retValueAsSting = g_strdup_printf("%s", l_dbl_str);
+  return (retValueAsSting);
+}  /* end gap_base_gdouble_to_ascii_string */
+
+
 /* --------------------------------
  * gap_base_fprintf_gdouble
  * --------------------------------
@@ -328,6 +354,7 @@ gap_base_fprintf_gdouble(FILE *fp, gdouble value, gint digits, gint precision_di
 }  /* end gap_base_fprintf_gdouble */
 
 
+
 /* ============================================================================
  * gap_base_sscan_flt_numbers
  * ============================================================================
@@ -414,10 +441,48 @@ gap_base_check_tooltips(gboolean *old_state)
 
 
 /* -----------------------------------------
+ * gap_base_get_gimprc_gdouble_value
+ * -----------------------------------------
+ * get gdouble configuration value for the keyname gimprc_option_name from the gimprc file.
+ * returns the configure value in constraint to the specified range 
+ * (between min_value and max_value)
+ * the specified default_value is returned in case the gimprc
+ * has no entry for the specified gimprc_option_name.
+ */
+gdouble
+gap_base_get_gimprc_gdouble_value (const char *gimprc_option_name
+   , gdouble default_value, gdouble min_value, gdouble max_value)
+{
+  char *value_string;
+  gdouble value;
+
+  value = default_value;
+
+  value_string = gimp_gimprc_query(gimprc_option_name);
+  if(value_string)
+  {
+     gchar *endptr;
+     gchar *nptr;
+     gdouble val;
+
+     nptr  = value_string;
+     val = g_ascii_strtod(nptr, &endptr);
+     if(nptr != endptr)
+     {
+       value = val;
+     }
+     g_free(value_string);
+  }
+  return (CLAMP(value, min_value, max_value));
+
+}  /* end gap_base_get_gimprc_gdouble_value */
+
+
+/* -----------------------------------------
  * gap_base_get_gimprc_int_value
  * -----------------------------------------
  * get integer configuration value for the keyname gimprc_option_name from the gimprc file.
- * returns the configure value in constaint to the specified range 
+ * returns the configure value in constraint to the specified range 
  * (between min_value and max_value)
  * the specified default_value is returned in case the gimprc
  * has no entry for the specified gimprc_option_name.
@@ -441,7 +506,6 @@ gap_base_get_gimprc_int_value (const char *gimprc_option_name
 
 }  /* end p_get_gimprc_int_value */
 
-
 /* -----------------------------------------
  * gap_base_get_gimprc_gboolean_value
  * -----------------------------------------
diff --git a/libgapbase/gap_base.h b/libgapbase/gap_base.h
index 97e0f3c..66efa31 100644
--- a/libgapbase/gap_base.h
+++ b/libgapbase/gap_base.h
@@ -94,6 +94,18 @@ char *
 gap_base_dup_filename_and_replace_extension_by_underscore(const char *filename);
 
 
+
+/* --------------------------------
+ * gap_base_gdouble_to_ascii_string
+ * --------------------------------
+ * convert the specified gdouble value to ascci string.
+ * (always use "." as decimalpoint, independent of LOCALE language settings)
+ * return the converted string. (the caller is responsible to g_free this string after usage)
+ */
+gchar *
+gap_base_gdouble_to_ascii_string(gdouble value, gint precision_digits);
+
+
 /* --------------------------------
  * gap_base_fprintf_gdouble
  * --------------------------------
@@ -126,12 +138,24 @@ gap_base_sscan_flt_numbers(gchar   *buf
 gboolean
 gap_base_check_tooltips(gboolean *old_state);
 
+/* -----------------------------------------
+ * gap_base_get_gimprc_gdouble_value
+ * -----------------------------------------
+ * get gdouble configuration value for the keyname gimprc_option_name from the gimprc file.
+ * returns the configure value in constraint to the specified range 
+ * (between min_value and max_value)
+ * the specified default_value is returned in case the gimprc
+ * has no entry for the specified gimprc_option_name.
+ */
+gdouble
+gap_base_get_gimprc_gdouble_value (const char *gimprc_option_name
+   , gdouble default_value, gdouble min_value, gdouble max_value);
 
 /* -----------------------------------------
  * gap_base_get_gimprc_int_value
  * -----------------------------------------
  * get integer configuration value for the keyname gimprc_option_name from the gimprc file.
- * returns the configure value in constaint to the specified range 
+ * returns the configure value in constraint to the specified range 
  * (between min_value and max_value)
  * the specified default_value is returned in case the gimprc
  * has no entry for the specified gimprc_option_name.
diff --git a/libgapvidapi/gap_vid_api_ffmpeg.c b/libgapvidapi/gap_vid_api_ffmpeg.c
index 9ac10a7..28d6f20 100644
--- a/libgapvidapi/gap_vid_api_ffmpeg.c
+++ b/libgapvidapi/gap_vid_api_ffmpeg.c
@@ -144,6 +144,8 @@ typedef struct t_GVA_ffmpeg
 
   gint32        samples_buffer_size;
   guchar        *samples_buffer[2];    /* Buffer for decoded samples from samples_base_a until read position */
+  int16_t       *av_samples;           /* aligned buffer for decoding audio samples (MMX requires aligned data) */
+  int            av_samples_size;
 
   gint32        samples_base[2];       /* start offset of the samples_buffers (unit is samples) */
 
@@ -385,6 +387,8 @@ p_wrapper_ffmpeg_open_read(char *filename, t_GVA_Handle *gvahand)
   handle->aud_pkt.data = NULL;          /* start with empty packet */
   handle->samples_buffer[0] = NULL;        /* set later (at 1.st audio packet read) */
   handle->samples_buffer[1] = NULL;        /* set later (at 1.st audio packet read) */
+  handle->av_samples = NULL;               /* set later (at 1.st audio packet read) */
+  handle->av_samples_size = 0;
   handle->samples_base[0] = 0;
   handle->samples_base[1] = 0;
   handle->bytes_filled[0] = 0;
@@ -631,6 +635,13 @@ p_wrapper_ffmpeg_close(t_GVA_Handle *gvahand)
 
   handle = (t_GVA_ffmpeg *)gvahand->decoder_handle;
 
+  if(handle->av_samples)
+  {
+    av_free(handle->av_samples);
+    handle->av_samples = NULL;
+    handle->av_samples_size = 0;
+  }
+
   if(handle->samples_buffer[0])
   {
     if(gap_debug) printf("p_wrapper_ffmpeg_close: FREE audio samples_buffer\n");
@@ -3249,7 +3260,10 @@ p_wrapper_ffmpeg_get_audio(t_GVA_Handle *gvahand
     l_sample_idx = gvahand->current_sample;
   }
 
-  if(gap_debug) printf("p_wrapper_ffmpeg_get_audio  samples: %d l_sample_idx:%d\n", (int)samples, (int)l_sample_idx);
+  if(gap_debug) 
+  {
+    printf("p_wrapper_ffmpeg_get_audio  samples: %d l_sample_idx:%d\n", (int)samples, (int)l_sample_idx);
+  }
 
   l_low_sample_idx = 0;
   if (handle->samples_base[0] > 0)
@@ -3268,6 +3282,13 @@ p_wrapper_ffmpeg_get_audio(t_GVA_Handle *gvahand
      * (is not chached in the sample_buffers any more)
      * we have to reset audio read, and restart reading from the begin
      */
+    if(gap_debug)
+    {
+      printf("ffmpeg_get_audio l_sample_idx:%d  l_low_sample_idx:%d (calling reopen)\n"
+        , (int)l_sample_idx
+        , (int)l_low_sample_idx
+	);
+    }
     p_ffmpeg_aud_reopen_read(handle, gvahand);
   }
 
@@ -3285,6 +3306,11 @@ p_wrapper_ffmpeg_get_audio(t_GVA_Handle *gvahand
                 , channel
                 );
 
+  if(gap_debug) 
+  {
+    printf("p_wrapper_ffmpeg_get_audio  DONE: l_rc:%d\n", (int)l_rc);
+  }
+
   if(l_rc == 0)
   {
     if(mode_flag != GVA_AMOD_REREAD)
@@ -3794,7 +3820,13 @@ p_ff_open_input(char *filename, t_GVA_Handle *gvahand, t_GVA_ffmpeg*  handle, gb
     {
         case AVMEDIA_TYPE_AUDIO:
             gvahand->atracks++;  /* count all audiostraems as audiotrack */
-            if(gap_debug) printf("\nInput Audio channels: %d\n", acc->channels);
+            if(gap_debug)
+	    {
+	      printf("\nInput Audio Track @ streamIndex:%d channels: %d\n"
+	        , ii
+		, acc->channels
+		);
+	    }
             if((gvahand->atracks == gvahand->aud_track)
             || (gvahand->atracks == 1))
             {
@@ -3813,6 +3845,12 @@ p_ff_open_input(char *filename, t_GVA_Handle *gvahand, t_GVA_ffmpeg*  handle, gb
             break;
         case AVMEDIA_TYPE_VIDEO:
             gvahand->vtracks++; /* count all videostraems as videotrack */
+            if(gap_debug)
+	    {
+	      printf("\nInput Video Track @ streamIndex:%d\n"
+	        , ii
+		);
+	    }
             if((gvahand->vtracks == gvahand->vid_track)
             || (gvahand->vtracks == 1))
             {
@@ -4161,7 +4199,10 @@ p_ffmpeg_vid_reopen_read(t_GVA_ffmpeg *handle, t_GVA_Handle *gvahand)
 static void
 p_ffmpeg_aud_reopen_read(t_GVA_ffmpeg *handle, t_GVA_Handle *gvahand)
 {
-    if(gap_debug) printf("p_ffmpeg_aud_reopen_read: REOPEN\n");
+    if(gap_debug)
+    {
+      printf("p_ffmpeg_aud_reopen_read: REOPEN\n");
+    }
 
 
     /* CLOSE the audio codec */
@@ -4297,6 +4338,11 @@ p_pick_channel( t_GVA_ffmpeg *handle
   gint32  this_idx;
   gint32  prev_idx;
 
+  if(gap_debug)
+  {
+    printf("p_pick_channel: START channel:%d\n", (int)channel);
+  }
+
   l_samples_picked = 0;
   bytes_per_sample =  2 * gvahand->audio_cannels;
 
@@ -4317,7 +4363,10 @@ p_pick_channel( t_GVA_ffmpeg *handle
        */
       l_this_samples = (gint32)  (handle->samples_read - sample_idx);
 
-      if(gap_debug) printf("p_pick_channel(2): l_this_samples:%d\n", (int)l_this_samples);
+      if(gap_debug)
+      {
+        printf("p_pick_channel(2): l_this_samples:%d\n", (int)l_this_samples);
+      }
   }
 
   if(sample_idx >= handle->samples_base[this_idx])
@@ -4483,6 +4532,11 @@ p_read_audio_packets( t_GVA_ffmpeg *handle, t_GVA_Handle *gvahand, gint32 max_sa
    {
      printf("p_read_audio_packets: before WHILE max_sample_pos: %d\n", (int)max_sample_pos);
      printf("p_read_audio_packets: before WHILE samples_read: %d\n", (int)handle->samples_read);
+     printf("samples_buffer[0]: %d samples_buffer[1]:%d output_samples_ptr:%d\n"
+         , (int)handle->samples_buffer[0]
+         , (int)handle->samples_buffer[1]
+         , (int)handle->output_samples_ptr
+	 );
    }
 
    while(handle->samples_read < max_sample_pos)
@@ -4504,7 +4558,10 @@ p_read_audio_packets( t_GVA_ffmpeg *handle, t_GVA_Handle *gvahand, gint32 max_sa
       if(l_pktlen < 0)
       {
          /* EOF reached */
-         if (gap_debug) printf("p_read_audio_packets: EOF reached (or read ERROR)\n");
+         if (gap_debug)
+	 {
+	   printf("p_read_audio_packets: EOF reached (or read ERROR)\n");
+	 }
 
          gvahand->total_aud_samples = handle->samples_read;
          gvahand->all_samples_counted = TRUE;
@@ -4513,7 +4570,12 @@ p_read_audio_packets( t_GVA_ffmpeg *handle, t_GVA_Handle *gvahand, gint32 max_sa
          break;
       }
 
-      /*if (gap_debug)  printf("aud_stream:%d pkt.stream_index #%d, pkt.size: %d samples_read:%d\n", handle->aud_stream_index, handle->aud_pkt.stream_index, handle->aud_pkt.size, (int)handle->samples_read);*/
+      if (gap_debug)
+      {
+        printf("aud_stream:%d pkt.stream_index #%d, pkt.size: %d samples_read:%d\n"
+	   , handle->aud_stream_index, handle->aud_pkt.stream_index
+	   , handle->aud_pkt.size, (int)handle->samples_read);
+      }
 
 
       /* check if packet belongs to the selected audio stream */
@@ -4525,14 +4587,20 @@ p_read_audio_packets( t_GVA_ffmpeg *handle, t_GVA_Handle *gvahand, gint32 max_sa
          continue;
       }
 
-      /* if (gap_debug)  printf("using Packet\n"); */
+      if (gap_debug)
+      {
+        printf("using Packet stream_index:%d data:%d size:%d\n"
+	   ,(int)handle->aud_pkt.stream_index
+	   ,(int)handle->aud_pkt.data
+	   ,(int)handle->aud_pkt.size
+	   );
+      }
 
       /* packet is part of the selected video stream, use that packet */
       handle->abuf_ptr = handle->aud_pkt.data;
       handle->abuf_len = handle->aud_pkt.size;
     }
 
-    /* if (gap_debug) printf("before avcodec_decode_audio2: abuf_ptr:%d abuf_len:%d\n", (int)handle->abuf_ptr, (int)handle->abuf_len); */
 
     /* decode a frame. return -1 if error, otherwise return the number of
      * bytes used. If no audio frame could be decompressed, data_size is
@@ -4540,6 +4608,11 @@ p_read_audio_packets( t_GVA_ffmpeg *handle, t_GVA_Handle *gvahand, gint32 max_sa
      */
     data_size = handle->samples_buffer_size;
 #ifdef GAP_USES_OLD_FFMPEG_0_5
+    if (gap_debug)
+    {
+       printf("before avcodec_decode_audio2: abuf_ptr:%d abuf_len:%d\n"
+         , (int)handle->abuf_ptr, (int)handle->abuf_len);
+    }
     l_len = avcodec_decode_audio2(handle->aud_codec_context  /* AVCodecContext * */
                                ,(int16_t *)handle->output_samples_ptr
                                ,&data_size
@@ -4547,17 +4620,53 @@ p_read_audio_packets( t_GVA_ffmpeg *handle, t_GVA_Handle *gvahand, gint32 max_sa
                                ,handle->abuf_len
                                );
 #else
+  {
+    data_size = FFMAX(handle->aud_pkt.size * sizeof(int16_t), AVCODEC_MAX_AUDIO_FRAME_SIZE);
+    if (data_size > handle->av_samples_size)
+    {
+      /* force re-allocation of the aligend buffer */
+      if (handle->av_samples != NULL)
+      {
+        av_free(handle->av_samples);
+	handle->av_samples = NULL;
+      }
+    
+    }
+    if (handle->av_samples == NULL)
+    {
+      handle->av_samples = av_malloc(data_size);
+      handle->av_samples_size = data_size;
+    }
+    if(gap_debug)
+    {
+       printf("before avcodec_decode_audio3: av_samples:%d data_size:%d\n"
+	 , (int)handle->av_samples
+	 , (int)data_size
+	 );
+    }
     l_len = avcodec_decode_audio3(handle->aud_codec_context  /* AVCodecContext * */
-                               ,(int16_t *)handle->output_samples_ptr
+                               ,handle->av_samples
                                ,&data_size
                                ,&handle->aud_pkt
                                );
+    if(data_size > 0)
+    {
+      /* copy the decoded samples from the aligned av_samples buffer
+       * to current position in the samples_buffer of the relevant channel
+       * Note that calling avcodec_decode_audio3 procedure with handle->output_samples_ptr
+       * as destination pointer for the 16bit samples would be faster, 
+       * but will crash when ffmpeg is configured
+       * with enabled MMX and this pointer is not aligned.
+       */
+      memcpy(handle->output_samples_ptr, handle->av_samples, data_size);
+    }
+  }
 #endif
 
 
     if (gap_debug)
     {
-      printf("after avcodec_decode_audio2: l_len:%d data_size:%d samples_read:%d \n"
+      printf("after avcodec_decode_audioX: l_len:%d data_size:%d samples_read:%d \n"
              " sample_fmt:%d %s (expect:%d SAMPLE_FMT_S16)\n"
                          , (int)l_len
                          , (int)data_size
@@ -4570,7 +4679,7 @@ p_read_audio_packets( t_GVA_ffmpeg *handle, t_GVA_Handle *gvahand, gint32 max_sa
 
     if(l_len < 0)
     {
-       printf("p_read_audio_packets: avcodec_decode_audio2 returned ERROR)\n"
+       printf("p_read_audio_packets: avcodec_decode_audioX returned ERROR)\n"
              "abuf_len:%d AVCODEC_MAX_AUDIO_FRAME_SIZE:%d samples_buffer_size:%d data_size:%d\n"
              , (int)handle->abuf_len
              , (int)AVCODEC_MAX_AUDIO_FRAME_SIZE
@@ -4675,6 +4784,10 @@ p_read_audio_packets( t_GVA_ffmpeg *handle, t_GVA_Handle *gvahand, gint32 max_sa
 
   }  /* end while packet_read and decode audio frame loop */
 
+  if(gap_debug)
+  {
+    printf("p_read_audio_packets: DONE return code:%d\n", (int)l_rc);
+  }
   return(l_rc);
 }  /* end p_read_audio_packets */
 
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 1fb3db7..22ce58f 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -5,6 +5,7 @@ gap/gap_arr_dialog.c
 gap/gap_audio_extract.c
 gap/gap_audio_wav.c
 gap/gap_base_ops.c
+gap/gap_blend_fill_main.c
 gap/gap_bluebox.c
 gap/gap_bluebox_main.c
 gap/gap_colormask_dialog.c
@@ -14,6 +15,9 @@ gap/gap_dbbrowser_utils.c
 gap/gap_decode_mplayer_main.c
 gap/gap_decode_mplayer.c
 gap/gap_decode_xanim.c
+gap/gap_detail_align_exec.c
+gap/gap_detail_tracking_main.c
+gap/gap_detail_tracking_exec.c
 gap/gap_filter_foreach.c
 gap/gap_filter_main.c
 gap/gap_fire_pattern.c
diff --git a/vid_enc_ffmpeg/gap_enc_ffmpeg_gui.c b/vid_enc_ffmpeg/gap_enc_ffmpeg_gui.c
index f0e7037..f6d3898 100644
--- a/vid_enc_ffmpeg/gap_enc_ffmpeg_gui.c
+++ b/vid_enc_ffmpeg/gap_enc_ffmpeg_gui.c
@@ -1066,6 +1066,8 @@ p_init_vid_checkbuttons(GapGveFFMpegGlobalParams *gpp)
                                , gpp->evl.codec_FLAG2_PSY);
   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (gpp->ff_codec_FLAG2_SSIM_checkbutton)
                                , gpp->evl.codec_FLAG2_SSIM);
+  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (gpp->ff_codec_FLAG2_INTRA_REFRESH_checkbutton)
+                               , gpp->evl.codec_FLAG2_INTRA_REFRESH);
 
 
   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (gpp->ff_partition_X264_PART_I4X4_checkbutton)
@@ -2935,6 +2937,22 @@ p_create_expert_flags2_frame (GapGveFFMpegGlobalParams *gpp)
                       G_CALLBACK (on_ff_gint32_checkbutton_toggled),
                       &gpp->evl.codec_FLAG_GMC);
 
+
+    /* the INTRA_REFRESH checkbutton */
+    checkbutton = gtk_check_button_new_with_label (_("Intra Refresh"));
+    gpp->ff_codec_FLAG2_INTRA_REFRESH_checkbutton = checkbutton;
+    gtk_widget_show (checkbutton);
+    gtk_table_attach (GTK_TABLE (flags_table), checkbutton, 1, 2, flags_row, flags_row+1,
+                      (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
+                      (GtkAttachOptions) (0), 0, 0);
+    gimp_help_set_help_data (checkbutton, _("Use periodic insertion of intra blocks instead of keyframes."), NULL);
+    g_object_set_data (G_OBJECT (checkbutton), GAP_ENC_FFGUI_GPP, (gpointer)gpp);
+    g_signal_connect (G_OBJECT (checkbutton), "toggled",
+                      G_CALLBACK (on_ff_gint32_checkbutton_toggled),
+                      &gpp->evl.codec_FLAG2_INTRA_REFRESH);
+
+
+
     flags_row++;
 
     /* the input_preserved checkbutton */
diff --git a/vid_enc_ffmpeg/gap_enc_ffmpeg_main.c b/vid_enc_ffmpeg/gap_enc_ffmpeg_main.c
index 857cdd8..68c49f0 100644
--- a/vid_enc_ffmpeg/gap_enc_ffmpeg_main.c
+++ b/vid_enc_ffmpeg/gap_enc_ffmpeg_main.c
@@ -802,8 +802,9 @@ p_base_get_thread_id_as_int()
 /* ---------------------------------------
  * p_debug_print_dump_AVCodecContext
  * ---------------------------------------
- * dump values of all (200 !!) parameters
+ * dump most parameters values
  * of the codec context to stdout
+ * (some unused and deprecated parameters are skipped)
  */
 static void
 p_debug_print_dump_AVCodecContext(AVCodecContext *codecContext)
@@ -1027,6 +1028,28 @@ p_debug_print_dump_AVCodecContext(AVCodecContext *codecContext)
   printf("(psy_trellis                  %f)\n", (float)  codecContext->psy_trellis);
   printf("(rc_lookahead                 %d)\n", (int)    codecContext->rc_lookahead);
 
+#ifndef GAP_USES_OLD_FFMPEG_0_6
+  printf("(crf_max                      %f)\n", (float)  codecContext->crf_max);
+  printf("(log_level_offset             %d)\n", (int)    codecContext->log_level_offset);
+  /* attribute_deprecated enum AVLPCType lpc_type; */
+  /* attribute_deprecated int lpc_passes;          */
+  printf("(slices                       %d)\n", (int)    codecContext->slices);
+  /* uint8_t *subtitle_header; */
+  /* int subtitle_header_size; */
+  /* AVPacket *pkt */
+  /* int is_copy;  */
+  printf("(thread_type                  %d)\n", (int)    codecContext->thread_type);
+  printf("(active_thread_type           %d)\n", (int)    codecContext->active_thread_type);
+  printf("(thread_safe_callbacks        %d)\n", (int)    codecContext->thread_safe_callbacks);
+  printf("(vbv_delay                    %lld)\n",        codecContext->vbv_delay);
+  printf("(audio_service_type           %d)\n", (int)    codecContext->audio_service_type);
+  /* enum AVSampleFormat request_sample_fmt */
+  /* int64_t pts_correction_num_faulty_pts; */
+  /* int64_t pts_correction_num_faulty_dts; */
+  /* int64_t pts_correction_last_pts; */
+  /* int64_t pts_correction_last_dts; */
+
+#endif
 #endif
 
   printf("AVCodecContext Settings END\n");
@@ -1518,6 +1541,8 @@ gap_enc_ffmpeg_main_init_preset_params(GapGveFFMpegValues *epp, gint preset_idx)
   epp->codec_FLAG2_MBTREE               = 0; /* 0: FALSE */
   epp->codec_FLAG2_PSY                  = 0; /* 0: FALSE */
   epp->codec_FLAG2_SSIM                 = 0; /* 0: FALSE */
+  /* new flags/flags2 of ffmpeg-0.7.11 2012.01.12 */
+  epp->codec_FLAG2_INTRA_REFRESH        = 0; /* 0: FALSE */
 
   epp->partition_X264_PART_I4X4         = 0; /* 0: FALSE */
   epp->partition_X264_PART_I8X8         = 0; /* 0: FALSE */
@@ -1525,6 +1550,17 @@ gap_enc_ffmpeg_main_init_preset_params(GapGveFFMpegValues *epp, gint preset_idx)
   epp->partition_X264_PART_P4X4         = 0; /* 0: FALSE */
   epp->partition_X264_PART_B8X8         = 0; /* 0: FALSE */
 
+
+  /* new parameters of ffmpeg-0.7.11 2012.01.12 */
+  epp->crf_max                       = 0.0;
+  epp->log_level_offset              = 0;
+  epp->slices                        = 0;
+  epp->thread_type                   = 3;   /* FF_THREAD_FRAME   1  + FF_THREAD_SLICE   2 */
+  epp->active_thread_type            = 0;
+  epp->thread_safe_callbacks         = 0;
+  epp->vbv_delay                     = 0;
+  epp->audio_service_type            = 0;
+
   if (preset_idx >= GAP_GVE_FFMPEG_PRESET_MAX_ELEMENTS)
   {
     if(gap_debug)
@@ -2379,6 +2415,9 @@ p_init_video_codec(t_ffmpeg_handle *ffh
   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);
+#ifndef GAP_USES_OLD_FFMPEG_0_6
+  p_set_flag(epp->codec_FLAG2_INTRA_REFRESH,       &video_enc->flags2, CODEC_FLAG2_INTRA_REFRESH);
+#endif
 #endif
 
 
@@ -2556,6 +2595,16 @@ p_init_video_codec(t_ffmpeg_handle *ffh
   video_enc->psy_rd = (float)epp->psy_rd;
   video_enc->psy_trellis = (float)epp->psy_trellis;
   video_enc->rc_lookahead = (int)epp->rc_lookahead;
+#ifndef GAP_USES_OLD_FFMPEG_0_6
+  video_enc->crf_max = (float)epp->crf_max;
+  video_enc->log_level_offset = (int)epp->log_level_offset;
+  video_enc->slices = (int)epp->slices;
+  video_enc->thread_type = (int)epp->thread_type;
+  video_enc->active_thread_type = (int)epp->active_thread_type;
+  video_enc->thread_safe_callbacks = (int)epp->thread_safe_callbacks;
+  video_enc->vbv_delay = epp->vbv_delay;
+  video_enc->audio_service_type = epp->audio_service_type;
+#endif
 #endif
 
 
diff --git a/vid_enc_ffmpeg/gap_enc_ffmpeg_main.h b/vid_enc_ffmpeg/gap_enc_ffmpeg_main.h
index 8f8d1f1..099144e 100644
--- a/vid_enc_ffmpeg/gap_enc_ffmpeg_main.h
+++ b/vid_enc_ffmpeg/gap_enc_ffmpeg_main.h
@@ -38,14 +38,18 @@
 #include "avformat.h"
 #include "avcodec.h"
 
-/// start ffmpeg 0.5 / 0.6 support
+/// start ffmpeg 0.5 / 0.6 / 0.7 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
+#if LIBAVCODEC_VERSION_MINOR <= 72
+#define GAP_USES_OLD_FFMPEG_0_6
+#endif
 #endif
 
 
@@ -403,6 +407,15 @@ typedef struct {
   gdouble psy_trellis;       // float psy_trellis;
   gint32  rc_lookahead;      // int rc_lookahead;
 
+  /* new params ffmpeg-0.7.11 */
+  gdouble crf_max;               // float crf_max;
+  gint32  log_level_offset;      // int log_level_offset;
+  gint32  slices;                // int slices;
+  gint32  thread_type;           // int thread_type;
+  gint32  active_thread_type;    // int active_thread_type;
+  gint32  thread_safe_callbacks; // int thread_safe_callbacks;
+  gint64  vbv_delay;             // uint64_t vbv_delay;
+  gint32  audio_service_type;    // enum AVAudioServiceType  audio_service_type;
 
 
   gint32 codec_FLAG_GMC;
@@ -430,6 +443,7 @@ typedef struct {
   gint32 codec_FLAG2_MBTREE;
   gint32 codec_FLAG2_PSY;
   gint32 codec_FLAG2_SSIM;
+  gint32 codec_FLAG2_INTRA_REFRESH;
 
   gint32 partition_X264_PART_I4X4;
   gint32 partition_X264_PART_I8X8;
@@ -564,6 +578,7 @@ typedef struct GapGveFFMpegGlobalParams {   /* nick: gpp */
   GtkWidget *ff_codec_FLAG2_MBTREE_checkbutton;
   GtkWidget *ff_codec_FLAG2_PSY_checkbutton;
   GtkWidget *ff_codec_FLAG2_SSIM_checkbutton;
+  GtkWidget *ff_codec_FLAG2_INTRA_REFRESH_checkbutton;
 
   GtkWidget *ff_partition_X264_PART_I4X4_checkbutton;
   GtkWidget *ff_partition_X264_PART_I8X8_checkbutton;
diff --git a/vid_enc_ffmpeg/gap_enc_ffmpeg_par.c b/vid_enc_ffmpeg/gap_enc_ffmpeg_par.c
index 955fd4d..4f85072 100644
--- a/vid_enc_ffmpeg/gap_enc_ffmpeg_par.c
+++ b/vid_enc_ffmpeg/gap_enc_ffmpeg_par.c
@@ -234,6 +234,16 @@ p_set_master_keywords(GapValKeyList *keylist, GapGveFFMpegValues *epp)
    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");
 
+   /* new params (introduced with ffmpeg 0.7.11  2012.01.12) */
+   gap_val_set_keyword(keylist, "(crf_max ",                  &epp->crf_max,               GAP_VAL_GDOUBLE, 0, "# Constant rate factor maximum");
+   gap_val_set_keyword(keylist, "(log_level_offset ",         &epp->log_level_offset,      GAP_VAL_GINT32, 0,  "#");
+   gap_val_set_keyword(keylist, "(slices ",                   &epp->slices,                GAP_VAL_GINT32, 0,  "# Indicates number of picture subdivisions");
+   gap_val_set_keyword(keylist, "(thread_type ",              &epp->thread_type,           GAP_VAL_GINT32, 0,  "# Which multithreading methods to use (FF_THREAD_FRAME 1, FF_THREAD_SLICE 2) ");
+   gap_val_set_keyword(keylist, "(active_thread_type ",       &epp->active_thread_type,    GAP_VAL_GINT32, 0,  "# Which multithreading methods are in use by the codec.");
+   gap_val_set_keyword(keylist, "(thread_safe_callbacks ",    &epp->thread_safe_callbacks, GAP_VAL_GINT32, 0,  "# Set by the client if its custom get_buffer() callback can be called from another thread");
+   gap_val_set_keyword(keylist, "(vbv_delay ",                &epp->vbv_delay,             GAP_VAL_GINT32, 0,  "# VBV delay coded in the last frame (in periods of a 27 MHz clock)");
+   gap_val_set_keyword(keylist, "(audio_service_type ",       &epp->audio_service_type,    GAP_VAL_GINT32, 0,  "# Type of service that the audio stream conveys.");
+
    /* codec flags */
 
    p_set_keyword_bool32(keylist, "(bitexact ",                &epp->bitexact,            "# CODEC_FLAG_BITEXACT (for testing, unused in productive version)");
@@ -292,6 +302,9 @@ p_set_master_keywords(GapValKeyList *keylist, GapGveFFMpegValues *epp)
    p_set_keyword_bool32(keylist, "(partp8x8 ",                &epp->partition_X264_PART_P4X4,           "# X264_PART_P4X4  Analyze p8x4, p4x8, p4x4");
    p_set_keyword_bool32(keylist, "(partb8x8 ",                &epp->partition_X264_PART_B8X8,           "# X264_PART_B8X8  Analyze b16x8, b8x16 and b8x8");
 
+   /* codec flags new in ffmpeg-0.7.11 */
+   p_set_keyword_bool32(keylist, "(use_bit_intra_refresh ",   &epp->codec_FLAG2_INTRA_REFRESH,          "# CODEC_FLAG2_INTRA_REFRESH       Use periodic insertion of intra blocks instead of keyframes.");
+
 }  /* end p_set_master_keywords */
 
 



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