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_misc/lv_debug.h"
15 #include "../lv_hal/lv_hal_tick.h"
16 #include "lv_task.h"
17 #include "lv_math.h"
18 #include "lv_gc.h"
19
20 #if defined(LV_GC_INCLUDE)
21 #include LV_GC_INCLUDE
22 #endif /* LV_ENABLE_GC */
23
24 /*********************
25 * DEFINES
26 *********************/
27 #define LV_ANIM_RESOLUTION 1024
28 #define LV_ANIM_RES_SHIFT 10
29 #define LV_ANIM_TASK_PRIO LV_TASK_PRIO_HIGH
30
31 /**********************
32 * TYPEDEFS
33 **********************/
34
35 /**********************
36 * STATIC PROTOTYPES
37 **********************/
38 static void anim_task(lv_task_t * param);
39 static void anim_mark_list_change(void);
40 static bool anim_ready_handler(lv_anim_t * a);
41
42 /**********************
43 * STATIC VARIABLES
44 **********************/
45 static uint32_t last_task_run;
46 static bool anim_list_changed;
47 static lv_task_t * _lv_anim_task;
48 const lv_anim_path_t lv_anim_path_def = {.cb = lv_anim_path_linear};
49
50 /**********************
51 * MACROS
52 **********************/
53
54 /**********************
55 * GLOBAL FUNCTIONS
56 **********************/
57
58 /**
59 * Init. the animation module
60 */
_lv_anim_core_init(void)61 void _lv_anim_core_init(void)
62 {
63 _lv_ll_init(&LV_GC_ROOT(_lv_anim_ll), sizeof(lv_anim_t));
64 last_task_run = lv_tick_get();
65 _lv_anim_task = lv_task_create(anim_task, LV_DISP_DEF_REFR_PERIOD, LV_ANIM_TASK_PRIO, NULL);
66 anim_mark_list_change(); /*Turn off the animation task*/
67 anim_list_changed = false; /*The list has not actually changed*/
68 }
69
70 /**
71 * Initialize an animation variable.
72 * E.g.:
73 * lv_anim_t a;
74 * lv_anim_init(&a);
75 * lv_anim_set_...(&a);
76 * @param a pointer to an `lv_anim_t` variable to initialize
77 */
lv_anim_init(lv_anim_t * a)78 void lv_anim_init(lv_anim_t * a)
79 {
80 _lv_memset_00(a, sizeof(lv_anim_t));
81 a->time = 500;
82 a->start = 0;
83 a->end = 100;
84 _lv_memcpy_small(&a->path, &lv_anim_path_def, sizeof(lv_anim_path_cb_t));
85 a->repeat_cnt = 1;
86 a->early_apply = 1;
87 }
88 /**
89 * Create an animation
90 * @param a an initialized 'anim_t' variable. Not required after call.
91 */
lv_anim_start(lv_anim_t * a)92 void lv_anim_start(lv_anim_t * a)
93 {
94 LV_LOG_TRACE("animation create started")
95 /* Do not let two animations for the same 'var' with the same 'fp'*/
96 if(a->exec_cb != NULL) lv_anim_del(a->var, a->exec_cb); /*fp == NULL would delete all animations of var*/
97
98 /*If the list is empty the anim task was suspended and it's last run measure is invalid*/
99 if(_lv_ll_is_empty(&LV_GC_ROOT(_lv_anim_ll))) {
100 last_task_run = lv_tick_get() - 1;
101 }
102
103 /*Add the new animation to the animation linked list*/
104 lv_anim_t * new_anim = _lv_ll_ins_head(&LV_GC_ROOT(_lv_anim_ll));
105 LV_ASSERT_MEM(new_anim);
106 if(new_anim == NULL) return;
107
108 /*Initialize the animation descriptor*/
109 a->time_orig = a->time;
110 _lv_memcpy(new_anim, a, sizeof(lv_anim_t));
111
112 /*Set the start value*/
113 if(new_anim->early_apply) {
114 if(new_anim->exec_cb && new_anim->var) new_anim->exec_cb(new_anim->var, new_anim->start);
115 }
116
117 /* Creating an animation changed the linked list.
118 * It's important if it happens in a ready callback. (see `anim_task`)*/
119 anim_mark_list_change();
120
121 LV_LOG_TRACE("animation created")
122 }
123
124 /**
125 * Delete an animation of a variable with a given animator function
126 * @param var pointer to variable
127 * @param exec_cb a function pointer which is animating 'var',
128 * or NULL to delete all the animations of 'var'
129 * @return true: at least 1 animation is deleted, false: no animation is deleted
130 */
lv_anim_del(void * var,lv_anim_exec_xcb_t exec_cb)131 bool lv_anim_del(void * var, lv_anim_exec_xcb_t exec_cb)
132 {
133 lv_anim_t * a;
134 lv_anim_t * a_next;
135 bool del = false;
136 a = _lv_ll_get_head(&LV_GC_ROOT(_lv_anim_ll));
137 while(a != NULL) {
138 /*'a' might be deleted, so get the next object while 'a' is valid*/
139 a_next = _lv_ll_get_next(&LV_GC_ROOT(_lv_anim_ll), a);
140
141 if(a->var == var && (a->exec_cb == exec_cb || exec_cb == NULL)) {
142 _lv_ll_remove(&LV_GC_ROOT(_lv_anim_ll), a);
143 lv_mem_free(a);
144 anim_mark_list_change(); /*Read by `anim_task`. It need to know if a delete occurred in
145 the linked list*/
146 del = true;
147 }
148
149 a = a_next;
150 }
151
152 return del;
153 }
154
155 /**
156 * Get the animation of a variable and its `exec_cb`.
157 * @param var pointer to variable
158 * @param exec_cb a function pointer which is animating 'var',
159 * or NULL to delete all the animations of 'var'
160 * @return pointer to the animation.
161 */
lv_anim_get(void * var,lv_anim_exec_xcb_t exec_cb)162 lv_anim_t * lv_anim_get(void * var, lv_anim_exec_xcb_t exec_cb)
163 {
164 lv_anim_t * a;
165 _LV_LL_READ(LV_GC_ROOT(_lv_anim_ll), a) {
166 if(a->var == var && a->exec_cb == exec_cb) {
167 return a;
168 }
169 }
170
171 return NULL;
172 }
173
174 /**
175 * Get the number of currently running animations
176 * @return the number of running animations
177 */
lv_anim_count_running(void)178 uint16_t lv_anim_count_running(void)
179 {
180 uint16_t cnt = 0;
181 lv_anim_t * a;
182 _LV_LL_READ(LV_GC_ROOT(_lv_anim_ll), a) cnt++;
183
184 return cnt;
185 }
186
187 /**
188 * Calculate the time of an animation with a given speed and the start and end values
189 * @param speed speed of animation in unit/sec
190 * @param start start value of the animation
191 * @param end end value of the animation
192 * @return the required time [ms] for the animation with the given parameters
193 */
lv_anim_speed_to_time(uint16_t speed,lv_anim_value_t start,lv_anim_value_t end)194 uint16_t lv_anim_speed_to_time(uint16_t speed, lv_anim_value_t start, lv_anim_value_t end)
195 {
196 int32_t d = LV_MATH_ABS((int32_t)start - end);
197 uint32_t time = (int32_t)((int32_t)(d * 1000) / speed);
198
199 if(time > UINT16_MAX) time = UINT16_MAX;
200
201 if(time == 0) {
202 time++;
203 }
204
205 return time;
206 }
207
208 /**
209 * Manually refresh the state of the animations.
210 * Useful to make the animations running in a blocking process where
211 * `lv_task_handler` can't run for a while.
212 * Shouldn't be used directly because it is called in `lv_refr_now()`.
213 */
lv_anim_refr_now(void)214 void lv_anim_refr_now(void)
215 {
216 anim_task(NULL);
217 }
218
219 /**
220 * Calculate the current value of an animation applying linear characteristic
221 * @param a pointer to an animation
222 * @return the current value to set
223 */
lv_anim_path_linear(const lv_anim_path_t * path,const lv_anim_t * a)224 lv_anim_value_t lv_anim_path_linear(const lv_anim_path_t * path, const lv_anim_t * a)
225 {
226 LV_UNUSED(path);
227
228 /*Calculate the current step*/
229 uint32_t step;
230 if(a->time == a->act_time) {
231 step = LV_ANIM_RESOLUTION; /*Use the last value if the time fully elapsed*/
232 }
233 else {
234 step = ((int32_t)a->act_time * LV_ANIM_RESOLUTION) / a->time;
235 }
236
237 /* Get the new value which will be proportional to `step`
238 * and the `start` and `end` values*/
239 int32_t new_value;
240 new_value = (int32_t)step * (a->end - a->start);
241 new_value = new_value >> LV_ANIM_RES_SHIFT;
242 new_value += a->start;
243
244 return (lv_anim_value_t)new_value;
245 }
246
247 /**
248 * Calculate the current value of an animation slowing down the start phase
249 * @param a pointer to an animation
250 * @return the current value to set
251 */
lv_anim_path_ease_in(const lv_anim_path_t * path,const lv_anim_t * a)252 lv_anim_value_t lv_anim_path_ease_in(const lv_anim_path_t * path, const lv_anim_t * a)
253 {
254 LV_UNUSED(path);
255
256 /*Calculate the current step*/
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, 1, 1, 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 slowing down the end phase
275 * @param a pointer to an animation
276 * @return the current value to set
277 */
lv_anim_path_ease_out(const lv_anim_path_t * path,const lv_anim_t * a)278 lv_anim_value_t lv_anim_path_ease_out(const lv_anim_path_t * path, const lv_anim_t * a)
279 {
280 LV_UNUSED(path);
281
282 /*Calculate the current step*/
283
284 uint32_t t;
285 if(a->time == a->act_time)
286 t = 1024;
287 else
288 t = (uint32_t)((uint32_t)a->act_time * 1024) / a->time;
289
290 int32_t step = _lv_bezier3(t, 0, 1023, 1023, 1024);
291
292 int32_t new_value;
293 new_value = (int32_t)step * (a->end - a->start);
294 new_value = new_value >> 10;
295 new_value += a->start;
296
297 return (lv_anim_value_t)new_value;
298 }
299
300 /**
301 * Calculate the current value of an animation applying an "S" characteristic (cosine)
302 * @param a pointer to an animation
303 * @return the current value to set
304 */
lv_anim_path_ease_in_out(const lv_anim_path_t * path,const lv_anim_t * a)305 lv_anim_value_t lv_anim_path_ease_in_out(const lv_anim_path_t * path, const lv_anim_t * a)
306 {
307 LV_UNUSED(path);
308
309 /*Calculate the current step*/
310
311 uint32_t t;
312 if(a->time == a->act_time)
313 t = 1024;
314 else
315 t = (uint32_t)((uint32_t)a->act_time * 1024) / a->time;
316
317 int32_t step = _lv_bezier3(t, 0, 100, 924, 1024);
318
319 int32_t new_value;
320 new_value = (int32_t)step * (a->end - a->start);
321 new_value = new_value >> 10;
322 new_value += a->start;
323
324 return (lv_anim_value_t)new_value;
325 }
326
327 /**
328 * Calculate the current value of an animation with overshoot at the end
329 * @param a pointer to an animation
330 * @return the current value to set
331 */
lv_anim_path_overshoot(const lv_anim_path_t * path,const lv_anim_t * a)332 lv_anim_value_t lv_anim_path_overshoot(const lv_anim_path_t * path, const lv_anim_t * a)
333 {
334 LV_UNUSED(path);
335
336 /*Calculate the current step*/
337
338 uint32_t t;
339 if(a->time == a->act_time)
340 t = 1024;
341 else
342 t = (uint32_t)((uint32_t)a->act_time * 1024) / a->time;
343
344 int32_t step = _lv_bezier3(t, 0, 1000, 2000, 1024);
345
346 int32_t new_value;
347 new_value = (int32_t)step * (a->end - a->start);
348 new_value = new_value >> 10;
349 new_value += a->start;
350
351 return (lv_anim_value_t)new_value;
352 }
353
354
355 /**
356 * Calculate the current value of an animation with 3 bounces
357 * @param a pointer to an animation
358 * @return the current value to set
359 */
lv_anim_path_bounce(const lv_anim_path_t * path,const lv_anim_t * a)360 lv_anim_value_t lv_anim_path_bounce(const lv_anim_path_t * path, const lv_anim_t * a)
361 {
362 LV_UNUSED(path);
363
364 /*Calculate the current step*/
365 uint32_t t;
366 if(a->time == a->act_time)
367 t = 1024;
368 else
369 t = (uint32_t)((uint32_t)a->act_time * 1024) / a->time;
370
371 int32_t diff = (a->end - a->start);
372
373 /*3 bounces has 5 parts: 3 down and 2 up. One part is t / 5 long*/
374
375 if(t < 408) {
376 /*Go down*/
377 t = (t * 2500) >> 10; /*[0..1024] range*/
378 }
379 else if(t >= 408 && t < 614) {
380 /*First bounce back*/
381 t -= 408;
382 t = t * 5; /*to [0..1024] range*/
383 t = 1024 - t;
384 diff = diff / 6;
385 }
386 else if(t >= 614 && t < 819) {
387 /*Fall back*/
388 t -= 614;
389 t = t * 5; /*to [0..1024] range*/
390 diff = diff / 6;
391 }
392 else if(t >= 819 && t < 921) {
393 /*Second bounce back*/
394 t -= 819;
395 t = t * 10; /*to [0..1024] range*/
396 t = 1024 - t;
397 diff = diff / 16;
398 }
399 else if(t >= 921 && t <= 1024) {
400 /*Fall back*/
401 t -= 921;
402 t = t * 10; /*to [0..1024] range*/
403 diff = diff / 16;
404 }
405
406 if(t > 1024) t = 1024;
407
408 int32_t step = _lv_bezier3(t, 1024, 1024, 800, 0);
409
410 int32_t new_value;
411 new_value = (int32_t)step * diff;
412 new_value = new_value >> 10;
413 new_value = a->end - new_value;
414
415 return (lv_anim_value_t)new_value;
416 }
417
418 /**
419 * Calculate the current value of an animation applying step characteristic.
420 * (Set end value on the end of the animation)
421 * @param a pointer to an animation
422 * @return the current value to set
423 */
lv_anim_path_step(const lv_anim_path_t * path,const lv_anim_t * a)424 lv_anim_value_t lv_anim_path_step(const lv_anim_path_t * path, const lv_anim_t * a)
425 {
426 LV_UNUSED(path);
427
428 if(a->act_time >= a->time)
429 return a->end;
430 else
431 return a->start;
432 }
433
434 /**********************
435 * STATIC FUNCTIONS
436 **********************/
437
438 /**
439 * Periodically handle the animations.
440 * @param param unused
441 */
anim_task(lv_task_t * param)442 static void anim_task(lv_task_t * param)
443 {
444 (void)param;
445
446 lv_anim_t * a;
447 _LV_LL_READ(LV_GC_ROOT(_lv_anim_ll), a) {
448 a->has_run = 0;
449 }
450
451 uint32_t elaps = lv_tick_elaps(last_task_run);
452
453 a = _lv_ll_get_head(&LV_GC_ROOT(_lv_anim_ll));
454
455 while(a != NULL) {
456 /*It can be set by `lv_anim_del()` typically in `end_cb`. If set then an animation delete
457 * happened in `anim_ready_handler` which could make this linked list reading corrupt
458 * because the list is changed meanwhile
459 */
460 anim_list_changed = false;
461
462 if(!a->has_run) {
463 a->has_run = 1; /*The list readying might be reseted so need to know which anim has run already*/
464
465 /*The animation will run now for the first time. Call `start_cb`*/
466 int32_t new_act_time = a->act_time + elaps;
467 if(a->act_time <= 0 && new_act_time >= 0) {
468 if(a->start_cb) a->start_cb(a);
469 }
470 a->act_time += elaps;
471 if(a->act_time >= 0) {
472 if(a->act_time > a->time) a->act_time = a->time;
473
474 int32_t new_value;
475 if(a->path.cb) new_value = a->path.cb(&a->path, a);
476 else new_value = lv_anim_path_linear(&a->path, a);
477
478 if(new_value != a->current) {
479 a->current = new_value;
480 /*Apply the calculated value*/
481 if(a->exec_cb) a->exec_cb(a->var, new_value);
482 }
483
484 /*If the time is elapsed the animation is ready*/
485 if(a->act_time >= a->time) {
486 anim_ready_handler(a);
487 }
488 }
489 }
490
491 /* If the linked list changed due to anim. delete then it's not safe to continue
492 * the reading of the list from here -> start from the head*/
493 if(anim_list_changed)
494 a = _lv_ll_get_head(&LV_GC_ROOT(_lv_anim_ll));
495 else
496 a = _lv_ll_get_next(&LV_GC_ROOT(_lv_anim_ll), a);
497 }
498
499 last_task_run = lv_tick_get();
500 }
501
502 /**
503 * Called when an animation is ready to do the necessary thinks
504 * e.g. repeat, play back, delete etc.
505 * @param a pointer to an animation descriptor
506 * @return true: animation delete occurred and the `LV_GC_ROOT(_lv_anim_ll)` has changed
507 * */
anim_ready_handler(lv_anim_t * a)508 static bool anim_ready_handler(lv_anim_t * a)
509 {
510 /*In the end of a forward anim decrement repeat cnt.*/
511 if(a->playback_now == 0 && a->repeat_cnt > 0 && a->repeat_cnt != LV_ANIM_REPEAT_INFINITE) {
512 a->repeat_cnt--;
513 }
514
515 /*Delete the animation if
516 * - no repeat left and no play back (simple one shot animation)
517 * - no repeat, play back is enabled and play back is ready */
518 if(a->repeat_cnt == 0 && ((a->playback_time == 0) || (a->playback_time && a->playback_now == 1))) {
519
520 /*Create copy from the animation and delete the animation from the list.
521 * This way the `ready_cb` will see the animations like it's animation is ready deleted*/
522 lv_anim_t a_tmp;
523 _lv_memcpy(&a_tmp, a, sizeof(lv_anim_t));
524 _lv_ll_remove(&LV_GC_ROOT(_lv_anim_ll), a);
525 lv_mem_free(a);
526 /*Flag that the list has changed */
527 anim_mark_list_change();
528
529 /* Call the callback function at the end*/
530 if(a_tmp.ready_cb != NULL) a_tmp.ready_cb(&a_tmp);
531 }
532 /*If the animation is not deleted then restart it*/
533 else {
534 a->act_time = -(int32_t)(a->repeat_delay); /*Restart the animation*/
535 /*Swap the start and end values in play back mode*/
536 if(a->playback_time != 0) {
537 /*If now turning back use the 'playback_pause*/
538 if(a->playback_now == 0) a->act_time = -(int32_t)(a->playback_delay);
539
540 /*Toggle the play back state*/
541 a->playback_now = a->playback_now == 0 ? 1 : 0;
542 /*Swap the start and end values*/
543 int32_t tmp;
544 tmp = a->start;
545 a->start = a->end;
546 a->end = tmp;
547
548 a->time = a->playback_now == 0 ? a->time_orig : a->playback_time;
549 }
550 }
551
552 return anim_list_changed;
553 }
anim_mark_list_change(void)554 static void anim_mark_list_change(void)
555 {
556 anim_list_changed = true;
557 if(_lv_ll_get_head(&LV_GC_ROOT(_lv_anim_ll)) == NULL)
558 lv_task_set_prio(_lv_anim_task, LV_TASK_PRIO_OFF);
559 else
560 lv_task_set_prio(_lv_anim_task, LV_ANIM_TASK_PRIO);
561 }
562 #endif
563