/**1* @file anim.c2*3*/45/*********************6* INCLUDES7*********************/8#include "lv_anim.h"910#if USE_LV_ANIMATION11#include <stddef.h>12#include <string.h>13#include "../lv_hal/lv_hal_tick.h"14#include "lv_task.h"15#include "lv_math.h"16#include "lv_gc.h"1718#if defined(LV_GC_INCLUDE)19# include LV_GC_INCLUDE20#endif /* LV_ENABLE_GC */212223/*********************24* DEFINES25*********************/26#define LV_ANIM_RESOLUTION 102427#define LV_ANIM_RES_SHIFT 102829/**********************30* TYPEDEFS31**********************/3233/**********************34* STATIC PROTOTYPES35**********************/36static void anim_task(void * param);37static bool anim_ready_handler(lv_anim_t * a);3839/**********************40* STATIC VARIABLES41**********************/42static uint32_t last_task_run;43static bool anim_list_changed;4445/**********************46* MACROS47**********************/4849/**********************50* GLOBAL FUNCTIONS51**********************/5253/**54* Init. the animation module55*/56void lv_anim_init(void)57{58lv_ll_init(&LV_GC_ROOT(_lv_anim_ll), sizeof(lv_anim_t));59last_task_run = lv_tick_get();60lv_task_create(anim_task, LV_REFR_PERIOD, LV_TASK_PRIO_MID, NULL);61}6263/**64* Create an animation65* @param anim_p an initialized 'anim_t' variable. Not required after call.66*/67void lv_anim_create(lv_anim_t * anim_p)68{69LV_LOG_TRACE("animation create started")70/* Do not let two animations for the same 'var' with the same 'fp'*/71if(anim_p->fp != NULL) lv_anim_del(anim_p->var, anim_p->fp); /*fp == NULL would delete all animations of var*/7273/*Add the new animation to the animation linked list*/74lv_anim_t * new_anim = lv_ll_ins_head(&LV_GC_ROOT(_lv_anim_ll));75lv_mem_assert(new_anim);76if(new_anim == NULL) return;7778/*Initialize the animation descriptor*/79anim_p->playback_now = 0;80memcpy(new_anim, anim_p, sizeof(lv_anim_t));8182/*Set the start value*/83if(new_anim->fp != NULL) new_anim->fp(new_anim->var, new_anim->start);8485/* Creating an animation changed the linked list.86* It's important if it happens in a ready callback. (see `anim_task`)*/87anim_list_changed = true;8889LV_LOG_TRACE("animation created")90}9192/**93* Delete an animation for a variable with a given animator function94* @param var pointer to variable95* @param fp a function pointer which is animating 'var',96* or NULL to delete all animations of 'var'97* @return true: at least 1 animation is deleted, false: no animation is deleted98*/99bool lv_anim_del(void * var, lv_anim_fp_t fp)100{101lv_anim_t * a;102lv_anim_t * a_next;103bool del = false;104a = lv_ll_get_head(&LV_GC_ROOT(_lv_anim_ll));105while(a != NULL) {106/*'a' might be deleted, so get the next object while 'a' is valid*/107a_next = lv_ll_get_next(&LV_GC_ROOT(_lv_anim_ll), a);108109if(a->var == var && (a->fp == fp || fp == NULL)) {110lv_ll_rem(&LV_GC_ROOT(_lv_anim_ll), a);111lv_mem_free(a);112anim_list_changed = true; /*Read by `anim_task`. It need to know if a delete occurred in the linked list*/113del = true;114}115116a = a_next;117}118119return del;120}121122/**123* Get the number of currently running animations124* @return the number of running animations125*/126uint16_t lv_anim_count_running(void)127{128uint16_t cnt = 0;129lv_anim_t * a;130LL_READ(LV_GC_ROOT(_lv_anim_ll), a) cnt++;131132return cnt++;133}134135/**136* Calculate the time of an animation with a given speed and the start and end values137* @param speed speed of animation in unit/sec138* @param start start value of the animation139* @param end end value of the animation140* @return the required time [ms] for the animation with the given parameters141*/142uint16_t lv_anim_speed_to_time(uint16_t speed, int32_t start, int32_t end)143{144int32_t d = LV_MATH_ABS((int32_t) start - end);145uint32_t time = (int32_t)((int32_t)(d * 1000) / speed);146147if(time > UINT16_MAX) time = UINT16_MAX;148149if(time == 0) {150time++;151}152153return time;154}155156/**157* Calculate the current value of an animation applying linear characteristic158* @param a pointer to an animation159* @return the current value to set160*/161int32_t lv_anim_path_linear(const lv_anim_t * a)162{163/*Calculate the current step*/164uint16_t step;165if(a->time == a->act_time) step = LV_ANIM_RESOLUTION; /*Use the last value if the time fully elapsed*/166else step = (a->act_time * LV_ANIM_RESOLUTION) / a->time;167168/* Get the new value which will be proportional to `step`169* and the `start` and `end` values*/170int32_t new_value;171new_value = (int32_t) step * (a->end - a->start);172new_value = new_value >> LV_ANIM_RES_SHIFT;173new_value += a->start;174175return new_value;176}177178/**179* Calculate the current value of an animation slowing down the start phase180* @param a pointer to an animation181* @return the current value to set182*/183int32_t lv_anim_path_ease_in(const lv_anim_t * a)184{185/*Calculate the current step*/186uint32_t t;187if(a->time == a->act_time) t = 1024;188else t = (uint32_t)((uint32_t)a->act_time * 1024) / a->time;189190int32_t step = lv_bezier3(t, 0, 1, 1, 1024);191192int32_t new_value;193new_value = (int32_t) step * (a->end - a->start);194new_value = new_value >> 10;195new_value += a->start;196197198return new_value;199}200201/**202* Calculate the current value of an animation slowing down the end phase203* @param a pointer to an animation204* @return the current value to set205*/206int32_t lv_anim_path_ease_out(const lv_anim_t * a)207{208/*Calculate the current step*/209210uint32_t t;211if(a->time == a->act_time) t = 1024;212else t = (uint32_t)((uint32_t)a->act_time * 1024) / a->time;213214int32_t step = lv_bezier3(t, 0, 1023, 1023, 1024);215216int32_t new_value;217new_value = (int32_t) step * (a->end - a->start);218new_value = new_value >> 10;219new_value += a->start;220221222return new_value;223}224225/**226* Calculate the current value of an animation applying an "S" characteristic (cosine)227* @param a pointer to an animation228* @return the current value to set229*/230int32_t lv_anim_path_ease_in_out(const lv_anim_t * a)231{232/*Calculate the current step*/233234uint32_t t;235if(a->time == a->act_time) t = 1024;236else t = (uint32_t)((uint32_t)a->act_time * 1024) / a->time;237238int32_t step = lv_bezier3(t, 0, 100, 924, 1024);239240int32_t new_value;241new_value = (int32_t) step * (a->end - a->start);242new_value = new_value >> 10;243new_value += a->start;244245246return new_value;247}248249/**250* Calculate the current value of an animation with overshoot at the end251* @param a pointer to an animation252* @return the current value to set253*/254int32_t lv_anim_path_overshoot(const lv_anim_t * a)255{256/*Calculate the current step*/257258uint32_t t;259if(a->time == a->act_time) t = 1024;260else t = (uint32_t)((uint32_t)a->act_time * 1024) / a->time;261262int32_t step = lv_bezier3(t, 0, 600, 1300, 1024);263264int32_t new_value;265new_value = (int32_t) step * (a->end - a->start);266new_value = new_value >> 10;267new_value += a->start;268269270return new_value;271}272273/**274* Calculate the current value of an animation with 3 bounces275* @param a pointer to an animation276* @return the current value to set277*/278int32_t lv_anim_path_bounce(const lv_anim_t * a)279{280/*Calculate the current step*/281uint32_t t;282if(a->time == a->act_time) t = 1024;283else t = (uint32_t)((uint32_t)a->act_time * 1024) / a->time;284285int32_t diff = (a->end - a->start);286287/*3 bounces has 5 parts: 3 down and 2 up. One part is t / 5 long*/288289if(t < 408){290/*Go down*/291t = (t * 2500) >> 10; /*[0..1024] range*/292}293else if(t >= 408 && t < 614) {294/*First bounce back*/295t -= 408;296t = t * 5; /*to [0..1024] range*/297t = 1024 - t;298diff = diff / 6;299}300else if(t >= 614 && t < 819) {301/*Fall back*/302t -= 614;303t = t * 5; /*to [0..1024] range*/304diff = diff / 6;305}306else if(t >= 819 && t < 921) {307/*Second bounce back*/308t -= 819;309t = t * 10; /*to [0..1024] range*/310t = 1024 - t;311diff = diff / 16;312}313else if(t >= 921 && t <= 1024) {314/*Fall back*/315t -= 921;316t = t * 10; /*to [0..1024] range*/317diff = diff / 16;318}319320if(t > 1024) t = 1024;321322int32_t step = lv_bezier3(t, 1024, 1024, 800, 0);323324int32_t new_value;325326new_value = (int32_t) step * diff;327new_value = new_value >> 10;328new_value = a->end - new_value;329330331return new_value;332}333334/**335* Calculate the current value of an animation applying step characteristic.336* (Set end value on the end of the animation)337* @param a pointer to an animation338* @return the current value to set339*/340int32_t lv_anim_path_step(const lv_anim_t * a)341{342if(a->act_time >= a->time) return a->end;343else return a->start;344}345346/**********************347* STATIC FUNCTIONS348**********************/349350/**351* Periodically handle the animations.352* @param param unused353*/354static void anim_task(void * param)355{356(void)param;357358lv_anim_t * a;359LL_READ(LV_GC_ROOT(_lv_anim_ll), a) {360a->has_run = 0;361}362363uint32_t elaps = lv_tick_elaps(last_task_run);364a = lv_ll_get_head(&LV_GC_ROOT(_lv_anim_ll));365366while(a != NULL) {367/*It can be set by `lv_anim_del()` typically in `end_cb`. If set then an animation delete happened in `anim_ready_handler`368* which could make this linked list reading corrupt because the list is changed meanwhile369*/370anim_list_changed = false;371372if(!a->has_run) {373a->has_run = 1; /*The list readying might be reseted so need to know which anim has run already*/374a->act_time += elaps;375if(a->act_time >= 0) {376if(a->act_time > a->time) a->act_time = a->time;377378int32_t new_value;379new_value = a->path(a);380381if(a->fp != NULL) a->fp(a->var, new_value); /*Apply the calculated value*/382383/*If the time is elapsed the animation is ready*/384if(a->act_time >= a->time) {385anim_ready_handler(a);386}387}388}389390/* If the linked list changed due to anim. delete then it's not safe to continue391* the reading of the list from here -> start from the head*/392if(anim_list_changed) a = lv_ll_get_head(&LV_GC_ROOT(_lv_anim_ll));393else a = lv_ll_get_next(&LV_GC_ROOT(_lv_anim_ll), a);394}395396last_task_run = lv_tick_get();397}398399/**400* Called when an animation is ready to do the necessary thinks401* e.g. repeat, play back, delete etc.402* @param a pointer to an animation descriptor403* @return true: animation delete occurred nnd the `LV_GC_ROOT(_lv_anim_ll)` has changed404* */405static bool anim_ready_handler(lv_anim_t * a)406{407408/*Delete the animation if409* - no repeat and no play back (simple one shot animation)410* - no repeat, play back is enabled and play back is ready */411if((a->repeat == 0 && a->playback == 0) ||412(a->repeat == 0 && a->playback == 1 && a->playback_now == 1)) {413void (*cb)(void *) = a->end_cb;414void * p = a->var;415lv_ll_rem(&LV_GC_ROOT(_lv_anim_ll), a);416lv_mem_free(a);417anim_list_changed = true;418419/* Call the callback function at the end*/420/* Check if an animation is deleted in the cb function421* if yes then the caller function has to know this*/422if(cb != NULL) cb(p);423}424/*If the animation is not deleted then restart it*/425else {426a->act_time = - a->repeat_pause; /*Restart the animation*/427/*Swap the start and end values in play back mode*/428if(a->playback != 0) {429/*If now turning back use the 'playback_pause*/430if(a->playback_now == 0) a->act_time = - a->playback_pause;431432/*Toggle the play back state*/433a->playback_now = a->playback_now == 0 ? 1 : 0;434/*Swap the start and end values*/435int32_t tmp;436tmp = a->start;437a->start = a->end;438a->end = tmp;439}440}441442return anim_list_changed;443}444#endif445446447