[gimp/wip/animation: 80/197] plug-ins: split the playback code out of the Animation class.



commit 7833558f587b910261286c16dbb5f15135eedf89
Author: Jehan <jehan girinstud io>
Date:   Fri Nov 4 02:16:38 2016 +0100

    plug-ins: split the playback code out of the Animation class.

 plug-ins/animation-play/Makefile.am                |    2 +
 .../animation-play/core/animation-celanimation.c   |   13 +-
 plug-ins/animation-play/core/animation-playback.c  |  628 ++++++++++++++++++++
 plug-ins/animation-play/core/animation-playback.h  |   86 +++
 plug-ins/animation-play/core/animation.c           |  512 +++--------------
 plug-ins/animation-play/core/animation.h           |   46 +-
 plug-ins/animation-play/core/animationanimatic.c   |  109 +---
 plug-ins/animation-play/core/animationanimatic.h   |   33 +-
 plug-ins/animation-play/widgets/animation-dialog.c |  322 +++++-----
 .../animation-play/widgets/animation-storyboard.c  |   66 ++-
 .../animation-play/widgets/animation-storyboard.h  |    3 +-
 plug-ins/animation-play/widgets/animation-xsheet.c |   49 +-
 plug-ins/animation-play/widgets/animation-xsheet.h |    1 +
 13 files changed, 1075 insertions(+), 795 deletions(-)
---
diff --git a/plug-ins/animation-play/Makefile.am b/plug-ins/animation-play/Makefile.am
index 4774dd7..ac66c6e 100644
--- a/plug-ins/animation-play/Makefile.am
+++ b/plug-ins/animation-play/Makefile.am
@@ -49,6 +49,8 @@ animation_play_SOURCES = \
        core/animationanimatic.c                \
        core/animation-celanimation.h   \
        core/animation-celanimation.c   \
+       core/animation-playback.h               \
+       core/animation-playback.c               \
        widgets/animation-dialog.h              \
        widgets/animation-dialog.c              \
        widgets/animation-storyboard.h  \
diff --git a/plug-ins/animation-play/core/animation-celanimation.c 
b/plug-ins/animation-play/core/animation-celanimation.c
index 703605c..d049c2d 100644
--- a/plug-ins/animation-play/core/animation-celanimation.c
+++ b/plug-ins/animation-play/core/animation-celanimation.c
@@ -220,16 +220,6 @@ animation_cel_animation_set_layers (AnimationCelAnimation *animation,
           frame_layer->data = 0;
         }
       animation_cel_animation_cache (animation, position);
-      if (animation_get_position (ANIMATION (animation)) == position)
-        {
-          GeglBuffer *buffer;
-
-          buffer = animation_get_frame (ANIMATION (animation), position);
-          g_signal_emit_by_name (animation, "render",
-                                 position, buffer, TRUE);
-          if (buffer)
-            g_object_unref (buffer);
-        }
     }
 }
 
