[clutter] timeline: Add support for step() progress



commit 4546f844080672580c469d75168630b737419c17
Author: Emmanuele Bassi <ebassi gnome org>
Date:   Thu Jul 19 19:47:48 2012 -0400

    timeline: Add support for step() progress
    
    The CSS3 Transitions specification from the W3C defines the possibility
    of using a parametrized step() timing function, with the following
    prototype:
    
            steps(n_steps, [ start | end ])
    
    where @n_steps represents the number of steps used to divide an interval
    between 0 and 1; the 'start' and 'end' tokens describe whether the value
    change should happen at the start of the transition, or at the end.
    
    For instance, the "steps(3, start)" timing function has the following
    profile:
    
      1 |           x
        |           |
        |       x---|
        |       '   |
        |   x---'   |
        |   '       |
      0 |---'       |
    
    Whereas the "steps(3, end)" timing function has the following profile:
    
      1 |       x---|
        |       '   |
        |   x---'   |
        |   '       |
        x---'       |
        |           |
      0 |           |
    
    Since ClutterTimeline uses an enumeration for controlling the progress
    mode, we need additional API to define the parameters of the steps()
    progress; for this reason, we need a CLUTTER_STEPS enumeration value,
    and a method for setting the number of steps and the value transition
    policy.
    
    The CSS3 Transitions spec helpfully also defines a step-start and a
    step-end shorthands, which expand to step(1, start) and step(1, end)
    respectively; we can provide a CLUTTER_STEP_START and CLUTTER_STEP_END
    enumeration values for those.

 clutter/clutter-easing.c          |   27 +++++++++
 clutter/clutter-easing.h          |    9 +++
 clutter/clutter-enums.h           |   31 ++++++++++
 clutter/clutter-timeline.c        |   96 ++++++++++++++++++++++++++++++++
 clutter/clutter-timeline.h        |    8 +++
 clutter/clutter.symbols           |    3 +
 tests/conform/Makefile.am         |    1 +
 tests/conform/test-conform-main.c |    2 +
 tests/conform/timeline-progress.c |  110 +++++++++++++++++++++++++++++++++++++
 9 files changed, 287 insertions(+), 0 deletions(-)
---
diff --git a/clutter/clutter-easing.c b/clutter/clutter-easing.c
index a8cf636..5f3dae2 100644
--- a/clutter/clutter-easing.c
+++ b/clutter/clutter-easing.c
@@ -379,6 +379,29 @@ clutter_ease_in_out_bounce (double t,
     return ease_out_bounce_internal (t * 2 - d, d) * 0.5 + 1.0 * 0.5;
 }
 
+static inline double
+ease_steps_end (double p,
+                int    n_steps)
+{
+  return floor (p * (double) n_steps) / (double) n_steps;
+}
+
+double
+clutter_ease_steps_start (double t,
+                          double d,
+                          int    n_steps)
+{
+  return 1.0 - ease_steps_end (1.0 - (t / d), n_steps);
+}
+
+double
+clutter_ease_steps_end (double t,
+                        double d,
+                        int    n_steps)
+{
+  return ease_steps_end ((t / d), n_steps);
+}
+
 /*< private >
  * _clutter_animation_modes:
  *
@@ -423,6 +446,10 @@ static const struct {
   { CLUTTER_EASE_OUT_BOUNCE,     clutter_ease_out_bounce, "easeOutBounce" },
   { CLUTTER_EASE_IN_OUT_BOUNCE,  clutter_ease_in_out_bounce, "easeInOutBounce" },
 
+  { CLUTTER_STEPS,               (ClutterEasingFunc) clutter_ease_steps_end, "steps" },
+  { CLUTTER_STEP_START,          (ClutterEasingFunc) clutter_ease_steps_start, "stepStart" },
+  { CLUTTER_STEP_END,            (ClutterEasingFunc) clutter_ease_steps_end, "stepEnd" },
+
   { CLUTTER_ANIMATION_LAST,      NULL, "sentinel" },
 };
 
diff --git a/clutter/clutter-easing.h b/clutter/clutter-easing.h
index 04f0e28..ab9556b 100644
--- a/clutter/clutter-easing.h
+++ b/clutter/clutter-easing.h
@@ -121,6 +121,15 @@ G_GNUC_INTERNAL
 double  clutter_ease_in_out_bounce      (double t,
                                          double d);
 
+G_GNUC_INTERNAL
+double  clutter_ease_steps_start        (double t,
+                                         double d,
+                                         int    steps);
+G_GNUC_INTERNAL
+double  clutter_ease_steps_end          (double t,
+                                         double d,
+                                         int    steps);
+
 G_END_DECLS
 
 #endif /* __CLUTTER_EASING_H__ */
