[gimp/wip/animation: 116/197] plug-ins: reorganize caching to have a visible GUI immediately.



commit ebbc934f3fbd32f1c30be08659947866bb86ddef
Author: Jehan <jehan girinstud io>
Date:   Tue Dec 20 07:43:25 2016 +0100

    plug-ins: reorganize caching to have a visible GUI immediately.
    
    Caching can take time and would delay the exposition of the GUI,
    having one wonder whether the plugin really started or not. Now the
    animation structure (saved in parasite as XML) is immediately parsed,
    and the frame caching is only started once the GUI is exposed.
    It is still not perfect because caching makes the GUI hardly responsive,
    but this is a first step.
    Also took the opportunity to some cleaning, renaming (i.e
    s/load_xml/deserialize/) and code reorganization here and there.

 plug-ins/animation-play/core/animation-animatic.c  |  305 ++++++++--------
 .../animation-play/core/animation-celanimation.c   |  379 ++++++++++----------
 plug-ins/animation-play/core/animation-playback.c  |    2 -
 plug-ins/animation-play/core/animation.c           |   77 +++--
 plug-ins/animation-play/core/animation.h           |   47 ++--
 plug-ins/animation-play/widgets/animation-dialog.c |   26 ++
 6 files changed, 439 insertions(+), 397 deletions(-)
---
diff --git a/plug-ins/animation-play/core/animation-animatic.c 
b/plug-ins/animation-play/core/animation-animatic.c
index c5a7a27..790549b 100644
--- a/plug-ins/animation-play/core/animation-animatic.c
+++ b/plug-ins/animation-play/core/animation-animatic.c
@@ -79,35 +79,39 @@ struct _AnimationAnimaticPrivate
                                      ANIMATION_TYPE_ANIMATIC, \
                                      AnimationAnimaticPrivate)
 
-static void         animation_animatic_finalize     (GObject           *object);
+static void         animation_animatic_finalize       (GObject           *object);
 
 /* Virtual methods */
 
-static gint         animation_animatic_get_duration (Animation         *animation);
+static gint         animation_animatic_get_duration   (Animation         *animation);
 
-static void         animation_animatic_load         (Animation         *animation);
-static void         animation_animatic_load_xml     (Animation         *animation,
-                                                     const gchar       *xml);
-static GeglBuffer * animation_animatic_get_frame    (Animation         *animation,
-                                                     gint               pos);
-static gchar      * animation_animatic_serialize    (Animation         *animation);
+static GeglBuffer * animation_animatic_get_frame      (Animation         *animation,
+                                                       gint               pos);
 
-static gboolean     animation_animatic_same         (Animation         *animation,
-                                                     gint               pos1,
-                                                     gint               pos2);
+static gboolean     animation_animatic_same           (Animation         *animation,
+                                                       gint               pos1,
+                                                       gint               pos2);
+
+static void         animation_animatic_purge_cache    (Animation         *animation);
+
+static void         animation_animatic_reset_defaults (Animation         *animation);
+static gchar      * animation_animatic_serialize      (Animation         *animation);
+static gboolean     animation_animatic_deserialize    (Animation         *animation,
+                                                       const gchar       *xml,
+                                                       GError           **error);
 
 /* XML parsing */
 
-static void      animation_animatic_start_element (GMarkupParseContext *context,
-                                                   const gchar         *element_name,
-                                                   const gchar        **attribute_names,
-                                                   const gchar        **attribute_values,
-                                                   gpointer             user_data,
-                                                   GError             **error);
-static void      animation_animatic_end_element   (GMarkupParseContext *context,
-                                                   const gchar         *element_name,
-                                                   gpointer             user_data,
-                                                   GError             **error);
+static void      animation_animatic_start_element (GMarkupParseContext  *context,
+                                                   const gchar          *element_name,
+                                                   const gchar         **attribute_names,
+                                                   const gchar         **attribute_values,
+                                                   gpointer              user_data,
+                                                   GError              **error);
+static void      animation_animatic_end_element   (GMarkupParseContext  *context,
+                                                   const gchar          *element_name,
+                                                   gpointer              user_data,
+                                                   GError              **error);
 
 static void      animation_animatic_text          (GMarkupParseContext  *context,
                                                    const gchar          *text,
@@ -117,19 +121,19 @@ static void      animation_animatic_text          (GMarkupParseContext  *context
 
 /* Utils */
 
-static void         animation_animatic_cache      (AnimationAnimatic *animation,
-                                                   gint               panel,
-                                                   gboolean           recursion);
+static void      animation_animatic_cache_panel   (AnimationAnimatic    *animation,
+                                                   gint                  panel,
+                                                   gboolean              recursion);
 
 /* Tag handling (from layer names) */
 
-static gint         parse_ms_tag                  (Animation         *animation,
-                                                   const gchar       *str);
-static gboolean     parse_combine_tag             (const gchar       *str);
+static gint      parse_ms_tag                     (Animation            *animation,
+                                                   const gchar          *str);
+static gboolean  parse_combine_tag                (const gchar          *str);
 
-static gboolean     is_ms_tag                     (const gchar       *str,
-                                                   gint              *duration,
-                                                   gint              *taglength);
+static gboolean  is_ms_tag                        (const gchar          *str,
+                                                   gint                 *duration,
+                                                   gint                 *taglength);
 
 G_DEFINE_TYPE (AnimationAnimatic, animation_animatic, ANIMATION_TYPE_ANIMATION)
 
@@ -165,14 +169,18 @@ animation_animatic_class_init (AnimationAnimaticClass *klass)
                   G_TYPE_INT,
                   G_TYPE_INT);
 
-  object_class->finalize = animation_animatic_finalize;
+  object_class->finalize     = animation_animatic_finalize;
 
-  anim_class->get_duration = animation_animatic_get_duration;
-  anim_class->load         = animation_animatic_load;
-  anim_class->load_xml     = animation_animatic_load_xml;
-  anim_class->get_frame    = animation_animatic_get_frame;
-  anim_class->serialize    = animation_animatic_serialize;
-  anim_class->same         = animation_animatic_same;
+  anim_class->get_duration   = animation_animatic_get_duration;
+  anim_class->get_frame      = animation_animatic_get_frame;
+
+  anim_class->same           = animation_animatic_same;
+
+  anim_class->purge_cache    = animation_animatic_purge_cache;
+
+  anim_class->reset_defaults = animation_animatic_reset_defaults;
+  anim_class->serialize      = animation_animatic_serialize;
+  anim_class->deserialize    = animation_animatic_deserialize;
 
   g_type_class_add_private (klass, sizeof (AnimationAnimaticPrivate));
 }
