[gimp-gap] merged sox call and raw image frame load support (based on ufraw) from 2-8branch back to master.



commit 1b2bcc87eea422dca86fb8a2b3e61e6c00dfbb6d
Author: Wolfgang Hofer <wolfgangh svn gnome org>
Date:   Sun Jan 12 09:51:43 2014 +0100

    merged sox call and raw image frame load support (based on ufraw) from 2-8branch back to master.

 ChangeLog                                |   85 ++++++
 docs/reference/txt/gap_gimprc_params.txt |   58 ++++-
 gap/gap_lib.c                            |  465 +++++++++++++++++++++++++-----
 gap/gap_pdb_calls.c                      |   54 ++++
 gap/gap_pdb_calls.h                      |    2 +
 gap/gap_story_sox.c                      |  266 ++++++++++++++++-
 6 files changed, 841 insertions(+), 89 deletions(-)
---
diff --git a/ChangeLog b/ChangeLog
index 83e3e70..cd09d37 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,88 @@
+2014-01-12 Wolfgang Hofer <hof gimp org>
+- support for RAW files as "readonly" frame sequences for processing 
+  as high bit depth video source in MovePath operations or Storyboard Clips.
+  
+  Note that the final video encoding (ffmpeg engine) still operates at 8 bit depth
+  (This will not change in the near future) but rendering in GIMP
+  can be done at higher bit depth.
+  
+  This feature was implemented to support rendering videos
+  based on RAW frame sequences 
+  (.cr2 files of a timelapse sequence shot on Canon EOS DSLR camera)
+  
+  To use RAW files you need a file load plug in that is capable to load
+  RAW files without truncating them to 8 bit depth
+  in non-interactive way (e.g. without opening a dialog
+  for each processed frame)
+  
+  For my tests i used the 3rd party plug-in UFRaw (version 0.19.2)
+  but it did not work properly out of the box.
+  workaround: I patched the source ufraw-gimp.c by commenting out the line:
+  //    if (run_mode == GIMP_RUN_NONINTERACTIVE) uf->conf->shrink = 8; 
+  
+  // because this causes loading at reduced size 1/8
+  // (i have no idea what is it good for ??)
+  
+  new gimprc parameters
+  ---------------------
+  o) (gap-load-ufraw-%s "yes")
+     force explicite call of the UFRaw file load plugin
+     whenever GIMP-GAP loads a frame image non-interactive mode.
+     
+  
+  o) (video-save-mode-for-%s "readonly")
+     # NON-XCF frame processing/export overwrite behaviour:
+        possible configuration values are:
+         "overwrite"
+           automaticaly overwrite the discfile image that corresponds 
+           to the current frame (which is opened in the gimp session)
+           on frame exchange opertions (such as go to next/previous frame)
+           where the file save plug-in is called with the backround layer as active layer.
+           
+         "overwrite_flattened"
+           automaticaly overwrite the discfile image that corresponds 
+           to the current frame (which is opened in the gimp session)
+           on frame exchange opertions (such as go to next/previous frame)
+           where the current frame is flattened before calling the file save plug-in.
+       
+         "readonly"
+           frame exchange will fail in case the current frame image has
+           unsaved changes (e.g. the dirty flag is set).
+           GIMP-GAP does not overwrite frame files on disc in this mode.
+           It is intended for fileformats where export/overwrite
+           is not available or not wanted.
+       
+         "readonly_discard"
+           GIMP-GAP does not overwrite frame files on disc in this mode,
+           but discards unsaved changes without any notice on frame exchange.
+           USE THIS ONLY WHEN YOU KONW WHAT YOUR DIONG ON YOUR OWN RISK !
+
+
+
+  * gap/gap_lib.c
+  * gap/gap_pdb_calls.c [.h]
+  * docs/reference/txt/gap_gimprc_params.txt
+
+2014-01-12 Wolfgang Hofer <hof gimp org>
+
+- audio resample via external tool (sox) did not work in WINDOWS environment.
+  The old code was restricted to Unix only.
+  Fixed the parameter substitution and use g_spawn_sync for the external audio converter call
+  that now shall work on other operating systems than unix.
+  
+  tests on windows with sox as external converter were successful,
+  but only in case when the external converter was configured with
+  the full absolute path.
+  sox is available on Linux, Windows, and MacOSX
+  see http://sox.sourceforge.net/
+
+  (note that this fix was already done 2013-01-16 in my local 
+   environment and already mentioned in the commit message on that date
+   .. but the file gap/gap_story_sox.c was forgotten to be added in the commit
+   .. now i recognized that mistake and can provide the fix)
+
+  * gap/gap_story_sox.c
+
 2013-11-09 Wolfgang Hofer <hof gimp org>
   - fixed comment lines 
     (related to #711402)
diff --git a/docs/reference/txt/gap_gimprc_params.txt b/docs/reference/txt/gap_gimprc_params.txt
index b4e73ff..93279c4 100644
--- a/docs/reference/txt/gap_gimprc_params.txt
+++ b/docs/reference/txt/gap_gimprc_params.txt
@@ -127,7 +127,21 @@ If you edit gimprc files by hand, you must do this before you startup GIMP.
 (audioconvert_program "/usr/local/bin/my_private_audioconverter")
 
 
-# NON-XCF frame save behaviour:
+# Configure GIMP-GAP for explicite use of the UFRaw 3rd party plug-in
+# for non-interactive loading of frame files from raw filetypes.
+#  configuration (gap-load-ufraw-%s "yes")
+# Use this when the generical gimp-file-load fails to load Canon .cr2
+# or other raw files
+# (because it picks the file-tiff-load plug that is not capable to load those filetypes)
+(gap-load-ufraw-dng "yes")
+(gap-load-ufraw-nef "yes")
+(gap-load-ufraw-cr2 "yes")
+(gap-load-ufraw-cr "yes")
+
+
+
+
+# NON-XCF frame processing/export overwrite behaviour:
 # -----------------------------
 # if you want to use GIMP-GAP with other fileformats than XCF
 # for your frames, you will get a warning dialog at the first
@@ -137,15 +151,53 @@ If you edit gimprc files by hand, you must do this before you startup GIMP.
 # extension. 
 # for all configured fileformat extensions GIMP-GAP uses the configuration from the
 # gimprc file and does not show the warning dialog.
+# configuration key is video-save-mode-for-%s where %s stands for an extension
+# such as jpg, tif, cr2,....
+# possible configuration values are:
+#  "overwrite"
+#    automaticaly overwrite the discfile image that corresponds 
+#    to the current frame (which is opened in the gimp session)
+#    on frame exchange opertions (such as go to next/previous frame)
+#    where the file save plug-in is called with the backround layer as active layer.
+#    
+#  "overwrite_flattened"
+#    automaticaly overwrite the discfile image that corresponds 
+#    to the current frame (which is opened in the gimp session)
+#    on frame exchange opertions (such as go to next/previous frame)
+#    where the current frame is flattened before calling the file save plug-in.
+#
+#  "readonly"
+#    frame exchange will fail in case the current frame image has
+#    unsaved changes (e.g. the dirty flag is set).
+#    GIMP-GAP does not overwrite frame files on disc in this mode.
+#    It is intended for fileformats where export/overwrite
+#    is not available or not wanted.
+#
+#  "readonly_discard"
+#    GIMP-GAP does not overwrite frame files on disc in this mode,
+#    but discards unsaved changes without any notice on frame exchange.
+#    USE THIS ONLY WHEN YOU KONW WHAT YOUR DIONG ON YOUR OWN RISK !
+#
 # Here are recommanded gimprc configuration settings
 # for some common used fileformats:
-
+(video-save-mode-for-jpg "overwrite_flattened")
+(video-save-mode-for-jpeg "overwrite_flattened")
+(video-save-mode-for-bmp "overwrite_flattened")
+(video-save-mode-for-ppg "overwrite_flattened")
+(video-save-mode-for-gif "overwrite")
+(video-save-mode-for-dng "readonly")
+(video-save-mode-for-cr2 "readonly")
+(video-save-mode-for-nef "readonly")
+
+
+# old way (supported for backwards compatibility) 
+#   where "yes" is the same as video-save-mode-for-%s "overwrite_flattened"
+#   and   "no"  is the same as video-save-mode-for-%s "overwrite"
 (video-save-flattened-jpg    "yes")
 (video-save-flattened-jpeg   "yes")
 (video-save-flattened-bmp    "yes")
 (video-save-flattened-png    "yes")
 (video-save-flattened-ppm    "yes")
-(video-save-flattened-xjt    "no")
 (video-save-flattened-gif    "no")
 
 
diff --git a/gap/gap_lib.c b/gap/gap_lib.c
index 85eaccf..0b28116 100644
--- a/gap/gap_lib.c
+++ b/gap/gap_lib.c
@@ -134,6 +134,24 @@
 #include "gap_thumbnail.h"
 #include "gap_vin.h"
 
+
+#define SAVE_MODE_FOR_NON_XCF_AS_IS      0  /* loss of information that is not supported by selected 
fileformat */
+#define SAVE_MODE_FOR_NON_XCF_FLATTEN    1  /* loss of layers and other information on frame excange */
+#define SAVE_MODE_FOR_NON_XCF_READONLY  -2  /* never save, blocks frame excange when dirty flag set */
+#define SAVE_MODE_FOR_NON_XCF_NEVERSAVE -3  /* never save, reset dirty flag and enable frame change (with 
loss of unsaved changes) */
+#define SAVE_MODE_FOR_NON_XCF_ASK       -1  /* (default) ask the user (with p_decide_save_as dialog) */
+
+#define GIMPRC_MODEVAL_NON_XCF_AS_IS      "overwrite"
+#define GIMPRC_MODEVAL_NON_XCF_FLATTEN    "overwrite_flattened"
+#define GIMPRC_MODEVAL_NON_XCF_READONLY   "readonly"
+#define GIMPRC_MODEVAL_NON_XCF_NEVERSAVE  "readonly_discard"
+#define GIMPRC_MODEVAL_NON_XCF_ASK        "ask"
+
+#define SAVE_NON_XCF_FAILED_OR_CANCELLED           -1
+#define SAVE_NON_XCF_FAKE_OK_FOR_READ_ONLY_TYPES   -2
+
+
+
 typedef struct
 {
   gdouble  quality;
@@ -1792,7 +1810,7 @@ gap_lib_dir_ainfo(GapAnimInfo *ainfo_ptr)
                     l_minnr = l_nr;
                  }
 
