/** * @file lv_draw_sw_fill.c * */ /********************* * INCLUDES *********************/ #include "../../misc/lv_area_private.h" #include "lv_draw_sw_mask_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 "lv_draw_sw_gradient_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 *********************/ /********************** * TYPEDEFS **********************/ /********************** * STATIC PROTOTYPES **********************/ /********************** * STATIC VARIABLES **********************/ /********************** * MACROS **********************/ /********************** * GLOBAL FUNCTIONS **********************/ void lv_draw_sw_fill(lv_draw_unit_t * draw_unit, lv_draw_fill_dsc_t * dsc, const lv_area_t * coords) { if(dsc->opa <= LV_OPA_MIN) return; lv_area_t bg_coords; lv_area_copy(&bg_coords, coords); lv_area_t clipped_coords; if(!lv_area_intersect(&clipped_coords, &bg_coords, draw_unit->clip_area)) return; lv_grad_dir_t grad_dir = dsc->grad.dir; lv_color_t bg_color = grad_dir == LV_GRAD_DIR_NONE ? dsc->color : dsc->grad.stops[0].color; lv_draw_sw_blend_dsc_t blend_dsc = {0}; blend_dsc.color = bg_color; /*Most simple case: just a plain rectangle*/ if(dsc->radius == 0 && (grad_dir == LV_GRAD_DIR_NONE)) { blend_dsc.blend_area = &bg_coords; blend_dsc.opa = dsc->opa; lv_draw_sw_blend(draw_unit, &blend_dsc); return; } /*Complex case: there is gradient, mask, or radius*/ #if LV_DRAW_SW_COMPLEX == 0 LV_LOG_WARN("Can't draw complex rectangle because LV_DRAW_SW_COMPLEX = 0"); #else lv_opa_t opa = dsc->opa >= LV_OPA_MAX ? LV_OPA_COVER : dsc->opa; /*Get the real radius. Can't be larger than the half of the shortest side */ int32_t coords_bg_w = lv_area_get_width(&bg_coords); int32_t coords_bg_h = lv_area_get_height(&bg_coords); int32_t short_side = LV_MIN(coords_bg_w, coords_bg_h); int32_t rout = LV_MIN(dsc->radius, short_side >> 1); /*Add a radius mask if there is a radius*/ int32_t clipped_w = lv_area_get_width(&clipped_coords); lv_opa_t * mask_buf = NULL; lv_draw_sw_mask_radius_param_t mask_rout_param; void * mask_list[2] = {NULL, NULL}; if(rout > 0) { mask_buf = lv_malloc(clipped_w); lv_draw_sw_mask_radius_init(&mask_rout_param, &bg_coords, rout, false); mask_list[0] = &mask_rout_param; } int32_t h; lv_area_t blend_area; blend_area.x1 = clipped_coords.x1; blend_area.x2 = clipped_coords.x2; blend_dsc.mask_buf = mask_buf; blend_dsc.blend_area = &blend_area; blend_dsc.mask_area = &blend_area; blend_dsc.opa = LV_OPA_COVER; /*Get gradient if appropriate*/ lv_grad_t * grad = lv_gradient_get(&dsc->grad, coords_bg_w, coords_bg_h); lv_opa_t * grad_opa_map = NULL; bool transp = false; if(grad && grad_dir >= LV_GRAD_DIR_HOR) { blend_dsc.src_area = &blend_area; blend_dsc.src_buf = grad->color_map + clipped_coords.x1 - bg_coords.x1; uint32_t s; for(s = 0; s < dsc->grad.stops_count; s++) { if(dsc->grad.stops[s].opa != LV_OPA_COVER) { transp = true; break; } } if(grad_dir == LV_GRAD_DIR_HOR) { if(transp) grad_opa_map = grad->opa_map + clipped_coords.x1 - bg_coords.x1; } blend_dsc.src_color_format = LV_COLOR_FORMAT_RGB888; } #if LV_USE_DRAW_SW_COMPLEX_GRADIENTS /*Prepare complex gradient*/ if(grad_dir >= LV_GRAD_DIR_LINEAR) { LV_ASSERT_NULL(grad); switch(grad_dir) { case LV_GRAD_DIR_LINEAR: lv_gradient_linear_setup(&dsc->grad, coords); break; case LV_GRAD_DIR_RADIAL: lv_gradient_radial_setup(&dsc->grad, coords); break; case LV_GRAD_DIR_CONICAL: lv_gradient_conical_setup(&dsc->grad, coords); break; default: LV_LOG_WARN("Gradient type is not supported"); return; } blend_dsc.src_area = &blend_area; /* For complex gradients we reuse the color map buffer for the pixel data */ blend_dsc.src_buf = grad->color_map; grad_opa_map = grad->opa_map; } #endif /* Draw the top of the rectangle line by line and mirror it to the bottom. */ for(h = 0; h < rout; h++) { int32_t top_y = bg_coords.y1 + h; int32_t bottom_y = bg_coords.y2 - h; if(top_y < clipped_coords.y1 && bottom_y > clipped_coords.y2) continue; /*This line is clipped now*/ bool preblend = false; /* Initialize the mask to opa instead of 0xFF and blend with LV_OPA_COVER. * It saves calculating the final opa in lv_draw_sw_blend*/ lv_memset(mask_buf, opa, clipped_w); blend_dsc.mask_res = lv_draw_sw_mask_apply(mask_list, mask_buf, blend_area.x1, top_y, clipped_w); if(blend_dsc.mask_res == LV_DRAW_SW_MASK_RES_FULL_COVER) blend_dsc.mask_res = LV_DRAW_SW_MASK_RES_CHANGED; bool hor_grad_processed = false; if(top_y >= clipped_coords.y1) { blend_area.y1 = top_y; blend_area.y2 = top_y; switch(grad_dir) { case LV_GRAD_DIR_VER: LV_ASSERT_NULL(grad); blend_dsc.color = grad->color_map[top_y - bg_coords.y1]; blend_dsc.opa = grad->opa_map[top_y - bg_coords.y1]; break; case LV_GRAD_DIR_HOR: hor_grad_processed = true; preblend = grad_opa_map != NULL; break; #if LV_USE_DRAW_SW_COMPLEX_GRADIENTS case LV_GRAD_DIR_LINEAR: lv_gradient_linear_get_line(&dsc->grad, clipped_coords.x1 - bg_coords.x1, top_y - bg_coords.y1, coords_bg_w, grad); preblend = true; break; case LV_GRAD_DIR_RADIAL: lv_gradient_radial_get_line(&dsc->grad, clipped_coords.x1 - bg_coords.x1, top_y - bg_coords.y1, coords_bg_w, grad); preblend = true; break; case LV_GRAD_DIR_CONICAL: lv_gradient_conical_get_line(&dsc->grad, clipped_coords.x1 - bg_coords.x1, top_y - bg_coords.y1, coords_bg_w, grad); preblend = true; break; #endif default: break; } /* pre-blend the mask */ if(preblend) { int32_t i; for(i = 0; i < clipped_w; i++) { if(grad_opa_map[i] < LV_OPA_MAX) mask_buf[i] = (mask_buf[i] * grad_opa_map[i]) >> 8; } blend_dsc.mask_res = LV_DRAW_SW_MASK_RES_CHANGED; } lv_draw_sw_blend(draw_unit, &blend_dsc); } if(bottom_y <= clipped_coords.y2) { blend_area.y1 = bottom_y; blend_area.y2 = bottom_y; switch(grad_dir) { case LV_GRAD_DIR_VER: LV_ASSERT_NULL(grad); blend_dsc.color = grad->color_map[bottom_y - bg_coords.y1]; blend_dsc.opa = grad->opa_map[bottom_y - bg_coords.y1]; break; case LV_GRAD_DIR_HOR: preblend = !hor_grad_processed && (grad_opa_map != NULL); break; #if LV_USE_DRAW_SW_COMPLEX_GRADIENTS case LV_GRAD_DIR_LINEAR: lv_gradient_linear_get_line(&dsc->grad, clipped_coords.x1 - bg_coords.x1, bottom_y - bg_coords.y1, coords_bg_w, grad); preblend = true; break; case LV_GRAD_DIR_RADIAL: lv_gradient_radial_get_line(&dsc->grad, clipped_coords.x1 - bg_coords.x1, bottom_y - bg_coords.y1, coords_bg_w, grad); preblend = true; break; case LV_GRAD_DIR_CONICAL: lv_gradient_conical_get_line(&dsc->grad, clipped_coords.x1 - bg_coords.x1, bottom_y - bg_coords.y1, coords_bg_w, grad); preblend = true; break; #endif default: break; } /* pre-blend the mask */ if(preblend) { int32_t i; if(grad_dir >= LV_GRAD_DIR_LINEAR) { /*Need to generate the mask again, because we have mixed in the upper part of the gradient*/ lv_memset(mask_buf, opa, clipped_w); blend_dsc.mask_res = lv_draw_sw_mask_apply(mask_list, mask_buf, blend_area.x1, top_y, clipped_w); if(blend_dsc.mask_res == LV_DRAW_SW_MASK_RES_FULL_COVER) blend_dsc.mask_res = LV_DRAW_SW_MASK_RES_CHANGED; } for(i = 0; i < clipped_w; i++) { if(grad_opa_map[i] < LV_OPA_MAX) mask_buf[i] = (mask_buf[i] * grad_opa_map[i]) >> 8; } blend_dsc.mask_res = LV_DRAW_SW_MASK_RES_CHANGED; } lv_draw_sw_blend(draw_unit, &blend_dsc); } } /* Draw the center of the rectangle.*/ /*If no gradient, the center is a simple rectangle*/ if(grad_dir == LV_GRAD_DIR_NONE) { blend_area.y1 = bg_coords.y1 + rout; blend_area.y2 = bg_coords.y2 - rout; blend_dsc.opa = opa; blend_dsc.mask_buf = NULL; lv_draw_sw_blend(draw_unit, &blend_dsc); } /*With gradient draw line by line*/ else { blend_dsc.opa = opa; switch(grad_dir) { case LV_GRAD_DIR_VER: blend_dsc.mask_res = LV_DRAW_SW_MASK_RES_FULL_COVER; break; case LV_GRAD_DIR_HOR: blend_dsc.mask_res = LV_DRAW_SW_MASK_RES_CHANGED; blend_dsc.mask_buf = grad_opa_map; break; case LV_GRAD_DIR_LINEAR: case LV_GRAD_DIR_RADIAL: case LV_GRAD_DIR_CONICAL: blend_dsc.mask_res = transp ? LV_DRAW_SW_MASK_RES_CHANGED : LV_DRAW_SW_MASK_RES_FULL_COVER; blend_dsc.mask_buf = grad_opa_map; break; default: break; } int32_t h_start = LV_MAX(bg_coords.y1 + rout, clipped_coords.y1); int32_t h_end = LV_MIN(bg_coords.y2 - rout, clipped_coords.y2); for(h = h_start; h <= h_end; h++) { blend_area.y1 = h; blend_area.y2 = h; switch(grad_dir) { case LV_GRAD_DIR_VER: LV_ASSERT_NULL(grad); blend_dsc.color = grad->color_map[h - bg_coords.y1]; if(opa >= LV_OPA_MAX) blend_dsc.opa = grad->opa_map[h - bg_coords.y1]; else blend_dsc.opa = LV_OPA_MIX2(grad->opa_map[h - bg_coords.y1], opa); break; #if LV_USE_DRAW_SW_COMPLEX_GRADIENTS case LV_GRAD_DIR_LINEAR: lv_gradient_linear_get_line(&dsc->grad, clipped_coords.x1 - bg_coords.x1, h - bg_coords.y1, coords_bg_w, grad); break; case LV_GRAD_DIR_RADIAL: lv_gradient_radial_get_line(&dsc->grad, clipped_coords.x1 - bg_coords.x1, h - bg_coords.y1, coords_bg_w, grad); break; case LV_GRAD_DIR_CONICAL: lv_gradient_conical_get_line(&dsc->grad, clipped_coords.x1 - bg_coords.x1, h - bg_coords.y1, coords_bg_w, grad); break; #endif default: break; } lv_draw_sw_blend(draw_unit, &blend_dsc); } } if(mask_buf) { lv_free(mask_buf); lv_draw_sw_mask_free_param(&mask_rout_param); } if(grad) { lv_gradient_cleanup(grad); } #if LV_USE_DRAW_SW_COMPLEX_GRADIENTS if(grad_dir >= LV_GRAD_DIR_LINEAR) { switch(grad_dir) { case LV_GRAD_DIR_LINEAR: lv_gradient_linear_cleanup(&dsc->grad); break; case LV_GRAD_DIR_RADIAL: lv_gradient_radial_cleanup(&dsc->grad); break; case LV_GRAD_DIR_CONICAL: lv_gradient_conical_cleanup(&dsc->grad); break; default: break; } } #endif #endif } #endif /*LV_USE_DRAW_SW*/