@@ -297,7 +305,7 @@ animation_animatic_set_combine (AnimationAnimatic *animatic,
   if (priv->combine[panel_num] != combine)
     {
       priv->combine[panel_num] = combine;
-      animation_animatic_cache (animatic, panel_num, TRUE);
+      animation_animatic_cache_panel (animatic, panel_num, TRUE);
     }
 }
 
@@ -375,8 +383,64 @@ animation_animatic_get_duration (Animation *animation)
   return count;
 }
 
+static GeglBuffer *
+animation_animatic_get_frame (Animation *animation,
+                              gint       pos)
+{
+  AnimationAnimaticPrivate *priv;
+  gint                      panel;
+
+  priv = GET_PRIVATE (animation);
+  panel = animation_animatic_get_panel (ANIMATION_ANIMATIC (animation),
+                                        pos);
+  return g_object_ref (priv->cache[panel]);
+}
+
+static gboolean
+animation_animatic_same (Animation *animation,
+                         gint       pos1,
+                         gint       pos2)
+{
+  AnimationAnimaticPrivate *priv = GET_PRIVATE (animation);
+  gint                      count = 0;
+  gboolean                  identical = FALSE;
+  gint                      i ;
+
+  for (i = 0; i < priv->n_panels; i++)
+    {
+      count += priv->durations[i];
+      if (count > pos1 && count > pos2)
+        {
+          identical = TRUE;
+          break;
+        }
+      else if (count > pos1 || count > pos2)
+        {
+          identical = FALSE;
+          break;
+        }
+    }
+
+  return identical;
+}
+
 static void
-animation_animatic_load (Animation *animation)
+animation_animatic_purge_cache (Animation *animation)
+{
+  AnimationAnimaticPrivate *priv = GET_PRIVATE (animation);
+  gint                      i;
+
+  for (i = 0; i < priv->n_panels; i++)
+    {
+      animation_animatic_cache_panel (ANIMATION_ANIMATIC (animation), i, FALSE);
+      g_signal_emit_by_name (animation, "loading",
+                             (gdouble) i / ((gdouble) priv->n_panels - 0.999));
+    }
+  g_signal_emit_by_name (animation, "loaded");
+}
+
+static void
+animation_animatic_reset_defaults (Animation *animation)
 {
   AnimationAnimaticPrivate *priv = GET_PRIVATE (animation);
   gint32                   *layers;
@@ -384,21 +448,17 @@ animation_animatic_load (Animation *animation)
   gint                      i;
 
   /* Cleaning. */
-  if (priv->cache)
+  g_free (priv->tattoos);
+  g_free (priv->durations);
+  g_free (priv->combine);
+  for (i = 0; i < priv->n_panels; i++)
     {
-      g_free (priv->tattoos);
-      g_free (priv->durations);
-      g_free (priv->combine);
-
-      for (i = 0; i < priv->n_panels; i++)
-        {
-          g_free (priv->comments[i]);
-          if (priv->cache[i])
-            g_object_unref (priv->cache[i]);
-        }
-      g_free (priv->comments);
-      g_free (priv->cache);
+      g_free (priv->comments[i]);
+      if (priv->cache[i])
+        g_object_unref (priv->cache[i]);
     }
+  g_free (priv->comments);
+  g_free (priv->cache);
 
   image_id = animation_get_image_id (animation);
   layers   = gimp_image_get_layers (image_id, &priv->n_panels);
@@ -423,9 +483,6 @@ animation_animatic_load (Animation *animation)
       gint      duration;
       gboolean  combine;
 
-      g_signal_emit_by_name (animation, "loading",
-                             (gdouble) i / ((gdouble) priv->n_panels - 0.999));
-
       layer_name = gimp_item_get_name (layers[priv->n_panels - (i + 1)]);
 
       duration = parse_ms_tag (animation, layer_name);
@@ -437,72 +494,10 @@ animation_animatic_load (Animation *animation)
       priv->combine[i]   = combine;
       /* Layer names are used as default comments. */
       priv->comments[i]  = layer_name;
-
-      /* Panel image. */
-      animation_animatic_cache (ANIMATION_ANIMATIC (animation), i, FALSE);
     }
   g_free (layers);
 }
 
-static void
-animation_animatic_load_xml (Animation   *animation,
-                             const gchar *xml)
-{
-  const GMarkupParser markup_parser =
-    {
-      animation_animatic_start_element,
-      animation_animatic_end_element,
-      animation_animatic_text,
-      NULL,  /*  passthrough  */
-      NULL   /*  error        */
-    };
-  GMarkupParseContext *context;
-  ParseStatus          status = { 0, };
-  GError              *error  = NULL;
-
-  g_return_if_fail (xml != NULL);
-
-  /* Init with a default load. */
-  animation_animatic_load (animation);
-
-  /* Parse XML to update. */
-  status.state = START_STATE;
-  status.animation = animation;
-  status.xml_level = 0;
-
-  context = g_markup_parse_context_new (&markup_parser,
-                                        0, &status, NULL);
-  g_markup_parse_context_parse (context, xml, strlen (xml), &error);
-  if (error)
-    {
-      g_warning ("Error parsing XML: %s", error->message);
-    }
-  else
-    {
-      g_markup_parse_context_end_parse (context, &error);
-      if (error)
-        g_warning ("Error parsing XML: %s", error->message);
-    }
-  g_markup_parse_context_free (context);
-
-  /* If XML parsing failed, just reset the animation. */
-  if (error)
-    animation_animatic_load (animation);
-}
-
-static GeglBuffer *
-animation_animatic_get_frame (Animation *animation,
-                              gint       pos)
-{
-  AnimationAnimaticPrivate *priv;
-  gint                      panel;
-
-  priv = GET_PRIVATE (animation);
-  panel = animation_animatic_get_panel (ANIMATION_ANIMATIC (animation),
-                                        pos);
-  return g_object_ref (priv->cache[panel]);
-}
-
 static gchar *
 animation_animatic_serialize (Animation *animation)
 {
@@ -566,31 +561,39 @@ animation_animatic_serialize (Animation *animation)
 }
 
 static gboolean
