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.
This commit is contained in:
Emmanuele Bassi 2012-07-19 19:47:48 -04:00
parent d7eb57a711
commit 4546f84408
9 changed files with 287 additions and 0 deletions

View File

@ -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" },
};

View File

@ -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__ */

View File

@ -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__ */

View File

@ -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;
}

View File

@ -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);

View File

@ -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

View File

@ -22,6 +22,7 @@ units_sources += \
state.c \
timeline.c \
timeline-interpolate.c \
timeline-progress.c \
timeline-rewind.c \
$(NULL)

View File

@ -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);

View File

@ -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);
}