gimp-gap r770 - in trunk: . gap libgapvidutil



Author: wolfgangh
Date: Thu Jul 31 18:05:59 2008
New Revision: 770
URL: http://svn.gnome.org/viewvc/gimp-gap?rev=770&view=rev

Log:
new feature: filtermacro mapping to persitent drawable ids

Added:
   trunk/gap/gap_drawable_vref_parasite.c
   trunk/gap/gap_drawable_vref_parasite.h
   trunk/gap/gap_fmac_context.c
   trunk/gap/gap_fmac_context.h
   trunk/gap/gap_frame_fetcher.c
   trunk/gap/gap_frame_fetcher.h
Modified:
   trunk/NEWS
   trunk/gap/Makefile.am
   trunk/gap/gap_filter_iterators.c
   trunk/gap/gap_filter_pdb.c
   trunk/gap/gap_filter_pdb.h
   trunk/gap/gap_fmac_base.c
   trunk/gap/gap_fmac_main.c
   trunk/gap/gap_player_dialog.c
   trunk/gap/gap_player_main.c
   trunk/gap/gap_player_main.h
   trunk/gap/gap_story_dialog.c
   trunk/gap/gap_story_file.c
   trunk/gap/gap_story_render_processor.c
   trunk/gap/gap_story_render_types.h
   trunk/gap/gap_story_vthumb.c
   trunk/libgapvidutil/gap_gve_story.h

Modified: trunk/NEWS
==============================================================================
--- trunk/NEWS	(original)
+++ trunk/NEWS	Thu Jul 31 18:05:59 2008
@@ -1,3 +1,33 @@
+Here is a short overview whats new in GIMP-GAP-2.5.0:
+-----------------------------------------------------
+(compared to gimp-gap release 2.4.0)
+
+- Player supports extracting audio track when playing clips
+  that refere to a videofile that has one or more audiotrack(s)
+
+- Filtermacro processing now supports mapping to 
+  persistent drawable id references.
+  This allows applying recorded filtermacros in another gimp session
+  in case the recorded last values buffer data contains references
+  to drawable ids.
+  such as the drawable for a bump map in the plug_in_bump_map,
+  or all the layers that were used to map on a quader in plug_in_map_object.
+ 
+  The persistent references can refer to anim frames (series of numbered
+  images), multilayer images, or videofiles.
+
+  Filtermacros can be applied with fixed or varying values
+  in Storyboard clips. For varying values 2 filtermacros 
+  are required to provide from and to values. Iteration of
+  persistent drawable ids is supported in case both refer to the
+  same image, anim frame series or the same videofile.
+
+- viedo extract supports generating transparency via bluebox effect.
+
+- Modify Frames now supports creation of grayscale layer from
+  alpha channel, or layermask or mix of both
+
+
 Here is a short overview whats new in GIMP-GAP-2.3.0:
 -----------------------------------------------------
 (compared to gimp-gap release 2.2.1)

Modified: trunk/gap/Makefile.am
==============================================================================
--- trunk/gap/Makefile.am	(original)
+++ trunk/gap/Makefile.am	Thu Jul 31 18:05:59 2008
@@ -70,8 +70,12 @@
 
 
 libgapstory_a_SOURCES = $(BASE_SOURCES)	\
+	gap_frame_fetcher.c		\
+	gap_frame_fetcher.h		\
 	gap_fmac_name.c		\
 	gap_fmac_name.h		\
+	gap_fmac_context.c	\
+	gap_fmac_context.h	\
 	gap_story_file.h		\
 	gap_story_file.c		\
 	gap_story_render_types.h	\
@@ -158,6 +162,12 @@
 	gap_filter_main.c	\
 	gap_filter_pdb.c	\
 	gap_filter_pdb.h	\
+	gap_drawable_vref_parasite.c	\
+	gap_drawable_vref_parasite.h	\
+	gap_fmac_context.c	\
+	gap_fmac_context.h	\
+	gap_frame_fetcher.c	\
+	gap_frame_fetcher.h	\
 	gap_lastvaldesc.c	\
 	gap_lastvaldesc.h	\
 	gap_libgimpgap.h	
@@ -170,11 +180,17 @@
 	gap_filter_foreach.c	\
 	gap_filter_iterators.c	\
 	gap_filter_iterators.h	\
+	gap_drawable_vref_parasite.c	\
+	gap_drawable_vref_parasite.h	\
 	gap_fmac_main.c	\
 	gap_filter_pdb.c	\
 	gap_filter_pdb.h	\
+	gap_frame_fetcher.c	\
+	gap_frame_fetcher.h	\
 	gap_fmac_name.c		\
 	gap_fmac_name.h		\
+	gap_fmac_context.c	\
+	gap_fmac_context.h	\
 	gap_fmac_base.c		\
 	gap_fmac_base.h		\
 	gap_lastvaldesc.c	\
@@ -185,11 +201,17 @@
 	gap_filter.h		\
 	gap_filter_iterators.c	\
 	gap_filter_iterators.h	\
+	gap_drawable_vref_parasite.c	\
+	gap_drawable_vref_parasite.h	\
 	gap_fmac_varying_main.c	\
 	gap_filter_pdb.c	\
 	gap_filter_pdb.h	\
+	gap_frame_fetcher.c	\
+	gap_frame_fetcher.h	\
 	gap_fmac_name.c		\
 	gap_fmac_name.h		\
+	gap_fmac_context.c	\
+	gap_fmac_context.h	\
 	gap_fmac_base.c		\
 	gap_fmac_base.h		\
 	gap_lastvaldesc.c	\
@@ -239,6 +261,8 @@
 	gap_player_cache.h	\
 	gap_audio_extract.c	\
 	gap_audio_extract.h	\
+	gap_drawable_vref_parasite.c	\
+	gap_drawable_vref_parasite.h	\
 	gap_libgapstory.h	\
 	gap_libgimpgap.h	
 
@@ -275,6 +299,8 @@
 	gap_player_dialog.h	\
 	gap_player_cache.c	\
 	gap_player_cache.h	\
+	gap_drawable_vref_parasite.c	\
+	gap_drawable_vref_parasite.h	\
 	gap_libgapstory.h	\
 	gap_libgimpgap.h	
 
@@ -294,6 +320,8 @@
 	gap_player_dialog.h	\
 	gap_player_cache.c	\
 	gap_player_cache.h	\
+	gap_drawable_vref_parasite.c	\
+	gap_drawable_vref_parasite.h	\
 	gap_libgapstory.h	\
 	gap_libgimpgap.h	
 
@@ -352,9 +380,9 @@
 
 gap_plugins_LDADD =          $(LIBGIMPGAP)  $(LIBGAPBASE) $(GIMP_LIBS)
 gap_bluebox_LDADD =          $(LIBGIMPGAP)  $(LIBGAPBASE) $(GIMP_LIBS)
-gap_filter_LDADD =           $(LIBGIMPGAP)  $(LIBGAPBASE) $(GIMP_LIBS)
-gap_fmac_LDADD =             $(LIBGIMPGAP)  $(LIBGAPBASE) $(GIMP_LIBS)
-gap_fmac_varying_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)
 gap_frontends_LDADD =        $(LIBGIMPGAP)  $(LIBGAPBASE) $(GIMP_LIBS)
 gap_morph_LDADD =            $(LIBGIMPGAP)  $(LIBGAPBASE) $(GIMP_LIBS)
 gap_name2layer_LDADD =       $(LIBGIMPGAP)  $(LIBGAPBASE) $(GIMP_LIBS)

Added: trunk/gap/gap_drawable_vref_parasite.c
==============================================================================
--- (empty file)
+++ trunk/gap/gap_drawable_vref_parasite.c	Thu Jul 31 18:05:59 2008
@@ -0,0 +1,210 @@
+/* gap_drawable_vref_parasite.c
+ *
+ *  This module handles gap specific video reference drawable parasites.
+ *  Such parasites are typically used to identify extracted video file frames
+ *  for usage in filtermacro as persistent drawable_id references at recording time of filtermacros.
+ *
+ *  The gap player does attach such vref drawable parasites (as temporary parasites)
+ *  when extracting a videoframe at original size (by click on the preview)
+ *
+ * Copyright (C) 2008 Wolfgang Hofer <hof gimp org>
+ *
+ *
+ * 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.
+ */
+
+#include <stdio.h>
+#include <string.h>
+
+#include "gap_drawable_vref_parasite.h"
+
+
+extern      int gap_debug; /* ==0  ... dont print debug infos */
+
+/* ------------------------------------------
+ * gap_dvref_debug_print_GapDrawableVideoRef
+ * ------------------------------------------
+ */
+void
+gap_dvref_debug_print_GapDrawableVideoRef(GapDrawableVideoRef *dvref)
+{
+  if(gap_debug)
+  {
+    if(dvref == NULL)
+    {
+      printf("GapDrawableVideoRef: dvref:(null)\n");
+      return;
+    }
+    if(dvref->videofile == NULL)
+    {
+      printf("GapDrawableVideoRef: videofile:(null)  frame:%d seltrack:%d (%s)\n"
+        ,dvref->para.framenr
+        ,dvref->para.seltrack
+        ,&dvref->para.preferred_decoder[0]
+        );
+      return;
+    }
+
+    printf("GapDrawableVideoRef: videofile:%s  frame:%d seltrack:%d (%s)\n"
+      ,dvref->videofile
+      ,dvref->para.framenr
+      ,dvref->para.seltrack
+      ,&dvref->para.preferred_decoder[0]
+      );
+  }
+}  /* end gap_dvref_debug_print_GapDrawableVideoRef */
+
+
+/* -------------------
+ * gap_dvref_free
+ * -------------------
+ */
+void
+gap_dvref_free(GapDrawableVideoRef **dvref_ptr)
+{
+  GapDrawableVideoRef *dvref;
+  
+  dvref = *dvref_ptr;
+  if (dvref)
+  {
+    if(dvref->videofile)
+    {
+      g_free(dvref->videofile);
+    }
+    g_free(dvref);
+  }
+  
+  *dvref_ptr = NULL;
+  
+}  /* end  gap_dvref_free */
+
+/* ---------------------------------------------------
+ * gap_dvref_get_drawable_video_reference_via_parasite
+ * ---------------------------------------------------
+ * return Gap drawable video reference parasite if such a parasite is atached to the specified drawable_id
+ *        oterwise return NULL;
+ */
+GapDrawableVideoRef *
+gap_dvref_get_drawable_video_reference_via_parasite(gint32 drawable_id)
+{
+  GapDrawableVideoRef *dvref;
+  GimpParasite  *l_parasite;
+  
+  dvref = NULL;
+
+  l_parasite = gimp_drawable_parasite_find(drawable_id, GAP_DRAWABLE_VIDEOFILE_PARASITE_NAME);
+  if(l_parasite)
+  {
+    if(gap_debug)
+    {
+        printf("gap_dvref_get_drawable_video_reference_via_parasite: size:%d  data:%s\n"
+          ,l_parasite->size
+          ,(char *)l_parasite->data
+          );
+    }
+
+    dvref = g_new(GapDrawableVideoRef, 1);
+
+    dvref->videofile = g_malloc0(l_parasite->size +1);
+    memcpy(dvref->videofile, l_parasite->data, l_parasite->size);
+
+
+    gimp_parasite_free(l_parasite);
+
+
+    l_parasite = gimp_drawable_parasite_find(drawable_id, GAP_DRAWABLE_VIDEOPARAMS_PARASITE_NAME);
+    if(l_parasite)
+    {
+      memcpy(&dvref->para, l_parasite->data, sizeof(GapDrawableVideoParasite));
+      gimp_parasite_free(l_parasite);
+
+      if(gap_debug)
+      {
+        printf("gap_dvref_get_drawable_video_reference_via_parasite: dvref PARASITES OK\n");
+        gap_dvref_debug_print_GapDrawableVideoRef(dvref);
+      }
+      return (dvref);
+    }
+
+  }
+
+  if(gap_debug)
+  {
+    printf("gap_dvref_get_drawable_video_reference_via_parasite: NO dvref parasites found.\n");
+  }
+  
+  return (NULL);
+}  /* end gap_dvref_get_drawable_video_reference_via_parasite */
+
+
+    
+/* --------------------------------------
+ * gap_dvref_assign_videoref_parasites
+ * --------------------------------------
+ * if gpp->drawable_vref contains vaild video reference
+ * then
+ * assign video reference parasites to the specified drawable_id (typically this is a layer.)
+ * (one parasite contains only the videfilename and has variable length,
+ * the other contains framenumber and other information that was used to fetch
+ * the frame from the videofile).
+ *
+ * the video reference is typically set on successful fetch
+ * from a video file. (and reset on all other types of frame fetches)
+ *
+ + TODO: query gimprc parameter that can configures using persitent drawable_videoref_parasites.
+ *       (default shall be temporary parasites)
+ */
+void
+gap_dvref_assign_videoref_parasites(GapDrawableVideoRef *dvref, gint32 drawable_id)
+{
+  GimpParasite    *l_parasite;
+
+  if(gap_debug)
+  {
+    printf("gap_assign_drawable_videoref_parasite: START\n");
+    gap_dvref_debug_print_GapDrawableVideoRef(dvref);
+  }
+
+  if(dvref->videofile == NULL)
+  {
+    /* no viedo reference available for the current frame */
+    return;
+  }
+
+
+  l_parasite = gimp_parasite_new(GAP_DRAWABLE_VIDEOFILE_PARASITE_NAME
+                                 ,0  /* GIMP_PARASITE_PERSISTENT  0 for non persistent */
+                                 ,1 + strlen(dvref->videofile)
+                                 ,dvref->videofile  /* parasite data */
+                                 );
+  if(l_parasite)
+  {
+    gimp_drawable_parasite_attach(drawable_id, l_parasite);
+    gimp_parasite_free(l_parasite);
+  }
+
+  l_parasite = gimp_parasite_new(GAP_DRAWABLE_VIDEOPARAMS_PARASITE_NAME
+                                 ,0 /* GIMP_PARASITE_PERSISTENT */
+                                 ,sizeof(GapDrawableVideoParasite)
+                                 ,&dvref->para  /* parasite data */
+                                 );
+  if(l_parasite)
+  {
+    gimp_drawable_parasite_attach(drawable_id, l_parasite);
+    gimp_parasite_free(l_parasite);
+  }
+
+}       /* end gap_dvref_assign_videoref_parasites */
+

Added: trunk/gap/gap_drawable_vref_parasite.h
==============================================================================
--- (empty file)
+++ trunk/gap/gap_drawable_vref_parasite.h	Thu Jul 31 18:05:59 2008
@@ -0,0 +1,57 @@
+/* gap_drawable_vref_parasite.h
+ *
+ *  This module handles gap specific video reference drawable parasites.
+ *  Such parasites are typically used to identify extracted video file frames
+ *  for usage in filtermacro as persistent drawable_id references at recording time of filtermacros.
+ *
+ *  The gap player does attach such vref drawable parasites (as temporary parasites)
+ *  when extracting a videoframe at original size (by click on the preview)
+ *
+ * Copyright (C) 2008 Wolfgang Hofer <hof gimp org>
+ *
+ *
+ * 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.
+ */
+
+#ifndef _GAP_DRAWABLE_VREF_PARASITE_H
+#define _GAP_DRAWABLE_VREF_PARASITE_H
+
+#include "libgimp/gimp.h"
+#include "libgimp/gimp.h"
+#include "gap_lib_common_defs.h"
+
+
+#define GAP_DRAWABLE_VIDEOFILE_PARASITE_NAME   "gap-video-file"
+#define GAP_DRAWABLE_VIDEOPARAMS_PARASITE_NAME "gap-video-param"
+
+typedef struct GapDrawableVideoParasite {
+  char   preferred_decoder[16];
+  gint32 framenr;
+  gint32 seltrack;
+} GapDrawableVideoParasite;
+
+typedef struct GapDrawableVideoRef { /* nickname:  dvref */
+  char                      *videofile;
+  GapDrawableVideoParasite   para;
+} GapDrawableVideoRef;
+
+
+GapDrawableVideoRef *   gap_dvref_get_drawable_video_reference_via_parasite(gint32 drawable_id);
+void                    gap_dvref_assign_videoref_parasites(GapDrawableVideoRef *dvref, gint32 layer_id);
+
+void                    gap_dvref_free(GapDrawableVideoRef **dvref);
+void                    gap_dvref_debug_print_GapDrawableVideoRef(GapDrawableVideoRef *dvref);
+
+#endif