-animation_animatic_same (Animation *animation,
-                         gint       pos1,
-                         gint       pos2)
+animation_animatic_deserialize (Animation    *animation,
+                                const gchar  *xml,
+                                GError      **error)
 {
-  AnimationAnimaticPrivate *priv = GET_PRIVATE (animation);
-  gint                      count = 0;
-  gboolean                  identical = FALSE;
-  gint                      i ;
-
-  for (i = 0; i < priv->n_panels; i++)
+  const GMarkupParser markup_parser =
     {
-      count += priv->durations[i];
-      if (count > pos1 && count > pos2)
-        {
-          identical = TRUE;
-          break;
-        }
-      else if (count > pos1 || count > pos2)
-        {
-          identical = FALSE;
-          break;
-        }
-    }
+      animation_animatic_start_element,
+      animation_animatic_end_element,
+      animation_animatic_text,
+      NULL,  /*  passthrough  */
+      NULL   /*  error        */
+    };
+  GMarkupParseContext *context;
+  ParseStatus          status = { 0, };
 
-  return identical;
+  g_return_val_if_fail (xml != NULL && *error == NULL, FALSE);
+
+  /* Init with a default load. */
+  animation_animatic_reset_defaults (animation);
+
+  /* Parse XML to update. */
+  status.state = START_STATE;
+  status.animation = animation;
+  status.xml_level = 0;
+
+  context = g_markup_parse_context_new (&markup_parser,
+                                        0, &status, NULL);
+  g_markup_parse_context_parse (context, xml, strlen (xml), error);
+  if (*error == NULL)
+    g_markup_parse_context_end_parse (context, error);
+  g_markup_parse_context_free (context);
+
+  return (*error == NULL);
 }
 
 static void
@@ -692,11 +695,7 @@ animation_animatic_start_element (GMarkupParseContext *context,
           values++;
         }
       if (priv->combine[status->panel] != combine)
-        {
-          priv->combine[status->panel] = combine;
-          animation_animatic_cache (ANIMATION_ANIMATIC (status->animation),
-                                    status->panel, FALSE);
-        }
+        priv->combine[status->panel] = combine;
       status->state = PANEL_STATE;
       break;
     case PANEL_STATE:
@@ -828,9 +827,9 @@ animation_animatic_text (GMarkupParseContext  *context,
 /**** Utils ****/
 
 static void
-animation_animatic_cache (AnimationAnimatic *animatic,
-                          gint               panel,
-                          gboolean           recursion)
+animation_animatic_cache_panel (AnimationAnimatic *animatic,
+                                gint               panel,
+                                gboolean           recursion)
 {
   AnimationAnimaticPrivate *priv      = GET_PRIVATE (animatic);
   Animation                *animation = ANIMATION (animatic);
@@ -878,10 +877,12 @@ animation_animatic_cache (AnimationAnimatic *animatic,
                                      layer_offx, layer_offy);
   g_object_unref (buffer);
 
-  g_signal_emit_by_name (animation, "cache-invalidated",
-                         animation_animatic_get_position (animatic,
-                                                          panel),
-                         1);
+  if (animation_animatic_get_panel_duration (animatic, panel) > 0)
+    g_signal_emit_by_name (animation, "cache-invalidated",
+                           animation_animatic_get_position (animatic,
+                                                            panel),
+                           animation_animatic_get_panel_duration (animatic,
+                                                                  panel));
 
   /* If next panel is in "combine" mode, it must also be re-cached.
    * And so on, recursively. */
@@ -889,7 +890,7 @@ animation_animatic_cache (AnimationAnimatic *animatic,
       panel < priv->n_panels - 1 &&
       priv->combine[panel + 1])
     {
-      animation_animatic_cache (animatic, panel + 1, TRUE);
+      animation_animatic_cache_panel (animatic, panel + 1, TRUE);
     }
 }
 
diff --git a/plug-ins/animation-play/core/animation-celanimation.c 
b/plug-ins/animation-play/core/animation-celanimation.c
index b860c43..5b08fc7 100644
--- a/plug-ins/animation-play/core/animation-celanimation.c
+++ b/plug-ins/animation-play/core/animation-celanimation.c
@@ -95,51 +95,55 @@ typedef struct
                                      ANIMATION_TYPE_CEL_ANIMATION, \
                                      AnimationCelAnimationPrivate)
 
-static void         animation_cel_animation_finalize     (GObject           *object);
+static void         animation_cel_animation_finalize       (GObject           *object);
 
 /* Virtual methods */
 
-static gint         animation_cel_animation_get_duration (Animation         *animation);
+static gint         animation_cel_animation_get_duration   (Animation         *animation);
 
-static void         animation_cel_animation_load         (Animation         *animation);
-static void         animation_cel_animation_load_xml     (Animation         *animation,
-                                                          const gchar       *xml);
-static GeglBuffer * animation_cel_animation_get_frame    (Animation         *animation,
-                                                          gint               pos);
-static gchar      * animation_cel_animation_serialize    (Animation         *animation);
+static GeglBuffer * animation_cel_animation_get_frame      (Animation         *animation,
+                                                            gint               pos);
 
