feat(draw): add gradient dithering support (#2872)
* Add dithering to gradients * Add support for 8x8 matrix for ordered dithering * Fix CI errors * Try error diffusion on vertical gradient too * Vertical error diffusion dithering * Add support for runtime based dithering mode selection (from none, ordered, error diffusion). * Reduce the binary size of the code by sharing the dithering table when appropriate. * Fix CI * Fix CI * Review corrections * Fix union mapping * Revert bg_color changes * Fix for keeping bg_color in the API. * Fix after review * Add support for setting multiple stops per gradient in the style API * Let's make an example Co-authored-by: Gabor Kiss-Vamosi <kisvegabor@gmail.com>
This commit is contained in:
@@ -252,6 +252,24 @@ Set the point from which the background's gradient color should start. 0 means t
|
|||||||
<li style='display:inline; margin-right: 20px; margin-left: 0px'><strong>Ext. draw</strong> No</li>
|
<li style='display:inline; margin-right: 20px; margin-left: 0px'><strong>Ext. draw</strong> No</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
|
### bg_gradient
|
||||||
|
Set the gradient definition for the body. The pointed instance must exist while the object is alive. NULL to disable
|
||||||
|
<ul>
|
||||||
|
<li style='display:inline; margin-right: 20px; margin-left: 0px'><strong>Default</strong> `NULL`</li>
|
||||||
|
<li style='display:inline; margin-right: 20px; margin-left: 0px'><strong>Inherited</strong> No</li>
|
||||||
|
<li style='display:inline; margin-right: 20px; margin-left: 0px'><strong>Layout</strong> No</li>
|
||||||
|
<li style='display:inline; margin-right: 20px; margin-left: 0px'><strong>Ext. draw</strong> No</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
### bg_dither_mode
|
||||||
|
Set the dithering mode of the gradient of the background. The possible values are `LV_DITHER_NONE/ORDERED/ERR_DIFF`.
|
||||||
|
<ul>
|
||||||
|
<li style='display:inline; margin-right: 20px; margin-left: 0px'><strong>Default</strong> `LV_DITHER_NONE`</li>
|
||||||
|
<li style='display:inline; margin-right: 20px; margin-left: 0px'><strong>Inherited</strong> No</li>
|
||||||
|
<li style='display:inline; margin-right: 20px; margin-left: 0px'><strong>Layout</strong> No</li>
|
||||||
|
<li style='display:inline; margin-right: 20px; margin-left: 0px'><strong>Ext. draw</strong> No</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
### bg_img_src
|
### bg_img_src
|
||||||
Set a background image. Can be a pointer to `lv_img_dsc_t`, a path to a file or an `LV_SYMBOL_...`
|
Set a background image. Can be a pointer to `lv_img_dsc_t`, a path to a file or an `LV_SYMBOL_...`
|
||||||
<ul>
|
<ul>
|
||||||
|
|||||||
@@ -12,13 +12,17 @@ void lv_example_style_2(void)
|
|||||||
|
|
||||||
/*Make a gradient*/
|
/*Make a gradient*/
|
||||||
lv_style_set_bg_opa(&style, LV_OPA_COVER);
|
lv_style_set_bg_opa(&style, LV_OPA_COVER);
|
||||||
lv_style_set_bg_color(&style, lv_palette_lighten(LV_PALETTE_GREY, 1));
|
static lv_gradient_t grad;
|
||||||
lv_style_set_bg_grad_color(&style, lv_palette_main(LV_PALETTE_BLUE));
|
grad.dir = LV_GRAD_DIR_VER;
|
||||||
lv_style_set_bg_grad_dir(&style, LV_GRAD_DIR_VER);
|
grad.stops_count = 2;
|
||||||
|
grad.stops[0].color = lv_palette_lighten(LV_PALETTE_GREY, 1);
|
||||||
|
grad.stops[1].color = lv_palette_main(LV_PALETTE_BLUE);
|
||||||
|
|
||||||
/*Shift the gradient to the bottom*/
|
/*Shift the gradient to the bottom*/
|
||||||
lv_style_set_bg_main_stop(&style, 128);
|
grad.stops[0].frac = 128;
|
||||||
lv_style_set_bg_grad_stop(&style, 192);
|
grad.stops[1].frac = 192;
|
||||||
|
|
||||||
|
lv_style_set_bg_gradient(&style, &grad);
|
||||||
|
|
||||||
/*Create an object with the new style*/
|
/*Create an object with the new style*/
|
||||||
lv_obj_t * obj = lv_obj_create(lv_scr_act());
|
lv_obj_t * obj = lv_obj_create(lv_scr_act());
|
||||||
|
|||||||
@@ -11,9 +11,9 @@ void lv_example_canvas_1(void)
|
|||||||
lv_draw_rect_dsc_init(&rect_dsc);
|
lv_draw_rect_dsc_init(&rect_dsc);
|
||||||
rect_dsc.radius = 10;
|
rect_dsc.radius = 10;
|
||||||
rect_dsc.bg_opa = LV_OPA_COVER;
|
rect_dsc.bg_opa = LV_OPA_COVER;
|
||||||
rect_dsc.bg_grad_dir = LV_GRAD_DIR_HOR;
|
rect_dsc.bg_grad.dir = LV_GRAD_DIR_HOR;
|
||||||
rect_dsc.bg_color = lv_palette_main(LV_PALETTE_RED);
|
rect_dsc.bg_grad.stops[0].color = lv_palette_main(LV_PALETTE_RED);
|
||||||
rect_dsc.bg_grad_color = lv_palette_main(LV_PALETTE_BLUE);
|
rect_dsc.bg_grad.stops[1].color = lv_palette_main(LV_PALETTE_BLUE);
|
||||||
rect_dsc.border_width = 2;
|
rect_dsc.border_width = 2;
|
||||||
rect_dsc.border_opa = LV_OPA_90;
|
rect_dsc.border_opa = LV_OPA_90;
|
||||||
rect_dsc.border_color = lv_color_white();
|
rect_dsc.border_color = lv_color_white();
|
||||||
|
|||||||
@@ -6,9 +6,9 @@ rect_dsc = lv.draw_rect_dsc_t()
|
|||||||
rect_dsc.init()
|
rect_dsc.init()
|
||||||
rect_dsc.radius = 10
|
rect_dsc.radius = 10
|
||||||
rect_dsc.bg_opa = lv.OPA.COVER
|
rect_dsc.bg_opa = lv.OPA.COVER
|
||||||
rect_dsc.bg_grad_dir = lv.GRAD_DIR.HOR
|
rect_dsc.bg_grad.dir = lv.GRAD_DIR.HOR
|
||||||
rect_dsc.bg_color = lv.palette_main(lv.PALETTE.RED)
|
rect_dsc.bg_grad.stops[0].color = lv.palette_main(lv.PALETTE.RED)
|
||||||
rect_dsc.bg_grad_color = lv.palette_main(lv.PALETTE.BLUE)
|
rect_dsc.bg_grad.stops[1].color = lv.palette_main(lv.PALETTE.BLUE)
|
||||||
rect_dsc.border_width = 2
|
rect_dsc.border_width = 2
|
||||||
rect_dsc.border_opa = lv.OPA._90
|
rect_dsc.border_opa = lv.OPA._90
|
||||||
rect_dsc.border_color = lv.color_white()
|
rect_dsc.border_color = lv.color_white()
|
||||||
|
|||||||
@@ -119,6 +119,20 @@
|
|||||||
* 0: to disable caching */
|
* 0: to disable caching */
|
||||||
#define LV_CIRCLE_CACHE_SIZE 4
|
#define LV_CIRCLE_CACHE_SIZE 4
|
||||||
|
|
||||||
|
/*Allow dithering gradient (to achieve visual smooth color gradients on limited color depth display)
|
||||||
|
*LV_DITHER_GRADIENT implies allocating one or two more lines of the object's rendering surface
|
||||||
|
*The increase in memory consumption is (32 bits * object width) plus 24 bits * object width if using error diffusion */
|
||||||
|
#define LV_DITHER_GRADIENT 1
|
||||||
|
|
||||||
|
/*Add support for error diffusion dithering.
|
||||||
|
*Error diffusion dithering gets a much better visual result, but implies more CPU consumption and memory when drawing.
|
||||||
|
*The increase in memory consumption is (24 bits * object's width)*/
|
||||||
|
#define LV_DITHER_ERROR_DIFFUSION 1
|
||||||
|
|
||||||
|
/**Number of stops allowed per gradient. Increase this to allow more stops.
|
||||||
|
*This adds (sizeof(lv_color_t) + 1) bytes per additional stop*/
|
||||||
|
#define LV_GRADIENT_MAX_STOPS 2
|
||||||
|
|
||||||
#endif /*LV_DRAW_COMPLEX*/
|
#endif /*LV_DRAW_COMPLEX*/
|
||||||
|
|
||||||
/*Default image cache size. Image caching keeps the images opened.
|
/*Default image cache size. Image caching keeps the images opened.
|
||||||
|
|||||||
@@ -120,6 +120,14 @@ props = [
|
|||||||
'style_type': 'num', 'var_type': 'lv_coord_t', 'default':255, 'inherited': 0, 'layout': 0, 'ext_draw': 0,
|
'style_type': 'num', 'var_type': 'lv_coord_t', 'default':255, 'inherited': 0, 'layout': 0, 'ext_draw': 0,
|
||||||
'dsc': "Set the point from which the background's gradient color should start. 0 means to top/left side, 255 the bottom/right side, 128 the center, and so on"},
|
'dsc': "Set the point from which the background's gradient color should start. 0 means to top/left side, 255 the bottom/right side, 128 the center, and so on"},
|
||||||
|
|
||||||
|
{'name': 'BG_GRADIENT',
|
||||||
|
'style_type': 'ptr', 'var_type': 'const lv_gradient_t *', 'default':'`NULL`', 'inherited': 0, 'layout': 0, 'ext_draw': 0,
|
||||||
|
'dsc': "Set the gradient definition for the body. The pointed instance must exist while the object is alive. NULL to disable"},
|
||||||
|
|
||||||
|
{'name': 'BG_DITHER_MODE',
|
||||||
|
'style_type': 'num', 'var_type': 'lv_dither_mode_t', 'default':'`LV_DITHER_NONE`', 'inherited': 0, 'layout': 0, 'ext_draw': 0,
|
||||||
|
'dsc': "Set the dithering mode of the gradient of the background. The possible values are `LV_DITHER_NONE/ORDERED/ERR_DIFF`."},
|
||||||
|
|
||||||
{'name': 'BG_IMG_SRC',
|
{'name': 'BG_IMG_SRC',
|
||||||
'style_type': 'ptr', 'var_type': 'const void *', 'default':'`NULL`', 'inherited': 0, 'layout': 0, 'ext_draw': 1,
|
'style_type': 'ptr', 'var_type': 'const void *', 'default':'`NULL`', 'inherited': 0, 'layout': 0, 'ext_draw': 1,
|
||||||
'dsc': "Set a background image. Can be a pointer to `lv_img_dsc_t`, a path to a file or an `LV_SYMBOL_...`"},
|
'dsc': "Set a background image. Can be a pointer to `lv_img_dsc_t`, a path to a file or an `LV_SYMBOL_...`"},
|
||||||
|
|||||||
@@ -58,11 +58,19 @@ void lv_obj_init_draw_rect_dsc(lv_obj_t * obj, uint32_t part, lv_draw_rect_dsc_t
|
|||||||
draw_dsc->bg_opa = lv_obj_get_style_bg_opa(obj, part);
|
draw_dsc->bg_opa = lv_obj_get_style_bg_opa(obj, part);
|
||||||
if(draw_dsc->bg_opa > LV_OPA_MIN) {
|
if(draw_dsc->bg_opa > LV_OPA_MIN) {
|
||||||
draw_dsc->bg_color = lv_obj_get_style_bg_color_filtered(obj, part);
|
draw_dsc->bg_color = lv_obj_get_style_bg_color_filtered(obj, part);
|
||||||
draw_dsc->bg_grad_dir = lv_obj_get_style_bg_grad_dir(obj, part);
|
const lv_gradient_t * grad = lv_obj_get_style_bg_gradient(obj, part);
|
||||||
if(draw_dsc->bg_grad_dir != LV_GRAD_DIR_NONE) {
|
if(grad && grad->dir != LV_GRAD_DIR_NONE) {
|
||||||
draw_dsc->bg_grad_color = lv_obj_get_style_bg_grad_color_filtered(obj, part);
|
lv_memcpy(&draw_dsc->bg_grad, grad, sizeof(*grad));
|
||||||
draw_dsc->bg_main_color_stop = lv_obj_get_style_bg_main_stop(obj, part);
|
}
|
||||||
draw_dsc->bg_grad_color_stop = lv_obj_get_style_bg_grad_stop(obj, part);
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -232,6 +232,22 @@ void lv_obj_set_style_bg_grad_stop(struct _lv_obj_t * obj, lv_coord_t value, lv_
|
|||||||
lv_obj_set_local_style_prop(obj, LV_STYLE_BG_GRAD_STOP, v, selector);
|
lv_obj_set_local_style_prop(obj, LV_STYLE_BG_GRAD_STOP, v, selector);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void lv_obj_set_style_bg_gradient(struct _lv_obj_t * obj, const lv_gradient_t * value, lv_style_selector_t selector)
|
||||||
|
{
|
||||||
|
lv_style_value_t v = {
|
||||||
|
.ptr = value
|
||||||
|
};
|
||||||
|
lv_obj_set_local_style_prop(obj, LV_STYLE_BG_GRADIENT, 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)
|
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 = {
|
lv_style_value_t v = {
|
||||||
|
|||||||
@@ -172,6 +172,18 @@ static inline lv_coord_t lv_obj_get_style_bg_grad_stop(const struct _lv_obj_t *
|
|||||||
return (lv_coord_t)v.num;
|
return (lv_coord_t)v.num;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline const lv_gradient_t * lv_obj_get_style_bg_gradient(const struct _lv_obj_t * obj, uint32_t part)
|
||||||
|
{
|
||||||
|
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_BG_GRADIENT);
|
||||||
|
return (const lv_gradient_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)
|
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);
|
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_BG_IMG_SRC);
|
||||||
@@ -562,6 +574,8 @@ void lv_obj_set_style_bg_grad_color_filtered(struct _lv_obj_t * obj, lv_color_t
|
|||||||
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_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_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_stop(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector);
|
||||||
|
void lv_obj_set_style_bg_gradient(struct _lv_obj_t * obj, const lv_gradient_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_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_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(struct _lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector);
|
||||||
|
|||||||
@@ -38,10 +38,14 @@ 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));
|
lv_memset_00(dsc, sizeof(lv_draw_rect_dsc_t));
|
||||||
dsc->bg_color = lv_color_white();
|
dsc->bg_color = lv_color_white();
|
||||||
dsc->bg_grad_color = lv_color_black();
|
#if __STDC_VERSION__ < 201112L
|
||||||
|
dsc->bg_grad.stops[0].color = lv_color_white();
|
||||||
|
#endif
|
||||||
|
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->border_color = lv_color_black();
|
||||||
dsc->shadow_color = lv_color_black();
|
dsc->shadow_color = lv_color_black();
|
||||||
dsc->bg_grad_color_stop = 0xFF;
|
|
||||||
dsc->bg_img_symbol_font = LV_FONT_DEFAULT;
|
dsc->bg_img_symbol_font = LV_FONT_DEFAULT;
|
||||||
dsc->bg_opa = LV_OPA_COVER;
|
dsc->bg_opa = LV_OPA_COVER;
|
||||||
dsc->bg_img_opa = LV_OPA_COVER;
|
dsc->bg_img_opa = LV_OPA_COVER;
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ extern "C" {
|
|||||||
#include "../misc/lv_color.h"
|
#include "../misc/lv_color.h"
|
||||||
#include "../misc/lv_area.h"
|
#include "../misc/lv_area.h"
|
||||||
#include "../misc/lv_style.h"
|
#include "../misc/lv_style.h"
|
||||||
|
#include "sw/lv_draw_sw_gradient.h"
|
||||||
|
|
||||||
/*********************
|
/*********************
|
||||||
* DEFINES
|
* DEFINES
|
||||||
@@ -33,12 +34,15 @@ typedef struct {
|
|||||||
lv_blend_mode_t blend_mode;
|
lv_blend_mode_t blend_mode;
|
||||||
|
|
||||||
/*Background*/
|
/*Background*/
|
||||||
lv_color_t bg_color;
|
|
||||||
lv_color_t bg_grad_color;
|
|
||||||
uint8_t bg_main_color_stop;
|
|
||||||
uint8_t bg_grad_color_stop;
|
|
||||||
lv_opa_t bg_opa;
|
lv_opa_t bg_opa;
|
||||||
lv_grad_dir_t bg_grad_dir : 3;
|
#if __STDC_VERSION__ >= 201112L
|
||||||
|
union {
|
||||||
|
#endif
|
||||||
|
lv_color_t bg_color; /**< First element of a gradient is a color, so it maps well here*/
|
||||||
|
lv_gradient_t bg_grad;
|
||||||
|
#if __STDC_VERSION__ >= 201112L
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
/*Background img*/
|
/*Background img*/
|
||||||
const void * bg_img_src;
|
const void * bg_img_src;
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ CSRCS += lv_draw_sw_letter.c
|
|||||||
CSRCS += lv_draw_sw_line.c
|
CSRCS += lv_draw_sw_line.c
|
||||||
CSRCS += lv_draw_sw_rect.c
|
CSRCS += lv_draw_sw_rect.c
|
||||||
CSRCS += lv_draw_sw_polygon.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
|
DEPPATH += --dep-path $(LVGL_DIR)/$(LVGL_DIR_NAME)/src/draw/sw
|
||||||
VPATH += :$(LVGL_DIR)/$(LVGL_DIR_NAME)/src/draw/sw
|
VPATH += :$(LVGL_DIR)/$(LVGL_DIR_NAME)/src/draw/sw
|
||||||
|
|||||||
@@ -128,7 +128,7 @@ LV_ATTRIBUTE_FAST_MEM void lv_draw_sw_blend_basic(lv_draw_ctx_t * draw_ctx, cons
|
|||||||
lv_coord_t src_stride;
|
lv_coord_t src_stride;
|
||||||
if(src_buf) {
|
if(src_buf) {
|
||||||
src_stride = lv_area_get_width(dsc->blend_area);
|
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);
|
src_buf += src_stride * (blend_area.y1 - dsc->blend_area->y1) + (blend_area.x1 - dsc->blend_area->x1);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
src_stride = 0;
|
src_stride = 0;
|
||||||
|
|||||||
@@ -28,8 +28,8 @@ extern "C" {
|
|||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
const lv_area_t * blend_area; /**< The area with absolute coordinates to draw on `draw_ctx->buf`
|
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` */
|
* 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`*/
|
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_color_t color; /**< Fill color*/
|
||||||
lv_opa_t * mask_buf; /**< NULL if ignored, or an alpha mask to apply on `blend_area`*/
|
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 */
|
lv_draw_mask_res_t mask_res; /**< The result of the previous mask operation */
|
||||||
|
|||||||
213
src/draw/sw/lv_draw_sw_dither.c
Normal file
213
src/draw/sw/lv_draw_sw_dither.c
Normal 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_gradient_cache_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_gradient_cache_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->hmap_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_gradient_cache_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->hmap_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_gradient_cache_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, next_l = grad->error_acc[1], 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->hmap_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->hmap_size - 1] = lv_color_hex(grad->hmap[grad->hmap_size - 1].full);
|
||||||
|
}
|
||||||
|
|
||||||
|
LV_ATTRIBUTE_FAST_MEM void lv_dither_err_diff_ver(lv_gradient_cache_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
|
||||||
71
src/draw/sw/lv_draw_sw_dither.h
Normal file
71
src/draw/sw/lv_draw_sw_dither.h
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
/**
|
||||||
|
* @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_DRAW_COMPLEX == 1 && 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
|
||||||
316
src/draw/sw/lv_draw_sw_gradient.c
Normal file
316
src/draw/sw/lv_draw_sw_gradient.c
Normal file
@@ -0,0 +1,316 @@
|
|||||||
|
/**
|
||||||
|
* @file lv_draw_sw_gradient.c
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*********************
|
||||||
|
* INCLUDES
|
||||||
|
*********************/
|
||||||
|
#include "lv_draw_sw_gradient.h"
|
||||||
|
|
||||||
|
|
||||||
|
/*********************
|
||||||
|
* DEFINES
|
||||||
|
*********************/
|
||||||
|
#if _DITHER_GRADIENT
|
||||||
|
#define GRAD_CM(r,g,b) LV_COLOR_MAKE32(r,g,b)
|
||||||
|
#define GRAD_CONV(t, x) t.full = lv_color_to32(x)
|
||||||
|
#else
|
||||||
|
#define GRAD_CM(r,g,b) LV_COLOR_MAKE(r,g,b)
|
||||||
|
#define GRAD_CONV(t, x) t = x
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define MAX_WIN_RES 1024 /**TODO: Find a way to get this information: max(horz_res, vert_res)*/
|
||||||
|
|
||||||
|
#if _DITHER_GRADIENT
|
||||||
|
#if LV_DITHER_ERROR_DIFFUSION == 1
|
||||||
|
#define LV_DEFAULT_GRAD_CACHE_SIZE sizeof(lv_gradient_cache_t) + MAX_WIN_RES * sizeof(lv_grad_color_t) + MAX_WIN_RES * sizeof(lv_color_t) + MAX_WIN_RES * sizeof(lv_scolor24_t)
|
||||||
|
#else
|
||||||
|
#define LV_DEFAULT_GRAD_CACHE_SIZE sizeof(lv_gradient_cache_t) + MAX_WIN_RES * sizeof(lv_grad_color_t) + MAX_WIN_RES * sizeof(lv_color_t)
|
||||||
|
#endif /* LV_DITHER_ERROR_DIFFUSION */
|
||||||
|
#else
|
||||||
|
#define LV_DEFAULT_GRAD_CACHE_SIZE sizeof(lv_gradient_cache_t) + MAX_WIN_RES * sizeof(lv_grad_color_t)
|
||||||
|
#endif /* _DITHER_GRADIENT */
|
||||||
|
|
||||||
|
/**********************
|
||||||
|
* STATIC PROTOTYPES
|
||||||
|
**********************/
|
||||||
|
static lv_gradient_cache_t * next_in_cache(lv_gradient_cache_t * first);
|
||||||
|
|
||||||
|
typedef lv_res_t (*op_cache_t)(lv_gradient_cache_t * c, void * ctx);
|
||||||
|
static lv_res_t iterate_cache(op_cache_t func, void * ctx, lv_gradient_cache_t ** out);
|
||||||
|
static size_t get_cache_item_size(lv_gradient_cache_t * c);
|
||||||
|
static lv_gradient_cache_t * allocate_item(const lv_gradient_t * g, lv_coord_t w, lv_coord_t h);
|
||||||
|
static lv_res_t find_oldest_item_life(lv_gradient_cache_t * c, void * ctx);
|
||||||
|
static lv_res_t kill_oldest_item(lv_gradient_cache_t * c, void * ctx);
|
||||||
|
static lv_res_t find_item(lv_gradient_cache_t * c, void * ctx);
|
||||||
|
static void free_item(lv_gradient_cache_t * c);
|
||||||
|
static uint32_t compute_key(const lv_gradient_t * g, lv_coord_t w, lv_coord_t h);
|
||||||
|
|
||||||
|
|
||||||
|
/**********************
|
||||||
|
* STATIC VARIABLE
|
||||||
|
**********************/
|
||||||
|
static uint8_t * grad_cache_mem = 0;
|
||||||
|
static size_t grad_cache_size = 0;
|
||||||
|
static uint8_t * grad_cache_end = 0;
|
||||||
|
|
||||||
|
/**********************
|
||||||
|
* STATIC FUNCTIONS
|
||||||
|
**********************/
|
||||||
|
union void_cast {
|
||||||
|
const void * ptr;
|
||||||
|
const uint32_t value;
|
||||||
|
};
|
||||||
|
|
||||||
|
static uint32_t compute_key(const lv_gradient_t * g, lv_coord_t size, lv_coord_t w)
|
||||||
|
{
|
||||||
|
union void_cast v;
|
||||||
|
v.ptr = g;
|
||||||
|
return (v.value ^ size ^ (w >> 1)); /*Yes, this is correct, it's like a hash that changes if the width changes*/
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static size_t get_cache_item_size(lv_gradient_cache_t * c)
|
||||||
|
{
|
||||||
|
size_t s = sizeof(*c) + c->size * sizeof(lv_color_t)
|
||||||
|
#if _DITHER_GRADIENT
|
||||||
|
+ c->hmap_size * sizeof(lv_color32_t)
|
||||||
|
#if LV_DITHER_ERROR_DIFFUSION == 1
|
||||||
|
+ c->size * sizeof(lv_scolor24_t)
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
;
|
||||||
|
return s; /* TODO: Should we align this ? */
|
||||||
|
}
|
||||||
|
|
||||||
|
static lv_gradient_cache_t * next_in_cache(lv_gradient_cache_t * first)
|
||||||
|
{
|
||||||
|
if(first == NULL)
|
||||||
|
return (lv_gradient_cache_t *)grad_cache_mem;
|
||||||
|
if(first == NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
size_t s = get_cache_item_size(first);
|
||||||
|
/*Compute the size for this cache item*/
|
||||||
|
if((uint8_t *)first + s > grad_cache_end)
|
||||||
|
return NULL;
|
||||||
|
return (lv_gradient_cache_t *)((uint8_t *)first + s);
|
||||||
|
}
|
||||||
|
|
||||||
|
static lv_res_t iterate_cache(op_cache_t func, void * ctx, lv_gradient_cache_t ** out)
|
||||||
|
{
|
||||||
|
lv_gradient_cache_t * first = next_in_cache(NULL);
|
||||||
|
while(first != NULL) {
|
||||||
|
if((*func)(first, ctx) == LV_RES_OK) {
|
||||||
|
if(out != NULL) *out = first;
|
||||||
|
return LV_RES_OK;
|
||||||
|
}
|
||||||
|
first = next_in_cache(first);
|
||||||
|
}
|
||||||
|
return LV_RES_INV;
|
||||||
|
}
|
||||||
|
|
||||||
|
static lv_res_t find_oldest_item_life(lv_gradient_cache_t * c, void * ctx)
|
||||||
|
{
|
||||||
|
uint32_t * min_life = (uint32_t *)ctx;
|
||||||
|
if(c->life < *min_life) *min_life = c->life;
|
||||||
|
return LV_RES_INV;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void free_item(lv_gradient_cache_t * c)
|
||||||
|
{
|
||||||
|
size_t size = get_cache_item_size(c);
|
||||||
|
size_t next_items_size = (size_t)(grad_cache_end - (uint8_t *)c) - size;
|
||||||
|
grad_cache_end -= size;
|
||||||
|
if(next_items_size) {
|
||||||
|
lv_memcpy(c, ((uint8_t *)c) + size, next_items_size);
|
||||||
|
/* Then need to fix all internal pointers too */
|
||||||
|
while((uint8_t *)c != grad_cache_end) {
|
||||||
|
c->map = (lv_color_t *)(((uint8_t *)c->map) - size);
|
||||||
|
#if _DITHER_GRADIENT
|
||||||
|
c->hmap = (lv_color32_t *)(((uint8_t *)c->hmap) - size);
|
||||||
|
#if LV_DITHER_ERROR_DIFFUSION == 1
|
||||||
|
c->error_acc = (lv_scolor24_t *)(((uint8_t *)c->error_acc) - size);
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
c = (lv_gradient_cache_t *)(((uint8_t *)c) + get_cache_item_size(c));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static lv_res_t kill_oldest_item(lv_gradient_cache_t * c, void * ctx)
|
||||||
|
{
|
||||||
|
uint32_t * min_life = (uint32_t *)ctx;
|
||||||
|
if(c->life == *min_life) {
|
||||||
|
/*Found, let's kill it*/
|
||||||
|
free_item(c);
|
||||||
|
return LV_RES_OK;
|
||||||
|
}
|
||||||
|
return LV_RES_INV;
|
||||||
|
}
|
||||||
|
static lv_res_t find_item(lv_gradient_cache_t * c, void * ctx)
|
||||||
|
{
|
||||||
|
uint32_t * k = (uint32_t *)ctx;
|
||||||
|
if(c->key == *k) return LV_RES_OK;
|
||||||
|
return LV_RES_INV;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static lv_gradient_cache_t * allocate_item(const lv_gradient_t * g, lv_coord_t w, lv_coord_t h)
|
||||||
|
{
|
||||||
|
lv_coord_t size = g->dir == LV_GRAD_DIR_HOR ? w : h;
|
||||||
|
size_t req_size = sizeof(lv_gradient_cache_t) + w * sizeof(lv_color_t)
|
||||||
|
#if _DITHER_GRADIENT
|
||||||
|
+ size * sizeof(lv_color32_t)
|
||||||
|
#if LV_DITHER_ERROR_DIFFUSION == 1
|
||||||
|
+ w * sizeof(lv_scolor24_t)
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
;
|
||||||
|
size_t act_size = (size_t)(grad_cache_end - grad_cache_mem);
|
||||||
|
if(req_size + act_size > grad_cache_size) {
|
||||||
|
/*Need to evict items from cache until we find enough space to allocate this one */
|
||||||
|
if(req_size > grad_cache_size) {
|
||||||
|
LV_LOG_WARN("Gradient cache too small, failed to allocate");
|
||||||
|
return NULL; /*No magic here, if the empty cache is still too small*/
|
||||||
|
}
|
||||||
|
while(act_size + req_size < grad_cache_size) {
|
||||||
|
uint32_t oldest_life = UINT32_MAX;
|
||||||
|
iterate_cache(&find_oldest_item_life, &oldest_life, NULL);
|
||||||
|
iterate_cache(&kill_oldest_item, &oldest_life, NULL);
|
||||||
|
act_size = (size_t)(grad_cache_end - grad_cache_mem);
|
||||||
|
}
|
||||||
|
/*Ok, now we have space to allocate*/
|
||||||
|
}
|
||||||
|
lv_gradient_cache_t * item = (lv_gradient_cache_t *)grad_cache_end;
|
||||||
|
item->key = compute_key(g, size, w);
|
||||||
|
item->life = 1;
|
||||||
|
item->filled = 0;
|
||||||
|
item->size = w;
|
||||||
|
item->map = (lv_color_t *)(grad_cache_end + sizeof(*item));
|
||||||
|
#if _DITHER_GRADIENT
|
||||||
|
item->hmap = (lv_color32_t *)(grad_cache_end + sizeof(*item) + w * sizeof(lv_color_t));
|
||||||
|
item->hmap_size = size;
|
||||||
|
#if LV_DITHER_ERROR_DIFFUSION == 1
|
||||||
|
item->error_acc = (lv_scolor24_t *)(grad_cache_end + sizeof(*item) + size * sizeof(lv_grad_color_t) + w * sizeof(
|
||||||
|
lv_color_t));
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
grad_cache_end += req_size;
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**********************
|
||||||
|
* FUNCTIONS
|
||||||
|
**********************/
|
||||||
|
void lv_grad_free_cache()
|
||||||
|
{
|
||||||
|
lv_mem_free(grad_cache_mem);
|
||||||
|
grad_cache_mem = grad_cache_end = NULL;
|
||||||
|
grad_cache_size = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void lv_grad_set_cache_size(size_t max_bytes)
|
||||||
|
{
|
||||||
|
lv_mem_free(grad_cache_mem);
|
||||||
|
grad_cache_end = grad_cache_mem = lv_mem_alloc(max_bytes);
|
||||||
|
LV_ASSERT_MALLOC(grad_cache_mem);
|
||||||
|
grad_cache_size = max_bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
lv_gradient_cache_t * lv_grad_get_from_cache(const lv_gradient_t * g, lv_coord_t w, lv_coord_t h)
|
||||||
|
{
|
||||||
|
/* No gradient, no cache */
|
||||||
|
if(g->dir == LV_GRAD_DIR_NONE) return NULL;
|
||||||
|
|
||||||
|
/* Step 0: Check if the cache exist (else create it) */
|
||||||
|
if(!grad_cache_size) {
|
||||||
|
lv_grad_set_cache_size(LV_DEFAULT_GRAD_CACHE_SIZE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Step 1: Search cache for the given key */
|
||||||
|
lv_coord_t size = g->dir == LV_GRAD_DIR_HOR ? w : h;
|
||||||
|
uint32_t key = compute_key(g, size, w);
|
||||||
|
lv_gradient_cache_t * item = NULL;
|
||||||
|
if(iterate_cache(&find_item, &key, &item) == LV_RES_OK) {
|
||||||
|
item->life++; /* Don't forget to bump the counter */
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Step 2: Need to allocate an item for it */
|
||||||
|
item = allocate_item(g, w, h);
|
||||||
|
if(item == NULL) return item;
|
||||||
|
|
||||||
|
/* Step 3: Fill it with the gradient, as expected */
|
||||||
|
#if _DITHER_GRADIENT
|
||||||
|
for(lv_coord_t i = 0; i < item->hmap_size; i++) {
|
||||||
|
item->hmap[i] = lv_grad_get(g, item->hmap_size, i);
|
||||||
|
}
|
||||||
|
#if LV_DITHER_ERROR_DIFFUSION == 1
|
||||||
|
lv_memset_00(item->error_acc, w * sizeof(lv_scolor24_t));
|
||||||
|
#endif
|
||||||
|
#else
|
||||||
|
for(lv_coord_t i = 0; i < item->size; i++) {
|
||||||
|
item->map[i] = lv_grad_get(g, item->size, i);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void lv_grad_pop_from_cache(const lv_gradient_t * g, lv_coord_t w, lv_coord_t h)
|
||||||
|
{
|
||||||
|
lv_coord_t size = g->dir == LV_GRAD_DIR_HOR ? w : h;
|
||||||
|
uint32_t key = compute_key(g, size, w);
|
||||||
|
lv_gradient_cache_t * item = NULL;
|
||||||
|
if(iterate_cache(&find_item, &key, &item) == LV_RES_OK) {
|
||||||
|
free_item(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
LV_ATTRIBUTE_FAST_MEM lv_grad_color_t lv_grad_get(const lv_gradient_t * dsc, lv_coord_t range, lv_coord_t frac)
|
||||||
|
{
|
||||||
|
lv_grad_color_t tmp;
|
||||||
|
lv_color32_t one, two;
|
||||||
|
/*Clip out-of-bounds first*/
|
||||||
|
int32_t min = (dsc->stops[0].frac * range) >> 8;
|
||||||
|
if(frac <= min) {
|
||||||
|
GRAD_CONV(tmp, dsc->stops[0].color);
|
||||||
|
return tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t max = (dsc->stops[dsc->stops_count - 1].frac * range) >> 8;
|
||||||
|
if(frac >= max) {
|
||||||
|
GRAD_CONV(tmp, dsc->stops[dsc->stops_count - 1].color);
|
||||||
|
return tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*Find the 2 closest stop now*/
|
||||||
|
int32_t d = 0;
|
||||||
|
for(uint8_t i = 1; i < dsc->stops_count; i++) {
|
||||||
|
int32_t cur = (dsc->stops[i].frac * range) >> 8;
|
||||||
|
if(frac <= cur) {
|
||||||
|
one.full = lv_color_to32(dsc->stops[i - 1].color);
|
||||||
|
two.full = lv_color_to32(dsc->stops[i].color);
|
||||||
|
min = (dsc->stops[i - 1].frac * range) >> 8;
|
||||||
|
max = (dsc->stops[i].frac * range) >> 8;
|
||||||
|
d = max - min;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*Then interpolate*/
|
||||||
|
frac -= min;
|
||||||
|
lv_opa_t mix = (frac * 255) / d;
|
||||||
|
lv_opa_t imix = 255 - mix;
|
||||||
|
|
||||||
|
lv_grad_color_t r = GRAD_CM(LV_UDIV255(two.ch.red * mix + one.ch.red * imix),
|
||||||
|
LV_UDIV255(two.ch.green * mix + one.ch.green * imix),
|
||||||
|
LV_UDIV255(two.ch.blue * mix + one.ch.blue * imix));
|
||||||
|
return r;
|
||||||
|
}
|
||||||
91
src/draw/sw/lv_draw_sw_gradient.h
Normal file
91
src/draw/sw/lv_draw_sw_gradient.h
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
/**
|
||||||
|
* @file lv_draw_sw_gradient.h
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef LV_DRAW_SW_GRADIENT_H
|
||||||
|
#define LV_DRAW_SW_GRADIENT_H
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*********************
|
||||||
|
* INCLUDES
|
||||||
|
*********************/
|
||||||
|
#include "../../misc/lv_color.h"
|
||||||
|
#include "../../misc/lv_style.h"
|
||||||
|
#include "lv_draw_sw_dither.h"
|
||||||
|
|
||||||
|
/*********************
|
||||||
|
* DEFINES
|
||||||
|
*********************/
|
||||||
|
#if LV_GRADIENT_MAX_STOPS < 2
|
||||||
|
#error LVGL needs at least 2 stops for gradients. Please increase the LV_GRADIENT_MAX_STOPS
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
/**********************
|
||||||
|
* TYPEDEFS
|
||||||
|
**********************/
|
||||||
|
#if _DITHER_GRADIENT
|
||||||
|
typedef lv_color32_t lv_grad_color_t;
|
||||||
|
#else
|
||||||
|
typedef lv_color_t lv_grad_color_t;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/** To avoid recomputing gradient for each draw operation,
|
||||||
|
* it's possible to cache the computation in this structure instance.
|
||||||
|
* Whenever possible, this structure is reused instead of recomputing the gradient map */
|
||||||
|
typedef struct _lv_gradient_cache_t {
|
||||||
|
uint32_t key; /**< A discriminating key that's built from the drawing operation.
|
||||||
|
* If the key does not match, the cache item is not used */
|
||||||
|
uint32_t life : 31; /**< A life counter that's incremented on usage. Higher counter is
|
||||||
|
* less likely to be evicted from the cache */
|
||||||
|
uint32_t filled : 1; /**< Used to skip dithering in it if already done */
|
||||||
|
lv_color_t * map; /**< The computed gradient low bitdepth color map, points into the
|
||||||
|
* cache's buffer, no free needed */
|
||||||
|
lv_coord_t size; /**< The computed gradient color map size, in colors */
|
||||||
|
#if _DITHER_GRADIENT
|
||||||
|
lv_color32_t * hmap; /**< If dithering, we need to store the current, high bitdepth gradient
|
||||||
|
* map too, points to the cache's buffer, no free needed */
|
||||||
|
lv_coord_t hmap_size; /**< The array size in pixels */
|
||||||
|
#if LV_DITHER_ERROR_DIFFUSION == 1
|
||||||
|
lv_scolor24_t * error_acc; /**< Error diffusion dithering algorithm requires storing the last error
|
||||||
|
* drawn, points to the cache's buffer, no free needed */
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
} lv_gradient_cache_t;
|
||||||
|
|
||||||
|
|
||||||
|
/**********************
|
||||||
|
* PROTOTYPES
|
||||||
|
**********************/
|
||||||
|
/** Compute the color in the given gradient and fraction
|
||||||
|
* Gradient are specified in a virtual [0-255] range, so this function scales the virtual range to the given range
|
||||||
|
* @param dsc The gradient descriptor to use
|
||||||
|
* @param range The range to use in computation.
|
||||||
|
* @param frac The current part used in the range. frac is in [0; range]
|
||||||
|
*/
|
||||||
|
LV_ATTRIBUTE_FAST_MEM lv_grad_color_t lv_grad_get(const lv_gradient_t * dsc, lv_coord_t range, lv_coord_t frac);
|
||||||
|
|
||||||
|
/** Set the gradient cache size */
|
||||||
|
void lv_grad_set_cache_size(size_t max_bytes);
|
||||||
|
|
||||||
|
/** Get a gradient cache from the given parameters */
|
||||||
|
lv_gradient_cache_t * lv_grad_get_from_cache(const lv_gradient_t * gradient, lv_coord_t w, lv_coord_t h);
|
||||||
|
|
||||||
|
/** Evict item from the gradient cache (not used anymore).
|
||||||
|
* This bypass the life counter on the item to remove this item.
|
||||||
|
*/
|
||||||
|
void lv_grad_pop_from_cache(const lv_gradient_t * gradient, lv_coord_t w, lv_coord_t h);
|
||||||
|
|
||||||
|
/** Free the gradient cache */
|
||||||
|
void lv_grad_free_cache(void);
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
} /*extern "C"*/
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /*LV_DRAW_GRADIENT_H*/
|
||||||
@@ -11,14 +11,16 @@
|
|||||||
#include "../../misc/lv_txt_ap.h"
|
#include "../../misc/lv_txt_ap.h"
|
||||||
#include "../../core/lv_refr.h"
|
#include "../../core/lv_refr.h"
|
||||||
#include "../../misc/lv_assert.h"
|
#include "../../misc/lv_assert.h"
|
||||||
|
#include "lv_draw_sw_dither.h"
|
||||||
|
|
||||||
/*********************
|
/*********************
|
||||||
* DEFINES
|
* DEFINES
|
||||||
*********************/
|
*********************/
|
||||||
#define SHADOW_UPSCALE_SHIFT 6
|
#define SHADOW_UPSCALE_SHIFT 6
|
||||||
#define SHADOW_ENHANCE 1
|
#define SHADOW_ENHANCE 1
|
||||||
#define SPLIT_LIMIT 50
|
#define SPLIT_LIMIT 50
|
||||||
|
|
||||||
|
|
||||||
/**********************
|
/**********************
|
||||||
* TYPEDEFS
|
* TYPEDEFS
|
||||||
**********************/
|
**********************/
|
||||||
@@ -46,9 +48,6 @@ void draw_border_generic(lv_draw_ctx_t * draw_ctx, const lv_area_t * outer_area,
|
|||||||
static void draw_border_simple(lv_draw_ctx_t * draw_ctx, const lv_area_t * outer_area, const lv_area_t * inner_area,
|
static void draw_border_simple(lv_draw_ctx_t * draw_ctx, const lv_area_t * outer_area, const lv_area_t * inner_area,
|
||||||
lv_color_t color, lv_opa_t opa);
|
lv_color_t color, lv_opa_t opa);
|
||||||
|
|
||||||
#if LV_DRAW_COMPLEX
|
|
||||||
LV_ATTRIBUTE_FAST_MEM static inline lv_color_t grad_get(const lv_draw_rect_dsc_t * dsc, lv_coord_t s, lv_coord_t i);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/**********************
|
/**********************
|
||||||
* STATIC VARIABLES
|
* STATIC VARIABLES
|
||||||
@@ -105,8 +104,9 @@ static void draw_bg(lv_draw_ctx_t * draw_ctx, const lv_draw_rect_dsc_t * dsc, co
|
|||||||
lv_area_t clipped_coords;
|
lv_area_t clipped_coords;
|
||||||
if(!_lv_area_intersect(&clipped_coords, &bg_coords, draw_ctx->clip_area)) return;
|
if(!_lv_area_intersect(&clipped_coords, &bg_coords, draw_ctx->clip_area)) return;
|
||||||
|
|
||||||
lv_grad_dir_t grad_dir = dsc->bg_grad_dir;
|
lv_grad_dir_t grad_dir = dsc->bg_grad.dir;
|
||||||
if(dsc->bg_color.full == dsc->bg_grad_color.full) grad_dir = LV_GRAD_DIR_NONE;
|
lv_color_t bg_color = grad_dir == LV_GRAD_DIR_NONE ? dsc->bg_color : dsc->bg_grad.stops[0].color;
|
||||||
|
if(bg_color.full == dsc->bg_grad.stops[1].color.full) grad_dir = LV_GRAD_DIR_NONE;
|
||||||
|
|
||||||
bool mask_any = lv_draw_mask_is_any(&bg_coords);
|
bool mask_any = lv_draw_mask_is_any(&bg_coords);
|
||||||
|
|
||||||
@@ -115,7 +115,7 @@ static void draw_bg(lv_draw_ctx_t * draw_ctx, const lv_draw_rect_dsc_t * dsc, co
|
|||||||
lv_draw_sw_blend_dsc_t blend_dsc;
|
lv_draw_sw_blend_dsc_t blend_dsc;
|
||||||
lv_memset_00(&blend_dsc, sizeof(lv_draw_sw_blend_dsc_t));
|
lv_memset_00(&blend_dsc, sizeof(lv_draw_sw_blend_dsc_t));
|
||||||
blend_dsc.blend_mode = dsc->blend_mode;
|
blend_dsc.blend_mode = dsc->blend_mode;
|
||||||
blend_dsc.color = dsc->bg_color;
|
blend_dsc.color = bg_color;
|
||||||
blend_dsc.blend_area = &bg_coords;
|
blend_dsc.blend_area = &bg_coords;
|
||||||
blend_dsc.opa = dsc->bg_opa;
|
blend_dsc.opa = dsc->bg_opa;
|
||||||
|
|
||||||
@@ -155,25 +155,40 @@ static void draw_bg(lv_draw_ctx_t * draw_ctx, const lv_draw_rect_dsc_t * dsc, co
|
|||||||
lv_draw_sw_blend_dsc_t blend_dsc;
|
lv_draw_sw_blend_dsc_t blend_dsc;
|
||||||
lv_memset_00(&blend_dsc, sizeof(lv_draw_sw_blend_dsc_t));
|
lv_memset_00(&blend_dsc, sizeof(lv_draw_sw_blend_dsc_t));
|
||||||
blend_dsc.blend_mode = dsc->blend_mode;
|
blend_dsc.blend_mode = dsc->blend_mode;
|
||||||
blend_dsc.color = dsc->bg_color;
|
blend_dsc.color = dsc->bg_grad.stops[0].color;
|
||||||
blend_dsc.mask_buf = mask_buf;
|
blend_dsc.mask_buf = mask_buf;
|
||||||
blend_dsc.opa = LV_OPA_COVER;
|
blend_dsc.opa = LV_OPA_COVER;
|
||||||
blend_dsc.blend_area = &blend_area;
|
blend_dsc.blend_area = &blend_area;
|
||||||
blend_dsc.mask_area = &blend_area;
|
blend_dsc.mask_area = &blend_area;
|
||||||
|
|
||||||
/*In case of horizontal gradient pre-compute a line with a gradient*/
|
/*Get gradient if appropriate*/
|
||||||
lv_color_t * grad_map = NULL;
|
lv_gradient_cache_t * grad = lv_grad_get_from_cache(&dsc->bg_grad, coords_bg_w, coords_bg_h);
|
||||||
lv_color_t * grad_map_ofs = NULL;
|
if(grad && grad_dir == LV_GRAD_DIR_HOR) {
|
||||||
if(grad_dir == LV_GRAD_DIR_HOR) {
|
blend_dsc.src_buf = grad->map + clipped_coords.x1 - bg_coords.x1;
|
||||||
grad_map = lv_mem_buf_get(coords_bg_w * sizeof(lv_color_t));
|
|
||||||
int32_t i;
|
|
||||||
for(i = 0; i < coords_bg_w; i++) grad_map[i] = grad_get(dsc, coords_bg_w, i);
|
|
||||||
|
|
||||||
grad_map_ofs = grad_map;
|
|
||||||
if(dsc->bg_grad_dir == LV_GRAD_DIR_HOR) grad_map_ofs += clipped_coords.x1 - bg_coords.x1;
|
|
||||||
blend_dsc.src_buf = grad_map_ofs;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if _DITHER_GRADIENT
|
||||||
|
lv_dither_mode_t dither_mode = dsc->bg_grad.dither;
|
||||||
|
lv_dither_func_t dither_func = &lv_dither_none;
|
||||||
|
if(grad_dir == LV_GRAD_DIR_VER && dither_mode != LV_DITHER_NONE) {
|
||||||
|
/* When dithering, we are still using a map that's changing from line to line*/
|
||||||
|
blend_dsc.src_buf = grad->map;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(grad && dither_mode == LV_DITHER_NONE) {
|
||||||
|
grad->filled = 0; /*Should we force refilling it each draw call ?*/
|
||||||
|
}
|
||||||
|
else
|
||||||
|
#if LV_DITHER_ERROR_DIFFUSION
|
||||||
|
if(dither_mode == LV_DITHER_ORDERED)
|
||||||
|
#endif
|
||||||
|
dither_func = grad_dir == LV_GRAD_DIR_HOR ? &lv_dither_ordered_hor : &lv_dither_ordered_ver;
|
||||||
|
#if LV_DITHER_ERROR_DIFFUSION
|
||||||
|
else if(dither_mode == LV_DITHER_ERR_DIFF)
|
||||||
|
dither_func = grad_dir == LV_GRAD_DIR_HOR ? &lv_dither_err_diff_hor : &lv_dither_err_diff_ver;
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
/*There is another mask too. Draw line by line. */
|
/*There is another mask too. Draw line by line. */
|
||||||
if(mask_any) {
|
if(mask_any) {
|
||||||
for(h = clipped_coords.y1; h <= clipped_coords.y2; h++) {
|
for(h = clipped_coords.y1; h <= clipped_coords.y2; h++) {
|
||||||
@@ -186,8 +201,10 @@ static void draw_bg(lv_draw_ctx_t * draw_ctx, const lv_draw_rect_dsc_t * dsc, co
|
|||||||
blend_dsc.mask_res = lv_draw_mask_apply(mask_buf, clipped_coords.x1, h, clipped_w);
|
blend_dsc.mask_res = lv_draw_mask_apply(mask_buf, clipped_coords.x1, h, clipped_w);
|
||||||
if(blend_dsc.mask_res == LV_DRAW_MASK_RES_FULL_COVER) blend_dsc.mask_res = LV_DRAW_MASK_RES_CHANGED;
|
if(blend_dsc.mask_res == LV_DRAW_MASK_RES_FULL_COVER) blend_dsc.mask_res = LV_DRAW_MASK_RES_CHANGED;
|
||||||
|
|
||||||
if(grad_dir == LV_GRAD_DIR_VER) blend_dsc.color = grad_get(dsc, coords_bg_h, h - bg_coords.y1);
|
#if _DITHER_GRADIENT
|
||||||
|
dither_func(grad, blend_area.x1, h - bg_coords.y1, coords_bg_w);
|
||||||
|
#endif
|
||||||
|
if(grad_dir == LV_GRAD_DIR_VER) blend_dsc.color = grad->map[h - bg_coords.y1];
|
||||||
lv_draw_sw_blend(draw_ctx, &blend_dsc);
|
lv_draw_sw_blend(draw_ctx, &blend_dsc);
|
||||||
}
|
}
|
||||||
goto bg_clean_up;
|
goto bg_clean_up;
|
||||||
@@ -210,8 +227,10 @@ static void draw_bg(lv_draw_ctx_t * draw_ctx, const lv_draw_rect_dsc_t * dsc, co
|
|||||||
blend_area.y1 = top_y;
|
blend_area.y1 = top_y;
|
||||||
blend_area.y2 = top_y;
|
blend_area.y2 = top_y;
|
||||||
|
|
||||||
if(grad_dir == LV_GRAD_DIR_VER) blend_dsc.color = grad_get(dsc, coords_bg_h, top_y - bg_coords.y1);
|
#if _DITHER_GRADIENT
|
||||||
|
dither_func(grad, blend_area.x1, top_y - bg_coords.y1, coords_bg_w);
|
||||||
|
#endif
|
||||||
|
if(grad_dir == LV_GRAD_DIR_VER) blend_dsc.color = grad->map[top_y - bg_coords.y1];
|
||||||
lv_draw_sw_blend(draw_ctx, &blend_dsc);
|
lv_draw_sw_blend(draw_ctx, &blend_dsc);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -219,8 +238,10 @@ static void draw_bg(lv_draw_ctx_t * draw_ctx, const lv_draw_rect_dsc_t * dsc, co
|
|||||||
blend_area.y1 = bottom_y;
|
blend_area.y1 = bottom_y;
|
||||||
blend_area.y2 = bottom_y;
|
blend_area.y2 = bottom_y;
|
||||||
|
|
||||||
if(grad_dir == LV_GRAD_DIR_VER) blend_dsc.color = grad_get(dsc, coords_bg_h, bottom_y - bg_coords.y1);
|
#if _DITHER_GRADIENT
|
||||||
|
dither_func(grad, blend_area.x1, bottom_y - bg_coords.y1, coords_bg_w);
|
||||||
|
#endif
|
||||||
|
if(grad_dir == LV_GRAD_DIR_VER) blend_dsc.color = grad->map[bottom_y - bg_coords.y1];
|
||||||
lv_draw_sw_blend(draw_ctx, &blend_dsc);
|
lv_draw_sw_blend(draw_ctx, &blend_dsc);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -256,15 +277,16 @@ static void draw_bg(lv_draw_ctx_t * draw_ctx, const lv_draw_rect_dsc_t * dsc, co
|
|||||||
blend_area.y1 = h;
|
blend_area.y1 = h;
|
||||||
blend_area.y2 = h;
|
blend_area.y2 = h;
|
||||||
|
|
||||||
if(grad_dir == LV_GRAD_DIR_VER) blend_dsc.color = grad_get(dsc, coords_bg_h, h - bg_coords.y1);
|
#if _DITHER_GRADIENT
|
||||||
|
dither_func(grad, blend_area.x1, h - bg_coords.y1, coords_bg_w);
|
||||||
|
#endif
|
||||||
|
if(grad_dir == LV_GRAD_DIR_VER) blend_dsc.color = grad->map[h - bg_coords.y1];
|
||||||
lv_draw_sw_blend(draw_ctx, &blend_dsc);
|
lv_draw_sw_blend(draw_ctx, &blend_dsc);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bg_clean_up:
|
bg_clean_up:
|
||||||
if(grad_map) lv_mem_buf_release(grad_map);
|
|
||||||
if(mask_buf) lv_mem_buf_release(mask_buf);
|
if(mask_buf) lv_mem_buf_release(mask_buf);
|
||||||
if(mask_rout_id != LV_MASK_ID_INV) {
|
if(mask_rout_id != LV_MASK_ID_INV) {
|
||||||
lv_draw_mask_remove_id(mask_rout_id);
|
lv_draw_mask_remove_id(mask_rout_id);
|
||||||
@@ -366,23 +388,7 @@ static void draw_border(lv_draw_ctx_t * draw_ctx, const lv_draw_rect_dsc_t * dsc
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#if LV_DRAW_COMPLEX
|
#if LV_DRAW_COMPLEX
|
||||||
LV_ATTRIBUTE_FAST_MEM static inline lv_color_t grad_get(const lv_draw_rect_dsc_t * dsc, lv_coord_t s, lv_coord_t i)
|
|
||||||
{
|
|
||||||
int32_t min = (dsc->bg_main_color_stop * s) >> 8;
|
|
||||||
if(i <= min) return dsc->bg_color;
|
|
||||||
|
|
||||||
int32_t max = (dsc->bg_grad_color_stop * s) >> 8;
|
|
||||||
if(i >= max) return dsc->bg_grad_color;
|
|
||||||
|
|
||||||
int32_t d = dsc->bg_grad_color_stop - dsc->bg_main_color_stop;
|
|
||||||
d = (s * d) >> 8;
|
|
||||||
i -= min;
|
|
||||||
lv_opa_t mix = (i * 255) / d;
|
|
||||||
return lv_color_mix(dsc->bg_grad_color, dsc->bg_color, mix);
|
|
||||||
}
|
|
||||||
|
|
||||||
LV_ATTRIBUTE_FAST_MEM static void draw_shadow(lv_draw_ctx_t * draw_ctx, const lv_draw_rect_dsc_t * dsc,
|
LV_ATTRIBUTE_FAST_MEM static void draw_shadow(lv_draw_ctx_t * draw_ctx, const lv_draw_rect_dsc_t * dsc,
|
||||||
const lv_area_t * coords)
|
const lv_area_t * coords)
|
||||||
{
|
{
|
||||||
@@ -432,6 +438,7 @@ LV_ATTRIBUTE_FAST_MEM static void draw_shadow(lv_draw_ctx_t * draw_ctx, const lv
|
|||||||
short_side = LV_MIN(lv_area_get_width(&core_area), lv_area_get_height(&core_area));
|
short_side = LV_MIN(lv_area_get_width(&core_area), lv_area_get_height(&core_area));
|
||||||
if(r_sh > short_side >> 1) r_sh = short_side >> 1;
|
if(r_sh > short_side >> 1) r_sh = short_side >> 1;
|
||||||
|
|
||||||
|
|
||||||
/*Get how many pixels are affected by the blur on the corners*/
|
/*Get how many pixels are affected by the blur on the corners*/
|
||||||
int32_t corner_size = dsc->shadow_width + r_sh;
|
int32_t corner_size = dsc->shadow_width + r_sh;
|
||||||
|
|
||||||
@@ -1061,7 +1068,6 @@ LV_ATTRIBUTE_FAST_MEM static void shadow_blur_corner(lv_coord_t size, lv_coord_t
|
|||||||
|
|
||||||
lv_mem_buf_release(sh_ups_blur_buf);
|
lv_mem_buf_release(sh_ups_blur_buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static void draw_outline(lv_draw_ctx_t * draw_ctx, const lv_draw_rect_dsc_t * dsc, const lv_area_t * coords)
|
static void draw_outline(lv_draw_ctx_t * draw_ctx, const lv_draw_rect_dsc_t * dsc, const lv_area_t * coords)
|
||||||
|
|||||||
@@ -1202,7 +1202,7 @@ static void draw_series_bar(lv_obj_t * obj, lv_draw_ctx_t * draw_ctx)
|
|||||||
lv_draw_rect_dsc_t col_dsc;
|
lv_draw_rect_dsc_t col_dsc;
|
||||||
lv_draw_rect_dsc_init(&col_dsc);
|
lv_draw_rect_dsc_init(&col_dsc);
|
||||||
lv_obj_init_draw_rect_dsc(obj, LV_PART_ITEMS, &col_dsc);
|
lv_obj_init_draw_rect_dsc(obj, LV_PART_ITEMS, &col_dsc);
|
||||||
col_dsc.bg_grad_dir = LV_GRAD_DIR_NONE;
|
col_dsc.bg_grad.dir = LV_GRAD_DIR_NONE;
|
||||||
col_dsc.bg_opa = LV_OPA_COVER;
|
col_dsc.bg_opa = LV_OPA_COVER;
|
||||||
|
|
||||||
/*Make the cols longer with `radius` to clip the rounding from the bottom*/
|
/*Make the cols longer with `radius` to clip the rounding from the bottom*/
|
||||||
|
|||||||
@@ -178,15 +178,17 @@ static void lv_led_event(const lv_obj_class_t * class_p, lv_event_t * e)
|
|||||||
lv_obj_init_draw_rect_dsc(obj, LV_PART_MAIN, &rect_dsc);
|
lv_obj_init_draw_rect_dsc(obj, LV_PART_MAIN, &rect_dsc);
|
||||||
|
|
||||||
/*Use the original colors brightness to modify color->led*/
|
/*Use the original colors brightness to modify color->led*/
|
||||||
rect_dsc.bg_color = lv_color_mix(led->color, lv_color_black(), lv_color_brightness(rect_dsc.bg_color));
|
rect_dsc.bg_grad.stops[0].color = lv_color_mix(led->color, lv_color_black(),
|
||||||
rect_dsc.bg_grad_color = lv_color_mix(led->color, lv_color_black(), lv_color_brightness(rect_dsc.bg_grad_color));
|
lv_color_brightness(rect_dsc.bg_grad.stops[0].color));
|
||||||
|
rect_dsc.bg_grad.stops[1].color = lv_color_mix(led->color, lv_color_black(),
|
||||||
|
lv_color_brightness(rect_dsc.bg_grad.stops[1].color));
|
||||||
rect_dsc.shadow_color = lv_color_mix(led->color, lv_color_black(), lv_color_brightness(rect_dsc.shadow_color));
|
rect_dsc.shadow_color = lv_color_mix(led->color, lv_color_black(), lv_color_brightness(rect_dsc.shadow_color));
|
||||||
rect_dsc.border_color = lv_color_mix(led->color, lv_color_black(), lv_color_brightness(rect_dsc.border_color));
|
rect_dsc.border_color = lv_color_mix(led->color, lv_color_black(), lv_color_brightness(rect_dsc.border_color));
|
||||||
rect_dsc.outline_color = lv_color_mix(led->color, lv_color_black(), lv_color_brightness(rect_dsc.outline_color));
|
rect_dsc.outline_color = lv_color_mix(led->color, lv_color_black(), lv_color_brightness(rect_dsc.outline_color));
|
||||||
|
|
||||||
/*Mix. the color with black proportionally with brightness*/
|
/*Mix. the color with black proportionally with brightness*/
|
||||||
rect_dsc.bg_color = lv_color_mix(rect_dsc.bg_color, lv_color_black(), led->bright);
|
rect_dsc.bg_grad.stops[0].color = lv_color_mix(rect_dsc.bg_grad.stops[0].color, lv_color_black(), led->bright);
|
||||||
rect_dsc.bg_grad_color = lv_color_mix(rect_dsc.bg_grad_color, lv_color_black(), led->bright);
|
rect_dsc.bg_grad.stops[1].color = lv_color_mix(rect_dsc.bg_grad.stops[1].color, lv_color_black(), led->bright);
|
||||||
rect_dsc.border_color = lv_color_mix(rect_dsc.border_color, lv_color_black(), led->bright);
|
rect_dsc.border_color = lv_color_mix(rect_dsc.border_color, lv_color_black(), led->bright);
|
||||||
rect_dsc.shadow_color = lv_color_mix(rect_dsc.shadow_color, lv_color_black(), led->bright);
|
rect_dsc.shadow_color = lv_color_mix(rect_dsc.shadow_color, lv_color_black(), led->bright);
|
||||||
rect_dsc.outline_color = lv_color_mix(rect_dsc.outline_color, lv_color_black(), led->bright);
|
rect_dsc.outline_color = lv_color_mix(rect_dsc.outline_color, lv_color_black(), led->bright);
|
||||||
|
|||||||
@@ -294,6 +294,46 @@
|
|||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/*Allow dithering gradient (to achieve visual smooth color gradients on limited color depth display)
|
||||||
|
*LV_DITHER_GRADIENT implies allocating one or two more lines of the object's rendering surface
|
||||||
|
*The increase in memory consumption is (32 bits * object width) plus 24 bits * object width if using error diffusion */
|
||||||
|
#ifndef LV_DITHER_GRADIENT
|
||||||
|
#ifdef _LV_KCONFIG_PRESENT
|
||||||
|
#ifdef CONFIG_LV_DITHER_GRADIENT
|
||||||
|
#define LV_DITHER_GRADIENT CONFIG_LV_DITHER_GRADIENT
|
||||||
|
#else
|
||||||
|
#define LV_DITHER_GRADIENT 0
|
||||||
|
#endif
|
||||||
|
#else
|
||||||
|
#define LV_DITHER_GRADIENT 1
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*Add support for error diffusion dithering.
|
||||||
|
*Error diffusion dithering gets a much better visual result, but implies more CPU consumption and memory when drawing.
|
||||||
|
*The increase in memory consumption is (24 bits * object's width)*/
|
||||||
|
#ifndef LV_DITHER_ERROR_DIFFUSION
|
||||||
|
#ifdef _LV_KCONFIG_PRESENT
|
||||||
|
#ifdef CONFIG_LV_DITHER_ERROR_DIFFUSION
|
||||||
|
#define LV_DITHER_ERROR_DIFFUSION CONFIG_LV_DITHER_ERROR_DIFFUSION
|
||||||
|
#else
|
||||||
|
#define LV_DITHER_ERROR_DIFFUSION 0
|
||||||
|
#endif
|
||||||
|
#else
|
||||||
|
#define LV_DITHER_ERROR_DIFFUSION 1
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**Number of stops allowed per gradient. Increase this to allow more stops.
|
||||||
|
*This adds (sizeof(lv_color_t) + 1) bytes per additional stop*/
|
||||||
|
#ifndef LV_GRADIENT_MAX_STOPS
|
||||||
|
#ifdef CONFIG_LV_GRADIENT_MAX_STOPS
|
||||||
|
#define LV_GRADIENT_MAX_STOPS CONFIG_LV_GRADIENT_MAX_STOPS
|
||||||
|
#else
|
||||||
|
#define LV_GRADIENT_MAX_STOPS 2
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
#endif /*LV_DRAW_COMPLEX*/
|
#endif /*LV_DRAW_COMPLEX*/
|
||||||
|
|
||||||
/*Default image cache size. Image caching keeps the images opened.
|
/*Default image cache size. Image caching keeps the images opened.
|
||||||
|
|||||||
@@ -51,6 +51,12 @@ LV_EXPORT_CONST_INT(LV_IMG_ZOOM_NONE);
|
|||||||
#define LV_STYLE_CONST_INIT(var_name, prop_array) const lv_style_t var_name = { .v_p = { .const_props = prop_array }, .has_group = 0xFF, .is_const = 1 }
|
#define LV_STYLE_CONST_INIT(var_name, prop_array) const lv_style_t var_name = { .v_p = { .const_props = prop_array }, .has_group = 0xFF, .is_const = 1 }
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/** On simple system, don't waste resources on gradients */
|
||||||
|
#if !defined(LV_DRAW_COMPLEX) || !defined(LV_GRADIENT_MAX_STOPS)
|
||||||
|
#define LV_GRADIENT_MAX_STOPS 2
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
/**********************
|
/**********************
|
||||||
* TYPEDEFS
|
* TYPEDEFS
|
||||||
**********************/
|
**********************/
|
||||||
@@ -106,6 +112,36 @@ enum {
|
|||||||
|
|
||||||
typedef uint8_t lv_grad_dir_t;
|
typedef uint8_t lv_grad_dir_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The dithering algorithm for the gradient
|
||||||
|
* Depends on LV_DITHER_GRADIENT
|
||||||
|
*/
|
||||||
|
enum {
|
||||||
|
LV_DITHER_NONE, /**< No dithering, colors are just quantized to the output resolution*/
|
||||||
|
LV_DITHER_ORDERED, /**< Ordered dithering. Faster to compute and use less memory but lower quality*/
|
||||||
|
LV_DITHER_ERR_DIFF, /**< Error diffusion mode. Slower to compute and use more memory but give highest dither quality*/
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef uint8_t lv_dither_mode_t;
|
||||||
|
|
||||||
|
/** A gradient stop definition.
|
||||||
|
* This matches a color and a position in a virtual 0-255 scale.
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
lv_color_t color; /**< The stop color */
|
||||||
|
uint8_t frac; /**< The stop position in 1/255 unit */
|
||||||
|
} lv_gradient_stop_t;
|
||||||
|
|
||||||
|
/** A descriptor of a gradient. */
|
||||||
|
typedef struct {
|
||||||
|
lv_gradient_stop_t stops[LV_GRADIENT_MAX_STOPS]; /**< A gradient stop array */
|
||||||
|
uint8_t stops_count; /**< The number of used stops in the array */
|
||||||
|
lv_grad_dir_t dir : 3; /**< The gradient direction.
|
||||||
|
* Any of LV_GRAD_DIR_HOR, LV_GRAD_DIR_VER, LV_GRAD_DIR_NONE */
|
||||||
|
lv_dither_mode_t dither : 3; /**< Whether to dither the gradient or not.
|
||||||
|
* Any of LV_DITHER_NONE, LV_DITHER_ORDERED, LV_DITHER_ERR_DIFF */
|
||||||
|
} lv_gradient_t;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A common type to handle all the property types in the same way.
|
* A common type to handle all the property types in the same way.
|
||||||
*/
|
*/
|
||||||
@@ -155,13 +191,16 @@ typedef enum {
|
|||||||
LV_STYLE_BG_GRAD_DIR = 35,
|
LV_STYLE_BG_GRAD_DIR = 35,
|
||||||
LV_STYLE_BG_MAIN_STOP = 36,
|
LV_STYLE_BG_MAIN_STOP = 36,
|
||||||
LV_STYLE_BG_GRAD_STOP = 37,
|
LV_STYLE_BG_GRAD_STOP = 37,
|
||||||
|
LV_STYLE_BG_GRADIENT = 38,
|
||||||
|
LV_STYLE_BG_DITHER_MODE = 39,
|
||||||
|
|
||||||
LV_STYLE_BG_IMG_SRC = 38 | LV_STYLE_PROP_EXT_DRAW,
|
|
||||||
LV_STYLE_BG_IMG_OPA = 39,
|
LV_STYLE_BG_IMG_SRC = 40 | LV_STYLE_PROP_EXT_DRAW,
|
||||||
LV_STYLE_BG_IMG_RECOLOR = 40,
|
LV_STYLE_BG_IMG_OPA = 41,
|
||||||
LV_STYLE_BG_IMG_RECOLOR_FILTERED = 40 | LV_STYLE_PROP_FILTER,
|
LV_STYLE_BG_IMG_RECOLOR = 42,
|
||||||
LV_STYLE_BG_IMG_RECOLOR_OPA = 41,
|
LV_STYLE_BG_IMG_RECOLOR_FILTERED = 42 | LV_STYLE_PROP_FILTER,
|
||||||
LV_STYLE_BG_IMG_TILED = 42,
|
LV_STYLE_BG_IMG_RECOLOR_OPA = 43,
|
||||||
|
LV_STYLE_BG_IMG_TILED = 44,
|
||||||
|
|
||||||
/*Group 3*/
|
/*Group 3*/
|
||||||
LV_STYLE_BORDER_COLOR = 48,
|
LV_STYLE_BORDER_COLOR = 48,
|
||||||
|
|||||||
@@ -232,6 +232,22 @@ void lv_style_set_bg_grad_stop(lv_style_t * style, lv_coord_t value)
|
|||||||
lv_style_set_prop(style, LV_STYLE_BG_GRAD_STOP, v);
|
lv_style_set_prop(style, LV_STYLE_BG_GRAD_STOP, v);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void lv_style_set_bg_gradient(lv_style_t * style, const lv_gradient_t * value)
|
||||||
|
{
|
||||||
|
lv_style_value_t v = {
|
||||||
|
.ptr = value
|
||||||
|
};
|
||||||
|
lv_style_set_prop(style, LV_STYLE_BG_GRADIENT, v);
|
||||||
|
}
|
||||||
|
|
||||||
|
void lv_style_set_bg_dither_mode(lv_style_t * style, lv_dither_mode_t value)
|
||||||
|
{
|
||||||
|
lv_style_value_t v = {
|
||||||
|
.num = (int32_t)value
|
||||||
|
};
|
||||||
|
lv_style_set_prop(style, LV_STYLE_BG_DITHER_MODE, v);
|
||||||
|
}
|
||||||
|
|
||||||
void lv_style_set_bg_img_src(lv_style_t * style, const void * value)
|
void lv_style_set_bg_img_src(lv_style_t * style, const void * value)
|
||||||
{
|
{
|
||||||
lv_style_value_t v = {
|
lv_style_value_t v = {
|
||||||
|
|||||||
@@ -27,6 +27,8 @@ void lv_style_set_bg_grad_color_filtered(lv_style_t * style, lv_color_t value);
|
|||||||
void lv_style_set_bg_grad_dir(lv_style_t * style, lv_grad_dir_t value);
|
void lv_style_set_bg_grad_dir(lv_style_t * style, lv_grad_dir_t value);
|
||||||
void lv_style_set_bg_main_stop(lv_style_t * style, lv_coord_t value);
|
void lv_style_set_bg_main_stop(lv_style_t * style, lv_coord_t value);
|
||||||
void lv_style_set_bg_grad_stop(lv_style_t * style, lv_coord_t value);
|
void lv_style_set_bg_grad_stop(lv_style_t * style, lv_coord_t value);
|
||||||
|
void lv_style_set_bg_gradient(lv_style_t * style, const lv_gradient_t * value);
|
||||||
|
void lv_style_set_bg_dither_mode(lv_style_t * style, lv_dither_mode_t value);
|
||||||
void lv_style_set_bg_img_src(lv_style_t * style, const void * value);
|
void lv_style_set_bg_img_src(lv_style_t * style, const void * value);
|
||||||
void lv_style_set_bg_img_opa(lv_style_t * style, lv_opa_t value);
|
void lv_style_set_bg_img_opa(lv_style_t * style, lv_opa_t value);
|
||||||
void lv_style_set_bg_img_recolor(lv_style_t * style, lv_color_t value);
|
void lv_style_set_bg_img_recolor(lv_style_t * style, lv_color_t value);
|
||||||
@@ -233,6 +235,16 @@ void lv_style_set_base_dir(lv_style_t * style, lv_base_dir_t value);
|
|||||||
.prop = LV_STYLE_BG_GRAD_STOP, .value = { .num = (int32_t)val } \
|
.prop = LV_STYLE_BG_GRAD_STOP, .value = { .num = (int32_t)val } \
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define LV_STYLE_CONST_BG_GRADIENT(val) \
|
||||||
|
{ \
|
||||||
|
.prop = LV_STYLE_BG_GRADIENT, .value = { .ptr = val } \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define LV_STYLE_CONST_BG_DITHER_MODE(val) \
|
||||||
|
{ \
|
||||||
|
.prop = LV_STYLE_BG_DITHER_MODE, .value = { .num = (int32_t)val } \
|
||||||
|
}
|
||||||
|
|
||||||
#define LV_STYLE_CONST_BG_IMG_SRC(val) \
|
#define LV_STYLE_CONST_BG_IMG_SRC(val) \
|
||||||
{ \
|
{ \
|
||||||
.prop = LV_STYLE_BG_IMG_SRC, .value = { .ptr = val } \
|
.prop = LV_STYLE_BG_IMG_SRC, .value = { .ptr = val } \
|
||||||
|
|||||||
Reference in New Issue
Block a user