From dd27c3530432ea0b09f01e604bf577f31d8ef841 Mon Sep 17 00:00:00 2001 From: jacqueline Date: Thu, 1 Jun 2023 15:41:47 +1000 Subject: convert lvgl from submodule to a plain old directory --- lib/lvgl | 1 - lib/lvgl/src/misc/lv_anim.c | 470 +++++++++++++ lib/lvgl/src/misc/lv_anim.h | 484 +++++++++++++ lib/lvgl/src/misc/lv_anim_timeline.c | 198 ++++++ lib/lvgl/src/misc/lv_anim_timeline.h | 103 +++ lib/lvgl/src/misc/lv_area.c | 535 +++++++++++++++ lib/lvgl/src/misc/lv_area.h | 295 ++++++++ lib/lvgl/src/misc/lv_assert.h | 79 +++ lib/lvgl/src/misc/lv_async.c | 105 +++ lib/lvgl/src/misc/lv_async.h | 61 ++ lib/lvgl/src/misc/lv_bidi.c | 682 +++++++++++++++++++ lib/lvgl/src/misc/lv_bidi.h | 141 ++++ lib/lvgl/src/misc/lv_color.c | 369 ++++++++++ lib/lvgl/src/misc/lv_color.h | 708 +++++++++++++++++++ lib/lvgl/src/misc/lv_fs.c | 518 ++++++++++++++ lib/lvgl/src/misc/lv_fs.h | 262 +++++++ lib/lvgl/src/misc/lv_gc.c | 47 ++ lib/lvgl/src/misc/lv_gc.h | 97 +++ lib/lvgl/src/misc/lv_ll.c | 408 +++++++++++ lib/lvgl/src/misc/lv_ll.h | 167 +++++ lib/lvgl/src/misc/lv_log.c | 144 ++++ lib/lvgl/src/misc/lv_log.h | 154 +++++ lib/lvgl/src/misc/lv_lru.c | 349 ++++++++++ lib/lvgl/src/misc/lv_lru.h | 87 +++ lib/lvgl/src/misc/lv_math.c | 273 ++++++++ lib/lvgl/src/misc/lv_math.h | 143 ++++ lib/lvgl/src/misc/lv_mem.c | 566 +++++++++++++++ lib/lvgl/src/misc/lv_mem.h | 243 +++++++ lib/lvgl/src/misc/lv_misc.mk | 26 + lib/lvgl/src/misc/lv_printf.c | 879 ++++++++++++++++++++++++ lib/lvgl/src/misc/lv_printf.h | 92 +++ lib/lvgl/src/misc/lv_style.c | 485 +++++++++++++ lib/lvgl/src/misc/lv_style.h | 597 ++++++++++++++++ lib/lvgl/src/misc/lv_style_gen.c | 673 ++++++++++++++++++ lib/lvgl/src/misc/lv_style_gen.h | 504 ++++++++++++++ lib/lvgl/src/misc/lv_templ.c | 40 ++ lib/lvgl/src/misc/lv_templ.h | 37 + lib/lvgl/src/misc/lv_timer.c | 341 ++++++++++ lib/lvgl/src/misc/lv_timer.h | 183 +++++ lib/lvgl/src/misc/lv_tlsf.c | 1246 ++++++++++++++++++++++++++++++++++ lib/lvgl/src/misc/lv_tlsf.h | 95 +++ lib/lvgl/src/misc/lv_txt.c | 864 +++++++++++++++++++++++ lib/lvgl/src/misc/lv_txt.h | 264 +++++++ lib/lvgl/src/misc/lv_txt_ap.c | 301 ++++++++ lib/lvgl/src/misc/lv_txt_ap.h | 49 ++ lib/lvgl/src/misc/lv_types.h | 94 +++ lib/lvgl/src/misc/lv_utils.c | 79 +++ lib/lvgl/src/misc/lv_utils.h | 58 ++ 48 files changed, 14595 insertions(+), 1 deletion(-) delete mode 160000 lib/lvgl create mode 100644 lib/lvgl/src/misc/lv_anim.c create mode 100644 lib/lvgl/src/misc/lv_anim.h create mode 100644 lib/lvgl/src/misc/lv_anim_timeline.c create mode 100644 lib/lvgl/src/misc/lv_anim_timeline.h create mode 100644 lib/lvgl/src/misc/lv_area.c create mode 100644 lib/lvgl/src/misc/lv_area.h create mode 100644 lib/lvgl/src/misc/lv_assert.h create mode 100644 lib/lvgl/src/misc/lv_async.c create mode 100644 lib/lvgl/src/misc/lv_async.h create mode 100644 lib/lvgl/src/misc/lv_bidi.c create mode 100644 lib/lvgl/src/misc/lv_bidi.h create mode 100644 lib/lvgl/src/misc/lv_color.c create mode 100644 lib/lvgl/src/misc/lv_color.h create mode 100644 lib/lvgl/src/misc/lv_fs.c create mode 100644 lib/lvgl/src/misc/lv_fs.h create mode 100644 lib/lvgl/src/misc/lv_gc.c create mode 100644 lib/lvgl/src/misc/lv_gc.h create mode 100644 lib/lvgl/src/misc/lv_ll.c create mode 100644 lib/lvgl/src/misc/lv_ll.h create mode 100644 lib/lvgl/src/misc/lv_log.c create mode 100644 lib/lvgl/src/misc/lv_log.h create mode 100644 lib/lvgl/src/misc/lv_lru.c create mode 100644 lib/lvgl/src/misc/lv_lru.h create mode 100644 lib/lvgl/src/misc/lv_math.c create mode 100644 lib/lvgl/src/misc/lv_math.h create mode 100644 lib/lvgl/src/misc/lv_mem.c create mode 100644 lib/lvgl/src/misc/lv_mem.h create mode 100644 lib/lvgl/src/misc/lv_misc.mk create mode 100644 lib/lvgl/src/misc/lv_printf.c create mode 100644 lib/lvgl/src/misc/lv_printf.h create mode 100644 lib/lvgl/src/misc/lv_style.c create mode 100644 lib/lvgl/src/misc/lv_style.h create mode 100644 lib/lvgl/src/misc/lv_style_gen.c create mode 100644 lib/lvgl/src/misc/lv_style_gen.h create mode 100644 lib/lvgl/src/misc/lv_templ.c create mode 100644 lib/lvgl/src/misc/lv_templ.h create mode 100644 lib/lvgl/src/misc/lv_timer.c create mode 100644 lib/lvgl/src/misc/lv_timer.h create mode 100644 lib/lvgl/src/misc/lv_tlsf.c create mode 100644 lib/lvgl/src/misc/lv_tlsf.h create mode 100644 lib/lvgl/src/misc/lv_txt.c create mode 100644 lib/lvgl/src/misc/lv_txt.h create mode 100644 lib/lvgl/src/misc/lv_txt_ap.c create mode 100644 lib/lvgl/src/misc/lv_txt_ap.h create mode 100644 lib/lvgl/src/misc/lv_types.h create mode 100644 lib/lvgl/src/misc/lv_utils.c create mode 100644 lib/lvgl/src/misc/lv_utils.h (limited to 'lib/lvgl/src/misc') diff --git a/lib/lvgl b/lib/lvgl deleted file mode 160000 index 0732400e..00000000 --- a/lib/lvgl +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 0732400e7b564dd0e7dc4a924619d8e19c5b23a0 diff --git a/lib/lvgl/src/misc/lv_anim.c b/lib/lvgl/src/misc/lv_anim.c new file mode 100644 index 00000000..4e4253a6 --- /dev/null +++ b/lib/lvgl/src/misc/lv_anim.c @@ -0,0 +1,470 @@ +/** + * @file lv_anim.c + * + */ + +/********************* + * INCLUDES + *********************/ +#include "lv_anim.h" + +#include "../hal/lv_hal_tick.h" +#include "lv_assert.h" +#include "lv_timer.h" +#include "lv_math.h" +#include "lv_mem.h" +#include "lv_gc.h" + +/********************* + * DEFINES + *********************/ +#define LV_ANIM_RESOLUTION 1024 +#define LV_ANIM_RES_SHIFT 10 + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ +static void anim_timer(lv_timer_t * param); +static void anim_mark_list_change(void); +static void anim_ready_handler(lv_anim_t * a); + +/********************** + * STATIC VARIABLES + **********************/ +static uint32_t last_timer_run; +static bool anim_list_changed; +static bool anim_run_round; +static lv_timer_t * _lv_anim_tmr; + +/********************** + * MACROS + **********************/ +#if LV_LOG_TRACE_ANIM + #define TRACE_ANIM(...) LV_LOG_TRACE(__VA_ARGS__) +#else + #define TRACE_ANIM(...) +#endif + + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +void _lv_anim_core_init(void) +{ + _lv_ll_init(&LV_GC_ROOT(_lv_anim_ll), sizeof(lv_anim_t)); + _lv_anim_tmr = lv_timer_create(anim_timer, LV_DISP_DEF_REFR_PERIOD, NULL); + anim_mark_list_change(); /*Turn off the animation timer*/ + anim_list_changed = false; +} + +void lv_anim_init(lv_anim_t * a) +{ + lv_memset_00(a, sizeof(lv_anim_t)); + a->time = 500; + a->start_value = 0; + a->end_value = 100; + a->repeat_cnt = 1; + a->path_cb = lv_anim_path_linear; + a->early_apply = 1; +} + +lv_anim_t * lv_anim_start(const lv_anim_t * a) +{ + TRACE_ANIM("begin"); + + /*Do not let two animations for the same 'var' with the same 'exec_cb'*/ + if(a->exec_cb != NULL) lv_anim_del(a->var, a->exec_cb); /*exec_cb == NULL would delete all animations of var*/ + + /*If the list is empty the anim timer was suspended and it's last run measure is invalid*/ + if(_lv_ll_is_empty(&LV_GC_ROOT(_lv_anim_ll))) { + last_timer_run = lv_tick_get(); + } + + /*Add the new animation to the animation linked list*/ + lv_anim_t * new_anim = _lv_ll_ins_head(&LV_GC_ROOT(_lv_anim_ll)); + LV_ASSERT_MALLOC(new_anim); + if(new_anim == NULL) return NULL; + + /*Initialize the animation descriptor*/ + lv_memcpy(new_anim, a, sizeof(lv_anim_t)); + if(a->var == a) new_anim->var = new_anim; + new_anim->run_round = anim_run_round; + + /*Set the start value*/ + if(new_anim->early_apply) { + if(new_anim->get_value_cb) { + int32_t v_ofs = new_anim->get_value_cb(new_anim); + new_anim->start_value += v_ofs; + new_anim->end_value += v_ofs; + } + + if(new_anim->exec_cb && new_anim->var) new_anim->exec_cb(new_anim->var, new_anim->start_value); + } + + /*Creating an animation changed the linked list. + *It's important if it happens in a ready callback. (see `anim_timer`)*/ + anim_mark_list_change(); + + TRACE_ANIM("finished"); + return new_anim; +} + +uint32_t lv_anim_get_playtime(lv_anim_t * a) +{ + uint32_t playtime = LV_ANIM_PLAYTIME_INFINITE; + + if(a->repeat_cnt == LV_ANIM_REPEAT_INFINITE) + return playtime; + + playtime = a->time - a->act_time; + if(a->playback_now == 0) + playtime += a->playback_delay + a->playback_time; + + if(a->repeat_cnt <= 1) + return playtime; + + playtime += (a->repeat_delay + a->time + + a->playback_delay + a->playback_time) * + (a->repeat_cnt - 1); + + return playtime; +} + +bool lv_anim_del(void * var, lv_anim_exec_xcb_t exec_cb) +{ + lv_anim_t * a; + lv_anim_t * a_next; + bool del = false; + a = _lv_ll_get_head(&LV_GC_ROOT(_lv_anim_ll)); + while(a != NULL) { + /*'a' might be deleted, so get the next object while 'a' is valid*/ + a_next = _lv_ll_get_next(&LV_GC_ROOT(_lv_anim_ll), a); + + if((a->var == var || var == NULL) && (a->exec_cb == exec_cb || exec_cb == NULL)) { + _lv_ll_remove(&LV_GC_ROOT(_lv_anim_ll), a); + if(a->deleted_cb != NULL) a->deleted_cb(a); + lv_mem_free(a); + anim_mark_list_change(); /*Read by `anim_timer`. It need to know if a delete occurred in + the linked list*/ + del = true; + } + + a = a_next; + } + + return del; +} + +void lv_anim_del_all(void) +{ + _lv_ll_clear(&LV_GC_ROOT(_lv_anim_ll)); + anim_mark_list_change(); +} + +lv_anim_t * lv_anim_get(void * var, lv_anim_exec_xcb_t exec_cb) +{ + lv_anim_t * a; + _LV_LL_READ(&LV_GC_ROOT(_lv_anim_ll), a) { + if(a->var == var && (a->exec_cb == exec_cb || exec_cb == NULL)) { + return a; + } + } + + return NULL; +} + +struct _lv_timer_t * lv_anim_get_timer(void) +{ + return _lv_anim_tmr; +} + +uint16_t lv_anim_count_running(void) +{ + uint16_t cnt = 0; + lv_anim_t * a; + _LV_LL_READ(&LV_GC_ROOT(_lv_anim_ll), a) cnt++; + + return cnt; +} + +uint32_t lv_anim_speed_to_time(uint32_t speed, int32_t start, int32_t end) +{ + uint32_t d = LV_ABS(start - end); + uint32_t time = (d * 1000) / speed; + + if(time == 0) { + time++; + } + + return time; +} + +void lv_anim_refr_now(void) +{ + anim_timer(NULL); +} + +int32_t lv_anim_path_linear(const lv_anim_t * a) +{ + /*Calculate the current step*/ + int32_t step = lv_map(a->act_time, 0, a->time, 0, LV_ANIM_RESOLUTION); + + /*Get the new value which will be proportional to `step` + *and the `start` and `end` values*/ + int32_t new_value; + new_value = step * (a->end_value - a->start_value); + new_value = new_value >> LV_ANIM_RES_SHIFT; + new_value += a->start_value; + + return new_value; +} + +int32_t lv_anim_path_ease_in(const lv_anim_t * a) +{ + /*Calculate the current step*/ + uint32_t t = lv_map(a->act_time, 0, a->time, 0, LV_BEZIER_VAL_MAX); + int32_t step = lv_bezier3(t, 0, 50, 100, LV_BEZIER_VAL_MAX); + + int32_t new_value; + new_value = step * (a->end_value - a->start_value); + new_value = new_value >> LV_BEZIER_VAL_SHIFT; + new_value += a->start_value; + + return new_value; +} + +int32_t lv_anim_path_ease_out(const lv_anim_t * a) +{ + /*Calculate the current step*/ + uint32_t t = lv_map(a->act_time, 0, a->time, 0, LV_BEZIER_VAL_MAX); + int32_t step = lv_bezier3(t, 0, 900, 950, LV_BEZIER_VAL_MAX); + + int32_t new_value; + new_value = step * (a->end_value - a->start_value); + new_value = new_value >> LV_BEZIER_VAL_SHIFT; + new_value += a->start_value; + + return new_value; +} + +int32_t lv_anim_path_ease_in_out(const lv_anim_t * a) +{ + /*Calculate the current step*/ + uint32_t t = lv_map(a->act_time, 0, a->time, 0, LV_BEZIER_VAL_MAX); + int32_t step = lv_bezier3(t, 0, 50, 952, LV_BEZIER_VAL_MAX); + + int32_t new_value; + new_value = step * (a->end_value - a->start_value); + new_value = new_value >> LV_BEZIER_VAL_SHIFT; + new_value += a->start_value; + + return new_value; +} + +int32_t lv_anim_path_overshoot(const lv_anim_t * a) +{ + /*Calculate the current step*/ + uint32_t t = lv_map(a->act_time, 0, a->time, 0, LV_BEZIER_VAL_MAX); + int32_t step = lv_bezier3(t, 0, 1000, 1300, LV_BEZIER_VAL_MAX); + + int32_t new_value; + new_value = step * (a->end_value - a->start_value); + new_value = new_value >> LV_BEZIER_VAL_SHIFT; + new_value += a->start_value; + + return new_value; +} + +int32_t lv_anim_path_bounce(const lv_anim_t * a) +{ + /*Calculate the current step*/ + int32_t t = lv_map(a->act_time, 0, a->time, 0, LV_BEZIER_VAL_MAX); + int32_t diff = (a->end_value - a->start_value); + + /*3 bounces has 5 parts: 3 down and 2 up. One part is t / 5 long*/ + + if(t < 408) { + /*Go down*/ + t = (t * 2500) >> LV_BEZIER_VAL_SHIFT; /*[0..1024] range*/ + } + else if(t >= 408 && t < 614) { + /*First bounce back*/ + t -= 408; + t = t * 5; /*to [0..1024] range*/ + t = LV_BEZIER_VAL_MAX - t; + diff = diff / 20; + } + else if(t >= 614 && t < 819) { + /*Fall back*/ + t -= 614; + t = t * 5; /*to [0..1024] range*/ + diff = diff / 20; + } + else if(t >= 819 && t < 921) { + /*Second bounce back*/ + t -= 819; + t = t * 10; /*to [0..1024] range*/ + t = LV_BEZIER_VAL_MAX - t; + diff = diff / 40; + } + else if(t >= 921 && t <= LV_BEZIER_VAL_MAX) { + /*Fall back*/ + t -= 921; + t = t * 10; /*to [0..1024] range*/ + diff = diff / 40; + } + + if(t > LV_BEZIER_VAL_MAX) t = LV_BEZIER_VAL_MAX; + if(t < 0) t = 0; + int32_t step = lv_bezier3(t, LV_BEZIER_VAL_MAX, 800, 500, 0); + + int32_t new_value; + new_value = step * diff; + new_value = new_value >> LV_BEZIER_VAL_SHIFT; + new_value = a->end_value - new_value; + + return new_value; +} + +int32_t lv_anim_path_step(const lv_anim_t * a) +{ + if(a->act_time >= a->time) + return a->end_value; + else + return a->start_value; +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +/** + * Periodically handle the animations. + * @param param unused + */ +static void anim_timer(lv_timer_t * param) +{ + LV_UNUSED(param); + + uint32_t elaps = lv_tick_elaps(last_timer_run); + + /*Flip the run round*/ + anim_run_round = anim_run_round ? false : true; + + lv_anim_t * a = _lv_ll_get_head(&LV_GC_ROOT(_lv_anim_ll)); + + while(a != NULL) { + /*It can be set by `lv_anim_del()` typically in `end_cb`. If set then an animation delete + * happened in `anim_ready_handler` which could make this linked list reading corrupt + * because the list is changed meanwhile + */ + anim_list_changed = false; + + if(a->run_round != anim_run_round) { + a->run_round = anim_run_round; /*The list readying might be reset so need to know which anim has run already*/ + + /*The animation will run now for the first time. Call `start_cb`*/ + int32_t new_act_time = a->act_time + elaps; + if(!a->start_cb_called && a->act_time <= 0 && new_act_time >= 0) { + if(a->early_apply == 0 && a->get_value_cb) { + int32_t v_ofs = a->get_value_cb(a); + a->start_value += v_ofs; + a->end_value += v_ofs; + } + if(a->start_cb) a->start_cb(a); + a->start_cb_called = 1; + } + a->act_time += elaps; + if(a->act_time >= 0) { + if(a->act_time > a->time) a->act_time = a->time; + + int32_t new_value; + new_value = a->path_cb(a); + + if(new_value != a->current_value) { + a->current_value = new_value; + /*Apply the calculated value*/ + if(a->exec_cb) a->exec_cb(a->var, new_value); + } + + /*If the time is elapsed the animation is ready*/ + if(a->act_time >= a->time) { + anim_ready_handler(a); + } + } + } + + /*If the linked list changed due to anim. delete then it's not safe to continue + *the reading of the list from here -> start from the head*/ + if(anim_list_changed) + a = _lv_ll_get_head(&LV_GC_ROOT(_lv_anim_ll)); + else + a = _lv_ll_get_next(&LV_GC_ROOT(_lv_anim_ll), a); + } + + last_timer_run = lv_tick_get(); +} + +/** + * Called when an animation is ready to do the necessary thinks + * e.g. repeat, play back, delete etc. + * @param a pointer to an animation descriptor + */ +static void anim_ready_handler(lv_anim_t * a) +{ + /*In the end of a forward anim decrement repeat cnt.*/ + if(a->playback_now == 0 && a->repeat_cnt > 0 && a->repeat_cnt != LV_ANIM_REPEAT_INFINITE) { + a->repeat_cnt--; + } + + /*Delete the animation if + * - no repeat left and no play back (simple one shot animation) + * - no repeat, play back is enabled and play back is ready*/ + if(a->repeat_cnt == 0 && (a->playback_time == 0 || a->playback_now == 1)) { + + /*Delete the animation from the list. + * This way the `ready_cb` will see the animations like it's animation is ready deleted*/ + _lv_ll_remove(&LV_GC_ROOT(_lv_anim_ll), a); + /*Flag that the list has changed*/ + anim_mark_list_change(); + + /*Call the callback function at the end*/ + if(a->ready_cb != NULL) a->ready_cb(a); + if(a->deleted_cb != NULL) a->deleted_cb(a); + lv_mem_free(a); + } + /*If the animation is not deleted then restart it*/ + else { + a->act_time = -(int32_t)(a->repeat_delay); /*Restart the animation*/ + /*Swap the start and end values in play back mode*/ + if(a->playback_time != 0) { + /*If now turning back use the 'playback_pause*/ + if(a->playback_now == 0) a->act_time = -(int32_t)(a->playback_delay); + + /*Toggle the play back state*/ + a->playback_now = a->playback_now == 0 ? 1 : 0; + /*Swap the start and end values*/ + int32_t tmp = a->start_value; + a->start_value = a->end_value; + a->end_value = tmp; + /*Swap the time and playback_time*/ + tmp = a->time; + a->time = a->playback_time; + a->playback_time = tmp; + } + } +} + +static void anim_mark_list_change(void) +{ + anim_list_changed = true; + if(_lv_ll_get_head(&LV_GC_ROOT(_lv_anim_ll)) == NULL) + lv_timer_pause(_lv_anim_tmr); + else + lv_timer_resume(_lv_anim_tmr); +} diff --git a/lib/lvgl/src/misc/lv_anim.h b/lib/lvgl/src/misc/lv_anim.h new file mode 100644 index 00000000..faef7278 --- /dev/null +++ b/lib/lvgl/src/misc/lv_anim.h @@ -0,0 +1,484 @@ +/** + * @file lv_anim.h + * + */ + +#ifndef LV_ANIM_H +#define LV_ANIM_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ +#include "../lv_conf_internal.h" + +#include +#include +#include + +/********************* + * DEFINES + *********************/ + +#define LV_ANIM_REPEAT_INFINITE 0xFFFF +#define LV_ANIM_PLAYTIME_INFINITE 0xFFFFFFFF + +LV_EXPORT_CONST_INT(LV_ANIM_REPEAT_INFINITE); +LV_EXPORT_CONST_INT(LV_ANIM_PLAYTIME_INFINITE); + +/********************** + * TYPEDEFS + **********************/ + +/** Can be used to indicate if animations are enabled or disabled in a case*/ +typedef enum { + LV_ANIM_OFF, + LV_ANIM_ON, +} lv_anim_enable_t; + +struct _lv_anim_t; +struct _lv_timer_t; + +/** Get the current value during an animation*/ +typedef int32_t (*lv_anim_path_cb_t)(const struct _lv_anim_t *); + +/** Generic prototype of "animator" functions. + * First parameter is the variable to animate. + * Second parameter is the value to set. + * Compatible with `lv_xxx_set_yyy(obj, value)` functions + * The `x` in `_xcb_t` means it's not a fully generic prototype because + * it doesn't receive `lv_anim_t *` as its first argument*/ +typedef void (*lv_anim_exec_xcb_t)(void *, int32_t); + +/** Same as `lv_anim_exec_xcb_t` but receives `lv_anim_t *` as the first parameter. + * It's more consistent but less convenient. Might be used by binding generator functions.*/ +typedef void (*lv_anim_custom_exec_cb_t)(struct _lv_anim_t *, int32_t); + +/** Callback to call when the animation is ready*/ +typedef void (*lv_anim_ready_cb_t)(struct _lv_anim_t *); + +/** Callback to call when the animation really stars (considering `delay`)*/ +typedef void (*lv_anim_start_cb_t)(struct _lv_anim_t *); + +/** Callback used when the animation values are relative to get the current value*/ +typedef int32_t (*lv_anim_get_value_cb_t)(struct _lv_anim_t *); + +/** Callback used when the animation is deleted*/ +typedef void (*lv_anim_deleted_cb_t)(struct _lv_anim_t *); + +/** Describes an animation*/ +typedef struct _lv_anim_t { + void * var; /**var = var; +} + +/** + * Set a function to animate `var` + * @param a pointer to an initialized `lv_anim_t` variable + * @param exec_cb a function to execute during animation + * LVGL's built-in functions can be used. + * E.g. lv_obj_set_x + */ +static inline void lv_anim_set_exec_cb(lv_anim_t * a, lv_anim_exec_xcb_t exec_cb) +{ + a->exec_cb = exec_cb; +} + +/** + * Set the duration of an animation + * @param a pointer to an initialized `lv_anim_t` variable + * @param duration duration of the animation in milliseconds + */ +static inline void lv_anim_set_time(lv_anim_t * a, uint32_t duration) +{ + a->time = duration; +} + +/** + * Set a delay before starting the animation + * @param a pointer to an initialized `lv_anim_t` variable + * @param delay delay before the animation in milliseconds + */ +static inline void lv_anim_set_delay(lv_anim_t * a, uint32_t delay) +{ + a->act_time = -(int32_t)(delay); +} + +/** + * Set the start and end values of an animation + * @param a pointer to an initialized `lv_anim_t` variable + * @param start the start value + * @param end the end value + */ +static inline void lv_anim_set_values(lv_anim_t * a, int32_t start, int32_t end) +{ + a->start_value = start; + a->current_value = start; + a->end_value = end; +} + +/** + * Similar to `lv_anim_set_exec_cb` but `lv_anim_custom_exec_cb_t` receives + * `lv_anim_t * ` as its first parameter instead of `void *`. + * This function might be used when LVGL is bound to other languages because + * it's more consistent to have `lv_anim_t *` as first parameter. + * The variable to animate can be stored in the animation's `user_data` + * @param a pointer to an initialized `lv_anim_t` variable + * @param exec_cb a function to execute. + */ +static inline void lv_anim_set_custom_exec_cb(lv_anim_t * a, lv_anim_custom_exec_cb_t exec_cb) +{ + a->var = a; + a->exec_cb = (lv_anim_exec_xcb_t)exec_cb; +} + +/** + * Set the path (curve) of the animation. + * @param a pointer to an initialized `lv_anim_t` variable + * @param path_cb a function to set the current value of the animation. + */ +static inline void lv_anim_set_path_cb(lv_anim_t * a, lv_anim_path_cb_t path_cb) +{ + a->path_cb = path_cb; +} + +/** + * Set a function call when the animation really starts (considering `delay`) + * @param a pointer to an initialized `lv_anim_t` variable + * @param start_cb a function call when the animation starts + */ +static inline void lv_anim_set_start_cb(lv_anim_t * a, lv_anim_start_cb_t start_cb) +{ + a->start_cb = start_cb; +} + +/** + * Set a function to use the current value of the variable and make start and end value + * relative to the returned current value. + * @param a pointer to an initialized `lv_anim_t` variable + * @param get_value_cb a function call when the animation starts + */ +static inline void lv_anim_set_get_value_cb(lv_anim_t * a, lv_anim_get_value_cb_t get_value_cb) +{ + a->get_value_cb = get_value_cb; +} + +/** + * Set a function call when the animation is ready + * @param a pointer to an initialized `lv_anim_t` variable + * @param ready_cb a function call when the animation is ready + */ +static inline void lv_anim_set_ready_cb(lv_anim_t * a, lv_anim_ready_cb_t ready_cb) +{ + a->ready_cb = ready_cb; +} + +/** + * Set a function call when the animation is deleted. + * @param a pointer to an initialized `lv_anim_t` variable + * @param deleted_cb a function call when the animation is deleted + */ +static inline void lv_anim_set_deleted_cb(lv_anim_t * a, lv_anim_deleted_cb_t deleted_cb) +{ + a->deleted_cb = deleted_cb; +} + +/** + * Make the animation to play back to when the forward direction is ready + * @param a pointer to an initialized `lv_anim_t` variable + * @param time the duration of the playback animation in milliseconds. 0: disable playback + */ +static inline void lv_anim_set_playback_time(lv_anim_t * a, uint32_t time) +{ + a->playback_time = time; +} + +/** + * Make the animation to play back to when the forward direction is ready + * @param a pointer to an initialized `lv_anim_t` variable + * @param delay delay in milliseconds before starting the playback animation. + */ +static inline void lv_anim_set_playback_delay(lv_anim_t * a, uint32_t delay) +{ + a->playback_delay = delay; +} + +/** + * Make the animation repeat itself. + * @param a pointer to an initialized `lv_anim_t` variable + * @param cnt repeat count or `LV_ANIM_REPEAT_INFINITE` for infinite repetition. 0: to disable repetition. + */ +static inline void lv_anim_set_repeat_count(lv_anim_t * a, uint16_t cnt) +{ + a->repeat_cnt = cnt; +} + +/** + * Set a delay before repeating the animation. + * @param a pointer to an initialized `lv_anim_t` variable + * @param delay delay in milliseconds before repeating the animation. + */ +static inline void lv_anim_set_repeat_delay(lv_anim_t * a, uint32_t delay) +{ + a->repeat_delay = delay; +} + +/** + * Set a whether the animation's should be applied immediately or only when the delay expired. + * @param a pointer to an initialized `lv_anim_t` variable + * @param en true: apply the start value immediately in `lv_anim_start`; + * false: apply the start value only when `delay` ms is elapsed and the animations really starts + */ +static inline void lv_anim_set_early_apply(lv_anim_t * a, bool en) +{ + a->early_apply = en; +} + +/** + * Set the custom user data field of the animation. + * @param a pointer to an initialized `lv_anim_t` variable + * @param user_data pointer to the new user_data. + */ +#if LV_USE_USER_DATA +static inline void lv_anim_set_user_data(lv_anim_t * a, void * user_data) +{ + a->user_data = user_data; +} +#endif + +/** + * Create an animation + * @param a an initialized 'anim_t' variable. Not required after call. + * @return pointer to the created animation (different from the `a` parameter) + */ +lv_anim_t * lv_anim_start(const lv_anim_t * a); + +/** + * Get a delay before starting the animation + * @param a pointer to an initialized `lv_anim_t` variable + * @return delay before the animation in milliseconds + */ +static inline uint32_t lv_anim_get_delay(lv_anim_t * a) +{ + return -a->act_time; +} + +/** + * Get the time used to play the animation. + * @param a pointer to an animation. + * @return the play time in milliseconds. + */ +uint32_t lv_anim_get_playtime(lv_anim_t * a); + +/** + * Get the user_data field of the animation + * @param a pointer to an initialized `lv_anim_t` variable + * @return the pointer to the custom user_data of the animation + */ +#if LV_USE_USER_DATA +static inline void * lv_anim_get_user_data(lv_anim_t * a) +{ + return a->user_data; +} +#endif + +/** + * Delete an animation of a variable with a given animator function + * @param var pointer to variable + * @param exec_cb a function pointer which is animating 'var', + * or NULL to ignore it and delete all the animations of 'var + * @return true: at least 1 animation is deleted, false: no animation is deleted + */ +bool lv_anim_del(void * var, lv_anim_exec_xcb_t exec_cb); + +/** + * Delete all the animations + */ +void lv_anim_del_all(void); + +/** + * Get the animation of a variable and its `exec_cb`. + * @param var pointer to variable + * @param exec_cb a function pointer which is animating 'var', or NULL to return first matching 'var' + * @return pointer to the animation. + */ +lv_anim_t * lv_anim_get(void * var, lv_anim_exec_xcb_t exec_cb); + +/** + * Get global animation refresher timer. + * @return pointer to the animation refresher timer. + */ +struct _lv_timer_t * lv_anim_get_timer(void); + +/** + * Delete an animation by getting the animated variable from `a`. + * Only animations with `exec_cb` will be deleted. + * This function exists because it's logical that all anim. functions receives an + * `lv_anim_t` as their first parameter. It's not practical in C but might make + * the API more consequent and makes easier to generate bindings. + * @param a pointer to an animation. + * @param exec_cb a function pointer which is animating 'var', + * or NULL to ignore it and delete all the animations of 'var + * @return true: at least 1 animation is deleted, false: no animation is deleted + */ +static inline bool lv_anim_custom_del(lv_anim_t * a, lv_anim_custom_exec_cb_t exec_cb) +{ + return lv_anim_del(a ? a->var : NULL, (lv_anim_exec_xcb_t)exec_cb); +} + +/** + * Get the animation of a variable and its `exec_cb`. + * This function exists because it's logical that all anim. functions receives an + * `lv_anim_t` as their first parameter. It's not practical in C but might make + * the API more consequent and makes easier to generate bindings. + * @param a pointer to an animation. + * @param exec_cb a function pointer which is animating 'var', or NULL to return first matching 'var' + * @return pointer to the animation. + */ +static inline lv_anim_t * lv_anim_custom_get(lv_anim_t * a, lv_anim_custom_exec_cb_t exec_cb) +{ + return lv_anim_get(a ? a->var : NULL, (lv_anim_exec_xcb_t)exec_cb); +} + +/** + * Get the number of currently running animations + * @return the number of running animations + */ +uint16_t lv_anim_count_running(void); + +/** + * Calculate the time of an animation with a given speed and the start and end values + * @param speed speed of animation in unit/sec + * @param start start value of the animation + * @param end end value of the animation + * @return the required time [ms] for the animation with the given parameters + */ +uint32_t lv_anim_speed_to_time(uint32_t speed, int32_t start, int32_t end); + +/** + * Manually refresh the state of the animations. + * Useful to make the animations running in a blocking process where + * `lv_timer_handler` can't run for a while. + * Shouldn't be used directly because it is called in `lv_refr_now()`. + */ +void lv_anim_refr_now(void); + +/** + * Calculate the current value of an animation applying linear characteristic + * @param a pointer to an animation + * @return the current value to set + */ +int32_t lv_anim_path_linear(const lv_anim_t * a); + +/** + * Calculate the current value of an animation slowing down the start phase + * @param a pointer to an animation + * @return the current value to set + */ +int32_t lv_anim_path_ease_in(const lv_anim_t * a); + +/** + * Calculate the current value of an animation slowing down the end phase + * @param a pointer to an animation + * @return the current value to set + */ +int32_t lv_anim_path_ease_out(const lv_anim_t * a); + +/** + * Calculate the current value of an animation applying an "S" characteristic (cosine) + * @param a pointer to an animation + * @return the current value to set + */ +int32_t lv_anim_path_ease_in_out(const lv_anim_t * a); + +/** + * Calculate the current value of an animation with overshoot at the end + * @param a pointer to an animation + * @return the current value to set + */ +int32_t lv_anim_path_overshoot(const lv_anim_t * a); + +/** + * Calculate the current value of an animation with 3 bounces + * @param a pointer to an animation + * @return the current value to set + */ +int32_t lv_anim_path_bounce(const lv_anim_t * a); + +/** + * Calculate the current value of an animation applying step characteristic. + * (Set end value on the end of the animation) + * @param a pointer to an animation + * @return the current value to set + */ +int32_t lv_anim_path_step(const lv_anim_t * a); + +/********************** + * GLOBAL VARIABLES + **********************/ + +/********************** + * MACROS + **********************/ + +#ifdef __cplusplus +} /*extern "C"*/ +#endif + +#endif /*LV_ANIM_H*/ diff --git a/lib/lvgl/src/misc/lv_anim_timeline.c b/lib/lvgl/src/misc/lv_anim_timeline.c new file mode 100644 index 00000000..08d5321f --- /dev/null +++ b/lib/lvgl/src/misc/lv_anim_timeline.c @@ -0,0 +1,198 @@ +/** + * @file lv_anim_timeline.c + * + */ + +/********************* + * INCLUDES + *********************/ +#include "lv_anim_timeline.h" +#include "lv_mem.h" +#include "lv_assert.h" + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/*Data of anim_timeline_dsc*/ +typedef struct { + lv_anim_t anim; + uint32_t start_time; +} lv_anim_timeline_dsc_t; + +/*Data of anim_timeline*/ +struct _lv_anim_timeline_t { + lv_anim_timeline_dsc_t * anim_dsc; /**< Dynamically allocated anim dsc array*/ + uint32_t anim_dsc_cnt; /**< The length of anim dsc array*/ + bool reverse; /**< Reverse playback*/ +}; + +/********************** + * STATIC PROTOTYPES + **********************/ +static void lv_anim_timeline_virtual_exec_cb(void * var, int32_t v); + +/********************** + * STATIC VARIABLES + **********************/ + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +lv_anim_timeline_t * lv_anim_timeline_create(void) +{ + lv_anim_timeline_t * at = (lv_anim_timeline_t *)lv_mem_alloc(sizeof(lv_anim_timeline_t)); + + LV_ASSERT_MALLOC(at); + + if(at) lv_memset_00(at, sizeof(lv_anim_timeline_t)); + + return at; +} + +void lv_anim_timeline_del(lv_anim_timeline_t * at) +{ + LV_ASSERT_NULL(at); + + lv_anim_timeline_stop(at); + + lv_mem_free(at->anim_dsc); + lv_mem_free(at); +} + +void lv_anim_timeline_add(lv_anim_timeline_t * at, uint32_t start_time, lv_anim_t * a) +{ + LV_ASSERT_NULL(at); + + at->anim_dsc_cnt++; + at->anim_dsc = lv_mem_realloc(at->anim_dsc, at->anim_dsc_cnt * sizeof(lv_anim_timeline_dsc_t)); + + LV_ASSERT_MALLOC(at->anim_dsc); + + at->anim_dsc[at->anim_dsc_cnt - 1].anim = *a; + at->anim_dsc[at->anim_dsc_cnt - 1].start_time = start_time; + + /*Add default var and virtual exec_cb, used to delete animation.*/ + if(a->var == NULL && a->exec_cb == NULL) { + at->anim_dsc[at->anim_dsc_cnt - 1].anim.var = at; + at->anim_dsc[at->anim_dsc_cnt - 1].anim.exec_cb = lv_anim_timeline_virtual_exec_cb; + } +} + +uint32_t lv_anim_timeline_start(lv_anim_timeline_t * at) +{ + LV_ASSERT_NULL(at); + + const uint32_t playtime = lv_anim_timeline_get_playtime(at); + bool reverse = at->reverse; + + for(uint32_t i = 0; i < at->anim_dsc_cnt; i++) { + lv_anim_t a = at->anim_dsc[i].anim; + uint32_t start_time = at->anim_dsc[i].start_time; + + if(reverse) { + int32_t temp = a.start_value; + a.start_value = a.end_value; + a.end_value = temp; + lv_anim_set_delay(&a, playtime - (start_time + a.time)); + } + else { + lv_anim_set_delay(&a, start_time); + } + + lv_anim_start(&a); + } + + return playtime; +} + +void lv_anim_timeline_stop(lv_anim_timeline_t * at) +{ + LV_ASSERT_NULL(at); + + for(uint32_t i = 0; i < at->anim_dsc_cnt; i++) { + lv_anim_t * a = &(at->anim_dsc[i].anim); + lv_anim_del(a->var, a->exec_cb); + } +} + +void lv_anim_timeline_set_reverse(lv_anim_timeline_t * at, bool reverse) +{ + LV_ASSERT_NULL(at); + at->reverse = reverse; +} + +void lv_anim_timeline_set_progress(lv_anim_timeline_t * at, uint16_t progress) +{ + LV_ASSERT_NULL(at); + + const uint32_t playtime = lv_anim_timeline_get_playtime(at); + const uint32_t act_time = progress * playtime / 0xFFFF; + + for(uint32_t i = 0; i < at->anim_dsc_cnt; i++) { + lv_anim_t * a = &(at->anim_dsc[i].anim); + + if(a->exec_cb == NULL) { + continue; + } + + uint32_t start_time = at->anim_dsc[i].start_time; + int32_t value = 0; + + if(act_time < start_time) { + value = a->start_value; + } + else if(act_time < (start_time + a->time)) { + a->act_time = act_time - start_time; + value = a->path_cb(a); + } + else { + value = a->end_value; + } + + a->exec_cb(a->var, value); + } +} + +uint32_t lv_anim_timeline_get_playtime(lv_anim_timeline_t * at) +{ + LV_ASSERT_NULL(at); + + uint32_t playtime = 0; + for(uint32_t i = 0; i < at->anim_dsc_cnt; i++) { + uint32_t end = lv_anim_get_playtime(&at->anim_dsc[i].anim); + if(end == LV_ANIM_PLAYTIME_INFINITE) + return end; + end += at->anim_dsc[i].start_time; + if(end > playtime) { + playtime = end; + } + } + + return playtime; +} + +bool lv_anim_timeline_get_reverse(lv_anim_timeline_t * at) +{ + LV_ASSERT_NULL(at); + return at->reverse; +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +static void lv_anim_timeline_virtual_exec_cb(void * var, int32_t v) +{ + LV_UNUSED(var); + LV_UNUSED(v); +} diff --git a/lib/lvgl/src/misc/lv_anim_timeline.h b/lib/lvgl/src/misc/lv_anim_timeline.h new file mode 100644 index 00000000..d4dd0fcf --- /dev/null +++ b/lib/lvgl/src/misc/lv_anim_timeline.h @@ -0,0 +1,103 @@ +/** + * @file lv_anim_timeline.h + * + */ + +#ifndef LV_ANIM_TIMELINE_H +#define LV_ANIM_TIMELINE_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ +#include "lv_anim.h" + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +struct _lv_anim_timeline_t; + +typedef struct _lv_anim_timeline_t lv_anim_timeline_t; + +/********************** +* GLOBAL PROTOTYPES +**********************/ + +/** + * Create an animation timeline. + * @return pointer to the animation timeline. + */ +lv_anim_timeline_t * lv_anim_timeline_create(void); + +/** + * Delete animation timeline. + * @param at pointer to the animation timeline. + */ +void lv_anim_timeline_del(lv_anim_timeline_t * at); + +/** + * Add animation to the animation timeline. + * @param at pointer to the animation timeline. + * @param start_time the time the animation started on the timeline, note that start_time will override the value of delay. + * @param a pointer to an animation. + */ +void lv_anim_timeline_add(lv_anim_timeline_t * at, uint32_t start_time, lv_anim_t * a); + +/** + * Start the animation timeline. + * @param at pointer to the animation timeline. + * @return total time spent in animation timeline. + */ +uint32_t lv_anim_timeline_start(lv_anim_timeline_t * at); + +/** + * Stop the animation timeline. + * @param at pointer to the animation timeline. + */ +void lv_anim_timeline_stop(lv_anim_timeline_t * at); + +/** + * Set the playback direction of the animation timeline. + * @param at pointer to the animation timeline. + * @param reverse whether to play in reverse. + */ +void lv_anim_timeline_set_reverse(lv_anim_timeline_t * at, bool reverse); + +/** + * Set the progress of the animation timeline. + * @param at pointer to the animation timeline. + * @param progress set value 0~65535 to map 0~100% animation progress. + */ +void lv_anim_timeline_set_progress(lv_anim_timeline_t * at, uint16_t progress); + +/** + * Get the time used to play the animation timeline. + * @param at pointer to the animation timeline. + * @return total time spent in animation timeline. + */ +uint32_t lv_anim_timeline_get_playtime(lv_anim_timeline_t * at); + +/** + * Get whether the animation timeline is played in reverse. + * @param at pointer to the animation timeline. + * @return return true if it is reverse playback. + */ +bool lv_anim_timeline_get_reverse(lv_anim_timeline_t * at); + +/********************** + * MACROS + **********************/ + +#ifdef __cplusplus +} /*extern "C"*/ +#endif + +#endif /*LV_ANIM_TIMELINE_H*/ diff --git a/lib/lvgl/src/misc/lv_area.c b/lib/lvgl/src/misc/lv_area.c new file mode 100644 index 00000000..c0221f7e --- /dev/null +++ b/lib/lvgl/src/misc/lv_area.c @@ -0,0 +1,535 @@ +/** + * @file lv_area.c + * + */ + +/********************* + * INCLUDES + *********************/ +#include "../lv_conf_internal.h" + +#include "lv_area.h" +#include "lv_math.h" + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ + +static bool lv_point_within_circle(const lv_area_t * area, const lv_point_t * p); + +/********************** + * STATIC VARIABLES + **********************/ + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +/** + * Initialize an area + * @param area_p pointer to an area + * @param x1 left coordinate of the area + * @param y1 top coordinate of the area + * @param x2 right coordinate of the area + * @param y2 bottom coordinate of the area + */ +void lv_area_set(lv_area_t * area_p, lv_coord_t x1, lv_coord_t y1, lv_coord_t x2, lv_coord_t y2) +{ + area_p->x1 = x1; + area_p->y1 = y1; + area_p->x2 = x2; + area_p->y2 = y2; +} + +/** + * Set the width of an area + * @param area_p pointer to an area + * @param w the new width of the area (w == 1 makes x1 == x2) + */ +void lv_area_set_width(lv_area_t * area_p, lv_coord_t w) +{ + area_p->x2 = area_p->x1 + w - 1; +} + +/** + * Set the height of an area + * @param area_p pointer to an area + * @param h the new height of the area (h == 1 makes y1 == y2) + */ +void lv_area_set_height(lv_area_t * area_p, lv_coord_t h) +{ + area_p->y2 = area_p->y1 + h - 1; +} + +/** + * Set the position of an area (width and height will be kept) + * @param area_p pointer to an area + * @param x the new x coordinate of the area + * @param y the new y coordinate of the area + */ +void _lv_area_set_pos(lv_area_t * area_p, lv_coord_t x, lv_coord_t y) +{ + lv_coord_t w = lv_area_get_width(area_p); + lv_coord_t h = lv_area_get_height(area_p); + area_p->x1 = x; + area_p->y1 = y; + lv_area_set_width(area_p, w); + lv_area_set_height(area_p, h); +} + +/** + * Return with area of an area (x * y) + * @param area_p pointer to an area + * @return size of area + */ +uint32_t lv_area_get_size(const lv_area_t * area_p) +{ + uint32_t size; + + size = (uint32_t)(area_p->x2 - area_p->x1 + 1) * (area_p->y2 - area_p->y1 + 1); + + return size; +} + +void lv_area_increase(lv_area_t * area, lv_coord_t w_extra, lv_coord_t h_extra) +{ + area->x1 -= w_extra; + area->x2 += w_extra; + area->y1 -= h_extra; + area->y2 += h_extra; +} + +void lv_area_move(lv_area_t * area, lv_coord_t x_ofs, lv_coord_t y_ofs) +{ + area->x1 += x_ofs; + area->x2 += x_ofs; + area->y1 += y_ofs; + area->y2 += y_ofs; +} + +/** + * Get the common parts of two areas + * @param res_p pointer to an area, the result will be stored here + * @param a1_p pointer to the first area + * @param a2_p pointer to the second area + * @return false: the two area has NO common parts, res_p is invalid + */ +bool _lv_area_intersect(lv_area_t * res_p, const lv_area_t * a1_p, const lv_area_t * a2_p) +{ + /*Get the smaller area from 'a1_p' and 'a2_p'*/ + res_p->x1 = LV_MAX(a1_p->x1, a2_p->x1); + res_p->y1 = LV_MAX(a1_p->y1, a2_p->y1); + res_p->x2 = LV_MIN(a1_p->x2, a2_p->x2); + res_p->y2 = LV_MIN(a1_p->y2, a2_p->y2); + + /*If x1 or y1 greater than x2 or y2 then the areas union is empty*/ + bool union_ok = true; + if((res_p->x1 > res_p->x2) || (res_p->y1 > res_p->y2)) { + union_ok = false; + } + + return union_ok; +} + +/** + * Join two areas into a third which involves the other two + * @param res_p pointer to an area, the result will be stored here + * @param a1_p pointer to the first area + * @param a2_p pointer to the second area + */ +void _lv_area_join(lv_area_t * a_res_p, const lv_area_t * a1_p, const lv_area_t * a2_p) +{ + a_res_p->x1 = LV_MIN(a1_p->x1, a2_p->x1); + a_res_p->y1 = LV_MIN(a1_p->y1, a2_p->y1); + a_res_p->x2 = LV_MAX(a1_p->x2, a2_p->x2); + a_res_p->y2 = LV_MAX(a1_p->y2, a2_p->y2); +} + +/** + * Check if a point is on an area + * @param a_p pointer to an area + * @param p_p pointer to a point + * @param radius radius of area (e.g. for rounded rectangle) + * @return false:the point is out of the area + */ +bool _lv_area_is_point_on(const lv_area_t * a_p, const lv_point_t * p_p, lv_coord_t radius) +{ + /*First check the basic area*/ + bool is_on_rect = false; + if((p_p->x >= a_p->x1 && p_p->x <= a_p->x2) && ((p_p->y >= a_p->y1 && p_p->y <= a_p->y2))) { + is_on_rect = true; + } + if(!is_on_rect) + return false; + /*Now handle potential rounded rectangles*/ + if(radius <= 0) { + /*No radius, it is within the rectangle*/ + return true; + } + lv_coord_t w = lv_area_get_width(a_p) / 2; + lv_coord_t h = lv_area_get_height(a_p) / 2; + lv_coord_t max_radius = LV_MIN(w, h); + if(radius > max_radius) + radius = max_radius; + + /*Check if it's in one of the corners*/ + lv_area_t corner_area; + /*Top left*/ + corner_area.x1 = a_p->x1; + corner_area.x2 = a_p->x1 + radius; + corner_area.y1 = a_p->y1; + corner_area.y2 = a_p->y1 + radius; + if(_lv_area_is_point_on(&corner_area, p_p, 0)) { + corner_area.x2 += radius; + corner_area.y2 += radius; + return lv_point_within_circle(&corner_area, p_p); + } + /*Bottom left*/ + corner_area.y1 = a_p->y2 - radius; + corner_area.y2 = a_p->y2; + if(_lv_area_is_point_on(&corner_area, p_p, 0)) { + corner_area.x2 += radius; + corner_area.y1 -= radius; + return lv_point_within_circle(&corner_area, p_p); + } + /*Bottom right*/ + corner_area.x1 = a_p->x2 - radius; + corner_area.x2 = a_p->x2; + if(_lv_area_is_point_on(&corner_area, p_p, 0)) { + corner_area.x1 -= radius; + corner_area.y1 -= radius; + return lv_point_within_circle(&corner_area, p_p); + } + /*Top right*/ + corner_area.y1 = a_p->y1; + corner_area.y2 = a_p->y1 + radius; + if(_lv_area_is_point_on(&corner_area, p_p, 0)) { + corner_area.x1 -= radius; + corner_area.y2 += radius; + return lv_point_within_circle(&corner_area, p_p); + } + /*Not within corners*/ + return true; +} + +/** + * Check if two area has common parts + * @param a1_p pointer to an area. + * @param a2_p pointer to an other area + * @return false: a1_p and a2_p has no common parts + */ +bool _lv_area_is_on(const lv_area_t * a1_p, const lv_area_t * a2_p) +{ + if((a1_p->x1 <= a2_p->x2) && (a1_p->x2 >= a2_p->x1) && (a1_p->y1 <= a2_p->y2) && (a1_p->y2 >= a2_p->y1)) { + return true; + } + else { + return false; + } +} + +/** + * Check if an area is fully on an other + * @param ain_p pointer to an area which could be in 'aholder_p' + * @param aholder_p pointer to an area which could involve 'ain_p' + * @param radius radius of `aholder_p` (e.g. for rounded rectangle) + * @return true: `ain_p` is fully inside `aholder_p` + */ +bool _lv_area_is_in(const lv_area_t * ain_p, const lv_area_t * aholder_p, lv_coord_t radius) +{ + bool is_in = false; + + if(ain_p->x1 >= aholder_p->x1 && ain_p->y1 >= aholder_p->y1 && ain_p->x2 <= aholder_p->x2 && + ain_p->y2 <= aholder_p->y2) { + is_in = true; + } + + if(!is_in) return false; + if(radius == 0) return true; + + /*Check if the corner points are inside the radius or not*/ + lv_point_t p; + + p.x = ain_p->x1; + p.y = ain_p->y1; + if(_lv_area_is_point_on(aholder_p, &p, radius) == false) return false; + + p.x = ain_p->x2; + p.y = ain_p->y1; + if(_lv_area_is_point_on(aholder_p, &p, radius) == false) return false; + + p.x = ain_p->x1; + p.y = ain_p->y2; + if(_lv_area_is_point_on(aholder_p, &p, radius) == false) return false; + + p.x = ain_p->x2; + p.y = ain_p->y2; + if(_lv_area_is_point_on(aholder_p, &p, radius) == false) return false; + + return true; +} + +/** + * Check if an area is fully out of an other + * @param aout_p pointer to an area which could be in 'aholder_p' + * @param aholder_p pointer to an area which could involve 'ain_p' + * @param radius radius of `aholder_p` (e.g. for rounded rectangle) + * @return true: `aout_p` is fully outside `aholder_p` + */ +bool _lv_area_is_out(const lv_area_t * aout_p, const lv_area_t * aholder_p, lv_coord_t radius) +{ + if(aout_p->x2 < aholder_p->x1 || aout_p->y2 < aholder_p->y1 || aout_p->x1 > aholder_p->x2 || + aout_p->y1 > aholder_p->y2) { + return true; + } + + if(radius == 0) return false; + + /*Check if the corner points are outside the radius or not*/ + lv_point_t p; + + p.x = aout_p->x1; + p.y = aout_p->y1; + if(_lv_area_is_point_on(aholder_p, &p, radius)) return false; + + p.x = aout_p->x2; + p.y = aout_p->y1; + if(_lv_area_is_point_on(aholder_p, &p, radius)) return false; + + p.x = aout_p->x1; + p.y = aout_p->y2; + if(_lv_area_is_point_on(aholder_p, &p, radius)) return false; + + p.x = aout_p->x2; + p.y = aout_p->y2; + if(_lv_area_is_point_on(aholder_p, &p, radius)) return false; + + return true; +} + +bool _lv_area_is_equal(const lv_area_t * a, const lv_area_t * b) +{ + return a->x1 == b->x1 && a->x2 == b->x2 && a->y1 == b->y1 && a->y2 == b->y2; +} + +/** + * Align an area to an other + * @param base an are where the other will be aligned + * @param to_align the area to align + * @param align `LV_ALIGN_...` + * @param res x/y coordinates where `to_align` align area should be placed + */ +void lv_area_align(const lv_area_t * base, lv_area_t * to_align, lv_align_t align, lv_coord_t ofs_x, lv_coord_t ofs_y) +{ + + lv_coord_t x; + lv_coord_t y; + switch(align) { + case LV_ALIGN_CENTER: + x = lv_area_get_width(base) / 2 - lv_area_get_width(to_align) / 2; + y = lv_area_get_height(base) / 2 - lv_area_get_height(to_align) / 2; + break; + + case LV_ALIGN_TOP_LEFT: + x = 0; + y = 0; + break; + case LV_ALIGN_TOP_MID: + x = lv_area_get_width(base) / 2 - lv_area_get_width(to_align) / 2; + y = 0; + break; + + case LV_ALIGN_TOP_RIGHT: + x = lv_area_get_width(base) - lv_area_get_width(to_align); + y = 0; + break; + + case LV_ALIGN_BOTTOM_LEFT: + x = 0; + y = lv_area_get_height(base) - lv_area_get_height(to_align); + break; + case LV_ALIGN_BOTTOM_MID: + x = lv_area_get_width(base) / 2 - lv_area_get_width(to_align) / 2; + y = lv_area_get_height(base) - lv_area_get_height(to_align); + break; + + case LV_ALIGN_BOTTOM_RIGHT: + x = lv_area_get_width(base) - lv_area_get_width(to_align); + y = lv_area_get_height(base) - lv_area_get_height(to_align); + break; + + case LV_ALIGN_LEFT_MID: + x = 0; + y = lv_area_get_height(base) / 2 - lv_area_get_height(to_align) / 2; + break; + + case LV_ALIGN_RIGHT_MID: + x = lv_area_get_width(base) - lv_area_get_width(to_align); + y = lv_area_get_height(base) / 2 - lv_area_get_height(to_align) / 2; + break; + + case LV_ALIGN_OUT_TOP_LEFT: + x = 0; + y = -lv_area_get_height(to_align); + break; + + case LV_ALIGN_OUT_TOP_MID: + x = lv_area_get_width(base) / 2 - lv_area_get_width(to_align) / 2; + y = -lv_area_get_height(to_align); + break; + + case LV_ALIGN_OUT_TOP_RIGHT: + x = lv_area_get_width(base) - lv_area_get_width(to_align); + y = -lv_area_get_height(to_align); + break; + + case LV_ALIGN_OUT_BOTTOM_LEFT: + x = 0; + y = lv_area_get_height(base); + break; + + case LV_ALIGN_OUT_BOTTOM_MID: + x = lv_area_get_width(base) / 2 - lv_area_get_width(to_align) / 2; + y = lv_area_get_height(base); + break; + + case LV_ALIGN_OUT_BOTTOM_RIGHT: + x = lv_area_get_width(base) - lv_area_get_width(to_align); + y = lv_area_get_height(base); + break; + + case LV_ALIGN_OUT_LEFT_TOP: + x = -lv_area_get_width(to_align); + y = 0; + break; + + case LV_ALIGN_OUT_LEFT_MID: + x = -lv_area_get_width(to_align); + y = lv_area_get_height(base) / 2 - lv_area_get_height(to_align) / 2; + break; + + case LV_ALIGN_OUT_LEFT_BOTTOM: + x = -lv_area_get_width(to_align); + y = lv_area_get_height(base) - lv_area_get_height(to_align); + break; + + case LV_ALIGN_OUT_RIGHT_TOP: + x = lv_area_get_width(base); + y = 0; + break; + + case LV_ALIGN_OUT_RIGHT_MID: + x = lv_area_get_width(base); + y = lv_area_get_height(base) / 2 - lv_area_get_height(to_align) / 2; + break; + + case LV_ALIGN_OUT_RIGHT_BOTTOM: + x = lv_area_get_width(base); + y = lv_area_get_height(base) - lv_area_get_height(to_align); + break; + default: + x = 0; + y = 0; + break; + } + + x += base->x1; + y += base->y1; + + lv_coord_t w = lv_area_get_width(to_align); + lv_coord_t h = lv_area_get_height(to_align); + to_align->x1 = x + ofs_x; + to_align->y1 = y + ofs_y; + to_align->x2 = to_align->x1 + w - 1; + to_align->y2 = to_align->y1 + h - 1; +} + +#define _LV_TRANSFORM_TRIGO_SHIFT 10 +void lv_point_transform(lv_point_t * p, int32_t angle, int32_t zoom, const lv_point_t * pivot) +{ + if(angle == 0 && zoom == 256) { + return; + } + + p->x -= pivot->x; + p->y -= pivot->y; + + if(angle == 0) { + p->x = (((int32_t)(p->x) * zoom) >> 8) + pivot->x; + p->y = (((int32_t)(p->y) * zoom) >> 8) + pivot->y; + return; + } + + static int32_t angle_prev = INT32_MIN; + static int32_t sinma; + static int32_t cosma; + if(angle_prev != angle) { + int32_t angle_limited = angle; + if(angle_limited > 3600) angle_limited -= 3600; + if(angle_limited < 0) angle_limited += 3600; + + int32_t angle_low = angle_limited / 10; + int32_t angle_high = angle_low + 1; + int32_t angle_rem = angle_limited - (angle_low * 10); + + int32_t s1 = lv_trigo_sin(angle_low); + int32_t s2 = lv_trigo_sin(angle_high); + + int32_t c1 = lv_trigo_sin(angle_low + 90); + int32_t c2 = lv_trigo_sin(angle_high + 90); + + sinma = (s1 * (10 - angle_rem) + s2 * angle_rem) / 10; + cosma = (c1 * (10 - angle_rem) + c2 * angle_rem) / 10; + sinma = sinma >> (LV_TRIGO_SHIFT - _LV_TRANSFORM_TRIGO_SHIFT); + cosma = cosma >> (LV_TRIGO_SHIFT - _LV_TRANSFORM_TRIGO_SHIFT); + angle_prev = angle; + } + int32_t x = p->x; + int32_t y = p->y; + if(zoom == 256) { + p->x = ((cosma * x - sinma * y) >> _LV_TRANSFORM_TRIGO_SHIFT) + pivot->x; + p->y = ((sinma * x + cosma * y) >> _LV_TRANSFORM_TRIGO_SHIFT) + pivot->y; + } + else { + p->x = (((cosma * x - sinma * y) * zoom) >> (_LV_TRANSFORM_TRIGO_SHIFT + 8)) + pivot->x; + p->y = (((sinma * x + cosma * y) * zoom) >> (_LV_TRANSFORM_TRIGO_SHIFT + 8)) + pivot->y; + } +} + + +/********************** + * STATIC FUNCTIONS + **********************/ + +static bool lv_point_within_circle(const lv_area_t * area, const lv_point_t * p) +{ + lv_coord_t r = (area->x2 - area->x1) / 2; + + /*Circle center*/ + lv_coord_t cx = area->x1 + r; + lv_coord_t cy = area->y1 + r; + + /*Simplify the code by moving everything to (0, 0)*/ + lv_coord_t px = p->x - cx; + lv_coord_t py = p->y - cy; + + uint32_t r_sqrd = r * r; + uint32_t dist = (px * px) + (py * py); + + if(dist <= r_sqrd) + return true; + else + return false; +} diff --git a/lib/lvgl/src/misc/lv_area.h b/lib/lvgl/src/misc/lv_area.h new file mode 100644 index 00000000..137931a2 --- /dev/null +++ b/lib/lvgl/src/misc/lv_area.h @@ -0,0 +1,295 @@ +/** + * @file lv_area.h + * + */ + +#ifndef LV_AREA_H +#define LV_AREA_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ +#include "../lv_conf_internal.h" +#include +#include + +/********************* + * DEFINES + *********************/ + +#if LV_USE_LARGE_COORD +typedef int32_t lv_coord_t; +#else +typedef int16_t lv_coord_t; +#endif + +/********************** + * TYPEDEFS + **********************/ + +/** + * Represents a point on the screen. + */ +typedef struct { + lv_coord_t x; + lv_coord_t y; +} lv_point_t; + +/** Represents an area of the screen.*/ +typedef struct { + lv_coord_t x1; + lv_coord_t y1; + lv_coord_t x2; + lv_coord_t y2; +} lv_area_t; + +/** Alignments*/ +enum { + LV_ALIGN_DEFAULT = 0, + LV_ALIGN_TOP_LEFT, + LV_ALIGN_TOP_MID, + LV_ALIGN_TOP_RIGHT, + LV_ALIGN_BOTTOM_LEFT, + LV_ALIGN_BOTTOM_MID, + LV_ALIGN_BOTTOM_RIGHT, + LV_ALIGN_LEFT_MID, + LV_ALIGN_RIGHT_MID, + LV_ALIGN_CENTER, + + LV_ALIGN_OUT_TOP_LEFT, + LV_ALIGN_OUT_TOP_MID, + LV_ALIGN_OUT_TOP_RIGHT, + LV_ALIGN_OUT_BOTTOM_LEFT, + LV_ALIGN_OUT_BOTTOM_MID, + LV_ALIGN_OUT_BOTTOM_RIGHT, + LV_ALIGN_OUT_LEFT_TOP, + LV_ALIGN_OUT_LEFT_MID, + LV_ALIGN_OUT_LEFT_BOTTOM, + LV_ALIGN_OUT_RIGHT_TOP, + LV_ALIGN_OUT_RIGHT_MID, + LV_ALIGN_OUT_RIGHT_BOTTOM, +}; +typedef uint8_t lv_align_t; + +enum { + LV_DIR_NONE = 0x00, + LV_DIR_LEFT = (1 << 0), + LV_DIR_RIGHT = (1 << 1), + LV_DIR_TOP = (1 << 2), + LV_DIR_BOTTOM = (1 << 3), + LV_DIR_HOR = LV_DIR_LEFT | LV_DIR_RIGHT, + LV_DIR_VER = LV_DIR_TOP | LV_DIR_BOTTOM, + LV_DIR_ALL = LV_DIR_HOR | LV_DIR_VER, +}; + +typedef uint8_t lv_dir_t; + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +/** + * Initialize an area + * @param area_p pointer to an area + * @param x1 left coordinate of the area + * @param y1 top coordinate of the area + * @param x2 right coordinate of the area + * @param y2 bottom coordinate of the area + */ +void lv_area_set(lv_area_t * area_p, lv_coord_t x1, lv_coord_t y1, lv_coord_t x2, lv_coord_t y2); + +/** + * Copy an area + * @param dest pointer to the destination area + * @param src pointer to the source area + */ +inline static void lv_area_copy(lv_area_t * dest, const lv_area_t * src) +{ + dest->x1 = src->x1; + dest->y1 = src->y1; + dest->x2 = src->x2; + dest->y2 = src->y2; +} + +/** + * Get the width of an area + * @param area_p pointer to an area + * @return the width of the area (if x1 == x2 -> width = 1) + */ +static inline lv_coord_t lv_area_get_width(const lv_area_t * area_p) +{ + return (lv_coord_t)(area_p->x2 - area_p->x1 + 1); +} + +/** + * Get the height of an area + * @param area_p pointer to an area + * @return the height of the area (if y1 == y2 -> height = 1) + */ +static inline lv_coord_t lv_area_get_height(const lv_area_t * area_p) +{ + return (lv_coord_t)(area_p->y2 - area_p->y1 + 1); +} + +/** + * Set the width of an area + * @param area_p pointer to an area + * @param w the new width of the area (w == 1 makes x1 == x2) + */ +void lv_area_set_width(lv_area_t * area_p, lv_coord_t w); + +/** + * Set the height of an area + * @param area_p pointer to an area + * @param h the new height of the area (h == 1 makes y1 == y2) + */ +void lv_area_set_height(lv_area_t * area_p, lv_coord_t h); + +/** + * Set the position of an area (width and height will be kept) + * @param area_p pointer to an area + * @param x the new x coordinate of the area + * @param y the new y coordinate of the area + */ +void _lv_area_set_pos(lv_area_t * area_p, lv_coord_t x, lv_coord_t y); + +/** + * Return with area of an area (x * y) + * @param area_p pointer to an area + * @return size of area + */ +uint32_t lv_area_get_size(const lv_area_t * area_p); + +void lv_area_increase(lv_area_t * area, lv_coord_t w_extra, lv_coord_t h_extra); + +void lv_area_move(lv_area_t * area, lv_coord_t x_ofs, lv_coord_t y_ofs); + +/** + * Get the common parts of two areas + * @param res_p pointer to an area, the result will be stored her + * @param a1_p pointer to the first area + * @param a2_p pointer to the second area + * @return false: the two area has NO common parts, res_p is invalid + */ +bool _lv_area_intersect(lv_area_t * res_p, const lv_area_t * a1_p, const lv_area_t * a2_p); + +/** + * Join two areas into a third which involves the other two + * @param res_p pointer to an area, the result will be stored here + * @param a1_p pointer to the first area + * @param a2_p pointer to the second area + */ +void _lv_area_join(lv_area_t * a_res_p, const lv_area_t * a1_p, const lv_area_t * a2_p); + +/** + * Check if a point is on an area + * @param a_p pointer to an area + * @param p_p pointer to a point + * @param radius radius of area (e.g. for rounded rectangle) + * @return false:the point is out of the area + */ +bool _lv_area_is_point_on(const lv_area_t * a_p, const lv_point_t * p_p, lv_coord_t radius); + +/** + * Check if two area has common parts + * @param a1_p pointer to an area. + * @param a2_p pointer to an other area + * @return false: a1_p and a2_p has no common parts + */ +bool _lv_area_is_on(const lv_area_t * a1_p, const lv_area_t * a2_p); + +/** + * Check if an area is fully on an other + * @param ain_p pointer to an area which could be in 'aholder_p' + * @param aholder_p pointer to an area which could involve 'ain_p' + * @param radius radius of `aholder_p` (e.g. for rounded rectangle) + * @return true: `ain_p` is fully inside `aholder_p` + */ +bool _lv_area_is_in(const lv_area_t * ain_p, const lv_area_t * aholder_p, lv_coord_t radius); + + +/** + * Check if an area is fully out of an other + * @param aout_p pointer to an area which could be in 'aholder_p' + * @param aholder_p pointer to an area which could involve 'ain_p' + * @param radius radius of `aholder_p` (e.g. for rounded rectangle) + * @return true: `aout_p` is fully outside `aholder_p` + */ +bool _lv_area_is_out(const lv_area_t * aout_p, const lv_area_t * aholder_p, lv_coord_t radius); + +/** + * Check if 2 area is the same + * @param a pointer to an area + * @param b pointer to another area + */ +bool _lv_area_is_equal(const lv_area_t * a, const lv_area_t * b); + +/** + * Align an area to an other + * @param base an are where the other will be aligned + * @param to_align the area to align + * @param align `LV_ALIGN_...` + */ +void lv_area_align(const lv_area_t * base, lv_area_t * to_align, lv_align_t align, lv_coord_t ofs_x, lv_coord_t ofs_y); + +void lv_point_transform(lv_point_t * p, int32_t angle, int32_t zoom, const lv_point_t * pivot); + +/********************** + * MACROS + **********************/ + +#if LV_USE_LARGE_COORD +#define _LV_COORD_TYPE_SHIFT (29U) +#else +#define _LV_COORD_TYPE_SHIFT (13U) +#endif + +#define _LV_COORD_TYPE_MASK (3 << _LV_COORD_TYPE_SHIFT) +#define _LV_COORD_TYPE(x) ((x) & _LV_COORD_TYPE_MASK) /*Extract type specifiers*/ +#define _LV_COORD_PLAIN(x) ((x) & ~_LV_COORD_TYPE_MASK) /*Remove type specifiers*/ + +#define _LV_COORD_TYPE_PX (0 << _LV_COORD_TYPE_SHIFT) +#define _LV_COORD_TYPE_SPEC (1 << _LV_COORD_TYPE_SHIFT) +#define _LV_COORD_TYPE_PX_NEG (3 << _LV_COORD_TYPE_SHIFT) + +#define LV_COORD_IS_PX(x) (_LV_COORD_TYPE(x) == _LV_COORD_TYPE_PX || \ + _LV_COORD_TYPE(x) == _LV_COORD_TYPE_PX_NEG ? true : false) +#define LV_COORD_IS_SPEC(x) (_LV_COORD_TYPE(x) == _LV_COORD_TYPE_SPEC ? true : false) + +#define LV_COORD_SET_SPEC(x) ((x) | _LV_COORD_TYPE_SPEC) + +/*Special coordinates*/ +#define LV_PCT(x) (x < 0 ? LV_COORD_SET_SPEC(1000 - (x)) : LV_COORD_SET_SPEC(x)) +#define LV_COORD_IS_PCT(x) ((LV_COORD_IS_SPEC(x) && _LV_COORD_PLAIN(x) <= 2000) ? true : false) +#define LV_COORD_GET_PCT(x) (_LV_COORD_PLAIN(x) > 1000 ? 1000 - _LV_COORD_PLAIN(x) : _LV_COORD_PLAIN(x)) +#define LV_SIZE_CONTENT LV_COORD_SET_SPEC(2001) + +LV_EXPORT_CONST_INT(LV_SIZE_CONTENT); + +/*Max coordinate value*/ +#define LV_COORD_MAX ((1 << _LV_COORD_TYPE_SHIFT) - 1) +#define LV_COORD_MIN (-LV_COORD_MAX) + +LV_EXPORT_CONST_INT(LV_COORD_MAX); +LV_EXPORT_CONST_INT(LV_COORD_MIN); + +/** + * Convert a percentage value to `lv_coord_t`. + * Percentage values are stored in special range + * @param x the percentage (0..1000) + * @return a coordinate that stores the percentage + */ +static inline lv_coord_t lv_pct(lv_coord_t x) +{ + return LV_PCT(x); +} + +#ifdef __cplusplus +} /*extern "C"*/ +#endif + +#endif diff --git a/lib/lvgl/src/misc/lv_assert.h b/lib/lvgl/src/misc/lv_assert.h new file mode 100644 index 00000000..48db7443 --- /dev/null +++ b/lib/lvgl/src/misc/lv_assert.h @@ -0,0 +1,79 @@ +/** + * @file lv_assert.h + * + */ + +#ifndef LV_ASSERT_H +#define LV_ASSERT_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ +#include "../lv_conf_internal.h" +#include "lv_log.h" +#include "lv_mem.h" +#include LV_ASSERT_HANDLER_INCLUDE + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +/********************** + * MACROS + **********************/ + +#define LV_ASSERT(expr) \ + do { \ + if(!(expr)) { \ + LV_LOG_ERROR("Asserted at expression: %s", #expr); \ + LV_ASSERT_HANDLER \ + } \ + } while(0) + +#define LV_ASSERT_MSG(expr, msg) \ + do { \ + if(!(expr)) { \ + LV_LOG_ERROR("Asserted at expression: %s (%s)", #expr, msg); \ + LV_ASSERT_HANDLER \ + } \ + } while(0) + +/*----------------- + * ASSERTS + *-----------------*/ + +#if LV_USE_ASSERT_NULL +# define LV_ASSERT_NULL(p) LV_ASSERT_MSG(p != NULL, "NULL pointer"); +#else +# define LV_ASSERT_NULL(p) +#endif + +#if LV_USE_ASSERT_MALLOC +# define LV_ASSERT_MALLOC(p) LV_ASSERT_MSG(p != NULL, "Out of memory"); +#else +# define LV_ASSERT_MALLOC(p) +#endif + +#if LV_USE_ASSERT_MEM_INTEGRITY +# define LV_ASSERT_MEM_INTEGRITY() LV_ASSERT_MSG(lv_mem_test() == LV_RES_OK, "Memory integrity error"); +#else +# define LV_ASSERT_MEM_INTEGRITY() +#endif + +#ifdef __cplusplus +} /*extern "C"*/ +#endif + +#endif /*LV_ASSERT_H*/ diff --git a/lib/lvgl/src/misc/lv_async.c b/lib/lvgl/src/misc/lv_async.c new file mode 100644 index 00000000..c4941e81 --- /dev/null +++ b/lib/lvgl/src/misc/lv_async.c @@ -0,0 +1,105 @@ +/** + * @file lv_async.c + * + */ + +/********************* + * INCLUDES + *********************/ + +#include "lv_async.h" +#include "lv_mem.h" +#include "lv_timer.h" + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +typedef struct _lv_async_info_t { + lv_async_cb_t cb; + void * user_data; +} lv_async_info_t; + +/********************** + * STATIC PROTOTYPES + **********************/ + +static void lv_async_timer_cb(lv_timer_t * timer); + +/********************** + * STATIC VARIABLES + **********************/ + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +lv_res_t lv_async_call(lv_async_cb_t async_xcb, void * user_data) +{ + /*Allocate an info structure*/ + lv_async_info_t * info = lv_mem_alloc(sizeof(lv_async_info_t)); + + if(info == NULL) + return LV_RES_INV; + + /*Create a new timer*/ + lv_timer_t * timer = lv_timer_create(lv_async_timer_cb, 0, info); + + if(timer == NULL) { + lv_mem_free(info); + return LV_RES_INV; + } + + info->cb = async_xcb; + info->user_data = user_data; + + lv_timer_set_repeat_count(timer, 1); + return LV_RES_OK; +} + +lv_res_t lv_async_call_cancel(lv_async_cb_t async_xcb, void * user_data) +{ + lv_timer_t * timer = lv_timer_get_next(NULL); + lv_res_t res = LV_RES_INV; + + while(timer != NULL) { + /*Find the next timer node*/ + lv_timer_t * timer_next = lv_timer_get_next(timer); + + /*Find async timer callback*/ + if(timer->timer_cb == lv_async_timer_cb) { + lv_async_info_t * info = (lv_async_info_t *)timer->user_data; + + /*Match user function callback and user data*/ + if(info->cb == async_xcb && info->user_data == user_data) { + lv_timer_del(timer); + lv_mem_free(info); + res = LV_RES_OK; + } + } + + timer = timer_next; + } + + return res; +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +static void lv_async_timer_cb(lv_timer_t * timer) +{ + lv_async_info_t * info = (lv_async_info_t *)timer->user_data; + + info->cb(info->user_data); + lv_mem_free(info); +} diff --git a/lib/lvgl/src/misc/lv_async.h b/lib/lvgl/src/misc/lv_async.h new file mode 100644 index 00000000..4ad5756d --- /dev/null +++ b/lib/lvgl/src/misc/lv_async.h @@ -0,0 +1,61 @@ +/** + * @file lv_async.h + * + */ + +#ifndef LV_ASYNC_H +#define LV_ASYNC_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ + +#include "lv_types.h" + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/** + * Type for async callback. + */ +typedef void (*lv_async_cb_t)(void *); + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +/** + * Call an asynchronous function the next time lv_timer_handler() is run. This function is likely to return + * **before** the call actually happens! + * @param async_xcb a callback which is the task itself. + * (the 'x' in the argument name indicates that it's not a fully generic function because it not follows + * the `func_name(object, callback, ...)` convention) + * @param user_data custom parameter + */ +lv_res_t lv_async_call(lv_async_cb_t async_xcb, void * user_data); + +/** + * Cancel an asynchronous function call + * @param async_xcb a callback which is the task itself. + * @param user_data custom parameter + */ +lv_res_t lv_async_call_cancel(lv_async_cb_t async_xcb, void * user_data); + +/********************** + * MACROS + **********************/ + +#ifdef __cplusplus +} /*extern "C"*/ +#endif + +#endif /*LV_ASYNC_H*/ diff --git a/lib/lvgl/src/misc/lv_bidi.c b/lib/lvgl/src/misc/lv_bidi.c new file mode 100644 index 00000000..3dc3ce7f --- /dev/null +++ b/lib/lvgl/src/misc/lv_bidi.c @@ -0,0 +1,682 @@ +/** + * @file lv_bidi.c + * + */ + +/********************* + * INCLUDES + *********************/ +#include +#include "lv_bidi.h" +#include "lv_txt.h" +#include "../misc/lv_mem.h" + +#if LV_USE_BIDI + +/********************* + * DEFINES + *********************/ +#define LV_BIDI_BRACKLET_DEPTH 4 + +// Highest bit of the 16-bit pos_conv value specifies whether this pos is RTL or not +#define GET_POS(x) ((x) & 0x7FFF) +#define IS_RTL_POS(x) (((x) & 0x8000) != 0) +#define SET_RTL_POS(x, is_rtl) (GET_POS(x) | ((is_rtl)? 0x8000: 0)) + +/********************** + * TYPEDEFS + **********************/ +typedef struct { + uint32_t bracklet_pos; + lv_base_dir_t dir; +} bracket_stack_t; + +/********************** + * STATIC PROTOTYPES + **********************/ + +static uint32_t lv_bidi_get_next_paragraph(const char * txt); +static lv_base_dir_t lv_bidi_get_letter_dir(uint32_t letter); +static bool lv_bidi_letter_is_weak(uint32_t letter); +static bool lv_bidi_letter_is_rtl(uint32_t letter); +static bool lv_bidi_letter_is_neutral(uint32_t letter); + +static lv_base_dir_t get_next_run(const char * txt, lv_base_dir_t base_dir, uint32_t max_len, uint32_t * len, + uint16_t * pos_conv_len); +static void rtl_reverse(char * dest, const char * src, uint32_t len, uint16_t * pos_conv_out, uint16_t pos_conv_rd_base, + uint16_t pos_conv_len); +static uint32_t char_change_to_pair(uint32_t letter); +static lv_base_dir_t bracket_process(const char * txt, uint32_t next_pos, uint32_t len, uint32_t letter, + lv_base_dir_t base_dir); +static void fill_pos_conv(uint16_t * out, uint16_t len, uint16_t index); +static uint32_t get_txt_len(const char * txt, uint32_t max_len); + +/********************** + * STATIC VARIABLES + **********************/ +static const uint8_t bracket_left[] = {"<({["}; +static const uint8_t bracket_right[] = {">)}]"}; +static bracket_stack_t br_stack[LV_BIDI_BRACKLET_DEPTH]; +static uint8_t br_stack_p; + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +/** + * Convert a text to get the characters in the correct visual order according to + * Unicode Bidirectional Algorithm + * @param str_in the text to process + * @param str_out store the result here. Has the be `strlen(str_in)` length + * @param base_dir `LV_BASE_DIR_LTR` or `LV_BASE_DIR_RTL` + */ +void _lv_bidi_process(const char * str_in, char * str_out, lv_base_dir_t base_dir) +{ + if(base_dir == LV_BASE_DIR_AUTO) base_dir = _lv_bidi_detect_base_dir(str_in); + + uint32_t par_start = 0; + uint32_t par_len; + + while(str_in[par_start] == '\n' || str_in[par_start] == '\r') { + str_out[par_start] = str_in[par_start]; + par_start ++; + } + + while(str_in[par_start] != '\0') { + par_len = lv_bidi_get_next_paragraph(&str_in[par_start]); + _lv_bidi_process_paragraph(&str_in[par_start], &str_out[par_start], par_len, base_dir, NULL, 0); + par_start += par_len; + + while(str_in[par_start] == '\n' || str_in[par_start] == '\r') { + str_out[par_start] = str_in[par_start]; + par_start ++; + } + } + + str_out[par_start] = '\0'; +} + +/** + * Auto-detect the direction of a text based on the first strong character + * @param txt the text to process + * @return `LV_BASE_DIR_LTR` or `LV_BASE_DIR_RTL` + */ +lv_base_dir_t _lv_bidi_detect_base_dir(const char * txt) +{ + uint32_t i = 0; + uint32_t letter; + while(txt[i] != '\0') { + letter = _lv_txt_encoded_next(txt, &i); + + lv_base_dir_t dir; + dir = lv_bidi_get_letter_dir(letter); + if(dir == LV_BASE_DIR_RTL || dir == LV_BASE_DIR_LTR) return dir; + } + + /*If there were no strong char earlier return with the default base dir*/ + if(LV_BIDI_BASE_DIR_DEF == LV_BASE_DIR_AUTO) return LV_BASE_DIR_LTR; + else return LV_BIDI_BASE_DIR_DEF; +} + +/** + * Get the logical position of a character in a line + * @param str_in the input string. Can be only one line. + * @param bidi_txt internally the text is bidi processed which buffer can be get here. + * If not required anymore has to freed with `lv_mem_free()` + * Can be `NULL` is unused + * @param len length of the line in character count + * @param base_dir base direction of the text: `LV_BASE_DIR_LTR` or `LV_BASE_DIR_RTL` + * @param visual_pos the visual character position which logical position should be get + * @param is_rtl tell the char at `visual_pos` is RTL or LTR context + * @return the logical character position + */ +uint16_t _lv_bidi_get_logical_pos(const char * str_in, char ** bidi_txt, uint32_t len, lv_base_dir_t base_dir, + uint32_t visual_pos, bool * is_rtl) +{ + uint32_t pos_conv_len = get_txt_len(str_in, len); + char * buf = lv_mem_buf_get(len + 1); + if(buf == NULL) return (uint16_t) -1; + + uint16_t * pos_conv_buf = lv_mem_buf_get(pos_conv_len * sizeof(uint16_t)); + if(pos_conv_buf == NULL) { + lv_mem_buf_release(buf); + return (uint16_t) -1; + } + + if(bidi_txt) *bidi_txt = buf; + + _lv_bidi_process_paragraph(str_in, bidi_txt ? *bidi_txt : NULL, len, base_dir, pos_conv_buf, pos_conv_len); + + if(is_rtl) *is_rtl = IS_RTL_POS(pos_conv_buf[visual_pos]); + + if(bidi_txt == NULL) lv_mem_buf_release(buf); + uint16_t res = GET_POS(pos_conv_buf[visual_pos]); + lv_mem_buf_release(pos_conv_buf); + return res; +} + +/** + * Get the visual position of a character in a line + * @param str_in the input string. Can be only one line. + * @param bidi_txt internally the text is bidi processed which buffer can be get here. + * If not required anymore has to freed with `lv_mem_free()` + * Can be `NULL` is unused + * @param len length of the line in character count + * @param base_dir base direction of the text: `LV_BASE_DIR_LTR` or `LV_BASE_DIR_RTL` + * @param logical_pos the logical character position which visual position should be get + * @param is_rtl tell the char at `logical_pos` is RTL or LTR context + * @return the visual character position + */ +uint16_t _lv_bidi_get_visual_pos(const char * str_in, char ** bidi_txt, uint16_t len, lv_base_dir_t base_dir, + uint32_t logical_pos, bool * is_rtl) +{ + uint32_t pos_conv_len = get_txt_len(str_in, len); + char * buf = lv_mem_buf_get(len + 1); + if(buf == NULL) return (uint16_t) -1; + + uint16_t * pos_conv_buf = lv_mem_buf_get(pos_conv_len * sizeof(uint16_t)); + if(pos_conv_buf == NULL) { + lv_mem_buf_release(buf); + return (uint16_t) -1; + } + + if(bidi_txt) *bidi_txt = buf; + + _lv_bidi_process_paragraph(str_in, bidi_txt ? *bidi_txt : NULL, len, base_dir, pos_conv_buf, pos_conv_len); + + for(uint16_t i = 0; i < pos_conv_len; i++) { + if(GET_POS(pos_conv_buf[i]) == logical_pos) { + + if(is_rtl) *is_rtl = IS_RTL_POS(pos_conv_buf[i]); + lv_mem_buf_release(pos_conv_buf); + + if(bidi_txt == NULL) lv_mem_buf_release(buf); + return i; + } + } + lv_mem_buf_release(pos_conv_buf); + if(bidi_txt == NULL) lv_mem_buf_release(buf); + return (uint16_t) -1; +} + +/** + * Bidi process a paragraph of text + * @param str_in the string to process + * @param str_out store the result here + * @param len length of the text + * @param base_dir base dir of the text + * @param pos_conv_out an `uint16_t` array to store the related logical position of the character. + * Can be `NULL` is unused + * @param pos_conv_len length of `pos_conv_out` in element count + */ +void _lv_bidi_process_paragraph(const char * str_in, char * str_out, uint32_t len, lv_base_dir_t base_dir, + uint16_t * pos_conv_out, uint16_t pos_conv_len) +{ + uint32_t run_len = 0; + lv_base_dir_t run_dir; + uint32_t rd = 0; + uint32_t wr; + uint16_t pos_conv_run_len = 0; + uint16_t pos_conv_rd = 0; + uint16_t pos_conv_wr; + + if(base_dir == LV_BASE_DIR_AUTO) base_dir = _lv_bidi_detect_base_dir(str_in); + if(base_dir == LV_BASE_DIR_RTL) { + wr = len; + pos_conv_wr = pos_conv_len; + } + else { + wr = 0; + pos_conv_wr = 0; + } + + if(str_out) str_out[len] = '\0'; + + lv_base_dir_t dir = base_dir; + + /*Empty the bracket stack*/ + br_stack_p = 0; + + /*Process neutral chars in the beginning*/ + while(rd < len) { + uint32_t letter = _lv_txt_encoded_next(str_in, &rd); + pos_conv_rd++; + dir = lv_bidi_get_letter_dir(letter); + if(dir == LV_BASE_DIR_NEUTRAL) dir = bracket_process(str_in, rd, len, letter, base_dir); + if(dir != LV_BASE_DIR_NEUTRAL && dir != LV_BASE_DIR_WEAK) break; + } + + if(rd && str_in[rd] != '\0') { + _lv_txt_encoded_prev(str_in, &rd); + pos_conv_rd--; + } + + if(rd) { + if(base_dir == LV_BASE_DIR_LTR) { + if(str_out) { + lv_memcpy(&str_out[wr], str_in, rd); + wr += rd; + } + if(pos_conv_out) { + fill_pos_conv(&pos_conv_out[pos_conv_wr], pos_conv_rd, 0); + pos_conv_wr += pos_conv_rd; + } + } + else { + wr -= rd; + pos_conv_wr -= pos_conv_rd; + rtl_reverse(str_out ? &str_out[wr] : NULL, str_in, rd, pos_conv_out ? &pos_conv_out[pos_conv_wr] : NULL, 0, + pos_conv_rd); + } + } + + /*Get and process the runs*/ + + while(rd < len && str_in[rd]) { + run_dir = get_next_run(&str_in[rd], base_dir, len - rd, &run_len, &pos_conv_run_len); + + if(base_dir == LV_BASE_DIR_LTR) { + if(run_dir == LV_BASE_DIR_LTR) { + if(str_out) lv_memcpy(&str_out[wr], &str_in[rd], run_len); + if(pos_conv_out) fill_pos_conv(&pos_conv_out[pos_conv_wr], pos_conv_run_len, pos_conv_rd); + } + else rtl_reverse(str_out ? &str_out[wr] : NULL, &str_in[rd], run_len, pos_conv_out ? &pos_conv_out[pos_conv_wr] : NULL, + pos_conv_rd, pos_conv_run_len); + wr += run_len; + pos_conv_wr += pos_conv_run_len; + } + else { + wr -= run_len; + pos_conv_wr -= pos_conv_run_len; + if(run_dir == LV_BASE_DIR_LTR) { + if(str_out) lv_memcpy(&str_out[wr], &str_in[rd], run_len); + if(pos_conv_out) fill_pos_conv(&pos_conv_out[pos_conv_wr], pos_conv_run_len, pos_conv_rd); + } + else rtl_reverse(str_out ? &str_out[wr] : NULL, &str_in[rd], run_len, pos_conv_out ? &pos_conv_out[pos_conv_wr] : NULL, + pos_conv_rd, pos_conv_run_len); + } + + rd += run_len; + pos_conv_rd += pos_conv_run_len; + } +} + +void lv_bidi_calculate_align(lv_text_align_t * align, lv_base_dir_t * base_dir, const char * txt) +{ + if(*base_dir == LV_BASE_DIR_AUTO) *base_dir = _lv_bidi_detect_base_dir(txt); + + if(*align == LV_TEXT_ALIGN_AUTO) { + if(*base_dir == LV_BASE_DIR_RTL) *align = LV_TEXT_ALIGN_RIGHT; + else *align = LV_TEXT_ALIGN_LEFT; + } +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +/** + * Get the next paragraph from a text + * @param txt the text to process + * @return the length of the current paragraph in byte count + */ +static uint32_t lv_bidi_get_next_paragraph(const char * txt) +{ + uint32_t i = 0; + + _lv_txt_encoded_next(txt, &i); + + while(txt[i] != '\0' && txt[i] != '\n' && txt[i] != '\r') { + _lv_txt_encoded_next(txt, &i); + } + + return i; +} + +/** + * Get the direction of a character + * @param letter a Unicode character + * @return `LV_BASE_DIR_RTL/LTR/WEAK/NEUTRAL` + */ +static lv_base_dir_t lv_bidi_get_letter_dir(uint32_t letter) +{ + if(lv_bidi_letter_is_rtl(letter)) return LV_BASE_DIR_RTL; + if(lv_bidi_letter_is_neutral(letter)) return LV_BASE_DIR_NEUTRAL; + if(lv_bidi_letter_is_weak(letter)) return LV_BASE_DIR_WEAK; + + return LV_BASE_DIR_LTR; +} +/** + * Tell whether a character is weak or not + * @param letter a Unicode character + * @return true/false + */ +static bool lv_bidi_letter_is_weak(uint32_t letter) +{ + uint32_t i = 0; + static const char weaks[] = "0123456789"; + + do { + uint32_t x = _lv_txt_encoded_next(weaks, &i); + if(letter == x) { + return true; + } + } while(weaks[i] != '\0'); + + return false; +} +/** + * Tell whether a character is RTL or not + * @param letter a Unicode character + * @return true/false + */ +static bool lv_bidi_letter_is_rtl(uint32_t letter) +{ + if(letter >= 0x5d0 && letter <= 0x5ea) return true; + if(letter == 0x202E) return true; /*Unicode of LV_BIDI_RLO*/ + + /*Check for Persian and Arabic characters [https://en.wikipedia.org/wiki/Arabic_script_in_Unicode]*/ + if(letter >= 0x600 && letter <= 0x6FF) return true; + if(letter >= 0xFB50 && letter <= 0xFDFF) return true; + if(letter >= 0xFE70 && letter <= 0xFEFF) return true; + + return false; +} + +/** + * Tell whether a character is neutral or not + * @param letter a Unicode character + * @return true/false + */ +static bool lv_bidi_letter_is_neutral(uint32_t letter) +{ + uint16_t i; + static const char neutrals[] = " \t\n\r.,:;'\"`!?%/\\-=()[]{}<>@#&$|"; + for(i = 0; neutrals[i] != '\0'; i++) { + if(letter == (uint32_t)neutrals[i]) return true; + } + + return false; +} + +static uint32_t get_txt_len(const char * txt, uint32_t max_len) +{ + uint32_t len = 0; + uint32_t i = 0; + + while(i < max_len && txt[i] != '\0') { + _lv_txt_encoded_next(txt, &i); + len++; + } + + return len; +} + +static void fill_pos_conv(uint16_t * out, uint16_t len, uint16_t index) +{ + uint16_t i; + for(i = 0; i < len; i++) { + out[i] = SET_RTL_POS(index, false); + index++; + } +} + +static lv_base_dir_t get_next_run(const char * txt, lv_base_dir_t base_dir, uint32_t max_len, uint32_t * len, + uint16_t * pos_conv_len) +{ + uint32_t i = 0; + uint32_t letter; + + uint16_t pos_conv_i = 0; + + letter = _lv_txt_encoded_next(txt, NULL); + lv_base_dir_t dir = lv_bidi_get_letter_dir(letter); + if(dir == LV_BASE_DIR_NEUTRAL) dir = bracket_process(txt, 0, max_len, letter, base_dir); + + /*Find the first strong char. Skip the neutrals*/ + while(dir == LV_BASE_DIR_NEUTRAL || dir == LV_BASE_DIR_WEAK) { + letter = _lv_txt_encoded_next(txt, &i); + + pos_conv_i++; + dir = lv_bidi_get_letter_dir(letter); + if(dir == LV_BASE_DIR_NEUTRAL) dir = bracket_process(txt, i, max_len, letter, base_dir); + + if(dir == LV_BASE_DIR_LTR || dir == LV_BASE_DIR_RTL) break; + + if(i >= max_len || txt[i] == '\0' || txt[i] == '\n' || txt[i] == '\r') { + *len = i; + *pos_conv_len = pos_conv_i; + return base_dir; + } + } + + lv_base_dir_t run_dir = dir; + + uint32_t i_prev = i; + uint32_t i_last_strong = i; + uint16_t pos_conv_i_prev = pos_conv_i; + uint16_t pos_conv_i_last_strong = pos_conv_i; + + /*Find the next char which has different direction*/ + lv_base_dir_t next_dir = base_dir; + while(i_prev < max_len && txt[i] != '\0' && txt[i] != '\n' && txt[i] != '\r') { + letter = _lv_txt_encoded_next(txt, &i); + pos_conv_i++; + next_dir = lv_bidi_get_letter_dir(letter); + if(next_dir == LV_BASE_DIR_NEUTRAL) next_dir = bracket_process(txt, i, max_len, letter, base_dir); + + if(next_dir == LV_BASE_DIR_WEAK) { + if(run_dir == LV_BASE_DIR_RTL) { + if(base_dir == LV_BASE_DIR_RTL) { + next_dir = LV_BASE_DIR_LTR; + } + } + } + + /*New dir found?*/ + if((next_dir == LV_BASE_DIR_RTL || next_dir == LV_BASE_DIR_LTR) && next_dir != run_dir) { + /*Include neutrals if `run_dir == base_dir`*/ + if(run_dir == base_dir) { + *len = i_prev; + *pos_conv_len = pos_conv_i_prev; + } + /*Exclude neutrals if `run_dir != base_dir`*/ + else { + *len = i_last_strong; + *pos_conv_len = pos_conv_i_last_strong; + } + + return run_dir; + } + + if(next_dir != LV_BASE_DIR_NEUTRAL) { + i_last_strong = i; + pos_conv_i_last_strong = pos_conv_i; + } + + i_prev = i; + pos_conv_i_prev = pos_conv_i; + } + + /*Handle end of of string. Apply `base_dir` on trailing neutrals*/ + + /*Include neutrals if `run_dir == base_dir`*/ + if(run_dir == base_dir) { + *len = i_prev; + *pos_conv_len = pos_conv_i_prev; + } + /*Exclude neutrals if `run_dir != base_dir`*/ + else { + *len = i_last_strong; + *pos_conv_len = pos_conv_i_last_strong; + } + + return run_dir; +} + +static void rtl_reverse(char * dest, const char * src, uint32_t len, uint16_t * pos_conv_out, uint16_t pos_conv_rd_base, + uint16_t pos_conv_len) +{ + uint32_t i = len; + uint32_t wr = 0; + uint16_t pos_conv_i = pos_conv_len; + uint16_t pos_conv_wr = 0; + + while(i) { + uint32_t letter = _lv_txt_encoded_prev(src, &i); + uint16_t pos_conv_letter = --pos_conv_i; + + /*Keep weak letters (numbers) as LTR*/ + if(lv_bidi_letter_is_weak(letter)) { + uint32_t last_weak = i; + uint32_t first_weak = i; + uint16_t pos_conv_last_weak = pos_conv_i; + uint16_t pos_conv_first_weak = pos_conv_i; + while(i) { + letter = _lv_txt_encoded_prev(src, &i); + pos_conv_letter = --pos_conv_i; + + /*No need to call `char_change_to_pair` because there not such chars here*/ + + /*Finish on non-weak char*/ + /*but treat number and currency related chars as weak*/ + if(lv_bidi_letter_is_weak(letter) == false && letter != '.' && letter != ',' && letter != '$' && letter != '%') { + _lv_txt_encoded_next(src, &i); /*Rewind one letter*/ + pos_conv_i++; + first_weak = i; + pos_conv_first_weak = pos_conv_i; + break; + } + } + if(i == 0) { + first_weak = 0; + pos_conv_first_weak = 0; + } + + if(dest) lv_memcpy(&dest[wr], &src[first_weak], last_weak - first_weak + 1); + if(pos_conv_out) fill_pos_conv(&pos_conv_out[pos_conv_wr], pos_conv_last_weak - pos_conv_first_weak + 1, + pos_conv_rd_base + pos_conv_first_weak); + wr += last_weak - first_weak + 1; + pos_conv_wr += pos_conv_last_weak - pos_conv_first_weak + 1; + } + + /*Simply store in reversed order*/ + else { + uint32_t letter_size = _lv_txt_encoded_size((const char *)&src[i]); + /*Swap arithmetical symbols*/ + if(letter_size == 1) { + uint32_t new_letter = letter = char_change_to_pair(letter); + if(dest) dest[wr] = (uint8_t)new_letter; + if(pos_conv_out) pos_conv_out[pos_conv_wr] = SET_RTL_POS(pos_conv_rd_base + pos_conv_letter, true); + wr++; + pos_conv_wr++; + } + /*Just store the letter*/ + else { + if(dest) lv_memcpy(&dest[wr], &src[i], letter_size); + if(pos_conv_out) pos_conv_out[pos_conv_wr] = SET_RTL_POS(pos_conv_rd_base + pos_conv_i, true); + wr += letter_size; + pos_conv_wr++; + } + } + } +} + +static uint32_t char_change_to_pair(uint32_t letter) +{ + + uint8_t i; + for(i = 0; bracket_left[i] != '\0'; i++) { + if(letter == bracket_left[i]) return bracket_right[i]; + } + + for(i = 0; bracket_right[i] != '\0'; i++) { + if(letter == bracket_right[i]) return bracket_left[i]; + } + + return letter; +} + +static lv_base_dir_t bracket_process(const char * txt, uint32_t next_pos, uint32_t len, uint32_t letter, + lv_base_dir_t base_dir) +{ + lv_base_dir_t bracket_dir = LV_BASE_DIR_NEUTRAL; + + uint8_t i; + /*Is the letter an opening bracket?*/ + for(i = 0; bracket_left[i] != '\0'; i++) { + if(bracket_left[i] == letter) { + /*If so find its matching closing bracket. + *If a char with base dir. direction is found then the brackets will have `base_dir` direction*/ + uint32_t txt_i = next_pos; + while(txt_i < len) { + uint32_t letter_next = _lv_txt_encoded_next(txt, &txt_i); + if(letter_next == bracket_right[i]) { + /*Closing bracket found*/ + break; + } + else { + /*Save the dir*/ + lv_base_dir_t letter_dir = lv_bidi_get_letter_dir(letter_next); + if(letter_dir == base_dir) { + bracket_dir = base_dir; + } + } + } + + /*There were no matching closing bracket*/ + if(txt_i > len) return LV_BASE_DIR_NEUTRAL; + + /*There where a strong char with base dir in the bracket so the dir is found.*/ + if(bracket_dir != LV_BASE_DIR_NEUTRAL && bracket_dir != LV_BASE_DIR_WEAK) break; + + /*If there were no matching strong chars in the brackets then check the previous chars*/ + txt_i = next_pos; + if(txt_i) _lv_txt_encoded_prev(txt, &txt_i); + while(txt_i > 0) { + uint32_t letter_next = _lv_txt_encoded_prev(txt, &txt_i); + lv_base_dir_t letter_dir = lv_bidi_get_letter_dir(letter_next); + if(letter_dir == LV_BASE_DIR_LTR || letter_dir == LV_BASE_DIR_RTL) { + bracket_dir = letter_dir; + break; + } + } + + /*There where a previous strong char which can be used*/ + if(bracket_dir != LV_BASE_DIR_NEUTRAL) break; + + /*There were no strong chars before the bracket, so use the base dir.*/ + if(txt_i == 0) bracket_dir = base_dir; + + break; + } + } + + /*The letter was an opening bracket*/ + if(bracket_left[i] != '\0') { + + if(bracket_dir == LV_BASE_DIR_NEUTRAL || br_stack_p == LV_BIDI_BRACKLET_DEPTH) return LV_BASE_DIR_NEUTRAL; + + br_stack[br_stack_p].bracklet_pos = i; + br_stack[br_stack_p].dir = bracket_dir; + + br_stack_p++; + return bracket_dir; + } + else if(br_stack_p > 0) { + /*Is the letter a closing bracket of the last opening?*/ + if(letter == bracket_right[br_stack[br_stack_p - 1].bracklet_pos]) { + bracket_dir = br_stack[br_stack_p - 1].dir; + br_stack_p--; + return bracket_dir; + } + } + + return LV_BASE_DIR_NEUTRAL; +} + +#endif /*LV_USE_BIDI*/ diff --git a/lib/lvgl/src/misc/lv_bidi.h b/lib/lvgl/src/misc/lv_bidi.h new file mode 100644 index 00000000..a27b5808 --- /dev/null +++ b/lib/lvgl/src/misc/lv_bidi.h @@ -0,0 +1,141 @@ +/** + * @file lv_bidi.h + * + */ + +#ifndef LV_BIDI_H +#define LV_BIDI_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ +#include "../lv_conf_internal.h" + +#include +#include +#include "lv_txt.h" + +/********************* + * DEFINES + *********************/ +/*Special non printable strong characters. + *They can be inserted to texts to affect the run's direction*/ +#define LV_BIDI_LRO "\xE2\x80\xAD" /*U+202D*/ +#define LV_BIDI_RLO "\xE2\x80\xAE" /*U+202E*/ + +/********************** + * TYPEDEFS + **********************/ +enum { + LV_BASE_DIR_LTR = 0x00, + LV_BASE_DIR_RTL = 0x01, + LV_BASE_DIR_AUTO = 0x02, + + LV_BASE_DIR_NEUTRAL = 0x20, + LV_BASE_DIR_WEAK = 0x21, +}; + +typedef uint8_t lv_base_dir_t; + +/********************** + * GLOBAL PROTOTYPES + **********************/ +#if LV_USE_BIDI + +/** + * Convert a text to get the characters in the correct visual order according to + * Unicode Bidirectional Algorithm + * @param str_in the text to process + * @param str_out store the result here. Has the be `strlen(str_in)` length + * @param base_dir `LV_BASE_DIR_LTR` or `LV_BASE_DIR_RTL` + */ +void _lv_bidi_process(const char * str_in, char * str_out, lv_base_dir_t base_dir); + +/** + * Auto-detect the direction of a text based on the first strong character + * @param txt the text to process + * @return `LV_BASE_DIR_LTR` or `LV_BASE_DIR_RTL` + */ +lv_base_dir_t _lv_bidi_detect_base_dir(const char * txt); + +/** + * Get the logical position of a character in a line + * @param str_in the input string. Can be only one line. + * @param bidi_txt internally the text is bidi processed which buffer can be get here. + * If not required anymore has to freed with `lv_mem_free()` + * Can be `NULL` is unused + * @param len length of the line in character count + * @param base_dir base direction of the text: `LV_BASE_DIR_LTR` or `LV_BASE_DIR_RTL` + * @param visual_pos the visual character position which logical position should be get + * @param is_rtl tell the char at `visual_pos` is RTL or LTR context + * @return the logical character position + */ +uint16_t _lv_bidi_get_logical_pos(const char * str_in, char ** bidi_txt, uint32_t len, lv_base_dir_t base_dir, + uint32_t visual_pos, bool * is_rtl); + +/** + * Get the visual position of a character in a line + * @param str_in the input string. Can be only one line. + * @param bidi_txt internally the text is bidi processed which buffer can be get here. + * If not required anymore has to freed with `lv_mem_free()` + * Can be `NULL` is unused + * @param len length of the line in character count + * @param base_dir base direction of the text: `LV_BASE_DIR_LTR` or `LV_BASE_DIR_RTL` + * @param logical_pos the logical character position which visual position should be get + * @param is_rtl tell the char at `logical_pos` is RTL or LTR context + * @return the visual character position + */ +uint16_t _lv_bidi_get_visual_pos(const char * str_in, char ** bidi_txt, uint16_t len, lv_base_dir_t base_dir, + uint32_t logical_pos, bool * is_rtl); + +/** + * Bidi process a paragraph of text + * @param str_in the string to process + * @param str_out store the result here + * @param len length of the text + * @param base_dir base dir of the text + * @param pos_conv_out an `uint16_t` array to store the related logical position of the character. + * Can be `NULL` is unused + * @param pos_conv_len length of `pos_conv_out` in element count + */ +void _lv_bidi_process_paragraph(const char * str_in, char * str_out, uint32_t len, lv_base_dir_t base_dir, + uint16_t * pos_conv_out, uint16_t pos_conv_len); + +/** + * Get the real text alignment from the a text alignment, base direction and a text. + * @param align LV_TEXT_ALIGN_..., write back the calculated align here (LV_TEXT_ALIGN_LEFT/RIGHT/CENTER) + * @param base_dir LV_BASE_DIR_..., write the calculated base dir here (LV_BASE_DIR_LTR/RTL) + * @param txt a text, used with LV_BASE_DIR_AUTO to determine the base direction + */ +void lv_bidi_calculate_align(lv_text_align_t * align, lv_base_dir_t * base_dir, const char * txt); + + +/********************** + * MACROS + **********************/ + +#else /*LV_USE_BIDI*/ +/** + * For compatibility if LV_USE_BIDI = 0 + * Get the real text alignment from the a text alignment, base direction and a text. + * @param align For LV_TEXT_ALIGN_AUTO give LV_TEXT_ALIGN_LEFT else leave unchanged, write back the calculated align here + * @param base_dir Unused + * @param txt Unused + */ +static inline void lv_bidi_calculate_align(lv_text_align_t * align, lv_base_dir_t * base_dir, const char * txt) +{ + LV_UNUSED(txt); + LV_UNUSED(base_dir); + if(*align == LV_TEXT_ALIGN_AUTO) * align = LV_TEXT_ALIGN_LEFT; +} +#endif /*LV_USE_BIDI*/ + +#ifdef __cplusplus +} /*extern "C"*/ +#endif + +#endif /*LV_BIDI_H*/ diff --git a/lib/lvgl/src/misc/lv_color.c b/lib/lvgl/src/misc/lv_color.c new file mode 100644 index 00000000..0e26624f --- /dev/null +++ b/lib/lvgl/src/misc/lv_color.c @@ -0,0 +1,369 @@ +/** + * @file lv_color.c + * + */ + +/********************* + * INCLUDES + *********************/ +#include "lv_color.h" +#include "lv_log.h" + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ + +/********************** + * STATIC VARIABLES + **********************/ + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +LV_ATTRIBUTE_FAST_MEM void lv_color_fill(lv_color_t * buf, lv_color_t color, uint32_t px_num) +{ +#if LV_COLOR_DEPTH == 16 + uintptr_t buf_int = (uintptr_t)buf; + if(buf_int & 0x3) { + *buf = color; + buf++; + px_num--; + } + + uint32_t c32 = (uint32_t)color.full + ((uint32_t)color.full << 16); + uint32_t * buf32 = (uint32_t *)buf; + + while(px_num > 16) { + *buf32 = c32; + buf32++; + *buf32 = c32; + buf32++; + *buf32 = c32; + buf32++; + *buf32 = c32; + buf32++; + + *buf32 = c32; + buf32++; + *buf32 = c32; + buf32++; + *buf32 = c32; + buf32++; + *buf32 = c32; + buf32++; + + px_num -= 16; + } + + buf = (lv_color_t *)buf32; + + while(px_num) { + *buf = color; + buf++; + px_num--; + } +#else + while(px_num > 16) { + *buf = color; + buf++; + *buf = color; + buf++; + *buf = color; + buf++; + *buf = color; + buf++; + + *buf = color; + buf++; + *buf = color; + buf++; + *buf = color; + buf++; + *buf = color; + buf++; + + *buf = color; + buf++; + *buf = color; + buf++; + *buf = color; + buf++; + *buf = color; + buf++; + + *buf = color; + buf++; + *buf = color; + buf++; + *buf = color; + buf++; + *buf = color; + buf++; + + px_num -= 16; + } + while(px_num) { + *buf = color; + buf++; + px_num--; + } +#endif +} + +lv_color_t lv_color_lighten(lv_color_t c, lv_opa_t lvl) +{ + return lv_color_mix(lv_color_white(), c, lvl); +} + +lv_color_t lv_color_darken(lv_color_t c, lv_opa_t lvl) +{ + return lv_color_mix(lv_color_black(), c, lvl); +} + +lv_color_t lv_color_change_lightness(lv_color_t c, lv_opa_t lvl) +{ + /*It'd be better to convert the color to HSL, change L and convert back to RGB.*/ + if(lvl == LV_OPA_50) return c; + else if(lvl < LV_OPA_50) return lv_color_darken(c, (LV_OPA_50 - lvl) * 2); + else return lv_color_lighten(c, (lvl - LV_OPA_50) * 2); +} + +/** + * Convert a HSV color to RGB + * @param h hue [0..359] + * @param s saturation [0..100] + * @param v value [0..100] + * @return the given RGB color in RGB (with LV_COLOR_DEPTH depth) + */ +lv_color_t lv_color_hsv_to_rgb(uint16_t h, uint8_t s, uint8_t v) +{ + h = (uint32_t)((uint32_t)h * 255) / 360; + s = (uint16_t)((uint16_t)s * 255) / 100; + v = (uint16_t)((uint16_t)v * 255) / 100; + + uint8_t r, g, b; + + uint8_t region, remainder, p, q, t; + + if(s == 0) { + return lv_color_make(v, v, v); + } + + region = h / 43; + remainder = (h - (region * 43)) * 6; + + p = (v * (255 - s)) >> 8; + q = (v * (255 - ((s * remainder) >> 8))) >> 8; + t = (v * (255 - ((s * (255 - remainder)) >> 8))) >> 8; + + switch(region) { + case 0: + r = v; + g = t; + b = p; + break; + case 1: + r = q; + g = v; + b = p; + break; + case 2: + r = p; + g = v; + b = t; + break; + case 3: + r = p; + g = q; + b = v; + break; + case 4: + r = t; + g = p; + b = v; + break; + default: + r = v; + g = p; + b = q; + break; + } + + lv_color_t result = lv_color_make(r, g, b); + return result; +} + +/** + * Convert a 32-bit RGB color to HSV + * @param r8 8-bit red + * @param g8 8-bit green + * @param b8 8-bit blue + * @return the given RGB color in HSV + */ +lv_color_hsv_t lv_color_rgb_to_hsv(uint8_t r8, uint8_t g8, uint8_t b8) +{ + uint16_t r = ((uint32_t)r8 << 10) / 255; + uint16_t g = ((uint32_t)g8 << 10) / 255; + uint16_t b = ((uint32_t)b8 << 10) / 255; + + uint16_t rgbMin = r < g ? (r < b ? r : b) : (g < b ? g : b); + uint16_t rgbMax = r > g ? (r > b ? r : b) : (g > b ? g : b); + + lv_color_hsv_t hsv; + + // https://en.wikipedia.org/wiki/HSL_and_HSV#Lightness + hsv.v = (100 * rgbMax) >> 10; + + int32_t delta = rgbMax - rgbMin; + if(delta < 3) { + hsv.h = 0; + hsv.s = 0; + return hsv; + } + + // https://en.wikipedia.org/wiki/HSL_and_HSV#Saturation + hsv.s = 100 * delta / rgbMax; + if(hsv.s < 3) { + hsv.h = 0; + return hsv; + } + + // https://en.wikipedia.org/wiki/HSL_and_HSV#Hue_and_chroma + int32_t h; + if(rgbMax == r) + h = (((g - b) << 10) / delta) + (g < b ? (6 << 10) : 0); // between yellow & magenta + else if(rgbMax == g) + h = (((b - r) << 10) / delta) + (2 << 10); // between cyan & yellow + else if(rgbMax == b) + h = (((r - g) << 10) / delta) + (4 << 10); // between magenta & cyan + else + h = 0; + h *= 60; + h >>= 10; + if(h < 0) h += 360; + + hsv.h = h; + return hsv; +} + +/** + * Convert a color to HSV + * @param color color + * @return the given color in HSV + */ +lv_color_hsv_t lv_color_to_hsv(lv_color_t color) +{ + lv_color32_t color32; + color32.full = lv_color_to32(color); + return lv_color_rgb_to_hsv(color32.ch.red, color32.ch.green, color32.ch.blue); +} + +lv_color_t lv_palette_main(lv_palette_t p) +{ + static const lv_color_t colors[] = { + LV_COLOR_MAKE(0xF4, 0x43, 0x36), LV_COLOR_MAKE(0xE9, 0x1E, 0x63), LV_COLOR_MAKE(0x9C, 0x27, 0xB0), LV_COLOR_MAKE(0x67, 0x3A, 0xB7), + LV_COLOR_MAKE(0x3F, 0x51, 0xB5), LV_COLOR_MAKE(0x21, 0x96, 0xF3), LV_COLOR_MAKE(0x03, 0xA9, 0xF4), LV_COLOR_MAKE(0x00, 0xBC, 0xD4), + LV_COLOR_MAKE(0x00, 0x96, 0x88), LV_COLOR_MAKE(0x4C, 0xAF, 0x50), LV_COLOR_MAKE(0x8B, 0xC3, 0x4A), LV_COLOR_MAKE(0xCD, 0xDC, 0x39), + LV_COLOR_MAKE(0xFF, 0xEB, 0x3B), LV_COLOR_MAKE(0xFF, 0xC1, 0x07), LV_COLOR_MAKE(0xFF, 0x98, 0x00), LV_COLOR_MAKE(0xFF, 0x57, 0x22), + LV_COLOR_MAKE(0x79, 0x55, 0x48), LV_COLOR_MAKE(0x60, 0x7D, 0x8B), LV_COLOR_MAKE(0x9E, 0x9E, 0x9E) + }; + + if(p >= _LV_PALETTE_LAST) { + LV_LOG_WARN("Invalid palette: %d", p); + return lv_color_black(); + } + + return colors[p]; + +} + +lv_color_t lv_palette_lighten(lv_palette_t p, uint8_t lvl) +{ + static const lv_color_t colors[][5] = { + {LV_COLOR_MAKE(0xEF, 0x53, 0x50), LV_COLOR_MAKE(0xE5, 0x73, 0x73), LV_COLOR_MAKE(0xEF, 0x9A, 0x9A), LV_COLOR_MAKE(0xFF, 0xCD, 0xD2), LV_COLOR_MAKE(0xFF, 0xEB, 0xEE)}, + {LV_COLOR_MAKE(0xEC, 0x40, 0x7A), LV_COLOR_MAKE(0xF0, 0x62, 0x92), LV_COLOR_MAKE(0xF4, 0x8F, 0xB1), LV_COLOR_MAKE(0xF8, 0xBB, 0xD0), LV_COLOR_MAKE(0xFC, 0xE4, 0xEC)}, + {LV_COLOR_MAKE(0xAB, 0x47, 0xBC), LV_COLOR_MAKE(0xBA, 0x68, 0xC8), LV_COLOR_MAKE(0xCE, 0x93, 0xD8), LV_COLOR_MAKE(0xE1, 0xBE, 0xE7), LV_COLOR_MAKE(0xF3, 0xE5, 0xF5)}, + {LV_COLOR_MAKE(0x7E, 0x57, 0xC2), LV_COLOR_MAKE(0x95, 0x75, 0xCD), LV_COLOR_MAKE(0xB3, 0x9D, 0xDB), LV_COLOR_MAKE(0xD1, 0xC4, 0xE9), LV_COLOR_MAKE(0xED, 0xE7, 0xF6)}, + {LV_COLOR_MAKE(0x5C, 0x6B, 0xC0), LV_COLOR_MAKE(0x79, 0x86, 0xCB), LV_COLOR_MAKE(0x9F, 0xA8, 0xDA), LV_COLOR_MAKE(0xC5, 0xCA, 0xE9), LV_COLOR_MAKE(0xE8, 0xEA, 0xF6)}, + {LV_COLOR_MAKE(0x42, 0xA5, 0xF5), LV_COLOR_MAKE(0x64, 0xB5, 0xF6), LV_COLOR_MAKE(0x90, 0xCA, 0xF9), LV_COLOR_MAKE(0xBB, 0xDE, 0xFB), LV_COLOR_MAKE(0xE3, 0xF2, 0xFD)}, + {LV_COLOR_MAKE(0x29, 0xB6, 0xF6), LV_COLOR_MAKE(0x4F, 0xC3, 0xF7), LV_COLOR_MAKE(0x81, 0xD4, 0xFA), LV_COLOR_MAKE(0xB3, 0xE5, 0xFC), LV_COLOR_MAKE(0xE1, 0xF5, 0xFE)}, + {LV_COLOR_MAKE(0x26, 0xC6, 0xDA), LV_COLOR_MAKE(0x4D, 0xD0, 0xE1), LV_COLOR_MAKE(0x80, 0xDE, 0xEA), LV_COLOR_MAKE(0xB2, 0xEB, 0xF2), LV_COLOR_MAKE(0xE0, 0xF7, 0xFA)}, + {LV_COLOR_MAKE(0x26, 0xA6, 0x9A), LV_COLOR_MAKE(0x4D, 0xB6, 0xAC), LV_COLOR_MAKE(0x80, 0xCB, 0xC4), LV_COLOR_MAKE(0xB2, 0xDF, 0xDB), LV_COLOR_MAKE(0xE0, 0xF2, 0xF1)}, + {LV_COLOR_MAKE(0x66, 0xBB, 0x6A), LV_COLOR_MAKE(0x81, 0xC7, 0x84), LV_COLOR_MAKE(0xA5, 0xD6, 0xA7), LV_COLOR_MAKE(0xC8, 0xE6, 0xC9), LV_COLOR_MAKE(0xE8, 0xF5, 0xE9)}, + {LV_COLOR_MAKE(0x9C, 0xCC, 0x65), LV_COLOR_MAKE(0xAE, 0xD5, 0x81), LV_COLOR_MAKE(0xC5, 0xE1, 0xA5), LV_COLOR_MAKE(0xDC, 0xED, 0xC8), LV_COLOR_MAKE(0xF1, 0xF8, 0xE9)}, + {LV_COLOR_MAKE(0xD4, 0xE1, 0x57), LV_COLOR_MAKE(0xDC, 0xE7, 0x75), LV_COLOR_MAKE(0xE6, 0xEE, 0x9C), LV_COLOR_MAKE(0xF0, 0xF4, 0xC3), LV_COLOR_MAKE(0xF9, 0xFB, 0xE7)}, + {LV_COLOR_MAKE(0xFF, 0xEE, 0x58), LV_COLOR_MAKE(0xFF, 0xF1, 0x76), LV_COLOR_MAKE(0xFF, 0xF5, 0x9D), LV_COLOR_MAKE(0xFF, 0xF9, 0xC4), LV_COLOR_MAKE(0xFF, 0xFD, 0xE7)}, + {LV_COLOR_MAKE(0xFF, 0xCA, 0x28), LV_COLOR_MAKE(0xFF, 0xD5, 0x4F), LV_COLOR_MAKE(0xFF, 0xE0, 0x82), LV_COLOR_MAKE(0xFF, 0xEC, 0xB3), LV_COLOR_MAKE(0xFF, 0xF8, 0xE1)}, + {LV_COLOR_MAKE(0xFF, 0xA7, 0x26), LV_COLOR_MAKE(0xFF, 0xB7, 0x4D), LV_COLOR_MAKE(0xFF, 0xCC, 0x80), LV_COLOR_MAKE(0xFF, 0xE0, 0xB2), LV_COLOR_MAKE(0xFF, 0xF3, 0xE0)}, + {LV_COLOR_MAKE(0xFF, 0x70, 0x43), LV_COLOR_MAKE(0xFF, 0x8A, 0x65), LV_COLOR_MAKE(0xFF, 0xAB, 0x91), LV_COLOR_MAKE(0xFF, 0xCC, 0xBC), LV_COLOR_MAKE(0xFB, 0xE9, 0xE7)}, + {LV_COLOR_MAKE(0x8D, 0x6E, 0x63), LV_COLOR_MAKE(0xA1, 0x88, 0x7F), LV_COLOR_MAKE(0xBC, 0xAA, 0xA4), LV_COLOR_MAKE(0xD7, 0xCC, 0xC8), LV_COLOR_MAKE(0xEF, 0xEB, 0xE9)}, + {LV_COLOR_MAKE(0x78, 0x90, 0x9C), LV_COLOR_MAKE(0x90, 0xA4, 0xAE), LV_COLOR_MAKE(0xB0, 0xBE, 0xC5), LV_COLOR_MAKE(0xCF, 0xD8, 0xDC), LV_COLOR_MAKE(0xEC, 0xEF, 0xF1)}, + {LV_COLOR_MAKE(0xBD, 0xBD, 0xBD), LV_COLOR_MAKE(0xE0, 0xE0, 0xE0), LV_COLOR_MAKE(0xEE, 0xEE, 0xEE), LV_COLOR_MAKE(0xF5, 0xF5, 0xF5), LV_COLOR_MAKE(0xFA, 0xFA, 0xFA)}, + }; + + if(p >= _LV_PALETTE_LAST) { + LV_LOG_WARN("Invalid palette: %d", p); + return lv_color_black(); + } + + if(lvl == 0 || lvl > 5) { + LV_LOG_WARN("Invalid level: %d. Must be 1..5", lvl); + return lv_color_black(); + } + + lvl--; + + return colors[p][lvl]; +} + +lv_color_t lv_palette_darken(lv_palette_t p, uint8_t lvl) +{ + static const lv_color_t colors[][4] = { + {LV_COLOR_MAKE(0xE5, 0x39, 0x35), LV_COLOR_MAKE(0xD3, 0x2F, 0x2F), LV_COLOR_MAKE(0xC6, 0x28, 0x28), LV_COLOR_MAKE(0xB7, 0x1C, 0x1C)}, + {LV_COLOR_MAKE(0xD8, 0x1B, 0x60), LV_COLOR_MAKE(0xC2, 0x18, 0x5B), LV_COLOR_MAKE(0xAD, 0x14, 0x57), LV_COLOR_MAKE(0x88, 0x0E, 0x4F)}, + {LV_COLOR_MAKE(0x8E, 0x24, 0xAA), LV_COLOR_MAKE(0x7B, 0x1F, 0xA2), LV_COLOR_MAKE(0x6A, 0x1B, 0x9A), LV_COLOR_MAKE(0x4A, 0x14, 0x8C)}, + {LV_COLOR_MAKE(0x5E, 0x35, 0xB1), LV_COLOR_MAKE(0x51, 0x2D, 0xA8), LV_COLOR_MAKE(0x45, 0x27, 0xA0), LV_COLOR_MAKE(0x31, 0x1B, 0x92)}, + {LV_COLOR_MAKE(0x39, 0x49, 0xAB), LV_COLOR_MAKE(0x30, 0x3F, 0x9F), LV_COLOR_MAKE(0x28, 0x35, 0x93), LV_COLOR_MAKE(0x1A, 0x23, 0x7E)}, + {LV_COLOR_MAKE(0x1E, 0x88, 0xE5), LV_COLOR_MAKE(0x19, 0x76, 0xD2), LV_COLOR_MAKE(0x15, 0x65, 0xC0), LV_COLOR_MAKE(0x0D, 0x47, 0xA1)}, + {LV_COLOR_MAKE(0x03, 0x9B, 0xE5), LV_COLOR_MAKE(0x02, 0x88, 0xD1), LV_COLOR_MAKE(0x02, 0x77, 0xBD), LV_COLOR_MAKE(0x01, 0x57, 0x9B)}, + {LV_COLOR_MAKE(0x00, 0xAC, 0xC1), LV_COLOR_MAKE(0x00, 0x97, 0xA7), LV_COLOR_MAKE(0x00, 0x83, 0x8F), LV_COLOR_MAKE(0x00, 0x60, 0x64)}, + {LV_COLOR_MAKE(0x00, 0x89, 0x7B), LV_COLOR_MAKE(0x00, 0x79, 0x6B), LV_COLOR_MAKE(0x00, 0x69, 0x5C), LV_COLOR_MAKE(0x00, 0x4D, 0x40)}, + {LV_COLOR_MAKE(0x43, 0xA0, 0x47), LV_COLOR_MAKE(0x38, 0x8E, 0x3C), LV_COLOR_MAKE(0x2E, 0x7D, 0x32), LV_COLOR_MAKE(0x1B, 0x5E, 0x20)}, + {LV_COLOR_MAKE(0x7C, 0xB3, 0x42), LV_COLOR_MAKE(0x68, 0x9F, 0x38), LV_COLOR_MAKE(0x55, 0x8B, 0x2F), LV_COLOR_MAKE(0x33, 0x69, 0x1E)}, + {LV_COLOR_MAKE(0xC0, 0xCA, 0x33), LV_COLOR_MAKE(0xAF, 0xB4, 0x2B), LV_COLOR_MAKE(0x9E, 0x9D, 0x24), LV_COLOR_MAKE(0x82, 0x77, 0x17)}, + {LV_COLOR_MAKE(0xFD, 0xD8, 0x35), LV_COLOR_MAKE(0xFB, 0xC0, 0x2D), LV_COLOR_MAKE(0xF9, 0xA8, 0x25), LV_COLOR_MAKE(0xF5, 0x7F, 0x17)}, + {LV_COLOR_MAKE(0xFF, 0xB3, 0x00), LV_COLOR_MAKE(0xFF, 0xA0, 0x00), LV_COLOR_MAKE(0xFF, 0x8F, 0x00), LV_COLOR_MAKE(0xFF, 0x6F, 0x00)}, + {LV_COLOR_MAKE(0xFB, 0x8C, 0x00), LV_COLOR_MAKE(0xF5, 0x7C, 0x00), LV_COLOR_MAKE(0xEF, 0x6C, 0x00), LV_COLOR_MAKE(0xE6, 0x51, 0x00)}, + {LV_COLOR_MAKE(0xF4, 0x51, 0x1E), LV_COLOR_MAKE(0xE6, 0x4A, 0x19), LV_COLOR_MAKE(0xD8, 0x43, 0x15), LV_COLOR_MAKE(0xBF, 0x36, 0x0C)}, + {LV_COLOR_MAKE(0x6D, 0x4C, 0x41), LV_COLOR_MAKE(0x5D, 0x40, 0x37), LV_COLOR_MAKE(0x4E, 0x34, 0x2E), LV_COLOR_MAKE(0x3E, 0x27, 0x23)}, + {LV_COLOR_MAKE(0x54, 0x6E, 0x7A), LV_COLOR_MAKE(0x45, 0x5A, 0x64), LV_COLOR_MAKE(0x37, 0x47, 0x4F), LV_COLOR_MAKE(0x26, 0x32, 0x38)}, + {LV_COLOR_MAKE(0x75, 0x75, 0x75), LV_COLOR_MAKE(0x61, 0x61, 0x61), LV_COLOR_MAKE(0x42, 0x42, 0x42), LV_COLOR_MAKE(0x21, 0x21, 0x21)}, + }; + + if(p >= _LV_PALETTE_LAST) { + LV_LOG_WARN("Invalid palette: %d", p); + return lv_color_black(); + } + + if(lvl == 0 || lvl > 4) { + LV_LOG_WARN("Invalid level: %d. Must be 1..4", lvl); + return lv_color_black(); + } + + lvl--; + + return colors[p][lvl]; +} diff --git a/lib/lvgl/src/misc/lv_color.h b/lib/lvgl/src/misc/lv_color.h new file mode 100644 index 00000000..2cc92f27 --- /dev/null +++ b/lib/lvgl/src/misc/lv_color.h @@ -0,0 +1,708 @@ +/** + * @file lv_color.h + * + */ + +#ifndef LV_COLOR_H +#define LV_COLOR_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ +#include "../lv_conf_internal.h" +#include "lv_assert.h" +#include "lv_math.h" +#include "lv_types.h" + +/*Error checking*/ +#if LV_COLOR_DEPTH == 24 +#error "LV_COLOR_DEPTH 24 is deprecated. Use LV_COLOR_DEPTH 32 instead (lv_conf.h)" +#endif + +#if LV_COLOR_DEPTH != 16 && LV_COLOR_16_SWAP != 0 +#error "LV_COLOR_16_SWAP requires LV_COLOR_DEPTH == 16. Set it in lv_conf.h" +#endif + +#include + +/********************* + * DEFINES + *********************/ +LV_EXPORT_CONST_INT(LV_COLOR_DEPTH); +LV_EXPORT_CONST_INT(LV_COLOR_16_SWAP); + +/** + * Opacity percentages. + */ +enum { + LV_OPA_TRANSP = 0, + LV_OPA_0 = 0, + LV_OPA_10 = 25, + LV_OPA_20 = 51, + LV_OPA_30 = 76, + LV_OPA_40 = 102, + LV_OPA_50 = 127, + LV_OPA_60 = 153, + LV_OPA_70 = 178, + LV_OPA_80 = 204, + LV_OPA_90 = 229, + LV_OPA_100 = 255, + LV_OPA_COVER = 255, +}; + +#define LV_OPA_MIN 2 /*Opacities below this will be transparent*/ +#define LV_OPA_MAX 253 /*Opacities above this will fully cover*/ + +#if LV_COLOR_DEPTH == 1 +#define LV_COLOR_SIZE 8 +#elif LV_COLOR_DEPTH == 8 +#define LV_COLOR_SIZE 8 +#elif LV_COLOR_DEPTH == 16 +#define LV_COLOR_SIZE 16 +#elif LV_COLOR_DEPTH == 32 +#define LV_COLOR_SIZE 32 +#else +#error "Invalid LV_COLOR_DEPTH in lv_conf.h! Set it to 1, 8, 16 or 32!" +#endif + +#if defined(__cplusplus) && !defined(_LV_COLOR_HAS_MODERN_CPP) +/** +* MSVC compiler's definition of the __cplusplus indicating 199711L regardless to C++ standard version +* see https://devblogs.microsoft.com/cppblog/msvc-now-correctly-reports-cplusplus +* so we use _MSC_VER macro instead of __cplusplus +*/ +#ifdef _MSC_VER +#if _MSC_VER >= 1900 /*Visual Studio 2015*/ +#define _LV_COLOR_HAS_MODERN_CPP 1 +#endif +#else +#if __cplusplus >= 201103L +#define _LV_COLOR_HAS_MODERN_CPP 1 +#endif +#endif +#endif /*__cplusplus*/ + +#ifndef _LV_COLOR_HAS_MODERN_CPP +#define _LV_COLOR_HAS_MODERN_CPP 0 +#endif + +#if _LV_COLOR_HAS_MODERN_CPP +/*Fix msvc compiler error C4576 inside C++ code*/ +#define _LV_COLOR_MAKE_TYPE_HELPER lv_color_t +#else +#define _LV_COLOR_MAKE_TYPE_HELPER (lv_color_t) +#endif + +/*--------------------------------------- + * Macros for all existing color depths + * to set/get values of the color channels + *------------------------------------------*/ +# define LV_COLOR_SET_R1(c, v) (c).ch.red = (uint8_t)((v) & 0x1) +# define LV_COLOR_SET_G1(c, v) (c).ch.green = (uint8_t)((v) & 0x1) +# define LV_COLOR_SET_B1(c, v) (c).ch.blue = (uint8_t)((v) & 0x1) +# define LV_COLOR_SET_A1(c, v) do {} while(0) + +# define LV_COLOR_GET_R1(c) (c).ch.red +# define LV_COLOR_GET_G1(c) (c).ch.green +# define LV_COLOR_GET_B1(c) (c).ch.blue +# define LV_COLOR_GET_A1(c) 0xFF + +# define _LV_COLOR_ZERO_INITIALIZER1 {0x00} +# define LV_COLOR_MAKE1(r8, g8, b8) {(uint8_t)((b8 >> 7) | (g8 >> 7) | (r8 >> 7))} + +# define LV_COLOR_SET_R8(c, v) (c).ch.red = (uint8_t)((v) & 0x7U) +# define LV_COLOR_SET_G8(c, v) (c).ch.green = (uint8_t)((v) & 0x7U) +# define LV_COLOR_SET_B8(c, v) (c).ch.blue = (uint8_t)((v) & 0x3U) +# define LV_COLOR_SET_A8(c, v) do {} while(0) + +# define LV_COLOR_GET_R8(c) (c).ch.red +# define LV_COLOR_GET_G8(c) (c).ch.green +# define LV_COLOR_GET_B8(c) (c).ch.blue +# define LV_COLOR_GET_A8(c) 0xFF + +# define _LV_COLOR_ZERO_INITIALIZER8 {{0x00, 0x00, 0x00}} +# define LV_COLOR_MAKE8(r8, g8, b8) {{(uint8_t)((b8 >> 6) & 0x3U), (uint8_t)((g8 >> 5) & 0x7U), (uint8_t)((r8 >> 5) & 0x7U)}} + +# define LV_COLOR_SET_R16(c, v) (c).ch.red = (uint8_t)((v) & 0x1FU) +#if LV_COLOR_16_SWAP == 0 +# define LV_COLOR_SET_G16(c, v) (c).ch.green = (uint8_t)((v) & 0x3FU) +#else +# define LV_COLOR_SET_G16(c, v) {(c).ch.green_h = (uint8_t)(((v) >> 3) & 0x7); (c).ch.green_l = (uint8_t)((v) & 0x7);} +#endif +# define LV_COLOR_SET_B16(c, v) (c).ch.blue = (uint8_t)((v) & 0x1FU) +# define LV_COLOR_SET_A16(c, v) do {} while(0) + +# define LV_COLOR_GET_R16(c) (c).ch.red +#if LV_COLOR_16_SWAP == 0 +# define LV_COLOR_GET_G16(c) (c).ch.green +#else +# define LV_COLOR_GET_G16(c) (((c).ch.green_h << 3) + (c).ch.green_l) +#endif +# define LV_COLOR_GET_B16(c) (c).ch.blue +# define LV_COLOR_GET_A16(c) 0xFF + +#if LV_COLOR_16_SWAP == 0 +# define _LV_COLOR_ZERO_INITIALIZER16 {{0x00, 0x00, 0x00}} +# define LV_COLOR_MAKE16(r8, g8, b8) {{(uint8_t)((b8 >> 3) & 0x1FU), (uint8_t)((g8 >> 2) & 0x3FU), (uint8_t)((r8 >> 3) & 0x1FU)}} +#else +# define _LV_COLOR_ZERO_INITIALIZER16 {{0x00, 0x00, 0x00, 0x00}} +# define LV_COLOR_MAKE16(r8, g8, b8) {{(uint8_t)((g8 >> 5) & 0x7U), (uint8_t)((r8 >> 3) & 0x1FU), (uint8_t)((b8 >> 3) & 0x1FU), (uint8_t)((g8 >> 2) & 0x7U)}} +#endif + +# define LV_COLOR_SET_R32(c, v) (c).ch.red = (uint8_t)((v) & 0xFF) +# define LV_COLOR_SET_G32(c, v) (c).ch.green = (uint8_t)((v) & 0xFF) +# define LV_COLOR_SET_B32(c, v) (c).ch.blue = (uint8_t)((v) & 0xFF) +# define LV_COLOR_SET_A32(c, v) (c).ch.alpha = (uint8_t)((v) & 0xFF) + +# define LV_COLOR_GET_R32(c) (c).ch.red +# define LV_COLOR_GET_G32(c) (c).ch.green +# define LV_COLOR_GET_B32(c) (c).ch.blue +# define LV_COLOR_GET_A32(c) (c).ch.alpha + +# define _LV_COLOR_ZERO_INITIALIZER32 {{0x00, 0x00, 0x00, 0x00}} +# define LV_COLOR_MAKE32(r8, g8, b8) {{b8, g8, r8, 0xff}} /*Fix 0xff alpha*/ + +/*--------------------------------------- + * Macros for the current color depth + * to set/get values of the color channels + *------------------------------------------*/ +#define LV_COLOR_SET_R(c, v) LV_CONCAT(LV_COLOR_SET_R, LV_COLOR_DEPTH)(c, v) +#define LV_COLOR_SET_G(c, v) LV_CONCAT(LV_COLOR_SET_G, LV_COLOR_DEPTH)(c, v) +#define LV_COLOR_SET_B(c, v) LV_CONCAT(LV_COLOR_SET_B, LV_COLOR_DEPTH)(c, v) +#define LV_COLOR_SET_A(c, v) LV_CONCAT(LV_COLOR_SET_A, LV_COLOR_DEPTH)(c, v) + +#define LV_COLOR_GET_R(c) LV_CONCAT(LV_COLOR_GET_R, LV_COLOR_DEPTH)(c) +#define LV_COLOR_GET_G(c) LV_CONCAT(LV_COLOR_GET_G, LV_COLOR_DEPTH)(c) +#define LV_COLOR_GET_B(c) LV_CONCAT(LV_COLOR_GET_B, LV_COLOR_DEPTH)(c) +#define LV_COLOR_GET_A(c) LV_CONCAT(LV_COLOR_GET_A, LV_COLOR_DEPTH)(c) + +#define _LV_COLOR_ZERO_INITIALIZER LV_CONCAT(_LV_COLOR_ZERO_INITIALIZER, LV_COLOR_DEPTH) +#define LV_COLOR_MAKE(r8, g8, b8) LV_CONCAT(LV_COLOR_MAKE, LV_COLOR_DEPTH)(r8, g8, b8) + +/********************** + * TYPEDEFS + **********************/ + +typedef union { + uint8_t full; /*must be declared first to set all bits of byte via initializer list*/ + union { + uint8_t blue : 1; + uint8_t green : 1; + uint8_t red : 1; + } ch; +} lv_color1_t; + +typedef union { + struct { + uint8_t blue : 2; + uint8_t green : 3; + uint8_t red : 3; + } ch; + uint8_t full; +} lv_color8_t; + +typedef union { + struct { +#if LV_COLOR_16_SWAP == 0 + uint16_t blue : 5; + uint16_t green : 6; + uint16_t red : 5; +#else + uint16_t green_h : 3; + uint16_t red : 5; + uint16_t blue : 5; + uint16_t green_l : 3; +#endif + } ch; + uint16_t full; +} lv_color16_t; + +typedef union { + struct { + uint8_t blue; + uint8_t green; + uint8_t red; + uint8_t alpha; + } ch; + uint32_t full; +} lv_color32_t; + +typedef LV_CONCAT3(uint, LV_COLOR_SIZE, _t) lv_color_int_t; +typedef LV_CONCAT3(lv_color, LV_COLOR_DEPTH, _t) lv_color_t; + +typedef struct { + uint16_t h; + uint8_t s; + uint8_t v; +} lv_color_hsv_t; + +//! @cond Doxygen_Suppress +/*No idea where the guard is required but else throws warnings in the docs*/ +typedef uint8_t lv_opa_t; +//! @endcond + +struct _lv_color_filter_dsc_t; + +typedef lv_color_t (*lv_color_filter_cb_t)(const struct _lv_color_filter_dsc_t *, lv_color_t, lv_opa_t); + +typedef struct _lv_color_filter_dsc_t { + lv_color_filter_cb_t filter_cb; + void * user_data; +} lv_color_filter_dsc_t; + + +typedef enum { + LV_PALETTE_RED, + LV_PALETTE_PINK, + LV_PALETTE_PURPLE, + LV_PALETTE_DEEP_PURPLE, + LV_PALETTE_INDIGO, + LV_PALETTE_BLUE, + LV_PALETTE_LIGHT_BLUE, + LV_PALETTE_CYAN, + LV_PALETTE_TEAL, + LV_PALETTE_GREEN, + LV_PALETTE_LIGHT_GREEN, + LV_PALETTE_LIME, + LV_PALETTE_YELLOW, + LV_PALETTE_AMBER, + LV_PALETTE_ORANGE, + LV_PALETTE_DEEP_ORANGE, + LV_PALETTE_BROWN, + LV_PALETTE_BLUE_GREY, + LV_PALETTE_GREY, + _LV_PALETTE_LAST, + LV_PALETTE_NONE = 0xff, +} lv_palette_t; + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +/*In color conversations: + * - When converting to bigger color type the LSB weight of 1 LSB is calculated + * E.g. 16 bit Red has 5 bits + * 8 bit Red has 3 bits + * ---------------------- + * 8 bit red LSB = (2^5 - 1) / (2^3 - 1) = 31 / 7 = 4 + * + * - When calculating to smaller color type simply shift out the LSBs + * E.g. 8 bit Red has 3 bits + * 16 bit Red has 5 bits + * ---------------------- + * Shift right with 5 - 3 = 2 + */ +static inline uint8_t lv_color_to1(lv_color_t color) +{ +#if LV_COLOR_DEPTH == 1 + return color.full; +#elif LV_COLOR_DEPTH == 8 + if((LV_COLOR_GET_R(color) & 0x4) || (LV_COLOR_GET_G(color) & 0x4) || (LV_COLOR_GET_B(color) & 0x2)) { + return 1; + } + else { + return 0; + } +#elif LV_COLOR_DEPTH == 16 + if((LV_COLOR_GET_R(color) & 0x10) || (LV_COLOR_GET_G(color) & 0x20) || (LV_COLOR_GET_B(color) & 0x10)) { + return 1; + } + else { + return 0; + } +#elif LV_COLOR_DEPTH == 32 + if((LV_COLOR_GET_R(color) & 0x80) || (LV_COLOR_GET_G(color) & 0x80) || (LV_COLOR_GET_B(color) & 0x80)) { + return 1; + } + else { + return 0; + } +#endif +} + +static inline uint8_t lv_color_to8(lv_color_t color) +{ +#if LV_COLOR_DEPTH == 1 + if(color.full == 0) + return 0; + else + return 0xFF; +#elif LV_COLOR_DEPTH == 8 + return color.full; +#elif LV_COLOR_DEPTH == 16 + lv_color8_t ret; + LV_COLOR_SET_R8(ret, LV_COLOR_GET_R(color) >> 2); /*5 - 3 = 2*/ + LV_COLOR_SET_G8(ret, LV_COLOR_GET_G(color) >> 3); /*6 - 3 = 3*/ + LV_COLOR_SET_B8(ret, LV_COLOR_GET_B(color) >> 3); /*5 - 2 = 3*/ + return ret.full; +#elif LV_COLOR_DEPTH == 32 + lv_color8_t ret; + LV_COLOR_SET_R8(ret, LV_COLOR_GET_R(color) >> 5); /*8 - 3 = 5*/ + LV_COLOR_SET_G8(ret, LV_COLOR_GET_G(color) >> 5); /*8 - 3 = 5*/ + LV_COLOR_SET_B8(ret, LV_COLOR_GET_B(color) >> 6); /*8 - 2 = 6*/ + return ret.full; +#endif +} + +static inline uint16_t lv_color_to16(lv_color_t color) +{ +#if LV_COLOR_DEPTH == 1 + if(color.full == 0) + return 0; + else + return 0xFFFF; +#elif LV_COLOR_DEPTH == 8 + lv_color16_t ret; + LV_COLOR_SET_R16(ret, LV_COLOR_GET_R(color) * 4); /*(2^5 - 1)/(2^3 - 1) = 31/7 = 4*/ + LV_COLOR_SET_G16(ret, LV_COLOR_GET_G(color) * 9); /*(2^6 - 1)/(2^3 - 1) = 63/7 = 9*/ + LV_COLOR_SET_B16(ret, LV_COLOR_GET_B(color) * 10); /*(2^5 - 1)/(2^2 - 1) = 31/3 = 10*/ + return ret.full; +#elif LV_COLOR_DEPTH == 16 + return color.full; +#elif LV_COLOR_DEPTH == 32 + lv_color16_t ret; + LV_COLOR_SET_R16(ret, LV_COLOR_GET_R(color) >> 3); /*8 - 5 = 3*/ + LV_COLOR_SET_G16(ret, LV_COLOR_GET_G(color) >> 2); /*8 - 6 = 2*/ + LV_COLOR_SET_B16(ret, LV_COLOR_GET_B(color) >> 3); /*8 - 5 = 3*/ + return ret.full; +#endif +} + +static inline uint32_t lv_color_to32(lv_color_t color) +{ +#if LV_COLOR_DEPTH == 1 + if(color.full == 0) + return 0xFF000000; + else + return 0xFFFFFFFF; +#elif LV_COLOR_DEPTH == 8 + lv_color32_t ret; + LV_COLOR_SET_R32(ret, LV_COLOR_GET_R(color) * 36); /*(2^8 - 1)/(2^3 - 1) = 255/7 = 36*/ + LV_COLOR_SET_G32(ret, LV_COLOR_GET_G(color) * 36); /*(2^8 - 1)/(2^3 - 1) = 255/7 = 36*/ + LV_COLOR_SET_B32(ret, LV_COLOR_GET_B(color) * 85); /*(2^8 - 1)/(2^2 - 1) = 255/3 = 85*/ + LV_COLOR_SET_A32(ret, 0xFF); + return ret.full; +#elif LV_COLOR_DEPTH == 16 + /** + * The floating point math for conversion is: + * valueto = valuefrom * ( (2^bitsto - 1) / (float)(2^bitsfrom - 1) ) + * The faster integer math for conversion is: + * valueto = ( valuefrom * multiplier + adder ) >> divisor + * multiplier = FLOOR( ( (2^bitsto - 1) << divisor ) / (float)(2^bitsfrom - 1) ) + * + * Find the first divisor where ( adder >> divisor ) <= 0 + * + * 5-bit to 8-bit: ( 31 * multiplier + adder ) >> divisor = 255 + * divisor multiplier adder min (0) max (31) + * 0 8 7 7 255 + * 1 16 14 7 255 + * 2 32 28 7 255 + * 3 65 25 3 255 + * 4 131 19 1 255 + * 5 263 7 0 255 + * + * 6-bit to 8-bit: 255 = ( 63 * multiplier + adder ) >> divisor + * divisor multiplier adder min (0) max (63) + * 0 4 3 3 255 + * 1 8 6 3 255 + * 2 16 12 3 255 + * 3 32 24 3 255 + * 4 64 48 3 255 + * 5 129 33 1 255 + * 6 259 3 0 255 + */ + + lv_color32_t ret; + LV_COLOR_SET_R32(ret, (LV_COLOR_GET_R(color) * 263 + 7) >> 5); + LV_COLOR_SET_G32(ret, (LV_COLOR_GET_G(color) * 259 + 3) >> 6); + LV_COLOR_SET_B32(ret, (LV_COLOR_GET_B(color) * 263 + 7) >> 5); + LV_COLOR_SET_A32(ret, 0xFF); + return ret.full; +#elif LV_COLOR_DEPTH == 32 + return color.full; +#endif +} + +//! @cond Doxygen_Suppress + +/** + * Mix two colors with a given ratio. + * @param c1 the first color to mix (usually the foreground) + * @param c2 the second color to mix (usually the background) + * @param mix The ratio of the colors. 0: full `c2`, 255: full `c1`, 127: half `c1` and half`c2` + * @return the mixed color + */ +LV_ATTRIBUTE_FAST_MEM static inline lv_color_t lv_color_mix(lv_color_t c1, lv_color_t c2, uint8_t mix) +{ + lv_color_t ret; + +#if LV_COLOR_DEPTH == 16 && LV_COLOR_16_SWAP == 0 && LV_COLOR_MIX_ROUND_OFS == 0 + /*Source: https://stackoverflow.com/a/50012418/1999969*/ + mix = (uint32_t)((uint32_t)mix + 4) >> 3; + uint32_t bg = (uint32_t)((uint32_t)c2.full | ((uint32_t)c2.full << 16)) & + 0x7E0F81F; /*0b00000111111000001111100000011111*/ + uint32_t fg = (uint32_t)((uint32_t)c1.full | ((uint32_t)c1.full << 16)) & 0x7E0F81F; + uint32_t result = ((((fg - bg) * mix) >> 5) + bg) & 0x7E0F81F; + ret.full = (uint16_t)((result >> 16) | result); +#elif LV_COLOR_DEPTH != 1 + /*LV_COLOR_DEPTH == 8, 16 or 32*/ + LV_COLOR_SET_R(ret, LV_UDIV255((uint16_t)LV_COLOR_GET_R(c1) * mix + LV_COLOR_GET_R(c2) * + (255 - mix) + LV_COLOR_MIX_ROUND_OFS)); + LV_COLOR_SET_G(ret, LV_UDIV255((uint16_t)LV_COLOR_GET_G(c1) * mix + LV_COLOR_GET_G(c2) * + (255 - mix) + LV_COLOR_MIX_ROUND_OFS)); + LV_COLOR_SET_B(ret, LV_UDIV255((uint16_t)LV_COLOR_GET_B(c1) * mix + LV_COLOR_GET_B(c2) * + (255 - mix) + LV_COLOR_MIX_ROUND_OFS)); + LV_COLOR_SET_A(ret, 0xFF); +#else + /*LV_COLOR_DEPTH == 1*/ + ret.full = mix > LV_OPA_50 ? c1.full : c2.full; +#endif + + return ret; +} + +LV_ATTRIBUTE_FAST_MEM static inline void lv_color_premult(lv_color_t c, uint8_t mix, uint16_t * out) +{ +#if LV_COLOR_DEPTH != 1 + out[0] = (uint16_t)LV_COLOR_GET_R(c) * mix; + out[1] = (uint16_t)LV_COLOR_GET_G(c) * mix; + out[2] = (uint16_t)LV_COLOR_GET_B(c) * mix; +#else + (void) mix; + /*Pre-multiplication can't be used with 1 bpp*/ + out[0] = LV_COLOR_GET_R(c); + out[1] = LV_COLOR_GET_G(c); + out[2] = LV_COLOR_GET_B(c); +#endif + +} + +/** + * Mix two colors with a given ratio. It runs faster then `lv_color_mix` but requires some pre computation. + * @param premult_c1 The first color. Should be preprocessed with `lv_color_premult(c1)` + * @param c2 The second color. As it is no pre computation required on it + * @param mix The ratio of the colors. 0: full `c1`, 255: full `c2`, 127: half `c1` and half `c2`. + * Should be modified like mix = `255 - mix` + * @return the mixed color + * @note 255 won't give clearly `c1`. + */ +LV_ATTRIBUTE_FAST_MEM static inline lv_color_t lv_color_mix_premult(uint16_t * premult_c1, lv_color_t c2, uint8_t mix) +{ + lv_color_t ret; +#if LV_COLOR_DEPTH != 1 + /*LV_COLOR_DEPTH == 8 or 32*/ + LV_COLOR_SET_R(ret, LV_UDIV255(premult_c1[0] + LV_COLOR_GET_R(c2) * mix + LV_COLOR_MIX_ROUND_OFS)); + LV_COLOR_SET_G(ret, LV_UDIV255(premult_c1[1] + LV_COLOR_GET_G(c2) * mix + LV_COLOR_MIX_ROUND_OFS)); + LV_COLOR_SET_B(ret, LV_UDIV255(premult_c1[2] + LV_COLOR_GET_B(c2) * mix + LV_COLOR_MIX_ROUND_OFS)); + LV_COLOR_SET_A(ret, 0xFF); +#else + /*LV_COLOR_DEPTH == 1*/ + /*Restore color1*/ + lv_color_t c1; + LV_COLOR_SET_R(c1, premult_c1[0]); + LV_COLOR_SET_G(c1, premult_c1[1]); + LV_COLOR_SET_B(c1, premult_c1[2]); + ret.full = mix > LV_OPA_50 ? c2.full : c1.full; +#endif + + return ret; +} + +/** + * Mix two colors. Both color can have alpha value. + * @param bg_color background color + * @param bg_opa alpha of the background color + * @param fg_color foreground color + * @param fg_opa alpha of the foreground color + * @param res_color the result color + * @param res_opa the result opacity + */ +LV_ATTRIBUTE_FAST_MEM static inline void lv_color_mix_with_alpha(lv_color_t bg_color, lv_opa_t bg_opa, + lv_color_t fg_color, lv_opa_t fg_opa, + lv_color_t * res_color, lv_opa_t * res_opa) +{ + /*Pick the foreground if it's fully opaque or the Background is fully transparent*/ + if(fg_opa >= LV_OPA_MAX || bg_opa <= LV_OPA_MIN) { + res_color->full = fg_color.full; + *res_opa = fg_opa; + } + /*Transparent foreground: use the Background*/ + else if(fg_opa <= LV_OPA_MIN) { + res_color->full = bg_color.full; + *res_opa = bg_opa; + } + /*Opaque background: use simple mix*/ + else if(bg_opa >= LV_OPA_MAX) { + *res_color = lv_color_mix(fg_color, bg_color, fg_opa); + *res_opa = LV_OPA_COVER; + } + /*Both colors have alpha. Expensive calculation need to be applied*/ + else { + /*Save the parameters and the result. If they will be asked again don't compute again*/ + static lv_opa_t fg_opa_save = 0; + static lv_opa_t bg_opa_save = 0; + static lv_color_t fg_color_save = _LV_COLOR_ZERO_INITIALIZER; + static lv_color_t bg_color_save = _LV_COLOR_ZERO_INITIALIZER; + static lv_color_t res_color_saved = _LV_COLOR_ZERO_INITIALIZER; + static lv_opa_t res_opa_saved = 0; + + if(fg_opa != fg_opa_save || bg_opa != bg_opa_save || fg_color.full != fg_color_save.full || + bg_color.full != bg_color_save.full) { + fg_opa_save = fg_opa; + bg_opa_save = bg_opa; + fg_color_save.full = fg_color.full; + bg_color_save.full = bg_color.full; + /*Info: + * https://en.wikipedia.org/wiki/Alpha_compositing#Analytical_derivation_of_the_over_operator*/ + res_opa_saved = 255 - ((uint16_t)((uint16_t)(255 - fg_opa) * (255 - bg_opa)) >> 8); + LV_ASSERT(res_opa_saved != 0); + lv_opa_t ratio = (uint16_t)((uint16_t)fg_opa * 255) / res_opa_saved; + res_color_saved = lv_color_mix(fg_color, bg_color, ratio); + + } + + res_color->full = res_color_saved.full; + *res_opa = res_opa_saved; + } +} + +//! @endcond + +/** + * Get the brightness of a color + * @param color a color + * @return the brightness [0..255] + */ +static inline uint8_t lv_color_brightness(lv_color_t color) +{ + lv_color32_t c32; + c32.full = lv_color_to32(color); + uint16_t bright = (uint16_t)(3u * LV_COLOR_GET_R32(c32) + LV_COLOR_GET_B32(c32) + 4u * LV_COLOR_GET_G32(c32)); + return (uint8_t)(bright >> 3); +} + +static inline lv_color_t lv_color_make(uint8_t r, uint8_t g, uint8_t b) +{ + return _LV_COLOR_MAKE_TYPE_HELPER LV_COLOR_MAKE(r, g, b); +} + +static inline lv_color_t lv_color_hex(uint32_t c) +{ +#if LV_COLOR_DEPTH == 16 + lv_color_t r; +#if LV_COLOR_16_SWAP == 0 + /* Convert a 4 bytes per pixel in format ARGB32 to R5G6B5 format + naive way (by calling lv_color_make with components): + r = ((c & 0xFF0000) >> 19) + g = ((c & 0xFF00) >> 10) + b = ((c & 0xFF) >> 3) + rgb565 = (r << 11) | (g << 5) | b + That's 3 mask, 5 bitshift and 2 or operations + + A bit better: + r = ((c & 0xF80000) >> 8) + g = ((c & 0xFC00) >> 5) + b = ((c & 0xFF) >> 3) + rgb565 = r | g | b + That's 3 mask, 3 bitshifts and 2 or operations */ + r.full = (uint16_t)(((c & 0xF80000) >> 8) | ((c & 0xFC00) >> 5) | ((c & 0xFF) >> 3)); +#else + /* We want: rrrr rrrr GGGg gggg bbbb bbbb => gggb bbbb rrrr rGGG */ + r.full = (uint16_t)(((c & 0xF80000) >> 16) | ((c & 0xFC00) >> 13) | ((c & 0x1C00) << 3) | ((c & 0xF8) << 5)); +#endif + return r; +#elif LV_COLOR_DEPTH == 32 + lv_color_t r; + r.full = c | 0xFF000000; + return r; +#else /*LV_COLOR_DEPTH == 8*/ + return lv_color_make((uint8_t)((c >> 16) & 0xFF), (uint8_t)((c >> 8) & 0xFF), (uint8_t)(c & 0xFF)); +#endif +} + +static inline lv_color_t lv_color_hex3(uint32_t c) +{ + return lv_color_make((uint8_t)(((c >> 4) & 0xF0) | ((c >> 8) & 0xF)), (uint8_t)((c & 0xF0) | ((c & 0xF0) >> 4)), + (uint8_t)((c & 0xF) | ((c & 0xF) << 4))); +} + +static inline void lv_color_filter_dsc_init(lv_color_filter_dsc_t * dsc, lv_color_filter_cb_t cb) +{ + dsc->filter_cb = cb; +} + +//! @cond Doxygen_Suppress +//! +LV_ATTRIBUTE_FAST_MEM void lv_color_fill(lv_color_t * buf, lv_color_t color, uint32_t px_num); + +//! @endcond +lv_color_t lv_color_lighten(lv_color_t c, lv_opa_t lvl); + +lv_color_t lv_color_darken(lv_color_t c, lv_opa_t lvl); + +lv_color_t lv_color_change_lightness(lv_color_t c, lv_opa_t lvl); + +/** + * Convert a HSV color to RGB + * @param h hue [0..359] + * @param s saturation [0..100] + * @param v value [0..100] + * @return the given RGB color in RGB (with LV_COLOR_DEPTH depth) + */ +lv_color_t lv_color_hsv_to_rgb(uint16_t h, uint8_t s, uint8_t v); + +/** + * Convert a 32-bit RGB color to HSV + * @param r8 8-bit red + * @param g8 8-bit green + * @param b8 8-bit blue + * @return the given RGB color in HSV + */ +lv_color_hsv_t lv_color_rgb_to_hsv(uint8_t r8, uint8_t g8, uint8_t b8); + +/** + * Convert a color to HSV + * @param color color + * @return the given color in HSV + */ +lv_color_hsv_t lv_color_to_hsv(lv_color_t color); + +/** + * Just a wrapper around LV_COLOR_CHROMA_KEY because it might be more convenient to use a function in some cases + * @return LV_COLOR_CHROMA_KEY + */ +static inline lv_color_t lv_color_chroma_key(void) +{ + return LV_COLOR_CHROMA_KEY; +} + +/********************** + * PREDEFINED COLORS + **********************/ +/*Source: https://vuetifyjs.com/en/styles/colors/#material-colors*/ + +lv_color_t lv_palette_main(lv_palette_t p); +static inline lv_color_t lv_color_white(void) +{ + return lv_color_make(0xff, 0xff, 0xff); +} +static inline lv_color_t lv_color_black(void) +{ + return lv_color_make(0x00, 0x0, 0x00); +} +lv_color_t lv_palette_lighten(lv_palette_t p, uint8_t lvl); +lv_color_t lv_palette_darken(lv_palette_t p, uint8_t lvl); + +/********************** + * MACROS + **********************/ + +#ifdef __cplusplus +} /*extern "C"*/ +#endif + +#endif /*LV_COLOR_H*/ diff --git a/lib/lvgl/src/misc/lv_fs.c b/lib/lvgl/src/misc/lv_fs.c new file mode 100644 index 00000000..52f3ce07 --- /dev/null +++ b/lib/lvgl/src/misc/lv_fs.c @@ -0,0 +1,518 @@ +/** + * @file lv_fs.c + * + */ + +/********************* + * INCLUDES + *********************/ +#include "lv_fs.h" + +#include "../misc/lv_assert.h" +#include "lv_ll.h" +#include +#include "lv_gc.h" + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ +static const char * lv_fs_get_real_path(const char * path); + +/********************** + * STATIC VARIABLES + **********************/ + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +void _lv_fs_init(void) +{ + _lv_ll_init(&LV_GC_ROOT(_lv_fsdrv_ll), sizeof(lv_fs_drv_t *)); +} + +bool lv_fs_is_ready(char letter) +{ + lv_fs_drv_t * drv = lv_fs_get_drv(letter); + + if(drv == NULL) return false; /*An unknown driver in not ready*/ + + if(drv->ready_cb == NULL) return true; /*Assume the driver is always ready if no handler provided*/ + + return drv->ready_cb(drv); +} + +lv_fs_res_t lv_fs_open(lv_fs_file_t * file_p, const char * path, lv_fs_mode_t mode) +{ + if(path == NULL) { + LV_LOG_WARN("Can't open file: path is NULL"); + return LV_FS_RES_INV_PARAM; + } + + char letter = path[0]; + lv_fs_drv_t * drv = lv_fs_get_drv(letter); + + if(drv == NULL) { + LV_LOG_WARN("Can't open file (%s): unknown driver letter", path); + return LV_FS_RES_NOT_EX; + } + + if(drv->ready_cb) { + if(drv->ready_cb(drv) == false) { + LV_LOG_WARN("Can't open file (%s): driver not ready", path); + return LV_FS_RES_HW_ERR; + } + } + + if(drv->open_cb == NULL) { + LV_LOG_WARN("Can't open file (%s): open function not exists", path); + return LV_FS_RES_NOT_IMP; + } + + const char * real_path = lv_fs_get_real_path(path); + void * file_d = drv->open_cb(drv, real_path, mode); + + if(file_d == NULL || file_d == (void *)(-1)) { + return LV_FS_RES_UNKNOWN; + } + + file_p->drv = drv; + file_p->file_d = file_d; + + if(drv->cache_size) { + file_p->cache = lv_mem_alloc(sizeof(lv_fs_file_cache_t)); + LV_ASSERT_MALLOC(file_p->cache); + lv_memset_00(file_p->cache, sizeof(lv_fs_file_cache_t)); + file_p->cache->start = UINT32_MAX; /*Set an invalid range by default*/ + file_p->cache->end = UINT32_MAX - 1; + } + + return LV_FS_RES_OK; +} + +lv_fs_res_t lv_fs_close(lv_fs_file_t * file_p) +{ + if(file_p->drv == NULL) { + return LV_FS_RES_INV_PARAM; + } + + if(file_p->drv->close_cb == NULL) { + return LV_FS_RES_NOT_IMP; + } + + lv_fs_res_t res = file_p->drv->close_cb(file_p->drv, file_p->file_d); + + if(file_p->drv->cache_size && file_p->cache) { + if(file_p->cache->buffer) { + lv_mem_free(file_p->cache->buffer); + } + + lv_mem_free(file_p->cache); + } + + file_p->file_d = NULL; + file_p->drv = NULL; + file_p->cache = NULL; + + return res; +} + +static lv_fs_res_t lv_fs_read_cached(lv_fs_file_t * file_p, char * buf, uint32_t btr, uint32_t * br) +{ + lv_fs_res_t res = LV_FS_RES_OK; + uint32_t file_position = file_p->cache->file_position; + uint32_t start = file_p->cache->start; + uint32_t end = file_p->cache->end; + char * buffer = file_p->cache->buffer; + uint16_t buffer_size = file_p->drv->cache_size; + + if(start <= file_position && file_position < end) { + /* Data can be read from cache buffer */ + uint16_t buffer_offset = file_position - start; + uint32_t buffer_remaining_length = LV_MIN((uint32_t)buffer_size - buffer_offset, (uint32_t)end - file_position); + + if(btr <= buffer_remaining_length) { + /*Data is in cache buffer, and buffer end not reached, no need to read from FS*/ + lv_memcpy(buf, buffer + buffer_offset, btr); + *br = btr; + } + else { + /*First part of data is in cache buffer, but we need to read rest of data from FS*/ + lv_memcpy(buf, buffer + buffer_offset, buffer_remaining_length); + + uint32_t bytes_read_to_buffer = 0; + if(btr > buffer_size) { + /*If remaining data chuck is bigger than buffer size, then do not use cache, instead read it directly from FS*/ + res = file_p->drv->read_cb(file_p->drv, file_p->file_d, (void *)(buf + buffer_remaining_length), + btr - buffer_remaining_length, &bytes_read_to_buffer); + } + else { + /*If remaining data chunk is smaller than buffer size, then read into cache buffer*/ + res = file_p->drv->read_cb(file_p->drv, file_p->file_d, (void *)buffer, buffer_size, &bytes_read_to_buffer); + file_p->cache->start = file_p->cache->end; + file_p->cache->end = file_p->cache->start + bytes_read_to_buffer; + + uint16_t data_chunk_remaining = LV_MIN(btr - buffer_remaining_length, bytes_read_to_buffer); + lv_memcpy(buf + buffer_remaining_length, buffer, data_chunk_remaining); + } + *br = LV_MIN(buffer_remaining_length + bytes_read_to_buffer, btr); + } + } + else { + /*Data is not in cache buffer*/ + if(btr > buffer_size) { + /*If bigger data is requested, then do not use cache, instead read it directly*/ + res = file_p->drv->read_cb(file_p->drv, file_p->file_d, (void *)buf, btr, br); + } + else { + /*If small data is requested, then read from FS into cache buffer*/ + if(buffer == NULL) { + file_p->cache->buffer = lv_mem_alloc(buffer_size); + LV_ASSERT_MALLOC(file_p->cache->buffer); + buffer = file_p->cache->buffer; + } + + uint32_t bytes_read_to_buffer = 0; + res = file_p->drv->read_cb(file_p->drv, file_p->file_d, (void *)buffer, buffer_size, &bytes_read_to_buffer); + file_p->cache->start = file_position; + file_p->cache->end = file_p->cache->start + bytes_read_to_buffer; + + *br = LV_MIN(btr, bytes_read_to_buffer); + lv_memcpy(buf, buffer, *br); + + } + } + + if(res == LV_FS_RES_OK) { + file_p->cache->file_position += *br; + } + + return res; +} + +lv_fs_res_t lv_fs_read(lv_fs_file_t * file_p, void * buf, uint32_t btr, uint32_t * br) +{ + if(br != NULL) *br = 0; + if(file_p->drv == NULL) return LV_FS_RES_INV_PARAM; + if(file_p->drv->read_cb == NULL) return LV_FS_RES_NOT_IMP; + + uint32_t br_tmp = 0; + lv_fs_res_t res; + + if(file_p->drv->cache_size) { + res = lv_fs_read_cached(file_p, (char *)buf, btr, &br_tmp); + } + else { + res = file_p->drv->read_cb(file_p->drv, file_p->file_d, buf, btr, &br_tmp); + } + + if(br != NULL) *br = br_tmp; + + return res; +} + +lv_fs_res_t lv_fs_write(lv_fs_file_t * file_p, const void * buf, uint32_t btw, uint32_t * bw) +{ + if(bw != NULL) *bw = 0; + + if(file_p->drv == NULL) { + return LV_FS_RES_INV_PARAM; + } + + if(file_p->drv->write_cb == NULL) { + return LV_FS_RES_NOT_IMP; + } + + uint32_t bw_tmp = 0; + lv_fs_res_t res = file_p->drv->write_cb(file_p->drv, file_p->file_d, buf, btw, &bw_tmp); + if(bw != NULL) *bw = bw_tmp; + + return res; +} + +lv_fs_res_t lv_fs_seek(lv_fs_file_t * file_p, uint32_t pos, lv_fs_whence_t whence) +{ + if(file_p->drv == NULL) { + return LV_FS_RES_INV_PARAM; + } + + if(file_p->drv->seek_cb == NULL) { + return LV_FS_RES_NOT_IMP; + } + + lv_fs_res_t res = LV_FS_RES_OK; + if(file_p->drv->cache_size) { + switch(whence) { + case LV_FS_SEEK_SET: { + file_p->cache->file_position = pos; + + /*FS seek if new position is outside cache buffer*/ + if(file_p->cache->file_position < file_p->cache->start || file_p->cache->file_position > file_p->cache->end) { + res = file_p->drv->seek_cb(file_p->drv, file_p->file_d, file_p->cache->file_position, LV_FS_SEEK_SET); + } + + break; + } + case LV_FS_SEEK_CUR: { + file_p->cache->file_position += pos; + + /*FS seek if new position is outside cache buffer*/ + if(file_p->cache->file_position < file_p->cache->start || file_p->cache->file_position > file_p->cache->end) { + res = file_p->drv->seek_cb(file_p->drv, file_p->file_d, file_p->cache->file_position, LV_FS_SEEK_SET); + } + + break; + } + case LV_FS_SEEK_END: { + /*Because we don't know the file size, we do a little trick: do a FS seek, then get new file position from FS*/ + res = file_p->drv->seek_cb(file_p->drv, file_p->file_d, pos, whence); + if(res == LV_FS_RES_OK) { + uint32_t tmp_position; + res = file_p->drv->tell_cb(file_p->drv, file_p->file_d, &tmp_position); + + if(res == LV_FS_RES_OK) { + file_p->cache->file_position = tmp_position; + } + } + break; + } + } + } + else { + res = file_p->drv->seek_cb(file_p->drv, file_p->file_d, pos, whence); + } + + return res; +} + +lv_fs_res_t lv_fs_tell(lv_fs_file_t * file_p, uint32_t * pos) +{ + if(file_p->drv == NULL) { + *pos = 0; + return LV_FS_RES_INV_PARAM; + } + + if(file_p->drv->tell_cb == NULL) { + *pos = 0; + return LV_FS_RES_NOT_IMP; + } + + lv_fs_res_t res; + if(file_p->drv->cache_size) { + *pos = file_p->cache->file_position; + res = LV_FS_RES_OK; + } + else { + res = file_p->drv->tell_cb(file_p->drv, file_p->file_d, pos); + } + + return res; +} + +lv_fs_res_t lv_fs_dir_open(lv_fs_dir_t * rddir_p, const char * path) +{ + if(path == NULL) return LV_FS_RES_INV_PARAM; + + char letter = path[0]; + lv_fs_drv_t * drv = lv_fs_get_drv(letter); + + if(drv == NULL) { + return LV_FS_RES_NOT_EX; + } + + if(drv->ready_cb) { + if(drv->ready_cb(drv) == false) { + return LV_FS_RES_HW_ERR; + } + } + + if(drv->dir_open_cb == NULL) { + return LV_FS_RES_NOT_IMP; + } + + const char * real_path = lv_fs_get_real_path(path); + void * dir_d = drv->dir_open_cb(drv, real_path); + + if(dir_d == NULL || dir_d == (void *)(-1)) { + return LV_FS_RES_UNKNOWN; + } + + rddir_p->drv = drv; + rddir_p->dir_d = dir_d; + + return LV_FS_RES_OK; +} + +lv_fs_res_t lv_fs_dir_read(lv_fs_dir_t * rddir_p, char * fn) +{ + if(rddir_p->drv == NULL || rddir_p->dir_d == NULL) { + fn[0] = '\0'; + return LV_FS_RES_INV_PARAM; + } + + if(rddir_p->drv->dir_read_cb == NULL) { + fn[0] = '\0'; + return LV_FS_RES_NOT_IMP; + } + + lv_fs_res_t res = rddir_p->drv->dir_read_cb(rddir_p->drv, rddir_p->dir_d, fn); + + return res; +} + +lv_fs_res_t lv_fs_dir_close(lv_fs_dir_t * rddir_p) +{ + if(rddir_p->drv == NULL || rddir_p->dir_d == NULL) { + return LV_FS_RES_INV_PARAM; + } + + if(rddir_p->drv->dir_close_cb == NULL) { + return LV_FS_RES_NOT_IMP; + } + + lv_fs_res_t res = rddir_p->drv->dir_close_cb(rddir_p->drv, rddir_p->dir_d); + + rddir_p->dir_d = NULL; + rddir_p->drv = NULL; + + return res; +} + +void lv_fs_drv_init(lv_fs_drv_t * drv) +{ + lv_memset_00(drv, sizeof(lv_fs_drv_t)); +} + +void lv_fs_drv_register(lv_fs_drv_t * drv_p) +{ + /*Save the new driver*/ + lv_fs_drv_t ** new_drv; + new_drv = _lv_ll_ins_head(&LV_GC_ROOT(_lv_fsdrv_ll)); + LV_ASSERT_MALLOC(new_drv); + if(new_drv == NULL) return; + + *new_drv = drv_p; +} + +lv_fs_drv_t * lv_fs_get_drv(char letter) +{ + lv_fs_drv_t ** drv; + + _LV_LL_READ(&LV_GC_ROOT(_lv_fsdrv_ll), drv) { + if((*drv)->letter == letter) { + return *drv; + } + } + + return NULL; +} + +char * lv_fs_get_letters(char * buf) +{ + lv_fs_drv_t ** drv; + uint8_t i = 0; + + _LV_LL_READ(&LV_GC_ROOT(_lv_fsdrv_ll), drv) { + buf[i] = (*drv)->letter; + i++; + } + + buf[i] = '\0'; + + return buf; +} + +const char * lv_fs_get_ext(const char * fn) +{ + size_t i; + for(i = strlen(fn); i > 0; i--) { + if(fn[i] == '.') { + return &fn[i + 1]; + } + else if(fn[i] == '/' || fn[i] == '\\') { + return ""; /*No extension if a '\' or '/' found*/ + } + } + + return ""; /*Empty string if no '.' in the file name.*/ +} + +char * lv_fs_up(char * path) +{ + size_t len = strlen(path); + if(len == 0) return path; + + len--; /*Go before the trailing '\0'*/ + + /*Ignore trailing '/' or '\'*/ + while(path[len] == '/' || path[len] == '\\') { + path[len] = '\0'; + if(len > 0) + len--; + else + return path; + } + + size_t i; + for(i = len; i > 0; i--) { + if(path[i] == '/' || path[i] == '\\') break; + } + + if(i > 0) path[i] = '\0'; + + return path; +} + +const char * lv_fs_get_last(const char * path) +{ + size_t len = strlen(path); + if(len == 0) return path; + + len--; /*Go before the trailing '\0'*/ + + /*Ignore trailing '/' or '\'*/ + while(path[len] == '/' || path[len] == '\\') { + if(len > 0) + len--; + else + return path; + } + + size_t i; + for(i = len; i > 0; i--) { + if(path[i] == '/' || path[i] == '\\') break; + } + + /*No '/' or '\' in the path so return with path itself*/ + if(i == 0) return path; + + return &path[i + 1]; +} +/********************** + * STATIC FUNCTIONS + **********************/ + +/** + * Skip the driver letter and the possible : after the letter + * @param path path string (E.g. S:/folder/file.txt) + * @return pointer to the beginning of the real path (E.g. /folder/file.txt) + */ +static const char * lv_fs_get_real_path(const char * path) +{ + path++; /*Ignore the driver letter*/ + if(*path == ':') path++; + + return path; +} diff --git a/lib/lvgl/src/misc/lv_fs.h b/lib/lvgl/src/misc/lv_fs.h new file mode 100644 index 00000000..9f65e1b2 --- /dev/null +++ b/lib/lvgl/src/misc/lv_fs.h @@ -0,0 +1,262 @@ +/** + * @file lv_fs.h + * + */ + +#ifndef LV_FS_H +#define LV_FS_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ +#include "../lv_conf_internal.h" + +#include +#include + +/********************* + * DEFINES + *********************/ +#define LV_FS_MAX_FN_LENGTH 64 +#define LV_FS_MAX_PATH_LENGTH 256 + +/********************** + * TYPEDEFS + **********************/ + +/** + * Errors in the file system module. + */ +enum { + LV_FS_RES_OK = 0, + LV_FS_RES_HW_ERR, /*Low level hardware error*/ + LV_FS_RES_FS_ERR, /*Error in the file system structure*/ + LV_FS_RES_NOT_EX, /*Driver, file or directory is not exists*/ + LV_FS_RES_FULL, /*Disk full*/ + LV_FS_RES_LOCKED, /*The file is already opened*/ + LV_FS_RES_DENIED, /*Access denied. Check 'fs_open' modes and write protect*/ + LV_FS_RES_BUSY, /*The file system now can't handle it, try later*/ + LV_FS_RES_TOUT, /*Process time outed*/ + LV_FS_RES_NOT_IMP, /*Requested function is not implemented*/ + LV_FS_RES_OUT_OF_MEM, /*Not enough memory for an internal operation*/ + LV_FS_RES_INV_PARAM, /*Invalid parameter among arguments*/ + LV_FS_RES_UNKNOWN, /*Other unknown error*/ +}; +typedef uint8_t lv_fs_res_t; + +/** + * File open mode. + */ +enum { + LV_FS_MODE_WR = 0x01, + LV_FS_MODE_RD = 0x02, +}; +typedef uint8_t lv_fs_mode_t; + + +/** + * Seek modes. + */ +typedef enum { + LV_FS_SEEK_SET = 0x00, /**< Set the position from absolutely (from the start of file)*/ + LV_FS_SEEK_CUR = 0x01, /**< Set the position from the current position*/ + LV_FS_SEEK_END = 0x02, /**< Set the position from the end of the file*/ +} lv_fs_whence_t; + +typedef struct _lv_fs_drv_t { + char letter; + uint16_t cache_size; + bool (*ready_cb)(struct _lv_fs_drv_t * drv); + + void * (*open_cb)(struct _lv_fs_drv_t * drv, const char * path, lv_fs_mode_t mode); + lv_fs_res_t (*close_cb)(struct _lv_fs_drv_t * drv, void * file_p); + lv_fs_res_t (*read_cb)(struct _lv_fs_drv_t * drv, void * file_p, void * buf, uint32_t btr, uint32_t * br); + lv_fs_res_t (*write_cb)(struct _lv_fs_drv_t * drv, void * file_p, const void * buf, uint32_t btw, uint32_t * bw); + lv_fs_res_t (*seek_cb)(struct _lv_fs_drv_t * drv, void * file_p, uint32_t pos, lv_fs_whence_t whence); + lv_fs_res_t (*tell_cb)(struct _lv_fs_drv_t * drv, void * file_p, uint32_t * pos_p); + + void * (*dir_open_cb)(struct _lv_fs_drv_t * drv, const char * path); + lv_fs_res_t (*dir_read_cb)(struct _lv_fs_drv_t * drv, void * rddir_p, char * fn); + lv_fs_res_t (*dir_close_cb)(struct _lv_fs_drv_t * drv, void * rddir_p); + +#if LV_USE_USER_DATA + void * user_data; /**< Custom file user data*/ +#endif +} lv_fs_drv_t; + +typedef struct { + uint32_t start; + uint32_t end; + uint32_t file_position; + void * buffer; +} lv_fs_file_cache_t; + +typedef struct { + void * file_d; + lv_fs_drv_t * drv; + lv_fs_file_cache_t * cache; +} lv_fs_file_t; + +typedef struct { + void * dir_d; + lv_fs_drv_t * drv; +} lv_fs_dir_t; + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +/** + * Initialize the File system interface + */ +void _lv_fs_init(void); + +/** + * Initialize a file system driver with default values. + * It is used to surly have known values in the fields ant not memory junk. + * After it you can set the fields. + * @param drv pointer to driver variable to initialize + */ +void lv_fs_drv_init(lv_fs_drv_t * drv); + +/** + * Add a new drive + * @param drv pointer to an lv_fs_drv_t structure which is inited with the + * corresponding function pointers. Only pointer is saved, so the + * driver should be static or dynamically allocated. + */ +void lv_fs_drv_register(lv_fs_drv_t * drv); + +/** + * Give a pointer to a driver from its letter + * @param letter the driver letter + * @return pointer to a driver or NULL if not found + */ +lv_fs_drv_t * lv_fs_get_drv(char letter); + +/** + * Test if a drive is ready or not. If the `ready` function was not initialized `true` will be + * returned. + * @param letter letter of the drive + * @return true: drive is ready; false: drive is not ready + */ +bool lv_fs_is_ready(char letter); + +/** + * Open a file + * @param file_p pointer to a lv_fs_file_t variable + * @param path path to the file beginning with the driver letter (e.g. S:/folder/file.txt) + * @param mode read: FS_MODE_RD, write: FS_MODE_WR, both: FS_MODE_RD | FS_MODE_WR + * @return LV_FS_RES_OK or any error from lv_fs_res_t enum + */ +lv_fs_res_t lv_fs_open(lv_fs_file_t * file_p, const char * path, lv_fs_mode_t mode); + +/** + * Close an already opened file + * @param file_p pointer to a lv_fs_file_t variable + * @return LV_FS_RES_OK or any error from lv_fs_res_t enum + */ +lv_fs_res_t lv_fs_close(lv_fs_file_t * file_p); + +/** + * Read from a file + * @param file_p pointer to a lv_fs_file_t variable + * @param buf pointer to a buffer where the read bytes are stored + * @param btr Bytes To Read + * @param br the number of real read bytes (Bytes Read). NULL if unused. + * @return LV_FS_RES_OK or any error from lv_fs_res_t enum + */ +lv_fs_res_t lv_fs_read(lv_fs_file_t * file_p, void * buf, uint32_t btr, uint32_t * br); + +/** + * Write into a file + * @param file_p pointer to a lv_fs_file_t variable + * @param buf pointer to a buffer with the bytes to write + * @param btw Bytes To Write + * @param bw the number of real written bytes (Bytes Written). NULL if unused. + * @return LV_FS_RES_OK or any error from lv_fs_res_t enum + */ +lv_fs_res_t lv_fs_write(lv_fs_file_t * file_p, const void * buf, uint32_t btw, uint32_t * bw); + +/** + * Set the position of the 'cursor' (read write pointer) in a file + * @param file_p pointer to a lv_fs_file_t variable + * @param pos the new position expressed in bytes index (0: start of file) + * @param whence tells from where set the position. See @lv_fs_whence_t + * @return LV_FS_RES_OK or any error from lv_fs_res_t enum + */ +lv_fs_res_t lv_fs_seek(lv_fs_file_t * file_p, uint32_t pos, lv_fs_whence_t whence); + +/** + * Give the position of the read write pointer + * @param file_p pointer to a lv_fs_file_t variable + * @param pos_p pointer to store the position of the read write pointer + * @return LV_FS_RES_OK or any error from 'fs_res_t' + */ +lv_fs_res_t lv_fs_tell(lv_fs_file_t * file_p, uint32_t * pos); + +/** + * Initialize a 'fs_dir_t' variable for directory reading + * @param rddir_p pointer to a 'lv_fs_dir_t' variable + * @param path path to a directory + * @return LV_FS_RES_OK or any error from lv_fs_res_t enum + */ +lv_fs_res_t lv_fs_dir_open(lv_fs_dir_t * rddir_p, const char * path); + +/** + * Read the next filename form a directory. + * The name of the directories will begin with '/' + * @param rddir_p pointer to an initialized 'fs_dir_t' variable + * @param fn pointer to a buffer to store the filename + * @return LV_FS_RES_OK or any error from lv_fs_res_t enum + */ +lv_fs_res_t lv_fs_dir_read(lv_fs_dir_t * rddir_p, char * fn); + +/** + * Close the directory reading + * @param rddir_p pointer to an initialized 'fs_dir_t' variable + * @return LV_FS_RES_OK or any error from lv_fs_res_t enum + */ +lv_fs_res_t lv_fs_dir_close(lv_fs_dir_t * rddir_p); + +/** + * Fill a buffer with the letters of existing drivers + * @param buf buffer to store the letters ('\0' added after the last letter) + * @return the buffer + */ +char * lv_fs_get_letters(char * buf); + +/** + * Return with the extension of the filename + * @param fn string with a filename + * @return pointer to the beginning extension or empty string if no extension + */ +const char * lv_fs_get_ext(const char * fn); + +/** + * Step up one level + * @param path pointer to a file name + * @return the truncated file name + */ +char * lv_fs_up(char * path); + +/** + * Get the last element of a path (e.g. U:/folder/file -> file) + * @param path pointer to a file name + * @return pointer to the beginning of the last element in the path + */ +const char * lv_fs_get_last(const char * path); + +/********************** + * MACROS + **********************/ + +#ifdef __cplusplus +} /*extern "C"*/ +#endif + +#endif /*LV_FS_H*/ diff --git a/lib/lvgl/src/misc/lv_gc.c b/lib/lvgl/src/misc/lv_gc.c new file mode 100644 index 00000000..95bb5499 --- /dev/null +++ b/lib/lvgl/src/misc/lv_gc.c @@ -0,0 +1,47 @@ +/** + * @file lv_gc.c + * + */ + +/********************* + * INCLUDES + *********************/ +#include "lv_gc.h" + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ + +/********************** + * STATIC VARIABLES + **********************/ + +#if(!defined(LV_ENABLE_GC)) || LV_ENABLE_GC == 0 + LV_ROOTS +#endif /*LV_ENABLE_GC*/ + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +void _lv_gc_clear_roots(void) +{ +#define LV_CLEAR_ROOT(root_type, root_name) lv_memset_00(&LV_GC_ROOT(root_name), sizeof(LV_GC_ROOT(root_name))); + LV_ITERATE_ROOTS(LV_CLEAR_ROOT) +} + +/********************** + * STATIC FUNCTIONS + **********************/ diff --git a/lib/lvgl/src/misc/lv_gc.h b/lib/lvgl/src/misc/lv_gc.h new file mode 100644 index 00000000..9d7d1bb9 --- /dev/null +++ b/lib/lvgl/src/misc/lv_gc.h @@ -0,0 +1,97 @@ +/** + * @file lv_gc.h + * + */ + +#ifndef LV_GC_H +#define LV_GC_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ +#include "../lv_conf_internal.h" +#include +#include "lv_mem.h" +#include "lv_ll.h" +#include "lv_timer.h" +#include "lv_types.h" +#include "../draw/lv_img_cache.h" +#include "../draw/lv_draw_mask.h" +#include "../core/lv_obj_pos.h" + +/********************* + * DEFINES + *********************/ +#if LV_IMG_CACHE_DEF_SIZE +# define LV_IMG_CACHE_DEF 1 +#else +# define LV_IMG_CACHE_DEF 0 +#endif + +#define LV_DISPATCH(f, t, n) f(t, n) +#define LV_DISPATCH_COND(f, t, n, m, v) LV_CONCAT3(LV_DISPATCH, m, v)(f, t, n) + +#define LV_DISPATCH00(f, t, n) LV_DISPATCH(f, t, n) +#define LV_DISPATCH01(f, t, n) +#define LV_DISPATCH10(f, t, n) +#define LV_DISPATCH11(f, t, n) LV_DISPATCH(f, t, n) + +#define LV_ITERATE_ROOTS(f) \ + LV_DISPATCH(f, lv_ll_t, _lv_timer_ll) /*Linked list to store the lv_timers*/ \ + LV_DISPATCH(f, lv_ll_t, _lv_disp_ll) /*Linked list of display device*/ \ + LV_DISPATCH(f, lv_ll_t, _lv_indev_ll) /*Linked list of input device*/ \ + LV_DISPATCH(f, lv_ll_t, _lv_fsdrv_ll) \ + LV_DISPATCH(f, lv_ll_t, _lv_anim_ll) \ + LV_DISPATCH(f, lv_ll_t, _lv_group_ll) \ + LV_DISPATCH(f, lv_ll_t, _lv_img_decoder_ll) \ + LV_DISPATCH(f, lv_ll_t, _lv_obj_style_trans_ll) \ + LV_DISPATCH(f, lv_layout_dsc_t *, _lv_layout_list) \ + LV_DISPATCH_COND(f, _lv_img_cache_entry_t*, _lv_img_cache_array, LV_IMG_CACHE_DEF, 1) \ + LV_DISPATCH_COND(f, _lv_img_cache_entry_t, _lv_img_cache_single, LV_IMG_CACHE_DEF, 0) \ + LV_DISPATCH(f, lv_timer_t*, _lv_timer_act) \ + LV_DISPATCH(f, lv_mem_buf_arr_t , lv_mem_buf) \ + LV_DISPATCH_COND(f, _lv_draw_mask_radius_circle_dsc_arr_t , _lv_circle_cache, LV_DRAW_COMPLEX, 1) \ + LV_DISPATCH_COND(f, _lv_draw_mask_saved_arr_t , _lv_draw_mask_list, LV_DRAW_COMPLEX, 1) \ + LV_DISPATCH(f, void * , _lv_theme_default_styles) \ + LV_DISPATCH(f, void * , _lv_theme_basic_styles) \ + LV_DISPATCH_COND(f, uint8_t *, _lv_font_decompr_buf, LV_USE_FONT_COMPRESSED, 1) \ + LV_DISPATCH(f, uint8_t * , _lv_grad_cache_mem) \ + LV_DISPATCH(f, uint8_t * , _lv_style_custom_prop_flag_lookup_table) + +#define LV_DEFINE_ROOT(root_type, root_name) root_type root_name; +#define LV_ROOTS LV_ITERATE_ROOTS(LV_DEFINE_ROOT) + +#if LV_ENABLE_GC == 1 +#if LV_MEM_CUSTOM != 1 +#error "GC requires CUSTOM_MEM" +#endif /*LV_MEM_CUSTOM*/ +#include LV_GC_INCLUDE +#else /*LV_ENABLE_GC*/ +#define LV_GC_ROOT(x) x +#define LV_EXTERN_ROOT(root_type, root_name) extern root_type root_name; +LV_ITERATE_ROOTS(LV_EXTERN_ROOT) +#endif /*LV_ENABLE_GC*/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +void _lv_gc_clear_roots(void); + +/********************** + * MACROS + **********************/ + +#ifdef __cplusplus +} /*extern "C"*/ +#endif + +#endif /*LV_GC_H*/ diff --git a/lib/lvgl/src/misc/lv_ll.c b/lib/lvgl/src/misc/lv_ll.c new file mode 100644 index 00000000..e7582316 --- /dev/null +++ b/lib/lvgl/src/misc/lv_ll.c @@ -0,0 +1,408 @@ +/** + * @file lv_ll.c + * Handle linked lists. + * The nodes are dynamically allocated by the 'lv_mem' module, + */ + +/********************* + * INCLUDES + *********************/ +#include "lv_ll.h" +#include "lv_mem.h" + +/********************* + * DEFINES + *********************/ +#define LL_NODE_META_SIZE (sizeof(lv_ll_node_t *) + sizeof(lv_ll_node_t *)) +#define LL_PREV_P_OFFSET(ll_p) (ll_p->n_size) +#define LL_NEXT_P_OFFSET(ll_p) (ll_p->n_size + sizeof(lv_ll_node_t *)) + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ +static void node_set_prev(lv_ll_t * ll_p, lv_ll_node_t * act, lv_ll_node_t * prev); +static void node_set_next(lv_ll_t * ll_p, lv_ll_node_t * act, lv_ll_node_t * next); + +/********************** + * STATIC VARIABLES + **********************/ + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +/** + * Initialize linked list + * @param ll_p pointer to lv_ll_t variable + * @param node_size the size of 1 node in bytes + */ +void _lv_ll_init(lv_ll_t * ll_p, uint32_t node_size) +{ + ll_p->head = NULL; + ll_p->tail = NULL; +#ifdef LV_ARCH_64 + /*Round the size up to 8*/ + node_size = (node_size + 7) & (~0x7); +#else + /*Round the size up to 4*/ + node_size = (node_size + 3) & (~0x3); +#endif + + ll_p->n_size = node_size; +} + +/** + * Add a new head to a linked list + * @param ll_p pointer to linked list + * @return pointer to the new head + */ +void * _lv_ll_ins_head(lv_ll_t * ll_p) +{ + lv_ll_node_t * n_new; + + n_new = lv_mem_alloc(ll_p->n_size + LL_NODE_META_SIZE); + + if(n_new != NULL) { + node_set_prev(ll_p, n_new, NULL); /*No prev. before the new head*/ + node_set_next(ll_p, n_new, ll_p->head); /*After new comes the old head*/ + + if(ll_p->head != NULL) { /*If there is old head then before it goes the new*/ + node_set_prev(ll_p, ll_p->head, n_new); + } + + ll_p->head = n_new; /*Set the new head in the dsc.*/ + if(ll_p->tail == NULL) { /*If there is no tail (1. node) set the tail too*/ + ll_p->tail = n_new; + } + } + + return n_new; +} + +/** + * Insert a new node in front of the n_act node + * @param ll_p pointer to linked list + * @param n_act pointer a node + * @return pointer to the new node + */ +void * _lv_ll_ins_prev(lv_ll_t * ll_p, void * n_act) +{ + lv_ll_node_t * n_new; + + if(NULL == ll_p || NULL == n_act) return NULL; + + if(_lv_ll_get_head(ll_p) == n_act) { + n_new = _lv_ll_ins_head(ll_p); + if(n_new == NULL) return NULL; + } + else { + n_new = lv_mem_alloc(ll_p->n_size + LL_NODE_META_SIZE); + if(n_new == NULL) return NULL; + + lv_ll_node_t * n_prev; + n_prev = _lv_ll_get_prev(ll_p, n_act); + node_set_next(ll_p, n_prev, n_new); + node_set_prev(ll_p, n_new, n_prev); + node_set_prev(ll_p, n_act, n_new); + node_set_next(ll_p, n_new, n_act); + } + + return n_new; +} + +/** + * Add a new tail to a linked list + * @param ll_p pointer to linked list + * @return pointer to the new tail + */ +void * _lv_ll_ins_tail(lv_ll_t * ll_p) +{ + lv_ll_node_t * n_new; + + n_new = lv_mem_alloc(ll_p->n_size + LL_NODE_META_SIZE); + + if(n_new != NULL) { + node_set_next(ll_p, n_new, NULL); /*No next after the new tail*/ + node_set_prev(ll_p, n_new, ll_p->tail); /*The prev. before new is the old tail*/ + if(ll_p->tail != NULL) { /*If there is old tail then the new comes after it*/ + node_set_next(ll_p, ll_p->tail, n_new); + } + + ll_p->tail = n_new; /*Set the new tail in the dsc.*/ + if(ll_p->head == NULL) { /*If there is no head (1. node) set the head too*/ + ll_p->head = n_new; + } + } + + return n_new; +} + +/** + * Remove the node 'node_p' from 'll_p' linked list. + * It does not free the memory of node. + * @param ll_p pointer to the linked list of 'node_p' + * @param node_p pointer to node in 'll_p' linked list + */ +void _lv_ll_remove(lv_ll_t * ll_p, void * node_p) +{ + if(ll_p == NULL) return; + + if(_lv_ll_get_head(ll_p) == node_p) { + /*The new head will be the node after 'n_act'*/ + ll_p->head = _lv_ll_get_next(ll_p, node_p); + if(ll_p->head == NULL) { + ll_p->tail = NULL; + } + else { + node_set_prev(ll_p, ll_p->head, NULL); + } + } + else if(_lv_ll_get_tail(ll_p) == node_p) { + /*The new tail will be the node before 'n_act'*/ + ll_p->tail = _lv_ll_get_prev(ll_p, node_p); + if(ll_p->tail == NULL) { + ll_p->head = NULL; + } + else { + node_set_next(ll_p, ll_p->tail, NULL); + } + } + else { + lv_ll_node_t * n_prev = _lv_ll_get_prev(ll_p, node_p); + lv_ll_node_t * n_next = _lv_ll_get_next(ll_p, node_p); + + node_set_next(ll_p, n_prev, n_next); + node_set_prev(ll_p, n_next, n_prev); + } +} + +/** + * Remove and free all elements from a linked list. The list remain valid but become empty. + * @param ll_p pointer to linked list + */ +void _lv_ll_clear(lv_ll_t * ll_p) +{ + void * i; + void * i_next; + + i = _lv_ll_get_head(ll_p); + i_next = NULL; + + while(i != NULL) { + i_next = _lv_ll_get_next(ll_p, i); + + _lv_ll_remove(ll_p, i); + lv_mem_free(i); + + i = i_next; + } +} + +/** + * Move a node to a new linked list + * @param ll_ori_p pointer to the original (old) linked list + * @param ll_new_p pointer to the new linked list + * @param node pointer to a node + * @param head true: be the head in the new list + * false be the tail in the new list + */ +void _lv_ll_chg_list(lv_ll_t * ll_ori_p, lv_ll_t * ll_new_p, void * node, bool head) +{ + _lv_ll_remove(ll_ori_p, node); + + if(head) { + /*Set node as head*/ + node_set_prev(ll_new_p, node, NULL); + node_set_next(ll_new_p, node, ll_new_p->head); + + if(ll_new_p->head != NULL) { /*If there is old head then before it goes the new*/ + node_set_prev(ll_new_p, ll_new_p->head, node); + } + + ll_new_p->head = node; /*Set the new head in the dsc.*/ + if(ll_new_p->tail == NULL) { /*If there is no tail (first node) set the tail too*/ + ll_new_p->tail = node; + } + } + else { + /*Set node as tail*/ + node_set_prev(ll_new_p, node, ll_new_p->tail); + node_set_next(ll_new_p, node, NULL); + + if(ll_new_p->tail != NULL) { /*If there is old tail then after it goes the new*/ + node_set_next(ll_new_p, ll_new_p->tail, node); + } + + ll_new_p->tail = node; /*Set the new tail in the dsc.*/ + if(ll_new_p->head == NULL) { /*If there is no head (first node) set the head too*/ + ll_new_p->head = node; + } + } +} + +/** + * Return with head node of the linked list + * @param ll_p pointer to linked list + * @return pointer to the head of 'll_p' + */ +void * _lv_ll_get_head(const lv_ll_t * ll_p) +{ + if(ll_p == NULL) return NULL; + return ll_p->head; +} + +/** + * Return with tail node of the linked list + * @param ll_p pointer to linked list + * @return pointer to the tail of 'll_p' + */ +void * _lv_ll_get_tail(const lv_ll_t * ll_p) +{ + if(ll_p == NULL) return NULL; + return ll_p->tail; +} + +/** + * Return with the pointer of the next node after 'n_act' + * @param ll_p pointer to linked list + * @param n_act pointer a node + * @return pointer to the next node + */ +void * _lv_ll_get_next(const lv_ll_t * ll_p, const void * n_act) +{ + /*Pointer to the next node is stored in the end of this node. + *Go there and return the address found there*/ + const lv_ll_node_t * n_act_d = n_act; + n_act_d += LL_NEXT_P_OFFSET(ll_p); + return *((lv_ll_node_t **)n_act_d); +} + +/** + * Return with the pointer of the previous node before 'n_act' + * @param ll_p pointer to linked list + * @param n_act pointer a node + * @return pointer to the previous node + */ +void * _lv_ll_get_prev(const lv_ll_t * ll_p, const void * n_act) +{ + /*Pointer to the prev. node is stored in the end of this node. + *Go there and return the address found there*/ + const lv_ll_node_t * n_act_d = n_act; + n_act_d += LL_PREV_P_OFFSET(ll_p); + return *((lv_ll_node_t **)n_act_d); +} + +/** + * Return the length of the linked list. + * @param ll_p pointer to linked list + * @return length of the linked list + */ +uint32_t _lv_ll_get_len(const lv_ll_t * ll_p) +{ + uint32_t len = 0; + void * node; + + for(node = _lv_ll_get_head(ll_p); node != NULL; node = _lv_ll_get_next(ll_p, node)) { + len++; + } + + return len; +} + +/** + * Move a node before an other node in the same linked list + * @param ll_p pointer to a linked list + * @param n_act pointer to node to move + * @param n_after pointer to a node which should be after `n_act` + */ +void _lv_ll_move_before(lv_ll_t * ll_p, void * n_act, void * n_after) +{ + if(n_act == n_after) return; /*Can't move before itself*/ + + void * n_before; + if(n_after != NULL) + n_before = _lv_ll_get_prev(ll_p, n_after); + else + n_before = _lv_ll_get_tail(ll_p); /*if `n_after` is NULL `n_act` should be the new tail*/ + + if(n_act == n_before) return; /*Already before `n_after`*/ + + /*It's much easier to remove from the list and add again*/ + _lv_ll_remove(ll_p, n_act); + + /*Add again by setting the prev. and next nodes*/ + node_set_next(ll_p, n_before, n_act); + node_set_prev(ll_p, n_act, n_before); + node_set_prev(ll_p, n_after, n_act); + node_set_next(ll_p, n_act, n_after); + + /*If `n_act` was moved before NULL then it become the new tail*/ + if(n_after == NULL) ll_p->tail = n_act; + + /*If `n_act` was moved before `NULL` then it's the new head*/ + if(n_before == NULL) ll_p->head = n_act; +} + +/** + * Check if a linked list is empty + * @param ll_p pointer to a linked list + * @return true: the linked list is empty; false: not empty + */ +bool _lv_ll_is_empty(lv_ll_t * ll_p) +{ + if(ll_p == NULL) return true; + + if(ll_p->head == NULL && ll_p->tail == NULL) return true; + + return false; +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +/** + * Set the previous node pointer of a node + * @param ll_p pointer to linked list + * @param act pointer to a node which prev. node pointer should be set + * @param prev pointer to a node which should be the previous node before 'act' + */ +static void node_set_prev(lv_ll_t * ll_p, lv_ll_node_t * act, lv_ll_node_t * prev) +{ + if(act == NULL) return; /*Can't set the prev node of `NULL`*/ + + uint8_t * act8 = (uint8_t *)act; + + act8 += LL_PREV_P_OFFSET(ll_p); + + lv_ll_node_t ** act_node_p = (lv_ll_node_t **) act8; + lv_ll_node_t ** prev_node_p = (lv_ll_node_t **) &prev; + + *act_node_p = *prev_node_p; +} + +/** + * Set the 'next node pointer' of a node + * @param ll_p pointer to linked list + * @param act pointer to a node which next node pointer should be set + * @param next pointer to a node which should be the next node before 'act' + */ +static void node_set_next(lv_ll_t * ll_p, lv_ll_node_t * act, lv_ll_node_t * next) +{ + if(act == NULL) return; /*Can't set the next node of `NULL`*/ + uint8_t * act8 = (uint8_t *)act; + + act8 += LL_NEXT_P_OFFSET(ll_p); + lv_ll_node_t ** act_node_p = (lv_ll_node_t **) act8; + lv_ll_node_t ** next_node_p = (lv_ll_node_t **) &next; + + *act_node_p = *next_node_p; +} diff --git a/lib/lvgl/src/misc/lv_ll.h b/lib/lvgl/src/misc/lv_ll.h new file mode 100644 index 00000000..d38f6928 --- /dev/null +++ b/lib/lvgl/src/misc/lv_ll.h @@ -0,0 +1,167 @@ +/** + * @file lv_ll.h + * Handle linked lists. The nodes are dynamically allocated by the 'lv_mem' module. + */ + +#ifndef LV_LL_H +#define LV_LL_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ +#include +#include +#include + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/** Dummy type to make handling easier*/ +typedef uint8_t lv_ll_node_t; + +/** Description of a linked list*/ +typedef struct { + uint32_t n_size; + lv_ll_node_t * head; + lv_ll_node_t * tail; +} lv_ll_t; + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +/** + * Initialize linked list + * @param ll_p pointer to lv_ll_t variable + * @param node_size the size of 1 node in bytes + */ +void _lv_ll_init(lv_ll_t * ll_p, uint32_t node_size); + +/** + * Add a new head to a linked list + * @param ll_p pointer to linked list + * @return pointer to the new head + */ +void * _lv_ll_ins_head(lv_ll_t * ll_p); + +/** + * Insert a new node in front of the n_act node + * @param ll_p pointer to linked list + * @param n_act pointer a node + * @return pointer to the new node + */ +void * _lv_ll_ins_prev(lv_ll_t * ll_p, void * n_act); + +/** + * Add a new tail to a linked list + * @param ll_p pointer to linked list + * @return pointer to the new tail + */ +void * _lv_ll_ins_tail(lv_ll_t * ll_p); + +/** + * Remove the node 'node_p' from 'll_p' linked list. + * It does not free the memory of node. + * @param ll_p pointer to the linked list of 'node_p' + * @param node_p pointer to node in 'll_p' linked list + */ +void _lv_ll_remove(lv_ll_t * ll_p, void * node_p); + +/** + * Remove and free all elements from a linked list. The list remain valid but become empty. + * @param ll_p pointer to linked list + */ +void _lv_ll_clear(lv_ll_t * ll_p); + +/** + * Move a node to a new linked list + * @param ll_ori_p pointer to the original (old) linked list + * @param ll_new_p pointer to the new linked list + * @param node pointer to a node + * @param head true: be the head in the new list + * false be the tail in the new list + */ +void _lv_ll_chg_list(lv_ll_t * ll_ori_p, lv_ll_t * ll_new_p, void * node, bool head); + +/** + * Return with head node of the linked list + * @param ll_p pointer to linked list + * @return pointer to the head of 'll_p' + */ +void * _lv_ll_get_head(const lv_ll_t * ll_p); + +/** + * Return with tail node of the linked list + * @param ll_p pointer to linked list + * @return pointer to the tail of 'll_p' + */ +void * _lv_ll_get_tail(const lv_ll_t * ll_p); + +/** + * Return with the pointer of the next node after 'n_act' + * @param ll_p pointer to linked list + * @param n_act pointer a node + * @return pointer to the next node + */ +void * _lv_ll_get_next(const lv_ll_t * ll_p, const void * n_act); + +/** + * Return with the pointer of the previous node after 'n_act' + * @param ll_p pointer to linked list + * @param n_act pointer a node + * @return pointer to the previous node + */ +void * _lv_ll_get_prev(const lv_ll_t * ll_p, const void * n_act); + +/** + * Return the length of the linked list. + * @param ll_p pointer to linked list + * @return length of the linked list + */ +uint32_t _lv_ll_get_len(const lv_ll_t * ll_p); + +/** + * TODO + * @param ll_p + * @param n1_p + * @param n2_p +void lv_ll_swap(lv_ll_t * ll_p, void * n1_p, void * n2_p); + */ + +/** + * Move a node before an other node in the same linked list + * @param ll_p pointer to a linked list + * @param n_act pointer to node to move + * @param n_after pointer to a node which should be after `n_act` + */ +void _lv_ll_move_before(lv_ll_t * ll_p, void * n_act, void * n_after); + +/** + * Check if a linked list is empty + * @param ll_p pointer to a linked list + * @return true: the linked list is empty; false: not empty + */ +bool _lv_ll_is_empty(lv_ll_t * ll_p); + +/********************** + * MACROS + **********************/ + +#define _LV_LL_READ(list, i) for(i = _lv_ll_get_head(list); i != NULL; i = _lv_ll_get_next(list, i)) + +#define _LV_LL_READ_BACK(list, i) for(i = _lv_ll_get_tail(list); i != NULL; i = _lv_ll_get_prev(list, i)) + +#ifdef __cplusplus +} /*extern "C"*/ +#endif + +#endif diff --git a/lib/lvgl/src/misc/lv_log.c b/lib/lvgl/src/misc/lv_log.c new file mode 100644 index 00000000..d79463f2 --- /dev/null +++ b/lib/lvgl/src/misc/lv_log.c @@ -0,0 +1,144 @@ +/** + * @file lv_log.c + * + */ + +/********************* + * INCLUDES + *********************/ +#include "lv_log.h" +#if LV_USE_LOG + +#include +#include +#include "lv_printf.h" +#include "../hal/lv_hal_tick.h" + +#if LV_LOG_PRINTF + #include +#endif + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ + +/********************** + * STATIC VARIABLES + **********************/ +static lv_log_print_g_cb_t custom_print_cb; + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +/** + * Register custom print/write function to call when a log is added. + * It can format its "File path", "Line number" and "Description" as required + * and send the formatted log message to a console or serial port. + * @param print_cb a function pointer to print a log + */ +void lv_log_register_print_cb(lv_log_print_g_cb_t print_cb) +{ + custom_print_cb = print_cb; +} + +/** + * Add a log + * @param level the level of log. (From `lv_log_level_t` enum) + * @param file name of the file when the log added + * @param line line number in the source code where the log added + * @param func name of the function when the log added + * @param format printf-like format string + * @param ... parameters for `format` + */ +void _lv_log_add(lv_log_level_t level, const char * file, int line, const char * func, const char * format, ...) +{ + if(level >= _LV_LOG_LEVEL_NUM) return; /*Invalid level*/ + + static uint32_t last_log_time = 0; + + if(level >= LV_LOG_LEVEL) { + va_list args; + va_start(args, format); + + /*Use only the file name not the path*/ + size_t p; + for(p = strlen(file); p > 0; p--) { + if(file[p] == '/' || file[p] == '\\') { + p++; /*Skip the slash*/ + break; + } + } + + uint32_t t = lv_tick_get(); + static const char * lvl_prefix[] = {"Trace", "Info", "Warn", "Error", "User"}; + +#if LV_LOG_PRINTF + printf("[%s]\t(%" LV_PRId32 ".%03" LV_PRId32 ", +%" LV_PRId32 ")\t %s: ", + lvl_prefix[level], t / 1000, t % 1000, t - last_log_time, func); + vprintf(format, args); + printf(" \t(in %s line #%d)\n", &file[p], line); +#else + if(custom_print_cb) { + char buf[512]; +#if LV_SPRINTF_CUSTOM + char msg[256]; + lv_vsnprintf(msg, sizeof(msg), format, args); + lv_snprintf(buf, sizeof(buf), "[%s]\t(%" LV_PRId32 ".%03" LV_PRId32 ", +%" LV_PRId32 ")\t %s: %s \t(in %s line #%d)\n", + lvl_prefix[level], t / 1000, t % 1000, t - last_log_time, func, msg, &file[p], line); +#else + lv_vaformat_t vaf = {format, &args}; + lv_snprintf(buf, sizeof(buf), "[%s]\t(%" LV_PRId32 ".%03" LV_PRId32 ", +%" LV_PRId32 ")\t %s: %pV \t(in %s line #%d)\n", + lvl_prefix[level], t / 1000, t % 1000, t - last_log_time, func, (void *)&vaf, &file[p], line); +#endif + custom_print_cb(buf); + } +#endif + + last_log_time = t; + va_end(args); + } +} + +void lv_log(const char * format, ...) +{ + if(LV_LOG_LEVEL >= LV_LOG_LEVEL_NONE) return; /* disable log */ + + va_list args; + va_start(args, format); + +#if LV_LOG_PRINTF + vprintf(format, args); +#else + if(custom_print_cb) { + char buf[512]; +#if LV_SPRINTF_CUSTOM + lv_vsnprintf(buf, sizeof(buf), format, args); +#else + lv_vaformat_t vaf = {format, &args}; + lv_snprintf(buf, sizeof(buf), "%pV", (void *)&vaf); +#endif + custom_print_cb(buf); + } +#endif + + va_end(args); +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +#endif /*LV_USE_LOG*/ diff --git a/lib/lvgl/src/misc/lv_log.h b/lib/lvgl/src/misc/lv_log.h new file mode 100644 index 00000000..9a009930 --- /dev/null +++ b/lib/lvgl/src/misc/lv_log.h @@ -0,0 +1,154 @@ +/** + * @file lv_log.h + * + */ + +#ifndef LV_LOG_H +#define LV_LOG_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ +#include "../lv_conf_internal.h" +#include + +#include "lv_types.h" + +/********************* + * DEFINES + *********************/ + +/*Possible log level. For compatibility declare it independently from `LV_USE_LOG`*/ + +#define LV_LOG_LEVEL_TRACE 0 /**< A lot of logs to give detailed information*/ +#define LV_LOG_LEVEL_INFO 1 /**< Log important events*/ +#define LV_LOG_LEVEL_WARN 2 /**< Log if something unwanted happened but didn't caused problem*/ +#define LV_LOG_LEVEL_ERROR 3 /**< Only critical issue, when the system may fail*/ +#define LV_LOG_LEVEL_USER 4 /**< Custom logs from the user*/ +#define LV_LOG_LEVEL_NONE 5 /**< Do not log anything*/ +#define _LV_LOG_LEVEL_NUM 6 /**< Number of log levels*/ + +LV_EXPORT_CONST_INT(LV_LOG_LEVEL_TRACE); +LV_EXPORT_CONST_INT(LV_LOG_LEVEL_INFO); +LV_EXPORT_CONST_INT(LV_LOG_LEVEL_WARN); +LV_EXPORT_CONST_INT(LV_LOG_LEVEL_ERROR); +LV_EXPORT_CONST_INT(LV_LOG_LEVEL_USER); +LV_EXPORT_CONST_INT(LV_LOG_LEVEL_NONE); + +typedef int8_t lv_log_level_t; + +#if LV_USE_LOG +/********************** + * TYPEDEFS + **********************/ + +/** + * Log print function. Receives a string buffer to print". + */ +typedef void (*lv_log_print_g_cb_t)(const char * buf); + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +/** + * Register custom print/write function to call when a log is added. + * It can format its "File path", "Line number" and "Description" as required + * and send the formatted log message to a console or serial port. + * @param print_cb a function pointer to print a log + */ +void lv_log_register_print_cb(lv_log_print_g_cb_t print_cb); + +/** + * Print a log message via `printf` if enabled with `LV_LOG_PRINTF` in `lv_conf.h` + * and/or a print callback if registered with `lv_log_register_print_cb` + * @param format printf-like format string + * @param ... parameters for `format` + */ +void lv_log(const char * format, ...) LV_FORMAT_ATTRIBUTE(1, 2); + +/** + * Add a log + * @param level the level of log. (From `lv_log_level_t` enum) + * @param file name of the file when the log added + * @param line line number in the source code where the log added + * @param func name of the function when the log added + * @param format printf-like format string + * @param ... parameters for `format` + */ +void _lv_log_add(lv_log_level_t level, const char * file, int line, + const char * func, const char * format, ...) LV_FORMAT_ATTRIBUTE(5, 6); + +/********************** + * MACROS + **********************/ +#ifndef LV_LOG_TRACE +# if LV_LOG_LEVEL <= LV_LOG_LEVEL_TRACE +# define LV_LOG_TRACE(...) _lv_log_add(LV_LOG_LEVEL_TRACE, __FILE__, __LINE__, __func__, __VA_ARGS__) +# else +# define LV_LOG_TRACE(...) do {}while(0) +# endif +#endif + +#ifndef LV_LOG_INFO +# if LV_LOG_LEVEL <= LV_LOG_LEVEL_INFO +# define LV_LOG_INFO(...) _lv_log_add(LV_LOG_LEVEL_INFO, __FILE__, __LINE__, __func__, __VA_ARGS__) +# else +# define LV_LOG_INFO(...) do {}while(0) +# endif +#endif + +#ifndef LV_LOG_WARN +# if LV_LOG_LEVEL <= LV_LOG_LEVEL_WARN +# define LV_LOG_WARN(...) _lv_log_add(LV_LOG_LEVEL_WARN, __FILE__, __LINE__, __func__, __VA_ARGS__) +# else +# define LV_LOG_WARN(...) do {}while(0) +# endif +#endif + +#ifndef LV_LOG_ERROR +# if LV_LOG_LEVEL <= LV_LOG_LEVEL_ERROR +# define LV_LOG_ERROR(...) _lv_log_add(LV_LOG_LEVEL_ERROR, __FILE__, __LINE__, __func__, __VA_ARGS__) +# else +# define LV_LOG_ERROR(...) do {}while(0) +# endif +#endif + +#ifndef LV_LOG_USER +# if LV_LOG_LEVEL <= LV_LOG_LEVEL_USER +# define LV_LOG_USER(...) _lv_log_add(LV_LOG_LEVEL_USER, __FILE__, __LINE__, __func__, __VA_ARGS__) +# else +# define LV_LOG_USER(...) do {}while(0) +# endif +#endif + +#ifndef LV_LOG +# if LV_LOG_LEVEL < LV_LOG_LEVEL_NONE +# define LV_LOG(...) lv_log(__VA_ARGS__) +# else +# define LV_LOG(...) do {} while(0) +# endif +#endif + +#else /*LV_USE_LOG*/ + +/*Do nothing if `LV_USE_LOG 0`*/ +#define _lv_log_add(level, file, line, ...) +#define LV_LOG_TRACE(...) do {}while(0) +#define LV_LOG_INFO(...) do {}while(0) +#define LV_LOG_WARN(...) do {}while(0) +#define LV_LOG_ERROR(...) do {}while(0) +#define LV_LOG_USER(...) do {}while(0) +#define LV_LOG(...) do {}while(0) + +#endif /*LV_USE_LOG*/ + +#ifdef __cplusplus +} /*extern "C"*/ +#endif + +#endif /*LV_LOG_H*/ diff --git a/lib/lvgl/src/misc/lv_lru.c b/lib/lvgl/src/misc/lv_lru.c new file mode 100644 index 00000000..6ff83903 --- /dev/null +++ b/lib/lvgl/src/misc/lv_lru.c @@ -0,0 +1,349 @@ +/** + * @file lv_lru.c + * + * @see https://github.com/willcannings/C-LRU-Cache + */ + +/********************* + * INCLUDES + *********************/ + +#include "lv_lru.h" +#include "lv_math.h" +#include "lv_mem.h" +#include "lv_assert.h" +#include "lv_log.h" + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +struct _lv_lru_item_t { + void * value; + void * key; + size_t value_length; + size_t key_length; + uint64_t access_count; + struct _lv_lru_item_t * next; +}; + +/********************** + * STATIC PROTOTYPES + **********************/ + +/** + * MurmurHash2 + * @author Austin Appleby + * @see http://sites.google.com/site/murmurhash/ + */ +static uint32_t lv_lru_hash(lv_lru_t * cache, const void * key, uint32_t key_length); + +/** compare a key against an existing item's key */ +static int lv_lru_cmp_keys(lv_lru_item_t * item, const void * key, uint32_t key_length); + +/** remove an item and push it to the free items queue */ +static void lv_lru_remove_item(lv_lru_t * cache, lv_lru_item_t * prev, lv_lru_item_t * item, uint32_t hash_index); + +/** pop an existing item off the free queue, or create a new one */ +static lv_lru_item_t * lv_lru_pop_or_create_item(lv_lru_t * cache); + +/********************** + * STATIC VARIABLES + **********************/ + +/********************** + * MACROS + **********************/ + +/* error helpers */ +#define error_for(conditions, error) if(conditions) {return error;} +#define test_for_missing_cache() error_for(!cache, LV_LRU_MISSING_CACHE) +#define test_for_missing_key() error_for(!key, LV_LRU_MISSING_KEY) +#define test_for_missing_value() error_for(!value || value_length == 0, LV_LRU_MISSING_VALUE) +#define test_for_value_too_large() error_for(value_length > cache->total_memory, LV_LRU_VALUE_TOO_LARGE) + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +lv_lru_t * lv_lru_create(size_t cache_size, size_t average_length, lv_lru_free_t * value_free, + lv_lru_free_t * key_free) +{ + // create the cache + lv_lru_t * cache = (lv_lru_t *) lv_mem_alloc(sizeof(lv_lru_t)); + lv_memset_00(cache, sizeof(lv_lru_t)); + if(!cache) { + LV_LOG_WARN("LRU Cache unable to create cache object"); + return NULL; + } + cache->hash_table_size = cache_size / average_length; + cache->average_item_length = average_length; + cache->free_memory = cache_size; + cache->total_memory = cache_size; + cache->seed = lv_rand(1, UINT32_MAX); + cache->value_free = value_free ? value_free : lv_mem_free; + cache->key_free = key_free ? key_free : lv_mem_free; + + // size the hash table to a guestimate of the number of slots required (assuming a perfect hash) + cache->items = (lv_lru_item_t **) lv_mem_alloc(sizeof(lv_lru_item_t *) * cache->hash_table_size); + lv_memset_00(cache->items, sizeof(lv_lru_item_t *) * cache->hash_table_size); + if(!cache->items) { + LV_LOG_WARN("LRU Cache unable to create cache hash table"); + lv_mem_free(cache); + return NULL; + } + return cache; +} + + +void lv_lru_del(lv_lru_t * cache) +{ + LV_ASSERT_NULL(cache); + + // free each of the cached items, and the hash table + lv_lru_item_t * item = NULL, *next = NULL; + uint32_t i = 0; + if(cache->items) { + for(; i < cache->hash_table_size; i++) { + item = cache->items[i]; + while(item) { + next = (lv_lru_item_t *) item->next; + cache->value_free(item->value); + cache->key_free(item->key); + cache->free_memory += item->value_length; + lv_mem_free(item); + item = next; + } + } + lv_mem_free(cache->items); + } + + if(cache->free_items) { + item = cache->free_items; + while(item) { + next = (lv_lru_item_t *) item->next; + lv_mem_free(item); + item = next; + } + } + + // free the cache + lv_mem_free(cache); +} + + +lv_lru_res_t lv_lru_set(lv_lru_t * cache, const void * key, size_t key_length, void * value, size_t value_length) +{ + test_for_missing_cache(); + test_for_missing_key(); + test_for_missing_value(); + test_for_value_too_large(); + + // see if the key already exists + uint32_t hash_index = lv_lru_hash(cache, key, key_length); + int required = 0; + lv_lru_item_t * item = NULL, *prev = NULL; + item = cache->items[hash_index]; + + while(item && lv_lru_cmp_keys(item, key, key_length)) { + prev = item; + item = (lv_lru_item_t *) item->next; + } + + if(item) { + // update the value and value_lengths + required = (int)(value_length - item->value_length); + cache->value_free(item->value); + item->value = value; + item->value_length = value_length; + + } + else { + // insert a new item + item = lv_lru_pop_or_create_item(cache); + item->value = value; + item->key = lv_mem_alloc(key_length); + memcpy(item->key, key, key_length); + item->value_length = value_length; + item->key_length = key_length; + required = (int) value_length; + + if(prev) + prev->next = item; + else + cache->items[hash_index] = item; + } + item->access_count = ++cache->access_count; + + // remove as many items as necessary to free enough space + if(required > 0 && (size_t) required > cache->free_memory) { + while(cache->free_memory < (size_t) required) + lv_lru_remove_lru_item(cache); + } + cache->free_memory -= required; + return LV_LRU_OK; +} + + +lv_lru_res_t lv_lru_get(lv_lru_t * cache, const void * key, size_t key_size, void ** value) +{ + test_for_missing_cache(); + test_for_missing_key(); + + // loop until we find the item, or hit the end of a chain + uint32_t hash_index = lv_lru_hash(cache, key, key_size); + lv_lru_item_t * item = cache->items[hash_index]; + + while(item && lv_lru_cmp_keys(item, key, key_size)) + item = (lv_lru_item_t *) item->next; + + if(item) { + *value = item->value; + item->access_count = ++cache->access_count; + } + else { + *value = NULL; + } + + return LV_LRU_OK; +} + +lv_lru_res_t lv_lru_remove(lv_lru_t * cache, const void * key, size_t key_size) +{ + test_for_missing_cache(); + test_for_missing_key(); + + // loop until we find the item, or hit the end of a chain + lv_lru_item_t * item = NULL, *prev = NULL; + uint32_t hash_index = lv_lru_hash(cache, key, key_size); + item = cache->items[hash_index]; + + while(item && lv_lru_cmp_keys(item, key, key_size)) { + prev = item; + item = (lv_lru_item_t *) item->next; + } + + if(item) { + lv_lru_remove_item(cache, prev, item, hash_index); + } + + return LV_LRU_OK; +} + +void lv_lru_remove_lru_item(lv_lru_t * cache) +{ + lv_lru_item_t * min_item = NULL, *min_prev = NULL; + lv_lru_item_t * item = NULL, *prev = NULL; + uint32_t i = 0, min_index = -1; + uint64_t min_access_count = -1; + + for(; i < cache->hash_table_size; i++) { + item = cache->items[i]; + prev = NULL; + + while(item) { + if(item->access_count < min_access_count || (int64_t) min_access_count == -1) { + min_access_count = item->access_count; + min_item = item; + min_prev = prev; + min_index = i; + } + prev = item; + item = item->next; + } + } + + if(min_item) { + lv_lru_remove_item(cache, min_prev, min_item, min_index); + } +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +static uint32_t lv_lru_hash(lv_lru_t * cache, const void * key, uint32_t key_length) +{ + uint32_t m = 0x5bd1e995; + uint32_t r = 24; + uint32_t h = cache->seed ^ key_length; + char * data = (char *) key; + + while(key_length >= 4) { + uint32_t k = *(uint32_t *) data; + k *= m; + k ^= k >> r; + k *= m; + h *= m; + h ^= k; + data += 4; + key_length -= 4; + } + + if(key_length >= 3) { + h ^= data[2] << 16; + } + if(key_length >= 2) { + h ^= data[1] << 8; + } + if(key_length >= 1) { + h ^= data[0]; + h *= m; + } + + h ^= h >> 13; + h *= m; + h ^= h >> 15; + return h % cache->hash_table_size; +} + +static int lv_lru_cmp_keys(lv_lru_item_t * item, const void * key, uint32_t key_length) +{ + if(key_length != item->key_length) { + return 1; + } + else { + return memcmp(key, item->key, key_length); + } +} + +static void lv_lru_remove_item(lv_lru_t * cache, lv_lru_item_t * prev, lv_lru_item_t * item, uint32_t hash_index) +{ + if(prev) { + prev->next = item->next; + } + else { + cache->items[hash_index] = (lv_lru_item_t *) item->next; + } + + // free memory and update the free memory counter + cache->free_memory += item->value_length; + cache->value_free(item->value); + cache->key_free(item->key); + + // push the item to the free items queue + lv_memset_00(item, sizeof(lv_lru_item_t)); + item->next = cache->free_items; + cache->free_items = item; +} + +static lv_lru_item_t * lv_lru_pop_or_create_item(lv_lru_t * cache) +{ + lv_lru_item_t * item = NULL; + + if(cache->free_items) { + item = cache->free_items; + cache->free_items = item->next; + lv_memset_00(item, sizeof(lv_lru_item_t)); + } + else { + item = (lv_lru_item_t *) lv_mem_alloc(sizeof(lv_lru_item_t)); + lv_memset_00(item, sizeof(lv_lru_item_t)); + } + + return item; +} diff --git a/lib/lvgl/src/misc/lv_lru.h b/lib/lvgl/src/misc/lv_lru.h new file mode 100644 index 00000000..2d0134e5 --- /dev/null +++ b/lib/lvgl/src/misc/lv_lru.h @@ -0,0 +1,87 @@ +/** + * @file lv_lru.h + * + */ + +#ifndef LV_LRU_H +#define LV_LRU_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ + +#include "../lv_conf_internal.h" + +#include "lv_types.h" + +#include +#include + + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +typedef enum { + LV_LRU_OK = 0, + LV_LRU_MISSING_CACHE, + LV_LRU_MISSING_KEY, + LV_LRU_MISSING_VALUE, + LV_LRU_LOCK_ERROR, + LV_LRU_VALUE_TOO_LARGE +} lv_lru_res_t; + +typedef void (lv_lru_free_t)(void * v); +typedef struct _lv_lru_item_t lv_lru_item_t; + +typedef struct lv_lru_t { + lv_lru_item_t ** items; + uint64_t access_count; + size_t free_memory; + size_t total_memory; + size_t average_item_length; + size_t hash_table_size; + uint32_t seed; + lv_lru_free_t * value_free; + lv_lru_free_t * key_free; + lv_lru_item_t * free_items; +} lv_lru_t; + + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +lv_lru_t * lv_lru_create(size_t cache_size, size_t average_length, lv_lru_free_t * value_free, + lv_lru_free_t * key_free); + +void lv_lru_del(lv_lru_t * cache); + +lv_lru_res_t lv_lru_set(lv_lru_t * cache, const void * key, size_t key_length, void * value, size_t value_length); + +lv_lru_res_t lv_lru_get(lv_lru_t * cache, const void * key, size_t key_size, void ** value); + +lv_lru_res_t lv_lru_remove(lv_lru_t * cache, const void * key, size_t key_size); + +/** + * remove the least recently used item + * + * @todo we can optimise this by finding the n lru items, where n = required_space / average_length + */ +void lv_lru_remove_lru_item(lv_lru_t * cache); +/********************** + * MACROS + **********************/ +#ifdef __cplusplus +} /*extern "C"*/ +#endif + +#endif /*LV_LRU_H*/ diff --git a/lib/lvgl/src/misc/lv_math.c b/lib/lvgl/src/misc/lv_math.c new file mode 100644 index 00000000..bfb3934b --- /dev/null +++ b/lib/lvgl/src/misc/lv_math.c @@ -0,0 +1,273 @@ +/** + * @file lv_math.c + * + */ + +/********************* + * INCLUDES + *********************/ +#include "lv_math.h" + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ + +/********************** + * STATIC VARIABLES + **********************/ +static const int16_t sin0_90_table[] = { + 0, 572, 1144, 1715, 2286, 2856, 3425, 3993, 4560, 5126, 5690, 6252, 6813, 7371, 7927, 8481, + 9032, 9580, 10126, 10668, 11207, 11743, 12275, 12803, 13328, 13848, 14364, 14876, 15383, 15886, 16383, 16876, + 17364, 17846, 18323, 18794, 19260, 19720, 20173, 20621, 21062, 21497, 21925, 22347, 22762, 23170, 23571, 23964, + 24351, 24730, 25101, 25465, 25821, 26169, 26509, 26841, 27165, 27481, 27788, 28087, 28377, 28659, 28932, 29196, + 29451, 29697, 29934, 30162, 30381, 30591, 30791, 30982, 31163, 31335, 31498, 31650, 31794, 31927, 32051, 32165, + 32269, 32364, 32448, 32523, 32587, 32642, 32687, 32722, 32747, 32762, 32767 +}; + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +/** + * Return with sinus of an angle + * @param angle + * @return sinus of 'angle'. sin(-90) = -32767, sin(90) = 32767 + */ +LV_ATTRIBUTE_FAST_MEM int16_t lv_trigo_sin(int16_t angle) +{ + int16_t ret = 0; + angle = angle % 360; + + if(angle < 0) angle = 360 + angle; + + if(angle < 90) { + ret = sin0_90_table[angle]; + } + else if(angle >= 90 && angle < 180) { + angle = 180 - angle; + ret = sin0_90_table[angle]; + } + else if(angle >= 180 && angle < 270) { + angle = angle - 180; + ret = -sin0_90_table[angle]; + } + else { /*angle >=270*/ + angle = 360 - angle; + ret = -sin0_90_table[angle]; + } + + return ret; +} + +/** + * Calculate a value of a Cubic Bezier function. + * @param t time in range of [0..LV_BEZIER_VAL_MAX] + * @param u0 start values in range of [0..LV_BEZIER_VAL_MAX] + * @param u1 control value 1 values in range of [0..LV_BEZIER_VAL_MAX] + * @param u2 control value 2 in range of [0..LV_BEZIER_VAL_MAX] + * @param u3 end values in range of [0..LV_BEZIER_VAL_MAX] + * @return the value calculated from the given parameters in range of [0..LV_BEZIER_VAL_MAX] + */ +uint32_t lv_bezier3(uint32_t t, uint32_t u0, uint32_t u1, uint32_t u2, uint32_t u3) +{ + uint32_t t_rem = 1024 - t; + uint32_t t_rem2 = (t_rem * t_rem) >> 10; + uint32_t t_rem3 = (t_rem2 * t_rem) >> 10; + uint32_t t2 = (t * t) >> 10; + uint32_t t3 = (t2 * t) >> 10; + + uint32_t v1 = (t_rem3 * u0) >> 10; + uint32_t v2 = (3 * t_rem2 * t * u1) >> 20; + uint32_t v3 = (3 * t_rem * t2 * u2) >> 20; + uint32_t v4 = (t3 * u3) >> 10; + + return v1 + v2 + v3 + v4; +} + +/** + * Get the square root of a number + * @param x integer which square root should be calculated + * @param q store the result here. q->i: integer part, q->f: fractional part in 1/256 unit + * @param mask optional to skip some iterations if the magnitude of the root is known. + * Set to 0x8000 by default. + * If root < 16: mask = 0x80 + * If root < 256: mask = 0x800 + * Else: mask = 0x8000 + */ +LV_ATTRIBUTE_FAST_MEM void lv_sqrt(uint32_t x, lv_sqrt_res_t * q, uint32_t mask) +{ + x = x << 8; /*To get 4 bit precision. (sqrt(256) = 16 = 4 bit)*/ + + uint32_t root = 0; + uint32_t trial; + // http://ww1.microchip.com/...en/AppNotes/91040a.pdf + do { + trial = root + mask; + if(trial * trial <= x) root = trial; + mask = mask >> 1; + } while(mask); + + q->i = root >> 4; + q->f = (root & 0xf) << 4; +} + +/** + * Calculate the atan2 of a vector. + * @param x + * @param y + * @return the angle in degree calculated from the given parameters in range of [0..360] + */ +uint16_t lv_atan2(int x, int y) +{ + // Fast XY vector to integer degree algorithm - Jan 2011 www.RomanBlack.com + // Converts any XY values including 0 to a degree value that should be + // within +/- 1 degree of the accurate value without needing + // large slow trig functions like ArcTan() or ArcCos(). + // NOTE! at least one of the X or Y values must be non-zero! + // This is the full version, for all 4 quadrants and will generate + // the angle in integer degrees from 0-360. + // Any values of X and Y are usable including negative values provided + // they are between -1456 and 1456 so the 16bit multiply does not overflow. + + unsigned char negflag; + unsigned char tempdegree; + unsigned char comp; + unsigned int degree; // this will hold the result + unsigned int ux; + unsigned int uy; + + // Save the sign flags then remove signs and get XY as unsigned ints + negflag = 0; + if(x < 0) { + negflag += 0x01; // x flag bit + x = (0 - x); // is now + + } + ux = x; // copy to unsigned var before multiply + if(y < 0) { + negflag += 0x02; // y flag bit + y = (0 - y); // is now + + } + uy = y; // copy to unsigned var before multiply + + // 1. Calc the scaled "degrees" + if(ux > uy) { + degree = (uy * 45) / ux; // degree result will be 0-45 range + negflag += 0x10; // octant flag bit + } + else { + degree = (ux * 45) / uy; // degree result will be 0-45 range + } + + // 2. Compensate for the 4 degree error curve + comp = 0; + tempdegree = degree; // use an unsigned char for speed! + if(tempdegree > 22) { // if top half of range + if(tempdegree <= 44) comp++; + if(tempdegree <= 41) comp++; + if(tempdegree <= 37) comp++; + if(tempdegree <= 32) comp++; // max is 4 degrees compensated + } + else { // else is lower half of range + if(tempdegree >= 2) comp++; + if(tempdegree >= 6) comp++; + if(tempdegree >= 10) comp++; + if(tempdegree >= 15) comp++; // max is 4 degrees compensated + } + degree += comp; // degree is now accurate to +/- 1 degree! + + // Invert degree if it was X>Y octant, makes 0-45 into 90-45 + if(negflag & 0x10) degree = (90 - degree); + + // 3. Degree is now 0-90 range for this quadrant, + // need to invert it for whichever quadrant it was in + if(negflag & 0x02) { // if -Y + if(negflag & 0x01) // if -Y -X + degree = (180 + degree); + else // else is -Y +X + degree = (180 - degree); + } + else { // else is +Y + if(negflag & 0x01) // if +Y -X + degree = (360 - degree); + } + return degree; +} + +/** + * Calculate the integer exponents. + * @param base + * @param power + * @return base raised to the power exponent + */ +int64_t lv_pow(int64_t base, int8_t exp) +{ + int64_t result = 1; + while(exp) { + if(exp & 1) + result *= base; + exp >>= 1; + base *= base; + } + + return result; +} + +/** + * Get the mapped of a number given an input and output range + * @param x integer which mapped value should be calculated + * @param min_in min input range + * @param max_in max input range + * @param min_out max output range + * @param max_out max output range + * @return the mapped number + */ +int32_t lv_map(int32_t x, int32_t min_in, int32_t max_in, int32_t min_out, int32_t max_out) +{ + if(max_in >= min_in && x >= max_in) return max_out; + if(max_in >= min_in && x <= min_in) return min_out; + + if(max_in <= min_in && x <= max_in) return max_out; + if(max_in <= min_in && x >= min_in) return min_out; + + /** + * The equation should be: + * ((x - min_in) * delta_out) / delta in) + min_out + * To avoid rounding error reorder the operations: + * (x - min_in) * (delta_out / delta_min) + min_out + */ + + int32_t delta_in = max_in - min_in; + int32_t delta_out = max_out - min_out; + + return ((x - min_in) * delta_out) / delta_in + min_out; +} + +uint32_t lv_rand(uint32_t min, uint32_t max) +{ + static uint32_t a = 0x1234ABCD; /*Seed*/ + + /*Algorithm "xor" from p. 4 of Marsaglia, "Xorshift RNGs"*/ + uint32_t x = a; + x ^= x << 13; + x ^= x >> 17; + x ^= x << 5; + a = x; + + return (a % (max - min + 1)) + min; +} + +/********************** + * STATIC FUNCTIONS + **********************/ diff --git a/lib/lvgl/src/misc/lv_math.h b/lib/lvgl/src/misc/lv_math.h new file mode 100644 index 00000000..4b2860a9 --- /dev/null +++ b/lib/lvgl/src/misc/lv_math.h @@ -0,0 +1,143 @@ +/** + * @file lv_math.h + * + */ + +#ifndef LV_MATH_H +#define LV_MATH_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ +#include "../lv_conf_internal.h" +#include + +/********************* + * DEFINES + *********************/ +#define LV_TRIGO_SIN_MAX 32767 +#define LV_TRIGO_SHIFT 15 /**< >> LV_TRIGO_SHIFT to normalize*/ + +#define LV_BEZIER_VAL_MAX 1024 /**< Max time in Bezier functions (not [0..1] to use integers)*/ +#define LV_BEZIER_VAL_SHIFT 10 /**< log2(LV_BEZIER_VAL_MAX): used to normalize up scaled values*/ + +/********************** + * TYPEDEFS + **********************/ + +typedef struct { + uint16_t i; + uint16_t f; +} lv_sqrt_res_t; + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +//! @cond Doxygen_Suppress +/** + * Return with sinus of an angle + * @param angle + * @return sinus of 'angle'. sin(-90) = -32767, sin(90) = 32767 + */ +LV_ATTRIBUTE_FAST_MEM int16_t lv_trigo_sin(int16_t angle); + +static inline LV_ATTRIBUTE_FAST_MEM int16_t lv_trigo_cos(int16_t angle) +{ + return lv_trigo_sin(angle + 90); +} + +//! @endcond + +/** + * Calculate a value of a Cubic Bezier function. + * @param t time in range of [0..LV_BEZIER_VAL_MAX] + * @param u0 start values in range of [0..LV_BEZIER_VAL_MAX] + * @param u1 control value 1 values in range of [0..LV_BEZIER_VAL_MAX] + * @param u2 control value 2 in range of [0..LV_BEZIER_VAL_MAX] + * @param u3 end values in range of [0..LV_BEZIER_VAL_MAX] + * @return the value calculated from the given parameters in range of [0..LV_BEZIER_VAL_MAX] + */ +uint32_t lv_bezier3(uint32_t t, uint32_t u0, uint32_t u1, uint32_t u2, uint32_t u3); + +/** + * Calculate the atan2 of a vector. + * @param x + * @param y + * @return the angle in degree calculated from the given parameters in range of [0..360] + */ +uint16_t lv_atan2(int x, int y); + +//! @cond Doxygen_Suppress + +/** + * Get the square root of a number + * @param x integer which square root should be calculated + * @param q store the result here. q->i: integer part, q->f: fractional part in 1/256 unit + * @param mask optional to skip some iterations if the magnitude of the root is known. + * Set to 0x8000 by default. + * If root < 16: mask = 0x80 + * If root < 256: mask = 0x800 + * Else: mask = 0x8000 + */ +LV_ATTRIBUTE_FAST_MEM void lv_sqrt(uint32_t x, lv_sqrt_res_t * q, uint32_t mask); + +//! @endcond + +/** + * Calculate the integer exponents. + * @param base + * @param power + * @return base raised to the power exponent + */ +int64_t lv_pow(int64_t base, int8_t exp); + +/** + * Get the mapped of a number given an input and output range + * @param x integer which mapped value should be calculated + * @param min_in min input range + * @param max_in max input range + * @param min_out max output range + * @param max_out max output range + * @return the mapped number + */ +int32_t lv_map(int32_t x, int32_t min_in, int32_t max_in, int32_t min_out, int32_t max_out); + +/** + * Get a pseudo random number in the given range + * @param min the minimum value + * @param max the maximum value + * @return return the random number. min <= return_value <= max + */ +uint32_t lv_rand(uint32_t min, uint32_t max); + +/********************** + * MACROS + **********************/ +#define LV_MIN(a, b) ((a) < (b) ? (a) : (b)) +#define LV_MIN3(a, b, c) (LV_MIN(LV_MIN(a,b), c)) +#define LV_MIN4(a, b, c, d) (LV_MIN(LV_MIN(a,b), LV_MIN(c,d))) + +#define LV_MAX(a, b) ((a) > (b) ? (a) : (b)) +#define LV_MAX3(a, b, c) (LV_MAX(LV_MAX(a,b), c)) +#define LV_MAX4(a, b, c, d) (LV_MAX(LV_MAX(a,b), LV_MAX(c,d))) + +#define LV_CLAMP(min, val, max) (LV_MAX(min, (LV_MIN(val, max)))) + +#define LV_ABS(x) ((x) > 0 ? (x) : (-(x))) +#define LV_UDIV255(x) (((x) * 0x8081U) >> 0x17) + +#define LV_IS_SIGNED(t) (((t)(-1)) < ((t)0)) +#define LV_UMAX_OF(t) (((0x1ULL << ((sizeof(t) * 8ULL) - 1ULL)) - 1ULL) | (0xFULL << ((sizeof(t) * 8ULL) - 4ULL))) +#define LV_SMAX_OF(t) (((0x1ULL << ((sizeof(t) * 8ULL) - 1ULL)) - 1ULL) | (0x7ULL << ((sizeof(t) * 8ULL) - 4ULL))) +#define LV_MAX_OF(t) ((unsigned long)(LV_IS_SIGNED(t) ? LV_SMAX_OF(t) : LV_UMAX_OF(t))) + +#ifdef __cplusplus +} /*extern "C"*/ +#endif + +#endif diff --git a/lib/lvgl/src/misc/lv_mem.c b/lib/lvgl/src/misc/lv_mem.c new file mode 100644 index 00000000..b7c602f7 --- /dev/null +++ b/lib/lvgl/src/misc/lv_mem.c @@ -0,0 +1,566 @@ +/** + * @file lv_mem.c + * General and portable implementation of malloc and free. + * The dynamic memory monitoring is also supported. + */ + +/********************* + * INCLUDES + *********************/ +#include "lv_mem.h" +#include "lv_tlsf.h" +#include "lv_gc.h" +#include "lv_assert.h" +#include "lv_log.h" + +#if LV_MEM_CUSTOM != 0 + #include LV_MEM_CUSTOM_INCLUDE +#endif + +#ifdef LV_MEM_POOL_INCLUDE + #include LV_MEM_POOL_INCLUDE +#endif + +/********************* + * DEFINES + *********************/ +/*memset the allocated memories to 0xaa and freed memories to 0xbb (just for testing purposes)*/ +#ifndef LV_MEM_ADD_JUNK + #define LV_MEM_ADD_JUNK 0 +#endif + +#ifdef LV_ARCH_64 + #define MEM_UNIT uint64_t + #define ALIGN_MASK 0x7 +#else + #define MEM_UNIT uint32_t + #define ALIGN_MASK 0x3 +#endif + +#define ZERO_MEM_SENTINEL 0xa1b2c3d4 + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ +#if LV_MEM_CUSTOM == 0 + static void lv_mem_walker(void * ptr, size_t size, int used, void * user); +#endif + +/********************** + * STATIC VARIABLES + **********************/ +#if LV_MEM_CUSTOM == 0 + static lv_tlsf_t tlsf; + static uint32_t cur_used; + static uint32_t max_used; +#endif + +static uint32_t zero_mem = ZERO_MEM_SENTINEL; /*Give the address of this variable if 0 byte should be allocated*/ + +/********************** + * MACROS + **********************/ +#if LV_LOG_TRACE_MEM + #define MEM_TRACE(...) LV_LOG_TRACE(__VA_ARGS__) +#else + #define MEM_TRACE(...) +#endif + +#define COPY32 *d32 = *s32; d32++; s32++; +#define COPY8 *d8 = *s8; d8++; s8++; +#define SET32(x) *d32 = x; d32++; +#define SET8(x) *d8 = x; d8++; +#define REPEAT8(expr) expr expr expr expr expr expr expr expr + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +/** + * Initialize the dyn_mem module (work memory and other variables) + */ +void lv_mem_init(void) +{ +#if LV_MEM_CUSTOM == 0 + +#if LV_MEM_ADR == 0 +#ifdef LV_MEM_POOL_ALLOC + tlsf = lv_tlsf_create_with_pool((void *)LV_MEM_POOL_ALLOC(LV_MEM_SIZE), LV_MEM_SIZE); +#else + /*Allocate a large array to store the dynamically allocated data*/ + static LV_ATTRIBUTE_LARGE_RAM_ARRAY MEM_UNIT work_mem_int[LV_MEM_SIZE / sizeof(MEM_UNIT)]; + tlsf = lv_tlsf_create_with_pool((void *)work_mem_int, LV_MEM_SIZE); +#endif +#else + tlsf = lv_tlsf_create_with_pool((void *)LV_MEM_ADR, LV_MEM_SIZE); +#endif +#endif + +#if LV_MEM_ADD_JUNK + LV_LOG_WARN("LV_MEM_ADD_JUNK is enabled which makes LVGL much slower"); +#endif +} + +/** + * Clean up the memory buffer which frees all the allocated memories. + * @note It work only if `LV_MEM_CUSTOM == 0` + */ +void lv_mem_deinit(void) +{ +#if LV_MEM_CUSTOM == 0 + lv_tlsf_destroy(tlsf); + lv_mem_init(); +#endif +} + +/** + * Allocate a memory dynamically + * @param size size of the memory to allocate in bytes + * @return pointer to the allocated memory + */ +void * lv_mem_alloc(size_t size) +{ + MEM_TRACE("allocating %lu bytes", (unsigned long)size); + if(size == 0) { + MEM_TRACE("using zero_mem"); + return &zero_mem; + } + +#if LV_MEM_CUSTOM == 0 + void * alloc = lv_tlsf_malloc(tlsf, size); +#else + void * alloc = LV_MEM_CUSTOM_ALLOC(size); +#endif + + if(alloc == NULL) { + LV_LOG_INFO("couldn't allocate memory (%lu bytes)", (unsigned long)size); +#if LV_LOG_LEVEL <= LV_LOG_LEVEL_INFO + lv_mem_monitor_t mon; + lv_mem_monitor(&mon); + LV_LOG_INFO("used: %6d (%3d %%), frag: %3d %%, biggest free: %6d", + (int)(mon.total_size - mon.free_size), mon.used_pct, mon.frag_pct, + (int)mon.free_biggest_size); +#endif + } +#if LV_MEM_ADD_JUNK + else { + lv_memset(alloc, 0xaa, size); + } +#endif + + if(alloc) { +#if LV_MEM_CUSTOM == 0 + cur_used += size; + max_used = LV_MAX(cur_used, max_used); +#endif + MEM_TRACE("allocated at %p", alloc); + } + return alloc; +} + +/** + * Free an allocated data + * @param data pointer to an allocated memory + */ +void lv_mem_free(void * data) +{ + MEM_TRACE("freeing %p", data); + if(data == &zero_mem) return; + if(data == NULL) return; + +#if LV_MEM_CUSTOM == 0 +# if LV_MEM_ADD_JUNK + lv_memset(data, 0xbb, lv_tlsf_block_size(data)); +# endif + size_t size = lv_tlsf_free(tlsf, data); + if(cur_used > size) cur_used -= size; + else cur_used = 0; +#else + LV_MEM_CUSTOM_FREE(data); +#endif +} + +/** + * Reallocate a memory with a new size. The old content will be kept. + * @param data pointer to an allocated memory. + * Its content will be copied to the new memory block and freed + * @param new_size the desired new size in byte + * @return pointer to the new memory + */ +void * lv_mem_realloc(void * data_p, size_t new_size) +{ + MEM_TRACE("reallocating %p with %lu size", data_p, (unsigned long)new_size); + if(new_size == 0) { + MEM_TRACE("using zero_mem"); + lv_mem_free(data_p); + return &zero_mem; + } + + if(data_p == &zero_mem) return lv_mem_alloc(new_size); + +#if LV_MEM_CUSTOM == 0 + void * new_p = lv_tlsf_realloc(tlsf, data_p, new_size); +#else + void * new_p = LV_MEM_CUSTOM_REALLOC(data_p, new_size); +#endif + if(new_p == NULL) { + LV_LOG_ERROR("couldn't allocate memory"); + return NULL; + } + + MEM_TRACE("allocated at %p", new_p); + return new_p; +} + +lv_res_t lv_mem_test(void) +{ + if(zero_mem != ZERO_MEM_SENTINEL) { + LV_LOG_WARN("zero_mem is written"); + return LV_RES_INV; + } + +#if LV_MEM_CUSTOM == 0 + if(lv_tlsf_check(tlsf)) { + LV_LOG_WARN("failed"); + return LV_RES_INV; + } + + if(lv_tlsf_check_pool(lv_tlsf_get_pool(tlsf))) { + LV_LOG_WARN("pool failed"); + return LV_RES_INV; + } +#endif + MEM_TRACE("passed"); + return LV_RES_OK; +} + +/** + * Give information about the work memory of dynamic allocation + * @param mon_p pointer to a lv_mem_monitor_t variable, + * the result of the analysis will be stored here + */ +void lv_mem_monitor(lv_mem_monitor_t * mon_p) +{ + /*Init the data*/ + lv_memset(mon_p, 0, sizeof(lv_mem_monitor_t)); +#if LV_MEM_CUSTOM == 0 + MEM_TRACE("begin"); + + lv_tlsf_walk_pool(lv_tlsf_get_pool(tlsf), lv_mem_walker, mon_p); + + mon_p->total_size = LV_MEM_SIZE; + mon_p->used_pct = 100 - (100U * mon_p->free_size) / mon_p->total_size; + if(mon_p->free_size > 0) { + mon_p->frag_pct = mon_p->free_biggest_size * 100U / mon_p->free_size; + mon_p->frag_pct = 100 - mon_p->frag_pct; + } + else { + mon_p->frag_pct = 0; /*no fragmentation if all the RAM is used*/ + } + + mon_p->max_used = max_used; + + MEM_TRACE("finished"); +#endif +} + + +/** + * Get a temporal buffer with the given size. + * @param size the required size + */ +void * lv_mem_buf_get(uint32_t size) +{ + if(size == 0) return NULL; + + MEM_TRACE("begin, getting %d bytes", size); + + /*Try to find a free buffer with suitable size*/ + int8_t i_guess = -1; + for(uint8_t i = 0; i < LV_MEM_BUF_MAX_NUM; i++) { + if(LV_GC_ROOT(lv_mem_buf[i]).used == 0 && LV_GC_ROOT(lv_mem_buf[i]).size >= size) { + if(LV_GC_ROOT(lv_mem_buf[i]).size == size) { + LV_GC_ROOT(lv_mem_buf[i]).used = 1; + return LV_GC_ROOT(lv_mem_buf[i]).p; + } + else if(i_guess < 0) { + i_guess = i; + } + /*If size of `i` is closer to `size` prefer it*/ + else if(LV_GC_ROOT(lv_mem_buf[i]).size < LV_GC_ROOT(lv_mem_buf[i_guess]).size) { + i_guess = i; + } + } + } + + if(i_guess >= 0) { + LV_GC_ROOT(lv_mem_buf[i_guess]).used = 1; + MEM_TRACE("returning already allocated buffer (buffer id: %d, address: %p)", i_guess, + LV_GC_ROOT(lv_mem_buf[i_guess]).p); + return LV_GC_ROOT(lv_mem_buf[i_guess]).p; + } + + /*Reallocate a free buffer*/ + for(uint8_t i = 0; i < LV_MEM_BUF_MAX_NUM; i++) { + if(LV_GC_ROOT(lv_mem_buf[i]).used == 0) { + /*if this fails you probably need to increase your LV_MEM_SIZE/heap size*/ + void * buf = lv_mem_realloc(LV_GC_ROOT(lv_mem_buf[i]).p, size); + LV_ASSERT_MSG(buf != NULL, "Out of memory, can't allocate a new buffer (increase your LV_MEM_SIZE/heap size)"); + if(buf == NULL) return NULL; + + LV_GC_ROOT(lv_mem_buf[i]).used = 1; + LV_GC_ROOT(lv_mem_buf[i]).size = size; + LV_GC_ROOT(lv_mem_buf[i]).p = buf; + MEM_TRACE("allocated (buffer id: %d, address: %p)", i, LV_GC_ROOT(lv_mem_buf[i]).p); + return LV_GC_ROOT(lv_mem_buf[i]).p; + } + } + + LV_LOG_ERROR("no more buffers. (increase LV_MEM_BUF_MAX_NUM)"); + LV_ASSERT_MSG(false, "No more buffers. Increase LV_MEM_BUF_MAX_NUM."); + return NULL; +} + +/** + * Release a memory buffer + * @param p buffer to release + */ +void lv_mem_buf_release(void * p) +{ + MEM_TRACE("begin (address: %p)", p); + + for(uint8_t i = 0; i < LV_MEM_BUF_MAX_NUM; i++) { + if(LV_GC_ROOT(lv_mem_buf[i]).p == p) { + LV_GC_ROOT(lv_mem_buf[i]).used = 0; + return; + } + } + + LV_LOG_ERROR("p is not a known buffer"); +} + +/** + * Free all memory buffers + */ +void lv_mem_buf_free_all(void) +{ + for(uint8_t i = 0; i < LV_MEM_BUF_MAX_NUM; i++) { + if(LV_GC_ROOT(lv_mem_buf[i]).p) { + lv_mem_free(LV_GC_ROOT(lv_mem_buf[i]).p); + LV_GC_ROOT(lv_mem_buf[i]).p = NULL; + LV_GC_ROOT(lv_mem_buf[i]).used = 0; + LV_GC_ROOT(lv_mem_buf[i]).size = 0; + } + } +} + +#if LV_MEMCPY_MEMSET_STD == 0 +/** + * Same as `memcpy` but optimized for 4 byte operation. + * @param dst pointer to the destination buffer + * @param src pointer to the source buffer + * @param len number of byte to copy + */ +LV_ATTRIBUTE_FAST_MEM void * lv_memcpy(void * dst, const void * src, size_t len) +{ + uint8_t * d8 = dst; + const uint8_t * s8 = src; + + lv_uintptr_t d_align = (lv_uintptr_t)d8 & ALIGN_MASK; + lv_uintptr_t s_align = (lv_uintptr_t)s8 & ALIGN_MASK; + + /*Byte copy for unaligned memories*/ + if(s_align != d_align) { + while(len > 32) { + REPEAT8(COPY8); + REPEAT8(COPY8); + REPEAT8(COPY8); + REPEAT8(COPY8); + len -= 32; + } + while(len) { + COPY8 + len--; + } + return dst; + } + + /*Make the memories aligned*/ + if(d_align) { + d_align = ALIGN_MASK + 1 - d_align; + while(d_align && len) { + COPY8; + d_align--; + len--; + } + } + + uint32_t * d32 = (uint32_t *)d8; + const uint32_t * s32 = (uint32_t *)s8; + while(len > 32) { + REPEAT8(COPY32) + len -= 32; + } + + while(len > 4) { + COPY32; + len -= 4; + } + + d8 = (uint8_t *)d32; + s8 = (const uint8_t *)s32; + while(len) { + COPY8 + len--; + } + + return dst; +} + +/** + * Same as `memset` but optimized for 4 byte operation. + * @param dst pointer to the destination buffer + * @param v value to set [0..255] + * @param len number of byte to set + */ +LV_ATTRIBUTE_FAST_MEM void lv_memset(void * dst, uint8_t v, size_t len) +{ + + uint8_t * d8 = (uint8_t *)dst; + + uintptr_t d_align = (lv_uintptr_t) d8 & ALIGN_MASK; + + /*Make the address aligned*/ + if(d_align) { + d_align = ALIGN_MASK + 1 - d_align; + while(d_align && len) { + SET8(v); + len--; + d_align--; + } + } + + uint32_t v32 = (uint32_t)v + ((uint32_t)v << 8) + ((uint32_t)v << 16) + ((uint32_t)v << 24); + + uint32_t * d32 = (uint32_t *)d8; + + while(len > 32) { + REPEAT8(SET32(v32)); + len -= 32; + } + + while(len > 4) { + SET32(v32); + len -= 4; + } + + d8 = (uint8_t *)d32; + while(len) { + SET8(v); + len--; + } +} + +/** + * Same as `memset(dst, 0x00, len)` but optimized for 4 byte operation. + * @param dst pointer to the destination buffer + * @param len number of byte to set + */ +LV_ATTRIBUTE_FAST_MEM void lv_memset_00(void * dst, size_t len) +{ + uint8_t * d8 = (uint8_t *)dst; + uintptr_t d_align = (lv_uintptr_t) d8 & ALIGN_MASK; + + /*Make the address aligned*/ + if(d_align) { + d_align = ALIGN_MASK + 1 - d_align; + while(d_align && len) { + SET8(0); + len--; + d_align--; + } + } + + uint32_t * d32 = (uint32_t *)d8; + while(len > 32) { + REPEAT8(SET32(0)); + len -= 32; + } + + while(len > 4) { + SET32(0); + len -= 4; + } + + d8 = (uint8_t *)d32; + while(len) { + SET8(0); + len--; + } +} + +/** + * Same as `memset(dst, 0xFF, len)` but optimized for 4 byte operation. + * @param dst pointer to the destination buffer + * @param len number of byte to set + */ +LV_ATTRIBUTE_FAST_MEM void lv_memset_ff(void * dst, size_t len) +{ + uint8_t * d8 = (uint8_t *)dst; + uintptr_t d_align = (lv_uintptr_t) d8 & ALIGN_MASK; + + /*Make the address aligned*/ + if(d_align) { + d_align = ALIGN_MASK + 1 - d_align; + while(d_align && len) { + SET8(0xFF); + len--; + d_align--; + } + } + + uint32_t * d32 = (uint32_t *)d8; + while(len > 32) { + REPEAT8(SET32(0xFFFFFFFF)); + len -= 32; + } + + while(len > 4) { + SET32(0xFFFFFFFF); + len -= 4; + } + + d8 = (uint8_t *)d32; + while(len) { + SET8(0xFF); + len--; + } +} + +#endif /*LV_MEMCPY_MEMSET_STD*/ + +/********************** + * STATIC FUNCTIONS + **********************/ + +#if LV_MEM_CUSTOM == 0 +static void lv_mem_walker(void * ptr, size_t size, int used, void * user) +{ + LV_UNUSED(ptr); + + lv_mem_monitor_t * mon_p = user; + if(used) { + mon_p->used_cnt++; + } + else { + mon_p->free_cnt++; + mon_p->free_size += size; + if(size > mon_p->free_biggest_size) + mon_p->free_biggest_size = size; + } +} +#endif diff --git a/lib/lvgl/src/misc/lv_mem.h b/lib/lvgl/src/misc/lv_mem.h new file mode 100644 index 00000000..7a83b3d5 --- /dev/null +++ b/lib/lvgl/src/misc/lv_mem.h @@ -0,0 +1,243 @@ +/** + * @file lv_mem.h + * + */ + +#ifndef LV_MEM_H +#define LV_MEM_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ +#include "../lv_conf_internal.h" + +#include +#include +#include + +#include "lv_types.h" + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/** + * Heap information structure. + */ +typedef struct { + uint32_t total_size; /**< Total heap size*/ + uint32_t free_cnt; + uint32_t free_size; /**< Size of available memory*/ + uint32_t free_biggest_size; + uint32_t used_cnt; + uint32_t max_used; /**< Max size of Heap memory used*/ + uint8_t used_pct; /**< Percentage used*/ + uint8_t frag_pct; /**< Amount of fragmentation*/ +} lv_mem_monitor_t; + +typedef struct { + void * p; + uint16_t size; + uint8_t used : 1; +} lv_mem_buf_t; + +typedef lv_mem_buf_t lv_mem_buf_arr_t[LV_MEM_BUF_MAX_NUM]; + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +/** + * Initialize the dyn_mem module (work memory and other variables) + */ +void lv_mem_init(void); + +/** + * Clean up the memory buffer which frees all the allocated memories. + * @note It work only if `LV_MEM_CUSTOM == 0` + */ +void lv_mem_deinit(void); + +/** + * Allocate a memory dynamically + * @param size size of the memory to allocate in bytes + * @return pointer to the allocated memory + */ +void * lv_mem_alloc(size_t size); + +/** + * Free an allocated data + * @param data pointer to an allocated memory + */ +void lv_mem_free(void * data); + +/** + * Reallocate a memory with a new size. The old content will be kept. + * @param data pointer to an allocated memory. + * Its content will be copied to the new memory block and freed + * @param new_size the desired new size in byte + * @return pointer to the new memory, NULL on failure + */ +void * lv_mem_realloc(void * data_p, size_t new_size); + +/** + * + * @return + */ +lv_res_t lv_mem_test(void); + +/** + * Give information about the work memory of dynamic allocation + * @param mon_p pointer to a lv_mem_monitor_t variable, + * the result of the analysis will be stored here + */ +void lv_mem_monitor(lv_mem_monitor_t * mon_p); + + +/** + * Get a temporal buffer with the given size. + * @param size the required size + */ +void * lv_mem_buf_get(uint32_t size); + +/** + * Release a memory buffer + * @param p buffer to release + */ +void lv_mem_buf_release(void * p); + +/** + * Free all memory buffers + */ +void lv_mem_buf_free_all(void); + +//! @cond Doxygen_Suppress + +#if LV_MEMCPY_MEMSET_STD + +/** + * Wrapper for the standard memcpy + * @param dst pointer to the destination buffer + * @param src pointer to the source buffer + * @param len number of byte to copy + */ +static inline void * lv_memcpy(void * dst, const void * src, size_t len) +{ + return memcpy(dst, src, len); +} + +/** + * Wrapper for the standard memcpy + * @param dst pointer to the destination buffer + * @param src pointer to the source buffer + * @param len number of byte to copy + */ +static inline void * lv_memcpy_small(void * dst, const void * src, size_t len) +{ + return memcpy(dst, src, len); +} + +/** + * Wrapper for the standard memset + * @param dst pointer to the destination buffer + * @param v value to set [0..255] + * @param len number of byte to set + */ +static inline void lv_memset(void * dst, uint8_t v, size_t len) +{ + memset(dst, v, len); +} + +/** + * Wrapper for the standard memset with fixed 0x00 value + * @param dst pointer to the destination buffer + * @param len number of byte to set + */ +static inline void lv_memset_00(void * dst, size_t len) +{ + memset(dst, 0x00, len); +} + +/** + * Wrapper for the standard memset with fixed 0xFF value + * @param dst pointer to the destination buffer + * @param len number of byte to set + */ +static inline void lv_memset_ff(void * dst, size_t len) +{ + memset(dst, 0xFF, len); +} + +#else +/** + * Same as `memcpy` but optimized for 4 byte operation. + * @param dst pointer to the destination buffer + * @param src pointer to the source buffer + * @param len number of byte to copy + */ +LV_ATTRIBUTE_FAST_MEM void * lv_memcpy(void * dst, const void * src, size_t len); + +/** + * Same as `memcpy` but optimized to copy only a few bytes. + * @param dst pointer to the destination buffer + * @param src pointer to the source buffer + * @param len number of byte to copy + */ +LV_ATTRIBUTE_FAST_MEM static inline void * lv_memcpy_small(void * dst, const void * src, size_t len) +{ + uint8_t * d8 = (uint8_t *)dst; + const uint8_t * s8 = (const uint8_t *)src; + + while(len) { + *d8 = *s8; + d8++; + s8++; + len--; + } + + return dst; +} + +/** + * Same as `memset` but optimized for 4 byte operation. + * @param dst pointer to the destination buffer + * @param v value to set [0..255] + * @param len number of byte to set + */ +LV_ATTRIBUTE_FAST_MEM void lv_memset(void * dst, uint8_t v, size_t len); + +/** + * Same as `memset(dst, 0x00, len)` but optimized for 4 byte operation. + * @param dst pointer to the destination buffer + * @param len number of byte to set + */ +LV_ATTRIBUTE_FAST_MEM void lv_memset_00(void * dst, size_t len); + +/** + * Same as `memset(dst, 0xFF, len)` but optimized for 4 byte operation. + * @param dst pointer to the destination buffer + * @param len number of byte to set + */ +LV_ATTRIBUTE_FAST_MEM void lv_memset_ff(void * dst, size_t len); + +//! @endcond + +#endif + +/********************** + * MACROS + **********************/ + +#ifdef __cplusplus +} /*extern "C"*/ +#endif + +#endif /*LV_MEM_H*/ diff --git a/lib/lvgl/src/misc/lv_misc.mk b/lib/lvgl/src/misc/lv_misc.mk new file mode 100644 index 00000000..1dfd4eec --- /dev/null +++ b/lib/lvgl/src/misc/lv_misc.mk @@ -0,0 +1,26 @@ +CSRCS += lv_anim.c +CSRCS += lv_anim_timeline.c +CSRCS += lv_area.c +CSRCS += lv_async.c +CSRCS += lv_bidi.c +CSRCS += lv_color.c +CSRCS += lv_fs.c +CSRCS += lv_gc.c +CSRCS += lv_ll.c +CSRCS += lv_log.c +CSRCS += lv_lru.c +CSRCS += lv_math.c +CSRCS += lv_mem.c +CSRCS += lv_printf.c +CSRCS += lv_style.c +CSRCS += lv_style_gen.c +CSRCS += lv_timer.c +CSRCS += lv_tlsf.c +CSRCS += lv_txt.c +CSRCS += lv_txt_ap.c +CSRCS += lv_utils.c + +DEPPATH += --dep-path $(LVGL_DIR)/$(LVGL_DIR_NAME)/src/misc +VPATH += :$(LVGL_DIR)/$(LVGL_DIR_NAME)/src/misc + +CFLAGS += "-I$(LVGL_DIR)/$(LVGL_DIR_NAME)/src/misc" diff --git a/lib/lvgl/src/misc/lv_printf.c b/lib/lvgl/src/misc/lv_printf.c new file mode 100644 index 00000000..9077a7a1 --- /dev/null +++ b/lib/lvgl/src/misc/lv_printf.c @@ -0,0 +1,879 @@ +/////////////////////////////////////////////////////////////////////////////// +// \author (c) Marco Paland (info@paland.com) +// 2014-2019, PALANDesign Hannover, Germany +// +// \license The MIT License (MIT) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +// \brief Tiny printf, sprintf and (v)snprintf implementation, optimized for speed on +// embedded systems with a very limited resources. These routines are thread +// safe and reentrant! +// Use this instead of the bloated standard/newlib printf cause these use +// malloc for printf (and may not be thread safe). +// +/////////////////////////////////////////////////////////////////////////////// + +/*Original repository: https://github.com/mpaland/printf*/ + +#include "lv_printf.h" + +#if LV_SPRINTF_CUSTOM == 0 + +#include + +#define PRINTF_DISABLE_SUPPORT_FLOAT (!LV_SPRINTF_USE_FLOAT) + +// 'ntoa' conversion buffer size, this must be big enough to hold one converted +// numeric number including padded zeros (dynamically created on stack) +// default: 32 byte +#ifndef PRINTF_NTOA_BUFFER_SIZE + #define PRINTF_NTOA_BUFFER_SIZE 32U +#endif + +// 'ftoa' conversion buffer size, this must be big enough to hold one converted +// float number including padded zeros (dynamically created on stack) +// default: 32 byte +#ifndef PRINTF_FTOA_BUFFER_SIZE + #define PRINTF_FTOA_BUFFER_SIZE 32U +#endif + +// support for the floating point type (%f) +// default: activated +#if !PRINTF_DISABLE_SUPPORT_FLOAT + #define PRINTF_SUPPORT_FLOAT +#endif + +// support for exponential floating point notation (%e/%g) +// default: activated +#ifndef PRINTF_DISABLE_SUPPORT_EXPONENTIAL + #define PRINTF_SUPPORT_EXPONENTIAL +#endif + +// define the default floating point precision +// default: 6 digits +#ifndef PRINTF_DEFAULT_FLOAT_PRECISION + #define PRINTF_DEFAULT_FLOAT_PRECISION 6U +#endif + +// define the largest float suitable to print with %f +// default: 1e9 +#ifndef PRINTF_MAX_FLOAT + #define PRINTF_MAX_FLOAT 1e9 +#endif + +// support for the long long types (%llu or %p) +// default: activated +#ifndef PRINTF_DISABLE_SUPPORT_LONG_LONG + #define PRINTF_SUPPORT_LONG_LONG +#endif + +// support for the ptrdiff_t type (%t) +// ptrdiff_t is normally defined in as long or long long type +// default: activated +#ifndef PRINTF_DISABLE_SUPPORT_PTRDIFF_T + #define PRINTF_SUPPORT_PTRDIFF_T +#endif + +/////////////////////////////////////////////////////////////////////////////// + +// internal flag definitions +#define FLAGS_ZEROPAD (1U << 0U) +#define FLAGS_LEFT (1U << 1U) +#define FLAGS_PLUS (1U << 2U) +#define FLAGS_SPACE (1U << 3U) +#define FLAGS_HASH (1U << 4U) +#define FLAGS_UPPERCASE (1U << 5U) +#define FLAGS_CHAR (1U << 6U) +#define FLAGS_SHORT (1U << 7U) +#define FLAGS_LONG (1U << 8U) +#define FLAGS_LONG_LONG (1U << 9U) +#define FLAGS_PRECISION (1U << 10U) +#define FLAGS_ADAPT_EXP (1U << 11U) + +// import float.h for DBL_MAX +#if defined(PRINTF_SUPPORT_FLOAT) + #include +#endif + +// output function type +typedef void (*out_fct_type)(char character, void * buffer, size_t idx, size_t maxlen); + +// wrapper (used as buffer) for output function type +typedef struct { + void (*fct)(char character, void * arg); + void * arg; +} out_fct_wrap_type; + +// internal buffer output +static inline void _out_buffer(char character, void * buffer, size_t idx, size_t maxlen) +{ + if(idx < maxlen) { + ((char *)buffer)[idx] = character; + } +} + +// internal null output +static inline void _out_null(char character, void * buffer, size_t idx, size_t maxlen) +{ + LV_UNUSED(character); + LV_UNUSED(buffer); + LV_UNUSED(idx); + LV_UNUSED(maxlen); +} + +// internal secure strlen +// \return The length of the string (excluding the terminating 0) limited by 'maxsize' +static inline unsigned int _strnlen_s(const char * str, size_t maxsize) +{ + const char * s; + for(s = str; *s && maxsize--; ++s); + return (unsigned int)(s - str); +} + +// internal test if char is a digit (0-9) +// \return true if char is a digit +static inline bool _is_digit(char ch) +{ + return (ch >= '0') && (ch <= '9'); +} + +// internal ASCII string to unsigned int conversion +static unsigned int _atoi(const char ** str) +{ + unsigned int i = 0U; + while(_is_digit(**str)) { + i = i * 10U + (unsigned int)(*((*str)++) - '0'); + } + return i; +} + +// output the specified string in reverse, taking care of any zero-padding +static size_t _out_rev(out_fct_type out, char * buffer, size_t idx, size_t maxlen, const char * buf, size_t len, + unsigned int width, unsigned int flags) +{ + const size_t start_idx = idx; + + // pad spaces up to given width + if(!(flags & FLAGS_LEFT) && !(flags & FLAGS_ZEROPAD)) { + size_t i; + for(i = len; i < width; i++) { + out(' ', buffer, idx++, maxlen); + } + } + + // reverse string + while(len) { + out(buf[--len], buffer, idx++, maxlen); + } + + // append pad spaces up to given width + if(flags & FLAGS_LEFT) { + while(idx - start_idx < width) { + out(' ', buffer, idx++, maxlen); + } + } + + return idx; +} + +// internal itoa format +static size_t _ntoa_format(out_fct_type out, char * buffer, size_t idx, size_t maxlen, char * buf, size_t len, + bool negative, unsigned int base, unsigned int prec, unsigned int width, unsigned int flags) +{ + // pad leading zeros + if(!(flags & FLAGS_LEFT)) { + if(width && (flags & FLAGS_ZEROPAD) && (negative || (flags & (FLAGS_PLUS | FLAGS_SPACE)))) { + width--; + } + while((len < prec) && (len < PRINTF_NTOA_BUFFER_SIZE)) { + buf[len++] = '0'; + } + while((flags & FLAGS_ZEROPAD) && (len < width) && (len < PRINTF_NTOA_BUFFER_SIZE)) { + buf[len++] = '0'; + } + } + + // handle hash + if(flags & FLAGS_HASH) { + if(!(flags & FLAGS_PRECISION) && len && ((len == prec) || (len == width))) { + len--; + if(len && (base == 16U)) { + len--; + } + } + if((base == 16U) && !(flags & FLAGS_UPPERCASE) && (len < PRINTF_NTOA_BUFFER_SIZE)) { + buf[len++] = 'x'; + } + else if((base == 16U) && (flags & FLAGS_UPPERCASE) && (len < PRINTF_NTOA_BUFFER_SIZE)) { + buf[len++] = 'X'; + } + else if((base == 2U) && (len < PRINTF_NTOA_BUFFER_SIZE)) { + buf[len++] = 'b'; + } + if(len < PRINTF_NTOA_BUFFER_SIZE) { + buf[len++] = '0'; + } + } + + if(len < PRINTF_NTOA_BUFFER_SIZE) { + if(negative) { + buf[len++] = '-'; + } + else if(flags & FLAGS_PLUS) { + buf[len++] = '+'; // ignore the space if the '+' exists + } + else if(flags & FLAGS_SPACE) { + buf[len++] = ' '; + } + } + + return _out_rev(out, buffer, idx, maxlen, buf, len, width, flags); +} + +// internal itoa for 'long' type +static size_t _ntoa_long(out_fct_type out, char * buffer, size_t idx, size_t maxlen, unsigned long value, bool negative, + unsigned long base, unsigned int prec, unsigned int width, unsigned int flags) +{ + char buf[PRINTF_NTOA_BUFFER_SIZE]; + size_t len = 0U; + + // no hash for 0 values + if(!value) { + flags &= ~FLAGS_HASH; + } + + // write if precision != 0 and value is != 0 + if(!(flags & FLAGS_PRECISION) || value) { + do { + const char digit = (char)(value % base); + buf[len++] = digit < 10 ? '0' + digit : (flags & FLAGS_UPPERCASE ? 'A' : 'a') + digit - 10; + value /= base; + } while(value && (len < PRINTF_NTOA_BUFFER_SIZE)); + } + + return _ntoa_format(out, buffer, idx, maxlen, buf, len, negative, (unsigned int)base, prec, width, flags); +} + +// internal itoa for 'long long' type +#if defined(PRINTF_SUPPORT_LONG_LONG) +static size_t _ntoa_long_long(out_fct_type out, char * buffer, size_t idx, size_t maxlen, unsigned long long value, + bool negative, unsigned long long base, unsigned int prec, unsigned int width, unsigned int flags) +{ + char buf[PRINTF_NTOA_BUFFER_SIZE]; + size_t len = 0U; + + // no hash for 0 values + if(!value) { + flags &= ~FLAGS_HASH; + } + + // write if precision != 0 and value is != 0 + if(!(flags & FLAGS_PRECISION) || value) { + do { + const char digit = (char)(value % base); + buf[len++] = digit < 10 ? '0' + digit : (flags & FLAGS_UPPERCASE ? 'A' : 'a') + digit - 10; + value /= base; + } while(value && (len < PRINTF_NTOA_BUFFER_SIZE)); + } + + return _ntoa_format(out, buffer, idx, maxlen, buf, len, negative, (unsigned int)base, prec, width, flags); +} +#endif // PRINTF_SUPPORT_LONG_LONG + +#if defined(PRINTF_SUPPORT_FLOAT) + +#if defined(PRINTF_SUPPORT_EXPONENTIAL) +// forward declaration so that _ftoa can switch to exp notation for values > PRINTF_MAX_FLOAT +static size_t _etoa(out_fct_type out, char * buffer, size_t idx, size_t maxlen, double value, unsigned int prec, + unsigned int width, unsigned int flags); +#endif + +// internal ftoa for fixed decimal floating point +static size_t _ftoa(out_fct_type out, char * buffer, size_t idx, size_t maxlen, double value, unsigned int prec, + unsigned int width, unsigned int flags) +{ + char buf[PRINTF_FTOA_BUFFER_SIZE]; + size_t len = 0U; + double diff = 0.0; + + // powers of 10 + static const double pow10[] = { 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000 }; + + // test for special values + if(value != value) + return _out_rev(out, buffer, idx, maxlen, "nan", 3, width, flags); + if(value < -DBL_MAX) + return _out_rev(out, buffer, idx, maxlen, "fni-", 4, width, flags); + if(value > DBL_MAX) + return _out_rev(out, buffer, idx, maxlen, (flags & FLAGS_PLUS) ? "fni+" : "fni", (flags & FLAGS_PLUS) ? 4U : 3U, width, + flags); + + // test for very large values + // standard printf behavior is to print EVERY whole number digit -- which could be 100s of characters overflowing your buffers == bad + if((value > PRINTF_MAX_FLOAT) || (value < -PRINTF_MAX_FLOAT)) { +#if defined(PRINTF_SUPPORT_EXPONENTIAL) + return _etoa(out, buffer, idx, maxlen, value, prec, width, flags); +#else + return 0U; +#endif + } + + // test for negative + bool negative = false; + if(value < 0) { + negative = true; + value = 0 - value; + } + + // set default precision, if not set explicitly + if(!(flags & FLAGS_PRECISION)) { + prec = PRINTF_DEFAULT_FLOAT_PRECISION; + } + // limit precision to 9, cause a prec >= 10 can lead to overflow errors + while((len < PRINTF_FTOA_BUFFER_SIZE) && (prec > 9U)) { + buf[len++] = '0'; + prec--; + } + + int whole = (int)value; + double tmp = (value - whole) * pow10[prec]; + unsigned long frac = (unsigned long)tmp; + diff = tmp - frac; + + if(diff > 0.5) { + ++frac; + // handle rollover, e.g. case 0.99 with prec 1 is 1.0 + if(frac >= pow10[prec]) { + frac = 0; + ++whole; + } + } + else if(diff < 0.5) { + } + else if((frac == 0U) || (frac & 1U)) { + // if halfway, round up if odd OR if last digit is 0 + ++frac; + } + + if(prec == 0U) { + diff = value - (double)whole; + if((!(diff < 0.5) || (diff > 0.5)) && (whole & 1)) { + // exactly 0.5 and ODD, then round up + // 1.5 -> 2, but 2.5 -> 2 + ++whole; + } + } + else { + unsigned int count = prec; + // now do fractional part, as an unsigned number + while(len < PRINTF_FTOA_BUFFER_SIZE) { + --count; + buf[len++] = (char)(48U + (frac % 10U)); + if(!(frac /= 10U)) { + break; + } + } + // add extra 0s + while((len < PRINTF_FTOA_BUFFER_SIZE) && (count-- > 0U)) { + buf[len++] = '0'; + } + if(len < PRINTF_FTOA_BUFFER_SIZE) { + // add decimal + buf[len++] = '.'; + } + } + + // do whole part, number is reversed + while(len < PRINTF_FTOA_BUFFER_SIZE) { + buf[len++] = (char)(48 + (whole % 10)); + if(!(whole /= 10)) { + break; + } + } + + // pad leading zeros + if(!(flags & FLAGS_LEFT) && (flags & FLAGS_ZEROPAD)) { + if(width && (negative || (flags & (FLAGS_PLUS | FLAGS_SPACE)))) { + width--; + } + while((len < width) && (len < PRINTF_FTOA_BUFFER_SIZE)) { + buf[len++] = '0'; + } + } + + if(len < PRINTF_FTOA_BUFFER_SIZE) { + if(negative) { + buf[len++] = '-'; + } + else if(flags & FLAGS_PLUS) { + buf[len++] = '+'; // ignore the space if the '+' exists + } + else if(flags & FLAGS_SPACE) { + buf[len++] = ' '; + } + } + + return _out_rev(out, buffer, idx, maxlen, buf, len, width, flags); +} + +#if defined(PRINTF_SUPPORT_EXPONENTIAL) +// internal ftoa variant for exponential floating-point type, contributed by Martijn Jasperse +static size_t _etoa(out_fct_type out, char * buffer, size_t idx, size_t maxlen, double value, unsigned int prec, + unsigned int width, unsigned int flags) +{ + // check for NaN and special values + if((value != value) || (value > DBL_MAX) || (value < -DBL_MAX)) { + return _ftoa(out, buffer, idx, maxlen, value, prec, width, flags); + } + + // determine the sign + const bool negative = value < 0; + if(negative) { + value = -value; + } + + // default precision + if(!(flags & FLAGS_PRECISION)) { + prec = PRINTF_DEFAULT_FLOAT_PRECISION; + } + + // determine the decimal exponent + // based on the algorithm by David Gay (https://www.ampl.com/netlib/fp/dtoa.c) + union { + uint64_t U; + double F; + } conv; + + conv.F = value; + int exp2 = (int)((conv.U >> 52U) & 0x07FFU) - 1023; // effectively log2 + conv.U = (conv.U & ((1ULL << 52U) - 1U)) | (1023ULL << 52U); // drop the exponent so conv.F is now in [1,2) + // now approximate log10 from the log2 integer part and an expansion of ln around 1.5 + int expval = (int)(0.1760912590558 + exp2 * 0.301029995663981 + (conv.F - 1.5) * 0.289529654602168); + // now we want to compute 10^expval but we want to be sure it won't overflow + exp2 = (int)(expval * 3.321928094887362 + 0.5); + const double z = expval * 2.302585092994046 - exp2 * 0.6931471805599453; + const double z2 = z * z; + conv.U = (uint64_t)(exp2 + 1023) << 52U; + // compute exp(z) using continued fractions, see https://en.wikipedia.org/wiki/Exponential_function#Continued_fractions_for_ex + conv.F *= 1 + 2 * z / (2 - z + (z2 / (6 + (z2 / (10 + z2 / 14))))); + // correct for rounding errors + if(value < conv.F) { + expval--; + conv.F /= 10; + } + + // the exponent format is "%+03d" and largest value is "307", so set aside 4-5 characters + unsigned int minwidth = ((expval < 100) && (expval > -100)) ? 4U : 5U; + + // in "%g" mode, "prec" is the number of *significant figures* not decimals + if(flags & FLAGS_ADAPT_EXP) { + // do we want to fall-back to "%f" mode? + if((value >= 1e-4) && (value < 1e6)) { + if((int)prec > expval) { + prec = (unsigned)((int)prec - expval - 1); + } + else { + prec = 0; + } + flags |= FLAGS_PRECISION; // make sure _ftoa respects precision + // no characters in exponent + minwidth = 0U; + expval = 0; + } + else { + // we use one sigfig for the whole part + if((prec > 0) && (flags & FLAGS_PRECISION)) { + --prec; + } + } + } + + // will everything fit? + unsigned int fwidth = width; + if(width > minwidth) { + // we didn't fall-back so subtract the characters required for the exponent + fwidth -= minwidth; + } + else { + // not enough characters, so go back to default sizing + fwidth = 0U; + } + if((flags & FLAGS_LEFT) && minwidth) { + // if we're padding on the right, DON'T pad the floating part + fwidth = 0U; + } + + // rescale the float value + if(expval) { + value /= conv.F; + } + + // output the floating part + const size_t start_idx = idx; + idx = _ftoa(out, buffer, idx, maxlen, negative ? -value : value, prec, fwidth, flags & ~FLAGS_ADAPT_EXP); + + // output the exponent part + if(minwidth) { + // output the exponential symbol + out((flags & FLAGS_UPPERCASE) ? 'E' : 'e', buffer, idx++, maxlen); + // output the exponent value + idx = _ntoa_long(out, buffer, idx, maxlen, (expval < 0) ? -expval : expval, expval < 0, 10, 0, minwidth - 1, + FLAGS_ZEROPAD | FLAGS_PLUS); + // might need to right-pad spaces + if(flags & FLAGS_LEFT) { + while(idx - start_idx < width) out(' ', buffer, idx++, maxlen); + } + } + return idx; +} +#endif // PRINTF_SUPPORT_EXPONENTIAL +#endif // PRINTF_SUPPORT_FLOAT + +// internal vsnprintf +static int _vsnprintf(out_fct_type out, char * buffer, const size_t maxlen, const char * format, va_list va) +{ + unsigned int flags, width, precision, n; + size_t idx = 0U; + + if(!buffer) { + // use null output function + out = _out_null; + } + + while(*format) { + // format specifier? %[flags][width][.precision][length] + if(*format != '%') { + // no + out(*format, buffer, idx++, maxlen); + format++; + continue; + } + else { + // yes, evaluate it + format++; + } + + // evaluate flags + flags = 0U; + do { + switch(*format) { + case '0': + flags |= FLAGS_ZEROPAD; + format++; + n = 1U; + break; + case '-': + flags |= FLAGS_LEFT; + format++; + n = 1U; + break; + case '+': + flags |= FLAGS_PLUS; + format++; + n = 1U; + break; + case ' ': + flags |= FLAGS_SPACE; + format++; + n = 1U; + break; + case '#': + flags |= FLAGS_HASH; + format++; + n = 1U; + break; + default : + n = 0U; + break; + } + } while(n); + + // evaluate width field + width = 0U; + if(_is_digit(*format)) { + width = _atoi(&format); + } + else if(*format == '*') { + const int w = va_arg(va, int); + if(w < 0) { + flags |= FLAGS_LEFT; // reverse padding + width = (unsigned int) - w; + } + else { + width = (unsigned int)w; + } + format++; + } + + // evaluate precision field + precision = 0U; + if(*format == '.') { + flags |= FLAGS_PRECISION; + format++; + if(_is_digit(*format)) { + precision = _atoi(&format); + } + else if(*format == '*') { + const int prec = (int)va_arg(va, int); + precision = prec > 0 ? (unsigned int)prec : 0U; + format++; + } + } + + // evaluate length field + switch(*format) { + case 'l' : + flags |= FLAGS_LONG; + format++; + if(*format == 'l') { + flags |= FLAGS_LONG_LONG; + format++; + } + break; + case 'h' : + flags |= FLAGS_SHORT; + format++; + if(*format == 'h') { + flags |= FLAGS_CHAR; + format++; + } + break; +#if defined(PRINTF_SUPPORT_PTRDIFF_T) + case 't' : + flags |= (sizeof(ptrdiff_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG); + format++; + break; +#endif + case 'j' : + flags |= (sizeof(intmax_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG); + format++; + break; + case 'z' : + flags |= (sizeof(size_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG); + format++; + break; + default : + break; + } + + // evaluate specifier + switch(*format) { + case 'd' : + case 'i' : + case 'u' : + case 'x' : + case 'X' : + case 'p' : + case 'P' : + case 'o' : + case 'b' : { + // set the base + unsigned int base; + if(*format == 'x' || *format == 'X') { + base = 16U; + } + else if(*format == 'p' || *format == 'P') { + base = 16U; + flags |= FLAGS_HASH; // always hash for pointer format +#if defined(PRINTF_SUPPORT_LONG_LONG) + if(sizeof(uintptr_t) == sizeof(long long)) + flags |= FLAGS_LONG_LONG; + else +#endif + flags |= FLAGS_LONG; + + if(*(format + 1) == 'V') + format++; + } + else if(*format == 'o') { + base = 8U; + } + else if(*format == 'b') { + base = 2U; + } + else { + base = 10U; + flags &= ~FLAGS_HASH; // no hash for dec format + } + // uppercase + if(*format == 'X' || *format == 'P') { + flags |= FLAGS_UPPERCASE; + } + + // no plus or space flag for u, x, X, o, b + if((*format != 'i') && (*format != 'd')) { + flags &= ~(FLAGS_PLUS | FLAGS_SPACE); + } + + // ignore '0' flag when precision is given + if(flags & FLAGS_PRECISION) { + flags &= ~FLAGS_ZEROPAD; + } + + // convert the integer + if((*format == 'i') || (*format == 'd')) { + // signed + if(flags & FLAGS_LONG_LONG) { +#if defined(PRINTF_SUPPORT_LONG_LONG) + const long long value = va_arg(va, long long); + idx = _ntoa_long_long(out, buffer, idx, maxlen, (unsigned long long)(value > 0 ? value : 0 - value), value < 0, base, + precision, width, flags); +#endif + } + else if(flags & FLAGS_LONG) { + const long value = va_arg(va, long); + idx = _ntoa_long(out, buffer, idx, maxlen, (unsigned long)(value > 0 ? value : 0 - value), value < 0, base, precision, + width, flags); + } + else { + const int value = (flags & FLAGS_CHAR) ? (char)va_arg(va, int) : (flags & FLAGS_SHORT) ? (short int)va_arg(va, + int) : va_arg(va, int); + idx = _ntoa_long(out, buffer, idx, maxlen, (unsigned int)(value > 0 ? value : 0 - value), value < 0, base, precision, + width, flags); + } + } + else if(*format == 'V') { + lv_vaformat_t * vaf = va_arg(va, lv_vaformat_t *); + va_list copy; + + va_copy(copy, *vaf->va); + idx += _vsnprintf(out, buffer + idx, maxlen - idx, vaf->fmt, copy); + va_end(copy); + } + else { + // unsigned + if(flags & FLAGS_LONG_LONG) { +#if defined(PRINTF_SUPPORT_LONG_LONG) + idx = _ntoa_long_long(out, buffer, idx, maxlen, va_arg(va, unsigned long long), false, base, precision, width, flags); +#endif + } + else if(flags & FLAGS_LONG) { + idx = _ntoa_long(out, buffer, idx, maxlen, va_arg(va, unsigned long), false, base, precision, width, flags); + } + else { + const unsigned int value = (flags & FLAGS_CHAR) ? (unsigned char)va_arg(va, + unsigned int) : (flags & FLAGS_SHORT) ? (unsigned short int)va_arg(va, unsigned int) : va_arg(va, unsigned int); + idx = _ntoa_long(out, buffer, idx, maxlen, value, false, base, precision, width, flags); + } + } + format++; + break; + } +#if defined(PRINTF_SUPPORT_FLOAT) + case 'f' : + case 'F' : + if(*format == 'F') flags |= FLAGS_UPPERCASE; + idx = _ftoa(out, buffer, idx, maxlen, va_arg(va, double), precision, width, flags); + format++; + break; +#if defined(PRINTF_SUPPORT_EXPONENTIAL) + case 'e': + case 'E': + case 'g': + case 'G': + if((*format == 'g') || (*format == 'G')) flags |= FLAGS_ADAPT_EXP; + if((*format == 'E') || (*format == 'G')) flags |= FLAGS_UPPERCASE; + idx = _etoa(out, buffer, idx, maxlen, va_arg(va, double), precision, width, flags); + format++; + break; +#endif // PRINTF_SUPPORT_EXPONENTIAL +#endif // PRINTF_SUPPORT_FLOAT + case 'c' : { + unsigned int l = 1U; + // pre padding + if(!(flags & FLAGS_LEFT)) { + while(l++ < width) { + out(' ', buffer, idx++, maxlen); + } + } + // char output + out((char)va_arg(va, int), buffer, idx++, maxlen); + // post padding + if(flags & FLAGS_LEFT) { + while(l++ < width) { + out(' ', buffer, idx++, maxlen); + } + } + format++; + break; + } + + case 's' : { + const char * p = va_arg(va, char *); + unsigned int l = _strnlen_s(p, precision ? precision : (size_t) -1); + // pre padding + if(flags & FLAGS_PRECISION) { + l = (l < precision ? l : precision); + } + if(!(flags & FLAGS_LEFT)) { + while(l++ < width) { + out(' ', buffer, idx++, maxlen); + } + } + // string output + while((*p != 0) && (!(flags & FLAGS_PRECISION) || precision--)) { + out(*(p++), buffer, idx++, maxlen); + } + // post padding + if(flags & FLAGS_LEFT) { + while(l++ < width) { + out(' ', buffer, idx++, maxlen); + } + } + format++; + break; + } + + case '%' : + out('%', buffer, idx++, maxlen); + format++; + break; + + default : + out(*format, buffer, idx++, maxlen); + format++; + break; + } + } + + // termination + out((char)0, buffer, idx < maxlen ? idx : maxlen - 1U, maxlen); + + // return written chars without terminating \0 + return (int)idx; +} + +/////////////////////////////////////////////////////////////////////////////// + +int lv_snprintf(char * buffer, size_t count, const char * format, ...) +{ + va_list va; + va_start(va, format); + const int ret = _vsnprintf(_out_buffer, buffer, count, format, va); + va_end(va); + return ret; +} + +int lv_vsnprintf(char * buffer, size_t count, const char * format, va_list va) +{ + return _vsnprintf(_out_buffer, buffer, count, format, va); +} + +#endif /*LV_SPRINTF_CUSTOM*/ diff --git a/lib/lvgl/src/misc/lv_printf.h b/lib/lvgl/src/misc/lv_printf.h new file mode 100644 index 00000000..4cbbd84c --- /dev/null +++ b/lib/lvgl/src/misc/lv_printf.h @@ -0,0 +1,92 @@ +/////////////////////////////////////////////////////////////////////////////// +// \author (c) Marco Paland (info@paland.com) +// 2014-2019, PALANDesign Hannover, Germany +// +// \license The MIT License (MIT) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +// \brief Tiny printf, sprintf and snprintf implementation, optimized for speed on +// embedded systems with a very limited resources. +// Use this instead of bloated standard/newlib printf. +// These routines are thread safe and reentrant. +// +/////////////////////////////////////////////////////////////////////////////// + +/*Original repository: https://github.com/mpaland/printf*/ + +#ifndef _LV_PRINTF_H_ +#define _LV_PRINTF_H_ + +#if defined(__has_include) + #if __has_include() + #include + /* platform-specific printf format for int32_t, usually "d" or "ld" */ + #define LV_PRId32 PRId32 + #define LV_PRIu32 PRIu32 + #else + #define LV_PRId32 "d" + #define LV_PRIu32 "u" + #endif +#else + /* hope this is correct for ports without __has_include or without inttypes.h */ + #define LV_PRId32 "d" + #define LV_PRIu32 "u" +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#include "../lv_conf_internal.h" + +#if LV_SPRINTF_CUSTOM == 0 + +#include +#include + +#include "lv_types.h" + +typedef struct { + const char * fmt; + va_list * va; +} lv_vaformat_t; + +/** + * Tiny snprintf/vsnprintf implementation + * \param buffer A pointer to the buffer where to store the formatted string + * \param count The maximum number of characters to store in the buffer, including a terminating null character + * \param format A string that specifies the format of the output + * \param va A value identifying a variable arguments list + * \return The number of characters that COULD have been written into the buffer, not counting the terminating + * null character. A value equal or larger than count indicates truncation. Only when the returned value + * is non-negative and less than count, the string has been completely written. + */ +int lv_snprintf(char * buffer, size_t count, const char * format, ...) LV_FORMAT_ATTRIBUTE(3, 4); +int lv_vsnprintf(char * buffer, size_t count, const char * format, va_list va) LV_FORMAT_ATTRIBUTE(3, 0); + +#else +#include LV_SPRINTF_INCLUDE +#endif + +#ifdef __cplusplus +} /*extern "C"*/ +#endif + +#endif // _LV_PRINTF_H_ diff --git a/lib/lvgl/src/misc/lv_style.c b/lib/lvgl/src/misc/lv_style.c new file mode 100644 index 00000000..419c29e4 --- /dev/null +++ b/lib/lvgl/src/misc/lv_style.c @@ -0,0 +1,485 @@ +/** + * @file lv_style.c + * + */ + +/********************* + * INCLUDES + *********************/ +#include "lv_style.h" +#include "../misc/lv_gc.h" +#include "../misc/lv_mem.h" +#include "lv_assert.h" +#include "lv_types.h" + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ + +static void lv_style_set_prop_internal(lv_style_t * style, lv_style_prop_t prop_and_meta, lv_style_value_t value, + void (*value_adjustment_helper)(lv_style_prop_t, lv_style_value_t, uint16_t *, lv_style_value_t *)); +static void lv_style_set_prop_helper(lv_style_prop_t prop, lv_style_value_t value, uint16_t * prop_storage, + lv_style_value_t * value_storage); +static void lv_style_set_prop_meta_helper(lv_style_prop_t prop, lv_style_value_t value, uint16_t * prop_storage, + lv_style_value_t * value_storage); + +/********************** + * GLOBAL VARIABLES + **********************/ + +const uint8_t _lv_style_builtin_prop_flag_lookup_table[_LV_STYLE_NUM_BUILT_IN_PROPS] = { + [LV_STYLE_WIDTH] = LV_STYLE_PROP_LAYOUT_REFR, + [LV_STYLE_MIN_WIDTH] = LV_STYLE_PROP_LAYOUT_REFR, + [LV_STYLE_MAX_WIDTH] = LV_STYLE_PROP_LAYOUT_REFR, + [LV_STYLE_HEIGHT] = LV_STYLE_PROP_LAYOUT_REFR, + [LV_STYLE_MIN_HEIGHT] = LV_STYLE_PROP_LAYOUT_REFR, + [LV_STYLE_MAX_HEIGHT] = LV_STYLE_PROP_LAYOUT_REFR, + [LV_STYLE_X] = LV_STYLE_PROP_LAYOUT_REFR, + [LV_STYLE_Y] = LV_STYLE_PROP_LAYOUT_REFR, + [LV_STYLE_ALIGN] = LV_STYLE_PROP_LAYOUT_REFR, + [LV_STYLE_TRANSFORM_WIDTH] = LV_STYLE_PROP_EXT_DRAW, + [LV_STYLE_TRANSFORM_HEIGHT] = LV_STYLE_PROP_EXT_DRAW, + [LV_STYLE_TRANSLATE_X] = LV_STYLE_PROP_LAYOUT_REFR | LV_STYLE_PROP_PARENT_LAYOUT_REFR, + [LV_STYLE_TRANSLATE_Y] = LV_STYLE_PROP_LAYOUT_REFR | LV_STYLE_PROP_PARENT_LAYOUT_REFR, + [LV_STYLE_TRANSFORM_ZOOM] = LV_STYLE_PROP_EXT_DRAW | LV_STYLE_PROP_LAYER_REFR, + [LV_STYLE_TRANSFORM_ANGLE] = LV_STYLE_PROP_EXT_DRAW | LV_STYLE_PROP_LAYER_REFR, + + [LV_STYLE_PAD_TOP] = LV_STYLE_PROP_EXT_DRAW | LV_STYLE_PROP_LAYOUT_REFR, + [LV_STYLE_PAD_BOTTOM] = LV_STYLE_PROP_EXT_DRAW | LV_STYLE_PROP_LAYOUT_REFR, + [LV_STYLE_PAD_LEFT] = LV_STYLE_PROP_EXT_DRAW | LV_STYLE_PROP_LAYOUT_REFR, + [LV_STYLE_PAD_RIGHT] = LV_STYLE_PROP_EXT_DRAW | LV_STYLE_PROP_LAYOUT_REFR, + [LV_STYLE_PAD_ROW] = LV_STYLE_PROP_EXT_DRAW | LV_STYLE_PROP_LAYOUT_REFR, + [LV_STYLE_PAD_COLUMN] = LV_STYLE_PROP_EXT_DRAW | LV_STYLE_PROP_LAYOUT_REFR, + + [LV_STYLE_BG_COLOR] = 0, + [LV_STYLE_BG_OPA] = 0, + [LV_STYLE_BG_GRAD_COLOR] = 0, + [LV_STYLE_BG_GRAD_DIR] = 0, + [LV_STYLE_BG_MAIN_STOP] = 0, + [LV_STYLE_BG_GRAD_STOP] = 0, + [LV_STYLE_BG_GRAD] = 0, + [LV_STYLE_BG_DITHER_MODE] = 0, + + [LV_STYLE_BG_IMG_SRC] = LV_STYLE_PROP_EXT_DRAW, + [LV_STYLE_BG_IMG_OPA] = 0, + [LV_STYLE_BG_IMG_RECOLOR] = 0, + [LV_STYLE_BG_IMG_RECOLOR_OPA] = 0, + [LV_STYLE_BG_IMG_TILED] = 0, + + [LV_STYLE_BORDER_COLOR] = 0, + [LV_STYLE_BORDER_OPA] = 0, + [LV_STYLE_BORDER_WIDTH] = LV_STYLE_PROP_LAYOUT_REFR, + [LV_STYLE_BORDER_SIDE] = 0, + [LV_STYLE_BORDER_POST] = 0, + + [LV_STYLE_OUTLINE_WIDTH] = LV_STYLE_PROP_EXT_DRAW, + [LV_STYLE_OUTLINE_COLOR] = 0, + [LV_STYLE_OUTLINE_OPA] = LV_STYLE_PROP_EXT_DRAW, + [LV_STYLE_OUTLINE_PAD] = LV_STYLE_PROP_EXT_DRAW, + + [LV_STYLE_SHADOW_WIDTH] = LV_STYLE_PROP_EXT_DRAW, + [LV_STYLE_SHADOW_OFS_X] = LV_STYLE_PROP_EXT_DRAW, + [LV_STYLE_SHADOW_OFS_Y] = LV_STYLE_PROP_EXT_DRAW, + [LV_STYLE_SHADOW_SPREAD] = LV_STYLE_PROP_EXT_DRAW, + [LV_STYLE_SHADOW_COLOR] = 0, + [LV_STYLE_SHADOW_OPA] = LV_STYLE_PROP_EXT_DRAW, + + [LV_STYLE_IMG_OPA] = 0, + [LV_STYLE_IMG_RECOLOR] = 0, + [LV_STYLE_IMG_RECOLOR_OPA] = 0, + + [LV_STYLE_LINE_WIDTH] = LV_STYLE_PROP_EXT_DRAW, + [LV_STYLE_LINE_DASH_WIDTH] = 0, + [LV_STYLE_LINE_DASH_GAP] = 0, + [LV_STYLE_LINE_ROUNDED] = 0, + [LV_STYLE_LINE_COLOR] = 0, + [LV_STYLE_LINE_OPA] = 0, + + [LV_STYLE_ARC_WIDTH] = LV_STYLE_PROP_EXT_DRAW, + [LV_STYLE_ARC_ROUNDED] = 0, + [LV_STYLE_ARC_COLOR] = 0, + [LV_STYLE_ARC_OPA] = 0, + [LV_STYLE_ARC_IMG_SRC] = 0, + + [LV_STYLE_TEXT_COLOR] = LV_STYLE_PROP_INHERIT, + [LV_STYLE_TEXT_OPA] = LV_STYLE_PROP_INHERIT, + [LV_STYLE_TEXT_FONT] = LV_STYLE_PROP_INHERIT | LV_STYLE_PROP_LAYOUT_REFR, + [LV_STYLE_TEXT_LETTER_SPACE] = LV_STYLE_PROP_INHERIT | LV_STYLE_PROP_LAYOUT_REFR, + [LV_STYLE_TEXT_LINE_SPACE] = LV_STYLE_PROP_INHERIT | LV_STYLE_PROP_LAYOUT_REFR, + [LV_STYLE_TEXT_DECOR] = LV_STYLE_PROP_INHERIT, + [LV_STYLE_TEXT_ALIGN] = LV_STYLE_PROP_INHERIT | LV_STYLE_PROP_LAYOUT_REFR, + + [LV_STYLE_RADIUS] = 0, + [LV_STYLE_CLIP_CORNER] = 0, + [LV_STYLE_OPA] = LV_STYLE_PROP_LAYER_REFR, + [LV_STYLE_COLOR_FILTER_DSC] = LV_STYLE_PROP_INHERIT, + [LV_STYLE_COLOR_FILTER_OPA] = LV_STYLE_PROP_INHERIT, + [LV_STYLE_ANIM_TIME] = 0, + [LV_STYLE_ANIM_SPEED] = 0, + [LV_STYLE_TRANSITION] = 0, + [LV_STYLE_BLEND_MODE] = LV_STYLE_PROP_LAYER_REFR, + [LV_STYLE_LAYOUT] = LV_STYLE_PROP_LAYOUT_REFR, + [LV_STYLE_BASE_DIR] = LV_STYLE_PROP_INHERIT | LV_STYLE_PROP_LAYOUT_REFR, +}; + +uint32_t _lv_style_custom_prop_flag_lookup_table_size = 0; + +/********************** + * STATIC VARIABLES + **********************/ + +static uint16_t last_custom_prop_id = (uint16_t)_LV_STYLE_LAST_BUILT_IN_PROP; +static const lv_style_value_t null_style_value = { .num = 0 }; + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +void lv_style_init(lv_style_t * style) +{ +#if LV_USE_ASSERT_STYLE + if(style->sentinel == LV_STYLE_SENTINEL_VALUE && style->prop_cnt > 1) { + LV_LOG_WARN("Style might be already inited. (Potential memory leak)"); + } +#endif + + lv_memset_00(style, sizeof(lv_style_t)); +#if LV_USE_ASSERT_STYLE + style->sentinel = LV_STYLE_SENTINEL_VALUE; +#endif +} + +void lv_style_reset(lv_style_t * style) +{ + LV_ASSERT_STYLE(style); + + if(style->prop1 == LV_STYLE_PROP_ANY) { + LV_LOG_ERROR("Cannot reset const style"); + return; + } + + if(style->prop_cnt > 1) lv_mem_free(style->v_p.values_and_props); + lv_memset_00(style, sizeof(lv_style_t)); +#if LV_USE_ASSERT_STYLE + style->sentinel = LV_STYLE_SENTINEL_VALUE; +#endif +} + +lv_style_prop_t lv_style_register_prop(uint8_t flag) +{ + if(LV_GC_ROOT(_lv_style_custom_prop_flag_lookup_table) == NULL) { + _lv_style_custom_prop_flag_lookup_table_size = 0; + last_custom_prop_id = (uint16_t)_LV_STYLE_LAST_BUILT_IN_PROP; + } + + if(((last_custom_prop_id + 1) & LV_STYLE_PROP_META_MASK) != 0) { + LV_LOG_ERROR("No more custom property IDs available"); + return LV_STYLE_PROP_INV; + } + + /* + * Allocate the lookup table if it's not yet available. + */ + size_t required_size = (last_custom_prop_id + 1 - _LV_STYLE_LAST_BUILT_IN_PROP); + if(_lv_style_custom_prop_flag_lookup_table_size < required_size) { + /* Round required_size up to the nearest 32-byte value */ + required_size = (required_size + 31) & ~31; + LV_ASSERT_MSG(required_size > 0, "required size has become 0?"); + uint8_t * old_p = LV_GC_ROOT(_lv_style_custom_prop_flag_lookup_table); + uint8_t * new_p = lv_mem_realloc(old_p, required_size * sizeof(uint8_t)); + if(new_p == NULL) { + LV_LOG_ERROR("Unable to allocate space for custom property lookup table"); + return LV_STYLE_PROP_INV; + } + LV_GC_ROOT(_lv_style_custom_prop_flag_lookup_table) = new_p; + _lv_style_custom_prop_flag_lookup_table_size = required_size; + } + last_custom_prop_id++; + /* This should never happen - we should bail out above */ + LV_ASSERT_NULL(LV_GC_ROOT(_lv_style_custom_prop_flag_lookup_table)); + LV_GC_ROOT(_lv_style_custom_prop_flag_lookup_table)[last_custom_prop_id - _LV_STYLE_NUM_BUILT_IN_PROPS] = flag; + return last_custom_prop_id; +} + +lv_style_prop_t lv_style_get_num_custom_props(void) +{ + return last_custom_prop_id - _LV_STYLE_LAST_BUILT_IN_PROP; +} + +bool lv_style_remove_prop(lv_style_t * style, lv_style_prop_t prop) +{ + LV_ASSERT_STYLE(style); + + if(style->prop1 == LV_STYLE_PROP_ANY) { + LV_LOG_ERROR("Cannot remove prop from const style"); + return false; + } + + if(style->prop_cnt == 0) return false; + + if(style->prop_cnt == 1) { + if(LV_STYLE_PROP_ID_MASK(style->prop1) == prop) { + style->prop1 = LV_STYLE_PROP_INV; + style->prop_cnt = 0; + return true; + } + return false; + } + + uint8_t * tmp = style->v_p.values_and_props + style->prop_cnt * sizeof(lv_style_value_t); + uint16_t * old_props = (uint16_t *)tmp; + uint32_t i; + for(i = 0; i < style->prop_cnt; i++) { + if(LV_STYLE_PROP_ID_MASK(old_props[i]) == prop) { + lv_style_value_t * old_values = (lv_style_value_t *)style->v_p.values_and_props; + + if(style->prop_cnt == 2) { + style->prop_cnt = 1; + style->prop1 = i == 0 ? old_props[1] : old_props[0]; + style->v_p.value1 = i == 0 ? old_values[1] : old_values[0]; + } + else { + size_t size = (style->prop_cnt - 1) * (sizeof(lv_style_value_t) + sizeof(uint16_t)); + uint8_t * new_values_and_props = lv_mem_alloc(size); + if(new_values_and_props == NULL) return false; + style->v_p.values_and_props = new_values_and_props; + style->prop_cnt--; + + tmp = new_values_and_props + style->prop_cnt * sizeof(lv_style_value_t); + uint16_t * new_props = (uint16_t *)tmp; + lv_style_value_t * new_values = (lv_style_value_t *)new_values_and_props; + + uint32_t j; + for(i = j = 0; j <= style->prop_cnt; + j++) { /*<=: because prop_cnt already reduced but all the old props. needs to be checked.*/ + if(old_props[j] != prop) { + new_values[i] = old_values[j]; + new_props[i++] = old_props[j]; + } + } + } + + lv_mem_free(old_values); + return true; + } + } + + return false; +} + +void lv_style_set_prop(lv_style_t * style, lv_style_prop_t prop, lv_style_value_t value) +{ + lv_style_set_prop_internal(style, prop, value, lv_style_set_prop_helper); +} + +void lv_style_set_prop_meta(lv_style_t * style, lv_style_prop_t prop, uint16_t meta) +{ + lv_style_set_prop_internal(style, prop | meta, null_style_value, lv_style_set_prop_meta_helper); +} + +lv_style_res_t lv_style_get_prop(const lv_style_t * style, lv_style_prop_t prop, lv_style_value_t * value) +{ + return lv_style_get_prop_inlined(style, prop, value); +} + +void lv_style_transition_dsc_init(lv_style_transition_dsc_t * tr, const lv_style_prop_t props[], + lv_anim_path_cb_t path_cb, uint32_t time, uint32_t delay, void * user_data) +{ + lv_memset_00(tr, sizeof(lv_style_transition_dsc_t)); + tr->props = props; + tr->path_xcb = path_cb == NULL ? lv_anim_path_linear : path_cb; + tr->time = time; + tr->delay = delay; +#if LV_USE_USER_DATA + tr->user_data = user_data; +#else + LV_UNUSED(user_data); +#endif +} + +lv_style_value_t lv_style_prop_get_default(lv_style_prop_t prop) +{ + lv_style_value_t value; + switch(prop) { + case LV_STYLE_TRANSFORM_ZOOM: + value.num = LV_IMG_ZOOM_NONE; + break; + case LV_STYLE_BG_COLOR: + value.color = lv_color_white(); + break; + case LV_STYLE_BG_GRAD_COLOR: + case LV_STYLE_BORDER_COLOR: + case LV_STYLE_SHADOW_COLOR: + case LV_STYLE_OUTLINE_COLOR: + case LV_STYLE_ARC_COLOR: + case LV_STYLE_LINE_COLOR: + case LV_STYLE_TEXT_COLOR: + case LV_STYLE_IMG_RECOLOR: + value.color = lv_color_black(); + break; + case LV_STYLE_OPA: + case LV_STYLE_BORDER_OPA: + case LV_STYLE_TEXT_OPA: + case LV_STYLE_IMG_OPA: + case LV_STYLE_BG_IMG_OPA: + case LV_STYLE_OUTLINE_OPA: + case LV_STYLE_SHADOW_OPA: + case LV_STYLE_LINE_OPA: + case LV_STYLE_ARC_OPA: + value.num = LV_OPA_COVER; + break; + case LV_STYLE_BG_GRAD_STOP: + value.num = 255; + break; + case LV_STYLE_BORDER_SIDE: + value.num = LV_BORDER_SIDE_FULL; + break; + case LV_STYLE_TEXT_FONT: + value.ptr = LV_FONT_DEFAULT; + break; + case LV_STYLE_MAX_WIDTH: + case LV_STYLE_MAX_HEIGHT: + value.num = LV_COORD_MAX; + break; + default: + value.ptr = NULL; + value.num = 0; + break; + } + + return value; +} + +bool lv_style_is_empty(const lv_style_t * style) +{ + LV_ASSERT_STYLE(style); + + return style->prop_cnt == 0 ? true : false; +} + +uint8_t _lv_style_get_prop_group(lv_style_prop_t prop) +{ + uint16_t group = (prop & 0x1FF) >> 4; + if(group > 7) group = 7; /*The MSB marks all the custom properties*/ + return (uint8_t)group; +} + +uint8_t _lv_style_prop_lookup_flags(lv_style_prop_t prop) +{ + extern const uint8_t _lv_style_builtin_prop_flag_lookup_table[]; + extern uint32_t _lv_style_custom_prop_flag_lookup_table_size; + if(prop == LV_STYLE_PROP_ANY) return LV_STYLE_PROP_ALL; /*Any prop can have any flags*/ + if(prop == LV_STYLE_PROP_INV) return 0; + + if(prop < _LV_STYLE_NUM_BUILT_IN_PROPS) + return _lv_style_builtin_prop_flag_lookup_table[prop]; + prop -= _LV_STYLE_NUM_BUILT_IN_PROPS; + if(LV_GC_ROOT(_lv_style_custom_prop_flag_lookup_table) != NULL && prop < _lv_style_custom_prop_flag_lookup_table_size) + return LV_GC_ROOT(_lv_style_custom_prop_flag_lookup_table)[prop]; + return 0; +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +static void lv_style_set_prop_helper(lv_style_prop_t prop, lv_style_value_t value, uint16_t * prop_storage, + lv_style_value_t * value_storage) +{ + *prop_storage = prop; + *value_storage = value; +} + +static void lv_style_set_prop_meta_helper(lv_style_prop_t prop, lv_style_value_t value, uint16_t * prop_storage, + lv_style_value_t * value_storage) +{ + LV_UNUSED(value); + LV_UNUSED(value_storage); + *prop_storage = prop; /* meta is OR-ed into the prop ID already */ +} + +static void lv_style_set_prop_internal(lv_style_t * style, lv_style_prop_t prop_and_meta, lv_style_value_t value, + void (*value_adjustment_helper)(lv_style_prop_t, lv_style_value_t, uint16_t *, lv_style_value_t *)) +{ + LV_ASSERT_STYLE(style); + + if(style->prop1 == LV_STYLE_PROP_ANY) { + LV_LOG_ERROR("Cannot set property of constant style"); + return; + } + + lv_style_prop_t prop_id = LV_STYLE_PROP_ID_MASK(prop_and_meta); + + if(style->prop_cnt > 1) { + uint8_t * tmp = style->v_p.values_and_props + style->prop_cnt * sizeof(lv_style_value_t); + uint16_t * props = (uint16_t *)tmp; + int32_t i; + for(i = style->prop_cnt - 1; i >= 0; i--) { + if(LV_STYLE_PROP_ID_MASK(props[i]) == prop_id) { + lv_style_value_t * values = (lv_style_value_t *)style->v_p.values_and_props; + value_adjustment_helper(prop_and_meta, value, &props[i], &values[i]); + return; + } + } + + size_t size = (style->prop_cnt + 1) * (sizeof(lv_style_value_t) + sizeof(uint16_t)); + uint8_t * values_and_props = lv_mem_realloc(style->v_p.values_and_props, size); + if(values_and_props == NULL) return; + style->v_p.values_and_props = values_and_props; + + tmp = values_and_props + style->prop_cnt * sizeof(lv_style_value_t); + props = (uint16_t *)tmp; + /*Shift all props to make place for the value before them*/ + for(i = style->prop_cnt - 1; i >= 0; i--) { + props[i + sizeof(lv_style_value_t) / sizeof(uint16_t)] = props[i]; + } + style->prop_cnt++; + + /*Go to the new position wit the props*/ + tmp = values_and_props + style->prop_cnt * sizeof(lv_style_value_t); + props = (uint16_t *)tmp; + lv_style_value_t * values = (lv_style_value_t *)values_and_props; + + /*Set the new property and value*/ + value_adjustment_helper(prop_and_meta, value, &props[style->prop_cnt - 1], &values[style->prop_cnt - 1]); + } + else if(style->prop_cnt == 1) { + if(LV_STYLE_PROP_ID_MASK(style->prop1) == prop_id) { + value_adjustment_helper(prop_and_meta, value, &style->prop1, &style->v_p.value1); + return; + } + size_t size = (style->prop_cnt + 1) * (sizeof(lv_style_value_t) + sizeof(uint16_t)); + uint8_t * values_and_props = lv_mem_alloc(size); + if(values_and_props == NULL) return; + lv_style_value_t value_tmp = style->v_p.value1; + style->v_p.values_and_props = values_and_props; + style->prop_cnt++; + + uint8_t * tmp = values_and_props + style->prop_cnt * sizeof(lv_style_value_t); + uint16_t * props = (uint16_t *)tmp; + lv_style_value_t * values = (lv_style_value_t *)values_and_props; + props[0] = style->prop1; + values[0] = value_tmp; + value_adjustment_helper(prop_and_meta, value, &props[1], &values[1]); + } + else { + style->prop_cnt = 1; + value_adjustment_helper(prop_and_meta, value, &style->prop1, &style->v_p.value1); + } + + uint8_t group = _lv_style_get_prop_group(prop_id); + style->has_group |= 1 << group; +} + diff --git a/lib/lvgl/src/misc/lv_style.h b/lib/lvgl/src/misc/lv_style.h new file mode 100644 index 00000000..1792dae8 --- /dev/null +++ b/lib/lvgl/src/misc/lv_style.h @@ -0,0 +1,597 @@ +/** + * @file lv_style.h + * + */ + +#ifndef LV_STYLE_H +#define LV_STYLE_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ +#include +#include +#include "../font/lv_font.h" +#include "lv_color.h" +#include "lv_area.h" +#include "lv_anim.h" +#include "lv_txt.h" +#include "lv_types.h" +#include "lv_assert.h" +#include "lv_bidi.h" + +/********************* + * DEFINES + *********************/ + +#define LV_STYLE_SENTINEL_VALUE 0xAABBCCDD + +/** + * Flags for style behavior + * + * The rest of the flags will have _FLAG added to their name in v9. + */ +#define LV_STYLE_PROP_FLAG_NONE (0) +#define LV_STYLE_PROP_INHERIT (1 << 0) /*Inherited*/ +#define LV_STYLE_PROP_EXT_DRAW (1 << 1) /*Requires ext. draw size update when changed*/ +#define LV_STYLE_PROP_LAYOUT_REFR (1 << 2) /*Requires layout update when changed*/ +#define LV_STYLE_PROP_PARENT_LAYOUT_REFR (1 << 3) /*Requires layout update on parent when changed*/ +#define LV_STYLE_PROP_LAYER_REFR (1 << 4) /*Affects layer handling*/ +#define LV_STYLE_PROP_ALL (0x1F) /*Indicating all flags*/ + +/** + * Other constants + */ +#define LV_IMG_ZOOM_NONE 256 /*Value for not zooming the image*/ +LV_EXPORT_CONST_INT(LV_IMG_ZOOM_NONE); + +// *INDENT-OFF* +#if LV_USE_ASSERT_STYLE +#define LV_STYLE_CONST_INIT(var_name, prop_array) \ + const lv_style_t var_name = { \ + .sentinel = LV_STYLE_SENTINEL_VALUE, \ + .v_p = { .const_props = prop_array }, \ + .has_group = 0xFF, \ + .prop1 = LV_STYLE_PROP_ANY, \ + .prop_cnt = (sizeof(prop_array) / sizeof((prop_array)[0])), \ + } +#else +#define LV_STYLE_CONST_INIT(var_name, prop_array) \ + const lv_style_t var_name = { \ + .v_p = { .const_props = prop_array }, \ + .has_group = 0xFF, \ + .prop1 = LV_STYLE_PROP_ANY, \ + .prop_cnt = (sizeof(prop_array) / sizeof((prop_array)[0])), \ + } +#endif +// *INDENT-ON* + +/** On simple system, don't waste resources on gradients */ +#if !defined(LV_DRAW_COMPLEX) || !defined(LV_GRADIENT_MAX_STOPS) +#define LV_GRADIENT_MAX_STOPS 2 +#endif + +#define LV_STYLE_PROP_META_INHERIT 0x8000 +#define LV_STYLE_PROP_META_INITIAL 0x4000 +#define LV_STYLE_PROP_META_MASK (LV_STYLE_PROP_META_INHERIT | LV_STYLE_PROP_META_INITIAL) + +#define LV_STYLE_PROP_ID_MASK(prop) ((lv_style_prop_t)((prop) & ~LV_STYLE_PROP_META_MASK)) + +/********************** + * TYPEDEFS + **********************/ + +/** + * Possible options how to blend opaque drawings + */ +enum { + LV_BLEND_MODE_NORMAL, /**< Simply mix according to the opacity value*/ + LV_BLEND_MODE_ADDITIVE, /**< Add the respective color channels*/ + LV_BLEND_MODE_SUBTRACTIVE,/**< Subtract the foreground from the background*/ + LV_BLEND_MODE_MULTIPLY, /**< Multiply the foreground and background*/ + LV_BLEND_MODE_REPLACE, /**< Replace background with foreground in the area*/ +}; + +typedef uint8_t lv_blend_mode_t; + +/** + * Some options to apply decorations on texts. + * 'OR'ed values can be used. + */ +enum { + LV_TEXT_DECOR_NONE = 0x00, + LV_TEXT_DECOR_UNDERLINE = 0x01, + LV_TEXT_DECOR_STRIKETHROUGH = 0x02, +}; + +typedef uint8_t lv_text_decor_t; + +/** + * Selects on which sides border should be drawn + * 'OR'ed values can be used. + */ +enum { + LV_BORDER_SIDE_NONE = 0x00, + LV_BORDER_SIDE_BOTTOM = 0x01, + LV_BORDER_SIDE_TOP = 0x02, + LV_BORDER_SIDE_LEFT = 0x04, + LV_BORDER_SIDE_RIGHT = 0x08, + LV_BORDER_SIDE_FULL = 0x0F, + LV_BORDER_SIDE_INTERNAL = 0x10, /**< FOR matrix-like objects (e.g. Button matrix)*/ +}; +typedef uint8_t lv_border_side_t; + +/** + * The direction of the gradient. + */ +enum { + LV_GRAD_DIR_NONE, /**< No gradient (the `grad_color` property is ignored)*/ + LV_GRAD_DIR_VER, /**< Vertical (top to bottom) gradient*/ + LV_GRAD_DIR_HOR, /**< Horizontal (left to right) gradient*/ +}; + +typedef uint8_t lv_grad_dir_t; + +/** + * The dithering algorithm for the gradient + * Depends on LV_DITHER_GRADIENT + */ +enum { + LV_DITHER_NONE, /**< No dithering, colors are just quantized to the output resolution*/ + LV_DITHER_ORDERED, /**< Ordered dithering. Faster to compute and use less memory but lower quality*/ + LV_DITHER_ERR_DIFF, /**< Error diffusion mode. Slower to compute and use more memory but give highest dither quality*/ +}; + +typedef uint8_t lv_dither_mode_t; + +/** A gradient stop definition. + * This matches a color and a position in a virtual 0-255 scale. + */ +typedef struct { + lv_color_t color; /**< The stop color */ + uint8_t frac; /**< The stop position in 1/255 unit */ +} lv_gradient_stop_t; + +/** A descriptor of a gradient. */ +typedef struct { + lv_gradient_stop_t stops[LV_GRADIENT_MAX_STOPS]; /**< A gradient stop array */ + uint8_t stops_count; /**< The number of used stops in the array */ + lv_grad_dir_t dir : 3; /**< The gradient direction. + * Any of LV_GRAD_DIR_HOR, LV_GRAD_DIR_VER, LV_GRAD_DIR_NONE */ + lv_dither_mode_t dither : 3; /**< Whether to dither the gradient or not. + * Any of LV_DITHER_NONE, LV_DITHER_ORDERED, LV_DITHER_ERR_DIFF */ +} lv_grad_dsc_t; + +/** + * A common type to handle all the property types in the same way. + */ +typedef union { + int32_t num; /**< Number integer number (opacity, enums, booleans or "normal" numbers)*/ + const void * ptr; /**< Constant pointers (font, cone text, etc)*/ + lv_color_t color; /**< Colors*/ +} lv_style_value_t; + +/** + * Enumeration of all built in style properties + * + * Props are split into groups of 16. When adding a new prop to a group, ensure it does not overflow into the next one. + */ +typedef enum { + LV_STYLE_PROP_INV = 0, + + /*Group 0*/ + LV_STYLE_WIDTH = 1, + LV_STYLE_MIN_WIDTH = 2, + LV_STYLE_MAX_WIDTH = 3, + LV_STYLE_HEIGHT = 4, + LV_STYLE_MIN_HEIGHT = 5, + LV_STYLE_MAX_HEIGHT = 6, + LV_STYLE_X = 7, + LV_STYLE_Y = 8, + LV_STYLE_ALIGN = 9, + LV_STYLE_LAYOUT = 10, + LV_STYLE_RADIUS = 11, + + /*Group 1*/ + LV_STYLE_PAD_TOP = 16, + LV_STYLE_PAD_BOTTOM = 17, + LV_STYLE_PAD_LEFT = 18, + LV_STYLE_PAD_RIGHT = 19, + LV_STYLE_PAD_ROW = 20, + LV_STYLE_PAD_COLUMN = 21, + LV_STYLE_BASE_DIR = 22, + LV_STYLE_CLIP_CORNER = 23, + + /*Group 2*/ + LV_STYLE_BG_COLOR = 32, + LV_STYLE_BG_OPA = 33, + LV_STYLE_BG_GRAD_COLOR = 34, + LV_STYLE_BG_GRAD_DIR = 35, + LV_STYLE_BG_MAIN_STOP = 36, + LV_STYLE_BG_GRAD_STOP = 37, + LV_STYLE_BG_GRAD = 38, + LV_STYLE_BG_DITHER_MODE = 39, + LV_STYLE_BG_IMG_SRC = 40, + LV_STYLE_BG_IMG_OPA = 41, + LV_STYLE_BG_IMG_RECOLOR = 42, + LV_STYLE_BG_IMG_RECOLOR_OPA = 43, + LV_STYLE_BG_IMG_TILED = 44, + + /*Group 3*/ + LV_STYLE_BORDER_COLOR = 48, + LV_STYLE_BORDER_OPA = 49, + LV_STYLE_BORDER_WIDTH = 50, + LV_STYLE_BORDER_SIDE = 51, + LV_STYLE_BORDER_POST = 52, + LV_STYLE_OUTLINE_WIDTH = 53, + LV_STYLE_OUTLINE_COLOR = 54, + LV_STYLE_OUTLINE_OPA = 55, + LV_STYLE_OUTLINE_PAD = 56, + + /*Group 4*/ + LV_STYLE_SHADOW_WIDTH = 64, + LV_STYLE_SHADOW_OFS_X = 65, + LV_STYLE_SHADOW_OFS_Y = 66, + LV_STYLE_SHADOW_SPREAD = 67, + LV_STYLE_SHADOW_COLOR = 68, + LV_STYLE_SHADOW_OPA = 69, + LV_STYLE_IMG_OPA = 70, + LV_STYLE_IMG_RECOLOR = 71, + LV_STYLE_IMG_RECOLOR_OPA = 72, + LV_STYLE_LINE_WIDTH = 73, + LV_STYLE_LINE_DASH_WIDTH = 74, + LV_STYLE_LINE_DASH_GAP = 75, + LV_STYLE_LINE_ROUNDED = 76, + LV_STYLE_LINE_COLOR = 77, + LV_STYLE_LINE_OPA = 78, + + /*Group 5*/ + LV_STYLE_ARC_WIDTH = 80, + LV_STYLE_ARC_ROUNDED = 81, + LV_STYLE_ARC_COLOR = 82, + LV_STYLE_ARC_OPA = 83, + LV_STYLE_ARC_IMG_SRC = 84, + LV_STYLE_TEXT_COLOR = 85, + LV_STYLE_TEXT_OPA = 86, + LV_STYLE_TEXT_FONT = 87, + LV_STYLE_TEXT_LETTER_SPACE = 88, + LV_STYLE_TEXT_LINE_SPACE = 89, + LV_STYLE_TEXT_DECOR = 90, + LV_STYLE_TEXT_ALIGN = 91, + + /*Group 6*/ + LV_STYLE_OPA = 96, + LV_STYLE_COLOR_FILTER_DSC = 97, + LV_STYLE_COLOR_FILTER_OPA = 98, + LV_STYLE_ANIM = 99, + LV_STYLE_ANIM_TIME = 100, + LV_STYLE_ANIM_SPEED = 101, + LV_STYLE_TRANSITION = 102, + LV_STYLE_BLEND_MODE = 103, + LV_STYLE_TRANSFORM_WIDTH = 104, + LV_STYLE_TRANSFORM_HEIGHT = 105, + LV_STYLE_TRANSLATE_X = 106, + LV_STYLE_TRANSLATE_Y = 107, + LV_STYLE_TRANSFORM_ZOOM = 108, + LV_STYLE_TRANSFORM_ANGLE = 109, + LV_STYLE_TRANSFORM_PIVOT_X = 110, + LV_STYLE_TRANSFORM_PIVOT_Y = 111, + + _LV_STYLE_LAST_BUILT_IN_PROP = 111, + _LV_STYLE_NUM_BUILT_IN_PROPS = _LV_STYLE_LAST_BUILT_IN_PROP + 1, + + LV_STYLE_PROP_ANY = 0xFFFF, + _LV_STYLE_PROP_CONST = 0xFFFF /* magic value for const styles */ +} lv_style_prop_t; + +enum { + LV_STYLE_RES_NOT_FOUND, + LV_STYLE_RES_FOUND, + LV_STYLE_RES_INHERIT +}; + +typedef uint8_t lv_style_res_t; + +/** + * Descriptor for style transitions + */ +typedef struct { + const lv_style_prop_t * props; /**< An array with the properties to animate.*/ +#if LV_USE_USER_DATA + void * user_data; /**< A custom user data that will be passed to the animation's user_data */ +#endif + lv_anim_path_cb_t path_xcb; /**< A path for the animation.*/ + uint32_t time; /**< Duration of the transition in [ms]*/ + uint32_t delay; /**< Delay before the transition in [ms]*/ +} lv_style_transition_dsc_t; + +/** + * Descriptor of a constant style property. + */ +typedef struct { + lv_style_prop_t prop; + lv_style_value_t value; +} lv_style_const_prop_t; + +/** + * Descriptor of a style (a collection of properties and values). + */ +typedef struct { + +#if LV_USE_ASSERT_STYLE + uint32_t sentinel; +#endif + + /*If there is only one property store it directly. + *For more properties allocate an array*/ + union { + lv_style_value_t value1; + uint8_t * values_and_props; + const lv_style_const_prop_t * const_props; + } v_p; + + uint16_t prop1; + uint8_t has_group; + uint8_t prop_cnt; +} lv_style_t; + +/********************** + * GLOBAL PROTOTYPES + **********************/ + + +/** + * Initialize a style + * @param style pointer to a style to initialize + * @note Do not call `lv_style_init` on styles that already have some properties + * because this function won't free the used memory, just sets a default state for the style. + * In other words be sure to initialize styles only once! + */ +void lv_style_init(lv_style_t * style); + +/** + * Clear all properties from a style and free all allocated memories. + * @param style pointer to a style + */ +void lv_style_reset(lv_style_t * style); + +/** + * Register a new style property for custom usage + * @return a new property ID, or LV_STYLE_PROP_INV if there are no more available. + * @example + * lv_style_prop_t MY_PROP; + * static inline void lv_style_set_my_prop(lv_style_t * style, lv_color_t value) { + * lv_style_value_t v = {.color = value}; lv_style_set_prop(style, MY_PROP, v); } + * + * ... + * MY_PROP = lv_style_register_prop(); + * ... + * lv_style_set_my_prop(&style1, lv_palette_main(LV_PALETTE_RED)); + */ +lv_style_prop_t lv_style_register_prop(uint8_t flag); + +/** + * Get the number of custom properties that have been registered thus far. + */ +lv_style_prop_t lv_style_get_num_custom_props(void); + +/** + * Remove a property from a style + * @param style pointer to a style + * @param prop a style property ORed with a state. + * @return true: the property was found and removed; false: the property wasn't found + */ +bool lv_style_remove_prop(lv_style_t * style, lv_style_prop_t prop); + +/** + * Set the value of property in a style. + * This function shouldn't be used directly by the user. + * Instead use `lv_style_set_()`. E.g. `lv_style_set_bg_color()` + * @param style pointer to style + * @param prop the ID of a property (e.g. `LV_STYLE_BG_COLOR`) + * @param value `lv_style_value_t` variable in which a field is set according to the type of `prop` + */ +void lv_style_set_prop(lv_style_t * style, lv_style_prop_t prop, lv_style_value_t value); + +/** + * Set a special meta state for a property in a style. + * This function shouldn't be used directly by the user. + * @param style pointer to style + * @param prop the ID of a property (e.g. `LV_STYLE_BG_COLOR`) + * @param meta the meta value to attach to the property in the style + */ +void lv_style_set_prop_meta(lv_style_t * style, lv_style_prop_t prop, uint16_t meta); + +/** + * Get the value of a property + * @param style pointer to a style + * @param prop the ID of a property + * @param value pointer to a `lv_style_value_t` variable to store the value + * @return LV_RES_INV: the property wasn't found in the style (`value` is unchanged) + * LV_RES_OK: the property was fond, and `value` is set accordingly + * @note For performance reasons there are no sanity check on `style` + */ +lv_style_res_t lv_style_get_prop(const lv_style_t * style, lv_style_prop_t prop, lv_style_value_t * value); + +/** + * Initialize a transition descriptor. + * @param tr pointer to a transition descriptor to initialize + * @param props an array with the properties to transition. The last element must be zero. + * @param path_cb an animation path (ease) callback. If `NULL` liner path will be used. + * @param time duration of the transition in [ms] + * @param delay delay before the transition in [ms] + * @param user_data any custom data that will be saved in the transition animation and will be available when `path_cb` is called + * @example + * const static lv_style_prop_t trans_props[] = { LV_STYLE_BG_OPA, LV_STYLE_BG_COLOR, 0 }; + * static lv_style_transition_dsc_t trans1; + * lv_style_transition_dsc_init(&trans1, trans_props, NULL, 300, 0, NULL); + */ +void lv_style_transition_dsc_init(lv_style_transition_dsc_t * tr, const lv_style_prop_t props[], + lv_anim_path_cb_t path_cb, uint32_t time, uint32_t delay, void * user_data); + +/** + * Get the default value of a property + * @param prop the ID of a property + * @return the default value + */ +lv_style_value_t lv_style_prop_get_default(lv_style_prop_t prop); + +/** + * Get the value of a property + * @param style pointer to a style + * @param prop the ID of a property + * @param value pointer to a `lv_style_value_t` variable to store the value + * @return LV_RES_INV: the property wasn't found in the style (`value` is unchanged) + * LV_RES_OK: the property was fond, and `value` is set accordingly + * @note For performance reasons there are no sanity check on `style` + * @note This function is the same as ::lv_style_get_prop but inlined. Use it only on performance critical places + */ +static inline lv_style_res_t lv_style_get_prop_inlined(const lv_style_t * style, lv_style_prop_t prop, + lv_style_value_t * value) +{ + if(style->prop1 == LV_STYLE_PROP_ANY) { + const lv_style_const_prop_t * const_prop; + uint32_t i; + for(i = 0; i < style->prop_cnt; i++) { + const_prop = style->v_p.const_props + i; + lv_style_prop_t prop_id = LV_STYLE_PROP_ID_MASK(const_prop->prop); + if(prop_id == prop) { + if(const_prop->prop & LV_STYLE_PROP_META_INHERIT) + return LV_STYLE_RES_INHERIT; + *value = (const_prop->prop & LV_STYLE_PROP_META_INITIAL) ? lv_style_prop_get_default(prop_id) : const_prop->value; + return LV_STYLE_RES_FOUND; + } + } + return LV_STYLE_RES_NOT_FOUND; + } + + if(style->prop_cnt == 0) return LV_STYLE_RES_NOT_FOUND; + + if(style->prop_cnt > 1) { + uint8_t * tmp = style->v_p.values_and_props + style->prop_cnt * sizeof(lv_style_value_t); + uint16_t * props = (uint16_t *)tmp; + uint32_t i; + for(i = 0; i < style->prop_cnt; i++) { + lv_style_prop_t prop_id = LV_STYLE_PROP_ID_MASK(props[i]); + if(prop_id == prop) { + if(props[i] & LV_STYLE_PROP_META_INHERIT) + return LV_STYLE_RES_INHERIT; + if(props[i] & LV_STYLE_PROP_META_INITIAL) + *value = lv_style_prop_get_default(prop_id); + else { + lv_style_value_t * values = (lv_style_value_t *)style->v_p.values_and_props; + *value = values[i]; + } + return LV_STYLE_RES_FOUND; + } + } + } + else if(LV_STYLE_PROP_ID_MASK(style->prop1) == prop) { + if(style->prop1 & LV_STYLE_PROP_META_INHERIT) + return LV_STYLE_RES_INHERIT; + *value = (style->prop1 & LV_STYLE_PROP_META_INITIAL) ? lv_style_prop_get_default(LV_STYLE_PROP_ID_MASK( + style->prop1)) : style->v_p.value1; + return LV_STYLE_RES_FOUND; + } + return LV_STYLE_RES_NOT_FOUND; +} + +/** + * Checks if a style is empty (has no properties) + * @param style pointer to a style + * @return true if the style is empty + */ +bool lv_style_is_empty(const lv_style_t * style); + +/** + * Tell the group of a property. If the a property from a group is set in a style the (1 << group) bit of style->has_group is set. + * It allows early skipping the style if the property is not exists in the style at all. + * @param prop a style property + * @return the group [0..7] 7 means all the custom properties with index > 112 + */ +uint8_t _lv_style_get_prop_group(lv_style_prop_t prop); + +/** + * Get the flags of a built-in or custom property. + * + * @param prop a style property + * @return the flags of the property + */ +uint8_t _lv_style_prop_lookup_flags(lv_style_prop_t prop); + +#include "lv_style_gen.h" + +static inline void lv_style_set_size(lv_style_t * style, lv_coord_t value) +{ + lv_style_set_width(style, value); + lv_style_set_height(style, value); +} + +static inline void lv_style_set_pad_all(lv_style_t * style, lv_coord_t value) +{ + lv_style_set_pad_left(style, value); + lv_style_set_pad_right(style, value); + lv_style_set_pad_top(style, value); + lv_style_set_pad_bottom(style, value); +} + +static inline void lv_style_set_pad_hor(lv_style_t * style, lv_coord_t value) +{ + lv_style_set_pad_left(style, value); + lv_style_set_pad_right(style, value); +} + +static inline void lv_style_set_pad_ver(lv_style_t * style, lv_coord_t value) +{ + lv_style_set_pad_top(style, value); + lv_style_set_pad_bottom(style, value); +} + +static inline void lv_style_set_pad_gap(lv_style_t * style, lv_coord_t value) +{ + lv_style_set_pad_row(style, value); + lv_style_set_pad_column(style, value); +} + +/** + * @brief Check if the style property has a specified behavioral flag. + * + * Do not pass multiple flags to this function as backwards-compatibility is not guaranteed + * for that. + * + * @param prop Property ID + * @param flag Flag + * @return true if the flag is set for this property + */ +static inline bool lv_style_prop_has_flag(lv_style_prop_t prop, uint8_t flag) +{ + return _lv_style_prop_lookup_flags(prop) & flag; +} + +/************************* + * GLOBAL VARIABLES + *************************/ + +/********************** + * MACROS + **********************/ + +#if LV_USE_ASSERT_STYLE +# define LV_ASSERT_STYLE(style_p) \ + do { \ + LV_ASSERT_MSG(style_p != NULL, "The style is NULL"); \ + LV_ASSERT_MSG(style_p->sentinel == LV_STYLE_SENTINEL_VALUE, "Style is not initialized or corrupted"); \ + } while(0) +#else +# define LV_ASSERT_STYLE(p) do{}while(0) +#endif + +#ifdef __cplusplus +} /*extern "C"*/ +#endif + +#endif /*LV_STYLE_H*/ diff --git a/lib/lvgl/src/misc/lv_style_gen.c b/lib/lvgl/src/misc/lv_style_gen.c new file mode 100644 index 00000000..13d85607 --- /dev/null +++ b/lib/lvgl/src/misc/lv_style_gen.c @@ -0,0 +1,673 @@ +#include "lv_style.h" + +void lv_style_set_width(lv_style_t * style, lv_coord_t value) +{ + lv_style_value_t v = { + .num = (int32_t)value + }; + lv_style_set_prop(style, LV_STYLE_WIDTH, v); +} + +void lv_style_set_min_width(lv_style_t * style, lv_coord_t value) +{ + lv_style_value_t v = { + .num = (int32_t)value + }; + lv_style_set_prop(style, LV_STYLE_MIN_WIDTH, v); +} + +void lv_style_set_max_width(lv_style_t * style, lv_coord_t value) +{ + lv_style_value_t v = { + .num = (int32_t)value + }; + lv_style_set_prop(style, LV_STYLE_MAX_WIDTH, v); +} + +void lv_style_set_height(lv_style_t * style, lv_coord_t value) +{ + lv_style_value_t v = { + .num = (int32_t)value + }; + lv_style_set_prop(style, LV_STYLE_HEIGHT, v); +} + +void lv_style_set_min_height(lv_style_t * style, lv_coord_t value) +{ + lv_style_value_t v = { + .num = (int32_t)value + }; + lv_style_set_prop(style, LV_STYLE_MIN_HEIGHT, v); +} + +void lv_style_set_max_height(lv_style_t * style, lv_coord_t value) +{ + lv_style_value_t v = { + .num = (int32_t)value + }; + lv_style_set_prop(style, LV_STYLE_MAX_HEIGHT, v); +} + +void lv_style_set_x(lv_style_t * style, lv_coord_t value) +{ + lv_style_value_t v = { + .num = (int32_t)value + }; + lv_style_set_prop(style, LV_STYLE_X, v); +} + +void lv_style_set_y(lv_style_t * style, lv_coord_t value) +{ + lv_style_value_t v = { + .num = (int32_t)value + }; + lv_style_set_prop(style, LV_STYLE_Y, v); +} + +void lv_style_set_align(lv_style_t * style, lv_align_t value) +{ + lv_style_value_t v = { + .num = (int32_t)value + }; + lv_style_set_prop(style, LV_STYLE_ALIGN, v); +} + +void lv_style_set_transform_width(lv_style_t * style, lv_coord_t value) +{ + lv_style_value_t v = { + .num = (int32_t)value + }; + lv_style_set_prop(style, LV_STYLE_TRANSFORM_WIDTH, v); +} + +void lv_style_set_transform_height(lv_style_t * style, lv_coord_t value) +{ + lv_style_value_t v = { + .num = (int32_t)value + }; + lv_style_set_prop(style, LV_STYLE_TRANSFORM_HEIGHT, v); +} + +void lv_style_set_translate_x(lv_style_t * style, lv_coord_t value) +{ + lv_style_value_t v = { + .num = (int32_t)value + }; + lv_style_set_prop(style, LV_STYLE_TRANSLATE_X, v); +} + +void lv_style_set_translate_y(lv_style_t * style, lv_coord_t value) +{ + lv_style_value_t v = { + .num = (int32_t)value + }; + lv_style_set_prop(style, LV_STYLE_TRANSLATE_Y, v); +} + +void lv_style_set_transform_zoom(lv_style_t * style, lv_coord_t value) +{ + lv_style_value_t v = { + .num = (int32_t)value + }; + lv_style_set_prop(style, LV_STYLE_TRANSFORM_ZOOM, v); +} + +void lv_style_set_transform_angle(lv_style_t * style, lv_coord_t value) +{ + lv_style_value_t v = { + .num = (int32_t)value + }; + lv_style_set_prop(style, LV_STYLE_TRANSFORM_ANGLE, v); +} + +void lv_style_set_transform_pivot_x(lv_style_t * style, lv_coord_t value) +{ + lv_style_value_t v = { + .num = (int32_t)value + }; + lv_style_set_prop(style, LV_STYLE_TRANSFORM_PIVOT_X, v); +} + +void lv_style_set_transform_pivot_y(lv_style_t * style, lv_coord_t value) +{ + lv_style_value_t v = { + .num = (int32_t)value + }; + lv_style_set_prop(style, LV_STYLE_TRANSFORM_PIVOT_Y, v); +} + +void lv_style_set_pad_top(lv_style_t * style, lv_coord_t value) +{ + lv_style_value_t v = { + .num = (int32_t)value + }; + lv_style_set_prop(style, LV_STYLE_PAD_TOP, v); +} + +void lv_style_set_pad_bottom(lv_style_t * style, lv_coord_t value) +{ + lv_style_value_t v = { + .num = (int32_t)value + }; + lv_style_set_prop(style, LV_STYLE_PAD_BOTTOM, v); +} + +void lv_style_set_pad_left(lv_style_t * style, lv_coord_t value) +{ + lv_style_value_t v = { + .num = (int32_t)value + }; + lv_style_set_prop(style, LV_STYLE_PAD_LEFT, v); +} + +void lv_style_set_pad_right(lv_style_t * style, lv_coord_t value) +{ + lv_style_value_t v = { + .num = (int32_t)value + }; + lv_style_set_prop(style, LV_STYLE_PAD_RIGHT, v); +} + +void lv_style_set_pad_row(lv_style_t * style, lv_coord_t value) +{ + lv_style_value_t v = { + .num = (int32_t)value + }; + lv_style_set_prop(style, LV_STYLE_PAD_ROW, v); +} + +void lv_style_set_pad_column(lv_style_t * style, lv_coord_t value) +{ + lv_style_value_t v = { + .num = (int32_t)value + }; + lv_style_set_prop(style, LV_STYLE_PAD_COLUMN, v); +} + +void lv_style_set_bg_color(lv_style_t * style, lv_color_t value) +{ + lv_style_value_t v = { + .color = value + }; + lv_style_set_prop(style, LV_STYLE_BG_COLOR, v); +} + +void lv_style_set_bg_opa(lv_style_t * style, lv_opa_t value) +{ + lv_style_value_t v = { + .num = (int32_t)value + }; + lv_style_set_prop(style, LV_STYLE_BG_OPA, v); +} + +void lv_style_set_bg_grad_color(lv_style_t * style, lv_color_t value) +{ + lv_style_value_t v = { + .color = value + }; + lv_style_set_prop(style, LV_STYLE_BG_GRAD_COLOR, v); +} + +void lv_style_set_bg_grad_dir(lv_style_t * style, lv_grad_dir_t value) +{ + lv_style_value_t v = { + .num = (int32_t)value + }; + lv_style_set_prop(style, LV_STYLE_BG_GRAD_DIR, v); +} + +void lv_style_set_bg_main_stop(lv_style_t * style, lv_coord_t value) +{ + lv_style_value_t v = { + .num = (int32_t)value + }; + lv_style_set_prop(style, LV_STYLE_BG_MAIN_STOP, v); +} + +void lv_style_set_bg_grad_stop(lv_style_t * style, lv_coord_t value) +{ + lv_style_value_t v = { + .num = (int32_t)value + }; + lv_style_set_prop(style, LV_STYLE_BG_GRAD_STOP, v); +} + +void lv_style_set_bg_grad(lv_style_t * style, const lv_grad_dsc_t * value) +{ + lv_style_value_t v = { + .ptr = value + }; + lv_style_set_prop(style, LV_STYLE_BG_GRAD, v); +} + +void lv_style_set_bg_dither_mode(lv_style_t * style, lv_dither_mode_t value) +{ + lv_style_value_t v = { + .num = (int32_t)value + }; + lv_style_set_prop(style, LV_STYLE_BG_DITHER_MODE, v); +} + +void lv_style_set_bg_img_src(lv_style_t * style, const void * value) +{ + lv_style_value_t v = { + .ptr = value + }; + lv_style_set_prop(style, LV_STYLE_BG_IMG_SRC, v); +} + +void lv_style_set_bg_img_opa(lv_style_t * style, lv_opa_t value) +{ + lv_style_value_t v = { + .num = (int32_t)value + }; + lv_style_set_prop(style, LV_STYLE_BG_IMG_OPA, v); +} + +void lv_style_set_bg_img_recolor(lv_style_t * style, lv_color_t value) +{ + lv_style_value_t v = { + .color = value + }; + lv_style_set_prop(style, LV_STYLE_BG_IMG_RECOLOR, v); +} + +void lv_style_set_bg_img_recolor_opa(lv_style_t * style, lv_opa_t value) +{ + lv_style_value_t v = { + .num = (int32_t)value + }; + lv_style_set_prop(style, LV_STYLE_BG_IMG_RECOLOR_OPA, v); +} + +void lv_style_set_bg_img_tiled(lv_style_t * style, bool value) +{ + lv_style_value_t v = { + .num = (int32_t)value + }; + lv_style_set_prop(style, LV_STYLE_BG_IMG_TILED, v); +} + +void lv_style_set_border_color(lv_style_t * style, lv_color_t value) +{ + lv_style_value_t v = { + .color = value + }; + lv_style_set_prop(style, LV_STYLE_BORDER_COLOR, v); +} + +void lv_style_set_border_opa(lv_style_t * style, lv_opa_t value) +{ + lv_style_value_t v = { + .num = (int32_t)value + }; + lv_style_set_prop(style, LV_STYLE_BORDER_OPA, v); +} + +void lv_style_set_border_width(lv_style_t * style, lv_coord_t value) +{ + lv_style_value_t v = { + .num = (int32_t)value + }; + lv_style_set_prop(style, LV_STYLE_BORDER_WIDTH, v); +} + +void lv_style_set_border_side(lv_style_t * style, lv_border_side_t value) +{ + lv_style_value_t v = { + .num = (int32_t)value + }; + lv_style_set_prop(style, LV_STYLE_BORDER_SIDE, v); +} + +void lv_style_set_border_post(lv_style_t * style, bool value) +{ + lv_style_value_t v = { + .num = (int32_t)value + }; + lv_style_set_prop(style, LV_STYLE_BORDER_POST, v); +} + +void lv_style_set_outline_width(lv_style_t * style, lv_coord_t value) +{ + lv_style_value_t v = { + .num = (int32_t)value + }; + lv_style_set_prop(style, LV_STYLE_OUTLINE_WIDTH, v); +} + +void lv_style_set_outline_color(lv_style_t * style, lv_color_t value) +{ + lv_style_value_t v = { + .color = value + }; + lv_style_set_prop(style, LV_STYLE_OUTLINE_COLOR, v); +} + +void lv_style_set_outline_opa(lv_style_t * style, lv_opa_t value) +{ + lv_style_value_t v = { + .num = (int32_t)value + }; + lv_style_set_prop(style, LV_STYLE_OUTLINE_OPA, v); +} + +void lv_style_set_outline_pad(lv_style_t * style, lv_coord_t value) +{ + lv_style_value_t v = { + .num = (int32_t)value + }; + lv_style_set_prop(style, LV_STYLE_OUTLINE_PAD, v); +} + +void lv_style_set_shadow_width(lv_style_t * style, lv_coord_t value) +{ + lv_style_value_t v = { + .num = (int32_t)value + }; + lv_style_set_prop(style, LV_STYLE_SHADOW_WIDTH, v); +} + +void lv_style_set_shadow_ofs_x(lv_style_t * style, lv_coord_t value) +{ + lv_style_value_t v = { + .num = (int32_t)value + }; + lv_style_set_prop(style, LV_STYLE_SHADOW_OFS_X, v); +} + +void lv_style_set_shadow_ofs_y(lv_style_t * style, lv_coord_t value) +{ + lv_style_value_t v = { + .num = (int32_t)value + }; + lv_style_set_prop(style, LV_STYLE_SHADOW_OFS_Y, v); +} + +void lv_style_set_shadow_spread(lv_style_t * style, lv_coord_t value) +{ + lv_style_value_t v = { + .num = (int32_t)value + }; + lv_style_set_prop(style, LV_STYLE_SHADOW_SPREAD, v); +} + +void lv_style_set_shadow_color(lv_style_t * style, lv_color_t value) +{ + lv_style_value_t v = { + .color = value + }; + lv_style_set_prop(style, LV_STYLE_SHADOW_COLOR, v); +} + +void lv_style_set_shadow_opa(lv_style_t * style, lv_opa_t value) +{ + lv_style_value_t v = { + .num = (int32_t)value + }; + lv_style_set_prop(style, LV_STYLE_SHADOW_OPA, v); +} + +void lv_style_set_img_opa(lv_style_t * style, lv_opa_t value) +{ + lv_style_value_t v = { + .num = (int32_t)value + }; + lv_style_set_prop(style, LV_STYLE_IMG_OPA, v); +} + +void lv_style_set_img_recolor(lv_style_t * style, lv_color_t value) +{ + lv_style_value_t v = { + .color = value + }; + lv_style_set_prop(style, LV_STYLE_IMG_RECOLOR, v); +} + +void lv_style_set_img_recolor_opa(lv_style_t * style, lv_opa_t value) +{ + lv_style_value_t v = { + .num = (int32_t)value + }; + lv_style_set_prop(style, LV_STYLE_IMG_RECOLOR_OPA, v); +} + +void lv_style_set_line_width(lv_style_t * style, lv_coord_t value) +{ + lv_style_value_t v = { + .num = (int32_t)value + }; + lv_style_set_prop(style, LV_STYLE_LINE_WIDTH, v); +} + +void lv_style_set_line_dash_width(lv_style_t * style, lv_coord_t value) +{ + lv_style_value_t v = { + .num = (int32_t)value + }; + lv_style_set_prop(style, LV_STYLE_LINE_DASH_WIDTH, v); +} + +void lv_style_set_line_dash_gap(lv_style_t * style, lv_coord_t value) +{ + lv_style_value_t v = { + .num = (int32_t)value + }; + lv_style_set_prop(style, LV_STYLE_LINE_DASH_GAP, v); +} + +void lv_style_set_line_rounded(lv_style_t * style, bool value) +{ + lv_style_value_t v = { + .num = (int32_t)value + }; + lv_style_set_prop(style, LV_STYLE_LINE_ROUNDED, v); +} + +void lv_style_set_line_color(lv_style_t * style, lv_color_t value) +{ + lv_style_value_t v = { + .color = value + }; + lv_style_set_prop(style, LV_STYLE_LINE_COLOR, v); +} + +void lv_style_set_line_opa(lv_style_t * style, lv_opa_t value) +{ + lv_style_value_t v = { + .num = (int32_t)value + }; + lv_style_set_prop(style, LV_STYLE_LINE_OPA, v); +} + +void lv_style_set_arc_width(lv_style_t * style, lv_coord_t value) +{ + lv_style_value_t v = { + .num = (int32_t)value + }; + lv_style_set_prop(style, LV_STYLE_ARC_WIDTH, v); +} + +void lv_style_set_arc_rounded(lv_style_t * style, bool value) +{ + lv_style_value_t v = { + .num = (int32_t)value + }; + lv_style_set_prop(style, LV_STYLE_ARC_ROUNDED, v); +} + +void lv_style_set_arc_color(lv_style_t * style, lv_color_t value) +{ + lv_style_value_t v = { + .color = value + }; + lv_style_set_prop(style, LV_STYLE_ARC_COLOR, v); +} + +void lv_style_set_arc_opa(lv_style_t * style, lv_opa_t value) +{ + lv_style_value_t v = { + .num = (int32_t)value + }; + lv_style_set_prop(style, LV_STYLE_ARC_OPA, v); +} + +void lv_style_set_arc_img_src(lv_style_t * style, const void * value) +{ + lv_style_value_t v = { + .ptr = value + }; + lv_style_set_prop(style, LV_STYLE_ARC_IMG_SRC, v); +} + +void lv_style_set_text_color(lv_style_t * style, lv_color_t value) +{ + lv_style_value_t v = { + .color = value + }; + lv_style_set_prop(style, LV_STYLE_TEXT_COLOR, v); +} + +void lv_style_set_text_opa(lv_style_t * style, lv_opa_t value) +{ + lv_style_value_t v = { + .num = (int32_t)value + }; + lv_style_set_prop(style, LV_STYLE_TEXT_OPA, v); +} + +void lv_style_set_text_font(lv_style_t * style, const lv_font_t * value) +{ + lv_style_value_t v = { + .ptr = value + }; + lv_style_set_prop(style, LV_STYLE_TEXT_FONT, v); +} + +void lv_style_set_text_letter_space(lv_style_t * style, lv_coord_t value) +{ + lv_style_value_t v = { + .num = (int32_t)value + }; + lv_style_set_prop(style, LV_STYLE_TEXT_LETTER_SPACE, v); +} + +void lv_style_set_text_line_space(lv_style_t * style, lv_coord_t value) +{ + lv_style_value_t v = { + .num = (int32_t)value + }; + lv_style_set_prop(style, LV_STYLE_TEXT_LINE_SPACE, v); +} + +void lv_style_set_text_decor(lv_style_t * style, lv_text_decor_t value) +{ + lv_style_value_t v = { + .num = (int32_t)value + }; + lv_style_set_prop(style, LV_STYLE_TEXT_DECOR, v); +} + +void lv_style_set_text_align(lv_style_t * style, lv_text_align_t value) +{ + lv_style_value_t v = { + .num = (int32_t)value + }; + lv_style_set_prop(style, LV_STYLE_TEXT_ALIGN, v); +} + +void lv_style_set_radius(lv_style_t * style, lv_coord_t value) +{ + lv_style_value_t v = { + .num = (int32_t)value + }; + lv_style_set_prop(style, LV_STYLE_RADIUS, v); +} + +void lv_style_set_clip_corner(lv_style_t * style, bool value) +{ + lv_style_value_t v = { + .num = (int32_t)value + }; + lv_style_set_prop(style, LV_STYLE_CLIP_CORNER, v); +} + +void lv_style_set_opa(lv_style_t * style, lv_opa_t value) +{ + lv_style_value_t v = { + .num = (int32_t)value + }; + lv_style_set_prop(style, LV_STYLE_OPA, v); +} + +void lv_style_set_color_filter_dsc(lv_style_t * style, const lv_color_filter_dsc_t * value) +{ + lv_style_value_t v = { + .ptr = value + }; + lv_style_set_prop(style, LV_STYLE_COLOR_FILTER_DSC, v); +} + +void lv_style_set_color_filter_opa(lv_style_t * style, lv_opa_t value) +{ + lv_style_value_t v = { + .num = (int32_t)value + }; + lv_style_set_prop(style, LV_STYLE_COLOR_FILTER_OPA, v); +} + +void lv_style_set_anim(lv_style_t * style, const lv_anim_t * value) +{ + lv_style_value_t v = { + .ptr = value + }; + lv_style_set_prop(style, LV_STYLE_ANIM, v); +} + +void lv_style_set_anim_time(lv_style_t * style, uint32_t value) +{ + lv_style_value_t v = { + .num = (int32_t)value + }; + lv_style_set_prop(style, LV_STYLE_ANIM_TIME, v); +} + +void lv_style_set_anim_speed(lv_style_t * style, uint32_t value) +{ + lv_style_value_t v = { + .num = (int32_t)value + }; + lv_style_set_prop(style, LV_STYLE_ANIM_SPEED, v); +} + +void lv_style_set_transition(lv_style_t * style, const lv_style_transition_dsc_t * value) +{ + lv_style_value_t v = { + .ptr = value + }; + lv_style_set_prop(style, LV_STYLE_TRANSITION, v); +} + +void lv_style_set_blend_mode(lv_style_t * style, lv_blend_mode_t value) +{ + lv_style_value_t v = { + .num = (int32_t)value + }; + lv_style_set_prop(style, LV_STYLE_BLEND_MODE, v); +} + +void lv_style_set_layout(lv_style_t * style, uint16_t value) +{ + lv_style_value_t v = { + .num = (int32_t)value + }; + lv_style_set_prop(style, LV_STYLE_LAYOUT, v); +} + +void lv_style_set_base_dir(lv_style_t * style, lv_base_dir_t value) +{ + lv_style_value_t v = { + .num = (int32_t)value + }; + lv_style_set_prop(style, LV_STYLE_BASE_DIR, v); +} diff --git a/lib/lvgl/src/misc/lv_style_gen.h b/lib/lvgl/src/misc/lv_style_gen.h new file mode 100644 index 00000000..8bf3b68f --- /dev/null +++ b/lib/lvgl/src/misc/lv_style_gen.h @@ -0,0 +1,504 @@ +void lv_style_set_width(lv_style_t * style, lv_coord_t value); +void lv_style_set_min_width(lv_style_t * style, lv_coord_t value); +void lv_style_set_max_width(lv_style_t * style, lv_coord_t value); +void lv_style_set_height(lv_style_t * style, lv_coord_t value); +void lv_style_set_min_height(lv_style_t * style, lv_coord_t value); +void lv_style_set_max_height(lv_style_t * style, lv_coord_t value); +void lv_style_set_x(lv_style_t * style, lv_coord_t value); +void lv_style_set_y(lv_style_t * style, lv_coord_t value); +void lv_style_set_align(lv_style_t * style, lv_align_t value); +void lv_style_set_transform_width(lv_style_t * style, lv_coord_t value); +void lv_style_set_transform_height(lv_style_t * style, lv_coord_t value); +void lv_style_set_translate_x(lv_style_t * style, lv_coord_t value); +void lv_style_set_translate_y(lv_style_t * style, lv_coord_t value); +void lv_style_set_transform_zoom(lv_style_t * style, lv_coord_t value); +void lv_style_set_transform_angle(lv_style_t * style, lv_coord_t value); +void lv_style_set_transform_pivot_x(lv_style_t * style, lv_coord_t value); +void lv_style_set_transform_pivot_y(lv_style_t * style, lv_coord_t value); +void lv_style_set_pad_top(lv_style_t * style, lv_coord_t value); +void lv_style_set_pad_bottom(lv_style_t * style, lv_coord_t value); +void lv_style_set_pad_left(lv_style_t * style, lv_coord_t value); +void lv_style_set_pad_right(lv_style_t * style, lv_coord_t value); +void lv_style_set_pad_row(lv_style_t * style, lv_coord_t value); +void lv_style_set_pad_column(lv_style_t * style, lv_coord_t value); +void lv_style_set_bg_color(lv_style_t * style, lv_color_t value); +void lv_style_set_bg_opa(lv_style_t * style, lv_opa_t value); +void lv_style_set_bg_grad_color(lv_style_t * style, lv_color_t value); +void lv_style_set_bg_grad_dir(lv_style_t * style, lv_grad_dir_t value); +void lv_style_set_bg_main_stop(lv_style_t * style, lv_coord_t value); +void lv_style_set_bg_grad_stop(lv_style_t * style, lv_coord_t value); +void lv_style_set_bg_grad(lv_style_t * style, const lv_grad_dsc_t * value); +void lv_style_set_bg_dither_mode(lv_style_t * style, lv_dither_mode_t value); +void lv_style_set_bg_img_src(lv_style_t * style, const void * value); +void lv_style_set_bg_img_opa(lv_style_t * style, lv_opa_t value); +void lv_style_set_bg_img_recolor(lv_style_t * style, lv_color_t value); +void lv_style_set_bg_img_recolor_opa(lv_style_t * style, lv_opa_t value); +void lv_style_set_bg_img_tiled(lv_style_t * style, bool value); +void lv_style_set_border_color(lv_style_t * style, lv_color_t value); +void lv_style_set_border_opa(lv_style_t * style, lv_opa_t value); +void lv_style_set_border_width(lv_style_t * style, lv_coord_t value); +void lv_style_set_border_side(lv_style_t * style, lv_border_side_t value); +void lv_style_set_border_post(lv_style_t * style, bool value); +void lv_style_set_outline_width(lv_style_t * style, lv_coord_t value); +void lv_style_set_outline_color(lv_style_t * style, lv_color_t value); +void lv_style_set_outline_opa(lv_style_t * style, lv_opa_t value); +void lv_style_set_outline_pad(lv_style_t * style, lv_coord_t value); +void lv_style_set_shadow_width(lv_style_t * style, lv_coord_t value); +void lv_style_set_shadow_ofs_x(lv_style_t * style, lv_coord_t value); +void lv_style_set_shadow_ofs_y(lv_style_t * style, lv_coord_t value); +void lv_style_set_shadow_spread(lv_style_t * style, lv_coord_t value); +void lv_style_set_shadow_color(lv_style_t * style, lv_color_t value); +void lv_style_set_shadow_opa(lv_style_t * style, lv_opa_t value); +void lv_style_set_img_opa(lv_style_t * style, lv_opa_t value); +void lv_style_set_img_recolor(lv_style_t * style, lv_color_t value); +void lv_style_set_img_recolor_opa(lv_style_t * style, lv_opa_t value); +void lv_style_set_line_width(lv_style_t * style, lv_coord_t value); +void lv_style_set_line_dash_width(lv_style_t * style, lv_coord_t value); +void lv_style_set_line_dash_gap(lv_style_t * style, lv_coord_t value); +void lv_style_set_line_rounded(lv_style_t * style, bool value); +void lv_style_set_line_color(lv_style_t * style, lv_color_t value); +void lv_style_set_line_opa(lv_style_t * style, lv_opa_t value); +void lv_style_set_arc_width(lv_style_t * style, lv_coord_t value); +void lv_style_set_arc_rounded(lv_style_t * style, bool value); +void lv_style_set_arc_color(lv_style_t * style, lv_color_t value); +void lv_style_set_arc_opa(lv_style_t * style, lv_opa_t value); +void lv_style_set_arc_img_src(lv_style_t * style, const void * value); +void lv_style_set_text_color(lv_style_t * style, lv_color_t value); +void lv_style_set_text_opa(lv_style_t * style, lv_opa_t value); +void lv_style_set_text_font(lv_style_t * style, const lv_font_t * value); +void lv_style_set_text_letter_space(lv_style_t * style, lv_coord_t value); +void lv_style_set_text_line_space(lv_style_t * style, lv_coord_t value); +void lv_style_set_text_decor(lv_style_t * style, lv_text_decor_t value); +void lv_style_set_text_align(lv_style_t * style, lv_text_align_t value); +void lv_style_set_radius(lv_style_t * style, lv_coord_t value); +void lv_style_set_clip_corner(lv_style_t * style, bool value); +void lv_style_set_opa(lv_style_t * style, lv_opa_t value); +void lv_style_set_color_filter_dsc(lv_style_t * style, const lv_color_filter_dsc_t * value); +void lv_style_set_color_filter_opa(lv_style_t * style, lv_opa_t value); +void lv_style_set_anim(lv_style_t * style, const lv_anim_t * value); +void lv_style_set_anim_time(lv_style_t * style, uint32_t value); +void lv_style_set_anim_speed(lv_style_t * style, uint32_t value); +void lv_style_set_transition(lv_style_t * style, const lv_style_transition_dsc_t * value); +void lv_style_set_blend_mode(lv_style_t * style, lv_blend_mode_t value); +void lv_style_set_layout(lv_style_t * style, uint16_t value); +void lv_style_set_base_dir(lv_style_t * style, lv_base_dir_t value); + +#define LV_STYLE_CONST_WIDTH(val) \ + { \ + .prop = LV_STYLE_WIDTH, .value = { .num = (int32_t)val } \ + } + +#define LV_STYLE_CONST_MIN_WIDTH(val) \ + { \ + .prop = LV_STYLE_MIN_WIDTH, .value = { .num = (int32_t)val } \ + } + +#define LV_STYLE_CONST_MAX_WIDTH(val) \ + { \ + .prop = LV_STYLE_MAX_WIDTH, .value = { .num = (int32_t)val } \ + } + +#define LV_STYLE_CONST_HEIGHT(val) \ + { \ + .prop = LV_STYLE_HEIGHT, .value = { .num = (int32_t)val } \ + } + +#define LV_STYLE_CONST_MIN_HEIGHT(val) \ + { \ + .prop = LV_STYLE_MIN_HEIGHT, .value = { .num = (int32_t)val } \ + } + +#define LV_STYLE_CONST_MAX_HEIGHT(val) \ + { \ + .prop = LV_STYLE_MAX_HEIGHT, .value = { .num = (int32_t)val } \ + } + +#define LV_STYLE_CONST_X(val) \ + { \ + .prop = LV_STYLE_X, .value = { .num = (int32_t)val } \ + } + +#define LV_STYLE_CONST_Y(val) \ + { \ + .prop = LV_STYLE_Y, .value = { .num = (int32_t)val } \ + } + +#define LV_STYLE_CONST_ALIGN(val) \ + { \ + .prop = LV_STYLE_ALIGN, .value = { .num = (int32_t)val } \ + } + +#define LV_STYLE_CONST_TRANSFORM_WIDTH(val) \ + { \ + .prop = LV_STYLE_TRANSFORM_WIDTH, .value = { .num = (int32_t)val } \ + } + +#define LV_STYLE_CONST_TRANSFORM_HEIGHT(val) \ + { \ + .prop = LV_STYLE_TRANSFORM_HEIGHT, .value = { .num = (int32_t)val } \ + } + +#define LV_STYLE_CONST_TRANSLATE_X(val) \ + { \ + .prop = LV_STYLE_TRANSLATE_X, .value = { .num = (int32_t)val } \ + } + +#define LV_STYLE_CONST_TRANSLATE_Y(val) \ + { \ + .prop = LV_STYLE_TRANSLATE_Y, .value = { .num = (int32_t)val } \ + } + +#define LV_STYLE_CONST_TRANSFORM_ZOOM(val) \ + { \ + .prop = LV_STYLE_TRANSFORM_ZOOM, .value = { .num = (int32_t)val } \ + } + +#define LV_STYLE_CONST_TRANSFORM_ANGLE(val) \ + { \ + .prop = LV_STYLE_TRANSFORM_ANGLE, .value = { .num = (int32_t)val } \ + } + +#define LV_STYLE_CONST_TRANSFORM_PIVOT_X(val) \ + { \ + .prop = LV_STYLE_TRANSFORM_PIVOT_X, .value = { .num = (int32_t)val } \ + } + +#define LV_STYLE_CONST_TRANSFORM_PIVOT_Y(val) \ + { \ + .prop = LV_STYLE_TRANSFORM_PIVOT_Y, .value = { .num = (int32_t)val } \ + } + +#define LV_STYLE_CONST_PAD_TOP(val) \ + { \ + .prop = LV_STYLE_PAD_TOP, .value = { .num = (int32_t)val } \ + } + +#define LV_STYLE_CONST_PAD_BOTTOM(val) \ + { \ + .prop = LV_STYLE_PAD_BOTTOM, .value = { .num = (int32_t)val } \ + } + +#define LV_STYLE_CONST_PAD_LEFT(val) \ + { \ + .prop = LV_STYLE_PAD_LEFT, .value = { .num = (int32_t)val } \ + } + +#define LV_STYLE_CONST_PAD_RIGHT(val) \ + { \ + .prop = LV_STYLE_PAD_RIGHT, .value = { .num = (int32_t)val } \ + } + +#define LV_STYLE_CONST_PAD_ROW(val) \ + { \ + .prop = LV_STYLE_PAD_ROW, .value = { .num = (int32_t)val } \ + } + +#define LV_STYLE_CONST_PAD_COLUMN(val) \ + { \ + .prop = LV_STYLE_PAD_COLUMN, .value = { .num = (int32_t)val } \ + } + +#define LV_STYLE_CONST_BG_COLOR(val) \ + { \ + .prop = LV_STYLE_BG_COLOR, .value = { .color = val } \ + } + +#define LV_STYLE_CONST_BG_OPA(val) \ + { \ + .prop = LV_STYLE_BG_OPA, .value = { .num = (int32_t)val } \ + } + +#define LV_STYLE_CONST_BG_GRAD_COLOR(val) \ + { \ + .prop = LV_STYLE_BG_GRAD_COLOR, .value = { .color = val } \ + } + +#define LV_STYLE_CONST_BG_GRAD_DIR(val) \ + { \ + .prop = LV_STYLE_BG_GRAD_DIR, .value = { .num = (int32_t)val } \ + } + +#define LV_STYLE_CONST_BG_MAIN_STOP(val) \ + { \ + .prop = LV_STYLE_BG_MAIN_STOP, .value = { .num = (int32_t)val } \ + } + +#define LV_STYLE_CONST_BG_GRAD_STOP(val) \ + { \ + .prop = LV_STYLE_BG_GRAD_STOP, .value = { .num = (int32_t)val } \ + } + +#define LV_STYLE_CONST_BG_GRAD(val) \ + { \ + .prop = LV_STYLE_BG_GRAD, .value = { .ptr = val } \ + } + +#define LV_STYLE_CONST_BG_DITHER_MODE(val) \ + { \ + .prop = LV_STYLE_BG_DITHER_MODE, .value = { .num = (int32_t)val } \ + } + +#define LV_STYLE_CONST_BG_IMG_SRC(val) \ + { \ + .prop = LV_STYLE_BG_IMG_SRC, .value = { .ptr = val } \ + } + +#define LV_STYLE_CONST_BG_IMG_OPA(val) \ + { \ + .prop = LV_STYLE_BG_IMG_OPA, .value = { .num = (int32_t)val } \ + } + +#define LV_STYLE_CONST_BG_IMG_RECOLOR(val) \ + { \ + .prop = LV_STYLE_BG_IMG_RECOLOR, .value = { .color = val } \ + } + +#define LV_STYLE_CONST_BG_IMG_RECOLOR_OPA(val) \ + { \ + .prop = LV_STYLE_BG_IMG_RECOLOR_OPA, .value = { .num = (int32_t)val } \ + } + +#define LV_STYLE_CONST_BG_IMG_TILED(val) \ + { \ + .prop = LV_STYLE_BG_IMG_TILED, .value = { .num = (int32_t)val } \ + } + +#define LV_STYLE_CONST_BORDER_COLOR(val) \ + { \ + .prop = LV_STYLE_BORDER_COLOR, .value = { .color = val } \ + } + +#define LV_STYLE_CONST_BORDER_OPA(val) \ + { \ + .prop = LV_STYLE_BORDER_OPA, .value = { .num = (int32_t)val } \ + } + +#define LV_STYLE_CONST_BORDER_WIDTH(val) \ + { \ + .prop = LV_STYLE_BORDER_WIDTH, .value = { .num = (int32_t)val } \ + } + +#define LV_STYLE_CONST_BORDER_SIDE(val) \ + { \ + .prop = LV_STYLE_BORDER_SIDE, .value = { .num = (int32_t)val } \ + } + +#define LV_STYLE_CONST_BORDER_POST(val) \ + { \ + .prop = LV_STYLE_BORDER_POST, .value = { .num = (int32_t)val } \ + } + +#define LV_STYLE_CONST_OUTLINE_WIDTH(val) \ + { \ + .prop = LV_STYLE_OUTLINE_WIDTH, .value = { .num = (int32_t)val } \ + } + +#define LV_STYLE_CONST_OUTLINE_COLOR(val) \ + { \ + .prop = LV_STYLE_OUTLINE_COLOR, .value = { .color = val } \ + } + +#define LV_STYLE_CONST_OUTLINE_OPA(val) \ + { \ + .prop = LV_STYLE_OUTLINE_OPA, .value = { .num = (int32_t)val } \ + } + +#define LV_STYLE_CONST_OUTLINE_PAD(val) \ + { \ + .prop = LV_STYLE_OUTLINE_PAD, .value = { .num = (int32_t)val } \ + } + +#define LV_STYLE_CONST_SHADOW_WIDTH(val) \ + { \ + .prop = LV_STYLE_SHADOW_WIDTH, .value = { .num = (int32_t)val } \ + } + +#define LV_STYLE_CONST_SHADOW_OFS_X(val) \ + { \ + .prop = LV_STYLE_SHADOW_OFS_X, .value = { .num = (int32_t)val } \ + } + +#define LV_STYLE_CONST_SHADOW_OFS_Y(val) \ + { \ + .prop = LV_STYLE_SHADOW_OFS_Y, .value = { .num = (int32_t)val } \ + } + +#define LV_STYLE_CONST_SHADOW_SPREAD(val) \ + { \ + .prop = LV_STYLE_SHADOW_SPREAD, .value = { .num = (int32_t)val } \ + } + +#define LV_STYLE_CONST_SHADOW_COLOR(val) \ + { \ + .prop = LV_STYLE_SHADOW_COLOR, .value = { .color = val } \ + } + +#define LV_STYLE_CONST_SHADOW_OPA(val) \ + { \ + .prop = LV_STYLE_SHADOW_OPA, .value = { .num = (int32_t)val } \ + } + +#define LV_STYLE_CONST_IMG_OPA(val) \ + { \ + .prop = LV_STYLE_IMG_OPA, .value = { .num = (int32_t)val } \ + } + +#define LV_STYLE_CONST_IMG_RECOLOR(val) \ + { \ + .prop = LV_STYLE_IMG_RECOLOR, .value = { .color = val } \ + } + +#define LV_STYLE_CONST_IMG_RECOLOR_OPA(val) \ + { \ + .prop = LV_STYLE_IMG_RECOLOR_OPA, .value = { .num = (int32_t)val } \ + } + +#define LV_STYLE_CONST_LINE_WIDTH(val) \ + { \ + .prop = LV_STYLE_LINE_WIDTH, .value = { .num = (int32_t)val } \ + } + +#define LV_STYLE_CONST_LINE_DASH_WIDTH(val) \ + { \ + .prop = LV_STYLE_LINE_DASH_WIDTH, .value = { .num = (int32_t)val } \ + } + +#define LV_STYLE_CONST_LINE_DASH_GAP(val) \ + { \ + .prop = LV_STYLE_LINE_DASH_GAP, .value = { .num = (int32_t)val } \ + } + +#define LV_STYLE_CONST_LINE_ROUNDED(val) \ + { \ + .prop = LV_STYLE_LINE_ROUNDED, .value = { .num = (int32_t)val } \ + } + +#define LV_STYLE_CONST_LINE_COLOR(val) \ + { \ + .prop = LV_STYLE_LINE_COLOR, .value = { .color = val } \ + } + +#define LV_STYLE_CONST_LINE_OPA(val) \ + { \ + .prop = LV_STYLE_LINE_OPA, .value = { .num = (int32_t)val } \ + } + +#define LV_STYLE_CONST_ARC_WIDTH(val) \ + { \ + .prop = LV_STYLE_ARC_WIDTH, .value = { .num = (int32_t)val } \ + } + +#define LV_STYLE_CONST_ARC_ROUNDED(val) \ + { \ + .prop = LV_STYLE_ARC_ROUNDED, .value = { .num = (int32_t)val } \ + } + +#define LV_STYLE_CONST_ARC_COLOR(val) \ + { \ + .prop = LV_STYLE_ARC_COLOR, .value = { .color = val } \ + } + +#define LV_STYLE_CONST_ARC_OPA(val) \ + { \ + .prop = LV_STYLE_ARC_OPA, .value = { .num = (int32_t)val } \ + } + +#define LV_STYLE_CONST_ARC_IMG_SRC(val) \ + { \ + .prop = LV_STYLE_ARC_IMG_SRC, .value = { .ptr = val } \ + } + +#define LV_STYLE_CONST_TEXT_COLOR(val) \ + { \ + .prop = LV_STYLE_TEXT_COLOR, .value = { .color = val } \ + } + +#define LV_STYLE_CONST_TEXT_OPA(val) \ + { \ + .prop = LV_STYLE_TEXT_OPA, .value = { .num = (int32_t)val } \ + } + +#define LV_STYLE_CONST_TEXT_FONT(val) \ + { \ + .prop = LV_STYLE_TEXT_FONT, .value = { .ptr = val } \ + } + +#define LV_STYLE_CONST_TEXT_LETTER_SPACE(val) \ + { \ + .prop = LV_STYLE_TEXT_LETTER_SPACE, .value = { .num = (int32_t)val } \ + } + +#define LV_STYLE_CONST_TEXT_LINE_SPACE(val) \ + { \ + .prop = LV_STYLE_TEXT_LINE_SPACE, .value = { .num = (int32_t)val } \ + } + +#define LV_STYLE_CONST_TEXT_DECOR(val) \ + { \ + .prop = LV_STYLE_TEXT_DECOR, .value = { .num = (int32_t)val } \ + } + +#define LV_STYLE_CONST_TEXT_ALIGN(val) \ + { \ + .prop = LV_STYLE_TEXT_ALIGN, .value = { .num = (int32_t)val } \ + } + +#define LV_STYLE_CONST_RADIUS(val) \ + { \ + .prop = LV_STYLE_RADIUS, .value = { .num = (int32_t)val } \ + } + +#define LV_STYLE_CONST_CLIP_CORNER(val) \ + { \ + .prop = LV_STYLE_CLIP_CORNER, .value = { .num = (int32_t)val } \ + } + +#define LV_STYLE_CONST_OPA(val) \ + { \ + .prop = LV_STYLE_OPA, .value = { .num = (int32_t)val } \ + } + +#define LV_STYLE_CONST_COLOR_FILTER_DSC(val) \ + { \ + .prop = LV_STYLE_COLOR_FILTER_DSC, .value = { .ptr = val } \ + } + +#define LV_STYLE_CONST_COLOR_FILTER_OPA(val) \ + { \ + .prop = LV_STYLE_COLOR_FILTER_OPA, .value = { .num = (int32_t)val } \ + } + +#define LV_STYLE_CONST_ANIM(val) \ + { \ + .prop = LV_STYLE_ANIM, .value = { .ptr = val } \ + } + +#define LV_STYLE_CONST_ANIM_TIME(val) \ + { \ + .prop = LV_STYLE_ANIM_TIME, .value = { .num = (int32_t)val } \ + } + +#define LV_STYLE_CONST_ANIM_SPEED(val) \ + { \ + .prop = LV_STYLE_ANIM_SPEED, .value = { .num = (int32_t)val } \ + } + +#define LV_STYLE_CONST_TRANSITION(val) \ + { \ + .prop = LV_STYLE_TRANSITION, .value = { .ptr = val } \ + } + +#define LV_STYLE_CONST_BLEND_MODE(val) \ + { \ + .prop = LV_STYLE_BLEND_MODE, .value = { .num = (int32_t)val } \ + } + +#define LV_STYLE_CONST_LAYOUT(val) \ + { \ + .prop = LV_STYLE_LAYOUT, .value = { .num = (int32_t)val } \ + } + +#define LV_STYLE_CONST_BASE_DIR(val) \ + { \ + .prop = LV_STYLE_BASE_DIR, .value = { .num = (int32_t)val } \ + } diff --git a/lib/lvgl/src/misc/lv_templ.c b/lib/lvgl/src/misc/lv_templ.c new file mode 100644 index 00000000..939930cf --- /dev/null +++ b/lib/lvgl/src/misc/lv_templ.c @@ -0,0 +1,40 @@ +/** + * @file lv_templ.c + * + */ + +/********************* + * INCLUDES + *********************/ + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/*This typedef exists purely to keep -Wpedantic happy when the file is empty.*/ +/*It can be removed.*/ +typedef int _keep_pedantic_happy; + +/********************** + * STATIC PROTOTYPES + **********************/ + +/********************** + * STATIC VARIABLES + **********************/ + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +/********************** + * STATIC FUNCTIONS + **********************/ diff --git a/lib/lvgl/src/misc/lv_templ.h b/lib/lvgl/src/misc/lv_templ.h new file mode 100644 index 00000000..f7e3c268 --- /dev/null +++ b/lib/lvgl/src/misc/lv_templ.h @@ -0,0 +1,37 @@ +/** + * @file lv_templ.h + * + */ + +#ifndef LV_TEMPL_H +#define LV_TEMPL_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +/********************** + * MACROS + **********************/ + +#ifdef __cplusplus +} /*extern "C"*/ +#endif + +#endif /*LV_TEMPL_H*/ diff --git a/lib/lvgl/src/misc/lv_timer.c b/lib/lvgl/src/misc/lv_timer.c new file mode 100644 index 00000000..d8dd55b7 --- /dev/null +++ b/lib/lvgl/src/misc/lv_timer.c @@ -0,0 +1,341 @@ +/** + * @file lv_timer.c + */ + +/********************* + * INCLUDES + *********************/ +#include "lv_timer.h" +#include "../hal/lv_hal_tick.h" +#include "lv_assert.h" +#include "lv_mem.h" +#include "lv_ll.h" +#include "lv_gc.h" + +/********************* + * DEFINES + *********************/ +#define IDLE_MEAS_PERIOD 500 /*[ms]*/ +#define DEF_PERIOD 500 + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ +static bool lv_timer_exec(lv_timer_t * timer); +static uint32_t lv_timer_time_remaining(lv_timer_t * timer); + +/********************** + * STATIC VARIABLES + **********************/ +static bool lv_timer_run = false; +static uint8_t idle_last = 0; +static bool timer_deleted; +static bool timer_created; + +/********************** + * MACROS + **********************/ +#if LV_LOG_TRACE_TIMER + #define TIMER_TRACE(...) LV_LOG_TRACE(__VA_ARGS__) +#else + #define TIMER_TRACE(...) +#endif + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +/** + * Init the lv_timer module + */ +void _lv_timer_core_init(void) +{ + _lv_ll_init(&LV_GC_ROOT(_lv_timer_ll), sizeof(lv_timer_t)); + + /*Initially enable the lv_timer handling*/ + lv_timer_enable(true); +} + +/** + * Call it periodically to handle lv_timers. + * @return the time after which it must be called again + */ +LV_ATTRIBUTE_TIMER_HANDLER uint32_t lv_timer_handler(void) +{ + TIMER_TRACE("begin"); + + /*Avoid concurrent running of the timer handler*/ + static bool already_running = false; + if(already_running) { + TIMER_TRACE("already running, concurrent calls are not allow, returning"); + return 1; + } + already_running = true; + + if(lv_timer_run == false) { + already_running = false; /*Release mutex*/ + return 1; + } + + static uint32_t idle_period_start = 0; + static uint32_t busy_time = 0; + + uint32_t handler_start = lv_tick_get(); + + if(handler_start == 0) { + static uint32_t run_cnt = 0; + run_cnt++; + if(run_cnt > 100) { + run_cnt = 0; + LV_LOG_WARN("It seems lv_tick_inc() is not called."); + } + } + + /*Run all timer from the list*/ + lv_timer_t * next; + do { + timer_deleted = false; + timer_created = false; + LV_GC_ROOT(_lv_timer_act) = _lv_ll_get_head(&LV_GC_ROOT(_lv_timer_ll)); + while(LV_GC_ROOT(_lv_timer_act)) { + /*The timer might be deleted if it runs only once ('repeat_count = 1') + *So get next element until the current is surely valid*/ + next = _lv_ll_get_next(&LV_GC_ROOT(_lv_timer_ll), LV_GC_ROOT(_lv_timer_act)); + + if(lv_timer_exec(LV_GC_ROOT(_lv_timer_act))) { + /*If a timer was created or deleted then this or the next item might be corrupted*/ + if(timer_created || timer_deleted) { + TIMER_TRACE("Start from the first timer again because a timer was created or deleted"); + break; + } + } + + LV_GC_ROOT(_lv_timer_act) = next; /*Load the next timer*/ + } + } while(LV_GC_ROOT(_lv_timer_act)); + + uint32_t time_till_next = LV_NO_TIMER_READY; + next = _lv_ll_get_head(&LV_GC_ROOT(_lv_timer_ll)); + while(next) { + if(!next->paused) { + uint32_t delay = lv_timer_time_remaining(next); + if(delay < time_till_next) + time_till_next = delay; + } + + next = _lv_ll_get_next(&LV_GC_ROOT(_lv_timer_ll), next); /*Find the next timer*/ + } + + busy_time += lv_tick_elaps(handler_start); + uint32_t idle_period_time = lv_tick_elaps(idle_period_start); + if(idle_period_time >= IDLE_MEAS_PERIOD) { + idle_last = (busy_time * 100) / idle_period_time; /*Calculate the busy percentage*/ + idle_last = idle_last > 100 ? 0 : 100 - idle_last; /*But we need idle time*/ + busy_time = 0; + idle_period_start = lv_tick_get(); + } + + already_running = false; /*Release the mutex*/ + + TIMER_TRACE("finished (%d ms until the next timer call)", time_till_next); + return time_till_next; +} + +/** + * Create an "empty" timer. It needs to initialized with at least + * `lv_timer_set_cb` and `lv_timer_set_period` + * @return pointer to the created timer + */ +lv_timer_t * lv_timer_create_basic(void) +{ + return lv_timer_create(NULL, DEF_PERIOD, NULL); +} + +/** + * Create a new lv_timer + * @param timer_xcb a callback which is the timer itself. It will be called periodically. + * (the 'x' in the argument name indicates that it's not a fully generic function because it not follows + * the `func_name(object, callback, ...)` convention) + * @param period call period in ms unit + * @param user_data custom parameter + * @return pointer to the new timer + */ +lv_timer_t * lv_timer_create(lv_timer_cb_t timer_xcb, uint32_t period, void * user_data) +{ + lv_timer_t * new_timer = NULL; + + new_timer = _lv_ll_ins_head(&LV_GC_ROOT(_lv_timer_ll)); + LV_ASSERT_MALLOC(new_timer); + if(new_timer == NULL) return NULL; + + new_timer->period = period; + new_timer->timer_cb = timer_xcb; + new_timer->repeat_count = -1; + new_timer->paused = 0; + new_timer->last_run = lv_tick_get(); + new_timer->user_data = user_data; + + timer_created = true; + + return new_timer; +} + +/** + * Set the callback the timer (the function to call periodically) + * @param timer pointer to a timer + * @param timer_cb the function to call periodically + */ +void lv_timer_set_cb(lv_timer_t * timer, lv_timer_cb_t timer_cb) +{ + timer->timer_cb = timer_cb; +} + +/** + * Delete a lv_timer + * @param timer pointer to timer created by timer + */ +void lv_timer_del(lv_timer_t * timer) +{ + _lv_ll_remove(&LV_GC_ROOT(_lv_timer_ll), timer); + timer_deleted = true; + + lv_mem_free(timer); +} + +/** + * Pause/resume a timer. + * @param timer pointer to an lv_timer + */ +void lv_timer_pause(lv_timer_t * timer) +{ + timer->paused = true; +} + +void lv_timer_resume(lv_timer_t * timer) +{ + timer->paused = false; +} + +/** + * Set new period for a lv_timer + * @param timer pointer to a lv_timer + * @param period the new period + */ +void lv_timer_set_period(lv_timer_t * timer, uint32_t period) +{ + timer->period = period; +} + +/** + * Make a lv_timer ready. It will not wait its period. + * @param timer pointer to a lv_timer. + */ +void lv_timer_ready(lv_timer_t * timer) +{ + timer->last_run = lv_tick_get() - timer->period - 1; +} + +/** + * Set the number of times a timer will repeat. + * @param timer pointer to a lv_timer. + * @param repeat_count -1 : infinity; 0 : stop ; n >0: residual times + */ +void lv_timer_set_repeat_count(lv_timer_t * timer, int32_t repeat_count) +{ + timer->repeat_count = repeat_count; +} + +/** + * Reset a lv_timer. + * It will be called the previously set period milliseconds later. + * @param timer pointer to a lv_timer. + */ +void lv_timer_reset(lv_timer_t * timer) +{ + timer->last_run = lv_tick_get(); +} + +/** + * Enable or disable the whole lv_timer handling + * @param en true: lv_timer handling is running, false: lv_timer handling is suspended + */ +void lv_timer_enable(bool en) +{ + lv_timer_run = en; +} + +/** + * Get idle percentage + * @return the lv_timer idle in percentage + */ +uint8_t lv_timer_get_idle(void) +{ + return idle_last; +} + +/** + * Iterate through the timers + * @param timer NULL to start iteration or the previous return value to get the next timer + * @return the next timer or NULL if there is no more timer + */ +lv_timer_t * lv_timer_get_next(lv_timer_t * timer) +{ + if(timer == NULL) return _lv_ll_get_head(&LV_GC_ROOT(_lv_timer_ll)); + else return _lv_ll_get_next(&LV_GC_ROOT(_lv_timer_ll), timer); +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +/** + * Execute timer if its remaining time is zero + * @param timer pointer to lv_timer + * @return true: execute, false: not executed + */ +static bool lv_timer_exec(lv_timer_t * timer) +{ + if(timer->paused) return false; + + bool exec = false; + if(lv_timer_time_remaining(timer) == 0) { + /* Decrement the repeat count before executing the timer_cb. + * If any timer is deleted `if(timer->repeat_count == 0)` is not executed below + * but at least the repeat count is zero and the timer can be deleted in the next round*/ + int32_t original_repeat_count = timer->repeat_count; + if(timer->repeat_count > 0) timer->repeat_count--; + timer->last_run = lv_tick_get(); + TIMER_TRACE("calling timer callback: %p", *((void **)&timer->timer_cb)); + if(timer->timer_cb && original_repeat_count != 0) timer->timer_cb(timer); + TIMER_TRACE("timer callback %p finished", *((void **)&timer->timer_cb)); + LV_ASSERT_MEM_INTEGRITY(); + exec = true; + } + + if(timer_deleted == false) { /*The timer might be deleted by itself as well*/ + if(timer->repeat_count == 0) { /*The repeat count is over, delete the timer*/ + TIMER_TRACE("deleting timer with %p callback because the repeat count is over", *((void **)&timer->timer_cb)); + lv_timer_del(timer); + } + } + + return exec; +} + +/** + * Find out how much time remains before a timer must be run. + * @param timer pointer to lv_timer + * @return the time remaining, or 0 if it needs to be run again + */ +static uint32_t lv_timer_time_remaining(lv_timer_t * timer) +{ + /*Check if at least 'period' time elapsed*/ + uint32_t elp = lv_tick_elaps(timer->last_run); + if(elp >= timer->period) + return 0; + return timer->period - elp; +} diff --git a/lib/lvgl/src/misc/lv_timer.h b/lib/lvgl/src/misc/lv_timer.h new file mode 100644 index 00000000..a9a8e50d --- /dev/null +++ b/lib/lvgl/src/misc/lv_timer.h @@ -0,0 +1,183 @@ +/** + * @file lv_timer.h + */ + +#ifndef LV_TIMER_H +#define LV_TIMER_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ +#include "../lv_conf_internal.h" +#include "../hal/lv_hal_tick.h" + +#include +#include + +/********************* + * DEFINES + *********************/ +#ifndef LV_ATTRIBUTE_TIMER_HANDLER +#define LV_ATTRIBUTE_TIMER_HANDLER +#endif + +#define LV_NO_TIMER_READY 0xFFFFFFFF + +/********************** + * TYPEDEFS + **********************/ + +struct _lv_timer_t; + +/** + * Timers execute this type of functions. + */ +typedef void (*lv_timer_cb_t)(struct _lv_timer_t *); + +/** + * Descriptor of a lv_timer + */ +typedef struct _lv_timer_t { + uint32_t period; /**< How often the timer should run*/ + uint32_t last_run; /**< Last time the timer ran*/ + lv_timer_cb_t timer_cb; /**< Timer function*/ + void * user_data; /**< Custom user data*/ + int32_t repeat_count; /**< 1: One time; -1 : infinity; n>0: residual times*/ + uint32_t paused : 1; +} lv_timer_t; + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +/** + * Init the lv_timer module + */ +void _lv_timer_core_init(void); + +//! @cond Doxygen_Suppress + +/** + * Call it periodically to handle lv_timers. + * @return time till it needs to be run next (in ms) + */ +LV_ATTRIBUTE_TIMER_HANDLER uint32_t lv_timer_handler(void); + +//! @endcond + +/** + * Call it in the super-loop of main() or threads. It will run lv_timer_handler() + * with a given period in ms. You can use it with sleep or delay in OS environment. + * This function is used to simplify the porting. + * @param __ms the period for running lv_timer_handler() + */ +static inline LV_ATTRIBUTE_TIMER_HANDLER uint32_t lv_timer_handler_run_in_period(uint32_t ms) +{ + static uint32_t last_tick = 0; + uint32_t curr_tick = lv_tick_get(); + + if((curr_tick - last_tick) >= (ms)) { + last_tick = curr_tick; + return lv_timer_handler(); + } + return 1; +} + +/** + * Create an "empty" timer. It needs to initialized with at least + * `lv_timer_set_cb` and `lv_timer_set_period` + * @return pointer to the created timer + */ +lv_timer_t * lv_timer_create_basic(void); + +/** + * Create a new lv_timer + * @param timer_xcb a callback to call periodically. + * (the 'x' in the argument name indicates that it's not a fully generic function because it not follows + * the `func_name(object, callback, ...)` convention) + * @param period call period in ms unit + * @param user_data custom parameter + * @return pointer to the new timer + */ +lv_timer_t * lv_timer_create(lv_timer_cb_t timer_xcb, uint32_t period, void * user_data); + +/** + * Delete a lv_timer + * @param timer pointer to an lv_timer + */ +void lv_timer_del(lv_timer_t * timer); + +/** + * Pause/resume a timer. + * @param timer pointer to an lv_timer + */ +void lv_timer_pause(lv_timer_t * timer); + +void lv_timer_resume(lv_timer_t * timer); + +/** + * Set the callback the timer (the function to call periodically) + * @param timer pointer to a timer + * @param timer_cb the function to call periodically + */ +void lv_timer_set_cb(lv_timer_t * timer, lv_timer_cb_t timer_cb); + +/** + * Set new period for a lv_timer + * @param timer pointer to a lv_timer + * @param period the new period + */ +void lv_timer_set_period(lv_timer_t * timer, uint32_t period); + +/** + * Make a lv_timer ready. It will not wait its period. + * @param timer pointer to a lv_timer. + */ +void lv_timer_ready(lv_timer_t * timer); + +/** + * Set the number of times a timer will repeat. + * @param timer pointer to a lv_timer. + * @param repeat_count -1 : infinity; 0 : stop ; n>0: residual times + */ +void lv_timer_set_repeat_count(lv_timer_t * timer, int32_t repeat_count); + +/** + * Reset a lv_timer. + * It will be called the previously set period milliseconds later. + * @param timer pointer to a lv_timer. + */ +void lv_timer_reset(lv_timer_t * timer); + +/** + * Enable or disable the whole lv_timer handling + * @param en true: lv_timer handling is running, false: lv_timer handling is suspended + */ +void lv_timer_enable(bool en); + +/** + * Get idle percentage + * @return the lv_timer idle in percentage + */ +uint8_t lv_timer_get_idle(void); + +/** + * Iterate through the timers + * @param timer NULL to start iteration or the previous return value to get the next timer + * @return the next timer or NULL if there is no more timer + */ +lv_timer_t * lv_timer_get_next(lv_timer_t * timer); + +/********************** + * MACROS + **********************/ + +#ifdef __cplusplus +} /*extern "C"*/ +#endif + +#endif diff --git a/lib/lvgl/src/misc/lv_tlsf.c b/lib/lvgl/src/misc/lv_tlsf.c new file mode 100644 index 00000000..27e0a46c --- /dev/null +++ b/lib/lvgl/src/misc/lv_tlsf.c @@ -0,0 +1,1246 @@ +#include "../lv_conf_internal.h" +#if LV_MEM_CUSTOM == 0 + +#include +#include "lv_tlsf.h" +#include "lv_mem.h" +#include "lv_log.h" +#include "lv_assert.h" + +#undef printf +#define printf LV_LOG_ERROR + +#define TLSF_MAX_POOL_SIZE LV_MEM_SIZE + +#if !defined(_DEBUG) + #define _DEBUG 0 +#endif + +#if defined(__cplusplus) + #define tlsf_decl inline +#else + #define tlsf_decl static +#endif + +/* +** Architecture-specific bit manipulation routines. +** +** TLSF achieves O(1) cost for malloc and free operations by limiting +** the search for a free block to a free list of guaranteed size +** adequate to fulfill the request, combined with efficient free list +** queries using bitmasks and architecture-specific bit-manipulation +** routines. +** +** Most modern processors provide instructions to count leading zeroes +** in a word, find the lowest and highest set bit, etc. These +** specific implementations will be used when available, falling back +** to a reasonably efficient generic implementation. +** +** NOTE: TLSF spec relies on ffs/fls returning value 0..31. +** ffs/fls return 1-32 by default, returning 0 for error. +*/ + +/* +** Detect whether or not we are building for a 32- or 64-bit (LP/LLP) +** architecture. There is no reliable portable method at compile-time. +*/ +#if defined (__alpha__) || defined (__ia64__) || defined (__x86_64__) \ + || defined (_WIN64) || defined (__LP64__) || defined (__LLP64__) + #define TLSF_64BIT +#endif + +/* +** Returns one plus the index of the most significant 1-bit of n, +** or if n is zero, returns zero. +*/ +#ifdef TLSF_64BIT + #define TLSF_FLS(n) ((n) & 0xffffffff00000000ull ? 32 + TLSF_FLS32((size_t)(n) >> 32) : TLSF_FLS32(n)) +#else + #define TLSF_FLS(n) TLSF_FLS32(n) +#endif + +#define TLSF_FLS32(n) ((n) & 0xffff0000 ? 16 + TLSF_FLS16((n) >> 16) : TLSF_FLS16(n)) +#define TLSF_FLS16(n) ((n) & 0xff00 ? 8 + TLSF_FLS8 ((n) >> 8) : TLSF_FLS8 (n)) +#define TLSF_FLS8(n) ((n) & 0xf0 ? 4 + TLSF_FLS4 ((n) >> 4) : TLSF_FLS4 (n)) +#define TLSF_FLS4(n) ((n) & 0xc ? 2 + TLSF_FLS2 ((n) >> 2) : TLSF_FLS2 (n)) +#define TLSF_FLS2(n) ((n) & 0x2 ? 1 + TLSF_FLS1 ((n) >> 1) : TLSF_FLS1 (n)) +#define TLSF_FLS1(n) ((n) & 0x1 ? 1 : 0) + +/* +** Returns round up value of log2(n). +** Note: it is used at compile time. +*/ +#define TLSF_LOG2_CEIL(n) ((n) & (n - 1) ? TLSF_FLS(n) : TLSF_FLS(n) - 1) + +/* +** gcc 3.4 and above have builtin support, specialized for architecture. +** Some compilers masquerade as gcc; patchlevel test filters them out. +*/ +#if defined (__GNUC__) && (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4)) \ + && defined (__GNUC_PATCHLEVEL__) + +#if defined (__SNC__) +/* SNC for Playstation 3. */ + +tlsf_decl int tlsf_ffs(unsigned int word) +{ + const unsigned int reverse = word & (~word + 1); + const int bit = 32 - __builtin_clz(reverse); + return bit - 1; +} + +#else + +tlsf_decl int tlsf_ffs(unsigned int word) +{ + return __builtin_ffs(word) - 1; +} + +#endif + +tlsf_decl int tlsf_fls(unsigned int word) +{ + const int bit = word ? 32 - __builtin_clz(word) : 0; + return bit - 1; +} + +#elif defined (_MSC_VER) && (_MSC_VER >= 1400) && (defined (_M_IX86) || defined (_M_X64)) +/* Microsoft Visual C++ support on x86/X64 architectures. */ + +#include + +#pragma intrinsic(_BitScanReverse) +#pragma intrinsic(_BitScanForward) + +tlsf_decl int tlsf_fls(unsigned int word) +{ + unsigned long index; + return _BitScanReverse(&index, word) ? index : -1; +} + +tlsf_decl int tlsf_ffs(unsigned int word) +{ + unsigned long index; + return _BitScanForward(&index, word) ? index : -1; +} + +#elif defined (_MSC_VER) && defined (_M_PPC) +/* Microsoft Visual C++ support on PowerPC architectures. */ + +#include + +tlsf_decl int tlsf_fls(unsigned int word) +{ + const int bit = 32 - _CountLeadingZeros(word); + return bit - 1; +} + +tlsf_decl int tlsf_ffs(unsigned int word) +{ + const unsigned int reverse = word & (~word + 1); + const int bit = 32 - _CountLeadingZeros(reverse); + return bit - 1; +} + +#elif defined (__ARMCC_VERSION) +/* RealView Compilation Tools for ARM */ + +tlsf_decl int tlsf_ffs(unsigned int word) +{ + const unsigned int reverse = word & (~word + 1); + const int bit = 32 - __clz(reverse); + return bit - 1; +} + +tlsf_decl int tlsf_fls(unsigned int word) +{ + const int bit = word ? 32 - __clz(word) : 0; + return bit - 1; +} + +#elif defined (__ghs__) +/* Green Hills support for PowerPC */ + +#include + +tlsf_decl int tlsf_ffs(unsigned int word) +{ + const unsigned int reverse = word & (~word + 1); + const int bit = 32 - __CLZ32(reverse); + return bit - 1; +} + +tlsf_decl int tlsf_fls(unsigned int word) +{ + const int bit = word ? 32 - __CLZ32(word) : 0; + return bit - 1; +} + +#else +/* Fall back to generic implementation. */ + +/* Implement ffs in terms of fls. */ +tlsf_decl int tlsf_ffs(unsigned int word) +{ + const unsigned int reverse = word & (~word + 1); + return TLSF_FLS32(reverse) - 1; +} + +tlsf_decl int tlsf_fls(unsigned int word) +{ + return TLSF_FLS32(word) - 1; +} + +#endif + +/* Possibly 64-bit version of tlsf_fls. */ +#if defined (TLSF_64BIT) +tlsf_decl int tlsf_fls_sizet(size_t size) +{ + int high = (int)(size >> 32); + int bits = 0; + if(high) { + bits = 32 + tlsf_fls(high); + } + else { + bits = tlsf_fls((int)size & 0xffffffff); + + } + return bits; +} +#else +#define tlsf_fls_sizet tlsf_fls +#endif + +#undef tlsf_decl + +/* +** Constants. +*/ + +/* Public constants: may be modified. */ +enum tlsf_public { + /* log2 of number of linear subdivisions of block sizes. Larger + ** values require more memory in the control structure. Values of + ** 4 or 5 are typical. + */ + SL_INDEX_COUNT_LOG2 = 5, +}; + +/* Private constants: do not modify. */ +enum tlsf_private { +#if defined (TLSF_64BIT) + /* All allocation sizes and addresses are aligned to 8 bytes. */ + ALIGN_SIZE_LOG2 = 3, +#else + /* All allocation sizes and addresses are aligned to 4 bytes. */ + ALIGN_SIZE_LOG2 = 2, +#endif + ALIGN_SIZE = (1 << ALIGN_SIZE_LOG2), + + /* + ** We support allocations of sizes up to (1 << FL_INDEX_MAX) bits. + ** However, because we linearly subdivide the second-level lists, and + ** our minimum size granularity is 4 bytes, it doesn't make sense to + ** create first-level lists for sizes smaller than SL_INDEX_COUNT * 4, + ** or (1 << (SL_INDEX_COUNT_LOG2 + 2)) bytes, as there we will be + ** trying to split size ranges into more slots than we have available. + ** Instead, we calculate the minimum threshold size, and place all + ** blocks below that size into the 0th first-level list. + */ + +#if defined (TLSF_MAX_POOL_SIZE) + FL_INDEX_MAX = TLSF_LOG2_CEIL(TLSF_MAX_POOL_SIZE), +#elif defined (TLSF_64BIT) + /* + ** TODO: We can increase this to support larger sizes, at the expense + ** of more overhead in the TLSF structure. + */ + FL_INDEX_MAX = 32, +#else + FL_INDEX_MAX = 30, +#endif + SL_INDEX_COUNT = (1 << SL_INDEX_COUNT_LOG2), + FL_INDEX_SHIFT = (SL_INDEX_COUNT_LOG2 + ALIGN_SIZE_LOG2), + FL_INDEX_COUNT = (FL_INDEX_MAX - FL_INDEX_SHIFT + 1), + + SMALL_BLOCK_SIZE = (1 << FL_INDEX_SHIFT), +}; + +/* +** Cast and min/max macros. +*/ + +#define tlsf_cast(t, exp) ((t) (exp)) +#define tlsf_min(a, b) ((a) < (b) ? (a) : (b)) +#define tlsf_max(a, b) ((a) > (b) ? (a) : (b)) + +/* +** Set assert macro, if it has not been provided by the user. +*/ +#define tlsf_assert LV_ASSERT + +#if !defined (tlsf_assert) + #define tlsf_assert assert +#endif + +/* +** Static assertion mechanism. +*/ + +#define _tlsf_glue2(x, y) x ## y +#define _tlsf_glue(x, y) _tlsf_glue2(x, y) +#define tlsf_static_assert(exp) \ + typedef char _tlsf_glue(static_assert, __LINE__) [(exp) ? 1 : -1] + +/* This code has been tested on 32- and 64-bit (LP/LLP) architectures. */ +tlsf_static_assert(sizeof(int) * CHAR_BIT == 32); +tlsf_static_assert(sizeof(size_t) * CHAR_BIT >= 32); +tlsf_static_assert(sizeof(size_t) * CHAR_BIT <= 64); + +/* SL_INDEX_COUNT must be <= number of bits in sl_bitmap's storage type. */ +tlsf_static_assert(sizeof(unsigned int) * CHAR_BIT >= SL_INDEX_COUNT); + +/* Ensure we've properly tuned our sizes. */ +tlsf_static_assert(ALIGN_SIZE == SMALL_BLOCK_SIZE / SL_INDEX_COUNT); + +/* +** Data structures and associated constants. +*/ + +/* +** Block header structure. +** +** There are several implementation subtleties involved: +** - The prev_phys_block field is only valid if the previous block is free. +** - The prev_phys_block field is actually stored at the end of the +** previous block. It appears at the beginning of this structure only to +** simplify the implementation. +** - The next_free / prev_free fields are only valid if the block is free. +*/ +typedef struct block_header_t { + /* Points to the previous physical block. */ + struct block_header_t * prev_phys_block; + + /* The size of this block, excluding the block header. */ + size_t size; + + /* Next and previous free blocks. */ + struct block_header_t * next_free; + struct block_header_t * prev_free; +} block_header_t; + +/* +** Since block sizes are always at least a multiple of 4, the two least +** significant bits of the size field are used to store the block status: +** - bit 0: whether block is busy or free +** - bit 1: whether previous block is busy or free +*/ +static const size_t block_header_free_bit = 1 << 0; +static const size_t block_header_prev_free_bit = 1 << 1; + +/* +** The size of the block header exposed to used blocks is the size field. +** The prev_phys_block field is stored *inside* the previous free block. +*/ +static const size_t block_header_overhead = sizeof(size_t); + +/* User data starts directly after the size field in a used block. */ +static const size_t block_start_offset = + offsetof(block_header_t, size) + sizeof(size_t); + +/* +** A free block must be large enough to store its header minus the size of +** the prev_phys_block field, and no larger than the number of addressable +** bits for FL_INDEX. +*/ +static const size_t block_size_min = + sizeof(block_header_t) - sizeof(block_header_t *); +static const size_t block_size_max = tlsf_cast(size_t, 1) << FL_INDEX_MAX; + + +/* The TLSF control structure. */ +typedef struct control_t { + /* Empty lists point at this block to indicate they are free. */ + block_header_t block_null; + + /* Bitmaps for free lists. */ + unsigned int fl_bitmap; + unsigned int sl_bitmap[FL_INDEX_COUNT]; + + /* Head of free lists. */ + block_header_t * blocks[FL_INDEX_COUNT][SL_INDEX_COUNT]; +} control_t; + +/* A type used for casting when doing pointer arithmetic. */ +typedef ptrdiff_t tlsfptr_t; + +/* +** block_header_t member functions. +*/ + +static size_t block_size(const block_header_t * block) +{ + return block->size & ~(block_header_free_bit | block_header_prev_free_bit); +} + +static void block_set_size(block_header_t * block, size_t size) +{ + const size_t oldsize = block->size; + block->size = size | (oldsize & (block_header_free_bit | block_header_prev_free_bit)); +} + +static int block_is_last(const block_header_t * block) +{ + return block_size(block) == 0; +} + +static int block_is_free(const block_header_t * block) +{ + return tlsf_cast(int, block->size & block_header_free_bit); +} + +static void block_set_free(block_header_t * block) +{ + block->size |= block_header_free_bit; +} + +static void block_set_used(block_header_t * block) +{ + block->size &= ~block_header_free_bit; +} + +static int block_is_prev_free(const block_header_t * block) +{ + return tlsf_cast(int, block->size & block_header_prev_free_bit); +} + +static void block_set_prev_free(block_header_t * block) +{ + block->size |= block_header_prev_free_bit; +} + +static void block_set_prev_used(block_header_t * block) +{ + block->size &= ~block_header_prev_free_bit; +} + +static block_header_t * block_from_ptr(const void * ptr) +{ + return tlsf_cast(block_header_t *, + tlsf_cast(unsigned char *, ptr) - block_start_offset); +} + +static void * block_to_ptr(const block_header_t * block) +{ + return tlsf_cast(void *, + tlsf_cast(unsigned char *, block) + block_start_offset); +} + +/* Return location of next block after block of given size. */ +static block_header_t * offset_to_block(const void * ptr, size_t size) +{ + return tlsf_cast(block_header_t *, tlsf_cast(tlsfptr_t, ptr) + size); +} + +/* Return location of previous block. */ +static block_header_t * block_prev(const block_header_t * block) +{ + tlsf_assert(block_is_prev_free(block) && "previous block must be free"); + return block->prev_phys_block; +} + +/* Return location of next existing block. */ +static block_header_t * block_next(const block_header_t * block) +{ + block_header_t * next = offset_to_block(block_to_ptr(block), + block_size(block) - block_header_overhead); + tlsf_assert(!block_is_last(block)); + return next; +} + +/* Link a new block with its physical neighbor, return the neighbor. */ +static block_header_t * block_link_next(block_header_t * block) +{ + block_header_t * next = block_next(block); + next->prev_phys_block = block; + return next; +} + +static void block_mark_as_free(block_header_t * block) +{ + /* Link the block to the next block, first. */ + block_header_t * next = block_link_next(block); + block_set_prev_free(next); + block_set_free(block); +} + +static void block_mark_as_used(block_header_t * block) +{ + block_header_t * next = block_next(block); + block_set_prev_used(next); + block_set_used(block); +} + +static size_t align_up(size_t x, size_t align) +{ + tlsf_assert(0 == (align & (align - 1)) && "must align to a power of two"); + return (x + (align - 1)) & ~(align - 1); +} + +static size_t align_down(size_t x, size_t align) +{ + tlsf_assert(0 == (align & (align - 1)) && "must align to a power of two"); + return x - (x & (align - 1)); +} + +static void * align_ptr(const void * ptr, size_t align) +{ + const tlsfptr_t aligned = + (tlsf_cast(tlsfptr_t, ptr) + (align - 1)) & ~(align - 1); + tlsf_assert(0 == (align & (align - 1)) && "must align to a power of two"); + return tlsf_cast(void *, aligned); +} + +/* +** Adjust an allocation size to be aligned to word size, and no smaller +** than internal minimum. +*/ +static size_t adjust_request_size(size_t size, size_t align) +{ + size_t adjust = 0; + if(size) { + const size_t aligned = align_up(size, align); + + /* aligned sized must not exceed block_size_max or we'll go out of bounds on sl_bitmap */ + if(aligned < block_size_max) { + adjust = tlsf_max(aligned, block_size_min); + } + } + return adjust; +} + +/* +** TLSF utility functions. In most cases, these are direct translations of +** the documentation found in the white paper. +*/ + +static void mapping_insert(size_t size, int * fli, int * sli) +{ + int fl, sl; + if(size < SMALL_BLOCK_SIZE) { + /* Store small blocks in first list. */ + fl = 0; + sl = tlsf_cast(int, size) / (SMALL_BLOCK_SIZE / SL_INDEX_COUNT); + } + else { + fl = tlsf_fls_sizet(size); + sl = tlsf_cast(int, size >> (fl - SL_INDEX_COUNT_LOG2)) ^ (1 << SL_INDEX_COUNT_LOG2); + fl -= (FL_INDEX_SHIFT - 1); + } + *fli = fl; + *sli = sl; +} + +/* This version rounds up to the next block size (for allocations) */ +static void mapping_search(size_t size, int * fli, int * sli) +{ + if(size >= SMALL_BLOCK_SIZE) { + const size_t round = (1 << (tlsf_fls_sizet(size) - SL_INDEX_COUNT_LOG2)) - 1; + size += round; + } + mapping_insert(size, fli, sli); +} + +static block_header_t * search_suitable_block(control_t * control, int * fli, int * sli) +{ + int fl = *fli; + int sl = *sli; + + /* + ** First, search for a block in the list associated with the given + ** fl/sl index. + */ + unsigned int sl_map = control->sl_bitmap[fl] & (~0U << sl); + if(!sl_map) { + /* No block exists. Search in the next largest first-level list. */ + const unsigned int fl_map = control->fl_bitmap & (~0U << (fl + 1)); + if(!fl_map) { + /* No free blocks available, memory has been exhausted. */ + return 0; + } + + fl = tlsf_ffs(fl_map); + *fli = fl; + sl_map = control->sl_bitmap[fl]; + } + tlsf_assert(sl_map && "internal error - second level bitmap is null"); + sl = tlsf_ffs(sl_map); + *sli = sl; + + /* Return the first block in the free list. */ + return control->blocks[fl][sl]; +} + +/* Remove a free block from the free list.*/ +static void remove_free_block(control_t * control, block_header_t * block, int fl, int sl) +{ + block_header_t * prev = block->prev_free; + block_header_t * next = block->next_free; + tlsf_assert(prev && "prev_free field can not be null"); + tlsf_assert(next && "next_free field can not be null"); + next->prev_free = prev; + prev->next_free = next; + + /* If this block is the head of the free list, set new head. */ + if(control->blocks[fl][sl] == block) { + control->blocks[fl][sl] = next; + + /* If the new head is null, clear the bitmap. */ + if(next == &control->block_null) { + control->sl_bitmap[fl] &= ~(1U << sl); + + /* If the second bitmap is now empty, clear the fl bitmap. */ + if(!control->sl_bitmap[fl]) { + control->fl_bitmap &= ~(1U << fl); + } + } + } +} + +/* Insert a free block into the free block list. */ +static void insert_free_block(control_t * control, block_header_t * block, int fl, int sl) +{ + block_header_t * current = control->blocks[fl][sl]; + tlsf_assert(current && "free list cannot have a null entry"); + tlsf_assert(block && "cannot insert a null entry into the free list"); + block->next_free = current; + block->prev_free = &control->block_null; + current->prev_free = block; + + tlsf_assert(block_to_ptr(block) == align_ptr(block_to_ptr(block), ALIGN_SIZE) + && "block not aligned properly"); + /* + ** Insert the new block at the head of the list, and mark the first- + ** and second-level bitmaps appropriately. + */ + control->blocks[fl][sl] = block; + control->fl_bitmap |= (1U << fl); + control->sl_bitmap[fl] |= (1U << sl); +} + +/* Remove a given block from the free list. */ +static void block_remove(control_t * control, block_header_t * block) +{ + int fl, sl; + mapping_insert(block_size(block), &fl, &sl); + remove_free_block(control, block, fl, sl); +} + +/* Insert a given block into the free list. */ +static void block_insert(control_t * control, block_header_t * block) +{ + int fl, sl; + mapping_insert(block_size(block), &fl, &sl); + insert_free_block(control, block, fl, sl); +} + +static int block_can_split(block_header_t * block, size_t size) +{ + return block_size(block) >= sizeof(block_header_t) + size; +} + +/* Split a block into two, the second of which is free. */ +static block_header_t * block_split(block_header_t * block, size_t size) +{ + /* Calculate the amount of space left in the remaining block. */ + block_header_t * remaining = + offset_to_block(block_to_ptr(block), size - block_header_overhead); + + const size_t remain_size = block_size(block) - (size + block_header_overhead); + + tlsf_assert(block_to_ptr(remaining) == align_ptr(block_to_ptr(remaining), ALIGN_SIZE) + && "remaining block not aligned properly"); + + tlsf_assert(block_size(block) == remain_size + size + block_header_overhead); + block_set_size(remaining, remain_size); + tlsf_assert(block_size(remaining) >= block_size_min && "block split with invalid size"); + + block_set_size(block, size); + block_mark_as_free(remaining); + + return remaining; +} + +/* Absorb a free block's storage into an adjacent previous free block. */ +static block_header_t * block_absorb(block_header_t * prev, block_header_t * block) +{ + tlsf_assert(!block_is_last(prev) && "previous block can't be last"); + /* Note: Leaves flags untouched. */ + prev->size += block_size(block) + block_header_overhead; + block_link_next(prev); + return prev; +} + +/* Merge a just-freed block with an adjacent previous free block. */ +static block_header_t * block_merge_prev(control_t * control, block_header_t * block) +{ + if(block_is_prev_free(block)) { + block_header_t * prev = block_prev(block); + tlsf_assert(prev && "prev physical block can't be null"); + tlsf_assert(block_is_free(prev) && "prev block is not free though marked as such"); + block_remove(control, prev); + block = block_absorb(prev, block); + } + + return block; +} + +/* Merge a just-freed block with an adjacent free block. */ +static block_header_t * block_merge_next(control_t * control, block_header_t * block) +{ + block_header_t * next = block_next(block); + tlsf_assert(next && "next physical block can't be null"); + + if(block_is_free(next)) { + tlsf_assert(!block_is_last(block) && "previous block can't be last"); + block_remove(control, next); + block = block_absorb(block, next); + } + + return block; +} + +/* Trim any trailing block space off the end of a block, return to pool. */ +static void block_trim_free(control_t * control, block_header_t * block, size_t size) +{ + tlsf_assert(block_is_free(block) && "block must be free"); + if(block_can_split(block, size)) { + block_header_t * remaining_block = block_split(block, size); + block_link_next(block); + block_set_prev_free(remaining_block); + block_insert(control, remaining_block); + } +} + +/* Trim any trailing block space off the end of a used block, return to pool. */ +static void block_trim_used(control_t * control, block_header_t * block, size_t size) +{ + tlsf_assert(!block_is_free(block) && "block must be used"); + if(block_can_split(block, size)) { + /* If the next block is free, we must coalesce. */ + block_header_t * remaining_block = block_split(block, size); + block_set_prev_used(remaining_block); + + remaining_block = block_merge_next(control, remaining_block); + block_insert(control, remaining_block); + } +} + +static block_header_t * block_trim_free_leading(control_t * control, block_header_t * block, size_t size) +{ + block_header_t * remaining_block = block; + if(block_can_split(block, size)) { + /* We want the 2nd block. */ + remaining_block = block_split(block, size - block_header_overhead); + block_set_prev_free(remaining_block); + + block_link_next(block); + block_insert(control, block); + } + + return remaining_block; +} + +static block_header_t * block_locate_free(control_t * control, size_t size) +{ + int fl = 0, sl = 0; + block_header_t * block = 0; + + if(size) { + mapping_search(size, &fl, &sl); + + /* + ** mapping_search can futz with the size, so for excessively large sizes it can sometimes wind up + ** with indices that are off the end of the block array. + ** So, we protect against that here, since this is the only callsite of mapping_search. + ** Note that we don't need to check sl, since it comes from a modulo operation that guarantees it's always in range. + */ + if(fl < FL_INDEX_COUNT) { + block = search_suitable_block(control, &fl, &sl); + } + } + + if(block) { + tlsf_assert(block_size(block) >= size); + remove_free_block(control, block, fl, sl); + } + + return block; +} + +static void * block_prepare_used(control_t * control, block_header_t * block, size_t size) +{ + void * p = 0; + if(block) { + tlsf_assert(size && "size must be non-zero"); + block_trim_free(control, block, size); + block_mark_as_used(block); + p = block_to_ptr(block); + } + return p; +} + +/* Clear structure and point all empty lists at the null block. */ +static void control_constructor(control_t * control) +{ + int i, j; + + control->block_null.next_free = &control->block_null; + control->block_null.prev_free = &control->block_null; + + control->fl_bitmap = 0; + for(i = 0; i < FL_INDEX_COUNT; ++i) { + control->sl_bitmap[i] = 0; + for(j = 0; j < SL_INDEX_COUNT; ++j) { + control->blocks[i][j] = &control->block_null; + } + } +} + +/* +** Debugging utilities. +*/ + +typedef struct integrity_t { + int prev_status; + int status; +} integrity_t; + +#define tlsf_insist(x) { tlsf_assert(x); if (!(x)) { status--; } } + +static void integrity_walker(void * ptr, size_t size, int used, void * user) +{ + block_header_t * block = block_from_ptr(ptr); + integrity_t * integ = tlsf_cast(integrity_t *, user); + const int this_prev_status = block_is_prev_free(block) ? 1 : 0; + const int this_status = block_is_free(block) ? 1 : 0; + const size_t this_block_size = block_size(block); + + int status = 0; + LV_UNUSED(used); + tlsf_insist(integ->prev_status == this_prev_status && "prev status incorrect"); + tlsf_insist(size == this_block_size && "block size incorrect"); + + integ->prev_status = this_status; + integ->status += status; +} + +int lv_tlsf_check(lv_tlsf_t tlsf) +{ + int i, j; + + control_t * control = tlsf_cast(control_t *, tlsf); + int status = 0; + + /* Check that the free lists and bitmaps are accurate. */ + for(i = 0; i < FL_INDEX_COUNT; ++i) { + for(j = 0; j < SL_INDEX_COUNT; ++j) { + const int fl_map = control->fl_bitmap & (1U << i); + const int sl_list = control->sl_bitmap[i]; + const int sl_map = sl_list & (1U << j); + const block_header_t * block = control->blocks[i][j]; + + /* Check that first- and second-level lists agree. */ + if(!fl_map) { + tlsf_insist(!sl_map && "second-level map must be null"); + } + + if(!sl_map) { + tlsf_insist(block == &control->block_null && "block list must be null"); + continue; + } + + /* Check that there is at least one free block. */ + tlsf_insist(sl_list && "no free blocks in second-level map"); + tlsf_insist(block != &control->block_null && "block should not be null"); + + while(block != &control->block_null) { + int fli, sli; + tlsf_insist(block_is_free(block) && "block should be free"); + tlsf_insist(!block_is_prev_free(block) && "blocks should have coalesced"); + tlsf_insist(!block_is_free(block_next(block)) && "blocks should have coalesced"); + tlsf_insist(block_is_prev_free(block_next(block)) && "block should be free"); + tlsf_insist(block_size(block) >= block_size_min && "block not minimum size"); + + mapping_insert(block_size(block), &fli, &sli); + tlsf_insist(fli == i && sli == j && "block size indexed in wrong list"); + block = block->next_free; + } + } + } + + return status; +} + +#undef tlsf_insist + +static void default_walker(void * ptr, size_t size, int used, void * user) +{ + LV_UNUSED(user); + printf("\t%p %s size: %x (%p)\n", ptr, used ? "used" : "free", (unsigned int)size, (void *)block_from_ptr(ptr)); +} + +void lv_tlsf_walk_pool(lv_pool_t pool, lv_tlsf_walker walker, void * user) +{ + lv_tlsf_walker pool_walker = walker ? walker : default_walker; + block_header_t * block = + offset_to_block(pool, -(int)block_header_overhead); + + while(block && !block_is_last(block)) { + pool_walker( + block_to_ptr(block), + block_size(block), + !block_is_free(block), + user); + block = block_next(block); + } +} + +size_t lv_tlsf_block_size(void * ptr) +{ + size_t size = 0; + if(ptr) { + const block_header_t * block = block_from_ptr(ptr); + size = block_size(block); + } + return size; +} + +int lv_tlsf_check_pool(lv_pool_t pool) +{ + /* Check that the blocks are physically correct. */ + integrity_t integ = { 0, 0 }; + lv_tlsf_walk_pool(pool, integrity_walker, &integ); + + return integ.status; +} + +/* +** Size of the TLSF structures in a given memory block passed to +** lv_tlsf_create, equal to the size of a control_t +*/ +size_t lv_tlsf_size(void) +{ + return sizeof(control_t); +} + +size_t lv_tlsf_align_size(void) +{ + return ALIGN_SIZE; +} + +size_t lv_tlsf_block_size_min(void) +{ + return block_size_min; +} + +size_t lv_tlsf_block_size_max(void) +{ + return block_size_max; +} + +/* +** Overhead of the TLSF structures in a given memory block passed to +** lv_tlsf_add_pool, equal to the overhead of a free block and the +** sentinel block. +*/ +size_t lv_tlsf_pool_overhead(void) +{ + return 2 * block_header_overhead; +} + +size_t lv_tlsf_alloc_overhead(void) +{ + return block_header_overhead; +} + +lv_pool_t lv_tlsf_add_pool(lv_tlsf_t tlsf, void * mem, size_t bytes) +{ + block_header_t * block; + block_header_t * next; + + const size_t pool_overhead = lv_tlsf_pool_overhead(); + const size_t pool_bytes = align_down(bytes - pool_overhead, ALIGN_SIZE); + + if(((ptrdiff_t)mem % ALIGN_SIZE) != 0) { + printf("lv_tlsf_add_pool: Memory must be aligned by %u bytes.\n", + (unsigned int)ALIGN_SIZE); + return 0; + } + + if(pool_bytes < block_size_min || pool_bytes > block_size_max) { +#if defined (TLSF_64BIT) + printf("lv_tlsf_add_pool: Memory size must be between 0x%x and 0x%x00 bytes.\n", + (unsigned int)(pool_overhead + block_size_min), + (unsigned int)((pool_overhead + block_size_max) / 256)); +#else + printf("lv_tlsf_add_pool: Memory size must be between %u and %u bytes.\n", + (unsigned int)(pool_overhead + block_size_min), + (unsigned int)(pool_overhead + block_size_max)); +#endif + return 0; + } + + /* + ** Create the main free block. Offset the start of the block slightly + ** so that the prev_phys_block field falls outside of the pool - + ** it will never be used. + */ + block = offset_to_block(mem, -(tlsfptr_t)block_header_overhead); + block_set_size(block, pool_bytes); + block_set_free(block); + block_set_prev_used(block); + block_insert(tlsf_cast(control_t *, tlsf), block); + + /* Split the block to create a zero-size sentinel block. */ + next = block_link_next(block); + block_set_size(next, 0); + block_set_used(next); + block_set_prev_free(next); + + return mem; +} + +void lv_tlsf_remove_pool(lv_tlsf_t tlsf, lv_pool_t pool) +{ + control_t * control = tlsf_cast(control_t *, tlsf); + block_header_t * block = offset_to_block(pool, -(int)block_header_overhead); + + int fl = 0, sl = 0; + + tlsf_assert(block_is_free(block) && "block should be free"); + tlsf_assert(!block_is_free(block_next(block)) && "next block should not be free"); + tlsf_assert(block_size(block_next(block)) == 0 && "next block size should be zero"); + + mapping_insert(block_size(block), &fl, &sl); + remove_free_block(control, block, fl, sl); +} + +/* +** TLSF main interface. +*/ + +#if _DEBUG +int test_ffs_fls() +{ + /* Verify ffs/fls work properly. */ + int rv = 0; + rv += (tlsf_ffs(0) == -1) ? 0 : 0x1; + rv += (tlsf_fls(0) == -1) ? 0 : 0x2; + rv += (tlsf_ffs(1) == 0) ? 0 : 0x4; + rv += (tlsf_fls(1) == 0) ? 0 : 0x8; + rv += (tlsf_ffs(0x80000000) == 31) ? 0 : 0x10; + rv += (tlsf_ffs(0x80008000) == 15) ? 0 : 0x20; + rv += (tlsf_fls(0x80000008) == 31) ? 0 : 0x40; + rv += (tlsf_fls(0x7FFFFFFF) == 30) ? 0 : 0x80; + +#if defined (TLSF_64BIT) + rv += (tlsf_fls_sizet(0x80000000) == 31) ? 0 : 0x100; + rv += (tlsf_fls_sizet(0x100000000) == 32) ? 0 : 0x200; + rv += (tlsf_fls_sizet(0xffffffffffffffff) == 63) ? 0 : 0x400; +#endif + + if(rv) { + printf("test_ffs_fls: %x ffs/fls tests failed.\n", rv); + } + return rv; +} +#endif + +lv_tlsf_t lv_tlsf_create(void * mem) +{ +#if _DEBUG + if(test_ffs_fls()) { + return 0; + } +#endif + + if(((tlsfptr_t)mem % ALIGN_SIZE) != 0) { + printf("lv_tlsf_create: Memory must be aligned to %u bytes.\n", + (unsigned int)ALIGN_SIZE); + return 0; + } + + control_constructor(tlsf_cast(control_t *, mem)); + + return tlsf_cast(lv_tlsf_t, mem); +} + +lv_tlsf_t lv_tlsf_create_with_pool(void * mem, size_t bytes) +{ + lv_tlsf_t tlsf = lv_tlsf_create(mem); + lv_tlsf_add_pool(tlsf, (char *)mem + lv_tlsf_size(), bytes - lv_tlsf_size()); + return tlsf; +} + +void lv_tlsf_destroy(lv_tlsf_t tlsf) +{ + /* Nothing to do. */ + LV_UNUSED(tlsf); +} + +lv_pool_t lv_tlsf_get_pool(lv_tlsf_t tlsf) +{ + return tlsf_cast(lv_pool_t, (char *)tlsf + lv_tlsf_size()); +} + +void * lv_tlsf_malloc(lv_tlsf_t tlsf, size_t size) +{ + control_t * control = tlsf_cast(control_t *, tlsf); + const size_t adjust = adjust_request_size(size, ALIGN_SIZE); + block_header_t * block = block_locate_free(control, adjust); + return block_prepare_used(control, block, adjust); +} + +void * lv_tlsf_memalign(lv_tlsf_t tlsf, size_t align, size_t size) +{ + control_t * control = tlsf_cast(control_t *, tlsf); + const size_t adjust = adjust_request_size(size, ALIGN_SIZE); + + /* + ** We must allocate an additional minimum block size bytes so that if + ** our free block will leave an alignment gap which is smaller, we can + ** trim a leading free block and release it back to the pool. We must + ** do this because the previous physical block is in use, therefore + ** the prev_phys_block field is not valid, and we can't simply adjust + ** the size of that block. + */ + const size_t gap_minimum = sizeof(block_header_t); + const size_t size_with_gap = adjust_request_size(adjust + align + gap_minimum, align); + + /* + ** If alignment is less than or equals base alignment, we're done. + ** If we requested 0 bytes, return null, as lv_tlsf_malloc(0) does. + */ + const size_t aligned_size = (adjust && align > ALIGN_SIZE) ? size_with_gap : adjust; + + block_header_t * block = block_locate_free(control, aligned_size); + + /* This can't be a static assert. */ + tlsf_assert(sizeof(block_header_t) == block_size_min + block_header_overhead); + + if(block) { + void * ptr = block_to_ptr(block); + void * aligned = align_ptr(ptr, align); + size_t gap = tlsf_cast(size_t, + tlsf_cast(tlsfptr_t, aligned) - tlsf_cast(tlsfptr_t, ptr)); + + /* If gap size is too small, offset to next aligned boundary. */ + if(gap && gap < gap_minimum) { + const size_t gap_remain = gap_minimum - gap; + const size_t offset = tlsf_max(gap_remain, align); + const void * next_aligned = tlsf_cast(void *, + tlsf_cast(tlsfptr_t, aligned) + offset); + + aligned = align_ptr(next_aligned, align); + gap = tlsf_cast(size_t, + tlsf_cast(tlsfptr_t, aligned) - tlsf_cast(tlsfptr_t, ptr)); + } + + if(gap) { + tlsf_assert(gap >= gap_minimum && "gap size too small"); + block = block_trim_free_leading(control, block, gap); + } + } + + return block_prepare_used(control, block, adjust); +} + +size_t lv_tlsf_free(lv_tlsf_t tlsf, const void * ptr) +{ + size_t size = 0; + /* Don't attempt to free a NULL pointer. */ + if(ptr) { + control_t * control = tlsf_cast(control_t *, tlsf); + block_header_t * block = block_from_ptr(ptr); + tlsf_assert(!block_is_free(block) && "block already marked as free"); + size = block->size; + block_mark_as_free(block); + block = block_merge_prev(control, block); + block = block_merge_next(control, block); + block_insert(control, block); + } + + return size; +} + +/* +** The TLSF block information provides us with enough information to +** provide a reasonably intelligent implementation of realloc, growing or +** shrinking the currently allocated block as required. +** +** This routine handles the somewhat esoteric edge cases of realloc: +** - a non-zero size with a null pointer will behave like malloc +** - a zero size with a non-null pointer will behave like free +** - a request that cannot be satisfied will leave the original buffer +** untouched +** - an extended buffer size will leave the newly-allocated area with +** contents undefined +*/ +void * lv_tlsf_realloc(lv_tlsf_t tlsf, void * ptr, size_t size) +{ + control_t * control = tlsf_cast(control_t *, tlsf); + void * p = 0; + + /* Zero-size requests are treated as free. */ + if(ptr && size == 0) { + lv_tlsf_free(tlsf, ptr); + } + /* Requests with NULL pointers are treated as malloc. */ + else if(!ptr) { + p = lv_tlsf_malloc(tlsf, size); + } + else { + block_header_t * block = block_from_ptr(ptr); + block_header_t * next = block_next(block); + + const size_t cursize = block_size(block); + const size_t combined = cursize + block_size(next) + block_header_overhead; + const size_t adjust = adjust_request_size(size, ALIGN_SIZE); + if(size > cursize && adjust == 0) { + /* The request is probably too large, fail */ + return NULL; + } + + tlsf_assert(!block_is_free(block) && "block already marked as free"); + + /* + ** If the next block is used, or when combined with the current + ** block, does not offer enough space, we must reallocate and copy. + */ + if(adjust > cursize && (!block_is_free(next) || adjust > combined)) { + p = lv_tlsf_malloc(tlsf, size); + if(p) { + const size_t minsize = tlsf_min(cursize, size); + lv_memcpy(p, ptr, minsize); + lv_tlsf_free(tlsf, ptr); + } + } + else { + /* Do we need to expand to the next block? */ + if(adjust > cursize) { + block_merge_next(control, block); + block_mark_as_used(block); + } + + /* Trim the resulting block and return the original pointer. */ + block_trim_used(control, block, adjust); + p = ptr; + } + } + + return p; +} + +#endif /* LV_MEM_CUSTOM == 0 */ diff --git a/lib/lvgl/src/misc/lv_tlsf.h b/lib/lvgl/src/misc/lv_tlsf.h new file mode 100644 index 00000000..f12590b6 --- /dev/null +++ b/lib/lvgl/src/misc/lv_tlsf.h @@ -0,0 +1,95 @@ +#include "../lv_conf_internal.h" +#if LV_MEM_CUSTOM == 0 + +#ifndef LV_TLSF_H +#define LV_TLSF_H + +/* +** Two Level Segregated Fit memory allocator, version 3.1. +** Written by Matthew Conte +** http://tlsf.baisoku.org +** +** Based on the original documentation by Miguel Masmano: +** http://www.gii.upv.es/tlsf/main/docs +** +** This implementation was written to the specification +** of the document, therefore no GPL restrictions apply. +** +** Copyright (c) 2006-2016, Matthew Conte +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** * Neither the name of the copyright holder nor the +** names of its contributors may be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +** WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +** DISCLAIMED. IN NO EVENT SHALL MATTHEW CONTE BE LIABLE FOR ANY +** DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +** (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +** ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +** SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include + +#if defined(__cplusplus) +extern "C" { +#endif + +/* lv_tlsf_t: a TLSF structure. Can contain 1 to N pools. */ +/* lv_pool_t: a block of memory that TLSF can manage. */ +typedef void * lv_tlsf_t; +typedef void * lv_pool_t; + +/* Create/destroy a memory pool. */ +lv_tlsf_t lv_tlsf_create(void * mem); +lv_tlsf_t lv_tlsf_create_with_pool(void * mem, size_t bytes); +void lv_tlsf_destroy(lv_tlsf_t tlsf); +lv_pool_t lv_tlsf_get_pool(lv_tlsf_t tlsf); + +/* Add/remove memory pools. */ +lv_pool_t lv_tlsf_add_pool(lv_tlsf_t tlsf, void * mem, size_t bytes); +void lv_tlsf_remove_pool(lv_tlsf_t tlsf, lv_pool_t pool); + +/* malloc/memalign/realloc/free replacements. */ +void * lv_tlsf_malloc(lv_tlsf_t tlsf, size_t bytes); +void * lv_tlsf_memalign(lv_tlsf_t tlsf, size_t align, size_t bytes); +void * lv_tlsf_realloc(lv_tlsf_t tlsf, void * ptr, size_t size); +size_t lv_tlsf_free(lv_tlsf_t tlsf, const void * ptr); + +/* Returns internal block size, not original request size */ +size_t lv_tlsf_block_size(void * ptr); + +/* Overheads/limits of internal structures. */ +size_t lv_tlsf_size(void); +size_t lv_tlsf_align_size(void); +size_t lv_tlsf_block_size_min(void); +size_t lv_tlsf_block_size_max(void); +size_t lv_tlsf_pool_overhead(void); +size_t lv_tlsf_alloc_overhead(void); + +/* Debugging. */ +typedef void (*lv_tlsf_walker)(void * ptr, size_t size, int used, void * user); +void lv_tlsf_walk_pool(lv_pool_t pool, lv_tlsf_walker walker, void * user); +/* Returns nonzero if any internal consistency check fails. */ +int lv_tlsf_check(lv_tlsf_t tlsf); +int lv_tlsf_check_pool(lv_pool_t pool); + +#if defined(__cplusplus) +}; +#endif + +#endif /*LV_TLSF_H*/ + +#endif /* LV_MEM_CUSTOM == 0 */ diff --git a/lib/lvgl/src/misc/lv_txt.c b/lib/lvgl/src/misc/lv_txt.c new file mode 100644 index 00000000..da7eca0b --- /dev/null +++ b/lib/lvgl/src/misc/lv_txt.c @@ -0,0 +1,864 @@ +/** + * @file lv_txt.c + * + */ + +/********************* + * INCLUDES + *********************/ +#include +#include "lv_txt.h" +#include "lv_txt_ap.h" +#include "lv_math.h" +#include "lv_log.h" +#include "lv_mem.h" +#include "lv_assert.h" + +/********************* + * DEFINES + *********************/ +#define NO_BREAK_FOUND UINT32_MAX + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ + +#if LV_TXT_ENC == LV_TXT_ENC_UTF8 + static uint8_t lv_txt_utf8_size(const char * str); + static uint32_t lv_txt_unicode_to_utf8(uint32_t letter_uni); + static uint32_t lv_txt_utf8_conv_wc(uint32_t c); + static uint32_t lv_txt_utf8_next(const char * txt, uint32_t * i); + static uint32_t lv_txt_utf8_prev(const char * txt, uint32_t * i_start); + static uint32_t lv_txt_utf8_get_byte_id(const char * txt, uint32_t utf8_id); + static uint32_t lv_txt_utf8_get_char_id(const char * txt, uint32_t byte_id); + static uint32_t lv_txt_utf8_get_length(const char * txt); +#elif LV_TXT_ENC == LV_TXT_ENC_ASCII + static uint8_t lv_txt_iso8859_1_size(const char * str); + static uint32_t lv_txt_unicode_to_iso8859_1(uint32_t letter_uni); + static uint32_t lv_txt_iso8859_1_conv_wc(uint32_t c); + static uint32_t lv_txt_iso8859_1_next(const char * txt, uint32_t * i); + static uint32_t lv_txt_iso8859_1_prev(const char * txt, uint32_t * i_start); + static uint32_t lv_txt_iso8859_1_get_byte_id(const char * txt, uint32_t utf8_id); + static uint32_t lv_txt_iso8859_1_get_char_id(const char * txt, uint32_t byte_id); + static uint32_t lv_txt_iso8859_1_get_length(const char * txt); +#endif +/********************** + * STATIC VARIABLES + **********************/ + +/********************** + * GLOBAL VARIABLES + **********************/ +#if LV_TXT_ENC == LV_TXT_ENC_UTF8 + uint8_t (*_lv_txt_encoded_size)(const char *) = lv_txt_utf8_size; + uint32_t (*_lv_txt_unicode_to_encoded)(uint32_t) = lv_txt_unicode_to_utf8; + uint32_t (*_lv_txt_encoded_conv_wc)(uint32_t) = lv_txt_utf8_conv_wc; + uint32_t (*_lv_txt_encoded_next)(const char *, uint32_t *) = lv_txt_utf8_next; + uint32_t (*_lv_txt_encoded_prev)(const char *, uint32_t *) = lv_txt_utf8_prev; + uint32_t (*_lv_txt_encoded_get_byte_id)(const char *, uint32_t) = lv_txt_utf8_get_byte_id; + uint32_t (*_lv_txt_encoded_get_char_id)(const char *, uint32_t) = lv_txt_utf8_get_char_id; + uint32_t (*_lv_txt_get_encoded_length)(const char *) = lv_txt_utf8_get_length; +#elif LV_TXT_ENC == LV_TXT_ENC_ASCII + uint8_t (*_lv_txt_encoded_size)(const char *) = lv_txt_iso8859_1_size; + uint32_t (*_lv_txt_unicode_to_encoded)(uint32_t) = lv_txt_unicode_to_iso8859_1; + uint32_t (*_lv_txt_encoded_conv_wc)(uint32_t) = lv_txt_iso8859_1_conv_wc; + uint32_t (*_lv_txt_encoded_next)(const char *, uint32_t *) = lv_txt_iso8859_1_next; + uint32_t (*_lv_txt_encoded_prev)(const char *, uint32_t *) = lv_txt_iso8859_1_prev; + uint32_t (*_lv_txt_encoded_get_byte_id)(const char *, uint32_t) = lv_txt_iso8859_1_get_byte_id; + uint32_t (*_lv_txt_encoded_get_char_id)(const char *, uint32_t) = lv_txt_iso8859_1_get_char_id; + uint32_t (*_lv_txt_get_encoded_length)(const char *) = lv_txt_iso8859_1_get_length; + +#endif + +/********************** + * MACROS + **********************/ + +#define LV_IS_ASCII(value) ((value & 0x80U) == 0x00U) +#define LV_IS_2BYTES_UTF8_CODE(value) ((value & 0xE0U) == 0xC0U) +#define LV_IS_3BYTES_UTF8_CODE(value) ((value & 0xF0U) == 0xE0U) +#define LV_IS_4BYTES_UTF8_CODE(value) ((value & 0xF8U) == 0xF0U) +#define LV_IS_INVALID_UTF8_CODE(value) ((value & 0xC0U) != 0x80U) + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +void lv_txt_get_size(lv_point_t * size_res, const char * text, const lv_font_t * font, lv_coord_t letter_space, + lv_coord_t line_space, lv_coord_t max_width, lv_text_flag_t flag) +{ + size_res->x = 0; + size_res->y = 0; + + if(text == NULL) return; + if(font == NULL) return; + + if(flag & LV_TEXT_FLAG_EXPAND) max_width = LV_COORD_MAX; + + uint32_t line_start = 0; + uint32_t new_line_start = 0; + uint16_t letter_height = lv_font_get_line_height(font); + + /*Calc. the height and longest line*/ + while(text[line_start] != '\0') { + new_line_start += _lv_txt_get_next_line(&text[line_start], font, letter_space, max_width, NULL, flag); + + if((unsigned long)size_res->y + (unsigned long)letter_height + (unsigned long)line_space > LV_MAX_OF(lv_coord_t)) { + LV_LOG_WARN("lv_txt_get_size: integer overflow while calculating text height"); + return; + } + else { + size_res->y += letter_height; + size_res->y += line_space; + } + + /*Calculate the longest line*/ + lv_coord_t act_line_length = lv_txt_get_width(&text[line_start], new_line_start - line_start, font, letter_space, + flag); + + size_res->x = LV_MAX(act_line_length, size_res->x); + line_start = new_line_start; + } + + /*Make the text one line taller if the last character is '\n' or '\r'*/ + if((line_start != 0) && (text[line_start - 1] == '\n' || text[line_start - 1] == '\r')) { + size_res->y += letter_height + line_space; + } + + /*Correction with the last line space or set the height manually if the text is empty*/ + if(size_res->y == 0) + size_res->y = letter_height; + else + size_res->y -= line_space; +} + +/** + * Get the next word of text. A word is delimited by break characters. + * + * If the word cannot fit in the max_width space, obey LV_TXT_LINE_BREAK_LONG_* rules. + * + * If the next word cannot fit anything, return 0. + * + * If the first character is a break character, returns the next index. + * + * Example calls from lv_txt_get_next_line() assuming sufficient max_width and + * txt = "Test text\n" + * 0123456789 + * + * Calls would be as follows: + * 1. Return i=4, pointing at breakchar ' ', for the string "Test" + * 2. Return i=5, since i=4 was a breakchar. + * 3. Return i=9, pointing at breakchar '\n' + * 4. Parenting lv_txt_get_next_line() would detect subsequent '\0' + * + * TODO: Returned word_w_ptr may overestimate the returned word's width when + * max_width is reached. In current usage, this has no impact. + * + * @param txt a '\0' terminated string + * @param font pointer to a font + * @param letter_space letter space + * @param max_width max width of the text (break the lines to fit this size). Set COORD_MAX to avoid line breaks + * @param flags settings for the text from 'txt_flag_type' enum + * @param[out] word_w_ptr width (in pixels) of the parsed word. May be NULL. + * @param cmd_state pointer to a txt_cmd_state_t variable which stores the current state of command processing + * @param force Force return the fraction of the word that can fit in the provided space. + * @return the index of the first char of the next word (in byte index not letter index. With UTF-8 they are different) + */ +static uint32_t lv_txt_get_next_word(const char * txt, const lv_font_t * font, + lv_coord_t letter_space, lv_coord_t max_width, + lv_text_flag_t flag, uint32_t * word_w_ptr, lv_text_cmd_state_t * cmd_state, bool force) +{ + if(txt == NULL || txt[0] == '\0') return 0; + if(font == NULL) return 0; + + if(flag & LV_TEXT_FLAG_EXPAND) max_width = LV_COORD_MAX; + + uint32_t i = 0, i_next = 0, i_next_next = 0; /*Iterating index into txt*/ + uint32_t letter = 0; /*Letter at i*/ + uint32_t letter_next = 0; /*Letter at i_next*/ + lv_coord_t letter_w; + lv_coord_t cur_w = 0; /*Pixel Width of transversed string*/ + uint32_t word_len = 0; /*Number of characters in the transversed word*/ + uint32_t break_index = NO_BREAK_FOUND; /*only used for "long" words*/ + uint32_t break_letter_count = 0; /*Number of characters up to the long word break point*/ + + letter = _lv_txt_encoded_next(txt, &i_next); + i_next_next = i_next; + + /*Obtain the full word, regardless if it fits or not in max_width*/ + while(txt[i] != '\0') { + letter_next = _lv_txt_encoded_next(txt, &i_next_next); + word_len++; + + /*Handle the recolor command*/ + if((flag & LV_TEXT_FLAG_RECOLOR) != 0) { + if(_lv_txt_is_cmd(cmd_state, letter) != false) { + i = i_next; + i_next = i_next_next; + letter = letter_next; + continue; /*Skip the letter if it is part of a command*/ + } + } + + letter_w = lv_font_get_glyph_width(font, letter, letter_next); + cur_w += letter_w; + + if(letter_w > 0) { + cur_w += letter_space; + } + + /*Test if this character fits within max_width*/ + if(break_index == NO_BREAK_FOUND && (cur_w - letter_space) > max_width) { + break_index = i; + break_letter_count = word_len - 1; + /*break_index is now pointing at the character that doesn't fit*/ + } + + /*Check for new line chars and breakchars*/ + if(letter == '\n' || letter == '\r' || _lv_txt_is_break_char(letter)) { + /*Update the output width on the first character if it fits. + *Must do this here in case first letter is a break character.*/ + if(i == 0 && break_index == NO_BREAK_FOUND && word_w_ptr != NULL) *word_w_ptr = cur_w; + word_len--; + break; + } + + /*Update the output width*/ + if(word_w_ptr != NULL && break_index == NO_BREAK_FOUND) *word_w_ptr = cur_w; + + i = i_next; + i_next = i_next_next; + letter = letter_next; + } + + /*Entire Word fits in the provided space*/ + if(break_index == NO_BREAK_FOUND) { + if(word_len == 0 || (letter == '\r' && letter_next == '\n')) i = i_next; + return i; + } + +#if LV_TXT_LINE_BREAK_LONG_LEN > 0 + /*Word doesn't fit in provided space, but isn't "long"*/ + if(word_len < LV_TXT_LINE_BREAK_LONG_LEN) { + if(force) return break_index; + if(word_w_ptr != NULL) *word_w_ptr = 0; /*Return no word*/ + return 0; + } + + /*Word is "long," but insufficient amounts can fit in provided space*/ + if(break_letter_count < LV_TXT_LINE_BREAK_LONG_PRE_MIN_LEN) { + if(force) return break_index; + if(word_w_ptr != NULL) *word_w_ptr = 0; + return 0; + } + + /*Word is a "long", but letters may need to be better distributed*/ + { + i = break_index; + int32_t n_move = LV_TXT_LINE_BREAK_LONG_POST_MIN_LEN - (word_len - break_letter_count); + /*Move pointer "i" backwards*/ + for(; n_move > 0; n_move--) { + _lv_txt_encoded_prev(txt, &i); + // TODO: it would be appropriate to update the returned word width here + // However, in current usage, this doesn't impact anything. + } + } + return i; +#else + if(force) return break_index; + if(word_w_ptr != NULL) *word_w_ptr = 0; /*Return no word*/ + (void) break_letter_count; + return 0; +#endif +} + +uint32_t _lv_txt_get_next_line(const char * txt, const lv_font_t * font, + lv_coord_t letter_space, lv_coord_t max_width, + lv_coord_t * used_width, lv_text_flag_t flag) +{ + if(used_width) *used_width = 0; + + if(txt == NULL) return 0; + if(txt[0] == '\0') return 0; + if(font == NULL) return 0; + + lv_coord_t line_w = 0; + + /*If max_width doesn't mater simply find the new line character + *without thinking about word wrapping*/ + if((flag & LV_TEXT_FLAG_EXPAND) || (flag & LV_TEXT_FLAG_FIT)) { + uint32_t i; + for(i = 0; txt[i] != '\n' && txt[i] != '\r' && txt[i] != '\0'; i++) { + /*Just find the new line chars or string ends by incrementing `i`*/ + } + if(txt[i] != '\0') i++; /*To go beyond `\n`*/ + if(used_width) *used_width = -1; + return i; + } + + if(flag & LV_TEXT_FLAG_EXPAND) max_width = LV_COORD_MAX; + lv_text_cmd_state_t cmd_state = LV_TEXT_CMD_STATE_WAIT; + uint32_t i = 0; /*Iterating index into txt*/ + + while(txt[i] != '\0' && max_width > 0) { + uint32_t word_w = 0; + uint32_t advance = lv_txt_get_next_word(&txt[i], font, letter_space, max_width, flag, &word_w, &cmd_state, i == 0); + max_width -= word_w; + line_w += word_w; + + if(advance == 0) { + break; + } + + i += advance; + + if(txt[0] == '\n' || txt[0] == '\r') break; + + if(txt[i] == '\n' || txt[i] == '\r') { + i++; /*Include the following newline in the current line*/ + break; + } + + } + + /*Always step at least one to avoid infinite loops*/ + if(i == 0) { + uint32_t letter = _lv_txt_encoded_next(txt, &i); + if(used_width != NULL) { + line_w = lv_font_get_glyph_width(font, letter, '\0'); + } + } + + if(used_width != NULL) { + *used_width = line_w; + } + + return i; +} + +lv_coord_t lv_txt_get_width(const char * txt, uint32_t length, const lv_font_t * font, lv_coord_t letter_space, + lv_text_flag_t flag) +{ + if(txt == NULL) return 0; + if(font == NULL) return 0; + if(txt[0] == '\0') return 0; + + uint32_t i = 0; + lv_coord_t width = 0; + lv_text_cmd_state_t cmd_state = LV_TEXT_CMD_STATE_WAIT; + + if(length != 0) { + while(i < length) { + uint32_t letter; + uint32_t letter_next; + _lv_txt_encoded_letter_next_2(txt, &letter, &letter_next, &i); + + if((flag & LV_TEXT_FLAG_RECOLOR) != 0) { + if(_lv_txt_is_cmd(&cmd_state, letter) != false) { + continue; + } + } + + lv_coord_t char_width = lv_font_get_glyph_width(font, letter, letter_next); + if(char_width > 0) { + width += char_width; + width += letter_space; + } + } + + if(width > 0) { + width -= letter_space; /*Trim the last letter space. Important if the text is center + aligned*/ + } + } + + return width; +} + +bool _lv_txt_is_cmd(lv_text_cmd_state_t * state, uint32_t c) +{ + bool ret = false; + + if(c == (uint32_t)LV_TXT_COLOR_CMD[0]) { + if(*state == LV_TEXT_CMD_STATE_WAIT) { /*Start char*/ + *state = LV_TEXT_CMD_STATE_PAR; + ret = true; + } + /*Other start char in parameter is escaped cmd. char*/ + else if(*state == LV_TEXT_CMD_STATE_PAR) { + *state = LV_TEXT_CMD_STATE_WAIT; + } + /*Command end*/ + else if(*state == LV_TEXT_CMD_STATE_IN) { + *state = LV_TEXT_CMD_STATE_WAIT; + ret = true; + } + } + + /*Skip the color parameter and wait the space after it*/ + if(*state == LV_TEXT_CMD_STATE_PAR) { + if(c == ' ') { + *state = LV_TEXT_CMD_STATE_IN; /*After the parameter the text is in the command*/ + } + ret = true; + } + + return ret; +} + +void _lv_txt_ins(char * txt_buf, uint32_t pos, const char * ins_txt) +{ + if(txt_buf == NULL || ins_txt == NULL) return; + + size_t old_len = strlen(txt_buf); + size_t ins_len = strlen(ins_txt); + if(ins_len == 0) return; + + size_t new_len = ins_len + old_len; + pos = _lv_txt_encoded_get_byte_id(txt_buf, pos); /*Convert to byte index instead of letter index*/ + + /*Copy the second part into the end to make place to text to insert*/ + size_t i; + for(i = new_len; i >= pos + ins_len; i--) { + txt_buf[i] = txt_buf[i - ins_len]; + } + + /*Copy the text into the new space*/ + lv_memcpy_small(txt_buf + pos, ins_txt, ins_len); +} + +void _lv_txt_cut(char * txt, uint32_t pos, uint32_t len) +{ + if(txt == NULL) return; + + size_t old_len = strlen(txt); + + pos = _lv_txt_encoded_get_byte_id(txt, pos); /*Convert to byte index instead of letter index*/ + len = _lv_txt_encoded_get_byte_id(&txt[pos], len); + + /*Copy the second part into the end to make place to text to insert*/ + uint32_t i; + for(i = pos; i <= old_len - len; i++) { + txt[i] = txt[i + len]; + } +} + +char * _lv_txt_set_text_vfmt(const char * fmt, va_list ap) +{ + /*Allocate space for the new text by using trick from C99 standard section 7.19.6.12*/ + va_list ap_copy; + va_copy(ap_copy, ap); + uint32_t len = lv_vsnprintf(NULL, 0, fmt, ap_copy); + va_end(ap_copy); + + char * text = 0; +#if LV_USE_ARABIC_PERSIAN_CHARS + /*Put together the text according to the format string*/ + char * raw_txt = lv_mem_buf_get(len + 1); + LV_ASSERT_MALLOC(raw_txt); + if(raw_txt == NULL) { + return NULL; + } + + lv_vsnprintf(raw_txt, len + 1, fmt, ap); + + /*Get the size of the Arabic text and process it*/ + size_t len_ap = _lv_txt_ap_calc_bytes_cnt(raw_txt); + text = lv_mem_alloc(len_ap + 1); + LV_ASSERT_MALLOC(text); + if(text == NULL) { + return NULL; + } + _lv_txt_ap_proc(raw_txt, text); + + lv_mem_buf_release(raw_txt); +#else + text = lv_mem_alloc(len + 1); + LV_ASSERT_MALLOC(text); + if(text == NULL) { + return NULL; + } + text[len] = 0; /*Ensure NULL termination*/ + + lv_vsnprintf(text, len + 1, fmt, ap); +#endif + + return text; +} + +void _lv_txt_encoded_letter_next_2(const char * txt, uint32_t * letter, uint32_t * letter_next, uint32_t * ofs) +{ + *letter = _lv_txt_encoded_next(txt, ofs); + *letter_next = *letter != '\0' ? _lv_txt_encoded_next(&txt[*ofs], NULL) : 0; +} + +#if LV_TXT_ENC == LV_TXT_ENC_UTF8 +/******************************* + * UTF-8 ENCODER/DECODER + ******************************/ + +/** + * Give the size of an UTF-8 coded character + * @param str pointer to a character in a string + * @return length of the UTF-8 character (1,2,3 or 4), 0 on invalid code. + */ +static uint8_t lv_txt_utf8_size(const char * str) +{ + if(LV_IS_ASCII(str[0])) + return 1; + else if(LV_IS_2BYTES_UTF8_CODE(str[0])) + return 2; + else if(LV_IS_3BYTES_UTF8_CODE(str[0])) + return 3; + else if(LV_IS_4BYTES_UTF8_CODE(str[0])) + return 4; + return 0; +} + +/** + * Convert a Unicode letter to UTF-8. + * @param letter_uni a Unicode letter + * @return UTF-8 coded character in Little Endian to be compatible with C chars (e.g. 'Á', 'Ű') + */ +static uint32_t lv_txt_unicode_to_utf8(uint32_t letter_uni) +{ + if(letter_uni < 128) return letter_uni; + uint8_t bytes[4]; + + if(letter_uni < 0x0800) { + bytes[0] = ((letter_uni >> 6) & 0x1F) | 0xC0; + bytes[1] = ((letter_uni >> 0) & 0x3F) | 0x80; + bytes[2] = 0; + bytes[3] = 0; + } + else if(letter_uni < 0x010000) { + bytes[0] = ((letter_uni >> 12) & 0x0F) | 0xE0; + bytes[1] = ((letter_uni >> 6) & 0x3F) | 0x80; + bytes[2] = ((letter_uni >> 0) & 0x3F) | 0x80; + bytes[3] = 0; + } + else if(letter_uni < 0x110000) { + bytes[0] = ((letter_uni >> 18) & 0x07) | 0xF0; + bytes[1] = ((letter_uni >> 12) & 0x3F) | 0x80; + bytes[2] = ((letter_uni >> 6) & 0x3F) | 0x80; + bytes[3] = ((letter_uni >> 0) & 0x3F) | 0x80; + } + else { + return 0; + } + + uint32_t * res_p = (uint32_t *)bytes; + return *res_p; +} + +/** + * Convert a wide character, e.g. 'Á' little endian to be UTF-8 compatible + * @param c a wide character or a Little endian number + * @return `c` in big endian + */ +static uint32_t lv_txt_utf8_conv_wc(uint32_t c) +{ +#if LV_BIG_ENDIAN_SYSTEM == 0 + /*Swap the bytes (UTF-8 is big endian, but the MCUs are little endian)*/ + if((c & 0x80) != 0) { + uint32_t swapped; + uint8_t c8[4]; + lv_memcpy_small(c8, &c, 4); + swapped = (c8[0] << 24) + (c8[1] << 16) + (c8[2] << 8) + (c8[3]); + uint8_t i; + for(i = 0; i < 4; i++) { + if((swapped & 0xFF) == 0) + swapped = (swapped >> 8); /*Ignore leading zeros (they were in the end originally)*/ + } + c = swapped; + } +#endif + return c; +} + +/** + * Decode an UTF-8 character from a string. + * @param txt pointer to '\0' terminated string + * @param i start byte index in 'txt' where to start. + * After call it will point to the next UTF-8 char in 'txt'. + * NULL to use txt[0] as index + * @return the decoded Unicode character or 0 on invalid UTF-8 code + */ +static uint32_t lv_txt_utf8_next(const char * txt, uint32_t * i) +{ + /** + * Unicode to UTF-8 + * 00000000 00000000 00000000 0xxxxxxx -> 0xxxxxxx + * 00000000 00000000 00000yyy yyxxxxxx -> 110yyyyy 10xxxxxx + * 00000000 00000000 zzzzyyyy yyxxxxxx -> 1110zzzz 10yyyyyy 10xxxxxx + * 00000000 000wwwzz zzzzyyyy yyxxxxxx -> 11110www 10zzzzzz 10yyyyyy 10xxxxxx + */ + + uint32_t result = 0; + + /*Dummy 'i' pointer is required*/ + uint32_t i_tmp = 0; + if(i == NULL) i = &i_tmp; + + /*Normal ASCII*/ + if(LV_IS_ASCII(txt[*i])) { + result = txt[*i]; + (*i)++; + } + /*Real UTF-8 decode*/ + else { + /*2 bytes UTF-8 code*/ + if(LV_IS_2BYTES_UTF8_CODE(txt[*i])) { + result = (uint32_t)(txt[*i] & 0x1F) << 6; + (*i)++; + if(LV_IS_INVALID_UTF8_CODE(txt[*i])) return 0; + result += (txt[*i] & 0x3F); + (*i)++; + } + /*3 bytes UTF-8 code*/ + else if(LV_IS_3BYTES_UTF8_CODE(txt[*i])) { + result = (uint32_t)(txt[*i] & 0x0F) << 12; + (*i)++; + + if(LV_IS_INVALID_UTF8_CODE(txt[*i])) return 0; + result += (uint32_t)(txt[*i] & 0x3F) << 6; + (*i)++; + + if(LV_IS_INVALID_UTF8_CODE(txt[*i])) return 0; + result += (txt[*i] & 0x3F); + (*i)++; + } + /*4 bytes UTF-8 code*/ + else if(LV_IS_4BYTES_UTF8_CODE(txt[*i])) { + result = (uint32_t)(txt[*i] & 0x07) << 18; + (*i)++; + + if(LV_IS_INVALID_UTF8_CODE(txt[*i])) return 0; + result += (uint32_t)(txt[*i] & 0x3F) << 12; + (*i)++; + + if(LV_IS_INVALID_UTF8_CODE(txt[*i])) return 0; + result += (uint32_t)(txt[*i] & 0x3F) << 6; + (*i)++; + + if(LV_IS_INVALID_UTF8_CODE(txt[*i])) return 0; + result += txt[*i] & 0x3F; + (*i)++; + } + else { + (*i)++; /*Not UTF-8 char. Go the next.*/ + } + } + return result; +} + +/** + * Get previous UTF-8 character form a string. + * @param txt pointer to '\0' terminated string + * @param i start byte index in 'txt' where to start. After the call it will point to the previous + * UTF-8 char in 'txt'. + * @return the decoded Unicode character or 0 on invalid UTF-8 code + */ +static uint32_t lv_txt_utf8_prev(const char * txt, uint32_t * i) +{ + uint8_t c_size; + uint8_t cnt = 0; + + /*Try to find a !0 long UTF-8 char by stepping one character back*/ + (*i)--; + do { + if(cnt >= 4) return 0; /*No UTF-8 char found before the initial*/ + + c_size = _lv_txt_encoded_size(&txt[*i]); + if(c_size == 0) { + if(*i != 0) + (*i)--; + else + return 0; + } + cnt++; + } while(c_size == 0); + + uint32_t i_tmp = *i; + uint32_t letter = _lv_txt_encoded_next(txt, &i_tmp); /*Character found, get it*/ + + return letter; +} + +/** + * Convert a character index (in an UTF-8 text) to byte index. + * E.g. in "AÁRT" index of 'R' is 2th char but start at byte 3 because 'Á' is 2 bytes long + * @param txt a '\0' terminated UTF-8 string + * @param utf8_id character index + * @return byte index of the 'utf8_id'th letter + */ +static uint32_t lv_txt_utf8_get_byte_id(const char * txt, uint32_t utf8_id) +{ + uint32_t i; + uint32_t byte_cnt = 0; + for(i = 0; i < utf8_id && txt[byte_cnt] != '\0'; i++) { + uint8_t c_size = _lv_txt_encoded_size(&txt[byte_cnt]); + /* If the char was invalid tell it's 1 byte long*/ + byte_cnt += c_size ? c_size : 1; + } + + return byte_cnt; +} + +/** + * Convert a byte index (in an UTF-8 text) to character index. + * E.g. in "AÁRT" index of 'R' is 2th char but start at byte 3 because 'Á' is 2 bytes long + * @param txt a '\0' terminated UTF-8 string + * @param byte_id byte index + * @return character index of the letter at 'byte_id'th position + */ +static uint32_t lv_txt_utf8_get_char_id(const char * txt, uint32_t byte_id) +{ + uint32_t i = 0; + uint32_t char_cnt = 0; + + while(i < byte_id) { + _lv_txt_encoded_next(txt, &i); /*'i' points to the next letter so use the prev. value*/ + char_cnt++; + } + + return char_cnt; +} + +/** + * Get the number of characters (and NOT bytes) in a string. Decode it with UTF-8 if enabled. + * E.g.: "ÁBC" is 3 characters (but 4 bytes) + * @param txt a '\0' terminated char string + * @return number of characters + */ +static uint32_t lv_txt_utf8_get_length(const char * txt) +{ + uint32_t len = 0; + uint32_t i = 0; + + while(txt[i] != '\0') { + _lv_txt_encoded_next(txt, &i); + len++; + } + + return len; +} + +#elif LV_TXT_ENC == LV_TXT_ENC_ASCII +/******************************* + * ASCII ENCODER/DECODER + ******************************/ + +/** + * Give the size of an ISO8859-1 coded character + * @param str pointer to a character in a string + * @return length of the UTF-8 character (1,2,3 or 4). O on invalid code + */ +static uint8_t lv_txt_iso8859_1_size(const char * str) +{ + LV_UNUSED(str); /*Unused*/ + return 1; +} + +/** + * Convert a Unicode letter to ISO8859-1. + * @param letter_uni a Unicode letter + * @return ISO8859-1 coded character in Little Endian to be compatible with C chars (e.g. 'Á', 'Ű') + */ +static uint32_t lv_txt_unicode_to_iso8859_1(uint32_t letter_uni) +{ + if(letter_uni < 256) + return letter_uni; + else + return ' '; +} + +/** + * Convert wide characters to ASCII, however wide characters in ASCII range (e.g. 'A') are ASCII compatible by default. + * So this function does nothing just returns with `c`. + * @param c a character, e.g. 'A' + * @return same as `c` + */ +static uint32_t lv_txt_iso8859_1_conv_wc(uint32_t c) +{ + return c; +} + +/** + * Decode an ISO8859-1 character from a string. + * @param txt pointer to '\0' terminated string + * @param i start byte index in 'txt' where to start. + * After call it will point to the next UTF-8 char in 'txt'. + * NULL to use txt[0] as index + * @return the decoded Unicode character or 0 on invalid UTF-8 code + */ +static uint32_t lv_txt_iso8859_1_next(const char * txt, uint32_t * i) +{ + if(i == NULL) return txt[0]; /*Get the next char*/ + + uint8_t letter = txt[*i]; + (*i)++; + return letter; +} + +/** + * Get previous ISO8859-1 character form a string. + * @param txt pointer to '\0' terminated string + * @param i start byte index in 'txt' where to start. After the call it will point to the previous UTF-8 char in 'txt'. + * @return the decoded Unicode character or 0 on invalid UTF-8 code + */ +static uint32_t lv_txt_iso8859_1_prev(const char * txt, uint32_t * i) +{ + if(i == NULL) return *(txt - 1); /*Get the prev. char*/ + + (*i)--; + uint8_t letter = txt[*i]; + + return letter; +} + +/** + * Convert a character index (in an ISO8859-1 text) to byte index. + * E.g. in "AÁRT" index of 'R' is 2th char but start at byte 3 because 'Á' is 2 bytes long + * @param txt a '\0' terminated UTF-8 string + * @param utf8_id character index + * @return byte index of the 'utf8_id'th letter + */ +static uint32_t lv_txt_iso8859_1_get_byte_id(const char * txt, uint32_t utf8_id) +{ + LV_UNUSED(txt); /*Unused*/ + return utf8_id; /*In Non encoded no difference*/ +} + +/** + * Convert a byte index (in an ISO8859-1 text) to character index. + * E.g. in "AÁRT" index of 'R' is 2th char but start at byte 3 because 'Á' is 2 bytes long + * @param txt a '\0' terminated UTF-8 string + * @param byte_id byte index + * @return character index of the letter at 'byte_id'th position + */ +static uint32_t lv_txt_iso8859_1_get_char_id(const char * txt, uint32_t byte_id) +{ + LV_UNUSED(txt); /*Unused*/ + return byte_id; /*In Non encoded no difference*/ +} + +/** + * Get the number of characters (and NOT bytes) in a string. Decode it with UTF-8 if enabled. + * E.g.: "ÁBC" is 3 characters (but 4 bytes) + * @param txt a '\0' terminated char string + * @return number of characters + */ +static uint32_t lv_txt_iso8859_1_get_length(const char * txt) +{ + return strlen(txt); +} +#else + +#error "Invalid character encoding. See `LV_TXT_ENC` in `lv_conf.h`" + +#endif diff --git a/lib/lvgl/src/misc/lv_txt.h b/lib/lvgl/src/misc/lv_txt.h new file mode 100644 index 00000000..46050dc3 --- /dev/null +++ b/lib/lvgl/src/misc/lv_txt.h @@ -0,0 +1,264 @@ +/** + * @file lv_txt.h + * + */ + +#ifndef LV_TXT_H +#define LV_TXT_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ +#include "../lv_conf_internal.h" + +#include +#include +#include "lv_area.h" +#include "../font/lv_font.h" +#include "lv_printf.h" +#include "lv_types.h" + +/********************* + * DEFINES + *********************/ +#ifndef LV_TXT_COLOR_CMD +#define LV_TXT_COLOR_CMD "#" +#endif + +#define LV_TXT_ENC_UTF8 1 +#define LV_TXT_ENC_ASCII 2 + +/********************** + * TYPEDEFS + **********************/ + +/** + * Options for text rendering. + */ +enum { + LV_TEXT_FLAG_NONE = 0x00, + LV_TEXT_FLAG_RECOLOR = 0x01, /**< Enable parsing of recolor command*/ + LV_TEXT_FLAG_EXPAND = 0x02, /**< Ignore max-width to avoid automatic word wrapping*/ + LV_TEXT_FLAG_FIT = 0x04, /**< Max-width is already equal to the longest line. (Used to skip some calculation)*/ +}; +typedef uint8_t lv_text_flag_t; + +/** + * State machine for text renderer.*/ +enum { + LV_TEXT_CMD_STATE_WAIT, /**< Waiting for command*/ + LV_TEXT_CMD_STATE_PAR, /**< Processing the parameter*/ + LV_TEXT_CMD_STATE_IN, /**< Processing the command*/ +}; +typedef uint8_t lv_text_cmd_state_t; + +/** Label align policy*/ +enum { + LV_TEXT_ALIGN_AUTO, /**< Align text auto*/ + LV_TEXT_ALIGN_LEFT, /**< Align text to left*/ + LV_TEXT_ALIGN_CENTER, /**< Align text to center*/ + LV_TEXT_ALIGN_RIGHT, /**< Align text to right*/ +}; +typedef uint8_t lv_text_align_t; + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +/** + * Get size of a text + * @param size_res pointer to a 'point_t' variable to store the result + * @param text pointer to a text + * @param font pointer to font of the text + * @param letter_space letter space of the text + * @param line_space line space of the text + * @param flags settings for the text from ::lv_text_flag_t + * @param max_width max width of the text (break the lines to fit this size). Set COORD_MAX to avoid + * line breaks + */ +void lv_txt_get_size(lv_point_t * size_res, const char * text, const lv_font_t * font, lv_coord_t letter_space, + lv_coord_t line_space, lv_coord_t max_width, lv_text_flag_t flag); + +/** + * Get the next line of text. Check line length and break chars too. + * @param txt a '\0' terminated string + * @param font pointer to a font + * @param letter_space letter space + * @param max_width max width of the text (break the lines to fit this size). Set COORD_MAX to avoid + * line breaks + * @param used_width When used_width != NULL, save the width of this line if + * flag == LV_TEXT_FLAG_NONE, otherwise save -1. + * @param flags settings for the text from 'txt_flag_type' enum + * @return the index of the first char of the new line (in byte index not letter index. With UTF-8 + * they are different) + */ +uint32_t _lv_txt_get_next_line(const char * txt, const lv_font_t * font, lv_coord_t letter_space, + lv_coord_t max_width, lv_coord_t * used_width, lv_text_flag_t flag); + +/** + * Give the length of a text with a given font + * @param txt a '\0' terminate string + * @param length length of 'txt' in byte count and not characters (Á is 1 character but 2 bytes in + * UTF-8) + * @param font pointer to a font + * @param letter_space letter space + * @param flags settings for the text from 'txt_flag_t' enum + * @return length of a char_num long text + */ +lv_coord_t lv_txt_get_width(const char * txt, uint32_t length, const lv_font_t * font, lv_coord_t letter_space, + lv_text_flag_t flag); + +/** + * Check next character in a string and decide if the character is part of the command or not + * @param state pointer to a txt_cmd_state_t variable which stores the current state of command + * processing + * @param c the current character + * @return true: the character is part of a command and should not be written, + * false: the character should be written + */ +bool _lv_txt_is_cmd(lv_text_cmd_state_t * state, uint32_t c); + +/** + * Insert a string into an other + * @param txt_buf the original text (must be big enough for the result text and NULL terminated) + * @param pos position to insert (0: before the original text, 1: after the first char etc.) + * @param ins_txt text to insert, must be '\0' terminated + */ +void _lv_txt_ins(char * txt_buf, uint32_t pos, const char * ins_txt); + +/** + * Delete a part of a string + * @param txt string to modify, must be '\0' terminated and should point to a heap or stack frame, not read-only memory. + * @param pos position where to start the deleting (0: before the first char, 1: after the first + * char etc.) + * @param len number of characters to delete + */ +void _lv_txt_cut(char * txt, uint32_t pos, uint32_t len); + +/** + * return a new formatted text. Memory will be allocated to store the text. + * @param fmt `printf`-like format + * @return pointer to the allocated text string. + */ +char * _lv_txt_set_text_vfmt(const char * fmt, va_list ap) LV_FORMAT_ATTRIBUTE(1, 0); + +/** + * Decode two encoded character from a string. + * @param txt pointer to '\0' terminated string + * @param letter the first decoded Unicode character or 0 on invalid data code + * @param letter_next the second decoded Unicode character or 0 on invalid data code + * @param ofs start index in 'txt' where to start. + * After the call it will point to the next encoded char in 'txt'. + * NULL to use txt[0] as index + */ +void _lv_txt_encoded_letter_next_2(const char * txt, uint32_t * letter, uint32_t * letter_next, uint32_t * ofs); + +/** + * Test if char is break char or not (a text can broken here or not) + * @param letter a letter + * @return false: 'letter' is not break char + */ +static inline bool _lv_txt_is_break_char(uint32_t letter) +{ + uint8_t i; + bool ret = false; + + /* each chinese character can be break */ + if(letter >= 0x4E00 && letter <= 0x9FA5) { + return true; + } + + /*Compare the letter to TXT_BREAK_CHARS*/ + for(i = 0; LV_TXT_BREAK_CHARS[i] != '\0'; i++) { + if(letter == (uint32_t)LV_TXT_BREAK_CHARS[i]) { + ret = true; /*If match then it is break char*/ + break; + } + } + + return ret; +} + +/*************************************************************** + * GLOBAL FUNCTION POINTERS FOR CHARACTER ENCODING INTERFACE + ***************************************************************/ + +/** + * Give the size of an encoded character + * @param str pointer to a character in a string + * @return length of the encoded character (1,2,3 ...). O in invalid + */ +extern uint8_t (*_lv_txt_encoded_size)(const char *); + +/** + * Convert a Unicode letter to encoded + * @param letter_uni a Unicode letter + * @return Encoded character in Little Endian to be compatible with C chars (e.g. 'Á', 'Ü') + */ +extern uint32_t (*_lv_txt_unicode_to_encoded)(uint32_t); + +/** + * Convert a wide character, e.g. 'Á' little endian to be compatible with the encoded format. + * @param c a wide character + * @return `c` in the encoded format + */ +extern uint32_t (*_lv_txt_encoded_conv_wc)(uint32_t c); + +/** + * Decode the next encoded character from a string. + * @param txt pointer to '\0' terminated string + * @param i start index in 'txt' where to start. + * After the call it will point to the next encoded char in 'txt'. + * NULL to use txt[0] as index + * @return the decoded Unicode character or 0 on invalid data code + */ +extern uint32_t (*_lv_txt_encoded_next)(const char *, uint32_t *); + +/** + * Get the previous encoded character form a string. + * @param txt pointer to '\0' terminated string + * @param i_start index in 'txt' where to start. After the call it will point to the previous + * encoded char in 'txt'. + * @return the decoded Unicode character or 0 on invalid data + */ +extern uint32_t (*_lv_txt_encoded_prev)(const char *, uint32_t *); + +/** + * Convert a letter index (in the encoded text) to byte index. + * E.g. in UTF-8 "AÁRT" index of 'R' is 2 but start at byte 3 because 'Á' is 2 bytes long + * @param txt a '\0' terminated UTF-8 string + * @param enc_id letter index + * @return byte index of the 'enc_id'th letter + */ +extern uint32_t (*_lv_txt_encoded_get_byte_id)(const char *, uint32_t); + +/** + * Convert a byte index (in an encoded text) to character index. + * E.g. in UTF-8 "AÁRT" index of 'R' is 2 but start at byte 3 because 'Á' is 2 bytes long + * @param txt a '\0' terminated UTF-8 string + * @param byte_id byte index + * @return character index of the letter at 'byte_id'th position + */ +extern uint32_t (*_lv_txt_encoded_get_char_id)(const char *, uint32_t); + +/** + * Get the number of characters (and NOT bytes) in a string. + * E.g. in UTF-8 "ÁBC" is 3 characters (but 4 bytes) + * @param txt a '\0' terminated char string + * @return number of characters + */ +extern uint32_t (*_lv_txt_get_encoded_length)(const char *); + +/********************** + * MACROS + **********************/ + +#ifdef __cplusplus +} /*extern "C"*/ +#endif + +#endif /*LV_TXT_H*/ diff --git a/lib/lvgl/src/misc/lv_txt_ap.c b/lib/lvgl/src/misc/lv_txt_ap.c new file mode 100644 index 00000000..54faf8b4 --- /dev/null +++ b/lib/lvgl/src/misc/lv_txt_ap.c @@ -0,0 +1,301 @@ +/** + * @file lv_txt_ap.c + * + */ + +/********************* + * INCLUDES + *********************/ +#include +#include "lv_bidi.h" +#include "lv_txt.h" +#include "lv_txt_ap.h" +#include "lv_mem.h" +#include "../draw/lv_draw.h" + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ +typedef struct { + uint8_t char_offset; + uint16_t char_end_form; + int8_t char_begining_form_offset; + int8_t char_middle_form_offset; + int8_t char_isolated_form_offset; + struct { + uint8_t conj_to_previous; + uint8_t conj_to_next; + } ap_chars_conjunction; +} ap_chars_map_t; + +/********************** + * STATIC PROTOTYPES + **********************/ +#if LV_USE_ARABIC_PERSIAN_CHARS == 1 +static uint32_t lv_ap_get_char_index(uint16_t c); +static uint32_t lv_txt_lam_alef(uint32_t ch_curr, uint32_t ch_next); +static bool lv_txt_is_arabic_vowel(uint16_t c); + +/********************** + * STATIC VARIABLES + **********************/ + +const ap_chars_map_t ap_chars_map[] = { + /*{Key Offset, End, Beginning, Middle, Isolated, {conjunction}}*/ + {1, 0xFE84, -1, 0, -1, {1, 0}}, // أ + {2, 0xFE86, -1, 0, -1, {1, 0}}, // ؤ + {3, 0xFE88, -1, 0, -1, {1, 0}}, // ﺇ + {4, 0xFE8A, 1, 2, -1, {1, 0}}, // ئ + {5, 0xFE8E, -1, 0, -1, {1, 0}}, // آ + {6, 0xFE90, 1, 2, -1, {1, 1}}, // ب + {92, 0xFB57, 1, 2, -1, {1, 1}}, // پ + {8, 0xFE96, 1, 2, -1, {1, 1}}, // ت + {9, 0xFE9A, 1, 2, -1, {1, 1}}, // ث + {10, 0xFE9E, 1, 2, -1, {1, 1}}, // ج + {100, 0xFB7B, 1, 2, -1, {1, 1}}, // چ + {11, 0xFEA2, 1, 2, -1, {1, 1}}, // ح + {12, 0xFEA6, 1, 2, -1, {1, 1}}, // خ + {13, 0xFEAA, -1, 0, -1, {1, 0}}, // د + {14, 0xFEAC, -1, 0, -1, {1, 0}}, // ذ + {15, 0xFEAE, -1, 0, -1, {1, 0}}, // ر + {16, 0xFEB0, -1, 0, -1, {1, 0}}, // ز + {118, 0xFB8B, -1, 0, -1, {1, 0}}, // ژ + {17, 0xFEB2, 1, 2, -1, {1, 1}}, // س + {18, 0xFEB6, 1, 2, -1, {1, 1}}, // ش + {19, 0xFEBA, 1, 2, -1, {1, 1}}, // ص + {20, 0xFEBE, 1, 2, -1, {1, 1}}, // ض + {21, 0xFEC2, 1, 2, -1, {1, 1}}, // ط + {22, 0xFEC6, 1, 2, -1, {1, 1}}, // ظ + {23, 0xFECA, 1, 2, -1, {1, 1}}, // ع + {24, 0xFECE, 1, 2, -1, {1, 1}}, // غ + {30, 0x0640, 0, 0, 0, {1, 1}}, // - (mad, hyphen) + {31, 0xFED2, 1, 2, -1, {1, 1}}, // ف + {32, 0xFED6, 1, 2, -1, {1, 1}}, // ق + {135, 0xFB8F, 1, 2, -1, {1, 1}}, // ک + {33, 0xFEDA, 1, 2, -1, {1, 1}}, // ﻙ + {141, 0xFB93, 1, 2, -1, {1, 1}}, // گ + {34, 0xFEDE, 1, 2, -1, {1, 1}}, // ل + {35, 0xFEE2, 1, 2, -1, {1, 1}}, // م + {36, 0xFEE6, 1, 2, -1, {1, 1}}, // ن + {38, 0xFEEE, -1, 0, -1, {1, 0}}, // و + {37, 0xFEEA, 1, 2, -1, {1, 1}}, // ه + {39, 0xFEF0, 0, 0, -1, {1, 0}}, // ى + {40, 0xFEF2, 1, 2, -1, {1, 1}}, // ي + {170, 0xFBFD, 1, 2, -1, {1, 1}}, // ی + {7, 0xFE94, 1, 2, -1, {1, 0}}, // ة + {206, 0x06F0, 1, 2, -1, {0, 0}}, // ۰ + {207, 0x06F1, 0, 0, 0, {0, 0}}, // ۱ + {208, 0x06F2, 0, 0, 0, {0, 0}}, // ۲ + {209, 0x06F3, 0, 0, 0, {0, 0}}, // ۳ + {210, 0x06F4, 0, 0, 0, {0, 0}}, // ۴ + {211, 0x06F5, 0, 0, 0, {0, 0}}, // ۵ + {212, 0x06F6, 0, 0, 0, {0, 0}}, // ۶ + {213, 0x06F7, 0, 0, 0, {0, 0}}, // ۷ + {214, 0x06F8, 0, 0, 0, {0, 0}}, // ۸ + {215, 0x06F9, 0, 0, 0, {0, 0}}, // ۹ + LV_AP_END_CHARS_LIST +}; +/********************** +* MACROS +**********************/ + +/********************** +* GLOBAL FUNCTIONS +**********************/ +uint32_t _lv_txt_ap_calc_bytes_cnt(const char * txt) +{ + uint32_t txt_length = 0; + uint32_t chars_cnt = 0; + uint32_t current_ap_idx = 0; + uint32_t i, j; + uint32_t ch_enc; + + txt_length = _lv_txt_get_encoded_length(txt); + + i = 0; + j = 0; + while(i < txt_length) { + ch_enc = _lv_txt_encoded_next(txt, &j); + current_ap_idx = lv_ap_get_char_index(ch_enc); + + if(current_ap_idx != LV_UNDEF_ARABIC_PERSIAN_CHARS) + ch_enc = ap_chars_map[current_ap_idx].char_end_form; + + if(ch_enc < 0x80) + chars_cnt++; + else if(ch_enc < 0x0800) + chars_cnt += 2; + else if(ch_enc < 0x010000) + chars_cnt += 3; + else + chars_cnt += 4; + + i++; + } + + return chars_cnt + 1; +} + +void _lv_txt_ap_proc(const char * txt, char * txt_out) +{ + uint32_t txt_length = 0; + uint32_t index_current, idx_next, idx_previous, i, j; + uint32_t * ch_enc; + uint32_t * ch_fin; + char * txt_out_temp; + + txt_length = _lv_txt_get_encoded_length(txt); + + ch_enc = (uint32_t *)lv_mem_alloc(sizeof(uint32_t) * (txt_length + 1)); + ch_fin = (uint32_t *)lv_mem_alloc(sizeof(uint32_t) * (txt_length + 1)); + + i = 0; + j = 0; + while(j < txt_length) + ch_enc[j++] = _lv_txt_encoded_next(txt, &i); + + ch_enc[j] = 0; + + i = 0; + j = 0; + idx_previous = LV_UNDEF_ARABIC_PERSIAN_CHARS; + while(i < txt_length) { + index_current = lv_ap_get_char_index(ch_enc[i]); + idx_next = lv_ap_get_char_index(ch_enc[i + 1]); + + if(lv_txt_is_arabic_vowel(ch_enc[i])) { // Current character is a vowel + ch_fin[j] = ch_enc[i]; + i++; + j++; + continue; // Skip this character + } + else if(lv_txt_is_arabic_vowel(ch_enc[i + 1])) { // Next character is a vowel + idx_next = lv_ap_get_char_index(ch_enc[i + 2]); // Skip the vowel character to join with the character after it + } + + if(index_current == LV_UNDEF_ARABIC_PERSIAN_CHARS) { + ch_fin[j] = ch_enc[i]; + j++; + i++; + idx_previous = LV_UNDEF_ARABIC_PERSIAN_CHARS; + continue; + } + + uint8_t conjunction_to_previuse = (i == 0 || + idx_previous == LV_UNDEF_ARABIC_PERSIAN_CHARS) ? 0 : ap_chars_map[idx_previous].ap_chars_conjunction.conj_to_next; + uint8_t conjunction_to_next = ((i == txt_length - 1) || + idx_next == LV_UNDEF_ARABIC_PERSIAN_CHARS) ? 0 : ap_chars_map[idx_next].ap_chars_conjunction.conj_to_previous; + + uint32_t lam_alef = lv_txt_lam_alef(index_current, idx_next); + if(lam_alef) { + if(conjunction_to_previuse) { + lam_alef ++; + } + ch_fin[j] = lam_alef; + idx_previous = LV_UNDEF_ARABIC_PERSIAN_CHARS; + i += 2; + j++; + continue; + } + + if(conjunction_to_previuse && conjunction_to_next) + ch_fin[j] = ap_chars_map[index_current].char_end_form + ap_chars_map[index_current].char_middle_form_offset; + else if(!conjunction_to_previuse && conjunction_to_next) + ch_fin[j] = ap_chars_map[index_current].char_end_form + ap_chars_map[index_current].char_begining_form_offset; + else if(conjunction_to_previuse && !conjunction_to_next) + ch_fin[j] = ap_chars_map[index_current].char_end_form; + else + ch_fin[j] = ap_chars_map[index_current].char_end_form + ap_chars_map[index_current].char_isolated_form_offset; + idx_previous = index_current; + i++; + j++; + } + ch_fin[j] = 0; + for(i = 0; i < txt_length; i++) + ch_enc[i] = 0; + for(i = 0; i < j; i++) + ch_enc[i] = ch_fin[i]; + lv_mem_free(ch_fin); + + txt_out_temp = txt_out; + i = 0; + + while(i < txt_length) { + if(ch_enc[i] < 0x80) { + *(txt_out_temp++) = ch_enc[i] & 0xFF; + } + else if(ch_enc[i] < 0x0800) { + *(txt_out_temp++) = ((ch_enc[i] >> 6) & 0x1F) | 0xC0; + *(txt_out_temp++) = ((ch_enc[i] >> 0) & 0x3F) | 0x80; + } + else if(ch_enc[i] < 0x010000) { + *(txt_out_temp++) = ((ch_enc[i] >> 12) & 0x0F) | 0xE0; + *(txt_out_temp++) = ((ch_enc[i] >> 6) & 0x3F) | 0x80; + *(txt_out_temp++) = ((ch_enc[i] >> 0) & 0x3F) | 0x80; + } + else if(ch_enc[i] < 0x110000) { + *(txt_out_temp++) = ((ch_enc[i] >> 18) & 0x07) | 0xF0; + *(txt_out_temp++) = ((ch_enc[i] >> 12) & 0x3F) | 0x80; + *(txt_out_temp++) = ((ch_enc[i] >> 6) & 0x3F) | 0x80; + *(txt_out_temp++) = ((ch_enc[i] >> 0) & 0x3F) | 0x80; + } + + i++; + } + *(txt_out_temp) = '\0'; + lv_mem_free(ch_enc); +} +/********************** +* STATIC FUNCTIONS +**********************/ + +static uint32_t lv_ap_get_char_index(uint16_t c) +{ + for(uint8_t i = 0; ap_chars_map[i].char_end_form; i++) { + if(c == (ap_chars_map[i].char_offset + LV_AP_ALPHABET_BASE_CODE)) + return i; + else if(c == ap_chars_map[i].char_end_form //is it an End form + || c == (ap_chars_map[i].char_end_form + ap_chars_map[i].char_begining_form_offset) //is it a Beginning form + || c == (ap_chars_map[i].char_end_form + ap_chars_map[i].char_middle_form_offset) //is it a middle form + || c == (ap_chars_map[i].char_end_form + ap_chars_map[i].char_isolated_form_offset)) { //is it an isolated form + return i; + } + } + return LV_UNDEF_ARABIC_PERSIAN_CHARS; +} + +static uint32_t lv_txt_lam_alef(uint32_t ch_curr, uint32_t ch_next) +{ + uint32_t ch_code = 0; + if(ap_chars_map[ch_curr].char_offset != 34) { + return 0; + } + if(ch_next == LV_UNDEF_ARABIC_PERSIAN_CHARS) { + return 0; + } + ch_code = ap_chars_map[ch_next].char_offset + LV_AP_ALPHABET_BASE_CODE; + if(ch_code == 0x0622) { + return 0xFEF5; // (lam-alef) mad + } + if(ch_code == 0x0623) { + return 0xFEF7; // (lam-alef) top hamza + } + if(ch_code == 0x0625) { + return 0xFEF9; // (lam-alef) bot hamza + } + if(ch_code == 0x0627) { + return 0xFEFB; // (lam-alef) alef + } + return 0; +} + +static bool lv_txt_is_arabic_vowel(uint16_t c) +{ + return (c >= 0x064B) && (c <= 0x0652); +} + +#endif diff --git a/lib/lvgl/src/misc/lv_txt_ap.h b/lib/lvgl/src/misc/lv_txt_ap.h new file mode 100644 index 00000000..e2d94b8d --- /dev/null +++ b/lib/lvgl/src/misc/lv_txt_ap.h @@ -0,0 +1,49 @@ +/** + * @file lv_txt_ap.h + * + */ + +#ifndef LV_TXT_AP_H +#define LV_TXT_AP_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ +#include +#include "lv_txt.h" +#include "../draw/lv_draw.h" + +#if LV_USE_ARABIC_PERSIAN_CHARS == 1 + +/********************* + * DEFINES + *********************/ + +#define LV_UNDEF_ARABIC_PERSIAN_CHARS (UINT32_MAX) +#define LV_AP_ALPHABET_BASE_CODE 0x0622 +#define LV_AP_END_CHARS_LIST {0,0,0,0,0,{0,0}} +/********************** + * TYPEDEFS + **********************/ + +/********************** + * GLOBAL PROTOTYPES + **********************/ +uint32_t _lv_txt_ap_calc_bytes_cnt(const char * txt); +void _lv_txt_ap_proc(const char * txt, char * txt_out); + +/********************** + * MACROS + **********************/ + +#endif // LV_USE_ARABIC_PERSIAN_CHARS + +#ifdef __cplusplus +} /*extern "C"*/ +#endif + +#endif /*LV_TXT_AP_H*/ diff --git a/lib/lvgl/src/misc/lv_types.h b/lib/lvgl/src/misc/lv_types.h new file mode 100644 index 00000000..84aee103 --- /dev/null +++ b/lib/lvgl/src/misc/lv_types.h @@ -0,0 +1,94 @@ +/** + * @file lv_types.h + * + */ + +#ifndef LV_TYPES_H +#define LV_TYPES_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ +#include + +/********************* + * DEFINES + *********************/ + +// If __UINTPTR_MAX__ or UINTPTR_MAX are available, use them to determine arch size +#if defined(__UINTPTR_MAX__) && __UINTPTR_MAX__ > 0xFFFFFFFF +#define LV_ARCH_64 + +#elif defined(UINTPTR_MAX) && UINTPTR_MAX > 0xFFFFFFFF +#define LV_ARCH_64 + +// Otherwise use compiler-dependent means to determine arch size +#elif defined(_WIN64) || defined(__x86_64__) || defined(__ppc64__) || defined (__aarch64__) +#define LV_ARCH_64 + +#endif + +/********************** + * TYPEDEFS + **********************/ + +/** + * LVGL error codes. + */ +enum { + LV_RES_INV = 0, /*Typically indicates that the object is deleted (become invalid) in the action + function or an operation was failed*/ + LV_RES_OK, /*The object is valid (no deleted) after the action*/ +}; +typedef uint8_t lv_res_t; + +#if defined(__cplusplus) || __STDC_VERSION__ >= 199901L +// If c99 or newer, use the definition of uintptr_t directly from +typedef uintptr_t lv_uintptr_t; + +#else + +// Otherwise, use the arch size determination +#ifdef LV_ARCH_64 +typedef uint64_t lv_uintptr_t; +#else +typedef uint32_t lv_uintptr_t; +#endif + +#endif + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +/********************** + * MACROS + **********************/ + +#define LV_UNUSED(x) ((void)x) + +#define _LV_CONCAT(x, y) x ## y +#define LV_CONCAT(x, y) _LV_CONCAT(x, y) + +#define _LV_CONCAT3(x, y, z) x ## y ## z +#define LV_CONCAT3(x, y, z) _LV_CONCAT3(x, y, z) + +#if defined(PYCPARSER) || defined(__CC_ARM) +#define LV_FORMAT_ATTRIBUTE(fmtstr, vararg) +#elif defined(__GNUC__) && ((__GNUC__ == 4 && __GNUC_MINOR__ >= 4) || __GNUC__ > 4) +#define LV_FORMAT_ATTRIBUTE(fmtstr, vararg) __attribute__((format(gnu_printf, fmtstr, vararg))) +#elif (defined(__clang__) || defined(__GNUC__) || defined(__GNUG__)) +#define LV_FORMAT_ATTRIBUTE(fmtstr, vararg) __attribute__((format(printf, fmtstr, vararg))) +#else +#define LV_FORMAT_ATTRIBUTE(fmtstr, vararg) +#endif + +#ifdef __cplusplus +} /*extern "C"*/ +#endif + +#endif /*LV_TYPES_H*/ diff --git a/lib/lvgl/src/misc/lv_utils.c b/lib/lvgl/src/misc/lv_utils.c new file mode 100644 index 00000000..e17a231d --- /dev/null +++ b/lib/lvgl/src/misc/lv_utils.c @@ -0,0 +1,79 @@ +/** + * @file lv_utils.c + * + */ + +/********************* + * INCLUDES + *********************/ +#include + +#include "lv_utils.h" + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ + +/********************** + * STATIC VARIABLES + **********************/ + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +/** Searches base[0] to base[n - 1] for an item that matches *key. + * + * @note The function cmp must return negative if its first + * argument (the search key) is less than its second (a table entry), + * zero if equal, and positive if greater. + * + * @note Items in the array must be in ascending order. + * + * @param key Pointer to item being searched for + * @param base Pointer to first element to search + * @param n Number of elements + * @param size Size of each element + * @param cmp Pointer to comparison function (see #unicode_list_compare as a comparison function + * example) + * + * @return a pointer to a matching item, or NULL if none exists. + */ +void * _lv_utils_bsearch(const void * key, const void * base, uint32_t n, uint32_t size, + int32_t (*cmp)(const void * pRef, const void * pElement)) +{ + const char * middle; + int32_t c; + + for(middle = base; n != 0;) { + middle += (n / 2) * size; + if((c = (*cmp)(key, middle)) > 0) { + n = (n / 2) - ((n & 1) == 0); + base = (middle += size); + } + else if(c < 0) { + n /= 2; + middle = base; + } + else { + return (char *)middle; + } + } + return NULL; +} + +/********************** + * STATIC FUNCTIONS + **********************/ diff --git a/lib/lvgl/src/misc/lv_utils.h b/lib/lvgl/src/misc/lv_utils.h new file mode 100644 index 00000000..84d2bb95 --- /dev/null +++ b/lib/lvgl/src/misc/lv_utils.h @@ -0,0 +1,58 @@ +/** + * @file lv_utils.h + * + */ + +#ifndef LV_UTILS_H +#define LV_UTILS_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ +#include + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +/** Searches base[0] to base[n - 1] for an item that matches *key. + * + * @note The function cmp must return negative if it's first + * argument (the search key) is less that it's second (a table entry), + * zero if equal, and positive if greater. + * + * @note Items in the array must be in ascending order. + * + * @param key Pointer to item being searched for + * @param base Pointer to first element to search + * @param n Number of elements + * @param size Size of each element + * @param cmp Pointer to comparison function (see #unicode_list_compare as a comparison function + * example) + * + * @return a pointer to a matching item, or NULL if none exists. + */ +void * _lv_utils_bsearch(const void * key, const void * base, uint32_t n, uint32_t size, + int32_t (*cmp)(const void * pRef, const void * pElement)); + +/********************** + * MACROS + **********************/ + +#ifdef __cplusplus +} /*extern "C"*/ +#endif + +#endif -- cgit v1.2.3