-static gboolean     animation_cel_animation_same         (Animation         *animation,
-                                                          gint               previous_pos,
-                                                          gint               next_pos);
+static gboolean     animation_cel_animation_same           (Animation         *animation,
+                                                            gint               previous_pos,
+                                                            gint               next_pos);
+
+static void         animation_cel_animation_purge_cache    (Animation         *animation);
+
+static void         animation_cel_animation_reset_defaults (Animation         *animation);
+static gchar      * animation_cel_animation_serialize      (Animation         *animation);
+static gboolean     animation_cel_animation_deserialize    (Animation         *animation,
+                                                            const gchar       *xml,
+                                                            GError           **error);
 
 /* XML parsing */
 
-static void      animation_cel_animation_start_element   (GMarkupParseContext *context,
-                                                          const gchar         *element_name,
-                                                          const gchar        **attribute_names,
-                                                          const gchar        **attribute_values,
-                                                          gpointer             user_data,
-                                                          GError             **error);
-static void      animation_cel_animation_end_element     (GMarkupParseContext *context,
-                                                          const gchar         *element_name,
-                                                          gpointer             user_data,
-                                                          GError             **error);
-
-static void      animation_cel_animation_text            (GMarkupParseContext  *context,
-                                                          const gchar          *text,
-                                                          gsize                 text_len,
-                                                          gpointer              user_data,
-                                                          GError              **error);
+static void      animation_cel_animation_start_element     (GMarkupParseContext *context,
+                                                            const gchar         *element_name,
+                                                            const gchar        **attribute_names,
+                                                            const gchar        **attribute_values,
+                                                            gpointer             user_data,
+                                                            GError             **error);
+static void      animation_cel_animation_end_element       (GMarkupParseContext *context,
+                                                            const gchar         *element_name,
+                                                            gpointer             user_data,
+                                                            GError             **error);
+
+static void      animation_cel_animation_text              (GMarkupParseContext  *context,
+                                                            const gchar          *text,
+                                                            gsize                 text_len,
+                                                            gpointer              user_data,
+                                                            GError              **error);
 
 /* Utils */
 
-static void         animation_cel_animation_cleanup      (AnimationCelAnimation  *animation);
-static void         animation_cel_animation_cache        (AnimationCelAnimation  *animation,
-                                                          gint                    position);
-static gboolean     animation_cel_animation_cache_cmp    (Cache                   *cache1,
-                                                          Cache                   *cache2);
-static void         animation_cel_animation_clean_cache  (Cache                   *cache);
-static void         animation_cel_animation_clean_track  (Track                   *track);
+static void         animation_cel_animation_cache          (AnimationCelAnimation  *animation,
+                                                            gint                    position);
+static void         animation_cel_animation_cleanup        (AnimationCelAnimation  *animation);
+static gboolean     animation_cel_animation_cache_cmp      (Cache                   *cache1,
+                                                            Cache                   *cache2);
+static void         animation_cel_animation_clean_cache    (Cache                   *cache);
+static void         animation_cel_animation_clean_track    (Track                   *track);
 
 G_DEFINE_TYPE (AnimationCelAnimation, animation_cel_animation, ANIMATION_TYPE_ANIMATION)
 
@@ -151,14 +155,17 @@ animation_cel_animation_class_init (AnimationCelAnimationClass *klass)
   GObjectClass   *object_class = G_OBJECT_CLASS (klass);
   AnimationClass *anim_class   = ANIMATION_CLASS (klass);
 
-  object_class->finalize = animation_cel_animation_finalize;
+  object_class->finalize     = animation_cel_animation_finalize;
+
+  anim_class->get_duration   = animation_cel_animation_get_duration;
+  anim_class->get_frame      = animation_cel_animation_get_frame;
+  anim_class->same           = animation_cel_animation_same;
 
-  anim_class->get_duration = animation_cel_animation_get_duration;
-  anim_class->load         = animation_cel_animation_load;
-  anim_class->load_xml     = animation_cel_animation_load_xml;
-  anim_class->get_frame    = animation_cel_animation_get_frame;
-  anim_class->serialize    = animation_cel_animation_serialize;
-  anim_class->same         = animation_cel_animation_same;
+  anim_class->purge_cache    = animation_cel_animation_purge_cache;
+
+  anim_class->reset_defaults = animation_cel_animation_reset_defaults;
+  anim_class->serialize      = animation_cel_animation_serialize;
+  anim_class->deserialize    = animation_cel_animation_deserialize;
 
   g_type_class_add_private (klass, sizeof (AnimationCelAnimationPrivate));
 }
@@ -222,6 +229,7 @@ animation_cel_animation_set_layers (AnimationCelAnimation *animation,
           layers->data = NULL;
         }
       animation_cel_animation_cache (animation, position);
+      g_signal_emit_by_name (animation, "cache-invalidated", position, 1);
     }
 }
 
@@ -389,6 +397,8 @@ animation_cel_animation_level_up (AnimationCelAnimation *animation,
               animation_cel_animation_cache (animation, i);
             }
         }
+      g_signal_emit_by_name (animation, "cache-invalidated",
+                             0, g_list_length (track->frames));
       g_signal_emit_by_name (animation, "loaded");
     }
 
@@ -436,6 +446,8 @@ animation_cel_animation_level_down (AnimationCelAnimation *animation,
               animation_cel_animation_cache (animation, i);
             }
         }
+      g_signal_emit_by_name (animation, "cache-invalidated",
+                             0, g_list_length (track->frames));
       g_signal_emit_by_name (animation, "loaded");
     }
 