diff --git a/clutter/clutter-enums.h b/clutter/clutter-enums.h
index b788a05..2df11a8 100644
--- a/clutter/clutter-enums.h
+++ b/clutter/clutter-enums.h
@@ -160,6 +160,12 @@ typedef enum { /*< prefix=CLUTTER_REQUEST >*/
  *   tweening, with bounce on end
  * @CLUTTER_EASE_IN_OUT_BOUNCE: exponentially decaying parabolic (bounce)
  *   tweening, with bounce on both ends
+ * @CLUTTER_STEPS: parametrized step function; see clutter_timeline_set_step_progress()
+ *   for further details. (Since 1.12)
+ * @CLUTTER_STEP_START: equivalent to %CLUTTER_STEPS with a number of steps
+ *   equal to 1, and a step mode of %CLUTTER_STEP_MODE_START. (Since 1.12)
+ * @CLUTTER_STEP_END: equivalent to %CLUTTER_STEPS with a number of steps
+ *   equal to 1, and a step mode of %CLUTTER_STEP_MODE_END. (Since: 1.12)
  * @CLUTTER_ANIMATION_LAST: last animation mode, used as a guard for
  *   registered global alpha functions
  *
@@ -233,6 +239,11 @@ typedef enum {
   CLUTTER_EASE_OUT_BOUNCE,
   CLUTTER_EASE_IN_OUT_BOUNCE,
 
+  /* step functions (see css3-transitions) */
+  CLUTTER_STEPS,
+  CLUTTER_STEP_START, /* steps(1, start) */
+  CLUTTER_STEP_END, /* steps(1, end) */
+
   /* guard, before registered alpha functions */
   CLUTTER_ANIMATION_LAST
 } ClutterAnimationMode;
@@ -1273,6 +1284,26 @@ typedef enum {
   CLUTTER_REPEAT_BOTH   = CLUTTER_REPEAT_X_AXIS | CLUTTER_REPEAT_Y_AXIS
 } ClutterContentRepeat;
 
+/**
+ * ClutterStepMode:
+ * @CLUTTER_STEP_MODE_START: The change in the value of a
+ *   %CLUTTER_STEP progress mode should occur at the start of
+ *   the transition
+ * @CLUTTER_STEP_MODE_END: The change in the value of a
+ *   %CLUTTER_STEP progress mode should occur at the end of
+ *   the transition
+ *
+ * Change the value transition of a step function.
+ *
+ * See clutter_timeline_set_step_progress().
+ *
+ * Since: 1.12
+ */
+typedef enum {
+  CLUTTER_STEP_MODE_START,
+  CLUTTER_STEP_MODE_END
+} ClutterStepMode;
+
 G_END_DECLS
 
 #endif /* __CLUTTER_ENUMS_H__ */
diff --git a/clutter/clutter-timeline.c b/clutter/clutter-timeline.c
index 64fbd35..cdde665 100644
--- a/clutter/clutter-timeline.c
+++ b/clutter/clutter-timeline.c
@@ -150,6 +150,9 @@ struct _ClutterTimelinePrivate
   GDestroyNotify progress_notify;
   ClutterAnimationMode progress_mode;
 
+  gint n_steps;
+  ClutterStepMode step_mode;
+
   guint is_playing         : 1;
 
   /* If we've just started playing and haven't yet gotten
@@ -812,6 +815,8 @@ clutter_timeline_init (ClutterTimeline *self)
                                  ClutterTimelinePrivate);
 
   priv->progress_mode = CLUTTER_LINEAR;
+  priv->n_steps = 1;
+  priv->step_mode = CLUTTER_STEP_MODE_END;
 }
 
 struct CheckIfMarkerHitClosure
@@ -2125,6 +2130,28 @@ clutter_timeline_progress_func (ClutterTimeline *timeline,
 {
   ClutterTimelinePrivate *priv = timeline->priv;
 
+  /* parametrized easing functions need to be handled separately */
+  switch (priv->progress_mode)
+    {
+    case CLUTTER_STEPS:
+      if (priv->step_mode == CLUTTER_STEP_MODE_START)
+        return clutter_ease_steps_start (elapsed, duration, priv->n_steps);
+      else if (priv->step_mode == CLUTTER_STEP_MODE_END)
+        return clutter_ease_steps_end (elapsed, duration, priv->n_steps);
+      else
+        g_assert_not_reached ();
+      break;
+
+    case CLUTTER_STEP_START:
+      return clutter_ease_steps_start (elapsed, duration, 1);
+
+    case CLUTTER_STEP_END:
+      return clutter_ease_steps_end (elapsed, duration, 1);
+
+    default:
+      break;
+    }
+
   return clutter_easing_for_mode (priv->progress_mode, elapsed, duration);
 }
 