@@ -1088,8 +1078,9 @@ 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 gboolean
 animation_cel_animation_cache_cmp (Cache *cache1,
diff --git a/plug-ins/animation-play/core/animation-playback.c 
b/plug-ins/animation-play/core/animation-playback.c
new file mode 100644
index 0000000..e2e6321
--- /dev/null
+++ b/plug-ins/animation-play/core/animation-playback.c
@@ -0,0 +1,628 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * animation-playback.c
+ * Copyright (C) 2016 Jehan <jehan gimp org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include <libgimp/gimp.h>
+
+#include "animation.h"
+#include "animation-playback.h"
+
+enum
+{
+  START,
+  STOP,
+  RANGE,
+  RENDER,
+  LOW_FRAMERATE,
+  LAST_SIGNAL
+};
+
+enum
+{
+  PROP_0,
+  PROP_ANIMATION
+};
+
+struct _AnimationPlaybackPrivate
+{
+  Animation   *animation;
+
+  /* State of the currently loaded playback. */
+  gint         position;
+  /* Playback can be a subset of frames. */
+  guint        start;
+  guint        stop;
+
+  guint        timer;
+  gint64       start_time;
+  gint64       frames_played;
+};
+
+
+#define ANIMATION_PLAYBACK_GET_PRIVATE(playback) \
+        G_TYPE_INSTANCE_GET_PRIVATE (playback, \
+                                     ANIMATION_PLAYBACK_TYPE_ANIMATION_PLAYBACK, \
+                                     AnimationPlaybackPrivate)
+
+static void       animation_playback_finalize               (GObject      *object);
+static void       animation_playback_set_property           (GObject           *object,
+                                                             guint              property_id,
+                                                             const GValue      *value,
+                                                             GParamSpec        *pspec);
+static void       animation_playback_get_property           (GObject           *object,
+                                                             guint              property_id,
+                                                             GValue            *value,
+                                                             GParamSpec        *pspec);
+
+static void       on_cache_invalidated                      (Animation         *animation,
+                                                             gint               position,
+                                                             gint               length,
+                                                             AnimationPlayback *playback);
+
+/* Timer callback for playback. */
+static gboolean   animation_playback_advance_frame_callback (AnimationPlayback *playback);
+
+
+G_DEFINE_TYPE (AnimationPlayback, animation_playback, G_TYPE_OBJECT)
+
+#define parent_class animation_playback_parent_class
+
+static guint animation_playback_signals[LAST_SIGNAL] = { 0 };
+
+static void
+animation_playback_class_init (AnimationPlaybackClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  /**
+   * AnimationPlayback::start:
+   * @playback: the #AnimationPlayback.
+   *
+   * The @playback starts to play.
+   */
+  animation_playback_signals[START] =
+    g_signal_new ("start",
+                  G_TYPE_FROM_CLASS (klass),
+                  G_SIGNAL_RUN_FIRST,
+                  G_STRUCT_OFFSET (AnimationPlaybackClass, start),
+                  NULL, NULL,
+                  g_cclosure_marshal_VOID__VOID,
+                  G_TYPE_NONE,
+                  0);
+  /**
+   * AnimationPlayback::stop:
+   * @playback: the #AnimationPlayback.
+   *
+   * The @playback stops playing.
+   */
+  animation_playback_signals[STOP] =
+    g_signal_new ("stop",
+                  G_TYPE_FROM_CLASS (klass),
+                  G_SIGNAL_RUN_FIRST,
+                  G_STRUCT_OFFSET (AnimationPlaybackClass, stop),
+                  NULL, NULL,
+                  g_cclosure_marshal_VOID__VOID,
+                  G_TYPE_NONE,
+                  0);
+  /**
+   * AnimationPlayback::range:
+   * @playback: the #AnimationPlayback.
+   * @start: the playback start frame.
+   * @stop: the playback last frame.
+   *
+   * The ::range signal is emitted when the playback range is
+   * updated.
+   */
+  animation_playback_signals[RANGE] =
+    g_signal_new ("range",
+                  G_TYPE_FROM_CLASS (klass),
+                  G_SIGNAL_RUN_FIRST,
+                  G_STRUCT_OFFSET (AnimationPlaybackClass, range),
+                  NULL, NULL,
+                  NULL,
+                  G_TYPE_NONE,
+                  2,
+                  G_TYPE_INT,
+                  G_TYPE_INT);
+  /**
+   * AnimationPlayback::render:
+   * @playback: the #AnimationPlayback.
+   * @position: current position to be rendered.
+   * @buffer: the #GeglBuffer for the frame at @position.
+   * @must_draw_null: meaning of a %NULL @buffer.
+   * %TRUE means we have to draw an empty frame.
+   * %FALSE means the new frame is same as the current frame.
+   *
+   * Sends a request for render to the GUI.
+   */
+  animation_playback_signals[RENDER] =
+    g_signal_new ("render",
+                  G_TYPE_FROM_CLASS (klass),
+                  G_SIGNAL_RUN_FIRST,
+                  G_STRUCT_OFFSET (AnimationPlaybackClass, render),
+                  NULL, NULL,
+                  NULL,
+                  G_TYPE_NONE,
+                  3,
+                  G_TYPE_INT,
+                  GEGL_TYPE_BUFFER,
+                  G_TYPE_BOOLEAN);
+  /**
+   * AnimationPlayback::low-framerate:
+   * @playback: the #AnimationPlayback.
+   * @actual_fps: the current playback framerate in fps.
+   *
+   * The ::low-framerate signal is emitted when the playback framerate
+   * is lower than expected. It is also emitted once when the framerate
+   * comes back to acceptable rate.
+   */
+  animation_playback_signals[LOW_FRAMERATE] =
+    g_signal_new ("low-framerate",
+                  G_TYPE_FROM_CLASS (klass),
+                  G_SIGNAL_RUN_FIRST,
+                  G_STRUCT_OFFSET (AnimationPlaybackClass, low_framerate),
+                  NULL, NULL,
+                  g_cclosure_marshal_VOID__DOUBLE,
+                  G_TYPE_NONE,
+                  1,
+                  G_TYPE_DOUBLE);
+
+  object_class->finalize     = animation_playback_finalize;
+  object_class->set_property = animation_playback_set_property;
+  object_class->get_property = animation_playback_get_property;
+
+  /**
+   * AnimationPlayback:animation:
+   *
+   * The associated #Animation.
+   */
+  g_object_class_install_property (object_class, PROP_ANIMATION,
+                                   g_param_spec_object ("animation",
+                                                        NULL, NULL,
+                                                        ANIMATION_TYPE_ANIMATION,
+                                                        G_PARAM_READWRITE));
+
+  g_type_class_add_private (klass, sizeof (AnimationPlaybackPrivate));
+}
+
+static void
+animation_playback_init (AnimationPlayback *playback)
+{
+  playback->priv = G_TYPE_INSTANCE_GET_PRIVATE (playback,
+                                                ANIMATION_TYPE_PLAYBACK,
+                                                AnimationPlaybackPrivate);
+}
+
+/************ Public Functions ****************/
+
+AnimationPlayback *
+animation_playback_new (void)
+{
+  AnimationPlayback *playback;
+
+  playback = g_object_new (ANIMATION_TYPE_PLAYBACK,
+                           NULL);
+
+  return playback;
+}
+
+void
+animation_playback_set_animation (AnimationPlayback *playback,
+                                  Animation         *animation)
+{
+  g_object_set (playback,
+                "animation", animation,
+                NULL);
+}
+
+Animation *
+animation_playback_get_animation (AnimationPlayback *playback)
+{
+  return playback->priv->animation;
+}
+
+gint
+animation_playback_get_position (AnimationPlayback *playback)
+{
+  return playback->priv->position;
+}
+
+gboolean
+animation_playback_is_playing (AnimationPlayback *playback)
+{
+  return (playback->priv->timer != 0);
+}
+
+void
+animation_playback_play (AnimationPlayback *playback)
+{
+  gint duration;
+
+  duration = (gint) (1000.0 /
+                     animation_get_framerate (playback->priv->animation));
+
+  if (playback->priv->timer)
+    {
+      /* It means we are already playing, so we should not need to play
+       * again.
+       * Still be liberal and simply remove the timer before creating a
+       * new one. */
+      g_source_remove (playback->priv->timer);
+      g_signal_emit (playback, animation_playback_signals[STOP], 0);
+    }
+
+  playback->priv->start_time = g_get_monotonic_time ();
+  playback->priv->frames_played = 1;
+
+  playback->priv->timer = g_timeout_add ((guint) duration,
+                                         (GSourceFunc) animation_playback_advance_frame_callback,
+                                         playback);
+  g_signal_emit (playback, animation_playback_signals[START], 0);
+}
+
+void
+animation_playback_stop (AnimationPlayback *playback)
+{
+  if (playback->priv->timer)
+    {
+      /* Stop playing by removing any playback timer. */
+      g_source_remove (playback->priv->timer);
+      playback->priv->timer = 0;
+      g_signal_emit (playback, animation_playback_signals[STOP], 0);
+    }
+}
+
+void
+animation_playback_next (AnimationPlayback *playback)
+{
+  Animation  *animation    = playback->priv->animation;
+  GeglBuffer *buffer       = NULL;
+  gint        previous_pos = playback->priv->position;
+  gboolean    identical;
+
+  if (! playback->priv->animation)
+    return;
+
+  playback->priv->position = playback->priv->start +
+                             ((playback->priv->position - playback->priv->start + 1) %
+                              (playback->priv->stop - playback->priv->start + 1));
+
+  identical = ANIMATION_GET_CLASS (animation)->same (animation,
+                                                     previous_pos,
+                                                     playback->priv->position);
+  if (! identical)
+    {
+      buffer = animation_get_frame (animation, playback->priv->position);
+    }
+  g_signal_emit (playback, animation_playback_signals[RENDER], 0,
+                 playback->priv->position, buffer, ! identical);
+  if (buffer != NULL)
+    {
+      g_object_unref (buffer);
+    }
+}
+
+void
+animation_playback_prev (AnimationPlayback *playback)
+{
+  Animation  *animation = playback->priv->animation;
+  GeglBuffer *buffer    = NULL;
+  gint        prev_pos  = playback->priv->position;
+  gboolean    identical;
+
+  if (! playback->priv->animation)
+    return;
+
+  if (playback->priv->position == playback->priv->start)
+    {
+      playback->priv->position = animation_playback_get_stop (playback);
+    }
+  else
+    {
+      --playback->priv->position;
+    }
+
+  identical = ANIMATION_GET_CLASS (animation)->same (animation,
+                                                     prev_pos,
+                                                     playback->priv->position);
+  if (! identical)
+    {
+      buffer = animation_get_frame (animation, playback->priv->position);
+    }
+  g_signal_emit (playback, animation_playback_signals[RENDER], 0,
+                 playback->priv->position, buffer, ! identical);
+  if (buffer)
+    g_object_unref (buffer);
+}
+
+void
+animation_playback_jump (AnimationPlayback *playback,
+                         gint               index)
+{
+  Animation  *animation = playback->priv->animation;
+  GeglBuffer *buffer    = NULL;
+  gint        prev_pos  = playback->priv->position;
+  gboolean    identical;
+
+  if (! playback->priv->animation)
+    return;
+
+  if (index < playback->priv->start ||
+      index > playback->priv->stop)
+    return;
+  else
+    playback->priv->position = index;
+
+  identical = ANIMATION_GET_CLASS (animation)->same (animation,
+                                                     prev_pos,
+                                                     playback->priv->position);
+  if (! identical)
+    {
+      buffer = animation_get_frame (animation, playback->priv->position);
+    }
+  g_signal_emit (playback, animation_playback_signals[RENDER], 0,
+                 playback->priv->position, buffer, ! identical);
+  if (buffer)
+    g_object_unref (buffer);
+}
+
+void
+animation_playback_set_start (AnimationPlayback *playback,
+                              gint               index)
+{
+  gint duration;
+
+  if (! playback->priv->animation)
+    return;
+
+  duration = animation_get_duration (playback->priv->animation);
+
+  if (index < 0 ||
+      index >= duration)
+    {
+      playback->priv->start = 0;
+    }
+  else
+    {
+      playback->priv->start = index;
+    }
+  if (playback->priv->stop < playback->priv->start)
+    {
+      playback->priv->stop = duration - 1;
+    }
+
+  g_signal_emit (playback, animation_playback_signals[RANGE], 0,
+                 playback->priv->start, playback->priv->stop);
+
+  if (playback->priv->position < playback->priv->start ||
+      playback->priv->position > playback->priv->stop)
+    {
+      animation_playback_jump (playback, playback->priv->start);
+    }
+}
+
+gint
+animation_playback_get_start (AnimationPlayback *playback)
+{
+  return playback->priv->start;
+}
+
+void
+animation_playback_set_stop (AnimationPlayback *playback,
+                             gint               index)
+{
+  gint duration;
+
+  if (! playback->priv->animation)
+    return;
+
+  duration = animation_get_duration (playback->priv->animation);
+
+  if (index < 0 ||
+      index >= duration)
+    {
+      playback->priv->stop = duration - 1;
+    }
+  else
+    {
+      playback->priv->stop = index;
+    }
+  if (playback->priv->stop < playback->priv->start)
+    {
+      playback->priv->start = 0;
+    }
+  g_signal_emit (playback, animation_playback_signals[RANGE], 0,
+                 playback->priv->start, playback->priv->stop);
+
+  if (playback->priv->position < playback->priv->start ||
+      playback->priv->position > playback->priv->stop)
+    {
+      animation_playback_jump (playback, playback->priv->start);
+    }
+}
+
+gint
+animation_playback_get_stop (AnimationPlayback *playback)
+{
+  return playback->priv->stop;
+}
+
+/************ Private Functions ****************/
+
+static void
+animation_playback_finalize (GObject      *object)
+{
+  AnimationPlayback *playback = ANIMATION_PLAYBACK (object);
+
+  if (playback->priv->animation)
+    g_object_unref (playback->priv->animation);
+
+  G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static void
+animation_playback_set_property (GObject      *object,
+                                 guint         property_id,
+                                 const GValue *value,
+                                 GParamSpec   *pspec)
+{
+  AnimationPlayback *playback = ANIMATION_PLAYBACK (object);
+
+  switch (property_id)
+    {
+    case PROP_ANIMATION:
+        {
+          Animation  *animation;
+          GeglBuffer *buffer;
+
+          if (playback->priv->animation)
+            g_object_unref (playback->priv->animation);
+
+          animation = g_value_dup_object (value);
+          playback->priv->animation = animation;
+
+          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;
+          playback->priv->stop  = animation_get_duration (animation) - 1;
+
+          g_signal_emit_by_name (animation, "loaded");
+          g_signal_connect (animation, "cache-invalidated",
+                            G_CALLBACK (on_cache_invalidated), playback);
+
+          /* Once loaded, let's ask render. */
+          buffer = animation_get_frame (animation, playback->priv->position);
+          g_signal_emit (playback, animation_playback_signals[RENDER], 0,
+                         playback->priv->position, buffer, TRUE);
+
+          if (buffer)
+            g_object_unref (buffer);
+        }
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+      break;
+    }
+}
+
+static void
+animation_playback_get_property (GObject    *object,
+                                 guint       property_id,
+                                 GValue     *value,
+                                 GParamSpec *pspec)
+{
+  AnimationPlayback *playback = ANIMATION_PLAYBACK (object);
+
+  switch (property_id)
+    {
+    case PROP_ANIMATION:
+      g_value_set_object (value, playback->priv->animation);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+      break;
+    }
+}
+
+static void
+on_cache_invalidated (Animation         *animation,
+                      gint               position,
+                      gint               length,
+                      AnimationPlayback *playback)
+{
+  gint cur_position;
+
+  cur_position = animation_playback_get_position (playback);
+
+  if (cur_position >= position &&
+      cur_position < position + length)
+    {
+      GeglBuffer *buffer;
+
+      buffer = animation_get_frame (animation, cur_position);
+      g_signal_emit_by_name (playback, "render",
+                             cur_position, buffer, TRUE);
+      if (buffer)
+        {
+          g_object_unref (buffer);
+        }
+    }
+}
+
+static gboolean
+animation_playback_advance_frame_callback (AnimationPlayback *playback)
+{
+  gdouble         framerate;
+  gint64          duration;
+  gint64          duration_since_start;
+  static gboolean prev_low_framerate = FALSE;
+
+  framerate = animation_get_framerate (playback->priv->animation);
+
+  animation_playback_next (playback);
+  duration = (gint) (1000.0 / framerate);
+
+  duration_since_start = (g_get_monotonic_time () - playback->priv->start_time) / 1000;
+  duration = duration - (duration_since_start - playback->priv->frames_played * duration);
+
+  if (duration < 1)
+    {
+      if (prev_low_framerate)
+        {
+          /* Let's only warn the user for several subsequent slow frames. */
+          gdouble real_framerate;
+
+          real_framerate = (gdouble) playback->priv->frames_played * 1000.0 / duration_since_start;
+
+          if (real_framerate < framerate)
+            g_signal_emit (playback, animation_playback_signals[LOW_FRAMERATE], 0,
+                           real_framerate);
+        }
+      duration = 1;
+      prev_low_framerate = TRUE;
+    }
+  else
+    {
+      if (prev_low_framerate)
+        {
+          /* Let's reset framerate warning. */
+          g_signal_emit (playback, animation_playback_signals[LOW_FRAMERATE], 0,
+                         framerate);
+        }
+      prev_low_framerate = FALSE;
+    }
+  playback->priv->frames_played++;
+
+  playback->priv->timer = g_timeout_add ((guint) duration,
+                                        (GSourceFunc) animation_playback_advance_frame_callback,
+                                        (AnimationPlayback *) playback);
+
+  return G_SOURCE_REMOVE;
+}
diff --git a/plug-ins/animation-play/core/animation-playback.h 
b/plug-ins/animation-play/core/animation-playback.h
new file mode 100644
index 0000000..e7ba300
--- /dev/null
+++ b/plug-ins/animation-play/core/animation-playback.h
@@ -0,0 +1,86 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * animation_playback.h
+ * Copyright (C) 2016 Jehan <jehan gimp org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __ANIMATION_PLAYBACK_H__
+#define __ANIMATION_PLAYBACK_H__
+
+#define ANIMATION_TYPE_PLAYBACK            (animation_playback_get_type ())
+#define ANIMATION_PLAYBACK(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), ANIMATION_TYPE_PLAYBACK, 
AnimationPlayback))
+#define ANIMATION_PLAYBACK_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), ANIMATION_TYPE_PLAYBACK, 
AnimationPlaybackClass))
+#define ANIMATION_IS_PLAYBACK(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), ANIMATION_TYPE_PLAYBACK))
+#define ANIMATION_IS_PLAYBACK_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), ANIMATION_TYPE_PLAYBACK))
+#define ANIMATION_PLAYBACK_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), ANIMATION_TYPE_PLAYBACK, 
AnimationPlaybackClass))
+
+typedef struct _AnimationPlayback        AnimationPlayback;
+typedef struct _AnimationPlaybackClass   AnimationPlaybackClass;
+typedef struct _AnimationPlaybackPrivate AnimationPlaybackPrivate;
+
+struct _AnimationPlayback
+{
+  GObject                   parent_instance;
+
+  AnimationPlaybackPrivate *priv;
+};
+
+struct _AnimationPlaybackClass
+{
+  GObjectClass               parent_class;
+
+  /* Signals */
+  void       (*start)            (AnimationPlayback *playback);
+  void       (*stop)             (AnimationPlayback *playback);
+  void       (*range)            (AnimationPlayback *playback,
+                                  gint               start,
+                                  gint               stop);
+  void       (*render)           (AnimationPlayback *playback,
+                                  gint               position,
+                                  GeglBuffer        *buffer,
+                                  gboolean           must_draw_null);
+  void       (*low_framerate)    (AnimationPlayback *playback,
+                                  gdouble            actual_fps);
+};
+
+GType         animation_playback_get_type (void);
+
+AnimationPlayback * animation_playback_new     (void);
+
+void          animation_playback_set_animation (AnimationPlayback   *playback,
+                                                Animation           *animation);
+Animation   * animation_playback_get_animation (AnimationPlayback   *playback);
+
+gint          animation_playback_get_position  (AnimationPlayback   *playback);
+
+gboolean      animation_playback_is_playing    (AnimationPlayback   *playback);
+void          animation_playback_play          (AnimationPlayback   *playback);
+void          animation_playback_stop          (AnimationPlayback   *playback);
+void          animation_playback_next          (AnimationPlayback   *playback);
+void          animation_playback_prev          (AnimationPlayback   *playback);
+void          animation_playback_jump          (AnimationPlayback   *playback,
+                                                gint                 index);
+
+void          animation_playback_set_start     (AnimationPlayback   *playback,
+                                                gint                 index);
+gint          animation_playback_get_start     (AnimationPlayback   *playback);
+
+void          animation_playback_set_stop      (AnimationPlayback   *playback,
+                                                gint                 index);
+gint          animation_playback_get_stop      (AnimationPlayback   *playback);
+
+#endif  /*  __ANIMATION_PLAYBACK_H__  */
diff --git a/plug-ins/animation-play/core/animation.c b/plug-ins/animation-play/core/animation.c
index ad7a8cc..0f6c8f1 100644
--- a/plug-ins/animation-play/core/animation.c
+++ b/plug-ins/animation-play/core/animation.c
@@ -42,16 +42,12 @@ CachedSettings;
 
 enum
 {
-  LOADING_START,
   LOADING,
   LOADED,
-  START,
-  STOP,
+  CACHE_INVALIDATED,
+  DURATION_CHANGED,
   FRAMERATE_CHANGED,
-  PLAYBACK_RANGE,
-  RENDER,
-  LOW_FRAMERATE,
-  PROXY,
+  PROXY_CHANGED,
   LAST_SIGNAL
 };
 
