perf(draw) reimplement circle drawing algorithms (#2374)

* perf(draw) reimplement circle drawing algorithms

Imporve the speed of circle drawing
Add circle draw caching
Various other speed improvements

* docs describe how to use masks

* fix(draw) add missing GC root usage
This commit is contained in:
Gabor Kiss-Vamosi
2021-07-27 19:16:00 +02:00
committed by GitHub
parent e23701e2c2
commit 637b706ddc
25 changed files with 517 additions and 323 deletions

View File

@@ -54,13 +54,13 @@ The difference between buffering modes regarding the drawing mechanism is the fo
To use LVGL it's not required to know about the mechanisms described here, but you might find interesting to know how drawing works under hood.
Knowing about masking comes in handy if you want to customize drawing.
To learn masking let's learn the steps of drawing first.
To learn masking let's see the steps of drawing first.
LVGL performs the following steps to render any shape, image or text. It can be considered as a drawing pipeline.
1. **Prepare the draw descriptors** Create a draw descriptor from an object's styles (e.g. `lv_draw_rect_dsc_t`). This gives us the parameters for drawing, for example the colors, widths, opacity, fonts, radius, etc.
2. **Call the draw function** Call the draw function with the draw descriptor and some other parameters (e.g. `lv_draw_rect()`). It renders the primitive shape to the current draw buffer.
3. **Create masks** If the shape is very simple and doesn't require masks go to #5. Else create the required masks (e.g. a rounded rectangle mask)
4. **Calculate all the added mask**. It creates 0..255 values into a *mask buffer* with the "shape" of the created masks.
2. **Call the draw function** Call the draw function with the draw descriptor and some other parameters (e.g. `lv_draw_rect()`). It will render the primitive shape to the current draw buffer.
3. **Create masks** If the shape is very simple and doesn't require masks go to #5. Else create the required masks in the draw function. (e.g. a rounded rectangle mask)
4. **Calculate all the added mask** It creates 0..255 values into a *mask buffer* with the "shape" of the created masks.
E.g. in case of a "line mask" according to the parameters of the mask, keep one side of the buffer as it is (255 by default) and set the rest to 0 to indicate that this side should be removed.
5. **Blend a color or image** During blending masks (make some pixels transparent or opaque), blending modes (additive, subtractive, etc) and opacity are handled.
@@ -81,6 +81,25 @@ Masks are used the create almost every basic primitives:
- **arc drawing** A circle border is drawn, but an arc mask is applied too.
- **ARGB images** The alpha channel is separated into a mask and the image is drawn as a normal RGB image.
### Using masks
Every mask type has a related paramter to describe the mask's data. The following paramater types exist:
- `lv_draw_mask_line_param_t`
- `lv_draw_mask_radius_param_t`
- `lv_draw_mask_angle_param_t`
- `lv_draw_mask_fade_param_t`
- `lv_draw_mask_map_param_t`
1. Initialize a mask parameter with `lv_draw_mask_<type>_init`. See `lv_draw_mask.h` for the whole API.
2. Add the mask parameter to the draw engine with `int16_t mask_id = lv_draw_mask_add(&param, ptr)`. `ptr` can be any pointer to identify the mask, (`NULL` if unused).
3. Call the draw functions
4. Remove the mask from the draw engine with `lv_draw_mask_remove_id(mask_id)` of `lv_draw_mask_remove_custom(ptr)`.
5. Free the parameter with `lv_draw_mask_free_param(&param)`.
A parameter can be added and removed any number of times but it needs to be freed when not required anymore.
`lv_draw_mask_add` saves only the pointer of the mask so the parameter needs to be valid while in use.
## Hook drawing
Although widgets can be very well customized by styles there might be cases when something really custom is required.
To ensure a great level of flexibility LVGL sends a lot events during drawing with parameters that tell what LVGL is about to draw.

View File

@@ -39,6 +39,8 @@ static void draw_event_cb(lv_event_t * e)
lv_draw_rect(&a, dsc->clip_area, &draw_rect_dsc);
/*Remove the masks*/
lv_draw_mask_free_param(&line_mask_param);
lv_draw_mask_free_param(&fade_mask_param);
lv_draw_mask_remove_id(line_mask_id);
lv_draw_mask_remove_id(fade_mask_id);
}

View File

@@ -43,6 +43,8 @@ static void mask_event_cb(lv_event_t * e)
lv_draw_mask_fade_param_t * fade_mask_bottom = lv_draw_mask_remove_id(mask_bottom_id);
lv_mem_buf_release(fade_mask_top);
lv_mem_buf_release(fade_mask_bottom);
lv_draw_mask_free_param(&fade_mask_top);
lv_draw_mask_free_param(&fade_mask_bottom);
mask_top_id = -1;
mask_bottom_id = -1;
}

View File