Modified: trunk/gap/gap_filter_iterators.c
==============================================================================
--- trunk/gap/gap_filter_iterators.c	(original)
+++ trunk/gap/gap_filter_iterators.c	Thu Jul 31 18:05:59 2008
@@ -94,7 +94,11 @@
 #include "gap_filter.h"
 #include "gap_filter_iterators.h"
 #include "gap_dbbrowser_utils.h"
-
+#include "gap_fmac_context.h"
+#include "gap_frame_fetcher.h"
+#include "gap_drawable_vref_parasite.h"
+#include "gap_lib.h"
+#include "gap/gap_layer_copy.h"
 
 
 static gchar *g_plugin_data_from = NULL;
@@ -178,6 +182,14 @@
   gdouble mutation_dist;
 } t_CML_PARAM;
 
+static void                 p_delta_drawable_simple(gint32 *val, gint32 val_from, gint32 val_to
+                                 , gint32 total_steps, gdouble current_step);
+
+static gint32               p_capture_image_name_and_assign_pesistent_id(GapFmacContext *fmacContext
+                                  , gint32 drawable_id);
+static gboolean             p_iteration_by_pesistent_id(GapFmacContext *fmacContext
+                                  ,gint32 *val, gint32 val_from, gint32 val_to
+                                  , gint32 total_steps, gdouble current_step);
 
 /* ----------------------------------------------------------------------
  * iterator functions for basic datatypes
@@ -337,45 +349,479 @@
        val->color[l_idx]  = val_from->color[l_idx] + delta;
     }
 }
-static void p_delta_drawable(gint32 *val, gint32 val_from, gint32 val_to, gint32 total_steps, gdouble current_step)
+
+
+/* ---------------------------
+ * p_delta_drawable
+ * ---------------------------
+ * the drawable iterator checks for current filtermacro context.
+ * if there is such a context, iteration tries to fetch the relvant drawable 
+ * via loading an image, or fetching a video frame.
+ * In this case, the drawables (gint32 val_from and val_to) are expected to refere
+ * to persistent Ids
+ * e.g. are >= GAP_FMCT_MIN_PERSISTENT_DRAWABLE_ID
+ * The image_ids of all fetched images are added to a list of last recent temp images
+ * this list has to be deleted at end of filtermacro processing. if the list contains
+ * more than NNNNN entries, the oldest entries are deleted at begin of a new fetch
+ * to make space for the next temporary image.
+ *
+ * if the record flag of the filtermacro context is set
+ * we do record identifying information about the the current drawable (*val)
+ * in the persistent_id_lookup_filename. The drawable id is therfore mapped into a persitent
+ * drawable_id that is unique in the persistent_id_lookup_file.
+ * (the 1st entry starts with GAP_FMCT_MIN_PERSISTENT_DRAWABLE_ID)
+ */
+void
+p_delta_drawable(gint32 *val, gint32 val_from, gint32 val_to, gint32 total_steps, gdouble current_step)
+{
+  gint            sizeFmacContext;
+
+  if(gap_debug)
+  {
+    printf("p_delta_drawable: START *val drawable_id:%d (from:%d  to:%d)\n"
+      ,(int)*val
+      ,(int)val_from
+      ,(int)val_to
+      );
+  }
+  
+  sizeFmacContext = gimp_get_data_size(GAP_FMAC_CONTEXT_KEYWORD);
+  
+  if(sizeFmacContext == sizeof(GapFmacContext))
+  {
+    GapFmacContext *fmacContext;
+    fmacContext = g_malloc0(sizeFmacContext);
+    if(fmacContext)
+    {
+      gimp_get_data(GAP_FMAC_CONTEXT_KEYWORD, fmacContext);
+
+      if(fmacContext->enabled == TRUE)
+      {
+        gboolean success;
+
+        gap_fmct_load_GapFmacContext(fmacContext);
+
+        /* check for record conditions */
+        if(fmacContext->recording_mode)
+        {
+          gint32 drawable_id;
+
+          drawable_id = *val;
+
+          /* calculate and assign the mapped persistent darawable_id */
+          *val = p_capture_image_name_and_assign_pesistent_id(fmacContext, drawable_id);
+
+          gap_fmct_free_GapFmacRefList(fmacContext);
+          g_free(fmacContext);
+          return;
+        }
+        success = p_iteration_by_pesistent_id(fmacContext, val, val_from, val_to, total_steps, current_step);
+        if (success)
+        {
+          gap_fmct_free_GapFmacRefList(fmacContext);
+          g_free(fmacContext);
+          return;
+        }
+      }
+      gap_fmct_free_GapFmacRefList(fmacContext);
+      g_free(fmacContext);
+    }
+
+  }
+   
+  /* simple iteration of layers if in the same image (without context) */
+  p_delta_drawable_simple(val, val_from, val_to, total_steps, current_step);
+  
+}  /* end p_delta_drawable */
+
+
+/* ---------------------------
+ * p_delta_drawable_simple
+ * ---------------------------
+ * simple iteration for drawable id (in case val_from and val_to both refere to
+ * the same image (that is already opened in the current gimp session)
+ */
+static void 
+p_delta_drawable_simple(gint32 *val, gint32 val_from, gint32 val_to, gint32 total_steps, gdouble current_step)
+{
+  gint    l_nlayers;
+  gint32 *l_layers_list;
+  gint32  l_tmp_image_id;
+  gint    l_idx, l_idx_from, l_idx_to;
+
+  if(gap_debug)
+  {
+    printf("p_delta_drawable_simple: START *val drawable_id:%d (from:%d  to:%d)\n"
+      ,(int)*val
+      ,(int)val_from
+      ,(int)val_to
+      );
+  }
+  if((val_from < 0) || (val_to < 0))
+  {
+    return;
+  }
+
+  l_tmp_image_id = gimp_drawable_get_image(val_from);
+
+  /* check if from and to values are both valid drawables within the same image */
+  if ((l_tmp_image_id > 0)
+  &&  (l_tmp_image_id = gimp_drawable_get_image(val_to)))
+  {
+     l_idx_from = -1;
+     l_idx_to   = -1;
+
+     /* check the layerstack index of from and to drawable */
+     l_layers_list = gimp_image_get_layers(l_tmp_image_id, &l_nlayers);
+     for (l_idx = l_nlayers -1; l_idx >= 0; l_idx--)
+     {
+        if( l_layers_list[l_idx] == val_from ) l_idx_from = l_idx;
+        if( l_layers_list[l_idx] == val_to )   l_idx_to   = l_idx;
+
+        if((l_idx_from != -1) && (l_idx_to != -1))
+        {
+          /* OK found both index values, iterate the index (proceed to next layer) */
+          p_delta_gint(&l_idx, l_idx_from, l_idx_to, total_steps, current_step);
+          *val = l_layers_list[l_idx];
+          break;
+        }
+     }
+     g_free (l_layers_list);
+  }
+}  /* end p_delta_drawable_simple */
+
+
+/* ------------------------------------
+ * p_drawable_is_alive
+ * ------------------------------------
+ * current implementation checks only for layers and layermasks.
+ * TODO check other drawable types such as channels ....
+ *
+ * return TRUE  if OK (drawable is still valid)
+ * return FALSE if drawable is NOT valid
+ */
+gboolean
+p_drawable_is_alive(gint32 drawable_id)
 {
+  gint32 *images;
+  gint    nimages;
+  gint    l_idi;
+  gboolean   l_found;
+
+  if(drawable_id < 0)
+  {
+     return FALSE;
+  }
+
+  images = gimp_image_list(&nimages);
+  l_idi = nimages -1;
+  l_found = FALSE;
+  while((l_idi >= 0) && images)
+  {
     gint    l_nlayers;
     gint32 *l_layers_list;
-    gint32  l_tmp_image_id;
-    gint    l_idx, l_idx_from, l_idx_to;
 
-    if((val_from < 0) || (val_to < 0))
+    l_layers_list = gimp_image_get_layers(images[l_idi], &l_nlayers);
+    if(l_layers_list != NULL)
     {
-      return;
+      gint    l_idx;
+      l_idx = l_nlayers;
+      for(l_idx = 0; l_idx < l_nlayers; l_idx++)
+      {
+         gint32  l_layer_id;
+         gint32  l_layermask_id;
+         
+         l_layer_id = l_layers_list[0];
+         if (l_layer_id == drawable_id)
+         {
+           l_found = TRUE;
+           break;
+         }
+         l_layermask_id = gimp_layer_get_mask(l_layer_id);
+         if (l_layermask_id == drawable_id)
+         {
+           l_found = TRUE;
+           break;
+         }
+      }
+      g_free (l_layers_list);
     }
+    
+    if (l_found == TRUE)
+    {
+      break;
+    }
+    l_idi--;
+  }
 
-    l_tmp_image_id = gimp_drawable_get_image(val_from);
+  if(images) g_free(images);
+  if(l_found)
+  {
+    return TRUE;  /* OK */
+  }
 
-    /* check if from and to values are both valid drawables within the same image */
-    if ((l_tmp_image_id > 0)
-    &&  (l_tmp_image_id = gimp_drawable_get_image(val_to)))
+  if(gap_debug)
+  {
+    printf("p_drawable_is_alive: drawable_id %d is not VALID\n", (int)drawable_id);
+  }
+ 
+  return FALSE ;   /* INVALID image id */
+}  /* end p_drawable_is_alive */
+
+/* --------------------------------------------
+ * p_capture_image_name_and_assign_pesistent_id
+ * --------------------------------------------
+ * Capture the identifying information about the image, anim_frame or videofile name and layerstack index
+ *   (or frameposition) of the specified drawable_id.
+ * Note: videofilename and framenumber can be fetched from layer parasite data. 
+ *         (plus additional information prefered_decoder and seltrack)
+ *         parasite data shall be set when the player creates a snapshot of a video clip.
+ *         (click in the preview)
+ *         (Player option: enable videoname parasites.)
+ * assign a persistent id that is unique within a persistent_id_lookup_file.
+ * filtermacro context.
+ * returns the assigned persistent_drawable_id.
+ *         (In case no assigned persisten_drawable_id could be created
+ *          return the original drawable_id).
+ */
+static gint32
+p_capture_image_name_and_assign_pesistent_id(GapFmacContext *fmacContext, gint32 drawable_id)
+{
+  gint32 persistent_drawable_id;
+  gint32 image_id;
+  GapDrawableVideoRef *dvref;
+  GapLibAinfoType ainfo_type;
+  gint32 frame_nr;
+  gint32 stackposition;
+  gint32 track;
+  char   *filename;
+  
+  
+  if(gap_debug)
+  {
+    printf("p_capture_image_name_and_assign_pesistent_id: START_REC orignal_drawable_id:%d\n"
+      ,drawable_id
+      );
+  }
+
+  if(p_drawable_is_alive(drawable_id) != TRUE)
+  {
+    /* drawable is no longer valid and can not be mapped.
+     * This may happen if the layer was removed or the refered image was close
+     * in the time since the plugin has stored the last values buffer
+     * and the time when filtermacro starts recording filtercalls.
+     */
+    return(drawable_id);
+  }
+  
+  persistent_drawable_id = drawable_id;
+  filename = NULL;
+  
+  dvref = gap_dvref_get_drawable_video_reference_via_parasite(drawable_id);
+  if (dvref != NULL)
+  {
+    if(gap_debug)
     {
-       l_idx_from = -1;
-       l_idx_to   = -1;
+      printf("p_capture_image_name_and_assign_pesistent_id: VIDEO ref found for orignal_drawable_id:%d\n"
+        ,drawable_id
+        );
+    }
+    ainfo_type = GAP_AINFO_MOVIE;
+    frame_nr = dvref->para.framenr;    /* video frame number */
+    stackposition = -1;   /* stackposition not relevant for video */
+    track = dvref->para.seltrack;
+    filename = g_strdup(dvref->videofile);
+
+    gap_dvref_free(&dvref);
+  }
+  else
+  {
+    GapAnimInfo *l_ainfo_ptr;
+    
+    image_id = gimp_drawable_get_image(drawable_id);
+    filename = gimp_image_get_filename(image_id);
+    ainfo_type = GAP_AINFO_ANIMIMAGE;
+    stackposition = gap_layer_get_stackposition(image_id, drawable_id);
+    track = 1;
+    frame_nr = 1;
+
+    // here we could check gimp_image_is_dirty(image_id)
+    // to check for unsaved changes of the image.
+    // but the recording of a filtermacro can happen much later than
+    // the initial recording of the plugin's last values buffer.
+  
+    l_ainfo_ptr = gap_lib_alloc_ainfo(image_id, GIMP_RUN_NONINTERACTIVE);
+    if(l_ainfo_ptr != NULL)
+    {
+      ainfo_type = l_ainfo_ptr->ainfo_type;
+      frame_nr = l_ainfo_ptr->frame_nr;
+      track = -1;   /* video track (not relevant for frames and single images) */
+
+      gap_lib_free_ainfo(&l_ainfo_ptr);
+    }
+  }
+
+  if (filename != NULL)
+  {
+    persistent_drawable_id = gap_fmct_add_GapFmacRefEntry(ainfo_type
+                                 , frame_nr
+                                 , stackposition
+                                 , track
+                                 , drawable_id
+                                 , filename
+                                 , FALSE           /* force_id NO (e.g generate unique id) */
+                                 , fmacContext
+                                 );
+    g_free(filename);
+  }
+
+
+  if(gap_debug)
+  {
+    printf("p_capture_image_name_and_assign_pesistent_id: orignal_drawable_id:%d mapped_drawable_id:%d\n"
+      ,drawable_id
+      ,persistent_drawable_id
+      );
+  }
+
+  return (persistent_drawable_id);
+}  /* end p_capture_image_name_and_assign_pesistent_id */
 