@@ -65,26 +61,15 @@ typedef struct _AnimationPrivate AnimationPrivate;
 
 struct _AnimationPrivate
 {
-  gint32       image_id;
-  gchar       *xml;
+  gint32    image_id;
+  gchar    *xml;
 
-  gdouble      framerate;
-
-  guint        playback_timer;
-
-  /* State of the currently loaded animation. */
-  gint         position;
-  /* Playback can be a subset of frames. */
-  guint        playback_start;
-  guint        playback_stop;
+  gdouble   framerate;
 
   /* Proxy settings generates a reload. */
-  gdouble      proxy_ratio;
+  gdouble   proxy_ratio;
 
-  gboolean     loaded;
-
-  gint64       playback_start_time;
-  gint64       frames_played;
+  gboolean  loaded;
 };
 
 
@@ -93,6 +78,7 @@ struct _AnimationPrivate
                                      ANIMATION_TYPE_ANIMATION, \
                                      AnimationPrivate)
 
+static void       animation_finalize               (GObject      *object);
 static void       animation_set_property           (GObject      *object,
                                                     guint         property_id,
                                                     const GValue *value,
@@ -103,12 +89,9 @@ static void       animation_get_property           (GObject      *object,
                                                     GParamSpec   *pspec);
 
 /* Base implementation of virtual methods. */
-static gboolean   animation_real_same               (Animation *animation,
-                                                     gint       prev_pos,
-                                                     gint       next_pos);
-
-/* Timer callback for playback. */
-static gboolean   animation_advance_frame_callback  (Animation *animation);
+static gboolean   animation_real_same              (Animation *animation,
+                                                    gint       prev_pos,
+                                                    gint       next_pos);
 
 G_DEFINE_TYPE (Animation, animation, G_TYPE_OBJECT)
 
@@ -122,37 +105,22 @@ animation_class_init (AnimationClass *klass)
   GObjectClass *object_class = G_OBJECT_CLASS (klass);
 
   /**
-   * Animation::loading-start:
-   * @animation: the animation loading.
-   *
-   * The ::loading-start signal is emitted when loading starts.
-   * GUI widgets depending on a consistent state of @animation should
-   * become unresponsive.
-   */
-  animation_signals[LOADING_START] =
-    g_signal_new ("loading-start",
-                  G_TYPE_FROM_CLASS (klass),
-                  G_SIGNAL_RUN_FIRST,
-                  0,
-                  NULL, NULL,
-                  NULL,
-                  G_TYPE_NONE,
-                  0);
-  /**
    * Animation::loading:
    * @animation: the animation loading.
    * @ratio: fraction loaded [0-1].
    *
-   * The ::loading signal must be emitted by subclass of Animation.
+   * The ::loading signal must be emitted by a subclass of Animation.
    * It can be used by a GUI to display a progress bar during loading.
+   * GUI widgets depending on a consistent state of @animation should
+   * become unresponsive.
    */
   animation_signals[LOADING] =
     g_signal_new ("loading",
                   G_TYPE_FROM_CLASS (klass),
                   G_SIGNAL_RUN_FIRST,
-                  0,
+                  G_STRUCT_OFFSET (AnimationClass, loading),
                   NULL, NULL,
-                  NULL,
+                  g_cclosure_marshal_VOID__DOUBLE,
                   G_TYPE_NONE,
                   1,
                   G_TYPE_DOUBLE);
@@ -160,8 +128,6 @@ animation_class_init (AnimationClass *klass)
    * Animation::loaded:
    * @animation: the animation loading.
    * @duration: number of frames.
-   * @playback_start: the playback start frame.
-   * @playback_stop: the playback last frame.
    * @width: display width in pixels.
    * @height: display height in pixels.
    *
@@ -173,125 +139,64 @@ animation_class_init (AnimationClass *klass)
     g_signal_new ("loaded",
                   G_TYPE_FROM_CLASS (klass),
                   G_SIGNAL_RUN_FIRST,
-                  0,
+                  G_STRUCT_OFFSET (AnimationClass, loaded),
                   NULL, NULL,
-                  NULL,
+                  g_cclosure_marshal_VOID__VOID,
                   G_TYPE_NONE,
-                  5,
-                  G_TYPE_INT,
-                  G_TYPE_INT,
-                  G_TYPE_INT,
-                  G_TYPE_INT,
-                  G_TYPE_INT);
+                  0);
   /**
-   * Animation::render:
+   * Animation::cache-invalidated:
    * @animation: the animation.
-   * @position: current position to be rendered.
-   * @buffer: the #GeglBuffer for the frame at @position.
-   * @must_draw_null: meaning of a %NULL @buffer.
-   * %TRUE means we have to draw an empty frame.
-   * %FALSE means the new frame is same as the current frame.
+   * @position: the first frame position whose cache is invalid.
+   * @length: the number of invalidated frames from @position.
    *
-   * Sends a request for render to the GUI.
+   * The ::cache-invalidated signal must be emitted when the contents
+   * of one or more successive frames change.
    */
-  animation_signals[RENDER] =
-    g_signal_new ("render",
+  animation_signals[CACHE_INVALIDATED] =
+    g_signal_new ("cache-invalidated",
                   G_TYPE_FROM_CLASS (klass),
                   G_SIGNAL_RUN_FIRST,
-                  0,
+                  G_STRUCT_OFFSET (AnimationClass, cache_invalidated),
                   NULL, NULL,
                   NULL,
                   G_TYPE_NONE,
-                  3,
+                  2,
                   G_TYPE_INT,
-                  GEGL_TYPE_BUFFER,
-                  G_TYPE_BOOLEAN);
-  /**
-   * Animation::start:
-   * @animation: the animation.
-   *
-   * The @animation starts to play.
-   */
-  animation_signals[START] =
-    g_signal_new ("start",
-                  G_TYPE_FROM_CLASS (klass),
-                  G_SIGNAL_RUN_FIRST,
-                  0,
-                  NULL, NULL,
-                  NULL,
-                  G_TYPE_NONE,
-                  0);
+                  G_TYPE_INT);
   /**
-   * Animation::stops:
+   * Animation::duration:
    * @animation: the animation.
+   * @duration: the new duration of @animation in number of frames.
    *
-   * The @animation stops playing.
+   * The ::playback-range signal must be emitted when the duration of
+   * @animation changes.
    */
-  animation_signals[STOP] =
-    g_signal_new ("stop",
+  animation_signals[DURATION_CHANGED] =
+    g_signal_new ("duration-changed",
                   G_TYPE_FROM_CLASS (klass),
                   G_SIGNAL_RUN_FIRST,
-                  0,
+                  G_STRUCT_OFFSET (AnimationClass, duration_changed),
                   NULL, NULL,
-                  NULL,
+                  g_cclosure_marshal_VOID__INT,
                   G_TYPE_NONE,
-                  0);
+                  1,
+                  G_TYPE_INT);
   /**
    * Animation::framerate-changed:
    * @animation: the animation.
-   * @framerate: the new framerate in frames per second.
+   * @framerate: the new framerate of @animation in frames per second.
    *
-   * The ::framerate-changed signal is emitted when framerate has
-   * changed.
+   * The ::playback-range signal is emitted when the framerate of
+   * @animation changes.
    */
   animation_signals[FRAMERATE_CHANGED] =
     g_signal_new ("framerate-changed",
                   G_TYPE_FROM_CLASS (klass),
                   G_SIGNAL_RUN_FIRST,
-                  0,
-                  NULL, NULL,
-                  NULL,
-                  G_TYPE_NONE,
-                  1,
-                  G_TYPE_DOUBLE);
-  /**
-   * Animation::playback-range:
-   * @animation: the animation.
-   * @playback_start: the playback start frame.
-   * @playback_stop: the playback last frame.
-   * @playback_duration: the playback duration (in frames).
-   *
-   * The ::playback-range signal is emitted when the playback range is
-   * updated.
-   */
-  animation_signals[PLAYBACK_RANGE] =
-    g_signal_new ("playback-range",
-                  G_TYPE_FROM_CLASS (klass),
-                  G_SIGNAL_RUN_FIRST,
-                  0,
-                  NULL, NULL,
-                  NULL,
-                  G_TYPE_NONE,
-                  3,
-                  G_TYPE_INT,
-                  G_TYPE_INT,
-                  G_TYPE_INT);
-  /**
-   * Animation::low-framerate:
-   * @animation: the animation.
-   * @actual_fps: the current playback framerate in fps.
-   *
-   * The ::low-framerate signal is emitted when the playback framerate
-   * is lower than expected. It is also emitted once when the framerate
-   * comes back to acceptable rate.
-   */
-  animation_signals[LOW_FRAMERATE] =
-    g_signal_new ("low-framerate-playback",
-                  G_TYPE_FROM_CLASS (klass),
-                  G_SIGNAL_RUN_FIRST,
-                  0,
+                  G_STRUCT_OFFSET (AnimationClass, framerate_changed),
                   NULL, NULL,
-                  NULL,
+                  g_cclosure_marshal_VOID__DOUBLE,
                   G_TYPE_NONE,
                   1,
                   G_TYPE_DOUBLE);
@@ -302,17 +207,18 @@ animation_class_init (AnimationClass *klass)
    *
    * The ::proxy signal is emitted to announce a change of proxy size.
    */
-  animation_signals[PROXY] =
-    g_signal_new ("proxy",
+  animation_signals[PROXY_CHANGED] =
+    g_signal_new ("proxy-changed",
                   G_TYPE_FROM_CLASS (klass),
                   G_SIGNAL_RUN_FIRST,
-                  0,
+                  G_STRUCT_OFFSET (AnimationClass, proxy),
                   NULL, NULL,
-                  NULL,
+                  g_cclosure_marshal_VOID__DOUBLE,
                   G_TYPE_NONE,
                   1,
                   G_TYPE_DOUBLE);
 
+  object_class->finalize     = animation_finalize;
   object_class->set_property = animation_set_property;
   object_class->get_property = animation_get_property;
 
@@ -345,7 +251,7 @@ animation_init (Animation *animation)
   gimp_get_data (PLUG_IN_PROC, &settings);
 
   /* Acceptable settings for the default. */
-  priv->framerate   = settings.framerate; /* fps */
+  priv->framerate   = settings.framerate;
   priv->proxy_ratio = 1.0;
 }
 
@@ -370,7 +276,7 @@ animation_new (gint32       image_id,
 }
 
 gint32
-animation_get_image_id (Animation   *animation)
+animation_get_image_id (Animation *animation)
 {
   AnimationPrivate *priv = ANIMATION_GET_PRIVATE (animation);
 
@@ -381,47 +287,25 @@ void
 animation_load (Animation *animation)
 {
   AnimationPrivate *priv = ANIMATION_GET_PRIVATE (animation);
-  GeglBuffer       *buffer;
-  gint              width, height;
-  gint              duration;
 
   priv->loaded = FALSE;
-  g_signal_emit (animation, animation_signals[LOADING_START], 0);
+  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;
 
-  duration = ANIMATION_GET_CLASS (animation)->get_duration (animation);
-  priv->position = 0;
-
-  /* Default playback is the full range of frames. */
-  priv->playback_start = 0;
-  priv->playback_stop  = duration - 1;
-
   priv->loaded = TRUE;
 
-  animation_get_size (animation, &width, &height);
-  g_signal_emit (animation, animation_signals[LOADED], 0,
-                 duration,
-                 priv->playback_start,
-                 priv->playback_stop,
-                 width,
-                 height);
-
-  /* Once loaded, let's ask render. */
-  buffer = animation_get_frame (animation, priv->position);
-  g_signal_emit (animation, animation_signals[RENDER], 0,
-                 priv->position, buffer, TRUE);
-
-  if (buffer)
-    g_object_unref (buffer);
+  /* XXX */
+  /*g_signal_emit (animation, animation_signals[LOADED], 0);*/
 }
 
 void
@@ -502,14 +386,6 @@ animation_save_to_parasite (Animation *animation)
 }
 
 gint
