summaryrefslogtreecommitdiff
path: root/src/lib/evas/canvas/efl_canvas_object_animation.c
blob: 3fbc0e94f46e36cf808bfdb21b0bab412cb4775f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
#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;
   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);
   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"