-                 if ((l_nr < ainfo_ptr->curr_frame_nr) 
+                 if ((l_nr < ainfo_ptr->curr_frame_nr)
                  && (l_nr > ainfo_ptr->frame_nr_before_curr_frame_nr))
                  {
                    ainfo_ptr->frame_nr_before_curr_frame_nr = l_nr;
@@ -2028,19 +2046,196 @@ p_gzip (char *orig_name, char *new_name, char *zip)
 
 }       /* end p_gzip */
 
+/* --------------------------------
+ * gap_lib_ascii_string_to_lower
+ * --------------------------------
+ * returns a copy of the original string
+ * where all ascii letter characters are transformed to lower case
+ */
+gchar *
+gap_lib_ascii_string_to_lower(gchar *original)
+{
+  gchar *result;
+
+  result = NULL;
+  if (original != NULL)
+  {
+    gchar *ptr;
+    result = g_strdup(original);
+    for(ptr = result; *ptr != '\0'; ptr++)
+    {
+       if (g_ascii_isalpha(*ptr))
+       {
+            *ptr = g_ascii_tolower(*ptr);
+       }
+       }
+  }
+  return (result);
+
+}  /* end gap_lib_ascii_string_to_lower */
+
+
+/* ============================================================================
+ * gap_lib_save_non_xcf_dialog
+ *   dialog window with buttons to let the user decide how to
+ *   handle frame exchange for non xcf filetypes.
+ *
+ *   This dialog provides an option to persist the decision in gimprc
+ *   file.
+ *   return: the value assigned with the pressed button.
+ * ============================================================================
+ */
+gint
+gap_lib_save_non_xcf_dialog(char *key_gimprc, char *lower_extension)
+{
+  #define NUMBER_OF_ARGS 2
+  #define NUMBER_OF_BUTTONS 5
+  static GapArrArg  argv[NUMBER_OF_ARGS];
+  static GapArrButtonArg  b_argv[NUMBER_OF_BUTTONS];
+  gint   b_default_val;
+  gint   itb;
+  gint l_rc;
+
+  b_default_val = SAVE_MODE_FOR_NON_XCF_ASK;
+  b_argv[0].but_txt  = GTK_STOCK_CANCEL;
+  b_argv[0].but_val  = SAVE_MODE_FOR_NON_XCF_ASK;
+  b_argv[1].but_txt  = _("overwrite flattened");
+  b_argv[1].but_val  = SAVE_MODE_FOR_NON_XCF_FLATTEN;
+  b_argv[2].but_txt  = _("overwrite");
+  b_argv[2].but_val  = SAVE_MODE_FOR_NON_XCF_AS_IS;
+  b_argv[3].but_txt  = _("read only");
+  b_argv[3].but_val  = SAVE_MODE_FOR_NON_XCF_READONLY;
+  b_argv[4].but_txt  = _("discard changes");
+  b_argv[4].but_val  = SAVE_MODE_FOR_NON_XCF_NEVERSAVE;
+
+
+  gap_arr_arg_init(&argv[0], GAP_ARR_WGT_LABEL);
+  argv[0].label_txt = g_strdup_printf(_("You are using another file format than xcf.\n"
+                        "This dialog configures how to handle exchanges of\n"
+                        "the current frame image (for frames with extension %s)\n"
+                        "Note that automatical save on frame change just works with XCF\n"
+                        "but automatical overwrite (e.g export) to other formats\n"
+                        "typically results in loss of layers and other information.")
+                        , lower_extension);
+
+  itb = 1;
+  gap_arr_arg_init(&argv[itb], GAP_ARR_WGT_TOGGLE);
+  argv[itb].label_txt = _("Save my decision:");
+  argv[itb].help_txt  = g_strdup_printf(_("Save decision for this fileformat for further gimp sessions.\n"
+                          "this creates an entry in your gimprc file with the "
+                          "key:%s)")
+                          , key_gimprc );
+  argv[itb].int_ret   = 0;
+  argv[itb].has_default = TRUE;
+  argv[itb].int_default = 0;
+
+
+  l_rc = gap_arr_std_dialog(_("Fileformat Warning"),
+                     _("Select"),
+                     NUMBER_OF_ARGS, argv,
+                     NUMBER_OF_BUTTONS, b_argv,
+                     b_default_val);
+                     
+  if (l_rc != SAVE_MODE_FOR_NON_XCF_ASK)
+  {
+    /* optional persist the decision as gimmprc value */
+    if (argv[itb].int_ret == 1)
+    {
+      if(l_rc == SAVE_MODE_FOR_NON_XCF_AS_IS) 
+      {
+        gimp_gimprc_set(key_gimprc, GIMPRC_MODEVAL_NON_XCF_AS_IS);
+      }      
+      else if(l_rc == SAVE_MODE_FOR_NON_XCF_FLATTEN) 
+      {
+        gimp_gimprc_set(key_gimprc, GIMPRC_MODEVAL_NON_XCF_FLATTEN);
+      }      
+      else if(l_rc == SAVE_MODE_FOR_NON_XCF_READONLY) 
+      {
+        gimp_gimprc_set(key_gimprc, GIMPRC_MODEVAL_NON_XCF_READONLY);
+      }      
+      else if(l_rc == SAVE_MODE_FOR_NON_XCF_NEVERSAVE) 
+      {
+        gimp_gimprc_set(key_gimprc, GIMPRC_MODEVAL_NON_XCF_NEVERSAVE);
+      }      
+      else
+      {
+        gimp_gimprc_set(key_gimprc, GIMPRC_MODEVAL_NON_XCF_ASK);
+      }      
+    }
+  }
+  
+  return (l_rc);
+                       
+}       /* end gap_lib_save_non_xcf_dialog */
+
+
+
 /* ============================================================================
  * p_decide_save_as
  *   decide what to do on attempt to save a frame in any image format
- *  (other than xcf)
- *   Let the user decide if the frame is to save "as it is" or "flattened"
- *   ("as it is" will save only the backround layer in most fileformat types)
+ *   (other than xcf)
+ *   The current implemetation provides "overwrite" and "read only" options.
+ *   Note that overwrite is already handled within this procedure
+ *   and exports the image.
+ *   
+ *   If this is the 1st call (and there is no gimprc configuration from previous
+ *   sessions) a dialog window is presented where the user
+ *   shall decide how to handle frame excanges.
+ *   The options:
+ *     SAVE_MODE_FOR_NON_XCF_ASK
+ *        in case the user cancelled the dialog (e.g did not decide)
+ *        frame excange shall be blocked
+ *
+ *     SAVE_MODE_FOR_NON_XCF_AS_IS
+ *        the current frame will be exported as is (e.g without flattening)
+ *        the returncode depends on the result of the export.
+ *        ("as it is" will save only the background layer in 
+ *         fileformat types that do not support multiple layers)
+ *
+ *     SAVE_MODE_FOR_NON_XCF_FLATTEN
+ *        the current frame will be exported with flattening.
+ *        the returncode depends on the result of the export.
+ *
+ *     SAVE_MODE_FOR_NON_XCF_READONLY
+ *        return SAVE_NON_XCF_FAILED_OR_CANCELLED 
+ *               in case the image has dirty flag set
+ *        but return SAVE_NON_XCF_FAKE_OK_FOR_READ_ONLY_TYPES
+ *               in case the image needs no save (dirty flag not set)
+ *
+ *     GIMPRC_MODEVAL_NON_XCF_NEVERSAVE
+ *        always return SAVE_NON_XCF_FAKE_OK_FOR_READ_ONLY_TYPES
+ *               the current frame file on disc is never overwritten
+ *               and the caller may continue to load the image from
+ *               the next frame file (which discards all unsaved changes
+ *               automatically)
+ *
  *   The SAVE_AS_MODE is stored , and reused
  *   (without displaying the dialog again)
  *   on subsequent calls of the same frame-basename and extension
  *   in the same GIMP-session.
  *
- *   return -1  ... CANCEL (do not save)
- *           0  ... save the image (may be flattened)
+ *   return positive integer on successful save.    
+ *          -1 SAVE_NON_XCF_FAILED_OR_CANCELLED 
+ *                  in case save procedure failed to export/overwrite
+ *                  or user has cancelled the dialog.
+ *                  The caller shall block excange of the current image with another frame
+ *                  due to the failure.
+ *          -2  SAVE_NON_XCF_FAKE_OK_FOR_READ_ONLY_TYPES
+ *                  in case a Read only image type was not exported/overwritten
+ *                  but the caller can continue to exchange the current image with another frame
+ *                  (either because configuration allows automatically discard of changes
+ *                   or the dirty flag is not set in current image)
+ *          -3  ... Never save                SAVE_MODE_FOR_NON_XCF_NEVERSAVE
+ *                    the caller shall clear the dirty flag and enable
+ *                    frame excange without a save attempt.
+ *                    
+ *           0  ... save the image as is      SAVE_MODE_FOR_NON_XCF_AS_IS
+ *                     the caller shall save the current frame image
+ *                     on success clean the dirty flag and enable frame excange.
+ *
+ *           1  ... save the image flattened  SAVE_MODE_FOR_NON_XCF_FLATTEN
+ *                     the caller shall sflatten and ave the current frame image
+ *                     on success clean the dirty flag and enable frame excange.
  * ============================================================================
  */
 int
@@ -2048,27 +2243,31 @@ p_decide_save_as(gint32 image_id, const char *sav_name, const char *final_sav_na
 {
   gchar *l_key_save_as_mode;
   gchar *l_extension;
+  gchar *l_lowerExtension;
   gchar *l_ext;
   gchar *l_basename;
   long  l_number;
   int   l_sav_rc;
 
-  static GapArrButtonArg  l_argv[3];
-  int               l_argc;
   int               l_save_as_mode;
   GimpRunMode      l_run_mode;
 
 
    /* check if there are SAVE_AS_MODE settings (from privious calls within one gimp session) */
-  l_save_as_mode = -1;
-  l_sav_rc = -1;
+  l_save_as_mode = SAVE_MODE_FOR_NON_XCF_ASK; /* -1 for ask and cancel */
 
   l_extension = gap_lib_alloc_extension(final_sav_name);
+  l_lowerExtension = gap_lib_ascii_string_to_lower(l_extension);
+  l_ext = l_lowerExtension;
+  if(*l_ext == '.')
+  {
+    l_ext++;
+  }
   l_basename = gap_lib_alloc_basename(final_sav_name, &l_number);
 
   l_key_save_as_mode = g_strdup_printf("GIMP_GAP_SAVE_MODE_%s%s"
                        ,l_basename
-                       ,l_extension
+                       ,l_lowerExtension
                        );
 
   gimp_get_data (l_key_save_as_mode, &l_save_as_mode);
@@ -2080,9 +2279,10 @@ p_decide_save_as(gint32 image_id, const char *sav_name, const char *final_sav_na
           );
   }
 
-  if(l_save_as_mode == -1)
+  if(l_save_as_mode == SAVE_MODE_FOR_NON_XCF_ASK)
   {
     gchar *l_key_gimprc;
+    gchar *l_key_gimprc_old;
     gchar *l_val_gimprc;
     gboolean l_ask;
 
@@ -2091,36 +2291,59 @@ p_decide_save_as(gint32 image_id, const char *sav_name, const char *final_sav_na
      * open a dialog (if no configuration value was found,
      * or configuration says "ask" (== other value than "yes" or "no" )
      */
-    l_ext = l_extension;
-    if(*l_ext == '.')
+    l_ask = TRUE;
+
+    l_key_gimprc = g_strdup_printf("video-save-mode-for-%s", l_ext);
+    l_key_gimprc_old = g_strdup_printf("video-save-flattened-%s", l_ext);
+    l_val_gimprc = gimp_gimprc_query(l_key_gimprc);
+    
+    if (l_val_gimprc == NULL)
     {
-      l_ext++;
+      /* fallback to OLD gimprc key when new key is not configured */
+      l_val_gimprc = gimp_gimprc_query(l_key_gimprc_old);
     }
-    l_key_gimprc = g_strdup_printf("video-save-flattened-%s", l_ext);
 
     if(gap_debug)
     {
+      printf("GIMPRC OLD KEY:%s:\n", l_key_gimprc_old);
       printf("GIMPRC KEY:%s:\n", l_key_gimprc);
     }
 
-    l_val_gimprc = gimp_gimprc_query(l_key_gimprc);
-    l_ask = TRUE;
 
-    if(l_val_gimprc)
+    if(l_val_gimprc != NULL)
     {
       if(gap_debug)
       {
         printf("GIMPRC VAL:%s:\n", l_val_gimprc);
       }
-
-      if(strcmp(l_val_gimprc, "yes") == 0)
+      if(strcmp(l_val_gimprc, GIMPRC_MODEVAL_NON_XCF_AS_IS) == 0)
       {
-        l_save_as_mode = 1;
+        l_save_as_mode = SAVE_MODE_FOR_NON_XCF_AS_IS;
         l_ask = FALSE;
       }
-      if(strcmp(l_val_gimprc, "no") == 0)
+      else if(strcmp(l_val_gimprc, GIMPRC_MODEVAL_NON_XCF_FLATTEN) == 0)
       {
-        l_save_as_mode = 0;
+        l_save_as_mode = SAVE_MODE_FOR_NON_XCF_FLATTEN;
+        l_ask = FALSE;
+      }
+      else if(strcmp(l_val_gimprc, GIMPRC_MODEVAL_NON_XCF_READONLY) == 0)
+      {
+        l_save_as_mode = SAVE_MODE_FOR_NON_XCF_READONLY;
+        l_ask = FALSE;
+      }
+      else if(strcmp(l_val_gimprc, GIMPRC_MODEVAL_NON_XCF_NEVERSAVE) == 0)
+      {
+        l_save_as_mode = SAVE_MODE_FOR_NON_XCF_NEVERSAVE;
+        l_ask = FALSE;
+      }
+      else if(strcmp(l_val_gimprc, "yes") == 0)
+      {
+        l_save_as_mode = SAVE_MODE_FOR_NON_XCF_FLATTEN;
+        l_ask = FALSE;
+      }
+      else if(strcmp(l_val_gimprc, "no") == 0)
+      {
+        l_save_as_mode = SAVE_MODE_FOR_NON_XCF_AS_IS;
         l_ask = FALSE;
       }
 
@@ -2136,32 +2359,11 @@ p_decide_save_as(gint32 image_id, const char *sav_name, const char *final_sav_na
 
     if(l_ask)
     {
-      gchar *l_msg;
-
-      l_argv[0].but_txt  = GTK_STOCK_CANCEL;
-      l_argv[0].but_val  = -1;
-      l_argv[1].but_txt  = _("Save Flattened");
-      l_argv[1].but_val  = 1;
-      l_argv[2].but_txt  = _("Save As Is");
-      l_argv[2].but_val  = 0;
-      l_argc             = 3;
-
-      l_msg = g_strdup_printf(_("You are using another file format than xcf.\n"
-                                "Save operations may result in loss of layer information.\n\n"
-                                "To configure flattening for this fileformat\n"
-                                "(permanent for all further sessions) please add the line:\n"
-                                "(%s %s)\n"
-                                "to your gimprc file.")
-                             , l_key_gimprc
-                             , "\"yes\""
-                             );
-      l_save_as_mode =  gap_arr_buttons_dialog (_("Fileformat Warning")
-                                                ,l_msg
-                                                , l_argc, l_argv, -1);
-      g_free(l_msg);
+      l_save_as_mode =  gap_lib_save_non_xcf_dialog(l_key_gimprc, l_lowerExtension);
     }
 
     g_free(l_key_gimprc);
+    g_free(l_key_gimprc_old);
 
     if(gap_debug)
     {
@@ -2179,18 +2381,38 @@ p_decide_save_as(gint32 image_id, const char *sav_name, const char *final_sav_na
 
   g_free(l_key_save_as_mode);
 
-  if(l_save_as_mode < 0)
+
+  l_sav_rc = SAVE_NON_XCF_FAILED_OR_CANCELLED;
+  if(l_save_as_mode == SAVE_MODE_FOR_NON_XCF_NEVERSAVE)
   {
-    l_sav_rc = -1;
+    /* this read only mode always fakes "save ok" 
+     * that discards all changes when the frame is excanged.
+     */
+    l_sav_rc = SAVE_NON_XCF_FAKE_OK_FOR_READ_ONLY_TYPES;
   }
-  else
+  else if(l_save_as_mode <= SAVE_MODE_FOR_NON_XCF_READONLY)
   {
-    if(l_save_as_mode == 1)
+    /* this read only mode fakes "save ok" 
+     * when no save is needed.
+     */
+    if (gimp_image_is_dirty(image_id))
+    {
+      g_message(_("Frame operation blocked\n"
+                  "due to unsaved changes in readonly frame image\n%s")
+               , sav_name);
+    }
+    else
+    {
+      l_sav_rc = SAVE_NON_XCF_FAKE_OK_FOR_READ_ONLY_TYPES;
+    }
+  }
+  else if (l_save_as_mode >= 0)
+  {
+    if(l_save_as_mode == SAVE_MODE_FOR_NON_XCF_FLATTEN)
     {
         gimp_image_flatten (image_id);
     }
 
-
     l_sav_rc = p_lib_save_named_image_1(image_id
                              , sav_name
                              , l_run_mode
@@ -2198,10 +2420,16 @@ p_decide_save_as(gint32 image_id, const char *sav_name, const char *final_sav_na
                              , l_basename
                              , l_extension
                              );
+    if (l_sav_rc < 0)
+    {
+      l_sav_rc = SAVE_NON_XCF_FAILED_OR_CANCELLED;
+    }
   }
 
+cleanup:
   g_free(l_basename);
   g_free(l_extension);
+  g_free(l_lowerExtension);
 
   return l_sav_rc;
 }       /* end p_decide_save_as */
@@ -2589,6 +2817,7 @@ gap_lib_save_named_frame(gint32 image_id, char *sav_name)
 {
   GimpParam *l_params;
   gchar     *l_ext;
+  gchar     *l_lowerExt;
   char      *l_tmpname;
   gint       l_retvals;
   int        l_gzip;
@@ -2598,9 +2827,9 @@ gap_lib_save_named_frame(gint32 image_id, char *sav_name)
   l_tmpname = NULL;
   l_rc   = -1;
   l_gzip = 0;          /* dont zip */
-  l_xcf  = 0;          /* assume no xcf format */
+  l_xcf  = 0;          /* assume no xcf format (1==xcf, 0==any other) */
 
-  /* check extension to decide if savd file will be zipped */
+  /* check extension to decide if saved file will be zipped */
   l_ext = gap_lib_alloc_extension(sav_name);
   if(l_ext == NULL)
   {
@@ -2609,19 +2838,20 @@ gap_lib_save_named_frame(gint32 image_id, char *sav_name)
       );
     return -1;
   }
+  l_lowerExt = gap_lib_ascii_string_to_lower(l_ext);
 
   gimp_image_set_filename(image_id, sav_name);
 
-  if(0 == strcmp(l_ext, ".xcf"))
+  if(0 == strcmp(l_lowerExt, ".xcf"))
   {
     l_xcf  = 1;
   }
-  else if(0 == strcmp(l_ext, ".xcfgz"))
+  else if(0 == strcmp(l_lowerExt, ".xcfgz"))
   {
     l_gzip = 1;          /* zip it */
     l_xcf  = 1;
   }
-  else if(0 == strcmp(l_ext, ".gz"))
+  else if(0 == strcmp(l_lowerExt, ".gz"))
   {
     l_gzip = 1;          /* zip it */
   }
@@ -2638,6 +2868,7 @@ gap_lib_save_named_frame(gint32 image_id, char *sav_name)
   }
 
   g_free(l_ext);
+  g_free(l_lowerExt);
 
 
   if(gap_debug)
@@ -2717,6 +2948,10 @@ gap_lib_save_named_frame(gint32 image_id, char *sav_name)
   {
      g_remove(l_tmpname);
      g_free(l_tmpname);  /* free temporary name */
+     if((l_xcf == 0) && (l_rc == SAVE_NON_XCF_FAKE_OK_FOR_READ_ONLY_TYPES))
+     {
+       return (0); /* FAKE save OK  */
+     }
      return l_rc;
   }
 
@@ -2822,27 +3057,84 @@ p_save_old_frame(GapAnimInfo *ainfo_ptr, GapVinVideoInfo *vin_ptr)
 
 
 
-/* ============================================================================
+/* ------------------------------
+ * p_check_ufraw_load_configured
+ * ------------------------------
+ * check the gimprc configuration for the specified filetype extension
+ * typical configuration values are:
+ *  (gap-load-ufraw-dng yes)
+ *  (gap-load-ufraw-nef yes)
+ *  (gap-load-ufraw-cr2 yes)
+ *  (gap-load-ufraw-cr yes)
+ */
+gboolean
+p_check_ufraw_load_configured(char  *lowerExtension)
+{
+  gchar *l_key_gimprc;
+  gboolean l_configured_for_ufraw;
+  char  *l_ext;
+
+  l_ext = lowerExtension;
+  if(*l_ext == '.')
+  {
+    l_ext++;
+  }
+
+
+  l_key_gimprc = g_strdup_printf("gap-load-ufraw-%s", l_ext);
+  l_configured_for_ufraw = gap_base_get_gimprc_gboolean_value(l_key_gimprc, FALSE);
+  
+  g_free(l_key_gimprc);
+  
+  return (l_configured_for_ufraw);
+  
+
+}  /* end p_check_ufraw_load_configured */
+
+
+
+
+/* ---------------------
  * gap_lib_load_image
- * load image of any type by filename, and return its image id
- * (or -1 in case of errors)
- * ============================================================================
+ * ---------------------
+ * load image of any type by filename, and 
+ * return its image id
+ *        (or -1 in case of errors)
+ * 
+ * This procedure calls the gimp_file_load procedure
+ * that normally detects and handles supported image filetypes generically,
+ * except for the case where the filetype (detected by the filename extension)
+ * is explicite configured to use the 3rd party UFraw gimp-plugin.
+ * (via gimprc key  gap-load-ufraw-<extension> )
+ *
+ * Note that my tests with Canon .cr2 raw files showed, that gimp_file_load picks the 
+ * file-load-tiff procedure (that fails to load Canon raw files)
+ * rather than file-ufraw-load (that is capable to load those raw files correct)
+ * when both pdb procedures are installed.
+ * This is the reason why i added the gimprc gap-load-ufraw-<extension> support
+ * that forces explicite non-interactive calls to the correct loader
+ * to enable read access to RAW frames with GIMP/GAP.
  */
 gint32
 gap_lib_load_image (char *lod_name)
 {
   char  *l_ext;
+  char  *l_lowerExt;
   char  *l_tmpname;
   gint32 l_tmp_image_id;
   int    l_rc;
+  gboolean l_configured_for_ufraw;
 
   l_rc = 0;
   l_tmpname = NULL;
+  l_configured_for_ufraw = FALSE;
   l_ext = gap_lib_alloc_extension(lod_name);
   if(l_ext != NULL)
   {
-    if((0 == strcmp(l_ext, ".xcfgz"))
-    || (0 == strcmp(l_ext, ".gz")))
+    l_lowerExt = gap_lib_ascii_string_to_lower(l_ext);
+    l_configured_for_ufraw = p_check_ufraw_load_configured(l_lowerExt);
+    if((0 == strcmp(l_lowerExt, ".xcfgz"))
+    || (0 == strcmp(l_lowerExt, ".gz")))
     {
 
       /* find a temp name and */
@@ -2851,6 +3143,7 @@ gap_lib_load_image (char *lod_name)
     }
     else l_tmpname = lod_name;
     g_free(l_ext);
+    g_free(l_lowerExt);
   }
 
   if(l_tmpname == NULL)
@@ -2859,15 +3152,46 @@ gap_lib_load_image (char *lod_name)
   }
 
 
-  if(gap_debug) printf("DEBUG: before   gap_lib_load_image: '%s'\n", l_tmpname);
+  l_tmp_image_id = -1;
 
-  l_tmp_image_id = gimp_file_load(GIMP_RUN_NONINTERACTIVE,
-                l_tmpname,
-                l_tmpname  /* raw name ? */
-                );
+  if(l_configured_for_ufraw)
+  {
+    if(gap_debug) 
+    {
+      printf("DEBUG: before   ufraw load: '%s'\n", l_tmpname);
+    }
+    
+    l_tmp_image_id = gap_pdb_call_ufraw_load_image(GIMP_RUN_NONINTERACTIVE,
+                    l_tmpname,
+                    l_tmpname  /* raw name ? */
+                    );
+    
+    if(gap_debug)
+    {
+      printf("DEBUG: after    ufraw load: '%s' id=%d\n\n",
+                                   l_tmpname, (int)l_tmp_image_id);
+    }
+  }
+  
+  if (l_tmp_image_id < 0)
+  {
+    if(gap_debug) 
+    {
+      printf("DEBUG: before   gimp_file_load: '%s'\n", l_tmpname);
+    }
+    
+    l_tmp_image_id = gimp_file_load(GIMP_RUN_NONINTERACTIVE,
+                    l_tmpname,
+                    l_tmpname  /* raw name ? */
+                    );
+    
+    if(gap_debug)
+    {
+      printf("DEBUG: after    gimp_file_load: '%s' id=%d\n\n",
+                                   l_tmpname, (int)l_tmp_image_id);
+    }
+  }
 
-  if(gap_debug) printf("DEBUG: after    gap_lib_load_image: '%s' id=%d\n\n",
-                               l_tmpname, (int)l_tmp_image_id);
 
   if(l_tmpname != lod_name)
   {
@@ -2881,7 +3205,6 @@ gap_lib_load_image (char *lod_name)
 }       /* end gap_lib_load_image */
 
 
-
 /* ============================================================================
  * gap_lib_load_named_frame
  *   load new frame,
diff --git a/gap/gap_pdb_calls.c b/gap/gap_pdb_calls.c
index 1b77178..4015ac6 100644
--- a/gap/gap_pdb_calls.c
+++ b/gap/gap_pdb_calls.c
@@ -503,3 +503,57 @@ gap_pdb_call_normalize(gint32 image_id, gint32 layer_id)
    return(FALSE);
 } /* end gap_pdb_call_normalize */
 
+
+
+/* ------------------------------
+ * gap_pdb_call_ufraw_load_image
+ * ------------------------------
+ * explicite call of the 3rd party ufraw loading procedure.
+ * returns value >= 0 for vaild image_id on success,
+ *         -1 in case image could not be loaded
+ *         -2 in case of other errors
+ *            (typicall for the scenario when UFRAW is not installed)
+ */
+gint32
+gap_pdb_call_ufraw_load_image(GimpRunMode run_mode, char* filename, char* raw_filename)
+{
+   static char     *l_called_proc = "file_ufraw_load";
+   GimpParam       *return_vals;
+   int              nreturn_vals;
+   gint32           image_id;
+
+   return_vals = gimp_run_procedure (l_called_proc,
+                                 &nreturn_vals,
+                                 GIMP_PDB_INT32,     run_mode,
+                                 GIMP_PDB_STRING,    filename,
+                                 GIMP_PDB_STRING,    raw_filename,
+                                 GIMP_PDB_END);
+
+   if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+   {
+      if (nreturn_vals >= 1) 
+      {
+        image_id  = return_vals[1].data.d_image;
+      }
+      else
+      {
+        printf("GAP: Error: PDB call of %s failed, d_status:%d %s expected returnvalue is missing\n"
+          , l_called_proc
+          , (int)return_vals[0].data.d_status
+          , gap_status_to_string(return_vals[0].data.d_status)
+          );
+        image_id = -1;
+      }
+      gimp_destroy_params(return_vals, nreturn_vals);
+      return (image_id);   /* image_id >= 0 .. OK */
+   }
+   gimp_destroy_params(return_vals, nreturn_vals);
+   printf("GAP: Error: PDB call of %s failed, d_status:%d %s\n"
+        , l_called_proc
+        , (int)return_vals[0].data.d_status
+        , gap_status_to_string(return_vals[0].data.d_status)
+        );
+   return(-2);
+}       /* end gap_pdb_call_ufraw_load_image */
+
+
diff --git a/gap/gap_pdb_calls.h b/gap/gap_pdb_calls.h
index 81d2089..981c174 100644
--- a/gap/gap_pdb_calls.h
+++ b/gap/gap_pdb_calls.h
@@ -70,4 +70,6 @@ gboolean    gap_pdb_call_solid_noise(gint32 image_id, gint32 layer_id
 
 gboolean    gap_pdb_call_normalize(gint32 image_id, gint32 layer_id);
 
+gint32      gap_pdb_call_ufraw_load_image(GimpRunMode run_mode, char* filename, char* raw_filename);
+
 #endif
diff --git a/gap/gap_story_sox.c b/gap/gap_story_sox.c
index 95fd365..1a5caa5 100644
--- a/gap/gap_story_sox.c
+++ b/gap/gap_story_sox.c
@@ -1,5 +1,8 @@
 /* gap_story_sox.c
- *    Audio resampling Modules based on calls to UNIX Utility Program sox
+ *    Audio resampling Modules based on calls to external audio converter program.
+ *    Default for the external converter is the Utility Program sox
+ *    (that is available on Linux, Windows, and MacOSX
+ *     see http://sox.sourceforge.net/ )
  */
 /*
  * Copyright
@@ -22,6 +25,7 @@
 #include <config.h>
 
 #include <stdlib.h>
+#include <string.h>
 
 #include <glib/gstdio.h>
 
@@ -32,6 +36,76 @@
 
 extern int gap_debug;  /* 1 == print debug infos , 0 dont print debug infos */
 
+
+/* --------------------------------------------
+ * p_allocate_and_substitute_calling_parameters
+ * --------------------------------------------
+ */
+char *
+p_allocate_and_substitute_calling_parameters(char *in_audiofile
+               ,char *out_audiofile
+               ,gint32 samplerate
+               ,char *util_sox_options)
+{
+  char *optPtr;
+  char *paramString;
+  char *paramStringNew;
+
+  paramString = g_strdup("");
+  if (util_sox_options != NULL)
+  {
+    optPtr = util_sox_options;
+
+    while(*optPtr != '\0')
+    {
+      if (strncmp("$IN", optPtr, strlen("$IN")) == 0)
+      {
+        paramStringNew = g_strdup_printf("%s%s"
+                      , paramString
+                      , in_audiofile               /* input audio file */
+                      );
+        g_free(paramString);
+        paramString = paramStringNew;
+        optPtr += strlen("$IN");
+      }
+      else if (strncmp("$OUT", optPtr, strlen("$OUT")) == 0)
+      {
+        paramStringNew = g_strdup_printf("%s%s"
+                      , paramString
+                      , out_audiofile              /* output audio file (tmp 16-bit wav file) */
+                      );
+        g_free(paramString);
+        paramString = paramStringNew;
+        optPtr += strlen("$OUT");
+      }
+      else if (strncmp("$RATE", optPtr, strlen("$RATE")) == 0)
+      {
+        paramStringNew = g_strdup_printf("%s%d"
+                      , paramString
+                      , (int)samplerate
+                      );
+        g_free(paramString);
+        paramString = paramStringNew;
+        optPtr += strlen("$RATE");
+      }
+      else
+      {
+        paramStringNew = g_strdup_printf("%s%c"
+                      , paramString
+                      , *optPtr
+                      );
+        g_free(paramString);
+        paramString = paramStringNew;
+        optPtr++;
+      }
+    }
+  }
+
+  return (paramString);
+
+}  /* end p_allocate_and_substitute_calling_parameters */
+
+
 /* --------------------------------
  * gap_story_sox_exec_resample
  * --------------------------------
@@ -44,36 +118,198 @@ gap_story_sox_exec_resample(char *in_audiofile
                ,char *util_sox_options
                )
 {
-  gchar *l_cmd;
+  gchar *l_cmd_argv[50];
+  gchar *paramString;
+  gchar *l_util_sox;
+  gchar *l_util_sox_options;
+  int    l_ret;
+  int    l_arg_idx;
+  char  *l_ptr;
+  char  *l_split_args_string;
+
+
 
   if(util_sox == NULL)
   {
-    util_sox = GAP_STORY_SOX_DEFAULT_UTIL_SOX;
+     if(gap_debug)
+     {
+       printf("util_sox is NULL\n");
+     }
+     l_util_sox = gimp_gimprc_query("video_encode_com_util_sox");
+     if(l_util_sox == NULL)
+     {
+       l_util_sox = g_strdup(GAP_STORY_SOX_DEFAULT_UTIL_SOX);
+     }
+  }
+  else
+  {
+    l_util_sox = g_strdup(util_sox);
   }
+
   if(util_sox_options == NULL)
   {
-    util_sox_options = GAP_STORY_SOX_DEFAULT_UTIL_SOX_OPTIONS;
+    l_util_sox_options = gimp_gimprc_query("video_encode_com_util_sox_options");
+    if(l_util_sox_options == NULL)
+    {
+      l_util_sox_options = g_strdup(GAP_STORY_SOX_DEFAULT_UTIL_SOX_OPTIONS);
+    }
   }
+  else
+  {
+    l_util_sox_options = g_strdup(util_sox_options);
+  }
+
 
-  /* the calling style requres UNIX Shell for Environment Variables
+  /* build the parameter string for calling the external audio converter tool
    * IN, OUT, RATE  that are used for Parameter substitution
    */
+  paramString = p_allocate_and_substitute_calling_parameters(in_audiofile
+                   ,out_audiofile
+                   ,samplerate
+                   ,l_util_sox_options
+                   );
+
+
+  l_arg_idx = 0;
+  l_cmd_argv[l_arg_idx++] = l_util_sox;  /* 1st param is name of called program */
+  
+  
+  /* split the substituted parameter string in substrings with whitespae as separator
+   * and put '\0' terminator characters wherever a substring ends.
+   * The l_cmd_argv[] array is filled with pointers
+   * to the substring start positions.
+   */
+  l_split_args_string = g_strdup(paramString);
+  l_ptr = l_split_args_string;
+  if (l_ptr != NULL)
+  {
+#define PARSE_STATUS_IS_SPACE     0
+#define PARSE_STATUS_IS_SINGLE_QT 1
+#define PARSE_STATUS_IS_DOUBLE_QT 2
+#define PARSE_STATUS_IS_TEXT      3
+
+    gint parseStatus = PARSE_STATUS_IS_SPACE;
+    
+    while(*l_ptr != '\0')
+    {
+      switch(parseStatus)
+      {
+        case PARSE_STATUS_IS_SINGLE_QT:
+          switch(*l_ptr)
+          {
+            case '\'':
+              parseStatus = PARSE_STATUS_IS_SPACE;
+              *l_ptr = '\0';  /* set termintate marker for substring */
+              break;
+            default:
+              break;
+          }
+          break;
+        case PARSE_STATUS_IS_DOUBLE_QT:
+          switch(*l_ptr)
+          {
+            case '"':
+              parseStatus = PARSE_STATUS_IS_SPACE;
+              *l_ptr = '\0';  /* set termintate marker for substring */
+              break;
+            default:
+              break;
+          }
+          break;
+        case PARSE_STATUS_IS_TEXT:
+          switch(*l_ptr)
+          {
+            case ' ':
+            case '\t':
+            case '\n':
+              parseStatus = PARSE_STATUS_IS_SPACE;
+              *l_ptr = '\0';  /* set termintate marker for substring */
+              break;    /* skip sequence of white space charactes */
+            default:
+              break;
+          }
+          break;
+        case PARSE_STATUS_IS_SPACE:
+          switch(*l_ptr)
+          {
+            case '\'':
+              parseStatus = PARSE_STATUS_IS_SINGLE_QT;
+              l_cmd_argv[l_arg_idx++] = &l_ptr[1];
+              break;
+            case '"':
+              parseStatus = PARSE_STATUS_IS_DOUBLE_QT;
+              l_cmd_argv[l_arg_idx++] = &l_ptr[1];
+              break;
+            case ' ':
+            case '\t':
+            case '\n':
+              break;    /* skip sequence of white space charactes */
+            default:
+              parseStatus = PARSE_STATUS_IS_TEXT;
+              l_cmd_argv[l_arg_idx++] = l_ptr;
+              break;
+          }
+          break;
+      }
+      
+      
+      l_ptr++;  /* advance to next character */
+    }
+  }
+ 
+  l_cmd_argv[l_arg_idx++] = NULL; /* (char *)0 indicates end of calling args */
+  
+
 
-  l_cmd = g_strdup_printf("IN='%s';OUT='%s';RATE=%d;%s %s\n"
-           , in_audiofile               /* input audio file */
-           , out_audiofile              /* output audio file (tmp 16-bit wav file) */
-           , (int)samplerate
-           , util_sox
-           , util_sox_options
-           );
 
   if(gap_debug)
   {
-    printf("Execute resample CMD:%s\n", l_cmd);
+    printf("Execute-resample CMD:%s  args:%s\n", l_util_sox, paramString);
   }
 
-  system(l_cmd);
-  g_free(l_cmd);
+  /* spawn external converter program (l_util_sox) */
+  {
+    gboolean spawnRc;
+    GSpawnFlags spawnFlags = G_SPAWN_STDOUT_TO_DEV_NULL | G_SPAWN_STDERR_TO_DEV_NULL;
+    GError     *spawnError = NULL;
+    gint exit_status;
+
+    if(!g_file_test (l_util_sox, G_FILE_TEST_IS_EXECUTABLE) )
+    {
+      /* in case the external program was not configured with absolute path
+       * enable the search Path flag
+       */
+      spawnFlags |= G_SPAWN_SEARCH_PATH;
+    }
+    spawnRc = g_spawn_sync(NULL,        /*  const gchar *working_directory  NULL: inherit from parent */
+                          l_cmd_argv,   /*  argument vector for the external program */
+                          NULL,         /*  gchar **envp NULL inherit from parent */
+                          spawnFlags,
+                          NULL,         /*  GSpawnChildSetupFunc child_setup function to run in the child 
just before exec() */
+                          NULL,         /*  gpointer user_data */
+                          NULL,         /*  gchar **standard_output */
+                          NULL,         /*  gchar **standard_error */
+                          &exit_status,
+                          NULL          /* GError **spawnError */
+                          );
+
+    if(!spawnRc)
+    {
+      printf("Failed to spawn external audio converter program: %s %s"
+        , l_util_sox
+        , paramString
+        );
+    }
+                          
+  }
+
+  g_free(l_util_sox);
+  g_free(l_util_sox_options);
+
+  g_free(l_split_args_string);
+  g_free(paramString);
+
+
 }  /* end gap_story_sox_exec_resample */
 
 



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