acid-drop- Hacking the planet from a LilyGo T-Deck using custom firmware |
git clone git://git.acid.vegas/acid-drop.git |
Log | Files | Refs | Archive | README | LICENSE |
lv_anim.c (13310B)
1 /** 2 * @file lv_anim.c 3 * 4 */ 5 6 /********************* 7 * INCLUDES 8 *********************/ 9 #include "lv_anim.h" 10 11 #include "../hal/lv_hal_tick.h" 12 #include "lv_assert.h" 13 #include "lv_timer.h" 14 #include "lv_math.h" 15 #include "lv_mem.h" 16 #include "lv_gc.h" 17 18 /********************* 19 * DEFINES 20 *********************/ 21 #define LV_ANIM_RESOLUTION 1024 22 #define LV_ANIM_RES_SHIFT 10 23 24 /********************** 25 * TYPEDEFS 26 **********************/ 27 28 /********************** 29 * STATIC PROTOTYPES 30 **********************/ 31 static void anim_timer(lv_timer_t * param); 32 static void anim_mark_list_change(void); 33 static void anim_ready_handler(lv_anim_t * a); 34 35 /********************** 36 * STATIC VARIABLES 37 **********************/ 38 static uint32_t last_timer_run; 39 static bool anim_list_changed; 40 static bool anim_run_round; 41 static lv_timer_t * _lv_anim_tmr; 42 43 /********************** 44 * MACROS 45 **********************/ 46 #if LV_LOG_TRACE_ANIM 47 #define TRACE_ANIM(...) LV_LOG_TRACE(__VA_ARGS__) 48 #else 49 #define TRACE_ANIM(...) 50 #endif 51 52 53 /********************** 54 * GLOBAL FUNCTIONS 55 **********************/ 56 57 void _lv_anim_core_init(void) 58 { 59 _lv_ll_init(&LV_GC_ROOT(_lv_anim_ll), sizeof(lv_anim_t)); 60 _lv_anim_tmr = lv_timer_create(anim_timer, LV_DISP_DEF_REFR_PERIOD, NULL); 61 anim_mark_list_change(); /*Turn off the animation timer*/ 62 anim_list_changed = false; 63 } 64 65 void lv_anim_init(lv_anim_t * a) 66 { 67 lv_memset_00(a, sizeof(lv_anim_t)); 68 a->time = 500; 69 a->start_value = 0; 70 a->end_value = 100; 71 a->repeat_cnt = 1; 72 a->path_cb = lv_anim_path_linear; 73 a->early_apply = 1; 74 } 75 76 lv_anim_t * lv_anim_start(const lv_anim_t * a) 77 { 78 TRACE_ANIM("begin"); 79 80 /*Do not let two animations for the same 'var' with the same 'exec_cb'*/ 81 if(a->exec_cb != NULL) lv_anim_del(a->var, a->exec_cb); /*exec_cb == NULL would delete all animations of var*/ 82 83 /*If the list is empty the anim timer was suspended and it's last run measure is invalid*/ 84 if(_lv_ll_is_empty(&LV_GC_ROOT(_lv_anim_ll))) { 85 last_timer_run = lv_tick_get(); 86 } 87 88 /*Add the new animation to the animation linked list*/ 89 lv_anim_t * new_anim = _lv_ll_ins_head(&LV_GC_ROOT(_lv_anim_ll)); 90 LV_ASSERT_MALLOC(new_anim); 91 if(new_anim == NULL) return NULL; 92 93 /*Initialize the animation descriptor*/ 94 lv_memcpy(new_anim, a, sizeof(lv_anim_t)); 95 if(a->var == a) new_anim->var = new_anim; 96 new_anim->run_round = anim_run_round; 97 98 /*Set the start value*/ 99 if(new_anim->early_apply) { 100 if(new_anim->get_value_cb) { 101 int32_t v_ofs = new_anim->get_value_cb(new_anim); 102 new_anim->start_value += v_ofs; 103 new_anim->end_value += v_ofs; 104 } 105 106 if(new_anim->exec_cb && new_anim->var) new_anim->exec_cb(new_anim->var, new_anim->start_value); 107 } 108 109 /*Creating an animation changed the linked list. 110 *It's important if it happens in a ready callback. (see `anim_timer`)*/ 111 anim_mark_list_change(); 112 113 TRACE_ANIM("finished"); 114 return new_anim; 115 } 116 117 uint32_t lv_anim_get_playtime(lv_anim_t * a) 118 { 119 uint32_t playtime = LV_ANIM_PLAYTIME_INFINITE; 120 121 if(a->repeat_cnt == LV_ANIM_REPEAT_INFINITE) 122 return playtime; 123 124 playtime = a->time - a->act_time; 125 if(a->playback_now == 0) 126 playtime += a->playback_delay + a->playback_time; 127 128 if(a->repeat_cnt <= 1) 129 return playtime; 130 131 playtime += (a->repeat_delay + a->time + 132 a->playback_delay + a->playback_time) * 133 (a->repeat_cnt - 1); 134 135 return playtime; 136 } 137 138 bool lv_anim_del(void * var, lv_anim_exec_xcb_t exec_cb) 139 { 140 lv_anim_t * a; 141 lv_anim_t * a_next; 142 bool del = false; 143 a = _lv_ll_get_head(&LV_GC_ROOT(_lv_anim_ll)); 144 while(a != NULL) { 145 /*'a' might be deleted, so get the next object while 'a' is valid*/ 146 a_next = _lv_ll_get_next(&LV_GC_ROOT(_lv_anim_ll), a); 147 148 if((a->var == var || var == NULL) && (a->exec_cb == exec_cb || exec_cb == NULL)) { 149 _lv_ll_remove(&LV_GC_ROOT(_lv_anim_ll), a); 150 lv_mem_free(a); 151 anim_mark_list_change(); /*Read by `anim_timer`. It need to know if a delete occurred in 152 the linked list*/ 153 del = true; 154 } 155 156 a = a_next; 157 } 158 159 return del; 160 } 161 162 void lv_anim_del_all(void) 163 { 164 _lv_ll_clear(&LV_GC_ROOT(_lv_anim_ll)); 165 anim_mark_list_change(); 166 } 167 168 lv_anim_t * lv_anim_get(void * var, lv_anim_exec_xcb_t exec_cb) 169 { 170 lv_anim_t * a; 171 _LV_LL_READ(&LV_GC_ROOT(_lv_anim_ll), a) { 172 if(a->var == var && (a->exec_cb == exec_cb || exec_cb == NULL)) { 173 return a; 174 } 175 } 176 177 return NULL; 178 } 179 180 uint16_t lv_anim_count_running(void) 181 { 182 uint16_t cnt = 0; 183 lv_anim_t * a; 184 _LV_LL_READ(&LV_GC_ROOT(_lv_anim_ll), a) cnt++; 185 186 return cnt; 187 } 188 189 uint32_t lv_anim_speed_to_time(uint32_t speed, int32_t start, int32_t end) 190 { 191 uint32_t d = LV_ABS(start - end); 192 uint32_t time = (d * 1000) / speed; 193 194 if(time == 0) { 195 time++; 196 } 197 198 return time; 199 } 200 201 void lv_anim_refr_now(void) 202 { 203 anim_timer(NULL); 204 } 205 206 int32_t lv_anim_path_linear(const lv_anim_t * a) 207 { 208 /*Calculate the current step*/ 209 int32_t step = lv_map(a->act_time, 0, a->time, 0, LV_ANIM_RESOLUTION); 210 211 /*Get the new value which will be proportional to `step` 212 *and the `start` and `end` values*/ 213 int32_t new_value; 214 new_value = step * (a->end_value - a->start_value); 215 new_value = new_value >> LV_ANIM_RES_SHIFT; 216 new_value += a->start_value; 217 218 return new_value; 219 } 220 221 int32_t lv_anim_path_ease_in(const lv_anim_t * a) 222 { 223 /*Calculate the current step*/ 224 uint32_t t = lv_map(a->act_time, 0, a->time, 0, LV_BEZIER_VAL_MAX); 225 int32_t step = lv_bezier3(t, 0, 50, 100, LV_BEZIER_VAL_MAX); 226 227 int32_t new_value; 228 new_value = step * (a->end_value - a->start_value); 229 new_value = new_value >> LV_BEZIER_VAL_SHIFT; 230 new_value += a->start_value; 231 232 return new_value; 233 } 234 235 int32_t lv_anim_path_ease_out(const lv_anim_t * a) 236 { 237 /*Calculate the current step*/ 238 uint32_t t = lv_map(a->act_time, 0, a->time, 0, LV_BEZIER_VAL_MAX); 239 int32_t step = lv_bezier3(t, 0, 900, 950, LV_BEZIER_VAL_MAX); 240 241 int32_t new_value; 242 new_value = step * (a->end_value - a->start_value); 243 new_value = new_value >> LV_BEZIER_VAL_SHIFT; 244 new_value += a->start_value; 245 246 return new_value; 247 } 248 249 int32_t lv_anim_path_ease_in_out(const lv_anim_t * a) 250 { 251 /*Calculate the current step*/ 252 uint32_t t = lv_map(a->act_time, 0, a->time, 0, LV_BEZIER_VAL_MAX); 253 int32_t step = lv_bezier3(t, 0, 50, 952, LV_BEZIER_VAL_MAX); 254 255 int32_t new_value; 256 new_value = step * (a->end_value - a->start_value); 257 new_value = new_value >> LV_BEZIER_VAL_SHIFT; 258 new_value += a->start_value; 259 260 return new_value; 261 } 262 263 int32_t lv_anim_path_overshoot(const lv_anim_t * a) 264 { 265 /*Calculate the current step*/ 266 uint32_t t = lv_map(a->act_time, 0, a->time, 0, LV_BEZIER_VAL_MAX); 267 int32_t step = lv_bezier3(t, 0, 1000, 1300, LV_BEZIER_VAL_MAX); 268 269 int32_t new_value; 270 new_value = step * (a->end_value - a->start_value); 271 new_value = new_value >> LV_BEZIER_VAL_SHIFT; 272 new_value += a->start_value; 273 274 return new_value; 275 } 276 277 int32_t lv_anim_path_bounce(const lv_anim_t * a) 278 { 279 /*Calculate the current step*/ 280 int32_t t = lv_map(a->act_time, 0, a->time, 0, LV_BEZIER_VAL_MAX); 281 int32_t diff = (a->end_value - a->start_value); 282 283 /*3 bounces has 5 parts: 3 down and 2 up. One part is t / 5 long*/ 284 285 if(t < 408) { 286 /*Go down*/ 287 t = (t * 2500) >> LV_BEZIER_VAL_SHIFT; /*[0..1024] range*/ 288 } 289 else if(t >= 408 && t < 614) { 290 /*First bounce back*/ 291 t -= 408; 292 t = t * 5; /*to [0..1024] range*/ 293 t = LV_BEZIER_VAL_MAX - t; 294 diff = diff / 20; 295 } 296 else if(t >= 614 && t < 819) { 297 /*Fall back*/ 298 t -= 614; 299 t = t * 5; /*to [0..1024] range*/ 300 diff = diff / 20; 301 } 302 else if(t >= 819 && t < 921) { 303 /*Second bounce back*/ 304 t -= 819; 305 t = t * 10; /*to [0..1024] range*/ 306 t = LV_BEZIER_VAL_MAX - t; 307 diff = diff / 40; 308 } 309 else if(t >= 921 && t <= LV_BEZIER_VAL_MAX) { 310 /*Fall back*/ 311 t -= 921; 312 t = t * 10; /*to [0..1024] range*/ 313 diff = diff / 40; 314 } 315 316 if(t > LV_BEZIER_VAL_MAX) t = LV_BEZIER_VAL_MAX; 317 if(t < 0) t = 0; 318 int32_t step = lv_bezier3(t, LV_BEZIER_VAL_MAX, 800, 500, 0); 319 320 int32_t new_value; 321 new_value = step * diff; 322 new_value = new_value >> LV_BEZIER_VAL_SHIFT; 323 new_value = a->end_value - new_value; 324 325 return new_value; 326 } 327 328 int32_t lv_anim_path_step(const lv_anim_t * a) 329 { 330 if(a->act_time >= a->time) 331 return a->end_value; 332 else 333 return a->start_value; 334 } 335 336 /********************** 337 * STATIC FUNCTIONS 338 **********************/ 339 340 /** 341 * Periodically handle the animations. 342 * @param param unused 343 */ 344 static void anim_timer(lv_timer_t * param) 345 { 346 LV_UNUSED(param); 347 348 uint32_t elaps = lv_tick_elaps(last_timer_run); 349 350 /*Flip the run round*/ 351 anim_run_round = anim_run_round ? false : true; 352 353 lv_anim_t * a = _lv_ll_get_head(&LV_GC_ROOT(_lv_anim_ll)); 354 355 while(a != NULL) { 356 /*It can be set by `lv_anim_del()` typically in `end_cb`. If set then an animation delete 357 * happened in `anim_ready_handler` which could make this linked list reading corrupt 358 * because the list is changed meanwhile 359 */ 360 anim_list_changed = false; 361 362 if(a->run_round != anim_run_round) { 363 a->run_round = anim_run_round; /*The list readying might be reset so need to know which anim has run already*/ 364 365 /*The animation will run now for the first time. Call `start_cb`*/ 366 int32_t new_act_time = a->act_time + elaps; 367 if(!a->start_cb_called && a->act_time <= 0 && new_act_time >= 0) { 368 if(a->early_apply == 0 && a->get_value_cb) { 369 int32_t v_ofs = a->get_value_cb(a); 370 a->start_value += v_ofs; 371 a->end_value += v_ofs; 372 } 373 if(a->start_cb) a->start_cb(a); 374 a->start_cb_called = 1; 375 } 376 a->act_time += elaps; 377 if(a->act_time >= 0) { 378 if(a->act_time > a->time) a->act_time = a->time; 379 380 int32_t new_value; 381 new_value = a->path_cb(a); 382 383 if(new_value != a->current_value) { 384 a->current_value = new_value; 385 /*Apply the calculated value*/ 386 if(a->exec_cb) a->exec_cb(a->var, new_value); 387 } 388 389 /*If the time is elapsed the animation is ready*/ 390 if(a->act_time >= a->time) { 391 anim_ready_handler(a); 392 } 393 } 394 } 395 396 /*If the linked list changed due to anim. delete then it's not safe to continue 397 *the reading of the list from here -> start from the head*/ 398 if(anim_list_changed) 399 a = _lv_ll_get_head(&LV_GC_ROOT(_lv_anim_ll)); 400 else 401 a = _lv_ll_get_next(&LV_GC_ROOT(_lv_anim_ll), a); 402 } 403 404 last_timer_run = lv_tick_get(); 405 } 406 407 /** 408 * Called when an animation is ready to do the necessary thinks 409 * e.g. repeat, play back, delete etc. 410 * @param a pointer to an animation descriptor 411 */ 412 static void anim_ready_handler(lv_anim_t * a) 413 { 414 /*In the end of a forward anim decrement repeat cnt.*/ 415 if(a->playback_now == 0 && a->repeat_cnt > 0 && a->repeat_cnt != LV_ANIM_REPEAT_INFINITE) { 416 a->repeat_cnt--; 417 } 418 419 /*Delete the animation if 420 * - no repeat left and no play back (simple one shot animation) 421 * - no repeat, play back is enabled and play back is ready*/ 422 if(a->repeat_cnt == 0 && (a->playback_time == 0 || a->playback_now == 1)) { 423 424 /*Delete the animation from the list. 425 * This way the `ready_cb` will see the animations like it's animation is ready deleted*/ 426 _lv_ll_remove(&LV_GC_ROOT(_lv_anim_ll), a); 427 /*Flag that the list has changed*/ 428 anim_mark_list_change(); 429 430 /*Call the callback function at the end*/ 431 if(a->ready_cb != NULL) a->ready_cb(a); 432 lv_mem_free(a); 433 } 434 /*If the animation is not deleted then restart it*/ 435 else { 436 a->act_time = -(int32_t)(a->repeat_delay); /*Restart the animation*/ 437 /*Swap the start and end values in play back mode*/ 438 if(a->playback_time != 0) { 439 /*If now turning back use the 'playback_pause*/ 440 if(a->playback_now == 0) a->act_time = -(int32_t)(a->playback_delay); 441 442 /*Toggle the play back state*/ 443 a->playback_now = a->playback_now == 0 ? 1 : 0; 444 /*Swap the start and end values*/ 445 int32_t tmp = a->start_value; 446 a->start_value = a->end_value; 447 a->end_value = tmp; 448 /*Swap the time and playback_time*/ 449 tmp = a->time; 450 a->time = a->playback_time; 451 a->playback_time = tmp; 452 } 453 } 454 } 455 456 static void anim_mark_list_change(void) 457 { 458 anim_list_changed = true; 459 if(_lv_ll_get_head(&LV_GC_ROOT(_lv_anim_ll)) == NULL) 460 lv_timer_pause(_lv_anim_tmr); 461 else 462 lv_timer_resume(_lv_anim_tmr); 463 }