perf: Enable 512KB LVGL image cache on PSRAM and move modified lvgl to local components
This commit is contained in:
@@ -0,0 +1,17 @@
|
||||
/**
|
||||
* @file lv_cache_clazz.h
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef LV_CACHE_CLAZZ_H
|
||||
#define LV_CACHE_CLAZZ_H
|
||||
|
||||
|
||||
/*********************
|
||||
* INCLUDES
|
||||
*********************/
|
||||
|
||||
#include "lv_cache_lru_rb.h"
|
||||
#include "lv_cache_lru_ll.h"
|
||||
|
||||
#endif //LV_CACHE_CLAZZ_H
|
||||
@@ -0,0 +1,443 @@
|
||||
/**
|
||||
* @file lv_cache_lru_ll.c
|
||||
*
|
||||
*/
|
||||
|
||||
/*********************************\
|
||||
* *
|
||||
* ┏ ━ ━ ━ ━ ━ ━ ━ ━ ━ ━ ━ ━ ━ ┓ *
|
||||
* ┌ ─ ─ ─ ┐ *
|
||||
* ┃ Cache insert ┃ *
|
||||
* │Hitting│ head *
|
||||
* ┃ ─ ─ ─ ─ ┃ *
|
||||
* ┌─────┐ *
|
||||
* ┃ │ B │ ┃ *
|
||||
* └──▲──┘ *
|
||||
* ┃ │ ┃ *
|
||||
* ┌──┴──┐ *
|
||||
* ┃ │ E │ ┃ *
|
||||
* └──▲──┘ *
|
||||
* ┃ │ ┃ *
|
||||
* ┌──┴──┐ *
|
||||
* ┃ │ A │ ┌ ─ ─ ─ ─ ─ ┐ ┃ *
|
||||
* └──▲──┘ LRU *
|
||||
* ┃ │ │ Cache │ ┃ *
|
||||
* ┌──┴──┐ ─ ─ ─ ─ ─ ─ *
|
||||
* ┃ │ D │ ┃ *
|
||||
* └──▲──┘ *
|
||||
* ┃ │ ┃ *
|
||||
* ┌──┴──┐ *
|
||||
* ┃ │ C │ ┃ *
|
||||
* └──▲──┘ *
|
||||
* ┃ ┌ ─ ─│─ ─ ┐ ┃ *
|
||||
* ┌──┴──┐ *
|
||||
* ┃ │ │ F │ │ ┃ *
|
||||
* └─────┘ *
|
||||
* ┃ └ ─ ─ ─ ─ ┘ ┃ *
|
||||
* remove *
|
||||
* ┃ tail ┃ *
|
||||
* ━ ━ ━ ━ ━ ━ ━ ━ ━ ━ ━ ━ ━ ━ *
|
||||
* *
|
||||
\*********************************/
|
||||
|
||||
/*********************
|
||||
* INCLUDES
|
||||
*********************/
|
||||
|
||||
#include "lv_cache_lru_ll.h"
|
||||
#include "../lv_cache_entry.h"
|
||||
#include "../../../stdlib/lv_sprintf.h"
|
||||
#include "../../../stdlib/lv_string.h"
|
||||
#include "../../lv_ll.h"
|
||||
|
||||
#include "../../lv_iter.h"
|
||||
#include "../../lv_assert.h"
|
||||
|
||||
/*********************
|
||||
* DEFINES
|
||||
*********************/
|
||||
|
||||
/**********************
|
||||
* TYPEDEFS
|
||||
**********************/
|
||||
|
||||
typedef uint32_t (get_data_size_cb_t)(const void * data);
|
||||
|
||||
struct _lv_cache_lru_ll_t {
|
||||
lv_cache_t cache;
|
||||
lv_ll_t ll;
|
||||
get_data_size_cb_t * get_data_size_cb;
|
||||
};
|
||||
|
||||
typedef struct _lv_cache_lru_ll_t lv_cache_lru_ll_t;
|
||||
|
||||
/**********************
|
||||
* STATIC PROTOTYPES
|
||||
**********************/
|
||||
|
||||
static void * alloc_cb(void);
|
||||
static bool init_cnt_cb(lv_cache_t * cache);
|
||||
static bool init_size_cb(lv_cache_t * cache);
|
||||
static void destroy_cb(lv_cache_t * cache, void * user_data);
|
||||
|
||||
static lv_cache_entry_t * get_cb(lv_cache_t * cache, const void * key, void * user_data);
|
||||
static lv_cache_entry_t * add_cb(lv_cache_t * cache, const void * key, void * user_data);
|
||||
static void remove_cb(lv_cache_t * cache, lv_cache_entry_t * entry, void * user_data);
|
||||
static void drop_cb(lv_cache_t * cache, const void * key, void * user_data);
|
||||
static void drop_all_cb(lv_cache_t * cache, void * user_data);
|
||||
static lv_cache_entry_t * get_victim_cb(lv_cache_t * cache, void * user_data);
|
||||
static lv_cache_reserve_cond_res_t reserve_cond_cb(lv_cache_t * cache, const void * key, size_t reserved_size,
|
||||
void * user_data);
|
||||
|
||||
static void * alloc_new_node(lv_cache_lru_ll_t * lru, void * key, void * user_data);
|
||||
|
||||
static uint32_t cnt_get_data_size_cb(const void * data);
|
||||
static uint32_t size_get_data_size_cb(const void * data);
|
||||
|
||||
static lv_iter_t * cache_iter_create_cb(lv_cache_t * cache);
|
||||
static lv_result_t cache_iter_next_cb(void * instance, void * context, void * elem);
|
||||
|
||||
/**********************
|
||||
* GLOBAL VARIABLES
|
||||
**********************/
|
||||
const lv_cache_class_t lv_cache_class_lru_ll_count = {
|
||||
.alloc_cb = alloc_cb,
|
||||
.init_cb = init_cnt_cb,
|
||||
.destroy_cb = destroy_cb,
|
||||
|
||||
.get_cb = get_cb,
|
||||
.add_cb = add_cb,
|
||||
.remove_cb = remove_cb,
|
||||
.drop_cb = drop_cb,
|
||||
.drop_all_cb = drop_all_cb,
|
||||
.get_victim_cb = get_victim_cb,
|
||||
.reserve_cond_cb = reserve_cond_cb,
|
||||
.iter_create_cb = cache_iter_create_cb,
|
||||
};
|
||||
|
||||
const lv_cache_class_t lv_cache_class_lru_ll_size = {
|
||||
.alloc_cb = alloc_cb,
|
||||
.init_cb = init_size_cb,
|
||||
.destroy_cb = destroy_cb,
|
||||
|
||||
.get_cb = get_cb,
|
||||
.add_cb = add_cb,
|
||||
.remove_cb = remove_cb,
|
||||
.drop_cb = drop_cb,
|
||||
.drop_all_cb = drop_all_cb,
|
||||
.get_victim_cb = get_victim_cb,
|
||||
.reserve_cond_cb = reserve_cond_cb,
|
||||
.iter_create_cb = cache_iter_create_cb,
|
||||
};
|
||||
|
||||
/**********************
|
||||
* STATIC VARIABLES
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* MACROS
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* GLOBAL FUNCTIONS
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* STATIC FUNCTIONS
|
||||
**********************/
|
||||
|
||||
static void * alloc_new_node(lv_cache_lru_ll_t * lru, void * key, void * user_data)
|
||||
{
|
||||
LV_UNUSED(user_data);
|
||||
|
||||
LV_ASSERT_NULL(lru);
|
||||
LV_ASSERT_NULL(key);
|
||||
|
||||
if(lru == NULL || key == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void * new_node = lv_ll_ins_head(&lru->ll);
|
||||
if(new_node == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
lv_memcpy(new_node, key, lru->cache.node_size);
|
||||
lv_cache_entry_t * entry = lv_cache_entry_get_entry(new_node, lru->cache.node_size);
|
||||
lv_cache_entry_init(entry, &lru->cache, lru->cache.node_size);
|
||||
return entry;
|
||||
}
|
||||
|
||||
static void * alloc_cb(void)
|
||||
{
|
||||
void * res = lv_malloc(sizeof(lv_cache_lru_ll_t));
|
||||
LV_ASSERT_MALLOC(res);
|
||||
if(res == NULL) {
|
||||
LV_LOG_ERROR("malloc failed");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
lv_memzero(res, sizeof(lv_cache_lru_ll_t));
|
||||
return res;
|
||||
}
|
||||
|
||||
static bool init_cnt_cb(lv_cache_t * cache)
|
||||
{
|
||||
lv_cache_lru_ll_t * lru = (lv_cache_lru_ll_t *)cache;
|
||||
|
||||
LV_ASSERT_NULL(lru->cache.ops.compare_cb);
|
||||
LV_ASSERT_NULL(lru->cache.ops.free_cb);
|
||||
LV_ASSERT(lru->cache.node_size > 0);
|
||||
|
||||
if(lru->cache.node_size <= 0 || lru->cache.ops.compare_cb == NULL || lru->cache.ops.free_cb == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
lv_ll_init(&lru->ll, lv_cache_entry_get_size(lru->cache.node_size));
|
||||
lru->get_data_size_cb = cnt_get_data_size_cb;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool init_size_cb(lv_cache_t * cache)
|
||||
{
|
||||
lv_cache_lru_ll_t * lru = (lv_cache_lru_ll_t *)cache;
|
||||
|
||||
LV_ASSERT_NULL(lru->cache.ops.compare_cb);
|
||||
LV_ASSERT_NULL(lru->cache.ops.free_cb);
|
||||
LV_ASSERT(lru->cache.node_size > 0);
|
||||
|
||||
if(lru->cache.node_size <= 0 || lru->cache.ops.compare_cb == NULL || lru->cache.ops.free_cb == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
lv_ll_init(&lru->ll, lv_cache_entry_get_size(lru->cache.node_size));
|
||||
lru->get_data_size_cb = size_get_data_size_cb;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void destroy_cb(lv_cache_t * cache, void * user_data)
|
||||
{
|
||||
LV_UNUSED(user_data);
|
||||
|
||||
lv_cache_lru_ll_t * lru = (lv_cache_lru_ll_t *)cache;
|
||||
|
||||
LV_ASSERT_NULL(lru);
|
||||
|
||||
if(lru == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
cache->clz->drop_all_cb(cache, user_data);
|
||||
cache->size = 0;
|
||||
}
|
||||
|
||||
static lv_cache_entry_t * get_cb(lv_cache_t * cache, const void * key, void * user_data)
|
||||
{
|
||||
LV_UNUSED(user_data);
|
||||
|
||||
lv_cache_lru_ll_t * lru = (lv_cache_lru_ll_t *)cache;
|
||||
|
||||
LV_ASSERT_NULL(lru);
|
||||
LV_ASSERT_NULL(key);
|
||||
|
||||
if(lru == NULL || key == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Linear search */
|
||||
void * node = NULL;
|
||||
LV_LL_READ(&lru->ll, node) {
|
||||
if(lru->cache.ops.compare_cb(node, key) == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
/*cache hit*/
|
||||
if(node) {
|
||||
void * head = lv_ll_get_head(&lru->ll);
|
||||
lv_ll_move_before(&lru->ll, node, head);
|
||||
|
||||
lv_cache_entry_t * entry = lv_cache_entry_get_entry(node, cache->node_size);
|
||||
return entry;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static lv_cache_entry_t * add_cb(lv_cache_t * cache, const void * key, void * user_data)
|
||||
{
|
||||
LV_UNUSED(user_data);
|
||||
|
||||
lv_cache_lru_ll_t * lru = (lv_cache_lru_ll_t *)cache;
|
||||
|
||||
LV_ASSERT_NULL(lru);
|
||||
LV_ASSERT_NULL(key);
|
||||
|
||||
if(lru == NULL || key == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
lv_cache_entry_t * entry = alloc_new_node(lru, (void *)key, user_data);
|
||||
if(entry == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
cache->size += lru->get_data_size_cb(key);
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
static void remove_cb(lv_cache_t * cache, lv_cache_entry_t * entry, void * user_data)
|
||||
{
|
||||
LV_UNUSED(user_data);
|
||||
|
||||
lv_cache_lru_ll_t * lru = (lv_cache_lru_ll_t *)cache;
|
||||
|
||||
LV_ASSERT_NULL(lru);
|
||||
LV_ASSERT_NULL(entry);
|
||||
|
||||
if(lru == NULL || entry == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
void * node = lv_cache_entry_get_data(entry);
|
||||
|
||||
lv_ll_remove(&lru->ll, node);
|
||||
|
||||
cache->size -= lru->get_data_size_cb(node);
|
||||
}
|
||||
|
||||
static void drop_cb(lv_cache_t * cache, const void * key, void * user_data)
|
||||
{
|
||||
lv_cache_lru_ll_t * lru = (lv_cache_lru_ll_t *)cache;
|
||||
|
||||
LV_ASSERT_NULL(lru);
|
||||
LV_ASSERT_NULL(key);
|
||||
|
||||
if(lru == NULL || key == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
void * node = cache->clz->get_cb(cache, key, user_data);
|
||||
if(node == NULL) {
|
||||
return;
|
||||
}
|
||||
lv_ll_remove(&lru->ll, node);
|
||||
|
||||
lru->cache.ops.free_cb(node, user_data);
|
||||
cache->size -= lru->get_data_size_cb(node);
|
||||
|
||||
lv_cache_entry_t * entry = lv_cache_entry_get_entry(node, cache->node_size);
|
||||
lv_cache_entry_delete(entry);
|
||||
}
|
||||
|
||||
static void drop_all_cb(lv_cache_t * cache, void * user_data)
|
||||
{
|
||||
lv_cache_lru_ll_t * lru = (lv_cache_lru_ll_t *)cache;
|
||||
|
||||
LV_ASSERT_NULL(lru);
|
||||
|
||||
if(lru == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t used_cnt = 0;
|
||||
void * node;
|
||||
LV_LL_READ(&lru->ll, node) {
|
||||
/*free user handled data and do other clean up*/
|
||||
lv_cache_entry_t * entry = lv_cache_entry_get_entry(node, cache->node_size);
|
||||
if(lv_cache_entry_get_ref(entry) == 0) {
|
||||
lru->cache.ops.free_cb(node, user_data);
|
||||
}
|
||||
else {
|
||||
LV_LOG_WARN("entry (%p) is still referenced (%" LV_PRId32 ")", (void *)entry, lv_cache_entry_get_ref(entry));
|
||||
used_cnt++;
|
||||
}
|
||||
}
|
||||
if(used_cnt > 0) {
|
||||
LV_LOG_WARN("%" LV_PRId32 " entries are still referenced", used_cnt);
|
||||
}
|
||||
|
||||
lv_ll_clear(&lru->ll);
|
||||
|
||||
cache->size = 0;
|
||||
}
|
||||
|
||||
static lv_cache_entry_t * get_victim_cb(lv_cache_t * cache, void * user_data)
|
||||
{
|
||||
LV_UNUSED(user_data);
|
||||
|
||||
lv_cache_lru_ll_t * lru = (lv_cache_lru_ll_t *)cache;
|
||||
|
||||
LV_ASSERT_NULL(lru);
|
||||
|
||||
void * tail;
|
||||
LV_LL_READ_BACK(&lru->ll, tail) {
|
||||
lv_cache_entry_t * entry = lv_cache_entry_get_entry(tail, cache->node_size);
|
||||
if(lv_cache_entry_get_ref(entry) == 0) {
|
||||
return entry;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static lv_cache_reserve_cond_res_t reserve_cond_cb(lv_cache_t * cache, const void * key, size_t reserved_size,
|
||||
void * user_data)
|
||||
{
|
||||
LV_UNUSED(user_data);
|
||||
|
||||
lv_cache_lru_ll_t * lru = (lv_cache_lru_ll_t *)cache;
|
||||
|
||||
LV_ASSERT_NULL(lru);
|
||||
|
||||
if(lru == NULL) {
|
||||
return LV_CACHE_RESERVE_COND_ERROR;
|
||||
}
|
||||
|
||||
uint32_t data_size = key ? lru->get_data_size_cb(key) : 0;
|
||||
if(data_size > lru->cache.max_size) {
|
||||
LV_LOG_ERROR("data size (%" LV_PRIu32 ") is larger than max size (%" LV_PRIu32 ")", data_size, lru->cache.max_size);
|
||||
return LV_CACHE_RESERVE_COND_TOO_LARGE;
|
||||
}
|
||||
|
||||
return cache->size + reserved_size + data_size > lru->cache.max_size
|
||||
? LV_CACHE_RESERVE_COND_NEED_VICTIM
|
||||
: LV_CACHE_RESERVE_COND_OK;
|
||||
}
|
||||
|
||||
static uint32_t cnt_get_data_size_cb(const void * data)
|
||||
{
|
||||
LV_UNUSED(data);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static uint32_t size_get_data_size_cb(const void * data)
|
||||
{
|
||||
lv_cache_slot_size_t * slot = (lv_cache_slot_size_t *)data;
|
||||
return slot->size;
|
||||
}
|
||||
|
||||
static lv_iter_t * cache_iter_create_cb(lv_cache_t * cache)
|
||||
{
|
||||
return lv_iter_create(cache, lv_cache_entry_get_size(cache->node_size), 0, cache_iter_next_cb);
|
||||
}
|
||||
|
||||
static lv_result_t cache_iter_next_cb(void * instance, void * context, void * elem)
|
||||
{
|
||||
lv_cache_lru_ll_t * lru = (lv_cache_lru_ll_t *)instance;
|
||||
void ** ll_node = context;
|
||||
|
||||
LV_ASSERT_NULL(ll_node);
|
||||
|
||||
if(*ll_node == NULL) *ll_node = lv_ll_get_head(&lru->ll);
|
||||
else *ll_node = lv_ll_get_next(&lru->ll, *ll_node);
|
||||
|
||||
void * node = *ll_node;
|
||||
|
||||
if(node == NULL) return LV_RESULT_INVALID;
|
||||
|
||||
lv_memcpy(elem, node, lv_cache_entry_get_size(lru->cache.node_size));
|
||||
|
||||
return LV_RESULT_OK;
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
/**
|
||||
* @file lv_cache_lru_ll.h
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef LV_CACHE_LRU_LL_H
|
||||
#define LV_CACHE_LRU_LL_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*********************
|
||||
* INCLUDES
|
||||
*********************/
|
||||
|
||||
#include "../lv_cache_private.h"
|
||||
|
||||
/*********************
|
||||
* DEFINES
|
||||
*********************/
|
||||
|
||||
/**********************
|
||||
* TYPEDEFS
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* GLOBAL PROTOTYPES
|
||||
**********************/
|
||||
|
||||
/*************************
|
||||
* GLOBAL VARIABLES
|
||||
*************************/
|
||||
LV_ATTRIBUTE_EXTERN_DATA extern const lv_cache_class_t lv_cache_class_lru_ll_count;
|
||||
LV_ATTRIBUTE_EXTERN_DATA extern const lv_cache_class_t lv_cache_class_lru_ll_size;
|
||||
/**********************
|
||||
* MACROS
|
||||
**********************/
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /*extern "C"*/
|
||||
#endif
|
||||
|
||||
#endif /*LV_CACHE_LRU_LL_H*/
|
||||
@@ -0,0 +1,497 @@
|
||||
/**
|
||||
* @file lv_cache_lru_rb.c
|
||||
*
|
||||
*/
|
||||
|
||||
/***************************************************************************\
|
||||
* *
|
||||
* ┏ ━ ━ ━ ━ ━ ━ ━ ━ ━ ━ ━ ━ ━ ┓ *
|
||||
* ┏ ━ ━ ━ ━ ━ ━ ━ ━ ━ ━ ━ ━ ━ ━ ━ ━ ━ ━ ━ ┌ ─ ─ ─ ┐ *
|
||||
* ┌ ─ ─ ─ ─ ─ ─ ─ ┃ ┃ Cache insert ┃ *
|
||||
* ┃ RB Tree │ │Hitting│ head *
|
||||
* └ ─ ─ ─ ─ ─ ─ ─ ┃ ┃ ─ ─ ─ ─ ┃ *
|
||||
* ┃ ┌─┬─┬─┬─┐ ┌─────┐ *
|
||||
* ┌──│◄│B│►│ │─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─┃─ ─ ╋ ─ ─▶│ B │ ┃ *
|
||||
* ┃ │ └─┴─┴─┴─┘ └──▲──┘ *
|
||||
* │ │ ┃ ┃ │ ┃ *
|
||||
* ┃ │ │ ┌──┴──┐ *
|
||||
* │ └──────┐ ┌ ─┃─ ─ ╋ ─ ─▶│ E │ ┃ *
|
||||
* ┃ ▼ ┌ ─ ─│─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┐ └──▲──┘ *
|
||||
* ┌─┬─┬─┬─┐ ▼ │ ┃ ┃ │ ┃ *
|
||||
* ┃│◄│A│►│ │─ ─ ┘ ┌─┬─┬─┬─┐ │ ┌──┴──┐ *
|
||||
* └─┴─┴─┴─┘ ┌───│◄│D│►│ │─ ─ ─ ─ ─ ─│─ ╋ ┐ ┃ ─ ▶│ A │ ┌ ─ ─ ─ ─ ─ ┐ ┃ *
|
||||
* ┃ │ └─┴─┴─┴─┘ └──▲──┘ LRU *
|
||||
* │ │ │ ┃ │ ┃ │ │ Cache │ ┃ *
|
||||
* ┃ ▼ └──────┐ ┌──┴──┐ ─ ─ ─ ─ ─ ─ *
|
||||
* ┌─┬─┬─┬─┐ ▼ │ ┃ └ ─┃─ ─ ▶│ D │ ┃ *
|
||||
* ┃ │◄│C│►│ │─ ─ ┌─┬─┬─┬─┐ └──▲──┘ *
|
||||
* └─┴─┴─┴─┘ │ │◄│E│►│ │─ ┘ ┃ ┃ │ ┃ *
|
||||
* ┃ └─┴─┴─┴─┘ ┌──┴──┐ *
|
||||
* │ │ ─ ╋ ─ ─┃─ ─ ▶│ C │ ┃ *
|
||||
* ┃ ─ ─ ─ ─ ┼ ─ ─ ┘ └──▲──┘ *
|
||||
* ▼ ┃ ┃ ┌ ─ ─│─ ─ ┐ ┃ *
|
||||
* ┃ ┌─┬─┬─┬─┐ ┌──┴──┐ *
|
||||
* │◄│F│►│ │─ ─┃─ ─ ╋ ─ ┼▶│ F │ │ ┃ *
|
||||
* ┃ └─┴─┴─┴─┘ └─────┘ *
|
||||
* ┃ ┃ └ ─ ─ ─ ─ ┘ ┃ *
|
||||
* ┃ remove *
|
||||
* ┃ ┃ tail ┃ *
|
||||
* ┗ ━ ━ ━ ━ ━ ━ ━ ━ ━ ━ ━ ━ ━ ━ ━ ━ ━ ━ ━ ━ ━ ━ ━ ━ ━ ━ ━ ━ ━ ━ ━ ━ ━ *
|
||||
* *
|
||||
\***************************************************************************/
|
||||
|
||||
/*********************
|
||||
* INCLUDES
|
||||
*********************/
|
||||
|
||||
#include "lv_cache_lru_rb.h"
|
||||
#include "../lv_cache_entry.h"
|
||||
#include "../../../stdlib/lv_sprintf.h"
|
||||
#include "../../../stdlib/lv_string.h"
|
||||
#include "../../lv_ll.h"
|
||||
#include "../../lv_rb_private.h"
|
||||
#include "../../lv_rb.h"
|
||||
#include "../../lv_iter.h"
|
||||
|
||||
/*********************
|
||||
* DEFINES
|
||||
*********************/
|
||||
|
||||
/**********************
|
||||
* TYPEDEFS
|
||||
**********************/
|
||||
typedef uint32_t (get_data_size_cb_t)(const void * data);
|
||||
|
||||
struct _lv_lru_rb_t {
|
||||
lv_cache_t cache;
|
||||
|
||||
lv_rb_t rb;
|
||||
lv_ll_t ll;
|
||||
|
||||
get_data_size_cb_t * get_data_size_cb;
|
||||
};
|
||||
typedef struct _lv_lru_rb_t lv_lru_rb_t_;
|
||||
/**********************
|
||||
* STATIC PROTOTYPES
|
||||
**********************/
|
||||
|
||||
static void * alloc_cb(void);
|
||||
static bool init_cnt_cb(lv_cache_t * cache);
|
||||
static bool init_size_cb(lv_cache_t * cache);
|
||||
static void destroy_cb(lv_cache_t * cache, void * user_data);
|
||||
|
||||
static lv_cache_entry_t * get_cb(lv_cache_t * cache, const void * key, void * user_data);
|
||||
static lv_cache_entry_t * add_cb(lv_cache_t * cache, const void * key, void * user_data);
|
||||
static void remove_cb(lv_cache_t * cache, lv_cache_entry_t * entry, void * user_data);
|
||||
static void drop_cb(lv_cache_t * cache, const void * key, void * user_data);
|
||||
static void drop_all_cb(lv_cache_t * cache, void * user_data);
|
||||
static lv_cache_entry_t * get_victim_cb(lv_cache_t * cache, void * user_data);
|
||||
static lv_cache_reserve_cond_res_t reserve_cond_cb(lv_cache_t * cache, const void * key, size_t reserved_size,
|
||||
void * user_data);
|
||||
|
||||
static void * alloc_new_node(lv_lru_rb_t_ * lru, void * key, void * user_data);
|
||||
inline static void ** get_lru_node(lv_lru_rb_t_ * lru, lv_rb_node_t * node);
|
||||
|
||||
static uint32_t cnt_get_data_size_cb(const void * data);
|
||||
static uint32_t size_get_data_size_cb(const void * data);
|
||||
|
||||
static lv_iter_t * cache_iter_create_cb(lv_cache_t * cache);
|
||||
static lv_result_t cache_iter_next_cb(void * instance, void * context, void * elem);
|
||||
|
||||
/**********************
|
||||
* GLOBAL VARIABLES
|
||||
**********************/
|
||||
const lv_cache_class_t lv_cache_class_lru_rb_count = {
|
||||
.alloc_cb = alloc_cb,
|
||||
.init_cb = init_cnt_cb,
|
||||
.destroy_cb = destroy_cb,
|
||||
|
||||
.get_cb = get_cb,
|
||||
.add_cb = add_cb,
|
||||
.remove_cb = remove_cb,
|
||||
.drop_cb = drop_cb,
|
||||
.drop_all_cb = drop_all_cb,
|
||||
.get_victim_cb = get_victim_cb,
|
||||
.reserve_cond_cb = reserve_cond_cb,
|
||||
.iter_create_cb = cache_iter_create_cb,
|
||||
};
|
||||
|
||||
const lv_cache_class_t lv_cache_class_lru_rb_size = {
|
||||
.alloc_cb = alloc_cb,
|
||||
.init_cb = init_size_cb,
|
||||
.destroy_cb = destroy_cb,
|
||||
|
||||
.get_cb = get_cb,
|
||||
.add_cb = add_cb,
|
||||
.remove_cb = remove_cb,
|
||||
.drop_cb = drop_cb,
|
||||
.drop_all_cb = drop_all_cb,
|
||||
.get_victim_cb = get_victim_cb,
|
||||
.reserve_cond_cb = reserve_cond_cb,
|
||||
.iter_create_cb = cache_iter_create_cb,
|
||||
};
|
||||
/**********************
|
||||
* STATIC VARIABLES
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* MACROS
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* GLOBAL FUNCTIONS
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* STATIC FUNCTIONS
|
||||
**********************/
|
||||
static void * alloc_new_node(lv_lru_rb_t_ * lru, void * key, void * user_data)
|
||||
{
|
||||
LV_UNUSED(user_data);
|
||||
|
||||
LV_ASSERT_NULL(lru);
|
||||
LV_ASSERT_NULL(key);
|
||||
|
||||
if(lru == NULL || key == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
lv_rb_node_t * node = lv_rb_insert(&lru->rb, key);
|
||||
if(node == NULL)
|
||||
goto FAILED_HANDLER2;
|
||||
|
||||
void * data = node->data;
|
||||
lv_cache_entry_t * entry = lv_cache_entry_get_entry(data, lru->cache.node_size);
|
||||
lv_memcpy(data, key, lru->cache.node_size);
|
||||
|
||||
void * lru_node = lv_ll_ins_head(&lru->ll);
|
||||
if(lru_node == NULL)
|
||||
goto FAILED_HANDLER1;
|
||||
|
||||
lv_memcpy(lru_node, &node, sizeof(void *));
|
||||
lv_memcpy(get_lru_node(lru, node), &lru_node, sizeof(void *));
|
||||
|
||||
lv_cache_entry_init(entry, &lru->cache, lru->cache.node_size);
|
||||
goto FAILED_HANDLER2;
|
||||
|
||||
FAILED_HANDLER1:
|
||||
lv_rb_drop_node(&lru->rb, node);
|
||||
node = NULL;
|
||||
FAILED_HANDLER2:
|
||||
return node;
|
||||
}
|
||||
|
||||
inline static void ** get_lru_node(lv_lru_rb_t_ * lru, lv_rb_node_t * node)
|
||||
{
|
||||
return (void **)((char *)node->data + lru->rb.size - sizeof(void *));
|
||||
}
|
||||
|
||||
static void * alloc_cb(void)
|
||||
{
|
||||
void * res = lv_malloc(sizeof(lv_lru_rb_t_));
|
||||
LV_ASSERT_MALLOC(res);
|
||||
if(res == NULL) {
|
||||
LV_LOG_ERROR("malloc failed");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
lv_memzero(res, sizeof(lv_lru_rb_t_));
|
||||
return res;
|
||||
}
|
||||
|
||||
static bool init_cnt_cb(lv_cache_t * cache)
|
||||
{
|
||||
lv_lru_rb_t_ * lru = (lv_lru_rb_t_ *)cache;
|
||||
|
||||
LV_ASSERT_NULL(lru->cache.ops.compare_cb);
|
||||
LV_ASSERT_NULL(lru->cache.ops.free_cb);
|
||||
LV_ASSERT(lru->cache.node_size > 0);
|
||||
|
||||
if(lru->cache.node_size <= 0 || lru->cache.ops.compare_cb == NULL || lru->cache.ops.free_cb == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/*add void* to store the ll node pointer*/
|
||||
if(!lv_rb_init(&lru->rb, lru->cache.ops.compare_cb, lv_cache_entry_get_size(lru->cache.node_size) + sizeof(void *))) {
|
||||
return false;
|
||||
}
|
||||
lv_ll_init(&lru->ll, sizeof(void *));
|
||||
|
||||
lru->get_data_size_cb = cnt_get_data_size_cb;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool init_size_cb(lv_cache_t * cache)
|
||||
{
|
||||
lv_lru_rb_t_ * lru = (lv_lru_rb_t_ *)cache;
|
||||
|
||||
LV_ASSERT_NULL(lru->cache.ops.compare_cb);
|
||||
LV_ASSERT_NULL(lru->cache.ops.free_cb);
|
||||
LV_ASSERT(lru->cache.node_size > 0);
|
||||
|
||||
if(lru->cache.node_size <= 0 || lru->cache.ops.compare_cb == NULL || lru->cache.ops.free_cb == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/*add void* to store the ll node pointer*/
|
||||
if(!lv_rb_init(&lru->rb, lru->cache.ops.compare_cb, lv_cache_entry_get_size(lru->cache.node_size) + sizeof(void *))) {
|
||||
return false;
|
||||
}
|
||||
lv_ll_init(&lru->ll, sizeof(void *));
|
||||
|
||||
lru->get_data_size_cb = size_get_data_size_cb;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void destroy_cb(lv_cache_t * cache, void * user_data)
|
||||
{
|
||||
LV_UNUSED(user_data);
|
||||
|
||||
lv_lru_rb_t_ * lru = (lv_lru_rb_t_ *)cache;
|
||||
|
||||
LV_ASSERT_NULL(lru);
|
||||
|
||||
if(lru == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
cache->clz->drop_all_cb(cache, user_data);
|
||||
}
|
||||
|
||||
static lv_cache_entry_t * get_cb(lv_cache_t * cache, const void * key, void * user_data)
|
||||
{
|
||||
LV_UNUSED(user_data);
|
||||
|
||||
lv_lru_rb_t_ * lru = (lv_lru_rb_t_ *)cache;
|
||||
|
||||
LV_ASSERT_NULL(lru);
|
||||
LV_ASSERT_NULL(key);
|
||||
|
||||
if(lru == NULL || key == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*try the first ll node first*/
|
||||
void * head = lv_ll_get_head(&lru->ll);
|
||||
if(head) {
|
||||
lv_rb_node_t * node = *(lv_rb_node_t **)head;
|
||||
void * data = node->data;
|
||||
lv_cache_entry_t * entry = lv_cache_entry_get_entry(data, cache->node_size);
|
||||
if(lru->cache.ops.compare_cb(data, key) == 0) {
|
||||
return entry;
|
||||
}
|
||||
}
|
||||
|
||||
lv_rb_node_t * node = lv_rb_find(&lru->rb, key);
|
||||
/*cache hit*/
|
||||
if(node) {
|
||||
void * lru_node = *get_lru_node(lru, node);
|
||||
head = lv_ll_get_head(&lru->ll);
|
||||
lv_ll_move_before(&lru->ll, lru_node, head);
|
||||
|
||||
lv_cache_entry_t * entry = lv_cache_entry_get_entry(node->data, cache->node_size);
|
||||
return entry;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static lv_cache_entry_t * add_cb(lv_cache_t * cache, const void * key, void * user_data)
|
||||
{
|
||||
LV_UNUSED(user_data);
|
||||
|
||||
lv_lru_rb_t_ * lru = (lv_lru_rb_t_ *)cache;
|
||||
|
||||
LV_ASSERT_NULL(lru);
|
||||
LV_ASSERT_NULL(key);
|
||||
|
||||
if(lru == NULL || key == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
lv_rb_node_t * new_node = alloc_new_node(lru, (void *)key, user_data);
|
||||
if(new_node == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
lv_cache_entry_t * entry = lv_cache_entry_get_entry(new_node->data, cache->node_size);
|
||||
|
||||
cache->size += lru->get_data_size_cb(key);
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
static void remove_cb(lv_cache_t * cache, lv_cache_entry_t * entry, void * user_data)
|
||||
{
|
||||
LV_UNUSED(user_data);
|
||||
|
||||
lv_lru_rb_t_ * lru = (lv_lru_rb_t_ *)cache;
|
||||
|
||||
LV_ASSERT_NULL(lru);
|
||||
LV_ASSERT_NULL(entry);
|
||||
|
||||
if(lru == NULL || entry == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
void * data = lv_cache_entry_get_data(entry);
|
||||
lv_rb_node_t * node = lv_rb_find(&lru->rb, data);
|
||||
if(node == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
void * lru_node = *get_lru_node(lru, node);
|
||||
lv_rb_remove_node(&lru->rb, node);
|
||||
lv_ll_remove(&lru->ll, lru_node);
|
||||
lv_free(lru_node);
|
||||
|
||||
cache->size -= lru->get_data_size_cb(data);
|
||||
}
|
||||
|
||||
static void drop_cb(lv_cache_t * cache, const void * key, void * user_data)
|
||||
{
|
||||
lv_lru_rb_t_ * lru = (lv_lru_rb_t_ *)cache;
|
||||
|
||||
LV_ASSERT_NULL(lru);
|
||||
LV_ASSERT_NULL(key);
|
||||
|
||||
if(lru == NULL || key == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
lv_rb_node_t * node = lv_rb_find(&lru->rb, key);
|
||||
if(node == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
void * data = node->data;
|
||||
|
||||
lru->cache.ops.free_cb(data, user_data);
|
||||
cache->size -= lru->get_data_size_cb(data);
|
||||
|
||||
lv_cache_entry_t * entry = lv_cache_entry_get_entry(data, cache->node_size);
|
||||
void * lru_node = *get_lru_node(lru, node);
|
||||
|
||||
lv_rb_remove_node(&lru->rb, node);
|
||||
lv_cache_entry_delete(entry);
|
||||
|
||||
lv_ll_remove(&lru->ll, lru_node);
|
||||
lv_free(lru_node);
|
||||
}
|
||||
|
||||
static void drop_all_cb(lv_cache_t * cache, void * user_data)
|
||||
{
|
||||
lv_lru_rb_t_ * lru = (lv_lru_rb_t_ *)cache;
|
||||
|
||||
LV_ASSERT_NULL(lru);
|
||||
|
||||
if(lru == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t used_cnt = 0;
|
||||
lv_rb_node_t ** node;
|
||||
LV_LL_READ(&lru->ll, node) {
|
||||
/*free user handled data and do other clean up*/
|
||||
void * search_key = (*node)->data;
|
||||
lv_cache_entry_t * entry = lv_cache_entry_get_entry(search_key, cache->node_size);
|
||||
if(lv_cache_entry_get_ref(entry) == 0) {
|
||||
lru->cache.ops.free_cb(search_key, user_data);
|
||||
}
|
||||
else {
|
||||
LV_LOG_WARN("entry (%p) is still referenced (%" LV_PRId32 ")", (void *)entry, lv_cache_entry_get_ref(entry));
|
||||
used_cnt++;
|
||||
}
|
||||
}
|
||||
if(used_cnt > 0) {
|
||||
LV_LOG_WARN("%" LV_PRId32 " entries are still referenced", used_cnt);
|
||||
}
|
||||
|
||||
lv_rb_destroy(&lru->rb);
|
||||
lv_ll_clear(&lru->ll);
|
||||
|
||||
cache->size = 0;
|
||||
}
|
||||
|
||||
static lv_cache_entry_t * get_victim_cb(lv_cache_t * cache, void * user_data)
|
||||
{
|
||||
LV_UNUSED(user_data);
|
||||
|
||||
lv_lru_rb_t_ * lru = (lv_lru_rb_t_ *)cache;
|
||||
|
||||
LV_ASSERT_NULL(lru);
|
||||
|
||||
lv_rb_node_t ** tail;
|
||||
LV_LL_READ_BACK(&lru->ll, tail) {
|
||||
lv_rb_node_t * tail_node = *tail;
|
||||
lv_cache_entry_t * entry = lv_cache_entry_get_entry(tail_node->data, cache->node_size);
|
||||
if(lv_cache_entry_get_ref(entry) == 0) {
|
||||
return entry;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static lv_cache_reserve_cond_res_t reserve_cond_cb(lv_cache_t * cache, const void * key, size_t reserved_size,
|
||||
void * user_data)
|
||||
{
|
||||
LV_UNUSED(user_data);
|
||||
|
||||
lv_lru_rb_t_ * lru = (lv_lru_rb_t_ *)cache;
|
||||
|
||||
LV_ASSERT_NULL(lru);
|
||||
|
||||
if(lru == NULL) {
|
||||
return LV_CACHE_RESERVE_COND_ERROR;
|
||||
}
|
||||
|
||||
uint32_t data_size = key ? lru->get_data_size_cb(key) : 0;
|
||||
if(data_size > lru->cache.max_size) {
|
||||
LV_LOG_ERROR("data size (%" LV_PRIu32 ") is larger than max size (%" LV_PRIu32 ")", data_size, lru->cache.max_size);
|
||||
return LV_CACHE_RESERVE_COND_TOO_LARGE;
|
||||
}
|
||||
|
||||
return cache->size + reserved_size + data_size > lru->cache.max_size
|
||||
? LV_CACHE_RESERVE_COND_NEED_VICTIM
|
||||
: LV_CACHE_RESERVE_COND_OK;
|
||||
}
|
||||
|
||||
static uint32_t cnt_get_data_size_cb(const void * data)
|
||||
{
|
||||
LV_UNUSED(data);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static uint32_t size_get_data_size_cb(const void * data)
|
||||
{
|
||||
lv_cache_slot_size_t * slot = (lv_cache_slot_size_t *)data;
|
||||
return slot->size;
|
||||
}
|
||||
|
||||
static lv_iter_t * cache_iter_create_cb(lv_cache_t * cache)
|
||||
{
|
||||
return lv_iter_create(cache, lv_cache_entry_get_size(cache->node_size), sizeof(void *), cache_iter_next_cb);
|
||||
}
|
||||
|
||||
static lv_result_t cache_iter_next_cb(void * instance, void * context, void * elem)
|
||||
{
|
||||
lv_lru_rb_t_ * lru = (lv_lru_rb_t_ *)instance;
|
||||
lv_rb_node_t *** ll_node = context;
|
||||
|
||||
LV_ASSERT_NULL(ll_node);
|
||||
|
||||
if(*ll_node == NULL) *ll_node = lv_ll_get_head(&lru->ll);
|
||||
else *ll_node = lv_ll_get_next(&lru->ll, *ll_node);
|
||||
|
||||
lv_rb_node_t ** node = *ll_node;
|
||||
|
||||
if(node == NULL) return LV_RESULT_INVALID;
|
||||
|
||||
uint32_t node_size = lru->cache.node_size;
|
||||
void * search_key = (*node)->data;
|
||||
lv_memcpy(elem, search_key, lv_cache_entry_get_size(node_size));
|
||||
|
||||
return LV_RESULT_OK;
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
/**
|
||||
* @file lv_cache_lru_rb.h
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef LV_CACHE_LRU_RB_H
|
||||
#define LV_CACHE_LRU_RB_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*********************
|
||||
* INCLUDES
|
||||
*********************/
|
||||
|
||||
#include "../lv_cache_private.h"
|
||||
|
||||
/*********************
|
||||
* DEFINES
|
||||
*********************/
|
||||
|
||||
/**********************
|
||||
* TYPEDEFS
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* GLOBAL PROTOTYPES
|
||||
**********************/
|
||||
|
||||
/*************************
|
||||
* GLOBAL VARIABLES
|
||||
*************************/
|
||||
LV_ATTRIBUTE_EXTERN_DATA extern const lv_cache_class_t lv_cache_class_lru_rb_count;
|
||||
LV_ATTRIBUTE_EXTERN_DATA extern const lv_cache_class_t lv_cache_class_lru_rb_size;
|
||||
/**********************
|
||||
* MACROS
|
||||
**********************/
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /*extern "C"*/
|
||||
#endif
|
||||
|
||||
#endif /*LV_CACHE_LRU_RB_H*/
|
||||
@@ -0,0 +1,16 @@
|
||||
/**
|
||||
* @file lv_cache_instance.h
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef LV_CACHE_INSTANCE_H
|
||||
#define LV_CACHE_INSTANCE_H
|
||||
|
||||
/*********************
|
||||
* INCLUDES
|
||||
*********************/
|
||||
|
||||
#include "lv_image_header_cache.h"
|
||||
#include "lv_image_cache.h"
|
||||
|
||||
#endif //LV_CACHE_INSTANCE_H
|
||||
@@ -0,0 +1,190 @@
|
||||
/**
|
||||
* @file lv_image_cache.c
|
||||
*
|
||||
*/
|
||||
|
||||
/*********************
|
||||
* INCLUDES
|
||||
*********************/
|
||||
|
||||
#include "../../../draw/lv_image_decoder_private.h"
|
||||
#include "../../lv_assert.h"
|
||||
#include "../../../core/lv_global.h"
|
||||
#include "../../../misc/lv_iter.h"
|
||||
|
||||
#include "lv_image_cache.h"
|
||||
|
||||
/*********************
|
||||
* DEFINES
|
||||
*********************/
|
||||
|
||||
#define CACHE_NAME "IMAGE"
|
||||
|
||||
#define img_cache_p (LV_GLOBAL_DEFAULT()->img_cache)
|
||||
#define image_cache_draw_buf_handlers &(LV_GLOBAL_DEFAULT()->image_cache_draw_buf_handlers)
|
||||
|
||||
/**********************
|
||||
* TYPEDEFS
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* STATIC PROTOTYPES
|
||||
**********************/
|
||||
|
||||
static lv_cache_compare_res_t image_cache_compare_cb(const lv_image_cache_data_t * lhs,
|
||||
const lv_image_cache_data_t * rhs);
|
||||
static void image_cache_free_cb(lv_image_cache_data_t * entry, void * user_data);
|
||||
static void iter_inspect_cb(void * elem);
|
||||
|
||||
/**********************
|
||||
* GLOBAL VARIABLES
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* STATIC VARIABLES
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* MACROS
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* GLOBAL FUNCTIONS
|
||||
**********************/
|
||||
|
||||
lv_result_t lv_image_cache_init(uint32_t size)
|
||||
{
|
||||
if(img_cache_p != NULL) {
|
||||
return LV_RESULT_OK;
|
||||
}
|
||||
|
||||
img_cache_p = lv_cache_create(&lv_cache_class_lru_rb_size,
|
||||
sizeof(lv_image_cache_data_t), size, (lv_cache_ops_t) {
|
||||
.compare_cb = (lv_cache_compare_cb_t) image_cache_compare_cb,
|
||||
.create_cb = NULL,
|
||||
.free_cb = (lv_cache_free_cb_t) image_cache_free_cb,
|
||||
});
|
||||
|
||||
lv_cache_set_name(img_cache_p, CACHE_NAME);
|
||||
return img_cache_p != NULL ? LV_RESULT_OK : LV_RESULT_INVALID;
|
||||
}
|
||||
|
||||
void lv_image_cache_resize(uint32_t new_size, bool evict_now)
|
||||
{
|
||||
lv_cache_set_max_size(img_cache_p, new_size, NULL);
|
||||
if(evict_now) {
|
||||
lv_cache_reserve(img_cache_p, new_size, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
void lv_image_cache_drop(const void * src)
|
||||
{
|
||||
/*If user invalidate image, the header cache should be invalidated too.*/
|
||||
lv_image_header_cache_drop(src);
|
||||
|
||||
if(src == NULL) {
|
||||
lv_cache_drop_all(img_cache_p, NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
lv_image_cache_data_t search_key = {
|
||||
.src = src,
|
||||
.src_type = lv_image_src_get_type(src),
|
||||
};
|
||||
|
||||
lv_cache_drop(img_cache_p, &search_key, NULL);
|
||||
}
|
||||
|
||||
bool lv_image_cache_is_enabled(void)
|
||||
{
|
||||
return lv_cache_is_enabled(img_cache_p);
|
||||
}
|
||||
|
||||
lv_iter_t * lv_image_cache_iter_create(void)
|
||||
{
|
||||
return lv_cache_iter_create(img_cache_p);
|
||||
}
|
||||
|
||||
void lv_image_cache_dump(void)
|
||||
{
|
||||
lv_iter_t * iter = lv_image_cache_iter_create();
|
||||
if(iter == NULL) return;
|
||||
|
||||
LV_LOG_USER("Image cache dump:");
|
||||
LV_LOG_USER("\tsize\tdata_size\tcf\trc\ttype\tdecoded\t\t\tsrc");
|
||||
lv_iter_inspect(iter, iter_inspect_cb);
|
||||
}
|
||||
|
||||
/**********************
|
||||
* STATIC FUNCTIONS
|
||||
**********************/
|
||||
|
||||
inline static lv_cache_compare_res_t image_cache_common_compare(const void * lhs_src, lv_image_src_t lhs_src_type,
|
||||
const void * rhs_src, lv_image_src_t rhs_src_type)
|
||||
{
|
||||
if(lhs_src_type == rhs_src_type) {
|
||||
if(lhs_src_type == LV_IMAGE_SRC_FILE) {
|
||||
int32_t cmp_res = lv_strcmp(lhs_src, rhs_src);
|
||||
if(cmp_res != 0) {
|
||||
return cmp_res > 0 ? 1 : -1;
|
||||
}
|
||||
}
|
||||
else if(lhs_src_type == LV_IMAGE_SRC_VARIABLE) {
|
||||
if(lhs_src != rhs_src) {
|
||||
return lhs_src > rhs_src ? 1 : -1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
return lhs_src_type > rhs_src_type ? 1 : -1;
|
||||
}
|
||||
|
||||
static lv_cache_compare_res_t image_cache_compare_cb(
|
||||
const lv_image_cache_data_t * lhs,
|
||||
const lv_image_cache_data_t * rhs)
|
||||
{
|
||||
return image_cache_common_compare(lhs->src, lhs->src_type, rhs->src, rhs->src_type);
|
||||
}
|
||||
|
||||
static void image_cache_free_cb(lv_image_cache_data_t * entry, void * user_data)
|
||||
{
|
||||
LV_UNUSED(user_data);
|
||||
|
||||
/* Destroy the decoded draw buffer if necessary. */
|
||||
lv_draw_buf_t * decoded = (lv_draw_buf_t *)entry->decoded;
|
||||
if(lv_draw_buf_has_flag(decoded, LV_IMAGE_FLAGS_ALLOCATED)) {
|
||||
lv_draw_buf_destroy(decoded);
|
||||
}
|
||||
|
||||
/*Free the duplicated file name*/
|
||||
if(entry->src_type == LV_IMAGE_SRC_FILE) lv_free((void *)entry->src);
|
||||
}
|
||||
|
||||
static void iter_inspect_cb(void * elem)
|
||||
{
|
||||
lv_image_cache_data_t * data = (lv_image_cache_data_t *)elem;
|
||||
lv_draw_buf_t * decoded = (lv_draw_buf_t *)data->decoded;
|
||||
lv_image_header_t * header = &decoded->header;
|
||||
lv_cache_entry_t * entry = lv_cache_entry_get_entry(data, img_cache_p->node_size);
|
||||
|
||||
LV_UNUSED(decoded);
|
||||
LV_UNUSED(header);
|
||||
LV_UNUSED(entry);
|
||||
|
||||
/* size data_size cf rc type decoded src*/
|
||||
#define IMAGE_CACHE_DUMP_FORMAT " %4dx%-4d %9"LV_PRIu32" %d %"LV_PRId32" "
|
||||
switch(data->src_type) {
|
||||
case LV_IMAGE_SRC_FILE:
|
||||
LV_LOG_USER(IMAGE_CACHE_DUMP_FORMAT "file\t%-12p\t%s", header->w, header->h, decoded->data_size, header->cf,
|
||||
lv_cache_entry_get_ref(entry), (void *)data->decoded, (char *)data->src);
|
||||
break;
|
||||
case LV_IMAGE_SRC_VARIABLE:
|
||||
LV_LOG_USER(IMAGE_CACHE_DUMP_FORMAT "var \t%-12p\t%p", header->w, header->h, decoded->data_size, header->cf,
|
||||
lv_cache_entry_get_ref(entry), (void *)data->decoded, data->src);
|
||||
break;
|
||||
default:
|
||||
LV_LOG_USER(IMAGE_CACHE_DUMP_FORMAT "unkn\t%-12p\t%p", header->w, header->h, decoded->data_size, header->cf,
|
||||
lv_cache_entry_get_ref(entry), (void *)data->decoded, data->src);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
/**
|
||||
* @file lv_image_cache.h
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef LV_IMAGE_CACHE_H
|
||||
#define LV_IMAGE_CACHE_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*********************
|
||||
* INCLUDES
|
||||
*********************/
|
||||
|
||||
#include "../../lv_types.h"
|
||||
|
||||
/*********************
|
||||
* DEFINES
|
||||
*********************/
|
||||
|
||||
/**********************
|
||||
* TYPEDEFS
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* GLOBAL PROTOTYPES
|
||||
**********************/
|
||||
|
||||
/**
|
||||
* Initialize image cache.
|
||||
* @param size size of the cache in bytes.
|
||||
* @return LV_RESULT_OK: initialization succeeded, LV_RESULT_INVALID: failed.
|
||||
*/
|
||||
lv_result_t lv_image_cache_init(uint32_t size);
|
||||
|
||||
/**
|
||||
* Resize image cache.
|
||||
* If set to 0, the cache will be disabled.
|
||||
* @param new_size new size of the cache in bytes.
|
||||
* @param evict_now true: evict the images should be removed by the eviction policy, false: wait for the next cache cleanup.
|
||||
*/
|
||||
void lv_image_cache_resize(uint32_t new_size, bool evict_now);
|
||||
|
||||
/**
|
||||
* Invalidate image cache. Use NULL to invalidate all images.
|
||||
* @param src pointer to an image source.
|
||||
*/
|
||||
void lv_image_cache_drop(const void * src);
|
||||
|
||||
/**
|
||||
* Return true if the image cache is enabled.
|
||||
* @return true: enabled, false: disabled.
|
||||
*/
|
||||
bool lv_image_cache_is_enabled(void);
|
||||
|
||||
/**
|
||||
* Create an iterator to iterate over the image cache.
|
||||
* @return an iterator to iterate over the image cache.
|
||||
*/
|
||||
lv_iter_t * lv_image_cache_iter_create(void);
|
||||
|
||||
/**
|
||||
* Dump the content of the image cache in a human-readable format with cache order.
|
||||
*/
|
||||
void lv_image_cache_dump(void);
|
||||
|
||||
/*************************
|
||||
* GLOBAL VARIABLES
|
||||
*************************/
|
||||
|
||||
/**********************
|
||||
* MACROS
|
||||
**********************/
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /*extern "C"*/
|
||||
#endif
|
||||
|
||||
#endif /*LV_IMAGE_CACHE_H*/
|
||||
@@ -0,0 +1,179 @@
|
||||
/**
|
||||
* @file lv_image_header_cache.c
|
||||
*
|
||||
*/
|
||||
|
||||
/*********************
|
||||
* INCLUDES
|
||||
*********************/
|
||||
|
||||
#include "../../../draw/lv_image_decoder_private.h"
|
||||
#include "../../lv_assert.h"
|
||||
#include "../../../core/lv_global.h"
|
||||
|
||||
#include "lv_image_header_cache.h"
|
||||
#include "../../lv_iter.h"
|
||||
|
||||
/*********************
|
||||
* DEFINES
|
||||
*********************/
|
||||
|
||||
#define CACHE_NAME "IMAGE_HEADER"
|
||||
|
||||
#define img_header_cache_p (LV_GLOBAL_DEFAULT()->img_header_cache)
|
||||
|
||||
/**********************
|
||||
* TYPEDEFS
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* STATIC PROTOTYPES
|
||||
**********************/
|
||||
|
||||
static lv_cache_compare_res_t image_header_cache_compare_cb(const lv_image_header_cache_data_t * lhs,
|
||||
const lv_image_header_cache_data_t * rhs);
|
||||
static void image_header_cache_free_cb(lv_image_header_cache_data_t * entry, void * user_data);
|
||||
static void iter_inspect_cb(void * elem);
|
||||
|
||||
/**********************
|
||||
* GLOBAL VARIABLES
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* STATIC VARIABLES
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* MACROS
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* GLOBAL FUNCTIONS
|
||||
**********************/
|
||||
|
||||
lv_result_t lv_image_header_cache_init(uint32_t count)
|
||||
{
|
||||
if(img_header_cache_p != NULL) {
|
||||
return LV_RESULT_OK;
|
||||
}
|
||||
|
||||
img_header_cache_p = lv_cache_create(&lv_cache_class_lru_rb_count,
|
||||
sizeof(lv_image_header_cache_data_t), count, (lv_cache_ops_t) {
|
||||
.compare_cb = (lv_cache_compare_cb_t) image_header_cache_compare_cb,
|
||||
.create_cb = NULL,
|
||||
.free_cb = (lv_cache_free_cb_t) image_header_cache_free_cb
|
||||
});
|
||||
|
||||
lv_cache_set_name(img_header_cache_p, CACHE_NAME);
|
||||
return img_header_cache_p != NULL ? LV_RESULT_OK : LV_RESULT_INVALID;
|
||||
}
|
||||
|
||||
void lv_image_header_cache_resize(uint32_t count, bool evict_now)
|
||||
{
|
||||
lv_cache_set_max_size(img_header_cache_p, count, NULL);
|
||||
if(evict_now) {
|
||||
lv_cache_reserve(img_header_cache_p, count, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
void lv_image_header_cache_drop(const void * src)
|
||||
{
|
||||
if(src == NULL) {
|
||||
lv_cache_drop_all(img_header_cache_p, NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
lv_image_header_cache_data_t search_key = {
|
||||
.src = src,
|
||||
.src_type = lv_image_src_get_type(src),
|
||||
};
|
||||
|
||||
lv_cache_drop(img_header_cache_p, &search_key, NULL);
|
||||
}
|
||||
|
||||
bool lv_image_header_cache_is_enabled(void)
|
||||
{
|
||||
return lv_cache_is_enabled(img_header_cache_p);
|
||||
}
|
||||
|
||||
lv_iter_t * lv_image_header_cache_iter_create(void)
|
||||
{
|
||||
return lv_cache_iter_create(img_header_cache_p);
|
||||
}
|
||||
|
||||
void lv_image_header_cache_dump(void)
|
||||
{
|
||||
lv_iter_t * iter = lv_image_cache_iter_create();
|
||||
if(iter == NULL) return;
|
||||
|
||||
LV_LOG_USER("Image cache dump:");
|
||||
LV_LOG_USER("\tsize\tdata_size\tcf\trc\ttype\tdecoded\t\t\tsrc");
|
||||
lv_iter_inspect(iter, iter_inspect_cb);
|
||||
}
|
||||
|
||||
/**********************
|
||||
* STATIC FUNCTIONS
|
||||
**********************/
|
||||
|
||||
inline static lv_cache_compare_res_t image_cache_common_compare(const void * lhs_src, lv_image_src_t lhs_src_type,
|
||||
const void * rhs_src, lv_image_src_t rhs_src_type)
|
||||
{
|
||||
if(lhs_src_type == rhs_src_type) {
|
||||
if(lhs_src_type == LV_IMAGE_SRC_FILE) {
|
||||
int32_t cmp_res = lv_strcmp(lhs_src, rhs_src);
|
||||
if(cmp_res != 0) {
|
||||
return cmp_res > 0 ? 1 : -1;
|
||||
}
|
||||
}
|
||||
else if(lhs_src_type == LV_IMAGE_SRC_VARIABLE) {
|
||||
if(lhs_src != rhs_src) {
|
||||
return lhs_src > rhs_src ? 1 : -1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
return lhs_src_type > rhs_src_type ? 1 : -1;
|
||||
}
|
||||
|
||||
static lv_cache_compare_res_t image_header_cache_compare_cb(
|
||||
const lv_image_header_cache_data_t * lhs,
|
||||
const lv_image_header_cache_data_t * rhs)
|
||||
{
|
||||
return image_cache_common_compare(lhs->src, lhs->src_type, rhs->src, rhs->src_type);
|
||||
}
|
||||
|
||||
static void image_header_cache_free_cb(lv_image_header_cache_data_t * entry, void * user_data)
|
||||
{
|
||||
LV_UNUSED(user_data); /*Unused*/
|
||||
|
||||
if(entry->src_type == LV_IMAGE_SRC_FILE) lv_free((void *)entry->src);
|
||||
}
|
||||
|
||||
static void iter_inspect_cb(void * elem)
|
||||
{
|
||||
lv_image_cache_data_t * data = (lv_image_cache_data_t *)elem;
|
||||
lv_draw_buf_t * decoded = (lv_draw_buf_t *)data->decoded;
|
||||
lv_image_header_t * header = &decoded->header;
|
||||
lv_cache_entry_t * entry = lv_cache_entry_get_entry(data, img_header_cache_p->node_size);
|
||||
|
||||
LV_UNUSED(decoded);
|
||||
LV_UNUSED(header);
|
||||
LV_UNUSED(entry);
|
||||
|
||||
/* size data_size cf rc type decoded src*/
|
||||
#define IMAGE_CACHE_DUMP_FORMAT " %4dx%-4d %9"LV_PRIu32" %d %" LV_PRId32 " "
|
||||
switch(data->src_type) {
|
||||
case LV_IMAGE_SRC_FILE:
|
||||
LV_LOG_USER(IMAGE_CACHE_DUMP_FORMAT "file\t%-12p\t%s", header->w, header->h, decoded->data_size, header->cf,
|
||||
lv_cache_entry_get_ref(entry), (void *)data->decoded, (char *)data->src);
|
||||
break;
|
||||
case LV_IMAGE_SRC_VARIABLE:
|
||||
LV_LOG_USER(IMAGE_CACHE_DUMP_FORMAT "var \t%-12p\t%p", header->w, header->h, decoded->data_size, header->cf,
|
||||
lv_cache_entry_get_ref(entry), (void *)data->decoded, data->src);
|
||||
break;
|
||||
default:
|
||||
LV_LOG_USER(IMAGE_CACHE_DUMP_FORMAT "unkn\t%-12p\t%p", header->w, header->h, decoded->data_size, header->cf,
|
||||
lv_cache_entry_get_ref(entry), (void *)data->decoded, data->src);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
/**
|
||||
* @file lv_image_header_cache.h
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef LV_IMAGE_HEADER_CACHE_H
|
||||
#define LV_IMAGE_HEADER_CACHE_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*********************
|
||||
* INCLUDES
|
||||
*********************/
|
||||
|
||||
#include "../../lv_types.h"
|
||||
|
||||
/*********************
|
||||
* DEFINES
|
||||
*********************/
|
||||
|
||||
/**********************
|
||||
* TYPEDEFS
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* GLOBAL PROTOTYPES
|
||||
**********************/
|
||||
|
||||
/**
|
||||
* Initialize image header cache.
|
||||
* @param count initial size of the cache in count of image headers.
|
||||
* @return LV_RESULT_OK: initialization succeeded, LV_RESULT_INVALID: failed.
|
||||
*/
|
||||
lv_result_t lv_image_header_cache_init(uint32_t count);
|
||||
|
||||
/**
|
||||
* Resize image header cache.
|
||||
* If set to 0, the cache is disabled.
|
||||
* @param count new max count of cached image headers.
|
||||
* @param evict_now true: evict the image headers should be removed by the eviction policy, false: wait for the next cache cleanup.
|
||||
*/
|
||||
void lv_image_header_cache_resize(uint32_t count, bool evict_now);
|
||||
|
||||
/**
|
||||
* Invalidate image header cache. Use NULL to invalidate all image headers.
|
||||
* It's also automatically called when an image is invalidated.
|
||||
* @param src pointer to an image source.
|
||||
*/
|
||||
void lv_image_header_cache_drop(const void * src);
|
||||
|
||||
/**
|
||||
* Return true if the image header cache is enabled.
|
||||
* @return true: enabled, false: disabled.
|
||||
*/
|
||||
bool lv_image_header_cache_is_enabled(void);
|
||||
|
||||
/**
|
||||
* Create an iterator to iterate over the image header cache.
|
||||
* @return an iterator to iterate over the image header cache.
|
||||
*/
|
||||
lv_iter_t * lv_image_header_cache_iter_create(void);
|
||||
|
||||
/**
|
||||
* Dump the content of the image header cache in a human-readable format with cache order.
|
||||
*/
|
||||
void lv_image_header_cache_dump(void);
|
||||
|
||||
/*************************
|
||||
* GLOBAL VARIABLES
|
||||
*************************/
|
||||
|
||||
/**********************
|
||||
* MACROS
|
||||
**********************/
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /*extern "C"*/
|
||||
#endif
|
||||
|
||||
#endif /*LV_IMAGE_HEADER_CACHE_H*/
|
||||
+358
@@ -0,0 +1,358 @@
|
||||
/**
|
||||
* @file lv_cache.c
|
||||
*
|
||||
*/
|
||||
|
||||
/*********************
|
||||
* INCLUDES
|
||||
*********************/
|
||||
#include "lv_cache.h"
|
||||
#include "../../stdlib/lv_sprintf.h"
|
||||
#include "../lv_assert.h"
|
||||
#include "lv_cache_entry_private.h"
|
||||
#include "lv_cache_private.h"
|
||||
|
||||
/*********************
|
||||
* DEFINES
|
||||
*********************/
|
||||
|
||||
/**********************
|
||||
* TYPEDEFS
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* STATIC PROTOTYPES
|
||||
**********************/
|
||||
static void cache_drop_internal_no_lock(lv_cache_t * cache, const void * key, void * user_data);
|
||||
static bool cache_evict_one_internal_no_lock(lv_cache_t * cache, void * user_data);
|
||||
static lv_cache_entry_t * cache_add_internal_no_lock(lv_cache_t * cache, const void * key, void * user_data);
|
||||
|
||||
/**********************
|
||||
* GLOBAL VARIABLES
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* STATIC VARIABLES
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* MACROS
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* GLOBAL FUNCTIONS
|
||||
**********************/
|
||||
|
||||
lv_cache_t * lv_cache_create(const lv_cache_class_t * cache_class,
|
||||
size_t node_size, size_t max_size,
|
||||
lv_cache_ops_t ops)
|
||||
{
|
||||
lv_cache_t * cache = cache_class->alloc_cb();
|
||||
LV_ASSERT_MALLOC(cache);
|
||||
|
||||
cache->clz = cache_class;
|
||||
cache->node_size = node_size;
|
||||
cache->max_size = max_size;
|
||||
cache->size = 0;
|
||||
cache->ops = ops;
|
||||
|
||||
if(cache->clz->init_cb(cache) == false) {
|
||||
LV_LOG_ERROR("Cache init failed");
|
||||
lv_free(cache);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
lv_mutex_init(&cache->lock);
|
||||
|
||||
return cache;
|
||||
}
|
||||
|
||||
void lv_cache_destroy(lv_cache_t * cache, void * user_data)
|
||||
{
|
||||
LV_ASSERT_NULL(cache);
|
||||
|
||||
lv_mutex_lock(&cache->lock);
|
||||
cache->clz->destroy_cb(cache, user_data);
|
||||
lv_mutex_unlock(&cache->lock);
|
||||
lv_mutex_delete(&cache->lock);
|
||||
lv_free(cache);
|
||||
}
|
||||
|
||||
lv_cache_entry_t * lv_cache_acquire(lv_cache_t * cache, const void * key, void * user_data)
|
||||
{
|
||||
LV_ASSERT_NULL(cache);
|
||||
LV_ASSERT_NULL(key);
|
||||
|
||||
LV_PROFILER_CACHE_BEGIN;
|
||||
|
||||
lv_mutex_lock(&cache->lock);
|
||||
|
||||
if(cache->size == 0) {
|
||||
lv_mutex_unlock(&cache->lock);
|
||||
|
||||
LV_PROFILER_CACHE_END;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
lv_cache_entry_t * entry = cache->clz->get_cb(cache, key, user_data);
|
||||
if(entry != NULL) {
|
||||
lv_cache_entry_acquire_data(entry);
|
||||
}
|
||||
lv_mutex_unlock(&cache->lock);
|
||||
|
||||
LV_PROFILER_CACHE_END;
|
||||
return entry;
|
||||
}
|
||||
void lv_cache_release(lv_cache_t * cache, lv_cache_entry_t * entry, void * user_data)
|
||||
{
|
||||
LV_ASSERT_NULL(entry);
|
||||
|
||||
LV_PROFILER_CACHE_BEGIN;
|
||||
|
||||
lv_mutex_lock(&cache->lock);
|
||||
lv_cache_entry_release_data(entry, user_data);
|
||||
|
||||
if(lv_cache_entry_get_ref(entry) == 0 && lv_cache_entry_is_invalid(entry)) {
|
||||
cache->ops.free_cb(lv_cache_entry_get_data(entry), user_data);
|
||||
lv_cache_entry_delete(entry);
|
||||
}
|
||||
lv_mutex_unlock(&cache->lock);
|
||||
|
||||
LV_PROFILER_CACHE_END;
|
||||
}
|
||||
lv_cache_entry_t * lv_cache_add(lv_cache_t * cache, const void * key, void * user_data)
|
||||
{
|
||||
LV_ASSERT_NULL(cache);
|
||||
LV_ASSERT_NULL(key);
|
||||
|
||||
LV_PROFILER_CACHE_BEGIN;
|
||||
|
||||
lv_mutex_lock(&cache->lock);
|
||||
if(cache->max_size == 0) {
|
||||
lv_mutex_unlock(&cache->lock);
|
||||
|
||||
LV_PROFILER_CACHE_END;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
lv_cache_entry_t * entry = cache_add_internal_no_lock(cache, key, user_data);
|
||||
if(entry != NULL) {
|
||||
lv_cache_entry_acquire_data(entry);
|
||||
}
|
||||
lv_mutex_unlock(&cache->lock);
|
||||
|
||||
LV_PROFILER_CACHE_END;
|
||||
return entry;
|
||||
}
|
||||
lv_cache_entry_t * lv_cache_acquire_or_create(lv_cache_t * cache, const void * key, void * user_data)
|
||||
{
|
||||
LV_ASSERT_NULL(cache);
|
||||
LV_ASSERT_NULL(key);
|
||||
|
||||
LV_PROFILER_CACHE_BEGIN;
|
||||
|
||||
lv_mutex_lock(&cache->lock);
|
||||
lv_cache_entry_t * entry = NULL;
|
||||
|
||||
if(cache->size != 0) {
|
||||
entry = cache->clz->get_cb(cache, key, user_data);
|
||||
if(entry != NULL) {
|
||||
lv_cache_entry_acquire_data(entry);
|
||||
lv_mutex_unlock(&cache->lock);
|
||||
|
||||
LV_PROFILER_CACHE_END;
|
||||
return entry;
|
||||
}
|
||||
}
|
||||
|
||||
if(cache->max_size == 0) {
|
||||
lv_mutex_unlock(&cache->lock);
|
||||
|
||||
LV_PROFILER_CACHE_END;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
entry = cache_add_internal_no_lock(cache, key, user_data);
|
||||
if(entry == NULL) {
|
||||
lv_mutex_unlock(&cache->lock);
|
||||
|
||||
LV_PROFILER_CACHE_END;
|
||||
return NULL;
|
||||
}
|
||||
bool create_res = cache->ops.create_cb(lv_cache_entry_get_data(entry), user_data);
|
||||
if(create_res == false) {
|
||||
cache->clz->remove_cb(cache, entry, user_data);
|
||||
cache->ops.free_cb(lv_cache_entry_get_data(entry), user_data);
|
||||
lv_cache_entry_delete(entry);
|
||||
entry = NULL;
|
||||
}
|
||||
else {
|
||||
lv_cache_entry_acquire_data(entry);
|
||||
}
|
||||
lv_mutex_unlock(&cache->lock);
|
||||
|
||||
LV_PROFILER_CACHE_END;
|
||||
return entry;
|
||||
}
|
||||
void lv_cache_reserve(lv_cache_t * cache, uint32_t reserved_size, void * user_data)
|
||||
{
|
||||
LV_ASSERT_NULL(cache);
|
||||
|
||||
LV_PROFILER_CACHE_BEGIN;
|
||||
|
||||
for(lv_cache_reserve_cond_res_t reserve_cond_res = cache->clz->reserve_cond_cb(cache, NULL, reserved_size, user_data);
|
||||
reserve_cond_res == LV_CACHE_RESERVE_COND_NEED_VICTIM;
|
||||
reserve_cond_res = cache->clz->reserve_cond_cb(cache, NULL, reserved_size, user_data))
|
||||
cache_evict_one_internal_no_lock(cache, user_data);
|
||||
|
||||
LV_PROFILER_CACHE_END;
|
||||
}
|
||||
void lv_cache_drop(lv_cache_t * cache, const void * key, void * user_data)
|
||||
{
|
||||
LV_ASSERT_NULL(cache);
|
||||
LV_ASSERT_NULL(key);
|
||||
|
||||
LV_PROFILER_CACHE_BEGIN;
|
||||
|
||||
lv_mutex_lock(&cache->lock);
|
||||
cache_drop_internal_no_lock(cache, key, user_data);
|
||||
lv_mutex_unlock(&cache->lock);
|
||||
|
||||
LV_PROFILER_CACHE_END;
|
||||
}
|
||||
bool lv_cache_evict_one(lv_cache_t * cache, void * user_data)
|
||||
{
|
||||
LV_ASSERT_NULL(cache);
|
||||
|
||||
LV_PROFILER_CACHE_BEGIN;
|
||||
|
||||
lv_mutex_lock(&cache->lock);
|
||||
bool res = cache_evict_one_internal_no_lock(cache, user_data);
|
||||
lv_mutex_unlock(&cache->lock);
|
||||
|
||||
LV_PROFILER_CACHE_END;
|
||||
return res;
|
||||
}
|
||||
void lv_cache_drop_all(lv_cache_t * cache, void * user_data)
|
||||
{
|
||||
LV_ASSERT_NULL(cache);
|
||||
|
||||
LV_PROFILER_CACHE_BEGIN;
|
||||
|
||||
lv_mutex_lock(&cache->lock);
|
||||
cache->clz->drop_all_cb(cache, user_data);
|
||||
lv_mutex_unlock(&cache->lock);
|
||||
|
||||
LV_PROFILER_CACHE_END;
|
||||
}
|
||||
|
||||
void lv_cache_set_max_size(lv_cache_t * cache, size_t max_size, void * user_data)
|
||||
{
|
||||
LV_UNUSED(user_data);
|
||||
cache->max_size = max_size;
|
||||
}
|
||||
size_t lv_cache_get_max_size(lv_cache_t * cache, void * user_data)
|
||||
{
|
||||
LV_UNUSED(user_data);
|
||||
return cache->max_size;
|
||||
}
|
||||
size_t lv_cache_get_size(lv_cache_t * cache, void * user_data)
|
||||
{
|
||||
LV_UNUSED(user_data);
|
||||
return cache->size;
|
||||
}
|
||||
size_t lv_cache_get_free_size(lv_cache_t * cache, void * user_data)
|
||||
{
|
||||
LV_UNUSED(user_data);
|
||||
return cache->max_size - cache->size;
|
||||
}
|
||||
bool lv_cache_is_enabled(lv_cache_t * cache)
|
||||
{
|
||||
return cache->max_size > 0;
|
||||
}
|
||||
void lv_cache_set_compare_cb(lv_cache_t * cache, lv_cache_compare_cb_t compare_cb, void * user_data)
|
||||
{
|
||||
LV_UNUSED(user_data);
|
||||
cache->ops.compare_cb = compare_cb;
|
||||
}
|
||||
void lv_cache_set_create_cb(lv_cache_t * cache, lv_cache_create_cb_t alloc_cb, void * user_data)
|
||||
{
|
||||
LV_UNUSED(user_data);
|
||||
cache->ops.create_cb = alloc_cb;
|
||||
}
|
||||
void lv_cache_set_free_cb(lv_cache_t * cache, lv_cache_free_cb_t free_cb, void * user_data)
|
||||
{
|
||||
LV_UNUSED(user_data);
|
||||
cache->ops.free_cb = free_cb;
|
||||
}
|
||||
void lv_cache_set_name(lv_cache_t * cache, const char * name)
|
||||
{
|
||||
if(cache == NULL) return;
|
||||
cache->name = name;
|
||||
}
|
||||
const char * lv_cache_get_name(lv_cache_t * cache)
|
||||
{
|
||||
return cache->name;
|
||||
}
|
||||
|
||||
lv_iter_t * lv_cache_iter_create(lv_cache_t * cache)
|
||||
{
|
||||
LV_ASSERT_NULL(cache);
|
||||
if(cache == NULL || cache->clz->iter_create_cb == NULL) return NULL;
|
||||
return cache->clz->iter_create_cb(cache);
|
||||
}
|
||||
|
||||
/**********************
|
||||
* STATIC FUNCTIONS
|
||||
**********************/
|
||||
|
||||
static void cache_drop_internal_no_lock(lv_cache_t * cache, const void * key, void * user_data)
|
||||
{
|
||||
lv_cache_entry_t * entry = cache->clz->get_cb(cache, key, user_data);
|
||||
if(entry == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(lv_cache_entry_get_ref(entry) == 0) {
|
||||
cache->clz->remove_cb(cache, entry, user_data);
|
||||
cache->ops.free_cb(lv_cache_entry_get_data(entry), user_data);
|
||||
lv_cache_entry_delete(entry);
|
||||
}
|
||||
else {
|
||||
lv_cache_entry_set_invalid(entry, true);
|
||||
cache->clz->remove_cb(cache, entry, user_data);
|
||||
}
|
||||
}
|
||||
|
||||
static bool cache_evict_one_internal_no_lock(lv_cache_t * cache, void * user_data)
|
||||
{
|
||||
lv_cache_entry_t * victim = cache->clz->get_victim_cb(cache, user_data);
|
||||
|
||||
if(victim == NULL) {
|
||||
LV_LOG_ERROR("No victim found");
|
||||
return false;
|
||||
}
|
||||
|
||||
cache->clz->remove_cb(cache, victim, user_data);
|
||||
cache->ops.free_cb(lv_cache_entry_get_data(victim), user_data);
|
||||
lv_cache_entry_delete(victim);
|
||||
return true;
|
||||
}
|
||||
|
||||
static lv_cache_entry_t * cache_add_internal_no_lock(lv_cache_t * cache, const void * key, void * user_data)
|
||||
{
|
||||
lv_cache_reserve_cond_res_t reserve_cond_res = cache->clz->reserve_cond_cb(cache, key, 0, user_data);
|
||||
if(reserve_cond_res == LV_CACHE_RESERVE_COND_TOO_LARGE) {
|
||||
LV_LOG_ERROR("data %p is too large that exceeds max size (%" LV_PRIu32 ")", key, cache->max_size);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for(; reserve_cond_res == LV_CACHE_RESERVE_COND_NEED_VICTIM;
|
||||
reserve_cond_res = cache->clz->reserve_cond_cb(cache, key, 0, user_data))
|
||||
if(cache_evict_one_internal_no_lock(cache, user_data) == false)
|
||||
return NULL;
|
||||
|
||||
lv_cache_entry_t * entry = cache->clz->add_cb(cache, key, user_data);
|
||||
|
||||
return entry;
|
||||
}
|
||||
+237
@@ -0,0 +1,237 @@
|
||||
/**
|
||||
* @file lv_cache.h
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef LV_CACHE_H
|
||||
#define LV_CACHE_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*********************
|
||||
* INCLUDES
|
||||
*********************/
|
||||
|
||||
#include "../lv_types.h"
|
||||
|
||||
#include "lv_cache_entry.h"
|
||||
|
||||
#include "class/lv_cache_class.h"
|
||||
#include "instance/lv_cache_instance.h"
|
||||
|
||||
/*********************
|
||||
* DEFINES
|
||||
*********************/
|
||||
|
||||
/**********************
|
||||
* TYPEDEFS
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* GLOBAL PROTOTYPES
|
||||
**********************/
|
||||
|
||||
/**
|
||||
* Create a cache object with the given parameters.
|
||||
* @param cache_class The class of the cache. Currently only support one two builtin classes:
|
||||
* - lv_cache_class_lru_rb_count for LRU-based cache with count-based eviction policy.
|
||||
* - lv_cache_class_lru_rb_size for LRU-based cache with size-based eviction policy.
|
||||
* @param node_size The node size is the size of the data stored in the cache..
|
||||
* @param max_size The max size is the maximum amount of memory or count that the cache can hold.
|
||||
* - lv_cache_class_lru_rb_count: max_size is the maximum count of nodes in the cache.
|
||||
* - lv_cache_class_lru_rb_size: max_size is the maximum size of the cache in bytes.
|
||||
* @param ops A set of operations that can be performed on the cache. See lv_cache_ops_t for details.
|
||||
* @return Returns a pointer to the created cache object on success, `NULL` on error.
|
||||
*/
|
||||
lv_cache_t * lv_cache_create(const lv_cache_class_t * cache_class,
|
||||
size_t node_size, size_t max_size,
|
||||
lv_cache_ops_t ops);
|
||||
|
||||
/**
|
||||
* Destroy a cache object.
|
||||
* @param cache The cache object pointer to destroy.
|
||||
* @param user_data A user data pointer that will be passed to the free callback.
|
||||
*/
|
||||
void lv_cache_destroy(lv_cache_t * cache, void * user_data);
|
||||
|
||||
/**
|
||||
* Acquire a cache entry with the given key. If entry not in cache, it will return `NULL` (not found).
|
||||
* If the entry is found, it's priority will be changed by the cache's policy. And the `lv_cache_entry_t::ref_cnt` will be incremented.
|
||||
* @param cache The cache object pointer to acquire the entry.
|
||||
* @param key The key of the entry to acquire.
|
||||
* @param user_data A user data pointer that will be passed to the create callback.
|
||||
* @return Returns a pointer to the acquired cache entry on success with `lv_cache_entry_t::ref_cnt` incremented, `NULL` on error.
|
||||
*/
|
||||
lv_cache_entry_t * lv_cache_acquire(lv_cache_t * cache, const void * key, void * user_data);
|
||||
|
||||
/**
|
||||
* Acquire a cache entry with the given key. If the entry is not in the cache, it will create a new entry with the given key.
|
||||
* If the entry is found, it's priority will be changed by the cache's policy. And the `lv_cache_entry_t::ref_cnt` will be incremented.
|
||||
* If you want to use this API to simplify the code, you should provide a `lv_cache_ops_t::create_cb` that creates a new entry with the given key.
|
||||
* This API is a combination of lv_cache_acquire() and lv_cache_add(). The effect is the same as calling lv_cache_acquire() and lv_cache_add() separately.
|
||||
* And the internal impact on cache is also consistent with these two APIs.
|
||||
* @param cache The cache object pointer to acquire the entry.
|
||||
* @param key The key of the entry to acquire or create.
|
||||
* @param user_data A user data pointer that will be passed to the create callback.
|
||||
* @return Returns a pointer to the acquired or created cache entry on success with `lv_cache_entry_t::ref_cnt` incremented, `NULL` on error.
|
||||
*/
|
||||
lv_cache_entry_t * lv_cache_acquire_or_create(lv_cache_t * cache, const void * key, void * user_data);
|
||||
|
||||
/**
|
||||
* Add a new cache entry with the given key and data. If the cache is full, the cache's policy will be used to evict an entry.
|
||||
* @param cache The cache object pointer to add the entry.
|
||||
* @param key The key of the entry to add.
|
||||
* @param user_data A user data pointer that will be passed to the create callback.
|
||||
* @return Returns a pointer to the added cache entry on success with `lv_cache_entry_t::ref_cnt` incremented, `NULL` on error.
|
||||
*/
|
||||
lv_cache_entry_t * lv_cache_add(lv_cache_t * cache, const void * key, void * user_data);
|
||||
|
||||
/**
|
||||
* Release a cache entry. The `lv_cache_entry_t::ref_cnt` will be decremented. If the `lv_cache_entry_t::ref_cnt` is zero, it will issue an error.
|
||||
* If the entry passed to this function is the last reference to the data and the entry is marked as invalid, the cache's policy will be used to evict the entry.
|
||||
* @param cache The cache object pointer to release the entry.
|
||||
* @param entry The cache entry pointer to release.
|
||||
* @param user_data A user data pointer that will be passed to the free callback.
|
||||
*/
|
||||
void lv_cache_release(lv_cache_t * cache, lv_cache_entry_t * entry, void * user_data);
|
||||
|
||||
/**
|
||||
* Reserve a certain amount of memory/count in the cache. This function is useful when you want to reserve a certain amount of memory/count in advance,
|
||||
* for example, when you know that you will need it later.
|
||||
* When the current cache size is max than the reserved size, the function will evict entries until the reserved size is reached.
|
||||
* @param cache The cache object pointer to reserve.
|
||||
* @param reserved_size The amount of memory/count to reserve.
|
||||
* @param user_data A user data pointer that will be passed to the free callback.
|
||||
*/
|
||||
void lv_cache_reserve(lv_cache_t * cache, uint32_t reserved_size, void * user_data);
|
||||
|
||||
/**
|
||||
* Drop a cache entry with the given key. If the entry is not in the cache, nothing will happen to it.
|
||||
* If the entry is found, it will be removed from the cache and its data will be freed when the last reference to it is released.
|
||||
* @note The data will not be freed immediately but when the last reference to it is released. But this entry will not be found by lv_cache_acquire().
|
||||
* If you want cache a same key again, you should use lv_cache_add() or lv_cache_acquire_or_create().
|
||||
* @param cache The cache object pointer to drop the entry.
|
||||
* @param key The key of the entry to drop.
|
||||
* @param user_data A user data pointer that will be passed to the free callback.
|
||||
*/
|
||||
void lv_cache_drop(lv_cache_t * cache, const void * key, void * user_data);
|
||||
|
||||
/**
|
||||
* Drop all cache entries. All entries will be removed from the cache and their data will be freed when the last reference to them is released.
|
||||
* @note If some entries are still referenced by other objects, it will issue an error. And this case shouldn't happen in normal cases..
|
||||
* @param cache The cache object pointer to drop all entries.
|
||||
* @param user_data A user data pointer that will be passed to the free callback.
|
||||
*/
|
||||
void lv_cache_drop_all(lv_cache_t * cache, void * user_data);
|
||||
|
||||
/**
|
||||
* Evict one entry from the cache. The eviction policy will be used to select the entry to evict.
|
||||
* @param cache The cache object pointer to evict an entry.
|
||||
* @param user_data A user data pointer that will be passed to the free callback.
|
||||
* @return Returns true if an entry is evicted, false if no entry is evicted.
|
||||
*/
|
||||
bool lv_cache_evict_one(lv_cache_t * cache, void * user_data);
|
||||
|
||||
/**
|
||||
* Set the maximum size of the cache.
|
||||
* If the current cache size is greater than the new maximum size, the cache's policy will be used to evict entries until the new maximum size is reached.
|
||||
* If set to 0, the cache will be disabled.
|
||||
* @note But this behavior will happen only new entries are added to the cache.
|
||||
* @param cache The cache object pointer to set the maximum size.
|
||||
* @param max_size The new maximum size of the cache.
|
||||
* @param user_data A user data pointer that will be passed to the free callback.
|
||||
*/
|
||||
void lv_cache_set_max_size(lv_cache_t * cache, size_t max_size, void * user_data);
|
||||
|
||||
/**
|
||||
* Get the maximum size of the cache.
|
||||
* @param cache The cache object pointer to get the maximum size.
|
||||
* @param user_data A user data pointer that will be passed to the free callback.
|
||||
* @return Returns the maximum size of the cache.
|
||||
*/
|
||||
size_t lv_cache_get_max_size(lv_cache_t * cache, void * user_data);
|
||||
|
||||
/**
|
||||
* Get the current size of the cache.
|
||||
* @param cache The cache object pointer to get the current size.
|
||||
* @param user_data A user data pointer that will be passed to the free callback.
|
||||
* @return Returns the current size of the cache.
|
||||
*/
|
||||
size_t lv_cache_get_size(lv_cache_t * cache, void * user_data);
|
||||
|
||||
/**
|
||||
* Get the free size of the cache.
|
||||
* @param cache The cache object pointer to get the free size.
|
||||
* @param user_data A user data pointer that will be passed to the free callback.
|
||||
* @return Returns the free size of the cache.
|
||||
*/
|
||||
size_t lv_cache_get_free_size(lv_cache_t * cache, void * user_data);
|
||||
|
||||
/**
|
||||
* Return true if the cache is enabled.
|
||||
* Disabled cache means that when the max_size of the cache is 0. In this case, all cache operations will be no-op.
|
||||
* @param cache The cache object pointer to check if it's disabled.
|
||||
* @return Returns true if the cache is enabled, false otherwise.
|
||||
*/
|
||||
bool lv_cache_is_enabled(lv_cache_t * cache);
|
||||
|
||||
/**
|
||||
* Set the compare callback of the cache.
|
||||
* @param cache The cache object pointer to set the compare callback.
|
||||
* @param compare_cb The compare callback to set.
|
||||
* @param user_data A user data pointer.
|
||||
*/
|
||||
void lv_cache_set_compare_cb(lv_cache_t * cache, lv_cache_compare_cb_t compare_cb, void * user_data);
|
||||
|
||||
/**
|
||||
* Set the create callback of the cache.
|
||||
* @param cache The cache object pointer to set the create callback.
|
||||
* @param alloc_cb The create callback to set.
|
||||
* @param user_data A user data pointer.
|
||||
*/
|
||||
void lv_cache_set_create_cb(lv_cache_t * cache, lv_cache_create_cb_t alloc_cb, void * user_data);
|
||||
|
||||
/**
|
||||
* Set the free callback of the cache.
|
||||
* @param cache The cache object pointer to set the free callback.
|
||||
* @param free_cb The free callback to set.
|
||||
* @param user_data A user data pointer.
|
||||
*/
|
||||
void lv_cache_set_free_cb(lv_cache_t * cache, lv_cache_free_cb_t free_cb, void * user_data);
|
||||
|
||||
/**
|
||||
* Give a name for a cache object. Only the pointer of the string is saved.
|
||||
* @param cache The cache object pointer to set the name.
|
||||
* @param name The name of the cache.
|
||||
*/
|
||||
void lv_cache_set_name(lv_cache_t * cache, const char * name);
|
||||
|
||||
/**
|
||||
* Get the name of a cache object.
|
||||
* @param cache The cache object pointer to get the name.
|
||||
* @return Returns the name of the cache.
|
||||
*/
|
||||
const char * lv_cache_get_name(lv_cache_t * cache);
|
||||
|
||||
/**
|
||||
* Create an iterator for the cache object. The iterator is used to iterate over all cache entries.
|
||||
* @param cache The cache object pointer to create the iterator.
|
||||
* @return Returns a pointer to the created iterator on success, `NULL` on error.
|
||||
*/
|
||||
lv_iter_t * lv_cache_iter_create(lv_cache_t * cache);
|
||||
|
||||
/*************************
|
||||
* GLOBAL VARIABLES
|
||||
*************************/
|
||||
|
||||
/**********************
|
||||
* MACROS
|
||||
**********************/
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /*extern "C"*/
|
||||
#endif
|
||||
|
||||
#endif /* LV_CACHE_H */
|
||||
@@ -0,0 +1,185 @@
|
||||
/**
|
||||
* @file lv_cache_entry.c
|
||||
*
|
||||
*/
|
||||
|
||||
/*********************
|
||||
* INCLUDES
|
||||
*********************/
|
||||
#include "lv_cache_entry.h"
|
||||
#include "../../stdlib/lv_sprintf.h"
|
||||
#include "../lv_assert.h"
|
||||
#include "lv_cache.h"
|
||||
#include "lv_cache_entry_private.h"
|
||||
#include "lv_cache_private.h"
|
||||
|
||||
/*********************
|
||||
* DEFINES
|
||||
*********************/
|
||||
|
||||
/**********************
|
||||
* TYPEDEFS
|
||||
**********************/
|
||||
struct _lv_cache_entry_t {
|
||||
const lv_cache_t * cache;
|
||||
int32_t ref_cnt;
|
||||
uint32_t node_size;
|
||||
|
||||
bool is_invalid;
|
||||
};
|
||||
/**********************
|
||||
* STATIC PROTOTYPES
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* GLOBAL VARIABLES
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* STATIC VARIABLES
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* MACROS
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* GLOBAL FUNCTIONS
|
||||
**********************/
|
||||
|
||||
void lv_cache_entry_reset_ref(lv_cache_entry_t * entry)
|
||||
{
|
||||
LV_ASSERT_NULL(entry);
|
||||
entry->ref_cnt = 0;
|
||||
}
|
||||
|
||||
void lv_cache_entry_inc_ref(lv_cache_entry_t * entry)
|
||||
{
|
||||
LV_ASSERT_NULL(entry);
|
||||
entry->ref_cnt++;
|
||||
}
|
||||
|
||||
void lv_cache_entry_dec_ref(lv_cache_entry_t * entry)
|
||||
{
|
||||
LV_ASSERT_NULL(entry);
|
||||
entry->ref_cnt--;
|
||||
if(entry->ref_cnt < 0) {
|
||||
LV_LOG_WARN("ref_cnt(%" LV_PRIu32 ") < 0", entry->ref_cnt);
|
||||
entry->ref_cnt = 0;
|
||||
}
|
||||
}
|
||||
|
||||
int32_t lv_cache_entry_get_ref(lv_cache_entry_t * entry)
|
||||
{
|
||||
LV_ASSERT_NULL(entry);
|
||||
return entry->ref_cnt;
|
||||
}
|
||||
|
||||
uint32_t lv_cache_entry_get_node_size(lv_cache_entry_t * entry)
|
||||
{
|
||||
return entry->node_size;
|
||||
}
|
||||
|
||||
void lv_cache_entry_set_node_size(lv_cache_entry_t * entry, uint32_t node_size)
|
||||
{
|
||||
LV_ASSERT_NULL(entry);
|
||||
entry->node_size = node_size;
|
||||
}
|
||||
|
||||
void lv_cache_entry_set_invalid(lv_cache_entry_t * entry, bool is_invalid)
|
||||
{
|
||||
LV_ASSERT_NULL(entry);
|
||||
entry->is_invalid = is_invalid;
|
||||
}
|
||||
|
||||
bool lv_cache_entry_is_invalid(lv_cache_entry_t * entry)
|
||||
{
|
||||
LV_ASSERT_NULL(entry);
|
||||
return entry->is_invalid;
|
||||
}
|
||||
|
||||
void * lv_cache_entry_get_data(lv_cache_entry_t * entry)
|
||||
{
|
||||
LV_ASSERT_NULL(entry);
|
||||
return (uint8_t *)entry - entry->node_size;
|
||||
}
|
||||
|
||||
void * lv_cache_entry_acquire_data(lv_cache_entry_t * entry)
|
||||
{
|
||||
LV_ASSERT_NULL(entry);
|
||||
|
||||
lv_cache_entry_inc_ref(entry);
|
||||
return lv_cache_entry_get_data(entry);
|
||||
}
|
||||
|
||||
void lv_cache_entry_release_data(lv_cache_entry_t * entry, void * user_data)
|
||||
{
|
||||
LV_UNUSED(user_data);
|
||||
|
||||
LV_ASSERT_NULL(entry);
|
||||
if(lv_cache_entry_get_ref(entry) == 0) {
|
||||
LV_LOG_ERROR("ref_cnt(%" LV_PRIu32 ") == 0", entry->ref_cnt);
|
||||
return;
|
||||
}
|
||||
|
||||
lv_cache_entry_dec_ref(entry);
|
||||
}
|
||||
|
||||
lv_cache_entry_t * lv_cache_entry_get_entry(void * data, const uint32_t node_size)
|
||||
{
|
||||
LV_ASSERT_NULL(data);
|
||||
return (lv_cache_entry_t *)((uint8_t *)data + node_size);
|
||||
}
|
||||
|
||||
void lv_cache_entry_set_cache(lv_cache_entry_t * entry, const lv_cache_t * cache)
|
||||
{
|
||||
LV_ASSERT_NULL(entry);
|
||||
entry->cache = cache;
|
||||
}
|
||||
|
||||
const lv_cache_t * lv_cache_entry_get_cache(const lv_cache_entry_t * entry)
|
||||
{
|
||||
LV_ASSERT_NULL(entry);
|
||||
return entry->cache;
|
||||
}
|
||||
|
||||
uint32_t lv_cache_entry_get_size(const uint32_t node_size)
|
||||
{
|
||||
return node_size + sizeof(lv_cache_entry_t);
|
||||
}
|
||||
|
||||
lv_cache_entry_t * lv_cache_entry_alloc(const uint32_t node_size, const lv_cache_t * cache)
|
||||
{
|
||||
void * res = lv_malloc_zeroed(lv_cache_entry_get_size(node_size));
|
||||
LV_ASSERT_MALLOC(res)
|
||||
if(res == NULL) {
|
||||
LV_LOG_ERROR("malloc failed");
|
||||
return NULL;
|
||||
}
|
||||
lv_cache_entry_t * entry = (lv_cache_entry_t *)res;
|
||||
lv_cache_entry_init(entry, cache, node_size);
|
||||
return (lv_cache_entry_t *)((uint8_t *)entry + node_size);
|
||||
}
|
||||
|
||||
void lv_cache_entry_init(lv_cache_entry_t * entry, const lv_cache_t * cache, const uint32_t node_size)
|
||||
{
|
||||
LV_ASSERT_NULL(entry);
|
||||
LV_ASSERT_NULL(cache);
|
||||
|
||||
entry->cache = cache;
|
||||
entry->node_size = node_size;
|
||||
entry->ref_cnt = 0;
|
||||
entry->is_invalid = false;
|
||||
}
|
||||
|
||||
void lv_cache_entry_delete(lv_cache_entry_t * entry)
|
||||
{
|
||||
LV_ASSERT_NULL(entry);
|
||||
|
||||
void * data = lv_cache_entry_get_data(entry);
|
||||
lv_free(data);
|
||||
}
|
||||
|
||||
/**********************
|
||||
* STATIC FUNCTIONS
|
||||
**********************/
|
||||
@@ -0,0 +1,113 @@
|
||||
/**
|
||||
* @file lv_cache_entry.h
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef LV_CACHE_ENTRY_H
|
||||
#define LV_CACHE_ENTRY_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*********************
|
||||
* INCLUDES
|
||||
*********************/
|
||||
#include "../lv_types.h"
|
||||
|
||||
/*********************
|
||||
* DEFINES
|
||||
*********************/
|
||||
|
||||
/**********************
|
||||
* TYPEDEFS
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* GLOBAL PROTOTYPES
|
||||
**********************/
|
||||
|
||||
/**
|
||||
* Get the size of a cache entry.
|
||||
* @param node_size The size of the node in the cache.
|
||||
* @return The size of the cache entry.
|
||||
*/
|
||||
uint32_t lv_cache_entry_get_size(const uint32_t node_size);
|
||||
|
||||
/**
|
||||
* Get the reference count of a cache entry.
|
||||
* @param entry The cache entry to get the reference count of.
|
||||
* @return The reference count of the cache entry.
|
||||
*/
|
||||
int32_t lv_cache_entry_get_ref(lv_cache_entry_t * entry);
|
||||
|
||||
/**
|
||||
* Get the node size of a cache entry. Which is the same size with lv_cache_entry_get_size()'s node_size parameter.
|
||||
* @param entry The cache entry to get the node size of.
|
||||
* @return The node size of the cache entry.
|
||||
*/
|
||||
uint32_t lv_cache_entry_get_node_size(lv_cache_entry_t * entry);
|
||||
|
||||
/**
|
||||
* Check if a cache entry is invalid.
|
||||
* @param entry The cache entry to check.
|
||||
* @return True: the cache entry is invalid. False: the cache entry is valid.
|
||||
*/
|
||||
bool lv_cache_entry_is_invalid(lv_cache_entry_t * entry);
|
||||
|
||||
/**
|
||||
* Get the data of a cache entry.
|
||||
* @param entry The cache entry to get the data of.
|
||||
* @return The pointer to the data of the cache entry.
|
||||
*/
|
||||
void * lv_cache_entry_get_data(lv_cache_entry_t * entry);
|
||||
|
||||
/**
|
||||
* Get the cache instance of a cache entry.
|
||||
* @param entry The cache entry to get the cache instance of.
|
||||
* @return The pointer to the cache instance of the cache entry.
|
||||
*/
|
||||
const lv_cache_t * lv_cache_entry_get_cache(const lv_cache_entry_t * entry);
|
||||
|
||||
/**
|
||||
* Get the cache entry of a data. The data should be allocated by the cache instance.
|
||||
* @param data The data to get the cache entry of.
|
||||
* @param node_size The size of the node in the cache.
|
||||
* @return The pointer to the cache entry of the data.
|
||||
*/
|
||||
lv_cache_entry_t * lv_cache_entry_get_entry(void * data, const uint32_t node_size);
|
||||
|
||||
/**
|
||||
* Allocate a cache entry.
|
||||
* @param node_size The size of the node in the cache.
|
||||
* @param cache The cache instance to allocate the cache entry from.
|
||||
* @return The pointer to the allocated cache entry.
|
||||
*/
|
||||
lv_cache_entry_t * lv_cache_entry_alloc(const uint32_t node_size, const lv_cache_t * cache);
|
||||
|
||||
/**
|
||||
* Initialize a cache entry.
|
||||
* @param entry The cache entry to initialize.
|
||||
* @param cache The cache instance to allocate the cache entry from.
|
||||
* @param node_size The size of the node in the cache.
|
||||
*/
|
||||
void lv_cache_entry_init(lv_cache_entry_t * entry, const lv_cache_t * cache, const uint32_t node_size);
|
||||
|
||||
/**
|
||||
* Deallocate a cache entry. And the data of the cache entry will be freed.
|
||||
* @param entry The cache entry to deallocate.
|
||||
*/
|
||||
void lv_cache_entry_delete(lv_cache_entry_t * entry);
|
||||
/*************************
|
||||
* GLOBAL VARIABLES
|
||||
*************************/
|
||||
|
||||
/**********************
|
||||
* MACROS
|
||||
**********************/
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /*extern "C"*/
|
||||
#endif
|
||||
|
||||
#endif /*LV_CACHE_ENTRY_H*/
|
||||
@@ -0,0 +1,52 @@
|
||||
/**
|
||||
* @file lv_cache_entry_private.h
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef LV_CACHE_ENTRY_PRIVATE_H
|
||||
#define LV_CACHE_ENTRY_PRIVATE_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*********************
|
||||
* INCLUDES
|
||||
*********************/
|
||||
#include "../lv_types.h"
|
||||
#include "../../osal/lv_os.h"
|
||||
#include "../lv_profiler.h"
|
||||
|
||||
/*********************
|
||||
* DEFINES
|
||||
*********************/
|
||||
|
||||
/**********************
|
||||
* TYPEDEFS
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* GLOBAL PROTOTYPES
|
||||
**********************/
|
||||
void lv_cache_entry_reset_ref(lv_cache_entry_t * entry);
|
||||
void lv_cache_entry_inc_ref(lv_cache_entry_t * entry);
|
||||
void lv_cache_entry_dec_ref(lv_cache_entry_t * entry);
|
||||
void lv_cache_entry_set_node_size(lv_cache_entry_t * entry, uint32_t node_size);
|
||||
void lv_cache_entry_set_invalid(lv_cache_entry_t * entry, bool is_invalid);
|
||||
void lv_cache_entry_set_cache(lv_cache_entry_t * entry, const lv_cache_t * cache);
|
||||
void * lv_cache_entry_acquire_data(lv_cache_entry_t * entry);
|
||||
void lv_cache_entry_release_data(lv_cache_entry_t * entry, void * user_data);
|
||||
|
||||
/*************************
|
||||
* GLOBAL VARIABLES
|
||||
*************************/
|
||||
|
||||
/**********************
|
||||
* MACROS
|
||||
**********************/
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /*extern "C"*/
|
||||
#endif
|
||||
|
||||
#endif /* LV_CACHE_ENTRY_PRIVATE_H */
|
||||
@@ -0,0 +1,199 @@
|
||||
/**
|
||||
* @file lv_cache_private.h
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef LV_CACHE_PRIVATE_H
|
||||
#define LV_CACHE_PRIVATE_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*********************
|
||||
* INCLUDES
|
||||
*********************/
|
||||
#include "../lv_types.h"
|
||||
#include "../../osal/lv_os.h"
|
||||
|
||||
/*********************
|
||||
* DEFINES
|
||||
*********************/
|
||||
|
||||
/**********************
|
||||
* TYPEDEFS
|
||||
**********************/
|
||||
|
||||
/**
|
||||
* The result of the cache reserve condition callback
|
||||
*/
|
||||
typedef enum {
|
||||
LV_CACHE_RESERVE_COND_OK, /**< The condition is met and no entries need to be evicted */
|
||||
LV_CACHE_RESERVE_COND_TOO_LARGE, /**< The condition is not met and the reserve size is too large */
|
||||
LV_CACHE_RESERVE_COND_NEED_VICTIM, /**< The condition is not met and a victim is needed to be evicted */
|
||||
LV_CACHE_RESERVE_COND_ERROR /**< An error occurred while checking the condition */
|
||||
} lv_cache_reserve_cond_res_t;
|
||||
|
||||
struct _lv_cache_ops_t;
|
||||
struct _lv_cache_t;
|
||||
struct _lv_cache_class_t;
|
||||
struct _lv_cache_entry_t;
|
||||
|
||||
typedef struct _lv_cache_ops_t lv_cache_ops_t;
|
||||
typedef struct _lv_cache_class_t lv_cache_class_t;
|
||||
|
||||
typedef int8_t lv_cache_compare_res_t;
|
||||
typedef bool (*lv_cache_create_cb_t)(void * node, void * user_data);
|
||||
typedef void (*lv_cache_free_cb_t)(void * node, void * user_data);
|
||||
typedef lv_cache_compare_res_t (*lv_cache_compare_cb_t)(const void * a, const void * b);
|
||||
|
||||
/**
|
||||
* The cache instance allocation function, used by the cache class to allocate memory for cache instances.
|
||||
* @return It should return a pointer to the allocated instance.
|
||||
*/
|
||||
typedef void * (*lv_cache_alloc_cb_t)(void);
|
||||
|
||||
/**
|
||||
* The cache instance initialization function, used by the cache class to initialize the cache instance.
|
||||
* @return It should return true if the initialization is successful, false otherwise.
|
||||
*/
|
||||
typedef bool (*lv_cache_init_cb_t)(lv_cache_t * cache);
|
||||
|
||||
/**
|
||||
* The cache instance destruction function, used by the cache class to destroy the cache instance.
|
||||
*/
|
||||
typedef void (*lv_cache_destroy_cb_t)(lv_cache_t * cache, void * user_data);
|
||||
|
||||
/**
|
||||
* The cache get function, used by the cache class to get a cache entry by its key.
|
||||
* @return `NULL` if the key is not found.
|
||||
*/
|
||||
typedef lv_cache_entry_t * (*lv_cache_get_cb_t)(lv_cache_t * cache, const void * key, void * user_data);
|
||||
|
||||
/**
|
||||
* The cache add function, used by the cache class to add a cache entry with a given key.
|
||||
* This function only cares about how to add the entry, it doesn't check if the entry already exists and doesn't care about is it a victim or not.
|
||||
* @return the added cache entry, or NULL if the entry is not added.
|
||||
*/
|
||||
typedef lv_cache_entry_t * (*lv_cache_add_cb_t)(lv_cache_t * cache, const void * key, void * user_data);
|
||||
|
||||
/**
|
||||
* The cache remove function, used by the cache class to remove a cache entry from the cache but doesn't free the memory..
|
||||
* This function only cares about how to remove the entry, it doesn't care about is it a victim or not.
|
||||
*/
|
||||
typedef void (*lv_cache_remove_cb_t)(lv_cache_t * cache, lv_cache_entry_t * entry, void * user_data);
|
||||
|
||||
/**
|
||||
* The cache drop function, used by the cache class to remove a cache entry from the cache and free the memory.
|
||||
*/
|
||||
typedef void (*lv_cache_drop_cb_t)(lv_cache_t * cache, const void * key, void * user_data);
|
||||
|
||||
/**
|
||||
* The cache drop all function, used by the cache class to remove all cache entries from the cache and free the memory.
|
||||
*/
|
||||
typedef void (*lv_cache_drop_all_cb_t)(lv_cache_t * cache, void * user_data);
|
||||
|
||||
/**
|
||||
* The cache get victim function, used by the cache class to get a victim entry to be evicted.
|
||||
*/
|
||||
typedef lv_cache_entry_t * (*lv_cache_get_victim_cb)(lv_cache_t * cache, void * user_data);
|
||||
|
||||
/**
|
||||
* The cache reserve condition function, used by the cache class to check if a new entry can be added to the cache without exceeding its maximum size.
|
||||
* See lv_cache_reserve_cond_res_t for the possible results.
|
||||
*/
|
||||
typedef lv_cache_reserve_cond_res_t (*lv_cache_reserve_cond_cb)(lv_cache_t * cache, const void * key, size_t size,
|
||||
void * user_data);
|
||||
|
||||
/**
|
||||
* The cache iterator creation function, used by the cache class to create an iterator for the cache.
|
||||
* @return A pointer to the created iterator, or NULL if the iterator cannot be created.
|
||||
*/
|
||||
typedef lv_iter_t * (*lv_cache_iter_create_cb)(lv_cache_t * cache);
|
||||
|
||||
/**
|
||||
* The cache operations struct
|
||||
*/
|
||||
struct _lv_cache_ops_t {
|
||||
lv_cache_compare_cb_t compare_cb; /**< Compare function for keys */
|
||||
lv_cache_create_cb_t create_cb; /**< Create function for nodes */
|
||||
lv_cache_free_cb_t free_cb; /**< Free function for nodes */
|
||||
};
|
||||
|
||||
/**
|
||||
* The cache entry struct
|
||||
*/
|
||||
struct _lv_cache_t {
|
||||
const lv_cache_class_t * clz; /**< Cache class. There are two built-in classes:
|
||||
* - lv_cache_class_lru_rb_count for LRU-based cache with count-based eviction policy.
|
||||
* - lv_cache_class_lru_rb_size for LRU-based cache with size-based eviction policy. */
|
||||
|
||||
uint32_t node_size; /**< Size of a node */
|
||||
|
||||
uint32_t max_size; /**< Maximum size of the cache */
|
||||
uint32_t size; /**< Current size of the cache */
|
||||
|
||||
lv_cache_ops_t ops; /**< Cache operations struct _lv_cache_ops_t */
|
||||
|
||||
lv_mutex_t lock; /**< Cache lock used to protect the cache in multithreading environments */
|
||||
|
||||
const char * name; /**< Name of the cache */
|
||||
};
|
||||
|
||||
/**
|
||||
* Cache class struct for building custom cache classes
|
||||
*
|
||||
* Examples:
|
||||
* - lv_cache_class_lru_rb_count for LRU-based cache with count-based eviction policy.
|
||||
* - lv_cache_class_lru_rb_size for LRU-based cache with size-based eviction policy.
|
||||
*/
|
||||
struct _lv_cache_class_t {
|
||||
lv_cache_alloc_cb_t alloc_cb; /**< The allocation function for cache entries */
|
||||
lv_cache_init_cb_t init_cb; /**< The initialization function for cache entries */
|
||||
lv_cache_destroy_cb_t destroy_cb; /**< The destruction function for cache entries */
|
||||
|
||||
lv_cache_get_cb_t get_cb; /**< The get function for cache entries */
|
||||
lv_cache_add_cb_t add_cb; /**< The add function for cache entries */
|
||||
lv_cache_remove_cb_t remove_cb; /**< The remove function for cache entries */
|
||||
lv_cache_drop_cb_t drop_cb; /**< The drop function for cache entries */
|
||||
lv_cache_drop_all_cb_t drop_all_cb; /**< The drop all function for cache entries */
|
||||
lv_cache_get_victim_cb get_victim_cb; /**< The get victim function for cache entries */
|
||||
lv_cache_reserve_cond_cb reserve_cond_cb; /**< The reserve condition function for cache entries */
|
||||
|
||||
lv_cache_iter_create_cb iter_create_cb; /**< The iterator creation function for cache entries */
|
||||
};
|
||||
|
||||
/*-----------------
|
||||
* Cache entry slot
|
||||
*----------------*/
|
||||
|
||||
struct _lv_cache_slot_size_t;
|
||||
|
||||
typedef struct _lv_cache_slot_size_t lv_cache_slot_size_t;
|
||||
|
||||
/**
|
||||
* Cache entry slot struct
|
||||
*
|
||||
* To add new fields to the cache entry, add them to a new struct and add it to the first
|
||||
* field of the cache data struct. And this one is a size slot for the cache entry.
|
||||
*/
|
||||
struct _lv_cache_slot_size_t {
|
||||
size_t size;
|
||||
};
|
||||
/**********************
|
||||
* GLOBAL PROTOTYPES
|
||||
**********************/
|
||||
|
||||
/*************************
|
||||
* GLOBAL VARIABLES
|
||||
*************************/
|
||||
|
||||
/**********************
|
||||
* MACROS
|
||||
**********************/
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /*extern "C"*/
|
||||
#endif
|
||||
|
||||
#endif /*LV_CACHE_PRIVATE_H*/
|
||||
@@ -0,0 +1,771 @@
|
||||
/**
|
||||
* @file lv_anim.c
|
||||
*
|
||||
*/
|
||||
|
||||
/*********************
|
||||
* INCLUDES
|
||||
*********************/
|
||||
#include "lv_anim_private.h"
|
||||
|
||||
#include "../core/lv_global.h"
|
||||
#include "../tick/lv_tick.h"
|
||||
#include "lv_assert.h"
|
||||
#include "lv_timer.h"
|
||||
#include "lv_math.h"
|
||||
#include "../stdlib/lv_mem.h"
|
||||
#include "../stdlib/lv_string.h"
|
||||
|
||||
/*********************
|
||||
* DEFINES
|
||||
*********************/
|
||||
|
||||
/**Perform linear animations in max 1024 steps. Used in `path_cb`s*/
|
||||
#define LV_ANIM_RESOLUTION 1024
|
||||
|
||||
/**log2(LV_ANIM_RESOLUTION)*/
|
||||
#define LV_ANIM_RES_SHIFT 10
|
||||
|
||||
/**In an anim. time this bit indicates that the value is speed, and not time*/
|
||||
#define LV_ANIM_SPEED_MASK 0x80000000
|
||||
|
||||
#define state LV_GLOBAL_DEFAULT()->anim_state
|
||||
#define anim_ll_p &(state.anim_ll)
|
||||
|
||||
/**********************
|
||||
* TYPEDEFS
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* STATIC PROTOTYPES
|
||||
**********************/
|
||||
static void anim_timer(lv_timer_t * param);
|
||||
static void anim_mark_list_change(void);
|
||||
static void anim_completed_handler(lv_anim_t * a);
|
||||
static int32_t lv_anim_path_cubic_bezier(const lv_anim_t * a, int32_t x1,
|
||||
int32_t y1, int32_t x2, int32_t y2);
|
||||
static void lv_anim_pause_for_internal(lv_anim_t * a, uint32_t ms);
|
||||
static void resolve_time(lv_anim_t * a);
|
||||
static bool remove_concurrent_anims(const lv_anim_t * a_current);
|
||||
static void remove_anim(void * a);
|
||||
|
||||
/**********************
|
||||
* STATIC VARIABLES
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* MACROS
|
||||
**********************/
|
||||
#if LV_USE_LOG && LV_LOG_TRACE_ANIM
|
||||
#define LV_TRACE_ANIM(...) LV_LOG_TRACE(__VA_ARGS__)
|
||||
#else
|
||||
#define LV_TRACE_ANIM(...)
|
||||
#endif
|
||||
|
||||
/**********************
|
||||
* GLOBAL FUNCTIONS
|
||||
**********************/
|
||||
|
||||
void lv_anim_core_init(void)
|
||||
{
|
||||
lv_ll_init(anim_ll_p, sizeof(lv_anim_t));
|
||||
state.timer = lv_timer_create(anim_timer, LV_DEF_REFR_PERIOD, NULL);
|
||||
anim_mark_list_change(); /*Turn off the animation timer*/
|
||||
state.anim_list_changed = false;
|
||||
state.anim_run_round = false;
|
||||
}
|
||||
|
||||
void lv_anim_core_deinit(void)
|
||||
{
|
||||
lv_anim_delete_all();
|
||||
}
|
||||
|
||||
void lv_anim_init(lv_anim_t * a)
|
||||
{
|
||||
lv_memzero(a, sizeof(lv_anim_t));
|
||||
a->duration = 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)
|
||||
{
|
||||
LV_TRACE_ANIM("begin");
|
||||
|
||||
/*Do not let two animations for the same 'var' with the same 'exec_cb'*/
|
||||
if(a->early_apply && (a->exec_cb || a->custom_exec_cb)) {
|
||||
remove_concurrent_anims(a);
|
||||
}
|
||||
|
||||
/*Add the new animation to the animation linked list*/
|
||||
lv_anim_t * new_anim = lv_ll_ins_head(anim_ll_p);
|
||||
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 = state.anim_run_round;
|
||||
new_anim->last_timer_run = lv_tick_get();
|
||||
new_anim->is_paused = false;
|
||||
|
||||
/*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;
|
||||
|
||||
}
|
||||
|
||||
resolve_time(new_anim);
|
||||
|
||||
new_anim->current_value = new_anim->path_cb(new_anim);
|
||||
if(new_anim->exec_cb) {
|
||||
new_anim->exec_cb(new_anim->var, new_anim->current_value);
|
||||
}
|
||||
if(new_anim->custom_exec_cb) {
|
||||
new_anim->custom_exec_cb(new_anim, new_anim->current_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();
|
||||
|
||||
LV_TRACE_ANIM("finished");
|
||||
return new_anim;
|
||||
}
|
||||
|
||||
uint32_t lv_anim_get_playtime(const lv_anim_t * a)
|
||||
{
|
||||
if(a->repeat_cnt == LV_ANIM_REPEAT_INFINITE) {
|
||||
return LV_ANIM_PLAYTIME_INFINITE;
|
||||
}
|
||||
|
||||
uint32_t repeat_cnt = a->repeat_cnt;
|
||||
if(repeat_cnt < 1) repeat_cnt = 1;
|
||||
|
||||
uint32_t playtime = a->repeat_delay + a->duration + a->reverse_delay + a->reverse_duration;
|
||||
playtime = playtime * repeat_cnt;
|
||||
return playtime;
|
||||
}
|
||||
|
||||
bool lv_anim_delete(void * var, lv_anim_exec_xcb_t exec_cb)
|
||||
{
|
||||
lv_anim_t * a;
|
||||
bool del_any = false;
|
||||
a = lv_ll_get_head(anim_ll_p);
|
||||
while(a != NULL) {
|
||||
bool del = false;
|
||||
if((a->var == var || var == NULL) && (a->exec_cb == exec_cb || exec_cb == NULL)) {
|
||||
remove_anim(a);
|
||||
anim_mark_list_change(); /*Read by `anim_timer`. It need to know if a delete occurred in
|
||||
the linked list*/
|
||||
del_any = true;
|
||||
del = true;
|
||||
}
|
||||
|
||||
/*Always start from the head on delete, because we don't know
|
||||
*how `anim_ll_p` was changes in `a->deleted_cb` */
|
||||
a = del ? lv_ll_get_head(anim_ll_p) : lv_ll_get_next(anim_ll_p, a);
|
||||
}
|
||||
|
||||
return del_any;
|
||||
}
|
||||
|
||||
void lv_anim_delete_all(void)
|
||||
{
|
||||
lv_ll_clear_custom(anim_ll_p, remove_anim);
|
||||
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(anim_ll_p, a) {
|
||||
if(a->var == var && (a->exec_cb == exec_cb || exec_cb == NULL)) {
|
||||
return a;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
lv_timer_t * lv_anim_get_timer(void)
|
||||
{
|
||||
return state.timer;
|
||||
}
|
||||
|
||||
uint16_t lv_anim_count_running(void)
|
||||
{
|
||||
uint16_t cnt = 0;
|
||||
lv_anim_t * a;
|
||||
LV_LL_READ(anim_ll_p, a) cnt++;
|
||||
|
||||
return cnt;
|
||||
}
|
||||
|
||||
uint32_t lv_anim_speed_clamped(uint32_t speed, uint32_t min_time, uint32_t max_time)
|
||||
{
|
||||
|
||||
if(speed > 10000) {
|
||||
LV_LOG_WARN("speed is truncated to 10000 (was %"LV_PRIu32")", speed);
|
||||
speed = 10230;
|
||||
}
|
||||
if(min_time > 10000) {
|
||||
LV_LOG_WARN("min_time is truncated to 10000 (was %"LV_PRIu32")", min_time);
|
||||
min_time = 10230;
|
||||
}
|
||||
if(max_time > 10000) {
|
||||
LV_LOG_WARN("max_time is truncated to 10000 (was %"LV_PRIu32")", max_time);
|
||||
max_time = 10230;
|
||||
}
|
||||
|
||||
/*Lower the resolution to fit the 0.1023 range*/
|
||||
speed = (speed + 5) / 10;
|
||||
min_time = (min_time + 5) / 10;
|
||||
max_time = (max_time + 5) / 10;
|
||||
|
||||
return LV_ANIM_SPEED_MASK + (max_time << 20) + (min_time << 10) + speed;
|
||||
|
||||
}
|
||||
|
||||
uint32_t lv_anim_speed(uint32_t speed)
|
||||
{
|
||||
return lv_anim_speed_clamped(speed, 0, 10000);
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
time = time == 0 ? 1 : 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->duration, 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)
|
||||
{
|
||||
return lv_anim_path_cubic_bezier(a, LV_BEZIER_VAL_FLOAT(0.42), LV_BEZIER_VAL_FLOAT(0),
|
||||
LV_BEZIER_VAL_FLOAT(1), LV_BEZIER_VAL_FLOAT(1));
|
||||
}
|
||||
|
||||
int32_t lv_anim_path_ease_out(const lv_anim_t * a)
|
||||
{
|
||||
return lv_anim_path_cubic_bezier(a, LV_BEZIER_VAL_FLOAT(0), LV_BEZIER_VAL_FLOAT(0),
|
||||
LV_BEZIER_VAL_FLOAT(0.58), LV_BEZIER_VAL_FLOAT(1));
|
||||
}
|
||||
|
||||
int32_t lv_anim_path_ease_in_out(const lv_anim_t * a)
|
||||
{
|
||||
return lv_anim_path_cubic_bezier(a, LV_BEZIER_VAL_FLOAT(0.42), LV_BEZIER_VAL_FLOAT(0),
|
||||
LV_BEZIER_VAL_FLOAT(0.58), LV_BEZIER_VAL_FLOAT(1));
|
||||
}
|
||||
|
||||
int32_t lv_anim_path_overshoot(const lv_anim_t * a)
|
||||
{
|
||||
return lv_anim_path_cubic_bezier(a, 341, 0, 683, 1300);
|
||||
}
|
||||
|
||||
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->duration, 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*/
|
||||
t = LV_BEZIER_VAL_MAX - t;
|
||||
}
|
||||
else if(t >= 408 && t < 614) {
|
||||
/*First bounce back*/
|
||||
t -= 408;
|
||||
t = t * 5; /*to [0..1024] range*/
|
||||
diff = diff / 20;
|
||||
}
|
||||
else if(t >= 614 && t < 819) {
|
||||
/*Fall back*/
|
||||
t -= 614;
|
||||
t = t * 5; /*to [0..1024] range*/
|
||||
t = LV_BEZIER_VAL_MAX - t;
|
||||
diff = diff / 20;
|
||||
}
|
||||
else if(t >= 819 && t < 921) {
|
||||
/*Second bounce back*/
|
||||
t -= 819;
|
||||
t = t * 10; /*to [0..1024] range*/
|
||||
diff = diff / 40;
|
||||
}
|
||||
else if(t >= 921 && t <= LV_BEZIER_VAL_MAX) {
|
||||
/*Fall back*/
|
||||
t -= 921;
|
||||
t = t * 10; /*to [0..1024] range*/
|
||||
t = LV_BEZIER_VAL_MAX - t;
|
||||
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, 0, 500, 800, LV_BEZIER_VAL_MAX);
|
||||
|
||||
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->duration)
|
||||
return a->end_value;
|
||||
else
|
||||
return a->start_value;
|
||||
}
|
||||
|
||||
int32_t lv_anim_path_custom_bezier3(const lv_anim_t * a)
|
||||
{
|
||||
const lv_anim_bezier3_para_t * para = &a->parameter.bezier3;
|
||||
return lv_anim_path_cubic_bezier(a, para->x1, para->y1, para->x2, para->y2);
|
||||
}
|
||||
|
||||
void lv_anim_set_var(lv_anim_t * a, void * var)
|
||||
{
|
||||
a->var = var;
|
||||
}
|
||||
|
||||
void lv_anim_set_exec_cb(lv_anim_t * a, lv_anim_exec_xcb_t exec_cb)
|
||||
{
|
||||
a->exec_cb = exec_cb;
|
||||
}
|
||||
|
||||
void lv_anim_set_duration(lv_anim_t * a, uint32_t duration)
|
||||
{
|
||||
a->duration = duration;
|
||||
}
|
||||
|
||||
void lv_anim_set_delay(lv_anim_t * a, uint32_t delay)
|
||||
{
|
||||
a->act_time = -(int32_t)(delay);
|
||||
}
|
||||
|
||||
void lv_anim_set_values(lv_anim_t * a, int32_t start, int32_t end)
|
||||
{
|
||||
a->start_value = start;
|
||||
a->current_value = INT32_MIN;
|
||||
a->end_value = end;
|
||||
}
|
||||
|
||||
void lv_anim_set_custom_exec_cb(lv_anim_t * a, lv_anim_custom_exec_cb_t exec_cb)
|
||||
{
|
||||
a->custom_exec_cb = exec_cb;
|
||||
}
|
||||
|
||||
void lv_anim_set_path_cb(lv_anim_t * a, lv_anim_path_cb_t path_cb)
|
||||
{
|
||||
a->path_cb = path_cb;
|
||||
}
|
||||
|
||||
void lv_anim_set_start_cb(lv_anim_t * a, lv_anim_start_cb_t start_cb)
|
||||
{
|
||||
a->start_cb = start_cb;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
void lv_anim_set_completed_cb(lv_anim_t * a, lv_anim_completed_cb_t completed_cb)
|
||||
{
|
||||
a->completed_cb = completed_cb;
|
||||
}
|
||||
|
||||
void lv_anim_set_deleted_cb(lv_anim_t * a, lv_anim_deleted_cb_t deleted_cb)
|
||||
{
|
||||
a->deleted_cb = deleted_cb;
|
||||
}
|
||||
|
||||
void lv_anim_set_reverse_duration(lv_anim_t * a, uint32_t duration)
|
||||
{
|
||||
a->reverse_duration = duration;
|
||||
}
|
||||
|
||||
void lv_anim_set_reverse_time(lv_anim_t * a, uint32_t duration)
|
||||
{
|
||||
lv_anim_set_reverse_duration(a, duration);
|
||||
}
|
||||
|
||||
void lv_anim_set_reverse_delay(lv_anim_t * a, uint32_t delay)
|
||||
{
|
||||
a->reverse_delay = delay;
|
||||
}
|
||||
|
||||
void lv_anim_set_repeat_count(lv_anim_t * a, uint32_t cnt)
|
||||
{
|
||||
a->repeat_cnt = cnt;
|
||||
}
|
||||
|
||||
void lv_anim_set_repeat_delay(lv_anim_t * a, uint32_t delay)
|
||||
{
|
||||
a->repeat_delay = delay;
|
||||
}
|
||||
|
||||
void lv_anim_set_early_apply(lv_anim_t * a, bool en)
|
||||
{
|
||||
a->early_apply = en;
|
||||
}
|
||||
|
||||
void lv_anim_set_user_data(lv_anim_t * a, void * user_data)
|
||||
{
|
||||
a->user_data = user_data;
|
||||
}
|
||||
|
||||
void lv_anim_set_bezier3_param(lv_anim_t * a, int16_t x1, int16_t y1, int16_t x2, int16_t y2)
|
||||
{
|
||||
lv_anim_bezier3_para_t * para = &a->parameter.bezier3;
|
||||
|
||||
para->x1 = x1;
|
||||
para->x2 = x2;
|
||||
para->y1 = y1;
|
||||
para->y2 = y2;
|
||||
}
|
||||
|
||||
uint32_t lv_anim_get_delay(const lv_anim_t * a)
|
||||
{
|
||||
return -a->act_time;
|
||||
}
|
||||
|
||||
uint32_t lv_anim_get_time(const lv_anim_t * a)
|
||||
{
|
||||
return a->duration;
|
||||
}
|
||||
|
||||
uint32_t lv_anim_get_repeat_count(const lv_anim_t * a)
|
||||
{
|
||||
return a->repeat_cnt;
|
||||
}
|
||||
|
||||
void * lv_anim_get_user_data(const lv_anim_t * a)
|
||||
{
|
||||
return a->user_data;
|
||||
}
|
||||
|
||||
bool lv_anim_custom_delete(lv_anim_t * a, lv_anim_custom_exec_cb_t exec_cb)
|
||||
{
|
||||
return lv_anim_delete(a ? a->var : NULL, (lv_anim_exec_xcb_t)exec_cb);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
uint32_t lv_anim_resolve_speed(uint32_t speed_or_time, int32_t start, int32_t end)
|
||||
{
|
||||
/*It was a simple time*/
|
||||
if((speed_or_time & LV_ANIM_SPEED_MASK) == 0) return speed_or_time;
|
||||
|
||||
uint32_t d = LV_ABS(start - end);
|
||||
uint32_t speed = speed_or_time & 0x3FF;
|
||||
uint32_t time = (d * 100) / speed; /*Speed is in 10 units per sec*/
|
||||
uint32_t max_time = (speed_or_time >> 20) & 0x3FF;
|
||||
uint32_t min_time = (speed_or_time >> 10) & 0x3FF;
|
||||
|
||||
return LV_CLAMP(min_time * 10, time, max_time * 10);
|
||||
}
|
||||
|
||||
bool lv_anim_is_paused(lv_anim_t * a)
|
||||
{
|
||||
LV_ASSERT_NULL(a);
|
||||
return a->is_paused;
|
||||
}
|
||||
|
||||
void lv_anim_pause(lv_anim_t * a)
|
||||
{
|
||||
LV_ASSERT_NULL(a);
|
||||
lv_anim_pause_for_internal(a, LV_ANIM_PAUSE_FOREVER);
|
||||
}
|
||||
|
||||
void lv_anim_pause_for(lv_anim_t * a, uint32_t ms)
|
||||
{
|
||||
LV_ASSERT_NULL(a);
|
||||
lv_anim_pause_for_internal(a, ms);
|
||||
}
|
||||
|
||||
void lv_anim_resume(lv_anim_t * a)
|
||||
{
|
||||
LV_ASSERT_NULL(a);
|
||||
a->is_paused = false;
|
||||
a->pause_duration = 0;
|
||||
a->run_round = state.anim_run_round;
|
||||
}
|
||||
|
||||
/**********************
|
||||
* STATIC FUNCTIONS
|
||||
**********************/
|
||||
|
||||
/**
|
||||
* Periodically handle the animations.
|
||||
* @param param unused
|
||||
*/
|
||||
static void anim_timer(lv_timer_t * param)
|
||||
{
|
||||
LV_UNUSED(param);
|
||||
|
||||
/*Flip the run round*/
|
||||
state.anim_run_round = state.anim_run_round ? false : true;
|
||||
|
||||
lv_anim_t * a = lv_ll_get_head(anim_ll_p);
|
||||
while(a != NULL) {
|
||||
uint32_t elaps = lv_tick_elaps(a->last_timer_run);
|
||||
|
||||
if(a->is_paused) {
|
||||
const uint32_t time_paused = lv_tick_elaps(a->pause_time);
|
||||
const bool is_pause_over = a->pause_duration != LV_ANIM_PAUSE_FOREVER && time_paused >= a->pause_duration;
|
||||
|
||||
if(is_pause_over) {
|
||||
const uint32_t pause_overrun = time_paused - a->pause_duration;
|
||||
a->is_paused = false;
|
||||
a->act_time += pause_overrun;
|
||||
a->run_round = !state.anim_run_round;
|
||||
}
|
||||
}
|
||||
else {
|
||||
a->act_time += elaps;
|
||||
}
|
||||
a->last_timer_run = lv_tick_get();
|
||||
|
||||
/*It can be set by `lv_anim_delete()` typically in `end_cb`. If set then an animation delete
|
||||
* happened in `anim_completed_handler` which could make this linked list reading corrupt
|
||||
* because the list is changed meanwhile
|
||||
*/
|
||||
state.anim_list_changed = false;
|
||||
|
||||
if(!a->is_paused && a->run_round != state.anim_run_round) {
|
||||
a->run_round = state.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`*/
|
||||
if(!a->start_cb_called && a->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;
|
||||
}
|
||||
|
||||
resolve_time(a);
|
||||
|
||||
if(a->start_cb) a->start_cb(a);
|
||||
a->start_cb_called = 1;
|
||||
|
||||
/*Do not let two animations for the same 'var' with the same 'exec_cb'*/
|
||||
remove_concurrent_anims(a);
|
||||
}
|
||||
|
||||
if(a->act_time >= 0) {
|
||||
int32_t act_time_original = a->act_time; /*The unclipped version is used later to correctly repeat the animation*/
|
||||
if(a->act_time > a->duration) a->act_time = a->duration;
|
||||
|
||||
int32_t act_time_before_exec = a->act_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(!state.anim_list_changed && a->custom_exec_cb) a->custom_exec_cb(a, new_value);
|
||||
}
|
||||
|
||||
if(!state.anim_list_changed) {
|
||||
/*Restore the original time to see is there is over time.
|
||||
*Restore only if it wasn't changed in the `exec_cb` for some special reasons.*/
|
||||
if(a->act_time == act_time_before_exec) a->act_time = act_time_original;
|
||||
|
||||
/*If the time is elapsed the animation is ready*/
|
||||
if(a->act_time >= a->duration) {
|
||||
anim_completed_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(state.anim_list_changed)
|
||||
a = lv_ll_get_head(anim_ll_p);
|
||||
else
|
||||
a = lv_ll_get_next(anim_ll_p, a);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when an animation is completed to do the necessary things
|
||||
* e.g. repeat, play in reverse, delete etc.
|
||||
* @param a pointer to an animation descriptor
|
||||
*/
|
||||
static void anim_completed_handler(lv_anim_t * a)
|
||||
{
|
||||
/*In the end of a forward anim decrement repeat cnt.*/
|
||||
if(a->reverse_play_in_progress == 0 && a->repeat_cnt > 0 && a->repeat_cnt != LV_ANIM_REPEAT_INFINITE) {
|
||||
a->repeat_cnt--;
|
||||
}
|
||||
|
||||
/*Delete animation if
|
||||
* - no repeat left and no reverse play scheduled (simple one shot animation); or
|
||||
* - no repeat, reverse play enabled (reverse_duration != 0) and reverse play is completed. */
|
||||
if(a->repeat_cnt == 0 && (a->reverse_duration == 0 || a->reverse_play_in_progress == 1)) {
|
||||
|
||||
/*Delete the animation from the list.
|
||||
* This way the `completed_cb` will see the animations like it's animation is already deleted*/
|
||||
lv_ll_remove(anim_ll_p, a);
|
||||
/*Flag that the list has changed*/
|
||||
anim_mark_list_change();
|
||||
|
||||
/*Call the callback function at the end*/
|
||||
if(a->completed_cb != NULL) a->completed_cb(a);
|
||||
if(a->deleted_cb != NULL) a->deleted_cb(a);
|
||||
lv_free(a);
|
||||
}
|
||||
/*If the animation is not deleted then restart it*/
|
||||
else {
|
||||
/*Restart the animation. If the time is over a little compensate it.*/
|
||||
int32_t over_time = 0;
|
||||
if(a->act_time > a->duration) over_time = a->act_time - a->duration;
|
||||
a->act_time = over_time - (int32_t)(a->repeat_delay);
|
||||
/*Swap start and end values in reverse-play mode*/
|
||||
if(a->reverse_duration != 0) {
|
||||
/*If now now playing in reverse, use the 'reverse_delay'.*/
|
||||
if(a->reverse_play_in_progress == 0) a->act_time = -(int32_t)(a->reverse_delay);
|
||||
|
||||
/*Toggle reverse-play state*/
|
||||
a->reverse_play_in_progress = a->reverse_play_in_progress == 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 reverse_duration*/
|
||||
tmp = a->duration;
|
||||
a->duration = a->reverse_duration;
|
||||
a->reverse_duration = tmp;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void anim_mark_list_change(void)
|
||||
{
|
||||
state.anim_list_changed = true;
|
||||
if(lv_ll_get_head(anim_ll_p) == NULL)
|
||||
lv_timer_pause(state.timer);
|
||||
else
|
||||
lv_timer_resume(state.timer);
|
||||
}
|
||||
|
||||
static int32_t lv_anim_path_cubic_bezier(const lv_anim_t * a, int32_t x1, int32_t y1, int32_t x2, int32_t y2)
|
||||
{
|
||||
/*Calculate the current step*/
|
||||
uint32_t t = lv_map(a->act_time, 0, a->duration, 0, LV_BEZIER_VAL_MAX);
|
||||
int32_t step = lv_cubic_bezier(t, x1, y1, x2, y2);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
static void lv_anim_pause_for_internal(lv_anim_t * a, uint32_t ms)
|
||||
{
|
||||
|
||||
a->is_paused = true;
|
||||
a->pause_time = lv_tick_get();
|
||||
a->pause_duration = ms;
|
||||
}
|
||||
|
||||
static void resolve_time(lv_anim_t * a)
|
||||
{
|
||||
a->duration = lv_anim_resolve_speed(a->duration, a->start_value, a->end_value);
|
||||
a->reverse_duration = lv_anim_resolve_speed(a->reverse_duration, a->start_value, a->end_value);
|
||||
a->reverse_delay = lv_anim_resolve_speed(a->reverse_delay, a->start_value, a->end_value);
|
||||
a->repeat_delay = lv_anim_resolve_speed(a->repeat_delay, a->start_value, a->end_value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove animations which are animating the same var with the same exec_cb
|
||||
* and they are already running or they have early_apply
|
||||
* @param a_current the current animation, use its var and exec_cb as reference to know what to remove
|
||||
* @return true: at least one animation was delete
|
||||
*/
|
||||
static bool remove_concurrent_anims(const lv_anim_t * a_current)
|
||||
{
|
||||
if(a_current->exec_cb == NULL && a_current->custom_exec_cb == NULL) return false;
|
||||
|
||||
lv_anim_t * a;
|
||||
bool del_any = false;
|
||||
a = lv_ll_get_head(anim_ll_p);
|
||||
while(a != NULL) {
|
||||
bool del = false;
|
||||
/*We can't test for custom_exec_cb equality because in the MicroPython binding
|
||||
*a wrapper callback is used here an the real callback data is stored in the `user_data`.
|
||||
*Therefore equality check would remove all animations.*/
|
||||
if(a != a_current &&
|
||||
(a->act_time >= 0 || a->early_apply) &&
|
||||
(a->var == a_current->var) &&
|
||||
((a->exec_cb && a->exec_cb == a_current->exec_cb)
|
||||
/*|| (a->custom_exec_cb && a->custom_exec_cb == a_current->custom_exec_cb)*/)) {
|
||||
lv_ll_remove(anim_ll_p, a);
|
||||
if(a->deleted_cb != NULL) a->deleted_cb(a);
|
||||
lv_free(a);
|
||||
/*Read by `anim_timer`. It need to know if a delete occurred in the linked list*/
|
||||
anim_mark_list_change();
|
||||
|
||||
del_any = true;
|
||||
del = true;
|
||||
}
|
||||
|
||||
/*Always start from the head on delete, because we don't know
|
||||
*how `anim_ll_p` was changes in `a->deleted_cb` */
|
||||
a = del ? lv_ll_get_head(anim_ll_p) : lv_ll_get_next(anim_ll_p, a);
|
||||
}
|
||||
|
||||
return del_any;
|
||||
}
|
||||
|
||||
static void remove_anim(void * a)
|
||||
{
|
||||
lv_anim_t * anim = a;
|
||||
lv_ll_remove(anim_ll_p, a);
|
||||
if(anim->deleted_cb != NULL) anim->deleted_cb(anim);
|
||||
lv_free(a);
|
||||
}
|
||||
@@ -0,0 +1,570 @@
|
||||
/**
|
||||
* @file lv_anim.h
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef LV_ANIM_H
|
||||
#define LV_ANIM_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*********************
|
||||
* INCLUDES
|
||||
*********************/
|
||||
#include "../lv_conf_internal.h"
|
||||
#include "lv_types.h"
|
||||
#include "lv_math.h"
|
||||
#include "lv_timer.h"
|
||||
#include "lv_ll.h"
|
||||
|
||||
/*********************
|
||||
* DEFINES
|
||||
*********************/
|
||||
|
||||
#define LV_ANIM_REPEAT_INFINITE 0xFFFFFFFF
|
||||
#define LV_ANIM_PLAYTIME_INFINITE 0xFFFFFFFF
|
||||
#define LV_ANIM_PAUSE_FOREVER 0xFFFFFFFF
|
||||
|
||||
/*
|
||||
* Macros used to set cubic-bezier anim parameter.
|
||||
* Parameters come from https://easings.net/
|
||||
*
|
||||
* Usage:
|
||||
*
|
||||
* lv_anim_t a;
|
||||
* lv_anim_init(&a);
|
||||
* ...
|
||||
* lv_anim_set_path_cb(&a, lv_anim_path_custom_bezier3);
|
||||
* LV_ANIM_SET_EASE_IN_SINE(&a); //Set cubic-bezier anim parameter to easeInSine
|
||||
* ...
|
||||
* lv_anim_start(&a);
|
||||
*/
|
||||
|
||||
#define _PARA(a, x1, y1, x2, y2) ((a)->parameter.bezier3 = \
|
||||
(lv_anim_bezier3_para_t) { \
|
||||
LV_BEZIER_VAL_FLOAT(x1), LV_BEZIER_VAL_FLOAT(y1), \
|
||||
LV_BEZIER_VAL_FLOAT(x2), LV_BEZIER_VAL_FLOAT(y2) } \
|
||||
)
|
||||
|
||||
#define LV_ANIM_SET_EASE_IN_SINE(a) _PARA(a, 0.12, 0, 0.39, 0)
|
||||
#define LV_ANIM_SET_EASE_OUT_SINE(a) _PARA(a, 0.61, 1, 0.88, 1)
|
||||
#define LV_ANIM_SET_EASE_IN_OUT_SINE(a) _PARA(a, 0.37, 0, 0.63, 1)
|
||||
#define LV_ANIM_SET_EASE_IN_QUAD(a) _PARA(a, 0.11, 0, 0.5, 0)
|
||||
#define LV_ANIM_SET_EASE_OUT_QUAD(a) _PARA(a, 0.5, 1, 0.89, 1)
|
||||
#define LV_ANIM_SET_EASE_IN_OUT_QUAD(a) _PARA(a, 0.45, 0, 0.55, 1)
|
||||
#define LV_ANIM_SET_EASE_IN_CUBIC(a) _PARA(a, 0.32, 0, 0.67, 0)
|
||||
#define LV_ANIM_SET_EASE_OUT_CUBIC(a) _PARA(a, 0.33, 1, 0.68, 1)
|
||||
#define LV_ANIM_SET_EASE_IN_OUT_CUBIC(a) _PARA(a, 0.65, 0, 0.35, 1)
|
||||
#define LV_ANIM_SET_EASE_IN_QUART(a) _PARA(a, 0.5, 0, 0.75, 0)
|
||||
#define LV_ANIM_SET_EASE_OUT_QUART(a) _PARA(a, 0.25, 1, 0.5, 1)
|
||||
#define LV_ANIM_SET_EASE_IN_OUT_QUART(a) _PARA(a, 0.76, 0, 0.24, 1)
|
||||
#define LV_ANIM_SET_EASE_IN_QUINT(a) _PARA(a, 0.64, 0, 0.78, 0)
|
||||
#define LV_ANIM_SET_EASE_OUT_QUINT(a) _PARA(a, 0.22, 1, 0.36, 1)
|
||||
#define LV_ANIM_SET_EASE_IN_OUT_QUINT(a) _PARA(a, 0.83, 0, 0.17, 1)
|
||||
#define LV_ANIM_SET_EASE_IN_EXPO(a) _PARA(a, 0.7, 0, 0.84, 0)
|
||||
#define LV_ANIM_SET_EASE_OUT_EXPO(a) _PARA(a, 0.16, 1, 0.3, 1)
|
||||
#define LV_ANIM_SET_EASE_IN_OUT_EXPO(a) _PARA(a, 0.87, 0, 0.13, 1)
|
||||
#define LV_ANIM_SET_EASE_IN_CIRC(a) _PARA(a, 0.55, 0, 1, 0.45)
|
||||
#define LV_ANIM_SET_EASE_OUT_CIRC(a) _PARA(a, 0, 0.55, 0.45, 1)
|
||||
#define LV_ANIM_SET_EASE_IN_OUT_CIRC(a) _PARA(a, 0.85, 0, 0.15, 1)
|
||||
#define LV_ANIM_SET_EASE_IN_BACK(a) _PARA(a, 0.36, 0, 0.66, -0.56)
|
||||
#define LV_ANIM_SET_EASE_OUT_BACK(a) _PARA(a, 0.34, 1.56, 0.64, 1)
|
||||
#define LV_ANIM_SET_EASE_IN_OUT_BACK(a) _PARA(a, 0.68, -0.6, 0.32, 1.6)
|
||||
|
||||
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*/
|
||||
#define LV_ANIM_OFF false
|
||||
#define LV_ANIM_ON true
|
||||
typedef bool lv_anim_enable_t;
|
||||
|
||||
/** Get the current value during an animation*/
|
||||
typedef int32_t (*lv_anim_path_cb_t)(const 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)(lv_anim_t *, int32_t);
|
||||
|
||||
/** Callback to call when the animation is ready*/
|
||||
typedef void (*lv_anim_completed_cb_t)(lv_anim_t *);
|
||||
|
||||
/** Callback to call when the animation really stars (considering `delay`)*/
|
||||
typedef void (*lv_anim_start_cb_t)(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)(lv_anim_t *);
|
||||
|
||||
/** Callback used when the animation is deleted*/
|
||||
typedef void (*lv_anim_deleted_cb_t)(lv_anim_t *);
|
||||
|
||||
/** Parameter used when path is custom_bezier */
|
||||
typedef struct {
|
||||
int16_t x1;
|
||||
int16_t y1;
|
||||
int16_t x2;
|
||||
int16_t y2;
|
||||
} lv_anim_bezier3_para_t;
|
||||
|
||||
/** Describes an animation*/
|
||||
struct _lv_anim_t {
|
||||
void * var; /**< Variable (Widget or other user-provided object) to animate */
|
||||
lv_anim_exec_xcb_t exec_cb; /**< Function to execute to animate */
|
||||
lv_anim_custom_exec_cb_t custom_exec_cb; /**< Function to execute to animate,
|
||||
* same purpose as exec_cb but different parameters */
|
||||
lv_anim_start_cb_t start_cb; /**< Call it when animation is starts (considering `delay`) */
|
||||
lv_anim_completed_cb_t completed_cb; /**< Call it when animation is fully completed */
|
||||
lv_anim_deleted_cb_t deleted_cb; /**< Call it when animation is deleted */
|
||||
lv_anim_get_value_cb_t get_value_cb; /**< Get current value in relative mode */
|
||||
void * user_data; /**< Custom user data */
|
||||
lv_anim_path_cb_t path_cb; /**< Provides path (curve) of animation */
|
||||
int32_t start_value; /**< Start value */
|
||||
int32_t current_value; /**< Current value */
|
||||
int32_t end_value; /**< End value */
|
||||
int32_t duration; /**< Animation duration in ms */
|
||||
int32_t act_time; /**< Ms elapsed since animation started. Set to negative to make delay. */
|
||||
uint32_t reverse_delay; /**< Wait (in ms) after forward play ends and before reverse play begins. */
|
||||
uint32_t reverse_duration; /**< Reverse animation duration in ms */
|
||||
uint32_t repeat_delay; /**< Wait before repeating */
|
||||
uint32_t repeat_cnt; /**< Repeat count for animation */
|
||||
union _lv_anim_path_para_t {
|
||||
lv_anim_bezier3_para_t bezier3; /**< Parameter used when path is custom_bezier */
|
||||
} parameter;
|
||||
|
||||
/* Animation system use these - user shouldn't set */
|
||||
uint32_t last_timer_run;
|
||||
uint32_t pause_time; /**<The time when the animation was paused*/
|
||||
uint32_t pause_duration; /**<The amount of the time the animation must stay paused for*/
|
||||
uint8_t is_paused : 1; /**<Indicates that the animation is paused */
|
||||
uint8_t reverse_play_in_progress : 1; /**< Reverse play is in progress */
|
||||
uint8_t run_round : 1; /**< When not equal to global.anim_state.anim_run_round (which toggles each
|
||||
* time animation timer executes), indicates this animation needs to be updated. */
|
||||
uint8_t start_cb_called : 1; /**< Indicates that `start_cb` was already called */
|
||||
uint8_t early_apply : 1; /**< 1: Apply start value immediately even is there is a `delay` */
|
||||
};
|
||||
|
||||
/**********************
|
||||
* GLOBAL PROTOTYPES
|
||||
**********************/
|
||||
|
||||
/**
|
||||
* Initialize an animation variable.
|
||||
* E.g.:
|
||||
* lv_anim_t a;
|
||||
* lv_anim_init(&a);
|
||||
* lv_anim_set_...(&a);
|
||||
* lv_anim_start(&a);
|
||||
* @param a pointer to an `lv_anim_t` variable to initialize
|
||||
*/
|
||||
void lv_anim_init(lv_anim_t * a);
|
||||
|
||||
/**
|
||||
* Set a variable to animate
|
||||
* @param a pointer to an initialized `lv_anim_t` variable
|
||||
* @param var pointer to a variable to animate
|
||||
*/
|
||||
void lv_anim_set_var(lv_anim_t * a, void * 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
|
||||
*/
|
||||
void lv_anim_set_exec_cb(lv_anim_t * a, lv_anim_exec_xcb_t 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
|
||||
*/
|
||||
void lv_anim_set_duration(lv_anim_t * a, uint32_t 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
|
||||
*/
|
||||
void lv_anim_set_delay(lv_anim_t * a, uint32_t delay);
|
||||
|
||||
/**
|
||||
* Resumes a paused animation
|
||||
* @param a pointer to an initialized `lv_anim_t` variable
|
||||
*/
|
||||
void lv_anim_resume(lv_anim_t * a);
|
||||
|
||||
/**
|
||||
* Pauses the animation
|
||||
* @param a pointer to an initialized `lv_anim_t` variable
|
||||
*/
|
||||
void lv_anim_pause(lv_anim_t * a);
|
||||
|
||||
/**
|
||||
* Pauses the animation for ms milliseconds
|
||||
* @param a pointer to an initialized `lv_anim_t` variable
|
||||
* @param ms the pause time in milliseconds
|
||||
*/
|
||||
void lv_anim_pause_for(lv_anim_t * a, uint32_t ms);
|
||||
|
||||
/**
|
||||
* Check if the animation is paused
|
||||
* @param a pointer to an initialized `lv_anim_t` variable
|
||||
* @return true if the animation is paused else false
|
||||
*/
|
||||
bool lv_anim_is_paused(lv_anim_t * a);
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
void lv_anim_set_values(lv_anim_t * a, int32_t start, int32_t 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.
|
||||
* @param a pointer to an initialized `lv_anim_t` variable
|
||||
* @param exec_cb a function to execute.
|
||||
*/
|
||||
void lv_anim_set_custom_exec_cb(lv_anim_t * a, lv_anim_custom_exec_cb_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.
|
||||
*/
|
||||
void lv_anim_set_path_cb(lv_anim_t * a, lv_anim_path_cb_t 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
|
||||
*/
|
||||
void lv_anim_set_start_cb(lv_anim_t * a, lv_anim_start_cb_t 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
|
||||
*/
|
||||
void lv_anim_set_get_value_cb(lv_anim_t * a, lv_anim_get_value_cb_t get_value_cb);
|
||||
|
||||
/**
|
||||
* Set a function call when the animation is completed
|
||||
* @param a pointer to an initialized `lv_anim_t` variable
|
||||
* @param completed_cb a function call when the animation is fully completed
|
||||
*/
|
||||
void lv_anim_set_completed_cb(lv_anim_t * a, lv_anim_completed_cb_t completed_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
|
||||
*/
|
||||
void lv_anim_set_deleted_cb(lv_anim_t * a, lv_anim_deleted_cb_t 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 duration duration of playback animation in milliseconds. 0: disable playback
|
||||
*/
|
||||
void lv_anim_set_reverse_duration(lv_anim_t * a, uint32_t duration);
|
||||
|
||||
/**
|
||||
* Legacy `lv_anim_set_reverse_time` API will be removed soon, use `lv_anim_set_reverse_duration` instead.
|
||||
*/
|
||||
void lv_anim_set_reverse_time(lv_anim_t * a, uint32_t duration);
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
void lv_anim_set_reverse_delay(lv_anim_t * a, uint32_t 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.
|
||||
*/
|
||||
void lv_anim_set_repeat_count(lv_anim_t * a, uint32_t 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.
|
||||
*/
|
||||
void lv_anim_set_repeat_delay(lv_anim_t * a, uint32_t 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
|
||||
*/
|
||||
void lv_anim_set_early_apply(lv_anim_t * a, bool 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.
|
||||
*/
|
||||
void lv_anim_set_user_data(lv_anim_t * a, void * user_data);
|
||||
|
||||
/**
|
||||
* Set parameter for cubic bezier path
|
||||
* @param a pointer to an initialized `lv_anim_t` variable
|
||||
* @param x1 first control point X
|
||||
* @param y1 first control point Y
|
||||
* @param x2 second control point X
|
||||
* @param y2 second control point Y
|
||||
*/
|
||||
void lv_anim_set_bezier3_param(lv_anim_t * a, int16_t x1, int16_t y1, int16_t x2, int16_t y2);
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
uint32_t lv_anim_get_delay(const lv_anim_t * a);
|
||||
|
||||
/**
|
||||
* 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(const lv_anim_t * a);
|
||||
|
||||
/**
|
||||
* Get the duration of an animation
|
||||
* @param a pointer to an initialized `lv_anim_t` variable
|
||||
* @return the duration of the animation in milliseconds
|
||||
*/
|
||||
uint32_t lv_anim_get_time(const lv_anim_t * a);
|
||||
|
||||
/**
|
||||
* Get the repeat count of the animation.
|
||||
* @param a pointer to an initialized `lv_anim_t` variable
|
||||
* @return the repeat count or `LV_ANIM_REPEAT_INFINITE` for infinite repetition. 0: disabled repetition.
|
||||
*/
|
||||
uint32_t lv_anim_get_repeat_count(const 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
|
||||
*/
|
||||
void * lv_anim_get_user_data(const lv_anim_t * a);
|
||||
|
||||
/**
|
||||
* Delete animation(s) 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_delete(void * var, lv_anim_exec_xcb_t exec_cb);
|
||||
|
||||
/**
|
||||
* Delete all the animations
|
||||
*/
|
||||
void lv_anim_delete_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.
|
||||
*/
|
||||
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
|
||||
*/
|
||||
bool lv_anim_custom_delete(lv_anim_t * a, lv_anim_custom_exec_cb_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.
|
||||
*/
|
||||
lv_anim_t * lv_anim_custom_get(lv_anim_t * a, lv_anim_custom_exec_cb_t exec_cb);
|
||||
|
||||
/**
|
||||
* Get the number of currently running animations
|
||||
* @return the number of running animations
|
||||
*/
|
||||
uint16_t lv_anim_count_running(void);
|
||||
|
||||
/**
|
||||
* Store the speed as a special value which can be used as time in animations.
|
||||
* It will be converted to time internally based on the start and end values.
|
||||
* The return value can be used as a constant with multiple animations
|
||||
* and let LVGL convert the speed to time based on the actual values.
|
||||
* LIMITATION: the max time stored this way can be 10,000 ms.
|
||||
* @param speed the speed of the animation in with unit / sec resolution in 0..10k range
|
||||
* @return a special value which can be used as an animation time
|
||||
* @note internally speed is stored as 10 unit/sec
|
||||
*/
|
||||
uint32_t lv_anim_speed(uint32_t speed);
|
||||
|
||||
/**
|
||||
* Store the speed as a special value which can be used as time in animations.
|
||||
* It will be converted to time internally based on the start and end values.
|
||||
* The return value can be used as a constant with multiple animations
|
||||
* and let LVGL convert the speed to time based on the actual values.
|
||||
* @param speed the speed of the animation in as unit / sec resolution in 0..10k range
|
||||
* @param min_time the minimum time in 0..10k range
|
||||
* @param max_time the maximum time in 0..10k range
|
||||
* @return a special value in where all three values are stored and can be used as an animation time
|
||||
* @note internally speed is stored as 10 unit/sec
|
||||
* @note internally min/max_time are stored with 10 ms unit
|
||||
*
|
||||
*/
|
||||
uint32_t lv_anim_speed_clamped(uint32_t speed, uint32_t min_time, uint32_t max_time);
|
||||
|
||||
/**
|
||||
* Resolve the speed (created with `lv_anim_speed` or `lv_anim_speed_clamped`) to time
|
||||
* based on start and end values.
|
||||
* @param speed return values of `lv_anim_speed` or `lv_anim_speed_clamped`
|
||||
* @param start the start value of the animation
|
||||
* @param end the end value of the animation
|
||||
* @return the time required to get from `start` to `end` with the given `speed` setting
|
||||
*/
|
||||
uint32_t lv_anim_resolve_speed(uint32_t speed, int32_t start, int32_t end);
|
||||
|
||||
/**
|
||||
* Calculate the time of an animation based on its speed, start and end values.
|
||||
* It simpler than `lv_anim_speed` or `lv_anim_speed_clamped` as it converts
|
||||
* speed, start, and end to a time immediately.
|
||||
* As it's simpler there is no limit on the maximum time.
|
||||
* @param speed the speed of the animation
|
||||
* @param start the start value
|
||||
* @param end the end value
|
||||
* @return the time of the animation in milliseconds
|
||||
*/
|
||||
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);
|
||||
|
||||
/**
|
||||
* A custom cubic bezier animation path, need to specify cubic-parameters in a->parameter.bezier3
|
||||
* @param a pointer to an animation
|
||||
* @return the current value to set
|
||||
*/
|
||||
int32_t lv_anim_path_custom_bezier3(const lv_anim_t * a);
|
||||
|
||||
/**********************
|
||||
* GLOBAL VARIABLES
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* MACROS
|
||||
**********************/
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /*extern "C"*/
|
||||
#endif
|
||||
|
||||
#endif /*LV_ANIM_H*/
|
||||
@@ -0,0 +1,56 @@
|
||||
/**
|
||||
* @file lv_anim_private.h
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef LV_ANIM_PRIVATE_H
|
||||
#define LV_ANIM_PRIVATE_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*********************
|
||||
* INCLUDES
|
||||
*********************/
|
||||
|
||||
#include "lv_anim.h"
|
||||
|
||||
/*********************
|
||||
* DEFINES
|
||||
*********************/
|
||||
|
||||
/**********************
|
||||
* TYPEDEFS
|
||||
**********************/
|
||||
|
||||
typedef struct {
|
||||
bool anim_list_changed;
|
||||
bool anim_run_round;
|
||||
lv_timer_t * timer;
|
||||
lv_ll_t anim_ll;
|
||||
} lv_anim_state_t;
|
||||
|
||||
/**********************
|
||||
* GLOBAL PROTOTYPES
|
||||
**********************/
|
||||
|
||||
/**
|
||||
* Init the animation module
|
||||
*/
|
||||
void lv_anim_core_init(void);
|
||||
|
||||
/**
|
||||
* Deinit the animation module
|
||||
*/
|
||||
void lv_anim_core_deinit(void);
|
||||
|
||||
/**********************
|
||||
* MACROS
|
||||
**********************/
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /*extern "C"*/
|
||||
#endif
|
||||
|
||||
#endif /*LV_ANIM_PRIVATE_H*/
|
||||
@@ -0,0 +1,307 @@
|
||||
/**
|
||||
* @file lv_anim_timeline.c
|
||||
*
|
||||
*/
|
||||
|
||||
/*********************
|
||||
* INCLUDES
|
||||
*********************/
|
||||
#include "lv_anim_private.h"
|
||||
#include "lv_assert.h"
|
||||
#include "lv_anim_timeline.h"
|
||||
#include "../stdlib/lv_mem.h"
|
||||
#include "../stdlib/lv_string.h"
|
||||
|
||||
/*********************
|
||||
* DEFINES
|
||||
*********************/
|
||||
|
||||
/**********************
|
||||
* TYPEDEFS
|
||||
**********************/
|
||||
/*Data of anim_timeline_dsc*/
|
||||
typedef struct {
|
||||
lv_anim_t anim;
|
||||
uint32_t start_time;
|
||||
uint8_t is_started : 1;
|
||||
uint8_t is_completed : 1;
|
||||
} 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*/
|
||||
uint32_t act_time; /**< Current time of the animation*/
|
||||
bool reverse; /**< Reverse playback*/
|
||||
uint32_t repeat_count; /**< Repeat count*/
|
||||
uint32_t repeat_delay; /**< Wait before repeat*/
|
||||
};
|
||||
|
||||
/**********************
|
||||
* STATIC PROTOTYPES
|
||||
**********************/
|
||||
static void anim_timeline_exec_cb(void * var, int32_t v);
|
||||
static void anim_timeline_set_act_time(lv_anim_timeline_t * at, uint32_t act_time);
|
||||
static int32_t anim_timeline_path_cb(const lv_anim_t * a);
|
||||
|
||||
/**********************
|
||||
* STATIC VARIABLES
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* MACROS
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* GLOBAL FUNCTIONS
|
||||
**********************/
|
||||
|
||||
lv_anim_timeline_t * lv_anim_timeline_create(void)
|
||||
{
|
||||
lv_anim_timeline_t * at = lv_malloc_zeroed(sizeof(lv_anim_timeline_t));
|
||||
LV_ASSERT_MALLOC(at);
|
||||
return at;
|
||||
}
|
||||
|
||||
void lv_anim_timeline_delete(lv_anim_timeline_t * at)
|
||||
{
|
||||
LV_ASSERT_NULL(at);
|
||||
|
||||
lv_anim_timeline_pause(at);
|
||||
|
||||
lv_free(at->anim_dsc);
|
||||
lv_free(at);
|
||||
}
|
||||
|
||||
void lv_anim_timeline_add(lv_anim_timeline_t * at, uint32_t start_time, const lv_anim_t * a)
|
||||
{
|
||||
LV_ASSERT_NULL(at);
|
||||
|
||||
at->anim_dsc_cnt++;
|
||||
at->anim_dsc = lv_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;
|
||||
}
|
||||
|
||||
uint32_t lv_anim_timeline_start(lv_anim_timeline_t * at)
|
||||
{
|
||||
LV_ASSERT_NULL(at);
|
||||
|
||||
uint32_t playtime = lv_anim_timeline_get_playtime(at);
|
||||
uint32_t repeat = at->repeat_count;
|
||||
uint32_t delay = at->repeat_delay;
|
||||
uint32_t start = at->act_time;
|
||||
uint32_t end = at->reverse ? 0 : playtime;
|
||||
uint32_t duration = end > start ? end - start : start - end;
|
||||
|
||||
if((!at->reverse && at->act_time == 0) || (at->reverse && at->act_time == playtime)) {
|
||||
for(uint32_t i = 0; i < at->anim_dsc_cnt; i++) {
|
||||
at->anim_dsc[i].is_started = 0;
|
||||
at->anim_dsc[i].is_completed = 0;
|
||||
}
|
||||
}
|
||||
|
||||
lv_anim_t a;
|
||||
lv_anim_init(&a);
|
||||
lv_anim_set_var(&a, at);
|
||||
lv_anim_set_exec_cb(&a, anim_timeline_exec_cb);
|
||||
lv_anim_set_values(&a, start, end);
|
||||
lv_anim_set_duration(&a, duration);
|
||||
lv_anim_set_path_cb(&a, anim_timeline_path_cb);
|
||||
lv_anim_set_repeat_count(&a, repeat);
|
||||
lv_anim_set_repeat_delay(&a, delay);
|
||||
lv_anim_start(&a);
|
||||
return playtime;
|
||||
}
|
||||
|
||||
void lv_anim_timeline_pause(lv_anim_timeline_t * at)
|
||||
{
|
||||
LV_ASSERT_NULL(at);
|
||||
|
||||
lv_anim_delete(at, anim_timeline_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_repeat_count(lv_anim_timeline_t * at, uint32_t cnt)
|
||||
{
|
||||
LV_ASSERT_NULL(at);
|
||||
at->repeat_count = cnt;
|
||||
}
|
||||
|
||||
void lv_anim_timeline_set_repeat_delay(lv_anim_timeline_t * at, uint32_t delay)
|
||||
{
|
||||
LV_ASSERT_NULL(at);
|
||||
at->repeat_delay = delay;
|
||||
}
|
||||
|
||||
void lv_anim_timeline_set_progress(lv_anim_timeline_t * at, uint16_t progress)
|
||||
{
|
||||
LV_ASSERT_NULL(at);
|
||||
|
||||
uint32_t playtime = lv_anim_timeline_get_playtime(at);
|
||||
uint32_t act_time = lv_map(progress, 0, LV_ANIM_TIMELINE_PROGRESS_MAX, 0, playtime);
|
||||
anim_timeline_set_act_time(at, act_time);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
uint16_t lv_anim_timeline_get_progress(lv_anim_timeline_t * at)
|
||||
{
|
||||
LV_ASSERT_NULL(at);
|
||||
uint32_t playtime = lv_anim_timeline_get_playtime(at);
|
||||
return lv_map(at->act_time, 0, playtime, 0, LV_ANIM_TIMELINE_PROGRESS_MAX);
|
||||
}
|
||||
|
||||
uint32_t lv_anim_timeline_get_repeat_count(lv_anim_timeline_t * at)
|
||||
{
|
||||
LV_ASSERT_NULL(at);
|
||||
return at->repeat_count;
|
||||
}
|
||||
|
||||
uint32_t lv_anim_timeline_get_repeat_delay(lv_anim_timeline_t * at)
|
||||
{
|
||||
LV_ASSERT_NULL(at);
|
||||
return at->repeat_delay;
|
||||
}
|
||||
|
||||
/**********************
|
||||
* STATIC FUNCTIONS
|
||||
**********************/
|
||||
|
||||
static void anim_timeline_set_act_time(lv_anim_timeline_t * at, uint32_t act_time)
|
||||
{
|
||||
at->act_time = act_time;
|
||||
bool anim_timeline_is_started = (lv_anim_get(at, anim_timeline_exec_cb) != NULL);
|
||||
for(uint32_t i = 0; i < at->anim_dsc_cnt; i++) {
|
||||
lv_anim_timeline_dsc_t * anim_dsc = &(at->anim_dsc[i]);
|
||||
lv_anim_t * a = &(anim_dsc->anim);
|
||||
|
||||
uint32_t start_time = anim_dsc->start_time;
|
||||
int32_t value = 0;
|
||||
|
||||
if(act_time < start_time && a->early_apply) {
|
||||
if(anim_timeline_is_started) {
|
||||
if(at->reverse) {
|
||||
if(!anim_dsc->is_started && a->start_cb) a->start_cb(a);
|
||||
anim_dsc->is_started = 1;
|
||||
}
|
||||
else {
|
||||
anim_dsc->is_started = 0;
|
||||
}
|
||||
}
|
||||
|
||||
value = a->start_value;
|
||||
if(a->exec_cb) a->exec_cb(a->var, value);
|
||||
if(a->custom_exec_cb) a->custom_exec_cb(a, value);
|
||||
|
||||
if(anim_timeline_is_started) {
|
||||
if(at->reverse) {
|
||||
if(!anim_dsc->is_completed && a->completed_cb) a->completed_cb(a);
|
||||
anim_dsc->is_completed = 1;
|
||||
}
|
||||
else {
|
||||
anim_dsc->is_completed = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(act_time >= start_time && act_time <= (start_time + a->duration)) {
|
||||
if(anim_timeline_is_started) {
|
||||
if(!anim_dsc->is_started && a->start_cb) a->start_cb(a);
|
||||
anim_dsc->is_started = 1;
|
||||
}
|
||||
|
||||
a->act_time = act_time - start_time;
|
||||
value = a->path_cb(a);
|
||||
if(a->exec_cb) a->exec_cb(a->var, value);
|
||||
if(a->custom_exec_cb) a->custom_exec_cb(a, value);
|
||||
|
||||
if(anim_timeline_is_started) {
|
||||
if(at->reverse) {
|
||||
if(act_time == start_time) {
|
||||
if(!anim_dsc->is_completed && a->completed_cb) a->completed_cb(a);
|
||||
anim_dsc->is_completed = 1;
|
||||
}
|
||||
else {
|
||||
anim_dsc->is_completed = 0;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if(act_time == (start_time + a->duration)) {
|
||||
if(!anim_dsc->is_completed && a->completed_cb) a->completed_cb(a);
|
||||
anim_dsc->is_completed = 1;
|
||||
}
|
||||
else {
|
||||
anim_dsc->is_completed = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(act_time > start_time + a->duration) {
|
||||
if(anim_timeline_is_started) {
|
||||
if(at->reverse) {
|
||||
anim_dsc->is_started = 0;
|
||||
}
|
||||
else {
|
||||
if(!anim_dsc->is_started && a->start_cb) a->start_cb(a);
|
||||
anim_dsc->is_started = 1;
|
||||
}
|
||||
}
|
||||
|
||||
value = a->end_value;
|
||||
if(a->exec_cb) a->exec_cb(a->var, value);
|
||||
if(a->custom_exec_cb) a->custom_exec_cb(a, value);
|
||||
|
||||
if(anim_timeline_is_started) {
|
||||
if(at->reverse) {
|
||||
anim_dsc->is_completed = 0;
|
||||
}
|
||||
else {
|
||||
if(!anim_dsc->is_completed && a->completed_cb) a->completed_cb(a);
|
||||
anim_dsc->is_completed = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int32_t anim_timeline_path_cb(const lv_anim_t * a)
|
||||
{
|
||||
/* Directly map original timestamps to avoid loss of accuracy */
|
||||
return lv_map(a->act_time, 0, a->duration, a->start_value, a->end_value);
|
||||
}
|
||||
|
||||
static void anim_timeline_exec_cb(void * var, int32_t v)
|
||||
{
|
||||
lv_anim_timeline_t * at = var;
|
||||
anim_timeline_set_act_time(at, v);
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
/**
|
||||
* @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
|
||||
*********************/
|
||||
|
||||
#define LV_ANIM_TIMELINE_PROGRESS_MAX 0xFFFF
|
||||
|
||||
/**********************
|
||||
* TYPEDEFS
|
||||
**********************/
|
||||
|
||||
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_delete(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, const 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);
|
||||
|
||||
/**
|
||||
* Pause the animation timeline.
|
||||
* @param at pointer to the animation timeline.
|
||||
*/
|
||||
void lv_anim_timeline_pause(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);
|
||||
|
||||
/**
|
||||
* Make the animation timeline repeat itself.
|
||||
* @param at pointer to the animation timeline.
|
||||
* @param cnt repeat count or `LV_ANIM_REPEAT_INFINITE` for infinite repetition. 0: to disable repetition.
|
||||
*/
|
||||
void lv_anim_timeline_set_repeat_count(lv_anim_timeline_t * at, uint32_t cnt);
|
||||
|
||||
/**
|
||||
* Set a delay before repeating the animation timeline.
|
||||
* @param at pointer to the animation timeline.
|
||||
* @param delay delay in milliseconds before repeating the animation timeline.
|
||||
*/
|
||||
void lv_anim_timeline_set_repeat_delay(lv_anim_timeline_t * at, uint32_t delay);
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
/**
|
||||
* Get the progress of the animation timeline.
|
||||
* @param at pointer to the animation timeline.
|
||||
* @return return value 0~65535 to map 0~100% animation progress.
|
||||
*/
|
||||
uint16_t lv_anim_timeline_get_progress(lv_anim_timeline_t * at);
|
||||
|
||||
/**
|
||||
* Get repeat count of the animation timeline.
|
||||
* @param at pointer to the animation timeline.
|
||||
*/
|
||||
uint32_t lv_anim_timeline_get_repeat_count(lv_anim_timeline_t * at);
|
||||
|
||||
/**
|
||||
* Get repeat delay of the animation timeline.
|
||||
* @param at pointer to the animation timeline.
|
||||
*/
|
||||
uint32_t lv_anim_timeline_get_repeat_delay(lv_anim_timeline_t * at);
|
||||
|
||||
/**********************
|
||||
* MACROS
|
||||
**********************/
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /*extern "C"*/
|
||||
#endif
|
||||
|
||||
#endif /*LV_ANIM_TIMELINE_H*/
|
||||
@@ -0,0 +1,603 @@
|
||||
/**
|
||||
* @file lv_area.c
|
||||
*
|
||||
*/
|
||||
|
||||
/*********************
|
||||
* INCLUDES
|
||||
*********************/
|
||||
#include "../lv_conf_internal.h"
|
||||
#include "../core/lv_global.h"
|
||||
|
||||
#include "lv_area_private.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
|
||||
**********************/
|
||||
|
||||
void lv_area_set(lv_area_t * area_p, int32_t x1, int32_t y1, int32_t x2, int32_t y2)
|
||||
{
|
||||
area_p->x1 = x1;
|
||||
area_p->y1 = y1;
|
||||
area_p->x2 = x2;
|
||||
area_p->y2 = y2;
|
||||
}
|
||||
|
||||
void lv_area_set_width(lv_area_t * area_p, int32_t w)
|
||||
{
|
||||
area_p->x2 = area_p->x1 + w - 1;
|
||||
}
|
||||
|
||||
void lv_area_set_height(lv_area_t * area_p, int32_t h)
|
||||
{
|
||||
area_p->y2 = area_p->y1 + h - 1;
|
||||
}
|
||||
|
||||
void lv_area_set_pos(lv_area_t * area_p, int32_t x, int32_t y)
|
||||
{
|
||||
int32_t w = lv_area_get_width(area_p);
|
||||
int32_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);
|
||||
}
|
||||
|
||||
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, int32_t w_extra, int32_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, int32_t x_ofs, int32_t y_ofs)
|
||||
{
|
||||
area->x1 += x_ofs;
|
||||
area->x2 += x_ofs;
|
||||
area->y1 += y_ofs;
|
||||
area->y2 += y_ofs;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
int8_t lv_area_diff(lv_area_t res_p[], const lv_area_t * a1_p, const lv_area_t * a2_p)
|
||||
{
|
||||
/*Areas have no common parts*/
|
||||
if(!lv_area_is_on(a1_p, a2_p)) return -1;
|
||||
|
||||
/*No remaining areas after removing common parts*/
|
||||
if(lv_area_is_in(a1_p, a2_p, 0)) return 0;
|
||||
|
||||
/*Result counter*/
|
||||
int8_t res_c = 0;
|
||||
|
||||
/*Get required information*/
|
||||
lv_area_t n;
|
||||
int32_t a1_w = lv_area_get_width(a1_p) - 1;
|
||||
int32_t a1_h = lv_area_get_height(a1_p) - 1;
|
||||
|
||||
/*Compute top rectangle*/
|
||||
int32_t th = a2_p->y1 - a1_p->y1;
|
||||
if(th > 0) {
|
||||
n.x1 = a1_p->x1;
|
||||
n.y1 = a1_p->y1;
|
||||
n.x2 = a1_p->x2;
|
||||
n.y2 = a1_p->y1 + th - 1;
|
||||
res_p[res_c++] = n;
|
||||
}
|
||||
|
||||
/*Compute the bottom rectangle*/
|
||||
int32_t bh = a1_h - (a2_p->y2 - a1_p->y1);
|
||||
if(bh > 0 && a2_p->y2 < a1_p->y2) {
|
||||
n.x1 = a1_p->x1;
|
||||
n.y1 = a2_p->y2 + 1;
|
||||
n.x2 = a1_p->x2;
|
||||
n.y2 = a2_p->y2 + bh;
|
||||
res_p[res_c++] = n;
|
||||
}
|
||||
|
||||
/*Compute side height*/
|
||||
int32_t y1 = a2_p->y1 > a1_p->y1 ? a2_p->y1 : a1_p->y1;
|
||||
int32_t y2 = a2_p->y2 < a1_p->y2 ? a2_p->y2 : a1_p->y2;
|
||||
int32_t sh = y2 - y1;
|
||||
|
||||
/*Compute the left rectangle*/
|
||||
int32_t lw = a2_p->x1 - a1_p->x1;
|
||||
if(lw > 0 && sh >= 0) {
|
||||
n.x1 = a1_p->x1;
|
||||
n.y1 = y1;
|
||||
n.x2 = a1_p->x1 + lw - 1;
|
||||
n.y2 = y1 + sh;
|
||||
res_p[res_c++] = n;
|
||||
}
|
||||
|
||||
/*Compute the right rectangle*/
|
||||
int32_t rw = a1_w - (a2_p->x2 - a1_p->x1);
|
||||
if(rw > 0 && sh >= 0) {
|
||||
n.x1 = a2_p->x2 + 1;
|
||||
n.y1 = y1;
|
||||
n.x2 = a2_p->x2 + rw;
|
||||
n.y2 = y1 + sh;
|
||||
res_p[res_c++] = n;
|
||||
}
|
||||
|
||||
//Return number of results
|
||||
return res_c;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
bool lv_area_is_point_on(const lv_area_t * a_p, const lv_point_t * p_p, int32_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;
|
||||
}
|
||||
int32_t w = lv_area_get_width(a_p) / 2;
|
||||
int32_t h = lv_area_get_height(a_p) / 2;
|
||||
int32_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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
bool lv_area_is_in(const lv_area_t * ain_p, const lv_area_t * aholder_p, int32_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;
|
||||
|
||||
lv_point_set(&p, ain_p->x1, ain_p->y1);
|
||||
if(lv_area_is_point_on(aholder_p, &p, radius) == false) return false;
|
||||
|
||||
lv_point_set(&p, ain_p->x2, ain_p->y1);
|
||||
if(lv_area_is_point_on(aholder_p, &p, radius) == false) return false;
|
||||
|
||||
lv_point_set(&p, ain_p->x1, ain_p->y2);
|
||||
if(lv_area_is_point_on(aholder_p, &p, radius) == false) return false;
|
||||
|
||||
lv_point_set(&p, ain_p->x2, ain_p->y2);
|
||||
if(lv_area_is_point_on(aholder_p, &p, radius) == false) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool lv_area_is_out(const lv_area_t * aout_p, const lv_area_t * aholder_p, int32_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;
|
||||
|
||||
lv_point_set(&p, aout_p->x1, aout_p->y1);
|
||||
if(lv_area_is_point_on(aholder_p, &p, radius)) return false;
|
||||
|
||||
lv_point_set(&p, aout_p->x2, aout_p->y1);
|
||||
if(lv_area_is_point_on(aholder_p, &p, radius)) return false;
|
||||
|
||||
lv_point_set(&p, aout_p->x1, aout_p->y2);
|
||||
if(lv_area_is_point_on(aholder_p, &p, radius)) return false;
|
||||
|
||||
lv_point_set(&p, aout_p->x2, 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;
|
||||
}
|
||||
|
||||
void lv_area_align(const lv_area_t * base, lv_area_t * to_align, lv_align_t align, int32_t ofs_x, int32_t ofs_y)
|
||||
{
|
||||
|
||||
int32_t x;
|
||||
int32_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;
|
||||
|
||||
int32_t w = lv_area_get_width(to_align);
|
||||
int32_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 * point, int32_t angle, int32_t scale_x, int32_t scale_y, const lv_point_t * pivot,
|
||||
bool zoom_first)
|
||||
{
|
||||
lv_point_array_transform(point, 1, angle, scale_x, scale_y, pivot, zoom_first);
|
||||
}
|
||||
|
||||
void lv_point_array_transform(lv_point_t * points, size_t count, int32_t angle, int32_t scale_x, int32_t scale_y,
|
||||
const lv_point_t * pivot,
|
||||
bool zoom_first)
|
||||
{
|
||||
if(angle == 0 && scale_x == 256 && scale_y == 256) {
|
||||
return;
|
||||
}
|
||||
uint32_t i;
|
||||
for(i = 0; i < count; i++) {
|
||||
points[i].x -= pivot->x;
|
||||
points[i].y -= pivot->y;
|
||||
|
||||
}
|
||||
|
||||
if(angle == 0) {
|
||||
for(i = 0; i < count; i++) {
|
||||
points[i].x = (((int32_t)(points[i].x) * scale_x) >> 8) + pivot->x;
|
||||
points[i].y = (((int32_t)(points[i].y) * scale_y) >> 8) + pivot->y;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
int32_t sinma = (s1 * (10 - angle_rem) + s2 * angle_rem) / 10;
|
||||
sinma = sinma >> (LV_TRIGO_SHIFT - LV_TRANSFORM_TRIGO_SHIFT);
|
||||
int32_t cosma = (c1 * (10 - angle_rem) + c2 * angle_rem) / 10;
|
||||
cosma = cosma >> (LV_TRIGO_SHIFT - LV_TRANSFORM_TRIGO_SHIFT);
|
||||
|
||||
for(i = 0; i < count; i++) {
|
||||
int32_t x = points[i].x;
|
||||
int32_t y = points[i].y;
|
||||
if(scale_x == 256 && scale_y == 256) {
|
||||
points[i].x = ((cosma * x - sinma * y) >> LV_TRANSFORM_TRIGO_SHIFT) + pivot->x;
|
||||
points[i].y = ((sinma * x + cosma * y) >> LV_TRANSFORM_TRIGO_SHIFT) + pivot->y;
|
||||
}
|
||||
else {
|
||||
if(zoom_first) {
|
||||
x *= scale_x;
|
||||
y *= scale_y;
|
||||
points[i].x = (((cosma * x - sinma * y)) >> (LV_TRANSFORM_TRIGO_SHIFT + 8)) + pivot->x;
|
||||
points[i].y = (((sinma * x + cosma * y)) >> (LV_TRANSFORM_TRIGO_SHIFT + 8)) + pivot->y;
|
||||
}
|
||||
else {
|
||||
points[i].x = (((cosma * x - sinma * y) * scale_x) >> (LV_TRANSFORM_TRIGO_SHIFT + 8)) + pivot->x;
|
||||
points[i].y = (((sinma * x + cosma * y) * scale_y) >> (LV_TRANSFORM_TRIGO_SHIFT + 8)) + pivot->y;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int32_t lv_area_get_width(const lv_area_t * area_p)
|
||||
{
|
||||
return (int32_t)(area_p->x2 - area_p->x1 + 1);
|
||||
}
|
||||
|
||||
int32_t lv_area_get_height(const lv_area_t * area_p)
|
||||
{
|
||||
return (int32_t)(area_p->y2 - area_p->y1 + 1);
|
||||
}
|
||||
|
||||
lv_point_t lv_point_from_precise(const lv_point_precise_t * p)
|
||||
{
|
||||
lv_point_t point = {
|
||||
(int32_t)p->x, (int32_t)p->y
|
||||
};
|
||||
|
||||
return point;
|
||||
}
|
||||
|
||||
lv_point_precise_t lv_point_to_precise(const lv_point_t * p)
|
||||
{
|
||||
lv_point_precise_t point = {
|
||||
(lv_value_precise_t)p->x, (lv_value_precise_t)p->y
|
||||
};
|
||||
|
||||
return point;
|
||||
}
|
||||
|
||||
void lv_point_set(lv_point_t * p, int32_t x, int32_t y)
|
||||
{
|
||||
p->x = x;
|
||||
p->y = y;
|
||||
}
|
||||
|
||||
void lv_point_precise_set(lv_point_precise_t * p, lv_value_precise_t x, lv_value_precise_t y)
|
||||
{
|
||||
p->x = x;
|
||||
p->y = y;
|
||||
}
|
||||
|
||||
void lv_point_swap(lv_point_t * p1, lv_point_t * p2)
|
||||
{
|
||||
lv_point_t tmp = *p1;
|
||||
*p1 = *p2;
|
||||
*p2 = tmp;
|
||||
}
|
||||
|
||||
void lv_point_precise_swap(lv_point_precise_t * p1, lv_point_precise_t * p2)
|
||||
{
|
||||
lv_point_precise_t tmp = *p1;
|
||||
*p1 = *p2;
|
||||
*p2 = tmp;
|
||||
}
|
||||
|
||||
int32_t lv_pct(int32_t x)
|
||||
{
|
||||
return LV_PCT(x);
|
||||
}
|
||||
|
||||
int32_t lv_pct_to_px(int32_t v, int32_t base)
|
||||
{
|
||||
if(LV_COORD_IS_PCT(v)) {
|
||||
return (LV_COORD_GET_PCT(v) * base) / 100;
|
||||
}
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
/**********************
|
||||
* STATIC FUNCTIONS
|
||||
**********************/
|
||||
|
||||
static bool lv_point_within_circle(const lv_area_t * area, const lv_point_t * p)
|
||||
{
|
||||
int32_t r = (area->x2 - area->x1) / 2;
|
||||
|
||||
/*Circle center*/
|
||||
int32_t cx = area->x1 + r;
|
||||
int32_t cy = area->y1 + r;
|
||||
|
||||
/*Simplify the code by moving everything to (0, 0)*/
|
||||
int32_t px = p->x - cx;
|
||||
int32_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;
|
||||
}
|
||||
@@ -0,0 +1,254 @@
|
||||
/**
|
||||
* @file lv_area.h
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef LV_AREA_H
|
||||
#define LV_AREA_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*********************
|
||||
* INCLUDES
|
||||
*********************/
|
||||
#include "../lv_conf_internal.h"
|
||||
#include "lv_types.h"
|
||||
#include "lv_math.h"
|
||||
|
||||
/*********************
|
||||
* DEFINES
|
||||
*********************/
|
||||
|
||||
/**********************
|
||||
* TYPEDEFS
|
||||
**********************/
|
||||
|
||||
/**
|
||||
* Represents a point on the screen.
|
||||
*/
|
||||
typedef struct {
|
||||
int32_t x;
|
||||
int32_t y;
|
||||
} lv_point_t;
|
||||
|
||||
typedef struct {
|
||||
lv_value_precise_t x;
|
||||
lv_value_precise_t y;
|
||||
} lv_point_precise_t;
|
||||
|
||||
/** Represents an area of the screen.*/
|
||||
typedef struct {
|
||||
int32_t x1;
|
||||
int32_t y1;
|
||||
int32_t x2;
|
||||
int32_t y2;
|
||||
} lv_area_t;
|
||||
|
||||
/** Alignments*/
|
||||
|
||||
typedef 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,
|
||||
} lv_align_t;
|
||||
|
||||
typedef 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,
|
||||
} 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, int32_t x1, int32_t y1, int32_t x2, int32_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)
|
||||
*/
|
||||
int32_t lv_area_get_width(const lv_area_t * area_p);
|
||||
|
||||
/**
|
||||
* Get the height of an area
|
||||
* @param area_p pointer to an area
|
||||
* @return the height of the area (if y1 == y2 -> height = 1)
|
||||
*/
|
||||
int32_t lv_area_get_height(const lv_area_t * area_p);
|
||||
|
||||
/**
|
||||
* 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, int32_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, int32_t 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);
|
||||
|
||||
void lv_area_increase(lv_area_t * area, int32_t w_extra, int32_t h_extra);
|
||||
|
||||
void lv_area_move(lv_area_t * area, int32_t x_ofs, int32_t y_ofs);
|
||||
|
||||
/**
|
||||
* Align an area to another
|
||||
* @param base an area where the other will be aligned
|
||||
* @param to_align the area to align
|
||||
* @param align `LV_ALIGN_...`
|
||||
* @param ofs_x X offset
|
||||
* @param ofs_y Y offset
|
||||
*/
|
||||
void lv_area_align(const lv_area_t * base, lv_area_t * to_align, lv_align_t align, int32_t ofs_x, int32_t ofs_y);
|
||||
|
||||
/**
|
||||
* Transform a point
|
||||
* @param point pointer to a point
|
||||
* @param angle angle with 0.1 resolutions (123 means 12.3°)
|
||||
* @param scale_x horizontal zoom, 256 means 100%
|
||||
* @param scale_y vertical zoom, 256 means 100%
|
||||
* @param pivot pointer to the pivot point of the transformation
|
||||
* @param zoom_first true: zoom first and rotate after that; else: opposite order
|
||||
*/
|
||||
void lv_point_transform(lv_point_t * point, int32_t angle, int32_t scale_x, int32_t scale_y, const lv_point_t * pivot,
|
||||
bool zoom_first);
|
||||
|
||||
/**
|
||||
* Transform an array of points
|
||||
* @param points pointer to an array of points
|
||||
* @param count number of points in the array
|
||||
* @param angle angle with 0.1 resolutions (123 means 12.3°)
|
||||
* @param scale_x horizontal zoom, 256 means 100%
|
||||
* @param scale_y vertical zoom, 256 means 100%
|
||||
* @param pivot pointer to the pivot point of the transformation
|
||||
* @param zoom_first true: zoom first and rotate after that; else: opposite order
|
||||
*/
|
||||
void lv_point_array_transform(lv_point_t * points, size_t count, int32_t angle, int32_t scale_x, int32_t scale_y,
|
||||
const lv_point_t * pivot,
|
||||
bool zoom_first);
|
||||
|
||||
lv_point_t lv_point_from_precise(const lv_point_precise_t * p);
|
||||
|
||||
lv_point_precise_t lv_point_to_precise(const lv_point_t * p);
|
||||
|
||||
void lv_point_set(lv_point_t * p, int32_t x, int32_t y);
|
||||
|
||||
void lv_point_precise_set(lv_point_precise_t * p, lv_value_precise_t x, lv_value_precise_t y);
|
||||
|
||||
void lv_point_swap(lv_point_t * p1, lv_point_t * p2);
|
||||
|
||||
void lv_point_precise_swap(lv_point_precise_t * p1, lv_point_precise_t * p2);
|
||||
|
||||
/**********************
|
||||
* MACROS
|
||||
**********************/
|
||||
|
||||
#define LV_COORD_TYPE_SHIFT (29U)
|
||||
|
||||
#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)
|
||||
#define LV_COORD_IS_SPEC(x) (LV_COORD_TYPE(x) == LV_COORD_TYPE_SPEC)
|
||||
|
||||
#define LV_COORD_SET_SPEC(x) ((x) | LV_COORD_TYPE_SPEC)
|
||||
|
||||
/** Max coordinate value */
|
||||
#define LV_COORD_MAX ((1 << LV_COORD_TYPE_SHIFT) - 1)
|
||||
#define LV_COORD_MIN (-LV_COORD_MAX)
|
||||
|
||||
/*Special coordinates*/
|
||||
#define LV_SIZE_CONTENT LV_COORD_SET_SPEC(LV_COORD_MAX)
|
||||
#define LV_PCT_STORED_MAX (LV_COORD_MAX - 1)
|
||||
#if LV_PCT_STORED_MAX % 2 != 0
|
||||
#error LV_PCT_STORED_MAX should be an even number
|
||||
#endif
|
||||
#define LV_PCT_POS_MAX (LV_PCT_STORED_MAX / 2)
|
||||
#define LV_PCT(x) (LV_COORD_SET_SPEC(((x) < 0 ? (LV_PCT_POS_MAX - LV_MAX((x), -LV_PCT_POS_MAX)) : LV_MIN((x), LV_PCT_POS_MAX))))
|
||||
#define LV_COORD_IS_PCT(x) ((LV_COORD_IS_SPEC(x) && LV_COORD_PLAIN(x) <= LV_PCT_STORED_MAX))
|
||||
#define LV_COORD_GET_PCT(x) (LV_COORD_PLAIN(x) > LV_PCT_POS_MAX ? LV_PCT_POS_MAX - LV_COORD_PLAIN(x) : LV_COORD_PLAIN(x))
|
||||
|
||||
LV_EXPORT_CONST_INT(LV_COORD_MAX);
|
||||
LV_EXPORT_CONST_INT(LV_COORD_MIN);
|
||||
LV_EXPORT_CONST_INT(LV_SIZE_CONTENT);
|
||||
|
||||
/**
|
||||
* Convert a percentage value to `int32_t`.
|
||||
* Percentage values are stored in special range
|
||||
* @param x the percentage (0..1000)
|
||||
* @return a coordinate that stores the percentage
|
||||
*/
|
||||
int32_t lv_pct(int32_t x);
|
||||
|
||||
int32_t lv_pct_to_px(int32_t v, int32_t base);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /*extern "C"*/
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,115 @@
|
||||
/**
|
||||
* @file lv_area_private.h
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef LV_AREA_PRIVATE_H
|
||||
#define LV_AREA_PRIVATE_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*********************
|
||||
* INCLUDES
|
||||
*********************/
|
||||
|
||||
#include "lv_area.h"
|
||||
|
||||
/*********************
|
||||
* DEFINES
|
||||
*********************/
|
||||
|
||||
/**********************
|
||||
* TYPEDEFS
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* GLOBAL PROTOTYPES
|
||||
**********************/
|
||||
|
||||
/**
|
||||
* 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, int32_t x, int32_t y);
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
/**
|
||||
* Get resulting sub areas after removing the common parts of two areas from the first area
|
||||
* @param res_p pointer to an array of areas with a count of 4, the resulting areas will be stored here
|
||||
* @param a1_p pointer to the first area
|
||||
* @param a2_p pointer to the second area
|
||||
* @return number of results (max 4) or -1 if no intersect
|
||||
*/
|
||||
int8_t lv_area_diff(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 a_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, int32_t radius);
|
||||
|
||||
/**
|
||||
* Check if two area has common parts
|
||||
* @param a1_p pointer to an area.
|
||||
* @param a2_p pointer to another 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 another
|
||||
* @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, int32_t radius);
|
||||
|
||||
/**
|
||||
* Check if an area is fully out of another
|
||||
* @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, int32_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);
|
||||
|
||||
/**********************
|
||||
* MACROS
|
||||
**********************/
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /*extern "C"*/
|
||||
#endif
|
||||
|
||||
#endif /*LV_AREA_PRIVATE_H*/
|
||||
@@ -0,0 +1,217 @@
|
||||
/**
|
||||
* @file lv_array.c
|
||||
* Array.
|
||||
* The nodes are dynamically allocated by the 'lv_mem' module,
|
||||
*/
|
||||
|
||||
/*********************
|
||||
* INCLUDES
|
||||
*********************/
|
||||
#include "lv_array.h"
|
||||
#include "../stdlib/lv_mem.h"
|
||||
#include "../stdlib/lv_string.h"
|
||||
|
||||
#include "lv_assert.h"
|
||||
/*********************
|
||||
* DEFINES
|
||||
*********************/
|
||||
|
||||
/**********************
|
||||
* TYPEDEFS
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* STATIC PROTOTYPES
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* STATIC VARIABLES
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* MACROS
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* GLOBAL FUNCTIONS
|
||||
**********************/
|
||||
void lv_array_init(lv_array_t * array, uint32_t capacity, uint32_t element_size)
|
||||
{
|
||||
array->size = 0;
|
||||
array->capacity = capacity;
|
||||
array->element_size = element_size;
|
||||
|
||||
array->data = lv_malloc(capacity * element_size);
|
||||
array->inner_alloc = true;
|
||||
LV_ASSERT_MALLOC(array->data);
|
||||
}
|
||||
|
||||
void lv_array_init_from_buf(lv_array_t * array, void * buf, uint32_t capacity, uint32_t element_size)
|
||||
{
|
||||
LV_ASSERT_NULL(buf);
|
||||
array->size = 0;
|
||||
array->capacity = capacity;
|
||||
array->element_size = element_size;
|
||||
|
||||
array->data = buf;
|
||||
array->inner_alloc = false;
|
||||
}
|
||||
|
||||
void lv_array_deinit(lv_array_t * array)
|
||||
{
|
||||
if(array->data) {
|
||||
if(array->inner_alloc) lv_free(array->data);
|
||||
array->data = NULL;
|
||||
}
|
||||
|
||||
array->size = 0;
|
||||
array->capacity = 0;
|
||||
}
|
||||
|
||||
void lv_array_copy(lv_array_t * target, const lv_array_t * source)
|
||||
{
|
||||
if(lv_array_is_empty(source)) {
|
||||
return;
|
||||
}
|
||||
lv_array_deinit(target);
|
||||
lv_array_init(target, source->capacity, source->element_size);
|
||||
lv_memcpy(target->data, source->data, source->size * source->element_size);
|
||||
target->size = source->size;
|
||||
}
|
||||
|
||||
void lv_array_shrink(lv_array_t * array)
|
||||
{
|
||||
if(array->size <= array->capacity / LV_ARRAY_DEFAULT_SHRINK_RATIO) {
|
||||
lv_array_resize(array, array->size);
|
||||
}
|
||||
}
|
||||
|
||||
lv_result_t lv_array_remove(lv_array_t * array, uint32_t index)
|
||||
{
|
||||
if(index >= array->size) {
|
||||
return LV_RESULT_INVALID;
|
||||
}
|
||||
|
||||
/*Shortcut*/
|
||||
if(index == array->size - 1) {
|
||||
array->size--;
|
||||
lv_array_shrink(array);
|
||||
return LV_RESULT_OK;
|
||||
}
|
||||
|
||||
uint8_t * start = lv_array_at(array, index);
|
||||
uint8_t * remaining = start + array->element_size;
|
||||
uint32_t remaining_size = (array->size - index - 1) * array->element_size;
|
||||
lv_memmove(start, remaining, remaining_size);
|
||||
array->size--;
|
||||
lv_array_shrink(array);
|
||||
return LV_RESULT_OK;
|
||||
}
|
||||
|
||||
lv_result_t lv_array_erase(lv_array_t * array, uint32_t start, uint32_t end)
|
||||
{
|
||||
if(end > array->size) {
|
||||
end = array->size;
|
||||
}
|
||||
|
||||
if(start >= end) {
|
||||
return LV_RESULT_INVALID;
|
||||
}
|
||||
|
||||
/*Shortcut*/
|
||||
if(end == array->size) {
|
||||
array->size = start;
|
||||
lv_array_shrink(array);
|
||||
return LV_RESULT_OK;
|
||||
}
|
||||
|
||||
uint8_t * start_p = lv_array_at(array, start);
|
||||
uint8_t * remaining = start_p + (end - start) * array->element_size;
|
||||
uint32_t remaining_size = (array->size - end) * array->element_size;
|
||||
lv_memcpy(start_p, remaining, remaining_size);
|
||||
array->size -= (end - start);
|
||||
lv_array_shrink(array);
|
||||
return LV_RESULT_OK;
|
||||
}
|
||||
|
||||
bool lv_array_resize(lv_array_t * array, uint32_t new_capacity)
|
||||
{
|
||||
if(array->inner_alloc == false) {
|
||||
LV_LOG_WARN("Cannot resize array with external buffer");
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t * data = lv_realloc(array->data, new_capacity * array->element_size);
|
||||
LV_ASSERT_NULL(data);
|
||||
|
||||
if(data == NULL) return false;
|
||||
|
||||
array->data = data;
|
||||
array->capacity = new_capacity;
|
||||
if(array->size > new_capacity) {
|
||||
array->size = new_capacity;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
lv_result_t lv_array_concat(lv_array_t * array, const lv_array_t * other)
|
||||
{
|
||||
LV_ASSERT_NULL(array->data);
|
||||
uint32_t size = other->size;
|
||||
if(array->size + size > array->capacity) {
|
||||
/*array is full*/
|
||||
if(lv_array_resize(array, array->size + size) == false) {
|
||||
return LV_RESULT_INVALID;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t * data = array->data + array->size * array->element_size;
|
||||
lv_memcpy(data, other->data, array->element_size * size);
|
||||
array->size += size;
|
||||
return LV_RESULT_OK;
|
||||
}
|
||||
|
||||
lv_result_t lv_array_push_back(lv_array_t * array, const void * element)
|
||||
{
|
||||
LV_ASSERT_NULL(array->data);
|
||||
|
||||
if(array->size == array->capacity) {
|
||||
/*array is full*/
|
||||
if(lv_array_resize(array, array->capacity + LV_ARRAY_DEFAULT_CAPACITY) == false) {
|
||||
return LV_RESULT_INVALID;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* When the element is NULL, it means that the user wants to add an empty element.
|
||||
*/
|
||||
uint8_t * data = array->data + array->size * array->element_size;
|
||||
if(element) lv_memcpy(data, element, array->element_size);
|
||||
else lv_memzero(data, array->element_size);
|
||||
|
||||
array->size++;
|
||||
return LV_RESULT_OK;
|
||||
}
|
||||
|
||||
void * lv_array_at(const lv_array_t * array, uint32_t index)
|
||||
{
|
||||
if(index >= array->size) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
LV_ASSERT_NULL(array->data);
|
||||
return array->data + index * array->element_size;
|
||||
}
|
||||
|
||||
lv_result_t lv_array_assign(lv_array_t * array, uint32_t index, const void * value)
|
||||
{
|
||||
uint8_t * data = lv_array_at(array, index);
|
||||
if(data == NULL) return LV_RESULT_INVALID;
|
||||
|
||||
lv_memcpy(data, value, array->element_size);
|
||||
return LV_RESULT_OK;
|
||||
}
|
||||
|
||||
/**********************
|
||||
* STATIC FUNCTIONS
|
||||
**********************/
|
||||
@@ -0,0 +1,226 @@
|
||||
/**
|
||||
* @file lv_array.h
|
||||
* Array. The elements are dynamically allocated by the 'lv_mem' module.
|
||||
*/
|
||||
|
||||
#ifndef LV_ARRAY_H
|
||||
#define LV_ARRAY_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*********************
|
||||
* INCLUDES
|
||||
*********************/
|
||||
#include "lv_types.h"
|
||||
|
||||
/*********************
|
||||
* DEFINES
|
||||
*********************/
|
||||
|
||||
#ifndef LV_ARRAY_DEFAULT_CAPACITY
|
||||
#define LV_ARRAY_DEFAULT_CAPACITY 4
|
||||
#endif
|
||||
|
||||
#ifndef LV_ARRAY_DEFAULT_SHRINK_RATIO
|
||||
#define LV_ARRAY_DEFAULT_SHRINK_RATIO 2
|
||||
#endif
|
||||
|
||||
/**********************
|
||||
* TYPEDEFS
|
||||
**********************/
|
||||
|
||||
/** Description of a array*/
|
||||
struct _lv_array_t {
|
||||
uint8_t * data;
|
||||
uint32_t size;
|
||||
uint32_t capacity;
|
||||
uint32_t element_size;
|
||||
|
||||
bool inner_alloc; /* true: data is allocated by the array; false: data is allocated by the user */
|
||||
};
|
||||
|
||||
/**********************
|
||||
* GLOBAL PROTOTYPES
|
||||
**********************/
|
||||
|
||||
/**
|
||||
* Init an array.
|
||||
* @param array pointer to an `lv_array_t` variable to initialize
|
||||
* @param capacity the initial capacity of the array
|
||||
* @param element_size the size of an element in bytes
|
||||
*/
|
||||
void lv_array_init(lv_array_t * array, uint32_t capacity, uint32_t element_size);
|
||||
|
||||
/**
|
||||
* Init an array from a buffer.
|
||||
* @note The buffer must be large enough to store `capacity` elements. The array will not release the buffer and reallocate it.
|
||||
* The user must ensure that the buffer is valid during the lifetime of the array. And release the buffer when the array is no longer needed.
|
||||
* @param array pointer to an `lv_array_t` variable to initialize
|
||||
* @param buf pointer to a buffer to use as the array's data
|
||||
* @param capacity the initial capacity of the array
|
||||
* @param element_size the size of an element in bytes
|
||||
*/
|
||||
void lv_array_init_from_buf(lv_array_t * array, void * buf, uint32_t capacity, uint32_t element_size);
|
||||
|
||||
/**
|
||||
* Resize the array to the given capacity.
|
||||
* @note if the new capacity is smaller than the current size, the array will be truncated.
|
||||
* @param array pointer to an `lv_array_t` variable
|
||||
* @param new_capacity the new capacity of the array
|
||||
*/
|
||||
bool lv_array_resize(lv_array_t * array, uint32_t new_capacity);
|
||||
|
||||
/**
|
||||
* Deinit the array, and free the allocated memory
|
||||
* @param array pointer to an `lv_array_t` variable to deinitialize
|
||||
*/
|
||||
void lv_array_deinit(lv_array_t * array);
|
||||
|
||||
/**
|
||||
* Return how many elements are stored in the array.
|
||||
* @param array pointer to an `lv_array_t` variable
|
||||
* @return the number of elements stored in the array
|
||||
*/
|
||||
static inline uint32_t lv_array_size(const lv_array_t * array)
|
||||
{
|
||||
return array->size;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the capacity of the array, i.e. how many elements can be stored.
|
||||
* @param array pointer to an `lv_array_t` variable
|
||||
* @return the capacity of the array
|
||||
*/
|
||||
static inline uint32_t lv_array_capacity(const lv_array_t * array)
|
||||
{
|
||||
return array->capacity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return if the array is empty
|
||||
* @param array pointer to an `lv_array_t` variable
|
||||
* @return true: array is empty; false: array is not empty
|
||||
*/
|
||||
static inline bool lv_array_is_empty(const lv_array_t * array)
|
||||
{
|
||||
return array->size == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return if the array is full
|
||||
* @param array pointer to an `lv_array_t` variable
|
||||
* @return true: array is full; false: array is not full
|
||||
*/
|
||||
static inline bool lv_array_is_full(const lv_array_t * array)
|
||||
{
|
||||
return array->size == array->capacity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy an array to another.
|
||||
* @note this will create a new array with the same capacity and size as the source array.
|
||||
* @param target pointer to an `lv_array_t` variable to copy to
|
||||
* @param source pointer to an `lv_array_t` variable to copy from
|
||||
*/
|
||||
void lv_array_copy(lv_array_t * target, const lv_array_t * source);
|
||||
|
||||
/**
|
||||
* Remove all elements in array.
|
||||
* @param array pointer to an `lv_array_t` variable
|
||||
*/
|
||||
static inline void lv_array_clear(lv_array_t * array)
|
||||
{
|
||||
array->size = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Shrink the memory capacity of array if necessary.
|
||||
* @param array pointer to an `lv_array_t` variable
|
||||
*/
|
||||
void lv_array_shrink(lv_array_t * array);
|
||||
|
||||
/**
|
||||
* Remove the element at the specified position in the array.
|
||||
* @param array pointer to an `lv_array_t` variable
|
||||
* @param index the index of the element to remove
|
||||
* @return LV_RESULT_OK: success, otherwise: error
|
||||
*/
|
||||
lv_result_t lv_array_remove(lv_array_t * array, uint32_t index);
|
||||
|
||||
/**
|
||||
* Remove from the array either a single element or a range of elements ([start, end)).
|
||||
* @note This effectively reduces the container size by the number of elements removed.
|
||||
* @note When start equals to end, the function has no effect.
|
||||
* @param array pointer to an `lv_array_t` variable
|
||||
* @param start the index of the first element to be removed
|
||||
* @param end the index of the first element that is not to be removed
|
||||
* @return LV_RESULT_OK: success, otherwise: error
|
||||
*/
|
||||
lv_result_t lv_array_erase(lv_array_t * array, uint32_t start, uint32_t end);
|
||||
|
||||
/**
|
||||
* Concatenate two arrays. Adds new elements to the end of the array.
|
||||
* @note The destination array is automatically expanded as necessary.
|
||||
* @param array pointer to an `lv_array_t` variable
|
||||
* @param other pointer to the array to concatenate
|
||||
* @return LV_RESULT_OK: success, otherwise: error
|
||||
*/
|
||||
lv_result_t lv_array_concat(lv_array_t * array, const lv_array_t * other);
|
||||
|
||||
/**
|
||||
* Push back element. Adds a new element to the end of the array.
|
||||
* If the array capacity is not enough for the new element, the array will be resized automatically.
|
||||
* @note If the element is NULL, it will be added as an empty element.
|
||||
* @param array pointer to an `lv_array_t` variable
|
||||
* @param element pointer to the element to add. NULL to push an empty element.
|
||||
* @return LV_RESULT_OK: success, otherwise: error
|
||||
*/
|
||||
lv_result_t lv_array_push_back(lv_array_t * array, const void * element);
|
||||
|
||||
/**
|
||||
* Assigns one content to the array, replacing its current content.
|
||||
* @param array pointer to an `lv_array_t` variable
|
||||
* @param index the index of the element to replace
|
||||
* @param value pointer to the elements to add
|
||||
* @return true: success; false: error
|
||||
*/
|
||||
lv_result_t lv_array_assign(lv_array_t * array, uint32_t index, const void * value);
|
||||
|
||||
/**
|
||||
* Returns a pointer to the element at position n in the array.
|
||||
* @param array pointer to an `lv_array_t` variable
|
||||
* @param index the index of the element to return
|
||||
* @return a pointer to the requested element, NULL if `index` is out of range
|
||||
*/
|
||||
void * lv_array_at(const lv_array_t * array, uint32_t index);
|
||||
|
||||
/**
|
||||
* Returns a pointer to the first element in the array.
|
||||
* @param array pointer to an `lv_array_t` variable
|
||||
* @return a pointer to the first element in the array
|
||||
*/
|
||||
static inline void * lv_array_front(const lv_array_t * array)
|
||||
{
|
||||
return lv_array_at(array, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a pointer to the last element in the array.
|
||||
* @param array pointer to an `lv_array_t` variable
|
||||
*/
|
||||
static inline void * lv_array_back(const lv_array_t * array)
|
||||
{
|
||||
return lv_array_at(array, lv_array_size(array) - 1);
|
||||
}
|
||||
|
||||
/**********************
|
||||
* MACROS
|
||||
**********************/
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /*extern "C"*/
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,87 @@
|
||||
/**
|
||||
* @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 "../stdlib/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)
|
||||
|
||||
#define LV_ASSERT_FORMAT_MSG(expr, format, ...) \
|
||||
do { \
|
||||
if(!(expr)) { \
|
||||
LV_LOG_ERROR("Asserted at expression: %s " format , #expr, __VA_ARGS__); \
|
||||
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_RESULT_OK, "Memory integrity error");
|
||||
#else
|
||||
# define LV_ASSERT_MEM_INTEGRITY()
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /*extern "C"*/
|
||||
#endif
|
||||
|
||||
#endif /*LV_ASSERT_H*/
|
||||
@@ -0,0 +1,108 @@
|
||||
/**
|
||||
* @file lv_async.c
|
||||
*
|
||||
*/
|
||||
|
||||
/*********************
|
||||
* INCLUDES
|
||||
*********************/
|
||||
|
||||
#include "lv_async.h"
|
||||
#include "lv_timer_private.h"
|
||||
#include "../stdlib/lv_mem.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_result_t lv_async_call(lv_async_cb_t async_xcb, void * user_data)
|
||||
{
|
||||
/*Allocate an info structure*/
|
||||
lv_async_info_t * info = lv_malloc(sizeof(lv_async_info_t));
|
||||
|
||||
if(info == NULL)
|
||||
return LV_RESULT_INVALID;
|
||||
|
||||
/*Create a new timer*/
|
||||
lv_timer_t * timer = lv_timer_create(lv_async_timer_cb, 0, info);
|
||||
|
||||
if(timer == NULL) {
|
||||
lv_free(info);
|
||||
return LV_RESULT_INVALID;
|
||||
}
|
||||
|
||||
info->cb = async_xcb;
|
||||
info->user_data = user_data;
|
||||
|
||||
lv_timer_set_repeat_count(timer, 1);
|
||||
return LV_RESULT_OK;
|
||||
}
|
||||
|
||||
lv_result_t lv_async_call_cancel(lv_async_cb_t async_xcb, void * user_data)
|
||||
{
|
||||
lv_timer_t * timer = lv_timer_get_next(NULL);
|
||||
lv_result_t res = LV_RESULT_INVALID;
|
||||
|
||||
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_delete(timer);
|
||||
lv_free(info);
|
||||
res = LV_RESULT_OK;
|
||||
}
|
||||
}
|
||||
|
||||
timer = timer_next;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
/**********************
|
||||
* STATIC FUNCTIONS
|
||||
**********************/
|
||||
|
||||
static void lv_async_timer_cb(lv_timer_t * timer)
|
||||
{
|
||||
/*Save the info because an lv_async_call_cancel might delete it in the callback*/
|
||||
lv_async_info_t * info = (lv_async_info_t *)timer->user_data;
|
||||
lv_async_info_t info_save = *info;
|
||||
lv_timer_delete(timer);
|
||||
lv_free(info);
|
||||
|
||||
info_save.cb(info_save.user_data);
|
||||
}
|
||||
@@ -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_result_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_result_t lv_async_call_cancel(lv_async_cb_t async_xcb, void * user_data);
|
||||
|
||||
/**********************
|
||||
* MACROS
|
||||
**********************/
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /*extern "C"*/
|
||||
#endif
|
||||
|
||||
#endif /*LV_ASYNC_H*/
|
||||
@@ -0,0 +1,663 @@
|
||||
/**
|
||||
* @file lv_bidi.c
|
||||
*
|
||||
*/
|
||||
|
||||
/*********************
|
||||
* INCLUDES
|
||||
*********************/
|
||||
#include "lv_bidi_private.h"
|
||||
#include "lv_text_private.h"
|
||||
#include "lv_types.h"
|
||||
#include "../stdlib/lv_mem.h"
|
||||
#include "../stdlib/lv_string.h"
|
||||
|
||||
#if LV_USE_BIDI
|
||||
|
||||
/*********************
|
||||
* DEFINES
|
||||
*********************/
|
||||
#define LV_BIDI_BRACKET_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 bracket_pos;
|
||||
lv_base_dir_t dir;
|
||||
} bracket_stack_t;
|
||||
|
||||
typedef struct {
|
||||
bracket_stack_t br_stack[LV_BIDI_BRACKET_DEPTH];
|
||||
uint8_t br_stack_p;
|
||||
} lv_bidi_ctx_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(lv_bidi_ctx_t * ctx, 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(lv_bidi_ctx_t * ctx, 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 const char * custom_neutrals = NULL;
|
||||
|
||||
/**********************
|
||||
* MACROS
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* GLOBAL FUNCTIONS
|
||||
**********************/
|
||||
|
||||
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_text_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;
|
||||
}
|
||||
|
||||
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_malloc(len + 1);
|
||||
if(buf == NULL) return (uint16_t) -1;
|
||||
|
||||
uint16_t * pos_conv_buf = lv_malloc(pos_conv_len * sizeof(uint16_t));
|
||||
if(pos_conv_buf == NULL) {
|
||||
lv_free(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_free(buf);
|
||||
uint16_t res = GET_POS(pos_conv_buf[visual_pos]);
|
||||
lv_free(pos_conv_buf);
|
||||
return res;
|
||||
}
|
||||
|
||||
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_malloc(len + 1);
|
||||
if(buf == NULL) return (uint16_t) -1;
|
||||
|
||||
uint16_t * pos_conv_buf = lv_malloc(pos_conv_len * sizeof(uint16_t));
|
||||
if(pos_conv_buf == NULL) {
|
||||
lv_free(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_free(pos_conv_buf);
|
||||
|
||||
if(bidi_txt == NULL) lv_free(buf);
|
||||
return i;
|
||||
}
|
||||
}
|
||||
lv_free(pos_conv_buf);
|
||||
if(bidi_txt == NULL) lv_free(buf);
|
||||
return (uint16_t) -1;
|
||||
}
|
||||
|
||||
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*/
|
||||
lv_bidi_ctx_t ctx;
|
||||
lv_memzero(&ctx, sizeof(ctx));
|
||||
|
||||
/*Process neutral chars in the beginning*/
|
||||
while(rd < len) {
|
||||
uint32_t letter = lv_text_encoded_next(str_in, &rd);
|
||||
pos_conv_rd++;
|
||||
dir = lv_bidi_get_letter_dir(letter);
|
||||
if(dir == LV_BASE_DIR_NEUTRAL) dir = bracket_process(&ctx, str_in, rd, len, letter, base_dir);
|
||||
else if(dir != LV_BASE_DIR_WEAK) break;
|
||||
}
|
||||
|
||||
if(rd && str_in[rd] != '\0' && rd < len) {
|
||||
lv_text_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(&ctx, &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;
|
||||
}
|
||||
}
|
||||
|
||||
void lv_bidi_set_custom_neutrals_static(const char * neutrals)
|
||||
{
|
||||
custom_neutrals = neutrals;
|
||||
}
|
||||
|
||||
/**********************
|
||||
* 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_text_encoded_next(txt, &i);
|
||||
|
||||
while(txt[i] != '\0' && txt[i] != '\n' && txt[i] != '\r') {
|
||||
lv_text_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_text_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 == 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;
|
||||
|
||||
/*Check for Hebrew characters [https://en.wikipedia.org/wiki/Unicode_and_HTML_for_the_Hebrew_alphabet]*/
|
||||
if(letter >= 0x590 && letter <= 0x5FF) return true;
|
||||
if(letter >= 0xFB1D && letter <= 0xFB4F) 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;
|
||||
const char * neutrals = " \t\n\r.,:;'\"`!?%/\\-=()[]{}<>@#&$|";
|
||||
if(custom_neutrals) {
|
||||
neutrals = custom_neutrals;
|
||||
}
|
||||
|
||||
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_text_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(lv_bidi_ctx_t * ctx, 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_text_encoded_next(txt, NULL);
|
||||
lv_base_dir_t dir = lv_bidi_get_letter_dir(letter);
|
||||
if(dir == LV_BASE_DIR_NEUTRAL) dir = bracket_process(ctx, 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_text_encoded_next(txt, &i);
|
||||
|
||||
pos_conv_i++;
|
||||
dir = lv_bidi_get_letter_dir(letter);
|
||||
if(dir == LV_BASE_DIR_NEUTRAL) dir = bracket_process(ctx, 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_text_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(ctx, 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_text_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_text_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_text_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_text_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(lv_bidi_ctx_t * ctx, 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_text_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_text_encoded_prev(txt, &txt_i);
|
||||
while(txt_i > 0) {
|
||||
uint32_t letter_next = lv_text_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 || ctx->br_stack_p == LV_BIDI_BRACKET_DEPTH) return LV_BASE_DIR_NEUTRAL;
|
||||
|
||||
ctx->br_stack[ctx->br_stack_p].bracket_pos = i;
|
||||
ctx->br_stack[ctx->br_stack_p].dir = bracket_dir;
|
||||
|
||||
ctx->br_stack_p++;
|
||||
return bracket_dir;
|
||||
}
|
||||
else if(ctx->br_stack_p > 0) {
|
||||
/*Is the letter a closing bracket of the last opening?*/
|
||||
if(letter == bracket_right[ctx->br_stack[ctx->br_stack_p - 1].bracket_pos]) {
|
||||
bracket_dir = ctx->br_stack[ctx->br_stack_p - 1].dir;
|
||||
ctx->br_stack_p--;
|
||||
return bracket_dir;
|
||||
}
|
||||
}
|
||||
|
||||
return LV_BASE_DIR_NEUTRAL;
|
||||
}
|
||||
|
||||
#endif /*LV_USE_BIDI*/
|
||||
@@ -0,0 +1,83 @@
|
||||
/**
|
||||
* @file lv_bidi.h
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef LV_BIDI_H
|
||||
#define LV_BIDI_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*********************
|
||||
* INCLUDES
|
||||
*********************/
|
||||
#include "../lv_conf_internal.h"
|
||||
#include "lv_types.h"
|
||||
#include "lv_text.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
|
||||
**********************/
|
||||
typedef 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,
|
||||
} lv_base_dir_t;
|
||||
|
||||
/**********************
|
||||
* GLOBAL PROTOTYPES
|
||||
**********************/
|
||||
#if LV_USE_BIDI
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
/**
|
||||
* Set custom neutrals string
|
||||
* @param neutrals default " \t\n\r.,:;'\"`!?%/\\-=()[]{}<>@#&$|"
|
||||
*/
|
||||
void lv_bidi_set_custom_neutrals_static(const char * neutrals);
|
||||
|
||||
/**********************
|
||||
* 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*/
|
||||
@@ -0,0 +1,101 @@
|
||||
/**
|
||||
* @file lv_bidi_private.h
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef LV_BIDI_PRIVATE_H
|
||||
#define LV_BIDI_PRIVATE_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*********************
|
||||
* INCLUDES
|
||||
*********************/
|
||||
|
||||
#include "lv_bidi.h"
|
||||
#if LV_USE_BIDI
|
||||
|
||||
/*********************
|
||||
* DEFINES
|
||||
*********************/
|
||||
|
||||
/**********************
|
||||
* TYPEDEFS
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* GLOBAL PROTOTYPES
|
||||
**********************/
|
||||
|
||||
/**
|
||||
* 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_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_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);
|
||||
|
||||
/**********************
|
||||
* MACROS
|
||||
**********************/
|
||||
|
||||
#endif /*LV_USE_BIDI*/
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /*extern "C"*/
|
||||
#endif
|
||||
|
||||
#endif /*LV_BIDI_PRIVATE_H*/
|
||||
@@ -0,0 +1,295 @@
|
||||
/**
|
||||
* @file lv_circle_buf.c
|
||||
*
|
||||
*/
|
||||
|
||||
/*********************
|
||||
* INCLUDES
|
||||
*********************/
|
||||
|
||||
#include "lv_assert.h"
|
||||
|
||||
#include "lv_circle_buf.h"
|
||||
#include "lv_array.h"
|
||||
|
||||
/*********************
|
||||
* DEFINES
|
||||
*********************/
|
||||
|
||||
/**********************
|
||||
* TYPEDEFS
|
||||
**********************/
|
||||
|
||||
struct _lv_circle_buf_t {
|
||||
lv_array_t array;
|
||||
uint32_t head;
|
||||
uint32_t tail; /**< The next write position */
|
||||
|
||||
bool inner_alloc; /**< true: the array is allocated by the buffer, false: the array is created from an external buffer */
|
||||
};
|
||||
|
||||
/**********************
|
||||
* STATIC PROTOTYPES
|
||||
**********************/
|
||||
|
||||
static void circle_buf_prepare_empty(lv_circle_buf_t * circle_buf);
|
||||
|
||||
/**********************
|
||||
* GLOBAL VARIABLES
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* STATIC VARIABLES
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* MACROS
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* GLOBAL FUNCTIONS
|
||||
**********************/
|
||||
|
||||
lv_circle_buf_t * lv_circle_buf_create(const uint32_t capacity, const uint32_t element_size)
|
||||
{
|
||||
lv_circle_buf_t * circle_buf = lv_malloc(sizeof(lv_circle_buf_t));
|
||||
LV_ASSERT_MALLOC(circle_buf);
|
||||
|
||||
if(circle_buf == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
lv_array_init(&circle_buf->array, capacity, element_size);
|
||||
circle_buf->head = 0;
|
||||
circle_buf->tail = 0;
|
||||
circle_buf->inner_alloc = true;
|
||||
|
||||
circle_buf_prepare_empty(circle_buf);
|
||||
|
||||
return circle_buf;
|
||||
}
|
||||
|
||||
lv_circle_buf_t * lv_circle_buf_create_from_buf(void * buf, const uint32_t capacity, const uint32_t element_size)
|
||||
{
|
||||
LV_ASSERT_NULL(buf);
|
||||
|
||||
lv_circle_buf_t * circle_buf = lv_malloc(sizeof(lv_circle_buf_t));
|
||||
LV_ASSERT_MALLOC(circle_buf);
|
||||
|
||||
if(circle_buf == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
lv_array_init_from_buf(&circle_buf->array, buf, capacity, element_size);
|
||||
circle_buf->head = 0;
|
||||
circle_buf->tail = 0;
|
||||
circle_buf->inner_alloc = false;
|
||||
|
||||
circle_buf_prepare_empty(circle_buf);
|
||||
|
||||
return circle_buf;
|
||||
}
|
||||
|
||||
lv_circle_buf_t * lv_circle_buf_create_from_array(const lv_array_t * array)
|
||||
{
|
||||
LV_ASSERT_NULL(array);
|
||||
if(array == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
lv_circle_buf_t * circle_buf = lv_malloc(sizeof(lv_circle_buf_t));
|
||||
LV_ASSERT_MALLOC(circle_buf);
|
||||
|
||||
if(circle_buf == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
circle_buf->array = *array;
|
||||
circle_buf->head = 0;
|
||||
circle_buf->tail = 0;
|
||||
circle_buf->inner_alloc = false;
|
||||
|
||||
circle_buf_prepare_empty(circle_buf);
|
||||
|
||||
return circle_buf;
|
||||
}
|
||||
|
||||
lv_result_t lv_circle_buf_resize(lv_circle_buf_t * circle_buf, const uint32_t capacity)
|
||||
{
|
||||
LV_ASSERT_NULL(circle_buf);
|
||||
|
||||
if(lv_array_resize(&circle_buf->array, capacity) == false) {
|
||||
return LV_RESULT_INVALID;
|
||||
}
|
||||
|
||||
circle_buf->head = 0;
|
||||
circle_buf->tail = 0;
|
||||
|
||||
circle_buf_prepare_empty(circle_buf);
|
||||
|
||||
return LV_RESULT_OK;
|
||||
}
|
||||
|
||||
void lv_circle_buf_destroy(lv_circle_buf_t * circle_buf)
|
||||
{
|
||||
LV_ASSERT_NULL(circle_buf);
|
||||
|
||||
lv_array_deinit(&circle_buf->array);
|
||||
|
||||
lv_free(circle_buf);
|
||||
}
|
||||
|
||||
uint32_t lv_circle_buf_size(const lv_circle_buf_t * circle_buf)
|
||||
{
|
||||
LV_ASSERT_NULL(circle_buf);
|
||||
|
||||
return circle_buf->tail - circle_buf->head;
|
||||
}
|
||||
|
||||
uint32_t lv_circle_buf_capacity(const lv_circle_buf_t * circle_buf)
|
||||
{
|
||||
LV_ASSERT_NULL(circle_buf);
|
||||
|
||||
return lv_array_capacity(&circle_buf->array);
|
||||
}
|
||||
|
||||
uint32_t lv_circle_buf_remain(const lv_circle_buf_t * circle_buf)
|
||||
{
|
||||
LV_ASSERT_NULL(circle_buf);
|
||||
|
||||
return lv_circle_buf_capacity(circle_buf) - lv_circle_buf_size(circle_buf);
|
||||
}
|
||||
|
||||
bool lv_circle_buf_is_empty(const lv_circle_buf_t * circle_buf)
|
||||
{
|
||||
LV_ASSERT_NULL(circle_buf);
|
||||
|
||||
return !lv_circle_buf_size(circle_buf);
|
||||
}
|
||||
|
||||
bool lv_circle_buf_is_full(const lv_circle_buf_t * circle_buf)
|
||||
{
|
||||
LV_ASSERT_NULL(circle_buf);
|
||||
|
||||
return !lv_circle_buf_remain(circle_buf);
|
||||
}
|
||||
|
||||
void lv_circle_buf_reset(lv_circle_buf_t * circle_buf)
|
||||
{
|
||||
LV_ASSERT_NULL(circle_buf);
|
||||
|
||||
circle_buf->head = 0;
|
||||
circle_buf->tail = 0;
|
||||
}
|
||||
|
||||
void * lv_circle_buf_head(const lv_circle_buf_t * circle_buf)
|
||||
{
|
||||
LV_ASSERT_NULL(circle_buf);
|
||||
|
||||
return lv_array_at(&circle_buf->array,
|
||||
circle_buf->head % lv_circle_buf_capacity(circle_buf));
|
||||
}
|
||||
|
||||
void * lv_circle_buf_tail(const lv_circle_buf_t * circle_buf)
|
||||
{
|
||||
LV_ASSERT_NULL(circle_buf);
|
||||
|
||||
return lv_array_at(&circle_buf->array,
|
||||
circle_buf->tail % lv_circle_buf_capacity(circle_buf));
|
||||
}
|
||||
|
||||
lv_result_t lv_circle_buf_read(lv_circle_buf_t * circle_buf, void * data)
|
||||
{
|
||||
LV_ASSERT_NULL(circle_buf);
|
||||
|
||||
if(lv_circle_buf_is_empty(circle_buf)) {
|
||||
circle_buf->head = 0;
|
||||
circle_buf->tail = 0;
|
||||
return LV_RESULT_INVALID;
|
||||
}
|
||||
|
||||
lv_circle_buf_peek_at(circle_buf, 0, data);
|
||||
circle_buf->head++;
|
||||
|
||||
return LV_RESULT_OK;
|
||||
}
|
||||
|
||||
lv_result_t lv_circle_buf_write(lv_circle_buf_t * circle_buf, const void * data)
|
||||
{
|
||||
LV_ASSERT_NULL(circle_buf);
|
||||
|
||||
if(lv_circle_buf_is_full(circle_buf)) {
|
||||
return LV_RESULT_INVALID;
|
||||
}
|
||||
|
||||
lv_array_assign(&circle_buf->array, circle_buf->tail % lv_circle_buf_capacity(circle_buf), data);
|
||||
circle_buf->tail++;
|
||||
|
||||
return LV_RESULT_OK;
|
||||
}
|
||||
|
||||
uint32_t lv_circle_buf_fill(lv_circle_buf_t * circle_buf, uint32_t count, lv_circle_buf_fill_cb_t fill_cb,
|
||||
void * user_data)
|
||||
{
|
||||
LV_ASSERT_NULL(circle_buf);
|
||||
LV_ASSERT_NULL(fill_cb);
|
||||
|
||||
uint32_t filled = 0;
|
||||
while(count > 0 && !lv_circle_buf_is_full(circle_buf)) {
|
||||
void * data = lv_circle_buf_tail(circle_buf);
|
||||
if(fill_cb(data, circle_buf->array.element_size, (int32_t)filled, user_data) == LV_RESULT_OK) {
|
||||
circle_buf->tail++;
|
||||
filled++;
|
||||
}
|
||||
else break;
|
||||
|
||||
count--;
|
||||
}
|
||||
|
||||
return filled;
|
||||
}
|
||||
|
||||
lv_result_t lv_circle_buf_skip(lv_circle_buf_t * circle_buf)
|
||||
{
|
||||
LV_ASSERT_NULL(circle_buf);
|
||||
|
||||
if(lv_circle_buf_is_empty(circle_buf)) {
|
||||
circle_buf->head = 0;
|
||||
circle_buf->tail = 0;
|
||||
return LV_RESULT_INVALID;
|
||||
}
|
||||
|
||||
circle_buf->head++;
|
||||
|
||||
return LV_RESULT_OK;
|
||||
}
|
||||
|
||||
lv_result_t lv_circle_buf_peek(const lv_circle_buf_t * circle_buf, void * data)
|
||||
{
|
||||
LV_ASSERT_NULL(circle_buf);
|
||||
LV_ASSERT_NULL(data);
|
||||
|
||||
return lv_circle_buf_peek_at(circle_buf, 0, data);
|
||||
}
|
||||
|
||||
lv_result_t lv_circle_buf_peek_at(const lv_circle_buf_t * circle_buf, const uint32_t index, void * data)
|
||||
{
|
||||
LV_ASSERT_NULL(circle_buf);
|
||||
LV_ASSERT_NULL(data);
|
||||
|
||||
const uint32_t real_index = (index % lv_circle_buf_size(circle_buf) + circle_buf->head) % lv_circle_buf_capacity(
|
||||
circle_buf);
|
||||
lv_memcpy(data, lv_array_at(&circle_buf->array, real_index), circle_buf->array.element_size);
|
||||
|
||||
return LV_RESULT_OK;
|
||||
}
|
||||
|
||||
/**********************
|
||||
* STATIC FUNCTIONS
|
||||
**********************/
|
||||
|
||||
static void circle_buf_prepare_empty(lv_circle_buf_t * circle_buf)
|
||||
{
|
||||
const uint32_t required = lv_array_capacity(&circle_buf->array) - lv_array_size(&circle_buf->array);
|
||||
for(uint32_t i = 0; i < required; i++) lv_array_push_back(&circle_buf->array, NULL);
|
||||
}
|
||||
@@ -0,0 +1,191 @@
|
||||
/**
|
||||
* @file lv_circle_buf.h
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifndef LV_CIRCLE_BUF_H
|
||||
#define LV_CIRCLE_BUF_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*********************
|
||||
* INCLUDES
|
||||
*********************/
|
||||
|
||||
#include "lv_types.h"
|
||||
|
||||
/*********************
|
||||
* DEFINES
|
||||
*********************/
|
||||
|
||||
/**********************
|
||||
* TYPEDEFS
|
||||
**********************/
|
||||
|
||||
typedef bool (*lv_circle_buf_fill_cb_t)(void * buf, uint32_t buff_len, int32_t index, void * user_data);
|
||||
|
||||
/**********************
|
||||
* GLOBAL PROTOTYPES
|
||||
**********************/
|
||||
|
||||
/**
|
||||
* Create a circle buffer
|
||||
* @param capacity the maximum number of elements in the buffer
|
||||
* @param element_size the size of an element in bytes
|
||||
* @return pointer to the created buffer
|
||||
*/
|
||||
lv_circle_buf_t * lv_circle_buf_create(uint32_t capacity, uint32_t element_size);
|
||||
|
||||
/**
|
||||
* Create a circle buffer from an existing buffer
|
||||
* @param buf pointer to a buffer
|
||||
* @param capacity the maximum number of elements in the buffer
|
||||
* @param element_size the size of an element in bytes
|
||||
* @return pointer to the created buffer
|
||||
*/
|
||||
lv_circle_buf_t * lv_circle_buf_create_from_buf(void * buf, uint32_t capacity, uint32_t element_size);
|
||||
|
||||
/**
|
||||
* Create a circle buffer from an existing array
|
||||
* @param array pointer to an array
|
||||
* @return pointer to the created buffer
|
||||
*/
|
||||
lv_circle_buf_t * lv_circle_buf_create_from_array(const lv_array_t * array);
|
||||
|
||||
/**
|
||||
* Resize the buffer
|
||||
* @param circle_buf pointer to a buffer
|
||||
* @param capacity the new capacity of the buffer
|
||||
* @return LV_RESULT_OK: the buffer is resized; LV_RESULT_INVALID: the buffer is not resized
|
||||
*/
|
||||
lv_result_t lv_circle_buf_resize(lv_circle_buf_t * circle_buf, uint32_t capacity);
|
||||
|
||||
/**
|
||||
* Destroy a circle buffer
|
||||
* @param circle_buf pointer to buffer
|
||||
*/
|
||||
void lv_circle_buf_destroy(lv_circle_buf_t * circle_buf);
|
||||
|
||||
/**
|
||||
* Get the size of the buffer
|
||||
* @param circle_buf pointer to buffer
|
||||
* @return the number of elements in the buffer
|
||||
*/
|
||||
uint32_t lv_circle_buf_size(const lv_circle_buf_t * circle_buf);
|
||||
|
||||
/**
|
||||
* Get the capacity of the buffer
|
||||
* @param circle_buf pointer to buffer
|
||||
* @return the maximum number of elements in the buffer
|
||||
*/
|
||||
uint32_t lv_circle_buf_capacity(const lv_circle_buf_t * circle_buf);
|
||||
|
||||
/**
|
||||
* Get the remaining space in the buffer
|
||||
* @param circle_buf pointer to buffer
|
||||
* @return the number of elements that can be written to the buffer
|
||||
*/
|
||||
uint32_t lv_circle_buf_remain(const lv_circle_buf_t * circle_buf);
|
||||
|
||||
/**
|
||||
* Check if the buffer is empty
|
||||
* @param circle_buf pointer to buffer
|
||||
* @return true: the buffer is empty; false: the buffer is not empty
|
||||
*/
|
||||
bool lv_circle_buf_is_empty(const lv_circle_buf_t * circle_buf);
|
||||
|
||||
/**
|
||||
* Check if the buffer is full
|
||||
* @param circle_buf pointer to buffer
|
||||
* @return true: the buffer is full; false: the buffer is not full
|
||||
*/
|
||||
bool lv_circle_buf_is_full(const lv_circle_buf_t * circle_buf);
|
||||
|
||||
/**
|
||||
* Reset the buffer
|
||||
* @param circle_buf pointer to buffer
|
||||
* @return LV_RESULT_OK: the buffer is reset; LV_RESULT_INVALID: the buffer is not reset
|
||||
*/
|
||||
void lv_circle_buf_reset(lv_circle_buf_t * circle_buf);
|
||||
|
||||
/**
|
||||
* Get the head of the buffer
|
||||
* @param circle_buf pointer to buffer
|
||||
* @return pointer to the head of the buffer
|
||||
*/
|
||||
void * lv_circle_buf_head(const lv_circle_buf_t * circle_buf);
|
||||
|
||||
/**
|
||||
* Get the tail of the buffer
|
||||
* @param circle_buf pointer to buffer
|
||||
* @return pointer to the tail of the buffer
|
||||
*/
|
||||
void * lv_circle_buf_tail(const lv_circle_buf_t * circle_buf);
|
||||
|
||||
/**
|
||||
* Read a value
|
||||
* @param circle_buf pointer to buffer
|
||||
* @param data pointer to a variable to store the read value
|
||||
* @return LV_RESULT_OK: the value is read; LV_RESULT_INVALID: the value is not read
|
||||
*/
|
||||
lv_result_t lv_circle_buf_read(lv_circle_buf_t * circle_buf, void * data);
|
||||
|
||||
/**
|
||||
* Write a value
|
||||
* @param circle_buf pointer to buffer
|
||||
* @param data pointer to the value to write
|
||||
* @return LV_RESULT_OK: the value is written; LV_RESULT_INVALID: the value is not written
|
||||
*/
|
||||
lv_result_t lv_circle_buf_write(lv_circle_buf_t * circle_buf, const void * data);
|
||||
|
||||
/**
|
||||
* Fill the buffer with values
|
||||
* @param circle_buf pointer to buffer
|
||||
* @param count the number of values to fill
|
||||
* @param fill_cb the callback function to fill the buffer
|
||||
* @param user_data
|
||||
* @return the number of values filled
|
||||
*/
|
||||
uint32_t lv_circle_buf_fill(lv_circle_buf_t * circle_buf, uint32_t count, lv_circle_buf_fill_cb_t fill_cb,
|
||||
void * user_data);
|
||||
|
||||
/**
|
||||
* Skip a value
|
||||
* @param circle_buf pointer to buffer
|
||||
* @return LV_RESULT_OK: the value is skipped; LV_RESULT_INVALID: the value is not skipped
|
||||
*/
|
||||
lv_result_t lv_circle_buf_skip(lv_circle_buf_t * circle_buf);
|
||||
|
||||
/**
|
||||
* Peek a value
|
||||
* @param circle_buf pointer to buffer
|
||||
* @param data pointer to a variable to store the peeked value
|
||||
* @return LV_RESULT_OK: the value is peeked; LV_RESULT_INVALID: the value is not peeked
|
||||
*/
|
||||
lv_result_t lv_circle_buf_peek(const lv_circle_buf_t * circle_buf, void * data);
|
||||
|
||||
/**
|
||||
* Peek a value at an index
|
||||
* @param circle_buf pointer to buffer
|
||||
* @param index the index of the value to peek, if the index is greater than the size of the buffer, it will return looply.
|
||||
* @param data pointer to a variable to store the peeked value
|
||||
* @return LV_RESULT_OK: the value is peeked; LV_RESULT_INVALID: the value is not peeked
|
||||
*/
|
||||
lv_result_t lv_circle_buf_peek_at(const lv_circle_buf_t * circle_buf, uint32_t index, void * data);
|
||||
|
||||
/*************************
|
||||
* GLOBAL VARIABLES
|
||||
*************************/
|
||||
|
||||
/**********************
|
||||
* MACROS
|
||||
**********************/
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /*extern "C"*/
|
||||
#endif
|
||||
|
||||
#endif /*LV_CIRCLE_BUF_H*/
|
||||
@@ -0,0 +1,421 @@
|
||||
/**
|
||||
* @file lv_color.c
|
||||
*
|
||||
*/
|
||||
|
||||
/*********************
|
||||
* INCLUDES
|
||||
*********************/
|
||||
#include "lv_color.h"
|
||||
#include "lv_log.h"
|
||||
|
||||
/*********************
|
||||
* DEFINES
|
||||
*********************/
|
||||
|
||||
/**********************
|
||||
* TYPEDEFS
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* STATIC PROTOTYPES
|
||||
**********************/
|
||||
static lv_color_t lv_color_filter_shade_cb(const lv_color_filter_dsc_t * dsc, lv_color_t c, lv_opa_t opa);
|
||||
|
||||
/**********************
|
||||
* GLOBAL VARIABLES
|
||||
**********************/
|
||||
|
||||
const lv_color_filter_dsc_t lv_color_filter_shade = {.filter_cb = lv_color_filter_shade_cb};
|
||||
|
||||
/**********************
|
||||
* STATIC VARIABLES
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* MACROS
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* GLOBAL FUNCTIONS
|
||||
**********************/
|
||||
|
||||
uint8_t lv_color_format_get_bpp(lv_color_format_t cf)
|
||||
{
|
||||
switch(cf) {
|
||||
case LV_COLOR_FORMAT_I1:
|
||||
case LV_COLOR_FORMAT_A1:
|
||||
return 1;
|
||||
case LV_COLOR_FORMAT_I2:
|
||||
case LV_COLOR_FORMAT_A2:
|
||||
return 2;
|
||||
case LV_COLOR_FORMAT_I4:
|
||||
case LV_COLOR_FORMAT_A4:
|
||||
case LV_COLOR_FORMAT_NEMA_TSC4:
|
||||
return 4;
|
||||
case LV_COLOR_FORMAT_NEMA_TSC6:
|
||||
case LV_COLOR_FORMAT_NEMA_TSC6A:
|
||||
case LV_COLOR_FORMAT_NEMA_TSC6AP:
|
||||
return 6;
|
||||
case LV_COLOR_FORMAT_L8:
|
||||
case LV_COLOR_FORMAT_A8:
|
||||
case LV_COLOR_FORMAT_I8:
|
||||
case LV_COLOR_FORMAT_ARGB2222:
|
||||
return 8;
|
||||
case LV_COLOR_FORMAT_NEMA_TSC12:
|
||||
case LV_COLOR_FORMAT_NEMA_TSC12A:
|
||||
return 12;
|
||||
case LV_COLOR_FORMAT_RGB565A8:
|
||||
case LV_COLOR_FORMAT_RGB565:
|
||||
case LV_COLOR_FORMAT_RGB565_SWAPPED:
|
||||
case LV_COLOR_FORMAT_YUY2:
|
||||
case LV_COLOR_FORMAT_AL88:
|
||||
case LV_COLOR_FORMAT_ARGB1555:
|
||||
case LV_COLOR_FORMAT_ARGB4444:
|
||||
return 16;
|
||||
|
||||
case LV_COLOR_FORMAT_ARGB8565:
|
||||
case LV_COLOR_FORMAT_RGB888:
|
||||
return 24;
|
||||
case LV_COLOR_FORMAT_ARGB8888:
|
||||
case LV_COLOR_FORMAT_ARGB8888_PREMULTIPLIED:
|
||||
case LV_COLOR_FORMAT_XRGB8888:
|
||||
return 32;
|
||||
|
||||
case LV_COLOR_FORMAT_UNKNOWN:
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
bool lv_color_format_has_alpha(lv_color_format_t cf)
|
||||
{
|
||||
switch(cf) {
|
||||
case LV_COLOR_FORMAT_A1:
|
||||
case LV_COLOR_FORMAT_A2:
|
||||
case LV_COLOR_FORMAT_A4:
|
||||
case LV_COLOR_FORMAT_A8:
|
||||
case LV_COLOR_FORMAT_I1:
|
||||
case LV_COLOR_FORMAT_I2:
|
||||
case LV_COLOR_FORMAT_I4:
|
||||
case LV_COLOR_FORMAT_I8:
|
||||
case LV_COLOR_FORMAT_RGB565A8:
|
||||
case LV_COLOR_FORMAT_ARGB8565:
|
||||
case LV_COLOR_FORMAT_ARGB8888:
|
||||
case LV_COLOR_FORMAT_ARGB8888_PREMULTIPLIED:
|
||||
case LV_COLOR_FORMAT_AL88:
|
||||
case LV_COLOR_FORMAT_ARGB2222:
|
||||
case LV_COLOR_FORMAT_ARGB1555:
|
||||
case LV_COLOR_FORMAT_ARGB4444:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
lv_color32_t lv_color_to_32(lv_color_t color, lv_opa_t opa)
|
||||
{
|
||||
lv_color32_t c32;
|
||||
c32.red = color.red;
|
||||
c32.green = color.green;
|
||||
c32.blue = color.blue;
|
||||
c32.alpha = opa;
|
||||
return c32;
|
||||
}
|
||||
|
||||
uint16_t lv_color_to_u16(lv_color_t color)
|
||||
{
|
||||
return ((color.red & 0xF8) << 8) + ((color.green & 0xFC) << 3) + ((color.blue & 0xF8) >> 3);
|
||||
}
|
||||
|
||||
uint32_t lv_color_to_u32(lv_color_t color)
|
||||
{
|
||||
return (uint32_t)((uint32_t)0xff << 24) + (color.red << 16) + (color.green << 8) + (color.blue);
|
||||
}
|
||||
|
||||
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_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;
|
||||
}
|
||||
|
||||
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 c)
|
||||
{
|
||||
return lv_color_rgb_to_hsv(c.red, c.green, c.blue);
|
||||
}
|
||||
|
||||
uint8_t lv_color_format_get_size(lv_color_format_t cf)
|
||||
{
|
||||
return (lv_color_format_get_bpp(cf) + 7) >> 3;
|
||||
}
|
||||
|
||||
uint32_t lv_color_to_int(lv_color_t c)
|
||||
{
|
||||
uint8_t * tmp = (uint8_t *) &c;
|
||||
return tmp[0] + (tmp[1] << 8) + (tmp[2] << 16);
|
||||
}
|
||||
|
||||
bool lv_color_eq(lv_color_t c1, lv_color_t c2)
|
||||
{
|
||||
return lv_color_to_int(c1) == lv_color_to_int(c2);
|
||||
}
|
||||
|
||||
bool lv_color32_eq(lv_color32_t c1, lv_color32_t c2)
|
||||
{
|
||||
return *((uint32_t *)&c1) == *((uint32_t *)&c2);
|
||||
}
|
||||
|
||||
lv_color_t lv_color_hex(uint32_t c)
|
||||
{
|
||||
lv_color_t ret;
|
||||
ret.red = (c >> 16) & 0xff;
|
||||
ret.green = (c >> 8) & 0xff;
|
||||
ret.blue = (c >> 0) & 0xff;
|
||||
return ret;
|
||||
}
|
||||
|
||||
lv_color_t lv_color_make(uint8_t r, uint8_t g, uint8_t b)
|
||||
{
|
||||
lv_color_t ret;
|
||||
ret.red = r;
|
||||
ret.green = g;
|
||||
ret.blue = b;
|
||||
return ret;
|
||||
}
|
||||
|
||||
lv_color32_t lv_color32_make(uint8_t r, uint8_t g, uint8_t b, uint8_t a)
|
||||
{
|
||||
lv_color32_t ret;
|
||||
ret.red = r;
|
||||
ret.green = g;
|
||||
ret.blue = b;
|
||||
ret.alpha = a;
|
||||
return ret;
|
||||
}
|
||||
|
||||
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)));
|
||||
}
|
||||
|
||||
uint16_t LV_ATTRIBUTE_FAST_MEM lv_color_16_16_mix(uint16_t c1, uint16_t c2, uint8_t mix)
|
||||
{
|
||||
if(mix == 255) return c1;
|
||||
if(mix == 0) return c2;
|
||||
if(c1 == c2) return c1;
|
||||
|
||||
uint16_t ret;
|
||||
|
||||
/* Source: https://stackoverflow.com/a/50012418/1999969*/
|
||||
mix = (uint32_t)((uint32_t)mix + 4) >> 3;
|
||||
|
||||
/*0x7E0F81F = 0b00000111111000001111100000011111*/
|
||||
uint32_t bg = (uint32_t)(c2 | ((uint32_t)c2 << 16)) & 0x7E0F81F;
|
||||
uint32_t fg = (uint32_t)(c1 | ((uint32_t)c1 << 16)) & 0x7E0F81F;
|
||||
uint32_t result = ((((fg - bg) * mix) >> 5) + bg) & 0x7E0F81F;
|
||||
ret = (uint16_t)(result >> 16) | result;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
lv_color_t lv_color_white(void)
|
||||
{
|
||||
return lv_color_make(0xff, 0xff, 0xff);
|
||||
}
|
||||
|
||||
lv_color_t lv_color_black(void)
|
||||
{
|
||||
return lv_color_make(0x00, 0x00, 0x00);
|
||||
}
|
||||
|
||||
void lv_color_premultiply(lv_color32_t * c)
|
||||
{
|
||||
if(c->alpha == LV_OPA_COVER) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(c->alpha == LV_OPA_TRANSP) {
|
||||
lv_memzero(c, sizeof(lv_color32_t));
|
||||
return;
|
||||
}
|
||||
|
||||
c->red = LV_OPA_MIX2(c->red, c->alpha);
|
||||
c->green = LV_OPA_MIX2(c->green, c->alpha);
|
||||
c->blue = LV_OPA_MIX2(c->blue, c->alpha);
|
||||
}
|
||||
|
||||
void lv_color16_premultiply(lv_color16_t * c, lv_opa_t a)
|
||||
{
|
||||
if(a == LV_OPA_COVER) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(a == LV_OPA_TRANSP) {
|
||||
lv_memzero(c, sizeof(lv_color16_t));
|
||||
return;
|
||||
}
|
||||
|
||||
c->red = LV_OPA_MIX2(c->red, a);
|
||||
c->green = LV_OPA_MIX2(c->green, a);
|
||||
c->blue = LV_OPA_MIX2(c->blue, a);
|
||||
}
|
||||
|
||||
uint8_t lv_color_luminance(lv_color_t c)
|
||||
{
|
||||
return (uint8_t)((uint16_t)(77u * c.red + 151u * c.green + 28u * c.blue) >> 8);
|
||||
}
|
||||
|
||||
uint8_t lv_color16_luminance(const lv_color16_t c)
|
||||
{
|
||||
return (uint8_t)((uint16_t)(635u * c.red + 613u * c.green + 231u * c.blue) >> 8);
|
||||
}
|
||||
|
||||
uint8_t lv_color24_luminance(const uint8_t * c)
|
||||
{
|
||||
return (uint8_t)((uint16_t)(77u * c[2] + 151u * c[1] + 28u * c[0]) >> 8);
|
||||
}
|
||||
|
||||
uint8_t lv_color32_luminance(lv_color32_t c)
|
||||
{
|
||||
return (uint8_t)((uint16_t)(77u * c.red + 151u * c.green + 28u * c.blue) >> 8);
|
||||
}
|
||||
|
||||
|
||||
/**********************
|
||||
* STATIC FUNCTIONS
|
||||
**********************/
|
||||
|
||||
/**
|
||||
* Helper function to easily create color filters
|
||||
* @param dsc pointer to a color filter descriptor
|
||||
* @param c the color to modify
|
||||
* @param opa the intensity of the modification
|
||||
* - LV_OPA_50: do nothing
|
||||
* - < LV_OPA_50: darken
|
||||
* - LV_OPA_0: fully black
|
||||
* - > LV_OPA_50: lighten
|
||||
* - LV_OPA_100: fully white
|
||||
* @return the modified color
|
||||
*/
|
||||
static lv_color_t lv_color_filter_shade_cb(const lv_color_filter_dsc_t * dsc, lv_color_t c, lv_opa_t opa)
|
||||
{
|
||||
LV_UNUSED(dsc);
|
||||
if(opa == LV_OPA_50) return c;
|
||||
if(opa < LV_OPA_50) return lv_color_lighten(c, (LV_OPA_50 - opa) * 2);
|
||||
else return lv_color_darken(c, (opa - LV_OPA_50 * LV_OPA_50) * 2);
|
||||
}
|
||||
@@ -0,0 +1,467 @@
|
||||
/**
|
||||
* @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"
|
||||
|
||||
/*********************
|
||||
* DEFINES
|
||||
*********************/
|
||||
LV_EXPORT_CONST_INT(LV_COLOR_DEPTH);
|
||||
|
||||
#if LV_COLOR_DEPTH == 8
|
||||
#define LV_COLOR_NATIVE_WITH_ALPHA_SIZE 2
|
||||
#elif LV_COLOR_DEPTH == 16
|
||||
#define LV_COLOR_NATIVE_WITH_ALPHA_SIZE 3
|
||||
#elif LV_COLOR_DEPTH == 24
|
||||
#define LV_COLOR_NATIVE_WITH_ALPHA_SIZE 4
|
||||
#elif LV_COLOR_DEPTH == 32
|
||||
#define LV_COLOR_NATIVE_WITH_ALPHA_SIZE 4
|
||||
#endif
|
||||
|
||||
/**
|
||||
* 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 /**< Fully transparent if opa <= LV_OPA_MIN */
|
||||
#define LV_OPA_MAX 253 /**< Fully cover if opa >= LV_OPA_MAX */
|
||||
|
||||
/**
|
||||
* Get the pixel size of a color format in bits, bpp
|
||||
* @param cf a color format (`LV_COLOR_FORMAT_...`)
|
||||
* @return the pixel size in bits
|
||||
* @sa lv_color_format_get_bpp
|
||||
*/
|
||||
#define LV_COLOR_FORMAT_GET_BPP(cf) ( \
|
||||
(cf) == LV_COLOR_FORMAT_I1 ? 1 : \
|
||||
(cf) == LV_COLOR_FORMAT_A1 ? 1 : \
|
||||
(cf) == LV_COLOR_FORMAT_I2 ? 2 : \
|
||||
(cf) == LV_COLOR_FORMAT_A2 ? 2 : \
|
||||
(cf) == LV_COLOR_FORMAT_I4 ? 4 : \
|
||||
(cf) == LV_COLOR_FORMAT_A4 ? 4 : \
|
||||
(cf) == LV_COLOR_FORMAT_NEMA_TSC4 ? 4 : \
|
||||
(cf) == LV_COLOR_FORMAT_NEMA_TSC6 ? 6 : \
|
||||
(cf) == LV_COLOR_FORMAT_NEMA_TSC6A ? 6 : \
|
||||
(cf) == LV_COLOR_FORMAT_NEMA_TSC6AP ? 6 : \
|
||||
(cf) == LV_COLOR_FORMAT_L8 ? 8 : \
|
||||
(cf) == LV_COLOR_FORMAT_A8 ? 8 : \
|
||||
(cf) == LV_COLOR_FORMAT_I8 ? 8 : \
|
||||
(cf) == LV_COLOR_FORMAT_ARGB2222 ? 8 : \
|
||||
(cf) == LV_COLOR_FORMAT_NEMA_TSC12 ? 12 : \
|
||||
(cf) == LV_COLOR_FORMAT_NEMA_TSC12A ? 12 : \
|
||||
(cf) == LV_COLOR_FORMAT_AL88 ? 16 : \
|
||||
(cf) == LV_COLOR_FORMAT_RGB565 ? 16 : \
|
||||
(cf) == LV_COLOR_FORMAT_RGB565_SWAPPED ? 16 : \
|
||||
(cf) == LV_COLOR_FORMAT_RGB565A8 ? 16 : \
|
||||
(cf) == LV_COLOR_FORMAT_YUY2 ? 16 : \
|
||||
(cf) == LV_COLOR_FORMAT_ARGB1555 ? 16 : \
|
||||
(cf) == LV_COLOR_FORMAT_ARGB4444 ? 16 : \
|
||||
(cf) == LV_COLOR_FORMAT_ARGB8565 ? 24 : \
|
||||
(cf) == LV_COLOR_FORMAT_RGB888 ? 24 : \
|
||||
(cf) == LV_COLOR_FORMAT_ARGB8888 ? 32 : \
|
||||
(cf) == LV_COLOR_FORMAT_ARGB8888_PREMULTIPLIED ? 32 : \
|
||||
(cf) == LV_COLOR_FORMAT_XRGB8888 ? 32 : \
|
||||
0 \
|
||||
)
|
||||
|
||||
/**
|
||||
* Get the pixel size of a color format in bytes
|
||||
* @param cf a color format (`LV_COLOR_FORMAT_...`)
|
||||
* @return the pixel size in bytes
|
||||
* @sa lv_color_format_get_size
|
||||
*/
|
||||
#define LV_COLOR_FORMAT_GET_SIZE(cf) ((LV_COLOR_FORMAT_GET_BPP(cf) + 7) >> 3)
|
||||
|
||||
/**********************
|
||||
* TYPEDEFS
|
||||
**********************/
|
||||
|
||||
typedef struct {
|
||||
uint8_t blue;
|
||||
uint8_t green;
|
||||
uint8_t red;
|
||||
} lv_color_t;
|
||||
|
||||
typedef struct {
|
||||
uint16_t blue : 5;
|
||||
uint16_t green : 6;
|
||||
uint16_t red : 5;
|
||||
} lv_color16_t;
|
||||
|
||||
typedef struct {
|
||||
uint8_t blue;
|
||||
uint8_t green;
|
||||
uint8_t red;
|
||||
uint8_t alpha;
|
||||
} lv_color32_t;
|
||||
|
||||
typedef struct {
|
||||
uint16_t h;
|
||||
uint8_t s;
|
||||
uint8_t v;
|
||||
} lv_color_hsv_t;
|
||||
|
||||
typedef struct {
|
||||
uint8_t lumi;
|
||||
uint8_t alpha;
|
||||
} lv_color16a_t;
|
||||
|
||||
typedef enum {
|
||||
LV_COLOR_FORMAT_UNKNOWN = 0,
|
||||
|
||||
LV_COLOR_FORMAT_RAW = 0x01,
|
||||
LV_COLOR_FORMAT_RAW_ALPHA = 0x02,
|
||||
|
||||
/*<=1 byte (+alpha) formats*/
|
||||
LV_COLOR_FORMAT_L8 = 0x06,
|
||||
LV_COLOR_FORMAT_I1 = 0x07,
|
||||
LV_COLOR_FORMAT_I2 = 0x08,
|
||||
LV_COLOR_FORMAT_I4 = 0x09,
|
||||
LV_COLOR_FORMAT_I8 = 0x0A,
|
||||
LV_COLOR_FORMAT_A8 = 0x0E,
|
||||
|
||||
/*2 byte (+alpha) formats*/
|
||||
LV_COLOR_FORMAT_RGB565 = 0x12,
|
||||
LV_COLOR_FORMAT_ARGB8565 = 0x13, /**< Not supported by sw renderer yet. */
|
||||
LV_COLOR_FORMAT_RGB565A8 = 0x14, /**< Color array followed by Alpha array*/
|
||||
LV_COLOR_FORMAT_AL88 = 0x15, /**< L8 with alpha >*/
|
||||
LV_COLOR_FORMAT_RGB565_SWAPPED = 0x1B,
|
||||
|
||||
/*3 byte (+alpha) formats*/
|
||||
LV_COLOR_FORMAT_RGB888 = 0x0F,
|
||||
LV_COLOR_FORMAT_ARGB8888 = 0x10,
|
||||
LV_COLOR_FORMAT_XRGB8888 = 0x11,
|
||||
LV_COLOR_FORMAT_ARGB8888_PREMULTIPLIED = 0x1A,
|
||||
|
||||
/*Formats not supported by software renderer but kept here so GPU can use it*/
|
||||
LV_COLOR_FORMAT_A1 = 0x0B,
|
||||
LV_COLOR_FORMAT_A2 = 0x0C,
|
||||
LV_COLOR_FORMAT_A4 = 0x0D,
|
||||
LV_COLOR_FORMAT_ARGB1555 = 0x16,
|
||||
LV_COLOR_FORMAT_ARGB4444 = 0x17,
|
||||
LV_COLOR_FORMAT_ARGB2222 = 0X18,
|
||||
|
||||
/* reference to https://wiki.videolan.org/YUV/ */
|
||||
/*YUV planar formats*/
|
||||
LV_COLOR_FORMAT_YUV_START = 0x20,
|
||||
LV_COLOR_FORMAT_I420 = LV_COLOR_FORMAT_YUV_START, /*YUV420 planar(3 plane)*/
|
||||
LV_COLOR_FORMAT_I422 = 0x21, /*YUV422 planar(3 plane)*/
|
||||
LV_COLOR_FORMAT_I444 = 0x22, /*YUV444 planar(3 plane)*/
|
||||
LV_COLOR_FORMAT_I400 = 0x23, /*YUV400 no chroma channel*/
|
||||
LV_COLOR_FORMAT_NV21 = 0x24, /*YUV420 planar(2 plane), UV plane in 'V, U, V, U'*/
|
||||
LV_COLOR_FORMAT_NV12 = 0x25, /*YUV420 planar(2 plane), UV plane in 'U, V, U, V'*/
|
||||
|
||||
/*YUV packed formats*/
|
||||
LV_COLOR_FORMAT_YUY2 = 0x26, /*YUV422 packed like 'Y U Y V'*/
|
||||
LV_COLOR_FORMAT_UYVY = 0x27, /*YUV422 packed like 'U Y V Y'*/
|
||||
|
||||
LV_COLOR_FORMAT_YUV_END = LV_COLOR_FORMAT_UYVY,
|
||||
|
||||
LV_COLOR_FORMAT_PROPRIETARY_START = 0x30,
|
||||
|
||||
LV_COLOR_FORMAT_NEMA_TSC_START = LV_COLOR_FORMAT_PROPRIETARY_START,
|
||||
LV_COLOR_FORMAT_NEMA_TSC4 = LV_COLOR_FORMAT_NEMA_TSC_START,
|
||||
LV_COLOR_FORMAT_NEMA_TSC6 = 0x31,
|
||||
LV_COLOR_FORMAT_NEMA_TSC6A = 0x32,
|
||||
LV_COLOR_FORMAT_NEMA_TSC6AP = 0x33,
|
||||
LV_COLOR_FORMAT_NEMA_TSC12 = 0x34,
|
||||
LV_COLOR_FORMAT_NEMA_TSC12A = 0x35,
|
||||
LV_COLOR_FORMAT_NEMA_TSC_END = LV_COLOR_FORMAT_NEMA_TSC12A,
|
||||
|
||||
/*Color formats in which LVGL can render*/
|
||||
#if LV_COLOR_DEPTH == 1
|
||||
LV_COLOR_FORMAT_NATIVE = LV_COLOR_FORMAT_I1,
|
||||
LV_COLOR_FORMAT_NATIVE_WITH_ALPHA = LV_COLOR_FORMAT_I1,
|
||||
#elif LV_COLOR_DEPTH == 8
|
||||
LV_COLOR_FORMAT_NATIVE = LV_COLOR_FORMAT_L8,
|
||||
LV_COLOR_FORMAT_NATIVE_WITH_ALPHA = LV_COLOR_FORMAT_AL88,
|
||||
#elif LV_COLOR_DEPTH == 16
|
||||
LV_COLOR_FORMAT_NATIVE = LV_COLOR_FORMAT_RGB565,
|
||||
LV_COLOR_FORMAT_NATIVE_WITH_ALPHA = LV_COLOR_FORMAT_RGB565A8,
|
||||
#elif LV_COLOR_DEPTH == 24
|
||||
LV_COLOR_FORMAT_NATIVE = LV_COLOR_FORMAT_RGB888,
|
||||
LV_COLOR_FORMAT_NATIVE_WITH_ALPHA = LV_COLOR_FORMAT_ARGB8888,
|
||||
#elif LV_COLOR_DEPTH == 32
|
||||
LV_COLOR_FORMAT_NATIVE = LV_COLOR_FORMAT_XRGB8888,
|
||||
LV_COLOR_FORMAT_NATIVE_WITH_ALPHA = LV_COLOR_FORMAT_ARGB8888,
|
||||
#else
|
||||
#error "LV_COLOR_DEPTH should be 1, 8, 16, 24 or 32"
|
||||
#endif
|
||||
|
||||
} lv_color_format_t;
|
||||
|
||||
#define LV_COLOR_FORMAT_IS_ALPHA_ONLY(cf) ((cf) >= LV_COLOR_FORMAT_A1 && (cf) <= LV_COLOR_FORMAT_A8)
|
||||
#define LV_COLOR_FORMAT_IS_INDEXED(cf) ((cf) >= LV_COLOR_FORMAT_I1 && (cf) <= LV_COLOR_FORMAT_I8)
|
||||
#define LV_COLOR_FORMAT_IS_YUV(cf) ((cf) >= LV_COLOR_FORMAT_YUV_START && (cf) <= LV_COLOR_FORMAT_YUV_END)
|
||||
#define LV_COLOR_INDEXED_PALETTE_SIZE(cf) ((cf) == LV_COLOR_FORMAT_I1 ? 2 :\
|
||||
(cf) == LV_COLOR_FORMAT_I2 ? 4 :\
|
||||
(cf) == LV_COLOR_FORMAT_I4 ? 16 :\
|
||||
(cf) == LV_COLOR_FORMAT_I8 ? 256 : 0)
|
||||
|
||||
/**********************
|
||||
* MACROS
|
||||
**********************/
|
||||
|
||||
#define LV_COLOR_MAKE(r8, g8, b8) {b8, g8, r8}
|
||||
|
||||
#define LV_OPA_MIX2(a1, a2) ((lv_opa_t)(((int32_t)(a1) * (a2)) >> 8))
|
||||
#define LV_OPA_MIX3(a1, a2, a3) ((lv_opa_t)(((int32_t)(a1) * (a2) * (a3)) >> 16))
|
||||
|
||||
/**********************
|
||||
* GLOBAL PROTOTYPES
|
||||
**********************/
|
||||
|
||||
/**
|
||||
* Get the pixel size of a color format in bits, bpp
|
||||
* @param cf a color format (`LV_COLOR_FORMAT_...`)
|
||||
* @return the pixel size in bits
|
||||
* @sa LV_COLOR_FORMAT_GET_BPP
|
||||
*/
|
||||
uint8_t lv_color_format_get_bpp(lv_color_format_t cf);
|
||||
|
||||
/**
|
||||
* Get the pixel size of a color format in bytes
|
||||
* @param cf a color format (`LV_COLOR_FORMAT_...`)
|
||||
* @return the pixel size in bytes
|
||||
* @sa LV_COLOR_FORMAT_GET_SIZE
|
||||
*/
|
||||
uint8_t lv_color_format_get_size(lv_color_format_t cf);
|
||||
|
||||
/**
|
||||
* Check if a color format has alpha channel or not
|
||||
* @param src_cf a color format (`LV_COLOR_FORMAT_...`)
|
||||
* @return true: has alpha channel; false: doesn't have alpha channel
|
||||
*/
|
||||
bool lv_color_format_has_alpha(lv_color_format_t src_cf);
|
||||
|
||||
/**
|
||||
* Create an ARGB8888 color from RGB888 + alpha
|
||||
* @param color an RGB888 color
|
||||
* @param opa the alpha value
|
||||
* @return the ARGB8888 color
|
||||
*/
|
||||
lv_color32_t lv_color_to_32(lv_color_t color, lv_opa_t opa);
|
||||
|
||||
/**
|
||||
* Convert an RGB888 color to an integer
|
||||
* @param c an RGB888 color
|
||||
* @return `c` as an integer
|
||||
*/
|
||||
uint32_t lv_color_to_int(lv_color_t c);
|
||||
|
||||
/**
|
||||
* Check if two RGB888 color are equal
|
||||
* @param c1 the first color
|
||||
* @param c2 the second color
|
||||
* @return true: equal
|
||||
*/
|
||||
bool lv_color_eq(lv_color_t c1, lv_color_t c2);
|
||||
|
||||
/**
|
||||
* Check if two ARGB8888 color are equal
|
||||
* @param c1 the first color
|
||||
* @param c2 the second color
|
||||
* @return true: equal
|
||||
*/
|
||||
bool lv_color32_eq(lv_color32_t c1, lv_color32_t c2);
|
||||
|
||||
/**
|
||||
* Create a color from 0x000000..0xffffff input
|
||||
* @param c the hex input
|
||||
* @return the color
|
||||
*/
|
||||
lv_color_t lv_color_hex(uint32_t c);
|
||||
|
||||
/**
|
||||
* Create an RGB888 color
|
||||
* @param r the red channel (0..255)
|
||||
* @param g the green channel (0..255)
|
||||
* @param b the blue channel (0..255)
|
||||
* @return the color
|
||||
*/
|
||||
lv_color_t lv_color_make(uint8_t r, uint8_t g, uint8_t b);
|
||||
|
||||
/**
|
||||
* Create an ARGB8888 color
|
||||
* @param r the red channel (0..255)
|
||||
* @param g the green channel (0..255)
|
||||
* @param b the blue channel (0..255)
|
||||
* @param a the alpha channel (0..255)
|
||||
* @return the color
|
||||
*/
|
||||
lv_color32_t lv_color32_make(uint8_t r, uint8_t g, uint8_t b, uint8_t a);
|
||||
|
||||
/**
|
||||
* Create a color from 0x000..0xfff input
|
||||
* @param c the hex input (e.g. 0x123 will be 0x112233)
|
||||
* @return the color
|
||||
*/
|
||||
lv_color_t lv_color_hex3(uint32_t c);
|
||||
|
||||
/**
|
||||
* Convert am RGB888 color to RGB565 stored in `uint16_t`
|
||||
* @param color and RGB888 color
|
||||
* @return `color` as RGB565 on `uin16_t`
|
||||
*/
|
||||
uint16_t lv_color_to_u16(lv_color_t color);
|
||||
|
||||
/**
|
||||
* Convert am RGB888 color to XRGB8888 stored in `uint32_t`
|
||||
* @param color and RGB888 color
|
||||
* @return `color` as XRGB8888 on `uin32_t` (the alpha channel is always set to 0xFF)
|
||||
*/
|
||||
uint32_t lv_color_to_u32(lv_color_t color);
|
||||
|
||||
/**
|
||||
* Mix two RGB565 colors
|
||||
* @param c1 the first color (typically the foreground color)
|
||||
* @param c2 the second color (typically the background color)
|
||||
* @param mix 0..255, or LV_OPA_0/10/20...
|
||||
* @return mix == 0: c2
|
||||
* mix == 255: c1
|
||||
* mix == 128: 0.5 x c1 + 0.5 x c2
|
||||
*/
|
||||
uint16_t LV_ATTRIBUTE_FAST_MEM lv_color_16_16_mix(uint16_t c1, uint16_t c2, uint8_t mix);
|
||||
|
||||
/**
|
||||
* Mix white to a color
|
||||
* @param c the base color
|
||||
* @param lvl the intensity of white (0: no change, 255: fully white)
|
||||
* @return the mixed color
|
||||
*/
|
||||
lv_color_t lv_color_lighten(lv_color_t c, lv_opa_t lvl);
|
||||
|
||||
/**
|
||||
* Mix black to a color
|
||||
* @param c the base color
|
||||
* @param lvl the intensity of black (0: no change, 255: fully black)
|
||||
* @return the mixed color
|
||||
*/
|
||||
lv_color_t lv_color_darken(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);
|
||||
|
||||
/*Source: https://vuetifyjs.com/en/styles/colors/#material-colors*/
|
||||
|
||||
/**
|
||||
* A helper for white color
|
||||
* @return a white color
|
||||
*/
|
||||
lv_color_t lv_color_white(void);
|
||||
|
||||
/**
|
||||
* A helper for black color
|
||||
* @return a black color
|
||||
*/
|
||||
lv_color_t lv_color_black(void);
|
||||
|
||||
void lv_color_premultiply(lv_color32_t * c);
|
||||
|
||||
void lv_color16_premultiply(lv_color16_t * c, lv_opa_t a);
|
||||
|
||||
/**
|
||||
* Get the luminance of a color: luminance = 0.3 R + 0.59 G + 0.11 B
|
||||
* @param c a color
|
||||
* @return the brightness [0..255]
|
||||
*/
|
||||
uint8_t lv_color_luminance(lv_color_t c);
|
||||
|
||||
/**
|
||||
* Get the luminance of a color16: luminance = 0.3 R + 0.59 G + 0.11 B
|
||||
* @param c a color
|
||||
* @return the brightness [0..255]
|
||||
*/
|
||||
uint8_t lv_color16_luminance(const lv_color16_t c);
|
||||
|
||||
/**
|
||||
* Get the luminance of a color24: luminance = 0.3 R + 0.59 G + 0.11 B
|
||||
* @param c a color
|
||||
* @return the brightness [0..255]
|
||||
*/
|
||||
uint8_t lv_color24_luminance(const uint8_t * c);
|
||||
|
||||
/**
|
||||
* Get the luminance of a color32: luminance = 0.3 R + 0.59 G + 0.11 B
|
||||
* @param c a color
|
||||
* @return the brightness [0..255]
|
||||
*/
|
||||
uint8_t lv_color32_luminance(lv_color32_t c);
|
||||
|
||||
|
||||
/**
|
||||
* Swap the endianness of an rgb565 color
|
||||
* @param c a color
|
||||
* @return the swapped color
|
||||
*/
|
||||
static inline uint16_t LV_ATTRIBUTE_FAST_MEM lv_color_swap_16(uint16_t c)
|
||||
{
|
||||
return (c >> 8) | (c << 8);
|
||||
}
|
||||
|
||||
/**********************
|
||||
* MACROS
|
||||
**********************/
|
||||
|
||||
#include "lv_palette.h"
|
||||
#include "lv_color_op.h"
|
||||
|
||||
LV_ATTRIBUTE_EXTERN_DATA extern const lv_color_filter_dsc_t lv_color_filter_shade;
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /*extern "C"*/
|
||||
#endif
|
||||
|
||||
#endif /*LV_COLOR_H*/
|
||||
@@ -0,0 +1,114 @@
|
||||
/**
|
||||
* @file lv_color.c
|
||||
*
|
||||
*/
|
||||
|
||||
/*********************
|
||||
* INCLUDES
|
||||
*********************/
|
||||
#include "lv_color_op_private.h"
|
||||
#include "lv_log.h"
|
||||
|
||||
/*********************
|
||||
* DEFINES
|
||||
*********************/
|
||||
|
||||
/**********************
|
||||
* TYPEDEFS
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* STATIC PROTOTYPES
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* STATIC VARIABLES
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* MACROS
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* GLOBAL FUNCTIONS
|
||||
**********************/
|
||||
|
||||
lv_color_t LV_ATTRIBUTE_FAST_MEM lv_color_mix(lv_color_t c1, lv_color_t c2, uint8_t mix)
|
||||
{
|
||||
lv_color_t ret;
|
||||
|
||||
ret.red = LV_UDIV255((uint16_t)c1.red * mix + c2.red * (255 - mix) + LV_COLOR_MIX_ROUND_OFS);
|
||||
ret.green = LV_UDIV255((uint16_t)c1.green * mix + c2.green * (255 - mix) + LV_COLOR_MIX_ROUND_OFS);
|
||||
ret.blue = LV_UDIV255((uint16_t)c1.blue * mix + c2.blue * (255 - mix) + LV_COLOR_MIX_ROUND_OFS);
|
||||
return ret;
|
||||
}
|
||||
|
||||
lv_color32_t lv_color_mix32(lv_color32_t fg, lv_color32_t bg)
|
||||
{
|
||||
if(fg.alpha >= LV_OPA_MAX) {
|
||||
fg.alpha = bg.alpha;
|
||||
return fg;
|
||||
}
|
||||
if(fg.alpha <= LV_OPA_MIN) {
|
||||
return bg;
|
||||
}
|
||||
bg.red = LV_UDIV255((uint32_t)((uint32_t)fg.red * fg.alpha + (uint32_t)bg.red * (255 - fg.alpha)));
|
||||
bg.green = LV_UDIV255((uint32_t)((uint32_t)fg.green * fg.alpha + (uint32_t)bg.green * (255 - fg.alpha)));
|
||||
bg.blue = LV_UDIV255((uint32_t)((uint32_t)fg.blue * fg.alpha + (uint32_t)bg.blue * (255 - fg.alpha)));
|
||||
return bg;
|
||||
}
|
||||
|
||||
lv_color32_t lv_color_mix32_premultiplied(lv_color32_t fg, lv_color32_t bg)
|
||||
{
|
||||
if(fg.alpha >= LV_OPA_MAX) {
|
||||
return fg; /* Fully opaque foreground replaces background */
|
||||
}
|
||||
if(fg.alpha <= LV_OPA_MIN) {
|
||||
return bg; /* Fully transparent foreground, return background */
|
||||
}
|
||||
|
||||
uint32_t inv_fg_alpha = LV_OPA_MAX - fg.alpha;
|
||||
|
||||
/* Premultiplied blending */
|
||||
bg.red = fg.red + ((bg.red * inv_fg_alpha) >> 8);
|
||||
bg.green = fg.green + ((bg.green * inv_fg_alpha) >> 8);
|
||||
bg.blue = fg.blue + ((bg.blue * inv_fg_alpha) >> 8);
|
||||
|
||||
return bg;
|
||||
}
|
||||
|
||||
uint8_t lv_color_brightness(lv_color_t c)
|
||||
{
|
||||
uint16_t bright = (uint16_t)(3u * c.red + c.green + 4u * c.blue);
|
||||
return (uint8_t)(bright >> 3);
|
||||
}
|
||||
|
||||
void lv_color_filter_dsc_init(lv_color_filter_dsc_t * dsc, lv_color_filter_cb_t cb)
|
||||
{
|
||||
dsc->filter_cb = cb;
|
||||
}
|
||||
|
||||
lv_color32_t lv_color_over32(lv_color32_t fg, lv_color32_t bg)
|
||||
{
|
||||
if(fg.alpha >= LV_OPA_MAX || bg.alpha <= LV_OPA_MIN) {
|
||||
return fg;
|
||||
}
|
||||
else if(fg.alpha <= LV_OPA_MIN) {
|
||||
return bg;
|
||||
}
|
||||
else if(bg.alpha == 255) {
|
||||
return lv_color_mix32(fg, bg);
|
||||
}
|
||||
|
||||
lv_opa_t res_alpha = 255 - LV_OPA_MIX2(255 - fg.alpha, 255 - bg.alpha);
|
||||
lv_opa_t ratio = (uint32_t)((uint32_t)fg.alpha * 255) / res_alpha;
|
||||
fg.alpha = ratio;
|
||||
lv_color32_t res = lv_color_mix32(fg, bg);
|
||||
res.alpha = res_alpha;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
/**********************
|
||||
* STATIC FUNCTIONS
|
||||
**********************/
|
||||
@@ -0,0 +1,105 @@
|
||||
/**
|
||||
* @file lv_color_op.h
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef LV_COLOR_OP_H
|
||||
#define LV_COLOR_OP_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*********************
|
||||
* INCLUDES
|
||||
*********************/
|
||||
#include "lv_assert.h"
|
||||
#include "lv_math.h"
|
||||
#include "lv_color.h"
|
||||
#include "lv_types.h"
|
||||
|
||||
/*********************
|
||||
* DEFINES
|
||||
*********************/
|
||||
|
||||
/**********************
|
||||
* TYPEDEFS
|
||||
**********************/
|
||||
|
||||
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);
|
||||
|
||||
struct _lv_color_filter_dsc_t {
|
||||
lv_color_filter_cb_t filter_cb;
|
||||
void * user_data;
|
||||
};
|
||||
|
||||
/**********************
|
||||
* GLOBAL PROTOTYPES
|
||||
**********************/
|
||||
|
||||
/**
|
||||
* 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_color_t LV_ATTRIBUTE_FAST_MEM lv_color_mix(lv_color_t c1, lv_color_t c2, uint8_t mix);
|
||||
|
||||
/**
|
||||
*
|
||||
* @param fg
|
||||
* @param bg
|
||||
* @return
|
||||
* @note Use bg.alpha in the return value
|
||||
* @note Use fg.alpha as mix ratio
|
||||
*/
|
||||
lv_color32_t lv_color_mix32(lv_color32_t fg, lv_color32_t bg);
|
||||
|
||||
/**
|
||||
* @brief Blends two premultiplied ARGB8888 colors while maintaining correct alpha compositing.
|
||||
*
|
||||
* This function correctly blends the foreground (fg) and background (bg) colors,
|
||||
* ensuring that the output remains in a premultiplied alpha format.
|
||||
*
|
||||
* @param fg The foreground color in premultiplied ARGB8888 format.
|
||||
* @param bg The background color in premultiplied ARGB8888 format.
|
||||
* @return The resulting blended color in premultiplied ARGB8888 format.
|
||||
*
|
||||
* @note If the foreground is fully opaque, it is returned as is.
|
||||
* @note If the foreground is fully transparent, the background is returned.
|
||||
*/
|
||||
lv_color32_t lv_color_mix32_premultiplied(lv_color32_t fg, lv_color32_t bg);
|
||||
|
||||
/**
|
||||
* Get the brightness of a color
|
||||
* @param c a color
|
||||
* @return brightness in range [0..255]
|
||||
*/
|
||||
uint8_t lv_color_brightness(lv_color_t c);
|
||||
|
||||
void lv_color_filter_dsc_init(lv_color_filter_dsc_t * dsc, lv_color_filter_cb_t cb);
|
||||
|
||||
/**
|
||||
* Blend two colors that have not been pre-multiplied using their alpha values
|
||||
* @param fg the foreground color
|
||||
* @param bg the background color
|
||||
* @return result color
|
||||
*/
|
||||
lv_color32_t lv_color_over32(lv_color32_t fg, lv_color32_t bg);
|
||||
|
||||
/**********************
|
||||
* PREDEFINED COLORS
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* MACROS
|
||||
**********************/
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /*extern "C"*/
|
||||
#endif
|
||||
|
||||
#endif /*LV_COLOR_H*/
|
||||
@@ -0,0 +1,39 @@
|
||||
/**
|
||||
* @file lv_color_op_private.h
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef LV_COLOR_OP_PRIVATE_H
|
||||
#define LV_COLOR_OP_PRIVATE_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*********************
|
||||
* INCLUDES
|
||||
*********************/
|
||||
|
||||
#include "lv_color_op.h"
|
||||
|
||||
/*********************
|
||||
* DEFINES
|
||||
*********************/
|
||||
|
||||
/**********************
|
||||
* TYPEDEFS
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* GLOBAL PROTOTYPES
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* MACROS
|
||||
**********************/
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /*extern "C"*/
|
||||
#endif
|
||||
|
||||
#endif /*LV_COLOR_OP_PRIVATE_H*/
|
||||
@@ -0,0 +1,404 @@
|
||||
/**
|
||||
* @file lv_event.c
|
||||
*
|
||||
*/
|
||||
|
||||
/*********************
|
||||
* INCLUDES
|
||||
*********************/
|
||||
#include "lv_event_private.h"
|
||||
#include "../core/lv_global.h"
|
||||
#include "../stdlib/lv_mem.h"
|
||||
#include "lv_assert.h"
|
||||
#include "lv_types.h"
|
||||
|
||||
/*********************
|
||||
* DEFINES
|
||||
*********************/
|
||||
|
||||
#define event_head LV_GLOBAL_DEFAULT()->event_header
|
||||
#define event_last_id LV_GLOBAL_DEFAULT()->event_last_register_id
|
||||
|
||||
/**********************
|
||||
* TYPEDEFS
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* STATIC PROTOTYPES
|
||||
**********************/
|
||||
|
||||
/* Traverse the list to delete the objects marked for deletion */
|
||||
static void cleanup_event_list(lv_event_list_t * list);
|
||||
static void cleanup_event_list_core(lv_array_t * array);
|
||||
|
||||
static void event_mark_deleting(lv_event_list_t * list, lv_event_dsc_t * dsc);
|
||||
static bool event_is_marked_deleting(lv_event_dsc_t * dsc);
|
||||
static uint32_t event_array_size(lv_event_list_t * list);
|
||||
static lv_event_dsc_t ** event_array_at(lv_event_list_t * list, uint32_t index);
|
||||
|
||||
/**********************
|
||||
* STATIC VARIABLES
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* MACROS
|
||||
**********************/
|
||||
|
||||
#if LV_USE_LOG && LV_LOG_TRACE_EVENT
|
||||
#define LV_TRACE_EVENT(...) LV_LOG_TRACE(__VA_ARGS__)
|
||||
#else
|
||||
#define LV_TRACE_EVENT(...)
|
||||
#endif
|
||||
|
||||
/**********************
|
||||
* GLOBAL FUNCTIONS
|
||||
**********************/
|
||||
|
||||
void lv_event_push(lv_event_t * e)
|
||||
{
|
||||
/*Build a simple linked list from the objects used in the events
|
||||
*It's important to know if this object was deleted by a nested event
|
||||
*called from this `event_cb`.*/
|
||||
e->prev = event_head;
|
||||
event_head = e;
|
||||
|
||||
}
|
||||
|
||||
void lv_event_pop(lv_event_t * e)
|
||||
{
|
||||
event_head = e->prev;
|
||||
}
|
||||
|
||||
lv_result_t lv_event_send(lv_event_list_t * list, lv_event_t * e, bool preprocess)
|
||||
{
|
||||
if(list == NULL) return LV_RESULT_OK;
|
||||
if(e->deleted) return LV_RESULT_INVALID;
|
||||
|
||||
/* When obj is deleted in its own event, it will cause the `list->array` header to be released,
|
||||
* but the content still exists, which leads to memory leakage.
|
||||
* Therefore, back up the header in advance,
|
||||
* which can strive to release the memory and prevent used-after-free. */
|
||||
lv_array_t back_array_head = list->array;
|
||||
|
||||
/* Dealing with the problem of nested event deletion event */
|
||||
const bool is_traversing = list->is_traversing;
|
||||
list->is_traversing = true;
|
||||
|
||||
lv_result_t res = LV_RESULT_OK;
|
||||
const uint32_t size = event_array_size(list);
|
||||
for(uint32_t i = 0; i < size && !e->deleted; i++) {
|
||||
lv_event_dsc_t * dsc = *event_array_at(list, i);
|
||||
if(dsc->cb == NULL) continue;
|
||||
if(event_is_marked_deleting(dsc)) continue;
|
||||
const bool is_preprocessed = (dsc->filter & LV_EVENT_PREPROCESS) != 0;
|
||||
if(is_preprocessed != preprocess) continue;
|
||||
lv_event_code_t filter = dsc->filter & ~LV_EVENT_PREPROCESS;
|
||||
if(filter == LV_EVENT_ALL || filter == e->code) {
|
||||
e->user_data = dsc->user_data;
|
||||
dsc->cb(e);
|
||||
if(e->stop_processing) break;
|
||||
|
||||
/*Stop if the object is deleted*/
|
||||
if(e->deleted) {
|
||||
res = LV_RESULT_INVALID;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(is_traversing) return res;
|
||||
|
||||
if(e->deleted) cleanup_event_list_core(&back_array_head);
|
||||
else {
|
||||
list->is_traversing = false;
|
||||
cleanup_event_list(list);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
lv_event_dsc_t * lv_event_add(lv_event_list_t * list, lv_event_cb_t cb, lv_event_code_t filter,
|
||||
void * user_data)
|
||||
{
|
||||
lv_event_dsc_t * dsc = lv_malloc(sizeof(lv_event_dsc_t));
|
||||
LV_ASSERT_NULL(dsc);
|
||||
|
||||
dsc->cb = cb;
|
||||
dsc->filter = filter;
|
||||
dsc->user_data = user_data;
|
||||
|
||||
if(event_array_size(list) == 0) {
|
||||
/*event list hasn't been initialized.*/
|
||||
lv_array_init(&list->array, 1, sizeof(lv_event_dsc_t *));
|
||||
}
|
||||
|
||||
lv_array_push_back(&list->array, &dsc);
|
||||
return dsc;
|
||||
}
|
||||
|
||||
bool lv_event_remove_dsc(lv_event_list_t * list, lv_event_dsc_t * dsc)
|
||||
{
|
||||
LV_ASSERT_NULL(list);
|
||||
LV_ASSERT_NULL(dsc);
|
||||
|
||||
const uint32_t size = event_array_size(list);
|
||||
for(uint32_t i = 0; i < size; i++) {
|
||||
lv_event_dsc_t * event = *event_array_at(list, i);
|
||||
if(event == dsc) {
|
||||
event_mark_deleting(list, event);
|
||||
cleanup_event_list(list);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t lv_event_get_count(lv_event_list_t * list)
|
||||
{
|
||||
LV_ASSERT_NULL(list);
|
||||
return event_array_size(list);
|
||||
}
|
||||
|
||||
lv_event_dsc_t * lv_event_get_dsc(lv_event_list_t * list, uint32_t index)
|
||||
{
|
||||
LV_ASSERT_NULL(list);
|
||||
lv_event_dsc_t ** dsc = event_array_at(list, index);
|
||||
return dsc ? *dsc : NULL;
|
||||
}
|
||||
|
||||
lv_event_cb_t lv_event_dsc_get_cb(lv_event_dsc_t * dsc)
|
||||
{
|
||||
LV_ASSERT_NULL(dsc);
|
||||
return dsc->cb;
|
||||
}
|
||||
|
||||
void * lv_event_dsc_get_user_data(lv_event_dsc_t * dsc)
|
||||
{
|
||||
LV_ASSERT_NULL(dsc);
|
||||
return dsc->user_data;
|
||||
|
||||
}
|
||||
|
||||
bool lv_event_remove(lv_event_list_t * list, uint32_t index)
|
||||
{
|
||||
LV_ASSERT_NULL(list);
|
||||
lv_event_dsc_t * dsc = lv_event_get_dsc(list, index);
|
||||
if(dsc == NULL) return false;
|
||||
event_mark_deleting(list, dsc);
|
||||
cleanup_event_list(list);
|
||||
return true;
|
||||
}
|
||||
|
||||
void lv_event_remove_all(lv_event_list_t * list)
|
||||
{
|
||||
LV_ASSERT_NULL(list);
|
||||
const uint32_t size = event_array_size(list);
|
||||
for(uint32_t i = 0; i < size; i++)
|
||||
event_mark_deleting(list, *event_array_at(list, i));
|
||||
cleanup_event_list(list);
|
||||
}
|
||||
|
||||
void * lv_event_get_current_target(lv_event_t * e)
|
||||
{
|
||||
return e->current_target;
|
||||
}
|
||||
|
||||
void * lv_event_get_target(lv_event_t * e)
|
||||
{
|
||||
return e->original_target;
|
||||
}
|
||||
|
||||
lv_event_code_t lv_event_get_code(lv_event_t * e)
|
||||
{
|
||||
return e->code & ~LV_EVENT_PREPROCESS;
|
||||
}
|
||||
|
||||
void * lv_event_get_param(lv_event_t * e)
|
||||
{
|
||||
return e->param;
|
||||
}
|
||||
|
||||
void * lv_event_get_user_data(lv_event_t * e)
|
||||
{
|
||||
return e->user_data;
|
||||
}
|
||||
|
||||
void lv_event_stop_bubbling(lv_event_t * e)
|
||||
{
|
||||
e->stop_bubbling = 1;
|
||||
}
|
||||
|
||||
void lv_event_stop_processing(lv_event_t * e)
|
||||
{
|
||||
e->stop_processing = 1;
|
||||
}
|
||||
|
||||
uint32_t lv_event_register_id(void)
|
||||
{
|
||||
event_last_id ++;
|
||||
return event_last_id;
|
||||
}
|
||||
|
||||
void lv_event_mark_deleted(void * target)
|
||||
{
|
||||
lv_event_t * e = event_head;
|
||||
|
||||
while(e) {
|
||||
if(e->original_target == target || e->current_target == target) e->deleted = 1;
|
||||
e = e->prev;
|
||||
}
|
||||
}
|
||||
|
||||
const char * lv_event_code_get_name(lv_event_code_t code)
|
||||
{
|
||||
/*Remove the preprocess flag*/
|
||||
code &= ~LV_EVENT_PREPROCESS;
|
||||
|
||||
#define ENUM_CASE(x) case LV_##x: return #x
|
||||
|
||||
switch(code) {
|
||||
ENUM_CASE(EVENT_ALL);
|
||||
|
||||
/** Input device events*/
|
||||
ENUM_CASE(EVENT_PRESSED);
|
||||
ENUM_CASE(EVENT_PRESSING);
|
||||
ENUM_CASE(EVENT_PRESS_LOST);
|
||||
ENUM_CASE(EVENT_SHORT_CLICKED);
|
||||
ENUM_CASE(EVENT_SINGLE_CLICKED);
|
||||
ENUM_CASE(EVENT_DOUBLE_CLICKED);
|
||||
ENUM_CASE(EVENT_TRIPLE_CLICKED);
|
||||
ENUM_CASE(EVENT_LONG_PRESSED);
|
||||
ENUM_CASE(EVENT_LONG_PRESSED_REPEAT);
|
||||
ENUM_CASE(EVENT_CLICKED);
|
||||
ENUM_CASE(EVENT_RELEASED);
|
||||
ENUM_CASE(EVENT_SCROLL_BEGIN);
|
||||
ENUM_CASE(EVENT_SCROLL_THROW_BEGIN);
|
||||
ENUM_CASE(EVENT_SCROLL_END);
|
||||
ENUM_CASE(EVENT_SCROLL);
|
||||
ENUM_CASE(EVENT_GESTURE);
|
||||
ENUM_CASE(EVENT_KEY);
|
||||
ENUM_CASE(EVENT_ROTARY);
|
||||
ENUM_CASE(EVENT_FOCUSED);
|
||||
ENUM_CASE(EVENT_DEFOCUSED);
|
||||
ENUM_CASE(EVENT_LEAVE);
|
||||
ENUM_CASE(EVENT_HIT_TEST);
|
||||
ENUM_CASE(EVENT_INDEV_RESET);
|
||||
ENUM_CASE(EVENT_HOVER_OVER);
|
||||
ENUM_CASE(EVENT_HOVER_LEAVE);
|
||||
|
||||
/** Drawing events*/
|
||||
ENUM_CASE(EVENT_COVER_CHECK);
|
||||
ENUM_CASE(EVENT_REFR_EXT_DRAW_SIZE);
|
||||
ENUM_CASE(EVENT_DRAW_MAIN_BEGIN);
|
||||
ENUM_CASE(EVENT_DRAW_MAIN);
|
||||
ENUM_CASE(EVENT_DRAW_MAIN_END);
|
||||
ENUM_CASE(EVENT_DRAW_POST_BEGIN);
|
||||
ENUM_CASE(EVENT_DRAW_POST);
|
||||
ENUM_CASE(EVENT_DRAW_POST_END);
|
||||
ENUM_CASE(EVENT_DRAW_TASK_ADDED);
|
||||
|
||||
/** Special events*/
|
||||
ENUM_CASE(EVENT_VALUE_CHANGED);
|
||||
ENUM_CASE(EVENT_INSERT);
|
||||
ENUM_CASE(EVENT_REFRESH);
|
||||
ENUM_CASE(EVENT_READY);
|
||||
ENUM_CASE(EVENT_CANCEL);
|
||||
|
||||
/** Other events*/
|
||||
ENUM_CASE(EVENT_CREATE);
|
||||
ENUM_CASE(EVENT_DELETE);
|
||||
ENUM_CASE(EVENT_CHILD_CHANGED);
|
||||
ENUM_CASE(EVENT_CHILD_CREATED);
|
||||
ENUM_CASE(EVENT_CHILD_DELETED);
|
||||
ENUM_CASE(EVENT_SCREEN_UNLOAD_START);
|
||||
ENUM_CASE(EVENT_SCREEN_LOAD_START);
|
||||
ENUM_CASE(EVENT_SCREEN_LOADED);
|
||||
ENUM_CASE(EVENT_SCREEN_UNLOADED);
|
||||
ENUM_CASE(EVENT_SIZE_CHANGED);
|
||||
ENUM_CASE(EVENT_STYLE_CHANGED);
|
||||
ENUM_CASE(EVENT_LAYOUT_CHANGED);
|
||||
ENUM_CASE(EVENT_GET_SELF_SIZE);
|
||||
|
||||
/** Events of optional LVGL components*/
|
||||
ENUM_CASE(EVENT_INVALIDATE_AREA);
|
||||
ENUM_CASE(EVENT_RESOLUTION_CHANGED);
|
||||
ENUM_CASE(EVENT_COLOR_FORMAT_CHANGED);
|
||||
ENUM_CASE(EVENT_REFR_REQUEST);
|
||||
ENUM_CASE(EVENT_REFR_START);
|
||||
ENUM_CASE(EVENT_REFR_READY);
|
||||
ENUM_CASE(EVENT_RENDER_START);
|
||||
ENUM_CASE(EVENT_RENDER_READY);
|
||||
ENUM_CASE(EVENT_FLUSH_START);
|
||||
ENUM_CASE(EVENT_FLUSH_FINISH);
|
||||
ENUM_CASE(EVENT_FLUSH_WAIT_START);
|
||||
ENUM_CASE(EVENT_FLUSH_WAIT_FINISH);
|
||||
|
||||
ENUM_CASE(EVENT_VSYNC);
|
||||
ENUM_CASE(EVENT_VSYNC_REQUEST);
|
||||
|
||||
/* Special event flags */
|
||||
case LV_EVENT_LAST:
|
||||
case LV_EVENT_PREPROCESS:
|
||||
case LV_EVENT_MARKED_DELETING:
|
||||
break;
|
||||
|
||||
/* Note that default is not added here because when adding new event code,
|
||||
* if forget to add case, the compiler will automatically report a warning.
|
||||
*/
|
||||
}
|
||||
|
||||
#undef ENUM_CASE
|
||||
|
||||
return "EVENT_UNKNOWN";
|
||||
}
|
||||
|
||||
/**********************
|
||||
* STATIC FUNCTIONS
|
||||
**********************/
|
||||
|
||||
static void cleanup_event_list_core(lv_array_t * array)
|
||||
{
|
||||
const uint32_t size = lv_array_size(array);
|
||||
uint32_t kept_count = 0;
|
||||
for(uint32_t i = 0; i < size; i++) {
|
||||
lv_event_dsc_t ** dsc_i = lv_array_at(array, i);
|
||||
lv_event_dsc_t ** dsc_kept = lv_array_at(array, kept_count);
|
||||
if(event_is_marked_deleting(*dsc_i)) lv_free(*dsc_i);
|
||||
else {
|
||||
*dsc_kept = *dsc_i;
|
||||
kept_count++;
|
||||
}
|
||||
}
|
||||
|
||||
if(kept_count == 0) lv_array_deinit(array);
|
||||
else lv_array_resize(array, kept_count);
|
||||
}
|
||||
|
||||
static void cleanup_event_list(lv_event_list_t * list)
|
||||
{
|
||||
if(list->is_traversing) return;
|
||||
if(list->has_marked_deleting == false) return;
|
||||
|
||||
cleanup_event_list_core(&list->array);
|
||||
|
||||
list->has_marked_deleting = false;
|
||||
}
|
||||
|
||||
static void event_mark_deleting(lv_event_list_t * list, lv_event_dsc_t * dsc)
|
||||
{
|
||||
list->has_marked_deleting = true;
|
||||
dsc->filter |= LV_EVENT_MARKED_DELETING;
|
||||
}
|
||||
static bool event_is_marked_deleting(lv_event_dsc_t * dsc)
|
||||
{
|
||||
return (dsc->filter & LV_EVENT_MARKED_DELETING) != 0;
|
||||
}
|
||||
static uint32_t event_array_size(lv_event_list_t * list)
|
||||
{
|
||||
return lv_array_size(&list->array);
|
||||
}
|
||||
static lv_event_dsc_t ** event_array_at(lv_event_list_t * list, uint32_t index)
|
||||
{
|
||||
return lv_array_at(&list->array, index);
|
||||
}
|
||||
@@ -0,0 +1,236 @@
|
||||
/**
|
||||
* @file lv_event.h
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef LV_EVENT_H
|
||||
#define LV_EVENT_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*********************
|
||||
* INCLUDES
|
||||
*********************/
|
||||
#include "lv_types.h"
|
||||
#include "../lv_conf_internal.h"
|
||||
|
||||
#include "lv_array.h"
|
||||
|
||||
/*********************
|
||||
* DEFINES
|
||||
*********************/
|
||||
|
||||
/**********************
|
||||
* TYPEDEFS
|
||||
**********************/
|
||||
|
||||
typedef void (*lv_event_cb_t)(lv_event_t * e);
|
||||
|
||||
/**
|
||||
* Type of event being sent to Widget
|
||||
*/
|
||||
typedef enum {
|
||||
LV_EVENT_ALL = 0,
|
||||
|
||||
/** Input device events*/
|
||||
LV_EVENT_PRESSED, /**< Widget has been pressed */
|
||||
LV_EVENT_PRESSING, /**< Widget is being pressed (sent continuously while pressing)*/
|
||||
LV_EVENT_PRESS_LOST, /**< Widget is still being pressed but slid cursor/finger off Widget */
|
||||
LV_EVENT_SHORT_CLICKED, /**< Widget was pressed for a short period of time, then released. Not sent if scrolled. */
|
||||
LV_EVENT_SINGLE_CLICKED, /**< Sent for first short click within a small distance and short time */
|
||||
LV_EVENT_DOUBLE_CLICKED, /**< Sent for second short click within small distance and short time */
|
||||
LV_EVENT_TRIPLE_CLICKED, /**< Sent for third short click within small distance and short time */
|
||||
LV_EVENT_LONG_PRESSED, /**< Object has been pressed for at least `long_press_time`. Not sent if scrolled. */
|
||||
LV_EVENT_LONG_PRESSED_REPEAT, /**< Sent after `long_press_time` in every `long_press_repeat_time` ms. Not sent if scrolled. */
|
||||
LV_EVENT_CLICKED, /**< Sent on release if not scrolled (regardless to long press)*/
|
||||
LV_EVENT_RELEASED, /**< Sent in every cases when Widget has been released */
|
||||
LV_EVENT_SCROLL_BEGIN, /**< Scrolling begins. The event parameter is a pointer to the animation of the scroll. Can be modified */
|
||||
LV_EVENT_SCROLL_THROW_BEGIN,
|
||||
LV_EVENT_SCROLL_END, /**< Scrolling ends */
|
||||
LV_EVENT_SCROLL, /**< Scrolling */
|
||||
LV_EVENT_GESTURE, /**< A gesture is detected. Get gesture with `lv_indev_get_gesture_dir(lv_indev_active());` */
|
||||
LV_EVENT_KEY, /**< A key is sent to Widget. Get key with `lv_indev_get_key(lv_indev_active());`*/
|
||||
LV_EVENT_ROTARY, /**< An encoder or wheel was rotated. Get rotation count with `lv_event_get_rotary_diff(e);`*/
|
||||
LV_EVENT_FOCUSED, /**< Widget received focus */
|
||||
LV_EVENT_DEFOCUSED, /**< Widget's focus has been lost */
|
||||
LV_EVENT_LEAVE, /**< Widget's focus has been lost but is still selected */
|
||||
LV_EVENT_HIT_TEST, /**< Perform advanced hit-testing */
|
||||
LV_EVENT_INDEV_RESET, /**< Indev has been reset */
|
||||
LV_EVENT_HOVER_OVER, /**< Indev hover over object */
|
||||
LV_EVENT_HOVER_LEAVE, /**< Indev hover leave object */
|
||||
|
||||
/** Drawing events */
|
||||
LV_EVENT_COVER_CHECK, /**< Check if Widget fully covers an area. The event parameter is `lv_cover_check_info_t *`. */
|
||||
LV_EVENT_REFR_EXT_DRAW_SIZE, /**< Get required extra draw area around Widget (e.g. for shadow). The event parameter is `int32_t *` to store the size. */
|
||||
LV_EVENT_DRAW_MAIN_BEGIN, /**< Starting the main drawing phase */
|
||||
LV_EVENT_DRAW_MAIN, /**< Perform the main drawing */
|
||||
LV_EVENT_DRAW_MAIN_END, /**< Finishing the main drawing phase */
|
||||
LV_EVENT_DRAW_POST_BEGIN, /**< Starting the post draw phase (when all children are drawn)*/
|
||||
LV_EVENT_DRAW_POST, /**< Perform the post draw phase (when all children are drawn)*/
|
||||
LV_EVENT_DRAW_POST_END, /**< Finishing the post draw phase (when all children are drawn)*/
|
||||
LV_EVENT_DRAW_TASK_ADDED, /**< Adding a draw task. The `LV_OBJ_FLAG_SEND_DRAW_TASK_EVENTS` flag needs to be set */
|
||||
|
||||
/** Special events */
|
||||
LV_EVENT_VALUE_CHANGED, /**< Widget's value has changed (i.e. slider moved)*/
|
||||
LV_EVENT_INSERT, /**< Text has been inserted into Widget. The event data is `char *` being inserted. */
|
||||
LV_EVENT_REFRESH, /**< Notify Widget to refresh something on it (for user)*/
|
||||
LV_EVENT_READY, /**< A process has finished */
|
||||
LV_EVENT_CANCEL, /**< A process has been cancelled */
|
||||
|
||||
/** Other events */
|
||||
LV_EVENT_CREATE, /**< Object is being created */
|
||||
LV_EVENT_DELETE, /**< Object is being deleted */
|
||||
LV_EVENT_CHILD_CHANGED, /**< Child was removed, added, or its size, position were changed */
|
||||
LV_EVENT_CHILD_CREATED, /**< Child was created, always bubbles up to all parents */
|
||||
LV_EVENT_CHILD_DELETED, /**< Child was deleted, always bubbles up to all parents */
|
||||
LV_EVENT_SCREEN_UNLOAD_START, /**< A screen unload started, fired immediately when scr_load is called */
|
||||
LV_EVENT_SCREEN_LOAD_START, /**< A screen load started, fired when the screen change delay is expired */
|
||||
LV_EVENT_SCREEN_LOADED, /**< A screen was loaded */
|
||||
LV_EVENT_SCREEN_UNLOADED, /**< A screen was unloaded */
|
||||
LV_EVENT_SIZE_CHANGED, /**< Object coordinates/size have changed */
|
||||
LV_EVENT_STYLE_CHANGED, /**< Object's style has changed */
|
||||
LV_EVENT_LAYOUT_CHANGED, /**< A child's position position has changed due to a layout recalculation */
|
||||
LV_EVENT_GET_SELF_SIZE, /**< Get internal size of a widget */
|
||||
|
||||
/** Events of optional LVGL components */
|
||||
LV_EVENT_INVALIDATE_AREA, /**< An area is invalidated (marked for redraw). `lv_event_get_param(e)`
|
||||
* returns a pointer to an `lv_area_t` object with the coordinates of the
|
||||
* area to be invalidated. The area can be freely modified if needed to
|
||||
* adapt it a special requirement of the display. Usually needed with
|
||||
* monochrome displays to invalidate `N x 8` rows or columns in one pass. */
|
||||
LV_EVENT_RESOLUTION_CHANGED, /**< Sent when the resolution changes due to `lv_display_set_resolution()` or `lv_display_set_rotation()`. */
|
||||
LV_EVENT_COLOR_FORMAT_CHANGED,/**< Sent as a result of any call to `lv_display_set_color_format()`. */
|
||||
LV_EVENT_REFR_REQUEST, /**< Sent when something happened that requires redraw. */
|
||||
LV_EVENT_REFR_START, /**< Sent before a refreshing cycle starts. Sent even if there is nothing to redraw. */
|
||||
LV_EVENT_REFR_READY, /**< Sent when refreshing has been completed (after rendering and calling flush callback). Sent even if no redraw happened. */
|
||||
LV_EVENT_RENDER_START, /**< Sent just before rendering begins. */
|
||||
LV_EVENT_RENDER_READY, /**< Sent after rendering has been completed (before calling flush callback) */
|
||||
LV_EVENT_FLUSH_START, /**< Sent before flush callback is called. */
|
||||
LV_EVENT_FLUSH_FINISH, /**< Sent after flush callback call has returned. */
|
||||
LV_EVENT_FLUSH_WAIT_START, /**< Sent before flush wait callback is called. */
|
||||
LV_EVENT_FLUSH_WAIT_FINISH, /**< Sent after flush wait callback call has returned. */
|
||||
|
||||
LV_EVENT_VSYNC,
|
||||
LV_EVENT_VSYNC_REQUEST,
|
||||
|
||||
LV_EVENT_LAST, /** Number of default events */
|
||||
|
||||
LV_EVENT_PREPROCESS = 0x8000, /** This is a flag that can be set with an event so it's processed
|
||||
before the class default event processing */
|
||||
LV_EVENT_MARKED_DELETING = 0x10000,
|
||||
} lv_event_code_t;
|
||||
|
||||
typedef struct {
|
||||
lv_array_t array;
|
||||
uint8_t is_traversing: 1; /**< True: the list is being nested traversed */
|
||||
uint8_t has_marked_deleting: 1; /**< True: the list has marked deleting objects
|
||||
when some of events are marked as deleting */
|
||||
} lv_event_list_t;
|
||||
|
||||
/**
|
||||
* @brief Event callback.
|
||||
* Events are used to notify the user of some action being taken on Widget.
|
||||
* For details, see ::lv_event_t.
|
||||
*/
|
||||
|
||||
lv_result_t lv_event_send(lv_event_list_t * list, lv_event_t * e, bool preprocess);
|
||||
|
||||
lv_event_dsc_t * lv_event_add(lv_event_list_t * list, lv_event_cb_t cb, lv_event_code_t filter, void * user_data);
|
||||
bool lv_event_remove_dsc(lv_event_list_t * list, lv_event_dsc_t * dsc);
|
||||
|
||||
uint32_t lv_event_get_count(lv_event_list_t * list);
|
||||
|
||||
lv_event_dsc_t * lv_event_get_dsc(lv_event_list_t * list, uint32_t index);
|
||||
|
||||
lv_event_cb_t lv_event_dsc_get_cb(lv_event_dsc_t * dsc);
|
||||
|
||||
void * lv_event_dsc_get_user_data(lv_event_dsc_t * dsc);
|
||||
|
||||
bool lv_event_remove(lv_event_list_t * list, uint32_t index);
|
||||
|
||||
void lv_event_remove_all(lv_event_list_t * list);
|
||||
|
||||
/**
|
||||
* Get Widget originally targeted by the event. It's the same even if event was bubbled.
|
||||
* @param e pointer to the event descriptor
|
||||
* @return the target of the event_code
|
||||
*/
|
||||
void * lv_event_get_target(lv_event_t * e);
|
||||
|
||||
/**
|
||||
* Get current target of the event. It's the Widget for which the event handler being called.
|
||||
* If the event is not bubbled it's the same as "normal" target.
|
||||
* @param e pointer to the event descriptor
|
||||
* @return pointer to the current target of the event_code
|
||||
*/
|
||||
void * lv_event_get_current_target(lv_event_t * e);
|
||||
|
||||
/**
|
||||
* Get event code of an event.
|
||||
* @param e pointer to the event descriptor
|
||||
* @return the event code. (E.g. `LV_EVENT_CLICKED`, `LV_EVENT_FOCUSED`, etc)
|
||||
*/
|
||||
lv_event_code_t lv_event_get_code(lv_event_t * e);
|
||||
|
||||
/**
|
||||
* Get parameter passed when event was sent.
|
||||
* @param e pointer to the event descriptor
|
||||
* @return pointer to the parameter
|
||||
*/
|
||||
void * lv_event_get_param(lv_event_t * e);
|
||||
|
||||
/**
|
||||
* Get user_data passed when event was registered on Widget.
|
||||
* @param e pointer to the event descriptor
|
||||
* @return pointer to the user_data
|
||||
*/
|
||||
void * lv_event_get_user_data(lv_event_t * e);
|
||||
|
||||
/**
|
||||
* Stop event from bubbling.
|
||||
* This is only valid when called in the middle of an event processing chain.
|
||||
* @param e pointer to the event descriptor
|
||||
*/
|
||||
void lv_event_stop_bubbling(lv_event_t * e);
|
||||
|
||||
/**
|
||||
* Stop processing this event.
|
||||
* This is only valid when called in the middle of an event processing chain.
|
||||
* @param e pointer to the event descriptor
|
||||
*/
|
||||
void lv_event_stop_processing(lv_event_t * e);
|
||||
|
||||
/**
|
||||
* Register a new, custom event ID.
|
||||
* It can be used the same way as e.g. `LV_EVENT_CLICKED` to send custom events
|
||||
* @return the new event id
|
||||
*
|
||||
* Example:
|
||||
* @code
|
||||
* uint32_t LV_EVENT_MINE = 0;
|
||||
* ...
|
||||
* e = lv_event_register_id();
|
||||
* ...
|
||||
* lv_obj_send_event(obj, LV_EVENT_MINE, &some_data);
|
||||
* @endcode
|
||||
*/
|
||||
uint32_t lv_event_register_id(void);
|
||||
|
||||
/**
|
||||
* Get the name of an event code.
|
||||
* @param code the event code
|
||||
* @return the name of the event code as a string
|
||||
*/
|
||||
const char * lv_event_code_get_name(lv_event_code_t code);
|
||||
|
||||
/**********************
|
||||
* MACROS
|
||||
**********************/
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /*extern "C"*/
|
||||
#endif
|
||||
|
||||
#endif /* LV_EVENT_H */
|
||||
@@ -0,0 +1,73 @@
|
||||
/**
|
||||
* @file lv_event_private.h
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef LV_EVENT_PRIVATE_H
|
||||
#define LV_EVENT_PRIVATE_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*********************
|
||||
* INCLUDES
|
||||
*********************/
|
||||
|
||||
#include "lv_event.h"
|
||||
|
||||
/*********************
|
||||
* DEFINES
|
||||
*********************/
|
||||
|
||||
/**********************
|
||||
* TYPEDEFS
|
||||
**********************/
|
||||
|
||||
struct _lv_event_dsc_t {
|
||||
lv_event_cb_t cb;
|
||||
void * user_data;
|
||||
uint32_t filter;
|
||||
};
|
||||
|
||||
struct _lv_event_t {
|
||||
void * current_target;
|
||||
void * original_target;
|
||||
lv_event_code_t code;
|
||||
void * user_data;
|
||||
void * param;
|
||||
lv_event_t * prev;
|
||||
uint8_t deleted : 1;
|
||||
uint8_t stop_processing : 1;
|
||||
uint8_t stop_bubbling : 1;
|
||||
};
|
||||
|
||||
|
||||
/**********************
|
||||
* GLOBAL PROTOTYPES
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* GLOBAL PROTOTYPES
|
||||
**********************/
|
||||
|
||||
void lv_event_push(lv_event_t * e);
|
||||
|
||||
void lv_event_pop(lv_event_t * e);
|
||||
|
||||
/**
|
||||
* Nested events can be called and one of them might belong to an object that is being deleted.
|
||||
* Mark this object's `event_temp_data` deleted to know that its `lv_obj_send_event` should return `LV_RESULT_INVALID`
|
||||
* @param target pointer to an event target which was deleted
|
||||
*/
|
||||
void lv_event_mark_deleted(void * target);
|
||||
|
||||
/**********************
|
||||
* MACROS
|
||||
**********************/
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /*extern "C"*/
|
||||
#endif
|
||||
|
||||
#endif /*LV_EVENT_PRIVATE_H*/
|
||||
@@ -0,0 +1,694 @@
|
||||
/**
|
||||
* @file lv_fs.c
|
||||
*
|
||||
*/
|
||||
|
||||
/*********************
|
||||
* INCLUDES
|
||||
*********************/
|
||||
#include "lv_fs_private.h"
|
||||
|
||||
#include "../misc/lv_assert.h"
|
||||
#include "../misc/lv_profiler.h"
|
||||
#include "../stdlib/lv_string.h"
|
||||
#include "lv_ll.h"
|
||||
#include "../core/lv_global.h"
|
||||
|
||||
/*********************
|
||||
* DEFINES
|
||||
*********************/
|
||||
|
||||
#if LV_FS_DEFAULT_DRIVER_LETTER != '\0' && (LV_FS_DEFAULT_DRIVER_LETTER < 'A' || 'Z' < LV_FS_DEFAULT_DRIVER_LETTER)
|
||||
#error "When enabled, LV_FS_DEFAULT_DRIVER_LETTER needs to be a capital ASCII letter (A-Z)"
|
||||
#endif
|
||||
|
||||
#define fsdrv_ll_p &(LV_GLOBAL_DEFAULT()->fsdrv_ll)
|
||||
|
||||
/**********************
|
||||
* TYPEDEFS
|
||||
**********************/
|
||||
typedef struct {
|
||||
char driver_letter;
|
||||
const char * real_path;
|
||||
} resolved_path_t;
|
||||
|
||||
/**********************
|
||||
* STATIC PROTOTYPES
|
||||
**********************/
|
||||
static resolved_path_t lv_fs_resolve_path(const char * path);
|
||||
static lv_fs_res_t lv_fs_read_cached(lv_fs_file_t * file_p, void * buf, uint32_t btr, uint32_t * br);
|
||||
static lv_fs_res_t lv_fs_write_cached(lv_fs_file_t * file_p, const void * buf, uint32_t btw, uint32_t * bw);
|
||||
static lv_fs_res_t lv_fs_seek_cached(lv_fs_file_t * file_p, uint32_t pos, lv_fs_whence_t whence);
|
||||
|
||||
/**********************
|
||||
* STATIC VARIABLES
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* MACROS
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* GLOBAL FUNCTIONS
|
||||
**********************/
|
||||
|
||||
void lv_fs_init(void)
|
||||
{
|
||||
lv_ll_init(fsdrv_ll_p, sizeof(lv_fs_drv_t *));
|
||||
}
|
||||
|
||||
void lv_fs_deinit(void)
|
||||
{
|
||||
lv_ll_clear(fsdrv_ll_p);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
resolved_path_t resolved_path = lv_fs_resolve_path(path);
|
||||
|
||||
lv_fs_drv_t * drv = lv_fs_get_drv(resolved_path.driver_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;
|
||||
}
|
||||
|
||||
LV_PROFILER_FS_BEGIN;
|
||||
|
||||
file_p->drv = drv;
|
||||
|
||||
/* For memory-mapped files we set the file handle to our file descriptor so that we can access the cache from the file operations */
|
||||
if(drv->cache_size == LV_FS_CACHE_FROM_BUFFER) {
|
||||
file_p->file_d = file_p;
|
||||
}
|
||||
else {
|
||||
void * file_d = drv->open_cb(drv, resolved_path.real_path, mode);
|
||||
if(file_d == NULL || file_d == (void *)(-1)) {
|
||||
LV_PROFILER_FS_END;
|
||||
return LV_FS_RES_UNKNOWN;
|
||||
}
|
||||
file_p->file_d = file_d;
|
||||
}
|
||||
|
||||
if(drv->cache_size) {
|
||||
file_p->cache = lv_malloc_zeroed(sizeof(lv_fs_file_cache_t));
|
||||
LV_ASSERT_MALLOC(file_p->cache);
|
||||
|
||||
/* If this is a memory-mapped file, then set "cache" to the memory buffer */
|
||||
if(drv->cache_size == LV_FS_CACHE_FROM_BUFFER) {
|
||||
lv_fs_path_ex_t * path_ex = (lv_fs_path_ex_t *)path;
|
||||
file_p->cache->buffer = (void *)path_ex->buffer;
|
||||
file_p->cache->start = 0;
|
||||
file_p->cache->file_position = 0;
|
||||
file_p->cache->end = path_ex->size;
|
||||
}
|
||||
/*Set an invalid range by default*/
|
||||
else {
|
||||
file_p->cache->start = UINT32_MAX;
|
||||
file_p->cache->end = UINT32_MAX - 1;
|
||||
}
|
||||
}
|
||||
|
||||
LV_PROFILER_FS_END;
|
||||
|
||||
return LV_FS_RES_OK;
|
||||
}
|
||||
|
||||
void lv_fs_make_path_from_buffer(lv_fs_path_ex_t * path, char letter, const void * buf, uint32_t size)
|
||||
{
|
||||
path->path[0] = letter;
|
||||
path->path[1] = ':';
|
||||
path->path[2] = 0;
|
||||
path->buffer = buf;
|
||||
path->size = size;
|
||||
}
|
||||
|
||||
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_PROFILER_FS_BEGIN;
|
||||
|
||||
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) {
|
||||
/* Only free cache if it was pre-allocated (for memory-mapped files it is never allocated) */
|
||||
if(file_p->drv->cache_size != LV_FS_CACHE_FROM_BUFFER && file_p->cache->buffer) {
|
||||
lv_free(file_p->cache->buffer);
|
||||
}
|
||||
|
||||
lv_free(file_p->cache);
|
||||
}
|
||||
|
||||
file_p->file_d = NULL;
|
||||
file_p->drv = NULL;
|
||||
file_p->cache = NULL;
|
||||
|
||||
LV_PROFILER_FS_END;
|
||||
|
||||
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->cache_size) {
|
||||
if(file_p->drv->read_cb == NULL || file_p->drv->seek_cb == NULL) return LV_FS_RES_NOT_IMP;
|
||||
}
|
||||
else {
|
||||
if(file_p->drv->read_cb == NULL) return LV_FS_RES_NOT_IMP;
|
||||
}
|
||||
|
||||
LV_PROFILER_FS_BEGIN;
|
||||
|
||||
uint32_t br_tmp = 0;
|
||||
lv_fs_res_t res;
|
||||
|
||||
if(file_p->drv->cache_size) {
|
||||
res = lv_fs_read_cached(file_p, 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;
|
||||
|
||||
LV_PROFILER_FS_END;
|
||||
|
||||
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->cache_size) {
|
||||
if(file_p->drv->write_cb == NULL || file_p->drv->seek_cb == NULL) return LV_FS_RES_NOT_IMP;
|
||||
}
|
||||
else {
|
||||
if(file_p->drv->write_cb == NULL) return LV_FS_RES_NOT_IMP;
|
||||
}
|
||||
|
||||
LV_PROFILER_FS_BEGIN;
|
||||
|
||||
lv_fs_res_t res;
|
||||
uint32_t bw_tmp = 0;
|
||||
if(file_p->drv->cache_size) {
|
||||
res = lv_fs_write_cached(file_p, buf, btw, &bw_tmp);
|
||||
}
|
||||
else {
|
||||
res = file_p->drv->write_cb(file_p->drv, file_p->file_d, buf, btw, &bw_tmp);
|
||||
}
|
||||
if(bw != NULL) *bw = bw_tmp;
|
||||
|
||||
LV_PROFILER_FS_END;
|
||||
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->cache_size) {
|
||||
if(file_p->drv->seek_cb == NULL || file_p->drv->tell_cb == NULL) return LV_FS_RES_NOT_IMP;
|
||||
}
|
||||
else {
|
||||
if(file_p->drv->seek_cb == NULL) return LV_FS_RES_NOT_IMP;
|
||||
}
|
||||
|
||||
LV_PROFILER_FS_BEGIN;
|
||||
|
||||
lv_fs_res_t res;
|
||||
if(file_p->drv->cache_size) {
|
||||
res = lv_fs_seek_cached(file_p, pos, whence);
|
||||
}
|
||||
else {
|
||||
res = file_p->drv->seek_cb(file_p->drv, file_p->file_d, pos, whence);
|
||||
}
|
||||
|
||||
LV_PROFILER_FS_END;
|
||||
|
||||
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->cache_size == 0 && file_p->drv->tell_cb == NULL) {
|
||||
*pos = 0;
|
||||
return LV_FS_RES_NOT_IMP;
|
||||
}
|
||||
|
||||
LV_PROFILER_FS_BEGIN;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
LV_PROFILER_FS_END;
|
||||
|
||||
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;
|
||||
|
||||
resolved_path_t resolved_path = lv_fs_resolve_path(path);
|
||||
|
||||
lv_fs_drv_t * drv = lv_fs_get_drv(resolved_path.driver_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;
|
||||
}
|
||||
|
||||
LV_PROFILER_FS_BEGIN;
|
||||
|
||||
void * dir_d = drv->dir_open_cb(drv, resolved_path.real_path);
|
||||
|
||||
if(dir_d == NULL || dir_d == (void *)(-1)) {
|
||||
LV_PROFILER_FS_END;
|
||||
return LV_FS_RES_UNKNOWN;
|
||||
}
|
||||
|
||||
rddir_p->drv = drv;
|
||||
rddir_p->dir_d = dir_d;
|
||||
|
||||
LV_PROFILER_FS_END;
|
||||
|
||||
return LV_FS_RES_OK;
|
||||
}
|
||||
|
||||
lv_fs_res_t lv_fs_dir_read(lv_fs_dir_t * rddir_p, char * fn, uint32_t fn_len)
|
||||
{
|
||||
if(fn_len == 0) {
|
||||
return LV_FS_RES_INV_PARAM;
|
||||
}
|
||||
|
||||
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_PROFILER_FS_BEGIN;
|
||||
|
||||
lv_fs_res_t res = rddir_p->drv->dir_read_cb(rddir_p->drv, rddir_p->dir_d, fn, fn_len);
|
||||
|
||||
LV_PROFILER_FS_END;
|
||||
|
||||
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_PROFILER_FS_BEGIN;
|
||||
|
||||
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;
|
||||
|
||||
LV_PROFILER_FS_END;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
void lv_fs_drv_init(lv_fs_drv_t * drv)
|
||||
{
|
||||
lv_memzero(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(fsdrv_ll_p);
|
||||
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(fsdrv_ll_p, 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(fsdrv_ll_p, 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 = lv_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 = lv_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 = lv_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
|
||||
**********************/
|
||||
|
||||
/**
|
||||
* Extract the drive letter and the real path from LVGL's "abstracted file system" path string
|
||||
* @param path path string (E.g. S:/folder/file.txt)
|
||||
*/
|
||||
static resolved_path_t lv_fs_resolve_path(const char * path)
|
||||
{
|
||||
resolved_path_t resolved;
|
||||
|
||||
#if LV_FS_DEFAULT_DRIVER_LETTER != '\0' /* When using default driver-identifier letter, strict format (X:) is mandatory */
|
||||
bool has_drive_prefix = ('A' <= path[0]) && (path[0] <= 'Z') && (path[1] == ':');
|
||||
|
||||
if(has_drive_prefix) {
|
||||
resolved.driver_letter = path[0];
|
||||
resolved.real_path = path + 2;
|
||||
}
|
||||
else {
|
||||
resolved.driver_letter = LV_FS_DEFAULT_DRIVER_LETTER;
|
||||
resolved.real_path = path;
|
||||
}
|
||||
# else /*Lean rules for backward compatibility*/
|
||||
resolved.driver_letter = path[0];
|
||||
|
||||
if(*path != '\0') {
|
||||
path++; /*Ignore the driver letter*/
|
||||
if(*path == ':') path++;
|
||||
}
|
||||
|
||||
resolved.real_path = path;
|
||||
#endif
|
||||
|
||||
return resolved;
|
||||
}
|
||||
|
||||
static lv_fs_res_t lv_fs_read_cached(lv_fs_file_t * file_p, void * 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;
|
||||
uint32_t buffer_size = file_p->drv->cache_size;
|
||||
|
||||
if(start <= file_position && file_position <= end) {
|
||||
/* Data can be read from cache buffer */
|
||||
uint32_t buffer_remaining_length = (uint32_t)end - file_position + 1;
|
||||
uint32_t buffer_offset = (end - start) - buffer_remaining_length + 1;
|
||||
|
||||
/* Do not allow reading beyond the actual memory block for memory-mapped files */
|
||||
if(file_p->drv->cache_size == LV_FS_CACHE_FROM_BUFFER) {
|
||||
if(btr > buffer_remaining_length)
|
||||
btr = buffer_remaining_length - 1;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
file_p->drv->seek_cb(file_p->drv, file_p->file_d, file_p->cache->end + 1,
|
||||
LV_FS_SEEK_SET);
|
||||
|
||||
uint32_t bytes_read_to_buffer = 0;
|
||||
if(btr - buffer_remaining_length > 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, (char *)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, buffer, buffer_size, &bytes_read_to_buffer);
|
||||
file_p->cache->start = file_p->cache->end + 1;
|
||||
file_p->cache->end = file_p->cache->start + bytes_read_to_buffer - 1;
|
||||
|
||||
uint16_t data_chunk_remaining = LV_MIN(btr - buffer_remaining_length, bytes_read_to_buffer);
|
||||
lv_memcpy((char *)buf + buffer_remaining_length, buffer, data_chunk_remaining);
|
||||
}
|
||||
*br = LV_MIN(buffer_remaining_length + bytes_read_to_buffer, btr);
|
||||
}
|
||||
}
|
||||
else {
|
||||
file_p->drv->seek_cb(file_p->drv, file_p->file_d, file_p->cache->file_position,
|
||||
LV_FS_SEEK_SET);
|
||||
|
||||
/*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_malloc(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 - 1;
|
||||
|
||||
*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;
|
||||
}
|
||||
|
||||
static lv_fs_res_t lv_fs_write_cached(lv_fs_file_t * file_p, const void * buf, uint32_t btw, uint32_t * bw)
|
||||
{
|
||||
lv_fs_res_t res = LV_FS_RES_OK;
|
||||
|
||||
/*Need to do FS seek before writing data to FS*/
|
||||
res = file_p->drv->seek_cb(file_p->drv, file_p->file_d, file_p->cache->file_position, LV_FS_SEEK_SET);
|
||||
if(res != LV_FS_RES_OK) return res;
|
||||
|
||||
res = file_p->drv->write_cb(file_p->drv, file_p->file_d, buf, btw, bw);
|
||||
if(res != LV_FS_RES_OK) return res;
|
||||
|
||||
if(file_p->cache->end >= file_p->cache->start) {
|
||||
uint32_t start_position = file_p->cache->file_position;
|
||||
uint32_t end_position = file_p->cache->file_position + *bw - 1;
|
||||
char * cache_buffer = file_p->cache->buffer;
|
||||
const char * write_buffer = buf;
|
||||
|
||||
if(start_position <= file_p->cache->start && end_position >= file_p->cache->end) {
|
||||
lv_memcpy(cache_buffer,
|
||||
write_buffer + (file_p->cache->start - start_position),
|
||||
file_p->cache->end + 1 - file_p->cache->start);
|
||||
}
|
||||
else if(start_position >= file_p->cache->start && end_position <= file_p->cache->end) {
|
||||
lv_memcpy(cache_buffer + (start_position - file_p->cache->start),
|
||||
write_buffer,
|
||||
end_position + 1 - start_position);
|
||||
}
|
||||
else if(end_position >= file_p->cache->start && end_position <= file_p->cache->end) {
|
||||
lv_memcpy(cache_buffer,
|
||||
write_buffer + (file_p->cache->start - start_position),
|
||||
end_position + 1 - file_p->cache->start);
|
||||
}
|
||||
else if(start_position >= file_p->cache->start && start_position <= file_p->cache->end) {
|
||||
lv_memcpy(cache_buffer + (start_position - file_p->cache->start),
|
||||
write_buffer,
|
||||
file_p->cache->end + 1 - start_position);
|
||||
}
|
||||
}
|
||||
|
||||
file_p->cache->file_position += *bw;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static lv_fs_res_t lv_fs_seek_cached(lv_fs_file_t * file_p, uint32_t pos, lv_fs_whence_t whence)
|
||||
{
|
||||
lv_fs_res_t res = LV_FS_RES_OK;
|
||||
switch(whence) {
|
||||
case LV_FS_SEEK_SET: {
|
||||
file_p->cache->file_position = pos;
|
||||
break;
|
||||
}
|
||||
case LV_FS_SEEK_CUR: {
|
||||
file_p->cache->file_position += pos;
|
||||
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 the 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;
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
@@ -0,0 +1,258 @@
|
||||
/**
|
||||
* @file lv_fs.h
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef LV_FS_H
|
||||
#define LV_FS_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*********************
|
||||
* INCLUDES
|
||||
*********************/
|
||||
#include "../lv_conf_internal.h"
|
||||
#include "lv_types.h"
|
||||
|
||||
/*********************
|
||||
* DEFINES
|
||||
*********************/
|
||||
#define LV_FS_MAX_FN_LENGTH 64
|
||||
#define LV_FS_MAX_PATH_LENGTH 256
|
||||
|
||||
#define LV_FS_CACHE_FROM_BUFFER UINT32_MAX
|
||||
|
||||
/**********************
|
||||
* TYPEDEFS
|
||||
**********************/
|
||||
|
||||
/**
|
||||
* Errors in the file system module.
|
||||
*/
|
||||
typedef 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*/
|
||||
} lv_fs_res_t;
|
||||
|
||||
/**
|
||||
* File open mode.
|
||||
*/
|
||||
typedef enum {
|
||||
LV_FS_MODE_WR = 0x01,
|
||||
LV_FS_MODE_RD = 0x02,
|
||||
} 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;
|
||||
|
||||
struct _lv_fs_drv_t;
|
||||
typedef struct _lv_fs_drv_t lv_fs_drv_t;
|
||||
struct _lv_fs_drv_t {
|
||||
char letter;
|
||||
uint32_t cache_size;
|
||||
bool (*ready_cb)(lv_fs_drv_t * drv);
|
||||
|
||||
void * (*open_cb)(lv_fs_drv_t * drv, const char * path, lv_fs_mode_t mode);
|
||||
lv_fs_res_t (*close_cb)(lv_fs_drv_t * drv, void * file_p);
|
||||
lv_fs_res_t (*read_cb)(lv_fs_drv_t * drv, void * file_p, void * buf, uint32_t btr, uint32_t * br);
|
||||
lv_fs_res_t (*write_cb)(lv_fs_drv_t * drv, void * file_p, const void * buf, uint32_t btw, uint32_t * bw);
|
||||
lv_fs_res_t (*seek_cb)(lv_fs_drv_t * drv, void * file_p, uint32_t pos, lv_fs_whence_t whence);
|
||||
lv_fs_res_t (*tell_cb)(lv_fs_drv_t * drv, void * file_p, uint32_t * pos_p);
|
||||
|
||||
void * (*dir_open_cb)(lv_fs_drv_t * drv, const char * path);
|
||||
lv_fs_res_t (*dir_read_cb)(lv_fs_drv_t * drv, void * rddir_p, char * fn, uint32_t fn_len);
|
||||
lv_fs_res_t (*dir_close_cb)(lv_fs_drv_t * drv, void * rddir_p);
|
||||
|
||||
void * user_data; /**< Custom file user data*/
|
||||
};
|
||||
|
||||
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 a file system driver with default values.
|
||||
* It is used to ensure all fields have known values and 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-identifier 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);
|
||||
|
||||
/**
|
||||
* Make a path object for the memory-mapped file compatible with the file system interface
|
||||
* @param path path to a lv_fs_path_ex object
|
||||
* @param letter the identifier letter of the driver. E.g. `LV_FS_MEMFS_LETTER`
|
||||
* @param buf address of the memory buffer
|
||||
* @param size size of the memory buffer in bytes
|
||||
*/
|
||||
void lv_fs_make_path_from_buffer(lv_fs_path_ex_t * path, char letter, const void * buf, uint32_t size);
|
||||
|
||||
/**
|
||||
* 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 to set 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 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
|
||||
* @param fn_len length of the 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, uint32_t fn_len);
|
||||
|
||||
/**
|
||||
* 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*/
|
||||
@@ -0,0 +1,63 @@
|
||||
/**
|
||||
* @file lv_fs_private.h
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef LV_FS_PRIVATE_H
|
||||
#define LV_FS_PRIVATE_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*********************
|
||||
* INCLUDES
|
||||
*********************/
|
||||
|
||||
#include "lv_fs.h"
|
||||
|
||||
/*********************
|
||||
* DEFINES
|
||||
*********************/
|
||||
|
||||
/**********************
|
||||
* TYPEDEFS
|
||||
**********************/
|
||||
|
||||
struct _lv_fs_file_cache_t {
|
||||
uint32_t start;
|
||||
uint32_t end;
|
||||
uint32_t file_position;
|
||||
void * buffer;
|
||||
};
|
||||
|
||||
/** Extended path object to specify buffer for memory-mapped files */
|
||||
struct _lv_fs_path_ex_t {
|
||||
char path[4]; /**< This is needed to make it compatible with a normal path */
|
||||
const void * buffer;
|
||||
uint32_t size;
|
||||
};
|
||||
|
||||
/**********************
|
||||
* GLOBAL PROTOTYPES
|
||||
**********************/
|
||||
|
||||
/**
|
||||
* Initialize the File system interface
|
||||
*/
|
||||
void lv_fs_init(void);
|
||||
|
||||
/**
|
||||
* Deinitialize the File system interface
|
||||
*/
|
||||
void lv_fs_deinit(void);
|
||||
|
||||
/**********************
|
||||
* MACROS
|
||||
**********************/
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /*extern "C"*/
|
||||
#endif
|
||||
|
||||
#endif /*LV_FS_PRIVATE_H*/
|
||||
@@ -0,0 +1,120 @@
|
||||
/**
|
||||
* @file lv_grad.c
|
||||
*
|
||||
*/
|
||||
|
||||
/*********************
|
||||
* INCLUDES
|
||||
*********************/
|
||||
#include "lv_grad.h"
|
||||
|
||||
/*********************
|
||||
* DEFINES
|
||||
*********************/
|
||||
|
||||
/**********************
|
||||
* TYPEDEFS
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* STATIC PROTOTYPES
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* STATIC VARIABLES
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* MACROS
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* GLOBAL FUNCTIONS
|
||||
**********************/
|
||||
void lv_grad_init_stops(lv_grad_dsc_t * dsc, const lv_color_t colors[], const lv_opa_t opa[],
|
||||
const uint8_t fracs[], int num_stops)
|
||||
{
|
||||
LV_ASSERT(num_stops <= LV_GRADIENT_MAX_STOPS);
|
||||
LV_ASSERT(num_stops > 1);
|
||||
LV_ASSERT_NULL(dsc);
|
||||
LV_ASSERT_NULL(colors);
|
||||
|
||||
dsc->stops_count = num_stops;
|
||||
for(int i = 0; i < num_stops; i++) {
|
||||
dsc->stops[i].color = colors[i];
|
||||
dsc->stops[i].opa = opa != NULL ? opa[i] : LV_OPA_COVER;
|
||||
dsc->stops[i].frac = fracs != NULL ? fracs[i] : 255 * i / (num_stops - 1);
|
||||
}
|
||||
}
|
||||
|
||||
void lv_grad_horizontal_init(lv_grad_dsc_t * dsc)
|
||||
{
|
||||
LV_ASSERT_NULL(dsc);
|
||||
|
||||
dsc->dir = LV_GRAD_DIR_HOR;
|
||||
}
|
||||
|
||||
void lv_grad_vertical_init(lv_grad_dsc_t * dsc)
|
||||
{
|
||||
LV_ASSERT_NULL(dsc);
|
||||
|
||||
dsc->dir = LV_GRAD_DIR_VER;
|
||||
}
|
||||
|
||||
|
||||
#if LV_USE_DRAW_SW_COMPLEX_GRADIENTS
|
||||
|
||||
void lv_grad_linear_init(lv_grad_dsc_t * dsc, int32_t from_x, int32_t from_y, int32_t to_x, int32_t to_y,
|
||||
lv_grad_extend_t extend)
|
||||
{
|
||||
LV_ASSERT_NULL(dsc);
|
||||
dsc->dir = LV_GRAD_DIR_LINEAR;
|
||||
dsc->params.linear.start.x = from_x;
|
||||
dsc->params.linear.start.y = from_y;
|
||||
dsc->params.linear.end.x = to_x;
|
||||
dsc->params.linear.end.y = to_y;
|
||||
dsc->extend = extend;
|
||||
}
|
||||
|
||||
void lv_grad_radial_init(lv_grad_dsc_t * dsc, int32_t center_x, int32_t center_y, int32_t to_x, int32_t to_y,
|
||||
lv_grad_extend_t extend)
|
||||
{
|
||||
LV_ASSERT_NULL(dsc);
|
||||
dsc->dir = LV_GRAD_DIR_RADIAL;
|
||||
dsc->params.radial.focal.x = center_x;
|
||||
dsc->params.radial.focal.y = center_y;
|
||||
dsc->params.radial.focal_extent.x = center_x;
|
||||
dsc->params.radial.focal_extent.y = center_y;
|
||||
dsc->params.radial.end.x = center_x;
|
||||
dsc->params.radial.end.y = center_y;
|
||||
dsc->params.radial.end_extent.x = to_x;
|
||||
dsc->params.radial.end_extent.y = to_y;
|
||||
dsc->extend = extend;
|
||||
}
|
||||
|
||||
void lv_grad_conical_init(lv_grad_dsc_t * dsc, int32_t center_x, int32_t center_y, int32_t start_angle,
|
||||
int32_t end_angle, lv_grad_extend_t extend)
|
||||
{
|
||||
LV_ASSERT_NULL(dsc);
|
||||
dsc->dir = LV_GRAD_DIR_CONICAL;
|
||||
dsc->params.conical.center.x = center_x;
|
||||
dsc->params.conical.center.y = center_y;
|
||||
dsc->params.conical.start_angle = start_angle;
|
||||
dsc->params.conical.end_angle = end_angle;
|
||||
dsc->extend = extend;
|
||||
}
|
||||
|
||||
void lv_grad_radial_set_focal(lv_grad_dsc_t * dsc, int32_t center_x, int32_t center_y, int32_t radius)
|
||||
{
|
||||
LV_ASSERT_NULL(dsc);
|
||||
dsc->params.radial.focal.x = center_x;
|
||||
dsc->params.radial.focal.y = center_y;
|
||||
dsc->params.radial.focal_extent.x = center_x + radius;
|
||||
dsc->params.radial.focal_extent.y = center_y;
|
||||
}
|
||||
|
||||
#endif /* LV_USE_DRAW_SW_COMPLEX_GRADIENTS */
|
||||
|
||||
/**********************
|
||||
* STATIC FUNCTIONS
|
||||
**********************/
|
||||
@@ -0,0 +1,182 @@
|
||||
/**
|
||||
* @file lv_grad.h
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef LV_GRAD_H
|
||||
#define LV_GRAD_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*********************
|
||||
* INCLUDES
|
||||
*********************/
|
||||
#include "../lv_conf_internal.h"
|
||||
#include "lv_color.h"
|
||||
#include "lv_area.h"
|
||||
|
||||
/*********************
|
||||
* DEFINES
|
||||
*********************/
|
||||
|
||||
/**********************
|
||||
* TYPEDEFS
|
||||
**********************/
|
||||
|
||||
/**
|
||||
* The direction of the gradient.
|
||||
*/
|
||||
typedef enum {
|
||||
LV_GRAD_DIR_NONE, /**< No gradient (the `grad_color` property is ignored)*/
|
||||
LV_GRAD_DIR_VER, /**< Simple vertical (top to bottom) gradient*/
|
||||
LV_GRAD_DIR_HOR, /**< Simple horizontal (left to right) gradient*/
|
||||
LV_GRAD_DIR_LINEAR, /**< Linear gradient defined by start and end points. Can be at any angle.*/
|
||||
LV_GRAD_DIR_RADIAL, /**< Radial gradient defined by start and end circles*/
|
||||
LV_GRAD_DIR_CONICAL, /**< Conical gradient defined by center point, start and end angles*/
|
||||
} lv_grad_dir_t;
|
||||
|
||||
/**
|
||||
* Gradient behavior outside the defined range.
|
||||
*/
|
||||
typedef enum {
|
||||
LV_GRAD_EXTEND_PAD, /**< Repeat the same color*/
|
||||
LV_GRAD_EXTEND_REPEAT, /**< Repeat the pattern*/
|
||||
LV_GRAD_EXTEND_REFLECT, /**< Repeat the pattern mirrored*/
|
||||
} lv_grad_extend_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 */
|
||||
lv_opa_t opa; /**< The opacity of the color*/
|
||||
uint8_t frac; /**< The stop position in 1/255 unit */
|
||||
} lv_grad_stop_t;
|
||||
|
||||
/** A descriptor of a gradient. */
|
||||
typedef struct {
|
||||
lv_grad_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 : 4; /**< The gradient direction.
|
||||
* Any of LV_GRAD_DIR_NONE, LV_GRAD_DIR_VER, LV_GRAD_DIR_HOR,
|
||||
* LV_GRAD_TYPE_LINEAR, LV_GRAD_TYPE_RADIAL, LV_GRAD_TYPE_CONICAL */
|
||||
lv_grad_extend_t extend : 3; /**< Behaviour outside the defined range.
|
||||
* LV_GRAD_EXTEND_NONE, LV_GRAD_EXTEND_PAD, LV_GRAD_EXTEND_REPEAT, LV_GRAD_EXTEND_REFLECT */
|
||||
#if LV_USE_DRAW_SW_COMPLEX_GRADIENTS
|
||||
union {
|
||||
/*Linear gradient parameters*/
|
||||
struct {
|
||||
lv_point_t start; /**< Linear gradient vector start point */
|
||||
lv_point_t end; /**< Linear gradient vector end point */
|
||||
} linear;
|
||||
/*Radial gradient parameters*/
|
||||
struct {
|
||||
lv_point_t focal; /**< Center of the focal (starting) circle in local coordinates */
|
||||
/* (can be the same as the ending circle to create concentric circles) */
|
||||
lv_point_t focal_extent; /**< Point on the circle (can be the same as the center) */
|
||||
lv_point_t end; /**< Center of the ending circle in local coordinates */
|
||||
lv_point_t end_extent; /**< Point on the circle determining the radius of the gradient */
|
||||
} radial;
|
||||
/*Conical gradient parameters*/
|
||||
struct {
|
||||
lv_point_t center; /**< Conical gradient center point */
|
||||
int16_t start_angle; /**< Start angle 0..3600 */
|
||||
int16_t end_angle; /**< End angle 0..3600 */
|
||||
} conical;
|
||||
} params;
|
||||
void * state;
|
||||
#endif
|
||||
} lv_grad_dsc_t;
|
||||
|
||||
|
||||
/**********************
|
||||
* GLOBAL PROTOTYPES
|
||||
**********************/
|
||||
|
||||
/**
|
||||
* Initialize gradient color map from a table
|
||||
* @param grad pointer to a gradient descriptor
|
||||
* @param colors color array
|
||||
* @param fracs position array (0..255): if NULL, then colors are distributed evenly
|
||||
* @param opa opacity array: if NULL, then LV_OPA_COVER is assumed
|
||||
* @param num_stops number of gradient stops (1..LV_GRADIENT_MAX_STOPS)
|
||||
*/
|
||||
void lv_grad_init_stops(lv_grad_dsc_t * grad, const lv_color_t colors[], const lv_opa_t opa[],
|
||||
const uint8_t fracs[], int num_stops);
|
||||
|
||||
/**
|
||||
* Helper function to initialize a horizontal gradient.
|
||||
* @param dsc gradient descriptor
|
||||
*/
|
||||
void lv_grad_horizontal_init(lv_grad_dsc_t * dsc);
|
||||
|
||||
/**
|
||||
* Helper function to initialize a vertical gradient.
|
||||
* @param dsc gradient descriptor
|
||||
*/
|
||||
void lv_grad_vertical_init(lv_grad_dsc_t * dsc);
|
||||
|
||||
#if LV_USE_DRAW_SW_COMPLEX_GRADIENTS
|
||||
|
||||
/**
|
||||
* Helper function to initialize linear gradient
|
||||
* @param dsc gradient descriptor
|
||||
* @param from_x start x position: can be a coordinate or an lv_pct() value
|
||||
* predefined constants LV_GRAD_LEFT, LV_GRAD_RIGHT, LV_GRAD_TOP, LV_GRAD_BOTTOM, LV_GRAD_CENTER can be used as well
|
||||
* @param from_y start y position
|
||||
* @param to_x end x position
|
||||
* @param to_y end y position
|
||||
* @param extend one of LV_GRAD_EXTEND_PAD, LV_GRAD_EXTEND_REPEAT or LV_GRAD_EXTEND_REFLECT
|
||||
*/
|
||||
void lv_grad_linear_init(lv_grad_dsc_t * dsc, int32_t from_x, int32_t from_y, int32_t to_x, int32_t to_y,
|
||||
lv_grad_extend_t extend);
|
||||
|
||||
/**
|
||||
* Helper function to initialize radial gradient
|
||||
* @param dsc gradient descriptor
|
||||
* @param center_x center x position: can be a coordinate or an lv_pct() value
|
||||
* predefined constants LV_GRAD_LEFT, LV_GRAD_RIGHT, LV_GRAD_TOP, LV_GRAD_BOTTOM, LV_GRAD_CENTER can be used as well
|
||||
* @param center_y center y position
|
||||
* @param to_x point on the end circle x position
|
||||
* @param to_y point on the end circle y position
|
||||
* @param extend one of LV_GRAD_EXTEND_PAD, LV_GRAD_EXTEND_REPEAT or LV_GRAD_EXTEND_REFLECT
|
||||
*/
|
||||
void lv_grad_radial_init(lv_grad_dsc_t * dsc, int32_t center_x, int32_t center_y, int32_t to_x, int32_t to_y,
|
||||
lv_grad_extend_t extend);
|
||||
|
||||
/**
|
||||
* Set focal (starting) circle of a radial gradient
|
||||
* @param dsc gradient descriptor
|
||||
* @param center_x center x position: can be a coordinate or an lv_pct() value
|
||||
* predefined constants LV_GRAD_LEFT, LV_GRAD_RIGHT, LV_GRAD_TOP, LV_GRAD_BOTTOM, LV_GRAD_CENTER can be used as well
|
||||
* @param center_y center y position
|
||||
* @param radius radius of the starting circle (NOTE: this must be a scalar number, not percentage)
|
||||
*/
|
||||
void lv_grad_radial_set_focal(lv_grad_dsc_t * dsc, int32_t center_x, int32_t center_y, int32_t radius);
|
||||
|
||||
/**
|
||||
* Helper function to initialize conical gradient
|
||||
* @param dsc gradient descriptor
|
||||
* @param center_x center x position: can be a coordinate or an lv_pct() value
|
||||
* predefined constants LV_GRAD_LEFT, LV_GRAD_RIGHT, LV_GRAD_TOP, LV_GRAD_BOTTOM, LV_GRAD_CENTER can be used as well
|
||||
* @param center_y center y position
|
||||
* @param start_angle start angle in degrees
|
||||
* @param end_angle end angle in degrees
|
||||
* @param extend one of LV_GRAD_EXTEND_PAD, LV_GRAD_EXTEND_REPEAT or LV_GRAD_EXTEND_REFLECT
|
||||
*/
|
||||
void lv_grad_conical_init(lv_grad_dsc_t * dsc, int32_t center_x, int32_t center_y, int32_t start_angle,
|
||||
int32_t end_angle, lv_grad_extend_t extend);
|
||||
|
||||
#endif /*LV_USE_DRAW_SW_COMPLEX_GRADIENTS*/
|
||||
|
||||
/**********************
|
||||
* MACROS
|
||||
**********************/
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /*extern "C"*/
|
||||
#endif
|
||||
|
||||
#endif /*LV_GRAD_H*/
|
||||
@@ -0,0 +1,213 @@
|
||||
/**
|
||||
* @file lv_iter.c
|
||||
*
|
||||
*/
|
||||
|
||||
/*********************
|
||||
* INCLUDES
|
||||
*********************/
|
||||
|
||||
#include "lv_assert.h"
|
||||
|
||||
#include "lv_iter.h"
|
||||
|
||||
#include "lv_circle_buf.h"
|
||||
|
||||
/*********************
|
||||
* DEFINES
|
||||
*********************/
|
||||
|
||||
/**********************
|
||||
* TYPEDEFS
|
||||
**********************/
|
||||
|
||||
struct _lv_iter_t {
|
||||
/* Iterator state */
|
||||
void * instance; /**< Pointer to the object to iterate over */
|
||||
uint32_t elem_size; /**< Size of one element in bytes */
|
||||
void * context; /**< Custom context for the iteration */
|
||||
uint32_t context_size; /**< Size of the custom context in bytes */
|
||||
|
||||
/* Peeking */
|
||||
lv_circle_buf_t * peek_buf; /**< Circular buffer for peeking */
|
||||
uint32_t peek_offset; /**< Offset in the peek buffer */
|
||||
|
||||
/* Callbacks */
|
||||
lv_iter_next_cb next_cb; /**< Callback to get the next element */
|
||||
};
|
||||
|
||||
/**********************
|
||||
* STATIC PROTOTYPES
|
||||
**********************/
|
||||
|
||||
static bool peek_fill_cb(void * buf, uint32_t buf_len, int32_t index, void * user_data);
|
||||
|
||||
/**********************
|
||||
* GLOBAL VARIABLES
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* STATIC VARIABLES
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* MACROS
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* GLOBAL FUNCTIONS
|
||||
**********************/
|
||||
|
||||
lv_iter_t * lv_iter_create(void * instance, const uint32_t elem_size, const uint32_t context_size,
|
||||
lv_iter_next_cb next_cb)
|
||||
{
|
||||
lv_iter_t * iter = lv_malloc_zeroed(sizeof(lv_iter_t));
|
||||
LV_ASSERT_MALLOC(iter);
|
||||
|
||||
if(iter == NULL) {
|
||||
LV_LOG_ERROR("Could not allocate memory for iterator");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
iter->instance = instance;
|
||||
iter->elem_size = elem_size;
|
||||
iter->context_size = context_size;
|
||||
iter->next_cb = next_cb;
|
||||
|
||||
if(context_size > 0) {
|
||||
iter->context = lv_malloc_zeroed(context_size);
|
||||
LV_ASSERT_MALLOC(iter->context);
|
||||
}
|
||||
|
||||
return iter;
|
||||
}
|
||||
|
||||
void * lv_iter_get_context(const lv_iter_t * iter)
|
||||
{
|
||||
LV_ASSERT_NULL(iter);
|
||||
|
||||
return iter ? iter->context : NULL;
|
||||
}
|
||||
|
||||
void lv_iter_destroy(lv_iter_t * iter)
|
||||
{
|
||||
LV_ASSERT_NULL(iter);
|
||||
if(iter == NULL) return;
|
||||
|
||||
if(iter->context_size > 0) lv_free(iter->context);
|
||||
if(iter->peek_buf != NULL) lv_circle_buf_destroy(iter->peek_buf);
|
||||
|
||||
iter->context = NULL;
|
||||
iter->peek_buf = NULL;
|
||||
|
||||
lv_free(iter);
|
||||
}
|
||||
|
||||
void lv_iter_make_peekable(lv_iter_t * iter, const uint32_t capacity)
|
||||
{
|
||||
LV_ASSERT_NULL(iter);
|
||||
if(iter == NULL) return;
|
||||
|
||||
if(capacity == 0 || iter->peek_buf != NULL) return;
|
||||
iter->peek_buf = lv_circle_buf_create(capacity, iter->elem_size);
|
||||
LV_ASSERT_NULL(iter->peek_buf);
|
||||
}
|
||||
|
||||
lv_result_t lv_iter_next(lv_iter_t * iter, void * elem)
|
||||
{
|
||||
LV_ASSERT_NULL(iter);
|
||||
if(iter == NULL) return LV_RESULT_INVALID;
|
||||
|
||||
lv_circle_buf_t * c_buf = iter->peek_buf;
|
||||
if(c_buf != NULL && !lv_circle_buf_is_empty(c_buf)) {
|
||||
if(elem) lv_circle_buf_read(c_buf, elem);
|
||||
else lv_circle_buf_skip(c_buf);
|
||||
iter->peek_offset = 0;
|
||||
return LV_RESULT_OK;
|
||||
}
|
||||
|
||||
const lv_result_t iter_res = iter->next_cb(iter->instance, iter->context, elem);
|
||||
if(iter_res == LV_RESULT_INVALID) return LV_RESULT_INVALID;
|
||||
|
||||
if(c_buf != NULL) iter->peek_offset = 0;
|
||||
|
||||
return iter_res;
|
||||
}
|
||||
|
||||
lv_result_t lv_iter_peek(lv_iter_t * iter, void * elem)
|
||||
{
|
||||
LV_ASSERT_NULL(iter);
|
||||
if(iter == NULL) return LV_RESULT_INVALID;
|
||||
|
||||
lv_circle_buf_t * c_buf = iter->peek_buf;
|
||||
if(c_buf == NULL) return LV_RESULT_INVALID;
|
||||
|
||||
const uint32_t peek_count = lv_circle_buf_size(c_buf);
|
||||
if(iter->peek_offset >= peek_count) {
|
||||
const uint32_t required = iter->peek_offset + 1 - peek_count;
|
||||
const uint32_t filled = lv_circle_buf_fill(c_buf, required, peek_fill_cb, iter);
|
||||
if(filled != required) return LV_RESULT_INVALID;
|
||||
}
|
||||
|
||||
lv_circle_buf_peek_at(c_buf, iter->peek_offset, elem);
|
||||
|
||||
return LV_RESULT_OK;
|
||||
}
|
||||
|
||||
lv_result_t lv_iter_peek_advance(lv_iter_t * iter)
|
||||
{
|
||||
LV_ASSERT_NULL(iter);
|
||||
if(iter == NULL) return LV_RESULT_INVALID;
|
||||
|
||||
if(iter->peek_buf == NULL || iter->peek_offset + 1 >= lv_circle_buf_capacity(iter->peek_buf))
|
||||
return LV_RESULT_INVALID;
|
||||
iter->peek_offset++;
|
||||
return LV_RESULT_OK;
|
||||
}
|
||||
|
||||
lv_result_t lv_iter_peek_reset(lv_iter_t * iter)
|
||||
{
|
||||
LV_ASSERT_NULL(iter);
|
||||
if(iter == NULL) return LV_RESULT_INVALID;
|
||||
|
||||
if(iter->peek_buf == NULL) return LV_RESULT_INVALID;
|
||||
|
||||
iter->peek_offset = 0;
|
||||
return LV_RESULT_OK;
|
||||
}
|
||||
|
||||
void lv_iter_inspect(lv_iter_t * iter, const lv_iter_inspect_cb inspect_cb)
|
||||
{
|
||||
LV_ASSERT_NULL(iter);
|
||||
if(iter == NULL) return;
|
||||
|
||||
void * elem = lv_malloc_zeroed(iter->elem_size);
|
||||
LV_ASSERT_MALLOC(elem);
|
||||
|
||||
if(elem == NULL) {
|
||||
LV_LOG_ERROR("Could not allocate memory for element");
|
||||
return;
|
||||
}
|
||||
|
||||
while(lv_iter_next(iter, elem) == LV_RESULT_OK) {
|
||||
inspect_cb(elem);
|
||||
}
|
||||
|
||||
lv_free(elem);
|
||||
}
|
||||
|
||||
/**********************
|
||||
* STATIC FUNCTIONS
|
||||
**********************/
|
||||
|
||||
static bool peek_fill_cb(void * buf, const uint32_t buf_len, const int32_t index, void * user_data)
|
||||
{
|
||||
LV_UNUSED(buf_len);
|
||||
LV_UNUSED(index);
|
||||
|
||||
const lv_iter_t * iter = user_data;
|
||||
const lv_result_t iter_res = iter->next_cb(iter->instance, iter->context, buf);
|
||||
if(iter_res == LV_RESULT_INVALID) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
/**
|
||||
* @file lv_iter.h
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifndef LV_ITER_H
|
||||
#define LV_ITER_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*********************
|
||||
* INCLUDES
|
||||
*********************/
|
||||
|
||||
#include "lv_types.h"
|
||||
|
||||
/*********************
|
||||
* DEFINES
|
||||
*********************/
|
||||
|
||||
/**********************
|
||||
* TYPEDEFS
|
||||
**********************/
|
||||
|
||||
typedef lv_result_t (*lv_iter_next_cb)(void * instance, void * context, void * elem);
|
||||
typedef void (*lv_iter_inspect_cb)(void * elem);
|
||||
|
||||
/**********************
|
||||
* GLOBAL PROTOTYPES
|
||||
**********************/
|
||||
|
||||
/**
|
||||
* Create an iterator based on an instance, and then the next element of the iterator can be obtained through lv_iter_next,
|
||||
* In order to obtain the next operation in a unified and abstract way.
|
||||
* @param instance The instance to be iterated
|
||||
* @param elem_size The size of the element to be iterated in bytes
|
||||
* @param context_size The size of the context to be passed to the next_cb in bytes
|
||||
* @param next_cb The callback function to get the next element
|
||||
* @return The iterator object
|
||||
*/
|
||||
lv_iter_t * lv_iter_create(void * instance, uint32_t elem_size, uint32_t context_size, lv_iter_next_cb next_cb);
|
||||
|
||||
/**
|
||||
* Get the context of the iterator. You can use it to store some temporary variables associated with current iterator..
|
||||
* @param iter `lv_iter_t` object create before
|
||||
* @return the iter context
|
||||
*/
|
||||
void * lv_iter_get_context(const lv_iter_t * iter);
|
||||
|
||||
/**
|
||||
* Destroy the iterator object, and release the context. Other resources allocated by the user are not released.
|
||||
* The user needs to release it by itself.
|
||||
* @param iter `lv_iter_t` object create before
|
||||
*/
|
||||
void lv_iter_destroy(lv_iter_t * iter);
|
||||
|
||||
/**
|
||||
* Get the next element of the iterator.
|
||||
* @param iter `lv_iter_t` object create before
|
||||
* @param elem The pointer to store the next element
|
||||
* @return LV_RESULT_OK: Get the next element successfully
|
||||
* LV_RESULT_INVALID: The next element is invalid
|
||||
*/
|
||||
lv_result_t lv_iter_next(lv_iter_t * iter, void * elem);
|
||||
|
||||
/**
|
||||
* Make the iterator peekable, which means that the user can peek the next element without advancing the iterator.
|
||||
* @param iter `lv_iter_t` object create before
|
||||
* @param capacity The capacity of the peek buffer
|
||||
*/
|
||||
void lv_iter_make_peekable(lv_iter_t * iter, uint32_t capacity);
|
||||
|
||||
/**
|
||||
* Peek the next element of the iterator without advancing the iterator.
|
||||
* @param iter `lv_iter_t` object create before
|
||||
* @param elem The pointer to store the next element
|
||||
* @return LV_RESULT_OK: Peek the next element successfully
|
||||
* LV_RESULT_INVALID: The next element is invalid
|
||||
*/
|
||||
lv_result_t lv_iter_peek(lv_iter_t * iter, void * elem);
|
||||
|
||||
/**
|
||||
* Only advance the iterator without getting the next element.
|
||||
* @param iter `lv_iter_t` object create before
|
||||
* @return LV_RESULT_OK: Peek the next element successfully
|
||||
* LV_RESULT_INVALID: The next element is invalid
|
||||
*/
|
||||
lv_result_t lv_iter_peek_advance(lv_iter_t * iter);
|
||||
|
||||
/**
|
||||
* Reset the peek cursor to the `next` cursor.
|
||||
* @param iter `lv_iter_t` object create before
|
||||
* @return LV_RESULT_OK: Reset the peek buffer successfully
|
||||
* LV_RESULT_INVALID: The peek buffer is invalid
|
||||
*/
|
||||
lv_result_t lv_iter_peek_reset(lv_iter_t * iter);
|
||||
|
||||
/**
|
||||
* Inspect the element of the iterator. The callback function will be called for each element of the iterator.
|
||||
* @param iter `lv_iter_t` object create before
|
||||
* @param inspect_cb The callback function to inspect the element
|
||||
*/
|
||||
void lv_iter_inspect(lv_iter_t * iter, lv_iter_inspect_cb inspect_cb);
|
||||
|
||||
/*************************
|
||||
* GLOBAL VARIABLES
|
||||
*************************/
|
||||
|
||||
/**********************
|
||||
* MACROS
|
||||
**********************/
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /*extern "C"*/
|
||||
#endif
|
||||
|
||||
#endif /*LV_ITER_H*/
|
||||
@@ -0,0 +1,339 @@
|
||||
/**
|
||||
* @file lv_ll.c
|
||||
* Handle linked lists.
|
||||
* The nodes are dynamically allocated by the 'lv_mem' module,
|
||||
*/
|
||||
|
||||
/*********************
|
||||
* INCLUDES
|
||||
*********************/
|
||||
#include "lv_ll.h"
|
||||
#include "../stdlib/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
|
||||
**********************/
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
void * lv_ll_ins_head(lv_ll_t * ll_p)
|
||||
{
|
||||
lv_ll_node_t * n_new;
|
||||
|
||||
n_new = lv_malloc(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;
|
||||
}
|
||||
|
||||
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_malloc(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;
|
||||
}
|
||||
|
||||
void * lv_ll_ins_tail(lv_ll_t * ll_p)
|
||||
{
|
||||
lv_ll_node_t * n_new;
|
||||
|
||||
n_new = lv_malloc(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;
|
||||
}
|
||||
|
||||
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 'node_p'*/
|
||||
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 'node_p'*/
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
void lv_ll_clear_custom(lv_ll_t * ll_p, void(*cleanup)(void *))
|
||||
{
|
||||
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);
|
||||
if(cleanup == NULL) {
|
||||
lv_ll_remove(ll_p, i);
|
||||
lv_free(i);
|
||||
}
|
||||
else {
|
||||
cleanup(i);
|
||||
}
|
||||
i = i_next;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void * lv_ll_get_head(const lv_ll_t * ll_p)
|
||||
{
|
||||
if(ll_p == NULL) return NULL;
|
||||
return ll_p->head;
|
||||
}
|
||||
|
||||
void * lv_ll_get_tail(const lv_ll_t * ll_p)
|
||||
{
|
||||
if(ll_p == NULL) return NULL;
|
||||
return ll_p->tail;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
void lv_ll_clear(lv_ll_t * ll_p)
|
||||
{
|
||||
lv_ll_clear_custom(ll_p, NULL);
|
||||
}
|
||||
|
||||
/**********************
|
||||
* 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;
|
||||
}
|
||||
@@ -0,0 +1,169 @@
|
||||
/**
|
||||
* @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 "../lv_conf_internal.h"
|
||||
#include "lv_types.h"
|
||||
|
||||
/*********************
|
||||
* 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);
|
||||
|
||||
void lv_ll_clear_custom(lv_ll_t * ll_p, void(*cleanup)(void *));
|
||||
|
||||
/**
|
||||
* 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 another 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
|
||||
@@ -0,0 +1,149 @@
|
||||
/**
|
||||
* @file lv_log.c
|
||||
*
|
||||
*/
|
||||
|
||||
/*********************
|
||||
* INCLUDES
|
||||
*********************/
|
||||
#include "lv_log.h"
|
||||
#if LV_USE_LOG
|
||||
|
||||
#include "../misc/lv_types.h"
|
||||
#include "../stdlib/lv_sprintf.h"
|
||||
#include "../stdlib/lv_mem.h"
|
||||
#include "../stdlib/lv_string.h"
|
||||
#include "../tick/lv_tick.h"
|
||||
#include "../core/lv_global.h"
|
||||
|
||||
#if LV_LOG_PRINTF
|
||||
#include <stdio.h>
|
||||
#endif
|
||||
|
||||
/*********************
|
||||
* DEFINES
|
||||
*********************/
|
||||
#if LV_LOG_USE_TIMESTAMP
|
||||
#define last_log_time LV_GLOBAL_DEFAULT()->log_last_log_time
|
||||
#endif
|
||||
#define custom_print_cb LV_GLOBAL_DEFAULT()->custom_log_print_cb
|
||||
|
||||
#if LV_LOG_USE_TIMESTAMP
|
||||
#define LOG_TIMESTAMP_FMT "\t(%" LV_PRIu32 ".%03" LV_PRIu32 ", +%" LV_PRIu32 ")\t"
|
||||
#define LOG_TIMESTAMP_EXPR t / 1000, t % 1000, t - last_log_time,
|
||||
#else
|
||||
#define LOG_TIMESTAMP_FMT
|
||||
#define LOG_TIMESTAMP_EXPR
|
||||
#endif
|
||||
|
||||
#if LV_LOG_USE_FILE_LINE
|
||||
#define LOG_FILE_LINE_FMT " %s:%d"
|
||||
#define LOG_FILE_LINE_EXPR , &file[p], line
|
||||
#else
|
||||
#define LOG_FILE_LINE_FMT
|
||||
#define LOG_FILE_LINE_EXPR
|
||||
#endif
|
||||
|
||||
/**********************
|
||||
* TYPEDEFS
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* STATIC PROTOTYPES
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* STATIC VARIABLES
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* MACROS
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* GLOBAL FUNCTIONS
|
||||
**********************/
|
||||
|
||||
void lv_log_register_print_cb(lv_log_print_g_cb_t print_cb)
|
||||
{
|
||||
custom_print_cb = print_cb;
|
||||
}
|
||||
|
||||
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*/
|
||||
|
||||
if(level >= LV_LOG_LEVEL) {
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
|
||||
#if LV_LOG_USE_FILE_LINE
|
||||
/*Use only the file name not the path*/
|
||||
size_t p;
|
||||
for(p = lv_strlen(file); p > 0; p--) {
|
||||
if(file[p] == '/' || file[p] == '\\') {
|
||||
p++; /*Skip the slash*/
|
||||
break;
|
||||
}
|
||||
}
|
||||
#else
|
||||
LV_UNUSED(file);
|
||||
LV_UNUSED(line);
|
||||
#endif
|
||||
|
||||
#if LV_LOG_USE_TIMESTAMP
|
||||
uint32_t t = lv_tick_get();
|
||||
#endif
|
||||
static const char * lvl_prefix[] = {"Trace", "Info", "Warn", "Error", "User"};
|
||||
|
||||
if(custom_print_cb) {
|
||||
char buf[512];
|
||||
char msg[256];
|
||||
lv_vsnprintf(msg, sizeof(msg), format, args);
|
||||
lv_snprintf(buf, sizeof(buf), "[%s]" LOG_TIMESTAMP_FMT " %s: %s" LOG_FILE_LINE_FMT "\n",
|
||||
lvl_prefix[level], LOG_TIMESTAMP_EXPR func, msg LOG_FILE_LINE_EXPR);
|
||||
custom_print_cb(level, buf);
|
||||
}
|
||||
#if LV_LOG_PRINTF
|
||||
else {
|
||||
printf("[%s]" LOG_TIMESTAMP_FMT " %s: ",
|
||||
lvl_prefix[level], LOG_TIMESTAMP_EXPR func);
|
||||
vprintf(format, args);
|
||||
printf(LOG_FILE_LINE_FMT "\n" LOG_FILE_LINE_EXPR);
|
||||
fflush(stdout);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if LV_LOG_USE_TIMESTAMP
|
||||
last_log_time = t;
|
||||
#endif
|
||||
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(custom_print_cb) {
|
||||
char buf[512];
|
||||
lv_vsnprintf(buf, sizeof(buf), format, args);
|
||||
custom_print_cb(LV_LOG_LEVEL_USER, buf);
|
||||
}
|
||||
#if LV_LOG_PRINTF
|
||||
else {
|
||||
vprintf(format, args);
|
||||
}
|
||||
#endif
|
||||
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
/**********************
|
||||
* STATIC FUNCTIONS
|
||||
**********************/
|
||||
|
||||
#endif /*LV_USE_LOG*/
|
||||
@@ -0,0 +1,162 @@
|
||||
/**
|
||||
* @file lv_log.h
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef LV_LOG_H
|
||||
#define LV_LOG_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*********************
|
||||
* INCLUDES
|
||||
*********************/
|
||||
#include "../lv_conf_internal.h"
|
||||
|
||||
#include "lv_types.h"
|
||||
|
||||
/*********************
|
||||
* DEFINES
|
||||
*********************/
|
||||
|
||||
/*Possible log level. For compatibility declare it independently from `LV_USE_LOG`*/
|
||||
|
||||
#define LV_LOG_LEVEL_TRACE 0 /**< Log 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 /**< Log only critical issues, when system may fail. */
|
||||
#define LV_LOG_LEVEL_USER 4 /**< Log only custom log messages added by the user. */
|
||||
#define LV_LOG_LEVEL_NONE 5 /**< Do not log anything. */
|
||||
#define LV_LOG_LEVEL_NUM 5 /**< 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
|
||||
|
||||
#if LV_LOG_USE_FILE_LINE
|
||||
#define LV_LOG_FILE __FILE__
|
||||
#define LV_LOG_LINE __LINE__
|
||||
#else
|
||||
#define LV_LOG_FILE NULL
|
||||
#define LV_LOG_LINE 0
|
||||
#endif
|
||||
|
||||
/**********************
|
||||
* TYPEDEFS
|
||||
**********************/
|
||||
|
||||
/**
|
||||
* Log print function. Receives a string buffer to print".
|
||||
*/
|
||||
typedef void (*lv_log_print_g_cb_t)(lv_log_level_t level, 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, LV_LOG_FILE, LV_LOG_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, LV_LOG_FILE, LV_LOG_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, LV_LOG_FILE, LV_LOG_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, LV_LOG_FILE, LV_LOG_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, LV_LOG_FILE, LV_LOG_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*/
|
||||
@@ -0,0 +1,344 @@
|
||||
/**
|
||||
* @file lv_lru.c
|
||||
*
|
||||
* @see https://github.com/willcannings/C-LRU-Cache
|
||||
*/
|
||||
|
||||
/*********************
|
||||
* INCLUDES
|
||||
*********************/
|
||||
|
||||
#include "lv_lru.h"
|
||||
#include "lv_math.h"
|
||||
#include "../stdlib/lv_mem.h"
|
||||
#include "../stdlib/lv_string.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_cb_t value_free,
|
||||
lv_lru_free_cb_t key_free)
|
||||
{
|
||||
// create the cache
|
||||
lv_lru_t * cache = lv_malloc_zeroed(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_free;
|
||||
cache->key_free = key_free ? key_free : lv_free;
|
||||
|
||||
// size the hash table to a guestimate of the number of slots required (assuming a perfect hash)
|
||||
cache->items = lv_malloc_zeroed(sizeof(lv_lru_item_t *) * cache->hash_table_size);
|
||||
if(!cache->items) {
|
||||
LV_LOG_WARN("LRU Cache unable to create cache hash table");
|
||||
lv_free(cache);
|
||||
return NULL;
|
||||
}
|
||||
return cache;
|
||||
}
|
||||
|
||||
void lv_lru_delete(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_free(item);
|
||||
item = next;
|
||||
}
|
||||
}
|
||||
lv_free(cache->items);
|
||||
}
|
||||
|
||||
if(cache->free_items) {
|
||||
item = cache->free_items;
|
||||
while(item) {
|
||||
next = (lv_lru_item_t *) item->next;
|
||||
lv_free(item);
|
||||
item = next;
|
||||
}
|
||||
}
|
||||
|
||||
// free the cache
|
||||
lv_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_malloc(key_length);
|
||||
lv_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 lv_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_memzero(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_memzero(item, sizeof(lv_lru_item_t));
|
||||
}
|
||||
else {
|
||||
item = lv_malloc_zeroed(sizeof(lv_lru_item_t));
|
||||
}
|
||||
|
||||
return item;
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
/**
|
||||
* @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"
|
||||
|
||||
/*********************
|
||||
* 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_cb_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_cb_t value_free;
|
||||
lv_lru_free_cb_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_cb_t value_free,
|
||||
lv_lru_free_cb_t key_free);
|
||||
|
||||
void lv_lru_delete(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*/
|
||||
@@ -0,0 +1,456 @@
|
||||
/**
|
||||
* @file lv_math.c
|
||||
*
|
||||
*/
|
||||
|
||||
/*********************
|
||||
* INCLUDES
|
||||
*********************/
|
||||
#include "lv_math.h"
|
||||
#include "../core/lv_global.h"
|
||||
|
||||
/*********************
|
||||
* DEFINES
|
||||
*********************/
|
||||
#define rand_seed LV_GLOBAL_DEFAULT()->math_rand_seed
|
||||
|
||||
/**********************
|
||||
* TYPEDEFS
|
||||
**********************/
|
||||
|
||||
#define CUBIC_NEWTON_ITERATIONS 8
|
||||
#define CUBIC_PRECISION_BITS 10 /* 10 or 14 bits recommended, int64_t calculation is used for >14bit precision */
|
||||
|
||||
#if CUBIC_PRECISION_BITS < 10 || CUBIC_PRECISION_BITS > 20
|
||||
#error "cubic precision bits should be in range of [10, 20] for 32bit/64bit calculations."
|
||||
#endif
|
||||
|
||||
/**********************
|
||||
* STATIC PROTOTYPES
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* STATIC VARIABLES
|
||||
**********************/
|
||||
static const uint16_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, 14365, 14876, 15384, 15886, 16384, 16877,
|
||||
17364, 17847, 18324, 18795, 19261, 19720, 20174, 20622, 21063, 21498, 21926, 22348, 22763, 23170, 23571, 23965,
|
||||
24351, 24730, 25102, 25466, 25822, 26170, 26510, 26842, 27166, 27482, 27789, 28088, 28378, 28660, 28932, 29197,
|
||||
29452, 29698, 29935, 30163, 30382, 30592, 30792, 30983, 31164, 31336, 31499, 31651, 31795, 31928, 32052, 32166,
|
||||
32270, 32365, 32449, 32524, 32588, 32643, 32688, 32723, 32748, 32763, 32768
|
||||
};
|
||||
|
||||
/**********************
|
||||
* MACROS
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* GLOBAL FUNCTIONS
|
||||
**********************/
|
||||
|
||||
int32_t LV_ATTRIBUTE_FAST_MEM lv_trigo_sin(int16_t angle)
|
||||
{
|
||||
int32_t ret = 0;
|
||||
while(angle < 0) angle += 360;
|
||||
while(angle >= 360) angle -= 360;
|
||||
|
||||
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];
|
||||
}
|
||||
|
||||
if(ret == 32767) return 32768;
|
||||
else if(ret == -32767) return -32768;
|
||||
else return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* cubic-bezier Reference:
|
||||
*
|
||||
* https://github.com/gre/bezier-easing
|
||||
* https://opensource.apple.com/source/WebCore/WebCore-955.66/platform/graphics/UnitBezier.h
|
||||
*
|
||||
* Copyright (c) 2014 Gaëtan Renaudeau
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
static int32_t do_cubic_bezier(int32_t t, int32_t a, int32_t b, int32_t c)
|
||||
{
|
||||
/*a * t^3 + b * t^2 + c * t*/
|
||||
#if CUBIC_PRECISION_BITS > 14
|
||||
int64_t ret;
|
||||
#else
|
||||
int32_t ret;
|
||||
#endif
|
||||
|
||||
ret = a;
|
||||
ret = (ret * t) >> CUBIC_PRECISION_BITS;
|
||||
ret = ((ret + b) * t) >> CUBIC_PRECISION_BITS;
|
||||
ret = ((ret + c) * t) >> CUBIC_PRECISION_BITS;
|
||||
return ret;
|
||||
}
|
||||
|
||||
int32_t lv_cubic_bezier(int32_t x, int32_t x1, int32_t y1, int32_t x2, int32_t y2)
|
||||
{
|
||||
int32_t ax, bx, cx, ay, by, cy;
|
||||
int32_t tl, tr, t; /*t in cubic-bezier function, used for bisection */
|
||||
int32_t xs; /*x sampled on curve */
|
||||
#if CUBIC_PRECISION_BITS > 14
|
||||
int64_t d; /*slope value at specified t*/
|
||||
#else
|
||||
int32_t d;
|
||||
#endif
|
||||
|
||||
if(x == 0 || x == LV_BEZIER_VAL_MAX) return x;
|
||||
|
||||
/* input is always LV_BEZIER_VAL_SHIFT bit precision */
|
||||
|
||||
#if CUBIC_PRECISION_BITS != LV_BEZIER_VAL_SHIFT
|
||||
x <<= CUBIC_PRECISION_BITS - LV_BEZIER_VAL_SHIFT;
|
||||
x1 <<= CUBIC_PRECISION_BITS - LV_BEZIER_VAL_SHIFT;
|
||||
x2 <<= CUBIC_PRECISION_BITS - LV_BEZIER_VAL_SHIFT;
|
||||
y1 <<= CUBIC_PRECISION_BITS - LV_BEZIER_VAL_SHIFT;
|
||||
y2 <<= CUBIC_PRECISION_BITS - LV_BEZIER_VAL_SHIFT;
|
||||
#endif
|
||||
|
||||
cx = 3 * x1;
|
||||
bx = 3 * (x2 - x1) - cx;
|
||||
ax = (1L << CUBIC_PRECISION_BITS) - cx - bx;
|
||||
|
||||
cy = 3 * y1;
|
||||
by = 3 * (y2 - y1) - cy;
|
||||
ay = (1L << CUBIC_PRECISION_BITS) - cy - by;
|
||||
|
||||
/*Try Newton's method firstly */
|
||||
t = x; /*Make a guess*/
|
||||
for(int i = 0; i < CUBIC_NEWTON_ITERATIONS; i++) {
|
||||
/*Check if x on curve at t matches input x*/
|
||||
xs = do_cubic_bezier(t, ax, bx, cx) - x;
|
||||
if(LV_ABS(xs) <= 1) goto found;
|
||||
|
||||
/* get slop at t, d = 3 * ax * t^2 + 2 * bx + t + cx */
|
||||
d = ax; /* use 64bit operation if needed. */
|
||||
d = (3 * d * t) >> CUBIC_PRECISION_BITS;
|
||||
d = ((d + 2 * bx) * t) >> CUBIC_PRECISION_BITS;
|
||||
d += cx;
|
||||
|
||||
if(LV_ABS(d) <= 1) break;
|
||||
|
||||
d = ((int64_t)xs * (1L << CUBIC_PRECISION_BITS)) / d;
|
||||
if(d == 0) break; /*Reached precision limits*/
|
||||
t -= d;
|
||||
}
|
||||
|
||||
/*Fallback to bisection method for reliability*/
|
||||
tl = 0, tr = 1L << CUBIC_PRECISION_BITS, t = x;
|
||||
|
||||
if(t < tl) {
|
||||
t = tl;
|
||||
goto found;
|
||||
}
|
||||
|
||||
if(t > tr) {
|
||||
t = tr;
|
||||
goto found;
|
||||
}
|
||||
|
||||
while(tl < tr) {
|
||||
xs = do_cubic_bezier(t, ax, bx, cx);
|
||||
if(LV_ABS(xs - x) <= 1) goto found;
|
||||
x > xs ? (tl = t) : (tr = t);
|
||||
t = (tr - tl) / 2 + tl;
|
||||
if(t == tl) break;
|
||||
}
|
||||
|
||||
/*Failed to find suitable t for given x, return a value anyway.*/
|
||||
found:
|
||||
/*Return y at t*/
|
||||
#if CUBIC_PRECISION_BITS != LV_BEZIER_VAL_SHIFT
|
||||
return do_cubic_bezier(t, ay, by, cy) >> (CUBIC_PRECISION_BITS - LV_BEZIER_VAL_SHIFT);
|
||||
#else
|
||||
return do_cubic_bezier(t, ay, by, cy);
|
||||
#endif
|
||||
}
|
||||
|
||||
void LV_ATTRIBUTE_FAST_MEM 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;
|
||||
}
|
||||
|
||||
/*
|
||||
// Alternative Integer Square Root function
|
||||
// Contributors include Arne Steinarson for the basic approximation idea,
|
||||
// Dann Corbit and Mathew Hendry for the first cut at the algorithm,
|
||||
// Lawrence Kirby for the rearrangement, improvements and range optimization
|
||||
// and Paul Hsieh for the round-then-adjust idea.
|
||||
*/
|
||||
int32_t LV_ATTRIBUTE_FAST_MEM lv_sqrt32(uint32_t x)
|
||||
{
|
||||
static const unsigned char sqq_table[] = {
|
||||
0, 16, 22, 27, 32, 35, 39, 42, 45, 48, 50, 53, 55, 57,
|
||||
59, 61, 64, 65, 67, 69, 71, 73, 75, 76, 78, 80, 81, 83,
|
||||
84, 86, 87, 89, 90, 91, 93, 94, 96, 97, 98, 99, 101, 102,
|
||||
103, 104, 106, 107, 108, 109, 110, 112, 113, 114, 115, 116, 117, 118,
|
||||
119, 120, 121, 122, 123, 124, 125, 126, 128, 128, 129, 130, 131, 132,
|
||||
133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 144, 145,
|
||||
146, 147, 148, 149, 150, 150, 151, 152, 153, 154, 155, 155, 156, 157,
|
||||
158, 159, 160, 160, 161, 162, 163, 163, 164, 165, 166, 167, 167, 168,
|
||||
169, 170, 170, 171, 172, 173, 173, 174, 175, 176, 176, 177, 178, 178,
|
||||
179, 180, 181, 181, 182, 183, 183, 184, 185, 185, 186, 187, 187, 188,
|
||||
189, 189, 190, 191, 192, 192, 193, 193, 194, 195, 195, 196, 197, 197,
|
||||
198, 199, 199, 200, 201, 201, 202, 203, 203, 204, 204, 205, 206, 206,
|
||||
207, 208, 208, 209, 209, 210, 211, 211, 212, 212, 213, 214, 214, 215,
|
||||
215, 216, 217, 217, 218, 218, 219, 219, 220, 221, 221, 222, 222, 223,
|
||||
224, 224, 225, 225, 226, 226, 227, 227, 228, 229, 229, 230, 230, 231,
|
||||
231, 232, 232, 233, 234, 234, 235, 235, 236, 236, 237, 237, 238, 238,
|
||||
239, 240, 240, 241, 241, 242, 242, 243, 243, 244, 244, 245, 245, 246,
|
||||
246, 247, 247, 248, 248, 249, 249, 250, 250, 251, 251, 252, 252, 253,
|
||||
253, 254, 254, 255
|
||||
};
|
||||
|
||||
int32_t xn;
|
||||
|
||||
if(x >= 0x10000)
|
||||
if(x >= 0x1000000)
|
||||
if(x >= 0x10000000)
|
||||
if(x >= 0x40000000) {
|
||||
if(x >= 65535UL * 65535UL)
|
||||
return 65535;
|
||||
xn = sqq_table[x >> 24] << 8;
|
||||
}
|
||||
else
|
||||
xn = sqq_table[x >> 22] << 7;
|
||||
else if(x >= 0x4000000)
|
||||
xn = sqq_table[x >> 20] << 6;
|
||||
else
|
||||
xn = sqq_table[x >> 18] << 5;
|
||||
else {
|
||||
if(x >= 0x100000)
|
||||
if(x >= 0x400000)
|
||||
xn = sqq_table[x >> 16] << 4;
|
||||
else
|
||||
xn = sqq_table[x >> 14] << 3;
|
||||
else if(x >= 0x40000)
|
||||
xn = sqq_table[x >> 12] << 2;
|
||||
else
|
||||
xn = sqq_table[x >> 10] << 1;
|
||||
|
||||
goto nr1;
|
||||
}
|
||||
else if(x >= 0x100) {
|
||||
if(x >= 0x1000)
|
||||
if(x >= 0x4000)
|
||||
xn = (sqq_table[x >> 8] >> 0) + 1;
|
||||
else
|
||||
xn = (sqq_table[x >> 6] >> 1) + 1;
|
||||
else if(x >= 0x400)
|
||||
xn = (sqq_table[x >> 4] >> 2) + 1;
|
||||
else
|
||||
xn = (sqq_table[x >> 2] >> 3) + 1;
|
||||
|
||||
goto adj;
|
||||
}
|
||||
else
|
||||
return sqq_table[x] >> 4;
|
||||
|
||||
/* Run two iterations of the standard convergence formula */
|
||||
|
||||
xn = (xn + 1 + x / xn) / 2;
|
||||
nr1:
|
||||
xn = (xn + 1 + x / xn) / 2;
|
||||
adj:
|
||||
|
||||
if(xn * xn > (int32_t)x) /* Correct rounding if necessary */
|
||||
xn--;
|
||||
|
||||
return xn;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
void lv_rand_set_seed(uint32_t seed)
|
||||
{
|
||||
rand_seed = seed;
|
||||
}
|
||||
|
||||
uint32_t lv_rand(uint32_t min, uint32_t max)
|
||||
{
|
||||
/*Algorithm "xor" from p. 4 of Marsaglia, "Xorshift RNGs"*/
|
||||
uint32_t x = rand_seed;
|
||||
x ^= x << 13;
|
||||
x ^= x >> 17;
|
||||
x ^= x << 5;
|
||||
rand_seed = x;
|
||||
|
||||
return (rand_seed % (max - min + 1)) + min;
|
||||
}
|
||||
|
||||
int32_t LV_ATTRIBUTE_FAST_MEM lv_trigo_cos(int16_t angle)
|
||||
{
|
||||
return lv_trigo_sin(angle + 90);
|
||||
}
|
||||
|
||||
int32_t lv_bezier3(int32_t t, int32_t u0, uint32_t u1, int32_t u2, int32_t u3)
|
||||
{
|
||||
LV_UNUSED(u0);
|
||||
LV_UNUSED(u3);
|
||||
return lv_cubic_bezier(t, 341, u1, 683, u2);
|
||||
}
|
||||
|
||||
/**********************
|
||||
* STATIC FUNCTIONS
|
||||
**********************/
|
||||
@@ -0,0 +1,174 @@
|
||||
/**
|
||||
* @file lv_math.h
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef LV_MATH_H
|
||||
#define LV_MATH_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*********************
|
||||
* INCLUDES
|
||||
*********************/
|
||||
#include "../lv_conf_internal.h"
|
||||
#include "lv_types.h"
|
||||
|
||||
/*********************
|
||||
* DEFINES
|
||||
*********************/
|
||||
#define LV_TRIGO_SIN_MAX 32768
|
||||
#define LV_TRIGO_SHIFT 15 /**< >> LV_TRIGO_SHIFT to normalize*/
|
||||
|
||||
#define LV_BEZIER_VAL_SHIFT 10 /**< log2(LV_BEZIER_VAL_MAX): used to normalize up scaled values*/
|
||||
#define LV_BEZIER_VAL_MAX (1L << LV_BEZIER_VAL_SHIFT) /**< Max time in Bezier functions (not [0..1] to use integers)*/
|
||||
#define LV_BEZIER_VAL_FLOAT(f) ((int32_t)((f) * LV_BEZIER_VAL_MAX)) /**< Convert const float number cubic-bezier values to fix-point value*/
|
||||
|
||||
/** Align up value x to align, align must be a power of two */
|
||||
#define LV_ALIGN_UP(x, align) (((x) + ((align) - 1)) & ~((align) - 1))
|
||||
|
||||
/** Round up value x to round, round can be any integer number */
|
||||
#define LV_ROUND_UP(x, round) ((((x) + ((round) - 1)) / (round)) * (round))
|
||||
|
||||
/**********************
|
||||
* TYPEDEFS
|
||||
**********************/
|
||||
|
||||
typedef struct {
|
||||
uint16_t i;
|
||||
uint16_t f;
|
||||
} lv_sqrt_res_t;
|
||||
|
||||
/**********************
|
||||
* GLOBAL PROTOTYPES
|
||||
**********************/
|
||||
|
||||
/**
|
||||
* Return with sinus of an angle
|
||||
* @param angle
|
||||
* @return sinus of 'angle'. sin(-90) = -32767, sin(90) = 32767
|
||||
*/
|
||||
int32_t /* LV_ATTRIBUTE_FAST_MEM */ lv_trigo_sin(int16_t angle);
|
||||
|
||||
int32_t LV_ATTRIBUTE_FAST_MEM lv_trigo_cos(int16_t angle);
|
||||
|
||||
/**
|
||||
* Calculate the y value of cubic-bezier(x1, y1, x2, y2) function as specified x.
|
||||
* @param x time in range of [0..LV_BEZIER_VAL_MAX]
|
||||
* @param x1 x of control point 1 in range of [0..LV_BEZIER_VAL_MAX]
|
||||
* @param y1 y of control point 1 in range of [0..LV_BEZIER_VAL_MAX]
|
||||
* @param x2 x of control point 2 in range of [0..LV_BEZIER_VAL_MAX]
|
||||
* @param y2 y of control point 2 in range of [0..LV_BEZIER_VAL_MAX]
|
||||
* @return the value calculated
|
||||
*/
|
||||
int32_t lv_cubic_bezier(int32_t x, int32_t x1, int32_t y1, int32_t x2, int32_t y2);
|
||||
|
||||
/**
|
||||
* Calculate a value of a Cubic Bezier function.
|
||||
* @param t time in range of [0..LV_BEZIER_VAL_MAX]
|
||||
* @param u0 must be 0
|
||||
* @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 must be LV_BEZIER_VAL_MAX
|
||||
* @return the value calculated from the given parameters in range of [0..LV_BEZIER_VAL_MAX]
|
||||
*/
|
||||
int32_t lv_bezier3(int32_t t, int32_t u0, uint32_t u1, int32_t u2, int32_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);
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
void /* LV_ATTRIBUTE_FAST_MEM */ lv_sqrt(uint32_t x, lv_sqrt_res_t * q, uint32_t mask);
|
||||
|
||||
/**
|
||||
* Alternative (fast, approximate) implementation for getting the square root of an integer.
|
||||
* @param x integer which square root should be calculated
|
||||
*/
|
||||
int32_t /* LV_ATTRIBUTE_FAST_MEM */ lv_sqrt32(uint32_t x);
|
||||
|
||||
/**
|
||||
* Calculate the square of an integer (input range is 0..32767).
|
||||
* @param x input
|
||||
* @return square
|
||||
*/
|
||||
static inline int32_t lv_sqr(int32_t x)
|
||||
{
|
||||
return x * x;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the integer exponents.
|
||||
* @param base
|
||||
* @param exp
|
||||
* @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);
|
||||
|
||||
/**
|
||||
* Set the seed of the pseudo random number generator
|
||||
* @param seed a number to initialize the random generator
|
||||
*/
|
||||
void lv_rand_set_seed(uint32_t seed);
|
||||
|
||||
/**
|
||||
* 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
|
||||
@@ -0,0 +1,241 @@
|
||||
/**
|
||||
* @file lv_matrix.c
|
||||
*
|
||||
*/
|
||||
|
||||
/*********************
|
||||
* INCLUDES
|
||||
*********************/
|
||||
|
||||
#include "lv_matrix.h"
|
||||
|
||||
#if LV_USE_MATRIX
|
||||
|
||||
#include "../stdlib/lv_string.h"
|
||||
#include "lv_math.h"
|
||||
#include <math.h>
|
||||
#include <float.h>
|
||||
#include "../misc/lv_log.h"
|
||||
/*********************
|
||||
* DEFINES
|
||||
*********************/
|
||||
#ifndef M_PI
|
||||
#define M_PI 3.1415926f
|
||||
#endif
|
||||
|
||||
/**********************
|
||||
* TYPEDEFS
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* STATIC PROTOTYPES
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* STATIC VARIABLES
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* MACROS
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* GLOBAL FUNCTIONS
|
||||
**********************/
|
||||
|
||||
void lv_matrix_identity(lv_matrix_t * matrix)
|
||||
{
|
||||
matrix->m[0][0] = 1.0f;
|
||||
matrix->m[0][1] = 0.0f;
|
||||
matrix->m[0][2] = 0.0f;
|
||||
matrix->m[1][0] = 0.0f;
|
||||
matrix->m[1][1] = 1.0f;
|
||||
matrix->m[1][2] = 0.0f;
|
||||
matrix->m[2][0] = 0.0f;
|
||||
matrix->m[2][1] = 0.0f;
|
||||
matrix->m[2][2] = 1.0f;
|
||||
}
|
||||
|
||||
void lv_matrix_translate(lv_matrix_t * matrix, float dx, float dy)
|
||||
{
|
||||
if(lv_matrix_is_identity_or_translation(matrix)) {
|
||||
/*optimization for matrix translation.*/
|
||||
matrix->m[0][2] += dx;
|
||||
matrix->m[1][2] += dy;
|
||||
return;
|
||||
}
|
||||
|
||||
lv_matrix_t tlm = {{
|
||||
{1.0f, 0.0f, dx},
|
||||
{0.0f, 1.0f, dy},
|
||||
{0.0f, 0.0f, 1.0f},
|
||||
}
|
||||
};
|
||||
|
||||
lv_matrix_multiply(matrix, &tlm);
|
||||
}
|
||||
|
||||
void lv_matrix_scale(lv_matrix_t * matrix, float scale_x, float scale_y)
|
||||
{
|
||||
lv_matrix_t scm = {{
|
||||
{scale_x, 0.0f, 0.0f},
|
||||
{0.0f, scale_y, 0.0f},
|
||||
{0.0f, 0.0f, 1.0f},
|
||||
}
|
||||
};
|
||||
|
||||
lv_matrix_multiply(matrix, &scm);
|
||||
}
|
||||
|
||||
void lv_matrix_rotate(lv_matrix_t * matrix, float degree)
|
||||
{
|
||||
float radian = degree / 180.0f * (float)M_PI;
|
||||
float cos_r = cosf(radian);
|
||||
float sin_r = sinf(radian);
|
||||
|
||||
lv_matrix_t rtm = {{
|
||||
{cos_r, -sin_r, 0.0f},
|
||||
{sin_r, cos_r, 0.0f},
|
||||
{0.0f, 0.0f, 1.0f},
|
||||
}
|
||||
};
|
||||
|
||||
lv_matrix_multiply(matrix, &rtm);
|
||||
}
|
||||
|
||||
void lv_matrix_skew(lv_matrix_t * matrix, float skew_x, float skew_y)
|
||||
{
|
||||
float rskew_x = skew_x / 180.0f * (float)M_PI;
|
||||
float rskew_y = skew_y / 180.0f * (float)M_PI;
|
||||
float tan_x = tanf(rskew_x);
|
||||
float tan_y = tanf(rskew_y);
|
||||
|
||||
lv_matrix_t skm = {{
|
||||
{1.0f, tan_x, 0.0f},
|
||||
{tan_y, 1.0f, 0.0f},
|
||||
{0.0f, 0.0f, 1.0f},
|
||||
}
|
||||
};
|
||||
|
||||
lv_matrix_multiply(matrix, &skm);
|
||||
}
|
||||
|
||||
void lv_matrix_multiply(lv_matrix_t * matrix, const lv_matrix_t * mul)
|
||||
{
|
||||
/*TODO: use NEON to optimize this function on ARM architecture.*/
|
||||
lv_matrix_t tmp;
|
||||
|
||||
for(int y = 0; y < 3; y++) {
|
||||
for(int x = 0; x < 3; x++) {
|
||||
tmp.m[y][x] = (matrix->m[y][0] * mul->m[0][x])
|
||||
+ (matrix->m[y][1] * mul->m[1][x])
|
||||
+ (matrix->m[y][2] * mul->m[2][x]);
|
||||
}
|
||||
}
|
||||
|
||||
lv_memcpy(matrix, &tmp, sizeof(lv_matrix_t));
|
||||
}
|
||||
|
||||
bool lv_matrix_inverse(lv_matrix_t * matrix, const lv_matrix_t * m)
|
||||
{
|
||||
float det00, det01, det02;
|
||||
float d;
|
||||
bool is_affine;
|
||||
|
||||
/* Test for identity matrix. */
|
||||
if(m == NULL) {
|
||||
lv_matrix_identity(matrix);
|
||||
return true;
|
||||
}
|
||||
|
||||
det00 = (m->m[1][1] * m->m[2][2]) - (m->m[2][1] * m->m[1][2]);
|
||||
det01 = (m->m[2][0] * m->m[1][2]) - (m->m[1][0] * m->m[2][2]);
|
||||
det02 = (m->m[1][0] * m->m[2][1]) - (m->m[2][0] * m->m[1][1]);
|
||||
|
||||
/* Compute determinant. */
|
||||
d = (m->m[0][0] * det00) + (m->m[0][1] * det01) + (m->m[0][2] * det02);
|
||||
|
||||
/* Return 0 if there is no inverse matrix. */
|
||||
if(d == 0.0f)
|
||||
return false;
|
||||
|
||||
/* Compute reciprocal. */
|
||||
d = 1.0f / d;
|
||||
|
||||
/* Determine if the matrix is affine. */
|
||||
is_affine = (m->m[2][0] == 0.0f) && (m->m[2][1] == 0.0f) && (m->m[2][2] == 1.0f);
|
||||
|
||||
matrix->m[0][0] = d * det00;
|
||||
matrix->m[0][1] = d * ((m->m[2][1] * m->m[0][2]) - (m->m[0][1] * m->m[2][2]));
|
||||
matrix->m[0][2] = d * ((m->m[0][1] * m->m[1][2]) - (m->m[1][1] * m->m[0][2]));
|
||||
matrix->m[1][0] = d * det01;
|
||||
matrix->m[1][1] = d * ((m->m[0][0] * m->m[2][2]) - (m->m[2][0] * m->m[0][2]));
|
||||
matrix->m[1][2] = d * ((m->m[1][0] * m->m[0][2]) - (m->m[0][0] * m->m[1][2]));
|
||||
matrix->m[2][0] = is_affine ? 0.0f : d * det02;
|
||||
matrix->m[2][1] = is_affine ? 0.0f : d * ((m->m[2][0] * m->m[0][1]) - (m->m[0][0] * m->m[2][1]));
|
||||
matrix->m[2][2] = is_affine ? 1.0f : d * ((m->m[0][0] * m->m[1][1]) - (m->m[1][0] * m->m[0][1]));
|
||||
|
||||
/* Success. */
|
||||
return true;
|
||||
}
|
||||
|
||||
lv_point_precise_t lv_matrix_transform_precise_point(const lv_matrix_t * matrix, const lv_point_precise_t * point)
|
||||
{
|
||||
lv_point_precise_t p;
|
||||
lv_value_precise_t w = point->x * matrix->m[2][0] + point->y * matrix->m[2][1] + matrix->m[2][2];
|
||||
if(LV_ABS(w) < FLT_EPSILON) {
|
||||
LV_LOG_ERROR("matrix is invalid");
|
||||
p.x = 0;
|
||||
p.y = 0;
|
||||
}
|
||||
else {
|
||||
lv_value_precise_t inv_w = 1.0f / w;
|
||||
p.x = (lv_value_precise_t)roundf((point->x * matrix->m[0][0] + point->y * matrix->m[0][1] + matrix->m[0][2]) * inv_w);
|
||||
p.y = (lv_value_precise_t)roundf((point->x * matrix->m[1][0] + point->y * matrix->m[1][1] + matrix->m[1][2]) * inv_w);
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
lv_area_t lv_matrix_transform_area(const lv_matrix_t * matrix, const lv_area_t * area)
|
||||
{
|
||||
lv_area_t res;
|
||||
lv_point_precise_t p[4] = {
|
||||
{area->x1, area->y1},
|
||||
{area->x1, area->y2},
|
||||
{area->x2, area->y1},
|
||||
{area->x2, area->y2},
|
||||
};
|
||||
p[0] = lv_matrix_transform_precise_point(matrix, &p[0]);
|
||||
p[1] = lv_matrix_transform_precise_point(matrix, &p[1]);
|
||||
p[2] = lv_matrix_transform_precise_point(matrix, &p[2]);
|
||||
p[3] = lv_matrix_transform_precise_point(matrix, &p[3]);
|
||||
|
||||
res.x1 = (int32_t)(LV_MIN4(p[0].x, p[1].x, p[2].x, p[3].x));
|
||||
res.x2 = (int32_t)(LV_MAX4(p[0].x, p[1].x, p[2].x, p[3].x));
|
||||
res.y1 = (int32_t)(LV_MIN4(p[0].y, p[1].y, p[2].y, p[3].y));
|
||||
res.y2 = (int32_t)(LV_MAX4(p[0].y, p[1].y, p[2].y, p[3].y));
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
bool lv_matrix_is_identity(const lv_matrix_t * matrix)
|
||||
{
|
||||
return (matrix->m[0][2] == 0.0f && matrix->m[1][2] == 0.0f && lv_matrix_is_identity_or_translation(matrix));
|
||||
}
|
||||
|
||||
bool lv_matrix_is_identity_or_translation(const lv_matrix_t * matrix)
|
||||
{
|
||||
return (matrix->m[0][0] == 1.0f &&
|
||||
matrix->m[0][1] == 0.0f &&
|
||||
matrix->m[1][0] == 0.0f &&
|
||||
matrix->m[1][1] == 1.0f &&
|
||||
matrix->m[2][0] == 0.0f &&
|
||||
matrix->m[2][1] == 0.0f &&
|
||||
matrix->m[2][2] == 1.0f);
|
||||
}
|
||||
|
||||
/**********************
|
||||
* STATIC FUNCTIONS
|
||||
**********************/
|
||||
|
||||
#endif /*LV_USE_MATRIX*/
|
||||
@@ -0,0 +1,136 @@
|
||||
/**
|
||||
* @file lv_matrix.h
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef LV_MATRIX_H
|
||||
#define LV_MATRIX_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*********************
|
||||
* INCLUDES
|
||||
*********************/
|
||||
|
||||
#include "../lv_conf_internal.h"
|
||||
|
||||
#if LV_USE_MATRIX
|
||||
|
||||
#include "lv_types.h"
|
||||
#include "lv_area.h"
|
||||
|
||||
/*********************
|
||||
* DEFINES
|
||||
*********************/
|
||||
|
||||
#if !LV_USE_FLOAT
|
||||
#error "LV_USE_FLOAT is required for lv_matrix"
|
||||
#endif
|
||||
|
||||
/**********************
|
||||
* TYPEDEFS
|
||||
**********************/
|
||||
|
||||
struct _lv_matrix_t {
|
||||
float m[3][3];
|
||||
};
|
||||
|
||||
/**********************
|
||||
* GLOBAL PROTOTYPES
|
||||
**********************/
|
||||
|
||||
/**
|
||||
* Set matrix to identity matrix
|
||||
* @param matrix pointer to a matrix
|
||||
*/
|
||||
void lv_matrix_identity(lv_matrix_t * matrix);
|
||||
|
||||
/**
|
||||
* Translate the matrix to new position
|
||||
* @param matrix pointer to a matrix
|
||||
* @param tx the amount of translate in x direction
|
||||
* @param tx the amount of translate in y direction
|
||||
*/
|
||||
void lv_matrix_translate(lv_matrix_t * matrix, float tx, float ty);
|
||||
|
||||
/**
|
||||
* Change the scale factor of the matrix
|
||||
* @param matrix pointer to a matrix
|
||||
* @param scale_x the scale factor for the X direction
|
||||
* @param scale_y the scale factor for the Y direction
|
||||
*/
|
||||
void lv_matrix_scale(lv_matrix_t * matrix, float scale_x, float scale_y);
|
||||
|
||||
/**
|
||||
* Rotate the matrix with origin
|
||||
* @param matrix pointer to a matrix
|
||||
* @param degree angle to rotate
|
||||
*/
|
||||
void lv_matrix_rotate(lv_matrix_t * matrix, float degree);
|
||||
|
||||
/**
|
||||
* Change the skew factor of the matrix
|
||||
* @param matrix pointer to a matrix
|
||||
* @param skew_x the skew factor for x direction
|
||||
* @param skew_y the skew factor for y direction
|
||||
*/
|
||||
void lv_matrix_skew(lv_matrix_t * matrix, float skew_x, float skew_y);
|
||||
|
||||
/**
|
||||
* Multiply two matrix and store the result to the first one
|
||||
* @param matrix pointer to a matrix
|
||||
* @param matrix2 pointer to another matrix
|
||||
*/
|
||||
void lv_matrix_multiply(lv_matrix_t * matrix, const lv_matrix_t * mul);
|
||||
|
||||
/**
|
||||
* Invert the matrix
|
||||
* @param matrix pointer to a matrix
|
||||
* @param m pointer to another matrix (optional)
|
||||
* @return true: the matrix is invertible, false: the matrix is singular and cannot be inverted
|
||||
*/
|
||||
bool lv_matrix_inverse(lv_matrix_t * matrix, const lv_matrix_t * m);
|
||||
|
||||
/**
|
||||
* Transform a point by a matrix
|
||||
* @param matrix pointer to a matrix
|
||||
* @param point pointer to a point
|
||||
* @return the transformed point
|
||||
*/
|
||||
lv_point_precise_t lv_matrix_transform_precise_point(const lv_matrix_t * matrix, const lv_point_precise_t * point);
|
||||
|
||||
/**
|
||||
* Transform an area by a matrix
|
||||
* @param matrix pointer to a matrix
|
||||
* @param area pointer to an area
|
||||
* @return the transformed area
|
||||
*/
|
||||
lv_area_t lv_matrix_transform_area(const lv_matrix_t * matrix, const lv_area_t * area);
|
||||
|
||||
/**
|
||||
* Check if the matrix is identity
|
||||
* @param matrix pointer to a matrix
|
||||
* @return true: the matrix is identity , false: the matrix is not identity
|
||||
*/
|
||||
bool lv_matrix_is_identity(const lv_matrix_t * matrix);
|
||||
|
||||
/**
|
||||
* Check if the matrix is identity or translation matrix
|
||||
* @param matrix pointer to a matrix
|
||||
* @return true: the matrix is identity or translation matrix, false: the matrix is not identity or translation matrix
|
||||
*/
|
||||
bool lv_matrix_is_identity_or_translation(const lv_matrix_t * matrix);
|
||||
|
||||
/**********************
|
||||
* MACROS
|
||||
**********************/
|
||||
|
||||
#endif /*LV_USE_MATRIX*/
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /*extern "C"*/
|
||||
#endif
|
||||
|
||||
#endif /*LV_MATRIX_H*/
|
||||
@@ -0,0 +1,130 @@
|
||||
/**
|
||||
* @file lv_palette.c
|
||||
*
|
||||
*/
|
||||
|
||||
/*********************
|
||||
* INCLUDES
|
||||
*********************/
|
||||
#include "lv_palette.h"
|
||||
|
||||
/*********************
|
||||
* DEFINES
|
||||
*********************/
|
||||
|
||||
/**********************
|
||||
* TYPEDEFS
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* STATIC PROTOTYPES
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* STATIC VARIABLES
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* MACROS
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* GLOBAL FUNCTIONS
|
||||
**********************/
|
||||
|
||||
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];
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
/**
|
||||
* @file lv_palette.h
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef LV_PALETTE_H
|
||||
#define LV_PALETTE_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*********************
|
||||
* INCLUDES
|
||||
*********************/
|
||||
#include "lv_color.h"
|
||||
#include "lv_types.h"
|
||||
|
||||
/*********************
|
||||
* DEFINES
|
||||
*********************/
|
||||
|
||||
/**********************
|
||||
* TYPEDEFS
|
||||
**********************/
|
||||
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
|
||||
**********************/
|
||||
|
||||
/*Source: https://vuetifyjs.com/en/styles/colors/#material-colors*/
|
||||
|
||||
lv_color_t lv_palette_main(lv_palette_t p);
|
||||
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_PALETTE_H*/
|
||||
@@ -0,0 +1,184 @@
|
||||
/**
|
||||
* @file lv_profiler.h
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef LV_PROFILER_H
|
||||
#define LV_PROFILER_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*********************
|
||||
* INCLUDES
|
||||
*********************/
|
||||
|
||||
#include "../lv_conf_internal.h"
|
||||
|
||||
#if LV_USE_PROFILER
|
||||
|
||||
#include LV_PROFILER_INCLUDE
|
||||
|
||||
/*********************
|
||||
* DEFINES
|
||||
*********************/
|
||||
|
||||
/**********************
|
||||
* TYPEDEFS
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* GLOBAL PROTOTYPES
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* MACROS
|
||||
**********************/
|
||||
|
||||
#else
|
||||
|
||||
#define LV_PROFILER_BEGIN
|
||||
#define LV_PROFILER_END
|
||||
#define LV_PROFILER_BEGIN_TAG(tag) LV_UNUSED(tag)
|
||||
#define LV_PROFILER_END_TAG(tag) LV_UNUSED(tag)
|
||||
|
||||
#endif /*LV_USE_PROFILER*/
|
||||
|
||||
#if LV_USE_PROFILER && LV_PROFILER_LAYOUT
|
||||
#define LV_PROFILER_LAYOUT_BEGIN LV_PROFILER_BEGIN
|
||||
#define LV_PROFILER_LAYOUT_END LV_PROFILER_END
|
||||
#define LV_PROFILER_LAYOUT_BEGIN_TAG(tag) LV_PROFILER_BEGIN_TAG(tag)
|
||||
#define LV_PROFILER_LAYOUT_END_TAG(tag) LV_PROFILER_END_TAG(tag)
|
||||
#else
|
||||
#define LV_PROFILER_LAYOUT_BEGIN
|
||||
#define LV_PROFILER_LAYOUT_END
|
||||
#define LV_PROFILER_LAYOUT_BEGIN_TAG(tag)
|
||||
#define LV_PROFILER_LAYOUT_END_TAG(tag)
|
||||
#endif
|
||||
|
||||
#if LV_USE_PROFILER && LV_PROFILER_STYLE
|
||||
#define LV_PROFILER_STYLE_BEGIN LV_PROFILER_BEGIN
|
||||
#define LV_PROFILER_STYLE_END LV_PROFILER_END
|
||||
#define LV_PROFILER_STYLE_BEGIN_TAG(tag) LV_PROFILER_BEGIN_TAG(tag)
|
||||
#define LV_PROFILER_STYLE_END_TAG(tag) LV_PROFILER_END_TAG(tag)
|
||||
#else
|
||||
#define LV_PROFILER_STYLE_BEGIN
|
||||
#define LV_PROFILER_STYLE_END
|
||||
#define LV_PROFILER_STYLE_BEGIN_TAG(tag)
|
||||
#define LV_PROFILER_STYLE_END_TAG(tag)
|
||||
#endif
|
||||
|
||||
#if LV_USE_PROFILER && LV_PROFILER_DRAW
|
||||
#define LV_PROFILER_DRAW_BEGIN LV_PROFILER_BEGIN
|
||||
#define LV_PROFILER_DRAW_END LV_PROFILER_END
|
||||
#define LV_PROFILER_DRAW_BEGIN_TAG(tag) LV_PROFILER_BEGIN_TAG(tag)
|
||||
#define LV_PROFILER_DRAW_END_TAG(tag) LV_PROFILER_END_TAG(tag)
|
||||
#else
|
||||
#define LV_PROFILER_DRAW_BEGIN
|
||||
#define LV_PROFILER_DRAW_END
|
||||
#define LV_PROFILER_DRAW_BEGIN_TAG(tag)
|
||||
#define LV_PROFILER_DRAW_END_TAG(tag)
|
||||
#endif
|
||||
|
||||
#if LV_USE_PROFILER && LV_PROFILER_DECODER
|
||||
#define LV_PROFILER_DECODER_BEGIN LV_PROFILER_BEGIN
|
||||
#define LV_PROFILER_DECODER_END LV_PROFILER_END
|
||||
#define LV_PROFILER_DECODER_BEGIN_TAG(tag) LV_PROFILER_BEGIN_TAG(tag)
|
||||
#define LV_PROFILER_DECODER_END_TAG(tag) LV_PROFILER_END_TAG(tag)
|
||||
#else
|
||||
#define LV_PROFILER_DECODER_BEGIN
|
||||
#define LV_PROFILER_DECODER_END
|
||||
#define LV_PROFILER_DECODER_BEGIN_TAG(tag)
|
||||
#define LV_PROFILER_DECODER_END_TAG(tag)
|
||||
#endif
|
||||
|
||||
#if LV_USE_PROFILER && LV_PROFILER_REFR
|
||||
#define LV_PROFILER_REFR_BEGIN LV_PROFILER_BEGIN
|
||||
#define LV_PROFILER_REFR_END LV_PROFILER_END
|
||||
#define LV_PROFILER_REFR_BEGIN_TAG(tag) LV_PROFILER_BEGIN_TAG(tag)
|
||||
#define LV_PROFILER_REFR_END_TAG(tag) LV_PROFILER_END_TAG(tag)
|
||||
#else
|
||||
#define LV_PROFILER_REFR_BEGIN
|
||||
#define LV_PROFILER_REFR_END
|
||||
#define LV_PROFILER_REFR_BEGIN_TAG(tag)
|
||||
#define LV_PROFILER_REFR_END_TAG(tag)
|
||||
#endif
|
||||
|
||||
#if LV_USE_PROFILER && LV_PROFILER_INDEV
|
||||
#define LV_PROFILER_INDEV_BEGIN LV_PROFILER_BEGIN
|
||||
#define LV_PROFILER_INDEV_END LV_PROFILER_END
|
||||
#define LV_PROFILER_INDEV_BEGIN_TAG(tag) LV_PROFILER_BEGIN_TAG(tag)
|
||||
#define LV_PROFILER_INDEV_END_TAG(tag) LV_PROFILER_END_TAG(tag)
|
||||
#else
|
||||
#define LV_PROFILER_INDEV_BEGIN
|
||||
#define LV_PROFILER_INDEV_END
|
||||
#define LV_PROFILER_INDEV_BEGIN_TAG(tag)
|
||||
#define LV_PROFILER_INDEV_END_TAG(tag)
|
||||
#endif
|
||||
|
||||
#if LV_USE_PROFILER && LV_PROFILER_FONT
|
||||
#define LV_PROFILER_FONT_BEGIN LV_PROFILER_BEGIN
|
||||
#define LV_PROFILER_FONT_END LV_PROFILER_END
|
||||
#define LV_PROFILER_FONT_BEGIN_TAG(tag) LV_PROFILER_BEGIN_TAG(tag)
|
||||
#define LV_PROFILER_FONT_END_TAG(tag) LV_PROFILER_END_TAG(tag)
|
||||
#else
|
||||
#define LV_PROFILER_FONT_BEGIN
|
||||
#define LV_PROFILER_FONT_END
|
||||
#define LV_PROFILER_FONT_BEGIN_TAG(tag)
|
||||
#define LV_PROFILER_FONT_END_TAG(tag)
|
||||
#endif
|
||||
|
||||
#if LV_USE_PROFILER && LV_PROFILER_CACHE
|
||||
#define LV_PROFILER_CACHE_BEGIN LV_PROFILER_BEGIN
|
||||
#define LV_PROFILER_CACHE_END LV_PROFILER_END
|
||||
#define LV_PROFILER_CACHE_BEGIN_TAG(tag) LV_PROFILER_BEGIN_TAG(tag)
|
||||
#define LV_PROFILER_CACHE_END_TAG(tag) LV_PROFILER_END_TAG(tag)
|
||||
#else
|
||||
#define LV_PROFILER_CACHE_BEGIN
|
||||
#define LV_PROFILER_CACHE_END
|
||||
#define LV_PROFILER_CACHE_BEGIN_TAG(tag)
|
||||
#define LV_PROFILER_CACHE_END_TAG(tag)
|
||||
#endif
|
||||
|
||||
#if LV_USE_PROFILER && LV_PROFILER_FS
|
||||
#define LV_PROFILER_FS_BEGIN LV_PROFILER_BEGIN
|
||||
#define LV_PROFILER_FS_END LV_PROFILER_END
|
||||
#define LV_PROFILER_FS_BEGIN_TAG(tag) LV_PROFILER_BEGIN_TAG(tag)
|
||||
#define LV_PROFILER_FS_END_TAG(tag) LV_PROFILER_END_TAG(tag)
|
||||
#else
|
||||
#define LV_PROFILER_FS_BEGIN
|
||||
#define LV_PROFILER_FS_END
|
||||
#define LV_PROFILER_FS_BEGIN_TAG(tag)
|
||||
#define LV_PROFILER_FS_END_TAG(tag)
|
||||
#endif
|
||||
|
||||
#if LV_USE_PROFILER && LV_PROFILER_TIMER
|
||||
#define LV_PROFILER_TIMER_BEGIN LV_PROFILER_BEGIN
|
||||
#define LV_PROFILER_TIMER_END LV_PROFILER_END
|
||||
#define LV_PROFILER_TIMER_BEGIN_TAG(tag) LV_PROFILER_BEGIN_TAG(tag)
|
||||
#define LV_PROFILER_TIMER_END_TAG(tag) LV_PROFILER_END_TAG(tag)
|
||||
#else
|
||||
#define LV_PROFILER_TIMER_BEGIN
|
||||
#define LV_PROFILER_TIMER_END
|
||||
#define LV_PROFILER_TIMER_BEGIN_TAG(tag)
|
||||
#define LV_PROFILER_TIMER_END_TAG(tag)
|
||||
#endif
|
||||
|
||||
#if LV_USE_PROFILER && LV_PROFILER_EVENT
|
||||
#define LV_PROFILER_EVENT_BEGIN LV_PROFILER_BEGIN
|
||||
#define LV_PROFILER_EVENT_END LV_PROFILER_END
|
||||
#define LV_PROFILER_EVENT_BEGIN_TAG(tag) LV_PROFILER_BEGIN_TAG(tag)
|
||||
#define LV_PROFILER_EVENT_END_TAG(tag) LV_PROFILER_END_TAG(tag)
|
||||
#else
|
||||
#define LV_PROFILER_EVENT_BEGIN
|
||||
#define LV_PROFILER_EVENT_END
|
||||
#define LV_PROFILER_EVENT_BEGIN_TAG(tag)
|
||||
#define LV_PROFILER_EVENT_END_TAG(tag)
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /*extern "C"*/
|
||||
#endif
|
||||
|
||||
#endif /*LV_PROFILER_H*/
|
||||
@@ -0,0 +1,268 @@
|
||||
/**
|
||||
* @file lv_profiler_builtin.c
|
||||
*
|
||||
*/
|
||||
|
||||
/*********************
|
||||
* INCLUDES
|
||||
*********************/
|
||||
|
||||
#include "lv_profiler_builtin_private.h"
|
||||
#include "../lvgl.h"
|
||||
#include "../core/lv_global.h"
|
||||
|
||||
/*********************
|
||||
* DEFINES
|
||||
*********************/
|
||||
|
||||
#if LV_USE_PROFILER && LV_USE_PROFILER_BUILTIN
|
||||
|
||||
#define profiler_ctx LV_GLOBAL_DEFAULT()->profiler_context
|
||||
|
||||
#define LV_PROFILER_STR_MAX_LEN 128
|
||||
#define LV_PROFILER_TICK_PER_SEC_MAX 1000000000 /* Maximum accuracy: 1 nanosecond */
|
||||
|
||||
#if LV_USE_OS
|
||||
#define LV_PROFILER_MULTEX_INIT lv_mutex_init(&profiler_ctx->mutex)
|
||||
#define LV_PROFILER_MULTEX_DEINIT lv_mutex_delete(&profiler_ctx->mutex)
|
||||
#define LV_PROFILER_MULTEX_LOCK lv_mutex_lock(&profiler_ctx->mutex)
|
||||
#define LV_PROFILER_MULTEX_UNLOCK lv_mutex_unlock(&profiler_ctx->mutex)
|
||||
#else
|
||||
#define LV_PROFILER_MULTEX_INIT
|
||||
#define LV_PROFILER_MULTEX_DEINIT
|
||||
#define LV_PROFILER_MULTEX_LOCK
|
||||
#define LV_PROFILER_MULTEX_UNLOCK
|
||||
#endif
|
||||
|
||||
/**********************
|
||||
* TYPEDEFS
|
||||
**********************/
|
||||
|
||||
/**
|
||||
* @brief Structure representing a built-in profiler item in LVGL
|
||||
*/
|
||||
typedef struct {
|
||||
uint64_t tick; /**< The tick value of the profiler item */
|
||||
char tag; /**< The tag of the profiler item */
|
||||
const char * func; /**< A pointer to the function associated with the profiler item */
|
||||
#if LV_USE_OS
|
||||
int tid; /**< The thread ID of the profiler item */
|
||||
int cpu; /**< The CPU ID of the profiler item */
|
||||
#endif
|
||||
} lv_profiler_builtin_item_t;
|
||||
|
||||
/**
|
||||
* @brief Structure representing a context for the LVGL built-in profiler
|
||||
*/
|
||||
typedef struct _lv_profiler_builtin_ctx_t {
|
||||
lv_profiler_builtin_item_t * item_arr; /**< Pointer to an array of profiler items */
|
||||
uint32_t item_num; /**< Number of profiler items in the array */
|
||||
uint32_t cur_index; /**< Index of the current profiler item */
|
||||
lv_profiler_builtin_config_t config; /**< Configuration for the built-in profiler */
|
||||
bool enable; /**< Whether the built-in profiler is enabled */
|
||||
#if LV_USE_OS
|
||||
lv_mutex_t mutex; /**< Mutex to protect the built-in profiler */
|
||||
#endif
|
||||
} lv_profiler_builtin_ctx_t;
|
||||
|
||||
/**********************
|
||||
* STATIC PROTOTYPES
|
||||
**********************/
|
||||
|
||||
static uint64_t default_tick_get_cb(void);
|
||||
static void default_flush_cb(const char * buf);
|
||||
static int default_tid_get_cb(void);
|
||||
static int default_cpu_get_cb(void);
|
||||
static void flush_no_lock(void);
|
||||
|
||||
/**********************
|
||||
* STATIC VARIABLES
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* MACROS
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* GLOBAL FUNCTIONS
|
||||
**********************/
|
||||
|
||||
void lv_profiler_builtin_config_init(lv_profiler_builtin_config_t * config)
|
||||
{
|
||||
LV_ASSERT_NULL(config);
|
||||
lv_memzero(config, sizeof(lv_profiler_builtin_config_t));
|
||||
config->buf_size = LV_PROFILER_BUILTIN_BUF_SIZE;
|
||||
config->tick_per_sec = 1000;
|
||||
config->tick_get_cb = default_tick_get_cb;
|
||||
config->flush_cb = default_flush_cb;
|
||||
config->tid_get_cb = default_tid_get_cb;
|
||||
config->cpu_get_cb = default_cpu_get_cb;
|
||||
}
|
||||
|
||||
void lv_profiler_builtin_init(const lv_profiler_builtin_config_t * config)
|
||||
{
|
||||
LV_ASSERT_NULL(config);
|
||||
LV_ASSERT_NULL(config->tick_get_cb);
|
||||
|
||||
uint32_t num = config->buf_size / sizeof(lv_profiler_builtin_item_t);
|
||||
if(num == 0) {
|
||||
LV_LOG_WARN("buf_size must > %d", (int)sizeof(lv_profiler_builtin_item_t));
|
||||
return;
|
||||
}
|
||||
|
||||
if(config->tick_per_sec == 0 || config->tick_per_sec > LV_PROFILER_TICK_PER_SEC_MAX) {
|
||||
LV_LOG_WARN("tick_per_sec range must be between 1~%d", LV_PROFILER_TICK_PER_SEC_MAX);
|
||||
return;
|
||||
}
|
||||
|
||||
/*Free the old item_arr memory*/
|
||||
if(profiler_ctx) {
|
||||
lv_profiler_builtin_uninit();
|
||||
}
|
||||
|
||||
profiler_ctx = lv_malloc_zeroed(sizeof(lv_profiler_builtin_ctx_t));
|
||||
LV_ASSERT_MALLOC(profiler_ctx);
|
||||
|
||||
profiler_ctx->item_arr = lv_malloc(num * sizeof(lv_profiler_builtin_item_t));
|
||||
LV_ASSERT_MALLOC(profiler_ctx->item_arr);
|
||||
if(profiler_ctx->item_arr == NULL) {
|
||||
lv_free(profiler_ctx);
|
||||
profiler_ctx = NULL;
|
||||
LV_LOG_ERROR("malloc failed for item_arr");
|
||||
return;
|
||||
}
|
||||
|
||||
LV_PROFILER_MULTEX_INIT;
|
||||
profiler_ctx->item_num = num;
|
||||
profiler_ctx->config = *config;
|
||||
|
||||
if(profiler_ctx->config.flush_cb) {
|
||||
/* add profiler header for perfetto */
|
||||
profiler_ctx->config.flush_cb("# tracer: nop\n");
|
||||
profiler_ctx->config.flush_cb("#\n");
|
||||
}
|
||||
|
||||
lv_profiler_builtin_set_enable(LV_PROFILER_BUILTIN_DEFAULT_ENABLE);
|
||||
|
||||
LV_LOG_INFO("init OK, item_num = %d", (int)num);
|
||||
}
|
||||
|
||||
void lv_profiler_builtin_uninit(void)
|
||||
{
|
||||
LV_ASSERT_NULL(profiler_ctx);
|
||||
LV_PROFILER_MULTEX_DEINIT;
|
||||
lv_free(profiler_ctx->item_arr);
|
||||
lv_free(profiler_ctx);
|
||||
profiler_ctx = NULL;
|
||||
}
|
||||
|
||||
void lv_profiler_builtin_set_enable(bool enable)
|
||||
{
|
||||
if(!profiler_ctx) {
|
||||
return;
|
||||
}
|
||||
|
||||
profiler_ctx->enable = enable;
|
||||
}
|
||||
|
||||
void lv_profiler_builtin_flush(void)
|
||||
{
|
||||
LV_ASSERT_NULL(profiler_ctx);
|
||||
|
||||
LV_PROFILER_MULTEX_LOCK;
|
||||
flush_no_lock();
|
||||
LV_PROFILER_MULTEX_UNLOCK;
|
||||
}
|
||||
|
||||
void lv_profiler_builtin_write(const char * func, char tag)
|
||||
{
|
||||
LV_ASSERT_NULL(profiler_ctx);
|
||||
LV_ASSERT_NULL(func);
|
||||
|
||||
if(!profiler_ctx->enable) {
|
||||
return;
|
||||
}
|
||||
|
||||
LV_PROFILER_MULTEX_LOCK;
|
||||
|
||||
if(profiler_ctx->cur_index >= profiler_ctx->item_num) {
|
||||
flush_no_lock();
|
||||
profiler_ctx->cur_index = 0;
|
||||
}
|
||||
|
||||
lv_profiler_builtin_item_t * item = &profiler_ctx->item_arr[profiler_ctx->cur_index];
|
||||
item->func = func;
|
||||
item->tag = tag;
|
||||
item->tick = profiler_ctx->config.tick_get_cb();
|
||||
|
||||
#if LV_USE_OS
|
||||
item->tid = profiler_ctx->config.tid_get_cb();
|
||||
item->cpu = profiler_ctx->config.cpu_get_cb();
|
||||
#endif
|
||||
|
||||
profiler_ctx->cur_index++;
|
||||
|
||||
LV_PROFILER_MULTEX_UNLOCK;
|
||||
}
|
||||
|
||||
/**********************
|
||||
* STATIC FUNCTIONS
|
||||
**********************/
|
||||
|
||||
static uint64_t default_tick_get_cb(void)
|
||||
{
|
||||
return lv_tick_get();
|
||||
}
|
||||
|
||||
static void default_flush_cb(const char * buf)
|
||||
{
|
||||
LV_LOG("%s", buf);
|
||||
}
|
||||
|
||||
static int default_tid_get_cb(void)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int default_cpu_get_cb(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void flush_no_lock(void)
|
||||
{
|
||||
if(!profiler_ctx->config.flush_cb) {
|
||||
LV_LOG_WARN("flush_cb is not registered");
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t cur = 0;
|
||||
char buf[LV_PROFILER_STR_MAX_LEN];
|
||||
uint32_t tick_per_sec = profiler_ctx->config.tick_per_sec;
|
||||
while(cur < profiler_ctx->cur_index) {
|
||||
lv_profiler_builtin_item_t * item = &profiler_ctx->item_arr[cur++];
|
||||
uint64_t sec = item->tick / tick_per_sec;
|
||||
uint64_t nsec = (item->tick % tick_per_sec) * (LV_PROFILER_TICK_PER_SEC_MAX / tick_per_sec);
|
||||
|
||||
#if LV_USE_OS
|
||||
lv_snprintf(buf, sizeof(buf),
|
||||
" LVGL-%d [%d] %" LV_PRIu64 ".%09" LV_PRIu64 ": tracing_mark_write: %c|1|%s\n",
|
||||
item->tid,
|
||||
item->cpu,
|
||||
sec,
|
||||
nsec,
|
||||
item->tag,
|
||||
item->func);
|
||||
#else
|
||||
lv_snprintf(buf, sizeof(buf),
|
||||
" LVGL-1 [0] %" LV_PRIu64 ".%09" LV_PRIu64 ": tracing_mark_write: %c|1|%s\n",
|
||||
sec,
|
||||
nsec,
|
||||
item->tag,
|
||||
item->func);
|
||||
#endif
|
||||
profiler_ctx->config.flush_cb(buf);
|
||||
}
|
||||
}
|
||||
|
||||
#endif /*LV_USE_PROFILER_BUILTIN*/
|
||||
@@ -0,0 +1,85 @@
|
||||
/**
|
||||
* @file lv_profiler_builtin.h
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef LV_PROFILER_BUILTIN_H
|
||||
#define LV_PROFILER_BUILTIN_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*********************
|
||||
* INCLUDES
|
||||
*********************/
|
||||
|
||||
#include "../lv_conf_internal.h"
|
||||
|
||||
#if LV_USE_PROFILER && LV_USE_PROFILER_BUILTIN
|
||||
|
||||
#include "lv_types.h"
|
||||
|
||||
/*********************
|
||||
* DEFINES
|
||||
*********************/
|
||||
|
||||
#define LV_PROFILER_BUILTIN_BEGIN_TAG(tag) lv_profiler_builtin_write((tag), 'B')
|
||||
#define LV_PROFILER_BUILTIN_END_TAG(tag) lv_profiler_builtin_write((tag), 'E')
|
||||
#define LV_PROFILER_BUILTIN_BEGIN LV_PROFILER_BUILTIN_BEGIN_TAG(__func__)
|
||||
#define LV_PROFILER_BUILTIN_END LV_PROFILER_BUILTIN_END_TAG(__func__)
|
||||
|
||||
/**********************
|
||||
* TYPEDEFS
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* GLOBAL PROTOTYPES
|
||||
**********************/
|
||||
|
||||
/**
|
||||
* @brief Initialize the configuration of the built-in profiler
|
||||
* @param config Pointer to the configuration structure of the built-in profiler
|
||||
*/
|
||||
void lv_profiler_builtin_config_init(lv_profiler_builtin_config_t * config);
|
||||
|
||||
/**
|
||||
* @brief Initialize the built-in profiler with the given configuration
|
||||
* @param config Pointer to the configuration structure of the built-in profiler
|
||||
*/
|
||||
void lv_profiler_builtin_init(const lv_profiler_builtin_config_t * config);
|
||||
|
||||
/**
|
||||
* @brief Uninitialize the built-in profiler
|
||||
*/
|
||||
void lv_profiler_builtin_uninit(void);
|
||||
|
||||
/**
|
||||
* @brief Enable or disable the built-in profiler
|
||||
* @param enable true to enable the built-in profiler, false to disable
|
||||
*/
|
||||
void lv_profiler_builtin_set_enable(bool enable);
|
||||
|
||||
/**
|
||||
* @brief Flush the profiling data to the console
|
||||
*/
|
||||
void lv_profiler_builtin_flush(void);
|
||||
|
||||
/**
|
||||
* @brief Write the profiling data for a function with the given tag
|
||||
* @param func Name of the function being profiled
|
||||
* @param tag Tag to associate with the profiling data for the function
|
||||
*/
|
||||
void lv_profiler_builtin_write(const char * func, char tag);
|
||||
|
||||
/**********************
|
||||
* MACROS
|
||||
**********************/
|
||||
|
||||
#endif /*LV_USE_PROFILER_BUILTIN*/
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /*extern "C"*/
|
||||
#endif
|
||||
|
||||
#endif /*LV_PROFILER_BUILTIN_H*/
|
||||
@@ -0,0 +1,56 @@
|
||||
/**
|
||||
* @file lv_profiler_builtin_private.h
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef LV_PROFILER_BUILTIN_PRIVATE_H
|
||||
#define LV_PROFILER_BUILTIN_PRIVATE_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*********************
|
||||
* INCLUDES
|
||||
*********************/
|
||||
|
||||
#include "lv_profiler_builtin.h"
|
||||
|
||||
#if LV_USE_PROFILER && LV_USE_PROFILER_BUILTIN
|
||||
|
||||
/*********************
|
||||
* DEFINES
|
||||
*********************/
|
||||
|
||||
/**********************
|
||||
* TYPEDEFS
|
||||
**********************/
|
||||
|
||||
/**
|
||||
* @brief LVGL profiler built-in configuration structure
|
||||
*/
|
||||
struct _lv_profiler_builtin_config_t {
|
||||
size_t buf_size; /**< The size of the buffer used for profiling data */
|
||||
uint32_t tick_per_sec; /**< The number of ticks per second */
|
||||
uint64_t (*tick_get_cb)(void); /**< Callback function to get the current tick count */
|
||||
void (*flush_cb)(const char * buf); /**< Callback function to flush the profiling data */
|
||||
int (*tid_get_cb)(void); /**< Callback function to get the current thread ID */
|
||||
int (*cpu_get_cb)(void); /**< Callback function to get the current CPU */
|
||||
};
|
||||
|
||||
|
||||
/**********************
|
||||
* GLOBAL PROTOTYPES
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* MACROS
|
||||
**********************/
|
||||
|
||||
#endif /* LV_USE_PROFILER && LV_USE_PROFILER_BUILTIN */
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /*extern "C"*/
|
||||
#endif
|
||||
|
||||
#endif /*LV_PROFILER_BUILTIN_PRIVATE_H*/
|
||||
@@ -0,0 +1,556 @@
|
||||
/**
|
||||
* @file lv_rb.c
|
||||
*
|
||||
*/
|
||||
|
||||
/*********************
|
||||
* INCLUDES
|
||||
*********************/
|
||||
#include "lv_rb_private.h"
|
||||
#include "../stdlib/lv_string.h"
|
||||
|
||||
/*********************
|
||||
* DEFINES
|
||||
*********************/
|
||||
|
||||
/**********************
|
||||
* TYPEDEFS
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* STATIC PROTOTYPES
|
||||
**********************/
|
||||
|
||||
static lv_rb_node_t * rb_create_node(lv_rb_t * tree);
|
||||
static lv_rb_node_t * rb_find_leaf_parent(lv_rb_t * tree, lv_rb_node_t * node);
|
||||
static void rb_right_rotate(lv_rb_t * tree, lv_rb_node_t * node);
|
||||
static void rb_left_rotate(lv_rb_t * tree, lv_rb_node_t * node);
|
||||
static void rb_insert_color(lv_rb_t * tree, lv_rb_node_t * node);
|
||||
static void rb_delete_color(lv_rb_t * tree, lv_rb_node_t * node1, lv_rb_node_t * node2);
|
||||
|
||||
/**********************
|
||||
* GLOBAL VARIABLES
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* STATIC VARIABLES
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* MACROS
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* GLOBAL FUNCTIONS
|
||||
**********************/
|
||||
|
||||
bool lv_rb_init(lv_rb_t * tree, lv_rb_compare_t compare, size_t node_size)
|
||||
{
|
||||
LV_ASSERT_NULL(tree);
|
||||
LV_ASSERT_NULL(compare);
|
||||
LV_ASSERT(node_size > 0);
|
||||
|
||||
if(tree == NULL || compare == NULL || node_size == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
lv_memzero(tree, sizeof(lv_rb_t));
|
||||
|
||||
tree->root = NULL;
|
||||
tree->compare = compare;
|
||||
tree->size = node_size;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
lv_rb_node_t * lv_rb_insert(lv_rb_t * tree, void * key)
|
||||
{
|
||||
LV_ASSERT_NULL(tree);
|
||||
if(tree == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
lv_rb_node_t * node = lv_rb_find(tree, key);
|
||||
if(node) return node;
|
||||
else {
|
||||
node = rb_create_node(tree);
|
||||
if(node == NULL) return NULL;
|
||||
|
||||
if(tree->root == NULL) {
|
||||
tree->root = node;
|
||||
node->parent = NULL;
|
||||
node->color = LV_RB_COLOR_BLACK;
|
||||
return node;
|
||||
}
|
||||
}
|
||||
|
||||
void * new_data = node->data;
|
||||
node->data = key;
|
||||
lv_rb_node_t * parent = rb_find_leaf_parent(tree, node);
|
||||
|
||||
node->parent = parent;
|
||||
node->color = LV_RB_COLOR_RED;
|
||||
|
||||
if(tree->compare(key, parent->data) < 0) parent->left = node;
|
||||
else parent->right = node;
|
||||
|
||||
rb_insert_color(tree, node);
|
||||
|
||||
node->data = new_data;
|
||||
return node;
|
||||
}
|
||||
|
||||
lv_rb_node_t * lv_rb_find(lv_rb_t * tree, const void * key)
|
||||
{
|
||||
LV_ASSERT_NULL(tree);
|
||||
if(tree == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
lv_rb_node_t * current = tree->root;
|
||||
|
||||
while(current != NULL) {
|
||||
lv_rb_compare_res_t cmp = tree->compare(key, current->data);
|
||||
|
||||
if(cmp == 0) {
|
||||
return current;
|
||||
}
|
||||
else if(cmp < 0) {
|
||||
current = current->left;
|
||||
}
|
||||
else {
|
||||
current = current->right;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void * lv_rb_remove_node(lv_rb_t * tree, lv_rb_node_t * node)
|
||||
{
|
||||
lv_rb_node_t * child = NULL;
|
||||
lv_rb_node_t * parent = NULL;
|
||||
lv_rb_color_t color = LV_RB_COLOR_BLACK;
|
||||
|
||||
if(node->left != NULL && node->right != NULL) {
|
||||
lv_rb_node_t * replace = node;
|
||||
replace = lv_rb_minimum_from(replace->right);
|
||||
|
||||
if(node->parent != NULL) {
|
||||
if(node->parent->left == node) {
|
||||
node->parent->left = replace;
|
||||
}
|
||||
else {
|
||||
node->parent->right = replace;
|
||||
}
|
||||
}
|
||||
else {
|
||||
tree->root = replace;
|
||||
}
|
||||
|
||||
child = replace->right;
|
||||
parent = replace->parent;
|
||||
color = replace->color;
|
||||
|
||||
if(parent == node) {
|
||||
parent = replace;
|
||||
}
|
||||
else {
|
||||
if(child != NULL) {
|
||||
child->parent = parent;
|
||||
}
|
||||
parent->left = child;
|
||||
replace->right = node->right;
|
||||
node->right->parent = replace;
|
||||
}
|
||||
|
||||
replace->parent = node->parent;
|
||||
replace->color = node->color;
|
||||
replace->left = node->left;
|
||||
node->left->parent = replace;
|
||||
|
||||
if(color == LV_RB_COLOR_BLACK) {
|
||||
rb_delete_color(tree, child, parent);
|
||||
}
|
||||
|
||||
void * data = node->data;
|
||||
lv_free(node);
|
||||
return data;
|
||||
}
|
||||
|
||||
child = node->right != NULL ? node->right : node->left;
|
||||
parent = node->parent;
|
||||
color = node->color;
|
||||
|
||||
if(child != NULL) {
|
||||
child->parent = parent;
|
||||
}
|
||||
|
||||
if(parent != NULL) {
|
||||
if(parent->left == node) {
|
||||
parent->left = child;
|
||||
}
|
||||
else {
|
||||
parent->right = child;
|
||||
}
|
||||
}
|
||||
else {
|
||||
tree->root = child;
|
||||
}
|
||||
|
||||
if(color == LV_RB_COLOR_BLACK) {
|
||||
rb_delete_color(tree, child, parent);
|
||||
}
|
||||
|
||||
void * data = node->data;
|
||||
lv_free(node);
|
||||
return data;
|
||||
}
|
||||
|
||||
void * lv_rb_remove(lv_rb_t * tree, const void * key)
|
||||
{
|
||||
LV_ASSERT_NULL(tree);
|
||||
if(tree == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
lv_rb_node_t * node = lv_rb_find(tree, key);
|
||||
LV_ASSERT_NULL(node);
|
||||
if(node == NULL) {
|
||||
LV_LOG_WARN("rb delete %d not found", (int)(uintptr_t)key);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return lv_rb_remove_node(tree, node);
|
||||
}
|
||||
|
||||
bool lv_rb_drop_node(lv_rb_t * tree, lv_rb_node_t * node)
|
||||
{
|
||||
LV_ASSERT_NULL(tree);
|
||||
if(tree == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
void * data = lv_rb_remove_node(tree, node);
|
||||
if(data) {
|
||||
lv_free(data);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool lv_rb_drop(lv_rb_t * tree, const void * key)
|
||||
{
|
||||
LV_ASSERT_NULL(tree);
|
||||
if(tree == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
void * data = lv_rb_remove(tree, key);
|
||||
if(data) {
|
||||
lv_free(data);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void lv_rb_destroy(lv_rb_t * tree)
|
||||
{
|
||||
LV_ASSERT_NULL(tree);
|
||||
|
||||
if(tree == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
lv_rb_node_t * node = tree->root;
|
||||
lv_rb_node_t * parent = NULL;
|
||||
|
||||
while(node != NULL) {
|
||||
if(node->left != NULL) {
|
||||
node = node->left;
|
||||
}
|
||||
else if(node->right != NULL) {
|
||||
node = node->right;
|
||||
}
|
||||
else {
|
||||
parent = node->parent;
|
||||
if(parent != NULL) {
|
||||
if(parent->left == node) {
|
||||
parent->left = NULL;
|
||||
}
|
||||
else {
|
||||
parent->right = NULL;
|
||||
}
|
||||
}
|
||||
lv_free(node->data);
|
||||
lv_free(node);
|
||||
node = parent;
|
||||
}
|
||||
}
|
||||
tree->root = NULL;
|
||||
}
|
||||
|
||||
lv_rb_node_t * lv_rb_minimum(lv_rb_t * tree)
|
||||
{
|
||||
LV_ASSERT_NULL(tree);
|
||||
if(tree == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
return lv_rb_minimum_from(tree->root);
|
||||
}
|
||||
|
||||
lv_rb_node_t * lv_rb_maximum(lv_rb_t * tree)
|
||||
{
|
||||
LV_ASSERT_NULL(tree);
|
||||
if(tree == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
return lv_rb_maximum_from(tree->root);
|
||||
}
|
||||
|
||||
lv_rb_node_t * lv_rb_minimum_from(lv_rb_node_t * node)
|
||||
{
|
||||
while(node->left != NULL) {
|
||||
node = node->left;
|
||||
}
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
lv_rb_node_t * lv_rb_maximum_from(lv_rb_node_t * node)
|
||||
{
|
||||
while(node->right != NULL) {
|
||||
node = node->right;
|
||||
}
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
/**********************
|
||||
* STATIC FUNCTIONS
|
||||
**********************/
|
||||
|
||||
static lv_rb_node_t * rb_create_node(lv_rb_t * tree)
|
||||
{
|
||||
lv_rb_node_t * node = lv_malloc_zeroed(sizeof(lv_rb_node_t));
|
||||
LV_ASSERT_MALLOC(node);
|
||||
if(node == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
node->data = lv_malloc_zeroed(tree->size);
|
||||
LV_ASSERT_MALLOC(node->data);
|
||||
if(node->data == NULL) {
|
||||
lv_free(node);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
node->color = LV_RB_COLOR_RED;
|
||||
node->left = NULL;
|
||||
node->right = NULL;
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
static lv_rb_node_t * rb_find_leaf_parent(lv_rb_t * tree, lv_rb_node_t * node)
|
||||
{
|
||||
lv_rb_node_t * current = tree->root;
|
||||
lv_rb_node_t * parent = current;
|
||||
|
||||
while(current != NULL) {
|
||||
parent = current;
|
||||
|
||||
if(tree->compare(node->data, current->data) < 0) {
|
||||
current = current->left;
|
||||
}
|
||||
else {
|
||||
current = current->right;
|
||||
}
|
||||
}
|
||||
|
||||
return parent;
|
||||
}
|
||||
|
||||
static void rb_right_rotate(lv_rb_t * tree, lv_rb_node_t * node)
|
||||
{
|
||||
lv_rb_node_t * left = node->left;
|
||||
node->left = left->right;
|
||||
|
||||
if(left->right != NULL) {
|
||||
left->right->parent = node;
|
||||
}
|
||||
|
||||
left->parent = node->parent;
|
||||
|
||||
if(node->parent == NULL) {
|
||||
tree->root = left;
|
||||
}
|
||||
else if(node == node->parent->right) {
|
||||
node->parent->right = left;
|
||||
}
|
||||
else {
|
||||
node->parent->left = left;
|
||||
}
|
||||
|
||||
left->right = node;
|
||||
node->parent = left;
|
||||
}
|
||||
|
||||
static void rb_left_rotate(lv_rb_t * tree, lv_rb_node_t * node)
|
||||
{
|
||||
lv_rb_node_t * right = node->right;
|
||||
node->right = right->left;
|
||||
|
||||
if(right->left != NULL) {
|
||||
right->left->parent = node;
|
||||
}
|
||||
|
||||
right->parent = node->parent;
|
||||
|
||||
if(node->parent == NULL) {
|
||||
tree->root = right;
|
||||
}
|
||||
else if(node == node->parent->left) {
|
||||
node->parent->left = right;
|
||||
}
|
||||
else {
|
||||
node->parent->right = right;
|
||||
}
|
||||
|
||||
right->left = node;
|
||||
node->parent = right;
|
||||
}
|
||||
|
||||
static void rb_insert_color(lv_rb_t * tree, lv_rb_node_t * node)
|
||||
{
|
||||
lv_rb_node_t * parent = NULL;
|
||||
lv_rb_node_t * gparent = NULL;
|
||||
|
||||
while((parent = node->parent) && parent->color == LV_RB_COLOR_RED) {
|
||||
gparent = parent->parent;
|
||||
|
||||
if(parent == gparent->left) {
|
||||
{
|
||||
lv_rb_node_t * uncle = gparent->right;
|
||||
if(uncle && uncle->color == LV_RB_COLOR_RED) {
|
||||
uncle->color = LV_RB_COLOR_BLACK;
|
||||
parent->color = LV_RB_COLOR_BLACK;
|
||||
gparent->color = LV_RB_COLOR_RED;
|
||||
node = gparent;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if(parent->right == node) {
|
||||
lv_rb_node_t * tmp;
|
||||
rb_left_rotate(tree, parent);
|
||||
tmp = parent;
|
||||
parent = node;
|
||||
node = tmp;
|
||||
}
|
||||
|
||||
parent->color = LV_RB_COLOR_BLACK;
|
||||
gparent->color = LV_RB_COLOR_RED;
|
||||
rb_right_rotate(tree, gparent);
|
||||
}
|
||||
else {
|
||||
{
|
||||
lv_rb_node_t * uncle = gparent->left;
|
||||
if(uncle && uncle->color == LV_RB_COLOR_RED) {
|
||||
uncle->color = LV_RB_COLOR_BLACK;
|
||||
parent->color = LV_RB_COLOR_BLACK;
|
||||
gparent->color = LV_RB_COLOR_RED;
|
||||
node = gparent;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if(parent->left == node) {
|
||||
lv_rb_node_t * tmp;
|
||||
rb_right_rotate(tree, parent);
|
||||
tmp = parent;
|
||||
parent = node;
|
||||
node = tmp;
|
||||
}
|
||||
|
||||
parent->color = LV_RB_COLOR_BLACK;
|
||||
gparent->color = LV_RB_COLOR_RED;
|
||||
rb_left_rotate(tree, gparent);
|
||||
}
|
||||
}
|
||||
|
||||
tree->root->color = LV_RB_COLOR_BLACK;
|
||||
}
|
||||
|
||||
static void rb_delete_color(lv_rb_t * tree, lv_rb_node_t * node1, lv_rb_node_t * node2)
|
||||
{
|
||||
LV_ASSERT_NULL(tree);
|
||||
if(tree == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
while((node1 == NULL || node1->color == LV_RB_COLOR_BLACK) && node1 != tree->root) {
|
||||
if(node2->left == node1) {
|
||||
lv_rb_node_t * pNode2 = node2->right;
|
||||
if(pNode2->color == LV_RB_COLOR_RED) {
|
||||
pNode2->color = LV_RB_COLOR_BLACK;
|
||||
node2->color = LV_RB_COLOR_RED;
|
||||
rb_left_rotate(tree, node2);
|
||||
pNode2 = node2->right;
|
||||
}
|
||||
|
||||
if((pNode2->left == NULL || pNode2->left->color == LV_RB_COLOR_BLACK)
|
||||
&& (pNode2->right == NULL || pNode2->right->color == LV_RB_COLOR_BLACK)) {
|
||||
pNode2->color = LV_RB_COLOR_RED;
|
||||
node1 = node2;
|
||||
node2 = node2->parent;
|
||||
}
|
||||
else {
|
||||
if(pNode2->right == NULL || pNode2->right->color == LV_RB_COLOR_BLACK) {
|
||||
pNode2->left->color = LV_RB_COLOR_BLACK;
|
||||
pNode2->color = LV_RB_COLOR_RED;
|
||||
rb_right_rotate(tree, pNode2);
|
||||
pNode2 = node2->right;
|
||||
}
|
||||
pNode2->color = node2->color;
|
||||
node2->color = LV_RB_COLOR_BLACK;
|
||||
pNode2->right->color = LV_RB_COLOR_BLACK;
|
||||
rb_left_rotate(tree, node2);
|
||||
node1 = tree->root;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
lv_rb_node_t * pNode2 = node2->left;
|
||||
if(pNode2->color == LV_RB_COLOR_RED) {
|
||||
pNode2->color = LV_RB_COLOR_BLACK;
|
||||
node2->color = LV_RB_COLOR_RED;
|
||||
rb_right_rotate(tree, node2);
|
||||
pNode2 = node2->left;
|
||||
}
|
||||
|
||||
if((pNode2->left == NULL || pNode2->left->color == LV_RB_COLOR_BLACK)
|
||||
&& (pNode2->right == NULL || pNode2->right->color == LV_RB_COLOR_BLACK)) {
|
||||
pNode2->color = LV_RB_COLOR_RED;
|
||||
node1 = node2;
|
||||
node2 = node2->parent;
|
||||
}
|
||||
else {
|
||||
if(pNode2->left == NULL || pNode2->left->color == LV_RB_COLOR_BLACK) {
|
||||
pNode2->right->color = LV_RB_COLOR_BLACK;
|
||||
pNode2->color = LV_RB_COLOR_RED;
|
||||
rb_left_rotate(tree, pNode2);
|
||||
pNode2 = node2->left;
|
||||
}
|
||||
pNode2->color = node2->color;
|
||||
node2->color = LV_RB_COLOR_BLACK;
|
||||
pNode2->left->color = LV_RB_COLOR_BLACK;
|
||||
rb_right_rotate(tree, node2);
|
||||
node1 = tree->root;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(node1 != NULL)
|
||||
node1->color = LV_RB_COLOR_BLACK;
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
/**
|
||||
* @file lv_rb.h
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef LV_RB_H
|
||||
#define LV_RB_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*********************
|
||||
* INCLUDES
|
||||
*********************/
|
||||
#include "lv_types.h"
|
||||
#include "lv_assert.h"
|
||||
#include "lv_types.h"
|
||||
|
||||
/*********************
|
||||
* DEFINES
|
||||
*********************/
|
||||
|
||||
/**********************
|
||||
* TYPEDEFS
|
||||
**********************/
|
||||
|
||||
typedef enum {
|
||||
LV_RB_COLOR_RED,
|
||||
LV_RB_COLOR_BLACK
|
||||
} lv_rb_color_t;
|
||||
|
||||
typedef int8_t lv_rb_compare_res_t;
|
||||
|
||||
typedef lv_rb_compare_res_t (*lv_rb_compare_t)(const void * a, const void * b);
|
||||
|
||||
/**********************
|
||||
* GLOBAL PROTOTYPES
|
||||
**********************/
|
||||
|
||||
bool lv_rb_init(lv_rb_t * tree, lv_rb_compare_t compare, size_t node_size);
|
||||
lv_rb_node_t * lv_rb_insert(lv_rb_t * tree, void * key);
|
||||
lv_rb_node_t * lv_rb_find(lv_rb_t * tree, const void * key);
|
||||
void * lv_rb_remove_node(lv_rb_t * tree, lv_rb_node_t * node);
|
||||
void * lv_rb_remove(lv_rb_t * tree, const void * key);
|
||||
bool lv_rb_drop_node(lv_rb_t * tree, lv_rb_node_t * node);
|
||||
bool lv_rb_drop(lv_rb_t * tree, const void * key);
|
||||
lv_rb_node_t * lv_rb_minimum(lv_rb_t * node);
|
||||
lv_rb_node_t * lv_rb_maximum(lv_rb_t * node);
|
||||
lv_rb_node_t * lv_rb_minimum_from(lv_rb_node_t * node);
|
||||
lv_rb_node_t * lv_rb_maximum_from(lv_rb_node_t * node);
|
||||
void lv_rb_destroy(lv_rb_t * tree);
|
||||
|
||||
/*************************
|
||||
* GLOBAL VARIABLES
|
||||
*************************/
|
||||
|
||||
/**********************
|
||||
* MACROS
|
||||
**********************/
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /*extern "C"*/
|
||||
#endif
|
||||
|
||||
#endif /*LV_RB_H*/
|
||||
@@ -0,0 +1,54 @@
|
||||
/**
|
||||
* @file lv_rb_private.h
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef LV_RB_PRIVATE_H
|
||||
#define LV_RB_PRIVATE_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*********************
|
||||
* INCLUDES
|
||||
*********************/
|
||||
|
||||
#include "lv_rb.h"
|
||||
|
||||
/*********************
|
||||
* DEFINES
|
||||
*********************/
|
||||
|
||||
/**********************
|
||||
* TYPEDEFS
|
||||
**********************/
|
||||
|
||||
struct _lv_rb_node_t {
|
||||
struct _lv_rb_node_t * parent;
|
||||
struct _lv_rb_node_t * left;
|
||||
struct _lv_rb_node_t * right;
|
||||
lv_rb_color_t color;
|
||||
void * data;
|
||||
};
|
||||
|
||||
struct _lv_rb_t {
|
||||
lv_rb_node_t * root;
|
||||
lv_rb_compare_t compare;
|
||||
size_t size;
|
||||
};
|
||||
|
||||
|
||||
/**********************
|
||||
* GLOBAL PROTOTYPES
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* MACROS
|
||||
**********************/
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /*extern "C"*/
|
||||
#endif
|
||||
|
||||
#endif /*LV_RB_PRIVATE_H*/
|
||||
@@ -0,0 +1,489 @@
|
||||
/**
|
||||
* @file lv_style.c
|
||||
*
|
||||
*/
|
||||
|
||||
/*********************
|
||||
* INCLUDES
|
||||
*********************/
|
||||
#include "lv_style_private.h"
|
||||
#include "../core/lv_global.h"
|
||||
#include "../stdlib/lv_mem.h"
|
||||
#include "../stdlib/lv_string.h"
|
||||
#include "lv_assert.h"
|
||||
#include "lv_types.h"
|
||||
|
||||
/*********************
|
||||
* DEFINES
|
||||
*********************/
|
||||
#define lv_style_custom_prop_flag_lookup_table_size LV_GLOBAL_DEFAULT()->style_custom_table_size
|
||||
#define lv_style_custom_prop_flag_lookup_table LV_GLOBAL_DEFAULT()->style_custom_prop_flag_lookup_table
|
||||
#define last_custom_prop_id LV_GLOBAL_DEFAULT()->style_last_custom_prop_id
|
||||
|
||||
/**********************
|
||||
* TYPEDEFS
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* STATIC PROTOTYPES
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* GLOBAL VARIABLES
|
||||
**********************/
|
||||
|
||||
const lv_style_prop_t lv_style_const_prop_id_inv = LV_STYLE_PROP_INV;
|
||||
|
||||
const uint8_t lv_style_builtin_prop_flag_lookup_table[LV_STYLE_NUM_BUILT_IN_PROPS] = {
|
||||
[LV_STYLE_WIDTH] = LV_STYLE_PROP_FLAG_LAYOUT_UPDATE,
|
||||
[LV_STYLE_MIN_WIDTH] = LV_STYLE_PROP_FLAG_LAYOUT_UPDATE,
|
||||
[LV_STYLE_MAX_WIDTH] = LV_STYLE_PROP_FLAG_LAYOUT_UPDATE,
|
||||
[LV_STYLE_HEIGHT] = LV_STYLE_PROP_FLAG_LAYOUT_UPDATE,
|
||||
[LV_STYLE_MIN_HEIGHT] = LV_STYLE_PROP_FLAG_LAYOUT_UPDATE,
|
||||
[LV_STYLE_MAX_HEIGHT] = LV_STYLE_PROP_FLAG_LAYOUT_UPDATE,
|
||||
[LV_STYLE_LENGTH] = LV_STYLE_PROP_FLAG_EXT_DRAW_UPDATE,
|
||||
[LV_STYLE_X] = LV_STYLE_PROP_FLAG_LAYOUT_UPDATE,
|
||||
[LV_STYLE_Y] = LV_STYLE_PROP_FLAG_LAYOUT_UPDATE,
|
||||
[LV_STYLE_ALIGN] = LV_STYLE_PROP_FLAG_LAYOUT_UPDATE,
|
||||
[LV_STYLE_TRANSFORM_WIDTH] = LV_STYLE_PROP_FLAG_EXT_DRAW_UPDATE | LV_STYLE_PROP_FLAG_TRANSFORM,
|
||||
[LV_STYLE_TRANSFORM_HEIGHT] = LV_STYLE_PROP_FLAG_EXT_DRAW_UPDATE | LV_STYLE_PROP_FLAG_TRANSFORM,
|
||||
[LV_STYLE_TRANSLATE_X] = LV_STYLE_PROP_FLAG_LAYOUT_UPDATE | LV_STYLE_PROP_FLAG_PARENT_LAYOUT_UPDATE,
|
||||
[LV_STYLE_TRANSLATE_Y] = LV_STYLE_PROP_FLAG_LAYOUT_UPDATE | LV_STYLE_PROP_FLAG_PARENT_LAYOUT_UPDATE,
|
||||
[LV_STYLE_TRANSFORM_SCALE_X] = LV_STYLE_PROP_FLAG_EXT_DRAW_UPDATE | LV_STYLE_PROP_FLAG_LAYER_UPDATE | LV_STYLE_PROP_FLAG_TRANSFORM,
|
||||
[LV_STYLE_TRANSFORM_SCALE_Y] = LV_STYLE_PROP_FLAG_EXT_DRAW_UPDATE | LV_STYLE_PROP_FLAG_LAYER_UPDATE | LV_STYLE_PROP_FLAG_TRANSFORM,
|
||||
[LV_STYLE_TRANSFORM_SKEW_X] = LV_STYLE_PROP_FLAG_EXT_DRAW_UPDATE | LV_STYLE_PROP_FLAG_LAYER_UPDATE | LV_STYLE_PROP_FLAG_TRANSFORM,
|
||||
[LV_STYLE_TRANSFORM_SKEW_Y] = LV_STYLE_PROP_FLAG_EXT_DRAW_UPDATE | LV_STYLE_PROP_FLAG_LAYER_UPDATE | LV_STYLE_PROP_FLAG_TRANSFORM,
|
||||
[LV_STYLE_TRANSFORM_ROTATION] = LV_STYLE_PROP_FLAG_EXT_DRAW_UPDATE | LV_STYLE_PROP_FLAG_LAYER_UPDATE | LV_STYLE_PROP_FLAG_TRANSFORM,
|
||||
|
||||
[LV_STYLE_PAD_TOP] = LV_STYLE_PROP_FLAG_EXT_DRAW_UPDATE | LV_STYLE_PROP_FLAG_LAYOUT_UPDATE,
|
||||
[LV_STYLE_PAD_BOTTOM] = LV_STYLE_PROP_FLAG_EXT_DRAW_UPDATE | LV_STYLE_PROP_FLAG_LAYOUT_UPDATE,
|
||||
[LV_STYLE_PAD_LEFT] = LV_STYLE_PROP_FLAG_EXT_DRAW_UPDATE | LV_STYLE_PROP_FLAG_LAYOUT_UPDATE,
|
||||
[LV_STYLE_PAD_RIGHT] = LV_STYLE_PROP_FLAG_EXT_DRAW_UPDATE | LV_STYLE_PROP_FLAG_LAYOUT_UPDATE,
|
||||
[LV_STYLE_PAD_ROW] = LV_STYLE_PROP_FLAG_EXT_DRAW_UPDATE | LV_STYLE_PROP_FLAG_LAYOUT_UPDATE,
|
||||
[LV_STYLE_PAD_COLUMN] = LV_STYLE_PROP_FLAG_EXT_DRAW_UPDATE | LV_STYLE_PROP_FLAG_LAYOUT_UPDATE,
|
||||
[LV_STYLE_MARGIN_TOP] = LV_STYLE_PROP_FLAG_EXT_DRAW_UPDATE | LV_STYLE_PROP_FLAG_LAYOUT_UPDATE,
|
||||
[LV_STYLE_MARGIN_BOTTOM] = LV_STYLE_PROP_FLAG_EXT_DRAW_UPDATE | LV_STYLE_PROP_FLAG_LAYOUT_UPDATE,
|
||||
[LV_STYLE_MARGIN_LEFT] = LV_STYLE_PROP_FLAG_EXT_DRAW_UPDATE | LV_STYLE_PROP_FLAG_LAYOUT_UPDATE,
|
||||
[LV_STYLE_MARGIN_RIGHT] = LV_STYLE_PROP_FLAG_EXT_DRAW_UPDATE | LV_STYLE_PROP_FLAG_LAYOUT_UPDATE,
|
||||
|
||||
[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_MAIN_OPA] = 0,
|
||||
[LV_STYLE_BG_GRAD_OPA] = 0,
|
||||
[LV_STYLE_BG_GRAD] = 0,
|
||||
|
||||
[LV_STYLE_BG_IMAGE_SRC] = LV_STYLE_PROP_FLAG_EXT_DRAW_UPDATE,
|
||||
[LV_STYLE_BG_IMAGE_OPA] = 0,
|
||||
[LV_STYLE_BG_IMAGE_RECOLOR] = 0,
|
||||
[LV_STYLE_BG_IMAGE_RECOLOR_OPA] = 0,
|
||||
[LV_STYLE_BG_IMAGE_TILED] = 0,
|
||||
|
||||
[LV_STYLE_BORDER_COLOR] = 0,
|
||||
[LV_STYLE_BORDER_OPA] = 0,
|
||||
[LV_STYLE_BORDER_WIDTH] = LV_STYLE_PROP_FLAG_LAYOUT_UPDATE,
|
||||
[LV_STYLE_BORDER_SIDE] = 0,
|
||||
[LV_STYLE_BORDER_POST] = 0,
|
||||
|
||||
[LV_STYLE_OUTLINE_WIDTH] = LV_STYLE_PROP_FLAG_EXT_DRAW_UPDATE,
|
||||
[LV_STYLE_OUTLINE_COLOR] = 0,
|
||||
[LV_STYLE_OUTLINE_OPA] = LV_STYLE_PROP_FLAG_EXT_DRAW_UPDATE,
|
||||
[LV_STYLE_OUTLINE_PAD] = LV_STYLE_PROP_FLAG_EXT_DRAW_UPDATE,
|
||||
|
||||
[LV_STYLE_SHADOW_WIDTH] = LV_STYLE_PROP_FLAG_EXT_DRAW_UPDATE,
|
||||
[LV_STYLE_SHADOW_OFFSET_X] = LV_STYLE_PROP_FLAG_EXT_DRAW_UPDATE,
|
||||
[LV_STYLE_SHADOW_OFFSET_Y] = LV_STYLE_PROP_FLAG_EXT_DRAW_UPDATE,
|
||||
[LV_STYLE_SHADOW_SPREAD] = LV_STYLE_PROP_FLAG_EXT_DRAW_UPDATE,
|
||||
[LV_STYLE_SHADOW_COLOR] = 0,
|
||||
[LV_STYLE_SHADOW_OPA] = LV_STYLE_PROP_FLAG_EXT_DRAW_UPDATE,
|
||||
|
||||
[LV_STYLE_IMAGE_OPA] = 0,
|
||||
[LV_STYLE_IMAGE_RECOLOR] = 0,
|
||||
[LV_STYLE_IMAGE_RECOLOR_OPA] = 0,
|
||||
|
||||
[LV_STYLE_LINE_WIDTH] = LV_STYLE_PROP_FLAG_EXT_DRAW_UPDATE,
|
||||
[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_FLAG_EXT_DRAW_UPDATE,
|
||||
[LV_STYLE_ARC_ROUNDED] = 0,
|
||||
[LV_STYLE_ARC_COLOR] = 0,
|
||||
[LV_STYLE_ARC_OPA] = 0,
|
||||
[LV_STYLE_ARC_IMAGE_SRC] = 0,
|
||||
|
||||
[LV_STYLE_TEXT_COLOR] = LV_STYLE_PROP_FLAG_INHERITABLE,
|
||||
[LV_STYLE_TEXT_OPA] = LV_STYLE_PROP_FLAG_INHERITABLE,
|
||||
[LV_STYLE_TEXT_FONT] = LV_STYLE_PROP_FLAG_INHERITABLE | LV_STYLE_PROP_FLAG_LAYOUT_UPDATE,
|
||||
[LV_STYLE_TEXT_LETTER_SPACE] = LV_STYLE_PROP_FLAG_INHERITABLE | LV_STYLE_PROP_FLAG_LAYOUT_UPDATE,
|
||||
[LV_STYLE_TEXT_LINE_SPACE] = LV_STYLE_PROP_FLAG_INHERITABLE | LV_STYLE_PROP_FLAG_LAYOUT_UPDATE,
|
||||
[LV_STYLE_TEXT_DECOR] = LV_STYLE_PROP_FLAG_INHERITABLE,
|
||||
[LV_STYLE_TEXT_ALIGN] = LV_STYLE_PROP_FLAG_INHERITABLE | LV_STYLE_PROP_FLAG_LAYOUT_UPDATE,
|
||||
|
||||
[LV_STYLE_RADIUS] = 0,
|
||||
[LV_STYLE_CLIP_CORNER] = 0,
|
||||
[LV_STYLE_OPA] = 0,
|
||||
[LV_STYLE_OPA_LAYERED] = LV_STYLE_PROP_FLAG_LAYER_UPDATE,
|
||||
[LV_STYLE_COLOR_FILTER_DSC] = LV_STYLE_PROP_FLAG_INHERITABLE,
|
||||
[LV_STYLE_COLOR_FILTER_OPA] = LV_STYLE_PROP_FLAG_INHERITABLE,
|
||||
[LV_STYLE_ANIM_DURATION] = 0,
|
||||
[LV_STYLE_TRANSITION] = 0,
|
||||
[LV_STYLE_BLEND_MODE] = LV_STYLE_PROP_FLAG_LAYER_UPDATE,
|
||||
[LV_STYLE_LAYOUT] = LV_STYLE_PROP_FLAG_LAYOUT_UPDATE,
|
||||
[LV_STYLE_BASE_DIR] = LV_STYLE_PROP_FLAG_INHERITABLE | LV_STYLE_PROP_FLAG_LAYOUT_UPDATE,
|
||||
[LV_STYLE_BITMAP_MASK_SRC] = LV_STYLE_PROP_FLAG_LAYER_UPDATE,
|
||||
[LV_STYLE_RECOLOR] = 0,
|
||||
[LV_STYLE_RECOLOR_OPA] = 0,
|
||||
|
||||
#if LV_USE_FLEX
|
||||
[LV_STYLE_FLEX_FLOW] = LV_STYLE_PROP_FLAG_LAYOUT_UPDATE,
|
||||
[LV_STYLE_FLEX_MAIN_PLACE] = LV_STYLE_PROP_FLAG_LAYOUT_UPDATE,
|
||||
[LV_STYLE_FLEX_CROSS_PLACE] = LV_STYLE_PROP_FLAG_LAYOUT_UPDATE,
|
||||
[LV_STYLE_FLEX_TRACK_PLACE] = LV_STYLE_PROP_FLAG_LAYOUT_UPDATE,
|
||||
[LV_STYLE_FLEX_GROW] = LV_STYLE_PROP_FLAG_LAYOUT_UPDATE,
|
||||
#endif
|
||||
|
||||
#if LV_USE_GRID
|
||||
[LV_STYLE_GRID_COLUMN_DSC_ARRAY] = LV_STYLE_PROP_FLAG_LAYOUT_UPDATE,
|
||||
[LV_STYLE_GRID_ROW_DSC_ARRAY] = LV_STYLE_PROP_FLAG_LAYOUT_UPDATE,
|
||||
[LV_STYLE_GRID_COLUMN_ALIGN] = LV_STYLE_PROP_FLAG_LAYOUT_UPDATE,
|
||||
[LV_STYLE_GRID_ROW_ALIGN] = LV_STYLE_PROP_FLAG_LAYOUT_UPDATE,
|
||||
[LV_STYLE_GRID_CELL_ROW_SPAN] = LV_STYLE_PROP_FLAG_LAYOUT_UPDATE,
|
||||
[LV_STYLE_GRID_CELL_ROW_POS] = LV_STYLE_PROP_FLAG_LAYOUT_UPDATE,
|
||||
[LV_STYLE_GRID_CELL_COLUMN_SPAN] = LV_STYLE_PROP_FLAG_LAYOUT_UPDATE,
|
||||
[LV_STYLE_GRID_CELL_COLUMN_POS] = LV_STYLE_PROP_FLAG_LAYOUT_UPDATE,
|
||||
[LV_STYLE_GRID_CELL_X_ALIGN] = LV_STYLE_PROP_FLAG_LAYOUT_UPDATE,
|
||||
[LV_STYLE_GRID_CELL_Y_ALIGN] = LV_STYLE_PROP_FLAG_LAYOUT_UPDATE,
|
||||
#endif
|
||||
|
||||
};
|
||||
|
||||
/**********************
|
||||
* STATIC VARIABLES
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* 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_memzero(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->prop_cnt != 255) lv_free(style->values_and_props);
|
||||
lv_memzero(style, sizeof(lv_style_t));
|
||||
#if LV_USE_ASSERT_STYLE
|
||||
style->sentinel = LV_STYLE_SENTINEL_VALUE;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void lv_style_copy(lv_style_t * dst, const lv_style_t * src)
|
||||
{
|
||||
if(lv_style_is_const(dst)) {
|
||||
LV_LOG_WARN("The destination can not be a constant style");
|
||||
return;
|
||||
}
|
||||
|
||||
lv_style_reset(dst);
|
||||
|
||||
/*Source is empty*/
|
||||
if(src->values_and_props == NULL) return;
|
||||
if(src->prop_cnt == 0) return;
|
||||
|
||||
int32_t i;
|
||||
if(lv_style_is_const(src)) {
|
||||
lv_style_const_prop_t * props_and_values = (lv_style_const_prop_t *)src->values_and_props;
|
||||
for(i = 0; props_and_values[i].prop != LV_STYLE_PROP_INV; i++) {
|
||||
lv_style_set_prop(dst, props_and_values[i].prop, props_and_values[i].value);
|
||||
}
|
||||
}
|
||||
else {
|
||||
lv_style_prop_t * props = (lv_style_prop_t *)src->values_and_props + src->prop_cnt * sizeof(lv_style_value_t);
|
||||
lv_style_value_t * values = (lv_style_value_t *)src->values_and_props;
|
||||
for(i = 0; i < src->prop_cnt; i++) {
|
||||
lv_style_set_prop(dst, props[i], values[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
lv_style_prop_t lv_style_register_prop(uint8_t flag)
|
||||
{
|
||||
if(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) != 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_style_custom_prop_flag_lookup_table;
|
||||
uint8_t * new_p = lv_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_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_style_custom_prop_flag_lookup_table);
|
||||
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(lv_style_is_const(style)) {
|
||||
LV_LOG_ERROR("Cannot remove prop from const style");
|
||||
return false;
|
||||
}
|
||||
|
||||
if(style->prop_cnt == 0) return false;
|
||||
|
||||
LV_PROFILER_STYLE_BEGIN;
|
||||
|
||||
uint8_t * tmp = (lv_style_prop_t *)style->values_and_props + style->prop_cnt * sizeof(lv_style_value_t);
|
||||
uint8_t * old_props = (uint8_t *)tmp;
|
||||
uint32_t i;
|
||||
for(i = 0; i < style->prop_cnt; i++) {
|
||||
if(old_props[i] == prop) {
|
||||
lv_style_value_t * old_values = (lv_style_value_t *)style->values_and_props;
|
||||
|
||||
size_t size = (style->prop_cnt - 1) * (sizeof(lv_style_value_t) + sizeof(lv_style_prop_t));
|
||||
uint8_t * new_values_and_props = lv_malloc(size);
|
||||
if(new_values_and_props == NULL) {
|
||||
LV_PROFILER_STYLE_END;
|
||||
return false;
|
||||
}
|
||||
|
||||
style->values_and_props = new_values_and_props;
|
||||
style->prop_cnt--;
|
||||
|
||||
tmp = new_values_and_props + style->prop_cnt * sizeof(lv_style_value_t);
|
||||
uint8_t * new_props = (uint8_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_free(old_values);
|
||||
LV_PROFILER_STYLE_END;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
LV_PROFILER_STYLE_END;
|
||||
return false;
|
||||
}
|
||||
|
||||
void lv_style_set_prop(lv_style_t * style, lv_style_prop_t prop, lv_style_value_t value)
|
||||
{
|
||||
LV_ASSERT_STYLE(style);
|
||||
|
||||
if(lv_style_is_const(style)) {
|
||||
LV_LOG_ERROR("Cannot set property of constant style");
|
||||
return;
|
||||
}
|
||||
|
||||
LV_ASSERT(prop != LV_STYLE_PROP_INV);
|
||||
LV_PROFILER_STYLE_BEGIN;
|
||||
lv_style_prop_t * props;
|
||||
int32_t i;
|
||||
|
||||
if(style->values_and_props) {
|
||||
props = (lv_style_prop_t *)style->values_and_props + style->prop_cnt * sizeof(lv_style_value_t);
|
||||
for(i = style->prop_cnt - 1; i >= 0; i--) {
|
||||
if(props[i] == prop) {
|
||||
lv_style_value_t * values = (lv_style_value_t *)style->values_and_props;
|
||||
values[i] = value;
|
||||
LV_PROFILER_STYLE_END;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
size_t size = (style->prop_cnt + 1) * (sizeof(lv_style_value_t) + sizeof(lv_style_prop_t));
|
||||
uint8_t * values_and_props = lv_realloc(style->values_and_props, size);
|
||||
if(values_and_props == NULL) {
|
||||
LV_PROFILER_STYLE_END;
|
||||
return;
|
||||
}
|
||||
|
||||
style->values_and_props = values_and_props;
|
||||
|
||||
props = values_and_props + style->prop_cnt * sizeof(lv_style_value_t);
|
||||
/*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(lv_style_prop_t)] = props[i];
|
||||
}
|
||||
style->prop_cnt++;
|
||||
|
||||
/*Go to the new position with the props*/
|
||||
props = values_and_props + style->prop_cnt * sizeof(lv_style_value_t);
|
||||
lv_style_value_t * values = (lv_style_value_t *)values_and_props;
|
||||
|
||||
/*Set the new property and value*/
|
||||
props[style->prop_cnt - 1] = prop;
|
||||
values[style->prop_cnt - 1] = value;
|
||||
|
||||
uint32_t group = lv_style_get_prop_group(prop);
|
||||
style->has_group |= (uint32_t)1 << group;
|
||||
LV_PROFILER_STYLE_END;
|
||||
}
|
||||
|
||||
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_memzero(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;
|
||||
tr->user_data = user_data;
|
||||
}
|
||||
|
||||
lv_style_value_t lv_style_prop_get_default(lv_style_prop_t prop)
|
||||
{
|
||||
const lv_color_t black = LV_COLOR_MAKE(0x00, 0x00, 0x00);
|
||||
const lv_color_t white = LV_COLOR_MAKE(0xff, 0xff, 0xff);
|
||||
switch(prop) {
|
||||
case LV_STYLE_TRANSFORM_SCALE_X:
|
||||
case LV_STYLE_TRANSFORM_SCALE_Y:
|
||||
return (lv_style_value_t) {
|
||||
.num = LV_SCALE_NONE
|
||||
};
|
||||
case LV_STYLE_BG_COLOR:
|
||||
return (lv_style_value_t) {
|
||||
.color = white
|
||||
};
|
||||
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_IMAGE_RECOLOR:
|
||||
case LV_STYLE_RECOLOR:
|
||||
return (lv_style_value_t) {
|
||||
.color = black
|
||||
};
|
||||
case LV_STYLE_OPA:
|
||||
case LV_STYLE_OPA_LAYERED:
|
||||
case LV_STYLE_BORDER_OPA:
|
||||
case LV_STYLE_TEXT_OPA:
|
||||
case LV_STYLE_IMAGE_OPA:
|
||||
case LV_STYLE_BG_GRAD_OPA:
|
||||
case LV_STYLE_BG_MAIN_OPA:
|
||||
case LV_STYLE_BG_IMAGE_OPA:
|
||||
case LV_STYLE_OUTLINE_OPA:
|
||||
case LV_STYLE_SHADOW_OPA:
|
||||
case LV_STYLE_LINE_OPA:
|
||||
case LV_STYLE_ARC_OPA:
|
||||
return (lv_style_value_t) {
|
||||
.num = LV_OPA_COVER
|
||||
};
|
||||
case LV_STYLE_BG_GRAD_STOP:
|
||||
return (lv_style_value_t) {
|
||||
.num = 255
|
||||
};
|
||||
case LV_STYLE_BORDER_SIDE:
|
||||
return (lv_style_value_t) {
|
||||
.num = LV_BORDER_SIDE_FULL
|
||||
};
|
||||
case LV_STYLE_TEXT_FONT:
|
||||
return (lv_style_value_t) {
|
||||
.ptr = LV_FONT_DEFAULT
|
||||
};
|
||||
case LV_STYLE_MAX_WIDTH:
|
||||
case LV_STYLE_MAX_HEIGHT:
|
||||
return (lv_style_value_t) {
|
||||
.num = LV_COORD_MAX
|
||||
};
|
||||
case LV_STYLE_ROTARY_SENSITIVITY:
|
||||
return (lv_style_value_t) {
|
||||
.num = 256
|
||||
};
|
||||
default:
|
||||
return (lv_style_value_t) {
|
||||
.ptr = NULL
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
bool lv_style_is_empty(const lv_style_t * style)
|
||||
{
|
||||
LV_ASSERT_STYLE(style);
|
||||
|
||||
return style->prop_cnt == 0;
|
||||
}
|
||||
|
||||
uint8_t lv_style_prop_lookup_flags(lv_style_prop_t prop)
|
||||
{
|
||||
if(prop == LV_STYLE_PROP_ANY) return LV_STYLE_PROP_FLAG_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_style_custom_prop_flag_lookup_table != NULL && prop < lv_style_custom_prop_flag_lookup_table_size)
|
||||
return lv_style_custom_prop_flag_lookup_table[prop];
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**********************
|
||||
* STATIC FUNCTIONS
|
||||
**********************/
|
||||
@@ -0,0 +1,622 @@
|
||||
/**
|
||||
* @file lv_style.h
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef LV_STYLE_H
|
||||
#define LV_STYLE_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*********************
|
||||
* INCLUDES
|
||||
*********************/
|
||||
#include "../font/lv_font.h"
|
||||
#include "lv_color.h"
|
||||
#include "lv_area.h"
|
||||
#include "lv_anim.h"
|
||||
#include "lv_text.h"
|
||||
#include "lv_types.h"
|
||||
#include "lv_assert.h"
|
||||
#include "lv_bidi.h"
|
||||
#include "lv_grad.h"
|
||||
#include "../layouts/lv_layout.h"
|
||||
|
||||
/*********************
|
||||
* DEFINES
|
||||
*********************/
|
||||
|
||||
#define LV_STYLE_SENTINEL_VALUE 0xAABBCCDD
|
||||
|
||||
/*
|
||||
* Flags for style behavior
|
||||
*/
|
||||
#define LV_STYLE_PROP_FLAG_NONE (0) /**< No special behavior */
|
||||
#define LV_STYLE_PROP_FLAG_INHERITABLE (1 << 0) /**< Inherited */
|
||||
#define LV_STYLE_PROP_FLAG_EXT_DRAW_UPDATE (1 << 1) /**< Requires ext. draw size update when changed */
|
||||
#define LV_STYLE_PROP_FLAG_LAYOUT_UPDATE (1 << 2) /**< Requires layout update when changed */
|
||||
#define LV_STYLE_PROP_FLAG_PARENT_LAYOUT_UPDATE (1 << 3) /**< Requires layout update on parent when changed */
|
||||
#define LV_STYLE_PROP_FLAG_LAYER_UPDATE (1 << 4) /**< Affects layer handling */
|
||||
#define LV_STYLE_PROP_FLAG_TRANSFORM (1 << 5) /**< Affects the object's transformation */
|
||||
#define LV_STYLE_PROP_FLAG_ALL (0x3F) /**< Indicating all flags */
|
||||
|
||||
/*
|
||||
* Other constants
|
||||
*/
|
||||
#define LV_SCALE_NONE 256 /**< Value for not zooming the image */
|
||||
LV_EXPORT_CONST_INT(LV_SCALE_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, \
|
||||
.values_and_props = (void*)prop_array, \
|
||||
.has_group = 0xFFFFFFFF, \
|
||||
.prop_cnt = 255 \
|
||||
}
|
||||
#else
|
||||
#define LV_STYLE_CONST_INIT(var_name, prop_array) \
|
||||
const lv_style_t var_name = { \
|
||||
.values_and_props = (void*)prop_array, \
|
||||
.has_group = 0xFFFFFFFF, \
|
||||
.prop_cnt = 255, \
|
||||
}
|
||||
#endif
|
||||
// *INDENT-ON*
|
||||
|
||||
#define LV_STYLE_CONST_PROPS_END { .prop = LV_STYLE_PROP_INV, .value = { .num = 0 } }
|
||||
|
||||
#if LV_GRADIENT_MAX_STOPS < 2
|
||||
#error LVGL needs at least 2 stops for gradients. Please increase the LV_GRADIENT_MAX_STOPS
|
||||
#endif
|
||||
|
||||
#define LV_GRAD_LEFT LV_PCT(0)
|
||||
#define LV_GRAD_RIGHT LV_PCT(100)
|
||||
#define LV_GRAD_TOP LV_PCT(0)
|
||||
#define LV_GRAD_BOTTOM LV_PCT(100)
|
||||
#define LV_GRAD_CENTER LV_PCT(50)
|
||||
|
||||
/**********************
|
||||
* TYPEDEFS
|
||||
**********************/
|
||||
|
||||
/**
|
||||
* Possible options for blending opaque drawings
|
||||
*/
|
||||
typedef 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_DIFFERENCE, /**< Absolute difference between foreground and background*/
|
||||
} lv_blend_mode_t;
|
||||
|
||||
/**
|
||||
* Some options to apply decorations on texts.
|
||||
* 'OR'ed values can be used.
|
||||
*/
|
||||
typedef enum {
|
||||
LV_TEXT_DECOR_NONE = 0x00,
|
||||
LV_TEXT_DECOR_UNDERLINE = 0x01,
|
||||
LV_TEXT_DECOR_STRIKETHROUGH = 0x02,
|
||||
} lv_text_decor_t;
|
||||
|
||||
/**
|
||||
* Selects on which sides border should be drawn
|
||||
* 'OR'ed values can be used.
|
||||
*/
|
||||
typedef 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)*/
|
||||
} lv_border_side_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.
|
||||
*/
|
||||
enum {
|
||||
LV_STYLE_PROP_INV = 0,
|
||||
|
||||
/*Group 0*/
|
||||
LV_STYLE_WIDTH = 1,
|
||||
LV_STYLE_HEIGHT = 2,
|
||||
LV_STYLE_LENGTH = 3,
|
||||
|
||||
LV_STYLE_MIN_WIDTH = 4,
|
||||
LV_STYLE_MAX_WIDTH = 5,
|
||||
LV_STYLE_MIN_HEIGHT = 6,
|
||||
LV_STYLE_MAX_HEIGHT = 7,
|
||||
|
||||
LV_STYLE_X = 8,
|
||||
LV_STYLE_Y = 9,
|
||||
LV_STYLE_ALIGN = 10,
|
||||
|
||||
LV_STYLE_RADIUS = 12,
|
||||
LV_STYLE_RADIAL_OFFSET = 13,
|
||||
LV_STYLE_PAD_RADIAL = 14,
|
||||
|
||||
/*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_LAYOUT = 22,
|
||||
|
||||
LV_STYLE_MARGIN_TOP = 24,
|
||||
LV_STYLE_MARGIN_BOTTOM = 25,
|
||||
LV_STYLE_MARGIN_LEFT = 26,
|
||||
LV_STYLE_MARGIN_RIGHT = 27,
|
||||
|
||||
/*Group 2*/
|
||||
LV_STYLE_BG_COLOR = 28,
|
||||
LV_STYLE_BG_OPA = 29,
|
||||
|
||||
LV_STYLE_BG_GRAD_DIR = 32,
|
||||
LV_STYLE_BG_MAIN_STOP = 33,
|
||||
LV_STYLE_BG_GRAD_STOP = 34,
|
||||
LV_STYLE_BG_GRAD_COLOR = 35,
|
||||
|
||||
LV_STYLE_BG_MAIN_OPA = 36,
|
||||
LV_STYLE_BG_GRAD_OPA = 37,
|
||||
LV_STYLE_BG_GRAD = 38,
|
||||
LV_STYLE_BASE_DIR = 39,
|
||||
|
||||
LV_STYLE_BG_IMAGE_SRC = 40,
|
||||
LV_STYLE_BG_IMAGE_OPA = 41,
|
||||
LV_STYLE_BG_IMAGE_RECOLOR = 42,
|
||||
LV_STYLE_BG_IMAGE_RECOLOR_OPA = 43,
|
||||
|
||||
LV_STYLE_BG_IMAGE_TILED = 44,
|
||||
LV_STYLE_CLIP_CORNER = 45,
|
||||
|
||||
/*Group 3*/
|
||||
LV_STYLE_BORDER_WIDTH = 48,
|
||||
LV_STYLE_BORDER_COLOR = 49,
|
||||
LV_STYLE_BORDER_OPA = 50,
|
||||
|
||||
LV_STYLE_BORDER_SIDE = 52,
|
||||
LV_STYLE_BORDER_POST = 53,
|
||||
|
||||
LV_STYLE_OUTLINE_WIDTH = 56,
|
||||
LV_STYLE_OUTLINE_COLOR = 57,
|
||||
LV_STYLE_OUTLINE_OPA = 58,
|
||||
LV_STYLE_OUTLINE_PAD = 59,
|
||||
|
||||
/*Group 4*/
|
||||
LV_STYLE_SHADOW_WIDTH = 60,
|
||||
LV_STYLE_SHADOW_COLOR = 61,
|
||||
LV_STYLE_SHADOW_OPA = 62,
|
||||
|
||||
LV_STYLE_SHADOW_OFFSET_X = 64,
|
||||
LV_STYLE_SHADOW_OFFSET_Y = 65,
|
||||
LV_STYLE_SHADOW_SPREAD = 66,
|
||||
|
||||
LV_STYLE_IMAGE_OPA = 68,
|
||||
LV_STYLE_IMAGE_RECOLOR = 69,
|
||||
LV_STYLE_IMAGE_RECOLOR_OPA = 70,
|
||||
|
||||
LV_STYLE_LINE_WIDTH = 72,
|
||||
LV_STYLE_LINE_DASH_WIDTH = 73,
|
||||
LV_STYLE_LINE_DASH_GAP = 74,
|
||||
LV_STYLE_LINE_ROUNDED = 75,
|
||||
LV_STYLE_LINE_COLOR = 76,
|
||||
LV_STYLE_LINE_OPA = 77,
|
||||
|
||||
/*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_IMAGE_SRC = 84,
|
||||
|
||||
LV_STYLE_TEXT_COLOR = 88,
|
||||
LV_STYLE_TEXT_OPA = 89,
|
||||
LV_STYLE_TEXT_FONT = 90,
|
||||
|
||||
LV_STYLE_TEXT_LETTER_SPACE = 91,
|
||||
LV_STYLE_TEXT_LINE_SPACE = 92,
|
||||
LV_STYLE_TEXT_DECOR = 93,
|
||||
LV_STYLE_TEXT_ALIGN = 94,
|
||||
LV_STYLE_TEXT_OUTLINE_STROKE_WIDTH = 95,
|
||||
LV_STYLE_TEXT_OUTLINE_STROKE_OPA = 96,
|
||||
LV_STYLE_TEXT_OUTLINE_STROKE_COLOR = 97,
|
||||
|
||||
LV_STYLE_OPA = 98,
|
||||
LV_STYLE_OPA_LAYERED = 99,
|
||||
LV_STYLE_COLOR_FILTER_DSC = 100,
|
||||
LV_STYLE_COLOR_FILTER_OPA = 101,
|
||||
LV_STYLE_ANIM = 102,
|
||||
LV_STYLE_ANIM_DURATION = 103,
|
||||
LV_STYLE_TRANSITION = 104,
|
||||
LV_STYLE_BLEND_MODE = 105,
|
||||
LV_STYLE_TRANSFORM_WIDTH = 106,
|
||||
LV_STYLE_TRANSFORM_HEIGHT = 107,
|
||||
LV_STYLE_TRANSLATE_X = 108,
|
||||
LV_STYLE_TRANSLATE_Y = 109,
|
||||
LV_STYLE_TRANSFORM_SCALE_X = 110,
|
||||
LV_STYLE_TRANSFORM_SCALE_Y = 111,
|
||||
LV_STYLE_TRANSFORM_ROTATION = 112,
|
||||
LV_STYLE_TRANSFORM_PIVOT_X = 113,
|
||||
LV_STYLE_TRANSFORM_PIVOT_Y = 114,
|
||||
LV_STYLE_TRANSFORM_SKEW_X = 115,
|
||||
LV_STYLE_TRANSFORM_SKEW_Y = 116,
|
||||
LV_STYLE_BITMAP_MASK_SRC = 117,
|
||||
LV_STYLE_ROTARY_SENSITIVITY = 118,
|
||||
LV_STYLE_TRANSLATE_RADIAL = 119,
|
||||
LV_STYLE_RECOLOR = 120,
|
||||
LV_STYLE_RECOLOR_OPA = 121,
|
||||
|
||||
LV_STYLE_FLEX_FLOW = 122,
|
||||
LV_STYLE_FLEX_MAIN_PLACE = 123,
|
||||
LV_STYLE_FLEX_CROSS_PLACE = 124,
|
||||
LV_STYLE_FLEX_TRACK_PLACE = 125,
|
||||
LV_STYLE_FLEX_GROW = 126,
|
||||
|
||||
LV_STYLE_GRID_COLUMN_ALIGN = 127,
|
||||
LV_STYLE_GRID_ROW_ALIGN = 128,
|
||||
LV_STYLE_GRID_ROW_DSC_ARRAY = 129,
|
||||
LV_STYLE_GRID_COLUMN_DSC_ARRAY = 130,
|
||||
LV_STYLE_GRID_CELL_COLUMN_POS = 131,
|
||||
LV_STYLE_GRID_CELL_COLUMN_SPAN = 132,
|
||||
LV_STYLE_GRID_CELL_X_ALIGN = 133,
|
||||
LV_STYLE_GRID_CELL_ROW_POS = 134,
|
||||
LV_STYLE_GRID_CELL_ROW_SPAN = 135,
|
||||
LV_STYLE_GRID_CELL_Y_ALIGN = 136,
|
||||
|
||||
LV_STYLE_LAST_BUILT_IN_PROP = 137,
|
||||
|
||||
LV_STYLE_NUM_BUILT_IN_PROPS = LV_STYLE_LAST_BUILT_IN_PROP + 1,
|
||||
|
||||
LV_STYLE_PROP_ANY = 0xFF,
|
||||
LV_STYLE_PROP_CONST = 0xFF /* magic value for const styles */
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
LV_STYLE_RES_NOT_FOUND,
|
||||
LV_STYLE_RES_FOUND,
|
||||
} lv_style_res_t;
|
||||
|
||||
/**
|
||||
* Descriptor for style transitions
|
||||
*/
|
||||
typedef struct {
|
||||
const lv_style_prop_t * props; /**< An array with the properties to animate.*/
|
||||
void * user_data; /**< A custom user data that will be passed to the animation's user_data */
|
||||
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
|
||||
|
||||
void * values_and_props;
|
||||
|
||||
uint32_t has_group;
|
||||
uint8_t prop_cnt; /**< 255 means it's a constant style*/
|
||||
} 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);
|
||||
|
||||
/**
|
||||
* Copy all properties of a style to an other.
|
||||
* It has the same affect callying the same `lv_set_style_...`
|
||||
* functions on both styles.
|
||||
* It means new memory will be allocated to store the properties in
|
||||
* the destination style.
|
||||
* After the copy the destination style is fully independent of the source
|
||||
* and source can removed without affecting the destination style.
|
||||
* @param dst the destination to copy into (can not the a constant style)
|
||||
* @param src the source style to copy from.
|
||||
*/
|
||||
void lv_style_copy(lv_style_t * dst, const lv_style_t * src);
|
||||
|
||||
|
||||
/**
|
||||
* Check if a style is constant
|
||||
* @param style pointer to a style
|
||||
* @return true: the style is constant
|
||||
*/
|
||||
static inline bool lv_style_is_const(const lv_style_t * style)
|
||||
{
|
||||
if(style->prop_cnt == 255) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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:
|
||||
* @code
|
||||
* 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));
|
||||
* @endcode
|
||||
*/
|
||||
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_<prop_name>()`. 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);
|
||||
|
||||
/**
|
||||
* 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_RESULT_INVALID: the property wasn't found in the style (`value` is unchanged)
|
||||
* LV_RESULT_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:
|
||||
* @code
|
||||
* 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);
|
||||
* @endcode
|
||||
*/
|
||||
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_RESULT_INVALID: the property wasn't found in the style (`value` is unchanged)
|
||||
* LV_RESULT_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(lv_style_is_const(style)) {
|
||||
lv_style_const_prop_t * props = (lv_style_const_prop_t *)style->values_and_props;
|
||||
uint32_t i;
|
||||
for(i = 0; props[i].prop != LV_STYLE_PROP_INV; i++) {
|
||||
if(props[i].prop == prop) {
|
||||
*value = props[i].value;
|
||||
return LV_STYLE_RES_FOUND;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
lv_style_prop_t * props = (lv_style_prop_t *)style->values_and_props + style->prop_cnt * sizeof(lv_style_value_t);
|
||||
uint32_t i;
|
||||
for(i = 0; i < style->prop_cnt; i++) {
|
||||
if(props[i] == prop) {
|
||||
lv_style_value_t * values = (lv_style_value_t *)style->values_and_props;
|
||||
*value = values[i];
|
||||
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..30] 30 means all the custom properties with index > 120
|
||||
*/
|
||||
static inline uint32_t lv_style_get_prop_group(lv_style_prop_t prop)
|
||||
{
|
||||
uint32_t group = prop >> 2;
|
||||
if(group > 30) group = 31; /*The MSB marks all the custom properties*/
|
||||
return group;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 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, int32_t width, int32_t height)
|
||||
{
|
||||
lv_style_set_width(style, width);
|
||||
lv_style_set_height(style, height);
|
||||
}
|
||||
|
||||
static inline void lv_style_set_pad_all(lv_style_t * style, int32_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, int32_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, int32_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, int32_t value)
|
||||
{
|
||||
lv_style_set_pad_row(style, value);
|
||||
lv_style_set_pad_column(style, value);
|
||||
}
|
||||
|
||||
static inline void lv_style_set_margin_hor(lv_style_t * style, int32_t value)
|
||||
{
|
||||
lv_style_set_margin_left(style, value);
|
||||
lv_style_set_margin_right(style, value);
|
||||
}
|
||||
|
||||
static inline void lv_style_set_margin_ver(lv_style_t * style, int32_t value)
|
||||
{
|
||||
lv_style_set_margin_top(style, value);
|
||||
lv_style_set_margin_bottom(style, value);
|
||||
}
|
||||
|
||||
static inline void lv_style_set_margin_all(lv_style_t * style, int32_t value)
|
||||
{
|
||||
lv_style_set_margin_left(style, value);
|
||||
lv_style_set_margin_right(style, value);
|
||||
lv_style_set_margin_top(style, value);
|
||||
lv_style_set_margin_bottom(style, value);
|
||||
}
|
||||
|
||||
static inline void lv_style_set_transform_scale(lv_style_t * style, int32_t value)
|
||||
{
|
||||
lv_style_set_transform_scale_x(style, value);
|
||||
lv_style_set_transform_scale_y(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
|
||||
*************************/
|
||||
|
||||
LV_ATTRIBUTE_EXTERN_DATA extern const lv_style_prop_t lv_style_const_prop_id_inv;
|
||||
|
||||
/**********************
|
||||
* 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*/
|
||||
@@ -0,0 +1,961 @@
|
||||
|
||||
/*
|
||||
**********************************************************************
|
||||
* DO NOT EDIT
|
||||
* This file is automatically generated by "style_api_gen.py"
|
||||
**********************************************************************
|
||||
*/
|
||||
|
||||
|
||||
#include "lv_style.h"
|
||||
|
||||
|
||||
void lv_style_set_width(lv_style_t * style, int32_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, int32_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, int32_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, int32_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, int32_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, int32_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_length(lv_style_t * style, int32_t value)
|
||||
{
|
||||
lv_style_value_t v = {
|
||||
.num = (int32_t)value
|
||||
};
|
||||
lv_style_set_prop(style, LV_STYLE_LENGTH, v);
|
||||
}
|
||||
|
||||
void lv_style_set_x(lv_style_t * style, int32_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, int32_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, int32_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, int32_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, int32_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, int32_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_translate_radial(lv_style_t * style, int32_t value)
|
||||
{
|
||||
lv_style_value_t v = {
|
||||
.num = (int32_t)value
|
||||
};
|
||||
lv_style_set_prop(style, LV_STYLE_TRANSLATE_RADIAL, v);
|
||||
}
|
||||
|
||||
void lv_style_set_transform_scale_x(lv_style_t * style, int32_t value)
|
||||
{
|
||||
lv_style_value_t v = {
|
||||
.num = (int32_t)value
|
||||
};
|
||||
lv_style_set_prop(style, LV_STYLE_TRANSFORM_SCALE_X, v);
|
||||
}
|
||||
|
||||
void lv_style_set_transform_scale_y(lv_style_t * style, int32_t value)
|
||||
{
|
||||
lv_style_value_t v = {
|
||||
.num = (int32_t)value
|
||||
};
|
||||
lv_style_set_prop(style, LV_STYLE_TRANSFORM_SCALE_Y, v);
|
||||
}
|
||||
|
||||
void lv_style_set_transform_rotation(lv_style_t * style, int32_t value)
|
||||
{
|
||||
lv_style_value_t v = {
|
||||
.num = (int32_t)value
|
||||
};
|
||||
lv_style_set_prop(style, LV_STYLE_TRANSFORM_ROTATION, v);
|
||||
}
|
||||
|
||||
void lv_style_set_transform_pivot_x(lv_style_t * style, int32_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, int32_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_transform_skew_x(lv_style_t * style, int32_t value)
|
||||
{
|
||||
lv_style_value_t v = {
|
||||
.num = (int32_t)value
|
||||
};
|
||||
lv_style_set_prop(style, LV_STYLE_TRANSFORM_SKEW_X, v);
|
||||
}
|
||||
|
||||
void lv_style_set_transform_skew_y(lv_style_t * style, int32_t value)
|
||||
{
|
||||
lv_style_value_t v = {
|
||||
.num = (int32_t)value
|
||||
};
|
||||
lv_style_set_prop(style, LV_STYLE_TRANSFORM_SKEW_Y, v);
|
||||
}
|
||||
|
||||
void lv_style_set_pad_top(lv_style_t * style, int32_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, int32_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, int32_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, int32_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, int32_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, int32_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_pad_radial(lv_style_t * style, int32_t value)
|
||||
{
|
||||
lv_style_value_t v = {
|
||||
.num = (int32_t)value
|
||||
};
|
||||
lv_style_set_prop(style, LV_STYLE_PAD_RADIAL, v);
|
||||
}
|
||||
|
||||
void lv_style_set_margin_top(lv_style_t * style, int32_t value)
|
||||
{
|
||||
lv_style_value_t v = {
|
||||
.num = (int32_t)value
|
||||
};
|
||||
lv_style_set_prop(style, LV_STYLE_MARGIN_TOP, v);
|
||||
}
|
||||
|
||||
void lv_style_set_margin_bottom(lv_style_t * style, int32_t value)
|
||||
{
|
||||
lv_style_value_t v = {
|
||||
.num = (int32_t)value
|
||||
};
|
||||
lv_style_set_prop(style, LV_STYLE_MARGIN_BOTTOM, v);
|
||||
}
|
||||
|
||||
void lv_style_set_margin_left(lv_style_t * style, int32_t value)
|
||||
{
|
||||
lv_style_value_t v = {
|
||||
.num = (int32_t)value
|
||||
};
|
||||
lv_style_set_prop(style, LV_STYLE_MARGIN_LEFT, v);
|
||||
}
|
||||
|
||||
void lv_style_set_margin_right(lv_style_t * style, int32_t value)
|
||||
{
|
||||
lv_style_value_t v = {
|
||||
.num = (int32_t)value
|
||||
};
|
||||
lv_style_set_prop(style, LV_STYLE_MARGIN_RIGHT, 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, int32_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, int32_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_main_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_MAIN_OPA, v);
|
||||
}
|
||||
|
||||
void lv_style_set_bg_grad_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_GRAD_OPA, 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_image_src(lv_style_t * style, const void * value)
|
||||
{
|
||||
lv_style_value_t v = {
|
||||
.ptr = value
|
||||
};
|
||||
lv_style_set_prop(style, LV_STYLE_BG_IMAGE_SRC, v);
|
||||
}
|
||||
|
||||
void lv_style_set_bg_image_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_IMAGE_OPA, v);
|
||||
}
|
||||
|
||||
void lv_style_set_bg_image_recolor(lv_style_t * style, lv_color_t value)
|
||||
{
|
||||
lv_style_value_t v = {
|
||||
.color = value
|
||||
};
|
||||
lv_style_set_prop(style, LV_STYLE_BG_IMAGE_RECOLOR, v);
|
||||
}
|
||||
|
||||
void lv_style_set_bg_image_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_IMAGE_RECOLOR_OPA, v);
|
||||
}
|
||||
|
||||
void lv_style_set_bg_image_tiled(lv_style_t * style, bool value)
|
||||
{
|
||||
lv_style_value_t v = {
|
||||
.num = (int32_t)value
|
||||
};
|
||||
lv_style_set_prop(style, LV_STYLE_BG_IMAGE_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, int32_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, int32_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, int32_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, int32_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_offset_x(lv_style_t * style, int32_t value)
|
||||
{
|
||||
lv_style_value_t v = {
|
||||
.num = (int32_t)value
|
||||
};
|
||||
lv_style_set_prop(style, LV_STYLE_SHADOW_OFFSET_X, v);
|
||||
}
|
||||
|
||||
void lv_style_set_shadow_offset_y(lv_style_t * style, int32_t value)
|
||||
{
|
||||
lv_style_value_t v = {
|
||||
.num = (int32_t)value
|
||||
};
|
||||
lv_style_set_prop(style, LV_STYLE_SHADOW_OFFSET_Y, v);
|
||||
}
|
||||
|
||||
void lv_style_set_shadow_spread(lv_style_t * style, int32_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_image_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_IMAGE_OPA, v);
|
||||
}
|
||||
|
||||
void lv_style_set_image_recolor(lv_style_t * style, lv_color_t value)
|
||||
{
|
||||
lv_style_value_t v = {
|
||||
.color = value
|
||||
};
|
||||
lv_style_set_prop(style, LV_STYLE_IMAGE_RECOLOR, v);
|
||||
}
|
||||
|
||||
void lv_style_set_image_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_IMAGE_RECOLOR_OPA, v);
|
||||
}
|
||||
|
||||
void lv_style_set_line_width(lv_style_t * style, int32_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, int32_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, int32_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, int32_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_image_src(lv_style_t * style, const void * value)
|
||||
{
|
||||
lv_style_value_t v = {
|
||||
.ptr = value
|
||||
};
|
||||
lv_style_set_prop(style, LV_STYLE_ARC_IMAGE_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, int32_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, int32_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_text_outline_stroke_color(lv_style_t * style, lv_color_t value)
|
||||
{
|
||||
lv_style_value_t v = {
|
||||
.color = value
|
||||
};
|
||||
lv_style_set_prop(style, LV_STYLE_TEXT_OUTLINE_STROKE_COLOR, v);
|
||||
}
|
||||
|
||||
void lv_style_set_text_outline_stroke_width(lv_style_t * style, int32_t value)
|
||||
{
|
||||
lv_style_value_t v = {
|
||||
.num = (int32_t)value
|
||||
};
|
||||
lv_style_set_prop(style, LV_STYLE_TEXT_OUTLINE_STROKE_WIDTH, v);
|
||||
}
|
||||
|
||||
void lv_style_set_text_outline_stroke_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_OUTLINE_STROKE_OPA, v);
|
||||
}
|
||||
|
||||
void lv_style_set_radius(lv_style_t * style, int32_t value)
|
||||
{
|
||||
lv_style_value_t v = {
|
||||
.num = (int32_t)value
|
||||
};
|
||||
lv_style_set_prop(style, LV_STYLE_RADIUS, v);
|
||||
}
|
||||
|
||||
void lv_style_set_radial_offset(lv_style_t * style, int32_t value)
|
||||
{
|
||||
lv_style_value_t v = {
|
||||
.num = (int32_t)value
|
||||
};
|
||||
lv_style_set_prop(style, LV_STYLE_RADIAL_OFFSET, 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_opa_layered(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_LAYERED, 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_recolor(lv_style_t * style, lv_color_t value)
|
||||
{
|
||||
lv_style_value_t v = {
|
||||
.color = value
|
||||
};
|
||||
lv_style_set_prop(style, LV_STYLE_RECOLOR, v);
|
||||
}
|
||||
|
||||
void lv_style_set_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_RECOLOR_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_duration(lv_style_t * style, uint32_t value)
|
||||
{
|
||||
lv_style_value_t v = {
|
||||
.num = (int32_t)value
|
||||
};
|
||||
lv_style_set_prop(style, LV_STYLE_ANIM_DURATION, 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);
|
||||
}
|
||||
|
||||
void lv_style_set_bitmap_mask_src(lv_style_t * style, const void * value)
|
||||
{
|
||||
lv_style_value_t v = {
|
||||
.ptr = value
|
||||
};
|
||||
lv_style_set_prop(style, LV_STYLE_BITMAP_MASK_SRC, v);
|
||||
}
|
||||
|
||||
void lv_style_set_rotary_sensitivity(lv_style_t * style, uint32_t value)
|
||||
{
|
||||
lv_style_value_t v = {
|
||||
.num = (int32_t)value
|
||||
};
|
||||
lv_style_set_prop(style, LV_STYLE_ROTARY_SENSITIVITY, v);
|
||||
}
|
||||
#if LV_USE_FLEX
|
||||
|
||||
void lv_style_set_flex_flow(lv_style_t * style, lv_flex_flow_t value)
|
||||
{
|
||||
lv_style_value_t v = {
|
||||
.num = (int32_t)value
|
||||
};
|
||||
lv_style_set_prop(style, LV_STYLE_FLEX_FLOW, v);
|
||||
}
|
||||
|
||||
void lv_style_set_flex_main_place(lv_style_t * style, lv_flex_align_t value)
|
||||
{
|
||||
lv_style_value_t v = {
|
||||
.num = (int32_t)value
|
||||
};
|
||||
lv_style_set_prop(style, LV_STYLE_FLEX_MAIN_PLACE, v);
|
||||
}
|
||||
|
||||
void lv_style_set_flex_cross_place(lv_style_t * style, lv_flex_align_t value)
|
||||
{
|
||||
lv_style_value_t v = {
|
||||
.num = (int32_t)value
|
||||
};
|
||||
lv_style_set_prop(style, LV_STYLE_FLEX_CROSS_PLACE, v);
|
||||
}
|
||||
|
||||
void lv_style_set_flex_track_place(lv_style_t * style, lv_flex_align_t value)
|
||||
{
|
||||
lv_style_value_t v = {
|
||||
.num = (int32_t)value
|
||||
};
|
||||
lv_style_set_prop(style, LV_STYLE_FLEX_TRACK_PLACE, v);
|
||||
}
|
||||
|
||||
void lv_style_set_flex_grow(lv_style_t * style, uint8_t value)
|
||||
{
|
||||
lv_style_value_t v = {
|
||||
.num = (int32_t)value
|
||||
};
|
||||
lv_style_set_prop(style, LV_STYLE_FLEX_GROW, v);
|
||||
}
|
||||
#endif /*LV_USE_FLEX*/
|
||||
|
||||
#if LV_USE_GRID
|
||||
|
||||
void lv_style_set_grid_column_dsc_array(lv_style_t * style, const int32_t * value)
|
||||
{
|
||||
lv_style_value_t v = {
|
||||
.ptr = value
|
||||
};
|
||||
lv_style_set_prop(style, LV_STYLE_GRID_COLUMN_DSC_ARRAY, v);
|
||||
}
|
||||
|
||||
void lv_style_set_grid_column_align(lv_style_t * style, lv_grid_align_t value)
|
||||
{
|
||||
lv_style_value_t v = {
|
||||
.num = (int32_t)value
|
||||
};
|
||||
lv_style_set_prop(style, LV_STYLE_GRID_COLUMN_ALIGN, v);
|
||||
}
|
||||
|
||||
void lv_style_set_grid_row_dsc_array(lv_style_t * style, const int32_t * value)
|
||||
{
|
||||
lv_style_value_t v = {
|
||||
.ptr = value
|
||||
};
|
||||
lv_style_set_prop(style, LV_STYLE_GRID_ROW_DSC_ARRAY, v);
|
||||
}
|
||||
|
||||
void lv_style_set_grid_row_align(lv_style_t * style, lv_grid_align_t value)
|
||||
{
|
||||
lv_style_value_t v = {
|
||||
.num = (int32_t)value
|
||||
};
|
||||
lv_style_set_prop(style, LV_STYLE_GRID_ROW_ALIGN, v);
|
||||
}
|
||||
|
||||
void lv_style_set_grid_cell_column_pos(lv_style_t * style, int32_t value)
|
||||
{
|
||||
lv_style_value_t v = {
|
||||
.num = (int32_t)value
|
||||
};
|
||||
lv_style_set_prop(style, LV_STYLE_GRID_CELL_COLUMN_POS, v);
|
||||
}
|
||||
|
||||
void lv_style_set_grid_cell_x_align(lv_style_t * style, lv_grid_align_t value)
|
||||
{
|
||||
lv_style_value_t v = {
|
||||
.num = (int32_t)value
|
||||
};
|
||||
lv_style_set_prop(style, LV_STYLE_GRID_CELL_X_ALIGN, v);
|
||||
}
|
||||
|
||||
void lv_style_set_grid_cell_column_span(lv_style_t * style, int32_t value)
|
||||
{
|
||||
lv_style_value_t v = {
|
||||
.num = (int32_t)value
|
||||
};
|
||||
lv_style_set_prop(style, LV_STYLE_GRID_CELL_COLUMN_SPAN, v);
|
||||
}
|
||||
|
||||
void lv_style_set_grid_cell_row_pos(lv_style_t * style, int32_t value)
|
||||
{
|
||||
lv_style_value_t v = {
|
||||
.num = (int32_t)value
|
||||
};
|
||||
lv_style_set_prop(style, LV_STYLE_GRID_CELL_ROW_POS, v);
|
||||
}
|
||||
|
||||
void lv_style_set_grid_cell_y_align(lv_style_t * style, lv_grid_align_t value)
|
||||
{
|
||||
lv_style_value_t v = {
|
||||
.num = (int32_t)value
|
||||
};
|
||||
lv_style_set_prop(style, LV_STYLE_GRID_CELL_Y_ALIGN, v);
|
||||
}
|
||||
|
||||
void lv_style_set_grid_cell_row_span(lv_style_t * style, int32_t value)
|
||||
{
|
||||
lv_style_value_t v = {
|
||||
.num = (int32_t)value
|
||||
};
|
||||
lv_style_set_prop(style, LV_STYLE_GRID_CELL_ROW_SPAN, v);
|
||||
}
|
||||
#endif /*LV_USE_GRID*/
|
||||
|
||||
@@ -0,0 +1,742 @@
|
||||
|
||||
/*
|
||||
**********************************************************************
|
||||
* DO NOT EDIT
|
||||
* This file is automatically generated by "style_api_gen.py"
|
||||
**********************************************************************
|
||||
*/
|
||||
|
||||
|
||||
#ifndef LV_STYLE_GEN_H
|
||||
#define LV_STYLE_GEN_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
void lv_style_set_width(lv_style_t * style, int32_t value);
|
||||
void lv_style_set_min_width(lv_style_t * style, int32_t value);
|
||||
void lv_style_set_max_width(lv_style_t * style, int32_t value);
|
||||
void lv_style_set_height(lv_style_t * style, int32_t value);
|
||||
void lv_style_set_min_height(lv_style_t * style, int32_t value);
|
||||
void lv_style_set_max_height(lv_style_t * style, int32_t value);
|
||||
void lv_style_set_length(lv_style_t * style, int32_t value);
|
||||
void lv_style_set_x(lv_style_t * style, int32_t value);
|
||||
void lv_style_set_y(lv_style_t * style, int32_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, int32_t value);
|
||||
void lv_style_set_transform_height(lv_style_t * style, int32_t value);
|
||||
void lv_style_set_translate_x(lv_style_t * style, int32_t value);
|
||||
void lv_style_set_translate_y(lv_style_t * style, int32_t value);
|
||||
void lv_style_set_translate_radial(lv_style_t * style, int32_t value);
|
||||
void lv_style_set_transform_scale_x(lv_style_t * style, int32_t value);
|
||||
void lv_style_set_transform_scale_y(lv_style_t * style, int32_t value);
|
||||
void lv_style_set_transform_rotation(lv_style_t * style, int32_t value);
|
||||
void lv_style_set_transform_pivot_x(lv_style_t * style, int32_t value);
|
||||
void lv_style_set_transform_pivot_y(lv_style_t * style, int32_t value);
|
||||
void lv_style_set_transform_skew_x(lv_style_t * style, int32_t value);
|
||||
void lv_style_set_transform_skew_y(lv_style_t * style, int32_t value);
|
||||
void lv_style_set_pad_top(lv_style_t * style, int32_t value);
|
||||
void lv_style_set_pad_bottom(lv_style_t * style, int32_t value);
|
||||
void lv_style_set_pad_left(lv_style_t * style, int32_t value);
|
||||
void lv_style_set_pad_right(lv_style_t * style, int32_t value);
|
||||
void lv_style_set_pad_row(lv_style_t * style, int32_t value);
|
||||
void lv_style_set_pad_column(lv_style_t * style, int32_t value);
|
||||
void lv_style_set_pad_radial(lv_style_t * style, int32_t value);
|
||||
void lv_style_set_margin_top(lv_style_t * style, int32_t value);
|
||||
void lv_style_set_margin_bottom(lv_style_t * style, int32_t value);
|
||||
void lv_style_set_margin_left(lv_style_t * style, int32_t value);
|
||||
void lv_style_set_margin_right(lv_style_t * style, int32_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, int32_t value);
|
||||
void lv_style_set_bg_grad_stop(lv_style_t * style, int32_t value);
|
||||
void lv_style_set_bg_main_opa(lv_style_t * style, lv_opa_t value);
|
||||
void lv_style_set_bg_grad_opa(lv_style_t * style, lv_opa_t value);
|
||||
void lv_style_set_bg_grad(lv_style_t * style, const lv_grad_dsc_t * value);
|
||||
void lv_style_set_bg_image_src(lv_style_t * style, const void * value);
|
||||
void lv_style_set_bg_image_opa(lv_style_t * style, lv_opa_t value);
|
||||
void lv_style_set_bg_image_recolor(lv_style_t * style, lv_color_t value);
|
||||
void lv_style_set_bg_image_recolor_opa(lv_style_t * style, lv_opa_t value);
|
||||
void lv_style_set_bg_image_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, int32_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, int32_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, int32_t value);
|
||||
void lv_style_set_shadow_width(lv_style_t * style, int32_t value);
|
||||
void lv_style_set_shadow_offset_x(lv_style_t * style, int32_t value);
|
||||
void lv_style_set_shadow_offset_y(lv_style_t * style, int32_t value);
|
||||
void lv_style_set_shadow_spread(lv_style_t * style, int32_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_image_opa(lv_style_t * style, lv_opa_t value);
|
||||
void lv_style_set_image_recolor(lv_style_t * style, lv_color_t value);
|
||||
void lv_style_set_image_recolor_opa(lv_style_t * style, lv_opa_t value);
|
||||
void lv_style_set_line_width(lv_style_t * style, int32_t value);
|
||||
void lv_style_set_line_dash_width(lv_style_t * style, int32_t value);
|
||||
void lv_style_set_line_dash_gap(lv_style_t * style, int32_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, int32_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_image_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, int32_t value);
|
||||
void lv_style_set_text_line_space(lv_style_t * style, int32_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_text_outline_stroke_color(lv_style_t * style, lv_color_t value);
|
||||
void lv_style_set_text_outline_stroke_width(lv_style_t * style, int32_t value);
|
||||
void lv_style_set_text_outline_stroke_opa(lv_style_t * style, lv_opa_t value);
|
||||
void lv_style_set_radius(lv_style_t * style, int32_t value);
|
||||
void lv_style_set_radial_offset(lv_style_t * style, int32_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_opa_layered(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_recolor(lv_style_t * style, lv_color_t value);
|
||||
void lv_style_set_recolor_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_duration(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);
|
||||
void lv_style_set_bitmap_mask_src(lv_style_t * style, const void * value);
|
||||
void lv_style_set_rotary_sensitivity(lv_style_t * style, uint32_t value);
|
||||
#if LV_USE_FLEX
|
||||
void lv_style_set_flex_flow(lv_style_t * style, lv_flex_flow_t value);
|
||||
void lv_style_set_flex_main_place(lv_style_t * style, lv_flex_align_t value);
|
||||
void lv_style_set_flex_cross_place(lv_style_t * style, lv_flex_align_t value);
|
||||
void lv_style_set_flex_track_place(lv_style_t * style, lv_flex_align_t value);
|
||||
void lv_style_set_flex_grow(lv_style_t * style, uint8_t value);
|
||||
#endif /*LV_USE_FLEX*/
|
||||
|
||||
#if LV_USE_GRID
|
||||
void lv_style_set_grid_column_dsc_array(lv_style_t * style, const int32_t * value);
|
||||
void lv_style_set_grid_column_align(lv_style_t * style, lv_grid_align_t value);
|
||||
void lv_style_set_grid_row_dsc_array(lv_style_t * style, const int32_t * value);
|
||||
void lv_style_set_grid_row_align(lv_style_t * style, lv_grid_align_t value);
|
||||
void lv_style_set_grid_cell_column_pos(lv_style_t * style, int32_t value);
|
||||
void lv_style_set_grid_cell_x_align(lv_style_t * style, lv_grid_align_t value);
|
||||
void lv_style_set_grid_cell_column_span(lv_style_t * style, int32_t value);
|
||||
void lv_style_set_grid_cell_row_pos(lv_style_t * style, int32_t value);
|
||||
void lv_style_set_grid_cell_y_align(lv_style_t * style, lv_grid_align_t value);
|
||||
void lv_style_set_grid_cell_row_span(lv_style_t * style, int32_t value);
|
||||
#endif /*LV_USE_GRID*/
|
||||
|
||||
|
||||
#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_LENGTH(val) \
|
||||
{ \
|
||||
.prop = LV_STYLE_LENGTH, .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_TRANSLATE_RADIAL(val) \
|
||||
{ \
|
||||
.prop = LV_STYLE_TRANSLATE_RADIAL, .value = { .num = (int32_t)val } \
|
||||
}
|
||||
|
||||
#define LV_STYLE_CONST_TRANSFORM_SCALE_X(val) \
|
||||
{ \
|
||||
.prop = LV_STYLE_TRANSFORM_SCALE_X, .value = { .num = (int32_t)val } \
|
||||
}
|
||||
|
||||
#define LV_STYLE_CONST_TRANSFORM_SCALE_Y(val) \
|
||||
{ \
|
||||
.prop = LV_STYLE_TRANSFORM_SCALE_Y, .value = { .num = (int32_t)val } \
|
||||
}
|
||||
|
||||
#define LV_STYLE_CONST_TRANSFORM_ROTATION(val) \
|
||||
{ \
|
||||
.prop = LV_STYLE_TRANSFORM_ROTATION, .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_TRANSFORM_SKEW_X(val) \
|
||||
{ \
|
||||
.prop = LV_STYLE_TRANSFORM_SKEW_X, .value = { .num = (int32_t)val } \
|
||||
}
|
||||
|
||||
#define LV_STYLE_CONST_TRANSFORM_SKEW_Y(val) \
|
||||
{ \
|
||||
.prop = LV_STYLE_TRANSFORM_SKEW_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_PAD_RADIAL(val) \
|
||||
{ \
|
||||
.prop = LV_STYLE_PAD_RADIAL, .value = { .num = (int32_t)val } \
|
||||
}
|
||||
|
||||
#define LV_STYLE_CONST_MARGIN_TOP(val) \
|
||||
{ \
|
||||
.prop = LV_STYLE_MARGIN_TOP, .value = { .num = (int32_t)val } \
|
||||
}
|
||||
|
||||
#define LV_STYLE_CONST_MARGIN_BOTTOM(val) \
|
||||
{ \
|
||||
.prop = LV_STYLE_MARGIN_BOTTOM, .value = { .num = (int32_t)val } \
|
||||
}
|
||||
|
||||
#define LV_STYLE_CONST_MARGIN_LEFT(val) \
|
||||
{ \
|
||||
.prop = LV_STYLE_MARGIN_LEFT, .value = { .num = (int32_t)val } \
|
||||
}
|
||||
|
||||
#define LV_STYLE_CONST_MARGIN_RIGHT(val) \
|
||||
{ \
|
||||
.prop = LV_STYLE_MARGIN_RIGHT, .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_MAIN_OPA(val) \
|
||||
{ \
|
||||
.prop = LV_STYLE_BG_MAIN_OPA, .value = { .num = (int32_t)val } \
|
||||
}
|
||||
|
||||
#define LV_STYLE_CONST_BG_GRAD_OPA(val) \
|
||||
{ \
|
||||
.prop = LV_STYLE_BG_GRAD_OPA, .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_IMAGE_SRC(val) \
|
||||
{ \
|
||||
.prop = LV_STYLE_BG_IMAGE_SRC, .value = { .ptr = val } \
|
||||
}
|
||||
|
||||
#define LV_STYLE_CONST_BG_IMAGE_OPA(val) \
|
||||
{ \
|
||||
.prop = LV_STYLE_BG_IMAGE_OPA, .value = { .num = (int32_t)val } \
|
||||
}
|
||||
|
||||
#define LV_STYLE_CONST_BG_IMAGE_RECOLOR(val) \
|
||||
{ \
|
||||
.prop = LV_STYLE_BG_IMAGE_RECOLOR, .value = { .color = val } \
|
||||
}
|
||||
|
||||
#define LV_STYLE_CONST_BG_IMAGE_RECOLOR_OPA(val) \
|
||||
{ \
|
||||
.prop = LV_STYLE_BG_IMAGE_RECOLOR_OPA, .value = { .num = (int32_t)val } \
|
||||
}
|
||||
|
||||
#define LV_STYLE_CONST_BG_IMAGE_TILED(val) \
|
||||
{ \
|
||||
.prop = LV_STYLE_BG_IMAGE_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_OFFSET_X(val) \
|
||||
{ \
|
||||
.prop = LV_STYLE_SHADOW_OFFSET_X, .value = { .num = (int32_t)val } \
|
||||
}
|
||||
|
||||
#define LV_STYLE_CONST_SHADOW_OFFSET_Y(val) \
|
||||
{ \
|
||||
.prop = LV_STYLE_SHADOW_OFFSET_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_IMAGE_OPA(val) \
|
||||
{ \
|
||||
.prop = LV_STYLE_IMAGE_OPA, .value = { .num = (int32_t)val } \
|
||||
}
|
||||
|
||||
#define LV_STYLE_CONST_IMAGE_RECOLOR(val) \
|
||||
{ \
|
||||
.prop = LV_STYLE_IMAGE_RECOLOR, .value = { .color = val } \
|
||||
}
|
||||
|
||||
#define LV_STYLE_CONST_IMAGE_RECOLOR_OPA(val) \
|
||||
{ \
|
||||
.prop = LV_STYLE_IMAGE_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_IMAGE_SRC(val) \
|
||||
{ \
|
||||
.prop = LV_STYLE_ARC_IMAGE_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_TEXT_OUTLINE_STROKE_COLOR(val) \
|
||||
{ \
|
||||
.prop = LV_STYLE_TEXT_OUTLINE_STROKE_COLOR, .value = { .color = val } \
|
||||
}
|
||||
|
||||
#define LV_STYLE_CONST_TEXT_OUTLINE_STROKE_WIDTH(val) \
|
||||
{ \
|
||||
.prop = LV_STYLE_TEXT_OUTLINE_STROKE_WIDTH, .value = { .num = (int32_t)val } \
|
||||
}
|
||||
|
||||
#define LV_STYLE_CONST_TEXT_OUTLINE_STROKE_OPA(val) \
|
||||
{ \
|
||||
.prop = LV_STYLE_TEXT_OUTLINE_STROKE_OPA, .value = { .num = (int32_t)val } \
|
||||
}
|
||||
|
||||
#define LV_STYLE_CONST_RADIUS(val) \
|
||||
{ \
|
||||
.prop = LV_STYLE_RADIUS, .value = { .num = (int32_t)val } \
|
||||
}
|
||||
|
||||
#define LV_STYLE_CONST_RADIAL_OFFSET(val) \
|
||||
{ \
|
||||
.prop = LV_STYLE_RADIAL_OFFSET, .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_OPA_LAYERED(val) \
|
||||
{ \
|
||||
.prop = LV_STYLE_OPA_LAYERED, .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_RECOLOR(val) \
|
||||
{ \
|
||||
.prop = LV_STYLE_RECOLOR, .value = { .color = val } \
|
||||
}
|
||||
|
||||
#define LV_STYLE_CONST_RECOLOR_OPA(val) \
|
||||
{ \
|
||||
.prop = LV_STYLE_RECOLOR_OPA, .value = { .num = (int32_t)val } \
|
||||
}
|
||||
|
||||
#define LV_STYLE_CONST_ANIM(val) \
|
||||
{ \
|
||||
.prop = LV_STYLE_ANIM, .value = { .ptr = val } \
|
||||
}
|
||||
|
||||
#define LV_STYLE_CONST_ANIM_DURATION(val) \
|
||||
{ \
|
||||
.prop = LV_STYLE_ANIM_DURATION, .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 } \
|
||||
}
|
||||
|
||||
#define LV_STYLE_CONST_BITMAP_MASK_SRC(val) \
|
||||
{ \
|
||||
.prop = LV_STYLE_BITMAP_MASK_SRC, .value = { .ptr = val } \
|
||||
}
|
||||
|
||||
#define LV_STYLE_CONST_ROTARY_SENSITIVITY(val) \
|
||||
{ \
|
||||
.prop = LV_STYLE_ROTARY_SENSITIVITY, .value = { .num = (int32_t)val } \
|
||||
}
|
||||
#if LV_USE_FLEX
|
||||
|
||||
#define LV_STYLE_CONST_FLEX_FLOW(val) \
|
||||
{ \
|
||||
.prop = LV_STYLE_FLEX_FLOW, .value = { .num = (int32_t)val } \
|
||||
}
|
||||
|
||||
#define LV_STYLE_CONST_FLEX_MAIN_PLACE(val) \
|
||||
{ \
|
||||
.prop = LV_STYLE_FLEX_MAIN_PLACE, .value = { .num = (int32_t)val } \
|
||||
}
|
||||
|
||||
#define LV_STYLE_CONST_FLEX_CROSS_PLACE(val) \
|
||||
{ \
|
||||
.prop = LV_STYLE_FLEX_CROSS_PLACE, .value = { .num = (int32_t)val } \
|
||||
}
|
||||
|
||||
#define LV_STYLE_CONST_FLEX_TRACK_PLACE(val) \
|
||||
{ \
|
||||
.prop = LV_STYLE_FLEX_TRACK_PLACE, .value = { .num = (int32_t)val } \
|
||||
}
|
||||
|
||||
#define LV_STYLE_CONST_FLEX_GROW(val) \
|
||||
{ \
|
||||
.prop = LV_STYLE_FLEX_GROW, .value = { .num = (int32_t)val } \
|
||||
}
|
||||
#endif /*LV_USE_FLEX*/
|
||||
|
||||
#if LV_USE_GRID
|
||||
|
||||
#define LV_STYLE_CONST_GRID_COLUMN_DSC_ARRAY(val) \
|
||||
{ \
|
||||
.prop = LV_STYLE_GRID_COLUMN_DSC_ARRAY, .value = { .ptr = val } \
|
||||
}
|
||||
|
||||
#define LV_STYLE_CONST_GRID_COLUMN_ALIGN(val) \
|
||||
{ \
|
||||
.prop = LV_STYLE_GRID_COLUMN_ALIGN, .value = { .num = (int32_t)val } \
|
||||
}
|
||||
|
||||
#define LV_STYLE_CONST_GRID_ROW_DSC_ARRAY(val) \
|
||||
{ \
|
||||
.prop = LV_STYLE_GRID_ROW_DSC_ARRAY, .value = { .ptr = val } \
|
||||
}
|
||||
|
||||
#define LV_STYLE_CONST_GRID_ROW_ALIGN(val) \
|
||||
{ \
|
||||
.prop = LV_STYLE_GRID_ROW_ALIGN, .value = { .num = (int32_t)val } \
|
||||
}
|
||||
|
||||
#define LV_STYLE_CONST_GRID_CELL_COLUMN_POS(val) \
|
||||
{ \
|
||||
.prop = LV_STYLE_GRID_CELL_COLUMN_POS, .value = { .num = (int32_t)val } \
|
||||
}
|
||||
|
||||
#define LV_STYLE_CONST_GRID_CELL_X_ALIGN(val) \
|
||||
{ \
|
||||
.prop = LV_STYLE_GRID_CELL_X_ALIGN, .value = { .num = (int32_t)val } \
|
||||
}
|
||||
|
||||
#define LV_STYLE_CONST_GRID_CELL_COLUMN_SPAN(val) \
|
||||
{ \
|
||||
.prop = LV_STYLE_GRID_CELL_COLUMN_SPAN, .value = { .num = (int32_t)val } \
|
||||
}
|
||||
|
||||
#define LV_STYLE_CONST_GRID_CELL_ROW_POS(val) \
|
||||
{ \
|
||||
.prop = LV_STYLE_GRID_CELL_ROW_POS, .value = { .num = (int32_t)val } \
|
||||
}
|
||||
|
||||
#define LV_STYLE_CONST_GRID_CELL_Y_ALIGN(val) \
|
||||
{ \
|
||||
.prop = LV_STYLE_GRID_CELL_Y_ALIGN, .value = { .num = (int32_t)val } \
|
||||
}
|
||||
|
||||
#define LV_STYLE_CONST_GRID_CELL_ROW_SPAN(val) \
|
||||
{ \
|
||||
.prop = LV_STYLE_GRID_CELL_ROW_SPAN, .value = { .num = (int32_t)val } \
|
||||
}
|
||||
#endif /*LV_USE_GRID*/
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
#endif
|
||||
|
||||
#endif /* LV_STYLE_GEN_H */
|
||||
@@ -0,0 +1,39 @@
|
||||
/**
|
||||
* @file lv_style_private.h
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef LV_STYLE_PRIVATE_H
|
||||
#define LV_STYLE_PRIVATE_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*********************
|
||||
* INCLUDES
|
||||
*********************/
|
||||
|
||||
#include "lv_style.h"
|
||||
|
||||
/*********************
|
||||
* DEFINES
|
||||
*********************/
|
||||
|
||||
/**********************
|
||||
* TYPEDEFS
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* GLOBAL PROTOTYPES
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* MACROS
|
||||
**********************/
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /*extern "C"*/
|
||||
#endif
|
||||
|
||||
#endif /*LV_STYLE_PRIVATE_H*/
|
||||
@@ -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
|
||||
**********************/
|
||||
@@ -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*/
|
||||
@@ -0,0 +1,915 @@
|
||||
/**
|
||||
* @file lv_text.c
|
||||
*
|
||||
*/
|
||||
|
||||
/*********************
|
||||
* INCLUDES
|
||||
*********************/
|
||||
#include "lv_text_private.h"
|
||||
#include "lv_text_ap.h"
|
||||
#include "lv_math.h"
|
||||
#include "lv_log.h"
|
||||
#include "lv_assert.h"
|
||||
#include "../stdlib/lv_mem.h"
|
||||
#include "../stdlib/lv_string.h"
|
||||
#include "../misc/lv_types.h"
|
||||
|
||||
/*********************
|
||||
* DEFINES
|
||||
*********************/
|
||||
#define NO_BREAK_FOUND UINT32_MAX
|
||||
|
||||
/**********************
|
||||
* TYPEDEFS
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* STATIC PROTOTYPES
|
||||
**********************/
|
||||
|
||||
#if LV_TXT_ENC == LV_TXT_ENC_UTF8
|
||||
static uint8_t lv_text_utf8_size(const char * str);
|
||||
static uint32_t lv_text_unicode_to_utf8(uint32_t letter_uni);
|
||||
static uint32_t lv_text_utf8_conv_wc(uint32_t c);
|
||||
static uint32_t lv_text_utf8_next(const char * txt, uint32_t * i);
|
||||
static uint32_t lv_text_utf8_prev(const char * txt, uint32_t * i_start);
|
||||
static uint32_t lv_text_utf8_get_byte_id(const char * txt, uint32_t utf8_id);
|
||||
static uint32_t lv_text_utf8_get_char_id(const char * txt, uint32_t byte_id);
|
||||
static uint32_t lv_text_utf8_get_length(const char * txt);
|
||||
#elif LV_TXT_ENC == LV_TXT_ENC_ASCII
|
||||
static uint8_t lv_text_iso8859_1_size(const char * str);
|
||||
static uint32_t lv_text_unicode_to_iso8859_1(uint32_t letter_uni);
|
||||
static uint32_t lv_text_iso8859_1_conv_wc(uint32_t c);
|
||||
static uint32_t lv_text_iso8859_1_next(const char * txt, uint32_t * i);
|
||||
static uint32_t lv_text_iso8859_1_prev(const char * txt, uint32_t * i_start);
|
||||
static uint32_t lv_text_iso8859_1_get_byte_id(const char * txt, uint32_t utf8_id);
|
||||
static uint32_t lv_text_iso8859_1_get_char_id(const char * txt, uint32_t byte_id);
|
||||
static uint32_t lv_text_iso8859_1_get_length(const char * txt);
|
||||
#endif
|
||||
/**********************
|
||||
* STATIC VARIABLES
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* GLOBAL VARIABLES
|
||||
**********************/
|
||||
#if LV_TXT_ENC == LV_TXT_ENC_UTF8
|
||||
uint8_t (*const lv_text_encoded_size)(const char *) = lv_text_utf8_size;
|
||||
uint32_t (*const lv_text_unicode_to_encoded)(uint32_t) = lv_text_unicode_to_utf8;
|
||||
uint32_t (*const lv_text_encoded_conv_wc)(uint32_t) = lv_text_utf8_conv_wc;
|
||||
uint32_t (*const lv_text_encoded_next)(const char *, uint32_t *) = lv_text_utf8_next;
|
||||
uint32_t (*const lv_text_encoded_prev)(const char *, uint32_t *) = lv_text_utf8_prev;
|
||||
uint32_t (*const lv_text_encoded_get_byte_id)(const char *, uint32_t) = lv_text_utf8_get_byte_id;
|
||||
uint32_t (*const lv_text_encoded_get_char_id)(const char *, uint32_t) = lv_text_utf8_get_char_id;
|
||||
uint32_t (*const lv_text_get_encoded_length)(const char *) = lv_text_utf8_get_length;
|
||||
#elif LV_TXT_ENC == LV_TXT_ENC_ASCII
|
||||
uint8_t (*const lv_text_encoded_size)(const char *) = lv_text_iso8859_1_size;
|
||||
uint32_t (*const lv_text_unicode_to_encoded)(uint32_t) = lv_text_unicode_to_iso8859_1;
|
||||
uint32_t (*const lv_text_encoded_conv_wc)(uint32_t) = lv_text_iso8859_1_conv_wc;
|
||||
uint32_t (*const lv_text_encoded_next)(const char *, uint32_t *) = lv_text_iso8859_1_next;
|
||||
uint32_t (*const lv_text_encoded_prev)(const char *, uint32_t *) = lv_text_iso8859_1_prev;
|
||||
uint32_t (*const lv_text_encoded_get_byte_id)(const char *, uint32_t) = lv_text_iso8859_1_get_byte_id;
|
||||
uint32_t (*const lv_text_encoded_get_char_id)(const char *, uint32_t) = lv_text_iso8859_1_get_char_id;
|
||||
uint32_t (*const lv_text_get_encoded_length)(const char *) = lv_text_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_text_get_size(lv_point_t * size_res, const char * text, const lv_font_t * font, int32_t letter_space,
|
||||
int32_t line_space, int32_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_text_get_next_line(&text[line_start], LV_TEXT_LEN_MAX, font, letter_space, max_width, NULL, flag);
|
||||
|
||||
if((unsigned long)size_res->y + (unsigned long)letter_height + (unsigned long)line_space > LV_MAX_OF(int32_t)) {
|
||||
LV_LOG_WARN("integer overflow while calculating text height");
|
||||
return;
|
||||
}
|
||||
else {
|
||||
size_res->y += letter_height;
|
||||
size_res->y += line_space;
|
||||
}
|
||||
|
||||
/*Calculate the longest line*/
|
||||
int32_t act_line_length = lv_text_get_width_with_flags(&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;
|
||||
}
|
||||
|
||||
bool lv_text_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_WAIT) {
|
||||
*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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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_text_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_text_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 lv_text_cmd_state_t variable which stored the current state of command processing
|
||||
* @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_text_get_next_word(const char * txt, const lv_font_t * font,
|
||||
int32_t letter_space, int32_t max_width,
|
||||
lv_text_flag_t flag, uint32_t * word_w_ptr,
|
||||
lv_text_cmd_state_t * cmd_state)
|
||||
{
|
||||
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*/
|
||||
int32_t letter_w;
|
||||
int32_t cur_w = 0; /*Pixel Width of traversed string*/
|
||||
uint32_t word_len = 0; /*Number of characters in the traversed 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_text_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_text_encoded_next(txt, &i_next_next);
|
||||
word_len++;
|
||||
|
||||
/*Handle the recolor command*/
|
||||
if((flag & LV_TEXT_FLAG_RECOLOR) != 0) {
|
||||
if(lv_text_is_cmd(cmd_state, letter)) {
|
||||
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;
|
||||
if(flag & LV_TEXT_FLAG_BREAK_ALL) {
|
||||
break;
|
||||
}
|
||||
/*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_text_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;
|
||||
}
|
||||
else if(lv_text_is_a_word(letter_next) || lv_text_is_a_word(letter)) {
|
||||
/*Found a word for single letter, usually true for CJK*/
|
||||
*word_w_ptr = cur_w;
|
||||
i = i_next;
|
||||
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(flag & LV_TEXT_FLAG_BREAK_ALL) 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(flag & LV_TEXT_FLAG_BREAK_ALL) 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_text_encoded_prev(txt, &i);
|
||||
/**
|
||||
* TODO: it would be appropriate to update the returned
|
||||
* word width hereHowever, in current usage, this doesn't impact anything.
|
||||
*/
|
||||
}
|
||||
}
|
||||
return i;
|
||||
#else
|
||||
if(flag & LV_TEXT_FLAG_BREAK_ALL) 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_text_get_next_line(const char * txt, uint32_t len,
|
||||
const lv_font_t * font, int32_t letter_space,
|
||||
int32_t max_width, int32_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;
|
||||
|
||||
int32_t line_w = 0;
|
||||
|
||||
/*If max_width doesn't matter 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; i < len && txt[i] != '\n' && txt[i] != '\r' && txt[i] != '\0'; i++) {
|
||||
/*Just find the new line chars or string ends by incrementing `i`*/
|
||||
}
|
||||
if(i < len && 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(i < len && txt[i] != '\0' && max_width > 0) {
|
||||
lv_text_flag_t word_flag = flag;
|
||||
if(i == 0) word_flag |= LV_TEXT_FLAG_BREAK_ALL;
|
||||
|
||||
uint32_t word_w = 0;
|
||||
uint32_t advance = lv_text_get_next_word(&txt[i], font, letter_space, max_width, word_flag, &word_w, &cmd_state);
|
||||
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_text_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;
|
||||
}
|
||||
|
||||
int32_t lv_text_get_width(const char * txt, uint32_t length, const lv_font_t * font, int32_t letter_space)
|
||||
{
|
||||
if(txt == NULL) return 0;
|
||||
if(font == NULL) return 0;
|
||||
if(txt[0] == '\0') return 0;
|
||||
|
||||
uint32_t i = 0;
|
||||
int32_t width = 0;
|
||||
|
||||
if(length != 0) {
|
||||
while(i < length) {
|
||||
uint32_t letter;
|
||||
uint32_t letter_next;
|
||||
lv_text_encoded_letter_next_2(txt, &letter, &letter_next, &i);
|
||||
|
||||
int32_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;
|
||||
}
|
||||
|
||||
int32_t lv_text_get_width_with_flags(const char * txt, uint32_t length, const lv_font_t * font, int32_t letter_space,
|
||||
lv_text_flag_t flags)
|
||||
{
|
||||
if(txt == NULL) return 0;
|
||||
if(font == NULL) return 0;
|
||||
if(txt[0] == '\0') return 0;
|
||||
|
||||
uint32_t i = 0;
|
||||
int32_t width = 0;
|
||||
lv_text_cmd_state_t cmd_state = LV_TEXT_CMD_STATE_WAIT;
|
||||
|
||||
if(length != 0) {
|
||||
while(txt[i] != '\0' && i < length) {
|
||||
uint32_t letter;
|
||||
uint32_t letter_next;
|
||||
lv_text_encoded_letter_next_2(txt, &letter, &letter_next, &i);
|
||||
|
||||
if((flags & LV_TEXT_FLAG_RECOLOR) != 0) {
|
||||
if(lv_text_is_cmd(&cmd_state, letter) != false) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
int32_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;
|
||||
}
|
||||
|
||||
void lv_text_ins(char * txt_buf, uint32_t pos, const char * ins_txt)
|
||||
{
|
||||
if(txt_buf == NULL || ins_txt == NULL) return;
|
||||
|
||||
size_t old_len = lv_strlen(txt_buf);
|
||||
size_t ins_len = lv_strlen(ins_txt);
|
||||
if(ins_len == 0) return;
|
||||
|
||||
size_t new_len = ins_len + old_len;
|
||||
pos = lv_text_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(txt_buf + pos, ins_txt, ins_len);
|
||||
}
|
||||
|
||||
void lv_text_cut(char * txt, uint32_t pos, uint32_t len)
|
||||
{
|
||||
if(txt == NULL) return;
|
||||
|
||||
size_t old_len = lv_strlen(txt);
|
||||
|
||||
pos = lv_text_encoded_get_byte_id(txt, pos); /*Convert to byte index instead of letter index*/
|
||||
len = lv_text_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_text_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_malloc(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_text_ap_calc_bytes_count(raw_txt);
|
||||
text = lv_malloc(len_ap + 1);
|
||||
LV_ASSERT_MALLOC(text);
|
||||
if(text == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
lv_text_ap_proc(raw_txt, text);
|
||||
|
||||
lv_free(raw_txt);
|
||||
#else
|
||||
text = lv_malloc(len + 1);
|
||||
LV_ASSERT_MALLOC(text);
|
||||
if(text == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
lv_vsnprintf(text, len + 1, fmt, ap);
|
||||
#endif
|
||||
|
||||
return text;
|
||||
}
|
||||
|
||||
void lv_text_encoded_letter_next_2(const char * txt, uint32_t * letter, uint32_t * letter_next, uint32_t * ofs)
|
||||
{
|
||||
*letter = lv_text_encoded_next(txt, ofs);
|
||||
*letter_next = *letter != '\0' ? lv_text_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_text_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_text_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_text_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(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_text_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;
|
||||
|
||||
/* Ensure the string is not null */
|
||||
if(txt == NULL || txt[*i] == '\0') {
|
||||
return result;
|
||||
}
|
||||
|
||||
/*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_text_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_text_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_text_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_text_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_text_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_text_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_text_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_text_utf8_get_length(const char * txt)
|
||||
{
|
||||
uint32_t len = 0;
|
||||
uint32_t i = 0;
|
||||
|
||||
while(txt[i] != '\0') {
|
||||
lv_text_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 ISO8859-1 coded character, will be always 1.
|
||||
*/
|
||||
static uint8_t lv_text_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_text_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_text_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 ISO8859-1 coded char in 'txt'.
|
||||
* NULL to use txt[0] as index
|
||||
* @return the decoded ISO8859-1 character.
|
||||
*/
|
||||
static uint32_t lv_text_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 ISO8859-1 coded char in 'txt'.
|
||||
* @return the decoded ISO8859-1 character.
|
||||
*/
|
||||
static uint32_t lv_text_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.
|
||||
* The ISO8859-1 encoding is compatible with ASCII so the indices of characters is the same as the indices of bytes.
|
||||
* @param txt a '\0' terminated char string
|
||||
* @param utf8_id character index
|
||||
* @return byte index of the 'utf8_id'th letter
|
||||
*/
|
||||
static uint32_t lv_text_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.
|
||||
* The ISO8859-1 encoding is compatible with ASCII so the indices of characters is the same as the indices of bytes.
|
||||
* @param txt a '\0' terminated char string
|
||||
* @param byte_id byte index
|
||||
* @return character index of the letter at 'byte_id'th position
|
||||
*/
|
||||
static uint32_t lv_text_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.
|
||||
* The ISO8859-1 encoding is compatible with ASCII so the number of characters is the same as the number of bytes.
|
||||
* @param txt a '\0' terminated char string
|
||||
* @return number of characters
|
||||
*/
|
||||
static uint32_t lv_text_iso8859_1_get_length(const char * txt)
|
||||
{
|
||||
return lv_strlen(txt);
|
||||
}
|
||||
#else
|
||||
|
||||
#error "Invalid character encoding. See `LV_TXT_ENC` in `lv_conf.h`"
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,127 @@
|
||||
/**
|
||||
* @file lv_text.h
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef LV_TEXT_H
|
||||
#define LV_TEXT_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*********************
|
||||
* INCLUDES
|
||||
*********************/
|
||||
#include "../lv_conf_internal.h"
|
||||
|
||||
#include "lv_types.h"
|
||||
#include "lv_area.h"
|
||||
#include "../font/lv_font.h"
|
||||
#include "../stdlib/lv_sprintf.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
|
||||
|
||||
#define LV_TEXT_LEN_MAX UINT32_MAX
|
||||
|
||||
/**********************
|
||||
* TYPEDEFS
|
||||
**********************/
|
||||
|
||||
/**
|
||||
* Options for text rendering.
|
||||
*/
|
||||
|
||||
typedef enum {
|
||||
LV_TEXT_FLAG_NONE = 0x00,
|
||||
LV_TEXT_FLAG_EXPAND = 0x01, /**< Ignore max-width to avoid automatic word wrapping*/
|
||||
LV_TEXT_FLAG_FIT = 0x02, /**< Max-width is already equal to the longest line. (Used to skip some calculation)*/
|
||||
LV_TEXT_FLAG_BREAK_ALL = 0x04, /**< To prevent overflow, insert breaks between any two characters.
|
||||
Otherwise breaks are inserted at word boundaries, as configured via LV_TXT_BREAK_CHARS
|
||||
or according to LV_TXT_LINE_BREAK_LONG_LEN, LV_TXT_LINE_BREAK_LONG_PRE_MIN_LEN,
|
||||
and LV_TXT_LINE_BREAK_LONG_POST_MIN_LEN.*/
|
||||
LV_TEXT_FLAG_RECOLOR = 0x08, /**< Enable parsing of recolor command*/
|
||||
} lv_text_flag_t;
|
||||
|
||||
/** Label align policy*/
|
||||
typedef 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*/
|
||||
} lv_text_align_t;
|
||||
|
||||
/** State machine for text renderer. */
|
||||
typedef 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*/
|
||||
} lv_text_cmd_state_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 max_width max width of the text (break the lines to fit this size). Set COORD_MAX to avoid
|
||||
* @param flag settings for the text from ::lv_text_flag_t
|
||||
|
||||
* line breaks
|
||||
*/
|
||||
void lv_text_get_size(lv_point_t * size_res, const char * text, const lv_font_t * font, int32_t letter_space,
|
||||
int32_t line_space, int32_t max_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
|
||||
* @return length of a char_num long text
|
||||
*/
|
||||
int32_t lv_text_get_width(const char * txt, uint32_t length, const lv_font_t * font, int32_t letter_space);
|
||||
|
||||
/**
|
||||
* Give the length of a text with a given font with text flags
|
||||
* @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 ::lv_text_flag_t
|
||||
* @return length of a char_num long text
|
||||
*/
|
||||
int32_t lv_text_get_width_with_flags(const char * txt, uint32_t length, const lv_font_t * font, int32_t letter_space,
|
||||
lv_text_flag_t flags);
|
||||
|
||||
/**
|
||||
* Check if c is command state
|
||||
* @param state
|
||||
* @param c
|
||||
* @return True if c is state
|
||||
*/
|
||||
bool lv_text_is_cmd(lv_text_cmd_state_t * state, uint32_t c);
|
||||
/**********************
|
||||
* MACROS
|
||||
**********************/
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /*extern "C"*/
|
||||
#endif
|
||||
|
||||
#endif /*LV_TEXT_H*/
|
||||
@@ -0,0 +1,302 @@
|
||||
/**
|
||||
* @file lv_text_ap.c
|
||||
*
|
||||
*/
|
||||
|
||||
/*********************
|
||||
* INCLUDES
|
||||
*********************/
|
||||
#include "lv_bidi.h"
|
||||
#include "lv_text_private.h"
|
||||
#include "lv_text_ap.h"
|
||||
#include "lv_types.h"
|
||||
#include "../stdlib/lv_mem.h"
|
||||
#include "../draw/lv_draw.h"
|
||||
|
||||
/*********************
|
||||
* DEFINES
|
||||
*********************/
|
||||
|
||||
/**********************
|
||||
* TYPEDEFS
|
||||
**********************/
|
||||
typedef struct {
|
||||
uint8_t char_offset;
|
||||
uint16_t char_end_form;
|
||||
int8_t char_beginning_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_text_lam_alef(uint32_t ch_curr, uint32_t ch_next);
|
||||
static bool lv_text_is_arabic_vowel(uint16_t c);
|
||||
|
||||
/**********************
|
||||
* STATIC VARIABLES
|
||||
**********************/
|
||||
|
||||
const ap_chars_map_t ap_chars_map[] = {
|
||||
/*{Key Offset, End, Beginning, Middle, Isolated, {conjunction}}*/
|
||||
{0, 0xFE81, 0, 0, 0, {0, 0}}, // أ
|
||||
{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, 1}}, // ئ
|
||||
{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, 0, {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_text_ap_calc_bytes_count(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_text_get_encoded_length(txt);
|
||||
|
||||
i = 0;
|
||||
j = 0;
|
||||
while(i < txt_length) {
|
||||
ch_enc = lv_text_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_text_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_text_get_encoded_length(txt);
|
||||
|
||||
ch_enc = (uint32_t *)lv_malloc(sizeof(uint32_t) * (txt_length + 1));
|
||||
ch_fin = (uint32_t *)lv_malloc(sizeof(uint32_t) * (txt_length + 1));
|
||||
|
||||
i = 0;
|
||||
j = 0;
|
||||
while(j < txt_length)
|
||||
ch_enc[j++] = lv_text_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_text_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_text_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_previous = (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_text_lam_alef(index_current, idx_next);
|
||||
if(lam_alef) {
|
||||
if(conjunction_to_previous) {
|
||||
lam_alef ++;
|
||||
}
|
||||
ch_fin[j] = lam_alef;
|
||||
idx_previous = LV_UNDEF_ARABIC_PERSIAN_CHARS;
|
||||
i += 2;
|
||||
j++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if(conjunction_to_previous && 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_previous && conjunction_to_next)
|
||||
ch_fin[j] = ap_chars_map[index_current].char_end_form + ap_chars_map[index_current].char_beginning_form_offset;
|
||||
else if(conjunction_to_previous && !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_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_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_beginning_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_text_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_text_is_arabic_vowel(uint16_t c)
|
||||
{
|
||||
return (c >= 0x064B) && (c <= 0x0652);
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,49 @@
|
||||
/**
|
||||
* @file lv_text_ap.h
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef LV_TEXT_AP_H
|
||||
#define LV_TEXT_AP_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*********************
|
||||
* INCLUDES
|
||||
*********************/
|
||||
#include "lv_text.h"
|
||||
#include "lv_types.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_text_ap_calc_bytes_count(const char * txt);
|
||||
void lv_text_ap_proc(const char * txt, char * txt_out);
|
||||
|
||||
/**********************
|
||||
* MACROS
|
||||
**********************/
|
||||
|
||||
#endif // LV_USE_ARABIC_PERSIAN_CHARS
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /*extern "C"*/
|
||||
#endif
|
||||
|
||||
#endif /*LV_TEXT_AP_H*/
|
||||
@@ -0,0 +1,274 @@
|
||||
/**
|
||||
* @file lv_text_private.h
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef LV_TEXT_PRIVATE_H
|
||||
#define LV_TEXT_PRIVATE_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*********************
|
||||
* INCLUDES
|
||||
*********************/
|
||||
|
||||
#include "lv_text.h"
|
||||
|
||||
/*********************
|
||||
* DEFINES
|
||||
*********************/
|
||||
|
||||
/**********************
|
||||
* TYPEDEFS
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* GLOBAL PROTOTYPES
|
||||
**********************/
|
||||
|
||||
/**
|
||||
* Get the next line of text. Check line length and break chars too.
|
||||
* @param txt a '\0' terminated string
|
||||
* @param len length of 'txt' in bytes
|
||||
* @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 flag 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_text_get_next_line(const char * txt, uint32_t len, const lv_font_t * font, int32_t letter_space,
|
||||
int32_t max_width, int32_t * used_width, lv_text_flag_t flag);
|
||||
|
||||
/**
|
||||
* Insert a string into another
|
||||
* @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_text_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_text_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
|
||||
* @param ap items to print
|
||||
|
||||
* @return pointer to the allocated text string.
|
||||
*/
|
||||
char * lv_text_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_text_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_text_is_break_char(uint32_t letter)
|
||||
{
|
||||
uint8_t i;
|
||||
bool ret = false;
|
||||
|
||||
/*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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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_text_is_a_word(uint32_t letter)
|
||||
{
|
||||
/*Cheap check on invalid letter*/
|
||||
if(letter == 0) return false;
|
||||
|
||||
/*CJK Unified Ideographs*/
|
||||
if(letter >= 0x4E00 && letter <= 0x9FFF) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/*Fullwidth ASCII variants*/
|
||||
if(letter >= 0xFF01 && letter <= 0xFF5E) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/*CJK symbols and punctuation*/
|
||||
if(letter >= 0x3000 && letter <= 0x303F) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/*CJK Radicals Supplement*/
|
||||
if(letter >= 0x2E80 && letter <= 0x2EFF) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/*CJK Strokes*/
|
||||
if(letter >= 0x31C0 && letter <= 0x31EF) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/*Hiragana and Katakana*/
|
||||
if(letter >= 0x3040 && letter <= 0x30FF) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/*Chinese Vertical Forms*/
|
||||
if(letter >= 0xFE10 && letter <= 0xFE1F) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/*CJK Compatibility Forms*/
|
||||
if(letter >= 0xFE30 && letter <= 0xFE4F) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if character can be treated as marker, and don't need to be rendered.
|
||||
* Note, this is not a full list. Add your findings to the list.
|
||||
*
|
||||
* @param letter a letter
|
||||
* @return true if so
|
||||
*/
|
||||
static inline bool lv_text_is_marker(uint32_t letter)
|
||||
{
|
||||
if(letter < 0x20) return true;
|
||||
|
||||
/*U+061C ARABIC LETTER MARK, see https://www.compart.com/en/unicode/block/U+0600*/
|
||||
if(letter == 0x061C) return true;
|
||||
|
||||
/*U+115F HANGUL CHOSEONG FILLER, See https://www.compart.com/en/unicode/block/U+1100*/
|
||||
if(letter == 0x115F) return true;
|
||||
/*U+1160 HANGUL JUNGSEONG FILLER*/
|
||||
if(letter == 0x1160) return true;
|
||||
|
||||
/*See https://www.compart.com/en/unicode/block/U+1800*/
|
||||
if(letter >= 0x180B && letter <= 0x180E) return true;
|
||||
|
||||
/*See https://www.compart.com/en/unicode/block/U+2000*/
|
||||
if(letter >= 0x200B && letter <= 0x200F) return true;
|
||||
if(letter >= 0x2028 && letter <= 0x202F) return true;
|
||||
if(letter >= 0x205F && letter <= 0x206F) return true;
|
||||
|
||||
/*U+FEFF ZERO WIDTH NO-BREAK SPACE, see https://www.compart.com/en/unicode/block/U+FE70*/
|
||||
if(letter == 0xFEFF) return true;
|
||||
|
||||
if(letter == 0xF8FF) return true; /*LV_SYMBOL_DUMMY*/
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/***************************************************************
|
||||
* GLOBAL FUNCTION POINTERS FOR CHARACTER ENCODING INTERFACE
|
||||
***************************************************************/
|
||||
|
||||
/**
|
||||
* Give the size of an encoded character
|
||||
* @param txt pointer to a character in a string
|
||||
* @return length of the encoded character (1,2,3 ...). O in invalid
|
||||
*/
|
||||
extern uint8_t (*const lv_text_encoded_size)(const char * txt);
|
||||
|
||||
/**
|
||||
* 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 (*const lv_text_unicode_to_encoded)(uint32_t letter_uni);
|
||||
|
||||
/**
|
||||
* 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 (*const lv_text_encoded_conv_wc)(uint32_t c);
|
||||
|
||||
/**
|
||||
* Decode the next encoded character from a string.
|
||||
* @param txt pointer to '\0' terminated string
|
||||
* @param i_start 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 (*const lv_text_encoded_next)(const char * txt, uint32_t * i_start);
|
||||
|
||||
/**
|
||||
* 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 (*const lv_text_encoded_prev)(const char * txt, uint32_t * i_start);
|
||||
|
||||
/**
|
||||
* 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 utf8_id character index
|
||||
* @return byte index of the 'enc_id'th letter
|
||||
*/
|
||||
extern uint32_t (*const lv_text_encoded_get_byte_id)(const char * txt, uint32_t utf8_id);
|
||||
|
||||
/**
|
||||
* 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 (*const lv_text_encoded_get_char_id)(const char * txt, uint32_t byte_id);
|
||||
|
||||
/**
|
||||
* 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 (*const lv_text_get_encoded_length)(const char * txt);
|
||||
|
||||
/**********************
|
||||
* MACROS
|
||||
**********************/
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /*extern "C"*/
|
||||
#endif
|
||||
|
||||
#endif /*LV_TEXT_PRIVATE_H*/
|
||||
@@ -0,0 +1,388 @@
|
||||
/**
|
||||
* @file lv_timer.c
|
||||
*/
|
||||
|
||||
/*********************
|
||||
* INCLUDES
|
||||
*********************/
|
||||
#include "lv_timer_private.h"
|
||||
#include "../core/lv_global.h"
|
||||
#include "../tick/lv_tick.h"
|
||||
#include "../stdlib/lv_mem.h"
|
||||
#include "../stdlib/lv_sprintf.h"
|
||||
#include "lv_assert.h"
|
||||
#include "lv_ll.h"
|
||||
#include "lv_profiler.h"
|
||||
|
||||
/*********************
|
||||
* DEFINES
|
||||
*********************/
|
||||
|
||||
#define IDLE_MEAS_PERIOD 500 /*[ms]*/
|
||||
#define DEF_PERIOD 500
|
||||
|
||||
#define state LV_GLOBAL_DEFAULT()->timer_state
|
||||
#define timer_ll_p &(state.timer_ll)
|
||||
|
||||
/**********************
|
||||
* TYPEDEFS
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* STATIC PROTOTYPES
|
||||
**********************/
|
||||
static bool lv_timer_exec(lv_timer_t * timer);
|
||||
static uint32_t lv_timer_time_remaining(lv_timer_t * timer);
|
||||
static void lv_timer_handler_resume(void);
|
||||
|
||||
/**********************
|
||||
* STATIC VARIABLES
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* MACROS
|
||||
**********************/
|
||||
#if LV_USE_LOG && LV_LOG_TRACE_TIMER
|
||||
#define LV_TRACE_TIMER(...) LV_LOG_TRACE(__VA_ARGS__)
|
||||
#else
|
||||
#define LV_TRACE_TIMER(...)
|
||||
#endif
|
||||
|
||||
/**********************
|
||||
* GLOBAL FUNCTIONS
|
||||
**********************/
|
||||
|
||||
void lv_timer_core_init(void)
|
||||
{
|
||||
lv_ll_init(timer_ll_p, sizeof(lv_timer_t));
|
||||
|
||||
/*Initially enable the lv_timer handling*/
|
||||
lv_timer_enable(true);
|
||||
}
|
||||
|
||||
LV_ATTRIBUTE_TIMER_HANDLER uint32_t lv_timer_handler(void)
|
||||
{
|
||||
LV_TRACE_TIMER("begin");
|
||||
|
||||
lv_timer_state_t * state_p = &state;
|
||||
/*Avoid concurrent running of the timer handler*/
|
||||
if(state_p->already_running) {
|
||||
LV_TRACE_TIMER("already running, concurrent calls are not allow, returning");
|
||||
return 1;
|
||||
}
|
||||
state_p->already_running = true;
|
||||
|
||||
if(state_p->lv_timer_run == false) {
|
||||
state_p->already_running = false; /*Release mutex*/
|
||||
return 1;
|
||||
}
|
||||
|
||||
LV_PROFILER_TIMER_BEGIN;
|
||||
lv_lock();
|
||||
|
||||
uint32_t handler_start = lv_tick_get();
|
||||
|
||||
if(handler_start == 0) {
|
||||
state.run_cnt++;
|
||||
if(state.run_cnt > 100) {
|
||||
state.run_cnt = 0;
|
||||
LV_LOG_WARN("It seems lv_tick_inc() is not called.");
|
||||
}
|
||||
}
|
||||
|
||||
/*Run all timer from the list*/
|
||||
lv_timer_t * next;
|
||||
lv_timer_t * timer_active;
|
||||
lv_ll_t * timer_head = timer_ll_p;
|
||||
do {
|
||||
state_p->timer_deleted = false;
|
||||
state_p->timer_created = false;
|
||||
|
||||
timer_active = lv_ll_get_head(timer_head);
|
||||
while(timer_active) {
|
||||
/*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(timer_head, timer_active);
|
||||
|
||||
if(lv_timer_exec(timer_active)) {
|
||||
/*If a timer was created or deleted then this or the next item might be corrupted*/
|
||||
if(state_p->timer_created || state_p->timer_deleted) {
|
||||
LV_TRACE_TIMER("Start from the first timer again because a timer was created or deleted");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
timer_active = next; /*Load the next timer*/
|
||||
}
|
||||
} while(timer_active);
|
||||
|
||||
uint32_t time_until_next = LV_NO_TIMER_READY;
|
||||
next = lv_ll_get_head(timer_head);
|
||||
while(next) {
|
||||
if(!next->paused) {
|
||||
uint32_t delay = lv_timer_time_remaining(next);
|
||||
if(delay < time_until_next)
|
||||
time_until_next = delay;
|
||||
}
|
||||
|
||||
next = lv_ll_get_next(timer_head, next); /*Find the next timer*/
|
||||
}
|
||||
|
||||
state_p->busy_time += lv_tick_elaps(handler_start);
|
||||
uint32_t idle_period_time = lv_tick_elaps(state_p->idle_period_start);
|
||||
if(idle_period_time >= IDLE_MEAS_PERIOD) {
|
||||
state_p->idle_last = (state_p->busy_time * 100) / idle_period_time; /*Calculate the busy percentage*/
|
||||
state_p->idle_last = state_p->idle_last > 100 ? 0 : 100 - state_p->idle_last; /*But we need idle time*/
|
||||
state_p->busy_time = 0;
|
||||
state_p->idle_period_start = lv_tick_get();
|
||||
}
|
||||
|
||||
state_p->timer_time_until_next = time_until_next;
|
||||
state_p->already_running = false; /*Release the mutex*/
|
||||
|
||||
LV_TRACE_TIMER("finished (%" LV_PRIu32 " ms until the next timer call)", time_until_next);
|
||||
lv_unlock();
|
||||
|
||||
LV_PROFILER_TIMER_END;
|
||||
return time_until_next;
|
||||
}
|
||||
|
||||
LV_ATTRIBUTE_TIMER_HANDLER void lv_timer_periodic_handler(void)
|
||||
{
|
||||
lv_timer_state_t * state_p = &state;
|
||||
if(lv_tick_elaps(state_p->periodic_last_tick) >= state_p->timer_time_until_next) {
|
||||
LV_TRACE_TIMER("calling lv_timer_handler()");
|
||||
lv_timer_handler();
|
||||
state_p->periodic_last_tick = lv_tick_get();
|
||||
}
|
||||
}
|
||||
|
||||
lv_timer_t * lv_timer_create_basic(void)
|
||||
{
|
||||
return lv_timer_create(NULL, DEF_PERIOD, NULL);
|
||||
}
|
||||
|
||||
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(timer_ll_p);
|
||||
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;
|
||||
new_timer->auto_delete = true;
|
||||
|
||||
state.timer_created = true;
|
||||
|
||||
lv_timer_handler_resume();
|
||||
|
||||
return new_timer;
|
||||
}
|
||||
|
||||
void lv_timer_set_cb(lv_timer_t * timer, lv_timer_cb_t timer_cb)
|
||||
{
|
||||
LV_ASSERT_NULL(timer);
|
||||
timer->timer_cb = timer_cb;
|
||||
}
|
||||
|
||||
void lv_timer_delete(lv_timer_t * timer)
|
||||
{
|
||||
lv_ll_remove(timer_ll_p, timer);
|
||||
state.timer_deleted = true;
|
||||
|
||||
lv_free(timer);
|
||||
}
|
||||
|
||||
void lv_timer_pause(lv_timer_t * timer)
|
||||
{
|
||||
LV_ASSERT_NULL(timer);
|
||||
timer->paused = true;
|
||||
}
|
||||
|
||||
void lv_timer_resume(lv_timer_t * timer)
|
||||
{
|
||||
LV_ASSERT_NULL(timer);
|
||||
timer->paused = false;
|
||||
lv_timer_handler_resume();
|
||||
}
|
||||
|
||||
void lv_timer_set_period(lv_timer_t * timer, uint32_t period)
|
||||
{
|
||||
LV_ASSERT_NULL(timer);
|
||||
timer->period = period;
|
||||
}
|
||||
|
||||
void lv_timer_ready(lv_timer_t * timer)
|
||||
{
|
||||
LV_ASSERT_NULL(timer);
|
||||
timer->last_run = lv_tick_get() - timer->period - 1;
|
||||
}
|
||||
|
||||
void lv_timer_set_repeat_count(lv_timer_t * timer, int32_t repeat_count)
|
||||
{
|
||||
LV_ASSERT_NULL(timer);
|
||||
timer->repeat_count = repeat_count;
|
||||
}
|
||||
|
||||
void lv_timer_set_auto_delete(lv_timer_t * timer, bool auto_delete)
|
||||
{
|
||||
LV_ASSERT_NULL(timer);
|
||||
timer->auto_delete = auto_delete;
|
||||
}
|
||||
|
||||
void lv_timer_set_user_data(lv_timer_t * timer, void * user_data)
|
||||
{
|
||||
LV_ASSERT_NULL(timer);
|
||||
timer->user_data = user_data;
|
||||
}
|
||||
|
||||
void lv_timer_reset(lv_timer_t * timer)
|
||||
{
|
||||
LV_ASSERT_NULL(timer);
|
||||
timer->last_run = lv_tick_get();
|
||||
lv_timer_handler_resume();
|
||||
}
|
||||
|
||||
void lv_timer_enable(bool en)
|
||||
{
|
||||
state.lv_timer_run = en;
|
||||
if(en) lv_timer_handler_resume();
|
||||
}
|
||||
|
||||
void lv_timer_core_deinit(void)
|
||||
{
|
||||
lv_timer_enable(false);
|
||||
|
||||
lv_ll_clear(timer_ll_p);
|
||||
}
|
||||
|
||||
uint32_t lv_timer_get_idle(void)
|
||||
{
|
||||
return state.idle_last;
|
||||
}
|
||||
|
||||
uint32_t lv_timer_get_time_until_next(void)
|
||||
{
|
||||
return state.timer_time_until_next;
|
||||
}
|
||||
|
||||
lv_timer_t * lv_timer_get_next(lv_timer_t * timer)
|
||||
{
|
||||
if(timer == NULL) return lv_ll_get_head(timer_ll_p);
|
||||
else return lv_ll_get_next(timer_ll_p, timer);
|
||||
}
|
||||
|
||||
LV_ATTRIBUTE_TIMER_HANDLER uint32_t lv_timer_handler_run_in_period(uint32_t period)
|
||||
{
|
||||
static uint32_t last_tick = 0;
|
||||
|
||||
if(lv_tick_elaps(last_tick) >= period) {
|
||||
last_tick = lv_tick_get();
|
||||
return lv_timer_handler();
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
void * lv_timer_get_user_data(lv_timer_t * timer)
|
||||
{
|
||||
return timer->user_data;
|
||||
}
|
||||
|
||||
bool lv_timer_get_paused(lv_timer_t * timer)
|
||||
{
|
||||
return timer->paused;
|
||||
}
|
||||
|
||||
/**********************
|
||||
* 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();
|
||||
LV_TRACE_TIMER("calling timer callback: %p", *((void **)&timer->timer_cb));
|
||||
|
||||
if(timer->timer_cb && original_repeat_count != 0) {
|
||||
LV_PROFILER_TIMER_BEGIN_TAG("timer_cb");
|
||||
timer->timer_cb(timer);
|
||||
LV_PROFILER_TIMER_END_TAG("timer_cb");
|
||||
}
|
||||
|
||||
if(!state.timer_deleted) {
|
||||
LV_TRACE_TIMER("timer callback %p finished", *((void **)&timer->timer_cb));
|
||||
}
|
||||
else {
|
||||
LV_TRACE_TIMER("timer callback finished");
|
||||
}
|
||||
|
||||
LV_ASSERT_MEM_INTEGRITY();
|
||||
exec = true;
|
||||
}
|
||||
|
||||
if(state.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*/
|
||||
if(timer->auto_delete) {
|
||||
LV_TRACE_TIMER("deleting timer with %p callback because the repeat count is over", *((void **)&timer->timer_cb));
|
||||
lv_timer_delete(timer);
|
||||
}
|
||||
else {
|
||||
LV_TRACE_TIMER("pausing timer with %p callback because the repeat count is over", *((void **)&timer->timer_cb));
|
||||
lv_timer_pause(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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Call the ready lv_timer
|
||||
*/
|
||||
static void lv_timer_handler_resume(void)
|
||||
{
|
||||
/*If there is a timer which is ready to run then resume the timer loop*/
|
||||
state.timer_time_until_next = 0;
|
||||
if(state.resume_cb) {
|
||||
state.resume_cb(state.resume_data);
|
||||
}
|
||||
}
|
||||
|
||||
void lv_timer_handler_set_resume_cb(lv_timer_handler_resume_cb_t cb, void * data)
|
||||
{
|
||||
state.resume_cb = cb;
|
||||
state.resume_data = data;
|
||||
}
|
||||
@@ -0,0 +1,206 @@
|
||||
/**
|
||||
* @file lv_timer.h
|
||||
*/
|
||||
|
||||
#ifndef LV_TIMER_H
|
||||
#define LV_TIMER_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*********************
|
||||
* INCLUDES
|
||||
*********************/
|
||||
#include "../lv_conf_internal.h"
|
||||
#include "../tick/lv_tick.h"
|
||||
#include "lv_types.h"
|
||||
#include "lv_ll.h"
|
||||
|
||||
/*********************
|
||||
* DEFINES
|
||||
*********************/
|
||||
#ifndef LV_ATTRIBUTE_TIMER_HANDLER
|
||||
#define LV_ATTRIBUTE_TIMER_HANDLER
|
||||
#endif
|
||||
|
||||
#define LV_NO_TIMER_READY 0xFFFFFFFF
|
||||
|
||||
/**********************
|
||||
* TYPEDEFS
|
||||
**********************/
|
||||
|
||||
/**
|
||||
* Timers execute this type of functions.
|
||||
*/
|
||||
typedef void (*lv_timer_cb_t)(lv_timer_t *);
|
||||
|
||||
/**
|
||||
* Timer handler resume this type of function.
|
||||
*/
|
||||
typedef void (*lv_timer_handler_resume_cb_t)(void * data);
|
||||
|
||||
/**********************
|
||||
* GLOBAL PROTOTYPES
|
||||
**********************/
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
/**
|
||||
* 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 period the period for running lv_timer_handler()
|
||||
* @return the time after which it must be called again
|
||||
*/
|
||||
LV_ATTRIBUTE_TIMER_HANDLER uint32_t lv_timer_handler_run_in_period(uint32_t period);
|
||||
|
||||
/**
|
||||
* Call it in the super-loop of main() or threads. It will automatically call lv_timer_handler() at the right time.
|
||||
* This function is used to simplify the porting.
|
||||
*/
|
||||
LV_ATTRIBUTE_TIMER_HANDLER void lv_timer_periodic_handler(void);
|
||||
|
||||
/**
|
||||
* Set the resume callback to the timer handler
|
||||
* @param cb the function to call when timer handler is resumed
|
||||
* @param data pointer to a resume data
|
||||
*/
|
||||
void lv_timer_handler_set_resume_cb(lv_timer_handler_resume_cb_t cb, void * data);
|
||||
|
||||
/**
|
||||
* Create an "empty" timer. It needs to be 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_delete(lv_timer_t * timer);
|
||||
|
||||
/**
|
||||
* Pause a timer.
|
||||
* @param timer pointer to an lv_timer
|
||||
*/
|
||||
void lv_timer_pause(lv_timer_t * timer);
|
||||
|
||||
/**
|
||||
* Resume a timer.
|
||||
* @param timer pointer to an lv_timer
|
||||
*/
|
||||
void lv_timer_resume(lv_timer_t * timer);
|
||||
|
||||
/**
|
||||
* Set the callback to 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);
|
||||
|
||||
/**
|
||||
* Set whether a lv_timer will be deleted automatically when it is called `repeat_count` times.
|
||||
* @param timer pointer to a lv_timer.
|
||||
* @param auto_delete true: auto delete; false: timer will be paused when it is called `repeat_count` times.
|
||||
*/
|
||||
void lv_timer_set_auto_delete(lv_timer_t * timer, bool auto_delete);
|
||||
|
||||
/**
|
||||
* Set custom parameter to the lv_timer.
|
||||
* @param timer pointer to a lv_timer.
|
||||
* @param user_data custom parameter
|
||||
*/
|
||||
void lv_timer_set_user_data(lv_timer_t * timer, void * user_data);
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
uint32_t lv_timer_get_idle(void);
|
||||
|
||||
/**
|
||||
* Get the time remaining until the next timer will run
|
||||
* @return the time remaining in ms
|
||||
*/
|
||||
uint32_t lv_timer_get_time_until_next(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);
|
||||
|
||||
/**
|
||||
* Get the user_data passed when the timer was created
|
||||
* @param timer pointer to the lv_timer
|
||||
* @return pointer to the user_data
|
||||
*/
|
||||
void * lv_timer_get_user_data(lv_timer_t * timer);
|
||||
|
||||
/**
|
||||
* Get the pause state of a timer
|
||||
* @param timer pointer to a lv_timer
|
||||
* @return true: timer is paused; false: timer is running
|
||||
*/
|
||||
bool lv_timer_get_paused(lv_timer_t * timer);
|
||||
|
||||
/**********************
|
||||
* MACROS
|
||||
**********************/
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /*extern "C"*/
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,81 @@
|
||||
/**
|
||||
* @file lv_timer_private.h
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef LV_TIMER_PRIVATE_H
|
||||
#define LV_TIMER_PRIVATE_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*********************
|
||||
* INCLUDES
|
||||
*********************/
|
||||
|
||||
#include "lv_timer.h"
|
||||
|
||||
/*********************
|
||||
* DEFINES
|
||||
*********************/
|
||||
|
||||
/**********************
|
||||
* TYPEDEFS
|
||||
**********************/
|
||||
|
||||
/**
|
||||
* Descriptor of a lv_timer
|
||||
*/
|
||||
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;
|
||||
uint32_t auto_delete : 1;
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
lv_ll_t timer_ll; /**< Linked list to store the lv_timers */
|
||||
|
||||
bool lv_timer_run;
|
||||
uint8_t idle_last;
|
||||
bool timer_deleted;
|
||||
bool timer_created;
|
||||
uint32_t timer_time_until_next;
|
||||
|
||||
bool already_running;
|
||||
uint32_t periodic_last_tick;
|
||||
uint32_t busy_time;
|
||||
uint32_t idle_period_start;
|
||||
uint32_t run_cnt;
|
||||
|
||||
lv_timer_handler_resume_cb_t resume_cb;
|
||||
void * resume_data;
|
||||
} lv_timer_state_t;
|
||||
|
||||
/**********************
|
||||
* GLOBAL PROTOTYPES
|
||||
**********************/
|
||||
|
||||
/**
|
||||
* Init the lv_timer module
|
||||
*/
|
||||
void lv_timer_core_init(void);
|
||||
|
||||
/**
|
||||
* Deinit the lv_timer module
|
||||
*/
|
||||
void lv_timer_core_deinit(void);
|
||||
|
||||
/**********************
|
||||
* MACROS
|
||||
**********************/
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /*extern "C"*/
|
||||
#endif
|
||||
|
||||
#endif /*LV_TIMER_PRIVATE_H*/
|
||||
@@ -0,0 +1,189 @@
|
||||
/**
|
||||
* @file lv_tree.c
|
||||
* Tree.
|
||||
* The nodes are dynamically allocated by the 'lv_mem' module,
|
||||
*/
|
||||
|
||||
/*********************
|
||||
* INCLUDES
|
||||
*********************/
|
||||
#include "lv_tree.h"
|
||||
#include "../stdlib/lv_mem.h"
|
||||
#include "../stdlib/lv_string.h"
|
||||
|
||||
#include "lv_assert.h"
|
||||
|
||||
/*********************
|
||||
* DEFINES
|
||||
*********************/
|
||||
#define INIT_CHILDREN_CAP 4
|
||||
|
||||
/**********************
|
||||
* TYPEDEFS
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* STATIC PROTOTYPES
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* STATIC VARIABLES
|
||||
**********************/
|
||||
|
||||
const lv_tree_class_t lv_tree_node_class = {
|
||||
.base_class = NULL,
|
||||
.instance_size = sizeof(lv_tree_node_t),
|
||||
.constructor_cb = NULL,
|
||||
.destructor_cb = NULL,
|
||||
};
|
||||
|
||||
/**********************
|
||||
* MACROS
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* GLOBAL FUNCTIONS
|
||||
**********************/
|
||||
|
||||
static void _lv_tree_node_construct(const lv_tree_class_t * class_p, lv_tree_node_t * node)
|
||||
{
|
||||
if(node->class_p->base_class) {
|
||||
const lv_tree_class_t * original_class_p = node->class_p;
|
||||
|
||||
node->class_p = node->class_p->base_class;
|
||||
|
||||
_lv_tree_node_construct(class_p, node);
|
||||
|
||||
node->class_p = original_class_p;
|
||||
}
|
||||
|
||||
if(node->class_p->constructor_cb) node->class_p->constructor_cb(class_p, node);
|
||||
}
|
||||
|
||||
static void _lv_tree_node_destruct(lv_tree_node_t * node)
|
||||
{
|
||||
if(node->class_p->destructor_cb) node->class_p->destructor_cb(node->class_p, node);
|
||||
|
||||
if(node->class_p->base_class) {
|
||||
node->class_p = node->class_p->base_class;
|
||||
|
||||
_lv_tree_node_destruct(node);
|
||||
}
|
||||
}
|
||||
|
||||
static uint32_t get_instance_size(const lv_tree_class_t * class_p)
|
||||
{
|
||||
const lv_tree_class_t * base = class_p;
|
||||
while(base && base->instance_size == 0)
|
||||
base = base->base_class;
|
||||
|
||||
LV_ASSERT_NULL(base);
|
||||
|
||||
return base->instance_size;
|
||||
}
|
||||
|
||||
static lv_tree_node_t * _lv_tree_class_create_node(const lv_tree_class_t * class_p, lv_tree_node_t * parent)
|
||||
{
|
||||
uint32_t s = get_instance_size(class_p);
|
||||
lv_tree_node_t * node = lv_malloc(s);
|
||||
if(node == NULL) return NULL;
|
||||
lv_memzero(node, s);
|
||||
node->class_p = class_p;
|
||||
node->parent = parent;
|
||||
node->child_cap = INIT_CHILDREN_CAP;
|
||||
node->children = lv_malloc(sizeof(lv_tree_node_t *) * node->child_cap);
|
||||
if(parent != NULL) {
|
||||
parent->child_cnt++;
|
||||
if(parent->child_cnt == parent->child_cap) {
|
||||
parent->child_cap <<= 1;
|
||||
parent->children = lv_realloc(parent->children, sizeof(lv_tree_node_t *) * parent->child_cap);
|
||||
}
|
||||
parent->children[parent->child_cnt - 1] = node;
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
lv_tree_node_t * lv_tree_node_create(const lv_tree_class_t * class_p, lv_tree_node_t * parent)
|
||||
{
|
||||
LV_ASSERT_NULL(class_p);
|
||||
lv_tree_node_t * node = _lv_tree_class_create_node(class_p, parent);
|
||||
LV_ASSERT_NULL(node);
|
||||
_lv_tree_node_construct(node->class_p, node);
|
||||
return node;
|
||||
}
|
||||
|
||||
static bool _lv_tree_node_destructor_cb(const lv_tree_node_t * n, void * user_data)
|
||||
{
|
||||
LV_UNUSED(user_data);
|
||||
if(n) {
|
||||
lv_tree_node_t * node = (lv_tree_node_t *)n;
|
||||
_lv_tree_node_destruct(node);
|
||||
lv_free(node->children);
|
||||
lv_free(node);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void lv_tree_node_delete(lv_tree_node_t * node)
|
||||
{
|
||||
if(node) {
|
||||
if(node->parent) {
|
||||
/* Remove from parent */
|
||||
lv_tree_node_t * parent = node->parent;
|
||||
for(uint32_t i = 0; i < parent->child_cnt; i++) {
|
||||
if(parent->children[i] == node) {
|
||||
parent->children[i] = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
lv_tree_walk(node, LV_TREE_WALK_POST_ORDER, _lv_tree_node_destructor_cb, NULL, NULL, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
bool lv_tree_walk(const lv_tree_node_t * node,
|
||||
lv_tree_walk_mode_t mode,
|
||||
lv_tree_traverse_cb_t cb,
|
||||
lv_tree_before_cb_t bcb,
|
||||
lv_tree_after_cb_t acb,
|
||||
void * user_data)
|
||||
{
|
||||
if(node && cb) {
|
||||
if(mode == LV_TREE_WALK_PRE_ORDER) {
|
||||
if(bcb && !bcb(node, user_data)) {
|
||||
return false;
|
||||
}
|
||||
if(!cb(node, user_data)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
for(uint32_t i = 0; i < node->child_cnt; i++) {
|
||||
if(!lv_tree_walk(node->children[i], mode, cb, bcb, acb, user_data)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if(mode == LV_TREE_WALK_PRE_ORDER) {
|
||||
if(acb) {
|
||||
acb(node, user_data);
|
||||
}
|
||||
}
|
||||
|
||||
if(mode == LV_TREE_WALK_POST_ORDER) {
|
||||
if(bcb && !bcb(node, user_data)) {
|
||||
return false;
|
||||
}
|
||||
if(!cb(node, user_data)) {
|
||||
return false;
|
||||
}
|
||||
if(acb) {
|
||||
acb(node, user_data);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
/**
|
||||
* @file lv_tree.h
|
||||
* Tree. The tree nodes are dynamically allocated by the 'lv_mem' module.
|
||||
*/
|
||||
|
||||
#ifndef LV_TREE_H
|
||||
#define LV_TREE_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*********************
|
||||
* INCLUDES
|
||||
*********************/
|
||||
#include "lv_types.h"
|
||||
|
||||
/*********************
|
||||
* DEFINES
|
||||
*********************/
|
||||
|
||||
#define LV_TREE_NODE(n) ((lv_tree_node_t*)(n))
|
||||
|
||||
/**********************
|
||||
* TYPEDEFS
|
||||
**********************/
|
||||
|
||||
struct _lv_tree_node_t;
|
||||
|
||||
/**
|
||||
* Describe the common methods of every object.
|
||||
* Similar to a C++ class.
|
||||
*/
|
||||
typedef struct _lv_tree_class_t {
|
||||
const struct _lv_tree_class_t * base_class;
|
||||
uint32_t instance_size;
|
||||
void (*constructor_cb)(const struct _lv_tree_class_t * class_p, struct _lv_tree_node_t * node);
|
||||
void (*destructor_cb)(const struct _lv_tree_class_t * class_p, struct _lv_tree_node_t * node);
|
||||
} lv_tree_class_t;
|
||||
|
||||
/** Description of a tree node*/
|
||||
typedef struct _lv_tree_node_t {
|
||||
struct _lv_tree_node_t * parent;
|
||||
struct _lv_tree_node_t ** children;
|
||||
uint32_t child_cnt;
|
||||
uint32_t child_cap;
|
||||
const struct _lv_tree_class_t * class_p;
|
||||
} lv_tree_node_t;
|
||||
|
||||
enum {
|
||||
LV_TREE_WALK_PRE_ORDER = 0,
|
||||
LV_TREE_WALK_POST_ORDER,
|
||||
};
|
||||
typedef uint8_t lv_tree_walk_mode_t;
|
||||
|
||||
typedef bool (*lv_tree_traverse_cb_t)(const lv_tree_node_t * node, void * user_data);
|
||||
typedef bool (*lv_tree_before_cb_t)(const lv_tree_node_t * node, void * user_data);
|
||||
typedef void (*lv_tree_after_cb_t)(const lv_tree_node_t * node, void * user_data);
|
||||
|
||||
/**********************
|
||||
* GLOBAL PROTOTYPES
|
||||
**********************/
|
||||
|
||||
extern const lv_tree_class_t lv_tree_node_class;
|
||||
|
||||
/**
|
||||
* @brief Create a tree node
|
||||
* @param class_p pointer to a class of the node
|
||||
* @param parent pointer to the parent node (or NULL if it's the root node)
|
||||
* @return pointer to the new node
|
||||
*/
|
||||
lv_tree_node_t * lv_tree_node_create(const lv_tree_class_t * class_p, lv_tree_node_t * parent);
|
||||
|
||||
/**
|
||||
* @brief Delete a tree node and all its children recursively
|
||||
* @param node pointer to the node to delete
|
||||
*/
|
||||
void lv_tree_node_delete(lv_tree_node_t * node);
|
||||
|
||||
/**
|
||||
* @brief Walk the tree recursively and call a callback function on each node
|
||||
* @param node pointer to the root node of the tree
|
||||
* @param mode LV_TREE_WALK_PRE_ORDER or LV_TREE_WALK_POST_ORDER
|
||||
* @param cb callback function to call on each node
|
||||
* @param bcb callback function to call before visiting a node
|
||||
* @param acb callback function to call after visiting a node
|
||||
* @param user_data user data to pass to the callback functions
|
||||
* @return true: traversal is finished; false: traversal broken
|
||||
*/
|
||||
bool lv_tree_walk(const lv_tree_node_t * node,
|
||||
lv_tree_walk_mode_t mode,
|
||||
lv_tree_traverse_cb_t cb,
|
||||
lv_tree_before_cb_t bcb,
|
||||
lv_tree_after_cb_t acb,
|
||||
void * user_data);
|
||||
|
||||
/**********************
|
||||
* MACROS
|
||||
**********************/
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /*extern "C"*/
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,416 @@
|
||||
/**
|
||||
* @file lv_types.h
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef LV_TYPES_H
|
||||
#define LV_TYPES_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*********************
|
||||
* INCLUDES
|
||||
*********************/
|
||||
#include "../lv_conf_internal.h"
|
||||
|
||||
#ifndef __ASSEMBLY__
|
||||
#include LV_STDINT_INCLUDE
|
||||
#include LV_STDDEF_INCLUDE
|
||||
#include LV_STDBOOL_INCLUDE
|
||||
#include LV_INTTYPES_INCLUDE
|
||||
#include LV_LIMITS_INCLUDE
|
||||
#include LV_STDARG_INCLUDE
|
||||
#endif
|
||||
|
||||
/*********************
|
||||
* 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
|
||||
|
||||
#if LV_USE_3DTEXTURE
|
||||
#if LV_USE_OPENGLES
|
||||
#define LV_3DTEXTURE_ID_NULL 0u
|
||||
#endif
|
||||
|
||||
#ifndef LV_3DTEXTURE_ID_NULL
|
||||
#error enable LV_USE_OPENGLES to use LV_USE_3DTEXTURE
|
||||
#endif
|
||||
#endif /*LV_USE_3DTEXTURE*/
|
||||
|
||||
/**********************
|
||||
* TYPEDEFS
|
||||
**********************/
|
||||
|
||||
/* Exclude C enum and struct definitions when included by assembly code */
|
||||
#ifndef __ASSEMBLY__
|
||||
|
||||
/**
|
||||
* LVGL error codes.
|
||||
*/
|
||||
typedef enum {
|
||||
LV_RESULT_INVALID = 0, /*Typically indicates that the object is deleted (become invalid) in the action
|
||||
function or an operation was failed*/
|
||||
LV_RESULT_OK, /*The object is valid (no deleted) after the action*/
|
||||
} lv_result_t;
|
||||
|
||||
#if defined(__cplusplus) || __STDC_VERSION__ >= 199901L
|
||||
/*If c99 or newer, use the definition of uintptr_t directly from <stdint.h>*/
|
||||
typedef uintptr_t lv_uintptr_t;
|
||||
typedef intptr_t lv_intptr_t;
|
||||
|
||||
#else
|
||||
|
||||
/*Otherwise, use the arch size determination*/
|
||||
#ifdef LV_ARCH_64
|
||||
typedef uint64_t lv_uintptr_t;
|
||||
typedef int64_t lv_intptr_t;
|
||||
#else
|
||||
typedef uint32_t lv_uintptr_t;
|
||||
typedef int32_t lv_intptr_t;
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
#if LV_USE_FLOAT
|
||||
typedef float lv_value_precise_t;
|
||||
#else
|
||||
typedef int32_t lv_value_precise_t;
|
||||
#endif
|
||||
|
||||
#if LV_USE_3DTEXTURE
|
||||
#if LV_USE_OPENGLES
|
||||
typedef unsigned int lv_3dtexture_id_t;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Typedefs from various lvgl modules.
|
||||
* They are defined here to avoid circular dependencies.
|
||||
*/
|
||||
|
||||
typedef struct _lv_obj_t lv_obj_t;
|
||||
|
||||
typedef uint16_t lv_state_t;
|
||||
typedef uint32_t lv_part_t;
|
||||
|
||||
typedef uint8_t lv_opa_t;
|
||||
|
||||
typedef uint8_t lv_style_prop_t;
|
||||
|
||||
typedef struct _lv_obj_class_t lv_obj_class_t;
|
||||
|
||||
typedef struct _lv_group_t lv_group_t;
|
||||
|
||||
typedef struct _lv_display_t lv_display_t;
|
||||
|
||||
typedef struct _lv_layer_t lv_layer_t;
|
||||
typedef struct _lv_draw_unit_t lv_draw_unit_t;
|
||||
typedef struct _lv_draw_task_t lv_draw_task_t;
|
||||
|
||||
typedef struct _lv_indev_t lv_indev_t;
|
||||
|
||||
typedef struct _lv_event_t lv_event_t;
|
||||
|
||||
typedef struct _lv_timer_t lv_timer_t;
|
||||
|
||||
typedef struct _lv_theme_t lv_theme_t;
|
||||
|
||||
typedef struct _lv_anim_t lv_anim_t;
|
||||
|
||||
typedef struct _lv_font_t lv_font_t;
|
||||
typedef struct _lv_font_class_t lv_font_class_t;
|
||||
typedef struct _lv_font_info_t lv_font_info_t;
|
||||
|
||||
typedef struct _lv_font_manager_t lv_font_manager_t;
|
||||
|
||||
typedef struct _lv_image_decoder_t lv_image_decoder_t;
|
||||
|
||||
typedef struct _lv_image_decoder_dsc_t lv_image_decoder_dsc_t;
|
||||
|
||||
typedef struct _lv_draw_image_dsc_t lv_draw_image_dsc_t;
|
||||
|
||||
typedef struct _lv_fragment_t lv_fragment_t;
|
||||
typedef struct _lv_fragment_class_t lv_fragment_class_t;
|
||||
typedef struct _lv_fragment_managed_states_t lv_fragment_managed_states_t;
|
||||
|
||||
typedef struct _lv_profiler_builtin_config_t lv_profiler_builtin_config_t;
|
||||
|
||||
typedef struct _lv_rb_node_t lv_rb_node_t;
|
||||
|
||||
typedef struct _lv_rb_t lv_rb_t;
|
||||
|
||||
typedef struct _lv_color_filter_dsc_t lv_color_filter_dsc_t;
|
||||
|
||||
typedef struct _lv_event_dsc_t lv_event_dsc_t;
|
||||
|
||||
typedef struct _lv_cache_t lv_cache_t;
|
||||
|
||||
typedef struct _lv_cache_entry_t lv_cache_entry_t;
|
||||
|
||||
typedef struct _lv_fs_file_cache_t lv_fs_file_cache_t;
|
||||
|
||||
typedef struct _lv_fs_path_ex_t lv_fs_path_ex_t;
|
||||
|
||||
typedef struct _lv_image_decoder_args_t lv_image_decoder_args_t;
|
||||
|
||||
typedef struct _lv_image_cache_data_t lv_image_cache_data_t;
|
||||
|
||||
typedef struct _lv_image_header_cache_data_t lv_image_header_cache_data_t;
|
||||
|
||||
typedef struct _lv_draw_mask_t lv_draw_mask_t;
|
||||
|
||||
typedef struct _lv_draw_label_hint_t lv_draw_label_hint_t;
|
||||
|
||||
typedef struct _lv_draw_glyph_dsc_t lv_draw_glyph_dsc_t;
|
||||
|
||||
typedef struct _lv_draw_image_sup_t lv_draw_image_sup_t;
|
||||
|
||||
typedef struct _lv_draw_mask_rect_dsc_t lv_draw_mask_rect_dsc_t;
|
||||
|
||||
typedef struct _lv_obj_style_t lv_obj_style_t;
|
||||
|
||||
typedef struct _lv_obj_style_transition_dsc_t lv_obj_style_transition_dsc_t;
|
||||
|
||||
typedef struct _lv_hit_test_info_t lv_hit_test_info_t;
|
||||
|
||||
typedef struct _lv_cover_check_info_t lv_cover_check_info_t;
|
||||
|
||||
typedef struct _lv_obj_spec_attr_t lv_obj_spec_attr_t;
|
||||
|
||||
typedef struct _lv_image_t lv_image_t;
|
||||
|
||||
typedef struct _lv_animimg_t lv_animimg_t;
|
||||
|
||||
typedef struct _lv_arc_t lv_arc_t;
|
||||
|
||||
typedef struct _lv_label_t lv_label_t;
|
||||
|
||||
typedef struct _lv_bar_anim_t lv_bar_anim_t;
|
||||
|
||||
typedef struct _lv_bar_t lv_bar_t;
|
||||
|
||||
typedef struct _lv_button_t lv_button_t;
|
||||
|
||||
typedef struct _lv_buttonmatrix_t lv_buttonmatrix_t;
|
||||
|
||||
typedef struct _lv_calendar_t lv_calendar_t;
|
||||
|
||||
typedef struct _lv_canvas_t lv_canvas_t;
|
||||
|
||||
typedef struct _lv_chart_series_t lv_chart_series_t;
|
||||
|
||||
typedef struct _lv_chart_cursor_t lv_chart_cursor_t;
|
||||
|
||||
typedef struct _lv_chart_t lv_chart_t;
|
||||
|
||||
typedef struct _lv_checkbox_t lv_checkbox_t;
|
||||
|
||||
typedef struct _lv_dropdown_t lv_dropdown_t;
|
||||
|
||||
typedef struct _lv_dropdown_list_t lv_dropdown_list_t;
|
||||
|
||||
typedef struct _lv_imagebutton_src_info_t lv_imagebutton_src_info_t;
|
||||
|
||||
typedef struct _lv_imagebutton_t lv_imagebutton_t;
|
||||
|
||||
typedef struct _lv_keyboard_t lv_keyboard_t;
|
||||
|
||||
typedef struct _lv_led_t lv_led_t;
|
||||
|
||||
typedef struct _lv_line_t lv_line_t;
|
||||
|
||||
typedef struct _lv_menu_load_page_event_data_t lv_menu_load_page_event_data_t;
|
||||
|
||||
typedef struct _lv_menu_history_t lv_menu_history_t;
|
||||
|
||||
typedef struct _lv_menu_t lv_menu_t;
|
||||
|
||||
typedef struct _lv_menu_page_t lv_menu_page_t;
|
||||
|
||||
typedef struct _lv_msgbox_t lv_msgbox_t;
|
||||
|
||||
typedef struct _lv_roller_t lv_roller_t;
|
||||
|
||||
typedef struct _lv_scale_section_t lv_scale_section_t;
|
||||
|
||||
typedef struct _lv_scale_t lv_scale_t;
|
||||
|
||||
typedef struct _lv_slider_t lv_slider_t;
|
||||
|
||||
typedef struct _lv_span_t lv_span_t;
|
||||
|
||||
typedef struct _lv_spangroup_t lv_spangroup_t;
|
||||
|
||||
typedef struct _lv_textarea_t lv_textarea_t;
|
||||
|
||||
typedef struct _lv_spinbox_t lv_spinbox_t;
|
||||
|
||||
typedef struct _lv_switch_t lv_switch_t;
|
||||
|
||||
typedef struct _lv_table_cell_t lv_table_cell_t;
|
||||
|
||||
typedef struct _lv_table_t lv_table_t;
|
||||
|
||||
typedef struct _lv_tabview_t lv_tabview_t;
|
||||
|
||||
typedef struct _lv_tileview_t lv_tileview_t;
|
||||
|
||||
typedef struct _lv_tileview_tile_t lv_tileview_tile_t;
|
||||
|
||||
typedef struct _lv_win_t lv_win_t;
|
||||
|
||||
typedef struct _lv_3dtexture_t lv_3dtexture_t;
|
||||
|
||||
typedef struct _lv_observer_t lv_observer_t;
|
||||
|
||||
typedef struct _lv_monkey_config_t lv_monkey_config_t;
|
||||
|
||||
typedef struct _lv_ime_pinyin_t lv_ime_pinyin_t;
|
||||
|
||||
typedef struct _lv_file_explorer_t lv_file_explorer_t;
|
||||
|
||||
typedef struct _lv_barcode_t lv_barcode_t;
|
||||
|
||||
typedef struct _lv_gif_t lv_gif_t;
|
||||
|
||||
typedef struct _lv_qrcode_t lv_qrcode_t;
|
||||
|
||||
typedef struct _lv_freetype_outline_vector_t lv_freetype_outline_vector_t;
|
||||
|
||||
typedef struct _lv_freetype_outline_event_param_t lv_freetype_outline_event_param_t;
|
||||
|
||||
typedef struct _lv_fpoint_t lv_fpoint_t;
|
||||
|
||||
typedef struct _lv_matrix_t lv_matrix_t;
|
||||
|
||||
typedef struct _lv_vector_path_t lv_vector_path_t;
|
||||
|
||||
typedef struct _lv_vector_gradient_t lv_vector_gradient_t;
|
||||
|
||||
typedef struct _lv_vector_fill_dsc_t lv_vector_fill_dsc_t;
|
||||
|
||||
typedef struct _lv_vector_stroke_dsc_t lv_vector_stroke_dsc_t;
|
||||
|
||||
typedef struct _lv_vector_draw_dsc_t lv_vector_draw_dsc_t;
|
||||
|
||||
typedef struct _lv_draw_vector_task_dsc_t lv_draw_vector_task_dsc_t;
|
||||
|
||||
typedef struct _lv_vector_dsc_t lv_vector_dsc_t;
|
||||
|
||||
typedef struct _lv_xkb_t lv_xkb_t;
|
||||
|
||||
typedef struct _lv_libinput_event_t lv_libinput_event_t;
|
||||
|
||||
typedef struct _lv_libinput_t lv_libinput_t;
|
||||
|
||||
typedef struct _lv_draw_sw_unit_t lv_draw_sw_unit_t;
|
||||
|
||||
typedef struct _lv_draw_sw_mask_common_dsc_t lv_draw_sw_mask_common_dsc_t;
|
||||
|
||||
typedef struct _lv_draw_sw_mask_line_param_t lv_draw_sw_mask_line_param_t;
|
||||
|
||||
typedef struct _lv_draw_sw_mask_angle_param_t lv_draw_sw_mask_angle_param_t;
|
||||
|
||||
typedef struct _lv_draw_sw_mask_radius_param_t lv_draw_sw_mask_radius_param_t;
|
||||
|
||||
typedef struct _lv_draw_sw_mask_fade_param_t lv_draw_sw_mask_fade_param_t;
|
||||
|
||||
typedef struct _lv_draw_sw_mask_map_param_t lv_draw_sw_mask_map_param_t;
|
||||
|
||||
typedef struct _lv_draw_sw_blend_dsc_t lv_draw_sw_blend_dsc_t;
|
||||
|
||||
typedef struct _lv_draw_sw_blend_fill_dsc_t lv_draw_sw_blend_fill_dsc_t;
|
||||
|
||||
typedef struct _lv_draw_sw_blend_image_dsc_t lv_draw_sw_blend_image_dsc_t;
|
||||
|
||||
typedef struct _lv_draw_buf_handlers_t lv_draw_buf_handlers_t;
|
||||
|
||||
typedef struct _lv_rlottie_t lv_rlottie_t;
|
||||
|
||||
typedef struct _lv_ffmpeg_player_t lv_ffmpeg_player_t;
|
||||
|
||||
typedef struct _lv_glfw_window_t lv_glfw_window_t;
|
||||
typedef struct _lv_glfw_texture_t lv_glfw_texture_t;
|
||||
|
||||
typedef uint32_t lv_prop_id_t;
|
||||
|
||||
typedef struct _lv_array_t lv_array_t;
|
||||
|
||||
typedef struct _lv_iter_t lv_iter_t;
|
||||
|
||||
typedef struct _lv_circle_buf_t lv_circle_buf_t;
|
||||
|
||||
typedef struct _lv_draw_buf_t lv_draw_buf_t;
|
||||
|
||||
#if LV_USE_OBJ_PROPERTY
|
||||
typedef struct _lv_property_name_t lv_property_name_t;
|
||||
#endif
|
||||
|
||||
#if LV_USE_SYSMON
|
||||
|
||||
typedef struct _lv_sysmon_backend_data_t lv_sysmon_backend_data_t;
|
||||
|
||||
#if LV_USE_PERF_MONITOR
|
||||
typedef struct _lv_sysmon_perf_info_t lv_sysmon_perf_info_t;
|
||||
#endif /*LV_USE_PERF_MONITOR*/
|
||||
|
||||
#endif /*LV_USE_SYSMON*/
|
||||
|
||||
|
||||
typedef struct _lv_xml_component_scope_t lv_xml_component_scope_t;
|
||||
|
||||
typedef struct _lv_xml_parser_state_t lv_xml_parser_state_t;
|
||||
|
||||
#if LV_USE_EVDEV
|
||||
typedef struct _lv_evdev_discovery_t lv_evdev_discovery_t;
|
||||
#endif
|
||||
|
||||
#endif /*__ASSEMBLY__*/
|
||||
|
||||
/**********************
|
||||
* 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)
|
||||
#undef _LV_CONCAT
|
||||
|
||||
#define _LV_CONCAT3(x, y, z) x ## y ## z
|
||||
#define LV_CONCAT3(x, y, z) _LV_CONCAT3(x, y, z)
|
||||
#undef _LV_CONCAT3
|
||||
|
||||
#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__) || defined(__IAR_SYSTEMS_ICC__))
|
||||
#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*/
|
||||
@@ -0,0 +1,95 @@
|
||||
/**
|
||||
* @file lv_utils.c
|
||||
*
|
||||
*/
|
||||
|
||||
/*********************
|
||||
* INCLUDES
|
||||
*********************/
|
||||
#include "lv_utils.h"
|
||||
#include "lv_fs.h"
|
||||
#include "lv_types.h"
|
||||
#include "cache/lv_cache.h"
|
||||
|
||||
/*********************
|
||||
* DEFINES
|
||||
*********************/
|
||||
|
||||
/**********************
|
||||
* TYPEDEFS
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* STATIC PROTOTYPES
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* STATIC VARIABLES
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* MACROS
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* GLOBAL FUNCTIONS
|
||||
**********************/
|
||||
|
||||
void * lv_utils_bsearch(const void * key, const void * base, size_t n, size_t size,
|
||||
int (*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;
|
||||
}
|
||||
|
||||
lv_result_t lv_draw_buf_save_to_file(const lv_draw_buf_t * draw_buf, const char * path)
|
||||
{
|
||||
lv_fs_file_t file;
|
||||
lv_fs_res_t res = lv_fs_open(&file, path, LV_FS_MODE_WR);
|
||||
if(res != LV_FS_RES_OK) {
|
||||
LV_LOG_ERROR("create file %s failed", path);
|
||||
return LV_RESULT_INVALID;
|
||||
}
|
||||
|
||||
/*Image content modified, invalidate image cache.*/
|
||||
lv_image_cache_drop(path);
|
||||
|
||||
uint32_t bw;
|
||||
res = lv_fs_write(&file, &draw_buf->header, sizeof(draw_buf->header), &bw);
|
||||
if(res != LV_FS_RES_OK || bw != sizeof(draw_buf->header)) {
|
||||
LV_LOG_ERROR("write draw_buf->header failed");
|
||||
lv_fs_close(&file);
|
||||
return LV_RESULT_INVALID;
|
||||
}
|
||||
|
||||
res = lv_fs_write(&file, draw_buf->data, draw_buf->data_size, &bw);
|
||||
if(res != LV_FS_RES_OK || bw != draw_buf->data_size) {
|
||||
LV_LOG_ERROR("write draw_buf->data failed");
|
||||
lv_fs_close(&file);
|
||||
return LV_RESULT_INVALID;
|
||||
}
|
||||
|
||||
lv_fs_close(&file);
|
||||
LV_LOG_TRACE("saved draw_buf to %s", path);
|
||||
return LV_RESULT_OK;
|
||||
}
|
||||
|
||||
/**********************
|
||||
* STATIC FUNCTIONS
|
||||
**********************/
|
||||
@@ -0,0 +1,91 @@
|
||||
/**
|
||||
* @file lv_utils.h
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef LV_UTILS_H
|
||||
#define LV_UTILS_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*********************
|
||||
* INCLUDES
|
||||
*********************/
|
||||
|
||||
#include "lv_types.h"
|
||||
#include "../draw/lv_draw_buf.h"
|
||||
|
||||
/*********************
|
||||
* 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, size_t n, size_t size,
|
||||
int (*cmp)(const void * pRef, const void * pElement));
|
||||
|
||||
/**
|
||||
* Save a draw buf to a file
|
||||
* @param draw_buf pointer to a draw buffer
|
||||
* @param path path to the file to save
|
||||
* @return LV_RESULT_OK: success; LV_RESULT_INVALID: error
|
||||
*/
|
||||
lv_result_t lv_draw_buf_save_to_file(const lv_draw_buf_t * draw_buf, const char * path);
|
||||
|
||||
/**
|
||||
* Reverse the order of the bytes in a 32-bit value.
|
||||
* @param x a 32-bit value.
|
||||
* @return the value `x` with reversed byte-order.
|
||||
*/
|
||||
static inline uint32_t lv_swap_bytes_32(uint32_t x)
|
||||
{
|
||||
return (x << 24)
|
||||
| ((x & 0x0000ff00U) << 8)
|
||||
| ((x & 0x00ff0000U) >> 8)
|
||||
| (x >> 24);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the order of the bytes in a 16-bit value.
|
||||
* @param x a 16-bit value.
|
||||
* @return the value `x` with reversed byte-order.
|
||||
*/
|
||||
static inline uint16_t lv_swap_bytes_16(uint16_t x)
|
||||
{
|
||||
return (x << 8) | (x >> 8);
|
||||
}
|
||||
|
||||
/**********************
|
||||
* MACROS
|
||||
**********************/
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /*extern "C"*/
|
||||
#endif
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user