@@ -459,7 +471,6 @@ animation_cel_animation_level_delete (AnimationCelAnimation *animation,
     {
       item = g_list_nth (animation->priv->tracks, level);
       track = item->data;
-      animation_cel_animation_clean_track (track);
       animation->priv->tracks = g_list_delete_link (animation->priv->tracks, item);
 
       iter = track->frames;
@@ -474,7 +485,10 @@ animation_cel_animation_level_delete (AnimationCelAnimation *animation,
               animation_cel_animation_cache (animation, i);
             }
         }
+      g_signal_emit_by_name (animation, "cache-invalidated",
+                             0, g_list_length (track->frames));
       g_signal_emit_by_name (animation, "loaded");
+      animation_cel_animation_clean_track (track);
 
       return TRUE;
     }
@@ -560,6 +574,9 @@ animation_cel_animation_cel_delete (AnimationCelAnimation *animation,
 
               animation_cel_animation_cache (animation, i);
             }
+          if (i > position)
+            g_signal_emit_by_name (animation, "cache-invalidated",
+                                   position, i - position);
           g_signal_emit_by_name (animation, "loaded");
 
           return TRUE;
@@ -610,6 +627,9 @@ animation_cel_animation_cel_add (AnimationCelAnimation *animation,
 
               animation_cel_animation_cache (animation, i);
             }
+          if (i > position)
+            g_signal_emit_by_name (animation, "cache-invalidated",
+                                   position, i - position);
           g_signal_emit_by_name (animation, "loaded");
 
           return TRUE;
@@ -626,154 +646,110 @@ animation_cel_animation_get_duration (Animation *animation)
   return ANIMATION_CEL_ANIMATION (animation)->priv->duration;
 }
 
-static void
-animation_cel_animation_load (Animation *animation)
+static GeglBuffer *
+animation_cel_animation_get_frame (Animation *animation,
+                                   gint       pos)
 {
-  AnimationCelAnimationPrivate *priv;
-  Track                        *track;
-  gint32                        image_id;
-  gint32                        layer;
-  gint                          i;
+  AnimationCelAnimation *cel_animation;
+  Cache                 *cache;
+  GeglBuffer            *frame = NULL;
 
-  priv = ANIMATION_CEL_ANIMATION (animation)->priv;
+  cel_animation = ANIMATION_CEL_ANIMATION (animation);
 
-  /* First load with default values. */
-  if (! priv->tracks)
+  cache = g_list_nth_data (cel_animation->priv->cache,
+                           pos);
+  if (cache)
     {
-      /* Purely arbitrary value. User will anyway change it to suit one's needs. */
-      priv->duration = 240;
-
-      /* There are at least 2 tracks.
-       * Second one is freely-named. */
-      track = g_new0 (Track, 1);
-      track->title = g_strdup (_("Name me"));
-      priv->tracks = g_list_prepend (priv->tracks, track);
-      /* The first track is called "Background". */
-      track = g_new0 (Track, 1);
-      track->title = g_strdup (_("Background"));
-      priv->tracks = g_list_prepend (priv->tracks, track);
-
-      /* If there is a layer named "Background", set it to all frames
-       * on background track. */
-      image_id = animation_get_image_id (animation);
-      layer    = gimp_image_get_layer_by_name (image_id, _("Background"));
-      if (layer > 0)
-        {
-          gint tattoo;
+      frame = g_object_ref (cache->buffer);
+    }
+  return frame;
+}
 
-          tattoo = gimp_item_get_tattoo (layer);
-          for (i = 0; i < priv->duration; i++)
-            {
-              GList *layers = NULL;
+static gboolean
+animation_cel_animation_same (Animation *animation,
+                              gint       pos1,
+                              gint       pos2)
+{
+  AnimationCelAnimation *cel_animation;
+  Cache                 *cache1;
+  Cache                 *cache2;
 
-              layers = g_list_prepend (layers,
-                                       GINT_TO_POINTER (tattoo));
-              track->frames = g_list_prepend (track->frames,
-                                              layers);
-            }
-        }
-    }
+  cel_animation = ANIMATION_CEL_ANIMATION (animation);
 
-  /* Invalidate all cache. */
-  g_list_free_full (priv->cache,
-                    (GDestroyNotify) animation_cel_animation_clean_cache);
-  priv->cache = NULL;
+  g_return_val_if_fail (pos1 >= 0                            &&
+                        pos1 < cel_animation->priv->duration &&
+                        pos2 >= 0                            &&
+                        pos2 < cel_animation->priv->duration,
+                        FALSE);
 
-  /* Finally cache. */
-  for (i = 0; i < priv->duration; i++)
-    {
-      g_signal_emit_by_name (animation, "loading",
-                             (gdouble) i / ((gdouble) priv->duration - 0.999));
+  cache1 = g_list_nth_data (cel_animation->priv->cache, pos1);
+  cache2 = g_list_nth_data (cel_animation->priv->cache, pos2);
 
-      /* Panel image. */
-      animation_cel_animation_cache (ANIMATION_CEL_ANIMATION (animation), i);
-    }
+  return animation_cel_animation_cache_cmp (cache1, cache2);
 }
 
 static void
-animation_cel_animation_load_xml (Animation   *animation,
-                                  const gchar *xml)
+animation_cel_animation_purge_cache (Animation *animation)
 {
-  const GMarkupParser markup_parser =
+  gint duration;
+  gint i;
+
+  duration = animation_get_duration (animation);
+  for (i = 0; i < duration; i++)
     {
-      animation_cel_animation_start_element,
-      animation_cel_animation_end_element,
-      animation_cel_animation_text,
-      NULL,  /*  passthrough  */
-      NULL   /*  error        */
-    };
-  GMarkupParseContext *context;
-  ParseStatus          status = { 0, };
-  GError              *error  = NULL;
+      animation_cel_animation_cache (ANIMATION_CEL_ANIMATION (animation),
+                                     i);
+      g_signal_emit_by_name (animation, "loading",
+                             (gdouble) i / ((gdouble) duration - 0.999));
+    }
+  g_signal_emit_by_name (animation, "loaded");
+}
 
