[gimp] plug-ins: give animation playback ability to take into account image updates.



commit 2bfae573adab409d59f3b4d354f31c185f94ba21
Author: Jehan <jehan girinstud io>
Date:   Sun Dec 9 16:07:39 2012 +0900

    plug-ins: give animation playback ability to take into account image updates.
    
    This includes a much better code organization where initialization part can be reused
    to free up memory and refresh the display on the fly.
    This refresh feature has a UI button and a shortcut (ctrl-r) associated.
    It implies any kind of change, additional or removed layers, new image size, etc.

 plug-ins/common/animation-play.c | 279 +++++++++++++++++++--------------------
 1 file changed, 137 insertions(+), 142 deletions(-)
---
diff --git a/plug-ins/common/animation-play.c b/plug-ins/common/animation-play.c
index efdbf55..8e64e8f 100644
--- a/plug-ins/common/animation-play.c
+++ b/plug-ins/common/animation-play.c
@@ -63,12 +63,15 @@ static void        run   (const gchar      *name,
                           gint             *nreturn_vals,
                           GimpParam       **return_vals);
 
-static void        do_playback               (void);
+static void        initialize                (void);
+static void        build_dialog              (gchar           *imagename);
+static void        refresh_dialog            (gchar           *imagename);
 
 static void        window_destroy            (GtkWidget       *widget);
 static void        play_callback             (GtkToggleAction *action);
 static void        step_back_callback        (GtkAction       *action);
 static void        step_callback             (GtkAction       *action);
+static void        refresh_callback          (GtkAction       *action);
 static void        rewind_callback           (GtkAction       *action);
 static void        speed_up_callback         (GtkAction       *action);
 static void        speed_down_callback       (GtkAction       *action);
@@ -87,7 +90,6 @@ static gboolean    repaint_da                (GtkWidget       *darea,
                                               gpointer         data);
 
 static void        init_frames               (void);
-static void        generate_frames           (void);
 static void        render_frame              (gint32           whichframe);
 static void        show_frame                (void);
 static void        total_alpha_preview       (guchar          *ptr);
@@ -100,8 +102,6 @@ static gint        get_fps                   (gint             index);
 /* tag util functions*/
 static gint        parse_ms_tag              (const gchar     *str);
 static DisposeType parse_disposal_tag        (const gchar     *str);
-static DisposeType get_frame_disposal        (guint            whichframe);
-static guint32     get_frame_duration        (guint            whichframe);
 static gboolean    is_disposal_tag           (const gchar     *str,
                                               DisposeType     *disposal,
                                               gint            *taglength);
@@ -128,20 +128,21 @@ static GtkWidget         *shape_drawing_area      = NULL;
 static guchar            *shape_drawing_area_data = NULL;
 static guchar            *drawing_area_data       = NULL;
 static GtkWidget         *progress;
-static guint              width, height;
-static guchar            *preview_alpha1_data;
-static guchar            *preview_alpha2_data;
+static guint              width = -1, height = -1;
+static guchar            *preview_alpha1_data     = NULL;
+static guchar            *preview_alpha2_data     = NULL;
 static gint32             image_id;
 static guchar            *rawframe                = NULL;
 static gint32            *frames                  = NULL;
+static guint32           *frame_durations         = NULL;
 static gint32             total_frames;
-static guint              frame_number;
-static gint32            *layers;
+static guint              frame_number            = 0;
+static gint32            *layers                  = NULL;
 static gint32             total_layers;
 static gboolean           playing = FALSE;
 static guint              timer   = 0;
 static GimpImageBaseType  imagetype;
-static guchar            *palette;
+static guchar            *palette                 = NULL;
 static gint               ncolours;
 static gint               duration_index = 3;
 static DisposeType        default_frame_disposal = DISPOSE_COMBINE;
