mutter/clutter/clutter-alpha.c
Emmanuele Bassi d6ccecd79e [alpha] Sanitize easing equations
Bug 1561 - Bad code in clutter-alpha.c

The implementation of the easing modes equations followed closely
the JavaScript and ActionScript counterparts. Obviously, JS and AS
are not C-compatible, so later versions of gcc (4.4.0 for instance)
would complain about uninitialized variables and such. The code is
also obfuscated and hard to debug/understand.

For these reasons, the implementation should be unobfuscated and
sanitized.
2009-04-23 11:35:10 +01:00

1265 lines
33 KiB
C

/*
* Clutter.
*
* An OpenGL based 'interactive canvas' library.
*
* Authored By Matthew Allum <mallum@openedhand.com>
* Jorn Baayen <jorn@openedhand.com>
* Emmanuele Bassi <ebassi@openedhand.com>
* Tomas Frydrych <tf@openedhand.com>
*
* Copyright (C) 2006, 2007, 2008 OpenedHand
* Copyright (C) 2009 Intel Corp.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
/**
* SECTION:clutter-alpha
* @short_description: A class for calculating an alpha value as a function
* of time.
*
* #ClutterAlpha is a class for calculating an floating point value
* dependent only on the position of a #ClutterTimeline.
*
* A #ClutterAlpha binds a #ClutterTimeline to a progress function which
* translates the time T into an adimensional factor alpha. The factor can
* then be used to drive a #ClutterBehaviour, which will translate the
* alpha value into something meaningful for a #ClutterActor.
*
* You should provide a #ClutterTimeline and bind it to the #ClutterAlpha
* instance using clutter_alpha_set_timeline(). You should also set an
* "animation mode", either by using the #ClutterAnimationMode values that
* Clutter itself provides or by registering custom functions using
* clutter_alpha_register_func().
*
* Instead of a #ClutterAnimationMode you may provide a function returning
* the alpha value depending on the progress of the timeline, using
* clutter_alpha_set_func() or clutter_alpha_set_closure(). The alpha
* function will be executed each time a new frame in the #ClutterTimeline
* is reached.
*
* Since the alpha function is controlled by the timeline instance, you can
* pause, stop or resume the #ClutterAlpha from calling the alpha function by
* using the appropriate functions of the #ClutterTimeline object.
*
* #ClutterAlpha is used to "drive" a #ClutterBehaviour instance, and it
* is internally used by the #ClutterAnimation API.
*
* <figure id="easing-modes">
* <title>Easing modes provided by Clutter</title>
* <graphic fileref="easing-modes.png" format="PNG"/>
* </figure>
*
* Since: 0.2
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <math.h>
#include "clutter-alpha.h"
#include "clutter-debug.h"
#include "clutter-enum-types.h"
#include "clutter-main.h"
#include "clutter-marshal.h"
#include "clutter-private.h"
G_DEFINE_TYPE (ClutterAlpha, clutter_alpha, G_TYPE_INITIALLY_UNOWNED);
struct _ClutterAlphaPrivate
{
ClutterTimeline *timeline;
guint timeline_new_frame_id;
gdouble alpha;
GClosure *closure;
gulong mode;
};
enum
{
PROP_0,
PROP_TIMELINE,
PROP_ALPHA,
PROP_MODE
};
static void
timeline_new_frame_cb (ClutterTimeline *timeline,
guint current_frame_num,
ClutterAlpha *alpha)
{
ClutterAlphaPrivate *priv = alpha->priv;
/* Update alpha value and notify */
priv->alpha = clutter_alpha_get_alpha (alpha);
g_object_notify (G_OBJECT (alpha), "alpha");
}
static void
clutter_alpha_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
ClutterAlpha *alpha;
ClutterAlphaPrivate *priv;
alpha = CLUTTER_ALPHA (object);
priv = alpha->priv;
switch (prop_id)
{
case PROP_TIMELINE:
clutter_alpha_set_timeline (alpha, g_value_get_object (value));
break;
case PROP_MODE:
clutter_alpha_set_mode (alpha, g_value_get_ulong (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
clutter_alpha_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
ClutterAlpha *alpha;
ClutterAlphaPrivate *priv;
alpha = CLUTTER_ALPHA (object);
priv = alpha->priv;
switch (prop_id)
{
case PROP_TIMELINE:
g_value_set_object (value, priv->timeline);
break;
case PROP_ALPHA:
g_value_set_double (value, priv->alpha);
break;
case PROP_MODE:
g_value_set_ulong (value, priv->mode);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
clutter_alpha_finalize (GObject *object)
{
ClutterAlphaPrivate *priv = CLUTTER_ALPHA (object)->priv;
if (priv->closure)
g_closure_unref (priv->closure);
G_OBJECT_CLASS (clutter_alpha_parent_class)->finalize (object);
}
static void
clutter_alpha_dispose (GObject *object)
{
ClutterAlpha *self = CLUTTER_ALPHA(object);
clutter_alpha_set_timeline (self, NULL);
G_OBJECT_CLASS (clutter_alpha_parent_class)->dispose (object);
}
static void
clutter_alpha_class_init (ClutterAlphaClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
GParamSpec *pspec;
object_class->set_property = clutter_alpha_set_property;
object_class->get_property = clutter_alpha_get_property;
object_class->finalize = clutter_alpha_finalize;
object_class->dispose = clutter_alpha_dispose;
g_type_class_add_private (klass, sizeof (ClutterAlphaPrivate));
/**
* ClutterAlpha:timeline:
*
* A #ClutterTimeline instance used to drive the alpha function.
*
* Since: 0.2
*/
pspec = g_param_spec_object ("timeline",
"Timeline",
"Timeline used by the alpha",
CLUTTER_TYPE_TIMELINE,
CLUTTER_PARAM_READWRITE);
g_object_class_install_property (object_class, PROP_TIMELINE, pspec);
/**
* ClutterAlpha:alpha:
*
* The alpha value as computed by the alpha function. The linear
* interval is 0.0 to 1.0, but the Alpha allows overshooting by
* one unit in each direction, so the valid interval is -1.0 to 2.0.
*
* Since: 0.2
*/
pspec = g_param_spec_double ("alpha",
"Alpha value",
"Alpha value",
-1.0, 2.0,
0.0,
CLUTTER_PARAM_READABLE);
g_object_class_install_property (object_class, PROP_ALPHA, pspec);
/**
* ClutterAlpha:mode:
*
* The progress function logical id - either a value from the
* #ClutterAnimationMode enumeration or a value returned by
* clutter_alpha_register_func().
*
* If %CLUTTER_CUSTOM_MODE is used then the function set using
* clutter_alpha_set_closure() or clutter_alpha_set_func()
* will be used.
*
* Since: 1.0
*/
pspec = g_param_spec_ulong ("mode",
"Mode",
"Progress mode",
0, G_MAXULONG,
CLUTTER_CUSTOM_MODE,
G_PARAM_CONSTRUCT | CLUTTER_PARAM_READWRITE);
g_object_class_install_property (object_class, PROP_MODE, pspec);
}
static void
clutter_alpha_init (ClutterAlpha *self)
{
self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
CLUTTER_TYPE_ALPHA,
ClutterAlphaPrivate);
self->priv->mode = CLUTTER_CUSTOM_MODE;
self->priv->alpha = 0.0;
}
/**
* clutter_alpha_get_alpha:
* @alpha: A #ClutterAlpha
*
* Query the current alpha value.
*
* Return Value: The current alpha value for the alpha
*
* Since: 0.2
*/
gdouble
clutter_alpha_get_alpha (ClutterAlpha *alpha)
{
ClutterAlphaPrivate *priv;
gdouble retval = 0;
g_return_val_if_fail (CLUTTER_IS_ALPHA (alpha), 0);
priv = alpha->priv;
if (G_LIKELY (priv->closure))
{
GValue params = { 0, };
GValue result_value = { 0, };
g_object_ref (alpha);
g_value_init (&result_value, G_TYPE_DOUBLE);
g_value_init (&params, CLUTTER_TYPE_ALPHA);
g_value_set_object (&params, alpha);
g_closure_invoke (priv->closure, &result_value, 1, &params, NULL);
retval = g_value_get_double (&result_value);
g_value_unset (&result_value);
g_value_unset (&params);
g_object_unref (alpha);
}
return retval;
}
/*
* clutter_alpha_set_closure_internal:
* @alpha: a #ClutterAlpha
* @closure: a #GClosure
*
* Sets the @closure for @alpha. This function does not
* set the #ClutterAlpha:mode property and does not emit
* the #GObject::notify signal for it.
*/
static inline void
clutter_alpha_set_closure_internal (ClutterAlpha *alpha,
GClosure *closure)
{
ClutterAlphaPrivate *priv = alpha->priv;
if (priv->closure)
g_closure_unref (priv->closure);
/* need to take ownership of the closure before sinking it */
priv->closure = g_closure_ref (closure);
g_closure_sink (closure);
/* set the marshaller */
if (G_CLOSURE_NEEDS_MARSHAL (closure))
{
GClosureMarshal marshal = clutter_marshal_DOUBLE__VOID;
g_closure_set_marshal (closure, marshal);
}
}
/**
* clutter_alpha_set_closure:
* @alpha: A #ClutterAlpha
* @closure: A #GClosure
*
* Sets the #GClosure used to compute the alpha value at each
* frame of the #ClutterTimeline bound to @alpha.
*
* Since: 0.8
*/
void
clutter_alpha_set_closure (ClutterAlpha *alpha,
GClosure *closure)
{
ClutterAlphaPrivate *priv;
g_return_if_fail (CLUTTER_IS_ALPHA (alpha));
g_return_if_fail (closure != NULL);
priv = alpha->priv;
clutter_alpha_set_closure_internal (alpha, closure);
priv->mode = CLUTTER_CUSTOM_MODE;
g_object_notify (G_OBJECT (alpha), "mode");
}
/**
* clutter_alpha_set_func:
* @alpha: A #ClutterAlpha
* @func: A #ClutterAlphaFunc
* @data: user data to be passed to the alpha function, or %NULL
* @destroy: notify function used when disposing the alpha function
*
* Sets the #ClutterAlphaFunc function used to compute
* the alpha value at each frame of the #ClutterTimeline
* bound to @alpha.
*
* This function will not register @func as a global alpha function.
*
* Since: 0.2
*/
void
clutter_alpha_set_func (ClutterAlpha *alpha,
ClutterAlphaFunc func,
gpointer data,
GDestroyNotify destroy)
{
ClutterAlphaPrivate *priv;
GClosure *closure;
g_return_if_fail (CLUTTER_IS_ALPHA (alpha));
g_return_if_fail (func != NULL);
priv = alpha->priv;
closure = g_cclosure_new (G_CALLBACK (func), data, (GClosureNotify) destroy);
clutter_alpha_set_closure_internal (alpha, closure);
priv->mode = CLUTTER_CUSTOM_MODE;
g_object_notify (G_OBJECT (alpha), "mode");
}
/**
* clutter_alpha_set_timeline:
* @alpha: A #ClutterAlpha
* @timeline: A #ClutterTimeline
*
* Binds @alpha to @timeline.
*
* Since: 0.2
*/
void
clutter_alpha_set_timeline (ClutterAlpha *alpha,
ClutterTimeline *timeline)
{
ClutterAlphaPrivate *priv;
g_return_if_fail (CLUTTER_IS_ALPHA (alpha));
g_return_if_fail (timeline == NULL || CLUTTER_IS_TIMELINE (timeline));
priv = alpha->priv;
if (priv->timeline == timeline)
return;
if (priv->timeline)
{
g_signal_handlers_disconnect_by_func (priv->timeline,
timeline_new_frame_cb,
alpha);
g_object_unref (priv->timeline);
priv->timeline = NULL;
}
if (timeline)
{
priv->timeline = g_object_ref (timeline);
g_signal_connect (priv->timeline, "new-frame",
G_CALLBACK (timeline_new_frame_cb),
alpha);
}
g_object_notify (G_OBJECT (alpha), "timeline");
}
/**
* clutter_alpha_get_timeline:
* @alpha: A #ClutterAlpha
*
* Gets the #ClutterTimeline bound to @alpha.
*
* Return value: (transfer none): a #ClutterTimeline instance
*
* Since: 0.2
*/
ClutterTimeline *
clutter_alpha_get_timeline (ClutterAlpha *alpha)
{
g_return_val_if_fail (CLUTTER_IS_ALPHA (alpha), NULL);
return alpha->priv->timeline;
}
/**
* clutter_alpha_new:
*
* Creates a new #ClutterAlpha instance. You must set a function
* to compute the alpha value using clutter_alpha_set_func() and
* bind a #ClutterTimeline object to the #ClutterAlpha instance
* using clutter_alpha_set_timeline().
*
* You should use the newly created #ClutterAlpha instance inside
* a #ClutterBehaviour object.
*
* Return value: the newly created empty #ClutterAlpha instance.
*
* Since: 0.2
*/
ClutterAlpha *
clutter_alpha_new (void)
{
return g_object_new (CLUTTER_TYPE_ALPHA, NULL);
}
/**
* clutter_alpha_new_full:
* @timeline: #ClutterTimeline timeline
* @mode: animation mode
*
* Creates a new #ClutterAlpha instance and sets the timeline
* and animation mode.
*
* See also clutter_alpha_set_timeline() and clutter_alpha_set_mode().
*
* Return Value: the newly created #ClutterAlpha
*
* Since: 1.0
*/
ClutterAlpha *
clutter_alpha_new_full (ClutterTimeline *timeline,
gulong mode)
{
g_return_val_if_fail (CLUTTER_IS_TIMELINE (timeline), NULL);
g_return_val_if_fail (mode != CLUTTER_ANIMATION_LAST, NULL);
return g_object_new (CLUTTER_TYPE_ALPHA,
"timeline", timeline,
"mode", mode,
NULL);
}
/**
* clutter_alpha_new_with_func:
* @timeline: a #ClutterTimeline
* @func: a #ClutterAlphaFunc
* @data: data to pass to the function, or %NULL
* @destroy: function to call when removing the alpha function, or %NULL
*
* Creates a new #ClutterAlpha instances and sets the timeline
* and the alpha function.
*
* This function will not register @func as a global alpha function.
*
* See also clutter_alpha_set_timeline() and clutter_alpha_set_func().
*
* Return value: the newly created #ClutterAlpha
*
* Since: 1.0
*/
ClutterAlpha *
clutter_alpha_new_with_func (ClutterTimeline *timeline,
ClutterAlphaFunc func,
gpointer data,
GDestroyNotify destroy)
{
ClutterAlpha *retval;
g_return_val_if_fail (CLUTTER_IS_TIMELINE (timeline), NULL);
g_return_val_if_fail (func != NULL, NULL);
retval = clutter_alpha_new ();
clutter_alpha_set_timeline (retval, timeline);
clutter_alpha_set_func (retval, func, data, destroy);
return retval;
}
/**
* clutter_alpha_get_mode:
* @alpha: a #ClutterAlpha
*
* Retrieves the #ClutterAnimationMode used by @alpha.
*
* Return value: the animation mode
*
* Since: 1.0
*/
gulong
clutter_alpha_get_mode (ClutterAlpha *alpha)
{
g_return_val_if_fail (CLUTTER_IS_ALPHA (alpha), CLUTTER_CUSTOM_MODE);
return alpha->priv->mode;
}
static gdouble
clutter_linear (ClutterAlpha *alpha,
gpointer dummy G_GNUC_UNUSED)
{
ClutterTimeline *timeline = alpha->priv->timeline;
return clutter_timeline_get_progress (timeline);
}
static gdouble
clutter_ease_in_quad (ClutterAlpha *alpha,
gpointer dummy G_GNUC_UNUSED)
{
ClutterTimeline *timeline = alpha->priv->timeline;
gdouble progress = clutter_timeline_get_progress (timeline);
return progress * progress;
}
static gdouble
clutter_ease_out_quad (ClutterAlpha *alpha,
gpointer dummy G_GNUC_UNUSED)
{
ClutterTimeline *timeline = alpha->priv->timeline;
gdouble t = clutter_timeline_get_current_frame (timeline);
gdouble d = clutter_timeline_get_n_frames (timeline);
gdouble p = t / d;
return -1.0 * p * (p - 2);
}
static gdouble
clutter_ease_in_out_quad (ClutterAlpha *alpha,
gpointer dummy G_GNUC_UNUSED)
{
ClutterTimeline *timeline = alpha->priv->timeline;
gdouble t = clutter_timeline_get_current_frame (timeline);
gdouble d = clutter_timeline_get_n_frames (timeline);
gdouble p = t / (d / 2);
if (p < 1)
return 0.5 * p * p;
p -= 1;
return -0.5 * (p * (p - 2) - 1);
}
static gdouble
clutter_ease_in_cubic (ClutterAlpha *alpha,
gpointer dummy G_GNUC_UNUSED)
{
ClutterTimeline *timeline = alpha->priv->timeline;
gdouble t = clutter_timeline_get_current_frame (timeline);
gdouble d = clutter_timeline_get_n_frames (timeline);
gdouble p = t / d;
return p * p * p;
}
static gdouble
clutter_ease_out_cubic (ClutterAlpha *alpha,
gpointer dummy G_GNUC_UNUSED)
{
ClutterTimeline *timeline = alpha->priv->timeline;
gdouble t = clutter_timeline_get_current_frame (timeline);
gdouble d = clutter_timeline_get_n_frames (timeline);
gdouble p = t / d - 1;
return p * p * p + 1;
}
static gdouble
clutter_ease_in_out_cubic (ClutterAlpha *alpha,
gpointer dummy G_GNUC_UNUSED)
{
ClutterTimeline *timeline = alpha->priv->timeline;
gdouble t = clutter_timeline_get_current_frame (timeline);
gdouble d = clutter_timeline_get_n_frames (timeline);
gdouble p = t / (d / 2);
if (p < 1)
return 0.5 * p * p * p;
p -= 2;
return 0.5 * (p * p * p + 2);
}
static gdouble
clutter_ease_in_quart (ClutterAlpha *alpha,
gpointer dummy G_GNUC_UNUSED)
{
ClutterTimeline *timeline = alpha->priv->timeline;
gdouble t = clutter_timeline_get_current_frame (timeline);
gdouble d = clutter_timeline_get_n_frames (timeline);
gdouble p = t / d;
return p * p * p * p;
}
static gdouble
clutter_ease_out_quart (ClutterAlpha *alpha,
gpointer dummy G_GNUC_UNUSED)
{
ClutterTimeline *timeline = alpha->priv->timeline;
gdouble t = clutter_timeline_get_current_frame (timeline);
gdouble d = clutter_timeline_get_n_frames (timeline);
gdouble p = t / d - 1;
return -1.0 * (p * p * p * p - 1);
}
static gdouble
clutter_ease_in_out_quart (ClutterAlpha *alpha,
gpointer dummy G_GNUC_UNUSED)
{
ClutterTimeline *timeline = alpha->priv->timeline;
gdouble t = clutter_timeline_get_current_frame (timeline);
gdouble d = clutter_timeline_get_n_frames (timeline);
gdouble p = t / (d / 2);
if (p < 1)
return 0.5 * p * p * p * p;
p -= 2;
return -0.5 * (p * p * p * p - 2);
}
static gdouble
clutter_ease_in_quint (ClutterAlpha *alpha,
gpointer dummy G_GNUC_UNUSED)
{
ClutterTimeline *timeline = alpha->priv->timeline;
gdouble t = clutter_timeline_get_current_frame (timeline);
gdouble d = clutter_timeline_get_n_frames (timeline);
gdouble p = t / d;
return p * p * p * p * p;
}
static gdouble
clutter_ease_out_quint (ClutterAlpha *alpha,
gpointer dummy G_GNUC_UNUSED)
{
ClutterTimeline *timeline = alpha->priv->timeline;
gdouble t = clutter_timeline_get_current_frame (timeline);
gdouble d = clutter_timeline_get_n_frames (timeline);
gdouble p = t / d - 1;
return p * p * p * p * p + 1;
}
static gdouble
clutter_ease_in_out_quint (ClutterAlpha *alpha,
gpointer dummy G_GNUC_UNUSED)
{
ClutterTimeline *timeline = alpha->priv->timeline;
gdouble t = clutter_timeline_get_current_frame (timeline);
gdouble d = clutter_timeline_get_n_frames (timeline);
gdouble p = t / (d / 2);
if (p < 1)
return 0.5 * p * p * p * p * p;
p -= 2;
return 0.5 * (p * p * p * p * p + 2);
}
static gdouble
clutter_ease_in_sine (ClutterAlpha *alpha,
gpointer dummy G_GNUC_UNUSED)
{
ClutterTimeline *timeline = alpha->priv->timeline;
gdouble t = clutter_timeline_get_current_frame (timeline);
gdouble d = clutter_timeline_get_n_frames (timeline);
return -1.0 * cos (t / d * G_PI_2) + 1.0;
}
static gdouble
clutter_ease_out_sine (ClutterAlpha *alpha,
gpointer dummy G_GNUC_UNUSED)
{
ClutterTimeline *timeline = alpha->priv->timeline;
gdouble t = clutter_timeline_get_current_frame (timeline);
gdouble d = clutter_timeline_get_n_frames (timeline);
return sin (t / d * G_PI_2);
}
static gdouble
clutter_ease_in_out_sine (ClutterAlpha *alpha,
gpointer dummy G_GNUC_UNUSED)
{
ClutterTimeline *timeline = alpha->priv->timeline;
gdouble t = clutter_timeline_get_current_frame (timeline);
gdouble d = clutter_timeline_get_n_frames (timeline);
return -0.5 * (cos (G_PI * t / d) - 1);
}
static gdouble
clutter_ease_in_expo (ClutterAlpha *alpha,
gpointer dummy G_GNUC_UNUSED)
{
ClutterTimeline *timeline = alpha->priv->timeline;
gdouble t = clutter_timeline_get_current_frame (timeline);
gdouble d = clutter_timeline_get_n_frames (timeline);
return (t == 0) ? 0.0 : pow (2, 10 * (t / d - 1));
}
static gdouble
clutter_ease_out_expo (ClutterAlpha *alpha,
gpointer dummy G_GNUC_UNUSED)
{
ClutterTimeline *timeline = alpha->priv->timeline;
gdouble t = clutter_timeline_get_current_frame (timeline);
gdouble d = clutter_timeline_get_n_frames (timeline);
return (t == d) ? 1.0 : -pow (2, -10 * t / d) + 1;
}
static gdouble
clutter_ease_in_out_expo (ClutterAlpha *alpha,
gpointer dummy G_GNUC_UNUSED)
{
ClutterTimeline *timeline = alpha->priv->timeline;
gdouble t = clutter_timeline_get_current_frame (timeline);
gdouble d = clutter_timeline_get_n_frames (timeline);
gdouble p;
if (t == 0)
return 0.0;
if (t == d)
return 1.0;
p = t / (d / 2);
if (p < 1)
return 0.5 * pow (2, 10 * (p - 1));
p -= 1;
return 0.5 * (-pow (2, -10 * p) + 2);
}
static gdouble
clutter_ease_in_circ (ClutterAlpha *alpha,
gpointer dummy G_GNUC_UNUSED)
{
ClutterTimeline *timeline = alpha->priv->timeline;
gdouble t = clutter_timeline_get_current_frame (timeline);
gdouble d = clutter_timeline_get_n_frames (timeline);
gdouble p = t / d;
return -1.0 * (sqrt (1 - p * p) - 1);
}
static gdouble
clutter_ease_out_circ (ClutterAlpha *alpha,
gpointer dummy G_GNUC_UNUSED)
{
ClutterTimeline *timeline = alpha->priv->timeline;
gdouble t = clutter_timeline_get_current_frame (timeline);
gdouble d = clutter_timeline_get_n_frames (timeline);
gdouble p = t / d - 1;
return sqrt (1 - p * p);
}
static gdouble
clutter_ease_in_out_circ (ClutterAlpha *alpha,
gpointer dummy G_GNUC_UNUSED)
{
ClutterTimeline *timeline = alpha->priv->timeline;
gdouble t = clutter_timeline_get_current_frame (timeline);
gdouble d = clutter_timeline_get_n_frames (timeline);
gdouble p = t / (d / 2);
if (p < 1)
return -0.5 * (sqrt (1 - p * p) - 1);
p -= 2;
return 0.5 * (sqrt (1 - p * p) + 1);
}
static gdouble
clutter_ease_in_elastic (ClutterAlpha *alpha,
gpointer dummy G_GNUC_UNUSED)
{
ClutterTimeline *timeline = alpha->priv->timeline;
gdouble t = clutter_timeline_get_current_frame (timeline);
gdouble d = clutter_timeline_get_n_frames (timeline);
gdouble p = d * .3;
gdouble s = p / 4;
gdouble q = t / d;
if (q == 1)
return 1.0;
q -= 1;
return -(pow (2, 10 * q) * sin ((q * d - s) * (2 * G_PI) / p));
}
static gdouble
clutter_ease_out_elastic (ClutterAlpha *alpha,
gpointer dummy G_GNUC_UNUSED)
{
ClutterTimeline *timeline = alpha->priv->timeline;
gdouble t = clutter_timeline_get_current_frame (timeline);
gdouble d = clutter_timeline_get_n_frames (timeline);
gdouble p = d * .3;
gdouble s = p / 4;
gdouble q = t / d;
if (q == 1)
return 1.0;
return pow (2, -10 * q) * sin ((q * d - s) * (2 * G_PI) / p) + 1.0;
}
static gdouble
clutter_ease_in_out_elastic (ClutterAlpha *alpha,
gpointer dummy G_GNUC_UNUSED)
{
ClutterTimeline *timeline = alpha->priv->timeline;
gdouble t = clutter_timeline_get_current_frame (timeline);
gdouble d = clutter_timeline_get_n_frames (timeline);
gdouble p = d * (.3 * 1.5);
gdouble s = p / 4;
gdouble q = t / (d / 2);
if (q == 2)
return 1.0;
if (q < 1)
{
q -= 1;
return -.5 * (pow (2, 10 * q) * sin ((q * d - s) * (2 * G_PI) / p));
}
else
{
q -= 1;
return pow (2, -10 * q)
* sin ((q * d - s) * (2 * G_PI) / p)
* .5 + 1.0;
}
}
static gdouble
clutter_ease_in_back (ClutterAlpha *alpha,
gpointer dummy G_GNUC_UNUSED)
{
ClutterTimeline *timeline = alpha->priv->timeline;
gdouble t = clutter_timeline_get_current_frame (timeline);
gdouble d = clutter_timeline_get_n_frames (timeline);
gdouble p = t / d;
return p * p * ((1.70158 + 1) * p - 1.70158);
}
static gdouble
clutter_ease_out_back (ClutterAlpha *alpha,
gpointer dummy G_GNUC_UNUSED)
{
ClutterTimeline *timeline = alpha->priv->timeline;
gdouble t = clutter_timeline_get_current_frame (timeline);
gdouble d = clutter_timeline_get_n_frames (timeline);
gdouble p = t / d - 1;
return p * p * ((1.70158 + 1) * p + 1.70158) + 1;
}
static gdouble
clutter_ease_in_out_back (ClutterAlpha *alpha,
gpointer dummy G_GNUC_UNUSED)
{
ClutterTimeline *timeline = alpha->priv->timeline;
gdouble t = clutter_timeline_get_current_frame (timeline);
gdouble d = clutter_timeline_get_n_frames (timeline);
gdouble p = t / (d / 2);
gdouble s = 1.70158 * 1.525;
if (p < 1)
return 0.5 * (p * p * ((s + 1) * p - s));
p -= 2;
return 0.5 * (p * p * ((s + 1) * p + s) + 2);
}
static gdouble
ease_out_bounce_internal (gdouble t,
gdouble d)
{
gdouble p = t / d;
if (p < (1 / 2.75))
return 7.5625 * p * p;
else if (p < (2 / 2.75))
{
p -= (1.5 / 2.75);
return 7.5625 * p * p + .75;
}
else if (p < (2.5 / 2.75))
{
p -= (2.25 / 2.75);
return 7.5625 * p * p + .9375;
}
else
{
p -= (2.625 / 2.75);
return 7.5625 * p * p + .984375;
}
}
static gdouble
ease_in_bounce_internal (gdouble t,
gdouble d)
{
return 1.0 - ease_out_bounce_internal (d - t, d);
}
static gdouble
clutter_ease_in_bounce (ClutterAlpha *alpha,
gpointer dummy G_GNUC_UNUSED)
{
ClutterTimeline *timeline = alpha->priv->timeline;
gdouble t = clutter_timeline_get_current_frame (timeline);
gdouble d = clutter_timeline_get_n_frames (timeline);
return ease_in_bounce_internal (t, d);
}
static gdouble
clutter_ease_out_bounce (ClutterAlpha *alpha,
gpointer dummy G_GNUC_UNUSED)
{
ClutterTimeline *timeline = alpha->priv->timeline;
gdouble t = clutter_timeline_get_current_frame (timeline);
gdouble d = clutter_timeline_get_n_frames (timeline);
return ease_out_bounce_internal (t, d);
}
static gdouble
clutter_ease_in_out_bounce (ClutterAlpha *alpha,
gpointer dummy G_GNUC_UNUSED)
{
ClutterTimeline *timeline = alpha->priv->timeline;
gdouble t = clutter_timeline_get_current_frame (timeline);
gdouble d = clutter_timeline_get_n_frames (timeline);
if (t < d / 2)
return ease_in_bounce_internal (t * 2, d) * 0.5;
else
return ease_out_bounce_internal (t * 2 - d, d) * 0.5 + 1.0 * 0.5;
}
/* static enum/function mapping table for the animation modes
* we provide internally
*
* XXX - keep in sync with ClutterAnimationMode
*/
static const struct {
gulong mode;
ClutterAlphaFunc func;
} animation_modes[] = {
{ CLUTTER_CUSTOM_MODE, NULL },
{ CLUTTER_LINEAR, clutter_linear },
{ CLUTTER_EASE_IN_QUAD, clutter_ease_in_quad },
{ CLUTTER_EASE_OUT_QUAD, clutter_ease_out_quad },
{ CLUTTER_EASE_IN_OUT_QUAD, clutter_ease_in_out_quad },
{ CLUTTER_EASE_IN_CUBIC, clutter_ease_in_cubic },
{ CLUTTER_EASE_OUT_CUBIC, clutter_ease_out_cubic },
{ CLUTTER_EASE_IN_OUT_CUBIC, clutter_ease_in_out_cubic },
{ CLUTTER_EASE_IN_QUART, clutter_ease_in_quart },
{ CLUTTER_EASE_OUT_QUART, clutter_ease_out_quart },
{ CLUTTER_EASE_IN_OUT_QUART, clutter_ease_in_out_quart },
{ CLUTTER_EASE_IN_QUINT, clutter_ease_in_quint },
{ CLUTTER_EASE_OUT_QUINT, clutter_ease_out_quint },
{ CLUTTER_EASE_IN_OUT_QUINT, clutter_ease_in_out_quint },
{ CLUTTER_EASE_IN_SINE, clutter_ease_in_sine },
{ CLUTTER_EASE_OUT_SINE, clutter_ease_out_sine },
{ CLUTTER_EASE_IN_OUT_SINE, clutter_ease_in_out_sine },
{ CLUTTER_EASE_IN_EXPO, clutter_ease_in_expo },
{ CLUTTER_EASE_OUT_EXPO, clutter_ease_out_expo },
{ CLUTTER_EASE_IN_OUT_EXPO, clutter_ease_in_out_expo },
{ CLUTTER_EASE_IN_CIRC, clutter_ease_in_circ },
{ CLUTTER_EASE_OUT_CIRC, clutter_ease_out_circ },
{ CLUTTER_EASE_IN_OUT_CIRC, clutter_ease_in_out_circ },
{ CLUTTER_EASE_IN_ELASTIC, clutter_ease_in_elastic },
{ CLUTTER_EASE_OUT_ELASTIC, clutter_ease_out_elastic },
{ CLUTTER_EASE_IN_OUT_ELASTIC, clutter_ease_in_out_elastic },
{ CLUTTER_EASE_IN_BACK, clutter_ease_in_back },
{ CLUTTER_EASE_OUT_BACK, clutter_ease_out_back },
{ CLUTTER_EASE_IN_OUT_BACK, clutter_ease_in_out_back },
{ CLUTTER_EASE_IN_BOUNCE, clutter_ease_in_bounce },
{ CLUTTER_EASE_OUT_BOUNCE, clutter_ease_out_bounce },
{ CLUTTER_EASE_IN_OUT_BOUNCE, clutter_ease_in_out_bounce },
{ CLUTTER_ANIMATION_LAST, NULL },
};
typedef struct _AlphaData {
guint closure_set : 1;
ClutterAlphaFunc func;
gpointer data;
GClosure *closure;
} AlphaData;
static GPtrArray *clutter_alphas = NULL;
/**
* clutter_alpha_set_mode:
* @alpha: a #ClutterAlpha
* @mode: a #ClutterAnimationMode
*
* Sets the progress function of @alpha using the symbolic value
* of @mode, as taken by the #ClutterAnimationMode enumeration or
* using the value returned by clutter_alpha_register_func().
*
* Since: 1.0
*/
void
clutter_alpha_set_mode (ClutterAlpha *alpha,
gulong mode)
{
ClutterAlphaPrivate *priv;
g_return_if_fail (CLUTTER_IS_ALPHA (alpha));
g_return_if_fail (mode != CLUTTER_ANIMATION_LAST);
priv = alpha->priv;
if (mode == CLUTTER_CUSTOM_MODE)
{
priv->mode = mode;
}
else if (mode < CLUTTER_ANIMATION_LAST)
{
GClosure *closure;
/* sanity check to avoid getting an out of sync
* enum/function mapping
*/
g_assert (animation_modes[mode].mode == mode);
g_assert (animation_modes[mode].func != NULL);
closure = g_cclosure_new (G_CALLBACK (animation_modes[mode].func),
NULL,
NULL);
clutter_alpha_set_closure_internal (alpha, closure);
priv->mode = mode;
}
else if (mode > CLUTTER_ANIMATION_LAST)
{
AlphaData *alpha_data = NULL;
gulong real_index = 0;
if (G_UNLIKELY (clutter_alphas == NULL))
{
g_warning ("No alpha functions defined for ClutterAlpha to use. "
"Use clutter_alpha_register_func() to register an "
"alpha function.");
return;
}
real_index = mode - CLUTTER_ANIMATION_LAST - 1;
alpha_data = g_ptr_array_index (clutter_alphas, real_index);
if (G_UNLIKELY (alpha_data == NULL))
{
g_warning ("No alpha function registered for mode %lu.",
mode);
return;
}
if (alpha_data->closure_set)
clutter_alpha_set_closure (alpha, alpha_data->closure);
else
{
GClosure *closure;
closure = g_cclosure_new (G_CALLBACK (alpha_data->func),
alpha_data->data,
NULL);
clutter_alpha_set_closure_internal (alpha, closure);
}
priv->mode = mode;
}
else
g_assert_not_reached ();
g_object_notify (G_OBJECT (alpha), "mode");
}
static gulong
register_alpha_internal (AlphaData *alpha_data)
{
if (G_UNLIKELY (clutter_alphas == NULL))
clutter_alphas = g_ptr_array_new ();
g_ptr_array_add (clutter_alphas, alpha_data);
return clutter_alphas->len + CLUTTER_ANIMATION_LAST;
}
/**
* clutter_alpha_register_func:
* @func: a #ClutterAlphaFunc
* @data: user data to pass to @func, or %NULL
*
* Registers a global alpha function and returns its logical id
* to be used by clutter_alpha_set_mode() or by #ClutterAnimation.
*
* The logical id is always greater than %CLUTTER_ANIMATION_LAST.
*
* Return value: the logical id of the alpha function
*
* Since: 1.0
*/
gulong
clutter_alpha_register_func (ClutterAlphaFunc func,
gpointer data)
{
AlphaData *alpha_data;
g_return_val_if_fail (func != NULL, 0);
alpha_data = g_slice_new (AlphaData);
alpha_data->closure_set = FALSE;
alpha_data->func = func;
alpha_data->data = data;
return register_alpha_internal (alpha_data);
}
/**
* clutter_alpha_register_closure:
* @closure: a #GClosure
*
* #GClosure variant of clutter_alpha_register_func().
*
* Registers a global alpha function and returns its logical id
* to be used by clutter_alpha_set_mode() or by #ClutterAnimation.
*
* The logical id is always greater than %CLUTTER_ANIMATION_LAST.
*
* Return value: the logical id of the alpha function
*
* Since: 1.0
*/
gulong
clutter_alpha_register_closure (GClosure *closure)
{
AlphaData *alpha_data;
g_return_val_if_fail (closure != NULL, 0);
alpha_data = g_slice_new (AlphaData);
alpha_data->closure_set = TRUE;
alpha_data->closure = closure;
return register_alpha_internal (alpha_data);
}