-  g_return_if_fail (xml != NULL);
+static void
+animation_cel_animation_reset_defaults (Animation *animation)
+{
+  AnimationCelAnimationPrivate *priv;
+  Track                        *track;
+  gint32                        image_id;
+  gint32                        layer;
+  gint                          i;
 
-  /* Parse XML to update. */
-  status.state = START_STATE;
-  status.animation = animation;
+  priv = ANIMATION_CEL_ANIMATION (animation)->priv;
+  animation_cel_animation_cleanup (ANIMATION_CEL_ANIMATION (animation));
 
-  context = g_markup_parse_context_new (&markup_parser,
-                                        0, &status, NULL);
-  g_markup_parse_context_parse (context, xml, strlen (xml), &error);
-  if (error)
-    {
-      g_warning ("Error parsing XML: %s", error->message);
-    }
-  else
-    {
-      g_markup_parse_context_end_parse (context, &error);
-      if (error)
-        g_warning ("Error parsing XML: %s", error->message);
-    }
-  g_markup_parse_context_free (context);
+  /* Purely arbitrary value. User will anyway change it to suit one's needs. */
+  priv->duration = 240;
 
-  /* If XML parsing failed, just reset the animation. */
-  if (error)
-    {
-      animation_cel_animation_cleanup (ANIMATION_CEL_ANIMATION (animation));
-      animation_cel_animation_load (animation);
-    }
-  else
+  /* There are at least 2 tracks. Second one is freely-named. */
+  track = g_new0 (Track, 1);
+  track->title = g_strdup (_("Name me"));
+  priv->tracks = g_list_prepend (priv->tracks, track);
+  /* The first track is called "Background". */
+  track = g_new0 (Track, 1);
+  track->title = g_strdup (_("Background"));
+  priv->tracks = g_list_prepend (priv->tracks, track);
+
+  /* If there is a layer named "Background", set it to all frames
+   * on background track. */
+  image_id = animation_get_image_id (animation);
+  layer    = gimp_image_get_layer_by_name (image_id, _("Background"));
+  if (layer > 0)
     {
-      AnimationCelAnimation *cel_animation;
-      gint                   duration = animation_get_duration (animation);
-      gint                   i;
-
-      cel_animation = ANIMATION_CEL_ANIMATION (animation);
-      /* Reverse track order. */
-      cel_animation->priv->tracks = g_list_reverse (cel_animation->priv->tracks);
+      gint tattoo;
 
-      /* cache. */
-      for (i = 0; i < duration; i++)
+      tattoo = gimp_item_get_tattoo (layer);
+      for (i = 0; i < priv->duration; i++)
         {
-          g_signal_emit_by_name (animation, "loading",
-                                 (gdouble) i / ((gdouble) duration - 0.999));
+          GList *layers = NULL;
 
-          /* Panel image. */
-          animation_cel_animation_cache (ANIMATION_CEL_ANIMATION (animation), i);
+          layers = g_list_prepend (layers,
+                                   GINT_TO_POINTER (tattoo));
+          track->frames = g_list_prepend (track->frames,
+                                          layers);
         }
     }
 }
 
-static GeglBuffer *
-animation_cel_animation_get_frame (Animation *animation,
-                                   gint       pos)
-{
-  AnimationCelAnimation *cel_animation;
-  Cache                 *cache;
-  GeglBuffer            *frame = NULL;
-
-  cel_animation = ANIMATION_CEL_ANIMATION (animation);
-
-  cache = g_list_nth_data (cel_animation->priv->cache,
-                           pos);
-  if (cache)
-    {
-      frame = g_object_ref (cache->buffer);
-    }
-  return frame;
-}
-
 static gchar *
 animation_cel_animation_serialize (Animation *animation)
 {
@@ -901,26 +877,43 @@ animation_cel_animation_serialize (Animation *animation)
 }
 
 static gboolean
-animation_cel_animation_same (Animation *animation,
-                              gint       pos1,
-                              gint       pos2)
+animation_cel_animation_deserialize (Animation    *animation,
+                                     const gchar  *xml,
+                                     GError      **error)
 {
-  AnimationCelAnimation *cel_animation;
-  Cache                 *cache1;
-  Cache                 *cache2;
+  const GMarkupParser markup_parser =
+    {
+      animation_cel_animation_start_element,
+      animation_cel_animation_end_element,
+      animation_cel_animation_text,
+      NULL,  /*  passthrough  */
+      NULL   /*  error        */
+    };
+  GMarkupParseContext *context;
+  ParseStatus          status = { 0, };
 
-  cel_animation = ANIMATION_CEL_ANIMATION (animation);
+  g_return_val_if_fail (xml != NULL && *error == NULL, FALSE);
 
-  g_return_val_if_fail (pos1 >= 0                            &&
-                        pos1 < cel_animation->priv->duration &&
-                        pos2 >= 0                            &&
-                        pos2 < cel_animation->priv->duration,
-                        FALSE);
+  /* Parse XML to update. */
+  status.state = START_STATE;
+  status.animation = animation;
 
-  cache1 = g_list_nth_data (cel_animation->priv->cache, pos1);
-  cache2 = g_list_nth_data (cel_animation->priv->cache, pos2);
+  context = g_markup_parse_context_new (&markup_parser,
+                                        0, &status, NULL);
+  g_markup_parse_context_parse (context, xml, strlen (xml), error);
+  if (*error == NULL)
+    {
+      AnimationCelAnimation *cel_animation;
 
-  return animation_cel_animation_cache_cmp (cache1, cache2);
+      g_markup_parse_context_end_parse (context, error);
+
+      cel_animation = ANIMATION_CEL_ANIMATION (animation);
+      /* Reverse track order. */
+      cel_animation->priv->tracks = g_list_reverse (cel_animation->priv->tracks);
+    }
+  g_markup_parse_context_free (context);
+
+  return (*error == NULL);
 }
 
 static void