@@ -218,7 +219,8 @@ run (const gchar      *name,
     {
       image_id = param[1].data.d_image;
 
-      do_playback ();
+      initialize ();
+      gtk_main ();
 
       if (run_mode != GIMP_RUN_NONINTERACTIVE)
         gimp_displays_flush ();
@@ -460,6 +462,10 @@ ui_manager_new (GtkWidget *window)
       NULL, NULL, N_("Rewind the animation"),
       G_CALLBACK (rewind_callback) },
 
+    { "refresh", GTK_STOCK_REFRESH,
+      NULL, "<control>R", N_("Reload the image"),
+      G_CALLBACK (refresh_callback) },
+
     { "help", GTK_STOCK_HELP,
       NULL, NULL, NULL,
       G_CALLBACK (help_callback) },
@@ -533,6 +539,7 @@ ui_manager_new (GtkWidget *window)
                                      "    <toolitem action=\"rewind\" />"
                                      "    <separator />"
                                      "    <toolitem action=\"detach\" />"
+                                     "    <toolitem action=\"refresh\" />"
                                      "    <separator name=\"space\" />"
                                      "    <toolitem action=\"help\" />"
                                      "  </toolbar>"
@@ -560,6 +567,7 @@ ui_manager_new (GtkWidget *window)
                                      "    <menuitem action=\"speed-reset\" />"
                                      "    <separator />"
                                      "    <menuitem action=\"detach\" />"
+                                     "    <menuitem action=\"refresh\" />"
                                      "    <menuitem action=\"close\" />"
                                      "  </popup>"
                                      "</ui>",
@@ -575,8 +583,23 @@ ui_manager_new (GtkWidget *window)
 }
 
 static void
-build_dialog (GimpImageBaseType  basetype,
-              gchar             *imagename)
+refresh_dialog (gchar *imagename)
+{
+  gchar     *name;
+
+  /* Image Name */
+  name = g_strconcat (_("Animation Playback:"), " ", imagename, NULL);
+  gtk_window_set_title (GTK_WINDOW (window), name);
+  g_free (name);
+
+  /* Update GUI size. */
+  gtk_widget_set_size_request (drawing_area, width, height);
+  gtk_widget_set_size_request (shape_drawing_area, width, height);
+  gtk_window_reshow_with_initial_size (GTK_WINDOW (window));
+}
+
+static void
+build_dialog (gchar             *imagename)
 {
   GtkWidget   *toolbar;
   GtkWidget   *frame;
@@ -696,21 +719,6 @@ build_dialog (GimpImageBaseType  basetype,
 
   gimp_help_set_help_data (speedcombo, _("Playback speed"), NULL);
 
-  if (total_frames < 2)
-    {
-      action = gtk_ui_manager_get_action (ui_manager,
-                                          "/ui/anim-play-toolbar/play");
-      gtk_action_set_sensitive (action, FALSE);
-
-      action = gtk_ui_manager_get_action (ui_manager,
-                                          "/ui/anim-play-toolbar/step");
-      gtk_action_set_sensitive (action, FALSE);
-
-      action = gtk_ui_manager_get_action (ui_manager,
-                                          "/ui/anim-play-toolbar/rewind");
-      gtk_action_set_sensitive (action, FALSE);
-    }
-
   action = gtk_ui_manager_get_action (ui_manager,
                                       "/anim-play-popup/speed-reset");
   gtk_action_set_sensitive (action, FALSE);
@@ -780,8 +788,23 @@ build_dialog (GimpImageBaseType  basetype,
 }
 
 static void
-do_playback (void)
+initialize (void)
 {
+  guint previous_width = width;
+  guint previous_height = height;
+
+  /* Freeing existing data after a refresh. */
+  g_free (layers);
+  g_free (palette);
+
+  /* Catch the case when the user has closed the image in the meantime. */
+  if (! gimp_image_is_valid (image_id))
+    {
+      gimp_message (_("Invalid image. Did you close it?"));
+      gtk_main_quit ();
+      return;
+    }
+
   width     = gimp_image_width (image_id);
   height    = gimp_image_height (image_id);
   layers    = gimp_image_get_layers (image_id, &total_layers);
@@ -803,19 +826,21 @@ do_playback (void)
       ncolours = 256;
     }
 
-  frame_number = 0;
-
-  init_preview ();
+  if (previous_width != width || previous_height != height)
+  {
+    g_free (rawframe);
+    rawframe = g_malloc (width * height * 4);
+    init_preview ();
+  }
 
-  build_dialog (gimp_image_base_type (image_id),
-                gimp_image_get_name (image_id));
+  if (window)
+    refresh_dialog (gimp_image_get_name (image_id));
+  else
+    build_dialog (gimp_image_get_name (image_id));
 
   init_frames ();
-
-  render_frame (0);
+  render_frame (frame_number);
   show_frame ();
-
-  gtk_main ();
 }
 
 
@@ -832,28 +857,11 @@ render_frame (gint32 whichframe)
   guchar        *srcptr;
   guchar        *destptr;
 
-  if (whichframe >= total_frames)
-    {
-      printf( "playback: Asked for frame number %d in a %d-frame animation!\n",
-             (int) (whichframe+1), (int) total_frames);
-      gimp_quit ();
-    }
+  g_assert (whichframe < total_frames);
 
   drawable_id = frames[whichframe];
   buffer = gimp_drawable_get_buffer (drawable_id);
 
-  /* Lame attempt to catch the case that a user has closed the image. */
-  if (!buffer)
-    {
-      gimp_message (_("Tried to display an invalid layer."));
-      gtk_main_quit ();
-      return;
-    }
-
-  /* Initialize the rawframe only once. */
-  if (rawframe == NULL)
-    rawframe = g_malloc (width * height * 4);
-
   /* Initialise and fetch the whole raw new frame */
   gegl_buffer_get (buffer, GEGL_RECTANGLE (0, 0, width, height), 1.0,
                    babl_format ("R'G'B'A u8"),
@@ -934,6 +942,12 @@ init_preview (void)
 {
   gint i;
 
+  /* Cleaning, after a refresh with different image size. */
+  g_free (preview_data);
+  g_free (shape_preview_mask);
+  g_free (preview_alpha1_data);
+  g_free (preview_alpha2_data);
+
   preview_data = g_malloc (width * height * 3);
   shape_preview_mask = g_malloc ((width * height) / 8 + 1 + height);
   preview_alpha1_data = g_malloc (width * 3);
@@ -961,7 +975,15 @@ init_preview (void)
         }
     }
 
-  drawing_area_data = preview_data;
+  if (detached)
+    {
+      g_free (drawing_area_data);
+      drawing_area_data = g_malloc (width * height * 3);
+      total_alpha_preview (drawing_area_data);
+      memset (shape_preview_mask, 0, (width * height) / 8 + height);
+    }
+  else
+    drawing_area_data = preview_data;
   shape_drawing_area_data = preview_data;
 }
 
