perf: Enable 512KB LVGL image cache on PSRAM and move modified lvgl to local components

This commit is contained in:
Adolfo Reyna
2026-07-09 22:27:55 -04:00
parent df93252281
commit c4ec7ead55
3999 changed files with 813776 additions and 0 deletions
+286
View File
@@ -0,0 +1,286 @@
/**
* @file lv_global.h
*
*/
#ifndef LV_GLOBAL_H
#define LV_GLOBAL_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../lv_conf_internal.h"
#include "../misc/lv_types.h"
#include "../draw/lv_draw.h"
#if LV_USE_DRAW_SW
#include "../draw/sw/lv_draw_sw.h"
#endif
#include "../misc/lv_anim.h"
#include "../misc/lv_area.h"
#include "../misc/lv_color_op.h"
#include "../misc/lv_ll.h"
#include "../misc/lv_log.h"
#include "../misc/lv_style.h"
#include "../misc/lv_timer.h"
#include "../osal/lv_os.h"
#include "../others/sysmon/lv_sysmon.h"
#include "../stdlib/builtin/lv_tlsf.h"
#if LV_USE_FONT_COMPRESSED
#include "../font/lv_font_fmt_txt_private.h"
#endif
#if LV_USE_OS != LV_OS_NONE && defined(__linux__)
#include "../osal/lv_linux_private.h"
#endif
#include "../tick/lv_tick.h"
#include "../layouts/lv_layout.h"
#include "../misc/lv_types.h"
#include "../misc/lv_timer_private.h"
#include "../misc/lv_anim_private.h"
#include "../tick/lv_tick_private.h"
#include "../draw/lv_draw_buf_private.h"
#include "../draw/lv_draw_private.h"
#include "../draw/sw/lv_draw_sw_private.h"
#include "../draw/sw/lv_draw_sw_mask_private.h"
#include "../stdlib/builtin/lv_tlsf_private.h"
#include "../others/sysmon/lv_sysmon_private.h"
#include "../others/test/lv_test_private.h"
#include "../layouts/lv_layout_private.h"
/*********************
* DEFINES
*********************/
#define ZERO_MEM_SENTINEL 0xa1b2c3d4
/**********************
* TYPEDEFS
**********************/
#if LV_USE_SPAN != 0
struct _snippet_stack;
#endif
#if LV_USE_FREETYPE
struct _lv_freetype_context_t;
#endif
#if LV_USE_PROFILER && LV_USE_PROFILER_BUILTIN
struct _lv_profiler_builtin_ctx_t;
#endif
#if LV_USE_NUTTX
struct _lv_nuttx_ctx_t;
#endif
typedef struct _lv_global_t {
bool inited;
bool deinit_in_progress; /**< Can be used e.g. in the LV_EVENT_DELETE to deinit the drivers too */
lv_ll_t disp_ll;
lv_display_t * disp_refresh;
lv_display_t * disp_default;
lv_ll_t style_trans_ll;
bool style_refresh;
uint32_t style_custom_table_size;
uint32_t style_last_custom_prop_id;
uint8_t * style_custom_prop_flag_lookup_table;
lv_ll_t group_ll;
lv_group_t * group_default;
lv_ll_t indev_ll;
lv_indev_t * indev_active;
lv_obj_t * indev_obj_active;
uint32_t layout_count;
lv_layout_dsc_t * layout_list;
bool layout_update_mutex;
uint32_t memory_zero;
uint32_t math_rand_seed;
lv_event_t * event_header;
uint32_t event_last_register_id;
lv_timer_state_t timer_state;
lv_anim_state_t anim_state;
lv_tick_state_t tick_state;
lv_draw_buf_handlers_t draw_buf_handlers;
lv_draw_buf_handlers_t font_draw_buf_handlers;
lv_draw_buf_handlers_t image_cache_draw_buf_handlers; /**< Ensure that all assigned draw buffers
* can be managed by image cache. */
lv_ll_t img_decoder_ll;
lv_cache_t * img_cache;
lv_cache_t * img_header_cache;
lv_draw_global_info_t draw_info;
lv_ll_t draw_sw_blend_handler_ll;
#if defined(LV_DRAW_SW_SHADOW_CACHE_SIZE) && LV_DRAW_SW_SHADOW_CACHE_SIZE > 0
lv_draw_sw_shadow_cache_t sw_shadow_cache;
#endif
#if LV_DRAW_SW_COMPLEX
lv_draw_sw_mask_radius_circle_dsc_arr_t sw_circle_cache;
#endif
#if LV_USE_LOG
lv_log_print_g_cb_t custom_log_print_cb;
#endif
#if LV_USE_LOG && LV_LOG_USE_TIMESTAMP
uint32_t log_last_log_time;
#endif
#if LV_USE_THEME_SIMPLE
void * theme_simple;
#endif
#if LV_USE_THEME_DEFAULT
void * theme_default;
#endif
#if LV_USE_THEME_MONO
void * theme_mono;
#endif
#if LV_USE_STDLIB_MALLOC == LV_STDLIB_BUILTIN
lv_tlsf_state_t tlsf_state;
#endif
lv_ll_t fsdrv_ll;
#if LV_USE_FS_STDIO != '\0'
lv_fs_drv_t stdio_fs_drv;
#endif
#if LV_USE_FS_POSIX
lv_fs_drv_t posix_fs_drv;
#endif
#if LV_USE_FS_FATFS
lv_fs_drv_t fatfs_fs_drv;
#endif
#if LV_USE_FS_WIN32 != '\0'
lv_fs_drv_t win32_fs_drv;
#endif
#if LV_USE_FS_UEFI
lv_fs_drv_t uefi_fs_drv;
#endif
#if LV_USE_FS_LITTLEFS
lv_fs_drv_t littlefs_fs_drv;
#endif
#if LV_USE_FS_ARDUINO_ESP_LITTLEFS
lv_fs_drv_t arduino_esp_littlefs_fs_drv;
#endif
#if LV_USE_FS_ARDUINO_SD
lv_fs_drv_t arduino_sd_fs_drv;
#endif
#if LV_USE_FREETYPE
struct _lv_freetype_context_t * ft_context;
#endif
#if LV_USE_FONT_COMPRESSED
lv_font_fmt_rle_t font_fmt_rle;
#endif
#if LV_USE_SPAN != 0
struct _snippet_stack * span_snippet_stack;
#endif
#if LV_USE_PROFILER && LV_USE_PROFILER_BUILTIN
struct _lv_profiler_builtin_ctx_t * profiler_context;
#endif
#if LV_USE_FILE_EXPLORER != 0
lv_style_t fe_list_button_style;
#endif
#if LV_USE_MEM_MONITOR
lv_sysmon_backend_data_t sysmon_mem;
#endif
#if LV_USE_IME_PINYIN != 0
size_t ime_cand_len;
#endif
#if LV_USE_OBJ_ID_BUILTIN
void * objid_array;
uint32_t objid_count;
#endif
#if LV_USE_TEST
lv_test_state_t test_state;
#endif
#if LV_USE_NUTTX
struct _lv_nuttx_ctx_t * nuttx_ctx;
#endif
#if LV_USE_OS != LV_OS_NONE
lv_mutex_t lv_general_mutex;
#if defined(__linux__)
lv_proc_stat_t linux_last_proc_stat;
#endif
#endif
#if LV_USE_OS == LV_OS_FREERTOS
uint32_t freertos_idle_time_sum;
uint32_t freertos_non_idle_time_sum;
uint32_t freertos_task_switch_timestamp;
bool freertos_idle_task_running;
#endif
#if LV_USE_EVDEV
lv_evdev_discovery_t * evdev_discovery;
#endif
void * user_data;
} lv_global_t;
/**********************
* MACROS
**********************/
#if LV_ENABLE_GLOBAL_CUSTOM
#include LV_GLOBAL_CUSTOM_INCLUDE
#ifndef LV_GLOBAL_CUSTOM
#define LV_GLOBAL_CUSTOM() lv_global_default()
#endif
#define LV_GLOBAL_DEFAULT() LV_GLOBAL_CUSTOM()
#else
LV_ATTRIBUTE_EXTERN_DATA extern lv_global_t lv_global;
#define LV_GLOBAL_DEFAULT() (&lv_global)
#endif
/**********************
* GLOBAL PROTOTYPES
**********************/
#if LV_ENABLE_GLOBAL_CUSTOM
/**
* Get the default global object for current thread
* @return pointer to the default global object
*/
lv_global_t * lv_global_default(void);
#endif
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_GLOBAL_H*/
+530
View File
@@ -0,0 +1,530 @@
/**
* @file lv_group.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_group_private.h"
#include "../core/lv_obj_private.h"
#include "../core/lv_global.h"
#include "../indev/lv_indev.h"
#include "../misc/lv_types.h"
/*********************
* DEFINES
*********************/
#define default_group LV_GLOBAL_DEFAULT()->group_default
#define group_ll_p &(LV_GLOBAL_DEFAULT()->group_ll)
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static bool focus_next_core(lv_group_t * group, void * (*begin)(const lv_ll_t *),
void * (*move)(const lv_ll_t *, const void *));
static void lv_group_refocus(lv_group_t * g);
static lv_indev_t * get_indev(const lv_group_t * g);
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
void lv_group_init(void)
{
lv_ll_init(group_ll_p, sizeof(lv_group_t));
}
void lv_group_deinit(void)
{
lv_ll_clear(group_ll_p);
}
lv_group_t * lv_group_create(void)
{
lv_group_t * group = lv_ll_ins_head(group_ll_p);
LV_ASSERT_MALLOC(group);
if(group == NULL) return NULL;
lv_ll_init(&group->obj_ll, sizeof(lv_obj_t *));
group->obj_focus = NULL;
group->frozen = 0;
group->focus_cb = NULL;
group->edge_cb = NULL;
group->editing = 0;
group->refocus_policy = LV_GROUP_REFOCUS_POLICY_PREV;
group->wrap = 1;
group->user_data = NULL;
return group;
}
void lv_group_delete(lv_group_t * group)
{
/*Defocus the currently focused object*/
LV_ASSERT_NULL(group);
if(group->obj_focus != NULL) {
lv_obj_send_event(*group->obj_focus, LV_EVENT_DEFOCUSED, get_indev(group));
lv_obj_invalidate(*group->obj_focus);
}
/*Remove the objects from the group*/
lv_obj_t ** obj;
LV_LL_READ(&group->obj_ll, obj) {
if((*obj)->spec_attr)(*obj)->spec_attr->group_p = NULL;
}
/*Remove the group from any indev devices */
lv_indev_t * indev = lv_indev_get_next(NULL);
while(indev) {
if(lv_indev_get_group(indev) == group) {
lv_indev_set_group(indev, NULL);
}
indev = lv_indev_get_next(indev);
}
/*If the group is the default group, set the default group as NULL*/
if(group == lv_group_get_default()) lv_group_set_default(NULL);
lv_ll_clear(&(group->obj_ll));
lv_ll_remove(group_ll_p, group);
lv_free(group);
}
void lv_group_set_default(lv_group_t * group)
{
default_group = group;
}
lv_group_t * lv_group_get_default(void)
{
return default_group;
}
void lv_group_add_obj(lv_group_t * group, lv_obj_t * obj)
{
if(group == NULL) return;
LV_LOG_TRACE("begin");
/*Be sure the object is removed from its current group*/
lv_group_remove_obj(obj);
if(obj->spec_attr == NULL) lv_obj_allocate_spec_attr(obj);
obj->spec_attr->group_p = group;
lv_obj_t ** next = lv_ll_ins_tail(&group->obj_ll);
LV_ASSERT_MALLOC(next);
if(next == NULL) return;
*next = obj;
/*If the head and the tail is equal then there is only one object in the linked list.
*In this case automatically activate it*/
if(lv_ll_get_head(&group->obj_ll) == next) {
lv_group_refocus(group);
}
LV_LOG_TRACE("finished");
}
void lv_group_swap_obj(lv_obj_t * obj1, lv_obj_t * obj2)
{
lv_group_t * g1 = lv_obj_get_group(obj1);
lv_group_t * g2 = lv_obj_get_group(obj2);
if(g1 != g2) return;
if(g1 == NULL) return;
/*Do not add the object twice*/
lv_obj_t ** obj_i;
LV_LL_READ(&g1->obj_ll, obj_i) {
if((*obj_i) == obj1)(*obj_i) = obj2;
else if((*obj_i) == obj2)(*obj_i) = obj1;
}
lv_obj_t * focused = lv_group_get_focused(g1);
if(focused == obj1) lv_group_focus_obj(obj2);
else if(focused == obj2) lv_group_focus_obj(obj1);
}
void lv_group_remove_obj(lv_obj_t * obj)
{
lv_group_t * g = lv_obj_get_group(obj);
if(g == NULL) return;
LV_LOG_TRACE("begin");
/*Focus on the next object*/
if(g->obj_focus && *g->obj_focus == obj) {
if(g->frozen) g->frozen = 0;
/*If this is the only object in the group then focus to nothing.*/
if(lv_ll_get_head(&g->obj_ll) == g->obj_focus && lv_ll_get_tail(&g->obj_ll) == g->obj_focus) {
lv_obj_send_event(*g->obj_focus, LV_EVENT_DEFOCUSED, get_indev(g));
}
/*If there more objects in the group then focus to the next/prev object*/
else {
lv_group_refocus(g);
}
}
/*If the focuses object is still the same then it was the only object in the group but it will
*be deleted. Set the `obj_focus` to NULL to get back to the initial state of the group with
*zero objects*/
if(g->obj_focus && *g->obj_focus == obj) {
g->obj_focus = NULL;
}
/*Search the object and remove it from its group*/
lv_obj_t ** i;
LV_LL_READ(&g->obj_ll, i) {
if(*i == obj) {
lv_ll_remove(&g->obj_ll, i);
lv_free(i);
if(obj->spec_attr) obj->spec_attr->group_p = NULL;
break;
}
}
LV_LOG_TRACE("finished");
}
void lv_group_remove_all_objs(lv_group_t * group)
{
LV_ASSERT_NULL(group);
/*Defocus the currently focused object*/
if(group->obj_focus != NULL) {
lv_obj_send_event(*group->obj_focus, LV_EVENT_DEFOCUSED, get_indev(group));
lv_obj_invalidate(*group->obj_focus);
group->obj_focus = NULL;
}
/*Remove the objects from the group*/
lv_obj_t ** obj;
LV_LL_READ(&group->obj_ll, obj) {
if((*obj)->spec_attr)(*obj)->spec_attr->group_p = NULL;
}
lv_ll_clear(&(group->obj_ll));
}
void lv_group_focus_obj(lv_obj_t * obj)
{
if(obj == NULL) return;
lv_group_t * g = lv_obj_get_group(obj);
if(g == NULL) return;
if(g->frozen != 0) return;
/*On defocus edit mode must be leaved*/
lv_group_set_editing(g, false);
lv_obj_t ** i;
LV_LL_READ(&g->obj_ll, i) {
if(*i == obj) {
if(g->obj_focus != NULL && obj != *g->obj_focus) { /*Do not defocus if the same object needs to be focused again*/
lv_result_t res = lv_obj_send_event(*g->obj_focus, LV_EVENT_DEFOCUSED, get_indev(g));
if(res != LV_RESULT_OK) return;
lv_obj_invalidate(*g->obj_focus);
}
g->obj_focus = i;
if(g->obj_focus != NULL) {
if(g->focus_cb) g->focus_cb(g);
lv_result_t res = lv_obj_send_event(*g->obj_focus, LV_EVENT_FOCUSED, get_indev(g));
if(res != LV_RESULT_OK) return;
lv_obj_invalidate(*g->obj_focus);
}
break;
}
}
}
void lv_group_focus_next(lv_group_t * group)
{
LV_ASSERT_NULL(group);
bool focus_changed = focus_next_core(group, lv_ll_get_head, lv_ll_get_next);
if(group->edge_cb) {
if(!focus_changed)
group->edge_cb(group, true);
}
}
void lv_group_focus_prev(lv_group_t * group)
{
LV_ASSERT_NULL(group);
bool focus_changed = focus_next_core(group, lv_ll_get_tail, lv_ll_get_prev);
if(group->edge_cb) {
if(!focus_changed)
group->edge_cb(group, false);
}
}
void lv_group_focus_freeze(lv_group_t * group, bool en)
{
LV_ASSERT_NULL(group);
if(en == false) group->frozen = 0;
else group->frozen = 1;
}
lv_result_t lv_group_send_data(lv_group_t * group, uint32_t c)
{
LV_ASSERT_NULL(group);
lv_obj_t * act = lv_group_get_focused(group);
if(act == NULL) return LV_RESULT_OK;
if(lv_obj_has_state(act, LV_STATE_DISABLED)) return LV_RESULT_OK;
return lv_obj_send_event(act, LV_EVENT_KEY, &c);
}
void lv_group_set_focus_cb(lv_group_t * group, lv_group_focus_cb_t focus_cb)
{
if(group == NULL) return;
group->focus_cb = focus_cb;
}
void lv_group_set_edge_cb(lv_group_t * group, lv_group_edge_cb_t edge_cb)
{
LV_ASSERT_NULL(group);
group->edge_cb = edge_cb;
}
void lv_group_set_editing(lv_group_t * group, bool edit)
{
LV_ASSERT_NULL(group);
uint8_t en_val = edit ? 1 : 0;
if(en_val == group->editing) return; /*Do not set the same mode again*/
group->editing = en_val;
lv_obj_t * focused = lv_group_get_focused(group);
if(focused) {
lv_result_t res = lv_obj_send_event(*group->obj_focus, LV_EVENT_FOCUSED, get_indev(group));
if(res != LV_RESULT_OK) return;
lv_obj_invalidate(focused);
}
}
void lv_group_set_refocus_policy(lv_group_t * group, lv_group_refocus_policy_t policy)
{
LV_ASSERT_NULL(group);
group->refocus_policy = policy & 0x01;
}
void lv_group_set_wrap(lv_group_t * group, bool en)
{
LV_ASSERT_NULL(group);
group->wrap = en ? 1 : 0;
}
lv_obj_t * lv_group_get_focused(const lv_group_t * group)
{
if(!group) return NULL;
if(group->obj_focus == NULL) return NULL;
return *group->obj_focus;
}
lv_group_focus_cb_t lv_group_get_focus_cb(const lv_group_t * group)
{
if(!group) return NULL;
return group->focus_cb;
}
lv_group_edge_cb_t lv_group_get_edge_cb(const lv_group_t * group)
{
if(!group) return NULL;
return group->edge_cb;
}
bool lv_group_get_editing(const lv_group_t * group)
{
if(!group) return false;
return group->editing;
}
bool lv_group_get_wrap(lv_group_t * group)
{
if(!group) return false;
return group->wrap;
}
uint32_t lv_group_get_obj_count(lv_group_t * group)
{
LV_ASSERT_NULL(group);
return lv_ll_get_len(&group->obj_ll);
}
lv_obj_t * lv_group_get_obj_by_index(lv_group_t * group, uint32_t index)
{
uint32_t len = 0;
lv_obj_t ** obj;
LV_LL_READ(&group->obj_ll, obj) {
if(len == index) {
return *obj;
}
len++;
}
return NULL;
}
uint32_t lv_group_get_count(void)
{
return lv_ll_get_len(group_ll_p);
}
lv_group_t * lv_group_by_index(uint32_t index)
{
uint32_t len = 0;
lv_group_t * group;
LV_LL_READ_BACK(group_ll_p, group) {
if(len == index) {
return group;
}
len++;
}
return NULL;
}
/**********************
* STATIC FUNCTIONS
**********************/
static void lv_group_refocus(lv_group_t * g)
{
/*Refocus must temporarily allow wrapping to work correctly*/
uint8_t temp_wrap = g->wrap;
g->wrap = 1;
if(g->refocus_policy == LV_GROUP_REFOCUS_POLICY_NEXT)
lv_group_focus_next(g);
else if(g->refocus_policy == LV_GROUP_REFOCUS_POLICY_PREV)
lv_group_focus_prev(g);
/*Restore wrap property*/
g->wrap = temp_wrap;
}
static bool focus_next_core(lv_group_t * group, void * (*begin)(const lv_ll_t *),
void * (*move)(const lv_ll_t *, const void *))
{
bool focus_changed = false;
if(group->frozen) return focus_changed;
lv_obj_t ** obj_next = group->obj_focus;
lv_obj_t ** obj_sentinel = NULL;
bool can_move = true;
bool can_begin = true;
for(;;) {
if(obj_next == NULL) {
if(group->wrap || obj_sentinel == NULL) {
if(!can_begin) return focus_changed;
obj_next = begin(&group->obj_ll);
can_move = false;
can_begin = false;
}
else {
/*Currently focused object is the last/first in the group, keep it that way*/
return focus_changed;
}
}
if(obj_sentinel == NULL) {
obj_sentinel = obj_next;
if(obj_sentinel == NULL) return focus_changed; /*Group is empty*/
}
if(can_move) {
obj_next = move(&group->obj_ll, obj_next);
/*Give up if we walked the entire list and haven't found another visible object*/
if(obj_next == obj_sentinel) return focus_changed;
}
can_move = true;
if(obj_next == NULL) continue;
if(lv_obj_get_state(*obj_next) & LV_STATE_DISABLED) continue;
/*Hidden objects don't receive focus.
*If any parent is hidden, the object is also hidden)*/
lv_obj_t * parent = *obj_next;
while(parent) {
if(lv_obj_has_flag(parent, LV_OBJ_FLAG_HIDDEN)) break;
parent = lv_obj_get_parent(parent);
}
if(parent && lv_obj_has_flag(parent, LV_OBJ_FLAG_HIDDEN)) continue;
/*If we got her a good candidate is found*/
break;
}
if(obj_next == group->obj_focus) return focus_changed; /*There's only one visible object and it's already focused*/
if(group->obj_focus) {
lv_result_t res = lv_obj_send_event(*group->obj_focus, LV_EVENT_DEFOCUSED, get_indev(group));
if(res != LV_RESULT_OK) return focus_changed;
lv_obj_invalidate(*group->obj_focus);
}
group->obj_focus = obj_next;
lv_result_t res = lv_obj_send_event(*group->obj_focus, LV_EVENT_FOCUSED, get_indev(group));
if(res != LV_RESULT_OK) return focus_changed;
lv_obj_invalidate(*group->obj_focus);
if(group->focus_cb) group->focus_cb(group);
focus_changed = true;
return focus_changed;
}
/**
* Find an indev preferably with POINTER type (because it's the most generic) that uses the given group.
* In other words, find an indev, that is related to the given group.
* In the worst case simply return the latest indev
* @param g a group the find in the indevs
* @return the suggested indev
*/
static lv_indev_t * get_indev(const lv_group_t * g)
{
lv_indev_t * indev_guess = NULL;
lv_indev_t * indev = lv_indev_get_next(NULL);
while(indev) {
lv_indev_type_t indev_type = lv_indev_get_type(indev);
/*Prefer POINTER*/
if(indev_type == LV_INDEV_TYPE_POINTER) return indev;
if(lv_indev_get_group(indev) == g) {
indev_guess = indev;
}
indev = lv_indev_get_next(indev);
}
return indev_guess;
}
+248
View File
@@ -0,0 +1,248 @@
/**
* @file lv_group.h
*
*/
#ifndef LV_GROUP_H
#define LV_GROUP_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../lv_conf_internal.h"
#include "../misc/lv_types.h"
#include "../misc/lv_ll.h"
/*********************
* DEFINES
*********************/
/** Predefined keys to control which Widget has focus via lv_group_send(group, c) */
typedef enum {
LV_KEY_UP = 17, /*0x11*/
LV_KEY_DOWN = 18, /*0x12*/
LV_KEY_RIGHT = 19, /*0x13*/
LV_KEY_LEFT = 20, /*0x14*/
LV_KEY_ESC = 27, /*0x1B*/
LV_KEY_DEL = 127, /*0x7F*/
LV_KEY_BACKSPACE = 8, /*0x08*/
LV_KEY_ENTER = 10, /*0x0A, '\n'*/
LV_KEY_NEXT = 9, /*0x09, '\t'*/
LV_KEY_PREV = 11, /*0x0B, '*/
LV_KEY_HOME = 2, /*0x02, STX*/
LV_KEY_END = 3, /*0x03, ETX*/
} lv_key_t;
/**********************
* TYPEDEFS
**********************/
typedef void (*lv_group_focus_cb_t)(lv_group_t *);
typedef void (*lv_group_edge_cb_t)(lv_group_t *, bool);
typedef enum {
LV_GROUP_REFOCUS_POLICY_NEXT = 0,
LV_GROUP_REFOCUS_POLICY_PREV = 1
} lv_group_refocus_policy_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Create new Widget group.
* @return pointer to the new Widget group
*/
lv_group_t * lv_group_create(void);
/**
* Delete group object.
* @param group pointer to a group
*/
void lv_group_delete(lv_group_t * group);
/**
* Set default group. New Widgets will be added to this group if it's enabled in
* their class with `add_to_def_group = true`.
* @param group pointer to a group (can be `NULL`)
*/
void lv_group_set_default(lv_group_t * group);
/**
* Get default group.
* @return pointer to the default group
*/
lv_group_t * lv_group_get_default(void);
/**
* Add an Widget to group.
* @param group pointer to a group
* @param obj pointer to a Widget to add
*/
void lv_group_add_obj(lv_group_t * group, lv_obj_t * obj);
/**
* Swap 2 Widgets in group. Widgets must be in the same group.
* @param obj1 pointer to a Widget
* @param obj2 pointer to another Widget
*/
void lv_group_swap_obj(lv_obj_t * obj1, lv_obj_t * obj2);
/**
* Remove a Widget from its group.
* @param obj pointer to Widget to remove
*/
void lv_group_remove_obj(lv_obj_t * obj);
/**
* Remove all Widgets from a group.
* @param group pointer to a group
*/
void lv_group_remove_all_objs(lv_group_t * group);
/**
* Focus on a Widget (defocus the current).
* @param obj pointer to Widget to focus on
*/
void lv_group_focus_obj(lv_obj_t * obj);
/**
* Focus on next Widget in a group (defocus the current).
* @param group pointer to a group
*/
void lv_group_focus_next(lv_group_t * group);
/**
* Focus on previous Widget in a group (defocus the current).
* @param group pointer to a group
*/
void lv_group_focus_prev(lv_group_t * group);
/**
* Do not allow changing focus from current Widget.
* @param group pointer to a group
* @param en true: freeze, false: release freezing (normal mode)
*/
void lv_group_focus_freeze(lv_group_t * group, bool en);
/**
* Send a control character to Widget that has focus in a group.
* @param group pointer to a group
* @param c a character (use LV_KEY_.. to navigate)
* @return result of Widget with focus in group.
*/
lv_result_t lv_group_send_data(lv_group_t * group, uint32_t c);
/**
* Set a function for a group which will be called when a new Widget has focus.
* @param group pointer to a group
* @param focus_cb the call back function or NULL if unused
*/
void lv_group_set_focus_cb(lv_group_t * group, lv_group_focus_cb_t focus_cb);
/**
* Set a function for a group which will be called when a focus edge is reached
* @param group pointer to a group
* @param edge_cb the call back function or NULL if unused
*/
void lv_group_set_edge_cb(lv_group_t * group, lv_group_edge_cb_t edge_cb);
/**
* Set whether the next or previous Widget in a group gets focus when Widget that has
* focus is deleted.
* @param group pointer to a group
* @param policy new refocus policy enum
*/
void lv_group_set_refocus_policy(lv_group_t * group, lv_group_refocus_policy_t policy);
/**
* Manually set the current mode (edit or navigate).
* @param group pointer to group
* @param edit true: edit mode; false: navigate mode
*/
void lv_group_set_editing(lv_group_t * group, bool edit);
/**
* Set whether moving focus to next/previous Widget will allow wrapping from
* first->last or last->first Widget.
* @param group pointer to group
* @param en true: wrapping enabled; false: wrapping disabled
*/
void lv_group_set_wrap(lv_group_t * group, bool en);
/**
* Get Widget that has focus, or NULL if there isn't one.
* @param group pointer to a group
* @return pointer to Widget with focus
*/
lv_obj_t * lv_group_get_focused(const lv_group_t * group);
/**
* Get focus callback function of a group.
* @param group pointer to a group
* @return the call back function or NULL if not set
*/
lv_group_focus_cb_t lv_group_get_focus_cb(const lv_group_t * group);
/**
* Get edge callback function of a group.
* @param group pointer to a group
* @return the call back function or NULL if not set
*/
lv_group_edge_cb_t lv_group_get_edge_cb(const lv_group_t * group);
/**
* Get current mode (edit or navigate).
* @param group pointer to group
* @return true: edit mode; false: navigate mode
*/
bool lv_group_get_editing(const lv_group_t * group);
/**
* Get whether moving focus to next/previous Widget will allow wrapping from
* first->last or last->first Widget.
* @param group pointer to group
*/
bool lv_group_get_wrap(lv_group_t * group);
/**
* Get number of Widgets in group.
* @param group pointer to a group
* @return number of Widgets in the group
*/
uint32_t lv_group_get_obj_count(lv_group_t * group);
/**
* Get nth Widget within group.
* @param group pointer to a group
* @param index index of Widget within the group
* @return pointer to Widget
*/
lv_obj_t * lv_group_get_obj_by_index(lv_group_t * group, uint32_t index);
/**
* Get the number of groups.
* @return number of groups
*/
uint32_t lv_group_get_count(void);
/**
* Get a group by its index.
* @param index index of the group
* @return pointer to the group
*/
lv_group_t * lv_group_by_index(uint32_t index);
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_GROUP_H*/
@@ -0,0 +1,75 @@
/**
* @file lv_group_private.h
*
*/
#ifndef LV_GROUP_PRIVATE_H
#define LV_GROUP_PRIVATE_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "lv_group.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**
* Groups can be used to logically hold objects so that they can be individually focused.
* They are NOT for laying out objects on a screen (try layouts for that).
*/
struct _lv_group_t {
lv_ll_t obj_ll; /**< Linked list to store the objects in the group*/
lv_obj_t ** obj_focus; /**< The object in focus*/
lv_group_focus_cb_t focus_cb; /**< A function to call when a new object is focused (optional)*/
lv_group_edge_cb_t edge_cb; /**< A function to call when an edge is reached, no more focus
targets are available in this direction (to allow edge feedback
like a sound or a scroll bounce) */
void * user_data;
uint8_t frozen : 1; /**< 1: can't focus to new object*/
uint8_t editing : 1; /**< 1: Edit mode, 0: Navigate mode*/
uint8_t refocus_policy : 1; /**< 1: Focus prev if focused on deletion. 0: Focus next if focused on
deletion.*/
uint8_t wrap : 1; /**< 1: Focus next/prev can wrap at end of list. 0: Focus next/prev stops at end
of list.*/
};
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Init the group module
* @remarks Internal function, do not call directly.
*/
void lv_group_init(void);
/**
* Deinit the group module
* @remarks Internal function, do not call directly.
*/
void lv_group_deinit(void);
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_GROUP_PRIVATE_H*/
File diff suppressed because it is too large Load Diff
+489
View File
@@ -0,0 +1,489 @@
/**
* @file lv_obj.h
*
*/
#ifndef LV_OBJ_H
#define LV_OBJ_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../lv_conf_internal.h"
#include "../misc/lv_types.h"
#include "../misc/lv_style.h"
#include "../misc/lv_area.h"
#include "../misc/lv_color.h"
#include "../misc/lv_assert.h"
#include "lv_obj_tree.h"
#include "lv_obj_pos.h"
#include "lv_obj_scroll.h"
#include "lv_obj_style.h"
#include "lv_obj_draw.h"
#include "lv_obj_class.h"
#include "lv_obj_event.h"
#include "lv_obj_property.h"
#include "lv_group.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**
* Possible states of a widget.
* OR-ed values are possible
*/
enum {
LV_STATE_DEFAULT = 0x0000,
LV_STATE_CHECKED = 0x0001,
LV_STATE_FOCUSED = 0x0002,
LV_STATE_FOCUS_KEY = 0x0004,
LV_STATE_EDITED = 0x0008,
LV_STATE_HOVERED = 0x0010,
LV_STATE_PRESSED = 0x0020,
LV_STATE_SCROLLED = 0x0040,
LV_STATE_DISABLED = 0x0080,
LV_STATE_USER_1 = 0x1000,
LV_STATE_USER_2 = 0x2000,
LV_STATE_USER_3 = 0x4000,
LV_STATE_USER_4 = 0x8000,
LV_STATE_ANY = 0xFFFF, /**< Special value can be used in some functions to target all states*/
};
/**
* The possible parts of widgets.
* The parts can be considered as the internal building block of the widgets.
* E.g. slider = background + indicator + knob
* Not all parts are used by every widget
*/
enum {
LV_PART_MAIN = 0x000000, /**< A background like rectangle*/
LV_PART_SCROLLBAR = 0x010000, /**< The scrollbar(s)*/
LV_PART_INDICATOR = 0x020000, /**< Indicator, e.g. for slider, bar, switch, or the tick box of the checkbox*/
LV_PART_KNOB = 0x030000, /**< Like handle to grab to adjust the value*/
LV_PART_SELECTED = 0x040000, /**< Indicate the currently selected option or section*/
LV_PART_ITEMS = 0x050000, /**< Used if the widget has multiple similar elements (e.g. table cells)*/
LV_PART_CURSOR = 0x060000, /**< Mark a specific place e.g. for text area's cursor or on a chart*/
LV_PART_CUSTOM_FIRST = 0x080000, /**< Extension point for custom widgets*/
LV_PART_ANY = 0x0F0000, /**< Special value can be used in some functions to target all parts*/
};
/**
* On/Off features controlling the object's behavior.
* OR-ed values are possible
*
* Note: update obj flags corresponding properties below
* whenever add/remove flags or change bit definition of flags.
*/
typedef enum {
LV_OBJ_FLAG_HIDDEN = (1L << 0), /**< Make the object hidden. (Like it wasn't there at all)*/
LV_OBJ_FLAG_CLICKABLE = (1L << 1), /**< Make the object clickable by the input devices*/
LV_OBJ_FLAG_CLICK_FOCUSABLE = (1L << 2), /**< Add focused state to the object when clicked*/
LV_OBJ_FLAG_CHECKABLE = (1L << 3), /**< Toggle checked state when the object is clicked*/
LV_OBJ_FLAG_SCROLLABLE = (1L << 4), /**< Make the object scrollable*/
LV_OBJ_FLAG_SCROLL_ELASTIC = (1L << 5), /**< Allow scrolling inside but with slower speed*/
LV_OBJ_FLAG_SCROLL_MOMENTUM = (1L << 6), /**< Make the object scroll further when "thrown"*/
LV_OBJ_FLAG_SCROLL_ONE = (1L << 7), /**< Allow scrolling only one snappable children*/
LV_OBJ_FLAG_SCROLL_CHAIN_HOR = (1L << 8), /**< Allow propagating the horizontal scroll to a parent*/
LV_OBJ_FLAG_SCROLL_CHAIN_VER = (1L << 9), /**< Allow propagating the vertical scroll to a parent*/
LV_OBJ_FLAG_SCROLL_CHAIN = (LV_OBJ_FLAG_SCROLL_CHAIN_HOR | LV_OBJ_FLAG_SCROLL_CHAIN_VER),
LV_OBJ_FLAG_SCROLL_ON_FOCUS = (1L << 10), /**< Automatically scroll object to make it visible when focused*/
LV_OBJ_FLAG_SCROLL_WITH_ARROW = (1L << 11), /**< Allow scrolling the focused object with arrow keys*/
LV_OBJ_FLAG_SNAPPABLE = (1L << 12), /**< If scroll snap is enabled on the parent it can snap to this object*/
LV_OBJ_FLAG_PRESS_LOCK = (1L << 13), /**< Keep the object pressed even if the press slid from the object*/
LV_OBJ_FLAG_EVENT_BUBBLE = (1L << 14), /**< Propagate the events to the parent too*/
LV_OBJ_FLAG_GESTURE_BUBBLE = (1L << 15), /**< Propagate the gestures to the parent*/
LV_OBJ_FLAG_ADV_HITTEST = (1L << 16), /**< Allow performing more accurate hit (click) test. E.g. consider rounded corners.*/
LV_OBJ_FLAG_IGNORE_LAYOUT = (1L << 17), /**< Make the object not positioned by the layouts*/
LV_OBJ_FLAG_FLOATING = (1L << 18), /**< Do not scroll the object when the parent scrolls and ignore layout*/
LV_OBJ_FLAG_SEND_DRAW_TASK_EVENTS = (1L << 19), /**< Send `LV_EVENT_DRAW_TASK_ADDED` events*/
LV_OBJ_FLAG_OVERFLOW_VISIBLE = (1L << 20),/**< Do not clip the children to the parent's ext draw size*/
#if LV_USE_FLEX
LV_OBJ_FLAG_FLEX_IN_NEW_TRACK = (1L << 21), /**< Start a new flex track on this item*/
#endif
LV_OBJ_FLAG_LAYOUT_1 = (1L << 23), /**< Custom flag, free to use by layouts*/
LV_OBJ_FLAG_LAYOUT_2 = (1L << 24), /**< Custom flag, free to use by layouts*/
LV_OBJ_FLAG_WIDGET_1 = (1L << 25), /**< Custom flag, free to use by widget*/
LV_OBJ_FLAG_WIDGET_2 = (1L << 26), /**< Custom flag, free to use by widget*/
LV_OBJ_FLAG_USER_1 = (1L << 27), /**< Custom flag, free to use by user*/
LV_OBJ_FLAG_USER_2 = (1L << 28), /**< Custom flag, free to use by user*/
LV_OBJ_FLAG_USER_3 = (1L << 29), /**< Custom flag, free to use by user*/
LV_OBJ_FLAG_USER_4 = (1L << 30), /**< Custom flag, free to use by user*/
} lv_obj_flag_t;
#if LV_USE_OBJ_PROPERTY
enum {
/*OBJ flag properties */
LV_PROPERTY_ID(OBJ, FLAG_START, LV_PROPERTY_TYPE_INT, 0),
LV_PROPERTY_ID(OBJ, FLAG_HIDDEN, LV_PROPERTY_TYPE_INT, 0),
LV_PROPERTY_ID(OBJ, FLAG_CLICKABLE, LV_PROPERTY_TYPE_INT, 1),
LV_PROPERTY_ID(OBJ, FLAG_CLICK_FOCUSABLE, LV_PROPERTY_TYPE_INT, 2),
LV_PROPERTY_ID(OBJ, FLAG_CHECKABLE, LV_PROPERTY_TYPE_INT, 3),
LV_PROPERTY_ID(OBJ, FLAG_SCROLLABLE, LV_PROPERTY_TYPE_INT, 4),
LV_PROPERTY_ID(OBJ, FLAG_SCROLL_ELASTIC, LV_PROPERTY_TYPE_INT, 5),
LV_PROPERTY_ID(OBJ, FLAG_SCROLL_MOMENTUM, LV_PROPERTY_TYPE_INT, 6),
LV_PROPERTY_ID(OBJ, FLAG_SCROLL_ONE, LV_PROPERTY_TYPE_INT, 7),
LV_PROPERTY_ID(OBJ, FLAG_SCROLL_CHAIN_HOR, LV_PROPERTY_TYPE_INT, 8),
LV_PROPERTY_ID(OBJ, FLAG_SCROLL_CHAIN_VER, LV_PROPERTY_TYPE_INT, 9),
LV_PROPERTY_ID(OBJ, FLAG_SCROLL_ON_FOCUS, LV_PROPERTY_TYPE_INT, 10),
LV_PROPERTY_ID(OBJ, FLAG_SCROLL_WITH_ARROW, LV_PROPERTY_TYPE_INT, 11),
LV_PROPERTY_ID(OBJ, FLAG_SNAPPABLE, LV_PROPERTY_TYPE_INT, 12),
LV_PROPERTY_ID(OBJ, FLAG_PRESS_LOCK, LV_PROPERTY_TYPE_INT, 13),
LV_PROPERTY_ID(OBJ, FLAG_EVENT_BUBBLE, LV_PROPERTY_TYPE_INT, 14),
LV_PROPERTY_ID(OBJ, FLAG_GESTURE_BUBBLE, LV_PROPERTY_TYPE_INT, 15),
LV_PROPERTY_ID(OBJ, FLAG_ADV_HITTEST, LV_PROPERTY_TYPE_INT, 16),
LV_PROPERTY_ID(OBJ, FLAG_IGNORE_LAYOUT, LV_PROPERTY_TYPE_INT, 17),
LV_PROPERTY_ID(OBJ, FLAG_FLOATING, LV_PROPERTY_TYPE_INT, 18),
LV_PROPERTY_ID(OBJ, FLAG_SEND_DRAW_TASK_EVENTS, LV_PROPERTY_TYPE_INT, 19),
LV_PROPERTY_ID(OBJ, FLAG_OVERFLOW_VISIBLE, LV_PROPERTY_TYPE_INT, 20),
LV_PROPERTY_ID(OBJ, FLAG_FLEX_IN_NEW_TRACK, LV_PROPERTY_TYPE_INT, 21),
LV_PROPERTY_ID(OBJ, FLAG_LAYOUT_1, LV_PROPERTY_TYPE_INT, 23),
LV_PROPERTY_ID(OBJ, FLAG_LAYOUT_2, LV_PROPERTY_TYPE_INT, 24),
LV_PROPERTY_ID(OBJ, FLAG_WIDGET_1, LV_PROPERTY_TYPE_INT, 25),
LV_PROPERTY_ID(OBJ, FLAG_WIDGET_2, LV_PROPERTY_TYPE_INT, 26),
LV_PROPERTY_ID(OBJ, FLAG_USER_1, LV_PROPERTY_TYPE_INT, 27),
LV_PROPERTY_ID(OBJ, FLAG_USER_2, LV_PROPERTY_TYPE_INT, 28),
LV_PROPERTY_ID(OBJ, FLAG_USER_3, LV_PROPERTY_TYPE_INT, 29),
LV_PROPERTY_ID(OBJ, FLAG_USER_4, LV_PROPERTY_TYPE_INT, 30),
LV_PROPERTY_ID(OBJ, FLAG_END, LV_PROPERTY_TYPE_INT, 30),
LV_PROPERTY_ID(OBJ, STATE_START, LV_PROPERTY_TYPE_INT, 31),
LV_PROPERTY_ID(OBJ, STATE_CHECKED, LV_PROPERTY_TYPE_INT, 31),
LV_PROPERTY_ID(OBJ, STATE_FOCUSED, LV_PROPERTY_TYPE_INT, 32),
LV_PROPERTY_ID(OBJ, STATE_FOCUS_KEY, LV_PROPERTY_TYPE_INT, 33),
LV_PROPERTY_ID(OBJ, STATE_EDITED, LV_PROPERTY_TYPE_INT, 34),
LV_PROPERTY_ID(OBJ, STATE_HOVERED, LV_PROPERTY_TYPE_INT, 35),
LV_PROPERTY_ID(OBJ, STATE_PRESSED, LV_PROPERTY_TYPE_INT, 36),
LV_PROPERTY_ID(OBJ, STATE_SCROLLED, LV_PROPERTY_TYPE_INT, 37),
LV_PROPERTY_ID(OBJ, STATE_DISABLED, LV_PROPERTY_TYPE_INT, 38),
/*not used bit8-bit11*/
LV_PROPERTY_ID(OBJ, STATE_USER_1, LV_PROPERTY_TYPE_INT, 43),
LV_PROPERTY_ID(OBJ, STATE_USER_2, LV_PROPERTY_TYPE_INT, 44),
LV_PROPERTY_ID(OBJ, STATE_USER_3, LV_PROPERTY_TYPE_INT, 45),
LV_PROPERTY_ID(OBJ, STATE_USER_4, LV_PROPERTY_TYPE_INT, 46),
LV_PROPERTY_ID(OBJ, STATE_ANY, LV_PROPERTY_TYPE_INT, 47),
LV_PROPERTY_ID(OBJ, STATE_END, LV_PROPERTY_TYPE_INT, 47),
/*OBJ normal properties*/
LV_PROPERTY_ID(OBJ, PARENT, LV_PROPERTY_TYPE_OBJ, 48),
LV_PROPERTY_ID(OBJ, X, LV_PROPERTY_TYPE_INT, 49),
LV_PROPERTY_ID(OBJ, Y, LV_PROPERTY_TYPE_INT, 50),
LV_PROPERTY_ID(OBJ, W, LV_PROPERTY_TYPE_INT, 51),
LV_PROPERTY_ID(OBJ, H, LV_PROPERTY_TYPE_INT, 52),
LV_PROPERTY_ID(OBJ, CONTENT_WIDTH, LV_PROPERTY_TYPE_INT, 53),
LV_PROPERTY_ID(OBJ, CONTENT_HEIGHT, LV_PROPERTY_TYPE_INT, 54),
LV_PROPERTY_ID(OBJ, LAYOUT, LV_PROPERTY_TYPE_INT, 55),
LV_PROPERTY_ID(OBJ, ALIGN, LV_PROPERTY_TYPE_INT, 56),
LV_PROPERTY_ID(OBJ, SCROLLBAR_MODE, LV_PROPERTY_TYPE_INT, 57),
LV_PROPERTY_ID(OBJ, SCROLL_DIR, LV_PROPERTY_TYPE_INT, 58),
LV_PROPERTY_ID(OBJ, SCROLL_SNAP_X, LV_PROPERTY_TYPE_INT, 59),
LV_PROPERTY_ID(OBJ, SCROLL_SNAP_Y, LV_PROPERTY_TYPE_INT, 60),
LV_PROPERTY_ID(OBJ, SCROLL_X, LV_PROPERTY_TYPE_INT, 61),
LV_PROPERTY_ID(OBJ, SCROLL_Y, LV_PROPERTY_TYPE_INT, 62),
LV_PROPERTY_ID(OBJ, SCROLL_TOP, LV_PROPERTY_TYPE_INT, 63),
LV_PROPERTY_ID(OBJ, SCROLL_BOTTOM, LV_PROPERTY_TYPE_INT, 64),
LV_PROPERTY_ID(OBJ, SCROLL_LEFT, LV_PROPERTY_TYPE_INT, 65),
LV_PROPERTY_ID(OBJ, SCROLL_RIGHT, LV_PROPERTY_TYPE_INT, 66),
LV_PROPERTY_ID(OBJ, SCROLL_END, LV_PROPERTY_TYPE_INT, 67),
LV_PROPERTY_ID(OBJ, EXT_DRAW_SIZE, LV_PROPERTY_TYPE_INT, 68),
LV_PROPERTY_ID(OBJ, EVENT_COUNT, LV_PROPERTY_TYPE_INT, 69),
LV_PROPERTY_ID(OBJ, SCREEN, LV_PROPERTY_TYPE_OBJ, 70),
LV_PROPERTY_ID(OBJ, DISPLAY, LV_PROPERTY_TYPE_POINTER, 71),
LV_PROPERTY_ID(OBJ, CHILD_COUNT, LV_PROPERTY_TYPE_INT, 72),
LV_PROPERTY_ID(OBJ, INDEX, LV_PROPERTY_TYPE_INT, 73),
LV_PROPERTY_OBJ_END,
};
#endif
/**
* Make the base object's class publicly available.
*/
LV_ATTRIBUTE_EXTERN_DATA extern const lv_obj_class_t lv_obj_class;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Create a base object (a rectangle)
* @param parent pointer to a parent object. If NULL then a screen will be created.
* @return pointer to the new object
*/
lv_obj_t * lv_obj_create(lv_obj_t * parent);
/*=====================
* Setter functions
*====================*/
/**
* Set one or more flags
* @param obj pointer to an object
* @param f OR-ed values from `lv_obj_flag_t` to set.
*/
void lv_obj_add_flag(lv_obj_t * obj, lv_obj_flag_t f);
/**
* Remove one or more flags
* @param obj pointer to an object
* @param f OR-ed values from `lv_obj_flag_t` to clear.
*/
void lv_obj_remove_flag(lv_obj_t * obj, lv_obj_flag_t f);
/**
* Set add or remove one or more flags.
* @param obj pointer to an object
* @param f OR-ed values from `lv_obj_flag_t` to update.
* @param v true: add the flags; false: remove the flags
*/
void lv_obj_set_flag(lv_obj_t * obj, lv_obj_flag_t f, bool v);
/**
* Add one or more states to the object. The other state bits will remain unchanged.
* If specified in the styles, transition animation will be started from the previous state to the current.
* @param obj pointer to an object
* @param state the states to add. E.g `LV_STATE_PRESSED | LV_STATE_FOCUSED`
*/
void lv_obj_add_state(lv_obj_t * obj, lv_state_t state);
/**
* Remove one or more states to the object. The other state bits will remain unchanged.
* If specified in the styles, transition animation will be started from the previous state to the current.
* @param obj pointer to an object
* @param state the states to add. E.g `LV_STATE_PRESSED | LV_STATE_FOCUSED`
*/
void lv_obj_remove_state(lv_obj_t * obj, lv_state_t state);
/**
* Add or remove one or more states to the object. The other state bits will remain unchanged.
* @param obj pointer to an object
* @param state the states to add. E.g `LV_STATE_PRESSED | LV_STATE_FOCUSED`
* @param v true: add the states; false: remove the states
*/
void lv_obj_set_state(lv_obj_t * obj, lv_state_t state, bool v);
/**
* Set the user_data field of the object
* @param obj pointer to an object
* @param user_data pointer to the new user_data.
*/
void lv_obj_set_user_data(lv_obj_t * obj, void * user_data);
/*=======================
* Getter functions
*======================*/
/**
* Check if a given flag or all the given flags are set on an object.
* @param obj pointer to an object
* @param f the flag(s) to check (OR-ed values can be used)
* @return true: all flags are set; false: not all flags are set
*/
bool lv_obj_has_flag(const lv_obj_t * obj, lv_obj_flag_t f);
/**
* Check if a given flag or any of the flags are set on an object.
* @param obj pointer to an object
* @param f the flag(s) to check (OR-ed values can be used)
* @return true: at least one flag is set; false: none of the flags are set
*/
bool lv_obj_has_flag_any(const lv_obj_t * obj, lv_obj_flag_t f);
/**
* Get the state of an object
* @param obj pointer to an object
* @return the state (OR-ed values from `lv_state_t`)
*/
lv_state_t lv_obj_get_state(const lv_obj_t * obj);
/**
* Check if the object is in a given state or not.
* @param obj pointer to an object
* @param state a state or combination of states to check
* @return true: `obj` is in `state`; false: `obj` is not in `state`
*/
bool lv_obj_has_state(const lv_obj_t * obj, lv_state_t state);
/**
* Get the group of the object
* @param obj pointer to an object
* @return the pointer to group of the object
*/
lv_group_t * lv_obj_get_group(const lv_obj_t * obj);
/**
* Get the user_data field of the object
* @param obj pointer to an object
* @return the pointer to the user_data of the object
*/
void * lv_obj_get_user_data(lv_obj_t * obj);
/*=======================
* Other functions
*======================*/
/**
* Allocate special data for an object if not allocated yet.
* @param obj pointer to an object
*/
void lv_obj_allocate_spec_attr(lv_obj_t * obj);
/**
* Check the type of obj.
* @param obj pointer to an object
* @param class_p a class to check (e.g. `lv_slider_class`)
* @return true: `class_p` is the `obj` class.
*/
bool lv_obj_check_type(const lv_obj_t * obj, const lv_obj_class_t * class_p);
/**
* Check if any object has a given class (type).
* It checks the ancestor classes too.
* @param obj pointer to an object
* @param class_p a class to check (e.g. `lv_slider_class`)
* @return true: `obj` has the given class
*/
bool lv_obj_has_class(const lv_obj_t * obj, const lv_obj_class_t * class_p);
/**
* Get the class (type) of the object
* @param obj pointer to an object
* @return the class (type) of the object
*/
const lv_obj_class_t * lv_obj_get_class(const lv_obj_t * obj);
/**
* Check if any object is still "alive".
* @param obj pointer to an object
* @return true: valid
*/
bool lv_obj_is_valid(const lv_obj_t * obj);
/**
* Utility to set an object reference to NULL when it gets deleted.
* The reference should be in a location that will not become invalid
* during the object's lifetime, i.e. static or allocated.
* @param obj_ptr a pointer to a pointer to an object
*/
void lv_obj_null_on_delete(lv_obj_t ** obj_ptr);
#if LV_USE_OBJ_ID
/**
* Set an id for an object.
* @param obj pointer to an object
* @param id the id of the object
*/
void lv_obj_set_id(lv_obj_t * obj, void * id);
/**
* Get the id of an object.
* @param obj pointer to an object
* @return the id of the object
*/
void * lv_obj_get_id(const lv_obj_t * obj);
/**
* DEPRECATED IDs are used only to print the widget trees.
* To find a widget use `lv_obj_find_by_name`
*
* Get the child object by its id.
* It will check children and grandchildren recursively.
* Function `lv_obj_id_compare` is used to matched obj id with given id.
*
* @param obj pointer to an object
* @param id the id of the child object
* @return pointer to the child object or NULL if not found
*/
lv_obj_t * lv_obj_find_by_id(const lv_obj_t * obj, const void * id);
/**
* Assign id to object if not previously assigned.
* This function gets called automatically when LV_OBJ_ID_AUTO_ASSIGN is enabled.
*
* Set `LV_USE_OBJ_ID_BUILTIN` to use the builtin method to generate object ID.
* Otherwise, these functions including `lv_obj_[set|assign|free|stringify]_id` and
* `lv_obj_id_compare`should be implemented externally.
*
* @param class_p the class this obj belongs to. Note obj->class_p is the class currently being constructed.
* @param obj pointer to an object
*/
void lv_obj_assign_id(const lv_obj_class_t * class_p, lv_obj_t * obj);
/**
* Free resources allocated by `lv_obj_assign_id` or `lv_obj_set_id`.
* This function is also called automatically when object is deleted.
* @param obj pointer to an object
*/
void lv_obj_free_id(lv_obj_t * obj);
/**
* Compare two obj id, return 0 if they are equal.
*
* Set `LV_USE_OBJ_ID_BUILTIN` to use the builtin method for compare.
* Otherwise, it must be implemented externally.
*
* @param id1: the first id
* @param id2: the second id
* @return 0 if they are equal, non-zero otherwise.
*/
int lv_obj_id_compare(const void * id1, const void * id2);
/**
* Format an object's id into a string.
* @param obj pointer to an object
* @param buf buffer to write the string into
* @param len length of the buffer
*/
const char * lv_obj_stringify_id(lv_obj_t * obj, char * buf, uint32_t len);
#if LV_USE_OBJ_ID_BUILTIN
/**
* Free resources used by builtin ID generator.
*/
void lv_objid_builtin_destroy(void);
#endif
#endif /*LV_USE_OBJ_ID*/
/**********************
* MACROS
**********************/
#if LV_USE_ASSERT_OBJ
# define LV_ASSERT_OBJ(obj_p, obj_class) \
do { \
LV_ASSERT_MSG(obj_p != NULL, "The object is NULL"); \
LV_ASSERT_MSG(lv_obj_has_class(obj_p, obj_class) == true, "Incompatible object type."); \
LV_ASSERT_MSG(lv_obj_is_valid(obj_p) == true, "The object is invalid, deleted or corrupted?"); \
} while(0)
# else
# define LV_ASSERT_OBJ(obj_p, obj_class) LV_ASSERT_NULL(obj_p)
#endif
#if LV_USE_LOG && LV_LOG_TRACE_OBJ_CREATE
# define LV_TRACE_OBJ_CREATE(...) LV_LOG_TRACE(__VA_ARGS__)
#else
# define LV_TRACE_OBJ_CREATE(...)
#endif
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_OBJ_H*/
@@ -0,0 +1,204 @@
/**
* @file lv_obj_class.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_obj_class_private.h"
#include "lv_obj_private.h"
#include "../themes/lv_theme.h"
#include "../display/lv_display.h"
#include "../display/lv_display_private.h"
#include "../stdlib/lv_string.h"
/*********************
* DEFINES
*********************/
#define MY_CLASS (&lv_obj_class)
/**********************
* TYPEDEFS
**********************/
/**********************
* GLOBAL PROTOTYPES
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static void lv_obj_construct(const lv_obj_class_t * class_p, lv_obj_t * obj);
static uint32_t get_instance_size(const lv_obj_class_t * class_p);
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
lv_obj_t * lv_obj_class_create_obj(const lv_obj_class_t * class_p, lv_obj_t * parent)
{
LV_TRACE_OBJ_CREATE("Creating object with %p class on %p parent", (void *)class_p, (void *)parent);
uint32_t s = get_instance_size(class_p);
lv_obj_t * obj = lv_malloc_zeroed(s);
if(obj == NULL) return NULL;
obj->class_p = class_p;
obj->parent = parent;
/*Create a screen*/
if(parent == NULL) {
LV_TRACE_OBJ_CREATE("creating a screen");
lv_display_t * disp = lv_display_get_default();
if(!disp) {
LV_LOG_WARN("No display created yet. No place to assign the new screen");
lv_free(obj);
return NULL;
}
if(disp->screens == NULL) {
disp->screen_cnt = 0;
}
lv_obj_t ** screens = lv_realloc(disp->screens, sizeof(lv_obj_t *) * (disp->screen_cnt + 1));
LV_ASSERT_MALLOC(screens);
if(screens == NULL) {
lv_free(obj);
return NULL;
}
disp->screen_cnt++;
disp->screens = screens;
disp->screens[disp->screen_cnt - 1] = obj;
/*Set coordinates to full screen size*/
obj->coords.x1 = 0;
obj->coords.y1 = 0;
obj->coords.x2 = lv_display_get_horizontal_resolution(NULL) - 1;
obj->coords.y2 = lv_display_get_vertical_resolution(NULL) - 1;
}
/*Create a normal object*/
else {
LV_TRACE_OBJ_CREATE("creating normal object");
LV_ASSERT_OBJ(parent, MY_CLASS);
if(parent->spec_attr == NULL) {
lv_obj_allocate_spec_attr(parent);
}
parent->spec_attr->child_cnt++;
parent->spec_attr->children = lv_realloc(parent->spec_attr->children,
sizeof(lv_obj_t *) * parent->spec_attr->child_cnt);
parent->spec_attr->children[parent->spec_attr->child_cnt - 1] = obj;
}
return obj;
}
void lv_obj_class_init_obj(lv_obj_t * obj)
{
if(obj == NULL) return;
lv_obj_mark_layout_as_dirty(obj);
lv_obj_enable_style_refresh(false);
lv_theme_apply(obj);
lv_obj_construct(obj->class_p, obj);
lv_obj_enable_style_refresh(true);
lv_obj_refresh_style(obj, LV_PART_ANY, LV_STYLE_PROP_ANY);
lv_obj_refresh_self_size(obj);
lv_group_t * def_group = lv_group_get_default();
if(def_group && lv_obj_is_group_def(obj)) {
lv_group_add_obj(def_group, obj);
}
lv_obj_t * parent = lv_obj_get_parent(obj);
if(parent) {
/*Call the ancestor's event handler to the parent to notify it about the new child.
*Also triggers layout update*/
lv_obj_send_event(parent, LV_EVENT_CHILD_CHANGED, obj);
lv_obj_send_event(parent, LV_EVENT_CHILD_CREATED, obj);
/*Invalidate the area if not screen created*/
lv_obj_invalidate(obj);
}
}
void lv_obj_destruct(lv_obj_t * obj)
{
if(obj->class_p->destructor_cb) obj->class_p->destructor_cb(obj->class_p, obj);
if(obj->class_p->base_class) {
/*Don't let the descendant methods run during destructing the ancestor type*/
obj->class_p = obj->class_p->base_class;
/*Call the base class's destructor too*/
lv_obj_destruct(obj);
}
}
bool lv_obj_is_editable(lv_obj_t * obj)
{
const lv_obj_class_t * class_p = obj->class_p;
/*Find a base in which editable is set*/
while(class_p && class_p->editable == LV_OBJ_CLASS_EDITABLE_INHERIT) class_p = class_p->base_class;
if(class_p == NULL) return false;
return class_p->editable == LV_OBJ_CLASS_EDITABLE_TRUE;
}
bool lv_obj_is_group_def(lv_obj_t * obj)
{
const lv_obj_class_t * class_p = obj->class_p;
/*Find a base in which group_def is set*/
while(class_p && class_p->group_def == LV_OBJ_CLASS_GROUP_DEF_INHERIT) class_p = class_p->base_class;
if(class_p == NULL) return false;
return class_p->group_def == LV_OBJ_CLASS_GROUP_DEF_TRUE;
}
/**********************
* STATIC FUNCTIONS
**********************/
static void lv_obj_construct(const lv_obj_class_t * class_p, lv_obj_t * obj)
{
if(obj->class_p->base_class) {
const lv_obj_class_t * original_class_p = obj->class_p;
/*Don't let the descendant methods run during constructing the ancestor type*/
obj->class_p = obj->class_p->base_class;
/*Construct the base first*/
lv_obj_construct(class_p, obj);
/*Restore the original class*/
obj->class_p = original_class_p;
}
if(obj->class_p->constructor_cb) obj->class_p->constructor_cb(class_p, obj);
}
static uint32_t get_instance_size(const lv_obj_class_t * class_p)
{
/*Find a base in which instance size is set*/
const lv_obj_class_t * base = class_p;
while(base && base->instance_size == 0) base = base->base_class;
if(base == NULL) return 0; /*Never happens: set at least in `lv_obj` class*/
return base->instance_size;
}
@@ -0,0 +1,72 @@
/**
* @file lv_obj_class.h
*
*/
#ifndef LV_OBJ_CLASS_H
#define LV_OBJ_CLASS_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../misc/lv_types.h"
#include "../misc/lv_area.h"
#include "lv_obj_property.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
typedef enum {
LV_OBJ_CLASS_EDITABLE_INHERIT, /**< Check the base class. Must have 0 value to let zero initialized class inherit*/
LV_OBJ_CLASS_EDITABLE_TRUE,
LV_OBJ_CLASS_EDITABLE_FALSE,
} lv_obj_class_editable_t;
typedef enum {
LV_OBJ_CLASS_GROUP_DEF_INHERIT, /**< Check the base class. Must have 0 value to let zero initialized class inherit*/
LV_OBJ_CLASS_GROUP_DEF_TRUE,
LV_OBJ_CLASS_GROUP_DEF_FALSE,
} lv_obj_class_group_def_t;
typedef enum {
LV_OBJ_CLASS_THEME_INHERITABLE_FALSE, /**< Do not inherit theme from base class. */
LV_OBJ_CLASS_THEME_INHERITABLE_TRUE,
} lv_obj_class_theme_inheritable_t;
typedef void (*lv_obj_class_event_cb_t)(lv_obj_class_t * class_p, lv_event_t * e);
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Create an object form a class descriptor
* @param class_p pointer to a class
* @param parent pointer to an object where the new object should be created
* @return pointer to the created object
*/
lv_obj_t * lv_obj_class_create_obj(const lv_obj_class_t * class_p, lv_obj_t * parent);
void lv_obj_class_init_obj(lv_obj_t * obj);
bool lv_obj_is_editable(lv_obj_t * obj);
bool lv_obj_is_group_def(lv_obj_t * obj);
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_OBJ_CLASS_H*/
@@ -0,0 +1,78 @@
/**
* @file lv_obj_class_private.h
*
*/
#ifndef LV_OBJ_CLASS_PRIVATE_H
#define LV_OBJ_CLASS_PRIVATE_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "lv_obj_class.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**
* Describe the common methods of every object.
* Similar to a C++ class.
*/
struct _lv_obj_class_t {
const lv_obj_class_t * base_class;
/** class_p is the final class while obj->class_p is the class currently being [de]constructed. */
void (*constructor_cb)(const lv_obj_class_t * class_p, lv_obj_t * obj);
void (*destructor_cb)(const lv_obj_class_t * class_p, lv_obj_t * obj);
/** class_p is the class in which event is being processed. */
void (*event_cb)(const lv_obj_class_t * class_p, lv_event_t * e); /**< Widget type specific event function*/
#if LV_USE_OBJ_PROPERTY
uint32_t prop_index_start;
uint32_t prop_index_end;
const lv_property_ops_t * properties;
uint32_t properties_count;
#if LV_USE_OBJ_PROPERTY_NAME
/* An array of property ID and name */
const lv_property_name_t * property_names;
uint32_t names_count;
#endif
#endif
void * user_data;
const char * name;
int32_t width_def;
int32_t height_def;
uint32_t editable : 2; /**< Value from ::lv_obj_class_editable_t*/
uint32_t group_def : 2; /**< Value from ::lv_obj_class_group_def_t*/
uint32_t instance_size : 16;
uint32_t theme_inheritable : 1; /**< Value from ::lv_obj_class_theme_inheritable_t*/
};
/**********************
* GLOBAL PROTOTYPES
**********************/
void lv_obj_destruct(lv_obj_t * obj);
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_OBJ_CLASS_PRIVATE_H*/
@@ -0,0 +1,454 @@
/**
* @file lv_obj_draw.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_obj_draw_private.h"
#include "lv_obj_private.h"
#include "lv_obj_style.h"
#include "../display/lv_display.h"
#include "../indev/lv_indev.h"
#include "../stdlib/lv_string.h"
#include "../draw/lv_draw_arc.h"
/*********************
* DEFINES
*********************/
#define MY_CLASS (&lv_obj_class)
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static inline lv_opa_t get_layer_opa(const lv_obj_t * obj, lv_part_t part, const lv_draw_dsc_base_t * base_dsc);
static lv_color_t normal_apply_layer_recolor(const lv_obj_t * obj, lv_part_t part, const lv_draw_dsc_base_t * base_dsc,
lv_color_t color);
static lv_color32_t image_apply_layer_recolor(const lv_obj_t * obj, lv_part_t part,
const lv_draw_dsc_base_t * base_dsc, lv_color_t color, lv_opa_t opa);
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
void lv_obj_init_draw_rect_dsc(lv_obj_t * obj, lv_part_t part, lv_draw_rect_dsc_t * draw_dsc)
{
LV_PROFILER_DRAW_BEGIN;
draw_dsc->base.obj = obj;
draw_dsc->base.part = part;
lv_opa_t opa = get_layer_opa(obj, part, &draw_dsc->base);
if(part != LV_PART_MAIN) {
if(opa <= LV_OPA_MIN) {
draw_dsc->bg_opa = LV_OPA_TRANSP;
draw_dsc->bg_image_opa = LV_OPA_TRANSP;
draw_dsc->border_opa = LV_OPA_TRANSP;
draw_dsc->outline_opa = LV_OPA_TRANSP;
draw_dsc->shadow_opa = LV_OPA_TRANSP;
LV_PROFILER_DRAW_END;
return;
}
}
draw_dsc->radius = lv_obj_get_style_radius(obj, part);
if(draw_dsc->bg_opa != LV_OPA_TRANSP) {
draw_dsc->bg_opa = lv_obj_get_style_bg_opa(obj, part);
if(draw_dsc->bg_opa > LV_OPA_MIN) {
lv_color_t bg_color = lv_obj_get_style_bg_color_filtered(obj, part);
draw_dsc->bg_color = normal_apply_layer_recolor(obj, part, &draw_dsc->base, bg_color);
const lv_grad_dsc_t * grad = lv_obj_get_style_bg_grad(obj, part);
if(grad && grad->dir != LV_GRAD_DIR_NONE) {
lv_memcpy(&draw_dsc->bg_grad, grad, sizeof(*grad));
}
else {
draw_dsc->bg_grad.dir = lv_obj_get_style_bg_grad_dir(obj, part);
if(draw_dsc->bg_grad.dir != LV_GRAD_DIR_NONE) {
draw_dsc->bg_grad.stops[0].color = draw_dsc->bg_color;
lv_color_t bg_grad_color = lv_obj_get_style_bg_grad_color_filtered(obj, part);
draw_dsc->bg_grad.stops[1].color = normal_apply_layer_recolor(obj, part, &draw_dsc->base, bg_grad_color);
draw_dsc->bg_grad.stops[0].frac = lv_obj_get_style_bg_main_stop(obj, part);
draw_dsc->bg_grad.stops[1].frac = lv_obj_get_style_bg_grad_stop(obj, part);
draw_dsc->bg_grad.stops[0].opa = lv_obj_get_style_bg_main_opa(obj, part);
draw_dsc->bg_grad.stops[1].opa = lv_obj_get_style_bg_grad_opa(obj, part);
}
}
}
}
if(draw_dsc->border_opa != LV_OPA_TRANSP) {
draw_dsc->border_width = lv_obj_get_style_border_width(obj, part);
if(draw_dsc->border_width) {
draw_dsc->border_opa = lv_obj_get_style_border_opa(obj, part);
if(draw_dsc->border_opa > LV_OPA_MIN) {
draw_dsc->border_side = lv_obj_get_style_border_side(obj, part);
lv_color_t border_color = lv_obj_get_style_border_color_filtered(obj, part);
draw_dsc->border_color = normal_apply_layer_recolor(obj, part, &draw_dsc->base, border_color);
}
}
}
if(draw_dsc->outline_opa != LV_OPA_TRANSP) {
draw_dsc->outline_width = lv_obj_get_style_outline_width(obj, part);
if(draw_dsc->outline_width) {
draw_dsc->outline_opa = lv_obj_get_style_outline_opa(obj, part);
if(draw_dsc->outline_opa > LV_OPA_MIN) {
draw_dsc->outline_pad = lv_obj_get_style_outline_pad(obj, part);
lv_color_t outline_color = lv_obj_get_style_outline_color_filtered(obj, part);
draw_dsc->outline_color = normal_apply_layer_recolor(obj, part, &draw_dsc->base, outline_color);
}
}
}
if(draw_dsc->bg_image_opa != LV_OPA_TRANSP) {
draw_dsc->bg_image_src = lv_obj_get_style_bg_image_src(obj, part);
if(draw_dsc->bg_image_src) {
draw_dsc->bg_image_opa = lv_obj_get_style_bg_image_opa(obj, part);
if(draw_dsc->bg_image_opa > LV_OPA_MIN) {
if(lv_image_src_get_type(draw_dsc->bg_image_src) == LV_IMAGE_SRC_SYMBOL) {
draw_dsc->bg_image_symbol_font = lv_obj_get_style_text_font(obj, part);
lv_color_t text_color = lv_obj_get_style_text_color_filtered(obj, part);
draw_dsc->bg_image_recolor = normal_apply_layer_recolor(obj, part, &draw_dsc->base, text_color);
}
else {
lv_color_t bg_image_recolor = lv_obj_get_style_bg_image_recolor_filtered(obj, part);
lv_opa_t bg_image_recolor_opa = lv_obj_get_style_bg_image_recolor_opa(obj, part);
lv_color32_t result = image_apply_layer_recolor(obj, part, &draw_dsc->base, bg_image_recolor, bg_image_recolor_opa);
draw_dsc->bg_image_recolor_opa = result.alpha;
draw_dsc->bg_image_recolor = lv_color_make(result.red, result.green, result.blue);
draw_dsc->bg_image_tiled = lv_obj_get_style_bg_image_tiled(obj, part);
}
}
}
}
if(draw_dsc->shadow_opa) {
draw_dsc->shadow_width = lv_obj_get_style_shadow_width(obj, part);
if(draw_dsc->shadow_width) {
if(draw_dsc->shadow_opa > LV_OPA_MIN) {
draw_dsc->shadow_opa = lv_obj_get_style_shadow_opa(obj, part);
if(draw_dsc->shadow_opa > LV_OPA_MIN) {
draw_dsc->shadow_offset_x = lv_obj_get_style_shadow_offset_x(obj, part);
draw_dsc->shadow_offset_y = lv_obj_get_style_shadow_offset_y(obj, part);
draw_dsc->shadow_spread = lv_obj_get_style_shadow_spread(obj, part);
lv_color_t shadow_color = lv_obj_get_style_shadow_color_filtered(obj, part);
draw_dsc->shadow_color = normal_apply_layer_recolor(obj, part, &draw_dsc->base, shadow_color);
}
}
}
}
if(opa < LV_OPA_MAX) {
draw_dsc->bg_opa = LV_OPA_MIX2(draw_dsc->bg_opa, opa);
draw_dsc->bg_image_opa = LV_OPA_MIX2(draw_dsc->bg_image_opa, opa);
draw_dsc->border_opa = LV_OPA_MIX2(draw_dsc->border_opa, opa);
draw_dsc->shadow_opa = LV_OPA_MIX2(draw_dsc->shadow_opa, opa);
draw_dsc->outline_opa = LV_OPA_MIX2(draw_dsc->outline_opa, opa);
}
LV_PROFILER_DRAW_END;
}
void lv_obj_init_draw_label_dsc(lv_obj_t * obj, lv_part_t part, lv_draw_label_dsc_t * draw_dsc)
{
LV_PROFILER_DRAW_BEGIN;
draw_dsc->base.obj = obj;
draw_dsc->base.part = part;
draw_dsc->opa = lv_obj_get_style_text_opa(obj, part);
if(draw_dsc->opa <= LV_OPA_MIN) {
LV_PROFILER_DRAW_END;
return;
}
lv_opa_t opa = get_layer_opa(obj, part, &draw_dsc->base);
if(opa < LV_OPA_MAX) {
draw_dsc->opa = LV_OPA_MIX2(draw_dsc->opa, opa);
}
if(draw_dsc->opa <= LV_OPA_MIN) {
LV_PROFILER_DRAW_END;
return;
}
lv_color_t text_color = lv_obj_get_style_text_color_filtered(obj, part);
draw_dsc->color = normal_apply_layer_recolor(obj, part, &draw_dsc->base, text_color);
draw_dsc->letter_space = lv_obj_get_style_text_letter_space(obj, part);
draw_dsc->line_space = lv_obj_get_style_text_line_space(obj, part);
draw_dsc->decor = lv_obj_get_style_text_decor(obj, part);
draw_dsc->font = lv_obj_get_style_text_font(obj, part);
#if LV_USE_BIDI
draw_dsc->bidi_dir = lv_obj_get_style_base_dir(obj, LV_PART_MAIN);
#endif
draw_dsc->align = lv_obj_get_style_text_align(obj, part);
LV_PROFILER_DRAW_END;
}
void lv_obj_init_draw_image_dsc(lv_obj_t * obj, lv_part_t part, lv_draw_image_dsc_t * draw_dsc)
{
LV_PROFILER_DRAW_BEGIN;
draw_dsc->base.obj = obj;
draw_dsc->base.part = part;
draw_dsc->opa = lv_obj_get_style_image_opa(obj, part);
if(draw_dsc->opa <= LV_OPA_MIN) {
LV_PROFILER_DRAW_END;
return;
}
lv_opa_t opa = get_layer_opa(obj, part, &draw_dsc->base);
if(opa < LV_OPA_MAX) {
draw_dsc->opa = LV_OPA_MIX2(draw_dsc->opa, opa);
}
if(draw_dsc->opa <= LV_OPA_MIN) {
LV_PROFILER_DRAW_END;
return;
}
draw_dsc->rotation = 0;
draw_dsc->scale_x = LV_SCALE_NONE;
draw_dsc->scale_y = LV_SCALE_NONE;
draw_dsc->pivot.x = lv_area_get_width(&obj->coords) / 2;
draw_dsc->pivot.y = lv_area_get_height(&obj->coords) / 2;
lv_color_t recolor = lv_obj_get_style_image_recolor_filtered(obj, part);
lv_opa_t recolor_opa = lv_obj_get_style_image_recolor_opa(obj, part);
lv_color32_t result = image_apply_layer_recolor(obj, part, &draw_dsc->base, recolor, recolor_opa);
draw_dsc->recolor_opa = result.alpha;
draw_dsc->recolor = lv_color_make(result.red, result.green, result.blue);
if(part != LV_PART_MAIN) draw_dsc->blend_mode = lv_obj_get_style_blend_mode(obj, part);
LV_PROFILER_DRAW_END;
}
void lv_obj_init_draw_line_dsc(lv_obj_t * obj, lv_part_t part, lv_draw_line_dsc_t * draw_dsc)
{
LV_PROFILER_DRAW_BEGIN;
draw_dsc->base.obj = obj;
draw_dsc->base.part = part;
draw_dsc->opa = lv_obj_get_style_line_opa(obj, part);
if(draw_dsc->opa <= LV_OPA_MIN) {
LV_PROFILER_DRAW_END;
return;
}
lv_opa_t opa = get_layer_opa(obj, part, &draw_dsc->base);
if(opa < LV_OPA_MAX) {
draw_dsc->opa = LV_OPA_MIX2(draw_dsc->opa, opa);
}
if(draw_dsc->opa <= LV_OPA_MIN) {
LV_PROFILER_DRAW_END;
return;
}
draw_dsc->width = lv_obj_get_style_line_width(obj, part);
if(draw_dsc->width == 0) {
LV_PROFILER_DRAW_END;
return;
}
lv_color_t line_color = lv_obj_get_style_line_color_filtered(obj, part);
draw_dsc->color = normal_apply_layer_recolor(obj, part, &draw_dsc->base, line_color);
draw_dsc->dash_width = lv_obj_get_style_line_dash_width(obj, part);
if(draw_dsc->dash_width) {
draw_dsc->dash_gap = lv_obj_get_style_line_dash_gap(obj, part);
}
draw_dsc->round_start = lv_obj_get_style_line_rounded(obj, part);
draw_dsc->round_end = draw_dsc->round_start;
LV_PROFILER_DRAW_END;
}
void lv_obj_init_draw_arc_dsc(lv_obj_t * obj, lv_part_t part, lv_draw_arc_dsc_t * draw_dsc)
{
LV_PROFILER_DRAW_BEGIN;
draw_dsc->base.obj = obj;
draw_dsc->base.part = part;
draw_dsc->width = lv_obj_get_style_arc_width(obj, part);
if(draw_dsc->width == 0) {
LV_PROFILER_DRAW_END;
return;
}
draw_dsc->opa = lv_obj_get_style_arc_opa(obj, part);
if(draw_dsc->opa <= LV_OPA_MIN) {
LV_PROFILER_DRAW_END;
return;
}
lv_opa_t opa = get_layer_opa(obj, part, &draw_dsc->base);
if(opa < LV_OPA_MAX) {
draw_dsc->opa = LV_OPA_MIX2(draw_dsc->opa, opa);
}
if(draw_dsc->opa <= LV_OPA_MIN) {
LV_PROFILER_DRAW_END;
return;
}
lv_color_t arc_color = lv_obj_get_style_arc_color_filtered(obj, part);
draw_dsc->color = normal_apply_layer_recolor(obj, part, &draw_dsc->base, arc_color);
draw_dsc->img_src = lv_obj_get_style_arc_image_src(obj, part);
draw_dsc->rounded = lv_obj_get_style_arc_rounded(obj, part);
LV_PROFILER_DRAW_END;
}
int32_t lv_obj_calculate_ext_draw_size(lv_obj_t * obj, lv_part_t part)
{
LV_PROFILER_DRAW_BEGIN;
int32_t s = 0;
int32_t sh_width = lv_obj_get_style_shadow_width(obj, part);
if(sh_width) {
lv_opa_t sh_opa = lv_obj_get_style_shadow_opa(obj, part);
if(sh_opa > LV_OPA_MIN) {
sh_width = sh_width / 2 + 1; /*The blur adds only half width*/
sh_width += lv_obj_get_style_shadow_spread(obj, part);
int32_t sh_ofs_x = lv_obj_get_style_shadow_offset_x(obj, part);
int32_t sh_ofs_y = lv_obj_get_style_shadow_offset_y(obj, part);
sh_width += LV_MAX(LV_ABS(sh_ofs_x), LV_ABS(sh_ofs_y));
s = LV_MAX(s, sh_width);
}
}
int32_t outline_width = lv_obj_get_style_outline_width(obj, part);
if(outline_width) {
lv_opa_t outline_opa = lv_obj_get_style_outline_opa(obj, part);
if(outline_opa > LV_OPA_MIN) {
int32_t outline_pad = lv_obj_get_style_outline_pad(obj, part);
s = LV_MAX(s, outline_pad + outline_width);
}
}
int32_t w = lv_obj_get_style_transform_width(obj, part);
int32_t h = lv_obj_get_style_transform_height(obj, part);
int32_t wh = LV_MAX(w, h);
if(wh > 0) s += wh;
LV_PROFILER_DRAW_END;
return s;
}
void lv_obj_refresh_ext_draw_size(lv_obj_t * obj)
{
LV_PROFILER_DRAW_BEGIN;
LV_ASSERT_OBJ(obj, MY_CLASS);
int32_t s_old = lv_obj_get_ext_draw_size(obj);
int32_t s_new = 0;
lv_obj_send_event(obj, LV_EVENT_REFR_EXT_DRAW_SIZE, &s_new);
/*Store the result if the special attrs already allocated*/
if(obj->spec_attr) {
obj->spec_attr->ext_draw_size = s_new;
}
/*Allocate spec. attrs. only if the result is not zero.
*Zero is the default value if the spec. attr. are not defined.*/
else if(s_new != 0) {
lv_obj_allocate_spec_attr(obj);
obj->spec_attr->ext_draw_size = s_new;
}
if(s_new != s_old) lv_obj_invalidate(obj);
LV_PROFILER_DRAW_END;
}
int32_t lv_obj_get_ext_draw_size(const lv_obj_t * obj)
{
if(obj->spec_attr) return obj->spec_attr->ext_draw_size;
else return 0;
}
lv_layer_type_t lv_obj_get_layer_type(const lv_obj_t * obj)
{
if(obj->spec_attr) return (lv_layer_type_t)obj->spec_attr->layer_type;
else return LV_LAYER_TYPE_NONE;
}
/**********************
* STATIC FUNCTIONS
**********************/
static inline lv_opa_t get_layer_opa(const lv_obj_t * obj, lv_part_t part, const lv_draw_dsc_base_t * base_dsc)
{
if(base_dsc->layer) {
/* Accessing the layer opa directly is faster than using get style opa recursive */
if(part == LV_PART_MAIN) {
return base_dsc->layer->opa;
}
return LV_OPA_MIX2(base_dsc->layer->opa, lv_obj_get_style_opa(obj, part));
}
/* fallback to old recursive style opa */
return lv_obj_get_style_opa_recursive(obj, part);
}
static lv_color_t normal_apply_layer_recolor(const lv_obj_t * obj, lv_part_t part, const lv_draw_dsc_base_t * base_dsc,
lv_color_t color)
{
lv_color32_t recolor;
if(base_dsc->layer) {
recolor = base_dsc->layer->recolor;
if(part != LV_PART_MAIN) {
recolor = lv_obj_style_apply_recolor(obj, part, recolor);
}
}
else {
recolor = lv_obj_get_style_recolor_recursive(obj, part);
}
return lv_color_mix(lv_color_make(recolor.red, recolor.green, recolor.blue), color, recolor.alpha);
}
static lv_color32_t image_apply_layer_recolor(const lv_obj_t * obj, lv_part_t part,
const lv_draw_dsc_base_t * base_dsc, lv_color_t color, lv_opa_t opa)
{
lv_color32_t recolor;
if(base_dsc->layer) {
recolor = base_dsc->layer->recolor;
if(part != LV_PART_MAIN) {
recolor = lv_obj_style_apply_recolor(obj, part, recolor);
}
}
else {
recolor = lv_obj_get_style_recolor_recursive(obj, part);
}
if(opa > LV_OPA_TRANSP && recolor.alpha > LV_OPA_TRANSP) {
return lv_color_over32(recolor, lv_color_to_32(color, opa));
}
else if(recolor.alpha > LV_OPA_TRANSP) {
return recolor;
}
else {
return lv_color_to_32(color, opa);
}
}
@@ -0,0 +1,128 @@
/**
* @file lv_obj_draw.h
*
*/
#ifndef LV_OBJ_DRAW_H
#define LV_OBJ_DRAW_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../misc/lv_types.h"
#include "../draw/lv_draw_rect.h"
#include "../draw/lv_draw_label.h"
#include "../draw/lv_draw_image.h"
#include "../draw/lv_draw_line.h"
#include "../draw/lv_draw_arc.h"
#include "../draw/lv_draw_triangle.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/** Store the type of layer required to render a widget.*/
typedef enum {
/**No layer is needed. */
LV_LAYER_TYPE_NONE,
/**Simple layer means that the layer can be rendered in chunks.
* For example with opa_layered = 140 it's possible to render only 10 lines
* from the layer. When it's ready go the the next 10 lines.
* It avoids large memory allocations for the layer buffer.
* The buffer size for a chunk can be set by `LV_DRAW_LAYER_SIMPLE_BUF_SIZE` in lv_conf.h.*/
LV_LAYER_TYPE_SIMPLE,
/**The widget is transformed and cannot be rendered in chunks.
* It's because - due to the transformations - pixel outside of
* a given area will also contribute to the final image.
* In this case there is no limitation on the buffer size.
* LVGL will allocate as large buffer as needed to render the transformed area.*/
LV_LAYER_TYPE_TRANSFORM,
} lv_layer_type_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Initialize a rectangle draw descriptor from an object's styles in its current state
* @param obj pointer to an object
* @param part part of the object, e.g. `LV_PART_MAIN`, `LV_PART_SCROLLBAR`, `LV_PART_KNOB`, etc
* @param draw_dsc the descriptor to initialize.
* If an `..._opa` field is set to `LV_OPA_TRANSP` the related properties won't be initialized.
* Should be initialized with `lv_draw_rect_dsc_init(draw_dsc)`.
* @note Only the relevant fields will be set.
* E.g. if `border width == 0` the other border properties won't be evaluated.
*/
void lv_obj_init_draw_rect_dsc(lv_obj_t * obj, lv_part_t part, lv_draw_rect_dsc_t * draw_dsc);
/**
* Initialize a label draw descriptor from an object's styles in its current state
* @param obj pointer to an object
* @param part part of the object, e.g. `LV_PART_MAIN`, `LV_PART_SCROLLBAR`, `LV_PART_KNOB`, etc
* @param draw_dsc the descriptor to initialize.
* If the `opa` field is set to or the property is equal to `LV_OPA_TRANSP` the rest won't be initialized.
* Should be initialized with `lv_draw_label_dsc_init(draw_dsc)`.
*/
void lv_obj_init_draw_label_dsc(lv_obj_t * obj, lv_part_t part, lv_draw_label_dsc_t * draw_dsc);
/**
* Initialize an image draw descriptor from an object's styles in its current state
* @param obj pointer to an object
* @param part part of the object, e.g. `LV_PART_MAIN`, `LV_PART_SCROLLBAR`, `LV_PART_KNOB`, etc
* @param draw_dsc the descriptor to initialize.
* Should be initialized with `lv_draw_image_dsc_init(draw_dsc)`.
*/
void lv_obj_init_draw_image_dsc(lv_obj_t * obj, lv_part_t part, lv_draw_image_dsc_t * draw_dsc);
/**
* Initialize a line draw descriptor from an object's styles in its current state
* @param obj pointer to an object
* @param part part of the object, e.g. `LV_PART_MAIN`, `LV_PART_SCROLLBAR`, `LV_PART_KNOB`, etc
* @param draw_dsc the descriptor to initialize.
* Should be initialized with `lv_draw_line_dsc_init(draw_dsc)`.
*/
void lv_obj_init_draw_line_dsc(lv_obj_t * obj, lv_part_t part, lv_draw_line_dsc_t * draw_dsc);
/**
* Initialize an arc draw descriptor from an object's styles in its current state
* @param obj pointer to an object
* @param part part of the object, e.g. `LV_PART_MAIN`, `LV_PART_SCROLLBAR`, `LV_PART_KNOB`, etc
* @param draw_dsc the descriptor to initialize.
* Should be initialized with `lv_draw_arc_dsc_init(draw_dsc)`.
*/
void lv_obj_init_draw_arc_dsc(lv_obj_t * obj, lv_part_t part, lv_draw_arc_dsc_t * draw_dsc);
/**
* Get the required extra size (around the object's part) to draw shadow, outline, value etc.
* @param obj pointer to an object
* @param part part of the object
* @return the extra size required around the object
*/
int32_t lv_obj_calculate_ext_draw_size(lv_obj_t * obj, lv_part_t part);
/**
* Send a 'LV_EVENT_REFR_EXT_DRAW_SIZE' Call the ancestor's event handler to the object to refresh the value of the extended draw size.
* The result will be saved in `obj`.
* @param obj pointer to an object
*/
void lv_obj_refresh_ext_draw_size(lv_obj_t * obj);
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_OBJ_DRAW_H*/
@@ -0,0 +1,48 @@
/**
* @file lv_obj_draw_private.h
*
*/
#ifndef LV_OBJ_DRAW_PRIVATE_H
#define LV_OBJ_DRAW_PRIVATE_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "lv_obj_draw.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Get the extended draw area of an object.
* @param obj pointer to an object
* @return the size extended draw area around the real coordinates
*/
int32_t lv_obj_get_ext_draw_size(const lv_obj_t * obj);
lv_layer_type_t lv_obj_get_layer_type(const lv_obj_t * obj);
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_OBJ_DRAW_PRIVATE_H*/
@@ -0,0 +1,423 @@
/**
* @file lv_obj_event.c
*
*/
/*********************
* INCLUDES
*********************/
#include "../misc/lv_event_private.h"
#include "lv_obj_event_private.h"
#include "lv_obj_class_private.h"
#include "lv_obj_private.h"
#include "../indev/lv_indev.h"
#include "../indev/lv_indev_private.h"
/*********************
* DEFINES
*********************/
#define MY_CLASS (&lv_obj_class)
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static lv_result_t event_send_core(lv_event_t * e);
static bool event_is_bubbled(lv_event_t * e);
/**********************
* 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
**********************/
lv_result_t lv_obj_send_event(lv_obj_t * obj, lv_event_code_t event_code, void * param)
{
if(obj == NULL) return LV_RESULT_OK;
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_event_t e;
e.current_target = obj;
e.original_target = obj;
e.code = event_code;
e.user_data = NULL;
e.param = param;
e.deleted = 0;
e.stop_bubbling = 0;
e.stop_processing = 0;
lv_event_push(&e);
/*Send the event*/
lv_result_t res = event_send_core(&e);
/*Remove this element from the list*/
lv_event_pop(&e);
return res;
}
lv_result_t lv_obj_event_base(const lv_obj_class_t * class_p, lv_event_t * e)
{
const lv_obj_class_t * base;
if(class_p == NULL) base = ((lv_obj_t *)e->current_target)->class_p;
else base = class_p->base_class;
/*Find a base in which call the ancestor's event handler_cb if set*/
while(base && base->event_cb == NULL) base = base->base_class;
if(base == NULL) return LV_RESULT_OK;
if(base->event_cb == NULL) return LV_RESULT_OK;
/*Call the actual event callback*/
e->user_data = NULL;
LV_PROFILER_EVENT_BEGIN_TAG(lv_event_code_get_name(e->code));
base->event_cb(base, e);
LV_PROFILER_EVENT_END_TAG(lv_event_code_get_name(e->code));
lv_result_t res = LV_RESULT_OK;
/*Stop if the object is deleted*/
if(e->deleted) res = LV_RESULT_INVALID;
return res;
}
lv_event_dsc_t * lv_obj_add_event_cb(lv_obj_t * obj, lv_event_cb_t event_cb, lv_event_code_t filter, void * user_data)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_obj_allocate_spec_attr(obj);
return lv_event_add(&obj->spec_attr->event_list, event_cb, filter, user_data);
}
uint32_t lv_obj_get_event_count(lv_obj_t * obj)
{
LV_ASSERT_NULL(obj);
if(obj->spec_attr == NULL) return 0;
return lv_event_get_count(&obj->spec_attr->event_list);
}
lv_event_dsc_t * lv_obj_get_event_dsc(lv_obj_t * obj, uint32_t index)
{
LV_ASSERT_NULL(obj);
if(obj->spec_attr == NULL) return NULL;
return lv_event_get_dsc(&obj->spec_attr->event_list, index);
}
bool lv_obj_remove_event(lv_obj_t * obj, uint32_t index)
{
LV_ASSERT_NULL(obj);
if(obj->spec_attr == NULL) return false;
return lv_event_remove(&obj->spec_attr->event_list, index);
}
bool lv_obj_remove_event_dsc(lv_obj_t * obj, lv_event_dsc_t * dsc)
{
LV_ASSERT_NULL(obj);
LV_ASSERT_NULL(dsc);
if(obj->spec_attr == NULL) return false;
return lv_event_remove_dsc(&obj->spec_attr->event_list, dsc);
}
uint32_t lv_obj_remove_event_cb(lv_obj_t * obj, lv_event_cb_t event_cb)
{
LV_ASSERT_NULL(obj);
uint32_t event_cnt = lv_obj_get_event_count(obj);
uint32_t removed_count = 0;
uint32_t i;
for(i = 0; i < event_cnt; i++) {
lv_event_dsc_t * dsc = lv_obj_get_event_dsc(obj, i);
if(dsc && dsc->cb == event_cb) {
lv_obj_remove_event(obj, i);
removed_count++;
}
}
return removed_count;
}
uint32_t lv_obj_remove_event_cb_with_user_data(lv_obj_t * obj, lv_event_cb_t event_cb, void * user_data)
{
LV_ASSERT_NULL(obj);
uint32_t event_cnt = lv_obj_get_event_count(obj);
uint32_t removed_count = 0;
int32_t i;
if(event_cnt == 0) return 0;
for(i = event_cnt - 1; i >= 0; i--) {
lv_event_dsc_t * dsc = lv_obj_get_event_dsc(obj, i);
if(dsc && (event_cb == NULL || dsc->cb == event_cb) && dsc->user_data == user_data) {
lv_obj_remove_event(obj, i);
removed_count ++;
}
}
return removed_count;
}
lv_obj_t * lv_event_get_current_target_obj(lv_event_t * e)
{
return lv_event_get_current_target(e);
}
lv_obj_t * lv_event_get_target_obj(lv_event_t * e)
{
return lv_event_get_target(e);
}
lv_indev_t * lv_event_get_indev(lv_event_t * e)
{
if(e->code == LV_EVENT_PRESSED ||
e->code == LV_EVENT_PRESSING ||
e->code == LV_EVENT_PRESS_LOST ||
e->code == LV_EVENT_SHORT_CLICKED ||
e->code == LV_EVENT_LONG_PRESSED ||
e->code == LV_EVENT_LONG_PRESSED_REPEAT ||
e->code == LV_EVENT_CLICKED ||
e->code == LV_EVENT_RELEASED ||
e->code == LV_EVENT_SCROLL_BEGIN ||
e->code == LV_EVENT_SCROLL_END ||
e->code == LV_EVENT_SCROLL ||
e->code == LV_EVENT_GESTURE ||
e->code == LV_EVENT_KEY ||
e->code == LV_EVENT_FOCUSED ||
e->code == LV_EVENT_DEFOCUSED ||
e->code == LV_EVENT_LEAVE ||
e->code == LV_EVENT_HOVER_OVER ||
e->code == LV_EVENT_HOVER_LEAVE) {
return lv_event_get_param(e);
}
else {
LV_LOG_WARN("Not interpreted with this event code");
return NULL;
}
}
lv_layer_t * lv_event_get_layer(lv_event_t * e)
{
if(e->code == LV_EVENT_DRAW_MAIN ||
e->code == LV_EVENT_DRAW_MAIN_BEGIN ||
e->code == LV_EVENT_DRAW_MAIN_END ||
e->code == LV_EVENT_DRAW_POST ||
e->code == LV_EVENT_DRAW_POST_BEGIN ||
e->code == LV_EVENT_DRAW_POST_END) {
return lv_event_get_param(e);
}
else {
LV_LOG_WARN("Not interpreted with this event code");
return NULL;
}
}
const lv_area_t * lv_event_get_old_size(lv_event_t * e)
{
if(e->code == LV_EVENT_SIZE_CHANGED) {
return lv_event_get_param(e);
}
else {
LV_LOG_WARN("Not interpreted with this event code");
return NULL;
}
}
uint32_t lv_event_get_key(lv_event_t * e)
{
if(e->code == LV_EVENT_KEY) {
uint32_t * k = lv_event_get_param(e);
if(k) return *k;
else return 0;
}
else {
LV_LOG_WARN("Not interpreted with this event code");
return 0;
}
}
int32_t lv_event_get_rotary_diff(lv_event_t * e)
{
if(e->code == LV_EVENT_ROTARY) {
int32_t * r = lv_event_get_param(e);
if(r) return *r;
else return 0;
}
else {
LV_LOG_WARN("Not interpreted with this event code");
return 0;
}
}
lv_anim_t * lv_event_get_scroll_anim(lv_event_t * e)
{
if(e->code == LV_EVENT_SCROLL_BEGIN) {
return lv_event_get_param(e);
}
else {
LV_LOG_WARN("Not interpreted with this event code");
return NULL;
}
}
void lv_event_set_ext_draw_size(lv_event_t * e, int32_t size)
{
if(e->code == LV_EVENT_REFR_EXT_DRAW_SIZE) {
int32_t * cur_size = lv_event_get_param(e);
*cur_size = LV_MAX(*cur_size, size);
}
else {
LV_LOG_WARN("Not interpreted with this event code");
}
}
lv_point_t * lv_event_get_self_size_info(lv_event_t * e)
{
if(e->code == LV_EVENT_GET_SELF_SIZE) {
return lv_event_get_param(e);
}
else {
LV_LOG_WARN("Not interpreted with this event code");
return 0;
}
}
lv_hit_test_info_t * lv_event_get_hit_test_info(lv_event_t * e)
{
if(e->code == LV_EVENT_HIT_TEST) {
return lv_event_get_param(e);
}
else {
LV_LOG_WARN("Not interpreted with this event code");
return 0;
}
}
const lv_area_t * lv_event_get_cover_area(lv_event_t * e)
{
if(e->code == LV_EVENT_COVER_CHECK) {
lv_cover_check_info_t * p = lv_event_get_param(e);
return p->area;
}
else {
LV_LOG_WARN("Not interpreted with this event code");
return NULL;
}
}
void lv_event_set_cover_res(lv_event_t * e, lv_cover_res_t res)
{
if(e->code == LV_EVENT_COVER_CHECK) {
lv_cover_check_info_t * p = lv_event_get_param(e);
if(res > p->res) p->res = res; /*Save only "stronger" results*/
}
else {
LV_LOG_WARN("Not interpreted with this event code");
}
}
lv_draw_task_t * lv_event_get_draw_task(lv_event_t * e)
{
if(e->code == LV_EVENT_DRAW_TASK_ADDED) {
return lv_event_get_param(e);
}
else {
LV_LOG_WARN("Not interpreted with this event code");
return NULL;
}
}
/**********************
* STATIC FUNCTIONS
**********************/
static lv_result_t event_send_core(lv_event_t * e)
{
LV_TRACE_EVENT("Sending event %d to %p with %p param", e->code, (void *)e->original_target, e->param);
/*Call the input device's feedback callback if set*/
lv_indev_t * indev_act = lv_indev_active();
if(indev_act) {
if(e->stop_processing) return LV_RESULT_OK;
if(e->deleted) return LV_RESULT_INVALID;
}
lv_obj_t * target = e->current_target;
lv_result_t res = LV_RESULT_OK;
lv_event_list_t * list = target->spec_attr ? &target->spec_attr->event_list : NULL;
res = lv_event_send(list, e, true);
if(res != LV_RESULT_OK || e->stop_processing) return res;
res = lv_obj_event_base(NULL, e);
if(res != LV_RESULT_OK || e->stop_processing) return res;
res = lv_event_send(list, e, false);
if(res != LV_RESULT_OK || e->stop_processing) return res;
lv_obj_t * parent = lv_obj_get_parent(e->current_target);
if(parent && event_is_bubbled(e)) {
e->current_target = parent;
res = event_send_core(e);
}
return res;
}
static bool event_is_bubbled(lv_event_t * e)
{
if(e->stop_bubbling) return false;
/*Event codes that always bubble*/
switch(e->code) {
case LV_EVENT_CHILD_CREATED:
case LV_EVENT_CHILD_DELETED:
return true;
default:
break;
}
/*Check other codes only if bubbling is enabled*/
if(lv_obj_has_flag(e->current_target, LV_OBJ_FLAG_EVENT_BUBBLE) == false) return false;
switch(e->code) {
case LV_EVENT_HIT_TEST:
case LV_EVENT_COVER_CHECK:
case LV_EVENT_REFR_EXT_DRAW_SIZE:
case LV_EVENT_DRAW_MAIN_BEGIN:
case LV_EVENT_DRAW_MAIN:
case LV_EVENT_DRAW_MAIN_END:
case LV_EVENT_DRAW_POST_BEGIN:
case LV_EVENT_DRAW_POST:
case LV_EVENT_DRAW_POST_END:
case LV_EVENT_DRAW_TASK_ADDED:
case LV_EVENT_REFRESH:
case LV_EVENT_DELETE:
case LV_EVENT_CHILD_CREATED:
case LV_EVENT_CHILD_DELETED:
case LV_EVENT_CHILD_CHANGED:
case LV_EVENT_SIZE_CHANGED:
case LV_EVENT_STYLE_CHANGED:
case LV_EVENT_GET_SELF_SIZE:
return false;
default:
return true;
}
}
@@ -0,0 +1,204 @@
/**
* @file lv_obj_event.h
*
*/
#ifndef LV_OBJ_EVENT_H
#define LV_OBJ_EVENT_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../misc/lv_types.h"
#include "../misc/lv_event.h"
#include "../indev/lv_indev.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/** Cover check results.*/
typedef enum {
LV_COVER_RES_COVER = 0,
LV_COVER_RES_NOT_COVER = 1,
LV_COVER_RES_MASKED = 2,
} lv_cover_res_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Send an event to the object
* @param obj pointer to an object
* @param event_code the type of the event from `lv_event_t`
* @param param arbitrary data depending on the widget type and the event. (Usually `NULL`)
* @return LV_RESULT_OK: `obj` was not deleted in the event; LV_RESULT_INVALID: `obj` was deleted in the event_code
*/
lv_result_t lv_obj_send_event(lv_obj_t * obj, lv_event_code_t event_code, void * param);
/**
* Used by the widgets internally to call the ancestor widget types's event handler
* @param class_p pointer to the class of the widget (NOT the ancestor class)
* @param e pointer to the event descriptor
* @return LV_RESULT_OK: the target object was not deleted in the event; LV_RESULT_INVALID: it was deleted in the event_code
*/
lv_result_t lv_obj_event_base(const lv_obj_class_t * class_p, lv_event_t * e);
/**
* Get the current target of the event. It's the object which event handler being called.
* If the event is not bubbled it's the same as "original" target.
* @param e pointer to the event descriptor
* @return the target of the event_code
*/
lv_obj_t * lv_event_get_current_target_obj(lv_event_t * e);
/**
* Get the object originally targeted by the event. It's the same even if the event is bubbled.
* @param e pointer to the event descriptor
* @return pointer to the original target of the event_code
*/
lv_obj_t * lv_event_get_target_obj(lv_event_t * e);
/**
* Add an event handler function for an object.
* Used by the user to react on event which happens with the object.
* An object can have multiple event handler. They will be called in the same order as they were added.
* @param obj pointer to an object
* @param filter an event code (e.g. `LV_EVENT_CLICKED`) on which the event should be called. `LV_EVENT_ALL` can be used to receive all the events.
* @param event_cb the new event function
* @param user_data custom data will be available in `event_cb`
* @return handler to the event. It can be used in `lv_obj_remove_event_dsc`.
*/
lv_event_dsc_t * lv_obj_add_event_cb(lv_obj_t * obj, lv_event_cb_t event_cb, lv_event_code_t filter, void * user_data);
uint32_t lv_obj_get_event_count(lv_obj_t * obj);
lv_event_dsc_t * lv_obj_get_event_dsc(lv_obj_t * obj, uint32_t index);
bool lv_obj_remove_event(lv_obj_t * obj, uint32_t index);
bool lv_obj_remove_event_dsc(lv_obj_t * obj, lv_event_dsc_t * dsc);
/**
* Remove an event_cb from an object
* @param obj pointer to a obj
* @param event_cb the event_cb of the event to remove
* @return the count of the event removed
*/
uint32_t lv_obj_remove_event_cb(lv_obj_t * obj, lv_event_cb_t event_cb);
/**
* Remove an event_cb with user_data
* @param obj pointer to a obj
* @param event_cb the event_cb of the event to remove
* @param user_data user_data
* @return the count of the event removed
*/
uint32_t lv_obj_remove_event_cb_with_user_data(lv_obj_t * obj, lv_event_cb_t event_cb, void * user_data);
/**
* Get the input device passed as parameter to indev related events.
* @param e pointer to an event
* @return the indev that triggered the event or NULL if called on a not indev related event
*/
lv_indev_t * lv_event_get_indev(lv_event_t * e);
/**
* Get the draw context which should be the first parameter of the draw functions.
* Namely: `LV_EVENT_DRAW_MAIN/POST`, `LV_EVENT_DRAW_MAIN/POST_BEGIN`, `LV_EVENT_DRAW_MAIN/POST_END`
* @param e pointer to an event
* @return pointer to a draw context or NULL if called on an unrelated event
*/
lv_layer_t * lv_event_get_layer(lv_event_t * e);
/**
* Get the old area of the object before its size was changed. Can be used in `LV_EVENT_SIZE_CHANGED`
* @param e pointer to an event
* @return the old absolute area of the object or NULL if called on an unrelated event
*/
const lv_area_t * lv_event_get_old_size(lv_event_t * e);
/**
* Get the key passed as parameter to an event. Can be used in `LV_EVENT_KEY`
* @param e pointer to an event
* @return the triggering key or NULL if called on an unrelated event
*/
uint32_t lv_event_get_key(lv_event_t * e);
/**
* Get the signed rotary encoder diff. passed as parameter to an event. Can be used in `LV_EVENT_ROTARY`
* @param e pointer to an event
* @return the triggering key or NULL if called on an unrelated event
*/
int32_t lv_event_get_rotary_diff(lv_event_t * e);
/**
* Get the animation descriptor of a scrolling. Can be used in `LV_EVENT_SCROLL_BEGIN`
* @param e pointer to an event
* @return the animation that will scroll the object. (can be modified as required)
*/
lv_anim_t * lv_event_get_scroll_anim(lv_event_t * e);
/**
* Set the new extra draw size. Can be used in `LV_EVENT_REFR_EXT_DRAW_SIZE`
* @param e pointer to an event
* @param size The new extra draw size
*/
void lv_event_set_ext_draw_size(lv_event_t * e, int32_t size);
/**
* Get a pointer to an `lv_point_t` variable in which the self size should be saved (width in `point->x` and height `point->y`).
* Can be used in `LV_EVENT_GET_SELF_SIZE`
* @param e pointer to an event
* @return pointer to `lv_point_t` or NULL if called on an unrelated event
*/
lv_point_t * lv_event_get_self_size_info(lv_event_t * e);
/**
* Get a pointer to an `lv_hit_test_info_t` variable in which the hit test result should be saved. Can be used in `LV_EVENT_HIT_TEST`
* @param e pointer to an event
* @return pointer to `lv_hit_test_info_t` or NULL if called on an unrelated event
*/
lv_hit_test_info_t * lv_event_get_hit_test_info(lv_event_t * e);
/**
* Get a pointer to an area which should be examined whether the object fully covers it or not.
* Can be used in `LV_EVENT_HIT_TEST`
* @param e pointer to an event
* @return an area with absolute coordinates to check
*/
const lv_area_t * lv_event_get_cover_area(lv_event_t * e);
/**
* Set the result of cover checking. Can be used in `LV_EVENT_COVER_CHECK`
* @param e pointer to an event
* @param res an element of ::lv_cover_check_info_t
*/
void lv_event_set_cover_res(lv_event_t * e, lv_cover_res_t res);
/**
* Get the draw task which was just added.
* Can be used in `LV_EVENT_DRAW_TASK_ADDED event`
* @param e pointer to an event
* @return the added draw task
*/
lv_draw_task_t * lv_event_get_draw_task(lv_event_t * e);
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_OBJ_EVENT_H*/
@@ -0,0 +1,62 @@
/**
* @file lv_obj_event_private.h
*
*/
#ifndef LV_OBJ_EVENT_PRIVATE_H
#define LV_OBJ_EVENT_PRIVATE_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "lv_obj_event.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**
* Used as the event parameter of ::LV_EVENT_HIT_TEST to check if an `point` can click the object or not.
* `res` should be set like this:
* - If already set to `false` another event wants that point non clickable. If you want to respect it leave it as `false` or set `true` to overwrite it.
* - If already set `true` and `point` shouldn't be clickable set to `false`
* - If already set to `true` you agree that `point` can click the object leave it as `true`
*/
struct _lv_hit_test_info_t {
const lv_point_t * point; /**< A point relative to screen to check if it can click the object or not*/
bool res; /**< true: `point` can click the object; false: it cannot*/
};
/**
* Used as the event parameter of ::LV_EVENT_COVER_CHECK to check if an area is covered by the object or not.
* In the event use `const lv_area_t * area = lv_event_get_cover_area(e)` to get the area to check
* and `lv_event_set_cover_res(e, res)` to set the result.
*/
struct _lv_cover_check_info_t {
lv_cover_res_t res;
const lv_area_t * area;
};
/**********************
* GLOBAL PROTOTYPES
**********************/
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_OBJ_EVENT_PRIVATE_H*/
@@ -0,0 +1,122 @@
/**
* @file lv_obj_id.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_obj_class_private.h"
#include "lv_obj_private.h"
#include "lv_global.h"
#include "../osal/lv_os.h"
#include "../stdlib/lv_sprintf.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
typedef struct _class_info_t {
const lv_obj_class_t * class_p;
uint32_t obj_count;
} class_info_t;
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
#if LV_USE_OBJ_ID && LV_USE_OBJ_ID_BUILTIN
void lv_obj_assign_id(const lv_obj_class_t * class_p, lv_obj_t * obj)
{
LV_ASSERT(obj && class_p);
uint32_t i;
uint32_t id = 0;
lv_global_t * global = LV_GLOBAL_DEFAULT();
class_info_t * info = NULL;
if(obj == NULL || class_p == NULL) return;
if(global == NULL) return;
obj->id = NULL;
for(i = 0; i < global->objid_count; i ++) {
info = ((class_info_t *)global->objid_array) + i;
if(class_p == info->class_p) break;
}
/*Resize array*/
if(i == global->objid_count) {
void * array = lv_realloc(global->objid_array, sizeof(class_info_t) * (global->objid_count + 1));
LV_ASSERT_MALLOC(array);
if(array == NULL) return;
global->objid_array = array;
global->objid_count ++;
info = ((class_info_t *)global->objid_array) + i;
info->obj_count = 0;
info->class_p = class_p;
}
id = ++info->obj_count;
obj->id = (void *)(lv_uintptr_t)id;
}
void lv_obj_set_id(lv_obj_t * obj, void * id)
{
LV_ASSERT_NULL(obj);
if(obj->id) lv_obj_free_id(obj);
obj->id = id;
}
void lv_obj_free_id(lv_obj_t * obj)
{
LV_UNUSED(obj);
obj->id = NULL;
}
const char * lv_obj_stringify_id(lv_obj_t * obj, char * buf, uint32_t len)
{
const char * name;
if(obj == NULL || obj->class_p == NULL) return NULL;
if(buf == NULL) return NULL;
name = obj->class_p->name;
if(name == NULL) name = "nameless";
lv_snprintf(buf, len, "%s%" LV_PRIu32 "", name, (uint32_t)(lv_uintptr_t)obj->id);
return buf;
}
void lv_objid_builtin_destroy(void)
{
lv_global_t * global = LV_GLOBAL_DEFAULT();
if(global == NULL) return;
lv_free(global->objid_array);
global->objid_count = 0;
}
int lv_obj_id_compare(const void * id1, const void * id2)
{
return id1 == id2 ? 0 : 1;
}
#endif /*LV_USE_OBJ_ID_BUILTIN*/
File diff suppressed because it is too large Load Diff
+473
View File
@@ -0,0 +1,473 @@
/**
* @file lv_obj_pos.h
*
*/
#ifndef LV_OBJ_POS_H
#define LV_OBJ_POS_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../misc/lv_area.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
typedef enum {
/** No flags */
LV_OBJ_POINT_TRANSFORM_FLAG_NONE = 0x00,
/** Consider the transformation properties of the parents too */
LV_OBJ_POINT_TRANSFORM_FLAG_RECURSIVE = 0x01,
/** Execute the inverse of the transformation (-angle and 1/zoom) */
LV_OBJ_POINT_TRANSFORM_FLAG_INVERSE = 0x02,
/** Both inverse and recursive*/
LV_OBJ_POINT_TRANSFORM_FLAG_INVERSE_RECURSIVE = 0x03,
} lv_obj_point_transform_flag_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Set the position of an object relative to the set alignment.
* @param obj pointer to an object
* @param x new x coordinate
* @param y new y coordinate
* @note With default alignment it's the distance from the top left corner
* @note E.g. LV_ALIGN_CENTER alignment it's the offset from the center of the parent
* @note The position is interpreted on the content area of the parent
* @note The values can be set in pixel or in percentage of parent size with `lv_pct(v)`
*/
void lv_obj_set_pos(lv_obj_t * obj, int32_t x, int32_t y);
/**
* Set the x coordinate of an object
* @param obj pointer to an object
* @param x new x coordinate
* @note With default alignment it's the distance from the top left corner
* @note E.g. LV_ALIGN_CENTER alignment it's the offset from the center of the parent
* @note The position is interpreted on the content area of the parent
* @note The values can be set in pixel or in percentage of parent size with `lv_pct(v)`
*/
void lv_obj_set_x(lv_obj_t * obj, int32_t x);
/**
* Set the y coordinate of an object
* @param obj pointer to an object
* @param y new y coordinate
* @note With default alignment it's the distance from the top left corner
* @note E.g. LV_ALIGN_CENTER alignment it's the offset from the center of the parent
* @note The position is interpreted on the content area of the parent
* @note The values can be set in pixel or in percentage of parent size with `lv_pct(v)`
*/
void lv_obj_set_y(lv_obj_t * obj, int32_t y);
/**
* Set the size of an object.
* @param obj pointer to an object
* @param w the new width
* @param h the new height
* @note possible values are:
* pixel simple set the size accordingly
* LV_SIZE_CONTENT set the size to involve all children in the given direction
* lv_pct(x) to set size in percentage of the parent's content area size (the size without paddings).
* x should be in [0..1000]% range
*/
void lv_obj_set_size(lv_obj_t * obj, int32_t w, int32_t h);
/**
* Recalculate the size of the object
* @param obj pointer to an object
* @return true: the size has been changed
*/
bool lv_obj_refr_size(lv_obj_t * obj);
/**
* Set the width of an object
* @param obj pointer to an object
* @param w the new width
* @note possible values are:
* pixel simple set the size accordingly
* LV_SIZE_CONTENT set the size to involve all children in the given direction
* lv_pct(x) to set size in percentage of the parent's content area size (the size without paddings).
* x should be in [0..1000]% range
*/
void lv_obj_set_width(lv_obj_t * obj, int32_t w);
/**
* Set the height of an object
* @param obj pointer to an object
* @param h the new height
* @note possible values are:
* pixel simple set the size accordingly
* LV_SIZE_CONTENT set the size to involve all children in the given direction
* lv_pct(x) to set size in percentage of the parent's content area size (the size without paddings).
* x should be in [0..1000]% range
*/
void lv_obj_set_height(lv_obj_t * obj, int32_t h);
/**
* Set the width reduced by the left and right padding and the border width.
* @param obj pointer to an object
* @param w the width without paddings in pixels
*/
void lv_obj_set_content_width(lv_obj_t * obj, int32_t w);
/**
* Set the height reduced by the top and bottom padding and the border width.
* @param obj pointer to an object
* @param h the height without paddings in pixels
*/
void lv_obj_set_content_height(lv_obj_t * obj, int32_t h);
/**
* Set a layout for an object
* @param obj pointer to an object
* @param layout pointer to a layout descriptor to set
*/
void lv_obj_set_layout(lv_obj_t * obj, uint32_t layout);
/**
* Test whether the and object is positioned by a layout or not
* @param obj pointer to an object to test
* @return true: positioned by a layout; false: not positioned by a layout
*/
bool lv_obj_is_layout_positioned(const lv_obj_t * obj);
/**
* Mark the object for layout update.
* @param obj pointer to an object whose children need to be updated
*/
void lv_obj_mark_layout_as_dirty(lv_obj_t * obj);
/**
* Update the layout of an object.
* @param obj pointer to an object whose position and size needs to be updated
*/
void lv_obj_update_layout(const lv_obj_t * obj);
/**
* Change the alignment of an object.
* @param obj pointer to an object to align
* @param align type of alignment (see 'lv_align_t' enum) `LV_ALIGN_OUT_...` can't be used.
*/
void lv_obj_set_align(lv_obj_t * obj, lv_align_t align);
/**
* Change the alignment of an object and set new coordinates.
* Equivalent to:
* lv_obj_set_align(obj, align);
* lv_obj_set_pos(obj, x_ofs, y_ofs);
* @param obj pointer to an object to align
* @param align type of alignment (see 'lv_align_t' enum) `LV_ALIGN_OUT_...` can't be used.
* @param x_ofs x coordinate offset after alignment
* @param y_ofs y coordinate offset after alignment
*/
void lv_obj_align(lv_obj_t * obj, lv_align_t align, int32_t x_ofs, int32_t y_ofs);
/**
* Align an object to another object.
* @param obj pointer to an object to align
* @param base pointer to another object (if NULL `obj`s parent is used). 'obj' will be aligned to it.
* @param align type of alignment (see 'lv_align_t' enum)
* @param x_ofs x coordinate offset after alignment
* @param y_ofs y coordinate offset after alignment
* @note if the position or size of `base` changes `obj` needs to be aligned manually again
*/
void lv_obj_align_to(lv_obj_t * obj, const lv_obj_t * base, lv_align_t align, int32_t x_ofs,
int32_t y_ofs);
/**
* Align an object to the center on its parent.
* @param obj pointer to an object to align
* @note if the parent size changes `obj` needs to be aligned manually again
*/
void lv_obj_center(lv_obj_t * obj);
/**
* Set the transform matrix of an object
* @param obj pointer to an object
* @param matrix pointer to a matrix to set
* @note `LV_DRAW_TRANSFORM_USE_MATRIX` needs to be enabled.
*/
void lv_obj_set_transform(lv_obj_t * obj, const lv_matrix_t * matrix);
/**
* Reset the transform matrix of an object to identity matrix
* @param obj pointer to an object
* @note `LV_DRAW_TRANSFORM_USE_MATRIX` needs to be enabled.
*/
void lv_obj_reset_transform(lv_obj_t * obj);
/**
* Copy the coordinates of an object to an area
* @param obj pointer to an object
* @param coords pointer to an area to store the coordinates
*/
void lv_obj_get_coords(const lv_obj_t * obj, lv_area_t * coords);
/**
* Get the x coordinate of object.
* @param obj pointer to an object
* @return distance of `obj` from the left side of its parent plus the parent's left padding
* @note The position of the object is recalculated only on the next redraw. To force coordinate recalculation
* call `lv_obj_update_layout(obj)`.
* @note Zero return value means the object is on the left padding of the parent, and not on the left edge.
* @note Scrolling of the parent doesn't change the returned value.
* @note The returned value is always the distance from the parent even if `obj` is positioned by a layout.
*/
int32_t lv_obj_get_x(const lv_obj_t * obj);
/**
* Get the x2 coordinate of object.
* @param obj pointer to an object
* @return distance of `obj` from the right side of its parent plus the parent's right padding
* @note The position of the object is recalculated only on the next redraw. To force coordinate recalculation
* call `lv_obj_update_layout(obj)`.
* @note Zero return value means the object is on the right padding of the parent, and not on the right edge.
* @note Scrolling of the parent doesn't change the returned value.
* @note The returned value is always the distance from the parent even if `obj` is positioned by a layout.
*/
int32_t lv_obj_get_x2(const lv_obj_t * obj);
/**
* Get the y coordinate of object.
* @param obj pointer to an object
* @return distance of `obj` from the top side of its parent plus the parent's top padding
* @note The position of the object is recalculated only on the next redraw. To force coordinate recalculation
* call `lv_obj_update_layout(obj)`.
* @note Zero return value means the object is on the top padding of the parent, and not on the top edge.
* @note Scrolling of the parent doesn't change the returned value.
* @note The returned value is always the distance from the parent even if `obj` is positioned by a layout.
*/
int32_t lv_obj_get_y(const lv_obj_t * obj);
/**
* Get the y2 coordinate of object.
* @param obj pointer to an object
* @return distance of `obj` from the bottom side of its parent plus the parent's bottom padding
* @note The position of the object is recalculated only on the next redraw. To force coordinate recalculation
* call `lv_obj_update_layout(obj)`.
* @note Zero return value means the object is on the bottom padding of the parent, and not on the bottom edge.
* @note Scrolling of the parent doesn't change the returned value.
* @note The returned value is always the distance from the parent even if `obj` is positioned by a layout.
*/
int32_t lv_obj_get_y2(const lv_obj_t * obj);
/**
* Get the actually set x coordinate of object, i.e. the offset from the set alignment
* @param obj pointer to an object
* @return the set x coordinate
*/
int32_t lv_obj_get_x_aligned(const lv_obj_t * obj);
/**
* Get the actually set y coordinate of object, i.e. the offset from the set alignment
* @param obj pointer to an object
* @return the set y coordinate
*/
int32_t lv_obj_get_y_aligned(const lv_obj_t * obj);
/**
* Get the width of an object
* @param obj pointer to an object
* @note The position of the object is recalculated only on the next redraw. To force coordinate recalculation
* call `lv_obj_update_layout(obj)`.
* @return the width in pixels
*/
int32_t lv_obj_get_width(const lv_obj_t * obj);
/**
* Get the height of an object
* @param obj pointer to an object
* @note The position of the object is recalculated only on the next redraw. To force coordinate recalculation
* call `lv_obj_update_layout(obj)`.
* @return the height in pixels
*/
int32_t lv_obj_get_height(const lv_obj_t * obj);
/**
* Get the width reduced by the left and right padding and the border width.
* @param obj pointer to an object
* @note The position of the object is recalculated only on the next redraw. To force coordinate recalculation
* call `lv_obj_update_layout(obj)`.
* @return the width which still fits into its parent without causing overflow (making the parent scrollable)
*/
int32_t lv_obj_get_content_width(const lv_obj_t * obj);
/**
* Get the height reduced by the top and bottom padding and the border width.
* @param obj pointer to an object
* @note The position of the object is recalculated only on the next redraw. To force coordinate recalculation
* call `lv_obj_update_layout(obj)`.
* @return the height which still fits into the parent without causing overflow (making the parent scrollable)
*/
int32_t lv_obj_get_content_height(const lv_obj_t * obj);
/**
* Get the area reduced by the paddings and the border width.
* @param obj pointer to an object
* @note The position of the object is recalculated only on the next redraw. To force coordinate recalculation
* call `lv_obj_update_layout(obj)`.
* @param area the area which still fits into the parent without causing overflow (making the parent scrollable)
*/
void lv_obj_get_content_coords(const lv_obj_t * obj, lv_area_t * area);
/**
* Get the width occupied by the "parts" of the widget. E.g. the width of all columns of a table.
* @param obj pointer to an objects
* @return the width of the virtually drawn content
* @note This size independent from the real size of the widget.
* It just tells how large the internal ("virtual") content is.
*/
int32_t lv_obj_get_self_width(const lv_obj_t * obj);
/**
* Get the height occupied by the "parts" of the widget. E.g. the height of all rows of a table.
* @param obj pointer to an objects
* @return the width of the virtually drawn content
* @note This size independent from the real size of the widget.
* It just tells how large the internal ("virtual") content is.
*/
int32_t lv_obj_get_self_height(const lv_obj_t * obj);
/**
* Handle if the size of the internal ("virtual") content of an object has changed.
* @param obj pointer to an object
* @return false: nothing happened; true: refresh happened
*/
bool lv_obj_refresh_self_size(lv_obj_t * obj);
void lv_obj_refr_pos(lv_obj_t * obj);
void lv_obj_move_to(lv_obj_t * obj, int32_t x, int32_t y);
void lv_obj_move_children_by(lv_obj_t * obj, int32_t x_diff, int32_t y_diff, bool ignore_floating);
/**
* Get the transform matrix of an object
* @param obj pointer to an object
* @return pointer to the transform matrix or NULL if not set
*/
const lv_matrix_t * lv_obj_get_transform(const lv_obj_t * obj);
/**
* Transform a point using the angle and zoom style properties of an object
* @param obj pointer to an object whose style properties should be used
* @param p a point to transform, the result will be written back here too
* @param flags OR-ed valued of :cpp:enum:`lv_obj_point_transform_flag_t`
*/
void lv_obj_transform_point(const lv_obj_t * obj, lv_point_t * p, lv_obj_point_transform_flag_t flags);
/**
* Transform an array of points using the angle and zoom style properties of an object
* @param obj pointer to an object whose style properties should be used
* @param points the array of points to transform, the result will be written back here too
* @param count number of points in the array
* @param flags OR-ed valued of :cpp:enum:`lv_obj_point_transform_flag_t`
*/
void lv_obj_transform_point_array(const lv_obj_t * obj, lv_point_t points[], size_t count,
lv_obj_point_transform_flag_t flags);
/**
* Transform an area using the angle and zoom style properties of an object
* @param obj pointer to an object whose style properties should be used
* @param area an area to transform, the result will be written back here too
* @param flags OR-ed valued of :cpp:enum:`lv_obj_point_transform_flag_t`
*/
void lv_obj_get_transformed_area(const lv_obj_t * obj, lv_area_t * area, lv_obj_point_transform_flag_t flags);
/**
* Mark an area of an object as invalid.
* The area will be truncated to the object's area and marked for redraw.
* @param obj pointer to an object
* @param area the area to redraw
*/
void lv_obj_invalidate_area(const lv_obj_t * obj, const lv_area_t * area);
/**
* Mark the object as invalid to redrawn its area
* @param obj pointer to an object
*/
void lv_obj_invalidate(const lv_obj_t * obj);
/**
* Tell whether an area of an object is visible (even partially) now or not
* @param obj pointer to an object
* @param area the are to check. The visible part of the area will be written back here.
* @return true visible; false not visible (hidden, out of parent, on other screen, etc)
*/
bool lv_obj_area_is_visible(const lv_obj_t * obj, lv_area_t * area);
/**
* Tell whether an object is visible (even partially) now or not
* @param obj pointer to an object
* @return true: visible; false not visible (hidden, out of parent, on other screen, etc)
*/
bool lv_obj_is_visible(const lv_obj_t * obj);
/**
* Set the size of an extended clickable area
* @param obj pointer to an object
* @param size extended clickable area in all 4 directions [px]
*/
void lv_obj_set_ext_click_area(lv_obj_t * obj, int32_t size);
/**
* Get the an area where to object can be clicked.
* It's the object's normal area plus the extended click area.
* @param obj pointer to an object
* @param area store the result area here
*/
void lv_obj_get_click_area(const lv_obj_t * obj, lv_area_t * area);
/**
* Hit-test an object given a particular point in screen space.
* @param obj object to hit-test
* @param point screen-space point (absolute coordinate)
* @return true: if the object is considered under the point
*/
bool lv_obj_hit_test(lv_obj_t * obj, const lv_point_t * point);
/**
* Clamp a width between min and max width. If the min/max width is in percentage value use the ref_width
* @param width width to clamp
* @param min_width the minimal width
* @param max_width the maximal width
* @param ref_width the reference width used when min/max width is in percentage
* @return the clamped width
*/
int32_t lv_clamp_width(int32_t width, int32_t min_width, int32_t max_width, int32_t ref_width);
/**
* Clamp a height between min and max height. If the min/max height is in percentage value use the ref_height
* @param height height to clamp
* @param min_height the minimal height
* @param max_height the maximal height
* @param ref_height the reference height used when min/max height is in percentage
* @return the clamped height
*/
int32_t lv_clamp_height(int32_t height, int32_t min_height, int32_t max_height, int32_t ref_height);
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_OBJ_POS_H*/
@@ -0,0 +1,94 @@
/**
* @file lv_obj_private.h
*
*/
#ifndef LV_OBJ_PRIVATE_H
#define LV_OBJ_PRIVATE_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "lv_obj.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**
* Special, rarely used attributes.
* They are allocated automatically if any elements is set.
*/
struct _lv_obj_spec_attr_t {
lv_obj_t ** children; /**< Store the pointer of the children in an array.*/
lv_group_t * group_p;
#if LV_DRAW_TRANSFORM_USE_MATRIX
lv_matrix_t * matrix; /**< The transform matrix*/
#endif
lv_event_list_t event_list;
#if LV_USE_OBJ_NAME
const char * name; /**< Pointer to the name */
#endif
lv_point_t scroll; /**< The current X/Y scroll offset*/
int32_t ext_click_pad; /**< Extra click padding in all direction*/
int32_t ext_draw_size; /**< EXTend the size in every direction for drawing.*/
uint16_t child_cnt; /**< Number of children*/
uint16_t scrollbar_mode : 2; /**< How to display scrollbars, see `lv_scrollbar_mode_t`*/
uint16_t scroll_snap_x : 2; /**< Where to align the snappable children horizontally, see `lv_scroll_snap_t`*/
uint16_t scroll_snap_y : 2; /**< Where to align the snappable children vertically*/
uint16_t scroll_dir : 4; /**< The allowed scroll direction(s), see `lv_dir_t`*/
uint16_t layer_type : 2; /**< Cache the layer type here. Element of lv_intermediate_layer_type_t */
uint16_t name_static : 1; /**< 1: `name` was not dynamically allocated */
};
struct _lv_obj_t {
const lv_obj_class_t * class_p;
lv_obj_t * parent;
lv_obj_spec_attr_t * spec_attr;
lv_obj_style_t * styles;
#if LV_OBJ_STYLE_CACHE
uint32_t style_main_prop_is_set;
uint32_t style_other_prop_is_set;
#endif
void * user_data;
#if LV_USE_OBJ_ID
void * id;
#endif
lv_area_t coords;
lv_obj_flag_t flags;
lv_state_t state;
uint16_t layout_inv : 1;
uint16_t readjust_scroll_after_layout : 1;
uint16_t scr_layout_inv : 1;
uint16_t skip_trans : 1;
uint16_t style_cnt : 6;
uint16_t h_layout : 1;
uint16_t w_layout : 1;
uint16_t is_deleting : 1;
};
/**********************
* GLOBAL PROTOTYPES
**********************/
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_OBJ_PRIVATE_H*/
@@ -0,0 +1,339 @@
/**
* @file lv_obj_id.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_obj_private.h"
#include "../core/lv_obj.h"
#include "../stdlib/lv_string.h"
#include "../misc/lv_utils.h"
#include "lv_obj_property.h"
#include "lv_obj_class_private.h"
#if LV_USE_OBJ_PROPERTY
/*********************
* DEFINES
*********************/
#define HANDLE_PROPERTY_TYPE(type, field) \
if(!set) { \
value->field = ((lv_property_get_##type##_t)(prop->getter))(obj); \
} else { \
switch(LV_PROPERTY_ID_TYPE2(prop->id)) { \
case LV_PROPERTY_ID_INVALID: \
((lv_property_set_##type##_t)(prop->setter))(obj, value->field); \
break; \
case LV_PROPERTY_TYPE_INT: \
((lv_property_set_##type##_integer_t)(prop->setter))(obj, value->arg1.field, value->arg2.num); \
break; \
case LV_PROPERTY_TYPE_BOOL: \
((lv_property_set_##type##_boolean_t)(prop->setter))(obj, value->arg1.field, value->arg2.enable); \
break; \
case LV_PROPERTY_TYPE_PRECISE: \
((lv_property_set_##type##_precise_t)(prop->setter))(obj, value->arg1.field, value->arg2.precise); \
break; \
case LV_PROPERTY_TYPE_COLOR: \
((lv_property_set_##type##_color_t)(prop->setter))(obj, value->arg1.field, value->arg2.color); \
break; \
case LV_PROPERTY_TYPE_POINTER: \
case LV_PROPERTY_TYPE_IMGSRC: \
case LV_PROPERTY_TYPE_TEXT: \
case LV_PROPERTY_TYPE_OBJ: \
case LV_PROPERTY_TYPE_DISPLAY: \
case LV_PROPERTY_TYPE_FONT: \
((lv_property_set_##type##_pointer_t)(prop->setter))(obj, value->arg1.field, value->arg2.ptr); \
break; \
} \
}
/**********************
* TYPEDEFS
**********************/
typedef int32_t integer;
typedef bool boolean;
typedef lv_value_precise_t precise;
typedef lv_color_t color;
typedef const void * pointer;
#define DEFINE_PROPERTY_SETTER_TYPES(type) \
typedef void (*lv_property_set_##type##_t)(lv_obj_t *, type); \
typedef void (*lv_property_set_##type##_integer_t)(lv_obj_t *, type, int32_t); \
typedef void (*lv_property_set_##type##_boolean_t)(lv_obj_t *, type, bool); \
typedef void (*lv_property_set_##type##_precise_t)(lv_obj_t *, type, lv_value_precise_t); \
typedef void (*lv_property_set_##type##_color_t)(lv_obj_t *, type, lv_color_t); \
typedef void (*lv_property_set_##type##_pointer_t)(lv_obj_t *, type, const void *)
DEFINE_PROPERTY_SETTER_TYPES(integer);
DEFINE_PROPERTY_SETTER_TYPES(boolean);
DEFINE_PROPERTY_SETTER_TYPES(precise);
DEFINE_PROPERTY_SETTER_TYPES(color);
DEFINE_PROPERTY_SETTER_TYPES(pointer);
typedef void (*lv_property_set_point_t)(lv_obj_t *, lv_point_t *);
typedef lv_result_t (*lv_property_setter_t)(lv_obj_t *, lv_prop_id_t, const lv_property_t *);
typedef integer(*lv_property_get_integer_t)(const lv_obj_t *);
typedef bool (*lv_property_get_boolean_t)(const lv_obj_t *);
typedef lv_value_precise_t (*lv_property_get_precise_t)(const lv_obj_t *);
typedef lv_color_t (*lv_property_get_color_t)(const lv_obj_t *);
typedef void * (*lv_property_get_pointer_t)(const lv_obj_t *);
typedef lv_point_t (*lv_property_get_point_t)(lv_obj_t *);
typedef lv_result_t (*lv_property_getter_t)(const lv_obj_t *, lv_prop_id_t, lv_property_t *);
/**********************
* STATIC PROTOTYPES
**********************/
static lv_result_t obj_property(lv_obj_t * obj, lv_prop_id_t id, lv_property_t * value, bool set);
static int property_name_compare(const void * ref, const void * element);
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
lv_result_t lv_obj_set_property(lv_obj_t * obj, const lv_property_t * value)
{
LV_ASSERT(obj && value);
uint32_t index = LV_PROPERTY_ID_INDEX(value->id);
if(value->id == LV_PROPERTY_ID_INVALID || index > LV_PROPERTY_ID_ANY) {
LV_LOG_WARN("Invalid property id set to %p", obj);
return LV_RESULT_INVALID;
}
if(index < LV_PROPERTY_ID_START) {
lv_obj_set_local_style_prop(obj, index, value->style, value->selector);
return LV_RESULT_OK;
}
return obj_property(obj, value->id, (lv_property_t *)value, true);
}
lv_result_t lv_obj_set_properties(lv_obj_t * obj, const lv_property_t * value, uint32_t count)
{
for(uint32_t i = 0; i < count; i++) {
lv_result_t result = lv_obj_set_property(obj, &value[i]);
if(result != LV_RESULT_OK) {
return result;
}
}
return LV_RESULT_OK;
}
lv_property_t lv_obj_get_property(lv_obj_t * obj, lv_prop_id_t id)
{
lv_result_t result;
lv_property_t value = { 0 };
uint32_t index = LV_PROPERTY_ID_INDEX(id);
if(id == LV_PROPERTY_ID_INVALID || index > LV_PROPERTY_ID_ANY) {
LV_LOG_WARN("Invalid property id to get from %p", obj);
value.id = LV_PROPERTY_ID_INVALID;
value.num = 0;
return value;
}
if(index < LV_PROPERTY_ID_START) {
lv_obj_get_local_style_prop(obj, index, &value.style, 0);
value.id = id;
value.selector = 0;
return value;
}
result = obj_property(obj, id, &value, false);
if(result != LV_RESULT_OK)
value.id = LV_PROPERTY_ID_INVALID;
return value;
}
lv_property_t lv_obj_get_style_property(lv_obj_t * obj, lv_prop_id_t id, uint32_t selector)
{
lv_property_t value;
uint32_t index = LV_PROPERTY_ID_INDEX(id);
if(index == LV_PROPERTY_ID_INVALID || index >= LV_PROPERTY_ID_START) {
LV_LOG_WARN("invalid style property id 0x%" LV_PRIx32, id);
value.id = LV_PROPERTY_ID_INVALID;
value.num = 0;
return value;
}
lv_obj_get_local_style_prop(obj, id, &value.style, selector);
value.id = id;
value.selector = selector;
return value;
}
lv_prop_id_t lv_style_property_get_id(const char * name)
{
#if LV_USE_OBJ_PROPERTY_NAME
lv_property_name_t * found;
/*Check style property*/
found = lv_utils_bsearch(name, lv_style_property_names, sizeof(lv_style_property_names) / sizeof(lv_property_name_t),
sizeof(lv_property_name_t), property_name_compare);
if(found) return found->id;
#else
LV_UNUSED(name);
#endif
return LV_PROPERTY_ID_INVALID;
}
lv_prop_id_t lv_obj_class_property_get_id(const lv_obj_class_t * clz, const char * name)
{
#if LV_USE_OBJ_PROPERTY_NAME
const lv_property_name_t * names;
lv_property_name_t * found;
names = clz->property_names;
if(names == NULL) {
/* try base class*/
return LV_PROPERTY_ID_INVALID;
}
found = lv_utils_bsearch(name, names, clz->names_count, sizeof(lv_property_name_t), property_name_compare);
if(found) return found->id;
#else
LV_UNUSED(obj);
LV_UNUSED(name);
LV_UNUSED(property_name_compare);
#endif
return LV_PROPERTY_ID_INVALID;
}
lv_prop_id_t lv_obj_property_get_id(const lv_obj_t * obj, const char * name)
{
#if LV_USE_OBJ_PROPERTY_NAME
const lv_obj_class_t * clz;
lv_prop_id_t id;
for(clz = obj->class_p; clz; clz = clz->base_class) {
id = lv_obj_class_property_get_id(clz, name);
if(id != LV_PROPERTY_ID_INVALID) return id;
}
/*Check style property*/
id = lv_style_property_get_id(name);
if(id != LV_PROPERTY_ID_INVALID) return id;
#else
LV_UNUSED(obj);
LV_UNUSED(name);
LV_UNUSED(property_name_compare);
#endif
return LV_PROPERTY_ID_INVALID;
}
/**********************
* STATIC FUNCTIONS
**********************/
static lv_result_t obj_property(lv_obj_t * obj, lv_prop_id_t id, lv_property_t * value, bool set)
{
const lv_property_ops_t * properties;
const lv_property_ops_t * prop;
const lv_obj_class_t * clz;
uint32_t index = LV_PROPERTY_ID_INDEX(id);
for(clz = obj->class_p ; clz; clz = clz->base_class) {
properties = clz->properties;
if(properties == NULL) {
/* try base class*/
continue;
}
if(id != LV_PROPERTY_ID_ANY && (index < clz->prop_index_start || index > clz->prop_index_end)) {
/* try base class*/
continue;
}
/*Check if there's setter available for this class*/
for(uint32_t i = 0; i < clz->properties_count; i++) {
prop = &properties[i];
/*pass id and value directly to widget's property method*/
if(prop->id == LV_PROPERTY_ID_ANY) {
value->id = prop->id;
if(set) return ((lv_property_setter_t)prop->setter)(obj, id, value);
else return ((lv_property_getter_t)prop->getter)(obj, id, value);
}
/*Not this id, check next*/
if(prop->id != id)
continue;
/*id matched but we got null pointer to functions*/
if(set ? prop->setter == NULL : prop->getter == NULL) {
LV_LOG_WARN("NULL %s provided, id: 0x%" LV_PRIx32, set ? "setter" : "getter", id);
return LV_RESULT_INVALID;
}
/*Update value id if it's a read*/
if(!set) value->id = prop->id;
switch(LV_PROPERTY_ID_TYPE(prop->id)) {
case LV_PROPERTY_TYPE_INT:
HANDLE_PROPERTY_TYPE(integer, num);
break;
case LV_PROPERTY_TYPE_BOOL:
HANDLE_PROPERTY_TYPE(boolean, enable);
break;
case LV_PROPERTY_TYPE_PRECISE:
HANDLE_PROPERTY_TYPE(precise, precise);
break;
case LV_PROPERTY_TYPE_COLOR:
HANDLE_PROPERTY_TYPE(color, color);
break;
case LV_PROPERTY_TYPE_POINTER:
case LV_PROPERTY_TYPE_IMGSRC:
case LV_PROPERTY_TYPE_TEXT:
case LV_PROPERTY_TYPE_OBJ:
case LV_PROPERTY_TYPE_DISPLAY:
case LV_PROPERTY_TYPE_FONT:
HANDLE_PROPERTY_TYPE(pointer, ptr);
break;
case LV_PROPERTY_TYPE_POINT: {
lv_point_t * point = &value->point;
if(set)((lv_property_set_point_t)(prop->setter))(obj, point);
else *point = ((lv_property_get_point_t)(prop->getter))(obj);
break;
}
default: {
LV_LOG_WARN("Unknown property id: 0x%08" LV_PRIx32, prop->id);
return LV_RESULT_INVALID;
}
}
return LV_RESULT_OK;
}
/*If no setter found, try base class then*/
}
LV_LOG_WARN("Unknown property id: 0x%08" LV_PRIx32, id);
return LV_RESULT_INVALID;
}
static int property_name_compare(const void * ref, const void * element)
{
const lv_property_name_t * prop = element;
return lv_strcmp(ref, prop->name);
}
#endif /*LV_USE_OBJ_PROPERTY*/
@@ -0,0 +1,234 @@
/**
* @file lv_obj_property.h
*
*/
#ifndef LV_OBJ_PROPERTY_H
#define LV_OBJ_PROPERTY_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../misc/lv_types.h"
#include "../misc/lv_style.h"
#if LV_USE_OBJ_PROPERTY
/*********************
* DEFINES
*********************/
/*All possible property value types*/
#define LV_PROPERTY_TYPE_INVALID 0 /*Use default 0 as invalid to detect program outliers*/
#define LV_PROPERTY_TYPE_INT 1 /*int32_t type*/
#define LV_PROPERTY_TYPE_PRECISE 2 /*lv_value_precise_t, int32_t or float depending on LV_USE_FLOAT*/
#define LV_PROPERTY_TYPE_COLOR 3 /*ARGB8888 type*/
#define LV_PROPERTY_TYPE_POINT 4 /*lv_point_t */
#define LV_PROPERTY_TYPE_POINTER 5 /*void * pointer*/
#define LV_PROPERTY_TYPE_IMGSRC 6 /*Special pointer for image*/
#define LV_PROPERTY_TYPE_TEXT 7 /*Special pointer of char* */
#define LV_PROPERTY_TYPE_OBJ 8 /*Special pointer of lv_obj_t* */
#define LV_PROPERTY_TYPE_DISPLAY 9 /*Special pointer of lv_display_t* */
#define LV_PROPERTY_TYPE_FONT 10 /*Special pointer of lv_font_t* */
#define LV_PROPERTY_TYPE_BOOL 11 /*int32_t type*/
#define LV_PROPERTY_TYPE_SHIFT 28
#define LV_PROPERTY_TYPE2_SHIFT 24
#define LV_PROPERTY_ID(clz, name, type, index) LV_PROPERTY_## clz ##_##name = (LV_PROPERTY_## clz ##_START + (index)) | ((type) << LV_PROPERTY_TYPE_SHIFT)
#define LV_PROPERTY_ID2(clz, name, type, type2, index) LV_PROPERTY_ID(clz, name, type, index) | ((type2) << LV_PROPERTY_TYPE2_SHIFT)
#define LV_PROPERTY_ID_TYPE(id) ((id) >> LV_PROPERTY_TYPE_SHIFT)
#define LV_PROPERTY_ID_TYPE2(id) ((id) >> LV_PROPERTY_TYPE_SHIFT)
#define LV_PROPERTY_ID_INDEX(id) ((id) & 0xfffffff)
/*Set properties from an array of lv_property_t*/
#define LV_OBJ_SET_PROPERTY_ARRAY(obj, array) lv_obj_set_properties(obj, array, sizeof(array)/sizeof(array[0]))
/**********************
* TYPEDEFS
**********************/
/**
* Group of predefined widget ID start value.
*/
enum {
LV_PROPERTY_ID_INVALID = 0,
/*ID 0x01 to 0xff are style ID, check lv_style_prop_t*/
LV_PROPERTY_STYLE_START = 0x00,
LV_PROPERTY_ID_START = 0x0100, /*ID smaller than 0xff is style ID*/
/*Define the property ID for every widget here. */
LV_PROPERTY_OBJ_START = 0x0100, /* lv_obj.c */
LV_PROPERTY_IMAGE_START = 0x0200, /* lv_image.c */
LV_PROPERTY_LABEL_START = 0x0300, /* lv_label.c */
LV_PROPERTY_KEYBOARD_START = 0x0400, /* lv_keyboard.c */
LV_PROPERTY_TEXTAREA_START = 0x0500, /* lv_textarea.c */
LV_PROPERTY_ROLLER_START = 0x0600, /* lv_roller.c */
LV_PROPERTY_DROPDOWN_START = 0x0700, /* lv_dropdown.c */
LV_PROPERTY_SLIDER_START = 0x0800, /* lv_slider.c */
LV_PROPERTY_ANIMIMAGE_START = 0x0900, /* lv_animimage.c */
/*Special ID, use it to extend ID and make sure it's unique and compile time determinant*/
LV_PROPERTY_ID_BUILTIN_LAST = 0xffff, /*ID of 0x10000 ~ 0xfffffff is reserved for user*/
LV_PROPERTY_ID_ANY = 0x7ffffffe, /*Special ID used by lvgl to intercept all setter/getter call.*/
};
struct _lv_property_name_t {
const char * name;
lv_prop_id_t id;
};
typedef struct {
lv_prop_id_t id;
union {
int32_t num; /**< Signed integer number (enums or "normal" numbers)*/
uint32_t num_u; /**< Unsigned integer number (opacity, Booleans) */
bool enable; /**< Booleans */
const void * ptr; /**< Constant pointers (font, cone text, etc.) */
lv_color_t color; /**< Colors */
lv_value_precise_t precise; /**< float or int for precise value */
lv_point_t point; /**< Point, contains two int32_t */
struct {
/**
* Note that place struct member `style` at first place is intended.
* `style` shares same memory with `num`, `ptr`, `color`.
* So we set the style value directly without using `prop.style.num`.
*
* E.g.
*
* static const lv_property_t obj_pos_x = {
* .id = LV_PROPERTY_STYLE_X,
* .num = 123,
* .selector = LV_STATE_PRESSED,
* }
*
* instead of:
* static const lv_property_t obj_pos_x = {
* .id = LV_PROPERTY_STYLE_X,
* .style.num = 123, // note this line.
* .selector = LV_STATE_PRESSED,
* }
*/
lv_style_value_t style; /**< Make sure it's the first element in struct. */
uint32_t selector; /**< Style selector, lv_part_t | lv_state_t */
};
/**
* For some properties like slider range, it contains two simple (4-byte) values
* so we can use `arg1.num` and `arg2.num` to set the argument.
*/
struct {
union {
int32_t num;
uint32_t num_u;
bool enable;
const void * ptr;
lv_color_t color;
lv_value_precise_t precise;
} arg1, arg2;
};
};
} lv_property_t;
typedef struct {
lv_prop_id_t id;
void * setter; /**< Callback used to set property. */
void * getter; /**< Callback used to get property. */
} lv_property_ops_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
/*=====================
* Setter functions
*====================*/
/**
* Set Widget property.
* @param obj pointer to Widget
* @param value property value to set
* @return return LV_RESULT_OK if call succeeded
*/
lv_result_t lv_obj_set_property(lv_obj_t * obj, const lv_property_t * value);
/**
* Set multiple Widget properties. Helper `LV_OBJ_SET_PROPERTY_ARRAY` can be used for constant property array.
* @param obj pointer to Widget
* @param value property value array to set
* @param count number of array elements
* @return return LV_RESULT_OK if call succeeded
*/
lv_result_t lv_obj_set_properties(lv_obj_t * obj, const lv_property_t * value, uint32_t count);
/*=====================
* Getter functions
*====================*/
/**
* Read property value from Widget.
* If id is a style property. Style selector is 0 by default.
* @param obj pointer to Widget
* @param id ID of property to read
* @return return property value read. The returned property ID is set to `LV_PROPERTY_ID_INVALID` if read failed.
*/
lv_property_t lv_obj_get_property(lv_obj_t * obj, lv_prop_id_t id);
/**
* Read style property value from Widget
* @param obj pointer to Widget
* @param id ID of style property
* @param selector selector for style property
* @return return property value read. The returned property ID is set to `LV_PROPERTY_ID_INVALID` if read failed.
*/
lv_property_t lv_obj_get_style_property(lv_obj_t * obj, lv_prop_id_t id, uint32_t selector);
/**
* Get property ID by recursively searching for name in Widget's class hierarchy, and
* if still not found, then search style properties.
* Requires to enabling `LV_USE_OBJ_PROPERTY_NAME`.
* @param obj pointer to Widget whose class and base-class hierarchy are to be searched.
* @param name property name
* @return property ID found or `LV_PROPERTY_ID_INVALID` if not found.
*/
lv_prop_id_t lv_obj_property_get_id(const lv_obj_t * obj, const char * name);
/**
* Get property ID by doing a non-recursive search for name directly in Widget class properties.
* Requires enabling `LV_USE_OBJ_PROPERTY_NAME`.
* @param clz pointer to Widget class that has specified property.
* @param name property name
* @return property ID found or `LV_PROPERTY_ID_INVALID` if not found.
*/
lv_prop_id_t lv_obj_class_property_get_id(const lv_obj_class_t * clz, const char * name);
/**
* Get style property ID by name. Requires enabling `LV_USE_OBJ_PROPERTY_NAME`.
* @param name property name
* @return property ID found or `LV_PROPERTY_ID_INVALID` if not found.
*/
lv_prop_id_t lv_style_property_get_id(const char * name);
/**********************
* MACROS
**********************/
#include "../widgets/property/lv_obj_property_names.h"
#include "../widgets/property/lv_style_properties.h"
#endif /*LV_USE_OBJ_PROPERTY*/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_OBJ_PROPERTY_H*/
@@ -0,0 +1,813 @@
/**
* @file lv_obj_scroll.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_obj_scroll_private.h"
#include "../misc/lv_anim_private.h"
#include "lv_obj_private.h"
#include "../indev/lv_indev.h"
#include "../indev/lv_indev_scroll.h"
#include "../display/lv_display.h"
#include "../misc/lv_area.h"
/*********************
* DEFINES
*********************/
#define MY_CLASS (&lv_obj_class)
#define SCROLL_ANIM_TIME_MIN 200 /*ms*/
#define SCROLL_ANIM_TIME_MAX 400 /*ms*/
#define SCROLLBAR_MIN_SIZE (LV_DPX(10))
/**********************
* TYPEDEFS
**********************/
/**********************
* GLOBAL PROTOTYPES
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static void scroll_x_anim(void * obj, int32_t v);
static void scroll_y_anim(void * obj, int32_t v);
static void scroll_end_cb(lv_anim_t * a);
static void scroll_area_into_view(const lv_area_t * area, lv_obj_t * child, lv_point_t * scroll_value,
lv_anim_enable_t anim_en);
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
/*=====================
* Setter functions
*====================*/
void lv_obj_set_scrollbar_mode(lv_obj_t * obj, lv_scrollbar_mode_t mode)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_obj_allocate_spec_attr(obj);
if(obj->spec_attr->scrollbar_mode == mode) return;
obj->spec_attr->scrollbar_mode = mode;
lv_obj_invalidate(obj);
}
void lv_obj_set_scroll_dir(lv_obj_t * obj, lv_dir_t dir)
{
lv_obj_allocate_spec_attr(obj);
if(dir != obj->spec_attr->scroll_dir) {
obj->spec_attr->scroll_dir = dir;
}
}
void lv_obj_set_scroll_snap_x(lv_obj_t * obj, lv_scroll_snap_t align)
{
lv_obj_allocate_spec_attr(obj);
obj->spec_attr->scroll_snap_x = align;
}
void lv_obj_set_scroll_snap_y(lv_obj_t * obj, lv_scroll_snap_t align)
{
lv_obj_allocate_spec_attr(obj);
obj->spec_attr->scroll_snap_y = align;
}
/*=====================
* Getter functions
*====================*/
lv_scrollbar_mode_t lv_obj_get_scrollbar_mode(const lv_obj_t * obj)
{
if(obj->spec_attr) return (lv_scrollbar_mode_t) obj->spec_attr->scrollbar_mode;
else return LV_SCROLLBAR_MODE_AUTO;
}
lv_dir_t lv_obj_get_scroll_dir(const lv_obj_t * obj)
{
if(obj->spec_attr) return (lv_dir_t) obj->spec_attr->scroll_dir;
else return LV_DIR_ALL;
}
lv_scroll_snap_t lv_obj_get_scroll_snap_x(const lv_obj_t * obj)
{
if(obj->spec_attr) return (lv_scroll_snap_t) obj->spec_attr->scroll_snap_x;
else return LV_SCROLL_SNAP_NONE;
}
lv_scroll_snap_t lv_obj_get_scroll_snap_y(const lv_obj_t * obj)
{
if(obj->spec_attr) return (lv_scroll_snap_t) obj->spec_attr->scroll_snap_y;
else return LV_SCROLL_SNAP_NONE;
}
int32_t lv_obj_get_scroll_x(const lv_obj_t * obj)
{
if(obj->spec_attr == NULL) return 0;
return -obj->spec_attr->scroll.x;
}
int32_t lv_obj_get_scroll_y(const lv_obj_t * obj)
{
if(obj->spec_attr == NULL) return 0;
return -obj->spec_attr->scroll.y;
}
int32_t lv_obj_get_scroll_top(const lv_obj_t * obj)
{
if(obj->spec_attr == NULL) return 0;
return -obj->spec_attr->scroll.y;
}
int32_t lv_obj_get_scroll_bottom(const lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
int32_t child_res = LV_COORD_MIN;
uint32_t i;
uint32_t child_cnt = lv_obj_get_child_count(obj);
for(i = 0; i < child_cnt; i++) {
const lv_obj_t * child = obj->spec_attr->children[i];
if(lv_obj_has_flag_any(child, LV_OBJ_FLAG_HIDDEN | LV_OBJ_FLAG_FLOATING)) continue;
int32_t tmp_y = child->coords.y2 + lv_obj_get_style_margin_bottom(child, LV_PART_MAIN);
child_res = LV_MAX(child_res, tmp_y);
}
int32_t space_top = lv_obj_get_style_space_top(obj, LV_PART_MAIN);
int32_t space_bottom = lv_obj_get_style_space_bottom(obj, LV_PART_MAIN);
if(child_res != LV_COORD_MIN) {
child_res -= (obj->coords.y2 - space_bottom);
}
int32_t self_h = lv_obj_get_self_height(obj);
self_h = self_h - (lv_obj_get_height(obj) - space_top - space_bottom);
self_h -= lv_obj_get_scroll_y(obj);
return LV_MAX(child_res, self_h);
}
int32_t lv_obj_get_scroll_left(const lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
/*Normally can't scroll the object out on the left.
*So simply use the current scroll position as "left size"*/
if(lv_obj_get_style_base_dir(obj, LV_PART_MAIN) != LV_BASE_DIR_RTL) {
if(obj->spec_attr == NULL) return 0;
return -obj->spec_attr->scroll.x;
}
/*With RTL base direction scrolling the left is normal so find the left most coordinate*/
int32_t space_right = lv_obj_get_style_space_right(obj, LV_PART_MAIN);
int32_t space_left = lv_obj_get_style_space_left(obj, LV_PART_MAIN);
int32_t child_res = 0;
uint32_t i;
int32_t x1 = LV_COORD_MAX;
uint32_t child_cnt = lv_obj_get_child_count(obj);
for(i = 0; i < child_cnt; i++) {
const lv_obj_t * child = obj->spec_attr->children[i];
if(lv_obj_has_flag_any(child, LV_OBJ_FLAG_HIDDEN | LV_OBJ_FLAG_FLOATING)) continue;
int32_t tmp_x = child->coords.x1 - lv_obj_get_style_margin_left(child, LV_PART_MAIN);
x1 = LV_MIN(x1, tmp_x);
}
if(x1 != LV_COORD_MAX) {
child_res = x1;
child_res = (obj->coords.x1 + space_left) - child_res;
}
else {
child_res = LV_COORD_MIN;
}
int32_t self_w = lv_obj_get_self_width(obj);
self_w = self_w - (lv_obj_get_width(obj) - space_right - space_left);
self_w += lv_obj_get_scroll_x(obj);
return LV_MAX(child_res, self_w);
}
int32_t lv_obj_get_scroll_right(const lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
/*With RTL base dir can't scroll to the object out on the right.
*So simply use the current scroll position as "right size"*/
if(lv_obj_get_style_base_dir(obj, LV_PART_MAIN) == LV_BASE_DIR_RTL) {
if(obj->spec_attr == NULL) return 0;
return obj->spec_attr->scroll.x;
}
/*With other base direction (LTR) scrolling to the right is normal so find the right most coordinate*/
int32_t child_res = LV_COORD_MIN;
uint32_t i;
uint32_t child_cnt = lv_obj_get_child_count(obj);
for(i = 0; i < child_cnt; i++) {
const lv_obj_t * child = obj->spec_attr->children[i];
if(lv_obj_has_flag_any(child, LV_OBJ_FLAG_HIDDEN | LV_OBJ_FLAG_FLOATING)) continue;
int32_t tmp_x = child->coords.x2 + lv_obj_get_style_margin_right(child, LV_PART_MAIN);
child_res = LV_MAX(child_res, tmp_x);
}
int32_t space_right = lv_obj_get_style_space_right(obj, LV_PART_MAIN);
int32_t space_left = lv_obj_get_style_space_left(obj, LV_PART_MAIN);
if(child_res != LV_COORD_MIN) {
child_res -= (obj->coords.x2 - space_right);
}
int32_t self_w;
self_w = lv_obj_get_self_width(obj);
self_w = self_w - (lv_obj_get_width(obj) - space_right - space_left);
self_w -= lv_obj_get_scroll_x(obj);
return LV_MAX(child_res, self_w);
}
void lv_obj_get_scroll_end(lv_obj_t * obj, lv_point_t * end)
{
lv_anim_t * a;
a = lv_anim_get(obj, scroll_x_anim);
end->x = a ? -a->end_value : lv_obj_get_scroll_x(obj);
a = lv_anim_get(obj, scroll_y_anim);
end->y = a ? -a->end_value : lv_obj_get_scroll_y(obj);
}
/*=====================
* Other functions
*====================*/
void lv_obj_scroll_by_bounded(lv_obj_t * obj, int32_t dx, int32_t dy, lv_anim_enable_t anim_en)
{
if(dx == 0 && dy == 0) return;
/*We need to know the final sizes for bound check*/
lv_obj_update_layout(obj);
/*Don't let scroll more than naturally possible by the size of the content*/
int32_t x_current = -lv_obj_get_scroll_x(obj);
int32_t x_bounded = x_current + dx;
if(lv_obj_get_style_base_dir(obj, LV_PART_MAIN) != LV_BASE_DIR_RTL) {
if(x_bounded > 0) x_bounded = 0;
if(x_bounded < 0) {
int32_t scroll_max = lv_obj_get_scroll_left(obj) + lv_obj_get_scroll_right(obj);
if(scroll_max < 0) scroll_max = 0;
if(x_bounded < -scroll_max) x_bounded = -scroll_max;
}
}
else {
if(x_bounded < 0) x_bounded = 0;
if(x_bounded > 0) {
int32_t scroll_max = lv_obj_get_scroll_left(obj) + lv_obj_get_scroll_right(obj);
if(scroll_max < 0) scroll_max = 0;
if(x_bounded > scroll_max) x_bounded = scroll_max;
}
}
/*Don't let scroll more than naturally possible by the size of the content*/
int32_t y_current = -lv_obj_get_scroll_y(obj);
int32_t y_bounded = y_current + dy;
if(y_bounded > 0) y_bounded = 0;
if(y_bounded < 0) {
int32_t scroll_max = lv_obj_get_scroll_top(obj) + lv_obj_get_scroll_bottom(obj);
if(scroll_max < 0) scroll_max = 0;
if(y_bounded < -scroll_max) y_bounded = -scroll_max;
}
dx = x_bounded - x_current;
dy = y_bounded - y_current;
if(dx || dy) {
lv_obj_scroll_by(obj, dx, dy, anim_en);
}
}
void lv_obj_scroll_by(lv_obj_t * obj, int32_t dx, int32_t dy, lv_anim_enable_t anim_en)
{
if(dx == 0 && dy == 0) return;
if(anim_en) {
lv_display_t * d = lv_obj_get_display(obj);
lv_anim_t a;
lv_anim_init(&a);
lv_anim_set_var(&a, obj);
lv_anim_set_deleted_cb(&a, scroll_end_cb);
if(dx) {
uint32_t t = lv_anim_speed_clamped((lv_display_get_horizontal_resolution(d)) >> 1, SCROLL_ANIM_TIME_MIN,
SCROLL_ANIM_TIME_MAX);
lv_anim_set_duration(&a, t);
int32_t sx = lv_obj_get_scroll_x(obj);
lv_anim_set_values(&a, -sx, -sx + dx);
lv_anim_set_exec_cb(&a, scroll_x_anim);
lv_anim_set_path_cb(&a, lv_anim_path_ease_out);
lv_result_t res;
res = lv_obj_send_event(obj, LV_EVENT_SCROLL_BEGIN, &a);
if(res != LV_RESULT_OK) return;
lv_anim_start(&a);
}
if(dy) {
uint32_t t = lv_anim_speed_clamped((lv_display_get_vertical_resolution(d)) >> 1, SCROLL_ANIM_TIME_MIN,
SCROLL_ANIM_TIME_MAX);
lv_anim_set_duration(&a, t);
int32_t sy = lv_obj_get_scroll_y(obj);
lv_anim_set_values(&a, -sy, -sy + dy);
lv_anim_set_exec_cb(&a, scroll_y_anim);
lv_anim_set_path_cb(&a, lv_anim_path_ease_out);
lv_result_t res;
res = lv_obj_send_event(obj, LV_EVENT_SCROLL_BEGIN, &a);
if(res != LV_RESULT_OK) return;
lv_anim_start(&a);
}
}
else {
/*Remove pending animations*/
lv_anim_delete(obj, scroll_y_anim);
lv_anim_delete(obj, scroll_x_anim);
lv_result_t res;
res = lv_obj_send_event(obj, LV_EVENT_SCROLL_BEGIN, NULL);
if(res != LV_RESULT_OK) return;
res = lv_obj_scroll_by_raw(obj, dx, dy);
if(res != LV_RESULT_OK) return;
res = lv_obj_send_event(obj, LV_EVENT_SCROLL_END, NULL);
if(res != LV_RESULT_OK) return;
}
}
void lv_obj_scroll_to(lv_obj_t * obj, int32_t x, int32_t y, lv_anim_enable_t anim_en)
{
lv_obj_scroll_to_x(obj, x, anim_en);
lv_obj_scroll_to_y(obj, y, anim_en);
}
void lv_obj_scroll_to_x(lv_obj_t * obj, int32_t x, lv_anim_enable_t anim_en)
{
lv_anim_delete(obj, scroll_x_anim);
int32_t scroll_x = lv_obj_get_scroll_x(obj);
int32_t diff = -x + scroll_x;
lv_obj_scroll_by_bounded(obj, diff, 0, anim_en);
}
void lv_obj_scroll_to_y(lv_obj_t * obj, int32_t y, lv_anim_enable_t anim_en)
{
lv_anim_delete(obj, scroll_y_anim);
int32_t scroll_y = lv_obj_get_scroll_y(obj);
int32_t diff = -y + scroll_y;
lv_obj_scroll_by_bounded(obj, 0, diff, anim_en);
}
void lv_obj_scroll_to_view(lv_obj_t * obj, lv_anim_enable_t anim_en)
{
/*Be sure the screens layout is correct*/
lv_obj_update_layout(obj);
lv_point_t p = {0, 0};
scroll_area_into_view(&obj->coords, obj, &p, anim_en);
}
void lv_obj_scroll_to_view_recursive(lv_obj_t * obj, lv_anim_enable_t anim_en)
{
/*Be sure the screens layout is correct*/
lv_obj_update_layout(obj);
lv_point_t p = {0, 0};
lv_obj_t * child = obj;
lv_obj_t * parent = lv_obj_get_parent(child);
while(parent) {
scroll_area_into_view(&obj->coords, child, &p, anim_en);
child = parent;
parent = lv_obj_get_parent(parent);
}
}
lv_result_t lv_obj_scroll_by_raw(lv_obj_t * obj, int32_t x, int32_t y)
{
if(x == 0 && y == 0) return LV_RESULT_OK;
lv_obj_allocate_spec_attr(obj);
obj->spec_attr->scroll.x += x;
obj->spec_attr->scroll.y += y;
lv_obj_move_children_by(obj, x, y, true);
lv_result_t res = lv_obj_send_event(obj, LV_EVENT_SCROLL, NULL);
if(res != LV_RESULT_OK) return res;
lv_obj_invalidate(obj);
return LV_RESULT_OK;
}
bool lv_obj_is_scrolling(const lv_obj_t * obj)
{
lv_indev_t * indev = lv_indev_get_next(NULL);
while(indev) {
if(lv_indev_get_scroll_obj(indev) == obj) return true;
indev = lv_indev_get_next(indev);
}
if(lv_anim_get((lv_obj_t *)obj, scroll_x_anim) != NULL ||
lv_anim_get((lv_obj_t *)obj, scroll_y_anim) != NULL) {
return true;
}
return false;
}
void lv_obj_stop_scroll_anim(const lv_obj_t * obj)
{
lv_anim_delete((lv_obj_t *)obj, scroll_y_anim);
lv_anim_delete((lv_obj_t *)obj, scroll_x_anim);
}
void lv_obj_update_snap(lv_obj_t * obj, lv_anim_enable_t anim_en)
{
lv_obj_update_layout(obj);
lv_point_t p;
lv_indev_scroll_get_snap_dist(obj, &p);
if(p.x == LV_COORD_MAX || p.x == LV_COORD_MIN) p.x = 0;
if(p.y == LV_COORD_MAX || p.y == LV_COORD_MIN) p.y = 0;
lv_obj_scroll_by(obj, p.x, p.y, anim_en);
}
void lv_obj_get_scrollbar_area(lv_obj_t * obj, lv_area_t * hor_area, lv_area_t * ver_area)
{
lv_area_set(hor_area, 0, 0, -1, -1);
lv_area_set(ver_area, 0, 0, -1, -1);
if(lv_obj_has_flag(obj, LV_OBJ_FLAG_SCROLLABLE) == false) return;
lv_scrollbar_mode_t sm = lv_obj_get_scrollbar_mode(obj);
if(sm == LV_SCROLLBAR_MODE_OFF) return;
/*If there is no indev scrolling this object but `mode==active` return*/
lv_indev_t * indev = lv_indev_get_next(NULL);
if(sm == LV_SCROLLBAR_MODE_ACTIVE) {
while(indev) {
if(lv_indev_get_scroll_obj(indev) == obj) break;
indev = lv_indev_get_next(indev);
}
if(indev == NULL) return;
}
int32_t st = lv_obj_get_scroll_top(obj);
int32_t sb = lv_obj_get_scroll_bottom(obj);
int32_t sl = lv_obj_get_scroll_left(obj);
int32_t sr = lv_obj_get_scroll_right(obj);
lv_dir_t dir = lv_obj_get_scroll_dir(obj);
bool ver_draw = false;
if((dir & LV_DIR_VER) &&
((sm == LV_SCROLLBAR_MODE_ON) ||
(sm == LV_SCROLLBAR_MODE_AUTO && (st > 0 || sb > 0)) ||
(sm == LV_SCROLLBAR_MODE_ACTIVE && lv_indev_get_scroll_dir(indev) == LV_DIR_VER))) {
ver_draw = true;
}
bool hor_draw = false;
if((dir & LV_DIR_HOR) &&
((sm == LV_SCROLLBAR_MODE_ON) ||
(sm == LV_SCROLLBAR_MODE_AUTO && (sl > 0 || sr > 0)) ||
(sm == LV_SCROLLBAR_MODE_ACTIVE && lv_indev_get_scroll_dir(indev) == LV_DIR_HOR))) {
hor_draw = true;
}
if(!hor_draw && !ver_draw) return;
bool rtl = lv_obj_get_style_base_dir(obj, LV_PART_SCROLLBAR) == LV_BASE_DIR_RTL;
int32_t top_space = lv_obj_get_style_pad_top(obj, LV_PART_SCROLLBAR);
int32_t bottom_space = lv_obj_get_style_pad_bottom(obj, LV_PART_SCROLLBAR);
int32_t left_space = lv_obj_get_style_pad_left(obj, LV_PART_SCROLLBAR);
int32_t right_space = lv_obj_get_style_pad_right(obj, LV_PART_SCROLLBAR);
int32_t thickness = lv_obj_get_style_width(obj, LV_PART_SCROLLBAR);
int32_t length = lv_obj_get_style_length(obj, LV_PART_SCROLLBAR);
int32_t obj_h = lv_obj_get_height(obj);
int32_t obj_w = lv_obj_get_width(obj);
/*Space required for the vertical and horizontal scrollbars*/
int32_t ver_reg_space = ver_draw ? thickness : 0;
int32_t hor_req_space = hor_draw ? thickness : 0;
int32_t rem;
if(lv_obj_get_style_bg_opa(obj, LV_PART_SCROLLBAR) <= LV_OPA_MIN &&
lv_obj_get_style_border_opa(obj, LV_PART_SCROLLBAR) <= LV_OPA_MIN) {
return;
}
/*Draw vertical scrollbar if the mode is ON or can be scrolled in this direction*/
int32_t content_h = obj_h + st + sb;
if(ver_draw && content_h) {
ver_area->y1 = obj->coords.y1;
ver_area->y2 = obj->coords.y2;
if(rtl) {
ver_area->x1 = obj->coords.x1 + left_space;
ver_area->x2 = ver_area->x1 + thickness - 1;
}
else {
ver_area->x2 = obj->coords.x2 - right_space;
ver_area->x1 = ver_area->x2 - thickness + 1;
}
int32_t sb_h = ((obj_h - top_space - bottom_space - hor_req_space) * obj_h) / content_h;
sb_h = LV_MAX(length > 0 ? length : sb_h, SCROLLBAR_MIN_SIZE); /*Style-defined size, calculated size, or minimum size*/
sb_h = LV_MIN(sb_h, obj_h); /*Limit scrollbar length to parent height*/
rem = (obj_h - top_space - bottom_space - hor_req_space) -
sb_h; /*Remaining size from the scrollbar track that is not the scrollbar itself*/
int32_t scroll_h = content_h - obj_h; /*The size of the content which can be really scrolled*/
if(scroll_h <= 0) {
ver_area->y1 = obj->coords.y1 + top_space;
ver_area->y2 = obj->coords.y2 - bottom_space - hor_req_space - 1;
}
else {
int32_t sb_y = (rem * sb) / scroll_h;
sb_y = rem - sb_y;
ver_area->y1 = obj->coords.y1 + sb_y + top_space;
ver_area->y2 = ver_area->y1 + sb_h - 1;
if(ver_area->y1 < obj->coords.y1 + top_space) {
ver_area->y1 = obj->coords.y1 + top_space;
if(ver_area->y1 + SCROLLBAR_MIN_SIZE > ver_area->y2) {
ver_area->y2 = ver_area->y1 + SCROLLBAR_MIN_SIZE;
}
}
if(ver_area->y2 > obj->coords.y2 - hor_req_space - bottom_space) {
ver_area->y2 = obj->coords.y2 - hor_req_space - bottom_space;
if(ver_area->y2 - SCROLLBAR_MIN_SIZE < ver_area->y1) {
ver_area->y1 = ver_area->y2 - SCROLLBAR_MIN_SIZE;
}
}
}
}
/*Draw horizontal scrollbar if the mode is ON or can be scrolled in this direction*/
int32_t content_w = obj_w + sl + sr;
if(hor_draw && content_w) {
hor_area->y2 = obj->coords.y2 - bottom_space;
hor_area->y1 = hor_area->y2 - thickness + 1;
hor_area->x1 = obj->coords.x1;
hor_area->x2 = obj->coords.x2;
int32_t sb_w = ((obj_w - left_space - right_space - ver_reg_space) * obj_w) / content_w;
sb_w = LV_MAX(length > 0 ? length : sb_w, SCROLLBAR_MIN_SIZE); /*Style-defined size, calculated size, or minimum size*/
sb_w = LV_MIN(sb_w, obj_w); /*Limit scrollbar length to parent width*/
rem = (obj_w - left_space - right_space - ver_reg_space) -
sb_w; /*Remaining size from the scrollbar track that is not the scrollbar itself*/
int32_t scroll_w = content_w - obj_w; /*The size of the content which can be really scrolled*/
if(scroll_w <= 0) {
if(rtl) {
hor_area->x1 = obj->coords.x1 + left_space + ver_reg_space - 1;
hor_area->x2 = obj->coords.x2 - right_space;
}
else {
hor_area->x1 = obj->coords.x1 + left_space;
hor_area->x2 = obj->coords.x2 - right_space - ver_reg_space - 1;
}
}
else {
int32_t sb_x = (rem * sr) / scroll_w;
sb_x = rem - sb_x;
if(rtl) {
hor_area->x1 = obj->coords.x1 + sb_x + left_space + ver_reg_space;
hor_area->x2 = hor_area->x1 + sb_w - 1;
if(hor_area->x1 < obj->coords.x1 + left_space + ver_reg_space) {
hor_area->x1 = obj->coords.x1 + left_space + ver_reg_space;
if(hor_area->x1 + SCROLLBAR_MIN_SIZE > hor_area->x2) {
hor_area->x2 = hor_area->x1 + SCROLLBAR_MIN_SIZE;
}
}
if(hor_area->x2 > obj->coords.x2 - right_space) {
hor_area->x2 = obj->coords.x2 - right_space;
if(hor_area->x2 - SCROLLBAR_MIN_SIZE < hor_area->x1) {
hor_area->x1 = hor_area->x2 - SCROLLBAR_MIN_SIZE;
}
}
}
else {
hor_area->x1 = obj->coords.x1 + sb_x + left_space;
hor_area->x2 = hor_area->x1 + sb_w - 1;
if(hor_area->x1 < obj->coords.x1 + left_space) {
hor_area->x1 = obj->coords.x1 + left_space;
if(hor_area->x1 + SCROLLBAR_MIN_SIZE > hor_area->x2) {
hor_area->x2 = hor_area->x1 + SCROLLBAR_MIN_SIZE;
}
}
if(hor_area->x2 > obj->coords.x2 - ver_reg_space - right_space) {
hor_area->x2 = obj->coords.x2 - ver_reg_space - right_space;
if(hor_area->x2 - SCROLLBAR_MIN_SIZE < hor_area->x1) {
hor_area->x1 = hor_area->x2 - SCROLLBAR_MIN_SIZE;
}
}
}
}
}
}
void lv_obj_scrollbar_invalidate(lv_obj_t * obj)
{
lv_area_t hor_area;
lv_area_t ver_area;
lv_obj_get_scrollbar_area(obj, &hor_area, &ver_area);
if(lv_area_get_size(&hor_area) <= 0 && lv_area_get_size(&ver_area) <= 0) return;
if(lv_area_get_size(&hor_area) > 0) lv_obj_invalidate_area(obj, &hor_area);
if(lv_area_get_size(&ver_area) > 0) lv_obj_invalidate_area(obj, &ver_area);
}
void lv_obj_readjust_scroll(lv_obj_t * obj, lv_anim_enable_t anim_en)
{
/*Be sure the bottom side is not remains scrolled in*/
/*With snapping the content can't be scrolled in*/
if(lv_obj_get_scroll_snap_y(obj) == LV_SCROLL_SNAP_NONE) {
int32_t st = lv_obj_get_scroll_top(obj);
int32_t sb = lv_obj_get_scroll_bottom(obj);
if(sb < 0 && st > 0) {
sb = LV_MIN(st, -sb);
lv_obj_scroll_by(obj, 0, sb, anim_en);
}
}
if(lv_obj_get_scroll_snap_x(obj) == LV_SCROLL_SNAP_NONE) {
int32_t sl = lv_obj_get_scroll_left(obj);
int32_t sr = lv_obj_get_scroll_right(obj);
if(lv_obj_get_style_base_dir(obj, LV_PART_MAIN) != LV_BASE_DIR_RTL) {
/*Be sure the left side is not remains scrolled in*/
if(sr < 0 && sl > 0) {
sr = LV_MIN(sl, -sr);
lv_obj_scroll_by(obj, sr, 0, anim_en);
}
}
else {
/*Be sure the right side is not remains scrolled in*/
if(sl < 0 && sr > 0) {
sr = LV_MIN(sr, -sl);
lv_obj_scroll_by(obj, sl, 0, anim_en);
}
}
}
}
/**********************
* STATIC FUNCTIONS
**********************/
static void scroll_x_anim(void * obj, int32_t v)
{
lv_obj_scroll_by_raw(obj, v + lv_obj_get_scroll_x(obj), 0);
}
static void scroll_y_anim(void * obj, int32_t v)
{
lv_obj_scroll_by_raw(obj, 0, v + lv_obj_get_scroll_y(obj));
}
static void scroll_end_cb(lv_anim_t * a)
{
/*Do not sent END event if there wasn't a BEGIN*/
if(a->start_cb_called) lv_obj_send_event(a->var, LV_EVENT_SCROLL_END, NULL);
}
static void scroll_area_into_view(const lv_area_t * area, lv_obj_t * child, lv_point_t * scroll_value,
lv_anim_enable_t anim_en)
{
lv_obj_t * parent = lv_obj_get_parent(child);
if(!lv_obj_has_flag(parent, LV_OBJ_FLAG_SCROLLABLE)) return;
lv_dir_t scroll_dir = lv_obj_get_scroll_dir(parent);
int32_t snap_goal = 0;
int32_t act = 0;
const lv_area_t * area_tmp;
int32_t y_scroll = 0;
lv_scroll_snap_t snap_y = lv_obj_get_scroll_snap_y(parent);
if(snap_y != LV_SCROLL_SNAP_NONE) area_tmp = &child->coords;
else area_tmp = area;
int32_t stop = lv_obj_get_style_space_top(parent, LV_PART_MAIN);
int32_t sbottom = lv_obj_get_style_space_bottom(parent, LV_PART_MAIN);
int32_t top_diff = parent->coords.y1 + stop - area_tmp->y1 - scroll_value->y;
int32_t bottom_diff = -(parent->coords.y2 - sbottom - area_tmp->y2 - scroll_value->y);
int32_t parent_h = lv_obj_get_height(parent) - stop - sbottom;
if((top_diff >= 0 && bottom_diff >= 0)) y_scroll = 0;
else if(top_diff > 0) {
y_scroll = top_diff;
/*Do not let scrolling in*/
int32_t st = lv_obj_get_scroll_top(parent);
if(st - y_scroll < 0) y_scroll = 0;
}
else if(bottom_diff > 0) {
y_scroll = -bottom_diff;
/*Do not let scrolling in*/
int32_t sb = lv_obj_get_scroll_bottom(parent);
if(sb + y_scroll < 0) y_scroll = 0;
}
switch(snap_y) {
case LV_SCROLL_SNAP_START:
snap_goal = parent->coords.y1 + stop;
act = area_tmp->y1 + y_scroll;
y_scroll += snap_goal - act;
break;
case LV_SCROLL_SNAP_END:
snap_goal = parent->coords.y2 - sbottom;
act = area_tmp->y2 + y_scroll;
y_scroll += snap_goal - act;
break;
case LV_SCROLL_SNAP_CENTER:
snap_goal = parent->coords.y1 + stop + parent_h / 2;
act = lv_area_get_height(area_tmp) / 2 + area_tmp->y1 + y_scroll;
y_scroll += snap_goal - act;
break;
case LV_SCROLL_SNAP_NONE:
break;
}
int32_t x_scroll = 0;
lv_scroll_snap_t snap_x = lv_obj_get_scroll_snap_x(parent);
if(snap_x != LV_SCROLL_SNAP_NONE) area_tmp = &child->coords;
else area_tmp = area;
int32_t sleft = lv_obj_get_style_space_left(parent, LV_PART_MAIN);
int32_t sright = lv_obj_get_style_space_right(parent, LV_PART_MAIN);
int32_t left_diff = parent->coords.x1 + sleft - area_tmp->x1 - scroll_value->x;
int32_t right_diff = -(parent->coords.x2 - sright - area_tmp->x2 - scroll_value->x);
if((left_diff >= 0 && right_diff >= 0)) x_scroll = 0;
else if(left_diff > 0) {
x_scroll = left_diff;
/*Do not let scrolling in*/
int32_t sl = lv_obj_get_scroll_left(parent);
if(sl - x_scroll < 0) x_scroll = 0;
}
else if(right_diff > 0) {
x_scroll = -right_diff;
/*Do not let scrolling in*/
int32_t sr = lv_obj_get_scroll_right(parent);
if(sr + x_scroll < 0) x_scroll = 0;
}
int32_t parent_w = lv_obj_get_width(parent) - sleft - sright;
switch(snap_x) {
case LV_SCROLL_SNAP_START:
snap_goal = parent->coords.x1 + sleft;
act = area_tmp->x1 + x_scroll;
x_scroll += snap_goal - act;
break;
case LV_SCROLL_SNAP_END:
snap_goal = parent->coords.x2 - sright;
act = area_tmp->x2 + x_scroll;
x_scroll += snap_goal - act;
break;
case LV_SCROLL_SNAP_CENTER:
snap_goal = parent->coords.x1 + sleft + parent_w / 2;
act = lv_area_get_width(area_tmp) / 2 + area_tmp->x1 + x_scroll;
x_scroll += snap_goal - act;
break;
case LV_SCROLL_SNAP_NONE:
break;
}
/*Remove any pending scroll animations.*/
lv_anim_delete(parent, scroll_y_anim);
lv_anim_delete(parent, scroll_x_anim);
if((scroll_dir & LV_DIR_LEFT) == 0 && x_scroll < 0) x_scroll = 0;
if((scroll_dir & LV_DIR_RIGHT) == 0 && x_scroll > 0) x_scroll = 0;
if((scroll_dir & LV_DIR_TOP) == 0 && y_scroll < 0) y_scroll = 0;
if((scroll_dir & LV_DIR_BOTTOM) == 0 && y_scroll > 0) y_scroll = 0;
scroll_value->x += anim_en ? x_scroll : 0;
scroll_value->y += anim_en ? y_scroll : 0;
lv_obj_scroll_by(parent, x_scroll, y_scroll, anim_en);
}
@@ -0,0 +1,301 @@
/**
* @file lv_obj_scroll.h
*
*/
#ifndef LV_OBJ_SCROLL_H
#define LV_OBJ_SCROLL_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../misc/lv_area.h"
#include "../misc/lv_anim.h"
#include "../misc/lv_types.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/*Can't include lv_obj.h because it includes this header file*/
/** Scrollbar modes: shows when should the scrollbars be visible*/
typedef enum {
LV_SCROLLBAR_MODE_OFF, /**< Never show scrollbars*/
LV_SCROLLBAR_MODE_ON, /**< Always show scrollbars*/
LV_SCROLLBAR_MODE_ACTIVE, /**< Show scroll bars when Widget is being scrolled*/
LV_SCROLLBAR_MODE_AUTO, /**< Show scroll bars when the content is large enough to be scrolled*/
} lv_scrollbar_mode_t;
/** Scroll span align options. Tells where to align the snappable children when scroll stops.*/
typedef enum {
LV_SCROLL_SNAP_NONE, /**< Do not align, leave where it is*/
LV_SCROLL_SNAP_START, /**< Align to the left/top*/
LV_SCROLL_SNAP_END, /**< Align to the right/bottom*/
LV_SCROLL_SNAP_CENTER /**< Align to the center*/
} lv_scroll_snap_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
/*=====================
* Setter functions
*====================*/
/**
* Set how the scrollbars should behave.
* @param obj pointer to Widget
* @param mode LV_SCROLL_MODE_ON/OFF/AUTO/ACTIVE
*/
void lv_obj_set_scrollbar_mode(lv_obj_t * obj, lv_scrollbar_mode_t mode);
/**
* Set direction Widget can be scrolled
* @param obj pointer to Widget
* @param dir one or more bit-wise OR-ed values of `lv_dir_t` enumeration
*/
void lv_obj_set_scroll_dir(lv_obj_t * obj, lv_dir_t dir);
/**
* Set where to snap the children when scrolling ends horizontally
* @param obj pointer to Widget
* @param align value from `lv_scroll_snap_t` enumeration
*/
void lv_obj_set_scroll_snap_x(lv_obj_t * obj, lv_scroll_snap_t align);
/**
* Set where to snap the children when scrolling ends vertically
* @param obj pointer to Widget
* @param align value from `lv_scroll_snap_t` enumeration
*/
void lv_obj_set_scroll_snap_y(lv_obj_t * obj, lv_scroll_snap_t align);
/*=====================
* Getter functions
*====================*/
/**
* Get the current scroll mode (when to hide the scrollbars)
* @param obj pointer to Widget
* @return the current scroll mode from `lv_scrollbar_mode_t`
*/
lv_scrollbar_mode_t lv_obj_get_scrollbar_mode(const lv_obj_t * obj);
/**
* Get directions Widget can be scrolled (set with `lv_obj_set_scroll_dir()`)
* @param obj pointer to Widget
* @return current scroll direction bit(s)
*/
lv_dir_t lv_obj_get_scroll_dir(const lv_obj_t * obj);
/**
* Get where to snap child Widgets when horizontal scrolling ends.
* @param obj pointer to Widget
* @return current snap value from `lv_scroll_snap_t`
*/
lv_scroll_snap_t lv_obj_get_scroll_snap_x(const lv_obj_t * obj);
/**
* Get where to snap child Widgets when vertical scrolling ends.
* @param obj pointer to Widget
* @return current snap value from `lv_scroll_snap_t`
*/
lv_scroll_snap_t lv_obj_get_scroll_snap_y(const lv_obj_t * obj);
/**
* Get current X scroll position. Identical to `lv_obj_get_scroll_left()`.
* @param obj pointer to scrollable container Widget
* @return current scroll position from left edge
* - If Widget is not scrolled return 0.
* - If scrolled return > 0.
* - If scrolled inside (elastic scroll) return < 0.
*/
int32_t lv_obj_get_scroll_x(const lv_obj_t * obj);
/**
* Get current Y scroll position. Identical to `lv_obj_get_scroll_top()`.
* @param obj pointer to scrollable container Widget
* @return current scroll position from top edge
* - If Widget is not scrolled return 0.
* - If scrolled return > 0.
* - If scrolled inside (elastic scroll) return < 0.
*/
int32_t lv_obj_get_scroll_y(const lv_obj_t * obj);
/**
* Number of pixels a scrollable container Widget can be scrolled down
* before its top edge appears. When LV_OBJ_FLAG_SCROLL_ELASTIC flag
* is set in Widget, this value can go negative while Widget is being
* dragged below its normal top-edge boundary.
* @param obj pointer to scrollable container Widget
* @return pixels Widget can be scrolled down before its top edge appears
*/
int32_t lv_obj_get_scroll_top(const lv_obj_t * obj);
/**
* Number of pixels a scrollable container Widget can be scrolled up
* before its bottom edge appears. When LV_OBJ_FLAG_SCROLL_ELASTIC flag
* is set in Widget, this value can go negative while Widget is being
* dragged above its normal bottom-edge boundary.
* @param obj pointer to scrollable container Widget
* @return pixels Widget can be scrolled up before its bottom edge appears
*/
int32_t lv_obj_get_scroll_bottom(const lv_obj_t * obj);
/**
* Number of pixels a scrollable container Widget can be scrolled right
* before its left edge appears. When LV_OBJ_FLAG_SCROLL_ELASTIC flag
* is set in Widget, this value can go negative while Widget is being
* dragged farther right than its normal left-edge boundary.
* @param obj pointer to scrollable container Widget
* @return pixels Widget can be scrolled right before its left edge appears
*/
int32_t lv_obj_get_scroll_left(const lv_obj_t * obj);
/**
* Number of pixels a scrollable container Widget can be scrolled left
* before its right edge appears. When LV_OBJ_FLAG_SCROLL_ELASTIC flag
* is set in Widget, this value can go negative while Widget is being
* dragged farther left than its normal right-edge boundary.
* @param obj pointer to scrollable container Widget
* @return pixels Widget can be scrolled left before its right edge appears
*/
int32_t lv_obj_get_scroll_right(const lv_obj_t * obj);
/**
* Get the X and Y coordinates where the scrolling will end for Widget if a scrolling animation is in progress.
* If no scrolling animation, give the current `x` or `y` scroll position.
* @param obj pointer to scrollable Widget
* @param end pointer to `lv_point_t` object in which to store result
*/
void lv_obj_get_scroll_end(lv_obj_t * obj, lv_point_t * end);
/*=====================
* Other functions
*====================*/
/**
* Scroll by given amount of pixels.
* @param obj pointer to scrollable Widget to scroll
* @param dx pixels to scroll horizontally
* @param dy pixels to scroll vertically
* @param anim_en LV_ANIM_ON: scroll with animation; LV_ANIM_OFF: scroll immediately
* @note > 0 value means scroll right/bottom (show the more content on the right/bottom)
* @note e.g. dy = -20 means scroll down 20 px
*/
void lv_obj_scroll_by(lv_obj_t * obj, int32_t dx, int32_t dy, lv_anim_enable_t anim_en);
/**
* Scroll by given amount of pixels.
* `dx` and `dy` will be limited internally to allow scrolling only on the content area.
* @param obj pointer to scrollable Widget to scroll
* @param dx pixels to scroll horizontally
* @param dy pixels to scroll vertically
* @param anim_en LV_ANIM_ON: scroll with animation; LV_ANIM_OFF: scroll immediately
* @note e.g. dy = -20 means scroll down 20 px
*/
void lv_obj_scroll_by_bounded(lv_obj_t * obj, int32_t dx, int32_t dy, lv_anim_enable_t anim_en);
/**
* Scroll to given coordinate on Widget.
* `x` and `y` will be limited internally to allow scrolling only on the content area.
* @param obj pointer to scrollable Widget to scroll
* @param x pixels to scroll horizontally
* @param y pixels to scroll vertically
* @param anim_en LV_ANIM_ON: scroll with animation; LV_ANIM_OFF: scroll immediately
*/
void lv_obj_scroll_to(lv_obj_t * obj, int32_t x, int32_t y, lv_anim_enable_t anim_en);
/**
* Scroll to X coordinate on Widget.
* `x` will be limited internally to allow scrolling only on the content area.
* @param obj pointer to scrollable Widget to scroll
* @param x pixels to scroll horizontally
* @param anim_en LV_ANIM_ON: scroll with animation; LV_ANIM_OFF: scroll immediately
*/
void lv_obj_scroll_to_x(lv_obj_t * obj, int32_t x, lv_anim_enable_t anim_en);
/**
* Scroll to Y coordinate on Widget.
* `y` will be limited internally to allow scrolling only on the content area.
* @param obj pointer to scrollable Widget to scroll
* @param y pixels to scroll vertically
* @param anim_en LV_ANIM_ON: scroll with animation; LV_ANIM_OFF: scroll immediately
*/
void lv_obj_scroll_to_y(lv_obj_t * obj, int32_t y, lv_anim_enable_t anim_en);
/**
* Scroll `obj`'s parent Widget until `obj` becomes visible.
* @param obj pointer to Widget to scroll into view
* @param anim_en LV_ANIM_ON: scroll with animation; LV_ANIM_OFF: scroll immediately
*/
void lv_obj_scroll_to_view(lv_obj_t * obj, lv_anim_enable_t anim_en);
/**
* Scroll `obj`'s parent Widgets recursively until `obj` becomes visible.
* Widget will be scrolled into view even it has nested scrollable parents.
* @param obj pointer to Widget to scroll into view
* @param anim_en LV_ANIM_ON: scroll with animation; LV_ANIM_OFF: scroll immediately
*/
void lv_obj_scroll_to_view_recursive(lv_obj_t * obj, lv_anim_enable_t anim_en);
/**
* Tell whether Widget is being scrolled or not at this moment
* @param obj pointer to Widget
* @return true: `obj` is being scrolled
*/
bool lv_obj_is_scrolling(const lv_obj_t * obj);
/**
* Stop scrolling the current object
*
* @param obj The object being scrolled
*/
void lv_obj_stop_scroll_anim(const lv_obj_t * obj);
/**
* Check children of `obj` and scroll `obj` to fulfill scroll_snap settings.
* @param obj Widget whose children need to be checked and snapped
* @param anim_en LV_ANIM_ON/OFF
*/
void lv_obj_update_snap(lv_obj_t * obj, lv_anim_enable_t anim_en);
/**
* Get the area of the scrollbars
* @param obj pointer to Widget
* @param hor pointer to store the area of the horizontal scrollbar
* @param ver pointer to store the area of the vertical scrollbar
*/
void lv_obj_get_scrollbar_area(lv_obj_t * obj, lv_area_t * hor, lv_area_t * ver);
/**
* Invalidate the area of the scrollbars
* @param obj pointer to Widget
*/
void lv_obj_scrollbar_invalidate(lv_obj_t * obj);
/**
* Checks if the content is scrolled "in" and adjusts it to a normal position.
* @param obj pointer to Widget
* @param anim_en LV_ANIM_ON/OFF
*/
void lv_obj_readjust_scroll(lv_obj_t * obj, lv_anim_enable_t anim_en);
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_OBJ_SCROLL_H*/
@@ -0,0 +1,50 @@
/**
* @file lv_obj_scroll_private.h
*
*/
#ifndef LV_OBJ_SCROLL_PRIVATE_H
#define LV_OBJ_SCROLL_PRIVATE_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "lv_obj_scroll.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Low level function to scroll by given x and y coordinates.
* `LV_EVENT_SCROLL` is sent.
* @param obj pointer to an object to scroll
* @param x pixels to scroll horizontally
* @param y pixels to scroll vertically
* @return `LV_RESULT_INVALID`: to object was deleted in `LV_EVENT_SCROLL`;
* `LV_RESULT_OK`: if the object is still valid
*/
lv_result_t lv_obj_scroll_by_raw(lv_obj_t * obj, int32_t x, int32_t y);
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_OBJ_SCROLL_PRIVATE_H*/
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,337 @@
/**
* @file lv_obj_style.h
*
*/
#ifndef LV_OBJ_STYLE_H
#define LV_OBJ_STYLE_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../misc/lv_bidi.h"
#include "../misc/lv_style.h"
#include "../misc/lv_types.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
typedef enum {
LV_STYLE_STATE_CMP_SAME, /**< The style properties in the 2 states are identical */
LV_STYLE_STATE_CMP_DIFF_REDRAW, /**< The differences can be shown with a simple redraw */
LV_STYLE_STATE_CMP_DIFF_DRAW_PAD, /**< The differences can be shown with a simple redraw */
LV_STYLE_STATE_CMP_DIFF_LAYOUT, /**< The differences can be shown with a simple redraw */
} lv_style_state_cmp_t;
typedef uint32_t lv_style_selector_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Add a style to an object.
* @param obj pointer to an object
* @param style pointer to a style to add
* @param selector OR-ed value of parts and state to which the style should be added
*
* Examples:
* @code
* lv_obj_add_style(btn, &style_btn, 0); //Default button style
*
* lv_obj_add_style(btn, &btn_red, LV_STATE_PRESSED); //Overwrite only some colors to red when pressed
* @endcode
*/
void lv_obj_add_style(lv_obj_t * obj, const lv_style_t * style, lv_style_selector_t selector);
/**
* Replaces a style of an object, preserving the order of the style stack (local styles and transitions are ignored).
* @param obj pointer to an object
* @param old_style pointer to a style to replace.
* @param new_style pointer to a style to replace the old style with.
* @param selector OR-ed values of states and a part to replace only styles with matching selectors. LV_STATE_ANY and LV_PART_ANY can be used
*
* Examples:
* @code
* lv_obj_replace_style(obj, &yellow_style, &blue_style, LV_PART_ANY | LV_STATE_ANY); //Replace a specific style
*
* lv_obj_replace_style(obj, &yellow_style, &blue_style, LV_PART_MAIN | LV_STATE_PRESSED); //Replace a specific style assigned to the main part when it is pressed
* @endcode
*/
bool lv_obj_replace_style(lv_obj_t * obj, const lv_style_t * old_style, const lv_style_t * new_style,
lv_style_selector_t selector);
/**
* Remove a style from an object.
* @param obj pointer to an object
* @param style pointer to a style to remove. Can be NULL to check only the selector
* @param selector OR-ed values of states and a part to remove only styles with matching selectors. LV_STATE_ANY and LV_PART_ANY can be used
*
* Examples:
* @code
* lv_obj_remove_style(obj, &style, LV_PART_ANY | LV_STATE_ANY); //Remove a specific style
*
* lv_obj_remove_style(obj, NULL, LV_PART_MAIN | LV_STATE_ANY); //Remove all styles from the main part
*
* lv_obj_remove_style(obj, NULL, LV_PART_ANY | LV_STATE_ANY); //Remove all styles
* @endcode
*/
void lv_obj_remove_style(lv_obj_t * obj, const lv_style_t * style, lv_style_selector_t selector);
/**
* Remove all styles from an object
* @param obj pointer to an object
*/
void lv_obj_remove_style_all(lv_obj_t * obj);
/**
* Notify all object if a style is modified
* @param style pointer to a style. Only the objects with this style will be notified
* (NULL to notify all objects)
*/
void lv_obj_report_style_change(lv_style_t * style);
/**
* Notify an object and its children about its style is modified.
* @param obj pointer to an object
* @param part the part whose style was changed. E.g. `LV_PART_ANY`, `LV_PART_MAIN`
* @param prop `LV_STYLE_PROP_ANY` or an `LV_STYLE_...` property.
* It is used to optimize what needs to be refreshed.
* `LV_STYLE_PROP_INV` to perform only a style cache update
*/
void lv_obj_refresh_style(lv_obj_t * obj, lv_part_t part, lv_style_prop_t prop);
/**
* Enable or disable automatic style refreshing when a new style is added/removed to/from an object
* or any other style change happens.
* @param en true: enable refreshing; false: disable refreshing
*/
void lv_obj_enable_style_refresh(bool en);
/**
* Get the value of a style property. The current state of the object will be considered.
* Inherited properties will be inherited.
* If a property is not set a default value will be returned.
* @param obj pointer to an object
* @param part a part from which the property should be get
* @param prop the property to get
* @return the value of the property.
* Should be read from the correct field of the `lv_style_value_t` according to the type of the property.
*/
lv_style_value_t lv_obj_get_style_prop(const lv_obj_t * obj, lv_part_t part, lv_style_prop_t prop);
/**
* Check if an object has a specified style property for a given style selector.
* @param obj pointer to an object
* @param selector the style selector to be checked, defining the scope of the style to be examined.
* @param prop the property to be checked.
* @return true if the object has the specified selector and property, false otherwise.
*/
bool lv_obj_has_style_prop(const lv_obj_t * obj, lv_style_selector_t selector, lv_style_prop_t prop);
/**
* Set local style property on an object's part and state.
* @param obj pointer to an object
* @param prop the property
* @param value value of the property. The correct element should be set according to the type of the property
* @param selector OR-ed value of parts and state for which the style should be set
*/
void lv_obj_set_local_style_prop(lv_obj_t * obj, lv_style_prop_t prop, lv_style_value_t value,
lv_style_selector_t selector);
lv_style_res_t lv_obj_get_local_style_prop(lv_obj_t * obj, lv_style_prop_t prop, lv_style_value_t * value,
lv_style_selector_t selector);
/**
* Remove a local style property from a part of an object with a given state.
* @param obj pointer to an object
* @param prop a style property to remove.
* @param selector OR-ed value of parts and state for which the style should be removed
* @return true the property was found and removed; false: the property was not found
*/
bool lv_obj_remove_local_style_prop(lv_obj_t * obj, lv_style_prop_t prop, lv_style_selector_t selector);
/**
* Used internally for color filtering
*/
lv_style_value_t lv_obj_style_apply_color_filter(const lv_obj_t * obj, lv_part_t part, lv_style_value_t v);
/**
* Fade in an an object and all its children.
* @param obj the object to fade in
* @param time time of fade
* @param delay delay to start the animation
*/
void lv_obj_fade_in(lv_obj_t * obj, uint32_t time, uint32_t delay);
/**
* Fade out an an object and all its children.
* @param obj the object to fade out
* @param time time of fade
* @param delay delay to start the animation
*/
void lv_obj_fade_out(lv_obj_t * obj, uint32_t time, uint32_t delay);
static inline lv_state_t lv_obj_style_get_selector_state(lv_style_selector_t selector)
{
return selector & 0xFFFF;
}
static inline lv_part_t lv_obj_style_get_selector_part(lv_style_selector_t selector)
{
return selector & 0xFF0000;
}
#include "lv_obj_style_gen.h"
static inline void lv_obj_set_style_pad_all(lv_obj_t * obj, int32_t value, lv_style_selector_t selector)
{
lv_obj_set_style_pad_left(obj, value, selector);
lv_obj_set_style_pad_right(obj, value, selector);
lv_obj_set_style_pad_top(obj, value, selector);
lv_obj_set_style_pad_bottom(obj, value, selector);
}
static inline void lv_obj_set_style_pad_hor(lv_obj_t * obj, int32_t value, lv_style_selector_t selector)
{
lv_obj_set_style_pad_left(obj, value, selector);
lv_obj_set_style_pad_right(obj, value, selector);
}
static inline void lv_obj_set_style_pad_ver(lv_obj_t * obj, int32_t value, lv_style_selector_t selector)
{
lv_obj_set_style_pad_top(obj, value, selector);
lv_obj_set_style_pad_bottom(obj, value, selector);
}
static inline void lv_obj_set_style_margin_all(lv_obj_t * obj, int32_t value, lv_style_selector_t selector)
{
lv_obj_set_style_margin_left(obj, value, selector);
lv_obj_set_style_margin_right(obj, value, selector);
lv_obj_set_style_margin_top(obj, value, selector);
lv_obj_set_style_margin_bottom(obj, value, selector);
}
static inline void lv_obj_set_style_margin_hor(lv_obj_t * obj, int32_t value, lv_style_selector_t selector)
{
lv_obj_set_style_margin_left(obj, value, selector);
lv_obj_set_style_margin_right(obj, value, selector);
}
static inline void lv_obj_set_style_margin_ver(lv_obj_t * obj, int32_t value, lv_style_selector_t selector)
{
lv_obj_set_style_margin_top(obj, value, selector);
lv_obj_set_style_margin_bottom(obj, value, selector);
}
static inline void lv_obj_set_style_pad_gap(lv_obj_t * obj, int32_t value, lv_style_selector_t selector)
{
lv_obj_set_style_pad_row(obj, value, selector);
lv_obj_set_style_pad_column(obj, value, selector);
}
static inline void lv_obj_set_style_size(lv_obj_t * obj, int32_t width, int32_t height,
lv_style_selector_t selector)
{
lv_obj_set_style_width(obj, width, selector);
lv_obj_set_style_height(obj, height, selector);
}
static inline void lv_obj_set_style_transform_scale(lv_obj_t * obj, int32_t value,
lv_style_selector_t selector)
{
lv_obj_set_style_transform_scale_x(obj, value, selector);
lv_obj_set_style_transform_scale_y(obj, value, selector);
}
static inline int32_t lv_obj_get_style_space_left(const lv_obj_t * obj, lv_part_t part)
{
int32_t padding = lv_obj_get_style_pad_left(obj, part);
int32_t border_width = lv_obj_get_style_border_width(obj, part);
lv_border_side_t border_side = lv_obj_get_style_border_side(obj, part);
return (border_side & LV_BORDER_SIDE_LEFT) ? padding + border_width : padding;
}
static inline int32_t lv_obj_get_style_space_right(const lv_obj_t * obj, lv_part_t part)
{
int32_t padding = lv_obj_get_style_pad_right(obj, part);
int32_t border_width = lv_obj_get_style_border_width(obj, part);
lv_border_side_t border_side = lv_obj_get_style_border_side(obj, part);
return (border_side & LV_BORDER_SIDE_RIGHT) ? padding + border_width : padding;
}
static inline int32_t lv_obj_get_style_space_top(const lv_obj_t * obj, lv_part_t part)
{
int32_t padding = lv_obj_get_style_pad_top(obj, part);
int32_t border_width = lv_obj_get_style_border_width(obj, part);
lv_border_side_t border_side = lv_obj_get_style_border_side(obj, part);
return (border_side & LV_BORDER_SIDE_TOP) ? padding + border_width : padding;
}
static inline int32_t lv_obj_get_style_space_bottom(const lv_obj_t * obj, lv_part_t part)
{
int32_t padding = lv_obj_get_style_pad_bottom(obj, part);
int32_t border_width = lv_obj_get_style_border_width(obj, part);
lv_border_side_t border_side = lv_obj_get_style_border_side(obj, part);
return (border_side & LV_BORDER_SIDE_BOTTOM) ? padding + border_width : padding;
}
lv_text_align_t lv_obj_calculate_style_text_align(const lv_obj_t * obj, lv_part_t part, const char * txt);
static inline int32_t lv_obj_get_style_transform_scale_x_safe(const lv_obj_t * obj, lv_part_t part)
{
int32_t scale = lv_obj_get_style_transform_scale_x(obj, part);
return scale > 0 ? scale : 1;
}
static inline int32_t lv_obj_get_style_transform_scale_y_safe(const lv_obj_t * obj, lv_part_t part)
{
int32_t scale = lv_obj_get_style_transform_scale_y(obj, part);
return scale > 0 ? scale : 1;
}
/**
* Get the `opa` style property from all parents and multiply and `>> 8` them.
* @param obj the object whose opacity should be get
* @param part the part whose opacity should be get. Non-MAIN parts will consider the `opa` of the MAIN part too
* @return the final opacity considering the parents' opacity too
*/
lv_opa_t lv_obj_get_style_opa_recursive(const lv_obj_t * obj, lv_part_t part);
/**
* Apply recolor effect to the input color based on the object's style properties.
* @param obj the target object containing recolor style properties
* @param part the part to retrieve recolor styles.
* @param color the original color to be modified
* @return the blended color after applying recolor and opacity
*/
lv_color32_t lv_obj_style_apply_recolor(const lv_obj_t * obj, lv_part_t part, lv_color32_t color);
/**
* Get the `recolor` style property from all parents and blend them recursively.
* @param obj the object whose recolor value should be retrieved
* @param part the target part to check. Non-MAIN parts will also consider
* the `recolor` value from the MAIN part during calculation
* @return the final blended recolor value combining all parent's recolor values
*/
lv_color32_t lv_obj_get_style_recolor_recursive(const lv_obj_t * obj, lv_part_t part);
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_OBJ_STYLE_H*/
@@ -0,0 +1,961 @@
/*
**********************************************************************
* DO NOT EDIT
* This file is automatically generated by "style_api_gen.py"
**********************************************************************
*/
#include "lv_obj.h"
void lv_obj_set_style_width(lv_obj_t * obj, int32_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_WIDTH, v, selector);
}
void lv_obj_set_style_min_width(lv_obj_t * obj, int32_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_MIN_WIDTH, v, selector);
}
void lv_obj_set_style_max_width(lv_obj_t * obj, int32_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_MAX_WIDTH, v, selector);
}
void lv_obj_set_style_height(lv_obj_t * obj, int32_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_HEIGHT, v, selector);
}
void lv_obj_set_style_min_height(lv_obj_t * obj, int32_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_MIN_HEIGHT, v, selector);
}
void lv_obj_set_style_max_height(lv_obj_t * obj, int32_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_MAX_HEIGHT, v, selector);
}
void lv_obj_set_style_length(lv_obj_t * obj, int32_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_LENGTH, v, selector);
}
void lv_obj_set_style_x(lv_obj_t * obj, int32_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_X, v, selector);
}
void lv_obj_set_style_y(lv_obj_t * obj, int32_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_Y, v, selector);
}
void lv_obj_set_style_align(lv_obj_t * obj, lv_align_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_ALIGN, v, selector);
}
void lv_obj_set_style_transform_width(lv_obj_t * obj, int32_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_TRANSFORM_WIDTH, v, selector);
}
void lv_obj_set_style_transform_height(lv_obj_t * obj, int32_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_TRANSFORM_HEIGHT, v, selector);
}
void lv_obj_set_style_translate_x(lv_obj_t * obj, int32_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_TRANSLATE_X, v, selector);
}
void lv_obj_set_style_translate_y(lv_obj_t * obj, int32_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_TRANSLATE_Y, v, selector);
}
void lv_obj_set_style_translate_radial(lv_obj_t * obj, int32_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_TRANSLATE_RADIAL, v, selector);
}
void lv_obj_set_style_transform_scale_x(lv_obj_t * obj, int32_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_TRANSFORM_SCALE_X, v, selector);
}
void lv_obj_set_style_transform_scale_y(lv_obj_t * obj, int32_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_TRANSFORM_SCALE_Y, v, selector);
}
void lv_obj_set_style_transform_rotation(lv_obj_t * obj, int32_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_TRANSFORM_ROTATION, v, selector);
}
void lv_obj_set_style_transform_pivot_x(lv_obj_t * obj, int32_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_TRANSFORM_PIVOT_X, v, selector);
}
void lv_obj_set_style_transform_pivot_y(lv_obj_t * obj, int32_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_TRANSFORM_PIVOT_Y, v, selector);
}
void lv_obj_set_style_transform_skew_x(lv_obj_t * obj, int32_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_TRANSFORM_SKEW_X, v, selector);
}
void lv_obj_set_style_transform_skew_y(lv_obj_t * obj, int32_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_TRANSFORM_SKEW_Y, v, selector);
}
void lv_obj_set_style_pad_top(lv_obj_t * obj, int32_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_PAD_TOP, v, selector);
}
void lv_obj_set_style_pad_bottom(lv_obj_t * obj, int32_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_PAD_BOTTOM, v, selector);
}
void lv_obj_set_style_pad_left(lv_obj_t * obj, int32_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_PAD_LEFT, v, selector);
}
void lv_obj_set_style_pad_right(lv_obj_t * obj, int32_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_PAD_RIGHT, v, selector);
}
void lv_obj_set_style_pad_row(lv_obj_t * obj, int32_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_PAD_ROW, v, selector);
}
void lv_obj_set_style_pad_column(lv_obj_t * obj, int32_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_PAD_COLUMN, v, selector);
}
void lv_obj_set_style_pad_radial(lv_obj_t * obj, int32_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_PAD_RADIAL, v, selector);
}
void lv_obj_set_style_margin_top(lv_obj_t * obj, int32_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_MARGIN_TOP, v, selector);
}
void lv_obj_set_style_margin_bottom(lv_obj_t * obj, int32_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_MARGIN_BOTTOM, v, selector);
}
void lv_obj_set_style_margin_left(lv_obj_t * obj, int32_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_MARGIN_LEFT, v, selector);
}
void lv_obj_set_style_margin_right(lv_obj_t * obj, int32_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_MARGIN_RIGHT, v, selector);
}
void lv_obj_set_style_bg_color(lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.color = value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_BG_COLOR, v, selector);
}
void lv_obj_set_style_bg_opa(lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_BG_OPA, v, selector);
}
void lv_obj_set_style_bg_grad_color(lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.color = value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_BG_GRAD_COLOR, v, selector);
}
void lv_obj_set_style_bg_grad_dir(lv_obj_t * obj, lv_grad_dir_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_BG_GRAD_DIR, v, selector);
}
void lv_obj_set_style_bg_main_stop(lv_obj_t * obj, int32_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_BG_MAIN_STOP, v, selector);
}
void lv_obj_set_style_bg_grad_stop(lv_obj_t * obj, int32_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_BG_GRAD_STOP, v, selector);
}
void lv_obj_set_style_bg_main_opa(lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_BG_MAIN_OPA, v, selector);
}
void lv_obj_set_style_bg_grad_opa(lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_BG_GRAD_OPA, v, selector);
}
void lv_obj_set_style_bg_grad(lv_obj_t * obj, const lv_grad_dsc_t * value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.ptr = value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_BG_GRAD, v, selector);
}
void lv_obj_set_style_bg_image_src(lv_obj_t * obj, const void * value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.ptr = value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_BG_IMAGE_SRC, v, selector);
}
void lv_obj_set_style_bg_image_opa(lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_BG_IMAGE_OPA, v, selector);
}
void lv_obj_set_style_bg_image_recolor(lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.color = value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_BG_IMAGE_RECOLOR, v, selector);
}
void lv_obj_set_style_bg_image_recolor_opa(lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_BG_IMAGE_RECOLOR_OPA, v, selector);
}
void lv_obj_set_style_bg_image_tiled(lv_obj_t * obj, bool value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_BG_IMAGE_TILED, v, selector);
}
void lv_obj_set_style_border_color(lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.color = value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_BORDER_COLOR, v, selector);
}
void lv_obj_set_style_border_opa(lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_BORDER_OPA, v, selector);
}
void lv_obj_set_style_border_width(lv_obj_t * obj, int32_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_BORDER_WIDTH, v, selector);
}
void lv_obj_set_style_border_side(lv_obj_t * obj, lv_border_side_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_BORDER_SIDE, v, selector);
}
void lv_obj_set_style_border_post(lv_obj_t * obj, bool value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_BORDER_POST, v, selector);
}
void lv_obj_set_style_outline_width(lv_obj_t * obj, int32_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_OUTLINE_WIDTH, v, selector);
}
void lv_obj_set_style_outline_color(lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.color = value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_OUTLINE_COLOR, v, selector);
}
void lv_obj_set_style_outline_opa(lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_OUTLINE_OPA, v, selector);
}
void lv_obj_set_style_outline_pad(lv_obj_t * obj, int32_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_OUTLINE_PAD, v, selector);
}
void lv_obj_set_style_shadow_width(lv_obj_t * obj, int32_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_SHADOW_WIDTH, v, selector);
}
void lv_obj_set_style_shadow_offset_x(lv_obj_t * obj, int32_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_SHADOW_OFFSET_X, v, selector);
}
void lv_obj_set_style_shadow_offset_y(lv_obj_t * obj, int32_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_SHADOW_OFFSET_Y, v, selector);
}
void lv_obj_set_style_shadow_spread(lv_obj_t * obj, int32_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_SHADOW_SPREAD, v, selector);
}
void lv_obj_set_style_shadow_color(lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.color = value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_SHADOW_COLOR, v, selector);
}
void lv_obj_set_style_shadow_opa(lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_SHADOW_OPA, v, selector);
}
void lv_obj_set_style_image_opa(lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_IMAGE_OPA, v, selector);
}
void lv_obj_set_style_image_recolor(lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.color = value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_IMAGE_RECOLOR, v, selector);
}
void lv_obj_set_style_image_recolor_opa(lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_IMAGE_RECOLOR_OPA, v, selector);
}
void lv_obj_set_style_line_width(lv_obj_t * obj, int32_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_LINE_WIDTH, v, selector);
}
void lv_obj_set_style_line_dash_width(lv_obj_t * obj, int32_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_LINE_DASH_WIDTH, v, selector);
}
void lv_obj_set_style_line_dash_gap(lv_obj_t * obj, int32_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_LINE_DASH_GAP, v, selector);
}
void lv_obj_set_style_line_rounded(lv_obj_t * obj, bool value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_LINE_ROUNDED, v, selector);
}
void lv_obj_set_style_line_color(lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.color = value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_LINE_COLOR, v, selector);
}
void lv_obj_set_style_line_opa(lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_LINE_OPA, v, selector);
}
void lv_obj_set_style_arc_width(lv_obj_t * obj, int32_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_ARC_WIDTH, v, selector);
}
void lv_obj_set_style_arc_rounded(lv_obj_t * obj, bool value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_ARC_ROUNDED, v, selector);
}
void lv_obj_set_style_arc_color(lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.color = value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_ARC_COLOR, v, selector);
}
void lv_obj_set_style_arc_opa(lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_ARC_OPA, v, selector);
}
void lv_obj_set_style_arc_image_src(lv_obj_t * obj, const void * value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.ptr = value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_ARC_IMAGE_SRC, v, selector);
}
void lv_obj_set_style_text_color(lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.color = value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_TEXT_COLOR, v, selector);
}
void lv_obj_set_style_text_opa(lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_TEXT_OPA, v, selector);
}
void lv_obj_set_style_text_font(lv_obj_t * obj, const lv_font_t * value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.ptr = value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_TEXT_FONT, v, selector);
}
void lv_obj_set_style_text_letter_space(lv_obj_t * obj, int32_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_TEXT_LETTER_SPACE, v, selector);
}
void lv_obj_set_style_text_line_space(lv_obj_t * obj, int32_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_TEXT_LINE_SPACE, v, selector);
}
void lv_obj_set_style_text_decor(lv_obj_t * obj, lv_text_decor_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_TEXT_DECOR, v, selector);
}
void lv_obj_set_style_text_align(lv_obj_t * obj, lv_text_align_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_TEXT_ALIGN, v, selector);
}
void lv_obj_set_style_text_outline_stroke_color(lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.color = value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_TEXT_OUTLINE_STROKE_COLOR, v, selector);
}
void lv_obj_set_style_text_outline_stroke_width(lv_obj_t * obj, int32_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_TEXT_OUTLINE_STROKE_WIDTH, v, selector);
}
void lv_obj_set_style_text_outline_stroke_opa(lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_TEXT_OUTLINE_STROKE_OPA, v, selector);
}
void lv_obj_set_style_radius(lv_obj_t * obj, int32_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_RADIUS, v, selector);
}
void lv_obj_set_style_radial_offset(lv_obj_t * obj, int32_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_RADIAL_OFFSET, v, selector);
}
void lv_obj_set_style_clip_corner(lv_obj_t * obj, bool value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_CLIP_CORNER, v, selector);
}
void lv_obj_set_style_opa(lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_OPA, v, selector);
}
void lv_obj_set_style_opa_layered(lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_OPA_LAYERED, v, selector);
}
void lv_obj_set_style_color_filter_dsc(lv_obj_t * obj, const lv_color_filter_dsc_t * value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.ptr = value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_COLOR_FILTER_DSC, v, selector);
}
void lv_obj_set_style_color_filter_opa(lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_COLOR_FILTER_OPA, v, selector);
}
void lv_obj_set_style_recolor(lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.color = value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_RECOLOR, v, selector);
}
void lv_obj_set_style_recolor_opa(lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_RECOLOR_OPA, v, selector);
}
void lv_obj_set_style_anim(lv_obj_t * obj, const lv_anim_t * value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.ptr = value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_ANIM, v, selector);
}
void lv_obj_set_style_anim_duration(lv_obj_t * obj, uint32_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_ANIM_DURATION, v, selector);
}
void lv_obj_set_style_transition(lv_obj_t * obj, const lv_style_transition_dsc_t * value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.ptr = value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_TRANSITION, v, selector);
}
void lv_obj_set_style_blend_mode(lv_obj_t * obj, lv_blend_mode_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_BLEND_MODE, v, selector);
}
void lv_obj_set_style_layout(lv_obj_t * obj, uint16_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_LAYOUT, v, selector);
}
void lv_obj_set_style_base_dir(lv_obj_t * obj, lv_base_dir_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_BASE_DIR, v, selector);
}
void lv_obj_set_style_bitmap_mask_src(lv_obj_t * obj, const void * value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.ptr = value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_BITMAP_MASK_SRC, v, selector);
}
void lv_obj_set_style_rotary_sensitivity(lv_obj_t * obj, uint32_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_ROTARY_SENSITIVITY, v, selector);
}
#if LV_USE_FLEX
void lv_obj_set_style_flex_flow(lv_obj_t * obj, lv_flex_flow_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_FLEX_FLOW, v, selector);
}
void lv_obj_set_style_flex_main_place(lv_obj_t * obj, lv_flex_align_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_FLEX_MAIN_PLACE, v, selector);
}
void lv_obj_set_style_flex_cross_place(lv_obj_t * obj, lv_flex_align_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_FLEX_CROSS_PLACE, v, selector);
}
void lv_obj_set_style_flex_track_place(lv_obj_t * obj, lv_flex_align_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_FLEX_TRACK_PLACE, v, selector);
}
void lv_obj_set_style_flex_grow(lv_obj_t * obj, uint8_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_FLEX_GROW, v, selector);
}
#endif /*LV_USE_FLEX*/
#if LV_USE_GRID
void lv_obj_set_style_grid_column_dsc_array(lv_obj_t * obj, const int32_t * value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.ptr = value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_GRID_COLUMN_DSC_ARRAY, v, selector);
}
void lv_obj_set_style_grid_column_align(lv_obj_t * obj, lv_grid_align_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_GRID_COLUMN_ALIGN, v, selector);
}
void lv_obj_set_style_grid_row_dsc_array(lv_obj_t * obj, const int32_t * value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.ptr = value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_GRID_ROW_DSC_ARRAY, v, selector);
}
void lv_obj_set_style_grid_row_align(lv_obj_t * obj, lv_grid_align_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_GRID_ROW_ALIGN, v, selector);
}
void lv_obj_set_style_grid_cell_column_pos(lv_obj_t * obj, int32_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_GRID_CELL_COLUMN_POS, v, selector);
}
void lv_obj_set_style_grid_cell_x_align(lv_obj_t * obj, lv_grid_align_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_GRID_CELL_X_ALIGN, v, selector);
}
void lv_obj_set_style_grid_cell_column_span(lv_obj_t * obj, int32_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_GRID_CELL_COLUMN_SPAN, v, selector);
}
void lv_obj_set_style_grid_cell_row_pos(lv_obj_t * obj, int32_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_GRID_CELL_ROW_POS, v, selector);
}
void lv_obj_set_style_grid_cell_y_align(lv_obj_t * obj, lv_grid_align_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_GRID_CELL_Y_ALIGN, v, selector);
}
void lv_obj_set_style_grid_cell_row_span(lv_obj_t * obj, int32_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_GRID_CELL_ROW_SPAN, v, selector);
}
#endif /*LV_USE_GRID*/
@@ -0,0 +1,931 @@
/*
**********************************************************************
* DO NOT EDIT
* This file is automatically generated by "style_api_gen.py"
**********************************************************************
*/
#ifndef LV_OBJ_STYLE_GEN_H
#define LV_OBJ_STYLE_GEN_H
#ifdef __cplusplus
extern "C" {
#endif
#include "../misc/lv_area.h"
#include "../misc/lv_style.h"
#include "../core/lv_obj_style.h"
#include "../misc/lv_types.h"
static inline int32_t lv_obj_get_style_width(const lv_obj_t * obj, lv_part_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_WIDTH);
return (int32_t)v.num;
}
static inline int32_t lv_obj_get_style_min_width(const lv_obj_t * obj, lv_part_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_MIN_WIDTH);
return (int32_t)v.num;
}
static inline int32_t lv_obj_get_style_max_width(const lv_obj_t * obj, lv_part_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_MAX_WIDTH);
return (int32_t)v.num;
}
static inline int32_t lv_obj_get_style_height(const lv_obj_t * obj, lv_part_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_HEIGHT);
return (int32_t)v.num;
}
static inline int32_t lv_obj_get_style_min_height(const lv_obj_t * obj, lv_part_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_MIN_HEIGHT);
return (int32_t)v.num;
}
static inline int32_t lv_obj_get_style_max_height(const lv_obj_t * obj, lv_part_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_MAX_HEIGHT);
return (int32_t)v.num;
}
static inline int32_t lv_obj_get_style_length(const lv_obj_t * obj, lv_part_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_LENGTH);
return (int32_t)v.num;
}
static inline int32_t lv_obj_get_style_x(const lv_obj_t * obj, lv_part_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_X);
return (int32_t)v.num;
}
static inline int32_t lv_obj_get_style_y(const lv_obj_t * obj, lv_part_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_Y);
return (int32_t)v.num;
}
static inline lv_align_t lv_obj_get_style_align(const lv_obj_t * obj, lv_part_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_ALIGN);
return (lv_align_t)v.num;
}
static inline int32_t lv_obj_get_style_transform_width(const lv_obj_t * obj, lv_part_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_TRANSFORM_WIDTH);
return (int32_t)v.num;
}
static inline int32_t lv_obj_get_style_transform_height(const lv_obj_t * obj, lv_part_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_TRANSFORM_HEIGHT);
return (int32_t)v.num;
}
static inline int32_t lv_obj_get_style_translate_x(const lv_obj_t * obj, lv_part_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_TRANSLATE_X);
return (int32_t)v.num;
}
static inline int32_t lv_obj_get_style_translate_y(const lv_obj_t * obj, lv_part_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_TRANSLATE_Y);
return (int32_t)v.num;
}
static inline int32_t lv_obj_get_style_translate_radial(const lv_obj_t * obj, lv_part_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_TRANSLATE_RADIAL);
return (int32_t)v.num;
}
static inline int32_t lv_obj_get_style_transform_scale_x(const lv_obj_t * obj, lv_part_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_TRANSFORM_SCALE_X);
return (int32_t)v.num;
}
static inline int32_t lv_obj_get_style_transform_scale_y(const lv_obj_t * obj, lv_part_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_TRANSFORM_SCALE_Y);
return (int32_t)v.num;
}
static inline int32_t lv_obj_get_style_transform_rotation(const lv_obj_t * obj, lv_part_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_TRANSFORM_ROTATION);
return (int32_t)v.num;
}
static inline int32_t lv_obj_get_style_transform_pivot_x(const lv_obj_t * obj, lv_part_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_TRANSFORM_PIVOT_X);
return (int32_t)v.num;
}
static inline int32_t lv_obj_get_style_transform_pivot_y(const lv_obj_t * obj, lv_part_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_TRANSFORM_PIVOT_Y);
return (int32_t)v.num;
}
static inline int32_t lv_obj_get_style_transform_skew_x(const lv_obj_t * obj, lv_part_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_TRANSFORM_SKEW_X);
return (int32_t)v.num;
}
static inline int32_t lv_obj_get_style_transform_skew_y(const lv_obj_t * obj, lv_part_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_TRANSFORM_SKEW_Y);
return (int32_t)v.num;
}
static inline int32_t lv_obj_get_style_pad_top(const lv_obj_t * obj, lv_part_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_PAD_TOP);
return (int32_t)v.num;
}
static inline int32_t lv_obj_get_style_pad_bottom(const lv_obj_t * obj, lv_part_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_PAD_BOTTOM);
return (int32_t)v.num;
}
static inline int32_t lv_obj_get_style_pad_left(const lv_obj_t * obj, lv_part_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_PAD_LEFT);
return (int32_t)v.num;
}
static inline int32_t lv_obj_get_style_pad_right(const lv_obj_t * obj, lv_part_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_PAD_RIGHT);
return (int32_t)v.num;
}
static inline int32_t lv_obj_get_style_pad_row(const lv_obj_t * obj, lv_part_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_PAD_ROW);
return (int32_t)v.num;
}
static inline int32_t lv_obj_get_style_pad_column(const lv_obj_t * obj, lv_part_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_PAD_COLUMN);
return (int32_t)v.num;
}
static inline int32_t lv_obj_get_style_pad_radial(const lv_obj_t * obj, lv_part_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_PAD_RADIAL);
return (int32_t)v.num;
}
static inline int32_t lv_obj_get_style_margin_top(const lv_obj_t * obj, lv_part_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_MARGIN_TOP);
return (int32_t)v.num;
}
static inline int32_t lv_obj_get_style_margin_bottom(const lv_obj_t * obj, lv_part_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_MARGIN_BOTTOM);
return (int32_t)v.num;
}
static inline int32_t lv_obj_get_style_margin_left(const lv_obj_t * obj, lv_part_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_MARGIN_LEFT);
return (int32_t)v.num;
}
static inline int32_t lv_obj_get_style_margin_right(const lv_obj_t * obj, lv_part_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_MARGIN_RIGHT);
return (int32_t)v.num;
}
static inline lv_color_t lv_obj_get_style_bg_color(const lv_obj_t * obj, lv_part_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_BG_COLOR);
return v.color;
}
static inline lv_color_t lv_obj_get_style_bg_color_filtered(const lv_obj_t * obj, lv_part_t part)
{
lv_style_value_t v = lv_obj_style_apply_color_filter(obj, part, lv_obj_get_style_prop(obj, part, LV_STYLE_BG_COLOR));
return v.color;
}
static inline lv_opa_t lv_obj_get_style_bg_opa(const lv_obj_t * obj, lv_part_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_BG_OPA);
return (lv_opa_t)v.num;
}
static inline lv_color_t lv_obj_get_style_bg_grad_color(const lv_obj_t * obj, lv_part_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_BG_GRAD_COLOR);
return v.color;
}
static inline lv_color_t lv_obj_get_style_bg_grad_color_filtered(const lv_obj_t * obj, lv_part_t part)
{
lv_style_value_t v = lv_obj_style_apply_color_filter(obj, part, lv_obj_get_style_prop(obj, part, LV_STYLE_BG_GRAD_COLOR));
return v.color;
}
static inline lv_grad_dir_t lv_obj_get_style_bg_grad_dir(const lv_obj_t * obj, lv_part_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_BG_GRAD_DIR);
return (lv_grad_dir_t)v.num;
}
static inline int32_t lv_obj_get_style_bg_main_stop(const lv_obj_t * obj, lv_part_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_BG_MAIN_STOP);
return (int32_t)v.num;
}
static inline int32_t lv_obj_get_style_bg_grad_stop(const lv_obj_t * obj, lv_part_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_BG_GRAD_STOP);
return (int32_t)v.num;
}
static inline lv_opa_t lv_obj_get_style_bg_main_opa(const lv_obj_t * obj, lv_part_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_BG_MAIN_OPA);
return (lv_opa_t)v.num;
}
static inline lv_opa_t lv_obj_get_style_bg_grad_opa(const lv_obj_t * obj, lv_part_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_BG_GRAD_OPA);
return (lv_opa_t)v.num;
}
static inline const lv_grad_dsc_t * lv_obj_get_style_bg_grad(const lv_obj_t * obj, lv_part_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_BG_GRAD);
return (const lv_grad_dsc_t *)v.ptr;
}
static inline const void * lv_obj_get_style_bg_image_src(const lv_obj_t * obj, lv_part_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_BG_IMAGE_SRC);
return (const void *)v.ptr;
}
static inline lv_opa_t lv_obj_get_style_bg_image_opa(const lv_obj_t * obj, lv_part_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_BG_IMAGE_OPA);
return (lv_opa_t)v.num;
}
static inline lv_color_t lv_obj_get_style_bg_image_recolor(const lv_obj_t * obj, lv_part_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_BG_IMAGE_RECOLOR);
return v.color;
}
static inline lv_color_t lv_obj_get_style_bg_image_recolor_filtered(const lv_obj_t * obj, lv_part_t part)
{
lv_style_value_t v = lv_obj_style_apply_color_filter(obj, part, lv_obj_get_style_prop(obj, part, LV_STYLE_BG_IMAGE_RECOLOR));
return v.color;
}
static inline lv_opa_t lv_obj_get_style_bg_image_recolor_opa(const lv_obj_t * obj, lv_part_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_BG_IMAGE_RECOLOR_OPA);
return (lv_opa_t)v.num;
}
static inline bool lv_obj_get_style_bg_image_tiled(const lv_obj_t * obj, lv_part_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_BG_IMAGE_TILED);
return (bool)v.num;
}
static inline lv_color_t lv_obj_get_style_border_color(const lv_obj_t * obj, lv_part_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_BORDER_COLOR);
return v.color;
}
static inline lv_color_t lv_obj_get_style_border_color_filtered(const lv_obj_t * obj, lv_part_t part)
{
lv_style_value_t v = lv_obj_style_apply_color_filter(obj, part, lv_obj_get_style_prop(obj, part, LV_STYLE_BORDER_COLOR));
return v.color;
}
static inline lv_opa_t lv_obj_get_style_border_opa(const lv_obj_t * obj, lv_part_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_BORDER_OPA);
return (lv_opa_t)v.num;
}
static inline int32_t lv_obj_get_style_border_width(const lv_obj_t * obj, lv_part_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_BORDER_WIDTH);
return (int32_t)v.num;
}
static inline lv_border_side_t lv_obj_get_style_border_side(const lv_obj_t * obj, lv_part_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_BORDER_SIDE);
return (lv_border_side_t)v.num;
}
static inline bool lv_obj_get_style_border_post(const lv_obj_t * obj, lv_part_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_BORDER_POST);
return (bool)v.num;
}
static inline int32_t lv_obj_get_style_outline_width(const lv_obj_t * obj, lv_part_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_OUTLINE_WIDTH);
return (int32_t)v.num;
}
static inline lv_color_t lv_obj_get_style_outline_color(const lv_obj_t * obj, lv_part_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_OUTLINE_COLOR);
return v.color;
}
static inline lv_color_t lv_obj_get_style_outline_color_filtered(const lv_obj_t * obj, lv_part_t part)
{
lv_style_value_t v = lv_obj_style_apply_color_filter(obj, part, lv_obj_get_style_prop(obj, part, LV_STYLE_OUTLINE_COLOR));
return v.color;
}
static inline lv_opa_t lv_obj_get_style_outline_opa(const lv_obj_t * obj, lv_part_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_OUTLINE_OPA);
return (lv_opa_t)v.num;
}
static inline int32_t lv_obj_get_style_outline_pad(const lv_obj_t * obj, lv_part_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_OUTLINE_PAD);
return (int32_t)v.num;
}
static inline int32_t lv_obj_get_style_shadow_width(const lv_obj_t * obj, lv_part_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_SHADOW_WIDTH);
return (int32_t)v.num;
}
static inline int32_t lv_obj_get_style_shadow_offset_x(const lv_obj_t * obj, lv_part_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_SHADOW_OFFSET_X);
return (int32_t)v.num;
}
static inline int32_t lv_obj_get_style_shadow_offset_y(const lv_obj_t * obj, lv_part_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_SHADOW_OFFSET_Y);
return (int32_t)v.num;
}
static inline int32_t lv_obj_get_style_shadow_spread(const lv_obj_t * obj, lv_part_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_SHADOW_SPREAD);
return (int32_t)v.num;
}
static inline lv_color_t lv_obj_get_style_shadow_color(const lv_obj_t * obj, lv_part_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_SHADOW_COLOR);
return v.color;
}
static inline lv_color_t lv_obj_get_style_shadow_color_filtered(const lv_obj_t * obj, lv_part_t part)
{
lv_style_value_t v = lv_obj_style_apply_color_filter(obj, part, lv_obj_get_style_prop(obj, part, LV_STYLE_SHADOW_COLOR));
return v.color;
}
static inline lv_opa_t lv_obj_get_style_shadow_opa(const lv_obj_t * obj, lv_part_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_SHADOW_OPA);
return (lv_opa_t)v.num;
}
static inline lv_opa_t lv_obj_get_style_image_opa(const lv_obj_t * obj, lv_part_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_IMAGE_OPA);
return (lv_opa_t)v.num;
}
static inline lv_color_t lv_obj_get_style_image_recolor(const lv_obj_t * obj, lv_part_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_IMAGE_RECOLOR);
return v.color;
}
static inline lv_color_t lv_obj_get_style_image_recolor_filtered(const lv_obj_t * obj, lv_part_t part)
{
lv_style_value_t v = lv_obj_style_apply_color_filter(obj, part, lv_obj_get_style_prop(obj, part, LV_STYLE_IMAGE_RECOLOR));
return v.color;
}
static inline lv_opa_t lv_obj_get_style_image_recolor_opa(const lv_obj_t * obj, lv_part_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_IMAGE_RECOLOR_OPA);
return (lv_opa_t)v.num;
}
static inline int32_t lv_obj_get_style_line_width(const lv_obj_t * obj, lv_part_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_LINE_WIDTH);
return (int32_t)v.num;
}
static inline int32_t lv_obj_get_style_line_dash_width(const lv_obj_t * obj, lv_part_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_LINE_DASH_WIDTH);
return (int32_t)v.num;
}
static inline int32_t lv_obj_get_style_line_dash_gap(const lv_obj_t * obj, lv_part_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_LINE_DASH_GAP);
return (int32_t)v.num;
}
static inline bool lv_obj_get_style_line_rounded(const lv_obj_t * obj, lv_part_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_LINE_ROUNDED);
return (bool)v.num;
}
static inline lv_color_t lv_obj_get_style_line_color(const lv_obj_t * obj, lv_part_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_LINE_COLOR);
return v.color;
}
static inline lv_color_t lv_obj_get_style_line_color_filtered(const lv_obj_t * obj, lv_part_t part)
{
lv_style_value_t v = lv_obj_style_apply_color_filter(obj, part, lv_obj_get_style_prop(obj, part, LV_STYLE_LINE_COLOR));
return v.color;
}
static inline lv_opa_t lv_obj_get_style_line_opa(const lv_obj_t * obj, lv_part_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_LINE_OPA);
return (lv_opa_t)v.num;
}
static inline int32_t lv_obj_get_style_arc_width(const lv_obj_t * obj, lv_part_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_ARC_WIDTH);
return (int32_t)v.num;
}
static inline bool lv_obj_get_style_arc_rounded(const lv_obj_t * obj, lv_part_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_ARC_ROUNDED);
return (bool)v.num;
}
static inline lv_color_t lv_obj_get_style_arc_color(const lv_obj_t * obj, lv_part_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_ARC_COLOR);
return v.color;
}
static inline lv_color_t lv_obj_get_style_arc_color_filtered(const lv_obj_t * obj, lv_part_t part)
{
lv_style_value_t v = lv_obj_style_apply_color_filter(obj, part, lv_obj_get_style_prop(obj, part, LV_STYLE_ARC_COLOR));
return v.color;
}
static inline lv_opa_t lv_obj_get_style_arc_opa(const lv_obj_t * obj, lv_part_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_ARC_OPA);
return (lv_opa_t)v.num;
}
static inline const void * lv_obj_get_style_arc_image_src(const lv_obj_t * obj, lv_part_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_ARC_IMAGE_SRC);
return (const void *)v.ptr;
}
static inline lv_color_t lv_obj_get_style_text_color(const lv_obj_t * obj, lv_part_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_TEXT_COLOR);
return v.color;
}
static inline lv_color_t lv_obj_get_style_text_color_filtered(const lv_obj_t * obj, lv_part_t part)
{
lv_style_value_t v = lv_obj_style_apply_color_filter(obj, part, lv_obj_get_style_prop(obj, part, LV_STYLE_TEXT_COLOR));
return v.color;
}
static inline lv_opa_t lv_obj_get_style_text_opa(const lv_obj_t * obj, lv_part_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_TEXT_OPA);
return (lv_opa_t)v.num;
}
static inline const lv_font_t * lv_obj_get_style_text_font(const lv_obj_t * obj, lv_part_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_TEXT_FONT);
return (const lv_font_t *)v.ptr;
}
static inline int32_t lv_obj_get_style_text_letter_space(const lv_obj_t * obj, lv_part_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_TEXT_LETTER_SPACE);
return (int32_t)v.num;
}
static inline int32_t lv_obj_get_style_text_line_space(const lv_obj_t * obj, lv_part_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_TEXT_LINE_SPACE);
return (int32_t)v.num;
}
static inline lv_text_decor_t lv_obj_get_style_text_decor(const lv_obj_t * obj, lv_part_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_TEXT_DECOR);
return (lv_text_decor_t)v.num;
}
static inline lv_text_align_t lv_obj_get_style_text_align(const lv_obj_t * obj, lv_part_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_TEXT_ALIGN);
return (lv_text_align_t)v.num;
}
static inline lv_color_t lv_obj_get_style_text_outline_stroke_color(const lv_obj_t * obj, lv_part_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_TEXT_OUTLINE_STROKE_COLOR);
return v.color;
}
static inline lv_color_t lv_obj_get_style_text_outline_stroke_color_filtered(const lv_obj_t * obj, lv_part_t part)
{
lv_style_value_t v = lv_obj_style_apply_color_filter(obj, part, lv_obj_get_style_prop(obj, part, LV_STYLE_TEXT_OUTLINE_STROKE_COLOR));
return v.color;
}
static inline int32_t lv_obj_get_style_text_outline_stroke_width(const lv_obj_t * obj, lv_part_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_TEXT_OUTLINE_STROKE_WIDTH);
return (int32_t)v.num;
}
static inline lv_opa_t lv_obj_get_style_text_outline_stroke_opa(const lv_obj_t * obj, lv_part_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_TEXT_OUTLINE_STROKE_OPA);
return (lv_opa_t)v.num;
}
static inline int32_t lv_obj_get_style_radius(const lv_obj_t * obj, lv_part_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_RADIUS);
return (int32_t)v.num;
}
static inline int32_t lv_obj_get_style_radial_offset(const lv_obj_t * obj, lv_part_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_RADIAL_OFFSET);
return (int32_t)v.num;
}
static inline bool lv_obj_get_style_clip_corner(const lv_obj_t * obj, lv_part_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_CLIP_CORNER);
return (bool)v.num;
}
static inline lv_opa_t lv_obj_get_style_opa(const lv_obj_t * obj, lv_part_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_OPA);
return (lv_opa_t)v.num;
}
static inline lv_opa_t lv_obj_get_style_opa_layered(const lv_obj_t * obj, lv_part_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_OPA_LAYERED);
return (lv_opa_t)v.num;
}
static inline const lv_color_filter_dsc_t * lv_obj_get_style_color_filter_dsc(const lv_obj_t * obj, lv_part_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_COLOR_FILTER_DSC);
return (const lv_color_filter_dsc_t *)v.ptr;
}
static inline lv_opa_t lv_obj_get_style_color_filter_opa(const lv_obj_t * obj, lv_part_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_COLOR_FILTER_OPA);
return (lv_opa_t)v.num;
}
static inline lv_color_t lv_obj_get_style_recolor(const lv_obj_t * obj, lv_part_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_RECOLOR);
return v.color;
}
static inline lv_opa_t lv_obj_get_style_recolor_opa(const lv_obj_t * obj, lv_part_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_RECOLOR_OPA);
return (lv_opa_t)v.num;
}
static inline const lv_anim_t * lv_obj_get_style_anim(const lv_obj_t * obj, lv_part_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_ANIM);
return (const lv_anim_t *)v.ptr;
}
static inline uint32_t lv_obj_get_style_anim_duration(const lv_obj_t * obj, lv_part_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_ANIM_DURATION);
return (uint32_t)v.num;
}
static inline const lv_style_transition_dsc_t * lv_obj_get_style_transition(const lv_obj_t * obj, lv_part_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_TRANSITION);
return (const lv_style_transition_dsc_t *)v.ptr;
}
static inline lv_blend_mode_t lv_obj_get_style_blend_mode(const lv_obj_t * obj, lv_part_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_BLEND_MODE);
return (lv_blend_mode_t)v.num;
}
static inline uint16_t lv_obj_get_style_layout(const lv_obj_t * obj, lv_part_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_LAYOUT);
return (uint16_t)v.num;
}
static inline lv_base_dir_t lv_obj_get_style_base_dir(const lv_obj_t * obj, lv_part_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_BASE_DIR);
return (lv_base_dir_t)v.num;
}
static inline const void * lv_obj_get_style_bitmap_mask_src(const lv_obj_t * obj, lv_part_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_BITMAP_MASK_SRC);
return (const void *)v.ptr;
}
static inline uint32_t lv_obj_get_style_rotary_sensitivity(const lv_obj_t * obj, lv_part_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_ROTARY_SENSITIVITY);
return (uint32_t)v.num;
}
#if LV_USE_FLEX
static inline lv_flex_flow_t lv_obj_get_style_flex_flow(const lv_obj_t * obj, lv_part_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_FLEX_FLOW);
return (lv_flex_flow_t)v.num;
}
static inline lv_flex_align_t lv_obj_get_style_flex_main_place(const lv_obj_t * obj, lv_part_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_FLEX_MAIN_PLACE);
return (lv_flex_align_t)v.num;
}
static inline lv_flex_align_t lv_obj_get_style_flex_cross_place(const lv_obj_t * obj, lv_part_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_FLEX_CROSS_PLACE);
return (lv_flex_align_t)v.num;
}
static inline lv_flex_align_t lv_obj_get_style_flex_track_place(const lv_obj_t * obj, lv_part_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_FLEX_TRACK_PLACE);
return (lv_flex_align_t)v.num;
}
static inline uint8_t lv_obj_get_style_flex_grow(const lv_obj_t * obj, lv_part_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_FLEX_GROW);
return (uint8_t)v.num;
}
#endif /*LV_USE_FLEX*/
#if LV_USE_GRID
static inline const int32_t * lv_obj_get_style_grid_column_dsc_array(const lv_obj_t * obj, lv_part_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_GRID_COLUMN_DSC_ARRAY);
return (const int32_t *)v.ptr;
}
static inline lv_grid_align_t lv_obj_get_style_grid_column_align(const lv_obj_t * obj, lv_part_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_GRID_COLUMN_ALIGN);
return (lv_grid_align_t)v.num;
}
static inline const int32_t * lv_obj_get_style_grid_row_dsc_array(const lv_obj_t * obj, lv_part_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_GRID_ROW_DSC_ARRAY);
return (const int32_t *)v.ptr;
}
static inline lv_grid_align_t lv_obj_get_style_grid_row_align(const lv_obj_t * obj, lv_part_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_GRID_ROW_ALIGN);
return (lv_grid_align_t)v.num;
}
static inline int32_t lv_obj_get_style_grid_cell_column_pos(const lv_obj_t * obj, lv_part_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_GRID_CELL_COLUMN_POS);
return (int32_t)v.num;
}
static inline lv_grid_align_t lv_obj_get_style_grid_cell_x_align(const lv_obj_t * obj, lv_part_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_GRID_CELL_X_ALIGN);
return (lv_grid_align_t)v.num;
}
static inline int32_t lv_obj_get_style_grid_cell_column_span(const lv_obj_t * obj, lv_part_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_GRID_CELL_COLUMN_SPAN);
return (int32_t)v.num;
}
static inline int32_t lv_obj_get_style_grid_cell_row_pos(const lv_obj_t * obj, lv_part_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_GRID_CELL_ROW_POS);
return (int32_t)v.num;
}
static inline lv_grid_align_t lv_obj_get_style_grid_cell_y_align(const lv_obj_t * obj, lv_part_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_GRID_CELL_Y_ALIGN);
return (lv_grid_align_t)v.num;
}
static inline int32_t lv_obj_get_style_grid_cell_row_span(const lv_obj_t * obj, lv_part_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_GRID_CELL_ROW_SPAN);
return (int32_t)v.num;
}
#endif /*LV_USE_GRID*/
void lv_obj_set_style_width(lv_obj_t * obj, int32_t value, lv_style_selector_t selector);
void lv_obj_set_style_min_width(lv_obj_t * obj, int32_t value, lv_style_selector_t selector);
void lv_obj_set_style_max_width(lv_obj_t * obj, int32_t value, lv_style_selector_t selector);
void lv_obj_set_style_height(lv_obj_t * obj, int32_t value, lv_style_selector_t selector);
void lv_obj_set_style_min_height(lv_obj_t * obj, int32_t value, lv_style_selector_t selector);
void lv_obj_set_style_max_height(lv_obj_t * obj, int32_t value, lv_style_selector_t selector);
void lv_obj_set_style_length(lv_obj_t * obj, int32_t value, lv_style_selector_t selector);
void lv_obj_set_style_x(lv_obj_t * obj, int32_t value, lv_style_selector_t selector);
void lv_obj_set_style_y(lv_obj_t * obj, int32_t value, lv_style_selector_t selector);
void lv_obj_set_style_align(lv_obj_t * obj, lv_align_t value, lv_style_selector_t selector);
void lv_obj_set_style_transform_width(lv_obj_t * obj, int32_t value, lv_style_selector_t selector);
void lv_obj_set_style_transform_height(lv_obj_t * obj, int32_t value, lv_style_selector_t selector);
void lv_obj_set_style_translate_x(lv_obj_t * obj, int32_t value, lv_style_selector_t selector);
void lv_obj_set_style_translate_y(lv_obj_t * obj, int32_t value, lv_style_selector_t selector);
void lv_obj_set_style_translate_radial(lv_obj_t * obj, int32_t value, lv_style_selector_t selector);
void lv_obj_set_style_transform_scale_x(lv_obj_t * obj, int32_t value, lv_style_selector_t selector);
void lv_obj_set_style_transform_scale_y(lv_obj_t * obj, int32_t value, lv_style_selector_t selector);
void lv_obj_set_style_transform_rotation(lv_obj_t * obj, int32_t value, lv_style_selector_t selector);
void lv_obj_set_style_transform_pivot_x(lv_obj_t * obj, int32_t value, lv_style_selector_t selector);
void lv_obj_set_style_transform_pivot_y(lv_obj_t * obj, int32_t value, lv_style_selector_t selector);
void lv_obj_set_style_transform_skew_x(lv_obj_t * obj, int32_t value, lv_style_selector_t selector);
void lv_obj_set_style_transform_skew_y(lv_obj_t * obj, int32_t value, lv_style_selector_t selector);
void lv_obj_set_style_pad_top(lv_obj_t * obj, int32_t value, lv_style_selector_t selector);
void lv_obj_set_style_pad_bottom(lv_obj_t * obj, int32_t value, lv_style_selector_t selector);
void lv_obj_set_style_pad_left(lv_obj_t * obj, int32_t value, lv_style_selector_t selector);
void lv_obj_set_style_pad_right(lv_obj_t * obj, int32_t value, lv_style_selector_t selector);
void lv_obj_set_style_pad_row(lv_obj_t * obj, int32_t value, lv_style_selector_t selector);
void lv_obj_set_style_pad_column(lv_obj_t * obj, int32_t value, lv_style_selector_t selector);
void lv_obj_set_style_pad_radial(lv_obj_t * obj, int32_t value, lv_style_selector_t selector);
void lv_obj_set_style_margin_top(lv_obj_t * obj, int32_t value, lv_style_selector_t selector);
void lv_obj_set_style_margin_bottom(lv_obj_t * obj, int32_t value, lv_style_selector_t selector);
void lv_obj_set_style_margin_left(lv_obj_t * obj, int32_t value, lv_style_selector_t selector);
void lv_obj_set_style_margin_right(lv_obj_t * obj, int32_t value, lv_style_selector_t selector);
void lv_obj_set_style_bg_color(lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector);
void lv_obj_set_style_bg_opa(lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector);
void lv_obj_set_style_bg_grad_color(lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector);
void lv_obj_set_style_bg_grad_dir(lv_obj_t * obj, lv_grad_dir_t value, lv_style_selector_t selector);
void lv_obj_set_style_bg_main_stop(lv_obj_t * obj, int32_t value, lv_style_selector_t selector);
void lv_obj_set_style_bg_grad_stop(lv_obj_t * obj, int32_t value, lv_style_selector_t selector);
void lv_obj_set_style_bg_main_opa(lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector);
void lv_obj_set_style_bg_grad_opa(lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector);
void lv_obj_set_style_bg_grad(lv_obj_t * obj, const lv_grad_dsc_t * value, lv_style_selector_t selector);
void lv_obj_set_style_bg_image_src(lv_obj_t * obj, const void * value, lv_style_selector_t selector);
void lv_obj_set_style_bg_image_opa(lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector);
void lv_obj_set_style_bg_image_recolor(lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector);
void lv_obj_set_style_bg_image_recolor_opa(lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector);
void lv_obj_set_style_bg_image_tiled(lv_obj_t * obj, bool value, lv_style_selector_t selector);
void lv_obj_set_style_border_color(lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector);
void lv_obj_set_style_border_opa(lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector);
void lv_obj_set_style_border_width(lv_obj_t * obj, int32_t value, lv_style_selector_t selector);
void lv_obj_set_style_border_side(lv_obj_t * obj, lv_border_side_t value, lv_style_selector_t selector);
void lv_obj_set_style_border_post(lv_obj_t * obj, bool value, lv_style_selector_t selector);
void lv_obj_set_style_outline_width(lv_obj_t * obj, int32_t value, lv_style_selector_t selector);
void lv_obj_set_style_outline_color(lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector);
void lv_obj_set_style_outline_opa(lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector);
void lv_obj_set_style_outline_pad(lv_obj_t * obj, int32_t value, lv_style_selector_t selector);
void lv_obj_set_style_shadow_width(lv_obj_t * obj, int32_t value, lv_style_selector_t selector);
void lv_obj_set_style_shadow_offset_x(lv_obj_t * obj, int32_t value, lv_style_selector_t selector);
void lv_obj_set_style_shadow_offset_y(lv_obj_t * obj, int32_t value, lv_style_selector_t selector);
void lv_obj_set_style_shadow_spread(lv_obj_t * obj, int32_t value, lv_style_selector_t selector);
void lv_obj_set_style_shadow_color(lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector);
void lv_obj_set_style_shadow_opa(lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector);
void lv_obj_set_style_image_opa(lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector);
void lv_obj_set_style_image_recolor(lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector);
void lv_obj_set_style_image_recolor_opa(lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector);
void lv_obj_set_style_line_width(lv_obj_t * obj, int32_t value, lv_style_selector_t selector);
void lv_obj_set_style_line_dash_width(lv_obj_t * obj, int32_t value, lv_style_selector_t selector);
void lv_obj_set_style_line_dash_gap(lv_obj_t * obj, int32_t value, lv_style_selector_t selector);
void lv_obj_set_style_line_rounded(lv_obj_t * obj, bool value, lv_style_selector_t selector);
void lv_obj_set_style_line_color(lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector);
void lv_obj_set_style_line_opa(lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector);
void lv_obj_set_style_arc_width(lv_obj_t * obj, int32_t value, lv_style_selector_t selector);
void lv_obj_set_style_arc_rounded(lv_obj_t * obj, bool value, lv_style_selector_t selector);
void lv_obj_set_style_arc_color(lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector);
void lv_obj_set_style_arc_opa(lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector);
void lv_obj_set_style_arc_image_src(lv_obj_t * obj, const void * value, lv_style_selector_t selector);
void lv_obj_set_style_text_color(lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector);
void lv_obj_set_style_text_opa(lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector);
void lv_obj_set_style_text_font(lv_obj_t * obj, const lv_font_t * value, lv_style_selector_t selector);
void lv_obj_set_style_text_letter_space(lv_obj_t * obj, int32_t value, lv_style_selector_t selector);
void lv_obj_set_style_text_line_space(lv_obj_t * obj, int32_t value, lv_style_selector_t selector);
void lv_obj_set_style_text_decor(lv_obj_t * obj, lv_text_decor_t value, lv_style_selector_t selector);
void lv_obj_set_style_text_align(lv_obj_t * obj, lv_text_align_t value, lv_style_selector_t selector);
void lv_obj_set_style_text_outline_stroke_color(lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector);
void lv_obj_set_style_text_outline_stroke_width(lv_obj_t * obj, int32_t value, lv_style_selector_t selector);
void lv_obj_set_style_text_outline_stroke_opa(lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector);
void lv_obj_set_style_radius(lv_obj_t * obj, int32_t value, lv_style_selector_t selector);
void lv_obj_set_style_radial_offset(lv_obj_t * obj, int32_t value, lv_style_selector_t selector);
void lv_obj_set_style_clip_corner(lv_obj_t * obj, bool value, lv_style_selector_t selector);
void lv_obj_set_style_opa(lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector);
void lv_obj_set_style_opa_layered(lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector);
void lv_obj_set_style_color_filter_dsc(lv_obj_t * obj, const lv_color_filter_dsc_t * value, lv_style_selector_t selector);
void lv_obj_set_style_color_filter_opa(lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector);
void lv_obj_set_style_recolor(lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector);
void lv_obj_set_style_recolor_opa(lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector);
void lv_obj_set_style_anim(lv_obj_t * obj, const lv_anim_t * value, lv_style_selector_t selector);
void lv_obj_set_style_anim_duration(lv_obj_t * obj, uint32_t value, lv_style_selector_t selector);
void lv_obj_set_style_transition(lv_obj_t * obj, const lv_style_transition_dsc_t * value, lv_style_selector_t selector);
void lv_obj_set_style_blend_mode(lv_obj_t * obj, lv_blend_mode_t value, lv_style_selector_t selector);
void lv_obj_set_style_layout(lv_obj_t * obj, uint16_t value, lv_style_selector_t selector);
void lv_obj_set_style_base_dir(lv_obj_t * obj, lv_base_dir_t value, lv_style_selector_t selector);
void lv_obj_set_style_bitmap_mask_src(lv_obj_t * obj, const void * value, lv_style_selector_t selector);
void lv_obj_set_style_rotary_sensitivity(lv_obj_t * obj, uint32_t value, lv_style_selector_t selector);
#if LV_USE_FLEX
void lv_obj_set_style_flex_flow(lv_obj_t * obj, lv_flex_flow_t value, lv_style_selector_t selector);
void lv_obj_set_style_flex_main_place(lv_obj_t * obj, lv_flex_align_t value, lv_style_selector_t selector);
void lv_obj_set_style_flex_cross_place(lv_obj_t * obj, lv_flex_align_t value, lv_style_selector_t selector);
void lv_obj_set_style_flex_track_place(lv_obj_t * obj, lv_flex_align_t value, lv_style_selector_t selector);
void lv_obj_set_style_flex_grow(lv_obj_t * obj, uint8_t value, lv_style_selector_t selector);
#endif /*LV_USE_FLEX*/
#if LV_USE_GRID
void lv_obj_set_style_grid_column_dsc_array(lv_obj_t * obj, const int32_t * value, lv_style_selector_t selector);
void lv_obj_set_style_grid_column_align(lv_obj_t * obj, lv_grid_align_t value, lv_style_selector_t selector);
void lv_obj_set_style_grid_row_dsc_array(lv_obj_t * obj, const int32_t * value, lv_style_selector_t selector);
void lv_obj_set_style_grid_row_align(lv_obj_t * obj, lv_grid_align_t value, lv_style_selector_t selector);
void lv_obj_set_style_grid_cell_column_pos(lv_obj_t * obj, int32_t value, lv_style_selector_t selector);
void lv_obj_set_style_grid_cell_x_align(lv_obj_t * obj, lv_grid_align_t value, lv_style_selector_t selector);
void lv_obj_set_style_grid_cell_column_span(lv_obj_t * obj, int32_t value, lv_style_selector_t selector);
void lv_obj_set_style_grid_cell_row_pos(lv_obj_t * obj, int32_t value, lv_style_selector_t selector);
void lv_obj_set_style_grid_cell_y_align(lv_obj_t * obj, lv_grid_align_t value, lv_style_selector_t selector);
void lv_obj_set_style_grid_cell_row_span(lv_obj_t * obj, int32_t value, lv_style_selector_t selector);
#endif /*LV_USE_GRID*/
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /* LV_OBJ_STYLE_GEN_H */
@@ -0,0 +1,95 @@
/**
* @file lv_obj_style_private.h
*
*/
#ifndef LV_OBJ_STYLE_PRIVATE_H
#define LV_OBJ_STYLE_PRIVATE_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "lv_obj_style.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
struct _lv_obj_style_t {
const lv_style_t * style;
uint32_t selector : 24;
uint32_t is_local : 1;
uint32_t is_trans : 1;
};
struct _lv_obj_style_transition_dsc_t {
uint16_t time;
uint16_t delay;
lv_style_selector_t selector;
lv_style_prop_t prop;
lv_anim_path_cb_t path_cb;
void * user_data;
};
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Initialize the object related style manager module.
* Called by LVGL in `lv_init()`
*/
void lv_obj_style_init(void);
/**
* Deinitialize the object related style manager module.
* Called by LVGL in `lv_deinit()`
*/
void lv_obj_style_deinit(void);
/**
* Used internally to create a style transition
* @param obj
* @param part
* @param prev_state
* @param new_state
* @param tr
*/
void lv_obj_style_create_transition(lv_obj_t * obj, lv_part_t part, lv_state_t prev_state,
lv_state_t new_state, const lv_obj_style_transition_dsc_t * tr);
/**
* Used internally to compare the appearance of an object in 2 states
* @param obj
* @param state1
* @param state2
* @return
*/
lv_style_state_cmp_t lv_obj_style_state_compare(lv_obj_t * obj, lv_state_t state1, lv_state_t state2);
/**
* Update the layer type of a widget bayed on its current styles.
* The result will be stored in `obj->spec_attr->layer_type`
* @param obj the object whose layer should be updated
*/
void lv_obj_update_layer_type(lv_obj_t * obj);
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_OBJ_STYLE_PRIVATE_H*/
@@ -0,0 +1,845 @@
/**
* @file lv_obj_tree.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_obj_private.h"
#include "lv_obj_class_private.h"
#include "../indev/lv_indev.h"
#include "../indev/lv_indev_private.h"
#include "../display/lv_display.h"
#include "../display/lv_display_private.h"
#include "../misc/lv_anim_private.h"
#include "../misc/lv_async.h"
#include "../core/lv_global.h"
/*********************
* DEFINES
*********************/
#define MY_CLASS (&lv_obj_class)
#define disp_ll_p &(LV_GLOBAL_DEFAULT()->disp_ll)
#define OBJ_DUMP_STRING_LEN 128
#define LV_OBJ_NAME_MAX_LEN 128
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static void lv_obj_delete_async_cb(void * obj);
static void obj_delete_core(lv_obj_t * obj);
static lv_obj_tree_walk_res_t walk_core(lv_obj_t * obj, lv_obj_tree_walk_cb_t cb, void * user_data);
static void dump_tree_core(lv_obj_t * obj, int32_t depth);
static lv_obj_t * lv_obj_get_first_not_deleting_child(lv_obj_t * obj);
#if LV_USE_OBJ_NAME
static lv_obj_t * find_by_name_direct(const lv_obj_t * parent, const char * name, size_t len);
#endif /*LV_USE_OBJ_NAME*/
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
void lv_obj_delete(lv_obj_t * obj)
{
if(obj->is_deleting)
return;
LV_LOG_TRACE("begin (delete %p)", (void *)obj);
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_obj_invalidate(obj);
lv_obj_t * par = lv_obj_get_parent(obj);
lv_display_t * disp = NULL;
bool act_screen_del = false;
if(par == NULL) {
disp = lv_obj_get_display(obj);
if(!disp) return; /*Shouldn't happen*/
if(disp->act_scr == obj) act_screen_del = true;
}
obj_delete_core(obj);
/*Call the ancestor's event handler to the parent to notify it about the child delete*/
if(par && !par->is_deleting) {
lv_obj_scrollbar_invalidate(par);
lv_obj_send_event(par, LV_EVENT_CHILD_CHANGED, NULL);
lv_obj_send_event(par, LV_EVENT_CHILD_DELETED, NULL);
}
/*Handle if the active screen was deleted*/
if(act_screen_del) {
LV_LOG_WARN("the active screen was deleted");
disp->act_scr = NULL;
}
LV_ASSERT_MEM_INTEGRITY();
LV_LOG_TRACE("finished (delete %p)", (void *)obj);
}
void lv_obj_clean(lv_obj_t * obj)
{
LV_LOG_TRACE("begin (clean %p)", (void *)obj);
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_obj_invalidate(obj);
uint32_t cnt = lv_obj_get_child_count(obj);
lv_obj_t * child = lv_obj_get_first_not_deleting_child(obj);
while(child) {
obj_delete_core(child);
child = lv_obj_get_first_not_deleting_child(obj);
}
/*Just to remove scroll animations if any*/
lv_obj_scroll_to(obj, 0, 0, LV_ANIM_OFF);
if(obj->spec_attr) {
obj->spec_attr->scroll.x = 0;
obj->spec_attr->scroll.y = 0;
}
if(lv_obj_get_child_count(obj) < cnt) {
lv_obj_send_event(obj, LV_EVENT_CHILD_CHANGED, NULL);
lv_obj_send_event(obj, LV_EVENT_CHILD_DELETED, NULL);
}
LV_ASSERT_MEM_INTEGRITY();
LV_LOG_TRACE("finished (clean %p)", (void *)obj);
}
void lv_obj_delete_delayed(lv_obj_t * obj, uint32_t delay_ms)
{
lv_anim_t a;
lv_anim_init(&a);
lv_anim_set_var(&a, obj);
lv_anim_set_exec_cb(&a, NULL);
lv_anim_set_duration(&a, 1);
lv_anim_set_delay(&a, delay_ms);
lv_anim_set_completed_cb(&a, lv_obj_delete_anim_completed_cb);
lv_anim_start(&a);
}
void lv_obj_delete_anim_completed_cb(lv_anim_t * a)
{
lv_obj_delete(a->var);
}
void lv_obj_delete_async(lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_async_call(lv_obj_delete_async_cb, obj);
}
void lv_obj_set_parent(lv_obj_t * obj, lv_obj_t * parent)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
LV_ASSERT_OBJ(parent, MY_CLASS);
if(obj->parent == NULL) {
LV_LOG_WARN("Can't set the parent of a screen");
return;
}
if(parent == NULL) {
LV_LOG_WARN("Can't set parent == NULL to an object");
return;
}
if(parent == obj->parent) {
return;
}
lv_obj_invalidate(obj);
lv_obj_allocate_spec_attr(parent);
lv_obj_t * old_parent = obj->parent;
/*Remove the object from the old parent's child list*/
int32_t i;
for(i = lv_obj_get_index(obj); i <= (int32_t)lv_obj_get_child_count(old_parent) - 2; i++) {
old_parent->spec_attr->children[i] = old_parent->spec_attr->children[i + 1];
}
old_parent->spec_attr->child_cnt--;
if(old_parent->spec_attr->child_cnt) {
old_parent->spec_attr->children = lv_realloc(old_parent->spec_attr->children,
old_parent->spec_attr->child_cnt * (sizeof(lv_obj_t *)));
}
else {
lv_free(old_parent->spec_attr->children);
old_parent->spec_attr->children = NULL;
}
/*Add the child to the new parent as the last (newest child)*/
parent->spec_attr->child_cnt++;
parent->spec_attr->children = lv_realloc(parent->spec_attr->children,
parent->spec_attr->child_cnt * (sizeof(lv_obj_t *)));
parent->spec_attr->children[lv_obj_get_child_count(parent) - 1] = obj;
obj->parent = parent;
/*Notify the original parent because one of its children is lost*/
lv_obj_scrollbar_invalidate(old_parent);
lv_obj_send_event(old_parent, LV_EVENT_CHILD_CHANGED, obj);
lv_obj_send_event(old_parent, LV_EVENT_CHILD_DELETED, NULL);
/*Notify the new parent about the child*/
lv_obj_send_event(parent, LV_EVENT_CHILD_CHANGED, obj);
lv_obj_send_event(parent, LV_EVENT_CHILD_CREATED, NULL);
lv_obj_mark_layout_as_dirty(obj);
lv_obj_invalidate(obj);
}
void lv_obj_move_to_index(lv_obj_t * obj, int32_t index)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
/* Check parent validity */
lv_obj_t * parent = lv_obj_get_parent(obj);
if(!parent) {
LV_LOG_WARN("parent is NULL");
return;
}
const uint32_t parent_child_count = lv_obj_get_child_count(parent);
/* old_index only can be 0 or greater, this point cannot be reached if the parent is not null */
const int32_t old_index = lv_obj_get_index(obj);
LV_ASSERT(0 <= old_index);
if(index < 0) {
index += parent_child_count;
}
/* Index was negative and the absolute value is greater than parent child count */
if((index < 0)
/* Index is same or bigger than parent child count */
|| (index >= (int32_t) parent_child_count)
/* If both previous and new index are the same */
|| (index == old_index)) {
return;
}
int32_t i = old_index;
if(index < old_index) {
while(i > index) {
parent->spec_attr->children[i] = parent->spec_attr->children[i - 1];
i--;
}
}
else {
while(i < index) {
parent->spec_attr->children[i] = parent->spec_attr->children[i + 1];
i++;
}
}
parent->spec_attr->children[index] = obj;
lv_obj_send_event(parent, LV_EVENT_CHILD_CHANGED, NULL);
lv_obj_invalidate(parent);
}
void lv_obj_swap(lv_obj_t * obj1, lv_obj_t * obj2)
{
LV_ASSERT_OBJ(obj1, MY_CLASS);
LV_ASSERT_OBJ(obj2, MY_CLASS);
lv_obj_t * parent = lv_obj_get_parent(obj1);
lv_obj_t * parent2 = lv_obj_get_parent(obj2);
uint_fast32_t index1 = lv_obj_get_index(obj1);
uint_fast32_t index2 = lv_obj_get_index(obj2);
lv_obj_send_event(parent2, LV_EVENT_CHILD_DELETED, obj2);
lv_obj_send_event(parent, LV_EVENT_CHILD_DELETED, obj1);
parent->spec_attr->children[index1] = obj2;
obj2->parent = parent;
parent2->spec_attr->children[index2] = obj1;
obj1->parent = parent2;
lv_obj_send_event(parent, LV_EVENT_CHILD_CHANGED, obj2);
lv_obj_send_event(parent, LV_EVENT_CHILD_CREATED, obj2);
lv_obj_send_event(parent2, LV_EVENT_CHILD_CHANGED, obj1);
lv_obj_send_event(parent2, LV_EVENT_CHILD_CREATED, obj1);
lv_obj_invalidate(parent);
if(parent != parent2) {
lv_obj_invalidate(parent2);
}
lv_group_swap_obj(obj1, obj2);
}
lv_obj_t * lv_obj_get_screen(const lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
const lv_obj_t * par = obj;
const lv_obj_t * act_par;
do {
act_par = par;
par = lv_obj_get_parent(act_par);
} while(par != NULL);
return (lv_obj_t *)act_par;
}
lv_display_t * lv_obj_get_display(const lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
const lv_obj_t * scr;
if(obj->parent == NULL) scr = obj; /*`obj` is a screen*/
else scr = lv_obj_get_screen(obj); /*get the screen of `obj`*/
lv_display_t * d;
lv_ll_t * disp_head = disp_ll_p;
LV_LL_READ(disp_head, d) {
uint32_t i;
for(i = 0; i < d->screen_cnt; i++) {
if(d->screens[i] == scr) return d;
}
}
LV_LOG_WARN("No screen found");
return NULL;
}
lv_obj_t * lv_obj_get_parent(const lv_obj_t * obj)
{
if(obj == NULL) return NULL;
LV_ASSERT_OBJ(obj, MY_CLASS);
return obj->parent;
}
lv_obj_t * lv_obj_get_child(const lv_obj_t * obj, int32_t idx)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
if(obj->spec_attr == NULL) return NULL;
uint32_t idu;
if(idx < 0) {
idx = obj->spec_attr->child_cnt + idx;
if(idx < 0) return NULL;
idu = (uint32_t) idx;
}
else {
idu = idx;
}
if(idu >= obj->spec_attr->child_cnt) return NULL;
else return obj->spec_attr->children[idx];
}
lv_obj_t * lv_obj_get_child_by_type(const lv_obj_t * obj, int32_t idx, const lv_obj_class_t * class_p)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
if(obj->spec_attr == NULL) return NULL;
int32_t i;
int32_t cnt = (int32_t)obj->spec_attr->child_cnt;
if(idx >= 0) {
for(i = 0; i < cnt; i++) {
if(obj->spec_attr->children[i]->class_p == class_p) {
if(idx == 0) return obj->spec_attr->children[i];
else idx--;
}
}
}
else {
idx++; /*-1 means the first child*/
for(i = cnt - 1; i >= 0; i--) {
if(obj->spec_attr->children[i]->class_p == class_p) {
if(idx == 0) return obj->spec_attr->children[i];
else idx++;
}
}
}
return NULL;
}
lv_obj_t * lv_obj_get_sibling(const lv_obj_t * obj, int32_t idx)
{
lv_obj_t * parent = lv_obj_get_parent(obj);
int32_t sibling_idx = (int32_t)lv_obj_get_index(obj) + idx;
if(sibling_idx < 0) return NULL;
return lv_obj_get_child(parent, sibling_idx);
}
lv_obj_t * lv_obj_get_sibling_by_type(const lv_obj_t * obj, int32_t idx, const lv_obj_class_t * class_p)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_obj_t * parent = lv_obj_get_parent(obj);
int32_t sibling_idx = (int32_t)lv_obj_get_index_by_type(obj, class_p) + idx;
if(sibling_idx < 0) return NULL;
return lv_obj_get_child_by_type(parent, sibling_idx, class_p);
}
uint32_t lv_obj_get_child_count(const lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
if(obj->spec_attr == NULL) return 0;
return obj->spec_attr->child_cnt;
}
uint32_t lv_obj_get_child_count_by_type(const lv_obj_t * obj, const lv_obj_class_t * class_p)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
if(obj->spec_attr == NULL) return 0;
uint32_t i;
uint32_t cnt = 0;
for(i = 0; i < obj->spec_attr->child_cnt; i++) {
if(obj->spec_attr->children[i]->class_p == class_p) cnt++;
}
return cnt;
}
#if LV_USE_OBJ_NAME
void lv_obj_set_name(lv_obj_t * obj, const char * name)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_obj_allocate_spec_attr(obj);
if(!obj->spec_attr->name_static && obj->spec_attr->name) lv_free((void *)obj->spec_attr->name);
if(name == NULL) {
obj->spec_attr->name = NULL;
obj->spec_attr->name_static = 1;
}
else {
obj->spec_attr->name = lv_strdup(name);
obj->spec_attr->name_static = 0;
}
}
void lv_obj_set_name_static(lv_obj_t * obj, const char * name)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_obj_allocate_spec_attr(obj);
if(!obj->spec_attr->name_static && obj->spec_attr->name) lv_free((void *)obj->spec_attr->name);
obj->spec_attr->name = name;
obj->spec_attr->name_static = 1;
}
const char * lv_obj_get_name(const lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
if(obj->spec_attr == NULL) return NULL;
else return obj->spec_attr->name;
}
void lv_obj_get_name_resolved(const lv_obj_t * obj, char buf[], size_t buf_size)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
const char * name = lv_obj_get_name(obj);
/*Use a default name which auto-indexing*/
char name_buf[LV_OBJ_NAME_MAX_LEN];
if(name == NULL) {
lv_snprintf(name_buf, sizeof(name_buf), "%s_#", obj->class_p->name);
name = name_buf;
}
size_t name_len = lv_strlen(name);
lv_obj_t * parent = lv_obj_get_parent(obj);
/*If the last character is # automatically index the children with the same name start*/
if(parent && name_len > 0 && name[name_len - 1] == '#') {
uint32_t child_cnt = lv_obj_get_child_count(parent);
uint32_t cnt = 0;
uint32_t i;
for(i = 0; i < child_cnt; i++) {
lv_obj_t * child = lv_obj_get_child(parent, i);
/*All siblings older siblings are checked, craft the name of this widget*/
if(child == obj) {
char num_buf[8];
size_t num_len;
num_len = lv_snprintf(num_buf, sizeof(num_buf), "%d", cnt);
/*Is there enough space for the name and the index?*/
if(buf_size > name_len + num_len) {
/*E.g. buf = "some_name_", so trim the # from the end*/
lv_strncpy(buf, name, name_len - 1);
lv_strcpy(&buf[name_len - 1], num_buf);
}
else {
/*Use the name as it is as a fallback*/
lv_strlcpy(buf, obj->spec_attr->name, buf_size);
}
break;
}
/*Check the older siblings. IF they start with the same name count them*/
else {
const char * child_name = lv_obj_get_name(child);
if(child_name == NULL) {
/*If the name we are looking for start with the child's class name
*increment the index. E.g. <class_name>_#*/
size_t class_name_len = lv_strlen(child->class_p->name);
if(name_len > 3 && class_name_len == name_len - 2 &&
lv_strncmp(child->class_p->name, name, class_name_len) == 0) {
cnt++;
}
}
/*The name is set, check if it's e.g. <some_name>#*/
else {
if(lv_strcmp(child->spec_attr->name, name) == 0) {
cnt++;
}
}
}
}
}
else {
/*Just use the set name*/
lv_strlcpy(buf, obj->spec_attr->name, buf_size);
}
}
lv_obj_t * lv_obj_get_child_by_name(const lv_obj_t * parent, const char * path)
{
LV_ASSERT_OBJ(parent, MY_CLASS);
if(parent == NULL || parent->spec_attr == NULL || path == NULL) return NULL;
while(*path) {
const char * segment = path;
uint32_t len = 0;
/* Calculate the length of the current segment */
while(path[len] && path[len] != '/')
len++;
/* Look for a child whose resolved name exactly matches the segment */
lv_obj_t * child = find_by_name_direct(parent, segment, len);
if(!child) return NULL; /*Segment not found*/
/* Advance to the next segment */
path += len;
if(*path == '/') path++; /* Skip the '/' */
/* If there is no further segment, we've found the target child */
if(*path == '\0') return child;
parent = child;
}
return NULL;
}
lv_obj_t * lv_obj_find_by_name(const lv_obj_t * parent, const char * name)
{
if(parent == NULL) parent = lv_display_get_screen_active(NULL);
if(parent == NULL) return NULL;
lv_obj_t * child = find_by_name_direct(parent, name, UINT16_MAX);
if(child) return child;
/*Search children recursively*/
uint32_t child_cnt = lv_obj_get_child_count(parent);
uint32_t i;
for(i = 0; i < child_cnt; i++) {
child = parent->spec_attr->children[i];
lv_obj_t * found = lv_obj_find_by_name(child, name);
if(found != NULL) return found;
}
return NULL;
}
#endif /*LV_USE_OBJ_NAME*/
int32_t lv_obj_get_index(const lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_obj_t * parent = lv_obj_get_parent(obj);
if(parent == NULL) return -1;
int32_t i = 0;
for(i = 0; i < parent->spec_attr->child_cnt; i++) {
if(parent->spec_attr->children[i] == obj) return i;
}
/*Shouldn't reach this point*/
LV_ASSERT(0);
return -1;
}
int32_t lv_obj_get_index_by_type(const lv_obj_t * obj, const lv_obj_class_t * class_p)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_obj_t * parent = lv_obj_get_parent(obj);
if(parent == NULL) return 0xFFFFFFFF;
uint32_t i = 0;
uint32_t idx = 0;
for(i = 0; i < parent->spec_attr->child_cnt; i++) {
lv_obj_t * child = parent->spec_attr->children[i];
if(child->class_p == class_p) {
if(child == obj) return idx;
idx++;
}
}
/*Can happen if there was no children with the given type*/
return -1;
}
void lv_obj_tree_walk(lv_obj_t * start_obj, lv_obj_tree_walk_cb_t cb, void * user_data)
{
walk_core(start_obj, cb, user_data);
}
void lv_obj_dump_tree(lv_obj_t * start_obj)
{
if(start_obj == NULL) {
lv_display_t * disp = lv_display_get_next(NULL);
while(disp) {
uint32_t i;
for(i = 0; i < disp->screen_cnt; i++) {
dump_tree_core(disp->screens[i], 0);
}
disp = lv_display_get_next(disp);
}
}
else {
dump_tree_core(start_obj, 0);
}
}
/**********************
* STATIC FUNCTIONS
**********************/
static void lv_obj_delete_async_cb(void * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_obj_delete(obj);
}
static void obj_indev_reset(lv_indev_t * indev, lv_obj_t * obj)
{
/* If the input device is already in the release state,
* there is no need to wait for the input device to be released
*/
if(lv_indev_get_state(indev) != LV_INDEV_STATE_RELEASED) {
/*Wait for release to avoid accidentally triggering other obj to be clicked*/
lv_indev_wait_release(indev);
}
/*Reset the input device*/
lv_indev_reset(indev, obj);
}
static void obj_delete_core(lv_obj_t * obj)
{
if(obj->is_deleting)
return;
obj->is_deleting = true;
/*Let the user free the resources used in `LV_EVENT_DELETE`*/
lv_result_t res = lv_obj_send_event(obj, LV_EVENT_DELETE, NULL);
if(res == LV_RESULT_INVALID) {
obj->is_deleting = false;
return;
}
/*Clean registered event_cb*/
if(obj->spec_attr) lv_event_remove_all(&(obj->spec_attr->event_list));
/*Recursively delete the children*/
lv_obj_t * child = lv_obj_get_child(obj, 0);
while(child) {
obj_delete_core(child);
child = lv_obj_get_child(obj, 0);
}
lv_group_t * group = lv_obj_get_group(obj);
/*Reset all input devices if the object to delete is used*/
lv_indev_t * indev = lv_indev_get_next(NULL);
while(indev) {
lv_indev_type_t indev_type = lv_indev_get_type(indev);
if(indev_type == LV_INDEV_TYPE_POINTER || indev_type == LV_INDEV_TYPE_BUTTON) {
if(indev->pointer.act_obj == obj || indev->pointer.last_obj == obj || indev->pointer.scroll_obj == obj) {
obj_indev_reset(indev, obj);
}
if(indev->pointer.last_pressed == obj) {
indev->pointer.last_pressed = NULL;
}
if(indev->pointer.last_hovered == obj) {
indev->pointer.last_hovered = NULL;
}
}
if(indev->group == group && obj == lv_indev_get_active_obj()) {
obj_indev_reset(indev, obj);
}
indev = lv_indev_get_next(indev);
}
/*Delete all pending async del-s*/
lv_result_t async_cancel_res = LV_RESULT_OK;
while(async_cancel_res == LV_RESULT_OK) {
async_cancel_res = lv_async_call_cancel(lv_obj_delete_async_cb, obj);
}
/*All children deleted. Now clean up the object specific data*/
lv_obj_destruct(obj);
/*Remove the screen for the screen list*/
if(obj->parent == NULL) {
lv_display_t * disp = lv_obj_get_display(obj);
uint32_t i;
/*Find the screen in the list*/
for(i = 0; i < disp->screen_cnt; i++) {
if(disp->screens[i] == obj) break;
}
uint32_t id = i;
for(i = id; i < disp->screen_cnt - 1; i++) {
disp->screens[i] = disp->screens[i + 1];
}
disp->screen_cnt--;
disp->screens = lv_realloc(disp->screens, disp->screen_cnt * sizeof(lv_obj_t *));
}
/*Remove the object from the child list of its parent*/
else {
int32_t id = lv_obj_get_index(obj);
uint16_t i;
for(i = id; i < obj->parent->spec_attr->child_cnt - 1; i++) {
obj->parent->spec_attr->children[i] = obj->parent->spec_attr->children[i + 1];
}
obj->parent->spec_attr->child_cnt--;
obj->parent->spec_attr->children = lv_realloc(obj->parent->spec_attr->children,
obj->parent->spec_attr->child_cnt * sizeof(lv_obj_t *));
}
/*Free the object itself*/
lv_free(obj);
}
static lv_obj_tree_walk_res_t walk_core(lv_obj_t * obj, lv_obj_tree_walk_cb_t cb, void * user_data)
{
lv_obj_tree_walk_res_t res = LV_OBJ_TREE_WALK_NEXT;
if(obj == NULL) {
lv_display_t * disp = lv_display_get_next(NULL);
while(disp) {
uint32_t i;
for(i = 0; i < disp->screen_cnt; i++) {
walk_core(disp->screens[i], cb, user_data);
}
disp = lv_display_get_next(disp);
}
return LV_OBJ_TREE_WALK_END; /*The value doesn't matter as it wasn't called recursively*/
}
res = cb(obj, user_data);
if(res == LV_OBJ_TREE_WALK_END) return LV_OBJ_TREE_WALK_END;
if(res != LV_OBJ_TREE_WALK_SKIP_CHILDREN) {
uint32_t i;
for(i = 0; i < lv_obj_get_child_count(obj); i++) {
res = walk_core(lv_obj_get_child(obj, i), cb, user_data);
if(res == LV_OBJ_TREE_WALK_END) return LV_OBJ_TREE_WALK_END;
}
}
return LV_OBJ_TREE_WALK_NEXT;
}
static void dump_tree_core(lv_obj_t * obj, int32_t depth)
{
#if LV_USE_LOG
const char * id;
#if LV_USE_OBJ_ID
char buf[OBJ_DUMP_STRING_LEN];
id = lv_obj_stringify_id(obj, buf, sizeof(buf));
if(id == NULL) id = "obj0";
#else
id = "obj0";
#endif
/*id of `obj0` is an invalid id for builtin id*/
LV_LOG_USER("%*sobj:%p, id:%s;", (int)(2 * depth), "", (void *)obj, id);
#endif /*LV_USE_LOG*/
if(obj && obj->spec_attr && obj->spec_attr->child_cnt) {
for(uint32_t i = 0; i < obj->spec_attr->child_cnt; i++) {
dump_tree_core(lv_obj_get_child(obj, i), depth + 1);
}
}
}
static lv_obj_t * lv_obj_get_first_not_deleting_child(lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
if(obj->spec_attr == NULL) return NULL;
int32_t i;
int32_t cnt = (int32_t)obj->spec_attr->child_cnt;
for(i = 0; i < cnt; i++) {
if(!obj->spec_attr->children[i]->is_deleting) {
return obj->spec_attr->children[i];
}
}
return NULL;
}
#if LV_USE_OBJ_NAME
static lv_obj_t * find_by_name_direct(const lv_obj_t * parent, const char * name, size_t len)
{
uint32_t i;
uint32_t child_cnt = lv_obj_get_child_count(parent);
for(i = 0; i < child_cnt; i++) {
lv_obj_t * child = parent->spec_attr->children[i];
char child_name_resolved[LV_OBJ_NAME_MAX_LEN];
lv_obj_get_name_resolved(child, child_name_resolved, sizeof(child_name_resolved));
if(lv_strncmp(child_name_resolved, name, len) == 0) return child;
}
return NULL;
}
#endif /*LV_USE_OBJ_NAME*/
@@ -0,0 +1,321 @@
/**
* @file lv_obj_tree.h
*
*/
#ifndef LV_OBJ_TREE_H
#define LV_OBJ_TREE_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../misc/lv_types.h"
#include "../misc/lv_anim.h"
#include "../display/lv_display.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
typedef enum {
LV_OBJ_TREE_WALK_NEXT,
LV_OBJ_TREE_WALK_SKIP_CHILDREN,
LV_OBJ_TREE_WALK_END,
} lv_obj_tree_walk_res_t;
typedef lv_obj_tree_walk_res_t (*lv_obj_tree_walk_cb_t)(lv_obj_t *, void *);
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Delete an object and all of its children.
* Also remove the objects from their group and remove all animations (if any).
* Send `LV_EVENT_DELETED` to deleted objects.
* @param obj pointer to an object
*/
void lv_obj_delete(lv_obj_t * obj);
/**
* Delete all children of an object.
* Also remove the objects from their group and remove all animations (if any).
* Send `LV_EVENT_DELETED` to deleted objects.
* @param obj pointer to an object
*/
void lv_obj_clean(lv_obj_t * obj);
/**
* Delete an object after some delay
* @param obj pointer to an object
* @param delay_ms time to wait before delete in milliseconds
*/
void lv_obj_delete_delayed(lv_obj_t * obj, uint32_t delay_ms);
/**
* A function to be easily used in animation ready callback to delete an object when the animation is ready
* @param a pointer to the animation
*/
void lv_obj_delete_anim_completed_cb(lv_anim_t * a);
/**
* Helper function for asynchronously deleting objects.
* Useful for cases where you can't delete an object directly in an `LV_EVENT_DELETE` handler (i.e. parent).
* @param obj object to delete
* @see lv_async_call
*/
void lv_obj_delete_async(lv_obj_t * obj);
/**
* Move the parent of an object. The relative coordinates will be kept.
*
* @param obj pointer to an object whose parent needs to be changed
* @param parent pointer to the new parent
*/
void lv_obj_set_parent(lv_obj_t * obj, lv_obj_t * parent);
/**
* Swap the positions of two objects.
* When used in listboxes, it can be used to sort the listbox items.
* @param obj1 pointer to the first object
* @param obj2 pointer to the second object
*/
void lv_obj_swap(lv_obj_t * obj1, lv_obj_t * obj2);
/**
* moves the object to the given index in its parent.
* When used in listboxes, it can be used to sort the listbox items.
* @param obj pointer to the object to be moved.
* @param index new index in parent. -1 to count from the back
* @note to move to the background: lv_obj_move_to_index(obj, 0)
* @note to move forward (up): lv_obj_move_to_index(obj, lv_obj_get_index(obj) - 1)
*/
void lv_obj_move_to_index(lv_obj_t * obj, int32_t index);
/**
* Get the screen of an object
* @param obj pointer to an object
* @return pointer to the object's screen
*/
lv_obj_t * lv_obj_get_screen(const lv_obj_t * obj);
/**
* Get the display of the object
* @param obj pointer to an object
* @return pointer to the object's display
*/
lv_display_t * lv_obj_get_display(const lv_obj_t * obj);
/**
* Get the parent of an object
* @param obj pointer to an object
* @return the parent of the object. (NULL if `obj` was a screen)
*/
lv_obj_t * lv_obj_get_parent(const lv_obj_t * obj);
/**
* Get the child of an object by the child's index.
* @param obj pointer to an object whose child should be get
* @param idx the index of the child.
* 0: the oldest (firstly created) child
* 1: the second oldest
* child count-1: the youngest
* -1: the youngest
* -2: the second youngest
* @return pointer to the child or NULL if the index was invalid
*/
lv_obj_t * lv_obj_get_child(const lv_obj_t * obj, int32_t idx);
/**
* Get the child of an object by the child's index. Consider the children only with a given type.
* @param obj pointer to an object whose child should be get
* @param idx the index of the child.
* 0: the oldest (firstly created) child
* 1: the second oldest
* child count-1: the youngest
* -1: the youngest
* -2: the second youngest
* @param class_p the type of the children to check
* @return pointer to the child or NULL if the index was invalid
*/
lv_obj_t * lv_obj_get_child_by_type(const lv_obj_t * obj, int32_t idx,
const lv_obj_class_t * class_p);
/**
* Return a sibling of an object
* @param obj pointer to an object whose sibling should be get
* @param idx 0: `obj` itself
* -1: the first older sibling
* -2: the next older sibling
* 1: the first younger sibling
* 2: the next younger sibling
* etc
* @return pointer to the requested sibling or NULL if there is no such sibling
*/
lv_obj_t * lv_obj_get_sibling(const lv_obj_t * obj, int32_t idx);
/**
* Return a sibling of an object. Consider the siblings only with a given type.
* @param obj pointer to an object whose sibling should be get
* @param idx 0: `obj` itself
* -1: the first older sibling
* -2: the next older sibling
* 1: the first younger sibling
* 2: the next younger sibling
* etc
* @param class_p the type of the children to check
* @return pointer to the requested sibling or NULL if there is no such sibling
*/
lv_obj_t * lv_obj_get_sibling_by_type(const lv_obj_t * obj, int32_t idx,
const lv_obj_class_t * class_p);
/**
* Get the number of children
* @param obj pointer to an object
* @return the number of children
*/
uint32_t lv_obj_get_child_count(const lv_obj_t * obj);
/**
* Get the number of children having a given type.
* @param obj pointer to an object
* @param class_p the type of the children to check
* @return the number of children
*/
uint32_t lv_obj_get_child_count_by_type(const lv_obj_t * obj, const lv_obj_class_t * class_p);
#if LV_USE_OBJ_NAME
/**
* Set a name for a widget. The name will be allocated and freed when the
* widget is deleted or a new name is set.
* @param obj pointer to an object
* @param name the name to set. If set to `NULL` the default "<widget_type>_#"
* name will be used.
* @note If the name ends with a `#`, older siblings with the same name
* will be counted, and the `#` will be replaced by the index of the
* given widget. For example, creating multiple widgets with the name
* "mybtn_#" will result in resolved names like "mybtn_0", "mybtn_1",
* "mybtn_2", etc. The name is resolved when `lv_obj_get_name_resolved`
* is called, so the result reflects the currently existing widgets at
* that time.
*/
void lv_obj_set_name(lv_obj_t * obj, const char * name);
/**
* Set a name for a widget. Only a pointer will be saved.
* @param obj pointer to an object
* @param name the name to set. If set to `NULL` the default "<widget_type>_#"
* name will be used.
* @note If the name ends with a `#`, older siblings with the same name
* will be counted, and the `#` will be replaced by the index of the
* given widget. For example, creating multiple widgets with the name
* "mybtn_#" will result in resolved names like "mybtn_0", "mybtn_1",
* "mybtn_2", etc. The name is resolved when `lv_obj_get_name_resolved`
* is called, so the result reflects the currently existing widgets at
* that time.
*/
void lv_obj_set_name_static(lv_obj_t * obj, const char * name);
/**
* Get the set name as it was set.
* @param obj pointer to an object
* @return get the set name or NULL if it wasn't set yet
*/
const char * lv_obj_get_name(const lv_obj_t * obj);
/**
* Get the set name or craft a name automatically.
* @param obj pointer to an object
* @param buf buffer to store the name
* @param buf_size the size of the buffer in bytes
* @note If the name ends with a `#`, older siblings with the same name
* will be counted, and the `#` will be replaced by the index of the
* given widget. For example, creating multiple widgets with the name
* "mybtn_#" will result in resolved names like "mybtn_0", "mybtn_1",
* "mybtn_2", etc. The name is resolved when `lv_obj_get_name_resolved`
* is called, so the result reflects the currently existing widgets at
* that time.
*/
void lv_obj_get_name_resolved(const lv_obj_t * obj, char buf[], size_t buf_size);
/**
* Find a child with a given name on a parent. This child doesn't have to be the
* direct child of the parent. First direct children of the parent will be checked,
* and the direct children of the first child, etc. (Breadth-first search).
*
* If the name of a widget was not set a name like "lv_button_1" will
* be created for it using `lv_obj_get_name_resolved`.
*
* @param parent the widget where the search should start
* @return the found widget or NULL if not found.
*/
lv_obj_t * lv_obj_find_by_name(const lv_obj_t * parent, const char * name);
/**
* Get an object by name. The name can be a path too, for example
* "main_container/lv_button_1/label".
* In this case the first part of the name-path should be the direct child of the parent,
* the second part, should the direct child of first one, etc.
*
* If the name of a widget was not set a name like "lv_button_1" will
* be created for it using `lv_obj_get_name_resolved`.
*
* @param parent the widget where the search should start
* @return the found widget or NULL if not found.
*/
lv_obj_t * lv_obj_get_child_by_name(const lv_obj_t * parent, const char * name_path);
#endif /*LV_USE_OBJ_NAME*/
/**
* Get the index of a child.
* @param obj pointer to an object
* @return the child index of the object.
* E.g. 0: the oldest (firstly created child).
* (-1 if child could not be found or no parent exists)
*/
int32_t lv_obj_get_index(const lv_obj_t * obj);
/**
* Get the index of a child. Consider the children only with a given type.
* @param obj pointer to an object
* @param class_p the type of the children to check
* @return the child index of the object.
* E.g. 0: the oldest (firstly created child with the given class).
* (-1 if child could not be found or no parent exists)
*/
int32_t lv_obj_get_index_by_type(const lv_obj_t * obj, const lv_obj_class_t * class_p);
/**
* Iterate through all children of any object.
* @param start_obj start integrating from this object
* @param cb call this callback on the objects
* @param user_data pointer to any user related data (will be passed to `cb`)
*/
void lv_obj_tree_walk(lv_obj_t * start_obj, lv_obj_tree_walk_cb_t cb, void * user_data);
/**
* Iterate through all children of any object and print their ID.
* @param start_obj start integrating from this object
*/
void lv_obj_dump_tree(lv_obj_t * start_obj);
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_OBJ_TREE_H*/
File diff suppressed because it is too large Load Diff
+74
View File
@@ -0,0 +1,74 @@
/**
* @file lv_refr.h
*
*/
#ifndef LV_REFR_H
#define LV_REFR_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "lv_obj.h"
#include "../display/lv_display.h"
#include "../misc/lv_types.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
/**
* Redraw the invalidated areas now.
* Normally the redrawing is periodically executed in `lv_timer_handler` but a long blocking process
* can prevent the call of `lv_timer_handler`. In this case if the GUI is updated in the process
* (e.g. progress bar) this function can be called when the screen should be updated.
* @param disp pointer to display to refresh. NULL to refresh all displays.
*/
void lv_refr_now(lv_display_t * disp);
/**
* Redrawn on object and all its children using the passed draw context
* @param layer pointer to a layer where to draw.
* @param obj the start object from the redraw should start
*/
void lv_obj_redraw(lv_layer_t * layer, lv_obj_t * obj);
/**
* Called periodically to handle the refreshing
* @param timer pointer to the timer itself, or `NULL`
*/
void lv_display_refr_timer(lv_timer_t * timer);
/**********************
* STATIC FUNCTIONS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_REFR_H*/
@@ -0,0 +1,69 @@
/**
* @file lv_refr_private.h
*
*/
#ifndef LV_REFR_PRIVATE_H
#define LV_REFR_PRIVATE_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "lv_refr.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Initialize the screen refresh subsystem
*/
void lv_refr_init(void);
/**
* Deinitialize the screen refresh subsystem
*/
void lv_refr_deinit(void);
/**
* Invalidate an area on display to redraw it
* @param area_p pointer to area which should be invalidated (NULL: delete the invalidated areas)
* @param disp pointer to display where the area should be invalidated (NULL can be used if there is
* only one display)
*/
void lv_inv_area(lv_display_t * disp, const lv_area_t * area_p);
/**
* Get the display which is being refreshed
* @return the display being refreshed
*/
lv_display_t * lv_refr_get_disp_refreshing(void);
/**
* Set the display which is being refreshed
* @param disp the display being refreshed
*/
void lv_refr_set_disp_refreshing(lv_display_t * disp);
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_REFR_PRIVATE_H*/
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,710 @@
/**
* @file lv_display.h
*
*/
#ifndef LV_DISPLAY_H
#define LV_DISPLAY_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../misc/lv_types.h"
#include "../misc/lv_timer.h"
#include "../misc/lv_event.h"
#include "../misc/lv_color.h"
#include "../misc/lv_area.h"
/*********************
* DEFINES
*********************/
#ifndef LV_ATTRIBUTE_FLUSH_READY
#define LV_ATTRIBUTE_FLUSH_READY
#endif
/**********************
* TYPEDEFS
**********************/
typedef enum {
LV_DISPLAY_ROTATION_0 = 0,
LV_DISPLAY_ROTATION_90,
LV_DISPLAY_ROTATION_180,
LV_DISPLAY_ROTATION_270
} lv_display_rotation_t;
typedef enum {
/**
* Use the buffer(s) to render the screen is smaller parts.
* This way the buffers can be smaller then the display to save RAM. At least 1/10 screen size buffer(s) are recommended.
*/
LV_DISPLAY_RENDER_MODE_PARTIAL,
/**
* The buffer(s) has to be screen sized and LVGL will render into the correct location of the buffer.
* This way the buffer always contain the whole image. Only the changed ares will be updated.
* With 2 buffers the buffers' content are kept in sync automatically and in flush_cb only address change is required.
*/
LV_DISPLAY_RENDER_MODE_DIRECT,
/**
* Always redraw the whole screen even if only one pixel has been changed.
* With 2 buffers in flush_cb only an address change is required.
*/
LV_DISPLAY_RENDER_MODE_FULL,
} lv_display_render_mode_t;
typedef enum {
LV_SCR_LOAD_ANIM_NONE,
LV_SCR_LOAD_ANIM_OVER_LEFT,
LV_SCR_LOAD_ANIM_OVER_RIGHT,
LV_SCR_LOAD_ANIM_OVER_TOP,
LV_SCR_LOAD_ANIM_OVER_BOTTOM,
LV_SCR_LOAD_ANIM_MOVE_LEFT,
LV_SCR_LOAD_ANIM_MOVE_RIGHT,
LV_SCR_LOAD_ANIM_MOVE_TOP,
LV_SCR_LOAD_ANIM_MOVE_BOTTOM,
LV_SCR_LOAD_ANIM_FADE_IN,
LV_SCR_LOAD_ANIM_FADE_ON = LV_SCR_LOAD_ANIM_FADE_IN, /*For backward compatibility*/
LV_SCR_LOAD_ANIM_FADE_OUT,
LV_SCR_LOAD_ANIM_OUT_LEFT,
LV_SCR_LOAD_ANIM_OUT_RIGHT,
LV_SCR_LOAD_ANIM_OUT_TOP,
LV_SCR_LOAD_ANIM_OUT_BOTTOM,
} lv_screen_load_anim_t;
typedef void (*lv_display_flush_cb_t)(lv_display_t * disp, const lv_area_t * area, uint8_t * px_map);
typedef void (*lv_display_flush_wait_cb_t)(lv_display_t * disp);
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Create a new display with the given resolution
* @param hor_res horizontal resolution in pixels
* @param ver_res vertical resolution in pixels
* @return pointer to a display object or `NULL` on error
*/
lv_display_t * lv_display_create(int32_t hor_res, int32_t ver_res);
/**
* Remove a display
* @param disp pointer to display
*/
void lv_display_delete(lv_display_t * disp);
/**
* Set a default display. The new screens will be created on it by default.
* @param disp pointer to a display
*/
void lv_display_set_default(lv_display_t * disp);
/**
* Get the default display
* @return pointer to the default display
*/
lv_display_t * lv_display_get_default(void);
/**
* Get the next display.
* @param disp pointer to the current display. NULL to initialize.
* @return the next display or NULL if no more. Gives the first display when the parameter is NULL.
*/
lv_display_t * lv_display_get_next(lv_display_t * disp);
/*---------------------
* RESOLUTION
*--------------------*/
/**
* Sets the resolution of a display. `LV_EVENT_RESOLUTION_CHANGED` event will be sent.
* Here the native resolution of the device should be set. If the display will be rotated later with
* `lv_display_set_rotation` LVGL will swap the hor. and ver. resolution automatically.
* @param disp pointer to a display
* @param hor_res the new horizontal resolution
* @param ver_res the new vertical resolution
*/
void lv_display_set_resolution(lv_display_t * disp, int32_t hor_res, int32_t ver_res);
/**
* It's not mandatory to use the whole display for LVGL, however in some cases physical resolution is important.
* For example the touchpad still sees whole resolution and the values needs to be converted
* to the active LVGL display area.
* @param disp pointer to a display
* @param hor_res the new physical horizontal resolution, or -1 to assume it's the same as the normal hor. res.
* @param ver_res the new physical vertical resolution, or -1 to assume it's the same as the normal hor. res.
*/
void lv_display_set_physical_resolution(lv_display_t * disp, int32_t hor_res, int32_t ver_res);
/**
* If physical resolution is not the same as the normal resolution
* the offset of the active display area can be set here.
* @param disp pointer to a display
* @param x X offset
* @param y Y offset
*/
void lv_display_set_offset(lv_display_t * disp, int32_t x, int32_t y);
/**
* Set the rotation of this display. LVGL will swap the horizontal and vertical resolutions internally.
* @param disp pointer to a display (NULL to use the default display)
* @param rotation `LV_DISPLAY_ROTATION_0/90/180/270`
*/
void lv_display_set_rotation(lv_display_t * disp, lv_display_rotation_t rotation);
/**
* Use matrix rotation for the display. This function is depended on `LV_DRAW_TRANSFORM_USE_MATRIX`
* @param disp pointer to a display (NULL to use the default display)
* @param enable true: enable matrix rotation, false: disable
*/
void lv_display_set_matrix_rotation(lv_display_t * disp, bool enable);
/**
* Set the DPI (dot per inch) of the display.
* dpi = sqrt(hor_res^2 + ver_res^2) / diagonal"
* @param disp pointer to a display
* @param dpi the new DPI
*/
void lv_display_set_dpi(lv_display_t * disp, int32_t dpi);
/**
* Get the horizontal resolution of a display.
* @param disp pointer to a display (NULL to use the default display)
* @return the horizontal resolution of the display.
*/
int32_t lv_display_get_horizontal_resolution(const lv_display_t * disp);
/**
* Get the vertical resolution of a display
* @param disp pointer to a display (NULL to use the default display)
* @return the vertical resolution of the display
*/
int32_t lv_display_get_vertical_resolution(const lv_display_t * disp);
/**
* Get the original horizontal resolution of a display without considering rotation
* @param disp pointer to a display (NULL to use the default display)
* @return the horizontal resolution of the display.
*/
int32_t lv_display_get_original_horizontal_resolution(const lv_display_t * disp);
/**
* Get the original vertical resolution of a display without considering rotation
* @param disp pointer to a display (NULL to use the default display)
* @return the vertical resolution of the display
*/
int32_t lv_display_get_original_vertical_resolution(const lv_display_t * disp);
/**
* Get the physical horizontal resolution of a display
* @param disp pointer to a display (NULL to use the default display)
* @return the physical horizontal resolution of the display
*/
int32_t lv_display_get_physical_horizontal_resolution(const lv_display_t * disp);
/**
* Get the physical vertical resolution of a display
* @param disp pointer to a display (NULL to use the default display)
* @return the physical vertical resolution of the display
*/
int32_t lv_display_get_physical_vertical_resolution(const lv_display_t * disp);
/**
* Get the horizontal offset from the full / physical display
* @param disp pointer to a display (NULL to use the default display)
* @return the horizontal offset from the physical display
*/
int32_t lv_display_get_offset_x(const lv_display_t * disp);
/**
* Get the vertical offset from the full / physical display
* @param disp pointer to a display (NULL to use the default display)
* @return the horizontal offset from the physical display
*/
int32_t lv_display_get_offset_y(const lv_display_t * disp);
/**
* Get the current rotation of this display.
* @param disp pointer to a display (NULL to use the default display)
* @return the current rotation
*/
lv_display_rotation_t lv_display_get_rotation(lv_display_t * disp);
/**
* Get if matrix rotation is enabled for a display or not
* @param disp pointer to a display (NULL to use the default display)
* @return true: matrix rotation is enabled; false: disabled
*/
bool lv_display_get_matrix_rotation(lv_display_t * disp);
/**
* Get the DPI of the display
* @param disp pointer to a display (NULL to use the default display)
* @return dpi of the display
*/
int32_t lv_display_get_dpi(const lv_display_t * disp);
/*---------------------
* BUFFERING
*--------------------*/
/**
* Set the buffers for a display, similarly to `lv_display_set_draw_buffers`, but accept the raw buffer pointers.
* For DIRECT/FULL rending modes, the buffer size must be at least
* `hor_res * ver_res * lv_color_format_get_size(lv_display_get_color_format(disp))`
* @param disp pointer to a display
* @param buf1 first buffer
* @param buf2 second buffer (can be `NULL`)
* @param buf_size buffer size in byte
* @param render_mode LV_DISPLAY_RENDER_MODE_PARTIAL/DIRECT/FULL
*/
void lv_display_set_buffers(lv_display_t * disp, void * buf1, void * buf2, uint32_t buf_size,
lv_display_render_mode_t render_mode);
/**
* Set the frame buffers for a display, similarly to `lv_display_set_buffers`, but allow
* for a custom stride as required by a display controller.
* This allows the frame buffers to have a stride alignment different from the rest of
* the buffers`
* @param disp pointer to a display
* @param buf1 first buffer
* @param buf2 second buffer (can be `NULL`)
* @param buf_size buffer size in byte
* @param stride buffer stride in bytes
* @param render_mode LV_DISPLAY_RENDER_MODE_PARTIAL/DIRECT/FULL
*/
void lv_display_set_buffers_with_stride(lv_display_t * disp, void * buf1, void * buf2, uint32_t buf_size,
uint32_t stride, lv_display_render_mode_t render_mode);
/**
* Set the buffers for a display, accept a draw buffer pointer.
* Normally use `lv_display_set_buffers` is enough for most cases.
* Use this function when an existing lv_draw_buf_t is available.
* @param disp pointer to a display
* @param buf1 first buffer
* @param buf2 second buffer (can be `NULL`)
*/
void lv_display_set_draw_buffers(lv_display_t * disp, lv_draw_buf_t * buf1, lv_draw_buf_t * buf2);
/**
* Set the third draw buffer for a display.
* @param disp pointer to a display
* @param buf3 third buffer
*/
void lv_display_set_3rd_draw_buffer(lv_display_t * disp, lv_draw_buf_t * buf3);
/**
* Set display render mode
* @param disp pointer to a display
* @param render_mode LV_DISPLAY_RENDER_MODE_PARTIAL/DIRECT/FULL
*/
void lv_display_set_render_mode(lv_display_t * disp, lv_display_render_mode_t render_mode);
/**
* Set the flush callback which will be called to copy the rendered image to the display.
* @param disp pointer to a display
* @param flush_cb the flush callback (`px_map` contains the rendered image as raw pixel map and it should be copied to `area` on the display)
*/
void lv_display_set_flush_cb(lv_display_t * disp, lv_display_flush_cb_t flush_cb);
/**
* Set a callback to be used while LVGL is waiting flushing to be finished.
* It can do any complex logic to wait, including semaphores, mutexes, polling flags, etc.
* If not set the `disp->flushing` flag is used which can be cleared with `lv_display_flush_ready()`
* @param disp pointer to a display
* @param wait_cb a callback to call while LVGL is waiting for flush ready.
* If NULL `lv_display_flush_ready()` can be used to signal that flushing is ready.
*/
void lv_display_set_flush_wait_cb(lv_display_t * disp, lv_display_flush_wait_cb_t wait_cb);
/**
* Set the color format of the display.
* @param disp pointer to a display
* @param color_format Possible values are
* - LV_COLOR_FORMAT_RGB565
* - LV_COLOR_FORMAT_RGB888
* - LV_COLOR_FORMAT_XRGB888
* - LV_COLOR_FORMAT_ARGB888
*@note To change the endianness of the rendered image in case of RGB565 format
* (i.e. swap the 2 bytes) call `lv_draw_sw_rgb565_swap` in the flush_cb
*/
void lv_display_set_color_format(lv_display_t * disp, lv_color_format_t color_format);
/**
* Get the color format of the display
* @param disp pointer to a display
* @return the color format
*/
lv_color_format_t lv_display_get_color_format(lv_display_t * disp);
/**
* Set the number of tiles for parallel rendering.
* @param disp pointer to a display
* @param tile_cnt number of tiles (1 =< tile_cnt < 256)
*/
void lv_display_set_tile_cnt(lv_display_t * disp, uint32_t tile_cnt);
/**
* Get the number of tiles used for parallel rendering
* @param disp pointer to a display
* @return number of tiles
*/
uint32_t lv_display_get_tile_cnt(lv_display_t * disp);
/**
* Enable anti-aliasing for the render engine
* @param disp pointer to a display
* @param en true/false
*/
void lv_display_set_antialiasing(lv_display_t * disp, bool en);
/**
* Get if anti-aliasing is enabled for a display or not
* @param disp pointer to a display (NULL to use the default display)
* @return true/false
*/
bool lv_display_get_antialiasing(lv_display_t * disp);
/**
* Call from the display driver when the flushing is finished
* @param disp pointer to display whose `flush_cb` was called
*/
LV_ATTRIBUTE_FLUSH_READY void lv_display_flush_ready(lv_display_t * disp);
/**
* Tell if it's the last area of the refreshing process.
* Can be called from `flush_cb` to execute some special display refreshing if needed when all areas area flushed.
* @param disp pointer to display
* @return true: it's the last area to flush;
* false: there are other areas too which will be refreshed soon
*/
LV_ATTRIBUTE_FLUSH_READY bool lv_display_flush_is_last(lv_display_t * disp);
bool lv_display_is_double_buffered(lv_display_t * disp);
/*---------------------
* SCREENS
*--------------------*/
/**
* Return a pointer to the active screen on a display
* @param disp pointer to display which active screen should be get.
* (NULL to use the default screen)
* @return pointer to the active screen object (loaded by 'lv_screen_load()')
*/
lv_obj_t * lv_display_get_screen_active(lv_display_t * disp);
/**
* Return with a pointer to the previous screen. Only used during screen transitions.
* @param disp pointer to display which previous screen should be get.
* (NULL to use the default screen)
* @return pointer to the previous screen object or NULL if not used now
*/
lv_obj_t * lv_display_get_screen_prev(lv_display_t * disp);
/**
* Return the top layer. The top layer is the same on all screens and it is above the normal screen layer.
* @param disp pointer to display which top layer should be get. (NULL to use the default screen)
* @return pointer to the top layer object
*/
lv_obj_t * lv_display_get_layer_top(lv_display_t * disp);
/**
* Return the sys. layer. The system layer is the same on all screen and it is above the normal screen and the top layer.
* @param disp pointer to display which sys. layer should be retrieved. (NULL to use the default screen)
* @return pointer to the sys layer object
*/
lv_obj_t * lv_display_get_layer_sys(lv_display_t * disp);
/**
* Return the bottom layer. The bottom layer is the same on all screen and it is under the normal screen layer.
* It's visible only if the screen is transparent.
* @param disp pointer to display (NULL to use the default screen)
* @return pointer to the bottom layer object
*/
lv_obj_t * lv_display_get_layer_bottom(lv_display_t * disp);
/**
* Load a screen on the default display
* @param scr pointer to a screen
*/
void lv_screen_load(struct _lv_obj_t * scr);
/**
* Switch screen with animation
* @param scr pointer to the new screen to load
* @param anim_type type of the animation from `lv_screen_load_anim_t`, e.g. `LV_SCR_LOAD_ANIM_MOVE_LEFT`
* @param time time of the animation
* @param delay delay before the transition
* @param auto_del true: automatically delete the old screen
*/
void lv_screen_load_anim(lv_obj_t * scr, lv_screen_load_anim_t anim_type, uint32_t time, uint32_t delay,
bool auto_del);
/**
* Get the active screen of the default display
* @return pointer to the active screen
*/
lv_obj_t * lv_screen_active(void);
/**
* Get the top layer of the default display
* @return pointer to the top layer
*/
lv_obj_t * lv_layer_top(void);
/**
* Get the system layer of the default display
* @return pointer to the sys layer
*/
lv_obj_t * lv_layer_sys(void);
/**
* Get the bottom layer of the default display
* @return pointer to the bottom layer
*/
lv_obj_t * lv_layer_bottom(void);
/*---------------------
* OTHERS
*--------------------*/
/**
* Add an event handler to the display
* @param disp pointer to a display
* @param event_cb an event callback
* @param filter event code to react or `LV_EVENT_ALL`
* @param user_data optional user_data
*/
void lv_display_add_event_cb(lv_display_t * disp, lv_event_cb_t event_cb, lv_event_code_t filter, void * user_data);
/**
* Get the number of event attached to a display
* @param disp pointer to a display
* @return number of events
*/
uint32_t lv_display_get_event_count(lv_display_t * disp);
/**
* Get an event descriptor for an event
* @param disp pointer to a display
* @param index the index of the event
* @return the event descriptor
*/
lv_event_dsc_t * lv_display_get_event_dsc(lv_display_t * disp, uint32_t index);
/**
* Remove an event
* @param disp pointer to a display
* @param index the index of the event to remove
* @return true: and event was removed; false: no event was removed
*/
bool lv_display_delete_event(lv_display_t * disp, uint32_t index);
/**
* Remove an event_cb with user_data
* @param disp pointer to a display
* @param event_cb the event_cb of the event to remove
* @param user_data user_data
* @return the count of the event removed
*/
uint32_t lv_display_remove_event_cb_with_user_data(lv_display_t * disp, lv_event_cb_t event_cb, void * user_data);
/**
* Send an event to a display
* @param disp pointer to a display
* @param code an event code. LV_EVENT_...
* @param param optional param
* @return LV_RESULT_OK: disp wasn't deleted in the event.
*/
lv_result_t lv_display_send_event(lv_display_t * disp, lv_event_code_t code, void * param);
/**
* Get the area to be invalidated. Can be used in `LV_EVENT_INVALIDATE_AREA`
* @param e pointer to an event
* @return the area to invalidated (can be modified as required)
*/
lv_area_t * lv_event_get_invalidated_area(lv_event_t * e);
/**
* Set the theme of a display. If there are no user created widgets yet the screens' theme will be updated
* @param disp pointer to a display
* @param th pointer to a theme
*/
void lv_display_set_theme(lv_display_t * disp, lv_theme_t * th);
/**
* Get the theme of a display
* @param disp pointer to a display
* @return the display's theme (can be NULL)
*/
lv_theme_t * lv_display_get_theme(lv_display_t * disp);
/**
* Get elapsed time since last user activity on a display (e.g. click)
* @param disp pointer to a display (NULL to get the overall smallest inactivity)
* @return elapsed ticks (milliseconds) since the last activity
*/
uint32_t lv_display_get_inactive_time(const lv_display_t * disp);
/**
* Manually trigger an activity on a display
* @param disp pointer to a display (NULL to use the default display)
*/
void lv_display_trigger_activity(lv_display_t * disp);
/**
* Temporarily enable and disable the invalidation of the display.
* @param disp pointer to a display (NULL to use the default display)
* @param en true: enable invalidation; false: invalidation
*/
void lv_display_enable_invalidation(lv_display_t * disp, bool en);
/**
* Get display invalidation is enabled.
* @param disp pointer to a display (NULL to use the default display)
* @return return true if invalidation is enabled
*/
bool lv_display_is_invalidation_enabled(lv_display_t * disp);
/**
* Get a pointer to the screen refresher timer to
* modify its parameters with `lv_timer_...` functions.
* @param disp pointer to a display
* @return pointer to the display refresher timer. (NULL on error)
*/
lv_timer_t * lv_display_get_refr_timer(lv_display_t * disp);
/**
* Delete screen refresher timer
* @param disp pointer to a display
*/
void lv_display_delete_refr_timer(lv_display_t * disp);
/**
* Register vsync event of a display. `LV_EVENT_VSYNC` event will be sent periodically.
* Please don't use it in display event listeners, as it may cause memory leaks and illegal access issues.
*
* @param disp pointer to a display
* @param event_cb an event callback
* @param user_data optional user_data
*/
bool lv_display_register_vsync_event(lv_display_t * disp, lv_event_cb_t event_cb, void * user_data);
/**
* Unregister vsync event of a display. `LV_EVENT_VSYNC` event won't be sent periodically.
* Please don't use it in display event listeners, as it may cause memory leaks and illegal access issues.
* @param disp pointer to a display
* @param event_cb an event callback
* @param user_data optional user_data
*/
bool lv_display_unregister_vsync_event(lv_display_t * disp, lv_event_cb_t event_cb, void * user_data);
/**
* Send an vsync event to a display
* @param disp pointer to a display
* @param param optional param
* @return LV_RESULT_OK: disp wasn't deleted in the event.
*/
lv_result_t lv_display_send_vsync_event(lv_display_t * disp, void * param);
void lv_display_set_user_data(lv_display_t * disp, void * user_data);
void lv_display_set_driver_data(lv_display_t * disp, void * driver_data);
void * lv_display_get_user_data(lv_display_t * disp);
void * lv_display_get_driver_data(lv_display_t * disp);
lv_draw_buf_t * lv_display_get_buf_active(lv_display_t * disp);
/**
* Rotate an area in-place according to the display's rotation
* @param disp pointer to a display
* @param area pointer to an area to rotate
*/
void lv_display_rotate_area(lv_display_t * disp, lv_area_t * area);
/**
* Get the size of the draw buffers
* @param disp pointer to a display
* @return the size of the draw buffer in bytes for valid display, 0 otherwise
*/
uint32_t lv_display_get_draw_buf_size(lv_display_t * disp);
/**
* Get the size of the invalidated draw buffer. Can be used in the flush callback
* to get the number of bytes used in the current render buffer.
* @param disp pointer to a display
* @param width the width of the invalidated area
* @param height the height of the invalidated area
* @return the size of the invalidated draw buffer in bytes, not accounting for
* any preceding palette information for a valid display, 0 otherwise
*/
uint32_t lv_display_get_invalidated_draw_buf_size(lv_display_t * disp, uint32_t width, uint32_t height);
/**********************
* MACROS
**********************/
/*------------------------------------------------
* To improve backward compatibility
* Recommended only if you have one display
*------------------------------------------------*/
#ifndef LV_HOR_RES
/**
* The horizontal resolution of the currently active display.
*/
#define LV_HOR_RES lv_display_get_horizontal_resolution(lv_display_get_default())
#endif
#ifndef LV_VER_RES
/**
* The vertical resolution of the currently active display.
*/
#define LV_VER_RES lv_display_get_vertical_resolution(lv_display_get_default())
#endif
/**
* See `lv_dpx()` and `lv_display_dpx()`.
* Same as Android's DIP. (Different name is chosen to avoid mistype between LV_DPI and LV_DIP)
*
* - 40 dip is 40 px on a 160 DPI screen (distance = 1/4 inch).
* - 40 dip is 80 px on a 320 DPI screen (distance still = 1/4 inch).
*
* @sa https://stackoverflow.com/questions/2025282/what-is-the-difference-between-px-dip-dp-and-sp
*/
#define LV_DPX_CALC(dpi, n) ((n) == 0 ? 0 :LV_MAX((( (dpi) * (n) + 80) / 160), 1)) /*+80 for rounding*/
#define LV_DPX(n) LV_DPX_CALC(lv_display_get_dpi(NULL), n)
/**
* For default display, computes the number of pixels (a distance or size) as if the
* display had 160 DPI. This allows you to specify 1/160-th fractions of an inch to
* get real distance on the display that will be consistent regardless of its current
* DPI. It ensures `lv_dpx(100)`, for example, will have the same physical size
* regardless to the DPI of the display.
* @param n number of 1/160-th-inch units to compute with
* @return number of pixels to use to make that distance
*/
int32_t lv_dpx(int32_t n);
/**
* For specified display, computes the number of pixels (a distance or size) as if the
* display had 160 DPI. This allows you to specify 1/160-th fractions of an inch to
* get real distance on the display that will be consistent regardless of its current
* DPI. It ensures `lv_dpx(100)`, for example, will have the same physical size
* regardless to the DPI of the display.
* @param disp pointer to display whose dpi should be considered
* @param n number of 1/160-th-inch units to compute with
* @return number of pixels to use to make that distance
*/
int32_t lv_display_dpx(const lv_display_t * disp, int32_t n);
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_DISPLAY_H*/
@@ -0,0 +1,188 @@
/**
* @file lv_display_private.h
*
*/
#ifndef LV_DISPLAY_PRIVATE_H
#define LV_DISPLAY_PRIVATE_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../misc/lv_types.h"
#include "../core/lv_obj.h"
#include "../draw/lv_draw.h"
#include "lv_display.h"
#if LV_USE_SYSMON
#include "../others/sysmon/lv_sysmon_private.h"
#endif
/*********************
* DEFINES
*********************/
#ifndef LV_INV_BUF_SIZE
#define LV_INV_BUF_SIZE 32 /**< Buffer size for invalid areas */
#endif
/**********************
* TYPEDEFS
**********************/
struct _lv_display_t {
/*---------------------
* Resolution
*--------------------*/
/** Horizontal resolution.*/
int32_t hor_res;
/** Vertical resolution.*/
int32_t ver_res;
/** Horizontal resolution of the full / physical display. Set to -1 for fullscreen mode.*/
int32_t physical_hor_res;
/** Vertical resolution of the full / physical display. Set to -1 for fullscreen mode.*/
int32_t physical_ver_res;
/** Horizontal offset from the full / physical display. Set to 0 for fullscreen mode.*/
int32_t offset_x;
/** Vertical offset from the full / physical display. Set to 0 for fullscreen mode.*/
int32_t offset_y;
/** DPI (dot per inch) of the display. Default value is `LV_DPI_DEF`.*/
uint32_t dpi;
/*---------------------
* Buffering
*--------------------*/
lv_draw_buf_t * buf_1;
lv_draw_buf_t * buf_2;
lv_draw_buf_t * buf_3;
/** Internal, used by the library*/
lv_draw_buf_t * buf_act;
/** MANDATORY: Write the internal buffer (draw_buf) to the display. 'lv_display_flush_ready()' has to be
* called when finished*/
lv_display_flush_cb_t flush_cb;
/**
* Used to wait while flushing is ready.
* It can do any complex logic to wait, including semaphores, mutexes, polling flags, etc.
* If not set `flushing` flag is used which can be cleared with `lv_display_flush_ready()` */
lv_display_flush_wait_cb_t flush_wait_cb;
/** 1: flushing is in progress. (It can't be a bit field because when it's cleared from IRQ
* Read-Modify-Write issue might occur) */
volatile int flushing;
/** 1: It was the last chunk to flush. (It can't be a bit field because when it's cleared
* from IRQ Read-Modify-Write issue might occur) */
volatile int flushing_last;
volatile uint32_t last_area : 1; /**< 1: last area is being rendered */
volatile uint32_t last_part : 1; /**< 1: last part of the current area is being rendered */
lv_display_render_mode_t render_mode;
uint32_t antialiasing : 1; /**< 1: anti-aliasing is enabled on this display.*/
uint32_t tile_cnt : 8; /**< Divide the display buffer into these number of tiles */
uint32_t stride_is_auto : 1; /**< 1: The stride of the buffers was not set explicitly. */
/** 1: The current screen rendering is in progress*/
uint32_t rendering_in_progress : 1;
lv_color_format_t color_format;
/** Invalidated (marked to redraw) areas*/
lv_area_t inv_areas[LV_INV_BUF_SIZE];
uint8_t inv_area_joined[LV_INV_BUF_SIZE];
uint32_t inv_p;
int32_t inv_en_cnt;
/** Double buffer sync areas (redrawn during last refresh) */
lv_ll_t sync_areas;
lv_draw_buf_t _static_buf1; /**< Used when user pass in a raw buffer as display draw buffer */
lv_draw_buf_t _static_buf2;
/*---------------------
* Layer
*--------------------*/
lv_layer_t * layer_head;
void (*layer_init)(lv_display_t * disp, lv_layer_t * layer);
void (*layer_deinit)(lv_display_t * disp, lv_layer_t * layer);
/*---------------------
* Screens
*--------------------*/
/** Screens of the display*/
lv_obj_t ** screens; /**< Array of screen objects.*/
lv_obj_t * sys_layer; /**< @see lv_display_get_layer_sys*/
lv_obj_t * top_layer; /**< @see lv_display_get_layer_top*/
lv_obj_t * act_scr; /**< Currently active screen on this display*/
lv_obj_t * bottom_layer;/**< @see lv_display_get_layer_bottom*/
lv_obj_t * prev_scr; /**< Previous screen. Used during screen animations*/
lv_obj_t * scr_to_load; /**< The screen prepared to load in lv_screen_load_anim*/
uint32_t screen_cnt;
uint8_t draw_prev_over_act : 1;/** 1: Draw previous screen over active screen*/
uint8_t del_prev : 1; /** 1: Automatically delete the previous screen when the screen load animation is ready*/
/*---------------------
* Others
*--------------------*/
void * driver_data; /**< Custom user data*/
void * user_data; /**< Custom user data*/
lv_event_list_t event_list;
uint32_t rotation : 3; /**< Element of lv_display_rotation_t*/
uint32_t matrix_rotation : 1; /**< 1: Use matrix for display rotation*/
lv_theme_t * theme; /**< The theme assigned to the screen*/
/** A timer which periodically checks the dirty areas and refreshes them*/
lv_timer_t * refr_timer;
/*Miscellaneous data*/
uint32_t last_activity_time; /**< Last time when there was activity on this display*/
/** The area being refreshed*/
lv_area_t refreshed_area;
uint32_t vsync_count;
#if LV_USE_PERF_MONITOR
lv_obj_t * perf_label;
lv_sysmon_backend_data_t perf_sysmon_backend;
lv_sysmon_perf_info_t perf_sysmon_info;
#endif
#if LV_USE_MEM_MONITOR
lv_obj_t * mem_label;
#endif
};
/**********************
* GLOBAL PROTOTYPES
**********************/
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_DISPLAY_PRIVATE_H*/
@@ -0,0 +1,472 @@
/**
* @file lv_draw_dma2d.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_draw_dma2d_private.h"
#if LV_USE_DRAW_DMA2D
#include "../sw/lv_draw_sw.h"
#include "../../misc/lv_area_private.h"
#if !LV_DRAW_DMA2D_ASYNC && LV_USE_DRAW_DMA2D_INTERRUPT
#warning LV_USE_DRAW_DMA2D_INTERRUPT is 1 but has no effect because LV_USE_OS is LV_OS_NONE
#endif
/*********************
* DEFINES
*********************/
#define DRAW_UNIT_ID_DMA2D 5
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static int32_t evaluate_cb(lv_draw_unit_t * draw_unit, lv_draw_task_t * task);
static int32_t dispatch_cb(lv_draw_unit_t * draw_unit, lv_layer_t * layer);
static int32_t delete_cb(lv_draw_unit_t * draw_unit);
#if LV_DRAW_DMA2D_ASYNC
static void thread_cb(void * arg);
#endif
#if !LV_DRAW_DMA2D_ASYNC
static bool check_transfer_completion(void);
#endif
static void post_transfer_tasks(lv_draw_dma2d_unit_t * u);
/**********************
* STATIC VARIABLES
**********************/
#if LV_DRAW_DMA2D_ASYNC
static lv_draw_dma2d_unit_t * g_unit;
#endif
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
void lv_draw_dma2d_init(void)
{
lv_draw_dma2d_unit_t * draw_dma2d_unit = lv_draw_create_unit(sizeof(lv_draw_dma2d_unit_t));
draw_dma2d_unit->base_unit.evaluate_cb = evaluate_cb;
draw_dma2d_unit->base_unit.dispatch_cb = dispatch_cb;
draw_dma2d_unit->base_unit.delete_cb = delete_cb;
draw_dma2d_unit->base_unit.name = "DMA2D";
#if LV_DRAW_DMA2D_ASYNC
g_unit = draw_dma2d_unit;
lv_result_t res = lv_thread_init(&draw_dma2d_unit->thread, "dma2d", LV_DRAW_THREAD_PRIO, thread_cb, 2 * 1024,
draw_dma2d_unit);
LV_ASSERT(res == LV_RESULT_OK);
#endif
/* enable the DMA2D clock */
#if defined(STM32F4) || defined(STM32F7) || defined(STM32U5)
RCC->AHB1ENR |= RCC_AHB1ENR_DMA2DEN;
#elif defined(STM32H7)
RCC->AHB3ENR |= RCC_AHB3ENR_DMA2DEN;
#elif defined(STM32H7RS)
RCC->AHB5ENR |= RCC_AHB5ENR_DMA2DEN;
#else
#warning "LVGL can't enable the clock for DMA2D"
#endif
/* disable dead time */
DMA2D->AMTCR = 0;
/* enable the interrupt */
NVIC_EnableIRQ(DMA2D_IRQn);
}
void lv_draw_dma2d_deinit(void)
{
/* disable the interrupt */
NVIC_DisableIRQ(DMA2D_IRQn);
/* disable the DMA2D clock */
#if defined(STM32F4) || defined(STM32F7) || defined(STM32U5) || defined(STM32L4)
RCC->AHB1ENR &= ~RCC_AHB1ENR_DMA2DEN;
#elif defined(STM32H7)
RCC->AHB3ENR &= ~RCC_AHB3ENR_DMA2DEN;
#elif defined(STM32H7RS)
RCC->AHB5ENR &= ~RCC_AHB5ENR_DMA2DEN;
#endif
#if LV_DRAW_DMA2D_ASYNC
lv_result_t res = lv_thread_delete(&g_unit->thread);
LV_ASSERT(res == LV_RESULT_OK);
res = lv_thread_sync_delete(&g_unit->interrupt_signal);
LV_ASSERT(res == LV_RESULT_OK);
g_unit = NULL;
#endif
}
#if LV_USE_DRAW_DMA2D_INTERRUPT
void lv_draw_dma2d_transfer_complete_interrupt_handler(void)
{
#if LV_DRAW_DMA2D_ASYNC
lv_thread_sync_signal_isr(&g_unit->interrupt_signal);
#endif
}
#endif
lv_draw_dma2d_output_cf_t lv_draw_dma2d_cf_to_dma2d_output_cf(lv_color_format_t cf)
{
switch(cf) {
case LV_COLOR_FORMAT_ARGB8888:
case LV_COLOR_FORMAT_XRGB8888:
return LV_DRAW_DMA2D_OUTPUT_CF_ARGB8888;
case LV_COLOR_FORMAT_RGB888:
return LV_DRAW_DMA2D_OUTPUT_CF_RGB888;
case LV_COLOR_FORMAT_RGB565:
return LV_DRAW_DMA2D_OUTPUT_CF_RGB565;
case LV_COLOR_FORMAT_ARGB1555:
return LV_DRAW_DMA2D_OUTPUT_CF_ARGB1555;
default:
LV_ASSERT_MSG(false, "unsupported output color format");
}
return LV_DRAW_DMA2D_OUTPUT_CF_RGB565;
}
uint32_t lv_draw_dma2d_color_to_dma2d_color(lv_draw_dma2d_output_cf_t cf, lv_color_t color)
{
switch(cf) {
case LV_DRAW_DMA2D_OUTPUT_CF_ARGB8888:
case LV_DRAW_DMA2D_OUTPUT_CF_RGB888:
return lv_color_to_u32(color);
case LV_DRAW_DMA2D_OUTPUT_CF_RGB565:
return lv_color_to_u16(color);
default:
LV_ASSERT_MSG(false, "unsupported output color format");
}
return 0;
}
void lv_draw_dma2d_configure_and_start_transfer(const lv_draw_dma2d_configuration_t * conf)
{
/* number of lines register */
DMA2D->NLR = (conf->w << DMA2D_NLR_PL_Pos) | (conf->h << DMA2D_NLR_NL_Pos);
/* output */
/* output memory address register */
DMA2D->OMAR = (uint32_t)(uintptr_t) conf->output_address;
/* output offset register */
DMA2D->OOR = conf->output_offset;
/* output pixel format converter control register */
DMA2D->OPFCCR = ((uint32_t) conf->output_cf) << DMA2D_OPFCCR_CM_Pos;
/* Fill color. Only for mode LV_DRAW_DMA2D_MODE_REGISTER_TO_MEMORY */
DMA2D->OCOLR = conf->reg_to_mem_mode_color;
/* foreground */
/* foreground memory address register */
DMA2D->FGMAR = (uint32_t)(uintptr_t) conf->fg_address;
/* foreground offset register */
DMA2D->FGOR = conf->fg_offset;
/* foreground color. only for mem-to-mem with blending and fixed-color foreground */
DMA2D->FGCOLR = conf->fg_color;
/* foreground pixel format converter control register */
DMA2D->FGPFCCR = (((uint32_t) conf->fg_cf) << DMA2D_FGPFCCR_CM_Pos)
| (conf->fg_alpha << DMA2D_FGPFCCR_ALPHA_Pos)
| (conf->fg_alpha_mode << DMA2D_FGPFCCR_AM_Pos);
/* background */
DMA2D->BGMAR = (uint32_t)(uintptr_t) conf->bg_address;
DMA2D->BGOR = conf->bg_offset;
DMA2D->BGCOLR = conf->bg_color;
DMA2D->BGPFCCR = (((uint32_t) conf->bg_cf) << DMA2D_BGPFCCR_CM_Pos)
| (conf->bg_alpha << DMA2D_BGPFCCR_ALPHA_Pos)
| (conf->bg_alpha_mode << DMA2D_BGPFCCR_AM_Pos);
/* ensure the DMA2D register values are observed before the start transfer bit is set */
__DSB();
/* start the transfer (also set mode and enable transfer complete interrupt) */
DMA2D->CR = DMA2D_CR_START | (((uint32_t) conf->mode) << DMA2D_CR_MODE_Pos)
#if LV_USE_DRAW_DMA2D_INTERRUPT
| DMA2D_CR_TCIE
#endif
;
}
#if LV_DRAW_DMA2D_CACHE
void lv_draw_dma2d_invalidate_cache(const lv_draw_dma2d_cache_area_t * mem_area)
{
if((SCB->CCR & SCB_CCR_DC_Msk) == 0) return; /* data cache is disabled */
uint32_t rows_remaining = mem_area->height;
uint32_t row_addr = (uint32_t)(uintptr_t) mem_area->first_byte;
uint32_t row_end_addr = 0;
__DSB();
while(rows_remaining) {
uint32_t addr = row_addr & ~(__SCB_DCACHE_LINE_SIZE - 1U);
uint32_t cache_lines = ((((row_addr + mem_area->width_bytes - 1) & ~(__SCB_DCACHE_LINE_SIZE - 1U)) - addr) /
__SCB_DCACHE_LINE_SIZE) + 1;
if(addr == row_end_addr) {
addr += __SCB_DCACHE_LINE_SIZE;
cache_lines--;
}
while(cache_lines) {
SCB->DCIMVAC = addr;
addr += __SCB_DCACHE_LINE_SIZE;
cache_lines--;
}
row_end_addr = addr - __SCB_DCACHE_LINE_SIZE;
row_addr += mem_area->stride;
rows_remaining--;
};
__DSB();
__ISB();
}
void lv_draw_dma2d_clean_cache(const lv_draw_dma2d_cache_area_t * mem_area)
{
if((SCB->CCR & SCB_CCR_DC_Msk) == 0) return; /* data cache is disabled */
uint32_t rows_remaining = mem_area->height;
uint32_t row_addr = (uint32_t)(uintptr_t) mem_area->first_byte;
uint32_t row_end_addr = 0;
__DSB();
while(rows_remaining) {
uint32_t addr = row_addr & ~(__SCB_DCACHE_LINE_SIZE - 1U);
uint32_t cache_lines = ((((row_addr + mem_area->width_bytes - 1) & ~(__SCB_DCACHE_LINE_SIZE - 1U)) - addr) /
__SCB_DCACHE_LINE_SIZE) + 1;
if(addr == row_end_addr) {
addr += __SCB_DCACHE_LINE_SIZE;
cache_lines--;
}
while(cache_lines) {
SCB->DCCMVAC = addr;
addr += __SCB_DCACHE_LINE_SIZE;
cache_lines--;
}
row_end_addr = addr - __SCB_DCACHE_LINE_SIZE;
row_addr += mem_area->stride;
rows_remaining--;
};
__DSB();
__ISB();
}
#endif
/**********************
* STATIC FUNCTIONS
**********************/
static int32_t evaluate_cb(lv_draw_unit_t * draw_unit, lv_draw_task_t * task)
{
switch(task->type) {
case LV_DRAW_TASK_TYPE_FILL: {
lv_draw_fill_dsc_t * dsc = task->draw_dsc;
if(!(dsc->radius == 0
&& dsc->grad.dir == LV_GRAD_DIR_NONE
&& (dsc->base.layer->color_format == LV_COLOR_FORMAT_ARGB8888
|| dsc->base.layer->color_format == LV_COLOR_FORMAT_XRGB8888
|| dsc->base.layer->color_format == LV_COLOR_FORMAT_RGB888
|| dsc->base.layer->color_format == LV_COLOR_FORMAT_RGB565))) {
return 0;
}
}
break;
case LV_DRAW_TASK_TYPE_IMAGE: {
lv_draw_image_dsc_t * dsc = task->draw_dsc;
if(!(dsc->header.cf < LV_COLOR_FORMAT_PROPRIETARY_START
&& dsc->clip_radius == 0
&& dsc->bitmap_mask_src == NULL
&& dsc->sup == NULL
&& dsc->tile == 0
&& dsc->blend_mode == LV_BLEND_MODE_NORMAL
&& dsc->recolor_opa <= LV_OPA_MIN
&& dsc->skew_y == 0
&& dsc->skew_x == 0
&& dsc->scale_x == 256
&& dsc->scale_y == 256
&& dsc->rotation == 0
&& lv_image_src_get_type(dsc->src) == LV_IMAGE_SRC_VARIABLE
&& (dsc->header.cf == LV_COLOR_FORMAT_ARGB8888
|| dsc->header.cf == LV_COLOR_FORMAT_XRGB8888
|| dsc->header.cf == LV_COLOR_FORMAT_RGB888
|| dsc->header.cf == LV_COLOR_FORMAT_RGB565
|| dsc->header.cf == LV_COLOR_FORMAT_ARGB1555)
&& (dsc->base.layer->color_format == LV_COLOR_FORMAT_ARGB8888
|| dsc->base.layer->color_format == LV_COLOR_FORMAT_XRGB8888
|| dsc->base.layer->color_format == LV_COLOR_FORMAT_RGB888
|| dsc->base.layer->color_format == LV_COLOR_FORMAT_RGB565))) {
return 0;
}
}
break;
default:
return 0;
}
task->preferred_draw_unit_id = DRAW_UNIT_ID_DMA2D;
task->preference_score = 0;
return 0;
}
static int32_t dispatch_cb(lv_draw_unit_t * draw_unit, lv_layer_t * layer)
{
lv_draw_dma2d_unit_t * draw_dma2d_unit = (lv_draw_dma2d_unit_t *) draw_unit;
if(draw_dma2d_unit->task_act) {
#if LV_DRAW_DMA2D_ASYNC
/*Return immediately if it's busy with draw task*/
return 0;
#else
if(!check_transfer_completion()) {
return LV_DRAW_UNIT_IDLE;
}
post_transfer_tasks(draw_dma2d_unit);
#endif
}
lv_draw_task_t * t = lv_draw_get_available_task(layer, NULL, DRAW_UNIT_ID_DMA2D);
if(t == NULL) {
return LV_DRAW_UNIT_IDLE;
}
void * buf = lv_draw_layer_alloc_buf(layer);
if(buf == NULL) {
return LV_DRAW_UNIT_IDLE;
}
t->state = LV_DRAW_TASK_STATE_IN_PROGRESS;
t->draw_unit = draw_unit;
draw_dma2d_unit->task_act = t;
if(t->type == LV_DRAW_TASK_TYPE_FILL) {
lv_draw_fill_dsc_t * dsc = t->draw_dsc;
const lv_area_t * coords = &t->area;
lv_area_t clipped_coords;
if(!lv_area_intersect(&clipped_coords, coords, &t->clip_area)) {
return LV_DRAW_UNIT_IDLE;
}
void * dest = lv_draw_layer_go_to_xy(layer,
clipped_coords.x1 - layer->buf_area.x1,
clipped_coords.y1 - layer->buf_area.y1);
if(dsc->opa >= LV_OPA_MAX) {
lv_draw_dma2d_opaque_fill(t,
dest,
lv_area_get_width(&clipped_coords),
lv_area_get_height(&clipped_coords),
lv_draw_buf_width_to_stride(lv_area_get_width(&layer->buf_area), dsc->base.layer->color_format));
}
else {
lv_draw_dma2d_fill(t,
dest,
lv_area_get_width(&clipped_coords),
lv_area_get_height(&clipped_coords),
lv_draw_buf_width_to_stride(lv_area_get_width(&layer->buf_area), dsc->base.layer->color_format));
}
}
else if(t->type == LV_DRAW_TASK_TYPE_IMAGE) {
lv_draw_image_dsc_t * dsc = t->draw_dsc;
const lv_area_t * coords = &t->area;
lv_area_t clipped_coords;
if(!lv_area_intersect(&clipped_coords, coords, &t->clip_area)) {
return LV_DRAW_UNIT_IDLE;
}
void * dest = lv_draw_layer_go_to_xy(layer,
clipped_coords.x1 - layer->buf_area.x1,
clipped_coords.y1 - layer->buf_area.y1);
if(dsc->opa >= LV_OPA_MAX) {
lv_draw_dma2d_opaque_image(
t,
dest,
&clipped_coords,
lv_draw_buf_width_to_stride(lv_area_get_width(&layer->buf_area), dsc->base.layer->color_format));
}
else {
lv_draw_dma2d_image(
t,
dest,
&clipped_coords,
lv_draw_buf_width_to_stride(lv_area_get_width(&layer->buf_area), dsc->base.layer->color_format));
}
}
#if !LV_DRAW_DMA2D_ASYNC
lv_draw_dispatch_request();
#endif
return 1;
}
static int32_t delete_cb(lv_draw_unit_t * draw_unit)
{
return 0;
}
#if LV_DRAW_DMA2D_ASYNC
static void thread_cb(void * arg)
{
lv_draw_dma2d_unit_t * u = arg;
lv_thread_sync_init(&u->interrupt_signal);
while(1) {
do {
lv_thread_sync_wait(&u->interrupt_signal);
} while(u->task_act != NULL);
post_transfer_tasks(u);
lv_draw_dispatch_request();
}
}
#endif
#if !LV_DRAW_DMA2D_ASYNC
static bool check_transfer_completion(void)
{
return !(DMA2D->CR & DMA2D_CR_START);
}
#endif
static void post_transfer_tasks(lv_draw_dma2d_unit_t * u)
{
#if LV_DRAW_DMA2D_CACHE
lv_draw_dma2d_invalidate_cache(&u->writing_area);
#endif
u->task_act->state = LV_DRAW_TASK_STATE_READY;
u->task_act = NULL;
}
#endif /*LV_USE_DRAW_DMA2D*/
@@ -0,0 +1,49 @@
/**
* @file lv_draw_dma2d.h
*
*/
#ifndef LV_DRAW_DMA2D_H
#define LV_DRAW_DMA2D_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../../lv_conf_internal.h"
#if LV_USE_DRAW_DMA2D
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* GLOBAL PROTOTYPES
**********************/
void lv_draw_dma2d_init(void);
void lv_draw_dma2d_deinit(void);
#if LV_USE_DRAW_DMA2D_INTERRUPT
void lv_draw_dma2d_transfer_complete_interrupt_handler(void);
#endif
/**********************
* MACROS
**********************/
#endif /*LV_USE_DRAW_DMA2D*/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_DRAW_DMA2D_H*/
@@ -0,0 +1,127 @@
/**
* @file lv_draw_dma2d_fill.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_draw_dma2d_private.h"
#if LV_USE_DRAW_DMA2D
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
void lv_draw_dma2d_opaque_fill(lv_draw_task_t * t, void * first_pixel, int32_t w, int32_t h, int32_t stride)
{
lv_draw_fill_dsc_t * dsc = t->draw_dsc;
lv_color_format_t cf = dsc->base.layer->color_format;
lv_draw_dma2d_output_cf_t output_cf = lv_draw_dma2d_cf_to_dma2d_output_cf(cf);
uint32_t cf_size = LV_COLOR_FORMAT_GET_SIZE(cf);
uint32_t reg_to_mem_color = lv_draw_dma2d_color_to_dma2d_color(output_cf, dsc->color);
#if LV_DRAW_DMA2D_CACHE
lv_draw_dma2d_cache_area_t cache_area = {
.first_byte = first_pixel,
.width_bytes = w * cf_size,
.height = h,
.stride = stride
};
lv_draw_dma2d_unit_t * u = (lv_draw_dma2d_unit_t *) t->draw_unit;
lv_memcpy(&u->writing_area, &cache_area, sizeof(lv_draw_dma2d_cache_area_t));
#endif
lv_draw_dma2d_configuration_t conf = {
.mode = LV_DRAW_DMA2D_MODE_REGISTER_TO_MEMORY,
.w = w,
.h = h,
.output_address = first_pixel,
.output_offset = (stride / cf_size) - w,
.output_cf = output_cf,
.reg_to_mem_mode_color = reg_to_mem_color
};
lv_draw_dma2d_configure_and_start_transfer(&conf);
}
void lv_draw_dma2d_fill(lv_draw_task_t * t, void * first_pixel, int32_t w, int32_t h, int32_t stride)
{
lv_draw_fill_dsc_t * dsc = t->draw_dsc;
lv_color_t color = dsc->color;
lv_color_format_t cf = dsc->base.layer->color_format;
lv_opa_t opa = dsc->opa;
lv_draw_dma2d_output_cf_t output_cf = lv_draw_dma2d_cf_to_dma2d_output_cf(cf);
uint32_t cf_size = LV_COLOR_FORMAT_GET_SIZE(cf);
#if LV_DRAW_DMA2D_CACHE
lv_draw_dma2d_cache_area_t cache_area = {
.first_byte = first_pixel,
.width_bytes = w * cf_size,
.height = h,
.stride = stride
};
lv_draw_dma2d_unit_t * u = (lv_draw_dma2d_unit_t *) t->draw_unit;
lv_memcpy(&u->writing_area, &cache_area, sizeof(lv_draw_dma2d_cache_area_t));
/* make sure the background area DMA2D is blending is up-to-date in main memory */
lv_draw_dma2d_clean_cache(&cache_area);
#endif
uint32_t output_offset = (stride / cf_size) - w;
lv_draw_dma2d_configuration_t conf = {
.mode = LV_DRAW_DMA2D_MODE_MEMORY_TO_MEMORY_WITH_BLENDING_AND_FIXED_COLOR_FG,
.w = w,
.h = h,
.output_address = first_pixel,
.output_offset = output_offset,
.output_cf = output_cf,
.fg_color = lv_color_to_u32(color),
.fg_alpha_mode = LV_DRAW_DMA2D_ALPHA_MODE_REPLACE_ALPHA_CHANNEL,
.fg_alpha = opa,
.bg_address = first_pixel,
.bg_offset = output_offset,
.bg_cf = (lv_draw_dma2d_fgbg_cf_t) output_cf
};
/* Background alpha channel should be treated as 0xFF if the cf is XRGB */
if(cf == LV_COLOR_FORMAT_XRGB8888) {
conf.bg_alpha_mode = LV_DRAW_DMA2D_ALPHA_MODE_REPLACE_ALPHA_CHANNEL;
conf.bg_alpha = 0xff;
}
lv_draw_dma2d_configure_and_start_transfer(&conf);
}
/**********************
* STATIC FUNCTIONS
**********************/
#endif /*LV_USE_DRAW_DMA2D*/
@@ -0,0 +1,210 @@
/**
* @file lv_draw_dma2d_image.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_draw_dma2d_private.h"
#if LV_USE_DRAW_DMA2D
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
void lv_draw_dma2d_opaque_image(lv_draw_task_t * t, void * dest_first_pixel, lv_area_t * clipped_coords,
int32_t dest_stride)
{
int32_t w = lv_area_get_width(clipped_coords);
int32_t h = lv_area_get_height(clipped_coords);
lv_draw_image_dsc_t * dsc = t->draw_dsc;
lv_color_format_t output_cf = dsc->base.layer->color_format;
lv_color_format_t image_cf = dsc->header.cf;
lv_draw_dma2d_output_cf_t output_cf_dma2d = lv_draw_dma2d_cf_to_dma2d_output_cf(output_cf);
uint32_t output_cf_size = LV_COLOR_FORMAT_GET_SIZE(output_cf);
lv_draw_dma2d_fgbg_cf_t image_cf_dma2d = (lv_draw_dma2d_fgbg_cf_t) lv_draw_dma2d_cf_to_dma2d_output_cf(image_cf);
uint32_t image_cf_size = LV_COLOR_FORMAT_GET_SIZE(image_cf);
const lv_image_dsc_t * img_dsc = dsc->src;
uint32_t image_stride = img_dsc->header.stride;
if(image_stride == 0) image_stride = image_cf_size * img_dsc->header.w;
#if LV_DRAW_DMA2D_CACHE
lv_draw_dma2d_cache_area_t dest_area = {
.first_byte = dest_first_pixel,
.width_bytes = w * output_cf_size,
.height = h,
.stride = dest_stride
};
lv_draw_dma2d_unit_t * u = (lv_draw_dma2d_unit_t *) t->draw_unit;
lv_memcpy(&u->writing_area, &dest_area, sizeof(lv_draw_dma2d_cache_area_t));
if(lv_color_format_has_alpha(image_cf)) {
/* make sure the background area DMA2D is blending is up-to-date in main memory */
lv_draw_dma2d_clean_cache(&dest_area);
}
#endif
const void * image_first_byte = img_dsc->data
+ (image_stride * (clipped_coords->y1 - dsc->image_area.y1))
+ (image_cf_size * (clipped_coords->x1 - dsc->image_area.x1));
#if LV_DRAW_DMA2D_CACHE
lv_draw_dma2d_cache_area_t src_area = {
.first_byte = image_first_byte,
.width_bytes = w * image_cf_size,
.height = h,
.stride = image_stride
};
/* make sure the image area is up-to-date in main memory for DMA2D */
lv_draw_dma2d_clean_cache(&src_area);
#endif
uint32_t output_offset = (dest_stride / output_cf_size) - w;
lv_draw_dma2d_configuration_t conf = {
.mode = LV_DRAW_DMA2D_MODE_MEMORY_TO_MEMORY_WITH_PFC,
.w = w,
.h = h,
.output_address = dest_first_pixel,
.output_offset = output_offset,
.output_cf = output_cf_dma2d,
.fg_address = image_first_byte,
.fg_offset = (image_stride / image_cf_size) - w,
.fg_cf = image_cf_dma2d
};
/* only process the background if the image might be transparent */
if(lv_color_format_has_alpha(image_cf)) {
conf.mode = LV_DRAW_DMA2D_MODE_MEMORY_TO_MEMORY_WITH_BLENDING;
conf.bg_address = dest_first_pixel;
conf.bg_offset = output_offset;
conf.bg_cf = output_cf_dma2d;
}
/* Alpha channel should be treated as 0xFF if the cf is XRGB */
if(image_cf == LV_COLOR_FORMAT_XRGB8888) {
conf.fg_alpha_mode = LV_DRAW_DMA2D_ALPHA_MODE_REPLACE_ALPHA_CHANNEL;
conf.fg_alpha = 0xff;
}
if(output_cf == LV_COLOR_FORMAT_XRGB8888) {
conf.bg_alpha_mode = LV_DRAW_DMA2D_ALPHA_MODE_REPLACE_ALPHA_CHANNEL;
conf.bg_alpha = 0xff;
}
lv_draw_dma2d_configure_and_start_transfer(&conf);
}
void lv_draw_dma2d_image(lv_draw_task_t * t, void * dest_first_pixel, lv_area_t * clipped_coords,
int32_t dest_stride)
{
int32_t w = lv_area_get_width(clipped_coords);
int32_t h = lv_area_get_height(clipped_coords);
lv_draw_image_dsc_t * dsc = t->draw_dsc;
lv_color_format_t output_cf = dsc->base.layer->color_format;
lv_color_format_t image_cf = dsc->header.cf;
lv_opa_t opa = dsc->opa;
lv_draw_dma2d_output_cf_t output_cf_dma2d = lv_draw_dma2d_cf_to_dma2d_output_cf(output_cf);
uint32_t output_cf_size = LV_COLOR_FORMAT_GET_SIZE(output_cf);
lv_draw_dma2d_fgbg_cf_t image_cf_dma2d = (lv_draw_dma2d_fgbg_cf_t) lv_draw_dma2d_cf_to_dma2d_output_cf(image_cf);
uint32_t image_cf_size = LV_COLOR_FORMAT_GET_SIZE(image_cf);
const lv_image_dsc_t * img_dsc = dsc->src;
uint32_t image_stride = img_dsc->header.stride;
if(image_stride == 0) image_stride = image_cf_size * img_dsc->header.w;
#if LV_DRAW_DMA2D_CACHE
lv_draw_dma2d_cache_area_t dest_area = {
.first_byte = dest_first_pixel,
.width_bytes = w * output_cf_size,
.height = h,
.stride = dest_stride
};
lv_draw_dma2d_unit_t * u = (lv_draw_dma2d_unit_t *) t->draw_unit;
lv_memcpy(&u->writing_area, &dest_area, sizeof(lv_draw_dma2d_cache_area_t));
/* make sure the background area DMA2D is blending is up-to-date in main memory */
lv_draw_dma2d_clean_cache(&dest_area);
#endif
const void * image_first_byte = img_dsc->data
+ (image_stride * (clipped_coords->y1 - dsc->image_area.y1))
+ (image_cf_size * (clipped_coords->x1 - dsc->image_area.x1));
#if LV_DRAW_DMA2D_CACHE
lv_draw_dma2d_cache_area_t src_area = {
.first_byte = image_first_byte,
.width_bytes = w * image_cf_size,
.height = h,
.stride = image_stride
};
/* make sure the image area is up-to-date in main memory for DMA2D */
lv_draw_dma2d_clean_cache(&src_area);
#endif
uint32_t output_offset = (dest_stride / output_cf_size) - w;
lv_draw_dma2d_configuration_t conf = {
.mode = LV_DRAW_DMA2D_MODE_MEMORY_TO_MEMORY_WITH_BLENDING,
.w = w,
.h = h,
.output_address = dest_first_pixel,
.output_offset = output_offset,
.output_cf = output_cf_dma2d,
.fg_address = image_first_byte,
.fg_offset = (image_stride / image_cf_size) - w,
.fg_cf = image_cf_dma2d,
.fg_alpha_mode = LV_DRAW_DMA2D_ALPHA_MODE_MULTIPLY_IMAGE_ALPHA_CHANNEL,
.fg_alpha = opa,
.bg_address = dest_first_pixel,
.bg_offset = output_offset,
.bg_cf = output_cf_dma2d,
};
/* Alpha channel should be treated as 0xFF if the cf is XRGB */
if(image_cf == LV_COLOR_FORMAT_XRGB8888) {
conf.fg_alpha_mode = LV_DRAW_DMA2D_ALPHA_MODE_REPLACE_ALPHA_CHANNEL;
}
if(output_cf == LV_COLOR_FORMAT_XRGB8888) {
conf.bg_alpha_mode = LV_DRAW_DMA2D_ALPHA_MODE_REPLACE_ALPHA_CHANNEL;
conf.bg_alpha = 0xff;
}
lv_draw_dma2d_configure_and_start_transfer(&conf);
}
/**********************
* STATIC FUNCTIONS
**********************/
#endif /*LV_USE_DRAW_DMA2D*/
@@ -0,0 +1,156 @@
/**
* @file lv_draw_dma2d_private.h
*
*/
#ifndef LV_DRAW_DMA2D_PRIVATE_H
#define LV_DRAW_DMA2D_PRIVATE_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "lv_draw_dma2d.h"
#if LV_USE_DRAW_DMA2D
#include "../lv_draw_private.h"
#include "../sw/lv_draw_sw.h"
#include LV_DRAW_DMA2D_HAL_INCLUDE
/*********************
* DEFINES
*********************/
#if LV_USE_DRAW_DMA2D_INTERRUPT && LV_USE_OS
#define LV_DRAW_DMA2D_ASYNC 1
#else
#define LV_DRAW_DMA2D_ASYNC 0
#endif
#if defined(__CORTEX_M) && (__CORTEX_M == 7)
#define LV_DRAW_DMA2D_CACHE 1
#else
#define LV_DRAW_DMA2D_CACHE 0
#endif
/**********************
* TYPEDEFS
**********************/
typedef enum {
LV_DRAW_DMA2D_OUTPUT_CF_ARGB8888 = 0,
LV_DRAW_DMA2D_OUTPUT_CF_RGB888,
LV_DRAW_DMA2D_OUTPUT_CF_RGB565,
LV_DRAW_DMA2D_OUTPUT_CF_ARGB1555,
LV_DRAW_DMA2D_OUTPUT_CF_ARGB4444
} lv_draw_dma2d_output_cf_t;
typedef enum {
LV_DRAW_DMA2D_FGBG_CF_ARGB8888 = 0,
LV_DRAW_DMA2D_FGBG_CF_RGB888,
LV_DRAW_DMA2D_FGBG_CF_RGB565,
LV_DRAW_DMA2D_FGBG_CF_ARGB1555,
LV_DRAW_DMA2D_FGBG_CF_ARGB4444,
LV_DRAW_DMA2D_FGBG_CF_L8,
LV_DRAW_DMA2D_FGBG_CF_AL44,
LV_DRAW_DMA2D_FGBG_CF_AL88,
LV_DRAW_DMA2D_FGBG_CF_L4,
LV_DRAW_DMA2D_FGBG_CF_A8,
LV_DRAW_DMA2D_FGBG_CF_A4,
LV_DRAW_DMA2D_FGBG_CF_YCBCR
} lv_draw_dma2d_fgbg_cf_t;
typedef enum {
LV_DRAW_DMA2D_MODE_MEMORY_TO_MEMORY = 0,
LV_DRAW_DMA2D_MODE_MEMORY_TO_MEMORY_WITH_PFC,
LV_DRAW_DMA2D_MODE_MEMORY_TO_MEMORY_WITH_BLENDING,
LV_DRAW_DMA2D_MODE_REGISTER_TO_MEMORY,
LV_DRAW_DMA2D_MODE_MEMORY_TO_MEMORY_WITH_BLENDING_AND_FIXED_COLOR_FG,
LV_DRAW_DMA2D_MODE_MEMORY_TO_MEMORY_WITH_BLENDING_AND_FIXED_COLOR_BG
} lv_draw_dma2d_mode_t;
typedef enum {
LV_DRAW_DMA2D_ALPHA_MODE_NO_MODIFY_IMAGE_ALPHA_CHANNEL = 0,
LV_DRAW_DMA2D_ALPHA_MODE_REPLACE_ALPHA_CHANNEL,
LV_DRAW_DMA2D_ALPHA_MODE_MULTIPLY_IMAGE_ALPHA_CHANNEL
} lv_draw_dma2d_alpha_mode_t;
typedef struct {
lv_draw_dma2d_mode_t mode;
uint32_t w;
uint32_t h;
void * output_address;
uint32_t output_offset;
lv_draw_dma2d_output_cf_t output_cf;
uint32_t reg_to_mem_mode_color;
const void * fg_address;
uint32_t fg_offset;
lv_draw_dma2d_fgbg_cf_t fg_cf;
uint32_t fg_color;
uint32_t fg_alpha_mode;
uint32_t fg_alpha;
const void * bg_address;
uint32_t bg_offset;
lv_draw_dma2d_fgbg_cf_t bg_cf;
uint32_t bg_color;
uint32_t bg_alpha_mode;
uint32_t bg_alpha;
} lv_draw_dma2d_configuration_t;
typedef struct {
const void * first_byte;
uint32_t width_bytes;
uint32_t height;
uint32_t stride;
} lv_draw_dma2d_cache_area_t;
typedef struct {
lv_draw_unit_t base_unit;
lv_draw_task_t * volatile task_act;
#if LV_DRAW_DMA2D_CACHE
lv_draw_dma2d_cache_area_t writing_area;
#endif
#if LV_DRAW_DMA2D_ASYNC
lv_thread_t thread;
lv_thread_sync_t interrupt_signal;
#endif
} lv_draw_dma2d_unit_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
void lv_draw_dma2d_opaque_fill(lv_draw_task_t * t, void * first_pixel, int32_t w, int32_t h, int32_t stride);
void lv_draw_dma2d_fill(lv_draw_task_t * t, void * first_pixel, int32_t w, int32_t h, int32_t stride);
void lv_draw_dma2d_opaque_image(lv_draw_task_t * t, void * dest_first_pixel, lv_area_t * clipped_coords,
int32_t dest_stride);
void lv_draw_dma2d_image(lv_draw_task_t * t, void * dest_first_pixel, lv_area_t * clipped_coords,
int32_t dest_stride);
lv_draw_dma2d_output_cf_t lv_draw_dma2d_cf_to_dma2d_output_cf(lv_color_format_t cf);
uint32_t lv_draw_dma2d_color_to_dma2d_color(lv_draw_dma2d_output_cf_t cf, lv_color_t color);
void lv_draw_dma2d_configure_and_start_transfer(const lv_draw_dma2d_configuration_t * conf);
#if LV_DRAW_DMA2D_CACHE
void lv_draw_dma2d_invalidate_cache(const lv_draw_dma2d_cache_area_t * mem_area);
void lv_draw_dma2d_clean_cache(const lv_draw_dma2d_cache_area_t * mem_area);
#endif
/**********************
* MACROS
**********************/
#endif /*LV_USE_DRAW_DMA2D*/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_DRAW_DMA2D_PRIVATE_H*/
+683
View File
@@ -0,0 +1,683 @@
/**
* @file lv_draw.c
*
*/
/**
* Modified by NXP in 2024
*/
/*********************
* INCLUDES
*********************/
#include "../misc/lv_area_private.h"
#include "../misc/lv_assert.h"
#include "lv_draw_private.h"
#include "lv_draw_mask_private.h"
#include "lv_draw_vector_private.h"
#include "lv_draw_3d.h"
#include "sw/lv_draw_sw.h"
#include "../display/lv_display_private.h"
#include "../core/lv_global.h"
#include "../core/lv_refr_private.h"
#include "../stdlib/lv_string.h"
/*********************
* DEFINES
*********************/
#define _draw_info LV_GLOBAL_DEFAULT()->draw_info
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static bool is_independent(lv_layer_t * layer, lv_draw_task_t * t_check);
static void cleanup_task(lv_draw_task_t * t, lv_display_t * disp);
static inline size_t get_draw_dsc_size(lv_draw_task_type_t type);
static lv_draw_task_t * get_first_available_task(lv_layer_t * layer);
#if LV_LOG_LEVEL <= LV_LOG_LEVEL_INFO
static inline uint32_t get_layer_size_kb(uint32_t size_byte)
{
return (size_byte + 1023) >> 10;
}
#endif
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
void lv_draw_init(void)
{
#if LV_USE_OS
lv_thread_sync_init(&_draw_info.sync);
#endif
}
void lv_draw_deinit(void)
{
#if LV_USE_OS
lv_thread_sync_delete(&_draw_info.sync);
#endif
lv_draw_unit_t * u = _draw_info.unit_head;
while(u) {
lv_draw_unit_t * cur_unit = u;
u = u->next;
if(cur_unit->delete_cb) cur_unit->delete_cb(cur_unit);
lv_free(cur_unit);
}
_draw_info.unit_head = NULL;
}
void * lv_draw_create_unit(size_t size)
{
lv_draw_unit_t * new_unit = lv_malloc_zeroed(size);
LV_ASSERT_MALLOC(new_unit);
new_unit->next = _draw_info.unit_head;
_draw_info.unit_head = new_unit;
_draw_info.unit_cnt++;
new_unit->idx = _draw_info.unit_cnt;
return new_unit;
}
lv_draw_task_t * lv_draw_add_task(lv_layer_t * layer, const lv_area_t * coords, lv_draw_task_type_t type)
{
LV_PROFILER_DRAW_BEGIN;
size_t dsc_size = get_draw_dsc_size(type);
LV_ASSERT_FORMAT_MSG(dsc_size > 0, "Draw task size is 0 for type %d", type);
lv_draw_task_t * new_task = lv_malloc_zeroed(LV_ALIGN_UP(sizeof(lv_draw_task_t), 8) + dsc_size);
LV_ASSERT_MALLOC(new_task);
new_task->area = *coords;
new_task->_real_area = *coords;
new_task->target_layer = layer;
new_task->clip_area = layer->_clip_area;
#if LV_DRAW_TRANSFORM_USE_MATRIX
new_task->matrix = layer->matrix;
#endif
new_task->type = type;
new_task->draw_dsc = (uint8_t *)new_task + LV_ALIGN_UP(sizeof(lv_draw_task_t), 8);
new_task->state = LV_DRAW_TASK_STATE_QUEUED;
/*Find the tail*/
if(layer->draw_task_head == NULL) {
layer->draw_task_head = new_task;
}
else {
lv_draw_task_t * tail = layer->draw_task_head;
while(tail->next) tail = tail->next;
tail->next = new_task;
}
LV_PROFILER_DRAW_END;
return new_task;
}
void lv_draw_finalize_task_creation(lv_layer_t * layer, lv_draw_task_t * t)
{
LV_PROFILER_DRAW_BEGIN;
lv_draw_dsc_base_t * base_dsc = t->draw_dsc;
base_dsc->layer = layer;
lv_draw_global_info_t * info = &_draw_info;
/*Send LV_EVENT_DRAW_TASK_ADDED and dispatch only on the "main" draw_task
*and not on the draw tasks added in the event.
*Sending LV_EVENT_DRAW_TASK_ADDED events might cause recursive event sends and besides
*dispatching might remove the "main" draw task while it's still being used in the event*/
if(info->task_running == false) {
if(base_dsc->obj && lv_obj_has_flag(base_dsc->obj, LV_OBJ_FLAG_SEND_DRAW_TASK_EVENTS)) {
info->task_running = true;
lv_obj_send_event(base_dsc->obj, LV_EVENT_DRAW_TASK_ADDED, t);
info->task_running = false;
}
/*Let the draw units set their preference score*/
t->preference_score = 100;
t->preferred_draw_unit_id = 0;
lv_draw_unit_t * u = info->unit_head;
while(u) {
if(u->evaluate_cb) {
LV_PROFILER_DRAW_BEGIN_TAG("evaluate_cb");
LV_PROFILER_DRAW_BEGIN_TAG(u->name);
u->evaluate_cb(u, t);
LV_PROFILER_DRAW_END_TAG(u->name);
LV_PROFILER_DRAW_END_TAG("evaluate_cb");
}
u = u->next;
}
if(t->preferred_draw_unit_id == LV_DRAW_UNIT_NONE) {
LV_LOG_WARN("the draw task was not taken by any units");
t->state = LV_DRAW_TASK_STATE_READY;
}
else {
lv_draw_dispatch();
}
}
else {
/*Let the draw units set their preference score*/
t->preference_score = 100;
t->preferred_draw_unit_id = 0;
lv_draw_unit_t * u = info->unit_head;
while(u) {
if(u->evaluate_cb) {
LV_PROFILER_DRAW_BEGIN_TAG("evaluate_cb");
LV_PROFILER_DRAW_BEGIN_TAG(u->name);
u->evaluate_cb(u, t);
LV_PROFILER_DRAW_END_TAG(u->name);
LV_PROFILER_DRAW_END_TAG("evaluate_cb");
}
u = u->next;
}
}
LV_PROFILER_DRAW_END;
}
void lv_draw_wait_for_finish(void)
{
#if LV_USE_OS
LV_PROFILER_DRAW_BEGIN;
lv_draw_unit_t * u = _draw_info.unit_head;
while(u) {
if(u->wait_for_finish_cb) {
LV_PROFILER_DRAW_BEGIN_TAG("wait_for_finish_cb");
LV_PROFILER_DRAW_BEGIN_TAG(u->name);
u->wait_for_finish_cb(u);
LV_PROFILER_DRAW_END_TAG(u->name);
LV_PROFILER_DRAW_END_TAG("wait_for_finish_cb");
}
u = u->next;
}
LV_PROFILER_DRAW_END;
#endif
}
void lv_draw_dispatch(void)
{
LV_PROFILER_DRAW_BEGIN;
bool task_dispatched = false;
lv_display_t * disp = lv_display_get_next(NULL);
while(disp) {
lv_layer_t * layer = disp->layer_head;
while(layer) {
if(lv_draw_dispatch_layer(disp, layer))
task_dispatched = true;
layer = layer->next;
}
if(!task_dispatched) {
lv_draw_wait_for_finish();
lv_draw_dispatch_request();
}
disp = lv_display_get_next(disp);
}
LV_PROFILER_DRAW_END;
}
bool lv_draw_dispatch_layer(lv_display_t * disp, lv_layer_t * layer)
{
LV_PROFILER_DRAW_BEGIN;
/*Remove the finished tasks first*/
lv_draw_task_t * t_prev = NULL;
lv_draw_task_t * t = layer->draw_task_head;
lv_draw_task_t * t_next;
bool remove_task = false;
while(t) {
t_next = t->next;
if(t->state == LV_DRAW_TASK_STATE_READY) {
cleanup_task(t, disp);
remove_task = true;
if(t_prev != NULL)
t_prev->next = t_next;
else
layer->draw_task_head = t_next;
}
else {
t_prev = t;
}
t = t_next;
}
bool task_dispatched = false;
/*This layer is ready, enable blending its buffer*/
if(layer->parent && layer->all_tasks_added && layer->draw_task_head == NULL) {
/*Find a draw task with TYPE_LAYER in the layer where the src is this layer*/
lv_draw_task_t * t_src = layer->parent->draw_task_head;
while(t_src) {
if(t_src->type == LV_DRAW_TASK_TYPE_LAYER && t_src->state == LV_DRAW_TASK_STATE_WAITING) {
lv_draw_image_dsc_t * draw_dsc = t_src->draw_dsc;
if(draw_dsc->src == layer) {
t_src->state = LV_DRAW_TASK_STATE_QUEUED;
lv_draw_dispatch_request();
break;
}
}
t_src = t_src->next;
}
}
/*Assign draw tasks to the draw_units*/
else if(remove_task || layer->draw_task_head) {
/*Find a draw unit which is not busy and can take at least one task*/
/*Let all draw units to pick draw tasks*/
lv_draw_unit_t * u = _draw_info.unit_head;
while(u) {
LV_PROFILER_DRAW_BEGIN_TAG("dispatch_cb");
LV_PROFILER_DRAW_BEGIN_TAG(u->name);
int32_t taken_cnt = u->dispatch_cb(u, layer);
LV_PROFILER_DRAW_END_TAG(u->name);
LV_PROFILER_DRAW_END_TAG("dispatch_cb");
if(taken_cnt != LV_DRAW_UNIT_IDLE) task_dispatched = true;
u = u->next;
}
}
LV_PROFILER_DRAW_END;
return task_dispatched;
}
void lv_draw_dispatch_wait_for_request(void)
{
LV_PROFILER_DRAW_BEGIN;
#if LV_USE_OS
lv_thread_sync_wait(&_draw_info.sync);
#else
while(!_draw_info.dispatch_req);
_draw_info.dispatch_req = 0;
#endif
LV_PROFILER_DRAW_END;
}
void lv_draw_dispatch_request(void)
{
LV_PROFILER_DRAW_BEGIN;
#if LV_USE_OS
lv_thread_sync_signal(&_draw_info.sync);
#else
_draw_info.dispatch_req = 1;
#endif
LV_PROFILER_DRAW_END;
}
uint32_t lv_draw_get_unit_count(void)
{
return _draw_info.unit_cnt;
}
lv_draw_task_t * lv_draw_get_available_task(lv_layer_t * layer, lv_draw_task_t * t_prev, uint8_t draw_unit_id)
{
if(_draw_info.unit_cnt == 1) {
return get_first_available_task(layer);
}
else {
return lv_draw_get_next_available_task(layer, t_prev, draw_unit_id);
}
}
lv_draw_task_t * lv_draw_get_next_available_task(lv_layer_t * layer, lv_draw_task_t * t_prev, uint8_t draw_unit_id)
{
LV_PROFILER_DRAW_BEGIN;
/*If the first task is screen sized, there cannot be independent areas*/
if(layer->draw_task_head) {
int32_t hor_res = lv_display_get_horizontal_resolution(lv_refr_get_disp_refreshing());
int32_t ver_res = lv_display_get_vertical_resolution(lv_refr_get_disp_refreshing());
lv_draw_task_t * t = layer->draw_task_head;
if(t->state != LV_DRAW_TASK_STATE_QUEUED &&
t->area.x1 <= 0 && t->area.x2 >= hor_res - 1 &&
t->area.y1 <= 0 && t->area.y2 >= ver_res - 1) {
LV_PROFILER_DRAW_END;
return NULL;
}
}
lv_draw_task_t * t = t_prev ? t_prev->next : layer->draw_task_head;
while(t) {
/*Find a queued and independent task*/
if(t->state == LV_DRAW_TASK_STATE_QUEUED &&
(t->preferred_draw_unit_id == LV_DRAW_UNIT_NONE || t->preferred_draw_unit_id == draw_unit_id) &&
is_independent(layer, t)) {
LV_PROFILER_DRAW_END;
return t;
}
t = t->next;
}
LV_PROFILER_DRAW_END;
return NULL;
}
uint32_t lv_draw_get_dependent_count(lv_draw_task_t * t_check)
{
if(t_check == NULL) return 0;
if(t_check->next == NULL) return 0;
LV_PROFILER_DRAW_BEGIN;
uint32_t cnt = 0;
lv_draw_task_t * t = t_check->next;
while(t) {
if((t->state == LV_DRAW_TASK_STATE_QUEUED || t->state == LV_DRAW_TASK_STATE_WAITING) &&
lv_area_is_on(&t_check->area, &t->area)) {
cnt++;
}
t = t->next;
}
LV_PROFILER_DRAW_END;
return cnt;
}
void lv_layer_init(lv_layer_t * layer)
{
LV_ASSERT_NULL(layer);
lv_memzero(layer, sizeof(lv_layer_t));
lv_layer_reset(layer);
}
void lv_layer_reset(lv_layer_t * layer)
{
LV_ASSERT_NULL(layer);
#if LV_DRAW_TRANSFORM_USE_MATRIX
lv_matrix_identity(&layer->matrix);
#endif
layer->opa = LV_OPA_COVER;
layer->recolor = lv_color32_make(0, 0, 0, 0);
}
lv_layer_t * lv_draw_layer_create(lv_layer_t * parent_layer, lv_color_format_t color_format, const lv_area_t * area)
{
LV_PROFILER_DRAW_BEGIN;
lv_layer_t * new_layer = lv_malloc_zeroed(sizeof(lv_layer_t));
LV_ASSERT_MALLOC(new_layer);
if(new_layer == NULL) {
LV_PROFILER_DRAW_END;
return NULL;
}
lv_draw_layer_init(new_layer, parent_layer, color_format, area);
/*Inherits transparency from parent*/
if(parent_layer) {
new_layer->opa = parent_layer->opa;
new_layer->recolor = parent_layer->recolor;
}
LV_PROFILER_DRAW_END;
return new_layer;
}
void lv_draw_layer_init(lv_layer_t * layer, lv_layer_t * parent_layer, lv_color_format_t color_format,
const lv_area_t * area)
{
LV_PROFILER_DRAW_BEGIN;
lv_layer_init(layer);
lv_display_t * disp = lv_refr_get_disp_refreshing();
layer->parent = parent_layer;
layer->_clip_area = *area;
layer->buf_area = *area;
layer->phy_clip_area = *area;
layer->color_format = color_format;
if(disp->layer_init) disp->layer_init(disp, layer);
if(disp->layer_head) {
lv_layer_t * tail = disp->layer_head;
while(tail->next) tail = tail->next;
tail->next = layer;
}
else {
disp->layer_head = layer;
}
LV_PROFILER_DRAW_END;
}
void * lv_draw_layer_alloc_buf(lv_layer_t * layer)
{
LV_PROFILER_DRAW_BEGIN;
/*If the buffer of the layer is already allocated return it*/
if(layer->draw_buf != NULL) {
LV_PROFILER_DRAW_END;
return layer->draw_buf->data;
}
/*If the buffer of the layer is not allocated yet, allocate it now*/
int32_t w = lv_area_get_width(&layer->buf_area);
int32_t h = lv_area_get_height(&layer->buf_area);
uint32_t layer_size_byte = h * lv_draw_buf_width_to_stride(w, layer->color_format);
#if LV_DRAW_LAYER_MAX_MEMORY > 0
/* Do not allocate the layer if the sum of allocated layer sizes
* will exceed `LV_DRAW_LAYER_MAX_MEMORY` */
if((_draw_info.used_memory_for_layers + layer_size_byte) > LV_DRAW_LAYER_MAX_MEMORY) {
LV_LOG_WARN("LV_DRAW_LAYER_MAX_MEMORY was reached when allocating the layer.");
return NULL;
}
#endif
layer->draw_buf = lv_draw_buf_create(w, h, layer->color_format, 0);
if(layer->draw_buf == NULL) {
LV_LOG_WARN("Allocating layer buffer failed. Try later");
LV_PROFILER_DRAW_END;
return NULL;
}
_draw_info.used_memory_for_layers += layer_size_byte;
LV_LOG_INFO("Layer memory used: %" LV_PRIu32 " kB", get_layer_size_kb(_draw_info.used_memory_for_layers));
if(lv_color_format_has_alpha(layer->color_format)) {
lv_draw_buf_clear(layer->draw_buf, NULL);
}
LV_PROFILER_DRAW_END;
return layer->draw_buf->data;
}
void * lv_draw_layer_go_to_xy(lv_layer_t * layer, int32_t x, int32_t y)
{
return lv_draw_buf_goto_xy(layer->draw_buf, x, y);
}
lv_draw_task_type_t lv_draw_task_get_type(const lv_draw_task_t * t)
{
return t->type;
}
void * lv_draw_task_get_draw_dsc(const lv_draw_task_t * t)
{
return t->draw_dsc;
}
void lv_draw_task_get_area(const lv_draw_task_t * t, lv_area_t * area)
{
*area = t->area;
}
/**********************
* STATIC FUNCTIONS
**********************/
/**
* Check if there are older draw task overlapping the area of `t_check`
* @param layer the draw ctx to search in
* @param t_check check this task if it overlaps with the older ones
* @return true: `t_check` is not overlapping with older tasks so it's independent
*/
static bool is_independent(lv_layer_t * layer, lv_draw_task_t * t_check)
{
LV_PROFILER_DRAW_BEGIN;
lv_draw_task_t * t = layer->draw_task_head;
/*If t_check is outside of the older tasks then it's independent*/
while(t && t != t_check) {
if(t->state != LV_DRAW_TASK_STATE_READY) {
lv_area_t a;
if(lv_area_intersect(&a, &t->_real_area, &t_check->_real_area)) {
LV_PROFILER_DRAW_END;
return false;
}
}
t = t->next;
}
LV_PROFILER_DRAW_END;
return true;
}
/**
* Get the size of the draw descriptor of a draw task
* @param type type of the draw task
* @return size of the draw descriptor in bytes
*/
static inline size_t get_draw_dsc_size(lv_draw_task_type_t type)
{
switch(type) {
case LV_DRAW_TASK_TYPE_NONE:
return 0;
case LV_DRAW_TASK_TYPE_FILL:
return sizeof(lv_draw_fill_dsc_t);
case LV_DRAW_TASK_TYPE_BORDER:
return sizeof(lv_draw_border_dsc_t);
case LV_DRAW_TASK_TYPE_BOX_SHADOW:
return sizeof(lv_draw_box_shadow_dsc_t);
case LV_DRAW_TASK_TYPE_LETTER:
return sizeof(lv_draw_letter_dsc_t);
case LV_DRAW_TASK_TYPE_LABEL:
return sizeof(lv_draw_label_dsc_t);
case LV_DRAW_TASK_TYPE_IMAGE:
return sizeof(lv_draw_image_dsc_t);
case LV_DRAW_TASK_TYPE_LAYER:
return sizeof(lv_draw_image_dsc_t);
case LV_DRAW_TASK_TYPE_LINE:
return sizeof(lv_draw_line_dsc_t);
case LV_DRAW_TASK_TYPE_ARC:
return sizeof(lv_draw_arc_dsc_t);
case LV_DRAW_TASK_TYPE_TRIANGLE:
return sizeof(lv_draw_triangle_dsc_t);
case LV_DRAW_TASK_TYPE_MASK_RECTANGLE:
return sizeof(lv_draw_mask_rect_dsc_t);
/* no struct match for LV_DRAW_TASK_TYPE_MASK_BITMAP, set it to zero now */
case LV_DRAW_TASK_TYPE_MASK_BITMAP:
return 0;
#if LV_USE_VECTOR_GRAPHIC
case LV_DRAW_TASK_TYPE_VECTOR:
return sizeof(lv_draw_vector_task_dsc_t);
#endif
#if LV_USE_3DTEXTURE
case LV_DRAW_TASK_TYPE_3D:
return sizeof(lv_draw_3d_dsc_t);
#endif
/* Note that default is not added here because when adding new draw task type,
* if forget to add case, the compiler will automatically report a warning.
*/
}
return 0;
}
/**
* Clean-up resources allocated by a finished task
* @param t pointer to a draw task
* @param disp pointer to a display on which the task was drawn
*/
static void cleanup_task(lv_draw_task_t * t, lv_display_t * disp)
{
LV_PROFILER_DRAW_BEGIN;
/*If it was layer drawing free the layer too*/
if(t->type == LV_DRAW_TASK_TYPE_LAYER) {
lv_draw_image_dsc_t * draw_image_dsc = t->draw_dsc;
lv_layer_t * layer_drawn = (lv_layer_t *)draw_image_dsc->src;
if(layer_drawn->draw_buf) {
int32_t h = lv_area_get_height(&layer_drawn->buf_area);
uint32_t layer_size_byte = h * layer_drawn->draw_buf->header.stride;
if(_draw_info.used_memory_for_layers >= layer_size_byte) {
_draw_info.used_memory_for_layers -= layer_size_byte;
}
else {
_draw_info.used_memory_for_layers = 0;
LV_LOG_WARN("More layers were freed than allocated");
}
LV_LOG_INFO("Layer memory used: %" LV_PRIu32 " kB", get_layer_size_kb(_draw_info.used_memory_for_layers));
lv_draw_buf_destroy(layer_drawn->draw_buf);
layer_drawn->draw_buf = NULL;
}
/*Remove the layer from the display's*/
if(disp) {
lv_layer_t * l2 = disp->layer_head;
while(l2) {
if(l2->next == layer_drawn) {
l2->next = layer_drawn->next;
break;
}
l2 = l2->next;
}
if(disp->layer_deinit) {
LV_PROFILER_DRAW_BEGIN_TAG("layer_deinit");
disp->layer_deinit(disp, layer_drawn);
LV_PROFILER_DRAW_END_TAG("layer_deinit");
}
lv_free(layer_drawn);
}
}
lv_draw_label_dsc_t * draw_label_dsc = lv_draw_task_get_label_dsc(t);
if(draw_label_dsc && draw_label_dsc->text_local) {
lv_free((void *)draw_label_dsc->text);
draw_label_dsc->text = NULL;
}
lv_free(t);
LV_PROFILER_DRAW_END;
}
static lv_draw_task_t * get_first_available_task(lv_layer_t * layer)
{
LV_PROFILER_DRAW_BEGIN;
/* If there is only 1 draw unit the task can be consumed linearly as
* they are added in the correct order. However, it can happen that
* there is a `LV_DRAW_TASK_TYPE_LAYER` which can be blended only when
* all its tasks are ready. As other areas might be on top of that
* layer-to-blend don't skip it. Instead stop there, so that the
* draw tasks of that layer can be consumed and can be finished.
* After that this layer-to-blenf will have `LV_DRAW_TASK_STATE_QUEUED`
* so it can be blended normally.*/
lv_draw_task_t * t = layer->draw_task_head;
while(t) {
/*Not queued yet, leave this layer while the first task is queued*/
if(t->state != LV_DRAW_TASK_STATE_QUEUED) {
t = NULL;
break;
}
/*It's a supported and queued task, process it*/
else {
break;
}
t = t->next;
}
LV_PROFILER_DRAW_END;
return t;
}
+332
View File
@@ -0,0 +1,332 @@
/**
* @file lv_draw.h
*
*/
/**
* Modified by NXP in 2024
*/
#ifndef LV_DRAW_H
#define LV_DRAW_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../lv_conf_internal.h"
#include "../misc/lv_types.h"
#include "../misc/lv_style.h"
#include "../misc/lv_text.h"
#include "../misc/lv_profiler.h"
#include "../misc/lv_matrix.h"
#include "lv_image_decoder.h"
#include "lv_draw_buf.h"
/*********************
* DEFINES
*********************/
#define LV_DRAW_UNIT_NONE 0
#define LV_DRAW_UNIT_IDLE -1 /**< The draw unit is idle, new dispatching might be requested to try again */
#if LV_DRAW_TRANSFORM_USE_MATRIX
#if !LV_USE_MATRIX
#error "LV_DRAW_TRANSFORM_USE_MATRIX requires LV_USE_MATRIX = 1"
#endif
#endif
/**********************
* TYPEDEFS
**********************/
typedef enum {
LV_DRAW_TASK_TYPE_NONE = 0,
LV_DRAW_TASK_TYPE_FILL,
LV_DRAW_TASK_TYPE_BORDER,
LV_DRAW_TASK_TYPE_BOX_SHADOW,
LV_DRAW_TASK_TYPE_LETTER,
LV_DRAW_TASK_TYPE_LABEL,
LV_DRAW_TASK_TYPE_IMAGE,
LV_DRAW_TASK_TYPE_LAYER,
LV_DRAW_TASK_TYPE_LINE,
LV_DRAW_TASK_TYPE_ARC,
LV_DRAW_TASK_TYPE_TRIANGLE,
LV_DRAW_TASK_TYPE_MASK_RECTANGLE,
LV_DRAW_TASK_TYPE_MASK_BITMAP,
#if LV_USE_VECTOR_GRAPHIC
LV_DRAW_TASK_TYPE_VECTOR,
#endif
#if LV_USE_3DTEXTURE
LV_DRAW_TASK_TYPE_3D,
#endif
} lv_draw_task_type_t;
typedef enum {
LV_DRAW_TASK_STATE_WAITING, /*Waiting for something to be finished. E.g. rendering a layer*/
LV_DRAW_TASK_STATE_QUEUED,
LV_DRAW_TASK_STATE_IN_PROGRESS,
LV_DRAW_TASK_STATE_READY,
} lv_draw_task_state_t;
struct _lv_layer_t {
/** Target draw buffer of the layer*/
lv_draw_buf_t * draw_buf;
/** The absolute coordinates of the buffer */
lv_area_t buf_area;
/** The color format of the layer. LV_COLOR_FORMAT_... */
lv_color_format_t color_format;
/**
* NEVER USE IT DRAW UNITS. USED INTERNALLY DURING DRAW TASK CREATION.
* The current clip area with absolute coordinates, always the same or smaller than `buf_area`
* Can be set before new draw tasks are added to indicate the clip area of the draw tasks.
* Therefore `lv_draw_add_task()` always saves it in the new draw task to know the clip area when the draw task was added.
* During drawing the draw units also sees the saved clip_area and should use it during drawing.
* During drawing the layer's clip area shouldn't be used as it might be already changed for other draw tasks.
*/
lv_area_t _clip_area;
/**
* The physical clipping area relative to the display.
*/
lv_area_t phy_clip_area;
#if LV_DRAW_TRANSFORM_USE_MATRIX
/** Transform matrix to be applied when rendering the layer */
lv_matrix_t matrix;
#endif
/** Opacity of the layer */
lv_opa_t opa;
/*Recolor of the layer*/
lv_color32_t recolor;
/** Partial y offset */
int32_t partial_y_offset;
/** Linked list of draw tasks */
lv_draw_task_t * draw_task_head;
lv_layer_t * parent;
lv_layer_t * next;
bool all_tasks_added;
void * user_data;
};
typedef struct {
/**The widget for which draw descriptor was created */
lv_obj_t * obj;
/**The widget part for which draw descriptor was created */
lv_part_t part;
/**A widget type specific ID (e.g. table row index). See the docs of the given widget.*/
uint32_t id1;
/**A widget type specific ID (e.g. table column index). See the docs of the given widget.*/
uint32_t id2;
/**The target layer */
lv_layer_t * layer;
/**Size of the specific draw descriptor into which this base descriptor is embedded*/
size_t dsc_size;
/**Any custom user data*/
void * user_data;
} lv_draw_dsc_base_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Used internally to initialize the drawing module
*/
void lv_draw_init(void);
/**
* Deinitialize the drawing module
*/
void lv_draw_deinit(void);
/**
* Allocate a new draw unit with the given size and appends it to the list of draw units
* @param size the size to allocate. E.g. `sizeof(my_draw_unit_t)`,
* where the first element of `my_draw_unit_t` is `lv_draw_unit_t`.
*/
void * lv_draw_create_unit(size_t size);
/**
* Add an empty draw task to the draw task list of a layer.
* @param layer pointer to a layer
* @param coords the coordinates of the draw task
* @return the created draw task which needs to be
* further configured e.g. by added a draw descriptor
*/
lv_draw_task_t * lv_draw_add_task(lv_layer_t * layer, const lv_area_t * coords, lv_draw_task_type_t type);
/**
* Needs to be called when a draw task is created and configured.
* It will send an event about the new draw task to the widget
* and assign it to a draw unit.
* @param layer pointer to a layer
* @param t pointer to a draw task
*/
void lv_draw_finalize_task_creation(lv_layer_t * layer, lv_draw_task_t * t);
/**
* Try dispatching draw tasks to draw units
*/
void lv_draw_dispatch(void);
/**
* Used internally to try dispatching draw tasks of a specific layer
* @param disp pointer to a display on which the dispatching was requested
* @param layer pointer to a layer
* @return at least one draw task is being rendered (maybe it was taken earlier)
*/
bool lv_draw_dispatch_layer(lv_display_t * disp, lv_layer_t * layer);
/**
* Wait for a new dispatch request.
* It's blocking if `LV_USE_OS == 0` else it yields
*/
void lv_draw_dispatch_wait_for_request(void);
/**
* Wait for draw finish in case of asynchronous task execution.
* If `LV_USE_OS == 0` it just return.
*/
void lv_draw_wait_for_finish(void);
/**
* When a draw unit finished a draw task it needs to request dispatching
* to let LVGL assign a new draw task to it
*/
void lv_draw_dispatch_request(void);
/**
* Get the total number of draw units.
*/
uint32_t lv_draw_get_unit_count(void);
/**
* If there is only one draw unit check the first draw task if it's available.
* If there are multiple draw units call `lv_draw_get_next_available_task` to find a task.
* @param layer the draw layer to search in
* @param t_prev continue searching from this task
* @param draw_unit_id check the task where `preferred_draw_unit_id` equals this value or `LV_DRAW_UNIT_NONE`
* @return an available draw task or NULL if there is not any
*/
lv_draw_task_t * lv_draw_get_available_task(lv_layer_t * layer, lv_draw_task_t * t_prev, uint8_t draw_unit_id);
/**
* Find and available draw task
* @param layer the draw layer to search in
* @param t_prev continue searching from this task
* @param draw_unit_id check the task where `preferred_draw_unit_id` equals this value or `LV_DRAW_UNIT_NONE`
* @return an available draw task or NULL if there is not any
*/
lv_draw_task_t * lv_draw_get_next_available_task(lv_layer_t * layer, lv_draw_task_t * t_prev, uint8_t draw_unit_id);
/**
* Tell how many draw task are waiting to be drawn on the area of `t_check`.
* It can be used to determine if a GPU shall combine many draw tasks into one or not.
* If a lot of tasks are waiting for the current ones it makes sense to draw them one-by-one
* to not block the dependent tasks' rendering
* @param t_check the task whose dependent tasks shall be counted
* @return number of tasks depending on `t_check`
*/
uint32_t lv_draw_get_dependent_count(lv_draw_task_t * t_check);
/**
* Initialize a layer
* @param layer pointer to a layer to initialize
*/
void lv_layer_init(lv_layer_t * layer);
/**
* Reset the layer to a drawable state
* @param layer pointer to a layer to reset
*/
void lv_layer_reset(lv_layer_t * layer);
/**
* Create (allocate) a new layer on a parent layer
* @param parent_layer the parent layer to which the layer will be merged when it's rendered
* @param color_format the color format of the layer
* @param area the areas of the layer (absolute coordinates)
* @return the new target_layer or NULL on error
*/
lv_layer_t * lv_draw_layer_create(lv_layer_t * parent_layer, lv_color_format_t color_format, const lv_area_t * area);
/**
* Initialize a layer which is allocated by the user
* @param layer pointer the layer to initialize (its lifetime needs to be managed by the user)
* @param parent_layer the parent layer to which the layer will be merged when it's rendered
* @param color_format the color format of the layer
* @param area the areas of the layer (absolute coordinates)
* @return the new target_layer or NULL on error
*/
void lv_draw_layer_init(lv_layer_t * layer, lv_layer_t * parent_layer, lv_color_format_t color_format,
const lv_area_t * area);
/**
* Try to allocate a buffer for the layer.
* @param layer pointer to a layer
* @return pointer to the allocated aligned buffer or NULL on failure
*/
void * lv_draw_layer_alloc_buf(lv_layer_t * layer);
/**
* Got to a pixel at X and Y coordinate on a layer
* @param layer pointer to a layer
* @param x the target X coordinate
* @param y the target X coordinate
* @return `buf` offset to point to the given X and Y coordinate
*/
void * lv_draw_layer_go_to_xy(lv_layer_t * layer, int32_t x, int32_t y);
/**
* Get the type of a draw task
* @param t the draw task to get the type of
* @return the draw task type
*/
lv_draw_task_type_t lv_draw_task_get_type(const lv_draw_task_t * t);
/**
* Get the draw descriptor of a draw task
* @param t the draw task to get the draw descriptor of
* @return a void pointer to the draw descriptor
*/
void * lv_draw_task_get_draw_dsc(const lv_draw_task_t * t);
/**
* Get the draw area of a draw task
* @param t the draw task to get the draw area of
* @param area the destination where the draw area will be stored
*/
void lv_draw_task_get_area(const lv_draw_task_t * t, lv_area_t * area);
/**********************
* GLOBAL VARIABLES
**********************/
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_DRAW_H*/
@@ -0,0 +1,69 @@
/**
* @file lv_draw_3d.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_draw_3d.h"
#if LV_USE_3DTEXTURE
#include "lv_draw_private.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
void lv_draw_3d_dsc_init(lv_draw_3d_dsc_t * dsc)
{
lv_memzero(dsc, sizeof(lv_draw_3d_dsc_t));
dsc->base.dsc_size = sizeof(lv_draw_3d_dsc_t);
dsc->tex_id = LV_3DTEXTURE_ID_NULL;
dsc->opa = LV_OPA_COVER;
}
lv_draw_3d_dsc_t * lv_draw_task_get_3d_dsc(lv_draw_task_t * task)
{
return task->type == LV_DRAW_TASK_TYPE_3D ? (lv_draw_3d_dsc_t *)task->draw_dsc : NULL;
}
void lv_draw_3d(lv_layer_t * layer, const lv_draw_3d_dsc_t * dsc, const lv_area_t * coords)
{
LV_PROFILER_DRAW_BEGIN;
lv_draw_task_t * t = lv_draw_add_task(layer, coords, LV_DRAW_TASK_TYPE_3D);
lv_memcpy(t->draw_dsc, dsc, sizeof(*dsc));
lv_draw_finalize_task_creation(layer, t);
LV_PROFILER_DRAW_END;
}
/**********************
* STATIC FUNCTIONS
**********************/
#endif /*LV_USE_3DTEXTURE*/
@@ -0,0 +1,70 @@
/**
* @file lv_draw_3d.h
*
*/
#ifndef LV_DRAW_3D_H
#define LV_DRAW_3D_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../lv_conf_internal.h"
#if LV_USE_3DTEXTURE
#include "lv_draw.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
typedef struct {
lv_draw_dsc_base_t base;
lv_3dtexture_id_t tex_id;
lv_opa_t opa;
} lv_draw_3d_dsc_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Initialize a 3D draw descriptor
* @param dsc pointer to a draw descriptor
*/
void lv_draw_3d_dsc_init(lv_draw_3d_dsc_t * dsc);
/**
* Try to get a 3D draw descriptor from a draw task.
* @param task draw task
* @return the task's draw descriptor or NULL if the task is not of type LV_DRAW_TASK_TYPE_3D
*/
lv_draw_3d_dsc_t * lv_draw_task_get_3d_dsc(lv_draw_task_t * task);
/**
* Create a 3D draw task
* @param layer pointer to a layer
* @param dsc pointer to an initialized `lv_draw_3d_dsc_t` variable
*/
void lv_draw_3d(lv_layer_t * layer, const lv_draw_3d_dsc_t * dsc, const lv_area_t * coords);
/**********************
* MACROS
**********************/
#endif /*LV_USE_3DTEXTURE*/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_DRAW_3D_H*/
@@ -0,0 +1,172 @@
/**
* @file lv_draw_arc.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_draw_private.h"
#include "../core/lv_obj.h"
#include "lv_draw_arc.h"
#include "../core/lv_obj_event.h"
#include "../stdlib/lv_string.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
void lv_draw_arc_dsc_init(lv_draw_arc_dsc_t * dsc)
{
lv_memzero(dsc, sizeof(lv_draw_arc_dsc_t));
dsc->width = 1;
dsc->opa = LV_OPA_COVER;
dsc->color = lv_color_black();
dsc->base.dsc_size = sizeof(lv_draw_arc_dsc_t);
}
lv_draw_arc_dsc_t * lv_draw_task_get_arc_dsc(lv_draw_task_t * task)
{
return task->type == LV_DRAW_TASK_TYPE_ARC ? (lv_draw_arc_dsc_t *)task->draw_dsc : NULL;
}
void lv_draw_arc(lv_layer_t * layer, const lv_draw_arc_dsc_t * dsc)
{
if(dsc->opa <= LV_OPA_MIN) return;
if(dsc->width == 0) return;
if(dsc->start_angle == dsc->end_angle) return;
LV_PROFILER_DRAW_BEGIN;
lv_area_t a;
a.x1 = dsc->center.x - dsc->radius;
a.y1 = dsc->center.y - dsc->radius;
a.x2 = dsc->center.x + dsc->radius - 1;
a.y2 = dsc->center.y + dsc->radius - 1;
lv_draw_task_t * t = lv_draw_add_task(layer, &a, LV_DRAW_TASK_TYPE_ARC);
lv_memcpy(t->draw_dsc, dsc, sizeof(*dsc));
lv_draw_finalize_task_creation(layer, t);
LV_PROFILER_DRAW_END;
}
void lv_draw_arc_get_area(int32_t x, int32_t y, uint16_t radius, lv_value_precise_t start_angle,
lv_value_precise_t end_angle,
int32_t w, bool rounded, lv_area_t * area)
{
int32_t rout = radius;
int32_t start_angle_int = (int32_t) start_angle;
int32_t end_angle_int = (int32_t) end_angle;
/*Special case: full arc invalidation */
if(end_angle_int == start_angle_int + 360) {
area->x1 = x - rout;
area->y1 = y - rout;
area->x2 = x + rout;
area->y2 = y + rout;
return;
}
if(start_angle_int > 360) start_angle_int -= 360;
if(end_angle_int > 360) end_angle_int -= 360;
int32_t rin = radius - w;
int32_t extra_area = rounded ? w / 2 + 1 : 0;
uint8_t start_quarter = start_angle_int / 90;
uint8_t end_quarter = end_angle_int / 90;
/*360 deg still counts as quarter 3 (360 / 90 would be 4)*/
if(start_quarter == 4) start_quarter = 3;
if(end_quarter == 4) end_quarter = 3;
if(start_quarter == end_quarter && start_angle_int <= end_angle_int) {
if(start_quarter == 0) {
area->y1 = y + ((lv_trigo_sin(start_angle_int) * rin) >> LV_TRIGO_SHIFT) - extra_area;
area->x2 = x + ((lv_trigo_sin(start_angle_int + 90) * rout) >> LV_TRIGO_SHIFT) + extra_area;
area->y2 = y + ((lv_trigo_sin(end_angle_int) * rout) >> LV_TRIGO_SHIFT) + extra_area;
area->x1 = x + ((lv_trigo_sin(end_angle_int + 90) * rin) >> LV_TRIGO_SHIFT) - extra_area;
}
else if(start_quarter == 1) {
area->y2 = y + ((lv_trigo_sin(start_angle_int) * rout) >> LV_TRIGO_SHIFT) + extra_area;
area->x2 = x + ((lv_trigo_sin(start_angle_int + 90) * rin) >> LV_TRIGO_SHIFT) + extra_area;
area->y1 = y + ((lv_trigo_sin(end_angle_int) * rin) >> LV_TRIGO_SHIFT) - extra_area;
area->x1 = x + ((lv_trigo_sin(end_angle_int + 90) * rout) >> LV_TRIGO_SHIFT) - extra_area;
}
else if(start_quarter == 2) {
area->x1 = x + ((lv_trigo_sin(start_angle_int + 90) * rout) >> LV_TRIGO_SHIFT) - extra_area;
area->y2 = y + ((lv_trigo_sin(start_angle_int) * rin) >> LV_TRIGO_SHIFT) + extra_area;
area->y1 = y + ((lv_trigo_sin(end_angle_int) * rout) >> LV_TRIGO_SHIFT) - extra_area;
area->x2 = x + ((lv_trigo_sin(end_angle_int + 90) * rin) >> LV_TRIGO_SHIFT) + extra_area;
}
else if(start_quarter == 3) {
area->x1 = x + ((lv_trigo_sin(start_angle_int + 90) * rin) >> LV_TRIGO_SHIFT) - extra_area;
area->y1 = y + ((lv_trigo_sin(start_angle_int) * rout) >> LV_TRIGO_SHIFT) - extra_area;
area->x2 = x + ((lv_trigo_sin(end_angle_int + 90) * rout) >> LV_TRIGO_SHIFT) + extra_area;
area->y2 = y + ((lv_trigo_sin(end_angle_int) * rin) >> LV_TRIGO_SHIFT) + extra_area;
}
}
else if(start_quarter == 0 && end_quarter == 1) {
area->x1 = x + ((lv_trigo_sin(end_angle_int + 90) * rout) >> LV_TRIGO_SHIFT) - extra_area;
area->y1 = y + ((LV_MIN(lv_trigo_sin(end_angle_int),
lv_trigo_sin(start_angle_int)) * rin) >> LV_TRIGO_SHIFT) - extra_area;
area->x2 = x + ((lv_trigo_sin(start_angle_int + 90) * rout) >> LV_TRIGO_SHIFT) + extra_area;
area->y2 = y + rout + extra_area;
}
else if(start_quarter == 1 && end_quarter == 2) {
area->x1 = x - rout - extra_area;
area->y1 = y + ((lv_trigo_sin(end_angle_int) * rout) >> LV_TRIGO_SHIFT) - extra_area;
area->x2 = x + ((LV_MAX(lv_trigo_sin(start_angle_int + 90),
lv_trigo_sin(end_angle_int + 90)) * rin) >> LV_TRIGO_SHIFT) + extra_area;
area->y2 = y + ((lv_trigo_sin(start_angle_int) * rout) >> LV_TRIGO_SHIFT) + extra_area;
}
else if(start_quarter == 2 && end_quarter == 3) {
area->x1 = x + ((lv_trigo_sin(start_angle_int + 90) * rout) >> LV_TRIGO_SHIFT) - extra_area;
area->y1 = y - rout - extra_area;
area->x2 = x + ((lv_trigo_sin(end_angle_int + 90) * rout) >> LV_TRIGO_SHIFT) + extra_area;
area->y2 = y + (LV_MAX(lv_trigo_sin(end_angle_int) * rin,
lv_trigo_sin(start_angle_int) * rin) >> LV_TRIGO_SHIFT) + extra_area;
}
else if(start_quarter == 3 && end_quarter == 0) {
area->x1 = x + ((LV_MIN(lv_trigo_sin(end_angle_int + 90),
lv_trigo_sin(start_angle_int + 90)) * rin) >> LV_TRIGO_SHIFT) - extra_area;
area->y1 = y + ((lv_trigo_sin(start_angle_int) * rout) >> LV_TRIGO_SHIFT) - extra_area;
area->x2 = x + rout + extra_area;
area->y2 = y + ((lv_trigo_sin(end_angle_int) * rout) >> LV_TRIGO_SHIFT) + extra_area;
}
else {
area->x1 = x - rout;
area->y1 = y - rout;
area->x2 = x + rout;
area->y2 = y + rout;
}
}
/**********************
* STATIC FUNCTIONS
**********************/
@@ -0,0 +1,109 @@
/**
* @file lv_draw_arc.h
*
*/
#ifndef LV_DRAW_ARC_H
#define LV_DRAW_ARC_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../lv_conf_internal.h"
#include "../misc/lv_color.h"
#include "../misc/lv_area.h"
#include "../misc/lv_style.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
typedef struct {
lv_draw_dsc_base_t base;
/**The color of the arc*/
lv_color_t color;
/**The width (thickness) of the arc */
int32_t width;
/**The start angle in 1 degree units (if `LV_USE_FLOAT` is enabled a float number can be also used)
* 0° is the 3 o'clock position, 90° is the 6 o'clock, etc. */
lv_value_precise_t start_angle;
/**The end angle, similarly to start_angle. */
lv_value_precise_t end_angle;
/**The center point of the arc. */
lv_point_t center;
/**The outer radius of the arc*/
uint16_t radius;
/**An image source to be used instead of `color`. `NULL` if unused*/
const void * img_src;
/**Opacity of the arc in 0...255 range.
* LV_OPA_TRANSP, LV_OPA_10, LV_OPA_20, .. LV_OPA_COVER can be used as well*/
lv_opa_t opa;
/**1: Make the arc ends rounded*/
uint8_t rounded : 1;
} lv_draw_arc_dsc_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Initialize an arc draw descriptor.
* @param dsc pointer to a draw descriptor
*/
void lv_draw_arc_dsc_init(lv_draw_arc_dsc_t * dsc);
/**
* Try to get an arc draw descriptor from a draw task.
* @param task draw task
* @return the task's draw descriptor or NULL if the task is not of type LV_DRAW_TASK_TYPE_ARC
*/
lv_draw_arc_dsc_t * lv_draw_task_get_arc_dsc(lv_draw_task_t * task);
/**
* Create an arc draw task.
* @param layer pointer to a layer
* @param dsc pointer to an initialized draw descriptor variable
*/
void lv_draw_arc(lv_layer_t * layer, const lv_draw_arc_dsc_t * dsc);
/**
* Get an area the should be invalidated when the arcs angle changed between start_angle and end_ange
* @param x the x coordinate of the center of the arc
* @param y the y coordinate of the center of the arc
* @param radius the radius of the arc
* @param start_angle the start angle of the arc (0 deg on the bottom, 90 deg on the right)
* @param end_angle the end angle of the arc
* @param w width of the arc
* @param rounded true: the arc is rounded
* @param area store the area to invalidate here
*/
void lv_draw_arc_get_area(int32_t x, int32_t y, uint16_t radius, lv_value_precise_t start_angle,
lv_value_precise_t end_angle,
int32_t w, bool rounded, lv_area_t * area);
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_DRAW_ARC_H*/
@@ -0,0 +1,725 @@
/**
* @file lv_draw_buf.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_draw_buf_private.h"
#include "../misc/lv_types.h"
#include "../stdlib/lv_string.h"
#include "../core/lv_global.h"
#include "../misc/lv_math.h"
#include "../misc/lv_area_private.h"
/*********************
* DEFINES
*********************/
#define default_handlers LV_GLOBAL_DEFAULT()->draw_buf_handlers
#define font_draw_buf_handlers LV_GLOBAL_DEFAULT()->font_draw_buf_handlers
#define image_cache_draw_buf_handlers LV_GLOBAL_DEFAULT()->image_cache_draw_buf_handlers
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static void * buf_malloc(size_t size, lv_color_format_t color_format);
static void buf_free(void * buf);
static void * buf_align(void * buf, lv_color_format_t color_format);
static void * draw_buf_malloc(const lv_draw_buf_handlers_t * handler, size_t size_bytes,
lv_color_format_t color_format);
static void draw_buf_free(const lv_draw_buf_handlers_t * handler, void * buf);
static uint32_t width_to_stride(uint32_t w, lv_color_format_t color_format);
static uint32_t _calculate_draw_buf_size(uint32_t w, uint32_t h, lv_color_format_t cf, uint32_t stride);
static void draw_buf_get_full_area(const lv_draw_buf_t * draw_buf, lv_area_t * full_area);
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
void lv_draw_buf_init_handlers(void)
{
lv_draw_buf_init_with_default_handlers(&default_handlers);
lv_draw_buf_init_with_default_handlers(&font_draw_buf_handlers);
lv_draw_buf_init_with_default_handlers(&image_cache_draw_buf_handlers);
}
void lv_draw_buf_init_with_default_handlers(lv_draw_buf_handlers_t * handlers)
{
lv_draw_buf_handlers_init(handlers, buf_malloc, buf_free, buf_align, NULL, NULL, width_to_stride);
}
void lv_draw_buf_handlers_init(lv_draw_buf_handlers_t * handlers,
lv_draw_buf_malloc_cb buf_malloc_cb,
lv_draw_buf_free_cb buf_free_cb,
lv_draw_buf_align_cb align_pointer_cb,
lv_draw_buf_cache_operation_cb invalidate_cache_cb,
lv_draw_buf_cache_operation_cb flush_cache_cb,
lv_draw_buf_width_to_stride_cb width_to_stride_cb)
{
lv_memzero(handlers, sizeof(lv_draw_buf_handlers_t));
handlers->buf_malloc_cb = buf_malloc_cb;
handlers->buf_free_cb = buf_free_cb;
handlers->align_pointer_cb = align_pointer_cb;
handlers->invalidate_cache_cb = invalidate_cache_cb;
handlers->flush_cache_cb = flush_cache_cb;
handlers->width_to_stride_cb = width_to_stride_cb;
}
lv_draw_buf_handlers_t * lv_draw_buf_get_handlers(void)
{
return &default_handlers;
}
lv_draw_buf_handlers_t * lv_draw_buf_get_font_handlers(void)
{
return &font_draw_buf_handlers;
}
lv_draw_buf_handlers_t * lv_draw_buf_get_image_handlers(void)
{
return &image_cache_draw_buf_handlers;
}
uint32_t lv_draw_buf_width_to_stride(uint32_t w, lv_color_format_t color_format)
{
return lv_draw_buf_width_to_stride_ex(&default_handlers, w, color_format);
}
uint32_t lv_draw_buf_width_to_stride_ex(const lv_draw_buf_handlers_t * handlers, uint32_t w,
lv_color_format_t color_format)
{
if(handlers->width_to_stride_cb) return handlers->width_to_stride_cb(w, color_format);
else return 0;
}
void * lv_draw_buf_align(void * data, lv_color_format_t color_format)
{
return lv_draw_buf_align_ex(&default_handlers, data, color_format);
}
void * lv_draw_buf_align_ex(const lv_draw_buf_handlers_t * handlers, void * data, lv_color_format_t color_format)
{
if(handlers->align_pointer_cb) return handlers->align_pointer_cb(data, color_format);
else return NULL;
}
void lv_draw_buf_invalidate_cache(const lv_draw_buf_t * draw_buf, const lv_area_t * area)
{
LV_ASSERT_NULL(draw_buf);
LV_ASSERT_NULL(draw_buf->handlers);
const lv_draw_buf_handlers_t * handlers = draw_buf->handlers;
if(!handlers->invalidate_cache_cb) {
return;
}
LV_PROFILER_DRAW_BEGIN;
lv_area_t full;
if(area == NULL) {
draw_buf_get_full_area(draw_buf, &full);
area = &full;
}
handlers->invalidate_cache_cb(draw_buf, area);
LV_PROFILER_DRAW_END;
}
void lv_draw_buf_flush_cache(const lv_draw_buf_t * draw_buf, const lv_area_t * area)
{
LV_ASSERT_NULL(draw_buf);
LV_ASSERT_NULL(draw_buf->handlers);
const lv_draw_buf_handlers_t * handlers = draw_buf->handlers;
if(!handlers->flush_cache_cb) {
return;
}
LV_PROFILER_DRAW_BEGIN;
lv_area_t full;
if(area == NULL) {
draw_buf_get_full_area(draw_buf, &full);
area = &full;
}
handlers->flush_cache_cb(draw_buf, area);
LV_PROFILER_DRAW_END;
}
void lv_draw_buf_clear(lv_draw_buf_t * draw_buf, const lv_area_t * a)
{
LV_ASSERT_NULL(draw_buf);
LV_PROFILER_DRAW_BEGIN;
const lv_image_header_t * header = &draw_buf->header;
uint32_t stride = header->stride;
if(a == NULL) {
uint8_t * buf = lv_draw_buf_goto_xy(draw_buf, 0, 0);
lv_memzero(buf, header->h * stride);
lv_draw_buf_flush_cache(draw_buf, a);
LV_PROFILER_DRAW_END;
return;
}
lv_area_t a_draw_buf;
a_draw_buf.x1 = 0;
a_draw_buf.y1 = 0;
a_draw_buf.x2 = draw_buf->header.w - 1;
a_draw_buf.y2 = draw_buf->header.h - 1;
lv_area_t a_clipped;
if(!lv_area_intersect(&a_clipped, a, &a_draw_buf)) {
LV_PROFILER_DRAW_END;
return;
}
if(lv_area_get_width(&a_clipped) <= 0) {
LV_PROFILER_DRAW_END;
return;
}
if(lv_area_get_height(&a_clipped) <= 0) {
LV_PROFILER_DRAW_END;
return;
}
uint8_t * buf = lv_draw_buf_goto_xy(draw_buf, a_clipped.x1, a_clipped.y1);
uint8_t bpp = lv_color_format_get_bpp(header->cf);
uint32_t line_length = (lv_area_get_width(&a_clipped) * bpp + 7) >> 3;
int32_t y;
for(y = a_clipped.y1; y <= a_clipped.y2; y++) {
lv_memzero(buf, line_length);
buf += stride;
}
lv_draw_buf_flush_cache(draw_buf, a);
LV_PROFILER_DRAW_END;
}
void lv_draw_buf_copy(lv_draw_buf_t * dest, const lv_area_t * dest_area,
const lv_draw_buf_t * src, const lv_area_t * src_area)
{
LV_PROFILER_DRAW_BEGIN;
uint8_t * dest_bufc;
uint8_t * src_bufc;
int32_t line_width;
/*Source and dest color format must be same. Color conversion is not supported yet.*/
LV_ASSERT_FORMAT_MSG(dest->header.cf == src->header.cf, "Color format mismatch: %d != %d",
dest->header.cf, src->header.cf);
if(dest_area == NULL) line_width = dest->header.w;
else line_width = lv_area_get_width(dest_area);
/* For indexed image, copy the palette if we are copying full image area*/
if(dest_area == NULL || src_area == NULL) {
if(LV_COLOR_FORMAT_IS_INDEXED(dest->header.cf)) {
lv_memcpy(dest->data, src->data, LV_COLOR_INDEXED_PALETTE_SIZE(dest->header.cf) * sizeof(lv_color32_t));
}
}
/*Check source and dest area have same width*/
if((src_area == NULL && line_width != src->header.w) || \
(src_area != NULL && line_width != lv_area_get_width(src_area))) {
LV_ASSERT_MSG(0, "Source and destination areas have different width");
LV_PROFILER_DRAW_END;
return;
}
if(src_area) src_bufc = lv_draw_buf_goto_xy(src, src_area->x1, src_area->y1);
else src_bufc = lv_draw_buf_goto_xy(src, 0, 0);
if(dest_area) dest_bufc = lv_draw_buf_goto_xy(dest, dest_area->x1, dest_area->y1);
else dest_bufc = lv_draw_buf_goto_xy(dest, 0, 0);
int32_t start_y, end_y;
if(dest_area) {
start_y = dest_area->y1;
end_y = dest_area->y2;
}
else {
start_y = 0;
end_y = dest->header.h - 1;
}
uint32_t dest_stride = dest->header.stride;
uint32_t src_stride = src->header.stride;
uint32_t line_bytes = (line_width * lv_color_format_get_bpp(dest->header.cf) + 7) >> 3;
for(; start_y <= end_y; start_y++) {
lv_memcpy(dest_bufc, src_bufc, line_bytes);
dest_bufc += dest_stride;
src_bufc += src_stride;
}
LV_PROFILER_DRAW_END;
}
lv_result_t lv_draw_buf_init(lv_draw_buf_t * draw_buf, uint32_t w, uint32_t h, lv_color_format_t cf, uint32_t stride,
void * data, uint32_t data_size)
{
LV_ASSERT_NULL(draw_buf);
if(draw_buf == NULL) return LV_RESULT_INVALID;
lv_memzero(draw_buf, sizeof(lv_draw_buf_t));
if(stride == 0) stride = lv_draw_buf_width_to_stride(w, cf);
if(stride * h > data_size) {
LV_LOG_WARN("Data size too small, required: %" LV_PRId32 ", provided: %" LV_PRId32, stride * h,
data_size);
return LV_RESULT_INVALID;
}
lv_image_header_t * header = &draw_buf->header;
header->w = w;
header->h = h;
header->cf = cf;
header->stride = stride;
header->flags = 0;
header->magic = LV_IMAGE_HEADER_MAGIC;
draw_buf->data = data;
draw_buf->unaligned_data = data;
draw_buf->handlers = &default_handlers;
draw_buf->data_size = data_size;
if(lv_draw_buf_align(data, cf) != draw_buf->unaligned_data) {
LV_LOG_WARN("Data is not aligned, ignored");
}
return LV_RESULT_OK;
}
lv_draw_buf_t * lv_draw_buf_create(uint32_t w, uint32_t h, lv_color_format_t cf, uint32_t stride)
{
return lv_draw_buf_create_ex(&default_handlers, w, h, cf, stride);
}
lv_draw_buf_t * lv_draw_buf_create_ex(const lv_draw_buf_handlers_t * handlers, uint32_t w, uint32_t h,
lv_color_format_t cf, uint32_t stride)
{
LV_PROFILER_DRAW_BEGIN;
lv_draw_buf_t * draw_buf = lv_malloc_zeroed(sizeof(lv_draw_buf_t));
LV_ASSERT_MALLOC(draw_buf);
if(draw_buf == NULL) {
LV_PROFILER_DRAW_END;
return NULL;
}
if(stride == 0) stride = lv_draw_buf_width_to_stride(w, cf);
uint32_t size = _calculate_draw_buf_size(w, h, cf, stride);
void * buf = draw_buf_malloc(handlers, size, cf);
/*Do not assert here as LVGL or the app might just want to try creating a draw_buf*/
if(buf == NULL) {
LV_LOG_WARN("No memory: %"LV_PRIu32"x%"LV_PRIu32", cf: %d, stride: %"LV_PRIu32", %"LV_PRIu32"Byte, ",
w, h, cf, stride, size);
lv_free(draw_buf);
LV_PROFILER_DRAW_END;
return NULL;
}
draw_buf->header.w = w;
draw_buf->header.h = h;
draw_buf->header.cf = cf;
draw_buf->header.flags = LV_IMAGE_FLAGS_MODIFIABLE | LV_IMAGE_FLAGS_ALLOCATED;
draw_buf->header.stride = stride;
draw_buf->header.magic = LV_IMAGE_HEADER_MAGIC;
draw_buf->data = lv_draw_buf_align(buf, cf);
draw_buf->unaligned_data = buf;
draw_buf->data_size = size;
draw_buf->handlers = handlers;
LV_PROFILER_DRAW_END;
return draw_buf;
}
lv_draw_buf_t * lv_draw_buf_dup(const lv_draw_buf_t * draw_buf)
{
return lv_draw_buf_dup_ex(&default_handlers, draw_buf);
}
lv_draw_buf_t * lv_draw_buf_dup_ex(const lv_draw_buf_handlers_t * handlers, const lv_draw_buf_t * draw_buf)
{
LV_PROFILER_DRAW_BEGIN;
const lv_image_header_t * header = &draw_buf->header;
lv_draw_buf_t * new_buf = lv_draw_buf_create_ex(handlers, header->w, header->h, header->cf, header->stride);
if(new_buf == NULL) {
LV_PROFILER_DRAW_END;
return NULL;
}
new_buf->header.flags = draw_buf->header.flags;
new_buf->header.flags |= LV_IMAGE_FLAGS_MODIFIABLE | LV_IMAGE_FLAGS_ALLOCATED;
/*Choose the smaller size to copy*/
uint32_t size = LV_MIN(draw_buf->data_size, new_buf->data_size);
/*Copy image data*/
lv_memcpy(new_buf->data, draw_buf->data, size);
LV_PROFILER_DRAW_END;
return new_buf;
}
lv_draw_buf_t * lv_draw_buf_reshape(lv_draw_buf_t * draw_buf, lv_color_format_t cf, uint32_t w, uint32_t h,
uint32_t stride)
{
if(draw_buf == NULL) return NULL;
LV_PROFILER_DRAW_BEGIN;
/*If color format is unknown, keep using the original color format.*/
if(cf == LV_COLOR_FORMAT_UNKNOWN) cf = draw_buf->header.cf;
if(stride == 0) stride = lv_draw_buf_width_to_stride(w, cf);
uint32_t size = _calculate_draw_buf_size(w, h, cf, stride);
if(size > draw_buf->data_size) {
LV_LOG_TRACE("Draw buf too small for new shape");
LV_PROFILER_DRAW_END;
return NULL;
}
draw_buf->header.cf = cf;
draw_buf->header.w = w;
draw_buf->header.h = h;
draw_buf->header.stride = stride;
LV_PROFILER_DRAW_END;
return draw_buf;
}
void lv_draw_buf_destroy(lv_draw_buf_t * draw_buf)
{
LV_ASSERT_NULL(draw_buf);
if(draw_buf == NULL) return;
LV_PROFILER_DRAW_BEGIN;
if(draw_buf->header.flags & LV_IMAGE_FLAGS_ALLOCATED) {
LV_ASSERT_NULL(draw_buf->handlers);
const lv_draw_buf_handlers_t * handlers = draw_buf->handlers;
draw_buf_free(handlers, draw_buf->unaligned_data);
lv_free(draw_buf);
}
else {
LV_LOG_ERROR("draw buffer is not allocated, ignored");
}
LV_PROFILER_DRAW_END;
}
void * lv_draw_buf_goto_xy(const lv_draw_buf_t * buf, uint32_t x, uint32_t y)
{
LV_ASSERT_NULL(buf);
if(buf == NULL) return NULL;
uint8_t * data = buf->data;
/*Skip palette*/
data += LV_COLOR_INDEXED_PALETTE_SIZE(buf->header.cf) * sizeof(lv_color32_t);
data += buf->header.stride * y;
if(x == 0) return data;
return data + x * lv_color_format_get_bpp(buf->header.cf) / 8;
}
lv_result_t lv_draw_buf_adjust_stride(lv_draw_buf_t * src, uint32_t stride)
{
LV_ASSERT_NULL(src);
LV_ASSERT_NULL(src->data);
if(src == NULL) return LV_RESULT_INVALID;
if(src->data == NULL) return LV_RESULT_INVALID;
LV_PROFILER_DRAW_BEGIN;
const lv_image_header_t * header = &src->header;
uint32_t w = header->w;
uint32_t h = header->h;
if(!lv_draw_buf_has_flag(src, LV_IMAGE_FLAGS_MODIFIABLE)) {
LV_PROFILER_DRAW_END;
return LV_RESULT_INVALID;
}
/*Use global stride*/
if(stride == 0) stride = lv_draw_buf_width_to_stride(w, header->cf);
/*Check if stride already match*/
if(header->stride == stride) {
LV_PROFILER_DRAW_END;
return LV_RESULT_OK;
}
/*Calculate the minimal stride allowed from bpp*/
uint32_t bpp = lv_color_format_get_bpp(header->cf);
uint32_t min_stride = (w * bpp + 7) >> 3;
if(stride < min_stride) {
LV_LOG_WARN("New stride is too small. min: %" LV_PRId32, min_stride);
LV_PROFILER_DRAW_END;
return LV_RESULT_INVALID;
}
/*Check if buffer has enough space. */
uint32_t new_size = _calculate_draw_buf_size(w, h, header->cf, stride);
if(new_size > src->data_size) {
LV_PROFILER_DRAW_END;
return LV_RESULT_INVALID;
}
uint32_t offset = LV_COLOR_INDEXED_PALETTE_SIZE(header->cf) * 4;
if(stride > header->stride) {
/*Copy from the last line to the first*/
uint8_t * src_data = src->data + offset + header->stride * (h - 1);
uint8_t * dst_data = src->data + offset + stride * (h - 1);
for(uint32_t y = 0; y < h; y++) {
lv_memmove(dst_data, src_data, min_stride);
src_data -= header->stride;
dst_data -= stride;
}
}
else {
/*Copy from the first line to the last*/
uint8_t * src_data = src->data + offset;
uint8_t * dst_data = src->data + offset;
for(uint32_t y = 0; y < h; y++) {
lv_memmove(dst_data, src_data, min_stride);
src_data += header->stride;
dst_data += stride;
}
}
src->header.stride = stride;
LV_PROFILER_DRAW_END;
return LV_RESULT_OK;
}
lv_result_t lv_draw_buf_premultiply(lv_draw_buf_t * draw_buf)
{
LV_ASSERT_NULL(draw_buf);
if(draw_buf == NULL) return LV_RESULT_INVALID;
if(draw_buf->header.flags & LV_IMAGE_FLAGS_PREMULTIPLIED) return LV_RESULT_INVALID;
if((draw_buf->header.flags & LV_IMAGE_FLAGS_MODIFIABLE) == 0) {
LV_LOG_WARN("draw buf is not modifiable: 0x%04x", draw_buf->header.flags);
return LV_RESULT_INVALID;
}
LV_PROFILER_DRAW_BEGIN;
/*Premultiply color with alpha, do case by case by judging color format*/
lv_color_format_t cf = draw_buf->header.cf;
if(LV_COLOR_FORMAT_IS_INDEXED(cf)) {
int size = LV_COLOR_INDEXED_PALETTE_SIZE(cf);
lv_color32_t * palette = (lv_color32_t *)draw_buf->data;
for(int i = 0; i < size; i++) {
lv_color_premultiply(&palette[i]);
}
}
else if(cf == LV_COLOR_FORMAT_ARGB8888) {
uint32_t h = draw_buf->header.h;
uint32_t w = draw_buf->header.w;
uint32_t stride = draw_buf->header.stride;
uint8_t * line = (uint8_t *)draw_buf->data;
for(uint32_t y = 0; y < h; y++) {
lv_color32_t * pixel = (lv_color32_t *)line;
for(uint32_t x = 0; x < w; x++) {
lv_color_premultiply(pixel);
pixel++;
}
line += stride;
}
}
else if(cf == LV_COLOR_FORMAT_RGB565A8) {
uint32_t h = draw_buf->header.h;
uint32_t w = draw_buf->header.w;
uint32_t stride = draw_buf->header.stride;
uint32_t alpha_stride = stride / 2;
uint8_t * line = (uint8_t *)draw_buf->data;
lv_opa_t * alpha = (lv_opa_t *)(line + stride * h);
for(uint32_t y = 0; y < h; y++) {
lv_color16_t * pixel = (lv_color16_t *)line;
for(uint32_t x = 0; x < w; x++) {
lv_color16_premultiply(pixel, alpha[x]);
pixel++;
}
line += stride;
alpha += alpha_stride;
}
}
else if(cf == LV_COLOR_FORMAT_ARGB8565) {
uint32_t h = draw_buf->header.h;
uint32_t w = draw_buf->header.w;
uint32_t stride = draw_buf->header.stride;
uint8_t * line = (uint8_t *)draw_buf->data;
for(uint32_t y = 0; y < h; y++) {
uint8_t * pixel = line;
for(uint32_t x = 0; x < w; x++) {
uint8_t alpha = pixel[2];
lv_color16_premultiply((lv_color16_t *)pixel, alpha);
pixel += 3;
}
line += stride;
}
}
else if(LV_COLOR_FORMAT_IS_ALPHA_ONLY(cf)) {
/*Pass*/
}
else {
LV_LOG_WARN("draw buf has no alpha, cf: %d", cf);
}
draw_buf->header.flags |= LV_IMAGE_FLAGS_PREMULTIPLIED;
LV_PROFILER_DRAW_END;
return LV_RESULT_OK;
}
void lv_draw_buf_set_palette(lv_draw_buf_t * draw_buf, uint8_t index, lv_color32_t color)
{
LV_ASSERT_NULL(draw_buf);
if(draw_buf == NULL) return;
if(!LV_COLOR_FORMAT_IS_INDEXED(draw_buf->header.cf)) {
LV_LOG_WARN("Not indexed color format");
return;
}
lv_color32_t * palette = (lv_color32_t *)draw_buf->data;
palette[index] = color;
}
bool lv_draw_buf_has_flag(const lv_draw_buf_t * draw_buf, lv_image_flags_t flag)
{
return draw_buf->header.flags & flag;
}
void lv_draw_buf_set_flag(lv_draw_buf_t * draw_buf, lv_image_flags_t flag)
{
draw_buf->header.flags |= flag;
}
void lv_draw_buf_clear_flag(lv_draw_buf_t * draw_buf, lv_image_flags_t flag)
{
draw_buf->header.flags &= ~flag;
}
lv_result_t lv_draw_buf_from_image(lv_draw_buf_t * buf, const lv_image_dsc_t * img)
{
const lv_result_t res = lv_draw_buf_init(buf, img->header.w, img->header.h, img->header.cf, img->header.stride,
(void *)img->data, img->data_size);
if(res != LV_RESULT_OK) {
return res;
}
buf->header.flags = img->header.flags;
return res;
}
void lv_draw_buf_to_image(const lv_draw_buf_t * buf, lv_image_dsc_t * img)
{
lv_memcpy((void *)img, buf, sizeof(lv_image_dsc_t));
}
void lv_image_buf_set_palette(lv_image_dsc_t * dsc, uint8_t id, lv_color32_t c)
{
LV_LOG_WARN("Deprecated API, use lv_draw_buf_set_palette instead.");
lv_draw_buf_set_palette((lv_draw_buf_t *)dsc, id, c);
}
void lv_image_buf_free(lv_image_dsc_t * dsc)
{
LV_LOG_WARN("Deprecated API, use lv_draw_buf_destroy instead.");
if(dsc != NULL) {
if(dsc->data != NULL)
lv_free((void *)dsc->data);
lv_free((void *)dsc);
}
}
/**********************
* STATIC FUNCTIONS
**********************/
static void * buf_malloc(size_t size_bytes, lv_color_format_t color_format)
{
LV_UNUSED(color_format);
/*Allocate larger memory to be sure it can be aligned as needed*/
size_bytes += LV_DRAW_BUF_ALIGN - 1;
return lv_malloc(size_bytes);
}
static void buf_free(void * buf)
{
lv_free(buf);
}
static void * buf_align(void * buf, lv_color_format_t color_format)
{
LV_UNUSED(color_format);
uint8_t * buf_u8 = buf;
if(buf_u8) {
buf_u8 = (uint8_t *)LV_ROUND_UP((lv_uintptr_t)buf_u8, LV_DRAW_BUF_ALIGN);
}
return buf_u8;
}
static uint32_t width_to_stride(uint32_t w, lv_color_format_t color_format)
{
uint32_t width_byte;
width_byte = w * lv_color_format_get_bpp(color_format);
width_byte = (width_byte + 7) >> 3; /*Round up*/
return LV_ROUND_UP(width_byte, LV_DRAW_BUF_STRIDE_ALIGN);
}
static void * draw_buf_malloc(const lv_draw_buf_handlers_t * handlers, size_t size_bytes,
lv_color_format_t color_format)
{
if(handlers->buf_malloc_cb) return handlers->buf_malloc_cb(size_bytes, color_format);
else return NULL;
}
static void draw_buf_free(const lv_draw_buf_handlers_t * handlers, void * buf)
{
if(handlers->buf_free_cb)
handlers->buf_free_cb(buf);
}
/**
* For given width, height, color format, and stride, calculate the size needed for a new draw buffer.
*/
static uint32_t _calculate_draw_buf_size(uint32_t w, uint32_t h, lv_color_format_t cf, uint32_t stride)
{
uint32_t size;
if(stride == 0) stride = lv_draw_buf_width_to_stride(w, cf);
size = stride * h;
if(cf == LV_COLOR_FORMAT_RGB565A8) {
size += (stride / 2) * h; /*A8 mask*/
}
else if(LV_COLOR_FORMAT_IS_INDEXED(cf)) {
/*@todo we have to include palette right before image data*/
size += LV_COLOR_INDEXED_PALETTE_SIZE(cf) * 4;
}
return size;
}
static void draw_buf_get_full_area(const lv_draw_buf_t * draw_buf, lv_area_t * full_area)
{
const lv_image_header_t * header = &draw_buf->header;
lv_area_set(full_area, 0, 0, header->w - 1, header->h - 1);
}
@@ -0,0 +1,356 @@
/**
* @file lv_draw_buf.h
*
*/
#ifndef LV_DRAW_BUF_H
#define LV_DRAW_BUF_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../misc/lv_types.h"
#include "../misc/lv_area.h"
#include "../misc/lv_color.h"
#include "../stdlib/lv_string.h"
#include "lv_image_dsc.h"
/*********************
* DEFINES
*********************/
/** Use this value to let LVGL calculate stride automatically */
#define LV_STRIDE_AUTO 0
LV_EXPORT_CONST_INT(LV_STRIDE_AUTO);
/**
* Stride alignment for draw buffers.
* It may vary between different color formats and hardware.
* Refine it to suit your needs.
*/
#define LV_DRAW_BUF_STRIDE(w, cf) \
LV_ROUND_UP(((w) * LV_COLOR_FORMAT_GET_BPP(cf) + 7) / 8, LV_DRAW_BUF_STRIDE_ALIGN)
/** Allocate a slightly larger buffer, so we can adjust the start address to meet alignment */
#define LV_DRAW_BUF_SIZE(w, h, cf) \
(LV_DRAW_BUF_STRIDE(w, cf) * (h) + LV_DRAW_BUF_ALIGN + \
LV_COLOR_INDEXED_PALETTE_SIZE(cf) * sizeof(lv_color32_t))
/**
* Define a static draw buffer with the given width, height, and color format.
* Stride alignment is set to LV_DRAW_BUF_STRIDE_ALIGN.
*
* For platform that needs special buffer alignment, call LV_DRAW_BUF_INIT_STATIC.
*/
#define LV_DRAW_BUF_DEFINE_STATIC(name, _w, _h, _cf) \
static LV_ATTRIBUTE_MEM_ALIGN uint8_t buf_##name[LV_DRAW_BUF_SIZE(_w, _h, _cf)]; \
static lv_draw_buf_t name = { \
.header = { \
.magic = LV_IMAGE_HEADER_MAGIC, \
.cf = (_cf), \
.flags = LV_IMAGE_FLAGS_MODIFIABLE, \
.w = (_w), \
.h = (_h), \
.stride = LV_DRAW_BUF_STRIDE(_w, _cf), \
.reserved_2 = 0, \
}, \
.data_size = sizeof(buf_##name), \
.data = buf_##name, \
.unaligned_data = buf_##name, \
}
#define LV_DRAW_BUF_INIT_STATIC(name) \
do { \
lv_image_header_t * header = &name.header; \
lv_draw_buf_init(&name, header->w, header->h, (lv_color_format_t)header->cf, header->stride, buf_##name, sizeof(buf_##name)); \
lv_draw_buf_set_flag(&name, LV_IMAGE_FLAGS_MODIFIABLE); \
} while(0)
/**********************
* TYPEDEFS
**********************/
typedef void * (*lv_draw_buf_malloc_cb)(size_t size, lv_color_format_t color_format);
typedef void (*lv_draw_buf_free_cb)(void * draw_buf);
typedef void * (*lv_draw_buf_align_cb)(void * buf, lv_color_format_t color_format);
typedef void (*lv_draw_buf_cache_operation_cb)(const lv_draw_buf_t * draw_buf, const lv_area_t * area);
typedef uint32_t (*lv_draw_buf_width_to_stride_cb)(uint32_t w, lv_color_format_t color_format);
struct _lv_draw_buf_t {
lv_image_header_t header;
uint32_t data_size; /**< Total buf size in bytes */
uint8_t * data;
void * unaligned_data; /**< Unaligned address of `data`, used internally by lvgl */
const lv_draw_buf_handlers_t * handlers; /**< draw buffer alloc/free ops. */
};
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Initialize the draw buffer with the default handlers.
*
* @param handlers the draw buffer handlers to set
*/
void lv_draw_buf_init_with_default_handlers(lv_draw_buf_handlers_t * handlers);
/**
* Initialize the draw buffer with given handlers.
*
* @param handlers the draw buffer handlers to set
* @param buf_malloc_cb the callback to allocate memory for the buffer
* @param buf_free_cb the callback to free memory of the buffer
* @param align_pointer_cb the callback to align the buffer
* @param invalidate_cache_cb the callback to invalidate the cache of the buffer
* @param flush_cache_cb the callback to flush buffer
* @param width_to_stride_cb the callback to calculate the stride based on the width and color format
*/
void lv_draw_buf_handlers_init(lv_draw_buf_handlers_t * handlers,
lv_draw_buf_malloc_cb buf_malloc_cb,
lv_draw_buf_free_cb buf_free_cb,
lv_draw_buf_align_cb align_pointer_cb,
lv_draw_buf_cache_operation_cb invalidate_cache_cb,
lv_draw_buf_cache_operation_cb flush_cache_cb,
lv_draw_buf_width_to_stride_cb width_to_stride_cb);
/**
* Get the struct which holds the callbacks for draw buf management.
* Custom callback can be set on the returned value
* @return pointer to the struct of handlers
*/
lv_draw_buf_handlers_t * lv_draw_buf_get_handlers(void);
lv_draw_buf_handlers_t * lv_draw_buf_get_font_handlers(void);
lv_draw_buf_handlers_t * lv_draw_buf_get_image_handlers(void);
/**
* Align the address of a buffer. The buffer needs to be large enough for the real data after alignment
* @param buf the data to align
* @param color_format the color format of the buffer
* @return the aligned buffer
*/
void * lv_draw_buf_align(void * buf, lv_color_format_t color_format);
/**
* Align the address of a buffer with custom draw buffer handlers.
* The buffer needs to be large enough for the real data after alignment
* @param handlers the draw buffer handlers
* @param buf the data to align
* @param color_format the color format of the buffer
* @return the aligned buffer
*/
void * lv_draw_buf_align_ex(const lv_draw_buf_handlers_t * handlers, void * buf, lv_color_format_t color_format);
/**
* Invalidate the cache of the buffer
* @param draw_buf the draw buffer needs to be invalidated
* @param area the area to invalidate in the buffer,
* use NULL to invalidate the whole draw buffer address range
*/
void lv_draw_buf_invalidate_cache(const lv_draw_buf_t * draw_buf, const lv_area_t * area);
/**
* Flush the cache of the buffer
* @param draw_buf the draw buffer needs to be flushed
* @param area the area to flush in the buffer,
* use NULL to flush the whole draw buffer address range
*/
void lv_draw_buf_flush_cache(const lv_draw_buf_t * draw_buf, const lv_area_t * area);
/**
* Calculate the stride in bytes based on a width and color format
* @param w the width in pixels
* @param color_format the color format
* @return the stride in bytes
*/
uint32_t lv_draw_buf_width_to_stride(uint32_t w, lv_color_format_t color_format);
/**
* Calculate the stride in bytes based on a width and color format
* @param handlers the draw buffer handlers
* @param w the width in pixels
* @param color_format the color format
* @return the stride in bytes
*/
uint32_t lv_draw_buf_width_to_stride_ex(const lv_draw_buf_handlers_t * handlers, uint32_t w,
lv_color_format_t color_format);
/**
* Clear an area on the buffer
* @param draw_buf pointer to draw buffer
* @param a the area to clear, or NULL to clear the whole buffer
*/
void lv_draw_buf_clear(lv_draw_buf_t * draw_buf, const lv_area_t * a);
/**
* Copy an area from a buffer to another
* @param dest pointer to the destination draw buffer
* @param dest_area the area to copy from the destination buffer, if NULL, use the whole buffer
* @param src pointer to the source draw buffer
* @param src_area the area to copy from the destination buffer, if NULL, use the whole buffer
* @note `dest_area` and `src_area` should have the same width and height
* @note `dest` and `src` should have same color format. Color converting is not supported fow now.
*/
void lv_draw_buf_copy(lv_draw_buf_t * dest, const lv_area_t * dest_area,
const lv_draw_buf_t * src, const lv_area_t * src_area);
/**
* Note: Eventually, lv_draw_buf_malloc/free will be kept as private.
* For now, we use `create` to distinguish with malloc.
*
* Create an draw buf by allocating struct for `lv_draw_buf_t` and allocating a buffer for it
* that meets specified requirements.
*
* @param w the buffer width in pixels
* @param h the buffer height in pixels
* @param cf the color format for image
* @param stride the stride in bytes for image. Use 0 for automatic calculation based on
* w, cf, and global stride alignment configuration.
*/
lv_draw_buf_t * lv_draw_buf_create(uint32_t w, uint32_t h, lv_color_format_t cf, uint32_t stride);
/**
* Note: Eventually, lv_draw_buf_malloc/free will be kept as private.
* For now, we use `create` to distinguish with malloc.
*
* Create an draw buf by allocating struct for `lv_draw_buf_t` and allocating a buffer for it
* that meets specified requirements.
*
* @param handlers the draw buffer handlers
* @param w the buffer width in pixels
* @param h the buffer height in pixels
* @param cf the color format for image
* @param stride the stride in bytes for image. Use 0 for automatic calculation based on
* w, cf, and global stride alignment configuration.
*/
lv_draw_buf_t * lv_draw_buf_create_ex(const lv_draw_buf_handlers_t * handlers, uint32_t w, uint32_t h,
lv_color_format_t cf, uint32_t stride);
/**
* Duplicate a draw buf with same image size, stride and color format. Copy the image data too.
* @param draw_buf the draw buf to duplicate
* @return the duplicated draw buf on success, NULL if failed
*/
lv_draw_buf_t * lv_draw_buf_dup(const lv_draw_buf_t * draw_buf);
/**
* Duplicate a draw buf with same image size, stride and color format. Copy the image data too.
* @param handlers the draw buffer handlers
* @param draw_buf the draw buf to duplicate
* @return the duplicated draw buf on success, NULL if failed
*/
lv_draw_buf_t * lv_draw_buf_dup_ex(const lv_draw_buf_handlers_t * handlers, const lv_draw_buf_t * draw_buf);
/**
* Initialize a draw buf with the given buffer and parameters. Clear draw buffer flag to zero.
* @param draw_buf the draw buf to initialize
* @param w the buffer width in pixels
* @param h the buffer height in pixels
* @param cf the color format
* @param stride the stride in bytes. Use 0 for automatic calculation
* @param data the buffer used for drawing. Unaligned `data` will be aligned internally
* @param data_size the size of the buffer in bytes
* @return return LV_RESULT_OK on success, LV_RESULT_INVALID otherwise
*/
lv_result_t lv_draw_buf_init(lv_draw_buf_t * draw_buf, uint32_t w, uint32_t h, lv_color_format_t cf, uint32_t stride,
void * data, uint32_t data_size);
/**
* Keep using the existing memory, reshape the draw buffer to the given width and height.
* Return NULL if data_size is smaller than the required size.
* @param draw_buf pointer to a draw buffer
* @param cf the new color format, use 0 or LV_COLOR_FORMAT_UNKNOWN to keep using the original color format.
* @param w the new width in pixels
* @param h the new height in pixels
* @param stride the stride in bytes for image. Use 0 for automatic calculation.
*/
lv_draw_buf_t * lv_draw_buf_reshape(lv_draw_buf_t * draw_buf, lv_color_format_t cf, uint32_t w, uint32_t h,
uint32_t stride);
/**
* Destroy a draw buf by freeing the actual buffer if it's marked as LV_IMAGE_FLAGS_ALLOCATED in header.
* Then free the lv_draw_buf_t struct.
*
* @param draw_buf the draw buffer to destroy
*/
void lv_draw_buf_destroy(lv_draw_buf_t * draw_buf);
/**
* Return pointer to the buffer at the given coordinates
*/
void * lv_draw_buf_goto_xy(const lv_draw_buf_t * buf, uint32_t x, uint32_t y);
/**
* Adjust the stride of a draw buf in place.
* @param src pointer to a draw buffer
* @param stride the new stride in bytes for image. Use LV_STRIDE_AUTO for automatic calculation.
* @return LV_RESULT_OK: success or LV_RESULT_INVALID: failed
*/
lv_result_t lv_draw_buf_adjust_stride(lv_draw_buf_t * src, uint32_t stride);
/**
* Premultiply draw buffer color with alpha channel.
* If it's already premultiplied, return directly.
* Only color formats with alpha channel will be processed.
*
* @return LV_RESULT_OK: premultiply success
*/
lv_result_t lv_draw_buf_premultiply(lv_draw_buf_t * draw_buf);
bool lv_draw_buf_has_flag(const lv_draw_buf_t * draw_buf, lv_image_flags_t flag);
void lv_draw_buf_set_flag(lv_draw_buf_t * draw_buf, lv_image_flags_t flag);
void lv_draw_buf_clear_flag(lv_draw_buf_t * draw_buf, lv_image_flags_t flag);
/**
* As of now, draw buf share same definition as `lv_image_dsc_t`.
* And is interchangeable with `lv_image_dsc_t`.
*/
lv_result_t lv_draw_buf_from_image(lv_draw_buf_t * buf, const lv_image_dsc_t * img);
void lv_draw_buf_to_image(const lv_draw_buf_t * buf, lv_image_dsc_t * img);
/**
* Set the palette color of an indexed image. Valid only for `LV_COLOR_FORMAT_I1/2/4/8`
* @param draw_buf pointer to an image descriptor
* @param index the palette color to set:
* - for `LV_COLOR_FORMAT_I1`: 0..1
* - for `LV_COLOR_FORMAT_I2`: 0..3
* - for `LV_COLOR_FORMAT_I4`: 0..15
* - for `LV_COLOR_FORMAT_I8`: 0..255
* @param color the color to set in lv_color32_t format
*/
void lv_draw_buf_set_palette(lv_draw_buf_t * draw_buf, uint8_t index, lv_color32_t color);
/**
* @deprecated Use lv_draw_buf_set_palette instead.
*/
void lv_image_buf_set_palette(lv_image_dsc_t * dsc, uint8_t id, lv_color32_t c);
/**
* @deprecated Use lv_draw_buffer_create/destroy instead.
* Free the data pointer and dsc struct of an image.
*/
void lv_image_buf_free(lv_image_dsc_t * dsc);
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_DRAW_BUF_H*/
@@ -0,0 +1,53 @@
/**
* @file lv_draw_buf_private.h
*
*/
#ifndef LV_DRAW_BUF_PRIVATE_H
#define LV_DRAW_BUF_PRIVATE_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "lv_draw_buf.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
struct _lv_draw_buf_handlers_t {
lv_draw_buf_malloc_cb buf_malloc_cb;
lv_draw_buf_free_cb buf_free_cb;
lv_draw_buf_align_cb align_pointer_cb;
lv_draw_buf_cache_operation_cb invalidate_cache_cb;
lv_draw_buf_cache_operation_cb flush_cache_cb;
lv_draw_buf_width_to_stride_cb width_to_stride_cb;
};
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Called internally to initialize the draw_buf_handlers in lv_global
*/
void lv_draw_buf_init_handlers(void);
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_DRAW_BUF_PRIVATE_H*/
@@ -0,0 +1,376 @@
/**
* @file lv_draw_img.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_draw_image_private.h"
#include "../misc/lv_area_private.h"
#include "lv_image_decoder_private.h"
#include "lv_draw_private.h"
#include "../display/lv_display.h"
#include "../misc/lv_log.h"
#include "../misc/lv_math.h"
#include "../core/lv_refr.h"
#include "../core/lv_obj_private.h"
#include "../stdlib/lv_mem.h"
#include "../stdlib/lv_string.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static void img_decode_and_draw(lv_draw_task_t * t, const lv_draw_image_dsc_t * draw_dsc,
lv_image_decoder_dsc_t * decoder_dsc, lv_area_t * relative_decoded_area,
const lv_area_t * img_area, const lv_area_t * clipped_img_area,
lv_draw_image_core_cb draw_core_cb);
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
void lv_draw_image_dsc_init(lv_draw_image_dsc_t * dsc)
{
lv_memzero(dsc, sizeof(lv_draw_image_dsc_t));
dsc->recolor = lv_color_black();
dsc->opa = LV_OPA_COVER;
dsc->scale_x = LV_SCALE_NONE;
dsc->scale_y = LV_SCALE_NONE;
dsc->antialias = LV_COLOR_DEPTH > 8 ? 1 : 0;
dsc->image_area.x2 = LV_COORD_MIN; /*Indicate invalid area by default by setting a negative size*/
dsc->base.dsc_size = sizeof(lv_draw_image_dsc_t);
}
lv_draw_image_dsc_t * lv_draw_task_get_image_dsc(lv_draw_task_t * task)
{
return task->type == LV_DRAW_TASK_TYPE_IMAGE ? (lv_draw_image_dsc_t *)task->draw_dsc : NULL;
}
void lv_draw_layer(lv_layer_t * layer, const lv_draw_image_dsc_t * dsc, const lv_area_t * coords)
{
if(dsc->scale_x <= 0 || dsc->scale_y <= 0) {
/* NOT draw if scale is negative or zero */
return;
}
LV_PROFILER_DRAW_BEGIN;
lv_draw_task_t * t = lv_draw_add_task(layer, coords, LV_DRAW_TASK_TYPE_LAYER);
lv_draw_image_dsc_t * new_image_dsc = t->draw_dsc;
lv_memcpy(new_image_dsc, dsc, sizeof(*dsc));
t->state = LV_DRAW_TASK_STATE_WAITING;
lv_image_buf_get_transformed_area(&t->_real_area, lv_area_get_width(coords), lv_area_get_height(coords),
dsc->rotation, dsc->scale_x, dsc->scale_y, &dsc->pivot);
lv_area_move(&t->_real_area, coords->x1, coords->y1);
/*If the image_area is not set assume that it's the same as the rendering area */
if(new_image_dsc->image_area.x2 == LV_COORD_MIN) {
new_image_dsc->image_area = *coords;
}
lv_layer_t * layer_to_draw = (lv_layer_t *)dsc->src;
layer_to_draw->all_tasks_added = true;
lv_draw_finalize_task_creation(layer, t);
LV_PROFILER_DRAW_END;
}
void lv_draw_image(lv_layer_t * layer, const lv_draw_image_dsc_t * dsc, const lv_area_t * image_coords)
{
if(dsc->src == NULL) {
LV_LOG_WARN("Image draw: src is NULL");
return;
}
if(dsc->opa <= LV_OPA_MIN) return;
if(dsc->scale_x <= 0 || dsc->scale_y <= 0) {
/* NOT draw if scale is negative or zero */
return;
}
LV_PROFILER_DRAW_BEGIN;
lv_draw_image_dsc_t new_image_dsc;
lv_memcpy(&new_image_dsc, dsc, sizeof(*dsc));
lv_result_t res = lv_image_decoder_get_info(new_image_dsc.src, &new_image_dsc.header);
if(res != LV_RESULT_OK) {
LV_LOG_WARN("Couldn't get info about the image");
LV_PROFILER_DRAW_END;
return;
}
/*If the image_area is not set assume that it's the same as the rendering area */
if(new_image_dsc.image_area.x2 == LV_COORD_MIN) {
new_image_dsc.image_area = *image_coords;
}
/*Typical case, draw the image as bitmap*/
if(!(new_image_dsc.header.flags & LV_IMAGE_FLAGS_CUSTOM_DRAW)) {
lv_draw_task_t * t = lv_draw_add_task(layer, image_coords, LV_DRAW_TASK_TYPE_IMAGE);
lv_memcpy(t->draw_dsc, &new_image_dsc, sizeof(lv_draw_image_dsc_t));
lv_image_buf_get_transformed_area(&t->_real_area, lv_area_get_width(image_coords), lv_area_get_height(image_coords),
dsc->rotation, dsc->scale_x, dsc->scale_y, &dsc->pivot);
lv_area_move(&t->_real_area, image_coords->x1, image_coords->y1);
lv_draw_finalize_task_creation(layer, t);
}
/*Use a custom draw callback*/
else {
lv_image_decoder_dsc_t decoder_dsc;
res = lv_image_decoder_open(&decoder_dsc, new_image_dsc.src, NULL);
if(res != LV_RESULT_OK) {
LV_LOG_ERROR("Failed to open image");
LV_PROFILER_DRAW_END;
return;
}
if(decoder_dsc.decoder && decoder_dsc.decoder->custom_draw_cb) {
lv_area_t draw_area = layer->buf_area;
lv_area_t coords_area = *image_coords;
lv_area_t obj_area = dsc->base.obj->coords;
if(layer->parent) { /* child layer */
if(lv_area_intersect(&coords_area, &coords_area, &obj_area)) {
int32_t xpos = image_coords->x1 - draw_area.x1;
int32_t ypos = image_coords->y1 - draw_area.y1;
lv_area_move(&coords_area, -(image_coords->x1 - xpos), -(image_coords->y1 - ypos));
layer->_clip_area = coords_area;
decoder_dsc.decoder->custom_draw_cb(layer, &decoder_dsc, &coords_area, &new_image_dsc, &coords_area);
}
}
else {
lv_area_t clip_area = draw_area;
if(lv_area_intersect(&clip_area, &clip_area, &coords_area)) {
lv_image_buf_get_transformed_area(&coords_area, lv_area_get_width(image_coords), lv_area_get_height(image_coords),
dsc->rotation, dsc->scale_x, dsc->scale_y, &dsc->pivot);
lv_area_move(&coords_area, image_coords->x1, image_coords->y1);
lv_image_buf_get_transformed_area(&clip_area, lv_area_get_width(image_coords), lv_area_get_height(image_coords),
dsc->rotation, dsc->scale_x, dsc->scale_y, &dsc->pivot);
lv_area_move(&clip_area, image_coords->x1, image_coords->y1);
if(lv_area_intersect(&clip_area, &clip_area, &obj_area)) {
decoder_dsc.decoder->custom_draw_cb(layer, &decoder_dsc, &coords_area, &new_image_dsc, &clip_area);
}
}
}
}
}
LV_PROFILER_DRAW_END;
}
lv_image_src_t lv_image_src_get_type(const void * src)
{
if(src == NULL) return LV_IMAGE_SRC_UNKNOWN;
const uint8_t * u8_p = src;
/*The first byte shows the type of the image source*/
if(u8_p[0] >= 0x20 && u8_p[0] <= 0x7F) {
return LV_IMAGE_SRC_FILE; /*If it's an ASCII character then it's file name*/
}
else if(u8_p[0] >= 0x80) {
return LV_IMAGE_SRC_SYMBOL; /*Symbols begins after 0x7F*/
}
else {
return LV_IMAGE_SRC_VARIABLE; /*`lv_image_dsc_t` is draw to the first byte < 0x20*/
}
}
void lv_draw_image_normal_helper(lv_draw_task_t * t, const lv_draw_image_dsc_t * draw_dsc,
const lv_area_t * coords, lv_draw_image_core_cb draw_core_cb)
{
if(draw_core_cb == NULL) {
LV_LOG_WARN("draw_core_cb is NULL");
return;
}
lv_area_t draw_area;
lv_area_copy(&draw_area, coords);
if(draw_dsc->rotation || draw_dsc->scale_x != LV_SCALE_NONE || draw_dsc->scale_y != LV_SCALE_NONE) {
int32_t w = lv_area_get_width(coords);
int32_t h = lv_area_get_height(coords);
lv_image_buf_get_transformed_area(&draw_area, w, h, draw_dsc->rotation, draw_dsc->scale_x, draw_dsc->scale_y,
&draw_dsc->pivot);
draw_area.x1 += coords->x1;
draw_area.y1 += coords->y1;
draw_area.x2 += coords->x1;
draw_area.y2 += coords->y1;
}
lv_area_t clipped_img_area;
if(!lv_area_intersect(&clipped_img_area, &draw_area, &t->clip_area)) {
return;
}
lv_image_decoder_dsc_t decoder_dsc;
lv_result_t res = lv_image_decoder_open(&decoder_dsc, draw_dsc->src, NULL);
if(res != LV_RESULT_OK) {
LV_LOG_ERROR("Failed to open image");
return;
}
img_decode_and_draw(t, draw_dsc, &decoder_dsc, NULL, coords, &clipped_img_area, draw_core_cb);
lv_image_decoder_close(&decoder_dsc);
}
void lv_draw_image_tiled_helper(lv_draw_task_t * t, const lv_draw_image_dsc_t * draw_dsc,
const lv_area_t * coords, lv_draw_image_core_cb draw_core_cb)
{
if(draw_core_cb == NULL) {
LV_LOG_WARN("draw_core_cb is NULL");
return;
}
lv_image_decoder_dsc_t decoder_dsc;
lv_result_t res = lv_image_decoder_open(&decoder_dsc, draw_dsc->src, NULL);
if(res != LV_RESULT_OK) {
LV_LOG_ERROR("Failed to open image");
return;
}
int32_t img_w = draw_dsc->header.w;
int32_t img_h = draw_dsc->header.h;
lv_area_t tile_area;
if(lv_area_get_width(&draw_dsc->image_area) >= 0) {
tile_area = draw_dsc->image_area;
}
else {
tile_area = *coords;
}
lv_area_set_width(&tile_area, img_w);
lv_area_set_height(&tile_area, img_h);
int32_t tile_x_start = tile_area.x1;
lv_area_t relative_decoded_area = {
.x1 = LV_COORD_MIN,
.y1 = LV_COORD_MIN,
.x2 = LV_COORD_MIN,
.y2 = LV_COORD_MIN,
};
while(tile_area.y1 <= coords->y2) {
while(tile_area.x1 <= coords->x2) {
lv_area_t clipped_img_area;
if(lv_area_intersect(&clipped_img_area, &tile_area, coords)) {
img_decode_and_draw(t, draw_dsc, &decoder_dsc, &relative_decoded_area, &tile_area, &clipped_img_area,
draw_core_cb);
}
tile_area.x1 += img_w;
tile_area.x2 += img_w;
}
tile_area.y1 += img_h;
tile_area.y2 += img_h;
tile_area.x1 = tile_x_start;
tile_area.x2 = tile_x_start + img_w - 1;
}
lv_image_decoder_close(&decoder_dsc);
}
void lv_image_buf_get_transformed_area(lv_area_t * res, int32_t w, int32_t h, int32_t angle,
uint16_t scale_x, uint16_t scale_y, const lv_point_t * pivot)
{
if(angle == 0 && scale_x == LV_SCALE_NONE && scale_y == LV_SCALE_NONE) {
res->x1 = 0;
res->y1 = 0;
res->x2 = w - 1;
res->y2 = h - 1;
return;
}
lv_point_t p[4] = {
{0, 0},
{w, 0},
{0, h},
{w, h},
};
lv_point_transform(&p[0], angle, scale_x, scale_y, pivot, true);
lv_point_transform(&p[1], angle, scale_x, scale_y, pivot, true);
lv_point_transform(&p[2], angle, scale_x, scale_y, pivot, true);
lv_point_transform(&p[3], angle, scale_x, scale_y, pivot, true);
res->x1 = LV_MIN4(p[0].x, p[1].x, p[2].x, p[3].x);
res->x2 = LV_MAX4(p[0].x, p[1].x, p[2].x, p[3].x) - 1;
res->y1 = LV_MIN4(p[0].y, p[1].y, p[2].y, p[3].y);
res->y2 = LV_MAX4(p[0].y, p[1].y, p[2].y, p[3].y) - 1;
}
/**********************
* STATIC FUNCTIONS
**********************/
static void img_decode_and_draw(lv_draw_task_t * t, const lv_draw_image_dsc_t * draw_dsc,
lv_image_decoder_dsc_t * decoder_dsc, lv_area_t * relative_decoded_area,
const lv_area_t * img_area, const lv_area_t * clipped_img_area,
lv_draw_image_core_cb draw_core_cb)
{
lv_draw_image_sup_t sup;
sup.alpha_color = draw_dsc->recolor;
sup.palette = decoder_dsc->palette;
sup.palette_size = decoder_dsc->palette_size;
/*The whole image is available, just draw it*/
if(decoder_dsc->decoded && (relative_decoded_area == NULL || relative_decoded_area->x1 == LV_COORD_MIN)) {
draw_core_cb(t, draw_dsc, decoder_dsc, &sup, img_area, clipped_img_area);
}
/*Draw in smaller pieces*/
else {
lv_area_t relative_full_area_to_decode = *clipped_img_area;
lv_area_move(&relative_full_area_to_decode, -img_area->x1, -img_area->y1);
lv_area_t tmp;
if(relative_decoded_area == NULL) relative_decoded_area = &tmp;
relative_decoded_area->x1 = LV_COORD_MIN;
relative_decoded_area->y1 = LV_COORD_MIN;
relative_decoded_area->x2 = LV_COORD_MIN;
relative_decoded_area->y2 = LV_COORD_MIN;
lv_result_t res = LV_RESULT_OK;
while(res == LV_RESULT_OK) {
res = lv_image_decoder_get_area(decoder_dsc, &relative_full_area_to_decode, relative_decoded_area);
lv_area_t absolute_decoded_area = *relative_decoded_area;
lv_area_move(&absolute_decoded_area, img_area->x1, img_area->y1);
if(res == LV_RESULT_OK) {
/*Limit draw area to the current decoded area and draw the image*/
lv_area_t clipped_img_area_sub;
if(lv_area_intersect(&clipped_img_area_sub, clipped_img_area, &absolute_decoded_area)) {
draw_core_cb(t, draw_dsc, decoder_dsc, &sup,
&absolute_decoded_area, &clipped_img_area_sub);
}
}
}
}
}
@@ -0,0 +1,165 @@
/**
* @file lv_draw_image.h
*
*/
#ifndef LV_DRAW_IMAGE_H
#define LV_DRAW_IMAGE_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "lv_draw.h"
#include "lv_image_decoder.h"
#include "lv_draw_buf.h"
#include "../misc/lv_style.h"
/*********************
* DEFINES
*********************/
/**********************
* MACROS
**********************/
struct _lv_draw_image_dsc_t {
lv_draw_dsc_base_t base;
/**The image source: pointer to `lv_image_dsc_t` or a path to a file*/
const void * src;
/**The header of the image. Initialized internally in `lv_draw_image` */
lv_image_header_t header;
/**Clip the corner of the image with this radius. Use `LV_RADIUS_CIRCLE` for max. radius */
int32_t clip_radius;
/**The rotation of the image in 0.1 degree unit. E.g. 234 means 23.4° */
int32_t rotation;
/**Horizontal scale (zoom) of the image.
* 256 (LV_SCALE_NONE): means no zoom, 512 double size, 128 half size.*/
int32_t scale_x;
/**Same as `scale_y` but vertically*/
int32_t scale_y;
/**Parallelogram like transformation of the image horizontally in 0.1 degree unit. E.g. 456 means 45.6°.*/
int32_t skew_x;
/**Same as `skew_x` but vertically*/
int32_t skew_y;
/**The pivot point of transformation (scale and rotation).
* 0;0 is the top left corner of the image. Can be outside of the image too.*/
lv_point_t pivot;
/**Mix this color to the images. In case of `LV_COLOR_FORMAT_A8` it will be the color of the visible pixels*/
lv_color_t recolor;
/**The intensity of recoloring. 0 means, no recolor, 255 means full cover (transparent pixels remain transparent)*/
lv_opa_t recolor_opa;
/**Opacity in 0...255 range.
* LV_OPA_TRANSP, LV_OPA_10, LV_OPA_20, .. LV_OPA_COVER can be used as well*/
lv_opa_t opa;
/**Describes how to blend the pixels of the image to the background.
* See `lv_blend_mode_t` for more details.
*/
lv_blend_mode_t blend_mode : 4;
/**1: perform the transformation with anti-alaising */
uint16_t antialias : 1;
/**If the image is smaller than the `image_area` field of `lv_draw_image_dsc_t`
* tile the image (repeat is both horizontally and vertically) to fill the
* `image_area` area*/
uint16_t tile : 1;
/**Used internally to store some information about the palette or the color of A8 images*/
lv_draw_image_sup_t * sup;
/** Used to indicate the entire original, non-clipped area where the image is to be drawn.
* This is important for:
* 1. Layer rendering, where it might happen that only a smaller area of the layer is rendered and e.g.
* `clip_radius` needs to know what the original image was.
* 2. Tiled images, where the target draw area is larger than the image to be tiled.
*/
lv_area_t image_area;
/**Pointer to an A8 or L8 image descriptor to mask the image with.
* The mask is always center aligned. */
const lv_image_dsc_t * bitmap_mask_src;
};
/**
* PErform the actual rendering of a decoded image
* @param t pointer to a draw task
* @param draw_dsc the draw descriptor of the image
* @param decoder_dsc pointer to the decoded image's descriptor
* @param sup supplementary data
* @param img_coords the absolute coordinates of the image
* @param clipped_img_area the absolute clip coordinates
*/
typedef void (*lv_draw_image_core_cb)(lv_draw_task_t * t, const lv_draw_image_dsc_t * draw_dsc,
const lv_image_decoder_dsc_t * decoder_dsc, lv_draw_image_sup_t * sup,
const lv_area_t * img_coords, const lv_area_t * clipped_img_area);
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Initialize an image draw descriptor.
* @param dsc pointer to a draw descriptor
*/
void lv_draw_image_dsc_init(lv_draw_image_dsc_t * dsc);
/**
* Try to get an image draw descriptor from a draw task.
* @param task draw task
* @return the task's draw descriptor or NULL if the task is not of type LV_DRAW_TASK_TYPE_IMAGE
*/
lv_draw_image_dsc_t * lv_draw_task_get_image_dsc(lv_draw_task_t * task);
/**
* Create an image draw task
* @param layer pointer to a layer
* @param dsc pointer to an initialized draw descriptor
* @param coords the coordinates of the image
* @note `coords` can be small than the real image area
* (if only a part of the image is rendered)
* or can be larger (in case of tiled images). .
*/
void lv_draw_image(lv_layer_t * layer, const lv_draw_image_dsc_t * dsc, const lv_area_t * coords);
/**
* Create a draw task to blend a layer to another layer
* @param layer pointer to a layer
* @param dsc pointer to an initialized draw descriptor. `src` must be set to the layer to blend
* @param coords the coordinates of the layer.
* @note `coords` can be small than the total widget area from which the layer is created
* (if only a part of the widget was rendered to a layer)
*/
void lv_draw_layer(lv_layer_t * layer, const lv_draw_image_dsc_t * dsc, const lv_area_t * coords);
/**
* Get the type of an image source
* @param src pointer to an image source:
* - pointer to an 'lv_image_t' variable (image stored internally and compiled into the code)
* - a path to a file (e.g. "S:/folder/image.bin")
* - or a symbol (e.g. LV_SYMBOL_CLOSE)
* @return type of the image source LV_IMAGE_SRC_VARIABLE/FILE/SYMBOL/UNKNOWN
*/
lv_image_src_t lv_image_src_get_type(const void * src);
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_DRAW_IMAGE_H*/
@@ -0,0 +1,85 @@
/**
* @file lv_draw_image_private.h
*
*/
#ifndef LV_DRAW_IMAGE_PRIVATE_H
#define LV_DRAW_IMAGE_PRIVATE_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "lv_draw_image.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* TYPEDEFS
**********************/
struct _lv_draw_image_sup_t {
lv_color_t alpha_color;
const lv_color32_t * palette;
uint32_t palette_size : 9;
};
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Can be used by draw units to handle the decoding and
* prepare everything for the actual image rendering
* @param t pointer to a draw task
* @param draw_dsc the draw descriptor of the image
* @param coords the absolute coordinates of the image
* @param draw_core_cb a callback to perform the actual rendering
*/
void lv_draw_image_normal_helper(lv_draw_task_t * t, const lv_draw_image_dsc_t * draw_dsc,
const lv_area_t * coords, lv_draw_image_core_cb draw_core_cb);
/**
* Can be used by draw units for TILED images to handle the decoding and
* prepare everything for the actual image rendering
* @param t pointer to a draw task
* @param draw_dsc the draw descriptor of the image
* @param coords the absolute coordinates of the image
* @param draw_core_cb a callback to perform the actual rendering
*/
void lv_draw_image_tiled_helper(lv_draw_task_t * t, const lv_draw_image_dsc_t * draw_dsc,
const lv_area_t * coords, lv_draw_image_core_cb draw_core_cb);
/**
* Get the area of a rectangle if its rotated and scaled
* @param res store the coordinates here
* @param w width of the rectangle to transform
* @param h height of the rectangle to transform
* @param angle angle of rotation
* @param scale_x zoom in x direction, (256 no zoom)
* @param scale_y zoom in y direction, (256 no zoom)
* @param pivot x,y pivot coordinates of rotation
*/
void lv_image_buf_get_transformed_area(lv_area_t * res, int32_t w, int32_t h, int32_t angle,
uint16_t scale_x, uint16_t scale_y, const lv_point_t * pivot);
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_DRAW_IMAGE_PRIVATE_H*/
@@ -0,0 +1,636 @@
/**
* @file lv_draw_label.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_draw_label_private.h"
#include "lv_draw_private.h"
#include "../misc/lv_area_private.h"
#include "lv_draw_vector_private.h"
#include "lv_draw_rect_private.h"
#include "../core/lv_obj.h"
#include "../misc/lv_math.h"
#include "../core/lv_obj_event.h"
#include "../misc/lv_bidi_private.h"
#include "../misc/lv_text_private.h"
#include "../misc/lv_assert.h"
#include "../stdlib/lv_mem.h"
#include "../stdlib/lv_string.h"
#include "../core/lv_global.h"
/*********************
* DEFINES
*********************/
#define LABEL_RECOLOR_PAR_LENGTH 6
#define LV_LABEL_HINT_UPDATE_TH 1024 /*Update the "hint" if the label's y coordinates have changed more then this*/
#define font_draw_buf_handlers &(LV_GLOBAL_DEFAULT()->font_draw_buf_handlers)
/**********************
* TYPEDEFS
**********************/
enum {
RECOLOR_CMD_STATE_WAIT_FOR_PARAMETER,
RECOLOR_CMD_STATE_PARAMETER,
RECOLOR_CMD_STATE_TEXT_INPUT,
};
typedef unsigned char cmd_state_t;
/**********************
* STATIC PROTOTYPES
**********************/
static uint8_t hex_char_to_num(char hex);
/**********************
* STATIC VARIABLES
**********************/
/**********************
* GLOBAL VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
void lv_draw_letter_dsc_init(lv_draw_letter_dsc_t * dsc)
{
lv_memzero(dsc, sizeof(lv_draw_letter_dsc_t));
dsc->opa = LV_OPA_COVER;
dsc->color = lv_color_black();
dsc->font = LV_FONT_DEFAULT;
dsc->rotation = 0;
dsc->scale_x = LV_SCALE_NONE;
dsc->scale_y = LV_SCALE_NONE;
dsc->base.dsc_size = sizeof(lv_draw_letter_dsc_t);
}
void lv_draw_label_dsc_init(lv_draw_label_dsc_t * dsc)
{
lv_memzero(dsc, sizeof(lv_draw_label_dsc_t));
dsc->opa = LV_OPA_COVER;
dsc->color = lv_color_black();
dsc->text_length = LV_TEXT_LEN_MAX;
dsc->font = LV_FONT_DEFAULT;
dsc->sel_start = LV_DRAW_LABEL_NO_TXT_SEL;
dsc->sel_end = LV_DRAW_LABEL_NO_TXT_SEL;
dsc->sel_color = lv_color_black();
dsc->sel_bg_color = lv_palette_main(LV_PALETTE_BLUE);
dsc->bidi_dir = LV_BASE_DIR_LTR;
dsc->base.dsc_size = sizeof(lv_draw_label_dsc_t);
}
lv_draw_label_dsc_t * lv_draw_task_get_label_dsc(lv_draw_task_t * task)
{
return task->type == LV_DRAW_TASK_TYPE_LABEL ? (lv_draw_label_dsc_t *)task->draw_dsc : NULL;
}
void lv_draw_glyph_dsc_init(lv_draw_glyph_dsc_t * dsc)
{
lv_memzero(dsc, sizeof(lv_draw_glyph_dsc_t));
}
void LV_ATTRIBUTE_FAST_MEM lv_draw_label(lv_layer_t * layer, const lv_draw_label_dsc_t * dsc,
const lv_area_t * coords)
{
if(dsc->opa <= LV_OPA_MIN) return;
if(dsc->text == NULL || dsc->text[0] == '\0') return;
if(dsc->font == NULL) {
LV_LOG_WARN("dsc->font == NULL");
return;
}
LV_PROFILER_DRAW_BEGIN;
lv_draw_task_t * t = lv_draw_add_task(layer, coords, LV_DRAW_TASK_TYPE_LABEL);
lv_memcpy(t->draw_dsc, dsc, sizeof(*dsc));
/*The text is stored in a local variable so malloc memory for it*/
if(dsc->text_local) {
lv_draw_label_dsc_t * new_dsc = t->draw_dsc;
new_dsc->text = lv_strndup(dsc->text, dsc->text_length);
LV_ASSERT_MALLOC(new_dsc->text);
}
lv_draw_finalize_task_creation(layer, t);
LV_PROFILER_DRAW_END;
}
void LV_ATTRIBUTE_FAST_MEM lv_draw_character(lv_layer_t * layer, lv_draw_label_dsc_t * dsc,
const lv_point_t * point, uint32_t unicode_letter)
{
if(dsc->opa <= LV_OPA_MIN) return;
if(dsc->font == NULL) {
LV_LOG_WARN("dsc->font == NULL");
return;
}
if(lv_text_is_marker(unicode_letter)) return;
LV_PROFILER_DRAW_BEGIN;
lv_font_glyph_dsc_t g;
lv_font_get_glyph_dsc(dsc->font, &g, unicode_letter, 0);
lv_area_t a;
a.x1 = point->x;
a.y1 = point->y;
a.x2 = a.x1 + g.adv_w;
a.y2 = a.y1 + lv_font_get_line_height(g.resolved_font ? g.resolved_font : dsc->font);
/*lv_draw_label needs UTF8 text so convert the Unicode character to an UTF8 string */
uint32_t letter_buf[2];
letter_buf[0] = lv_text_unicode_to_encoded(unicode_letter);
letter_buf[1] = '\0';
const char * letter_buf_char = (const char *)letter_buf;
#if LV_BIG_ENDIAN_SYSTEM
while(*letter_buf_char == 0) ++letter_buf_char;
#endif
dsc->text = letter_buf_char;
dsc->text_local = 1;
lv_draw_label(layer, dsc, &a);
LV_PROFILER_DRAW_END;
}
void LV_ATTRIBUTE_FAST_MEM lv_draw_letter(lv_layer_t * layer, lv_draw_letter_dsc_t * dsc, const lv_point_t * point)
{
if(dsc->opa <= LV_OPA_MIN) return;
if(dsc->font == NULL) {
LV_LOG_WARN("dsc->font == NULL");
return;
}
const lv_font_t * font = dsc->font;
LV_PROFILER_DRAW_BEGIN;
lv_font_glyph_dsc_t g;
lv_font_get_glyph_dsc(font, &g, dsc->unicode, 0);
font = g.resolved_font ? g.resolved_font : dsc->font;
lv_area_t a;
a.x1 = point->x;
a.y1 = point->y;
a.x2 = a.x1 + g.adv_w;
a.y2 = a.y1 + lv_font_get_line_height(font);
dsc->pivot.x = g.adv_w / 2 ;
dsc->pivot.y = font->line_height - font->base_line;
lv_draw_task_t * t = lv_draw_add_task(layer, &a, LV_DRAW_TASK_TYPE_LETTER);
lv_memcpy(t->draw_dsc, dsc, sizeof(*dsc));
lv_draw_finalize_task_creation(layer, t);
LV_PROFILER_DRAW_END;
}
void lv_draw_label_iterate_characters(lv_draw_task_t * t, const lv_draw_label_dsc_t * dsc,
const lv_area_t * coords,
lv_draw_glyph_cb_t cb)
{
lv_draw_dsc_base_t * base_dsc = t->draw_dsc;
const lv_font_t * font = dsc->font;
int32_t w;
lv_area_t clipped_area;
bool clip_ok = lv_area_intersect(&clipped_area, coords, &t->clip_area);
if(!clip_ok) return;
lv_text_align_t align = dsc->align;
lv_base_dir_t base_dir = dsc->bidi_dir;
lv_bidi_calculate_align(&align, &base_dir, dsc->text);
if((dsc->flag & LV_TEXT_FLAG_EXPAND) == 0) {
/*Normally use the label's width as width*/
w = lv_area_get_width(coords);
}
else {
/*If EXPAND is enabled then not limit the text's width to the object's width*/
if(base_dsc->obj && !lv_obj_has_flag(base_dsc->obj, LV_OBJ_FLAG_SEND_DRAW_TASK_EVENTS)) {
w = dsc->text_size.x;
}
else {
lv_point_t p;
lv_text_get_size(&p, dsc->text, dsc->font, dsc->letter_space, dsc->line_space, LV_COORD_MAX,
dsc->flag);
w = p.x;
}
}
int32_t line_height_font = lv_font_get_line_height(font);
int32_t line_height = line_height_font + dsc->line_space;
/*Init variables for the first line*/
int32_t line_width = 0;
lv_point_t pos;
lv_point_set(&pos, coords->x1, coords->y1);
int32_t x_ofs = 0;
int32_t y_ofs = 0;
x_ofs = dsc->ofs_x;
y_ofs = dsc->ofs_y;
pos.y += y_ofs;
uint32_t line_start = 0;
int32_t last_line_start = -1;
/*Check the hint to use the cached info*/
if(dsc->hint && y_ofs == 0 && coords->y1 < 0) {
/*If the label changed too much recalculate the hint.*/
if(LV_ABS(dsc->hint->coord_y - coords->y1) > LV_LABEL_HINT_UPDATE_TH - 2 * line_height) {
dsc->hint->line_start = -1;
}
last_line_start = dsc->hint->line_start;
}
/*Use the hint if it's valid*/
if(dsc->hint && last_line_start >= 0) {
line_start = last_line_start;
pos.y += dsc->hint->y;
}
uint32_t remaining_len = dsc->text_length;
uint32_t line_end = line_start + lv_text_get_next_line(&dsc->text[line_start], remaining_len, font, dsc->letter_space,
w, NULL, dsc->flag);
/*Go the first visible line*/
while(pos.y + line_height_font < t->clip_area.y1) {
/*Go to next line*/
line_start = line_end;
line_end += lv_text_get_next_line(&dsc->text[line_start], remaining_len, font, dsc->letter_space, w, NULL, dsc->flag);
pos.y += line_height;
/*Save at the threshold coordinate*/
if(dsc->hint && pos.y >= -LV_LABEL_HINT_UPDATE_TH && dsc->hint->line_start < 0) {
dsc->hint->line_start = line_start;
dsc->hint->y = pos.y - coords->y1;
dsc->hint->coord_y = coords->y1;
}
if(dsc->text[line_start] == '\0') return;
}
/*Align to middle*/
if(align == LV_TEXT_ALIGN_CENTER) {
line_width = lv_text_get_width_with_flags(&dsc->text[line_start], line_end - line_start, font, dsc->letter_space,
dsc->flag);
pos.x += (lv_area_get_width(coords) - line_width) / 2;
}
/*Align to the right*/
else if(align == LV_TEXT_ALIGN_RIGHT) {
line_width = lv_text_get_width_with_flags(&dsc->text[line_start], line_end - line_start, font, dsc->letter_space,
dsc->flag);
pos.x += lv_area_get_width(coords) - line_width;
}
uint32_t sel_start = dsc->sel_start;
uint32_t sel_end = dsc->sel_end;
if(sel_start > sel_end) {
uint32_t tmp = sel_start;
sel_start = sel_end;
sel_end = tmp;
}
lv_area_t bg_coords;
lv_draw_glyph_dsc_t draw_letter_dsc;
lv_draw_glyph_dsc_init(&draw_letter_dsc);
draw_letter_dsc.opa = dsc->opa;
draw_letter_dsc.bg_coords = &bg_coords;
draw_letter_dsc.color = dsc->color;
draw_letter_dsc.rotation = dsc->rotation;
/* Set letter outline stroke attributes */
draw_letter_dsc.outline_stroke_width = dsc->outline_stroke_width;
draw_letter_dsc.outline_stroke_opa = dsc->outline_stroke_opa;
draw_letter_dsc.outline_stroke_color = dsc->outline_stroke_color;
lv_draw_fill_dsc_t fill_dsc;
lv_draw_fill_dsc_init(&fill_dsc);
fill_dsc.opa = dsc->opa;
int32_t underline_width = font->underline_thickness ? font->underline_thickness : 1;
int32_t line_start_x;
uint32_t next_char_offset;
uint32_t recolor_command_start_index = 0;
int32_t letter_w;
cmd_state_t recolor_cmd_state = RECOLOR_CMD_STATE_WAIT_FOR_PARAMETER;
lv_color_t recolor = lv_color_black(); /* Holds the selected color inside the recolor command */
uint8_t is_first_space_after_cmd = 0;
/*Write out all lines*/
while(remaining_len && dsc->text[line_start] != '\0') {
pos.x += x_ofs;
line_start_x = pos.x;
/*Write all letter of a line*/
recolor_cmd_state = RECOLOR_CMD_STATE_WAIT_FOR_PARAMETER;
next_char_offset = 0;
#if LV_USE_BIDI
size_t bidi_size = line_end - line_start;
char * bidi_txt = lv_malloc(bidi_size + 1);
LV_ASSERT_MALLOC(bidi_txt);
/**
* has_bided = 1: already executed lv_bidi_process_paragraph.
* has_bided = 0: has not been executed lv_bidi_process_paragraph.*/
if(dsc->has_bided) {
lv_memcpy(bidi_txt, &dsc->text[line_start], bidi_size);
}
else {
lv_bidi_process_paragraph(dsc->text + line_start, bidi_txt, bidi_size, base_dir, NULL, 0);
}
#else
const char * bidi_txt = dsc->text + line_start;
#endif
while(next_char_offset < remaining_len && next_char_offset < line_end - line_start) {
uint32_t logical_char_pos = 0;
/* Check if the text selection is enabled */
if(sel_start != LV_DRAW_LABEL_NO_TXT_SEL && sel_end != LV_DRAW_LABEL_NO_TXT_SEL) {
#if LV_USE_BIDI
if(dsc->has_bided) {
logical_char_pos = lv_text_encoded_get_char_id(dsc->text, line_start + next_char_offset);
}
else {
logical_char_pos = lv_text_encoded_get_char_id(dsc->text, line_start);
uint32_t c_idx = lv_text_encoded_get_char_id(bidi_txt, next_char_offset);
logical_char_pos += lv_bidi_get_logical_pos(bidi_txt, NULL, line_end - line_start, base_dir, c_idx, NULL);
}
#else
logical_char_pos = lv_text_encoded_get_char_id(dsc->text, line_start + next_char_offset);
#endif
}
uint32_t letter;
uint32_t letter_next;
lv_text_encoded_letter_next_2(bidi_txt, &letter, &letter_next, &next_char_offset);
/* If recolor is enabled */
if((dsc->flag & LV_TEXT_FLAG_RECOLOR) != 0) {
if(letter == (uint32_t)LV_TXT_COLOR_CMD[0]) {
/* Handle the recolor command marker depending of the current recolor state */
if(recolor_cmd_state == RECOLOR_CMD_STATE_WAIT_FOR_PARAMETER) {
recolor_command_start_index = next_char_offset;
recolor_cmd_state = RECOLOR_CMD_STATE_PARAMETER;
continue;
}
/*Other start char in parameter escaped cmd. char*/
else if(recolor_cmd_state == RECOLOR_CMD_STATE_PARAMETER) {
recolor_cmd_state = RECOLOR_CMD_STATE_WAIT_FOR_PARAMETER;
}
/* If letter is LV_TXT_COLOR_CMD and we were in the CMD_STATE_IN then the recolor close marked has been found */
else if(recolor_cmd_state == RECOLOR_CMD_STATE_TEXT_INPUT) {
recolor_cmd_state = RECOLOR_CMD_STATE_WAIT_FOR_PARAMETER;
continue;
}
}
/* Find the first space (aka ' ') after the recolor command parameter, we need to skip rendering it */
if((recolor_cmd_state == RECOLOR_CMD_STATE_PARAMETER) && (letter == ' ') && (is_first_space_after_cmd == 0)) {
is_first_space_after_cmd = 1;
}
else {
is_first_space_after_cmd = 0;
}
/* Skip the color parameter and wait the space after it
* Once we have reach the space ' ', then we will extract the color information
* and store it into the recolor variable */
if(recolor_cmd_state == RECOLOR_CMD_STATE_PARAMETER) {
/* Not an space? Continue with the next character */
if(letter != ' ') {
continue;
}
/*Get the recolor parameter*/
if((next_char_offset - recolor_command_start_index) == LABEL_RECOLOR_PAR_LENGTH + 1) {
/* Temporary buffer to hold the recolor information */
char buf[LABEL_RECOLOR_PAR_LENGTH + 1];
lv_memcpy(buf, &bidi_txt[recolor_command_start_index], LABEL_RECOLOR_PAR_LENGTH);
buf[LABEL_RECOLOR_PAR_LENGTH] = '\0';
uint8_t r, g, b;
r = (hex_char_to_num(buf[0]) << 4) + hex_char_to_num(buf[1]);
g = (hex_char_to_num(buf[2]) << 4) + hex_char_to_num(buf[3]);
b = (hex_char_to_num(buf[4]) << 4) + hex_char_to_num(buf[5]);
recolor = lv_color_make(r, g, b);
}
else {
recolor.red = dsc->color.red;
recolor.blue = dsc->color.blue;
recolor.green = dsc->color.green;
}
/*After the parameter the text is in the command*/
recolor_cmd_state = RECOLOR_CMD_STATE_TEXT_INPUT;
}
/* Don't draw the first space after the recolor command */
if(is_first_space_after_cmd) {
continue;
}
}
/* If we're in the CMD_STATE_IN state then we need to subtract the recolor command length */
if(((dsc->flag & LV_TEXT_FLAG_RECOLOR) != 0) && (recolor_cmd_state == RECOLOR_CMD_STATE_TEXT_INPUT)) {
logical_char_pos -= (LABEL_RECOLOR_PAR_LENGTH + 1);
}
letter_w = lv_font_get_glyph_width(font, letter, letter_next);
/*Always set the bg_coordinates for placeholder drawing*/
bg_coords.x1 = pos.x;
bg_coords.y1 = pos.y;
bg_coords.x2 = pos.x + letter_w - 1;
bg_coords.y2 = pos.y + line_height - 1;
if(next_char_offset >= line_end - line_start) {
if(dsc->decor & LV_TEXT_DECOR_UNDERLINE) {
lv_area_t fill_area;
fill_area.x1 = line_start_x;
fill_area.x2 = pos.x + letter_w - 1;
fill_area.y1 = pos.y + font->line_height - font->base_line - font->underline_position;
fill_area.y2 = fill_area.y1 + underline_width - 1;
fill_dsc.color = dsc->color;
cb(t, NULL, &fill_dsc, &fill_area);
}
if(dsc->decor & LV_TEXT_DECOR_STRIKETHROUGH) {
lv_area_t fill_area;
fill_area.x1 = line_start_x;
fill_area.x2 = pos.x + letter_w - 1;
fill_area.y1 = pos.y + (font->line_height - font->base_line) * 2 / 3 + font->underline_thickness / 2;
fill_area.y2 = fill_area.y1 + underline_width - 1;
fill_dsc.color = dsc->color;
cb(t, NULL, &fill_dsc, &fill_area);
}
}
/* Handle text selection */
if(sel_start != LV_DRAW_LABEL_NO_TXT_SEL && sel_end != LV_DRAW_LABEL_NO_TXT_SEL
&& logical_char_pos >= sel_start && logical_char_pos < sel_end) {
draw_letter_dsc.color = dsc->sel_color;
fill_dsc.color = dsc->sel_bg_color;
cb(t, NULL, &fill_dsc, &bg_coords);
}
else if(recolor_cmd_state == RECOLOR_CMD_STATE_TEXT_INPUT) {
draw_letter_dsc.color = recolor;
}
else {
draw_letter_dsc.color = dsc->color;
}
lv_draw_unit_draw_letter(t, &draw_letter_dsc, &pos, font, letter, cb);
if(letter_w > 0) {
pos.x += letter_w + dsc->letter_space;
}
}
#if LV_USE_BIDI
lv_free(bidi_txt);
bidi_txt = NULL;
#endif
/*Go to next line*/
remaining_len -= line_end - line_start;
line_start = line_end;
if(remaining_len) {
line_end += lv_text_get_next_line(&dsc->text[line_start], remaining_len, font, dsc->letter_space, w, NULL, dsc->flag);
}
pos.x = coords->x1;
/*Align to middle*/
if(align == LV_TEXT_ALIGN_CENTER) {
line_width =
lv_text_get_width_with_flags(&dsc->text[line_start], line_end - line_start, font, dsc->letter_space, dsc->flag);
pos.x += (lv_area_get_width(coords) - line_width) / 2;
}
/*Align to the right*/
else if(align == LV_TEXT_ALIGN_RIGHT) {
line_width =
lv_text_get_width_with_flags(&dsc->text[line_start], line_end - line_start, font, dsc->letter_space, dsc->flag);
pos.x += lv_area_get_width(coords) - line_width;
}
/*Go the next line position*/
pos.y += line_height;
if(pos.y > t->clip_area.y2) break;
}
if(draw_letter_dsc._draw_buf) lv_draw_buf_destroy(draw_letter_dsc._draw_buf);
LV_ASSERT_MEM_INTEGRITY();
}
/**********************
* STATIC FUNCTIONS
**********************/
/**
* Convert a hexadecimal characters to a number (0..15)
* @param hex Pointer to a hexadecimal character (0..9, A..F)
* @return the numerical value of `hex` or 0 on error
*/
static uint8_t hex_char_to_num(char hex)
{
if(hex >= '0' && hex <= '9') return hex - '0';
if(hex >= 'a') hex -= 'a' - 'A'; /*Convert to upper case*/
return 'A' <= hex && hex <= 'F' ? hex - 'A' + 10 : 0;
}
void lv_draw_unit_draw_letter(lv_draw_task_t * t, lv_draw_glyph_dsc_t * dsc, const lv_point_t * pos,
const lv_font_t * font, uint32_t letter, lv_draw_glyph_cb_t cb)
{
lv_font_glyph_dsc_t g;
if(lv_text_is_marker(letter)) /*Markers are valid letters but should not be rendered.*/
return;
LV_PROFILER_DRAW_BEGIN;
bool g_ret = lv_font_get_glyph_dsc(font, &g, letter, '\0');
if(g_ret == false) {
/*Add warning if the dsc is not found*/
LV_LOG_WARN("lv_draw_letter: glyph dsc. not found for U+%" LV_PRIX32, letter);
}
/*Don't draw anything if the character is empty. E.g. space*/
if((g.box_h == 0) || (g.box_w == 0)) {
LV_PROFILER_DRAW_END;
return;
}
lv_area_t letter_coords;
letter_coords.x1 = pos->x + g.ofs_x;
letter_coords.x2 = letter_coords.x1 + g.box_w - 1;
letter_coords.y1 = pos->y + (font->line_height - font->base_line) - g.box_h - g.ofs_y;
letter_coords.y2 = letter_coords.y1 + g.box_h - 1;
lv_area_move(&letter_coords, -dsc->pivot.x, -dsc->pivot.y);
/*If the letter is completely out of mask don't draw it*/
if(lv_area_is_out(&letter_coords, &t->clip_area, 0) &&
dsc->bg_coords &&
lv_area_is_out(dsc->bg_coords, &t->clip_area, 0)) {
LV_PROFILER_DRAW_END;
return;
}
if(g.resolved_font) {
lv_draw_buf_t * draw_buf = NULL;
if(LV_FONT_GLYPH_FORMAT_NONE < g.format && g.format < LV_FONT_GLYPH_FORMAT_IMAGE) {
/*Only check draw buf for bitmap glyph*/
draw_buf = lv_draw_buf_reshape(dsc->_draw_buf, 0, g.box_w, g.box_h, LV_STRIDE_AUTO);
if(draw_buf == NULL) {
if(dsc->_draw_buf) lv_draw_buf_destroy(dsc->_draw_buf);
uint32_t h = LV_ROUND_UP(g.box_h, 32); /*Assume a larger size to avoid many reallocations*/
draw_buf = lv_draw_buf_create_ex(font_draw_buf_handlers, g.box_w, h, LV_COLOR_FORMAT_A8, LV_STRIDE_AUTO);
LV_ASSERT_MALLOC(draw_buf);
draw_buf->header.h = g.box_h;
dsc->_draw_buf = draw_buf;
}
}
dsc->format = g.format;
if(g.format == LV_FONT_GLYPH_FORMAT_VECTOR) {
/*Load the outline of the glyph, even if the function says bitmap*/
g.outline_stroke_width = dsc->outline_stroke_width;
dsc->glyph_data = (void *) lv_font_get_glyph_bitmap(&g, draw_buf);
dsc->format = dsc->glyph_data ? g.format : LV_FONT_GLYPH_FORMAT_NONE;
}
}
else {
dsc->format = LV_FONT_GLYPH_FORMAT_NONE;
}
dsc->letter_coords = &letter_coords;
dsc->g = &g;
cb(t, dsc, NULL, NULL);
lv_font_glyph_release_draw_data(&g);
LV_PROFILER_DRAW_END;
}
@@ -0,0 +1,250 @@
/**
* @file lv_draw_label.h
*
*/
#ifndef LV_DRAW_LABEL_H
#define LV_DRAW_LABEL_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "lv_draw.h"
#include "lv_draw_rect.h"
#include "../misc/lv_bidi.h"
#include "../misc/lv_text.h"
#include "../misc/lv_color.h"
#include "../misc/lv_style.h"
/*********************
* DEFINES
*********************/
#define LV_DRAW_LABEL_NO_TXT_SEL (0xFFFF)
/**********************
* TYPEDEFS
**********************/
typedef struct {
lv_draw_dsc_base_t base;
/**The text to draw*/
const char * text;
/**The size of the text*/
lv_point_t text_size;
/**The font to use. Fallback fonts are also handled.*/
const lv_font_t * font;
/**Color of the text*/
lv_color_t color;
/**Extra space between the lines*/
int32_t line_space;
/**Extra space between the characters*/
int32_t letter_space;
/**Offset the text with this value horizontally*/
int32_t ofs_x;
/**Offset the text with this value vertically*/
int32_t ofs_y;
/**Rotation of the letters in 0.1 degree unit*/
int32_t rotation;
/**The first characters index for selection (not byte index). `LV_DRAW_LABEL_NO_TXT_SEL` for no selection*/
uint32_t sel_start;
/**The last characters's index for selection (not byte index). `LV_DRAW_LABEL_NO_TXT_SEL` for no selection*/
uint32_t sel_end;
/**Color of the selected characters*/
lv_color_t sel_color;
/**Background color of the selected characters*/
lv_color_t sel_bg_color;
/**The number of characters to render. 0: means render until reaching the `\0` termination.*/
uint32_t text_length;
/**Opacity of the text in 0...255 range.
* LV_OPA_TRANSP, LV_OPA_10, LV_OPA_20, .. LV_OPA_COVER can be used as well*/
lv_opa_t opa;
/**The alignment of the text `LV_TEXT_ALIGN_LEFT/RIGHT/CENTER`*/
lv_text_align_t align;
/**The base direction. Used when type setting Right-to-left (e.g. Arabic) texts*/
lv_base_dir_t bidi_dir;
/**Text decoration, e.g. underline*/
lv_text_decor_t decor : 3;
/**Some flags to control type setting*/
lv_text_flag_t flag : 5;
/**1: malloc a buffer and copy `text` there.
* 0: `text` will be valid during rendering.*/
uint8_t text_local : 1;
/**Indicate that the text is constant and its pointer can be safely saved e.g. in a cache.*/
uint8_t text_static : 1;
/**1: already executed lv_bidi_process_paragraph.
* 0: has not been executed lv_bidi_process_paragraph.*/
uint8_t has_bided : 1;
/**Pointer to an externally stored struct where some data can be cached to speed up rendering*/
lv_draw_label_hint_t * hint;
/* Properties of the letter outlines */
lv_opa_t outline_stroke_opa;
lv_color_t outline_stroke_color;
int32_t outline_stroke_width;
} lv_draw_label_dsc_t;
typedef struct {
lv_draw_dsc_base_t base;
uint32_t unicode;
const lv_font_t * font;
lv_color_t color;
int32_t rotation;
int32_t scale_x;
int32_t scale_y;
int32_t skew_x;
int32_t skew_y;
lv_point_t pivot;
lv_opa_t opa;
lv_text_decor_t decor : 3;
lv_blend_mode_t blend_mode : 3;
/* Properties of the letter outlines */
lv_opa_t outline_stroke_opa;
int32_t outline_stroke_width;
lv_color_t outline_stroke_color;
} lv_draw_letter_dsc_t;
/**
* Passed as a parameter to `lv_draw_label_iterate_characters` to
* draw the characters one by one
* @param t pointer to a draw task
* @param dsc pointer to `lv_draw_glyph_dsc_t` to describe the character to draw
* if NULL don't draw character
* @param fill_dsc pointer to a fill descriptor to draw a background for the character or
* underline or strike through
* if NULL do not fill anything
* @param fill_area the area to fill
* if NULL do not fill anything
*/
typedef void(*lv_draw_glyph_cb_t)(lv_draw_task_t * t, lv_draw_glyph_dsc_t * dsc, lv_draw_fill_dsc_t * fill_dsc,
const lv_area_t * fill_area);
/**********************
* GLOBAL PROTOTYPES
**********************/
void /* LV_ATTRIBUTE_FAST_MEM */ lv_draw_letter_dsc_init(lv_draw_letter_dsc_t * dsc);
/**
* Initialize a label draw descriptor
* @param dsc pointer to a draw descriptor
*/
void /* LV_ATTRIBUTE_FAST_MEM */ lv_draw_label_dsc_init(lv_draw_label_dsc_t * dsc);
/**
* Try to get a label draw descriptor from a draw task.
* @param task draw task
* @return the task's draw descriptor or NULL if the task is not of type LV_DRAW_TASK_TYPE_LABEL
*/
lv_draw_label_dsc_t * lv_draw_task_get_label_dsc(lv_draw_task_t * task);
/**
* Initialize a glyph draw descriptor.
* Used internally.
* @param dsc pointer to a draw descriptor
*/
void lv_draw_glyph_dsc_init(lv_draw_glyph_dsc_t * dsc);
/**
* Create a draw task to render a text
* @param layer pointer to a layer
* @param dsc pointer to draw descriptor
* @param coords coordinates of the character
*/
void /* LV_ATTRIBUTE_FAST_MEM */ lv_draw_label(lv_layer_t * layer, const lv_draw_label_dsc_t * dsc,
const lv_area_t * coords);
/**
* Create a draw task to render a single character
* @param layer pointer to a layer
* @param dsc pointer to draw descriptor
* @param point position of the label
* @param unicode_letter the letter to draw
*/
void /* LV_ATTRIBUTE_FAST_MEM */ lv_draw_character(lv_layer_t * layer, lv_draw_label_dsc_t * dsc,
const lv_point_t * point, uint32_t unicode_letter);
/**
* Draw a single letter
* @param layer pointer to a layer
* @param dsc pointer to draw descriptor
* @param point position of the label
*/
void /* LV_ATTRIBUTE_FAST_MEM */ lv_draw_letter(lv_layer_t * layer, lv_draw_letter_dsc_t * dsc,
const lv_point_t * point);
/**
* Should be used during rendering the characters to get the position and other
* parameters of the characters
* @param t pointer to a draw task
* @param dsc pointer to draw descriptor
* @param coords coordinates of the label
* @param cb a callback to call to draw each glyphs one by one
*/
void lv_draw_label_iterate_characters(lv_draw_task_t * t, const lv_draw_label_dsc_t * dsc,
const lv_area_t * coords, lv_draw_glyph_cb_t cb);
/**
* @brief Draw a single letter using the provided draw unit, glyph descriptor, position, font, and callback.
*
* This function is responsible for rendering a single character from a text string,
* applying the necessary styling described by the glyph descriptor (`dsc`). It handles
* the retrieval of the glyph's description, checks its visibility within the clipping area,
* and invokes the callback (`cb`) to render the glyph at the specified position (`pos`)
* using the given font (`font`).
*
* @param t Pointer to the drawing task.
* @param dsc Pointer to the descriptor containing styling for the glyph to be drawn.
* @param pos Pointer to the point coordinates where the letter should be drawn.
* @param font Pointer to the font containing the glyph.
* @param letter The Unicode code point of the letter to be drawn.
* @param cb Callback function to execute the actual rendering of the glyph.
*/
void lv_draw_unit_draw_letter(lv_draw_task_t * t, lv_draw_glyph_dsc_t * dsc, const lv_point_t * pos,
const lv_font_t * font, uint32_t letter, lv_draw_glyph_cb_t cb);
/***********************
* GLOBAL VARIABLES
***********************/
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_DRAW_LABEL_H*/
@@ -0,0 +1,74 @@
/**
* @file lv_draw_label_private.h
*
*/
#ifndef LV_DRAW_LABEL_PRIVATE_H
#define LV_DRAW_LABEL_PRIVATE_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "lv_draw_label.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/** Store some info to speed up drawing of very large texts
* It takes a lot of time to get the first visible character because
* all the previous characters needs to be checked to calculate the positions.
* This structure stores an earlier (e.g. at -1000 px) coordinate and the index of that line.
* Therefore the calculations can start from here.*/
struct _lv_draw_label_hint_t {
/** Index of the line at `y` coordinate*/
int32_t line_start;
/** Give the `y` coordinate of the first letter at `line start` index. Relative to the label's coordinates*/
int32_t y;
/** The 'y1' coordinate of the label when the hint was saved.
* Used to invalidate the hint if the label has moved too much.*/
int32_t coord_y;
};
struct _lv_draw_glyph_dsc_t {
/** Depends on `format` field, it could be image source or draw buf of bitmap or vector data. */
const void * glyph_data;
lv_font_glyph_format_t format;
const lv_area_t * letter_coords;
const lv_area_t * bg_coords;
lv_font_glyph_dsc_t * g;
lv_color_t color;
lv_opa_t opa;
lv_color_t outline_stroke_color;
lv_opa_t outline_stroke_opa;
int32_t outline_stroke_width;
int32_t rotation;
lv_point_t pivot; /**< Rotation pivot point associated with total glyph including line_height */
lv_draw_buf_t * _draw_buf; /**< a shared draw buf for get_bitmap, do not use it directly, use glyph_data instead */
};
/**********************
* GLOBAL PROTOTYPES
**********************/
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_DRAW_LABEL_PRIVATE_H*/
@@ -0,0 +1,76 @@
/**
* @file lv_draw_line.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_draw_private.h"
#include "../core/lv_refr.h"
#include "../misc/lv_math.h"
#include "../misc/lv_types.h"
#include "../stdlib/lv_string.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
void LV_ATTRIBUTE_FAST_MEM lv_draw_line_dsc_init(lv_draw_line_dsc_t * dsc)
{
lv_memzero(dsc, sizeof(lv_draw_line_dsc_t));
dsc->width = 1;
dsc->opa = LV_OPA_COVER;
dsc->color = lv_color_black();
dsc->base.dsc_size = sizeof(lv_draw_line_dsc_t);
}
lv_draw_line_dsc_t * lv_draw_task_get_line_dsc(lv_draw_task_t * task)
{
return task->type == LV_DRAW_TASK_TYPE_LINE ? (lv_draw_line_dsc_t *)task->draw_dsc : NULL;
}
void LV_ATTRIBUTE_FAST_MEM lv_draw_line(lv_layer_t * layer, const lv_draw_line_dsc_t * dsc)
{
if(dsc->width == 0) return;
if(dsc->opa <= LV_OPA_MIN) return;
LV_PROFILER_DRAW_BEGIN;
lv_area_t a;
a.x1 = (int32_t)LV_MIN(dsc->p1.x, dsc->p2.x) - dsc->width;
a.x2 = (int32_t)LV_MAX(dsc->p1.x, dsc->p2.x) + dsc->width;
a.y1 = (int32_t)LV_MIN(dsc->p1.y, dsc->p2.y) - dsc->width;
a.y2 = (int32_t)LV_MAX(dsc->p1.y, dsc->p2.y) + dsc->width;
lv_draw_task_t * t = lv_draw_add_task(layer, &a, LV_DRAW_TASK_TYPE_LINE);
lv_memcpy(t->draw_dsc, dsc, sizeof(*dsc));
lv_draw_finalize_task_creation(layer, t);
LV_PROFILER_DRAW_END;
}
/**********************
* STATIC FUNCTIONS
**********************/
@@ -0,0 +1,95 @@
/**
* @file lv_draw_line.h
*
*/
#ifndef LV_DRAW_LINE_H
#define LV_DRAW_LINE_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../lv_conf_internal.h"
#include "../misc/lv_color.h"
#include "../misc/lv_area.h"
#include "../misc/lv_style.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
typedef struct {
lv_draw_dsc_base_t base;
/**The first point of the line. If `LV_USE_FLOAT` is enabled float number can be also used*/
lv_point_precise_t p1;
/**The second point of the line. If `LV_USE_FLOAT` is enabled float number can be also used*/
lv_point_precise_t p2;
/**The color of the line*/
lv_color_t color;
/**The width (thickness) of the line*/
int32_t width;
/** The length of a dash (0: don't dash)*/
int32_t dash_width;
/** The length of the gaps between dashes (0: don't dash)*/
int32_t dash_gap;
/**Opacity of the line in 0...255 range.
* LV_OPA_TRANSP, LV_OPA_10, LV_OPA_20, .. LV_OPA_COVER can be used as well*/
lv_opa_t opa;
/**Make the line start rounded*/
uint8_t round_start : 1;
/**Make the line end rounded*/
uint8_t round_end : 1;
/**1: Do not bother with line ending (if it's not visible for any reason) */
uint8_t raw_end : 1;
} lv_draw_line_dsc_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Initialize a line draw descriptor
* @param dsc pointer to a draw descriptor
*/
void lv_draw_line_dsc_init(lv_draw_line_dsc_t * dsc);
/**
* Try to get a line draw descriptor from a draw task.
* @param task draw task
* @return the task's draw descriptor or NULL if the task is not of type LV_DRAW_TASK_TYPE_LINE
*/
lv_draw_line_dsc_t * lv_draw_task_get_line_dsc(lv_draw_task_t * task);
/**
* Create a line draw task
* @param layer pointer to a layer
* @param dsc pointer to an initialized `lv_draw_line_dsc_t` variable
*/
void lv_draw_line(lv_layer_t * layer, const lv_draw_line_dsc_t * dsc);
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_DRAW_LINE_H*/
@@ -0,0 +1,79 @@
/**
* @file lv_draw_mask.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_draw_mask_private.h"
#include "lv_draw_private.h"
#include "../core/lv_refr.h"
#include "../misc/lv_math.h"
#include "../misc/lv_types.h"
#include "../stdlib/lv_string.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
void LV_ATTRIBUTE_FAST_MEM lv_draw_mask_rect_dsc_init(lv_draw_mask_rect_dsc_t * dsc)
{
lv_memzero(dsc, sizeof(lv_draw_mask_rect_dsc_t));
}
lv_draw_mask_rect_dsc_t * lv_draw_task_get_mask_rect_dsc(lv_draw_task_t * task)
{
return task->type == LV_DRAW_TASK_TYPE_MASK_RECTANGLE ? (lv_draw_mask_rect_dsc_t *)task->draw_dsc : NULL;
}
void LV_ATTRIBUTE_FAST_MEM lv_draw_mask_rect(lv_layer_t * layer, const lv_draw_mask_rect_dsc_t * dsc)
{
if(!lv_color_format_has_alpha(layer->color_format)) {
LV_LOG_WARN("Only layers with alpha channel can be masked");
return;
}
LV_PROFILER_DRAW_BEGIN;
lv_draw_task_t * t = lv_draw_add_task(layer, &layer->buf_area, LV_DRAW_TASK_TYPE_MASK_RECTANGLE);
lv_memcpy(t->draw_dsc, dsc, sizeof(*dsc));
lv_draw_dsc_base_t * base_dsc = t->draw_dsc;
base_dsc->layer = layer;
if(base_dsc->obj && lv_obj_has_flag(base_dsc->obj, LV_OBJ_FLAG_SEND_DRAW_TASK_EVENTS)) {
/*Disable sending LV_EVENT_DRAW_TASK_ADDED first to avoid triggering recursive
*event calls due draw task adds in the event*/
lv_obj_remove_flag(base_dsc->obj, LV_OBJ_FLAG_SEND_DRAW_TASK_EVENTS);
lv_obj_send_event(dsc->base.obj, LV_EVENT_DRAW_TASK_ADDED, t);
lv_obj_add_flag(base_dsc->obj, LV_OBJ_FLAG_SEND_DRAW_TASK_EVENTS);
}
lv_draw_finalize_task_creation(layer, t);
LV_PROFILER_DRAW_END;
}
/**********************
* STATIC FUNCTIONS
**********************/
@@ -0,0 +1,56 @@
/**
* @file lv_draw_mask.h
*
*/
#ifndef LV_DRAW_MASK_H
#define LV_DRAW_MASK_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../misc/lv_color.h"
#include "../misc/lv_area.h"
#include "../misc/lv_style.h"
/*********************
* DEFINES
*********************/
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Initialize a rectangle mask draw descriptor.
* @param dsc pointer to a draw descriptor
*/
void /* LV_ATTRIBUTE_FAST_MEM */ lv_draw_mask_rect_dsc_init(lv_draw_mask_rect_dsc_t * dsc);
/**
* Try to get a rectangle mask draw descriptor from a draw task.
* @param task draw task
* @return the task's draw descriptor or NULL if the task is not of type LV_DRAW_TASK_TYPE_MASK_RECTANGLE
*/
lv_draw_mask_rect_dsc_t * lv_draw_task_get_mask_rect_dsc(lv_draw_task_t * task);
/**
* Create a draw task to mask a rectangle from the buffer
* @param layer pointer to a layer
* @param dsc pointer to a draw descriptor
*/
void lv_draw_mask_rect(lv_layer_t * layer, const lv_draw_mask_rect_dsc_t * dsc);
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_DRAW_MASK_H*/
@@ -0,0 +1,58 @@
/**
* @file lv_draw_mask_private.h
*
*/
#ifndef LV_DRAW_MASK_PRIVATE_H
#define LV_DRAW_MASK_PRIVATE_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "lv_draw_mask.h"
#include "lv_draw.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* TYPEDEFS
**********************/
struct _lv_draw_mask_rect_dsc_t {
lv_draw_dsc_base_t base;
/**The area t mask.*/
lv_area_t area;
/**The radius of masking*/
int32_t radius;
/**0: clear the content out of the `area`.
* 1: don't touch the area out of `area`*/
uint32_t keep_outside : 1;
};
/**********************
* GLOBAL PROTOTYPES
**********************/
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_DRAW_MASK_PRIVATE_H*/
@@ -0,0 +1,201 @@
/**
* @file lv_draw_private.h
*
*/
/**
* Modified by NXP in 2024
*/
#ifndef LV_DRAW_PRIVATE_H
#define LV_DRAW_PRIVATE_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "lv_draw.h"
#include "../osal/lv_os.h"
#include "../misc/cache/lv_cache.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
struct _lv_draw_task_t {
lv_draw_task_t * next;
lv_draw_task_type_t type;
/**
* The area where to draw
*/
lv_area_t area;
/**
* The real draw area. E.g. for shadow, outline, or transformed images it's different from `area`
*/
lv_area_t _real_area;
/** The original area which is updated*/
lv_area_t clip_area_original;
/**
* The clip area of the layer is saved here when the draw task is created.
* As the clip area of the layer can be changed as new draw tasks are added its current value needs to be saved.
* Therefore during drawing the layer's clip area shouldn't be used as it might be already changed for other draw tasks.
*/
lv_area_t clip_area;
lv_layer_t * target_layer;
#if LV_DRAW_TRANSFORM_USE_MATRIX
/** Transform matrix to be applied when rendering the layer */
lv_matrix_t matrix;
#endif
/* Reference to the draw unit for debug or draw context purposes */
lv_draw_unit_t * draw_unit;
volatile int state; /** int instead of lv_draw_task_state_t to be sure its atomic */
void * draw_dsc;
/**
* The ID of the draw_unit which should take this task
*/
uint8_t preferred_draw_unit_id;
/**
* Set to which extent `preferred_draw_unit_id` is good at this task.
* 80: means 20% better (faster) than software rendering
* 100: the default value
* 110: means 10% worse (slower) than software rendering
*/
uint8_t preference_score;
};
struct _lv_draw_mask_t {
void * user_data;
};
struct _lv_draw_unit_t {
lv_draw_unit_t * next;
/**
* Name and ID of the draw unit, for debugging purposes only.
*/
const char * name;
int32_t idx;
/**
* Called to try to assign a draw task to itself.
* `lv_draw_get_next_available_task` can be used to get an independent draw task.
* A draw task should be assign only if the draw unit can draw it too
* @param draw_unit pointer to the draw unit
* @param layer pointer to a layer on which the draw task should be drawn
* @return >=0: The number of taken draw task:
* 0 means the task has not yet been completed.
* 1 means a new task has been accepted.
* -1: The draw unit wanted to work on a task but couldn't do that
* due to some errors (e.g. out of memory).
* It signals that LVGL should call the dispatcher later again
* to let draw unit try to start the rendering again.
*/
int32_t (*dispatch_cb)(lv_draw_unit_t * draw_unit, lv_layer_t * layer);
/**
*
* @param draw_unit
* @param task
* @return
*/
int32_t (*evaluate_cb)(lv_draw_unit_t * draw_unit, lv_draw_task_t * task);
/**
* Called to signal the unit to complete all tasks in order to return their ready status.
* This callback can be implemented in case of asynchronous task processing.
* Below is an example to show the difference between synchronous and asynchronous:
*
* Synchronous:
* LVGL thread DRAW thread HW
*
* task1 --> submit --> Receive task1
* wait_for_finish()
* <-- task1->state = READY <-- Complete task1
* task2 --> submit --> Receive task2
* wait_for_finish()
* task2->state = READY <-- Complete task2
* task3 --> submit --> Receive task3
* wait_for_finish()
* <-- task3->state = READY <-- Complete task3
* task4 --> submit --> Receive task4
* wait_for_finish()
* <-- task4->state = READY <-- Complete task4
* NO MORE TASKS
*
*
* Asynchronous:
* LVGL thread DRAW thread HW
* is IDLE
* task1 --> queue task1
* submit --> Receive task1
* task2 --> queue task2 is BUSY (with task1)
* task3 --> queue task3 still BUSY (with task1)
* task4 --> queue task4 becomes IDLE
* <-- task1->state = READY <-- Complete task1
* submit --> Receive task2, task3, task4
* NO MORE TASKS
* wait_for_finish_cb() wait_for_finish()
* <-- Complete task2, task3, task4
* <-- task2->state = READY <--
* <-- task3->state = READY <--
* <-- task4->state = READY <--
*
* @param draw_unit
* @return
*/
int32_t (*wait_for_finish_cb)(lv_draw_unit_t * draw_unit);
/**
* Called to delete draw unit.
* @param draw_unit
* @return
*/
int32_t (*delete_cb)(lv_draw_unit_t * draw_unit);
};
typedef struct {
lv_draw_unit_t * unit_head;
uint32_t unit_cnt;
uint32_t used_memory_for_layers; /* measured as bytes */
#if LV_USE_OS
lv_thread_sync_t sync;
#else
volatile int dispatch_req;
#endif
lv_mutex_t circle_cache_mutex;
bool task_running;
} lv_draw_global_info_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_DRAW_PRIVATE_H*/
@@ -0,0 +1,337 @@
/**
* @file lv_draw_rect.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_draw_rect_private.h"
#include "lv_draw_private.h"
#include "../core/lv_obj.h"
#include "../misc/lv_assert.h"
#include "../core/lv_obj_event.h"
#include "../stdlib/lv_string.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
void LV_ATTRIBUTE_FAST_MEM lv_draw_rect_dsc_init(lv_draw_rect_dsc_t * dsc)
{
lv_memzero(dsc, sizeof(lv_draw_rect_dsc_t));
dsc->bg_color = lv_color_white();
dsc->bg_grad.stops[0].color = lv_color_white();
dsc->bg_grad.stops[1].color = lv_color_black();
dsc->bg_grad.stops[1].frac = 0xFF;
dsc->bg_grad.stops_count = 2;
dsc->border_color = lv_color_black();
dsc->shadow_color = lv_color_black();
dsc->bg_image_symbol_font = LV_FONT_DEFAULT;
dsc->bg_opa = LV_OPA_COVER;
dsc->bg_image_opa = LV_OPA_COVER;
dsc->outline_opa = LV_OPA_COVER;
dsc->border_opa = LV_OPA_COVER;
dsc->shadow_opa = LV_OPA_COVER;
dsc->border_side = LV_BORDER_SIDE_FULL;
}
void lv_draw_fill_dsc_init(lv_draw_fill_dsc_t * dsc)
{
lv_memzero(dsc, sizeof(*dsc));
dsc->opa = LV_OPA_COVER;
dsc->base.dsc_size = sizeof(lv_draw_fill_dsc_t);
}
lv_draw_fill_dsc_t * lv_draw_task_get_fill_dsc(lv_draw_task_t * task)
{
return task->type == LV_DRAW_TASK_TYPE_FILL ? (lv_draw_fill_dsc_t *)task->draw_dsc : NULL;
}
void lv_draw_fill(lv_layer_t * layer, const lv_draw_fill_dsc_t * dsc, const lv_area_t * coords)
{
if(dsc->opa <= LV_OPA_MIN) return;
LV_PROFILER_DRAW_BEGIN;
lv_draw_task_t * t = lv_draw_add_task(layer, coords, LV_DRAW_TASK_TYPE_FILL);
lv_memcpy(t->draw_dsc, dsc, sizeof(*dsc));
lv_draw_finalize_task_creation(layer, t);
LV_PROFILER_DRAW_END;
}
void lv_draw_border_dsc_init(lv_draw_border_dsc_t * dsc)
{
lv_memzero(dsc, sizeof(*dsc));
dsc->opa = LV_OPA_COVER;
dsc->side = LV_BORDER_SIDE_FULL;
dsc->base.dsc_size = sizeof(lv_draw_border_dsc_t);
}
lv_draw_border_dsc_t * lv_draw_task_get_border_dsc(lv_draw_task_t * task)
{
return task->type == LV_DRAW_TASK_TYPE_BORDER ? (lv_draw_border_dsc_t *)task->draw_dsc : NULL;
}
void lv_draw_border(lv_layer_t * layer, const lv_draw_border_dsc_t * dsc, const lv_area_t * coords)
{
if(dsc->opa <= LV_OPA_MIN) return;
LV_PROFILER_DRAW_BEGIN;
lv_draw_task_t * t = lv_draw_add_task(layer, coords, LV_DRAW_TASK_TYPE_BORDER);
lv_memcpy(t->draw_dsc, dsc, sizeof(*dsc));
lv_draw_finalize_task_creation(layer, t);
LV_PROFILER_DRAW_END;
}
void lv_draw_box_shadow_dsc_init(lv_draw_box_shadow_dsc_t * dsc)
{
lv_memzero(dsc, sizeof(*dsc));
dsc->opa = LV_OPA_COVER;
dsc->base.dsc_size = sizeof(lv_draw_box_shadow_dsc_t);
}
lv_draw_box_shadow_dsc_t * lv_draw_task_get_box_shadow_dsc(lv_draw_task_t * task)
{
return task->type == LV_DRAW_TASK_TYPE_BOX_SHADOW ? (lv_draw_box_shadow_dsc_t *)task->draw_dsc : NULL;
}
void lv_draw_box_shadow(lv_layer_t * layer, const lv_draw_box_shadow_dsc_t * dsc, const lv_area_t * coords)
{
if(dsc->opa <= LV_OPA_MIN) return;
LV_PROFILER_DRAW_BEGIN;
lv_draw_task_t * t = lv_draw_add_task(layer, coords, LV_DRAW_TASK_TYPE_BOX_SHADOW);
lv_memcpy(t->draw_dsc, dsc, sizeof(*dsc));
lv_draw_finalize_task_creation(layer, t);
LV_PROFILER_DRAW_END;
}
void lv_draw_rect(lv_layer_t * layer, const lv_draw_rect_dsc_t * dsc, const lv_area_t * coords)
{
LV_PROFILER_DRAW_BEGIN;
bool has_shadow;
bool has_fill;
bool has_border;
bool has_outline;
bool has_bg_img;
if(dsc->shadow_width == 0 ||
dsc->shadow_opa <= LV_OPA_MIN ||
(dsc->shadow_width == 1 && dsc->shadow_spread <= 0 &&
dsc->shadow_offset_x == 0 && dsc->shadow_offset_y == 0)) {
has_shadow = false;
}
else {
has_shadow = true;
}
if(dsc->bg_opa <= LV_OPA_MIN) has_fill = false;
else has_fill = true;
if(dsc->bg_image_opa <= LV_OPA_MIN || dsc->bg_image_src == NULL) has_bg_img = false;
else has_bg_img = true;
if(dsc->border_opa <= LV_OPA_MIN
|| dsc->border_width == 0
|| dsc->border_post == true
|| dsc->border_side == LV_BORDER_SIDE_NONE) has_border = false;
else has_border = true;
if(dsc->outline_opa <= LV_OPA_MIN || dsc->outline_width == 0) has_outline = false;
else has_outline = true;
bool bg_cover = true;
if(dsc->bg_opa < LV_OPA_COVER) bg_cover = false;
else if(dsc->bg_grad.dir != LV_GRAD_DIR_NONE) {
uint32_t s;
for(s = 0; s < dsc->bg_grad.stops_count; s++) {
if(dsc->bg_grad.stops[s].opa != LV_OPA_COVER) {
bg_cover = false;
break;
}
}
}
lv_draw_task_t * t;
/*Shadow*/
if(has_shadow) {
/*Check whether the shadow is visible*/
t = lv_draw_add_task(layer, coords, LV_DRAW_TASK_TYPE_BOX_SHADOW);
lv_draw_box_shadow_dsc_t * shadow_dsc = t->draw_dsc;
lv_area_increase(&t->_real_area, dsc->shadow_spread, dsc->shadow_spread);
lv_area_increase(&t->_real_area, dsc->shadow_width, dsc->shadow_width);
lv_area_move(&t->_real_area, dsc->shadow_offset_x, dsc->shadow_offset_y);
shadow_dsc->base = dsc->base;
shadow_dsc->base.dsc_size = sizeof(lv_draw_box_shadow_dsc_t);
shadow_dsc->radius = dsc->radius;
shadow_dsc->color = dsc->shadow_color;
shadow_dsc->width = dsc->shadow_width;
shadow_dsc->spread = dsc->shadow_spread;
shadow_dsc->opa = dsc->shadow_opa;
shadow_dsc->ofs_x = dsc->shadow_offset_x;
shadow_dsc->ofs_y = dsc->shadow_offset_y;
shadow_dsc->bg_cover = bg_cover;
lv_draw_finalize_task_creation(layer, t);
}
/*Background*/
if(has_fill) {
lv_area_t bg_coords = *coords;
/*If the border fully covers make the bg area 1px smaller to avoid artifacts on the corners*/
if(dsc->border_width > 1 && dsc->border_opa >= LV_OPA_MAX && dsc->radius != 0) {
bg_coords.x1 += (dsc->border_side & LV_BORDER_SIDE_LEFT) ? 1 : 0;
bg_coords.y1 += (dsc->border_side & LV_BORDER_SIDE_TOP) ? 1 : 0;
bg_coords.x2 -= (dsc->border_side & LV_BORDER_SIDE_RIGHT) ? 1 : 0;
bg_coords.y2 -= (dsc->border_side & LV_BORDER_SIDE_BOTTOM) ? 1 : 0;
}
t = lv_draw_add_task(layer, &bg_coords, LV_DRAW_TASK_TYPE_FILL);
lv_draw_fill_dsc_t * bg_dsc = t->draw_dsc;
lv_draw_fill_dsc_init(bg_dsc);
bg_dsc->base = dsc->base;
bg_dsc->base.dsc_size = sizeof(lv_draw_fill_dsc_t);
bg_dsc->radius = dsc->radius;
bg_dsc->color = dsc->bg_color;
bg_dsc->grad = dsc->bg_grad;
bg_dsc->opa = dsc->bg_opa;
lv_draw_finalize_task_creation(layer, t);
}
/*Background image*/
if(has_bg_img) {
lv_image_src_t src_type = lv_image_src_get_type(dsc->bg_image_src);
lv_result_t res = LV_RESULT_OK;
lv_image_header_t header;
if(src_type == LV_IMAGE_SRC_VARIABLE || src_type == LV_IMAGE_SRC_FILE) {
res = lv_image_decoder_get_info(dsc->bg_image_src, &header);
}
else {
lv_memzero(&header, sizeof(header));
if(src_type == LV_IMAGE_SRC_UNKNOWN) {
res = LV_RESULT_INVALID;
}
}
if(res == LV_RESULT_OK) {
if(src_type == LV_IMAGE_SRC_VARIABLE || src_type == LV_IMAGE_SRC_FILE) {
if(dsc->bg_image_tiled) {
t = lv_draw_add_task(layer, coords, LV_DRAW_TASK_TYPE_IMAGE);
}
else {
lv_area_t a = {0, 0, header.w - 1, header.h - 1};
lv_area_align(coords, &a, LV_ALIGN_CENTER, 0, 0);
t = lv_draw_add_task(layer, &a, LV_DRAW_TASK_TYPE_IMAGE);
}
lv_draw_image_dsc_t * bg_image_dsc = t->draw_dsc;
lv_draw_image_dsc_init(bg_image_dsc);
bg_image_dsc->base = dsc->base;
bg_image_dsc->base.dsc_size = sizeof(lv_draw_image_dsc_t);
bg_image_dsc->src = dsc->bg_image_src;
bg_image_dsc->opa = dsc->bg_image_opa;
bg_image_dsc->recolor = dsc->bg_image_recolor;
bg_image_dsc->recolor_opa = dsc->bg_image_recolor_opa;
bg_image_dsc->tile = dsc->bg_image_tiled;
bg_image_dsc->header = header;
bg_image_dsc->clip_radius = dsc->radius;
bg_image_dsc->image_area = *coords;
lv_draw_finalize_task_creation(layer, t);
}
else {
lv_point_t s;
lv_text_get_size(&s, dsc->bg_image_src, dsc->bg_image_symbol_font, 0, 0, LV_COORD_MAX, LV_TEXT_FLAG_NONE);
lv_area_t a = {0, 0, s.x - 1, s.y - 1};
lv_area_align(coords, &a, LV_ALIGN_CENTER, 0, 0);
t = lv_draw_add_task(layer, &a, LV_DRAW_TASK_TYPE_LABEL);
lv_draw_label_dsc_t * bg_label_dsc = t->draw_dsc;
lv_draw_label_dsc_init(bg_label_dsc);
bg_label_dsc->base = dsc->base;
bg_label_dsc->base.dsc_size = sizeof(lv_draw_label_dsc_t);
bg_label_dsc->color = dsc->bg_image_recolor;
bg_label_dsc->font = dsc->bg_image_symbol_font;
bg_label_dsc->text = dsc->bg_image_src;
t->type = LV_DRAW_TASK_TYPE_LABEL;
lv_draw_finalize_task_creation(layer, t);
}
}
}
/*Border*/
if(has_border) {
t = lv_draw_add_task(layer, coords, LV_DRAW_TASK_TYPE_BORDER);
lv_draw_border_dsc_t * border_dsc = t->draw_dsc;
border_dsc->base = dsc->base;
border_dsc->base.dsc_size = sizeof(lv_draw_border_dsc_t);
border_dsc->radius = dsc->radius;
border_dsc->color = dsc->border_color;
border_dsc->opa = dsc->border_opa;
border_dsc->width = dsc->border_width;
border_dsc->side = dsc->border_side;
lv_draw_finalize_task_creation(layer, t);
}
/*Outline*/
if(has_outline) {
lv_area_t outline_coords = *coords;
lv_area_increase(&outline_coords, dsc->outline_width + dsc->outline_pad, dsc->outline_width + dsc->outline_pad);
t = lv_draw_add_task(layer, &outline_coords, LV_DRAW_TASK_TYPE_BORDER);
lv_draw_border_dsc_t * outline_dsc = t->draw_dsc;
lv_area_increase(&t->_real_area, dsc->outline_width, dsc->outline_width);
lv_area_increase(&t->_real_area, dsc->outline_pad, dsc->outline_pad);
outline_dsc->base = dsc->base;
outline_dsc->base.dsc_size = sizeof(lv_draw_border_dsc_t);
outline_dsc->radius = dsc->radius == LV_RADIUS_CIRCLE ? LV_RADIUS_CIRCLE : dsc->radius + dsc->outline_width +
dsc->outline_pad;
outline_dsc->color = dsc->outline_color;
outline_dsc->opa = dsc->outline_opa;
outline_dsc->width = dsc->outline_width;
outline_dsc->side = LV_BORDER_SIDE_FULL;
lv_draw_finalize_task_creation(layer, t);
}
LV_ASSERT_MEM_INTEGRITY();
LV_PROFILER_DRAW_END;
}
/**********************
* STATIC FUNCTIONS
**********************/
@@ -0,0 +1,233 @@
/**
* @file lv_draw_rect.h
*
*/
#ifndef LV_DRAW_RECT_H
#define LV_DRAW_RECT_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "lv_draw.h"
#include "../misc/lv_color.h"
#include "../misc/lv_area.h"
#include "../misc/lv_style.h"
/*********************
* DEFINES
*********************/
#define LV_RADIUS_CIRCLE 0x7FFF /**< A very big radius to always draw as circle*/
LV_EXPORT_CONST_INT(LV_RADIUS_CIRCLE);
/**********************
* TYPEDEFS
**********************/
typedef struct {
lv_draw_dsc_base_t base;
int32_t radius;
/*Background*/
lv_opa_t bg_opa;
lv_color_t bg_color; /**< First element of a gradient is a color, so it maps well here*/
lv_grad_dsc_t bg_grad;
/*Background img*/
const void * bg_image_src;
const void * bg_image_symbol_font;
lv_color_t bg_image_recolor;
lv_opa_t bg_image_opa;
lv_opa_t bg_image_recolor_opa;
uint8_t bg_image_tiled;
/*Border*/
lv_color_t border_color;
int32_t border_width;
lv_opa_t border_opa;
lv_border_side_t border_side : 5;
uint8_t border_post : 1; /*The border will be drawn later*/
/*Outline*/
lv_color_t outline_color;
int32_t outline_width;
int32_t outline_pad;
lv_opa_t outline_opa;
/*Shadow*/
lv_color_t shadow_color;
int32_t shadow_width;
int32_t shadow_offset_x;
int32_t shadow_offset_y;
int32_t shadow_spread;
lv_opa_t shadow_opa;
} lv_draw_rect_dsc_t;
typedef struct {
lv_draw_dsc_base_t base;
/**Radius, LV_RADIUS_CIRCLE for max. radius */
int32_t radius;
/**Opacity in 0...255 range.
* LV_OPA_TRANSP, LV_OPA_10, LV_OPA_20, .. LV_OPA_COVER can be used as well*/
lv_opa_t opa;
/**The color of the rectangle.
* If the gradient is set (grad.dir!=LV_GRAD_DIR_NONE) it's ignored. */
lv_color_t color;
/**Describe a gradient. If `grad.dir` is not `LV_GRAD_DIR_NONE` `color` will be ignored*/
lv_grad_dsc_t grad;
} lv_draw_fill_dsc_t;
typedef struct {
lv_draw_dsc_base_t base;
/**Radius, LV_RADIUS_CIRCLE for max. radius */
int32_t radius;
/**The color of the border. */
lv_color_t color;
/**The width of the border in pixels */
int32_t width;
/**Opacity in 0...255 range.
* LV_OPA_TRANSP, LV_OPA_10, LV_OPA_20, .. LV_OPA_COVER can be used as well*/
lv_opa_t opa;
/**LV_BORDER_SIDE_NONE/LEFT/RIGHT/TOP/BOTTOM/FULL.
* LV_BORDER_SIDE_INTERNAL is an information for upper layers
* and shouldn't be used here. */
lv_border_side_t side : 5;
} lv_draw_border_dsc_t;
typedef struct {
lv_draw_dsc_base_t base;
/**Radius, LV_RADIUS_CIRCLE for max. radius */
int32_t radius;
/**Color of the the shadow */
lv_color_t color;
/**Width of the shadow. (radius of the blur)*/
int32_t width;
/**Make the rectangle larger with this value in all directions. Can be negative too. */
int32_t spread;
/**Offset the rectangle horizontally.*/
int32_t ofs_x;
/**Offset the rectangle vertically.*/
int32_t ofs_y;
/**Opacity in 0...255 range.
* LV_OPA_TRANSP, LV_OPA_10, LV_OPA_20, .. LV_OPA_COVER can be used as well*/
lv_opa_t opa;
/**Set `bg_cover` to 1 if the background will cover the shadow.
* It's a hint to the renderer about it might skip some masking.*/
uint8_t bg_cover : 1;
} lv_draw_box_shadow_dsc_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Initialize a rectangle draw descriptor.
* @param dsc pointer to a draw descriptor
*/
void /* LV_ATTRIBUTE_FAST_MEM */ lv_draw_rect_dsc_init(lv_draw_rect_dsc_t * dsc);
/**
* Initialize a fill draw descriptor.
* @param dsc pointer to a draw descriptor
*/
void lv_draw_fill_dsc_init(lv_draw_fill_dsc_t * dsc);
/**
* Try to get a fill draw descriptor from a draw task.
* @param task draw task
* @return the task's draw descriptor or NULL if the task is not of type LV_DRAW_TASK_TYPE_FILL
*/
lv_draw_fill_dsc_t * lv_draw_task_get_fill_dsc(lv_draw_task_t * task);
/**
* Fill an area
* @param layer pointer to a layer
* @param dsc pointer to an initialized draw descriptor variable
* @param coords the coordinates of the rectangle
*/
void lv_draw_fill(lv_layer_t * layer, const lv_draw_fill_dsc_t * dsc, const lv_area_t * coords);
/**
* Initialize a border draw descriptor.
* @param dsc pointer to a draw descriptor
*/
void lv_draw_border_dsc_init(lv_draw_border_dsc_t * dsc);
/**
* Try to get a border draw descriptor from a draw task.
* @param task draw task
* @return the task's draw descriptor or NULL if the task is not of type LV_DRAW_TASK_TYPE_BORDER
*/
lv_draw_border_dsc_t * lv_draw_task_get_border_dsc(lv_draw_task_t * task);
/**
* Draw a border
* @param layer pointer to a layer
* @param dsc pointer to an initialized draw descriptor variable
* @param coords the coordinates of the rectangle
*/
void lv_draw_border(lv_layer_t * layer, const lv_draw_border_dsc_t * dsc, const lv_area_t * coords);
/**
* Initialize a box shadow draw descriptor.
* @param dsc pointer to a draw descriptor
*/
void lv_draw_box_shadow_dsc_init(lv_draw_box_shadow_dsc_t * dsc);
/**
* Try to get a box shadow draw descriptor from a draw task.
* @param task draw task
* @return the task's draw descriptor or NULL if the task is not of type LV_DRAW_TASK_TYPE_BOX_SHADOW
*/
lv_draw_box_shadow_dsc_t * lv_draw_task_get_box_shadow_dsc(lv_draw_task_t * task);
/**
* Draw a box shadow
* @param layer pointer to a layer
* @param dsc pointer to an initialized draw descriptor variable
* @param coords the coordinates of the rectangle
*/
void lv_draw_box_shadow(lv_layer_t * layer, const lv_draw_box_shadow_dsc_t * dsc, const lv_area_t * coords);
/**
* The rectangle is a wrapper for fill, border, bg. image and box shadow.
* Internally fill, border, image and box shadow draw tasks will be created.
* @param layer pointer to a layer
* @param dsc pointer to an initialized draw descriptor variable
* @param coords the coordinates of the rectangle
*/
void lv_draw_rect(lv_layer_t * layer, const lv_draw_rect_dsc_t * dsc, const lv_area_t * coords);
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_DRAW_RECT_H*/
@@ -0,0 +1,39 @@
/**
* @file lv_draw_rect_private.h
*
*/
#ifndef LV_DRAW_RECT_PRIVATE_H
#define LV_DRAW_RECT_PRIVATE_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "lv_draw_rect.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* GLOBAL PROTOTYPES
**********************/
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_DRAW_RECT_PRIVATE_H*/
@@ -0,0 +1,82 @@
/**
* @file lv_draw_triangle.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_draw_triangle_private.h"
#include "lv_draw_private.h"
#include "../core/lv_obj.h"
#include "../misc/lv_math.h"
#include "../stdlib/lv_mem.h"
#include "../stdlib/lv_string.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
void lv_draw_triangle_dsc_init(lv_draw_triangle_dsc_t * dsc)
{
LV_PROFILER_DRAW_BEGIN;
lv_memzero(dsc, sizeof(lv_draw_triangle_dsc_t));
dsc->color = lv_color_white();
dsc->grad.stops[0].color = lv_color_white();
dsc->grad.stops[1].color = lv_color_black();
dsc->grad.stops[1].frac = 0xFF;
dsc->grad.stops_count = 2;
dsc->opa = LV_OPA_COVER;
dsc->base.dsc_size = sizeof(lv_draw_triangle_dsc_t);
LV_PROFILER_DRAW_END;
}
lv_draw_triangle_dsc_t * lv_draw_task_get_triangle_dsc(lv_draw_task_t * task)
{
return task->type == LV_DRAW_TASK_TYPE_TRIANGLE ? (lv_draw_triangle_dsc_t *)task->draw_dsc : NULL;
}
void lv_draw_triangle(lv_layer_t * layer, const lv_draw_triangle_dsc_t * dsc)
{
if(dsc->opa <= LV_OPA_MIN) return;
LV_PROFILER_DRAW_BEGIN;
lv_area_t a;
a.x1 = (int32_t)LV_MIN3(dsc->p[0].x, dsc->p[1].x, dsc->p[2].x);
a.y1 = (int32_t)LV_MIN3(dsc->p[0].y, dsc->p[1].y, dsc->p[2].y);
a.x2 = (int32_t)LV_MAX3(dsc->p[0].x, dsc->p[1].x, dsc->p[2].x);
a.y2 = (int32_t)LV_MAX3(dsc->p[0].y, dsc->p[1].y, dsc->p[2].y);
lv_draw_task_t * t = lv_draw_add_task(layer, &a, LV_DRAW_TASK_TYPE_TRIANGLE);
lv_memcpy(t->draw_dsc, dsc, sizeof(*dsc));
lv_draw_finalize_task_creation(layer, t);
LV_PROFILER_DRAW_END;
}
/**********************
* STATIC FUNCTIONS
**********************/
@@ -0,0 +1,75 @@
/**
* @file lv_draw_triangle.h
*
*/
#ifndef LV_DRAW_TRIANGLE_H
#define LV_DRAW_TRIANGLE_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "lv_draw_rect.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
typedef struct {
lv_draw_dsc_base_t base;
/**Points of the triangle. If `LV_USE_FLOAT` is enabled floats can be used here*/
lv_point_precise_t p[3];
/**Color of the triangle*/
lv_color_t color;
/**Opacity of the arc in 0...255 range.
* LV_OPA_TRANSP, LV_OPA_10, LV_OPA_20, .. LV_OPA_COVER can be used as well*/
lv_opa_t opa;
/**Describe a gradient. If `grad.dir` is not `LV_GRAD_DIR_NONE` `color` will be ignored*/
lv_grad_dsc_t grad;
} lv_draw_triangle_dsc_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Initialize a triangle draw descriptor
* @param draw_dsc pointer to a draw descriptor
*/
void lv_draw_triangle_dsc_init(lv_draw_triangle_dsc_t * draw_dsc);
/**
* Try to get a triangle draw descriptor from a draw task.
* @param task draw task
* @return the task's draw descriptor or NULL if the task is not of type LV_DRAW_TASK_TYPE_TRIANGLE
*/
lv_draw_triangle_dsc_t * lv_draw_task_get_triangle_dsc(lv_draw_task_t * task);
/**
* Create a triangle draw task
* @param layer pointer to a layer
* @param draw_dsc pointer to an initialized `lv_draw_triangle_dsc_t` object
*/
void lv_draw_triangle(lv_layer_t * layer, const lv_draw_triangle_dsc_t * draw_dsc);
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_DRAW_TRIANGLE_H*/
@@ -0,0 +1,43 @@
/**
* @file lv_draw_triangle_private.h
*
*/
#ifndef LV_DRAW_TRIANGLE_PRIVATE_H
#define LV_DRAW_TRIANGLE_PRIVATE_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "lv_draw_triangle.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* GLOBAL PROTOTYPES
**********************/
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_DRAW_TRIANGLE_PRIVATE_H*/
@@ -0,0 +1,819 @@
/**
* @file lv_draw_vector.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_draw_vector_private.h"
#include "../misc/lv_area_private.h"
#include "lv_draw_private.h"
#if LV_USE_VECTOR_GRAPHIC
#include "../misc/lv_ll.h"
#include "../misc/lv_types.h"
#include "../stdlib/lv_string.h"
#include <math.h>
#include <float.h>
#define MATH_PI 3.14159265358979323846f
#define MATH_HALF_PI 1.57079632679489661923f
#define DEG_TO_RAD 0.017453292519943295769236907684886f
#define RAD_TO_DEG 57.295779513082320876798154814105f
#define MATH_RADIANS(deg) ((deg) * DEG_TO_RAD)
#define MATH_DEGREES(rad) ((rad) * RAD_TO_DEG)
/*********************
* DEFINES
*********************/
#ifndef M_PI
#define M_PI 3.1415926f
#endif
#define CHECK_AND_RESIZE_PATH_CONTAINER(P, N) \
do { \
if ((lv_array_size(&(P)->ops) + (N)) > lv_array_capacity(&(P)->ops)) { \
lv_array_resize(&(P)->ops, ((P)->ops.capacity << 1)); \
} \
if ((lv_array_size(&(P)->points) + (N)) > lv_array_capacity(&(P)->points)) { \
lv_array_resize(&(P)->points, ((P)->points.capacity << 1)); \
} \
} while(0)
/**********************
* TYPEDEFS
**********************/
typedef struct {
lv_vector_path_t * path;
lv_vector_draw_dsc_t dsc;
} lv_vector_draw_task;
/**********************
* STATIC PROTOTYPES
**********************/
static void _copy_draw_dsc(lv_vector_draw_dsc_t * dst, const lv_vector_draw_dsc_t * src)
{
lv_memcpy(&(dst->fill_dsc), &(src->fill_dsc), sizeof(lv_vector_fill_dsc_t));
dst->stroke_dsc.style = src->stroke_dsc.style;
dst->stroke_dsc.color = src->stroke_dsc.color;
dst->stroke_dsc.opa = src->stroke_dsc.opa;
dst->stroke_dsc.width = src->stroke_dsc.width;
dst->stroke_dsc.cap = src->stroke_dsc.cap;
dst->stroke_dsc.join = src->stroke_dsc.join;
dst->stroke_dsc.miter_limit = src->stroke_dsc.miter_limit;
lv_array_copy(&(dst->stroke_dsc.dash_pattern), &(src->stroke_dsc.dash_pattern));
dst->stroke_dsc.gradient.style = src->stroke_dsc.gradient.style;
dst->stroke_dsc.gradient.cx = src->stroke_dsc.gradient.cx;
dst->stroke_dsc.gradient.cy = src->stroke_dsc.gradient.cy;
dst->stroke_dsc.gradient.cr = src->stroke_dsc.gradient.cr;
dst->stroke_dsc.gradient.spread = src->fill_dsc.gradient.spread;
lv_memcpy(&(dst->stroke_dsc.gradient), &(src->stroke_dsc.gradient), sizeof(lv_vector_gradient_t));
lv_memcpy(&(dst->stroke_dsc.matrix), &(src->stroke_dsc.matrix), sizeof(lv_matrix_t));
dst->blend_mode = src->blend_mode;
lv_memcpy(&(dst->matrix), &(src->matrix), sizeof(lv_matrix_t));
lv_area_copy(&(dst->scissor_area), &(src->scissor_area));
}
/**********************
* GLOBAL FUNCTIONS
**********************/
void lv_matrix_transform_point(const lv_matrix_t * matrix, lv_fpoint_t * point)
{
float x = point->x;
float y = point->y;
point->x = x * matrix->m[0][0] + y * matrix->m[1][0] + matrix->m[0][2];
point->y = x * matrix->m[0][1] + y * matrix->m[1][1] + matrix->m[1][2];
}
void lv_matrix_transform_path(const lv_matrix_t * matrix, lv_vector_path_t * path)
{
lv_fpoint_t * pt = lv_array_front(&path->points);
uint32_t size = lv_array_size(&path->points);
for(uint32_t i = 0; i < size; i++) {
lv_matrix_transform_point(matrix, &pt[i]);
}
}
/* path functions */
lv_vector_path_t * lv_vector_path_create(lv_vector_path_quality_t quality)
{
lv_vector_path_t * path = lv_malloc(sizeof(lv_vector_path_t));
LV_ASSERT_MALLOC(path);
lv_memzero(path, sizeof(lv_vector_path_t));
path->quality = quality;
lv_array_init(&path->ops, 8, sizeof(lv_vector_path_op_t));
lv_array_init(&path->points, 8, sizeof(lv_fpoint_t));
return path;
}
void lv_vector_path_copy(lv_vector_path_t * target_path, const lv_vector_path_t * path)
{
target_path->quality = path->quality;
lv_array_copy(&target_path->ops, &path->ops);
lv_array_copy(&target_path->points, &path->points);
}
void lv_vector_path_clear(lv_vector_path_t * path)
{
lv_array_clear(&path->ops);
lv_array_clear(&path->points);
}
void lv_vector_path_delete(lv_vector_path_t * path)
{
lv_array_deinit(&path->ops);
lv_array_deinit(&path->points);
lv_free(path);
}
void lv_vector_path_move_to(lv_vector_path_t * path, const lv_fpoint_t * p)
{
CHECK_AND_RESIZE_PATH_CONTAINER(path, 1);
lv_vector_path_op_t op = LV_VECTOR_PATH_OP_MOVE_TO;
lv_array_push_back(&path->ops, &op);
lv_array_push_back(&path->points, p);
}
void lv_vector_path_line_to(lv_vector_path_t * path, const lv_fpoint_t * p)
{
if(lv_array_is_empty(&path->ops)) {
/*first op must be move_to*/
return;
}
CHECK_AND_RESIZE_PATH_CONTAINER(path, 1);
lv_vector_path_op_t op = LV_VECTOR_PATH_OP_LINE_TO;
lv_array_push_back(&path->ops, &op);
lv_array_push_back(&path->points, p);
}
void lv_vector_path_quad_to(lv_vector_path_t * path, const lv_fpoint_t * p1, const lv_fpoint_t * p2)
{
if(lv_array_is_empty(&path->ops)) {
/*first op must be move_to*/
return;
}
CHECK_AND_RESIZE_PATH_CONTAINER(path, 2);
lv_vector_path_op_t op = LV_VECTOR_PATH_OP_QUAD_TO;
lv_array_push_back(&path->ops, &op);
lv_array_push_back(&path->points, p1);
lv_array_push_back(&path->points, p2);
}
void lv_vector_path_cubic_to(lv_vector_path_t * path, const lv_fpoint_t * p1, const lv_fpoint_t * p2,
const lv_fpoint_t * p3)
{
if(lv_array_is_empty(&path->ops)) {
/*first op must be move_to*/
return;
}
CHECK_AND_RESIZE_PATH_CONTAINER(path, 3);
lv_vector_path_op_t op = LV_VECTOR_PATH_OP_CUBIC_TO;
lv_array_push_back(&path->ops, &op);
lv_array_push_back(&path->points, p1);
lv_array_push_back(&path->points, p2);
lv_array_push_back(&path->points, p3);
}
void lv_vector_path_close(lv_vector_path_t * path)
{
if(lv_array_is_empty(&path->ops)) {
/*first op must be move_to*/
return;
}
CHECK_AND_RESIZE_PATH_CONTAINER(path, 1);
lv_vector_path_op_t op = LV_VECTOR_PATH_OP_CLOSE;
lv_array_push_back(&path->ops, &op);
}
void lv_vector_path_get_bounding(const lv_vector_path_t * path, lv_area_t * area)
{
LV_ASSERT_NULL(path);
LV_ASSERT_NULL(area);
uint32_t len = lv_array_size(&path->points);
if(len == 0) {
lv_memzero(area, sizeof(lv_area_t));
return;
}
lv_fpoint_t * p = lv_array_front(&path->points);
float x1 = p[0].x;
float x2 = p[0].x;
float y1 = p[0].y;
float y2 = p[0].y;
for(uint32_t i = 1; i < len; i++) {
if(p[i].x < x1) x1 = p[i].x;
if(p[i].y < y1) y1 = p[i].y;
if(p[i].x > x2) x2 = p[i].x;
if(p[i].y > y2) y2 = p[i].y;
}
area->x1 = lroundf(x1);
area->y1 = lroundf(y1);
area->x2 = lroundf(x2);
area->y2 = lroundf(y2);
}
void lv_vector_path_append_rect(lv_vector_path_t * path, const lv_area_t * rect, float rx, float ry)
{
float x = rect->x1;
float y = rect->y1;
float w = (float)lv_area_get_width(rect);
float h = (float)lv_area_get_height(rect);
float hw = w * 0.5f;
float hh = h * 0.5f;
if(rx > hw) rx = hw;
if(ry > hh) ry = hh;
if(rx == 0 && ry == 0) {
lv_fpoint_t pt = {x, y};
lv_vector_path_move_to(path, &pt);
pt.x += w;
lv_vector_path_line_to(path, &pt);
pt.y += h;
lv_vector_path_line_to(path, &pt);
pt.x -= w;
lv_vector_path_line_to(path, &pt);
lv_vector_path_close(path);
}
else if(rx == hw && ry == hh) {
lv_fpoint_t pt = {x + w * 0.5f, y + h * 0.5f};
lv_vector_path_append_circle(path, &pt, rx, ry);
}
else {
float hrx = rx * 0.5f;
float hry = ry * 0.5f;
lv_fpoint_t pt, pt2, pt3;
pt.x = x + rx;
pt.y = y;
lv_vector_path_move_to(path, &pt);
pt.x = x + w - rx;
pt.y = y;
lv_vector_path_line_to(path, &pt);
pt.x = x + w - rx + hrx;
pt.y = y;
pt2.x = x + w;
pt2.y = y + ry - hry;
pt3.x = x + w;
pt3.y = y + ry;
lv_vector_path_cubic_to(path, &pt, &pt2, &pt3);
pt.x = x + w;
pt.y = y + h - ry;
lv_vector_path_line_to(path, &pt);
pt.x = x + w;
pt.y = y + h - ry + hry;
pt2.x = x + w - rx + hrx;
pt2.y = y + h;
pt3.x = x + w - rx;
pt3.y = y + h;
lv_vector_path_cubic_to(path, &pt, &pt2, &pt3);
pt.x = x + rx;
pt.y = y + h;
lv_vector_path_line_to(path, &pt);
pt.x = x + rx - hrx;
pt.y = y + h;
pt2.x = x;
pt2.y = y + h - ry + hry;
pt3.x = x;
pt3.y = y + h - ry;
lv_vector_path_cubic_to(path, &pt, &pt2, &pt3);
pt.x = x;
pt.y = y + ry;
lv_vector_path_line_to(path, &pt);
pt.x = x;
pt.y = y + ry - hry;
pt2.x = x + rx - hrx;
pt2.y = y;
pt3.x = x + rx;
pt3.y = y;
lv_vector_path_cubic_to(path, &pt, &pt2, &pt3);
lv_vector_path_close(path);
}
}
void lv_vector_path_append_circle(lv_vector_path_t * path, const lv_fpoint_t * c, float rx, float ry)
{
float krx = rx * 0.552284f;
float kry = ry * 0.552284f;
float cx = c->x;
float cy = c->y;
lv_fpoint_t pt, pt2, pt3;
pt.x = cx;
pt.y = cy - ry;
lv_vector_path_move_to(path, &pt);
pt.x = cx + krx;
pt.y = cy - ry;
pt2.x = cx + rx;
pt2.y = cy - kry;
pt3.x = cx + rx;
pt3.y = cy;
lv_vector_path_cubic_to(path, &pt, &pt2, &pt3);
pt.x = cx + rx;
pt.y = cy + kry;
pt2.x = cx + krx;
pt2.y = cy + ry;
pt3.x = cx;
pt3.y = cy + ry;
lv_vector_path_cubic_to(path, &pt, &pt2, &pt3);
pt.x = cx - krx;
pt.y = cy + ry;
pt2.x = cx - rx;
pt2.y = cy + kry;
pt3.x = cx - rx;
pt3.y = cy;
lv_vector_path_cubic_to(path, &pt, &pt2, &pt3);
pt.x = cx - rx;
pt.y = cy - kry;
pt2.x = cx - krx;
pt2.y = cy - ry;
pt3.x = cx;
pt3.y = cy - ry;
lv_vector_path_cubic_to(path, &pt, &pt2, &pt3);
lv_vector_path_close(path);
}
/**
* Add a arc to the path
* @param path pointer to a path
* @param c pointer to a `lv_fpoint_t` variable for center of the circle
* @param radius the radius for arc
* @param start_angle the start angle for arc
* @param sweep the sweep angle for arc, could be negative
* @param pie true: draw a pie, false: draw a arc
*/
void lv_vector_path_append_arc(lv_vector_path_t * path, const lv_fpoint_t * c, float radius, float start_angle,
float sweep, bool pie)
{
float cx = c->x;
float cy = c->y;
/* just circle */
if(sweep >= 360.0f || sweep <= -360.0f) {
lv_vector_path_append_circle(path, c, radius, radius);
return;
}
start_angle = MATH_RADIANS(start_angle);
sweep = MATH_RADIANS(sweep);
int n_curves = (int)ceil(fabsf(sweep / MATH_HALF_PI));
float sweep_sign = sweep < 0 ? -1.f : 1.f;
float fract = fmodf(sweep, MATH_HALF_PI);
fract = (fabsf(fract) < FLT_EPSILON) ? MATH_HALF_PI * sweep_sign : fract;
/* Start from here */
lv_fpoint_t start = {
.x = radius * cosf(start_angle),
.y = radius * sinf(start_angle),
};
if(pie) {
lv_vector_path_move_to(path, &(lv_fpoint_t) {
cx, cy
});
lv_vector_path_line_to(path, &(lv_fpoint_t) {
start.x + cx, start.y + cy
});
}
else {
lv_vector_path_move_to(path, &(lv_fpoint_t) {
start.x + cx, start.y + cy
});
}
for(int i = 0; i < n_curves; ++i) {
float end_angle = start_angle + ((i != n_curves - 1) ? MATH_HALF_PI * sweep_sign : fract);
float end_x = radius * cosf(end_angle);
float end_y = radius * sinf(end_angle);
/* variables needed to calculate bezier control points */
/** get bezier control points using article:
* (http://itc.ktu.lt/index.php/ITC/article/view/11812/6479)
*/
float ax = start.x;
float ay = start.y;
float bx = end_x;
float by = end_y;
float q1 = ax * ax + ay * ay;
float q2 = ax * bx + ay * by + q1;
float k2 = (4.0f / 3.0f) * ((sqrtf(2 * q1 * q2) - q2) / (ax * by - ay * bx));
/* Next start point is the current end point */
start.x = end_x;
start.y = end_y;
end_x += cx;
end_y += cy;
lv_fpoint_t ctrl1 = {ax - k2 * ay + cx, ay + k2 * ax + cy};
lv_fpoint_t ctrl2 = {bx + k2 * by + cx, by - k2 * bx + cy};
lv_fpoint_t end = {end_x, end_y};
lv_vector_path_cubic_to(path, &ctrl1, &ctrl2, &end);
start_angle = end_angle;
}
if(pie) {
lv_vector_path_close(path);
}
}
void lv_vector_path_append_path(lv_vector_path_t * path, const lv_vector_path_t * subpath)
{
uint32_t ops_size = lv_array_size(&path->ops);
uint32_t nops_size = lv_array_size(&subpath->ops);
uint32_t point_size = lv_array_size(&path->points);
uint32_t npoint_size = lv_array_size(&subpath->points);
lv_array_concat(&path->ops, &subpath->ops);
path->ops.size = ops_size + nops_size;
lv_array_concat(&path->points, &subpath->points);
path->points.size = point_size + npoint_size;
}
/* draw dsc functions */
lv_vector_dsc_t * lv_vector_dsc_create(lv_layer_t * layer)
{
lv_vector_dsc_t * dsc = lv_malloc(sizeof(lv_vector_dsc_t));
LV_ASSERT_MALLOC(dsc);
lv_memzero(dsc, sizeof(lv_vector_dsc_t));
dsc->layer = layer;
lv_vector_fill_dsc_t * fill_dsc = &(dsc->current_dsc.fill_dsc);
fill_dsc->style = LV_VECTOR_DRAW_STYLE_SOLID;
fill_dsc->color = lv_color_to_32(lv_color_black(), 0xFF);
fill_dsc->opa = LV_OPA_COVER;
fill_dsc->fill_rule = LV_VECTOR_FILL_NONZERO;
lv_matrix_identity(&(fill_dsc->matrix)); /*identity matrix*/
lv_vector_stroke_dsc_t * stroke_dsc = &(dsc->current_dsc.stroke_dsc);
stroke_dsc->style = LV_VECTOR_DRAW_STYLE_SOLID;
stroke_dsc->color = lv_color_to_32(lv_color_black(), 0xFF);
stroke_dsc->opa = LV_OPA_0; /*default no stroke*/
stroke_dsc->width = 1.0f;
stroke_dsc->cap = LV_VECTOR_STROKE_CAP_BUTT;
stroke_dsc->join = LV_VECTOR_STROKE_JOIN_MITER;
stroke_dsc->miter_limit = 4.0f;
lv_matrix_identity(&(stroke_dsc->matrix)); /*identity matrix*/
dsc->current_dsc.blend_mode = LV_VECTOR_BLEND_SRC_OVER;
dsc->current_dsc.scissor_area = layer->_clip_area;
lv_matrix_identity(&(dsc->current_dsc.matrix)); /*identity matrix*/
dsc->tasks.task_list = NULL;
return dsc;
}
void lv_vector_dsc_delete(lv_vector_dsc_t * dsc)
{
if(dsc->tasks.task_list) {
lv_ll_t * task_list = dsc->tasks.task_list;
lv_vector_for_each_destroy_tasks(task_list, NULL, NULL);
dsc->tasks.task_list = NULL;
}
lv_array_deinit(&(dsc->current_dsc.stroke_dsc.dash_pattern));
lv_free(dsc);
}
void lv_vector_dsc_set_blend_mode(lv_vector_dsc_t * dsc, lv_vector_blend_t blend)
{
dsc->current_dsc.blend_mode = blend;
}
void lv_vector_dsc_set_transform(lv_vector_dsc_t * dsc, const lv_matrix_t * matrix)
{
lv_memcpy(&(dsc->current_dsc.matrix), matrix, sizeof(lv_matrix_t));
}
void lv_vector_dsc_set_fill_color(lv_vector_dsc_t * dsc, lv_color_t color)
{
dsc->current_dsc.fill_dsc.style = LV_VECTOR_DRAW_STYLE_SOLID;
dsc->current_dsc.fill_dsc.color = lv_color_to_32(color, 0xFF);
}
void lv_vector_dsc_set_fill_color32(lv_vector_dsc_t * dsc, lv_color32_t color)
{
dsc->current_dsc.fill_dsc.style = LV_VECTOR_DRAW_STYLE_SOLID;
dsc->current_dsc.fill_dsc.color = color;
}
void lv_vector_dsc_set_fill_opa(lv_vector_dsc_t * dsc, lv_opa_t opa)
{
dsc->current_dsc.fill_dsc.opa = opa;
}
void lv_vector_dsc_set_fill_rule(lv_vector_dsc_t * dsc, lv_vector_fill_t rule)
{
dsc->current_dsc.fill_dsc.fill_rule = rule;
}
void lv_vector_dsc_set_fill_units(lv_vector_dsc_t * dsc, const lv_vector_fill_units_t units)
{
dsc->current_dsc.fill_dsc.fill_units = units;
}
void lv_vector_dsc_set_fill_image(lv_vector_dsc_t * dsc, const lv_draw_image_dsc_t * img_dsc)
{
dsc->current_dsc.fill_dsc.style = LV_VECTOR_DRAW_STYLE_PATTERN;
lv_memcpy(&(dsc->current_dsc.fill_dsc.img_dsc), img_dsc, sizeof(lv_draw_image_dsc_t));
}
void lv_vector_dsc_set_fill_linear_gradient(lv_vector_dsc_t * dsc, float x1, float y1, float x2, float y2)
{
dsc->current_dsc.fill_dsc.style = LV_VECTOR_DRAW_STYLE_GRADIENT;
dsc->current_dsc.fill_dsc.gradient.style = LV_VECTOR_GRADIENT_STYLE_LINEAR;
dsc->current_dsc.fill_dsc.gradient.x1 = x1;
dsc->current_dsc.fill_dsc.gradient.y1 = y1;
dsc->current_dsc.fill_dsc.gradient.x2 = x2;
dsc->current_dsc.fill_dsc.gradient.y2 = y2;
}
void lv_vector_dsc_set_fill_radial_gradient(lv_vector_dsc_t * dsc, float cx, float cy, float radius)
{
dsc->current_dsc.fill_dsc.style = LV_VECTOR_DRAW_STYLE_GRADIENT;
dsc->current_dsc.fill_dsc.gradient.style = LV_VECTOR_GRADIENT_STYLE_RADIAL;
dsc->current_dsc.fill_dsc.gradient.cx = cx;
dsc->current_dsc.fill_dsc.gradient.cy = cy;
dsc->current_dsc.fill_dsc.gradient.cr = radius;
}
void lv_vector_dsc_set_fill_gradient_spread(lv_vector_dsc_t * dsc, lv_vector_gradient_spread_t spread)
{
dsc->current_dsc.fill_dsc.gradient.spread = spread;
}
void lv_vector_dsc_set_fill_gradient_color_stops(lv_vector_dsc_t * dsc, const lv_grad_stop_t * stops,
uint16_t count)
{
if(count > LV_GRADIENT_MAX_STOPS) {
LV_LOG_WARN("Gradient stops limited: %d, max: %d", count, LV_GRADIENT_MAX_STOPS);
count = LV_GRADIENT_MAX_STOPS;
}
lv_memcpy(&(dsc->current_dsc.fill_dsc.gradient.stops), stops, sizeof(lv_grad_stop_t) * count);
dsc->current_dsc.fill_dsc.gradient.stops_count = count;
}
void lv_vector_dsc_set_fill_transform(lv_vector_dsc_t * dsc, const lv_matrix_t * matrix)
{
lv_memcpy(&(dsc->current_dsc.fill_dsc.matrix), matrix, sizeof(lv_matrix_t));
}
void lv_vector_dsc_set_stroke_transform(lv_vector_dsc_t * dsc, const lv_matrix_t * matrix)
{
lv_memcpy(&(dsc->current_dsc.stroke_dsc.matrix), matrix, sizeof(lv_matrix_t));
}
void lv_vector_dsc_set_stroke_color32(lv_vector_dsc_t * dsc, lv_color32_t color)
{
dsc->current_dsc.stroke_dsc.style = LV_VECTOR_DRAW_STYLE_SOLID;
dsc->current_dsc.stroke_dsc.color = color;
}
void lv_vector_dsc_set_stroke_color(lv_vector_dsc_t * dsc, lv_color_t color)
{
dsc->current_dsc.stroke_dsc.style = LV_VECTOR_DRAW_STYLE_SOLID;
dsc->current_dsc.stroke_dsc.color = lv_color_to_32(color, 0xFF);
}
void lv_vector_dsc_set_stroke_opa(lv_vector_dsc_t * dsc, lv_opa_t opa)
{
dsc->current_dsc.stroke_dsc.opa = opa;
}
void lv_vector_dsc_set_stroke_width(lv_vector_dsc_t * dsc, float width)
{
dsc->current_dsc.stroke_dsc.width = width;
}
void lv_vector_dsc_set_stroke_dash(lv_vector_dsc_t * dsc, float * dash_pattern, uint16_t dash_count)
{
lv_array_t * dash_array = &(dsc->current_dsc.stroke_dsc.dash_pattern);
if(dash_pattern) {
lv_array_clear(dash_array);
if(lv_array_capacity(dash_array) == 0) {
lv_array_init(dash_array, dash_count, sizeof(float));
}
else {
lv_array_resize(dash_array, dash_count);
}
for(uint16_t i = 0; i < dash_count; i++) {
lv_array_push_back(dash_array, &dash_pattern[i]);
}
}
else { /*clear dash*/
lv_array_clear(dash_array);
}
}
void lv_vector_dsc_set_stroke_cap(lv_vector_dsc_t * dsc, lv_vector_stroke_cap_t cap)
{
dsc->current_dsc.stroke_dsc.cap = cap;
}
void lv_vector_dsc_set_stroke_join(lv_vector_dsc_t * dsc, lv_vector_stroke_join_t join)
{
dsc->current_dsc.stroke_dsc.join = join;
}
void lv_vector_dsc_set_stroke_miter_limit(lv_vector_dsc_t * dsc, uint16_t miter_limit)
{
dsc->current_dsc.stroke_dsc.miter_limit = miter_limit;
}
void lv_vector_dsc_set_stroke_linear_gradient(lv_vector_dsc_t * dsc, float x1, float y1, float x2, float y2)
{
dsc->current_dsc.stroke_dsc.style = LV_VECTOR_DRAW_STYLE_GRADIENT;
dsc->current_dsc.stroke_dsc.gradient.style = LV_VECTOR_GRADIENT_STYLE_LINEAR;
dsc->current_dsc.stroke_dsc.gradient.x1 = x1;
dsc->current_dsc.stroke_dsc.gradient.y1 = y1;
dsc->current_dsc.stroke_dsc.gradient.x2 = x2;
dsc->current_dsc.stroke_dsc.gradient.y2 = y2;
}
void lv_vector_dsc_set_stroke_radial_gradient(lv_vector_dsc_t * dsc, float cx, float cy, float radius)
{
dsc->current_dsc.stroke_dsc.style = LV_VECTOR_DRAW_STYLE_GRADIENT;
dsc->current_dsc.stroke_dsc.gradient.style = LV_VECTOR_GRADIENT_STYLE_RADIAL;
dsc->current_dsc.stroke_dsc.gradient.cx = cx;
dsc->current_dsc.stroke_dsc.gradient.cy = cy;
dsc->current_dsc.stroke_dsc.gradient.cr = radius;
}
void lv_vector_dsc_set_stroke_gradient_spread(lv_vector_dsc_t * dsc, lv_vector_gradient_spread_t spread)
{
dsc->current_dsc.stroke_dsc.gradient.spread = spread;
}
void lv_vector_dsc_set_stroke_gradient_color_stops(lv_vector_dsc_t * dsc, const lv_grad_stop_t * stops,
uint16_t count)
{
if(count > LV_GRADIENT_MAX_STOPS) {
LV_LOG_WARN("Gradient stops limited: %d, max: %d", count, LV_GRADIENT_MAX_STOPS);
count = LV_GRADIENT_MAX_STOPS;
}
lv_memcpy(&(dsc->current_dsc.stroke_dsc.gradient.stops), stops, sizeof(lv_grad_stop_t) * count);
dsc->current_dsc.stroke_dsc.gradient.stops_count = count;
}
/* draw functions */
void lv_vector_dsc_add_path(lv_vector_dsc_t * dsc, const lv_vector_path_t * path)
{
lv_area_t rect;
if(!lv_area_intersect(&rect, &(dsc->layer->_clip_area), &(dsc->current_dsc.scissor_area))) {
return;
}
if(dsc->current_dsc.fill_dsc.opa == 0
&& dsc->current_dsc.stroke_dsc.opa == 0) {
return;
}
if(!dsc->tasks.task_list) {
dsc->tasks.task_list = lv_malloc(sizeof(lv_ll_t));
LV_ASSERT_MALLOC(dsc->tasks.task_list);
lv_ll_init(dsc->tasks.task_list, sizeof(lv_vector_draw_task));
}
lv_vector_draw_task * new_task = (lv_vector_draw_task *)lv_ll_ins_tail(dsc->tasks.task_list);
lv_memset(new_task, 0, sizeof(lv_vector_draw_task));
new_task->path = lv_vector_path_create(0);
_copy_draw_dsc(&(new_task->dsc), &(dsc->current_dsc));
lv_vector_path_copy(new_task->path, path);
new_task->dsc.scissor_area = rect;
}
void lv_vector_clear_area(lv_vector_dsc_t * dsc, const lv_area_t * rect)
{
lv_area_t r;
if(!lv_area_intersect(&r, &(dsc->layer->_clip_area), &(dsc->current_dsc.scissor_area))) {
return;
}
lv_area_t final_rect;
if(!lv_area_intersect(&final_rect, &r, rect)) {
return;
}
if(!dsc->tasks.task_list) {
dsc->tasks.task_list = lv_malloc(sizeof(lv_ll_t));
LV_ASSERT_MALLOC(dsc->tasks.task_list);
lv_ll_init(dsc->tasks.task_list, sizeof(lv_vector_draw_task));
}
lv_vector_draw_task * new_task = (lv_vector_draw_task *)lv_ll_ins_tail(dsc->tasks.task_list);
lv_memset(new_task, 0, sizeof(lv_vector_draw_task));
new_task->dsc.fill_dsc.color = dsc->current_dsc.fill_dsc.color;
new_task->dsc.fill_dsc.opa = dsc->current_dsc.fill_dsc.opa;
lv_area_copy(&(new_task->dsc.scissor_area), &final_rect);
}
void lv_draw_vector(lv_vector_dsc_t * dsc)
{
if(!dsc->tasks.task_list) {
return;
}
lv_layer_t * layer = dsc->layer;
lv_draw_task_t * t = lv_draw_add_task(layer, &(layer->_clip_area), LV_DRAW_TASK_TYPE_VECTOR);
lv_memcpy(t->draw_dsc, &(dsc->tasks), sizeof(lv_draw_vector_task_dsc_t));
lv_draw_finalize_task_creation(layer, t);
dsc->tasks.task_list = NULL;
}
/* draw dsc transform */
void lv_vector_dsc_identity(lv_vector_dsc_t * dsc)
{
lv_matrix_identity(&(dsc->current_dsc.matrix)); /*identity matrix*/
}
void lv_vector_dsc_scale(lv_vector_dsc_t * dsc, float scale_x, float scale_y)
{
lv_matrix_scale(&(dsc->current_dsc.matrix), scale_x, scale_y);
}
void lv_vector_dsc_rotate(lv_vector_dsc_t * dsc, float degree)
{
lv_matrix_rotate(&(dsc->current_dsc.matrix), degree);
}
void lv_vector_dsc_translate(lv_vector_dsc_t * dsc, float tx, float ty)
{
lv_matrix_translate(&(dsc->current_dsc.matrix), tx, ty);
}
void lv_vector_dsc_skew(lv_vector_dsc_t * dsc, float skew_x, float skew_y)
{
lv_matrix_skew(&(dsc->current_dsc.matrix), skew_x, skew_y);
}
void lv_vector_for_each_destroy_tasks(lv_ll_t * task_list, vector_draw_task_cb cb, void * data)
{
if(task_list == NULL) return;
lv_vector_draw_task * task = lv_ll_get_head(task_list);
lv_vector_draw_task * next_task = NULL;
while(task != NULL) {
next_task = lv_ll_get_next(task_list, task);
lv_ll_remove(task_list, task);
if(cb) {
cb(data, task->path, &(task->dsc));
}
if(task->path) {
lv_vector_path_delete(task->path);
}
lv_array_deinit(&(task->dsc.stroke_dsc.dash_pattern));
lv_free(task);
task = next_task;
}
lv_free(task_list);
}
#endif /* LV_USE_VECTOR_GRAPHIC */
@@ -0,0 +1,503 @@
/**
* @file lv_draw_vector.h
*
*/
#ifndef LV_DRAW_VECTOR_H
#define LV_DRAW_VECTOR_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../misc/lv_array.h"
#include "../misc/lv_matrix.h"
#include "lv_draw_image.h"
#if LV_USE_VECTOR_GRAPHIC
#if !LV_USE_MATRIX
#error "lv_draw_vector needs LV_USE_MATRIX = 1"
#endif
/**********************
* TYPEDEFS
**********************/
typedef enum {
LV_VECTOR_FILL_NONZERO = 0,
LV_VECTOR_FILL_EVENODD,
} lv_vector_fill_t;
typedef enum {
LV_VECTOR_STROKE_CAP_BUTT = 0,
LV_VECTOR_STROKE_CAP_SQUARE,
LV_VECTOR_STROKE_CAP_ROUND,
} lv_vector_stroke_cap_t;
typedef enum {
LV_VECTOR_STROKE_JOIN_MITER = 0,
LV_VECTOR_STROKE_JOIN_BEVEL,
LV_VECTOR_STROKE_JOIN_ROUND,
} lv_vector_stroke_join_t;
typedef enum {
LV_VECTOR_PATH_QUALITY_MEDIUM = 0, /* default*/
LV_VECTOR_PATH_QUALITY_HIGH,
LV_VECTOR_PATH_QUALITY_LOW,
} lv_vector_path_quality_t;
typedef enum {
LV_VECTOR_BLEND_SRC_OVER = 0,
LV_VECTOR_BLEND_SRC_IN,
LV_VECTOR_BLEND_DST_OVER,
LV_VECTOR_BLEND_DST_IN,
LV_VECTOR_BLEND_SCREEN,
LV_VECTOR_BLEND_MULTIPLY,
LV_VECTOR_BLEND_NONE,
LV_VECTOR_BLEND_ADDITIVE,
LV_VECTOR_BLEND_SUBTRACTIVE,
} lv_vector_blend_t;
typedef enum {
LV_VECTOR_PATH_OP_MOVE_TO = 0,
LV_VECTOR_PATH_OP_LINE_TO,
LV_VECTOR_PATH_OP_QUAD_TO,
LV_VECTOR_PATH_OP_CUBIC_TO,
LV_VECTOR_PATH_OP_CLOSE,
} lv_vector_path_op_t;
typedef enum {
LV_VECTOR_DRAW_STYLE_SOLID = 0,
LV_VECTOR_DRAW_STYLE_PATTERN,
LV_VECTOR_DRAW_STYLE_GRADIENT,
} lv_vector_draw_style_t;
typedef enum {
LV_VECTOR_GRADIENT_SPREAD_PAD = 0,
LV_VECTOR_GRADIENT_SPREAD_REPEAT,
LV_VECTOR_GRADIENT_SPREAD_REFLECT,
} lv_vector_gradient_spread_t;
typedef enum {
LV_VECTOR_GRADIENT_STYLE_LINEAR = 0,
LV_VECTOR_GRADIENT_STYLE_RADIAL,
} lv_vector_gradient_style_t;
typedef enum {
LV_VECTOR_FILL_UNITS_OBJECT_BOUNDING_BOX = 0, /** Relative coordinates relative to the object bounding box. */
LV_VECTOR_FILL_UNITS_USER_SPACE_ON_USE, /** Absolute coordinates relative to the layer's coordinate system */
} lv_vector_fill_units_t;
struct _lv_fpoint_t {
float x;
float y;
};
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Transform the coordinates of a point using given matrix
* @param matrix pointer to a matrix
* @param point pointer to a point
*/
void lv_matrix_transform_point(const lv_matrix_t * matrix, lv_fpoint_t * point);
/**
* Transform all the coordinates of a path using given matrix
* @param matrix pointer to a matrix
* @param path pointer to a path
*/
void lv_matrix_transform_path(const lv_matrix_t * matrix, lv_vector_path_t * path);
/**
* Create a vector graphic path object
* @param quality the quality hint of path
* @return pointer to the created path object
*/
lv_vector_path_t * lv_vector_path_create(lv_vector_path_quality_t quality);
/**
* Copy a path data to another
* @param target_path pointer to a path
* @param path pointer to source path
*/
void lv_vector_path_copy(lv_vector_path_t * target_path, const lv_vector_path_t * path);
/**
* Clear path data
* @param path pointer to a path
*/
void lv_vector_path_clear(lv_vector_path_t * path);
/**
* Delete the graphic path object
* @param path pointer to a path
*/
void lv_vector_path_delete(lv_vector_path_t * path);
/**
* Begin a new sub path and set a point to path
* @param path pointer to a path
* @param p pointer to a `lv_fpoint_t` variable
*/
void lv_vector_path_move_to(lv_vector_path_t * path, const lv_fpoint_t * p);
/**
* Add a line to the path from last point to the point
* @param path pointer to a path
* @param p pointer to a `lv_fpoint_t` variable
*/
void lv_vector_path_line_to(lv_vector_path_t * path, const lv_fpoint_t * p);
/**
* Add a quadratic bezier line to the path from last point to the point
* @param path pointer to a path
* @param p1 pointer to a `lv_fpoint_t` variable for control point
* @param p2 pointer to a `lv_fpoint_t` variable for end point
*/
void lv_vector_path_quad_to(lv_vector_path_t * path, const lv_fpoint_t * p1, const lv_fpoint_t * p2);
/**
* Add a cubic bezier line to the path from last point to the point
* @param path pointer to a path
* @param p1 pointer to a `lv_fpoint_t` variable for first control point
* @param p2 pointer to a `lv_fpoint_t` variable for second control point
* @param p3 pointer to a `lv_fpoint_t` variable for end point
*/
void lv_vector_path_cubic_to(lv_vector_path_t * path, const lv_fpoint_t * p1, const lv_fpoint_t * p2,
const lv_fpoint_t * p3);
/**
* Close the sub path
* @param path pointer to a path
*/
void lv_vector_path_close(lv_vector_path_t * path);
/**
* Get the bounding box of a path
* @param path pointer to a path
* @param area pointer to a `lv_area_t` variable for bounding box
*/
void lv_vector_path_get_bounding(const lv_vector_path_t * path, lv_area_t * area);
/**
* Add a rectangle to the path
* @param path pointer to a path
* @param rect pointer to a `lv_area_t` variable
* @param rx the horizontal radius for rounded rectangle
* @param ry the vertical radius for rounded rectangle
*/
void lv_vector_path_append_rect(lv_vector_path_t * path, const lv_area_t * rect, float rx, float ry);
/**
* Add a circle to the path
* @param path pointer to a path
* @param c pointer to a `lv_fpoint_t` variable for center of the circle
* @param rx the horizontal radius for circle
* @param ry the vertical radius for circle
*/
void lv_vector_path_append_circle(lv_vector_path_t * path, const lv_fpoint_t * c, float rx, float ry);
/**
* Add a arc to the path
* @param path pointer to a path
* @param c pointer to a `lv_fpoint_t` variable for center of the circle
* @param radius the radius for arc
* @param start_angle the start angle for arc
* @param sweep the sweep angle for arc, could be negative
* @param pie true: draw a pie, false: draw a arc
*/
void lv_vector_path_append_arc(lv_vector_path_t * path, const lv_fpoint_t * c, float radius, float start_angle,
float sweep, bool pie);
/**
* Add an sub path to the path
* @param path pointer to a path
* @param subpath pointer to another path which will be added
*/
void lv_vector_path_append_path(lv_vector_path_t * path, const lv_vector_path_t * subpath);
/**
* Create a vector graphic descriptor
* @param layer pointer to a layer
* @return pointer to the created descriptor
*/
lv_vector_dsc_t * lv_vector_dsc_create(lv_layer_t * layer);
/**
* Delete the vector graphic descriptor
* @param dsc pointer to a vector graphic descriptor
*/
void lv_vector_dsc_delete(lv_vector_dsc_t * dsc);
/**
* Set a matrix to current transformation matrix
* @param dsc pointer to a vector graphic descriptor
* @param matrix pointer to a matrix
*/
void lv_vector_dsc_set_transform(lv_vector_dsc_t * dsc, const lv_matrix_t * matrix);
/**
* Set blend mode for descriptor
* @param dsc pointer to a vector graphic descriptor
* @param blend the blend mode to be set in `lv_vector_blend_t`
*/
void lv_vector_dsc_set_blend_mode(lv_vector_dsc_t * dsc, lv_vector_blend_t blend);
/**
* Set fill color for descriptor
* @param dsc pointer to a vector graphic descriptor
* @param color the color to be set in lv_color32_t format
*/
void lv_vector_dsc_set_fill_color32(lv_vector_dsc_t * dsc, lv_color32_t color);
/**
* Set fill color for descriptor
* @param dsc pointer to a vector graphic descriptor
* @param color the color to be set in lv_color_t format
*/
void lv_vector_dsc_set_fill_color(lv_vector_dsc_t * dsc, lv_color_t color);
/**
* Set fill opacity for descriptor
* @param dsc pointer to a vector graphic descriptor
* @param opa the opacity to be set in lv_opa_t format
*/
void lv_vector_dsc_set_fill_opa(lv_vector_dsc_t * dsc, lv_opa_t opa);
/**
* Set fill rule for descriptor
* @param dsc pointer to a vector graphic descriptor
* @param rule the fill rule to be set in lv_vector_fill_t format
*/
void lv_vector_dsc_set_fill_rule(lv_vector_dsc_t * dsc, lv_vector_fill_t rule);
/**
* Set the fill units for descriptor.
* @param dsc pointer to a vector graphic descriptor
* @param units the units to be set in lv_vector_fill_units_t format
* @note The units can be either relative to the object bounding box or absolute in user space.
* This API specifically affects the drawing position of the fill image and does not impact other elements.
*/
void lv_vector_dsc_set_fill_units(lv_vector_dsc_t * dsc, const lv_vector_fill_units_t units);
/**
* Set fill image for descriptor
* @param dsc pointer to a vector graphic descriptor
* @param img_dsc pointer to a `lv_draw_image_dsc_t` variable
*/
void lv_vector_dsc_set_fill_image(lv_vector_dsc_t * dsc, const lv_draw_image_dsc_t * img_dsc);
/**
* Set fill linear gradient for descriptor
* @param dsc pointer to a vector graphic descriptor
* @param x1 the x for start point
* @param y1 the y for start point
* @param x2 the x for end point
* @param y2 the y for end point
*/
void lv_vector_dsc_set_fill_linear_gradient(lv_vector_dsc_t * dsc, float x1, float y1, float x2, float y2);
/**
* Set fill radial gradient radius for descriptor
* @param dsc pointer to a vector graphic descriptor
* @param cx the x for center of the circle
* @param cy the y for center of the circle
* @param radius the radius for circle
*/
void lv_vector_dsc_set_fill_radial_gradient(lv_vector_dsc_t * dsc, float cx, float cy, float radius);
/**
* Set fill radial gradient spread for descriptor
* @param dsc pointer to a vector graphic descriptor
* @param spread the gradient spread to be set in lv_vector_gradient_spread_t format
*/
void lv_vector_dsc_set_fill_gradient_spread(lv_vector_dsc_t * dsc, lv_vector_gradient_spread_t spread);
/**
* Set fill gradient color stops for descriptor
* @param dsc pointer to a vector graphic descriptor
* @param stops an array of `lv_grad_stop_t` variables
* @param count the number of stops in the array, range: 0..LV_GRADIENT_MAX_STOPS
*/
void lv_vector_dsc_set_fill_gradient_color_stops(lv_vector_dsc_t * dsc, const lv_grad_stop_t * stops,
uint16_t count);
/**
* Set a matrix to current fill transformation matrix
* @param dsc pointer to a vector graphic descriptor
* @param matrix pointer to a matrix
*/
void lv_vector_dsc_set_fill_transform(lv_vector_dsc_t * dsc, const lv_matrix_t * matrix);
/**
* Set stroke color for descriptor
* @param dsc pointer to a vector graphic descriptor
* @param color the color to be set in lv_color32_t format
*/
void lv_vector_dsc_set_stroke_color32(lv_vector_dsc_t * dsc, lv_color32_t color);
/**
* Set stroke color for descriptor
* @param dsc pointer to a vector graphic descriptor
* @param color the color to be set in lv_color_t format
*/
void lv_vector_dsc_set_stroke_color(lv_vector_dsc_t * dsc, lv_color_t color);
/**
* Set stroke opacity for descriptor
* @param dsc pointer to a vector graphic descriptor
* @param opa the opacity to be set in lv_opa_t format
*/
void lv_vector_dsc_set_stroke_opa(lv_vector_dsc_t * dsc, lv_opa_t opa);
/**
* Set stroke line width for descriptor
* @param dsc pointer to a vector graphic descriptor
* @param width the stroke line width
*/
void lv_vector_dsc_set_stroke_width(lv_vector_dsc_t * dsc, float width);
/**
* Set stroke line dash pattern for descriptor
* @param dsc pointer to a vector graphic descriptor
* @param dash_pattern an array of values that specify the segments of dash line
* @param dash_count the length of dash pattern array
*/
void lv_vector_dsc_set_stroke_dash(lv_vector_dsc_t * dsc, float * dash_pattern, uint16_t dash_count);
/**
* Set stroke line cap style for descriptor
* @param dsc pointer to a vector graphic descriptor
* @param cap the line cap to be set in lv_vector_stroke_cap_t format
*/
void lv_vector_dsc_set_stroke_cap(lv_vector_dsc_t * dsc, lv_vector_stroke_cap_t cap);
/**
* Set stroke line join style for descriptor
* @param dsc pointer to a vector graphic descriptor
* @param join the line join to be set in lv_vector_stroke_join_t format
*/
void lv_vector_dsc_set_stroke_join(lv_vector_dsc_t * dsc, lv_vector_stroke_join_t join);
/**
* Set stroke miter limit for descriptor
* @param dsc pointer to a vector graphic descriptor
* @param miter_limit the stroke miter_limit
*/
void lv_vector_dsc_set_stroke_miter_limit(lv_vector_dsc_t * dsc, uint16_t miter_limit);
/**
* Set stroke linear gradient for descriptor
* @param dsc pointer to a vector graphic descriptor
* @param x1 the x for start point
* @param y1 the y for start point
* @param x2 the x for end point
* @param y2 the y for end point
*/
void lv_vector_dsc_set_stroke_linear_gradient(lv_vector_dsc_t * dsc, float x1, float y1, float x2, float y2);
/**
* Set stroke radial gradient for descriptor
* @param dsc pointer to a vector graphic descriptor
* @param cx the x for center of the circle
* @param cy the y for center of the circle
* @param radius the radius for circle
*/
void lv_vector_dsc_set_stroke_radial_gradient(lv_vector_dsc_t * dsc, float cx, float cy, float radius);
/**
* Set stroke color stops for descriptor
* @param dsc pointer to a vector graphic descriptor
* @param spread the gradient spread to be set in lv_vector_gradient_spread_t format
*/
void lv_vector_dsc_set_stroke_gradient_spread(lv_vector_dsc_t * dsc, lv_vector_gradient_spread_t spread);
/**
* Set stroke color stops for descriptor
* @param dsc pointer to a vector graphic descriptor
* @param stops an array of `lv_grad_stop_t` variables
* @param count the number of stops in the array
*/
void lv_vector_dsc_set_stroke_gradient_color_stops(lv_vector_dsc_t * dsc, const lv_grad_stop_t * stops,
uint16_t count);
/**
* Set a matrix to current stroke transformation matrix
* @param dsc pointer to a vector graphic descriptor
* @param matrix pointer to a matrix
*/
void lv_vector_dsc_set_stroke_transform(lv_vector_dsc_t * dsc, const lv_matrix_t * matrix);
/**
* Set current transformation matrix to identity matrix
* @param dsc pointer to a vector graphic descriptor
*/
void lv_vector_dsc_identity(lv_vector_dsc_t * dsc);
/**
* Change the scale factor of current transformation matrix
* @param dsc pointer to a vector graphic descriptor
* @param scale_x the scale factor for the X direction
* @param scale_y the scale factor for the Y direction
*/
void lv_vector_dsc_scale(lv_vector_dsc_t * dsc, float scale_x, float scale_y);
/**
* Rotate current transformation matrix with origin
* @param dsc pointer to a vector graphic descriptor
* @param degree angle to rotate
*/
void lv_vector_dsc_rotate(lv_vector_dsc_t * dsc, float degree);
/**
* Translate current transformation matrix to new position
* @param dsc pointer to a vector graphic descriptor
* @param tx the amount of translate in x direction
* @param tx the amount of translate in y direction
*/
void lv_vector_dsc_translate(lv_vector_dsc_t * dsc, float tx, float ty);
/**
* Change the skew factor of current transformation matrix
* @param dsc pointer to a vector graphic descriptor
* @param skew_x the skew factor for x direction
* @param skew_y the skew factor for y direction
*/
void lv_vector_dsc_skew(lv_vector_dsc_t * dsc, float skew_x, float skew_y);
/**
* Add a graphic path to the draw list
* @param dsc pointer to a vector graphic descriptor
* @param path pointer to a path
*/
void lv_vector_dsc_add_path(lv_vector_dsc_t * dsc, const lv_vector_path_t * path);
/**
* Clear a rectangle area use current fill color
* @param dsc pointer to a vector graphic descriptor
* @param rect the area to clear in the buffer
*/
void lv_vector_clear_area(lv_vector_dsc_t * dsc, const lv_area_t * rect);
/**
* Draw all the vector graphic paths
* @param dsc pointer to a vector graphic descriptor
*/
void lv_draw_vector(lv_vector_dsc_t * dsc);
/* Traverser for task list */
typedef void (*vector_draw_task_cb)(void * ctx, const lv_vector_path_t * path, const lv_vector_draw_dsc_t * dsc);
#endif /* LV_USE_VECTOR_GRAPHIC */
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /* LV_DRAW_VECTOR_H */
@@ -0,0 +1,109 @@
/**
* @file lv_draw_vector_private.h
*
*/
#ifndef LV_DRAW_VECTOR_PRIVATE_H
#define LV_DRAW_VECTOR_PRIVATE_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "lv_draw_vector.h"
#if LV_USE_VECTOR_GRAPHIC
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
struct _lv_vector_path_t {
lv_vector_path_quality_t quality;
lv_array_t ops;
lv_array_t points;
};
struct _lv_vector_gradient_t {
lv_vector_gradient_style_t style;
lv_grad_stop_t stops[LV_GRADIENT_MAX_STOPS]; /**< A gradient stop array */
uint16_t stops_count; /**< The number of used stops in the array */
float x1;
float y1;
float x2;
float y2;
float cx;
float cy;
float cr;
lv_vector_gradient_spread_t spread;
};
struct _lv_vector_fill_dsc_t {
lv_vector_draw_style_t style;
lv_color32_t color;
lv_opa_t opa;
lv_vector_fill_t fill_rule;
lv_vector_fill_units_t fill_units;
lv_draw_image_dsc_t img_dsc;
lv_vector_gradient_t gradient;
lv_matrix_t matrix;
};
struct _lv_vector_stroke_dsc_t {
lv_vector_draw_style_t style;
lv_color32_t color;
lv_opa_t opa;
float width;
lv_array_t dash_pattern;
lv_vector_stroke_cap_t cap;
lv_vector_stroke_join_t join;
uint16_t miter_limit;
lv_vector_gradient_t gradient;
lv_matrix_t matrix;
};
struct _lv_vector_draw_dsc_t {
lv_vector_fill_dsc_t fill_dsc;
lv_vector_stroke_dsc_t stroke_dsc;
lv_matrix_t matrix;
lv_vector_blend_t blend_mode;
lv_area_t scissor_area;
};
struct _lv_draw_vector_task_dsc_t {
lv_draw_dsc_base_t base;
lv_ll_t * task_list; /*draw task list.*/
};
struct _lv_vector_dsc_t {
lv_layer_t * layer;
lv_vector_draw_dsc_t current_dsc;
/* private data */
lv_draw_vector_task_dsc_t tasks;
};
/**********************
* GLOBAL PROTOTYPES
**********************/
void lv_vector_for_each_destroy_tasks(lv_ll_t * task_list, vector_draw_task_cb cb, void * data);
/**********************
* MACROS
**********************/
#endif /* LV_USE_VECTOR_GRAPHIC */
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_DRAW_VECTOR_PRIVATE_H*/
@@ -0,0 +1,417 @@
/**
* @file lv_image_decoder.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_image_decoder_private.h"
#include "../misc/lv_assert.h"
#include "../draw/lv_draw_image.h"
#include "../misc/lv_ll.h"
#include "../stdlib/lv_string.h"
#include "../core/lv_global.h"
/*********************
* DEFINES
*********************/
#define img_decoder_ll_p &(LV_GLOBAL_DEFAULT()->img_decoder_ll)
#define img_cache_p (LV_GLOBAL_DEFAULT()->img_cache)
#define img_header_cache_p (LV_GLOBAL_DEFAULT()->img_header_cache)
#define image_cache_draw_buf_handlers &(LV_GLOBAL_DEFAULT()->image_cache_draw_buf_handlers)
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static uint32_t img_width_to_stride(lv_image_header_t * header);
/**
* Get the header info of an image source, and return the a pointer to the decoder that can open it.
* @param dsc Image descriptor containing the source and type of the image and other info.
* @param header The header of the image
* @return The decoder that can open the image source or NULL if not found (or can't open it).
*/
static lv_image_decoder_t * image_decoder_get_info(lv_image_decoder_dsc_t * dsc, lv_image_header_t * header);
static lv_result_t try_cache(lv_image_decoder_dsc_t * dsc);
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
/**
* Initialize the image decoder module
*/
void lv_image_decoder_init(uint32_t image_cache_size, uint32_t image_header_count)
{
lv_ll_init(img_decoder_ll_p, sizeof(lv_image_decoder_t));
/*Initialize the cache*/
lv_image_cache_init(image_cache_size);
lv_image_header_cache_init(image_header_count);
}
/**
* Deinitialize the image decoder module
*/
void lv_image_decoder_deinit(void)
{
lv_cache_destroy(img_cache_p, NULL);
lv_cache_destroy(img_header_cache_p, NULL);
lv_ll_clear(img_decoder_ll_p);
}
lv_result_t lv_image_decoder_get_info(const void * src, lv_image_header_t * header)
{
lv_image_decoder_dsc_t dsc;
lv_memzero(&dsc, sizeof(lv_image_decoder_dsc_t));
dsc.src = src;
dsc.src_type = lv_image_src_get_type(src);
lv_image_decoder_t * decoder = image_decoder_get_info(&dsc, header);
if(decoder == NULL) return LV_RESULT_INVALID;
return LV_RESULT_OK;
}
lv_result_t lv_image_decoder_open(lv_image_decoder_dsc_t * dsc, const void * src, const lv_image_decoder_args_t * args)
{
lv_memzero(dsc, sizeof(lv_image_decoder_dsc_t));
if(src == NULL) return LV_RESULT_INVALID;
dsc->src = src;
dsc->src_type = lv_image_src_get_type(src);
if(lv_image_cache_is_enabled()) {
dsc->cache = img_cache_p;
/*Try cache first, unless we are told to ignore cache.*/
if(!(args && args->no_cache)) {
/*
* Check the cache first
* If the image is found in the cache, just return it.*/
if(try_cache(dsc) == LV_RESULT_OK) return LV_RESULT_OK;
}
}
/*Find the decoder that can open the image source, and get the header info in the same time.*/
dsc->decoder = image_decoder_get_info(dsc, &dsc->header);
if(dsc->decoder == NULL) return LV_RESULT_INVALID;
/*Make a copy of args*/
dsc->args = args ? *args : (lv_image_decoder_args_t) {
.stride_align = LV_DRAW_BUF_STRIDE_ALIGN != 1,
.premultiply = false,
.no_cache = false,
.use_indexed = false,
.flush_cache = false,
};
/*
* We assume that if a decoder can get the info, it can open the image.
* If decoder open failed, free the source and return error.
* If decoder open succeed, add the image to cache if enabled.
* */
lv_result_t res = dsc->decoder->open_cb(dsc->decoder, dsc);
if(res == LV_RESULT_OK && dsc->decoded != NULL) {
LV_ASSERT_MSG(dsc->decoded->unaligned_data && dsc->decoded->handlers, "Invalid draw buffer");
/* Flush the D-Cache if enabled and the image was successfully opened */
if(dsc->args.flush_cache) {
lv_draw_buf_flush_cache(dsc->decoded, NULL);
LV_LOG_INFO("Flushed D-cache: src %p (%s) (W%d x H%d, data: %p cf: %d)",
src,
dsc->src_type == LV_IMAGE_SRC_FILE ? (const char *)src : "c-array",
dsc->decoded->header.w,
dsc->decoded->header.h,
(void *)dsc->decoded->data,
dsc->decoded->header.cf);
}
}
return res;
}
lv_result_t lv_image_decoder_get_area(lv_image_decoder_dsc_t * dsc, const lv_area_t * full_area,
lv_area_t * decoded_area)
{
lv_result_t res = LV_RESULT_INVALID;
if(dsc->decoder->get_area_cb) res = dsc->decoder->get_area_cb(dsc->decoder, dsc, full_area, decoded_area);
return res;
}
void lv_image_decoder_close(lv_image_decoder_dsc_t * dsc)
{
if(dsc->decoder) {
if(dsc->decoder->close_cb) dsc->decoder->close_cb(dsc->decoder, dsc);
if(lv_image_cache_is_enabled() && dsc->cache && dsc->cache_entry) {
/*Decoded data is in cache, release it from cache's callback*/
lv_cache_release(dsc->cache, dsc->cache_entry, NULL);
}
}
}
/**
* Create a new image decoder
* @return pointer to the new image decoder
*/
lv_image_decoder_t * lv_image_decoder_create(void)
{
lv_image_decoder_t * decoder;
decoder = lv_ll_ins_head(img_decoder_ll_p);
LV_ASSERT_MALLOC(decoder);
if(decoder == NULL) return NULL;
lv_memzero(decoder, sizeof(lv_image_decoder_t));
return decoder;
}
void lv_image_decoder_delete(lv_image_decoder_t * decoder)
{
lv_ll_remove(img_decoder_ll_p, decoder);
lv_free(decoder);
}
lv_image_decoder_t * lv_image_decoder_get_next(lv_image_decoder_t * decoder)
{
if(decoder == NULL)
return lv_ll_get_head(img_decoder_ll_p);
else
return lv_ll_get_next(img_decoder_ll_p, decoder);
}
void lv_image_decoder_set_info_cb(lv_image_decoder_t * decoder, lv_image_decoder_info_f_t info_cb)
{
decoder->info_cb = info_cb;
}
void lv_image_decoder_set_open_cb(lv_image_decoder_t * decoder, lv_image_decoder_open_f_t open_cb)
{
decoder->open_cb = open_cb;
}
void lv_image_decoder_set_get_area_cb(lv_image_decoder_t * decoder, lv_image_decoder_get_area_cb_t get_area_cb)
{
decoder->get_area_cb = get_area_cb;
}
void lv_image_decoder_set_close_cb(lv_image_decoder_t * decoder, lv_image_decoder_close_f_t close_cb)
{
decoder->close_cb = close_cb;
}
lv_cache_entry_t * lv_image_decoder_add_to_cache(lv_image_decoder_t * decoder,
lv_image_cache_data_t * search_key,
const lv_draw_buf_t * decoded, void * user_data)
{
lv_cache_entry_t * cache_entry = lv_cache_add(img_cache_p, search_key, NULL);
if(cache_entry == NULL) {
return NULL;
}
lv_image_cache_data_t * cached_data;
cached_data = lv_cache_entry_get_data(cache_entry);
/*Set the cache entry to decoder data*/
cached_data->decoded = decoded;
if(cached_data->src_type == LV_IMAGE_SRC_FILE) {
cached_data->src = lv_strdup(cached_data->src);
}
cached_data->user_data = user_data; /*Need to free data on cache invalidate instead of decoder_close*/
cached_data->decoder = decoder;
return cache_entry;
}
lv_draw_buf_t * lv_image_decoder_post_process(lv_image_decoder_dsc_t * dsc, lv_draw_buf_t * decoded)
{
if(decoded == NULL) return NULL; /*No need to adjust*/
lv_image_decoder_args_t * args = &dsc->args;
if(args->stride_align && decoded->header.cf != LV_COLOR_FORMAT_RGB565A8) {
uint32_t stride_expect = lv_draw_buf_width_to_stride(decoded->header.w, decoded->header.cf);
if(decoded->header.stride != stride_expect) {
LV_LOG_TRACE("Stride mismatch");
lv_result_t res = lv_draw_buf_adjust_stride(decoded, stride_expect);
if(res != LV_RESULT_OK) {
lv_draw_buf_t * aligned = lv_draw_buf_create_ex(image_cache_draw_buf_handlers, decoded->header.w, decoded->header.h,
decoded->header.cf, stride_expect);
if(aligned == NULL) {
LV_LOG_ERROR("No memory for Stride adjust.");
return NULL;
}
lv_draw_buf_copy(aligned, NULL, decoded, NULL);
decoded = aligned;
}
}
}
/*Premultiply alpha channel*/
if(args->premultiply
&& !LV_COLOR_FORMAT_IS_ALPHA_ONLY(decoded->header.cf)
&& lv_color_format_has_alpha(decoded->header.cf)
&& !lv_draw_buf_has_flag(decoded, LV_IMAGE_FLAGS_PREMULTIPLIED) /*Hasn't done yet*/
) {
LV_LOG_TRACE("Alpha premultiply.");
if(lv_draw_buf_has_flag(decoded, LV_IMAGE_FLAGS_MODIFIABLE)) {
/*Do it directly*/
lv_draw_buf_premultiply(decoded);
}
else {
decoded = lv_draw_buf_dup_ex(image_cache_draw_buf_handlers, decoded);
if(decoded == NULL) {
LV_LOG_ERROR("No memory for premultiplying.");
return NULL;
}
lv_draw_buf_premultiply(decoded);
}
}
return decoded;
}
/**********************
* STATIC FUNCTIONS
**********************/
static lv_image_decoder_t * image_decoder_get_info(lv_image_decoder_dsc_t * dsc, lv_image_header_t * header)
{
lv_memzero(header, sizeof(lv_image_header_t));
const void * src = dsc->src;
lv_image_src_t src_type = dsc->src_type;
if(src_type == LV_IMAGE_SRC_VARIABLE) {
const lv_image_dsc_t * img_dsc = src;
if(img_dsc->data == NULL) return NULL;
}
if(src_type == LV_IMAGE_SRC_FILE) LV_LOG_TRACE("Try to find decoder for %s", (const char *)src);
else LV_LOG_TRACE("Try to find decoder for %p", src);
lv_image_decoder_t * decoder;
bool is_header_cache_enabled = lv_image_header_cache_is_enabled();
if(is_header_cache_enabled && src_type == LV_IMAGE_SRC_FILE) {
lv_image_header_cache_data_t search_key;
search_key.src_type = src_type;
search_key.src = src;
lv_cache_entry_t * entry = lv_cache_acquire(img_header_cache_p, &search_key, NULL);
if(entry) {
lv_image_header_cache_data_t * cached_data = lv_cache_entry_get_data(entry);
*header = cached_data->header;
decoder = cached_data->decoder;
lv_cache_release(img_header_cache_p, entry, NULL);
LV_LOG_TRACE("Found decoder %s in header cache", decoder->name);
return decoder;
}
}
if(src_type == LV_IMAGE_SRC_FILE) {
lv_fs_res_t fs_res = lv_fs_open(&dsc->file, src, LV_FS_MODE_RD);
if(fs_res != LV_FS_RES_OK) {
LV_LOG_ERROR("File open failed: %" LV_PRIu32, (uint32_t)fs_res);
return NULL;
}
}
/*Search the decoders*/
LV_LL_READ(img_decoder_ll_p, decoder) {
/*Info and Open callbacks are required*/
if(decoder->info_cb && decoder->open_cb) {
lv_fs_seek(&dsc->file, 0, LV_FS_SEEK_SET);
lv_result_t res = decoder->info_cb(decoder, dsc, header);
if(res == LV_RESULT_OK) {
if(header->stride == 0) {
LV_LOG_INFO("Image decoder didn't set stride. Calculate it from width.");
header->stride = img_width_to_stride(header);
}
break;
}
else {
LV_LOG_TRACE("Can't open image with decoder %s. Trying next decoder.", decoder->name);
}
}
}
if(decoder == NULL) LV_LOG_TRACE("No decoder found");
else LV_LOG_TRACE("Found decoder %s", decoder->name);
if(src_type == LV_IMAGE_SRC_FILE) {
lv_fs_close(&dsc->file);
}
if(is_header_cache_enabled && src_type == LV_IMAGE_SRC_FILE && decoder) {
lv_cache_entry_t * entry;
lv_image_header_cache_data_t search_key;
search_key.src_type = src_type;
search_key.src = lv_strdup(src);
search_key.decoder = decoder;
search_key.header = *header;
entry = lv_cache_add(img_header_cache_p, &search_key, NULL);
if(entry == NULL) {
if(src_type == LV_IMAGE_SRC_FILE) lv_free((void *)search_key.src);
return NULL;
}
lv_cache_release(img_header_cache_p, entry, NULL);
}
return decoder;
}
static uint32_t img_width_to_stride(lv_image_header_t * header)
{
if(header->cf == LV_COLOR_FORMAT_RGB565A8) {
return header->w * 2;
}
else {
return ((uint32_t)header->w * lv_color_format_get_bpp(header->cf) + 7) >> 3;
}
}
static lv_result_t try_cache(lv_image_decoder_dsc_t * dsc)
{
lv_cache_t * cache = dsc->cache;
lv_image_cache_data_t search_key;
search_key.src_type = dsc->src_type;
search_key.src = dsc->src;
lv_cache_entry_t * entry = lv_cache_acquire(cache, &search_key, NULL);
if(entry) {
lv_image_cache_data_t * cached_data = lv_cache_entry_get_data(entry);
dsc->decoded = cached_data->decoded;
dsc->decoder = (lv_image_decoder_t *)cached_data->decoder;
dsc->cache_entry = entry; /*Save the cache to release it in decoder_close*/
return LV_RESULT_OK;
}
return LV_RESULT_INVALID;
}
@@ -0,0 +1,203 @@
/**
* @file lv_image_decoder.h
*
*/
#ifndef LV_IMAGE_DECODER_H
#define LV_IMAGE_DECODER_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../lv_conf_internal.h"
#include "lv_draw_buf.h"
#include "../misc/lv_fs.h"
#include "../misc/lv_types.h"
#include "../misc/lv_area.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**
* Source of image.*/
typedef enum {
LV_IMAGE_SRC_VARIABLE, /** Binary/C variable*/
LV_IMAGE_SRC_FILE, /** File in filesystem*/
LV_IMAGE_SRC_SYMBOL, /** Symbol (@ref lv_symbol_def.h)*/
LV_IMAGE_SRC_UNKNOWN, /** Unknown source*/
} lv_image_src_t;
/**
* Get info from an image and store in the `header`
* @param decoder pointer to decoder object
* @param dsc pointer to decoder descriptor
* @param header store the info here
* @return LV_RESULT_OK: info written correctly; LV_RESULT_INVALID: failed
*/
typedef lv_result_t (*lv_image_decoder_info_f_t)(lv_image_decoder_t * decoder, lv_image_decoder_dsc_t * dsc,
lv_image_header_t * header);
/**
* Open an image for decoding. Prepare it as it is required to read it later
* @param decoder pointer to the decoder the function associated with
* @param dsc pointer to decoder descriptor. `src`, `color` are already initialized in it.
*/
typedef lv_result_t (*lv_image_decoder_open_f_t)(lv_image_decoder_t * decoder, lv_image_decoder_dsc_t * dsc);
/**
* Decode `full_area` pixels incrementally by calling in a loop. Set `decoded_area` values to `LV_COORD_MIN` on first call.
* Required only if the "open" function can't return with the whole decoded pixel array.
* @param decoder pointer to the decoder the function associated with
* @param dsc pointer to decoder descriptor
* @param full_area input parameter. the full area to decode after enough subsequent calls
* @param decoded_area input+output parameter. set the values to `LV_COORD_MIN` for the first call and to reset decoding.
* the decoded area is stored here after each call.
* @return LV_RESULT_OK: ok; LV_RESULT_INVALID: failed or there is nothing left to decode
*/
typedef lv_result_t (*lv_image_decoder_get_area_cb_t)(lv_image_decoder_t * decoder,
lv_image_decoder_dsc_t * dsc,
const lv_area_t * full_area, lv_area_t * decoded_area);
/**
* Close the pending decoding. Free resources etc.
* @param decoder pointer to the decoder the function associated with
* @param dsc pointer to decoder descriptor
*/
typedef void (*lv_image_decoder_close_f_t)(lv_image_decoder_t * decoder, lv_image_decoder_dsc_t * dsc);
/**
* Custom drawing functions for special image formats.
* @param layer pointer to a layer
* @param dsc pointer to decoder descriptor
* @param coords the coordinates of the image
* @param draw_dsc the draw image descriptor
* @param clip_area the clip area of the image
*/
typedef void (*lv_image_decoder_custom_draw_t)(lv_layer_t * layer, const lv_image_decoder_dsc_t * dsc,
const lv_area_t * coords, const lv_draw_image_dsc_t * draw_dsc, const lv_area_t * clip_area);
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Get information about an image.
* Try the created image decoder one by one. Once one is able to get info that info will be used.
* @param src the image source. Can be
* 1) File name: E.g. "S:folder/img1.png" (The drivers needs to registered via `lv_fs_drv_register()`)
* 2) Variable: Pointer to an `lv_image_dsc_t` variable
* 3) Symbol: E.g. `LV_SYMBOL_OK`
* @param header the image info will be stored here
* @return LV_RESULT_OK: success; LV_RESULT_INVALID: wasn't able to get info about the image
*/
lv_result_t lv_image_decoder_get_info(const void * src, lv_image_header_t * header);
/**
* Open an image.
* Try the created image decoders one by one. Once one is able to open the image that decoder is saved in `dsc`
* @param dsc describes a decoding session. Simply a pointer to an `lv_image_decoder_dsc_t` variable.
* @param src the image source. Can be
* 1) File name: E.g. "S:folder/img1.png" (The drivers needs to registered via `lv_fs_drv_register())`)
* 2) Variable: Pointer to an `lv_image_dsc_t` variable
* 3) Symbol: E.g. `LV_SYMBOL_OK`
* @param args args about how the image should be opened.
* @return LV_RESULT_OK: opened the image. `dsc->decoded` and `dsc->header` are set.
* LV_RESULT_INVALID: none of the registered image decoders were able to open the image.
*/
lv_result_t lv_image_decoder_open(lv_image_decoder_dsc_t * dsc, const void * src, const lv_image_decoder_args_t * args);
/**
* Decode `full_area` pixels incrementally by calling in a loop. Set `decoded_area` to `LV_COORD_MIN` on first call.
* @param dsc image decoder descriptor
* @param full_area input parameter. the full area to decode after enough subsequent calls
* @param decoded_area input+output parameter. set the values to `LV_COORD_MIN` for the first call and to reset decoding.
* the decoded area is stored here after each call.
* @return LV_RESULT_OK: success; LV_RESULT_INVALID: an error occurred or there is nothing left to decode
*/
lv_result_t lv_image_decoder_get_area(lv_image_decoder_dsc_t * dsc, const lv_area_t * full_area,
lv_area_t * decoded_area);
/**
* Close a decoding session
* @param dsc pointer to `lv_image_decoder_dsc_t` used in `lv_image_decoder_open`
*/
void lv_image_decoder_close(lv_image_decoder_dsc_t * dsc);
/**
* Create a new image decoder
* @return pointer to the new image decoder
*/
lv_image_decoder_t * lv_image_decoder_create(void);
/**
* Delete an image decoder
* @param decoder pointer to an image decoder
*/
void lv_image_decoder_delete(lv_image_decoder_t * decoder);
/**
* Get the next image decoder in the linked list of image decoders
* @param decoder pointer to an image decoder or NULL to get the first one
* @return the next image decoder or NULL if no more image decoder exists
*/
lv_image_decoder_t * lv_image_decoder_get_next(lv_image_decoder_t * decoder);
/**
* Set a callback to get information about the image
* @param decoder pointer to an image decoder
* @param info_cb a function to collect info about an image (fill an `lv_image_header_t` struct)
*/
void lv_image_decoder_set_info_cb(lv_image_decoder_t * decoder, lv_image_decoder_info_f_t info_cb);
/**
* Set a callback to open an image
* @param decoder pointer to an image decoder
* @param open_cb a function to open an image
*/
void lv_image_decoder_set_open_cb(lv_image_decoder_t * decoder, lv_image_decoder_open_f_t open_cb);
/**
* Set a callback to a decoded line of an image
* @param decoder pointer to an image decoder
* @param read_line_cb a function to read a line of an image
*/
void lv_image_decoder_set_get_area_cb(lv_image_decoder_t * decoder, lv_image_decoder_get_area_cb_t read_line_cb);
/**
* Set a callback to close a decoding session. E.g. close files and free other resources.
* @param decoder pointer to an image decoder
* @param close_cb a function to close a decoding session
*/
void lv_image_decoder_set_close_cb(lv_image_decoder_t * decoder, lv_image_decoder_close_f_t close_cb);
lv_cache_entry_t * lv_image_decoder_add_to_cache(lv_image_decoder_t * decoder,
lv_image_cache_data_t * search_key,
const lv_draw_buf_t * decoded, void * user_data);
/**
* Check the decoded image, make any modification if decoder `args` requires.
* @note A new draw buf will be allocated if provided `decoded` is not modifiable or stride mismatch etc.
* @param dsc pointer to a decoder descriptor
* @param decoded pointer to a decoded image to post process to meet dsc->args requirement.
* @return post processed draw buffer, when it differs with `decoded`, it's newly allocated.
*/
lv_draw_buf_t * lv_image_decoder_post_process(lv_image_decoder_dsc_t * dsc, lv_draw_buf_t * decoded);
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_IMAGE_DECODER_H*/
@@ -0,0 +1,144 @@
/**
* @file lv_image_decoder_private.h
*
*/
#ifndef LV_IMAGE_DECODER_PRIVATE_H
#define LV_IMAGE_DECODER_PRIVATE_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "lv_image_decoder.h"
#include "../misc/cache/lv_cache.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**
* Image decoder args.
* It determines how to decoder an image, e.g. whether to premultiply the alpha or not.
* It should be passed to lv_img_decoder_open() function. If NULL is provided, default
* args are used.
*
* Default args:
* all field are zero or false.
*/
struct _lv_image_decoder_args_t {
bool stride_align; /**< Whether stride should be aligned */
bool premultiply; /**< Whether image should be premultiplied or not after decoding */
bool no_cache; /**< When set, decoded image won't be put to cache, and decoder open will also ignore cache. */
bool use_indexed; /**< Decoded indexed image as is. Convert to ARGB8888 if false. */
bool flush_cache; /**< Whether to flush the data cache after decoding */
};
struct _lv_image_decoder_t {
lv_image_decoder_info_f_t info_cb;
lv_image_decoder_open_f_t open_cb;
lv_image_decoder_get_area_cb_t get_area_cb;
lv_image_decoder_close_f_t close_cb;
lv_image_decoder_custom_draw_t custom_draw_cb;
const char * name;
void * user_data;
};
struct _lv_image_cache_data_t {
lv_cache_slot_size_t slot;
const void * src;
lv_image_src_t src_type;
const lv_draw_buf_t * decoded;
const lv_image_decoder_t * decoder;
void * user_data;
};
struct _lv_image_header_cache_data_t {
const void * src;
lv_image_src_t src_type;
lv_image_header_t header;
lv_image_decoder_t * decoder;
};
/**Describe an image decoding session. Stores data about the decoding*/
struct _lv_image_decoder_dsc_t {
/**The decoder which was able to open the image source*/
lv_image_decoder_t * decoder;
/**A copy of parameters of how this image is decoded*/
lv_image_decoder_args_t args;
/**The image source. A file path like "S:my_img.png" or pointer to an `lv_image_dsc_t` variable*/
const void * src;
/**Type of the source: file or variable. Can be set in `open` function if required*/
lv_image_src_t src_type;
lv_fs_file_t file;
/**Info about the opened image: color format, size, etc. MUST be set in `open` function*/
lv_image_header_t header;
/** Pointer to a draw buffer where the image's data (pixels) are stored in a decoded, plain format.
* MUST be set in `open` or `get_area_cb`function*/
const lv_draw_buf_t * decoded;
const lv_color32_t * palette;
uint32_t palette_size;
/** How much time did it take to open the image. [ms]
* If not set `lv_image_cache` will measure and set the time to open*/
uint32_t time_to_open;
/**A text to display instead of the image when the image can't be opened.
* Can be set in `open` function or set NULL.*/
const char * error_msg;
lv_cache_t * cache;
/**Point to cache entry information*/
lv_cache_entry_t * cache_entry;
/**Store any custom data here is required*/
void * user_data;
};
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Initialize the image decoder module
* @param image_cache_size Image cache size in bytes. 0 to disable cache.
* @param image_header_count Number of header cache entries. 0 to disable header cache.
*/
void lv_image_decoder_init(uint32_t image_cache_size, uint32_t image_header_count);
/**
* Deinitialize the image decoder module
*/
void lv_image_decoder_deinit(void);
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_IMAGE_DECODER_PRIVATE_H*/
@@ -0,0 +1,147 @@
/**
* @file lv_image_dsc.h
*
*/
#ifndef LV_IMAGE_DSC_H
#define LV_IMAGE_DSC_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../lv_conf_internal.h"
/*********************
* DEFINES
*********************/
/** Magic number for lvgl image, 9 means lvgl version 9
* It must be neither a valid ASCII character nor larger than 0x80. See `lv_image_src_get_type`.
*/
#define LV_IMAGE_HEADER_MAGIC (0x19)
LV_EXPORT_CONST_INT(LV_IMAGE_HEADER_MAGIC);
/**********************
* TYPEDEFS
**********************/
typedef enum _lvimage_flags_t {
/**
* For RGB map of the image data, mark if it's pre-multiplied with alpha.
* For indexed image, this bit indicated palette data is pre-multiplied with alpha.
*/
LV_IMAGE_FLAGS_PREMULTIPLIED = 0x0001,
/**
* The image data is compressed, so decoder needs to decode image firstly.
* If this flag is set, the whole image will be decompressed upon decode, and
* `get_area_cb` won't be necessary.
*/
LV_IMAGE_FLAGS_COMPRESSED = 0x0008,
/*Below flags are applicable only for draw buffer header.*/
/**
* The image is allocated from heap, thus should be freed after use.
*/
LV_IMAGE_FLAGS_ALLOCATED = 0x0010,
/**
* If the image data is malloced and can be processed in place.
* In image decoder post processing, this flag means we modify it in-place.
*/
LV_IMAGE_FLAGS_MODIFIABLE = 0x0020,
/**
* The image has custom drawing methods.
*/
LV_IMAGE_FLAGS_CUSTOM_DRAW = 0x0040,
/**
* Flags reserved for user, lvgl won't use these bits.
*/
LV_IMAGE_FLAGS_USER1 = 0x0100,
LV_IMAGE_FLAGS_USER2 = 0x0200,
LV_IMAGE_FLAGS_USER3 = 0x0400,
LV_IMAGE_FLAGS_USER4 = 0x0800,
LV_IMAGE_FLAGS_USER5 = 0x1000,
LV_IMAGE_FLAGS_USER6 = 0x2000,
LV_IMAGE_FLAGS_USER7 = 0x4000,
LV_IMAGE_FLAGS_USER8 = 0x8000,
} lv_image_flags_t;
typedef enum {
LV_IMAGE_COMPRESS_NONE = 0,
LV_IMAGE_COMPRESS_RLE, /**< LVGL custom RLE compression */
LV_IMAGE_COMPRESS_LZ4,
} lv_image_compress_t;
#if LV_BIG_ENDIAN_SYSTEM
typedef struct {
uint32_t reserved_2: 16; /**< Reserved to be used later*/
uint32_t stride: 16; /**< Number of bytes in a row*/
uint32_t h: 16;
uint32_t w: 16;
uint32_t flags: 16; /**< Image flags, see `lv_image_flags_t`*/
uint32_t cf : 8; /**< Color format: See `lv_color_format_t`*/
uint32_t magic: 8; /**< Magic number. Must be LV_IMAGE_HEADER_MAGIC*/
} lv_image_header_t;
#else
typedef struct {
uint32_t magic: 8; /**< Magic number. Must be LV_IMAGE_HEADER_MAGIC*/
uint32_t cf : 8; /**< Color format: See `lv_color_format_t`*/
uint32_t flags: 16; /**< Image flags, see `lv_image_flags_t`*/
uint32_t w: 16;
uint32_t h: 16;
uint32_t stride: 16; /**< Number of bytes in a row*/
uint32_t reserved_2: 16; /**< Reserved to be used later*/
} lv_image_header_t;
#endif
typedef struct {
void * buf;
uint32_t stride; /**< Number of bytes in a row*/
} lv_yuv_plane_t;
typedef union {
lv_yuv_plane_t yuv; /**< packed format*/
struct {
lv_yuv_plane_t y;
lv_yuv_plane_t u;
lv_yuv_plane_t v;
} planar; /**< planar format with 3 plane*/
struct {
lv_yuv_plane_t y;
lv_yuv_plane_t uv;
} semi_planar; /**< planar format with 2 plane*/
} lv_yuv_buf_t;
/**
* Struct to describe a constant image resource.
* It's similar to lv_draw_buf_t, but the data is constant.
*/
typedef struct {
lv_image_header_t header; /**< A header describing the basics of the image*/
uint32_t data_size; /**< Size of the image in bytes*/
const uint8_t * data; /**< Pointer to the data of the image*/
const void * reserved; /**< A reserved field to make it has same size as lv_draw_buf_t*/
const void * reserved_2; /**< A reserved field to make it has same size as lv_draw_buf_t*/
} lv_image_dsc_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_IMAGE_DSC_H*/
@@ -0,0 +1,405 @@
/**
* MIT License
*
* -----------------------------------------------------------------------------
* Copyright (c) 2008-24 Think Silicon Single Member PC
* -----------------------------------------------------------------------------
*
* 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 (including the next paragraph)
* 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.
*
*/
/**
* @file lv_draw_nema_gfx.c
*
*/
/*********************
* INCLUDES
*********************/
#include "../../core/lv_refr.h"
#if LV_USE_NEMA_GFX
#include "lv_draw_nema_gfx.h"
#include "../../font/lv_font.h"
#include "../../font/lv_font_fmt_txt.h"
/*********************
* DEFINES
*********************/
#define DRAW_UNIT_ID_NEMA_GFX 7
/**********************
* TYPEDEFS
**********************/
#if LV_USE_OS
/**
* Structure of pending nema_gfx draw task
*/
typedef struct _nema_gfx_draw_task_t {
lv_draw_task_t * task;
bool flushed;
} nema_gfx_draw_task_t;
#endif
/**********************
* STATIC PROTOTYPES
**********************/
#if LV_USE_OS
static void nema_gfx_render_thread_cb(void * ptr);
#endif
static void nema_gfx_execute_drawing(lv_draw_nema_gfx_unit_t * u);
static int32_t nema_gfx_dispatch(lv_draw_unit_t * draw_unit, lv_layer_t * layer);
static int32_t nema_gfx_evaluate(lv_draw_unit_t * draw_unit, lv_draw_task_t * task);
static int32_t nema_gfx_delete(lv_draw_unit_t * draw_unit);
static int32_t nema_gfx_wait_for_finish(lv_draw_unit_t * draw_unit);
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
void lv_draw_nema_gfx_init(void)
{
lv_draw_nema_gfx_unit_t * draw_nema_gfx_unit = lv_draw_create_unit(sizeof(lv_draw_nema_gfx_unit_t));
/*Initialize NemaGFX*/
nema_init();
draw_nema_gfx_unit->base_unit.dispatch_cb = nema_gfx_dispatch;
draw_nema_gfx_unit->base_unit.evaluate_cb = nema_gfx_evaluate;
draw_nema_gfx_unit->base_unit.delete_cb = nema_gfx_delete;
draw_nema_gfx_unit->base_unit.wait_for_finish_cb = nema_gfx_wait_for_finish;
draw_nema_gfx_unit->base_unit.name = "NEMA_GFX";
#if LV_USE_NEMA_VG
/*Initialize NemaVG */
nema_vg_init(LV_NEMA_GFX_MAX_RESX, LV_NEMA_GFX_MAX_RESY);
/* Allocate VG Buffers*/
draw_nema_gfx_unit->paint = nema_vg_paint_create();
draw_nema_gfx_unit->gradient = nema_vg_grad_create();
draw_nema_gfx_unit->path = nema_vg_path_create();
/*Initialize Freetype Support*/
lv_draw_nema_gfx_label_init(&(draw_nema_gfx_unit->base_unit));
#endif
/*Create GPU Command List*/
draw_nema_gfx_unit->cl = nema_cl_create();
/*Bind Command List*/
nema_cl_bind_circular(&(draw_nema_gfx_unit->cl));
#if LV_USE_OS
lv_thread_init(&draw_nema_gfx_unit->thread, "nemagfx", LV_DRAW_THREAD_PRIO, nema_gfx_render_thread_cb, 2 * 1024,
draw_nema_gfx_unit);
#endif
}
void lv_draw_nema_gfx_deinit(void)
{
return;
}
/**********************
* STATIC FUNCTIONS
**********************/
static int32_t nema_gfx_wait_for_finish(lv_draw_unit_t * draw_unit)
{
lv_draw_nema_gfx_unit_t * draw_nema_gfx_unit = (lv_draw_nema_gfx_unit_t *)draw_unit;
nema_cl_submit(&(draw_nema_gfx_unit->cl));
nema_cl_wait(&(draw_nema_gfx_unit->cl));
return 1;
}
static int32_t nema_gfx_evaluate(lv_draw_unit_t * draw_unit, lv_draw_task_t * task)
{
lv_draw_nema_gfx_unit_t * draw_nema_gfx_unit = (lv_draw_nema_gfx_unit_t *)draw_unit;
switch(task->type) {
case LV_DRAW_TASK_TYPE_LAYER: {
if(task->preference_score > 80) {
task->preference_score = 80;
task->preferred_draw_unit_id = DRAW_UNIT_ID_NEMA_GFX;
}
return 1;
}
#if LV_USE_NEMA_VG
case LV_DRAW_TASK_TYPE_TRIANGLE:
case LV_DRAW_TASK_TYPE_ARC:
case LV_DRAW_TASK_TYPE_FILL: {
if(task->preference_score > 80) {
task->preference_score = 80;
task->preferred_draw_unit_id = DRAW_UNIT_ID_NEMA_GFX;
}
return 1;
}
#else
case LV_DRAW_TASK_TYPE_FILL: {
lv_draw_fill_dsc_t * draw_fill_dsc = (lv_draw_fill_dsc_t *) task->draw_dsc;
if((draw_fill_dsc->grad.dir == (lv_grad_dir_t)LV_GRAD_DIR_NONE)) {
if(task->preference_score > 80) {
task->preference_score = 80;
task->preferred_draw_unit_id = DRAW_UNIT_ID_NEMA_GFX;
}
return 1;
}
break;
}
case LV_DRAW_TASK_TYPE_TRIANGLE: {
lv_draw_triangle_dsc_t * draw_triangle_dsc = (lv_draw_triangle_dsc_t *) task->draw_dsc;
if((draw_triangle_dsc->grad.dir == (lv_grad_dir_t)LV_GRAD_DIR_NONE)) {
if(task->preference_score > 80) {
task->preference_score = 80;
task->preferred_draw_unit_id = DRAW_UNIT_ID_NEMA_GFX;
}
return 1;
}
break;
}
#endif
case LV_DRAW_TASK_TYPE_IMAGE: {
lv_draw_image_dsc_t * draw_image_dsc = (lv_draw_image_dsc_t *) task->draw_dsc;
/*Guard for previous NemaGFX Version*/
#ifndef NEMA_BLOP_RECOLOR
if(draw_image_dsc->recolor_opa > LV_OPA_MIN)
break;
#endif
const lv_image_dsc_t * img_dsc = draw_image_dsc->src;
if(!lv_nemagfx_is_cf_supported(img_dsc->header.cf))
break;
if(draw_image_dsc->blend_mode != LV_BLEND_MODE_SUBTRACTIVE) {
if(task->preference_score > 80) {
task->preference_score = 80;
task->preferred_draw_unit_id = DRAW_UNIT_ID_NEMA_GFX;
}
return 1;
}
break;
}
case LV_DRAW_TASK_TYPE_LABEL: {
lv_draw_label_dsc_t * draw_label_dsc = (lv_draw_label_dsc_t *) task->draw_dsc;
lv_font_fmt_txt_dsc_t * fdsc = (lv_font_fmt_txt_dsc_t *)(draw_label_dsc->font->dsc);
if(fdsc->bitmap_format != LV_FONT_FMT_TXT_COMPRESSED) {
if(task->preference_score > 80) {
task->preference_score = 80;
task->preferred_draw_unit_id = DRAW_UNIT_ID_NEMA_GFX;
}
return 1;
}
break;
}
case LV_DRAW_TASK_TYPE_LINE: {
lv_draw_line_dsc_t * draw_line_dsc = (lv_draw_line_dsc_t *) task->draw_dsc;
bool is_dashed = (draw_line_dsc->dash_width && draw_line_dsc->dash_gap);
if(!is_dashed && !(draw_line_dsc->round_end || draw_line_dsc->round_start)) {
if(task->preference_score > 80) {
task->preference_score = 80;
task->preferred_draw_unit_id = DRAW_UNIT_ID_NEMA_GFX;
}
return 1;
}
break;
}
case LV_DRAW_TASK_TYPE_BORDER: {
const lv_draw_border_dsc_t * draw_dsc = (lv_draw_border_dsc_t *) task->draw_dsc;
if((!(draw_dsc->side != (lv_border_side_t)LV_BORDER_SIDE_FULL && draw_dsc->radius > 0)) &&
(draw_dsc->radius > draw_dsc->width)) {
if(task->preference_score > 80) {
task->preference_score = 80;
task->preferred_draw_unit_id = DRAW_UNIT_ID_NEMA_GFX;
}
return 1;
}
break;
}
case LV_DRAW_TASK_TYPE_BOX_SHADOW:
case LV_DRAW_TASK_TYPE_MASK_RECTANGLE:
case LV_DRAW_TASK_TYPE_MASK_BITMAP:
#if LV_USE_VECTOR_GRAPHIC
case LV_DRAW_TASK_TYPE_VECTOR:
#endif
default:
break;
}
nema_cl_wait(&(draw_nema_gfx_unit->cl));
return 0;
}
static int32_t nema_gfx_dispatch(lv_draw_unit_t * draw_unit, lv_layer_t * layer)
{
lv_draw_nema_gfx_unit_t * draw_nema_gfx_unit = (lv_draw_nema_gfx_unit_t *) draw_unit;
/* Return immediately if it's busy with draw task. */
if(draw_nema_gfx_unit->task_act)
return 0;
/* Try to get an ready to draw. */
lv_draw_task_t * t = lv_draw_get_available_task(layer, NULL, DRAW_UNIT_ID_NEMA_GFX);
/* Return 0 is no selection, some tasks can be supported by other units. */
if(t == NULL || t->preferred_draw_unit_id != DRAW_UNIT_ID_NEMA_GFX)
return LV_DRAW_UNIT_IDLE;
void * buf = lv_draw_layer_alloc_buf(layer);
if(buf == NULL)
return LV_DRAW_UNIT_IDLE;
t->state = LV_DRAW_TASK_STATE_IN_PROGRESS;
draw_nema_gfx_unit->task_act = t;
#if LV_USE_OS
/* Let the render thread work. */
if(draw_nema_gfx_unit->inited)
lv_thread_sync_signal(&draw_nema_gfx_unit->sync);
#else
nema_gfx_execute_drawing(draw_nema_gfx_unit);
draw_nema_gfx_unit->task_act->state = LV_DRAW_TASK_STATE_READY;
draw_nema_gfx_unit->task_act = NULL;
/* The draw unit is free now. Request a new dispatching as it can get a new task. */
lv_draw_dispatch_request();
#endif
return 1;
}
static void nema_gfx_execute_drawing(lv_draw_nema_gfx_unit_t * u)
{
lv_draw_task_t * t = u->task_act;
/* remember draw unit for access to unit's context */
t->draw_unit = (lv_draw_unit_t *)u;
switch(t->type) {
case LV_DRAW_TASK_TYPE_FILL:
lv_draw_nema_gfx_fill(t, t->draw_dsc, &t->area);
break;
case LV_DRAW_TASK_TYPE_IMAGE:
lv_draw_nema_gfx_img(t, t->draw_dsc, &t->area);
break;
case LV_DRAW_TASK_TYPE_TRIANGLE:
lv_draw_nema_gfx_triangle(t, t->draw_dsc);
break;
case LV_DRAW_TASK_TYPE_LABEL:
lv_draw_nema_gfx_label(t, t->draw_dsc, &t->area);
break;
case LV_DRAW_TASK_TYPE_LAYER:
lv_draw_nema_gfx_layer(t, t->draw_dsc, &t->area);
break;
case LV_DRAW_TASK_TYPE_LINE:
lv_draw_nema_gfx_line(t, t->draw_dsc);
break;
#if LV_USE_NEMA_VG
case LV_DRAW_TASK_TYPE_ARC:
lv_draw_nema_gfx_arc(t, t->draw_dsc, &t->area);
break;
#endif
case LV_DRAW_TASK_TYPE_BORDER:
lv_draw_nema_gfx_border(t, t->draw_dsc, &t->area);
break;
default:
break;
}
}
static int32_t nema_gfx_delete(lv_draw_unit_t * draw_unit)
{
#if LV_USE_NEMA_VG
/*Free VG Buffers*/
lv_draw_nema_gfx_unit_t * draw_nema_gfx_unit = (lv_draw_nema_gfx_unit_t *) draw_unit;
nema_vg_paint_destroy(draw_nema_gfx_unit->paint);
nema_vg_path_destroy(draw_nema_gfx_unit->path);
nema_vg_grad_destroy(draw_nema_gfx_unit->gradient);
#endif
#if LV_USE_OS
lv_draw_nema_gfx_unit_t * _draw_nema_gfx_unit = (lv_draw_nema_gfx_unit_t *) draw_unit;
LV_LOG_INFO("Cancel NemaGFX draw thread.");
_draw_nema_gfx_unit->exit_status = true;
if(_draw_nema_gfx_unit->inited)
lv_thread_sync_signal(&_draw_nema_gfx_unit->sync);
lv_result_t res = lv_thread_delete(&_draw_nema_gfx_unit->thread);
return res;
#endif
#if LV_USE_NEMA_VG == 0 && LV_USE_OS == LV_OS_NONE
LV_UNUSED(draw_unit);
#endif
return 0;
}
#if LV_USE_OS
static void nema_gfx_render_thread_cb(void * ptr)
{
lv_draw_nema_gfx_unit_t * u = ptr;
lv_thread_sync_init(&u->sync);
u->inited = true;
while(1) {
/* Wait for sync if there is no task set. */
while(u->task_act == NULL) {
if(u->exit_status)
break;
lv_thread_sync_wait(&u->sync);
}
if(u->exit_status) {
LV_LOG_INFO("Ready to exit NemaGFX draw thread.");
break;
}
if(u->task_act) {
nema_gfx_execute_drawing(u);
}
/* Signal the ready state to dispatcher. */
u->task_act->state = LV_DRAW_TASK_STATE_READY;
/* Cleanup. */
u->task_act = NULL;
/* The draw unit is free now. Request a new dispatching as it can get a new task. */
lv_draw_dispatch_request();
}
u->inited = false;
lv_thread_sync_delete(&u->sync);
LV_LOG_INFO("Exit NemaGFX draw thread.");
}
#endif
#endif
@@ -0,0 +1,124 @@
/**
* MIT License
*
* -----------------------------------------------------------------------------
* Copyright (c) 2008-24 Think Silicon Single Member PC
* -----------------------------------------------------------------------------
*
* 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 (including the next paragraph)
* 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.
*
*/
/**
* @file lv_draw_nema_gfx.h
*
*/
#ifndef LV_DRAW_NEMA_GFX_H
#define LV_DRAW_NEMA_GFX_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../../lv_conf_internal.h"
#if LV_USE_NEMA_GFX
#include "lv_draw_nema_gfx_utils.h"
#include "../lv_draw_private.h"
#include "../lv_draw_buf_private.h"
#include "../lv_draw_image_private.h"
#include "../lv_image_decoder_private.h"
#include "../lv_draw_label_private.h"
#include "../lv_draw_mask_private.h"
#include "../lv_draw_rect_private.h"
#include "../lv_draw_triangle_private.h"
#include "../lv_draw_vector_private.h"
#include "../../misc/lv_area_private.h"
/**********************
* TYPEDEFS
**********************/
typedef struct {
lv_draw_unit_t base_unit;
lv_draw_task_t * task_act;
#if LV_USE_OS
lv_thread_sync_t sync;
lv_thread_t thread;
volatile bool inited;
volatile bool exit_status;
#endif
uint32_t idx;
nema_cmdlist_t cl;
#if LV_USE_NEMA_VG
NEMA_VG_PAINT_HANDLE paint;
NEMA_VG_GRAD_HANDLE gradient;
NEMA_VG_PATH_HANDLE path;
#endif
} lv_draw_nema_gfx_unit_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
void lv_draw_nema_gfx_init(void);
void lv_draw_nema_gfx_deinit(void);
void lv_draw_nema_gfx_fill(lv_draw_task_t * t,
const lv_draw_fill_dsc_t * dsc, const lv_area_t * coords);
void lv_draw_nema_gfx_triangle(lv_draw_task_t * t, const lv_draw_triangle_dsc_t * dsc);
void lv_draw_nema_gfx_img(lv_draw_task_t * t, const lv_draw_image_dsc_t * dsc,
const lv_area_t * coords);
void lv_draw_nema_gfx_label(lv_draw_task_t * t, const lv_draw_label_dsc_t * dsc,
const lv_area_t * coords);
void lv_draw_nema_gfx_label_init(lv_draw_unit_t * draw_unit);
void lv_draw_nema_gfx_layer(lv_draw_task_t * t, const lv_draw_image_dsc_t * draw_dsc,
const lv_area_t * coords);
void lv_draw_nema_gfx_line(lv_draw_task_t * t, const lv_draw_line_dsc_t * dsc);
void lv_draw_nema_gfx_border(lv_draw_task_t * t, const lv_draw_border_dsc_t * dsc,
const lv_area_t * coords);
void lv_draw_nema_gfx_arc(lv_draw_task_t * t, const lv_draw_arc_dsc_t * dsc,
const lv_area_t * coords);
/**********************
* MACROS
**********************/
#endif /*LV_USE_NEMA_GFX*/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_DRAW_NEMA_GFX_H*/
@@ -0,0 +1,105 @@
/**
* MIT License
*
* -----------------------------------------------------------------------------
* Copyright (c) 2008-24 Think Silicon Single Member PC
* -----------------------------------------------------------------------------
*
* 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 (including the next paragraph)
* 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.
*
*/
/**
* @file lv_draw_nema_gfx_arc.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_draw_nema_gfx.h"
#if LV_USE_NEMA_GFX && LV_USE_NEMA_VG
/**********************
* GLOBAL FUNCTIONS
**********************/
void lv_draw_nema_gfx_arc(lv_draw_task_t * t, const lv_draw_arc_dsc_t * dsc, const lv_area_t * coords)
{
LV_UNUSED(coords);
if(dsc->opa <= (lv_opa_t)LV_OPA_MIN)
return;
if(dsc->width == 0)
return;
if(dsc->start_angle == dsc->end_angle)
return;
lv_draw_nema_gfx_unit_t * draw_nema_gfx_unit = (lv_draw_nema_gfx_unit_t *)t->draw_unit;
lv_layer_t * layer = t->target_layer;
lv_point_t center = {dsc->center.x - layer->buf_area.x1, dsc->center.y - layer->buf_area.y1};
lv_area_t clip_area;
lv_area_copy(&clip_area, &t->clip_area);
lv_area_move(&clip_area, -layer->buf_area.x1, -layer->buf_area.y1);
nema_set_clip(clip_area.x1, clip_area.y1, lv_area_get_width(&clip_area), lv_area_get_height(&clip_area));
lv_value_precise_t start_angle = dsc->start_angle;
lv_value_precise_t end_angle = dsc->end_angle;
if(start_angle >= end_angle) {
end_angle += 360.0f;
}
if(end_angle - start_angle > 360.0f) {
start_angle = 0.0f;
end_angle = 360.0f;
}
else {
while(end_angle > 360.0f) {
start_angle -= 360.0f;
end_angle -= 360.0f;
}
}
nema_vg_paint_clear(draw_nema_gfx_unit->paint);
nema_vg_paint_set_type(draw_nema_gfx_unit->paint, NEMA_VG_PAINT_COLOR);
lv_color32_t col32 = lv_color_to_32(dsc->color, dsc->opa);
uint32_t bg_color = nema_rgba(col32.red, col32.green, col32.blue, col32.alpha);
nema_vg_paint_set_paint_color(draw_nema_gfx_unit->paint, bg_color); // green
nema_vg_paint_set_stroke_width(draw_nema_gfx_unit->paint, dsc->width);
nema_vg_set_blend(NEMA_BL_SRC_OVER | NEMA_BLOP_SRC_PREMULT);
if(dsc->rounded == 1) {
nema_vg_draw_ring(center.x, center.y, (float)dsc->radius - (float)dsc->width * 0.5f, start_angle, end_angle,
draw_nema_gfx_unit->paint);
}
else {
nema_vg_draw_ring_generic(center.x, center.y, (float)dsc->radius - (float)dsc->width * 0.5f, start_angle,
end_angle, draw_nema_gfx_unit->paint, 0U);
}
nema_cl_submit(&(draw_nema_gfx_unit->cl));
}
#endif
@@ -0,0 +1,226 @@
/**
* MIT License
*
* -----------------------------------------------------------------------------
* Copyright (c) 2008-24 Think Silicon Single Member PC
* -----------------------------------------------------------------------------
*
* 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 (including the next paragraph)
* 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.
*
*/
/**
* @file lv_draw_nema_gfx_fill.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_draw_nema_gfx.h"
#if LV_USE_NEMA_GFX
#include <math.h>
/**********************
* GLOBAL FUNCTIONS
**********************/
void lv_draw_nema_gfx_border(lv_draw_task_t * t, const lv_draw_border_dsc_t * dsc, const lv_area_t * coords)
{
if(dsc->opa <= LV_OPA_MIN)
return;
if(dsc->width == 0)
return;
if(dsc->side == LV_BORDER_SIDE_NONE)
return;
lv_draw_nema_gfx_unit_t * draw_nema_gfx_unit = (lv_draw_nema_gfx_unit_t *)t->draw_unit;
lv_layer_t * layer = t->target_layer;
lv_area_t inward_coords;
int32_t width = dsc->width;
/* Move border inwards to align with software rendered border */
inward_coords.x1 = coords->x1 + ceil(width / 2.0f);
inward_coords.x2 = coords->x2 - floor(width / 2.0f);
inward_coords.y1 = coords->y1 + ceil(width / 2.0f);
inward_coords.y2 = coords->y2 - floor(width / 2.0f);
lv_area_move(&inward_coords, -layer->buf_area.x1, -layer->buf_area.y1);
lv_area_t clip_area;
lv_area_copy(&clip_area, &t->clip_area);
lv_area_move(&clip_area, -layer->buf_area.x1, -layer->buf_area.y1);
nema_set_clip(clip_area.x1, clip_area.y1, lv_area_get_width(&clip_area), lv_area_get_height(&clip_area));
lv_area_t clipped_coords;
if(!lv_area_intersect(&clipped_coords, &inward_coords, &clip_area))
return; /*Fully clipped, nothing to do*/
lv_color_format_t dst_cf = layer->draw_buf->header.cf;
uint32_t dst_nema_cf = lv_nemagfx_cf_to_nema(dst_cf);
/* the stride should be computed internally for NEMA_TSC images and images missing a stride value */
int32_t stride = (dst_cf >= LV_COLOR_FORMAT_NEMA_TSC_START && dst_cf <= LV_COLOR_FORMAT_NEMA_TSC_END) ?
-1 : lv_area_get_width(&(layer->buf_area)) * lv_color_format_get_size(dst_cf);
nema_bind_dst_tex((uintptr_t)NEMA_VIRT2PHYS(layer->draw_buf->data), lv_area_get_width(&(layer->buf_area)),
lv_area_get_height(&(layer->buf_area)), dst_nema_cf, stride);
/* Recalculate float Dimensions */
float x1 = (float)coords->x1 + ((float)width / 2.0f) - (float)layer->buf_area.x1;
float x2 = (float)coords->x2 - ((float)width / 2.0f) - (float)layer->buf_area.x1;
float y1 = (float)coords->y1 + ((float)width / 2.0f) - (float)layer->buf_area.y1;
float y2 = (float)coords->y2 - ((float)width / 2.0f) - (float)layer->buf_area.y1;
float coords_bg_w = x2 - x1 + 1;
float coords_bg_h = y2 - y1 + 1;
int32_t short_side = LV_MIN(coords_bg_w, coords_bg_h);
float radius = (float)LV_MIN(dsc->radius, short_side >> 1);
lv_color32_t col32 = lv_color_to_32(dsc->color, dsc->opa);
uint32_t bg_color = nema_rgba(col32.red, col32.green, col32.blue, col32.alpha);
if(col32.alpha < 255U) {
nema_set_blend_fill(NEMA_BL_SRC_OVER);
bg_color = nema_premultiply_rgba(bg_color);
}
else {
nema_set_blend_fill(NEMA_BL_SRC);
}
if(radius > 0.0f) {
nema_draw_rounded_rect_aa(x1, y1, coords_bg_w, coords_bg_h, radius, width, bg_color);
}
else {
lv_area_t rect_coords = *coords;
lv_area_move(&rect_coords, -layer->buf_area.x1, -layer->buf_area.y1);
int32_t border_width = lv_area_get_width(&rect_coords);
int32_t border_height = lv_area_get_height(&rect_coords);
if(dsc->side & LV_BORDER_SIDE_TOP) {
float x = rect_coords.x1 + width;
float y = rect_coords.y1;
float w = border_width - 2 * width;
float h = width;
nema_enable_aa(1, 0, 1, 0);
nema_fill_rect_f(x, y, w, h, bg_color);
}
if(dsc->side & LV_BORDER_SIDE_BOTTOM) {
float x = rect_coords.x1 + width;
float y = rect_coords.y1 + border_height - width;
float w = border_width - 2 * width;
float h = width;
nema_enable_aa(1, 0, 1, 0);
nema_fill_rect_f(x, y, w, h, bg_color);
}
if(dsc->side & LV_BORDER_SIDE_LEFT) {
float x = rect_coords.x1;
float y = rect_coords.y1 + width;
float w = width;
float h = border_height - 2 * width;
nema_enable_aa(0, 1, 0, 1);
nema_fill_rect_f(x, y, w, h, bg_color);
}
if(dsc->side & LV_BORDER_SIDE_RIGHT) {
float x = rect_coords.x1 + border_width - width;
float y = rect_coords.y1 + width;
float w = width;
float h = border_height - 2 * width;
nema_enable_aa(0, 1, 0, 1);
nema_fill_rect_f(x, y, w, h, bg_color);
}
/*Draw small corner rectangles
Top Left*/
if(dsc->side & LV_BORDER_SIDE_TOP || dsc->side & LV_BORDER_SIDE_LEFT) {
float x = rect_coords.x1;
float y = rect_coords.y1;
float w = width;
float h = width;
if(!(dsc->side & LV_BORDER_SIDE_TOP))
nema_enable_aa(1, 1, 0, 1);
else if(!(dsc->side & LV_BORDER_SIDE_LEFT))
nema_enable_aa(1, 0, 1, 1);
else
nema_enable_aa(1, 0, 0, 1);
nema_fill_rect_f(x, y, w, h, bg_color);
}
/*Top Right*/
if(dsc->side & LV_BORDER_SIDE_TOP || dsc->side & LV_BORDER_SIDE_RIGHT) {
float x = rect_coords.x1 + border_width - width;
float y = rect_coords.y1;
float w = width;
float h = width;
if(!(dsc->side & LV_BORDER_SIDE_TOP))
nema_enable_aa(1, 1, 0, 1);
else if(!(dsc->side & LV_BORDER_SIDE_RIGHT))
nema_enable_aa(1, 1, 1, 0);
else
nema_enable_aa(1, 1, 0, 0);
nema_fill_rect_f(x, y, w, h, bg_color);
}
/*Bottom Right*/
if(dsc->side & LV_BORDER_SIDE_BOTTOM || dsc->side & LV_BORDER_SIDE_RIGHT) {
float x = rect_coords.x1 + border_width - width;
float y = rect_coords.y1 + border_height - width;
float w = width;
float h = width;
if(!(dsc->side & LV_BORDER_SIDE_BOTTOM))
nema_enable_aa(0, 1, 1, 1);
else if(!(dsc->side & LV_BORDER_SIDE_RIGHT))
nema_enable_aa(1, 1, 1, 0);
else
nema_enable_aa(0, 1, 1, 0);
nema_fill_rect_f(x, y, w, h, bg_color);
}
/*Bottom Left*/
if(dsc->side & LV_BORDER_SIDE_BOTTOM || dsc->side & LV_BORDER_SIDE_LEFT) {
float x = rect_coords.x1;
float y = rect_coords.y1 + border_height - width;
float w = width;
float h = width;
if(!(dsc->side & LV_BORDER_SIDE_BOTTOM))
nema_enable_aa(0, 1, 1, 1);
else if(!(dsc->side & LV_BORDER_SIDE_LEFT))
nema_enable_aa(1, 0, 1, 1);
else
nema_enable_aa(0, 0, 1, 1);
nema_fill_rect_f(x, y, w, h, bg_color);
}
}
nema_cl_submit(&(draw_nema_gfx_unit->cl));
}
#endif
@@ -0,0 +1,149 @@
/**
* MIT License
*
* -----------------------------------------------------------------------------
* Copyright (c) 2008-24 Think Silicon Single Member PC
* -----------------------------------------------------------------------------
*
* 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 (including the next paragraph)
* 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.
*
*/
/**
* @file lv_draw_nema_gfx_fill.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_draw_nema_gfx.h"
#if LV_USE_NEMA_GFX
/**********************
* GLOBAL FUNCTIONS
**********************/
void lv_draw_nema_gfx_fill(lv_draw_task_t * t, const lv_draw_fill_dsc_t * dsc, const lv_area_t * coords)
{
if(dsc->opa <= LV_OPA_MIN) return;
lv_draw_nema_gfx_unit_t * draw_nema_gfx_unit = (lv_draw_nema_gfx_unit_t *)t->draw_unit;
lv_layer_t * layer = t->target_layer;
lv_area_t rel_coords;
lv_area_copy(&rel_coords, coords);
lv_area_move(&rel_coords, -layer->buf_area.x1, -layer->buf_area.y1);
lv_area_t rel_clip_area;
lv_area_copy(&rel_clip_area, &t->clip_area);
lv_area_move(&rel_clip_area, -layer->buf_area.x1, -layer->buf_area.y1);
nema_set_clip(rel_clip_area.x1, rel_clip_area.y1, lv_area_get_width(&rel_clip_area),
lv_area_get_height(&rel_clip_area));
lv_area_t clipped_coords;
if(!lv_area_intersect(&clipped_coords, &rel_coords, &rel_clip_area))
return; /*Fully clipped, nothing to do*/
lv_color_format_t dst_cf = layer->draw_buf->header.cf;
uint32_t dst_nema_cf = lv_nemagfx_cf_to_nema(dst_cf);
/* the stride should be computed internally for NEMA_TSC images and images missing a stride value */
int32_t stride = (dst_cf >= LV_COLOR_FORMAT_NEMA_TSC_START && dst_cf <= LV_COLOR_FORMAT_NEMA_TSC_END) ?
-1 : lv_area_get_width(&(layer->buf_area)) * lv_color_format_get_size(dst_cf);
nema_bind_dst_tex((uintptr_t)NEMA_VIRT2PHYS(layer->draw_buf->data), lv_area_get_width(&(layer->buf_area)),
lv_area_get_height(&(layer->buf_area)), dst_nema_cf, stride);
int32_t coords_bg_w = lv_area_get_width(&rel_coords);
int32_t coords_bg_h = lv_area_get_height(&rel_coords);
int32_t short_side = LV_MIN(coords_bg_w, coords_bg_h);
int32_t radius = LV_MIN(dsc->radius, short_side >> 1);
if((dsc->grad.dir == (lv_grad_dir_t)LV_GRAD_DIR_NONE)) {
lv_color32_t col32 = lv_color_to_32(dsc->color, dsc->opa);
uint32_t bg_color = nema_rgba(col32.red, col32.green, col32.blue, col32.alpha);
if(col32.alpha < 255U) {
nema_set_blend_fill(NEMA_BL_SRC_OVER);
bg_color = nema_premultiply_rgba(bg_color);
}
else {
nema_set_blend_fill(NEMA_BL_SRC);
}
if(radius > 0.f)
nema_fill_rounded_rect_aa(rel_coords.x1, rel_coords.y1, coords_bg_w, coords_bg_h, radius, bg_color);
else
nema_fill_rect(rel_coords.x1, rel_coords.y1, coords_bg_w, coords_bg_h, bg_color);
}
#if LV_USE_NEMA_VG
else {
nema_vg_paint_clear(draw_nema_gfx_unit->paint);
nema_vg_paint_set_type(draw_nema_gfx_unit->paint, NEMA_VG_PAINT_GRAD_LINEAR);
nema_vg_set_blend(NEMA_BL_SRC_OVER | NEMA_BLOP_SRC_PREMULT);
float stops[LV_GRADIENT_MAX_STOPS];
color_var_t colors[LV_GRADIENT_MAX_STOPS];
uint32_t cnt = LV_MAX(dsc->grad.stops_count, LV_GRADIENT_MAX_STOPS);
for(uint8_t i = 0; i < cnt; i++) {
stops[i] = (float)(dsc->grad.stops[i].frac) / 255.f;
colors[i].a = LV_OPA_MIX2(dsc->grad.stops[i].opa, dsc->opa);
colors[i].r = dsc->grad.stops[i].color.red;
colors[i].g = dsc->grad.stops[i].color.green;
colors[i].b = dsc->grad.stops[i].color.blue;
}
nema_vg_grad_set(draw_nema_gfx_unit->gradient, cnt, stops, colors);
float x0, y0, x1, y1;
if(dsc->grad.dir == LV_GRAD_DIR_HOR) {
x0 = rel_coords.x1;
x1 = rel_coords.x2;
y0 = rel_coords.y1;
y1 = rel_coords.y1;
}
else {
x0 = rel_coords.x1;
x1 = rel_coords.x1;
y0 = rel_coords.y1;
y1 = rel_coords.y2;
}
nema_vg_paint_set_grad_linear(draw_nema_gfx_unit->paint, draw_nema_gfx_unit->gradient, x0, y0, x1, y1,
NEMA_TEX_CLAMP | NEMA_FILTER_BL);
if(radius > 0.f)
nema_vg_draw_rounded_rect(rel_coords.x1, rel_coords.y1, coords_bg_w, coords_bg_h, radius, radius, NULL,
draw_nema_gfx_unit->paint);
else
nema_vg_draw_rect(rel_coords.x1, rel_coords.y1, coords_bg_w, coords_bg_h, NULL, draw_nema_gfx_unit->paint);
}
#endif
nema_cl_submit(&(draw_nema_gfx_unit->cl));
}
#endif
@@ -0,0 +1,283 @@
/**
* MIT License
*
* -----------------------------------------------------------------------------
* Copyright (c) 2008-24 Think Silicon Single Member PC
* -----------------------------------------------------------------------------
*
* 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 (including the next paragraph)
* 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.
*
*/
/**
* @file lv_draw_nema_gfx_fill.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_draw_nema_gfx.h"
#if LV_USE_NEMA_GFX
/**********************
* STATIC PROTOTYPES
**********************/
static void _draw_nema_gfx_tile(lv_draw_task_t * t, const lv_draw_image_dsc_t * dsc, const lv_area_t * coords);
static void _draw_nema_gfx_img(lv_draw_task_t * t, const lv_draw_image_dsc_t * dsc, const lv_area_t * coords);
static uint32_t lv_nemagfx_mask_cf_to_nema(lv_color_format_t cf);
/**********************
* STATIC FUNCTIONS
**********************/
static void _draw_nema_gfx_tile(lv_draw_task_t * t, const lv_draw_image_dsc_t * dsc, const lv_area_t * coords)
{
lv_image_decoder_dsc_t decoder_dsc;
lv_result_t res = lv_image_decoder_open(&decoder_dsc, dsc->src, NULL);
if(res != LV_RESULT_OK) {
LV_LOG_ERROR("Failed to open image");
return;
}
int32_t img_w = dsc->header.w;
int32_t img_h = dsc->header.h;
lv_area_t tile_area;
if(lv_area_get_width(&dsc->image_area) >= 0) {
tile_area = dsc->image_area;
}
else {
tile_area = *coords;
}
lv_area_set_width(&tile_area, img_w);
lv_area_set_height(&tile_area, img_h);
int32_t tile_x_start = tile_area.x1;
while(tile_area.y1 <= t->clip_area.y2) {
while(tile_area.x1 <= t->clip_area.x2) {
lv_area_t clipped_img_area;
if(lv_area_intersect(&clipped_img_area, &tile_area, &t->clip_area)) {
_draw_nema_gfx_img(t, dsc, &tile_area);
}
tile_area.x1 += img_w;
tile_area.x2 += img_w;
}
tile_area.y1 += img_h;
tile_area.y2 += img_h;
tile_area.x1 = tile_x_start;
tile_area.x2 = tile_x_start + img_w - 1;
}
lv_image_decoder_close(&decoder_dsc);
}
static void _draw_nema_gfx_img(lv_draw_task_t * t, const lv_draw_image_dsc_t * dsc, const lv_area_t * coords)
{
if(dsc->opa <= LV_OPA_MIN) return;
lv_draw_nema_gfx_unit_t * draw_nema_gfx_unit = (lv_draw_nema_gfx_unit_t *)t->draw_unit;
lv_layer_t * layer = t->target_layer;
const lv_image_dsc_t * img_dsc = dsc->src;
bool masked = dsc->bitmap_mask_src != NULL;
lv_area_t blend_area;
/*Let's get the blend area which is the intersection of the area to fill and the clip area.*/
if(!lv_area_intersect(&blend_area, coords, &t->clip_area))
return; /*Fully clipped, nothing to do*/
lv_area_t rel_clip_area;
lv_area_copy(&rel_clip_area, &t->clip_area);
lv_area_move(&rel_clip_area, -layer->buf_area.x1, -layer->buf_area.y1);
bool has_transform = (dsc->rotation != 0 || dsc->scale_x != LV_SCALE_NONE || dsc->scale_y != LV_SCALE_NONE);
bool recolor = (dsc->recolor_opa > LV_OPA_MIN);
/*Make the blend area relative to the buffer*/
lv_area_move(&blend_area, -layer->buf_area.x1, -layer->buf_area.y1);
uint32_t tex_w = lv_area_get_width(coords);
uint32_t tex_h = lv_area_get_height(coords);
nema_set_clip(rel_clip_area.x1, rel_clip_area.y1, lv_area_get_width(&rel_clip_area),
lv_area_get_height(&rel_clip_area));
lv_color_format_t dst_cf = layer->draw_buf->header.cf;
uint32_t dst_nema_cf = lv_nemagfx_cf_to_nema(dst_cf);
const void * src_buf = img_dsc->data;
uint32_t blending_mode = lv_nemagfx_blending_mode(dsc->blend_mode);
lv_color_format_t src_cf = img_dsc->header.cf;
/*Image contains Alpha*/
if(src_cf == LV_COLOR_FORMAT_ARGB8888 || src_cf == LV_COLOR_FORMAT_XRGB8888) {
blending_mode |= NEMA_BLOP_SRC_PREMULT;
}
uint32_t src_nema_cf = lv_nemagfx_cf_to_nema(src_cf);
/* the stride should be computed internally for NEMA_TSC images and images missing a stride value */
int32_t src_stride = (src_cf >= LV_COLOR_FORMAT_NEMA_TSC_START && src_cf <= LV_COLOR_FORMAT_NEMA_TSC_END)
|| img_dsc->header.stride == 0 ? -1 : (int32_t)img_dsc->header.stride;
int32_t stride = (dst_cf >= LV_COLOR_FORMAT_NEMA_TSC_START && dst_cf <= LV_COLOR_FORMAT_NEMA_TSC_END) ?
-1 : lv_area_get_width(&(layer->buf_area)) * lv_color_format_get_size(dst_cf);
nema_bind_dst_tex((uintptr_t)NEMA_VIRT2PHYS(layer->draw_buf->data), lv_area_get_width(&(layer->buf_area)),
lv_area_get_height(&(layer->buf_area)), dst_nema_cf, stride);
if(!LV_COLOR_FORMAT_IS_INDEXED(src_cf)) {
nema_bind_src_tex((uintptr_t)(src_buf), tex_w, tex_h, src_nema_cf, src_stride,
dsc->antialias ? NEMA_FILTER_BL : NEMA_FILTER_PS);
}
else {
nema_bind_lut_tex((uintptr_t)((uint8_t *)src_buf + LV_COLOR_INDEXED_PALETTE_SIZE(src_cf) * 4), tex_w, tex_h,
src_nema_cf, src_stride, NEMA_FILTER_PS, (uintptr_t)(src_buf), NEMA_BGRA8888);
blending_mode |= NEMA_BLOP_LUT;
}
/*Guard for previous NemaGFX Version*/
#ifdef NEMA_BLOP_RECOLOR
if(recolor) {
lv_color32_t col32 = lv_color_to_32(dsc->recolor, LV_OPA_MIX2(dsc->recolor_opa, dsc->opa));
uint32_t color = nema_rgba(col32.red, col32.green, col32.blue, col32.alpha);
nema_set_recolor_color(color);
blending_mode |= NEMA_BLOP_RECOLOR;
}
#endif
if(dsc->opa < 255) {
uint32_t rgba = ((uint32_t)dsc->opa << 24U) | ((uint32_t)dsc->opa << 16U) | ((uint32_t)dsc->opa << 8U) | ((
uint32_t)dsc->opa);
nema_set_const_color(rgba);
blending_mode |= NEMA_BLOP_MODULATE_A;
}
if(!has_transform && masked && !recolor) {
if(dsc->bitmap_mask_src->header.cf == LV_COLOR_FORMAT_A8 || dsc->bitmap_mask_src->header.cf == LV_COLOR_FORMAT_L8) {
blending_mode |= NEMA_BLOP_STENCIL_TXTY;
const lv_image_dsc_t * mask = dsc->bitmap_mask_src;
const void * mask_buf = mask->data;
const lv_area_t * image_area;
lv_area_t mask_area;
if(lv_area_get_width(&dsc->image_area) < 0) image_area = coords;
else image_area = &dsc->image_area;
lv_area_set(&mask_area, 0, 0, dsc->bitmap_mask_src->header.w - 1, dsc->bitmap_mask_src->header.h - 1);
lv_area_align(image_area, &mask_area, LV_ALIGN_CENTER, 0, 0);
mask_buf += dsc->bitmap_mask_src->header.w * (coords->y1 - mask_area.y1) + (coords->x1 - mask_area.x1);
nema_bind_tex(NEMA_TEX3, (uintptr_t)NEMA_VIRT2PHYS(mask_buf), mask->header.w, mask->header.h,
lv_nemagfx_mask_cf_to_nema(mask->header.cf),
mask->header.stride, NEMA_FILTER_BL);
}
}
nema_set_blend_blit(blending_mode);
if(!has_transform) {
nema_blit_rect((coords->x1 - layer->buf_area.x1),
(coords->y1 - layer->buf_area.y1), tex_w, tex_h);
}
else {
/*Calculate the transformed points*/
float x0 = (coords->x1 - layer->buf_area.x1);
float y0 = (coords->y1 - layer->buf_area.y1);
float x1 = x0 + tex_w ;
float y1 = y0;
float x2 = x0 + tex_w ;
float y2 = y0 + tex_h;
float x3 = x0 ;
float y3 = y0 + tex_h;
nema_matrix3x3_t m;
nema_mat3x3_load_identity(m);
nema_mat3x3_translate(m, -x0, -y0);
nema_mat3x3_translate(m, -(float)dsc->pivot.x, -(float)dsc->pivot.y);
nema_mat3x3_rotate(m, (dsc->rotation / 10.0f)); /* angle is 1/10 degree */
float scale_x = 1.f * dsc->scale_x / LV_SCALE_NONE;
float scale_y = 1.f * dsc->scale_y / LV_SCALE_NONE;
nema_mat3x3_scale(m, (float)scale_x, (float)scale_y);
nema_mat3x3_translate(m, (float)dsc->pivot.x, (float)dsc->pivot.y);
nema_mat3x3_translate(m, x0, y0);
/*Apply Transformation Matrix to Vertices*/
nema_mat3x3_mul_vec(m, &x0, &y0);
nema_mat3x3_mul_vec(m, &x1, &y1);
nema_mat3x3_mul_vec(m, &x2, &y2);
nema_mat3x3_mul_vec(m, &x3, &y3);
nema_blit_quad_fit(x0, y0,
x1, y1,
x2, y2,
x3, y3);
}
nema_cl_submit(&(draw_nema_gfx_unit->cl));
}
/*NemaGFX does mask operations with A8,A4,A2 and A1 formats*/
static uint32_t lv_nemagfx_mask_cf_to_nema(lv_color_format_t cf)
{
switch(cf) {
case LV_COLOR_FORMAT_A1:
return NEMA_A1;
case LV_COLOR_FORMAT_A2:
return NEMA_A2;
case LV_COLOR_FORMAT_A4:
return NEMA_A4;
case LV_COLOR_FORMAT_A8:
case LV_COLOR_FORMAT_L8:
default:
break;
}
return NEMA_A8;
}
/**********************
* GLOBAL FUNCTIONS
**********************/
void lv_draw_nema_gfx_img(lv_draw_task_t * t, const lv_draw_image_dsc_t * dsc, const lv_area_t * coords)
{
if(!dsc->tile) {
_draw_nema_gfx_img(t, dsc, coords);
}
else {
_draw_nema_gfx_tile(t, dsc, coords);
}
}
#endif
@@ -0,0 +1,850 @@
/**
* MIT License
*
* -----------------------------------------------------------------------------
* Copyright (c) 2008-24 Think Silicon Single Member PC
* -----------------------------------------------------------------------------
*
* 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 (including the next paragraph)
* 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.
*
*/
/**
* @file lv_draw_nema_gfx_fill.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_draw_nema_gfx.h"
#if LV_USE_NEMA_GFX
#include "../../misc/lv_utils.h"
#include "../../misc/lv_bidi_private.h"
#include "../../misc/lv_text_private.h"
#include "../../lvgl.h"
#include "../../libs/freetype/lv_freetype_private.h"
#include "../../core/lv_global.h"
/*********************
* DEFINES
*********************/
#define LABEL_RECOLOR_PAR_LENGTH 6
#define LV_LABEL_HINT_UPDATE_TH 1024 /*Update the "hint" if the label's y coordinates have changed more then this*/
#define FT_F26DOT6_SHIFT 6
#define NEMA_COORD_LIMIT 2046
#define font_draw_buf_handlers &(LV_GLOBAL_DEFAULT()->font_draw_buf_handlers)
/** After converting the font reference size, it is also necessary to scale the 26dot6 data
* in the path to the real physical size
*/
#define FT_F26DOT6_TO_PATH_SCALE(x) (LV_FREETYPE_F26DOT6_TO_FLOAT(x) / (1 << FT_F26DOT6_SHIFT))
/*Forward declarations*/
void nema_set_matrix(nema_matrix3x3_t m);
void nema_raster_rect(int x, int y, int w, int h);
/**********************
* TYPEDEFS
**********************/
enum {
RECOLOR_CMD_STATE_WAIT_FOR_PARAMETER,
RECOLOR_CMD_STATE_PARAMETER,
RECOLOR_CMD_STATE_TEXT_INPUT,
};
typedef unsigned char cmd_state_t;
/**********************
* STATIC PROTOTYPES
**********************/
static void _draw_nema_gfx_letter(lv_draw_task_t * t, lv_draw_glyph_dsc_t * glyph_draw_dsc,
lv_draw_fill_dsc_t * fill_draw_dsc, const lv_area_t * fill_area);
static void _draw_label_iterate_characters(lv_draw_task_t * t, const lv_draw_label_dsc_t * dsc,
const lv_area_t * coords);
static inline uint8_t _bpp_nema_gfx_format(lv_draw_glyph_dsc_t * glyph_draw_dsc);
static void _draw_letter(lv_draw_task_t * t, lv_draw_glyph_dsc_t * dsc, const lv_point_t * pos,
const lv_font_t * font, uint32_t letter);
static uint8_t hex_char_to_num(char hex);
static bool is_raw_bitmap;
#if LV_USE_FREETYPE && LV_USE_NEMA_VG
#include "lv_nema_gfx_path.h"
static void _draw_nema_gfx_outline(lv_draw_task_t * t, lv_draw_glyph_dsc_t * glyph_draw_dsc);
static void freetype_outline_event_cb(lv_event_t * e);
static void lv_nema_gfx_outline_push(const lv_freetype_outline_event_param_t * param);
static void lv_nema_outline_event_alloc(const lv_freetype_outline_event_param_t * param);
#endif
/**********************
* GLOBAL FUNCTIONS
**********************/
void lv_draw_nema_gfx_label_init(lv_draw_unit_t * draw_unit)
{
#if LV_USE_FREETYPE
/*Set up the freetype outline event*/
lv_freetype_outline_add_event(freetype_outline_event_cb, LV_EVENT_ALL, draw_unit);
#else
LV_UNUSED(draw_unit);
#endif /* LV_USE_FREETYPE */
}
void lv_draw_nema_gfx_label(lv_draw_task_t * t, const lv_draw_label_dsc_t * dsc, const lv_area_t * coords)
{
if(dsc->opa <= LV_OPA_MIN) return;
lv_layer_t * layer = t->target_layer;
lv_area_t clip_area;
lv_area_copy(&clip_area, &t->clip_area);
lv_area_move(&clip_area, -layer->buf_area.x1, -layer->buf_area.y1);
lv_color_format_t dst_cf = layer->draw_buf->header.cf;
uint32_t dst_nema_cf = lv_nemagfx_cf_to_nema(dst_cf);
int32_t stride = (dst_cf >= LV_COLOR_FORMAT_NEMA_TSC_START && dst_cf <= LV_COLOR_FORMAT_NEMA_TSC_END) ?
-1 : lv_area_get_width(&(layer->buf_area)) * lv_color_format_get_size(dst_cf);
nema_bind_dst_tex((uintptr_t)NEMA_VIRT2PHYS(layer->draw_buf->data), lv_area_get_width(&(layer->buf_area)),
lv_area_get_height(&(layer->buf_area)), dst_nema_cf, stride);
nema_set_clip(clip_area.x1, clip_area.y1, lv_area_get_width(&clip_area), lv_area_get_height(&clip_area));
_draw_label_iterate_characters(t, dsc, coords);
lv_draw_nema_gfx_unit_t * draw_nema_gfx_unit = (lv_draw_nema_gfx_unit_t *)t->draw_unit;
nema_cl_submit(&(draw_nema_gfx_unit->cl));
nema_cl_wait(&(draw_nema_gfx_unit->cl));
}
/**********************
* STATIC FUNCTIONS
**********************/
#if LV_USE_FREETYPE && LV_USE_NEMA_VG
static void _draw_nema_gfx_outline(lv_draw_task_t * t, lv_draw_glyph_dsc_t * glyph_draw_dsc)
{
lv_area_t blend_area;
if(!_lv_area_intersect(&blend_area, glyph_draw_dsc->letter_coords, &t->clip_area))
return;
lv_draw_nema_gfx_unit_t * draw_nema_gfx_unit = (lv_draw_nema_gfx_unit_t *)t->draw_unit;
lv_nema_gfx_path_t * nema_gfx_path = (lv_nema_gfx_path_t *)glyph_draw_dsc->glyph_data;
lv_point_t pos = {glyph_draw_dsc->letter_coords->x1, glyph_draw_dsc->letter_coords->y1};
float scale = FT_F26DOT6_TO_PATH_SCALE(lv_freetype_outline_get_scale(glyph_draw_dsc->g->resolved_font));
/*Calculate Path Matrix*/
nema_matrix3x3_t matrix;
nema_mat3x3_load_identity(matrix);
nema_mat3x3_scale(matrix, scale, -scale);
nema_mat3x3_translate(matrix, pos.x - glyph_draw_dsc->g->ofs_x,
pos.y + glyph_draw_dsc->g->box_h + glyph_draw_dsc->g->ofs_y);
nema_vg_path_clear(nema_gfx_path->path);
nema_vg_paint_clear(nema_gfx_path->paint);
nema_vg_set_fill_rule(NEMA_VG_FILL_EVEN_ODD);
nema_vg_path_set_shape(nema_gfx_path->path, nema_gfx_path->seg_size, nema_gfx_path->seg, nema_gfx_path->data_size,
nema_gfx_path->data);
nema_vg_paint_set_type(nema_gfx_path->paint, NEMA_VG_PAINT_COLOR);
lv_color32_t dsc_col32 = lv_color_to_32(glyph_draw_dsc->color, glyph_draw_dsc->opa);
uint32_t nema_dsc_color = nema_rgba(dsc_col32.red, dsc_col32.green, dsc_col32.blue, dsc_col32.alpha);
nema_vg_paint_set_paint_color(nema_gfx_path->paint, nema_dsc_color);
nema_vg_path_set_matrix(nema_gfx_path->path, matrix);
nema_vg_draw_path(nema_gfx_path->path, nema_gfx_path->paint);
return;
}
static void freetype_outline_event_cb(lv_event_t * e)
{
LV_PROFILER_DRAW_BEGIN;
lv_event_code_t code = lv_event_get_code(e);
lv_freetype_outline_event_param_t * param = lv_event_get_param(e);
switch(code) {
case LV_EVENT_CREATE:
param->outline = lv_nema_gfx_path_create();
lv_nema_outline_event_alloc(param);
break;
case LV_EVENT_DELETE:
lv_nema_gfx_path_destroy(param->outline);
break;
case LV_EVENT_INSERT:
lv_nema_gfx_outline_push(param);
break;
default:
LV_LOG_WARN("unknown event code: %d", code);
break;
}
LV_PROFILER_DRAW_END;
}
static void lv_nema_gfx_outline_push(const lv_freetype_outline_event_param_t * param)
{
LV_PROFILER_DRAW_BEGIN;
lv_nema_gfx_path_t * outline = param->outline;
LV_ASSERT_NULL(outline);
lv_freetype_outline_type_t type = param->type;
switch(type) {
case LV_FREETYPE_OUTLINE_END:
lv_nema_gfx_path_end(outline);
break;
case LV_FREETYPE_OUTLINE_MOVE_TO:
lv_nema_gfx_path_move_to(outline, param->to.x, param->to.y);
break;
case LV_FREETYPE_OUTLINE_LINE_TO:
lv_nema_gfx_path_line_to(outline, param->to.x, param->to.y);
break;
case LV_FREETYPE_OUTLINE_CUBIC_TO:
lv_nema_gfx_path_cubic_to(outline, param->control1.x, param->control1.y,
param->control2.x, param->control2.y,
param->to.x, param->to.y);
break;
case LV_FREETYPE_OUTLINE_CONIC_TO:
lv_nema_gfx_path_quad_to(outline, param->control1.x, param->control1.y,
param->to.x, param->to.y);
break;
default:
LV_LOG_ERROR("unknown point type: %d", type);
LV_ASSERT(false);
break;
}
LV_PROFILER_DRAW_END;
}
static void lv_nema_outline_event_alloc(const lv_freetype_outline_event_param_t * param)
{
lv_nema_gfx_path_t * outline = param->outline;
outline->data_size = param->sizes.data_size;
outline->seg_size = param->sizes.segments_size;
lv_nema_gfx_path_alloc(outline);
}
#endif /* LV_USE_FREETYPE && LV_USE_NEMA_VG */
/**
* Convert a hexadecimal characters to a number (0..15)
* @param hex Pointer to a hexadecimal character (0..9, A..F)
* @return the numerical value of `hex` or 0 on error
*/
static uint8_t hex_char_to_num(char hex)
{
if(hex >= '0' && hex <= '9') return hex - '0';
if(hex >= 'a') hex -= 'a' - 'A'; /*Convert to upper case*/
return 'A' <= hex && hex <= 'F' ? hex - 'A' + 10 : 0;
}
static inline uint8_t _bpp_nema_gfx_format(lv_draw_glyph_dsc_t * glyph_draw_dsc)
{
uint32_t format = glyph_draw_dsc->g->format;
switch(format) {
case LV_FONT_GLYPH_FORMAT_A1:
return NEMA_A1;
case LV_FONT_GLYPH_FORMAT_A2:
return NEMA_A2;
case LV_FONT_GLYPH_FORMAT_A4:
return NEMA_A4;
case LV_FONT_GLYPH_FORMAT_A8:
default:
return NEMA_A8;
}
}
static void _draw_nema_gfx_letter(lv_draw_task_t * t, lv_draw_glyph_dsc_t * glyph_draw_dsc,
lv_draw_fill_dsc_t * fill_draw_dsc, const lv_area_t * fill_area)
{
if(glyph_draw_dsc) {
if(glyph_draw_dsc->format == LV_FONT_GLYPH_FORMAT_NONE) {
#if LV_USE_FONT_PLACEHOLDER
/* Draw a placeholder rectangle*/
lv_draw_border_dsc_t border_draw_dsc;
lv_draw_border_dsc_init(&border_draw_dsc);
border_draw_dsc.opa = glyph_draw_dsc->opa;
border_draw_dsc.color = glyph_draw_dsc->color;
border_draw_dsc.width = 1;
lv_draw_nema_gfx_border(t, &border_draw_dsc, glyph_draw_dsc->bg_coords);
#endif
}
else if(glyph_draw_dsc->format >= LV_FONT_GLYPH_FORMAT_A1 &&
glyph_draw_dsc->format <= LV_FONT_GLYPH_FORMAT_A8) {
/*Do not draw transparent things*/
if(glyph_draw_dsc->opa <= LV_OPA_MIN)
return;
lv_layer_t * layer = t->target_layer;
lv_area_t blend_area;
if(!lv_area_intersect(&blend_area, glyph_draw_dsc->letter_coords, &t->clip_area))
return;
const lv_draw_buf_t * draw_buf = glyph_draw_dsc->glyph_data;
const void * mask_buf;
uint32_t src_cf;
lv_area_t mask_area = *glyph_draw_dsc->letter_coords;
lv_area_t rel_coords;
lv_area_copy(&rel_coords, &blend_area);
lv_area_move(&rel_coords, -layer->buf_area.x1, -layer->buf_area.y1);
int32_t x, y, w, h;
/*Read the static font*/
if(is_raw_bitmap) {
mask_buf = glyph_draw_dsc->glyph_data;
src_cf = _bpp_nema_gfx_format(glyph_draw_dsc);
x = glyph_draw_dsc->letter_coords->x1 - layer->buf_area.x1;
y = glyph_draw_dsc->letter_coords->y1 - layer->buf_area.y1;
w = glyph_draw_dsc->g->box_w;
h = glyph_draw_dsc->g->box_h;
}
/*Read the draw buffer*/
else {
mask_buf = draw_buf->data;
src_cf = lv_nemagfx_cf_to_nema(draw_buf->header.cf);
mask_area.x2 = mask_area.x1 + lv_draw_buf_width_to_stride(lv_area_get_width(&mask_area), LV_COLOR_FORMAT_A8) - 1;
mask_buf += draw_buf->header.stride * (blend_area.y1 - mask_area.y1) + (blend_area.x1 - mask_area.x1);
x = rel_coords.x1;
y = rel_coords.y1;
w = lv_area_get_width(&rel_coords);
h = lv_area_get_height(&rel_coords);
}
if(is_raw_bitmap && (glyph_draw_dsc->format <= LV_FONT_GLYPH_FORMAT_A4)) {
nema_bind_src_tex((uintptr_t)(mask_buf), w * h, 1, src_cf, glyph_draw_dsc->g->stride, NEMA_FILTER_PS);
nema_matrix3x3_t m = {
{1, w, -x - (y * w) - (0.5 * w)},
{0, 1, 0},
{0, 0, 1}
};
nema_set_matrix(m);
nema_raster_rect(x, y, w, h);
}
else {
nema_bind_src_tex((uintptr_t)(mask_buf), lv_area_get_width(&mask_area), lv_area_get_height(&mask_area), src_cf,
glyph_draw_dsc->g->stride ? glyph_draw_dsc->g->stride : lv_area_get_width(&mask_area),
NEMA_FILTER_BL);
nema_blit_rect(x, y, w, h);
lv_draw_nema_gfx_unit_t * draw_nema_gfx_unit = (lv_draw_nema_gfx_unit_t *)t->draw_unit;
nema_cl_submit(&(draw_nema_gfx_unit->cl));
nema_cl_wait(&(draw_nema_gfx_unit->cl));
}
}
else if(glyph_draw_dsc->format == LV_FONT_GLYPH_FORMAT_IMAGE) {
#if LV_USE_IMGFONT
lv_draw_img_dsc_t img_dsc;
lv_draw_img_dsc_init(&img_dsc);
img_dsc.angle = 0;
img_dsc.zoom = LV_ZOOM_NONE;
img_dsc.opa = glyph_draw_dsc->opa;
img_dsc.src = glyph_draw_dsc->glyph_data;
lv_draw_nema_gfx_img(t, &img_dsc, glyph_draw_dsc->letter_coords);
#endif
}
#if LV_USE_FREETYPE && LV_USE_NEMA_VG
else if(glyph_draw_dsc->format == LV_FONT_GLYPH_FORMAT_VECTOR) {
if(lv_freetype_is_outline_font(glyph_draw_dsc->g->resolved_font)) {
_draw_nema_gfx_outline(t, glyph_draw_dsc);
}
}
#endif
}
if(fill_draw_dsc && fill_area) {
lv_draw_nema_gfx_fill(t, fill_draw_dsc, fill_area);
}
}
static inline void _set_color_blend(uint32_t color, uint8_t alpha)
{
nema_set_tex_color(color);
if(alpha < 255U) {
nema_set_blend_blit(NEMA_BL_SIMPLE | NEMA_BLOP_MODULATE_A);
nema_set_const_color(color);
}
else {
nema_set_blend_blit(NEMA_BL_SIMPLE);
}
}
static void _draw_label_iterate_characters(lv_draw_task_t * t, const lv_draw_label_dsc_t * dsc,
const lv_area_t * coords)
{
const lv_font_t * font = dsc->font;
int32_t w;
lv_area_t clipped_area;
bool clip_ok = lv_area_intersect(&clipped_area, coords, &t->clip_area);
if(!clip_ok) return;
lv_text_align_t align = dsc->align;
lv_base_dir_t base_dir = dsc->bidi_dir;
lv_bidi_calculate_align(&align, &base_dir, dsc->text);
if((dsc->flag & LV_TEXT_FLAG_EXPAND) == 0) {
/*Normally use the label's width as width*/
w = lv_area_get_width(coords);
}
else {
/*If EXPAND is enabled then not limit the text's width to the object's width*/
lv_point_t p;
lv_text_get_size(&p, dsc->text, dsc->font, dsc->letter_space, dsc->line_space, LV_COORD_MAX,
dsc->flag);
w = p.x;
}
int32_t line_height_font = lv_font_get_line_height(font);
int32_t line_height = line_height_font + dsc->line_space;
/*Init variables for the first line*/
int32_t line_width = 0;
lv_point_t pos;
lv_point_set(&pos, coords->x1, coords->y1);
int32_t x_ofs = 0;
int32_t y_ofs = 0;
x_ofs = dsc->ofs_x;
y_ofs = dsc->ofs_y;
pos.y += y_ofs;
uint32_t line_start = 0;
int32_t last_line_start = -1;
/*Check the hint to use the cached info*/
if(dsc->hint && y_ofs == 0 && coords->y1 < 0) {
/*If the label changed too much recalculate the hint.*/
if(LV_ABS(dsc->hint->coord_y - coords->y1) > LV_LABEL_HINT_UPDATE_TH - 2 * line_height) {
dsc->hint->line_start = -1;
}
last_line_start = dsc->hint->line_start;
}
/*Use the hint if it's valid*/
if(dsc->hint && last_line_start >= 0) {
line_start = last_line_start;
pos.y += dsc->hint->y;
}
uint32_t remaining_len = dsc->text_length;
uint32_t line_end = line_start + lv_text_get_next_line(&dsc->text[line_start], remaining_len, font, dsc->letter_space,
w, NULL, dsc->flag);
/*Go the first visible line*/
while(pos.y + line_height_font < t->clip_area.y1) {
/*Go to next line*/
line_start = line_end;
line_end += lv_text_get_next_line(&dsc->text[line_start], remaining_len, font, dsc->letter_space, w, NULL, dsc->flag);
pos.y += line_height;
/*Save at the threshold coordinate*/
if(dsc->hint && pos.y >= -LV_LABEL_HINT_UPDATE_TH && dsc->hint->line_start < 0) {
dsc->hint->line_start = line_start;
dsc->hint->y = pos.y - coords->y1;
dsc->hint->coord_y = coords->y1;
}
if(dsc->text[line_start] == '\0') return;
}
/*Align to middle*/
if(align == LV_TEXT_ALIGN_CENTER) {
line_width = lv_text_get_width_with_flags(&dsc->text[line_start], line_end - line_start, font, dsc->letter_space,
dsc->flag);
pos.x += (lv_area_get_width(coords) - line_width) / 2;
}
/*Align to the right*/
else if(align == LV_TEXT_ALIGN_RIGHT) {
line_width = lv_text_get_width_with_flags(&dsc->text[line_start], line_end - line_start, font, dsc->letter_space,
dsc->flag);
pos.x += lv_area_get_width(coords) - line_width;
}
uint32_t sel_start = dsc->sel_start;
uint32_t sel_end = dsc->sel_end;
if(sel_start > sel_end) {
uint32_t tmp = sel_start;
sel_start = sel_end;
sel_end = tmp;
}
lv_area_t bg_coords;
lv_draw_glyph_dsc_t draw_letter_dsc;
lv_draw_glyph_dsc_init(&draw_letter_dsc);
draw_letter_dsc.opa = dsc->opa;
draw_letter_dsc.bg_coords = &bg_coords;
draw_letter_dsc.color = dsc->color;
draw_letter_dsc.rotation = dsc->rotation;
lv_draw_fill_dsc_t fill_dsc;
lv_draw_fill_dsc_init(&fill_dsc);
fill_dsc.opa = dsc->opa;
int32_t underline_width = font->underline_thickness ? font->underline_thickness : 1;
int32_t line_start_x;
uint32_t next_char_offset;
uint32_t recolor_command_start_index = 0;
int32_t letter_w;
cmd_state_t recolor_cmd_state = RECOLOR_CMD_STATE_WAIT_FOR_PARAMETER;
lv_color_t recolor = lv_color_black(); /* Holds the selected color inside the recolor command */
uint8_t is_first_space_after_cmd = 0;
lv_color32_t dsc_col32 = lv_color_to_32(dsc->color, dsc->opa);
uint32_t nema_dsc_color = nema_rgba(dsc_col32.red, dsc_col32.green, dsc_col32.blue, dsc_col32.alpha);
lv_color32_t dsc_sel_col32 = lv_color_to_32(dsc->sel_color, dsc->opa);
uint32_t nema_dsc_sel_color = nema_rgba(dsc_sel_col32.red, dsc_sel_col32.green, dsc_sel_col32.blue,
dsc_sel_col32.alpha);
uint32_t blend_color;
uint8_t blend_alpha = 255;
_set_color_blend(nema_dsc_color, dsc_col32.alpha);
uint8_t cur_state = 2;
uint8_t prev_state = 2;
/*Write out all lines*/
while(remaining_len && dsc->text[line_start] != '\0') {
pos.x += x_ofs;
line_start_x = pos.x;
/*Write all letter of a line*/
recolor_cmd_state = RECOLOR_CMD_STATE_WAIT_FOR_PARAMETER;
next_char_offset = 0;
#if LV_USE_BIDI
char * bidi_txt = lv_malloc(line_end - line_start + 1);
LV_ASSERT_MALLOC(bidi_txt);
lv_bidi_process_paragraph(dsc->text + line_start, bidi_txt, line_end - line_start, base_dir, NULL, 0);
#else
const char * bidi_txt = dsc->text + line_start;
#endif
while(next_char_offset < remaining_len && next_char_offset < line_end - line_start) {
uint32_t logical_char_pos = 0;
/* Check if the text selection is enabled */
if(sel_start != LV_DRAW_LABEL_NO_TXT_SEL && sel_end != LV_DRAW_LABEL_NO_TXT_SEL) {
#if LV_USE_BIDI
logical_char_pos = lv_text_encoded_get_char_id(dsc->text, line_start);
uint32_t c_idx = lv_text_encoded_get_char_id(bidi_txt, next_char_offset);
logical_char_pos += lv_bidi_get_logical_pos(bidi_txt, NULL, line_end - line_start, base_dir, c_idx, NULL);
#else
logical_char_pos = lv_text_encoded_get_char_id(dsc->text, line_start + next_char_offset);
#endif
}
uint32_t letter;
uint32_t letter_next;
lv_text_encoded_letter_next_2(bidi_txt, &letter, &letter_next, &next_char_offset);
/* If recolor is enabled */
if((dsc->flag & LV_TEXT_FLAG_RECOLOR) != 0) {
if(letter == (uint32_t)LV_TXT_COLOR_CMD[0]) {
/* Handle the recolor command marker depending of the current recolor state */
if(recolor_cmd_state == RECOLOR_CMD_STATE_WAIT_FOR_PARAMETER) {
recolor_command_start_index = next_char_offset;
recolor_cmd_state = RECOLOR_CMD_STATE_PARAMETER;
continue;
}
/*Other start char in parameter escaped cmd. char*/
else if(recolor_cmd_state == RECOLOR_CMD_STATE_PARAMETER) {
recolor_cmd_state = RECOLOR_CMD_STATE_WAIT_FOR_PARAMETER;
}
/* If letter is LV_TXT_COLOR_CMD and we were in the CMD_STATE_IN then the recolor close marked has been found */
else if(recolor_cmd_state == RECOLOR_CMD_STATE_TEXT_INPUT) {
recolor_cmd_state = RECOLOR_CMD_STATE_WAIT_FOR_PARAMETER;
continue;
}
}
/* Find the first space (aka ' ') after the recolor command parameter, we need to skip rendering it */
if((recolor_cmd_state == RECOLOR_CMD_STATE_PARAMETER) && (letter == ' ') && (is_first_space_after_cmd == 0)) {
is_first_space_after_cmd = 1;
}
else {
is_first_space_after_cmd = 0;
}
/* Skip the color parameter and wait the space after it
* Once we have reach the space ' ', then we will extract the color information
* and store it into the recolor variable */
if(recolor_cmd_state == RECOLOR_CMD_STATE_PARAMETER) {
/* Not an space? Continue with the next character */
if(letter != ' ') {
continue;
}
/*Get the recolor parameter*/
if((next_char_offset - recolor_command_start_index) == LABEL_RECOLOR_PAR_LENGTH + 1) {
/* Temporary buffer to hold the recolor information */
char buf[LABEL_RECOLOR_PAR_LENGTH + 1];
lv_memcpy(buf, &bidi_txt[recolor_command_start_index], LABEL_RECOLOR_PAR_LENGTH);
buf[LABEL_RECOLOR_PAR_LENGTH] = '\0';
uint8_t r, g, b;
r = (hex_char_to_num(buf[0]) << 4) + hex_char_to_num(buf[1]);
g = (hex_char_to_num(buf[2]) << 4) + hex_char_to_num(buf[3]);
b = (hex_char_to_num(buf[4]) << 4) + hex_char_to_num(buf[5]);
recolor = lv_color_make(r, g, b);
}
else {
recolor.red = dsc->color.red;
recolor.blue = dsc->color.blue;
recolor.green = dsc->color.green;
}
/*After the parameter the text is in the command*/
recolor_cmd_state = RECOLOR_CMD_STATE_TEXT_INPUT;
}
/* Don't draw the first space after the recolor command */
if(is_first_space_after_cmd) {
continue;
}
}
/* If we're in the CMD_STATE_IN state then we need to subtract the recolor command length */
if(((dsc->flag & LV_TEXT_FLAG_RECOLOR) != 0) && (recolor_cmd_state == RECOLOR_CMD_STATE_TEXT_INPUT)) {
logical_char_pos -= (LABEL_RECOLOR_PAR_LENGTH + 1);
}
letter_w = lv_font_get_glyph_width(font, letter, letter_next);
/*Always set the bg_coordinates for placeholder drawing*/
bg_coords.x1 = pos.x;
bg_coords.y1 = pos.y;
bg_coords.x2 = pos.x + letter_w - 1;
bg_coords.y2 = pos.y + line_height - 1;
if(next_char_offset >= line_end - line_start) {
if(dsc->decor & LV_TEXT_DECOR_UNDERLINE) {
lv_area_t fill_area;
fill_area.x1 = line_start_x;
fill_area.x2 = pos.x + letter_w - 1;
fill_area.y1 = pos.y + font->line_height - font->base_line - font->underline_position;
fill_area.y2 = fill_area.y1 + underline_width - 1;
fill_dsc.color = dsc->color;
lv_draw_nema_gfx_fill(t, &fill_dsc, &fill_area);
}
if(dsc->decor & LV_TEXT_DECOR_STRIKETHROUGH) {
lv_area_t fill_area;
fill_area.x1 = line_start_x;
fill_area.x2 = pos.x + letter_w - 1;
fill_area.y1 = pos.y + (font->line_height - font->base_line) * 2 / 3 + font->underline_thickness / 2;
fill_area.y2 = fill_area.y1 + underline_width - 1;
fill_dsc.color = dsc->color;
lv_draw_nema_gfx_fill(t, &fill_dsc, &fill_area);
}
}
/* Handle text selection */
if(sel_start != LV_DRAW_LABEL_NO_TXT_SEL && sel_end != LV_DRAW_LABEL_NO_TXT_SEL
&& logical_char_pos >= sel_start && logical_char_pos < sel_end) {
draw_letter_dsc.color = dsc->sel_color;
fill_dsc.color = dsc->sel_bg_color;
lv_draw_nema_gfx_fill(t, &fill_dsc, &bg_coords);
cur_state = 0 ;
blend_alpha = dsc_sel_col32.alpha;
blend_color = nema_dsc_sel_color;
}
else if(recolor_cmd_state == RECOLOR_CMD_STATE_TEXT_INPUT) {
draw_letter_dsc.color = recolor;
cur_state = 1 ;
blend_alpha = dsc_col32.alpha;
lv_color32_t dsc_recolor_col32 = lv_color_to_32(recolor, dsc->opa);
blend_color = nema_rgba(dsc_recolor_col32.red, dsc_recolor_col32.green, dsc_recolor_col32.blue,
dsc_recolor_col32.alpha);
}
else {
draw_letter_dsc.color = dsc->color;
cur_state = 2;
blend_alpha = dsc_col32.alpha;
blend_color = nema_dsc_color;
}
if(cur_state != prev_state) {
_set_color_blend(blend_color, blend_alpha);
prev_state = cur_state;
}
_draw_letter(t, &draw_letter_dsc, &pos, font, letter);
if(letter_w > 0) {
pos.x += letter_w + dsc->letter_space;
}
}
#if LV_USE_BIDI
lv_free(bidi_txt);
bidi_txt = NULL;
#endif
/*Go to next line*/
remaining_len -= line_end - line_start;
line_start = line_end;
if(remaining_len) {
line_end += lv_text_get_next_line(&dsc->text[line_start], remaining_len, font, dsc->letter_space, w, NULL, dsc->flag);
}
pos.x = coords->x1;
/*Align to middle*/
if(align == LV_TEXT_ALIGN_CENTER) {
line_width =
lv_text_get_width_with_flags(&dsc->text[line_start], line_end - line_start, font, dsc->letter_space, dsc->flag);
pos.x += (lv_area_get_width(coords) - line_width) / 2;
}
/*Align to the right*/
else if(align == LV_TEXT_ALIGN_RIGHT) {
line_width =
lv_text_get_width_with_flags(&dsc->text[line_start], line_end - line_start, font, dsc->letter_space, dsc->flag);
pos.x += lv_area_get_width(coords) - line_width;
}
/*Go the next line position*/
pos.y += line_height;
if(pos.y > t->clip_area.y2) break;
}
if(draw_letter_dsc._draw_buf) lv_draw_buf_destroy(draw_letter_dsc._draw_buf);
LV_ASSERT_MEM_INTEGRITY();
}
static void _draw_letter(lv_draw_task_t * t, lv_draw_glyph_dsc_t * dsc, const lv_point_t * pos,
const lv_font_t * font, uint32_t letter)
{
lv_font_glyph_dsc_t g;
if(lv_text_is_marker(letter)) /*Markers are valid letters but should not be rendered.*/
return;
LV_PROFILER_DRAW_BEGIN;
bool g_ret = lv_font_get_glyph_dsc(font, &g, letter, '\0');
if(g_ret == false) {
/*Add warning if the dsc is not found*/
LV_LOG_WARN("lv_draw_letter: glyph dsc. not found for U+%" LV_PRIX32, letter);
}
/*Don't draw anything if the character is empty. E.g. space*/
if((g.box_h == 0) || (g.box_w == 0)) {
LV_PROFILER_DRAW_END;
return;
}
lv_area_t letter_coords;
letter_coords.x1 = pos->x + g.ofs_x;
letter_coords.x2 = letter_coords.x1 + g.box_w - 1;
letter_coords.y1 = pos->y + (font->line_height - font->base_line) - g.box_h - g.ofs_y;
letter_coords.y2 = letter_coords.y1 + g.box_h - 1;
lv_area_move(&letter_coords, -dsc->pivot.x, -dsc->pivot.y);
/*If the letter is completely out of mask don't draw it*/
if(lv_area_is_out(&letter_coords, &t->clip_area, 0) &&
dsc->bg_coords &&
lv_area_is_out(dsc->bg_coords, &t->clip_area, 0)) {
LV_PROFILER_DRAW_END;
return;
}
if(g.resolved_font) {
lv_draw_buf_t * draw_buf = NULL;
if(LV_FONT_GLYPH_FORMAT_NONE < g.format && g.format < LV_FONT_GLYPH_FORMAT_IMAGE) {
/*Only check draw buf for bitmap glyph*/
draw_buf = lv_draw_buf_reshape(dsc->_draw_buf, 0, g.box_w, g.box_h, LV_STRIDE_AUTO);
if(draw_buf == NULL) {
if(dsc->_draw_buf) lv_draw_buf_destroy(dsc->_draw_buf);
uint32_t h = LV_ROUND_UP(g.box_h, 32); /*Assume a larger size to avoid many reallocations*/
draw_buf = lv_draw_buf_create_ex(font_draw_buf_handlers, g.box_w, h, LV_COLOR_FORMAT_A8, LV_STRIDE_AUTO);
LV_ASSERT_MALLOC(draw_buf);
draw_buf->header.h = g.box_h;
dsc->_draw_buf = draw_buf;
}
}
/* Performance Optimization for lv_font_fmt_txt_dsc_t fonts, always request raw bitmaps */
/*Exception for w*h >= NEMA_COORD_LIMIT due to HW limitation on data handling*/
is_raw_bitmap = false;
if(g.box_h * g.box_w <= NEMA_COORD_LIMIT) {
g.req_raw_bitmap = 1;
if(font->get_glyph_bitmap == lv_font_get_bitmap_fmt_txt) {
lv_font_fmt_txt_dsc_t * fdsc = (lv_font_fmt_txt_dsc_t *)font->dsc;
if(fdsc->bitmap_format == LV_FONT_FMT_TXT_PLAIN) {
is_raw_bitmap = true;
}
}
}
dsc->glyph_data = g.resolved_font->get_glyph_bitmap(&g, draw_buf);
dsc->format = dsc->glyph_data ? g.format : LV_FONT_GLYPH_FORMAT_NONE;
}
else {
dsc->format = LV_FONT_GLYPH_FORMAT_NONE;
}
dsc->letter_coords = &letter_coords;
dsc->g = &g;
_draw_nema_gfx_letter(t, dsc, NULL, NULL);
if(g.resolved_font && font->release_glyph) {
lv_draw_nema_gfx_unit_t * draw_nema_gfx_unit = (lv_draw_nema_gfx_unit_t *)t->draw_unit;
nema_cl_submit(&(draw_nema_gfx_unit->cl));
nema_cl_wait(&(draw_nema_gfx_unit->cl));
font->release_glyph(font, &g);
}
LV_PROFILER_DRAW_END;
}
#endif /*LV_USE_NEMA_GFX*/
@@ -0,0 +1,57 @@
/**
* MIT License
*
* -----------------------------------------------------------------------------
* Copyright (c) 2008-24 Think Silicon Single Member PC
* -----------------------------------------------------------------------------
*
* 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 (including the next paragraph)
* 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.
*
*/
/**
* @file lv_draw_nema_gfx_fill.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_draw_nema_gfx.h"
#if LV_USE_NEMA_GFX
/**********************
* GLOBAL FUNCTIONS
**********************/
void lv_draw_nema_gfx_layer(lv_draw_task_t * t, const lv_draw_image_dsc_t * draw_dsc, const lv_area_t * coords)
{
lv_layer_t * layer_to_draw = (lv_layer_t *)draw_dsc->src;
/*It can happen that nothing was draw on a layer and therefore its buffer is not allocated.
*In this case just return. */
if(layer_to_draw->draw_buf == NULL) return;
lv_draw_image_dsc_t new_draw_dsc = *draw_dsc;
new_draw_dsc.src = layer_to_draw->draw_buf;
lv_draw_nema_gfx_img(t, &new_draw_dsc, coords);
}
#endif /*LV_USE_NEMA_GFX*/
@@ -0,0 +1,96 @@
/**
* MIT License
*
* -----------------------------------------------------------------------------
* Copyright (c) 2008-24 Think Silicon Single Member PC
* -----------------------------------------------------------------------------
*
* 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 (including the next paragraph)
* 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.
*
*/
/**
* @file lv_draw_nema_gfx_fill.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_draw_nema_gfx.h"
#if LV_USE_NEMA_GFX
/**********************
* GLOBAL FUNCTIONS
**********************/
void lv_draw_nema_gfx_line(lv_draw_task_t * t, const lv_draw_line_dsc_t * dsc)
{
if(dsc->width == 0)
return;
if(dsc->opa <= (lv_opa_t)LV_OPA_MIN)
return;
if(dsc->p1.x == dsc->p2.x && dsc->p1.y == dsc->p2.y)
return;
lv_draw_nema_gfx_unit_t * draw_nema_gfx_unit = (lv_draw_nema_gfx_unit_t *)t->draw_unit;
lv_layer_t * layer = t->target_layer;
lv_area_t clip_area;
clip_area.x1 = LV_MIN(dsc->p1.x, dsc->p2.x) - dsc->width / 2;
clip_area.x2 = LV_MAX(dsc->p1.x, dsc->p2.x) + dsc->width / 2;
clip_area.y1 = LV_MIN(dsc->p1.y, dsc->p2.y) - dsc->width / 2;
clip_area.y2 = LV_MAX(dsc->p1.y, dsc->p2.y) + dsc->width / 2;
if(!lv_area_intersect(&clip_area, &clip_area, &t->clip_area))
return; /*Fully clipped, nothing to do*/
lv_area_move(&clip_area, -layer->buf_area.x1, -layer->buf_area.y1);
lv_point_t point1 = {dsc->p1.x - layer->buf_area.x1, dsc->p1.y - layer->buf_area.y1};
lv_point_t point2 = {dsc->p2.x - layer->buf_area.x1, dsc->p2.y - layer->buf_area.y1};
nema_set_clip(clip_area.x1, clip_area.y1, lv_area_get_width(&clip_area), lv_area_get_height(&clip_area));
lv_color32_t col32 = lv_color_to_32(dsc->color, dsc->opa);
uint32_t bg_color = nema_rgba(col32.red, col32.green, col32.blue, col32.alpha);
lv_color_format_t dst_cf = layer->draw_buf->header.cf;
uint32_t dst_nema_cf = lv_nemagfx_cf_to_nema(dst_cf);
/* the stride should be computed internally for NEMA_TSC images and images missing a stride value */
int32_t stride = (dst_cf >= LV_COLOR_FORMAT_NEMA_TSC_START && dst_cf <= LV_COLOR_FORMAT_NEMA_TSC_END) ?
-1 : lv_area_get_width(&(layer->buf_area)) * lv_color_format_get_size(dst_cf);
nema_bind_dst_tex((uintptr_t)NEMA_VIRT2PHYS(layer->draw_buf->data), lv_area_get_width(&(layer->buf_area)),
lv_area_get_height(&(layer->buf_area)), dst_nema_cf, stride);
if(col32.alpha < 255U) {
nema_set_blend_fill(NEMA_BL_SIMPLE);
}
else {
nema_set_blend_fill(NEMA_BL_SRC);
}
nema_draw_line_aa(point1.x, point1.y, point2.x, point2.y, dsc->width, bg_color);
nema_cl_submit(&(draw_nema_gfx_unit->cl));
}
#endif
@@ -0,0 +1,254 @@
/**
* @file lv_draw_nema_gfx_hal.c
*
* Global functions that implement some HAL functionality
* which Nema will call directly.
*/
/*********************
* INCLUDES
*********************/
#include "../../lv_conf_internal.h"
#if LV_USE_NEMA_GFX
#if LV_USE_NEMA_HAL == LV_NEMA_HAL_STM32
#include "../../misc/lv_types.h"
#include "../../misc/lv_assert.h"
#include "../../stdlib/lv_string.h"
#include <nema_sys_defs.h>
#include <nema_core.h>
#include <assert.h>
#include <string.h>
#include LV_NEMA_STM32_HAL_INCLUDE
#include "tsi_malloc.h"
extern GPU2D_HandleTypeDef hgpu2d;
/*********************
* DEFINES
*********************/
#define RING_SIZE 1024 /* Ring Buffer Size in byte */
/* NemaGFX byte pool size in bytes.
* One byte per peixel for masking/stencling plus 10240 for additional allocations.
*/
#if defined(LV_NEMA_GFX_MAX_RESX) && defined(LV_NEMA_GFX_MAX_RESY)
#define NEMAGFX_MEM_POOL_SIZE ((LV_NEMA_GFX_MAX_RESX * LV_NEMA_GFX_MAX_RESY) + 10240)
#else
/* LV_USE_NEMA_VG is 0 so masking/stencling memory is not needed. */
#define NEMAGFX_MEM_POOL_SIZE 10240
#endif
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
#if (USE_HAL_GPU2D_REGISTER_CALLBACKS == 1)
static void GPU2D_CommandListCpltCallback(GPU2D_HandleTypeDef * hgpu2d, uint32_t CmdListID);
#endif
/**********************
* STATIC VARIABLES
**********************/
static uint8_t nemagfx_pool_mem[NEMAGFX_MEM_POOL_SIZE]; /* NemaGFX memory pool */
static nema_ringbuffer_t ring_buffer_str;
static volatile int last_cl_id = -1;
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
#if (USE_HAL_GPU2D_REGISTER_CALLBACKS == 1)
static void GPU2D_CommandListCpltCallback(GPU2D_HandleTypeDef * hgpu2d, uint32_t CmdListID)
#else
void HAL_GPU2D_CommandListCpltCallback(GPU2D_HandleTypeDef * hgpu2d, uint32_t CmdListID)
#endif
{
LV_UNUSED(hgpu2d);
last_cl_id = CmdListID;
}
int32_t nema_sys_init(void)
{
int error_code = 0;
/* Setup GPU2D Callback */
#if (USE_HAL_GPU2D_REGISTER_CALLBACKS == 1)
/* Register Command List Complete Callback */
HAL_GPU2D_RegisterCommandListCpltCallback(&hgpu2d, GPU2D_CommandListCpltCallback);
#endif
/* Initialise Mem Space */
error_code = tsi_malloc_init_pool_aligned(0, (void *)nemagfx_pool_mem, (uintptr_t)nemagfx_pool_mem,
NEMAGFX_MEM_POOL_SIZE, 1, 8);
LV_ASSERT(error_code == 0);
/* Allocate ring_buffer memory */
ring_buffer_str.bo = nema_buffer_create(RING_SIZE);
LV_ASSERT(ring_buffer_str.bo.base_virt);
/* Initialize Ring Buffer */
error_code = nema_rb_init(&ring_buffer_str, 1);
if(error_code < 0) {
return error_code;
}
/* Reset last_cl_id counter */
last_cl_id = 0;
return error_code;
}
uint32_t nema_reg_read(uint32_t reg)
{
return HAL_GPU2D_ReadRegister(&hgpu2d, reg);
}
void nema_reg_write(uint32_t reg, uint32_t value)
{
HAL_GPU2D_WriteRegister(&hgpu2d, reg, value);
}
int nema_wait_irq(void)
{
return 0;
}
int nema_wait_irq_cl(int cl_id)
{
while(last_cl_id < cl_id) {
(void)nema_wait_irq();
}
return 0;
}
int nema_wait_irq_brk(int brk_id)
{
while(nema_reg_read(GPU2D_BREAKPOINT) == 0U) {
(void)nema_wait_irq();
}
return 0;
}
void nema_host_free(void * ptr)
{
tsi_free(ptr);
}
void * nema_host_malloc(unsigned size)
{
return tsi_malloc(size);
}
nema_buffer_t nema_buffer_create(int size)
{
nema_buffer_t bo;
lv_memset(&bo, 0, sizeof(bo));
bo.base_virt = tsi_malloc(size);
bo.base_phys = (uint32_t)bo.base_virt;
bo.size = size;
LV_ASSERT_MSG(bo.base_virt != 0, "Unable to allocate memory in nema_buffer_create");
return bo;
}
nema_buffer_t nema_buffer_create_pool(int pool, int size)
{
LV_UNUSED(pool);
return nema_buffer_create(size);
}
void * nema_buffer_map(nema_buffer_t * bo)
{
return bo->base_virt;
}
void nema_buffer_unmap(nema_buffer_t * bo)
{
LV_UNUSED(bo);
}
void nema_buffer_destroy(nema_buffer_t * bo)
{
if(bo->fd == -1) {
return; /* Buffer weren't allocated! */
}
tsi_free(bo->base_virt);
bo->base_virt = (void *)0;
bo->base_phys = 0;
bo->size = 0;
bo->fd = -1; /* Buffer not allocated */
}
uintptr_t nema_buffer_phys(nema_buffer_t * bo)
{
return bo->base_phys;
}
void nema_buffer_flush(nema_buffer_t * bo)
{
LV_UNUSED(bo);
}
int nema_mutex_lock(int mutex_id)
{
int retval = 0;
LV_UNUSED(mutex_id);
return retval;
}
int nema_mutex_unlock(int mutex_id)
{
int retval = 0;
LV_UNUSED(mutex_id);
return retval;
}
void platform_disable_cache(void)
{
}
void platform_invalidate_cache(void)
{
}
/**********************
* STATIC FUNCTIONS
**********************/
#endif /* LV_USE_NEMA_HAL == LV_NEMA_HAL_STM32 */
#endif /* LV_USE_NEMA_GFX */
@@ -0,0 +1,171 @@
/**
* MIT License
*
* -----------------------------------------------------------------------------
* Copyright (c) 2008-24 Think Silicon Single Member PC
* -----------------------------------------------------------------------------
*
* 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 (including the next paragraph)
* 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.
*
*/
/**
* @file lv_draw_nema_gfx_fill.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_draw_nema_gfx.h"
#if LV_USE_NEMA_GFX
/**********************
* GLOBAL FUNCTIONS
**********************/
void lv_draw_nema_gfx_triangle(lv_draw_task_t * t, const lv_draw_triangle_dsc_t * dsc)
{
if(dsc->opa <= LV_OPA_MIN) return;
lv_draw_nema_gfx_unit_t * draw_nema_gfx_unit = (lv_draw_nema_gfx_unit_t *)t->draw_unit;
lv_layer_t * layer = t->target_layer;
lv_area_t rel_clip_area;
lv_area_copy(&rel_clip_area, &t->clip_area);
lv_area_move(&rel_clip_area, -layer->buf_area.x1, -layer->buf_area.y1);
lv_area_t coords;
coords.x1 = (int32_t)LV_MIN3(dsc->p[0].x, dsc->p[1].x, dsc->p[2].x);
coords.y1 = (int32_t)LV_MIN3(dsc->p[0].y, dsc->p[1].y, dsc->p[2].y);
coords.x2 = (int32_t)LV_MAX3(dsc->p[0].x, dsc->p[1].x, dsc->p[2].x);
coords.y2 = (int32_t)LV_MAX3(dsc->p[0].y, dsc->p[1].y, dsc->p[2].y);
lv_area_move(&coords, -layer->buf_area.x1, -layer->buf_area.y1);
lv_area_t clipped_coords;
if(!lv_area_intersect(&clipped_coords, &coords, &rel_clip_area))
return; /* Fully clipped, nothing to do */
nema_set_clip(rel_clip_area.x1, rel_clip_area.y1, lv_area_get_width(&rel_clip_area),
lv_area_get_height(&rel_clip_area));
lv_color_format_t dst_cf = layer->draw_buf->header.cf;
uint32_t dst_nema_cf = lv_nemagfx_cf_to_nema(dst_cf);
/* the stride should be computed internally for NEMA_TSC images and images missing a stride value */
int32_t stride = (dst_cf >= LV_COLOR_FORMAT_NEMA_TSC_START && dst_cf <= LV_COLOR_FORMAT_NEMA_TSC_END) ?
-1 : lv_area_get_width(&(layer->buf_area)) * lv_color_format_get_size(dst_cf);
nema_bind_dst_tex((uintptr_t)NEMA_VIRT2PHYS(layer->draw_buf->data), lv_area_get_width(&(layer->buf_area)),
lv_area_get_height(&(layer->buf_area)), dst_nema_cf, stride);
if(dsc->grad.dir == (lv_grad_dir_t)LV_GRAD_DIR_NONE) {
lv_color32_t col32 = lv_color_to_32(dsc->color, dsc->opa);
if(col32.alpha < 255U) {
nema_set_blend_fill(NEMA_BL_SIMPLE);
}
else {
nema_set_blend_fill(NEMA_BL_SRC);
}
uint32_t bg_color = nema_rgba(col32.red, col32.green, col32.blue, col32.alpha);
nema_enable_aa(1, 1, 1, 0);
nema_fill_triangle(dsc->p[0].x, dsc->p[0].y, dsc->p[1].x, dsc->p[1].y, dsc->p[2].x, dsc->p[2].y, bg_color);
}
#if LV_USE_NEMA_VG
else {
nema_vg_path_clear(draw_nema_gfx_unit->path);
nema_vg_paint_clear(draw_nema_gfx_unit->paint);
nema_vg_paint_set_type(draw_nema_gfx_unit->paint, NEMA_VG_PAINT_GRAD_LINEAR);
nema_vg_set_blend(NEMA_BL_SRC_OVER | NEMA_BLOP_SRC_PREMULT);
nema_vg_set_fill_rule(NEMA_VG_FILL_EVEN_ODD);
float stops[LV_GRADIENT_MAX_STOPS];
color_var_t colors[LV_GRADIENT_MAX_STOPS];
uint32_t cnt = LV_MAX(dsc->grad.stops_count, LV_GRADIENT_MAX_STOPS);
for(uint8_t i = 0; i < cnt; i++) {
stops[i] = (float)(dsc->grad.stops[i].frac) / 255.f;
colors[i].a = dsc->grad.stops[i].opa;
colors[i].r = dsc->grad.stops[i].color.red;
colors[i].g = dsc->grad.stops[i].color.green;
colors[i].b = dsc->grad.stops[i].color.blue;
}
nema_vg_grad_set(draw_nema_gfx_unit->gradient, cnt, stops, colors);
//Calculate Bounding Box
float min_x = LV_MIN(dsc->p[0].x, dsc->p[1].x);
min_x = LV_MIN(dsc->p[2].x, min_x);
float min_y = LV_MIN(dsc->p[0].y, dsc->p[1].y);
min_y = LV_MIN(dsc->p[2].y, min_y);
float max_x = LV_MAX(dsc->p[0].x, dsc->p[1].x);
max_x = LV_MAX(dsc->p[2].x, max_x);
float max_y = LV_MAX(dsc->p[0].y, dsc->p[1].y);
max_y = LV_MAX(dsc->p[2].y, max_y);
float x0, x1, y0, y1;
if(dsc->grad.dir == LV_GRAD_DIR_HOR) {
x0 = min_x;
x1 = max_x;
y0 = min_y;
y1 = min_y;
}
else {
x0 = min_x;
x1 = min_x;
y0 = min_y;
y1 = max_y;
}
y0 -= (float) layer->buf_area.y1;
y1 -= (float) layer->buf_area.y1;
x0 -= (float) layer->buf_area.x1;
x1 -= (float) layer->buf_area.x1;
uint8_t cmds_polygon[] = {NEMA_VG_PRIM_MOVE, NEMA_VG_PRIM_POLYGON};
float triangle_coords[] = {dsc->p[0].x - layer->buf_area.x1, dsc->p[0].y - layer->buf_area.y1,
4.0f,
dsc->p[1].x - layer->buf_area.x1, dsc->p[1].y - layer->buf_area.y1,
dsc->p[2].x - layer->buf_area.x1, dsc->p[2].y - layer->buf_area.y1
};
nema_enable_aa(0, 0, 0, 0);
nema_vg_paint_set_grad_linear(draw_nema_gfx_unit->paint, draw_nema_gfx_unit->gradient, x0, y0, x1, y1,
NEMA_TEX_CLAMP | NEMA_FILTER_BL);
nema_vg_path_set_shape(draw_nema_gfx_unit->path, 2, cmds_polygon, 7, triangle_coords);
nema_vg_draw_path(draw_nema_gfx_unit->path, draw_nema_gfx_unit->paint);
}
#endif
nema_cl_submit(&(draw_nema_gfx_unit->cl));
}
#endif
@@ -0,0 +1,143 @@
/**
* MIT License
*
* -----------------------------------------------------------------------------
* Copyright (c) 2008-24 Think Silicon Single Member PC
* -----------------------------------------------------------------------------
*
* 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 (including the next paragraph)
* 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.
*
*/
/**
* @file lv_draw_nema_gfx_utils.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_draw_nema_gfx.h"
#if LV_USE_NEMA_GFX
/**********************
* GLOBAL FUNCTIONS
**********************/
uint32_t lv_nemagfx_cf_to_nema(lv_color_format_t cf)
{
switch(cf) {
case LV_COLOR_FORMAT_A1:
return NEMA_A1;
case LV_COLOR_FORMAT_A2:
return NEMA_A2;
case LV_COLOR_FORMAT_A4:
return NEMA_A4;
case LV_COLOR_FORMAT_A8:
return NEMA_A8;
case LV_COLOR_FORMAT_I1:
return NEMA_L1;
case LV_COLOR_FORMAT_I2:
return NEMA_L2;
case LV_COLOR_FORMAT_I4:
return NEMA_L4;
case LV_COLOR_FORMAT_L8:
case LV_COLOR_FORMAT_I8:
return NEMA_L8;
case LV_COLOR_FORMAT_RGB565:
return NEMA_RGB565;
case LV_COLOR_FORMAT_RGB888:
return NEMA_BGR24;
case LV_COLOR_FORMAT_ARGB8888:
return NEMA_BGRA8888;
case LV_COLOR_FORMAT_XRGB8888:
return NEMA_BGRX8888;
case LV_COLOR_FORMAT_NEMA_TSC4:
return NEMA_TSC4;
case LV_COLOR_FORMAT_NEMA_TSC6:
return NEMA_TSC6;
case LV_COLOR_FORMAT_NEMA_TSC6A:
return NEMA_TSC6A;
case LV_COLOR_FORMAT_NEMA_TSC12:
return NEMA_TSC12;
case LV_COLOR_FORMAT_NEMA_TSC12A:
return NEMA_TSC12A;
/*Guard for previous NemaGFX Version*/
#ifdef NEMA_TSC6AP
case LV_COLOR_FORMAT_NEMA_TSC6AP:
return NEMA_TSC6AP;
#endif
default:
return LV_NEMA_GFX_COLOR_FORMAT;
}
}
uint32_t lv_nemagfx_blending_mode(lv_blend_mode_t lv_blend_mode)
{
uint32_t blending_mode;
switch(lv_blend_mode) {
case LV_BLEND_MODE_NORMAL:
blending_mode = NEMA_BL_SIMPLE;
break;
case LV_BLEND_MODE_ADDITIVE:
blending_mode = NEMA_BL_ADD;
break;
case LV_BLEND_MODE_MULTIPLY:
blending_mode = nema_blending_mode(NEMA_BF_DESTCOLOR, NEMA_BF_INVSRCALPHA, NEMA_BLOP_SRC_PREMULT);
break;
default:
blending_mode = NEMA_BL_SIMPLE;
break;
}
return blending_mode;
}
void lv_nemagfx_grad_set(NEMA_VG_GRAD_HANDLE gradient, lv_grad_dsc_t lv_grad, lv_opa_t opa)
{
float stops[LV_GRADIENT_MAX_STOPS];
color_var_t colors[LV_GRADIENT_MAX_STOPS];
uint32_t cnt = LV_MAX(lv_grad.stops_count, LV_GRADIENT_MAX_STOPS);
for(uint8_t i = 0; i < cnt; i++) {
stops[i] = (float)(lv_grad.stops[i].frac) / 255.f;
colors[i].a = LV_OPA_MIX2(lv_grad.stops[i].opa, opa);
colors[i].r = lv_grad.stops[i].color.red;
colors[i].g = lv_grad.stops[i].color.green;
colors[i].b = lv_grad.stops[i].color.blue;
}
nema_vg_grad_set(gradient, cnt, stops, colors);
}
bool lv_nemagfx_is_cf_supported(lv_color_format_t cf)
{
switch(cf) {
case LV_COLOR_FORMAT_ARGB8565:
case LV_COLOR_FORMAT_RGB565A8:
return false;
default:
break;
}
return true;
}
#endif /*LV_USE_NEMA_GFX*/
@@ -0,0 +1,132 @@
/**
* MIT License
*
* -----------------------------------------------------------------------------
* Copyright (c) 2008-24 Think Silicon Single Member PC
* -----------------------------------------------------------------------------
*
* 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 (including the next paragraph)
* 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.
*
*/
/**
* @file lv_draw_nema_gfx.h
*
*/
#ifndef LV_DRAW_NEMA_GFX_UTILS_H
#define LV_DRAW_NEMA_GFX_UTILS_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../../lv_conf_internal.h"
#if LV_USE_NEMA_GFX
#include "../sw/lv_draw_sw.h"
#include "nema_core.h"
#include "nema_utils.h"
#include "nema_error.h"
#include "nema_provisional.h"
#include "nema_vg.h"
/*********************
* DEFINES
*********************/
#ifndef NEMA_VIRT2PHYS
#define NEMA_VIRT2PHYS
#else
uintptr_t NEMA_VIRT2PHYS(void * addr);
#endif
/*Color depth: 1 (1 byte per pixel), 8 (RGB332), 16 (RGB565), 32 (ARGB8888)*/
#if LV_COLOR_DEPTH == 8
#define LV_NEMA_GFX_COLOR_FORMAT NEMA_L8
#define LV_NEMA_GFX_FORMAT_MULTIPLIER 1
#elif LV_COLOR_DEPTH == 16
#define LV_NEMA_GFX_COLOR_FORMAT NEMA_RGB565
#define LV_NEMA_GFX_FORMAT_MULTIPLIER 2
#elif LV_COLOR_DEPTH == 24
#define LV_NEMA_GFX_COLOR_FORMAT NEMA_BGR24
#define LV_NEMA_GFX_FORMAT_MULTIPLIER 3
#elif LV_COLOR_DEPTH == 32
#define LV_NEMA_GFX_COLOR_FORMAT NEMA_BGRA8888
#define LV_NEMA_GFX_FORMAT_MULTIPLIER 4
#else
/*Can't use GPU with other formats*/
#error Selected Color Depth Not Supported
#endif
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Check if `lv_color_format_t` is supported.
* @param cf The LVGL color format
* @return True/false
*/
bool lv_nemagfx_is_cf_supported(lv_color_format_t cf);
/**
* Convert a `lv_color_format_t` to a Nema color format.
* @param cf The LVGL color format
* @return The Nema color format
*/
uint32_t lv_nemagfx_cf_to_nema(lv_color_format_t cf);
/**
* Get NemaGFX blending mode
*
* @param[in] lv_blend_mode The LVGL blend mode
*
* @return NemaGFX blending mode
*
*/
uint32_t lv_nemagfx_blending_mode(lv_blend_mode_t lv_blend_mode);
/**
* Get NemaGFX blending mode
*
* @param[in] gradient NemaGFX Gradient Buffer
*
* @param[in] lv_grad Gradient descriptor
*
* @param[in] opa Descriptor's opacity
*
*/
void lv_nemagfx_grad_set(NEMA_VG_GRAD_HANDLE gradient, lv_grad_dsc_t lv_grad, lv_opa_t opa);
/**********************
* MACROS
**********************/
#endif /*LV_USE_NEMA_GFX*/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_DRAW_NEMA_GFX_UTILS_H*/
@@ -0,0 +1,159 @@
/**
* MIT License
*
* -----------------------------------------------------------------------------
* Copyright (c) 2008-24 Think Silicon Single Member PC
* -----------------------------------------------------------------------------
*
* 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 (including the next paragraph)
* 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.
*
*/
/**
* @file lv_nema_gfx_path.c
*
*/
/*********************
* INCLUDES
*********************/
#include "../../core/lv_refr.h"
#if LV_USE_NEMA_GFX
#if LV_USE_NEMA_VG
#include "lv_nema_gfx_path.h"
/**********************
* GLOBAL FUNCTIONS
**********************/
static int data_point = 0;
static int seg_point = 0;
lv_nema_gfx_path_t * lv_nema_gfx_path_create(void)
{
LV_PROFILER_DRAW_BEGIN;
lv_nema_gfx_path_t * nema_gfx_path = lv_malloc_zeroed(sizeof(lv_nema_gfx_path_t));
LV_ASSERT_MALLOC(nema_gfx_path);
nema_gfx_path->seg = NULL;
nema_gfx_path->data = NULL;
nema_gfx_path->seg_size = 0;
nema_gfx_path->data_size = 0;
data_point = 0;
seg_point = 0;
LV_PROFILER_DRAW_END;
return nema_gfx_path;
}
void lv_nema_gfx_path_alloc(lv_nema_gfx_path_t * nema_gfx_path)
{
LV_PROFILER_DRAW_BEGIN;
nema_gfx_path->path = nema_vg_path_create();
nema_gfx_path->paint = nema_vg_paint_create();
nema_gfx_path->data = (float *) lv_malloc(nema_gfx_path->data_size * sizeof(float));
LV_ASSERT_MALLOC(nema_gfx_path->data);
nema_gfx_path->seg = (uint8_t *) lv_malloc(nema_gfx_path->seg_size * sizeof(uint8_t));
LV_ASSERT_MALLOC(nema_gfx_path->seg);
LV_PROFILER_DRAW_END;
}
void lv_nema_gfx_path_destroy(lv_nema_gfx_path_t * nema_gfx_path)
{
LV_PROFILER_DRAW_BEGIN;
LV_ASSERT_NULL(nema_gfx_path);
if(nema_gfx_path->path != NULL) {
nema_vg_path_destroy(nema_gfx_path->path);
nema_gfx_path->path = NULL;
}
if(nema_gfx_path->paint != NULL) {
nema_vg_paint_destroy(nema_gfx_path->paint);
nema_gfx_path->paint = NULL;
}
if(nema_gfx_path->data != NULL) {
lv_free(nema_gfx_path->data);
nema_gfx_path->data = NULL;
}
if(nema_gfx_path->seg != NULL) {
lv_free(nema_gfx_path->seg);
nema_gfx_path->seg = NULL;
}
lv_free(nema_gfx_path);
LV_PROFILER_DRAW_END;
}
void lv_nema_gfx_path_move_to(lv_nema_gfx_path_t * path, float x, float y)
{
LV_ASSERT_NULL(path);
LV_ASSERT(path->data_size > data_point + 1);
LV_ASSERT(path->seg_size > seg_point);
path->seg[seg_point++] = NEMA_VG_PRIM_MOVE;
path->data[data_point++] = x;
path->data[data_point++] = y;
}
void lv_nema_gfx_path_line_to(lv_nema_gfx_path_t * path, float x, float y)
{
LV_ASSERT_NULL(path);
LV_ASSERT(path->data_size > data_point + 1);
LV_ASSERT(path->seg_size > seg_point);
path->seg[seg_point++] = NEMA_VG_PRIM_LINE;
path->data[data_point++] = x;
path->data[data_point++] = y;
}
void lv_nema_gfx_path_quad_to(lv_nema_gfx_path_t * path, float cx, float cy, float x, float y)
{
LV_ASSERT_NULL(path);
LV_ASSERT(path->data_size > data_point + 3);
LV_ASSERT(path->seg_size > seg_point);
path->seg[seg_point++] = NEMA_VG_PRIM_BEZIER_QUAD;
path->data[data_point++] = cx;
path->data[data_point++] = cy;
path->data[data_point++] = x;
path->data[data_point++] = y;
}
void lv_nema_gfx_path_cubic_to(lv_nema_gfx_path_t * path, float cx1, float cy1, float cx2, float cy2, float x, float y)
{
LV_ASSERT_NULL(path);
LV_ASSERT(path->data_size > data_point + 5);
LV_ASSERT(path->seg_size > seg_point);
path->seg[seg_point++] = NEMA_VG_PRIM_BEZIER_CUBIC;
path->data[data_point++] = cx1;
path->data[data_point++] = cy1;
path->data[data_point++] = cx2;
path->data[data_point++] = cy2;
path->data[data_point++] = x;
path->data[data_point++] = y;
}
void lv_nema_gfx_path_end(lv_nema_gfx_path_t * path)
{
/* Do Path end jobs....whatever*/
seg_point = 0;
data_point = 0;
}
#endif /*LV_USE_NEMA_VG*/
#endif /*LV_USE_NEMA_GFX*/
@@ -0,0 +1,97 @@
/**
* MIT License
*
* -----------------------------------------------------------------------------
* Copyright (c) 2008-24 Think Silicon Single Member PC
* -----------------------------------------------------------------------------
*
* 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 (including the next paragraph)
* 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.
*
*/
/**
* @file lv_nema_gfx_path.h
*
*/
#ifndef LV_NEMA_GFX_PATH_H
#define LV_NEMA_GFX_PATH_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "lv_draw_nema_gfx.h"
#if LV_USE_NEMA_GFX
#if LV_USE_NEMA_VG
/**********************
* TYPEDEFS
**********************/
typedef struct {
NEMA_VG_PATH_HANDLE path;
NEMA_VG_PAINT_HANDLE paint;
float * data;
uint8_t * seg;
uint32_t data_size;
uint32_t seg_size;
} lv_nema_gfx_path_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
lv_nema_gfx_path_t * lv_nema_gfx_path_create(void);
void lv_nema_gfx_path_alloc(lv_nema_gfx_path_t * nema_gfx_path);
void lv_nema_gfx_path_destroy(lv_nema_gfx_path_t * nema_gfx_path);
void lv_nema_gfx_path_move_to(lv_nema_gfx_path_t * nema_gfx_path,
float x, float y);
void lv_nema_gfx_path_line_to(lv_nema_gfx_path_t * nema_gfx_path,
float x, float y);
void lv_nema_gfx_path_quad_to(lv_nema_gfx_path_t * nema_gfx_path,
float cx, float cy,
float x, float y);
void lv_nema_gfx_path_cubic_to(lv_nema_gfx_path_t * nema_gfx_path,
float cx1, float cy1,
float cx2, float cy2,
float x, float y);
void lv_nema_gfx_path_end(lv_nema_gfx_path_t * nema_gfx_path);
#endif /*LV_USE_NEMA_VG*/
#endif /*LV_USE_NEMA_GFX*/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_NEMA_GFX_PATH_H*/
@@ -0,0 +1,92 @@
/**
* @file lv_draw_buf_g2d.c
*
*/
/**
* Copyright 2024 NXP
*
* SPDX-License-Identifier: MIT
*/
/*********************
* INCLUDES
*********************/
#include "lv_draw_g2d.h"
#if LV_USE_DRAW_G2D
#include "../../lv_draw_buf_private.h"
#include "g2d.h"
#include "lv_g2d_buf_map.h"
#include "lv_g2d_utils.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static void * _buf_malloc(size_t size_bytes, lv_color_format_t color_format);
static void _buf_free(void * buf);
static void _invalidate_cache(const lv_draw_buf_t * draw_buf, const lv_area_t * area);
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
void lv_draw_buf_g2d_init_handlers(void)
{
lv_draw_buf_handlers_t * handlers = lv_draw_buf_get_handlers();
handlers->buf_malloc_cb = _buf_malloc;
handlers->buf_free_cb = _buf_free;
handlers->invalidate_cache_cb = _invalidate_cache;
}
/**********************
* STATIC FUNCTIONS
**********************/
static void * _buf_malloc(size_t size_bytes, lv_color_format_t color_format)
{
LV_UNUSED(color_format);
size_bytes += LV_DRAW_BUF_ALIGN - 1;
struct g2d_buf * buf = g2d_alloc(size_bytes, 1);
G2D_ASSERT_MSG(buf, "Failed to alloc buffer.");
g2d_insert_buf_map(buf->buf_vaddr, buf);
return buf->buf_vaddr;
}
static void _buf_free(void * buf)
{
g2d_free_item(buf);
}
static void _invalidate_cache(const lv_draw_buf_t * draw_buf, const lv_area_t * area)
{
LV_UNUSED(area);
struct g2d_buf * buf = g2d_search_buf_map(draw_buf->data);
G2D_ASSERT_MSG(buf, "Failed to find buffer in map.");
g2d_cache_op(buf, G2D_CACHE_FLUSH);
}
#endif /*LV_USE_DRAW_G2D*/
@@ -0,0 +1,336 @@
/**
* @file lv_draw_g2d.c
*
*/
/**
* Copyright 2024 NXP
*
* SPDX-License-Identifier: MIT
*/
#include "lv_draw_g2d.h"
#if LV_USE_DRAW_G2D
#include "../../../misc/lv_area_private.h"
#include "g2d.h"
#include "lv_g2d_buf_map.h"
/*********************
* DEFINES
*********************/
#define DRAW_UNIT_ID_G2D 8
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
/**
* Evaluate a task and set the score and preferred G2D unit.
* Return 1 if task is preferred, 0 otherwise (task is not supported).
*/
static int32_t _g2d_evaluate(lv_draw_unit_t * draw_unit, lv_draw_task_t * task);
/**
* Dispatch a task to the G2D unit.
* Return 1 if task was dispatched, 0 otherwise (task not supported).
*/
static int32_t _g2d_dispatch(lv_draw_unit_t * draw_unit, lv_layer_t * layer);
/**
* Delete the G2D draw unit.
*/
static int32_t _g2d_delete(lv_draw_unit_t * draw_unit);
#if LV_USE_G2D_DRAW_THREAD
static void _g2d_render_thread_cb(void * ptr);
#endif
static void _g2d_execute_drawing(lv_draw_task_t * t);
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
void lv_draw_g2d_init(void)
{
lv_draw_buf_g2d_init_handlers();
lv_draw_g2d_unit_t * draw_g2d_unit = lv_draw_create_unit(sizeof(lv_draw_g2d_unit_t));
draw_g2d_unit->base_unit.evaluate_cb = _g2d_evaluate;
draw_g2d_unit->base_unit.dispatch_cb = _g2d_dispatch;
draw_g2d_unit->base_unit.delete_cb = _g2d_delete;
draw_g2d_unit->base_unit.name = "G2D";
g2d_create_buf_map();
if(g2d_open(&draw_g2d_unit->g2d_handle)) {
LV_LOG_ERROR("g2d_open fail.\n");
}
#if LV_USE_G2D_DRAW_THREAD
lv_draw_sw_thread_dsc_t * thread_dsc = &draw_g2d_unit->thread_dsc;
thread_dsc->idx = 0;
thread_dsc->draw_unit = (void *) draw_g2d_unit;
lv_thread_init(&thread_dsc->thread, "g2ddraw", LV_DRAW_THREAD_PRIO, _g2d_render_thread_cb, LV_DRAW_THREAD_STACK_SIZE,
thread_dsc);
#endif
}
void lv_draw_g2d_deinit(void)
{
g2d_free_buf_map();
}
/**********************
* STATIC FUNCTIONS
**********************/
static inline bool _g2d_dest_cf_supported(lv_color_format_t cf)
{
bool is_cf_supported = false;
switch(cf) {
case LV_COLOR_FORMAT_ARGB8888:
case LV_COLOR_FORMAT_XRGB8888:
is_cf_supported = true;
break;
default:
break;
}
return is_cf_supported;
}
static inline bool _g2d_src_cf_supported(lv_draw_unit_t * drawunit, lv_color_format_t cf)
{
bool is_cf_supported = false;
switch(cf) {
case LV_COLOR_FORMAT_ARGB8888:
case LV_COLOR_FORMAT_XRGB8888:
is_cf_supported = true;
break;
case LV_COLOR_FORMAT_RGB565: {
int32_t hw_pxp = 0;
lv_draw_g2d_unit_t * u = (lv_draw_g2d_unit_t *)drawunit;
g2d_query_hardware(u->g2d_handle, G2D_HARDWARE_PXP, &hw_pxp);
if(!hw_pxp) {
is_cf_supported = true;
}
}
break;
default:
break;
}
return is_cf_supported;
}
static bool _g2d_draw_img_supported(const lv_draw_image_dsc_t * draw_dsc)
{
bool is_tiled = draw_dsc->tile;
/* Tiled image (repeat image) is currently not supported. */
if(is_tiled)
return false;
bool has_recolor = (draw_dsc->recolor_opa > LV_OPA_MIN);
bool has_rotation = (draw_dsc->rotation != 0);
/* Recolor or rotation are not supported. */
if(has_recolor || has_rotation)
return false;
return true;
}
static int32_t _g2d_evaluate(lv_draw_unit_t * u, lv_draw_task_t * t)
{
LV_UNUSED(u);
const lv_draw_dsc_base_t * draw_dsc_base = (lv_draw_dsc_base_t *) t->draw_dsc;
lv_draw_buf_t * draw_buf = draw_dsc_base->layer->draw_buf;
if(!_g2d_dest_cf_supported(draw_dsc_base->layer->color_format))
return 0;
if(draw_buf && !g2d_search_buf_map(draw_buf->data))
return 0;
switch(t->type) {
case LV_DRAW_TASK_TYPE_FILL: {
const lv_draw_fill_dsc_t * draw_dsc = (lv_draw_fill_dsc_t *) t->draw_dsc;
/* Most simple case: just a plain rectangle (no radius, no gradient). */
if((draw_dsc->radius != 0) || (draw_dsc->grad.dir != (lv_grad_dir_t)LV_GRAD_DIR_NONE))
return 0;
if(t->preference_score > 70) {
t->preference_score = 70;
t->preferred_draw_unit_id = DRAW_UNIT_ID_G2D;
}
return 1;
}
case LV_DRAW_TASK_TYPE_IMAGE: {
const lv_draw_image_dsc_t * draw_dsc = (lv_draw_image_dsc_t *) t->draw_dsc;
if(!_g2d_src_cf_supported(u, draw_dsc->header.cf))
return 0;
if(!_g2d_draw_img_supported(draw_dsc))
return 0;
if(t->preference_score > 70) {
t->preference_score = 70;
t->preferred_draw_unit_id = DRAW_UNIT_ID_G2D;
}
return 1;
}
default:
return 0;
}
return 0;
}
static int32_t _g2d_dispatch(lv_draw_unit_t * draw_unit, lv_layer_t * layer)
{
lv_draw_g2d_unit_t * draw_g2d_unit = (lv_draw_g2d_unit_t *) draw_unit;
#if LV_USE_OS
lv_draw_sw_thread_dsc_t * thread_dsc = &draw_g2d_unit->thread_dsc;
/* Return immediately if it's busy with draw task. */
if(thread_dsc->task_act)
return 0;
#else
/* Return immediately if it's busy with draw task. */
if(draw_g2d_unit->task_act)
return 0;
#endif
/* Try to get an ready to draw. */
lv_draw_task_t * t = lv_draw_get_available_task(layer, NULL, DRAW_UNIT_ID_G2D);
if(t == NULL || t->preferred_draw_unit_id != DRAW_UNIT_ID_G2D)
return LV_DRAW_UNIT_IDLE;
if(lv_draw_layer_alloc_buf(layer) == NULL)
return LV_DRAW_UNIT_IDLE;
t->state = LV_DRAW_TASK_STATE_IN_PROGRESS;
t->draw_unit = draw_unit;
#if LV_USE_G2D_DRAW_THREAD
thread_dsc->task_act = t;
/* Let the render thread work. */
if(thread_dsc->inited)
lv_thread_sync_signal(&thread_dsc->sync);
#else
draw_g2d_unit->task_act = t;
_g2d_execute_drawing(t);
draw_g2d_unit->task_act->state = LV_DRAW_TASK_STATE_READY;
draw_g2d_unit->task_act = NULL;
/* The draw unit is free now. Request a new dispatching as it can get a new task. */
lv_draw_dispatch_request();
#endif
return 1;
}
static int32_t _g2d_delete(lv_draw_unit_t * draw_unit)
{
lv_draw_g2d_unit_t * draw_g2d_unit = (lv_draw_g2d_unit_t *) draw_unit;
lv_result_t res = LV_RESULT_OK;
#if LV_USE_G2D_DRAW_THREAD
lv_draw_sw_thread_dsc_t * thread_dsc = &draw_g2d_unit->thread_dsc;
LV_LOG_INFO("Cancel G2D draw thread.");
thread_dsc->exit_status = true;
if(thread_dsc->inited)
lv_thread_sync_signal(&thread_dsc->sync);
res = lv_thread_delete(&thread_dsc->thread);
#endif
g2d_close(draw_g2d_unit->g2d_handle);
return res;
}
static void _g2d_execute_drawing(lv_draw_task_t * t)
{
lv_layer_t * layer = t->target_layer;
lv_draw_buf_t * draw_buf = layer->draw_buf;
/* Invalidate only the drawing area */
lv_draw_buf_invalidate_cache(draw_buf, NULL);
switch(t->type) {
case LV_DRAW_TASK_TYPE_FILL:
lv_draw_g2d_fill(t);
break;
case LV_DRAW_TASK_TYPE_IMAGE:
lv_draw_g2d_img(t);
break;
default:
break;
}
}
#if LV_USE_G2D_DRAW_THREAD
static void _g2d_render_thread_cb(void * ptr)
{
lv_draw_sw_thread_dsc_t * thread_dsc = ptr;
lv_thread_sync_init(&thread_dsc->sync);
thread_dsc->inited = true;
while(1) {
/* Wait for sync if there is no task set. */
while(thread_dsc->task_act == NULL) {
if(thread_dsc->exit_status)
break;
lv_thread_sync_wait(&thread_dsc->sync);
}
if(thread_dsc->exit_status) {
LV_LOG_INFO("Ready to exit G2D draw thread.");
break;
}
_g2d_execute_drawing(thread_dsc->task_act);
/* Signal the ready state to dispatcher. */
thread_dsc->task_act->state = LV_DRAW_TASK_STATE_READY;
/* Cleanup. */
thread_dsc->task_act = NULL;
/* The draw unit is free now. Request a new dispatching as it can get a new task. */
lv_draw_dispatch_request();
}
thread_dsc->inited = false;
lv_thread_sync_delete(&thread_dsc->sync);
LV_LOG_INFO("Exit G2D draw thread.");
}
#endif
#endif /*LV_USE_DRAW_G2D*/
@@ -0,0 +1,71 @@
/**
* @file lv_draw_g2d.h
*
*/
/**
* Copyright 2024 NXP
*
* SPDX-License-Identifier: MIT
*/
#ifndef LV_DRAW_G2D_H
#define LV_DRAW_G2D_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../../../lv_conf_internal.h"
#if LV_USE_DRAW_G2D
#include "../../sw/lv_draw_sw_private.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
typedef struct lv_draw_g2d_unit {
lv_draw_unit_t base_unit;
#if LV_USE_OS
lv_draw_sw_thread_dsc_t thread_dsc;
#else
lv_draw_task_t * task_act;
#endif
void * g2d_handle;
} lv_draw_g2d_unit_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
void lv_draw_g2d_init(void);
void lv_draw_g2d_deinit(void);
void lv_draw_buf_g2d_init_handlers(void);
void lv_draw_g2d_fill(lv_draw_task_t * t);
void lv_draw_g2d_img(lv_draw_task_t * t);
/**********************
* MACROS
**********************/
#endif /*LV_USE_DRAW_G2D*/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_DRAW_G2D_H*/
@@ -0,0 +1,178 @@
/**
* @file lv_draw_g2d_fill.c
*
*/
/**
* Copyright 2024 NXP
*
* SPDX-License-Identifier: MIT
*/
/*********************
* INCLUDES
*********************/
#include "lv_draw_g2d.h"
#if LV_USE_DRAW_G2D
#include <stdlib.h>
#include "g2d.h"
#include "../../../misc/lv_area_private.h"
#include "lv_g2d_buf_map.h"
#include "lv_g2d_utils.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
/* Blit simple w/ opa and alpha channel */
static void _g2d_fill(void * g2d_handle, struct g2d_surface * dst_surf);
static void _g2d_fill_with_opa(void * g2d_handle, struct g2d_surface * dst_surf, struct g2d_surface * src_surf);
static void _g2d_set_src_surf(struct g2d_surface * src_surf, struct g2d_buf * buf, const lv_area_t * area,
lv_color_t color, lv_opa_t opa);
static void _g2d_set_dst_surf(struct g2d_surface * dst_surf, struct g2d_buf * buf, const lv_area_t * area,
int32_t stride, lv_color_t color);
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
void lv_draw_g2d_fill(lv_draw_task_t * t)
{
lv_draw_fill_dsc_t * dsc = t->draw_dsc;
if(dsc->opa <= (lv_opa_t)LV_OPA_MIN)
return;
lv_draw_g2d_unit_t * u = (lv_draw_g2d_unit_t *)t->draw_unit;
lv_layer_t * layer = t->target_layer;
lv_draw_buf_t * draw_buf = layer->draw_buf;
lv_area_t * coords = &t->area;
lv_area_t rel_coords;
lv_area_copy(&rel_coords, coords);
lv_area_move(&rel_coords, -layer->buf_area.x1, -layer->buf_area.y1);
lv_area_t rel_clip_area;
lv_area_copy(&rel_clip_area, &t->clip_area);
lv_area_move(&rel_clip_area, -layer->buf_area.x1, -layer->buf_area.y1);
lv_area_t blend_area;
if(!lv_area_intersect(&blend_area, &rel_coords, &rel_clip_area))
return; /*Fully clipped, nothing to do*/
/* G2D takes stride in pixels. */
int32_t stride = draw_buf->header.stride / (lv_color_format_get_bpp(draw_buf->header.cf) / 8);
/* Destination buffer */
struct g2d_buf * dst_buf = g2d_search_buf_map(draw_buf->data);
bool has_opa = (dsc->opa < (lv_opa_t)LV_OPA_MAX);
struct g2d_surface dst_surf;
_g2d_set_dst_surf(&dst_surf, dst_buf, &blend_area, stride, dsc->color);
if(has_opa) {
struct g2d_buf * tmp_buf = g2d_alloc(lv_area_get_width(&blend_area) * lv_area_get_height(&blend_area) * sizeof(
lv_color32_t), 1);
G2D_ASSERT_MSG(tmp_buf, "Failed to alloc temporary buffer.");
struct g2d_surface src_surf;
_g2d_set_src_surf(&src_surf, tmp_buf, &blend_area, dsc->color, dsc->opa);
_g2d_fill_with_opa(u->g2d_handle, &dst_surf, &src_surf);
g2d_free(tmp_buf);
}
else {
_g2d_fill(u->g2d_handle, &dst_surf);
}
}
/**********************
* STATIC FUNCTIONS
**********************/
static void _g2d_set_src_surf(struct g2d_surface * src_surf, struct g2d_buf * buf, const lv_area_t * area,
lv_color_t color, lv_opa_t opa)
{
int32_t width = lv_area_get_width(area);
int32_t height = lv_area_get_height(area);
src_surf->format = G2D_RGBA8888;
src_surf->left = 0;
src_surf->top = 0;
src_surf->right = width;
src_surf->bottom = height;
src_surf->stride = width;
src_surf->width = width;
src_surf->height = height;
src_surf->planes[0] = buf->buf_paddr;
src_surf->rot = G2D_ROTATION_0;
src_surf->clrcolor = g2d_rgba_to_u32(color);
src_surf->global_alpha = opa;
src_surf->blendfunc = G2D_ONE | G2D_PRE_MULTIPLIED_ALPHA;
}
static void _g2d_set_dst_surf(struct g2d_surface * dst_surf, struct g2d_buf * buf, const lv_area_t * area,
int32_t stride, lv_color_t color)
{
int32_t width = lv_area_get_width(area);
int32_t height = lv_area_get_height(area);
dst_surf->format = G2D_RGBA8888;
dst_surf->left = area->x1;
dst_surf->top = area->y1;
dst_surf->right = area->x1 + width;
dst_surf->bottom = area->y1 + height;
dst_surf->stride = stride;
dst_surf->width = width;
dst_surf->height = height;
dst_surf->planes[0] = buf->buf_paddr;
dst_surf->rot = G2D_ROTATION_0;
dst_surf->clrcolor = g2d_rgba_to_u32(color);
dst_surf->global_alpha = 0xff;
dst_surf->blendfunc = G2D_ONE_MINUS_SRC_ALPHA | G2D_PRE_MULTIPLIED_ALPHA;
}
static void _g2d_fill_with_opa(void * g2d_handle, struct g2d_surface * dst_surf, struct g2d_surface * src_surf)
{
g2d_clear(g2d_handle, src_surf);
g2d_enable(g2d_handle, G2D_BLEND);
g2d_enable(g2d_handle, G2D_GLOBAL_ALPHA);
g2d_blit(g2d_handle, src_surf, dst_surf);
g2d_finish(g2d_handle);
g2d_disable(g2d_handle, G2D_GLOBAL_ALPHA);
g2d_disable(g2d_handle, G2D_BLEND);
}
static void _g2d_fill(void * g2d_handle, struct g2d_surface * dst_surf)
{
g2d_clear(g2d_handle, dst_surf);
g2d_finish(g2d_handle);
}
#endif /*LV_USE_DRAW_G2D*/
@@ -0,0 +1,214 @@
/**
* @file lv_draw_g2d_img.c
*
*/
/**
* Copyright 2024 NXP
*
* SPDX-License-Identifier: MIT
*/
/*********************
* INCLUDES
*********************/
#include "lv_draw_g2d.h"
#if LV_USE_DRAW_G2D
#include <stdlib.h>
#include <math.h>
#include "g2d.h"
#include "../../../misc/lv_area_private.h"
#include "lv_g2d_utils.h"
#include "lv_g2d_buf_map.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static struct g2d_buf * _g2d_handle_src_buf(const lv_image_dsc_t * data);
static void _g2d_set_src_surf(struct g2d_surface * src_surf, struct g2d_buf * buf, const lv_area_t * area,
int32_t stride, lv_color_format_t cf, lv_opa_t opa);
static void _g2d_set_dst_surf(struct g2d_surface * dst_surf, struct g2d_buf * buf, const lv_area_t * area,
int32_t stride, lv_color_format_t cf, const lv_draw_image_dsc_t * dsc);
/* Blit simple w/ opa and alpha channel */
static void _g2d_blit(void * g2d_handle, struct g2d_surface * dst_surf, struct g2d_surface * src_surf);
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
void lv_draw_g2d_img(lv_draw_task_t * t)
{
lv_draw_image_dsc_t * dsc = t->draw_dsc;
if(dsc->opa <= (lv_opa_t)LV_OPA_MIN)
return;
lv_draw_g2d_unit_t * u = (lv_draw_g2d_unit_t *)t->draw_unit;
lv_layer_t * layer = t->target_layer;
lv_draw_buf_t * draw_buf = layer->draw_buf;
const lv_image_dsc_t * img_dsc = dsc->src;
lv_area_t * coords = &t->area;
lv_area_t rel_coords;
lv_area_copy(&rel_coords, coords);
lv_area_move(&rel_coords, -layer->buf_area.x1, -layer->buf_area.y1);
lv_area_t clip_area;
lv_area_copy(&clip_area, &t->clip_area);
lv_area_move(&clip_area, -layer->buf_area.x1, -layer->buf_area.y1);
lv_area_t blend_area;
bool has_transform = (dsc->rotation != 0 || dsc->scale_x != LV_SCALE_NONE || dsc->scale_y != LV_SCALE_NONE);
if(has_transform)
lv_area_copy(&blend_area, &rel_coords);
else if(!lv_area_intersect(&blend_area, &rel_coords, &clip_area))
return; /*Fully clipped, nothing to do*/
lv_area_t src_area;
src_area.x1 = blend_area.x1 - (coords->x1 - layer->buf_area.x1);
src_area.y1 = blend_area.y1 - (coords->y1 - layer->buf_area.y1);
src_area.x2 = src_area.x1 + lv_area_get_width(&blend_area);
src_area.y2 = src_area.y1 + lv_area_get_height(&blend_area);
int32_t src_stride = img_dsc->header.stride / (lv_color_format_get_bpp(img_dsc->header.cf) / 8);
lv_color_format_t src_cf = img_dsc->header.cf;
/* Source image */
struct g2d_buf * src_buf = _g2d_handle_src_buf(img_dsc);
/* Destination buffer */
struct g2d_buf * dst_buf = g2d_search_buf_map(draw_buf->data);
/* G2D takes stride in pixels. */
int32_t dest_stride = draw_buf->header.stride / (lv_color_format_get_bpp(draw_buf->header.cf) / 8);
lv_color_format_t dest_cf = draw_buf->header.cf;
struct g2d_surface src_surf;
struct g2d_surface dst_surf;
_g2d_set_src_surf(&src_surf, src_buf, &src_area, src_stride, src_cf, dsc->opa);
_g2d_set_dst_surf(&dst_surf, dst_buf, &blend_area, dest_stride, dest_cf, dsc);
_g2d_blit(u->g2d_handle, &dst_surf, &src_surf);
}
/**********************
* STATIC FUNCTIONS
**********************/
static struct g2d_buf * _g2d_handle_src_buf(const lv_image_dsc_t * img_dsc)
{
struct g2d_buf * src_buf = g2d_search_buf_map((void *)img_dsc->data);
if(src_buf == NULL) {
src_buf = g2d_alloc(img_dsc->data_size, 1);
G2D_ASSERT_MSG(src_buf, "Failed to alloc source buffer.");
memcpy((uint8_t *)src_buf->buf_vaddr, img_dsc->data, img_dsc->data_size);
g2d_cache_op(src_buf, G2D_CACHE_FLUSH);
g2d_insert_buf_map((void *)img_dsc->data, src_buf);
}
return src_buf;
}
static void _g2d_set_src_surf(struct g2d_surface * src_surf, struct g2d_buf * buf, const lv_area_t * area,
int32_t stride, lv_color_format_t cf, lv_opa_t opa)
{
src_surf->format = g2d_get_buf_format(cf);
src_surf->left = area->x1;
src_surf->top = area->y1;
src_surf->right = area->x2;
src_surf->bottom = area->y2;
src_surf->stride = stride;
src_surf->width = area->x2 - area->x1;
src_surf->height = area->y2 - area->y1;
src_surf->planes[0] = buf->buf_paddr;
src_surf->rot = G2D_ROTATION_0;
src_surf->clrcolor = g2d_rgba_to_u32(lv_color_black());
src_surf->global_alpha = opa;
src_surf->blendfunc = G2D_ONE | G2D_PRE_MULTIPLIED_ALPHA;
}
static void _g2d_set_dst_surf(struct g2d_surface * dst_surf, struct g2d_buf * buf, const lv_area_t * area,
int32_t stride, lv_color_format_t cf, const lv_draw_image_dsc_t * dsc)
{
int32_t width = lv_area_get_width(area);
int32_t height = lv_area_get_height(area);
lv_point_t pivot = dsc->pivot;
/*The offsets are now relative to the transformation result with pivot ULC*/
int32_t piv_offset_x = 0;
int32_t piv_offset_y = 0;
int32_t trim_x = 0;
int32_t trim_y = 0;
int32_t dest_w;
int32_t dest_h;
float fp_scale_x = (float)dsc->scale_x / LV_SCALE_NONE;
float fp_scale_y = (float)dsc->scale_y / LV_SCALE_NONE;
int32_t int_scale_x = (int32_t)fp_scale_x;
int32_t int_scale_y = (int32_t)fp_scale_y;
/*Any scale_factor in (k, k + 1] will result in a trim equal to k*/
trim_x = (fp_scale_x == int_scale_x) ? int_scale_x - 1 : int_scale_x;
trim_y = (fp_scale_y == int_scale_y) ? int_scale_y - 1 : int_scale_y;
dest_w = width * fp_scale_x + trim_x;
dest_h = height * fp_scale_y + trim_y;
/*Final pivot offset = scale_factor * rotation_pivot_offset + scaling_pivot_offset*/
piv_offset_x = floor(fp_scale_x * piv_offset_x) - floor((fp_scale_x - 1) * pivot.x);
piv_offset_y = floor(fp_scale_y * piv_offset_y) - floor((fp_scale_y - 1) * pivot.y);
dst_surf->format = g2d_get_buf_format(cf);
dst_surf->left = area->x1 + piv_offset_x;
dst_surf->top = area->y1 + piv_offset_y;
dst_surf->right = area->x1 + piv_offset_x + dest_w - trim_x;
dst_surf->bottom = area->y1 + piv_offset_y + dest_h - trim_y;
dst_surf->stride = stride;
dst_surf->width = dest_w - trim_x;
dst_surf->height = dest_h - trim_y;
dst_surf->planes[0] = buf->buf_paddr;
dst_surf->rot = G2D_ROTATION_0;
dst_surf->clrcolor = g2d_rgba_to_u32(lv_color_black());
dst_surf->global_alpha = 0xff;
dst_surf->blendfunc = G2D_ONE_MINUS_SRC_ALPHA | G2D_PRE_MULTIPLIED_ALPHA;
}
static void _g2d_blit(void * g2d_handle, struct g2d_surface * dst_surf, struct g2d_surface * src_surf)
{
g2d_enable(g2d_handle, G2D_BLEND);
g2d_enable(g2d_handle, G2D_GLOBAL_ALPHA);
g2d_blit(g2d_handle, src_surf, dst_surf);
g2d_finish(g2d_handle);
g2d_disable(g2d_handle, G2D_GLOBAL_ALPHA);
g2d_disable(g2d_handle, G2D_BLEND);
}
#endif /*LV_USE_DRAW_G2D*/
@@ -0,0 +1,247 @@
/**
* @file lv_g2d_buf_map.c
*
*/
/**
* Copyright 2024 NXP
*
* SPDX-License-Identifier: MIT
*/
#include "lv_g2d_buf_map.h"
#if LV_USE_DRAW_G2D
#include <stdio.h>
#include "lv_g2d_utils.h"
#include "g2d.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static unsigned long _map_hash_function(void * ptr);
static lv_map_item_t * _map_create_item(void * key, struct g2d_buf * value);
static void _map_free_item(lv_map_item_t * item);
static void _handle_collision(unsigned long index, lv_map_item_t * item);
/**********************
* STATIC VARIABLES
**********************/
static lv_buf_map_t * table;
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
void g2d_create_buf_map(void)
{
table = (lv_buf_map_t *) lv_malloc(sizeof(lv_buf_map_t));
table->size = LV_G2D_HASH_TABLE_SIZE;
table->count = 0;
table->items = (lv_map_item_t **) lv_malloc_zeroed(table->size * sizeof(lv_map_item_t *));
table->overflow_list = (lv_array_t **) lv_malloc_zeroed(table->size * sizeof(lv_array_t *));
}
void g2d_free_buf_map(void)
{
for(int i = 0; i < table->size; i++) {
if(table->overflow_list[i]) {
lv_array_deinit(table->overflow_list[i]);
lv_free(table->overflow_list[i]);
}
lv_map_item_t * item = table->items[i];
if(item != NULL)
_map_free_item(item);
}
lv_free(table->items);
lv_free(table->overflow_list);
lv_free(table);
}
void g2d_insert_buf_map(void * key, struct g2d_buf * value)
{
lv_map_item_t * item = _map_create_item(key, value);
int index = _map_hash_function(key);
if(table->items[index] == NULL) {
/* Key not found. */
if(table->count == table->size) {
/* Table is full. */
_map_free_item(item);
G2D_ASSERT_MSG(false, "Hash table is full.");
return;
}
}
else {
if(table->items[index]->key == key) {
/* Key already exists, update value. */
table->items[index]->value = value;
return;
}
else {
/* Handle the collision */
_handle_collision(index, item);
return;
}
}
/* Insert item. */
table->items[index] = item;
table->count++;
}
struct g2d_buf * g2d_search_buf_map(void * key)
{
int index = _map_hash_function(key);
lv_map_item_t * item = table->items[index];
lv_array_t * list = (lv_array_t *)table->overflow_list[index];
if(item == NULL)
return NULL;
if(item->key == key)
return item->value;
if(list == NULL)
return NULL;
for(uint32_t i = 0; i < lv_array_size(list); i++) {
item = (lv_map_item_t *)lv_array_at(list, i);
if(item->key == key)
return item->value;
}
return NULL;
}
void g2d_free_item(void * key)
{
/* Delete an item from the table. */
int index = _map_hash_function(key);
lv_map_item_t * item = table->items[index];
lv_array_t * list = (lv_array_t *)table->overflow_list[index];
if(item == NULL) {
return;
}
else if(list == NULL && item->key == key) {
/* No collision chain, just remove item. */
table->items[index] = NULL;
_map_free_item(item);
table->count--;
return;
}
else if(list != NULL) {
/* Collision chain exists. */
for(uint32_t i = 0; i < lv_array_size(list); i++) {
item = (lv_map_item_t *)lv_array_at(list, i);
if(item->key == key) {
lv_array_remove(list, i);
return;
}
}
}
}
void g2d_print_table(void)
{
LV_LOG("\nHash Table\n-------------------\n");
for(int i = 0; i < table->size; i++) {
if(table->items[i]) {
LV_LOG("Index:%d, Key:%p, Value:%p\n", i, table->items[i] -> key, (void *)table->items[i]->value);
if(table->overflow_list[i]) {
for(uint32_t j = 0 ; j < lv_array_size(table->overflow_list[i]); j++) {
lv_map_item_t * item = (lv_map_item_t *)lv_array_at(table->overflow_list[i], j);
LV_LOG("Index:%d, Key:%p, Value:%p\n", i, item -> key, (void *)item->value);
}
}
}
}
LV_LOG("-------------------\n\n");
}
/**********************
* STATIC FUNCTIONS
**********************/
static unsigned long _map_hash_function(void * ptr)
{
unsigned long i = 0;
char str[64];
G2D_ASSERT_MSG(ptr, "Key is null.");
sprintf(str, "%p", ptr);
for(int j = 0; str[j]; j++)
i += str[j];
return i % LV_G2D_HASH_TABLE_SIZE;
}
static void _handle_collision(unsigned long index, lv_map_item_t * item)
{
if(table->overflow_list[index] == NULL) {
/* Create the list. */
lv_array_t * list = (lv_array_t *) lv_malloc(sizeof(lv_array_t));;
lv_array_init(list, LV_ARRAY_DEFAULT_CAPACITY, sizeof(lv_map_item_t));
lv_array_push_back(list, item);
table->overflow_list[index] = list;
return;
}
else {
lv_array_t * list = (lv_array_t *)table->overflow_list[index];
for(uint32_t i = 0; i < lv_array_size(list); i++) {
lv_map_item_t * it = (lv_map_item_t *)lv_array_at(list, i);
if(it->key == item->key) {
/* Key exists, update value. */
it->value = item->value;
return;
}
}
/* Insert to the list. */
lv_array_push_back(table->overflow_list[index], item);
return;
}
}
static lv_map_item_t * _map_create_item(void * key, struct g2d_buf * value)
{
lv_map_item_t * item = (lv_map_item_t *) lv_malloc(sizeof(lv_map_item_t));
G2D_ASSERT_MSG(item, "Failed to alloc item.");
item->key = key;
item->value = value;
return item;
}
static void _map_free_item(lv_map_item_t * item)
{
/* Also free the g2d_buf. */
g2d_free(item->value);
item->key = NULL;
item->value = NULL;
lv_free(item);
item = NULL;
}
#endif /*LV_USE_DRAW_G2D*/
@@ -0,0 +1,81 @@
/**
* @file lv_g2d_buf_map.h
*
*/
/**
* Copyright 2024 NXP
*
* SPDX-License-Identifier: MIT
*/
#ifndef LV_G2D_BUF_MAP_H
#define LV_G2D_BUF_MAP_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../../../lv_conf_internal.h"
#if LV_USE_DRAW_G2D
#include "../../../misc/lv_array.h"
#include <string.h>
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/* Map item definition. */
typedef struct lv_map_item {
/* Virtual address buffer. */
void * key;
struct g2d_buf * value;
} lv_map_item_t;
/*Buf map definition. */
typedef struct lv_buf_map {
lv_map_item_t ** items;
lv_array_t ** overflow_list;
int size;
int count;
} lv_buf_map_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
void g2d_create_buf_map(void);
void g2d_free_buf_map(void);
void g2d_insert_buf_map(void * key, struct g2d_buf * value);
struct g2d_buf * g2d_search_buf_map(void * key);
void g2d_free_item(void * key);
void g2d_print_table(void);
/**********************
* MACROS
**********************/
#endif /*LV_USE_DRAW_G2D*/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /* LV_G2D_BUF_MAP_H */
@@ -0,0 +1,97 @@
/**
* @file lv_g2d_utils.c
*
*/
/**
* Copyright 2024 NXP
*
* SPDX-License-Identifier: MIT
*/
/*********************
* INCLUDES
*********************/
#include "lv_g2d_utils.h"
#if LV_USE_DRAW_G2D
#include "lv_g2d_buf_map.h"
#include "lv_draw_g2d.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
g2d_format_t g2d_get_buf_format(lv_color_format_t cf)
{
g2d_format_t color_f = G2D_RGB565;
switch(cf) {
case LV_COLOR_FORMAT_RGB565:
color_f = G2D_RGB565;
break;
case LV_COLOR_FORMAT_ARGB8888:
color_f = G2D_BGRA8888;
break;
case LV_COLOR_FORMAT_XRGB8888:
color_f = G2D_BGRX8888;
break;
case LV_COLOR_FORMAT_RGB888:
color_f = G2D_BGR888;
break;
case LV_COLOR_FORMAT_NV12:
color_f = G2D_NV12;
break;
case LV_COLOR_FORMAT_I420:
color_f = G2D_I420;
break;
case LV_COLOR_FORMAT_NV21:
color_f = G2D_NV21;
break;
case LV_COLOR_FORMAT_YUY2:
color_f = G2D_YUYV;
break;
case LV_COLOR_FORMAT_UYVY:
color_f = G2D_UYVY;
break;
default:
G2D_ASSERT_MSG(false, "Unsupported color format.");
break;
}
return color_f;
}
uint32_t g2d_rgba_to_u32(lv_color_t color)
{
return (uint32_t)((color.red) + (color.green << 8) + (color.blue << 16) + ((uint32_t)0xff << 24));
}
int32_t g2d_get_buf_fd(const lv_draw_buf_t * draw_buf)
{
struct g2d_buf * buf = g2d_search_buf_map(draw_buf->data);
G2D_ASSERT_MSG(buf, "Failed to find buffer in map.");
return g2d_buf_export_fd(buf);
}
/**********************
* STATIC FUNCTIONS
**********************/
#endif /*LV_USE_DRAW_G2D*/
@@ -0,0 +1,72 @@
/**
* @file lv_g2d_utils.h
*
*/
/**
* Copyright 2024 NXP
*
* SPDX-License-Identifier: MIT
*/
#ifndef LV_G2D_UTILS_H
#define LV_G2D_UTILS_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../../../lv_conf_internal.h"
#if LV_USE_DRAW_G2D
#include "../../sw/lv_draw_sw_private.h"
#include "g2d.h"
#include "g2dExt.h"
/*********************
* DEFINES
*********************/
#if LV_USE_G2D_ASSERT
#define G2D_ASSERT(expr) LV_ASSERT(expr)
#else
#define G2D_ASSERT(expr)
#endif
#define G2D_ASSERT_MSG(expr, msg) \
do { \
if(!(expr)) { \
LV_LOG_ERROR(msg); \
G2D_ASSERT(false); \
} \
} while(0)
/**********************
* TYPEDEFS
**********************/
typedef enum g2d_format g2d_format_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
g2d_format_t g2d_get_buf_format(lv_color_format_t cf);
uint32_t g2d_rgba_to_u32(lv_color_t color);
int32_t g2d_get_buf_fd(const lv_draw_buf_t * draw_buf);
/**********************
* MACROS
**********************/
#endif /*LV_USE_DRAW_G2D*/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_G2D_UTILS_H*/

Some files were not shown because too many files have changed in this diff Show More