efl/src/lib/evas/canvas/efl_canvas_object_animation.c

215 lines
6.8 KiB
C

#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include "evas_common_private.h"
#include "evas_private.h"
#include "efl_canvas_object_animation.eo.h"
#include <Ecore.h>
#define MY_CLASS EFL_CANVAS_OBJECT_ANIMATION_MIXIN
typedef struct
{
Efl_Canvas_Animation *animation;
double speed;
double progress;
double run_start_time;
double start_pos;
int remaining_repeats;
Efl_Loop_Timer *timer;
Eina_Bool pause_state : 1;
} Efl_Canvas_Object_Animation_Indirect_Data;
typedef struct
{
Efl_Canvas_Object_Animation_Indirect_Data *in;
} Efl_Canvas_Object_Animation_Data;
static void _end(Efl_Canvas_Object_Animation *obj, Efl_Canvas_Object_Animation_Data *pd);
static void
_animator_cb(void *data, const Efl_Event *ev EINA_UNUSED)
{
Eo *obj = data;
Efl_Canvas_Object_Animation_Data *pd = efl_data_scope_get(obj, MY_CLASS);
double duration, elapsed_time, vector, current;
EINA_SAFETY_ON_NULL_RETURN(pd->in);
current = ecore_loop_time_get();
EINA_SAFETY_ON_FALSE_RETURN(pd->in->run_start_time <= current);
duration = efl_animation_duration_get(pd->in->animation) / pd->in->speed;
elapsed_time = current - pd->in->run_start_time;
if (EINA_DBL_EQ(duration, 0))
{
if (pd->in->speed < 0.0)
vector = -1.0;
else
vector = 1.0;
}
else
vector = elapsed_time / duration;
/* When animation player starts, _animator_cb() is called immediately so
* both elapsed time and progress are 0.0.
* Since it is the beginning of the animation if progress is 0.0, the
* following codes for animation should be executed. */
if (pd->in->speed < 0.0)
vector += 1.0;
pd->in->progress = CLAMP(0.0, vector, 1.0);
/* The previously applied map effect should be reset before applying the
* current map effect. Otherwise, the incrementally added map effects
* increase numerical error. */
efl_gfx_mapping_reset(obj);
efl_animation_apply(pd->in->animation, pd->in->progress, obj);
double progress = pd->in->progress;
efl_event_callback_call(obj, EFL_CANVAS_OBJECT_ANIMATION_EVENT_ANIMATION_PROGRESS_UPDATED, &progress);
//Check if animation stopped in animation_progress,updated callback.
if (!pd->in) return;
//Not end. Keep going.
if ((pd->in->speed < 0 && EINA_DBL_EQ(pd->in->progress, 0)) ||
(pd->in->speed > 0 && EINA_DBL_EQ(pd->in->progress, 1.0)))
{
//Repeat animation
if ((efl_animation_play_count_get(pd->in->animation) == 0) ||
(pd->in->remaining_repeats > 0))
{
pd->in->remaining_repeats--;
if (efl_animation_repeat_mode_get(pd->in->animation) == EFL_CANVAS_ANIMATION_REPEAT_MODE_REVERSE)
pd->in->speed *= -1;
pd->in->run_start_time = current;
}
else
{
efl_canvas_object_animation_stop(obj);
}
}
}
static void
_end(Efl_Canvas_Object_Animation *obj, Efl_Canvas_Object_Animation_Data *pd)
{
EINA_SAFETY_ON_NULL_RETURN(pd->in);
efl_event_callback_del(obj, EFL_CANVAS_OBJECT_EVENT_ANIMATOR_TICK, _animator_cb, obj);
}
static void
_start(Efl_Canvas_Object_Animation *obj, Efl_Canvas_Object_Animation_Data *pd, double delay)
{
EINA_SAFETY_ON_NULL_RETURN(pd->in);
pd->in->run_start_time = ecore_loop_time_get() - efl_animation_duration_get(pd->in->animation)*delay;
efl_event_callback_add(obj, EFL_CANVAS_OBJECT_EVENT_ANIMATOR_TICK, _animator_cb, obj);
_animator_cb(obj, NULL);
}
static Eina_Value
_start_fcb(Eo *o, void *data EINA_UNUSED, const Eina_Value v)
{
Efl_Canvas_Object_Animation_Data *pd = efl_data_scope_safe_get(o, MY_CLASS);
EINA_SAFETY_ON_NULL_RETURN_VAL(pd, EINA_VALUE_EMPTY);
if (!pd->in) return v; //animation was stopped before anything started
_start(o, pd, pd->in->start_pos);
return v;
}
EOLIAN static Efl_Canvas_Animation*
_efl_canvas_object_animation_animation_get(const Eo *obj EINA_UNUSED, Efl_Canvas_Object_Animation_Data *pd)
{
if (!pd->in) return NULL;
return pd->in->animation;
}
EOLIAN static double
_efl_canvas_object_animation_animation_progress_get(const Eo *obj EINA_UNUSED, Efl_Canvas_Object_Animation_Data *pd)
{
if (pd->in && pd->in->animation)
return (pd->in->speed < 0) ? fabs(1.0 - pd->in->progress) : pd->in->progress;
else
return -1.0;
}
EOLIAN static void
_efl_canvas_object_animation_animation_pause_set(Eo *obj, Efl_Canvas_Object_Animation_Data *pd, Eina_Bool pause)
{
EINA_SAFETY_ON_NULL_RETURN(pd->in);
if (pd->in->pause_state == pause) return;
if (pause)
_end(obj, pd);
else
_start(obj, pd,(pd->in->speed < 0) ? 1.0 - pd->in->progress : pd->in->progress);
if (pd->in) pd->in->pause_state = pause;
}
EOLIAN static Eina_Bool
_efl_canvas_object_animation_animation_pause_get(const Eo *obj EINA_UNUSED, Efl_Canvas_Object_Animation_Data *pd)
{
if (!pd->in) return EINA_FALSE;
return pd->in->pause_state;
}
EOLIAN static void
_efl_canvas_object_animation_animation_start(Eo *obj, Efl_Canvas_Object_Animation_Data *pd, Efl_Canvas_Animation *animation, double speed, double start_pos)
{
Efl_Canvas_Object_Animation_Indirect_Data *in;
if (pd->in && pd->in->animation)
efl_canvas_object_animation_stop(obj);
EINA_SAFETY_ON_FALSE_RETURN(!pd->in);
in = pd->in = calloc(1, sizeof(Efl_Canvas_Object_Animation_Indirect_Data));
EINA_SAFETY_ON_NULL_RETURN(animation);
EINA_SAFETY_ON_FALSE_RETURN(start_pos >= 0.0 && start_pos <= 1.0);
EINA_SAFETY_ON_FALSE_RETURN(!EINA_DBL_EQ(speed, 0.0));
EINA_SAFETY_ON_FALSE_RETURN(efl_playable_seekable_get(animation));
in->pause_state = EINA_FALSE;
in->animation = efl_ref(animation);
in->remaining_repeats = efl_animation_play_count_get(animation) - 1; // -1 because one run is already going on
in->speed = speed;
in->start_pos = start_pos;
efl_event_callback_call(obj, EFL_CANVAS_OBJECT_ANIMATION_EVENT_ANIMATION_CHANGED, in->animation);
//You should not rely on in beeing available after calling the above event.
in = NULL;
if (efl_animation_start_delay_get(animation) > 0.0)
{
Eina_Future *f = efl_loop_timeout(efl_loop_get(obj), efl_animation_start_delay_get(animation));
efl_future_then(obj, f, .success = _start_fcb);
}
else
_start(obj, pd, start_pos);
}
EOLIAN static void
_efl_canvas_object_animation_animation_stop(Eo *obj, Efl_Canvas_Object_Animation_Data *pd)
{
if (!pd->in) return;
if (!efl_animation_final_state_keep_get(pd->in->animation))
efl_gfx_mapping_reset(obj);
_end(obj, pd);
efl_unref(pd->in->animation);
pd->in->animation = NULL;
efl_event_callback_call(obj, EFL_CANVAS_OBJECT_ANIMATION_EVENT_ANIMATION_CHANGED, pd->in->animation);
//this could be NULL if some weird callstack calls stop again while the above event is executed
if (pd->in)
free(pd->in);
pd->in = NULL;
}
#include "efl_canvas_object_animation.eo.c"