@@ -2243,3 +2270,72 @@ clutter_timeline_get_current_repeat (ClutterTimeline *timeline)
 
   return timeline->priv->current_repeat;
 }
+
+/**
+ * clutter_timeline_set_step_progress:
+ * @timeline: a #ClutterTimeline
+ * @n_steps: the number of steps
+ * @step_mode: whether the change should happen at the start
+ *   or at the end of the step
+ *
+ * Sets the #ClutterTimeline:progress-mode of the @timeline to %CLUTTER_STEPS
+ * and provides the parameters of the step function.
+ *
+ * Since: 1.12
+ */
+void
+clutter_timeline_set_step_progress (ClutterTimeline *timeline,
+                                    gint             n_steps,
+                                    ClutterStepMode  step_mode)
+{
+  ClutterTimelinePrivate *priv;
+
+  g_return_if_fail (CLUTTER_IS_TIMELINE (timeline));
+  g_return_if_fail (n_steps > 0);
+
+  priv = timeline->priv;
+
+  if (priv->progress_mode == CLUTTER_STEPS &&
+      priv->n_steps == n_steps &&
+      priv->step_mode == step_mode)
+    return;
+
+  priv->n_steps = n_steps;
+  priv->step_mode = step_mode;
+  clutter_timeline_set_progress_mode (timeline, CLUTTER_STEPS);
+}
+
+/**
+ * clutter_timeline_get_step_progress:
+ * @timeline: a #ClutterTimeline
+ * @n_steps: (out): return location for the number of steps, or %NULL
+ * @step_mode: (out): return location for the value change policy,
+ *   or %NULL
+ *
+ * Retrieves the parameters of the step progress mode used by @timeline.
+ *
+ * Return value: %TRUE if the @timeline is using a step progress
+ *   mode, and %FALSE otherwise
+ *
+ * Since: 1.12
+ */
+gboolean
+clutter_timeline_get_step_progress (ClutterTimeline *timeline,
+                                    gint            *n_steps,
+                                    ClutterStepMode *step_mode)
+{
+  g_return_val_if_fail (CLUTTER_IS_TIMELINE (timeline), FALSE);
+
+  if (timeline->priv->progress_mode != CLUTTER_STEPS ||
+      timeline->priv->progress_mode != CLUTTER_STEP_START ||
+      timeline->priv->progress_mode != CLUTTER_STEP_END)
+    return FALSE;
+
+  if (n_steps != NULL)
+    *n_steps = timeline->priv->n_steps;
+
+  if (step_mode != NULL)
+    *step_mode = timeline->priv->step_mode;
+
+  return TRUE;
+}
diff --git a/clutter/clutter-timeline.h b/clutter/clutter-timeline.h
index 4457553..e153613 100644
--- a/clutter/clutter-timeline.h
+++ b/clutter/clutter-timeline.h
@@ -170,6 +170,14 @@ void                            clutter_timeline_set_progress_mode      (Clutter
                                                                          ClutterAnimationMode      mode);
 CLUTTER_AVAILABLE_IN_1_10
 ClutterAnimationMode            clutter_timeline_get_progress_mode      (ClutterTimeline          *timeline);