@@ -95,6 +95,13 @@
*LV_SHADOW_CACHE_SIZE is the max. shadow size to buffer, where shadow size is `shadow_width + radius`
*Caching has LV_SHADOW_CACHE_SIZE^2 RAM cost*/
#define LV_SHADOW_CACHE_SIZE 0
/* Set number of maximally cached circle data.
* The circumference of 1/4 circle are saved for anti-aliasing
* radius * 4 bytes are used per circle (the most often used radiuses are saved)
* 0: to disable caching */
#define LV_CIRCLE_CACHE_SIZE 4
#endif /*LV_DRAW_COMPLEX*/
/*Default image cache size. Image caching keeps the images opened.

View File

@@ -290,8 +290,9 @@ lv_obj_t * lv_indev_search_obj(lv_obj_t * obj, lv_point_t * point)
/*If the point is on this object check its children too*/
if(lv_obj_hit_test(obj, point)) {
int32_t i;
for(i = lv_obj_get_child_cnt(obj) - 1; i >= 0; i--) {
lv_obj_t * child = lv_obj_get_child(obj, i);
uint32_t child_cnt = lv_obj_get_child_cnt(obj);
for(i = child_cnt - 1; i >= 0; i--) {
lv_obj_t * child = obj->spec_attr->children[i];
found_p = lv_indev_search_obj(child, point);
/*If a child was found then break*/

View File

@@ -421,7 +421,7 @@ static lv_coord_t find_snap_point_x(const lv_obj_t * obj, lv_coord_t min, lv_coo
uint32_t i;
uint32_t child_cnt = lv_obj_get_child_cnt(obj);
for(i = 0; i < child_cnt; i++) {
lv_obj_t * child = lv_obj_get_child(obj, 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;
@@ -476,7 +476,7 @@ static lv_coord_t find_snap_point_y(const lv_obj_t * obj, lv_coord_t min, lv_coo
uint32_t i;
uint32_t child_cnt = lv_obj_get_child_cnt(obj);
for(i = 0; i < child_cnt; i++) {
lv_obj_t * child = lv_obj_get_child(obj, 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;

View File

@@ -533,6 +533,7 @@ static void lv_obj_draw(lv_event_t * e)
#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);
lv_draw_mask_free_param(param);
lv_mem_buf_release(param);
}
#endif
@@ -781,7 +782,7 @@ static void lv_obj_event(const lv_obj_class_t * class_p, lv_event_t * e)
uint32_t i;
uint32_t child_cnt = lv_obj_get_child_cnt(obj);
for(i = 0; i < child_cnt; i++) {
lv_obj_t * child = lv_obj_get_child(obj, i);
lv_obj_t * child = obj->spec_attr->children[i];
lv_obj_mark_layout_as_dirty(child);
}
}

View File

@@ -754,7 +754,7 @@ void lv_obj_move_children_by(lv_obj_t * obj, lv_coord_t x_diff, lv_coord_t y_dif
uint32_t i;
uint32_t child_cnt = lv_obj_get_child_cnt(obj);
for(i = 0; i < child_cnt; i++) {
lv_obj_t * child = lv_obj_get_child(obj, i);
lv_obj_t * child = obj->spec_attr->children[i];
if(ignore_floating && lv_obj_has_flag(child, LV_OBJ_FLAG_FLOATING)) continue;
child->coords.x1 += x_diff;
child->coords.y1 += y_diff;
@@ -937,7 +937,7 @@ static void layout_update_core(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 = lv_obj_get_child(obj, i);
lv_obj_t * child = obj->spec_attr->children[i];
layout_update_core(child);
}
@@ -948,7 +948,7 @@ static void layout_update_core(lv_obj_t * obj)
lv_obj_refr_size(obj);
lv_obj_refr_pos(obj);
if(lv_obj_get_child_cnt(obj) > 0) {
if(child_cnt > 0) {
uint32_t layout_id = lv_obj_get_style_layout(obj, LV_PART_MAIN);
if(layout_id > 0 && layout_id <= layout_cnt) {
void * user_data = LV_GC_ROOT(_lv_layout_list)[layout_id -1].user_data;

View File

@@ -139,7 +139,7 @@ lv_coord_t lv_obj_get_scroll_bottom(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 = lv_obj_get_child(obj, 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);
}
@@ -180,8 +180,8 @@ lv_coord_t lv_obj_get_scroll_left(lv_obj_t * obj)
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 = lv_obj_get_child(obj, i);
if(lv_obj_has_flag_any(child, LV_OBJ_FLAG_HIDDEN | LV_OBJ_FLAG_FLOATING)) continue;
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);
}
@@ -216,7 +216,7 @@ lv_coord_t lv_obj_get_scroll_right(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 = lv_obj_get_child(obj, 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);
}

View File

@@ -623,7 +623,7 @@ static void report_style_change_core(void * style, lv_obj_t * obj)
uint32_t child_cnt = lv_obj_get_child_cnt(obj);
for(i = 0; i < child_cnt; i++) {
report_style_change_core(style, lv_obj_get_child(obj, i));
report_style_change_core(style, obj->spec_attr->children[i]);
}
}
@@ -637,7 +637,7 @@ 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 = lv_obj_get_child(obj, 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);

View File

@@ -236,6 +236,10 @@ void _lv_disp_refr_timer(lv_timer_t * tmr)
lv_mem_buf_free_all();
_lv_font_clean_up_fmt_txt();
#if LV_DRAW_COMPLEX
_lv_draw_mask_cleanup();
#endif
#if LV_USE_PERF_MONITOR && LV_USE_LABEL
static lv_obj_t * perf_label = NULL;
if(perf_label == NULL) {
@@ -586,7 +590,7 @@ static lv_obj_t * lv_refr_get_top_obj(const lv_area_t * area_p, 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 = lv_obj_get_child(obj, i);
lv_obj_t * child = obj->spec_attr->children[i];
found_p = lv_refr_get_top_obj(area_p, child);
/*If a children is ok then break*/
@@ -634,7 +638,7 @@ static void lv_refr_obj_and_children(lv_obj_t * top_p, const lv_area_t * mask_p)
uint32_t i;
uint32_t child_cnt = lv_obj_get_child_cnt(par);
for(i = 0; i < child_cnt; i++) {
lv_obj_t * child = lv_obj_get_child(par, i);
lv_obj_t * child = par->spec_attr->children[i];
if(!go) {
if(child == border_p) go = true;
} else {
@@ -707,7 +711,7 @@ static void lv_refr_obj(lv_obj_t * obj, const lv_area_t * mask_ori_p)
uint32_t i;
uint32_t child_cnt = lv_obj_get_child_cnt(obj);
for(i = 0; i < child_cnt; i++) {
lv_obj_t * child = lv_obj_get_child(obj, i);
lv_obj_t * child = obj->spec_attr->children[i];
lv_obj_get_coords(child, &child_area);
ext_size = _lv_obj_get_ext_draw_size(child);
child_area.x1 -= ext_size;

View File

@@ -159,6 +159,11 @@ void lv_draw_arc(lv_coord_t center_x, lv_coord_t center_y, uint16_t radius, uin
else {
lv_draw_rect(&area_out, clip_area, &cir_dsc);
}
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);
lv_draw_mask_remove_id(mask_in_id);
@@ -180,6 +185,7 @@ void lv_draw_arc(lv_coord_t center_x, lv_coord_t center_y, uint16_t radius, uin
lv_draw_rect(&area_out, &clip_area2, &cir_dsc);
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);
@@ -193,6 +199,7 @@ void lv_draw_arc(lv_coord_t center_x, lv_coord_t center_y, uint16_t radius, uin
lv_draw_rect(&area_out, &clip_area2, &cir_dsc);
lv_draw_mask_remove_id(mask_end_id);
lv_draw_mask_free_param(&mask_end_param);
}
}
#else

View File

@@ -73,22 +73,21 @@ static inline lv_color_t color_blend_true_color_subtractive(lv_color_t fg, lv_co
/**********************
* MACROS
**********************/
#if LV_COLOR_SCREEN_TRANSP == 0
#define FILL_NORMAL_MASK_PX(color) \
if(*mask == LV_OPA_COVER) *disp_buf_first = color; \
else *disp_buf_first = lv_color_mix(color, *disp_buf_first, *mask); \
mask++; \
disp_buf_first++;
#define FILL_NORMAL_MASK_PX(out_x, color) \
if(*mask_tmp_x) { \
if(*mask_tmp_x == LV_OPA_COVER) disp_buf_first[out_x] = color; \
else disp_buf_first[out_x] = lv_color_mix(color, disp_buf_first[out_x], *mask_tmp_x); \
} \
mask_tmp_x++;
#define FILL_NORMAL_MASK_PX_SCR_TRANSP(out_x, color) \
if(*mask_tmp_x) { \
if(*mask_tmp_x == LV_OPA_COVER) disp_buf_first[out_x] = color; \
else if(disp->driver->screen_transp) lv_color_mix_with_alpha(disp_buf_first[out_x], disp_buf_first[out_x].ch.alpha, \
color, *mask_tmp_x, &disp_buf_first[out_x], &disp_buf_first[out_x].ch.alpha); \
else disp_buf_first[out_x] = lv_color_mix(color, disp_buf_first[out_x], *mask_tmp_x); \
} \
mask_tmp_x++;
#else
#define FILL_NORMAL_MASK_PX(color) \
if(*mask == LV_OPA_COVER) *disp_buf_first = color; \
else if(disp->driver->screen_transp) lv_color_mix_with_alpha(*disp_buf_first, disp_buf_first->ch.alpha, color, *mask, disp_buf_first, &disp_buf_first->ch.alpha); \
else *disp_buf_first = lv_color_mix(color, *disp_buf_first, *mask); \
mask++; \
disp_buf_first++;
#endif
#define MAP_NORMAL_MASK_PX(x) \
if(*mask_tmp_x) { \
@@ -326,7 +325,6 @@ LV_ATTRIBUTE_FAST_MEM static void fill_normal(const lv_area_t * disp_area, lv_co
if(lv_gpu_nxp_vglite_fill(disp_buf, disp_w, lv_area_get_height(disp_area), draw_area, color, opa) == LV_RES_OK) {
return;
}
/*Fall down to SW render in case of error*/
}
#elif LV_USE_GPU_STM32_DMA2D
if(lv_area_get_size(draw_area) >= 240) {
@@ -401,65 +399,55 @@ LV_ATTRIBUTE_FAST_MEM static void fill_normal(const lv_area_t * disp_area, lv_co
int32_t x_end4 = draw_area_w - 4;
#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) {
for(y = 0; y < draw_area_h; y++) {
const lv_opa_t * mask_tmp_x = mask;
#if 0
for(x = 0; x < draw_area_w; x++) {
#if LV_COLOR_SCREEN_TRANSP
FILL_NORMAL_MASK_PX_SCR_TRANSP(x, color)
#else
FILL_NORMAL_MASK_PX(x, color)
#endif
}
#else
for(x = 0; x < draw_area_w && ((lv_uintptr_t)mask_tmp_x & 0x3); x++) {
#if LV_COLOR_SCREEN_TRANSP
FILL_NORMAL_MASK_PX_SCR_TRANSP(x, color)
#else
FILL_NORMAL_MASK_PX(x, color)
#endif
for(x = 0; x < draw_area_w && ((lv_uintptr_t)(mask) & 0x3); x++) {
FILL_NORMAL_MASK_PX(color)
}
uint32_t * mask32 = (uint32_t *)mask_tmp_x;
for(; x <= x_end4; x += 4) {
if(*mask32) {
if((*mask32) == 0xFFFFFFFF) {
disp_buf_first[x] = color;
disp_buf_first[x + 1] = color;
disp_buf_first[x + 2] = color;
disp_buf_first[x + 3] = color;
uint32_t mask32 = *((uint32_t *)mask);
if(mask32 == 0xFFFFFFFF) {
#if LV_COLOR_DEPTH == 16
if((lv_uintptr_t)disp_buf_first & 0x3) {
*(disp_buf_first + 0) = color;
uint32_t * d = (uint32_t * )(disp_buf_first + 1);
*d = c32;
*(disp_buf_first + 3) = color;
} else {
uint32_t * d = (uint32_t * )disp_buf_first;
*d = c32;
*(d + 1) = c32;
}
else {
mask_tmp_x = (const lv_opa_t *)mask32;
#if LV_COLOR_SCREEN_TRANSP
FILL_NORMAL_MASK_PX_SCR_TRANSP(x, color)
FILL_NORMAL_MASK_PX_SCR_TRANSP(x + 1, color)
FILL_NORMAL_MASK_PX_SCR_TRANSP(x + 2, color)
FILL_NORMAL_MASK_PX_SCR_TRANSP(x + 3, color)
#else
FILL_NORMAL_MASK_PX(x, color)
FILL_NORMAL_MASK_PX(x + 1, color)
FILL_NORMAL_MASK_PX(x + 2, color)
FILL_NORMAL_MASK_PX(x + 3, color)
disp_buf_first[0] = color;
disp_buf_first[1] = color;
disp_buf_first[2] = color;
disp_buf_first[3] = color;
#endif
}
disp_buf_first+= 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;
disp_buf_first += 4;
}
mask32++;
}
mask_tmp_x = (const lv_opa_t *)mask32;
for(; x < draw_area_w ; x++) {
#if LV_COLOR_SCREEN_TRANSP
FILL_NORMAL_MASK_PX_SCR_TRANSP(x, color)
#else
FILL_NORMAL_MASK_PX(x, color)
#endif
FILL_NORMAL_MASK_PX(color)
}
#endif
disp_buf_first += disp_w;
mask += draw_area_w;
disp_buf_first += (disp_w-draw_area_w);
}
}
/*Handle opa and mask values too*/
@@ -701,8 +689,8 @@ LV_ATTRIBUTE_FAST_MEM static void map_normal(const lv_area_t * disp_area, lv_col
blit.src_stride = lv_area_get_width(map_area) * sizeof(lv_color_t);
blit.src_area.x1 = (draw_area->x1 - (map_area->x1 - disp_area->x1));
blit.src_area.y1 = (draw_area->y1 - (map_area->y1 - disp_area->y1));
blit.src_area.x2 = blit.src_area.x1 + draw_area_w - 1;
blit.src_area.y2 = blit.src_area.y1 + draw_area_h - 1;
blit.src_area.x2 = blit.src_area.x1 + draw_area_w;
blit.src_area.y2 = blit.src_area.y1 + draw_area_h;
blit.dst = disp_buf;
blit.dst_width = lv_area_get_width(disp_area);
@@ -710,8 +698,8 @@ LV_ATTRIBUTE_FAST_MEM static void map_normal(const lv_area_t * disp_area, lv_col
blit.dst_stride = lv_area_get_width(disp_area) * sizeof(lv_color_t);
blit.dst_area.x1 = draw_area->x1;
blit.dst_area.y1 = draw_area->y1;
blit.dst_area.x2 = blit.dst_area.x1 + draw_area_w - 1;
blit.dst_area.y2 = blit.dst_area.y1 + draw_area_h - 1;
blit.dst_area.x2 = blit.dst_area.x1 + draw_area_w;
blit.dst_area.y2 = blit.dst_area.y1 + draw_area_h;
blit.opa = opa;
@@ -751,8 +739,8 @@ LV_ATTRIBUTE_FAST_MEM static void map_normal(const lv_area_t * disp_area, lv_col
blit.src_stride = lv_area_get_width(map_area) * sizeof(lv_color_t);
blit.src_area.x1 = (draw_area->x1 - (map_area->x1 - disp_area->x1));
blit.src_area.y1 = (draw_area->y1 - (map_area->y1 - disp_area->y1));
blit.src_area.x2 = blit.src_area.x1 + draw_area_w - 1;
blit.src_area.y2 = blit.src_area.y1 + draw_area_h - 1;
blit.src_area.x2 = blit.src_area.x1 + draw_area_w;
blit.src_area.y2 = blit.src_area.y1 + draw_area_h;
blit.dst = disp_buf;
blit.dst_width = lv_area_get_width(disp_area);
@@ -760,8 +748,8 @@ LV_ATTRIBUTE_FAST_MEM static void map_normal(const lv_area_t * disp_area, lv_col
blit.dst_stride = lv_area_get_width(disp_area) * sizeof(lv_color_t);
blit.dst_area.x1 = draw_area->x1;
blit.dst_area.y1 = draw_area->y1;
blit.dst_area.x2 = blit.dst_area.x1 + draw_area_w - 1;
blit.dst_area.y2 = blit.dst_area.y1 + draw_area_h - 1;
blit.dst_area.x2 = blit.dst_area.x1 + draw_area_w;
blit.dst_area.y2 = blit.dst_area.y1 + draw_area_h;
blit.opa = opa;
@@ -933,13 +921,7 @@ static void map_blended(const lv_area_t * disp_area, lv_color_t * disp_buf, con
for(y = draw_area->y1; y <= draw_area->y2; y++) {
for(x = draw_area->x1; x <= draw_area->x2; x++) {
#if LV_COLOR_DEPTH == 32
if(map_buf_tmp[x].full != 0xff000000) {
#else
if(map_buf_tmp[x].full != 0) {
#endif
disp_buf_tmp[x] = blend_fp(map_buf_tmp[x], disp_buf_tmp[x], opa);
}
disp_buf_tmp[x] = blend_fp(map_buf_tmp[x], disp_buf_tmp[x], opa);
}
disp_buf_tmp += disp_w;
map_buf_tmp += map_w;
@@ -957,14 +939,7 @@ static void map_blended(const lv_area_t * disp_area, lv_color_t * disp_buf, con
for(x = draw_area->x1; x <= draw_area->x2; x++) {
if(mask_tmp[x] == 0) continue;
lv_opa_t opa_tmp = mask_tmp[x] >= LV_OPA_MAX ? opa : ((opa * mask_tmp[x]) >> 8);
#if LV_COLOR_DEPTH == 32
if(map_buf_tmp[x].full != 0xff000000) {
#else
if(map_buf_tmp[x].full != 0) {
#endif
disp_buf_tmp[x] = blend_fp(map_buf_tmp[x], disp_buf_tmp[x], opa_tmp);
}
disp_buf_tmp[x] = blend_fp(map_buf_tmp[x], disp_buf_tmp[x], opa_tmp);
}
disp_buf_tmp += disp_w;
mask_tmp += draw_area_w;
@@ -975,6 +950,9 @@ static void map_blended(const lv_area_t * disp_area, lv_color_t * disp_buf, con
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;
@@ -990,7 +968,6 @@ static inline lv_color_t color_blend_true_color_additive(lv_color_t fg, lv_color
#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
@@ -1004,7 +981,6 @@ static inline lv_color_t color_blend_true_color_additive(lv_color_t fg, lv_color
#endif
#elif LV_COLOR_DEPTH == 32
tmp = bg.ch.green + fg.ch.green;
fg.ch.green = LV_MIN(tmp, 255);
#endif

View File

@@ -474,6 +474,10 @@ LV_ATTRIBUTE_FAST_MEM static void draw_line_skew(const lv_point_t * point1, cons
lv_mem_buf_release(mask_buf);
lv_draw_mask_free_param(&mask_left_param);
lv_draw_mask_free_param(&mask_right_param);
if(mask_top_id != LV_MASK_ID_INV) lv_draw_mask_free_param(&mask_top_param);
if(mask_bottom_id != LV_MASK_ID_INV) lv_draw_mask_free_param(&mask_bottom_param);
lv_draw_mask_remove_id(mask_left_id);
lv_draw_mask_remove_id(mask_right_id);
lv_draw_mask_remove_id(mask_top_id);

View File

@@ -6,8 +6,6 @@
/*********************
* INCLUDES
*********************/
#include "lv_draw_mask.h"
#if LV_DRAW_COMPLEX
#include "../misc/lv_math.h"
@@ -18,6 +16,8 @@
/*********************
* DEFINES
*********************/
#define CIRCLE_CACHE_LIFE_MAX 1000
#define CIRCLE_CACHE_AGING(life, r) life = LV_MIN(life + (r < 16 ? 1 : (r >> 4)), 1000)
/**********************
* TYPEDEFS
@@ -49,8 +49,12 @@ LV_ATTRIBUTE_FAST_MEM static lv_draw_mask_res_t line_mask_steep(lv_opa_t * mask_
lv_coord_t len,
lv_draw_mask_line_param_t * p);
static void circ_init(lv_point_t * c, lv_coord_t * tmp, lv_coord_t radius);
static bool circ_cont(lv_point_t * c);
static void circ_next(lv_point_t * c, lv_coord_t * tmp);
static void circ_calc_aa4(_lv_draw_mask_radius_circle_dsc_t * c, lv_coord_t radius);
static lv_opa_t * get_next_line(_lv_draw_mask_radius_circle_dsc_t * c, lv_coord_t y, lv_coord_t * len, lv_coord_t * x_start);
LV_ATTRIBUTE_FAST_MEM static inline lv_opa_t mask_mix(lv_opa_t mask_act, lv_opa_t mask_new);
LV_ATTRIBUTE_FAST_MEM static inline void sqrt_approx(lv_sqrt_res_t * q, lv_sqrt_res_t * ref, uint32_t x);
/**********************
* STATIC VARIABLES
@@ -129,7 +133,7 @@ LV_ATTRIBUTE_FAST_MEM lv_draw_mask_res_t lv_draw_mask_apply(lv_opa_t * mask_buf,
*/
void * lv_draw_mask_remove_id(int16_t id)
{
void * p = NULL;
_lv_draw_mask_common_dsc_t * p = NULL;
if(id != LV_MASK_ID_INV) {
p = LV_GC_ROOT(_lv_draw_mask_list[id]).param;
@@ -148,18 +152,51 @@ void * lv_draw_mask_remove_id(int16_t id)
*/
void * lv_draw_mask_remove_custom(void * custom_id)
{
void * p = NULL;
_lv_draw_mask_common_dsc_t * p = NULL;
uint8_t i;
for(i = 0; i < _LV_MASK_MAX_NUM; i++) {
if(LV_GC_ROOT(_lv_draw_mask_list[i]).custom_id == custom_id) {
p = LV_GC_ROOT(_lv_draw_mask_list[i]).param;
LV_GC_ROOT(_lv_draw_mask_list[i]).param = NULL;
LV_GC_ROOT(_lv_draw_mask_list[i]).custom_id = NULL;
lv_draw_mask_remove_id(i);
}
}
return p;
}
/**
* 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)
{
_lv_draw_mask_common_dsc_t * pdsc = p;
if(pdsc->type == LV_DRAW_MASK_TYPE_RADIUS) {
lv_draw_mask_radius_param_t * radius_p = (lv_draw_mask_radius_param_t *) p;
if(radius_p->circle) {
if(radius_p->circle->life < 0) {
lv_mem_free(radius_p->circle->cir_opa);
lv_mem_free(radius_p->circle);
} else {
radius_p->circle->used_cnt--;
}
}
}
}
void _lv_draw_mask_cleanup(void)
{
uint8_t i;
for(i = 0; i < LV_CIRCLE_CACHE_SIZE; i++) {
if(LV_GC_ROOT(_lv_circle_cache[i]).buf) {
lv_mem_free(LV_GC_ROOT(_lv_circle_cache[i]).buf);
}
lv_memset_00(&LV_GC_ROOT(_lv_circle_cache[i]), sizeof(LV_GC_ROOT(_lv_circle_cache[i])));
}
}
/**
* Count the currently added masks
* @return number of active masks
@@ -373,6 +410,7 @@ void lv_draw_mask_radius_init(lv_draw_mask_radius_param_t * param, const lv_area
{
lv_coord_t w = lv_area_get_width(rect);
lv_coord_t h = lv_area_get_height(rect);
if(radius < 0) radius = 0;
int32_t short_side = LV_MIN(w, h);
if(radius > short_side >> 1) radius = short_side >> 1;
@@ -381,9 +419,47 @@ void lv_draw_mask_radius_init(lv_draw_mask_radius_param_t * param, const lv_area
param->cfg.outer = inv ? 1 : 0;
param->dsc.cb = (lv_draw_mask_xcb_t)lv_draw_mask_radius;
param->dsc.type = LV_DRAW_MASK_TYPE_RADIUS;
param->y_prev = INT32_MIN;
param->y_prev_x.f = 0;
param->y_prev_x.i = 0;
if(radius == 0) {
param->circle = NULL;
return;
}
uint32_t i;
/*Try to reuse a circle cache entry*/
for(i = 0; i < LV_CIRCLE_CACHE_SIZE; i++) {
if(LV_GC_ROOT(_lv_circle_cache[i]).radius == radius) {
LV_GC_ROOT(_lv_circle_cache[i]).used_cnt++;
CIRCLE_CACHE_AGING(LV_GC_ROOT(_lv_circle_cache[i]).life, radius);
param->circle = &LV_GC_ROOT(_lv_circle_cache[i]);
return;
}
}
/*If not found find a free entry with lowest life*/
_lv_draw_mask_radius_circle_dsc_t * entry = NULL;
for(i = 0; i < LV_CIRCLE_CACHE_SIZE; i++) {
if(LV_GC_ROOT(_lv_circle_cache[i]).used_cnt == 0) {
if(!entry) entry = &LV_GC_ROOT(_lv_circle_cache[i]);
else if(LV_GC_ROOT(_lv_circle_cache[i]).life < entry->life) entry = &LV_GC_ROOT(_lv_circle_cache[i]);
}
}
if(!entry) {
entry = lv_mem_alloc(sizeof(_lv_draw_mask_radius_circle_dsc_t));
LV_ASSERT_MALLOC(param->circle);
lv_memset_00(entry, sizeof(_lv_draw_mask_radius_circle_dsc_t));
entry->life = -1;
} else {
entry->used_cnt++;
entry->life = 0;
CIRCLE_CACHE_AGING(entry->life, radius);
}
param->circle = entry;
circ_calc_aa4(param->circle, radius);
}
/**
@@ -881,6 +957,8 @@ LV_ATTRIBUTE_FAST_MEM static lv_draw_mask_res_t lv_draw_mask_angle(lv_opa_t * ma
}
}
LV_ATTRIBUTE_FAST_MEM static lv_draw_mask_res_t lv_draw_mask_radius(lv_opa_t * mask_buf, lv_coord_t abs_x,
lv_coord_t abs_y, lv_coord_t len,
lv_draw_mask_radius_param_t * p)
@@ -891,7 +969,7 @@ LV_ATTRIBUTE_FAST_MEM static lv_draw_mask_res_t lv_draw_mask_radius(lv_opa_t * m
lv_area_copy(&rect, &p->cfg.rect);
if(outer == false) {
if(abs_y < rect.y1 || abs_y > rect.y2) {
if((abs_y < rect.y1 || abs_y > rect.y2)) {
return LV_DRAW_MASK_RES_TRANSP;
}
}
@@ -932,6 +1010,13 @@ LV_ATTRIBUTE_FAST_MEM static lv_draw_mask_res_t lv_draw_mask_radius(lv_opa_t * m
}
return LV_DRAW_MASK_RES_CHANGED;
}
// printf("exec: x:%d.. %d, y:%d: r:%d, %s\n", abs_x, abs_x + len - 1, abs_y, p->cfg.radius, p->cfg.outer ? "inv" : "norm");
// if( abs_x == 276 && abs_x + len - 1 == 479 && abs_y == 63 && p->cfg.radius == 5 && p->cfg.outer == 1) {
// char x = 0;
// }
//exec: x:276.. 479, y:63: r:5, inv)
int32_t k = rect.x1 - abs_x; /*First relevant coordinate on the of the mask*/
int32_t w = lv_area_get_width(&rect);
@@ -939,194 +1024,51 @@ LV_ATTRIBUTE_FAST_MEM static lv_draw_mask_res_t lv_draw_mask_radius(lv_opa_t * m
abs_x -= rect.x1;
abs_y -= rect.y1;
uint32_t r2 = p->cfg.radius * p->cfg.radius;
lv_coord_t aa_len;
lv_coord_t x_start;
lv_coord_t cir_y;
if(abs_y < radius) {
cir_y = radius - abs_y - 1;
} else {
cir_y = abs_y - (h - radius);
}
lv_opa_t * aa_opa = get_next_line(p->circle, cir_y, &aa_len, &x_start);
lv_coord_t cir_x_right = k + w - radius + x_start;
lv_coord_t cir_x_left = k + radius - x_start - 1;
lv_coord_t i;
/*Handle corner areas*/
if(abs_y < radius || abs_y > h - radius - 1) {
uint32_t sqrt_mask;
if(radius <= 32) sqrt_mask = 0x200;
if(radius <= 256) sqrt_mask = 0x800;
else sqrt_mask = 0x8000;
lv_sqrt_res_t x0;
lv_sqrt_res_t x1;
/*y = 0 should mean the top of the circle*/
int32_t y;
if(abs_y < radius) {
y = radius - abs_y;
/*Get the x intersection points for `abs_y` and `abs_y-1`
*Use the circle's equation x = sqrt(r^2 - y^2)
*Try to use the values from the previous run*/
if(y == p->y_prev) {
x0.f = p->y_prev_x.f;
x0.i = p->y_prev_x.i;
if(outer == false) {
for(i = 0; i < aa_len; i++) {
lv_opa_t opa = aa_opa[aa_len - i - 1];
if(cir_x_right + i >= 0 && cir_x_right + i < len) {
mask_buf[cir_x_right + i] = mask_mix(opa, mask_buf[cir_x_right + i]);
}
else {
lv_sqrt(r2 - (y * y), &x0, sqrt_mask);
}
lv_sqrt(r2 - ((y - 1) * (y - 1)), &x1, sqrt_mask);
p->y_prev = y - 1;
p->y_prev_x.f = x1.f;
p->y_prev_x.i = x1.i;
}
else {
y = radius - (h - abs_y) + 1;
/*Get the x intersection points for `abs_y` and `abs_y-1`
*Use the circle's equation x = sqrt(r^2 - y^2)
*Try to use the values from the previous run*/
if((y - 1) == p->y_prev) {
x1.f = p->y_prev_x.f;
x1.i = p->y_prev_x.i;
}
else {
lv_sqrt(r2 - ((y - 1) * (y - 1)), &x1, sqrt_mask);
}
lv_sqrt(r2 - (y * y), &x0, sqrt_mask);
p->y_prev = y;
p->y_prev_x.f = x0.f;
p->y_prev_x.i = x0.i;
}
/*If x1 is on the next round coordinate (e.g. x0: 3.5, x1:4.0)
*then treat x1 as x1: 3.99 to handle them as they were on the same pixel*/
if(x0.i == x1.i - 1 && x1.f == 0) {
x1.i--;
x1.f = 0xFF;
}
/*If the two x intersections are on the same x then just get average of the fractions*/
if(x0.i == x1.i) {
lv_opa_t m = (x0.f + x1.f) >> 1;
if(outer) m = 255 - m;
int32_t ofs = radius - x0.i - 1;
/*Left corner*/
int32_t kl = k + ofs;
if(kl >= 0 && kl < len) {
mask_buf[kl] = mask_mix(mask_buf[kl], m);
}
/*Right corner*/
int32_t kr = k + (w - ofs - 1);
if(kr >= 0 && kr < len) {
mask_buf[kr] = mask_mix(mask_buf[kr], m);
}
/*Clear the unused parts*/
if(outer == false) {
kr++;
if(kl > len) {
return LV_DRAW_MASK_RES_TRANSP;
}
if(kl >= 0) {
lv_memset_00(&mask_buf[0], kl);
}
if(kr < 0) {
return LV_DRAW_MASK_RES_TRANSP;
}
if(kr <= len) {
lv_memset_00(&mask_buf[kr], len - kr);
}
}
else {
kl++;
int32_t first = kl;
if(first < 0) first = 0;
int32_t len_tmp = kr - first;
if(len_tmp + first > len) len_tmp = len - first;
if(first < len && len_tmp >= 0) {
lv_memset_00(&mask_buf[first], len_tmp);
}
if(cir_x_left - i >= 0 && cir_x_left - i < len) {
mask_buf[cir_x_left - i] = mask_mix(opa, mask_buf[cir_x_left - i]);
}
}
/*Multiple pixels are affected. Get y intersection of the pixels*/
else {
int32_t ofs = radius - (x0.i + 1);
int32_t kl = k + ofs;
int32_t kr = k + (w - ofs - 1);
if(outer) {
int32_t first = kl + 1;
if(first < 0) first = 0;
/*Clean the right side*/
cir_x_right = LV_CLAMP(0, cir_x_right + i, len);
lv_memset_00(&mask_buf[cir_x_right], len - cir_x_right);
int32_t len_tmp = kr - first;
if(len_tmp + first > len) len_tmp = len - first;
if(first < len && len_tmp >= 0) {
lv_memset_00(&mask_buf[first], len_tmp);
}
/*Clean the left side*/
cir_x_left = LV_CLAMP(0, cir_x_left - aa_len + 1, len);
lv_memset_00(&mask_buf[0], cir_x_left);
} else {
for(i = 0; i < aa_len; i++) {
lv_opa_t opa = 255 - (aa_opa[aa_len - 1 - i]);
if(cir_x_right + i >= 0 && cir_x_right + i < len) {
mask_buf[cir_x_right + i] = mask_mix(opa, mask_buf[cir_x_right + i]);
}
uint32_t i = x0.i + 1;
lv_opa_t m;
lv_sqrt_res_t y_prev;
lv_sqrt_res_t y_next;
lv_sqrt(r2 - (x0.i * x0.i), &y_prev, sqrt_mask);
if(y_prev.f == 0) {
y_prev.i--;
y_prev.f = 0xFF;
}
/*The first y intersection is special as it might be in the previous line*/
if(y_prev.i >= y) {
lv_sqrt(r2 - (i * i), &y_next, sqrt_mask);
m = 255 - (((255 - x0.f) * (255 - y_next.f)) >> 9);
if(outer) m = 255 - m;
if(kl >= 0 && kl < len) mask_buf[kl] = mask_mix(mask_buf[kl], m);
if(kr >= 0 && kr < len) mask_buf[kr] = mask_mix(mask_buf[kr], m);
kl--;
kr++;
y_prev.f = y_next.f;
i++;
}
/*Set all points which are crossed by the circle*/
for(; i <= x1.i; i++) {
/*These values are very close to each other. It's enough to approximate sqrt
*The non-approximated version is lv_sqrt(r2 - (i * i), &y_next, sqrt_mask);*/
sqrt_approx(&y_next, &y_prev, r2 - (i * i));
m = (y_prev.f + y_next.f) >> 1;
if(outer) m = 255 - m;
if(kl >= 0 && kl < len) mask_buf[kl] = mask_mix(mask_buf[kl], m);
if(kr >= 0 && kr < len) mask_buf[kr] = mask_mix(mask_buf[kr], m);
kl--;
kr++;
y_prev.f = y_next.f;
}
/*If the last pixel was left in its middle therefore
* the circle still has parts on the next one*/
if(y_prev.f) {
m = (y_prev.f * x1.f) >> 9;
if(outer) m = 255 - m;
if(kl >= 0 && kl < len) mask_buf[kl] = mask_mix(mask_buf[kl], m);
if(kr >= 0 && kr < len) mask_buf[kr] = mask_mix(mask_buf[kr], m);
kl--;
kr++;
}
if(outer == 0) {
kl++;
if(kl > len) {
return LV_DRAW_MASK_RES_TRANSP;
}
if(kl >= 0) lv_memset_00(&mask_buf[0], kl);
if(kr < 0) {
return LV_DRAW_MASK_RES_TRANSP;
}
if(kr < len) lv_memset_00(&mask_buf[kr], len - kr);
if(cir_x_left - i >= 0 && cir_x_left - i < len) {
mask_buf[cir_x_left - i] = mask_mix(opa, mask_buf[cir_x_left - i]);
}
}
lv_coord_t clr_start = LV_CLAMP(0, cir_x_left + 1, len);
lv_coord_t clr_len = LV_CLAMP(0, cir_x_right - clr_start, len - clr_start);
lv_memset_00(&mask_buf[clr_start], clr_len);
}
return LV_DRAW_MASK_RES_CHANGED;
@@ -1212,6 +1154,191 @@ LV_ATTRIBUTE_FAST_MEM static lv_draw_mask_res_t lv_draw_mask_map(lv_opa_t * mask
return LV_DRAW_MASK_RES_CHANGED;
}
/**
* Initialize the circle drawing
* @param c pointer to a point. The coordinates will be calculated here
* @param tmp point to a variable. It will store temporary data
* @param radius radius of the circle
*/
static void circ_init(lv_point_t * c, lv_coord_t * tmp, lv_coord_t radius)
{
c->x = radius;
c->y = 0;
*tmp = 1 - radius;
}
/**
* Test the circle drawing is ready or not
* @param c same as in circ_init
* @return true if the circle is not ready yet
*/
static bool circ_cont(lv_point_t * c)
{
return c->y <= c->x ? true : false;
}
/**
* Get the next point from the circle
* @param c same as in circ_init. The next point stored here.
* @param tmp same as in circ_init.
*/
static void circ_next(lv_point_t * c, lv_coord_t * tmp)
{
if(*tmp <= 0) {
(*tmp) += 2 * c->y + 3; /*Change in decision criterion for y -> y+1*/
} else {
(*tmp) += 2 * (c->y - c->x) + 5; /*Change for y -> y+1, x -> x-1*/
c->x--;
}
c->y++;
}
static void circ_calc_aa4(_lv_draw_mask_radius_circle_dsc_t * c, lv_coord_t radius)
{
if(radius == 0) return;
c->radius = radius;
/*Allocate buffers*/
if(c->buf) lv_mem_free(c->buf);
c->buf = lv_mem_alloc(radius * 6 + 6); /*Use uint16_t for opa_start_on_y and x_start_on_y*/
c->cir_opa = c->buf;
c->opa_start_on_y = (uint16_t *) (c->buf + 2 * radius + 2);
c->x_start_on_y = (uint16_t *) (c->buf + 4 * radius + 4);
lv_coord_t * cir_x = lv_mem_buf_get((radius + 1) * 2 * 2 * sizeof(lv_coord_t));
lv_coord_t * cir_y = &cir_x[(radius + 1) * 2];
uint32_t y_8th_cnt = 0;
lv_point_t cp;
lv_coord_t tmp;
circ_init(&cp, &tmp, radius * 4); /*Upscale by 4*/
int32_t i;
uint32_t x_int[4];
uint32_t x_fract[4];
lv_coord_t cir_size = 0;
x_int[0] = cp.x >> 2;
x_fract[0] = 0;
/*Calculate an 1/8 circle*/
while(circ_cont(&cp)) {
/*Calculate 4 point of the circle */
for(i = 0; i < 4; i++) {
circ_next(&cp, &tmp);
if(circ_cont(&cp) == false) break;
x_int[i] = cp.x >> 2;
x_fract[i] = cp.x & 0x3;
}
if(i != 4) break;
/*All lines on the same x when downscaled*/
if(x_int[0] == x_int[3]) {
cir_x[cir_size] = x_int[0];
cir_y[cir_size] = y_8th_cnt;
c->cir_opa[cir_size] = x_fract[0] + x_fract[1] + x_fract[2] + x_fract[3];
c->cir_opa[cir_size] *= 16;
cir_size++;
}
/*Second line on new x when downscaled*/
else if(x_int[0] != x_int[1]) {
cir_x[cir_size] = x_int[0];
cir_y[cir_size] = y_8th_cnt;
c->cir_opa[cir_size] = x_fract[0];
c->cir_opa[cir_size] *= 16;
cir_size++;
cir_x[cir_size] = x_int[0] - 1;
cir_y[cir_size] = y_8th_cnt;
c->cir_opa[cir_size] = 1 * 4 + x_fract[1] + x_fract[2] + x_fract[3];;
c->cir_opa[cir_size] *= 16;
cir_size++;
}
/*Third line on new x when downscaled*/
else if(x_int[0] != x_int[2]) {
cir_x[cir_size] = x_int[0];
cir_y[cir_size] = y_8th_cnt;
c->cir_opa[cir_size] = x_fract[0] + x_fract[1];
c->cir_opa[cir_size] *= 16;
cir_size++;
cir_x[cir_size] = x_int[0] - 1;
cir_y[cir_size] = y_8th_cnt;
c->cir_opa[cir_size] = 2 * 4 + x_fract[2] + x_fract[3];;
c->cir_opa[cir_size] *= 16;
cir_size++;
}
/*Forth line on new x when downscaled*/
else {
cir_x[cir_size] = x_int[0];
cir_y[cir_size] = y_8th_cnt;
c->cir_opa[cir_size] = x_fract[0] + x_fract[1] + x_fract[2];
c->cir_opa[cir_size] *= 16;
cir_size++;
cir_x[cir_size] = x_int[0] - 1;
cir_y[cir_size] = y_8th_cnt;
c->cir_opa[cir_size] = 3 * 4 + x_fract[3];;
c->cir_opa[cir_size] *= 16;
cir_size++;
}
y_8th_cnt++;
}
/*The point on the 1/8 circle is special, calculate it manually*/
int32_t mid = radius * 723;
int32_t mid_int = mid >> 10;
if(cir_x[cir_size-1] != mid_int || cir_y[cir_size-1] != mid_int) {
tmp = mid - (mid_int << 10);
if(tmp <= 512) {
tmp = tmp * tmp * 2;
tmp = tmp >> (10 + 6);
} else {
tmp = 1024 - tmp;
tmp = (int32_t)tmp * tmp * 2;
tmp = (int32_t)tmp >> (10 + 6);
tmp = 15 - tmp;
}
cir_x[cir_size] = mid_int;
cir_y[cir_size] = mid_int;
c->cir_opa[cir_size] = tmp;
c->cir_opa[cir_size] *= 16;
cir_size++;
}
/*Build the second octet by mirroring the first*/
for(i = cir_size - 2; i >= 0; i--, cir_size++) {
cir_x[cir_size] = cir_y[i];
cir_y[cir_size] = cir_x[i];
c->cir_opa[cir_size] = c->cir_opa[i];
}
lv_coord_t y = 0;
i = 0;
c->opa_start_on_y[0] = 0;
while(i < cir_size) {
c->opa_start_on_y[y] = i;
c->x_start_on_y[y] = cir_x[i];
for(; cir_y[i] == y && i < (int32_t)cir_size; i++) {
c->x_start_on_y[y] = LV_MIN(c->x_start_on_y[y], cir_x[i]);
}
y++;
}
lv_mem_buf_release(cir_x);
}
static lv_opa_t * get_next_line(_lv_draw_mask_radius_circle_dsc_t * c, lv_coord_t y, lv_coord_t * len, lv_coord_t * x_start)
{
*len = c->opa_start_on_y[y + 1] - c->opa_start_on_y[y];
*x_start = c->x_start_on_y[y];
return &c->cir_opa[c->opa_start_on_y[y]];
}
LV_ATTRIBUTE_FAST_MEM static inline lv_opa_t mask_mix(lv_opa_t mask_act, lv_opa_t mask_new)
{
if(mask_new >= LV_OPA_MAX) return mask_act;
@@ -1220,24 +1347,5 @@ LV_ATTRIBUTE_FAST_MEM static inline lv_opa_t mask_mix(lv_opa_t mask_act, lv_opa_
return LV_UDIV255(mask_act * mask_new);// >> 8);
}
/**
* Approximate the sqrt near to an already calculated value
* @param q store the result here
* @param ref the reference point (already calculated sqrt)
* @param x the value which sqrt should be approximated
*/
LV_ATTRIBUTE_FAST_MEM static inline void sqrt_approx(lv_sqrt_res_t * q, lv_sqrt_res_t * ref, uint32_t x)
{
x = x << 8; /*Upscale for extra precision*/
uint32_t raw = (ref->i << 4) + (ref->f >> 4);
uint32_t raw2 = raw * raw;
int32_t d = x - raw2;
d = (int32_t)d / (int32_t)(2 * raw) + raw;
q->i = d >> 4;
q->f = (d & 0xF) << 4;
}
#endif /*LV_DRAW_COMPLEX*/

View File

@@ -147,6 +147,18 @@ typedef struct {
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;
@@ -157,9 +169,8 @@ typedef struct {
/*Invert the mask. 0: Keep the pixels inside.*/
uint8_t outer: 1;
} cfg;
int32_t y_prev;
lv_sqrt_res_t y_prev_x;
_lv_draw_mask_radius_circle_dsc_t * circle;
} lv_draw_mask_radius_param_t;
@@ -235,6 +246,21 @@ void * lv_draw_mask_remove_id(int16_t id);
*/
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
/**

View File

@@ -228,7 +228,7 @@ LV_ATTRIBUTE_FAST_MEM static void draw_bg(const lv_area_t * coords, const lv_are
y < coords_bg.y2 - rout - 1) {
mask_res = LV_DRAW_MASK_RES_FULL_COVER;
if(simple_mode == false) {
lv_memset(mask_buf, opa, draw_area_w);
lv_memset(mask_buf, 0xff, draw_area_w);
mask_res = lv_draw_mask_apply(mask_buf, draw_buf->area.x1 + draw_area.x1, draw_buf->area.y1 + h, draw_area_w);
}
}
@@ -240,7 +240,7 @@ LV_ATTRIBUTE_FAST_MEM static void draw_bg(const lv_area_t * coords, const lv_are
/*If mask will taken into account its base opacity was already set by memset above*/
if(mask_res == LV_DRAW_MASK_RES_CHANGED) {
opa2 = LV_OPA_COVER;
// opa2 = LV_OPA_COVER;
}
/*Get the current line color*/
@@ -329,6 +329,7 @@ LV_ATTRIBUTE_FAST_MEM static void draw_bg(const lv_area_t * coords, const lv_are
if(grad_map) lv_mem_buf_release(grad_map);
if(mask_buf) lv_mem_buf_release(mask_buf);
lv_draw_mask_remove_id(mask_rout_id);
if(mask_rout_id != LV_MASK_ID_INV) lv_draw_mask_free_param(&mask_rout_param);
}
#endif
@@ -509,8 +510,10 @@ LV_ATTRIBUTE_FAST_MEM static void draw_border(const lv_area_t * coords, const lv
fill_area.y2++;
}
lv_draw_mask_free_param(&mask_rin_param);
lv_draw_mask_free_param(&mask_rout_param);
lv_draw_mask_remove_id(mask_rin_id);
lv_draw_mask_remove_id(mask_rout_id);
if(mask_rout_id != LV_MASK_ID_INV) lv_draw_mask_remove_id(mask_rout_id);
lv_mem_buf_release(mask_buf);
}
#else /*LV_DRAW_COMPLEX*/
@@ -1007,6 +1010,7 @@ LV_ATTRIBUTE_FAST_MEM static void draw_shadow(const lv_area_t * coords, const lv
}
}
lv_draw_mask_free_param(&mask_rout_param);
lv_draw_mask_remove_id(mask_rout_id);
lv_mem_buf_release(mask_buf);
lv_mem_buf_release(sh_buf);
@@ -1064,6 +1068,8 @@ LV_ATTRIBUTE_FAST_MEM static void shadow_draw_corner_buf(const lv_area_t * coord
}
lv_mem_buf_release(mask_line);
lv_draw_mask_free_param(&mask_param);
if(sw == 1) {
int32_t i;
lv_opa_t * res_buf = (lv_opa_t *)sh_buf;
@@ -1394,6 +1400,8 @@ static void draw_full_border(const lv_area_t * area_inner, const lv_area_t * are
}
}
lv_draw_mask_free_param(&mask_rin_param);
lv_draw_mask_free_param(&mask_rout_param);
lv_draw_mask_remove_id(mask_rin_id);
lv_draw_mask_remove_id(mask_rout_id);
lv_mem_buf_release(mask_buf);

View File

@@ -287,6 +287,8 @@ static void draw_disc_grad(lv_event_t * e)
}
#if LV_DRAW_COMPLEX
lv_draw_mask_free_param(&mask_out_param);
lv_draw_mask_free_param(&mask_in_param);
lv_draw_mask_remove_id(mask_out_id);
lv_draw_mask_remove_id(mask_in_id);
#endif

View File

@@ -414,7 +414,7 @@ static void draw_ticks_and_labels(lv_obj_t * obj, const lv_area_t * clip_area, c
lv_draw_mask_radius_init(&outer_mask, &area_outer, LV_RADIUS_CIRCLE, false);
int16_t outer_mask_id = lv_draw_mask_add(&outer_mask, NULL);
int16_t inner_act_mask_id = -1; /*Will be added later*/
int16_t inner_act_mask_id = LV_MASK_ID_INV; /*Will be added later*/
uint32_t minor_cnt = scale->tick_major_nth ? scale->tick_major_nth - 1 : 0xFFFF;
for(i = 0; i < scale->tick_cnt; i++) {
@@ -530,6 +530,9 @@ static void draw_ticks_and_labels(lv_obj_t * obj, const lv_area_t * clip_area, c
line_dsc.width = line_width_ori;
}
lv_draw_mask_free_param(&inner_minor_mask);
lv_draw_mask_free_param(&inner_major_mask);
lv_draw_mask_free_param(&outer_mask);
lv_draw_mask_remove_id(outer_mask_id);
}
}

View File

@@ -250,6 +250,19 @@
# define LV_SHADOW_CACHE_SIZE 0
# endif
#endif
/* Set number of maximally cached circle data.
* The circumference of 1/4 circle are saved for anti-aliasing
* radius * 4 bytes are used per circle (the most often used radiuses are saved)
* 0: to disable caching */
#ifndef LV_CIRCLE_CACHE_SIZE
# ifdef CONFIG_LV_CIRCLE_CACHE_SIZE
# define LV_CIRCLE_CACHE_SIZE CONFIG_LV_CIRCLE_CACHE_SIZE
# else
# define LV_CIRCLE_CACHE_SIZE 4
# endif
#endif
#endif /*LV_DRAW_COMPLEX*/
/*Default image cache size. Image caching keeps the images opened.

View File

@@ -459,7 +459,15 @@ static inline uint32_t lv_color_to32(lv_color_t color)
LV_ATTRIBUTE_FAST_MEM static inline lv_color_t lv_color_mix(lv_color_t c1, lv_color_t c2, uint8_t mix)
{
lv_color_t ret;
#if LV_COLOR_DEPTH != 1
#if LV_COLOR_DEPTH == 16 && LV_COLOR_16_SWAP == 0
/*Source: https://stackoverflow.com/a/50012418/1999969*/
mix = ( mix + 4 ) >> 3;
uint32_t bg = (c2.full | (c2.full << 16)) & 0x7E0F81F; /*0b00000111111000001111100000011111*/
uint32_t fg = (c1.full | (c1.full << 16)) & 0x7E0F81F;
uint32_t result = ((((fg - bg) * mix) >> 5) + bg) & 0x7E0F81F;
ret.full = (uint16_t)((result >> 16) | result);
#elif LV_COLOR_DEPTH != 1
/*LV_COLOR_DEPTH == 8, 16 or 32*/
LV_COLOR_SET_R(ret, LV_UDIV255((uint16_t) LV_COLOR_GET_R(c1) * mix + LV_COLOR_GET_R(c2) *
(255 - mix) + LV_COLOR_MIX_ROUND_OFS));

View File

@@ -53,6 +53,7 @@ extern "C" {
LV_DISPATCH_COND(f, _lv_img_cache_entry_t, _lv_img_cache_single, LV_IMG_CACHE_DEF, 0) \
LV_DISPATCH(f, lv_timer_t*, _lv_timer_act) \
LV_DISPATCH(f, lv_mem_buf_arr_t , lv_mem_buf) \
LV_DISPATCH_COND(f, _lv_draw_mask_radius_circle_dsc_arr_t , _lv_circle_cache, LV_DRAW_COMPLEX, 1) \
LV_DISPATCH_COND(f, _lv_draw_mask_saved_arr_t , _lv_draw_mask_list, LV_DRAW_COMPLEX, 1) \
LV_DISPATCH(f, void * , _lv_theme_default_styles) \
LV_DISPATCH_COND(f, uint8_t *, _lv_font_decompr_buf, LV_USE_FONT_COMPRESSED, 1)

View File

@@ -486,6 +486,8 @@ static void draw_indic(lv_event_t * e)
lv_draw_rect(&bar->indic_area, clip_area, &draw_rect_dsc);
#if LV_DRAW_COMPLEX
lv_draw_mask_free_param(&mask_indic_param);
lv_draw_mask_free_param(&mask_bg_param);
lv_draw_mask_remove_id(mask_indic_id);
lv_draw_mask_remove_id(mask_bg_id);
#endif

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 17 KiB