diff --git a/CHANGELOG.md b/CHANGELOG.md index e607b8987..44375b440 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,8 @@ ## v7.4.0 (planned on 18.08.2020) *Available in the `dev` branch* +- Reduce code size by adding: `LV_USE_FONT_COMPRESSED` and `LV_FONT_USE_SUBPX` and applying some optimization + ## v7.3.0 (planned on 04.08.2020) *Available in the `master` branch* diff --git a/lv_conf_template.h b/lv_conf_template.h index e42d722c5..fcbe9175b 100644 --- a/lv_conf_template.h +++ b/lv_conf_template.h @@ -396,11 +396,15 @@ typedef void * lv_indev_drv_user_data_t; /*Type of user data in the i */ #define LV_USE_FONT_COMPRESSED 1 +/* Enable subpixel rendering */ +#define LV_USE_FONT_SUBPX 1 +#if LV_USE_FONT_SUBPX /* Set the pixel order of the display. * Important only if "subpx fonts" are used. * With "normal" font it doesn't matter. */ #define LV_FONT_SUBPX_BGR 0 +#endif /*Declare the type of the user data of fonts (can be e.g. `void *`, `int`, `struct`)*/ typedef void * lv_font_user_data_t; diff --git a/src/lv_conf_internal.h b/src/lv_conf_internal.h index 135e8c374..7f86b0eae 100644 --- a/src/lv_conf_internal.h +++ b/src/lv_conf_internal.h @@ -589,6 +589,18 @@ e.g. "stm32f769xx.h" or "stm32f429xx.h" */ #define LV_FONT_FMT_TXT_LARGE 0 #endif +/* Enables/disables support for compressed fonts. If it's disabled, compressed + * glyphs cannot be processed by the library and won't be rendered. + */ +#ifndef LV_USE_FONT_COMPRESSED +#define LV_USE_FONT_COMPRESSED 1 +#endif + +/* Enable subpixel rendering */ +#ifndef LV_USE_FONT_SUBPX +#define LV_USE_FONT_SUBPX 1 +#endif +#if LV_USE_FONT_SUBPX /* Set the pixel order of the display. * Important only if "subpx fonts" are used. * With "normal" font it doesn't matter. @@ -596,6 +608,7 @@ e.g. "stm32f769xx.h" or "stm32f429xx.h" */ #ifndef LV_FONT_SUBPX_BGR #define LV_FONT_SUBPX_BGR 0 #endif +#endif /*Declare the type of the user data of fonts (can be e.g. `void *`, `int`, `struct`)*/ diff --git a/src/lv_draw/lv_draw_label.c b/src/lv_draw/lv_draw_label.c index 136b4d0d7..ac1b21086 100644 --- a/src/lv_draw/lv_draw_label.c +++ b/src/lv_draw/lv_draw_label.c @@ -607,6 +607,7 @@ LV_ATTRIBUTE_FAST_MEM static void draw_letter_normal(lv_coord_t pos_x, lv_coord_ static void draw_letter_subpx(lv_coord_t pos_x, lv_coord_t pos_y, lv_font_glyph_dsc_t * g, const lv_area_t * clip_area, const uint8_t * map_p, lv_color_t color, lv_opa_t opa, lv_blend_mode_t blend_mode) { +#if LV_USE_FONT_SUBPX const uint8_t * bpp_opa_table; uint32_t bitmask_init; uint32_t bitmask; @@ -806,6 +807,9 @@ static void draw_letter_subpx(lv_coord_t pos_x, lv_coord_t pos_y, lv_font_glyph_ _lv_mem_buf_release(mask_buf); _lv_mem_buf_release(color_buf); +#else + LV_LOG_WARN("Can't draw sub-pixel rendered letter because LV_USE_FONT_SUBPX == 0 in lv_conf.h"); +#endif } diff --git a/src/lv_draw/lv_draw_rect.c b/src/lv_draw/lv_draw_rect.c index 47ed3dc70..41a7fab26 100644 --- a/src/lv_draw/lv_draw_rect.c +++ b/src/lv_draw/lv_draw_rect.c @@ -41,6 +41,7 @@ LV_ATTRIBUTE_FAST_MEM static void shadow_blur_corner(lv_coord_t size, lv_coord_t #endif static void draw_pattern(const lv_area_t * coords, const lv_area_t * clip, lv_draw_rect_dsc_t * dsc); static void draw_value(const lv_area_t * coords, const lv_area_t * clip, lv_draw_rect_dsc_t * dsc); +static void draw_full_border(const lv_area_t * area_inner, const lv_area_t * area_outer, const lv_area_t * clip, lv_coord_t radius, lv_color_t color, lv_opa_t opa, lv_blend_mode_t blend_mode); /********************** * STATIC VARIABLES @@ -370,41 +371,6 @@ LV_ATTRIBUTE_FAST_MEM static void draw_border(const lv_area_t * coords, const lv if(dsc->border_width == 0) return; if(dsc->border_side == LV_BORDER_SIDE_NONE) return; - lv_opa_t opa = dsc->border_opa; - - if(opa > LV_OPA_MAX) opa = LV_OPA_COVER; - - lv_disp_t * disp = _lv_refr_get_disp_refreshing(); - lv_disp_buf_t * vdb = lv_disp_get_buf(disp); - - /* Get clipped fill area which is the real draw area. - * It is always the same or inside `fill_area` */ - lv_area_t draw_area; - bool is_common; - is_common = _lv_area_intersect(&draw_area, coords, clip); - if(is_common == false) return; - - const lv_area_t * disp_area = &vdb->area; - - /* Now `draw_area` has absolute coordinates. - * Make it relative to `disp_area` to simplify draw to `disp_buf`*/ - draw_area.x1 -= disp_area->x1; - draw_area.y1 -= disp_area->y1; - draw_area.x2 -= disp_area->x1; - draw_area.y2 -= disp_area->y1; - - int32_t draw_area_w = lv_area_get_width(&draw_area); - - /*Create a mask if there is a radius*/ - lv_opa_t * mask_buf = _lv_mem_buf_get(draw_area_w); - - uint8_t other_mask_cnt = lv_draw_mask_get_cnt(); - bool simple_mode = true; - if(other_mask_cnt) simple_mode = false; - else if(dsc->border_side != LV_BORDER_SIDE_FULL) simple_mode = false; - - int16_t mask_rout_id = LV_MASK_ID_INV; - int32_t coords_w = lv_area_get_width(coords); int32_t coords_h = lv_area_get_height(coords); @@ -413,133 +379,73 @@ LV_ATTRIBUTE_FAST_MEM static void draw_border(const lv_area_t * coords, const lv int32_t short_side = LV_MATH_MIN(coords_w, coords_h); if(rout > short_side >> 1) rout = short_side >> 1; - /*Get the outer area*/ - lv_draw_mask_radius_param_t mask_rout_param; - if(rout > 0) { - lv_draw_mask_radius_init(&mask_rout_param, coords, rout, false); - mask_rout_id = lv_draw_mask_add(&mask_rout_param, NULL); - } - - /*Get the inner radius*/ - int32_t rin = rout - dsc->border_width; - if(rin < 0) rin = 0; - /*Get the inner area*/ - lv_area_t area_small; - lv_area_copy(&area_small, coords); - area_small.x1 += ((dsc->border_side & LV_BORDER_SIDE_LEFT) ? dsc->border_width : - (dsc->border_width + rout)); - area_small.x2 -= ((dsc->border_side & LV_BORDER_SIDE_RIGHT) ? dsc->border_width : - (dsc->border_width + rout)); - area_small.y1 += ((dsc->border_side & LV_BORDER_SIDE_TOP) ? dsc->border_width : - (dsc->border_width + rout)); - area_small.y2 -= ((dsc->border_side & LV_BORDER_SIDE_BOTTOM) ? dsc->border_width : - (dsc->border_width + rout)); + lv_area_t area_inner; + lv_area_copy(&area_inner, coords); + area_inner.x1 += ((dsc->border_side & LV_BORDER_SIDE_LEFT) ? dsc->border_width : - (dsc->border_width + rout)); + area_inner.x2 -= ((dsc->border_side & LV_BORDER_SIDE_RIGHT) ? dsc->border_width : - (dsc->border_width + rout)); + area_inner.y1 += ((dsc->border_side & LV_BORDER_SIDE_TOP) ? dsc->border_width : - (dsc->border_width + rout)); + area_inner.y2 -= ((dsc->border_side & LV_BORDER_SIDE_BOTTOM) ? dsc->border_width : - (dsc->border_width + rout)); - /*Create inner the mask*/ - lv_draw_mask_radius_param_t mask_rin_param; - lv_draw_mask_radius_init(&mask_rin_param, &area_small, rout - dsc->border_width, true); - int16_t mask_rin_id = lv_draw_mask_add(&mask_rin_param, NULL); - - int32_t corner_size = LV_MATH_MAX(rout, dsc->border_width - 1); - - int32_t h; - lv_draw_mask_res_t mask_res; - lv_area_t fill_area; - - lv_color_t color = dsc->border_color; - lv_blend_mode_t blend_mode = dsc->border_blend_mode; - - /*Apply some optimization if there is no other mask*/ - if(simple_mode) { - /*Draw the upper corner area*/ - int32_t upper_corner_end = coords->y1 - disp_area->y1 + corner_size; - upper_corner_end = LV_MATH_MIN(upper_corner_end, draw_area.y2); - fill_area.x1 = coords->x1; - fill_area.x2 = coords->x2; - fill_area.y1 = disp_area->y1 + draw_area.y1; - fill_area.y2 = fill_area.y1; - for(h = draw_area.y1; h <= upper_corner_end; h++) { - _lv_memset_ff(mask_buf, draw_area_w); - mask_res = lv_draw_mask_apply(mask_buf, vdb->area.x1 + draw_area.x1, vdb->area.y1 + h, draw_area_w); - - lv_area_t fill_area2; - fill_area2.y1 = fill_area.y1; - fill_area2.y2 = fill_area.y2; - - fill_area2.x1 = coords->x1; - fill_area2.x2 = coords->x1 + rout - 1; - - _lv_blend_fill(clip, &fill_area2, color, mask_buf, mask_res, opa, blend_mode); - - /*Draw the top horizontal line*/ - if(fill_area2.y2 < coords->y1 + dsc->border_width) { - fill_area2.x1 = coords->x1 + rout; - fill_area2.x2 = coords->x2 - rout; - - _lv_blend_fill(clip, &fill_area2, color, NULL, LV_DRAW_MASK_RES_FULL_COVER, opa, blend_mode); - } - - fill_area2.x1 = coords->x2 - rout + 1; - fill_area2.x2 = coords->x2; - - int32_t mask_ofs = (coords->x2 - rout + 1) - (vdb->area.x1 + draw_area.x1); - if(mask_ofs < 0) mask_ofs = 0; - _lv_blend_fill(clip, &fill_area2, color, mask_buf + mask_ofs, mask_res, opa, blend_mode); - - fill_area.y1++; - fill_area.y2++; - } - - /*Draw the lower corner area */ - int32_t lower_corner_end = coords->y2 - disp_area->y1 - corner_size; - lower_corner_end = LV_MATH_MAX(lower_corner_end, draw_area.y1); - if(lower_corner_end <= upper_corner_end) lower_corner_end = upper_corner_end + 1; - fill_area.y1 = disp_area->y1 + lower_corner_end; - fill_area.y2 = fill_area.y1; - for(h = lower_corner_end; h <= draw_area.y2; h++) { - _lv_memset_ff(mask_buf, draw_area_w); - mask_res = lv_draw_mask_apply(mask_buf, vdb->area.x1 + draw_area.x1, vdb->area.y1 + h, draw_area_w); - - lv_area_t fill_area2; - fill_area2.x1 = coords->x1; - fill_area2.x2 = coords->x1 + rout - 1; - fill_area2.y1 = fill_area.y1; - fill_area2.y2 = fill_area.y2; - - _lv_blend_fill(clip, &fill_area2, color, mask_buf, mask_res, opa, blend_mode); - - /*Draw the bottom horizontal line*/ - if(fill_area2.y2 > coords->y2 - dsc->border_width) { - fill_area2.x1 = coords->x1 + rout; - fill_area2.x2 = coords->x2 - rout; - - _lv_blend_fill(clip, &fill_area2, color, NULL, LV_DRAW_MASK_RES_FULL_COVER, opa, blend_mode); - } - fill_area2.x1 = coords->x2 - rout + 1; - fill_area2.x2 = coords->x2; - - int32_t mask_ofs = (coords->x2 - rout + 1) - (vdb->area.x1 + draw_area.x1); - if(mask_ofs < 0) mask_ofs = 0; - _lv_blend_fill(clip, &fill_area2, color, mask_buf + mask_ofs, mask_res, opa, blend_mode); - - - fill_area.y1++; - fill_area.y2++; - } - - /*Draw the left vertical border part*/ - fill_area.y1 = coords->y1 + corner_size + 1; - fill_area.y2 = coords->y2 - corner_size - 1; - - fill_area.x1 = coords->x1; - fill_area.x2 = coords->x1 + dsc->border_width - 1; - _lv_blend_fill(clip, &fill_area, color, NULL, LV_DRAW_MASK_RES_FULL_COVER, opa, blend_mode); - - /*Draw the right vertical border*/ - fill_area.x1 = coords->x2 - dsc->border_width + 1; - fill_area.x2 = coords->x2; - - _lv_blend_fill(clip, &fill_area, color, NULL, LV_DRAW_MASK_RES_FULL_COVER, opa, blend_mode); + if(dsc->border_side == LV_BORDER_SIDE_FULL) { + draw_full_border(&area_inner, coords, clip, dsc->radius, dsc->border_color, dsc->border_opa, dsc->border_blend_mode); } - /*Process line by line if there is other mask too*/ else { + lv_opa_t opa = dsc->border_opa; + if(opa > LV_OPA_MAX) opa = LV_OPA_COVER; + + lv_disp_t * disp = _lv_refr_get_disp_refreshing(); + lv_disp_buf_t * vdb = lv_disp_get_buf(disp); + + /* Get clipped fill area which is the real draw area. + * It is always the same or inside `fill_area` */ + lv_area_t draw_area; + bool is_common; + is_common = _lv_area_intersect(&draw_area, coords, clip); + if(is_common == false) return; + + const lv_area_t * disp_area = &vdb->area; + + /* Now `draw_area` has absolute coordinates. + * Make it relative to `disp_area` to simplify draw to `disp_buf`*/ + draw_area.x1 -= disp_area->x1; + draw_area.y1 -= disp_area->y1; + draw_area.x2 -= disp_area->x1; + draw_area.y2 -= disp_area->y1; + + int32_t draw_area_w = lv_area_get_width(&draw_area); + + /*Create a mask if there is a radius*/ + lv_opa_t * mask_buf = _lv_mem_buf_get(draw_area_w); + + uint8_t other_mask_cnt = lv_draw_mask_get_cnt(); + bool simple_mode = true; + if(other_mask_cnt) simple_mode = false; + + /*Create mask for the outer area*/ + int16_t mask_rout_id = LV_MASK_ID_INV; + lv_draw_mask_radius_param_t mask_rout_param; + if(rout > 0) { + lv_draw_mask_radius_init(&mask_rout_param, coords, rout, false); + mask_rout_id = lv_draw_mask_add(&mask_rout_param, NULL); + } + + /*Create mask for the inner mask*/ + int32_t rin = rout - dsc->border_width; + if(rin < 0) rin = 0; + lv_draw_mask_radius_param_t mask_rin_param; + lv_draw_mask_radius_init(&mask_rin_param, &area_inner, rout - dsc->border_width, true); + int16_t mask_rin_id = lv_draw_mask_add(&mask_rin_param, NULL); + + int32_t corner_size = LV_MATH_MAX(rout, dsc->border_width - 1); + + int32_t h; + lv_draw_mask_res_t mask_res; + lv_area_t fill_area; + + lv_color_t color = dsc->border_color; + lv_blend_mode_t blend_mode = dsc->border_blend_mode; + fill_area.x1 = coords->x1; fill_area.x2 = coords->x2; fill_area.y1 = disp_area->y1 + draw_area.y1; @@ -565,8 +471,8 @@ LV_ATTRIBUTE_FAST_MEM static void draw_border(const lv_area_t * coords, const lv for(h = draw_area.y1; h <= draw_area.y2; h++) { if(normal || - (top_only && fill_area.y1 <= coords->y1 + corner_size) || - (bottom_only && fill_area.y1 >= coords->y2 - corner_size)) { + (top_only && fill_area.y1 <= coords->y1 + corner_size) || + (bottom_only && fill_area.y1 >= coords->y2 - corner_size)) { _lv_memset_ff(mask_buf, draw_area_w); mask_res = lv_draw_mask_apply(mask_buf, vdb->area.x1 + draw_area.x1, vdb->area.y1 + h, draw_area_w); _lv_blend_fill(clip, &fill_area, color, mask_buf + buf_ofs, mask_res, opa, blend_mode); @@ -575,10 +481,10 @@ LV_ATTRIBUTE_FAST_MEM static void draw_border(const lv_area_t * coords, const lv fill_area.y2++; } + lv_draw_mask_remove_id(mask_rin_id); + lv_draw_mask_remove_id(mask_rout_id); + _lv_mem_buf_release(mask_buf); } - lv_draw_mask_remove_id(mask_rin_id); - lv_draw_mask_remove_id(mask_rout_id); - _lv_mem_buf_release(mask_buf); } LV_ATTRIBUTE_FAST_MEM static inline lv_color_t grad_get(lv_draw_rect_dsc_t * dsc, lv_coord_t s, lv_coord_t i) @@ -1235,10 +1141,6 @@ static void draw_outline(const lv_area_t * coords, const lv_area_t * clip, lv_dr if(opa > LV_OPA_MAX) opa = LV_OPA_COVER; - uint8_t other_mask_cnt = lv_draw_mask_get_cnt(); - bool simple_mode = true; - if(other_mask_cnt) simple_mode = false; - /*Get the inner radius*/ lv_area_t area_inner; lv_area_copy(&area_inner, coords); @@ -1247,16 +1149,6 @@ static void draw_outline(const lv_area_t * coords, const lv_area_t * clip, lv_dr area_inner.x2 += dsc->outline_pad; area_inner.y2 += dsc->outline_pad; - int32_t inner_w = lv_area_get_width(&area_inner); - int32_t inner_h = lv_area_get_height(&area_inner); - - int32_t rin = dsc->radius; - int32_t short_side = LV_MATH_MIN(inner_w, inner_h); - if(rin > short_side >> 1) rin = short_side >> 1; - - /*Get the outer area*/ - int32_t rout = rin + dsc->outline_width; - lv_area_t area_outer; lv_area_copy(&area_outer, &area_inner); @@ -1265,163 +1157,7 @@ static void draw_outline(const lv_area_t * coords, const lv_area_t * clip, lv_dr area_outer.y1 -= dsc->outline_width; area_outer.y2 += dsc->outline_width; - int32_t coords_out_w = lv_area_get_width(&area_outer); - int32_t coords_out_h = lv_area_get_height(&area_outer); - short_side = LV_MATH_MIN(coords_out_w, coords_out_h); - if(rout > short_side >> 1) rout = short_side >> 1; - - lv_disp_t * disp = _lv_refr_get_disp_refreshing(); - lv_disp_buf_t * vdb = lv_disp_get_buf(disp); - - /* Get clipped fill area which is the real draw area. - * It is always the same or inside `fill_area` */ - lv_area_t draw_area; - bool is_common; - is_common = _lv_area_intersect(&draw_area, &area_outer, clip); - if(is_common == false) return; - - const lv_area_t * disp_area = &vdb->area; - - /* Now `draw_area` has absolute coordinates. - * Make it relative to `disp_area` to simplify draw to `disp_buf`*/ - draw_area.x1 -= disp_area->x1; - draw_area.y1 -= disp_area->y1; - draw_area.x2 -= disp_area->x1; - draw_area.y2 -= disp_area->y1; - - int32_t draw_area_w = lv_area_get_width(&draw_area); - - /*Create inner the mask*/ - lv_draw_mask_radius_param_t mask_rin_param; - lv_draw_mask_radius_init(&mask_rin_param, &area_inner, rin, true); - int16_t mask_rin_id = lv_draw_mask_add(&mask_rin_param, NULL); - - lv_draw_mask_radius_param_t mask_rout_param; - lv_draw_mask_radius_init(&mask_rout_param, &area_outer, rout, false); - int16_t mask_rout_id = lv_draw_mask_add(&mask_rout_param, NULL); - - lv_opa_t * mask_buf = _lv_mem_buf_get(draw_area_w); - - int32_t corner_size = LV_MATH_MAX(rout, dsc->outline_width - 1); - - int32_t h; - lv_draw_mask_res_t mask_res; - lv_area_t fill_area; - - lv_color_t color = dsc->outline_color; - lv_blend_mode_t blend_mode = dsc->outline_blend_mode; - - /*Apply some optimization if there is no other mask*/ - if(simple_mode) { - /*Draw the upper corner area*/ - int32_t upper_corner_end = area_outer.y1 - disp_area->y1 + corner_size; - - fill_area.x1 = area_outer.x1; - fill_area.x2 = area_outer.x2; - fill_area.y1 = disp_area->y1 + draw_area.y1; - fill_area.y2 = fill_area.y1; - for(h = draw_area.y1; h <= upper_corner_end; h++) { - _lv_memset_ff(mask_buf, draw_area_w); - mask_res = lv_draw_mask_apply(mask_buf, vdb->area.x1 + draw_area.x1, vdb->area.y1 + h, draw_area_w); - - lv_area_t fill_area2; - fill_area2.y1 = fill_area.y1; - fill_area2.y2 = fill_area.y2; - - fill_area2.x1 = area_outer.x1; - fill_area2.x2 = area_outer.x1 + rout - 1; - - _lv_blend_fill(clip, &fill_area2, color, mask_buf, mask_res, opa, blend_mode); - - /*Draw the top horizontal line*/ - if(fill_area2.y2 < area_outer.y1 + dsc->outline_width) { - fill_area2.x1 = area_outer.x1 + rout; - fill_area2.x2 = area_outer.x2 - rout; - - _lv_blend_fill(clip, &fill_area2, color, NULL, LV_DRAW_MASK_RES_FULL_COVER, opa, blend_mode); - } - - fill_area2.x1 = area_outer.x2 - rout + 1; - fill_area2.x2 = area_outer.x2; - - int32_t mask_ofs = (area_outer.x2 - rout + 1) - (vdb->area.x1 + draw_area.x1); - if(mask_ofs < 0) mask_ofs = 0; - _lv_blend_fill(clip, &fill_area2, color, mask_buf + mask_ofs, mask_res, opa, blend_mode); - - fill_area.y1++; - fill_area.y2++; - } - - /*Draw the lower corner area */ - int32_t lower_corner_end = area_outer.y2 - disp_area->y1 - corner_size; - if(lower_corner_end <= upper_corner_end) lower_corner_end = upper_corner_end + 1; - fill_area.y1 = disp_area->y1 + lower_corner_end; - fill_area.y2 = fill_area.y1; - for(h = lower_corner_end; h <= draw_area.y2; h++) { - _lv_memset_ff(mask_buf, draw_area_w); - mask_res = lv_draw_mask_apply(mask_buf, vdb->area.x1 + draw_area.x1, vdb->area.y1 + h, draw_area_w); - - lv_area_t fill_area2; - fill_area2.x1 = area_outer.x1; - fill_area2.x2 = area_outer.x1 + rout - 1; - fill_area2.y1 = fill_area.y1; - fill_area2.y2 = fill_area.y2; - - _lv_blend_fill(clip, &fill_area2, color, mask_buf, mask_res, opa, blend_mode); - - /*Draw the bottom horizontal line*/ - if(fill_area2.y2 > area_outer.y2 - dsc->outline_width) { - fill_area2.x1 = area_outer.x1 + rout; - fill_area2.x2 = area_outer.x2 - rout; - - _lv_blend_fill(clip, &fill_area2, color, NULL, LV_DRAW_MASK_RES_FULL_COVER, opa, blend_mode); - } - fill_area2.x1 = area_outer.x2 - rout + 1; - fill_area2.x2 = area_outer.x2; - - int32_t mask_ofs = (area_outer.x2 - rout + 1) - (vdb->area.x1 + draw_area.x1); - if(mask_ofs < 0) mask_ofs = 0; - _lv_blend_fill(clip, &fill_area2, color, mask_buf + mask_ofs, mask_res, opa, blend_mode); - - - fill_area.y1++; - fill_area.y2++; - } - - /*Draw the left vertical part*/ - fill_area.y1 = area_outer.y1 + corner_size + 1; - fill_area.y2 = area_outer.y2 - corner_size - 1; - - fill_area.x1 = area_outer.x1; - fill_area.x2 = area_outer.x1 + dsc->outline_width - 1; - _lv_blend_fill(clip, &fill_area, color, NULL, LV_DRAW_MASK_RES_FULL_COVER, opa, blend_mode); - - /*Draw the right vertical border*/ - fill_area.x1 = area_outer.x2 - dsc->outline_width + 1; - fill_area.x2 = area_outer.x2; - - _lv_blend_fill(clip, &fill_area, color, NULL, LV_DRAW_MASK_RES_FULL_COVER, opa, blend_mode); - } - /*Process line by line if there is other mask too*/ - else { - fill_area.x1 = area_outer.x1; - fill_area.x2 = area_outer.x2; - fill_area.y1 = disp_area->y1 + draw_area.y1; - fill_area.y2 = fill_area.y1; - - for(h = draw_area.y1; h <= draw_area.y2; h++) { - _lv_memset_ff(mask_buf, draw_area_w); - mask_res = lv_draw_mask_apply(mask_buf, vdb->area.x1 + draw_area.x1, vdb->area.y1 + h, draw_area_w); - - _lv_blend_fill(clip, &fill_area, color, mask_buf, mask_res, opa, blend_mode); - fill_area.y1++; - fill_area.y2++; - - } - } - lv_draw_mask_remove_id(mask_rin_id); - lv_draw_mask_remove_id(mask_rout_id); - _lv_mem_buf_release(mask_buf); + draw_full_border(&area_inner, &area_outer, clip, dsc->radius, dsc->outline_color, dsc->outline_opa, dsc->outline_blend_mode); } static void draw_pattern(const lv_area_t * coords, const lv_area_t * clip, lv_draw_rect_dsc_t * dsc) @@ -1559,3 +1295,177 @@ static void draw_value(const lv_area_t * coords, const lv_area_t * clip, lv_draw lv_draw_label(&value_area, clip, &label_dsc, dsc->value_str, NULL); } + +static void draw_full_border(const lv_area_t * area_inner, const lv_area_t * area_outer, const lv_area_t * clip, lv_coord_t radius, lv_color_t color, lv_opa_t opa, lv_blend_mode_t blend_mode) +{ + uint8_t other_mask_cnt = lv_draw_mask_get_cnt(); + bool simple_mode = true; + if(other_mask_cnt) simple_mode = false; + + int32_t inner_w = lv_area_get_width(area_inner); + int32_t inner_h = lv_area_get_height(area_inner); + lv_coord_t border_width = area_outer->x2 - area_inner->x2; + int32_t rin = radius; + + int32_t short_side = LV_MATH_MIN(inner_w, inner_h); + if(rin > short_side >> 1) rin = short_side >> 1; + + /*Get the outer area*/ + int32_t rout = rin + border_width; + + int32_t coords_out_w = lv_area_get_width(area_outer); + int32_t coords_out_h = lv_area_get_height(area_outer); + short_side = LV_MATH_MIN(coords_out_w, coords_out_h); + if(rout > short_side >> 1) rout = short_side >> 1; + + lv_disp_t * disp = _lv_refr_get_disp_refreshing(); + lv_disp_buf_t * vdb = lv_disp_get_buf(disp); + + /* Get clipped fill area which is the real draw area. + * It is always the same or inside `fill_area` */ + lv_area_t draw_area; + bool is_common; + is_common = _lv_area_intersect(&draw_area, area_outer, clip); + if(is_common == false) return; + + const lv_area_t * disp_area = &vdb->area; + + /* Now `draw_area` has absolute coordinates. + * Make it relative to `disp_area` to simplify draw to `disp_buf`*/ + draw_area.x1 -= disp_area->x1; + draw_area.y1 -= disp_area->y1; + draw_area.x2 -= disp_area->x1; + draw_area.y2 -= disp_area->y1; + + int32_t draw_area_w = lv_area_get_width(&draw_area); + + /*Create inner the mask*/ + lv_draw_mask_radius_param_t mask_rin_param; + lv_draw_mask_radius_init(&mask_rin_param, area_inner, rin, true); + int16_t mask_rin_id = lv_draw_mask_add(&mask_rin_param, NULL); + + lv_draw_mask_radius_param_t mask_rout_param; + lv_draw_mask_radius_init(&mask_rout_param, area_outer, rout, false); + int16_t mask_rout_id = lv_draw_mask_add(&mask_rout_param, NULL); + + lv_opa_t * mask_buf = _lv_mem_buf_get(draw_area_w); + + int32_t corner_size = LV_MATH_MAX(rout, border_width - 1); + + int32_t h; + lv_draw_mask_res_t mask_res; + lv_area_t fill_area; + + /*Apply some optimization if there is no other mask*/ + if(simple_mode) { + /*Draw the upper corner area*/ + int32_t upper_corner_end = area_outer->y1 - disp_area->y1 + corner_size; + + fill_area.x1 = area_outer->x1; + fill_area.x2 = area_outer->x2; + fill_area.y1 = disp_area->y1 + draw_area.y1; + fill_area.y2 = fill_area.y1; + for(h = draw_area.y1; h <= upper_corner_end; h++) { + _lv_memset_ff(mask_buf, draw_area_w); + mask_res = lv_draw_mask_apply(mask_buf, vdb->area.x1 + draw_area.x1, vdb->area.y1 + h, draw_area_w); + + lv_area_t fill_area2; + fill_area2.y1 = fill_area.y1; + fill_area2.y2 = fill_area.y2; + + fill_area2.x1 = area_outer->x1; + fill_area2.x2 = area_outer->x1 + rout - 1; + + _lv_blend_fill(clip, &fill_area2, color, mask_buf, mask_res, opa, blend_mode); + + /*Draw the top horizontal line*/ + if(fill_area2.y2 < area_outer->y1 + border_width) { + fill_area2.x1 = area_outer->x1 + rout; + fill_area2.x2 = area_outer->x2 - rout; + + _lv_blend_fill(clip, &fill_area2, color, NULL, LV_DRAW_MASK_RES_FULL_COVER, opa, blend_mode); + } + + fill_area2.x1 = area_outer->x2 - rout + 1; + fill_area2.x2 = area_outer->x2; + + int32_t mask_ofs = (area_outer->x2 - rout + 1) - (vdb->area.x1 + draw_area.x1); + if(mask_ofs < 0) mask_ofs = 0; + _lv_blend_fill(clip, &fill_area2, color, mask_buf + mask_ofs, mask_res, opa, blend_mode); + + fill_area.y1++; + fill_area.y2++; + } + + /*Draw the lower corner area */ + int32_t lower_corner_end = area_outer->y2 - disp_area->y1 - corner_size; + if(lower_corner_end <= upper_corner_end) lower_corner_end = upper_corner_end + 1; + fill_area.y1 = disp_area->y1 + lower_corner_end; + fill_area.y2 = fill_area.y1; + for(h = lower_corner_end; h <= draw_area.y2; h++) { + _lv_memset_ff(mask_buf, draw_area_w); + mask_res = lv_draw_mask_apply(mask_buf, vdb->area.x1 + draw_area.x1, vdb->area.y1 + h, draw_area_w); + + lv_area_t fill_area2; + fill_area2.x1 = area_outer->x1; + fill_area2.x2 = area_outer->x1 + rout - 1; + fill_area2.y1 = fill_area.y1; + fill_area2.y2 = fill_area.y2; + + _lv_blend_fill(clip, &fill_area2, color, mask_buf, mask_res, opa, blend_mode); + + /*Draw the bottom horizontal line*/ + if(fill_area2.y2 > area_outer->y2 - border_width) { + fill_area2.x1 = area_outer->x1 + rout; + fill_area2.x2 = area_outer->x2 - rout; + + _lv_blend_fill(clip, &fill_area2, color, NULL, LV_DRAW_MASK_RES_FULL_COVER, opa, blend_mode); + } + fill_area2.x1 = area_outer->x2 - rout + 1; + fill_area2.x2 = area_outer->x2; + + int32_t mask_ofs = (area_outer->x2 - rout + 1) - (vdb->area.x1 + draw_area.x1); + if(mask_ofs < 0) mask_ofs = 0; + _lv_blend_fill(clip, &fill_area2, color, mask_buf + mask_ofs, mask_res, opa, blend_mode); + + + fill_area.y1++; + fill_area.y2++; + } + + /*Draw the left vertical part*/ + fill_area.y1 = area_outer->y1 + corner_size + 1; + fill_area.y2 = area_outer->y2 - corner_size - 1; + + fill_area.x1 = area_outer->x1; + fill_area.x2 = area_outer->x1 + border_width - 1; + _lv_blend_fill(clip, &fill_area, color, NULL, LV_DRAW_MASK_RES_FULL_COVER, opa, blend_mode); + + /*Draw the right vertical border*/ + fill_area.x1 = area_outer->x2 - border_width + 1; + fill_area.x2 = area_outer->x2; + + _lv_blend_fill(clip, &fill_area, color, NULL, LV_DRAW_MASK_RES_FULL_COVER, opa, blend_mode); + } + /*Process line by line if there is other mask too*/ + else { + fill_area.x1 = area_outer->x1; + fill_area.x2 = area_outer->x2; + fill_area.y1 = disp_area->y1 + draw_area.y1; + fill_area.y2 = fill_area.y1; + + for(h = draw_area.y1; h <= draw_area.y2; h++) { + _lv_memset_ff(mask_buf, draw_area_w); + mask_res = lv_draw_mask_apply(mask_buf, vdb->area.x1 + draw_area.x1, vdb->area.y1 + h, draw_area_w); + + _lv_blend_fill(clip, &fill_area, color, mask_buf, mask_res, opa, blend_mode); + fill_area.y1++; + fill_area.y2++; + + } + } + lv_draw_mask_remove_id(mask_rin_id); + lv_draw_mask_remove_id(mask_rout_id); + _lv_mem_buf_release(mask_buf); +} +