/** * @file lv_draw_sw_border.c * */ /********************* * INCLUDES *********************/ #include "../../misc/lv_area_private.h" #include "lv_draw_sw_mask_private.h" #include "../lv_draw_private.h" #include "../lv_draw_private.h" #include "lv_draw_sw.h" #if LV_USE_DRAW_SW #include "blend/lv_draw_sw_blend_private.h" #include "../../misc/lv_math.h" #include "../../misc/lv_text_ap.h" #include "../../core/lv_refr.h" #include "../../misc/lv_assert.h" #include "../../stdlib/lv_string.h" #include "../lv_draw_mask.h" /********************* * DEFINES *********************/ #define SPLIT_LIMIT 50 /********************** * TYPEDEFS **********************/ /********************** * STATIC PROTOTYPES **********************/ static void draw_border_complex(lv_draw_unit_t * draw_unit, const lv_area_t * outer_area, const lv_area_t * inner_area, int32_t rout, int32_t rin, lv_color_t color, lv_opa_t opa); static void draw_border_simple(lv_draw_unit_t * draw_unit, const lv_area_t * outer_area, const lv_area_t * inner_area, lv_color_t color, lv_opa_t opa); /********************** * STATIC VARIABLES **********************/ /********************** * MACROS **********************/ /********************** * GLOBAL FUNCTIONS **********************/ void lv_draw_sw_border(lv_draw_unit_t * draw_unit, const lv_draw_border_dsc_t * dsc, const lv_area_t * coords) { if(dsc->opa <= LV_OPA_MIN) return; if(dsc->width == 0) return; if(dsc->side == LV_BORDER_SIDE_NONE) return; int32_t coords_w = lv_area_get_width(coords); int32_t coords_h = lv_area_get_height(coords); int32_t rout = dsc->radius; int32_t short_side = LV_MIN(coords_w, coords_h); if(rout > short_side >> 1) rout = short_side >> 1; /*Get the inner area*/ lv_area_t area_inner; lv_area_copy(&area_inner, coords); area_inner.x1 += ((dsc->side & LV_BORDER_SIDE_LEFT) ? dsc->width : - (dsc->width + rout)); area_inner.x2 -= ((dsc->side & LV_BORDER_SIDE_RIGHT) ? dsc->width : - (dsc->width + rout)); area_inner.y1 += ((dsc->side & LV_BORDER_SIDE_TOP) ? dsc->width : - (dsc->width + rout)); area_inner.y2 -= ((dsc->side & LV_BORDER_SIDE_BOTTOM) ? dsc->width : - (dsc->width + rout)); int32_t rin = rout - dsc->width; if(rin < 0) rin = 0; if(rout == 0 && rin == 0) { draw_border_simple(draw_unit, coords, &area_inner, dsc->color, dsc->opa); } else { draw_border_complex(draw_unit, coords, &area_inner, rout, rin, dsc->color, dsc->opa); } } /********************** * STATIC FUNCTIONS **********************/ void draw_border_complex(lv_draw_unit_t * draw_unit, const lv_area_t * outer_area, const lv_area_t * inner_area, int32_t rout, int32_t rin, lv_color_t color, lv_opa_t opa) { #if LV_DRAW_SW_COMPLEX /*Get clipped draw area which is the real draw area. *It is always the same or inside `coords`*/ lv_area_t draw_area; if(!lv_area_intersect(&draw_area, outer_area, draw_unit->clip_area)) return; int32_t draw_area_w = lv_area_get_width(&draw_area); lv_draw_sw_blend_dsc_t blend_dsc; lv_memzero(&blend_dsc, sizeof(blend_dsc)); lv_opa_t * mask_buf = lv_malloc(draw_area_w); blend_dsc.mask_buf = mask_buf; void * mask_list[3] = {0}; /*Create mask for the inner mask*/ lv_draw_sw_mask_radius_param_t mask_rin_param; lv_draw_sw_mask_radius_init(&mask_rin_param, inner_area, rin, true); mask_list[0] = &mask_rin_param; /*Create mask for the outer area*/ lv_draw_sw_mask_radius_param_t mask_rout_param; if(rout > 0) { lv_draw_sw_mask_radius_init(&mask_rout_param, outer_area, rout, false); mask_list[1] = &mask_rout_param; } int32_t h; lv_area_t blend_area; blend_dsc.blend_area = &blend_area; blend_dsc.mask_area = &blend_area; blend_dsc.color = color; blend_dsc.opa = opa; /*Calculate the x and y coordinates where the straight parts area is*/ lv_area_t core_area; core_area.x1 = LV_MAX(outer_area->x1 + rout, inner_area->x1); core_area.x2 = LV_MIN(outer_area->x2 - rout, inner_area->x2); core_area.y1 = LV_MAX(outer_area->y1 + rout, inner_area->y1); core_area.y2 = LV_MIN(outer_area->y2 - rout, inner_area->y2); int32_t core_w = lv_area_get_width(&core_area); bool top_side = outer_area->y1 <= inner_area->y1; bool bottom_side = outer_area->y2 >= inner_area->y2; /*No masks*/ bool left_side = outer_area->x1 <= inner_area->x1; bool right_side = outer_area->x2 >= inner_area->x2; bool split_hor = true; if(left_side && right_side && top_side && bottom_side && core_w < SPLIT_LIMIT) { split_hor = false; } blend_dsc.mask_res = LV_DRAW_SW_MASK_RES_FULL_COVER; /*Draw the straight lines first if they are long enough*/ if(top_side && split_hor) { blend_area.x1 = core_area.x1; blend_area.x2 = core_area.x2; blend_area.y1 = outer_area->y1; blend_area.y2 = inner_area->y1 - 1; lv_draw_sw_blend(draw_unit, &blend_dsc); } if(bottom_side && split_hor) { blend_area.x1 = core_area.x1; blend_area.x2 = core_area.x2; blend_area.y1 = inner_area->y2 + 1; blend_area.y2 = outer_area->y2; lv_draw_sw_blend(draw_unit, &blend_dsc); } /*If the border is very thick and the vertical sides overlap horizontally draw a single rectangle*/ if(inner_area->x1 >= inner_area->x2 && left_side && right_side) { blend_area.x1 = outer_area->x1; blend_area.x2 = outer_area->x2; blend_area.y1 = core_area.y1; blend_area.y2 = core_area.y2; lv_draw_sw_blend(draw_unit, &blend_dsc); } else { if(left_side) { blend_area.x1 = outer_area->x1; blend_area.x2 = inner_area->x1 - 1; blend_area.y1 = core_area.y1; blend_area.y2 = core_area.y2; lv_draw_sw_blend(draw_unit, &blend_dsc); } if(right_side) { blend_area.x1 = inner_area->x2 + 1; blend_area.x2 = outer_area->x2; blend_area.y1 = core_area.y1; blend_area.y2 = core_area.y2; lv_draw_sw_blend(draw_unit, &blend_dsc); } } /*Draw the corners*/ int32_t blend_w; /*Left and right corner together if they are close to each other*/ if(!split_hor) { /*Calculate the top corner and mirror it to the bottom*/ blend_area.x1 = draw_area.x1; blend_area.x2 = draw_area.x2; int32_t max_h = LV_MAX(rout, inner_area->y1 - outer_area->y1); for(h = 0; h < max_h; h++) { int32_t top_y = outer_area->y1 + h; int32_t bottom_y = outer_area->y2 - h; if(top_y < draw_area.y1 && bottom_y > draw_area.y2) continue; /*This line is clipped now*/ lv_memset(mask_buf, 0xff, draw_area_w); blend_dsc.mask_res = lv_draw_sw_mask_apply(mask_list, mask_buf, blend_area.x1, top_y, draw_area_w); if(top_y >= draw_area.y1) { blend_area.y1 = top_y; blend_area.y2 = top_y; lv_draw_sw_blend(draw_unit, &blend_dsc); } if(bottom_y <= draw_area.y2) { blend_area.y1 = bottom_y; blend_area.y2 = bottom_y; lv_draw_sw_blend(draw_unit, &blend_dsc); } } } else { /*Left corners*/ blend_area.x1 = draw_area.x1; blend_area.x2 = LV_MIN(draw_area.x2, core_area.x1 - 1); blend_w = lv_area_get_width(&blend_area); if(blend_w > 0) { if(left_side || top_side) { for(h = draw_area.y1; h < core_area.y1; h++) { blend_area.y1 = h; blend_area.y2 = h; lv_memset(mask_buf, 0xff, blend_w); blend_dsc.mask_res = lv_draw_sw_mask_apply(mask_list, mask_buf, blend_area.x1, h, blend_w); lv_draw_sw_blend(draw_unit, &blend_dsc); } } if(left_side || bottom_side) { for(h = core_area.y2 + 1; h <= draw_area.y2; h++) { blend_area.y1 = h; blend_area.y2 = h; lv_memset(mask_buf, 0xff, blend_w); blend_dsc.mask_res = lv_draw_sw_mask_apply(mask_list, mask_buf, blend_area.x1, h, blend_w); lv_draw_sw_blend(draw_unit, &blend_dsc); } } } /*Right corners*/ blend_area.x1 = LV_MAX(draw_area.x1, blend_area.x2 + 1); /*To not overlap with the left side*/ blend_area.x1 = LV_MAX(draw_area.x1, core_area.x2 + 1); blend_area.x2 = draw_area.x2; blend_w = lv_area_get_width(&blend_area); if(blend_w > 0) { if(right_side || top_side) { for(h = draw_area.y1; h < core_area.y1; h++) { blend_area.y1 = h; blend_area.y2 = h; lv_memset(mask_buf, 0xff, blend_w); blend_dsc.mask_res = lv_draw_sw_mask_apply(mask_list, mask_buf, blend_area.x1, h, blend_w); lv_draw_sw_blend(draw_unit, &blend_dsc); } } if(right_side || bottom_side) { for(h = core_area.y2 + 1; h <= draw_area.y2; h++) { blend_area.y1 = h; blend_area.y2 = h; lv_memset(mask_buf, 0xff, blend_w); blend_dsc.mask_res = lv_draw_sw_mask_apply(mask_list, mask_buf, blend_area.x1, h, blend_w); lv_draw_sw_blend(draw_unit, &blend_dsc); } } } } lv_draw_sw_mask_free_param(&mask_rin_param); if(rout > 0) lv_draw_sw_mask_free_param(&mask_rout_param); lv_free(mask_buf); #endif /*LV_DRAW_SW_COMPLEX*/ } static void draw_border_simple(lv_draw_unit_t * draw_unit, const lv_area_t * outer_area, const lv_area_t * inner_area, lv_color_t color, lv_opa_t opa) { lv_area_t a; lv_draw_sw_blend_dsc_t blend_dsc; lv_memzero(&blend_dsc, sizeof(lv_draw_sw_blend_dsc_t)); blend_dsc.blend_area = &a; blend_dsc.color = color; blend_dsc.opa = opa; bool top_side = outer_area->y1 <= inner_area->y1; bool bottom_side = outer_area->y2 >= inner_area->y2; bool left_side = outer_area->x1 <= inner_area->x1; bool right_side = outer_area->x2 >= inner_area->x2; /*Top*/ a.x1 = outer_area->x1; a.x2 = outer_area->x2; a.y1 = outer_area->y1; a.y2 = inner_area->y1 - 1; if(top_side) { lv_draw_sw_blend(draw_unit, &blend_dsc); } /*Bottom*/ a.y1 = inner_area->y2 + 1; a.y2 = outer_area->y2; if(bottom_side) { lv_draw_sw_blend(draw_unit, &blend_dsc); } /*Left*/ a.x1 = outer_area->x1; a.x2 = inner_area->x1 - 1; a.y1 = (top_side) ? inner_area->y1 : outer_area->y1; a.y2 = (bottom_side) ? inner_area->y2 : outer_area->y2; if(left_side) { lv_draw_sw_blend(draw_unit, &blend_dsc); } /*Right*/ a.x1 = inner_area->x2 + 1; a.x2 = outer_area->x2; if(right_side) { lv_draw_sw_blend(draw_unit, &blend_dsc); } } #endif /*LV_USE_DRAW_SW*/