[gimp/wip/animation: 372/373] plug-ins: add zoom keyframing for the animation plug-in.



commit b502befa7b48301f17e789dcb4bef6fa18d807d7
Author: Jehan <jehan girinstud io>
Date:   Thu Sep 28 02:11:26 2017 +0200

    plug-ins: add zoom keyframing for the animation plug-in.
    
    It's still very edgy, but that's a start.

 plug-ins/animation-play/animation-utils.c          |    6 +-
 plug-ins/animation-play/core/animation-camera.c    |  315 ++++++++++++++++----
 plug-ins/animation-play/core/animation-camera.h    |   51 ++--
 .../animation-play/core/animation-celanimation.c   |   96 ++++--
 plug-ins/animation-play/widgets/animation-dialog.c |    6 +-
 .../widgets/animation-keyframe-view.c              |  131 +++++++--
 plug-ins/animation-play/widgets/animation-xsheet.c |   22 +-
 7 files changed, 468 insertions(+), 159 deletions(-)
---
diff --git a/plug-ins/animation-play/animation-utils.c b/plug-ins/animation-play/animation-utils.c
index f4bfdd0..8b83341 100755
--- a/plug-ins/animation-play/animation-utils.c
+++ b/plug-ins/animation-play/animation-utils.c
@@ -150,12 +150,10 @@ normal_blend (gint        width,
   gdouble     offx;
   gdouble     offy;
 
-  g_return_val_if_fail (source_scale_ratio >  0.0 &&
-                        source_scale_ratio <= 1.0 &&
+  g_return_val_if_fail (source_scale_ratio >=  0.0 &&
                         source_buffer             &&
                         (! backdrop_buffer ||
-                         (backdrop_scale_ratio >= 0.1 &&
-                          backdrop_scale_ratio <= 1.0)),
+                         backdrop_scale_ratio >= 0.0),
                         NULL);
 
   /* Panel image. */
diff --git a/plug-ins/animation-play/core/animation-camera.c b/plug-ins/animation-play/core/animation-camera.c
index 275085e..d3151d2 100644
--- a/plug-ins/animation-play/core/animation-camera.c
+++ b/plug-ins/animation-play/core/animation-camera.c
@@ -35,7 +35,7 @@ enum
 
 enum
 {
-  OFFSETS_CHANGED,
+  CAMERA_CHANGED,
   KEYFRAME_SET,
   KEYFRAME_DELETED,
   LAST_SIGNAL
@@ -54,10 +54,14 @@ struct _AnimationCameraPrivate
 
   /* Panning and tilting. */
   GList     *offsets;
+  GList     *zoom;
 
   /* Preview */
   Offset    *preview_offset;
   gint       preview_position;
+  gdouble    preview_scale;
+
+  gboolean   block_signals;
 };
 
 static void   animation_camera_finalize             (GObject         *object);
@@ -70,12 +74,13 @@ static void   animation_camera_get_property         (GObject         *object,
                                                      GValue          *value,
                                                      GParamSpec      *pspec);
 
-static void   animation_camera_emit_offsets_changed (AnimationCamera *camera,
+static void   animation_camera_emit_camera_changed  (AnimationCamera *camera,
                                                      gint             position);
 static void   animation_camera_get_real             (AnimationCamera *camera,
                                                      gint             position,
                                                      gint            *x_offset,
-                                                     gint            *y_offset);
+                                                     gint            *y_offset,
+                                                     gdouble         *scale);
 
 G_DEFINE_TYPE (AnimationCamera, animation_camera, G_TYPE_OBJECT)
 
@@ -93,19 +98,20 @@ animation_camera_class_init (AnimationCameraClass *klass)
   object_class->set_property = animation_camera_set_property;
 
   /**
-   * AnimationCamera::offsets-changed:
+   * AnimationCamera::camera-changed:
    * @camera: the #AnimationCamera.
    * @position:
    * @duration:
    *
-   * The ::offsets-changed signal will be emitted when camera offsets
-   * were updated between [@position; @position + @duration[.
+   * The ::camera-changed signal will be emitted when camera offsets,
+   * zoom, or other characteristics were updated between
+   * [@position; @position + @duration[.
    */
-  signals[OFFSETS_CHANGED] =
-    g_signal_new ("offsets-changed",
+  signals[CAMERA_CHANGED] =
+    g_signal_new ("camera-changed",
                   G_TYPE_FROM_CLASS (klass),
                   G_SIGNAL_RUN_FIRST,
-                  G_STRUCT_OFFSET (AnimationCameraClass, offsets_changed),
+                  G_STRUCT_OFFSET (AnimationCameraClass, camera_changed),
                   NULL, NULL,
                   NULL,
                   G_TYPE_NONE,
@@ -125,7 +131,7 @@ animation_camera_class_init (AnimationCameraClass *klass)
     g_signal_new ("keyframe-set",
                   G_TYPE_FROM_CLASS (klass),
                   G_SIGNAL_RUN_FIRST,
-                  G_STRUCT_OFFSET (AnimationCameraClass, offsets_changed),
+                  G_STRUCT_OFFSET (AnimationCameraClass, keyframe_set),
                   NULL, NULL,
                   NULL,
                   G_TYPE_NONE,
@@ -144,7 +150,7 @@ animation_camera_class_init (AnimationCameraClass *klass)
     g_signal_new ("keyframe-deleted",
                   G_TYPE_FROM_CLASS (klass),
                   G_SIGNAL_RUN_FIRST,
-                  G_STRUCT_OFFSET (AnimationCameraClass, offsets_changed),
+                  G_STRUCT_OFFSET (AnimationCameraClass, keyframe_deleted),
                   NULL, NULL,
                   NULL,
                   G_TYPE_NONE,
@@ -190,21 +196,32 @@ animation_camera_new (Animation *animation)
 }
 
 gboolean
-animation_camera_has_keyframe (AnimationCamera *camera,
-                               gint             position)
+animation_camera_has_offset_keyframe (AnimationCamera *camera,
+                                      gint             position)
 {
   g_return_val_if_fail (position >= 0 &&
                         position < animation_get_duration (camera->priv->animation),
                         FALSE);
 
-  return (g_list_nth_data (camera->priv->offsets, position) != NULL);
+  return g_list_nth_data (camera->priv->offsets, position) != NULL;
+}
+
+gboolean
+animation_camera_has_zoom_keyframe (AnimationCamera *camera,
+                                    gint             position)
+{
+  g_return_val_if_fail (position >= 0 &&
+                        position < animation_get_duration (camera->priv->animation),
+                        FALSE);
+
+  return g_list_nth_data (camera->priv->zoom, position) != NULL;
 }
 
 void
-animation_camera_set_keyframe (AnimationCamera *camera,
-                               gint             position,
-                               gint             x,
-                               gint             y)
+animation_camera_set_offsets (AnimationCamera *camera,
+                              gint             position,
+                              gint             x,
+                              gint             y)
 {
   GList  *iter;
   Offset *offset;
@@ -238,28 +255,92 @@ animation_camera_set_keyframe (AnimationCamera *camera,
   offset->x = x;
   offset->y = y;
 
-  g_signal_emit (camera, signals[KEYFRAME_SET], 0, position);
-  animation_camera_emit_offsets_changed (camera, position);
+  if (! camera->priv->block_signals)
+    {
+      g_signal_emit (camera, signals[KEYFRAME_SET], 0, position);
+      animation_camera_emit_camera_changed (camera, position);
+    }
+}
+
+void
+animation_camera_zoom (AnimationCamera *camera,
+                       gint             position,
+                       gdouble          scale)
+{
+  GList   *iter;
+  gdouble *zoom;
+
+  g_return_if_fail (position >= 0 &&
+                    position < animation_get_duration (camera->priv->animation));
+
+  iter = g_list_nth (camera->priv->zoom, position);
+
+  if (! iter)
+    {
+      gint length = g_list_length (camera->priv->zoom);
+      gint i;
+
+      for (i = length; i < position; i++)
+        {
+          camera->priv->zoom = g_list_append (camera->priv->zoom, NULL);
+        }
+      zoom = g_new (gdouble, 1);
+      camera->priv->zoom = g_list_append (camera->priv->zoom, zoom);
+    }
+  else
+    {
+      if (! iter->data)
+        {
+          iter->data = g_new (gdouble, 1);
+        }
+      zoom = iter->data;
+    }
+  *zoom = scale;
+
+  if (! camera->priv->block_signals)
+    {
+      g_signal_emit (camera, signals[KEYFRAME_SET], 0, position);
+      animation_camera_emit_camera_changed (camera, position);
+    }
 }
 
 void
-animation_camera_delete_keyframe (AnimationCamera *camera,
-                                  gint             position)
+animation_camera_delete_offset_keyframe (AnimationCamera *camera,
+                                         gint             position)
 {
-  GList *offset;
+  GList *iter;
 
   g_return_if_fail (position >= 0 &&
                     position < animation_get_duration (camera->priv->animation));
 
-  offset = g_list_nth (camera->priv->offsets, position);
+  iter = g_list_nth (camera->priv->offsets, position);
+  if (iter && iter->data)
+    {
+      g_free (iter->data);
+      iter->data = NULL;
+
+      g_signal_emit (camera, signals[KEYFRAME_DELETED], 0, position);
+      animation_camera_emit_camera_changed (camera, position);
+    }
+}
 
-  if (offset && offset->data)
+void
+animation_camera_delete_zoom_keyframe (AnimationCamera *camera,
+                                       gint             position)
+{
+  GList *iter;
+
+  g_return_if_fail (position >= 0 &&
+                    position < animation_get_duration (camera->priv->animation));
+
+  iter = g_list_nth (camera->priv->zoom, position);
+  if (iter && iter->data)
     {
-      g_free (offset->data);
-      offset->data = NULL;
+      g_free (iter->data);
+      iter->data = NULL;
 
       g_signal_emit (camera, signals[KEYFRAME_DELETED], 0, position);
-      animation_camera_emit_offsets_changed (camera, position);
+      animation_camera_emit_camera_changed (camera, position);
     }
 }
 
@@ -267,7 +348,8 @@ void
 animation_camera_preview_keyframe (AnimationCamera *camera,
                                    gint             position,
                                    gint             x,
-                                   gint             y)
+                                   gint             y,
+                                   gdouble          scale)
 {
   g_return_if_fail (position >= 0 &&
                     position < animation_get_duration (camera->priv->animation));
@@ -277,9 +359,10 @@ animation_camera_preview_keyframe (AnimationCamera *camera,
 
   camera->priv->preview_offset->x = x;
   camera->priv->preview_offset->y = y;
+  camera->priv->preview_scale     = scale;
   camera->priv->preview_position  = position;
 
-  g_signal_emit (camera, signals[OFFSETS_CHANGED], 0,
+  g_signal_emit (camera, signals[CAMERA_CHANGED], 0,
                  position, 1);
 }
 
@@ -288,49 +371,70 @@ animation_camera_apply_preview (AnimationCamera *camera)
 {
   if (camera->priv->preview_offset)
     {
-      gint preview_offset_x;
-      gint preview_offset_y;
-      gint real_offset_x;
-      gint real_offset_y;
-      gint position;
+      gint    preview_offset_x;
+      gint    preview_offset_y;
+      gdouble preview_scale;
+      gint    real_offset_x;
+      gint    real_offset_y;
+      gdouble real_scale;
+      gint    position;
 
       animation_camera_get (camera, camera->priv->preview_position,
-                            &preview_offset_x, &preview_offset_y);
+                            &preview_offset_x, &preview_offset_y,
+                            &preview_scale);
       animation_camera_get_real (camera, camera->priv->preview_position,
-                                 &real_offset_x, &real_offset_y);
+                                 &real_offset_x, &real_offset_y,
+                                 &real_scale);
 
       g_free (camera->priv->preview_offset);
       camera->priv->preview_offset    = NULL;
       position = camera->priv->preview_position;
       camera->priv->preview_position  = -1;
 
+      /* Do not run the changed signal twice and recompute twice the
+       * same frame. Just a little internal trick. */
+      camera->priv->block_signals = TRUE;
       if (preview_offset_x != real_offset_x ||
           preview_offset_y != real_offset_y)
-        animation_camera_set_keyframe (camera, position,
-                                       preview_offset_x,
-                                       preview_offset_y);
+        {
+          animation_camera_set_offsets (camera, position,
+                                        preview_offset_x,
+                                        preview_offset_y);
+        }
+      if (preview_scale != real_scale)
+        {
+          animation_camera_zoom (camera, position, preview_scale);
+        }
+      camera->priv->block_signals = FALSE;
+      g_signal_emit (camera, signals[KEYFRAME_SET], 0, position);
+      animation_camera_emit_camera_changed (camera, position);
     }
 }
 
 void
 animation_camera_reset_preview (AnimationCamera *camera)
 {
-  gboolean offsets_changed = FALSE;
+  gboolean changed = FALSE;
   gint     position_changed;
 
   if (camera->priv->preview_offset)
     {
-      gint preview_offset_x;
-      gint preview_offset_y;
-      gint real_offset_x;
-      gint real_offset_y;
+      gint    preview_offset_x;
+      gint    preview_offset_y;
+      gdouble preview_scale;
+      gint    real_offset_x;
+      gint    real_offset_y;
+      gdouble real_scale;
 
       animation_camera_get (camera, camera->priv->preview_position,
-                            &preview_offset_x, &preview_offset_y);
+                            &preview_offset_x, &preview_offset_y,
+                            &preview_scale);
       animation_camera_get_real (camera, camera->priv->preview_position,
-                                 &real_offset_x, &real_offset_y);
-      offsets_changed = (preview_offset_x != real_offset_x ||
-                         preview_offset_y != real_offset_y);
+                                 &real_offset_x, &real_offset_y,
+                                 &real_scale);
+      changed = (preview_offset_x != real_offset_x ||
+                 preview_offset_y != real_offset_y ||
+                 preview_scale != real_scale);
       position_changed = camera->priv->preview_position;
 
       g_free (camera->priv->preview_offset);
@@ -339,8 +443,8 @@ animation_camera_reset_preview (AnimationCamera *camera)
 
   camera->priv->preview_position  = -1;
 
-  if (offsets_changed)
-    g_signal_emit (camera, signals[OFFSETS_CHANGED], 0,
+  if (changed)
+    g_signal_emit (camera, signals[CAMERA_CHANGED], 0,
                    position_changed, 1);
 }
 
@@ -348,16 +452,20 @@ void
 animation_camera_get (AnimationCamera *camera,
                       gint             position,
                       gint            *x_offset,
-                      gint            *y_offset)
+                      gint            *y_offset,
+                      gdouble         *scale)
 {
   if (camera->priv->preview_position == position)
     {
       *x_offset = camera->priv->preview_offset->x;
       *y_offset = camera->priv->preview_offset->y;
+      *scale    = camera->priv->preview_scale;
     }
   else
     {
-      animation_camera_get_real (camera, position, x_offset, y_offset);
+      animation_camera_get_real (camera, position,
+                                 x_offset, y_offset,
+                                 scale);
     }
 }
 
@@ -367,6 +475,7 @@ static void
 animation_camera_finalize (GObject *object)
 {
   g_list_free_full (ANIMATION_CAMERA (object)->priv->offsets, g_free);
+  g_list_free_full (ANIMATION_CAMERA (object)->priv->zoom, g_free);
   G_OBJECT_CLASS (parent_class)->finalize (object);
 }
 
@@ -412,8 +521,8 @@ animation_camera_get_property (GObject      *object,
 }
 
 static void
-animation_camera_emit_offsets_changed (AnimationCamera *camera,
-                                       gint             position)
+animation_camera_emit_camera_changed (AnimationCamera *camera,
+                                      gint             position)
 {
   GList *iter;
   gint   prev_keyframe;
@@ -426,7 +535,11 @@ animation_camera_emit_offsets_changed (AnimationCamera *camera,
       iter = g_list_nth (camera->priv->offsets, i);
       for (; iter && ! iter->data; iter = iter->prev, i--)
         ;
+      iter = g_list_nth (camera->priv->zoom, i);
       prev_keyframe = i + 1;
+      for (; iter && ! iter->data; iter = iter->prev, i--)
+        ;
+      prev_keyframe = MIN (i + 1, prev_keyframe);
     }
   else
     {
@@ -439,15 +552,23 @@ animation_camera_emit_offsets_changed (AnimationCamera *camera,
       for (; iter && ! iter->data; iter = iter->next, i++)
         ;
       if (iter && iter->data)
-        next_keyframe = i - 1;
+        {
+          next_keyframe = i - 1;
+          iter = g_list_nth (camera->priv->zoom, i);
+          for (; iter && ! iter->data; iter = iter->next, i++)
+            ;
+          next_keyframe = MAX (i - 1, next_keyframe);
+        }
       else
-        next_keyframe = animation_get_duration (camera->priv->animation) - 1;
+        {
+          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,
+  g_signal_emit (camera, signals[CAMERA_CHANGED], 0,
                  prev_keyframe, next_keyframe - prev_keyframe + 1);
 }
 
@@ -455,19 +576,21 @@ static void
 animation_camera_get_real (AnimationCamera *camera,
                            gint             position,
                            gint            *x_offset,
-                           gint            *y_offset)
+                           gint            *y_offset,
+                           gdouble         *scale)
 {
-  Offset *keyframe;
+  Offset  *position_keyframe;
+  gdouble *zoom_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)
+  position_keyframe = g_list_nth_data (camera->priv->offsets, position);
+  if (position_keyframe)
     {
       /* There is a keyframe to this exact position. Use its values. */
-      *x_offset = keyframe->x;
-      *y_offset = keyframe->y;
+      *x_offset = position_keyframe->x;
+      *y_offset = position_keyframe->y;
     }
   else
     {
@@ -531,4 +654,68 @@ animation_camera_get_real (AnimationCamera *camera,
                                          (next_keyframe_pos - prev_keyframe_pos);
         }
     }
+
+  zoom_keyframe = g_list_nth_data (camera->priv->zoom, position);
+  if (zoom_keyframe)
+    {
+      /* There is a keyframe to this exact position. Use its values. */
+      *scale = *zoom_keyframe;
+    }
+  else
+    {
+      GList  *iter;
+      gdouble *prev_keyframe = NULL;
+      gdouble *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->zoom) - 1);
+          iter = g_list_nth (camera->priv->zoom, 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->zoom, 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)
+        {
+          *scale = 1.0;
+        }
+      else if (prev_keyframe == NULL)
+        {
+          *scale = *next_keyframe;
+        }
+      else if (next_keyframe == NULL)
+        {
+          *scale = *prev_keyframe;
+        }
+      else
+        {
+          /* XXX No curve editing or anything like this yet.
+           * All keyframing is linear in this first version.
+           */
+          *scale = *prev_keyframe + (position - prev_keyframe_pos) *
+                                    (*next_keyframe - *prev_keyframe) /
+                                    (next_keyframe_pos - prev_keyframe_pos);
+        }
+    }
 }
diff --git a/plug-ins/animation-play/core/animation-camera.h b/plug-ins/animation-play/core/animation-camera.h
index 344e6a8..d4d2681 100644
--- a/plug-ins/animation-play/core/animation-camera.h
+++ b/plug-ins/animation-play/core/animation-camera.h
@@ -44,7 +44,7 @@ struct _AnimationCameraClass
   GObjectClass              parent_class;
 
   /* Signals */
-  void         (*offsets_changed)   (AnimationCamera *camera,
+  void         (*camera_changed)    (AnimationCamera *camera,
                                      gint             position,
                                      gint             duration);
   void         (*keyframe_set)      (AnimationCamera *camera,
@@ -53,29 +53,38 @@ struct _AnimationCameraClass
                                      gint             position);
 };
 
-GType             animation_camera_get_type         (void) G_GNUC_CONST;
+GType             animation_camera_get_type               (void) G_GNUC_CONST;
 
-AnimationCamera * animation_camera_new              (Animation       *animation);
+AnimationCamera * animation_camera_new                    (Animation       *animation);
 
-gboolean          animation_camera_has_keyframe     (AnimationCamera *camera,
-                                                     gint             position);
+gboolean          animation_camera_has_offset_keyframe    (AnimationCamera *camera,
+                                                           gint             position);
+gboolean          animation_camera_has_zoom_keyframe      (AnimationCamera *camera,
+                                                           gint             position);
 
-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_preview_keyframe (AnimationCamera *camera,
-                                                     gint             position,
-                                                     gint             x,
-                                                     gint             y);
-void              animation_camera_apply_preview    (AnimationCamera *camera);
-void              animation_camera_reset_preview    (AnimationCamera *camera);
+void              animation_camera_set_offsets            (AnimationCamera *camera,
+                                                           gint             position,
+                                                           gint             x,
+                                                           gint             y);
+void              animation_camera_zoom                   (AnimationCamera *camera,
+                                                           gint             position,
+                                                           gdouble          scale);
+void              animation_camera_delete_offset_keyframe (AnimationCamera *camera,
+                                                           gint             position);
+void              animation_camera_delete_zoom_keyframe   (AnimationCamera *camera,
+                                                           gint             position);
+void              animation_camera_preview_keyframe       (AnimationCamera *camera,
+                                                           gint             position,
+                                                           gint             x,
+                                                           gint             y,
+                                                           gdouble          scale);
+void              animation_camera_apply_preview          (AnimationCamera *camera);
+void              animation_camera_reset_preview          (AnimationCamera *camera);
 
-void              animation_camera_get              (AnimationCamera *camera,
-                                                     gint             position,
-                                                     gint            *x_offset,
-                                                     gint            *y_offset);
+void              animation_camera_get                    (AnimationCamera *camera,
+                                                           gint             position,
+                                                           gint            *x_offset,
+                                                           gint            *y_offset,
+                                                           gdouble         *scale);
 
 #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 9c2ae7c..940bb55 100644
--- a/plug-ins/animation-play/core/animation-celanimation.c
+++ b/plug-ins/animation-play/core/animation-celanimation.c
@@ -133,7 +133,7 @@ static void      animation_cel_animation_text              (GMarkupParseContext
 
 /* Signal handling */
 
-static void      on_camera_offsets_changed                 (AnimationCamera        *camera,
+static void      on_camera_changed                         (AnimationCamera        *camera,
                                                             gint                    position,
                                                             gint                    duration,
                                                             AnimationCelAnimation  *animation);
@@ -187,8 +187,8 @@ animation_cel_animation_constructed (GObject *object)
 {
   AnimationCelAnimation *animation = ANIMATION_CEL_ANIMATION (object);
 
-  g_signal_connect (animation->priv->camera, "offsets-changed",
-                    G_CALLBACK (on_camera_offsets_changed),
+  g_signal_connect (animation->priv->camera, "camera-changed",
+                    G_CALLBACK (on_camera_changed),
                     animation);
 }
 
@@ -653,6 +653,7 @@ animation_cel_animation_create_frame (Animation *animation,
   gint                   preview_height;
   gint                   offset_x;
   gint                   offset_y;
+  gdouble                scale;
 
   cel_animation = ANIMATION_CEL_ANIMATION (animation);
   image_id = animation_get_image_id (animation);
@@ -661,7 +662,7 @@ animation_cel_animation_create_frame (Animation *animation,
   preview_height *= proxy_ratio;
   preview_width  *= proxy_ratio;
   animation_camera_get (cel_animation->priv->camera,
-                        position, &offset_x, &offset_y);
+                        position, &offset_x, &offset_y, &scale);
 
   for (iter = cel_animation->priv->tracks; iter; iter = iter->next)
     {
@@ -694,7 +695,7 @@ animation_cel_animation_create_frame (Animation *animation,
           gimp_drawable_offsets (layer, &layer_offx, &layer_offy);
           intermediate = normal_blend (preview_width, preview_height,
                                        buffer, 1.0, 0, 0,
-                                       source, proxy_ratio,
+                                       source, proxy_ratio * scale,
                                        layer_offx + offset_x,
                                        layer_offy + offset_y);
           g_object_unref (source);
@@ -868,16 +869,33 @@ animation_cel_animation_serialize (Animation   *animation,
 
   for (i = 0; i < priv->duration; i++)
     {
-      if (animation_camera_has_keyframe (priv->camera, i))
+      if (animation_camera_has_offset_keyframe (priv->camera, i) ||
+          animation_camera_has_zoom_keyframe (priv->camera, i))
         {
-          gint offset_x;
-          gint offset_y;
+          gint    offset_x;
+          gint    offset_y;
+          gdouble scale;
 
           animation_camera_get (priv->camera,
-                                i, &offset_x, &offset_y);
-          xml2 = g_markup_printf_escaped ("<keyframe " "position=\"%d\""
-                                          " x=\"%d\" y=\"%d\"/>",
-                                          i, offset_x, offset_y);
+                                i, &offset_x, &offset_y, &scale);
+          if (animation_camera_has_offset_keyframe (priv->camera, i) &&
+              animation_camera_has_zoom_keyframe (priv->camera, i))
+            {
+              xml2 = g_markup_printf_escaped ("<keyframe " "position=\"%d\""
+                                              " x=\"%d\" y=\"%d\" scale=\"%f\"/>",
+                                              i, offset_x, offset_y, scale);
+            }
+          else if (animation_camera_has_offset_keyframe (priv->camera, i))
+            {
+              xml2 = g_markup_printf_escaped ("<keyframe " "position=\"%d\""
+                                              " x=\"%d\" y=\"%d\"/>",
+                                              i, offset_x, offset_y);
+            }
+          else
+            {
+              xml2 = g_markup_printf_escaped ("<keyframe " "position=\"%d\""
+                                              " scale=\"%f\"/>", i, scale);
+            }
           tmp = xml;
           xml = g_strconcat (xml, xml2, NULL);
           g_free (tmp);
@@ -968,8 +986,8 @@ animation_cel_animation_update_paint_view (Animation *animation,
   gchar                 *prev_hash;
   gint                   num_layers;
   gint32                 image_id;
-  gint                   last_layer;
-  gint                   skin = 0;
+  gint                   last_layer = 0;
+  gint                   skin       = 0;
   gint                   i;
 
   cel_animation = ANIMATION_CEL_ANIMATION (animation);
@@ -1289,11 +1307,13 @@ animation_cel_animation_start_element (GMarkupParseContext  *context,
         }
       else
         {
-          gboolean has_x    = FALSE;
-          gboolean has_y    = FALSE;
-          gint     position = -1;
-          gint     x;
-          gint     y;
+          gboolean has_x     = FALSE;
+          gboolean has_y     = FALSE;
+          gboolean has_scale = FALSE;
+          gint     position  = -1;
+          gint     x         = 0;
+          gint     y         = 0;
+          gdouble  scale     = 1.0;
 
           while (*names && *values)
             {
@@ -1311,12 +1331,22 @@ animation_cel_animation_start_element (GMarkupParseContext  *context,
                   has_y = TRUE;
                   y = (gint) g_ascii_strtoll (*values, NULL, 10);
                 }
+              else if (strcmp (*names, "scale") == 0 && **values)
+                {
+                  has_scale = TRUE;
+                  scale = g_ascii_strtod (*values, NULL);
+                }
 
               names++;
               values++;
             }
-          if (position >= 0 && has_x && has_y)
-            animation_camera_set_keyframe (priv->camera, position, x, y);
+          if (position >= 0)
+            {
+              if (has_x && has_y)
+                animation_camera_set_offsets (priv->camera, position, x, y);
+              if (has_scale)
+                animation_camera_zoom (priv->camera, position, scale);
+            }
         }
       status->state = KEYFRAME_STATE;
       break;
@@ -1434,10 +1464,10 @@ animation_cel_animation_text (GMarkupParseContext  *context,
 /**** Signal handling ****/
 
 static void
-on_camera_offsets_changed (AnimationCamera       *camera,
-                           gint                   position,
-                           gint                   duration,
-                           AnimationCelAnimation *animation)
+on_camera_changed (AnimationCamera       *camera,
+                   gint                   position,
+                   gint                   duration,
+                   AnimationCelAnimation *animation)
 {
   g_signal_emit_by_name (animation, "frames-changed",
                          position, duration);
@@ -1472,13 +1502,15 @@ animation_cel_animation_get_hash (AnimationCelAnimation *animation,
                                   gint                   position,
                                   gboolean               layers_only)
 {
-  gchar *hash = g_strdup ("");
-  GList *iter;
-  gint   main_offset_x;
-  gint   main_offset_y;
+  gchar   *hash = g_strdup ("");
+  GList   *iter;
+  gint     main_offset_x;
+  gint     main_offset_y;
+  gdouble  scale;
 
   animation_camera_get (animation->priv->camera,
-                        position, &main_offset_x, &main_offset_y);
+                        position, &main_offset_x, &main_offset_y,
+                        &scale);
 
   /* Create the new buffer layer composition. */
   for (iter = animation->priv->tracks; iter; iter = iter->next)
@@ -1504,10 +1536,10 @@ animation_cel_animation_get_hash (AnimationCelAnimation *animation,
                 }
               else
                 {
-                  hash = g_strdup_printf ("%s[%d,%d]%d;",
+                  hash = g_strdup_printf ("%s[%d,%d]%fx%d;",
                                           hash,
                                           main_offset_x, main_offset_y,
-                                          tattoo);
+                                          scale, tattoo);
                 }
               g_free (tmp);
             }
diff --git a/plug-ins/animation-play/widgets/animation-dialog.c 
b/plug-ins/animation-play/widgets/animation-dialog.c
index 422de0a..185b1f7 100755
--- a/plug-ins/animation-play/widgets/animation-dialog.c
+++ b/plug-ins/animation-play/widgets/animation-dialog.c
@@ -2565,14 +2565,16 @@ da_button_motion (GtkWidget       *widget,
           gint                   position;
           gint                   x_offset;
           gint                   y_offset;
+          gdouble                scale;
 
           animation = ANIMATION_CEL_ANIMATION (priv->animation);
           camera = ANIMATION_CAMERA (animation_cel_animation_get_main_camera (animation));
           position = animation_playback_get_position (priv->playback);
-          animation_camera_get (camera, position, &x_offset, &y_offset);
+          animation_camera_get (camera, position, &x_offset, &y_offset, &scale);
           animation_camera_preview_keyframe (camera, position,
                                              x_offset + (event->x - p->x) / priv->zoom,
-                                             y_offset + (event->y - p->y) / priv->zoom);
+                                             y_offset + (event->y - p->y) / priv->zoom,
+                                             scale);
           p->x = event->x;
           p->y = event->y;
           return TRUE;
diff --git a/plug-ins/animation-play/widgets/animation-keyframe-view.c 
b/plug-ins/animation-play/widgets/animation-keyframe-view.c
index ea7eff6..f2e5308 100644
--- a/plug-ins/animation-play/widgets/animation-keyframe-view.c
+++ b/plug-ins/animation-play/widgets/animation-keyframe-view.c
@@ -37,11 +37,15 @@ struct _AnimationKeyFrameViewPrivate
   gint             position;
 
   GtkWidget       *offset_entry;
-  GtkWidget       *delete_button;
+  GtkWidget       *delete_offset_button;
+
+  GtkWidget       *scale_entry;
+  GtkWidget       *delete_scale_button;
 
   guint            update_source;
   gint             update_x_offset;
   gint             update_y_offset;
+  gdouble          update_scale;
   gint             update_position;
 };
 
@@ -51,13 +55,18 @@ static void     animation_keyframe_view_dispose      (GObject               *obj
 
 
 static gboolean animation_keyframe_update_source     (gpointer               user_data);
+
+static void     on_scale_entry_changed               (GtkSpinButton         *button,
+                                                      AnimationKeyFrameView *view);
 static void     on_offset_entry_changed              (GimpSizeEntry         *entry,
                                                       AnimationKeyFrameView *view);
 static void     on_offsets_changed                   (AnimationCamera       *camera,
                                                       gint                   position,
                                                       gint                   duration,
                                                       AnimationKeyFrameView *view);
-static void     on_delete_clicked                    (GtkButton             *button,
+static void     on_delete_offset_clicked             (GtkButton             *button,
+                                                      AnimationKeyFrameView *view);
+static void     on_delete_scale_clicked              (GtkButton             *button,
                                                       AnimationKeyFrameView *view);
 
 G_DEFINE_TYPE (AnimationKeyFrameView, animation_keyframe_view, GTK_TYPE_NOTEBOOK)
@@ -126,6 +135,7 @@ animation_keyframe_view_show (AnimationKeyFrameView *view,
   gdouble          yres;
   gint             x_offset;
   gint             y_offset;
+  gdouble          scale;
 
   camera = ANIMATION_CAMERA (animation_cel_animation_get_main_camera (animation));
 
@@ -136,10 +146,13 @@ animation_keyframe_view_show (AnimationKeyFrameView *view,
           view->priv->update_position != -1)
         {
           /* We jumped to another position. Apply the ongoing preview. */
-          animation_camera_set_keyframe (view->priv->camera,
-                                         view->priv->update_position,
-                                         view->priv->update_x_offset,
-                                         view->priv->update_y_offset);
+          animation_camera_set_offsets (view->priv->camera,
+                                        view->priv->update_position,
+                                        view->priv->update_x_offset,
+                                        view->priv->update_y_offset);
+          animation_camera_zoom (view->priv->camera,
+                                 view->priv->update_position,
+                                 view->priv->update_scale);
         }
       view->priv->camera   = camera;
       view->priv->position = position;
@@ -163,6 +176,9 @@ animation_keyframe_view_show (AnimationKeyFrameView *view,
       gimp_size_entry_set_resolution (GIMP_SIZE_ENTRY (view->priv->offset_entry),
                                       1, yres, TRUE);
 
+      g_signal_handlers_disconnect_by_func (view->priv->scale_entry,
+                                            G_CALLBACK (on_scale_entry_changed),
+                                            view);
       g_signal_handlers_disconnect_by_func (view->priv->offset_entry,
                                             G_CALLBACK (on_offset_entry_changed),
                                             view);
@@ -170,23 +186,32 @@ animation_keyframe_view_show (AnimationKeyFrameView *view,
                                             G_CALLBACK (on_offsets_changed),
                                             view);
       animation_camera_reset_preview (camera);
-      animation_camera_get (camera, position, &x_offset, &y_offset);
+      animation_camera_get (camera, position, &x_offset, &y_offset, &scale);
       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);
+      gtk_spin_button_set_value (GTK_SPIN_BUTTON (view->priv->scale_entry),
+                                 scale * 100.0);
+      g_signal_connect (view->priv->scale_entry, "value-changed",
+                        G_CALLBACK (on_scale_entry_changed),
+                        view);
       g_signal_connect (view->priv->offset_entry, "value-changed",
                         G_CALLBACK (on_offset_entry_changed),
                         view);
-      g_signal_connect (camera, "offsets-changed",
+      g_signal_connect (camera, "camera-changed",
                         G_CALLBACK (on_offsets_changed),
                         view);
       gtk_widget_show (GTK_WIDGET (view));
 
-      if (animation_camera_has_keyframe (camera, position))
-        gtk_widget_show (view->priv->delete_button);
+      if (animation_camera_has_offset_keyframe (camera, view->priv->position))
+        gtk_widget_show (view->priv->delete_offset_button);
       else
-        gtk_widget_hide (view->priv->delete_button);
+        gtk_widget_hide (view->priv->delete_offset_button);
+      if (animation_camera_has_zoom_keyframe (camera, view->priv->position))
+        gtk_widget_show (view->priv->delete_scale_button);
+      else
+        gtk_widget_hide (view->priv->delete_scale_button);
     }
 }
 
@@ -212,9 +237,10 @@ animation_keyframe_view_constructed (GObject *object)
   AnimationKeyFrameView *view = ANIMATION_KEYFRAME_VIEW (object);
   GtkWidget             *page;
   GtkWidget             *label;
+  GtkWidget             *widget;
 
   page = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
-  label = gtk_image_new_from_icon_name ("camera-video",
+  label = gtk_image_new_from_icon_name ("gimp-tool-move",
                                         GTK_ICON_SIZE_SMALL_TOOLBAR);
   gtk_notebook_append_page (GTK_NOTEBOOK (view), page,
                             label);
@@ -231,11 +257,33 @@ animation_keyframe_view_constructed (GObject *object)
   gtk_box_pack_start (GTK_BOX (page), view->priv->offset_entry, FALSE, FALSE, 0);
   gtk_widget_show (view->priv->offset_entry);
 
-  view->priv->delete_button = gtk_button_new_with_label (_("Reset"));
-  g_signal_connect (view->priv->delete_button, "clicked",
-                    G_CALLBACK (on_delete_clicked),
+  view->priv->delete_offset_button = gtk_button_new_with_label (_("Reset Offsets"));
+  g_signal_connect (view->priv->delete_offset_button, "clicked",
+                    G_CALLBACK (on_delete_offset_clicked),
                     view);
-  gtk_box_pack_end (GTK_BOX (page), view->priv->delete_button, FALSE, FALSE, 0);
+  gtk_box_pack_end (GTK_BOX (page), view->priv->delete_offset_button, FALSE, FALSE, 0);
+
+  gtk_widget_show (page);
+
+  page = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
+  label = gtk_image_new_from_icon_name ("gimp-scale",
+                                        GTK_ICON_SIZE_SMALL_TOOLBAR);
+  gtk_notebook_append_page (GTK_NOTEBOOK (view), page,
+                            label);
+
+  widget = gtk_label_new (_("Zoom: "));
+  gtk_box_pack_start (GTK_BOX (page), widget, FALSE, FALSE, 0);
+  gtk_widget_show (widget);
+
+  view->priv->scale_entry = gtk_spin_button_new_with_range (0.0, 1000.0, 1.0);
+  gtk_box_pack_start (GTK_BOX (page), view->priv->scale_entry, FALSE, FALSE, 0);
+  gtk_widget_show (view->priv->scale_entry);
+
+  view->priv->delete_scale_button = gtk_button_new_with_label (_("Reset Zoom"));
+  g_signal_connect (view->priv->delete_scale_button, "clicked",
+                    G_CALLBACK (on_delete_scale_clicked),
+                    view);
+  gtk_box_pack_end (GTK_BOX (page), view->priv->delete_scale_button, FALSE, FALSE, 0);
 
   gtk_widget_show (page);
 }
@@ -252,17 +300,26 @@ animation_keyframe_update_source (gpointer user_data)
       animation_camera_preview_keyframe (view->priv->camera,
                                          view->priv->update_position,
                                          view->priv->update_x_offset,
-                                         view->priv->update_y_offset);
+                                         view->priv->update_y_offset,
+                                         view->priv->update_scale);
     }
   return G_SOURCE_REMOVE;
 }
 
 static void
-on_offset_entry_changed (GimpSizeEntry         *entry,
+on_scale_entry_changed (GtkSpinButton         *button G_GNUC_UNUSED,
+                        AnimationKeyFrameView *view)
+{
+  on_offset_entry_changed (NULL, view);
+}
+
+static void
+on_offset_entry_changed (GimpSizeEntry         *entry G_GNUC_UNUSED,
                          AnimationKeyFrameView *view)
 {
   gdouble x_offset;
   gdouble y_offset;
+  gdouble scale;
 
   /* If a timeout is pending, remove before recreating in order to
    * postpone the camera update. */
@@ -271,10 +328,13 @@ on_offset_entry_changed (GimpSizeEntry         *entry,
       g_source_remove (view->priv->update_source);
     }
 
+  scale = gtk_spin_button_get_value (GTK_SPIN_BUTTON (view->priv->scale_entry)) / 100.0;
   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);
+
   view->priv->update_x_offset = x_offset;
   view->priv->update_y_offset = y_offset;
+  view->priv->update_scale    = scale;
   view->priv->update_position = view->priv->position;
   view->priv->update_source = g_timeout_add (10, animation_keyframe_update_source, view);
 }
@@ -290,31 +350,48 @@ on_offsets_changed (AnimationCamera       *camera,
     {
       gint x_offset;
       gint y_offset;
+      gdouble scale;
 
       g_signal_handlers_block_by_func (view->priv->offset_entry,
                                        G_CALLBACK (on_offset_entry_changed),
                                        view);
-      animation_camera_get (camera, view->priv->position, &x_offset, &y_offset);
+      animation_camera_get (camera, view->priv->position,
+                            &x_offset, &y_offset, &scale);
       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);
+      gtk_spin_button_set_value (GTK_SPIN_BUTTON (view->priv->scale_entry),
+                                 scale * 100.0);
       g_signal_handlers_unblock_by_func (view->priv->offset_entry,
                                          G_CALLBACK (on_offset_entry_changed),
                                          view);
 
-      if (animation_camera_has_keyframe (camera, view->priv->position))
-        gtk_widget_show (view->priv->delete_button);
+      if (animation_camera_has_offset_keyframe (camera, view->priv->position))
+        gtk_widget_show (view->priv->delete_offset_button);
+      else
+        gtk_widget_hide (view->priv->delete_offset_button);
+      if (animation_camera_has_zoom_keyframe (camera, view->priv->position))
+        gtk_widget_show (view->priv->delete_scale_button);
       else
-        gtk_widget_hide (view->priv->delete_button);
+        gtk_widget_hide (view->priv->delete_scale_button);
     }
 }
 
 static void
-on_delete_clicked (GtkButton             *button,
-                   AnimationKeyFrameView *view)
+on_delete_offset_clicked (GtkButton             *button,
+                          AnimationKeyFrameView *view)
+{
+  animation_camera_delete_offset_keyframe (view->priv->camera,
+                                           view->priv->position);
+  gtk_widget_hide (view->priv->delete_offset_button);
+}
+
+static void
+on_delete_scale_clicked (GtkButton             *button,
+                         AnimationKeyFrameView *view)
 {
-  animation_camera_delete_keyframe  (view->priv->camera,
-                                     view->priv->position);
-  gtk_widget_hide (view->priv->delete_button);
+  animation_camera_delete_zoom_keyframe (view->priv->camera,
+                                         view->priv->position);
+  gtk_widget_hide (view->priv->delete_scale_button);
 }
diff --git a/plug-ins/animation-play/widgets/animation-xsheet.c 
b/plug-ins/animation-play/widgets/animation-xsheet.c
index 19d4459..8f28f90 100755
--- a/plug-ins/animation-play/widgets/animation-xsheet.c
+++ b/plug-ins/animation-play/widgets/animation-xsheet.c
@@ -819,7 +819,8 @@ animation_xsheet_add_frames (AnimationXSheet *xsheet,
       xsheet->priv->effect_buttons = g_list_append (xsheet->priv->effect_buttons,
                                                     label);
 
-      if (animation_camera_has_keyframe (camera, i))
+      if (animation_camera_has_offset_keyframe (camera, i) ||
+          animation_camera_has_zoom_keyframe (camera, i))
         {
           GtkWidget *image;
 
@@ -1323,15 +1324,18 @@ 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)))
+  if (! animation_camera_has_offset_keyframe (camera, position) &&
+      ! animation_camera_has_zoom_keyframe (camera, position))
     {
-      gtk_container_remove (GTK_CONTAINER (button),
-                            gtk_bin_get_child (GTK_BIN (button)));
+      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)));
+        }
     }
 }
 


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