-animation_get_position (Animation *animation)
-{
-  AnimationPrivate *priv = ANIMATION_GET_PRIVATE (animation);
-
-  return priv->position;
-}
-
-gint
 animation_get_duration (Animation *animation)
 {
   return ANIMATION_GET_CLASS (animation)->get_duration (animation);
@@ -526,7 +402,7 @@ animation_set_proxy (Animation *animation,
   if (priv->proxy_ratio != ratio)
     {
       priv->proxy_ratio = ratio;
-      g_signal_emit (animation, animation_signals[PROXY], 0, ratio);
+      g_signal_emit (animation, animation_signals[PROXY_CHANGED], 0, ratio);
 
       /* A proxy change implies a reload. */
       animation_load (animation);
@@ -569,222 +445,6 @@ animation_get_frame (Animation *animation,
   return ANIMATION_GET_CLASS (animation)->get_frame (animation, pos);
 }
 
-gboolean
-animation_is_playing (Animation *animation)
-{
-  AnimationPrivate *priv = ANIMATION_GET_PRIVATE (animation);
-
-  return (priv->playback_timer != 0);
-}
-
-void
-animation_play (Animation *animation)
-{
-  AnimationPrivate *priv = ANIMATION_GET_PRIVATE (animation);
-  gint              duration;
-
-  duration = (gint) (1000.0 / animation_get_framerate (animation));
-
-  if (priv->playback_timer)
-    {
-      /* It means we are already playing, so we should not need to play
-       * again.
-       * Still be liberal and simply remove the timer before creating a
-       * new one. */
-      g_source_remove (priv->playback_timer);
-    }
-
-  priv->playback_start_time = g_get_monotonic_time ();
-  priv->frames_played = 1;
-
-  priv->playback_timer = g_timeout_add ((guint) duration,
-                                        (GSourceFunc) animation_advance_frame_callback,
-                                        animation);
-  g_signal_emit (animation, animation_signals[START], 0);
-}
-
-void
-animation_stop (Animation *animation)
-{
-  AnimationPrivate *priv = ANIMATION_GET_PRIVATE (animation);
-
-  if (priv->playback_timer)
-    {
-      /* Stop playing by removing any playback timer. */
-      g_source_remove (priv->playback_timer);
-      priv->playback_timer = 0;
-      g_signal_emit (animation, animation_signals[STOP], 0);
-    }
-}
-
-void
-animation_next (Animation *animation)
-{
-  AnimationPrivate *priv         = ANIMATION_GET_PRIVATE (animation);
-  GeglBuffer       *buffer       = NULL;
-  gint              previous_pos = priv->position;
-  gboolean          identical;
-
-  priv->position = animation_get_playback_start (animation) +
-                   ((priv->position - animation_get_playback_start (animation) + 1) %
-                    (animation_get_playback_stop (animation) - animation_get_playback_start (animation) + 
1));
-
-  identical = ANIMATION_GET_CLASS (animation)->same (animation,
-                                                     previous_pos,
-                                                     priv->position);
-  if (! identical)
-    {
-      buffer = animation_get_frame (animation, priv->position);
-    }
-  g_signal_emit (animation, animation_signals[RENDER], 0,
-                 priv->position, buffer, ! identical);
-  if (buffer != NULL)
-    {
-      g_object_unref (buffer);
-    }
-}
-
-void
-animation_prev (Animation *animation)
-{
-  AnimationPrivate *priv     = ANIMATION_GET_PRIVATE (animation);
-  GeglBuffer       *buffer   = NULL;
-  gint              prev_pos = priv->position;
-  gboolean          identical;
-
-  if (priv->position == animation_get_playback_start (animation))
-    {
-      priv->position = animation_get_playback_stop (animation);
-    }
-  else
-    {
-      --priv->position;
-    }
-
-  identical = ANIMATION_GET_CLASS (animation)->same (animation,
-                                                     prev_pos,
-                                                     priv->position);
-  if (! identical)
-    {
-      buffer = animation_get_frame (animation, priv->position);
-    }
-  g_signal_emit (animation, animation_signals[RENDER], 0,
-                 priv->position, buffer, ! identical);
-  if (buffer)
-    g_object_unref (buffer);
-}
-
-void
-animation_jump (Animation *animation,
-                gint       index)
-{
-  AnimationPrivate *priv     = ANIMATION_GET_PRIVATE (animation);
-  GeglBuffer       *buffer   = NULL;
-  gint              prev_pos = priv->position;
-  gboolean          identical;
-
-  if (index < priv->playback_start ||
-      index > priv->playback_stop)
-    return;
-  else
-    priv->position = index;
-
-  identical = ANIMATION_GET_CLASS (animation)->same (animation,
-                                                     prev_pos,
-                                                     priv->position);
-  if (! identical)
-    {
-      buffer = animation_get_frame (animation, priv->position);
-    }
-  g_signal_emit (animation, animation_signals[RENDER], 0,
-                 priv->position, buffer, ! identical);
-  if (buffer)
-    g_object_unref (buffer);
-}
-
-void
-animation_set_playback_start (Animation *animation,
-                              gint       frame_number)
-{
-  AnimationPrivate *priv = ANIMATION_GET_PRIVATE (animation);
-  gint              duration;
-
-  duration = animation_get_duration (animation);
-
-  if (frame_number < 0 ||
-      frame_number >= duration)
-    {
-      priv->playback_start = 0;
-    }
-  else
-    {
-      priv->playback_start = frame_number;
-    }
-  if (priv->playback_stop < priv->playback_start)
-    {
-      priv->playback_stop = duration - 1;
-    }
-
-  g_signal_emit (animation, animation_signals[PLAYBACK_RANGE], 0,
-                 priv->playback_start, priv->playback_stop,
-                 duration);
-
-  if (priv->position < priv->playback_start ||
-      priv->position > priv->playback_stop)
-    {
-      animation_jump (animation, priv->playback_start);
-    }
-}
-
-gint
-animation_get_playback_start (Animation *animation)
-{
-  AnimationPrivate *priv = ANIMATION_GET_PRIVATE (animation);
-
-  return priv->playback_start;
-}
-
-void
-animation_set_playback_stop (Animation *animation,
-                             gint       frame_number)
-{
-  AnimationPrivate *priv = ANIMATION_GET_PRIVATE (animation);
-  gint              duration;
-
-  duration = animation_get_duration (animation);
-
-  if (frame_number < 0 ||
-      frame_number >= duration)
-    {
-      priv->playback_stop = duration - 1;
-    }
-  else
-    {
-      priv->playback_stop = frame_number;
-    }
-  if (priv->playback_stop < priv->playback_start)
-    {
-      priv->playback_start = 0;
-    }
-  g_signal_emit (animation, animation_signals[PLAYBACK_RANGE], 0,
-                 priv->playback_start, priv->playback_stop,
-                 duration);
-
-  if (priv->position < priv->playback_start ||
-      priv->position > priv->playback_stop)
-    {
-      animation_jump (animation, priv->playback_start);
-    }
-}
-
-gint
-animation_get_playback_stop (Animation *animation)
-{
-  AnimationPrivate *priv = ANIMATION_GET_PRIVATE (animation);
-
-  return priv->playback_stop;
-}
-
 void
 animation_set_framerate (Animation *animation,
                          gdouble    fps)
@@ -818,6 +478,18 @@ animation_loaded (Animation *animation)
 /************ Private Functions ****************/
 
 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);
+}
+
+static void
 animation_set_property (GObject      *object,
                         guint         property_id,
                         const GValue *value,
@@ -867,49 +539,3 @@ animation_real_same (Animation *animation,
   /* By default all frames are supposed different. */
   return (previous_pos == next_pos);
 }
-
-static gboolean
-animation_advance_frame_callback (Animation *animation)
-{
-  AnimationPrivate *priv = ANIMATION_GET_PRIVATE (animation);
-  gint64            duration;
-  gint64            duration_since_start;
-  static gboolean   prev_low_framerate = FALSE;
-
-  animation_next (animation);
-  duration = (gint) (1000.0 / animation_get_framerate (animation));
-
-  duration_since_start = (g_get_monotonic_time () - priv->playback_start_time) / 1000;
-  duration = duration - (duration_since_start - priv->frames_played * duration);
-
-  if (duration < 1)
-    {
-      if (prev_low_framerate)
-        {
-          /* Let's only warn the user for several subsequent slow frames. */
-          gdouble real_framerate = (gdouble) priv->frames_played * 1000.0 / duration_since_start;
-          if (real_framerate < priv->framerate)
-            g_signal_emit (animation, animation_signals[LOW_FRAMERATE], 0,
-                           real_framerate);
-        }
-      duration = 1;
-      prev_low_framerate = TRUE;
-    }
-  else
-    {
-      if (prev_low_framerate)
-        {
-          /* Let's reset framerate warning. */
-          g_signal_emit (animation, animation_signals[LOW_FRAMERATE], 0,
-                         animation_get_framerate (animation));
-        }
-      prev_low_framerate = FALSE;
-    }
-  priv->frames_played++;
-
-  priv->playback_timer = g_timeout_add ((guint) duration,
-                                        (GSourceFunc) animation_advance_frame_callback,
-                                        (Animation *) animation);
-
-  return G_SOURCE_REMOVE;
-}
diff --git a/plug-ins/animation-play/core/animation.h b/plug-ins/animation-play/core/animation.h
index cf826b5..de47fbc 100644
--- a/plug-ins/animation-play/core/animation.h
+++ b/plug-ins/animation-play/core/animation.h
@@ -40,22 +40,35 @@ 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);
+
   /* Defaults to returning FALSE for any different position. */
   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);
-
+  void         (*load)               (Animation   *animation);
+  void         (*load_xml)           (Animation   *animation,
+                                      const gchar *xml);
+  gint         (*get_duration)       (Animation   *animation);
 
-  GeglBuffer * (*get_frame)     (Animation   *animation,
-                                 gint         pos);
+  GeglBuffer * (*get_frame)          (Animation   *animation,
+                                      gint         pos);
 
-  gchar      * (*serialize)     (Animation   *animation);
+  gchar      * (*serialize)          (Animation   *animation);
 };
 
 GType         animation_get_type (void);
@@ -70,7 +83,6 @@ void          animation_load               (Animation   *animation);
 
 void          animation_save_to_parasite   (Animation   *animation);
 
