init lvgl code

This commit is contained in:
ShallowGreen123
2022-08-07 15:24:16 +08:00
parent d5ebbd8eb9
commit 77ddc13604
1963 changed files with 719922 additions and 318 deletions

View File

@@ -0,0 +1,20 @@
CSRCS += lv_disp.c
CSRCS += lv_group.c
CSRCS += lv_indev.c
CSRCS += lv_indev_scroll.c
CSRCS += lv_obj.c
CSRCS += lv_obj_class.c
CSRCS += lv_obj_draw.c
CSRCS += lv_obj_pos.c
CSRCS += lv_obj_scroll.c
CSRCS += lv_obj_style.c
CSRCS += lv_obj_style_gen.c
CSRCS += lv_obj_tree.c
CSRCS += lv_event.c
CSRCS += lv_refr.c
CSRCS += lv_theme.c
DEPPATH += --dep-path $(LVGL_DIR)/$(LVGL_DIR_NAME)/src/core
VPATH += :$(LVGL_DIR)/$(LVGL_DIR_NAME)/src/core
CFLAGS += "-I$(LVGL_DIR)/$(LVGL_DIR_NAME)/src/core"

View File

@@ -0,0 +1,490 @@
/**
* @file lv_disp.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_disp.h"
#include "../misc/lv_math.h"
#include "../core/lv_refr.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static void scr_load_anim_start(lv_anim_t * a);
static void opa_scale_anim(void * obj, int32_t v);
static void set_x_anim(void * obj, int32_t v);
static void set_y_anim(void * obj, int32_t v);
static void scr_anim_ready(lv_anim_t * a);
static bool is_out_anim(lv_scr_load_anim_t a);
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
/**
* Return with a pointer to the active screen
* @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_scr_load()')
*/
lv_obj_t * lv_disp_get_scr_act(lv_disp_t * disp)
{
if(!disp) disp = lv_disp_get_default();
if(!disp) {
LV_LOG_WARN("no display registered to get its active screen");
return NULL;
}
return disp->act_scr;
}
/**
* 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_disp_get_scr_prev(lv_disp_t * disp)
{
if(!disp) disp = lv_disp_get_default();
if(!disp) {
LV_LOG_WARN("no display registered to get its previous screen");
return NULL;
}
return disp->prev_scr;
}
/**
* Make a screen active
* @param scr pointer to a screen
*/
void lv_disp_load_scr(lv_obj_t * scr)
{
lv_disp_t * d = lv_obj_get_disp(scr);
if(!d) return; /*Shouldn't happen, just to be sure*/
lv_obj_t * old_scr = d->act_scr;
if(d->act_scr) lv_event_send(old_scr, LV_EVENT_SCREEN_UNLOAD_START, NULL);
if(d->act_scr) lv_event_send(scr, LV_EVENT_SCREEN_LOAD_START, NULL);
d->act_scr = scr;
if(d->act_scr) lv_event_send(scr, LV_EVENT_SCREEN_LOADED, NULL);
if(d->act_scr) lv_event_send(old_scr, LV_EVENT_SCREEN_UNLOADED, NULL);
lv_obj_invalidate(scr);
}
/**
* Return with the top layer. (Same on every screen 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 (transparent screen sized lv_obj)
*/
lv_obj_t * lv_disp_get_layer_top(lv_disp_t * disp)
{
if(!disp) disp = lv_disp_get_default();
if(!disp) {
LV_LOG_WARN("lv_layer_top: no display registered to get its top layer");
return NULL;
}
return disp->top_layer;
}
/**
* Return with the sys. layer. (Same on every 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 (transparent screen sized lv_obj)
*/
lv_obj_t * lv_disp_get_layer_sys(lv_disp_t * disp)
{
if(!disp) disp = lv_disp_get_default();
if(!disp) {
LV_LOG_WARN("lv_layer_sys: no display registered to get its sys. layer");
return NULL;
}
return disp->sys_layer;
}
/**
* Set the theme of a display
* @param disp pointer to a display
*/
void lv_disp_set_theme(lv_disp_t * disp, lv_theme_t * th)
{
if(!disp) disp = lv_disp_get_default();
if(!disp) {
LV_LOG_WARN("no display registered");
return;
}
disp->theme = th;
if(disp->screen_cnt == 3 &&
lv_obj_get_child_cnt(disp->screens[0]) == 0 &&
lv_obj_get_child_cnt(disp->screens[1]) == 0 &&
lv_obj_get_child_cnt(disp->screens[2]) == 0) {
lv_theme_apply(disp->screens[0]);
}
}
/**
* Get the theme of a display
* @param disp pointer to a display
* @return the display's theme (can be NULL)
*/
lv_theme_t * lv_disp_get_theme(lv_disp_t * disp)
{
if(disp == NULL) disp = lv_disp_get_default();
return disp->theme;
}
/**
* Set the background color of a display
* @param disp pointer to a display
* @param color color of the background
*/
void lv_disp_set_bg_color(lv_disp_t * disp, lv_color_t color)
{
if(!disp) disp = lv_disp_get_default();
if(!disp) {
LV_LOG_WARN("no display registered");
return;
}
disp->bg_color = color;
lv_area_t a;
lv_area_set(&a, 0, 0, lv_disp_get_hor_res(disp) - 1, lv_disp_get_ver_res(disp) - 1);
_lv_inv_area(disp, &a);
}
/**
* Set the background image of a display
* @param disp pointer to a display
* @param img_src path to file or pointer to an `lv_img_dsc_t` variable
*/
void lv_disp_set_bg_image(lv_disp_t * disp, const void * img_src)
{
if(!disp) disp = lv_disp_get_default();
if(!disp) {
LV_LOG_WARN("no display registered");
return;
}
disp->bg_img = img_src;
lv_area_t a;
lv_area_set(&a, 0, 0, lv_disp_get_hor_res(disp) - 1, lv_disp_get_ver_res(disp) - 1);
_lv_inv_area(disp, &a);
}
/**
* Set opacity of the background
* @param disp pointer to a display
* @param opa opacity (0..255)
*/
void lv_disp_set_bg_opa(lv_disp_t * disp, lv_opa_t opa)
{
if(!disp) disp = lv_disp_get_default();
if(!disp) {
LV_LOG_WARN("no display registered");
return;
}
disp->bg_opa = opa;
lv_area_t a;
lv_area_set(&a, 0, 0, lv_disp_get_hor_res(disp) - 1, lv_disp_get_ver_res(disp) - 1);
_lv_inv_area(disp, &a);
}
/**
* Switch screen with animation
* @param scr pointer to the new screen to load
* @param anim_type type of the animation from `lv_scr_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_scr_load_anim(lv_obj_t * new_scr, lv_scr_load_anim_t anim_type, uint32_t time, uint32_t delay, bool auto_del)
{
lv_disp_t * d = lv_obj_get_disp(new_scr);
lv_obj_t * act_scr = lv_scr_act();
/*If an other screen load animation is in progress
*make target screen loaded immediately. */
if(d->scr_to_load && act_scr != d->scr_to_load) {
lv_disp_load_scr(d->scr_to_load);
lv_anim_del(d->scr_to_load, NULL);
lv_obj_set_pos(d->scr_to_load, 0, 0);
lv_obj_remove_local_style_prop(d->scr_to_load, LV_STYLE_OPA, 0);
if(d->del_prev) {
lv_obj_del(act_scr);
}
act_scr = d->scr_to_load;
}
d->scr_to_load = new_scr;
if(d->prev_scr && d->del_prev) {
lv_obj_del(d->prev_scr);
d->prev_scr = NULL;
}
d->draw_prev_over_act = is_out_anim(anim_type);
d->del_prev = auto_del;
/*Be sure there is no other animation on the screens*/
lv_anim_del(new_scr, NULL);
lv_anim_del(lv_scr_act(), NULL);
/*Be sure both screens are in a normal position*/
lv_obj_set_pos(new_scr, 0, 0);
lv_obj_set_pos(lv_scr_act(), 0, 0);
lv_obj_remove_local_style_prop(new_scr, LV_STYLE_OPA, 0);
lv_obj_remove_local_style_prop(lv_scr_act(), LV_STYLE_OPA, 0);
lv_anim_t a_new;
lv_anim_init(&a_new);
lv_anim_set_var(&a_new, new_scr);
lv_anim_set_start_cb(&a_new, scr_load_anim_start);
lv_anim_set_ready_cb(&a_new, scr_anim_ready);
lv_anim_set_time(&a_new, time);
lv_anim_set_delay(&a_new, delay);
lv_anim_t a_old;
lv_anim_init(&a_old);
lv_anim_set_var(&a_old, d->act_scr);
lv_anim_set_time(&a_old, time);
lv_anim_set_delay(&a_old, delay);
switch(anim_type) {
case LV_SCR_LOAD_ANIM_NONE:
/*Create a dummy animation to apply the delay*/
lv_anim_set_exec_cb(&a_new, set_x_anim);
lv_anim_set_values(&a_new, 0, 0);
break;
case LV_SCR_LOAD_ANIM_OVER_LEFT:
lv_anim_set_exec_cb(&a_new, set_x_anim);
lv_anim_set_values(&a_new, lv_disp_get_hor_res(d), 0);
break;
case LV_SCR_LOAD_ANIM_OVER_RIGHT:
lv_anim_set_exec_cb(&a_new, set_x_anim);
lv_anim_set_values(&a_new, -lv_disp_get_hor_res(d), 0);
break;
case LV_SCR_LOAD_ANIM_OVER_TOP:
lv_anim_set_exec_cb(&a_new, set_y_anim);
lv_anim_set_values(&a_new, lv_disp_get_ver_res(d), 0);
break;
case LV_SCR_LOAD_ANIM_OVER_BOTTOM:
lv_anim_set_exec_cb(&a_new, set_y_anim);
lv_anim_set_values(&a_new, -lv_disp_get_ver_res(d), 0);
break;
case LV_SCR_LOAD_ANIM_MOVE_LEFT:
lv_anim_set_exec_cb(&a_new, set_x_anim);
lv_anim_set_values(&a_new, lv_disp_get_hor_res(d), 0);
lv_anim_set_exec_cb(&a_old, set_x_anim);
lv_anim_set_values(&a_old, 0, -lv_disp_get_hor_res(d));
break;
case LV_SCR_LOAD_ANIM_MOVE_RIGHT:
lv_anim_set_exec_cb(&a_new, set_x_anim);
lv_anim_set_values(&a_new, -lv_disp_get_hor_res(d), 0);
lv_anim_set_exec_cb(&a_old, set_x_anim);
lv_anim_set_values(&a_old, 0, lv_disp_get_hor_res(d));
break;
case LV_SCR_LOAD_ANIM_MOVE_TOP:
lv_anim_set_exec_cb(&a_new, set_y_anim);
lv_anim_set_values(&a_new, lv_disp_get_ver_res(d), 0);
lv_anim_set_exec_cb(&a_old, set_y_anim);
lv_anim_set_values(&a_old, 0, -lv_disp_get_ver_res(d));
break;
case LV_SCR_LOAD_ANIM_MOVE_BOTTOM:
lv_anim_set_exec_cb(&a_new, set_y_anim);
lv_anim_set_values(&a_new, -lv_disp_get_ver_res(d), 0);
lv_anim_set_exec_cb(&a_old, set_y_anim);
lv_anim_set_values(&a_old, 0, lv_disp_get_ver_res(d));
break;
case LV_SCR_LOAD_ANIM_FADE_IN:
lv_anim_set_exec_cb(&a_new, opa_scale_anim);
lv_anim_set_values(&a_new, LV_OPA_TRANSP, LV_OPA_COVER);
break;
case LV_SCR_LOAD_ANIM_FADE_OUT:
lv_anim_set_exec_cb(&a_old, opa_scale_anim);
lv_anim_set_values(&a_old, LV_OPA_COVER, LV_OPA_TRANSP);
break;
case LV_SCR_LOAD_ANIM_OUT_LEFT:
lv_anim_set_exec_cb(&a_old, set_x_anim);
lv_anim_set_values(&a_old, 0, -lv_disp_get_hor_res(d));
break;
case LV_SCR_LOAD_ANIM_OUT_RIGHT:
lv_anim_set_exec_cb(&a_old, set_x_anim);
lv_anim_set_values(&a_old, 0, lv_disp_get_hor_res(d));
break;
case LV_SCR_LOAD_ANIM_OUT_TOP:
lv_anim_set_exec_cb(&a_old, set_y_anim);
lv_anim_set_values(&a_old, 0, -lv_disp_get_ver_res(d));
break;
case LV_SCR_LOAD_ANIM_OUT_BOTTOM:
lv_anim_set_exec_cb(&a_old, set_y_anim);
lv_anim_set_values(&a_old, 0, lv_disp_get_ver_res(d));
break;
}
lv_event_send(act_scr, LV_EVENT_SCREEN_UNLOAD_START, NULL);
lv_anim_start(&a_new);
lv_anim_start(&a_old);
}
/**
* 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_disp_get_inactive_time(const lv_disp_t * disp)
{
if(disp) return lv_tick_elaps(disp->last_activity_time);
lv_disp_t * d;
uint32_t t = UINT32_MAX;
d = lv_disp_get_next(NULL);
while(d) {
uint32_t elaps = lv_tick_elaps(d->last_activity_time);
t = LV_MIN(t, elaps);
d = lv_disp_get_next(d);
}
return t;
}
/**
* Manually trigger an activity on a display
* @param disp pointer to a display (NULL to use the default display)
*/
void lv_disp_trig_activity(lv_disp_t * disp)
{
if(!disp) disp = lv_disp_get_default();
if(!disp) {
LV_LOG_WARN("lv_disp_trig_activity: no display registered");
return;
}
disp->last_activity_time = lv_tick_get();
}
/**
* Clean any CPU cache that is related to the display.
* @param disp pointer to a display (NULL to use the default display)
*/
void lv_disp_clean_dcache(lv_disp_t * disp)
{
if(!disp) disp = lv_disp_get_default();
if(!disp) {
LV_LOG_WARN("lv_disp_clean_dcache: no display registered");
return;
}
if(disp->driver->clean_dcache_cb)
disp->driver->clean_dcache_cb(disp->driver);
}
/**
* 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_disp_get_refr_timer(lv_disp_t * disp)
{
if(!disp) disp = lv_disp_get_default();
if(!disp) {
LV_LOG_WARN("lv_disp_get_refr_timer: no display registered");
return NULL;
}
return disp->refr_timer;
}
/**********************
* STATIC FUNCTIONS
**********************/
static void scr_load_anim_start(lv_anim_t * a)
{
lv_disp_t * d = lv_obj_get_disp(a->var);
d->prev_scr = lv_scr_act();
d->act_scr = a->var;
lv_event_send(d->act_scr, LV_EVENT_SCREEN_LOAD_START, NULL);
}
static void opa_scale_anim(void * obj, int32_t v)
{
lv_obj_set_style_opa(obj, v, 0);
}
static void set_x_anim(void * obj, int32_t v)
{
lv_obj_set_x(obj, v);
}
static void set_y_anim(void * obj, int32_t v)
{
lv_obj_set_y(obj, v);
}
static void scr_anim_ready(lv_anim_t * a)
{
lv_disp_t * d = lv_obj_get_disp(a->var);
lv_event_send(d->act_scr, LV_EVENT_SCREEN_LOADED, NULL);
lv_event_send(d->prev_scr, LV_EVENT_SCREEN_UNLOADED, NULL);
if(d->prev_scr && d->del_prev) lv_obj_del(d->prev_scr);
d->prev_scr = NULL;
d->draw_prev_over_act = false;
d->scr_to_load = NULL;
lv_obj_remove_local_style_prop(a->var, LV_STYLE_OPA, 0);
}
static bool is_out_anim(lv_scr_load_anim_t anim_type)
{
return anim_type == LV_SCR_LOAD_ANIM_FADE_OUT ||
anim_type == LV_SCR_LOAD_ANIM_OUT_LEFT ||
anim_type == LV_SCR_LOAD_ANIM_OUT_RIGHT ||
anim_type == LV_SCR_LOAD_ANIM_OUT_TOP ||
anim_type == LV_SCR_LOAD_ANIM_OUT_BOTTOM;
}

View File

@@ -0,0 +1,250 @@
/**
* @file lv_disp.h
*
*/
#ifndef LV_DISP_H
#define LV_DISP_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../hal/lv_hal.h"
#include "lv_obj.h"
#include "lv_theme.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
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_scr_load_anim_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Return with a pointer to the active screen
* @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_scr_load()')
*/
lv_obj_t * lv_disp_get_scr_act(lv_disp_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_disp_get_scr_prev(lv_disp_t * disp);
/**
* Make a screen active
* @param scr pointer to a screen
*/
void lv_disp_load_scr(lv_obj_t * scr);
/**
* Return with the top layer. (Same on every screen 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 (transparent screen sized lv_obj)
*/
lv_obj_t * lv_disp_get_layer_top(lv_disp_t * disp);
/**
* Return with the sys. layer. (Same on every 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 (transparent screen sized lv_obj)
*/
lv_obj_t * lv_disp_get_layer_sys(lv_disp_t * disp);
/**
* Set the theme of a display
* @param disp pointer to a display
*/
void lv_disp_set_theme(lv_disp_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_disp_get_theme(lv_disp_t * disp);
/**
* Set the background color of a display
* @param disp pointer to a display
* @param color color of the background
*/
void lv_disp_set_bg_color(lv_disp_t * disp, lv_color_t color);
/**
* Set the background image of a display
* @param disp pointer to a display
* @param img_src path to file or pointer to an `lv_img_dsc_t` variable
*/
void lv_disp_set_bg_image(lv_disp_t * disp, const void * img_src);
/**
* Set opacity of the background
* @param disp pointer to a display
* @param opa opacity (0..255)
*/
void lv_disp_set_bg_opa(lv_disp_t * disp, lv_opa_t opa);
/**
* Switch screen with animation
* @param scr pointer to the new screen to load
* @param anim_type type of the animation from `lv_scr_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_scr_load_anim(lv_obj_t * scr, lv_scr_load_anim_t anim_type, uint32_t time, uint32_t delay, bool auto_del);
/**
* 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_disp_get_inactive_time(const lv_disp_t * disp);
/**
* Manually trigger an activity on a display
* @param disp pointer to a display (NULL to use the default display)
*/
void lv_disp_trig_activity(lv_disp_t * disp);
/**
* Clean any CPU cache that is related to the display.
* @param disp pointer to a display (NULL to use the default display)
*/
void lv_disp_clean_dcache(lv_disp_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_disp_get_refr_timer(lv_disp_t * disp);
/*------------------------------------------------
* To improve backward compatibility
* Recommended only if you have one display
*------------------------------------------------*/
/**
* Get the active screen of the default display
* @return pointer to the active screen
*/
static inline lv_obj_t * lv_scr_act(void)
{
return lv_disp_get_scr_act(lv_disp_get_default());
}
/**
* Get the top layer of the default display
* @return pointer to the top layer
*/
static inline lv_obj_t * lv_layer_top(void)
{
return lv_disp_get_layer_top(lv_disp_get_default());
}
/**
* Get the active screen of the default display
* @return pointer to the sys layer
*/
static inline lv_obj_t * lv_layer_sys(void)
{
return lv_disp_get_layer_sys(lv_disp_get_default());
}
static inline void lv_scr_load(lv_obj_t * scr)
{
lv_disp_load_scr(scr);
}
/**********************
* 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_disp_get_hor_res(lv_disp_get_default())
#endif
#ifndef LV_VER_RES
/**
* The vertical resolution of the currently active display.
*/
#define LV_VER_RES lv_disp_get_ver_res(lv_disp_get_default())
#endif
/**
* Scale the given number of pixels (a distance or size) relative to a 160 DPI display
* considering the DPI of the default display.
* It ensures that e.g. `lv_dpx(100)` will have the same physical size regardless to the
* DPI of the display.
* @param n the number of pixels to scale
* @return `n x current_dpi/160`
*/
static inline lv_coord_t lv_dpx(lv_coord_t n)
{
return LV_DPX(n);
}
/**
* Scale the given number of pixels (a distance or size) relative to a 160 DPI display
* considering the DPI of the given display.
* It ensures that e.g. `lv_dpx(100)` will have the same physical size regardless to the
* DPI of the display.
* @param obj a display whose dpi should be considered
* @param n the number of pixels to scale
* @return `n x current_dpi/160`
*/
static inline lv_coord_t lv_disp_dpx(const lv_disp_t * disp, lv_coord_t n)
{
return _LV_DPX_CALC(lv_disp_get_dpi(disp), n);
}
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_DISP_H*/

View File

@@ -0,0 +1,527 @@
/**
* @file lv_event.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_obj.h"
#include "lv_indev.h"
/*********************
* DEFINES
*********************/
#define MY_CLASS &lv_obj_class
/**********************
* TYPEDEFS
**********************/
typedef struct _lv_event_dsc_t {
lv_event_cb_t cb;
void * user_data;
lv_event_code_t filter : 8;
} lv_event_dsc_t;
/**********************
* STATIC PROTOTYPES
**********************/
static lv_event_dsc_t * lv_obj_get_event_dsc(const lv_obj_t * obj, uint32_t id);
static lv_res_t event_send_core(lv_event_t * e);
static bool event_is_bubbled(lv_event_t * e);
/**********************
* STATIC VARIABLES
**********************/
static lv_event_t * event_head;
/**********************
* MACROS
**********************/
#if LV_LOG_TRACE_EVENT
#define EVENT_TRACE(...) LV_LOG_TRACE(__VA_ARGS__)
#else
#define EVENT_TRACE(...)
#endif
/**********************
* GLOBAL FUNCTIONS
**********************/
lv_res_t lv_event_send(lv_obj_t * obj, lv_event_code_t event_code, void * param)
{
if(obj == NULL) return LV_RES_OK;
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_event_t e;
e.target = obj;
e.current_target = obj;
e.code = event_code;
e.user_data = NULL;
e.param = param;
e.deleted = 0;
e.stop_bubbling = 0;
e.stop_processing = 0;
/*Build a simple linked list from the objects used in the events
*It's important to know if this object was deleted by a nested event
*called from this `event_cb`.*/
e.prev = event_head;
event_head = &e;
/*Send the event*/
lv_res_t res = event_send_core(&e);
/*Remove this element from the list*/
event_head = e.prev;
return res;
}
lv_res_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 = 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_RES_OK;
if(base->event_cb == NULL) return LV_RES_OK;
/*Call the actual event callback*/
e->user_data = NULL;
base->event_cb(base, e);
lv_res_t res = LV_RES_OK;
/*Stop if the object is deleted*/
if(e->deleted) res = LV_RES_INV;
return res;
}
lv_obj_t * lv_event_get_target(lv_event_t * e)
{
return e->target;
}
lv_obj_t * lv_event_get_current_target(lv_event_t * e)
{
return e->current_target;
}
lv_event_code_t lv_event_get_code(lv_event_t * e)
{
return e->code & ~LV_EVENT_PREPROCESS;
}
void * lv_event_get_param(lv_event_t * e)
{
return e->param;
}
void * lv_event_get_user_data(lv_event_t * e)
{
return e->user_data;
}
void lv_event_stop_bubbling(lv_event_t * e)
{
e->stop_bubbling = 1;
}
void lv_event_stop_processing(lv_event_t * e)
{
e->stop_processing = 1;
}
uint32_t lv_event_register_id(void)
{
static uint32_t last_id = _LV_EVENT_LAST;
last_id ++;
return last_id;
}
void _lv_event_mark_deleted(lv_obj_t * obj)
{
lv_event_t * e = event_head;
while(e) {
if(e->current_target == obj || e->target == obj) e->deleted = 1;
e = e->prev;
}
}
struct _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);
obj->spec_attr->event_dsc_cnt++;
obj->spec_attr->event_dsc = lv_mem_realloc(obj->spec_attr->event_dsc,
obj->spec_attr->event_dsc_cnt * sizeof(lv_event_dsc_t));
LV_ASSERT_MALLOC(obj->spec_attr->event_dsc);
obj->spec_attr->event_dsc[obj->spec_attr->event_dsc_cnt - 1].cb = event_cb;
obj->spec_attr->event_dsc[obj->spec_attr->event_dsc_cnt - 1].filter = filter;
obj->spec_attr->event_dsc[obj->spec_attr->event_dsc_cnt - 1].user_data = user_data;
return &obj->spec_attr->event_dsc[obj->spec_attr->event_dsc_cnt - 1];
}
bool lv_obj_remove_event_cb(lv_obj_t * obj, lv_event_cb_t event_cb)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
if(obj->spec_attr == NULL) return false;
int32_t i = 0;
for(i = 0; i < obj->spec_attr->event_dsc_cnt; i++) {
if(event_cb == NULL || obj->spec_attr->event_dsc[i].cb == event_cb) {
/*Shift the remaining event handlers forward*/
for(; i < (obj->spec_attr->event_dsc_cnt - 1); i++) {
obj->spec_attr->event_dsc[i] = obj->spec_attr->event_dsc[i + 1];
}
obj->spec_attr->event_dsc_cnt--;
obj->spec_attr->event_dsc = lv_mem_realloc(obj->spec_attr->event_dsc,
obj->spec_attr->event_dsc_cnt * sizeof(lv_event_dsc_t));
LV_ASSERT_MALLOC(obj->spec_attr->event_dsc);
return true;
}
}
/*No event handler found*/
return false;
}
bool lv_obj_remove_event_cb_with_user_data(lv_obj_t * obj, lv_event_cb_t event_cb, const void * user_data)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
if(obj->spec_attr == NULL) return false;
int32_t i = 0;
for(i = 0; i < obj->spec_attr->event_dsc_cnt; i++) {
if((event_cb == NULL || obj->spec_attr->event_dsc[i].cb == event_cb) &&
obj->spec_attr->event_dsc[i].user_data == user_data) {
/*Shift the remaining event handlers forward*/
for(; i < (obj->spec_attr->event_dsc_cnt - 1); i++) {
obj->spec_attr->event_dsc[i] = obj->spec_attr->event_dsc[i + 1];
}
obj->spec_attr->event_dsc_cnt--;
obj->spec_attr->event_dsc = lv_mem_realloc(obj->spec_attr->event_dsc,
obj->spec_attr->event_dsc_cnt * sizeof(lv_event_dsc_t));
LV_ASSERT_MALLOC(obj->spec_attr->event_dsc);
return true;
}
}
/*No event handler found*/
return false;
}
bool lv_obj_remove_event_dsc(lv_obj_t * obj, struct _lv_event_dsc_t * event_dsc)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
if(obj->spec_attr == NULL) return false;
int32_t i = 0;
for(i = 0; i < obj->spec_attr->event_dsc_cnt; i++) {
if(&obj->spec_attr->event_dsc[i] == event_dsc) {
/*Shift the remaining event handlers forward*/
for(; i < (obj->spec_attr->event_dsc_cnt - 1); i++) {
obj->spec_attr->event_dsc[i] = obj->spec_attr->event_dsc[i + 1];
}
obj->spec_attr->event_dsc_cnt--;
obj->spec_attr->event_dsc = lv_mem_realloc(obj->spec_attr->event_dsc,
obj->spec_attr->event_dsc_cnt * sizeof(lv_event_dsc_t));
LV_ASSERT_MALLOC(obj->spec_attr->event_dsc);
return true;
}
}
/*No event handler found*/
return false;
}
void * lv_obj_get_event_user_data(struct _lv_obj_t * obj, lv_event_cb_t event_cb)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
if(obj->spec_attr == NULL) return NULL;
int32_t i = 0;
for(i = 0; i < obj->spec_attr->event_dsc_cnt; i++) {
if(event_cb == obj->spec_attr->event_dsc[i].cb) return obj->spec_attr->event_dsc[i].user_data;
}
return NULL;
}
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) {
return lv_event_get_param(e);
}
else {
LV_LOG_WARN("Not interpreted with this event code");
return NULL;
}
}
lv_obj_draw_part_dsc_t * lv_event_get_draw_part_dsc(lv_event_t * e)
{
if(e->code == LV_EVENT_DRAW_PART_BEGIN ||
e->code == LV_EVENT_DRAW_PART_END) {
return lv_event_get_param(e);
}
else {
LV_LOG_WARN("Not interpreted with this event code");
return NULL;
}
}
lv_draw_ctx_t * lv_event_get_draw_ctx(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;
}
}
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 0;
}
}
void lv_event_set_ext_draw_size(lv_event_t * e, lv_coord_t size)
{
if(e->code == LV_EVENT_REFR_EXT_DRAW_SIZE) {
lv_coord_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");
}
}
/**********************
* STATIC FUNCTIONS
**********************/
static lv_event_dsc_t * lv_obj_get_event_dsc(const lv_obj_t * obj, uint32_t id)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
if(!obj->spec_attr) return NULL;
if(id >= obj->spec_attr->event_dsc_cnt) return NULL;
return &obj->spec_attr->event_dsc[id];
}
static lv_res_t event_send_core(lv_event_t * e)
{
EVENT_TRACE("Sending event %d to %p with %p param", e->code, (void *)e->current_target, e->param);
/*Call the input device's feedback callback if set*/
lv_indev_t * indev_act = lv_indev_get_act();
if(indev_act) {
if(indev_act->driver->feedback_cb) indev_act->driver->feedback_cb(indev_act->driver, e->code);
if(e->stop_processing) return LV_RES_OK;
if(e->deleted) return LV_RES_INV;
}
lv_res_t res = LV_RES_OK;
lv_event_dsc_t * event_dsc = lv_obj_get_event_dsc(e->current_target, 0);
uint32_t i = 0;
while(event_dsc && res == LV_RES_OK) {
if(event_dsc->cb && ((event_dsc->filter & LV_EVENT_PREPROCESS) == LV_EVENT_PREPROCESS)
&& (event_dsc->filter == (LV_EVENT_ALL | LV_EVENT_PREPROCESS) ||
(event_dsc->filter & ~LV_EVENT_PREPROCESS) == e->code)) {
e->user_data = event_dsc->user_data;
event_dsc->cb(e);
if(e->stop_processing) return LV_RES_OK;
/*Stop if the object is deleted*/
if(e->deleted) return LV_RES_INV;
}
i++;
event_dsc = lv_obj_get_event_dsc(e->current_target, i);
}
res = lv_obj_event_base(NULL, e);
event_dsc = res == LV_RES_INV ? NULL : lv_obj_get_event_dsc(e->current_target, 0);
i = 0;
while(event_dsc && res == LV_RES_OK) {
if(event_dsc->cb && ((event_dsc->filter & LV_EVENT_PREPROCESS) == 0)
&& (event_dsc->filter == LV_EVENT_ALL || event_dsc->filter == e->code)) {
e->user_data = event_dsc->user_data;
event_dsc->cb(e);
if(e->stop_processing) return LV_RES_OK;
/*Stop if the object is deleted*/
if(e->deleted) return LV_RES_INV;
}
i++;
event_dsc = lv_obj_get_event_dsc(e->current_target, i);
}
if(res == LV_RES_OK && e->current_target->parent && event_is_bubbled(e)) {
e->current_target = e->current_target->parent;
res = event_send_core(e);
if(res != LV_RES_OK) return LV_RES_INV;
}
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_PART_BEGIN:
case LV_EVENT_DRAW_PART_END:
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;
}
}

View File

@@ -0,0 +1,363 @@
/**
* @file lv_event.h
*
*/
#ifndef LV_EVENT_H
#define LV_EVENT_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include <stdbool.h>
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
struct _lv_obj_t;
struct _lv_event_dsc_t;
/**
* Type of event being sent to the object.
*/
typedef enum {
LV_EVENT_ALL = 0,
/** Input device events*/
LV_EVENT_PRESSED, /**< The object has been pressed*/
LV_EVENT_PRESSING, /**< The object is being pressed (called continuously while pressing)*/
LV_EVENT_PRESS_LOST, /**< The object is still being pressed but slid cursor/finger off of the object */
LV_EVENT_SHORT_CLICKED, /**< The object was pressed for a short period of time, then released it. Not called if scrolled.*/
LV_EVENT_LONG_PRESSED, /**< Object has been pressed for at least `long_press_time`. Not called if scrolled.*/
LV_EVENT_LONG_PRESSED_REPEAT, /**< Called after `long_press_time` in every `long_press_repeat_time` ms. Not called if scrolled.*/
LV_EVENT_CLICKED, /**< Called on release if not scrolled (regardless to long press)*/
LV_EVENT_RELEASED, /**< Called in every cases when the object has been released*/
LV_EVENT_SCROLL_BEGIN, /**< Scrolling begins. The event parameter is a pointer to the animation of the scroll. Can be modified*/
LV_EVENT_SCROLL_END, /**< Scrolling ends*/
LV_EVENT_SCROLL, /**< Scrolling*/
LV_EVENT_GESTURE, /**< A gesture is detected. Get the gesture with `lv_indev_get_gesture_dir(lv_indev_get_act());` */
LV_EVENT_KEY, /**< A key is sent to the object. Get the key with `lv_indev_get_key(lv_indev_get_act());`*/
LV_EVENT_FOCUSED, /**< The object is focused*/
LV_EVENT_DEFOCUSED, /**< The object is defocused*/
LV_EVENT_LEAVE, /**< The object is defocused but still selected*/
LV_EVENT_HIT_TEST, /**< Perform advanced hit-testing*/
/** Drawing events*/
LV_EVENT_COVER_CHECK, /**< Check if the object fully covers an area. The event parameter is `lv_cover_check_info_t *`.*/
LV_EVENT_REFR_EXT_DRAW_SIZE, /**< Get the required extra draw area around the object (e.g. for shadow). The event parameter is `lv_coord_t *` to store the size.*/
LV_EVENT_DRAW_MAIN_BEGIN, /**< Starting the main drawing phase*/
LV_EVENT_DRAW_MAIN, /**< Perform the main drawing*/
LV_EVENT_DRAW_MAIN_END, /**< Finishing the main drawing phase*/
LV_EVENT_DRAW_POST_BEGIN, /**< Starting the post draw phase (when all children are drawn)*/
LV_EVENT_DRAW_POST, /**< Perform the post draw phase (when all children are drawn)*/
LV_EVENT_DRAW_POST_END, /**< Finishing the post draw phase (when all children are drawn)*/
LV_EVENT_DRAW_PART_BEGIN, /**< Starting to draw a part. The event parameter is `lv_obj_draw_dsc_t *`. */
LV_EVENT_DRAW_PART_END, /**< Finishing to draw a part. The event parameter is `lv_obj_draw_dsc_t *`. */
/** Special events*/
LV_EVENT_VALUE_CHANGED, /**< The object's value has changed (i.e. slider moved)*/
LV_EVENT_INSERT, /**< A text is inserted to the object. The event data is `char *` being inserted.*/
LV_EVENT_REFRESH, /**< Notify the object to refresh something on it (for the user)*/
LV_EVENT_READY, /**< A process has finished*/
LV_EVENT_CANCEL, /**< A process has been cancelled */
/** Other events*/
LV_EVENT_DELETE, /**< Object is being deleted*/
LV_EVENT_CHILD_CHANGED, /**< Child was removed, added, or its size, position were changed */
LV_EVENT_CHILD_CREATED, /**< Child was created, always bubbles up to all parents*/
LV_EVENT_CHILD_DELETED, /**< Child was deleted, always bubbles up to all parents*/
LV_EVENT_SCREEN_UNLOAD_START, /**< A screen unload started, fired immediately when scr_load is called*/
LV_EVENT_SCREEN_LOAD_START, /**< A screen load started, fired when the screen change delay is expired*/
LV_EVENT_SCREEN_LOADED, /**< A screen was loaded*/
LV_EVENT_SCREEN_UNLOADED, /**< A screen was unloaded*/
LV_EVENT_SIZE_CHANGED, /**< Object coordinates/size have changed*/
LV_EVENT_STYLE_CHANGED, /**< Object's style has changed*/
LV_EVENT_LAYOUT_CHANGED, /**< The children position has changed due to a layout recalculation*/
LV_EVENT_GET_SELF_SIZE, /**< Get the internal size of a widget*/
_LV_EVENT_LAST, /** Number of default events*/
LV_EVENT_PREPROCESS = 0x80, /** This is a flag that can be set with an event so it's processed
before the class default event processing */
} lv_event_code_t;
typedef struct _lv_event_t {
struct _lv_obj_t * target;
struct _lv_obj_t * current_target;
lv_event_code_t code;
void * user_data;
void * param;
struct _lv_event_t * prev;
uint8_t deleted : 1;
uint8_t stop_processing : 1;
uint8_t stop_bubbling : 1;
} lv_event_t;
/**
* @brief Event callback.
* Events are used to notify the user of some action being taken on the object.
* For details, see ::lv_event_t.
*/
typedef void (*lv_event_cb_t)(lv_event_t * e);
/**
* 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` an other 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`
*/
typedef struct {
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*/
} lv_hit_test_info_t;
/**
* 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.
*/
typedef struct {
lv_cover_res_t res;
const lv_area_t * area;
} lv_cover_check_info_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_RES_OK: `obj` was not deleted in the event; LV_RES_INV: `obj` was deleted in the event_code
*/
lv_res_t lv_event_send(struct _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_RES_OK: the target object was not deleted in the event; LV_RES_INV: it was deleted in the event_code
*/
lv_res_t lv_obj_event_base(const lv_obj_class_t * class_p, 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 the target of the event_code
*/
struct _lv_obj_t * lv_event_get_target(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 "normal" target.
* @param e pointer to the event descriptor
* @return pointer to the current target of the event_code
*/
struct _lv_obj_t * lv_event_get_current_target(lv_event_t * e);
/**
* Get the event code of an event
* @param e pointer to the event descriptor
* @return the event code. (E.g. `LV_EVENT_CLICKED`, `LV_EVENT_FOCUSED`, etc)
*/
lv_event_code_t lv_event_get_code(lv_event_t * e);
/**
* Get the parameter passed when the event was sent
* @param e pointer to the event descriptor
* @return pointer to the parameter
*/
void * lv_event_get_param(lv_event_t * e);
/**
* Get the user_data passed when the event was registered on the object
* @param e pointer to the event descriptor
* @return pointer to the user_data
*/
void * lv_event_get_user_data(lv_event_t * e);
/**
* Stop the event from bubbling.
* This is only valid when called in the middle of an event processing chain.
* @param e pointer to the event descriptor
*/
void lv_event_stop_bubbling(lv_event_t * e);
/**
* Stop processing this event.
* This is only valid when called in the middle of an event processing chain.
* @param e pointer to the event descriptor
*/
void lv_event_stop_processing(lv_event_t * e);
/**
* Register a new, custom event ID.
* It can be used the same way as e.g. `LV_EVENT_CLICKED` to send custom events
* @return the new event id
* @example
* uint32_t LV_EVENT_MINE = 0;
* ...
* e = lv_event_register_id();
* ...
* lv_event_send(obj, LV_EVENT_MINE, &some_data);
*/
uint32_t lv_event_register_id(void);
/**
* Nested events can be called and one of them might belong to an object that is being deleted.
* Mark this object's `event_temp_data` deleted to know that its `lv_event_send` should return `LV_RES_INV`
* @param obj pointer to an object to mark as deleted
*/
void _lv_event_mark_deleted(struct _lv_obj_t * obj);
/**
* 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 and event code (e.g. `LV_EVENT_CLICKED`) on which the event should be called. `LV_EVENT_ALL` can be sued the receive all the events.
* @param event_cb the new event function
* @param user_data custom data data will be available in `event_cb`
* @return a pointer the event descriptor. Can be used in ::lv_obj_remove_event_dsc
*/
struct _lv_event_dsc_t * lv_obj_add_event_cb(struct _lv_obj_t * obj, lv_event_cb_t event_cb, lv_event_code_t filter,
void * user_data);
/**
* Remove an event handler function for an object.
* @param obj pointer to an object
* @param event_cb the event function to remove, or `NULL` to remove the firstly added event callback
* @return true if any event handlers were removed
*/
bool lv_obj_remove_event_cb(struct _lv_obj_t * obj, lv_event_cb_t event_cb);
/**
* Remove an event handler function with a specific user_data from an object.
* @param obj pointer to an object
* @param event_cb the event function to remove, or `NULL` only `user_data` matters.
* @param event_user_data the user_data specified in ::lv_obj_add_event_cb
* @return true if any event handlers were removed
*/
bool lv_obj_remove_event_cb_with_user_data(struct _lv_obj_t * obj, lv_event_cb_t event_cb,
const void * event_user_data);
/**
* DEPRECATED because doesn't work if multiple event handlers are added to an object.
* Remove an event handler function for an object.
* @param obj pointer to an object
* @param event_dsc pointer to an event descriptor to remove (returned by ::lv_obj_add_event_cb)
* @return true if any event handlers were removed
*/
bool lv_obj_remove_event_dsc(struct _lv_obj_t * obj, struct _lv_event_dsc_t * event_dsc);
/**
* The user data of an event object event callback. Always the first match with `event_cb` will be returned.
* @param obj pointer to an object
* @param event_cb the event function
* @return the user_data
*/
void * lv_obj_get_event_user_data(struct _lv_obj_t * obj, lv_event_cb_t event_cb);
/**
* 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 part draw descriptor passed as parameter to `LV_EVENT_DRAW_PART_BEGIN/END`.
* @param e pointer to an event
* @return the part draw descriptor to hook the drawing or NULL if called on an unrelated event
*/
lv_obj_draw_part_dsc_t * lv_event_get_draw_part_dsc(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_draw_ctx_t * lv_event_get_draw_ctx(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 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, lv_coord_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);
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_EVENT_H*/

View File

@@ -0,0 +1,471 @@
/**
* @file lv_group.c
*
*/
/*********************
* INCLUDES
*********************/
#include <stddef.h>
#include "lv_group.h"
#include "../misc/lv_gc.h"
#include "../core/lv_obj.h"
#include "../core/lv_indev.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static void 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
**********************/
static lv_group_t * default_group;
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
void _lv_group_init(void)
{
_lv_ll_init(&LV_GC_ROOT(_lv_group_ll), sizeof(lv_group_t));
}
lv_group_t * lv_group_create(void)
{
lv_group_t * group = _lv_ll_ins_head(&LV_GC_ROOT(_lv_group_ll));
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->editing = 0;
group->refocus_policy = LV_GROUP_REFOCUS_POLICY_PREV;
group->wrap = 1;
#if LV_USE_USER_DATA
group->user_data = NULL;
#endif
return group;
}
void lv_group_del(lv_group_t * group)
{
/*Defocus the currently focused object*/
if(group->obj_focus != NULL) {
lv_event_send(*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(indev->group == group) {
lv_indev_set_group(indev, NULL);
}
indev = lv_indev_get_next(indev);
}
_lv_ll_clear(&(group->obj_ll));
_lv_ll_remove(&LV_GC_ROOT(_lv_group_ll), group);
lv_mem_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");
/*Do not add the object twice*/
lv_obj_t ** obj_i;
_LV_LL_READ(&group->obj_ll, obj_i) {
if((*obj_i) == obj) {
LV_LOG_INFO("the object is already added to this group");
return;
}
}
/*If the object is already in a group and focused then refocus it*/
lv_group_t * group_cur = lv_obj_get_group(obj);
if(group_cur) {
if(obj->spec_attr->group_p && *(obj->spec_attr->group_p->obj_focus) == obj) {
lv_group_refocus(group_cur);
LV_LOG_INFO("changing object's group");
}
}
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;
}
if(*g1->obj_focus == obj1) lv_group_focus_obj(obj2);
else if(*g1->obj_focus == 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_event_send(*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_mem_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)
{
/*Defocus the currently focused object*/
if(group->obj_focus != NULL) {
lv_event_send(*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_res_t res = lv_event_send(*g->obj_focus, LV_EVENT_DEFOCUSED, get_indev(g));
if(res != LV_RES_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_res_t res = lv_event_send(*g->obj_focus, LV_EVENT_FOCUSED, get_indev(g));
if(res != LV_RES_OK) return;
lv_obj_invalidate(*g->obj_focus);
}
break;
}
}
}
void lv_group_focus_next(lv_group_t * group)
{
focus_next_core(group, _lv_ll_get_head, _lv_ll_get_next);
}
void lv_group_focus_prev(lv_group_t * group)
{
focus_next_core(group, _lv_ll_get_tail, _lv_ll_get_prev);
}
void lv_group_focus_freeze(lv_group_t * group, bool en)
{
if(en == false) group->frozen = 0;
else group->frozen = 1;
}
lv_res_t lv_group_send_data(lv_group_t * group, uint32_t c)
{
lv_obj_t * act = lv_group_get_focused(group);
if(act == NULL) return LV_RES_OK;
if(lv_obj_has_state(act, LV_STATE_DISABLED)) return LV_RES_OK;
return lv_event_send(act, LV_EVENT_KEY, &c);
}
void lv_group_set_focus_cb(lv_group_t * group, lv_group_focus_cb_t focus_cb)
{
group->focus_cb = focus_cb;
}
void lv_group_set_editing(lv_group_t * group, bool edit)
{
if(group == NULL) return;
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_res_t res = lv_event_send(*group->obj_focus, LV_EVENT_FOCUSED, get_indev(group));
if(res != LV_RES_OK) return;
lv_obj_invalidate(focused);
}
}
void lv_group_set_refocus_policy(lv_group_t * group, lv_group_refocus_policy_t policy)
{
group->refocus_policy = policy & 0x01;
}
void lv_group_set_wrap(lv_group_t * group, bool en)
{
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;
}
bool lv_group_get_editing(const lv_group_t * group)
{
if(!group) return false;
return group->editing ? true : false;
}
bool lv_group_get_wrap(lv_group_t * group)
{
if(!group) return false;
return group->wrap ? true : false;
}
uint32_t lv_group_get_obj_count(lv_group_t * group)
{
return _lv_ll_get_len(&group->obj_ll);
}
/**********************
* 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 void focus_next_core(lv_group_t * group, void * (*begin)(const lv_ll_t *),
void * (*move)(const lv_ll_t *, const void *))
{
if(group->frozen) return;
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;
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;
}
}
if(obj_sentinel == NULL) {
obj_sentinel = obj_next;
if(obj_sentinel == NULL) return; /*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;
}
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; /*There's only one visible object and it's already focused*/
if(group->obj_focus) {
lv_res_t res = lv_event_send(*group->obj_focus, LV_EVENT_DEFOCUSED, get_indev(group));
if(res != LV_RES_OK) return;
lv_obj_invalidate(*group->obj_focus);
}
group->obj_focus = obj_next;
lv_res_t res = lv_event_send(*group->obj_focus, LV_EVENT_FOCUSED, get_indev(group));
if(res != LV_RES_OK) return;
lv_obj_invalidate(*group->obj_focus);
if(group->focus_cb) group->focus_cb(group);
}
/**
* Find an indev preferably with KEYPAD or ENCOEDR type 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_encoder = NULL;
lv_indev_t * indev_group = NULL;
lv_indev_t * indev = lv_indev_get_next(NULL);
while(indev) {
lv_indev_type_t indev_type = lv_indev_get_type(indev);
if(indev->group == g) {
/*Prefer KEYPAD*/
if(indev_type == LV_INDEV_TYPE_KEYPAD) return indev;
if(indev_type == LV_INDEV_TYPE_ENCODER) indev_encoder = indev;
indev_group = indev;
}
indev = lv_indev_get_next(indev);
}
if(indev_encoder) return indev_encoder;
if(indev_group) return indev_group;
/*In lack of a better option use the first input device. (It can be NULL if there is no input device)*/
return lv_indev_get_next(NULL);
}

View File

@@ -0,0 +1,246 @@
/**
* @file lv_group.h
*
*/
#ifndef LV_GROUP_H
#define LV_GROUP_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../lv_conf_internal.h"
#include <stdint.h>
#include <stdbool.h>
#include "../misc/lv_ll.h"
#include "../misc/lv_types.h"
/*********************
* DEFINES
*********************/
/*Predefined keys to control the focused object via lv_group_send(group, c)*/
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*/
};
typedef uint8_t lv_key_t;
/**********************
* TYPEDEFS
**********************/
struct _lv_obj_t;
struct _lv_group_t;
typedef void (*lv_group_focus_cb_t)(struct _lv_group_t *);
/**
* 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).
*/
typedef struct _lv_group_t {
lv_ll_t obj_ll; /**< Linked list to store the objects in the group*/
struct _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)*/
#if LV_USE_USER_DATA
void * user_data;
#endif
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.*/
} lv_group_t;
typedef enum {
LV_GROUP_REFOCUS_POLICY_NEXT = 0,
LV_GROUP_REFOCUS_POLICY_PREV = 1
} lv_group_refocus_policy_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Init. the group module
* @remarks Internal function, do not call directly.
*/
void _lv_group_init(void);
/**
* Create a new object group
* @return pointer to the new object group
*/
lv_group_t * lv_group_create(void);
/**
* Delete a group object
* @param group pointer to a group
*/
void lv_group_del(lv_group_t * group);
/**
* Set a default group. New object are 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 the default group
* @return pointer to the default group
*/
lv_group_t * lv_group_get_default(void);
/**
* Add an object to a group
* @param group pointer to a group
* @param obj pointer to an object to add
*/
void lv_group_add_obj(lv_group_t * group, struct _lv_obj_t * obj);
/**
* Swap 2 object in a group. The object must be in the same group
* @param obj1 pointer to an object
* @param obj2 pointer to an other object
*/
void lv_group_swap_obj(struct _lv_obj_t * obj1, struct _lv_obj_t * obj2);
/**
* Remove an object from its group
* @param obj pointer to an object to remove
*/
void lv_group_remove_obj(struct _lv_obj_t * obj);
/**
* Remove all objects from a group
* @param group pointer to a group
*/
void lv_group_remove_all_objs(lv_group_t * group);
/**
* Focus on an object (defocus the current)
* @param obj pointer to an object to focus on
*/
void lv_group_focus_obj(struct _lv_obj_t * obj);
/**
* Focus the next object in a group (defocus the current)
* @param group pointer to a group
*/
void lv_group_focus_next(lv_group_t * group);
/**
* Focus the previous object in a group (defocus the current)
* @param group pointer to a group
*/
void lv_group_focus_prev(lv_group_t * group);
/**
* Do not let to change the focus from the current object
* @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 the focuses object of a group
* @param group pointer to a group
* @param c a character (use LV_KEY_.. to navigate)
* @return result of focused object in group.
*/
lv_res_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 object is focused
* @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 whether the next or previous item in a group is focused if the currently focused obj 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 focus next/prev will allow wrapping from first->last or last->first object.
* @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 the focused object or NULL if there isn't one
* @param group pointer to a group
* @return pointer to the focused object
*/
struct _lv_obj_t * lv_group_get_focused(const lv_group_t * group);
/**
* Get the 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 the 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 focus next/prev will allow wrapping from first->last or last->first object.
* @param group pointer to group
* @param en true: wrapping enabled; false: wrapping disabled
*/
bool lv_group_get_wrap(lv_group_t * group);
/**
* Get the number of object in the group
* @param group pointer to a group
* @return number of objects in the group
*/
uint32_t lv_group_get_obj_count(lv_group_t * group);
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_GROUP_H*/

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,176 @@
/**
* @file lv_indev.h
*
*/
#ifndef LV_INDEV_H
#define LV_INDEV_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "lv_obj.h"
#include "../hal/lv_hal_indev.h"
#include "lv_group.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Called periodically to read the input devices
* @param timer pointer to a timer to read
*/
void lv_indev_read_timer_cb(lv_timer_t * timer);
/**
* Enable or disable one or all input devices (default enabled)
* @param indev pointer to an input device or NULL to enable/disable all of them
* @param en true to enable, false to disable
*/
void lv_indev_enable(lv_indev_t * indev, bool en);
/**
* Get the currently processed input device. Can be used in action functions too.
* @return pointer to the currently processed input device or NULL if no input device processing
* right now
*/
lv_indev_t * lv_indev_get_act(void);
/**
* Get the type of an input device
* @param indev pointer to an input device
* @return the type of the input device from `lv_hal_indev_type_t` (`LV_INDEV_TYPE_...`)
*/
lv_indev_type_t lv_indev_get_type(const lv_indev_t * indev);
/**
* Reset one or all input devices
* @param indev pointer to an input device to reset or NULL to reset all of them
* @param obj pointer to an object which triggers the reset.
*/
void lv_indev_reset(lv_indev_t * indev, lv_obj_t * obj);
/**
* Reset the long press state of an input device
* @param indev pointer to an input device
*/
void lv_indev_reset_long_press(lv_indev_t * indev);
/**
* Set a cursor for a pointer input device (for LV_INPUT_TYPE_POINTER and LV_INPUT_TYPE_BUTTON)
* @param indev pointer to an input device
* @param cur_obj pointer to an object to be used as cursor
*/
void lv_indev_set_cursor(lv_indev_t * indev, lv_obj_t * cur_obj);
/**
* Set a destination group for a keypad input device (for LV_INDEV_TYPE_KEYPAD)
* @param indev pointer to an input device
* @param group point to a group
*/
void lv_indev_set_group(lv_indev_t * indev, lv_group_t * group);
/**
* Set the an array of points for LV_INDEV_TYPE_BUTTON.
* These points will be assigned to the buttons to press a specific point on the screen
* @param indev pointer to an input device
* @param group point to a group
*/
void lv_indev_set_button_points(lv_indev_t * indev, const lv_point_t points[]);
/**
* Get the last point of an input device (for LV_INDEV_TYPE_POINTER and LV_INDEV_TYPE_BUTTON)
* @param indev pointer to an input device
* @param point pointer to a point to store the result
*/
void lv_indev_get_point(const lv_indev_t * indev, lv_point_t * point);
/**
* Get the current gesture direct
* @param indev pointer to an input device
* @return current gesture direct
*/
lv_dir_t lv_indev_get_gesture_dir(const lv_indev_t * indev);
/**
* Get the last pressed key of an input device (for LV_INDEV_TYPE_KEYPAD)
* @param indev pointer to an input device
* @return the last pressed key (0 on error)
*/
uint32_t lv_indev_get_key(const lv_indev_t * indev);
/**
* Check the current scroll direction of an input device (for LV_INDEV_TYPE_POINTER and
* LV_INDEV_TYPE_BUTTON)
* @param indev pointer to an input device
* @return LV_DIR_NONE: no scrolling now
* LV_DIR_HOR/VER
*/
lv_dir_t lv_indev_get_scroll_dir(const lv_indev_t * indev);
/**
* Get the currently scrolled object (for LV_INDEV_TYPE_POINTER and
* LV_INDEV_TYPE_BUTTON)
* @param indev pointer to an input device
* @return pointer to the currently scrolled object or NULL if no scrolling by this indev
*/
lv_obj_t * lv_indev_get_scroll_obj(const lv_indev_t * indev);
/**
* Get the movement vector of an input device (for LV_INDEV_TYPE_POINTER and
* LV_INDEV_TYPE_BUTTON)
* @param indev pointer to an input device
* @param point pointer to a point to store the types.pointer.vector
*/
void lv_indev_get_vect(const lv_indev_t * indev, lv_point_t * point);
/**
* Do nothing until the next release
* @param indev pointer to an input device
*/
void lv_indev_wait_release(lv_indev_t * indev);
/**
* Gets a pointer to the currently active object in the currently processed input device.
* @return pointer to currently active object or NULL if no active object
*/
lv_obj_t * lv_indev_get_obj_act(void);
/**
* Get a pointer to the indev read timer to
* modify its parameters with `lv_timer_...` functions.
* @param indev pointer to an input device
* @return pointer to the indev read refresher timer. (NULL on error)
*/
lv_timer_t * lv_indev_get_read_timer(lv_disp_t * indev);
/**
* Search the most top, clickable object by a point
* @param obj pointer to a start object, typically the screen
* @param point pointer to a point for searching the most top child
* @return pointer to the found object or NULL if there was no suitable object
*/
lv_obj_t * lv_indev_search_obj(lv_obj_t * obj, lv_point_t * point);
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_INDEV_H*/

View File

@@ -0,0 +1,651 @@
/**
* @file lv_indev_scroll.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_indev.h"
#include "lv_indev_scroll.h"
/*********************
* DEFINES
*********************/
#define ELASTIC_SLOWNESS_FACTOR 4 /*Scrolling on elastic parts are slower by this factor*/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static lv_obj_t * find_scroll_obj(_lv_indev_proc_t * proc);
static void init_scroll_limits(_lv_indev_proc_t * proc);
static lv_coord_t find_snap_point_x(const lv_obj_t * obj, lv_coord_t min, lv_coord_t max, lv_coord_t ofs);
static lv_coord_t find_snap_point_y(const lv_obj_t * obj, lv_coord_t min, lv_coord_t max, lv_coord_t ofs);
static void scroll_limit_diff(_lv_indev_proc_t * proc, lv_coord_t * diff_x, lv_coord_t * diff_y);
static lv_coord_t scroll_throw_predict_y(_lv_indev_proc_t * proc);
static lv_coord_t scroll_throw_predict_x(_lv_indev_proc_t * proc);
static lv_coord_t elastic_diff(lv_obj_t * scroll_obj, lv_coord_t diff, lv_coord_t scroll_start, lv_coord_t scroll_end,
lv_dir_t dir);
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
void _lv_indev_scroll_handler(_lv_indev_proc_t * proc)
{
lv_obj_t * scroll_obj = proc->types.pointer.scroll_obj;
/*If there is no scroll object yet try to find one*/
if(scroll_obj == NULL) {
proc->types.pointer.scroll_sum.x += proc->types.pointer.vect.x;
proc->types.pointer.scroll_sum.y += proc->types.pointer.vect.y;
scroll_obj = find_scroll_obj(proc);
if(scroll_obj == NULL) return;
init_scroll_limits(proc);
lv_event_send(scroll_obj, LV_EVENT_SCROLL_BEGIN, NULL);
if(proc->reset_query) return;
}
/*Set new position or scroll if the vector is not zero*/
if(proc->types.pointer.vect.x != 0 || proc->types.pointer.vect.y != 0) {
lv_coord_t diff_x = 0;
lv_coord_t diff_y = 0;
if(proc->types.pointer.scroll_dir == LV_DIR_HOR) {
lv_coord_t sr = lv_obj_get_scroll_right(scroll_obj);
lv_coord_t sl = lv_obj_get_scroll_left(scroll_obj);
diff_x = elastic_diff(scroll_obj, proc->types.pointer.vect.x, sl, sr, LV_DIR_HOR);
}
else {
lv_coord_t st = lv_obj_get_scroll_top(scroll_obj);
lv_coord_t sb = lv_obj_get_scroll_bottom(scroll_obj);
diff_y = elastic_diff(scroll_obj, proc->types.pointer.vect.y, st, sb, LV_DIR_VER);
}
lv_dir_t scroll_dir = lv_obj_get_scroll_dir(scroll_obj);
if((scroll_dir & LV_DIR_LEFT) == 0 && diff_x > 0) diff_x = 0;
if((scroll_dir & LV_DIR_RIGHT) == 0 && diff_x < 0) diff_x = 0;
if((scroll_dir & LV_DIR_TOP) == 0 && diff_y > 0) diff_y = 0;
if((scroll_dir & LV_DIR_BOTTOM) == 0 && diff_y < 0) diff_y = 0;
/*Respect the scroll limit area*/
scroll_limit_diff(proc, &diff_x, &diff_y);
lv_obj_scroll_by(scroll_obj, diff_x, diff_y, LV_ANIM_OFF);
proc->types.pointer.scroll_sum.x += diff_x;
proc->types.pointer.scroll_sum.y += diff_y;
}
}
void _lv_indev_scroll_throw_handler(_lv_indev_proc_t * proc)
{
lv_obj_t * scroll_obj = proc->types.pointer.scroll_obj;
if(scroll_obj == NULL) return;
if(proc->types.pointer.scroll_dir == LV_DIR_NONE) return;
lv_indev_t * indev_act = lv_indev_get_act();
lv_coord_t scroll_throw = indev_act->driver->scroll_throw;
if(lv_obj_has_flag(scroll_obj, LV_OBJ_FLAG_SCROLL_MOMENTUM) == false) {
proc->types.pointer.scroll_throw_vect.y = 0;
proc->types.pointer.scroll_throw_vect.x = 0;
}
lv_scroll_snap_t align_x = lv_obj_get_scroll_snap_x(scroll_obj);
lv_scroll_snap_t align_y = lv_obj_get_scroll_snap_y(scroll_obj);
if(proc->types.pointer.scroll_dir == LV_DIR_VER) {
proc->types.pointer.scroll_throw_vect.x = 0;
/*If no snapping "throw"*/
if(align_y == LV_SCROLL_SNAP_NONE) {
proc->types.pointer.scroll_throw_vect.y =
proc->types.pointer.scroll_throw_vect.y * (100 - scroll_throw) / 100;
lv_coord_t sb = lv_obj_get_scroll_bottom(scroll_obj);
lv_coord_t st = lv_obj_get_scroll_top(scroll_obj);
proc->types.pointer.scroll_throw_vect.y = elastic_diff(scroll_obj, proc->types.pointer.scroll_throw_vect.y, st, sb,
LV_DIR_VER);
lv_obj_scroll_by(scroll_obj, 0, proc->types.pointer.scroll_throw_vect.y, LV_ANIM_OFF);
}
/*With snapping find the nearest snap point and scroll there*/
else {
lv_coord_t diff_y = scroll_throw_predict_y(proc);
proc->types.pointer.scroll_throw_vect.y = 0;
scroll_limit_diff(proc, NULL, &diff_y);
lv_coord_t y = find_snap_point_y(scroll_obj, LV_COORD_MIN, LV_COORD_MAX, diff_y);
lv_obj_scroll_by(scroll_obj, 0, diff_y + y, LV_ANIM_ON);
}
}
else if(proc->types.pointer.scroll_dir == LV_DIR_HOR) {
proc->types.pointer.scroll_throw_vect.y = 0;
/*If no snapping "throw"*/
if(align_x == LV_SCROLL_SNAP_NONE) {
proc->types.pointer.scroll_throw_vect.x =
proc->types.pointer.scroll_throw_vect.x * (100 - scroll_throw) / 100;
lv_coord_t sl = lv_obj_get_scroll_left(scroll_obj);
lv_coord_t sr = lv_obj_get_scroll_right(scroll_obj);
proc->types.pointer.scroll_throw_vect.x = elastic_diff(scroll_obj, proc->types.pointer.scroll_throw_vect.x, sl, sr,
LV_DIR_HOR);
lv_obj_scroll_by(scroll_obj, proc->types.pointer.scroll_throw_vect.x, 0, LV_ANIM_OFF);
}
/*With snapping find the nearest snap point and scroll there*/
else {
lv_coord_t diff_x = scroll_throw_predict_x(proc);
proc->types.pointer.scroll_throw_vect.x = 0;
scroll_limit_diff(proc, &diff_x, NULL);
lv_coord_t x = find_snap_point_x(scroll_obj, LV_COORD_MIN, LV_COORD_MAX, diff_x);
lv_obj_scroll_by(scroll_obj, x + diff_x, 0, LV_ANIM_ON);
}
}
/*Check if the scroll has finished*/
if(proc->types.pointer.scroll_throw_vect.x == 0 && proc->types.pointer.scroll_throw_vect.y == 0) {
/*Revert if scrolled in*/
/*If vertically scrollable and not controlled by snap*/
if(align_y == LV_SCROLL_SNAP_NONE) {
lv_coord_t st = lv_obj_get_scroll_top(scroll_obj);
lv_coord_t sb = lv_obj_get_scroll_bottom(scroll_obj);
if(st > 0 || sb > 0) {
if(st < 0) {
lv_obj_scroll_by(scroll_obj, 0, st, LV_ANIM_ON);
}
else if(sb < 0) {
lv_obj_scroll_by(scroll_obj, 0, -sb, LV_ANIM_ON);
}
}
}
/*If horizontally scrollable and not controlled by snap*/
if(align_x == LV_SCROLL_SNAP_NONE) {
lv_coord_t sl = lv_obj_get_scroll_left(scroll_obj);
lv_coord_t sr = lv_obj_get_scroll_right(scroll_obj);
if(sl > 0 || sr > 0) {
if(sl < 0) {
lv_obj_scroll_by(scroll_obj, sl, 0, LV_ANIM_ON);
}
else if(sr < 0) {
lv_obj_scroll_by(scroll_obj, -sr, 0, LV_ANIM_ON);
}
}
}
lv_event_send(scroll_obj, LV_EVENT_SCROLL_END, indev_act);
if(proc->reset_query) return;
proc->types.pointer.scroll_dir = LV_DIR_NONE;
proc->types.pointer.scroll_obj = NULL;
}
}
/**
* Predict where would a scroll throw end
* @param indev pointer to an input device
* @param dir `LV_DIR_VER` or `LV_DIR_HOR`
* @return the difference compared to the current position when the throw would be finished
*/
lv_coord_t lv_indev_scroll_throw_predict(lv_indev_t * indev, lv_dir_t dir)
{
if(indev == NULL) return 0;
lv_coord_t v;
switch(dir) {
case LV_DIR_VER:
v = indev->proc.types.pointer.scroll_throw_vect_ori.y;
break;
case LV_DIR_HOR:
v = indev->proc.types.pointer.scroll_throw_vect_ori.x;
break;
default:
return 0;
}
lv_coord_t scroll_throw = indev->driver->scroll_throw;
lv_coord_t sum = 0;
while(v) {
sum += v;
v = v * (100 - scroll_throw) / 100;
}
return sum;
}
void lv_indev_scroll_get_snap_dist(lv_obj_t * obj, lv_point_t * p)
{
p->x = find_snap_point_x(obj, obj->coords.x1, obj->coords.x2, 0);
p->y = find_snap_point_y(obj, obj->coords.y1, obj->coords.y2, 0);
}
/**********************
* STATIC FUNCTIONS
**********************/
static lv_obj_t * find_scroll_obj(_lv_indev_proc_t * proc)
{
lv_obj_t * obj_candidate = NULL;
lv_dir_t dir_candidate = LV_DIR_NONE;
lv_indev_t * indev_act = lv_indev_get_act();
lv_coord_t scroll_limit = indev_act->driver->scroll_limit;
/*Go until find a scrollable object in the current direction
*More precisely:
* 1. Check the pressed object and all of its ancestors and try to find an object which is scrollable
* 2. Scrollable means it has some content out of its area
* 3. If an object can be scrolled into the current direction then use it ("real match"")
* 4. If can be scrolled on the current axis (hor/ver) save it as candidate (at least show an elastic scroll effect)
* 5. Use the last candidate. Always the "deepest" parent or the object from point 3*/
lv_obj_t * obj_act = proc->types.pointer.act_obj;
/*Decide if it's a horizontal or vertical scroll*/
bool hor_en = false;
bool ver_en = false;
if(LV_ABS(proc->types.pointer.scroll_sum.x) > LV_ABS(proc->types.pointer.scroll_sum.y)) {
hor_en = true;
}
else {
ver_en = true;
}
while(obj_act) {
if(lv_obj_has_flag(obj_act, LV_OBJ_FLAG_SCROLLABLE) == false) {
/*If this object don't want to chain the scroll to the parent stop searching*/
if(lv_obj_has_flag(obj_act, LV_OBJ_FLAG_SCROLL_CHAIN_HOR) == false && hor_en) break;
if(lv_obj_has_flag(obj_act, LV_OBJ_FLAG_SCROLL_CHAIN_VER) == false && ver_en) break;
obj_act = lv_obj_get_parent(obj_act);
continue;
}
/*Consider both up-down or left/right scrollable according to the current direction*/
bool up_en = ver_en;
bool down_en = ver_en;
bool left_en = hor_en;
bool right_en = hor_en;
/*The object might have disabled some directions.*/
lv_dir_t scroll_dir = lv_obj_get_scroll_dir(obj_act);
if((scroll_dir & LV_DIR_LEFT) == 0) left_en = false;
if((scroll_dir & LV_DIR_RIGHT) == 0) right_en = false;
if((scroll_dir & LV_DIR_TOP) == 0) up_en = false;
if((scroll_dir & LV_DIR_BOTTOM) == 0) down_en = false;
/*The object is scrollable to a direction if its content overflow in that direction.*/
lv_coord_t st = lv_obj_get_scroll_top(obj_act);
lv_coord_t sb = lv_obj_get_scroll_bottom(obj_act);
lv_coord_t sl = lv_obj_get_scroll_left(obj_act);
lv_coord_t sr = lv_obj_get_scroll_right(obj_act);
/*If this object is scrollable into the current scroll direction then save it as a candidate.
*It's important only to be scrollable on the current axis (hor/ver) because if the scroll
*is propagated to this object it can show at least elastic scroll effect.
*But if not hor/ver scrollable do not scroll it at all (so it's not a good candidate)*/
if((st > 0 || sb > 0) &&
((up_en && proc->types.pointer.scroll_sum.y >= scroll_limit) ||
(down_en && proc->types.pointer.scroll_sum.y <= - scroll_limit))) {
obj_candidate = obj_act;
dir_candidate = LV_DIR_VER;
}
if((sl > 0 || sr > 0) &&
((left_en && proc->types.pointer.scroll_sum.x >= scroll_limit) ||
(right_en && proc->types.pointer.scroll_sum.x <= - scroll_limit))) {
obj_candidate = obj_act;
dir_candidate = LV_DIR_HOR;
}
if(st <= 0) up_en = false;
if(sb <= 0) down_en = false;
if(sl <= 0) left_en = false;
if(sr <= 0) right_en = false;
/*If the object really can be scrolled into the current direction the use it.*/
if((left_en && proc->types.pointer.scroll_sum.x >= scroll_limit) ||
(right_en && proc->types.pointer.scroll_sum.x <= - scroll_limit) ||
(up_en && proc->types.pointer.scroll_sum.y >= scroll_limit) ||
(down_en && proc->types.pointer.scroll_sum.y <= - scroll_limit)) {
proc->types.pointer.scroll_dir = hor_en ? LV_DIR_HOR : LV_DIR_VER;
break;
}
/*If this object don't want to chain the scroll to the parent stop searching*/
if(lv_obj_has_flag(obj_act, LV_OBJ_FLAG_SCROLL_CHAIN_HOR) == false && hor_en) break;
if(lv_obj_has_flag(obj_act, LV_OBJ_FLAG_SCROLL_CHAIN_VER) == false && ver_en) break;
/*Try the parent*/
obj_act = lv_obj_get_parent(obj_act);
}
/*Use the last candidate*/
if(obj_candidate) {
proc->types.pointer.scroll_dir = dir_candidate;
proc->types.pointer.scroll_obj = obj_candidate;
proc->types.pointer.scroll_sum.x = 0;
proc->types.pointer.scroll_sum.y = 0;
}
return obj_candidate;
}
static void init_scroll_limits(_lv_indev_proc_t * proc)
{
lv_obj_t * obj = proc->types.pointer.scroll_obj;
/*If there no STOP allow scrolling anywhere*/
if(lv_obj_has_flag(obj, LV_OBJ_FLAG_SCROLL_ONE) == false) {
lv_area_set(&proc->types.pointer.scroll_area, LV_COORD_MIN, LV_COORD_MIN, LV_COORD_MAX, LV_COORD_MAX);
}
/*With STOP limit the scrolling to the perv and next snap point*/
else {
switch(lv_obj_get_scroll_snap_y(obj)) {
case LV_SCROLL_SNAP_START:
proc->types.pointer.scroll_area.y1 = find_snap_point_y(obj, obj->coords.y1 + 1, LV_COORD_MAX, 0);
proc->types.pointer.scroll_area.y2 = find_snap_point_y(obj, LV_COORD_MIN, obj->coords.y1 - 1, 0);
break;
case LV_SCROLL_SNAP_END:
proc->types.pointer.scroll_area.y1 = find_snap_point_y(obj, obj->coords.y2, LV_COORD_MAX, 0);
proc->types.pointer.scroll_area.y2 = find_snap_point_y(obj, LV_COORD_MIN, obj->coords.y2, 0);
break;
case LV_SCROLL_SNAP_CENTER: {
lv_coord_t y_mid = obj->coords.y1 + lv_area_get_height(&obj->coords) / 2;
proc->types.pointer.scroll_area.y1 = find_snap_point_y(obj, y_mid + 1, LV_COORD_MAX, 0);
proc->types.pointer.scroll_area.y2 = find_snap_point_y(obj, LV_COORD_MIN, y_mid - 1, 0);
break;
}
default:
proc->types.pointer.scroll_area.y1 = LV_COORD_MIN;
proc->types.pointer.scroll_area.y2 = LV_COORD_MAX;
break;
}
switch(lv_obj_get_scroll_snap_x(obj)) {
case LV_SCROLL_SNAP_START:
proc->types.pointer.scroll_area.x1 = find_snap_point_x(obj, obj->coords.x1, LV_COORD_MAX, 0);
proc->types.pointer.scroll_area.x2 = find_snap_point_x(obj, LV_COORD_MIN, obj->coords.x1, 0);
break;
case LV_SCROLL_SNAP_END:
proc->types.pointer.scroll_area.x1 = find_snap_point_x(obj, obj->coords.x2, LV_COORD_MAX, 0);
proc->types.pointer.scroll_area.x2 = find_snap_point_x(obj, LV_COORD_MIN, obj->coords.x2, 0);
break;
case LV_SCROLL_SNAP_CENTER: {
lv_coord_t x_mid = obj->coords.x1 + lv_area_get_width(&obj->coords) / 2;
proc->types.pointer.scroll_area.x1 = find_snap_point_x(obj, x_mid + 1, LV_COORD_MAX, 0);
proc->types.pointer.scroll_area.x2 = find_snap_point_x(obj, LV_COORD_MIN, x_mid - 1, 0);
break;
}
default:
proc->types.pointer.scroll_area.x1 = LV_COORD_MIN;
proc->types.pointer.scroll_area.x2 = LV_COORD_MAX;
break;
}
}
/*Allow scrolling on the edges. It will be reverted to the edge due to snapping anyway*/
if(proc->types.pointer.scroll_area.x1 == 0) proc->types.pointer.scroll_area.x1 = LV_COORD_MIN;
if(proc->types.pointer.scroll_area.x2 == 0) proc->types.pointer.scroll_area.x2 = LV_COORD_MAX;
if(proc->types.pointer.scroll_area.y1 == 0) proc->types.pointer.scroll_area.y1 = LV_COORD_MIN;
if(proc->types.pointer.scroll_area.y2 == 0) proc->types.pointer.scroll_area.y2 = LV_COORD_MAX;
}
/**
* Search for snap point in the `min` - `max` range.
* @param obj the object on which snap point should be found
* @param min ignore snap points smaller than this. (Absolute coordinate)
* @param max ignore snap points greater than this. (Absolute coordinate)
* @param ofs offset to snap points. Useful the get a snap point in an imagined case
* what if children are already moved by this value
* @return the distance of the snap point.
*/
static lv_coord_t find_snap_point_x(const lv_obj_t * obj, lv_coord_t min, lv_coord_t max, lv_coord_t ofs)
{
lv_scroll_snap_t align = lv_obj_get_scroll_snap_x(obj);
if(align == LV_SCROLL_SNAP_NONE) return 0;
lv_coord_t dist = LV_COORD_MAX;
lv_coord_t pad_left = lv_obj_get_style_pad_left(obj, LV_PART_MAIN);
lv_coord_t pad_right = lv_obj_get_style_pad_right(obj, LV_PART_MAIN);
uint32_t i;
uint32_t child_cnt = lv_obj_get_child_cnt(obj);
for(i = 0; i < child_cnt; i++) {
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;
if(lv_obj_has_flag(child, LV_OBJ_FLAG_SNAPPABLE)) {
lv_coord_t x_child = 0;
lv_coord_t x_parent = 0;
switch(align) {
case LV_SCROLL_SNAP_START:
x_child = child->coords.x1;
x_parent = obj->coords.x1 + pad_left;
break;
case LV_SCROLL_SNAP_END:
x_child = child->coords.x2;
x_parent = obj->coords.x2 - pad_right;
break;
case LV_SCROLL_SNAP_CENTER:
x_child = child->coords.x1 + lv_area_get_width(&child->coords) / 2;
x_parent = obj->coords.x1 + pad_left + (lv_area_get_width(&obj->coords) - pad_left - pad_right) / 2;
break;
default:
continue;
}
x_child += ofs;
if(x_child >= min && x_child <= max) {
lv_coord_t x = x_child - x_parent;
if(LV_ABS(x) < LV_ABS(dist)) dist = x;
}
}
}
return dist == LV_COORD_MAX ? 0 : -dist;
}
/**
* Search for snap point in the `min` - `max` range.
* @param obj the object on which snap point should be found
* @param min ignore snap points smaller than this. (Absolute coordinate)
* @param max ignore snap points greater than this. (Absolute coordinate)
* @param ofs offset to snap points. Useful to get a snap point in an imagined case
* what if children are already moved by this value
* @return the distance of the snap point.
*/
static lv_coord_t find_snap_point_y(const lv_obj_t * obj, lv_coord_t min, lv_coord_t max, lv_coord_t ofs)
{
lv_scroll_snap_t align = lv_obj_get_scroll_snap_y(obj);
if(align == LV_SCROLL_SNAP_NONE) return 0;
lv_coord_t dist = LV_COORD_MAX;
lv_coord_t pad_top = lv_obj_get_style_pad_top(obj, LV_PART_MAIN);
lv_coord_t pad_bottom = lv_obj_get_style_pad_bottom(obj, LV_PART_MAIN);
uint32_t i;
uint32_t child_cnt = lv_obj_get_child_cnt(obj);
for(i = 0; i < child_cnt; i++) {
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;
if(lv_obj_has_flag(child, LV_OBJ_FLAG_SNAPPABLE)) {
lv_coord_t y_child = 0;
lv_coord_t y_parent = 0;
switch(align) {
case LV_SCROLL_SNAP_START:
y_child = child->coords.y1;
y_parent = obj->coords.y1 + pad_top;
break;
case LV_SCROLL_SNAP_END:
y_child = child->coords.y2;
y_parent = obj->coords.y2 - pad_bottom;
break;
case LV_SCROLL_SNAP_CENTER:
y_child = child->coords.y1 + lv_area_get_height(&child->coords) / 2;
y_parent = obj->coords.y1 + pad_top + (lv_area_get_height(&obj->coords) - pad_top - pad_bottom) / 2;
break;
default:
continue;
}
y_child += ofs;
if(y_child >= min && y_child <= max) {
lv_coord_t y = y_child - y_parent;
if(LV_ABS(y) < LV_ABS(dist)) dist = y;
}
}
}
return dist == LV_COORD_MAX ? 0 : -dist;
}
static void scroll_limit_diff(_lv_indev_proc_t * proc, lv_coord_t * diff_x, lv_coord_t * diff_y)
{
if(diff_y) {
if(proc->types.pointer.scroll_sum.y + *diff_y < proc->types.pointer.scroll_area.y1) {
*diff_y = proc->types.pointer.scroll_area.y1 - proc->types.pointer.scroll_sum.y;
}
if(proc->types.pointer.scroll_sum.y + *diff_y > proc->types.pointer.scroll_area.y2) {
*diff_y = proc->types.pointer.scroll_area.y2 - proc->types.pointer.scroll_sum.y;
}
}
if(diff_x) {
if(proc->types.pointer.scroll_sum.x + *diff_x < proc->types.pointer.scroll_area.x1) {
*diff_x = proc->types.pointer.scroll_area.x1 - proc->types.pointer.scroll_sum.x;
}
if(proc->types.pointer.scroll_sum.x + *diff_x > proc->types.pointer.scroll_area.x2) {
*diff_x = proc->types.pointer.scroll_area.x2 - proc->types.pointer.scroll_sum.x;
}
}
}
static lv_coord_t scroll_throw_predict_y(_lv_indev_proc_t * proc)
{
lv_coord_t y = proc->types.pointer.scroll_throw_vect.y;
lv_coord_t move = 0;
lv_indev_t * indev_act = lv_indev_get_act();
lv_coord_t scroll_throw = indev_act->driver->scroll_throw;
while(y) {
move += y;
y = y * (100 - scroll_throw) / 100;
}
return move;
}
static lv_coord_t scroll_throw_predict_x(_lv_indev_proc_t * proc)
{
lv_coord_t x = proc->types.pointer.scroll_throw_vect.x;
lv_coord_t move = 0;
lv_indev_t * indev_act = lv_indev_get_act();
lv_coord_t scroll_throw = indev_act->driver->scroll_throw;
while(x) {
move += x;
x = x * (100 - scroll_throw) / 100;
}
return move;
}
static lv_coord_t elastic_diff(lv_obj_t * scroll_obj, lv_coord_t diff, lv_coord_t scroll_start, lv_coord_t scroll_end,
lv_dir_t dir)
{
if(lv_obj_has_flag(scroll_obj, LV_OBJ_FLAG_SCROLL_ELASTIC)) {
/*If there is snapping in the current direction don't use the elastic factor because
*it's natural that the first and last items are scrolled (snapped) in.*/
lv_scroll_snap_t snap;
snap = dir == LV_DIR_HOR ? lv_obj_get_scroll_snap_x(scroll_obj) : lv_obj_get_scroll_snap_y(scroll_obj);
lv_obj_t * act_obj = lv_indev_get_obj_act();
lv_coord_t snap_point = 0;
lv_coord_t act_obj_point = 0;
if(dir == LV_DIR_HOR) {
lv_coord_t pad_left = lv_obj_get_style_pad_left(scroll_obj, LV_PART_MAIN);
lv_coord_t pad_right = lv_obj_get_style_pad_right(scroll_obj, LV_PART_MAIN);
switch(snap) {
case LV_SCROLL_SNAP_CENTER:
snap_point = pad_left + (lv_area_get_width(&scroll_obj->coords) - pad_left - pad_right) / 2 + scroll_obj->coords.x1;
act_obj_point = lv_area_get_width(&act_obj->coords) / 2 + act_obj->coords.x1;
break;
case LV_SCROLL_SNAP_START:
snap_point = scroll_obj->coords.x1 + pad_left;
act_obj_point = act_obj->coords.x1;
break;
case LV_SCROLL_SNAP_END:
snap_point = scroll_obj->coords.x2 - pad_right;
act_obj_point = act_obj->coords.x2;
break;
}
}
else {
lv_coord_t pad_top = lv_obj_get_style_pad_top(scroll_obj, LV_PART_MAIN);
lv_coord_t pad_bottom = lv_obj_get_style_pad_bottom(scroll_obj, LV_PART_MAIN);
switch(snap) {
case LV_SCROLL_SNAP_CENTER:
snap_point = pad_top + (lv_area_get_height(&scroll_obj->coords) - pad_top - pad_bottom) / 2 + scroll_obj->coords.y1;
act_obj_point = lv_area_get_height(&act_obj->coords) / 2 + act_obj->coords.y1;
break;
case LV_SCROLL_SNAP_START:
snap_point = scroll_obj->coords.y1 + pad_top;
act_obj_point = act_obj->coords.y1;
break;
case LV_SCROLL_SNAP_END:
snap_point = scroll_obj->coords.y2 - pad_bottom;
act_obj_point = act_obj->coords.y2;
break;
}
}
if(scroll_end < 0) {
if(snap != LV_SCROLL_SNAP_NONE && act_obj_point > snap_point) return diff;
/*Rounding*/
if(diff < 0) diff -= ELASTIC_SLOWNESS_FACTOR / 2;
if(diff > 0) diff += ELASTIC_SLOWNESS_FACTOR / 2;
return diff / ELASTIC_SLOWNESS_FACTOR;
}
else if(scroll_start < 0) {
if(snap != LV_SCROLL_SNAP_NONE && act_obj_point < snap_point) return diff;
/*Rounding*/
if(diff < 0) diff -= ELASTIC_SLOWNESS_FACTOR / 2;
if(diff > 0) diff += ELASTIC_SLOWNESS_FACTOR / 2;
return diff / ELASTIC_SLOWNESS_FACTOR;
}
}
else {
/*Scroll back to the boundary if required*/
if(scroll_end + diff < 0) diff = - scroll_end;
if(scroll_start - diff < 0) diff = scroll_start;
}
return diff;
}

View File

@@ -0,0 +1,65 @@
/**
* @file lv_indev_scroll.h
*
*/
#ifndef LV_INDEV_SCROLL_H
#define LV_INDEV_SCROLL_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "lv_obj.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Handle scrolling. Called by LVGL during input device processing
* @param proc pointer to an input device's proc field
*/
void _lv_indev_scroll_handler(_lv_indev_proc_t * proc);
/**
* Handle throwing after scrolling. Called by LVGL during input device processing
* @param proc pointer to an input device's proc field
*/
void _lv_indev_scroll_throw_handler(_lv_indev_proc_t * proc);
/**
* Predict where would a scroll throw end
* @param indev pointer to an input device
* @param dir ` LV_DIR_VER` or `LV_DIR_HOR`
* @return the difference compared to the current position when the throw would be finished
*/
lv_coord_t lv_indev_scroll_throw_predict(lv_indev_t * indev, lv_dir_t dir);
/**
* Get the distance of the nearest snap point
* @param obj the object on which snap points should be found
* @param p save the distance of the found snap point there
*/
void lv_indev_scroll_get_snap_dist(lv_obj_t * obj, lv_point_t * p);
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_INDEV_SCROLL_H*/

View File

@@ -0,0 +1,955 @@
/**
* @file lv_obj.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_obj.h"
#include "lv_indev.h"
#include "lv_refr.h"
#include "lv_group.h"
#include "lv_disp.h"
#include "lv_theme.h"
#include "../misc/lv_assert.h"
#include "../draw/lv_draw.h"
#include "../misc/lv_anim.h"
#include "../misc/lv_timer.h"
#include "../misc/lv_async.h"
#include "../misc/lv_fs.h"
#include "../misc/lv_gc.h"
#include "../misc/lv_math.h"
#include "../misc/lv_log.h"
#include "../hal/lv_hal.h"
#include "../extra/lv_extra.h"
#include <stdint.h>
#include <string.h>
#if LV_USE_GPU_STM32_DMA2D
#include "../draw/stm32_dma2d/lv_gpu_stm32_dma2d.h"
#endif
#if LV_USE_GPU_NXP_PXP && LV_USE_GPU_NXP_PXP_AUTO_INIT
#include "../gpu/lv_gpu_nxp_pxp.h"
#include "../gpu/lv_gpu_nxp_pxp_osa.h"
#endif
/*********************
* DEFINES
*********************/
#define MY_CLASS &lv_obj_class
#define LV_OBJ_DEF_WIDTH (LV_DPX(100))
#define LV_OBJ_DEF_HEIGHT (LV_DPX(50))
#define STYLE_TRANSITION_MAX 32
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static void lv_obj_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj);
static void lv_obj_destructor(const lv_obj_class_t * class_p, lv_obj_t * obj);
static void lv_obj_draw(lv_event_t * e);
static void lv_obj_event(const lv_obj_class_t * class_p, lv_event_t * e);
static void draw_scrollbar(lv_obj_t * obj, lv_draw_ctx_t * draw_ctx);
static lv_res_t scrollbar_init_draw_dsc(lv_obj_t * obj, lv_draw_rect_dsc_t * dsc);
static bool obj_valid_child(const lv_obj_t * parent, const lv_obj_t * obj_to_find);
static void lv_obj_set_state(lv_obj_t * obj, lv_state_t new_state);
/**********************
* STATIC VARIABLES
**********************/
static bool lv_initialized = false;
const lv_obj_class_t lv_obj_class = {
.constructor_cb = lv_obj_constructor,
.destructor_cb = lv_obj_destructor,
.event_cb = lv_obj_event,
.width_def = LV_DPI_DEF,
.height_def = LV_DPI_DEF,
.editable = LV_OBJ_CLASS_EDITABLE_FALSE,
.group_def = LV_OBJ_CLASS_GROUP_DEF_FALSE,
.instance_size = (sizeof(lv_obj_t)),
.base_class = NULL,
};
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
bool lv_is_initialized(void)
{
return lv_initialized;
}
void lv_init(void)
{
/*Do nothing if already initialized*/
if(lv_initialized) {
LV_LOG_WARN("lv_init: already inited");
return;
}
LV_LOG_INFO("begin");
/*Initialize the misc modules*/
lv_mem_init();
_lv_timer_core_init();
_lv_fs_init();
_lv_anim_core_init();
_lv_group_init();
lv_draw_init();
#if LV_USE_GPU_STM32_DMA2D
/*Initialize DMA2D GPU*/
lv_draw_stm32_dma2d_init();
#endif
#if LV_USE_GPU_NXP_PXP && LV_USE_GPU_NXP_PXP_AUTO_INIT
if(lv_gpu_nxp_pxp_init(&pxp_default_cfg) != LV_RES_OK) {
LV_LOG_ERROR("PXP init error. STOP.\n");
for(; ;) ;
}
#endif
_lv_obj_style_init();
_lv_ll_init(&LV_GC_ROOT(_lv_disp_ll), sizeof(lv_disp_t));
_lv_ll_init(&LV_GC_ROOT(_lv_indev_ll), sizeof(lv_indev_t));
/*Initialize the screen refresh system*/
_lv_refr_init();
_lv_img_decoder_init();
#if LV_IMG_CACHE_DEF_SIZE
lv_img_cache_set_size(LV_IMG_CACHE_DEF_SIZE);
#endif
/*Test if the IDE has UTF-8 encoding*/
char * txt = "Á";
uint8_t * txt_u8 = (uint8_t *)txt;
if(txt_u8[0] != 0xc3 || txt_u8[1] != 0x81 || txt_u8[2] != 0x00) {
LV_LOG_WARN("The strings have no UTF-8 encoding. Non-ASCII characters won't be displayed.");
}
uint32_t endianess_test = 0x11223344;
uint8_t * endianess_test_p = (uint8_t *) &endianess_test;
bool big_endian = endianess_test_p[0] == 0x11 ? true : false;
if(big_endian) {
LV_ASSERT_MSG(LV_BIG_ENDIAN_SYSTEM == 1,
"It's a big endian system but LV_BIG_ENDIAN_SYSTEM is not enabled in lv_conf.h");
}
else {
LV_ASSERT_MSG(LV_BIG_ENDIAN_SYSTEM == 0,
"It's a little endian system but LV_BIG_ENDIAN_SYSTEM is enabled in lv_conf.h");
}
#if LV_USE_ASSERT_MEM_INTEGRITY
LV_LOG_WARN("Memory integrity checks are enabled via LV_USE_ASSERT_MEM_INTEGRITY which makes LVGL much slower");
#endif
#if LV_USE_ASSERT_OBJ
LV_LOG_WARN("Object sanity checks are enabled via LV_USE_ASSERT_OBJ which makes LVGL much slower");
#endif
#if LV_USE_ASSERT_STYLE
LV_LOG_WARN("Style sanity checks are enabled that uses more RAM");
#endif
#if LV_LOG_LEVEL == LV_LOG_LEVEL_TRACE
LV_LOG_WARN("Log level is set to 'Trace' which makes LVGL much slower");
#endif
lv_extra_init();
lv_initialized = true;
LV_LOG_TRACE("finished");
}
#if LV_ENABLE_GC || !LV_MEM_CUSTOM
void lv_deinit(void)
{
_lv_gc_clear_roots();
lv_disp_set_default(NULL);
lv_mem_deinit();
lv_initialized = false;
LV_LOG_INFO("lv_deinit done");
#if LV_USE_LOG
lv_log_register_print_cb(NULL);
#endif
}
#endif
lv_obj_t * lv_obj_create(lv_obj_t * parent)
{
LV_LOG_INFO("begin");
lv_obj_t * obj = lv_obj_class_create_obj(MY_CLASS, parent);
lv_obj_class_init_obj(obj);
return obj;
}
/*=====================
* Setter functions
*====================*/
/*-----------------
* Attribute set
*----------------*/
void lv_obj_add_flag(lv_obj_t * obj, lv_obj_flag_t f)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
bool was_on_layout = lv_obj_is_layout_positioned(obj);
if(f & LV_OBJ_FLAG_HIDDEN) lv_obj_invalidate(obj);
obj->flags |= f;
if(f & LV_OBJ_FLAG_HIDDEN) {
lv_obj_invalidate(obj);
}
if((was_on_layout != lv_obj_is_layout_positioned(obj)) || (f & (LV_OBJ_FLAG_LAYOUT_1 | LV_OBJ_FLAG_LAYOUT_2))) {
lv_obj_mark_layout_as_dirty(lv_obj_get_parent(obj));
lv_obj_mark_layout_as_dirty(obj);
}
if(f & LV_OBJ_FLAG_SCROLLABLE) {
lv_area_t hor_area, ver_area;
lv_obj_get_scrollbar_area(obj, &hor_area, &ver_area);
lv_obj_invalidate_area(obj, &hor_area);
lv_obj_invalidate_area(obj, &ver_area);
}
}
void lv_obj_clear_flag(lv_obj_t * obj, lv_obj_flag_t f)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
bool was_on_layout = lv_obj_is_layout_positioned(obj);
if(f & LV_OBJ_FLAG_SCROLLABLE) {
lv_area_t hor_area, ver_area;
lv_obj_get_scrollbar_area(obj, &hor_area, &ver_area);
lv_obj_invalidate_area(obj, &hor_area);
lv_obj_invalidate_area(obj, &ver_area);
}
obj->flags &= (~f);
if(f & LV_OBJ_FLAG_HIDDEN) {
lv_obj_invalidate(obj);
if(lv_obj_is_layout_positioned(obj)) {
lv_obj_mark_layout_as_dirty(lv_obj_get_parent(obj));
lv_obj_mark_layout_as_dirty(obj);
}
}
if((was_on_layout != lv_obj_is_layout_positioned(obj)) || (f & (LV_OBJ_FLAG_LAYOUT_1 | LV_OBJ_FLAG_LAYOUT_2))) {
lv_obj_mark_layout_as_dirty(lv_obj_get_parent(obj));
}
}
void lv_obj_add_state(lv_obj_t * obj, lv_state_t state)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_state_t new_state = obj->state | state;
if(obj->state != new_state) {
lv_obj_set_state(obj, new_state);
}
}
void lv_obj_clear_state(lv_obj_t * obj, lv_state_t state)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_state_t new_state = obj->state & (~state);
if(obj->state != new_state) {
lv_obj_set_state(obj, new_state);
}
}
/*=======================
* Getter functions
*======================*/
bool lv_obj_has_flag(const lv_obj_t * obj, lv_obj_flag_t f)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
return (obj->flags & f) == f ? true : false;
}
bool lv_obj_has_flag_any(const lv_obj_t * obj, lv_obj_flag_t f)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
return (obj->flags & f) ? true : false;
}
lv_state_t lv_obj_get_state(const lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
return obj->state;
}
bool lv_obj_has_state(const lv_obj_t * obj, lv_state_t state)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
return obj->state & state ? true : false;
}
void * lv_obj_get_group(const lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
if(obj->spec_attr) return obj->spec_attr->group_p;
else return NULL;
}
/*-------------------
* OTHER FUNCTIONS
*------------------*/
void lv_obj_allocate_spec_attr(lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
if(obj->spec_attr == NULL) {
static uint32_t x = 0;
x++;
obj->spec_attr = lv_mem_alloc(sizeof(_lv_obj_spec_attr_t));
LV_ASSERT_MALLOC(obj->spec_attr);
if(obj->spec_attr == NULL) return;
lv_memset_00(obj->spec_attr, sizeof(_lv_obj_spec_attr_t));
obj->spec_attr->scroll_dir = LV_DIR_ALL;
obj->spec_attr->scrollbar_mode = LV_SCROLLBAR_MODE_AUTO;
}
}
bool lv_obj_check_type(const lv_obj_t * obj, const lv_obj_class_t * class_p)
{
if(obj == NULL) return false;
return obj->class_p == class_p ? true : false;
}
bool lv_obj_has_class(const lv_obj_t * obj, const lv_obj_class_t * class_p)
{
const lv_obj_class_t * obj_class = obj->class_p;
while(obj_class) {
if(obj_class == class_p) return true;
obj_class = obj_class->base_class;
}
return false;
}
const lv_obj_class_t * lv_obj_get_class(const lv_obj_t * obj)
{
return obj->class_p;
}
bool lv_obj_is_valid(const lv_obj_t * obj)
{
lv_disp_t * disp = lv_disp_get_next(NULL);
while(disp) {
uint32_t i;
for(i = 0; i < disp->screen_cnt; i++) {
if(disp->screens[i] == obj) return true;
bool found = obj_valid_child(disp->screens[i], obj);
if(found) return true;
}
disp = lv_disp_get_next(disp);
}
return false;
}
/**********************
* STATIC FUNCTIONS
**********************/
static void lv_obj_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
{
LV_UNUSED(class_p);
LV_TRACE_OBJ_CREATE("begin");
lv_obj_t * parent = obj->parent;
if(parent) {
lv_coord_t sl = lv_obj_get_scroll_left(parent);
lv_coord_t st = lv_obj_get_scroll_top(parent);
obj->coords.y1 = parent->coords.y1 + lv_obj_get_style_pad_top(parent, LV_PART_MAIN) - st;
obj->coords.y2 = obj->coords.y1 - 1;
obj->coords.x1 = parent->coords.x1 + lv_obj_get_style_pad_left(parent, LV_PART_MAIN) - sl;
obj->coords.x2 = obj->coords.x1 - 1;
}
/*Set attributes*/
obj->flags = LV_OBJ_FLAG_CLICKABLE;
obj->flags |= LV_OBJ_FLAG_SNAPPABLE;
if(parent) obj->flags |= LV_OBJ_FLAG_PRESS_LOCK;
if(parent) obj->flags |= LV_OBJ_FLAG_SCROLL_CHAIN;
obj->flags |= LV_OBJ_FLAG_CLICK_FOCUSABLE;
obj->flags |= LV_OBJ_FLAG_SCROLLABLE;
obj->flags |= LV_OBJ_FLAG_SCROLL_ELASTIC;
obj->flags |= LV_OBJ_FLAG_SCROLL_MOMENTUM;
obj->flags |= LV_OBJ_FLAG_SCROLL_WITH_ARROW;
if(parent) obj->flags |= LV_OBJ_FLAG_GESTURE_BUBBLE;
LV_TRACE_OBJ_CREATE("finished");
}
static void lv_obj_destructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
{
LV_UNUSED(class_p);
_lv_event_mark_deleted(obj);
/*Remove all style*/
lv_obj_enable_style_refresh(false); /*No need to refresh the style because the object will be deleted*/
lv_obj_remove_style_all(obj);
lv_obj_enable_style_refresh(true);
/*Remove the animations from this object*/
lv_anim_del(obj, NULL);
/*Delete from the group*/
lv_group_t * group = lv_obj_get_group(obj);
if(group) lv_group_remove_obj(obj);
if(obj->spec_attr) {
if(obj->spec_attr->children) {
lv_mem_free(obj->spec_attr->children);
obj->spec_attr->children = NULL;
}
if(obj->spec_attr->event_dsc) {
lv_mem_free(obj->spec_attr->event_dsc);
obj->spec_attr->event_dsc = NULL;
}
lv_mem_free(obj->spec_attr);
obj->spec_attr = NULL;
}
}
static void lv_obj_draw(lv_event_t * e)
{
lv_event_code_t code = lv_event_get_code(e);
lv_obj_t * obj = lv_event_get_target(e);
if(code == LV_EVENT_COVER_CHECK) {
lv_cover_check_info_t * info = lv_event_get_param(e);
if(info->res == LV_COVER_RES_MASKED) return;
if(lv_obj_get_style_clip_corner(obj, LV_PART_MAIN)) {
info->res = LV_COVER_RES_MASKED;
return;
}
/*Most trivial test. Is the mask fully IN the object? If no it surely doesn't cover it*/
lv_coord_t r = lv_obj_get_style_radius(obj, LV_PART_MAIN);
lv_coord_t w = lv_obj_get_style_transform_width(obj, LV_PART_MAIN);
lv_coord_t h = lv_obj_get_style_transform_height(obj, LV_PART_MAIN);
lv_area_t coords;
lv_area_copy(&coords, &obj->coords);
coords.x1 -= w;
coords.x2 += w;
coords.y1 -= h;
coords.y2 += h;
if(_lv_area_is_in(info->area, &coords, r) == false) {
info->res = LV_COVER_RES_NOT_COVER;
return;
}
if(lv_obj_get_style_bg_opa(obj, LV_PART_MAIN) < LV_OPA_MAX) {
info->res = LV_COVER_RES_NOT_COVER;
return;
}
#if LV_DRAW_COMPLEX
if(lv_obj_get_style_blend_mode(obj, LV_PART_MAIN) != LV_BLEND_MODE_NORMAL) {
info->res = LV_COVER_RES_NOT_COVER;
return;
}
#endif
if(lv_obj_get_style_opa(obj, LV_PART_MAIN) < LV_OPA_MAX) {
info->res = LV_COVER_RES_NOT_COVER;
return;
}
info->res = LV_COVER_RES_COVER;
}
else if(code == LV_EVENT_DRAW_MAIN) {
lv_draw_ctx_t * draw_ctx = lv_event_get_draw_ctx(e);
lv_draw_rect_dsc_t draw_dsc;
lv_draw_rect_dsc_init(&draw_dsc);
/*If the border is drawn later disable loading its properties*/
if(lv_obj_get_style_border_post(obj, LV_PART_MAIN)) {
draw_dsc.border_post = 1;
}
lv_obj_init_draw_rect_dsc(obj, LV_PART_MAIN, &draw_dsc);
lv_coord_t w = lv_obj_get_style_transform_width(obj, LV_PART_MAIN);
lv_coord_t h = lv_obj_get_style_transform_height(obj, LV_PART_MAIN);
lv_area_t coords;
lv_area_copy(&coords, &obj->coords);
coords.x1 -= w;
coords.x2 += w;
coords.y1 -= h;
coords.y2 += h;
lv_obj_draw_part_dsc_t part_dsc;
lv_obj_draw_dsc_init(&part_dsc, draw_ctx);
part_dsc.class_p = MY_CLASS;
part_dsc.type = LV_OBJ_DRAW_PART_RECTANGLE;
part_dsc.rect_dsc = &draw_dsc;
part_dsc.draw_area = &coords;
part_dsc.part = LV_PART_MAIN;
lv_event_send(obj, LV_EVENT_DRAW_PART_BEGIN, &part_dsc);
#if LV_DRAW_COMPLEX
/*With clip corner enabled draw the bg img separately to make it clipped*/
bool clip_corner = (lv_obj_get_style_clip_corner(obj, LV_PART_MAIN) && draw_dsc.radius != 0) ? true : false;
const void * bg_img_src = draw_dsc.bg_img_src;
if(clip_corner) {
draw_dsc.bg_img_src = NULL;
}
#endif
lv_draw_rect(draw_ctx, &draw_dsc, &coords);
#if LV_DRAW_COMPLEX
if(clip_corner) {
lv_draw_mask_radius_param_t * mp = lv_mem_buf_get(sizeof(lv_draw_mask_radius_param_t));
lv_draw_mask_radius_init(mp, &obj->coords, draw_dsc.radius, false);
/*Add the mask and use `obj+8` as custom id. Don't use `obj` directly because it might be used by the user*/
lv_draw_mask_add(mp, obj + 8);
if(bg_img_src) {
draw_dsc.bg_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;
draw_dsc.bg_img_src = bg_img_src;
lv_draw_rect(draw_ctx, &draw_dsc, &coords);
}
}
#endif
lv_event_send(obj, LV_EVENT_DRAW_PART_END, &part_dsc);
}
else if(code == LV_EVENT_DRAW_POST) {
lv_draw_ctx_t * draw_ctx = lv_event_get_draw_ctx(e);
draw_scrollbar(obj, draw_ctx);
#if LV_DRAW_COMPLEX
if(lv_obj_get_style_clip_corner(obj, LV_PART_MAIN)) {
lv_draw_mask_radius_param_t * param = lv_draw_mask_remove_custom(obj + 8);
if(param) {
lv_draw_mask_free_param(param);
lv_mem_buf_release(param);
}
}
#endif
/*If the border is drawn later disable loading other properties*/
if(lv_obj_get_style_border_post(obj, LV_PART_MAIN)) {
lv_draw_rect_dsc_t draw_dsc;
lv_draw_rect_dsc_init(&draw_dsc);
draw_dsc.bg_opa = LV_OPA_TRANSP;
draw_dsc.bg_img_opa = LV_OPA_TRANSP;
draw_dsc.outline_opa = LV_OPA_TRANSP;
draw_dsc.shadow_opa = LV_OPA_TRANSP;
lv_obj_init_draw_rect_dsc(obj, LV_PART_MAIN, &draw_dsc);
lv_coord_t w = lv_obj_get_style_transform_width(obj, LV_PART_MAIN);
lv_coord_t h = lv_obj_get_style_transform_height(obj, LV_PART_MAIN);
lv_area_t coords;
lv_area_copy(&coords, &obj->coords);
coords.x1 -= w;
coords.x2 += w;
coords.y1 -= h;
coords.y2 += h;
lv_obj_draw_part_dsc_t part_dsc;
lv_obj_draw_dsc_init(&part_dsc, draw_ctx);
part_dsc.class_p = MY_CLASS;
part_dsc.type = LV_OBJ_DRAW_PART_BORDER_POST;
part_dsc.rect_dsc = &draw_dsc;
part_dsc.draw_area = &coords;
part_dsc.part = LV_PART_MAIN;
lv_event_send(obj, LV_EVENT_DRAW_PART_BEGIN, &part_dsc);
lv_draw_rect(draw_ctx, &draw_dsc, &coords);
lv_event_send(obj, LV_EVENT_DRAW_PART_END, &part_dsc);
}
}
}
static void draw_scrollbar(lv_obj_t * obj, lv_draw_ctx_t * draw_ctx)
{
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;
lv_draw_rect_dsc_t draw_dsc;
lv_res_t sb_res = scrollbar_init_draw_dsc(obj, &draw_dsc);
if(sb_res != LV_RES_OK) return;
lv_obj_draw_part_dsc_t part_dsc;
lv_obj_draw_dsc_init(&part_dsc, draw_ctx);
part_dsc.class_p = MY_CLASS;
part_dsc.type = LV_OBJ_DRAW_PART_SCROLLBAR;
part_dsc.rect_dsc = &draw_dsc;
part_dsc.part = LV_PART_SCROLLBAR;
if(lv_area_get_size(&hor_area) > 0) {
part_dsc.draw_area = &hor_area;
lv_event_send(obj, LV_EVENT_DRAW_PART_BEGIN, &part_dsc);
lv_draw_rect(draw_ctx, &draw_dsc, &hor_area);
lv_event_send(obj, LV_EVENT_DRAW_PART_END, &part_dsc);
}
if(lv_area_get_size(&ver_area) > 0) {
part_dsc.draw_area = &ver_area;
lv_event_send(obj, LV_EVENT_DRAW_PART_BEGIN, &part_dsc);
part_dsc.draw_area = &ver_area;
lv_draw_rect(draw_ctx, &draw_dsc, &ver_area);
lv_event_send(obj, LV_EVENT_DRAW_PART_END, &part_dsc);
}
}
/**
* Initialize the draw descriptor for the scrollbar
* @param obj pointer to an object
* @param dsc the draw descriptor to initialize
* @return LV_RES_OK: the scrollbar is visible; LV_RES_INV: the scrollbar is not visible
*/
static lv_res_t scrollbar_init_draw_dsc(lv_obj_t * obj, lv_draw_rect_dsc_t * dsc)
{
lv_draw_rect_dsc_init(dsc);
dsc->bg_opa = lv_obj_get_style_bg_opa(obj, LV_PART_SCROLLBAR);
if(dsc->bg_opa > LV_OPA_MIN) {
dsc->bg_color = lv_obj_get_style_bg_color(obj, LV_PART_SCROLLBAR);
}
dsc->border_opa = lv_obj_get_style_border_opa(obj, LV_PART_SCROLLBAR);
if(dsc->border_opa > LV_OPA_MIN) {
dsc->border_width = lv_obj_get_style_border_width(obj, LV_PART_SCROLLBAR);
if(dsc->border_width > 0) {
dsc->border_color = lv_obj_get_style_border_color(obj, LV_PART_SCROLLBAR);
}
else {
dsc->border_opa = LV_OPA_TRANSP;
}
}
#if LV_DRAW_COMPLEX
dsc->shadow_opa = lv_obj_get_style_shadow_opa(obj, LV_PART_SCROLLBAR);
if(dsc->shadow_opa > LV_OPA_MIN) {
dsc->shadow_width = lv_obj_get_style_shadow_width(obj, LV_PART_SCROLLBAR);
if(dsc->shadow_width > 0) {
dsc->shadow_spread = lv_obj_get_style_shadow_spread(obj, LV_PART_SCROLLBAR);
dsc->shadow_color = lv_obj_get_style_shadow_color(obj, LV_PART_SCROLLBAR);
}
else {
dsc->shadow_opa = LV_OPA_TRANSP;
}
}
lv_opa_t opa = lv_obj_get_style_opa(obj, LV_PART_SCROLLBAR);
if(opa < LV_OPA_MAX) {
dsc->bg_opa = (dsc->bg_opa * opa) >> 8;
dsc->border_opa = (dsc->bg_opa * opa) >> 8;
dsc->shadow_opa = (dsc->bg_opa * opa) >> 8;
}
if(dsc->bg_opa != LV_OPA_TRANSP || dsc->border_opa != LV_OPA_TRANSP || dsc->shadow_opa != LV_OPA_TRANSP) {
dsc->radius = lv_obj_get_style_radius(obj, LV_PART_SCROLLBAR);
return LV_RES_OK;
}
else {
return LV_RES_INV;
}
#else
if(dsc->bg_opa != LV_OPA_TRANSP || dsc->border_opa != LV_OPA_TRANSP) return LV_RES_OK;
else return LV_RES_INV;
#endif
}
static void lv_obj_event(const lv_obj_class_t * class_p, lv_event_t * e)
{
LV_UNUSED(class_p);
lv_event_code_t code = lv_event_get_code(e);
lv_obj_t * obj = lv_event_get_current_target(e);
if(code == LV_EVENT_PRESSED) {
lv_obj_add_state(obj, LV_STATE_PRESSED);
}
else if(code == LV_EVENT_RELEASED) {
lv_obj_clear_state(obj, LV_STATE_PRESSED);
void * param = lv_event_get_param(e);
/*Go the checked state if enabled*/
if(lv_indev_get_scroll_obj(param) == NULL && lv_obj_has_flag(obj, LV_OBJ_FLAG_CHECKABLE)) {
if(!(lv_obj_get_state(obj) & LV_STATE_CHECKED)) lv_obj_add_state(obj, LV_STATE_CHECKED);
else lv_obj_clear_state(obj, LV_STATE_CHECKED);
lv_res_t res = lv_event_send(obj, LV_EVENT_VALUE_CHANGED, NULL);
if(res != LV_RES_OK) return;
}
}
else if(code == LV_EVENT_PRESS_LOST) {
lv_obj_clear_state(obj, LV_STATE_PRESSED);
}
else if(code == LV_EVENT_STYLE_CHANGED) {
uint32_t child_cnt = lv_obj_get_child_cnt(obj);
for(uint32_t i = 0; i < child_cnt; i++) {
lv_obj_t * child = obj->spec_attr->children[i];
lv_obj_mark_layout_as_dirty(child);
}
}
else if(code == LV_EVENT_KEY) {
if(lv_obj_has_flag(obj, LV_OBJ_FLAG_CHECKABLE)) {
char c = *((char *)lv_event_get_param(e));
if(c == LV_KEY_RIGHT || c == LV_KEY_UP) {
lv_obj_add_state(obj, LV_STATE_CHECKED);
}
else if(c == LV_KEY_LEFT || c == LV_KEY_DOWN) {
lv_obj_clear_state(obj, LV_STATE_CHECKED);
}
/*With Enter LV_EVENT_RELEASED will send VALUE_CHANGE event*/
if(c != LV_KEY_ENTER) {
lv_res_t res = lv_event_send(obj, LV_EVENT_VALUE_CHANGED, NULL);
if(res != LV_RES_OK) return;
}
}
else if(lv_obj_has_flag(obj, LV_OBJ_FLAG_SCROLLABLE | LV_OBJ_FLAG_SCROLL_WITH_ARROW) && !lv_obj_is_editable(obj)) {
/*scroll by keypad or encoder*/
lv_anim_enable_t anim_enable = LV_ANIM_OFF;
lv_coord_t sl = lv_obj_get_scroll_left(obj);
lv_coord_t sr = lv_obj_get_scroll_right(obj);
char c = *((char *)lv_event_get_param(e));
if(c == LV_KEY_DOWN) {
/*use scroll_to_x/y functions to enforce scroll limits*/
lv_obj_scroll_to_y(obj, lv_obj_get_scroll_y(obj) + lv_obj_get_height(obj) / 4, anim_enable);
}
else if(c == LV_KEY_UP) {
lv_obj_scroll_to_y(obj, lv_obj_get_scroll_y(obj) - lv_obj_get_height(obj) / 4, anim_enable);
}
else if(c == LV_KEY_RIGHT) {
/*If the object can't be scrolled horizontally then scroll it vertically*/
if(!((lv_obj_get_scroll_dir(obj) & LV_DIR_HOR) && (sl > 0 || sr > 0)))
lv_obj_scroll_to_y(obj, lv_obj_get_scroll_y(obj) + lv_obj_get_height(obj) / 4, anim_enable);
else
lv_obj_scroll_to_x(obj, lv_obj_get_scroll_x(obj) + lv_obj_get_width(obj) / 4, anim_enable);
}
else if(c == LV_KEY_LEFT) {
/*If the object can't be scrolled horizontally then scroll it vertically*/
if(!((lv_obj_get_scroll_dir(obj) & LV_DIR_HOR) && (sl > 0 || sr > 0)))
lv_obj_scroll_to_y(obj, lv_obj_get_scroll_y(obj) - lv_obj_get_height(obj) / 4, anim_enable);
else
lv_obj_scroll_to_x(obj, lv_obj_get_scroll_x(obj) - lv_obj_get_width(obj) / 4, anim_enable);
}
}
}
else if(code == LV_EVENT_FOCUSED) {
if(lv_obj_has_flag(obj, LV_OBJ_FLAG_SCROLL_ON_FOCUS)) {
lv_obj_scroll_to_view_recursive(obj, LV_ANIM_ON);
}
bool editing = false;
editing = lv_group_get_editing(lv_obj_get_group(obj));
lv_state_t state = LV_STATE_FOCUSED;
/* Use the indev for then indev handler.
* But if the obj was focused manually it returns NULL so try to
* use the indev from the event*/
lv_indev_t * indev = lv_indev_get_act();
if(indev == NULL) indev = lv_event_get_indev(e);
lv_indev_type_t indev_type = lv_indev_get_type(indev);
if(indev_type == LV_INDEV_TYPE_KEYPAD || indev_type == LV_INDEV_TYPE_ENCODER) state |= LV_STATE_FOCUS_KEY;
if(editing) {
state |= LV_STATE_EDITED;
lv_obj_add_state(obj, state);
}
else {
lv_obj_add_state(obj, state);
lv_obj_clear_state(obj, LV_STATE_EDITED);
}
}
else if(code == LV_EVENT_SCROLL_BEGIN) {
lv_obj_add_state(obj, LV_STATE_SCROLLED);
}
else if(code == LV_EVENT_SCROLL_END) {
lv_obj_clear_state(obj, LV_STATE_SCROLLED);
if(lv_obj_get_scrollbar_mode(obj) == LV_SCROLLBAR_MODE_ACTIVE) {
lv_area_t hor_area, ver_area;
lv_obj_get_scrollbar_area(obj, &hor_area, &ver_area);
lv_obj_invalidate_area(obj, &hor_area);
lv_obj_invalidate_area(obj, &ver_area);
}
}
else if(code == LV_EVENT_DEFOCUSED) {
lv_obj_clear_state(obj, LV_STATE_FOCUSED | LV_STATE_EDITED | LV_STATE_FOCUS_KEY);
}
else if(code == LV_EVENT_SIZE_CHANGED) {
lv_coord_t align = lv_obj_get_style_align(obj, LV_PART_MAIN);
uint16_t layout = lv_obj_get_style_layout(obj, LV_PART_MAIN);
if(layout || align) {
lv_obj_mark_layout_as_dirty(obj);
}
uint32_t i;
uint32_t child_cnt = lv_obj_get_child_cnt(obj);
for(i = 0; i < child_cnt; i++) {
lv_obj_t * child = obj->spec_attr->children[i];
lv_obj_mark_layout_as_dirty(child);
}
}
else if(code == LV_EVENT_CHILD_CHANGED) {
lv_coord_t w = lv_obj_get_style_width(obj, LV_PART_MAIN);
lv_coord_t h = lv_obj_get_style_height(obj, LV_PART_MAIN);
lv_coord_t align = lv_obj_get_style_align(obj, LV_PART_MAIN);
uint16_t layout = lv_obj_get_style_layout(obj, LV_PART_MAIN);
if(layout || align || w == LV_SIZE_CONTENT || h == LV_SIZE_CONTENT) {
lv_obj_mark_layout_as_dirty(obj);
}
}
else if(code == LV_EVENT_REFR_EXT_DRAW_SIZE) {
lv_coord_t * s = lv_event_get_param(e);
lv_coord_t d = lv_obj_calculate_ext_draw_size(obj, LV_PART_MAIN);
*s = LV_MAX(*s, d);
}
else if(code == LV_EVENT_DRAW_MAIN || code == LV_EVENT_DRAW_POST || code == LV_EVENT_COVER_CHECK) {
lv_obj_draw(e);
}
}
/**
* Set the state (fully overwrite) of an object.
* If specified in the styles, transition animations will be started from the previous state to the current.
* @param obj pointer to an object
* @param state the new state
*/
static void lv_obj_set_state(lv_obj_t * obj, lv_state_t new_state)
{
if(obj->state == new_state) return;
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_state_t prev_state = obj->state;
obj->state = new_state;
_lv_style_state_cmp_t cmp_res = _lv_obj_style_state_compare(obj, prev_state, new_state);
/*If there is no difference in styles there is nothing else to do*/
if(cmp_res == _LV_STYLE_STATE_CMP_SAME) return;
_lv_obj_style_transition_dsc_t * ts = lv_mem_buf_get(sizeof(_lv_obj_style_transition_dsc_t) * STYLE_TRANSITION_MAX);
lv_memset_00(ts, sizeof(_lv_obj_style_transition_dsc_t) * STYLE_TRANSITION_MAX);
uint32_t tsi = 0;
uint32_t i;
for(i = 0; i < obj->style_cnt && tsi < STYLE_TRANSITION_MAX; i++) {
_lv_obj_style_t * obj_style = &obj->styles[i];
lv_state_t state_act = lv_obj_style_get_selector_state(obj->styles[i].selector);
lv_part_t part_act = lv_obj_style_get_selector_part(obj->styles[i].selector);
if(state_act & (~new_state)) continue; /*Skip unrelated styles*/
if(obj_style->is_trans) continue;
lv_style_value_t v;
if(lv_style_get_prop_inlined(obj_style->style, LV_STYLE_TRANSITION, &v) == false) continue;
const lv_style_transition_dsc_t * tr = v.ptr;
/*Add the props to the set if not added yet or added but with smaller weight*/
uint32_t j;
for(j = 0; tr->props[j] != 0 && tsi < STYLE_TRANSITION_MAX; j++) {
uint32_t t;
for(t = 0; t < tsi; t++) {
lv_style_selector_t selector = ts[t].selector;
lv_state_t state_ts = lv_obj_style_get_selector_state(selector);
lv_part_t part_ts = lv_obj_style_get_selector_part(selector);
if(ts[t].prop == tr->props[j] && part_ts == part_act && state_ts >= state_act) break;
}
/*If not found add it*/
if(t == tsi) {
ts[tsi].time = tr->time;
ts[tsi].delay = tr->delay;
ts[tsi].path_cb = tr->path_xcb;
ts[tsi].prop = tr->props[j];
#if LV_USE_USER_DATA
ts[tsi].user_data = tr->user_data;
#endif
ts[tsi].selector = obj_style->selector;
tsi++;
}
}
}
for(i = 0; i < tsi; i++) {
lv_part_t part_act = lv_obj_style_get_selector_part(ts[i].selector);
_lv_obj_style_create_transition(obj, part_act, prev_state, new_state, &ts[i]);
}
lv_mem_buf_release(ts);
if(cmp_res == _LV_STYLE_STATE_CMP_DIFF_REDRAW) {
lv_obj_invalidate(obj);
}
else if(cmp_res == _LV_STYLE_STATE_CMP_DIFF_LAYOUT) {
lv_obj_refresh_style(obj, LV_PART_ANY, LV_STYLE_PROP_ANY);
}
else if(cmp_res == _LV_STYLE_STATE_CMP_DIFF_DRAW_PAD) {
lv_obj_invalidate(obj);
lv_obj_refresh_ext_draw_size(obj);
}
}
static bool obj_valid_child(const lv_obj_t * parent, const lv_obj_t * obj_to_find)
{
/*Check all children of `parent`*/
uint32_t child_cnt = 0;
if(parent->spec_attr) child_cnt = parent->spec_attr->child_cnt;
uint32_t i;
for(i = 0; i < child_cnt; i++) {
lv_obj_t * child = parent->spec_attr->children[i];
if(child == obj_to_find) {
return true;
}
/*Check the children*/
bool found = obj_valid_child(child, obj_to_find);
if(found) {
return true;
}
}
return false;
}

View File

@@ -0,0 +1,408 @@
/**
* @file lv_obj.h
*
*/
#ifndef LV_OBJ_H
#define LV_OBJ_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../lv_conf_internal.h"
#include <stddef.h>
#include <stdbool.h>
#include "../misc/lv_style.h"
#include "../misc/lv_types.h"
#include "../misc/lv_area.h"
#include "../misc/lv_color.h"
#include "../misc/lv_assert.h"
#include "../hal/lv_hal.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
struct _lv_obj_t;
/**
* 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*/
};
typedef uint16_t lv_state_t;
/**
* 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_TICKS = 0x060000, /**< Ticks on scale e.g. for a chart or meter*/
LV_PART_CURSOR = 0x070000, /**< 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*/
};
typedef uint32_t lv_part_t;
/**
* On/Off features controlling the object's behavior.
* OR-ed values are possible
*/
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 position-able by the layouts*/
LV_OBJ_FLAG_FLOATING = (1L << 18), /**< Do not scroll the object when the parent scrolls and ignore layout*/
LV_OBJ_FLAG_OVERFLOW_VISIBLE = (1L << 19), /**< Do not clip the children's content to the parent's boundary*/
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*/
};
typedef uint32_t lv_obj_flag_t;
/**
* `type` field in `lv_obj_draw_part_dsc_t` if `class_p = lv_obj_class`
* Used in `LV_EVENT_DRAW_PART_BEGIN` and `LV_EVENT_DRAW_PART_END`
*/
typedef enum {
LV_OBJ_DRAW_PART_RECTANGLE, /**< The main rectangle*/
LV_OBJ_DRAW_PART_BORDER_POST,/**< The border if style_border_post = true*/
LV_OBJ_DRAW_PART_SCROLLBAR, /**< The scrollbar*/
} lv_obj_draw_part_type_t;
#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_event.h"
#include "lv_group.h"
/**
* Make the base object's class publicly available.
*/
extern const lv_obj_class_t lv_obj_class;
/**
* Special, rarely used attributes.
* They are allocated automatically if any elements is set.
*/
typedef struct {
struct _lv_obj_t ** children; /**< Store the pointer of the children in an array.*/
uint32_t child_cnt; /**< Number of children*/
lv_group_t * group_p;
struct _lv_event_dsc_t * event_dsc; /**< Dynamically allocated event callback and user data array*/
lv_point_t scroll; /**< The current X/Y scroll offset*/
lv_coord_t ext_click_pad; /**< Extra click padding in all direction*/
lv_coord_t ext_draw_size; /**< EXTend the size in every direction for drawing.*/
lv_scrollbar_mode_t scrollbar_mode : 2; /**< How to display scrollbars*/
lv_scroll_snap_t scroll_snap_x : 2; /**< Where to align the snappable children horizontally*/
lv_scroll_snap_t scroll_snap_y : 2; /**< Where to align the snappable children vertically*/
lv_dir_t scroll_dir : 4; /**< The allowed scroll direction(s)*/
uint8_t event_dsc_cnt; /**< Number of event callbacks stored in `event_dsc` array*/
} _lv_obj_spec_attr_t;
typedef struct _lv_obj_t {
const lv_obj_class_t * class_p;
struct _lv_obj_t * parent;
_lv_obj_spec_attr_t * spec_attr;
_lv_obj_style_t * styles;
#if LV_USE_USER_DATA
void * user_data;
#endif
lv_area_t coords;
lv_obj_flag_t flags;
lv_state_t state;
uint16_t layout_inv : 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;
} lv_obj_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Initialize LVGL library.
* Should be called before any other LVGL related function.
*/
void lv_init(void);
#if LV_ENABLE_GC || !LV_MEM_CUSTOM
/**
* Deinit the 'lv' library
* Currently only implemented when not using custom allocators, or GC is enabled.
*/
void lv_deinit(void);
#endif
/**
* Returns whether the 'lv' library is currently initialized
*/
bool lv_is_initialized(void);
/**
* 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 R-ed values from `lv_obj_flag_t` to set.
*/
void lv_obj_add_flag(lv_obj_t * obj, lv_obj_flag_t f);
/**
* Clear 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_clear_flag(lv_obj_t * obj, lv_obj_flag_t f);
/**
* 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_clear_state(lv_obj_t * obj, lv_state_t state);
/**
* Set the user_data field of the object
* @param obj pointer to an object
* @param user_data pointer to the new user_data.
*/
#if LV_USE_USER_DATA
static inline void lv_obj_set_user_data(lv_obj_t * obj, void * user_data)
{
obj->user_data = user_data;
}
#endif
/*=======================
* 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 lest one flag 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
*/
void * 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
*/
#if LV_USE_USER_DATA
static inline void * lv_obj_get_user_data(lv_obj_t * obj)
{
return obj->user_data;
}
#endif
/*=======================
* 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);
/**
* Scale the given number of pixels (a distance or size) relative to a 160 DPI display
* considering the DPI of the `obj`'s display.
* It ensures that e.g. `lv_dpx(100)` will have the same physical size regardless to the
* DPI of the display.
* @param obj an object whose display's dpi should be considered
* @param n the number of pixels to scale
* @return `n x current_dpi/160`
*/
static inline lv_coord_t lv_obj_dpx(const lv_obj_t * obj, lv_coord_t n)
{
return _LV_DPX_CALC(lv_disp_get_dpi(lv_obj_get_disp(obj)), n);
}
/**********************
* 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) do{}while(0)
#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*/

View File

@@ -0,0 +1,202 @@
/**
* @file lv_obj_class.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_obj.h"
#include "lv_theme.h"
/*********************
* DEFINES
*********************/
#define MY_CLASS &lv_obj_class
/**********************
* TYPEDEFS
**********************/
/**********************
* GLOBAL PROTOTYPES
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static void lv_obj_construct(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_mem_alloc(s);
if(obj == NULL) return NULL;
lv_memset_00(obj, s);
obj->class_p = class_p;
obj->parent = parent;
/*Create a screen*/
if(parent == NULL) {
LV_TRACE_OBJ_CREATE("creating a screen");
lv_disp_t * disp = lv_disp_get_default();
if(!disp) {
LV_LOG_WARN("No display created yet. No place to assign the new screen");
lv_mem_free(obj);
return NULL;
}
if(disp->screens == NULL) {
disp->screens = lv_mem_alloc(sizeof(lv_obj_t *));
disp->screens[0] = obj;
disp->screen_cnt = 1;
}
else {
disp->screen_cnt++;
disp->screens = lv_mem_realloc(disp->screens, sizeof(lv_obj_t *) * disp->screen_cnt);
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_disp_get_hor_res(NULL) - 1;
obj->coords.y2 = lv_disp_get_ver_res(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);
}
if(parent->spec_attr->children == NULL) {
parent->spec_attr->children = lv_mem_alloc(sizeof(lv_obj_t *));
parent->spec_attr->children[0] = obj;
parent->spec_attr->child_cnt = 1;
}
else {
parent->spec_attr->child_cnt++;
parent->spec_attr->children = lv_mem_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)
{
lv_obj_mark_layout_as_dirty(obj);
lv_obj_enable_style_refresh(false);
lv_theme_apply(obj);
lv_obj_construct(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_event_send(parent, LV_EVENT_CHILD_CHANGED, obj);
lv_event_send(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 ? true : false;
}
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 ? true : false;
}
/**********************
* STATIC FUNCTIONS
**********************/
static void lv_obj_construct(lv_obj_t * obj)
{
const lv_obj_class_t * original_class_p = obj->class_p;
if(obj->class_p->base_class) {
/*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(obj);
}
/*Restore the original class*/
obj->class_p = original_class_p;
if(obj->class_p->constructor_cb) obj->class_p->constructor_cb(obj->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;
}

View File

@@ -0,0 +1,94 @@
/**
* @file lv_obj_class.h
*
*/
#ifndef LV_OBJ_CLASS_H
#define LV_OBJ_CLASS_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include <stdint.h>
#include <stdbool.h>
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
struct _lv_obj_t;
struct _lv_obj_class_t;
struct _lv_event_t;
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 void (*lv_obj_class_event_cb_t)(struct _lv_obj_class_t * class_p, struct _lv_event_t * e);
/**
* Describe the common methods of every object.
* Similar to a C++ class.
*/
typedef struct _lv_obj_class_t {
const struct _lv_obj_class_t * base_class;
void (*constructor_cb)(const struct _lv_obj_class_t * class_p, struct _lv_obj_t * obj);
void (*destructor_cb)(const struct _lv_obj_class_t * class_p, struct _lv_obj_t * obj);
#if LV_USE_USER_DATA
void * user_data;
#endif
void (*event_cb)(const struct _lv_obj_class_t * class_p,
struct _lv_event_t * e); /**< Widget type specific event function*/
lv_coord_t width_def;
lv_coord_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;
} lv_obj_class_t;
/**********************
* 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
*/
struct _lv_obj_t * lv_obj_class_create_obj(const struct _lv_obj_class_t * class_p, struct _lv_obj_t * parent);
void lv_obj_class_init_obj(struct _lv_obj_t * obj);
void _lv_obj_destruct(struct _lv_obj_t * obj);
bool lv_obj_is_editable(struct _lv_obj_t * obj);
bool lv_obj_is_group_def(struct _lv_obj_t * obj);
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_OBJ_CLASS_H*/

View File

@@ -0,0 +1,378 @@
/**
* @file lv_obj_draw.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_obj_draw.h"
#include "lv_obj.h"
#include "lv_disp.h"
#include "lv_indev.h"
/*********************
* DEFINES
*********************/
#define MY_CLASS &lv_obj_class
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
void lv_obj_init_draw_rect_dsc(lv_obj_t * obj, uint32_t part, lv_draw_rect_dsc_t * draw_dsc)
{
#if LV_DRAW_COMPLEX
draw_dsc->radius = lv_obj_get_style_radius(obj, part);
lv_opa_t main_opa = part != LV_PART_MAIN ? lv_obj_get_style_opa(obj, part) : LV_OPA_COVER;
lv_opa_t opa = lv_obj_get_style_opa(obj, part);
if(opa <= LV_OPA_MIN || main_opa <= LV_OPA_MIN) {
draw_dsc->bg_opa = LV_OPA_TRANSP;
draw_dsc->border_opa = LV_OPA_TRANSP;
draw_dsc->shadow_opa = LV_OPA_TRANSP;
draw_dsc->outline_opa = LV_OPA_TRANSP;
return;
}
draw_dsc->blend_mode = lv_obj_get_style_blend_mode(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) {
draw_dsc->bg_color = lv_obj_get_style_bg_color_filtered(obj, part);
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 = lv_obj_get_style_bg_color_filtered(obj, part);
draw_dsc->bg_grad.stops[1].color = lv_obj_get_style_bg_grad_color_filtered(obj, part);
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.dither = lv_obj_get_style_bg_dither_mode(obj, part);
}
}
}
draw_dsc->border_width = lv_obj_get_style_border_width(obj, part);
if(draw_dsc->border_width) {
if(draw_dsc->border_opa != LV_OPA_TRANSP) {
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);
draw_dsc->border_color = lv_obj_get_style_border_color_filtered(obj, part);
}
}
}
draw_dsc->outline_width = lv_obj_get_style_outline_width(obj, part);
if(draw_dsc->outline_width) {
if(draw_dsc->outline_opa != LV_OPA_TRANSP) {
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);
draw_dsc->outline_color = lv_obj_get_style_outline_color_filtered(obj, part);
}
}
}
if(draw_dsc->bg_img_opa != LV_OPA_TRANSP) {
draw_dsc->bg_img_src = lv_obj_get_style_bg_img_src(obj, part);
if(draw_dsc->bg_img_src) {
draw_dsc->bg_img_opa = lv_obj_get_style_bg_img_opa(obj, part);
if(draw_dsc->bg_img_opa > LV_OPA_MIN) {
if(lv_img_src_get_type(draw_dsc->bg_img_src) == LV_IMG_SRC_SYMBOL) {
draw_dsc->bg_img_symbol_font = lv_obj_get_style_text_font(obj, part);
draw_dsc->bg_img_recolor = lv_obj_get_style_text_color_filtered(obj, part);
}
else {
draw_dsc->bg_img_recolor = lv_obj_get_style_bg_img_recolor_filtered(obj, part);
draw_dsc->bg_img_recolor_opa = lv_obj_get_style_bg_img_recolor_opa(obj, part);
draw_dsc->bg_img_tiled = lv_obj_get_style_bg_img_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_ofs_x = lv_obj_get_style_shadow_ofs_x(obj, part);
draw_dsc->shadow_ofs_y = lv_obj_get_style_shadow_ofs_y(obj, part);
draw_dsc->shadow_spread = lv_obj_get_style_shadow_spread(obj, part);
draw_dsc->shadow_color = lv_obj_get_style_shadow_color_filtered(obj, part);
}
}
}
}
if(main_opa < LV_OPA_MAX) {
opa = (uint16_t)((uint16_t) main_opa * opa) >> 8;
}
if(opa < LV_OPA_MAX) {
draw_dsc->bg_opa = (uint16_t)((uint16_t)draw_dsc->bg_opa * opa) >> 8;
draw_dsc->bg_img_opa = (uint16_t)((uint16_t)draw_dsc->bg_img_opa * opa) >> 8;
draw_dsc->border_opa = (uint16_t)((uint16_t)draw_dsc->border_opa * opa) >> 8;
draw_dsc->shadow_opa = (uint16_t)((uint16_t)draw_dsc->shadow_opa * opa) >> 8;
draw_dsc->outline_opa = (uint16_t)((uint16_t)draw_dsc->outline_opa * opa) >> 8;
}
#else /*LV_DRAW_COMPLEX*/
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) {
draw_dsc->bg_color = lv_obj_get_style_bg_color_filtered(obj, part);
}
}
draw_dsc->border_width = lv_obj_get_style_border_width(obj, part);
if(draw_dsc->border_width) {
if(draw_dsc->border_opa != LV_OPA_TRANSP) {
draw_dsc->border_opa = lv_obj_get_style_border_opa(obj, part);
if(draw_dsc->border_opa > LV_OPA_MIN) {
draw_dsc->border_color = lv_obj_get_style_border_color_filtered(obj, part);
draw_dsc->border_side = lv_obj_get_style_border_side(obj, part);
}
}
}
draw_dsc->outline_width = lv_obj_get_style_outline_width(obj, part);
if(draw_dsc->outline_width) {
if(draw_dsc->outline_opa != LV_OPA_TRANSP) {
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);
draw_dsc->outline_color = lv_obj_get_style_outline_color_filtered(obj, part);
}
}
}
if(draw_dsc->bg_img_opa != LV_OPA_TRANSP) {
draw_dsc->bg_img_src = lv_obj_get_style_bg_img_src(obj, part);
if(draw_dsc->bg_img_src) {
draw_dsc->bg_img_opa = lv_obj_get_style_bg_img_opa(obj, part);
if(draw_dsc->bg_img_opa > LV_OPA_MIN) {
if(lv_img_src_get_type(draw_dsc->bg_img_src) == LV_IMG_SRC_SYMBOL) {
draw_dsc->bg_img_symbol_font = lv_obj_get_style_text_font(obj, part);
draw_dsc->bg_img_recolor = lv_obj_get_style_text_color_filtered(obj, part);
}
else {
draw_dsc->bg_img_recolor = lv_obj_get_style_bg_img_recolor_filtered(obj, part);
draw_dsc->bg_img_recolor_opa = lv_obj_get_style_bg_img_recolor_opa(obj, part);
draw_dsc->bg_img_tiled = lv_obj_get_style_bg_img_tiled(obj, part);
}
}
}
}
#endif
}
void lv_obj_init_draw_label_dsc(lv_obj_t * obj, uint32_t part, lv_draw_label_dsc_t * draw_dsc)
{
draw_dsc->opa = lv_obj_get_style_text_opa(obj, part);
if(draw_dsc->opa <= LV_OPA_MIN) return;
lv_opa_t opa = lv_obj_get_style_opa(obj, part);
if(opa < LV_OPA_MAX) {
draw_dsc->opa = (uint16_t)((uint16_t)draw_dsc->opa * opa) >> 8;
}
if(draw_dsc->opa <= LV_OPA_MIN) return;
draw_dsc->color = lv_obj_get_style_text_color_filtered(obj, part);
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);
#if LV_DRAW_COMPLEX
draw_dsc->blend_mode = lv_obj_get_style_blend_mode(obj, part);
#endif
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);
}
void lv_obj_init_draw_img_dsc(lv_obj_t * obj, uint32_t part, lv_draw_img_dsc_t * draw_dsc)
{
draw_dsc->opa = lv_obj_get_style_img_opa(obj, part);
if(draw_dsc->opa <= LV_OPA_MIN) return;
lv_opa_t opa_scale = lv_obj_get_style_opa(obj, part);
if(opa_scale < LV_OPA_MAX) {
draw_dsc->opa = (uint16_t)((uint16_t)draw_dsc->opa * opa_scale) >> 8;
}
if(draw_dsc->opa <= LV_OPA_MIN) return;
draw_dsc->angle = 0;
draw_dsc->zoom = LV_IMG_ZOOM_NONE;
draw_dsc->pivot.x = lv_area_get_width(&obj->coords) / 2;
draw_dsc->pivot.y = lv_area_get_height(&obj->coords) / 2;
draw_dsc->recolor_opa = lv_obj_get_style_img_recolor_opa(obj, part);
if(draw_dsc->recolor_opa > 0) {
draw_dsc->recolor = lv_obj_get_style_img_recolor_filtered(obj, part);
}
#if LV_DRAW_COMPLEX
draw_dsc->blend_mode = lv_obj_get_style_blend_mode(obj, part);
#endif
}
void lv_obj_init_draw_line_dsc(lv_obj_t * obj, uint32_t part, lv_draw_line_dsc_t * draw_dsc)
{
draw_dsc->width = lv_obj_get_style_line_width(obj, part);
if(draw_dsc->width == 0) return;
draw_dsc->opa = lv_obj_get_style_line_opa(obj, part);
if(draw_dsc->opa <= LV_OPA_MIN) return;
lv_opa_t opa = lv_obj_get_style_opa(obj, part);
if(opa < LV_OPA_MAX) {
draw_dsc->opa = (uint16_t)((uint16_t)draw_dsc->opa * opa) >> 8;
}
if(draw_dsc->opa <= LV_OPA_MIN) return;
draw_dsc->color = lv_obj_get_style_line_color_filtered(obj, part);
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;
#if LV_DRAW_COMPLEX
draw_dsc->blend_mode = lv_obj_get_style_blend_mode(obj, part);
#endif
}
void lv_obj_init_draw_arc_dsc(lv_obj_t * obj, uint32_t part, lv_draw_arc_dsc_t * draw_dsc)
{
draw_dsc->width = lv_obj_get_style_arc_width(obj, part);
if(draw_dsc->width == 0) return;
draw_dsc->opa = lv_obj_get_style_arc_opa(obj, part);
if(draw_dsc->opa <= LV_OPA_MIN) return;
lv_opa_t opa = lv_obj_get_style_opa(obj, part);
if(opa < LV_OPA_MAX) {
draw_dsc->opa = (uint16_t)((uint16_t)draw_dsc->opa * opa) >> 8;
}
if(draw_dsc->opa <= LV_OPA_MIN) return;
draw_dsc->color = lv_obj_get_style_arc_color_filtered(obj, part);
draw_dsc->img_src = lv_obj_get_style_arc_img_src(obj, part);
draw_dsc->rounded = lv_obj_get_style_arc_rounded(obj, part);
#if LV_DRAW_COMPLEX
draw_dsc->blend_mode = lv_obj_get_style_blend_mode(obj, part);
#endif
}
lv_coord_t lv_obj_calculate_ext_draw_size(lv_obj_t * obj, uint32_t part)
{
lv_coord_t s = 0;
lv_coord_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);
lv_coord_t sh_ofs_x = lv_obj_get_style_shadow_ofs_x(obj, part);
lv_coord_t sh_ofs_y = lv_obj_get_style_shadow_ofs_y(obj, part);
sh_width += LV_MAX(LV_ABS(sh_ofs_x), LV_ABS(sh_ofs_y));
s = LV_MAX(s, sh_width);
}
}
lv_coord_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) {
lv_coord_t outline_pad = lv_obj_get_style_outline_pad(obj, part);
s = LV_MAX(s, outline_pad + outline_width);
}
}
lv_coord_t w = lv_obj_get_style_transform_width(obj, part);
lv_coord_t h = lv_obj_get_style_transform_height(obj, part);
lv_coord_t wh = LV_MAX(w, h);
if(wh > 0) s += wh;
return s;
}
void lv_obj_draw_dsc_init(lv_obj_draw_part_dsc_t * dsc, lv_draw_ctx_t * draw_ctx)
{
lv_memset_00(dsc, sizeof(lv_obj_draw_part_dsc_t));
dsc->draw_ctx = draw_ctx;
}
bool lv_obj_draw_part_check_type(lv_obj_draw_part_dsc_t * dsc, const lv_obj_class_t * class_p, uint32_t type)
{
if(dsc->class_p == class_p && dsc->type == type) return true;
else return false;
}
void lv_obj_refresh_ext_draw_size(lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_coord_t s_old = _lv_obj_get_ext_draw_size(obj);
lv_coord_t s_new = 0;
lv_event_send(obj, LV_EVENT_REFR_EXT_DRAW_SIZE, &s_new);
if(s_new != s_old) lv_obj_invalidate(obj);
/*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_coord_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;
}
/**********************
* STATIC FUNCTIONS
**********************/

View File

@@ -0,0 +1,163 @@
/**
* @file lv_obj_draw.h
*
*/
#ifndef LV_OBJ_DRAW_H
#define LV_OBJ_DRAW_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../draw/lv_draw.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
struct _lv_obj_t;
struct _lv_obj_class_t;
/** 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;
typedef struct {
lv_draw_ctx_t * draw_ctx; /**< Draw context*/
const struct _lv_obj_class_t * class_p; /**< The class that sent the event */
uint32_t type; /**< The type if part being draw. Element of `lv_<name>_draw_part_type_t` */
lv_area_t * draw_area; /**< The area of the part being drawn*/
lv_draw_rect_dsc_t *
rect_dsc; /**< A draw descriptor that can be modified to changed what LVGL will draw. Set only for rectangle-like parts*/
lv_draw_label_dsc_t *
label_dsc; /**< A draw descriptor that can be modified to changed what LVGL will draw. Set only for text-like parts*/
lv_draw_line_dsc_t *
line_dsc; /**< A draw descriptor that can be modified to changed what LVGL will draw. Set only for line-like parts*/
lv_draw_img_dsc_t *
img_dsc; /**< A draw descriptor that can be modified to changed what LVGL will draw. Set only for image-like parts*/
lv_draw_arc_dsc_t *
arc_dsc; /**< A draw descriptor that can be modified to changed what LVGL will draw. Set only for arc-like parts*/
const lv_point_t *
p1; /**< A point calculated during drawing. E.g. a point of chart or the center of an arc.*/
const lv_point_t * p2; /**< A point calculated during drawing. E.g. a point of chart.*/
char * text; /**< A text calculated during drawing. Can be modified. E.g. tick labels on a chart axis.*/
uint32_t text_length; /**< Size of the text buffer containing null-terminated text string calculated during drawing.*/
uint32_t part; /**< The current part for which the event is sent*/
uint32_t id; /**< The index of the part. E.g. a button's index on button matrix or table cell index.*/
lv_coord_t radius; /**< E.g. the radius of an arc (not the corner radius).*/
int32_t value; /**< A value calculated during drawing. E.g. Chart's tick line value.*/
const void * sub_part_ptr; /**< A pointer the identifies something in the part. E.g. chart series. */
} lv_obj_draw_part_dsc_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(struct _lv_obj_t * obj, uint32_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(struct _lv_obj_t * obj, uint32_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_img_dsc(struct _lv_obj_t * obj, uint32_t part, lv_draw_img_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(struct _lv_obj_t * obj, uint32_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(struct _lv_obj_t * obj, uint32_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
*/
lv_coord_t lv_obj_calculate_ext_draw_size(struct _lv_obj_t * obj, uint32_t part);
/**
* Initialize a draw descriptor used in events.
* @param dsc pointer to a descriptor. Later it should be passed as parameter to an `LV_EVENT_DRAW_PART_BEGIN/END` event.
* @param draw the current draw context. (usually returned by `lv_event_get_draw_ctx(e)`)
*/
void lv_obj_draw_dsc_init(lv_obj_draw_part_dsc_t * dsc, lv_draw_ctx_t * draw_ctx);
/**
* Check the type obj a part draw descriptor
* @param dsc the descriptor (normally the event parameter)
* @param class_p pointer to class to which `type` is related
* @param type element of `lv_<name>_draw_part_type_t`
* @return true if ::dsc is related to ::class_p and ::type
*/
bool lv_obj_draw_part_check_type(lv_obj_draw_part_dsc_t * dsc, const struct _lv_obj_class_t * class_p, uint32_t type);
/**
* 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(struct _lv_obj_t * obj);
/**
* Get the extended draw area of an object.
* @param obj pointer to an object
* @return the size extended draw area around the real coordinates
*/
lv_coord_t _lv_obj_get_ext_draw_size(const struct _lv_obj_t * obj);
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_OBJ_DRAW_H*/

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,431 @@
/**
* @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
**********************/
struct _lv_obj_t;
typedef void (*lv_layout_update_cb_t)(struct _lv_obj_t *, void * user_data);
typedef struct {
lv_layout_update_cb_t cb;
void * user_data;
} lv_layout_dsc_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(struct _lv_obj_t * obj, lv_coord_t x, lv_coord_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(struct _lv_obj_t * obj, lv_coord_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(struct _lv_obj_t * obj, lv_coord_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_SIZE_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(struct _lv_obj_t * obj, lv_coord_t w, lv_coord_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(struct _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(struct _lv_obj_t * obj, lv_coord_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(struct _lv_obj_t * obj, lv_coord_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(struct _lv_obj_t * obj, lv_coord_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(struct _lv_obj_t * obj, lv_coord_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(struct _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 struct _lv_obj_t * obj);
/**
* Mark the object for layout update.
* @param obj pointer to an object whose children needs to be updated
*/
void lv_obj_mark_layout_as_dirty(struct _lv_obj_t * obj);
/**
* Update the layout of an object.
* @param obj pointer to an object whose children needs to be updated
*/
void lv_obj_update_layout(const struct _lv_obj_t * obj);
/**
* Register a new layout
* @param cb the layout update callback
* @param user_data custom data that will be passed to `cb`
* @return the ID of the new layout
*/
uint32_t lv_layout_register(lv_layout_update_cb_t cb, void * user_data);
/**
* 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(struct _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(struct _lv_obj_t * obj, lv_align_t align, lv_coord_t x_ofs, lv_coord_t y_ofs);
/**
* Align an object to an other object.
* @param obj pointer to an object to align
* @param base pointer to an other 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(struct _lv_obj_t * obj, const struct _lv_obj_t * base, lv_align_t align, lv_coord_t x_ofs,
lv_coord_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
*/
static inline void lv_obj_center(struct _lv_obj_t * obj)
{
lv_obj_align(obj, LV_ALIGN_CENTER, 0, 0);
}
/**
* 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 struct _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.
*/
lv_coord_t lv_obj_get_x(const struct _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.
*/
lv_coord_t lv_obj_get_x2(const struct _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.
*/
lv_coord_t lv_obj_get_y(const struct _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.
*/
lv_coord_t lv_obj_get_y2(const struct _lv_obj_t * obj);
/**
* Get the actually set x coordinate of object, i.e. the offset form the set alignment
* @param obj pointer to an object
* @return the set x coordinate
*/
lv_coord_t lv_obj_get_x_aligned(const struct _lv_obj_t * obj);
/**
* Get the actually set y coordinate of object, i.e. the offset form the set alignment
* @param obj pointer to an object
* @return the set y coordinate
*/
lv_coord_t lv_obj_get_y_aligned(const struct _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
*/
lv_coord_t lv_obj_get_width(const struct _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
*/
lv_coord_t lv_obj_get_height(const struct _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)
*/
lv_coord_t lv_obj_get_content_width(const struct _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)
*/
lv_coord_t lv_obj_get_content_height(const struct _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 struct _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.
*/
lv_coord_t lv_obj_get_self_width(const struct _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.
*/
lv_coord_t lv_obj_get_self_height(const struct _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(struct _lv_obj_t * obj);
void lv_obj_refr_pos(struct _lv_obj_t * obj);
void lv_obj_move_to(struct _lv_obj_t * obj, lv_coord_t x, lv_coord_t y);
void lv_obj_move_children_by(struct _lv_obj_t * obj, lv_coord_t x_diff, lv_coord_t y_diff, bool ignore_floating);
/**
* 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 struct _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 struct _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 struct _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 struct _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(struct _lv_obj_t * obj, lv_coord_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 struct _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(struct _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
*/
lv_coord_t lv_clamp_width(lv_coord_t width, lv_coord_t min_width, lv_coord_t max_width, lv_coord_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
*/
lv_coord_t lv_clamp_height(lv_coord_t height, lv_coord_t min_height, lv_coord_t max_height, lv_coord_t ref_height);
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_OBJ_POS_H*/

View File

@@ -0,0 +1,799 @@
/**
* @file lv_obj_scroll.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_obj_scroll.h"
#include "lv_obj.h"
#include "lv_indev.h"
#include "lv_disp.h"
#include "lv_indev_scroll.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 lv_res_t scroll_by_raw(lv_obj_t * obj, lv_coord_t x, lv_coord_t y);
static void scroll_x_anim(void * obj, int32_t v);
static void scroll_y_anim(void * obj, int32_t v);
static void scroll_anim_ready_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 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 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 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 obj->spec_attr->scroll_snap_y;
else return LV_SCROLL_SNAP_NONE;
}
lv_coord_t lv_obj_get_scroll_x(const lv_obj_t * obj)
{
if(obj->spec_attr == NULL) return 0;
return -obj->spec_attr->scroll.x;
}
lv_coord_t lv_obj_get_scroll_y(const lv_obj_t * obj)
{
if(obj->spec_attr == NULL) return 0;
return -obj->spec_attr->scroll.y;
}
lv_coord_t lv_obj_get_scroll_top(lv_obj_t * obj)
{
if(obj->spec_attr == NULL) return 0;
return -obj->spec_attr->scroll.y;
}
lv_coord_t lv_obj_get_scroll_bottom(lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_coord_t child_res = LV_COORD_MIN;
uint32_t i;
uint32_t child_cnt = lv_obj_get_child_cnt(obj);
for(i = 0; i < child_cnt; i++) {
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;
child_res = LV_MAX(child_res, child->coords.y2);
}
lv_coord_t pad_top = lv_obj_get_style_pad_top(obj, LV_PART_MAIN);
lv_coord_t pad_bottom = lv_obj_get_style_pad_bottom(obj, LV_PART_MAIN);
lv_coord_t border_width = lv_obj_get_style_border_width(obj, LV_PART_MAIN);
if(child_res != LV_COORD_MIN) {
child_res -= (obj->coords.y2 - pad_bottom - border_width);
}
lv_coord_t self_h = lv_obj_get_self_height(obj);
self_h = self_h - (lv_obj_get_height(obj) - pad_top - pad_bottom - 2 * border_width);
self_h -= lv_obj_get_scroll_y(obj);
return LV_MAX(child_res, self_h);
}
lv_coord_t lv_obj_get_scroll_left(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*/
lv_coord_t pad_right = lv_obj_get_style_pad_right(obj, LV_PART_MAIN);
lv_coord_t pad_left = lv_obj_get_style_pad_left(obj, LV_PART_MAIN);
lv_coord_t border_width = lv_obj_get_style_border_width(obj, LV_PART_MAIN);
lv_coord_t child_res = 0;
uint32_t i;
lv_coord_t x1 = LV_COORD_MAX;
uint32_t child_cnt = lv_obj_get_child_cnt(obj);
for(i = 0; i < child_cnt; i++) {
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;
x1 = LV_MIN(x1, child->coords.x1);
}
if(x1 != LV_COORD_MAX) {
child_res = x1;
child_res = (obj->coords.x1 + pad_left + border_width) - child_res;
}
else {
child_res = LV_COORD_MIN;
}
lv_coord_t self_w = lv_obj_get_self_width(obj);
self_w = self_w - (lv_obj_get_width(obj) - pad_right - pad_left - 2 * border_width);
self_w += lv_obj_get_scroll_x(obj);
return LV_MAX(child_res, self_w);
}
lv_coord_t lv_obj_get_scroll_right(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*/
lv_coord_t child_res = LV_COORD_MIN;
uint32_t i;
uint32_t child_cnt = lv_obj_get_child_cnt(obj);
for(i = 0; i < child_cnt; i++) {
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;
child_res = LV_MAX(child_res, child->coords.x2);
}
lv_coord_t pad_right = lv_obj_get_style_pad_right(obj, LV_PART_MAIN);
lv_coord_t pad_left = lv_obj_get_style_pad_left(obj, LV_PART_MAIN);
lv_coord_t border_width = lv_obj_get_style_border_width(obj, LV_PART_MAIN);
if(child_res != LV_COORD_MIN) {
child_res -= (obj->coords.x2 - pad_right - border_width);
}
lv_coord_t self_w;
self_w = lv_obj_get_self_width(obj);
self_w = self_w - (lv_obj_get_width(obj) - pad_right - pad_left - 2 * border_width);
self_w -= lv_obj_get_scroll_x(obj);
return LV_MAX(child_res, self_w);
}
void lv_obj_get_scroll_end(struct _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, lv_coord_t dx, lv_coord_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 then naturally possible by the size of the content*/
lv_coord_t x_current = -lv_obj_get_scroll_x(obj);
lv_coord_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) {
lv_coord_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) {
lv_coord_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 then naturally possible by the size of the content*/
lv_coord_t y_current = -lv_obj_get_scroll_y(obj);
lv_coord_t y_bounded = y_current + dy;
if(y_bounded > 0) y_bounded = 0;
if(y_bounded < 0) {
lv_coord_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, lv_coord_t dx, lv_coord_t dy, lv_anim_enable_t anim_en)
{
if(dx == 0 && dy == 0) return;
if(anim_en == LV_ANIM_ON) {
lv_disp_t * d = lv_obj_get_disp(obj);
lv_anim_t a;
lv_anim_init(&a);
lv_anim_set_var(&a, obj);
lv_anim_set_ready_cb(&a, scroll_anim_ready_cb);
if(dx) {
uint32_t t = lv_anim_speed_to_time((lv_disp_get_hor_res(d) * 2) >> 2, 0, dx);
if(t < SCROLL_ANIM_TIME_MIN) t = SCROLL_ANIM_TIME_MIN;
if(t > SCROLL_ANIM_TIME_MAX) t = SCROLL_ANIM_TIME_MAX;
lv_anim_set_time(&a, t);
lv_coord_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_res_t res;
res = lv_event_send(obj, LV_EVENT_SCROLL_BEGIN, &a);
if(res != LV_RES_OK) return;
lv_anim_start(&a);
}
if(dy) {
uint32_t t = lv_anim_speed_to_time((lv_disp_get_ver_res(d) * 2) >> 2, 0, dy);
if(t < SCROLL_ANIM_TIME_MIN) t = SCROLL_ANIM_TIME_MIN;
if(t > SCROLL_ANIM_TIME_MAX) t = SCROLL_ANIM_TIME_MAX;
lv_anim_set_time(&a, t);
lv_coord_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_res_t res;
res = lv_event_send(obj, LV_EVENT_SCROLL_BEGIN, &a);
if(res != LV_RES_OK) return;
lv_anim_start(&a);
}
}
else {
/*Remove pending animations*/
lv_anim_del(obj, scroll_y_anim);
lv_anim_del(obj, scroll_x_anim);
lv_res_t res;
res = lv_event_send(obj, LV_EVENT_SCROLL_BEGIN, NULL);
if(res != LV_RES_OK) return;
res = scroll_by_raw(obj, dx, dy);
if(res != LV_RES_OK) return;
res = lv_event_send(obj, LV_EVENT_SCROLL_END, NULL);
if(res != LV_RES_OK) return;
}
}
void lv_obj_scroll_to(lv_obj_t * obj, lv_coord_t x, lv_coord_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, lv_coord_t x, lv_anim_enable_t anim_en)
{
lv_anim_del(obj, scroll_x_anim);
lv_coord_t scroll_x = lv_obj_get_scroll_x(obj);
lv_coord_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, lv_coord_t y, lv_anim_enable_t anim_en)
{
lv_anim_del(obj, scroll_y_anim);
lv_coord_t scroll_y = lv_obj_get_scroll_y(obj);
lv_coord_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);
}
}
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);
}
return false;
}
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);
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_dir_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;
}
lv_coord_t st = lv_obj_get_scroll_top(obj);
lv_coord_t sb = lv_obj_get_scroll_bottom(obj);
lv_coord_t sl = lv_obj_get_scroll_left(obj);
lv_coord_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 ? true : false;
lv_coord_t top_space = lv_obj_get_style_pad_top(obj, LV_PART_SCROLLBAR);
lv_coord_t bottom_space = lv_obj_get_style_pad_bottom(obj, LV_PART_SCROLLBAR);
lv_coord_t left_space = lv_obj_get_style_pad_left(obj, LV_PART_SCROLLBAR);
lv_coord_t right_space = lv_obj_get_style_pad_right(obj, LV_PART_SCROLLBAR);
lv_coord_t tickness = lv_obj_get_style_width(obj, LV_PART_SCROLLBAR);
lv_coord_t obj_h = lv_obj_get_height(obj);
lv_coord_t obj_w = lv_obj_get_width(obj);
/*Space required for the vertical and horizontal scrollbars*/
lv_coord_t ver_reg_space = ver_draw ? tickness : 0;
lv_coord_t hor_req_space = hor_draw ? tickness : 0;
lv_coord_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*/
lv_coord_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 + tickness - 1;
}
else {
ver_area->x2 = obj->coords.x2 - right_space;
ver_area->x1 = ver_area->x2 - tickness + 1;
}
lv_coord_t sb_h = ((obj_h - top_space - bottom_space - hor_req_space) * obj_h) / content_h;
sb_h = LV_MAX(sb_h, SCROLLBAR_MIN_SIZE);
rem = (obj_h - top_space - bottom_space - hor_req_space) -
sb_h; /*Remaining size from the scrollbar track that is not the scrollbar itself*/
lv_coord_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 {
lv_coord_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*/
lv_coord_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 - tickness + 1;
hor_area->x1 = obj->coords.x1;
hor_area->x2 = obj->coords.x2;
lv_coord_t sb_w = ((obj_w - left_space - right_space - ver_reg_space) * obj_w) / content_w;
sb_w = LV_MAX(sb_w, SCROLLBAR_MIN_SIZE);
rem = (obj_w - left_space - right_space - ver_reg_space) -
sb_w; /*Remaining size from the scrollbar track that is not the scrollbar itself*/
lv_coord_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 {
lv_coord_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) {
lv_coord_t st = lv_obj_get_scroll_top(obj);
lv_coord_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) {
lv_coord_t sl = lv_obj_get_scroll_left(obj);
lv_coord_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 lv_res_t scroll_by_raw(lv_obj_t * obj, lv_coord_t x, lv_coord_t y)
{
if(x == 0 && y == 0) return LV_RES_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_res_t res = lv_event_send(obj, LV_EVENT_SCROLL, NULL);
if(res != LV_RES_OK) return res;
lv_obj_invalidate(obj);
return LV_RES_OK;
}
static void scroll_x_anim(void * obj, int32_t v)
{
scroll_by_raw(obj, v + lv_obj_get_scroll_x(obj), 0);
}
static void scroll_y_anim(void * obj, int32_t v)
{
scroll_by_raw(obj, 0, v + lv_obj_get_scroll_y(obj));
}
static void scroll_anim_ready_cb(lv_anim_t * a)
{
lv_event_send(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);
lv_coord_t snap_goal = 0;
lv_coord_t act = 0;
const lv_area_t * area_tmp;
lv_coord_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;
lv_coord_t border_width = lv_obj_get_style_border_width(parent, LV_PART_MAIN);
lv_coord_t ptop = lv_obj_get_style_pad_top(parent, LV_PART_MAIN) + border_width;
lv_coord_t pbottom = lv_obj_get_style_pad_bottom(parent, LV_PART_MAIN) + border_width;
lv_coord_t top_diff = parent->coords.y1 + ptop - area_tmp->y1 - scroll_value->y;
lv_coord_t bottom_diff = -(parent->coords.y2 - pbottom - area_tmp->y2 - scroll_value->y);
lv_coord_t parent_h = lv_obj_get_height(parent) - ptop - pbottom;
if((top_diff >= 0 && bottom_diff >= 0)) y_scroll = 0;
else if(top_diff > 0) {
y_scroll = top_diff;
/*Do not let scrolling in*/
lv_coord_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*/
lv_coord_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 + ptop;
act = area_tmp->y1 + y_scroll;
y_scroll += snap_goal - act;
break;
case LV_SCROLL_SNAP_END:
snap_goal = parent->coords.y2 - pbottom;
act = area_tmp->y2 + y_scroll;
y_scroll += snap_goal - act;
break;
case LV_SCROLL_SNAP_CENTER:
snap_goal = parent->coords.y1 + ptop + parent_h / 2;
act = lv_area_get_height(area_tmp) / 2 + area_tmp->y1 + y_scroll;
y_scroll += snap_goal - act;
break;
}
lv_coord_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;
lv_coord_t pleft = lv_obj_get_style_pad_left(parent, LV_PART_MAIN) + border_width;
lv_coord_t pright = lv_obj_get_style_pad_right(parent, LV_PART_MAIN) + border_width;
lv_coord_t left_diff = parent->coords.x1 + pleft - area_tmp->x1 - scroll_value->x;
lv_coord_t right_diff = -(parent->coords.x2 - pright - 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*/
lv_coord_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*/
lv_coord_t sr = lv_obj_get_scroll_right(parent);
if(sr + x_scroll < 0) x_scroll = 0;
}
lv_coord_t parent_w = lv_obj_get_width(parent) - pleft - pright;
switch(snap_x) {
case LV_SCROLL_SNAP_START:
snap_goal = parent->coords.x1 + pleft;
act = area_tmp->x1 + x_scroll;
x_scroll += snap_goal - act;
break;
case LV_SCROLL_SNAP_END:
snap_goal = parent->coords.x2 - pright;
act = area_tmp->x2 + x_scroll;
x_scroll += snap_goal - act;
break;
case LV_SCROLL_SNAP_CENTER:
snap_goal = parent->coords.x1 + pleft + parent_w / 2;
act = lv_area_get_width(area_tmp) / 2 + area_tmp->x1 + x_scroll;
x_scroll += snap_goal - act;
break;
}
/*Remove any pending scroll animations.*/
bool y_del = lv_anim_del(parent, scroll_y_anim);
bool x_del = lv_anim_del(parent, scroll_x_anim);
if(y_del || x_del) {
lv_res_t res;
res = lv_event_send(parent, LV_EVENT_SCROLL_END, NULL);
if(res != LV_RES_OK) return;
}
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 == LV_ANIM_OFF ? 0 : x_scroll;
scroll_value->y += anim_en == LV_ANIM_OFF ? 0 : y_scroll;
lv_obj_scroll_by(parent, x_scroll, y_scroll, anim_en);
}

View File

@@ -0,0 +1,294 @@
/**
* @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"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/*Can't include lv_obj.h because it includes this header file*/
struct _lv_obj_t;
/** Scrollbar modes: shows when should the scrollbars be visible*/
enum {
LV_SCROLLBAR_MODE_OFF, /**< Never show scrollbars*/
LV_SCROLLBAR_MODE_ON, /**< Always show scrollbars*/
LV_SCROLLBAR_MODE_ACTIVE, /**< Show scroll bars when object is being scrolled*/
LV_SCROLLBAR_MODE_AUTO, /**< Show scroll bars when the content is large enough to be scrolled*/
};
typedef uint8_t lv_scrollbar_mode_t;
/** Scroll span align options. Tells where to align the snappable children when scroll stops.*/
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*/
};
typedef uint8_t lv_scroll_snap_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
/*=====================
* Setter functions
*====================*/
/**
* Set how the scrollbars should behave.
* @param obj pointer to an object
* @param mode LV_SCROLL_MODE_ON/OFF/AUTO/ACTIVE
*/
void lv_obj_set_scrollbar_mode(struct _lv_obj_t * obj, lv_scrollbar_mode_t mode);
/**
* Set the object in which directions can be scrolled
* @param obj pointer to an object
* @param dir the allow scroll directions. An element or OR-ed values of `lv_dir_t`
*/
void lv_obj_set_scroll_dir(struct _lv_obj_t * obj, lv_dir_t dir);
/**
* Set where to snap the children when scrolling ends horizontally
* @param obj pointer to an object
* @param align the snap align to set from `lv_scroll_snap_t`
*/
void lv_obj_set_scroll_snap_x(struct _lv_obj_t * obj, lv_scroll_snap_t align);
/**
* Set where to snap the children when scrolling ends vertically
* @param obj pointer to an object
* @param align the snap align to set from `lv_scroll_snap_t`
*/
void lv_obj_set_scroll_snap_y(struct _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 an object
* @return the current scroll mode from `lv_scrollbar_mode_t`
*/
lv_scrollbar_mode_t lv_obj_get_scrollbar_mode(const struct _lv_obj_t * obj);
/**
* Get the object in which directions can be scrolled
* @param obj pointer to an object
* @param dir the allow scroll directions. An element or OR-ed values of `lv_dir_t`
*/
lv_dir_t lv_obj_get_scroll_dir(const struct _lv_obj_t * obj);
/**
* Get where to snap the children when scrolling ends horizontally
* @param obj pointer to an object
* @return the current snap align from `lv_scroll_snap_t`
*/
lv_scroll_snap_t lv_obj_get_scroll_snap_x(const struct _lv_obj_t * obj);
/**
* Get where to snap the children when scrolling ends vertically
* @param obj pointer to an object
* @return the current snap align from `lv_scroll_snap_t`
*/
lv_scroll_snap_t lv_obj_get_scroll_snap_y(const struct _lv_obj_t * obj);
/**
* Get current X scroll position.
* @param obj pointer to an object
* @return the current scroll position from the left edge.
* If the object is not scrolled return 0
* If scrolled return > 0
* If scrolled in (elastic scroll) return < 0
*/
lv_coord_t lv_obj_get_scroll_x(const struct _lv_obj_t * obj);
/**
* Get current Y scroll position.
* @param obj pointer to an object
* @return the current scroll position from the top edge.
* If the object is not scrolled return 0
* If scrolled return > 0
* If scrolled inside return < 0
*/
lv_coord_t lv_obj_get_scroll_y(const struct _lv_obj_t * obj);
/**
* Return the height of the area above the object.
* That is the number of pixels the object can be scrolled down.
* Normally positive but can be negative when scrolled inside.
* @param obj pointer to an object
* @return the scrollable area above the object in pixels
*/
lv_coord_t lv_obj_get_scroll_top(struct _lv_obj_t * obj);
/**
* Return the height of the area below the object.
* That is the number of pixels the object can be scrolled down.
* Normally positive but can be negative when scrolled inside.
* @param obj pointer to an object
* @return the scrollable area below the object in pixels
*/
lv_coord_t lv_obj_get_scroll_bottom(struct _lv_obj_t * obj);
/**
* Return the width of the area on the left the object.
* That is the number of pixels the object can be scrolled down.
* Normally positive but can be negative when scrolled inside.
* @param obj pointer to an object
* @return the scrollable area on the left the object in pixels
*/
lv_coord_t lv_obj_get_scroll_left(struct _lv_obj_t * obj);
/**
* Return the width of the area on the right the object.
* That is the number of pixels the object can be scrolled down.
* Normally positive but can be negative when scrolled inside.
* @param obj pointer to an object
* @return the scrollable area on the right the object in pixels
*/
lv_coord_t lv_obj_get_scroll_right(struct _lv_obj_t * obj);
/**
* Get the X and Y coordinates where the scrolling will end for this object if a scrolling animation is in progress.
* If no scrolling animation, give the current `x` or `y` scroll position.
* @param obj pointer to an object
* @param end pointer to store the result
*/
void lv_obj_get_scroll_end(struct _lv_obj_t * obj, lv_point_t * end);
/*=====================
* Other functions
*====================*/
/**
* Scroll by a given amount of pixels
* @param obj pointer to an object 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(struct _lv_obj_t * obj, lv_coord_t x, lv_coord_t y, lv_anim_enable_t anim_en);
/**
* Scroll by a given amount of pixels.
* `dx` and `dy` will be limited internally to allow scrolling only on the content area.
* @param obj pointer to an object 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(struct _lv_obj_t * obj, lv_coord_t dx, lv_coord_t dy, lv_anim_enable_t anim_en);
/**
* Scroll to a given coordinate on an object.
* `x` and `y` will be limited internally to allow scrolling only on the content area.
* @param obj pointer to an object 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(struct _lv_obj_t * obj, lv_coord_t x, lv_coord_t y, lv_anim_enable_t anim_en);
/**
* Scroll to a given X coordinate on an object.
* `x` will be limited internally to allow scrolling only on the content area.
* @param obj pointer to an object 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(struct _lv_obj_t * obj, lv_coord_t x, lv_anim_enable_t anim_en);
/**
* Scroll to a given Y coordinate on an object
* `y` will be limited internally to allow scrolling only on the content area.
* @param obj pointer to an object 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(struct _lv_obj_t * obj, lv_coord_t y, lv_anim_enable_t anim_en);
/**
* Scroll to an object until it becomes visible on its parent
* @param obj pointer to an object to scroll into view
* @param anim_en LV_ANIM_ON: scroll with animation; LV_ANIM_OFF: scroll immediately
*/
void lv_obj_scroll_to_view(struct _lv_obj_t * obj, lv_anim_enable_t anim_en);
/**
* Scroll to an object until it becomes visible on its parent.
* Do the same on the parent's parent, and so on.
* Therefore the object will be scrolled into view even it has nested scrollable parents
* @param obj pointer to an object 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(struct _lv_obj_t * obj, lv_anim_enable_t anim_en);
/**
* Tell whether an object is being scrolled or not at this moment
* @param obj pointer to an object
* @return true: `obj` is being scrolled
*/
bool lv_obj_is_scrolling(const struct _lv_obj_t * obj);
/**
* Check the children of `obj` and scroll `obj` to fulfill the scroll_snap settings
* @param obj an object whose children needs to checked and snapped
* @param anim_en LV_ANIM_ON/OFF
*/
void lv_obj_update_snap(struct _lv_obj_t * obj, lv_anim_enable_t anim_en);
/**
* Get the area of the scrollbars
* @param obj pointer to an object
* @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(struct _lv_obj_t * obj, lv_area_t * hor, lv_area_t * ver);
/**
* Invalidate the area of the scrollbars
* @param obj pointer to an object
*/
void lv_obj_scrollbar_invalidate(struct _lv_obj_t * obj);
/**
* Checked if the content is scrolled "in" and adjusts it to a normal position.
* @param obj pointer to an object
* @param anim_en LV_ANIM_ON/OFF
*/
void lv_obj_readjust_scroll(struct _lv_obj_t * obj, lv_anim_enable_t anim_en);
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_OBJ_SCROLL_H*/

View File

@@ -0,0 +1,824 @@
/**
* @file lv_obj_style.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_obj.h"
#include "lv_disp.h"
#include "../misc/lv_gc.h"
/*********************
* DEFINES
*********************/
#define MY_CLASS &lv_obj_class
/**********************
* TYPEDEFS
**********************/
typedef struct {
lv_obj_t * obj;
lv_style_prop_t prop;
lv_style_selector_t selector;
lv_style_value_t start_value;
lv_style_value_t end_value;
} trans_t;
typedef enum {
CACHE_ZERO = 0,
CACHE_TRUE = 1,
CACHE_UNSET = 2,
CACHE_255 = 3,
CACHE_NEED_CHECK = 4,
} cache_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static lv_style_t * get_local_style(lv_obj_t * obj, lv_style_selector_t selector);
static _lv_obj_style_t * get_trans_style(lv_obj_t * obj, uint32_t part);
static bool get_prop_core(const lv_obj_t * obj, lv_part_t part, lv_style_prop_t prop, lv_style_value_t * v);
static void report_style_change_core(void * style, lv_obj_t * obj);
static void refresh_children_style(lv_obj_t * obj);
static bool trans_del(lv_obj_t * obj, lv_part_t part, lv_style_prop_t prop, trans_t * tr_limit);
static void trans_anim_cb(void * _tr, int32_t v);
static void trans_anim_start_cb(lv_anim_t * a);
static void trans_anim_ready_cb(lv_anim_t * a);
static void fade_anim_cb(void * obj, int32_t v);
static void fade_in_anim_ready(lv_anim_t * a);
/**********************
* STATIC VARIABLES
**********************/
static bool style_refr = true;
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
void _lv_obj_style_init(void)
{
_lv_ll_init(&LV_GC_ROOT(_lv_obj_style_trans_ll), sizeof(trans_t));
}
void lv_obj_add_style(lv_obj_t * obj, lv_style_t * style, lv_style_selector_t selector)
{
trans_del(obj, selector, LV_STYLE_PROP_ANY, NULL);
uint32_t i;
/*Go after the transition and local styles*/
for(i = 0; i < obj->style_cnt; i++) {
if(obj->styles[i].is_trans) continue;
if(obj->styles[i].is_local) continue;
break;
}
/*Now `i` is at the first normal style. Insert the new style before this*/
/*Allocate space for the new style and shift the rest of the style to the end*/
obj->style_cnt++;
obj->styles = lv_mem_realloc(obj->styles, obj->style_cnt * sizeof(_lv_obj_style_t));
uint32_t j;
for(j = obj->style_cnt - 1; j > i ; j--) {
obj->styles[j] = obj->styles[j - 1];
}
lv_memset_00(&obj->styles[i], sizeof(_lv_obj_style_t));
obj->styles[i].style = style;
obj->styles[i].selector = selector;
lv_obj_refresh_style(obj, selector, LV_STYLE_PROP_ANY);
}
void lv_obj_remove_style(lv_obj_t * obj, lv_style_t * style, lv_style_selector_t selector)
{
lv_state_t state = lv_obj_style_get_selector_state(selector);
lv_part_t part = lv_obj_style_get_selector_part(selector);
lv_style_prop_t prop = LV_STYLE_PROP_ANY;
if(style && style->prop_cnt == 0) prop = LV_STYLE_PROP_INV;
uint32_t i = 0;
bool deleted = false;
while(i < obj->style_cnt) {
lv_state_t state_act = lv_obj_style_get_selector_state(obj->styles[i].selector);
lv_part_t part_act = lv_obj_style_get_selector_part(obj->styles[i].selector);
if((state != LV_STATE_ANY && state_act != state) ||
(part != LV_PART_ANY && part_act != part) ||
(style != NULL && style != obj->styles[i].style)) {
i++;
continue;
}
if(obj->styles[i].is_trans) {
trans_del(obj, part, LV_STYLE_PROP_ANY, NULL);
}
if(obj->styles[i].is_local || obj->styles[i].is_trans) {
lv_style_reset(obj->styles[i].style);
lv_mem_free(obj->styles[i].style);
obj->styles[i].style = NULL;
}
/*Shift the styles after `i` by one*/
uint32_t j;
for(j = i; j < (uint32_t)obj->style_cnt - 1 ; j++) {
obj->styles[j] = obj->styles[j + 1];
}
obj->style_cnt--;
obj->styles = lv_mem_realloc(obj->styles, obj->style_cnt * sizeof(_lv_obj_style_t));
deleted = true;
/*The style from the current `i` index is removed, so `i` points to the next style.
*Therefore it doesn't needs to be incremented*/
}
if(deleted && prop != LV_STYLE_PROP_INV) {
lv_obj_refresh_style(obj, part, prop);
}
}
void lv_obj_report_style_change(lv_style_t * style)
{
if(!style_refr) return;
lv_disp_t * d = lv_disp_get_next(NULL);
while(d) {
uint32_t i;
for(i = 0; i < d->screen_cnt; i++) {
report_style_change_core(style, d->screens[i]);
}
d = lv_disp_get_next(d);
}
}
void lv_obj_refresh_style(lv_obj_t * obj, lv_style_selector_t selector, lv_style_prop_t prop)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
if(!style_refr) return;
lv_obj_invalidate(obj);
lv_part_t part = lv_obj_style_get_selector_part(selector);
bool is_layout_refr = lv_style_prop_has_flag(prop, LV_STYLE_PROP_LAYOUT_REFR);
bool is_ext_draw = lv_style_prop_has_flag(prop, LV_STYLE_PROP_EXT_DRAW);
bool is_inherit = lv_style_prop_has_flag(prop, LV_STYLE_PROP_INHERIT);
if(is_layout_refr) {
if(part == LV_PART_ANY ||
part == LV_PART_MAIN ||
lv_obj_get_style_height(obj, 0) == LV_SIZE_CONTENT ||
lv_obj_get_style_width(obj, 0) == LV_SIZE_CONTENT) {
lv_event_send(obj, LV_EVENT_STYLE_CHANGED, NULL);
lv_obj_mark_layout_as_dirty(obj);
}
}
if((part == LV_PART_ANY || part == LV_PART_MAIN) && (prop == LV_STYLE_PROP_ANY || is_layout_refr)) {
lv_obj_t * parent = lv_obj_get_parent(obj);
if(parent) lv_obj_mark_layout_as_dirty(parent);
}
if(prop == LV_STYLE_PROP_ANY || is_ext_draw) {
lv_obj_refresh_ext_draw_size(obj);
}
lv_obj_invalidate(obj);
if(prop == LV_STYLE_PROP_ANY || (is_inherit && (is_ext_draw || is_layout_refr))) {
if(part != LV_PART_SCROLLBAR) {
refresh_children_style(obj);
}
}
}
void lv_obj_enable_style_refresh(bool en)
{
style_refr = en;
}
lv_style_value_t lv_obj_get_style_prop(const lv_obj_t * obj, lv_part_t part, lv_style_prop_t prop)
{
lv_style_value_t value_act;
bool inherit = lv_style_prop_has_flag(prop, LV_STYLE_PROP_INHERIT);
bool found = false;
while(obj) {
found = get_prop_core(obj, part, prop, &value_act);
if(found) break;
if(!inherit) break;
/*If not found, check the `MAIN` style first*/
if(part != LV_PART_MAIN) {
part = LV_PART_MAIN;
continue;
}
/*Check the parent too.*/
obj = lv_obj_get_parent(obj);
}
if(!found) {
if(part == LV_PART_MAIN && (prop == LV_STYLE_WIDTH || prop == LV_STYLE_HEIGHT)) {
const lv_obj_class_t * cls = obj->class_p;
while(cls) {
if(prop == LV_STYLE_WIDTH) {
if(cls->width_def != 0) break;
}
else {
if(cls->height_def != 0) break;
}
cls = cls->base_class;
}
if(cls) {
value_act.num = prop == LV_STYLE_WIDTH ? cls->width_def : cls->height_def;
}
else {
value_act.num = 0;
}
}
else {
value_act = lv_style_prop_get_default(prop);
}
}
return value_act;
}
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_t * style = get_local_style(obj, selector);
lv_style_set_prop(style, prop, value);
lv_obj_refresh_style(obj, selector, prop);
}
lv_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)
{
uint32_t i;
for(i = 0; i < obj->style_cnt; i++) {
if(obj->styles[i].is_local &&
obj->styles[i].selector == selector) {
return lv_style_get_prop(obj->styles[i].style, prop, value);
}
}
return LV_RES_INV;
}
bool lv_obj_remove_local_style_prop(lv_obj_t * obj, lv_style_prop_t prop, lv_style_selector_t selector)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
uint32_t i;
/*Find the style*/
for(i = 0; i < obj->style_cnt; i++) {
if(obj->styles[i].is_local &&
obj->styles[i].selector == selector) {
break;
}
}
/*The style is not found*/
if(i == obj->style_cnt) return false;
return lv_style_remove_prop(obj->styles[i].style, prop);
}
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_dsc)
{
trans_t * tr;
/*Get the previous and current values*/
obj->skip_trans = 1;
obj->state = prev_state;
lv_style_value_t v1 = lv_obj_get_style_prop(obj, part, tr_dsc->prop);
obj->state = new_state;
lv_style_value_t v2 = lv_obj_get_style_prop(obj, part, tr_dsc->prop);
obj->skip_trans = 0;
if(v1.ptr == v2.ptr && v1.num == v2.num && v1.color.full == v2.color.full) return;
obj->state = prev_state;
v1 = lv_obj_get_style_prop(obj, part, tr_dsc->prop);
obj->state = new_state;
_lv_obj_style_t * style_trans = get_trans_style(obj, part);
lv_style_set_prop(style_trans->style, tr_dsc->prop, v1); /*Be sure `trans_style` has a valid value*/
if(tr_dsc->prop == LV_STYLE_RADIUS) {
if(v1.num == LV_RADIUS_CIRCLE || v2.num == LV_RADIUS_CIRCLE) {
lv_coord_t whalf = lv_obj_get_width(obj) / 2;
lv_coord_t hhalf = lv_obj_get_width(obj) / 2;
if(v1.num == LV_RADIUS_CIRCLE) v1.num = LV_MIN(whalf + 1, hhalf + 1);
if(v2.num == LV_RADIUS_CIRCLE) v2.num = LV_MIN(whalf + 1, hhalf + 1);
}
}
tr = _lv_ll_ins_head(&LV_GC_ROOT(_lv_obj_style_trans_ll));
LV_ASSERT_MALLOC(tr);
if(tr == NULL) return;
tr->start_value = v1;
tr->end_value = v2;
tr->obj = obj;
tr->prop = tr_dsc->prop;
tr->selector = part;
lv_anim_t a;
lv_anim_init(&a);
lv_anim_set_var(&a, tr);
lv_anim_set_exec_cb(&a, trans_anim_cb);
lv_anim_set_start_cb(&a, trans_anim_start_cb);
lv_anim_set_ready_cb(&a, trans_anim_ready_cb);
lv_anim_set_values(&a, 0x00, 0xFF);
lv_anim_set_time(&a, tr_dsc->time);
lv_anim_set_delay(&a, tr_dsc->delay);
lv_anim_set_path_cb(&a, tr_dsc->path_cb);
lv_anim_set_early_apply(&a, false);
#if LV_USE_USER_DATA
a.user_data = tr_dsc->user_data;
#endif
lv_anim_start(&a);
}
lv_style_value_t _lv_obj_style_apply_color_filter(const lv_obj_t * obj, uint32_t part, lv_style_value_t v)
{
if(obj == NULL) return v;
const lv_color_filter_dsc_t * f = lv_obj_get_style_color_filter_dsc(obj, part);
if(f && f->filter_cb) {
lv_opa_t f_opa = lv_obj_get_style_color_filter_opa(obj, part);
if(f_opa != 0) v.color = f->filter_cb(f, v.color, f_opa);
}
return v;
}
_lv_style_state_cmp_t _lv_obj_style_state_compare(lv_obj_t * obj, lv_state_t state1, lv_state_t state2)
{
_lv_style_state_cmp_t res = _LV_STYLE_STATE_CMP_SAME;
/*Are there any new styles for the new state?*/
uint32_t i;
for(i = 0; i < obj->style_cnt; i++) {
if(obj->styles[i].is_trans) continue;
lv_state_t state_act = lv_obj_style_get_selector_state(obj->styles[i].selector);
/*The style is valid for a state but not the other*/
bool valid1 = state_act & (~state1) ? false : true;
bool valid2 = state_act & (~state2) ? false : true;
if(valid1 != valid2) {
lv_style_t * style = obj->styles[i].style;
lv_style_value_t v;
/*If there is layout difference on the main part, return immediately. There is no more serious difference*/
bool layout_diff = false;
if(lv_style_get_prop(style, LV_STYLE_PAD_TOP, &v))layout_diff = true;
else if(lv_style_get_prop(style, LV_STYLE_PAD_BOTTOM, &v)) layout_diff = true;
else if(lv_style_get_prop(style, LV_STYLE_PAD_LEFT, &v)) layout_diff = true;
else if(lv_style_get_prop(style, LV_STYLE_PAD_RIGHT, &v)) layout_diff = true;
else if(lv_style_get_prop(style, LV_STYLE_PAD_COLUMN, &v)) layout_diff = true;
else if(lv_style_get_prop(style, LV_STYLE_PAD_ROW, &v)) layout_diff = true;
else if(lv_style_get_prop(style, LV_STYLE_LAYOUT, &v)) layout_diff = true;
else if(lv_style_get_prop(style, LV_STYLE_TRANSLATE_X, &v)) layout_diff = true;
else if(lv_style_get_prop(style, LV_STYLE_TRANSLATE_Y, &v)) layout_diff = true;
else if(lv_style_get_prop(style, LV_STYLE_WIDTH, &v)) layout_diff = true;
else if(lv_style_get_prop(style, LV_STYLE_HEIGHT, &v)) layout_diff = true;
else if(lv_style_get_prop(style, LV_STYLE_MIN_WIDTH, &v)) layout_diff = true;
else if(lv_style_get_prop(style, LV_STYLE_MAX_WIDTH, &v)) layout_diff = true;
else if(lv_style_get_prop(style, LV_STYLE_MIN_HEIGHT, &v)) layout_diff = true;
else if(lv_style_get_prop(style, LV_STYLE_MAX_HEIGHT, &v)) layout_diff = true;
else if(lv_style_get_prop(style, LV_STYLE_BORDER_WIDTH, &v)) layout_diff = true;
else if(lv_style_get_prop(style, LV_STYLE_TRANSFORM_ANGLE, &v)) layout_diff = true;
else if(lv_style_get_prop(style, LV_STYLE_TRANSFORM_ZOOM, &v)) layout_diff = true;
if(layout_diff) {
return _LV_STYLE_STATE_CMP_DIFF_LAYOUT;
}
/*Check for draw pad changes*/
if(lv_style_get_prop(style, LV_STYLE_TRANSFORM_WIDTH, &v)) res = _LV_STYLE_STATE_CMP_DIFF_DRAW_PAD;
else if(lv_style_get_prop(style, LV_STYLE_TRANSFORM_HEIGHT, &v)) res = _LV_STYLE_STATE_CMP_DIFF_DRAW_PAD;
else if(lv_style_get_prop(style, LV_STYLE_TRANSFORM_ANGLE, &v)) res = _LV_STYLE_STATE_CMP_DIFF_DRAW_PAD;
else if(lv_style_get_prop(style, LV_STYLE_TRANSFORM_ZOOM, &v)) res = _LV_STYLE_STATE_CMP_DIFF_DRAW_PAD;
else if(lv_style_get_prop(style, LV_STYLE_OUTLINE_OPA, &v)) res = _LV_STYLE_STATE_CMP_DIFF_DRAW_PAD;
else if(lv_style_get_prop(style, LV_STYLE_OUTLINE_PAD, &v)) res = _LV_STYLE_STATE_CMP_DIFF_DRAW_PAD;
else if(lv_style_get_prop(style, LV_STYLE_OUTLINE_WIDTH, &v)) res = _LV_STYLE_STATE_CMP_DIFF_DRAW_PAD;
else if(lv_style_get_prop(style, LV_STYLE_SHADOW_WIDTH, &v)) res = _LV_STYLE_STATE_CMP_DIFF_DRAW_PAD;
else if(lv_style_get_prop(style, LV_STYLE_SHADOW_OPA, &v)) res = _LV_STYLE_STATE_CMP_DIFF_DRAW_PAD;
else if(lv_style_get_prop(style, LV_STYLE_SHADOW_OFS_X, &v)) res = _LV_STYLE_STATE_CMP_DIFF_DRAW_PAD;
else if(lv_style_get_prop(style, LV_STYLE_SHADOW_OFS_Y, &v)) res = _LV_STYLE_STATE_CMP_DIFF_DRAW_PAD;
else if(lv_style_get_prop(style, LV_STYLE_SHADOW_SPREAD, &v)) res = _LV_STYLE_STATE_CMP_DIFF_DRAW_PAD;
else if(lv_style_get_prop(style, LV_STYLE_LINE_WIDTH, &v)) res = _LV_STYLE_STATE_CMP_DIFF_DRAW_PAD;
else if(res == _LV_STYLE_STATE_CMP_SAME) res = _LV_STYLE_STATE_CMP_DIFF_REDRAW;
}
}
return res;
}
void lv_obj_fade_in(lv_obj_t * obj, uint32_t time, uint32_t delay)
{
lv_anim_t a;
lv_anim_init(&a);
lv_anim_set_var(&a, obj);
lv_anim_set_values(&a, 0, LV_OPA_COVER);
lv_anim_set_exec_cb(&a, fade_anim_cb);
lv_anim_set_ready_cb(&a, fade_in_anim_ready);
lv_anim_set_time(&a, time);
lv_anim_set_delay(&a, delay);
lv_anim_start(&a);
}
void lv_obj_fade_out(lv_obj_t * obj, uint32_t time, uint32_t delay)
{
lv_anim_t a;
lv_anim_init(&a);
lv_anim_set_var(&a, obj);
lv_anim_set_values(&a, lv_obj_get_style_opa(obj, 0), LV_OPA_TRANSP);
lv_anim_set_exec_cb(&a, fade_anim_cb);
lv_anim_set_time(&a, time);
lv_anim_set_delay(&a, delay);
lv_anim_start(&a);
}
lv_state_t lv_obj_style_get_selector_state(lv_style_selector_t selector)
{
return selector & 0xFFFF;
}
lv_part_t lv_obj_style_get_selector_part(lv_style_selector_t selector)
{
return selector & 0xFF0000;
}
lv_text_align_t lv_obj_calculate_style_text_align(const struct _lv_obj_t * obj, lv_part_t part, const char * txt)
{
lv_text_align_t align = lv_obj_get_style_text_align(obj, part);
lv_base_dir_t base_dir = lv_obj_get_style_base_dir(obj, part);
lv_bidi_calculate_align(&align, &base_dir, txt);
return align;
}
/**********************
* STATIC FUNCTIONS
**********************/
/**
* Get the local style of an object for a given part and for a given state.
* If the local style for the part-state pair doesn't exist allocate and return it.
* @param obj pointer to an object
* @param selector OR-ed value of parts and state for which the style should be get
* @return pointer to the local style
*/
static lv_style_t * get_local_style(lv_obj_t * obj, lv_style_selector_t selector)
{
uint32_t i;
for(i = 0; i < obj->style_cnt; i++) {
if(obj->styles[i].is_local &&
obj->styles[i].selector == selector) {
return obj->styles[i].style;
}
}
obj->style_cnt++;
obj->styles = lv_mem_realloc(obj->styles, obj->style_cnt * sizeof(_lv_obj_style_t));
LV_ASSERT_MALLOC(obj->styles);
for(i = obj->style_cnt - 1; i > 0 ; i--) {
/*Copy only normal styles (not local and transition).
*The new local style will be added as the last local style*/
if(obj->styles[i - 1].is_local || obj->styles[i - 1].is_trans) break;
obj->styles[i] = obj->styles[i - 1];
}
lv_memset_00(&obj->styles[i], sizeof(_lv_obj_style_t));
obj->styles[i].style = lv_mem_alloc(sizeof(lv_style_t));
lv_style_init(obj->styles[i].style);
obj->styles[i].is_local = 1;
obj->styles[i].selector = selector;
return obj->styles[i].style;
}
/**
* Get the transition style of an object for a given part and for a given state.
* If the transition style for the part-state pair doesn't exist allocate and return it.
* @param obj pointer to an object
* @param selector OR-ed value of parts and state for which the style should be get
* @return pointer to the transition style
*/
static _lv_obj_style_t * get_trans_style(lv_obj_t * obj, lv_style_selector_t selector)
{
uint32_t i;
for(i = 0; i < obj->style_cnt; i++) {
if(obj->styles[i].is_trans && obj->styles[i].selector == selector) break;
}
/*Already have a transition style for it*/
if(i != obj->style_cnt) return &obj->styles[i];
obj->style_cnt++;
obj->styles = lv_mem_realloc(obj->styles, obj->style_cnt * sizeof(_lv_obj_style_t));
for(i = obj->style_cnt - 1; i > 0 ; i--) {
obj->styles[i] = obj->styles[i - 1];
}
lv_memset_00(&obj->styles[0], sizeof(_lv_obj_style_t));
obj->styles[0].style = lv_mem_alloc(sizeof(lv_style_t));
lv_style_init(obj->styles[0].style);
obj->styles[0].is_trans = 1;
obj->styles[0].selector = selector;
return &obj->styles[0];
}
static bool get_prop_core(const lv_obj_t * obj, lv_part_t part, lv_style_prop_t prop, lv_style_value_t * v)
{
uint8_t group = 1 << _lv_style_get_prop_group(prop);
int32_t weight = -1;
lv_state_t state = obj->state;
lv_state_t state_inv = ~state;
lv_style_value_t value_tmp;
bool skip_trans = obj->skip_trans;
uint32_t i;
bool found;
for(i = 0; i < obj->style_cnt; i++) {
_lv_obj_style_t * obj_style = &obj->styles[i];
if(obj_style->is_trans == false) break;
if(skip_trans) continue;
lv_part_t part_act = lv_obj_style_get_selector_part(obj->styles[i].selector);
if(part_act != part) continue;
if((obj_style->style->has_group & group) == 0) continue;
found = lv_style_get_prop(obj_style->style, prop, &value_tmp);
if(found) {
*v = value_tmp;
return true;
}
}
for(; i < obj->style_cnt; i++) {
_lv_obj_style_t * obj_style = &obj->styles[i];
lv_part_t part_act = lv_obj_style_get_selector_part(obj->styles[i].selector);
lv_state_t state_act = lv_obj_style_get_selector_state(obj->styles[i].selector);
if(part_act != part) continue;
if((obj_style->style->has_group & group) == 0) continue;
/*Be sure the style not specifies other state than the requested.
*E.g. For HOVER+PRESS object state, HOVER style only is OK, but HOVER+FOCUS style is not*/
if((state_act & state_inv)) continue;
/*Check only better candidates*/
if(state_act <= weight) continue;
found = lv_style_get_prop(obj_style->style, prop, &value_tmp);
if(found) {
if(state_act == state) {
*v = value_tmp;
return true;
}
if(weight < state_act) {
weight = state_act;
*v = value_tmp;
}
}
}
if(weight >= 0) {
*v = value_tmp;
return true;
}
else return false;
}
/**
* Refresh the style of all children of an object. (Called recursively)
* @param style refresh objects only with this
* @param obj pointer to an object
*/
static void report_style_change_core(void * style, lv_obj_t * obj)
{
uint32_t i;
for(i = 0; i < obj->style_cnt; i++) {
if(style == NULL || obj->styles[i].style == style) {
lv_obj_refresh_style(obj, LV_PART_ANY, LV_STYLE_PROP_ANY);
break;
}
}
uint32_t child_cnt = lv_obj_get_child_cnt(obj);
for(i = 0; i < child_cnt; i++) {
report_style_change_core(style, obj->spec_attr->children[i]);
}
}
/**
* Recursively refresh the style of the children. Go deeper until a not NULL style is found
* because the NULL styles are inherited from the parent
* @param obj pointer to an object
*/
static void refresh_children_style(lv_obj_t * obj)
{
uint32_t i;
uint32_t child_cnt = lv_obj_get_child_cnt(obj);
for(i = 0; i < child_cnt; i++) {
lv_obj_t * child = obj->spec_attr->children[i];
lv_obj_invalidate(child);
lv_event_send(child, LV_EVENT_STYLE_CHANGED, NULL);
lv_obj_invalidate(child);
refresh_children_style(child); /*Check children too*/
}
}
/**
* Remove the transition from object's part's property.
* - Remove the transition from `_lv_obj_style_trans_ll` and free it
* - Delete pending transitions
* @param obj pointer to an object which transition(s) should be removed
* @param part a part of object or 0xFF to remove from all parts
* @param prop a property or 0xFF to remove all properties
* @param tr_limit delete transitions only "older" than this. `NULL` if not used
*/
static bool trans_del(lv_obj_t * obj, lv_part_t part, lv_style_prop_t prop, trans_t * tr_limit)
{
trans_t * tr;
trans_t * tr_prev;
bool removed = false;
tr = _lv_ll_get_tail(&LV_GC_ROOT(_lv_obj_style_trans_ll));
while(tr != NULL) {
if(tr == tr_limit) break;
/*'tr' might be deleted, so get the next object while 'tr' is valid*/
tr_prev = _lv_ll_get_prev(&LV_GC_ROOT(_lv_obj_style_trans_ll), tr);
if(tr->obj == obj && (part == tr->selector || part == LV_PART_ANY) && (prop == tr->prop || prop == LV_STYLE_PROP_ANY)) {
/*Remove any transitioned properties from the trans. style
*to allow changing it by normal styles*/
uint32_t i;
for(i = 0; i < obj->style_cnt; i++) {
if(obj->styles[i].is_trans && (part == LV_PART_ANY || obj->styles[i].selector == part)) {
lv_style_remove_prop(obj->styles[i].style, tr->prop);
}
}
/*Free the transition descriptor too*/
lv_anim_del(tr, NULL);
_lv_ll_remove(&LV_GC_ROOT(_lv_obj_style_trans_ll), tr);
lv_mem_free(tr);
removed = true;
}
tr = tr_prev;
}
return removed;
}
static void trans_anim_cb(void * _tr, int32_t v)
{
trans_t * tr = _tr;
lv_obj_t * obj = tr->obj;
uint32_t i;
for(i = 0; i < obj->style_cnt; i++) {
if(obj->styles[i].is_trans == 0 || obj->styles[i].selector != tr->selector) continue;
lv_style_value_t value_final;
switch(tr->prop) {
case LV_STYLE_BORDER_SIDE:
case LV_STYLE_BORDER_POST:
case LV_STYLE_BLEND_MODE:
if(v < 255) value_final.num = tr->start_value.num;
else value_final.num = tr->end_value.num;
break;
case LV_STYLE_TRANSITION:
case LV_STYLE_TEXT_FONT:
if(v < 255) value_final.ptr = tr->start_value.ptr;
else value_final.ptr = tr->end_value.ptr;
break;
case LV_STYLE_COLOR_FILTER_DSC:
if(tr->start_value.ptr == NULL) value_final.ptr = tr->end_value.ptr;
else if(tr->end_value.ptr == NULL) value_final.ptr = tr->start_value.ptr;
else if(v < 128) value_final.ptr = tr->start_value.ptr;
else value_final.ptr = tr->end_value.ptr;
break;
case LV_STYLE_BG_COLOR:
case LV_STYLE_BORDER_COLOR:
case LV_STYLE_TEXT_COLOR:
case LV_STYLE_SHADOW_COLOR:
case LV_STYLE_OUTLINE_COLOR:
case LV_STYLE_IMG_RECOLOR:
if(v <= 0) value_final.color = tr->start_value.color;
else if(v >= 255) value_final.color = tr->end_value.color;
else value_final.color = lv_color_mix(tr->end_value.color, tr->start_value.color, v);
break;
default:
if(v == 0) value_final.num = tr->start_value.num;
else if(v == 255) value_final.num = tr->end_value.num;
else value_final.num = tr->start_value.num + ((int32_t)((int32_t)(tr->end_value.num - tr->start_value.num) * v) >> 8);
break;
}
lv_style_value_t old_value;
bool refr = true;
if(lv_style_get_prop(obj->styles[i].style, tr->prop, &old_value)) {
if(value_final.ptr == old_value.ptr && value_final.color.full == old_value.color.full &&
value_final.num == old_value.num) {
refr = false;
}
}
lv_style_set_prop(obj->styles[i].style, tr->prop, value_final);
if(refr) lv_obj_refresh_style(tr->obj, tr->selector, tr->prop);
break;
}
}
static void trans_anim_start_cb(lv_anim_t * a)
{
trans_t * tr = a->var;
lv_part_t part = lv_obj_style_get_selector_part(tr->selector);
tr->start_value = lv_obj_get_style_prop(tr->obj, part, tr->prop);
/*Init prop to an invalid values to be sure `trans_del` won't delete this added `tr`*/
lv_style_prop_t prop_tmp = tr->prop;
tr->prop = LV_STYLE_PROP_INV;
/*Delete the related transitions if any*/
trans_del(tr->obj, part, prop_tmp, tr);
tr->prop = prop_tmp;
_lv_obj_style_t * style_trans = get_trans_style(tr->obj, tr->selector);
lv_style_set_prop(style_trans->style, tr->prop, tr->start_value); /*Be sure `trans_style` has a valid value*/
}
static void trans_anim_ready_cb(lv_anim_t * a)
{
trans_t * tr = a->var;
lv_obj_t * obj = tr->obj;
lv_style_prop_t prop = tr->prop;
/*Remove the transitioned property from trans. style
*if there no more transitions for this property
*It allows changing it by normal styles*/
bool running = false;
trans_t * tr_i;
_LV_LL_READ(&LV_GC_ROOT(_lv_obj_style_trans_ll), tr_i) {
if(tr_i != tr && tr_i->obj == tr->obj && tr_i->selector == tr->selector && tr_i->prop == tr->prop) {
running = true;
break;
}
}
if(!running) {
uint32_t i;
for(i = 0; i < obj->style_cnt; i++) {
if(obj->styles[i].is_trans && obj->styles[i].selector == tr->selector) {
_lv_ll_remove(&LV_GC_ROOT(_lv_obj_style_trans_ll), tr);
lv_mem_free(tr);
_lv_obj_style_t * obj_style = &obj->styles[i];
lv_style_remove_prop(obj_style->style, prop);
if(lv_style_is_empty(obj->styles[i].style)) {
lv_obj_remove_style(obj, obj_style->style, obj_style->selector);
}
break;
}
}
}
}
static void fade_anim_cb(void * obj, int32_t v)
{
lv_obj_set_style_opa(obj, v, 0);
}
static void fade_in_anim_ready(lv_anim_t * a)
{
lv_obj_remove_local_style_prop(a->var, LV_STYLE_OPA, 0);
}

View File

@@ -0,0 +1,245 @@
/**
* @file lv_obj_style.h
*
*/
#ifndef LV_OBJ_STYLE_H
#define LV_OBJ_STYLE_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include <stdint.h>
#include <stdbool.h>
#include "../misc/lv_bidi.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/*Can't include lv_obj.h because it includes this header file*/
struct _lv_obj_t;
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;
typedef struct {
lv_style_t * style;
uint32_t selector : 24;
uint32_t is_local : 1;
uint32_t is_trans : 1;
} _lv_obj_style_t;
typedef struct {
uint16_t time;
uint16_t delay;
lv_style_selector_t selector;
lv_style_prop_t prop;
lv_anim_path_cb_t path_cb;
#if LV_USE_USER_DATA
void * user_data;
#endif
} _lv_obj_style_transition_dsc_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Initialize the object related style manager module.
* Called by LVGL in `lv_init()`
*/
void _lv_obj_style_init(void);
/**
* 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
* @example lv_obj_add_style(btn, &style_btn, 0); //Default button style
* @example lv_obj_add_style(btn, &btn_red, LV_STATE_PRESSED); //Overwrite only some colors to red when pressed
*/
void lv_obj_add_style(struct _lv_obj_t * obj, lv_style_t * style, lv_style_selector_t selector);
/**
* Add a style to 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
* @example lv_obj_remove_style(obj, &style, LV_PART_ANY | LV_STATE_ANY); //Remove a specific style
* @example lv_obj_remove_style(obj, NULL, LV_PART_MAIN | LV_STATE_ANY); //Remove all styles from the main part
* @example lv_obj_remove_style(obj, NULL, LV_PART_ANY | LV_STATE_ANY); //Remove all styles
*/
void lv_obj_remove_style(struct _lv_obj_t * obj, lv_style_t * style, lv_style_selector_t selector);
/**
* Remove all styles from an object
* @param obj pointer to an object
*/
static inline void lv_obj_remove_style_all(struct _lv_obj_t * obj)
{
lv_obj_remove_style(obj, NULL, LV_PART_ANY | LV_STATE_ANY);
}
/**
* 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(struct _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 struct _lv_obj_t * obj, lv_part_t part, 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(struct _lv_obj_t * obj, lv_style_prop_t prop, lv_style_value_t value,
lv_style_selector_t selector);
lv_res_t lv_obj_get_local_style_prop(struct _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(struct _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 struct _lv_obj_t * obj, uint32_t part, lv_style_value_t v);
/**
* 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(struct _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(struct _lv_obj_t * obj, lv_state_t state1, lv_state_t state2);
/**
* 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(struct _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(struct _lv_obj_t * obj, uint32_t time, uint32_t delay);
lv_state_t lv_obj_style_get_selector_state(lv_style_selector_t selector);
lv_part_t lv_obj_style_get_selector_part(lv_style_selector_t selector);
#include "lv_obj_style_gen.h"
static inline void lv_obj_set_style_pad_all(struct _lv_obj_t * obj, lv_coord_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(struct _lv_obj_t * obj, lv_coord_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(struct _lv_obj_t * obj, lv_coord_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_pad_gap(struct _lv_obj_t * obj, lv_coord_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(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector)
{
lv_obj_set_style_width(obj, value, selector);
lv_obj_set_style_height(obj, value, selector);
}
lv_text_align_t lv_obj_calculate_style_text_align(const struct _lv_obj_t * obj, lv_part_t part, const char * txt);
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_OBJ_STYLE_H*/

View File

@@ -0,0 +1,657 @@
#include "lv_obj.h"
void lv_obj_set_style_width(struct _lv_obj_t * obj, lv_coord_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(struct _lv_obj_t * obj, lv_coord_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(struct _lv_obj_t * obj, lv_coord_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(struct _lv_obj_t * obj, lv_coord_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(struct _lv_obj_t * obj, lv_coord_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(struct _lv_obj_t * obj, lv_coord_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_x(struct _lv_obj_t * obj, lv_coord_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(struct _lv_obj_t * obj, lv_coord_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(struct _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(struct _lv_obj_t * obj, lv_coord_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(struct _lv_obj_t * obj, lv_coord_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(struct _lv_obj_t * obj, lv_coord_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(struct _lv_obj_t * obj, lv_coord_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_transform_zoom(struct _lv_obj_t * obj, lv_coord_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_ZOOM, v, selector);
}
void lv_obj_set_style_transform_angle(struct _lv_obj_t * obj, lv_coord_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_ANGLE, v, selector);
}
void lv_obj_set_style_pad_top(struct _lv_obj_t * obj, lv_coord_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(struct _lv_obj_t * obj, lv_coord_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(struct _lv_obj_t * obj, lv_coord_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(struct _lv_obj_t * obj, lv_coord_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(struct _lv_obj_t * obj, lv_coord_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(struct _lv_obj_t * obj, lv_coord_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_bg_color(struct _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(struct _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(struct _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(struct _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(struct _lv_obj_t * obj, lv_coord_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(struct _lv_obj_t * obj, lv_coord_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_grad(struct _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_dither_mode(struct _lv_obj_t * obj, lv_dither_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_BG_DITHER_MODE, v, selector);
}
void lv_obj_set_style_bg_img_src(struct _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_IMG_SRC, v, selector);
}
void lv_obj_set_style_bg_img_opa(struct _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_IMG_OPA, v, selector);
}
void lv_obj_set_style_bg_img_recolor(struct _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_IMG_RECOLOR, v, selector);
}
void lv_obj_set_style_bg_img_recolor_opa(struct _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_IMG_RECOLOR_OPA, v, selector);
}
void lv_obj_set_style_bg_img_tiled(struct _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_IMG_TILED, v, selector);
}
void lv_obj_set_style_border_color(struct _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(struct _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(struct _lv_obj_t * obj, lv_coord_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(struct _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(struct _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(struct _lv_obj_t * obj, lv_coord_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(struct _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(struct _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(struct _lv_obj_t * obj, lv_coord_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(struct _lv_obj_t * obj, lv_coord_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_ofs_x(struct _lv_obj_t * obj, lv_coord_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_OFS_X, v, selector);
}
void lv_obj_set_style_shadow_ofs_y(struct _lv_obj_t * obj, lv_coord_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_OFS_Y, v, selector);
}
void lv_obj_set_style_shadow_spread(struct _lv_obj_t * obj, lv_coord_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(struct _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(struct _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_img_opa(struct _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_IMG_OPA, v, selector);
}
void lv_obj_set_style_img_recolor(struct _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_IMG_RECOLOR, v, selector);
}
void lv_obj_set_style_img_recolor_opa(struct _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_IMG_RECOLOR_OPA, v, selector);
}
void lv_obj_set_style_line_width(struct _lv_obj_t * obj, lv_coord_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(struct _lv_obj_t * obj, lv_coord_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(struct _lv_obj_t * obj, lv_coord_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(struct _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(struct _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(struct _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(struct _lv_obj_t * obj, lv_coord_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(struct _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(struct _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(struct _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_img_src(struct _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_IMG_SRC, v, selector);
}
void lv_obj_set_style_text_color(struct _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(struct _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(struct _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(struct _lv_obj_t * obj, lv_coord_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(struct _lv_obj_t * obj, lv_coord_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(struct _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(struct _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_radius(struct _lv_obj_t * obj, lv_coord_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_clip_corner(struct _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(struct _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_color_filter_dsc(struct _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(struct _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_anim(struct _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_time(struct _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_TIME, v, selector);
}
void lv_obj_set_style_anim_speed(struct _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_SPEED, v, selector);
}
void lv_obj_set_style_transition(struct _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(struct _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(struct _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(struct _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);
}

View File

@@ -0,0 +1,634 @@
static inline lv_coord_t lv_obj_get_style_width(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_WIDTH);
return (lv_coord_t)v.num;
}
static inline lv_coord_t lv_obj_get_style_min_width(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_MIN_WIDTH);
return (lv_coord_t)v.num;
}
static inline lv_coord_t lv_obj_get_style_max_width(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_MAX_WIDTH);
return (lv_coord_t)v.num;
}
static inline lv_coord_t lv_obj_get_style_height(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_HEIGHT);
return (lv_coord_t)v.num;
}
static inline lv_coord_t lv_obj_get_style_min_height(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_MIN_HEIGHT);
return (lv_coord_t)v.num;
}
static inline lv_coord_t lv_obj_get_style_max_height(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_MAX_HEIGHT);
return (lv_coord_t)v.num;
}
static inline lv_coord_t lv_obj_get_style_x(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_X);
return (lv_coord_t)v.num;
}
static inline lv_coord_t lv_obj_get_style_y(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_Y);
return (lv_coord_t)v.num;
}
static inline lv_align_t lv_obj_get_style_align(const struct _lv_obj_t * obj, uint32_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 lv_coord_t lv_obj_get_style_transform_width(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_TRANSFORM_WIDTH);
return (lv_coord_t)v.num;
}
static inline lv_coord_t lv_obj_get_style_transform_height(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_TRANSFORM_HEIGHT);
return (lv_coord_t)v.num;
}
static inline lv_coord_t lv_obj_get_style_translate_x(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_TRANSLATE_X);
return (lv_coord_t)v.num;
}
static inline lv_coord_t lv_obj_get_style_translate_y(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_TRANSLATE_Y);
return (lv_coord_t)v.num;
}
static inline lv_coord_t lv_obj_get_style_transform_zoom(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_TRANSFORM_ZOOM);
return (lv_coord_t)v.num;
}
static inline lv_coord_t lv_obj_get_style_transform_angle(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_TRANSFORM_ANGLE);
return (lv_coord_t)v.num;
}
static inline lv_coord_t lv_obj_get_style_pad_top(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_PAD_TOP);
return (lv_coord_t)v.num;
}
static inline lv_coord_t lv_obj_get_style_pad_bottom(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_PAD_BOTTOM);
return (lv_coord_t)v.num;
}
static inline lv_coord_t lv_obj_get_style_pad_left(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_PAD_LEFT);
return (lv_coord_t)v.num;
}
static inline lv_coord_t lv_obj_get_style_pad_right(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_PAD_RIGHT);
return (lv_coord_t)v.num;
}
static inline lv_coord_t lv_obj_get_style_pad_row(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_PAD_ROW);
return (lv_coord_t)v.num;
}
static inline lv_coord_t lv_obj_get_style_pad_column(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_PAD_COLUMN);
return (lv_coord_t)v.num;
}
static inline lv_color_t lv_obj_get_style_bg_color(const struct _lv_obj_t * obj, uint32_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 struct _lv_obj_t * obj, uint32_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 struct _lv_obj_t * obj, uint32_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 struct _lv_obj_t * obj, uint32_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 struct _lv_obj_t * obj, uint32_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 struct _lv_obj_t * obj, uint32_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 lv_coord_t lv_obj_get_style_bg_main_stop(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_BG_MAIN_STOP);
return (lv_coord_t)v.num;
}
static inline lv_coord_t lv_obj_get_style_bg_grad_stop(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_BG_GRAD_STOP);
return (lv_coord_t)v.num;
}
static inline const lv_grad_dsc_t * lv_obj_get_style_bg_grad(const struct _lv_obj_t * obj, uint32_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 lv_dither_mode_t lv_obj_get_style_bg_dither_mode(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_BG_DITHER_MODE);
return (lv_dither_mode_t)v.num;
}
static inline const void * lv_obj_get_style_bg_img_src(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_BG_IMG_SRC);
return (const void *)v.ptr;
}
static inline lv_opa_t lv_obj_get_style_bg_img_opa(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_BG_IMG_OPA);
return (lv_opa_t)v.num;
}
static inline lv_color_t lv_obj_get_style_bg_img_recolor(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_BG_IMG_RECOLOR);
return v.color;
}
static inline lv_color_t lv_obj_get_style_bg_img_recolor_filtered(const struct _lv_obj_t * obj, uint32_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_IMG_RECOLOR));
return v.color;
}
static inline lv_opa_t lv_obj_get_style_bg_img_recolor_opa(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_BG_IMG_RECOLOR_OPA);
return (lv_opa_t)v.num;
}
static inline bool lv_obj_get_style_bg_img_tiled(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_BG_IMG_TILED);
return (bool)v.num;
}
static inline lv_color_t lv_obj_get_style_border_color(const struct _lv_obj_t * obj, uint32_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 struct _lv_obj_t * obj, uint32_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 struct _lv_obj_t * obj, uint32_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 lv_coord_t lv_obj_get_style_border_width(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_BORDER_WIDTH);
return (lv_coord_t)v.num;
}
static inline lv_border_side_t lv_obj_get_style_border_side(const struct _lv_obj_t * obj, uint32_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 struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_BORDER_POST);
return (bool)v.num;
}
static inline lv_coord_t lv_obj_get_style_outline_width(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_OUTLINE_WIDTH);
return (lv_coord_t)v.num;
}
static inline lv_color_t lv_obj_get_style_outline_color(const struct _lv_obj_t * obj, uint32_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 struct _lv_obj_t * obj, uint32_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 struct _lv_obj_t * obj, uint32_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 lv_coord_t lv_obj_get_style_outline_pad(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_OUTLINE_PAD);
return (lv_coord_t)v.num;
}
static inline lv_coord_t lv_obj_get_style_shadow_width(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_SHADOW_WIDTH);
return (lv_coord_t)v.num;
}
static inline lv_coord_t lv_obj_get_style_shadow_ofs_x(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_SHADOW_OFS_X);
return (lv_coord_t)v.num;
}
static inline lv_coord_t lv_obj_get_style_shadow_ofs_y(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_SHADOW_OFS_Y);
return (lv_coord_t)v.num;
}
static inline lv_coord_t lv_obj_get_style_shadow_spread(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_SHADOW_SPREAD);
return (lv_coord_t)v.num;
}
static inline lv_color_t lv_obj_get_style_shadow_color(const struct _lv_obj_t * obj, uint32_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 struct _lv_obj_t * obj, uint32_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 struct _lv_obj_t * obj, uint32_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_img_opa(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_IMG_OPA);
return (lv_opa_t)v.num;
}
static inline lv_color_t lv_obj_get_style_img_recolor(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_IMG_RECOLOR);
return v.color;
}
static inline lv_color_t lv_obj_get_style_img_recolor_filtered(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = _lv_obj_style_apply_color_filter(obj, part, lv_obj_get_style_prop(obj, part, LV_STYLE_IMG_RECOLOR));
return v.color;
}
static inline lv_opa_t lv_obj_get_style_img_recolor_opa(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_IMG_RECOLOR_OPA);
return (lv_opa_t)v.num;
}
static inline lv_coord_t lv_obj_get_style_line_width(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_LINE_WIDTH);
return (lv_coord_t)v.num;
}
static inline lv_coord_t lv_obj_get_style_line_dash_width(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_LINE_DASH_WIDTH);
return (lv_coord_t)v.num;
}
static inline lv_coord_t lv_obj_get_style_line_dash_gap(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_LINE_DASH_GAP);
return (lv_coord_t)v.num;
}
static inline bool lv_obj_get_style_line_rounded(const struct _lv_obj_t * obj, uint32_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 struct _lv_obj_t * obj, uint32_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 struct _lv_obj_t * obj, uint32_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 struct _lv_obj_t * obj, uint32_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 lv_coord_t lv_obj_get_style_arc_width(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_ARC_WIDTH);
return (lv_coord_t)v.num;
}
static inline bool lv_obj_get_style_arc_rounded(const struct _lv_obj_t * obj, uint32_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 struct _lv_obj_t * obj, uint32_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 struct _lv_obj_t * obj, uint32_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 struct _lv_obj_t * obj, uint32_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_img_src(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_ARC_IMG_SRC);
return (const void *)v.ptr;
}
static inline lv_color_t lv_obj_get_style_text_color(const struct _lv_obj_t * obj, uint32_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 struct _lv_obj_t * obj, uint32_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 struct _lv_obj_t * obj, uint32_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 struct _lv_obj_t * obj, uint32_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 lv_coord_t lv_obj_get_style_text_letter_space(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_TEXT_LETTER_SPACE);
return (lv_coord_t)v.num;
}
static inline lv_coord_t lv_obj_get_style_text_line_space(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_TEXT_LINE_SPACE);
return (lv_coord_t)v.num;
}
static inline lv_text_decor_t lv_obj_get_style_text_decor(const struct _lv_obj_t * obj, uint32_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 struct _lv_obj_t * obj, uint32_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_coord_t lv_obj_get_style_radius(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_RADIUS);
return (lv_coord_t)v.num;
}
static inline bool lv_obj_get_style_clip_corner(const struct _lv_obj_t * obj, uint32_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 struct _lv_obj_t * obj, uint32_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 const lv_color_filter_dsc_t * lv_obj_get_style_color_filter_dsc(const struct _lv_obj_t * obj, uint32_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 struct _lv_obj_t * obj, uint32_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 const lv_anim_t * lv_obj_get_style_anim(const struct _lv_obj_t * obj, uint32_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_time(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_ANIM_TIME);
return (uint32_t)v.num;
}
static inline uint32_t lv_obj_get_style_anim_speed(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_ANIM_SPEED);
return (uint32_t)v.num;
}
static inline const lv_style_transition_dsc_t * lv_obj_get_style_transition(const struct _lv_obj_t * obj, uint32_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 struct _lv_obj_t * obj, uint32_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 struct _lv_obj_t * obj, uint32_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 struct _lv_obj_t * obj, uint32_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;
}
void lv_obj_set_style_width(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector);
void lv_obj_set_style_min_width(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector);
void lv_obj_set_style_max_width(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector);
void lv_obj_set_style_height(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector);
void lv_obj_set_style_min_height(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector);
void lv_obj_set_style_max_height(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector);
void lv_obj_set_style_x(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector);
void lv_obj_set_style_y(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector);
void lv_obj_set_style_align(struct _lv_obj_t * obj, lv_align_t value, lv_style_selector_t selector);
void lv_obj_set_style_transform_width(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector);
void lv_obj_set_style_transform_height(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector);
void lv_obj_set_style_translate_x(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector);
void lv_obj_set_style_translate_y(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector);
void lv_obj_set_style_transform_zoom(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector);
void lv_obj_set_style_transform_angle(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector);
void lv_obj_set_style_pad_top(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector);
void lv_obj_set_style_pad_bottom(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector);
void lv_obj_set_style_pad_left(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector);
void lv_obj_set_style_pad_right(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector);
void lv_obj_set_style_pad_row(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector);
void lv_obj_set_style_pad_column(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector);
void lv_obj_set_style_bg_color(struct _lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector);
void lv_obj_set_style_bg_opa(struct _lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector);
void lv_obj_set_style_bg_grad_color(struct _lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector);
void lv_obj_set_style_bg_grad_dir(struct _lv_obj_t * obj, lv_grad_dir_t value, lv_style_selector_t selector);
void lv_obj_set_style_bg_main_stop(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector);
void lv_obj_set_style_bg_grad_stop(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector);
void lv_obj_set_style_bg_grad(struct _lv_obj_t * obj, const lv_grad_dsc_t * value, lv_style_selector_t selector);
void lv_obj_set_style_bg_dither_mode(struct _lv_obj_t * obj, lv_dither_mode_t value, lv_style_selector_t selector);
void lv_obj_set_style_bg_img_src(struct _lv_obj_t * obj, const void * value, lv_style_selector_t selector);
void lv_obj_set_style_bg_img_opa(struct _lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector);
void lv_obj_set_style_bg_img_recolor(struct _lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector);
void lv_obj_set_style_bg_img_recolor_opa(struct _lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector);
void lv_obj_set_style_bg_img_tiled(struct _lv_obj_t * obj, bool value, lv_style_selector_t selector);
void lv_obj_set_style_border_color(struct _lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector);
void lv_obj_set_style_border_opa(struct _lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector);
void lv_obj_set_style_border_width(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector);
void lv_obj_set_style_border_side(struct _lv_obj_t * obj, lv_border_side_t value, lv_style_selector_t selector);
void lv_obj_set_style_border_post(struct _lv_obj_t * obj, bool value, lv_style_selector_t selector);
void lv_obj_set_style_outline_width(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector);
void lv_obj_set_style_outline_color(struct _lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector);
void lv_obj_set_style_outline_opa(struct _lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector);
void lv_obj_set_style_outline_pad(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector);
void lv_obj_set_style_shadow_width(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector);
void lv_obj_set_style_shadow_ofs_x(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector);
void lv_obj_set_style_shadow_ofs_y(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector);
void lv_obj_set_style_shadow_spread(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector);
void lv_obj_set_style_shadow_color(struct _lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector);
void lv_obj_set_style_shadow_opa(struct _lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector);
void lv_obj_set_style_img_opa(struct _lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector);
void lv_obj_set_style_img_recolor(struct _lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector);
void lv_obj_set_style_img_recolor_opa(struct _lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector);
void lv_obj_set_style_line_width(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector);
void lv_obj_set_style_line_dash_width(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector);
void lv_obj_set_style_line_dash_gap(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector);
void lv_obj_set_style_line_rounded(struct _lv_obj_t * obj, bool value, lv_style_selector_t selector);
void lv_obj_set_style_line_color(struct _lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector);
void lv_obj_set_style_line_opa(struct _lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector);
void lv_obj_set_style_arc_width(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector);
void lv_obj_set_style_arc_rounded(struct _lv_obj_t * obj, bool value, lv_style_selector_t selector);
void lv_obj_set_style_arc_color(struct _lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector);
void lv_obj_set_style_arc_opa(struct _lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector);
void lv_obj_set_style_arc_img_src(struct _lv_obj_t * obj, const void * value, lv_style_selector_t selector);
void lv_obj_set_style_text_color(struct _lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector);
void lv_obj_set_style_text_opa(struct _lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector);
void lv_obj_set_style_text_font(struct _lv_obj_t * obj, const lv_font_t * value, lv_style_selector_t selector);
void lv_obj_set_style_text_letter_space(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector);
void lv_obj_set_style_text_line_space(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector);
void lv_obj_set_style_text_decor(struct _lv_obj_t * obj, lv_text_decor_t value, lv_style_selector_t selector);
void lv_obj_set_style_text_align(struct _lv_obj_t * obj, lv_text_align_t value, lv_style_selector_t selector);
void lv_obj_set_style_radius(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector);
void lv_obj_set_style_clip_corner(struct _lv_obj_t * obj, bool value, lv_style_selector_t selector);
void lv_obj_set_style_opa(struct _lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector);
void lv_obj_set_style_color_filter_dsc(struct _lv_obj_t * obj, const lv_color_filter_dsc_t * value, lv_style_selector_t selector);
void lv_obj_set_style_color_filter_opa(struct _lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector);
void lv_obj_set_style_anim(struct _lv_obj_t * obj, const lv_anim_t * value, lv_style_selector_t selector);
void lv_obj_set_style_anim_time(struct _lv_obj_t * obj, uint32_t value, lv_style_selector_t selector);
void lv_obj_set_style_anim_speed(struct _lv_obj_t * obj, uint32_t value, lv_style_selector_t selector);
void lv_obj_set_style_transition(struct _lv_obj_t * obj, const lv_style_transition_dsc_t * value, lv_style_selector_t selector);
void lv_obj_set_style_blend_mode(struct _lv_obj_t * obj, lv_blend_mode_t value, lv_style_selector_t selector);
void lv_obj_set_style_layout(struct _lv_obj_t * obj, uint16_t value, lv_style_selector_t selector);
void lv_obj_set_style_base_dir(struct _lv_obj_t * obj, lv_base_dir_t value, lv_style_selector_t selector);

View File

@@ -0,0 +1,452 @@
/**
* @file lv_obj_tree.c
*
*/
/*********************
* INCLUDES
*********************/
#include <stdlib.h>
#include "lv_obj.h"
#include "lv_indev.h"
#include "../misc/lv_anim.h"
#include "../misc/lv_gc.h"
#include "../misc/lv_async.h"
/*********************
* DEFINES
*********************/
#define MY_CLASS &lv_obj_class
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static void lv_obj_del_async_cb(void * obj);
static void obj_del_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 VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
void lv_obj_del(lv_obj_t * obj)
{
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);
if(par) {
lv_obj_scrollbar_invalidate(par);
}
lv_disp_t * disp = NULL;
bool act_scr_del = false;
if(par == NULL) {
disp = lv_obj_get_disp(obj);
if(!disp) return; /*Shouldn't happen*/
if(disp->act_scr == obj) act_scr_del = true;
}
obj_del_core(obj);
/*Call the ancestor's event handler to the parent to notify it about the child delete*/
if(par) {
lv_obj_update_layout(par);
lv_obj_readjust_scroll(par, LV_ANIM_OFF);
lv_obj_scrollbar_invalidate(par);
lv_event_send(par, LV_EVENT_CHILD_CHANGED, NULL);
lv_event_send(par, LV_EVENT_CHILD_DELETED, NULL);
}
/*Handle if the active screen was deleted*/
if(act_scr_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 (delete %p)", (void *)obj);
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_obj_invalidate(obj);
lv_obj_t * child = lv_obj_get_child(obj, 0);
while(child) {
obj_del_core(child);
child = lv_obj_get_child(obj, 0);
}
/*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;
}
LV_ASSERT_MEM_INTEGRITY();
LV_LOG_TRACE("finished (delete %p)", (void *)obj);
}
void lv_obj_del_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_time(&a, 1);
lv_anim_set_delay(&a, delay_ms);
lv_anim_set_ready_cb(&a, lv_obj_del_anim_ready_cb);
lv_anim_start(&a);
}
void lv_obj_del_anim_ready_cb(lv_anim_t * a)
{
lv_obj_del(a->var);
}
void lv_obj_del_async(lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_async_call(lv_obj_del_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;
}
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_cnt(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_mem_realloc(old_parent->spec_attr->children,
old_parent->spec_attr->child_cnt * (sizeof(lv_obj_t *)));
}
else {
lv_mem_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_mem_realloc(parent->spec_attr->children,
parent->spec_attr->child_cnt * (sizeof(lv_obj_t *)));
parent->spec_attr->children[lv_obj_get_child_cnt(parent) - 1] = obj;
obj->parent = parent;
/*Notify the original parent because one of its children is lost*/
lv_obj_readjust_scroll(old_parent, LV_ANIM_OFF);
lv_obj_scrollbar_invalidate(old_parent);
lv_event_send(old_parent, LV_EVENT_CHILD_CHANGED, obj);
lv_event_send(old_parent, LV_EVENT_CHILD_DELETED, NULL);
/*Notify the new parent about the child*/
lv_event_send(parent, LV_EVENT_CHILD_CHANGED, obj);
lv_event_send(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);
if(index < 0) {
index = lv_obj_get_child_cnt(lv_obj_get_parent(obj)) + index;
}
const int32_t old_index = lv_obj_get_index(obj);
lv_obj_t * parent = lv_obj_get_parent(obj);
if(index < 0) return;
if(index >= (int32_t) lv_obj_get_child_cnt(parent)) return;
if(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_event_send(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_event_send(parent2, LV_EVENT_CHILD_DELETED, obj2);
lv_event_send(parent, LV_EVENT_CHILD_DELETED, obj1);
parent->spec_attr->children[index1] = obj2;
parent2->spec_attr->children[index2] = obj1;
lv_event_send(parent, LV_EVENT_CHILD_CHANGED, obj2);
lv_event_send(parent, LV_EVENT_CHILD_CREATED, obj2);
lv_event_send(parent2, LV_EVENT_CHILD_CHANGED, obj1);
lv_event_send(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_disp_t * lv_obj_get_disp(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_disp_t * d;
_LV_LL_READ(&LV_GC_ROOT(_lv_disp_ll), 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 id)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
if(obj->spec_attr == NULL) return NULL;
uint32_t idu;
if(id < 0) {
id = obj->spec_attr->child_cnt + id;
if(id < 0) return NULL;
idu = (uint32_t) id;
}
else {
idu = id;
}
if(idu >= obj->spec_attr->child_cnt) return NULL;
else return obj->spec_attr->children[id];
}
uint32_t lv_obj_get_child_cnt(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_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 0;
uint32_t i = 0;
for(i = 0; i < lv_obj_get_child_cnt(parent); i++) {
if(lv_obj_get_child(parent, i) == obj) return i;
}
return 0xFFFFFFFF; /*Shouldn't happen*/
}
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);
}
/**********************
* STATIC FUNCTIONS
**********************/
static void lv_obj_del_async_cb(void * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_obj_del(obj);
}
static void obj_del_core(lv_obj_t * obj)
{
/*Let the user free the resources used in `LV_EVENT_DELETE`*/
lv_res_t res = lv_event_send(obj, LV_EVENT_DELETE, NULL);
if(res == LV_RES_INV) return;
/*Recursively delete the children*/
lv_obj_t * child = lv_obj_get_child(obj, 0);
while(child) {
obj_del_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) {
if(indev->proc.types.pointer.act_obj == obj || indev->proc.types.pointer.last_obj == obj) {
lv_indev_reset(indev, obj);
}
if(indev->proc.types.pointer.last_pressed == obj) {
indev->proc.types.pointer.last_pressed = NULL;
}
if(indev->group == group && obj == lv_indev_get_obj_act()) {
lv_indev_reset(indev, obj);
}
indev = lv_indev_get_next(indev);
}
/*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_disp_t * disp = lv_obj_get_disp(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_mem_realloc(disp->screens, disp->screen_cnt * sizeof(lv_obj_t *));
}
/*Remove the object from the child list of its parent*/
else {
uint32_t id = lv_obj_get_index(obj);
uint32_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_mem_realloc(obj->parent->spec_attr->children,
obj->parent->spec_attr->child_cnt * sizeof(lv_obj_t *));
}
/*Free the object itself*/
lv_mem_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_disp_t * disp = lv_disp_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_disp_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_cnt(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;
}

View File

@@ -0,0 +1,172 @@
/**
* @file struct _lv_obj_tree.h
*
*/
#ifndef LV_OBJ_TREE_H
#define LV_OBJ_TREE_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include <stddef.h>
#include <stdbool.h>
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
struct _lv_obj_t;
struct _lv_obj_class_t;
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)(struct _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_del(struct _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(struct _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_del_delayed(struct _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_del_anim_ready_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_del_async(struct _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(struct _lv_obj_t * obj, struct _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(struct _lv_obj_t * obj1, struct _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(struct _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
*/
struct _lv_obj_t * lv_obj_get_screen(const struct _lv_obj_t * obj);
/**
* Get the display of the object
* @param obj pointer to an object
* @return pointer to the object's display
*/
lv_disp_t * lv_obj_get_disp(const struct _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)
*/
struct _lv_obj_t * lv_obj_get_parent(const struct _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 id 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
*/
struct _lv_obj_t * lv_obj_get_child(const struct _lv_obj_t * obj, int32_t id);
/**
* Get the number of children
* @param obj pointer to an object
* @return the number of children
*/
uint32_t lv_obj_get_child_cnt(const struct _lv_obj_t * obj);
/**
* 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)
*/
uint32_t lv_obj_get_index(const struct _lv_obj_t * obj);
/**
* 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(struct _lv_obj_t * start_obj, lv_obj_tree_walk_cb_t cb, void * user_data);
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_OBJ_TREE_H*/

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,115 @@
/**
* @file lv_refr.h
*
*/
#ifndef LV_REFR_H
#define LV_REFR_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "lv_obj.h"
#include <stdbool.h>
/*********************
* DEFINES
*********************/
#define LV_REFR_TASK_PRIO LV_TASK_PRIO_MID
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
/**
* Initialize the screen refresh subsystem
*/
void _lv_refr_init(void);
/**
* 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_disp_t * disp);
/**
* Redrawn on object an all its children using the passed draw context
* @param draw pointer to an initialized draw context
* @param obj the start object from the redraw should start
*/
void lv_refr_obj(lv_draw_ctx_t * draw_ctx, lv_obj_t * obj);
/**
* 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_disp_t * disp, const lv_area_t * area_p);
/**
* Get the display which is being refreshed
* @return the display being refreshed
*/
lv_disp_t * _lv_refr_get_disp_refreshing(void);
/**
* Set the display which is being refreshed.
* It shouldn't be used directly by the user.
* It can be used to trick the drawing functions about there is an active display.
* @param the display being refreshed
*/
void _lv_refr_set_disp_refreshing(lv_disp_t * disp);
#if LV_USE_PERF_MONITOR
/**
* Reset FPS counter
*/
void lv_refr_reset_fps_counter(void);
/**
* Get the average FPS
* @return the average FPS
*/
uint32_t lv_refr_get_fps_avg(void);
#endif
/**
* Called periodically to handle the refreshing
* @param timer pointer to the timer itself
*/
void _lv_disp_refr_timer(lv_timer_t * timer);
/**********************
* STATIC FUNCTIONS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_REFR_H*/

View File

@@ -0,0 +1,118 @@
/**
* @file lv_theme.c
*
*/
/*********************
* INCLUDES
*********************/
#include "../../lvgl.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static void apply_theme(lv_theme_t * th, lv_obj_t * obj);
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
lv_theme_t * lv_theme_get_from_obj(lv_obj_t * obj)
{
lv_disp_t * disp = obj ? lv_obj_get_disp(obj) : lv_disp_get_default();
return lv_disp_get_theme(disp);
}
/**
* Apply the active theme on an object
* @param obj pointer to an object
* @param name the name of the theme element to apply. E.g. `LV_THEME_BTN`
*/
void lv_theme_apply(lv_obj_t * obj)
{
lv_theme_t * th = lv_theme_get_from_obj(obj);
if(th == NULL) return;
lv_obj_remove_style_all(obj);
apply_theme(th, obj); /*Apply the theme including the base theme(s)*/
}
/**
* Set a base theme for a theme.
* The styles from the base them will be added before the styles of the current theme.
* Arbitrary long chain of themes can be created by setting base themes.
* @param new_theme pointer to theme which base should be set
* @param base pointer to the base theme
*/
void lv_theme_set_parent(lv_theme_t * new_theme, lv_theme_t * base)
{
new_theme->parent = base;
}
/**
* Set a callback for a theme.
* The callback is used to add styles to different objects
* @param theme pointer to theme which callback should be set
* @param cb pointer to the callback
*/
void lv_theme_set_apply_cb(lv_theme_t * theme, lv_theme_apply_cb_t apply_cb)
{
theme->apply_cb = apply_cb;
}
const lv_font_t * lv_theme_get_font_small(lv_obj_t * obj)
{
lv_theme_t * th = lv_theme_get_from_obj(obj);
return th ? th->font_small : LV_FONT_DEFAULT;
}
const lv_font_t * lv_theme_get_font_normal(lv_obj_t * obj)
{
lv_theme_t * th = lv_theme_get_from_obj(obj);
return th ? th->font_normal : LV_FONT_DEFAULT;
}
const lv_font_t * lv_theme_get_font_large(lv_obj_t * obj)
{
lv_theme_t * th = lv_theme_get_from_obj(obj);
return th ? th->font_large : LV_FONT_DEFAULT;
}
lv_color_t lv_theme_get_color_primary(lv_obj_t * obj)
{
lv_theme_t * th = lv_theme_get_from_obj(obj);
return th ? th->color_primary : lv_palette_main(LV_PALETTE_BLUE_GREY);
}
lv_color_t lv_theme_get_color_secondary(lv_obj_t * obj)
{
lv_theme_t * th = lv_theme_get_from_obj(obj);
return th ? th->color_secondary : lv_palette_main(LV_PALETTE_BLUE);
}
/**********************
* STATIC FUNCTIONS
**********************/
static void apply_theme(lv_theme_t * th, lv_obj_t * obj)
{
if(th->parent) apply_theme(th->parent, obj);
if(th->apply_cb) th->apply_cb(th, obj);
}

View File

@@ -0,0 +1,120 @@
/**
*@file lv_theme.h
*
*/
#ifndef LV_THEME_H
#define LV_THEME_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../core/lv_obj.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
struct _lv_theme_t;
struct _lv_disp_t;
typedef void (*lv_theme_apply_cb_t)(struct _lv_theme_t *, lv_obj_t *);
typedef struct _lv_theme_t {
lv_theme_apply_cb_t apply_cb;
struct _lv_theme_t * parent; /**< Apply the current theme's style on top of this theme.*/
void * user_data;
struct _lv_disp_t * disp;
lv_color_t color_primary;
lv_color_t color_secondary;
const lv_font_t * font_small;
const lv_font_t * font_normal;
const lv_font_t * font_large;
uint32_t flags; /*Any custom flag used by the theme*/
} lv_theme_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Get the theme assigned to the display of the object
* @param obj pointer to a theme object
* @return the theme of the object's display (can be NULL)
*/
lv_theme_t * lv_theme_get_from_obj(lv_obj_t * obj);
/**
* Apply the active theme on an object
* @param obj pointer to an object
*/
void lv_theme_apply(lv_obj_t * obj);
/**
* Set a base theme for a theme.
* The styles from the base them will be added before the styles of the current theme.
* Arbitrary long chain of themes can be created by setting base themes.
* @param new_theme pointer to theme which base should be set
* @param parent pointer to the base theme
*/
void lv_theme_set_parent(lv_theme_t * new_theme, lv_theme_t * parent);
/**
* Set an apply callback for a theme.
* The apply callback is used to add styles to different objects
* @param theme pointer to theme which callback should be set
* @param apply_cb pointer to the callback
*/
void lv_theme_set_apply_cb(lv_theme_t * theme, lv_theme_apply_cb_t apply_cb);
/**
* Get the small font of the theme
* @param obj pointer to an object
* @return pointer to the font
*/
const lv_font_t * lv_theme_get_font_small(lv_obj_t * obj);
/**
* Get the normal font of the theme
* @param obj pointer to an object
* @return pointer to the font
*/
const lv_font_t * lv_theme_get_font_normal(lv_obj_t * obj);
/**
* Get the subtitle font of the theme
* @param obj pointer to an object
* @return pointer to the font
*/
const lv_font_t * lv_theme_get_font_large(lv_obj_t * obj);
/**
* Get the primary color of the theme
* @param obj pointer to an object
* @return the color
*/
lv_color_t lv_theme_get_color_primary(lv_obj_t * obj);
/**
* Get the secondary color of the theme
* @param obj pointer to an object
* @return the color
*/
lv_color_t lv_theme_get_color_secondary(lv_obj_t * obj);
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_THEME_H*/

View File

@@ -0,0 +1,6 @@
CSRCS += lv_gpu_arm2d.c
DEPPATH += --dep-path $(LVGL_DIR)/$(LVGL_DIR_NAME)/src/draw/arm2d
VPATH += :$(LVGL_DIR)/$(LVGL_DIR_NAME)/src/draw/arm2d
CFLAGS += "-I$(LVGL_DIR)/$(LVGL_DIR_NAME)/src/draw/arm2d"

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,51 @@
/**
* @file lv_gpu_arm2d.h
*
*/
#ifndef LV_GPU_ARM2D_H
#define LV_GPU_ARM2D_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../../misc/lv_color.h"
#include "../../hal/lv_hal_disp.h"
#include "../sw/lv_draw_sw.h"
#if LV_USE_GPU_ARM2D
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
typedef lv_draw_sw_ctx_t lv_draw_arm2d_ctx_t;
struct _lv_disp_drv_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
void lv_draw_arm2d_ctx_init(struct _lv_disp_drv_t * drv, lv_draw_ctx_t * draw_ctx);
void lv_draw_arm2d_ctx_deinit(struct _lv_disp_drv_t * drv, lv_draw_ctx_t * draw_ctx);
/**********************
* MACROS
**********************/
#endif /*LV_USE_GPU_ARM2D*/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_GPU_ARM2D_H*/

View File

@@ -0,0 +1,48 @@
/**
* @file lv_draw.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_draw.h"
#include "sw/lv_draw_sw.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
/**********************
* STATIC VARIABLES
**********************/
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
void lv_draw_init(void)
{
/*Nothing to init now*/
}
/**********************
* STATIC FUNCTIONS
**********************/

View File

@@ -0,0 +1,138 @@
/**
* @file lv_draw.h
*
*/
#ifndef LV_DRAW_H
#define LV_DRAW_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../lv_conf_internal.h"
#include "../misc/lv_style.h"
#include "../misc/lv_txt.h"
#include "lv_img_decoder.h"
#include "lv_img_cache.h"
#include "lv_draw_rect.h"
#include "lv_draw_label.h"
#include "lv_draw_img.h"
#include "lv_draw_line.h"
#include "lv_draw_triangle.h"
#include "lv_draw_arc.h"
#include "lv_draw_mask.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
typedef struct {
void * user_data;
} lv_draw_mask_t;
typedef struct _lv_draw_ctx_t {
/**
* Pointer to a buffer to draw into
*/
void * buf;
/**
* The position and size of `buf` (absolute coordinates)
*/
lv_area_t * buf_area;
/**
* The current clip area with absolute coordinates, always the same or smaller than `buf_area`
*/
const lv_area_t * clip_area;
void (*draw_rect)(struct _lv_draw_ctx_t * draw_ctx, const lv_draw_rect_dsc_t * dsc, const lv_area_t * coords);
void (*draw_arc)(struct _lv_draw_ctx_t * draw_ctx, const lv_draw_arc_dsc_t * dsc, const lv_point_t * center,
uint16_t radius, uint16_t start_angle, uint16_t end_angle);
void (*draw_img_decoded)(struct _lv_draw_ctx_t * draw_ctx, const lv_draw_img_dsc_t * dsc,
const lv_area_t * coords, const uint8_t * map_p, lv_img_cf_t color_format);
lv_res_t (*draw_img)(struct _lv_draw_ctx_t * draw_ctx, const lv_draw_img_dsc_t * draw_dsc,
const lv_area_t * coords, const void * src);
void (*draw_letter)(struct _lv_draw_ctx_t * draw_ctx, const lv_draw_label_dsc_t * dsc, const lv_point_t * pos_p,
uint32_t letter);
void (*draw_line)(struct _lv_draw_ctx_t * draw_ctx, const lv_draw_line_dsc_t * dsc, const lv_point_t * point1,
const lv_point_t * point2);
void (*draw_polygon)(struct _lv_draw_ctx_t * draw_ctx, const lv_draw_rect_dsc_t * draw_dsc,
const lv_point_t * points, uint16_t point_cnt);
/**
* Replace the buffer with a rect without decoration like radius or borders
*/
void (*draw_bg)(struct _lv_draw_ctx_t * draw_ctx, const lv_draw_rect_dsc_t * draw_dsc, const lv_area_t * coords);
/**
* Wait until all background operations are finished. (E.g. GPU operations)
*/
void (*wait_for_finish)(struct _lv_draw_ctx_t * draw_ctx);
/**
* Copy an area from buffer to an other
* @param draw_ctx pointer to a draw context
* @param dest_buf copy the buffer into this buffer
* @param dest_stride the width of the dest_buf in pixels
* @param dest_area the destination area
* @param src_buf copy from this buffer
* @param src_stride the width of src_buf in pixels
* @param src_area the source area.
*
* @note dest_area and src_area must have the same width and height
* but can have different x and y position.
* @note dest_area and src_area must be clipped to the real dimensions of the buffers
*/
void (*buffer_copy)(struct _lv_draw_ctx_t * draw_ctx, void * dest_buf, lv_coord_t dest_stride,
const lv_area_t * dest_area,
void * src_buf, lv_coord_t src_stride, const lv_area_t * src_area);
#if LV_USE_USER_DATA
void * user_data;
#endif
} lv_draw_ctx_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
void lv_draw_init(void);
/**********************
* GLOBAL VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* POST INCLUDES
*********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_DRAW_H*/

View File

@@ -0,0 +1,23 @@
CSRCS += lv_draw_arc.c
CSRCS += lv_draw.c
CSRCS += lv_draw_img.c
CSRCS += lv_draw_label.c
CSRCS += lv_draw_line.c
CSRCS += lv_draw_mask.c
CSRCS += lv_draw_rect.c
CSRCS += lv_draw_triangle.c
CSRCS += lv_img_buf.c
CSRCS += lv_img_cache.c
CSRCS += lv_img_decoder.c
DEPPATH += --dep-path $(LVGL_DIR)/$(LVGL_DIR_NAME)/src/draw
VPATH += :$(LVGL_DIR)/$(LVGL_DIR_NAME)/src/draw
CFLAGS += "-I$(LVGL_DIR)/$(LVGL_DIR_NAME)/src/draw"
include $(LVGL_DIR)/$(LVGL_DIR_NAME)/src/draw/arm2d/lv_draw_arm2d.mk
include $(LVGL_DIR)/$(LVGL_DIR_NAME)/src/draw/nxp_pxp/lv_draw_nxp_pxp.mk
include $(LVGL_DIR)/$(LVGL_DIR_NAME)/src/draw/nxp_vglite/lv_draw_nxp_vglite.mk
include $(LVGL_DIR)/$(LVGL_DIR_NAME)/src/draw/sdl/lv_draw_sdl.mk
include $(LVGL_DIR)/$(LVGL_DIR_NAME)/src/draw/stm32_dma2d/lv_draw_stm32_dma2d.mk
include $(LVGL_DIR)/$(LVGL_DIR_NAME)/src/draw/sw/lv_draw_sw.mk

View File

@@ -0,0 +1,152 @@
/**
* @file lv_draw_arc.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_draw.h"
#include "lv_draw_arc.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
void lv_draw_arc_dsc_init(lv_draw_arc_dsc_t * dsc)
{
lv_memset_00(dsc, sizeof(lv_draw_arc_dsc_t));
dsc->width = 1;
dsc->opa = LV_OPA_COVER;
dsc->color = lv_color_black();
}
void lv_draw_arc(lv_draw_ctx_t * draw_ctx, const lv_draw_arc_dsc_t * dsc, const lv_point_t * center, uint16_t radius,
uint16_t start_angle, uint16_t end_angle)
{
if(dsc->opa <= LV_OPA_MIN) return;
if(dsc->width == 0) return;
if(start_angle == end_angle) return;
draw_ctx->draw_arc(draw_ctx, dsc, center, radius, start_angle, end_angle);
// const lv_draw_backend_t * backend = lv_draw_backend_get();
// backend->draw_arc(center_x, center_y, radius, start_angle, end_angle, clip_area, dsc);
}
void lv_draw_arc_get_area(lv_coord_t x, lv_coord_t y, uint16_t radius, uint16_t start_angle, uint16_t end_angle,
lv_coord_t w, bool rounded, lv_area_t * area)
{
lv_coord_t rout = radius;
/*Special case: full arc invalidation */
if(end_angle == start_angle + 360) {
area->x1 = x - rout;
area->y1 = y - rout;
area->x2 = x + rout;
area->y2 = y + rout;
return;
}
if(start_angle > 360) start_angle -= 360;
if(end_angle > 360) end_angle -= 360;
lv_coord_t rin = radius - w;
lv_coord_t extra_area = rounded ? w / 2 + 1 : 0;
uint8_t start_quarter = start_angle / 90;
uint8_t end_quarter = end_angle / 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 <= end_angle) {
if(start_quarter == 0) {
area->y1 = y + ((lv_trigo_sin(start_angle) * rin) >> LV_TRIGO_SHIFT) - extra_area;
area->x2 = x + ((lv_trigo_sin(start_angle + 90) * rout) >> LV_TRIGO_SHIFT) + extra_area;
area->y2 = y + ((lv_trigo_sin(end_angle) * rout) >> LV_TRIGO_SHIFT) + extra_area;
area->x1 = x + ((lv_trigo_sin(end_angle + 90) * rin) >> LV_TRIGO_SHIFT) - extra_area;
}
else if(start_quarter == 1) {
area->y2 = y + ((lv_trigo_sin(start_angle) * rout) >> LV_TRIGO_SHIFT) + extra_area;
area->x2 = x + ((lv_trigo_sin(start_angle + 90) * rin) >> LV_TRIGO_SHIFT) + extra_area;
area->y1 = y + ((lv_trigo_sin(end_angle) * rin) >> LV_TRIGO_SHIFT) - extra_area;
area->x1 = x + ((lv_trigo_sin(end_angle + 90) * rout) >> LV_TRIGO_SHIFT) - extra_area;
}
else if(start_quarter == 2) {
area->x1 = x + ((lv_trigo_sin(start_angle + 90) * rout) >> LV_TRIGO_SHIFT) - extra_area;
area->y2 = y + ((lv_trigo_sin(start_angle) * rin) >> LV_TRIGO_SHIFT) + extra_area;
area->y1 = y + ((lv_trigo_sin(end_angle) * rout) >> LV_TRIGO_SHIFT) - extra_area;
area->x2 = x + ((lv_trigo_sin(end_angle + 90) * rin) >> LV_TRIGO_SHIFT) + extra_area;
}
else if(start_quarter == 3) {
area->x1 = x + ((lv_trigo_sin(start_angle + 90) * rin) >> LV_TRIGO_SHIFT) - extra_area;
area->y1 = y + ((lv_trigo_sin(start_angle) * rout) >> LV_TRIGO_SHIFT) - extra_area;
area->x2 = x + ((lv_trigo_sin(end_angle + 90) * rout) >> LV_TRIGO_SHIFT) + extra_area;
area->y2 = y + ((lv_trigo_sin(end_angle) * rin) >> LV_TRIGO_SHIFT) + extra_area;
}
}
else if(start_quarter == 0 && end_quarter == 1) {
area->x1 = x + ((lv_trigo_sin(end_angle + 90) * rout) >> LV_TRIGO_SHIFT) - extra_area;
area->y1 = y + ((LV_MIN(lv_trigo_sin(end_angle),
lv_trigo_sin(start_angle)) * rin) >> LV_TRIGO_SHIFT) - extra_area;
area->x2 = x + ((lv_trigo_sin(start_angle + 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) * rout) >> LV_TRIGO_SHIFT) - extra_area;
area->x2 = x + ((LV_MAX(lv_trigo_sin(start_angle + 90),
lv_trigo_sin(end_angle + 90)) * rin) >> LV_TRIGO_SHIFT) + extra_area;
area->y2 = y + ((lv_trigo_sin(start_angle) * rout) >> LV_TRIGO_SHIFT) + extra_area;
}
else if(start_quarter == 2 && end_quarter == 3) {
area->x1 = x + ((lv_trigo_sin(start_angle + 90) * rout) >> LV_TRIGO_SHIFT) - extra_area;
area->y1 = y - rout - extra_area;
area->x2 = x + ((lv_trigo_sin(end_angle + 90) * rout) >> LV_TRIGO_SHIFT) + extra_area;
area->y2 = y + (LV_MAX(lv_trigo_sin(end_angle) * rin,
lv_trigo_sin(start_angle) * rin) >> LV_TRIGO_SHIFT) + extra_area;
}
else if(start_quarter == 3 && end_quarter == 0) {
area->x1 = x + ((LV_MIN(lv_trigo_sin(end_angle + 90),
lv_trigo_sin(start_angle + 90)) * rin) >> LV_TRIGO_SHIFT) - extra_area;
area->y1 = y + ((lv_trigo_sin(start_angle) * rout) >> LV_TRIGO_SHIFT) - extra_area;
area->x2 = x + rout + extra_area;
area->y2 = y + ((lv_trigo_sin(end_angle) * 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
**********************/

View File

@@ -0,0 +1,83 @@
/**
* @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_color_t color;
lv_coord_t width;
uint16_t start_angle;
uint16_t end_angle;
const void * img_src;
lv_opa_t opa;
lv_blend_mode_t blend_mode : 2;
uint8_t rounded : 1;
} lv_draw_arc_dsc_t;
struct _lv_draw_ctx_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
void lv_draw_arc_dsc_init(lv_draw_arc_dsc_t * dsc);
/**
* Draw an arc. (Can draw pie too with great thickness.)
* @param center_x the x coordinate of the center of the arc
* @param center_y the y coordinate of the center of the arc
* @param radius the radius of the arc
* @param mask the arc will be drawn only in this mask
* @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 clip_area the arc will be drawn only in this area
* @param dsc pointer to an initialized `lv_draw_line_dsc_t` variable
*/
void lv_draw_arc(struct _lv_draw_ctx_t * draw_ctx, const lv_draw_arc_dsc_t * dsc, const lv_point_t * center,
uint16_t radius, uint16_t start_angle, uint16_t end_angle);
/**
* 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(lv_coord_t x, lv_coord_t y, uint16_t radius, uint16_t start_angle, uint16_t end_angle,
lv_coord_t w, bool rounded, lv_area_t * area);
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_DRAW_ARC_H*/

View File

@@ -0,0 +1,355 @@
/**
* @file lv_draw_img.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_draw_img.h"
#include "lv_img_cache.h"
#include "../hal/lv_hal_disp.h"
#include "../misc/lv_log.h"
#include "../core/lv_refr.h"
#include "../misc/lv_mem.h"
#include "../misc/lv_math.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
LV_ATTRIBUTE_FAST_MEM static lv_res_t decode_and_draw(lv_draw_ctx_t * draw_ctx, const lv_draw_img_dsc_t * draw_dsc,
const lv_area_t * coords, const void * src);
static void show_error(lv_draw_ctx_t * draw_ctx, const lv_area_t * coords, const char * msg);
static void draw_cleanup(_lv_img_cache_entry_t * cache);
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
void lv_draw_img_dsc_init(lv_draw_img_dsc_t * dsc)
{
lv_memset_00(dsc, sizeof(lv_draw_img_dsc_t));
dsc->recolor = lv_color_black();
dsc->opa = LV_OPA_COVER;
dsc->zoom = LV_IMG_ZOOM_NONE;
dsc->antialias = LV_COLOR_DEPTH > 8 ? 1 : 0;
}
/**
* Draw an image
* @param coords the coordinates of the image
* @param mask the image will be drawn only in this area
* @param src pointer to a lv_color_t array which contains the pixels of the image
* @param dsc pointer to an initialized `lv_draw_img_dsc_t` variable
*/
void lv_draw_img(lv_draw_ctx_t * draw_ctx, const lv_draw_img_dsc_t * dsc, const lv_area_t * coords, const void * src)
{
if(src == NULL) {
LV_LOG_WARN("Image draw: src is NULL");
show_error(draw_ctx, coords, "No\ndata");
return;
}
if(dsc->opa <= LV_OPA_MIN) return;
lv_res_t res;
if(draw_ctx->draw_img) {
res = draw_ctx->draw_img(draw_ctx, dsc, coords, src);
}
else {
res = decode_and_draw(draw_ctx, dsc, coords, src);
}
if(res == LV_RES_INV) {
LV_LOG_WARN("Image draw error");
show_error(draw_ctx, coords, "No\ndata");
return;
}
}
/**
* Get the pixel size of a color format in bits
* @param cf a color format (`LV_IMG_CF_...`)
* @return the pixel size in bits
*/
uint8_t lv_img_cf_get_px_size(lv_img_cf_t cf)
{
uint8_t px_size = 0;
switch(cf) {
case LV_IMG_CF_UNKNOWN:
case LV_IMG_CF_RAW:
px_size = 0;
break;
case LV_IMG_CF_TRUE_COLOR:
case LV_IMG_CF_TRUE_COLOR_CHROMA_KEYED:
px_size = LV_COLOR_SIZE;
break;
case LV_IMG_CF_TRUE_COLOR_ALPHA:
px_size = LV_IMG_PX_SIZE_ALPHA_BYTE << 3;
break;
case LV_IMG_CF_INDEXED_1BIT:
case LV_IMG_CF_ALPHA_1BIT:
px_size = 1;
break;
case LV_IMG_CF_INDEXED_2BIT:
case LV_IMG_CF_ALPHA_2BIT:
px_size = 2;
break;
case LV_IMG_CF_INDEXED_4BIT:
case LV_IMG_CF_ALPHA_4BIT:
px_size = 4;
break;
case LV_IMG_CF_INDEXED_8BIT:
case LV_IMG_CF_ALPHA_8BIT:
px_size = 8;
break;
default:
px_size = 0;
break;
}
return px_size;
}
/**
* Check if a color format is chroma keyed or not
* @param cf a color format (`LV_IMG_CF_...`)
* @return true: chroma keyed; false: not chroma keyed
*/
bool lv_img_cf_is_chroma_keyed(lv_img_cf_t cf)
{
bool is_chroma_keyed = false;
switch(cf) {
case LV_IMG_CF_TRUE_COLOR_CHROMA_KEYED:
case LV_IMG_CF_RAW_CHROMA_KEYED:
is_chroma_keyed = true;
break;
default:
is_chroma_keyed = false;
break;
}
return is_chroma_keyed;
}
/**
* Check if a color format has alpha channel or not
* @param cf a color format (`LV_IMG_CF_...`)
* @return true: has alpha channel; false: doesn't have alpha channel
*/
bool lv_img_cf_has_alpha(lv_img_cf_t cf)
{
bool has_alpha = false;
switch(cf) {
case LV_IMG_CF_TRUE_COLOR_ALPHA:
case LV_IMG_CF_RAW_ALPHA:
case LV_IMG_CF_INDEXED_1BIT:
case LV_IMG_CF_INDEXED_2BIT:
case LV_IMG_CF_INDEXED_4BIT:
case LV_IMG_CF_INDEXED_8BIT:
case LV_IMG_CF_ALPHA_1BIT:
case LV_IMG_CF_ALPHA_2BIT:
case LV_IMG_CF_ALPHA_4BIT:
case LV_IMG_CF_ALPHA_8BIT:
has_alpha = true;
break;
default:
has_alpha = false;
break;
}
return has_alpha;
}
/**
* Get the type of an image source
* @param src pointer to an image source:
* - pointer to an 'lv_img_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_IMG_SRC_VARIABLE/FILE/SYMBOL/UNKNOWN
*/
lv_img_src_t lv_img_src_get_type(const void * src)
{
lv_img_src_t img_src_type = LV_IMG_SRC_UNKNOWN;
if(src == NULL) return img_src_type;
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) {
img_src_type = LV_IMG_SRC_FILE; /*If it's an ASCII character then it's file name*/
}
else if(u8_p[0] >= 0x80) {
img_src_type = LV_IMG_SRC_SYMBOL; /*Symbols begins after 0x7F*/
}
else {
img_src_type = LV_IMG_SRC_VARIABLE; /*`lv_img_dsc_t` is draw to the first byte < 0x20*/
}
if(LV_IMG_SRC_UNKNOWN == img_src_type) {
LV_LOG_WARN("lv_img_src_get_type: unknown image type");
}
return img_src_type;
}
void lv_draw_img_decoded(lv_draw_ctx_t * draw_ctx, const lv_draw_img_dsc_t * dsc,
const lv_area_t * coords, const uint8_t * map_p, lv_img_cf_t color_format)
{
if(draw_ctx->draw_img_decoded == NULL) return;
draw_ctx->draw_img_decoded(draw_ctx, dsc, coords, map_p, color_format);
}
/**********************
* STATIC FUNCTIONS
**********************/
LV_ATTRIBUTE_FAST_MEM static lv_res_t decode_and_draw(lv_draw_ctx_t * draw_ctx, const lv_draw_img_dsc_t * draw_dsc,
const lv_area_t * coords, const void * src)
{
if(draw_dsc->opa <= LV_OPA_MIN) return LV_RES_OK;
_lv_img_cache_entry_t * cdsc = _lv_img_cache_open(src, draw_dsc->recolor, draw_dsc->frame_id);
if(cdsc == NULL) return LV_RES_INV;
lv_img_cf_t cf;
if(lv_img_cf_is_chroma_keyed(cdsc->dec_dsc.header.cf)) cf = LV_IMG_CF_TRUE_COLOR_CHROMA_KEYED;
else if(lv_img_cf_has_alpha(cdsc->dec_dsc.header.cf)) cf = LV_IMG_CF_TRUE_COLOR_ALPHA;
else cf = LV_IMG_CF_TRUE_COLOR;
if(cdsc->dec_dsc.error_msg != NULL) {
LV_LOG_WARN("Image draw error");
show_error(draw_ctx, coords, cdsc->dec_dsc.error_msg);
}
/*The decoder could open the image and gave the entire uncompressed image.
*Just draw it!*/
else if(cdsc->dec_dsc.img_data) {
lv_area_t map_area_rot;
lv_area_copy(&map_area_rot, coords);
if(draw_dsc->angle || draw_dsc->zoom != LV_IMG_ZOOM_NONE) {
int32_t w = lv_area_get_width(coords);
int32_t h = lv_area_get_height(coords);
_lv_img_buf_get_transformed_area(&map_area_rot, w, h, draw_dsc->angle, draw_dsc->zoom, &draw_dsc->pivot);
map_area_rot.x1 += coords->x1;
map_area_rot.y1 += coords->y1;
map_area_rot.x2 += coords->x1;
map_area_rot.y2 += coords->y1;
}
lv_area_t clip_com; /*Common area of mask and coords*/
bool union_ok;
union_ok = _lv_area_intersect(&clip_com, draw_ctx->clip_area, &map_area_rot);
/*Out of mask. There is nothing to draw so the image is drawn successfully.*/
if(union_ok == false) {
draw_cleanup(cdsc);
return LV_RES_OK;
}
const lv_area_t * clip_area_ori = draw_ctx->clip_area;
draw_ctx->clip_area = &clip_com;
lv_draw_img_decoded(draw_ctx, draw_dsc, coords, cdsc->dec_dsc.img_data, cf);
draw_ctx->clip_area = clip_area_ori;
}
/*The whole uncompressed image is not available. Try to read it line-by-line*/
else {
lv_area_t mask_com; /*Common area of mask and coords*/
bool union_ok;
union_ok = _lv_area_intersect(&mask_com, draw_ctx->clip_area, coords);
/*Out of mask. There is nothing to draw so the image is drawn successfully.*/
if(union_ok == false) {
draw_cleanup(cdsc);
return LV_RES_OK;
}
int32_t width = lv_area_get_width(&mask_com);
uint8_t * buf = lv_mem_buf_get(lv_area_get_width(&mask_com) *
LV_IMG_PX_SIZE_ALPHA_BYTE); /*+1 because of the possible alpha byte*/
const lv_area_t * clip_area_ori = draw_ctx->clip_area;
lv_area_t line;
lv_area_copy(&line, &mask_com);
lv_area_set_height(&line, 1);
int32_t x = mask_com.x1 - coords->x1;
int32_t y = mask_com.y1 - coords->y1;
int32_t row;
lv_res_t read_res;
for(row = mask_com.y1; row <= mask_com.y2; row++) {
lv_area_t mask_line;
union_ok = _lv_area_intersect(&mask_line, clip_area_ori, &line);
if(union_ok == false) continue;
read_res = lv_img_decoder_read_line(&cdsc->dec_dsc, x, y, width, buf);
if(read_res != LV_RES_OK) {
lv_img_decoder_close(&cdsc->dec_dsc);
LV_LOG_WARN("Image draw can't read the line");
lv_mem_buf_release(buf);
draw_cleanup(cdsc);
draw_ctx->clip_area = clip_area_ori;
return LV_RES_INV;
}
draw_ctx->clip_area = &mask_line;
lv_draw_img_decoded(draw_ctx, draw_dsc, &line, buf, cf);
line.y1++;
line.y2++;
y++;
}
draw_ctx->clip_area = clip_area_ori;
lv_mem_buf_release(buf);
}
draw_cleanup(cdsc);
return LV_RES_OK;
}
static void show_error(lv_draw_ctx_t * draw_ctx, const lv_area_t * coords, const char * msg)
{
lv_draw_rect_dsc_t rect_dsc;
lv_draw_rect_dsc_init(&rect_dsc);
rect_dsc.bg_color = lv_color_white();
lv_draw_rect(draw_ctx, &rect_dsc, coords);
lv_draw_label_dsc_t label_dsc;
lv_draw_label_dsc_init(&label_dsc);
lv_draw_label(draw_ctx, &label_dsc, coords, msg, NULL);
}
static void draw_cleanup(_lv_img_cache_entry_t * cache)
{
/*Automatically close images with no caching*/
#if LV_IMG_CACHE_DEF_SIZE == 0
lv_img_decoder_close(&cache->dec_dsc);
#else
LV_UNUSED(cache);
#endif
}

View File

@@ -0,0 +1,104 @@
/**
* @file lv_draw_img.h
*
*/
#ifndef LV_DRAW_IMG_H
#define LV_DRAW_IMG_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "lv_img_decoder.h"
#include "lv_img_buf.h"
#include "../misc/lv_style.h"
/*********************
* DEFINES
*********************/
/**********************
* MACROS
**********************/
/**********************
* TYPEDEFS
**********************/
typedef struct {
uint16_t angle;
uint16_t zoom;
lv_point_t pivot;
lv_color_t recolor;
lv_opa_t recolor_opa;
lv_opa_t opa;
lv_blend_mode_t blend_mode : 4;
int32_t frame_id;
uint8_t antialias : 1;
} lv_draw_img_dsc_t;
struct _lv_draw_ctx_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
void lv_draw_img_dsc_init(lv_draw_img_dsc_t * dsc);
/**
* Draw an image
* @param coords the coordinates of the image
* @param mask the image will be drawn only in this area
* @param src pointer to a lv_color_t array which contains the pixels of the image
* @param dsc pointer to an initialized `lv_draw_img_dsc_t` variable
*/
void lv_draw_img(struct _lv_draw_ctx_t * draw_ctx, const lv_draw_img_dsc_t * dsc, const lv_area_t * coords,
const void * src);
void lv_draw_img_decoded(struct _lv_draw_ctx_t * draw_ctx, const lv_draw_img_dsc_t * dsc,
const lv_area_t * coords, const uint8_t * map_p, lv_img_cf_t color_format);
/**
* Get the type of an image source
* @param src pointer to an image source:
* - pointer to an 'lv_img_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_IMG_SRC_VARIABLE/FILE/SYMBOL/UNKNOWN
*/
lv_img_src_t lv_img_src_get_type(const void * src);
/**
* Get the pixel size of a color format in bits
* @param cf a color format (`LV_IMG_CF_...`)
* @return the pixel size in bits
*/
uint8_t lv_img_cf_get_px_size(lv_img_cf_t cf);
/**
* Check if a color format is chroma keyed or not
* @param cf a color format (`LV_IMG_CF_...`)
* @return true: chroma keyed; false: not chroma keyed
*/
bool lv_img_cf_is_chroma_keyed(lv_img_cf_t cf);
/**
* Check if a color format has alpha channel or not
* @param cf a color format (`LV_IMG_CF_...`)
* @return true: has alpha channel; false: doesn't have alpha channel
*/
bool lv_img_cf_has_alpha(lv_img_cf_t cf);
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_DRAW_IMG_H*/

View File

@@ -0,0 +1,417 @@
/**
* @file lv_draw_label.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_draw.h"
#include "lv_draw_label.h"
#include "../misc/lv_math.h"
#include "../hal/lv_hal_disp.h"
#include "../core/lv_refr.h"
#include "../misc/lv_bidi.h"
#include "../misc/lv_assert.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*/
/**********************
* TYPEDEFS
**********************/
enum {
CMD_STATE_WAIT,
CMD_STATE_PAR,
CMD_STATE_IN,
};
typedef uint8_t cmd_state_t;
/**********************
* STATIC PROTOTYPES
**********************/
static uint8_t hex_char_to_num(char hex);
/**********************
* STATIC VARIABLES
**********************/
/**********************
* GLOBAL VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
void lv_draw_label_dsc_init(lv_draw_label_dsc_t * dsc)
{
lv_memset_00(dsc, sizeof(lv_draw_label_dsc_t));
dsc->opa = LV_OPA_COVER;
dsc->color = lv_color_black();
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;
}
/**
* Write a text
* @param coords coordinates of the label
* @param mask the label will be drawn only in this area
* @param dsc pointer to draw descriptor
* @param txt `\0` terminated text to write
* @param hint pointer to a `lv_draw_label_hint_t` variable.
* It is managed by the draw to speed up the drawing of very long texts (thousands of lines).
*/
LV_ATTRIBUTE_FAST_MEM void lv_draw_label(lv_draw_ctx_t * draw_ctx, const lv_draw_label_dsc_t * dsc,
const lv_area_t * coords, const char * txt, lv_draw_label_hint_t * hint)
{
if(dsc->opa <= LV_OPA_MIN) return;
if(dsc->font == NULL) {
LV_LOG_WARN("dsc->font == NULL");
return;
}
if(draw_ctx->draw_letter == NULL) {
LV_LOG_WARN("draw->draw_letter == NULL (there is no function to draw letters)");
return;
}
lv_draw_label_dsc_t dsc_mod = *dsc;
const lv_font_t * font = dsc->font;
int32_t w;
/*No need to waste processor time if string is empty*/
if(txt == NULL || txt[0] == '\0')
return;
lv_area_t clipped_area;
bool clip_ok = _lv_area_intersect(&clipped_area, coords, draw_ctx->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, txt);
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_txt_get_size(&p, txt, 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;
pos.x = coords->x1;
pos.y = 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(hint && y_ofs == 0 && coords->y1 < 0) {
/*If the label changed too much recalculate the hint.*/
if(LV_ABS(hint->coord_y - coords->y1) > LV_LABEL_HINT_UPDATE_TH - 2 * line_height) {
hint->line_start = -1;
}
last_line_start = hint->line_start;
}
/*Use the hint if it's valid*/
if(hint && last_line_start >= 0) {
line_start = last_line_start;
pos.y += hint->y;
}
uint32_t line_end = line_start + _lv_txt_get_next_line(&txt[line_start], font, dsc->letter_space, w, NULL, dsc->flag);
/*Go the first visible line*/
while(pos.y + line_height_font < draw_ctx->clip_area->y1) {
/*Go to next line*/
line_start = line_end;
line_end += _lv_txt_get_next_line(&txt[line_start], font, dsc->letter_space, w, NULL, dsc->flag);
pos.y += line_height;
/*Save at the threshold coordinate*/
if(hint && pos.y >= -LV_LABEL_HINT_UPDATE_TH && hint->line_start < 0) {
hint->line_start = line_start;
hint->y = pos.y - coords->y1;
hint->coord_y = coords->y1;
}
if(txt[line_start] == '\0') return;
}
/*Align to middle*/
if(align == LV_TEXT_ALIGN_CENTER) {
line_width = lv_txt_get_width(&txt[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_txt_get_width(&txt[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_draw_line_dsc_t line_dsc;
if((dsc->decor & LV_TEXT_DECOR_UNDERLINE) || (dsc->decor & LV_TEXT_DECOR_STRIKETHROUGH)) {
lv_draw_line_dsc_init(&line_dsc);
line_dsc.color = dsc->color;
line_dsc.width = font->underline_thickness ? font->underline_thickness : 1;
line_dsc.opa = dsc->opa;
line_dsc.blend_mode = dsc->blend_mode;
}
cmd_state_t cmd_state = CMD_STATE_WAIT;
uint32_t i;
uint32_t par_start = 0;
lv_color_t recolor;
lv_color_t color = lv_color_black();
int32_t letter_w;
lv_draw_rect_dsc_t draw_dsc_sel;
lv_draw_rect_dsc_init(&draw_dsc_sel);
draw_dsc_sel.bg_color = dsc->sel_bg_color;
int32_t pos_x_start = pos.x;
/*Write out all lines*/
while(txt[line_start] != '\0') {
pos.x += x_ofs;
/*Write all letter of a line*/
cmd_state = CMD_STATE_WAIT;
i = 0;
#if LV_USE_BIDI
char * bidi_txt = lv_mem_buf_get(line_end - line_start + 1);
_lv_bidi_process_paragraph(txt + line_start, bidi_txt, line_end - line_start, base_dir, NULL, 0);
#else
const char * bidi_txt = txt + line_start;
#endif
while(i < line_end - line_start) {
uint32_t logical_char_pos = 0;
if(sel_start != 0xFFFF && sel_end != 0xFFFF) {
#if LV_USE_BIDI
logical_char_pos = _lv_txt_encoded_get_char_id(txt, line_start);
uint32_t t = _lv_txt_encoded_get_char_id(bidi_txt, i);
logical_char_pos += _lv_bidi_get_logical_pos(bidi_txt, NULL, line_end - line_start, base_dir, t, NULL);
#else
logical_char_pos = _lv_txt_encoded_get_char_id(txt, line_start + i);
#endif
}
uint32_t letter;
uint32_t letter_next;
_lv_txt_encoded_letter_next_2(bidi_txt, &letter, &letter_next, &i);
/*Handle the re-color command*/
if((dsc->flag & LV_TEXT_FLAG_RECOLOR) != 0) {
if(letter == (uint32_t)LV_TXT_COLOR_CMD[0]) {
if(cmd_state == CMD_STATE_WAIT) { /*Start char*/
par_start = i;
cmd_state = CMD_STATE_PAR;
continue;
}
else if(cmd_state == CMD_STATE_PAR) { /*Other start char in parameter escaped cmd. char*/
cmd_state = CMD_STATE_WAIT;
}
else if(cmd_state == CMD_STATE_IN) { /*Command end*/
cmd_state = CMD_STATE_WAIT;
continue;
}
}
/*Skip the color parameter and wait the space after it*/
if(cmd_state == CMD_STATE_PAR) {
if(letter == ' ') {
/*Get the parameter*/
if(i - par_start == LABEL_RECOLOR_PAR_LENGTH + 1) {
char buf[LABEL_RECOLOR_PAR_LENGTH + 1];
lv_memcpy_small(buf, &bidi_txt[par_start], LABEL_RECOLOR_PAR_LENGTH);
buf[LABEL_RECOLOR_PAR_LENGTH] = '\0';
int 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.full = dsc->color.full;
}
cmd_state = CMD_STATE_IN; /*After the parameter the text is in the command*/
}
continue;
}
}
color = dsc->color;
if(cmd_state == CMD_STATE_IN) color = recolor;
letter_w = lv_font_get_glyph_width(font, letter, letter_next);
if(sel_start != 0xFFFF && sel_end != 0xFFFF) {
if(logical_char_pos >= sel_start && logical_char_pos < sel_end) {
lv_area_t sel_coords;
sel_coords.x1 = pos.x;
sel_coords.y1 = pos.y;
sel_coords.x2 = pos.x + letter_w + dsc->letter_space - 1;
sel_coords.y2 = pos.y + line_height - 1;
lv_draw_rect(draw_ctx, &draw_dsc_sel, &sel_coords);
color = dsc->sel_color;
}
}
dsc_mod.color = color;
lv_draw_letter(draw_ctx, &dsc_mod, &pos, letter);
if(letter_w > 0) {
pos.x += letter_w + dsc->letter_space;
}
}
if(dsc->decor & LV_TEXT_DECOR_STRIKETHROUGH) {
lv_point_t p1;
lv_point_t p2;
p1.x = pos_x_start;
p1.y = pos.y + (dsc->font->line_height / 2) + line_dsc.width / 2;
p2.x = pos.x;
p2.y = p1.y;
line_dsc.color = color;
lv_draw_line(draw_ctx, &line_dsc, &p1, &p2);
}
if(dsc->decor & LV_TEXT_DECOR_UNDERLINE) {
lv_point_t p1;
lv_point_t p2;
p1.x = pos_x_start;
p1.y = pos.y + dsc->font->line_height - dsc->font->base_line - font->underline_position;
p2.x = pos.x;
p2.y = p1.y;
line_dsc.color = color;
lv_draw_line(draw_ctx, &line_dsc, &p1, &p2);
}
#if LV_USE_BIDI
lv_mem_buf_release(bidi_txt);
bidi_txt = NULL;
#endif
/*Go to next line*/
line_start = line_end;
line_end += _lv_txt_get_next_line(&txt[line_start], font, dsc->letter_space, w, NULL, dsc->flag);
pos.x = coords->x1;
/*Align to middle*/
if(align == LV_TEXT_ALIGN_CENTER) {
line_width =
lv_txt_get_width(&txt[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_txt_get_width(&txt[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 > draw_ctx->clip_area->y2) return;
}
LV_ASSERT_MEM_INTEGRITY();
}
void lv_draw_letter(lv_draw_ctx_t * draw_ctx, const lv_draw_label_dsc_t * dsc, const lv_point_t * pos_p,
uint32_t letter)
{
draw_ctx->draw_letter(draw_ctx, dsc, pos_p, letter);
}
/**********************
* 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)
{
uint8_t result = 0;
if(hex >= '0' && hex <= '9') {
result = hex - '0';
}
else {
if(hex >= 'a') hex -= 'a' - 'A'; /*Convert to upper case*/
switch(hex) {
case 'A':
result = 10;
break;
case 'B':
result = 11;
break;
case 'C':
result = 12;
break;
case 'D':
result = 13;
break;
case 'E':
result = 14;
break;
case 'F':
result = 15;
break;
default:
result = 0;
break;
}
}
return result;
}

View File

@@ -0,0 +1,100 @@
/**
* @file lv_draw_label.h
*
*/
#ifndef LV_DRAW_LABEL_H
#define LV_DRAW_LABEL_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../misc/lv_bidi.h"
#include "../misc/lv_txt.h"
#include "../misc/lv_color.h"
#include "../misc/lv_style.h"
/*********************
* DEFINES
*********************/
#define LV_DRAW_LABEL_NO_TXT_SEL (0xFFFF)
/**********************
* TYPEDEFS
**********************/
typedef struct {
const lv_font_t * font;
uint32_t sel_start;
uint32_t sel_end;
lv_color_t color;
lv_color_t sel_color;
lv_color_t sel_bg_color;
lv_coord_t line_space;
lv_coord_t letter_space;
lv_coord_t ofs_x;
lv_coord_t ofs_y;
lv_opa_t opa;
lv_base_dir_t bidi_dir;
lv_text_align_t align;
lv_text_flag_t flag;
lv_text_decor_t decor : 3;
lv_blend_mode_t blend_mode: 3;
} lv_draw_label_dsc_t;
/** 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.*/
typedef 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;
} lv_draw_label_hint_t;
struct _lv_draw_ctx_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
LV_ATTRIBUTE_FAST_MEM void lv_draw_label_dsc_init(lv_draw_label_dsc_t * dsc);
/**
* Write a text
* @param coords coordinates of the label
* @param mask the label will be drawn only in this area
* @param dsc pointer to draw descriptor
* @param txt `\0` terminated text to write
* @param hint pointer to a `lv_draw_label_hint_t` variable.
* It is managed by the draw to speed up the drawing of very long texts (thousands of lines).
*/
LV_ATTRIBUTE_FAST_MEM void lv_draw_label(struct _lv_draw_ctx_t * draw_ctx, const lv_draw_label_dsc_t * dsc,
const lv_area_t * coords, const char * txt, lv_draw_label_hint_t * hint);
void lv_draw_letter(struct _lv_draw_ctx_t * draw_ctx, const lv_draw_label_dsc_t * dsc, const lv_point_t * pos_p,
uint32_t letter);
/***********************
* GLOBAL VARIABLES
***********************/
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_DRAW_LABEL_H*/

View File

@@ -0,0 +1,56 @@
/**
* @file lv_draw_line.c
*
*/
/*********************
* INCLUDES
*********************/
#include <stdbool.h>
#include "../core/lv_refr.h"
#include "../misc/lv_math.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
LV_ATTRIBUTE_FAST_MEM void lv_draw_line_dsc_init(lv_draw_line_dsc_t * dsc)
{
lv_memset_00(dsc, sizeof(lv_draw_line_dsc_t));
dsc->width = 1;
dsc->opa = LV_OPA_COVER;
dsc->color = lv_color_black();
}
LV_ATTRIBUTE_FAST_MEM void lv_draw_line(struct _lv_draw_ctx_t * draw_ctx, const lv_draw_line_dsc_t * dsc,
const lv_point_t * point1, const lv_point_t * point2)
{
if(dsc->width == 0) return;
if(dsc->opa <= LV_OPA_MIN) return;
draw_ctx->draw_line(draw_ctx, dsc, point1, point2);
}
/**********************
* STATIC FUNCTIONS
**********************/

View File

@@ -0,0 +1,67 @@
/**
* @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_color_t color;
lv_coord_t width;
lv_coord_t dash_width;
lv_coord_t dash_gap;
lv_opa_t opa;
lv_blend_mode_t blend_mode : 2;
uint8_t round_start : 1;
uint8_t round_end : 1;
uint8_t raw_end : 1; /*Do not bother with perpendicular line ending if it's not visible for any reason*/
} lv_draw_line_dsc_t;
struct _lv_draw_ctx_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
LV_ATTRIBUTE_FAST_MEM void lv_draw_line_dsc_init(lv_draw_line_dsc_t * dsc);
/**
* Draw a line
* @param point1 first point of the line
* @param point2 second point of the line
* @param clip the line will be drawn only in this area
* @param dsc pointer to an initialized `lv_draw_line_dsc_t` variable
*/
void lv_draw_line(struct _lv_draw_ctx_t * draw_ctx, const lv_draw_line_dsc_t * dsc, const lv_point_t * point1,
const lv_point_t * point2);
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_DRAW_LINE_H*/

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,394 @@
/**
* @file lv_draw_mask.h
*
*/
#ifndef LV_DRAW_MASK_H
#define LV_DRAW_MASK_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include <stdbool.h>
#include "../misc/lv_area.h"
#include "../misc/lv_color.h"
#include "../misc/lv_math.h"
/*********************
* DEFINES
*********************/
#define LV_MASK_ID_INV (-1)
#if LV_DRAW_COMPLEX
# define _LV_MASK_MAX_NUM 16
#else
# define _LV_MASK_MAX_NUM 1
#endif
/**********************
* TYPEDEFS
**********************/
enum {
LV_DRAW_MASK_RES_TRANSP,
LV_DRAW_MASK_RES_FULL_COVER,
LV_DRAW_MASK_RES_CHANGED,
LV_DRAW_MASK_RES_UNKNOWN
};
typedef uint8_t lv_draw_mask_res_t;
typedef struct {
void * param;
void * custom_id;
} _lv_draw_mask_saved_t;
typedef _lv_draw_mask_saved_t _lv_draw_mask_saved_arr_t[_LV_MASK_MAX_NUM];
#if LV_DRAW_COMPLEX == 0
static inline uint8_t lv_draw_mask_get_cnt(void)
{
return 0;
}
static inline bool lv_draw_mask_is_any(const lv_area_t * a)
{
LV_UNUSED(a);
return false;
}
#endif
#if LV_DRAW_COMPLEX
enum {
LV_DRAW_MASK_TYPE_LINE,
LV_DRAW_MASK_TYPE_ANGLE,
LV_DRAW_MASK_TYPE_RADIUS,
LV_DRAW_MASK_TYPE_FADE,
LV_DRAW_MASK_TYPE_MAP,
LV_DRAW_MASK_TYPE_POLYGON,
};
typedef uint8_t lv_draw_mask_type_t;
enum {
LV_DRAW_MASK_LINE_SIDE_LEFT = 0,
LV_DRAW_MASK_LINE_SIDE_RIGHT,
LV_DRAW_MASK_LINE_SIDE_TOP,
LV_DRAW_MASK_LINE_SIDE_BOTTOM,
};
/**
* A common callback type for every mask type.
* Used internally by the library.
*/
typedef lv_draw_mask_res_t (*lv_draw_mask_xcb_t)(lv_opa_t * mask_buf, lv_coord_t abs_x, lv_coord_t abs_y,
lv_coord_t len,
void * p);
typedef uint8_t lv_draw_mask_line_side_t;
typedef struct {
lv_draw_mask_xcb_t cb;
lv_draw_mask_type_t type;
} _lv_draw_mask_common_dsc_t;
typedef struct {
/*The first element must be the common descriptor*/
_lv_draw_mask_common_dsc_t dsc;
struct {
/*First point*/
lv_point_t p1;
/*Second point*/
lv_point_t p2;
/*Which side to keep?*/
lv_draw_mask_line_side_t side : 2;
} cfg;
/*A point of the line*/
lv_point_t origo;
/*X / (1024*Y) steepness (X is 0..1023 range). What is the change of X in 1024 Y?*/
int32_t xy_steep;
/*Y / (1024*X) steepness (Y is 0..1023 range). What is the change of Y in 1024 X?*/
int32_t yx_steep;
/*Helper which stores yx_steep for flat lines and xy_steep for steep (non flat) lines*/
int32_t steep;
/*Steepness in 1 px in 0..255 range. Used only by flat lines.*/
int32_t spx;
/*1: It's a flat line? (Near to horizontal)*/
uint8_t flat : 1;
/*Invert the mask. The default is: Keep the left part.
*It is used to select left/right/top/bottom*/
uint8_t inv: 1;
} lv_draw_mask_line_param_t;
typedef struct {
/*The first element must be the common descriptor*/
_lv_draw_mask_common_dsc_t dsc;
struct {
lv_point_t vertex_p;
lv_coord_t start_angle;
lv_coord_t end_angle;
} cfg;
lv_draw_mask_line_param_t start_line;
lv_draw_mask_line_param_t end_line;
uint16_t delta_deg;
} lv_draw_mask_angle_param_t;
typedef struct {
uint8_t * buf;
lv_opa_t * cir_opa; /*Opacity of values on the circumference of an 1/4 circle*/
uint16_t * x_start_on_y; /*The x coordinate of the circle for each y value*/
uint16_t * opa_start_on_y; /*The index of `cir_opa` for each y value*/
int32_t life; /*How many times the entry way used*/
uint32_t used_cnt; /*Like a semaphore to count the referencing masks*/
lv_coord_t radius; /*The radius of the entry*/
} _lv_draw_mask_radius_circle_dsc_t;
typedef _lv_draw_mask_radius_circle_dsc_t _lv_draw_mask_radius_circle_dsc_arr_t[LV_CIRCLE_CACHE_SIZE];
typedef struct {
/*The first element must be the common descriptor*/
_lv_draw_mask_common_dsc_t dsc;
struct {
lv_area_t rect;
lv_coord_t radius;
/*Invert the mask. 0: Keep the pixels inside.*/
uint8_t outer: 1;
} cfg;
_lv_draw_mask_radius_circle_dsc_t * circle;
} lv_draw_mask_radius_param_t;
typedef struct {
/*The first element must be the common descriptor*/
_lv_draw_mask_common_dsc_t dsc;
struct {
lv_area_t coords;
lv_coord_t y_top;
lv_coord_t y_bottom;
lv_opa_t opa_top;
lv_opa_t opa_bottom;
} cfg;
} lv_draw_mask_fade_param_t;
typedef struct _lv_draw_mask_map_param_t {
/*The first element must be the common descriptor*/
_lv_draw_mask_common_dsc_t dsc;
struct {
lv_area_t coords;
const lv_opa_t * map;
} cfg;
} lv_draw_mask_map_param_t;
typedef struct {
/*The first element must be the common descriptor*/
_lv_draw_mask_common_dsc_t dsc;
struct {
lv_point_t * points;
uint16_t point_cnt;
} cfg;
} lv_draw_mask_polygon_param_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Add a draw mask. Everything drawn after it (until removing the mask) will be affected by the mask.
* @param param an initialized mask parameter. Only the pointer is saved.
* @param custom_id a custom pointer to identify the mask. Used in `lv_draw_mask_remove_custom`.
* @return the an integer, the ID of the mask. Can be used in `lv_draw_mask_remove_id`.
*/
int16_t lv_draw_mask_add(void * param, void * custom_id);
//! @cond Doxygen_Suppress
/**
* Apply the added buffers on a line. Used internally by the library's drawing routines.
* @param mask_buf store the result mask here. Has to be `len` byte long. Should be initialized with `0xFF`.
* @param abs_x absolute X coordinate where the line to calculate start
* @param abs_y absolute Y coordinate where the line to calculate start
* @param len length of the line to calculate (in pixel count)
* @return One of these values:
* - `LV_DRAW_MASK_RES_FULL_TRANSP`: the whole line is transparent. `mask_buf` is not set to zero
* - `LV_DRAW_MASK_RES_FULL_COVER`: the whole line is fully visible. `mask_buf` is unchanged
* - `LV_DRAW_MASK_RES_CHANGED`: `mask_buf` has changed, it shows the desired opacity of each pixel in the given line
*/
LV_ATTRIBUTE_FAST_MEM lv_draw_mask_res_t lv_draw_mask_apply(lv_opa_t * mask_buf, lv_coord_t abs_x, lv_coord_t abs_y,
lv_coord_t len);
/**
* Apply the specified buffers on a line. Used internally by the library's drawing routines.
* @param mask_buf store the result mask here. Has to be `len` byte long. Should be initialized with `0xFF`.
* @param abs_x absolute X coordinate where the line to calculate start
* @param abs_y absolute Y coordinate where the line to calculate start
* @param len length of the line to calculate (in pixel count)
* @param ids ID array of added buffers
* @param ids_count number of ID array
* @return One of these values:
* - `LV_DRAW_MASK_RES_FULL_TRANSP`: the whole line is transparent. `mask_buf` is not set to zero
* - `LV_DRAW_MASK_RES_FULL_COVER`: the whole line is fully visible. `mask_buf` is unchanged
* - `LV_DRAW_MASK_RES_CHANGED`: `mask_buf` has changed, it shows the desired opacity of each pixel in the given line
*/
LV_ATTRIBUTE_FAST_MEM lv_draw_mask_res_t lv_draw_mask_apply_ids(lv_opa_t * mask_buf, lv_coord_t abs_x, lv_coord_t abs_y,
lv_coord_t len, const int16_t * ids, int16_t ids_count);
//! @endcond
/**
* Remove a mask with a given ID
* @param id the ID of the mask. Returned by `lv_draw_mask_add`
* @return the parameter of the removed mask.
* If more masks have `custom_id` ID then the last mask's parameter will be returned
*/
void * lv_draw_mask_remove_id(int16_t id);
/**
* Remove all mask with a given custom ID
* @param custom_id a pointer used in `lv_draw_mask_add`
* @return return the parameter of the removed mask.
* If more masks have `custom_id` ID then the last mask's parameter will be returned
*/
void * lv_draw_mask_remove_custom(void * custom_id);
/**
* Free the data from the parameter.
* It's called inside `lv_draw_mask_remove_id` and `lv_draw_mask_remove_custom`
* Needs to be called only in special cases when the mask is not added by `lv_draw_mask_add`
* and not removed by `lv_draw_mask_remove_id` or `lv_draw_mask_remove_custom`
* @param p pointer to a mask parameter
*/
void lv_draw_mask_free_param(void * p);
/**
* Called by LVGL the rendering of a screen is ready to clean up
* the temporal (cache) data of the masks
*/
void _lv_draw_mask_cleanup(void);
//! @cond Doxygen_Suppress
/**
* Count the currently added masks
* @return number of active masks
*/
LV_ATTRIBUTE_FAST_MEM uint8_t lv_draw_mask_get_cnt(void);
/**
* Check if there is any added draw mask
* @param a an area to test for affecting masks.
* @return true: there is t least 1 draw mask; false: there are no draw masks
*/
bool lv_draw_mask_is_any(const lv_area_t * a);
//! @endcond
/**
*Initialize a line mask from two points.
* @param param pointer to a `lv_draw_mask_param_t` to initialize
* @param p1x X coordinate of the first point of the line
* @param p1y Y coordinate of the first point of the line
* @param p2x X coordinate of the second point of the line
* @param p2y y coordinate of the second point of the line
* @param side and element of `lv_draw_mask_line_side_t` to describe which side to keep.
* With `LV_DRAW_MASK_LINE_SIDE_LEFT/RIGHT` and horizontal line all pixels are kept
* With `LV_DRAW_MASK_LINE_SIDE_TOP/BOTTOM` and vertical line all pixels are kept
*/
void lv_draw_mask_line_points_init(lv_draw_mask_line_param_t * param, lv_coord_t p1x, lv_coord_t p1y, lv_coord_t p2x,
lv_coord_t p2y, lv_draw_mask_line_side_t side);
/**
*Initialize a line mask from a point and an angle.
* @param param pointer to a `lv_draw_mask_param_t` to initialize
* @param px X coordinate of a point of the line
* @param py X coordinate of a point of the line
* @param angle right 0 deg, bottom: 90
* @param side and element of `lv_draw_mask_line_side_t` to describe which side to keep.
* With `LV_DRAW_MASK_LINE_SIDE_LEFT/RIGHT` and horizontal line all pixels are kept
* With `LV_DRAW_MASK_LINE_SIDE_TOP/BOTTOM` and vertical line all pixels are kept
*/
void lv_draw_mask_line_angle_init(lv_draw_mask_line_param_t * param, lv_coord_t p1x, lv_coord_t py, int16_t angle,
lv_draw_mask_line_side_t side);
/**
* Initialize an angle mask.
* @param param pointer to a `lv_draw_mask_param_t` to initialize
* @param vertex_x X coordinate of the angle vertex (absolute coordinates)
* @param vertex_y Y coordinate of the angle vertex (absolute coordinates)
* @param start_angle start angle in degrees. 0 deg on the right, 90 deg, on the bottom
* @param end_angle end angle
*/
void lv_draw_mask_angle_init(lv_draw_mask_angle_param_t * param, lv_coord_t vertex_x, lv_coord_t vertex_y,
lv_coord_t start_angle, lv_coord_t end_angle);
/**
* Initialize a fade mask.
* @param param pointer to an `lv_draw_mask_radius_param_t` to initialize
* @param rect coordinates of the rectangle to affect (absolute coordinates)
* @param radius radius of the rectangle
* @param inv true: keep the pixels inside the rectangle; keep the pixels outside of the rectangle
*/
void lv_draw_mask_radius_init(lv_draw_mask_radius_param_t * param, const lv_area_t * rect, lv_coord_t radius, bool inv);
/**
* Initialize a fade mask.
* @param param pointer to a `lv_draw_mask_param_t` to initialize
* @param coords coordinates of the area to affect (absolute coordinates)
* @param opa_top opacity on the top
* @param y_top at which coordinate start to change to opacity to `opa_bottom`
* @param opa_bottom opacity at the bottom
* @param y_bottom at which coordinate reach `opa_bottom`.
*/
void lv_draw_mask_fade_init(lv_draw_mask_fade_param_t * param, const lv_area_t * coords, lv_opa_t opa_top,
lv_coord_t y_top,
lv_opa_t opa_bottom, lv_coord_t y_bottom);
/**
* Initialize a map mask.
* @param param pointer to a `lv_draw_mask_param_t` to initialize
* @param coords coordinates of the map (absolute coordinates)
* @param map array of bytes with the mask values
*/
void lv_draw_mask_map_init(lv_draw_mask_map_param_t * param, const lv_area_t * coords, const lv_opa_t * map);
void lv_draw_mask_polygon_init(lv_draw_mask_polygon_param_t * param, const lv_point_t * points, uint16_t point_cnt);
#endif /*LV_DRAW_COMPLEX*/
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_DRAW_MASK_H*/

View File

@@ -0,0 +1,73 @@
/**
* @file lv_draw_rect.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_draw.h"
#include "lv_draw_rect.h"
#include "../misc/lv_assert.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
LV_ATTRIBUTE_FAST_MEM void lv_draw_rect_dsc_init(lv_draw_rect_dsc_t * dsc)
{
lv_memset_00(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_img_symbol_font = LV_FONT_DEFAULT;
dsc->bg_opa = LV_OPA_COVER;
dsc->bg_img_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;
}
/**
* Draw a rectangle
* @param coords the coordinates of the rectangle
* @param mask the rectangle will be drawn only in this mask
* @param dsc pointer to an initialized `lv_draw_rect_dsc_t` variable
*/
void lv_draw_rect(lv_draw_ctx_t * draw_ctx, const lv_draw_rect_dsc_t * dsc, const lv_area_t * coords)
{
if(lv_area_get_height(coords) < 1 || lv_area_get_width(coords) < 1) return;
draw_ctx->draw_rect(draw_ctx, dsc, coords);
LV_ASSERT_MEM_INTEGRITY();
}
/**********************
* STATIC FUNCTIONS
**********************/

View File

@@ -0,0 +1,96 @@
/**
* @file lv_draw_rect.h
*
*/
#ifndef LV_DRAW_RECT_H
#define LV_DRAW_RECT_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"
#include "sw/lv_draw_sw_gradient.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_coord_t radius;
lv_blend_mode_t blend_mode;
/*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_img_src;
const void * bg_img_symbol_font;
lv_color_t bg_img_recolor;
lv_opa_t bg_img_opa;
lv_opa_t bg_img_recolor_opa;
uint8_t bg_img_tiled;
/*Border*/
lv_color_t border_color;
lv_coord_t border_width;
lv_opa_t border_opa;
uint8_t border_post : 1; /*There is a border it will be drawn later.*/
lv_border_side_t border_side : 5;
/*Outline*/
lv_color_t outline_color;
lv_coord_t outline_width;
lv_coord_t outline_pad;
lv_opa_t outline_opa;
/*Shadow*/
lv_color_t shadow_color;
lv_coord_t shadow_width;
lv_coord_t shadow_ofs_x;
lv_coord_t shadow_ofs_y;
lv_coord_t shadow_spread;
lv_opa_t shadow_opa;
} lv_draw_rect_dsc_t;
struct _lv_draw_ctx_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
LV_ATTRIBUTE_FAST_MEM void lv_draw_rect_dsc_init(lv_draw_rect_dsc_t * dsc);
/**
* Draw a rectangle
* @param coords the coordinates of the rectangle
* @param clip the rectangle will be drawn only in this area
* @param dsc pointer to an initialized `lv_draw_rect_dsc_t` variable
*/
void lv_draw_rect(struct _lv_draw_ctx_t * draw_ctx, const lv_draw_rect_dsc_t * dsc, const lv_area_t * coords);
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_DRAW_RECT_H*/

View File

@@ -0,0 +1,52 @@
/**
* @file lv_draw_triangle.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_draw.h"
#include "lv_draw_triangle.h"
#include "../misc/lv_math.h"
#include "../misc/lv_mem.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
void lv_draw_polygon(struct _lv_draw_ctx_t * draw_ctx, const lv_draw_rect_dsc_t * draw_dsc, const lv_point_t points[],
uint16_t point_cnt)
{
draw_ctx->draw_polygon(draw_ctx, draw_dsc, points, point_cnt);
}
void lv_draw_triangle(struct _lv_draw_ctx_t * draw_ctx, const lv_draw_rect_dsc_t * draw_dsc, const lv_point_t points[])
{
draw_ctx->draw_polygon(draw_ctx, draw_dsc, points, 3);
}
/**********************
* STATIC FUNCTIONS
**********************/

View File

@@ -0,0 +1,42 @@
/**
* @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
**********************/
/**********************
* GLOBAL PROTOTYPES
**********************/
void lv_draw_polygon(struct _lv_draw_ctx_t * draw_ctx, const lv_draw_rect_dsc_t * draw_dsc, const lv_point_t points[],
uint16_t point_cnt);
void lv_draw_triangle(struct _lv_draw_ctx_t * draw_ctx, const lv_draw_rect_dsc_t * draw_dsc, const lv_point_t points[]);
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_DRAW_TRIANGLE_H*/

View File

@@ -0,0 +1,772 @@
/**
* @file lv_img_buf.c
*
*/
/*********************
* INCLUDES
*********************/
#include <stddef.h>
#include <string.h>
#include "lv_img_buf.h"
#include "lv_draw_img.h"
#include "../misc/lv_math.h"
#include "../misc/lv_log.h"
#include "../misc/lv_mem.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
/**
* Get the color of an image's pixel
* @param dsc an image descriptor
* @param x x coordinate of the point to get
* @param y x coordinate of the point to get
* @param color the color of the image. In case of `LV_IMG_CF_ALPHA_1/2/4/8` this color is used.
* Not used in other cases.
* @param safe true: check out of bounds
* @return color of the point
*/
lv_color_t lv_img_buf_get_px_color(lv_img_dsc_t * dsc, lv_coord_t x, lv_coord_t y, lv_color_t color)
{
lv_color_t p_color = lv_color_black();
uint8_t * buf_u8 = (uint8_t *)dsc->data;
if(dsc->header.cf == LV_IMG_CF_TRUE_COLOR || dsc->header.cf == LV_IMG_CF_TRUE_COLOR_CHROMA_KEYED ||
dsc->header.cf == LV_IMG_CF_TRUE_COLOR_ALPHA) {
uint8_t px_size = lv_img_cf_get_px_size(dsc->header.cf) >> 3;
uint32_t px = dsc->header.w * y * px_size + x * px_size;
lv_memcpy_small(&p_color, &buf_u8[px], sizeof(lv_color_t));
#if LV_COLOR_SIZE == 32
p_color.ch.alpha = 0xFF; /*Only the color should be get so use a default alpha value*/
#endif
}
else if(dsc->header.cf == LV_IMG_CF_INDEXED_1BIT) {
buf_u8 += 4 * 2;
uint8_t bit = x & 0x7;
x = x >> 3;
/*Get the current pixel.
*dsc->header.w + 7 means rounding up to 8 because the lines are byte aligned
*so the possible real width are 8, 16, 24 ...*/
uint32_t px = ((dsc->header.w + 7) >> 3) * y + x;
p_color.full = (buf_u8[px] & (1 << (7 - bit))) >> (7 - bit);
}
else if(dsc->header.cf == LV_IMG_CF_INDEXED_2BIT) {
buf_u8 += 4 * 4;
uint8_t bit = (x & 0x3) * 2;
x = x >> 2;
/*Get the current pixel.
*dsc->header.w + 3 means rounding up to 4 because the lines are byte aligned
*so the possible real width are 4, 8, 12 ...*/
uint32_t px = ((dsc->header.w + 3) >> 2) * y + x;
p_color.full = (buf_u8[px] & (3 << (6 - bit))) >> (6 - bit);
}
else if(dsc->header.cf == LV_IMG_CF_INDEXED_4BIT) {
buf_u8 += 4 * 16;
uint8_t bit = (x & 0x1) * 4;
x = x >> 1;
/*Get the current pixel.
*dsc->header.w + 1 means rounding up to 2 because the lines are byte aligned
*so the possible real width are 2, 4, 6 ...*/
uint32_t px = ((dsc->header.w + 1) >> 1) * y + x;
p_color.full = (buf_u8[px] & (0xF << (4 - bit))) >> (4 - bit);
}
else if(dsc->header.cf == LV_IMG_CF_INDEXED_8BIT) {
buf_u8 += 4 * 256;
uint32_t px = dsc->header.w * y + x;
p_color.full = buf_u8[px];
}
else if(dsc->header.cf == LV_IMG_CF_ALPHA_1BIT || dsc->header.cf == LV_IMG_CF_ALPHA_2BIT ||
dsc->header.cf == LV_IMG_CF_ALPHA_4BIT || dsc->header.cf == LV_IMG_CF_ALPHA_8BIT) {
p_color = color;
}
return p_color;
}
/**
* Get the alpha value of an image's pixel
* @param dsc pointer to an image descriptor
* @param x x coordinate of the point to set
* @param y x coordinate of the point to set
* @param safe true: check out of bounds
* @return alpha value of the point
*/
lv_opa_t lv_img_buf_get_px_alpha(lv_img_dsc_t * dsc, lv_coord_t x, lv_coord_t y)
{
uint8_t * buf_u8 = (uint8_t *)dsc->data;
if(dsc->header.cf == LV_IMG_CF_TRUE_COLOR_ALPHA) {
uint32_t px = dsc->header.w * y * LV_IMG_PX_SIZE_ALPHA_BYTE + x * LV_IMG_PX_SIZE_ALPHA_BYTE;
return buf_u8[px + LV_IMG_PX_SIZE_ALPHA_BYTE - 1];
}
else if(dsc->header.cf == LV_IMG_CF_ALPHA_1BIT) {
uint8_t bit = x & 0x7;
x = x >> 3;
/*Get the current pixel.
*dsc->header.w + 7 means rounding up to 8 because the lines are byte aligned
*so the possible real width are 8 ,16, 24 ...*/
uint32_t px = ((dsc->header.w + 7) >> 3) * y + x;
uint8_t px_opa = (buf_u8[px] & (1 << (7 - bit))) >> (7 - bit);
return px_opa ? LV_OPA_TRANSP : LV_OPA_COVER;
}
else if(dsc->header.cf == LV_IMG_CF_ALPHA_2BIT) {
const uint8_t opa_table[4] = {0, 85, 170, 255}; /*Opacity mapping with bpp = 2*/
uint8_t bit = (x & 0x3) * 2;
x = x >> 2;
/*Get the current pixel.
*dsc->header.w + 4 means rounding up to 8 because the lines are byte aligned
*so the possible real width are 4 ,8, 12 ...*/
uint32_t px = ((dsc->header.w + 3) >> 2) * y + x;
uint8_t px_opa = (buf_u8[px] & (3 << (6 - bit))) >> (6 - bit);
return opa_table[px_opa];
}
else if(dsc->header.cf == LV_IMG_CF_ALPHA_4BIT) {
const uint8_t opa_table[16] = {0, 17, 34, 51, /*Opacity mapping with bpp = 4*/
68, 85, 102, 119, 136, 153, 170, 187, 204, 221, 238, 255
};
uint8_t bit = (x & 0x1) * 4;
x = x >> 1;
/*Get the current pixel.
*dsc->header.w + 1 means rounding up to 8 because the lines are byte aligned
*so the possible real width are 2 ,4, 6 ...*/
uint32_t px = ((dsc->header.w + 1) >> 1) * y + x;
uint8_t px_opa = (buf_u8[px] & (0xF << (4 - bit))) >> (4 - bit);
return opa_table[px_opa];
}
else if(dsc->header.cf == LV_IMG_CF_ALPHA_8BIT) {
uint32_t px = dsc->header.w * y + x;
return buf_u8[px];
}
return LV_OPA_COVER;
}
/**
* Set the alpha value of a pixel of an image. The color won't be affected
* @param dsc pointer to an image descriptor
* @param x x coordinate of the point to set
* @param y x coordinate of the point to set
* @param opa the desired opacity
* @param safe true: check out of bounds
*/
void lv_img_buf_set_px_alpha(lv_img_dsc_t * dsc, lv_coord_t x, lv_coord_t y, lv_opa_t opa)
{
uint8_t * buf_u8 = (uint8_t *)dsc->data;
if(dsc->header.cf == LV_IMG_CF_TRUE_COLOR_ALPHA) {
uint8_t px_size = lv_img_cf_get_px_size(dsc->header.cf) >> 3;
uint32_t px = dsc->header.w * y * px_size + x * px_size;
buf_u8[px + px_size - 1] = opa;
}
else if(dsc->header.cf == LV_IMG_CF_ALPHA_1BIT) {
opa = opa >> 7; /*opa -> [0,1]*/
uint8_t bit = x & 0x7;
x = x >> 3;
/*Get the current pixel.
*dsc->header.w + 7 means rounding up to 8 because the lines are byte aligned
*so the possible real width are 8 ,16, 24 ...*/
uint32_t px = ((dsc->header.w + 7) >> 3) * y + x;
buf_u8[px] = buf_u8[px] & ~(1 << (7 - bit));
buf_u8[px] = buf_u8[px] | ((opa & 0x1) << (7 - bit));
}
else if(dsc->header.cf == LV_IMG_CF_ALPHA_2BIT) {
opa = opa >> 6; /*opa -> [0,3]*/
uint8_t bit = (x & 0x3) * 2;
x = x >> 2;
/*Get the current pixel.
*dsc->header.w + 4 means rounding up to 8 because the lines are byte aligned
*so the possible real width are 4 ,8, 12 ...*/
uint32_t px = ((dsc->header.w + 3) >> 2) * y + x;
buf_u8[px] = buf_u8[px] & ~(3 << (6 - bit));
buf_u8[px] = buf_u8[px] | ((opa & 0x3) << (6 - bit));
}
else if(dsc->header.cf == LV_IMG_CF_ALPHA_4BIT) {
opa = opa >> 4; /*opa -> [0,15]*/
uint8_t bit = (x & 0x1) * 4;
x = x >> 1;
/*Get the current pixel.
*dsc->header.w + 1 means rounding up to 8 because the lines are byte aligned
*so the possible real width are 2 ,4, 6 ...*/
uint32_t px = ((dsc->header.w + 1) >> 1) * y + x;
buf_u8[px] = buf_u8[px] & ~(0xF << (4 - bit));
buf_u8[px] = buf_u8[px] | ((opa & 0xF) << (4 - bit));
}
else if(dsc->header.cf == LV_IMG_CF_ALPHA_8BIT) {
uint32_t px = dsc->header.w * y + x;
buf_u8[px] = opa;
}
}
/**
* Set the color of a pixel of an image. The alpha channel won't be affected.
* @param dsc pointer to an image descriptor
* @param x x coordinate of the point to set
* @param y x coordinate of the point to set
* @param c color of the point
* @param safe true: check out of bounds
*/
void lv_img_buf_set_px_color(lv_img_dsc_t * dsc, lv_coord_t x, lv_coord_t y, lv_color_t c)
{
uint8_t * buf_u8 = (uint8_t *)dsc->data;
if(dsc->header.cf == LV_IMG_CF_TRUE_COLOR || dsc->header.cf == LV_IMG_CF_TRUE_COLOR_CHROMA_KEYED) {
uint8_t px_size = lv_img_cf_get_px_size(dsc->header.cf) >> 3;
uint32_t px = dsc->header.w * y * px_size + x * px_size;
lv_memcpy_small(&buf_u8[px], &c, px_size);
}
else if(dsc->header.cf == LV_IMG_CF_TRUE_COLOR_ALPHA) {
uint8_t px_size = lv_img_cf_get_px_size(dsc->header.cf) >> 3;
uint32_t px = dsc->header.w * y * px_size + x * px_size;
lv_memcpy_small(&buf_u8[px], &c, px_size - 1); /*-1 to not overwrite the alpha value*/
}
else if(dsc->header.cf == LV_IMG_CF_INDEXED_1BIT) {
buf_u8 += sizeof(lv_color32_t) * 2; /*Skip the palette*/
uint8_t bit = x & 0x7;
x = x >> 3;
/*Get the current pixel.
*dsc->header.w + 7 means rounding up to 8 because the lines are byte aligned
*so the possible real width are 8 ,16, 24 ...*/
uint32_t px = ((dsc->header.w + 7) >> 3) * y + x;
buf_u8[px] = buf_u8[px] & ~(1 << (7 - bit));
buf_u8[px] = buf_u8[px] | ((c.full & 0x1) << (7 - bit));
}
else if(dsc->header.cf == LV_IMG_CF_INDEXED_2BIT) {
buf_u8 += sizeof(lv_color32_t) * 4; /*Skip the palette*/
uint8_t bit = (x & 0x3) * 2;
x = x >> 2;
/*Get the current pixel.
*dsc->header.w + 3 means rounding up to 4 because the lines are byte aligned
*so the possible real width are 4, 8 ,12 ...*/
uint32_t px = ((dsc->header.w + 3) >> 2) * y + x;
buf_u8[px] = buf_u8[px] & ~(3 << (6 - bit));
buf_u8[px] = buf_u8[px] | ((c.full & 0x3) << (6 - bit));
}
else if(dsc->header.cf == LV_IMG_CF_INDEXED_4BIT) {
buf_u8 += sizeof(lv_color32_t) * 16; /*Skip the palette*/
uint8_t bit = (x & 0x1) * 4;
x = x >> 1;
/*Get the current pixel.
*dsc->header.w + 1 means rounding up to 2 because the lines are byte aligned
*so the possible real width are 2 ,4, 6 ...*/
uint32_t px = ((dsc->header.w + 1) >> 1) * y + x;
buf_u8[px] = buf_u8[px] & ~(0xF << (4 - bit));
buf_u8[px] = buf_u8[px] | ((c.full & 0xF) << (4 - bit));
}
else if(dsc->header.cf == LV_IMG_CF_INDEXED_8BIT) {
buf_u8 += sizeof(lv_color32_t) * 256; /*Skip the palette*/
uint32_t px = dsc->header.w * y + x;
buf_u8[px] = c.full;
}
}
/**
* Set the palette color of an indexed image. Valid only for `LV_IMG_CF_INDEXED1/2/4/8`
* @param dsc pointer to an image descriptor
* @param id the palette color to set:
* - for `LV_IMG_CF_INDEXED1`: 0..1
* - for `LV_IMG_CF_INDEXED2`: 0..3
* - for `LV_IMG_CF_INDEXED4`: 0..15
* - for `LV_IMG_CF_INDEXED8`: 0..255
* @param c the color to set
*/
void lv_img_buf_set_palette(lv_img_dsc_t * dsc, uint8_t id, lv_color_t c)
{
if((dsc->header.cf == LV_IMG_CF_ALPHA_1BIT && id > 1) || (dsc->header.cf == LV_IMG_CF_ALPHA_2BIT && id > 3) ||
(dsc->header.cf == LV_IMG_CF_ALPHA_4BIT && id > 15) || (dsc->header.cf == LV_IMG_CF_ALPHA_8BIT)) {
LV_LOG_WARN("lv_img_buf_set_px_alpha: invalid 'id'");
return;
}
lv_color32_t c32;
c32.full = lv_color_to32(c);
uint8_t * buf = (uint8_t *)dsc->data;
lv_memcpy_small(&buf[id * sizeof(c32)], &c32, sizeof(c32));
}
/**
* Allocate an image buffer in RAM
* @param w width of image
* @param h height of image
* @param cf a color format (`LV_IMG_CF_...`)
* @return an allocated image, or NULL on failure
*/
lv_img_dsc_t * lv_img_buf_alloc(lv_coord_t w, lv_coord_t h, lv_img_cf_t cf)
{
/*Allocate image descriptor*/
lv_img_dsc_t * dsc = lv_mem_alloc(sizeof(lv_img_dsc_t));
if(dsc == NULL)
return NULL;
lv_memset_00(dsc, sizeof(lv_img_dsc_t));
/*Get image data size*/
dsc->data_size = lv_img_buf_get_img_size(w, h, cf);
if(dsc->data_size == 0) {
lv_mem_free(dsc);
return NULL;
}
/*Allocate raw buffer*/
dsc->data = lv_mem_alloc(dsc->data_size);
if(dsc->data == NULL) {
lv_mem_free(dsc);
return NULL;
}
lv_memset_00((uint8_t *)dsc->data, dsc->data_size);
/*Fill in header*/
dsc->header.always_zero = 0;
dsc->header.w = w;
dsc->header.h = h;
dsc->header.cf = cf;
return dsc;
}
/**
* Free an allocated image buffer
* @param dsc image buffer to free
*/
void lv_img_buf_free(lv_img_dsc_t * dsc)
{
if(dsc != NULL) {
if(dsc->data != NULL)
lv_mem_free((void *)dsc->data);
lv_mem_free(dsc);
}
}
/**
* Get the memory consumption of a raw bitmap, given color format and dimensions.
* @param w width
* @param h height
* @param cf color format
* @return size in bytes
*/
uint32_t lv_img_buf_get_img_size(lv_coord_t w, lv_coord_t h, lv_img_cf_t cf)
{
switch(cf) {
case LV_IMG_CF_TRUE_COLOR:
return LV_IMG_BUF_SIZE_TRUE_COLOR(w, h);
case LV_IMG_CF_TRUE_COLOR_ALPHA:
return LV_IMG_BUF_SIZE_TRUE_COLOR_ALPHA(w, h);
case LV_IMG_CF_TRUE_COLOR_CHROMA_KEYED:
return LV_IMG_BUF_SIZE_TRUE_COLOR_CHROMA_KEYED(w, h);
case LV_IMG_CF_ALPHA_1BIT:
return LV_IMG_BUF_SIZE_ALPHA_1BIT(w, h);
case LV_IMG_CF_ALPHA_2BIT:
return LV_IMG_BUF_SIZE_ALPHA_2BIT(w, h);
case LV_IMG_CF_ALPHA_4BIT:
return LV_IMG_BUF_SIZE_ALPHA_4BIT(w, h);
case LV_IMG_CF_ALPHA_8BIT:
return LV_IMG_BUF_SIZE_ALPHA_8BIT(w, h);
case LV_IMG_CF_INDEXED_1BIT:
return LV_IMG_BUF_SIZE_INDEXED_1BIT(w, h);
case LV_IMG_CF_INDEXED_2BIT:
return LV_IMG_BUF_SIZE_INDEXED_2BIT(w, h);
case LV_IMG_CF_INDEXED_4BIT:
return LV_IMG_BUF_SIZE_INDEXED_4BIT(w, h);
case LV_IMG_CF_INDEXED_8BIT:
return LV_IMG_BUF_SIZE_INDEXED_8BIT(w, h);
default:
return 0;
}
}
#if LV_DRAW_COMPLEX
/**
* Initialize a descriptor to transform an image
* @param dsc pointer to an `lv_img_transform_dsc_t` variable whose `cfg` field is initialized
*/
void _lv_img_buf_transform_init(lv_img_transform_dsc_t * dsc)
{
dsc->tmp.pivot_x_256 = dsc->cfg.pivot_x * 256;
dsc->tmp.pivot_y_256 = dsc->cfg.pivot_y * 256;
int32_t angle_low = dsc->cfg.angle / 10;
int32_t angle_high = angle_low + 1;
int32_t angle_rem = dsc->cfg.angle - (angle_low * 10);
int32_t s1 = lv_trigo_sin(-angle_low);
int32_t s2 = lv_trigo_sin(-angle_high);
int32_t c1 = lv_trigo_sin(-angle_low + 90);
int32_t c2 = lv_trigo_sin(-angle_high + 90);
dsc->tmp.sinma = (s1 * (10 - angle_rem) + s2 * angle_rem) / 10;
dsc->tmp.cosma = (c1 * (10 - angle_rem) + c2 * angle_rem) / 10;
/*Use smaller value to avoid overflow*/
dsc->tmp.sinma = dsc->tmp.sinma >> (LV_TRIGO_SHIFT - _LV_TRANSFORM_TRIGO_SHIFT);
dsc->tmp.cosma = dsc->tmp.cosma >> (LV_TRIGO_SHIFT - _LV_TRANSFORM_TRIGO_SHIFT);
dsc->tmp.chroma_keyed = lv_img_cf_is_chroma_keyed(dsc->cfg.cf) ? 1 : 0;
dsc->tmp.has_alpha = lv_img_cf_has_alpha(dsc->cfg.cf) ? 1 : 0;
if(dsc->cfg.cf == LV_IMG_CF_TRUE_COLOR || dsc->cfg.cf == LV_IMG_CF_TRUE_COLOR_ALPHA ||
dsc->cfg.cf == LV_IMG_CF_TRUE_COLOR_CHROMA_KEYED) {
dsc->tmp.native_color = 1;
}
else {
dsc->tmp.native_color = 0;
}
dsc->tmp.img_dsc.data = dsc->cfg.src;
dsc->tmp.img_dsc.header.always_zero = 0;
dsc->tmp.img_dsc.header.cf = dsc->cfg.cf;
dsc->tmp.img_dsc.header.w = dsc->cfg.src_w;
dsc->tmp.img_dsc.header.h = dsc->cfg.src_h;
/*The inverse of the zoom will be sued during the transformation
* + dsc->cfg.zoom / 2 for rounding*/
dsc->tmp.zoom_inv = (((256 * 256) << _LV_ZOOM_INV_UPSCALE) + dsc->cfg.zoom / 2) / dsc->cfg.zoom;
dsc->res.opa = LV_OPA_COVER;
dsc->res.color = dsc->cfg.color;
}
#endif
/**
* 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 zoom zoom, (256 no zoom)
* @param pivot x,y pivot coordinates of rotation
*/
void _lv_img_buf_get_transformed_area(lv_area_t * res, lv_coord_t w, lv_coord_t h, int16_t angle, uint16_t zoom,
const lv_point_t * pivot)
{
#if LV_DRAW_COMPLEX
if(angle == 0 && zoom == LV_IMG_ZOOM_NONE) {
res->x1 = 0;
res->y1 = 0;
res->x2 = w - 1;
res->y2 = h - 1;
return;
}
res->x1 = (((int32_t)(-pivot->x) * zoom) >> 8) - 1;
res->y1 = (((int32_t)(-pivot->y) * zoom) >> 8) - 1;
res->x2 = (((int32_t)(w - pivot->x) * zoom) >> 8) + 2;
res->y2 = (((int32_t)(h - pivot->y) * zoom) >> 8) + 2;
if(angle == 0) {
res->x1 += pivot->x;
res->y1 += pivot->y;
res->x2 += pivot->x;
res->y2 += pivot->y;
return;
}
int32_t angle_low = angle / 10;
int32_t angle_high = angle_low + 1;
int32_t angle_rem = angle - (angle_low * 10);
int32_t s1 = lv_trigo_sin(angle_low);
int32_t s2 = lv_trigo_sin(angle_high);
int32_t c1 = lv_trigo_sin(angle_low + 90);
int32_t c2 = lv_trigo_sin(angle_high + 90);
int32_t sinma = (s1 * (10 - angle_rem) + s2 * angle_rem) / 10;
int32_t cosma = (c1 * (10 - angle_rem) + c2 * angle_rem) / 10;
/*Use smaller value to avoid overflow*/
sinma = sinma >> (LV_TRIGO_SHIFT - _LV_TRANSFORM_TRIGO_SHIFT);
cosma = cosma >> (LV_TRIGO_SHIFT - _LV_TRANSFORM_TRIGO_SHIFT);
lv_point_t lt;
lv_point_t rt;
lv_point_t lb;
lv_point_t rb;
lv_coord_t xt;
lv_coord_t yt;
xt = res->x1;
yt = res->y1;
lt.x = ((cosma * xt - sinma * yt) >> _LV_TRANSFORM_TRIGO_SHIFT) + pivot->x;
lt.y = ((sinma * xt + cosma * yt) >> _LV_TRANSFORM_TRIGO_SHIFT) + pivot->y;
xt = res->x2;
yt = res->y1;
rt.x = ((cosma * xt - sinma * yt) >> _LV_TRANSFORM_TRIGO_SHIFT) + pivot->x;
rt.y = ((sinma * xt + cosma * yt) >> _LV_TRANSFORM_TRIGO_SHIFT) + pivot->y;
xt = res->x1;
yt = res->y2;
lb.x = ((cosma * xt - sinma * yt) >> _LV_TRANSFORM_TRIGO_SHIFT) + pivot->x;
lb.y = ((sinma * xt + cosma * yt) >> _LV_TRANSFORM_TRIGO_SHIFT) + pivot->y;
xt = res->x2;
yt = res->y2;
rb.x = ((cosma * xt - sinma * yt) >> _LV_TRANSFORM_TRIGO_SHIFT) + pivot->x;
rb.y = ((sinma * xt + cosma * yt) >> _LV_TRANSFORM_TRIGO_SHIFT) + pivot->y;
res->x1 = LV_MIN4(lb.x, lt.x, rb.x, rt.x);
res->x2 = LV_MAX4(lb.x, lt.x, rb.x, rt.x);
res->y1 = LV_MIN4(lb.y, lt.y, rb.y, rt.y);
res->y2 = LV_MAX4(lb.y, lt.y, rb.y, rt.y);
#else
LV_UNUSED(angle);
LV_UNUSED(zoom);
LV_UNUSED(pivot);
res->x1 = 0;
res->y1 = 0;
res->x2 = w - 1;
res->y2 = h - 1;
#endif
}
#if LV_DRAW_COMPLEX
/**
* Get which color and opa would come to a pixel if it were rotated
* @param dsc a descriptor initialized by `lv_img_buf_rotate_init`
* @param x the coordinate which color and opa should be get
* @param y the coordinate which color and opa should be get
* @return true: there is valid pixel on these x/y coordinates; false: the rotated pixel was out of the image
* @note the result is written back to `dsc->res_color` and `dsc->res_opa`
*/
bool _lv_img_buf_transform(lv_img_transform_dsc_t * dsc, lv_coord_t x, lv_coord_t y)
{
const uint8_t * src_u8 = (const uint8_t *)dsc->cfg.src;
/*Get the target point relative coordinates to the pivot*/
int32_t xt = x - dsc->cfg.pivot_x;
int32_t yt = y - dsc->cfg.pivot_y;
int32_t xs;
int32_t ys;
if(dsc->cfg.zoom == LV_IMG_ZOOM_NONE) {
/*Get the source pixel from the upscaled image*/
xs = ((dsc->tmp.cosma * xt - dsc->tmp.sinma * yt) >> (_LV_TRANSFORM_TRIGO_SHIFT - 8)) + dsc->tmp.pivot_x_256;
ys = ((dsc->tmp.sinma * xt + dsc->tmp.cosma * yt) >> (_LV_TRANSFORM_TRIGO_SHIFT - 8)) + dsc->tmp.pivot_y_256;
}
else if(dsc->cfg.angle == 0) {
xt = (int32_t)((int32_t)xt * dsc->tmp.zoom_inv) >> _LV_ZOOM_INV_UPSCALE;
yt = (int32_t)((int32_t)yt * dsc->tmp.zoom_inv) >> _LV_ZOOM_INV_UPSCALE;
xs = xt + dsc->tmp.pivot_x_256;
ys = yt + dsc->tmp.pivot_y_256;
}
else {
xt = (int32_t)((int32_t)xt * dsc->tmp.zoom_inv) >> _LV_ZOOM_INV_UPSCALE;
yt = (int32_t)((int32_t)yt * dsc->tmp.zoom_inv) >> _LV_ZOOM_INV_UPSCALE;
xs = ((dsc->tmp.cosma * xt - dsc->tmp.sinma * yt) >> (_LV_TRANSFORM_TRIGO_SHIFT)) + dsc->tmp.pivot_x_256;
ys = ((dsc->tmp.sinma * xt + dsc->tmp.cosma * yt) >> (_LV_TRANSFORM_TRIGO_SHIFT)) + dsc->tmp.pivot_y_256;
}
/*Get the integer part of the source pixel*/
int32_t xs_int = xs >> 8;
int32_t ys_int = ys >> 8;
if(xs_int >= dsc->cfg.src_w) return false;
else if(xs_int < 0) return false;
if(ys_int >= dsc->cfg.src_h) return false;
else if(ys_int < 0) return false;
uint8_t px_size;
uint32_t pxi;
if(dsc->tmp.native_color) {
if(dsc->tmp.has_alpha == 0) {
px_size = LV_COLOR_SIZE >> 3;
pxi = dsc->cfg.src_w * ys_int * px_size + xs_int * px_size;
lv_memcpy_small(&dsc->res.color, &src_u8[pxi], px_size);
}
else {
px_size = LV_IMG_PX_SIZE_ALPHA_BYTE;
pxi = dsc->cfg.src_w * ys_int * px_size + xs_int * px_size;
lv_memcpy_small(&dsc->res.color, &src_u8[pxi], px_size - 1);
dsc->res.opa = src_u8[pxi + px_size - 1];
}
}
else {
pxi = 0; /*unused*/
px_size = 0; /*unused*/
dsc->res.color = lv_img_buf_get_px_color(&dsc->tmp.img_dsc, xs_int, ys_int, dsc->cfg.color);
dsc->res.opa = lv_img_buf_get_px_alpha(&dsc->tmp.img_dsc, xs_int, ys_int);
}
if(dsc->tmp.chroma_keyed) {
lv_color_t ct = LV_COLOR_CHROMA_KEY;
if(dsc->res.color.full == ct.full) return false;
}
if(dsc->cfg.antialias == false) return true;
dsc->tmp.xs = xs;
dsc->tmp.ys = ys;
dsc->tmp.xs_int = xs_int;
dsc->tmp.ys_int = ys_int;
dsc->tmp.pxi = pxi;
dsc->tmp.px_size = px_size;
bool ret;
ret = _lv_img_buf_transform_anti_alias(dsc);
return ret;
}
/**
* Continue transformation by taking the neighbors into account
* @param dsc pointer to the transformation descriptor
*/
bool _lv_img_buf_transform_anti_alias(lv_img_transform_dsc_t * dsc)
{
const uint8_t * src_u8 = dsc->cfg.src;
/*Get the fractional part of the source pixel*/
int xs_fract = dsc->tmp.xs & 0xff;
int ys_fract = dsc->tmp.ys & 0xff;
int32_t xn; /*x neighbor*/
lv_opa_t xr; /*x mix ratio*/
if(xs_fract < 0x70) {
xn = - 1;
if(dsc->tmp.xs_int + xn < 0) xn = 0;
xr = xs_fract + 0x80;
}
else if(xs_fract > 0x90) {
xn = 1;
if(dsc->tmp.xs_int + xn >= dsc->cfg.src_w) xn = 0;
xr = (0xFF - xs_fract) + 0x80;
}
else {
xn = 0;
xr = 0xFF;
}
int32_t yn; /*x neighbor*/
lv_opa_t yr; /*x mix ratio*/
if(ys_fract < 0x70) {
yn = - 1;
if(dsc->tmp.ys_int + yn < 0) yn = 0;
yr = ys_fract + 0x80;
}
else if(ys_fract > 0x90) {
yn = 1;
if(dsc->tmp.ys_int + yn >= dsc->cfg.src_h) yn = 0;
yr = (0xFF - ys_fract) + 0x80;
}
else {
yn = 0;
yr = 0xFF;
}
lv_color_t c00 = dsc->res.color;
lv_color_t c01;
lv_color_t c10;
lv_color_t c11;
lv_opa_t a00 = dsc->res.opa;
lv_opa_t a10 = 0;
lv_opa_t a01 = 0;
lv_opa_t a11 = 0;
if(dsc->tmp.native_color) {
lv_memcpy_small(&c01, &src_u8[dsc->tmp.pxi + dsc->tmp.px_size * xn], sizeof(lv_color_t));
lv_memcpy_small(&c10, &src_u8[dsc->tmp.pxi + dsc->cfg.src_w * dsc->tmp.px_size * yn], sizeof(lv_color_t));
lv_memcpy_small(&c11, &src_u8[dsc->tmp.pxi + dsc->cfg.src_w * dsc->tmp.px_size * yn + dsc->tmp.px_size * xn],
sizeof(lv_color_t));
if(dsc->tmp.has_alpha) {
a10 = src_u8[dsc->tmp.pxi + dsc->tmp.px_size * xn + dsc->tmp.px_size - 1];
a01 = src_u8[dsc->tmp.pxi + dsc->cfg.src_w * dsc->tmp.px_size * yn + dsc->tmp.px_size - 1];
a11 = src_u8[dsc->tmp.pxi + dsc->cfg.src_w * dsc->tmp.px_size * yn + dsc->tmp.px_size * xn + dsc->tmp.px_size - 1];
}
}
else {
c01 = lv_img_buf_get_px_color(&dsc->tmp.img_dsc, dsc->tmp.xs_int + xn, dsc->tmp.ys_int, dsc->cfg.color);
c10 = lv_img_buf_get_px_color(&dsc->tmp.img_dsc, dsc->tmp.xs_int, dsc->tmp.ys_int + yn, dsc->cfg.color);
c11 = lv_img_buf_get_px_color(&dsc->tmp.img_dsc, dsc->tmp.xs_int + xn, dsc->tmp.ys_int + yn, dsc->cfg.color);
if(dsc->tmp.has_alpha) {
a10 = lv_img_buf_get_px_alpha(&dsc->tmp.img_dsc, dsc->tmp.xs_int + xn, dsc->tmp.ys_int);
a01 = lv_img_buf_get_px_alpha(&dsc->tmp.img_dsc, dsc->tmp.xs_int, dsc->tmp.ys_int + yn);
a11 = lv_img_buf_get_px_alpha(&dsc->tmp.img_dsc, dsc->tmp.xs_int + xn, dsc->tmp.ys_int + yn);
}
}
lv_opa_t xr0 = xr;
lv_opa_t xr1 = xr;
if(dsc->tmp.has_alpha) {
lv_opa_t a0 = (a00 * xr + (a10 * (255 - xr))) >> 8;
lv_opa_t a1 = (a01 * xr + (a11 * (255 - xr))) >> 8;
dsc->res.opa = (a0 * yr + (a1 * (255 - yr))) >> 8;
if(a0 <= LV_OPA_MIN && a1 <= LV_OPA_MIN) return false;
if(a0 <= LV_OPA_MIN) yr = LV_OPA_TRANSP;
if(a1 <= LV_OPA_MIN) yr = LV_OPA_COVER;
if(a00 <= LV_OPA_MIN) xr0 = LV_OPA_TRANSP;
if(a10 <= LV_OPA_MIN) xr0 = LV_OPA_COVER;
if(a01 <= LV_OPA_MIN) xr1 = LV_OPA_TRANSP;
if(a11 <= LV_OPA_MIN) xr1 = LV_OPA_COVER;
}
else {
xr0 = xr;
xr1 = xr;
dsc->res.opa = LV_OPA_COVER;
}
lv_color_t c0;
if(xr0 == LV_OPA_TRANSP) c0 = c01;
else if(xr0 == LV_OPA_COVER) c0 = c00;
else c0 = lv_color_mix(c00, c01, xr0);
lv_color_t c1;
if(xr1 == LV_OPA_TRANSP) c1 = c11;
else if(xr1 == LV_OPA_COVER) c1 = c10;
else c1 = lv_color_mix(c10, c11, xr1);
if(yr == LV_OPA_TRANSP) dsc->res.color = c1;
else if(yr == LV_OPA_COVER) dsc->res.color = c0;
else dsc->res.color = lv_color_mix(c0, c1, yr);
return true;
}
#endif
/**********************
* STATIC FUNCTIONS
**********************/

View File

@@ -0,0 +1,310 @@
/**
* @file lv_img_buf.h
*
*/
#ifndef LV_IMG_BUF_H
#define LV_IMG_BUF_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include <stdbool.h>
#include "../misc/lv_color.h"
#include "../misc/lv_area.h"
/*********************
* DEFINES
*********************/
/*If image pixels contains alpha we need to know how much byte is a pixel*/
#if LV_COLOR_DEPTH == 1 || LV_COLOR_DEPTH == 8
#define LV_IMG_PX_SIZE_ALPHA_BYTE 2
#elif LV_COLOR_DEPTH == 16
#define LV_IMG_PX_SIZE_ALPHA_BYTE 3
#elif LV_COLOR_DEPTH == 32
#define LV_IMG_PX_SIZE_ALPHA_BYTE 4
#endif
#define LV_IMG_BUF_SIZE_TRUE_COLOR(w, h) ((LV_COLOR_SIZE / 8) * w * h)
#define LV_IMG_BUF_SIZE_TRUE_COLOR_CHROMA_KEYED(w, h) ((LV_COLOR_SIZE / 8) * w * h)
#define LV_IMG_BUF_SIZE_TRUE_COLOR_ALPHA(w, h) (LV_IMG_PX_SIZE_ALPHA_BYTE * w * h)
/*+ 1: to be sure no fractional row*/
#define LV_IMG_BUF_SIZE_ALPHA_1BIT(w, h) ((((w / 8) + 1) * h))
#define LV_IMG_BUF_SIZE_ALPHA_2BIT(w, h) ((((w / 4) + 1) * h))
#define LV_IMG_BUF_SIZE_ALPHA_4BIT(w, h) ((((w / 2) + 1) * h))
#define LV_IMG_BUF_SIZE_ALPHA_8BIT(w, h) ((w * h))
/*4 * X: for palette*/
#define LV_IMG_BUF_SIZE_INDEXED_1BIT(w, h) (LV_IMG_BUF_SIZE_ALPHA_1BIT(w, h) + 4 * 2)
#define LV_IMG_BUF_SIZE_INDEXED_2BIT(w, h) (LV_IMG_BUF_SIZE_ALPHA_2BIT(w, h) + 4 * 4)
#define LV_IMG_BUF_SIZE_INDEXED_4BIT(w, h) (LV_IMG_BUF_SIZE_ALPHA_4BIT(w, h) + 4 * 16)
#define LV_IMG_BUF_SIZE_INDEXED_8BIT(w, h) (LV_IMG_BUF_SIZE_ALPHA_8BIT(w, h) + 4 * 256)
#define _LV_TRANSFORM_TRIGO_SHIFT 10
#define _LV_ZOOM_INV_UPSCALE 5
/**********************
* TYPEDEFS
**********************/
/*Image color format*/
enum {
LV_IMG_CF_UNKNOWN = 0,
LV_IMG_CF_RAW, /**< Contains the file as it is. Needs custom decoder function*/
LV_IMG_CF_RAW_ALPHA, /**< Contains the file as it is. The image has alpha. Needs custom decoder
function*/
LV_IMG_CF_RAW_CHROMA_KEYED, /**< Contains the file as it is. The image is chroma keyed. Needs
custom decoder function*/
LV_IMG_CF_TRUE_COLOR, /**< Color format and depth should match with LV_COLOR settings*/
LV_IMG_CF_TRUE_COLOR_ALPHA, /**< Same as `LV_IMG_CF_TRUE_COLOR` but every pixel has an alpha byte*/
LV_IMG_CF_TRUE_COLOR_CHROMA_KEYED, /**< Same as `LV_IMG_CF_TRUE_COLOR` but LV_COLOR_TRANSP pixels
will be transparent*/
LV_IMG_CF_INDEXED_1BIT, /**< Can have 2 different colors in a palette (can't be chroma keyed)*/
LV_IMG_CF_INDEXED_2BIT, /**< Can have 4 different colors in a palette (can't be chroma keyed)*/
LV_IMG_CF_INDEXED_4BIT, /**< Can have 16 different colors in a palette (can't be chroma keyed)*/
LV_IMG_CF_INDEXED_8BIT, /**< Can have 256 different colors in a palette (can't be chroma keyed)*/
LV_IMG_CF_ALPHA_1BIT, /**< Can have one color and it can be drawn or not*/
LV_IMG_CF_ALPHA_2BIT, /**< Can have one color but 4 different alpha value*/
LV_IMG_CF_ALPHA_4BIT, /**< Can have one color but 16 different alpha value*/
LV_IMG_CF_ALPHA_8BIT, /**< Can have one color but 256 different alpha value*/
LV_IMG_CF_RESERVED_15, /**< Reserved for further use.*/
LV_IMG_CF_RESERVED_16, /**< Reserved for further use.*/
LV_IMG_CF_RESERVED_17, /**< Reserved for further use.*/
LV_IMG_CF_RESERVED_18, /**< Reserved for further use.*/
LV_IMG_CF_RESERVED_19, /**< Reserved for further use.*/
LV_IMG_CF_RESERVED_20, /**< Reserved for further use.*/
LV_IMG_CF_RESERVED_21, /**< Reserved for further use.*/
LV_IMG_CF_RESERVED_22, /**< Reserved for further use.*/
LV_IMG_CF_RESERVED_23, /**< Reserved for further use.*/
LV_IMG_CF_USER_ENCODED_0, /**< User holder encoding format.*/
LV_IMG_CF_USER_ENCODED_1, /**< User holder encoding format.*/
LV_IMG_CF_USER_ENCODED_2, /**< User holder encoding format.*/
LV_IMG_CF_USER_ENCODED_3, /**< User holder encoding format.*/
LV_IMG_CF_USER_ENCODED_4, /**< User holder encoding format.*/
LV_IMG_CF_USER_ENCODED_5, /**< User holder encoding format.*/
LV_IMG_CF_USER_ENCODED_6, /**< User holder encoding format.*/
LV_IMG_CF_USER_ENCODED_7, /**< User holder encoding format.*/
};
typedef uint8_t lv_img_cf_t;
/**
* The first 8 bit is very important to distinguish the different source types.
* For more info see `lv_img_get_src_type()` in lv_img.c
* On big endian systems the order is reversed so cf and always_zero must be at
* the end of the struct.
*/
#if LV_BIG_ENDIAN_SYSTEM
typedef struct {
uint32_t h : 11; /*Height of the image map*/
uint32_t w : 11; /*Width of the image map*/
uint32_t reserved : 2; /*Reserved to be used later*/
uint32_t always_zero : 3; /*It the upper bits of the first byte. Always zero to look like a
non-printable character*/
uint32_t cf : 5; /*Color format: See `lv_img_color_format_t`*/
} lv_img_header_t;
#else
typedef struct {
uint32_t cf : 5; /*Color format: See `lv_img_color_format_t`*/
uint32_t always_zero : 3; /*It the upper bits of the first byte. Always zero to look like a
non-printable character*/
uint32_t reserved : 2; /*Reserved to be used later*/
uint32_t w : 11; /*Width of the image map*/
uint32_t h : 11; /*Height of the image map*/
} lv_img_header_t;
#endif
/** Image header it is compatible with
* the result from image converter utility*/
typedef struct {
lv_img_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*/
} lv_img_dsc_t;
typedef struct {
struct {
const void * src; /*image source (array of pixels)*/
lv_coord_t src_w; /*width of the image source*/
lv_coord_t src_h; /*height of the image source*/
lv_coord_t pivot_x; /*pivot x*/
lv_coord_t pivot_y; /*pivot y*/
int16_t angle; /*angle to rotate*/
uint16_t zoom; /*256 no zoom, 128 half size, 512 double size*/
lv_color_t color; /*a color used for `LV_IMG_CF_INDEXED_1/2/4/8BIT` color formats*/
lv_img_cf_t cf; /*color format of the image to rotate*/
bool antialias;
} cfg;
struct {
lv_color_t color;
lv_opa_t opa;
} res;
struct {
lv_img_dsc_t img_dsc;
int32_t pivot_x_256;
int32_t pivot_y_256;
int32_t sinma;
int32_t cosma;
uint8_t chroma_keyed : 1;
uint8_t has_alpha : 1;
uint8_t native_color : 1;
uint32_t zoom_inv;
/*Runtime data*/
lv_coord_t xs;
lv_coord_t ys;
lv_coord_t xs_int;
lv_coord_t ys_int;
uint32_t pxi;
uint8_t px_size;
} tmp;
} lv_img_transform_dsc_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Allocate an image buffer in RAM
* @param w width of image
* @param h height of image
* @param cf a color format (`LV_IMG_CF_...`)
* @return an allocated image, or NULL on failure
*/
lv_img_dsc_t * lv_img_buf_alloc(lv_coord_t w, lv_coord_t h, lv_img_cf_t cf);
/**
* Get the color of an image's pixel
* @param dsc an image descriptor
* @param x x coordinate of the point to get
* @param y x coordinate of the point to get
* @param color the color of the image. In case of `LV_IMG_CF_ALPHA_1/2/4/8` this color is used.
* Not used in other cases.
* @param safe true: check out of bounds
* @return color of the point
*/
lv_color_t lv_img_buf_get_px_color(lv_img_dsc_t * dsc, lv_coord_t x, lv_coord_t y, lv_color_t color);
/**
* Get the alpha value of an image's pixel
* @param dsc pointer to an image descriptor
* @param x x coordinate of the point to set
* @param y x coordinate of the point to set
* @param safe true: check out of bounds
* @return alpha value of the point
*/
lv_opa_t lv_img_buf_get_px_alpha(lv_img_dsc_t * dsc, lv_coord_t x, lv_coord_t y);
/**
* Set the color of a pixel of an image. The alpha channel won't be affected.
* @param dsc pointer to an image descriptor
* @param x x coordinate of the point to set
* @param y x coordinate of the point to set
* @param c color of the point
* @param safe true: check out of bounds
*/
void lv_img_buf_set_px_color(lv_img_dsc_t * dsc, lv_coord_t x, lv_coord_t y, lv_color_t c);
/**
* Set the alpha value of a pixel of an image. The color won't be affected
* @param dsc pointer to an image descriptor
* @param x x coordinate of the point to set
* @param y x coordinate of the point to set
* @param opa the desired opacity
* @param safe true: check out of bounds
*/
void lv_img_buf_set_px_alpha(lv_img_dsc_t * dsc, lv_coord_t x, lv_coord_t y, lv_opa_t opa);
/**
* Set the palette color of an indexed image. Valid only for `LV_IMG_CF_INDEXED1/2/4/8`
* @param dsc pointer to an image descriptor
* @param id the palette color to set:
* - for `LV_IMG_CF_INDEXED1`: 0..1
* - for `LV_IMG_CF_INDEXED2`: 0..3
* - for `LV_IMG_CF_INDEXED4`: 0..15
* - for `LV_IMG_CF_INDEXED8`: 0..255
* @param c the color to set
*/
void lv_img_buf_set_palette(lv_img_dsc_t * dsc, uint8_t id, lv_color_t c);
/**
* Free an allocated image buffer
* @param dsc image buffer to free
*/
void lv_img_buf_free(lv_img_dsc_t * dsc);
/**
* Get the memory consumption of a raw bitmap, given color format and dimensions.
* @param w width
* @param h height
* @param cf color format
* @return size in bytes
*/
uint32_t lv_img_buf_get_img_size(lv_coord_t w, lv_coord_t h, lv_img_cf_t cf);
#if LV_DRAW_COMPLEX
/**
* Initialize a descriptor to rotate an image
* @param dsc pointer to an `lv_img_transform_dsc_t` variable whose `cfg` field is initialized
*/
void _lv_img_buf_transform_init(lv_img_transform_dsc_t * dsc);
/**
* Continue transformation by taking the neighbors into account
* @param dsc pointer to the transformation descriptor
*/
bool _lv_img_buf_transform_anti_alias(lv_img_transform_dsc_t * dsc);
/**
* Get which color and opa would come to a pixel if it were rotated
* @param dsc a descriptor initialized by `lv_img_buf_rotate_init`
* @param x the coordinate which color and opa should be get
* @param y the coordinate which color and opa should be get
* @return true: there is valid pixel on these x/y coordinates; false: the rotated pixel was out of the image
* @note the result is written back to `dsc->res_color` and `dsc->res_opa`
*/
bool _lv_img_buf_transform(lv_img_transform_dsc_t * dsc, lv_coord_t x, lv_coord_t y);
#endif
/**
* 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 zoom zoom, (256 no zoom)
* @param pivot x,y pivot coordinates of rotation
*/
void _lv_img_buf_get_transformed_area(lv_area_t * res, lv_coord_t w, lv_coord_t h, int16_t angle, uint16_t zoom,
const lv_point_t * pivot);
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_IMG_BUF_H*/

View File

@@ -0,0 +1,215 @@
/**
* @file lv_img_cache.c
*
*/
/*********************
* INCLUDES
*********************/
#include "../misc/lv_assert.h"
#include "lv_img_cache.h"
#include "lv_img_decoder.h"
#include "lv_draw_img.h"
#include "../hal/lv_hal_tick.h"
#include "../misc/lv_gc.h"
/*********************
* DEFINES
*********************/
/*Decrement life with this value on every open*/
#define LV_IMG_CACHE_AGING 1
/*Boost life by this factor (multiply time_to_open with this value)*/
#define LV_IMG_CACHE_LIFE_GAIN 1
/*Don't let life to be greater than this limit because it would require a lot of time to
* "die" from very high values*/
#define LV_IMG_CACHE_LIFE_LIMIT 1000
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
#if LV_IMG_CACHE_DEF_SIZE
static bool lv_img_cache_match(const void * src1, const void * src2);
#endif
/**********************
* STATIC VARIABLES
**********************/
#if LV_IMG_CACHE_DEF_SIZE
static uint16_t entry_cnt;
#endif
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
/**
* Open an image using the image decoder interface and cache it.
* The image will be left open meaning if the image decoder open callback allocated memory then it will remain.
* The image is closed if a new image is opened and the new image takes its place in the cache.
* @param src source of the image. Path to file or pointer to an `lv_img_dsc_t` variable
* @param color color The color of the image with `LV_IMG_CF_ALPHA_...`
* @return pointer to the cache entry or NULL if can open the image
*/
_lv_img_cache_entry_t * _lv_img_cache_open(const void * src, lv_color_t color, int32_t frame_id)
{
/*Is the image cached?*/
_lv_img_cache_entry_t * cached_src = NULL;
#if LV_IMG_CACHE_DEF_SIZE
if(entry_cnt == 0) {
LV_LOG_WARN("lv_img_cache_open: the cache size is 0");
return NULL;
}
_lv_img_cache_entry_t * cache = LV_GC_ROOT(_lv_img_cache_array);
/*Decrement all lifes. Make the entries older*/
uint16_t i;
for(i = 0; i < entry_cnt; i++) {
if(cache[i].life > INT32_MIN + LV_IMG_CACHE_AGING) {
cache[i].life -= LV_IMG_CACHE_AGING;
}
}
for(i = 0; i < entry_cnt; i++) {
if(color.full == cache[i].dec_dsc.color.full &&
frame_id == cache[i].dec_dsc.frame_id &&
lv_img_cache_match(src, cache[i].dec_dsc.src)) {
/*If opened increment its life.
*Image difficult to open should live longer to keep avoid frequent their recaching.
*Therefore increase `life` with `time_to_open`*/
cached_src = &cache[i];
cached_src->life += cached_src->dec_dsc.time_to_open * LV_IMG_CACHE_LIFE_GAIN;
if(cached_src->life > LV_IMG_CACHE_LIFE_LIMIT) cached_src->life = LV_IMG_CACHE_LIFE_LIMIT;
LV_LOG_TRACE("image source found in the cache");
break;
}
}
/*The image is not cached then cache it now*/
if(cached_src) return cached_src;
/*Find an entry to reuse. Select the entry with the least life*/
cached_src = &cache[0];
for(i = 1; i < entry_cnt; i++) {
if(cache[i].life < cached_src->life) {
cached_src = &cache[i];
}
}
/*Close the decoder to reuse if it was opened (has a valid source)*/
if(cached_src->dec_dsc.src) {
lv_img_decoder_close(&cached_src->dec_dsc);
LV_LOG_INFO("image draw: cache miss, close and reuse an entry");
}
else {
LV_LOG_INFO("image draw: cache miss, cached to an empty entry");
}
#else
cached_src = &LV_GC_ROOT(_lv_img_cache_single);
#endif
/*Open the image and measure the time to open*/
uint32_t t_start = lv_tick_get();
lv_res_t open_res = lv_img_decoder_open(&cached_src->dec_dsc, src, color, frame_id);
if(open_res == LV_RES_INV) {
LV_LOG_WARN("Image draw cannot open the image resource");
lv_memset_00(cached_src, sizeof(_lv_img_cache_entry_t));
cached_src->life = INT32_MIN; /*Make the empty entry very "weak" to force its us*/
return NULL;
}
cached_src->life = 0;
/*If `time_to_open` was not set in the open function set it here*/
if(cached_src->dec_dsc.time_to_open == 0) {
cached_src->dec_dsc.time_to_open = lv_tick_elaps(t_start);
}
if(cached_src->dec_dsc.time_to_open == 0) cached_src->dec_dsc.time_to_open = 1;
return cached_src;
}
/**
* Set the number of images to be cached.
* More cached images mean more opened image at same time which might mean more memory usage.
* E.g. if 20 PNG or JPG images are open in the RAM they consume memory while opened in the cache.
* @param new_entry_cnt number of image to cache
*/
void lv_img_cache_set_size(uint16_t new_entry_cnt)
{
#if LV_IMG_CACHE_DEF_SIZE == 0
LV_UNUSED(new_entry_cnt);
LV_LOG_WARN("Can't change cache size because it's disabled by LV_IMG_CACHE_DEF_SIZE = 0");
#else
if(LV_GC_ROOT(_lv_img_cache_array) != NULL) {
/*Clean the cache before free it*/
lv_img_cache_invalidate_src(NULL);
lv_mem_free(LV_GC_ROOT(_lv_img_cache_array));
}
/*Reallocate the cache*/
LV_GC_ROOT(_lv_img_cache_array) = lv_mem_alloc(sizeof(_lv_img_cache_entry_t) * new_entry_cnt);
LV_ASSERT_MALLOC(LV_GC_ROOT(_lv_img_cache_array));
if(LV_GC_ROOT(_lv_img_cache_array) == NULL) {
entry_cnt = 0;
return;
}
entry_cnt = new_entry_cnt;
/*Clean the cache*/
lv_memset_00(LV_GC_ROOT(_lv_img_cache_array), entry_cnt * sizeof(_lv_img_cache_entry_t));
#endif
}
/**
* Invalidate an image source in the cache.
* Useful if the image source is updated therefore it needs to be cached again.
* @param src an image source path to a file or pointer to an `lv_img_dsc_t` variable.
*/
void lv_img_cache_invalidate_src(const void * src)
{
LV_UNUSED(src);
#if LV_IMG_CACHE_DEF_SIZE
_lv_img_cache_entry_t * cache = LV_GC_ROOT(_lv_img_cache_array);
uint16_t i;
for(i = 0; i < entry_cnt; i++) {
if(src == NULL || lv_img_cache_match(src, cache[i].dec_dsc.src)) {
if(cache[i].dec_dsc.src != NULL) {
lv_img_decoder_close(&cache[i].dec_dsc);
}
lv_memset_00(&cache[i], sizeof(_lv_img_cache_entry_t));
}
}
#endif
}
/**********************
* STATIC FUNCTIONS
**********************/
#if LV_IMG_CACHE_DEF_SIZE
static bool lv_img_cache_match(const void * src1, const void * src2)
{
lv_img_src_t src_type = lv_img_src_get_type(src1);
if(src_type == LV_IMG_SRC_VARIABLE)
return src1 == src2;
if(src_type != LV_IMG_SRC_FILE)
return false;
if(lv_img_src_get_type(src2) != LV_IMG_SRC_FILE)
return false;
return strcmp(src1, src2) == 0;
}
#endif

View File

@@ -0,0 +1,78 @@
/**
* @file lv_img_cache.h
*
*/
#ifndef LV_IMG_CACHE_H
#define LV_IMG_CACHE_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "lv_img_decoder.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**
* When loading images from the network it can take a long time to download and decode the image.
*
* To avoid repeating this heavy load images can be cached.
*/
typedef struct {
lv_img_decoder_dsc_t dec_dsc; /**< Image information*/
/** Count the cache entries's life. Add `time_to_open` to `life` when the entry is used.
* Decrement all lifes by one every in every ::lv_img_cache_open.
* If life == 0 the entry can be reused*/
int32_t life;
} _lv_img_cache_entry_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Open an image using the image decoder interface and cache it.
* The image will be left open meaning if the image decoder open callback allocated memory then it will remain.
* The image is closed if a new image is opened and the new image takes its place in the cache.
* @param src source of the image. Path to file or pointer to an `lv_img_dsc_t` variable
* @param color The color of the image with `LV_IMG_CF_ALPHA_...`
* @param frame_id the index of the frame. Used only with animated images, set 0 for normal images
* @return pointer to the cache entry or NULL if can open the image
*/
_lv_img_cache_entry_t * _lv_img_cache_open(const void * src, lv_color_t color, int32_t frame_id);
/**
* Set the number of images to be cached.
* More cached images mean more opened image at same time which might mean more memory usage.
* E.g. if 20 PNG or JPG images are open in the RAM they consume memory while opened in the cache.
* @param new_entry_cnt number of image to cache
*/
void lv_img_cache_set_size(uint16_t new_slot_num);
/**
* Invalidate an image source in the cache.
* Useful if the image source is updated therefore it needs to be cached again.
* @param src an image source path to a file or pointer to an `lv_img_dsc_t` variable.
*/
void lv_img_cache_invalidate_src(const void * src);
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_IMG_CACHE_H*/

View File

@@ -0,0 +1,704 @@
/**
* @file lv_img_decoder.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_img_decoder.h"
#include "../misc/lv_assert.h"
#include "../draw/lv_draw_img.h"
#include "../misc/lv_ll.h"
#include "../misc/lv_gc.h"
/*********************
* DEFINES
*********************/
#define CF_BUILT_IN_FIRST LV_IMG_CF_TRUE_COLOR
#define CF_BUILT_IN_LAST LV_IMG_CF_ALPHA_8BIT
/**********************
* TYPEDEFS
**********************/
typedef struct {
lv_fs_file_t f;
lv_color_t * palette;
lv_opa_t * opa;
} lv_img_decoder_built_in_data_t;
/**********************
* STATIC PROTOTYPES
**********************/
static lv_res_t lv_img_decoder_built_in_line_true_color(lv_img_decoder_dsc_t * dsc, lv_coord_t x, lv_coord_t y,
lv_coord_t len, uint8_t * buf);
static lv_res_t lv_img_decoder_built_in_line_alpha(lv_img_decoder_dsc_t * dsc, lv_coord_t x, lv_coord_t y,
lv_coord_t len, uint8_t * buf);
static lv_res_t lv_img_decoder_built_in_line_indexed(lv_img_decoder_dsc_t * dsc, lv_coord_t x, lv_coord_t y,
lv_coord_t len, uint8_t * buf);
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
/**
* Initialize the image decoder module
*/
void _lv_img_decoder_init(void)
{
_lv_ll_init(&LV_GC_ROOT(_lv_img_decoder_ll), sizeof(lv_img_decoder_t));
lv_img_decoder_t * decoder;
/*Create a decoder for the built in color format*/
decoder = lv_img_decoder_create();
LV_ASSERT_MALLOC(decoder);
if(decoder == NULL) {
LV_LOG_WARN("lv_img_decoder_init: out of memory");
return;
}
lv_img_decoder_set_info_cb(decoder, lv_img_decoder_built_in_info);
lv_img_decoder_set_open_cb(decoder, lv_img_decoder_built_in_open);
lv_img_decoder_set_read_line_cb(decoder, lv_img_decoder_built_in_read_line);
lv_img_decoder_set_close_cb(decoder, lv_img_decoder_built_in_close);
}
/**
* 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. E.g. file name or variable.
* @param header the image info will be stored here
* @return LV_RES_OK: success; LV_RES_INV: wasn't able to get info about the image
*/
lv_res_t lv_img_decoder_get_info(const void * src, lv_img_header_t * header)
{
lv_memset_00(header, sizeof(lv_img_header_t));
if(src == NULL) return LV_RES_INV;
lv_img_src_t src_type = lv_img_src_get_type(src);
if(src_type == LV_IMG_SRC_VARIABLE) {
const lv_img_dsc_t * img_dsc = src;
if(img_dsc->data == NULL) return LV_RES_INV;
}
lv_res_t res = LV_RES_INV;
lv_img_decoder_t * d;
_LV_LL_READ(&LV_GC_ROOT(_lv_img_decoder_ll), d) {
if(d->info_cb) {
res = d->info_cb(d, src, header);
if(res == LV_RES_OK) break;
}
}
return res;
}
lv_res_t lv_img_decoder_open(lv_img_decoder_dsc_t * dsc, const void * src, lv_color_t color, int32_t frame_id)
{
lv_memset_00(dsc, sizeof(lv_img_decoder_dsc_t));
if(src == NULL) return LV_RES_INV;
lv_img_src_t src_type = lv_img_src_get_type(src);
if(src_type == LV_IMG_SRC_VARIABLE) {
const lv_img_dsc_t * img_dsc = src;
if(img_dsc->data == NULL) return LV_RES_INV;
}
dsc->color = color;
dsc->src_type = src_type;
dsc->frame_id = frame_id;
if(dsc->src_type == LV_IMG_SRC_FILE) {
size_t fnlen = strlen(src);
dsc->src = lv_mem_alloc(fnlen + 1);
LV_ASSERT_MALLOC(dsc->src);
if(dsc->src == NULL) {
LV_LOG_WARN("lv_img_decoder_open: out of memory");
return LV_RES_INV;
}
strcpy((char *)dsc->src, src);
}
else {
dsc->src = src;
}
lv_res_t res = LV_RES_INV;
lv_img_decoder_t * decoder;
_LV_LL_READ(&LV_GC_ROOT(_lv_img_decoder_ll), decoder) {
/*Info and Open callbacks are required*/
if(decoder->info_cb == NULL || decoder->open_cb == NULL) continue;
res = decoder->info_cb(decoder, src, &dsc->header);
if(res != LV_RES_OK) continue;
dsc->decoder = decoder;
res = decoder->open_cb(decoder, dsc);
/*Opened successfully. It is a good decoder for this image source*/
if(res == LV_RES_OK) return res;
/*Prepare for the next loop*/
lv_memset_00(&dsc->header, sizeof(lv_img_header_t));
dsc->error_msg = NULL;
dsc->img_data = NULL;
dsc->user_data = NULL;
dsc->time_to_open = 0;
}
if(dsc->src_type == LV_IMG_SRC_FILE)
lv_mem_free((void *)dsc->src);
return res;
}
/**
* Read a line from an opened image
* @param dsc pointer to `lv_img_decoder_dsc_t` used in `lv_img_decoder_open`
* @param x start X coordinate (from left)
* @param y start Y coordinate (from top)
* @param len number of pixels to read
* @param buf store the data here
* @return LV_RES_OK: success; LV_RES_INV: an error occurred
*/
lv_res_t lv_img_decoder_read_line(lv_img_decoder_dsc_t * dsc, lv_coord_t x, lv_coord_t y, lv_coord_t len, uint8_t * buf)
{
lv_res_t res = LV_RES_INV;
if(dsc->decoder->read_line_cb) res = dsc->decoder->read_line_cb(dsc->decoder, dsc, x, y, len, buf);
return res;
}
/**
* Close a decoding session
* @param dsc pointer to `lv_img_decoder_dsc_t` used in `lv_img_decoder_open`
*/
void lv_img_decoder_close(lv_img_decoder_dsc_t * dsc)
{
if(dsc->decoder) {
if(dsc->decoder->close_cb) dsc->decoder->close_cb(dsc->decoder, dsc);
if(dsc->src_type == LV_IMG_SRC_FILE) {
lv_mem_free((void *)dsc->src);
dsc->src = NULL;
}
}
}
/**
* Create a new image decoder
* @return pointer to the new image decoder
*/
lv_img_decoder_t * lv_img_decoder_create(void)
{
lv_img_decoder_t * decoder;
decoder = _lv_ll_ins_head(&LV_GC_ROOT(_lv_img_decoder_ll));
LV_ASSERT_MALLOC(decoder);
if(decoder == NULL) return NULL;
lv_memset_00(decoder, sizeof(lv_img_decoder_t));
return decoder;
}
/**
* Delete an image decoder
* @param decoder pointer to an image decoder
*/
void lv_img_decoder_delete(lv_img_decoder_t * decoder)
{
_lv_ll_remove(&LV_GC_ROOT(_lv_img_decoder_ll), decoder);
lv_mem_free(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_img_header_t` struct)
*/
void lv_img_decoder_set_info_cb(lv_img_decoder_t * decoder, lv_img_decoder_info_f_t info_cb)
{
decoder->info_cb = 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_img_decoder_set_open_cb(lv_img_decoder_t * decoder, lv_img_decoder_open_f_t open_cb)
{
decoder->open_cb = 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_img_decoder_set_read_line_cb(lv_img_decoder_t * decoder, lv_img_decoder_read_line_f_t read_line_cb)
{
decoder->read_line_cb = 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_img_decoder_set_close_cb(lv_img_decoder_t * decoder, lv_img_decoder_close_f_t close_cb)
{
decoder->close_cb = close_cb;
}
/**
* Get info about a built-in image
* @param decoder the decoder where this function belongs
* @param src the image source: pointer to an `lv_img_dsc_t` variable, a file path or a symbol
* @param header store the image data here
* @return LV_RES_OK: the info is successfully stored in `header`; LV_RES_INV: unknown format or other error.
*/
lv_res_t lv_img_decoder_built_in_info(lv_img_decoder_t * decoder, const void * src, lv_img_header_t * header)
{
LV_UNUSED(decoder); /*Unused*/
lv_img_src_t src_type = lv_img_src_get_type(src);
if(src_type == LV_IMG_SRC_VARIABLE) {
lv_img_cf_t cf = ((lv_img_dsc_t *)src)->header.cf;
if(cf < CF_BUILT_IN_FIRST || cf > CF_BUILT_IN_LAST) return LV_RES_INV;
header->w = ((lv_img_dsc_t *)src)->header.w;
header->h = ((lv_img_dsc_t *)src)->header.h;
header->cf = ((lv_img_dsc_t *)src)->header.cf;
}
else if(src_type == LV_IMG_SRC_FILE) {
/*Support only "*.bin" files*/
if(strcmp(lv_fs_get_ext(src), "bin")) return LV_RES_INV;
lv_fs_file_t f;
lv_fs_res_t res = lv_fs_open(&f, src, LV_FS_MODE_RD);
if(res == LV_FS_RES_OK) {
uint32_t rn;
res = lv_fs_read(&f, header, sizeof(lv_img_header_t), &rn);
lv_fs_close(&f);
if(res != LV_FS_RES_OK || rn != sizeof(lv_img_header_t)) {
LV_LOG_WARN("Image get info get read file header");
return LV_RES_INV;
}
}
if(header->cf < CF_BUILT_IN_FIRST || header->cf > CF_BUILT_IN_LAST) return LV_RES_INV;
}
else if(src_type == LV_IMG_SRC_SYMBOL) {
/*The size depend on the font but it is unknown here. It should be handled outside of the
*function*/
header->w = 1;
header->h = 1;
/*Symbols always have transparent parts. Important because of cover check in the draw
*function. The actual value doesn't matter because lv_draw_label will draw it*/
header->cf = LV_IMG_CF_ALPHA_1BIT;
}
else {
LV_LOG_WARN("Image get info found unknown src type");
return LV_RES_INV;
}
return LV_RES_OK;
}
/**
* Open a built in image
* @param decoder the decoder where this function belongs
* @param dsc pointer to decoder descriptor. `src`, `color` are already initialized in it.
* @return LV_RES_OK: the info is successfully stored in `header`; LV_RES_INV: unknown format or other error.
*/
lv_res_t lv_img_decoder_built_in_open(lv_img_decoder_t * decoder, lv_img_decoder_dsc_t * dsc)
{
/*Open the file if it's a file*/
if(dsc->src_type == LV_IMG_SRC_FILE) {
/*Support only "*.bin" files*/
if(strcmp(lv_fs_get_ext(dsc->src), "bin")) return LV_RES_INV;
lv_fs_file_t f;
lv_fs_res_t res = lv_fs_open(&f, dsc->src, LV_FS_MODE_RD);
if(res != LV_FS_RES_OK) {
LV_LOG_WARN("Built-in image decoder can't open the file");
return LV_RES_INV;
}
/*If the file was open successfully save the file descriptor*/
if(dsc->user_data == NULL) {
dsc->user_data = lv_mem_alloc(sizeof(lv_img_decoder_built_in_data_t));
LV_ASSERT_MALLOC(dsc->user_data);
if(dsc->user_data == NULL) {
LV_LOG_ERROR("img_decoder_built_in_open: out of memory");
lv_fs_close(&f);
return LV_RES_INV;
}
lv_memset_00(dsc->user_data, sizeof(lv_img_decoder_built_in_data_t));
}
lv_img_decoder_built_in_data_t * user_data = dsc->user_data;
lv_memcpy_small(&user_data->f, &f, sizeof(f));
}
else if(dsc->src_type == LV_IMG_SRC_VARIABLE) {
/*The variables should have valid data*/
if(((lv_img_dsc_t *)dsc->src)->data == NULL) {
return LV_RES_INV;
}
}
lv_img_cf_t cf = dsc->header.cf;
/*Process true color formats*/
if(cf == LV_IMG_CF_TRUE_COLOR || cf == LV_IMG_CF_TRUE_COLOR_ALPHA || cf == LV_IMG_CF_TRUE_COLOR_CHROMA_KEYED) {
if(dsc->src_type == LV_IMG_SRC_VARIABLE) {
/*In case of uncompressed formats the image stored in the ROM/RAM.
*So simply give its pointer*/
dsc->img_data = ((lv_img_dsc_t *)dsc->src)->data;
return LV_RES_OK;
}
else {
/*If it's a file it need to be read line by line later*/
return LV_RES_OK;
}
}
/*Process indexed images. Build a palette*/
else if(cf == LV_IMG_CF_INDEXED_1BIT || cf == LV_IMG_CF_INDEXED_2BIT || cf == LV_IMG_CF_INDEXED_4BIT ||
cf == LV_IMG_CF_INDEXED_8BIT) {
uint8_t px_size = lv_img_cf_get_px_size(cf);
uint32_t palette_size = 1 << px_size;
/*Allocate the palette*/
if(dsc->user_data == NULL) {
dsc->user_data = lv_mem_alloc(sizeof(lv_img_decoder_built_in_data_t));
LV_ASSERT_MALLOC(dsc->user_data);
if(dsc->user_data == NULL) {
LV_LOG_ERROR("img_decoder_built_in_open: out of memory");
return LV_RES_INV;
}
lv_memset_00(dsc->user_data, sizeof(lv_img_decoder_built_in_data_t));
}
lv_img_decoder_built_in_data_t * user_data = dsc->user_data;
user_data->palette = lv_mem_alloc(palette_size * sizeof(lv_color_t));
LV_ASSERT_MALLOC(user_data->palette);
user_data->opa = lv_mem_alloc(palette_size * sizeof(lv_opa_t));
LV_ASSERT_MALLOC(user_data->opa);
if(user_data->palette == NULL || user_data->opa == NULL) {
LV_LOG_ERROR("img_decoder_built_in_open: out of memory");
lv_img_decoder_built_in_close(decoder, dsc);
return LV_RES_INV;
}
if(dsc->src_type == LV_IMG_SRC_FILE) {
/*Read the palette from file*/
lv_fs_seek(&user_data->f, 4, LV_FS_SEEK_SET); /*Skip the header*/
lv_color32_t cur_color;
uint32_t i;
for(i = 0; i < palette_size; i++) {
lv_fs_read(&user_data->f, &cur_color, sizeof(lv_color32_t), NULL);
user_data->palette[i] = lv_color_make(cur_color.ch.red, cur_color.ch.green, cur_color.ch.blue);
user_data->opa[i] = cur_color.ch.alpha;
}
}
else {
/*The palette begins in the beginning of the image data. Just point to it.*/
lv_color32_t * palette_p = (lv_color32_t *)((lv_img_dsc_t *)dsc->src)->data;
uint32_t i;
for(i = 0; i < palette_size; i++) {
user_data->palette[i] = lv_color_make(palette_p[i].ch.red, palette_p[i].ch.green, palette_p[i].ch.blue);
user_data->opa[i] = palette_p[i].ch.alpha;
}
}
return LV_RES_OK;
}
/*Alpha indexed images.*/
else if(cf == LV_IMG_CF_ALPHA_1BIT || cf == LV_IMG_CF_ALPHA_2BIT || cf == LV_IMG_CF_ALPHA_4BIT ||
cf == LV_IMG_CF_ALPHA_8BIT) {
return LV_RES_OK; /*Nothing to process*/
}
/*Unknown format. Can't decode it.*/
else {
/*Free the potentially allocated memories*/
lv_img_decoder_built_in_close(decoder, dsc);
LV_LOG_WARN("Image decoder open: unknown color format");
return LV_RES_INV;
}
}
/**
* Decode `len` pixels starting from the given `x`, `y` coordinates and store them in `buf`.
* 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 x start x coordinate
* @param y start y coordinate
* @param len number of pixels to decode
* @param buf a buffer to store the decoded pixels
* @return LV_RES_OK: ok; LV_RES_INV: failed
*/
lv_res_t lv_img_decoder_built_in_read_line(lv_img_decoder_t * decoder, lv_img_decoder_dsc_t * dsc, lv_coord_t x,
lv_coord_t y, lv_coord_t len, uint8_t * buf)
{
LV_UNUSED(decoder); /*Unused*/
lv_res_t res = LV_RES_INV;
if(dsc->header.cf == LV_IMG_CF_TRUE_COLOR || dsc->header.cf == LV_IMG_CF_TRUE_COLOR_ALPHA ||
dsc->header.cf == LV_IMG_CF_TRUE_COLOR_CHROMA_KEYED) {
/*For TRUE_COLOR images read line required only for files.
*For variables the image data was returned in `open`*/
if(dsc->src_type == LV_IMG_SRC_FILE) {
res = lv_img_decoder_built_in_line_true_color(dsc, x, y, len, buf);
}
}
else if(dsc->header.cf == LV_IMG_CF_ALPHA_1BIT || dsc->header.cf == LV_IMG_CF_ALPHA_2BIT ||
dsc->header.cf == LV_IMG_CF_ALPHA_4BIT || dsc->header.cf == LV_IMG_CF_ALPHA_8BIT) {
res = lv_img_decoder_built_in_line_alpha(dsc, x, y, len, buf);
}
else if(dsc->header.cf == LV_IMG_CF_INDEXED_1BIT || dsc->header.cf == LV_IMG_CF_INDEXED_2BIT ||
dsc->header.cf == LV_IMG_CF_INDEXED_4BIT || dsc->header.cf == LV_IMG_CF_INDEXED_8BIT) {
res = lv_img_decoder_built_in_line_indexed(dsc, x, y, len, buf);
}
else {
LV_LOG_WARN("Built-in image decoder read not supports the color format");
return LV_RES_INV;
}
return res;
}
/**
* Close the pending decoding. Free resources etc.
* @param decoder pointer to the decoder the function associated with
* @param dsc pointer to decoder descriptor
*/
void lv_img_decoder_built_in_close(lv_img_decoder_t * decoder, lv_img_decoder_dsc_t * dsc)
{
LV_UNUSED(decoder); /*Unused*/
lv_img_decoder_built_in_data_t * user_data = dsc->user_data;
if(user_data) {
if(dsc->src_type == LV_IMG_SRC_FILE) {
lv_fs_close(&user_data->f);
}
if(user_data->palette) lv_mem_free(user_data->palette);
if(user_data->opa) lv_mem_free(user_data->opa);
lv_mem_free(user_data);
dsc->user_data = NULL;
}
}
/**********************
* STATIC FUNCTIONS
**********************/
static lv_res_t lv_img_decoder_built_in_line_true_color(lv_img_decoder_dsc_t * dsc, lv_coord_t x, lv_coord_t y,
lv_coord_t len, uint8_t * buf)
{
lv_img_decoder_built_in_data_t * user_data = dsc->user_data;
lv_fs_res_t res;
uint8_t px_size = lv_img_cf_get_px_size(dsc->header.cf);
uint32_t pos = ((y * dsc->header.w + x) * px_size) >> 3;
pos += 4; /*Skip the header*/
res = lv_fs_seek(&user_data->f, pos, LV_FS_SEEK_SET);
if(res != LV_FS_RES_OK) {
LV_LOG_WARN("Built-in image decoder seek failed");
return LV_RES_INV;
}
uint32_t btr = len * (px_size >> 3);
uint32_t br = 0;
res = lv_fs_read(&user_data->f, buf, btr, &br);
if(res != LV_FS_RES_OK || btr != br) {
LV_LOG_WARN("Built-in image decoder read failed");
return LV_RES_INV;
}
return LV_RES_OK;
}
static lv_res_t lv_img_decoder_built_in_line_alpha(lv_img_decoder_dsc_t * dsc, lv_coord_t x, lv_coord_t y,
lv_coord_t len, uint8_t * buf)
{
const lv_opa_t alpha1_opa_table[2] = {0, 255}; /*Opacity mapping with bpp = 1 (Just for compatibility)*/
const lv_opa_t alpha2_opa_table[4] = {0, 85, 170, 255}; /*Opacity mapping with bpp = 2*/
const lv_opa_t alpha4_opa_table[16] = {0, 17, 34, 51, /*Opacity mapping with bpp = 4*/
68, 85, 102, 119, 136, 153, 170, 187, 204, 221, 238, 255
};
/*Simply fill the buffer with the color. Later only the alpha value will be modified.*/
lv_color_t bg_color = dsc->color;
lv_coord_t i;
for(i = 0; i < len; i++) {
#if LV_COLOR_DEPTH == 8 || LV_COLOR_DEPTH == 1
buf[i * LV_IMG_PX_SIZE_ALPHA_BYTE] = bg_color.full;
#elif LV_COLOR_DEPTH == 16
/*Because of Alpha byte 16 bit color can start on odd address which can cause crash*/
buf[i * LV_IMG_PX_SIZE_ALPHA_BYTE] = bg_color.full & 0xFF;
buf[i * LV_IMG_PX_SIZE_ALPHA_BYTE + 1] = (bg_color.full >> 8) & 0xFF;
#elif LV_COLOR_DEPTH == 32
*((uint32_t *)&buf[i * LV_IMG_PX_SIZE_ALPHA_BYTE]) = bg_color.full;
#else
#error "Invalid LV_COLOR_DEPTH. Check it in lv_conf.h"
#endif
}
const lv_opa_t * opa_table = NULL;
uint8_t px_size = lv_img_cf_get_px_size(dsc->header.cf);
uint16_t mask = (1 << px_size) - 1; /*E.g. px_size = 2; mask = 0x03*/
lv_coord_t w = 0;
uint32_t ofs = 0;
int8_t pos = 0;
switch(dsc->header.cf) {
case LV_IMG_CF_ALPHA_1BIT:
w = (dsc->header.w + 7) >> 3; /*E.g. w = 20 -> w = 2 + 1*/
ofs += w * y + (x >> 3); /*First pixel*/
pos = 7 - (x & 0x7);
opa_table = alpha1_opa_table;
break;
case LV_IMG_CF_ALPHA_2BIT:
w = (dsc->header.w + 3) >> 2; /*E.g. w = 13 -> w = 3 + 1 (bytes)*/
ofs += w * y + (x >> 2); /*First pixel*/
pos = 6 - (x & 0x3) * 2;
opa_table = alpha2_opa_table;
break;
case LV_IMG_CF_ALPHA_4BIT:
w = (dsc->header.w + 1) >> 1; /*E.g. w = 13 -> w = 6 + 1 (bytes)*/
ofs += w * y + (x >> 1); /*First pixel*/
pos = 4 - (x & 0x1) * 4;
opa_table = alpha4_opa_table;
break;
case LV_IMG_CF_ALPHA_8BIT:
w = dsc->header.w; /*E.g. x = 7 -> w = 7 (bytes)*/
ofs += w * y + x; /*First pixel*/
pos = 0;
break;
}
lv_img_decoder_built_in_data_t * user_data = dsc->user_data;
uint8_t * fs_buf = lv_mem_buf_get(w);
if(fs_buf == NULL) return LV_RES_INV;
const uint8_t * data_tmp = NULL;
if(dsc->src_type == LV_IMG_SRC_VARIABLE) {
const lv_img_dsc_t * img_dsc = dsc->src;
data_tmp = img_dsc->data + ofs;
}
else {
lv_fs_seek(&user_data->f, ofs + 4, LV_FS_SEEK_SET); /*+4 to skip the header*/
lv_fs_read(&user_data->f, fs_buf, w, NULL);
data_tmp = fs_buf;
}
for(i = 0; i < len; i++) {
uint8_t val_act = (*data_tmp >> pos) & mask;
buf[i * LV_IMG_PX_SIZE_ALPHA_BYTE + LV_IMG_PX_SIZE_ALPHA_BYTE - 1] =
dsc->header.cf == LV_IMG_CF_ALPHA_8BIT ? val_act : opa_table[val_act];
pos -= px_size;
if(pos < 0) {
pos = 8 - px_size;
data_tmp++;
}
}
lv_mem_buf_release(fs_buf);
return LV_RES_OK;
}
static lv_res_t lv_img_decoder_built_in_line_indexed(lv_img_decoder_dsc_t * dsc, lv_coord_t x, lv_coord_t y,
lv_coord_t len, uint8_t * buf)
{
uint8_t px_size = lv_img_cf_get_px_size(dsc->header.cf);
uint16_t mask = (1 << px_size) - 1; /*E.g. px_size = 2; mask = 0x03*/
lv_coord_t w = 0;
int8_t pos = 0;
uint32_t ofs = 0;
switch(dsc->header.cf) {
case LV_IMG_CF_INDEXED_1BIT:
w = (dsc->header.w + 7) >> 3; /*E.g. w = 20 -> w = 2 + 1*/
ofs += w * y + (x >> 3); /*First pixel*/
ofs += 8; /*Skip the palette*/
pos = 7 - (x & 0x7);
break;
case LV_IMG_CF_INDEXED_2BIT:
w = (dsc->header.w + 3) >> 2; /*E.g. w = 13 -> w = 3 + 1 (bytes)*/
ofs += w * y + (x >> 2); /*First pixel*/
ofs += 16; /*Skip the palette*/
pos = 6 - (x & 0x3) * 2;
break;
case LV_IMG_CF_INDEXED_4BIT:
w = (dsc->header.w + 1) >> 1; /*E.g. w = 13 -> w = 6 + 1 (bytes)*/
ofs += w * y + (x >> 1); /*First pixel*/
ofs += 64; /*Skip the palette*/
pos = 4 - (x & 0x1) * 4;
break;
case LV_IMG_CF_INDEXED_8BIT:
w = dsc->header.w; /*E.g. x = 7 -> w = 7 (bytes)*/
ofs += w * y + x; /*First pixel*/
ofs += 1024; /*Skip the palette*/
pos = 0;
break;
}
lv_img_decoder_built_in_data_t * user_data = dsc->user_data;
uint8_t * fs_buf = lv_mem_buf_get(w);
if(fs_buf == NULL) return LV_RES_INV;
const uint8_t * data_tmp = NULL;
if(dsc->src_type == LV_IMG_SRC_VARIABLE) {
const lv_img_dsc_t * img_dsc = dsc->src;
data_tmp = img_dsc->data + ofs;
}
else {
lv_fs_seek(&user_data->f, ofs + 4, LV_FS_SEEK_SET); /*+4 to skip the header*/
lv_fs_read(&user_data->f, fs_buf, w, NULL);
data_tmp = fs_buf;
}
lv_coord_t i;
for(i = 0; i < len; i++) {
uint8_t val_act = (*data_tmp >> pos) & mask;
lv_color_t color = user_data->palette[val_act];
#if LV_COLOR_DEPTH == 8 || LV_COLOR_DEPTH == 1
buf[i * LV_IMG_PX_SIZE_ALPHA_BYTE] = color.full;
#elif LV_COLOR_DEPTH == 16
/*Because of Alpha byte 16 bit color can start on odd address which can cause crash*/
buf[i * LV_IMG_PX_SIZE_ALPHA_BYTE] = color.full & 0xFF;
buf[i * LV_IMG_PX_SIZE_ALPHA_BYTE + 1] = (color.full >> 8) & 0xFF;
#elif LV_COLOR_DEPTH == 32
*((uint32_t *)&buf[i * LV_IMG_PX_SIZE_ALPHA_BYTE]) = color.full;
#else
#error "Invalid LV_COLOR_DEPTH. Check it in lv_conf.h"
#endif
buf[i * LV_IMG_PX_SIZE_ALPHA_BYTE + LV_IMG_PX_SIZE_ALPHA_BYTE - 1] = user_data->opa[val_act];
pos -= px_size;
if(pos < 0) {
pos = 8 - px_size;
data_tmp++;
}
}
lv_mem_buf_release(fs_buf);
return LV_RES_OK;
}

View File

@@ -0,0 +1,274 @@
/**
* @file lv_img_decoder.h
*
*/
#ifndef LV_IMG_DECODER_H
#define LV_IMG_DECODER_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../lv_conf_internal.h"
#include <stdint.h>
#include "lv_img_buf.h"
#include "../misc/lv_fs.h"
#include "../misc/lv_types.h"
#include "../misc/lv_area.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**
* Source of image.*/
enum {
LV_IMG_SRC_VARIABLE, /** Binary/C variable*/
LV_IMG_SRC_FILE, /** File in filesystem*/
LV_IMG_SRC_SYMBOL, /** Symbol (@ref lv_symbol_def.h)*/
LV_IMG_SRC_UNKNOWN, /** Unknown source*/
};
typedef uint8_t lv_img_src_t;
/*Decoder function definitions*/
struct _lv_img_decoder_dsc_t;
struct _lv_img_decoder_t;
/**
* Get info from an image and store in the `header`
* @param src the image source. Can be a pointer to a C array or a file name (Use
* `lv_img_src_get_type` to determine the type)
* @param header store the info here
* @return LV_RES_OK: info written correctly; LV_RES_INV: failed
*/
typedef lv_res_t (*lv_img_decoder_info_f_t)(struct _lv_img_decoder_t * decoder, const void * src,
lv_img_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_res_t (*lv_img_decoder_open_f_t)(struct _lv_img_decoder_t * decoder, struct _lv_img_decoder_dsc_t * dsc);
/**
* Decode `len` pixels starting from the given `x`, `y` coordinates and store them in `buf`.
* 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 x start x coordinate
* @param y start y coordinate
* @param len number of pixels to decode
* @param buf a buffer to store the decoded pixels
* @return LV_RES_OK: ok; LV_RES_INV: failed
*/
typedef lv_res_t (*lv_img_decoder_read_line_f_t)(struct _lv_img_decoder_t * decoder, struct _lv_img_decoder_dsc_t * dsc,
lv_coord_t x, lv_coord_t y, lv_coord_t len, uint8_t * buf);
/**
* 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_img_decoder_close_f_t)(struct _lv_img_decoder_t * decoder, struct _lv_img_decoder_dsc_t * dsc);
typedef struct _lv_img_decoder_t {
lv_img_decoder_info_f_t info_cb;
lv_img_decoder_open_f_t open_cb;
lv_img_decoder_read_line_f_t read_line_cb;
lv_img_decoder_close_f_t close_cb;
#if LV_USE_USER_DATA
void * user_data;
#endif
} lv_img_decoder_t;
/**Describe an image decoding session. Stores data about the decoding*/
typedef struct _lv_img_decoder_dsc_t {
/**The decoder which was able to open the image source*/
lv_img_decoder_t * decoder;
/**The image source. A file path like "S:my_img.png" or pointer to an `lv_img_dsc_t` variable*/
const void * src;
/**Color to draw the image. USed when the image has alpha channel only*/
lv_color_t color;
/**Frame of the image, using with animated images*/
int32_t frame_id;
/**Type of the source: file or variable. Can be set in `open` function if required*/
lv_img_src_t src_type;
/**Info about the opened image: color format, size, etc. MUST be set in `open` function*/
lv_img_header_t header;
/** Pointer to a buffer where the image's data (pixels) are stored in a decoded, plain format.
* MUST be set in `open` function*/
const uint8_t * img_data;
/** How much time did it take to open the image. [ms]
* If not set `lv_img_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;
/**Store any custom data here is required*/
void * user_data;
} lv_img_decoder_dsc_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Initialize the image decoder module
*/
void _lv_img_decoder_init(void);
/**
* 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_add_drv()`)
* 2) Variable: Pointer to an `lv_img_dsc_t` variable
* 3) Symbol: E.g. `LV_SYMBOL_OK`
* @param header the image info will be stored here
* @return LV_RES_OK: success; LV_RES_INV: wasn't able to get info about the image
*/
lv_res_t lv_img_decoder_get_info(const void * src, lv_img_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_img_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_add_drv()`)
* 2) Variable: Pointer to an `lv_img_dsc_t` variable
* 3) Symbol: E.g. `LV_SYMBOL_OK`
* @param color The color of the image with `LV_IMG_CF_ALPHA_...`
* @param frame_id the index of the frame. Used only with animated images, set 0 for normal images
* @return LV_RES_OK: opened the image. `dsc->img_data` and `dsc->header` are set.
* LV_RES_INV: none of the registered image decoders were able to open the image.
*/
lv_res_t lv_img_decoder_open(lv_img_decoder_dsc_t * dsc, const void * src, lv_color_t color, int32_t frame_id);
/**
* Read a line from an opened image
* @param dsc pointer to `lv_img_decoder_dsc_t` used in `lv_img_decoder_open`
* @param x start X coordinate (from left)
* @param y start Y coordinate (from top)
* @param len number of pixels to read
* @param buf store the data here
* @return LV_RES_OK: success; LV_RES_INV: an error occurred
*/
lv_res_t lv_img_decoder_read_line(lv_img_decoder_dsc_t * dsc, lv_coord_t x, lv_coord_t y, lv_coord_t len,
uint8_t * buf);
/**
* Close a decoding session
* @param dsc pointer to `lv_img_decoder_dsc_t` used in `lv_img_decoder_open`
*/
void lv_img_decoder_close(lv_img_decoder_dsc_t * dsc);
/**
* Create a new image decoder
* @return pointer to the new image decoder
*/
lv_img_decoder_t * lv_img_decoder_create(void);
/**
* Delete an image decoder
* @param decoder pointer to an image decoder
*/
void lv_img_decoder_delete(lv_img_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_img_header_t` struct)
*/
void lv_img_decoder_set_info_cb(lv_img_decoder_t * decoder, lv_img_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_img_decoder_set_open_cb(lv_img_decoder_t * decoder, lv_img_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_img_decoder_set_read_line_cb(lv_img_decoder_t * decoder, lv_img_decoder_read_line_f_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_img_decoder_set_close_cb(lv_img_decoder_t * decoder, lv_img_decoder_close_f_t close_cb);
/**
* Get info about a built-in image
* @param decoder the decoder where this function belongs
* @param src the image source: pointer to an `lv_img_dsc_t` variable, a file path or a symbol
* @param header store the image data here
* @return LV_RES_OK: the info is successfully stored in `header`; LV_RES_INV: unknown format or other error.
*/
lv_res_t lv_img_decoder_built_in_info(lv_img_decoder_t * decoder, const void * src, lv_img_header_t * header);
/**
* Open a built in image
* @param decoder the decoder where this function belongs
* @param dsc pointer to decoder descriptor. `src`, `style` are already initialized in it.
* @return LV_RES_OK: the info is successfully stored in `header`; LV_RES_INV: unknown format or other error.
*/
lv_res_t lv_img_decoder_built_in_open(lv_img_decoder_t * decoder, lv_img_decoder_dsc_t * dsc);
/**
* Decode `len` pixels starting from the given `x`, `y` coordinates and store them in `buf`.
* 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 x start x coordinate
* @param y start y coordinate
* @param len number of pixels to decode
* @param buf a buffer to store the decoded pixels
* @return LV_RES_OK: ok; LV_RES_INV: failed
*/
lv_res_t lv_img_decoder_built_in_read_line(lv_img_decoder_t * decoder, lv_img_decoder_dsc_t * dsc, lv_coord_t x,
lv_coord_t y, lv_coord_t len, uint8_t * buf);
/**
* Close the pending decoding. Free resources etc.
* @param decoder pointer to the decoder the function associated with
* @param dsc pointer to decoder descriptor
*/
void lv_img_decoder_built_in_close(lv_img_decoder_t * decoder, lv_img_decoder_dsc_t * dsc);
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_IMG_DECODER_H*/

View File

@@ -0,0 +1,7 @@
CSRCS += lv_gpu_nxp_pxp.c
CSRCS += lv_gpu_nxp_pxp_osa.c
DEPPATH += --dep-path $(LVGL_DIR)/$(LVGL_DIR_NAME)/src/draw/nxp_pxp
VPATH += :$(LVGL_DIR)/$(LVGL_DIR_NAME)/src/draw/nxp_pxp
CFLAGS += "-I$(LVGL_DIR)/$(LVGL_DIR_NAME)/src/draw/nxp_pxp"

View File

@@ -0,0 +1,446 @@
/**
* @file lv_gpu_nxp_pxp.c
*
*/
/**
* MIT License
*
* Copyright (c) 2020 NXP
*
* 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.
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_gpu_nxp_pxp.h"
#if LV_USE_GPU_NXP_PXP
#include "../misc/lv_mem.h"
#include "../misc/lv_log.h"
#include "fsl_pxp.h"
#include "fsl_cache.h"
/*********************
* DEFINES
*********************/
#if LV_COLOR_16_SWAP
#error Color swap not implemented. Disable LV_COLOR_16_SWAP feature.
#endif
#if LV_COLOR_DEPTH==16
#define PXP_OUT_PIXEL_FORMAT kPXP_OutputPixelFormatRGB565
#define PXP_AS_PIXEL_FORMAT kPXP_AsPixelFormatRGB565
#define PXP_PS_PIXEL_FORMAT kPXP_PsPixelFormatRGB565
#else
#error Only 16bit color depth is supported. Set LV_COLOR_DEPTH to 16.
#endif
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static void lv_gpu_nxp_pxp_run(void);
static void lv_gpu_nxp_pxp_blit_recolor(lv_color_t * dest, lv_coord_t dest_width, const lv_color_t * src,
lv_coord_t src_width,
lv_coord_t copy_width, lv_coord_t copy_height, lv_opa_t opa, lv_color_t recolor, lv_opa_t recolorOpa);
/**********************
* STATIC VARIABLES
**********************/
static bool colorKeyEnabled = false;
static uint32_t colorKey = 0x0;
static bool recolorEnabled = false;
static lv_color_t recolor = {.full = 0x0};
static lv_opa_t recolorOpa = 0x0;
static lv_nxp_pxp_cfg_t pxp_cfg;
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
/**
* Reset and initialize PXP device. This function should be called as a part
* of display init sequence.
*
* @return LV_RES_OK: PXP init ok; LV_RES_INV: init error. See error log for more information.
*/
lv_res_t lv_gpu_nxp_pxp_init(lv_nxp_pxp_cfg_t * cfg)
{
if(!cfg || !cfg->pxp_interrupt_deinit || !cfg->pxp_interrupt_init || !cfg->pxp_run) {
LV_LOG_ERROR("PXP configuration error. Check callback pointers.");
return LV_RES_INV;
}
PXP_Init(PXP);
PXP_EnableCsc1(PXP, false); /*Disable CSC1, it is enabled by default.*/
PXP_EnableInterrupts(PXP, kPXP_CompleteInterruptEnable);
pxp_cfg = *cfg;
if(pxp_cfg.pxp_interrupt_init() != LV_RES_OK) {
PXP_Deinit(PXP);
LV_LOG_ERROR("PXP interrupt init error. Check pxp_interrupt_init callback.");
return LV_RES_INV;
}
colorKey = lv_color_to32(LV_COLOR_CHROMA_KEY);
return LV_RES_OK;
}
/**
* Disable PXP device. Should be called during display deinit sequence.
*/
void lv_gpu_nxp_pxp_deinit(void)
{
pxp_cfg.pxp_interrupt_deinit();
PXP_DisableInterrupts(PXP, kPXP_CompleteInterruptEnable);
PXP_Deinit(LV_GPU_NXP_PXP_ID);
}
/**
* Fill area, with optional opacity.
*
* @param[in/out] dest_buf destination buffer
* @param[in] dest_width width (stride) of destination buffer in pixels
* @param[in] fill_area area to fill
* @param[in] color color
* @param[in] opa transparency of the color
*/
void lv_gpu_nxp_pxp_fill(lv_color_t * dest_buf, lv_coord_t dest_width, const lv_area_t * fill_area, lv_color_t color,
lv_opa_t opa)
{
PXP_Init(LV_GPU_NXP_PXP_ID);
PXP_EnableCsc1(LV_GPU_NXP_PXP_ID, false); /*Disable CSC1, it is enabled by default.*/
PXP_SetProcessBlockSize(PXP, kPXP_BlockSize16); /*Block size 16x16 for higher performance*/
/*OUT buffer configure*/
pxp_output_buffer_config_t outputConfig = {
.pixelFormat = PXP_OUT_PIXEL_FORMAT,
.interlacedMode = kPXP_OutputProgressive,
.buffer0Addr = (uint32_t)(dest_buf + dest_width * fill_area->y1 + fill_area->x1),
.buffer1Addr = (uint32_t)NULL,
.pitchBytes = dest_width * sizeof(lv_color_t),
.width = fill_area->x2 - fill_area->x1 + 1,
.height = fill_area->y2 - fill_area->y1 + 1,
};
PXP_SetOutputBufferConfig(LV_GPU_NXP_PXP_ID, &outputConfig);
if(opa > LV_OPA_MAX) {
/*Simple color fill without opacity - AS disabled, PS as color generator*/
PXP_SetAlphaSurfacePosition(LV_GPU_NXP_PXP_ID, 0xFFFFU, 0xFFFFU, 0U, 0U); /*Disable AS.*/
PXP_SetProcessSurfacePosition(LV_GPU_NXP_PXP_ID, 0xFFFFU, 0xFFFFU, 0U, 0U); /*Disable PS.*/
PXP_SetProcessSurfaceBackGroundColor(LV_GPU_NXP_PXP_ID, lv_color_to32(color));
}
else {
/*Fill with opacity - AS used as source (same as OUT), PS used as color generator, blended together*/
pxp_as_buffer_config_t asBufferConfig;
pxp_porter_duff_config_t pdConfig;
/*Set AS to OUT*/
asBufferConfig.pixelFormat = PXP_AS_PIXEL_FORMAT;
asBufferConfig.bufferAddr = (uint32_t)outputConfig.buffer0Addr;
asBufferConfig.pitchBytes = outputConfig.pitchBytes;
PXP_SetAlphaSurfaceBufferConfig(LV_GPU_NXP_PXP_ID, &asBufferConfig);
PXP_SetAlphaSurfacePosition(LV_GPU_NXP_PXP_ID, 0U, 0U, fill_area->x2 - fill_area->x1 + 1,
fill_area->y2 - fill_area->y1 + 1);
/*Disable PS, use as color generator*/
PXP_SetProcessSurfacePosition(LV_GPU_NXP_PXP_ID, 0xFFFFU, 0xFFFFU, 0U, 0U);
PXP_SetProcessSurfaceBackGroundColor(LV_GPU_NXP_PXP_ID, lv_color_to32(color));
/*Configure Porter-Duff blending - For RGB 565 only!*/
pdConfig.enable = 1;
pdConfig.dstColorMode = kPXP_PorterDuffColorStraight;
pdConfig.srcColorMode = kPXP_PorterDuffColorStraight;
pdConfig.dstGlobalAlphaMode = kPXP_PorterDuffGlobalAlpha;
pdConfig.srcGlobalAlphaMode = kPXP_PorterDuffGlobalAlpha;
pdConfig.srcFactorMode = kPXP_PorterDuffFactorStraight;
pdConfig.dstFactorMode = kPXP_PorterDuffFactorStraight;
pdConfig.srcGlobalAlpha = opa;
pdConfig.dstGlobalAlpha = 255 - opa;
pdConfig.srcAlphaMode = kPXP_PorterDuffAlphaStraight; /*don't care*/
pdConfig.dstAlphaMode = kPXP_PorterDuffAlphaStraight; /*don't care*/
PXP_SetPorterDuffConfig(LV_GPU_NXP_PXP_ID, &pdConfig);
}
lv_gpu_nxp_pxp_run(); /*Start PXP task*/
}
/**
* @brief BLock Image Transfer - copy rectangular image from src buffer to dst buffer with effects.
*
* By default, image is copied directly, with optional opacity configured by \p opa.
* Color keying can be enabled by calling lv_gpu_nxp_pxp_enable_color_key() before calling this function.
* Recoloring can be enabled by calling lv_gpu_nxp_pxp_enable_recolor() before calling this function.
* Note that color keying and recoloring at the same time is not supported and black rectangle is rendered.
*
* @param[in/out] dest destination buffer
* @param[in] dest_width width (stride) of destination buffer in pixels
* @param[in] src source buffer
* @param[in] src_with width (stride) of source buffer in pixels
* @param[in] copy_w width of area to be copied from src to dest
* @param[in] copy_h height of area to be copied from src to dest
* @param[in] opa opacity of the result
*/
void lv_gpu_nxp_pxp_blit(lv_color_t * dest, lv_coord_t dest_width, const lv_color_t * src, lv_coord_t src_width,
lv_coord_t copy_width, lv_coord_t copy_height, lv_opa_t opa)
{
if(recolorEnabled) { /*switch to recolor version of blit*/
lv_gpu_nxp_pxp_blit_recolor(dest, dest_width, src, src_width, copy_width, copy_height, opa, recolor, recolorOpa);
return;
};
PXP_Init(PXP);
PXP_EnableCsc1(PXP, false); /*Disable CSC1, it is enabled by default.*/
PXP_SetProcessBlockSize(PXP, kPXP_BlockSize16); /*block size 16x16 for higher performance*/
pxp_output_buffer_config_t outputBufferConfig;
pxp_as_buffer_config_t asBufferConfig;
pxp_as_blend_config_t asBlendConfig;
asBlendConfig.alpha = opa;
asBlendConfig.invertAlpha = false;
asBlendConfig.alphaMode = kPXP_AlphaRop;
asBlendConfig.ropMode = kPXP_RopMergeAs;
if(opa >= LV_OPA_MAX && !colorKeyEnabled) {
/*Simple blit, no effect - Disable PS buffer*/
PXP_SetProcessSurfacePosition(LV_GPU_NXP_PXP_ID, 0xFFFFU, 0xFFFFU, 0U, 0U);
}
else {
/*Alpha blending or color keying enabled - PS must be enabled to fetch background pixels
PS and OUT buffers are the same, blend will be done in-place*/
pxp_ps_buffer_config_t psBufferConfig = {
.pixelFormat = PXP_PS_PIXEL_FORMAT,
.swapByte = false,
.bufferAddr = (uint32_t)dest,
.bufferAddrU = 0U,
.bufferAddrV = 0U,
.pitchBytes = dest_width * sizeof(lv_color_t)
};
asBlendConfig.alphaMode = kPXP_AlphaOverride;
PXP_SetProcessSurfaceBufferConfig(LV_GPU_NXP_PXP_ID, &psBufferConfig);
PXP_SetProcessSurfacePosition(LV_GPU_NXP_PXP_ID, 0U, 0U, copy_width - 1, copy_height - 1);
}
/*AS buffer - source image*/
asBufferConfig.pixelFormat = PXP_AS_PIXEL_FORMAT;
asBufferConfig.bufferAddr = (uint32_t)src;
asBufferConfig.pitchBytes = src_width * sizeof(lv_color_t);
PXP_SetAlphaSurfaceBufferConfig(LV_GPU_NXP_PXP_ID, &asBufferConfig);
PXP_SetAlphaSurfacePosition(LV_GPU_NXP_PXP_ID, 0U, 0U, copy_width - 1U, copy_height - 1U);
PXP_SetAlphaSurfaceBlendConfig(LV_GPU_NXP_PXP_ID, &asBlendConfig);
if(colorKeyEnabled) {
PXP_SetAlphaSurfaceOverlayColorKey(LV_GPU_NXP_PXP_ID, colorKey, colorKey);
}
PXP_EnableAlphaSurfaceOverlayColorKey(LV_GPU_NXP_PXP_ID, colorKeyEnabled);
/*Output buffer.*/
outputBufferConfig.pixelFormat = (pxp_output_pixel_format_t)PXP_OUT_PIXEL_FORMAT;
outputBufferConfig.interlacedMode = kPXP_OutputProgressive;
outputBufferConfig.buffer0Addr = (uint32_t)dest;
outputBufferConfig.buffer1Addr = (uint32_t)0U;
outputBufferConfig.pitchBytes = dest_width * sizeof(lv_color_t);
outputBufferConfig.width = copy_width;
outputBufferConfig.height = copy_height;
PXP_SetOutputBufferConfig(LV_GPU_NXP_PXP_ID, &outputBufferConfig);
lv_gpu_nxp_pxp_run(); /* Start PXP task */
}
/**
* @brief Enable color keying for subsequent calls to lv_gpu_nxp_pxp_blit()
*
* Color key is defined by symbol in lv_conf.h
*/
void lv_gpu_nxp_pxp_enable_color_key(void)
{
colorKeyEnabled = true;
}
/**
* @brief Disable color keying for subsequent calls to lv_gpu_nxp_pxp_blit()
*
*/
void lv_gpu_nxp_pxp_disable_color_key(void)
{
colorKeyEnabled = false;
}
/**
* @brief Enable recolor feature for subsequent calls to lv_gpu_nxp_pxp_blit()
*
* @param[in] color recolor value
* @param[in] opa effect opacity
*/
void lv_gpu_nxp_pxp_enable_recolor(lv_color_t color, lv_opa_t opa)
{
recolorEnabled = true;
recolor = color;
recolorOpa = opa;
}
/**
* @brief Disable recolor feature for subsequent calls to lv_gpu_nxp_pxp_blit()
*/
void lv_gpu_nxp_pxp_disable_recolor(void)
{
recolorEnabled = false;
}
/**********************
* STATIC FUNCTIONS
**********************/
/**
* @brief Start PXP job and wait for results
*
* Function used internally to start PXP task according current device
* configuration.
*/
static void lv_gpu_nxp_pxp_run(void)
{
lv_disp_t * disp = _lv_refr_get_disp_refreshing();
if(disp && disp->driver->clean_dcache_cb) { /* Clean & invalidate cache */
disp->driver->clean_dcache_cb(disp->driver);
}
pxp_cfg.pxp_run();
}
/**
* @brief BLock Image Transfer - copy rectangular image from src buffer to dst buffer with recoloring.
*
* Note that color keying and recoloring at the same time is not supported and black rectangle is rendered.
*
* @param[in/out] dest destination buffer
* @param[in] dest_width width (stride) of destination buffer in pixels
* @param[in] src source buffer
* @param[in] src_with width (stride) of source buffer in pixels
* @param[in] copy_w width of area to be copied from src to dest
* @param[in] copy_h height of area to be copied from src to dest
* @param[in] opa opacity of the result
* @param[in] recolor recolor value
* @param[in] recolorOpa effect opacity
*/
static void lv_gpu_nxp_pxp_blit_recolor(lv_color_t * dest, lv_coord_t dest_width, const lv_color_t * src,
lv_coord_t src_width,
lv_coord_t copy_width, lv_coord_t copy_height, lv_opa_t opa, lv_color_t recolor, lv_opa_t recolorOpa)
{
pxp_output_buffer_config_t outputBufferConfig;
pxp_as_buffer_config_t asBufferConfig;
if(colorKeyEnabled) {
/*should never get here, recolor & color keying not supported. Draw black box instead.*/
const lv_area_t fill_area = {.x1 = 0, .y1 = 0, .x2 = copy_width - 1, .y2 = copy_height - 1};
lv_gpu_nxp_pxp_fill(dest, dest_width, &fill_area, lv_color_black(), LV_OPA_MAX);
LV_LOG_WARN("Recoloring and color keying is not supported. Black rectangle rendered.");
return ;
}
else {
/*Recoloring without color keying*/
if(opa > LV_OPA_MAX) {
/*Recolor with full opacity - AS source image, PS color generator, OUT destination*/
PXP_Init(PXP);
PXP_EnableCsc1(PXP, false); /*Disable CSC1, it is enabled by default.*/
PXP_SetProcessBlockSize(PXP, kPXP_BlockSize16); /*block size 16x16 for higher performance*/
/*AS buffer - source image*/
asBufferConfig.pixelFormat = PXP_AS_PIXEL_FORMAT;
asBufferConfig.bufferAddr = (uint32_t)src;
asBufferConfig.pitchBytes = src_width * sizeof(lv_color_t);
PXP_SetAlphaSurfaceBufferConfig(LV_GPU_NXP_PXP_ID, &asBufferConfig);
PXP_SetAlphaSurfacePosition(LV_GPU_NXP_PXP_ID, 0U, 0U, copy_width - 1U, copy_height - 1U);
/*Disable PS buffer, use as color generator*/
PXP_SetProcessSurfacePosition(LV_GPU_NXP_PXP_ID, 0xFFFFU, 0xFFFFU, 0U, 0U);
PXP_SetProcessSurfaceBackGroundColor(LV_GPU_NXP_PXP_ID, lv_color_to32(recolor));
/*Output buffer*/
outputBufferConfig.pixelFormat = (pxp_output_pixel_format_t)PXP_OUT_PIXEL_FORMAT;
outputBufferConfig.interlacedMode = kPXP_OutputProgressive;
outputBufferConfig.buffer0Addr = (uint32_t)dest;
outputBufferConfig.buffer1Addr = (uint32_t)0U;
outputBufferConfig.pitchBytes = dest_width * sizeof(lv_color_t);
outputBufferConfig.width = copy_width;
outputBufferConfig.height = copy_height;
PXP_SetOutputBufferConfig(LV_GPU_NXP_PXP_ID, &outputBufferConfig);
pxp_porter_duff_config_t pdConfig;
/*Configure Porter-Duff blending - For RGB 565 only!*/
pdConfig.enable = 1;
pdConfig.dstColorMode = kPXP_PorterDuffColorStraight;
pdConfig.srcColorMode = kPXP_PorterDuffColorStraight;
pdConfig.dstGlobalAlphaMode = kPXP_PorterDuffGlobalAlpha;
pdConfig.srcGlobalAlphaMode = kPXP_PorterDuffGlobalAlpha;
pdConfig.srcFactorMode = kPXP_PorterDuffFactorStraight;
pdConfig.dstFactorMode = kPXP_PorterDuffFactorStraight;
pdConfig.srcGlobalAlpha = recolorOpa;
pdConfig.dstGlobalAlpha = 255 - recolorOpa;
pdConfig.srcAlphaMode = kPXP_PorterDuffAlphaStraight; /*don't care*/
pdConfig.dstAlphaMode = kPXP_PorterDuffAlphaStraight; /*don't care*/
PXP_SetPorterDuffConfig(LV_GPU_NXP_PXP_ID, &pdConfig);
lv_gpu_nxp_pxp_run(); /*Start PXP task*/
}
else {
/*Recolor with transparency*/
/*Step 1: Recolor with full opacity to temporary buffer*/
lv_color_t * tmpBuf = (lv_color_t *)lv_mem_buf_get(copy_width * copy_height * sizeof(lv_color_t));
lv_gpu_nxp_pxp_blit_recolor(tmpBuf, copy_width, src, src_width, copy_width, copy_height, LV_OPA_COVER, recolor,
recolorOpa);
/*Step 2: BLIT temporary results with required opacity to output*/
lv_gpu_nxp_pxp_disable_recolor(); /*make sure to take BLIT path, not the recolor*/
lv_gpu_nxp_pxp_blit(dest, dest_width, tmpBuf, copy_width, copy_width, copy_height, opa);
lv_gpu_nxp_pxp_enable_recolor(recolor, recolorOpa); /*restore state*/
/*Step 3: Clean-up memory*/
lv_mem_buf_release(tmpBuf);
}
}
}
#endif /* LV_USE_GPU_NXP_PXP */

View File

@@ -0,0 +1,193 @@
/**
* @file lv_gpu_nxp_pxp.h
*
*/
/**
* MIT License
*
* Copyright (c) 2020 NXP
*
* 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.
*
*/
#ifndef LV_SRC_LV_GPU_LV_GPU_NXP_PXP_H_
#define LV_SRC_LV_GPU_LV_GPU_NXP_PXP_H_
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../../lv_conf_internal.h"
#if LV_USE_GPU_NXP_PXP
#include "../misc/lv_area.h"
#include "../misc/lv_color.h"
/*********************
* DEFINES
*********************/
/** PXP module instance to use*/
#define LV_GPU_NXP_PXP_ID PXP
/** PXP interrupt line I*/
#define LV_GPU_NXP_PXP_IRQ_ID PXP_IRQn
#ifndef LV_GPU_NXP_PXP_BLIT_SIZE_LIMIT
/** Minimum area (in pixels) for image copy with 100% opacity to be handled by PXP*/
#define LV_GPU_NXP_PXP_BLIT_SIZE_LIMIT 32
#endif
#ifndef LV_GPU_NXP_PXP_BLIT_OPA_SIZE_LIMIT
/** Minimum area (in pixels) for image copy with transparency to be handled by PXP*/
#define LV_GPU_NXP_PXP_BLIT_OPA_SIZE_LIMIT 16
#endif
#ifndef LV_GPU_NXP_PXP_BUFF_SYNC_BLIT_SIZE_LIMIT
/** Minimum invalidated area (in pixels) to be synchronized by PXP during buffer sync */
#define LV_GPU_NXP_PXP_BUFF_SYNC_BLIT_SIZE_LIMIT 32
#endif
#ifndef LV_GPU_NXP_PXP_FILL_SIZE_LIMIT
/** Minimum area (in pixels) to be filled by PXP with 100% opacity*/
#define LV_GPU_NXP_PXP_FILL_SIZE_LIMIT 64
#endif
#ifndef LV_GPU_NXP_PXP_FILL_OPA_SIZE_LIMIT
/** Minimum area (in pixels) to be filled by PXP with transparency*/
#define LV_GPU_NXP_PXP_FILL_OPA_SIZE_LIMIT 32
#endif
/**********************
* TYPEDEFS
**********************/
/**
* NXP PXP device configuration - call-backs used for
* interrupt init/wait/deinit.
*/
typedef struct {
/** Callback for PXP interrupt initialization*/
lv_res_t (*pxp_interrupt_init)(void);
/** Callback for PXP interrupt de-initialization*/
void (*pxp_interrupt_deinit)(void);
/** Callback that should start PXP and wait for operation complete*/
void (*pxp_run)(void);
} lv_nxp_pxp_cfg_t;
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
/**
* Reset and initialize PXP device. This function should be called as a part
* of display init sequence.
*
* @return LV_RES_OK: PXP init ok; LV_RES_INV: init error. See error log for more information.
*/
lv_res_t lv_gpu_nxp_pxp_init(lv_nxp_pxp_cfg_t * cfg);
/**
* Disable PXP device. Should be called during display deinit sequence.
*/
void lv_gpu_nxp_pxp_deinit(void);
/**
* Fill area, with optional opacity.
*
* @param[in/out] dest_buf destination buffer
* @param[in] dest_width width (stride) of destination buffer in pixels
* @param[in] fill_area area to fill
* @param[in] color color
* @param[in] opa transparency of the color
*/
void lv_gpu_nxp_pxp_fill(lv_color_t * dest_buf, lv_coord_t dest_width, const lv_area_t * fill_area, lv_color_t color,
lv_opa_t opa);
/**
* @brief BLock Image Transfer - copy rectangular image from src buffer to dst buffer with effects.
*
* By default, image is copied directly, with optional opacity configured by \p opa.
* Color keying can be enabled by calling lv_gpu_nxp_pxp_enable_color_key() before calling this function.
* Recoloring can be enabled by calling lv_gpu_nxp_pxp_enable_recolor() before calling this function.
* Note that color keying and recoloring at the same time is not supported and black rectangle is rendered.
*
* @param[in/out] dest destination buffer
* @param[in] dest_width width (stride) of destination buffer in pixels
* @param[in] src source buffer
* @param[in] src_with width (stride) of source buffer in pixels
* @param[in] copy_w width of area to be copied from src to dest
* @param[in] copy_h height of area to be copied from src to dest
* @param[in] opa opacity of the result
*/
void lv_gpu_nxp_pxp_blit(lv_color_t * dest, lv_coord_t dest_width, const lv_color_t * src, lv_coord_t src_width,
lv_coord_t copy_width, lv_coord_t copy_height, lv_opa_t opa);
/**
* @brief Enable color keying for subsequent calls to lv_gpu_nxp_pxp_blit()
*
* Color key is defined by LV_COLOR_TRANSP symbol in lv_conf.h
*/
void lv_gpu_nxp_pxp_enable_color_key(void);
/**
* @brief Disable color keying for subsequent calls to lv_gpu_nxp_pxp_blit()
*
*/
void lv_gpu_nxp_pxp_disable_color_key(void);
/**
* @brief Enable recolor feature for subsequent calls to lv_gpu_nxp_pxp_blit()
*
* @param[in] color recolor value
* @param[in] opa effect opacity
*/
void lv_gpu_nxp_pxp_enable_recolor(lv_color_t color, lv_opa_t opa);
/**
* @brief Disable recolor feature for subsequent calls to lv_gpu_nxp_pxp_blit()
*/
void lv_gpu_nxp_pxp_disable_recolor(void);
/**********************
* STATIC FUNCTIONS
**********************/
#endif /*LV_USE_GPU_NXP_PXP*/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_SRC_LV_GPU_LV_GPU_NXP_PXP_H_*/

View File

@@ -0,0 +1,166 @@
/**
* @file lv_gpu_nxp_pxp_osa.c
*
*/
/**
* MIT License
*
* Copyright (c) 2020 NXP
*
* 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.
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_gpu_nxp_pxp_osa.h"
#if LV_USE_GPU_NXP_PXP && LV_USE_GPU_NXP_PXP_AUTO_INIT
#include "../misc/lv_log.h"
#include "lv_gpu_nxp_pxp.h"
#include "fsl_pxp.h"
#if defined(SDK_OS_FREE_RTOS)
#include "FreeRTOS.h"
#include "semphr.h"
#endif
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static lv_res_t _lv_gpu_nxp_pxp_interrupt_init(void);
static void _lv_gpu_nxp_pxp_interrupt_deinit(void);
static void _lv_gpu_nxp_pxp_run(void);
/**********************
* STATIC VARIABLES
**********************/
#if defined(SDK_OS_FREE_RTOS)
static SemaphoreHandle_t s_pxpIdle;
#else
static volatile bool s_pxpIdle;
#endif
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
/**
* PXP device interrupt handler. Used to check PXP task completion status.
*/
void PXP_IRQHandler(void)
{
#if defined(SDK_OS_FREE_RTOS)
BaseType_t taskAwake = pdFALSE;
#endif
if(kPXP_CompleteFlag & PXP_GetStatusFlags(LV_GPU_NXP_PXP_ID)) {
PXP_ClearStatusFlags(LV_GPU_NXP_PXP_ID, kPXP_CompleteFlag);
#if defined(SDK_OS_FREE_RTOS)
xSemaphoreGiveFromISR(s_pxpIdle, &taskAwake);
portYIELD_FROM_ISR(taskAwake);
#else
s_pxpIdle = true;
#endif
}
}
/**********************
* STATIC FUNCTIONS
**********************/
/**
* PXP interrupt initialization.
*/
static lv_res_t _lv_gpu_nxp_pxp_interrupt_init(void)
{
#if defined(SDK_OS_FREE_RTOS)
s_pxpIdle = xSemaphoreCreateBinary();
if(s_pxpIdle == NULL) {
return LV_RES_INV;
}
NVIC_SetPriority(LV_GPU_NXP_PXP_IRQ_ID, configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY + 1);
#else
s_pxpIdle = true;
#endif
NVIC_EnableIRQ(LV_GPU_NXP_PXP_IRQ_ID);
return LV_RES_OK;
}
/**
* PXP interrupt de-initialization.
*/
static void _lv_gpu_nxp_pxp_interrupt_deinit(void)
{
NVIC_DisableIRQ(LV_GPU_NXP_PXP_IRQ_ID);
#if defined(SDK_OS_FREE_RTOS)
vSemaphoreDelete(s_pxpIdle);
#endif
}
/**
* Function to start PXP job. This function must wait for task complete.
*/
static void _lv_gpu_nxp_pxp_run(void)
{
#if !defined(SDK_OS_FREE_RTOS)
s_pxpIdle = false;
#endif
PXP_EnableInterrupts(LV_GPU_NXP_PXP_ID, kPXP_CompleteInterruptEnable);
PXP_Start(LV_GPU_NXP_PXP_ID);
#if defined(SDK_OS_FREE_RTOS)
if(xSemaphoreTake(s_pxpIdle, portMAX_DELAY) != pdTRUE) {
LV_LOG_ERROR("xSemaphoreTake error. Task halted.");
for(; ;) ;
}
#else
while(s_pxpIdle == false) {
}
#endif
}
lv_nxp_pxp_cfg_t pxp_default_cfg = {
.pxp_interrupt_init = _lv_gpu_nxp_pxp_interrupt_init,
.pxp_interrupt_deinit = _lv_gpu_nxp_pxp_interrupt_deinit,
.pxp_run = _lv_gpu_nxp_pxp_run
};
#endif /*LV_USE_GPU_NXP_PXP && LV_USE_GPU_NXP_PXP_AUTO_INIT*/

View File

@@ -0,0 +1,47 @@
/**
* @file lv_gpu_nxp_pxp_osa.h
*
*/
/**
* MIT License
*
* Copyright (c) 2020 NXP
*
* 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.
*
*/
#ifndef LV_SRC_LV_GPU_LV_GPU_NXP_PXP_OSA_H_
#define LV_SRC_LV_GPU_LV_GPU_NXP_PXP_OSA_H_
#ifdef __cplusplus
extern "C" {
#endif
#include "../../lv_conf_internal.h"
#if LV_USE_GPU_NXP_PXP && LV_USE_GPU_NXP_PXP_AUTO_INIT
extern lv_nxp_pxp_cfg_t pxp_default_cfg;
#endif
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_SRC_LV_GPU_LV_GPU_NXP_PXP_OSA_H_*/

View File

@@ -0,0 +1,6 @@
CSRCS += lv_gpu_nxp_vglite.c
DEPPATH += --dep-path $(LVGL_DIR)/$(LVGL_DIR_NAME)/src/draw/nxp_vglite
VPATH += :$(LVGL_DIR)/$(LVGL_DIR_NAME)/src/draw/nxp_vglite
CFLAGS += "-I$(LVGL_DIR)/$(LVGL_DIR_NAME)/src/draw/nxp_vglite"

View File

@@ -0,0 +1,770 @@
/**
* @file lv_gpu_nxp_vglite.c
*
*/
/**
* MIT License
*
* Copyright (c) 2020 NXP
*
* 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.
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_gpu_nxp_vglite.h"
#if LV_USE_GPU_NXP_VG_LITE
#include "lvgl.h"
#include "../misc/lv_log.h"
#include "fsl_cache.h"
#include "vg_lite.h"
#include "fsl_debug_console.h"
/*********************
* DEFINES
*********************/
#if LV_COLOR_DEPTH==16
#define VGLITE_PX_FMT VG_LITE_RGB565
#else
#error Only 16bit color depth is supported. Set LV_COLOR_DEPTH to 16.
#endif
/* Enable BLIT quality degradation workaround for RT595 */
#define RT595_BLIT_WRKRND_ENABLED 1
/* If LV_HOR_RES_MAX/LV_VER_RES_MAX is higher than this value, workaround will be enabled */
#define RT595_BLIT_WRKRND_THR 352
/* Print detailed info to SDK console (NOT to LVGL log system) */
#define BLIT_DBG_VERBOSE 0
/* Draw rectangles around BLIT tiles */
#define BLIT_DBG_AREAS 0
/* Redirect PRINT to SDK PRINTF */
#define PRINT PRINTF
/* Verbose debug print */
#if BLIT_DBG_VERBOSE
#define PRINT_BLT PRINTF
#else
#define PRINT_BLT(...)
#endif
/* Internal compound symbol */
#if (defined(CPU_MIMXRT595SFFOB) || defined(CPU_MIMXRT595SFFOB_cm33) || \
defined(CPU_MIMXRT595SFFOC) || defined(CPU_MIMXRT595SFFOC_cm33)) && \
((LV_HOR_RES_MAX > RT595_BLIT_WRKRND_THR) || (LV_VER_RES_MAX > RT595_BLIT_WRKRND_THR)) && \
RT595_BLIT_WRKRND_ENABLED
#define _BLIT_SPLIT_ENABLED 1
#else
#define _BLIT_SPLIT_ENABLED 0
#endif
/* BLIT split threshold - BLITs with width or height higher than this value will be done
* in multiple steps. Value must be 16-aligned. Don't change.
* */
#define LV_GPU_NXP_VG_LITE_BLIT_SPLIT_THR 352
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static lv_res_t _init_vg_buf(vg_lite_buffer_t * dst, uint32_t width, uint32_t height, uint32_t stride,
const lv_color_t * ptr, bool source);
static lv_res_t _lv_gpu_nxp_vglite_blit_single(lv_gpu_nxp_vglite_blit_info_t * blit);
#if _BLIT_SPLIT_ENABLED
static void _align_x(lv_area_t * area, lv_color_t ** buf);
static void _align_y(lv_area_t * area, lv_color_t ** buf, uint32_t stridePx);
static void _sw_blit(lv_gpu_nxp_vglite_blit_info_t * blit);
#if BLIT_DBG_AREAS
static void _draw_rectangle(lv_color_t * dest_buf, lv_coord_t dest_width, lv_coord_t dest_height,
lv_area_t * fill_area, lv_color_t color);
#endif
static lv_res_t _lv_gpu_nxp_vglite_check_blit(lv_gpu_nxp_vglite_blit_info_t * blit);
#endif
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
#define CHECK(cond, txt) \
do { \
if (cond) { \
PRINT("%s. STOP!\n", txt); \
for ( ; ; ); \
} \
} while(0)
/**********************
* GLOBAL FUNCTIONS
**********************/
/***
* Fills rectangular area in buffer.
* @param[in] dest_buf Destination buffer pointer (must be aligned on 32 bytes)
* @param[in] dest_width Destination buffer width in pixels (must be aligned on 16 px)
* @param[in] dest_height Destination buffer height in pixels
* @param[in] fill_area Area to be filled
* @param[in] color Fill color
* @param[in] opa Opacity (255 = full, 128 = 50% background/50% color, 0 = no fill)
* @retval LV_RES_OK Fill completed
* @retval LV_RES_INV Error occurred (\see LV_GPU_NXP_VG_LITE_LOG_ERRORS)
*/
lv_res_t lv_gpu_nxp_vglite_fill(lv_color_t * dest_buf, lv_coord_t dest_width, lv_coord_t dest_height,
const lv_area_t * fill_area, lv_color_t color, lv_opa_t opa)
{
vg_lite_buffer_t rt;
vg_lite_rectangle_t rect;
vg_lite_error_t err = VG_LITE_SUCCESS;
lv_color32_t col32 = {.full = lv_color_to32(color)}; /*Convert color to RGBA8888*/
lv_disp_t * disp = _lv_refr_get_disp_refreshing();
if(_init_vg_buf(&rt, (uint32_t) dest_width, (uint32_t) dest_height, (uint32_t) dest_width * sizeof(lv_color_t),
(const lv_color_t *) dest_buf, false) != LV_RES_OK) {
#if LV_GPU_NXP_VG_LITE_LOG_ERRORS
LV_LOG_ERROR("init_vg_buf reported error. Fill failed.");
#endif
return LV_RES_INV;
}
if(opa >= (lv_opa_t) LV_OPA_MAX) { /*Opaque fill*/
rect.x = fill_area->x1;
rect.y = fill_area->y1;
rect.width = (int32_t) fill_area->x2 - (int32_t) fill_area->x1 + 1;
rect.height = (int32_t) fill_area->y2 - (int32_t) fill_area->y1 + 1;
if(disp != NULL && disp->driver->clean_dcache_cb != NULL) { /*Clean & invalidate cache*/
disp->driver->clean_dcache_cb(disp->driver);
}
err = vg_lite_clear(&rt, &rect, col32.full);
if(err != VG_LITE_SUCCESS) {
#if LV_GPU_NXP_VG_LITE_LOG_ERRORS
LV_LOG_ERROR("vg_lite_clear reported error. Fill failed.");
#endif
return LV_RES_INV;
}
err = vg_lite_finish();
if(err != VG_LITE_SUCCESS) {
#if LV_GPU_NXP_VG_LITE_LOG_ERRORS
LV_LOG_ERROR("vg_lite_finish reported error. Fill failed.");
#endif
return LV_RES_INV;
}
}
else { /*fill with transparency*/
vg_lite_path_t path;
lv_color32_t colMix;
int16_t path_data[] = { /*VG rectangular path*/
VLC_OP_MOVE, fill_area->x1, fill_area->y1,
VLC_OP_LINE, fill_area->x2 + 1, fill_area->y1,
VLC_OP_LINE, fill_area->x2 + 1, fill_area->y2 + 1,
VLC_OP_LINE, fill_area->x1, fill_area->y2 + 1,
VLC_OP_LINE, fill_area->x1, fill_area->y1,
VLC_OP_END
};
err = vg_lite_init_path(&path, VG_LITE_S16, VG_LITE_LOW, sizeof(path_data), path_data,
(vg_lite_float_t) fill_area->x1, (vg_lite_float_t) fill_area->y1, ((vg_lite_float_t) fill_area->x2) + 1.0f,
((vg_lite_float_t) fill_area->y2) + 1.0f);
if(err != VG_LITE_SUCCESS) {
#if LV_GPU_NXP_VG_LITE_LOG_ERRORS
LV_LOG_ERROR("vg_lite_init_path() failed.");
#endif
return LV_RES_INV;
}
colMix.ch.red = (uint8_t)(((uint16_t)col32.ch.red * opa) >> 8); /*Pre-multiply color*/
colMix.ch.green = (uint8_t)(((uint16_t)col32.ch.green * opa) >> 8);
colMix.ch.blue = (uint8_t)(((uint16_t)col32.ch.blue * opa) >> 8);
colMix.ch.alpha = opa;
if((disp != NULL) && (disp->driver->clean_dcache_cb != NULL)) { /*Clean & invalidate cache*/
disp->driver->clean_dcache_cb(disp->driver);
}
vg_lite_matrix_t matrix;
vg_lite_identity(&matrix);
/*Draw rectangle*/
err = vg_lite_draw(&rt, &path, VG_LITE_FILL_EVEN_ODD, &matrix, VG_LITE_BLEND_SRC_OVER, colMix.full);
if(err != VG_LITE_SUCCESS) {
#if LV_GPU_NXP_VG_LITE_LOG_ERRORS
LV_LOG_ERROR("vg_lite_draw() failed.");
#endif
vg_lite_clear_path(&path);
return LV_RES_INV;
}
err = vg_lite_finish();
if(err != VG_LITE_SUCCESS) {
#if LV_GPU_NXP_VG_LITE_LOG_ERRORS
LV_LOG_ERROR("vg_lite_finish() failed.");
#endif
return LV_RES_INV;
}
err = vg_lite_clear_path(&path);
if(err != VG_LITE_SUCCESS) {
#if LV_GPU_NXP_VG_LITE_LOG_ERRORS
LV_LOG_ERROR("vg_lite_clear_path() failed.");
#endif
return LV_RES_INV;
}
}
if(err == VG_LITE_SUCCESS) {
return LV_RES_OK;
}
else {
#if LV_GPU_NXP_VG_LITE_LOG_ERRORS
LV_LOG_ERROR("VG Lite Fill failed.");
#endif
return LV_RES_INV;
}
}
/***
* BLock Image Transfer.
* @param[in] blit Description of the transfer
* @retval LV_RES_OK Transfer complete
* @retval LV_RES_INV Error occurred (\see LV_GPU_NXP_VG_LITE_LOG_ERRORS)
*/
lv_res_t lv_gpu_nxp_vglite_blit(lv_gpu_nxp_vglite_blit_info_t * blit)
{
#if _BLIT_SPLIT_ENABLED
lv_res_t rv = LV_RES_INV;
if(_lv_gpu_nxp_vglite_check_blit(blit) != LV_RES_OK) {
PRINT_BLT("Blit check failed\n");
return LV_RES_INV;
}
PRINT_BLT("BLIT from: "
"Area: %03d,%03d - %03d,%03d "
"Addr: %d\n\n",
blit->src_area.x1, blit->src_area.y1,
blit->src_area.x2, blit->src_area.y2,
(uintptr_t) blit->src);
PRINT_BLT("BLIT to: "
"Area: %03d,%03d - %03d,%03d "
"Addr: %d\n\n",
blit->dst_area.x1, blit->dst_area.y1,
blit->dst_area.x2, blit->dst_area.y2,
(uintptr_t) blit->src);
/* Stage 1: Move starting pointers as close as possible to [x1, y1], so coordinates are as small as possible. */
_align_x(&blit->src_area, (lv_color_t **)&blit->src);
_align_y(&blit->src_area, (lv_color_t **)&blit->src, blit->src_stride / sizeof(lv_color_t));
_align_x(&blit->dst_area, (lv_color_t **)&blit->dst);
_align_y(&blit->dst_area, (lv_color_t **)&blit->dst, blit->dst_stride / sizeof(lv_color_t));
/* Stage 2: If we're in limit, do a single BLIT */
if((blit->src_area.x2 < LV_GPU_NXP_VG_LITE_BLIT_SPLIT_THR) &&
(blit->src_area.y2 < LV_GPU_NXP_VG_LITE_BLIT_SPLIT_THR)) {
PRINT_BLT("Simple blit!\n");
return _lv_gpu_nxp_vglite_blit_single(blit);
};
/* Stage 3: Split the BLIT into multiple tiles */
PRINT_BLT("Split blit!\n");
PRINT_BLT("Blit "
"([%03d,%03d], [%03d,%03d]) -> "
"([%03d,%03d], [%03d,%03d]) | "
"([%03dx%03d] -> [%03dx%03d]) | "
"A:(%d -> %d)\n",
blit->src_area.x1, blit->src_area.y1, blit->src_area.x2, blit->src_area.y2,
blit->dst_area.x1, blit->dst_area.y1, blit->dst_area.x2, blit->dst_area.y2,
lv_area_get_width(&blit->src_area), lv_area_get_height(&blit->src_area),
lv_area_get_width(&blit->dst_area), lv_area_get_height(&blit->dst_area),
(uintptr_t) blit->src, (uintptr_t) blit->dst);
uint32_t totalWidth = lv_area_get_width(&blit->src_area);
uint32_t totalHeight = lv_area_get_height(&blit->src_area);
lv_gpu_nxp_vglite_blit_info_t tileBlit;
/* Number of tiles needed */
int totalTilesX = (blit->src_area.x1 + totalWidth + LV_GPU_NXP_VG_LITE_BLIT_SPLIT_THR - 1) /
LV_GPU_NXP_VG_LITE_BLIT_SPLIT_THR;
int totalTilesY = (blit->src_area.y1 + totalHeight + LV_GPU_NXP_VG_LITE_BLIT_SPLIT_THR - 1) /
LV_GPU_NXP_VG_LITE_BLIT_SPLIT_THR;
/* src and dst buffer shift against each other. Src buffer real data [0,0] may start actually at [3,0] in buffer, as
* the buffer pointer has to be aligned, while dst buffer real data [0,0] may start at [1,0] in buffer. alignment may be
* different */
int shiftSrcX = (blit->src_area.x1 > blit->dst_area.x1) ? (blit->src_area.x1 - blit->dst_area.x1) : 0;
int shiftDstX = (blit->src_area.x1 < blit->dst_area.x1) ? (blit->dst_area.x1 - blit->src_area.x1) : 0;
PRINT_BLT("\n");
PRINT_BLT("Align shift: src: %d, dst: %d\n", shiftSrcX, shiftDstX);
tileBlit = *blit;
for(int tileY = 0; tileY < totalTilesY; tileY++) {
tileBlit.src_area.y1 = 0; /* no vertical alignment, always start from 0 */
tileBlit.src_area.y2 = totalHeight - tileY * LV_GPU_NXP_VG_LITE_BLIT_SPLIT_THR - 1;
if(tileBlit.src_area.y2 >= LV_GPU_NXP_VG_LITE_BLIT_SPLIT_THR) {
tileBlit.src_area.y2 = LV_GPU_NXP_VG_LITE_BLIT_SPLIT_THR - 1; /* Should never happen */
}
tileBlit.src = blit->src + tileY * LV_GPU_NXP_VG_LITE_BLIT_SPLIT_THR * blit->src_stride / sizeof(
lv_color_t); /* stride in px! */
tileBlit.dst_area.y1 = tileBlit.src_area.y1; /* y has no alignment, always in sync with src */
tileBlit.dst_area.y2 = tileBlit.src_area.y2;
tileBlit.dst = blit->dst + tileY * LV_GPU_NXP_VG_LITE_BLIT_SPLIT_THR * blit->dst_stride / sizeof(
lv_color_t); /* stride in px! */
for(int tileX = 0; tileX < totalTilesX; tileX++) {
if(tileX == 0) {
/* 1st tile is special - there may be a gap between buffer start pointer
* and area.x1 value, as the pointer has to be aligned.
* tileBlit.src pointer - keep init value from Y-loop.
* Also, 1st tile start is not shifted! shift is applied from 2nd tile */
tileBlit.src_area.x1 = blit->src_area.x1;
tileBlit.dst_area.x1 = blit->dst_area.x1;
}
else {
/* subsequent tiles always starts from 0, but shifted*/
tileBlit.src_area.x1 = 0 + shiftSrcX;
tileBlit.dst_area.x1 = 0 + shiftDstX;
/* and advance start pointer + 1 tile size */
tileBlit.src += LV_GPU_NXP_VG_LITE_BLIT_SPLIT_THR;
tileBlit.dst += LV_GPU_NXP_VG_LITE_BLIT_SPLIT_THR;
}
/* Clip tile end coordinates */
tileBlit.src_area.x2 = totalWidth + blit->src_area.x1 - tileX * LV_GPU_NXP_VG_LITE_BLIT_SPLIT_THR - 1;
if(tileBlit.src_area.x2 >= LV_GPU_NXP_VG_LITE_BLIT_SPLIT_THR) {
tileBlit.src_area.x2 = LV_GPU_NXP_VG_LITE_BLIT_SPLIT_THR - 1;
}
tileBlit.dst_area.x2 = totalWidth + blit->dst_area.x1 - tileX * LV_GPU_NXP_VG_LITE_BLIT_SPLIT_THR - 1;
if(tileBlit.dst_area.x2 >= LV_GPU_NXP_VG_LITE_BLIT_SPLIT_THR) {
tileBlit.dst_area.x2 = LV_GPU_NXP_VG_LITE_BLIT_SPLIT_THR - 1;
}
if(tileX < (totalTilesX - 1)) {
/* And adjust end coords if shifted, but not for last tile! */
tileBlit.src_area.x2 += shiftSrcX;
tileBlit.dst_area.x2 += shiftDstX;
}
rv = _lv_gpu_nxp_vglite_blit_single(&tileBlit);
#if BLIT_DBG_AREAS
_draw_rectangle((lv_color_t *) tileBlit.dst, tileBlit.dst_width, tileBlit.dst_height, &tileBlit.dst_area, LV_COLOR_RED);
_draw_rectangle((lv_color_t *) tileBlit.src, tileBlit.src_width, tileBlit.src_height, &tileBlit.src_area,
LV_COLOR_GREEN);
#endif
PRINT_BLT("Tile [%d, %d]: "
"([%d,%d], [%d,%d]) -> "
"([%d,%d], [%d,%d]) | "
"([%dx%d] -> [%dx%d]) | "
"A:(0x%8X -> 0x%8X) %s\n",
tileX, tileY,
tileBlit.src_area.x1, tileBlit.src_area.y1, tileBlit.src_area.x2, tileBlit.src_area.y2,
tileBlit.dst_area.x1, tileBlit.dst_area.y1, tileBlit.dst_area.x2, tileBlit.dst_area.y2,
lv_area_get_width(&tileBlit.src_area), lv_area_get_height(&tileBlit.src_area),
lv_area_get_width(&tileBlit.dst_area), lv_area_get_height(&tileBlit.dst_area),
(uintptr_t) tileBlit.src, (uintptr_t) tileBlit.dst,
rv == LV_RES_OK ? "OK!" : "!!! FAILED !!!");
if(rv != LV_RES_OK) { /* if anything goes wrong... */
#if LV_GPU_NXP_VG_LITE_LOG_ERRORS
LV_LOG_ERROR("Split BLIT failed. Trying SW BLIT instead.");
#endif
_sw_blit(&tileBlit);
rv = LV_RES_OK; /* Don't report error, as SW BLIT was performed */
}
}
PRINT_BLT(" \n");
}
return rv; /* should never fail */
#else /* non RT595 */
/* Just pass down */
return _lv_gpu_nxp_vglite_blit_single(blit);
#endif
}
/**********************
* STATIC FUNCTIONS
**********************/
/***
* BLock Image Transfer - single direct BLIT.
* @param[in] blit Description of the transfer
* @retval LV_RES_OK Transfer complete
* @retval LV_RES_INV Error occurred (\see LV_GPU_NXP_VG_LITE_LOG_ERRORS)
*/
static lv_res_t _lv_gpu_nxp_vglite_blit_single(lv_gpu_nxp_vglite_blit_info_t * blit)
{
vg_lite_buffer_t src_vgbuf, dst_vgbuf;
vg_lite_error_t err = VG_LITE_SUCCESS;
uint32_t rect[4];
lv_disp_t * disp = _lv_refr_get_disp_refreshing();
if(blit == NULL) {
/*Wrong parameter*/
return LV_RES_INV;
}
if(blit->opa < (lv_opa_t) LV_OPA_MIN) {
return LV_RES_OK; /*Nothing to BLIT*/
}
/*Wrap src/dst buffer into VG-Lite buffer*/
if(_init_vg_buf(&src_vgbuf, (uint32_t) blit->src_width, (uint32_t) blit->src_height, (uint32_t) blit->src_stride,
blit->src, true) != LV_RES_OK) {
#if LV_GPU_NXP_VG_LITE_LOG_ERRORS
LV_LOG_ERROR("init_vg_buf reported error. BLIT failed.");
#endif
return LV_RES_INV;
}
if(_init_vg_buf(&dst_vgbuf, (uint32_t) blit->dst_width, (uint32_t) blit->dst_height, (uint32_t) blit->dst_stride,
blit->dst, false) != LV_RES_OK) {
#if LV_GPU_NXP_VG_LITE_LOG_ERRORS
LV_LOG_ERROR("init_vg_buf reported error. BLIT failed.");
#endif
return LV_RES_INV;
}
rect[0] = (uint32_t) blit->src_area.x1; /* start x */
rect[1] = (uint32_t) blit->src_area.y1; /* start y */
rect[2] = (uint32_t) blit->src_area.x2 - (uint32_t) blit->src_area.x1 + 1U; /* width */
rect[3] = (uint32_t) blit->src_area.y2 - (uint32_t) blit->src_area.y1 + 1U; /* height */
vg_lite_matrix_t matrix;
vg_lite_identity(&matrix);
vg_lite_translate((vg_lite_float_t)blit->dst_area.x1, (vg_lite_float_t)blit->dst_area.y1, &matrix);
if((disp != NULL) && (disp->driver->clean_dcache_cb != NULL)) { /*Clean & invalidate cache*/
disp->driver->clean_dcache_cb(disp->driver);
}
uint32_t color;
vg_lite_blend_t blend;
if(blit->opa >= (uint8_t) LV_OPA_MAX) {
color = 0x0;
blend = VG_LITE_BLEND_NONE;
}
else {
uint32_t opa = (uint32_t) blit->opa;
color = (opa << 24) | (opa << 16) | (opa << 8) | opa;
blend = VG_LITE_BLEND_SRC_OVER;
src_vgbuf.image_mode = VG_LITE_MULTIPLY_IMAGE_MODE;
}
err = vg_lite_blit_rect(&dst_vgbuf, &src_vgbuf, rect, &matrix, blend, color, VG_LITE_FILTER_POINT);
if(err != VG_LITE_SUCCESS) {
#if LV_GPU_NXP_VG_LITE_LOG_ERRORS
LV_LOG_ERROR("vg_lite_blit_rect() failed.");
#endif
return LV_RES_INV;
}
err = vg_lite_finish();
if(err != VG_LITE_SUCCESS) {
#if LV_GPU_NXP_VG_LITE_LOG_ERRORS
LV_LOG_ERROR("vg_lite_finish() failed.");
#endif
return LV_RES_INV;
}
if(err == VG_LITE_SUCCESS) {
return LV_RES_OK;
}
else {
#if LV_GPU_NXP_VG_LITE_LOG_ERRORS
LV_LOG_ERROR("vg_lite_blit_rect or vg_lite_finish reported error. BLIT failed.");
#endif
return LV_RES_INV;
}
}
/***
* Fills vg_lite_buffer_t structure according given parameters.
* @param[out] dst Buffer structure to be filled
* @param[in] width Width of buffer in pixels
* @param[in] height Height of buffer in pixels
* @param[in] stride Stride of the buffer in bytes
* @param[in] ptr Pointer to the buffer (must be aligned according VG-Lite requirements)
*/
static lv_res_t _init_vg_buf(vg_lite_buffer_t * dst, uint32_t width, uint32_t height, uint32_t stride,
const lv_color_t * ptr, bool source)
{
if((((uintptr_t)ptr) % (uintptr_t)LV_ATTRIBUTE_MEM_ALIGN_SIZE) != 0x0U) { /*Test for alignment*/
#if LV_GPU_NXP_VG_LITE_LOG_ERRORS
LV_LOG_ERROR("ptr (0x%X) not aligned to %d.", (size_t) ptr, LV_ATTRIBUTE_MEM_ALIGN_SIZE);
#endif
return LV_RES_INV;
}
if(source &&
(stride % (LV_GPU_NXP_VG_LITE_STRIDE_ALIGN_PX * sizeof(lv_color_t))) != 0x0U) { /*Test for stride alignment*/
#if LV_GPU_NXP_VG_LITE_LOG_ERRORS
LV_LOG_ERROR("Buffer stride (%d px) not aligned to %d bytes.", stride,
LV_GPU_NXP_VG_LITE_STRIDE_ALIGN_PX * sizeof(lv_color_t));
#endif
return LV_RES_INV;
}
dst->format = VGLITE_PX_FMT;
dst->tiled = VG_LITE_LINEAR;
dst->image_mode = VG_LITE_NORMAL_IMAGE_MODE;
dst->transparency_mode = VG_LITE_IMAGE_OPAQUE;
dst->width = (int32_t) width;
dst->height = (int32_t) height;
dst->stride = (int32_t) stride;
void * r_ptr = memset(&dst->yuv, 0, sizeof(dst->yuv));
if(r_ptr == NULL) {
return LV_RES_INV;
}
dst->memory = (void *)ptr;
dst->address = (uint32_t) dst->memory;
dst->handle = NULL;
return LV_RES_OK;
}
#if _BLIT_SPLIT_ENABLED
/**
* Software BLIT as a fall-back scenario
* @param[in] blit BLIT configuration
*/
static void _sw_blit(lv_gpu_nxp_vglite_blit_info_t * blit)
{
int x, y;
lv_coord_t w = lv_area_get_width(&blit->src_area);
lv_coord_t h = lv_area_get_height(&blit->src_area);
uint32_t srcStridePx = blit->src_stride / sizeof(lv_color_t);
uint32_t dstStridePx = blit->dst_stride / sizeof(lv_color_t);
lv_color_t * src = (lv_color_t *)blit->src + blit->src_area.y1 * srcStridePx + blit->src_area.x1;
lv_color_t * dst = (lv_color_t *)blit->dst + blit->dst_area.y1 * dstStridePx + blit->dst_area.x1;
if(blit->opa >= LV_OPA_MAX) {
/* simple copy */
for(y = 0; y < h; y++) {
_lv_memcpy(dst, src, w * sizeof(lv_color_t));
src += srcStridePx;
dst += dstStridePx;
}
}
else if(blit->opa >= LV_OPA_MIN) {
/* alpha blending */
for(y = 0; y < h; y++) {
for(x = 0; x < w; x++) {
dst[x] = lv_color_mix(src[x], dst[x], blit->opa);
}
src += srcStridePx;
dst += dstStridePx;
}
}
}
/**
* Verify BLIT structure - widths, stride, pointer alignment
* @param[in] blit
* @return
*/
static lv_res_t _lv_gpu_nxp_vglite_check_blit(lv_gpu_nxp_vglite_blit_info_t * blit)
{
if(lv_area_get_width(&blit->src_area) < LV_GPU_NXP_VG_LITE_STRIDE_ALIGN_PX) { /* Test for minimal width */
#if LV_GPU_NXP_VG_LITE_LOG_ERRORS
LV_LOG_ERROR("source area width (%d) is smaller than required (%d).", lv_area_get_width(&blit->src_area),
LV_GPU_NXP_VG_LITE_STRIDE_ALIGN_PX);
#endif
return LV_RES_INV;
}
if(lv_area_get_width(&blit->dst_area) < LV_GPU_NXP_VG_LITE_STRIDE_ALIGN_PX) { /* Test for minimal width */
#if LV_GPU_NXP_VG_LITE_LOG_ERRORS
LV_LOG_ERROR("destination area width (%d) is smaller than required (%d).", lv_area_get_width(&blit->dst_area),
LV_GPU_NXP_VG_LITE_STRIDE_ALIGN_PX);
#endif
return LV_RES_INV;
}
if((((uintptr_t) blit->src) % LV_ATTRIBUTE_MEM_ALIGN_SIZE) != 0x0) { /* Test for pointer alignment */
#if LV_GPU_NXP_VG_LITE_LOG_ERRORS
LV_LOG_ERROR("source buffer ptr (0x%X) not aligned to %d.", (size_t) blit->src, LV_ATTRIBUTE_MEM_ALIGN_SIZE);
#endif
return LV_RES_INV;
}
/* No alignment requirement for destination pixel buffer when using mode VG_LITE_LINEAR */
if((blit->src_stride % (LV_GPU_NXP_VG_LITE_STRIDE_ALIGN_PX * LV_COLOR_DEPTH / 8)) !=
0x0) { /* Test for stride alignment */
#if LV_GPU_NXP_VG_LITE_LOG_ERRORS
LV_LOG_ERROR("source buffer stride (%d px) not aligned to %d px.", blit->src_stride,
LV_GPU_NXP_VG_LITE_STRIDE_ALIGN_PX);
#endif
return LV_RES_INV;
}
if((blit->dst_stride % (LV_GPU_NXP_VG_LITE_STRIDE_ALIGN_PX * LV_COLOR_DEPTH / 8)) !=
0x0) { /* Test for stride alignment */
#if LV_GPU_NXP_VG_LITE_LOG_ERRORS
LV_LOG_ERROR("destination buffer stride (%d px) not aligned to %d px.", blit->dst_stride,
LV_GPU_NXP_VG_LITE_STRIDE_ALIGN_PX);
#endif
return LV_RES_INV;
}
if((lv_area_get_width(&blit->src_area) != lv_area_get_width(&blit->dst_area)) ||
(lv_area_get_height(&blit->src_area) != lv_area_get_height(&blit->dst_area))) {
#if LV_GPU_NXP_VG_LITE_LOG_ERRORS
LV_LOG_ERROR("source and destination buffer areas are not equal.");
#endif
return LV_RES_INV;
}
return LV_RES_OK;
}
/***
* Move buffer pointer as close as possible to area, but with respect to alignment requirements. X-axis only.
* @param[in,out] area Area to be updated
* @param[in,out] buf Pointer to be updated
*/
static void _align_x(lv_area_t * area, lv_color_t ** buf)
{
int alignedAreaStartPx = area->x1 - (area->x1 % (LV_ATTRIBUTE_MEM_ALIGN_SIZE * 8 / LV_COLOR_DEPTH));
CHECK(alignedAreaStartPx < 0, "Should never happen.");
area->x1 -= alignedAreaStartPx;
area->x2 -= alignedAreaStartPx;
*buf += alignedAreaStartPx;
}
/***
* Move buffer pointer to the area start and update variables, Y-axis only.
* @param[in,out] area Area to be updated
* @param[in,out] buf Pointer to be updated
* @param[in] stridePx Buffer stride in pixels
*/
static void _align_y(lv_area_t * area, lv_color_t ** buf, uint32_t stridePx)
{
int LineToAlignMem;
int alignedAreaStartPy;
/* find how many lines of pixels will respect memory alignment requirement */
if(stridePx % LV_ATTRIBUTE_MEM_ALIGN_SIZE == 0) {
alignedAreaStartPy = area->y1;
}
else {
LineToAlignMem = LV_ATTRIBUTE_MEM_ALIGN_SIZE / (sizeof(lv_color_t) * LV_GPU_NXP_VG_LITE_STRIDE_ALIGN_PX);
CHECK(LV_ATTRIBUTE_MEM_ALIGN_SIZE % (sizeof(lv_color_t) * LV_GPU_NXP_VG_LITE_STRIDE_ALIGN_PX) != 0,
"Complex case: need gcd function.");
alignedAreaStartPy = area->y1 - (area->y1 % LineToAlignMem);
CHECK(alignedAreaStartPy < 0, "Should never happen.");
}
area->y1 -= alignedAreaStartPy;
area->y2 -= alignedAreaStartPy;
*buf += alignedAreaStartPy * stridePx;
}
#if BLIT_DBG_AREAS
/***
* Draws a simple rectangle, 1 px line width.
* @param dest_buf Destination buffer
* @param dest_width Destination buffer width (must be aligned on 16px)
* @param dest_height Destination buffer height
* @param fill_area Rectangle coordinates
* @param color Rectangle color
*/
static void _draw_rectangle(lv_color_t * dest_buf, lv_coord_t dest_width, lv_coord_t dest_height,
lv_area_t * fill_area, lv_color_t color)
{
lv_area_t a;
/* top line */
a.x1 = fill_area->x1;
a.x2 = fill_area->x2;
a.y1 = fill_area->y1;
a.y2 = fill_area->y1;
lv_gpu_nxp_vglite_fill(dest_buf, dest_width, dest_height, &a, color, LV_OPA_COVER);
/* bottom line */
a.x1 = fill_area->x1;
a.x2 = fill_area->x2;
a.y1 = fill_area->y2;
a.y2 = fill_area->y2;
lv_gpu_nxp_vglite_fill(dest_buf, dest_width, dest_height, &a, color, LV_OPA_COVER);
/* left line */
a.x1 = fill_area->x1;
a.x2 = fill_area->x1;
a.y1 = fill_area->y1;
a.y2 = fill_area->y2;
lv_gpu_nxp_vglite_fill(dest_buf, dest_width, dest_height, &a, color, LV_OPA_COVER);
/* right line */
a.x1 = fill_area->x2;
a.x2 = fill_area->x2;
a.y1 = fill_area->y1;
a.y2 = fill_area->y2;
lv_gpu_nxp_vglite_fill(dest_buf, dest_width, dest_height, &a, color, LV_OPA_COVER);
}
#endif /* BLIT_DBG_AREAS */
#endif /* _BLIT_SPLIT_ENABLED */
#endif /*LV_USE_GPU_NXP_VG_LITE*/

View File

@@ -0,0 +1,145 @@
/**
* @file lv_gpu_nxp_vglite.h
*
*/
/**
* MIT License
*
* Copyright (c) 2020 NXP
*
* 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.
*
*/
#ifndef LV_SRC_LV_GPU_LV_GPU_NXP_VGLITE_H_
#define LV_SRC_LV_GPU_LV_GPU_NXP_VGLITE_H_
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../../lv_conf_internal.h"
#if LV_USE_GPU_NXP_VG_LITE
/*********************
* DEFINES
*********************/
/** Use this symbol as limit to disable feature (value has to be larger than supported resolution) */
#define LV_GPU_NXP_VG_LITE_FEATURE_DISABLED (1920*1080+1)
/** Stride in px required by VG-Lite HW. Don't change this. */
#define LV_GPU_NXP_VG_LITE_STRIDE_ALIGN_PX 16U
#ifndef LV_GPU_NXP_VG_LITE_FILL_SIZE_LIMIT
/** Minimum area (in pixels) to be filled by VG-Lite with 100% opacity*/
#define LV_GPU_NXP_VG_LITE_FILL_SIZE_LIMIT 32
#endif
#ifndef LV_GPU_NXP_VG_LITE_FILL_OPA_SIZE_LIMIT
/** Minimum area (in pixels) to be filled by VG-Lite with transparency*/
#define LV_GPU_NXP_VG_LITE_FILL_OPA_SIZE_LIMIT 32
#endif
#ifndef LV_GPU_NXP_VG_LITE_BLIT_SIZE_LIMIT
/** Minimum area (in pixels) for image copy with 100% opacity to be handled by VG-Lite*/
#define LV_GPU_NXP_VG_LITE_BLIT_SIZE_LIMIT 32
#endif
#ifndef LV_GPU_NXP_VG_LITE_BUFF_SYNC_BLIT_SIZE_LIMIT
/** Minimum invalidated area (in pixels) to be synchronized by VG-Lite during buffer sync */
#define LV_GPU_NXP_VG_LITE_BUFF_SYNC_BLIT_SIZE_LIMIT 32
#endif
#ifndef LV_GPU_NXP_VG_LITE_BLIT_OPA_SIZE_LIMIT
/** Minimum area (in pixels) for image copy with transparency to be handled by VG-Lite*/
#define LV_GPU_NXP_VG_LITE_BLIT_OPA_SIZE_LIMIT 32
#endif
#ifndef LV_GPU_NXP_VG_LITE_LOG_ERRORS
/** Enable logging of VG-Lite errors (\see LV_LOG_ERROR)*/
#define LV_GPU_NXP_VG_LITE_LOG_ERRORS 1
#endif
/**********************
* TYPEDEFS
**********************/
/**
* BLock Image Transfer descriptor structure
*/
typedef struct {
const lv_color_t * src; /**< Source buffer pointer (must be aligned on 32 bytes)*/
lv_area_t src_area; /**< Area to be copied from source*/
lv_coord_t src_width; /**< Source buffer width*/
lv_coord_t src_height; /**< Source buffer height*/
uint32_t src_stride; /**< Source buffer stride in bytes (must be aligned on 16 px)*/
const lv_color_t * dst; /**< Destination buffer pointer (must be aligned on 32 bytes)*/
lv_area_t dst_area; /**< Target area in destination buffer (must be the same as src_area)*/
lv_coord_t dst_width; /**< Destination buffer width*/
lv_coord_t dst_height; /**< Destination buffer height*/
uint32_t dst_stride; /**< Destination buffer stride in bytes (must be aligned on 16 px)*/
lv_opa_t opa; /**< Opacity - alpha mix (0 = source not copied, 255 = 100% opaque)*/
} lv_gpu_nxp_vglite_blit_info_t;
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
/***
* Fills rectangular area in buffer.
* @param[in] dest_buf Destination buffer pointer (must be aligned on 32 bytes)
* @param[in] dest_width Destination buffer width in pixels ((must be aligned on 16 px)
* @param[in] dest_height Destination buffer height in pixels
* @param[in] fill_area Area to be filled
* @param[in] color Fill color
* @param[in] opa Opacity (255 = full, 128 = 50% background/50% color, 0 = no fill)
* @retval LV_RES_OK Fill completed
* @retval LV_RES_INV Error occurred (\see LV_GPU_NXP_VG_LITE_LOG_ERRORS)
*/
lv_res_t lv_gpu_nxp_vglite_fill(lv_color_t * dest_buf, lv_coord_t dest_width, lv_coord_t dest_height,
const lv_area_t * fill_area, lv_color_t color, lv_opa_t opa);
/***
* BLock Image Transfer.
* @param[in] blit Description of the transfer
* @retval LV_RES_OK Transfer complete
* @retval LV_RES_INV Error occurred (\see LV_GPU_NXP_VG_LITE_LOG_ERRORS)
*/
lv_res_t lv_gpu_nxp_vglite_blit(lv_gpu_nxp_vglite_blit_info_t * blit);
#endif /*LV_USE_GPU_NXP_VG_LITE*/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_SRC_LV_GPU_LV_GPU_NXP_VGLITE_H_*/

View File

@@ -0,0 +1,28 @@
# SDL_Renderer Based Drawing Functions
In LVGL, drawing was performed by CPU. To improve drawing performance on platforms with GPU,
we should perform drawing operations on GPU if possible.
This implementation has moved most bitmap blending and drawing procedures to utilize SDL_Renderer,
which takes advantages of hardware acceleration APIs like DirectX or OpenGL.
This implementation can be also considered as a reference implementation, for contributors wants to
develop accelerated drawing functions with other APIs such as OpenGL/OpenGL ES.
## Caveats
`lv_draw_arc`, `lv_draw_line` is not enabled, due to incomplete implementation. So lines and arcs will
have significant impact to drawing performances.
Performance of this implementation still has room to improve. Or we should use more powerful APIs
such as OpenGL.
## Notices for files
### `lv_draw_sdl_stack_blur.c`
Contains modified code from [android-stackblur](https://github.com/kikoso/android-stackblur) project.
Apache License 2.0
### `lv_draw_sdl_lru.c`/`lv_draw_sdl_lru.h`
Contains modified code from [C-LRU-Cache](https://github.com/willcannings/C-LRU-Cache) project. No license defined.

View File

@@ -0,0 +1,98 @@
/**
* @file lv_draw_sdl.c
*
*/
/*********************
* INCLUDES
*********************/
#include "../../lv_conf_internal.h"
#if LV_USE_GPU_SDL
#include "lv_draw_sdl.h"
#include "lv_draw_sdl_utils.h"
#include "lv_draw_sdl_texture_cache.h"
/*********************
* DEFINES
*********************/
void lv_draw_sdl_draw_rect(lv_draw_ctx_t * draw_ctx, const lv_draw_rect_dsc_t * dsc, const lv_area_t * coords);
lv_res_t lv_draw_sdl_img_core(lv_draw_ctx_t * draw_ctx, const lv_draw_img_dsc_t * draw_dsc,
const lv_area_t * coords, const void * src);
void lv_draw_sdl_draw_letter(lv_draw_ctx_t * draw_ctx, const lv_draw_label_dsc_t * dsc, const lv_point_t * pos_p,
uint32_t letter);
void lv_draw_sdl_draw_line(lv_draw_ctx_t * draw_ctx, const lv_draw_line_dsc_t * dsc, const lv_point_t * point1,
const lv_point_t * point2);
void lv_draw_sdl_draw_arc(lv_draw_ctx_t * draw_ctx, const lv_draw_arc_dsc_t * dsc, const lv_point_t * center,
uint16_t radius, uint16_t start_angle, uint16_t end_angle);
void lv_draw_sdl_polygon(lv_draw_ctx_t * draw_ctx, const lv_draw_rect_dsc_t * draw_dsc, const lv_point_t * points,
uint16_t point_cnt);
void lv_draw_sdl_draw_bg(lv_draw_ctx_t * draw_ctx, const lv_draw_rect_dsc_t * dsc, const lv_area_t * coords);
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
void lv_draw_sdl_init_ctx(lv_disp_drv_t * disp_drv, lv_draw_ctx_t * draw_ctx)
{
_lv_draw_sdl_utils_init();
lv_memset_00(draw_ctx, sizeof(lv_draw_sdl_ctx_t));
draw_ctx->draw_rect = lv_draw_sdl_draw_rect;
draw_ctx->draw_img = lv_draw_sdl_img_core;
draw_ctx->draw_letter = lv_draw_sdl_draw_letter;
draw_ctx->draw_line = lv_draw_sdl_draw_line;
draw_ctx->draw_arc = lv_draw_sdl_draw_arc;
draw_ctx->draw_polygon = lv_draw_sdl_polygon;
draw_ctx->draw_bg = lv_draw_sdl_draw_bg;
lv_draw_sdl_ctx_t * draw_ctx_sdl = (lv_draw_sdl_ctx_t *) draw_ctx;
draw_ctx_sdl->renderer = (lv_draw_sdl_drv_param_t *) disp_drv->user_data;
draw_ctx_sdl->internals = lv_mem_alloc(sizeof(lv_draw_sdl_context_internals_t));
lv_memset_00(draw_ctx_sdl->internals, sizeof(lv_draw_sdl_context_internals_t));
lv_draw_sdl_texture_cache_init(draw_ctx_sdl);
}
void lv_draw_sdl_deinit_ctx(lv_disp_drv_t * disp_drv, lv_draw_ctx_t * draw_ctx)
{
lv_draw_sdl_ctx_t * draw_ctx_sdl = (lv_draw_sdl_ctx_t *) draw_ctx;
lv_draw_sdl_texture_cache_deinit(draw_ctx_sdl);
lv_mem_free(draw_ctx_sdl->internals);
_lv_draw_sdl_utils_deinit();
}
SDL_Texture * lv_draw_sdl_create_screen_texture(SDL_Renderer * renderer, lv_coord_t hor, lv_coord_t ver)
{
SDL_Texture * texture = SDL_CreateTexture(renderer, LV_DRAW_SDL_TEXTURE_FORMAT, SDL_TEXTUREACCESS_TARGET, hor, ver);
SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND);
return texture;
}
/**********************
* STATIC FUNCTIONS
**********************/
#endif /*LV_USE_GPU_SDL*/

View File

@@ -0,0 +1,96 @@
/**
* @file lv_draw_sdl.h
*
*/
#ifndef LV_DRAW_SDL_H
#define LV_DRAW_SDL_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../../lv_conf_internal.h"
#if LV_USE_GPU_SDL
#include LV_GPU_SDL_INCLUDE_PATH
#include "../lv_draw.h"
#include "../../core/lv_disp.h"
/*********************
* DEFINES
*********************/
#if SDL_BYTEORDER == SDL_BIG_ENDIAN
#define LV_DRAW_SDL_TEXTURE_FORMAT SDL_PIXELFORMAT_ARGB8888
#else
#define LV_DRAW_SDL_TEXTURE_FORMAT SDL_PIXELFORMAT_RGBA8888
#endif
/**********************
* TYPEDEFS
**********************/
struct lv_draw_sdl_context_internals_t;
typedef struct {
/**
* Render for display driver
*/
SDL_Renderer * renderer;
void * user_data;
} lv_draw_sdl_drv_param_t;
typedef struct {
lv_draw_ctx_t base_draw;
SDL_Renderer * renderer;
struct lv_draw_sdl_context_internals_t * internals;
} lv_draw_sdl_ctx_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
void lv_draw_sdl_init_ctx(lv_disp_drv_t * disp_drv, lv_draw_ctx_t * draw_ctx);
/**
* @brief Free caches
*
*/
void lv_draw_sdl_deinit_ctx(lv_disp_drv_t * disp_drv, lv_draw_ctx_t * draw_ctx);
SDL_Texture * lv_draw_sdl_create_screen_texture(SDL_Renderer * renderer, lv_coord_t hor, lv_coord_t ver);
/*======================
* Add/remove functions
*=====================*/
/*=====================
* Setter functions
*====================*/
/*=====================
* Getter functions
*====================*/
/*=====================
* Other functions
*====================*/
/**********************
* MACROS
**********************/
#endif /*LV_USE_GPU_SDL*/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_DRAW_SDL_H*/

View File

@@ -0,0 +1,18 @@
CSRCS += lv_draw_sdl.c
CSRCS += lv_draw_sdl_arc.c
CSRCS += lv_draw_sdl_bg.c
CSRCS += lv_draw_sdl_composite.c
CSRCS += lv_draw_sdl_img.c
CSRCS += lv_draw_sdl_label.c
CSRCS += lv_draw_sdl_line.c
CSRCS += lv_draw_sdl_mask.c
CSRCS += lv_draw_sdl_polygon.c
CSRCS += lv_draw_sdl_rect.c
CSRCS += lv_draw_sdl_stack_blur.c
CSRCS += lv_draw_sdl_texture_cache.c
CSRCS += lv_draw_sdl_utils.c
DEPPATH += --dep-path $(LVGL_DIR)/$(LVGL_DIR_NAME)/src/draw/sdl
VPATH += :$(LVGL_DIR)/$(LVGL_DIR_NAME)/src/draw/sdl
CFLAGS += "-I$(LVGL_DIR)/$(LVGL_DIR_NAME)/src/draw/sdl"

View File

@@ -0,0 +1,238 @@
/**
* @file lv_draw_sdl_arc.c
*
*/
/*********************
* INCLUDES
*********************/
#include "../../lv_conf_internal.h"
#if LV_USE_GPU_SDL
#include "lv_draw_sdl.h"
#include "lv_draw_sdl_utils.h"
#include "lv_draw_sdl_texture_cache.h"
#include "lv_draw_sdl_composite.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
static void dump_masks(SDL_Texture * texture, const lv_area_t * coords, const int16_t * ids, int16_t ids_count,
const int16_t * caps);
static void get_cap_area(int16_t angle, lv_coord_t thickness, uint16_t radius, const lv_point_t * center,
lv_area_t * out);
/**********************
* GLOBAL FUNCTIONS
**********************/
void lv_draw_sdl_draw_arc(lv_draw_ctx_t * draw_ctx, const lv_draw_arc_dsc_t * dsc, const lv_point_t * center,
uint16_t radius, uint16_t start_angle, uint16_t end_angle)
{
lv_draw_sdl_ctx_t * ctx = (lv_draw_sdl_ctx_t *) draw_ctx;
lv_area_t area_out;
area_out.x1 = center->x - radius;
area_out.y1 = center->y - radius;
area_out.x2 = center->x + radius - 1; /*-1 because the center already belongs to the left/bottom part*/
area_out.y2 = center->y + radius - 1;
lv_area_t draw_area;
if(!_lv_area_intersect(&draw_area, &area_out, draw_ctx->clip_area)) {
return;
}
lv_area_t area_in;
lv_area_copy(&area_in, &area_out);
area_in.x1 += dsc->width;
area_in.y1 += dsc->width;
area_in.x2 -= dsc->width;
area_in.y2 -= dsc->width;
while(start_angle >= 360) start_angle -= 360;
while(end_angle >= 360) end_angle -= 360;
int16_t mask_ids[3] = {LV_MASK_ID_INV, LV_MASK_ID_INV, LV_MASK_ID_INV}, mask_ids_count = 1;
int16_t cap_ids[2] = {LV_MASK_ID_INV, LV_MASK_ID_INV};
lv_draw_mask_radius_param_t mask_out_param;
lv_draw_mask_radius_init(&mask_out_param, &area_out, LV_RADIUS_CIRCLE, false);
mask_ids[0] = lv_draw_mask_add(&mask_out_param, NULL);
lv_draw_mask_radius_param_t mask_in_param;
if(lv_area_get_width(&area_in) > 0 && lv_area_get_height(&area_in) > 0) {
lv_draw_mask_radius_init(&mask_in_param, &area_in, LV_RADIUS_CIRCLE, true);
mask_ids[1] = lv_draw_mask_add(&mask_in_param, NULL);
mask_ids_count++;
}
lv_draw_mask_angle_param_t mask_angle_param;
if((start_angle - end_angle) % 360) {
lv_draw_mask_angle_init(&mask_angle_param, center->x, center->y, start_angle, end_angle);
mask_ids[2] = lv_draw_mask_add(&mask_angle_param, NULL);
mask_ids_count++;
}
lv_draw_mask_radius_param_t cap_start_param, cap_end_param;
if(mask_ids_count == 3 && dsc->rounded) {
lv_area_t start_area, end_area;
get_cap_area((int16_t) start_angle, dsc->width, radius, center, &start_area);
get_cap_area((int16_t) end_angle, dsc->width, radius, center, &end_area);
lv_draw_mask_radius_init(&cap_start_param, &start_area, dsc->width / 2, false);
cap_ids[0] = lv_draw_mask_add(&cap_start_param, NULL);
lv_draw_mask_radius_init(&cap_end_param, &end_area, dsc->width / 2, false);
cap_ids[1] = lv_draw_mask_add(&cap_end_param, NULL);
}
lv_coord_t w = lv_area_get_width(&draw_area), h = lv_area_get_height(&draw_area);
SDL_Texture * texture = lv_draw_sdl_composite_texture_obtain(ctx, LV_DRAW_SDL_COMPOSITE_TEXTURE_ID_STREAM1, w, h);
SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND);
dump_masks(texture, &draw_area, mask_ids, mask_ids_count, cap_ids[0] != LV_MASK_ID_INV ? cap_ids : NULL);
lv_draw_mask_remove_id(mask_ids[0]);
lv_draw_mask_free_param(&mask_out_param);
if(mask_ids_count > 1) {
lv_draw_mask_remove_id(mask_ids[1]);
lv_draw_mask_free_param(&mask_in_param);
}
if(mask_ids_count > 2) {
lv_draw_mask_remove_id(mask_ids[2]);
lv_draw_mask_free_param(&mask_angle_param);
}
if(cap_ids[0] != LV_MASK_ID_INV) {
lv_draw_mask_remove_id(cap_ids[0]);
lv_draw_mask_remove_id(cap_ids[1]);
lv_draw_mask_free_param(&cap_start_param);
lv_draw_mask_free_param(&cap_end_param);
}
SDL_Rect srcrect = {0, 0, w, h}, dstrect;
lv_area_to_sdl_rect(&draw_area, &dstrect);
SDL_Color color;
lv_color_to_sdl_color(&dsc->color, &color);
SDL_SetTextureColorMod(texture, color.r, color.g, color.b);
SDL_SetTextureAlphaMod(texture, dsc->opa);
SDL_RenderCopy(ctx->renderer, texture, &srcrect, &dstrect);
}
/**********************
* STATIC FUNCTIONS
**********************/
static void dump_masks(SDL_Texture * texture, const lv_area_t * coords, const int16_t * ids, int16_t ids_count,
const int16_t * caps)
{
lv_coord_t w = lv_area_get_width(coords), h = lv_area_get_height(coords);
SDL_assert(w > 0 && h > 0);
SDL_Rect rect = {0, 0, w, h};
uint8_t * pixels;
int pitch;
if(SDL_LockTexture(texture, &rect, (void **) &pixels, &pitch) != 0) return;
lv_opa_t * line_buf = lv_mem_buf_get(rect.w);
for(lv_coord_t y = 0; y < rect.h; y++) {
lv_memset_ff(line_buf, rect.w);
lv_coord_t abs_x = (lv_coord_t) coords->x1, abs_y = (lv_coord_t)(y + coords->y1), len = (lv_coord_t) rect.w;
lv_draw_mask_res_t res;
res = lv_draw_mask_apply_ids(line_buf, abs_x, abs_y, len, ids, ids_count);
if(res == LV_DRAW_MASK_RES_TRANSP) {
lv_memset_00(&pixels[y * pitch], 4 * rect.w);
}
else if(res == LV_DRAW_MASK_RES_FULL_COVER) {
lv_memset_ff(&pixels[y * pitch], 4 * rect.w);
}
else {
for(int x = 0; x < rect.w; x++) {
uint8_t * pixel = &pixels[y * pitch + x * 4];
*pixel = line_buf[x];
pixel[1] = pixel[2] = pixel[3] = 0xFF;
}
}
if(caps) {
for(int i = 0; i < 2; i++) {
lv_memset_ff(line_buf, rect.w);
res = lv_draw_mask_apply_ids(line_buf, abs_x, abs_y, len, &caps[i], 1);
if(res == LV_DRAW_MASK_RES_TRANSP) {
/* Ignore */
}
else if(res == LV_DRAW_MASK_RES_FULL_COVER) {
lv_memset_ff(&pixels[y * pitch], 4 * rect.w);
}
else {
for(int x = 0; x < rect.w; x++) {
uint8_t * pixel = &pixels[y * pitch + x * 4];
uint16_t old_opa = line_buf[x] + *pixel;
*pixel = LV_MIN(old_opa, 0xFF);
pixel[1] = pixel[2] = pixel[3] = 0xFF;
}
}
}
}
}
lv_mem_buf_release(line_buf);
SDL_UnlockTexture(texture);
}
static void get_cap_area(int16_t angle, lv_coord_t thickness, uint16_t radius, const lv_point_t * center,
lv_area_t * out)
{
const uint8_t ps = 8;
const uint8_t pa = 127;
int32_t thick_half = thickness / 2;
uint8_t thick_corr = (thickness & 0x01) ? 0 : 1;
int32_t cir_x;
int32_t cir_y;
cir_x = ((radius - thick_half) * lv_trigo_sin((int16_t)(90 - angle))) >> (LV_TRIGO_SHIFT - ps);
cir_y = ((radius - thick_half) * lv_trigo_sin(angle)) >> (LV_TRIGO_SHIFT - ps);
/*Actually the center of the pixel need to be calculated so apply 1/2 px offset*/
if(cir_x > 0) {
cir_x = (cir_x - pa) >> ps;
out->x1 = cir_x - thick_half + thick_corr;
out->x2 = cir_x + thick_half;
}
else {
cir_x = (cir_x + pa) >> ps;
out->x1 = cir_x - thick_half;
out->x2 = cir_x + thick_half - thick_corr;
}
if(cir_y > 0) {
cir_y = (cir_y - pa) >> ps;
out->y1 = cir_y - thick_half + thick_corr;
out->y2 = cir_y + thick_half;
}
else {
cir_y = (cir_y + pa) >> ps;
out->y1 = cir_y - thick_half;
out->y2 = cir_y + thick_half - thick_corr;
}
lv_area_move(out, center->x, center->y);
}
#endif /*LV_USE_GPU_SDL*/

View File

@@ -0,0 +1,106 @@
/**
* @file lv_draw_sdl_bg.c
*
*/
/*********************
* INCLUDES
*********************/
#include "../../lv_conf_internal.h"
#if LV_USE_GPU_SDL
#include "../lv_draw_rect.h"
#include "../lv_draw_img.h"
#include "../lv_draw_label.h"
#include "../lv_draw_mask.h"
#include "../../core/lv_refr.h"
#include "lv_draw_sdl_utils.h"
#include "lv_draw_sdl_texture_cache.h"
#include "lv_draw_sdl_composite.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static void draw_bg_color(lv_draw_sdl_ctx_t * ctx, const lv_area_t * coords, const lv_area_t * draw_area,
const lv_draw_rect_dsc_t * dsc);
static void draw_bg_img(lv_draw_sdl_ctx_t * ctx, const lv_area_t * coords, const lv_area_t * draw_area,
const lv_draw_rect_dsc_t * dsc);
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
void lv_draw_sdl_draw_bg(lv_draw_ctx_t * draw_ctx, const lv_draw_rect_dsc_t * dsc, const lv_area_t * coords)
{
const lv_area_t * clip = draw_ctx->clip_area;
lv_draw_sdl_ctx_t * ctx = (lv_draw_sdl_ctx_t *) draw_ctx;
/* Coords will be translated so coords will start at (0,0) */
lv_area_t t_area;
bool has_content = _lv_area_intersect(&t_area, coords, clip);
/* Shadows and outlines will also draw in extended area */
if(has_content) {
if(dsc->bg_img_src) {
draw_bg_img(ctx, coords, &t_area, dsc);
}
else {
draw_bg_color(ctx, coords, &t_area, dsc);
}
}
}
/**********************
* STATIC FUNCTIONS
**********************/
static void draw_bg_color(lv_draw_sdl_ctx_t * ctx, const lv_area_t * coords, const lv_area_t * draw_area,
const lv_draw_rect_dsc_t * dsc)
{
SDL_Color bg_color;
lv_color_to_sdl_color(&dsc->bg_color, &bg_color);
SDL_SetRenderDrawBlendMode(ctx->renderer, SDL_BLENDMODE_NONE);
SDL_SetRenderDrawColor(ctx->renderer, bg_color.r, bg_color.g, bg_color.b, dsc->bg_opa);
SDL_Rect rect;
lv_area_to_sdl_rect(draw_area, &rect);
SDL_RenderFillRect(ctx->renderer, &rect);
SDL_SetRenderDrawBlendMode(ctx->renderer, SDL_BLENDMODE_BLEND);
}
static void draw_bg_img(lv_draw_sdl_ctx_t * ctx, const lv_area_t * coords, const lv_area_t * draw_area,
const lv_draw_rect_dsc_t * dsc)
{
SDL_SetRenderDrawBlendMode(ctx->renderer, SDL_BLENDMODE_NONE);
SDL_SetRenderDrawColor(ctx->renderer, 0, 0, 0, 0);
SDL_Rect rect;
lv_area_to_sdl_rect(draw_area, &rect);
SDL_RenderFillRect(ctx->renderer, &rect);
SDL_SetRenderDrawBlendMode(ctx->renderer, SDL_BLENDMODE_BLEND);
lv_draw_rect((lv_draw_ctx_t *) ctx, dsc, coords);
}
#endif /*LV_USE_GPU_SDL*/

View File

@@ -0,0 +1,256 @@
/**
* @file lv_draw_sdl_composite.c
*
*/
/*********************
* INCLUDES
*********************/
#include "../../lv_conf_internal.h"
#if LV_USE_GPU_SDL
#include "../../misc/lv_gc.h"
#include "../../core/lv_refr.h"
#include "lv_draw_sdl_composite.h"
#include "lv_draw_sdl_mask.h"
#include "lv_draw_sdl_utils.h"
#include "lv_draw_sdl_priv.h"
#include "lv_draw_sdl_texture_cache.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
typedef struct {
lv_sdl_cache_key_magic_t magic;
lv_draw_sdl_composite_texture_id_t type;
} composite_key_t;
/**********************
* STATIC PROTOTYPES
**********************/
static composite_key_t mask_key_create(lv_draw_sdl_composite_texture_id_t type);
static lv_coord_t next_pow_of_2(lv_coord_t num);
static void dump_masks(SDL_Texture * texture, const lv_area_t * coords);
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
bool lv_draw_sdl_composite_begin(lv_draw_sdl_ctx_t * ctx, const lv_area_t * coords_in, const lv_area_t * clip_in,
const lv_area_t * extension, lv_blend_mode_t blend_mode, lv_area_t * coords_out,
lv_area_t * clip_out, lv_area_t * apply_area)
{
lv_area_t full_coords = *coords_in;
/* Normalize full_coords */
if(full_coords.x1 > full_coords.x2) {
lv_coord_t x2 = full_coords.x2;
full_coords.x2 = full_coords.x1;
full_coords.x1 = x2;
}
if(full_coords.y1 > full_coords.y2) {
lv_coord_t y2 = full_coords.y2;
full_coords.y2 = full_coords.y1;
full_coords.y1 = y2;
}
if(extension) {
full_coords.x1 -= extension->x1;
full_coords.x2 += extension->x2;
full_coords.y1 -= extension->y1;
full_coords.y2 += extension->y2;
}
if(!_lv_area_intersect(apply_area, &full_coords, clip_in)) return false;
bool has_mask = lv_draw_mask_is_any(apply_area);
const bool draw_mask = has_mask && LV_GPU_SDL_CUSTOM_BLEND_MODE;
const bool draw_blend = blend_mode != LV_BLEND_MODE_NORMAL;
if(draw_mask || draw_blend) {
lv_draw_sdl_context_internals_t * internals = ctx->internals;
LV_ASSERT(internals->mask == NULL && internals->composition == NULL);
lv_coord_t w = lv_area_get_width(apply_area), h = lv_area_get_height(apply_area);
internals->composition = lv_draw_sdl_composite_texture_obtain(ctx, LV_DRAW_SDL_COMPOSITE_TEXTURE_ID_TARGET0, w, h);
/* Don't need to worry about overflow */
lv_coord_t ofs_x = (lv_coord_t) - apply_area->x1, ofs_y = (lv_coord_t) - apply_area->y1;
/* Offset draw area to start with (0,0) of coords */
lv_area_move(coords_out, ofs_x, ofs_y);
lv_area_move(clip_out, ofs_x, ofs_y);
SDL_SetRenderTarget(ctx->renderer, internals->composition);
SDL_SetRenderDrawColor(ctx->renderer, 255, 255, 255, 0);
SDL_RenderClear(ctx->renderer);
#if LV_GPU_SDL_CUSTOM_BLEND_MODE
internals->mask = lv_draw_sdl_composite_texture_obtain(ctx, LV_DRAW_SDL_COMPOSITE_TEXTURE_ID_STREAM0, w, h);
dump_masks(internals->mask, apply_area);
#endif
}
else if(has_mask) {
/* Fallback mask handling. This will at least make bars looks less bad */
for(uint8_t i = 0; i < _LV_MASK_MAX_NUM; i++) {
_lv_draw_mask_common_dsc_t * comm_param = LV_GC_ROOT(_lv_draw_mask_list[i]).param;
if(comm_param == NULL) continue;
switch(comm_param->type) {
case LV_DRAW_MASK_TYPE_RADIUS: {
const lv_draw_mask_radius_param_t * param = (const lv_draw_mask_radius_param_t *) comm_param;
if(param->cfg.outer) break;
_lv_area_intersect(clip_out, apply_area, &param->cfg.rect);
break;
}
default:
break;
}
}
}
return has_mask;
}
void lv_draw_sdl_composite_end(lv_draw_sdl_ctx_t * ctx, const lv_area_t * apply_area, lv_blend_mode_t blend_mode)
{
lv_draw_sdl_context_internals_t * internals = ctx->internals;
SDL_Rect src_rect = {0, 0, lv_area_get_width(apply_area), lv_area_get_height(apply_area)};
#if LV_GPU_SDL_CUSTOM_BLEND_MODE
if(internals->mask) {
SDL_BlendMode mode = SDL_ComposeCustomBlendMode(SDL_BLENDFACTOR_ZERO, SDL_BLENDFACTOR_ONE,
SDL_BLENDOPERATION_ADD, SDL_BLENDFACTOR_ZERO,
SDL_BLENDFACTOR_SRC_ALPHA, SDL_BLENDOPERATION_ADD);
SDL_SetTextureBlendMode(internals->mask, mode);
SDL_RenderCopy(ctx->renderer, internals->mask, &src_rect, &src_rect);
}
#endif
/* Shapes are drawn on composite layer when mask or blend mode is present */
if(internals->composition) {
SDL_Rect dst_rect;
lv_area_to_sdl_rect(apply_area, &dst_rect);
SDL_SetRenderTarget(ctx->renderer, ctx->base_draw.buf);
switch(blend_mode) {
case LV_BLEND_MODE_NORMAL:
SDL_SetTextureBlendMode(internals->composition, SDL_BLENDMODE_BLEND);
break;
case LV_BLEND_MODE_ADDITIVE:
SDL_SetTextureBlendMode(internals->composition, SDL_BLENDMODE_ADD);
break;
#if LV_GPU_SDL_CUSTOM_BLEND_MODE
case LV_BLEND_MODE_SUBTRACTIVE: {
SDL_BlendMode mode = SDL_ComposeCustomBlendMode(SDL_BLENDFACTOR_ONE, SDL_BLENDFACTOR_ONE,
SDL_BLENDOPERATION_SUBTRACT, SDL_BLENDFACTOR_ONE,
SDL_BLENDFACTOR_ONE, SDL_BLENDOPERATION_SUBTRACT);
SDL_SetTextureBlendMode(internals->composition, mode);
break;
}
case LV_BLEND_MODE_MULTIPLY: {
SDL_BlendMode mode = SDL_ComposeCustomBlendMode(SDL_BLENDFACTOR_ZERO, SDL_BLENDFACTOR_SRC_COLOR,
SDL_BLENDOPERATION_ADD, SDL_BLENDFACTOR_ZERO,
SDL_BLENDFACTOR_DST_ALPHA, SDL_BLENDOPERATION_ADD);
SDL_SetTextureBlendMode(internals->composition, mode);
break;
}
#endif
default:
LV_LOG_WARN("Doesn't support blend mode %d", blend_mode);
SDL_SetTextureBlendMode(internals->composition, SDL_BLENDMODE_BLEND);
/* Unsupported yet */
break;
}
SDL_RenderCopy(ctx->renderer, internals->composition, &src_rect, &dst_rect);
}
internals->mask = internals->composition = NULL;
}
SDL_Texture * lv_draw_sdl_composite_texture_obtain(lv_draw_sdl_ctx_t * ctx, lv_draw_sdl_composite_texture_id_t id,
lv_coord_t w, lv_coord_t h)
{
lv_point_t * tex_size = NULL;
composite_key_t mask_key = mask_key_create(id);
SDL_Texture * result = lv_draw_sdl_texture_cache_get_with_userdata(ctx, &mask_key, sizeof(composite_key_t), NULL,
(void **) &tex_size);
if(!result || tex_size->x < w || tex_size->y < h) {
lv_coord_t size = next_pow_of_2(LV_MAX(w, h));
int access = SDL_TEXTUREACCESS_STREAMING;
if(id >= LV_DRAW_SDL_COMPOSITE_TEXTURE_ID_TARGET0) {
access = SDL_TEXTUREACCESS_TARGET;
}
result = SDL_CreateTexture(ctx->renderer, LV_DRAW_SDL_TEXTURE_FORMAT, access, size, size);
tex_size = lv_mem_alloc(sizeof(lv_point_t));
tex_size->x = tex_size->y = size;
lv_draw_sdl_texture_cache_put_advanced(ctx, &mask_key, sizeof(composite_key_t), result, tex_size, lv_mem_free, 0);
}
return result;
}
/**********************
* STATIC FUNCTIONS
**********************/
static composite_key_t mask_key_create(lv_draw_sdl_composite_texture_id_t type)
{
composite_key_t key;
/* VERY IMPORTANT! Padding between members is uninitialized, so we have to wipe them manually */
SDL_memset(&key, 0, sizeof(key));
key.magic = LV_GPU_CACHE_KEY_MAGIC_MASK;
key.type = type;
return key;
}
static lv_coord_t next_pow_of_2(lv_coord_t num)
{
lv_coord_t n = 128;
while(n < num && n < 16384) {
n = n << 1;
}
return n;
}
static void dump_masks(SDL_Texture * texture, const lv_area_t * coords)
{
lv_coord_t w = lv_area_get_width(coords), h = lv_area_get_height(coords);
SDL_assert(w > 0 && h > 0);
SDL_Rect rect = {0, 0, w, h};
uint8_t * pixels;
int pitch;
if(SDL_LockTexture(texture, &rect, (void **) &pixels, &pitch) != 0) return;
lv_opa_t * line_buf = lv_mem_buf_get(rect.w);
for(lv_coord_t y = 0; y < rect.h; y++) {
lv_memset_ff(line_buf, rect.w);
lv_coord_t abs_x = (lv_coord_t) coords->x1, abs_y = (lv_coord_t)(y + coords->y1), len = (lv_coord_t) rect.w;
lv_draw_mask_res_t res;
res = lv_draw_mask_apply(line_buf, abs_x, abs_y, len);
if(res == LV_DRAW_MASK_RES_TRANSP) {
lv_memset_00(&pixels[y * pitch], 4 * rect.w);
}
else if(res == LV_DRAW_MASK_RES_FULL_COVER) {
lv_memset_ff(&pixels[y * pitch], 4 * rect.w);
}
else {
for(int x = 0; x < rect.w; x++) {
const size_t idx = y * pitch + x * 4;
pixels[idx] = line_buf[x];
pixels[idx + 1] = pixels[idx + 2] = pixels[idx + 3] = 0xFF;
}
}
}
lv_mem_buf_release(line_buf);
SDL_UnlockTexture(texture);
}
#endif /*LV_USE_GPU_SDL*/

View File

@@ -0,0 +1,72 @@
/**
* @file lv_draw_sdl_composite.h
*
*/
#ifndef LV_DRAW_SDL_COMPOSITE_H
#define LV_DRAW_SDL_COMPOSITE_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../../lv_conf_internal.h"
#include LV_GPU_SDL_INCLUDE_PATH
#include "lv_draw_sdl.h"
#include "../../misc/lv_area.h"
#include "../../misc/lv_color.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
typedef enum lv_draw_sdl_composite_texture_id_t {
LV_DRAW_SDL_COMPOSITE_TEXTURE_ID_STREAM0,
LV_DRAW_SDL_COMPOSITE_TEXTURE_ID_STREAM1,
LV_DRAW_SDL_COMPOSITE_TEXTURE_ID_TARGET0,
LV_DRAW_SDL_COMPOSITE_TEXTURE_ID_TARGET1,
} lv_draw_sdl_composite_texture_id_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Begin drawing with mask. Render target will be switched to a temporary texture,
* and drawing coordinates may get clipped or translated
* @param coords_in Original coordinates
* @param clip_in Original clip area
* @param extension Useful for shadows or outlines, can be NULL
* @param coords_out Translated coords
* @param clip_out Translated clip area
* @param apply_area Area of actual composited texture will be drawn
* @return true if there are any mask needs to be drawn, false otherwise
*/
bool lv_draw_sdl_composite_begin(lv_draw_sdl_ctx_t * ctx, const lv_area_t * coords_in, const lv_area_t * clip_in,
const lv_area_t * extension, lv_blend_mode_t blend_mode, lv_area_t * coords_out,
lv_area_t * clip_out, lv_area_t * apply_area);
void lv_draw_sdl_composite_end(lv_draw_sdl_ctx_t * ctx, const lv_area_t * apply_area, lv_blend_mode_t blend_mode);
SDL_Texture * lv_draw_sdl_composite_texture_obtain(lv_draw_sdl_ctx_t * ctx, lv_draw_sdl_composite_texture_id_t id,
lv_coord_t w, lv_coord_t h);
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_DRAW_SDL_COMPOSITE_H*/

View File

@@ -0,0 +1,459 @@
/**
* @file lv_draw_sdl_img.c
*
*/
/*********************
* INCLUDES
*********************/
#include "../../lv_conf_internal.h"
#if LV_USE_GPU_SDL
#include "../lv_draw_img.h"
#include "../lv_img_cache.h"
#include "../lv_draw_mask.h"
#include "../../misc/lv_lru.h"
#include "../../misc/lv_gc.h"
#include "lv_draw_sdl_img.h"
#include "lv_draw_sdl_utils.h"
#include "lv_draw_sdl_texture_cache.h"
#include "lv_draw_sdl_composite.h"
#include "lv_draw_sdl_rect.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
typedef struct {
lv_sdl_cache_key_magic_t magic;
const SDL_Texture * texture;
lv_coord_t w, h, radius;
} lv_draw_img_rounded_key_t;
enum {
ROUNDED_IMG_PART_LEFT = 0,
ROUNDED_IMG_PART_HCENTER = 1,
ROUNDED_IMG_PART_RIGHT = 2,
ROUNDED_IMG_PART_TOP = 3,
ROUNDED_IMG_PART_VCENTER = 4,
ROUNDED_IMG_PART_BOTTOM = 5,
};
enum {
ROUNDED_IMG_CORNER_TOP_LEFT = 0,
ROUNDED_IMG_CORNER_TOP_RIGHT = 1,
ROUNDED_IMG_CORNER_BOTTOM_RIGHT = 2,
ROUNDED_IMG_CORNER_BOTTOM_LEFT = 3,
};
/**********************
* STATIC PROTOTYPES
**********************/
static SDL_Texture * upload_img_texture(SDL_Renderer * renderer, lv_img_decoder_dsc_t * dsc);
static SDL_Texture * upload_img_texture_fallback(SDL_Renderer * renderer, lv_img_decoder_dsc_t * dsc);
static bool check_mask_simple_radius(const lv_area_t * coords, lv_coord_t * radius);
static void draw_img_simple(lv_draw_sdl_ctx_t * ctx, SDL_Texture * texture, const lv_draw_sdl_img_header_t * header,
const lv_draw_img_dsc_t * draw_dsc, const lv_area_t * coords, const lv_area_t * clip);
static void draw_img_rounded(lv_draw_sdl_ctx_t * ctx, SDL_Texture * texture, const lv_draw_sdl_img_header_t * header,
const lv_draw_img_dsc_t * draw_dsc, const lv_area_t * coords, const lv_area_t * clip,
lv_coord_t radius);
static SDL_Texture * img_rounded_frag_obtain(lv_draw_sdl_ctx_t * ctx, SDL_Texture * texture,
const lv_draw_sdl_img_header_t * header, int w, int h, lv_coord_t radius);
static lv_draw_img_rounded_key_t rounded_key_create(const SDL_Texture * texture, lv_coord_t w, lv_coord_t h,
lv_coord_t radius);
static void calc_draw_part(SDL_Texture * texture, const lv_draw_sdl_img_header_t * header, const lv_area_t * coords,
const lv_area_t * clip, SDL_Rect * clipped_src, SDL_Rect * clipped_dst);
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
static void apply_recolor_opa(SDL_Texture * texture, const lv_draw_img_dsc_t * draw_dsc);
/**********************
* GLOBAL FUNCTIONS
**********************/
lv_res_t lv_draw_sdl_img_core(lv_draw_ctx_t * draw_ctx, const lv_draw_img_dsc_t * draw_dsc,
const lv_area_t * coords, const void * src)
{
const lv_area_t * clip = draw_ctx->clip_area;
lv_draw_sdl_ctx_t * ctx = (lv_draw_sdl_ctx_t *) draw_ctx;
size_t key_size;
lv_draw_sdl_cache_key_head_img_t * key = lv_draw_sdl_texture_img_key_create(src, draw_dsc->frame_id, &key_size);
bool texture_found = false;
lv_draw_sdl_img_header_t * header = NULL;
SDL_Texture * texture = lv_draw_sdl_texture_cache_get_with_userdata(ctx, key, key_size, &texture_found,
(void **) &header);
if(!texture_found) {
lv_draw_sdl_img_load_texture(ctx, key, key_size, src, draw_dsc->frame_id, &texture, &header);
}
SDL_free(key);
if(!texture) {
return LV_RES_INV;
}
lv_area_t zoomed_cords;
_lv_img_buf_get_transformed_area(&zoomed_cords, lv_area_get_width(coords), lv_area_get_height(coords), 0,
draw_dsc->zoom, &draw_dsc->pivot);
lv_area_move(&zoomed_cords, coords->x1, coords->y1);
/* When in > 0, draw simple radius */
lv_coord_t radius = 0;
/* Coords will be translated so coords will start at (0,0) */
lv_area_t t_coords = zoomed_cords, t_clip = *clip, apply_area;
if(!check_mask_simple_radius(&t_coords, &radius)) {
lv_draw_sdl_composite_begin(ctx, &zoomed_cords, clip, NULL, draw_dsc->blend_mode,
&t_coords, &t_clip, &apply_area);
}
SDL_Rect clip_rect, coords_rect;
lv_area_to_sdl_rect(&t_clip, &clip_rect);
lv_area_to_sdl_rect(&t_coords, &coords_rect);
SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND);
if(radius > 0) {
draw_img_rounded(ctx, texture, header, draw_dsc, &t_coords, &t_clip, radius);
}
else {
draw_img_simple(ctx, texture, header, draw_dsc, &t_coords, &t_clip);
}
lv_draw_sdl_composite_end(ctx, &apply_area, draw_dsc->blend_mode);
return LV_RES_OK;
}
static void calc_draw_part(SDL_Texture * texture, const lv_draw_sdl_img_header_t * header, const lv_area_t * coords,
const lv_area_t * clip, SDL_Rect * clipped_src, SDL_Rect * clipped_dst)
{
double x = 0, y = 0, w, h;
if(SDL_RectEmpty(&header->rect)) {
Uint32 format = 0;
int access = 0, tw, th;
SDL_QueryTexture(texture, &format, &access, &tw, &th);
w = tw;
h = th;
}
else {
x = header->rect.x;
y = header->rect.y;
w = header->rect.w;
h = header->rect.h;
}
if(clip) {
lv_area_t clipped_area;
_lv_area_intersect(&clipped_area, coords, clip);
lv_area_to_sdl_rect(&clipped_area, clipped_dst);
}
else {
lv_area_to_sdl_rect(coords, clipped_dst);
}
lv_coord_t coords_w = lv_area_get_width(coords), coords_h = lv_area_get_height(coords);
clipped_src->x = (int)(x + (clipped_dst->x - coords->x1) * w / coords_w);
clipped_src->y = (int)(y + (clipped_dst->y - coords->y1) * h / coords_h);
clipped_src->w = (int)(w - (coords_w - clipped_dst->w) * w / coords_w);
clipped_src->h = (int)(h - (coords_h - clipped_dst->h) * h / coords_h);
}
bool lv_draw_sdl_img_load_texture(lv_draw_sdl_ctx_t * ctx, lv_draw_sdl_cache_key_head_img_t * key, size_t key_size,
const void * src, int32_t frame_id, SDL_Texture ** texture,
lv_draw_sdl_img_header_t ** header)
{
_lv_img_cache_entry_t * cdsc = _lv_img_cache_open(src, lv_color_white(), frame_id);
lv_draw_sdl_cache_flag_t tex_flags = 0;
SDL_Rect rect;
SDL_memset(&rect, 0, sizeof(SDL_Rect));
if(cdsc) {
lv_img_decoder_dsc_t * dsc = &cdsc->dec_dsc;
if(dsc->user_data && SDL_memcmp(dsc->user_data, LV_DRAW_SDL_DEC_DSC_TEXTURE_HEAD, 8) == 0) {
lv_draw_sdl_dec_dsc_userdata_t * ptr = (lv_draw_sdl_dec_dsc_userdata_t *) dsc->user_data;
*texture = ptr->texture;
rect = ptr->rect;
if(ptr->texture_managed) {
tex_flags |= LV_DRAW_SDL_CACHE_FLAG_MANAGED;
}
ptr->texture_referenced = true;
}
else {
*texture = upload_img_texture(ctx->renderer, dsc);
}
#if LV_IMG_CACHE_DEF_SIZE == 0
lv_img_decoder_close(dsc);
#endif
}
if(texture && cdsc) {
*header = SDL_malloc(sizeof(lv_draw_sdl_img_header_t));
SDL_memcpy(&(*header)->base, &cdsc->dec_dsc.header, sizeof(lv_img_header_t));
(*header)->rect = rect;
lv_draw_sdl_texture_cache_put_advanced(ctx, key, key_size, *texture, *header, SDL_free, tex_flags);
}
else {
lv_draw_sdl_texture_cache_put(ctx, key, key_size, NULL);
return false;
}
return true;
}
/**********************
* STATIC FUNCTIONS
**********************/
static SDL_Texture * upload_img_texture(SDL_Renderer * renderer, lv_img_decoder_dsc_t * dsc)
{
if(!dsc->img_data) {
return upload_img_texture_fallback(renderer, dsc);
}
bool chroma_keyed = dsc->header.cf == (uint32_t) LV_IMG_CF_TRUE_COLOR_CHROMA_KEYED;
int h = (int) dsc->header.h;
int w = (int) dsc->header.w;
void * data = (void *) dsc->img_data;
Uint32 rmask = 0x00FF0000;
Uint32 gmask = 0x0000FF00;
Uint32 bmask = 0x000000FF;
Uint32 amask = 0xFF000000;
if(chroma_keyed) {
amask = 0x00;
}
SDL_Surface * surface = SDL_CreateRGBSurfaceFrom(data, w, h, LV_COLOR_DEPTH, w * LV_COLOR_DEPTH / 8,
rmask, gmask, bmask, amask);
SDL_SetColorKey(surface, chroma_keyed, lv_color_to32(LV_COLOR_CHROMA_KEY));
SDL_Texture * texture = SDL_CreateTextureFromSurface(renderer, surface);
SDL_FreeSurface(surface);
return texture;
}
static SDL_Texture * upload_img_texture_fallback(SDL_Renderer * renderer, lv_img_decoder_dsc_t * dsc)
{
lv_coord_t h = (lv_coord_t) dsc->header.h;
lv_coord_t w = (lv_coord_t) dsc->header.w;
uint8_t * data = lv_mem_buf_get(w * h * sizeof(lv_color_t));
for(lv_coord_t y = 0; y < h; y++) {
lv_img_decoder_read_line(dsc, 0, y, w, &data[y * w * sizeof(lv_color_t)]);
}
Uint32 rmask = 0x00FF0000;
Uint32 gmask = 0x0000FF00;
Uint32 bmask = 0x000000FF;
Uint32 amask = 0xFF000000;
SDL_Surface * surface = SDL_CreateRGBSurfaceFrom(data, w, h, LV_COLOR_DEPTH, w * LV_COLOR_DEPTH / 8,
rmask, gmask, bmask, amask);
SDL_SetColorKey(surface, SDL_TRUE, lv_color_to32(LV_COLOR_CHROMA_KEY));
SDL_Texture * texture = SDL_CreateTextureFromSurface(renderer, surface);
SDL_FreeSurface(surface);
lv_mem_buf_release(data);
return texture;
}
/**
* Check if there is only one radius mask
* @param radius Set to radius value if the only mask is a radius mask
* @return true if the only mask is a radius mask
*/
static bool check_mask_simple_radius(const lv_area_t * coords, lv_coord_t * radius)
{
if(lv_draw_mask_get_cnt() != 1) return false;
for(uint8_t i = 0; i < _LV_MASK_MAX_NUM; i++) {
_lv_draw_mask_common_dsc_t * param = LV_GC_ROOT(_lv_draw_mask_list[i]).param;
if(param->type == LV_DRAW_MASK_TYPE_RADIUS) {
lv_draw_mask_radius_param_t * rparam = (lv_draw_mask_radius_param_t *) param;
if(rparam->cfg.outer) return false;
if(!_lv_area_is_equal(&rparam->cfg.rect, coords)) return false;
*radius = rparam->cfg.radius;
return true;
}
}
return false;
}
static void draw_img_simple(lv_draw_sdl_ctx_t * ctx, SDL_Texture * texture, const lv_draw_sdl_img_header_t * header,
const lv_draw_img_dsc_t * draw_dsc, const lv_area_t * coords, const lv_area_t * clip)
{
apply_recolor_opa(texture, draw_dsc);
SDL_Point pivot = {.x = draw_dsc->pivot.x, .y = draw_dsc->pivot.y};
/*Image needs to be rotated, so we have to use clip rect which is slower*/
if(draw_dsc->angle != 0) {
/* No radius, set clip here */
SDL_Rect clip_rect;
lv_area_to_sdl_rect(clip, &clip_rect);
SDL_RenderSetClipRect(ctx->renderer, &clip_rect);
}
SDL_Rect src_rect, dst_rect;
calc_draw_part(texture, header, coords, clip, &src_rect, &dst_rect);
SDL_RenderCopyEx(ctx->renderer, texture, &src_rect, &dst_rect, draw_dsc->angle, &pivot, SDL_FLIP_NONE);
if(draw_dsc->angle != 0) {
SDL_RenderSetClipRect(ctx->renderer, NULL);
}
}
static void draw_img_rounded(lv_draw_sdl_ctx_t * ctx, SDL_Texture * texture, const lv_draw_sdl_img_header_t * header,
const lv_draw_img_dsc_t * draw_dsc, const lv_area_t * coords, const lv_area_t * clip,
lv_coord_t radius)
{
const int w = lv_area_get_width(coords), h = lv_area_get_height(coords);
lv_coord_t real_radius = LV_MIN3(radius, w, h);
SDL_Texture * frag = img_rounded_frag_obtain(ctx, texture, header, w, h, real_radius);
apply_recolor_opa(frag, draw_dsc);
lv_draw_sdl_rect_bg_frag_draw_corners(ctx, frag, real_radius, coords, clip, true);
apply_recolor_opa(texture, draw_dsc);
SDL_Rect src_rect, dst_rect;
/* Draw 3 parts */
lv_area_t clip_tmp, part;
calc_draw_part(texture, header, coords, NULL, &src_rect, &dst_rect);
for(int i = w > h ? ROUNDED_IMG_PART_LEFT : ROUNDED_IMG_PART_TOP, j = i + 3; i <= j; i++) {
switch(i) {
case ROUNDED_IMG_PART_LEFT:
lv_area_set(&part, coords->x1, coords->y1 + radius, coords->x1 + radius - 1, coords->y2 - radius);
break;
case ROUNDED_IMG_PART_HCENTER:
lv_area_set(&part, coords->x1 + radius, coords->y1, coords->x2 - radius, coords->y2);
break;
case ROUNDED_IMG_PART_RIGHT:
lv_area_set(&part, coords->x2 - radius + 1, coords->y1 + radius, coords->x2, coords->y2 - radius);
break;
case ROUNDED_IMG_PART_TOP:
lv_area_set(&part, coords->x1 + radius, coords->y1, coords->x2 - radius, coords->y1 + radius - 1);
break;
case ROUNDED_IMG_PART_VCENTER:
lv_area_set(&part, coords->x1 + radius, coords->y2 - radius + 1, coords->x2 - radius, coords->y2);
break;
case ROUNDED_IMG_PART_BOTTOM:
lv_area_set(&part, coords->x1, coords->y1 + radius, coords->x2, coords->y2 - radius);
break;
default:
break;
}
if(!_lv_area_intersect(&clip_tmp, &part, clip)) continue;
SDL_Rect clip_rect;
lv_area_to_sdl_rect(&clip_tmp, &clip_rect);
SDL_RenderSetClipRect(ctx->renderer, &clip_rect);
SDL_RenderCopy(ctx->renderer, texture, &src_rect, &dst_rect);
}
SDL_RenderSetClipRect(ctx->renderer, NULL);
}
static void apply_recolor_opa(SDL_Texture * texture, const lv_draw_img_dsc_t * draw_dsc)
{
if(draw_dsc->recolor_opa > LV_OPA_TRANSP) {
/* Draw with mixed recolor */
lv_color_t recolor = lv_color_mix(draw_dsc->recolor, lv_color_white(), draw_dsc->recolor_opa);
SDL_SetTextureColorMod(texture, recolor.ch.red, recolor.ch.green, recolor.ch.blue);
}
else {
/* Draw with no recolor */
SDL_SetTextureColorMod(texture, 0xFF, 0xFF, 0xFF);
}
SDL_SetTextureAlphaMod(texture, draw_dsc->opa);
}
static SDL_Texture * img_rounded_frag_obtain(lv_draw_sdl_ctx_t * ctx, SDL_Texture * texture,
const lv_draw_sdl_img_header_t * header, int w, int h, lv_coord_t radius)
{
lv_draw_img_rounded_key_t key = rounded_key_create(texture, w, h, radius);
SDL_Texture * mask_frag = lv_draw_sdl_rect_bg_frag_obtain(ctx, radius);
SDL_Texture * img_frag = lv_draw_sdl_texture_cache_get(ctx, &key, sizeof(key), NULL);
if(img_frag == NULL) {
const lv_coord_t full_frag_size = radius * 2 + 3;
img_frag = SDL_CreateTexture(ctx->renderer, LV_DRAW_SDL_TEXTURE_FORMAT, SDL_TEXTUREACCESS_TARGET,
full_frag_size, full_frag_size);
SDL_SetTextureBlendMode(img_frag, SDL_BLENDMODE_BLEND);
SDL_Texture * old_target = SDL_GetRenderTarget(ctx->renderer);
SDL_SetRenderTarget(ctx->renderer, img_frag);
SDL_SetRenderDrawColor(ctx->renderer, 0, 0, 0, 0);
SDL_RenderClear(ctx->renderer);
lv_area_t coords = {0, 0, w - 1, h - 1}, clip;
lv_area_t frag_coords = {0, 0, full_frag_size - 1, full_frag_size - 1};
lv_draw_sdl_rect_bg_frag_draw_corners(ctx, mask_frag, radius, &frag_coords, NULL, false);
SDL_SetTextureAlphaMod(texture, 0xFF);
SDL_SetTextureColorMod(texture, 0xFF, 0xFF, 0xFF);
#if LV_GPU_SDL_CUSTOM_BLEND_MODE
SDL_BlendMode blend_mode = SDL_ComposeCustomBlendMode(SDL_BLENDFACTOR_ONE, SDL_BLENDFACTOR_ZERO,
SDL_BLENDOPERATION_ADD, SDL_BLENDFACTOR_DST_ALPHA,
SDL_BLENDFACTOR_ZERO, SDL_BLENDOPERATION_ADD);
SDL_SetTextureBlendMode(texture, blend_mode);
#else
SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_MOD);
#endif
SDL_Rect srcrect, cliprect, dstrect = {0, 0, radius, radius};
cliprect.w = cliprect.h = radius;
for(int i = 0; i <= ROUNDED_IMG_CORNER_BOTTOM_LEFT; i++) {
switch(i) {
case ROUNDED_IMG_CORNER_TOP_LEFT:
cliprect.x = 0;
cliprect.y = 0;
lv_area_align(&frag_coords, &coords, LV_ALIGN_TOP_LEFT, 0, 0);
break;
case ROUNDED_IMG_CORNER_TOP_RIGHT:
cliprect.x = full_frag_size - radius;
cliprect.y = 0;
lv_area_align(&frag_coords, &coords, LV_ALIGN_TOP_RIGHT, 0, 0);
break;
case ROUNDED_IMG_CORNER_BOTTOM_RIGHT:
cliprect.x = full_frag_size - radius;
cliprect.y = full_frag_size - radius;
lv_area_align(&frag_coords, &coords, LV_ALIGN_BOTTOM_RIGHT, 0, 0);
break;
case ROUNDED_IMG_CORNER_BOTTOM_LEFT:
cliprect.x = 0;
cliprect.y = full_frag_size - radius;
lv_area_align(&frag_coords, &coords, LV_ALIGN_BOTTOM_LEFT, 0, 0);
break;
default:
break;
}
calc_draw_part(texture, header, &coords, NULL, &srcrect, &dstrect);
SDL_RenderSetClipRect(ctx->renderer, &cliprect);
SDL_RenderCopy(ctx->renderer, texture, &srcrect, &dstrect);
}
SDL_RenderSetClipRect(ctx->renderer, NULL);
SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND);
SDL_SetRenderTarget(ctx->renderer, old_target);
lv_draw_sdl_texture_cache_put(ctx, &key, sizeof(key), img_frag);
}
return img_frag;
}
static lv_draw_img_rounded_key_t rounded_key_create(const SDL_Texture * texture, lv_coord_t w, lv_coord_t h,
lv_coord_t radius)
{
lv_draw_img_rounded_key_t key;
SDL_memset(&key, 0, sizeof(key));
key.magic = LV_GPU_CACHE_KEY_MAGIC_IMG_ROUNDED_CORNERS;
key.texture = texture;
key.w = w;
key.h = h;
key.radius = radius;
return key;
}
#endif /*LV_USE_GPU_SDL*/

View File

@@ -0,0 +1,72 @@
/**
* @file lv_draw_sdl_img.h
*
*/
#ifndef LV_DRAW_SDL_IMG_H
#define LV_DRAW_SDL_IMG_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../../lv_conf_internal.h"
#if LV_USE_GPU_SDL
#include LV_GPU_SDL_INCLUDE_PATH
#include "../lv_draw.h"
#include "lv_draw_sdl_texture_cache.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
typedef struct lv_draw_sdl_img_header_t {
lv_img_header_t base;
SDL_Rect rect;
} lv_draw_sdl_img_header_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
/*======================
* Add/remove functions
*=====================*/
/*=====================
* Setter functions
*====================*/
/*=====================
* Getter functions
*====================*/
/*=====================
* Other functions
*====================*/
bool lv_draw_sdl_img_load_texture(lv_draw_sdl_ctx_t * ctx, lv_draw_sdl_cache_key_head_img_t * key, size_t key_size,
const void * src, int32_t frame_id, SDL_Texture ** texture,
lv_draw_sdl_img_header_t ** header);
/**********************
* MACROS
**********************/
#endif /*LV_USE_GPU_SDL*/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_DRAW_SDL_IMG_H*/

View File

@@ -0,0 +1,173 @@
/**
* @file lv_draw_sdl_label.c
*
*/
/*********************
* INCLUDES
*********************/
#include "../../lv_conf_internal.h"
#if LV_USE_GPU_SDL
#include LV_GPU_SDL_INCLUDE_PATH
#include "../lv_draw_label.h"
#include "../../misc/lv_utils.h"
#include "lv_draw_sdl_utils.h"
#include "lv_draw_sdl_texture_cache.h"
#include "lv_draw_sdl_composite.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
typedef struct {
lv_sdl_cache_key_magic_t magic;
const lv_font_t * font_p;
uint32_t letter;
} lv_font_glyph_key_t;
/**********************
* STATIC PROTOTYPES
**********************/
static lv_font_glyph_key_t font_key_glyph_create(const lv_font_t * font_p, uint32_t letter);
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
void lv_draw_sdl_draw_letter(lv_draw_ctx_t * draw_ctx, const lv_draw_label_dsc_t * dsc, const lv_point_t * pos_p,
uint32_t letter)
{
const lv_area_t * clip_area = draw_ctx->clip_area;
const lv_font_t * font_p = dsc->font;
lv_opa_t opa = dsc->opa;
lv_color_t color = dsc->color;
if(opa < LV_OPA_MIN) return;
if(opa > LV_OPA_MAX) opa = LV_OPA_COVER;
if(font_p == NULL) {
LV_LOG_WARN("lv_draw_letter: font is NULL");
return;
}
lv_font_glyph_dsc_t g;
bool g_ret = lv_font_get_glyph_dsc(font_p, &g, letter, '\0');
if(g_ret == false) {
/*Add warning if the dsc is not found
*but do not print warning for non printable ASCII chars (e.g. '\n')*/
if(letter >= 0x20 &&
letter != 0xf8ff && /*LV_SYMBOL_DUMMY*/
letter != 0x200c) { /*ZERO WIDTH NON-JOINER*/
LV_LOG_WARN("lv_draw_letter: glyph dsc. not found for U+%X", letter);
/* draw placeholder */
lv_area_t glyph_coords;
lv_draw_rect_dsc_t glyph_dsc;
lv_coord_t begin_x = pos_p->x + g.ofs_x;
lv_coord_t begin_y = pos_p->y + g.ofs_y;
lv_area_set(&glyph_coords, begin_x, begin_y, begin_x + g.box_w, begin_y + g.box_h);
lv_draw_rect_dsc_init(&glyph_dsc);
glyph_dsc.bg_opa = LV_OPA_MIN;
glyph_dsc.outline_opa = LV_OPA_MIN;
glyph_dsc.shadow_opa = LV_OPA_MIN;
glyph_dsc.bg_img_opa = LV_OPA_MIN;
glyph_dsc.border_color = dsc->color;
glyph_dsc.border_width = 1;
draw_ctx->draw_rect(draw_ctx, &glyph_dsc, &glyph_coords);
}
return;
}
/*Don't draw anything if the character is empty. E.g. space*/
if((g.box_h == 0) || (g.box_w == 0)) return;
int32_t pos_x = pos_p->x + g.ofs_x;
int32_t pos_y = pos_p->y + (font_p->line_height - font_p->base_line) - g.box_h - g.ofs_y;
const lv_area_t letter_area = {pos_x, pos_y, pos_x + g.box_w - 1, pos_y + g.box_h - 1};
lv_area_t draw_area;
/*If the letter is completely out of mask don't draw it*/
if(!_lv_area_intersect(&draw_area, &letter_area, clip_area)) {
return;
}
lv_draw_sdl_ctx_t * ctx = (lv_draw_sdl_ctx_t *) draw_ctx;
SDL_Renderer * renderer = ctx->renderer;
lv_font_glyph_key_t glyph_key = font_key_glyph_create(font_p, letter);
bool glyph_found = false;
SDL_Texture * texture = lv_draw_sdl_texture_cache_get(ctx, &glyph_key, sizeof(glyph_key), &glyph_found);
if(!glyph_found) {
if(g.resolved_font) {
font_p = g.resolved_font;
}
const uint8_t * bmp = lv_font_get_glyph_bitmap(font_p, letter);
uint8_t * buf = lv_mem_alloc(g.box_w * g.box_h);
lv_sdl_to_8bpp(buf, bmp, g.box_w, g.box_h, g.box_w, g.bpp);
SDL_Surface * mask = lv_sdl_create_opa_surface(buf, g.box_w, g.box_h, g.box_w);
texture = SDL_CreateTextureFromSurface(renderer, mask);
SDL_FreeSurface(mask);
lv_mem_free(buf);
lv_draw_sdl_texture_cache_put(ctx, &glyph_key, sizeof(glyph_key), texture);
}
if(!texture) {
return;
}
lv_area_t t_letter = letter_area, t_clip = *clip_area, apply_area;
bool has_mask = lv_draw_sdl_composite_begin(ctx, &letter_area, clip_area, NULL, dsc->blend_mode, &t_letter, &t_clip,
&apply_area);
/*If the letter is completely out of mask don't draw it*/
if(!_lv_area_intersect(&draw_area, &t_letter, &t_clip)) {
return;
}
SDL_Rect srcrect, dstrect;
lv_area_to_sdl_rect(&draw_area, &dstrect);
srcrect.x = draw_area.x1 - t_letter.x1;
srcrect.y = draw_area.y1 - t_letter.y1;
srcrect.w = dstrect.w;
srcrect.h = dstrect.h;
SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND);
SDL_SetTextureAlphaMod(texture, opa);
SDL_SetTextureColorMod(texture, color.ch.red, color.ch.green, color.ch.blue);
SDL_RenderCopy(renderer, texture, &srcrect, &dstrect);
lv_draw_sdl_composite_end(ctx, &apply_area, dsc->blend_mode);
}
/**********************
* STATIC FUNCTIONS
**********************/
static lv_font_glyph_key_t font_key_glyph_create(const lv_font_t * font_p, uint32_t letter)
{
lv_font_glyph_key_t key;
/* VERY IMPORTANT! Padding between members is uninitialized, so we have to wipe them manually */
SDL_memset(&key, 0, sizeof(key));
key.magic = LV_GPU_CACHE_KEY_MAGIC_FONT_GLYPH;
key.font_p = font_p;
key.letter = letter;
return key;
}
#endif /*LV_USE_GPU_SDL*/

View File

@@ -0,0 +1,154 @@
/**
* @file lv_draw_sdl_line.c
*
*/
/*********************
* INCLUDES
*********************/
#include "../../lv_conf_internal.h"
#if LV_USE_GPU_SDL
#include "lv_draw_sdl.h"
#include "lv_draw_sdl_utils.h"
#include "lv_draw_sdl_texture_cache.h"
#include "lv_draw_sdl_composite.h"
#include "lv_draw_sdl_mask.h"
/*********************
* DEFINES
*********************/
#define ROUND_START 0x01
#define ROUND_END 0x02
/**********************
* TYPEDEFS
**********************/
typedef struct {
lv_sdl_cache_key_magic_t magic;
lv_coord_t length;
lv_coord_t width;
uint8_t round;
} lv_draw_line_key_t;
/**********************
* STATIC PROTOTYPES
**********************/
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
static lv_draw_line_key_t line_key_create(const lv_draw_line_dsc_t * dsc, lv_coord_t length);
static SDL_Texture * line_texture_create(lv_draw_sdl_ctx_t * sdl_ctx, const lv_draw_line_dsc_t * dsc,
lv_coord_t length);
/**********************
* GLOBAL FUNCTIONS
**********************/
void lv_draw_sdl_draw_line(lv_draw_ctx_t * draw_ctx, const lv_draw_line_dsc_t * dsc, const lv_point_t * point1,
const lv_point_t * point2)
{
lv_draw_sdl_ctx_t * sdl_ctx = (lv_draw_sdl_ctx_t *) draw_ctx;
SDL_Renderer * renderer = sdl_ctx->renderer;
lv_coord_t x1 = point1->x, x2 = point2->x, y1 = point1->y, y2 = point2->y;
double length = SDL_sqrt(SDL_pow(x2 - x1, 2) + SDL_pow(y2 - y1, 2));
if(length - (long) length > 0.5) {
length = (long) length + 1;
}
double angle = SDL_atan2(y2 - y1, x2 - x1) * 180 / M_PI;
lv_draw_line_key_t key = line_key_create(dsc, (lv_coord_t) length);
SDL_Texture * texture = lv_draw_sdl_texture_cache_get(sdl_ctx, &key, sizeof(key), NULL);
if(!texture) {
texture = line_texture_create(sdl_ctx, dsc, (lv_coord_t) length);
lv_draw_sdl_texture_cache_put(sdl_ctx, &key, sizeof(key), texture);
}
lv_area_t coords = {x1, y1, x2, y2};
const lv_area_t * clip = draw_ctx->clip_area;
SDL_Rect coords_r, clip_r;
lv_area_to_sdl_rect(&coords, &coords_r);
lv_area_to_sdl_rect(clip, &clip_r);
lv_area_t t_coords = coords, t_clip = *clip, apply_area;
lv_area_t extension = {dsc->width / 2, dsc->width / 2, dsc->width / 2, dsc->width / 2};
lv_draw_sdl_composite_begin(sdl_ctx, &coords, clip, &extension, dsc->blend_mode, &t_coords, &t_clip,
&apply_area);
SDL_Color color;
lv_color_to_sdl_color(&dsc->color, &color);
SDL_SetTextureColorMod(texture, color.r, color.g, color.b);
SDL_SetTextureAlphaMod(texture, dsc->opa);
SDL_Rect srcrect = {0, 0, (int) length + dsc->width + 2, dsc->width + 2},
dstrect = {t_coords.x1 - 1 - dsc->width / 2, t_coords.y1 - 1, srcrect.w, srcrect.h};
SDL_Point center = {1 + dsc->width / 2, 1 + dsc->width / 2};
SDL_Rect clip_rect;
lv_area_to_sdl_rect(&t_clip, &clip_rect);
if(!SDL_RectEquals(&clip_rect, &dstrect) || angle != 0) {
SDL_RenderSetClipRect(renderer, &clip_rect);
}
SDL_RenderCopyEx(renderer, texture, &srcrect, &dstrect, angle, &center, 0);
SDL_RenderSetClipRect(renderer, NULL);
lv_draw_sdl_composite_end(sdl_ctx, &apply_area, dsc->blend_mode);
}
/**********************
* STATIC FUNCTIONS
**********************/
static lv_draw_line_key_t line_key_create(const lv_draw_line_dsc_t * dsc, lv_coord_t length)
{
lv_draw_line_key_t key;
lv_memset_00(&key, sizeof(lv_draw_line_key_t));
key.magic = LV_GPU_CACHE_KEY_MAGIC_LINE;
key.length = length;
key.width = dsc->width;
key.round = (dsc->round_start ? ROUND_START : 0) | (dsc->round_end ? ROUND_END : 0);
return key;
}
static SDL_Texture * line_texture_create(lv_draw_sdl_ctx_t * sdl_ctx, const lv_draw_line_dsc_t * dsc, lv_coord_t length)
{
SDL_Texture * texture = SDL_CreateTexture(sdl_ctx->renderer, LV_DRAW_SDL_TEXTURE_FORMAT, SDL_TEXTUREACCESS_TARGET,
length + dsc->width + 2, dsc->width + 2);
SDL_Texture * target = SDL_GetRenderTarget(sdl_ctx->renderer);
SDL_SetRenderTarget(sdl_ctx->renderer, texture);
SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND);
SDL_SetRenderDrawColor(sdl_ctx->renderer, 0xFF, 0xFF, 0xFF, 0x0);
SDL_RenderClear(sdl_ctx->renderer);
SDL_SetRenderDrawColor(sdl_ctx->renderer, 0xFF, 0xFF, 0xFF, 0xFF);
SDL_Rect line_rect = {1 + dsc->width / 2, 1, length, dsc->width};
SDL_RenderFillRect(sdl_ctx->renderer, &line_rect);
if(dsc->round_start || dsc->round_end) {
lv_draw_mask_radius_param_t param;
lv_area_t round_area = {0, 0, dsc->width - 1, dsc->width - 1};
lv_draw_mask_radius_init(&param, &round_area, LV_RADIUS_CIRCLE, false);
int16_t mask_id = lv_draw_mask_add(&param, NULL);
SDL_Texture * round_texture = lv_draw_sdl_mask_dump_texture(sdl_ctx->renderer, &round_area, &mask_id, 1);
lv_draw_mask_remove_id(mask_id);
SDL_Rect round_src = {0, 0, dsc->width, dsc->width};
SDL_Rect round_dst = {line_rect.x - dsc->width / 2, 1, dsc->width, dsc->width};
SDL_RenderCopy(sdl_ctx->renderer, round_texture, &round_src, &round_dst);
round_dst.x = line_rect.w + dsc->width / 2;
SDL_RenderCopy(sdl_ctx->renderer, round_texture, &round_src, &round_dst);
SDL_DestroyTexture(round_texture);
}
SDL_SetRenderTarget(sdl_ctx->renderer, target);
return texture;
}
#endif /*LV_USE_GPU_SDL*/

View File

@@ -0,0 +1,84 @@
/**
* @file lv_draw_sdl_mask.c
*
*/
/*********************
* INCLUDES
*********************/
#include "../../lv_conf_internal.h"
#if LV_USE_GPU_SDL
#include "../../misc/lv_gc.h"
#include "lv_draw_sdl_mask.h"
#include "lv_draw_sdl_utils.h"
/*********************
* DEFINES
*********************/
#ifndef HAVE_SDL_CUSTOM_BLEND_MODE
#define HAVE_SDL_CUSTOM_BLEND_MODE (SDL_VERSION_ATLEAST(2, 0, 6))
#endif
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
lv_opa_t * lv_draw_sdl_mask_dump_opa(const lv_area_t * coords, const int16_t * ids, int16_t ids_count)
{
SDL_assert(coords->x2 >= coords->x1);
SDL_assert(coords->y2 >= coords->y1);
lv_coord_t w = lv_area_get_width(coords), h = lv_area_get_height(coords);
lv_opa_t * mask_buf = lv_mem_buf_get(w * h);
for(lv_coord_t y = 0; y < h; y++) {
lv_opa_t * line_buf = &mask_buf[y * w];
lv_memset_ff(line_buf, w);
lv_coord_t abs_x = (lv_coord_t) coords->x1, abs_y = (lv_coord_t)(y + coords->y1), len = (lv_coord_t) w;
lv_draw_mask_res_t res;
if(ids) {
res = lv_draw_mask_apply_ids(line_buf, abs_x, abs_y, len, ids, ids_count);
}
else {
res = lv_draw_mask_apply(line_buf, abs_x, abs_y, len);
}
if(res == LV_DRAW_MASK_RES_TRANSP) {
lv_memset_00(line_buf, w);
}
}
return mask_buf;
}
SDL_Texture * lv_draw_sdl_mask_dump_texture(SDL_Renderer * renderer, const lv_area_t * coords, const int16_t * ids,
int16_t ids_count)
{
lv_coord_t w = lv_area_get_width(coords), h = lv_area_get_height(coords);
lv_opa_t * mask_buf = lv_draw_sdl_mask_dump_opa(coords, ids, ids_count);
SDL_Surface * surface = lv_sdl_create_opa_surface(mask_buf, w, h, w);
lv_mem_buf_release(mask_buf);
SDL_Texture * texture = SDL_CreateTextureFromSurface(renderer, surface);
SDL_FreeSurface(surface);
return texture;
}
/**********************
* STATIC FUNCTIONS
**********************/
#endif /*LV_USE_GPU_SDL*/

View File

@@ -0,0 +1,51 @@
/**
* @file lv_draw_sdl_mask.h
*
*/
#ifndef LV_DRAW_SDL_MASK_H
#define LV_DRAW_SDL_MASK_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../../lv_conf_internal.h"
#include LV_GPU_SDL_INCLUDE_PATH
#include "lv_draw_sdl.h"
#include "../../misc/lv_area.h"
#include "../../misc/lv_color.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* GLOBAL PROTOTYPES
**********************/
lv_opa_t * lv_draw_sdl_mask_dump_opa(const lv_area_t * coords, const int16_t * ids, int16_t ids_count);
SDL_Texture * lv_draw_sdl_mask_dump_texture(SDL_Renderer * renderer, const lv_area_t * coords, const int16_t * ids,
int16_t ids_count);
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_DRAW_SDL_MASK_H*/

View File

@@ -0,0 +1,133 @@
/**
* @file lv_draw_sdl_polygon.c
*
*/
/*********************
* INCLUDES
*********************/
#include "../../lv_conf_internal.h"
#if LV_USE_GPU_SDL
#include "lv_draw_sdl.h"
#include "lv_draw_sdl_utils.h"
#include "lv_draw_sdl_texture_cache.h"
#include "lv_draw_sdl_composite.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
static void dump_masks(SDL_Texture * texture, const lv_area_t * coords);
/**********************
* GLOBAL FUNCTIONS
**********************/
void lv_draw_sdl_polygon(lv_draw_ctx_t * draw_ctx, const lv_draw_rect_dsc_t * draw_dsc, const lv_point_t * points,
uint16_t point_cnt)
{
if(point_cnt < 3) return;
if(points == NULL) return;
lv_draw_mask_polygon_param_t polygon_param;
lv_draw_mask_polygon_init(&polygon_param, points, point_cnt);
if(polygon_param.cfg.point_cnt < 3) {
lv_draw_mask_free_param(&polygon_param);
return;
}
lv_area_t poly_coords = {.x1 = LV_COORD_MAX, .y1 = LV_COORD_MAX, .x2 = LV_COORD_MIN, .y2 = LV_COORD_MIN};
uint16_t i;
for(i = 0; i < point_cnt; i++) {
poly_coords.x1 = LV_MIN(poly_coords.x1, polygon_param.cfg.points[i].x);
poly_coords.y1 = LV_MIN(poly_coords.y1, polygon_param.cfg.points[i].y);
poly_coords.x2 = LV_MAX(poly_coords.x2, polygon_param.cfg.points[i].x);
poly_coords.y2 = LV_MAX(poly_coords.y2, polygon_param.cfg.points[i].y);
}
bool is_common;
lv_area_t draw_area;
is_common = _lv_area_intersect(&draw_area, &poly_coords, draw_ctx->clip_area);
if(!is_common) {
lv_draw_mask_free_param(&polygon_param);
return;
}
lv_draw_sdl_ctx_t * ctx = (lv_draw_sdl_ctx_t *) draw_ctx;
int16_t mask_id = lv_draw_mask_add(&polygon_param, NULL);
lv_coord_t w = lv_area_get_width(&draw_area), h = lv_area_get_height(&draw_area);
SDL_Texture * texture = lv_draw_sdl_composite_texture_obtain(ctx, LV_DRAW_SDL_COMPOSITE_TEXTURE_ID_STREAM1, w, h);
SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND);
dump_masks(texture, &draw_area);
lv_draw_mask_remove_id(mask_id);
lv_draw_mask_free_param(&polygon_param);
SDL_Rect srcrect = {0, 0, w, h}, dstrect;
lv_area_to_sdl_rect(&draw_area, &dstrect);
SDL_Color color;
lv_color_to_sdl_color(&draw_dsc->bg_color, &color);
SDL_SetTextureColorMod(texture, color.r, color.g, color.b);
SDL_SetTextureAlphaMod(texture, draw_dsc->bg_opa);
SDL_RenderCopy(ctx->renderer, texture, &srcrect, &dstrect);
}
/**********************
* STATIC FUNCTIONS
**********************/
static void dump_masks(SDL_Texture * texture, const lv_area_t * coords)
{
lv_coord_t w = lv_area_get_width(coords), h = lv_area_get_height(coords);
SDL_assert(w > 0 && h > 0);
SDL_Rect rect = {0, 0, w, h};
uint8_t * pixels;
int pitch;
if(SDL_LockTexture(texture, &rect, (void **) &pixels, &pitch) != 0) return;
lv_opa_t * line_buf = lv_mem_buf_get(rect.w);
for(lv_coord_t y = 0; y < rect.h; y++) {
lv_memset_ff(line_buf, rect.w);
lv_coord_t abs_x = (lv_coord_t) coords->x1, abs_y = (lv_coord_t)(y + coords->y1), len = (lv_coord_t) rect.w;
lv_draw_mask_res_t res;
res = lv_draw_mask_apply(line_buf, abs_x, abs_y, len);
if(res == LV_DRAW_MASK_RES_TRANSP) {
lv_memset_00(&pixels[y * pitch], 4 * rect.w);
}
else if(res == LV_DRAW_MASK_RES_FULL_COVER) {
lv_memset_ff(&pixels[y * pitch], 4 * rect.w);
}
else {
for(int x = 0; x < rect.w; x++) {
uint8_t * pixel = &pixels[y * pitch + x * 4];
*pixel = line_buf[x];
pixel[1] = pixel[2] = pixel[3] = 0xFF;
}
}
}
lv_mem_buf_release(line_buf);
SDL_UnlockTexture(texture);
}
#endif /*LV_USE_GPU_SDL*/

View File

@@ -0,0 +1,70 @@
/**
* @file lv_draw_sdl_priv.h
*
*/
#ifndef LV_DRAW_SDL_PRIV_H
#define LV_DRAW_SDL_PRIV_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../../lv_conf_internal.h"
#if LV_USE_GPU_SDL
#include LV_GPU_SDL_INCLUDE_PATH
#include "../lv_draw.h"
#include "../../misc/lv_lru.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
typedef struct lv_draw_sdl_context_internals_t {
lv_lru_t * texture_cache;
SDL_Texture * mask;
SDL_Texture * composition;
} lv_draw_sdl_context_internals_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
/*======================
* Add/remove functions
*=====================*/
/*=====================
* Setter functions
*====================*/
/*=====================
* Getter functions
*====================*/
/*=====================
* Other functions
*====================*/
/**********************
* MACROS
**********************/
#endif /*LV_USE_GPU_SDL*/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_DRAW_SDL_PRIV_H*/

View File

@@ -0,0 +1,708 @@
/**
* @file lv_draw_sdl_rect.c
*
*/
/*********************
* INCLUDES
*********************/
#include "../../lv_conf_internal.h"
#if LV_USE_GPU_SDL
#include "../lv_draw_rect.h"
#include "../lv_draw_img.h"
#include "../lv_draw_label.h"
#include "../lv_draw_mask.h"
#include "../../core/lv_refr.h"
#include "lv_draw_sdl_utils.h"
#include "lv_draw_sdl_texture_cache.h"
#include "lv_draw_sdl_composite.h"
#include "lv_draw_sdl_mask.h"
#include "lv_draw_sdl_stack_blur.h"
#include "lv_draw_sdl_img.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
typedef struct {
lv_sdl_cache_key_magic_t magic;
lv_coord_t radius;
lv_coord_t size;
} lv_draw_rect_bg_key_t;
typedef struct {
lv_sdl_cache_key_magic_t magic;
lv_coord_t radius;
lv_coord_t size;
lv_coord_t blur;
} lv_draw_rect_shadow_key_t;
typedef struct {
lv_sdl_cache_key_magic_t magic;
lv_coord_t rout, rin;
lv_area_t offsets;
} lv_draw_rect_border_key_t;
/**********************
* STATIC PROTOTYPES
**********************/
static void draw_bg_color(lv_draw_sdl_ctx_t * ctx, const lv_area_t * coords, const lv_area_t * draw_area,
const lv_draw_rect_dsc_t * dsc);
static void draw_bg_img(lv_draw_sdl_ctx_t * ctx, const lv_area_t * coords, const lv_area_t * draw_area,
const lv_draw_rect_dsc_t * dsc);
static void draw_border(lv_draw_sdl_ctx_t * ctx, const lv_area_t * coords, const lv_area_t * draw_area,
const lv_draw_rect_dsc_t * dsc);
static void draw_shadow(lv_draw_sdl_ctx_t * ctx, const lv_area_t * coords, const lv_area_t * clip,
const lv_draw_rect_dsc_t * dsc);
static void draw_outline(lv_draw_sdl_ctx_t * ctx, const lv_area_t * coords, const lv_area_t * clip,
const lv_draw_rect_dsc_t * dsc);
static void draw_border_generic(lv_draw_sdl_ctx_t * ctx, const lv_area_t * outer_area, const lv_area_t * inner_area,
const lv_area_t * clip, lv_coord_t rout, lv_coord_t rin, lv_color_t color, lv_opa_t opa,
lv_blend_mode_t blend_mode);
static void frag_render_borders(SDL_Renderer * renderer, SDL_Texture * frag, lv_coord_t frag_size,
const lv_area_t * coords, const lv_area_t * clipped, bool full);
static void frag_render_center(SDL_Renderer * renderer, SDL_Texture * frag, lv_coord_t frag_size,
const lv_area_t * coords, const lv_area_t * clipped, bool full);
static lv_draw_rect_bg_key_t rect_bg_key_create(lv_coord_t radius, lv_coord_t size);
static lv_draw_rect_shadow_key_t rect_shadow_key_create(lv_coord_t radius, lv_coord_t size, lv_coord_t blur);
static lv_draw_rect_border_key_t rect_border_key_create(lv_coord_t rout, lv_coord_t rin, const lv_area_t * outer_area,
const lv_area_t * inner_area);
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
#define SKIP_BORDER(dsc) ((dsc)->border_opa <= LV_OPA_MIN || (dsc)->border_width == 0 || (dsc)->border_side == LV_BORDER_SIDE_NONE || (dsc)->border_post)
#define SKIP_SHADOW(dsc) ((dsc)->shadow_width == 0 || (dsc)->shadow_opa <= LV_OPA_MIN || ((dsc)->shadow_width == 1 && (dsc)->shadow_spread <= 0 && (dsc)->shadow_ofs_x == 0 && (dsc)->shadow_ofs_y == 0))
#define SKIP_IMAGE(dsc) ((dsc)->bg_img_src == NULL || (dsc)->bg_img_opa <= LV_OPA_MIN)
#define SKIP_OUTLINE(dsc) ((dsc)->outline_opa <= LV_OPA_MIN || (dsc)->outline_width == 0)
/**********************
* GLOBAL FUNCTIONS
**********************/
void lv_draw_sdl_draw_rect(lv_draw_ctx_t * draw_ctx, const lv_draw_rect_dsc_t * dsc, const lv_area_t * coords)
{
const lv_area_t * clip = draw_ctx->clip_area;
lv_draw_sdl_ctx_t * ctx = (lv_draw_sdl_ctx_t *) draw_ctx;
lv_area_t extension = {0, 0, 0, 0};
if(!SKIP_SHADOW(dsc)) {
lv_coord_t ext = (lv_coord_t)(dsc->shadow_spread - dsc->shadow_width / 2 + 1);
extension.x1 = LV_MAX(extension.x1, -dsc->shadow_ofs_x + ext);
extension.x2 = LV_MAX(extension.x2, dsc->shadow_ofs_x + ext);
extension.y1 = LV_MAX(extension.y1, -dsc->shadow_ofs_y + ext);
extension.y2 = LV_MAX(extension.y2, dsc->shadow_ofs_y + ext);
}
if(!SKIP_OUTLINE(dsc)) {
lv_coord_t ext = (lv_coord_t)(dsc->outline_pad - 1 + dsc->outline_width);
extension.x1 = LV_MAX(extension.x1, ext);
extension.x2 = LV_MAX(extension.x2, ext);
extension.y1 = LV_MAX(extension.y1, ext);
extension.y2 = LV_MAX(extension.y2, ext);
}
/* Coords will be translated so coords will start at (0,0) */
lv_area_t t_coords = *coords, t_clip = *clip, apply_area, t_area;
lv_draw_sdl_composite_begin(ctx, coords, clip, &extension, dsc->blend_mode, &t_coords, &t_clip, &apply_area);
bool has_content = _lv_area_intersect(&t_area, &t_coords, &t_clip);
SDL_Rect clip_rect;
lv_area_to_sdl_rect(&t_clip, &clip_rect);
draw_shadow(ctx, &t_coords, &t_clip, dsc);
/* Shadows and outlines will also draw in extended area */
if(has_content) {
draw_bg_color(ctx, &t_coords, &t_area, dsc);
draw_bg_img(ctx, &t_coords, &t_area, dsc);
draw_border(ctx, &t_coords, &t_area, dsc);
}
draw_outline(ctx, &t_coords, &t_clip, dsc);
lv_draw_sdl_composite_end(ctx, &apply_area, dsc->blend_mode);
}
SDL_Texture * lv_draw_sdl_rect_bg_frag_obtain(lv_draw_sdl_ctx_t * ctx, lv_coord_t radius)
{
lv_draw_rect_bg_key_t key = rect_bg_key_create(radius, radius);
lv_area_t coords = {0, 0, radius * 2 - 1, radius * 2 - 1};
lv_area_t coords_frag = {0, 0, radius - 1, radius - 1};
SDL_Texture * texture = lv_draw_sdl_texture_cache_get(ctx, &key, sizeof(key), NULL);
if(texture == NULL) {
lv_draw_mask_radius_param_t mask_rout_param;
lv_draw_mask_radius_init(&mask_rout_param, &coords, radius, false);
int16_t mask_id = lv_draw_mask_add(&mask_rout_param, NULL);
texture = lv_draw_sdl_mask_dump_texture(ctx->renderer, &coords_frag, &mask_id, 1);
lv_draw_mask_remove_id(mask_id);
SDL_assert(texture);
lv_draw_sdl_texture_cache_put(ctx, &key, sizeof(key), texture);
}
return texture;
}
void lv_draw_sdl_rect_bg_frag_draw_corners(lv_draw_sdl_ctx_t * ctx, SDL_Texture * frag, lv_coord_t frag_size,
const lv_area_t * coords, const lv_area_t * clip, bool full)
{
if(!clip) clip = coords;
lv_area_t corner_area, dst_area;
/* Upper left */
corner_area.x1 = coords->x1;
corner_area.y1 = coords->y1;
corner_area.x2 = coords->x1 + frag_size - 1;
corner_area.y2 = coords->y1 + frag_size - 1;
if(_lv_area_intersect(&dst_area, &corner_area, clip)) {
SDL_Rect dst_rect;
lv_area_to_sdl_rect(&dst_area, &dst_rect);
lv_coord_t dw = lv_area_get_width(&dst_area), dh = lv_area_get_height(&dst_area);
lv_coord_t sx = (lv_coord_t)(dst_area.x1 - corner_area.x1), sy = (lv_coord_t)(dst_area.y1 - corner_area.y1);
SDL_Rect src_rect = {sx, sy, dw, dh};
SDL_RenderCopy(ctx->renderer, frag, &src_rect, &dst_rect);
}
/* Upper right, clip right edge if too big */
corner_area.x1 = LV_MAX(coords->x2 - frag_size + 1, coords->x1 + frag_size);
corner_area.x2 = coords->x2;
if(_lv_area_intersect(&dst_area, &corner_area, clip)) {
SDL_Rect dst_rect;
lv_area_to_sdl_rect(&dst_area, &dst_rect);
lv_coord_t dw = lv_area_get_width(&dst_area), dh = lv_area_get_height(&dst_area);
if(full) {
lv_coord_t sx = (lv_coord_t)(dst_area.x1 - corner_area.x1),
sy = (lv_coord_t)(dst_area.y1 - corner_area.y1);
SDL_Rect src_rect = {frag_size + 3 + sx, sy, dw, dh};
SDL_RenderCopy(ctx->renderer, frag, &src_rect, &dst_rect);
}
else {
SDL_Rect src_rect = {corner_area.x2 - dst_area.x2, dst_area.y1 - corner_area.y1, dw, dh};
SDL_RenderCopyEx(ctx->renderer, frag, &src_rect, &dst_rect, 0, NULL, SDL_FLIP_HORIZONTAL);
}
}
/* Lower right, clip bottom edge if too big */
corner_area.y1 = LV_MAX(coords->y2 - frag_size + 1, coords->y1 + frag_size);
corner_area.y2 = coords->y2;
if(_lv_area_intersect(&dst_area, &corner_area, clip)) {
SDL_Rect dst_rect;
lv_area_to_sdl_rect(&dst_area, &dst_rect);
lv_coord_t dw = lv_area_get_width(&dst_area), dh = lv_area_get_height(&dst_area);
if(full) {
lv_coord_t sx = (lv_coord_t)(dst_area.x1 - corner_area.x1),
sy = (lv_coord_t)(dst_area.y1 - corner_area.y1);
SDL_Rect src_rect = {frag_size + 3 + sx, frag_size + 3 + sy, dw, dh};
SDL_RenderCopy(ctx->renderer, frag, &src_rect, &dst_rect);
}
else {
SDL_Rect src_rect = {corner_area.x2 - dst_area.x2, corner_area.y2 - dst_area.y2, dw, dh};
SDL_RenderCopyEx(ctx->renderer, frag, &src_rect, &dst_rect, 0, NULL, SDL_FLIP_HORIZONTAL | SDL_FLIP_VERTICAL);
}
}
/* Lower left, right edge should not be clip */
corner_area.x1 = coords->x1;
corner_area.x2 = coords->x1 + frag_size - 1;
if(_lv_area_intersect(&dst_area, &corner_area, clip)) {
SDL_Rect dst_rect;
lv_area_to_sdl_rect(&dst_area, &dst_rect);
lv_coord_t dw = lv_area_get_width(&dst_area), dh = lv_area_get_height(&dst_area);
if(full) {
lv_coord_t sx = (lv_coord_t)(dst_area.x1 - corner_area.x1),
sy = (lv_coord_t)(dst_area.y1 - corner_area.y1);
SDL_Rect src_rect = {sx, frag_size + 3 + sy, dw, dh};
SDL_RenderCopy(ctx->renderer, frag, &src_rect, &dst_rect);
}
else {
SDL_Rect src_rect = {dst_area.x1 - corner_area.x1, corner_area.y2 - dst_area.y2, dw, dh};
SDL_RenderCopyEx(ctx->renderer, frag, &src_rect, &dst_rect, 0, NULL, SDL_FLIP_VERTICAL);
}
}
}
/**********************
* STATIC FUNCTIONS
**********************/
static void draw_bg_color(lv_draw_sdl_ctx_t * ctx, const lv_area_t * coords, const lv_area_t * draw_area,
const lv_draw_rect_dsc_t * dsc)
{
if(dsc->bg_opa == 0) {
return;
}
SDL_Color bg_color;
lv_color_to_sdl_color(&dsc->bg_color, &bg_color);
lv_coord_t radius = dsc->radius;
if(radius <= 0) {
SDL_Rect rect;
lv_area_to_sdl_rect(draw_area, &rect);
SDL_SetRenderDrawColor(ctx->renderer, bg_color.r, bg_color.g, bg_color.b, dsc->bg_opa);
SDL_SetRenderDrawBlendMode(ctx->renderer, SDL_BLENDMODE_BLEND);
SDL_RenderFillRect(ctx->renderer, &rect);
return;
}
/*A small texture with a quarter of the rect is enough*/
lv_coord_t bg_w = lv_area_get_width(coords), bg_h = lv_area_get_height(coords);
lv_coord_t real_radius = LV_MIN3(bg_w / 2, bg_h / 2, radius);
SDL_Texture * texture = lv_draw_sdl_rect_bg_frag_obtain(ctx, real_radius);
SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND);
SDL_SetTextureAlphaMod(texture, dsc->bg_opa);
SDL_SetTextureColorMod(texture, bg_color.r, bg_color.g, bg_color.b);
lv_draw_sdl_rect_bg_frag_draw_corners(ctx, texture, real_radius, coords, draw_area, false);
frag_render_borders(ctx->renderer, texture, real_radius, coords, draw_area, false);
frag_render_center(ctx->renderer, texture, real_radius, coords, draw_area, false);
}
static void draw_bg_img(lv_draw_sdl_ctx_t * ctx, const lv_area_t * coords, const lv_area_t * draw_area,
const lv_draw_rect_dsc_t * dsc)
{
if(SKIP_IMAGE(dsc)) return;
lv_img_src_t src_type = lv_img_src_get_type(dsc->bg_img_src);
if(src_type == LV_IMG_SRC_SYMBOL) {
lv_point_t size;
lv_txt_get_size(&size, dsc->bg_img_src, dsc->bg_img_symbol_font, 0, 0, LV_COORD_MAX, LV_TEXT_FLAG_NONE);
lv_area_t a;
a.x1 = coords->x1 + lv_area_get_width(coords) / 2 - size.x / 2;
a.x2 = a.x1 + size.x - 1;
a.y1 = coords->y1 + lv_area_get_height(coords) / 2 - size.y / 2;
a.y2 = a.y1 + size.y - 1;
lv_draw_label_dsc_t label_draw_dsc;
lv_draw_label_dsc_init(&label_draw_dsc);
label_draw_dsc.font = dsc->bg_img_symbol_font;
label_draw_dsc.color = dsc->bg_img_recolor;
label_draw_dsc.opa = dsc->bg_img_opa;
lv_draw_label((lv_draw_ctx_t *) ctx, &label_draw_dsc, &a, dsc->bg_img_src, NULL);
}
else {
lv_img_header_t header;
size_t key_size;
lv_draw_sdl_cache_key_head_img_t * key = lv_draw_sdl_texture_img_key_create(dsc->bg_img_src, 0, &key_size);
bool key_found;
lv_img_header_t * cache_header = NULL;
SDL_Texture * texture = lv_draw_sdl_texture_cache_get_with_userdata(ctx, key, key_size, &key_found,
(void **) &cache_header);
SDL_free(key);
if(texture) {
header = *cache_header;
}
else if(key_found || lv_img_decoder_get_info(dsc->bg_img_src, &header) != LV_RES_OK) {
/* When cache hit but with negative result, use default decoder. If still fail, return.*/
LV_LOG_WARN("Couldn't read the background image");
return;
}
lv_draw_img_dsc_t img_dsc;
lv_draw_img_dsc_init(&img_dsc);
img_dsc.blend_mode = dsc->blend_mode;
img_dsc.recolor = dsc->bg_img_recolor;
img_dsc.recolor_opa = dsc->bg_img_recolor_opa;
img_dsc.opa = dsc->bg_img_opa;
img_dsc.frame_id = 0;
int16_t radius_mask_id = LV_MASK_ID_INV;
lv_draw_mask_radius_param_t radius_param;
if(dsc->radius > 0) {
lv_draw_mask_radius_init(&radius_param, coords, dsc->radius, false);
radius_mask_id = lv_draw_mask_add(&radius_param, NULL);
}
/*Center align*/
if(dsc->bg_img_tiled == false) {
lv_area_t area;
area.x1 = coords->x1 + lv_area_get_width(coords) / 2 - header.w / 2;
area.y1 = coords->y1 + lv_area_get_height(coords) / 2 - header.h / 2;
area.x2 = area.x1 + header.w - 1;
area.y2 = area.y1 + header.h - 1;
lv_draw_img((lv_draw_ctx_t *) ctx, &img_dsc, &area, dsc->bg_img_src);
}
else {
lv_area_t area;
area.y1 = coords->y1;
area.y2 = area.y1 + header.h - 1;
for(; area.y1 <= coords->y2; area.y1 += header.h, area.y2 += header.h) {
area.x1 = coords->x1;
area.x2 = area.x1 + header.w - 1;
for(; area.x1 <= coords->x2; area.x1 += header.w, area.x2 += header.w) {
lv_draw_img((lv_draw_ctx_t *) ctx, &img_dsc, &area, dsc->bg_img_src);
}
}
}
if(radius_mask_id != LV_MASK_ID_INV) {
lv_draw_mask_remove_id(radius_mask_id);
lv_draw_mask_free_param(&radius_param);
}
}
}
static void draw_shadow(lv_draw_sdl_ctx_t * ctx, const lv_area_t * coords, const lv_area_t * clip,
const lv_draw_rect_dsc_t * dsc)
{
/*Check whether the shadow is visible*/
if(SKIP_SHADOW(dsc)) return;
lv_coord_t sw = dsc->shadow_width;
lv_area_t core_area;
core_area.x1 = coords->x1 + dsc->shadow_ofs_x - dsc->shadow_spread;
core_area.x2 = coords->x2 + dsc->shadow_ofs_x + dsc->shadow_spread;
core_area.y1 = coords->y1 + dsc->shadow_ofs_y - dsc->shadow_spread;
core_area.y2 = coords->y2 + dsc->shadow_ofs_y + dsc->shadow_spread;
lv_area_t shadow_area;
shadow_area.x1 = core_area.x1 - sw / 2 - 1;
shadow_area.x2 = core_area.x2 + sw / 2 + 1;
shadow_area.y1 = core_area.y1 - sw / 2 - 1;
shadow_area.y2 = core_area.y2 + sw / 2 + 1;
lv_opa_t opa = dsc->shadow_opa;
if(opa > LV_OPA_MAX) opa = LV_OPA_COVER;
/*Get clipped draw area which is the real draw area.
*It is always the same or inside `shadow_area`*/
lv_area_t draw_area;
if(!_lv_area_intersect(&draw_area, &shadow_area, clip)) return;
SDL_Rect core_area_rect;
lv_area_to_sdl_rect(&shadow_area, &core_area_rect);
lv_coord_t radius = dsc->radius;
/* No matter how big the shadow is, what we need is just a corner */
lv_coord_t frag_size = LV_MIN3(lv_area_get_width(&core_area) / 2, lv_area_get_height(&core_area) / 2,
LV_MAX(sw / 2, radius));
/* This is how big the corner is after blurring */
lv_coord_t blur_growth = (lv_coord_t)(sw / 2 + 1);
lv_coord_t blur_frag_size = (lv_coord_t)(frag_size + blur_growth);
lv_draw_rect_shadow_key_t key = rect_shadow_key_create(radius, frag_size, sw);
SDL_Texture * texture = lv_draw_sdl_texture_cache_get(ctx, &key, sizeof(key), NULL);
if(texture == NULL) {
lv_area_t mask_area = {blur_growth, blur_growth}, mask_area_blurred = {0, 0};
lv_area_set_width(&mask_area, frag_size * 2);
lv_area_set_height(&mask_area, frag_size * 2);
lv_area_set_width(&mask_area_blurred, blur_frag_size * 2);
lv_area_set_height(&mask_area_blurred, blur_frag_size * 2);
lv_draw_mask_radius_param_t mask_rout_param;
lv_draw_mask_radius_init(&mask_rout_param, &mask_area, radius, false);
int16_t mask_id = lv_draw_mask_add(&mask_rout_param, NULL);
lv_opa_t * mask_buf = lv_draw_sdl_mask_dump_opa(&mask_area_blurred, &mask_id, 1);
lv_stack_blur_grayscale(mask_buf, lv_area_get_width(&mask_area_blurred), lv_area_get_height(&mask_area_blurred),
sw / 2 + sw % 2);
texture = lv_sdl_create_opa_texture(ctx->renderer, mask_buf, blur_frag_size, blur_frag_size,
lv_area_get_width(&mask_area_blurred));
lv_mem_buf_release(mask_buf);
lv_draw_mask_remove_id(mask_id);
SDL_assert(texture);
lv_draw_sdl_texture_cache_put(ctx, &key, sizeof(key), texture);
}
SDL_Color shadow_color;
lv_color_to_sdl_color(&dsc->shadow_color, &shadow_color);
SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND);
SDL_SetTextureAlphaMod(texture, opa);
SDL_SetTextureColorMod(texture, shadow_color.r, shadow_color.g, shadow_color.b);
lv_draw_sdl_rect_bg_frag_draw_corners(ctx, texture, blur_frag_size, &shadow_area, clip, false);
frag_render_borders(ctx->renderer, texture, blur_frag_size, &shadow_area, clip, false);
frag_render_center(ctx->renderer, texture, blur_frag_size, &shadow_area, clip, false);
}
static void draw_border(lv_draw_sdl_ctx_t * ctx, const lv_area_t * coords, const lv_area_t * draw_area,
const lv_draw_rect_dsc_t * dsc)
{
if(SKIP_BORDER(dsc)) return;
SDL_Color border_color;
lv_color_to_sdl_color(&dsc->border_color, &border_color);
lv_coord_t coords_w = lv_area_get_width(coords), coords_h = lv_area_get_height(coords);
lv_coord_t short_side = LV_MIN(coords_w, coords_h);
lv_coord_t rout = LV_MIN(dsc->radius, short_side / 2);/*Get the inner area*/
lv_area_t area_inner;
lv_area_copy(&area_inner, coords);// lv_area_increase(&area_inner, 1, 1);
area_inner.x1 += ((dsc->border_side & LV_BORDER_SIDE_LEFT) ? dsc->border_width : -(dsc->border_width + rout));
area_inner.x2 -= ((dsc->border_side & LV_BORDER_SIDE_RIGHT) ? dsc->border_width : -(dsc->border_width + rout));
area_inner.y1 += ((dsc->border_side & LV_BORDER_SIDE_TOP) ? dsc->border_width : -(dsc->border_width + rout));
area_inner.y2 -= ((dsc->border_side & LV_BORDER_SIDE_BOTTOM) ? dsc->border_width : -(dsc->border_width + rout));
lv_coord_t rin = LV_MAX(rout - dsc->border_width, 0);
draw_border_generic(ctx, coords, &area_inner, draw_area, rout, rin, dsc->border_color, dsc->border_opa,
dsc->blend_mode);
}
static void draw_outline(lv_draw_sdl_ctx_t * ctx, const lv_area_t * coords, const lv_area_t * clip,
const lv_draw_rect_dsc_t * dsc)
{
if(SKIP_OUTLINE(dsc)) return;
lv_opa_t opa = dsc->outline_opa;
if(opa > LV_OPA_MAX) opa = LV_OPA_COVER;
/*Get the inner radius*/
lv_area_t area_inner;
lv_area_copy(&area_inner, coords);
/*Bring the outline closer to make sure there is no color bleeding with pad=0*/
lv_coord_t pad = dsc->outline_pad - 1;
area_inner.x1 -= pad;
area_inner.y1 -= pad;
area_inner.x2 += pad;
area_inner.y2 += pad;
lv_area_t area_outer;
lv_area_copy(&area_outer, &area_inner);
area_outer.x1 -= dsc->outline_width;
area_outer.x2 += dsc->outline_width;
area_outer.y1 -= dsc->outline_width;
area_outer.y2 += dsc->outline_width;
lv_area_t draw_area;
if(!_lv_area_intersect(&draw_area, &area_outer, clip)) return;
int32_t inner_w = lv_area_get_width(&area_inner);
int32_t inner_h = lv_area_get_height(&area_inner);
lv_coord_t rin = dsc->radius;
int32_t short_side = LV_MIN(inner_w, inner_h);
if(rin > short_side >> 1) rin = short_side >> 1;
lv_coord_t rout = rin + dsc->outline_width;
draw_border_generic(ctx, &area_outer, &area_inner, clip, rout, rin, dsc->outline_color, dsc->outline_opa,
dsc->blend_mode);
}
static void draw_border_generic(lv_draw_sdl_ctx_t * ctx, const lv_area_t * outer_area, const lv_area_t * inner_area,
const lv_area_t * clip, lv_coord_t rout, lv_coord_t rin, lv_color_t color, lv_opa_t opa,
lv_blend_mode_t blend_mode)
{
opa = opa >= LV_OPA_COVER ? LV_OPA_COVER : opa;
SDL_Renderer * renderer = ctx->renderer;
lv_draw_rect_border_key_t key = rect_border_key_create(rout, rin, outer_area, inner_area);
lv_coord_t radius = LV_MIN3(rout, lv_area_get_width(outer_area) / 2, lv_area_get_height(outer_area) / 2);
lv_coord_t max_side = LV_MAX4(key.offsets.x1, key.offsets.y1, -key.offsets.x2, -key.offsets.y2);
lv_coord_t frag_size = LV_MAX(radius, max_side);
SDL_Texture * texture = lv_draw_sdl_texture_cache_get(ctx, &key, sizeof(key), NULL);
if(texture == NULL) {
/* Create a mask texture with size of (frag_size * 2 + 3) */
const lv_area_t frag_area = {0, 0, frag_size * 2 + 2, frag_size * 2 + 2};
/*Create mask for the outer area*/
int16_t mask_ids[2] = {LV_MASK_ID_INV, LV_MASK_ID_INV};
lv_draw_mask_radius_param_t mask_rout_param;
if(rout > 0) {
lv_draw_mask_radius_init(&mask_rout_param, &frag_area, rout, false);
mask_ids[0] = lv_draw_mask_add(&mask_rout_param, NULL);
}
/*Create mask for the inner mask*/
if(rin < 0) rin = 0;
const lv_area_t frag_inner_area = {frag_area.x1 + key.offsets.x1, frag_area.y1 + key.offsets.y1,
frag_area.x2 + key.offsets.x2, frag_area.y2 + key.offsets.y2
};
lv_draw_mask_radius_param_t mask_rin_param;
lv_draw_mask_radius_init(&mask_rin_param, &frag_inner_area, rin, true);
mask_ids[1] = lv_draw_mask_add(&mask_rin_param, NULL);
texture = lv_draw_sdl_mask_dump_texture(renderer, &frag_area, mask_ids, 2);
lv_draw_mask_remove_id(mask_ids[1]);
lv_draw_mask_remove_id(mask_ids[0]);
SDL_assert(texture);
lv_draw_sdl_texture_cache_put(ctx, &key, sizeof(key), texture);
}
SDL_Rect outer_rect;
lv_area_to_sdl_rect(outer_area, &outer_rect);
SDL_Color color_sdl;
lv_color_to_sdl_color(&color, &color_sdl);
SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND);
SDL_SetTextureAlphaMod(texture, opa);
SDL_SetTextureColorMod(texture, color_sdl.r, color_sdl.g, color_sdl.b);
lv_draw_sdl_rect_bg_frag_draw_corners(ctx, texture, frag_size, outer_area, clip, true);
frag_render_borders(renderer, texture, frag_size, outer_area, clip, true);
}
static void frag_render_borders(SDL_Renderer * renderer, SDL_Texture * frag, lv_coord_t frag_size,
const lv_area_t * coords, const lv_area_t * clipped, bool full)
{
lv_area_t border_area, dst_area;
/* Top border */
border_area.x1 = coords->x1 + frag_size;
border_area.y1 = coords->y1;
border_area.x2 = coords->x2 - frag_size;
border_area.y2 = coords->y1 + frag_size - 1;
if(_lv_area_intersect(&dst_area, &border_area, clipped)) {
SDL_Rect dst_rect;
lv_area_to_sdl_rect(&dst_area, &dst_rect);
lv_coord_t sy = (lv_coord_t)(dst_area.y1 - border_area.y1);
if(full) {
SDL_Rect src_rect = {frag_size + 1, sy, 1, lv_area_get_height(&dst_area)};
SDL_RenderCopy(renderer, frag, &src_rect, &dst_rect);
}
else {
SDL_Rect src_rect = {frag_size - 1, sy, 1, lv_area_get_height(&dst_area)};
SDL_RenderCopy(renderer, frag, &src_rect, &dst_rect);
}
}
/* Bottom border */
border_area.y1 = LV_MAX(coords->y2 - frag_size + 1, coords->y1 + frag_size);
border_area.y2 = coords->y2;
if(_lv_area_intersect(&dst_area, &border_area, clipped)) {
SDL_Rect dst_rect;
lv_area_to_sdl_rect(&dst_area, &dst_rect);
lv_coord_t dh = lv_area_get_height(&dst_area);
if(full) {
lv_coord_t sy = (lv_coord_t)(dst_area.y1 - border_area.y1);
SDL_Rect src_rect = {frag_size + 1, frag_size + 3 + sy, 1, dh};
SDL_RenderCopy(renderer, frag, &src_rect, &dst_rect);
}
else {
lv_coord_t sy = (lv_coord_t)(border_area.y2 - dst_area.y2);
SDL_Rect src_rect = {frag_size - 1, sy, 1, dh};
SDL_RenderCopyEx(renderer, frag, &src_rect, &dst_rect, 0, NULL, SDL_FLIP_VERTICAL);
}
}
/* Left border */
border_area.x1 = coords->x1;
border_area.y1 = coords->y1 + frag_size;
border_area.x2 = coords->x1 + frag_size - 1;
border_area.y2 = coords->y2 - frag_size;
if(_lv_area_intersect(&dst_area, &border_area, clipped)) {
SDL_Rect dst_rect;
lv_area_to_sdl_rect(&dst_area, &dst_rect);
lv_coord_t dw = lv_area_get_width(&dst_area);
lv_coord_t sx = (lv_coord_t)(dst_area.x1 - border_area.x1);
if(full) {
SDL_Rect src_rect = {sx, frag_size + 1, dw, 1};
SDL_RenderCopy(renderer, frag, &src_rect, &dst_rect);
}
else {
SDL_Rect src_rect = {sx, frag_size - 1, dw, 1};
SDL_RenderCopy(renderer, frag, &src_rect, &dst_rect);
}
}
/* Right border */
border_area.x1 = LV_MAX(coords->x2 - frag_size + 1, coords->x1 + frag_size);
border_area.x2 = coords->x2;
if(_lv_area_intersect(&dst_area, &border_area, clipped)) {
SDL_Rect dst_rect;
lv_area_to_sdl_rect(&dst_area, &dst_rect);
lv_coord_t dw = lv_area_get_width(&dst_area);
if(full) {
lv_coord_t sx = (lv_coord_t)(dst_area.x1 - border_area.x1);
SDL_Rect src_rect = {frag_size + 3 + sx, frag_size + 1, dw, 1};
SDL_RenderCopy(renderer, frag, &src_rect, &dst_rect);
}
else {
lv_coord_t sx = (lv_coord_t)(border_area.x2 - dst_area.x2);
SDL_Rect src_rect = {sx, frag_size - 1, dw, 1};
SDL_RenderCopyEx(renderer, frag, &src_rect, &dst_rect, 0, NULL, SDL_FLIP_HORIZONTAL);
}
}
}
static void frag_render_center(SDL_Renderer * renderer, SDL_Texture * frag, lv_coord_t frag_size,
const lv_area_t * coords,
const lv_area_t * clipped, bool full)
{
lv_area_t center_area = {
coords->x1 + frag_size,
coords->y1 + frag_size,
coords->x2 - frag_size,
coords->y2 - frag_size,
};
if(center_area.x2 < center_area.x1 || center_area.y2 < center_area.y1) return;
lv_area_t draw_area;
if(!_lv_area_intersect(&draw_area, &center_area, clipped)) {
return;
}
SDL_Rect dst_rect;
lv_area_to_sdl_rect(&draw_area, &dst_rect);
if(full) {
SDL_Rect src_rect = {frag_size, frag_size, 1, 1};
SDL_RenderCopy(renderer, frag, &src_rect, &dst_rect);
}
else {
SDL_Rect src_rect = {frag_size - 1, frag_size - 1, 1, 1};
SDL_RenderCopy(renderer, frag, &src_rect, &dst_rect);
}
}
static lv_draw_rect_bg_key_t rect_bg_key_create(lv_coord_t radius, lv_coord_t size)
{
lv_draw_rect_bg_key_t key;
SDL_memset(&key, 0, sizeof(key));
key.magic = LV_GPU_CACHE_KEY_MAGIC_RECT_BG;
key.radius = radius;
key.size = size;
return key;
}
static lv_draw_rect_shadow_key_t rect_shadow_key_create(lv_coord_t radius, lv_coord_t size, lv_coord_t blur)
{
lv_draw_rect_shadow_key_t key;
SDL_memset(&key, 0, sizeof(key));
key.magic = LV_GPU_CACHE_KEY_MAGIC_RECT_SHADOW;
key.radius = radius;
key.size = size;
key.blur = blur;
return key;
}
static lv_draw_rect_border_key_t rect_border_key_create(lv_coord_t rout, lv_coord_t rin, const lv_area_t * outer_area,
const lv_area_t * inner_area)
{
lv_draw_rect_border_key_t key;
/* VERY IMPORTANT! Padding between members is uninitialized, so we have to wipe them manually */
SDL_memset(&key, 0, sizeof(key));
key.magic = LV_GPU_CACHE_KEY_MAGIC_RECT_BORDER;
key.rout = rout;
key.rin = rin;
key.offsets.x1 = inner_area->x1 - outer_area->x1;
key.offsets.x2 = inner_area->x2 - outer_area->x2;
key.offsets.y1 = inner_area->y1 - outer_area->y1;
key.offsets.y2 = inner_area->y2 - outer_area->y2;
return key;
}
#endif /*LV_USE_GPU_SDL*/

View File

@@ -0,0 +1,75 @@
/**
* @file lv_draw_sdl_rect.h
*
*/
#ifndef LV_DRAW_SDL_RECT_H
#define LV_DRAW_SDL_RECT_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../../lv_conf_internal.h"
#if LV_USE_GPU_SDL
#include LV_GPU_SDL_INCLUDE_PATH
#include "../lv_draw.h"
#include "lv_draw_sdl_texture_cache.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
typedef struct lv_draw_sdl_rect_header_t {
lv_img_header_t base;
SDL_Rect rect;
} lv_draw_sdl_rect_header_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
/*======================
* Add/remove functions
*=====================*/
/*=====================
* Setter functions
*====================*/
/*=====================
* Getter functions
*====================*/
/*=====================
* Other functions
*====================*/
SDL_Texture * lv_draw_sdl_rect_bg_frag_obtain(lv_draw_sdl_ctx_t * ctx, lv_coord_t radius);
void lv_draw_sdl_rect_bg_frag_draw_corners(lv_draw_sdl_ctx_t * ctx, SDL_Texture * frag, lv_coord_t frag_size,
const lv_area_t * coords, const lv_area_t * clip, bool full);
/**********************
* MACROS
**********************/
#endif /*LV_USE_GPU_SDL*/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_DRAW_SDL_RECT_H*/

View File

@@ -0,0 +1,249 @@
/**
* @file lv_draw_sdl_stack_blur.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_draw_sdl_stack_blur.h"
#if LV_USE_GPU_SDL
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static void
stack_blur_job(lv_opa_t * src, unsigned int w, unsigned int h, unsigned int radius, int cores, int core, int step);
/**********************
* STATIC VARIABLES
**********************/
// Based heavily on http://vitiy.info/Code/stackblur.cpp
// See http://vitiy.info/stackblur-algorithm-multi-threaded-blur-for-cpp/
// Stack Blur Algorithm by Mario Klingemann <mario@quasimondo.com>
static unsigned short const stackblur_mul[255] = {
512, 512, 456, 512, 328, 456, 335, 512, 405, 328, 271, 456, 388, 335, 292, 512,
454, 405, 364, 328, 298, 271, 496, 456, 420, 388, 360, 335, 312, 292, 273, 512,
482, 454, 428, 405, 383, 364, 345, 328, 312, 298, 284, 271, 259, 496, 475, 456,
437, 420, 404, 388, 374, 360, 347, 335, 323, 312, 302, 292, 282, 273, 265, 512,
497, 482, 468, 454, 441, 428, 417, 405, 394, 383, 373, 364, 354, 345, 337, 328,
320, 312, 305, 298, 291, 284, 278, 271, 265, 259, 507, 496, 485, 475, 465, 456,
446, 437, 428, 420, 412, 404, 396, 388, 381, 374, 367, 360, 354, 347, 341, 335,
329, 323, 318, 312, 307, 302, 297, 292, 287, 282, 278, 273, 269, 265, 261, 512,
505, 497, 489, 482, 475, 468, 461, 454, 447, 441, 435, 428, 422, 417, 411, 405,
399, 394, 389, 383, 378, 373, 368, 364, 359, 354, 350, 345, 341, 337, 332, 328,
324, 320, 316, 312, 309, 305, 301, 298, 294, 291, 287, 284, 281, 278, 274, 271,
268, 265, 262, 259, 257, 507, 501, 496, 491, 485, 480, 475, 470, 465, 460, 456,
451, 446, 442, 437, 433, 428, 424, 420, 416, 412, 408, 404, 400, 396, 392, 388,
385, 381, 377, 374, 370, 367, 363, 360, 357, 354, 350, 347, 344, 341, 338, 335,
332, 329, 326, 323, 320, 318, 315, 312, 310, 307, 304, 302, 299, 297, 294, 292,
289, 287, 285, 282, 280, 278, 275, 273, 271, 269, 267, 265, 263, 261, 259
};
static unsigned char const stackblur_shr[255] = {
9, 11, 12, 13, 13, 14, 14, 15, 15, 15, 15, 16, 16, 16, 16, 17,
17, 17, 17, 17, 17, 17, 18, 18, 18, 18, 18, 18, 18, 18, 18, 19,
19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 20, 20, 20,
20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 21,
21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21,
21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 22, 22, 22, 22, 22, 22,
22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22,
22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 23,
23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23,
23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23,
23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23,
23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24
};
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
void lv_stack_blur_grayscale(lv_opa_t * buf, uint16_t w, uint16_t h, uint16_t r)
{
stack_blur_job(buf, w, h, r, 1, 0, 1);
stack_blur_job(buf, w, h, r, 1, 0, 2);
}
/**********************
* STATIC FUNCTIONS
**********************/
static void stack_blur_job(lv_opa_t * src, unsigned int w, unsigned int h, unsigned int radius, int cores, int core,
int step)
{
if(radius < 2 || radius > 254) {
/* Silently ignore bad radius */
return;
}
unsigned int x, y, xp, yp, i;
unsigned int sp;
unsigned int stack_start;
unsigned char * stack_ptr;
lv_opa_t * src_ptr;
lv_opa_t * dst_ptr;
unsigned long sum_r;
unsigned long sum_in_r;
unsigned long sum_out_r;
unsigned int wm = w - 1;
unsigned int hm = h - 1;
unsigned int stride = w;
unsigned int div = (radius * 2) + 1;
unsigned int mul_sum = stackblur_mul[radius];
unsigned char shr_sum = stackblur_shr[radius];
unsigned char stack[254 * 2 + 1];
if(step == 1) {
unsigned int minY = core * h / cores;
unsigned int maxY = (core + 1) * h / cores;
for(y = minY; y < maxY; y++) {
sum_r =
sum_in_r =
sum_out_r = 0;
src_ptr = src + stride * y; // start of line (0,y)
for(i = 0; i <= radius; i++) {
stack_ptr = &stack[i];
stack_ptr[0] = src_ptr[0];
sum_r += src_ptr[0] * (i + 1);
sum_out_r += src_ptr[0];
}
for(i = 1; i <= radius; i++) {
if(i <= wm) src_ptr += 1;
stack_ptr = &stack[i + radius];
stack_ptr[0] = src_ptr[0];
sum_r += src_ptr[0] * (radius + 1 - i);
sum_in_r += src_ptr[0];
}
sp = radius;
xp = radius;
if(xp > wm) xp = wm;
src_ptr = src + (xp + y * w); // img.pix_ptr(xp, y);
dst_ptr = src + y * stride; // img.pix_ptr(0, y);
for(x = 0; x < w; x++) {
dst_ptr[0] = LV_CLAMP((sum_r * mul_sum) >> shr_sum, 0, 255);
dst_ptr += 1;
sum_r -= sum_out_r;
stack_start = sp + div - radius;
if(stack_start >= div) stack_start -= div;
stack_ptr = &stack[stack_start];
sum_out_r -= stack_ptr[0];
if(xp < wm) {
src_ptr += 1;
++xp;
}
stack_ptr[0] = src_ptr[0];
sum_in_r += src_ptr[0];
sum_r += sum_in_r;
++sp;
if(sp >= div) sp = 0;
stack_ptr = &stack[sp];
sum_out_r += stack_ptr[0];
sum_in_r -= stack_ptr[0];
}
}
}
// step 2
if(step == 2) {
unsigned int minX = core * w / cores;
unsigned int maxX = (core + 1) * w / cores;
for(x = minX; x < maxX; x++) {
sum_r =
sum_in_r =
sum_out_r = 0;
src_ptr = src + x; // x,0
for(i = 0; i <= radius; i++) {
stack_ptr = &stack[i];
stack_ptr[0] = src_ptr[0];
sum_r += src_ptr[0] * (i + 1);
sum_out_r += src_ptr[0];
}
for(i = 1; i <= radius; i++) {
if(i <= hm) src_ptr += stride; // +stride
stack_ptr = &stack[i + radius];
stack_ptr[0] = src_ptr[0];
sum_r += src_ptr[0] * (radius + 1 - i);
sum_in_r += src_ptr[0];
}
sp = radius;
yp = radius;
if(yp > hm) yp = hm;
src_ptr = src + (x + yp * w); // img.pix_ptr(x, yp);
dst_ptr = src + x; // img.pix_ptr(x, 0);
for(y = 0; y < h; y++) {
dst_ptr[0] = LV_CLAMP((sum_r * mul_sum) >> shr_sum, 0, 255);
dst_ptr += stride;
sum_r -= sum_out_r;
stack_start = sp + div - radius;
if(stack_start >= div) stack_start -= div;
stack_ptr = &stack[stack_start];
sum_out_r -= stack_ptr[0];
if(yp < hm) {
src_ptr += stride; // stride
++yp;
}
stack_ptr[0] = src_ptr[0];
sum_in_r += src_ptr[0];
sum_r += sum_in_r;
++sp;
if(sp >= div) sp = 0;
stack_ptr = &stack[sp];
sum_out_r += stack_ptr[0];
sum_in_r -= stack_ptr[0];
}
}
}
}
#endif /*LV_USE_GPU_SDL*/

View File

@@ -0,0 +1,46 @@
/**
* @file lv_draw_sdl_stack_blur.h
*
*/
#ifndef LV_DRAW_SDL_STACK_BLUR_H
#define LV_DRAW_SDL_STACK_BLUR_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../../lv_conf_internal.h"
#if LV_USE_GPU_SDL
#include "../../misc/lv_color.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* GLOBAL PROTOTYPES
**********************/
void lv_stack_blur_grayscale(lv_opa_t * buf, uint16_t w, uint16_t h, uint16_t r);
/**********************
* MACROS
**********************/
#endif /*LV_USE_GPU_SDL*/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_DRAW_SDL_STACK_BLUR_H*/

View File

@@ -0,0 +1,178 @@
/**
* @file lv_draw_sdl_texture_cache.c
*
*/
/*********************
* INCLUDES
*********************/
#include "../../lv_conf_internal.h"
#if LV_USE_GPU_SDL
#include "lv_draw_sdl_texture_cache.h"
#include "lv_draw_sdl_utils.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
typedef struct {
SDL_Texture * texture;
void * userdata;
lv_lru_free_t * userdata_free;
lv_draw_sdl_cache_flag_t flags;
} draw_cache_value_t;
typedef struct {
lv_sdl_cache_key_magic_t magic;
} temp_texture_key_t;
typedef struct {
lv_coord_t width, height;
} temp_texture_userdata_t;
static void draw_cache_free_value(draw_cache_value_t *);
static draw_cache_value_t * draw_cache_get_entry(lv_draw_sdl_ctx_t * ctx, const void * key, size_t key_length,
bool * found);
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
void lv_draw_sdl_texture_cache_init(lv_draw_sdl_ctx_t * ctx)
{
ctx->internals->texture_cache = lv_lru_create(LV_GPU_SDL_LRU_SIZE, 65536,
(lv_lru_free_t *) draw_cache_free_value, NULL);
}
void lv_draw_sdl_texture_cache_deinit(lv_draw_sdl_ctx_t * ctx)
{
lv_lru_del(ctx->internals->texture_cache);
}
SDL_Texture * lv_draw_sdl_texture_cache_get(lv_draw_sdl_ctx_t * ctx, const void * key, size_t key_length, bool * found)
{
return lv_draw_sdl_texture_cache_get_with_userdata(ctx, key, key_length, found, NULL);
}
SDL_Texture * lv_draw_sdl_texture_cache_get_with_userdata(lv_draw_sdl_ctx_t * ctx, const void * key, size_t key_length,
bool * found, void ** userdata)
{
draw_cache_value_t * value = draw_cache_get_entry(ctx, key, key_length, found);
if(!value) return NULL;
if(userdata) {
*userdata = value->userdata;
}
return value->texture;
}
void lv_draw_sdl_texture_cache_put(lv_draw_sdl_ctx_t * ctx, const void * key, size_t key_length, SDL_Texture * texture)
{
lv_draw_sdl_texture_cache_put_advanced(ctx, key, key_length, texture, NULL, NULL, 0);
}
void lv_draw_sdl_texture_cache_put_advanced(lv_draw_sdl_ctx_t * ctx, const void * key, size_t key_length,
SDL_Texture * texture, void * userdata, void userdata_free(void *),
lv_draw_sdl_cache_flag_t flags)
{
lv_lru_t * lru = ctx->internals->texture_cache;
draw_cache_value_t * value = SDL_malloc(sizeof(draw_cache_value_t));
value->texture = texture;
value->userdata = userdata;
value->userdata_free = userdata_free;
value->flags = flags;
if(!texture) {
lv_lru_set(lru, key, key_length, value, 1);
return;
}
if(flags & LV_DRAW_SDL_CACHE_FLAG_MANAGED) {
/* Managed texture doesn't count into cache size */
LV_LOG_INFO("cache texture %p", texture);
lv_lru_set(lru, key, key_length, value, 1);
return;
}
Uint32 format;
int access, width, height;
if(SDL_QueryTexture(texture, &format, &access, &width, &height) != 0) {
return;
}
LV_LOG_INFO("cache texture %p, %d*%d@%dbpp", texture, width, height, SDL_BITSPERPIXEL(format));
lv_lru_set(lru, key, key_length, value, width * height * SDL_BITSPERPIXEL(format) / 8);
}
lv_draw_sdl_cache_key_head_img_t * lv_draw_sdl_texture_img_key_create(const void * src, int32_t frame_id, size_t * size)
{
lv_draw_sdl_cache_key_head_img_t header;
/* VERY IMPORTANT! Padding between members is uninitialized, so we have to wipe them manually */
SDL_memset(&header, 0, sizeof(header));
header.magic = LV_GPU_CACHE_KEY_MAGIC_IMG;
header.type = lv_img_src_get_type(src);
header.frame_id = frame_id;
void * key;
size_t key_size;
if(header.type == LV_IMG_SRC_FILE || header.type == LV_IMG_SRC_SYMBOL) {
size_t srclen = SDL_strlen(src);
key_size = sizeof(header) + srclen;
key = SDL_malloc(key_size);
SDL_memcpy(key, &header, sizeof(header));
/*Copy string content as key value*/
SDL_memcpy(key + sizeof(header), src, srclen);
}
else {
key_size = sizeof(header) + sizeof(void *);
key = SDL_malloc(key_size);
SDL_memcpy(key, &header, sizeof(header));
/*Copy address number as key value*/
SDL_memcpy(key + sizeof(header), &src, sizeof(void *));
}
*size = key_size;
return (lv_draw_sdl_cache_key_head_img_t *) key;
}
static void draw_cache_free_value(draw_cache_value_t * value)
{
if(value->texture && !(value->flags & LV_DRAW_SDL_CACHE_FLAG_MANAGED)) {
LV_LOG_INFO("destroy texture %p", value->texture);
SDL_DestroyTexture(value->texture);
}
if(value->userdata_free) {
value->userdata_free(value->userdata);
}
SDL_free(value);
}
static draw_cache_value_t * draw_cache_get_entry(lv_draw_sdl_ctx_t * ctx, const void * key, size_t key_length,
bool * found)
{
lv_lru_t * lru = ctx->internals->texture_cache;
draw_cache_value_t * value = NULL;
lv_lru_get(lru, key, key_length, (void **) &value);
if(!value) {
if(found) {
*found = false;
}
return NULL;
}
if(found) {
*found = true;
}
return value;
}
#endif /*LV_USE_GPU_SDL*/

View File

@@ -0,0 +1,102 @@
/**
* @file lv_draw_sdl_texture_cache.h
*
*/
#ifndef LV_DRAW_SDL_TEXTURE_CACHE_H
#define LV_DRAW_SDL_TEXTURE_CACHE_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../../lv_conf_internal.h"
#if LV_USE_GPU_SDL
#include LV_GPU_SDL_INCLUDE_PATH
#include "lv_draw_sdl.h"
#include "lv_draw_sdl_priv.h"
#include "../../draw/lv_img_decoder.h"
#include "../../misc/lv_area.h"
/*********************
* DEFINES
*********************/
#define LV_DRAW_SDL_DEC_DSC_TEXTURE_HEAD "@LVSDLTex"
/**********************
* TYPEDEFS
**********************/
typedef struct {
char head[8];
SDL_Texture * texture;
SDL_Rect rect;
bool texture_managed;
bool texture_referenced;
} lv_draw_sdl_dec_dsc_userdata_t;
typedef enum {
LV_GPU_CACHE_KEY_MAGIC_ARC = 0x01,
LV_GPU_CACHE_KEY_MAGIC_IMG = 0x11,
LV_GPU_CACHE_KEY_MAGIC_IMG_ROUNDED_CORNERS = 0x12,
LV_GPU_CACHE_KEY_MAGIC_LINE = 0x21,
LV_GPU_CACHE_KEY_MAGIC_RECT_BG = 0x31,
LV_GPU_CACHE_KEY_MAGIC_RECT_SHADOW = 0x32,
LV_GPU_CACHE_KEY_MAGIC_RECT_BORDER = 0x33,
LV_GPU_CACHE_KEY_MAGIC_FONT_GLYPH = 0x41,
LV_GPU_CACHE_KEY_MAGIC_MASK = 0x51,
} lv_sdl_cache_key_magic_t;
typedef enum {
LV_DRAW_SDL_CACHE_FLAG_NONE = 0,
LV_DRAW_SDL_CACHE_FLAG_MANAGED = 1,
} lv_draw_sdl_cache_flag_t;
typedef struct {
lv_sdl_cache_key_magic_t magic;
lv_img_src_t type;
int32_t frame_id;
} lv_draw_sdl_cache_key_head_img_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
void lv_draw_sdl_texture_cache_init(lv_draw_sdl_ctx_t * ctx);
void lv_draw_sdl_texture_cache_deinit(lv_draw_sdl_ctx_t * ctx);
/**
* Find cached texture by key. The texture can be destroyed during usage.
*/
SDL_Texture * lv_draw_sdl_texture_cache_get(lv_draw_sdl_ctx_t * ctx, const void * key, size_t key_length, bool * found);
SDL_Texture * lv_draw_sdl_texture_cache_get_with_userdata(lv_draw_sdl_ctx_t * ctx, const void * key, size_t key_length,
bool * found, void ** userdata);
void lv_draw_sdl_texture_cache_put(lv_draw_sdl_ctx_t * ctx, const void * key, size_t key_length, SDL_Texture * texture);
void lv_draw_sdl_texture_cache_put_advanced(lv_draw_sdl_ctx_t * ctx, const void * key, size_t key_length,
SDL_Texture * texture, void * userdata, void userdata_free(void *),
lv_draw_sdl_cache_flag_t flags);
lv_draw_sdl_cache_key_head_img_t * lv_draw_sdl_texture_img_key_create(const void * src, int32_t frame_id,
size_t * size);
/**********************
* MACROS
**********************/
#endif /*LV_USE_GPU_SDL*/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_DRAW_SDL_TEXTURE_CACHE_H*/

View File

@@ -0,0 +1,183 @@
/**
* @file lv_draw_sdl_utils.c
*
*/
/*********************
* INCLUDES
*********************/
#include "../../lv_conf_internal.h"
#if LV_USE_GPU_SDL
#include "lv_draw_sdl_utils.h"
#include "../lv_draw.h"
#include "../lv_draw_label.h"
#include "../../core/lv_refr.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
/**********************
* STATIC VARIABLES
**********************/
extern const uint8_t _lv_bpp1_opa_table[2];
extern const uint8_t _lv_bpp2_opa_table[4];
extern const uint8_t _lv_bpp4_opa_table[16];
extern const uint8_t _lv_bpp8_opa_table[256];
static int utils_init_count = 0;
static SDL_Palette * lv_sdl_palette_grayscale8 = NULL;
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
void _lv_draw_sdl_utils_init()
{
utils_init_count++;
if(utils_init_count > 1) {
return;
}
lv_sdl_palette_grayscale8 = lv_sdl_alloc_palette_for_bpp(_lv_bpp8_opa_table, 8);
}
void _lv_draw_sdl_utils_deinit()
{
if(utils_init_count == 0) {
return;
}
utils_init_count--;
if(utils_init_count == 0) {
SDL_FreePalette(lv_sdl_palette_grayscale8);
lv_sdl_palette_grayscale8 = NULL;
}
}
void lv_area_to_sdl_rect(const lv_area_t * in, SDL_Rect * out)
{
out->x = in->x1;
out->y = in->y1;
out->w = in->x2 - in->x1 + 1;
out->h = in->y2 - in->y1 + 1;
}
void lv_color_to_sdl_color(const lv_color_t * in, SDL_Color * out)
{
#if LV_COLOR_DEPTH == 32
out->a = in->ch.alpha;
out->r = in->ch.red;
out->g = in->ch.green;
out->b = in->ch.blue;
#else
uint32_t color32 = lv_color_to32(*in);
lv_color32_t * color32_t = (lv_color32_t *) &color32;
out->a = color32_t->ch.alpha;
out->r = color32_t->ch.red;
out->g = color32_t->ch.green;
out->b = color32_t->ch.blue;
#endif
}
void lv_area_zoom_to_sdl_rect(const lv_area_t * in, SDL_Rect * out, uint16_t zoom, const lv_point_t * pivot)
{
if(zoom == LV_IMG_ZOOM_NONE) {
lv_area_to_sdl_rect(in, out);
return;
}
lv_area_t tmp;
_lv_img_buf_get_transformed_area(&tmp, lv_area_get_width(in), lv_area_get_height(in), 0, zoom, pivot);
lv_area_move(&tmp, in->x1, in->y1);
lv_area_to_sdl_rect(&tmp, out);
}
SDL_Palette * lv_sdl_alloc_palette_for_bpp(const uint8_t * mapping, uint8_t bpp)
{
SDL_assert(bpp >= 1 && bpp <= 8);
int color_cnt = 1 << bpp;
SDL_Palette * result = SDL_AllocPalette(color_cnt);
SDL_Color palette[256];
for(int i = 0; i < color_cnt; i++) {
palette[i].r = palette[i].g = palette[i].b = 0xFF;
palette[i].a = mapping ? mapping[i] : i;
}
SDL_SetPaletteColors(result, palette, 0, color_cnt);
return result;
}
SDL_Surface * lv_sdl_create_opa_surface(lv_opa_t * opa, lv_coord_t width, lv_coord_t height, lv_coord_t stride)
{
SDL_Surface * indexed = SDL_CreateRGBSurfaceFrom(opa, width, height, 8, stride, 0, 0, 0, 0);
SDL_SetSurfacePalette(indexed, lv_sdl_palette_grayscale8);
SDL_Surface * converted = SDL_ConvertSurfaceFormat(indexed, LV_DRAW_SDL_TEXTURE_FORMAT, 0);
SDL_FreeSurface(indexed);
return converted;
}
SDL_Texture * lv_sdl_create_opa_texture(SDL_Renderer * renderer, lv_opa_t * pixels, lv_coord_t width,
lv_coord_t height, lv_coord_t stride)
{
SDL_Surface * indexed = lv_sdl_create_opa_surface(pixels, width, height, stride);
SDL_Texture * texture = SDL_CreateTextureFromSurface(renderer, indexed);
SDL_FreeSurface(indexed);
return texture;
}
void lv_sdl_to_8bpp(uint8_t * dest, const uint8_t * src, int width, int height, int stride, uint8_t bpp)
{
int src_len = width * height;
int cur = 0;
int curbit;
uint8_t opa_mask;
const uint8_t * opa_table;
switch(bpp) {
case 1:
opa_mask = 0x1;
opa_table = _lv_bpp1_opa_table;
break;
case 2:
opa_mask = 0x4;
opa_table = _lv_bpp2_opa_table;
break;
case 4:
opa_mask = 0xF;
opa_table = _lv_bpp4_opa_table;
break;
case 8:
opa_mask = 0xFF;
opa_table = _lv_bpp8_opa_table;
break;
default:
return;
}
/* Does this work well on big endian systems? */
while(cur < src_len) {
curbit = 8 - bpp;
uint8_t src_byte = src[cur * bpp / 8];
while(curbit >= 0 && cur < src_len) {
uint8_t src_bits = opa_mask & (src_byte >> curbit);
dest[(cur / width * stride) + (cur % width)] = opa_table[src_bits];
curbit -= bpp;
cur++;
}
}
}
/**********************
* STATIC FUNCTIONS
**********************/
#endif /*LV_USE_GPU_SDL*/

View File

@@ -0,0 +1,65 @@
/**
* @file lv_draw_sdl_utils.h
*
*/
#ifndef LV_DRAW_SDL_UTILS_H
#define LV_DRAW_SDL_UTILS_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../../lv_conf_internal.h"
#if LV_USE_GPU_SDL
#include "lv_draw_sdl.h"
#include "../../misc/lv_color.h"
#include "../../misc/lv_area.h"
#include LV_GPU_SDL_INCLUDE_PATH
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* GLOBAL PROTOTYPES
**********************/
void _lv_draw_sdl_utils_init();
void _lv_draw_sdl_utils_deinit();
void lv_area_to_sdl_rect(const lv_area_t * in, SDL_Rect * out);
void lv_color_to_sdl_color(const lv_color_t * in, SDL_Color * out);
void lv_area_zoom_to_sdl_rect(const lv_area_t * in, SDL_Rect * out, uint16_t zoom, const lv_point_t * pivot);
SDL_Palette * lv_sdl_alloc_palette_for_bpp(const uint8_t * mapping, uint8_t bpp);
SDL_Surface * lv_sdl_create_opa_surface(lv_opa_t * opa, lv_coord_t width, lv_coord_t height, lv_coord_t stride);
SDL_Texture * lv_sdl_create_opa_texture(SDL_Renderer * renderer, lv_opa_t * pixels, lv_coord_t width,
lv_coord_t height, lv_coord_t stride);
void lv_sdl_to_8bpp(uint8_t * dest, const uint8_t * src, int width, int height, int stride, uint8_t bpp);
/**********************
* MACROS
**********************/
#endif /*LV_USE_GPU_SDL*/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_DRAW_SDL_UTILS_H*/

View File

@@ -0,0 +1,6 @@
CSRCS += lv_gpu_stm32_dma2d.c
DEPPATH += --dep-path $(LVGL_DIR)/$(LVGL_DIR_NAME)/src/draw/stm32_dma2d
VPATH += :$(LVGL_DIR)/$(LVGL_DIR_NAME)/src/draw/stm32_dma2d
CFLAGS += "-I$(LVGL_DIR)/$(LVGL_DIR_NAME)/src/draw/stm32_dma2d"

View File

@@ -0,0 +1,265 @@
/**
* @file lv_gpu_stm32_dma2d.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_gpu_stm32_dma2d.h"
#include "../../core/lv_refr.h"
#if LV_USE_GPU_STM32_DMA2D
#include LV_GPU_DMA2D_CMSIS_INCLUDE
/*********************
* DEFINES
*********************/
#if LV_COLOR_16_SWAP
// TODO: F7 has red blue swap bit in control register for all layers and output
#error "Can't use DMA2D with LV_COLOR_16_SWAP 1"
#endif
#if LV_COLOR_DEPTH == 8
#error "Can't use DMA2D with LV_COLOR_DEPTH == 8"
#endif
#if LV_COLOR_DEPTH == 16
#define LV_DMA2D_COLOR_FORMAT LV_DMA2D_RGB565
#elif LV_COLOR_DEPTH == 32
#define LV_DMA2D_COLOR_FORMAT LV_DMA2D_ARGB8888
#else
/*Can't use GPU with other formats*/
#endif
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static void lv_draw_stm32_dma2d_blend_fill(lv_color_t * dest_buf, lv_coord_t dest_stride, const lv_area_t * fill_area,
lv_color_t color);
static void lv_draw_stm32_dma2d_blend_map(lv_color_t * dest_buf, const lv_area_t * dest_area, lv_coord_t dest_stride,
const lv_color_t * src_buf, lv_coord_t src_stride, lv_opa_t opa);
static void lv_draw_stm32_dma2d_img_decoded(lv_draw_ctx_t * draw, const lv_draw_img_dsc_t * dsc,
const lv_area_t * coords, const uint8_t * map_p, lv_img_cf_t color_format);
static void invalidate_cache(void);
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
/**
* Turn on the peripheral and set output color mode, this only needs to be done once
*/
void lv_draw_stm32_dma2d_init(void)
{
/*Enable DMA2D clock*/
#if defined(STM32F4) || defined(STM32F7)
RCC->AHB1ENR |= RCC_AHB1ENR_DMA2DEN;
#elif defined(STM32H7)
RCC->AHB3ENR |= RCC_AHB3ENR_DMA2DEN;
#else
# warning "LVGL can't enable the clock of DMA2D"
#endif
/*Wait for hardware access to complete*/
__asm volatile("DSB\n");
/*Delay after setting peripheral clock*/
volatile uint32_t temp = RCC->AHB1ENR;
LV_UNUSED(temp);
/*set output colour mode*/
DMA2D->OPFCCR = LV_DMA2D_COLOR_FORMAT;
}
void lv_draw_stm32_dma2d_ctx_init(lv_disp_drv_t * drv, lv_draw_ctx_t * draw_ctx)
{
lv_draw_sw_init_ctx(drv, draw_ctx);
lv_draw_stm32_dma2d_ctx_t * dma2d_draw_ctx = (lv_draw_sw_ctx_t *)draw_ctx;
dma2d_draw_ctx->blend = lv_draw_stm32_dma2d_blend;
// dma2d_draw_ctx->base_draw.draw_img_decoded = lv_draw_stm32_dma2d_img_decoded;
dma2d_draw_ctx->base_draw.wait_for_finish = lv_gpu_stm32_dma2d_wait_cb;
dma2d_draw_ctx->base_draw.buffer_copy = lv_draw_stm32_dma2d_buffer_copy;
}
void lv_draw_stm32_dma2d_ctx_deinit(lv_disp_drv_t * drv, lv_draw_ctx_t * draw_ctx)
{
LV_UNUSED(drv);
LV_UNUSED(draw_ctx);
}
void lv_draw_stm32_dma2d_blend(lv_draw_ctx_t * draw_ctx, const lv_draw_sw_blend_dsc_t * dsc)
{
lv_area_t blend_area;
if(!_lv_area_intersect(&blend_area, dsc->blend_area, draw_ctx->clip_area)) return;
bool done = false;
if(dsc->mask_buf == NULL && dsc->blend_mode == LV_BLEND_MODE_NORMAL && lv_area_get_size(&blend_area) > 100) {
lv_coord_t dest_stride = lv_area_get_width(draw_ctx->buf_area);
lv_color_t * dest_buf = draw_ctx->buf;
dest_buf += dest_stride * (blend_area.y1 - draw_ctx->buf_area->y1) + (blend_area.x1 - draw_ctx->buf_area->x1);
const lv_color_t * src_buf = dsc->src_buf;
if(src_buf) {
lv_draw_sw_blend_basic(draw_ctx, dsc);
lv_coord_t src_stride;
src_stride = lv_area_get_width(dsc->blend_area);
src_buf += src_stride * (blend_area.y1 - dsc->blend_area->y1) + (blend_area.x1 - dsc->blend_area->x1);
lv_area_move(&blend_area, -draw_ctx->buf_area->x1, -draw_ctx->buf_area->y1);
lv_draw_stm32_dma2d_blend_map(dest_buf, &blend_area, dest_stride, src_buf, src_stride, dsc->opa);
done = true;
}
else if(dsc->opa >= LV_OPA_MAX) {
lv_area_move(&blend_area, -draw_ctx->buf_area->x1, -draw_ctx->buf_area->y1);
lv_draw_stm32_dma2d_blend_fill(dest_buf, dest_stride, &blend_area, dsc->color);
done = true;
}
}
if(!done) lv_draw_sw_blend_basic(draw_ctx, dsc);
}
void lv_draw_stm32_dma2d_buffer_copy(lv_draw_ctx_t * draw_ctx,
void * dest_buf, lv_coord_t dest_stride, const lv_area_t * dest_area,
void * src_buf, lv_coord_t src_stride, const lv_area_t * src_area)
{
LV_UNUSED(draw_ctx);
lv_draw_stm32_dma2d_blend_map(dest_buf, dest_area, dest_stride, src_buf, src_stride, LV_OPA_MAX);
}
static void lv_draw_stm32_dma2d_img_decoded(lv_draw_ctx_t * draw_ctx, const lv_draw_img_dsc_t * dsc,
const lv_area_t * coords, const uint8_t * map_p, lv_img_cf_t color_format)
{
/*TODO basic ARGB8888 image can be handles here*/
lv_draw_sw_img_decoded(draw_ctx, dsc, coords, map_p, color_format);
}
static void lv_draw_stm32_dma2d_blend_fill(lv_color_t * dest_buf, lv_coord_t dest_stride, const lv_area_t * fill_area,
lv_color_t color)
{
/*Simply fill an area*/
int32_t area_w = lv_area_get_width(fill_area);
int32_t area_h = lv_area_get_height(fill_area);
invalidate_cache();
DMA2D->CR = 0x30000;
DMA2D->OMAR = (uint32_t)dest_buf;
/*as input color mode is same as output we don't need to convert here do we?*/
DMA2D->OCOLR = color.full;
DMA2D->OOR = dest_stride - area_w;
DMA2D->NLR = (area_w << DMA2D_NLR_PL_Pos) | (area_h << DMA2D_NLR_NL_Pos);
/*start transfer*/
DMA2D->CR |= DMA2D_CR_START_Msk;
}
static void lv_draw_stm32_dma2d_blend_map(lv_color_t * dest_buf, const lv_area_t * dest_area, lv_coord_t dest_stride,
const lv_color_t * src_buf, lv_coord_t src_stride, lv_opa_t opa)
{
/*Simple copy*/
int32_t dest_w = lv_area_get_width(dest_area);
int32_t dest_h = lv_area_get_height(dest_area);
invalidate_cache();
if(opa >= LV_OPA_MAX) {
DMA2D->CR = 0;
/*copy output colour mode, this register controls both input and output colour format*/
DMA2D->FGPFCCR = LV_DMA2D_COLOR_FORMAT;
DMA2D->FGMAR = (uint32_t)src_buf;
DMA2D->FGOR = src_stride - dest_w;
DMA2D->OMAR = (uint32_t)dest_buf;
DMA2D->OOR = dest_stride - dest_w;
DMA2D->NLR = (dest_w << DMA2D_NLR_PL_Pos) | (dest_h << DMA2D_NLR_NL_Pos);
/*start transfer*/
DMA2D->CR |= DMA2D_CR_START_Msk;
}
else {
DMA2D->CR = 0x20000;
DMA2D->BGPFCCR = LV_DMA2D_COLOR_FORMAT;
DMA2D->BGMAR = (uint32_t)dest_buf;
DMA2D->BGOR = dest_stride - dest_w;
DMA2D->FGPFCCR = (uint32_t)LV_DMA2D_COLOR_FORMAT
/*alpha mode 2, replace with foreground * alpha value*/
| (2 << DMA2D_FGPFCCR_AM_Pos)
/*alpha value*/
| (opa << DMA2D_FGPFCCR_ALPHA_Pos);
DMA2D->FGMAR = (uint32_t)src_buf;
DMA2D->FGOR = src_stride - dest_w;
DMA2D->OMAR = (uint32_t)src_buf;
DMA2D->OOR = src_stride - dest_w;
DMA2D->NLR = (dest_w << DMA2D_NLR_PL_Pos) | (dest_h << DMA2D_NLR_NL_Pos);
/*start transfer*/
DMA2D->CR |= DMA2D_CR_START_Msk;
}
}
void lv_gpu_stm32_dma2d_wait_cb(lv_draw_ctx_t * draw_ctx)
{
lv_disp_t * disp = _lv_refr_get_disp_refreshing();
if(disp->driver && disp->driver->wait_cb) {
while(DMA2D->CR & DMA2D_CR_START_Msk) {
disp->driver->wait_cb(disp->driver);
}
}
else {
while(DMA2D->CR & DMA2D_CR_START_Msk);
}
lv_draw_sw_wait_for_finish(draw_ctx);
}
/**********************
* STATIC FUNCTIONS
**********************/
static void invalidate_cache(void)
{
lv_disp_t * disp = _lv_refr_get_disp_refreshing();
if(disp->driver->clean_dcache_cb) disp->driver->clean_dcache_cb(disp->driver);
else {
#if __CORTEX_M >= 0x07
if((SCB->CCR) & (uint32_t)SCB_CCR_DC_Msk)
SCB_CleanInvalidateDCache();
#endif
}
}
#endif

View File

@@ -0,0 +1,70 @@
/**
* @file lv_gpu_stm32_dma2d.h
*
*/
#ifndef LV_GPU_STM32_DMA2D_H
#define LV_GPU_STM32_DMA2D_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../../misc/lv_color.h"
#include "../../hal/lv_hal_disp.h"
#include "../sw/lv_draw_sw.h"
#if LV_USE_GPU_STM32_DMA2D
/*********************
* DEFINES
*********************/
#define LV_DMA2D_ARGB8888 0
#define LV_DMA2D_RGB888 1
#define LV_DMA2D_RGB565 2
#define LV_DMA2D_ARGB1555 3
#define LV_DMA2D_ARGB4444 4
/**********************
* TYPEDEFS
**********************/
typedef lv_draw_sw_ctx_t lv_draw_stm32_dma2d_ctx_t;
struct _lv_disp_drv_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Turn on the peripheral and set output color mode, this only needs to be done once
*/
void lv_draw_stm32_dma2d_init(void);
void lv_draw_stm32_dma2d_ctx_init(struct _lv_disp_drv_t * drv, lv_draw_ctx_t * draw_ctx);
void lv_draw_stm32_dma2d_ctx_deinit(struct _lv_disp_drv_t * drv, lv_draw_ctx_t * draw_ctx);
void lv_draw_stm32_dma2d_blend(lv_draw_ctx_t * draw_ctx, const lv_draw_sw_blend_dsc_t * dsc);
void lv_draw_stm32_dma2d_buffer_copy(lv_draw_ctx_t * draw_ctx,
void * dest_buf, lv_coord_t dest_stride, const lv_area_t * dest_area,
void * src_buf, lv_coord_t src_stride, const lv_area_t * src_area);
void lv_gpu_stm32_dma2d_wait_cb(lv_draw_ctx_t * draw_ctx);
/**********************
* MACROS
**********************/
#endif /*LV_USE_GPU_STM32_DMA2D*/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_GPU_STM32_DMA2D_H*/

View File

@@ -0,0 +1,101 @@
/**
* @file lv_draw_sw.c
*
*/
/*********************
* INCLUDES
*********************/
#include "../lv_draw.h"
#include "lv_draw_sw.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
/**********************
* GLOBAL PROTOTYPES
**********************/
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
void lv_draw_sw_init_ctx(lv_disp_drv_t * drv, lv_draw_ctx_t * draw_ctx)
{
LV_UNUSED(drv);
lv_draw_sw_ctx_t * draw_sw_ctx = (lv_draw_sw_ctx_t *) draw_ctx;
lv_memset_00(draw_sw_ctx, sizeof(lv_draw_sw_ctx_t));
draw_sw_ctx->base_draw.draw_arc = lv_draw_sw_arc;
draw_sw_ctx->base_draw.draw_rect = lv_draw_sw_rect;
draw_sw_ctx->base_draw.draw_bg = lv_draw_sw_bg;
draw_sw_ctx->base_draw.draw_letter = lv_draw_sw_letter;
draw_sw_ctx->base_draw.draw_img_decoded = lv_draw_sw_img_decoded;
draw_sw_ctx->base_draw.draw_line = lv_draw_sw_line;
draw_sw_ctx->base_draw.draw_polygon = lv_draw_sw_polygon;
draw_sw_ctx->base_draw.wait_for_finish = lv_draw_sw_wait_for_finish;
draw_sw_ctx->base_draw.buffer_copy = lv_draw_sw_buffer_copy;
draw_sw_ctx->blend = lv_draw_sw_blend_basic;
}
void lv_draw_sw_deinit_ctx(lv_disp_drv_t * drv, lv_draw_ctx_t * draw_ctx)
{
LV_UNUSED(drv);
lv_draw_sw_ctx_t * draw_sw_ctx = (lv_draw_sw_ctx_t *) draw_ctx;
lv_memset_00(draw_sw_ctx, sizeof(lv_draw_sw_ctx_t));
}
void lv_draw_sw_wait_for_finish(lv_draw_ctx_t * draw_ctx)
{
LV_UNUSED(draw_ctx);
/*Nothing to wait for*/
}
void lv_draw_sw_buffer_copy(lv_draw_ctx_t * draw_ctx,
void * dest_buf, lv_coord_t dest_stride, const lv_area_t * dest_area,
void * src_buf, lv_coord_t src_stride, const lv_area_t * src_area)
{
LV_UNUSED(draw_ctx);
lv_color_t * dest_bufc = dest_buf;
lv_color_t * src_bufc = src_buf;
/*Got the first pixel of each buffer*/
dest_bufc += dest_stride * dest_area->y1;
dest_bufc += dest_area->x1;
src_bufc += src_stride * src_area->y1;
src_bufc += src_area->x1;
uint32_t line_length = lv_area_get_width(dest_area) * sizeof(lv_color_t);
lv_coord_t y;
for(y = dest_area->y1; y <= dest_area->y2; y++) {
lv_memcpy(dest_bufc, src_bufc, line_length);
dest_bufc += dest_stride;
src_bufc += src_stride;
}
}
/**********************
* STATIC FUNCTIONS
**********************/

View File

@@ -0,0 +1,81 @@
/**
* @file lv_draw_sw.h
*
*/
#ifndef LV_DRAW_SW_H
#define LV_DRAW_SW_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "lv_draw_sw_blend.h"
#include "../lv_draw.h"
#include "../../misc/lv_area.h"
#include "../../misc/lv_color.h"
#include "../../hal/lv_hal_disp.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
struct _lv_disp_drv_t;
typedef struct {
lv_draw_ctx_t base_draw;
/** Fill an area of the destination buffer with a color*/
void (*blend)(lv_draw_ctx_t * draw_ctx, const lv_draw_sw_blend_dsc_t * dsc);
} lv_draw_sw_ctx_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
void lv_draw_sw_init_ctx(struct _lv_disp_drv_t * drv, lv_draw_ctx_t * draw_ctx);
void lv_draw_sw_deinit_ctx(struct _lv_disp_drv_t * drv, lv_draw_ctx_t * draw_ctx);
void lv_draw_sw_wait_for_finish(lv_draw_ctx_t * draw_ctx);
void lv_draw_sw_arc(lv_draw_ctx_t * draw_ctx, const lv_draw_arc_dsc_t * dsc, const lv_point_t * center, uint16_t radius,
uint16_t start_angle, uint16_t end_angle);
void lv_draw_sw_rect(lv_draw_ctx_t * draw_ctx, const lv_draw_rect_dsc_t * dsc, const lv_area_t * coords);
void lv_draw_sw_bg(lv_draw_ctx_t * draw_ctx, const lv_draw_rect_dsc_t * dsc, const lv_area_t * coords);
void lv_draw_sw_letter(lv_draw_ctx_t * draw_ctx, const lv_draw_label_dsc_t * dsc, const lv_point_t * pos_p,
uint32_t letter);
LV_ATTRIBUTE_FAST_MEM void lv_draw_sw_img_decoded(struct _lv_draw_ctx_t * draw_ctx, const lv_draw_img_dsc_t * draw_dsc,
const lv_area_t * coords, const uint8_t * src_buf, lv_img_cf_t cf);
LV_ATTRIBUTE_FAST_MEM void lv_draw_sw_line(struct _lv_draw_ctx_t * draw_ctx, const lv_draw_line_dsc_t * dsc,
const lv_point_t * point1, const lv_point_t * point2);
void lv_draw_sw_polygon(struct _lv_draw_ctx_t * draw_ctx, const lv_draw_rect_dsc_t * draw_dsc,
const lv_point_t * points, uint16_t point_cnt);
void lv_draw_sw_buffer_copy(lv_draw_ctx_t * draw_ctx,
void * dest_buf, lv_coord_t dest_stride, const lv_area_t * dest_area,
void * src_buf, lv_coord_t src_stride, const lv_area_t * src_area);
/***********************
* GLOBAL VARIABLES
***********************/
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_DRAW_SW_H*/

View File

@@ -0,0 +1,15 @@
CSRCS += lv_draw_sw.c
CSRCS += lv_draw_sw_arc.c
CSRCS += lv_draw_sw_blend.c
CSRCS += lv_draw_sw_img.c
CSRCS += lv_draw_sw_letter.c
CSRCS += lv_draw_sw_line.c
CSRCS += lv_draw_sw_rect.c
CSRCS += lv_draw_sw_polygon.c
CSRCS += lv_draw_sw_gradient.c
CSRCS += lv_draw_sw_dither.c
DEPPATH += --dep-path $(LVGL_DIR)/$(LVGL_DIR_NAME)/src/draw/sw
VPATH += :$(LVGL_DIR)/$(LVGL_DIR_NAME)/src/draw/sw
CFLAGS += "-I$(LVGL_DIR)/$(LVGL_DIR_NAME)/src/draw/sw"

View File

@@ -0,0 +1,531 @@
/**
* @file lv_draw_arc.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_draw_sw.h"
#include "../../misc/lv_math.h"
#include "../../misc/lv_log.h"
#include "../../misc/lv_mem.h"
#include "../lv_draw.h"
/*********************
* DEFINES
*********************/
#define SPLIT_RADIUS_LIMIT 10 /*With radius greater than this the arc will drawn in quarters. A quarter is drawn only if there is arc in it*/
#define SPLIT_ANGLE_GAP_LIMIT 60 /*With small gaps in the arc don't bother with splitting because there is nothing to skip.*/
/**********************
* TYPEDEFS
**********************/
typedef struct {
const lv_point_t * center;
lv_coord_t radius;
uint16_t start_angle;
uint16_t end_angle;
uint16_t start_quarter;
uint16_t end_quarter;
lv_coord_t width;
lv_draw_rect_dsc_t * draw_dsc;
const lv_area_t * draw_area;
lv_draw_ctx_t * draw_ctx;
} quarter_draw_dsc_t;
/**********************
* STATIC PROTOTYPES
**********************/
#if LV_DRAW_COMPLEX
static void draw_quarter_0(quarter_draw_dsc_t * q);
static void draw_quarter_1(quarter_draw_dsc_t * q);
static void draw_quarter_2(quarter_draw_dsc_t * q);
static void draw_quarter_3(quarter_draw_dsc_t * q);
static void get_rounded_area(int16_t angle, lv_coord_t radius, uint8_t thickness, lv_area_t * res_area);
#endif /*LV_DRAW_COMPLEX*/
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
void lv_draw_sw_arc(lv_draw_ctx_t * draw_ctx, const lv_draw_arc_dsc_t * dsc, const lv_point_t * center, uint16_t radius,
uint16_t start_angle, uint16_t end_angle)
{
#if LV_DRAW_COMPLEX
if(dsc->opa <= LV_OPA_MIN) return;
if(dsc->width == 0) return;
if(start_angle == end_angle) return;
lv_coord_t width = dsc->width;
if(width > radius) width = radius;
lv_draw_rect_dsc_t cir_dsc;
lv_draw_rect_dsc_init(&cir_dsc);
cir_dsc.blend_mode = dsc->blend_mode;
if(dsc->img_src) {
cir_dsc.bg_opa = LV_OPA_TRANSP;
cir_dsc.bg_img_src = dsc->img_src;
cir_dsc.bg_img_opa = dsc->opa;
}
else {
cir_dsc.bg_opa = dsc->opa;
cir_dsc.bg_color = dsc->color;
}
lv_area_t area_out;
area_out.x1 = center->x - radius;
area_out.y1 = center->y - radius;
area_out.x2 = center->x + radius - 1; /*-1 because the center already belongs to the left/bottom part*/
area_out.y2 = center->y + radius - 1;
lv_area_t area_in;
lv_area_copy(&area_in, &area_out);
area_in.x1 += dsc->width;
area_in.y1 += dsc->width;
area_in.x2 -= dsc->width;
area_in.y2 -= dsc->width;
/*Create inner the mask*/
int16_t mask_in_id = LV_MASK_ID_INV;
lv_draw_mask_radius_param_t mask_in_param;
if(lv_area_get_width(&area_in) > 0 && lv_area_get_height(&area_in) > 0) {
lv_draw_mask_radius_init(&mask_in_param, &area_in, LV_RADIUS_CIRCLE, true);
mask_in_id = lv_draw_mask_add(&mask_in_param, NULL);
}
lv_draw_mask_radius_param_t mask_out_param;
lv_draw_mask_radius_init(&mask_out_param, &area_out, LV_RADIUS_CIRCLE, false);
int16_t mask_out_id = lv_draw_mask_add(&mask_out_param, NULL);
/*Draw a full ring*/
if(start_angle + 360 == end_angle || start_angle == end_angle + 360) {
cir_dsc.radius = LV_RADIUS_CIRCLE;
lv_draw_rect(draw_ctx, &cir_dsc, &area_out);
lv_draw_mask_remove_id(mask_out_id);
if(mask_in_id != LV_MASK_ID_INV) lv_draw_mask_remove_id(mask_in_id);
lv_draw_mask_free_param(&mask_out_param);
lv_draw_mask_free_param(&mask_in_param);
return;
}
while(start_angle >= 360) start_angle -= 360;
while(end_angle >= 360) end_angle -= 360;
lv_draw_mask_angle_param_t mask_angle_param;
lv_draw_mask_angle_init(&mask_angle_param, center->x, center->y, start_angle, end_angle);
int16_t mask_angle_id = lv_draw_mask_add(&mask_angle_param, NULL);
int32_t angle_gap;
if(end_angle > start_angle) {
angle_gap = 360 - (end_angle - start_angle);
}
else {
angle_gap = start_angle - end_angle;
}
const lv_area_t * clip_area_ori = draw_ctx->clip_area;
if(angle_gap > SPLIT_ANGLE_GAP_LIMIT && radius > SPLIT_RADIUS_LIMIT) {
/*Handle each quarter individually and skip which is empty*/
quarter_draw_dsc_t q_dsc;
q_dsc.center = center;
q_dsc.radius = radius;
q_dsc.start_angle = start_angle;
q_dsc.end_angle = end_angle;
q_dsc.start_quarter = (start_angle / 90) & 0x3;
q_dsc.end_quarter = (end_angle / 90) & 0x3;
q_dsc.width = width;
q_dsc.draw_dsc = &cir_dsc;
q_dsc.draw_area = &area_out;
q_dsc.draw_ctx = draw_ctx;
draw_quarter_0(&q_dsc);
draw_quarter_1(&q_dsc);
draw_quarter_2(&q_dsc);
draw_quarter_3(&q_dsc);
}
else {
lv_draw_rect(draw_ctx, &cir_dsc, &area_out);
}
lv_draw_mask_free_param(&mask_angle_param);
lv_draw_mask_free_param(&mask_out_param);
lv_draw_mask_free_param(&mask_in_param);
lv_draw_mask_remove_id(mask_angle_id);
lv_draw_mask_remove_id(mask_out_id);
if(mask_in_id != LV_MASK_ID_INV) lv_draw_mask_remove_id(mask_in_id);
if(dsc->rounded) {
lv_draw_mask_radius_param_t mask_end_param;
lv_area_t round_area;
get_rounded_area(start_angle, radius, width, &round_area);
round_area.x1 += center->x;
round_area.x2 += center->x;
round_area.y1 += center->y;
round_area.y2 += center->y;
lv_area_t clip_area2;
if(_lv_area_intersect(&clip_area2, clip_area_ori, &round_area)) {
lv_draw_mask_radius_init(&mask_end_param, &round_area, LV_RADIUS_CIRCLE, false);
int16_t mask_end_id = lv_draw_mask_add(&mask_end_param, NULL);
draw_ctx->clip_area = &clip_area2;
lv_draw_rect(draw_ctx, &cir_dsc, &area_out);
lv_draw_mask_remove_id(mask_end_id);
lv_draw_mask_free_param(&mask_end_param);
}
get_rounded_area(end_angle, radius, width, &round_area);
round_area.x1 += center->x;
round_area.x2 += center->x;
round_area.y1 += center->y;
round_area.y2 += center->y;
if(_lv_area_intersect(&clip_area2, clip_area_ori, &round_area)) {
lv_draw_mask_radius_init(&mask_end_param, &round_area, LV_RADIUS_CIRCLE, false);
int16_t mask_end_id = lv_draw_mask_add(&mask_end_param, NULL);
draw_ctx->clip_area = &clip_area2;
lv_draw_rect(draw_ctx, &cir_dsc, &area_out);
lv_draw_mask_remove_id(mask_end_id);
lv_draw_mask_free_param(&mask_end_param);
}
draw_ctx->clip_area = clip_area_ori;
}
#else
LV_LOG_WARN("Can't draw arc with LV_DRAW_COMPLEX == 0");
LV_UNUSED(center);
LV_UNUSED(radius);
LV_UNUSED(start_angle);
LV_UNUSED(end_angle);
LV_UNUSED(draw_ctx);
LV_UNUSED(dsc);
#endif /*LV_DRAW_COMPLEX*/
}
/**********************
* STATIC FUNCTIONS
**********************/
#if LV_DRAW_COMPLEX
static void draw_quarter_0(quarter_draw_dsc_t * q)
{
const lv_area_t * clip_area_ori = q->draw_ctx->clip_area;
lv_area_t quarter_area;
if(q->start_quarter == 0 && q->end_quarter == 0 && q->start_angle < q->end_angle) {
/*Small arc here*/
quarter_area.y1 = q->center->y + ((lv_trigo_sin(q->start_angle) * (q->radius - q->width)) >> LV_TRIGO_SHIFT);
quarter_area.x2 = q->center->x + ((lv_trigo_sin(q->start_angle + 90) * (q->radius)) >> LV_TRIGO_SHIFT);
quarter_area.y2 = q->center->y + ((lv_trigo_sin(q->end_angle) * q->radius) >> LV_TRIGO_SHIFT);
quarter_area.x1 = q->center->x + ((lv_trigo_sin(q->end_angle + 90) * (q->radius - q->width)) >> LV_TRIGO_SHIFT);
bool ok = _lv_area_intersect(&quarter_area, &quarter_area, clip_area_ori);
if(ok) {
q->draw_ctx->clip_area = &quarter_area;
lv_draw_rect(q->draw_ctx, q->draw_dsc, q->draw_area);
}
}
else if(q->start_quarter == 0 || q->end_quarter == 0) {
/*Start and/or end arcs here*/
if(q->start_quarter == 0) {
quarter_area.x1 = q->center->x;
quarter_area.y2 = q->center->y + q->radius;
quarter_area.y1 = q->center->y + ((lv_trigo_sin(q->start_angle) * (q->radius - q->width)) >> LV_TRIGO_SHIFT);
quarter_area.x2 = q->center->x + ((lv_trigo_sin(q->start_angle + 90) * (q->radius)) >> LV_TRIGO_SHIFT);
bool ok = _lv_area_intersect(&quarter_area, &quarter_area, clip_area_ori);
if(ok) {
q->draw_ctx->clip_area = &quarter_area;
lv_draw_rect(q->draw_ctx, q->draw_dsc, q->draw_area);
}
}
if(q->end_quarter == 0) {
quarter_area.x2 = q->center->x + q->radius;
quarter_area.y1 = q->center->y;
quarter_area.y2 = q->center->y + ((lv_trigo_sin(q->end_angle) * q->radius) >> LV_TRIGO_SHIFT);
quarter_area.x1 = q->center->x + ((lv_trigo_sin(q->end_angle + 90) * (q->radius - q->width)) >> LV_TRIGO_SHIFT);
bool ok = _lv_area_intersect(&quarter_area, &quarter_area, clip_area_ori);
if(ok) {
q->draw_ctx->clip_area = &quarter_area;
lv_draw_rect(q->draw_ctx, q->draw_dsc, q->draw_area);
}
}
}
else if((q->start_quarter == q->end_quarter && q->start_quarter != 0 && q->end_angle < q->start_angle) ||
(q->start_quarter == 2 && q->end_quarter == 1) ||
(q->start_quarter == 3 && q->end_quarter == 2) ||
(q->start_quarter == 3 && q->end_quarter == 1)) {
/*Arc crosses here*/
quarter_area.x1 = q->center->x;
quarter_area.y1 = q->center->y;
quarter_area.x2 = q->center->x + q->radius;
quarter_area.y2 = q->center->y + q->radius;
bool ok = _lv_area_intersect(&quarter_area, &quarter_area, clip_area_ori);
if(ok) {
q->draw_ctx->clip_area = &quarter_area;
lv_draw_rect(q->draw_ctx, q->draw_dsc, q->draw_area);
}
}
q->draw_ctx->clip_area = clip_area_ori;
}
static void draw_quarter_1(quarter_draw_dsc_t * q)
{
const lv_area_t * clip_area_ori = q->draw_ctx->clip_area;
lv_area_t quarter_area;
if(q->start_quarter == 1 && q->end_quarter == 1 && q->start_angle < q->end_angle) {
/*Small arc here*/
quarter_area.y2 = q->center->y + ((lv_trigo_sin(q->start_angle) * (q->radius)) >> LV_TRIGO_SHIFT);
quarter_area.x2 = q->center->x + ((lv_trigo_sin(q->start_angle + 90) * (q->radius - q->width)) >> LV_TRIGO_SHIFT);
quarter_area.y1 = q->center->y + ((lv_trigo_sin(q->end_angle) * (q->radius - q->width)) >> LV_TRIGO_SHIFT);
quarter_area.x1 = q->center->x + ((lv_trigo_sin(q->end_angle + 90) * (q->radius)) >> LV_TRIGO_SHIFT);
bool ok = _lv_area_intersect(&quarter_area, &quarter_area, clip_area_ori);
if(ok) {
q->draw_ctx->clip_area = &quarter_area;
lv_draw_rect(q->draw_ctx, q->draw_dsc, q->draw_area);
}
}
else if(q->start_quarter == 1 || q->end_quarter == 1) {
/*Start and/or end arcs here*/
if(q->start_quarter == 1) {
quarter_area.x1 = q->center->x - q->radius;
quarter_area.y1 = q->center->y;
quarter_area.y2 = q->center->y + ((lv_trigo_sin(q->start_angle) * (q->radius)) >> LV_TRIGO_SHIFT);
quarter_area.x2 = q->center->x + ((lv_trigo_sin(q->start_angle + 90) * (q->radius - q->width)) >> LV_TRIGO_SHIFT);
bool ok = _lv_area_intersect(&quarter_area, &quarter_area, clip_area_ori);
if(ok) {
q->draw_ctx->clip_area = &quarter_area;
lv_draw_rect(q->draw_ctx, q->draw_dsc, q->draw_area);
}
}
if(q->end_quarter == 1) {
quarter_area.x2 = q->center->x - 1;
quarter_area.y2 = q->center->y + q->radius;
quarter_area.y1 = q->center->y + ((lv_trigo_sin(q->end_angle) * (q->radius - q->width)) >> LV_TRIGO_SHIFT);
quarter_area.x1 = q->center->x + ((lv_trigo_sin(q->end_angle + 90) * (q->radius)) >> LV_TRIGO_SHIFT);
bool ok = _lv_area_intersect(&quarter_area, &quarter_area, clip_area_ori);
if(ok) {
q->draw_ctx->clip_area = &quarter_area;
lv_draw_rect(q->draw_ctx, q->draw_dsc, q->draw_area);
}
}
}
else if((q->start_quarter == q->end_quarter && q->start_quarter != 1 && q->end_angle < q->start_angle) ||
(q->start_quarter == 0 && q->end_quarter == 2) ||
(q->start_quarter == 0 && q->end_quarter == 3) ||
(q->start_quarter == 3 && q->end_quarter == 2)) {
/*Arc crosses here*/
quarter_area.x1 = q->center->x - q->radius;
quarter_area.y1 = q->center->y;
quarter_area.x2 = q->center->x - 1;
quarter_area.y2 = q->center->y + q->radius;
bool ok = _lv_area_intersect(&quarter_area, &quarter_area, clip_area_ori);
if(ok) {
q->draw_ctx->clip_area = &quarter_area;
lv_draw_rect(q->draw_ctx, q->draw_dsc, q->draw_area);
}
}
q->draw_ctx->clip_area = clip_area_ori;
}
static void draw_quarter_2(quarter_draw_dsc_t * q)
{
const lv_area_t * clip_area_ori = q->draw_ctx->clip_area;
lv_area_t quarter_area;
if(q->start_quarter == 2 && q->end_quarter == 2 && q->start_angle < q->end_angle) {
/*Small arc here*/
quarter_area.x1 = q->center->x + ((lv_trigo_sin(q->start_angle + 90) * (q->radius)) >> LV_TRIGO_SHIFT);
quarter_area.y2 = q->center->y + ((lv_trigo_sin(q->start_angle) * (q->radius - q->width)) >> LV_TRIGO_SHIFT);
quarter_area.y1 = q->center->y + ((lv_trigo_sin(q->end_angle) * q->radius) >> LV_TRIGO_SHIFT);
quarter_area.x2 = q->center->x + ((lv_trigo_sin(q->end_angle + 90) * (q->radius - q->width)) >> LV_TRIGO_SHIFT);
bool ok = _lv_area_intersect(&quarter_area, &quarter_area, clip_area_ori);
if(ok) {
q->draw_ctx->clip_area = &quarter_area;
lv_draw_rect(q->draw_ctx, q->draw_dsc, q->draw_area);
}
}
else if(q->start_quarter == 2 || q->end_quarter == 2) {
/*Start and/or end arcs here*/
if(q->start_quarter == 2) {
quarter_area.x2 = q->center->x - 1;
quarter_area.y1 = q->center->y - q->radius;
quarter_area.x1 = q->center->x + ((lv_trigo_sin(q->start_angle + 90) * (q->radius)) >> LV_TRIGO_SHIFT);
quarter_area.y2 = q->center->y + ((lv_trigo_sin(q->start_angle) * (q->radius - q->width)) >> LV_TRIGO_SHIFT);
bool ok = _lv_area_intersect(&quarter_area, &quarter_area, clip_area_ori);
if(ok) {
q->draw_ctx->clip_area = &quarter_area;
lv_draw_rect(q->draw_ctx, q->draw_dsc, q->draw_area);
}
}
if(q->end_quarter == 2) {
quarter_area.x1 = q->center->x - q->radius;
quarter_area.y2 = q->center->y - 1;
quarter_area.x2 = q->center->x + ((lv_trigo_sin(q->end_angle + 90) * (q->radius - q->width)) >> LV_TRIGO_SHIFT);
quarter_area.y1 = q->center->y + ((lv_trigo_sin(q->end_angle) * (q->radius)) >> LV_TRIGO_SHIFT);
bool ok = _lv_area_intersect(&quarter_area, &quarter_area, clip_area_ori);
if(ok) {
q->draw_ctx->clip_area = &quarter_area;
lv_draw_rect(q->draw_ctx, q->draw_dsc, q->draw_area);
}
}
}
else if((q->start_quarter == q->end_quarter && q->start_quarter != 2 && q->end_angle < q->start_angle) ||
(q->start_quarter == 0 && q->end_quarter == 3) ||
(q->start_quarter == 1 && q->end_quarter == 3) ||
(q->start_quarter == 1 && q->end_quarter == 0)) {
/*Arc crosses here*/
quarter_area.x1 = q->center->x - q->radius;
quarter_area.y1 = q->center->y - q->radius;
quarter_area.x2 = q->center->x - 1;
quarter_area.y2 = q->center->y - 1;
bool ok = _lv_area_intersect(&quarter_area, &quarter_area, clip_area_ori);
if(ok) {
q->draw_ctx->clip_area = &quarter_area;
lv_draw_rect(q->draw_ctx, q->draw_dsc, q->draw_area);
}
}
q->draw_ctx->clip_area = clip_area_ori;
}
static void draw_quarter_3(quarter_draw_dsc_t * q)
{
const lv_area_t * clip_area_ori = q->draw_ctx->clip_area;
lv_area_t quarter_area;
if(q->start_quarter == 3 && q->end_quarter == 3 && q->start_angle < q->end_angle) {
/*Small arc here*/
quarter_area.x1 = q->center->x + ((lv_trigo_sin(q->start_angle + 90) * (q->radius - q->width)) >> LV_TRIGO_SHIFT);
quarter_area.y1 = q->center->y + ((lv_trigo_sin(q->start_angle) * (q->radius)) >> LV_TRIGO_SHIFT);
quarter_area.x2 = q->center->x + ((lv_trigo_sin(q->end_angle + 90) * (q->radius)) >> LV_TRIGO_SHIFT);
quarter_area.y2 = q->center->y + ((lv_trigo_sin(q->end_angle) * (q->radius - q->width)) >> LV_TRIGO_SHIFT);
bool ok = _lv_area_intersect(&quarter_area, &quarter_area, clip_area_ori);
if(ok) {
q->draw_ctx->clip_area = &quarter_area;
lv_draw_rect(q->draw_ctx, q->draw_dsc, q->draw_area);
}
}
else if(q->start_quarter == 3 || q->end_quarter == 3) {
/*Start and/or end arcs here*/
if(q->start_quarter == 3) {
quarter_area.x2 = q->center->x + q->radius;
quarter_area.y2 = q->center->y - 1;
quarter_area.x1 = q->center->x + ((lv_trigo_sin(q->start_angle + 90) * (q->radius - q->width)) >> LV_TRIGO_SHIFT);
quarter_area.y1 = q->center->y + ((lv_trigo_sin(q->start_angle) * (q->radius)) >> LV_TRIGO_SHIFT);
bool ok = _lv_area_intersect(&quarter_area, &quarter_area, clip_area_ori);
if(ok) {
q->draw_ctx->clip_area = &quarter_area;
lv_draw_rect(q->draw_ctx, q->draw_dsc, q->draw_area);
}
}
if(q->end_quarter == 3) {
quarter_area.x1 = q->center->x;
quarter_area.y1 = q->center->y - q->radius;
quarter_area.x2 = q->center->x + ((lv_trigo_sin(q->end_angle + 90) * (q->radius)) >> LV_TRIGO_SHIFT);
quarter_area.y2 = q->center->y + ((lv_trigo_sin(q->end_angle) * (q->radius - q->width)) >> LV_TRIGO_SHIFT);
bool ok = _lv_area_intersect(&quarter_area, &quarter_area, clip_area_ori);
if(ok) {
q->draw_ctx->clip_area = &quarter_area;
lv_draw_rect(q->draw_ctx, q->draw_dsc, q->draw_area);
}
}
}
else if((q->start_quarter == q->end_quarter && q->start_quarter != 3 && q->end_angle < q->start_angle) ||
(q->start_quarter == 2 && q->end_quarter == 0) ||
(q->start_quarter == 1 && q->end_quarter == 0) ||
(q->start_quarter == 2 && q->end_quarter == 1)) {
/*Arc crosses here*/
quarter_area.x1 = q->center->x;
quarter_area.y1 = q->center->y - q->radius;
quarter_area.x2 = q->center->x + q->radius;
quarter_area.y2 = q->center->y - 1;
bool ok = _lv_area_intersect(&quarter_area, &quarter_area, clip_area_ori);
if(ok) {
q->draw_ctx->clip_area = &quarter_area;
lv_draw_rect(q->draw_ctx, q->draw_dsc, q->draw_area);
}
}
q->draw_ctx->clip_area = clip_area_ori;
}
static void get_rounded_area(int16_t angle, lv_coord_t radius, uint8_t thickness, lv_area_t * res_area)
{
const uint8_t ps = 8;
const uint8_t pa = 127;
int32_t thick_half = thickness / 2;
uint8_t thick_corr = (thickness & 0x01) ? 0 : 1;
int32_t cir_x;
int32_t cir_y;
cir_x = ((radius - thick_half) * lv_trigo_sin(90 - angle)) >> (LV_TRIGO_SHIFT - ps);
cir_y = ((radius - thick_half) * lv_trigo_sin(angle)) >> (LV_TRIGO_SHIFT - ps);
/*Actually the center of the pixel need to be calculated so apply 1/2 px offset*/
if(cir_x > 0) {
cir_x = (cir_x - pa) >> ps;
res_area->x1 = cir_x - thick_half + thick_corr;
res_area->x2 = cir_x + thick_half;
}
else {
cir_x = (cir_x + pa) >> ps;
res_area->x1 = cir_x - thick_half;
res_area->x2 = cir_x + thick_half - thick_corr;
}
if(cir_y > 0) {
cir_y = (cir_y - pa) >> ps;
res_area->y1 = cir_y - thick_half + thick_corr;
res_area->y2 = cir_y + thick_half;
}
else {
cir_y = (cir_y + pa) >> ps;
res_area->y1 = cir_y - thick_half;
res_area->y2 = cir_y + thick_half - thick_corr;
}
}
#endif /*LV_DRAW_COMPLEX*/

View File

@@ -0,0 +1,757 @@
/**
* @file lv_draw_sw_blend.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_draw_sw.h"
#include "../../misc/lv_math.h"
#include "../../hal/lv_hal_disp.h"
#include "../../core/lv_refr.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static void fill_set_px(lv_color_t * dest_buf, const lv_area_t * blend_area, lv_coord_t dest_stride,
lv_color_t color, lv_opa_t opa, const lv_opa_t * mask, lv_coord_t mask_stide);
LV_ATTRIBUTE_FAST_MEM static void fill_normal(lv_color_t * dest_buf, const lv_area_t * dest_area,
lv_coord_t dest_stride, lv_color_t color, lv_opa_t opa, const lv_opa_t * mask, lv_coord_t mask_stride);
#if LV_DRAW_COMPLEX
static void fill_blended(lv_color_t * dest_buf, const lv_area_t * dest_area, lv_coord_t dest_stride, lv_color_t color,
lv_opa_t opa, const lv_opa_t * mask, lv_coord_t mask_stride, lv_blend_mode_t blend_mode);
#endif /*LV_DRAW_COMPLEX*/
static void map_set_px(lv_color_t * dest_buf, const lv_area_t * dest_area, lv_coord_t dest_stride,
const lv_color_t * src_buf, lv_coord_t src_stride, lv_opa_t opa, const lv_opa_t * mask, lv_coord_t mask_stride);
LV_ATTRIBUTE_FAST_MEM static void map_normal(lv_color_t * dest_buf, const lv_area_t * dest_area, lv_coord_t dest_stride,
const lv_color_t * src_buf, lv_coord_t src_stride, lv_opa_t opa, const lv_opa_t * mask, lv_coord_t mask_stride);
#if LV_DRAW_COMPLEX
static void map_blended(lv_color_t * dest_buf, const lv_area_t * dest_area, lv_coord_t dest_stride,
const lv_color_t * src_buf, lv_coord_t src_stride, lv_opa_t opa,
const lv_opa_t * mask, lv_coord_t mask_stride, lv_blend_mode_t blend_mode);
static inline lv_color_t color_blend_true_color_additive(lv_color_t fg, lv_color_t bg, lv_opa_t opa);
static inline lv_color_t color_blend_true_color_subtractive(lv_color_t fg, lv_color_t bg, lv_opa_t opa);
static inline lv_color_t color_blend_true_color_multiply(lv_color_t fg, lv_color_t bg, lv_opa_t opa);
#endif /*LV_DRAW_COMPLEX*/
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
#if LV_COLOR_SCREEN_TRANSP == 0
#define FILL_NORMAL_MASK_PX(color) \
if(*mask == LV_OPA_COVER) *dest_buf = color; \
else *dest_buf = lv_color_mix(color, *dest_buf, *mask); \
mask++; \
dest_buf++;
#else
#define FILL_NORMAL_MASK_PX(color) \
if(*mask == LV_OPA_COVER) *dest_buf = color; \
else if(disp->driver->screen_transp) lv_color_mix_with_alpha(*dest_buf, dest_buf->ch.alpha, color, *mask, dest_buf, &dest_buf->ch.alpha); \
else *dest_buf = lv_color_mix(color, *dest_buf, *mask); \
mask++; \
dest_buf++;
#endif
#define MAP_NORMAL_MASK_PX(x) \
if(*mask_tmp_x) { \
if(*mask_tmp_x == LV_OPA_COVER) dest_buf[x] = src_buf[x]; \
else dest_buf[x] = lv_color_mix(src_buf[x], dest_buf[x], *mask_tmp_x); \
} \
mask_tmp_x++;
#define MAP_NORMAL_MASK_PX_SCR_TRANSP(x) \
if(*mask_tmp_x) { \
if(*mask_tmp_x == LV_OPA_COVER) dest_buf[x] = src_buf[x]; \
else if(disp->driver->screen_transp) lv_color_mix_with_alpha(dest_buf[x], dest_buf[x].ch.alpha, \
src_buf[x], *mask_tmp_x, &dest_buf[x], &dest_buf[x].ch.alpha); \
else dest_buf[x] = lv_color_mix(src_buf[x], dest_buf[x], *mask_tmp_x); \
} \
mask_tmp_x++;
/**********************
* GLOBAL FUNCTIONS
**********************/
void lv_draw_sw_blend(lv_draw_ctx_t * draw_ctx, const lv_draw_sw_blend_dsc_t * dsc)
{
/*Do not draw transparent things*/
if(dsc->opa <= LV_OPA_MIN) return;
lv_area_t blend_area;
if(!_lv_area_intersect(&blend_area, dsc->blend_area, draw_ctx->clip_area)) return;
if(draw_ctx->wait_for_finish) draw_ctx->wait_for_finish(draw_ctx);
((lv_draw_sw_ctx_t *)draw_ctx)->blend(draw_ctx, dsc);
}
LV_ATTRIBUTE_FAST_MEM void lv_draw_sw_blend_basic(lv_draw_ctx_t * draw_ctx, const lv_draw_sw_blend_dsc_t * dsc)
{
const lv_opa_t * mask;
if(dsc->mask_buf == NULL) mask = NULL;
if(dsc->mask_buf && dsc->mask_res == LV_DRAW_MASK_RES_TRANSP) return;
else if(dsc->mask_res == LV_DRAW_MASK_RES_FULL_COVER) mask = NULL;
else mask = dsc->mask_buf;
lv_coord_t dest_stride = lv_area_get_width(draw_ctx->buf_area);
lv_area_t blend_area;
if(!_lv_area_intersect(&blend_area, dsc->blend_area, draw_ctx->clip_area)) return;
lv_disp_t * disp = _lv_refr_get_disp_refreshing();
lv_color_t * dest_buf = draw_ctx->buf;
if(disp->driver->set_px_cb == NULL) {
dest_buf += dest_stride * (blend_area.y1 - draw_ctx->buf_area->y1) + (blend_area.x1 - draw_ctx->buf_area->x1);
}
const lv_color_t * src_buf = dsc->src_buf;
lv_coord_t src_stride;
if(src_buf) {
src_stride = lv_area_get_width(dsc->blend_area);
src_buf += src_stride * (blend_area.y1 - dsc->blend_area->y1) + (blend_area.x1 - dsc->blend_area->x1);
}
else {
src_stride = 0;
}
lv_coord_t mask_stride;
if(mask) {
mask_stride = lv_area_get_width(dsc->mask_area);
mask += mask_stride * (dsc->mask_area->y1 - blend_area.y1) + (dsc->mask_area->x1 - blend_area.x1);
}
else {
mask_stride = 0;
}
lv_area_move(&blend_area, -draw_ctx->buf_area->x1, -draw_ctx->buf_area->y1);
if(disp->driver->set_px_cb) {
if(dsc->src_buf == NULL) {
fill_set_px(dest_buf, &blend_area, dest_stride, dsc->color, dsc->opa, mask, mask_stride);
}
else {
map_set_px(dest_buf, &blend_area, dest_stride, src_buf, src_stride, dsc->opa, mask, mask_stride);
}
}
else if(dsc->src_buf == NULL) {
if(dsc->blend_mode == LV_BLEND_MODE_NORMAL) {
fill_normal(dest_buf, &blend_area, dest_stride, dsc->color, dsc->opa, mask, mask_stride);
}
#if LV_DRAW_COMPLEX
else {
fill_blended(dest_buf, &blend_area, dest_stride, dsc->color, dsc->opa, mask, mask_stride, dsc->blend_mode);
}
#endif
}
else {
if(dsc->blend_mode == LV_BLEND_MODE_NORMAL) {
map_normal(dest_buf, &blend_area, dest_stride, src_buf, src_stride, dsc->opa, mask, mask_stride);
}
#if LV_DRAW_COMPLEX
else {
map_blended(dest_buf, &blend_area, dest_stride, src_buf, src_stride, dsc->opa, mask, mask_stride, dsc->blend_mode);
}
#endif
}
}
/**********************
* STATIC FUNCTIONS
**********************/
static void fill_set_px(lv_color_t * dest_buf, const lv_area_t * blend_area, lv_coord_t dest_stride,
lv_color_t color, lv_opa_t opa, const lv_opa_t * mask, lv_coord_t mask_stide)
{
lv_disp_t * disp = _lv_refr_get_disp_refreshing();
int32_t x;
int32_t y;
if(mask == NULL) {
for(y = blend_area->y1; y <= blend_area->y2; y++) {
for(x = blend_area->x1; x <= blend_area->x2; x++) {
disp->driver->set_px_cb(disp->driver, (void *)dest_buf, dest_stride, x, y, color, opa);
}
}
}
else {
int32_t w = lv_area_get_width(blend_area);
int32_t h = lv_area_get_height(blend_area);
for(y = 0; y < h; y++) {
for(x = 0; x < w; x++) {
if(mask[x]) {
disp->driver->set_px_cb(disp->driver, (void *)dest_buf, dest_stride, blend_area->x1 + x, blend_area->y1 + y, color,
(uint32_t)((uint32_t)opa * mask[x]) >> 8);
}
}
mask += mask_stide;
}
}
}
LV_ATTRIBUTE_FAST_MEM static void fill_normal(lv_color_t * dest_buf, const lv_area_t * dest_area,
lv_coord_t dest_stride, lv_color_t color, lv_opa_t opa, const lv_opa_t * mask, lv_coord_t mask_stride)
{
lv_disp_t * disp = _lv_refr_get_disp_refreshing();
int32_t w = lv_area_get_width(dest_area);
int32_t h = lv_area_get_height(dest_area);
int32_t x;
int32_t y;
/*No mask*/
if(mask == NULL) {
if(opa >= LV_OPA_MAX) {
for(y = 0; y < h; y++) {
lv_color_fill(dest_buf, color, w);
dest_buf += dest_stride;
}
}
/*Has opacity*/
else {
lv_color_t last_dest_color = lv_color_black();
lv_color_t last_res_color = lv_color_mix(color, last_dest_color, opa);
uint16_t color_premult[3];
lv_color_premult(color, opa, color_premult);
lv_opa_t opa_inv = 255 - opa;
for(y = 0; y < h; y++) {
for(x = 0; x < w; x++) {
if(last_dest_color.full != dest_buf[x].full) {
last_dest_color = dest_buf[x];
#if LV_COLOR_SCREEN_TRANSP
if(disp->driver->screen_transp) {
lv_color_mix_with_alpha(dest_buf[x], dest_buf[x].ch.alpha, color, opa, &last_res_color,
&last_res_color.ch.alpha);
}
else
#else
LV_UNUSED(disp);
#endif
{
last_res_color = lv_color_mix_premult(color_premult, dest_buf[x], opa_inv);
}
}
dest_buf[x] = last_res_color;
}
dest_buf += dest_stride;
}
}
}
/*Masked*/
else {
#if LV_COLOR_DEPTH == 16
uint32_t c32 = color.full + ((uint32_t)color.full << 16);
#endif
/*Only the mask matters*/
if(opa >= LV_OPA_MAX) {
int32_t x_end4 = w - 4;
for(y = 0; y < h; y++) {
for(x = 0; x < w && ((lv_uintptr_t)(mask) & 0x3); x++) {
FILL_NORMAL_MASK_PX(color)
}
for(; x <= x_end4; x += 4) {
uint32_t mask32 = *((uint32_t *)mask);
if(mask32 == 0xFFFFFFFF) {
#if LV_COLOR_DEPTH == 16
if((lv_uintptr_t)dest_buf & 0x3) {
*(dest_buf + 0) = color;
uint32_t * d = (uint32_t *)(dest_buf + 1);
*d = c32;
*(dest_buf + 3) = color;
}
else {
uint32_t * d = (uint32_t *)dest_buf;
*d = c32;
*(d + 1) = c32;
}
#else
dest_buf[0] = color;
dest_buf[1] = color;
dest_buf[2] = color;
dest_buf[3] = color;
#endif
dest_buf += 4;
mask += 4;
}
else if(mask32) {
FILL_NORMAL_MASK_PX(color)
FILL_NORMAL_MASK_PX(color)
FILL_NORMAL_MASK_PX(color)
FILL_NORMAL_MASK_PX(color)
}
else {
mask += 4;
dest_buf += 4;
}
}
for(; x < w ; x++) {
FILL_NORMAL_MASK_PX(color)
}
dest_buf += (dest_stride - w);
mask += (mask_stride - w);
}
}
/*With opacity*/
else {
/*Buffer the result color to avoid recalculating the same color*/
lv_color_t last_dest_color;
lv_color_t last_res_color;
lv_opa_t last_mask = LV_OPA_TRANSP;
last_dest_color.full = dest_buf[0].full;
last_res_color.full = dest_buf[0].full;
lv_opa_t opa_tmp = LV_OPA_TRANSP;
for(y = 0; y < h; y++) {
for(x = 0; x < w; x++) {
if(*mask) {
if(*mask != last_mask) opa_tmp = *mask == LV_OPA_COVER ? opa :
(uint32_t)((uint32_t)(*mask) * opa) >> 8;
if(*mask != last_mask || last_dest_color.full != dest_buf[x].full) {
#if LV_COLOR_SCREEN_TRANSP
if(disp->driver->screen_transp) {
lv_color_mix_with_alpha(dest_buf[x], dest_buf[x].ch.alpha, color, opa_tmp, &last_res_color,
&last_res_color.ch.alpha);
}
else
#endif
{
if(opa_tmp == LV_OPA_COVER) last_res_color = color;
else last_res_color = lv_color_mix(color, dest_buf[x], opa_tmp);
}
last_mask = *mask;
last_dest_color.full = dest_buf[x].full;
}
dest_buf[x] = last_res_color;
}
mask++;
}
dest_buf += dest_stride;
mask += (mask_stride - w);
}
}
}
}
#if LV_DRAW_COMPLEX
static void fill_blended(lv_color_t * dest_buf, const lv_area_t * dest_area,
lv_coord_t dest_stride, lv_color_t color, lv_opa_t opa, const lv_opa_t * mask, lv_coord_t mask_stride,
lv_blend_mode_t blend_mode)
{
int32_t w = lv_area_get_width(dest_area);
int32_t h = lv_area_get_height(dest_area);
int32_t x;
int32_t y;
lv_color_t (*blend_fp)(lv_color_t, lv_color_t, lv_opa_t);
switch(blend_mode) {
case LV_BLEND_MODE_ADDITIVE:
blend_fp = color_blend_true_color_additive;
break;
case LV_BLEND_MODE_SUBTRACTIVE:
blend_fp = color_blend_true_color_subtractive;
break;
case LV_BLEND_MODE_MULTIPLY:
blend_fp = color_blend_true_color_multiply;
break;
default:
LV_LOG_WARN("fill_blended: unsupported blend mode");
return;
}
/*Simple fill (maybe with opacity), no masking*/
if(mask == NULL) {
lv_color_t last_dest_color = dest_buf[0];
lv_color_t last_res_color = blend_fp(color, dest_buf[0], opa);
for(y = 0; y < h; y++) {
for(x = 0; x < w; x++) {
if(last_dest_color.full != dest_buf[x].full) {
last_dest_color = dest_buf[x];
last_res_color = blend_fp(color, dest_buf[x], opa);
}
dest_buf[x] = last_res_color;
}
dest_buf += dest_stride;
}
}
/*Masked*/
else {
/*Buffer the result color to avoid recalculating the same color*/
lv_color_t last_dest_color;
lv_color_t last_res_color;
lv_opa_t last_mask = LV_OPA_TRANSP;
last_dest_color = dest_buf[0];
lv_opa_t opa_tmp = mask[0] >= LV_OPA_MAX ? opa : (uint32_t)((uint32_t)mask[0] * opa) >> 8;
last_res_color = blend_fp(color, last_dest_color, opa_tmp);
for(y = 0; y < h; y++) {
for(x = 0; x < w; x++) {
if(mask[x] == 0) continue;
if(mask[x] != last_mask || last_dest_color.full != dest_buf[x].full) {
opa_tmp = mask[x] >= LV_OPA_MAX ? opa : (uint32_t)((uint32_t)mask[x] * opa) >> 8;
last_res_color = blend_fp(color, dest_buf[x], opa_tmp);
last_mask = mask[x];
last_dest_color.full = dest_buf[x].full;
}
dest_buf[x] = last_res_color;
}
dest_buf += dest_stride;
mask += mask_stride;
}
}
}
#endif
static void map_set_px(lv_color_t * dest_buf, const lv_area_t * dest_area, lv_coord_t dest_stride,
const lv_color_t * src_buf, lv_coord_t src_stride, lv_opa_t opa, const lv_opa_t * mask, lv_coord_t mask_stride)
{
lv_disp_t * disp = _lv_refr_get_disp_refreshing();
int32_t w = lv_area_get_width(dest_area);
int32_t h = lv_area_get_height(dest_area);
int32_t x;
int32_t y;
if(mask == NULL) {
for(y = 0; y < h; y++) {
for(x = 0; x < w; x++) {
disp->driver->set_px_cb(disp->driver, (void *)dest_buf, dest_stride, dest_area->x1 + x, dest_area->y1 + y, src_buf[x],
opa);
}
src_buf += src_stride;
}
}
else {
for(y = 0; y < h; y++) {
for(x = 0; x < w; x++) {
if(mask[x]) {
disp->driver->set_px_cb(disp->driver, (void *)dest_buf, dest_stride, dest_area->x1 + x, dest_area->y1 + y, src_buf[x],
(uint32_t)((uint32_t)opa * mask[x]) >> 8);
}
}
mask += mask_stride;
src_buf += src_stride;
}
}
}
LV_ATTRIBUTE_FAST_MEM static void map_normal(lv_color_t * dest_buf, const lv_area_t * dest_area, lv_coord_t dest_stride,
const lv_color_t * src_buf, lv_coord_t src_stride, lv_opa_t opa, const lv_opa_t * mask, lv_coord_t mask_stride)
{
int32_t w = lv_area_get_width(dest_area);
int32_t h = lv_area_get_height(dest_area);
int32_t x;
int32_t y;
#if LV_COLOR_SCREEN_TRANSP
lv_disp_t * disp = _lv_refr_get_disp_refreshing();
#endif
/*Simple fill (maybe with opacity), no masking*/
if(mask == NULL) {
if(opa >= LV_OPA_MAX) {
for(y = 0; y < h; y++) {
lv_memcpy(dest_buf, src_buf, w * sizeof(lv_color_t));
dest_buf += dest_stride;
src_buf += src_stride;
}
}
else {
for(y = 0; y < h; y++) {
for(x = 0; x < w; x++) {
#if LV_COLOR_SCREEN_TRANSP
if(disp->driver->screen_transp) {
lv_color_mix_with_alpha(dest_buf[x], dest_buf[x].ch.alpha, src_buf[x], opa, &dest_buf[x],
&dest_buf[x].ch.alpha);
}
else
#endif
{
dest_buf[x] = lv_color_mix(src_buf[x], dest_buf[x], opa);
}
}
dest_buf += dest_stride;
src_buf += src_stride;
}
}
}
/*Masked*/
else {
/*Only the mask matters*/
if(opa > LV_OPA_MAX) {
int32_t x_end4 = w - 4;
for(y = 0; y < h; y++) {
const lv_opa_t * mask_tmp_x = mask;
#if 0
for(x = 0; x < w; x++) {
MAP_NORMAL_MASK_PX(x);
}
#else
for(x = 0; x < w && ((lv_uintptr_t)mask_tmp_x & 0x3); x++) {
#if LV_COLOR_SCREEN_TRANSP
MAP_NORMAL_MASK_PX_SCR_TRANSP(x)
#else
MAP_NORMAL_MASK_PX(x)
#endif
}
uint32_t * mask32 = (uint32_t *)mask_tmp_x;
for(; x < x_end4; x += 4) {
if(*mask32) {
if((*mask32) == 0xFFFFFFFF) {
dest_buf[x] = src_buf[x];
dest_buf[x + 1] = src_buf[x + 1];
dest_buf[x + 2] = src_buf[x + 2];
dest_buf[x + 3] = src_buf[x + 3];
}
else {
mask_tmp_x = (const lv_opa_t *)mask32;
#if LV_COLOR_SCREEN_TRANSP
MAP_NORMAL_MASK_PX_SCR_TRANSP(x)
MAP_NORMAL_MASK_PX_SCR_TRANSP(x + 1)
MAP_NORMAL_MASK_PX_SCR_TRANSP(x + 2)
MAP_NORMAL_MASK_PX_SCR_TRANSP(x + 3)
#else
MAP_NORMAL_MASK_PX(x)
MAP_NORMAL_MASK_PX(x + 1)
MAP_NORMAL_MASK_PX(x + 2)
MAP_NORMAL_MASK_PX(x + 3)
#endif
}
}
mask32++;
}
mask_tmp_x = (const lv_opa_t *)mask32;
for(; x < w ; x++) {
#if LV_COLOR_SCREEN_TRANSP
MAP_NORMAL_MASK_PX_SCR_TRANSP(x)
#else
MAP_NORMAL_MASK_PX(x)
#endif
}
#endif
dest_buf += dest_stride;
src_buf += src_stride;
mask += mask_stride;
}
}
/*Handle opa and mask values too*/
else {
for(y = 0; y < h; y++) {
for(x = 0; x < w; x++) {
if(mask[x]) {
lv_opa_t opa_tmp = mask[x] >= LV_OPA_MAX ? opa : ((opa * mask[x]) >> 8);
#if LV_COLOR_SCREEN_TRANSP
if(disp->driver->screen_transp) {
lv_color_mix_with_alpha(dest_buf[x], dest_buf[x].ch.alpha, src_buf[x], opa_tmp,
&dest_buf[x], &dest_buf[x].ch.alpha);
}
else
#endif
{
dest_buf[x] = lv_color_mix(src_buf[x], dest_buf[x], opa_tmp);
}
}
}
dest_buf += dest_stride;
src_buf += src_stride;
mask += mask_stride;
}
}
}
}
#if LV_DRAW_COMPLEX
static void map_blended(lv_color_t * dest_buf, const lv_area_t * dest_area, lv_coord_t dest_stride,
const lv_color_t * src_buf, lv_coord_t src_stride, lv_opa_t opa,
const lv_opa_t * mask, lv_coord_t mask_stride, lv_blend_mode_t blend_mode)
{
int32_t w = lv_area_get_width(dest_area);
int32_t h = lv_area_get_height(dest_area);
int32_t x;
int32_t y;
lv_color_t (*blend_fp)(lv_color_t, lv_color_t, lv_opa_t);
switch(blend_mode) {
case LV_BLEND_MODE_ADDITIVE:
blend_fp = color_blend_true_color_additive;
break;
case LV_BLEND_MODE_SUBTRACTIVE:
blend_fp = color_blend_true_color_subtractive;
break;
case LV_BLEND_MODE_MULTIPLY:
blend_fp = color_blend_true_color_multiply;
break;
default:
LV_LOG_WARN("fill_blended: unsupported blend mode");
return;
}
/*Simple fill (maybe with opacity), no masking*/
if(mask == NULL) {
/*The map will be indexed from `draw_area->x1` so compensate it.*/
for(y = 0; y < h; y++) {
for(x = 0; x < w; x++) {
dest_buf[x] = blend_fp(src_buf[x], dest_buf[x], opa);
}
dest_buf += dest_stride;
src_buf += src_stride;
}
}
/*Masked*/
else {
for(y = 0; y < h; y++) {
for(x = 0; x < w; x++) {
if(mask[x] == 0) continue;
lv_opa_t opa_tmp = mask[x] >= LV_OPA_MAX ? opa : ((opa * mask[x]) >> 8);
dest_buf[x] = blend_fp(src_buf[x], dest_buf[x], opa_tmp);
}
dest_buf += dest_stride;
src_buf += src_stride;
mask += mask_stride;
}
}
}
static inline lv_color_t color_blend_true_color_additive(lv_color_t fg, lv_color_t bg, lv_opa_t opa)
{
if(opa <= LV_OPA_MIN) return bg;
uint32_t tmp;
#if LV_COLOR_DEPTH == 1
tmp = bg.full + fg.full;
fg.full = LV_MIN(tmp, 1);
#else
tmp = bg.ch.red + fg.ch.red;
#if LV_COLOR_DEPTH == 8
fg.ch.red = LV_MIN(tmp, 7);
#elif LV_COLOR_DEPTH == 16
fg.ch.red = LV_MIN(tmp, 31);
#elif LV_COLOR_DEPTH == 32
fg.ch.red = LV_MIN(tmp, 255);
#endif
#if LV_COLOR_DEPTH == 8
tmp = bg.ch.green + fg.ch.green;
fg.ch.green = LV_MIN(tmp, 7);
#elif LV_COLOR_DEPTH == 16
#if LV_COLOR_16_SWAP == 0
tmp = bg.ch.green + fg.ch.green;
fg.ch.green = LV_MIN(tmp, 63);
#else
tmp = (bg.ch.green_h << 3) + bg.ch.green_l + (fg.ch.green_h << 3) + fg.ch.green_l;
tmp = LV_MIN(tmp, 63);
fg.ch.green_h = tmp >> 3;
fg.ch.green_l = tmp & 0x7;
#endif
#elif LV_COLOR_DEPTH == 32
tmp = bg.ch.green + fg.ch.green;
fg.ch.green = LV_MIN(tmp, 255);
#endif
tmp = bg.ch.blue + fg.ch.blue;
#if LV_COLOR_DEPTH == 8
fg.ch.blue = LV_MIN(tmp, 4);
#elif LV_COLOR_DEPTH == 16
fg.ch.blue = LV_MIN(tmp, 31);
#elif LV_COLOR_DEPTH == 32
fg.ch.blue = LV_MIN(tmp, 255);
#endif
#endif
if(opa == LV_OPA_COVER) return fg;
return lv_color_mix(fg, bg, opa);
}
static inline lv_color_t color_blend_true_color_subtractive(lv_color_t fg, lv_color_t bg, lv_opa_t opa)
{
if(opa <= LV_OPA_MIN) return bg;
int32_t tmp;
tmp = bg.ch.red - fg.ch.red;
fg.ch.red = LV_MAX(tmp, 0);
#if LV_COLOR_16_SWAP == 0
tmp = bg.ch.green - fg.ch.green;
fg.ch.green = LV_MAX(tmp, 0);
#else
tmp = (bg.ch.green_h << 3) + bg.ch.green_l + (fg.ch.green_h << 3) + fg.ch.green_l;
tmp = LV_MAX(tmp, 0);
fg.ch.green_h = tmp >> 3;
fg.ch.green_l = tmp & 0x7;
#endif
tmp = bg.ch.blue - fg.ch.blue;
fg.ch.blue = LV_MAX(tmp, 0);
if(opa == LV_OPA_COVER) return fg;
return lv_color_mix(fg, bg, opa);
}
static inline lv_color_t color_blend_true_color_multiply(lv_color_t fg, lv_color_t bg, lv_opa_t opa)
{
if(opa <= LV_OPA_MIN) return bg;
#if LV_COLOR_DEPTH == 32
fg.ch.red = (fg.ch.red * bg.ch.red) >> 8;
fg.ch.green = (fg.ch.green * bg.ch.green) >> 8;
fg.ch.blue = (fg.ch.blue * bg.ch.blue) >> 8;
#elif LV_COLOR_DEPTH == 16
fg.ch.red = (fg.ch.red * bg.ch.red) >> 5;
fg.ch.blue = (fg.ch.blue * bg.ch.blue) >> 5;
LV_COLOR_SET_G(fg, (LV_COLOR_GET_G(fg) * LV_COLOR_GET_G(bg)) >> 6);
#elif LV_COLOR_DEPTH == 8
fg.ch.red = (fg.ch.red * bg.ch.red) >> 3;
fg.ch.green = (fg.ch.green * bg.ch.green) >> 3;
fg.ch.blue = (fg.ch.blue * bg.ch.blue) >> 2;
#endif
if(opa == LV_OPA_COVER) return fg;
return lv_color_mix(fg, bg, opa);
}
#endif

View File

@@ -0,0 +1,69 @@
/**
* @file lv_draw_sw_blend.h
*
*/
#ifndef LV_DRAW_SW_BLEND_H
#define LV_DRAW_SW_BLEND_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../../misc/lv_color.h"
#include "../../misc/lv_area.h"
#include "../../misc/lv_style.h"
#include "../lv_draw_mask.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
typedef struct {
const lv_area_t * blend_area; /**< The area with absolute coordinates to draw on `draw_ctx->buf`
* will be clipped to `draw_ctx->clip_area` */
const lv_color_t * src_buf; /**< Pointer to an image to blend. If set `fill_color` is ignored */
lv_color_t color; /**< Fill color*/
lv_opa_t * mask_buf; /**< NULL if ignored, or an alpha mask to apply on `blend_area`*/
lv_draw_mask_res_t mask_res; /**< The result of the previous mask operation */
const lv_area_t * mask_area; /**< The area of `mask_buf` with absolute coordinates*/
lv_opa_t opa; /**< The overall opacity*/
lv_blend_mode_t blend_mode; /**< E.g. LV_BLEND_MODE_ADDITIVE*/
} lv_draw_sw_blend_dsc_t;
struct _lv_draw_ctx_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Call the blend function of the `draw_ctx`.
* @param draw_ctx pointer to a draw context
* @param dsc pointer to an initialized blend descriptor
*/
void lv_draw_sw_blend(struct _lv_draw_ctx_t * draw_ctx, const lv_draw_sw_blend_dsc_t * dsc);
/**
* The basic blend function used with software rendering.
* @param draw_ctx pointer to a draw context
* @param dsc pointer to an initialized blend descriptor
*/
LV_ATTRIBUTE_FAST_MEM void lv_draw_sw_blend_basic(struct _lv_draw_ctx_t * draw_ctx, const lv_draw_sw_blend_dsc_t * dsc);
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_DRAW_SW_BLEND_H*/

View File

@@ -0,0 +1,213 @@
/**
* @file lv_draw_sw_dither.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_draw_sw_dither.h"
#include "lv_draw_sw_gradient.h"
#include "../../misc/lv_color.h"
/**********************
* STATIC FUNCTIONS
**********************/
#if _DITHER_GRADIENT
LV_ATTRIBUTE_FAST_MEM void lv_dither_none(lv_grad_t * grad, lv_coord_t x, lv_coord_t y, lv_coord_t w)
{
LV_UNUSED(x);
LV_UNUSED(y);
if(grad == NULL || grad->filled) return;
for(lv_coord_t i = 0; i < w; i++) {
grad->map[i] = lv_color_hex(grad->hmap[i].full);
}
grad->filled = 1;
}
static const uint8_t dither_ordered_threshold_matrix[8 * 8] = {
0, 48, 12, 60, 3, 51, 15, 63,
32, 16, 44, 28, 35, 19, 47, 31,
8, 56, 4, 52, 11, 59, 7, 55,
40, 24, 36, 20, 43, 27, 39, 23,
2, 50, 14, 62, 1, 49, 13, 61,
34, 18, 46, 30, 33, 17, 45, 29,
10, 58, 6, 54, 9, 57, 5, 53,
42, 26, 38, 22, 41, 25, 37, 21
}; /* Shift by 6 to normalize */
LV_ATTRIBUTE_FAST_MEM void lv_dither_ordered_hor(lv_grad_t * grad, lv_coord_t x, lv_coord_t y, lv_coord_t w)
{
LV_UNUSED(x);
/* For vertical dithering, the error is spread on the next column (and not next line).
Since the renderer is scanline based, it's not obvious what could be used to perform the rendering efficiently.
The algorithm below is based on few assumptions:
1. An error diffusion algorithm (like Floyd Steinberg) here would be hard to implement since it means that a pixel on column n depends on the pixel on row n
2. Instead an ordered dithering algorithm shift the value a bit, but the influence only spread from the matrix size (used 8x8 here)
3. It means that a pixel i,j only depends on the value of a pixel i-7, j-7 to i,j and no other one.
Then we compute a complete row of ordered dither and store it in out. */
/*The apply the algorithm for this patch*/
for(lv_coord_t j = 0; j < w; j++) {
int8_t factor = dither_ordered_threshold_matrix[(y & 7) * 8 + ((j) & 7)] - 32;
lv_color32_t tmp = grad->hmap[LV_CLAMP(0, j - 4, grad->size)];
lv_color32_t t;
t.ch.red = LV_CLAMP(0, tmp.ch.red + factor, 255);
t.ch.green = LV_CLAMP(0, tmp.ch.green + factor, 255);
t.ch.blue = LV_CLAMP(0, tmp.ch.blue + factor, 255);
grad->map[j] = lv_color_hex(t.full);
}
}
LV_ATTRIBUTE_FAST_MEM void lv_dither_ordered_ver(lv_grad_t * grad, lv_coord_t x, lv_coord_t y, lv_coord_t w)
{
/* For vertical dithering, the error is spread on the next column (and not next line).
Since the renderer is scanline based, it's not obvious what could be used to perform the rendering efficiently.
The algorithm below is based on few assumptions:
1. An error diffusion algorithm (like Floyd Steinberg) here would be hard to implement since it means that a pixel on column n depends on the pixel on row n
2. Instead an ordered dithering algorithm shift the value a bit, but the influence only spread from the matrix size (used 8x8 here)
3. It means that a pixel i,j only depends on the value of a pixel i-7, j-7 to i,j and no other one.
Then we compute a complete row of ordered dither and store it in out. */
/*Extract patch for working with, selected pseudo randomly*/
lv_color32_t tmp = grad->hmap[LV_CLAMP(0, y - 4, grad->size)];
/*The apply the algorithm for this patch*/
for(lv_coord_t j = 0; j < 8; j++) {
int8_t factor = dither_ordered_threshold_matrix[(y & 7) * 8 + ((j + x) & 7)] - 32;
lv_color32_t t;
t.ch.red = LV_CLAMP(0, tmp.ch.red + factor, 255);
t.ch.green = LV_CLAMP(0, tmp.ch.green + factor, 255);
t.ch.blue = LV_CLAMP(0, tmp.ch.blue + factor, 255);
grad->map[j] = lv_color_hex(t.full);
}
/*Finally fill the line*/
lv_coord_t j = 8;
for(; j < w - 8; j += 8) {
lv_memcpy(grad->map + j, grad->map, 8 * sizeof(*grad->map));
}
/* Prevent overwriting */
for(; j < w; j++) {
grad->map[j] = grad->map[j & 7];
}
}
#if LV_DITHER_ERROR_DIFFUSION == 1
LV_ATTRIBUTE_FAST_MEM void lv_dither_err_diff_hor(lv_grad_t * grad, lv_coord_t xs, lv_coord_t y, lv_coord_t w)
{
LV_UNUSED(xs);
LV_UNUSED(y);
LV_UNUSED(w);
/* Implement Floyd Steinberg algorithm, see https://surma.dev/things/ditherpunk/
Coefs are: x 7
3 5 1
/ 16
Can be implemented as: x (x<<3 - x)
(x<<2 - x) (x<<2+x) x
*/
int coef[4] = {0, 0, 0, 0};
#define FS_COMPUTE_ERROR(e) { coef[0] = (e<<3) - e; coef[1] = (e<<2) - e; coef[2] = (e<<2) + e; coef[3] = e; }
#define FS_COMPONENTS(A, OP, B, C) A.ch.red = LV_CLAMP(0, A.ch.red OP B.r OP C.r, 255); A.ch.green = LV_CLAMP(0, A.ch.green OP B.g OP C.g, 255); A.ch.blue = LV_CLAMP(0, A.ch.blue OP B.b OP C.b, 255);
#define FS_QUANT_ERROR(e, t, q) { lv_color32_t u; u.full = lv_color_to32(q); e.r = (int8_t)(t.ch.red - u.ch.red); e.g = (int8_t)(t.ch.green - u.ch.green); e.b = (int8_t)(t.ch.blue - u.ch.blue); }
lv_scolor24_t next_px_err = {0}, next_l = {0}, error;
/*First last pixel are not dithered */
grad->map[0] = lv_color_hex(grad->hmap[0].full);
for(lv_coord_t x = 1; x < grad->size - 1; x++) {
lv_color32_t t = grad->hmap[x];
lv_color_t q;
/*Add error term*/
FS_COMPONENTS(t, +, next_px_err, next_l);
next_l = grad->error_acc[x + 1];
/*Quantify*/
q = lv_color_hex(t.full);
/*Then compute error*/
FS_QUANT_ERROR(error, t, q);
/*Dither the error*/
FS_COMPUTE_ERROR(error.r);
next_px_err.r = coef[0] >> 4;
grad->error_acc[x - 1].r += coef[1] >> 4;
grad->error_acc[x].r += coef[2] >> 4;
grad->error_acc[x + 1].r = coef[3] >> 4;
FS_COMPUTE_ERROR(error.g);
next_px_err.g = coef[0] >> 4;
grad->error_acc[x - 1].g += coef[1] >> 4;
grad->error_acc[x].g += coef[2] >> 4;
grad->error_acc[x + 1].g = coef[3] >> 4;
FS_COMPUTE_ERROR(error.b);
next_px_err.b = coef[0] >> 4;
grad->error_acc[x - 1].b += coef[1] >> 4;
grad->error_acc[x].b += coef[2] >> 4;
grad->error_acc[x + 1].b = coef[3] >> 4;
grad->map[x] = q;
}
grad->map[grad->size - 1] = lv_color_hex(grad->hmap[grad->size - 1].full);
}
LV_ATTRIBUTE_FAST_MEM void lv_dither_err_diff_ver(lv_grad_t * grad, lv_coord_t xs, lv_coord_t y, lv_coord_t w)
{
/* Try to implement error diffusion on a vertical gradient and an horizontal map using those tricks:
Since the given hi-resolution gradient (in src) is vertical, the Floyd Steinberg algorithm pass need to be rotated,
so we'll get this instead (from top to bottom):
A B C
1 [ ][ ][ ]
2 [ ][ ][ ] Pixel A2 will spread its error on pixel A3 with coefficient 7,
3 [ ][ ][ ] Pixel A2 will spread its error on pixel B1 with coefficient 3, B2 with coef 5 and B3 with coef 1
When taking into account an arbitrary pixel P(i,j), its added error diffusion term is:
e(i,j) = 1/16 * [ e(i-1,j) * 5 + e(i-1,j+1) * 3 + e(i-1,j-1) * 1 + e(i,j-1) * 7]
This means that the error term depends on pixel W, NW, N and SW.
If we consider that we are generating the error diffused gradient map from top to bottom, we can remember the previous
line (N, NW) in the term above. Also, we remember the (W) term too since we are computing the gradient map from left to right.
However, the SW term is painful for us, we can't support it (since to get it, we need its own SW term and so on).
Let's remove it and re-dispatch the error factor accordingly so they stays normalized:
e(i,j) ~= 1/16 * [ e(i-1,j) * 6 + e(i-1,j-1) * 1 + e(i,j-1) * 9]
That's the idea of this pseudo Floyd Steinberg dithering */
#define FS_APPLY(d, s, c) d.r = (int8_t)(s.r * c) >> 4; d.g = (int8_t)(s.g * c) >> 4; d.b = (int8_t)(s.b * c) >> 4;
#define FS_COMPONENTS3(A, OP, B, b, C, c, D, d) \
A.ch.red = LV_CLAMP(0, A.ch.red OP ((B.r * b OP C.r * c OP D.r * d) >> 4), 255); \
A.ch.green = LV_CLAMP(0, A.ch.green OP ((B.r * b OP C.r * c OP D.r * d) >> 4), 255); \
A.ch.blue = LV_CLAMP(0, A.ch.blue OP ((B.r * b OP C.r * c OP D.r * d) >> 4), 255);
lv_scolor24_t next_px_err, prev_l = grad->error_acc[0];
/*Compute the error term for the current pixel (first pixel is never dithered)*/
if(xs == 0) {
grad->map[0] = lv_color_hex(grad->hmap[y].full);
FS_QUANT_ERROR(next_px_err, grad->hmap[y], grad->map[0]);
}
else {
lv_color_t tmp = lv_color_hex(grad->hmap[y].full);
lv_color32_t t = grad->hmap[y];
FS_QUANT_ERROR(next_px_err, grad->hmap[y], tmp);
FS_COMPONENTS3(t, +, next_px_err, 6, prev_l, 1, grad->error_acc[0], 9);
grad->map[0] = lv_color_hex(t.full);
}
for(lv_coord_t x = 1; x < w; x++) {
lv_color32_t t = grad->hmap[y];
lv_color_t q;
/*Add the current error term*/
FS_COMPONENTS3(t, +, next_px_err, 6, prev_l, 1, grad->error_acc[x], 9);
prev_l = grad->error_acc[x];
/*Quantize and compute error term*/
q = lv_color_hex(t.full);
FS_QUANT_ERROR(next_px_err, t, q);
/*Store error for next line computation*/
grad->error_acc[x] = next_px_err;
grad->map[x] = q;
}
}
#endif
#endif

View File

@@ -0,0 +1,70 @@
/**
* @file lv_draw_sw_dither.h
*
*/
#ifndef LV_DRAW_SW_DITHER_H
#define LV_DRAW_SW_DITHER_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../../core/lv_obj_pos.h"
/*********************
* DEFINES
*********************/
#if LV_COLOR_DEPTH < 32 && LV_DITHER_GRADIENT == 1
#define _DITHER_GRADIENT 1
#else
#define _DITHER_GRADIENT 0
#endif
/**********************
* TYPEDEFS
**********************/
#if _DITHER_GRADIENT
/*A signed error color component*/
typedef struct {
int8_t r, g, b;
} lv_scolor24_t;
struct _lv_gradient_cache_t;
typedef void (*lv_dither_func_t)(struct _lv_gradient_cache_t * grad, lv_coord_t x, lv_coord_t y, lv_coord_t w);
#endif
/**********************
* PROTOTYPES
**********************/
#if LV_DRAW_COMPLEX
#if _DITHER_GRADIENT
LV_ATTRIBUTE_FAST_MEM void lv_dither_none(struct _lv_gradient_cache_t * grad, lv_coord_t x, lv_coord_t y, lv_coord_t w);
LV_ATTRIBUTE_FAST_MEM void lv_dither_ordered_hor(struct _lv_gradient_cache_t * grad, const lv_coord_t xs,
const lv_coord_t y, const lv_coord_t w);
LV_ATTRIBUTE_FAST_MEM void lv_dither_ordered_ver(struct _lv_gradient_cache_t * grad, const lv_coord_t xs,
const lv_coord_t y, const lv_coord_t w);
#if LV_DITHER_ERROR_DIFFUSION == 1
LV_ATTRIBUTE_FAST_MEM void lv_dither_err_diff_hor(struct _lv_gradient_cache_t * grad, const lv_coord_t xs,
const lv_coord_t y, const lv_coord_t w);
LV_ATTRIBUTE_FAST_MEM void lv_dither_err_diff_ver(struct _lv_gradient_cache_t * grad, const lv_coord_t xs,
const lv_coord_t y, const lv_coord_t w);
#endif /* LV_DITHER_ERROR_DIFFUSION */
#endif /* _DITHER_GRADIENT */
#endif
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif

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