From 637b706ddc7adc237e8d63292068d2b859843843 Mon Sep 17 00:00:00 2001 From: Gabor Kiss-Vamosi Date: Tue, 27 Jul 2021 19:16:00 +0200 Subject: [PATCH] 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 --- docs/overview/drawing.md | 27 +- examples/widgets/chart/lv_example_chart_2.c | 2 + examples/widgets/roller/lv_example_roller_3.c | 2 + lv_conf_template.h | 7 + src/core/lv_indev.c | 5 +- src/core/lv_indev_scroll.c | 4 +- src/core/lv_obj.c | 3 +- src/core/lv_obj_pos.c | 6 +- src/core/lv_obj_scroll.c | 8 +- src/core/lv_obj_style.c | 4 +- src/core/lv_refr.c | 10 +- src/draw/lv_draw_arc.c | 7 + src/draw/lv_draw_blend.c | 148 +++-- src/draw/lv_draw_line.c | 4 + src/draw/lv_draw_mask.c | 526 +++++++++++------- src/draw/lv_draw_mask.h | 30 +- src/draw/lv_draw_rect.c | 14 +- src/extra/widgets/colorwheel/lv_colorwheel.c | 2 + src/extra/widgets/meter/lv_meter.c | 5 +- src/lv_conf_internal.h | 13 + src/misc/lv_color.h | 10 +- src/misc/lv_gc.h | 1 + src/widgets/lv_bar.c | 2 + tests/ref_imgs/dropdown_1.png | Bin 12842 -> 12426 bytes tests/ref_imgs/dropdown_2.png | Bin 18285 -> 17458 bytes 25 files changed, 517 insertions(+), 323 deletions(-) diff --git a/docs/overview/drawing.md b/docs/overview/drawing.md index cf85bdbf1..f4fb7bb96 100644 --- a/docs/overview/drawing.md +++ b/docs/overview/drawing.md @@ -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__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(¶m, 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(¶m)`. + +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. diff --git a/examples/widgets/chart/lv_example_chart_2.c b/examples/widgets/chart/lv_example_chart_2.c index cd1d85fef..a300a74a6 100644 --- a/examples/widgets/chart/lv_example_chart_2.c +++ b/examples/widgets/chart/lv_example_chart_2.c @@ -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); } diff --git a/examples/widgets/roller/lv_example_roller_3.c b/examples/widgets/roller/lv_example_roller_3.c index 1f43841ca..27e616632 100644 --- a/examples/widgets/roller/lv_example_roller_3.c +++ b/examples/widgets/roller/lv_example_roller_3.c @@ -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; } diff --git a/lv_conf_template.h b/lv_conf_template.h index 4de79ad98..fd5698136 100644 --- a/lv_conf_template.h +++ b/lv_conf_template.h @@ -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. diff --git a/src/core/lv_indev.c b/src/core/lv_indev.c index 1fd72d17b..62b70e7b8 100644 --- a/src/core/lv_indev.c +++ b/src/core/lv_indev.c @@ -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*/ diff --git a/src/core/lv_indev_scroll.c b/src/core/lv_indev_scroll.c index a582f1576..eeb492abf 100644 --- a/src/core/lv_indev_scroll.c +++ b/src/core/lv_indev_scroll.c @@ -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; diff --git a/src/core/lv_obj.c b/src/core/lv_obj.c index 63d1f0989..767e51db4 100644 --- a/src/core/lv_obj.c +++ b/src/core/lv_obj.c @@ -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); } } diff --git a/src/core/lv_obj_pos.c b/src/core/lv_obj_pos.c index 48c2a0ec9..bb8da3b50 100644 --- a/src/core/lv_obj_pos.c +++ b/src/core/lv_obj_pos.c @@ -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; diff --git a/src/core/lv_obj_scroll.c b/src/core/lv_obj_scroll.c index c0f35178e..111b1fbd3 100644 --- a/src/core/lv_obj_scroll.c +++ b/src/core/lv_obj_scroll.c @@ -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); } diff --git a/src/core/lv_obj_style.c b/src/core/lv_obj_style.c index 260025379..39be28e22 100644 --- a/src/core/lv_obj_style.c +++ b/src/core/lv_obj_style.c @@ -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); diff --git a/src/core/lv_refr.c b/src/core/lv_refr.c index d5b46aa76..f90ed5a82 100644 --- a/src/core/lv_refr.c +++ b/src/core/lv_refr.c @@ -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; diff --git a/src/draw/lv_draw_arc.c b/src/draw/lv_draw_arc.c index b3c975a1b..ac54f310e 100644 --- a/src/draw/lv_draw_arc.c +++ b/src/draw/lv_draw_arc.c @@ -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 diff --git a/src/draw/lv_draw_blend.c b/src/draw/lv_draw_blend.c index 336f13c23..754906490 100644 --- a/src/draw/lv_draw_blend.c +++ b/src/draw/lv_draw_blend.c @@ -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 diff --git a/src/draw/lv_draw_line.c b/src/draw/lv_draw_line.c index ae0501544..50150d1a9 100644 --- a/src/draw/lv_draw_line.c +++ b/src/draw/lv_draw_line.c @@ -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); diff --git a/src/draw/lv_draw_mask.c b/src/draw/lv_draw_mask.c index 2d8c862fe..69fccc78a 100644 --- a/src/draw/lv_draw_mask.c +++ b/src/draw/lv_draw_mask.c @@ -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*/ diff --git a/src/draw/lv_draw_mask.h b/src/draw/lv_draw_mask.h index 893040494..d0d4fb08c 100644 --- a/src/draw/lv_draw_mask.h +++ b/src/draw/lv_draw_mask.h @@ -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 /** diff --git a/src/draw/lv_draw_rect.c b/src/draw/lv_draw_rect.c index 59953ac70..e66bfef9c 100644 --- a/src/draw/lv_draw_rect.c +++ b/src/draw/lv_draw_rect.c @@ -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); diff --git a/src/extra/widgets/colorwheel/lv_colorwheel.c b/src/extra/widgets/colorwheel/lv_colorwheel.c index 4d2853745..ebae114b1 100644 --- a/src/extra/widgets/colorwheel/lv_colorwheel.c +++ b/src/extra/widgets/colorwheel/lv_colorwheel.c @@ -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 diff --git a/src/extra/widgets/meter/lv_meter.c b/src/extra/widgets/meter/lv_meter.c index 1cddca946..71594e93b 100644 --- a/src/extra/widgets/meter/lv_meter.c +++ b/src/extra/widgets/meter/lv_meter.c @@ -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); } } diff --git a/src/lv_conf_internal.h b/src/lv_conf_internal.h index 74224d315..5bcd5b1fa 100644 --- a/src/lv_conf_internal.h +++ b/src/lv_conf_internal.h @@ -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. diff --git a/src/misc/lv_color.h b/src/misc/lv_color.h index fffd7dabd..dd78748cc 100644 --- a/src/misc/lv_color.h +++ b/src/misc/lv_color.h @@ -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)); diff --git a/src/misc/lv_gc.h b/src/misc/lv_gc.h index 00b5fee7d..a5f7a19fd 100644 --- a/src/misc/lv_gc.h +++ b/src/misc/lv_gc.h @@ -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) diff --git a/src/widgets/lv_bar.c b/src/widgets/lv_bar.c index 6f119ae02..bf586007c 100644 --- a/src/widgets/lv_bar.c +++ b/src/widgets/lv_bar.c @@ -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 diff --git a/tests/ref_imgs/dropdown_1.png b/tests/ref_imgs/dropdown_1.png index 1cf57d430e298b3d9d0609a658b6772c06fc8904..c22d4d45d24737952843dbda8ee169ff56691c2e 100644 GIT binary patch literal 12426 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 literal 12842 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 D;U^?5