-gint          animation_get_position       (Animation   *animation);
 gint          animation_get_duration       (Animation   *animation);
 
 void          animation_set_proxy          (Animation   *animation,
@@ -83,22 +95,6 @@ void          animation_get_size           (Animation   *animation,
 GeglBuffer  * animation_get_frame          (Animation   *animation,
                                             gint         frame_number);
 
-
-gboolean      animation_is_playing         (Animation   *animation);
-void          animation_play               (Animation   *animation);
-void          animation_stop               (Animation   *animation);
-void          animation_next               (Animation   *animation);
-void          animation_prev               (Animation   *animation);
-void          animation_jump               (Animation   *animation,
-                                            gint         index);
-
-void          animation_set_playback_start (Animation   *animation,
-                                            gint         frame_number);
-gint          animation_get_playback_start (Animation   *animation);
-void          animation_set_playback_stop  (Animation   *animation,
-                                            gint         frame_number);
-gint          animation_get_playback_stop  (Animation   *animation);
-
 void          animation_set_framerate      (Animation   *animation,
                                             gdouble      fps);
 gdouble       animation_get_framerate      (Animation   *animation);
diff --git a/plug-ins/animation-play/core/animationanimatic.c 
b/plug-ins/animation-play/core/animationanimatic.c
index 756876a..e792648 100644
--- a/plug-ins/animation-play/core/animationanimatic.c
+++ b/plug-ins/animation-play/core/animationanimatic.c
@@ -79,7 +79,7 @@ struct _AnimationAnimaticPrivate
                                      ANIMATION_TYPE_ANIMATIC, \
                                      AnimationAnimaticPrivate)
 
-static void         animation_animatic_finalize   (GObject           *object);
+static void         animation_animatic_finalize     (GObject           *object);
 
 /* Virtual methods */
 
@@ -99,11 +99,11 @@ static gboolean     animation_animatic_same         (Animation         *animatio
 /* 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);
+                                                   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,
@@ -120,8 +120,6 @@ static void      animation_animatic_text          (GMarkupParseContext  *context
 static void         animation_animatic_cache      (AnimationAnimatic *animation,
                                                    gint               panel,
                                                    gboolean           recursion);
-static gint         animation_animatic_get_layer  (AnimationAnimatic *animation,
-                                                   gint               pos);
 
 /* Tag handling (from layer names) */
 
@@ -229,50 +227,19 @@ animation_animatic_set_panel_duration (AnimationAnimatic *animatic,
 {
   AnimationAnimaticPrivate *priv           = GET_PRIVATE (animatic);
   Animation                *animation      = ANIMATION (animatic);
-  gint                      prev_length    = animation_get_duration (animation);
-  gint                      playback_start = animation_get_playback_start (animation);
-  gint                      playback_stop  = animation_get_playback_stop (animation);
-  gint                      position       = animation_get_position (animation);
-  gint                      layer_id;
-  gint                      length;
+  gint                      duration;
 
   g_return_if_fail (panel_duration >= 0  &&
                     panel_num >= 0 &&
                     panel_num < priv->n_panels);
 
-  layer_id = animation_animatic_get_layer (animatic, position);
-
   priv->durations[panel_num] = panel_duration;
-  length = animation_get_duration (animation);
+  duration = animation_get_duration (animation);
 
-  if (playback_start >= length)
-    {
-      playback_start = 0;
-    }
-  if (playback_stop >= length ||
-      playback_stop == prev_length - 1)
-    {
-      playback_stop = length - 1;
-    }
   g_signal_emit (animatic, signals[PANEL_DURATION], 0,
                  panel_num, panel_duration);
-  g_signal_emit_by_name (animatic, "playback-range",
-                         playback_start, playback_stop,
-                         animation_get_duration (animation));
-  if (position >= length)
-    {
-      animation_jump (animation, length - 1);
-    }
-  else if (layer_id != animation_animatic_get_layer (animatic, position))
-    {
-      GeglBuffer *buffer;
-
-      buffer = animation_get_frame (animation, position);
-      g_signal_emit_by_name (animation, "render",
-                             position, buffer, TRUE);
-      if (buffer)
-        g_object_unref (buffer);
-    }
+  g_signal_emit_by_name (animation, "duration-changed",
+                         duration);
 }
 
 gint
@@ -313,6 +280,7 @@ animation_animatic_get_comment (AnimationAnimatic *animatic,
   g_return_val_if_fail (panel_num >= 0 &&
                         panel_num < priv->n_panels,
                         0);
+
   return priv->comments[panel_num];
 }
 
@@ -342,6 +310,7 @@ animation_animatic_get_combine (AnimationAnimatic *animatic,
   g_return_val_if_fail (panel_num >= 0 &&
                         panel_num < priv->n_panels,
                         FALSE);
+
   return priv->combine[panel_num];
 }
 
@@ -370,22 +339,23 @@ animation_animatic_get_panel (AnimationAnimatic *animation,
   return -1;
 }
 
-void animation_animatic_jump_panel (AnimationAnimatic *animation,
-                                    gint               panel)
+gint
+animation_animatic_get_position (AnimationAnimatic *animation,
+                                 gint               panel)
 {
   AnimationAnimaticPrivate *priv = GET_PRIVATE (animation);
-  /* Get the first frame position for a given panel. */
   gint                      pos  = 0;
   gint                      i;
 
-  g_return_if_fail (panel < priv->n_panels);
+  g_return_val_if_fail (panel < priv->n_panels,
+                        priv->n_panels - 1);
 
   for (i = 0; i < panel; i++)
     {
       pos += priv->durations[i];
     }
 
-  animation_jump (ANIMATION (animation), pos);
+  return pos;
 }
 
 /**** Virtual methods ****/
@@ -868,7 +838,7 @@ animation_animatic_cache (AnimationAnimatic *animatic,
   GeglBuffer               *backdrop_buffer = NULL;
   gint                      layer_offx;
   gint                      layer_offy;
-  gint                      position;
+  /*gint                      position;*/
   gint                      preview_width;
   gint                      preview_height;
   gint32                    image_id;
@@ -907,6 +877,11 @@ 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 next panel is in "combine" mode, it must also be re-cached.
    * And so on, recursively. */
   if (recursion                  &&
@@ -915,42 +890,6 @@ animation_animatic_cache (AnimationAnimatic *animatic,
     {
       animation_animatic_cache (animatic, panel + 1, TRUE);
     }
-
-  /* Finally re-render if we are currently showing this panel. */
-  position = animation_get_position (animation);
-  if (animation_animatic_get_panel (animatic, position) == panel)
-    {
-      buffer = animation_get_frame (animation, position);
-      g_signal_emit_by_name (animation, "render",
-                             position, buffer, TRUE);
-      if (buffer)
-        {
-          g_object_unref (buffer);
-        }
-    }
-}
-
-static gint
-animation_animatic_get_layer (AnimationAnimatic *animation,
-                              gint               pos)
-{
-  AnimationAnimaticPrivate *priv  = GET_PRIVATE (animation);
-  gint                      count = 0;
-  gint                      i     = -1;
-
-  if (priv->n_panels > 0 &&
-      pos >= 0           &&
-      pos < animation_animatic_get_duration (ANIMATION (animation)))
-    {
-      for (i = priv->n_panels - 1; i >= 0; i--)
-        {
-          count += priv->durations[i];
-          if (count >= pos)
-            break;
-        }
-    }
-
-  return i;
 }
 
 /**** TAG UTILS ****/
diff --git a/plug-ins/animation-play/core/animationanimatic.h 
b/plug-ins/animation-play/core/animationanimatic.h
index 12e6180..fccd85c 100644
--- a/plug-ins/animation-play/core/animationanimatic.h
+++ b/plug-ins/animation-play/core/animationanimatic.h
@@ -46,25 +46,26 @@ struct _AnimationAnimaticClass
 GType            animation_animatic_get_type (void);
 
 void             animation_animatic_set_panel_duration (AnimationAnimatic *animatic,
-                                                        gint               panel_num,
+                                                        gint               panel,
                                                         gint               duration);
 gint             animation_animatic_get_panel_duration (AnimationAnimatic *animatic,
-                                                        gint               panel_num);
+                                                        gint               panel);
 
-void             animation_animatic_set_comment  (AnimationAnimatic *animatic,
-                                                  gint               panel_num,
-                                                  const gchar       *comment);
-const gchar    * animation_animatic_get_comment  (AnimationAnimatic *animatic,
-                                                  gint               panel_num);
+void             animation_animatic_set_comment        (AnimationAnimatic *animatic,
+                                                        gint               panel,
+                                                        const gchar       *comment);
+const gchar    * animation_animatic_get_comment        (AnimationAnimatic *animatic,
+                                                        gint               panel);
 
-void             animation_animatic_set_combine  (AnimationAnimatic *animatic,
-                                                  gint               panel_num,
-                                                  gboolean           combine);
-const gboolean   animation_animatic_get_combine  (AnimationAnimatic *animatic,
-                                                  gint               panel_num);
+void             animation_animatic_set_combine        (AnimationAnimatic *animatic,
+                                                        gint               panel,
+                                                        gboolean           combine);
+const gboolean   animation_animatic_get_combine        (AnimationAnimatic *animatic,
+                                                        gint               panel);
+
+gint             animation_animatic_get_panel          (AnimationAnimatic *animation,
+                                                        gint               pos);
+gint             animation_animatic_get_position       (AnimationAnimatic *animation,
+                                                        gint               panel);
 
-gint             animation_animatic_get_panel    (AnimationAnimatic *animation,
-                                                  gint               pos);
-void             animation_animatic_jump_panel   (AnimationAnimatic *animation,
-                                                  gint               panel);
 #endif  /*  __ANIMATION_ANIMATIC_H__  */
diff --git a/plug-ins/animation-play/widgets/animation-dialog.c 
b/plug-ins/animation-play/widgets/animation-dialog.c
index 184ba87..35d8f3d 100755
--- a/plug-ins/animation-play/widgets/animation-dialog.c
+++ b/plug-ins/animation-play/widgets/animation-dialog.c
@@ -31,6 +31,7 @@
 #include "core/animation.h"
 #include "core/animationanimatic.h"
 #include "core/animation-celanimation.h"
+#include "core/animation-playback.h"
 
 #include "animation-dialog.h"
 #include "animation-layer-view.h"
@@ -57,54 +58,55 @@ typedef struct _AnimationDialogPrivate AnimationDialogPrivate;
 
 struct _AnimationDialogPrivate
 {
-  gint32          image_id;
+  gint32             image_id;
 
-  Animation      *animation;
-  gdouble         zoom;
+  Animation         *animation;
+  AnimationPlayback *playback;
+  gdouble            zoom;
 
   /* GUI */
-  GtkWidget      *play_bar;
-  GtkWidget      *progress_bar;
-  GtkWidget      *settings_bar;
+  GtkWidget         *play_bar;
+  GtkWidget         *progress_bar;
+  GtkWidget         *settings_bar;
 
-  GtkWidget      *animation_type_combo;
-  GtkWidget      *fpscombo;
-  GtkWidget      *zoomcombo;
-  GtkWidget      *proxycombo;
+  GtkWidget         *animation_type_combo;
+  GtkWidget         *fpscombo;
+  GtkWidget         *zoomcombo;
+  GtkWidget         *proxycombo;
 
-  GtkWidget      *progress;
-  GtkWidget      *startframe_spin;
-  GtkWidget      *endframe_spin;
+  GtkWidget         *progress;
+  GtkWidget         *startframe_spin;
+  GtkWidget         *endframe_spin;
 
-  GtkWidget      *view_bar;
-  GtkWidget      *refresh;
+  GtkWidget         *view_bar;
+  GtkWidget         *refresh;
 
-  GtkWidget      *drawing_area;
-  guchar         *drawing_area_data;
-  guint           drawing_area_width;
-  guint           drawing_area_height;
+  GtkWidget         *drawing_area;
+  guchar            *drawing_area_data;
+  guint              drawing_area_width;
+  guint              drawing_area_height;
 
-  GtkWidget      *shape_window;
-  GtkWidget      *shape_drawing_area;
-  guchar         *shape_drawing_area_data;
-  guint           shape_drawing_area_width;
-  guint           shape_drawing_area_height;
+  GtkWidget         *shape_window;
+  GtkWidget         *shape_drawing_area;
+  guchar            *shape_drawing_area_data;
+  guint              shape_drawing_area_width;
+  guint              shape_drawing_area_height;
 
   /* The hpaned (left is preview, right is layer list. */
-  GtkWidget      *hpaned;
-  GtkWidget      *layer_list;
+  GtkWidget         *hpaned;
+  GtkWidget         *layer_list;
 
   /* The vpaned (bottom is timeline, above is preview). */
-  GtkWidget      *vpaned;
-  GtkWidget      *xsheet;
+  GtkWidget         *vpaned;
+  GtkWidget         *xsheet;
 
   /* Actions */
-  GtkUIManager   *ui_manager;
+  GtkUIManager      *ui_manager;
 
-  GtkActionGroup *play_actions;
-  GtkActionGroup *settings_actions;
-  GtkActionGroup *view_actions;
-  GtkActionGroup *various_actions;
+  GtkActionGroup    *play_actions;
+  GtkActionGroup    *settings_actions;
+  GtkActionGroup    *view_actions;
+  GtkActionGroup    *various_actions;
 };
 
 #define GET_PRIVATE(dialog) \
@@ -198,24 +200,14 @@ static gboolean    popup_menu                (GtkWidget        *widget,
 static void        show_loading_progress     (Animation        *animation,
                                               gdouble           load_rate,
                                               AnimationDialog  *dialog);
-static void        animation_update_progress (Animation        *animation,
-                                              gint              num_frames,
-                                              gint              playback_start,
-                                              gint              playback_stop,
-                                              AnimationDialog  *dialog);
-static void        block_ui                  (Animation        *animation,
-                                              AnimationDialog  *dialog);
 static void        unblock_ui                (Animation        *animation,
-                                              gint              num_frames,
-                                              gint              playback_start,
-                                              gint              playback_stop,
-                                              gint              preview_width,
-                                              gint              preview_height,
                                               AnimationDialog  *dialog);
-static void        playback_range_changed    (Animation        *animation,
+static void        duration_changed          (AnimationPlayback *playback,
+                                              gint               duration,
+                                              AnimationDialog   *dialog);
+static void        playback_range_changed    (AnimationPlayback *playback,
                                               gint              playback_start,
                                               gint              playback_stop,
-                                              gint              length,
                                               AnimationDialog  *dialog);
 static void        proxy_changed             (Animation        *animation,
                                               gdouble           fps,
@@ -223,7 +215,7 @@ static void        proxy_changed             (Animation        *animation,
 static void        framerate_changed         (Animation        *animation,
                                               gdouble           fps,
                                               AnimationDialog  *dialog);
-static void        low_framerate_cb          (Animation        *animation,
+static void        low_framerate_cb          (AnimationPlayback *playback,
                                               gdouble           real_framerate,
                                               AnimationDialog  *dialog);
 
@@ -244,7 +236,7 @@ static gboolean    shape_released            (GtkWidget        *widget);
 static gboolean    shape_motion              (GtkWidget        *widget,
                                               GdkEventMotion   *event);
 
-static void        render_callback           (Animation        *animation,
+static void        render_callback           (AnimationPlayback *animation,
                                               gint              frame_number,
                                               GeglBuffer       *buffer,
                                               gboolean          must_draw_null,
@@ -276,6 +268,8 @@ static void        show_goto_progress        (AnimationDialog  *dialog,
 static void        show_playing_progress     (AnimationDialog  *dialog);
 
 /* Utils */
+static void        update_progress           (AnimationDialog  *dialog);
+static void        block_ui                  (AnimationDialog  *dialog);
 static gboolean    is_detached               (AnimationDialog  *dialog);
 static void        play_pause                (AnimationDialog  *dialog);
 
@@ -313,6 +307,9 @@ animation_dialog_class_init (AnimationDialogClass *klass)
 static void
 animation_dialog_init (AnimationDialog *dialog)
 {
+  AnimationDialogPrivate *priv = GET_PRIVATE (dialog);
+
+  priv->playback = animation_playback_new ();
 }
 
 /**** Public Functions ****/
@@ -326,7 +323,6 @@ animation_dialog_new (gint32 image_id)
   const gchar    *xml = NULL;
   gboolean        animatic_selected = TRUE;
 
-  /* If this animation has specific settings already, override the global ones. */
   parasite = gimp_image_get_parasite (image_id,
                                       PLUG_IN_PROC "/selected");
   if (parasite)
@@ -367,7 +363,6 @@ animation_dialog_new (gint32 image_id)
                          NULL);
   animation_dialog_set_animation (ANIMATION_DIALOG (dialog),
                                   animation);
-  animation_load (animation);
 
   return dialog;
 }
@@ -861,6 +856,9 @@ animation_dialog_finalize (GObject *object)
   if (priv->animation)
     g_object_unref (priv->animation);
 
+  if (priv->playback)
+    g_object_unref (priv->playback);
+
   G_OBJECT_CLASS (parent_class)->finalize (object);
 
   /* When this window ends, the plugin ends. */
@@ -1098,6 +1096,9 @@ animation_dialog_set_animation (AnimationDialog *dialog,
                                             G_CALLBACK (framerate_changed),
                                             dialog);
       g_signal_handlers_disconnect_by_func (priv->animation,
+                                            G_CALLBACK (duration_changed),
+                                            dialog);
+      g_signal_handlers_disconnect_by_func (priv->playback,
                                             G_CALLBACK (playback_range_changed),
                                             dialog);
 
@@ -1105,15 +1106,12 @@ animation_dialog_set_animation (AnimationDialog *dialog,
                                             (GCallback) show_loading_progress,
                                             dialog);
       g_signal_handlers_disconnect_by_func (priv->animation,
-                                            (GCallback) block_ui,
-                                            dialog);
-      g_signal_handlers_disconnect_by_func (priv->animation,
                                             (GCallback) unblock_ui,
                                             dialog);
       g_signal_handlers_disconnect_by_func (priv->animation,
                                             G_CALLBACK (render_callback),
                                             dialog);
-      g_signal_handlers_disconnect_by_func (priv->animation,
+      g_signal_handlers_disconnect_by_func (priv->playback,
                                             G_CALLBACK (low_framerate_cb),
                                             dialog);
     }
@@ -1168,7 +1166,7 @@ animation_dialog_set_animation (AnimationDialog *dialog,
                                    G_CALLBACK (proxycombo_changed),
                                    dialog);
   /* Settings: fps */
-  fps = animation_get_framerate (animation);
+  fps = animation_get_framerate (priv->animation);
 
   gtk_combo_box_set_active (GTK_COMBO_BOX (priv->fpscombo), -1);
   for (index = 0; index < 5; index++)
@@ -1242,7 +1240,8 @@ animation_dialog_set_animation (AnimationDialog *dialog,
       gtk_frame_set_label (GTK_FRAME (frame), _("Storyboard"));
 
       /* The Storyboard view. */
-      storyboard = animation_storyboard_new (ANIMATION_ANIMATIC (animation));
+      storyboard = animation_storyboard_new (ANIMATION_ANIMATIC (animation),
+                                             priv->playback);
       gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW (scrolled_win),
                                              storyboard);
       gtk_widget_show (storyboard);
@@ -1281,7 +1280,7 @@ animation_dialog_set_animation (AnimationDialog *dialog,
       gtk_widget_show (frame);
 
       priv->xsheet = animation_xsheet_new (ANIMATION_CEL_ANIMATION (animation),
-                                           priv->layer_list);
+                                           priv->playback, priv->layer_list);
       gtk_container_add (GTK_CONTAINER (frame), priv->xsheet);
       gtk_widget_show (priv->xsheet);
     }
@@ -1292,33 +1291,34 @@ animation_dialog_set_animation (AnimationDialog *dialog,
                                      dialog);
 
   /* Animation. */
-  g_signal_connect (priv->animation, "proxy",
+  g_signal_connect (priv->animation, "proxy-changed",
                     G_CALLBACK (proxy_changed),
                     dialog);
   g_signal_connect (priv->animation, "framerate-changed",
                     G_CALLBACK (framerate_changed),
                     dialog);
-  g_signal_connect (priv->animation,
-                    "playback-range",
+  g_signal_connect (priv->animation, "duration-changed",
+                    G_CALLBACK (duration_changed),
+                    dialog);
+  g_signal_connect (priv->playback, "range",
                     G_CALLBACK (playback_range_changed),
                     dialog);
 
   g_signal_connect (priv->animation, "loading",
                     (GCallback) show_loading_progress,
                     dialog);
-  g_signal_connect (priv->animation, "loading-start",
-                    (GCallback) block_ui,
-                    dialog);
   g_signal_connect (priv->animation, "loaded",
                     (GCallback) unblock_ui,
                     dialog);
-  g_signal_connect (priv->animation, "render",
+  g_signal_connect (priv->playback, "render",
                     G_CALLBACK (render_callback),
                     dialog);
-  g_signal_connect (priv->animation, "low-framerate-playback",
+  g_signal_connect (priv->playback, "low-framerate",
                     G_CALLBACK (low_framerate_cb),
                     dialog);
 
+  /* Set the playback */
+  animation_playback_set_animation (priv->playback, animation);
 }
 
 static void
@@ -1369,7 +1369,7 @@ update_ui_sensitivity (AnimationDialog *dialog)
   AnimationDialogPrivate *priv = GET_PRIVATE (dialog);
   gboolean                animated;
 
-  animated = animation_get_playback_stop (priv->animation) - animation_get_playback_start (priv->animation) 
1;
+  animated = animation_playback_get_stop (priv->playback) - animation_playback_get_start (priv->playback) > 
1;
   /* Play actions only if we selected several frames between start/end. */
   gtk_action_group_set_sensitive (priv->play_actions, animated);
   gtk_widget_set_sensitive (GTK_WIDGET (priv->play_bar), animated);
@@ -1397,7 +1397,7 @@ close_callback (GtkAction       *action,
 {
   AnimationDialogPrivate *priv = GET_PRIVATE (dialog);
 
-  animation_stop (priv->animation);
+  animation_playback_stop (priv->playback);
   gtk_widget_destroy (GTK_WIDGET (dialog));
 }
 
@@ -1621,7 +1621,7 @@ proxycombo_activated (GtkEntry        *combo_entry,
     {
       gboolean was_playing;
 
-      was_playing = animation_is_playing (priv->animation);
+      was_playing = animation_playback_is_playing (priv->playback);
 
       animation_set_proxy (priv->animation, ratio);
       update_scale (dialog, get_zoom (dialog, -1));
@@ -1733,7 +1733,7 @@ adjustment_pressed (GtkWidget       *widget,
       GtkAdjustment *adj = gtk_spin_button_get_adjustment (spin);
 
       gtk_adjustment_set_value (adj,
-                                (gdouble) animation_get_position (priv->animation) + 1.0);
+                                (gdouble) animation_playback_get_position (priv->playback) + 1.0);
 
       /* We don't want the middle click to have another usage (in
        * particular, there is likely no need to copy-paste in these spin
@@ -1754,7 +1754,7 @@ startframe_changed (GtkAdjustment   *adjustment,
   if (! priv->animation)
     return;
 
-  animation_set_playback_start (priv->animation, (gint) value - 1);
+  animation_playback_set_start (priv->playback, (gint) value - 1);
 
   update_ui_sensitivity (dialog);
 }
@@ -1769,7 +1769,7 @@ endframe_changed (GtkAdjustment   *adjustment,
   if (! priv->animation)
     return;
 
-  animation_set_playback_stop (priv->animation, (gint) value - 1);
+  animation_playback_set_stop (priv->playback, (gint) value - 1);
 
   update_ui_sensitivity (dialog);
 }
@@ -1780,15 +1780,15 @@ play_callback (GtkToggleAction *action,
 {
   AnimationDialogPrivate *priv = GET_PRIVATE (dialog);
 
-  if (! animation_is_playing (priv->animation))
+  if (! animation_playback_is_playing (priv->playback))
     {
       gtk_action_set_icon_name (GTK_ACTION (action), "media-playback-pause");
-      animation_play (priv->animation);
+      animation_playback_play (priv->playback);
     }
   else
     {
       gtk_action_set_icon_name (GTK_ACTION (action), "media-playback-start");
-      animation_stop (priv->animation);
+      animation_playback_stop (priv->playback);
 
       /* The framerate combo might have been modified to display slowness
        * warnings. */
@@ -1799,7 +1799,7 @@ play_callback (GtkToggleAction *action,
 
   g_object_set (action,
                 "tooltip",
-                animation_is_playing (priv->animation) ?  _("Pause playback") : _("Start playback"),
+                animation_playback_is_playing (priv->playback) ?  _("Pause playback") : _("Start playback"),
                 NULL);
 }
 
@@ -1809,9 +1809,9 @@ step_back_callback (GtkAction           *action,
 {
   AnimationDialogPrivate *priv = GET_PRIVATE (dialog);
 
-  if (animation_is_playing (priv->animation))
+  if (animation_playback_is_playing (priv->playback))
     play_pause (dialog);
-  animation_prev (priv->animation);
+  animation_playback_prev (priv->playback);
 }
 
 static void
@@ -1820,9 +1820,9 @@ step_callback (GtkAction       *action,
 {
   AnimationDialogPrivate *priv = GET_PRIVATE (dialog);
 
-  if (animation_is_playing (priv->animation))
-    animation_stop (priv->animation);
-  animation_next (priv->animation);
+  if (animation_playback_is_playing (priv->playback))
+    animation_playback_stop (priv->playback);
+  animation_playback_next (priv->playback);
 }
 
 static void
@@ -1832,12 +1832,12 @@ rewind_callback (GtkAction       *action,
   AnimationDialogPrivate *priv = GET_PRIVATE (dialog);
   gboolean                was_playing;
 
-  was_playing = animation_is_playing (priv->animation);
+  was_playing = animation_playback_is_playing (priv->playback);
 
   if (was_playing)
     play_pause (dialog);
 
-  animation_jump (priv->animation, animation_get_playback_start (priv->animation));
+  animation_playback_jump (priv->playback, animation_playback_get_start (priv->playback));
 
   /* If we were playing, start playing again. */
   if (was_playing)
@@ -1899,7 +1899,7 @@ detach_callback (GtkToggleAction *action,
 
   /* Force a refresh after detachment/attachment. */
   buffer = animation_get_frame (priv->animation,
-                                animation_get_position (priv->animation));
+                                animation_playback_get_position (priv->playback));
   render_frame (dialog, buffer, TRUE);
   /* clean up */
   if (buffer != NULL)
@@ -1931,7 +1931,7 @@ show_loading_progress (Animation       *animation,
   AnimationDialogPrivate *priv = GET_PRIVATE (dialog);
   gchar *text;
 
-  block_ui (animation, dialog);
+  block_ui (dialog);
 
   /* update the dialog's progress bar */
   gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (priv->progress), load_rate);
@@ -1946,85 +1946,32 @@ show_loading_progress (Animation       *animation,
 }
 
 static void
-animation_update_progress (Animation       *animation,
-                           gint             num_frames,
-                           gint             playback_start,
-                           gint             playback_stop,
-                           AnimationDialog *dialog)
-{
-  AnimationDialogPrivate *priv = GET_PRIVATE (dialog);
-  gint frame_spin_size;
-  gint last_frame = num_frames;
-
-  frame_spin_size = (gint) (log10 (last_frame + 1 - (last_frame % 10))) + 1;
-  gtk_entry_set_width_chars (GTK_ENTRY (priv->startframe_spin), frame_spin_size);
-  gtk_entry_set_width_chars (GTK_ENTRY (priv->endframe_spin), frame_spin_size);
-
-  gtk_adjustment_configure (gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON (priv->startframe_spin)),
-                            (gdouble) playback_start + 1.0,
-                            1.0,
-                            (gdouble) last_frame,
-                            1.0, 5.0, 0.0);
-  gtk_adjustment_configure (gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON (priv->endframe_spin)),
-                            (gdouble) playback_stop + 1.0,
-                            (gdouble) playback_start + 1.0,
-                            (gdouble) last_frame,
-                            1.0, 5.0, 0.0);
-
-  update_ui_sensitivity (dialog);
-}
-
-static void
-block_ui (Animation       *animation,
-          AnimationDialog *dialog)
+unblock_ui (Animation      *animation,
+            AnimationDialog *dialog)
 {
-  AnimationDialogPrivate *priv = GET_PRIVATE (dialog);
-
-  if (animation_is_playing (priv->animation))
-    play_pause (dialog);
-
-  gtk_action_group_set_sensitive (priv->play_actions, FALSE);
-  gtk_widget_set_sensitive (priv->play_bar, FALSE);
-  gtk_widget_set_sensitive (priv->progress_bar, FALSE);
-  gtk_action_group_set_sensitive (priv->settings_actions, FALSE);
-  gtk_widget_set_sensitive (GTK_WIDGET (priv->settings_bar), FALSE);
-  gtk_action_group_set_sensitive (priv->view_actions, FALSE);
-  gtk_widget_set_sensitive (GTK_WIDGET (priv->view_bar), FALSE);
+  update_progress (dialog);
+  animation_dialog_refresh (dialog);
 }
 
 static void
-unblock_ui (Animation      *animation,
-            gint            num_frames,
-            gint            playback_start,
-            gint            playback_stop,
-            gint            preview_width,
-            gint            preview_height,
-            AnimationDialog *dialog)
+duration_changed (AnimationPlayback *playback,
+                  gint               duration,
+                  AnimationDialog   *dialog)
 {
-  animation_update_progress (animation,
-                             num_frames,
-                             playback_start,
-                             playback_stop,
-                             dialog);
-  animation_dialog_refresh (dialog);
+  update_progress (dialog);
 }
 
 static void
-playback_range_changed (Animation       *animation,
-                        gint             playback_start,
-                        gint             playback_stop,
-                        gint             length,
-                        AnimationDialog *dialog)
+playback_range_changed (AnimationPlayback *playback,
+                        gint               playback_start,
+                        gint               playback_stop,
+                        AnimationDialog   *dialog)
 {
   AnimationDialogPrivate *priv = GET_PRIVATE (dialog);
   GtkAdjustment *startframe_adjust;
   GtkAdjustment *stopframe_adjust;
 
-  animation_update_progress (animation,
-                             length,
-                             playback_start,
-                             playback_stop,
-                             dialog);
+  update_progress (dialog);
 
   startframe_adjust = gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON (priv->startframe_spin));
   stopframe_adjust  = gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON (priv->endframe_spin));
@@ -2100,9 +2047,9 @@ framerate_changed (Animation        *animation,
 }
 
 static void
-low_framerate_cb (Animation       *animation,
-                  gdouble          real_framerate,
-                  AnimationDialog *dialog)
+low_framerate_cb (AnimationPlayback *playback,
+                  gdouble            real_framerate,
+                  AnimationDialog   *dialog)
 {
   AnimationDialogPrivate *priv = GET_PRIVATE (dialog);
   gchar                  *text;
@@ -2290,7 +2237,7 @@ da_size_callback (GtkWidget       *drawing_area,
               GeglBuffer *buffer;
 
               buffer = animation_get_frame (priv->animation,
-                                            animation_get_position (priv->animation));
+                                            animation_playback_get_position (priv->playback));
               render_frame (dialog, buffer, TRUE);
               /* clean up */
               if (buffer != NULL)
@@ -2371,11 +2318,11 @@ shape_motion (GtkWidget      *widget,
 }
 
 static void
-render_callback (Animation       *animation,
-                 gint             frame_number,
-                 GeglBuffer      *buffer,
-                 gboolean         must_draw_null,
-                 AnimationDialog *dialog)
+render_callback (AnimationPlayback *playback,
+                 gint               frame_number,
+                 GeglBuffer        *buffer,
+                 gboolean           must_draw_null,
+                 AnimationDialog   *dialog)
 {
   render_frame (dialog, buffer, must_draw_null);
 
@@ -2391,7 +2338,7 @@ render_on_realize (GtkWidget       *drawing_area,
   GeglBuffer *buffer;
 
   buffer = animation_get_frame (priv->animation,
-                                animation_get_position (priv->animation));
+                                animation_playback_get_position (priv->playback));
   render_frame (dialog, buffer, TRUE);
   /* clean up */
   if (buffer != NULL)
@@ -2590,7 +2537,7 @@ progress_button (GtkWidget       *widget,
                       ((gdouble) allocation.width /
                        ((gdouble) duration - 0.99)));
 
-      animation_jump (priv->animation, frame);
+      animation_playback_jump (priv->playback, frame);
     }
 
   return FALSE;
@@ -2678,17 +2625,64 @@ show_playing_progress (AnimationDialog *dialog)
 
   /* update the dialog's progress bar */
   gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (priv->progress),
-                                 ((gfloat) animation_get_position (priv->animation) /
+                                 ((gfloat) animation_playback_get_position (priv->playback) /
                                   (gfloat) (animation_get_duration (priv->animation) - 0.999)));
 
   text = g_strdup_printf (_("Frame %d of %d"),
-                          animation_get_position (priv->animation) + 1,
+                          animation_playback_get_position (priv->playback) + 1,
                           animation_get_duration (priv->animation));
 
   gtk_progress_bar_set_text (GTK_PROGRESS_BAR (priv->progress), text);
   g_free (text);
 }
 
+static void
+update_progress (AnimationDialog *dialog)
+{
+  AnimationDialogPrivate *priv = GET_PRIVATE (dialog);
+  gint frame_spin_size;
+  gint duration = animation_get_duration (priv->animation);
+  gint playback_start;
+  gint playback_stop;
+
+  playback_start = animation_playback_get_start (priv->playback);
+  playback_stop = animation_playback_get_stop (priv->playback);
+
+  frame_spin_size = (gint) (log10 (duration + 1 - (duration % 10))) + 1;
+  gtk_entry_set_width_chars (GTK_ENTRY (priv->startframe_spin), frame_spin_size);
+  gtk_entry_set_width_chars (GTK_ENTRY (priv->endframe_spin), frame_spin_size);
+
+  gtk_adjustment_configure (gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON (priv->startframe_spin)),
+                            (gdouble) playback_start + 1.0,
+                            1.0,
+                            (gdouble) duration,
+                            1.0, 5.0, 0.0);
+  gtk_adjustment_configure (gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON (priv->endframe_spin)),
+                            (gdouble) playback_stop + 1.0,
+                            (gdouble) playback_start + 1.0,
+                            (gdouble) duration,
+                            1.0, 5.0, 0.0);
+
+  update_ui_sensitivity (dialog);
+}
+
+static void
+block_ui (AnimationDialog *dialog)
+{
+  AnimationDialogPrivate *priv = GET_PRIVATE (dialog);
+
+  if (animation_playback_is_playing (priv->playback))
+    play_pause (dialog);
+
+  gtk_action_group_set_sensitive (priv->play_actions, FALSE);
+  gtk_widget_set_sensitive (priv->play_bar, FALSE);
+  gtk_widget_set_sensitive (priv->progress_bar, FALSE);
+  gtk_action_group_set_sensitive (priv->settings_actions, FALSE);
+  gtk_widget_set_sensitive (GTK_WIDGET (priv->settings_bar), FALSE);
+  gtk_action_group_set_sensitive (priv->view_actions, FALSE);
+  gtk_widget_set_sensitive (GTK_WIDGET (priv->view_bar), FALSE);
+}
+
 static gboolean
 is_detached (AnimationDialog *dialog)
 {
diff --git a/plug-ins/animation-play/widgets/animation-storyboard.c 
b/plug-ins/animation-play/widgets/animation-storyboard.c
index 613c2de..feaaa30 100644
--- a/plug-ins/animation-play/widgets/animation-storyboard.c
+++ b/plug-ins/animation-play/widgets/animation-storyboard.c
@@ -28,6 +28,7 @@
 #include "libgimp/stdplugins-intl.h"
 
 #include "core/animationanimatic.h"
+#include "core/animation-playback.h"
 
 #include "animation-storyboard.h"
 
@@ -41,6 +42,7 @@ enum
 struct _AnimationStoryboardPrivate
 {
   AnimationAnimatic  *animation;
+  AnimationPlayback  *playback;
 
   gint                current_panel;
 
@@ -63,20 +65,15 @@ static void animation_storyboard_finalize              (GObject             *obj
 
 /* Callbacks on animation */
 static void animation_storyboard_load                  (Animation           *animation,
-                                                        G_GNUC_UNUSED gint   num_frames,
-                                                        G_GNUC_UNUSED gint   playback_start,
-                                                        G_GNUC_UNUSED gint   playback_stop,
-                                                        G_GNUC_UNUSED gint   preview_width,
-                                                        G_GNUC_UNUSED gint   preview_height,
                                                         AnimationStoryboard *view);
-static void animation_storyboard_rendered              (Animation           *animation,
+static void animation_storyboard_rendered              (AnimationPlayback   *playback,
                                                         gint                 frame_number,
                                                         GeglBuffer          *buffer,
                                                         gboolean             must_draw_null,
                                                         AnimationStoryboard *view);
 
 static void animation_storyboard_duration_spin_changed (GtkSpinButton       *spinbutton,
-                                                        AnimationAnimatic   *animation);
+                                                        AnimationStoryboard *storyboard);
 
 static gboolean animation_storyboard_comment_keypress  (GtkWidget           *entry,
                                                         GdkEventKey         *event,
@@ -86,7 +83,7 @@ static void animation_storyboard_comment_changed       (GtkTextBuffer       *tex
 static void animation_storyboard_disposal_toggled      (GtkToggleButton     *button,
                                                         AnimationAnimatic   *animatic);
 static void animation_storyboard_button_clicked        (GtkWidget           *widget,
-                                                        AnimationAnimatic   *animatic);
+                                                        AnimationStoryboard *storyboard);
 
 G_DEFINE_TYPE (AnimationStoryboard, animation_storyboard, GTK_TYPE_TABLE)
 
@@ -136,13 +133,17 @@ animation_storyboard_init (AnimationStoryboard *view)
  * Creates a new layer view tied to @animation, ready to be displayed.
  */
 GtkWidget *
-animation_storyboard_new (AnimationAnimatic *animation)
+animation_storyboard_new (AnimationAnimatic *animation,
+                          AnimationPlayback *playback)
 {
-  GtkWidget *layer_view;
+  GtkWidget           *layer_view;
+  AnimationStoryboard *storyboard;
 
   layer_view = g_object_new (ANIMATION_TYPE_STORYBOARD,
                              "animation", animation,
                              NULL);
+  storyboard = ANIMATION_STORYBOARD (layer_view);
+  storyboard->priv->playback = playback;
   return layer_view;
 }
 
@@ -228,11 +229,6 @@ animation_storyboard_finalize (GObject *object)
  */
 static void
 animation_storyboard_load (Animation           *animation,
-                           gint                 num_frames,
-                           gint                 playback_start,
-                           gint                 playback_stop,
-                           gint                 preview_width,
-                           gint                 preview_height,
                            AnimationStoryboard *view)
 {
   AnimationAnimatic *animatic = ANIMATION_ANIMATIC (animation);
@@ -299,7 +295,7 @@ animation_storyboard_load (Animation           *animation,
                          GINT_TO_POINTER (panel_num));
       g_signal_connect (panel_button, "clicked",
                         G_CALLBACK (animation_storyboard_button_clicked),
-                        animation);
+                        view);
 
       view->priv->panel_buttons = g_list_prepend (view->priv->panel_buttons,
                                                   panel_button);
@@ -392,7 +388,7 @@ animation_storyboard_load (Animation           *animation,
                          GINT_TO_POINTER (panel_num));
       g_signal_connect (duration, "value-changed",
                         (GCallback) animation_storyboard_duration_spin_changed,
-                        animation);
+                        view);
       gtk_widget_show (duration);
 
       disposal = gtk_toggle_button_new ();
@@ -419,14 +415,14 @@ animation_storyboard_load (Animation           *animation,
                                                     disposal);
       gtk_widget_show (disposal);
     }
-  g_signal_connect (animation, "render",
+  g_signal_connect (view->priv->playback, "render",
                     (GCallback) animation_storyboard_rendered,
                     view);
   gimp_image_delete (image_id);
 }
 
 static void
-animation_storyboard_rendered (Animation           *animation,
+animation_storyboard_rendered (AnimationPlayback   *playback,
                                gint                 frame_number,
                                GeglBuffer          *buffer,
                                gboolean             must_draw_null,
@@ -436,7 +432,7 @@ animation_storyboard_rendered (Animation           *animation,
   GtkWidget *arrow;
   gint       panel;
 
-  panel = animation_animatic_get_panel (ANIMATION_ANIMATIC (animation),
+  panel = animation_animatic_get_panel (view->priv->animation,
                                         frame_number);
   if (view->priv->current_panel >= 0)
     {
@@ -456,18 +452,33 @@ animation_storyboard_rendered (Animation           *animation,
 }
 
 static void
-animation_storyboard_duration_spin_changed (GtkSpinButton     *spinbutton,
-                                            AnimationAnimatic *animation)
+animation_storyboard_duration_spin_changed (GtkSpinButton       *spinbutton,
+                                            AnimationStoryboard *storyboard)
 {
   gpointer panel_num;
   gint     duration;
+  gint     panel_position;
+  gint     position;
 
   panel_num = g_object_get_data (G_OBJECT (spinbutton), "panel-num");
   duration = gtk_spin_button_get_value_as_int (spinbutton);
 
-  animation_animatic_set_panel_duration (animation,
+  position = animation_playback_get_position (storyboard->priv->playback);
+  panel_position = animation_animatic_get_position (storyboard->priv->animation,
+                                                    GPOINTER_TO_INT (panel_num));
+  if (position >= panel_position)
+    {
+      gint cur_duration;
+
+      cur_duration = animation_animatic_get_panel_duration (storyboard->priv->animation,
+                                                            GPOINTER_TO_INT (panel_num));
+      position += duration - cur_duration;
+    }
+
+  animation_animatic_set_panel_duration (storyboard->priv->animation,
                                          GPOINTER_TO_INT (panel_num),
                                          duration);
+  animation_playback_jump (storyboard->priv->playback, position);
 }
 
 static gboolean
@@ -535,11 +546,14 @@ animation_storyboard_disposal_toggled (GtkToggleButton   *button,
 }
 
 static void
-animation_storyboard_button_clicked (GtkWidget         *widget,
-                                     AnimationAnimatic *animatic)
+animation_storyboard_button_clicked (GtkWidget           *widget,
+                                     AnimationStoryboard *storyboard)
 {
   gpointer panel_num;
+  gint     position;
 
   panel_num = g_object_get_data (G_OBJECT (widget), "panel-num");
-  animation_animatic_jump_panel (animatic, GPOINTER_TO_INT (panel_num));
+  position = animation_animatic_get_position (storyboard->priv->animation,
+                                              GPOINTER_TO_INT (panel_num));
+  animation_playback_jump (storyboard->priv->playback, position);
 }
diff --git a/plug-ins/animation-play/widgets/animation-storyboard.h 
b/plug-ins/animation-play/widgets/animation-storyboard.h
index f92a23e..cb7452b 100644
--- a/plug-ins/animation-play/widgets/animation-storyboard.h
+++ b/plug-ins/animation-play/widgets/animation-storyboard.h
@@ -46,7 +46,8 @@ struct _AnimationStoryboardClass
 
 GType       animation_storyboard_get_type (void) G_GNUC_CONST;
 
-GtkWidget * animation_storyboard_new      (AnimationAnimatic *animation);
+GtkWidget * animation_storyboard_new      (AnimationAnimatic *animation,
+                                           AnimationPlayback *playback);
 
 #endif  /*  __ANIMATION_STORYBOARD_H__  */
 
diff --git a/plug-ins/animation-play/widgets/animation-xsheet.c 
b/plug-ins/animation-play/widgets/animation-xsheet.c
index 4161f3d..c7aaa9a 100755
--- a/plug-ins/animation-play/widgets/animation-xsheet.c
+++ b/plug-ins/animation-play/widgets/animation-xsheet.c
@@ -25,6 +25,8 @@
 
 #include <libgimp/gimp.h>
 
+#include "core/animation.h"
+#include "core/animation-playback.h"
 #include "core/animation-celanimation.h"
 #include "animation-layer-view.h"
 
@@ -42,6 +44,8 @@ enum
 struct _AnimationXSheetPrivate
 {
   AnimationCelAnimation *animation;
+  AnimationPlayback     *playback;
+
   GtkWidget             *layer_view;
 
   GtkWidget             *track_layout;
@@ -72,17 +76,13 @@ static void animation_xsheet_reset_layout (AnimationXSheet *xsheet);
 
 /* Callbacks on animation. */
 static void on_animation_loaded           (Animation       *animation,
-                                           gint             num_frames,
-                                           gint             playback_start,
-                                           gint             playback_stop,
-                                           gint             preview_width,
-                                           gint             preview_height,
-                                           AnimationXSheet *xsheet);
-static void on_animation_rendered         (Animation       *animation,
-                                           gint             frame_number,
-                                           GeglBuffer      *buffer,
-                                           gboolean         must_draw_null,
                                            AnimationXSheet *xsheet);
+/* Callbacks on callback. */
+static void on_animation_rendered         (AnimationPlayback *animation,
+                                           gint               frame_number,
+                                           GeglBuffer        *buffer,
+                                           gboolean           must_draw_null,
+                                           AnimationXSheet   *xsheet);
 
 /* Callbacks on layer view. */
 static void on_layer_selection            (AnimationLayerView *view,
@@ -160,6 +160,7 @@ animation_xsheet_init (AnimationXSheet *xsheet)
 
 GtkWidget *
 animation_xsheet_new (AnimationCelAnimation *animation,
+                      AnimationPlayback     *playback,
                       GtkWidget             *layer_view)
 {
   GtkWidget *xsheet;
@@ -168,6 +169,10 @@ animation_xsheet_new (AnimationCelAnimation *animation,
                          "animation", animation,
                          "layer-view", layer_view,
                          NULL);
+  ANIMATION_XSHEET (xsheet)->priv->playback = playback;
+  g_signal_connect (ANIMATION_XSHEET (xsheet)->priv->playback,
+                    "render",
+                    G_CALLBACK (on_animation_rendered), xsheet);
 
   return xsheet;
 }
@@ -199,8 +204,6 @@ animation_xsheet_constructed (GObject *object)
   /* Reload everything when we reload the animation. */
   g_signal_connect_after (xsheet->priv->animation, "loaded",
                           G_CALLBACK (on_animation_loaded), xsheet);
-  g_signal_connect (xsheet->priv->animation, "render",
-                    G_CALLBACK (on_animation_rendered), xsheet);
 }
 
 static void
@@ -254,6 +257,9 @@ animation_xsheet_finalize (GObject *object)
 {
   AnimationXSheet *xsheet = ANIMATION_XSHEET (object);
 
+  g_signal_handlers_disconnect_by_func (ANIMATION_XSHEET (xsheet)->priv->playback,
+                                        G_CALLBACK (on_animation_rendered),
+                                        xsheet);
   if (xsheet->priv->animation)
     g_object_unref (xsheet->priv->animation);
   if (xsheet->priv->layer_view)
@@ -478,22 +484,17 @@ on_layer_selection (AnimationLayerView *view,
 
 static void
 on_animation_loaded (Animation       *animation,
-                     gint             num_frames,
-                     gint             playback_start,
-                     gint             playback_stop,
-                     gint             preview_width,
-                     gint             preview_height,
                      AnimationXSheet *xsheet)
 {
   animation_xsheet_reset_layout (xsheet);
 }
 
 static void
-on_animation_rendered (Animation       *animation,
-                       gint             frame_number,
-                       GeglBuffer      *buffer,
-                       gboolean         must_draw_null,
-                       AnimationXSheet *xsheet)
+on_animation_rendered (AnimationPlayback *playback,
+                       gint               frame_number,
+                       GeglBuffer        *buffer,
+                       gboolean           must_draw_null,
+                       AnimationXSheet   *xsheet)
 {
   GtkWidget *button;
 
@@ -522,8 +523,8 @@ animation_xsheet_frame_clicked (GtkWidget       *button,
 
   position = g_object_get_data (G_OBJECT (button), "frame-position");
 
-  animation_jump (ANIMATION (xsheet->priv->animation),
-                  GPOINTER_TO_INT (position) + 1);
+  animation_playback_jump (xsheet->priv->playback,
+                           GPOINTER_TO_INT (position));
   if (xsheet->priv->active_pos_button)
     {
       GtkToggleButton *active_button;
diff --git a/plug-ins/animation-play/widgets/animation-xsheet.h 
b/plug-ins/animation-play/widgets/animation-xsheet.h
index 9346419..4a4dfba 100755
--- a/plug-ins/animation-play/widgets/animation-xsheet.h
+++ b/plug-ins/animation-play/widgets/animation-xsheet.h
@@ -47,6 +47,7 @@ struct _AnimationXSheetClass
 GType       animation_xsheet_get_type (void) G_GNUC_CONST;
 
 GtkWidget * animation_xsheet_new      (AnimationCelAnimation *animation,
+                                       AnimationPlayback     *playback,
                                        GtkWidget             *layer_view);
 
 #endif  /*  __ANIMATION_XSHEET_H__  */


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