+CLUTTER_AVAILABLE_IN_1_12
+void                            clutter_timeline_set_step_progress      (ClutterTimeline          *timeline,
+                                                                         gint                      n_steps,
+                                                                         ClutterStepMode           step_mode);
+CLUTTER_AVAILABLE_IN_1_12
+gboolean                        clutter_timeline_get_step_progress      (ClutterTimeline          *timeline,
+                                                                         gint                     *n_steps,
+                                                                         ClutterStepMode          *step_mode);
 
 CLUTTER_AVAILABLE_IN_1_10
 gint64                          clutter_timeline_get_duration_hint      (ClutterTimeline          *timeline);
diff --git a/clutter/clutter.symbols b/clutter/clutter.symbols
index 22a4b8d..66b791c 100644
--- a/clutter/clutter.symbols
+++ b/clutter/clutter.symbols
@@ -1283,6 +1283,7 @@ clutter_state_set_key
 clutter_state_set_state
 clutter_state_warp_to_state
 clutter_static_color_get_type
+clutter_step_mode_get_type
 clutter_swipe_action_get_type
 clutter_swipe_action_new
 clutter_swipe_direction_get_type
@@ -1463,6 +1464,7 @@ clutter_timeline_get_loop
 clutter_timeline_get_progress_mode
 clutter_timeline_get_progress
 clutter_timeline_get_repeat_count
+clutter_timeline_get_step_progress
 clutter_timeline_get_type
 clutter_timeline_has_marker
 clutter_timeline_is_playing
@@ -1478,6 +1480,7 @@ clutter_timeline_set_loop
 clutter_timeline_set_progress_func
 clutter_timeline_set_progress_mode
 clutter_timeline_set_repeat_count
+clutter_timeline_set_step_progress
 clutter_timeline_skip
 clutter_timeline_start
 clutter_timeline_stop
diff --git a/tests/conform/Makefile.am b/tests/conform/Makefile.am
index 51482a4..e06620e 100644
--- a/tests/conform/Makefile.am
+++ b/tests/conform/Makefile.am
@@ -22,6 +22,7 @@ units_sources += \
 	state.c				\
 	timeline.c			\
 	timeline-interpolate.c 		\
+	timeline-progress.c		\
 	timeline-rewind.c 		\
 	$(NULL)
 
diff --git a/tests/conform/test-conform-main.c b/tests/conform/test-conform-main.c
index ef3408c..f57a589 100644
--- a/tests/conform/test-conform-main.c
+++ b/tests/conform/test-conform-main.c
@@ -231,6 +231,8 @@ main (int argc, char **argv)
   TEST_CONFORM_SIMPLE ("/timeline", timeline_markers_from_script);
   TEST_CONFORM_SKIP (g_test_slow (), "/timeline", timeline_interpolation);
   TEST_CONFORM_SKIP (g_test_slow (), "/timeline", timeline_rewind);
+  TEST_CONFORM_SIMPLE ("/timeline", timeline_progress_mode);
+  TEST_CONFORM_SIMPLE ("/timeline", timeline_progress_step);
 
   TEST_CONFORM_SIMPLE ("/score", score_base);
 