@@ -1217,20 +1210,6 @@ animation_cel_animation_text (GMarkupParseContext  *context,
 /**** Utils ****/
 
 static void
-animation_cel_animation_cleanup (AnimationCelAnimation *animation)
-{
-  g_list_free_full (animation->priv->cache,
-                    (GDestroyNotify) animation_cel_animation_clean_cache);
-  animation->priv->cache    = NULL;
-  g_list_free_full (animation->priv->comments,
-                    (GDestroyNotify) g_free);
-  animation->priv->comments = NULL;
-  g_list_free_full (animation->priv->tracks,
-                    (GDestroyNotify) animation_cel_animation_clean_track);
-  animation->priv->tracks   = NULL;
-}
-
-static void
 animation_cel_animation_cache (AnimationCelAnimation *animation,
                                gint                   pos)
 {
@@ -1386,8 +1365,20 @@ animation_cel_animation_cache (AnimationCelAnimation *animation,
 
   /* This item exists and has a NULL data. */
   g_list_nth (animation->priv->cache, pos)->data = cache;
+}
 
-  g_signal_emit_by_name (animation, "cache-invalidated", pos, 1);
+static void
+animation_cel_animation_cleanup (AnimationCelAnimation *animation)
+{
+  g_list_free_full (animation->priv->cache,
+                    (GDestroyNotify) animation_cel_animation_clean_cache);
+  animation->priv->cache    = NULL;
+  g_list_free_full (animation->priv->comments,
+                    (GDestroyNotify) g_free);
+  animation->priv->comments = NULL;
+  g_list_free_full (animation->priv->tracks,
+                    (GDestroyNotify) animation_cel_animation_clean_track);
+  animation->priv->tracks   = NULL;
 }
 
 static gboolean
diff --git a/plug-ins/animation-play/core/animation-playback.c 
b/plug-ins/animation-play/core/animation-playback.c
index f40b402..7c194e2 100644
--- a/plug-ins/animation-play/core/animation-playback.c
+++ b/plug-ins/animation-play/core/animation-playback.c
@@ -514,8 +514,6 @@ animation_playback_set_property (GObject      *object,
           if (! animation)
             break;
 
-          if (! animation_loaded (animation))
-            animation_load (animation);
           /* Default playback is the full range of frames. */
           playback->priv->position = 0;
           playback->priv->start = 0;
diff --git a/plug-ins/animation-play/core/animation.c b/plug-ins/animation-play/core/animation.c
index c714be3..654ec3a 100644
--- a/plug-ins/animation-play/core/animation.c
+++ b/plug-ins/animation-play/core/animation.c
@@ -54,7 +54,8 @@ enum
 enum
 {
   PROP_0,
-  PROP_IMAGE
+  PROP_IMAGE,
+  PROP_XML
 };
 
 typedef struct _AnimationPrivate AnimationPrivate;
@@ -62,7 +63,6 @@ typedef struct _AnimationPrivate AnimationPrivate;
 struct _AnimationPrivate
 {
   gint32    image_id;
-  gchar    *xml;
 
   gdouble   framerate;
 
@@ -227,13 +227,23 @@ animation_class_init (AnimationClass *klass)
   /**
    * Animation:image:
    *
-   * The attached image id.
+   * The associated image id.
    */
   g_object_class_install_property (object_class, PROP_IMAGE,
                                    g_param_spec_int ("image", NULL, NULL,
                                                      0, G_MAXINT, 0,
                                                      G_PARAM_READWRITE |
                                                      G_PARAM_CONSTRUCT_ONLY));
+  /**
+   * Animation:xml:
+   *
+   * The animation serialized as a XML string.
+   */
+  g_object_class_install_property (object_class, PROP_XML,
+                                   g_param_spec_string ("xml", NULL,
+                                                        NULL, NULL,
+                                                        G_PARAM_READWRITE |
+                                                        G_PARAM_CONSTRUCT_ONLY));
 
   g_type_class_add_private (klass, sizeof (AnimationPrivate));
 }
@@ -262,15 +272,13 @@ animation_new (gint32       image_id,
                gboolean     animatic,
                const gchar *xml)
 {
-  Animation        *animation;
-  AnimationPrivate *priv;
+  Animation *animation;
 
   animation = g_object_new (animatic? ANIMATION_TYPE_ANIMATIC :
                                       ANIMATION_TYPE_CEL_ANIMATION,
                             "image", image_id,
+                            "xml", xml,
                             NULL);
-  priv = ANIMATION_GET_PRIVATE (animation);
-  priv->xml = g_strdup (xml);
 
   return animation;
 }
@@ -283,27 +291,21 @@ animation_get_image_id (Animation *animation)
   return priv->image_id;
 }
 
+/**
+ * animation_load:
+ * @animation: the #Animation.
+ *
+ * Cache the whole animation. This is to be used at the start, or each
+ * time you want to reload the image contents.
+ **/
 void
 animation_load (Animation *animation)
 {
   AnimationPrivate *priv = ANIMATION_GET_PRIVATE (animation);
 
   priv->loaded = FALSE;
-  g_signal_emit (animation, animation_signals[LOADING], 0, 0.0);
-
-  if (priv->xml)
-    ANIMATION_GET_CLASS (animation)->load_xml (animation,
-                                               priv->xml);
-  else
-    ANIMATION_GET_CLASS (animation)->load (animation);
-
-  /* XML is only used for the first load.
-   * Any next loads will use internal data. */
-  g_free (priv->xml);
-  priv->xml = NULL;
-
+  ANIMATION_GET_CLASS (animation)->purge_cache (animation);
   priv->loaded = TRUE;
-  g_signal_emit (animation, animation_signals[LOADED], 0);
   g_signal_emit (animation, animation_signals[CACHE_INVALIDATED], 0,
                  0, animation_get_duration (animation));
 }
@@ -480,12 +482,6 @@ animation_loaded (Animation *animation)
 static void
 animation_finalize (GObject *object)
 {
-  Animation        *animation = ANIMATION (object);
-  AnimationPrivate *priv = ANIMATION_GET_PRIVATE (animation);
-
-  if (priv->xml)
-    g_free (priv->xml);
-
   G_OBJECT_CLASS (parent_class)->finalize (object);
 }
 
@@ -503,6 +499,25 @@ animation_set_property (GObject      *object,
     case PROP_IMAGE:
       priv->image_id = g_value_get_int (value);
       break;
+    case PROP_XML:
+        {
+          const gchar *xml   = g_value_get_string (value);
+          GError      *error = NULL;
+
+          if (! xml ||
+              ! ANIMATION_GET_CLASS (animation)->deserialize (animation,
+                                                              xml,
+                                                              &error))
+            {
+              if (error)
+                g_warning ("Error parsing XML: %s", error->message);
+
+              /* First time or XML parsing failed: reset to defaults. */
+              ANIMATION_GET_CLASS (animation)->reset_defaults (animation);
+            }
+          g_clear_error (&error);
+        }
+      break;
 
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
@@ -524,6 +539,14 @@ animation_get_property (GObject    *object,
     case PROP_IMAGE:
       g_value_set_int (value, priv->image_id);
       break;
+    case PROP_XML:
+        {
+          gchar *xml;
+
+          xml = ANIMATION_GET_CLASS (animation)->serialize (animation);
+          g_value_take_string (value, xml);
+        }
+      break;
 
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
diff --git a/plug-ins/animation-play/core/animation.h b/plug-ins/animation-play/core/animation.h
index de47fbc..59be8bc 100644
--- a/plug-ins/animation-play/core/animation.h
+++ b/plug-ins/animation-play/core/animation.h
@@ -41,34 +41,37 @@ struct _AnimationClass
   GObjectClass  parent_class;
 
   /* Signals */
-  void         (*loading)            (Animation *animation,
-                                      gdouble    ratio);
-  void         (*loaded)             (Animation *animation);
-
-  void         (*cache_invalidated)  (Animation *animation,
-                                      gint       position);
-  void         (*duration_changed)   (Animation *animation,
-                                      gint       duration);
-  void         (*framerate_changed)  (Animation *animation,
-                                      gdouble    fps);
-  void         (*proxy)              (Animation *animation,
-                                      gdouble    ratio);
+  void         (*loading)            (Animation    *animation,
+                                      gdouble       ratio);
+  void         (*loaded)             (Animation    *animation);
+
+  void         (*cache_invalidated)  (Animation    *animation,
+                                      gint          position);
+  void         (*duration_changed)   (Animation    *animation,
+                                      gint          duration);
+  void         (*framerate_changed)  (Animation    *animation,
+                                      gdouble       fps);
+  void         (*proxy)              (Animation    *animation,
+                                      gdouble       ratio);
 
   /* Defaults to returning FALSE for any different position. */
-  gboolean     (*same)               (Animation *animation,
-                                      gint       prev_pos,
-                                      gint       next_pos);
+  gboolean     (*same)               (Animation    *animation,
+                                      gint          prev_pos,
+                                      gint          next_pos);
 
   /* These virtual methods must be implemented by any subclass. */
-  void         (*load)               (Animation   *animation);
-  void         (*load_xml)           (Animation   *animation,
-                                      const gchar *xml);
-  gint         (*get_duration)       (Animation   *animation);
+  gint         (*get_duration)       (Animation    *animation);
 
-  GeglBuffer * (*get_frame)          (Animation   *animation,
-                                      gint         pos);
+  GeglBuffer * (*get_frame)          (Animation    *animation,
+                                      gint          pos);
 
-  gchar      * (*serialize)          (Animation   *animation);
+  void         (*purge_cache)        (Animation    *animation);
+
+  void         (*reset_defaults)     (Animation    *animation);
+  gchar      * (*serialize)          (Animation    *animation);
+  gboolean     (*deserialize)        (Animation    *animation,
+                                      const gchar  *xml,
+                                      GError      **error);
 };
 
 GType         animation_get_type (void);
diff --git a/plug-ins/animation-play/widgets/animation-dialog.c 
b/plug-ins/animation-play/widgets/animation-dialog.c
index a0eb27f..ab68d23 100755
--- a/plug-ins/animation-play/widgets/animation-dialog.c
+++ b/plug-ins/animation-play/widgets/animation-dialog.c
@@ -139,6 +139,10 @@ static void        connect_accelerators      (GtkUIManager     *ui_manager,
 
 static void        animation_dialog_set_animation (AnimationDialog *dialog,
                                                    Animation       *animation);
+
+static gboolean    on_dialog_expose                (GtkWidget      *widget,
+                                                    GdkEvent       *event,
+                                                    Animation      *animation);
 /* Finalization. */
 static void        update_ui_sensitivity     (AnimationDialog  *dialog);
 
@@ -1361,6 +1365,28 @@ animation_dialog_set_animation (AnimationDialog *dialog,
 
   /* Set the playback */
   animation_playback_set_animation (priv->playback, animation);
+
+  if (gtk_widget_get_realized (GTK_WIDGET (dialog)))
+    animation_load (priv->animation);
+  else
+    /* Wait for the dialog to be realized because cache loading can
+       take time and it is friendlier to have a visible GUI first. */
+    g_signal_connect_after (dialog, "expose-event",
+                              G_CALLBACK (on_dialog_expose),
+                              priv->animation);
+}
+
+static gboolean
+on_dialog_expose (GtkWidget *widget,
+                  GdkEvent  *event,
+                  Animation *animation)
+{
+  g_signal_handlers_disconnect_by_func (widget,
+                                        G_CALLBACK (on_dialog_expose),
+                                        animation);
+  animation_load (animation);
+
+  return FALSE;
 }
 
 /* Update the tool sensitivity for playing, depending on the number of frames. */


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