[gimp/wip/animation: 125/197] plug-ins: add a start of implementation for camera keyframing.
- From: Jehan Pagès <jehanp src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gimp/wip/animation: 125/197] plug-ins: add a start of implementation for camera keyframing.
- Date: Sat, 7 Oct 2017 03:08:57 +0000 (UTC)
commit 1be912fa547b578f94937e55a5b4da9815c964e0
Author: Jehan <jehan girinstud io>
Date: Mon Jan 9 01:46:06 2017 +0100
plug-ins: add a start of implementation for camera keyframing.
It is barely usable at this state since it will recompute too much at
any update. It also miss a lot of the UI to set or delete a keyframe,
and navigate through the animation positions.
It works only for a full frame for the time being (there should also be
camera works possible on independant levels) and only provides offset
(i.e. tilting and panning). Zoom or rotation features will be necessary,
as well as effects (basically animating GEGL operations).
Finally data persistence will need to be implemented in the
serialization of the animation.
Yet that's a start for further development in progress.
plug-ins/animation-play/Makefile.am | 8 +-
plug-ins/animation-play/core/animation-camera.c | 406 ++++++++++++++++++++
plug-ins/animation-play/core/animation-camera.h | 72 ++++
.../animation-play/core/animation-celanimation.c | 100 ++++-
.../animation-play/core/animation-celanimation.h | 2 +
plug-ins/animation-play/widgets/animation-dialog.c | 36 ++-
.../widgets/animation-keyframe-view.c | 236 ++++++++++++
.../widgets/animation-keyframe-view.h | 56 +++
plug-ins/animation-play/widgets/animation-xsheet.c | 200 +++++++++-
plug-ins/animation-play/widgets/animation-xsheet.h | 3 +-
10 files changed, 1069 insertions(+), 50 deletions(-)
---
diff --git a/plug-ins/animation-play/Makefile.am b/plug-ins/animation-play/Makefile.am
index 549e05e..b4f88d4 100644
--- a/plug-ins/animation-play/Makefile.am
+++ b/plug-ins/animation-play/Makefile.am
@@ -47,18 +47,22 @@ animation_play_SOURCES = \
core/animation.c \
core/animation-animatic.h \
core/animation-animatic.c \
+ core/animation-camera.h \
+ core/animation-camera.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-keyframe-view.h \
+ widgets/animation-keyframe-view.c \
+ widgets/animation-layer-view.h \
+ widgets/animation-layer-view.c \
widgets/animation-menus.h \
widgets/animation-menus.c \
widgets/animation-storyboard.h \
widgets/animation-storyboard.c \
- widgets/animation-layer-view.h \
- widgets/animation-layer-view.c \
widgets/animation-xsheet.h \
widgets/animation-xsheet.c \
animation-utils.h \
diff --git a/plug-ins/animation-play/core/animation-camera.c b/plug-ins/animation-play/core/animation-camera.c
new file mode 100644
index 0000000..8376158
--- /dev/null
+++ b/plug-ins/animation-play/core/animation-camera.c
@@ -0,0 +1,406 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * animation-camera.c
+ * Copyright (C) 2016-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-camera.h"
+
+enum
+{
+ PROP_0,
+ PROP_ANIMATION,
+};
+
+enum
+{
+ OFFSETS_CHANGED,
+ KEYFRAME_SET,
+ KEYFRAME_DELETED,
+ LAST_SIGNAL
+};
+
+typedef struct
+{
+ gint x;
+ gint y;
+}
+Offset;
+
+struct _AnimationCameraPrivate
+{
+ Animation *animation;
+
+ /* Panning and tilting. */
+ GList *offsets;
+};
+
+static void animation_camera_finalize (GObject *object);
+static void animation_camera_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec);
+static void animation_camera_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec);
+
+static void animation_camera_emit_offsets_changed (AnimationCamera *camera,
+ gint position);
+
+G_DEFINE_TYPE (AnimationCamera, animation_camera, G_TYPE_OBJECT)
+
+#define parent_class animation_camera_parent_class
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+static void
+animation_camera_class_init (AnimationCameraClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->finalize = animation_camera_finalize;
+ object_class->get_property = animation_camera_get_property;
+ object_class->set_property = animation_camera_set_property;
+
+ /**
+ * AnimationCamera::offsets-changed:
+ * @camera: the #AnimationCamera.
+ * @position:
+ * @duration:
+ *
+ * The ::offsets-changed signal will be emitted when camera offsets
+ * were updated between [@position; @position + @duration[.
+ */
+ signals[OFFSETS_CHANGED] =
+ g_signal_new ("offsets-changed",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (AnimationCameraClass, offsets_changed),
+ NULL, NULL,
+ NULL,
+ G_TYPE_NONE,
+ 2,
+ G_TYPE_INT,
+ G_TYPE_INT);
+
+ /**
+ * AnimationCamera::keyframe-set:
+ * @camera: the #AnimationCamera.
+ * @position:
+ *
+ * The ::keyframe-set signal will be emitted when a keyframe is
+ * created or modified at @position.
+ */
+ signals[KEYFRAME_SET] =
+ g_signal_new ("keyframe-set",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (AnimationCameraClass, offsets_changed),
+ NULL, NULL,
+ NULL,
+ G_TYPE_NONE,
+ 1,
+ G_TYPE_INT);
+
+ /**
+ * AnimationCamera::keyframe-deleted:
+ * @camera: the #AnimationCamera.
+ * @position:
+ *
+ * The ::keyframe-set signal will be emitted when a keyframe is
+ * deleted at @position.
+ */
+ signals[KEYFRAME_DELETED] =
+ g_signal_new ("keyframe-deleted",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (AnimationCameraClass, offsets_changed),
+ NULL, NULL,
+ NULL,
+ G_TYPE_NONE,
+ 1,
+ G_TYPE_INT);
+
+ 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 (AnimationCameraPrivate));
+}
+
+static void
+animation_camera_init (AnimationCamera *view)
+{
+ view->priv = G_TYPE_INSTANCE_GET_PRIVATE (view,
+ ANIMATION_TYPE_CAMERA,
+ AnimationCameraPrivate);
+}
+
+/************ Public Functions ****************/
+
+/**
+ * animation_camera_new:
+ *
+ * Creates a new camera.
+ */
+AnimationCamera *
+animation_camera_new (Animation *animation)
+{
+ AnimationCamera *camera;
+
+ camera = g_object_new (ANIMATION_TYPE_CAMERA,
+ "animation", animation,
+ NULL);
+
+ return camera;
+}
+
+void
+animation_camera_set_keyframe (AnimationCamera *camera,
+ gint position,
+ gint x,
+ gint y)
+{
+ Offset *offset;
+
+ g_return_if_fail (position >= 0 &&
+ position < animation_get_duration (camera->priv->animation));
+
+ offset = g_list_nth_data (camera->priv->offsets, position);
+
+ if (! offset)
+ {
+ gint length = g_list_length (camera->priv->offsets);
+ gint i;
+
+ for (i = length; i < position; i++)
+ {
+ camera->priv->offsets = g_list_append (camera->priv->offsets, NULL);
+ }
+ offset = g_new (Offset, 1);
+ camera->priv->offsets = g_list_append (camera->priv->offsets, offset);
+ }
+
+ offset->x = x;
+ offset->y = y;
+
+ g_signal_emit (camera, signals[KEYFRAME_SET], 0, position);
+ animation_camera_emit_offsets_changed (camera, position);
+}
+
+void
+animation_camera_delete_keyframe (AnimationCamera *camera,
+ gint position)
+{
+ GList *offset;
+
+ g_return_if_fail (position >= 0 &&
+ position < animation_get_duration (camera->priv->animation));
+
+ offset = g_list_nth (camera->priv->offsets, position);
+
+ if (offset && offset->data)
+ {
+ g_free (offset->data);
+ offset->data = NULL;
+
+ g_signal_emit (camera, signals[KEYFRAME_DELETED], 0, position);
+ animation_camera_emit_offsets_changed (camera, position);
+ }
+}
+
+void
+animation_camera_get (AnimationCamera *camera,
+ gint position,
+ gint *x_offset,
+ gint *y_offset)
+{
+ Offset *keyframe;
+
+ g_return_if_fail (position >= 0 &&
+ position < animation_get_duration (camera->priv->animation));
+
+ keyframe = g_list_nth_data (camera->priv->offsets, position);
+ if (keyframe)
+ {
+ /* There is a keyframe to this exact position. Use its values. */
+ *x_offset = keyframe->x;
+ *y_offset = keyframe->y;
+ }
+ else
+ {
+ GList *iter;
+ Offset *prev_keyframe = NULL;
+ Offset *next_keyframe = NULL;
+ gint prev_keyframe_pos;
+ gint next_keyframe_pos;
+ gint i;
+
+ /* This position is not a keyframe. */
+ if (position > 0)
+ {
+ i = MIN (position - 1, g_list_length (camera->priv->offsets) - 1);
+ iter = g_list_nth (camera->priv->offsets, i);
+ for (; iter && ! iter->data; iter = iter->prev, i--)
+ ;
+ if (iter && iter->data)
+ {
+ prev_keyframe_pos = i;
+ prev_keyframe = iter->data;
+ }
+ }
+ if (position < animation_get_duration (camera->priv->animation) - 1)
+ {
+ i = position + 1;
+ iter = g_list_nth (camera->priv->offsets, i);
+ for (; iter && ! iter->data; iter = iter->next, i++)
+ ;
+ if (iter && iter->data)
+ {
+ next_keyframe_pos = i;
+ next_keyframe = iter->data;
+ }
+ }
+
+ if (prev_keyframe == NULL && next_keyframe == NULL)
+ {
+ *x_offset = *y_offset = 0;
+ }
+ else if (prev_keyframe == NULL)
+ {
+ *x_offset = next_keyframe->x;
+ *y_offset = next_keyframe->y;
+ }
+ else if (next_keyframe == NULL)
+ {
+ *x_offset = prev_keyframe->x;
+ *y_offset = prev_keyframe->y;
+ }
+ else
+ {
+ /* XXX No curve editing or anything like this yet.
+ * All keyframing is linear in this first version.
+ */
+ *x_offset = prev_keyframe->x + (position - prev_keyframe_pos) *
+ (next_keyframe->x - prev_keyframe->x) /
+ (next_keyframe_pos - prev_keyframe_pos);
+ *y_offset = prev_keyframe->y + (position - prev_keyframe_pos) *
+ (next_keyframe->y - prev_keyframe->y) /
+ (next_keyframe_pos - prev_keyframe_pos);
+ }
+ }
+}
+
+/************ Private Functions ****************/
+
+static void
+animation_camera_finalize (GObject *object)
+{
+ g_list_free_full (ANIMATION_CAMERA (object)->priv->offsets, g_free);
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static void
+animation_camera_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ AnimationCamera *camera = ANIMATION_CAMERA (object);
+
+ switch (property_id)
+ {
+ case PROP_ANIMATION:
+ camera->priv->animation = g_value_get_object (value);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+
+static void
+animation_camera_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ AnimationCamera *camera = ANIMATION_CAMERA (object);
+
+ switch (property_id)
+ {
+ case PROP_ANIMATION:
+ g_value_set_object (value, camera->priv->animation);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+animation_camera_emit_offsets_changed (AnimationCamera *camera,
+ gint position)
+{
+ GList *iter;
+ gint prev_keyframe;
+ gint next_keyframe;
+ gint i;
+
+ if (position > 0)
+ {
+ i = position - 1;
+ iter = g_list_nth (camera->priv->offsets, i);
+ for (; iter && ! iter->data; iter = iter->prev, i--)
+ ;
+ prev_keyframe = i + 1;
+ }
+ else
+ {
+ prev_keyframe = 0;
+ }
+ if (position < animation_get_duration (camera->priv->animation) - 1)
+ {
+ i = position + 1;
+ iter = g_list_nth (camera->priv->offsets, i);
+ for (; iter && ! iter->data; iter = iter->next, i++)
+ ;
+ if (iter && iter->data)
+ next_keyframe = i - 1;
+ else
+ next_keyframe = animation_get_duration (camera->priv->animation) - 1;
+ }
+ else
+ {
+ next_keyframe = animation_get_duration (camera->priv->animation) - 1;
+ }
+ g_signal_emit (camera, signals[OFFSETS_CHANGED], 0,
+ prev_keyframe, next_keyframe - prev_keyframe + 1);
+}
diff --git a/plug-ins/animation-play/core/animation-camera.h b/plug-ins/animation-play/core/animation-camera.h
new file mode 100644
index 0000000..4f27b19
--- /dev/null
+++ b/plug-ins/animation-play/core/animation-camera.h
@@ -0,0 +1,72 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * animation-camera.h
+ * Copyright (C) 2016-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_CAMERA_H__
+#define __ANIMATION_CAMERA_H__
+
+#define ANIMATION_TYPE_CAMERA (animation_camera_get_type ())
+#define ANIMATION_CAMERA(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), ANIMATION_TYPE_CAMERA,
AnimationCamera))
+#define ANIMATION_CAMERA_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), ANIMATION_TYPE_CAMERA,
AnimationCameraClass))
+#define ANIMATION_IS_CAMERA(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), ANIMATION_TYPE_CAMERA))
+#define ANIMATION_IS_CAMERA_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), ANIMATION_TYPE_CAMERA))
+#define ANIMATION_CAMERA_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), ANIMATION_TYPE_CAMERA,
AnimationCameraClass))
+
+typedef struct _AnimationCamera AnimationCamera;
+typedef struct _AnimationCameraClass AnimationCameraClass;
+typedef struct _AnimationCameraPrivate AnimationCameraPrivate;
+
+struct _AnimationCamera
+{
+ GObject parent_instance;
+
+ AnimationCameraPrivate *priv;
+};
+
+struct _AnimationCameraClass
+{
+ GObjectClass parent_class;
+
+ /* Signals */
+ void (*offsets_changed) (AnimationCamera *camera,
+ gint position,
+ gint duration);
+ void (*keyframe_set) (AnimationCamera *camera,
+ gint position);
+ void (*keyframe_deleted) (AnimationCamera *camera,
+ gint position);
+};
+
+GType animation_camera_get_type (void) G_GNUC_CONST;
+
+AnimationCamera * animation_camera_new (Animation *animation);
+
+void animation_camera_set_keyframe (AnimationCamera *camera,
+ gint position,
+ gint x,
+ gint y);
+void animation_camera_delete_keyframe (AnimationCamera *camera,
+ gint position);
+
+void animation_camera_get (AnimationCamera *camera,
+ gint position,
+ gint *x_offset,
+ gint *y_offset);
+
+#endif /* __ANIMATION_CAMERA_H__ */
diff --git a/plug-ins/animation-play/core/animation-celanimation.c
b/plug-ins/animation-play/core/animation-celanimation.c
index 5b08fc7..419bb0a 100644
--- a/plug-ins/animation-play/core/animation-celanimation.c
+++ b/plug-ins/animation-play/core/animation-celanimation.c
@@ -24,6 +24,10 @@
#include <libgimp/stdplugins-intl.h>
#include "animation-utils.h"
+
+#include "animation.h"
+#include "animation-camera.h"
+
#include "animation-celanimation.h"
typedef struct _AnimationCelAnimationPrivate AnimationCelAnimationPrivate;
@@ -38,34 +42,45 @@ 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.*/
- gint *composition;
- gint n_sources;
+ CompLayer *composition;
+ gint n_sources;
- gint refs;
+ gint refs;
}
Cache;
struct _AnimationCelAnimationPrivate
{
/* The number of frames. */
- gint duration;
+ gint duration;
/* Frames are cached as GEGL buffers. */
- GList *cache;
+ GList *cache;
/* Panel comments. */
- GList *comments;
+ GList *comments;
/* List of tracks/levels.
* The background is a special-named track, always present
* and first.
* There is always at least 1 additional track. */
- GList *tracks;
+ GList *tracks;
+
+ /* The globale camera. */
+ AnimationCamera *camera;
};
typedef enum
@@ -135,6 +150,12 @@ static void animation_cel_animation_text (GMarkupParseContext
gpointer user_data,
GError **error);
+/* Signal handling */
+
+static void on_camera_offsets_changed (AnimationCamera *camera,
+ gint position,
+ gint duration,
+ AnimationCelAnimation *animation);
/* Utils */
static void animation_cel_animation_cache (AnimationCelAnimation *animation,
@@ -350,6 +371,12 @@ animation_cel_animation_set_duration (AnimationCelAnimation *animation,
}
}
+GObject *
+animation_cel_animation_get_main_camera (AnimationCelAnimation *animation)
+{
+ return G_OBJECT (animation->priv->camera);
+}
+
gint
animation_cel_animation_get_levels (AnimationCelAnimation *animation)
{
@@ -748,6 +775,11 @@ animation_cel_animation_reset_defaults (Animation *animation)
layers);
}
}
+
+ priv->camera = animation_camera_new (animation);
+ g_signal_connect (priv->camera, "offsets-changed",
+ G_CALLBACK (on_camera_offsets_changed),
+ animation);
}
static gchar *
@@ -910,6 +942,15 @@ animation_cel_animation_deserialize (Animation *animation,
cel_animation = ANIMATION_CEL_ANIMATION (animation);
/* Reverse track order. */
cel_animation->priv->tracks = g_list_reverse (cel_animation->priv->tracks);
+
+ /* TODO: just testing right now. I will have to add actual
+ * (de)serialization, otherwise there is no persistency of
+ * camera works.
+ */
+ cel_animation->priv->camera = animation_camera_new (animation);
+ g_signal_connect (cel_animation->priv->camera, "offsets-changed",
+ G_CALLBACK (on_camera_offsets_changed),
+ animation);
}
g_markup_parse_context_free (context);
@@ -1207,6 +1248,23 @@ animation_cel_animation_text (GMarkupParseContext *context,
}
}
+/**** Signal handling ****/
+
+static void
+on_camera_offsets_changed (AnimationCamera *camera,
+ gint position,
+ 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",
+ position, duration);
+}
+
/**** Utils ****/
static void
@@ -1216,13 +1274,15 @@ animation_cel_animation_cache (AnimationCelAnimation *animation,
GeglBuffer *backdrop = NULL;
GList *iter;
Cache *cache;
- gint *composition;
+ 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);
@@ -1266,8 +1326,11 @@ animation_cel_animation_cache (AnimationCelAnimation *animation,
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 (int, n_sources);
+ composition = g_new0 (CompLayer, n_sources);
i = 0;
for (iter = animation->priv->tracks; iter; iter = iter->next)
{
@@ -1284,7 +1347,9 @@ animation_cel_animation_cache (AnimationCelAnimation *animation,
tattoo = GPOINTER_TO_INT (layer->data);
if (tattoo)
{
- composition[i++] = tattoo;
+ composition[i].tattoo = tattoo;
+ composition[i].offset_x = main_offset_x;
+ composition[i++].offset_y = main_offset_y;
}
}
}
@@ -1302,7 +1367,9 @@ animation_cel_animation_cache (AnimationCelAnimation *animation,
same = TRUE;
for (i = 0; i < n_sources; i++)
{
- if (cache->composition[i] != composition[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;
@@ -1340,7 +1407,7 @@ animation_cel_animation_cache (AnimationCelAnimation *animation,
gint layer_offy;
layer = gimp_image_get_layer_by_tattoo (image_id,
- cache->composition[i]);
+ cache->composition[i].tattoo);
if (layer > 0)
source = gimp_drawable_get_buffer (layer);
if (layer <= 0 || ! source)
@@ -1353,7 +1420,8 @@ animation_cel_animation_cache (AnimationCelAnimation *animation,
intermediate = normal_blend (preview_width, preview_height,
backdrop, 1.0, 0, 0,
source, proxy_ratio,
- layer_offx, layer_offy);
+ layer_offx + cache->composition[i].offset_x,
+ layer_offy + cache->composition[i].offset_y);
g_object_unref (source);
if (backdrop)
{
@@ -1379,6 +1447,8 @@ animation_cel_animation_cleanup (AnimationCelAnimation *animation)
g_list_free_full (animation->priv->tracks,
(GDestroyNotify) animation_cel_animation_clean_track);
animation->priv->tracks = NULL;
+
+ g_object_unref (animation->priv->camera);
}
static gboolean
@@ -1395,7 +1465,9 @@ animation_cel_animation_cache_cmp (Cache *cache1,
identical = TRUE;
for (i = 0; i < cache1->n_sources; i++)
{
- if (cache1->composition[i] != cache2->composition[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;
diff --git a/plug-ins/animation-play/core/animation-celanimation.h
b/plug-ins/animation-play/core/animation-celanimation.h
index 36205fc..1aa0d00 100644
--- a/plug-ins/animation-play/core/animation-celanimation.h
+++ b/plug-ins/animation-play/core/animation-celanimation.h
@@ -66,6 +66,8 @@ const gchar * animation_cel_animation_get_comment (AnimationCelAnimation *an
void animation_cel_animation_set_duration (AnimationCelAnimation *animation,
gint duration);
+GObject * animation_cel_animation_get_main_camera (AnimationCelAnimation *animation);
+
gint animation_cel_animation_get_levels (AnimationCelAnimation *animation);
gint animation_cel_animation_level_up (AnimationCelAnimation *animation,
gint level);
diff --git a/plug-ins/animation-play/widgets/animation-dialog.c
b/plug-ins/animation-play/widgets/animation-dialog.c
index 2b909ed..ed0c2b6 100755
--- a/plug-ins/animation-play/widgets/animation-dialog.c
+++ b/plug-ins/animation-play/widgets/animation-dialog.c
@@ -30,10 +30,12 @@
#include "core/animation.h"
#include "core/animation-animatic.h"
+#include "core/animation-camera.h"
#include "core/animation-celanimation.h"
#include "core/animation-playback.h"
#include "animation-dialog.h"
+#include "animation-keyframe-view.h"
#include "animation-layer-view.h"
#include "animation-storyboard.h"
#include "animation-xsheet.h"
@@ -91,7 +93,9 @@ struct _AnimationDialogPrivate
guint shape_drawing_area_height;
/* Notebook on the right (layer list, storyboard, settings). */
+ GtkWidget *right_pane;
GtkWidget *right_notebook;
+ GtkWidget *keyframe_view;
/* Notebook: settings. */
GtkWidget *settings;
@@ -103,8 +107,8 @@ struct _AnimationDialogPrivate
/* Notebook: layer list. */
GtkWidget *layer_list;
- /* The vpaned (bottom is timeline, above is preview). */
- GtkWidget *vpaned;
+ /* The left panel (bottom is timeline, above is preview). */
+ GtkWidget *left_pane;
GtkWidget *xsheet;
/* Actions */
@@ -425,15 +429,24 @@ animation_dialog_constructed (GObject *object)
gtk_container_add (GTK_CONTAINER (dialog), hpaned);
gtk_widget_show (hpaned);
- priv->vpaned = gtk_paned_new (GTK_ORIENTATION_VERTICAL);
- gtk_paned_pack1 (GTK_PANED (hpaned), priv->vpaned, TRUE, TRUE);
- gtk_widget_show (priv->vpaned);
+ priv->left_pane = gtk_paned_new (GTK_ORIENTATION_VERTICAL);
+ gtk_paned_pack1 (GTK_PANED (hpaned), priv->left_pane, TRUE, TRUE);
+ gtk_widget_show (priv->left_pane);
- priv->right_notebook = gtk_notebook_new ();
- gtk_paned_pack2 (GTK_PANED (hpaned), priv->right_notebook,
+ priv->right_pane = gtk_paned_new (GTK_ORIENTATION_VERTICAL);
+ gtk_paned_pack2 (GTK_PANED (hpaned), priv->right_pane,
TRUE, TRUE);
+ gtk_widget_show (priv->right_pane);
+
+ priv->right_notebook = gtk_notebook_new ();
+ gtk_paned_pack1 (GTK_PANED (priv->right_pane), priv->right_notebook,
+ TRUE, FALSE);
gtk_widget_show (priv->right_notebook);
+ priv->keyframe_view = animation_keyframe_view_new ();
+ gtk_paned_pack2 (GTK_PANED (priv->right_pane), priv->keyframe_view,
+ TRUE, FALSE);
+
/******************\
|**** Settings ****|
\******************/
@@ -579,7 +592,7 @@ animation_dialog_constructed (GObject *object)
/* Playback vertical box. */
main_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
- gtk_paned_pack1 (GTK_PANED (priv->vpaned), main_vbox, TRUE, TRUE);
+ gtk_paned_pack1 (GTK_PANED (priv->left_pane), main_vbox, TRUE, TRUE);
gtk_widget_show (main_vbox);
/*************/
@@ -1327,7 +1340,7 @@ animation_dialog_set_animation (AnimationDialog *dialog,
}
/* The bottom panel. */
- frame = gtk_paned_get_child2 (GTK_PANED (priv->vpaned));
+ frame = gtk_paned_get_child2 (GTK_PANED (priv->left_pane));
if (frame)
{
gtk_widget_destroy (frame);
@@ -1337,12 +1350,13 @@ animation_dialog_set_animation (AnimationDialog *dialog,
if (ANIMATION_IS_CEL_ANIMATION (animation))
{
frame = gtk_frame_new (_("X-Sheet"));
- gtk_paned_pack2 (GTK_PANED (priv->vpaned), frame,
+ gtk_paned_pack2 (GTK_PANED (priv->left_pane), frame,
TRUE, TRUE);
gtk_widget_show (frame);
priv->xsheet = animation_xsheet_new (ANIMATION_CEL_ANIMATION (animation),
- priv->playback, priv->layer_list);
+ priv->playback, priv->layer_list,
+ priv->keyframe_view);
gtk_container_add (GTK_CONTAINER (frame), priv->xsheet);
gtk_widget_show (priv->xsheet);
}
diff --git a/plug-ins/animation-play/widgets/animation-keyframe-view.c
b/plug-ins/animation-play/widgets/animation-keyframe-view.c
new file mode 100644
index 0000000..810f09c
--- /dev/null
+++ b/plug-ins/animation-play/widgets/animation-keyframe-view.c
@@ -0,0 +1,236 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * animation-keyframe_view.c
+ * Copyright (C) 2016-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/gimpui.h>
+
+#include "libgimp/stdplugins-intl.h"
+
+#include "core/animation.h"
+#include "core/animation-camera.h"
+#include "core/animation-celanimation.h"
+
+#include "animation-keyframe-view.h"
+
+struct _AnimationKeyFrameViewPrivate
+{
+ AnimationCamera *camera;
+ gint position;
+
+ GtkWidget *offset_entry;
+};
+
+/* GObject handlers */
+static void animation_keyframe_view_constructed (GObject *object);
+
+static void on_offset_entry_changed (GimpSizeEntry *entry,
+ AnimationKeyFrameView *view);
+static void on_offsets_changed (AnimationCamera *camera,
+ gint position,
+ gint duration,
+ AnimationKeyFrameView *view);
+
+G_DEFINE_TYPE (AnimationKeyFrameView, animation_keyframe_view, GTK_TYPE_NOTEBOOK)
+
+#define parent_class animation_keyframe_view_parent_class
+
+static void
+animation_keyframe_view_class_init (AnimationKeyFrameViewClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->constructed = animation_keyframe_view_constructed;
+
+ g_type_class_add_private (klass, sizeof (AnimationKeyFrameViewPrivate));
+}
+
+static void
+animation_keyframe_view_init (AnimationKeyFrameView *view)
+{
+ view->priv = G_TYPE_INSTANCE_GET_PRIVATE (view,
+ ANIMATION_TYPE_KEYFRAME_VIEW,
+ AnimationKeyFrameViewPrivate);
+}
+
+/************ Public Functions ****************/
+
+/**
+ * animation_keyframe_view_new:
+ *
+ * Creates a new layer view. You should not show it with
+ * gtk_widget_show() but with animation_keyframe_view_show() instead.
+ */
+GtkWidget *
+animation_keyframe_view_new ()
+{
+ GtkWidget *view;
+
+ view = g_object_new (ANIMATION_TYPE_KEYFRAME_VIEW,
+ NULL);
+
+ return view;
+}
+
+/**
+ * animation_keyframe_view_show:
+ * @view: the #AnimationKeyFrameView.
+ * @animation: the #Animation.
+ * @position:
+ *
+ * Show the @view widget, displaying the keyframes set on
+ * @animation at @position.
+ */
+void
+animation_keyframe_view_show (AnimationKeyFrameView *view,
+ AnimationCelAnimation *animation,
+ gint position)
+{
+ AnimationCamera *camera;
+ gint32 image_id;
+ gint width;
+ gint height;
+ gdouble xres;
+ gdouble yres;
+ gint x_offset;
+ gint y_offset;
+
+ camera = ANIMATION_CAMERA (animation_cel_animation_get_main_camera (animation));
+
+ view->priv->camera = camera;
+ view->priv->position = position;
+
+ image_id = animation_get_image_id (ANIMATION (animation));
+ gimp_image_get_resolution (image_id, &xres, &yres);
+
+ animation_get_size (ANIMATION (animation), &width, &height);
+ gimp_size_entry_set_refval_boundaries (GIMP_SIZE_ENTRY (view->priv->offset_entry),
+ 0, (gdouble) -width, (gdouble) width);
+ gimp_size_entry_set_refval_boundaries (GIMP_SIZE_ENTRY (view->priv->offset_entry),
+ 1, (gdouble) -height, (gdouble) height);
+ gimp_size_entry_set_size (GIMP_SIZE_ENTRY (view->priv->offset_entry),
+ 0, 0.0, (gdouble) width);
+ gimp_size_entry_set_size (GIMP_SIZE_ENTRY (view->priv->offset_entry),
+ 1, 0.0, (gdouble) height);
+ gimp_size_entry_set_resolution (GIMP_SIZE_ENTRY (view->priv->offset_entry),
+ 0, xres, TRUE);
+ gimp_size_entry_set_resolution (GIMP_SIZE_ENTRY (view->priv->offset_entry),
+ 1, yres, TRUE);
+
+ g_signal_handlers_disconnect_by_func (view->priv->offset_entry,
+ G_CALLBACK (on_offset_entry_changed),
+ view);
+ g_signal_handlers_disconnect_by_func (view->priv->camera,
+ G_CALLBACK (on_offsets_changed),
+ view);
+ animation_camera_get (camera, position, &x_offset, &y_offset);
+ gimp_size_entry_set_value (GIMP_SIZE_ENTRY (view->priv->offset_entry),
+ 0, (gdouble) x_offset);
+ gimp_size_entry_set_value (GIMP_SIZE_ENTRY (view->priv->offset_entry),
+ 1, (gdouble) y_offset);
+ g_signal_connect (view->priv->offset_entry, "value-changed",
+ G_CALLBACK (on_offset_entry_changed),
+ view);
+ g_signal_connect (camera, "offsets-changed",
+ G_CALLBACK (on_offsets_changed),
+ view);
+ gtk_widget_show (GTK_WIDGET (view));
+}
+
+void
+animation_keyframe_view_hide (AnimationKeyFrameView *view)
+{
+ g_signal_handlers_disconnect_by_func (view->priv->offset_entry,
+ G_CALLBACK (on_offset_entry_changed),
+ view);
+ g_signal_handlers_disconnect_by_func (view->priv->camera,
+ G_CALLBACK (on_offsets_changed),
+ view);
+}
+
+/************ Private Functions ****************/
+
+static void
+animation_keyframe_view_constructed (GObject *object)
+{
+ AnimationKeyFrameView *view = ANIMATION_KEYFRAME_VIEW (object);
+ GtkWidget *page;
+ GtkWidget *label;
+
+ page = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
+ label = gtk_image_new_from_icon_name ("camera-video",
+ GTK_ICON_SIZE_SMALL_TOOLBAR);
+ gtk_notebook_append_page (GTK_NOTEBOOK (view), page,
+ label);
+
+ view->priv->offset_entry = gimp_size_entry_new (2, GIMP_UNIT_PIXEL, NULL,
+ TRUE, TRUE, FALSE, 5,
+ GIMP_SIZE_ENTRY_UPDATE_SIZE);
+ gimp_size_entry_attach_label (GIMP_SIZE_ENTRY (view->priv->offset_entry),
+ _("Horizontal offset:"), 0, 1, 0.0);
+ gimp_size_entry_attach_label (GIMP_SIZE_ENTRY (view->priv->offset_entry),
+ _("Vertical offset:"), 0, 2, 0.0);
+ gimp_size_entry_set_pixel_digits (GIMP_SIZE_ENTRY (view->priv->offset_entry), 0);
+ gtk_box_pack_start (GTK_BOX (page), view->priv->offset_entry, FALSE, FALSE, 0);
+ gtk_widget_show (view->priv->offset_entry);
+
+ gtk_widget_show (page);
+}
+
+static void
+on_offset_entry_changed (GimpSizeEntry *entry,
+ AnimationKeyFrameView *view)
+{
+ gdouble x_offset;
+ gdouble y_offset;
+
+ x_offset = gimp_size_entry_get_refval (GIMP_SIZE_ENTRY (view->priv->offset_entry), 0);
+ y_offset = gimp_size_entry_get_refval (GIMP_SIZE_ENTRY (view->priv->offset_entry), 1);
+ animation_camera_set_keyframe (view->priv->camera,
+ view->priv->position,
+ x_offset, y_offset);
+}
+
+static void
+on_offsets_changed (AnimationCamera *camera,
+ gint position,
+ gint duration,
+ AnimationKeyFrameView *view)
+{
+ if (view->priv->position >= position &&
+ view->priv->position < position + duration)
+ {
+ gint x_offset;
+ gint y_offset;
+
+ g_signal_handlers_block_by_func (view->priv->offset_entry,
+ G_CALLBACK (on_offset_entry_changed),
+ view);
+ animation_camera_get (camera, position, &x_offset, &y_offset);
+ gimp_size_entry_set_value (GIMP_SIZE_ENTRY (view->priv->offset_entry),
+ 0, (gdouble) x_offset);
+ gimp_size_entry_set_value (GIMP_SIZE_ENTRY (view->priv->offset_entry),
+ 1, (gdouble) y_offset);
+ g_signal_handlers_unblock_by_func (view->priv->offset_entry,
+ G_CALLBACK (on_offset_entry_changed),
+ view);
+ }
+}
diff --git a/plug-ins/animation-play/widgets/animation-keyframe-view.h
b/plug-ins/animation-play/widgets/animation-keyframe-view.h
new file mode 100644
index 0000000..64f7d12
--- /dev/null
+++ b/plug-ins/animation-play/widgets/animation-keyframe-view.h
@@ -0,0 +1,56 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * animation-keyframe_view.h
+ * Copyright (C) 2016-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_KEYFRAME_VIEW_H__
+#define __ANIMATION_KEYFRAME_VIEW_H__
+
+#define ANIMATION_TYPE_KEYFRAME_VIEW (animation_keyframe_view_get_type ())
+#define ANIMATION_KEYFRAME_VIEW(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj),
ANIMATION_TYPE_KEYFRAME_VIEW, AnimationKeyFrameView))
+#define ANIMATION_KEYFRAME_VIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass),
ANIMATION_TYPE_KEYFRAME_VIEW, AnimationKeyFrameViewClass))
+#define ANIMATION_IS_KEYFRAME_VIEW(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj),
ANIMATION_TYPE_KEYFRAME_VIEW))
+#define ANIMATION_IS_KEYFRAME_VIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass),
ANIMATION_TYPE_KEYFRAME_VIEW))
+#define ANIMATION_KEYFRAME_VIEW_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj),
ANIMATION_TYPE_KEYFRAME_VIEW, AnimationKeyFrameViewClass))
+
+typedef struct _AnimationKeyFrameView AnimationKeyFrameView;
+typedef struct _AnimationKeyFrameViewClass AnimationKeyFrameViewClass;
+typedef struct _AnimationKeyFrameViewPrivate AnimationKeyFrameViewPrivate;
+
+struct _AnimationKeyFrameView
+{
+ GtkNotebook parent_instance;
+
+ AnimationKeyFrameViewPrivate *priv;
+};
+
+struct _AnimationKeyFrameViewClass
+{
+ GtkNotebookClass parent_class;
+};
+
+GType animation_keyframe_view_get_type (void) G_GNUC_CONST;
+
+GtkWidget * animation_keyframe_view_new (void);
+
+void animation_keyframe_view_show (AnimationKeyFrameView *view,
+ AnimationCelAnimation *animation,
+ gint position);
+void animation_keyframe_view_hide (AnimationKeyFrameView *view);
+
+#endif /* __ANIMATION_KEYFRAME_VIEW_H__ */
diff --git a/plug-ins/animation-play/widgets/animation-xsheet.c
b/plug-ins/animation-play/widgets/animation-xsheet.c
index 43a5950..a591625 100755
--- a/plug-ins/animation-play/widgets/animation-xsheet.c
+++ b/plug-ins/animation-play/widgets/animation-xsheet.c
@@ -29,9 +29,11 @@
#include "animation-utils.h"
#include "core/animation.h"
+#include "core/animation-camera.h"
#include "core/animation-playback.h"
#include "core/animation-celanimation.h"
+#include "animation-keyframe-view.h"
#include "animation-layer-view.h"
#include "animation-menus.h"
@@ -44,6 +46,7 @@ enum
PROP_0,
PROP_ANIMATION,
PROP_LAYER_VIEW,
+ PROP_KEYFRAME_VIEW,
PROP_SUITE_CYCLE,
PROP_SUITE_FPI
};
@@ -54,6 +57,7 @@ struct _AnimationXSheetPrivate
AnimationPlayback *playback;
GtkWidget *layer_view;
+ GtkWidget *keyframe_view;
GtkWidget *track_layout;
@@ -67,6 +71,8 @@ struct _AnimationXSheetPrivate
gint suite_cycle;
gint suite_fpi;
+ GList *effect_buttons;
+
GList *cels;
gint selected_track;
GQueue *selected_frames;
@@ -141,10 +147,18 @@ static void on_animation_loaded (Animation *animation,
static void on_animation_duration_changed (Animation *animation,
gint duration,
AnimationXSheet *xsheet);
+/* Callbacks on camera. */
+static void on_camera_keyframe_set (AnimationCamera *camera,
+ gint position,
+ AnimationXSheet *xsheet);
+static void on_camera_keyframe_deleted (AnimationCamera *camera,
+ gint position,
+ AnimationXSheet *xsheet);
+
/* Callbacks on playback. */
-static void on_animation_stopped (AnimationPlayback *playback,
+static void on_playback_stopped (AnimationPlayback *playback,
AnimationXSheet *xsheet);
-static void on_animation_rendered (AnimationPlayback *animation,
+static void on_playback_rendered (AnimationPlayback *animation,
gint frame_number,
GeglBuffer *buffer,
gboolean must_draw_null,
@@ -160,6 +174,9 @@ static void animation_xsheet_suite_do (GtkWidget *button,
AnimationXSheet *xsheet);
static void animation_xsheet_suite_cancelled (GtkWidget *button,
AnimationXSheet *xsheet);
+static gboolean animation_xsheet_effect_clicked (GtkWidget *button,
+ GdkEvent *event,
+ AnimationXSheet *xsheet);
static gboolean animation_xsheet_frame_clicked (GtkWidget *button,
GdkEvent *event,
AnimationXSheet *xsheet);
@@ -245,6 +262,17 @@ animation_xsheet_class_init (AnimationXSheetClass *klass)
G_PARAM_READWRITE |
G_PARAM_CONSTRUCT_ONLY));
/**
+ * AnimationXSheet:keyframe-view:
+ *
+ * The associated #AnimationLayerView.
+ */
+ g_object_class_install_property (object_class, PROP_KEYFRAME_VIEW,
+ g_param_spec_object ("keyframe-view",
+ NULL, NULL,
+ ANIMATION_TYPE_KEYFRAME_VIEW,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY));
+ /**
* AnimationXSheet:suite-cycle:
*
* The configured cycle repeat. 0 means indefinitely.
@@ -284,20 +312,22 @@ animation_xsheet_init (AnimationXSheet *xsheet)
GtkWidget *
animation_xsheet_new (AnimationCelAnimation *animation,
AnimationPlayback *playback,
- GtkWidget *layer_view)
+ GtkWidget *layer_view,
+ GtkWidget *keyframe_view)
{
GtkWidget *xsheet;
xsheet = g_object_new (ANIMATION_TYPE_XSHEET,
"animation", animation,
"layer-view", layer_view,
+ "keyframe-view", keyframe_view,
NULL);
ANIMATION_XSHEET (xsheet)->priv->playback = playback;
g_signal_connect (ANIMATION_XSHEET (xsheet)->priv->playback,
- "render", G_CALLBACK (on_animation_rendered),
+ "render", G_CALLBACK (on_playback_rendered),
xsheet);
g_signal_connect (ANIMATION_XSHEET (xsheet)->priv->playback,
- "stop", G_CALLBACK (on_animation_stopped),
+ "stop", G_CALLBACK (on_playback_stopped),
xsheet);
return xsheet;
@@ -352,6 +382,9 @@ animation_xsheet_set_property (GObject *object,
case PROP_LAYER_VIEW:
xsheet->priv->layer_view = g_value_dup_object (value);
break;
+ case PROP_KEYFRAME_VIEW:
+ xsheet->priv->keyframe_view = g_value_dup_object (value);
+ break;
case PROP_SUITE_CYCLE:
xsheet->priv->suite_cycle = g_value_get_int (value);
break;
@@ -381,6 +414,9 @@ animation_xsheet_get_property (GObject *object,
case PROP_LAYER_VIEW:
g_value_set_object (value, xsheet->priv->layer_view);
break;
+ case PROP_KEYFRAME_VIEW:
+ g_value_set_object (value, xsheet->priv->keyframe_view);
+ break;
case PROP_SUITE_CYCLE:
g_value_set_int (value, xsheet->priv->suite_cycle);
break;
@@ -400,10 +436,10 @@ 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),
+ G_CALLBACK (on_playback_rendered),
xsheet);
g_signal_handlers_disconnect_by_func (ANIMATION_XSHEET (xsheet)->priv->playback,
- G_CALLBACK (on_animation_stopped),
+ G_CALLBACK (on_playback_stopped),
xsheet);
if (xsheet->priv->animation)
g_object_unref (xsheet->priv->animation);
@@ -413,6 +449,7 @@ animation_xsheet_finalize (GObject *object)
g_list_free_full (xsheet->priv->cels, (GDestroyNotify) g_list_free);
g_list_free (xsheet->priv->comment_fields);
+ g_list_free (xsheet->priv->effect_buttons);
G_OBJECT_CLASS (parent_class)->finalize (object);
}
@@ -438,7 +475,7 @@ animation_xsheet_add_headers (AnimationXSheet *xsheet,
frame, level);
gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_OUT);
gtk_table_attach (GTK_TABLE (xsheet->priv->track_layout),
- frame, level * 9 + 1, level * 9 + 10, 1, 2,
+ frame, level * 9 + 2, level * 9 + 11, 1, 2,
GTK_FILL, GTK_FILL, 0, 0);
label = gtk_entry_new ();
gtk_entry_set_text (GTK_ENTRY (label), title);
@@ -493,7 +530,7 @@ animation_xsheet_add_headers (AnimationXSheet *xsheet,
gtk_widget_show (GTK_WIDGET (item));
gtk_table_attach (GTK_TABLE (xsheet->priv->track_layout),
- toolbar, level * 9 + 9, level * 9 + 11, 0, 1,
+ toolbar, level * 9 + 10, level * 9 + 12, 0, 1,
GTK_FILL, GTK_FILL, 0, 0);
gtk_widget_show (toolbar);
@@ -563,7 +600,7 @@ animation_xsheet_add_headers (AnimationXSheet *xsheet,
gtk_widget_show (GTK_WIDGET (item));
gtk_table_attach (GTK_TABLE (xsheet->priv->track_layout),
- toolbar, level * 9 + 2, level * 9 + 9, 0, 1,
+ toolbar, level * 9 + 3, level * 9 + 10, 0, 1,
GTK_FILL, GTK_FILL, 0, 0);
gtk_widget_show (toolbar);
}
@@ -735,11 +772,41 @@ animation_xsheet_add_frames (AnimationXSheet *xsheet,
gtk_widget_show (label);
gtk_widget_show (frame);
+ /* Create effect button. */
+ frame = gtk_frame_new (NULL);
+ gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_OUT);
+ gtk_table_attach (GTK_TABLE (xsheet->priv->track_layout),
+ frame, 1, 2, i + 2, i + 3,
+ GTK_FILL, GTK_FILL, 0, 0);
+
+ label = gtk_toggle_button_new ();
+ xsheet->priv->effect_buttons = g_list_append (xsheet->priv->effect_buttons,
+ label);
+ g_object_set_data (G_OBJECT (label), "frame-position",
+ GINT_TO_POINTER (i));
+ gtk_button_set_relief (GTK_BUTTON (label), GTK_RELIEF_NONE);
+ gtk_button_set_focus_on_click (GTK_BUTTON (label), FALSE);
+ g_signal_connect (label, "button-press-event",
+ G_CALLBACK (animation_xsheet_effect_clicked),
+ xsheet);
+ g_signal_connect (animation_cel_animation_get_main_camera (xsheet->priv->animation),
+ "keyframe-set",
+ G_CALLBACK (on_camera_keyframe_set),
+ xsheet);
+ g_signal_connect (animation_cel_animation_get_main_camera (xsheet->priv->animation),
+ "keyframe-deleted",
+ G_CALLBACK (on_camera_keyframe_deleted),
+ xsheet);
+ gtk_container_add (GTK_CONTAINER (frame), label);
+
+ gtk_widget_show (label);
+ gtk_widget_show (frame);
+
/* Create new comment fields. */
frame = gtk_frame_new (NULL);
gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
gtk_table_attach (GTK_TABLE (xsheet->priv->track_layout),
- frame, n_tracks * 9 + 1, n_tracks * 9 + 6, i + 2, i + 3,
+ frame, n_tracks * 9 + 2, n_tracks * 9 + 7, i + 2, i + 3,
GTK_FILL, GTK_FILL, 0, 0);
comment_field = gtk_text_view_new ();
xsheet->priv->comment_fields = g_list_append (xsheet->priv->comment_fields,
@@ -836,6 +903,24 @@ animation_xsheet_remove_frames (AnimationXSheet *xsheet,
item->prev = NULL;
g_list_free (item);
+ /* Remove the effect button. */
+ item = g_list_nth (xsheet->priv->effect_buttons, position);
+ for (iter = item, i = position; iter && i < position + n_frames; iter = iter->next)
+ {
+ gtk_widget_destroy (gtk_widget_get_parent (iter->data));
+ }
+ if (item->prev)
+ item->prev->next = iter;
+ else
+ xsheet->priv->effect_buttons = iter;
+ if (iter)
+ {
+ iter->prev->next = NULL;
+ iter->prev = item->prev;
+ }
+ item->prev = NULL;
+ g_list_free (item);
+
/* Remove the comments field. */
item = g_list_nth (xsheet->priv->comment_fields,
position);
@@ -877,6 +962,7 @@ static void
animation_xsheet_reset_layout (AnimationXSheet *xsheet)
{
GtkWidget *frame;
+ GtkWidget *image;
GtkWidget *label;
gint n_tracks;
gint n_frames;
@@ -887,6 +973,8 @@ animation_xsheet_reset_layout (AnimationXSheet *xsheet)
NULL);
g_list_free_full (xsheet->priv->cels, (GDestroyNotify) g_list_free);
xsheet->priv->cels = NULL;
+ g_list_free (xsheet->priv->effect_buttons);
+ xsheet->priv->effect_buttons = NULL;
g_list_free (xsheet->priv->comment_fields);
xsheet->priv->comment_fields = NULL;
g_list_free (xsheet->priv->second_separators);
@@ -926,7 +1014,7 @@ animation_xsheet_reset_layout (AnimationXSheet *xsheet)
/* Add 4 columns for track names and 1 row for frame numbers. */
gtk_table_resize (GTK_TABLE (xsheet->priv->track_layout),
(guint) (n_frames + 2),
- (guint) (n_tracks * 9 + 6));
+ (guint) (n_tracks * 9 + 7));
animation_xsheet_add_frames (xsheet, 0, n_frames);
/* Titles. */
@@ -935,13 +1023,20 @@ animation_xsheet_reset_layout (AnimationXSheet *xsheet)
animation_xsheet_add_headers (xsheet, j);
}
+ image = gtk_image_new_from_icon_name ("camera-video",
+ GTK_ICON_SIZE_SMALL_TOOLBAR);
+ gtk_table_attach (GTK_TABLE (xsheet->priv->track_layout), image,
+ 1, 2, 1, 2,
+ GTK_FILL, GTK_FILL, 0, 0);
+ gtk_widget_show (image);
+
frame = gtk_frame_new (NULL);
label = gtk_label_new (_("Comments"));
gtk_container_add (GTK_CONTAINER (frame), label);
gtk_widget_show (label);
gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_OUT);
gtk_table_attach (GTK_TABLE (xsheet->priv->track_layout),
- frame, n_tracks * 9 + 1, n_tracks * 9 + 6, 1, 2,
+ frame, n_tracks * 9 + 2, n_tracks * 9 + 7, 1, 2,
GTK_FILL, GTK_FILL, 0, 0);
xsheet->priv->comments_title = frame;
gtk_widget_show (frame);
@@ -1143,19 +1238,62 @@ on_animation_duration_changed (Animation *animation,
}
static void
-on_animation_stopped (AnimationPlayback *playback,
- AnimationXSheet *xsheet)
+on_camera_keyframe_set (AnimationCamera *camera,
+ gint position,
+ AnimationXSheet *xsheet)
+{
+ GtkWidget *button;
+
+ button = g_list_nth_data (xsheet->priv->effect_buttons,
+ position);
+
+ if (button)
+ {
+ GtkWidget *image;
+
+ if (gtk_bin_get_child (GTK_BIN (button)))
+ {
+ gtk_container_remove (GTK_CONTAINER (button),
+ gtk_bin_get_child (GTK_BIN (button)));
+ }
+ image = gtk_image_new_from_icon_name ("gtk-ok",
+ GTK_ICON_SIZE_SMALL_TOOLBAR);
+ gtk_container_add (GTK_CONTAINER (button), image);
+ gtk_widget_show (image);
+ }
+}
+
+static void
+on_camera_keyframe_deleted (AnimationCamera *camera,
+ gint position,
+ AnimationXSheet *xsheet)
+{
+ GtkWidget *button;
+
+ button = g_list_nth_data (xsheet->priv->effect_buttons,
+ position);
+
+ if (button && gtk_bin_get_child (GTK_BIN (button)))
+ {
+ gtk_container_remove (GTK_CONTAINER (button),
+ gtk_bin_get_child (GTK_BIN (button)));
+ }
+}
+
+static void
+on_playback_stopped (AnimationPlayback *playback,
+ AnimationXSheet *xsheet)
{
animation_xsheet_jump (xsheet,
animation_playback_get_position (playback));
}
static void
-on_animation_rendered (AnimationPlayback *playback,
- gint frame_number,
- GeglBuffer *buffer,
- gboolean must_draw_null,
- AnimationXSheet *xsheet)
+on_playback_rendered (AnimationPlayback *playback,
+ gint frame_number,
+ GeglBuffer *buffer,
+ gboolean must_draw_null,
+ AnimationXSheet *xsheet)
{
animation_xsheet_jump (xsheet, frame_number);
}
@@ -1339,6 +1477,23 @@ animation_xsheet_suite_cancelled (GtkWidget *button,
}
static gboolean
+animation_xsheet_effect_clicked (GtkWidget *button,
+ GdkEvent *event,
+ AnimationXSheet *xsheet)
+{
+ gpointer position;
+
+ position = g_object_get_data (G_OBJECT (button), "frame-position");
+
+ animation_keyframe_view_show (ANIMATION_KEYFRAME_VIEW (xsheet->priv->keyframe_view),
+ ANIMATION_CEL_ANIMATION (xsheet->priv->animation),
+ GPOINTER_TO_INT (position));
+
+ /* All handled here. */
+ return TRUE;
+}
+
+static gboolean
animation_xsheet_frame_clicked (GtkWidget *button,
GdkEvent *event,
AnimationXSheet *xsheet)
@@ -1604,7 +1759,8 @@ animation_xsheet_cel_clicked (GtkWidget *button,
}
else if (event->type == GDK_BUTTON_RELEASE && event->button == 3)
{
- animation_menu_cell (xsheet->priv->animation, event,
+ animation_menu_cell (xsheet->priv->animation,
+ event,
GPOINTER_TO_INT (position),
GPOINTER_TO_INT (track_num));
}
@@ -1951,7 +2107,7 @@ animation_xsheet_attach_cel (AnimationXSheet *xsheet,
gtk_container_add (GTK_CONTAINER (frame), cel);
}
gtk_table_attach (GTK_TABLE (xsheet->priv->track_layout),
- frame, track * 9 + 1, track * 9 + 10,
+ frame, track * 9 + 2, track * 9 + 11,
pos + 2, pos + 3,
GTK_FILL, GTK_FILL, 0, 0);
gtk_widget_show (frame);
diff --git a/plug-ins/animation-play/widgets/animation-xsheet.h
b/plug-ins/animation-play/widgets/animation-xsheet.h
index 4a4dfba..1e90e13 100755
--- a/plug-ins/animation-play/widgets/animation-xsheet.h
+++ b/plug-ins/animation-play/widgets/animation-xsheet.h
@@ -48,6 +48,7 @@ GType animation_xsheet_get_type (void) G_GNUC_CONST;
GtkWidget * animation_xsheet_new (AnimationCelAnimation *animation,
AnimationPlayback *playback,
- GtkWidget *layer_view);
+ GtkWidget *layer_view,
+ GtkWidget *keyframe_view);
#endif /* __ANIMATION_XSHEET_H__ */
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]