diff --git a/tests/conform/timeline-progress.c b/tests/conform/timeline-progress.c
new file mode 100644
index 0000000..9c966ca
--- /dev/null
+++ b/tests/conform/timeline-progress.c
@@ -0,0 +1,110 @@
+#include <glib.h>
+#include <clutter/clutter.h>
+#include "test-conform-common.h"
+
+void
+timeline_progress_step (TestConformSimpleFixture *fixture G_GNUC_UNUSED,
+                        gconstpointer dummy G_GNUC_UNUSED)
+{
+  ClutterTimeline *timeline;
+
+  timeline = clutter_timeline_new (1000);
+
+  if (g_test_verbose ())
+    g_print ("mode: step(3, end)\n");
+
+  clutter_timeline_rewind (timeline);
+  clutter_timeline_set_step_progress (timeline, 3, CLUTTER_STEP_MODE_END);
+  g_assert_cmpint (clutter_timeline_get_progress (timeline), ==, 0);
+
+  clutter_timeline_advance (timeline, 1000 / 3 - 1);
+  g_assert_cmpint (clutter_timeline_get_progress (timeline) * 1000, ==, 0);
+
+  clutter_timeline_advance (timeline, 1000 / 3 + 1);
+  g_assert_cmpint (clutter_timeline_get_progress (timeline) * 1000, ==, 333);
+
+  clutter_timeline_advance (timeline, 1000 / 3 * 2 - 1);
+  g_assert_cmpint (clutter_timeline_get_progress (timeline) * 1000, ==, 333);
+
+  clutter_timeline_advance (timeline, 1000 / 3 * 2 + 1);
+  g_assert_cmpint (clutter_timeline_get_progress (timeline) * 1000, ==, 666);
+
+  clutter_timeline_rewind (timeline);
+  clutter_timeline_set_progress_mode (timeline, CLUTTER_STEP_START);
+  g_assert_cmpfloat (clutter_timeline_get_progress (timeline), ==, 0.0);
+
+  clutter_timeline_advance (timeline, 1);
+  g_assert_cmpfloat (clutter_timeline_get_progress (timeline), ==, 1.0);
+
+  clutter_timeline_advance (timeline, 500);
+  g_assert_cmpfloat (clutter_timeline_get_progress (timeline), ==, 1.0);
+
+  clutter_timeline_advance (timeline, 999);
+  g_assert_cmpfloat (clutter_timeline_get_progress (timeline), ==, 1.0);
+
+  clutter_timeline_advance (timeline, 1000);
+  g_assert_cmpfloat (clutter_timeline_get_progress (timeline), ==, 1.0);
+
+  if (g_test_verbose ())
+    g_print ("mode: step-start\n");
+
+  clutter_timeline_rewind (timeline);
+  clutter_timeline_set_progress_mode (timeline, CLUTTER_STEP_START);
+  g_assert_cmpfloat (clutter_timeline_get_progress (timeline), ==, 0.0);
+
+  clutter_timeline_advance (timeline, 1);
+  g_assert_cmpfloat (clutter_timeline_get_progress (timeline), ==, 1.0);
+
+  clutter_timeline_advance (timeline, 500);
+  g_assert_cmpfloat (clutter_timeline_get_progress (timeline), ==, 1.0);
+
+  clutter_timeline_advance (timeline, 999);
+  g_assert_cmpfloat (clutter_timeline_get_progress (timeline), ==, 1.0);
+
+  clutter_timeline_advance (timeline, 1000);
+  g_assert_cmpfloat (clutter_timeline_get_progress (timeline), ==, 1.0);
+
+  if (g_test_verbose ())
+    g_print ("mode: step-end\n");
+
+  clutter_timeline_rewind (timeline);
+  clutter_timeline_set_progress_mode (timeline, CLUTTER_STEP_END);
+  g_assert_cmpfloat (clutter_timeline_get_progress (timeline), ==, 0.0);
+
+  clutter_timeline_advance (timeline, 1);
+  g_assert_cmpfloat (clutter_timeline_get_progress (timeline), ==, 0.0);
+
+  clutter_timeline_advance (timeline, 500);
+  g_assert_cmpfloat (clutter_timeline_get_progress (timeline), ==, 0.0);
+
+  clutter_timeline_advance (timeline, 999);
+  g_assert_cmpfloat (clutter_timeline_get_progress (timeline), ==, 0.0);
+
+  clutter_timeline_advance (timeline, 1000);
+  g_assert_cmpfloat (clutter_timeline_get_progress (timeline), ==, 1.0);
+
+  g_object_unref (timeline);
+}
+
+void
+timeline_progress_mode (TestConformSimpleFixture *fixture G_GNUC_UNUSED,
+                        gconstpointer dummy G_GNUC_UNUSED)
+{
+  ClutterTimeline *timeline;
+
+  timeline = clutter_timeline_new (1000);
+
+  g_assert (clutter_timeline_get_progress_mode (timeline) == CLUTTER_LINEAR);
+  g_assert_cmpfloat (clutter_timeline_get_progress (timeline), ==, 0.0);
+
+  clutter_timeline_advance (timeline, 500);
+  g_assert_cmpfloat (clutter_timeline_get_progress (timeline), ==, 0.5);
+
+  clutter_timeline_advance (timeline, 1000);
+  g_assert_cmpfloat (clutter_timeline_get_progress (timeline), ==, 1.0);
+
+  clutter_timeline_rewind (timeline);
+  g_assert_cmpfloat (clutter_timeline_get_progress (timeline), ==, 0.0);
+
+  g_object_unref (timeline);
+}



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