-       /* check the layerstack index of from and to drawable */
-       l_layers_list = gimp_image_get_layers(l_tmp_image_id, &l_nlayers);
-       for (l_idx = l_nlayers -1; l_idx >= 0; l_idx--)
-       {
-          if( l_layers_list[l_idx] == val_from ) l_idx_from = l_idx;
-          if( l_layers_list[l_idx] == val_to )   l_idx_to   = l_idx;
 
-          if((l_idx_from != -1) && (l_idx_to != -1))
+/* -------------------------------------
+ * p_iteration_by_pesistent_id
+ * -------------------------------------
+ * fetch frame image by persistent id,
+ * assign drawable and calculate iteration between
+ *   start_drawable_id
+ *   end_drawable_id
+ * o) check if both start_drawable_id and end_drawable_id 
+ *    are found in the persistent_id_lookup
+ *
+ *    o) if both refere to the same image filename and ainfo_type == GAP_AINFO_ANIMIMAGE
+ *       then open this image (if not already opened)
+ *       and calculate the drawable id according to current step
+ *       by iterating layerstack numbers.
+ *    o) if both refere to images, that are anim frames
+ *       with the same basename and extension
+ *       (for example:
+ *         anim_000001.xcf 
+ *         anim_000009.xcf
+ *       )
+ *       then iterate by framenumber (in the example this will be between 1 and 9)
+ *
+ *    o) filename references to videofiles are supported if ainfo_type == GAP_AINFO_MOVIE
+ *
+ * Note that each fetch of a persistent darawable (the fetched_layer_id) creates
+ * a temporary image in the frame fetcher (assotiated with fmacContext->ffetch_user_id)
+ * In this iterator we can not delete those temp. image(s) because they are required for
+ * processing in the corresponding filter plug-in.
+ * Therefore the master filtermacro processing has to do this clean up step after the
+ * filtercall has finished.
+ */
+static gboolean
+p_iteration_by_pesistent_id(GapFmacContext *fmacContext
+  ,gint32 *val, gint32 val_from, gint32 val_to, gint32 total_steps, gdouble current_step)
+{
+  GapFmacRefEntry *fmref_entry_from;
+
+  if(gap_debug)
+  {
+    printf("p_iteration_by_pesistent_id: START *val drawable_id:%d (from:%d  to:%d)\n"
+      ,(int)*val
+      ,(int)val_from
+      ,(int)val_to
+      );
+  }
+  
+  fmref_entry_from = gap_fmct_get_GapFmacRefEntry_by_persitent_id(fmacContext, val_from);
+  if (fmref_entry_from)
+  {
+    GapFmacRefEntry *fmref_entry_to;
+    
+    fmref_entry_to = gap_fmct_get_GapFmacRefEntry_by_persitent_id(fmacContext, val_to);
+    if (fmref_entry_to)
+    {
+      if ((strcmp(fmref_entry_from->filename, fmref_entry_to->filename) == 0)
+      && (fmref_entry_from->track == fmref_entry_to->track)
+      && (fmref_entry_from->ainfo_type == fmref_entry_to->ainfo_type))
+      {
+        gint32 fetched_layer_id;
+
+        fetched_layer_id = -1;
+
+        // TODO: what to do if mtime has changed ? (maybe print warning if this occurs the 1st time ?)
+        
+        if (fmref_entry_from->ainfo_type == GAP_AINFO_ANIMIMAGE)
+        {
+          /* iterate stackposition */
+          gint32 stackposition;
+          
+          stackposition = fmref_entry_from->stackposition;
+          p_delta_gint32(&stackposition, fmref_entry_from->stackposition, fmref_entry_to->stackposition
+                        ,total_steps, current_step);
+                        
+          fetched_layer_id = gap_frame_fetch_dup_image(fmacContext->ffetch_user_id
+                                    ,fmref_entry_from->filename   /* full filename of the image */
+                                    ,stackposition                /* pick layer by stackposition */
+                                    ,TRUE                         /* enable caching */
+                                    );
+        }
+        else
+        {
+          /* iterate frame_nr */
+          gint32 frame_nr;
+          
+          frame_nr = fmref_entry_from->frame_nr;
+          p_delta_gint32(&frame_nr, fmref_entry_from->frame_nr, fmref_entry_to->frame_nr
+                        ,total_steps, current_step);
+
+          if (fmref_entry_from->ainfo_type == GAP_AINFO_MOVIE)
           {
-            /* OK found both index values, iterate the index (proceed to next layer) */
-            p_delta_gint(&l_idx, l_idx_from, l_idx_to, total_steps, current_step);
-            *val = l_layers_list[l_idx];
-            break;
+            fetched_layer_id = gap_frame_fetch_dup_video(fmacContext->ffetch_user_id
+                                    ,fmref_entry_from->filename   /* full filename of a video */
+                                    ,frame_nr                     /* frame within the video (starting at 1) */
+                                    ,fmref_entry_from->track      /* videotrack */
+                                    , NULL                        /* char *prefered_decoder*/
+                                    );
           }
-       }
-       g_free (l_layers_list);
+          else
+          {
+            fetched_layer_id = gap_frame_fetch_dup_image(fmacContext->ffetch_user_id
+                                    ,fmref_entry_from->filename   /* full filename of the image */
+                                    ,-1                           /* 0 pick layer on top of stack, -1 merge visible layers */
+                                    ,TRUE                         /* enable caching */
+                                    );
+          }
+
+        }
+
+        *val = fetched_layer_id;
+        if(gap_debug)
+        {
+          printf("p_iteration_by_pesistent_id: SUCCESS drawable_id:%d (from:%d  to:%d)\n"
+            ,(int)*val
+            ,(int)val_from
+            ,(int)val_to
+            );
+        }
+        return (TRUE);
+      }
     }
-}
+  }
+
+  if(gap_debug)
+  {
+    printf("p_iteration_by_pesistent_id: FAILED *val drawable_id:%d (from:%d  to:%d)\n"
+      ,(int)*val
+      ,(int)val_from
+      ,(int)val_to
+      );
+  }
+  return (FALSE);
+}  /* end p_iteration_by_pesistent_id */
+
+
 static void
 p_delta_gintdrawable(gint *val, gint val_from, gint val_to, gint32 total_steps, gdouble current_step)
 {

Modified: trunk/gap/gap_filter_pdb.c
==============================================================================
--- trunk/gap/gap_filter_pdb.c	(original)
+++ trunk/gap/gap_filter_pdb.c	Thu Jul 31 18:05:59 2008
@@ -478,7 +478,7 @@
  * and there is no individual Iterator Procedure.
  */
 char * 
-gap_filt_pdb_get_iterator_proc(char *plugin_name, gint *count)
+gap_filt_pdb_get_iterator_proc(const char *plugin_name, gint *count)
 {
   char      *l_plugin_iterator;
   char      *canonical_name;

Modified: trunk/gap/gap_filter_pdb.h
==============================================================================
--- trunk/gap/gap_filter_pdb.h	(original)
+++ trunk/gap/gap_filter_pdb.h	Thu Jul 31 18:05:59 2008
@@ -49,7 +49,7 @@
 gint gap_filt_pdb_get_data(char *key);
 void gap_filt_pdb_set_data(char *key, gint plugin_data_len);
 gint gap_filt_pdb_procedure_available(char  *proc_name, GapFiltPdbProcType ptype);
-char * gap_filt_pdb_get_iterator_proc(char *plugin_name, gint *count);
+char * gap_filt_pdb_get_iterator_proc(const char *plugin_name, gint *count);
 
 int gap_filt_pdb_constraint_proc_sel1(gchar *proc_name, gint32 image_id);
 int gap_filt_pdb_constraint_proc_sel2(gchar *proc_name, gint32 image_id);

Modified: trunk/gap/gap_fmac_base.c
==============================================================================
--- trunk/gap/gap_fmac_base.c	(original)
+++ trunk/gap/gap_fmac_base.c	Thu Jul 31 18:05:59 2008
@@ -53,6 +53,8 @@
 #include "gap_filter_pdb.h"
 #include "gap_fmac_name.h"
 #include "gap_fmac_base.h"
+#include "gap_fmac_context.h"
+#include "gap_frame_fetcher.h"
 
 
 
@@ -61,6 +63,7 @@
 typedef struct FMacElem {
  char       *filtername;
  gboolean    assigned_flag;
+ gboolean    varying_flag;
  char       *buffer_from;
  gint32      buffer_from_length;
  char       *buffer_to;
@@ -239,6 +242,12 @@
  * init the assigned_flag with FALSE for all elements that are added to the list.
  * Note that the order of elements in the list must match the order in the
  * filtermacro file.
+ * Furthermore assign the matching iteratorname (a name of
+ * an iterator plug-in that can handle the filter specific parameter value mix
+ * for the iterable parameter values and/or can do the mapping of persistent drawable id's
+ * in the last values buffer.
+ * the varying_flag is initilized with FALSE. 
+ *
  * returns root elem of the list or NULL if load failed.
  */
 static FMacElem *
@@ -280,6 +289,7 @@
     if (fmacLine)
     {
       FMacElem *fmac_elem;
+      gint l_count;
       
       /* create fmac_elem according to scanned fmac line */
       fmac_elem = g_malloc0(sizeof(FMacElem));
@@ -287,9 +297,15 @@
       fmac_elem->buffer_from  = g_malloc0(fmacLine->paramlength);
       fmac_elem->buffer_to    = g_malloc0(fmacLine->paramlength);
       fmac_elem->buffer_from_length = fmacLine->paramlength;
-      fmac_elem->iteratorname  = NULL;
       fmac_elem->assigned_flag = FALSE;
+      fmac_elem->varying_flag = FALSE;
       fmac_elem->next = NULL;
+      /* assign the matching Iterator PluginProcedure
+       * (if there is any)
+       */
+      fmac_elem->iteratorname = 
+         gap_filt_pdb_get_iterator_proc(fmac_elem->filtername
+                                       , &l_count);
       
       memcpy(fmac_elem->buffer_from , fmacLine->paramdata, fmacLine->paramlength);
       memcpy(fmac_elem->buffer_to , fmacLine->paramdata, fmacLine->paramlength);
@@ -334,10 +350,11 @@
  * read filters from the 2nd filtermacro file and merge
  * in the buffer_to values by overwriting already initialized
  * values where filtername matches and assigned_flag is not yet set.
- * Furthermore assign the matching iteratorname (a name of
- * an iterator plug-in that can handle the filter specific parameter value mix
- * for the iterable parameter values.
  * Note that all non-matching entries are ignored.
+ *
+ * the varying_flag is set to TRUE for those lines that have a matching line
+ * in the 2nd filtermacro file AND have an iterator 
+ * (that can do the plug-in specific mix of the parmetervakues)
  */
 gboolean
 p_merge_fmac_list(FMacElem *fmac_root, const char *filtermacro_file, GimpRunMode run_mode)
@@ -383,17 +400,14 @@
         {
           if (fmac_elem->buffer_from_length == fmacLine->paramlength)
           {
-            gint l_count;
             /* overwrite param data */
             memcpy(fmac_elem->buffer_to, fmacLine->paramdata, fmacLine->paramlength);
             fmac_elem->assigned_flag = TRUE;
 
-            /* assign the matching Iterator PluginProcedures
-             * (if there is any)
-             */
-            fmac_elem->iteratorname = 
-               gap_filt_pdb_get_iterator_proc(fmac_elem->filtername
-                                             , &l_count);
+            if(fmac_elem->iteratorname != NULL)
+            {
+              fmac_elem->varying_flag = TRUE;
+            }
 
           }
           else
@@ -496,11 +510,22 @@
 
     if(gap_debug)
     {
-      printf("p_fmac_execute_single_filter: VARYING APPLY iteratorname:%s\n  key_from:%s\n  key_to:%s\n"
+      if(fmac_elem->varying_flag == TRUE)
+      {
+        printf("p_fmac_execute_single_filter: VARYING APPLY iteratorname:%s\n  key_from:%s\n  key_to:%s\n"
              ,fmac_elem->iteratorname
              ,l_key_from
              ,l_key_to
              );
+      }
+      else
+      {
+        printf("p_fmac_execute_single_filter: MAPPING APPLY iteratorname:%s\n  key_from:%s\n  key_to:%s\n"
+             ,fmac_elem->iteratorname
+             ,l_key_from
+             ,l_key_to
+             );
+      }
     }
     
     /* the iterator call will set the last values buffer for the corresponding
@@ -535,7 +560,6 @@
     gint     l_exec_len;
     gint     l_ii;
 
-    /* make backup of last values buffer (if available) */
     l_exec_len = gimp_get_data_size(fmac_elem->filtername);
     
     printf("p_fmac_execute_single_filter: EXEC image:%d, drawable:%d filtername:%s len:%d\n"
@@ -626,6 +650,17 @@
   if (fmac_root)
   {
     FMacElem *fmac_elem;
+    GapFmacContext theFmacContext;
+    GapFmacContext *fmacContext;
+
+    fmacContext = &theFmacContext;
+
+    gap_fmct_setup_GapFmacContext(fmacContext
+                                 , FALSE  /* no recording_mode  (e.g. apply mode) */
+                                 , filtermacro_file1
+                                 );
+
+
 
     if(filtermacro_file2 != NULL)
     {
@@ -638,7 +673,15 @@
           , current_step
           , total_steps
           );
+      
+      /* intermediate cleanup of temporary image duplicates that may have been
+       * created while iterating persistent drawable ids (by iterator sub-procedur p_delta_drawable)
+       */
+      gap_frame_fetch_delete_list_of_duplicated_images(fmacContext->ffetch_user_id);
     }
+
+    /* disable the sessionwide filtermacro context */
+    gap_fmct_disable_GapFmacContext();
     
     //TODO p_free_fmac_list(fmac_root);  /* free the filtermacro processing list */
   }

Added: trunk/gap/gap_fmac_context.c
==============================================================================
--- (empty file)
+++ trunk/gap/gap_fmac_context.c	Thu Jul 31 18:05:59 2008
@@ -0,0 +1,676 @@
+/* gap_fmac_context.c
+ *
+ *
+ *  This module handles the filtermacro context.
+ *  The filtermacro context is used for itration of "persistent drawable ids" 
+ *  for animated (or constant) filter apply.
+ *  If gap controlled filterapply is done via a filtermacro
+ *  the iteration is done within a filtermacro context.
+ *  (e.g. while filtermacro is beeing recorded or is applied)
+ *  the handled drawable ids are mapped with the help of a filtermacro reference file
+ *  In this case the "persitent_drawable_id" is used to open the referenced
+ *  image, frame or videoframe at apply time (this may happen in another gimp
+ *  session than the recording of the filtermacro was done).
+ *  
+ *
+ * Copyright (C) 2008 Wolfgang Hofer <hof gimp org>
+ *
+ *
+ * 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.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include <glib/gstdio.h>
+
+/* GAP includes */
+#include "gap_fmac_context.h"
+#include "gap_frame_fetcher.h"
+#include "gap_file_util.h"
+#include "gap_val_file.h"
+
+
+extern      int gap_debug; /* ==0  ... dont print debug infos */
+
+char *                      p_parse_string(char **scan_ptr);
+static void                 p_parse_and_add_GapFmacRefEntry(GapFmacContext *fmacContext, char *line_ptr);
+
+
+/* --------------------------------------------
+ * gap_fmct_set_derived_lookup_filename
+ * --------------------------------------------
+ * set persistent_id_lookup_filename = filename.fmref
+ */
+void
+gap_fmct_set_derived_lookup_filename(GapFmacContext *fmacContext, const char *filename)
+{
+  if ((fmacContext) && (filename))
+  {
+    g_snprintf(fmacContext->persistent_id_lookup_filename , sizeof(fmacContext->persistent_id_lookup_filename), "%s%s"
+       ,filename
+       ,GAP_FMREF_FILENAME_EXTENSION
+       );
+  }
+}  /* end gap_fmct_set_derived_lookup_filename */
+
+
+/* --------------------------------------------
+ * gap_fmct_setup_GapFmacContext
+ * --------------------------------------------
+ * setup a filtermacro context for recording or playback mode.
+ * the context affects the behaviour of drawable_iteration in the whole gimp_session
+ * until the procedure gap_fmct_disable_GapFmacContext is called.
+ * This procedure registers a new frame fetcher resource user_id if playback (e.g NOT recording_mode)
+ * is used. The drawable_iteration will refere to this ffetch_user_id when fetching
+ * frames via mapped persistent drawable ids.
+ * 
+ * The current implementation can NOT handle parallell processing of multiple filtermacros
+ * because there is only ONE context that will be overwritten in case of concurrent calls.
+ */
+void
+gap_fmct_setup_GapFmacContext(GapFmacContext *fmacContext, gboolean recording_mode, const char *filename)
+{
+  gap_fmct_set_derived_lookup_filename(fmacContext, filename);
+  fmacContext->recording_mode = recording_mode;
+  fmacContext->enabled = TRUE;
+  if (recording_mode)
+  {
+    fmacContext->ffetch_user_id = -1;
+  }
+  else
+  {
+    fmacContext->ffetch_user_id = gap_frame_fetch_register_user("gap_fmct_setup_GapFmacContext");
+  }
+  fmacContext->fmref_list = NULL;
+  gimp_set_data(GAP_FMAC_CONTEXT_KEYWORD, fmacContext, sizeof(GapFmacContext));
+}  /* end gap_fmct_setup_GapFmacContext */
+
+
+/* --------------------------------------------
+ * gap_fmct_disable_GapFmacContext
+ * --------------------------------------------
+ * disable the gimp session wide global filtermacro context.
+ * (this also unregisters frame fetcher resource usage if
+ * there was a valid context in playback mode at calling time)
+ */
+void
+gap_fmct_disable_GapFmacContext(void)
+{
+  gint            sizeFmacContext;
+  
+  sizeFmacContext = gimp_get_data_size(GAP_FMAC_CONTEXT_KEYWORD);
+
+  if(sizeFmacContext == sizeof(GapFmacContext))
+  {
+    GapFmacContext *fmacContext;
+    fmacContext = g_malloc0(sizeFmacContext);
+
+    if(fmacContext)
+    {
+      gimp_get_data(GAP_FMAC_CONTEXT_KEYWORD, fmacContext);
+      fmacContext->enabled = FALSE;
+      fmacContext->fmref_list = NULL;
+      if(fmacContext->ffetch_user_id >= 0)
+      {
+        /* unregister (shall drop cached resources of the frame fetcher when last user unregisters) */
+        gap_frame_fetch_unregister_user(fmacContext->ffetch_user_id);
+      }
+      gimp_set_data(GAP_FMAC_CONTEXT_KEYWORD, fmacContext, sizeof(GapFmacContext));
+      g_free(fmacContext);
+    }
+  }
+}  /* end gap_fmct_disable_GapFmacContext */
+
+
+/* --------------------------------------------
+ * gap_fmct_free_GapFmacRefList
+ * --------------------------------------------
+ * free all elements of the fmacContext->fmref_list.
+ */
+void
+gap_fmct_free_GapFmacRefList(GapFmacContext *fmacContext)
+{
+  GapFmacRefEntry *fmref_entry;
+  GapFmacRefEntry *fmref_next;
+
+  fmref_entry = fmacContext->fmref_list;
+  while(fmref_entry != NULL)
+  {
+    fmref_next = fmref_entry->next;
+    g_free(fmref_entry);
+    fmref_entry = fmref_next;
+  }
+  fmacContext->fmref_list = NULL;
+
+}  /* end gap_fmct_free_GapFmacRefList */
+
+
+
+/* --------------------------
+ * p_parse_string_raw
+ * --------------------------
+ * return a pointer to the start of the next non-white space character
+ *        in this line and advance the scan_ptr to the next white space
+ *        or to one position after the ':' (that terminates key strings)
+ *        NULL is returned if there is no (more) non-white space character
+ *        left in the line rest (starting at *scan_ptr).
+ * the scan_ptr is rest to NULL when end of line \n or end of string \0
+ * is reached.
+ */
+char *
+p_parse_string_raw(char **scan_ptr)
+{
+  char *ptr;
+  char *result_ptr;
+  
+  result_ptr = NULL;
+  ptr = *scan_ptr;
+
+  /* advance to 1st non blank position */
+  while(ptr)
+  {
+    if ((*ptr == '\0') || (*ptr == '\n'))
+    {
+      /* end of line (or string) reached */
+      *scan_ptr = NULL;
+      return (result_ptr);
+    }
+    if ((*ptr != ' ') && (*ptr != '\t'))
+    {
+      /* first non-white space character found */
+      result_ptr = ptr;
+      break;
+    }
+    ptr++;  /* skip white space and continue */
+  }
+  
+  
+  while(ptr)
+  {
+    ptr++;
+    if ((*ptr == '\0') || (*ptr == '\n'))
+    {
+      *scan_ptr = NULL;
+      return (result_ptr);
+    }
+    if (*result_ptr == '"')
+    {
+      if(*ptr == '"')
+      {
+        ptr++;
+        *scan_ptr = ptr;
+        return (result_ptr);
+      }
+    }
+    else
+    {
+      if ((*ptr == ' ') || (*ptr == '\t'))
+      {
+        *scan_ptr = ptr;
+        return (result_ptr);
+      }
+      if (*ptr == ':')
+      {
+        ptr++;
+        *scan_ptr = ptr;
+        return (result_ptr);
+      }
+    }
+  }
+  
+  return (result_ptr);
+}  /* end p_parse_string_raw */
+
+/* --------------------------
+ * p_parse_string
+ * --------------------------
+ * return a pointer to the start of the next non-white space character
+ *        in this line and advance the scan_ptr to the next white space
+ *        or to one position after the ':' (that terminates key strings)
+ *        NULL is returned if there is no (more) non-white space character
+ *        left in the line rest (starting at *scan_ptr).
+ * the scan_ptr is rest to NULL when end of line \n or end of string \0
+ * is reached.
+ */
+char *
+p_parse_string(char **scan_ptr)
+{
+  char *l_ptr;
+  char *l_result;
+
+  l_result =  NULL;
+  l_ptr = p_parse_string_raw(scan_ptr);
+  if(l_ptr != NULL)
+  {
+    l_result = g_strdup(l_ptr);
+    if (*scan_ptr != NULL)
+    {
+      l_result[*scan_ptr - l_ptr] = '\0';
+    }
+  }
+  if(gap_debug)
+  {
+    if(l_result)
+    {
+      printf("p_parse_string:(%s)\n", l_result);
+    }
+    else
+    {
+      printf("p_parse_string:(NULL)\n");
+    }
+  }
+  return (l_result);
+}
+
+/* --------------------------------------------
+ * p_parse_and_add_GapFmacRefEntry
+ * --------------------------------------------
+ * parse one record and add it to
+ * the the fmacContext->fmref_list on success.
+ */
+static void
+p_parse_and_add_GapFmacRefEntry(GapFmacContext *fmacContext, char *line_ptr)
+{
+  char *l_key;
+  char *l_value;
+  char *scan_ptr;
+
+  long id;
+  long ainfo_type;
+  long frame_nr;
+  long stackposition;
+  long track;
+  long mtime;
+  char *filename;
+
+  id = -1;
+  ainfo_type = -1;
+  frame_nr = -1;
+  stackposition = -1;
+  track = -1;
+  mtime = -1;
+  filename = NULL;
+  
+  scan_ptr = line_ptr;
+  while (scan_ptr != NULL)
+  {
+    l_key = p_parse_string(&scan_ptr);
+    if (l_key == NULL)
+    {
+      break;
+    }
+    l_value = p_parse_string(&scan_ptr);
+    if (l_value == NULL)
+    {
+      g_free(l_key);
+      break;
+    }
+    
+    if (strcmp(l_key, GAP_FMREF_ID) == 0)
+    {
+      id = atol(l_value);
+    } 
+    else if (strcmp(l_key, GAP_FMREF_FRAME_NR) == 0)
+    {
+      frame_nr = atol(l_value);
+    }
+    else if (strcmp(l_key, GAP_FMREF_STACK) == 0)
+    {
+      stackposition = atol(l_value);
+    }
+    else if (strcmp(l_key, GAP_FMREF_TRACK) == 0)
+    {
+      track = atol(l_value);
+    }
+    else if (strcmp(l_key, GAP_FMREF_MTIME) == 0)
+    {
+      mtime = atol(l_value);
+    }
+    else if (strcmp(l_key, GAP_FMREF_TYPE) == 0)
+    {
+      ainfo_type = atol(l_value);
+    }
+    else if (strcmp(l_key, GAP_FMREF_FILE) == 0)
+    {
+      int len;
+      char *l_raw_filename;
+      
+      l_raw_filename = l_value;
+      if(gap_debug)
+      {
+        printf("RAW filename:%s:\n", l_raw_filename);
+      }
+      if (*l_raw_filename == '"')
+      {
+        l_raw_filename++;
+      }
+      len = strlen(l_raw_filename);
+      if (len > 0)
+      {
+        if (l_raw_filename[len -1] == '"')
+        {
+          l_raw_filename[len -1] = '\0';
+        }
+      }
+      filename = g_strdup(l_raw_filename);
+    }
+    else
+    {
+      printf("WARNING: unkonwn token: %s\n", l_key);
+    }
+
+    g_free(l_key);
+    g_free(l_value);
+  }
+
+  /* now check if required attributes are present */
+  if (id < 0)
+  {
+    printf("ERROR: incomplete record, expected: %s is missing", GAP_FMREF_ID);
+    return;
+  }
+  if (frame_nr < 0)
+  {
+    printf("ERROR: incomplete record, expected: %s is missing", GAP_FMREF_FRAME_NR);
+    return;
+  }
+  if (filename == NULL)
+  {
+    printf("ERROR: incomplete record, expected: %s is missing", GAP_FMREF_FILE);
+    return;
+  }
+  if (ainfo_type < 0)
+  {
+    printf("ERROR: incomplete record, expected: %s is missing", GAP_FMREF_TYPE);
+    return;
+  }
+  if (mtime < 0)
+  {
+    printf("ERROR: incomplete record, expected: %s is missing", GAP_FMREF_MTIME);
+    return;
+  }
+
+  /* create and add the scanned entry to the list */
+  gap_fmct_add_GapFmacRefEntry(ainfo_type
+      , frame_nr
+      , stackposition
+      , track
+      , id
+      , filename
+      , TRUE            /* force_id scanned from file (dont generate a new one ) */
+      , fmacContext
+      );
+
+  if (filename != NULL)
+  {
+    g_free(filename);
+  }
+
+}  /* end p_parse_and_add_GapFmacRefEntry */
+
+
+/* --------------------------------------------
+ * gap_fmct_load_GapFmacContext
+ * --------------------------------------------
+ */
+void
+gap_fmct_load_GapFmacContext(GapFmacContext *fmacContext)
+{
+  GapValTextFileLines *txf_ptr_root;
+  GapValTextFileLines *txf_ptr;
+  gint32 line_nr;
+
+
+  gap_fmct_free_GapFmacRefList(fmacContext);
+
+  txf_ptr_root = gap_val_load_textfile(fmacContext->persistent_id_lookup_filename);
+  line_nr = 0;
+
+  for(txf_ptr = txf_ptr_root; txf_ptr != NULL; txf_ptr = (GapValTextFileLines *) txf_ptr->next)
+  {
+    gint l_len;
+
+    line_nr++;
+    if(gap_debug)
+    {
+      printf("line_nr: %d\n", (int)line_nr);
+    }
+    gap_file_chop_trailingspace_and_nl(&txf_ptr->line[0]);
+    l_len = strlen(txf_ptr->line);
+
+    if(gap_debug)
+    {
+      printf("line:%s:\n", txf_ptr->line);
+    }
+    
+    if (line_nr == 1)
+    {
+      if (strncmp(txf_ptr->line, GAP_FMREF_FILEHEADER, strlen(GAP_FMREF_FILEHEADER)) != 0)
+      {
+        printf("ERROR: file:%s does not start with expected header:%s\n"
+               , fmacContext->persistent_id_lookup_filename
+               , GAP_FMREF_FILEHEADER
+               );
+        break;
+      }
+    }
+    else
+    {
+      if (*txf_ptr->line != '#')
+      {
+        p_parse_and_add_GapFmacRefEntry(fmacContext, txf_ptr->line);
+      }
+    }
+
+  }
+
+  if(txf_ptr_root)
+  {
+    gap_val_free_textfile_lines(txf_ptr_root);
+  }
+  
+  if(gap_debug)
+  {
+    gap_fmct_debug_print_GapFmacContext(fmacContext);
+  }
+
+}  /* end gap_fmct_load_GapFmacContext */
+
+
+/* --------------------------------------------
+ * gap_fmct_save_GapFmacContext
+ * --------------------------------------------
+ * save the list of filtermacro references (to persistent drawable ids)
+ * in the filename that is specified via persistent_id_lookup_filename
+ * in the filtermacro context.
+ * The file created or completely overwritten.
+ */
+void
+gap_fmct_save_GapFmacContext(GapFmacContext *fmacContext)
+{
+  GapFmacRefEntry *fmref_entry;
+  FILE            *fp;
+
+  fp = g_fopen(fmacContext->persistent_id_lookup_filename, "w");
+  if (fp == NULL)
+  {
+    printf("ERROR: could not open write file:%s\n", fmacContext->persistent_id_lookup_filename);
+    return;
+  }
+ 
+  fprintf(fp, "%s\n", GAP_FMREF_FILEHEADER);
+  fprintf(fp, "# type: %d=IMAGE,%d=ANIMIMAGE,%d=FRAMES,%d=MOVIE\n"
+          , GAP_AINFO_IMAGE, GAP_AINFO_ANIMIMAGE, GAP_AINFO_FRAMES, GAP_AINFO_MOVIE);
+
+  /* write all reference entries in the following format: 
+   * id:800000 frameNr:000001 stack:-1 file:"frame_000001.xcf"   mtime:123455678
+   */
+  for(fmref_entry = fmacContext->fmref_list; fmref_entry != NULL; fmref_entry = fmref_entry->next)
+  {
+    fprintf(fp, "%s%06d %s%06d %s%02d %s%d %s%011d  %s%d %s\"%s\"\n"
+           , GAP_FMREF_ID         , fmref_entry->persistent_drawable_id
+           , GAP_FMREF_FRAME_NR   , fmref_entry->frame_nr
+           , GAP_FMREF_STACK      , fmref_entry->stackposition
+           , GAP_FMREF_TRACK      , fmref_entry->track
+           , GAP_FMREF_MTIME      , fmref_entry->mtime
+           , GAP_FMREF_TYPE       , fmref_entry->ainfo_type
+           , GAP_FMREF_FILE       , fmref_entry->filename
+           );
+  }
+  
+  fclose(fp);
+
+}  /* end gap_fmct_save_GapFmacContext */
+
+/* --------------------------------------------
+ * gap_fmct_debug_print_GapFmacContext
+ * --------------------------------------------
+ * print the current filtermacro context for debug purpose.
+ */
+void
+gap_fmct_debug_print_GapFmacContext(GapFmacContext *fmacContext)
+{
+  GapFmacRefEntry *fmref_entry;
+
+  printf("fmacContext START  persistent_id_lookup_filename:%s\n"
+      , fmacContext->persistent_id_lookup_filename
+      );
+
+  printf("  recording_mode:%d  enabled:%d ffetch_user_id:%d\n"
+      , fmacContext->recording_mode
+      , fmacContext->enabled
+      , fmacContext->ffetch_user_id
+      ); 
+
+  for(fmref_entry = fmacContext->fmref_list; fmref_entry != NULL; fmref_entry = fmref_entry->next)
+  {
+    printf("%s%06d %s%06d %s%02d %s%d %s%011d  %s%d %s\"%s\"\n"
+           , GAP_FMREF_ID         , fmref_entry->persistent_drawable_id
+           , GAP_FMREF_FRAME_NR   , fmref_entry->frame_nr
+           , GAP_FMREF_STACK      , fmref_entry->stackposition
+           , GAP_FMREF_TRACK      , fmref_entry->track
+           , GAP_FMREF_MTIME      , fmref_entry->mtime
+           , GAP_FMREF_TYPE       , fmref_entry->ainfo_type
+           , GAP_FMREF_FILE       , fmref_entry->filename
+           );
+  }
+
+  printf("fmacContext END persistent_id_lookup_filename:%s\n"
+      , fmacContext->persistent_id_lookup_filename
+      );
+
+}  /* end gap_fmct_debug_print_GapFmacContext */
+
+
+
+/* --------------------------------------------
+ * gap_fmct_get_GapFmacRefEntry_by_persitent_id
+ * --------------------------------------------
+ */
+GapFmacRefEntry *
+gap_fmct_get_GapFmacRefEntry_by_persitent_id(GapFmacContext *fmacContext, gint32 persistent_drawable_id)
+{
+  GapFmacRefEntry *fmref_entry;
+
+  for(fmref_entry = fmacContext->fmref_list; fmref_entry != NULL; fmref_entry = fmref_entry->next)
+  {
+    if (fmref_entry->persistent_drawable_id == persistent_drawable_id)
+    {
+      return(fmref_entry);
+    }
+  }
+  return (NULL);
+  
+}  /* end gap_fmct_get_GapFmacRefEntry_by_persitent_id */
+
+/* --------------------------------------------
+ * gap_fmct_add_GapFmacRefEntry
+ * --------------------------------------------
+ */
+gint32
+gap_fmct_add_GapFmacRefEntry(GapLibAinfoType ainfo_type
+  , gint32 frame_nr
+  , gint32 stackposition
+  , gint32 track
+  , gint32 drawable_id
+  , const char *filename
+  , gboolean force_id
+  , GapFmacContext *fmacContext)
+{
+  GapFmacRefEntry *new_fmref_entry;
+  GapFmacRefEntry *fmref_entry;
+  gint32           l_max_persistent_drawable_id;
+  
+  if(filename == NULL)
+  {
+     return (drawable_id);
+  } 
+  if(!g_file_test(filename, G_FILE_TEST_EXISTS))
+  {
+    return (drawable_id);
+  }
+  if(fmacContext == NULL)
+  {
+     return (drawable_id);
+  } 
+
+
+  l_max_persistent_drawable_id = GAP_FMCT_MIN_PERSISTENT_DRAWABLE_ID;
+
+  /* check if entry already present in the list */
+  for(fmref_entry = fmacContext->fmref_list; fmref_entry != NULL; fmref_entry = fmref_entry->next)
+  {
+    if((fmref_entry->ainfo_type == ainfo_type)
+    && (fmref_entry->frame_nr == frame_nr)
+    && (fmref_entry->stackposition == stackposition)
+    && (fmref_entry->track == track)
+    && (strcmp(fmref_entry->filename, filename) == 0))
+    {
+      /* the list already contains an equal entry, nothing left to do.. */
+      return(fmref_entry->persistent_drawable_id);
+    }
+    l_max_persistent_drawable_id = MAX(l_max_persistent_drawable_id, fmref_entry->persistent_drawable_id);
+  }
+
+  /* add the new entry (and assign a unique persistent_drawable_id) */
+  new_fmref_entry = g_new(GapFmacRefEntry, 1);
+
+  if(force_id)
+  {
+    new_fmref_entry->persistent_drawable_id = drawable_id;
+  }
+  else
+  {
+    new_fmref_entry->persistent_drawable_id = l_max_persistent_drawable_id + 1;
+  }
+  new_fmref_entry->ainfo_type = ainfo_type;
+  new_fmref_entry->frame_nr = frame_nr;
+  new_fmref_entry->stackposition = stackposition;
+  new_fmref_entry->track = track;
+  new_fmref_entry->mtime = gap_file_get_mtime(filename);
+  g_snprintf(new_fmref_entry->filename, sizeof(new_fmref_entry->filename), "%s", filename);
+
+    
+  new_fmref_entry->next = fmacContext->fmref_list;
+  fmacContext->fmref_list = new_fmref_entry;
+
+  gap_fmct_save_GapFmacContext(fmacContext);
+
+  return(new_fmref_entry->persistent_drawable_id);
+
+}  /* end gap_fmct_add_GapFmacRefEntry */
+

Added: trunk/gap/gap_fmac_context.h
==============================================================================
--- (empty file)
+++ trunk/gap/gap_fmac_context.h	Thu Jul 31 18:05:59 2008
@@ -0,0 +1,96 @@
+/* gap_fmac_context.h
+ *
+ *
+ *  This module handles the filtermacro context.
+ *  The filtermacro context is used for itration of "persistent drawable ids" 
+ *  for animated (or constant) filter apply.
+ *  If gap controlled filterapply is done via a filtermacro
+ *  the iteration is done within a filtermacro context.
+ *  (e.g. while filtermacro is beeing recorded or is applied)
+ *  the handled drawable ids are mapped with the help of a filtermacro reference file
+ *  In this case the "persitent_drawable_id" is used to open the referenced
+ *  image, frame or videoframe at apply time (this may happen in another gimp
+ *  session than the recording of the filtermacro was done).
+ *  
+ *
+ * Copyright (C) 2008 Wolfgang Hofer <hof gimp org>
+ *
+ *
+ * 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.
+ */
+
+#ifndef _GAP_FMAC_CONTEXT_H
+#define _GAP_FMAC_CONTEXT_H
+
+#include "libgimp/gimp.h"
+#include "libgimp/gimp.h"
+#include "gap_lib_common_defs.h"
+
+typedef struct GapFmacRefEntry {
+  GapLibAinfoType ainfo_type;
+  gint32      persistent_drawable_id;
+  gint32      frame_nr;
+  gint32      stackposition;
+  gint32      track;
+  gint32      mtime;
+  char        filename[1024];
+  struct GapFmacRefEntry *next;
+} GapFmacRefEntry;
+
+
+typedef struct GapFmacContext {
+  gboolean recording_mode;
+  gboolean enabled;
+  gint32 ffetch_user_id;
+  char persistent_id_lookup_filename[1024];
+  GapFmacRefEntry  *fmref_list;
+} GapFmacContext;
+
+
+
+#define GAP_FMREF_FILENAME_EXTENSION  ".fmref"
+#define GAP_FMREF_FILEHEADER          "GAP_FILTERMACRO_PERSITENT_DRAWABLE_ID_LOOKUP_FILE"
+#define GAP_FMREF_ID         "id:"
+#define GAP_FMREF_FRAME_NR   "frameNr:"
+#define GAP_FMREF_STACK      "stack:"
+#define GAP_FMREF_TRACK      "track:"
+#define GAP_FMREF_MTIME      "mtime:"
+#define GAP_FMREF_TYPE       "type:"
+#define GAP_FMREF_FILE       "file:"
+
+
+#define GAP_FMAC_CONTEXT_KEYWORD   "GAP_FMAC_CONTEXT_KEYWORD"
+#define GAP_FMCT_MIN_PERSISTENT_DRAWABLE_ID  800000
+
+void                 gap_fmct_set_derived_lookup_filename(GapFmacContext *fmacContext, const char *filename);
+void                 gap_fmct_setup_GapFmacContext(GapFmacContext *fmacContext, gboolean recording_mode, const char *filename);
+void                 gap_fmct_disable_GapFmacContext(void);
+void                 gap_fmct_debug_print_GapFmacContext(GapFmacContext *fmacContext);
+
+void                 gap_fmct_load_GapFmacContext(GapFmacContext *fmacContext);
+void                 gap_fmct_save_GapFmacContext(GapFmacContext *fmacContext);
+GapFmacRefEntry *    gap_fmct_get_GapFmacRefEntry_by_persitent_id(GapFmacContext *fmacContext
+                                  , gint32 persistent_drawable_id);
+void                 gap_fmct_free_GapFmacRefList(GapFmacContext *fmacContext);
+gint32               gap_fmct_add_GapFmacRefEntry(GapLibAinfoType ainfo_type
+                                  , gint32 frame_nr
+                                  , gint32 stackposition
+                                  , gint32 track
+                                  , gint32 drawable_id
+                                  , const char *filename
+                                  , gboolean force_id
+                                  , GapFmacContext *fmacContext);
+
+#endif

Modified: trunk/gap/gap_fmac_main.c
==============================================================================
--- trunk/gap/gap_fmac_main.c	(original)
+++ trunk/gap/gap_fmac_main.c	Thu Jul 31 18:05:59 2008
@@ -58,6 +58,7 @@
 #include "gap_fmac_base.h"
 #include "gap_dbbrowser_utils.h"
 #include "gap_lastvaldesc.h"
+#include "gap_fmac_context.h"
 
 /* revision history:
  * gimp   1.3.26b;  2004/02/29  hof: bugfix NONINTERACTIVE call did crash
@@ -129,6 +130,7 @@
 static gboolean  p_chk_filtermacro_file(const char *filtermacro_file);
 static void      p_print_and_free_msg(char *msg, GimpRunMode run_mode);
 static gchar *   p_get_gap_filter_data_string(const char *plugin_name);
+static gchar *   p_get_mapped_gap_filter_data_string(const char *plugin_name, const char *filtermacro_file);
 static gint      p_fmac_add_filter_to_file(const char *filtermacro_file, const char *plugin_name);
 static gint      p_fmac_add_filter(const char *filtermacro_file, gint32 image_id);
 static int       p_fmac_pdb_constraint_proc(gchar *proc_name, gint32 image_id);
@@ -398,7 +400,10 @@
   plugin_data = NULL;
 
 
-   if(gap_debug) printf("p_get_gap_lastfilter: plugin_name:%s:\n", plugin_name);
+   if(gap_debug)
+   {
+     printf("p_get_gap_filter_data_string: plugin_name:%s:\n", plugin_name);
+   }
 
    plugin_data_len = gimp_get_data_size (plugin_name);
    if (plugin_data_len > 0)
@@ -426,7 +431,10 @@
         l_str = g_strdup_printf("%s\n", l_str_tmp);
         g_free(l_str_tmp);
 
-        if (gap_debug) printf("p_get_gap_lastfilter: %s", l_str);
+        if (gap_debug)
+        {
+          printf("p_get_gap_filter_data_string: %s", l_str);
+        }
 
         data_string = l_str;
         g_free(plugin_data);
@@ -438,6 +446,121 @@
 }  /* end p_get_gap_filter_data_string */
 
 
+/* -----------------------------------
+ * p_get_mapped_gap_filter_data_string
+ * -----------------------------------
+ *   return a textstring with the plugin_name
+ *   and its values as textstring
+ *   that has format like this:
+ *      "plug_in_name" len hexbyte1 hexbyte2 .......\n
+ *   example:
+ *      "plug_in_sharpen"    4  0a 00 00 00
+ *
+ *
+ *  In case the plugin has at least one iterable drawable id within its last values parameters
+ *  those values are mapped to persistent_drawable_id's with the help of a special
+ *  iterator call with a filtermacro context and a corresponding .fmref file in recording mode.
+ *
+ * return data_string or  NULL pointer if nothing was found.
+ *        the returned data_string should be g_free'd by the caller (if it was not NULL)
+ * 
+ */
+static gchar *
+p_get_mapped_gap_filter_data_string(const char *plugin_name, const char *filtermacro_file)
+{
+  static char l_key_from[512];
+  static char l_key_to[512];
+  gint plugin_data_len;
+  guchar *plugin_data_bck;
+  gchar  *data_string;
+  const char *l_iteratorname;
+  gint l_count;
+
+
+  data_string = NULL;
+ 
+  if(plugin_name == NULL)
+  {
+    return (NULL);
+  }
+  plugin_data_len = gimp_get_data_size (plugin_name);
+
+  if(gap_debug)
+  {
+    printf("p_get_mapped_gap_filter_data_string: plugin_name:%s:  plugin_data_len:%d\n"
+       , plugin_name
+       , plugin_data_len
+       );
+  }
+
+  /* assign the matching Iterator PluginProcedures (if there is any) */
+  l_iteratorname = gap_filt_pdb_get_iterator_proc(plugin_name, &l_count);
+
+  if ((plugin_data_len > 0) && (l_iteratorname != NULL))
+  {
+    GapFmacContext theFmacContext;
+    GapFmacContext *fmacContext;
+
+    fmacContext = &theFmacContext;
+
+    if(gap_debug)
+    {
+      printf("p_get_mapped_gap_filter_data_string: l_iteratorname:%s:\n"
+         , l_iteratorname
+         );
+    }
+    gap_fmct_setup_GapFmacContext(fmacContext
+                                 , TRUE  /* recording_mode */
+                                 , filtermacro_file
+                                 );
+
+
+    /* retrieve the data (to backup original data) */
+    plugin_data_bck = g_malloc0(plugin_data_len);
+    gimp_get_data(plugin_name, plugin_data_bck);
+
+
+    /* Set FROM and TO buffers. 
+     * (in recording mode we use all the same data as the backup of the original buffer)
+     * those buffers are not relevant in recording mode, but are required
+     * for the iterator call interface.
+     */
+    g_snprintf(l_key_from, sizeof(l_key_from), "%s%s", plugin_name, GAP_ITER_TO_SUFFIX);
+    gimp_set_data(l_key_from, plugin_data_bck, plugin_data_len);
+
+    g_snprintf(l_key_to, sizeof(l_key_to), "%s%s", plugin_name, GAP_ITER_FROM_SUFFIX);
+    gimp_set_data(l_key_to, plugin_data_bck, plugin_data_len);
+
+    /* call the iterator in recording mode
+     * this triggers the mapping of drawable ids in the persistent .fmref file.
+     * (but only in case the called plugin has at least one an iterable drawable_id in its last_values paramters,
+     * otherwise the las values buffer shall not change by the iteratorcall,
+     * due to same settings for from and to values) 
+     */
+    gap_filter_iterator_call(l_iteratorname
+       , 1       /* total_steps */
+       , 1.0     /* current_step */
+       , plugin_name
+       , plugin_data_len
+       );
+
+    data_string = p_get_gap_filter_data_string(plugin_name);
+
+    /* restore original data from backup buffer */
+    gimp_set_data(plugin_name, plugin_data_bck, plugin_data_len);
+
+    g_free(plugin_data_bck);
+    
+    /* disable the sessionwide filtermacro context */
+    gap_fmct_disable_GapFmacContext();
+
+  }
+
+  return (data_string);
+
+}  /* end p_get_mapped_gap_filter_data_string */
+
+
 /* -------------------------
  * p_fmac_add_filter_to_file
  * -------------------------
@@ -472,12 +595,12 @@
   {
     char *data_string;
     char *canonical_plugin_name;
-    
+
     canonical_plugin_name = gimp_canonicalize_identifier(plugin_name);
 
-    data_string = p_get_gap_filter_data_string(canonical_plugin_name);
+    data_string = p_get_mapped_gap_filter_data_string(canonical_plugin_name, filtermacro_file);
     g_free(canonical_plugin_name);
-    
+
     if(data_string)
     {
       fprintf(fp, "%s", data_string);

Added: trunk/gap/gap_frame_fetcher.c
==============================================================================
--- (empty file)
+++ trunk/gap/gap_frame_fetcher.c	Thu Jul 31 18:05:59 2008
@@ -0,0 +1,872 @@
+/* gap_frame_fetcher.c
+ *
+ *
+ *  The FrameFetcher provides access to frames both from imagefiles and videofiles.
+ *  
+ *  It holds a global image cache of temporary gimp images intended for
+ *  read only access in various gimp-gap render processings.
+ *  
+ *  There are methods to get the temporary image 
+ *  or to get a duplicate that has only one layer at imagesize.
+ *  (merged or picked via desired stackposition)
+ *
+ *  For videofiles it holds a cache of open videofile handles.
+ *  (note that caching of videoframes is already available in the videohandle)
+ *  
+ *  The current implementation of the frame fetcher is NOT multithred save !
+ *  (the procedures may drop cached images that are still in use by a concurrent thread
+ *  further the cache lists can be messe up if they are modified by concurrent threads
+ *  at the same time.
+ *
+ *  Currently there is no support to keep track of cached images during the full length
+ *  of a gimp session. This simple version of the frame fetcher is limited 
+ *  to one main program (such as the storyboard or the filtermacro plug-in)
+ *  and loses its information on exit of the main program.
+ *  (If there are still registrated users at exit time, the cached images are still
+ *  loaded in the gimp session)
+ *  TODO: a more sophisticated version of the frame fetcher may keep its information
+ *        using the gimp_det_data feature or mark the cached images with a tattoo.
+ *
+
+ *
+ *
+ * Copyright (C) 2008 Wolfgang Hofer <hof gimp org>
+ *
+ *
+ * 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.
+ */
+
+/*
+ * 2008.08.20  hof  - created (moved image cache stuff from gap_story_render_processing modules to this  new module)
+ *                  - new feature:  caching of videohandles.
+ *
+ */
+
+#include <config.h>
+
+/* SYTEM (UNIX) includes */
+#include <stdlib.h>
+//#include <sys/types.h>
+//#include <sys/stat.h>
+//#include <math.h>
+//#include <errno.h>
+
+//#include <dirent.h>
+
+
+#include <glib/gstdio.h>
+
+
+
+/* GIMP includes */
+#include "gtk/gtk.h"
+#include "gap-intl.h"
+#include "libgimp/gimp.h"
+
+
+#include "gap_libgimpgap.h"
+#include "gap_lib_common_defs.h"
+#include "gap_file_util.h"
+#include "gap_vid_api.h"
+#include "gap_layer_copy.h"
+//#include "gap_fmac_name.h"
+
+#include "gap_frame_fetcher.h"
+
+#define GAP_FFETCH_MAX_IMG_CACHE_ELEMENTS 12
+#define GAP_FFETCH_MAX_GVC_CACHE_ELEMENTS 6
+#define GAP_FFETCH_GVA_FRAMES_TO_KEEP_CACHED 36
+
+
+
+typedef struct GapFFetchResourceUserElem
+{
+   gint32  ffetch_user_id;
+   void *next;
+} GapFFetchResourceUserElem;
+
+
+
+typedef struct GapFFetchDuplicatedImagesElem
+{
+   gint32  ffetch_user_id;
+   gint32  image_id;
+   void *next;
+} GapFFetchDuplicatedImagesElem;
+
+
+/* -------- types for the image cache  ------- */
+
+typedef struct GapFFetchImageCacheElem
+{
+   gint32  image_id;
+   char   *filename;
+   void *next;
+} GapFFetchImageCacheElem;
+
+typedef struct GapFFetchImageCache
+{
+  GapFFetchImageCacheElem *ic_list;
+  gint32            max_img_cache;  /* number of images to hold in the cache */
+} GapFFetchImageCache;
+
+
+/* -------- types for the video handle cache  ------- */
+
+typedef struct GapFFetchGvahandCacheElem
+{
+   t_GVA_Handle *gvahand;
+   gint32        mtime;
+   gint32        seltrack;
+   char         *filename;
+   void *next;
+} GapFFetchGvahandCacheElem;
+
+
+typedef struct GapFFetchGvahandCache
+{
+  GapFFetchGvahandCacheElem *gvc_list;
+  gint32            max_vid_cache;  /* number of videohandles to hold in the cache */
+} GapFFetchGvahandCache;
+
+
+
+extern int gap_debug;  /* 1 == print debug infos , 0 dont print debug infos */
+
+/*************************************************************
+ *         STATIC varaibles                                  *
+ *************************************************************
+ */
+
+static GapFFetchDuplicatedImagesElem *global_duplicated_images = NULL;
+
+static GapFFetchImageCache *global_imcache = NULL;
+
+static GapFFetchGvahandCache *global_gvcache = NULL;
+
+static GapFFetchResourceUserElem *global_rsource_users = NULL;
+
+/*************************************************************
+ *         FRAME FETCHER procedures                          *
+ *************************************************************
+ */
+static gint32         p_load_cache_image(const char* filename, gboolean addToCache);
+static void           p_drop_image_cache_elem1(GapFFetchImageCache *imcache);
+static void           p_drop_image_cache(void);
+#ifdef GAP_ENABLE_VIDEOAPI_SUPPORT
+static void           p_drop_gvahand_cache_elem1(GapFFetchGvahandCache *gvcache);
+static void           p_drop_vidhandle_cache(void);
+
+static t_GVA_Handle*  p_ffetch_get_open_gvahand(const char* filename, gint32 seltrack
+                          , const char *preferred_decoder);
+#endif
+
+static void           p_add_image_to_list_of_duplicated_images(gint32 image_id, gint32 ffetch_user_id);
+
+
+/* ----------------------------------------------------
+ * p_find_img_cache_by_image_id
+ * ----------------------------------------------------
+ */
+static GapFFetchImageCacheElem  *
+p_find_img_cache_by_image_id(gint32 image_id)
+{
+  GapFFetchImageCacheElem  *ic_ptr;
+
+  if((global_imcache == NULL) || (image_id < 0))
+  {
+    return (NULL);
+  }
+
+  for(ic_ptr = global_imcache->ic_list; ic_ptr != NULL; ic_ptr = (GapFFetchImageCacheElem *)ic_ptr->next)
+  {
+    if(ic_ptr->image_id == image_id)
+    {
+      /* image found in cache */
+      return(ic_ptr);
+    }
+  }
+
+  return (NULL);
+}  /* end  p_find_img_cache_by_image_id */
+
+
+/* ----------------------------------------------------
+ * p_load_cache_image
+ * ----------------------------------------------------
+ */
+static gint32
+p_load_cache_image(const char* filename, gboolean addToCache)
+{
+  gint32 l_idx;
+  gint32 l_image_id;
+  GapFFetchImageCacheElem  *ic_ptr;
+  GapFFetchImageCacheElem  *ic_last;
+  GapFFetchImageCacheElem  *ic_new;
+  GapFFetchImageCache  *imcache;
+  char *l_filename;
+
+  if(filename == NULL)
+  {
+    printf("p_load_cache_image: ** ERROR cant load filename == NULL!\n");
+    return -1;
+  }
+
+  if(global_imcache == NULL)
+  {
+    /* init the global_image cache */
+    global_imcache = g_malloc0(sizeof(GapFFetchImageCache));
+    global_imcache->ic_list = NULL;
+    global_imcache->max_img_cache = GAP_FFETCH_MAX_IMG_CACHE_ELEMENTS;
+  }
+
+  imcache = global_imcache;
+  ic_last = imcache->ic_list;
+
+  l_idx = 0;
+  for(ic_ptr = imcache->ic_list; ic_ptr != NULL; ic_ptr = (GapFFetchImageCacheElem *)ic_ptr->next)
+  {
+    l_idx++;
+    if(strcmp(filename, ic_ptr->filename) == 0)
+    {
+      if(gap_debug)
+      {
+        printf("FrameFetcher: p_load_cache_image CACHE-HIT :%s (image_id:%d)\n"
+          , ic_ptr->filename, (int)ic_ptr->image_id);
+      }
+      /* image found in cache, can skip load */
+      return(ic_ptr->image_id);
+    }
+    ic_last = ic_ptr;
+  }
+
+  l_filename = g_strdup(filename);
+  l_image_id = gap_lib_load_image(l_filename);
+  if(gap_debug)
+  {
+    printf("FrameFetcher: loaded imafe from disk:%s (image_id:%d)\n"
+      , l_filename, (int)l_image_id);
+  }
+
+  if((l_image_id >= 0) && (addToCache == TRUE))
+  {
+    ic_new = g_malloc0(sizeof(GapFFetchImageCacheElem));
+    ic_new->filename = l_filename;
+    ic_new->image_id = l_image_id;
+
+    if(imcache->ic_list == NULL)
+    {
+      imcache->ic_list = ic_new;   /* 1.st elem starts the list */
+    }
+    else
+    {
+      ic_last->next = (GapFFetchImageCacheElem *)ic_new;  /* add new elem at end of the cache list */
+    }
+
+    if(l_idx > imcache->max_img_cache)
+    {
+      /* chache list has more elements than desired,
+       * drop the 1.st (oldest) entry in the chache list
+       */
+      p_drop_image_cache_elem1(imcache);
+    }
+  }
+  else
+  {
+    g_free(l_filename);
+  }
+  return(l_image_id);
+}  /* end p_load_cache_image */
+
+
+/* ----------------------------------------------------
+ * p_drop_image_cache_elem1
+ * ----------------------------------------------------
+ */
+static void
+p_drop_image_cache_elem1(GapFFetchImageCache *imcache)
+{
+  GapFFetchImageCacheElem  *ic_ptr;
+
+  if(imcache)
+  {
+    ic_ptr = imcache->ic_list;
+    if(ic_ptr)
+    {
+      if(gap_debug)
+      {
+        printf("p_drop_image_cache_elem1 delete:%s (image_id:%d)\n"
+          , ic_ptr->filename, (int)ic_ptr->image_id);
+      }
+      gap_image_delete_immediate(ic_ptr->image_id);
+      g_free(ic_ptr->filename);
+      imcache->ic_list = (GapFFetchImageCacheElem  *)ic_ptr->next;
+      g_free(ic_ptr);
+    }
+  }
+}  /* end p_drop_image_cache_elem1 */
+
+
+
+/* ----------------------------------------------------
+ * p_drop_image_cache
+ * ----------------------------------------------------
+ * drop the image cache.
+ */
+static void
+p_drop_image_cache(void)
+{
+  GapFFetchImageCache *imcache;
+
+  if(gap_debug)
+  {
+    printf("p_drop_image_cache START\n");
+  }
+  imcache = global_imcache;
+  if(imcache)
+  {
+    while(imcache->ic_list)
+    {
+      p_drop_image_cache_elem1(imcache);
+    }
+  }
+  global_imcache = NULL;
+
+  if(gap_debug)
+  {
+    printf("p_drop_image_cache END\n");
+  }
+
+}  /* end p_drop_image_cache */
+
+
+
+#ifdef GAP_ENABLE_VIDEOAPI_SUPPORT
+
+
+/* ----------------------------------------------------
+ * p_drop_gvahand_cache_elem1
+ * ----------------------------------------------------
+ */
+static void
+p_drop_gvahand_cache_elem1(GapFFetchGvahandCache *gvcache)
+{
+  GapFFetchGvahandCacheElem  *gvc_ptr;
+
+  if(gvcache)
+  {
+    gvc_ptr = gvcache->gvc_list;
+    if(gvc_ptr)
+    {
+      if(gap_debug)
+      {
+        printf("p_drop_gvahand_cache_elem1 delete:%s (gvahand:%d seltrack:%d mtime:%ld)\n"
+               , gvc_ptr->filename, (int)gvc_ptr->gvahand
+               , (int)gvc_ptr->seltrack, (long)gvc_ptr->mtime);
+      }
+      GVA_close(gvc_ptr->gvahand);
+      g_free(gvc_ptr->filename);
+      gvcache->gvc_list = (GapFFetchGvahandCacheElem  *)gvc_ptr->next;
+      g_free(gvc_ptr);
+    }
+  }
+}  /* end p_drop_gvahand_cache_elem1 */
+
+
+/* ----------------------------------------------------
+ * p_drop_vidhandle_cache
+ * ----------------------------------------------------
+ */
+static void
+p_drop_vidhandle_cache(void)
+{
+  GapFFetchGvahandCache *gvc_cache;
+
+  if(gap_debug)
+  {
+    printf("p_drop_vidhandle_cache START\n");
+  }
+  gvc_cache = global_gvcache;
+  if(gvc_cache)
+  {
+    while(gvc_cache->gvc_list)
+    {
+      p_drop_gvahand_cache_elem1(gvc_cache);
+    }
+  }
+  global_gvcache = NULL;
+
+  if(gap_debug)
+  {
+    printf("p_drop_vidhandle_cache END\n");
+  }
+  
+
+}  /* end p_drop_vidhandle_cache */
+
+
+
+
+/* ----------------------------------------------------
+ * p_ffetch_get_open_gvahand
+ * ----------------------------------------------------
+ * get videohandle from the cache of already open handles.
+ * opens a new handle if there is no cache hit.
+ * if too many handles are alredy open, the oldest one is closed.
+ * note that the cached handles must not be used outside the
+ * frame fetcher module. Therefore the closing old handles
+ * can be done.
+ */
+static t_GVA_Handle*
+p_ffetch_get_open_gvahand(const char* filename, gint32 seltrack, const char *preferred_decoder)
+{
+  gint32 l_idx;
+  t_GVA_Handle *l_gvahand;
+  GapFFetchGvahandCacheElem  *gvc_ptr;
+  GapFFetchGvahandCacheElem  *gvc_last;
+  GapFFetchGvahandCacheElem  *gvc_new;
+  GapFFetchGvahandCache      *gvcache;
+
+  if(filename == NULL)
+  {
+    printf("p_ffetch_get_open_gvahand: ** ERROR cant load video filename == NULL!\n");
+    return (NULL);
+  }
+
+  if(global_gvcache == NULL)
+  {
+    /* init the global_image cache */
+    global_gvcache = g_malloc0(sizeof(GapFFetchGvahandCache));
+    global_gvcache->gvc_list = NULL;
+    global_gvcache->max_vid_cache = GAP_FFETCH_MAX_GVC_CACHE_ELEMENTS;
+  }
+
+  gvcache = global_gvcache;
+  gvc_last = gvcache->gvc_list;
+
+  l_idx = 0;
+  for(gvc_ptr = gvcache->gvc_list; gvc_ptr != NULL; gvc_ptr = (GapFFetchGvahandCacheElem *)gvc_ptr->next)
+  {
+    l_idx++;
+    if((strcmp(filename, gvc_ptr->filename) == 0) && (seltrack == gvc_ptr->seltrack))
+    {
+      /* videohandle found in cache, can skip opening a new handle */
+      return(gvc_ptr->gvahand);
+    }
+    gvc_last = gvc_ptr;
+  }
+
+  if(preferred_decoder)
+  {
+    l_gvahand = GVA_open_read_pref(filename
+                                  , seltrack
+                                  , 1 /* aud_track */
+                                  , preferred_decoder
+                                  , FALSE  /* use MMX if available (disable_mmx == FALSE) */
+                                  );
+  }
+  else
+  {
+    l_gvahand = GVA_open_read(filename
+                             ,seltrack
+                             ,1 /* aud_track */
+                             );
+  }
+  
+  if(l_gvahand)
+  {
+    GVA_set_fcache_size(l_gvahand, GAP_FFETCH_GVA_FRAMES_TO_KEEP_CACHED);
+
+    gvc_new = g_malloc0(sizeof(GapFFetchGvahandCacheElem));
+    gvc_new->filename = g_strdup(filename);
+    gvc_new->seltrack = seltrack;
+    gvc_new->mtime = GVA_file_get_mtime(filename);
+    gvc_new->gvahand = l_gvahand;
+
+    if(gvcache->gvc_list == NULL)
+    {
+      gvcache->gvc_list = gvc_new;   /* 1.st elem starts the list */
+    }
+    else
+    {
+      gvc_last->next = (GapFFetchGvahandCacheElem *)gvc_new;  /* add new elem at end of the cache list */
+    }
+
+    if(l_idx > gvcache->max_vid_cache)
+    {
+      /* chache list has more elements than desired,
+       * drop the 1.st (oldest) entry in the chache list
+       * (this closes the droped handle)
+       */
+      p_drop_gvahand_cache_elem1(gvcache);
+    }
+  }
+  return(l_gvahand);
+}  /* end p_ffetch_get_open_gvahand */
+
+#endif
+
+/* -----------------------------------------
+ * p_add_image_to_list_of_duplicated_images
+ * -----------------------------------------
+ * add specified image to the list of duplicated images.
+ * this list contains temporary images of both fetched video frames
+ * and merged duplicates of the cached original images.
+ */
+static void
+p_add_image_to_list_of_duplicated_images(gint32 image_id, gint32 ffetch_user_id)
+{
+  GapFFetchDuplicatedImagesElem *dupElem;
+  
+  dupElem = g_new(GapFFetchDuplicatedImagesElem, 1);
+  dupElem->next = global_duplicated_images;
+  dupElem->image_id = image_id;
+  dupElem->ffetch_user_id = ffetch_user_id;
+  global_duplicated_images = dupElem;
+}   /* end p_add_image_to_list_of_duplicated_images */
+
+
+/* -------------------------------------------------
+ * gap_frame_fetch_delete_list_of_duplicated_images
+ * -------------------------------------------------
+ * deletes all duplicate imageas that wre created by the specified ffetch_user_id
+ * (if ffetch_user_id -1 is specified delte all duplicated images)
+ */
+void
+gap_frame_fetch_delete_list_of_duplicated_images(gint32 ffetch_user_id)
+{
+  GapFFetchDuplicatedImagesElem *dupElem;
+  GapFFetchDuplicatedImagesElem *nextDupElem;
+  
+  dupElem = global_duplicated_images;
+  while(dupElem)
+  {
+    nextDupElem = dupElem->next;
+
+    if (((ffetch_user_id == dupElem->ffetch_user_id) || (ffetch_user_id < 0))
+    && (dupElem->image_id >= 0))
+    {
+      gap_image_delete_immediate(dupElem->image_id);
+      dupElem->image_id = -1;  /* set image invalid */
+    }
+
+    if (ffetch_user_id < 0)
+    {
+      g_free(dupElem);
+    }
+
+    dupElem = nextDupElem;
+  }
+  if (ffetch_user_id < 0)
+  {
+    global_duplicated_images = NULL;
+  }
+}  /* end gap_frame_fetch_delete_list_of_duplicated_images */
+
+
+
+
+
+
+/* ----------------------------
+ * gap_frame_fetch_orig_image
+ * ----------------------------
+ * returns image_id of the original cached image.
+ */
+gint32
+gap_frame_fetch_orig_image(gint32 ffetch_user_id
+    ,const char *filename            /* full filename of the image */
+    ,gboolean addToCache             /* enable caching */
+    )
+{
+  return (p_load_cache_image(filename, addToCache));
+}  /* end gap_frame_fetch_orig_image */
+
+
+
+/* ----------------------------
+ * gap_frame_fetch_dup_image
+ * ----------------------------
+ * returns merged or selected layer_id 
+ *        (that is the only visible layer in temporary created scratch image)
+ *        the caller is resonsible to delete the scratch image when processing is done.
+ *         this can be done by calling gap_frame_fetch_delete_list_of_duplicated_images()
+ */
+gint32
+gap_frame_fetch_dup_image(gint32 ffetch_user_id
+    ,const char *filename            /* full filename of the image (already contains framenr) */
+    ,gint32      stackpos            /* 0 pick layer on top of stack, -1 merge visible layers */
+    ,gboolean addToCache             /* enable caching */
+    )
+{
+  gint32 resulting_layer;
+  gint32 image_id;
+  gint32 dup_image_id;
+
+  resulting_layer = -1;
+  image_id = p_load_cache_image(filename, addToCache);
+  if (image_id < 0)
+  {
+    return(-1);
+  }
+  
+  if (stackpos < 0)
+  {
+    dup_image_id = gimp_image_duplicate(image_id);
+    resulting_layer = gap_image_merge_visible_layers(dup_image_id, GIMP_CLIP_TO_IMAGE);
+  }
+  else
+  {
+    gint          l_nlayers;
+    gint32       *l_layers_list;
+     
+
+    l_layers_list = gimp_image_get_layers(image_id, &l_nlayers);
+    if(l_layers_list != NULL)
+    {
+      if (stackpos < l_nlayers)
+      {
+        gint32 src_layer_id;
+
+        src_layer_id = l_layers_list[stackpos];
+        dup_image_id = gimp_image_new (gimp_image_width(image_id)
+                                     , gimp_image_height(image_id)
+                                     , gimp_image_base_type(image_id)
+                                     );
+        resulting_layer = gap_layer_copy_to_image (dup_image_id, src_layer_id);
+      }
+      
+      g_free (l_layers_list);
+    }
+  }
+
+  p_add_image_to_list_of_duplicated_images(dup_image_id, ffetch_user_id);
+
+
+  if (addToCache != TRUE)
+  {
+    GapFFetchImageCacheElem *ic_elem;
+    
+    ic_elem = p_find_img_cache_by_image_id(image_id);
+    
+    if (ic_elem == NULL)
+    {
+      /* the original image is not cached
+       * (delete it because the caller gets the preprocessed duplicate)
+       */
+      gap_image_delete_immediate(image_id);
+    }
+  }
+
+  return(resulting_layer);
+
+}  /* end gap_frame_fetch_dup_image */
+
+
+
+/* ----------------------------
+ * gap_frame_fetch_dup_video
+ * ----------------------------
+ * returns the fetched video frame as gimp layer_id.
+ *         the returned layer id is (the only layer) in a temporary image.
+ *         note the caller is responsible to delete that temporary image after processing is done.
+ *         this can be done by calling gap_frame_fetch_delete_list_of_duplicated_images()
+ */
+gint32
+gap_frame_fetch_dup_video(gint32 ffetch_user_id
+    ,const char *filename            /* full filename of a video */
+    ,gint32      framenr             /* frame within the video (starting at 1) */
+    ,gint32      seltrack            /* videotrack */
+    ,const char *preferred_decoder)
+{
+  gint32         l_layer_id = -1;
+#ifdef GAP_ENABLE_VIDEOAPI_SUPPORT
+  t_GVA_Handle *gvahand;
+  t_GVA_RetCode  l_fcr;
+  
+  gvahand = p_ffetch_get_open_gvahand(filename, seltrack, preferred_decoder);
+  if (gvahand == NULL)
+  {
+    return(-1);
+  }
+
+  /* attempt to get frame from the handles internal cache */
+  l_fcr = GVA_frame_to_gimp_layer(gvahand
+                                    , TRUE      /* delete_mode */
+                                    , framenr   /* framenumber */
+                                    , 0         /* deinterlace */
+                                    , 0.0       /* threshold */
+                                    );
+
+  if (l_fcr != GVA_RET_OK)
+  {
+    /* if no success, we try explicite read that frame  */
+    if(gvahand->current_seek_nr != framenr)
+    {
+      if(((gvahand->current_seek_nr + GAP_FFETCH_GVA_FRAMES_TO_KEEP_CACHED) > framenr)
+      &&  (gvahand->current_seek_nr < framenr ) )
+      {
+        /* near forward seek is performed by dummyreads to fill up the 
+         * handles internal framecache
+         */
+        while(gvahand->current_seek_nr < framenr)
+        {
+          GVA_get_next_frame(gvahand);
+        }
+      }
+      else
+      {
+        GVA_seek_frame(gvahand, (gdouble)framenr, GVA_UPOS_FRAMES);
+     }
+    }
+
+    if(GVA_get_next_frame(gvahand) == GVA_RET_OK)
+    {
+      GVA_frame_to_gimp_layer(gvahand
+                      , TRUE      /* delete_mode */
+                      , framenr   /* framenumber */
+                      , 0         /* deinterlace */
+                      , 0.0       /* threshold */
+                      );
+    }
+  }
+
+  /* return the newly created layer from the temporary image in the gvahand stucture.
+   */
+  l_layer_id = gvahand->layer_id;
+  
+  p_add_image_to_list_of_duplicated_images(gvahand->image_id, ffetch_user_id);
+  gvahand->image_id = -1;
+  gvahand->layer_id = -1;
+
+#endif  
+  return (l_layer_id);
+
+}  /* end gap_frame_fetch_dup_video */    
+
+
+/* -------------------------------------------------
+ * gap_frame_fetch_drop_resources
+ * -------------------------------------------------
+ * drop all cached resources and all working copies (in the list of duplicated images).
+ * (regardless if there are still resource users registrated)
+ */
+void
+gap_frame_fetch_drop_resources()
+{
+  gap_frame_fetch_delete_list_of_duplicated_images(-1);
+
+  p_drop_image_cache();
+  p_drop_vidhandle_cache();
+}  /* end gap_frame_fetch_drop_resources */
+
+
+/* -------------------------------------------------
+ * gap_frame_fetch_register_user
+ * -------------------------------------------------
+ * register for using the frame fetcher resource.
+ * returns a unique resource user id.
+ */
+gint32
+gap_frame_fetch_register_user(const char *caller_name)
+{
+  gint32 max_ffetch_user_id;
+  GapFFetchResourceUserElem *usr_ptr;
+  GapFFetchResourceUserElem *new_usr_ptr;
+  
+  max_ffetch_user_id = 0;
+  new_usr_ptr = NULL;
+  
+  for(usr_ptr = global_rsource_users; usr_ptr != NULL; usr_ptr = (GapFFetchResourceUserElem *)usr_ptr->next)
+  {
+    printf("usr_ptr->ffetch_user_id: %d  usr_ptr:%d\n", usr_ptr->ffetch_user_id, usr_ptr);
+  
+    if (usr_ptr->ffetch_user_id >= 0)
+    {
+      max_ffetch_user_id = MAX(max_ffetch_user_id, usr_ptr->ffetch_user_id);
+    }
+    else
+    {
+      new_usr_ptr = usr_ptr;  /* reuse inactive element */
+    }
+  }
+  
+  max_ffetch_user_id++;
+  if (new_usr_ptr == NULL)
+  {
+    new_usr_ptr = g_new(GapFFetchResourceUserElem, 1);
+    new_usr_ptr->next = global_rsource_users;
+    global_rsource_users = new_usr_ptr;
+  }
+  new_usr_ptr->ffetch_user_id = max_ffetch_user_id;
+  
+  if(gap_debug)
+  {
+    printf("gap_frame_fetch_register_user: REGISTRATED ffetch_user_id:%d  caller_name:%s  new_usr_ptr:%d\n"
+          , new_usr_ptr->ffetch_user_id
+          , caller_name
+          , new_usr_ptr
+          );
+  }
+  return (max_ffetch_user_id);
+}  /* end  gap_frame_fetch_register_user*/
+
+
+/* -------------------------------------------------
+ * gap_frame_fetch_unregister_user
+ * -------------------------------------------------
+ * unregister the specified resource user id.
+ + (if there are still registered resource users
+ *  cached images and videohandles are kept.
+ *  until the last resource user calls this procedure.
+ *  if there are no more registered users all
+ *  cached resources and duplicates are dropped)
+ */
+void
+gap_frame_fetch_unregister_user(gint32 ffetch_user_id)
+{
+  gint32 count_active_users;
+  GapFFetchResourceUserElem *usr_ptr;
+
+  if(gap_debug)
+  {
+    printf("gap_frame_fetch_unregister_user: UNREGISTER ffetch_user_id:%d\n"
+          , ffetch_user_id
+          );
+  }
+
+  count_active_users = 0;
+  for(usr_ptr = global_rsource_users; usr_ptr != NULL; usr_ptr = (GapFFetchResourceUserElem *)usr_ptr->next)
+  {
+    if (ffetch_user_id == usr_ptr->ffetch_user_id)
+    {
+      usr_ptr->ffetch_user_id = -1;
+    }
+    else if (usr_ptr->ffetch_user_id >= 0)
+    {
+      count_active_users++;
+    }
+  }
+  
+  if(count_active_users == 0)
+  {
+    if(gap_debug)
+    {
+      printf("gap_frame_fetch_unregister_user: no more resource users, DROP cached resources\n");
+    }
+    gap_frame_fetch_drop_resources();    
+  }
+
+}  /* end gap_frame_fetch_unregister_user */

Added: trunk/gap/gap_frame_fetcher.h
==============================================================================
--- (empty file)
+++ trunk/gap/gap_frame_fetcher.h	Thu Jul 31 18:05:59 2008
@@ -0,0 +1,137 @@
+/* gap_frame_fetcher.h
+ *
+ *
+ *  The FrameFetcher provides access to frames both from imagefiles and videofiles.
+ *  
+ *  It holds a global image cache of temporary gimp images intended for
+ *  read only access in various gimp-gap render processings.
+ *  
+ *  There are methods to get the temporary image 
+ *  or to get a duplicate that has only one layer at imagesize.
+ *  (merged or picked via desired stackposition)
+ *
+ *  For videofiles it holds a cache of open videofile handles.
+ *  (note that caching of videoframes is already available in the videohandle)
+ *  
+ *
+ * Copyright (C) 2008 Wolfgang Hofer <hof gimp org>
+ *
+ *
+ * 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.
+ */
+
+#ifndef _GAP_FRAME_FETCHER_H
+#define _GAP_FRAME_FETCHER_H
+
+#include "libgimp/gimp.h"
+#include "libgimp/gimp.h"
+#ifdef GAP_ENABLE_VIDEOAPI_SUPPORT
+#include "gap_vid_api.h"
+#else
+#ifndef GAP_STUBTYPE_GVA_HANDLE
+typedef gpointer t_GVA_Handle;
+#define GAP_STUBTYPE_GVA_HANDLE
+#endif
+#endif
+
+/* -------------------------------------------------
+ * gap_frame_fetch_register_user
+ * -------------------------------------------------
+ * register for using the frame fetcher resource.
+ * returns a unique resource user id.
+ */
+gint32
+gap_frame_fetch_register_user(const char *caller_name);
+
+/* -------------------------------------------------
+ * gap_frame_fetch_unregister_user
+ * -------------------------------------------------
+ * unregister the specified resource user id.
+ + (if there are still registered resource users
+ *  cached images and videohandles are kept.
+ *  until the last resource user calls this procedure.
+ *  if there are no more registered users all
+ *  cached resources and duplicates are dropped)
+ */
+void
+gap_frame_fetch_unregister_user(gint32 user_id);
+
+/* -------------------------------------------------
+ * gap_frame_fetch_drop_resources
+ * -------------------------------------------------
+ * drop all cached resources
+ * (regardless if there are still resource users registrated)
+ */
+void
+gap_frame_fetch_drop_resources();
+
+/* -------------------------------------------------
+ * gap_frame_fetch_delete_list_of_duplicated_images
+ * -------------------------------------------------
+ * deletes all duplicate imageas that wre created by the specified ffetch_user_id
+ * (if ffetch_user_id -1 is specified delte all duplicated images)
+ */
+void
+gap_frame_fetch_delete_list_of_duplicated_images(gint32 ffetch_user_id);
+
+
+/* ----------------------------
+ * gap_frame_fetch_orig_image
+ * ----------------------------
+ * returns image_id of the original cached image.
+ *    RESTRICTION: the Caller must NOT not modify that image and shall not open a display for it!
+ */
+gint32
+gap_frame_fetch_orig_image(gint32 ffetch_user_id
+    ,const char *filename            /* full filename of the image */
+    ,gboolean addToCache             /* enable caching */
+    );
+
+
+/* ----------------------------
+ * gap_frame_fetch_dup_image
+ * ----------------------------
+ * returns merged or selected layer_id 
+ *        (that is the only visible layer in temporary created scratch image)
+ *        the caller is resonsible to delete the scratch image when processing is done.
+ *         this can be done by calling gap_frame_fetch_delete_list_of_duplicated_images()
+ */
+gint32
+gap_frame_fetch_dup_image(gint32 ffetch_user_id
+    ,const char *filename            /* full filename of the image (already contains framenr) */
+    ,gint32      stackpos            /* 0 pick layer on top of stack, -1 merge visible layers */
+    ,gboolean addToCache             /* enable caching */
+    );
+
+
+/* ----------------------------
+ * gap_frame_fetch_dup_video
+ * ----------------------------
+ * returns the fetched video frame as gimp layer_id.
+ *         the returned layer id is (the only layer) in a temporary image.
+ *         note the caller is responsible to delete that temporary image after processing is done.
+ *         this can be done by calling gap_frame_fetch_delete_list_of_duplicated_images()
+ */
+gint32
+gap_frame_fetch_dup_video(gint32 ffetch_user_id
+    ,const char *filename            /* full filename of a video */
+    ,gint32      framenr             /* frame within the video (starting at 1) */
+    ,gint32      seltrack            /* videotrack */
+    ,const char *preferred_decoder
+    );
+
+
+
+#endif

Modified: trunk/gap/gap_player_dialog.c
==============================================================================
--- trunk/gap/gap_player_dialog.c	(original)
+++ trunk/gap/gap_player_dialog.c	Thu Jul 31 18:05:59 2008
@@ -111,6 +111,7 @@
 #include "gap_audio_extract.h"
 #include "gap_audio_extract.h"
 #include "gap_file_util.h"
+#include "gap_drawable_vref_parasite.h"
 
 #include "gap-intl.h"
 
@@ -1211,6 +1212,40 @@
 
 
 /* ------------------------------
+ * p_conditional_attach_dvref
+ * ------------------------------
+ * check if there is a valid current videoreference.
+ * if yes attach the reference as (temporary) videoreferenc parasite
+ * to the specified drawable_id 
+ * (that is typically the layer in the mtrace image)
+ */
+static void
+p_conditional_attach_dvref(GapPlayerMainGlobalParams *gpp
+               ,gint32 drawable_id)
+{
+  if(gap_debug)
+  {
+    printf("MTRACE: mtrace_mode:%d (GAP_PLAYER_MTRACE_IMG_SIZE == %d) dvref\n"
+        ,(int)gpp->mtrace_mode
+        ,(int)GAP_PLAYER_MTRACE_IMG_SIZE
+        );
+    gap_dvref_debug_print_GapDrawableVideoRef(gpp->dvref_ptr);
+  }
+
+  if((gpp->mtrace_mode == GAP_PLAYER_MTRACE_IMG_SIZE)
+  && (gpp->dvref_ptr != NULL))
+  {
+    if(gpp->dvref_ptr->videofile != NULL)
+    {
+      if (gpp->dvref_ptr->videofile == gpp->gva_videofile)
+      {
+        gap_dvref_assign_videoref_parasites(gpp->dvref_ptr, drawable_id);
+      }
+    }
+  } 
+}  /* end p_conditional_attach_dvref */
+
+/* ------------------------------
  * p_mtrace_image
  * ------------------------------
  * add image as one composite layer
@@ -1230,6 +1265,15 @@
   gint32 dst_layer_id;
   gint   l_src_offset_x, l_src_offset_y;    /* layeroffsets as they were in src_image */
 
+  if(gap_debug)
+  {
+      printf("MTRACE:p_mtrace_image START mtrace_mode:%d (GAP_PLAYER_MTRACE_IMG_SIZE == %d)\n"
+          ,(int)gpp->mtrace_mode
+          ,(int)GAP_PLAYER_MTRACE_IMG_SIZE
+          );
+      gap_dvref_debug_print_GapDrawableVideoRef(gpp->dvref_ptr);
+  }
+
   if(gpp->mtrace_mode != GAP_PLAYER_MTRACE_OFF)
   {
     width = MAX(gimp_image_width(image_id), 1);
@@ -1262,6 +1306,8 @@
       l_src_offset_y = (gint32)(((gdouble)mtrace_height / (gdouble)height) * (gdouble)l_src_offset_y);
     }
     gimp_layer_set_offsets(dst_layer_id, l_src_offset_x, l_src_offset_y);
+    p_conditional_attach_dvref(gpp, dst_layer_id);
+
 
     {
       gchar *l_name;
@@ -1330,6 +1376,8 @@
                           , 0  /* offset_y */
                           );
 
+    p_conditional_attach_dvref(gpp, dst_layer_id);
+
     {
       gchar *l_name;
 
@@ -2821,6 +2869,26 @@
       g_free(th_data);
       th_data = NULL;
     }
+    else if (gpp->dvref_ptr != NULL)
+    {
+      /* refresh current videofile reference 
+       * Note: the  dvref_ptr->videofile gets no COPY but only a rference to the current videofilename
+       *       this pointer can be reset to NULL any time (at p_display_frame)
+       *       but MUST NOT free up the refered name.
+       */
+      gpp->dvref_ptr->videofile = gpp->gva_videofile;
+      gpp->dvref_ptr->para.framenr = framenumber;
+      gpp->dvref_ptr->para.seltrack = seltrack;
+      g_snprintf(&gpp->dvref_ptr->para.preferred_decoder[0], sizeof(gpp->dvref_ptr->para.preferred_decoder), "%s"
+                , preferred_decoder
+                );
+      if(gap_debug)
+      {
+        printf("p_fetch_videoframe: dvref\n");
+        gap_dvref_debug_print_GapDrawableVideoRef(gpp->dvref_ptr);
+      }
+
+    }
   }
   else
   {
@@ -3650,6 +3718,11 @@
   framenr_is_the_active_image = FALSE;
   l_composite_image_id = -1;
 
+  if(gpp->dvref_ptr != NULL)
+  {
+    gpp->dvref_ptr->videofile = NULL;
+  }
+
   if(gpp->stb_ptr)
   {
     l_flip_request = GAP_STB_FLIP_NONE;
@@ -4632,7 +4705,10 @@
                                         GdkEventButton  *bevent,
                                         GapPlayerMainGlobalParams *gpp)
 {
-  /*if(gap_debug) printf("on_vid_preview_button_press_event: START\n"); */
+  if(gap_debug)
+  {
+    printf("on_vid_preview_button_press_event: START\n");
+  }
 
   if(gpp->mtrace_mode == GAP_PLAYER_MTRACE_OFF)
   {
@@ -8334,6 +8410,9 @@
   gpp->old_resize_width = 0;
   gpp->old_resize_height = 0;
 
+  gpp->dvref_ptr = g_new(GapDrawableVideoRef, 1);
+  gpp->dvref_ptr->videofile = NULL;
+
   p_create_player_window(gpp);
   p_set_frame_with_name_label(gpp);
 

Modified: trunk/gap/gap_player_main.c
==============================================================================
--- trunk/gap/gap_player_main.c	(original)
+++ trunk/gap/gap_player_main.c	Thu Jul 31 18:05:59 2008
@@ -222,6 +222,7 @@
 , NULL                /* GtkWidget *progress_bar_audio */
 , NULL                /* GtkWidget *audio_enable_checkbutton */
 
+, NULL                /* GapDrawableVideoRef  *dvref_ptr */
 };
 
 

Modified: trunk/gap/gap_player_main.h
==============================================================================
--- trunk/gap/gap_player_main.h	(original)
+++ trunk/gap/gap_player_main.h	Thu Jul 31 18:05:59 2008
@@ -40,6 +40,7 @@
 #include "gap_story_file.h"
 #include "gap_player_cache.h"
 #include "gap_story_render_types.h"
+#include "gap_drawable_vref_parasite.h"
 
 #ifdef GAP_ENABLE_VIDEOAPI_SUPPORT
 #include "gap_vid_api.h"
@@ -255,6 +256,7 @@
   GtkWidget *progress_bar_audio;
   GtkWidget *audio_enable_checkbutton;
 
+  GapDrawableVideoRef  *dvref_ptr;
   
 } GapPlayerMainGlobalParams;
 

Modified: trunk/gap/gap_story_dialog.c
==============================================================================
--- trunk/gap/gap_story_dialog.c	(original)
+++ trunk/gap/gap_story_dialog.c	Thu Jul 31 18:05:59 2008
@@ -65,6 +65,7 @@
 #include "gap_story_vthumb.h"
 #include "gap_story_section_properties.h"
 #include "gap_file_util.h"
+#include "gap_frame_fetcher.h"
 
 #include "images/gap-stock-pixbufs.h"
 
@@ -8450,8 +8451,19 @@
   /* init player window */
   p_player_img_mode_cb(NULL, sgpp);
 
-  gtk_main ();
-  gdk_flush ();
+  {
+    gint32 ffetch_user_id;
+    
+    /* register for frame fetcher resources (image cache) 
+     */
+    ffetch_user_id = gap_frame_fetch_register_user("gap_storyboard_dialog");
+
+    gtk_main ();
+    gdk_flush ();
+
+    /* unregister (shall drop cached resources of the frame fetcher) */
+    gap_frame_fetch_unregister_user(ffetch_user_id);
+  }
 
 }  /* end gap_storyboard_dialog */
 

Modified: trunk/gap/gap_story_file.c
==============================================================================
--- trunk/gap/gap_story_file.c	(original)
+++ trunk/gap/gap_story_file.c	Thu Jul 31 18:05:59 2008
@@ -4913,7 +4913,7 @@
      fprintf(fp, "%s           %s:\"%s\" %s:%d %s:%d\n"
          , GAP_STBKEY_EDIT_SETTINGS
          , l_parnam_tab.parname[1]
-         , (int)section_name
+         , section_name
          , l_parnam_tab.parname[2]
          , (int)l_track
          , l_parnam_tab.parname[3]

Modified: trunk/gap/gap_story_render_processor.c
==============================================================================
--- trunk/gap/gap_story_render_processor.c	(original)
+++ trunk/gap/gap_story_render_processor.c	Thu Jul 31 18:05:59 2008
@@ -111,8 +111,6 @@
 static void     p_init_stb_error(GapStoryRenderErrors *sterr);
 static void     p_free_stb_error(GapStoryRenderErrors *sterr);
 static void     p_set_stb_error(GapStoryRenderErrors *sterr, char *errtext);
-static void     p_drop_image_cache_elem1(GapStoryRenderImageCache *imcache);
-static gint32   p_load_cache_image( char* filename);
 static void     p_find_min_max_vid_tracknumbers(GapStoryRenderFrameRangeElem *frn_list
                              , gint32 *lowest_tracknr
                              , gint32 *highest_tracknr
@@ -835,124 +833,6 @@
 }  /* end gap_story_render_set_stb_warning */
 
 
-/* ----------------------------------------------------
- * p_drop_image_cache_elem1
- * ----------------------------------------------------
- */
-static void
-p_drop_image_cache_elem1(GapStoryRenderImageCache *imcache)
-{
-  GapStoryRenderImageCacheElem  *ic_ptr;
-
-  if(imcache)
-  {
-    ic_ptr = imcache->ic_list;
-    if(ic_ptr)
-    {
-      if(gap_debug) printf("p_drop_image_cache_elem1 delete:%s (image_id:%d)\n", ic_ptr->filename, (int)ic_ptr->image_id);
-      gap_image_delete_immediate(ic_ptr->image_id);
-      g_free(ic_ptr->filename);
-      imcache->ic_list = (GapStoryRenderImageCacheElem  *)ic_ptr->next;
-      g_free(ic_ptr);
-    }
-  }
-}  /* end p_drop_image_cache_elem1 */
-
-
-/* ----------------------------------------------------
- * gap_story_render_drop_image_cache
- * ----------------------------------------------------
- */
-void
-gap_story_render_drop_image_cache(void)
-{
-  GapStoryRenderImageCache *imcache;
-
-  if(gap_debug)  printf("gap_story_render_drop_image_cache START\n");
-  imcache = global_imcache;
-  if(imcache)
-  {
-    while(imcache->ic_list)
-    {
-      p_drop_image_cache_elem1(imcache);
-    }
-  }
-  if(gap_debug) printf("gap_story_render_drop_image_cache END\n");
-
-}  /* end gap_story_render_drop_image_cache */
-
-
-/* ----------------------------------------------------
- * p_load_cache_image
- * ----------------------------------------------------
- */
-static gint32
-p_load_cache_image( char* filename)
-{
-  gint32 l_idx;
-  gint32 l_image_id;
-  GapStoryRenderImageCacheElem  *ic_ptr;
-  GapStoryRenderImageCacheElem  *ic_last;
-  GapStoryRenderImageCacheElem  *ic_new;
-  GapStoryRenderImageCache  *imcache;
-
-  if(filename == NULL)
-  {
-    printf("p_load_cache_image: ** ERROR cant load filename == NULL!\n");
-    return -1;
-  }
-
-  if(global_imcache == NULL)
-  {
-    /* init the global_image cache */
-    global_imcache = g_malloc0(sizeof(GapStoryRenderImageCache));
-    global_imcache->ic_list = NULL;
-    global_imcache->max_img_cache = MAX_IMG_CACHE_ELEMENTS;
-  }
-
-  imcache = global_imcache;
-  ic_last = imcache->ic_list;
-
-  l_idx = 0;
-  for(ic_ptr = imcache->ic_list; ic_ptr != NULL; ic_ptr = (GapStoryRenderImageCacheElem *)ic_ptr->next)
-  {
-    l_idx++;
-    if(strcmp(filename, ic_ptr->filename) == 0)
-    {
-      /* image found in cache, can skip load */
-      return(ic_ptr->image_id);
-    }
-    ic_last = ic_ptr;
-  }
-
-  l_image_id = gap_lib_load_image(filename);
-  if(l_image_id >= 0)
-  {
-    ic_new = g_malloc0(sizeof(GapStoryRenderImageCacheElem));
-    ic_new->filename = g_strdup(filename);
-    ic_new->image_id = l_image_id;
-
-    if(imcache->ic_list == NULL)
-    {
-      imcache->ic_list = ic_new;   /* 1.st elem starts the list */
-    }
-    else
-    {
-      ic_last->next = (GapStoryRenderImageCacheElem *)ic_new;  /* add new elem at end of the cache list */
-    }
-
-    if(l_idx > imcache->max_img_cache)
-    {
-      /* chache list has more elements than desired,
-       * drop the 1.st (oldest) entry in the chache list
-       */
-      p_drop_image_cache_elem1(imcache);
-    }
-  }
-  return(l_image_id);
-}  /* end p_load_cache_image */
-
-
 
 
 /* ----------------------------------------------------
@@ -3167,6 +3047,8 @@
    p_free_stb_error(vidhand->sterr);
    p_free_mask_definitions(vidhand);
 
+   /* unregister frame fetcher resource usage (e.g. the image cache) */
+   gap_frame_fetch_unregister_user(vidhand->ffetch_user_id);
    vidhand->section_list = NULL;
    vidhand->frn_list = NULL;
    vidhand->sterr = NULL;
@@ -3506,6 +3388,9 @@
     vidhand->status_msg_len = 0;
   }
 
+  /* registrate as user of the frame fetcher resources (e.g. the image cache) */
+  vidhand->ffetch_user_id = gap_frame_fetch_register_user("gap_story_render_processor.p_open_video_handle_private");
+
   vidhand->frn_list = NULL;
   vidhand->preferred_decoder = NULL;
   vidhand->do_gimp_progress = do_gimp_progress;
@@ -4762,7 +4647,13 @@
            {
              gint32 l_orig_image_id;
 
-             l_orig_image_id = p_load_cache_image(l_framename);
+             l_orig_image_id = gap_frame_fetch_orig_image(vidhand->ffetch_user_id
+                                   , l_framename            /* full filename of the image */
+                                   , TRUE /*  enable caching */
+                                  );
+
+             
+             
              gimp_selection_none(l_orig_image_id);
              if(l_frn_type == GAP_FRN_IMAGE)
              {

Modified: trunk/gap/gap_story_render_types.h
==============================================================================
--- trunk/gap/gap_story_render_types.h	(original)
+++ trunk/gap/gap_story_render_types.h	Thu Jul 31 18:05:59 2008
@@ -319,6 +319,8 @@
   struct GapStoryRenderMaskDefElem *maskdef_elem;  /* list of mask definitions */
   gboolean   is_mask_handle;
 
+  gint32     ffetch_user_id;
+
 } GapStoryRenderVidHandle;  /* used for storyboard processing */
 
 

Modified: trunk/gap/gap_story_vthumb.c
==============================================================================
--- trunk/gap/gap_story_vthumb.c	(original)
+++ trunk/gap/gap_story_vthumb.c	Thu Jul 31 18:05:59 2008
@@ -332,7 +332,7 @@
     if((l_have_valid_vindex == FALSE)
     && (vindex_permission))
     {
-      //if(gap_debug)
+      if(gap_debug)
       {
         printf("STORY: DEBUG: create vidindex start\n");
       }
@@ -344,7 +344,7 @@
       sgpp->gvahand->create_vindex = TRUE;
       GVA_count_frames(sgpp->gvahand);
 
-      //if(gap_debug)
+      if(gap_debug)
       {
          printf("STORY: DEBUG: create vidindex done\n");
       }

Modified: trunk/libgapvidutil/gap_gve_story.h
==============================================================================
--- trunk/libgapvidutil/gap_gve_story.h	(original)
+++ trunk/libgapvidutil/gap_gve_story.h	Thu Jul 31 18:05:59 2008
@@ -45,6 +45,7 @@
 #include "gap_vid_api.h"
 #include "gap_story_file.h"
 #include "gap_story_render_processor.h"
+#include "gap_frame_fetcher.h"
 
 
 /* --------------------------*/
@@ -73,7 +74,7 @@
 
 #define gap_gve_story_debug_print_framerange_list           gap_story_render_debug_print_framerange_list
 #define gap_gve_story_debug_print_audiorange_list           gap_story_render_debug_print_audiorange_list
-#define gap_gve_story_drop_image_cache                      gap_story_render_drop_image_cache
+#define gap_gve_story_drop_image_cache                      gap_frame_fetch_drop_resources
 #define gap_gve_story_remove_tmp_audiofiles                 gap_story_render_remove_tmp_audiofiles
 #define gap_gve_story_drop_audio_cache                      gap_story_render_drop_audio_cache
 #define gap_gve_story_fetch_composite_image                 gap_story_render_fetch_composite_image



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