1
0
mirror of https://github.com/lvgl/lvgl.git synced 2025-01-21 06:53:01 +08:00

add 2d blending

This commit is contained in:
Gabor Kiss-Vamosi 2019-08-23 10:53:38 +02:00
parent 9b5e84eaca
commit 85507c4af4
6 changed files with 240 additions and 84 deletions

View File

@ -170,7 +170,7 @@ void lv_style_init(void)
lv_style_btn_rel.body.padding.bottom = LV_DPI / 6;
lv_style_btn_rel.body.padding.inner = LV_DPI / 10;
lv_style_btn_rel.body.border.color = lv_color_make(0x0b, 0x19, 0x28);
lv_style_btn_rel.body.border.width = 20;//LV_DPI / 50 >= 1 ? LV_DPI / 50 : 1;
lv_style_btn_rel.body.border.width = LV_DPI / 50 >= 1 ? LV_DPI / 50 : 1;
lv_style_btn_rel.body.border.opa = LV_OPA_70;
lv_style_btn_rel.body.shadow.color = LV_COLOR_GRAY;
lv_style_btn_rel.body.shadow.width = 0;

View File

@ -32,6 +32,133 @@
* GLOBAL FUNCTIONS
**********************/
/**
*
* @param disp_area
* @param clip_area already truncated to disp_arae
* @param fill_area
* @param disp_buf
* @param cf
* @param color
* @param mask
* @param mask_res
* @param opa
* @param mode
*/
void lv_blend_fill(const lv_area_t * disp_area, const lv_area_t * clip_area, const lv_area_t * fill_area,
lv_color_t * disp_buf, lv_img_cf_t cf, lv_color_t color,
lv_opa_t * mask, lv_mask_res_t mask_res, lv_opa_t opa, lv_blend_mode_t mode)
{
/*Do not draw transparent things*/
if(opa < LV_OPA_MIN) return;
if(mask_res == LV_MASK_RES_FULL_TRANSP) return;
/* Get clipped fill area which is the real draw area.
* It is always the same or inside `fill_area` */
lv_area_t draw_area;
bool is_common;
is_common = lv_area_intersect(&draw_area, clip_area, fill_area);
if(!is_common) return;
/* Now `draw_area` has absolute coordinates.
* Make it relative to `disp_area` to simplify draw to `disp_buf`*/
draw_area.x1 -= disp_area->x1;
draw_area.y1 -= disp_area->y1;
draw_area.x2 -= disp_area->x1;
draw_area.y2 -= disp_area->y1;
/*Get the width of the `disp_area` it will be used to go to the next line*/
lv_coord_t disp_w = lv_area_get_width(disp_area);
/*Create a temp. disp_buf which always point to current line to draw*/
lv_color_t * disp_buf_tmp = disp_buf + disp_w * draw_area.y1;
lv_coord_t x;
lv_coord_t y;
/*Simple fill (maybe with opacity), no masking*/
if(mask_res == LV_MASK_RES_FULL_COVER) {
if(opa > LV_OPA_MAX) {
for(y = draw_area.y1; y <= draw_area.y2; y++) {
for(x = draw_area.x1; x <= draw_area.x2; x++) {
disp_buf_tmp[x].full = color.full;
}
disp_buf_tmp += disp_w;
}
}
else {
for(y = draw_area.y1; y <= draw_area.y2; y++) {
for(x = draw_area.x1; x <= draw_area.x2; x++) {
disp_buf_tmp[x] = lv_color_mix(color, disp_buf[x], opa);
}
disp_buf_tmp += disp_w;
}
}
}
/*Masked*/
else {
/*Get the width of the `fill_area` it will be used to go to the next line of the mask*/
lv_coord_t fill_w = lv_area_get_width(fill_area);
/* The mask is relative to the original `fill_area`.
* If some lines and columns are clipped move on the mask accordingly.*/
lv_opa_t * mask_tmp = mask + fill_w * (draw_area.y1 - fill_area->y1 + disp_area->y1);
mask_tmp -= draw_area.x1;
/*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 = disp_buf_tmp[0].full;
last_res_color.full = disp_buf_tmp[0].full;
/*Only the mask matters*/
if(opa > LV_OPA_MAX) {
for(y = draw_area.y1; y <= draw_area.y2; y++) {
for(x = draw_area.x1; x <= draw_area.x2; x++) {
if(mask_tmp[x] != last_mask || last_dest_color.full != disp_buf_tmp[x].full) {
if(mask_tmp[x] > LV_OPA_MAX) last_res_color = color;
else if(mask_tmp[x] < LV_OPA_MIN) last_res_color = disp_buf_tmp[x];
else last_res_color = lv_color_mix(color, disp_buf_tmp[x], mask_tmp[x]);
last_mask = mask_tmp[x];
last_dest_color.full = disp_buf_tmp[x].full;
}
disp_buf_tmp[x] = last_res_color;
}
disp_buf_tmp += disp_w;
mask_tmp += fill_w;
}
}
/*Handle opa and mask values too*/
else {
for(y = draw_area.y1; y <= draw_area.y2; y++) {
for(x = draw_area.x1; x <= draw_area.x2; x++) {
if(mask[x] != last_mask || last_dest_color.full != disp_buf_tmp[x].full) {
lv_opa_t opa_tmp = (uint16_t)((uint16_t)mask_tmp[x] * opa) >> 8;
if(opa_tmp > LV_OPA_MAX) last_res_color = color;
else if(opa_tmp < LV_OPA_MIN) last_res_color = disp_buf_tmp[x];
else last_res_color = lv_color_mix(color, disp_buf_tmp[x],opa_tmp);
last_mask = mask[x];
last_dest_color.full = disp_buf_tmp[x].full;
}
disp_buf_tmp[x] = last_res_color;
}
disp_buf_tmp += disp_w;
mask_tmp += fill_w;
}
}
}
}
void lv_blend_color(lv_color_t * dest_buf, lv_img_cf_t dest_cf, lv_coord_t len,
lv_color_t color, lv_img_cf_t src_cf,
lv_opa_t * mask, lv_mask_res_t mask_res, lv_opa_t opa, lv_blend_mode_t mode)
@ -65,7 +192,7 @@ void lv_blend_color(lv_color_t * dest_buf, lv_img_cf_t dest_cf, lv_coord_t len,
last_res_color.full = dest_buf[0].full;
for(i = 0; i < len; i++) {
// if(mask[i] == 0) continue;
// if(mask[i] == 0) continue;
if(mask[i] != last_mask || last_dest_color.full != dest_buf[i].full) {
if(mask[i] > LV_OPA_MAX) last_res_color = color;
@ -83,7 +210,7 @@ void lv_blend_color(lv_color_t * dest_buf, lv_img_cf_t dest_cf, lv_coord_t len,
last_dest_color.full = dest_buf[0].full;
last_res_color.full = dest_buf[0].full;
for(i = 0; i < len; i++) {
// if(mask[i] == 0) continue;
// if(mask[i] == 0) continue;
if(mask[i] != last_mask || last_dest_color.full != dest_buf[i].full) {
lv_opa_t tmp = (uint16_t)((uint16_t)mask[i] * opa) >> 8;
@ -94,7 +221,7 @@ void lv_blend_color(lv_color_t * dest_buf, lv_img_cf_t dest_cf, lv_coord_t len,
last_dest_color.full = dest_buf[i].full;
}
dest_buf[i] = last_res_color;
//
//
}
}
}

View File

@ -34,6 +34,11 @@ typedef enum {
* GLOBAL PROTOTYPES
**********************/
void lv_blend_fill(const lv_area_t * disp_area, const lv_area_t * clip_area, const lv_area_t * fill_area,
lv_color_t * disp_buf, lv_img_cf_t cf, lv_color_t color,
lv_opa_t * mask, lv_mask_res_t mask_res, lv_opa_t opa, lv_blend_mode_t mode);
void lv_blend_color(lv_color_t * dest_buf, lv_img_cf_t dest_cf, lv_coord_t len,
lv_color_t color, lv_img_cf_t src_cf,
lv_opa_t * mask, lv_mask_res_t mask_res, lv_opa_t opa, lv_blend_mode_t mode);

View File

@ -65,46 +65,59 @@ static void draw_bg(const lv_area_t * coords, const lv_area_t * clip, const lv_s
if(opa > LV_OPA_MAX) opa = LV_OPA_COVER;
lv_area_t draw_a;
bool union_ok;
/* Get the union of `coords` and `clip`*/
/* `clip` is already truncated to the `vdb` size
* in 'lv_refr_area' function */
union_ok = lv_area_intersect(&draw_a, coords, clip);
/*If there are common part of `clip` and `vdb` then draw*/
if(union_ok == false) return;
lv_disp_t * disp = lv_refr_get_disp_refreshing();
lv_disp_buf_t * vdb = lv_disp_get_buf(disp);
/*Store the coordinates of the `draw_a` relative to the VDB */
lv_area_t draw_rel_a;
draw_rel_a.x1 = draw_a.x1 - vdb->area.x1;
draw_rel_a.y1 = draw_a.y1 - vdb->area.y1;
draw_rel_a.x2 = draw_a.x2 - vdb->area.x1;
draw_rel_a.y2 = draw_a.y2 - vdb->area.y1;
lv_coord_t vdb_width = lv_area_get_width(&vdb->area);
lv_coord_t draw_a_width = lv_area_get_width(&draw_rel_a);
/* Get clipped fill area which is the real draw area.
* It is always the same or inside `fill_area` */
lv_area_t draw_area;
bool is_common;
is_common = lv_area_intersect(&draw_area, coords, clip);
if(is_common == false) return;
/*Move the vdb_buf_tmp to the first row*/
lv_color_t * vdb_buf_tmp = vdb->buf_act;
vdb_buf_tmp += vdb_width * draw_rel_a.y1;
const lv_area_t * disp_area = &vdb->area;
lv_color_t * disp_buf = vdb->buf_act;
/* Now `draw_area` has absolute coordinates.
* Make it relative to `disp_area` to simplify draw to `disp_buf`*/
draw_area.x1 -= disp_area->x1;
draw_area.y1 -= disp_area->y1;
draw_area.x2 -= disp_area->x1;
draw_area.y2 -= disp_area->y1;
lv_coord_t draw_area_w = lv_area_get_width(&draw_area);
/*Get the width of the `disp_area` it will be used to go to the next line*/
lv_coord_t disp_w = lv_area_get_width(disp_area);
/*Create a temp. disp_buf which always point to current line to draw*/
lv_color_t * disp_buf_tmp = disp_buf + disp_w * draw_area.y1;
/*Create a mask if there is a radius*/
lv_opa_t mask_buf[LV_HOR_RES_MAX];
lv_coord_t short_side = LV_MATH_MIN(lv_area_get_width(coords), lv_area_get_height(coords));
lv_coord_t rout = style->body.radius;
if(rout > short_side >> 1) rout = short_side >> 1;
/*Create the outer mask*/
lv_mask_param_t mask_rout_param;
int16_t mask_rout_id = LV_MASK_ID_INV;
uint8_t other_mask_cnt = lv_mask_get_cnt();
int16_t mask_rout_id = LV_MASK_ID_INV;
if(rout != 0) {
/*Get the real radius*/
lv_coord_t rout = style->body.radius;
if(rout == LV_RADIUS_CIRCLE) {
lv_coord_t short_side = LV_MATH_MIN(lv_area_get_width(coords), lv_area_get_height(coords));
if(rout > short_side >> 1) rout = short_side >> 1;
}
/*Most simple case: just a plain rectangle*/
if(other_mask_cnt == 0 && rout == 0 && style->body.main_color.full == style->body.grad_color.full) {
lv_blend_fill(&vdb->area, clip, coords,
vdb->buf_act, LV_IMG_CF_TRUE_COLOR, style->body.main_color,
NULL, LV_MASK_RES_FULL_COVER, style->body.opa, LV_BLIT_MODE_NORMAL);
}
/*More complex case: there is a radius, gradient or mask.*/
else {
lv_mask_param_t mask_rout_param;
if(rout > 0) {
lv_mask_radius_init(&mask_rout_param, coords, rout, false);
mask_rout_id = lv_mask_add(lv_mask_radius, &mask_rout_param, NULL);
}
@ -116,30 +129,41 @@ static void draw_bg(const lv_area_t * coords, const lv_area_t * clip, const lv_s
/*Fill the first row with 'color'*/
if(opa >= LV_OPA_MIN) {
for(h = draw_rel_a.y1; h <= draw_rel_a.y2; h++) {
for(h = draw_area.y1; h <= draw_area.y2; h++) {
lv_coord_t y = h + vdb->area.y1;
/*In not corner areas apply the mask only if required*/
if(y > coords->y1 + rout + 1 &&
y < coords->y2 - rout - 1) {
mask_res = LV_MASK_RES_FULL_COVER;
if(other_mask_cnt != 0) {
memset(mask_buf, LV_OPA_COVER, draw_a_width);
mask_res = lv_mask_apply(mask_buf, vdb->area.x1 + draw_rel_a.x1, vdb->area.y1 + h, draw_a_width);
memset(mask_buf, LV_OPA_COVER, draw_area_w);
mask_res = lv_mask_apply(mask_buf, vdb->area.x1 + draw_area.x1, vdb->area.y1 + h, draw_area_w);
}
} else {
memset(mask_buf, LV_OPA_COVER, draw_a_width);
mask_res = lv_mask_apply(mask_buf, vdb->area.x1 + draw_rel_a.x1, vdb->area.y1 + h, draw_a_width);
}
/*In corner areas apply the mask anyway*/
else {
memset(mask_buf, LV_OPA_COVER, draw_area_w);
mask_res = lv_mask_apply(mask_buf, vdb->area.x1 + draw_area.x1, vdb->area.y1 + h, draw_area_w);
}
/*Get the current line color*/
if(style->body.main_color.full != style->body.grad_color.full) {
lv_opa_t mix = (uint32_t)((uint32_t) (y - coords->y1) * 255) / lv_area_get_height(coords);
grad_color = lv_color_mix(style->body.grad_color, style->body.main_color, mix);
}
lv_blend_color(&vdb_buf_tmp[draw_rel_a.x1], LV_IMG_CF_TRUE_COLOR, draw_a_width,
grad_color, LV_IMG_CF_TRUE_COLOR,
mask_buf, mask_res, style->body.opa, LV_BLIT_MODE_NORMAL);
/*Create a area for this line to fill*/
lv_area_t fill_area;
fill_area.x1 = coords->x1;
fill_area.x2 = coords->x2;
fill_area.y1 = disp_area->y1 + h;
fill_area.y2 = fill_area.y1;
vdb_buf_tmp += vdb_width;
lv_blend_fill(disp_area, clip, &fill_area,
disp_buf, LV_IMG_CF_TRUE_COLOR, grad_color,
mask_buf, mask_res, style->body.opa, LV_BLIT_MODE_NORMAL);
}
}
}
@ -147,8 +171,6 @@ static void draw_bg(const lv_area_t * coords, const lv_area_t * clip, const lv_s
lv_coord_t border_width = style->body.border.width;
if(border_width) {
/*Move the vdb_buf_tmp to the first row*/
vdb_buf_tmp = vdb->buf_act;
vdb_buf_tmp += vdb_width * draw_rel_a.y1;
lv_mask_param_t mask_rsmall_param;
lv_coord_t rin = rout - border_width;
@ -162,19 +184,21 @@ static void draw_bg(const lv_area_t * coords, const lv_area_t * clip, const lv_s
lv_mask_radius_init(&mask_rsmall_param, &area_small, style->body.radius - border_width, true);
int16_t mask_rsmall_id = lv_mask_add(lv_mask_radius, &mask_rsmall_param, NULL);
lv_coord_t len_left = (coords->x1 + border_width) - (vdb->area.x1 + draw_rel_a.x1);
if(draw_rel_a.x1 + len_left > draw_rel_a.x2) {
len_left = draw_rel_a.x2 - draw_rel_a.x1 + 1;
lv_coord_t len_left = (coords->x1 + border_width) - (vdb->area.x1 + draw_area.x1);
if(draw_area.x1 + len_left > draw_area.x2) {
len_left = draw_area.x2 - draw_area.x1 + 1;
}
lv_coord_t first_right = coords->x2 - (vdb->area.x1 + draw_rel_a.x1 + border_width - 1);
lv_coord_t first_right = coords->x2 - (vdb->area.x1 + draw_area.x1 + border_width - 1);
if(first_right < 0) first_right = 0;
lv_coord_t len_right = draw_a_width - first_right;
lv_coord_t len_right = draw_area_w - first_right;
lv_coord_t corner_size = LV_MATH_MAX(rout, border_width) + 1;
/*Fill the first row with 'color'*/
for(h = draw_rel_a.y1; h <= draw_rel_a.y2; h++) {
lv_coord_t h;
lv_mask_res_t mask_res;
for(h = draw_area.y1; h <= draw_area.y2; h++) {
/* Do not blend the large empty area in the middle.
* Truncate the mask to use only the very edges*/
lv_coord_t y = h + vdb->area.y1;
@ -182,32 +206,32 @@ static void draw_bg(const lv_area_t * coords, const lv_area_t * clip, const lv_s
y < coords->y2 - corner_size) {
mask_res = LV_MASK_RES_FULL_COVER;
if(other_mask_cnt != 0) {
memset(mask_buf, LV_OPA_COVER, draw_a_width);
mask_res = lv_mask_apply(mask_buf, vdb->area.x1 + draw_rel_a.x1, vdb->area.y1 + h, draw_a_width);
memset(mask_buf, LV_OPA_COVER, draw_area_w);
mask_res = lv_mask_apply(mask_buf, vdb->area.x1 + draw_area.x1, vdb->area.y1 + h, draw_area_w);
}
/* It's sure that we don't need more then border_width pixels on the left.*/
if(len_left > 0) {
lv_blend_color(&vdb_buf_tmp[draw_rel_a.x1], LV_IMG_CF_TRUE_COLOR, len_left,
lv_blend_color(&disp_buf_tmp[draw_area.x1], LV_IMG_CF_TRUE_COLOR, len_left,
style->body.border.color, LV_IMG_CF_TRUE_COLOR,
mask_buf, mask_res, style->body.border.opa, LV_BLIT_MODE_NORMAL);
}
/* Similarly we don't need more then border_width pixels on the right.*/
if(len_right > 0) {
lv_blend_color(&vdb_buf_tmp[draw_rel_a.x1 + first_right], LV_IMG_CF_TRUE_COLOR, len_right,
lv_blend_color(&disp_buf_tmp[draw_area.x1 + first_right], LV_IMG_CF_TRUE_COLOR, len_right,
style->body.border.color, LV_IMG_CF_TRUE_COLOR,
mask_buf + first_right, mask_res, style->body.border.opa, LV_BLIT_MODE_NORMAL);
}
}
else {
memset(mask_buf, LV_OPA_COVER, draw_a_width);
mask_res = lv_mask_apply(mask_buf, vdb->area.x1 + draw_rel_a.x1, vdb->area.y1 + h, draw_a_width);
memset(mask_buf, LV_OPA_COVER, draw_area_w);
mask_res = lv_mask_apply(mask_buf, vdb->area.x1 + draw_area.x1, vdb->area.y1 + h, draw_area_w);
lv_blend_color(&vdb_buf_tmp[draw_rel_a.x1], LV_IMG_CF_TRUE_COLOR, draw_a_width,
lv_blend_color(&disp_buf_tmp[draw_area.x1], LV_IMG_CF_TRUE_COLOR, draw_area_w,
style->body.border.color, LV_IMG_CF_TRUE_COLOR,
mask_buf, mask_res, style->body.border.opa, LV_BLIT_MODE_NORMAL);
}
vdb_buf_tmp += vdb_width;
disp_buf_tmp += disp_w;
}
lv_mask_remove_id(mask_rsmall_id);

View File

@ -345,7 +345,7 @@ lv_mask_res_t lv_mask_angle(lv_opa_t * mask_buf, lv_coord_t abs_x, lv_coord_t ab
else return LV_MASK_RES_CHANGED;
}
void lv_mask_radius_init(lv_mask_param_t * param, lv_area_t * rect, lv_coord_t radius, bool inv)
void lv_mask_radius_init(lv_mask_param_t * param, const lv_area_t * rect, lv_coord_t radius, bool inv)
{
lv_mask_radius_param_t * p = &param->radius;

View File

@ -112,7 +112,7 @@ void lv_mask_line_angle_init(lv_mask_param_t * param, lv_coord_t p1x, lv_coord_t
lv_mask_res_t lv_mask_line(lv_opa_t * mask_buf, lv_coord_t abs_x, lv_coord_t abs_y, lv_coord_t len, lv_mask_param_t * param);
void lv_mask_radius_init(lv_mask_param_t * param, lv_area_t * rect, lv_coord_t radius, bool inv);
void lv_mask_radius_init(lv_mask_param_t * param, const lv_area_t * rect, lv_coord_t radius, bool inv);
lv_mask_res_t lv_mask_radius(lv_opa_t * mask_buf, lv_coord_t abs_x, lv_coord_t abs_y, lv_coord_t len, lv_mask_param_t * param);
void lv_mask_angle_init(lv_mask_param_t * param, lv_coord_t origio_x, lv_coord_t origo_y, lv_coord_t start_angle, lv_coord_t end_angle);