[gimp/wip/animation: 309/373] plug-ins: move all the caching to a separate renderer.
- From: Jehan Pagès <jehanp src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gimp/wip/animation: 309/373] plug-ins: move all the caching to a separate renderer.
- Date: Sat, 7 Oct 2017 02:20:42 +0000 (UTC)
commit b0fdeb5b4a74a000f9fe67363bdd090f92b59316
Author: Jehan <jehan girinstud io>
Date: Thu May 18 23:21:06 2017 +0200
plug-ins: move all the caching to a separate renderer.
This is a first step towards a threaded rendering which won't block the
GUI.
plug-ins/animation-play/Makefile.am | 2 +
plug-ins/animation-play/core/animation-animatic.c | 242 +++-----
plug-ins/animation-play/core/animation-animatic.h | 2 +-
.../animation-play/core/animation-celanimation.c | 689 +++++++-------------
plug-ins/animation-play/core/animation-playback.c | 106 ++--
plug-ins/animation-play/core/animation-playback.h | 2 +
plug-ins/animation-play/core/animation-renderer.c | 355 ++++++++++
plug-ins/animation-play/core/animation-renderer.h | 60 ++
plug-ins/animation-play/core/animation.c | 49 +-
plug-ins/animation-play/core/animation.h | 25 +-
plug-ins/animation-play/widgets/animation-dialog.c | 18 +-
plug-ins/animation-play/widgets/animation-xsheet.c | 12 +-
12 files changed, 841 insertions(+), 721 deletions(-)
---
diff --git a/plug-ins/animation-play/Makefile.am b/plug-ins/animation-play/Makefile.am
index b4f88d4..b47a8f1 100644
--- a/plug-ins/animation-play/Makefile.am
+++ b/plug-ins/animation-play/Makefile.am
@@ -53,6 +53,8 @@ animation_play_SOURCES = \
core/animation-celanimation.c \
core/animation-playback.h \
core/animation-playback.c \
+ core/animation-renderer.h \
+ core/animation-renderer.c \
widgets/animation-dialog.h \
widgets/animation-dialog.c \
widgets/animation-keyframe-view.h \
diff --git a/plug-ins/animation-play/core/animation-animatic.c
b/plug-ins/animation-play/core/animation-animatic.c
index 92fe0a0..fd2e789 100644
--- a/plug-ins/animation-play/core/animation-animatic.c
+++ b/plug-ins/animation-play/core/animation-animatic.c
@@ -24,7 +24,9 @@
#include <libgimp/stdplugins-intl.h>
#include "animation-utils.h"
+
#include "animation-animatic.h"
+#include "animation-renderer.h"
typedef enum
{
@@ -61,8 +63,6 @@ typedef struct _AnimationAnimaticPrivate AnimationAnimaticPrivate;
struct _AnimationAnimaticPrivate
{
- /* Panels are cached as GEGL buffers. */
- GeglBuffer **cache;
/* The number of panels. */
gint n_panels;
/* Layers associated to each panel. For serialization. */
@@ -86,14 +86,11 @@ static void animation_animatic_finalize (GObject *object
static gint animation_animatic_get_duration (Animation *animation);
-static GeglBuffer * animation_animatic_get_frame (Animation *animation,
- gint pos);
-
-static gboolean animation_animatic_same (Animation *animation,
- gint pos1,
- gint pos2);
-
-static void animation_animatic_purge_cache (Animation *animation);
+static gchar * animation_animatic_get_frame_hash (Animation *animation,
+ gint position);
+static GeglBuffer * animation_animatic_create_frame (Animation *animation,
+ GObject *renderer,
+ gint position);
static void animation_animatic_reset_defaults (Animation *animation);
static gchar * animation_animatic_serialize (Animation *animation,
@@ -121,12 +118,6 @@ static void animation_animatic_text (GMarkupParseContext *context
gpointer user_data,
GError **error);
-/* Utils */
-
-static void animation_animatic_cache_panel (AnimationAnimatic *animation,
- gint panel,
- gboolean recursion);
-
/* Tag handling (from layer names) */
static gint parse_ms_tag (Animation *animation,
@@ -174,11 +165,8 @@ animation_animatic_class_init (AnimationAnimaticClass *klass)
object_class->finalize = animation_animatic_finalize;
anim_class->get_duration = animation_animatic_get_duration;
- anim_class->get_frame = animation_animatic_get_frame;
-
- anim_class->same = animation_animatic_same;
-
- anim_class->purge_cache = animation_animatic_purge_cache;
+ anim_class->get_frame_hash = animation_animatic_get_frame_hash;
+ anim_class->create_frame = animation_animatic_create_frame;
anim_class->reset_defaults = animation_animatic_reset_defaults;
anim_class->serialize = animation_animatic_serialize;
@@ -212,15 +200,6 @@ animation_animatic_finalize (GObject *object)
}
g_free (priv->comments);
}
- if (priv->cache)
- {
- for (i = 0; i < priv->n_panels; i++)
- {
- if (priv->cache[i])
- g_object_unref (priv->cache[i]);
- }
- g_free (priv->cache);
- }
G_OBJECT_CLASS (parent_class)->finalize (object);
}
@@ -303,8 +282,32 @@ animation_animatic_set_combine (AnimationAnimatic *animatic,
if (priv->combine[panel_num] != combine)
{
+ gint i;
+
priv->combine[panel_num] = combine;
- animation_animatic_cache_panel (animatic, panel_num, TRUE);
+
+ if (animation_animatic_get_panel_duration (animatic, panel_num) > 0)
+ g_signal_emit_by_name (animatic, "frames-changed",
+ animation_animatic_get_position (animatic,
+ panel_num),
+ animation_animatic_get_panel_duration (animatic,
+ panel_num));
+
+ /* If next panel is in "combine" mode, it must also be re-cached.
+ * And so on, recursively. */
+ for (i = panel_num + 1; i < priv->n_panels; i++)
+ {
+ if (priv->combine[i])
+ {
+ g_signal_emit_by_name (animatic, "frames-changed",
+ animation_animatic_get_position (animatic, i),
+ animation_animatic_get_panel_duration (animatic, i));
+ }
+ else
+ {
+ break;
+ }
+ }
}
}
@@ -382,64 +385,78 @@ animation_animatic_get_duration (Animation *animation)
return count;
}
-static GeglBuffer *
-animation_animatic_get_frame (Animation *animation,
- gint pos)
+static gchar *
+animation_animatic_get_frame_hash (Animation *animation,
+ gint position)
{
- AnimationAnimaticPrivate *priv;
- GeglBuffer *frame = NULL;
+ AnimationAnimaticPrivate *priv = GET_PRIVATE (animation);
+ gchar *hash;
gint panel;
- priv = GET_PRIVATE (animation);
panel = animation_animatic_get_panel (ANIMATION_ANIMATIC (animation),
- pos);
- if (priv->cache[panel])
- frame = g_object_ref (priv->cache[panel]);
-
- return frame;
+ position);
+ hash = g_strdup_printf ("%d;", priv->tattoos[panel]);
+ while (panel > 0 && priv->combine[panel--])
+ {
+ gchar *tmp = hash;
+ hash = g_strdup_printf ("%s%d;", hash, priv->tattoos[panel]);
+ g_free (tmp);
+ }
+ return hash;
}
-static gboolean
-animation_animatic_same (Animation *animation,
- gint pos1,
- gint pos2)
+static GeglBuffer *
+animation_animatic_create_frame (Animation *animation,
+ GObject *renderer,
+ gint position)
{
AnimationAnimaticPrivate *priv = GET_PRIVATE (animation);
- gint count = 0;
- gboolean identical = FALSE;
- gint i ;
+ GeglBuffer *buffer;
+ GeglBuffer *buffer2;
+ GeglBuffer *backdrop = NULL;
+ gint layer_offx;
+ gint layer_offy;
+ gint preview_width;
+ gint preview_height;
+ gint32 image_id;
+ gint32 layer;
+ gdouble proxy_ratio;
+ gint panel;
- for (i = 0; i < priv->n_panels; i++)
+ panel = animation_animatic_get_panel (ANIMATION_ANIMATIC (animation),
+ position);
+ image_id = animation_get_image_id (animation);
+ layer = gimp_image_get_layer_by_tattoo (image_id,
+ priv->tattoos[panel]);
+ if (! layer)
{
- count += priv->durations[i];
- if (count > pos1 && count > pos2)
- {
- identical = TRUE;
- break;
- }
- else if (count > pos1 || count > pos2)
- {
- identical = FALSE;
- break;
- }
+ g_printerr ("Warning: buffer creation of panel %d failed; "
+ "the associated layer must have been deleted.\n",
+ panel);
+ return NULL;
}
- return identical;
-}
-
-static void
-animation_animatic_purge_cache (Animation *animation)
-{
- AnimationAnimaticPrivate *priv = GET_PRIVATE (animation);
- gint i;
+ proxy_ratio = animation_get_proxy (animation);
+ buffer2 = gimp_drawable_get_buffer (layer);
+ animation_get_size (animation, &preview_width, &preview_height);
+ gimp_drawable_offsets (layer,
+ &layer_offx, &layer_offy);
- for (i = 0; i < priv->n_panels; i++)
+ if (panel > 0 && priv->combine[panel])
{
- animation_animatic_cache_panel (ANIMATION_ANIMATIC (animation), i, FALSE);
- g_signal_emit_by_name (animation, "loading",
- (gdouble) i / ((gdouble) priv->n_panels - 0.999));
+ backdrop = animation_renderer_get_buffer (ANIMATION_RENDERER (renderer),
+ panel - 1);
}
- g_signal_emit_by_name (animation, "loaded");
+
+ buffer = normal_blend (preview_width, preview_height,
+ backdrop, 1.0, 0, 0,
+ buffer2, proxy_ratio,
+ layer_offx, layer_offy);
+ g_object_unref (buffer2);
+ if (backdrop)
+ g_object_unref (backdrop);
+
+ return buffer;
}
static void
@@ -457,11 +474,8 @@ animation_animatic_reset_defaults (Animation *animation)
for (i = 0; i < priv->n_panels; i++)
{
g_free (priv->comments[i]);
- if (priv->cache[i])
- g_object_unref (priv->cache[i]);
}
g_free (priv->comments);
- g_free (priv->cache);
image_id = animation_get_image_id (animation);
layers = gimp_image_get_layers (image_id, &priv->n_panels);
@@ -470,10 +484,8 @@ animation_animatic_reset_defaults (Animation *animation)
priv->durations = g_try_malloc0_n (priv->n_panels, sizeof (gint));
priv->combine = g_try_malloc0_n (priv->n_panels, sizeof (gboolean));
priv->comments = g_try_malloc0_n (priv->n_panels, sizeof (gchar*));
- priv->cache = g_try_malloc0_n (priv->n_panels, sizeof (GeglBuffer*));
if (! priv->tattoos || ! priv->durations ||
- ! priv->combine || ! priv->comments ||
- ! priv->cache)
+ ! priv->combine || ! priv->comments)
{
gimp_message (_("Memory could not be allocated to the animatic."));
gimp_quit ();
@@ -841,76 +853,6 @@ animation_animatic_text (GMarkupParseContext *context,
}
}
-/**** Utils ****/
-
-static void
-animation_animatic_cache_panel (AnimationAnimatic *animatic,
- gint panel,
- gboolean recursion)
-{
- AnimationAnimaticPrivate *priv = GET_PRIVATE (animatic);
- Animation *animation = ANIMATION (animatic);
- GeglBuffer *buffer;
- GeglBuffer *backdrop_buffer = NULL;
- gint layer_offx;
- gint layer_offy;
- gint preview_width;
- gint preview_height;
- gint32 image_id;
- gint32 layer;
- gdouble proxy_ratio;
-
- image_id = animation_get_image_id (animation);
- layer = gimp_image_get_layer_by_tattoo (image_id,
- priv->tattoos[panel]);
- if (! layer)
- {
- g_printerr ("Warning: caching of panel %d failed; "
- "the associated layer must have been deleted.\n",
- panel);
- return;
- }
-
- /* Destroy existing cache. */
- if (priv->cache[panel])
- {
- g_object_unref (priv->cache[panel]);
- }
-
- proxy_ratio = animation_get_proxy (animation);
- buffer = gimp_drawable_get_buffer (layer);
- animation_get_size (animation, &preview_width, &preview_height);
- gimp_drawable_offsets (layer,
- &layer_offx, &layer_offy);
-
- if (panel > 0 && priv->combine[panel])
- {
- backdrop_buffer = priv->cache[panel - 1];
- }
-
- priv->cache[panel] = normal_blend (preview_width, preview_height,
- backdrop_buffer, 1.0, 0, 0,
- buffer, proxy_ratio,
- layer_offx, layer_offy);
- g_object_unref (buffer);
-
- if (animation_animatic_get_panel_duration (animatic, panel) > 0)
- g_signal_emit_by_name (animation, "cache-invalidated",
- animation_animatic_get_position (animatic,
- panel),
- animation_animatic_get_panel_duration (animatic,
- panel));
-
- /* If next panel is in "combine" mode, it must also be re-cached.
- * And so on, recursively. */
- if (recursion &&
- panel < priv->n_panels - 1 &&
- priv->combine[panel + 1])
- {
- animation_animatic_cache_panel (animatic, panel + 1, TRUE);
- }
-}
-
/**** TAG UTILS ****/
static gint
diff --git a/plug-ins/animation-play/core/animation-animatic.h
b/plug-ins/animation-play/core/animation-animatic.h
index fccd85c..7860c0a 100644
--- a/plug-ins/animation-play/core/animation-animatic.h
+++ b/plug-ins/animation-play/core/animation-animatic.h
@@ -64,7 +64,7 @@ const gboolean animation_animatic_get_combine (AnimationAnimatic *anima
gint panel);
gint animation_animatic_get_panel (AnimationAnimatic *animation,
- gint pos);
+ gint position);
gint animation_animatic_get_position (AnimationAnimatic *animation,
gint panel);
diff --git a/plug-ins/animation-play/core/animation-celanimation.c
b/plug-ins/animation-play/core/animation-celanimation.c
index 5ec24e6..cbe2bb5 100644
--- a/plug-ins/animation-play/core/animation-celanimation.c
+++ b/plug-ins/animation-play/core/animation-celanimation.c
@@ -27,7 +27,6 @@
#include "animation.h"
#include "animation-camera.h"
-
#include "animation-celanimation.h"
typedef struct _AnimationCelAnimationPrivate AnimationCelAnimationPrivate;
@@ -35,41 +34,16 @@ typedef struct _AnimationCelAnimationPrivate AnimationCelAnimationPrivate;
typedef struct
{
gchar *title;
- /* The list of list of layers (identified by their tattoos). */
+ /* List of list of layers identified by tattoos. */
GList *frames;
}
Track;
-typedef struct
-{
- gint tattoo;
- gint offset_x;
- gint offset_y;
-}
-CompLayer;
-
-typedef struct
-{
- GeglBuffer *buffer;
-
- /* The list of layers (identified by their tattoos) composited into
- * this buffer allows to easily compare caches so that to not
- * duplicate them.*/
- CompLayer *composition;
- gint n_sources;
-
- gint refs;
-}
-Cache;
-
struct _AnimationCelAnimationPrivate
{
/* The number of frames. */
gint duration;
- /* Frames are cached as GEGL buffers. */
- GList *cache;
-
/* Panel comments. */
GList *comments;
@@ -111,30 +85,27 @@ typedef struct
ANIMATION_TYPE_CEL_ANIMATION, \
AnimationCelAnimationPrivate)
-static void animation_cel_animation_finalize (GObject *object);
+static void animation_cel_animation_finalize (GObject *object);
/* Virtual methods */
-static gint animation_cel_animation_get_duration (Animation *animation);
+static gint animation_cel_animation_get_duration (Animation *animation);
-static GeglBuffer * animation_cel_animation_get_frame (Animation *animation,
- gint pos);
+static gchar * animation_cel_animation_get_frame_hash (Animation *animation,
+ gint position);
+static GeglBuffer * animation_cel_animation_create_frame (Animation *animation,
+ GObject *renderer,
+ gint position);
-static gboolean animation_cel_animation_same (Animation *animation,
- gint previous_pos,
- gint next_pos);
+static void animation_cel_animation_reset_defaults (Animation *animation);
+static gchar * animation_cel_animation_serialize (Animation *animation,
+ const gchar *playback_xml);
+static gboolean animation_cel_animation_deserialize (Animation *animation,
+ const gchar *xml,
+ GError **error);
-static void animation_cel_animation_purge_cache (Animation *animation);
-
-static void animation_cel_animation_reset_defaults (Animation *animation);
-static gchar * animation_cel_animation_serialize (Animation *animation,
- const gchar *playback_xml);
-static gboolean animation_cel_animation_deserialize (Animation *animation,
- const gchar *xml,
- GError **error);
-
-static void animation_cel_animation_update_paint_view (Animation *animation,
- gint position);
+static void animation_cel_animation_update_paint_view (Animation *animation,
+ gint position);
/* XML parsing */
@@ -163,12 +134,7 @@ static void on_camera_offsets_changed (AnimationCamera
AnimationCelAnimation *animation);
/* Utils */
-static void animation_cel_animation_cache (AnimationCelAnimation *animation,
- gint position);
static void animation_cel_animation_cleanup (AnimationCelAnimation *animation);
-static gboolean animation_cel_animation_cache_cmp (Cache *cache1,
- Cache *cache2);
-static void animation_cel_animation_clean_cache (Cache *cache);
static void animation_cel_animation_clean_track (Track *track);
G_DEFINE_TYPE (AnimationCelAnimation, animation_cel_animation, ANIMATION_TYPE_ANIMATION)
@@ -184,10 +150,9 @@ animation_cel_animation_class_init (AnimationCelAnimationClass *klass)
object_class->finalize = animation_cel_animation_finalize;
anim_class->get_duration = animation_cel_animation_get_duration;
- anim_class->get_frame = animation_cel_animation_get_frame;
- anim_class->same = animation_cel_animation_same;
- anim_class->purge_cache = animation_cel_animation_purge_cache;
+ anim_class->get_frame_hash = animation_cel_animation_get_frame_hash;
+ anim_class->create_frame = animation_cel_animation_create_frame;
anim_class->reset_defaults = animation_cel_animation_reset_defaults;
anim_class->serialize = animation_cel_animation_serialize;
@@ -223,39 +188,40 @@ animation_cel_animation_set_layers (AnimationCelAnimation *animation,
const GList *new_layers)
{
Track *track;
+ GList *layers;
track = g_list_nth_data (animation->priv->tracks, level);
+ g_return_if_fail (track && position >= 0 &&
+ position < animation->priv->duration);
- if (track)
- {
- GList *layers = g_list_nth (track->frames, position);
+ layers = g_list_nth (track->frames, position);
- if (! layers)
- {
- gint frames_length = g_list_length (track->frames);
- gint i;
+ if (! layers)
+ {
+ gint frames_length = g_list_length (track->frames);
+ gint i;
- track->frames = g_list_reverse (track->frames);
- for (i = frames_length; i < position + 1; i++)
- {
- track->frames = g_list_prepend (track->frames, NULL);
- layers = track->frames;
- }
- track->frames = g_list_reverse (track->frames);
- }
- /* Clean out previous layer list. */
- g_list_free (layers->data);
- if (new_layers)
+ track->frames = g_list_reverse (track->frames);
+ for (i = frames_length; i < position + 1; i++)
{
- layers->data = g_list_copy ((GList *) new_layers);
+ track->frames = g_list_prepend (track->frames, NULL);
+ layers = track->frames;
}
- else
- {
- layers->data = NULL;
- }
- animation_cel_animation_cache (animation, position);
- g_signal_emit_by_name (animation, "cache-invalidated", position, 1);
+ track->frames = g_list_reverse (track->frames);
+ }
+
+ /* Clean out previous layer list. */
+ g_list_free (layers->data);
+ if (new_layers)
+ {
+ layers->data = g_list_copy ((GList *) new_layers);
+ }
+ else
+ {
+ layers->data = NULL;
}
+
+ g_signal_emit_by_name (animation, "frames-changed", position, 1);
}
const GList *
@@ -263,17 +229,14 @@ animation_cel_animation_get_layers (AnimationCelAnimation *animation,
gint level,
gint position)
{
- GList *layers = NULL;
Track *track;
track = g_list_nth_data (animation->priv->tracks, level);
+ g_return_val_if_fail (track && position >= 0 &&
+ position < animation->priv->duration,
+ NULL);
- if (track)
- {
- layers = g_list_nth_data (track->frames, position);
- }
-
- return layers;
+ return g_list_nth_data (track->frames, position);
}
void
@@ -328,14 +291,6 @@ animation_cel_animation_set_duration (AnimationCelAnimation *animation,
GList *iter;
/* Free memory. */
- iter = g_list_nth (animation->priv->cache, duration);
- if (iter && iter->prev)
- {
- iter->prev->next = NULL;
- iter->prev = NULL;
- }
- g_list_free_full (iter, (GDestroyNotify) animation_cel_animation_clean_cache);
-
iter = g_list_nth (animation->priv->tracks, duration);
if (iter && iter->prev)
{
@@ -391,46 +346,39 @@ gint
animation_cel_animation_level_up (AnimationCelAnimation *animation,
gint level)
{
- gint tracks_n = g_list_length (animation->priv->tracks);
+ GList *track;
+ GList *prev_track;
+ GList *next_track;
+ GList *iter;
+ gint i;
- g_return_val_if_fail (level >= 0 && level < tracks_n, level);
+ g_return_val_if_fail (level >= 0 &&
+ level < g_list_length (animation->priv->tracks) - 1,
+ level);
- if (level < tracks_n - 1)
+ track = g_list_nth (animation->priv->tracks, level);
+ prev_track = track->prev;
+ next_track = track->next;
+
+ if (prev_track)
+ prev_track->next = next_track;
+ else
+ animation->priv->tracks = next_track;
+ next_track->prev = prev_track;
+ track->prev = next_track;
+ track->next = next_track->next;
+ next_track->next = track;
+
+ level++;
+
+ iter = ((Track *) track->data)->frames;
+ for (i = 0; iter; iter = iter->next, i++)
{
- GList *item = g_list_nth (animation->priv->tracks, level);
- GList *prev = item->prev;
- GList *next = item->next;
- Track *track;
- GList *iter;
- gint i;
-
- if (prev)
- prev->next = next;
- else
- animation->priv->tracks = next;
- next->prev = prev;
- item->prev = next;
- item->next = next->next;
- next->next = item;
-
- level++;
-
- track = item->data;
- iter = track->frames;
- for (i = 0; iter; iter = iter->next, i++)
+ if (iter->data)
{
- g_signal_emit_by_name (animation, "loading",
- (gdouble) i / ((gdouble) animation->priv->duration - 0.999));
-
- if (iter->data)
- {
- /* Only cache if the track had contents for this frame. */
- animation_cel_animation_cache (animation, i);
- }
+ /* Only cache if the track had contents for this frame. */
+ g_signal_emit_by_name (animation, "frames-changed", i, 1);
}
- g_signal_emit_by_name (animation, "cache-invalidated",
- 0, g_list_length (track->frames));
- g_signal_emit_by_name (animation, "loaded");
}
return level;
@@ -440,46 +388,39 @@ gint
animation_cel_animation_level_down (AnimationCelAnimation *animation,
gint level)
{
- gint tracks_n = g_list_length (animation->priv->tracks);
+ GList *track;
+ GList *prev_track;
+ GList *next_track;
+ GList *iter;
+ gint i;
- g_return_val_if_fail (level >= 0 && level < tracks_n, level);
+ g_return_val_if_fail (level > 0 &&
+ level < g_list_length (animation->priv->tracks),
+ level);
- if (level > 0)
+ track = g_list_nth (animation->priv->tracks, level);
+ prev_track = track->prev;
+ next_track = track->next;
+
+ if (! prev_track->prev)
+ animation->priv->tracks = track;
+ if (next_track)
+ next_track->prev = prev_track;
+ prev_track->next = next_track;
+ track->next = prev_track;
+ track->prev = prev_track->prev;
+ prev_track->prev = track;
+
+ level--;
+
+ iter = ((Track *) track->data)->frames;
+ for (i = 0; iter; iter = iter->next, i++)
{
- GList *item = g_list_nth (animation->priv->tracks, level);
- GList *prev = item->prev;
- GList *next = item->next;
- Track *track;
- GList *iter;
- gint i;
-
- if (! prev->prev)
- animation->priv->tracks = item;
- if (next)
- next->prev = prev;
- prev->next = next;
- item->next = prev;
- item->prev = prev->prev;
- prev->prev = item;
-
- level--;
-
- track = item->data;
- iter = track->frames;
- for (i = 0; iter; iter = iter->next, i++)
+ if (iter->data)
{
- g_signal_emit_by_name (animation, "loading",
- (gdouble) i / ((gdouble) animation->priv->duration - 0.999));
-
- if (iter->data)
- {
- /* Only cache if the track had contents for this frame. */
- animation_cel_animation_cache (animation, i);
- }
+ /* Only cache if the track had contents for this frame. */
+ g_signal_emit_by_name (animation, "frames-changed", i, 1);
}
- g_signal_emit_by_name (animation, "cache-invalidated",
- 0, g_list_length (track->frames));
- g_signal_emit_by_name (animation, "loaded");
}
return level;
@@ -489,36 +430,31 @@ gboolean
animation_cel_animation_level_delete (AnimationCelAnimation *animation,
gint level)
{
- gint tracks_n = g_list_length (animation->priv->tracks);
- GList *item;
- GList *iter;
- Track *track;
- gint i;
+ gint tracks_n = g_list_length (animation->priv->tracks);
g_return_val_if_fail (level >= 0 && level < tracks_n, FALSE);
/* Do not remove when there is only a single level. */
if (tracks_n > 1)
{
+ Track *track;
+ GList *item;
+ GList *iter;
+ gint i;
+
item = g_list_nth (animation->priv->tracks, level);
track = item->data;
- animation->priv->tracks = g_list_delete_link (animation->priv->tracks, item);
-
+ animation->priv->tracks = g_list_delete_link (animation->priv->tracks,
+ item);
iter = track->frames;
for (i = 0; iter; iter = iter->next, i++)
{
- g_signal_emit_by_name (animation, "loading",
- (gdouble) i / ((gdouble) animation->priv->duration - 0.999));
-
if (iter->data)
{
/* Only cache if the track had contents for this frame. */
- animation_cel_animation_cache (animation, i);
+ g_signal_emit_by_name (animation, "frames-changed", i, 1);
}
}
- g_signal_emit_by_name (animation, "cache-invalidated",
- 0, g_list_length (track->frames));
- g_signal_emit_by_name (animation, "loaded");
animation_cel_animation_clean_track (track);
return TRUE;
@@ -600,15 +536,8 @@ animation_cel_animation_cel_delete (AnimationCelAnimation *animation,
for (i = position; iter; iter = iter->next, i++)
{
- g_signal_emit_by_name (animation, "loading",
- (gdouble) i / ((gdouble) animation->priv->duration - 0.999));
-
- animation_cel_animation_cache (animation, i);
+ g_signal_emit_by_name (animation, "frames-changed", i, 1);
}
- if (i > position)
- g_signal_emit_by_name (animation, "cache-invalidated",
- position, i - position);
- g_signal_emit_by_name (animation, "loaded");
return TRUE;
}
@@ -653,16 +582,8 @@ animation_cel_animation_cel_add (AnimationCelAnimation *animation,
{
for (; cel; cel = cel->next, i++)
{
- g_signal_emit_by_name (animation, "loading",
- (gdouble) i / ((gdouble) animation->priv->duration - 0.999));
-
- animation_cel_animation_cache (animation, i);
+ g_signal_emit_by_name (animation, "frames-changed", i, 1);
}
- if (i > position)
- g_signal_emit_by_name (animation, "cache-invalidated",
- position, i - position);
- g_signal_emit_by_name (animation, "loaded");
-
return TRUE;
}
}
@@ -677,63 +598,120 @@ animation_cel_animation_get_duration (Animation *animation)
return ANIMATION_CEL_ANIMATION (animation)->priv->duration;
}
-static GeglBuffer *
-animation_cel_animation_get_frame (Animation *animation,
- gint pos)
+static gchar *
+animation_cel_animation_get_frame_hash (Animation *animation,
+ gint position)
{
AnimationCelAnimation *cel_animation;
- Cache *cache;
- GeglBuffer *frame = NULL;
+ gchar *hash = g_strdup ("");
+ GList *iter;
+ gint main_offset_x;
+ gint main_offset_y;
cel_animation = ANIMATION_CEL_ANIMATION (animation);
+ animation_camera_get (cel_animation->priv->camera,
+ position, &main_offset_x, &main_offset_y);
+
+ /* Create the new buffer layer composition. */
+ for (iter = cel_animation->priv->tracks; iter; iter = iter->next)
+ {
+ Track *track = iter->data;
+ GList *layers;
+ GList *layer;
- cache = g_list_nth_data (cel_animation->priv->cache,
- pos);
- if (cache)
+ layers = g_list_nth_data (track->frames, position);
+
+ for (layer = layers; layer; layer = layer->next)
+ {
+ gint tattoo;
+
+ tattoo = GPOINTER_TO_INT (layer->data);
+ if (tattoo)
+ {
+ gchar *tmp = hash;
+ hash = g_strdup_printf ("%s[%d,%d]%d;",
+ hash,
+ main_offset_x, main_offset_y,
+ tattoo);
+ g_free (tmp);
+ }
+ }
+ }
+ if (strlen (hash) == 0)
{
- frame = g_object_ref (cache->buffer);
+ g_free (hash);
+ hash = NULL;
}
- return frame;
+ return hash;
+
}
-static gboolean
-animation_cel_animation_same (Animation *animation,
- gint pos1,
- gint pos2)
+static GeglBuffer *
+animation_cel_animation_create_frame (Animation *animation,
+ GObject *renderer G_GNUC_UNUSED,
+ gint position)
{
AnimationCelAnimation *cel_animation;
- Cache *cache1;
- Cache *cache2;
+ GeglBuffer *buffer = NULL;
+ GList *iter;
+ gint32 image_id;
+ gdouble proxy_ratio;
+ gint preview_width;
+ gint preview_height;
+ gint offset_x;
+ gint offset_y;
cel_animation = ANIMATION_CEL_ANIMATION (animation);
+ image_id = animation_get_image_id (animation);
+ proxy_ratio = animation_get_proxy (animation);
+ animation_get_size (animation,
+ &preview_width, &preview_height);
+ animation_camera_get (cel_animation->priv->camera,
+ position, &offset_x, &offset_y);
- g_return_val_if_fail (pos1 >= 0 &&
- pos1 < cel_animation->priv->duration &&
- pos2 >= 0 &&
- pos2 < cel_animation->priv->duration,
- FALSE);
+ for (iter = cel_animation->priv->tracks; iter; iter = iter->next)
+ {
+ Track *track = iter->data;
+ GList *layers;
+ GList *iter2;
- cache1 = g_list_nth_data (cel_animation->priv->cache, pos1);
- cache2 = g_list_nth_data (cel_animation->priv->cache, pos2);
+ layers = g_list_nth_data (track->frames, position);
- return animation_cel_animation_cache_cmp (cache1, cache2);
-}
+ for (iter2 = layers; iter2; iter2 = iter2->next)
+ {
+ GeglBuffer *source = NULL;
+ GeglBuffer *intermediate;
+ gint layer_offx;
+ gint layer_offy;
+ gint32 layer;
+ gint tattoo;
-static void
-animation_cel_animation_purge_cache (Animation *animation)
-{
- gint duration;
- gint i;
+ tattoo = GPOINTER_TO_INT (iter2->data);
- duration = animation_get_duration (animation);
- for (i = 0; i < duration; i++)
- {
- animation_cel_animation_cache (ANIMATION_CEL_ANIMATION (animation),
- i);
- g_signal_emit_by_name (animation, "loading",
- (gdouble) i / ((gdouble) duration - 0.999));
+ layer = gimp_image_get_layer_by_tattoo (image_id, tattoo);
+ if (layer > 0)
+ source = gimp_drawable_get_buffer (layer);
+ if (layer <= 0 || ! source)
+ {
+ g_printerr ("Warning: a layer used for frame %d has been deleted.\n",
+ position);
+ continue;
+ }
+ gimp_drawable_offsets (layer, &layer_offx, &layer_offy);
+ intermediate = normal_blend (preview_width, preview_height,
+ buffer, 1.0, 0, 0,
+ source, proxy_ratio,
+ layer_offx + offset_x,
+ layer_offy + offset_y);
+ g_object_unref (source);
+ if (buffer)
+ {
+ g_object_unref (buffer);
+ }
+ buffer = intermediate;
+ }
}
- g_signal_emit_by_name (animation, "loaded");
+ return buffer;
}
static void
@@ -808,7 +786,7 @@ animation_cel_animation_serialize (Animation *animation,
{
Track *track = iter->data;
GList *iter2;
- gint pos;
+ gint position;
gint duration;
xml2 = g_markup_printf_escaped ("<sequence name=\"%s\">",
@@ -818,7 +796,7 @@ animation_cel_animation_serialize (Animation *animation,
g_free (tmp);
g_free (xml2);
- pos = 1;
+ position = 1;
duration = 0;
for (iter2 = track->frames; iter2; iter2 = iter2->next)
{
@@ -851,7 +829,8 @@ animation_cel_animation_serialize (Animation *animation,
/* Open tag. */
xml2 = g_markup_printf_escaped ("<frame position=\"%d\""
" duration=\"%d\">",
- pos - duration, duration);
+ position - duration,
+ duration);
tmp = xml;
xml = g_strconcat (xml, xml2, NULL);
g_free (tmp);
@@ -879,7 +858,7 @@ animation_cel_animation_serialize (Animation *animation,
duration = 0;
}
}
- pos++;
+ position++;
}
tmp = xml;
@@ -956,6 +935,8 @@ animation_cel_animation_deserialize (Animation *animation,
g_signal_connect (cel_animation->priv->camera, "offsets-changed",
G_CALLBACK (on_camera_offsets_changed),
animation);
+ g_signal_emit_by_name (animation, "frames-changed", 0,
+ cel_animation->priv->duration);
}
g_markup_parse_context_free (context);
@@ -967,12 +948,13 @@ animation_cel_animation_update_paint_view (Animation *animation,
gint position)
{
AnimationCelAnimation *cel_animation;
- Cache *cache;
gint *layers;
+ GList *iter;
gint num_layers;
gint32 image_id;
gint i;
+ cel_animation = ANIMATION_CEL_ANIMATION (animation);
image_id = animation_get_image_id (animation);
/* Hide all layers. */
@@ -983,21 +965,21 @@ animation_cel_animation_update_paint_view (Animation *animation,
}
/* Show layers */
- cel_animation = ANIMATION_CEL_ANIMATION (animation);
-
- cache = g_list_nth_data (cel_animation->priv->cache,
- position);
- if (cache)
+ for (iter = cel_animation->priv->tracks; iter; iter = iter->next)
{
- gint i;
+ Track *track = iter->data;
+ GList *frame_layers;
+ GList *iter2;
- for (i = 0; i < cache->n_sources; i++)
+ frame_layers = g_list_nth_data (track->frames, position);
+
+ for (iter2 = frame_layers; iter2; iter2 = iter2->next)
{
- gint tattoo = cache->composition[i].tattoo;
+ gint tattoo;
gint layer;
+ tattoo = GPOINTER_TO_INT (iter2->data);
layer = gimp_image_get_layer_by_tattoo (image_id, tattoo);
-
show_item (layer, GIMP_COLOR_TAG_RED);
}
}
@@ -1310,191 +1292,15 @@ on_camera_offsets_changed (AnimationCamera *camera,
gint duration,
AnimationCelAnimation *animation)
{
- gint i;
-
- for (i = position; i < position + duration; i++)
- animation_cel_animation_cache (animation, i);
-
- g_signal_emit_by_name (animation, "cache-invalidated",
+ g_signal_emit_by_name (animation, "frames-changed",
position, duration);
}
/**** Utils ****/
static void
-animation_cel_animation_cache (AnimationCelAnimation *animation,
- gint pos)
-{
- GeglBuffer *backdrop = NULL;
- GList *iter;
- Cache *cache;
- CompLayer *composition;
- gint n_sources = 0;
- gint32 image_id;
- gdouble proxy_ratio;
- gint preview_width;
- gint preview_height;
- gint i;
- gint main_offset_x;
- gint main_offset_y;
-
- /* Clean out current cache. */
- iter = g_list_nth (animation->priv->cache, pos);
- if (iter && iter->data)
- {
- Cache *cache = iter->data;
-
- if (--(cache->refs) == 0)
- {
- g_free (cache->composition);
- g_object_unref (cache->buffer);
- g_free (cache);
- }
- iter->data = NULL;
- }
-
- /* Check if new configuration needs caching. */
- for (iter = animation->priv->tracks; iter; iter = iter->next)
- {
- Track *track = iter->data;
- GList *layers;
-
- layers = g_list_nth_data (track->frames, pos);
-
- n_sources += g_list_length (layers);
- }
- if (n_sources == 0)
- {
- return;
- }
-
- /* Make sure the cache list is long enough. */
- if (pos >= g_list_length (animation->priv->cache))
- {
- animation->priv->cache = g_list_reverse (animation->priv->cache);
- for (i = g_list_length (animation->priv->cache); i <= pos; i++)
- {
- animation->priv->cache = g_list_prepend (animation->priv->cache,
- NULL);
- }
- animation->priv->cache = g_list_reverse (animation->priv->cache);
- }
-
- animation_camera_get (animation->priv->camera,
- pos, &main_offset_x, &main_offset_y);
-
- /* Create the new buffer composition. */
- composition = g_new0 (CompLayer, n_sources);
- i = 0;
- for (iter = animation->priv->tracks; iter; iter = iter->next)
- {
- Track *track = iter->data;
- GList *layers;
- GList *layer;
-
- layers = g_list_nth_data (track->frames, pos);
-
- for (layer = layers; layer; layer = layer->next)
- {
- gint tattoo;
-
- tattoo = GPOINTER_TO_INT (layer->data);
- if (tattoo)
- {
- composition[i].tattoo = tattoo;
- composition[i].offset_x = main_offset_x;
- composition[i++].offset_y = main_offset_y;
- }
- }
- }
-
- /* Check if new configuration was not already cached. */
- for (iter = animation->priv->cache; iter; iter = iter->next)
- {
- if (iter->data)
- {
- Cache *cache = iter->data;
- gboolean same = FALSE;
-
- if (n_sources == cache->n_sources)
- {
- same = TRUE;
- for (i = 0; i < n_sources; i++)
- {
- if (cache->composition[i].tattoo != composition[i].tattoo ||
- cache->composition[i].offset_x != composition[i].offset_x ||
- cache->composition[i].offset_y != composition[i].offset_y)
- {
- same = FALSE;
- break;
- }
- }
- if (same)
- {
- /* A buffer with the same contents already exists. */
- g_free (composition);
- (cache->refs)++;
- g_list_nth (animation->priv->cache, pos)->data = cache;
- return;
- }
- }
- }
- }
-
- /* New configuration. Finally compute the cache. */
- cache = g_new0 (Cache, 1);
- cache->refs = 1;
- cache->n_sources = n_sources;
- cache->composition = composition;
-
- image_id = animation_get_image_id (ANIMATION (animation));
- proxy_ratio = animation_get_proxy (ANIMATION (animation));
- animation_get_size (ANIMATION (animation),
- &preview_width, &preview_height);
-
- for (i = 0; i < n_sources; i++)
- {
- GeglBuffer *source;
- GeglBuffer *intermediate;
- gint32 layer;
- gint layer_offx;
- gint layer_offy;
-
- layer = gimp_image_get_layer_by_tattoo (image_id,
- cache->composition[i].tattoo);
- if (layer > 0)
- source = gimp_drawable_get_buffer (layer);
- if (layer <= 0 || ! source)
- {
- g_printerr ("Warning: a layer used for frame %d has been deleted.\n",
- pos);
- continue;
- }
- gimp_drawable_offsets (layer, &layer_offx, &layer_offy);
- intermediate = normal_blend (preview_width, preview_height,
- backdrop, 1.0, 0, 0,
- source, proxy_ratio,
- layer_offx + cache->composition[i].offset_x,
- layer_offy + cache->composition[i].offset_y);
- g_object_unref (source);
- if (backdrop)
- {
- g_object_unref (backdrop);
- }
- backdrop = intermediate;
- }
- cache->buffer = backdrop;
-
- /* This item exists and has a NULL data. */
- g_list_nth (animation->priv->cache, pos)->data = cache;
-}
-
-static void
animation_cel_animation_cleanup (AnimationCelAnimation *animation)
{
- g_list_free_full (animation->priv->cache,
- (GDestroyNotify) animation_cel_animation_clean_cache);
- animation->priv->cache = NULL;
g_list_free_full (animation->priv->comments,
(GDestroyNotify) g_free);
animation->priv->comments = NULL;
@@ -1505,43 +1311,6 @@ animation_cel_animation_cleanup (AnimationCelAnimation *animation)
g_object_unref (animation->priv->camera);
}
-static gboolean
-animation_cel_animation_cache_cmp (Cache *cache1,
- Cache *cache2)
-{
- gboolean identical = FALSE;
-
- if (cache1 && cache2 &&
- cache1->n_sources == cache2->n_sources)
- {
- gint i;
-
- identical = TRUE;
- for (i = 0; i < cache1->n_sources; i++)
- {
- if (cache1->composition[i].tattoo != cache2->composition[i].tattoo ||
- cache1->composition[i].offset_x != cache2->composition[i].offset_x ||
- cache1->composition[i].offset_y != cache2->composition[i].offset_y)
- {
- identical = FALSE;
- break;
- }
- }
- }
- return identical;
-}
-
-static void
-animation_cel_animation_clean_cache (Cache *cache)
-{
- if (cache != NULL && --(cache->refs) == 0)
- {
- g_object_unref (cache->buffer);
- g_free (cache->composition);
- g_free (cache);
- }
-}
-
static void
animation_cel_animation_clean_track (Track *track)
{
diff --git a/plug-ins/animation-play/core/animation-playback.c
b/plug-ins/animation-play/core/animation-playback.c
index 50ca135..b4b2da3 100644
--- a/plug-ins/animation-play/core/animation-playback.c
+++ b/plug-ins/animation-play/core/animation-playback.c
@@ -27,6 +27,7 @@
#include "animation.h"
#include "animation-playback.h"
+#include "animation-renderer.h"
enum
{
@@ -47,6 +48,7 @@ enum
struct _AnimationPlaybackPrivate
{
Animation *animation;
+ GObject *renderer;
/* State of the currently loaded playback. */
gint position;
@@ -67,11 +69,6 @@ typedef struct
gint level;
} ParseStatus;
-#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,
@@ -85,9 +82,8 @@ static void animation_playback_get_property (GObject *
static void on_duration_changed (Animation *animation,
gint duration,
AnimationPlayback *playback);
-static void on_cache_invalidated (Animation *animation,
+static void on_cache_updated (AnimationRenderer *renderer,
gint position,
- gint length,
AnimationPlayback *playback);
/* Timer callback for playback. */
@@ -312,6 +308,16 @@ animation_playback_get_position (AnimationPlayback *playback)
return playback->priv->position;
}
+GeglBuffer *
+animation_playback_get_buffer (AnimationPlayback *playback,
+ gint position)
+{
+ AnimationRenderer *renderer;
+
+ renderer = ANIMATION_RENDERER (playback->priv->renderer);
+ return animation_renderer_get_buffer (renderer, position);
+}
+
gboolean
animation_playback_is_playing (AnimationPlayback *playback)
{
@@ -360,10 +366,10 @@ animation_playback_stop (AnimationPlayback *playback)
void
animation_playback_next (AnimationPlayback *playback)
{
- Animation *animation = playback->priv->animation;
- GeglBuffer *buffer = NULL;
- gint previous_pos = playback->priv->position;
- gboolean identical;
+ AnimationRenderer *renderer;
+ GeglBuffer *buffer = NULL;
+ gint previous_pos = playback->priv->position;
+ gboolean identical;
if (! playback->priv->animation)
return;
@@ -372,12 +378,14 @@ animation_playback_next (AnimationPlayback *playback)
((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);
+ renderer = ANIMATION_RENDERER (playback->priv->renderer);
+ identical = animation_renderer_identical (renderer,
+ previous_pos,
+ playback->priv->position);
if (! identical)
{
- buffer = animation_get_frame (animation, playback->priv->position);
+ buffer = animation_renderer_get_buffer (renderer,
+ playback->priv->position);
}
g_signal_emit (playback, animation_playback_signals[RENDER], 0,
playback->priv->position, buffer, ! identical);
@@ -390,10 +398,10 @@ animation_playback_next (AnimationPlayback *playback)
void
animation_playback_prev (AnimationPlayback *playback)
{
- Animation *animation = playback->priv->animation;
- GeglBuffer *buffer = NULL;
- gint prev_pos = playback->priv->position;
- gboolean identical;
+ AnimationRenderer *renderer;
+ GeglBuffer *buffer = NULL;
+ gint prev_pos = playback->priv->position;
+ gboolean identical;
if (! playback->priv->animation)
return;
@@ -407,12 +415,13 @@ animation_playback_prev (AnimationPlayback *playback)
--playback->priv->position;
}
- identical = ANIMATION_GET_CLASS (animation)->same (animation,
- prev_pos,
- playback->priv->position);
+ renderer = ANIMATION_RENDERER (playback->priv->renderer);
+ identical = animation_renderer_identical (renderer,
+ prev_pos,
+ playback->priv->position);
if (! identical)
{
- buffer = animation_get_frame (animation, playback->priv->position);
+ buffer = animation_renderer_get_buffer (renderer, playback->priv->position);
}
g_signal_emit (playback, animation_playback_signals[RENDER], 0,
playback->priv->position, buffer, ! identical);
@@ -424,10 +433,10 @@ void
animation_playback_jump (AnimationPlayback *playback,
gint index)
{
- Animation *animation = playback->priv->animation;
- GeglBuffer *buffer = NULL;
- gint prev_pos = playback->priv->position;
- gboolean identical;
+ AnimationRenderer *renderer;
+ GeglBuffer *buffer = NULL;
+ gint prev_pos = playback->priv->position;
+ gboolean identical;
if (! playback->priv->animation)
return;
@@ -438,12 +447,13 @@ animation_playback_jump (AnimationPlayback *playback,
else
playback->priv->position = index;
- identical = ANIMATION_GET_CLASS (animation)->same (animation,
- prev_pos,
- playback->priv->position);
+ renderer = ANIMATION_RENDERER (playback->priv->renderer);
+ identical = animation_renderer_identical (renderer,
+ prev_pos,
+ playback->priv->position);
if (! identical)
{
- buffer = animation_get_frame (animation, playback->priv->position);
+ buffer = animation_renderer_get_buffer (renderer, playback->priv->position);
}
g_signal_emit (playback, animation_playback_signals[RENDER], 0,
playback->priv->position, buffer, ! identical);
@@ -545,6 +555,7 @@ animation_playback_finalize (GObject *object)
{
AnimationPlayback *playback = ANIMATION_PLAYBACK (object);
+ g_object_unref (playback->priv->renderer);
if (playback->priv->animation)
g_object_unref (playback->priv->animation);
@@ -567,9 +578,12 @@ animation_playback_set_property (GObject *object,
if (playback->priv->animation)
g_object_unref (playback->priv->animation);
+ if (playback->priv->renderer)
+ g_object_unref (playback->priv->renderer);
animation = g_value_dup_object (value);
playback->priv->animation = animation;
+ playback->priv->renderer = NULL;
if (! animation)
break;
@@ -580,10 +594,12 @@ animation_playback_set_property (GObject *object,
playback->priv->stop = animation_get_duration (animation) - 1;
playback->priv->stop_at_end = TRUE;
- g_signal_connect (animation, "cache-invalidated",
- G_CALLBACK (on_cache_invalidated), playback);
g_signal_connect (animation, "duration-changed",
G_CALLBACK (on_duration_changed), playback);
+
+ playback->priv->renderer = animation_renderer_new (object);
+ g_signal_connect (playback->priv->renderer, "cache-updated",
+ G_CALLBACK (on_cache_updated), playback);
}
break;
@@ -643,23 +659,19 @@ on_duration_changed (Animation *animation,
}
static void
-on_cache_invalidated (Animation *animation,
- gint position,
- gint length,
- AnimationPlayback *playback)
+on_cache_updated (AnimationRenderer *renderer,
+ gint position,
+ AnimationPlayback *playback)
{
- gint cur_position;
-
- cur_position = animation_playback_get_position (playback);
-
- if (cur_position >= position &&
- cur_position < position + length)
+ if (animation_playback_get_position (playback) == position)
{
- GeglBuffer *buffer;
+ AnimationRenderer *renderer;
+ GeglBuffer *buffer;
- buffer = animation_get_frame (animation, cur_position);
- g_signal_emit_by_name (playback, "render",
- cur_position, buffer, TRUE);
+ renderer = ANIMATION_RENDERER (playback->priv->renderer);
+ buffer = animation_renderer_get_buffer (renderer, position);
+ g_signal_emit (playback, animation_playback_signals[RENDER], 0,
+ position, buffer, TRUE);
if (buffer)
{
g_object_unref (buffer);
diff --git a/plug-ins/animation-play/core/animation-playback.h
b/plug-ins/animation-play/core/animation-playback.h
index 08d59ff..145ac57 100644
--- a/plug-ins/animation-play/core/animation-playback.h
+++ b/plug-ins/animation-play/core/animation-playback.h
@@ -68,6 +68,8 @@ void animation_playback_set_animation (AnimationPlayback *playback,
Animation * animation_playback_get_animation (AnimationPlayback *playback);
gint animation_playback_get_position (AnimationPlayback *playback);
+GeglBuffer * animation_playback_get_buffer (AnimationPlayback *playback,
+ gint position);
gboolean animation_playback_is_playing (AnimationPlayback *playback);
void animation_playback_play (AnimationPlayback *playback);
diff --git a/plug-ins/animation-play/core/animation-renderer.c
b/plug-ins/animation-play/core/animation-renderer.c
new file mode 100644
index 0000000..272439e
--- /dev/null
+++ b/plug-ins/animation-play/core/animation-renderer.c
@@ -0,0 +1,355 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * animation-renderer.c
+ * Copyright (C) 2017 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 <libgimp/gimp.h>
+#include <libgimp/stdplugins-intl.h>
+
+#include "animation.h"
+#include "animation-playback.h"
+#include "animation-renderer.h"
+
+enum
+{
+ CACHE_UPDATED,
+ LAST_SIGNAL
+};
+enum
+{
+ PROP_0,
+ PROP_PLAYBACK
+};
+
+struct _AnimationRendererPrivate
+{
+ AnimationPlayback *playback;
+
+ /* Frames are cached as GEGL buffers. */
+ GeglBuffer **cache;
+ gchar **hashes;
+ GHashTable *cache_table;
+ gint cache_size;
+};
+
+static void animation_renderer_finalize (GObject *object);
+static void animation_renderer_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec);
+static void animation_renderer_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec);
+
+static void on_frames_changed (Animation *animation,
+ gint position,
+ gint length,
+ AnimationRenderer *renderer);
+static void on_duration_changed (Animation *animation,
+ gint duration,
+ AnimationRenderer *renderer);
+
+G_DEFINE_TYPE (AnimationRenderer, animation_renderer, G_TYPE_OBJECT)
+
+#define parent_class animation_renderer_parent_class
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+static void
+animation_renderer_class_init (AnimationRendererClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ /**
+ * Animation::cache-updated:
+ * @animation: the animation.
+ * @position: the frame position whose cache was updated.
+ *
+ * The ::cache-updated signal will be emitted when the contents
+ * of frame at @position changes.
+ */
+ signals[CACHE_UPDATED] =
+ g_signal_new ("cache-updated",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (AnimationRendererClass, cache_updated),
+ NULL, NULL,
+ NULL,
+ G_TYPE_NONE,
+ 1,
+ G_TYPE_INT);
+
+ object_class->finalize = animation_renderer_finalize;
+ object_class->set_property = animation_renderer_set_property;
+ object_class->get_property = animation_renderer_get_property;
+
+ /**
+ * AnimationRenderer:animation:
+ *
+ * The associated #Animation.
+ */
+ g_object_class_install_property (object_class, PROP_PLAYBACK,
+ g_param_spec_object ("playback",
+ NULL, NULL,
+ ANIMATION_TYPE_PLAYBACK,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY));
+
+ g_type_class_add_private (klass, sizeof (AnimationRendererPrivate));
+}
+
+static void
+animation_renderer_init (AnimationRenderer *renderer)
+{
+ renderer->priv = G_TYPE_INSTANCE_GET_PRIVATE (renderer,
+ ANIMATION_TYPE_RENDERER,
+ AnimationRendererPrivate);
+ renderer->priv->cache_table = g_hash_table_new_full (g_str_hash, g_str_equal,
+ (GDestroyNotify) g_free,
+ (GDestroyNotify) g_weak_ref_clear);
+}
+
+static void
+animation_renderer_finalize (GObject *object)
+{
+ AnimationRenderer *renderer = ANIMATION_RENDERER (object);
+ Animation *animation;
+ gint i;
+
+ animation = animation_playback_get_animation (renderer->priv->playback);
+
+ for (i = 0; i < animation_get_duration (animation); i++)
+ {
+ if (renderer->priv->cache[i])
+ g_object_unref (renderer->priv->cache[i]);
+ if (renderer->priv->hashes[i])
+ g_free (renderer->priv->hashes[i]);
+ }
+ g_free (renderer->priv->cache);
+ g_free (renderer->priv->hashes);
+ g_hash_table_destroy (renderer->priv->cache_table);
+
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static void
+animation_renderer_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ AnimationRenderer *renderer = ANIMATION_RENDERER (object);
+
+ switch (property_id)
+ {
+ case PROP_PLAYBACK:
+ renderer->priv->playback = g_value_get_object (value);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+animation_renderer_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ AnimationRenderer *renderer = ANIMATION_RENDERER (object);
+
+ switch (property_id)
+ {
+ case PROP_PLAYBACK:
+ g_value_set_object (value, renderer->priv->playback);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+on_frames_changed (Animation *animation,
+ gint position,
+ gint length,
+ AnimationRenderer *renderer)
+{
+ gint i;
+
+ for (i = position; i < position + length; i++)
+ {
+ GeglBuffer *buffer = NULL;
+ GWeakRef *ref = NULL;
+ gchar *hash = NULL;
+ gchar *hash_cp = NULL;
+
+ hash = ANIMATION_GET_CLASS (animation)->get_frame_hash (animation, i);
+ if (hash)
+ {
+ ref = g_hash_table_lookup (renderer->priv->cache_table, hash);
+ hash_cp = g_strdup (hash);
+ if (ref)
+ {
+ /* Acquire and add a new reference to the buffer. */
+ buffer = g_weak_ref_get (ref);
+ }
+ else
+ {
+ ref = g_new (GWeakRef, 1);
+ g_weak_ref_init (ref, NULL);
+ g_hash_table_insert (renderer->priv->cache_table,
+ hash, ref);
+ }
+
+ if (! buffer)
+ {
+ buffer = ANIMATION_GET_CLASS (animation)->create_frame (animation,
+ G_OBJECT (renderer),
+ i);
+ g_weak_ref_set (ref, buffer);
+ }
+ }
+
+ if (renderer->priv->cache[i])
+ g_object_unref (renderer->priv->cache[i]);
+ if (renderer->priv->hashes[i])
+ g_free (renderer->priv->hashes[i]);
+ renderer->priv->cache[i] = buffer;
+ renderer->priv->hashes[i] = hash_cp;
+
+ g_signal_emit_by_name (renderer, "cache-updated", i);
+ }
+}
+
+static void
+on_duration_changed (Animation *animation,
+ gint duration,
+ AnimationRenderer *renderer)
+{
+ gint i;
+
+ if (duration < renderer->priv->cache_size)
+ {
+ for (i = duration; i < renderer->priv->cache_size; i++)
+ {
+ if (renderer->priv->cache[i])
+ g_object_unref (renderer->priv->cache[i]);
+ if (renderer->priv->hashes[i])
+ g_free (renderer->priv->hashes[i]);
+ }
+ renderer->priv->cache = g_renew (GeglBuffer*,
+ renderer->priv->cache,
+ duration);
+ renderer->priv->hashes = g_renew (gchar*,
+ renderer->priv->hashes,
+ duration);
+ }
+ else if (duration > renderer->priv->cache_size)
+ {
+ renderer->priv->cache = g_renew (GeglBuffer*,
+ renderer->priv->cache,
+ duration);
+ renderer->priv->hashes = g_renew (gchar*,
+ renderer->priv->hashes,
+ duration);
+ for (i = renderer->priv->cache_size; i < duration; i++)
+ {
+ renderer->priv->cache[i] = NULL;
+ renderer->priv->hashes[i] = NULL;
+ }
+ }
+ renderer->priv->cache_size = duration;
+}
+
+/**** Public Functions ****/
+
+/**
+ * animation_renderer_new:
+ * @playback: the #AnimationPlayback.
+ *
+ * Returns: a new #AnimationRenderer. This renderer as well as all its methods
+ * should only be visible by the attached @playback.
+ **/
+GObject *
+animation_renderer_new (GObject *playback)
+{
+ GObject *object;
+ AnimationRenderer *renderer;
+ Animation *animation;
+
+ object = g_object_new (ANIMATION_TYPE_RENDERER,
+ "playback", playback,
+ NULL);
+ renderer = ANIMATION_RENDERER (object);
+
+ animation = animation_playback_get_animation (renderer->priv->playback);
+ renderer->priv->cache_size = animation_get_duration (animation);
+ renderer->priv->cache = g_new0 (GeglBuffer*,
+ renderer->priv->cache_size);
+ renderer->priv->hashes = g_new0 (gchar*,
+ renderer->priv->cache_size);
+ g_signal_connect (animation, "frames-changed",
+ G_CALLBACK (on_frames_changed), renderer);
+ g_signal_connect (animation, "duration-changed",
+ G_CALLBACK (on_duration_changed), renderer);
+
+ return object;
+}
+
+/**
+ * animation_renderer_get_buffer:
+ * @renderer: the #AnimationRenderer.
+ * @position:
+ *
+ * Returns: the #GeglBuffer cached for the frame at @position, with an
+ * additional reference, so that it will stay valid even if the frame is
+ * updated in-between. Therefore call g_object_unref() after usage.
+ * As all other renderer function, it should only be visible by the playback,
+ * or by frame-rendering code in #Animation subclasses themselves, since the
+ * renderer passes itself as argument when it requests a new frame buffer.
+ * Other pieces of code, in particular the GUI, should only call
+ * animation_playback_get_buffer().
+ **/
+GeglBuffer *
+animation_renderer_get_buffer (AnimationRenderer *renderer,
+ gint position)
+{
+ GeglBuffer *frame;
+
+ frame = renderer->priv->cache[position];
+ if (frame)
+ frame = g_object_ref (frame);
+
+ return frame;
+}
+
+gboolean
+animation_renderer_identical (AnimationRenderer *renderer,
+ gint position1,
+ gint position2)
+{
+ return (g_strcmp0 (renderer->priv->hashes[position1],
+ renderer->priv->hashes[position2]) == 0);
+}
diff --git a/plug-ins/animation-play/core/animation-renderer.h
b/plug-ins/animation-play/core/animation-renderer.h
new file mode 100644
index 0000000..8d374a7
--- /dev/null
+++ b/plug-ins/animation-play/core/animation-renderer.h
@@ -0,0 +1,60 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * animation-renderer.h
+ * Copyright (C) 2017 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_RENDERER_H__
+#define __ANIMATION_RENDERER_H__
+
+#define ANIMATION_TYPE_RENDERER (animation_renderer_get_type ())
+#define ANIMATION_RENDERER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), ANIMATION_TYPE_RENDERER,
AnimationRenderer))
+#define ANIMATION_RENDERER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), ANIMATION_TYPE_RENDERER,
AnimationRendererClass))
+#define ANIMATION_IS_RENDERER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), ANIMATION_TYPE_RENDERER))
+#define ANIMATION_IS_RENDERER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), ANIMATION_TYPE_RENDERER))
+#define ANIMATION_RENDERER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), ANIMATION_TYPE_RENDERER,
AnimationRendererClass))
+
+typedef struct _AnimationRenderer AnimationRenderer;
+typedef struct _AnimationRendererClass AnimationRendererClass;
+typedef struct _AnimationRendererPrivate AnimationRendererPrivate;
+
+struct _AnimationRenderer
+{
+ GObject parent_instance;
+
+ AnimationRendererPrivate *priv;
+};
+
+struct _AnimationRendererClass
+{
+ GObjectClass parent_class;
+
+ void (*cache_updated) (AnimationRenderer *renderer,
+ gint position);
+};
+
+GType animation_renderer_get_type (void);
+
+
+GObject * animation_renderer_new (GObject *playback);
+GeglBuffer * animation_renderer_get_buffer (AnimationRenderer *renderer,
+ gint position);
+gboolean animation_renderer_identical (AnimationRenderer *renderer,
+ gint position1,
+ gint position2);
+
+#endif /* __ANIMATION_RENDERER_H__ */
diff --git a/plug-ins/animation-play/core/animation.c b/plug-ins/animation-play/core/animation.c
index 4b6bfc4..d4fc4c4 100644
--- a/plug-ins/animation-play/core/animation.c
+++ b/plug-ins/animation-play/core/animation.c
@@ -30,6 +30,8 @@
#include "animation.h"
#include "animation-animatic.h"
#include "animation-celanimation.h"
+#include "animation-playback.h"
+#include "animation-renderer.h"
/* Settings we cache assuming they may be the user's
* favorite, like a framerate.
@@ -44,7 +46,7 @@ enum
{
LOADING,
LOADED,
- CACHE_INVALIDATED,
+ FRAMES_CHANGED,
DURATION_CHANGED,
FRAMERATE_CHANGED,
PROXY_CHANGED,
@@ -88,11 +90,6 @@ static void animation_get_property (GObject *object,
GValue *value,
GParamSpec *pspec);
-/* Base implementation of virtual methods. */
-static gboolean animation_real_same (Animation *animation,
- gint prev_pos,
- gint next_pos);
-
G_DEFINE_TYPE (Animation, animation, G_TYPE_OBJECT)
#define parent_class animation_parent_class
@@ -145,19 +142,19 @@ animation_class_init (AnimationClass *klass)
G_TYPE_NONE,
0);
/**
- * Animation::cache-invalidated:
+ * Animation::frames-changed:
* @animation: the animation.
- * @position: the first frame position whose cache is invalid.
- * @length: the number of invalidated frames from @position.
+ * @position: the first frame position whose contents changed.
+ * @length: the number of changed frames from @position.
*
- * The ::cache-invalidated signal must be emitted when the contents
+ * The ::frames-changed signal must be emitted when the contents
* of one or more successive frames change.
*/
- animation_signals[CACHE_INVALIDATED] =
- g_signal_new ("cache-invalidated",
+ animation_signals[FRAMES_CHANGED] =
+ g_signal_new ("frames-changed",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_FIRST,
- G_STRUCT_OFFSET (AnimationClass, cache_invalidated),
+ G_STRUCT_OFFSET (AnimationClass, frames_changed),
NULL, NULL,
NULL,
G_TYPE_NONE,
@@ -222,8 +219,6 @@ animation_class_init (AnimationClass *klass)
object_class->set_property = animation_set_property;
object_class->get_property = animation_get_property;
- klass->same = animation_real_same;
-
/**
* Animation:image:
*
@@ -279,6 +274,8 @@ animation_new (gint32 image_id,
"image", image_id,
"xml", xml,
NULL);
+ g_signal_emit (animation, animation_signals[FRAMES_CHANGED], 0,
+ 0, animation_get_duration (animation));
return animation;
}
@@ -304,10 +301,10 @@ animation_load (Animation *animation)
AnimationPrivate *priv = ANIMATION_GET_PRIVATE (animation);
priv->loaded = FALSE;
- ANIMATION_GET_CLASS (animation)->purge_cache (animation);
- priv->loaded = TRUE;
- g_signal_emit (animation, animation_signals[CACHE_INVALIDATED], 0,
+ g_signal_emit (animation, animation_signals[FRAMES_CHANGED], 0,
0, animation_get_duration (animation));
+ priv->loaded = TRUE;
+ g_signal_emit (animation, animation_signals[LOADED], 0);
}
void
@@ -444,13 +441,6 @@ animation_get_size (Animation *animation,
*height *= priv->proxy_ratio;
}
-GeglBuffer *
-animation_get_frame (Animation *animation,
- gint pos)
-{
- return ANIMATION_GET_CLASS (animation)->get_frame (animation, pos);
-}
-
void
animation_set_framerate (Animation *animation,
gdouble fps)
@@ -567,12 +557,3 @@ animation_get_property (GObject *object,
break;
}
}
-
-static gboolean
-animation_real_same (Animation *animation,
- gint previous_pos,
- gint next_pos)
-{
- /* By default all frames are supposed different. */
- return (previous_pos == next_pos);
-}
diff --git a/plug-ins/animation-play/core/animation.h b/plug-ins/animation-play/core/animation.h
index 03a01c4..3a17d7d 100644
--- a/plug-ins/animation-play/core/animation.h
+++ b/plug-ins/animation-play/core/animation.h
@@ -45,8 +45,9 @@ struct _AnimationClass
gdouble ratio);
void (*loaded) (Animation *animation);
- void (*cache_invalidated) (Animation *animation,
- gint position);
+ void (*frames_changed) (Animation *animation,
+ gint position,
+ gint length);
void (*duration_changed) (Animation *animation,
gint duration);
void (*framerate_changed) (Animation *animation,
@@ -54,19 +55,9 @@ struct _AnimationClass
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. */
gint (*get_duration) (Animation *animation);
- GeglBuffer * (*get_frame) (Animation *animation,
- gint pos);
-
- void (*purge_cache) (Animation *animation);
-
void (*reset_defaults) (Animation *animation);
gchar * (*serialize) (Animation *animation,
const gchar *playback_xml);
@@ -76,6 +67,13 @@ struct _AnimationClass
void (*update_paint_view) (Animation *animation,
gint position);
+
+ /* Used by the renderer only. Must be implemented too. */
+ gchar * (*get_frame_hash) (Animation *animation,
+ gint position);
+ GeglBuffer * (*create_frame) (Animation *animation,
+ GObject *renderer,
+ gint position);
};
GType animation_get_type (void);
@@ -100,9 +98,6 @@ void animation_get_size (Animation *animation,
gint *width,
gint *height);
-GeglBuffer * animation_get_frame (Animation *animation,
- gint frame_number);
-
void animation_set_framerate (Animation *animation,
gdouble fps);
gdouble animation_get_framerate (Animation *animation);
diff --git a/plug-ins/animation-play/widgets/animation-dialog.c
b/plug-ins/animation-play/widgets/animation-dialog.c
index 08eb1c6..2ef0493 100755
--- a/plug-ins/animation-play/widgets/animation-dialog.c
+++ b/plug-ins/animation-play/widgets/animation-dialog.c
@@ -1976,11 +1976,11 @@ detach_callback (GtkToggleAction *action,
}
/* Force a refresh after detachment/attachment. */
- buffer = animation_get_frame (priv->animation,
- animation_playback_get_position (priv->playback));
+ buffer = animation_playback_get_buffer (priv->playback,
+ animation_playback_get_position (priv->playback));
render_frame (dialog, buffer, TRUE);
/* clean up */
- if (buffer != NULL)
+ if (buffer)
g_object_unref (buffer);
}
@@ -2330,11 +2330,11 @@ da_size_callback (GtkWidget *drawing_area,
{
GeglBuffer *buffer;
- buffer = animation_get_frame (priv->animation,
- animation_playback_get_position (priv->playback));
+ buffer = animation_playback_get_buffer (priv->playback,
+ animation_playback_get_position (priv->playback));
render_frame (dialog, buffer, TRUE);
/* clean up */
- if (buffer != NULL)
+ if (buffer)
g_object_unref (buffer);
}
}
@@ -2431,11 +2431,11 @@ render_on_realize (GtkWidget *drawing_area,
AnimationDialogPrivate *priv = GET_PRIVATE (dialog);
GeglBuffer *buffer;
- buffer = animation_get_frame (priv->animation,
- animation_playback_get_position (priv->playback));
+ buffer = animation_playback_get_buffer (priv->playback,
+ animation_playback_get_position (priv->playback));
render_frame (dialog, buffer, TRUE);
/* clean up */
- if (buffer != NULL)
+ if (buffer)
g_object_unref (buffer);
g_signal_handlers_disconnect_by_func (drawing_area,
diff --git a/plug-ins/animation-play/widgets/animation-xsheet.c
b/plug-ins/animation-play/widgets/animation-xsheet.c
index dd9cf74..e1aae8a 100755
--- a/plug-ins/animation-play/widgets/animation-xsheet.c
+++ b/plug-ins/animation-play/widgets/animation-xsheet.c
@@ -1295,7 +1295,8 @@ on_playback_rendered (AnimationPlayback *playback,
gboolean must_draw_null,
AnimationXSheet *xsheet)
{
- animation_xsheet_jump (xsheet, frame_number);
+ if (animation_loaded (ANIMATION (xsheet->priv->animation)))
+ animation_xsheet_jump (xsheet, frame_number);
}
static void
@@ -1925,7 +1926,7 @@ animation_xsheet_rename_cel (AnimationXSheet *xsheet,
gboolean recursively)
{
const GList *layers;
- const GList *prev_layers;
+ const GList *prev_layers = NULL;
gpointer track_num;
gpointer position;
gboolean same_as_prev = FALSE;
@@ -1938,9 +1939,10 @@ animation_xsheet_rename_cel (AnimationXSheet *xsheet,
layers = animation_cel_animation_get_layers (xsheet->priv->animation,
GPOINTER_TO_INT (track_num),
GPOINTER_TO_INT (position));
- prev_layers = animation_cel_animation_get_layers (xsheet->priv->animation,
- GPOINTER_TO_INT (track_num),
- GPOINTER_TO_INT (position) - 1);
+ if (position > 0)
+ prev_layers = animation_cel_animation_get_layers (xsheet->priv->animation,
+ GPOINTER_TO_INT (track_num),
+ GPOINTER_TO_INT (position) - 1);
/* Remove the previous label, if any. */
if (gtk_bin_get_child (GTK_BIN (cel)))
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]