@@ -1031,7 +1053,7 @@ advance_frame_callback (gpointer data)
 
   remove_timer();
 
-  duration = get_frame_duration ((frame_number + 1) % total_frames);
+  duration = frame_durations[(frame_number + 1) % total_frames];
 
   timer = g_timeout_add (duration * get_duration_factor (duration_index),
                          advance_frame_callback, NULL);
@@ -1053,7 +1075,7 @@ play_callback (GtkToggleAction *action)
 
   if (playing)
     {
-      timer = g_timeout_add (get_frame_duration (frame_number) *
+      timer = g_timeout_add ((gdouble) frame_durations[frame_number] *
                              get_duration_factor (duration_index),
                              advance_frame_callback, NULL);
 
@@ -1142,6 +1164,12 @@ step_callback (GtkAction *action)
 }
 
 static void
+refresh_callback (GtkAction *action)
+{
+  initialize ();
+}
+
+static void
 rewind_callback (GtkAction *action)
 {
   if (playing)
@@ -1250,38 +1278,6 @@ update_combobox (void)
 
 /* tag util. */
 
-static DisposeType
-get_frame_disposal (guint whichframe)
-{
-  DisposeType  disposal;
-  gchar       *layer_name;
-
-  layer_name = gimp_item_get_name (layers[total_frames-(whichframe+1)]);
-  disposal = parse_disposal_tag (layer_name);
-  g_free (layer_name);
-
-  return disposal;
-}
-
-static guint32
-get_frame_duration (guint whichframe)
-{
-  gchar *layer_name;
-  gint   duration = 0;
-
-  layer_name = gimp_item_get_name (layers[total_frames-(whichframe+1)]);
-  if (layer_name)
-    {
-      duration = parse_ms_tag (layer_name);
-      g_free (layer_name);
-    }
-
-  if (duration <= 0)
-    duration = default_frame_duration;
-
-  return (guint32) duration;
-}
-
 static gboolean
 is_ms_tag (const gchar *str,
            gint        *duration,
@@ -1405,86 +1401,85 @@ parse_disposal_tag (const gchar *str)
 static void
 init_frames (void)
 {
-  GtkAction   *action;
-  total_frames = total_layers;
-  generate_frames();
-  if (total_frames < 2)
-    {
-      action = gtk_ui_manager_get_action (ui_manager,
-                                          "/ui/anim-play-toolbar/play");
-      gtk_action_set_sensitive (action, FALSE);
-
-      action = gtk_ui_manager_get_action (ui_manager,
-                                          "/ui/anim-play-toolbar/step-back");
-      gtk_action_set_sensitive (action, FALSE);
-
-      action = gtk_ui_manager_get_action (ui_manager,
-                                          "/ui/anim-play-toolbar/step");
-      gtk_action_set_sensitive (action, FALSE);
-
-      action = gtk_ui_manager_get_action (ui_manager,
-                                          "/ui/anim-play-toolbar/rewind");
-      gtk_action_set_sensitive (action, FALSE);
-    }
-  else
-    {
-      action = gtk_ui_manager_get_action (ui_manager,
-                                          "/ui/anim-play-toolbar/play");
-      gtk_action_set_sensitive (action, TRUE);
-
-      action = gtk_ui_manager_get_action (ui_manager,
-                                          "/ui/anim-play-toolbar/step-back");
-      gtk_action_set_sensitive (action, TRUE);
-
-      action = gtk_ui_manager_get_action (ui_manager,
-                                          "/ui/anim-play-toolbar/step");
-      gtk_action_set_sensitive (action, TRUE);
-
-      action = gtk_ui_manager_get_action (ui_manager,
-                                          "/ui/anim-play-toolbar/rewind");
-      gtk_action_set_sensitive (action, TRUE);
-    }
-}
-
-static void
-generate_frames (void)
-{
   /* Frames are associated to an unused image. */
   static gint32 frames_image_id;
   gint          i;
   gint32        new_frame, previous_frame, new_layer;
+  gboolean      animated;
+  GtkAction    *action;
+  gint          duration = 0;
+  DisposeType   disposal = default_frame_disposal;
+  gchar        *layer_name;
+
+  total_frames = total_layers;
 
   /* Cleanup before re-generation. */
-  if (frames != NULL)
+  if (frames)
     {
-      for (i = 0; i < total_frames; i++)
-        gimp_image_remove_layer (frames_image_id, frames[i]);
       gimp_image_delete (frames_image_id);
       g_free (frames);
+      g_free (frame_durations);
     }
   frames = g_try_malloc0_n (total_frames, sizeof (gint32));
-
-  if (frames == NULL)
+  frame_durations = g_try_malloc0_n (total_frames, sizeof (guint32));
+  if (! frames || ! frame_durations)
     {
       gimp_message (_("Memory could not be allocated to the frame container."));
       gtk_main_quit ();
+      gimp_quit ();
       return;
     }
   frames_image_id = gimp_image_new (width, height, imagetype);
+  /* Save processing time and memory by not saving history and merged frames. */
+  gimp_image_undo_disable (frames_image_id);
 
   for (i = 0; i < total_frames; i++)
     {
-      if (i > 0 && get_frame_disposal (i) != DISPOSE_REPLACE)
+      layer_name = gimp_item_get_name (layers[total_layers - (i + 1)]);
+      if (layer_name)
+        {
+          duration = parse_ms_tag (layer_name);
+          disposal = parse_disposal_tag (layer_name);
+          g_free (layer_name);
+        }
+
+      if (i > 0 && disposal != DISPOSE_REPLACE)
         {
           previous_frame = gimp_layer_copy (frames[i - 1]);
           gimp_image_insert_layer (frames_image_id, previous_frame, 0, -1);
           gimp_item_set_visible (previous_frame, TRUE);
         }
       new_layer = gimp_layer_new_from_drawable (layers[total_layers - (i + 1)], frames_image_id);
-      gimp_item_set_visible (new_layer, TRUE);
       gimp_image_insert_layer (frames_image_id, new_layer, 0, -1);
+      gimp_item_set_visible (new_layer, TRUE);
       new_frame = gimp_image_merge_visible_layers (frames_image_id, GIMP_CLIP_TO_IMAGE);
       frames[i] = new_frame;
       gimp_item_set_visible (new_frame, FALSE);
+
+      if (duration <= 0)
+        duration = default_frame_duration;
+      frame_durations[i] = (guint32) duration;
     }
+
+  /* Update the UI. */
+  animated = total_frames >= 2;
+  action = gtk_ui_manager_get_action (ui_manager,
+                                      "/ui/anim-play-toolbar/play");
+  gtk_action_set_sensitive (action, animated);
+
+  action = gtk_ui_manager_get_action (ui_manager,
+                                      "/ui/anim-play-toolbar/step-back");
+  gtk_action_set_sensitive (action, animated);
+
+  action = gtk_ui_manager_get_action (ui_manager,
+                                      "/ui/anim-play-toolbar/step");
+  gtk_action_set_sensitive (action, animated);
+
+  action = gtk_ui_manager_get_action (ui_manager,
+                                      "/ui/anim-play-toolbar/rewind");
+  gtk_action_set_sensitive (action, animated);
+
+  /* Keep the same frame number, unless it is now invalid. */
+  if (frame_number >= total_frames)
+    frame_number = 0;
 }


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