1 /**
2 * @file anim.c
3 *
4 */
5
6 /*********************
7 * INCLUDES
8 *********************/
9 #include "lv_anim.h"
10
11 #if LV_USE_ANIMATION
12 #include <stddef.h>
13 #include <string.h>
14 #include "../lv_hal/lv_hal_tick.h"
15 #include "lv_task.h"
16 #include "lv_math.h"
17 #include "lv_gc.h"
18
19 #if defined(LV_GC_INCLUDE)
20 #include LV_GC_INCLUDE
21 #endif /* LV_ENABLE_GC */
22
23 /*********************
24 * DEFINES
25 *********************/
26 #define LV_ANIM_RESOLUTION 1024
27 #define LV_ANIM_RES_SHIFT 10
28
29 /**********************
30 * TYPEDEFS
31 **********************/
32
33 /**********************
34 * STATIC PROTOTYPES
35 **********************/
36 static void anim_task(lv_task_t * param);
37 static bool anim_ready_handler(lv_anim_t * a);
38
39 /**********************
40 * STATIC VARIABLES
41 **********************/
42 static uint32_t last_task_run;
43 static bool anim_list_changed;
44
45 /**********************
46 * MACROS
47 **********************/
48
49 /**********************
50 * GLOBAL FUNCTIONS
51 **********************/
52
53 /**
54 * Init. the animation module
55 */
lv_anim_core_init(void)56 void lv_anim_core_init(void)
57 {
58 lv_ll_init(&LV_GC_ROOT(_lv_anim_ll), sizeof(lv_anim_t));
59 last_task_run = lv_tick_get();
60 lv_task_create(anim_task, LV_DISP_DEF_REFR_PERIOD, LV_TASK_PRIO_MID, NULL);
61 }
62
63 /**
64 * Initialize an animation variable.
65 * E.g.:
66 * lv_anim_t a;
67 * lv_anim_init(&a);
68 * lv_anim_set_...(&a);
69 * lv_anim_craete(&a);
70 * @param a pointer to an `lv_anim_t` variable to initialize
71 */
lv_anim_init(lv_anim_t * a)72 void lv_anim_init(lv_anim_t * a)
73 {
74 memset(a, 0, sizeof(lv_anim_t));
75 a->time = 500;
76 a->start = 0;
77 a->end = 100;
78 a->path_cb = lv_anim_path_linear;
79 }
80 /**
81 * Create an animation
82 * @param a an initialized 'anim_t' variable. Not required after call.
83 */
lv_anim_create(lv_anim_t * a)84 void lv_anim_create(lv_anim_t * a)
85 {
86 LV_LOG_TRACE("animation create started")
87 /* Do not let two animations for the same 'var' with the same 'fp'*/
88 if(a->exec_cb != NULL) lv_anim_del(a->var, a->exec_cb); /*fp == NULL would delete all animations of var*/
89
90 /*Add the new animation to the animation linked list*/
91 lv_anim_t * new_anim = lv_ll_ins_head(&LV_GC_ROOT(_lv_anim_ll));
92 lv_mem_assert(new_anim);
93 if(new_anim == NULL) return;
94
95 /*Initialize the animation descriptor*/
96 a->playback_now = 0;
97 memcpy(new_anim, a, sizeof(lv_anim_t));
98
99 /*Set the start value*/
100 if(new_anim->exec_cb) new_anim->exec_cb(new_anim->var, new_anim->start);
101
102 /* Creating an animation changed the linked list.
103 * It's important if it happens in a ready callback. (see `anim_task`)*/
104 anim_list_changed = true;
105
106 LV_LOG_TRACE("animation created")
107 }
108
109 /**
110 * Delete an animation of a variable with a given animator function
111 * @param var pointer to variable
112 * @param exec_cb a function pointer which is animating 'var',
113 * or NULL to delete all the animations of 'var'
114 * @return true: at least 1 animation is deleted, false: no animation is deleted
115 */
lv_anim_del(void * var,lv_anim_exec_xcb_t exec_cb)116 bool lv_anim_del(void * var, lv_anim_exec_xcb_t exec_cb)
117 {
118 lv_anim_t * a;
119 lv_anim_t * a_next;
120 bool del = false;
121 a = lv_ll_get_head(&LV_GC_ROOT(_lv_anim_ll));
122 while(a != NULL) {
123 /*'a' might be deleted, so get the next object while 'a' is valid*/
124 a_next = lv_ll_get_next(&LV_GC_ROOT(_lv_anim_ll), a);
125
126 if(a->var == var && (a->exec_cb == exec_cb || exec_cb == NULL)) {
127 lv_ll_rem(&LV_GC_ROOT(_lv_anim_ll), a);
128 lv_mem_free(a);
129 anim_list_changed = true; /*Read by `anim_task`. It need to know if a delete occurred in
130 the linked list*/
131 del = true;
132 }
133
134 a = a_next;
135 }
136
137 return del;
138 }
139
140 /**
141 * Get the number of currently running animations
142 * @return the number of running animations
143 */
lv_anim_count_running(void)144 uint16_t lv_anim_count_running(void)
145 {
146 uint16_t cnt = 0;
147 lv_anim_t * a;
148 LV_LL_READ(LV_GC_ROOT(_lv_anim_ll), a) cnt++;
149
150 return cnt++;
151 }
152
153 /**
154 * Calculate the time of an animation with a given speed and the start and end values
155 * @param speed speed of animation in unit/sec
156 * @param start start value of the animation
157 * @param end end value of the animation
158 * @return the required time [ms] for the animation with the given parameters
159 */
lv_anim_speed_to_time(uint16_t speed,lv_anim_value_t start,lv_anim_value_t end)160 uint16_t lv_anim_speed_to_time(uint16_t speed, lv_anim_value_t start, lv_anim_value_t end)
161 {
162 int32_t d = LV_MATH_ABS((int32_t)start - end);
163 uint32_t time = (int32_t)((int32_t)(d * 1000) / speed);
164
165 if(time > UINT16_MAX) time = UINT16_MAX;
166
167 if(time == 0) {
168 time++;
169 }
170
171 return time;
172 }
173
174 /**
175 * Calculate the current value of an animation applying linear characteristic
176 * @param a pointer to an animation
177 * @return the current value to set
178 */
lv_anim_path_linear(const lv_anim_t * a)179 lv_anim_value_t lv_anim_path_linear(const lv_anim_t * a)
180 {
181 /*Calculate the current step*/
182 uint32_t step;
183 if(a->time == a->act_time) {
184 step = LV_ANIM_RESOLUTION; /*Use the last value if the time fully elapsed*/
185 } else {
186 step = ((int32_t)a->act_time * LV_ANIM_RESOLUTION) / a->time;
187 }
188
189 /* Get the new value which will be proportional to `step`
190 * and the `start` and `end` values*/
191 int32_t new_value;
192 new_value = (int32_t)step * (a->end - a->start);
193 new_value = new_value >> LV_ANIM_RES_SHIFT;
194 new_value += a->start;
195
196 return (lv_anim_value_t)new_value;
197 }
198
199 /**
200 * Calculate the current value of an animation slowing down the start phase
201 * @param a pointer to an animation
202 * @return the current value to set
203 */
lv_anim_path_ease_in(const lv_anim_t * a)204 lv_anim_value_t lv_anim_path_ease_in(const lv_anim_t * a)
205 {
206 /*Calculate the current step*/
207 uint32_t t;
208 if(a->time == a->act_time)
209 t = 1024;
210 else
211 t = (uint32_t)((uint32_t)a->act_time * 1024) / a->time;
212
213 int32_t step = lv_bezier3(t, 0, 1, 1, 1024);
214
215 int32_t new_value;
216 new_value = (int32_t)step * (a->end - a->start);
217 new_value = new_value >> 10;
218 new_value += a->start;
219
220 return (lv_anim_value_t)new_value;
221 }
222
223 /**
224 * Calculate the current value of an animation slowing down the end phase
225 * @param a pointer to an animation
226 * @return the current value to set
227 */
lv_anim_path_ease_out(const lv_anim_t * a)228 lv_anim_value_t lv_anim_path_ease_out(const lv_anim_t * a)
229 {
230 /*Calculate the current step*/
231
232 uint32_t t;
233 if(a->time == a->act_time)
234 t = 1024;
235 else
236 t = (uint32_t)((uint32_t)a->act_time * 1024) / a->time;
237
238 int32_t step = lv_bezier3(t, 0, 1023, 1023, 1024);
239
240 int32_t new_value;
241 new_value = (int32_t)step * (a->end - a->start);
242 new_value = new_value >> 10;
243 new_value += a->start;
244
245 return (lv_anim_value_t)new_value;
246 }
247
248 /**
249 * Calculate the current value of an animation applying an "S" characteristic (cosine)
250 * @param a pointer to an animation
251 * @return the current value to set
252 */
lv_anim_path_ease_in_out(const lv_anim_t * a)253 lv_anim_value_t lv_anim_path_ease_in_out(const lv_anim_t * a)
254 {
255 /*Calculate the current step*/
256
257 uint32_t t;
258 if(a->time == a->act_time)
259 t = 1024;
260 else
261 t = (uint32_t)((uint32_t)a->act_time * 1024) / a->time;
262
263 int32_t step = lv_bezier3(t, 0, 100, 924, 1024);
264
265 int32_t new_value;
266 new_value = (int32_t)step * (a->end - a->start);
267 new_value = new_value >> 10;
268 new_value += a->start;
269
270 return (lv_anim_value_t)new_value;
271 }
272
273 /**
274 * Calculate the current value of an animation with overshoot at the end
275 * @param a pointer to an animation
276 * @return the current value to set
277 */
lv_anim_path_overshoot(const lv_anim_t * a)278 lv_anim_value_t lv_anim_path_overshoot(const lv_anim_t * a)
279 {
280 /*Calculate the current step*/
281
282 uint32_t t;
283 if(a->time == a->act_time)
284 t = 1024;
285 else
286 t = (uint32_t)((uint32_t)a->act_time * 1024) / a->time;
287
288 int32_t step = lv_bezier3(t, 0, 600, 1300, 1024);
289
290 int32_t new_value;
291 new_value = (int32_t)step * (a->end - a->start);
292 new_value = new_value >> 10;
293 new_value += a->start;
294
295 return (lv_anim_value_t)new_value;
296 }
297
298 /**
299 * Calculate the current value of an animation with 3 bounces
300 * @param a pointer to an animation
301 * @return the current value to set
302 */
lv_anim_path_bounce(const lv_anim_t * a)303 lv_anim_value_t lv_anim_path_bounce(const lv_anim_t * a)
304 {
305 /*Calculate the current step*/
306 uint32_t t;
307 if(a->time == a->act_time)
308 t = 1024;
309 else
310 t = (uint32_t)((uint32_t)a->act_time * 1024) / a->time;
311
312 int32_t diff = (a->end - a->start);
313
314 /*3 bounces has 5 parts: 3 down and 2 up. One part is t / 5 long*/
315
316 if(t < 408) {
317 /*Go down*/
318 t = (t * 2500) >> 10; /*[0..1024] range*/
319 } else if(t >= 408 && t < 614) {
320 /*First bounce back*/
321 t -= 408;
322 t = t * 5; /*to [0..1024] range*/
323 t = 1024 - t;
324 diff = diff / 6;
325 } else if(t >= 614 && t < 819) {
326 /*Fall back*/
327 t -= 614;
328 t = t * 5; /*to [0..1024] range*/
329 diff = diff / 6;
330 } else if(t >= 819 && t < 921) {
331 /*Second bounce back*/
332 t -= 819;
333 t = t * 10; /*to [0..1024] range*/
334 t = 1024 - t;
335 diff = diff / 16;
336 } else if(t >= 921 && t <= 1024) {
337 /*Fall back*/
338 t -= 921;
339 t = t * 10; /*to [0..1024] range*/
340 diff = diff / 16;
341 }
342
343 if(t > 1024) t = 1024;
344
345 int32_t step = lv_bezier3(t, 1024, 1024, 800, 0);
346
347 int32_t new_value;
348 new_value = (int32_t)step * diff;
349 new_value = new_value >> 10;
350 new_value = a->end - new_value;
351
352 return (lv_anim_value_t)new_value;
353 }
354
355 /**
356 * Calculate the current value of an animation applying step characteristic.
357 * (Set end value on the end of the animation)
358 * @param a pointer to an animation
359 * @return the current value to set
360 */
lv_anim_path_step(const lv_anim_t * a)361 lv_anim_value_t lv_anim_path_step(const lv_anim_t * a)
362 {
363 if(a->act_time >= a->time)
364 return a->end;
365 else
366 return a->start;
367 }
368
369 /**********************
370 * STATIC FUNCTIONS
371 **********************/
372
373 /**
374 * Periodically handle the animations.
375 * @param param unused
376 */
anim_task(lv_task_t * param)377 static void anim_task(lv_task_t * param)
378 {
379 (void)param;
380
381 lv_anim_t * a;
382 LV_LL_READ(LV_GC_ROOT(_lv_anim_ll), a)
383 {
384 a->has_run = 0;
385 }
386
387 uint32_t elaps = lv_tick_elaps(last_task_run);
388
389 a = lv_ll_get_head(&LV_GC_ROOT(_lv_anim_ll));
390
391 while(a != NULL) {
392 /*It can be set by `lv_anim_del()` typically in `end_cb`. If set then an animation delete
393 * happened in `anim_ready_handler` which could make this linked list reading corrupt
394 * because the list is changed meanwhile
395 */
396 anim_list_changed = false;
397
398 if(!a->has_run) {
399 a->has_run = 1; /*The list readying might be reseted so need to know which anim has run already*/
400 a->act_time += elaps;
401 if(a->act_time >= 0) {
402 if(a->act_time > a->time) a->act_time = a->time;
403
404 int32_t new_value;
405 new_value = a->path_cb(a);
406
407 /*Apply the calculated value*/
408 if(a->exec_cb) a->exec_cb(a->var, new_value);
409
410 /*If the time is elapsed the animation is ready*/
411 if(a->act_time >= a->time) {
412 anim_ready_handler(a);
413 }
414 }
415 }
416
417 /* If the linked list changed due to anim. delete then it's not safe to continue
418 * the reading of the list from here -> start from the head*/
419 if(anim_list_changed)
420 a = lv_ll_get_head(&LV_GC_ROOT(_lv_anim_ll));
421 else
422 a = lv_ll_get_next(&LV_GC_ROOT(_lv_anim_ll), a);
423 }
424
425 last_task_run = lv_tick_get();
426 }
427
428 /**
429 * Called when an animation is ready to do the necessary thinks
430 * e.g. repeat, play back, delete etc.
431 * @param a pointer to an animation descriptor
432 * @return true: animation delete occurred nnd the `LV_GC_ROOT(_lv_anim_ll)` has changed
433 * */
anim_ready_handler(lv_anim_t * a)434 static bool anim_ready_handler(lv_anim_t * a)
435 {
436
437 /*Delete the animation if
438 * - no repeat and no play back (simple one shot animation)
439 * - no repeat, play back is enabled and play back is ready */
440 if((a->repeat == 0 && a->playback == 0) || (a->repeat == 0 && a->playback == 1 && a->playback_now == 1)) {
441
442 /*Create copy from the animation and delete the animation from the list.
443 * This way the `ready_cb` will see the animations like it's animation is ready deleted*/
444 lv_anim_t a_tmp;
445 memcpy(&a_tmp, a, sizeof(lv_anim_t));
446 lv_ll_rem(&LV_GC_ROOT(_lv_anim_ll), a);
447 lv_mem_free(a);
448 anim_list_changed = true;
449
450 /* Call the callback function at the end*/
451 if(a_tmp.ready_cb != NULL) a_tmp.ready_cb(&a_tmp);
452 }
453 /*If the animation is not deleted then restart it*/
454 else {
455 a->act_time = -a->repeat_pause; /*Restart the animation*/
456 /*Swap the start and end values in play back mode*/
457 if(a->playback != 0) {
458 /*If now turning back use the 'playback_pause*/
459 if(a->playback_now == 0) a->act_time = -a->playback_pause;
460
461 /*Toggle the play back state*/
462 a->playback_now = a->playback_now == 0 ? 1 : 0;
463 /*Swap the start and end values*/
464 int32_t tmp;
465 tmp = a->start;
466 a->start = a->end;
467 a->end = tmp;
468 }
469 }
470
471 return anim_list_changed;
472 }
473 #endif
474