From 8f6b1f8d6c60c5a038bcc31f1ba56a77d7b0ebb0 Mon Sep 17 00:00:00 2001 From: Gabor Kiss-Vamosi Date: Fri, 26 Nov 2021 12:44:10 +0100 Subject: [PATCH] arch(draw) separate SW renderer to allow replacing it (#2803) * decouple sw renderer * simplify blend interface * make tests pass * udpate makefiles * fix warning * fix build errors * run code formatter * antialias fix --- examples/widgets/img/lv_example_img_3.c | 6 +- lvgl.mk | 1 + src/core/lv_obj.c | 28 +- src/draw/lv_draw.c | 66 + src/draw/lv_draw.h | 66 +- src/draw/lv_draw.mk | 2 +- src/draw/lv_draw_arc.c | 427 +----- src/draw/lv_draw_arc.h | 7 +- src/draw/lv_draw_blend.c | 1040 +------------ src/draw/lv_draw_blend.h | 36 +- src/draw/lv_draw_img.c | 336 +---- src/draw/lv_draw_img.h | 2 +- src/draw/lv_draw_label.c | 502 +------ src/draw/lv_draw_label.h | 17 +- src/draw/lv_draw_line.c | 442 +----- src/draw/lv_draw_line.h | 11 +- src/draw/lv_draw_mask.h | 3 - src/draw/lv_draw_rect.c | 1276 +--------------- src/draw/lv_draw_rect.h | 18 +- src/draw/lv_draw_triangle.c | 171 +-- src/draw/sw/lv_draw_sw.c | 59 + src/draw/sw/lv_draw_sw.h | 82 + src/draw/sw/lv_draw_sw.mk | 14 + src/draw/sw/lv_draw_sw_arc.c | 467 ++++++ src/draw/sw/lv_draw_sw_blend.c | 754 ++++++++++ src/draw/sw/lv_draw_sw_img.c | 322 ++++ src/draw/sw/lv_draw_sw_letter.c | 530 +++++++ src/draw/sw/lv_draw_sw_line.c | 485 ++++++ .../{lv_draw_mask.c => sw/lv_draw_sw_mask.c} | 10 +- src/draw/sw/lv_draw_sw_polygon.c | 202 +++ src/draw/sw/lv_draw_sw_rect.c | 1331 +++++++++++++++++ src/extra/libs/rlottie/lv_rlottie.c | 4 +- src/gpu/sdl/lv_gpu_sdl_draw_blend.c | 10 +- 33 files changed, 4544 insertions(+), 4183 deletions(-) create mode 100644 src/draw/lv_draw.c create mode 100644 src/draw/sw/lv_draw_sw.c create mode 100644 src/draw/sw/lv_draw_sw.h create mode 100644 src/draw/sw/lv_draw_sw.mk create mode 100644 src/draw/sw/lv_draw_sw_arc.c create mode 100644 src/draw/sw/lv_draw_sw_blend.c create mode 100644 src/draw/sw/lv_draw_sw_img.c create mode 100644 src/draw/sw/lv_draw_sw_letter.c create mode 100644 src/draw/sw/lv_draw_sw_line.c rename src/draw/{lv_draw_mask.c => sw/lv_draw_sw_mask.c} (99%) create mode 100644 src/draw/sw/lv_draw_sw_polygon.c create mode 100644 src/draw/sw/lv_draw_sw_rect.c diff --git a/examples/widgets/img/lv_example_img_3.c b/examples/widgets/img/lv_example_img_3.c index 817601e0e..03085d5a2 100644 --- a/examples/widgets/img/lv_example_img_3.c +++ b/examples/widgets/img/lv_example_img_3.c @@ -32,12 +32,14 @@ void lv_example_img_3(void) lv_anim_set_values(&a, 0, 3600); lv_anim_set_time(&a, 5000); lv_anim_set_repeat_count(&a, LV_ANIM_REPEAT_INFINITE); - lv_anim_start(&a); +// lv_anim_start(&a); lv_anim_set_exec_cb(&a, set_zoom); lv_anim_set_values(&a, 128, 256); lv_anim_set_playback_time(&a, 3000); - lv_anim_start(&a); +// lv_anim_start(&a); + + lv_obj_set_style_blend_mode(img, LV_BLEND_MODE_MULTIPLY, 0); } diff --git a/lvgl.mk b/lvgl.mk index 664987aa7..2918370c5 100644 --- a/lvgl.mk +++ b/lvgl.mk @@ -2,6 +2,7 @@ include $(LVGL_DIR)/$(LVGL_DIR_NAME)/examples/examples.mk include $(LVGL_DIR)/$(LVGL_DIR_NAME)/src/extra/extra.mk include $(LVGL_DIR)/$(LVGL_DIR_NAME)/src/core/lv_core.mk include $(LVGL_DIR)/$(LVGL_DIR_NAME)/src/draw/lv_draw.mk +include $(LVGL_DIR)/$(LVGL_DIR_NAME)/src/draw/sw/lv_draw_sw.mk include $(LVGL_DIR)/$(LVGL_DIR_NAME)/src/font/lv_font.mk include $(LVGL_DIR)/$(LVGL_DIR_NAME)/src/gpu/lv_gpu.mk include $(LVGL_DIR)/$(LVGL_DIR_NAME)/src/hal/lv_hal.mk diff --git a/src/core/lv_obj.c b/src/core/lv_obj.c index 2c04d6d7f..1531895b4 100644 --- a/src/core/lv_obj.c +++ b/src/core/lv_obj.c @@ -114,20 +114,22 @@ void lv_init(void) _lv_group_init(); -#if LV_USE_GPU_STM32_DMA2D - /*Initialize DMA2D GPU*/ - lv_gpu_stm32_dma2d_init(); -#endif + lv_draw_init(); -#if LV_USE_GPU_NXP_PXP && LV_USE_GPU_NXP_PXP_AUTO_INIT - if(lv_gpu_nxp_pxp_init(&pxp_default_cfg) != LV_RES_OK) { - LV_LOG_ERROR("PXP init error. STOP.\n"); - for(; ;) ; - } -#endif -#if LV_USE_GPU_SDL - lv_gpu_sdl_init(); -#endif + //#if LV_USE_GPU_STM32_DMA2D + // /*Initialize DMA2D GPU*/ + // lv_gpu_stm32_dma2d_init(); + //#endif + // + //#if LV_USE_GPU_NXP_PXP && LV_USE_GPU_NXP_PXP_AUTO_INIT + // if(lv_gpu_nxp_pxp_init(&pxp_default_cfg) != LV_RES_OK) { + // LV_LOG_ERROR("PXP init error. STOP.\n"); + // for(; ;) ; + // } + //#endif + //#if LV_USE_GPU_SDL + // lv_gpu_sdl_init(); + //#endif _lv_obj_style_init(); _lv_ll_init(&LV_GC_ROOT(_lv_disp_ll), sizeof(lv_disp_t)); diff --git a/src/draw/lv_draw.c b/src/draw/lv_draw.c new file mode 100644 index 000000000..65a1142b4 --- /dev/null +++ b/src/draw/lv_draw.c @@ -0,0 +1,66 @@ +/** + * @file lv_draw.c + * + */ + +/********************* + * INCLUDES + *********************/ +#include "lv_draw.h" +#include "sw/lv_draw_sw.h" + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ + +/********************** + * STATIC VARIABLES + **********************/ + +/********************** + * STATIC VARIABLES + **********************/ + +static lv_draw_backend_t * backend_head; + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +void lv_draw_init(void) +{ + backend_head = NULL; + lv_draw_sw_init(); +} + +void lv_draw_backend_init(lv_draw_backend_t * backend) +{ + lv_memset_00(backend, sizeof(lv_draw_backend_t)); +} + +void lv_draw_backend_add(lv_draw_backend_t * backend) +{ + backend->base = backend_head; + backend_head = backend; +} + +const lv_draw_backend_t * lv_draw_backend_get(void) +{ + return backend_head; +} + +/********************** + * STATIC FUNCTIONS + **********************/ diff --git a/src/draw/lv_draw.h b/src/draw/lv_draw.h index 7f78dc8ee..4df549f3c 100644 --- a/src/draw/lv_draw.h +++ b/src/draw/lv_draw.h @@ -26,7 +26,6 @@ extern "C" { #include "lv_draw_line.h" #include "lv_draw_triangle.h" #include "lv_draw_arc.h" -#include "lv_draw_blend.h" #include "lv_draw_mask.h" /********************* @@ -36,11 +35,76 @@ extern "C" { /********************** * TYPEDEFS **********************/ +typedef struct _lv_draw_backend_t { + struct _lv_draw_backend_t * base; + + void * ctx; + + void (*draw_rect)(const lv_area_t * coords, const lv_area_t * clip, const lv_draw_rect_dsc_t * dsc); + + void (*draw_arc)(lv_coord_t center_x, lv_coord_t center_y, uint16_t radius, uint16_t start_angle, uint16_t end_angle, + const lv_area_t * clip_area, const lv_draw_arc_dsc_t * dsc); + + void (*draw_img)(const lv_area_t * map_area, const lv_area_t * clip_area, + const uint8_t * map_p, + const lv_draw_img_dsc_t * draw_dsc, + bool chroma_key, bool alpha_byte); + + void (*draw_letter)(const lv_point_t * pos_p, const lv_area_t * clip_area, + const lv_font_t * font_p, uint32_t letter, + lv_color_t color, lv_opa_t opa, lv_blend_mode_t blend_mode); + + + void (*draw_line)(const lv_point_t * point1, const lv_point_t * point2, const lv_area_t * clip, + const lv_draw_line_dsc_t * dsc); + + + void (*draw_polygon)(const lv_point_t points[], uint16_t point_cnt, const lv_area_t * clip_area, + const lv_draw_rect_dsc_t * draw_dsc); + + + /** + * Fill an arae of a buffer with a color + * @param dest_buf pointer to a buffer to fill + * @param dest_stride stride of `dest_buf` (number of pixel in a line) + * @param fill_area the area to fill on `dest_buf` + * @param color fill color + * @param mask NULL if ignored, or an alpha mask to apply on `fill_area` + * @param opa overall opacity + * @param blend_mode e.g. LV_BLEND_MODE_ADDITIVE + */ + void (*blend_fill)(lv_color_t * dest_buf, lv_coord_t dest_stride, const lv_area_t * fill_area, + lv_color_t color, lv_opa_t * mask, lv_opa_t opa, lv_blend_mode_t blend_mode); + + /** + * Blend a source buffer to a destination buffer + * @param dest_buf pointer to the destination buffer + * @param dest_stride stride of `dest_buf` (number of pixel in a line) + * @param clip_area clip the blending to this area + * @param src_buf pointer to the destination buffer + * @param src_area coordinates of the `src_buf` relative to the `dest_buf` + * @param mask NULL if ignored, or an alpha mask to apply on `clip_area` (it's size is equal to the `clip_area`) + * @param opa overall opacity + * @param blend_mode e.g. LV_BLEND_MODE_ADDITIVE + */ + void (*blend_map)(lv_color_t * dest_buf, lv_coord_t dest_stride, const lv_area_t * clip_area, + const lv_color_t * src_buf, const lv_area_t * src_area, + lv_opa_t * mask, lv_opa_t opa, lv_blend_mode_t blend_mode); + +} lv_draw_backend_t; /********************** * GLOBAL PROTOTYPES **********************/ +void lv_draw_init(void); + +void lv_draw_backend_init(lv_draw_backend_t * backend); + +void lv_draw_backend_add(lv_draw_backend_t * backend); + +const lv_draw_backend_t * lv_draw_backend_get(void); + /********************** * GLOBAL VARIABLES **********************/ diff --git a/src/draw/lv_draw.mk b/src/draw/lv_draw.mk index a31fedf43..615875086 100644 --- a/src/draw/lv_draw.mk +++ b/src/draw/lv_draw.mk @@ -1,9 +1,9 @@ CSRCS += lv_draw_arc.c +CSRCS += lv_draw.c CSRCS += lv_draw_blend.c CSRCS += lv_draw_img.c CSRCS += lv_draw_label.c CSRCS += lv_draw_line.c -CSRCS += lv_draw_mask.c CSRCS += lv_draw_rect.c CSRCS += lv_draw_triangle.c CSRCS += lv_img_buf.c diff --git a/src/draw/lv_draw_arc.c b/src/draw/lv_draw_arc.c index 44084f4d1..bccb7a4fc 100644 --- a/src/draw/lv_draw_arc.c +++ b/src/draw/lv_draw_arc.c @@ -6,46 +6,20 @@ /********************* * INCLUDES *********************/ +#include "lv_draw.h" #include "lv_draw_arc.h" -#include "lv_draw_rect.h" -#include "lv_draw_mask.h" -#include "../misc/lv_math.h" -#include "../misc/lv_log.h" -#include "../misc/lv_mem.h" /********************* * DEFINES *********************/ -#define SPLIT_RADIUS_LIMIT 10 /*With radius greater than this the arc will drawn in quarters. A quarter is drawn only if there is arc in it*/ -#define SPLIT_ANGLE_GAP_LIMIT 60 /*With small gaps in the arc don't bother with splitting because there is nothing to skip.*/ /********************** * TYPEDEFS **********************/ -typedef struct { - lv_coord_t center_x; - lv_coord_t center_y; - lv_coord_t radius; - uint16_t start_angle; - uint16_t end_angle; - uint16_t start_quarter; - uint16_t end_quarter; - lv_coord_t width; - lv_draw_rect_dsc_t * draw_dsc; - const lv_area_t * draw_area; - const lv_area_t * clip_area; -} quarter_draw_dsc_t; /********************** * STATIC PROTOTYPES **********************/ -#if LV_DRAW_COMPLEX - static void draw_quarter_0(quarter_draw_dsc_t * q); - static void draw_quarter_1(quarter_draw_dsc_t * q); - static void draw_quarter_2(quarter_draw_dsc_t * q); - static void draw_quarter_3(quarter_draw_dsc_t * q); - static void get_rounded_area(int16_t angle, lv_coord_t radius, uint8_t thickness, lv_area_t * res_area); -#endif /*LV_DRAW_COMPLEX*/ /********************** * STATIC VARIABLES @@ -59,7 +33,7 @@ typedef struct { * GLOBAL FUNCTIONS **********************/ -LV_ATTRIBUTE_FAST_MEM void lv_draw_arc_dsc_init(lv_draw_arc_dsc_t * dsc) +void lv_draw_arc_dsc_init(lv_draw_arc_dsc_t * dsc) { lv_memset_00(dsc, sizeof(lv_draw_arc_dsc_t)); dsc->width = 1; @@ -70,152 +44,12 @@ LV_ATTRIBUTE_FAST_MEM void lv_draw_arc_dsc_init(lv_draw_arc_dsc_t * dsc) void lv_draw_arc(lv_coord_t center_x, lv_coord_t center_y, uint16_t radius, uint16_t start_angle, uint16_t end_angle, const lv_area_t * clip_area, const lv_draw_arc_dsc_t * dsc) { -#if LV_DRAW_COMPLEX if(dsc->opa <= LV_OPA_MIN) return; if(dsc->width == 0) return; if(start_angle == end_angle) return; - lv_coord_t width = dsc->width; - if(width > radius) width = radius; - - lv_draw_rect_dsc_t cir_dsc; - lv_draw_rect_dsc_init(&cir_dsc); - cir_dsc.blend_mode = dsc->blend_mode; - if(dsc->img_src) { - cir_dsc.bg_opa = LV_OPA_TRANSP; - cir_dsc.bg_img_src = dsc->img_src; - cir_dsc.bg_img_opa = dsc->opa; - } - else { - cir_dsc.bg_opa = dsc->opa; - cir_dsc.bg_color = dsc->color; - } - - lv_area_t area_out; - area_out.x1 = center_x - radius; - area_out.y1 = center_y - radius; - area_out.x2 = center_x + radius - 1; /*-1 because the center already belongs to the left/bottom part*/ - area_out.y2 = center_y + radius - 1; - - lv_area_t area_in; - lv_area_copy(&area_in, &area_out); - area_in.x1 += dsc->width; - area_in.y1 += dsc->width; - area_in.x2 -= dsc->width; - area_in.y2 -= dsc->width; - - /*Create inner the mask*/ - int16_t mask_in_id = LV_MASK_ID_INV; - lv_draw_mask_radius_param_t mask_in_param; - if(lv_area_get_width(&area_in) > 0 && lv_area_get_height(&area_in) > 0) { - lv_draw_mask_radius_init(&mask_in_param, &area_in, LV_RADIUS_CIRCLE, true); - mask_in_id = lv_draw_mask_add(&mask_in_param, NULL); - } - - lv_draw_mask_radius_param_t mask_out_param; - lv_draw_mask_radius_init(&mask_out_param, &area_out, LV_RADIUS_CIRCLE, false); - int16_t mask_out_id = lv_draw_mask_add(&mask_out_param, NULL); - - /*Draw a full ring*/ - if(start_angle + 360 == end_angle || start_angle == end_angle + 360) { - cir_dsc.radius = LV_RADIUS_CIRCLE; - lv_draw_rect(&area_out, clip_area, &cir_dsc); - - lv_draw_mask_remove_id(mask_out_id); - if(mask_in_id != LV_MASK_ID_INV) lv_draw_mask_remove_id(mask_in_id); - return; - } - - while(start_angle >= 360) start_angle -= 360; - while(end_angle >= 360) end_angle -= 360; - - lv_draw_mask_angle_param_t mask_angle_param; - lv_draw_mask_angle_init(&mask_angle_param, center_x, center_y, start_angle, end_angle); - int16_t mask_angle_id = lv_draw_mask_add(&mask_angle_param, NULL); - - int32_t angle_gap; - if(end_angle > start_angle) { - angle_gap = 360 - (end_angle - start_angle); - } - else { - angle_gap = start_angle - end_angle; - } - if(angle_gap > SPLIT_ANGLE_GAP_LIMIT && radius > SPLIT_RADIUS_LIMIT) { - /*Handle each quarter individually and skip which is empty*/ - quarter_draw_dsc_t q_dsc; - q_dsc.center_x = center_x; - q_dsc.center_y = center_y; - q_dsc.radius = radius; - q_dsc.start_angle = start_angle; - q_dsc.end_angle = end_angle; - q_dsc.start_quarter = (start_angle / 90) & 0x3; - q_dsc.end_quarter = (end_angle / 90) & 0x3; - q_dsc.width = width; - q_dsc.draw_dsc = &cir_dsc; - q_dsc.draw_area = &area_out; - q_dsc.clip_area = clip_area; - - draw_quarter_0(&q_dsc); - draw_quarter_1(&q_dsc); - draw_quarter_2(&q_dsc); - draw_quarter_3(&q_dsc); - } - 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); - if(mask_in_id != LV_MASK_ID_INV) lv_draw_mask_remove_id(mask_in_id); - - if(dsc->rounded) { - - lv_draw_mask_radius_param_t mask_end_param; - - lv_area_t round_area; - get_rounded_area(start_angle, radius, width, &round_area); - round_area.x1 += center_x; - round_area.x2 += center_x; - round_area.y1 += center_y; - round_area.y2 += center_y; - lv_area_t clip_area2; - if(_lv_area_intersect(&clip_area2, clip_area, &round_area)) { - lv_draw_mask_radius_init(&mask_end_param, &round_area, LV_RADIUS_CIRCLE, false); - int16_t mask_end_id = lv_draw_mask_add(&mask_end_param, NULL); - - 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); - round_area.x1 += center_x; - round_area.x2 += center_x; - round_area.y1 += center_y; - round_area.y2 += center_y; - if(_lv_area_intersect(&clip_area2, clip_area, &round_area)) { - lv_draw_mask_radius_init(&mask_end_param, &round_area, LV_RADIUS_CIRCLE, false); - int16_t mask_end_id = lv_draw_mask_add(&mask_end_param, NULL); - - 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 - LV_LOG_WARN("Can't draw arc with LV_DRAW_COMPLEX == 0"); - LV_UNUSED(center_x); - LV_UNUSED(center_y); - LV_UNUSED(radius); - LV_UNUSED(start_angle); - LV_UNUSED(end_angle); - LV_UNUSED(clip_area); - LV_UNUSED(dsc); -#endif /*LV_DRAW_COMPLEX*/ + const lv_draw_backend_t * backend = lv_draw_backend_get(); + backend->draw_arc(center_x, center_y, radius, start_angle, end_angle, clip_area, dsc); } void lv_draw_arc_get_area(lv_coord_t x, lv_coord_t y, uint16_t radius, uint16_t start_angle, uint16_t end_angle, @@ -314,256 +148,3 @@ void lv_draw_arc_get_area(lv_coord_t x, lv_coord_t y, uint16_t radius, uint16_t /********************** * STATIC FUNCTIONS **********************/ - -#if LV_DRAW_COMPLEX -static void draw_quarter_0(quarter_draw_dsc_t * q) -{ - lv_area_t quarter_area; - - if(q->start_quarter == 0 && q->end_quarter == 0 && q->start_angle < q->end_angle) { - /*Small arc here*/ - quarter_area.y1 = q->center_y + ((lv_trigo_sin(q->start_angle) * (q->radius - q->width)) >> LV_TRIGO_SHIFT); - quarter_area.x2 = q->center_x + ((lv_trigo_sin(q->start_angle + 90) * (q->radius)) >> LV_TRIGO_SHIFT); - - quarter_area.y2 = q->center_y + ((lv_trigo_sin(q->end_angle) * q->radius) >> LV_TRIGO_SHIFT); - quarter_area.x1 = q->center_x + ((lv_trigo_sin(q->end_angle + 90) * (q->radius - q->width)) >> LV_TRIGO_SHIFT); - - bool ok = _lv_area_intersect(&quarter_area, &quarter_area, q->clip_area); - if(ok) lv_draw_rect(q->draw_area, &quarter_area, q->draw_dsc); - } - else if(q->start_quarter == 0 || q->end_quarter == 0) { - /*Start and/or end arcs here*/ - if(q->start_quarter == 0) { - quarter_area.x1 = q->center_x; - quarter_area.y2 = q->center_y + q->radius; - - quarter_area.y1 = q->center_y + ((lv_trigo_sin(q->start_angle) * (q->radius - q->width)) >> LV_TRIGO_SHIFT); - quarter_area.x2 = q->center_x + ((lv_trigo_sin(q->start_angle + 90) * (q->radius)) >> LV_TRIGO_SHIFT); - - bool ok = _lv_area_intersect(&quarter_area, &quarter_area, q->clip_area); - if(ok) lv_draw_rect(q->draw_area, &quarter_area, q->draw_dsc); - } - if(q->end_quarter == 0) { - quarter_area.x2 = q->center_x + q->radius; - quarter_area.y1 = q->center_y; - - quarter_area.y2 = q->center_y + ((lv_trigo_sin(q->end_angle) * q->radius) >> LV_TRIGO_SHIFT); - quarter_area.x1 = q->center_x + ((lv_trigo_sin(q->end_angle + 90) * (q->radius - q->width)) >> LV_TRIGO_SHIFT); - - bool ok = _lv_area_intersect(&quarter_area, &quarter_area, q->clip_area); - if(ok) lv_draw_rect(q->draw_area, &quarter_area, q->draw_dsc); - } - } - else if((q->start_quarter == q->end_quarter && q->start_quarter != 0 && q->end_angle < q->start_angle) || - (q->start_quarter == 2 && q->end_quarter == 1) || - (q->start_quarter == 3 && q->end_quarter == 2) || - (q->start_quarter == 3 && q->end_quarter == 1)) { - /*Arc crosses here*/ - quarter_area.x1 = q->center_x; - quarter_area.y1 = q->center_y; - quarter_area.x2 = q->center_x + q->radius; - quarter_area.y2 = q->center_y + q->radius; - - bool ok = _lv_area_intersect(&quarter_area, &quarter_area, q->clip_area); - if(ok) lv_draw_rect(q->draw_area, &quarter_area, q->draw_dsc); - } -} - -static void draw_quarter_1(quarter_draw_dsc_t * q) -{ - lv_area_t quarter_area; - - if(q->start_quarter == 1 && q->end_quarter == 1 && q->start_angle < q->end_angle) { - /*Small arc here*/ - quarter_area.y2 = q->center_y + ((lv_trigo_sin(q->start_angle) * (q->radius)) >> LV_TRIGO_SHIFT); - quarter_area.x2 = q->center_x + ((lv_trigo_sin(q->start_angle + 90) * (q->radius - q->width)) >> LV_TRIGO_SHIFT); - - quarter_area.y1 = q->center_y + ((lv_trigo_sin(q->end_angle) * (q->radius - q->width)) >> LV_TRIGO_SHIFT); - quarter_area.x1 = q->center_x + ((lv_trigo_sin(q->end_angle + 90) * (q->radius)) >> LV_TRIGO_SHIFT); - - bool ok = _lv_area_intersect(&quarter_area, &quarter_area, q->clip_area); - if(ok) lv_draw_rect(q->draw_area, &quarter_area, q->draw_dsc); - } - else if(q->start_quarter == 1 || q->end_quarter == 1) { - /*Start and/or end arcs here*/ - if(q->start_quarter == 1) { - quarter_area.x1 = q->center_x - q->radius; - quarter_area.y1 = q->center_y; - - quarter_area.y2 = q->center_y + ((lv_trigo_sin(q->start_angle) * (q->radius)) >> LV_TRIGO_SHIFT); - quarter_area.x2 = q->center_x + ((lv_trigo_sin(q->start_angle + 90) * (q->radius - q->width)) >> LV_TRIGO_SHIFT); - - bool ok = _lv_area_intersect(&quarter_area, &quarter_area, q->clip_area); - if(ok) lv_draw_rect(q->draw_area, &quarter_area, q->draw_dsc); - } - if(q->end_quarter == 1) { - quarter_area.x2 = q->center_x - 1; - quarter_area.y2 = q->center_y + q->radius; - - quarter_area.y1 = q->center_y + ((lv_trigo_sin(q->end_angle) * (q->radius - q->width)) >> LV_TRIGO_SHIFT); - quarter_area.x1 = q->center_x + ((lv_trigo_sin(q->end_angle + 90) * (q->radius)) >> LV_TRIGO_SHIFT); - - bool ok = _lv_area_intersect(&quarter_area, &quarter_area, q->clip_area); - if(ok) lv_draw_rect(q->draw_area, &quarter_area, q->draw_dsc); - } - } - else if((q->start_quarter == q->end_quarter && q->start_quarter != 1 && q->end_angle < q->start_angle) || - (q->start_quarter == 0 && q->end_quarter == 2) || - (q->start_quarter == 0 && q->end_quarter == 3) || - (q->start_quarter == 3 && q->end_quarter == 2)) { - /*Arc crosses here*/ - quarter_area.x1 = q->center_x - q->radius; - quarter_area.y1 = q->center_y; - quarter_area.x2 = q->center_x - 1; - quarter_area.y2 = q->center_y + q->radius; - - bool ok = _lv_area_intersect(&quarter_area, &quarter_area, q->clip_area); - if(ok) lv_draw_rect(q->draw_area, &quarter_area, q->draw_dsc); - } -} - -static void draw_quarter_2(quarter_draw_dsc_t * q) -{ - lv_area_t quarter_area; - - if(q->start_quarter == 2 && q->end_quarter == 2 && q->start_angle < q->end_angle) { - /*Small arc here*/ - quarter_area.x1 = q->center_x + ((lv_trigo_sin(q->start_angle + 90) * (q->radius)) >> LV_TRIGO_SHIFT); - quarter_area.y2 = q->center_y + ((lv_trigo_sin(q->start_angle) * (q->radius - q->width)) >> LV_TRIGO_SHIFT); - - quarter_area.y1 = q->center_y + ((lv_trigo_sin(q->end_angle) * q->radius) >> LV_TRIGO_SHIFT); - quarter_area.x2 = q->center_x + ((lv_trigo_sin(q->end_angle + 90) * (q->radius - q->width)) >> LV_TRIGO_SHIFT); - - bool ok = _lv_area_intersect(&quarter_area, &quarter_area, q->clip_area); - if(ok) lv_draw_rect(q->draw_area, &quarter_area, q->draw_dsc); - } - else if(q->start_quarter == 2 || q->end_quarter == 2) { - /*Start and/or end arcs here*/ - if(q->start_quarter == 2) { - quarter_area.x2 = q->center_x - 1; - quarter_area.y1 = q->center_y - q->radius; - - quarter_area.x1 = q->center_x + ((lv_trigo_sin(q->start_angle + 90) * (q->radius)) >> LV_TRIGO_SHIFT); - quarter_area.y2 = q->center_y + ((lv_trigo_sin(q->start_angle) * (q->radius - q->width)) >> LV_TRIGO_SHIFT); - - bool ok = _lv_area_intersect(&quarter_area, &quarter_area, q->clip_area); - if(ok) lv_draw_rect(q->draw_area, &quarter_area, q->draw_dsc); - } - if(q->end_quarter == 2) { - quarter_area.x1 = q->center_x - q->radius; - quarter_area.y2 = q->center_y - 1; - - quarter_area.x2 = q->center_x + ((lv_trigo_sin(q->end_angle + 90) * (q->radius - q->width)) >> LV_TRIGO_SHIFT); - quarter_area.y1 = q->center_y + ((lv_trigo_sin(q->end_angle) * (q->radius)) >> LV_TRIGO_SHIFT); - - bool ok = _lv_area_intersect(&quarter_area, &quarter_area, q->clip_area); - if(ok) lv_draw_rect(q->draw_area, &quarter_area, q->draw_dsc); - } - } - else if((q->start_quarter == q->end_quarter && q->start_quarter != 2 && q->end_angle < q->start_angle) || - (q->start_quarter == 0 && q->end_quarter == 3) || - (q->start_quarter == 1 && q->end_quarter == 3) || - (q->start_quarter == 1 && q->end_quarter == 0)) { - /*Arc crosses here*/ - quarter_area.x1 = q->center_x - q->radius; - quarter_area.y1 = q->center_y - q->radius; - quarter_area.x2 = q->center_x - 1; - quarter_area.y2 = q->center_y - 1; - - bool ok = _lv_area_intersect(&quarter_area, &quarter_area, q->clip_area); - if(ok) lv_draw_rect(q->draw_area, &quarter_area, q->draw_dsc); - } -} - -static void draw_quarter_3(quarter_draw_dsc_t * q) -{ - lv_area_t quarter_area; - - if(q->start_quarter == 3 && q->end_quarter == 3 && q->start_angle < q->end_angle) { - /*Small arc here*/ - quarter_area.x1 = q->center_x + ((lv_trigo_sin(q->start_angle + 90) * (q->radius - q->width)) >> LV_TRIGO_SHIFT); - quarter_area.y1 = q->center_y + ((lv_trigo_sin(q->start_angle) * (q->radius)) >> LV_TRIGO_SHIFT); - - quarter_area.x2 = q->center_x + ((lv_trigo_sin(q->end_angle + 90) * (q->radius)) >> LV_TRIGO_SHIFT); - quarter_area.y2 = q->center_y + ((lv_trigo_sin(q->end_angle) * (q->radius - q->width)) >> LV_TRIGO_SHIFT); - - bool ok = _lv_area_intersect(&quarter_area, &quarter_area, q->clip_area); - if(ok) lv_draw_rect(q->draw_area, &quarter_area, q->draw_dsc); - } - else if(q->start_quarter == 3 || q->end_quarter == 3) { - /*Start and/or end arcs here*/ - if(q->start_quarter == 3) { - quarter_area.x2 = q->center_x + q->radius; - quarter_area.y2 = q->center_y - 1; - - quarter_area.x1 = q->center_x + ((lv_trigo_sin(q->start_angle + 90) * (q->radius - q->width)) >> LV_TRIGO_SHIFT); - quarter_area.y1 = q->center_y + ((lv_trigo_sin(q->start_angle) * (q->radius)) >> LV_TRIGO_SHIFT); - - bool ok = _lv_area_intersect(&quarter_area, &quarter_area, q->clip_area); - if(ok) lv_draw_rect(q->draw_area, &quarter_area, q->draw_dsc); - } - if(q->end_quarter == 3) { - quarter_area.x1 = q->center_x; - quarter_area.y1 = q->center_y - q->radius; - - quarter_area.x2 = q->center_x + ((lv_trigo_sin(q->end_angle + 90) * (q->radius)) >> LV_TRIGO_SHIFT); - quarter_area.y2 = q->center_y + ((lv_trigo_sin(q->end_angle) * (q->radius - q->width)) >> LV_TRIGO_SHIFT); - - bool ok = _lv_area_intersect(&quarter_area, &quarter_area, q->clip_area); - if(ok) lv_draw_rect(q->draw_area, &quarter_area, q->draw_dsc); - } - } - else if((q->start_quarter == q->end_quarter && q->start_quarter != 3 && q->end_angle < q->start_angle) || - (q->start_quarter == 2 && q->end_quarter == 0) || - (q->start_quarter == 1 && q->end_quarter == 0) || - (q->start_quarter == 2 && q->end_quarter == 1)) { - /*Arc crosses here*/ - quarter_area.x1 = q->center_x; - quarter_area.y1 = q->center_y - q->radius; - quarter_area.x2 = q->center_x + q->radius; - quarter_area.y2 = q->center_y - 1; - - bool ok = _lv_area_intersect(&quarter_area, &quarter_area, q->clip_area); - if(ok) lv_draw_rect(q->draw_area, &quarter_area, q->draw_dsc); - } -} - -static void get_rounded_area(int16_t angle, lv_coord_t radius, uint8_t thickness, lv_area_t * res_area) -{ - const uint8_t ps = 8; - const uint8_t pa = 127; - - int32_t thick_half = thickness / 2; - uint8_t thick_corr = (thickness & 0x01) ? 0 : 1; - - int32_t cir_x; - int32_t cir_y; - - cir_x = ((radius - thick_half) * lv_trigo_sin(90 - angle)) >> (LV_TRIGO_SHIFT - ps); - cir_y = ((radius - thick_half) * lv_trigo_sin(angle)) >> (LV_TRIGO_SHIFT - ps); - - /*Actually the center of the pixel need to be calculated so apply 1/2 px offset*/ - if(cir_x > 0) { - cir_x = (cir_x - pa) >> ps; - res_area->x1 = cir_x - thick_half + thick_corr; - res_area->x2 = cir_x + thick_half; - } - else { - cir_x = (cir_x + pa) >> ps; - res_area->x1 = cir_x - thick_half; - res_area->x2 = cir_x + thick_half - thick_corr; - } - - if(cir_y > 0) { - cir_y = (cir_y - pa) >> ps; - res_area->y1 = cir_y - thick_half + thick_corr; - res_area->y2 = cir_y + thick_half; - } - else { - cir_y = (cir_y + pa) >> ps; - res_area->y1 = cir_y - thick_half; - res_area->y2 = cir_y + thick_half - thick_corr; - } -} - -#endif /*LV_DRAW_COMPLEX*/ diff --git a/src/draw/lv_draw_arc.h b/src/draw/lv_draw_arc.h index 8868b0fe9..8e652a0a3 100644 --- a/src/draw/lv_draw_arc.h +++ b/src/draw/lv_draw_arc.h @@ -13,7 +13,10 @@ extern "C" { /********************* * INCLUDES *********************/ -#include "lv_draw_line.h" +#include "../lv_conf_internal.h" +#include "../misc/lv_color.h" +#include "../misc/lv_area.h" +#include "../misc/lv_style.h" /********************* * DEFINES @@ -35,7 +38,7 @@ typedef struct { * GLOBAL PROTOTYPES **********************/ -LV_ATTRIBUTE_FAST_MEM void lv_draw_arc_dsc_init(lv_draw_arc_dsc_t * dsc); +void lv_draw_arc_dsc_init(lv_draw_arc_dsc_t * dsc); /** * Draw an arc. (Can draw pie too with great thickness.) diff --git a/src/draw/lv_draw_blend.c b/src/draw/lv_draw_blend.c index 4d26160e3..0f846d319 100644 --- a/src/draw/lv_draw_blend.c +++ b/src/draw/lv_draw_blend.c @@ -33,42 +33,6 @@ * STATIC PROTOTYPES **********************/ -#if LV_USE_EXTERNAL_RENDERER == 0 -static void fill_set_px(const lv_area_t * disp_area, lv_color_t * disp_buf, const lv_area_t * draw_area, - lv_color_t color, lv_opa_t opa, - const lv_opa_t * mask, lv_draw_mask_res_t mask_res); - -LV_ATTRIBUTE_FAST_MEM static void fill_normal(const lv_area_t * disp_area, lv_color_t * disp_buf, - const lv_area_t * draw_area, - lv_color_t color, lv_opa_t opa, - const lv_opa_t * mask, lv_draw_mask_res_t mask_res); - -#if LV_DRAW_COMPLEX -static void fill_blended(const lv_area_t * disp_area, lv_color_t * disp_buf, const lv_area_t * draw_area, - lv_color_t color, lv_opa_t opa, - const lv_opa_t * mask, lv_draw_mask_res_t mask_res, lv_blend_mode_t mode); -#endif - -static void map_set_px(const lv_area_t * disp_area, lv_color_t * disp_buf, const lv_area_t * draw_area, - const lv_area_t * map_area, const lv_color_t * map_buf, lv_opa_t opa, - const lv_opa_t * mask, lv_draw_mask_res_t mask_res); - -LV_ATTRIBUTE_FAST_MEM static void map_normal(const lv_area_t * disp_area, lv_color_t * disp_buf, - const lv_area_t * draw_area, - const lv_area_t * map_area, const lv_color_t * map_buf, lv_opa_t opa, - const lv_opa_t * mask, lv_draw_mask_res_t mask_res); - -#if LV_DRAW_COMPLEX -static void map_blended(const lv_area_t * disp_area, lv_color_t * disp_buf, const lv_area_t * draw_area, - const lv_area_t * map_area, const lv_color_t * map_buf, lv_opa_t opa, - const lv_opa_t * mask, lv_draw_mask_res_t mask_res, lv_blend_mode_t mode); - -static inline lv_color_t color_blend_true_color_additive(lv_color_t fg, lv_color_t bg, lv_opa_t opa); -static inline lv_color_t color_blend_true_color_subtractive(lv_color_t fg, lv_color_t bg, lv_opa_t opa); -static inline lv_color_t color_blend_true_color_multiply(lv_color_t fg, lv_color_t bg, lv_opa_t opa); -#endif -#endif //LV_USE_GPU_SDL_RENDER - /********************** * STATIC VARIABLES **********************/ @@ -76,988 +40,82 @@ static inline lv_color_t color_blend_true_color_multiply(lv_color_t fg, lv_color /********************** * 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++; - -#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) { \ - if(*mask_tmp_x == LV_OPA_COVER) disp_buf_first[x] = map_buf_first[x]; \ - else disp_buf_first[x] = lv_color_mix(map_buf_first[x], disp_buf_first[x], *mask_tmp_x); \ - } \ - mask_tmp_x++; - -#define MAP_NORMAL_MASK_PX_SCR_TRANSP(x) \ - if(*mask_tmp_x) { \ - if(*mask_tmp_x == LV_OPA_COVER) disp_buf_first[x] = map_buf_first[x]; \ - else if(disp->driver->screen_transp) lv_color_mix_with_alpha(disp_buf_first[x], disp_buf_first[x].ch.alpha, \ - map_buf_first[x], *mask_tmp_x, &disp_buf_first[x], &disp_buf_first[x].ch.alpha); \ - else disp_buf_first[x] = lv_color_mix(map_buf_first[x], disp_buf_first[x], *mask_tmp_x); \ - } \ - mask_tmp_x++; /********************** * GLOBAL FUNCTIONS **********************/ -#if LV_USE_EXTERNAL_RENDERER == 0 -/** - * Fill and area in the display buffer. - * @param clip_area clip the fill to this area (absolute coordinates) - * @param fill_area fill this area (absolute coordinates) (should be clipped) - * @param color fill color - * @param mask a mask to apply on the fill (uint8_t array with 0x00..0xff values). - * Relative to fill area but its width is truncated to clip area. - * @param mask_res LV_MASK_RES_COVER: the mask has only 0xff values (no mask), - * LV_MASK_RES_TRANSP: the mask has only 0x00 values (full transparent), - * LV_MASK_RES_CHANGED: the mask has mixed values - * @param opa overall opacity in 0x00..0xff range - * @param mode blend mode from `lv_blend_mode_t` - */ -LV_ATTRIBUTE_FAST_MEM void _lv_blend_fill(const lv_area_t * clip_area, const lv_area_t * fill_area, - lv_color_t color, lv_opa_t * mask, lv_draw_mask_res_t mask_res, lv_opa_t opa, - lv_blend_mode_t mode) +LV_ATTRIBUTE_FAST_MEM void lv_draw_blend_fill(const lv_area_t * clip_area, const lv_area_t * fill_area, + lv_color_t color, lv_opa_t * mask, lv_draw_mask_res_t mask_res, lv_opa_t opa, + lv_blend_mode_t blend_mode) { /*Do not draw transparent things*/ if(opa < LV_OPA_MIN) return; if(mask_res == LV_DRAW_MASK_RES_TRANSP) return; - - lv_disp_t * disp = _lv_refr_get_disp_refreshing(); - lv_disp_draw_buf_t * draw_buf = lv_disp_get_draw_buf(disp); - const lv_area_t * disp_area = &draw_buf->area; - lv_color_t * disp_buf = draw_buf->buf_act; - - if(disp->driver->gpu_wait_cb) disp->driver->gpu_wait_cb(disp->driver); + if(mask_res == LV_DRAW_MASK_RES_FULL_COVER) mask = NULL; /*Get clipped fill area which is the real draw area. *It is always the same or inside `fill_area`*/ lv_area_t draw_area; if(!_lv_area_intersect(&draw_area, clip_area, fill_area)) return; - /*Now `draw_area` has absolute coordinates. - *Make it relative to `disp_area` to simplify the drawing to `disp_buf`*/ - lv_area_move(&draw_area, -disp_area->x1, -disp_area->y1); - - /*Round the values in the mask if anti-aliasing is disabled*/ - if(mask && disp->driver->antialiasing == 0 && mask) { - int32_t mask_w = lv_area_get_width(&draw_area); - int32_t i; - for(i = 0; i < mask_w; i++) mask[i] = mask[i] > 128 ? LV_OPA_COVER : LV_OPA_TRANSP; - } - - if(disp->driver->set_px_cb) { - fill_set_px(disp_area, disp_buf, &draw_area, color, opa, mask, mask_res); - } - else if(mode == LV_BLEND_MODE_NORMAL) { - fill_normal(disp_area, disp_buf, &draw_area, color, opa, mask, mask_res); - } -#if LV_DRAW_COMPLEX - else { - fill_blended(disp_area, disp_buf, &draw_area, color, opa, mask, mask_res, mode); - } -#endif -} - -/** - * Copy a map (image) to a display buffer. - * @param clip_area clip the map to this area (absolute coordinates) - * @param map_area area of the image (absolute coordinates) - * @param map_buf a pixels of the map (image) - * @param mask a mask to apply on every pixel (uint8_t array with 0x00..0xff values). - * Relative to map area but its width is truncated to clip area. - * @param mask_res LV_MASK_RES_COVER: the mask has only 0xff values (no mask), - * LV_MASK_RES_TRANSP: the mask has only 0x00 values (full transparent), - * LV_MASK_RES_CHANGED: the mask has mixed values - * @param opa overall opacity in 0x00..0xff range - * @param mode blend mode from `lv_blend_mode_t` - */ -LV_ATTRIBUTE_FAST_MEM void _lv_blend_map(const lv_area_t * clip_area, const lv_area_t * map_area, - const lv_color_t * map_buf, - lv_opa_t * mask, lv_draw_mask_res_t mask_res, - lv_opa_t opa, lv_blend_mode_t mode) -{ - /*Do not draw transparent things*/ - if(opa < LV_OPA_MIN) return; - if(mask_res == LV_DRAW_MASK_RES_TRANSP) return; - - /*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, clip_area, map_area); - if(!is_common) return; - lv_disp_t * disp = _lv_refr_get_disp_refreshing(); lv_disp_draw_buf_t * draw_buf = lv_disp_get_draw_buf(disp); const lv_area_t * disp_area = &draw_buf->area; lv_color_t * disp_buf = draw_buf->buf_act; + /*Now `draw_area` has absolute coordinates. + *Make it relative to `disp_area` to simplify the drawing to `disp_buf`*/ + lv_area_move(&draw_area, -disp_area->x1, -disp_area->y1); + + lv_coord_t stride = lv_area_get_width(disp_area); + if(disp->driver->gpu_wait_cb) disp->driver->gpu_wait_cb(disp->driver); + const lv_draw_backend_t * backend = lv_draw_backend_get(); + backend->blend_fill(disp_buf, stride, &draw_area, color, mask, opa, blend_mode); + +} + +void lv_draw_blend_map(const lv_area_t * clip_area, const lv_area_t * map_area, + const lv_color_t * map_buf, + lv_opa_t * mask, lv_draw_mask_res_t mask_res, + lv_opa_t opa, lv_blend_mode_t blend_mode) +{ + /*Do not draw transparent things*/ + if(opa < LV_OPA_MIN) return; + if(mask_res == LV_DRAW_MASK_RES_TRANSP) return; + if(mask_res == LV_DRAW_MASK_RES_FULL_COVER) mask = NULL; + + /*Get clipped fill area which is the real draw area. + *It is always the same or inside `map_area`*/ + lv_area_t draw_area; + if(!_lv_area_intersect(&draw_area, clip_area, map_area)) return; + + lv_disp_t * disp = _lv_refr_get_disp_refreshing(); + lv_disp_draw_buf_t * draw_buf = lv_disp_get_draw_buf(disp); + const lv_area_t * disp_area = &draw_buf->area; + lv_color_t * buf = draw_buf->buf_act; + + /*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; + lv_area_move(&draw_area, -disp_area->x1, -disp_area->y1); - /*Round the values in the mask if anti-aliasing is disabled*/ - if(mask && disp->driver->antialiasing == 0) { - int32_t mask_w = lv_area_get_width(&draw_area); - int32_t i; - for(i = 0; i < mask_w; i++) mask[i] = mask[i] > 128 ? LV_OPA_COVER : LV_OPA_TRANSP; - } - if(disp->driver->set_px_cb) { - map_set_px(disp_area, disp_buf, &draw_area, map_area, map_buf, opa, mask, mask_res); - } - else if(mode == LV_BLEND_MODE_NORMAL) { - map_normal(disp_area, disp_buf, &draw_area, map_area, map_buf, opa, mask, mask_res); - } -#if LV_DRAW_COMPLEX - else { - map_blended(disp_area, disp_buf, &draw_area, map_area, map_buf, opa, mask, mask_res, mode); - } -#endif + lv_area_t map_area_relative; + lv_area_copy(&map_area_relative, map_area); + lv_area_move(&map_area_relative, -disp_area->x1, -disp_area->y1); + + if(disp->driver->gpu_wait_cb) disp->driver->gpu_wait_cb(disp->driver); + + lv_coord_t stride = lv_area_get_width(disp_area); + + const lv_draw_backend_t * backend = lv_draw_backend_get(); + backend->blend_map(buf, stride, &draw_area, + map_buf, &map_area_relative, + mask, opa, blend_mode); } /********************** * STATIC FUNCTIONS **********************/ - -static void fill_set_px(const lv_area_t * disp_area, lv_color_t * disp_buf, const lv_area_t * draw_area, - lv_color_t color, lv_opa_t opa, - const lv_opa_t * mask, lv_draw_mask_res_t mask_res) -{ - - lv_disp_t * disp = _lv_refr_get_disp_refreshing(); - - /*Get the width of the `disp_area` it will be used to go to the next line*/ - int32_t disp_w = lv_area_get_width(disp_area); - - int32_t x; - int32_t y; - - if(mask_res == LV_DRAW_MASK_RES_FULL_COVER) { - for(y = draw_area->y1; y <= draw_area->y2; y++) { - for(x = draw_area->x1; x <= draw_area->x2; x++) { - disp->driver->set_px_cb(disp->driver, (void *)disp_buf, disp_w, x, y, color, opa); - } - } - } - else { - /*The mask is relative to the clipped area. - *In the cycles below mask will be indexed from `draw_area.x1` - *but it corresponds to zero index. So prepare `mask_tmp` accordingly.*/ - const lv_opa_t * mask_tmp = mask - draw_area->x1; - - /*Get the width of the `draw_area` it will be used to go to the next line of the mask*/ - int32_t draw_area_w = lv_area_get_width(draw_area); - - for(y = draw_area->y1; y <= draw_area->y2; y++) { - for(x = draw_area->x1; x <= draw_area->x2; x++) { - if(mask_tmp[x]) { - disp->driver->set_px_cb(disp->driver, (void *)disp_buf, disp_w, x, y, color, - (uint32_t)((uint32_t)opa * mask_tmp[x]) >> 8); - } - } - mask_tmp += draw_area_w; - } - } -} - -#if LV_USE_EXTERNAL_RENDERER == 0 -/** - * Fill an area with a color - * @param disp_area the current display area (destination area) - * @param disp_buf destination buffer - * @param draw_area fill this area (relative to `disp_area`) - * @param color fill color - * @param opa overall opacity in 0x00..0xff range - * @param mask a mask to apply on every pixel (uint8_t array with 0x00..0xff values). - * It fits into draw_area. - * @param mask_res LV_MASK_RES_COVER: the mask has only 0xff values (no mask), - * LV_MASK_RES_TRANSP: the mask has only 0x00 values (full transparent), - * LV_MASK_RES_CHANGED: the mask has mixed values - */ -LV_ATTRIBUTE_FAST_MEM static void fill_normal(const lv_area_t * disp_area, lv_color_t * disp_buf, - const lv_area_t * draw_area, - lv_color_t color, lv_opa_t opa, - const lv_opa_t * mask, lv_draw_mask_res_t mask_res) -{ - - lv_disp_t * disp = _lv_refr_get_disp_refreshing(); - - /*Get the width of the `disp_area` it will be used to go to the next line*/ - int32_t disp_w = lv_area_get_width(disp_area); - - int32_t draw_area_w = lv_area_get_width(draw_area); - int32_t draw_area_h = lv_area_get_height(draw_area); - - /*Create a temp. disp_buf which always point to the first pixel of the destination area*/ - lv_color_t * disp_buf_first = disp_buf + disp_w * draw_area->y1 + draw_area->x1; - - int32_t x; - int32_t y; - - /*Simple fill (maybe with opacity), no masking*/ - if(mask_res == LV_DRAW_MASK_RES_FULL_COVER) { - if(opa > LV_OPA_MAX) { -#if LV_USE_GPU_NXP_PXP - if(lv_area_get_size(draw_area) >= LV_GPU_NXP_PXP_FILL_SIZE_LIMIT) { - lv_gpu_nxp_pxp_fill(disp_buf, disp_w, draw_area, color, opa); - return; - } -#elif LV_USE_GPU_NXP_VG_LITE - if(lv_area_get_size(draw_area) >= LV_GPU_NXP_VG_LITE_FILL_SIZE_LIMIT) { - if(lv_gpu_nxp_vglite_fill(disp_buf, disp_w, lv_area_get_height(disp_area), draw_area, color, opa) == LV_RES_OK) { - return; - } - } -#elif LV_USE_GPU_STM32_DMA2D - if(lv_area_get_size(draw_area) >= 240) { - lv_gpu_stm32_dma2d_fill(disp_buf_first, disp_w, color, draw_area_w, draw_area_h); - return; - } -#endif - - if(disp->driver->gpu_fill_cb && lv_area_get_size(draw_area) > GPU_SIZE_LIMIT) { - disp->driver->gpu_fill_cb(disp->driver, disp_buf, disp_w, draw_area, color); - return; - } - - /*Software rendering*/ - for(y = 0; y < draw_area_h; y++) { - lv_color_fill(disp_buf_first, color, draw_area_w); - disp_buf_first += disp_w; - } - } - /*No mask with opacity*/ - else { -#if LV_USE_GPU_NXP_PXP - if(lv_area_get_size(draw_area) >= LV_GPU_NXP_PXP_FILL_OPA_SIZE_LIMIT) { - lv_gpu_nxp_pxp_fill(disp_buf, disp_w, draw_area, color, opa); - return; - } -#elif LV_USE_GPU_NXP_VG_LITE - if(lv_area_get_size(draw_area) >= LV_GPU_NXP_VG_LITE_FILL_OPA_SIZE_LIMIT) { - 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*/ - } -#endif - lv_color_t last_dest_color = lv_color_black(); - lv_color_t last_res_color = lv_color_mix(color, last_dest_color, opa); - - uint16_t color_premult[3]; - lv_color_premult(color, opa, color_premult); - lv_opa_t opa_inv = 255 - opa; - - for(y = 0; y < draw_area_h; y++) { - for(x = 0; x < draw_area_w; x++) { - if(last_dest_color.full != disp_buf_first[x].full) { - last_dest_color = disp_buf_first[x]; - -#if LV_COLOR_SCREEN_TRANSP - if(disp->driver->screen_transp) { - lv_color_mix_with_alpha(disp_buf_first[x], disp_buf_first[x].ch.alpha, color, opa, &last_res_color, - &last_res_color.ch.alpha); - } - else -#endif - { - last_res_color = lv_color_mix_premult(color_premult, disp_buf_first[x], opa_inv); - } - } - disp_buf_first[x] = last_res_color; - } - disp_buf_first += disp_w; - } - } - } - /*Masked*/ - else { - 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++) { - for(x = 0; x < draw_area_w && ((lv_uintptr_t)(mask) & 0x3); x++) { - FILL_NORMAL_MASK_PX(color) - } - - for(; x <= x_end4; x += 4) { - 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 - 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; - } - } - - for(; x < draw_area_w ; x++) { - FILL_NORMAL_MASK_PX(color) - } - disp_buf_first += (disp_w - draw_area_w); - } - } - /*Handle opa and mask values too*/ - else { - /*Buffer the result color to avoid recalculating the same color*/ - lv_color_t last_dest_color; - lv_color_t last_res_color; - lv_opa_t last_mask = LV_OPA_TRANSP; - last_dest_color.full = disp_buf_first[0].full; - last_res_color.full = disp_buf_first[0].full; - lv_opa_t opa_tmp = LV_OPA_TRANSP; - - for(y = draw_area->y1; y <= draw_area->y2; y++) { - const lv_opa_t * mask_tmp_x = mask; - for(x = 0; x < draw_area_w; x++) { - if(*mask_tmp_x) { - if(*mask_tmp_x != last_mask) opa_tmp = *mask_tmp_x == LV_OPA_COVER ? opa : - (uint32_t)((uint32_t)(*mask_tmp_x) * opa) >> 8; - if(*mask_tmp_x != last_mask || last_dest_color.full != disp_buf_first[x].full) { -#if LV_COLOR_SCREEN_TRANSP - if(disp->driver->screen_transp) { - lv_color_mix_with_alpha(disp_buf_first[x], disp_buf_first[x].ch.alpha, color, opa_tmp, &last_res_color, - &last_res_color.ch.alpha); - } - else -#endif - { - if(opa_tmp == LV_OPA_COVER) last_res_color = color; - else last_res_color = lv_color_mix(color, disp_buf_first[x], opa_tmp); - } - last_mask = *mask_tmp_x; - last_dest_color.full = disp_buf_first[x].full; - } - disp_buf_first[x] = last_res_color; - } - mask_tmp_x++; - } - disp_buf_first += disp_w; - mask += draw_area_w; - } - } - } -} - -#if LV_DRAW_COMPLEX -/** - * Fill an area with a color but apply blending algorithms - * @param disp_area the current display area (destination area) - * @param disp_buf destination buffer - * @param draw_area fill this area (relative to `disp_area`) - * @param color fill color - * @param opa overall opacity in 0x00..0xff range - * @param mask a mask to apply on every pixel (uint8_t array with 0x00..0xff values). - * It fits into draw_area. - * @param mask_res LV_MASK_RES_COVER: the mask has only 0xff values (no mask), - * LV_MASK_RES_TRANSP: the mask has only 0x00 values (full transparent), - * LV_MASK_RES_CHANGED: the mask has mixed values - * @param mode blend mode from `lv_blend_mode_t` - */ -static void fill_blended(const lv_area_t * disp_area, lv_color_t * disp_buf, const lv_area_t * draw_area, - lv_color_t color, lv_opa_t opa, - const lv_opa_t * mask, lv_draw_mask_res_t mask_res, lv_blend_mode_t mode) -{ - /*Get the width of the `disp_area` it will be used to go to the next line*/ - int32_t disp_w = lv_area_get_width(disp_area); - - /*Create a temp. disp_buf which always point to current line to draw*/ - lv_color_t * disp_buf_tmp = disp_buf + disp_w * draw_area->y1; - - lv_color_t (*blend_fp)(lv_color_t, lv_color_t, lv_opa_t); - switch(mode) { - case LV_BLEND_MODE_ADDITIVE: - blend_fp = color_blend_true_color_additive; - break; - case LV_BLEND_MODE_SUBTRACTIVE: - blend_fp = color_blend_true_color_subtractive; - break; - case LV_BLEND_MODE_MULTIPLY: - blend_fp = color_blend_true_color_multiply; - break; - default: - LV_LOG_WARN("fill_blended: unsupported blend mode"); - return; - } - - int32_t x; - int32_t y; - - /*Simple fill (maybe with opacity), no masking*/ - if(mask_res == LV_DRAW_MASK_RES_FULL_COVER) { - lv_color_t last_dest_color = lv_color_black(); - lv_color_t last_res_color = lv_color_mix(color, last_dest_color, opa); - for(y = draw_area->y1; y <= draw_area->y2; y++) { - for(x = draw_area->x1; x <= draw_area->x2; x++) { - if(last_dest_color.full != disp_buf_tmp[x].full) { - last_dest_color = disp_buf_tmp[x]; - last_res_color = blend_fp(color, disp_buf_tmp[x], opa); - } - disp_buf_tmp[x] = last_res_color; - } - disp_buf_tmp += disp_w; - } - } - /*Masked*/ - else { - /*Get the width of the `draw_area` it will be used to go to the next line of the mask*/ - int32_t draw_area_w = lv_area_get_width(draw_area); - - /*The mask is relative to the clipped area. - *In the cycles below mask will be indexed from `draw_area.x1` - *but it corresponds to zero index. So prepare `mask_tmp` accordingly.*/ - const lv_opa_t * mask_tmp = mask - draw_area->x1; - - /*Buffer the result color to avoid recalculating the same color*/ - lv_color_t last_dest_color; - lv_color_t last_res_color; - lv_opa_t last_mask = LV_OPA_TRANSP; - last_dest_color.full = disp_buf_tmp[0].full; - last_res_color.full = disp_buf_tmp[0].full; - - for(y = draw_area->y1; y <= draw_area->y2; y++) { - for(x = draw_area->x1; x <= draw_area->x2; x++) { - if(mask_tmp[x] == 0) continue; - if(mask_tmp[x] != last_mask || last_dest_color.full != disp_buf_tmp[x].full) { - lv_opa_t opa_tmp = mask_tmp[x] >= LV_OPA_MAX ? opa : (uint32_t)((uint32_t)mask_tmp[x] * opa) >> 8; - - last_res_color = blend_fp(color, disp_buf_tmp[x], opa_tmp); - last_mask = mask_tmp[x]; - last_dest_color.full = disp_buf_tmp[x].full; - } - disp_buf_tmp[x] = last_res_color; - } - disp_buf_tmp += disp_w; - mask_tmp += draw_area_w; - } - } -} -#endif -#endif // LV_USE_GPU_SDL_RENDER - -static void map_set_px(const lv_area_t * disp_area, lv_color_t * disp_buf, const lv_area_t * draw_area, - const lv_area_t * map_area, const lv_color_t * map_buf, lv_opa_t opa, - const lv_opa_t * mask, lv_draw_mask_res_t mask_res) - -{ - lv_disp_t * disp = _lv_refr_get_disp_refreshing(); - - /*Get the width of the `disp_area` it will be used to go to the next line*/ - int32_t disp_w = lv_area_get_width(disp_area); - - /*Get the width of the `draw_area` it will be used to go to the next line of the mask*/ - int32_t draw_area_w = lv_area_get_width(draw_area); - - /*Get the width of the `mask_area` it will be used to go to the next line*/ - int32_t map_w = lv_area_get_width(map_area); - - /*Create a temp. map_buf which always point to current line to draw*/ - const lv_color_t * map_buf_tmp = map_buf + map_w * (draw_area->y1 - (map_area->y1 - disp_area->y1)); - - map_buf_tmp += (draw_area->x1 - (map_area->x1 - disp_area->x1)); - map_buf_tmp -= draw_area->x1; - int32_t x; - int32_t y; - - if(mask_res == LV_DRAW_MASK_RES_FULL_COVER) { - for(y = draw_area->y1; y <= draw_area->y2; y++) { - for(x = draw_area->x1; x <= draw_area->x2; x++) { - disp->driver->set_px_cb(disp->driver, (void *)disp_buf, disp_w, x, y, map_buf_tmp[x], opa); - } - map_buf_tmp += map_w; - } - } - else { - /*The mask is relative to the clipped area. - *In the cycles below mask will be indexed from `draw_area.x1` - *but it corresponds to zero index. So prepare `mask_tmp` accordingly.*/ - const lv_opa_t * mask_tmp = mask - draw_area->x1; - - for(y = draw_area->y1; y <= draw_area->y2; y++) { - for(x = draw_area->x1; x <= draw_area->x2; x++) { - if(mask_tmp[x]) { - disp->driver->set_px_cb(disp->driver, (void *)disp_buf, disp_w, x, y, map_buf_tmp[x], - (uint32_t)((uint32_t)opa * mask_tmp[x]) >> 8); - } - } - mask_tmp += draw_area_w; - map_buf_tmp += map_w; - } - } -} - -/** - * Copy an image to an area - * @param disp_area the current display area (destination area) - * @param disp_buf destination buffer - * @param map_area coordinates of the map (image) to copy. (absolute coordinates) - * @param map_buf the pixel of the image - * @param opa overall opacity in 0x00..0xff range - * @param mask a mask to apply on every pixel (uint8_t array with 0x00..0xff values). - * It fits into draw_area. - * @param mask_res LV_MASK_RES_COVER: the mask has only 0xff values (no mask), - * LV_MASK_RES_TRANSP: the mask has only 0x00 values (full transparent), - * LV_MASK_RES_CHANGED: the mask has mixed values - */ -LV_ATTRIBUTE_FAST_MEM static void map_normal(const lv_area_t * disp_area, lv_color_t * disp_buf, - const lv_area_t * draw_area, - const lv_area_t * map_area, const lv_color_t * map_buf, lv_opa_t opa, - const lv_opa_t * mask, lv_draw_mask_res_t mask_res) -{ - - /*Get the width of the `disp_area` it will be used to go to the next line*/ - int32_t disp_w = lv_area_get_width(disp_area); - - int32_t draw_area_w = lv_area_get_width(draw_area); - int32_t draw_area_h = lv_area_get_height(draw_area); - - /*Get the width of the `mask_area` it will be used to go to the next line*/ - int32_t map_w = lv_area_get_width(map_area); - - /*Create a temp. disp_buf which always point to first pixel to draw*/ - lv_color_t * disp_buf_first = disp_buf + disp_w * draw_area->y1 + draw_area->x1; - - /*Create a temp. map_buf which always point to first pixel to draw from the map*/ - const lv_color_t * map_buf_first = map_buf + map_w * (draw_area->y1 - (map_area->y1 - disp_area->y1)); - map_buf_first += (draw_area->x1 - (map_area->x1 - disp_area->x1)); - -#if LV_COLOR_SCREEN_TRANSP - lv_disp_t * disp = _lv_refr_get_disp_refreshing(); -#endif - - int32_t x; - int32_t y; - - /*Simple fill (maybe with opacity), no masking*/ - if(mask_res == LV_DRAW_MASK_RES_FULL_COVER) { - if(opa > LV_OPA_MAX) { -#if LV_USE_GPU_NXP_PXP - if(lv_area_get_size(draw_area) >= LV_GPU_NXP_PXP_BLIT_SIZE_LIMIT) { - lv_gpu_nxp_pxp_blit(disp_buf_first, disp_w, map_buf_first, map_w, draw_area_w, draw_area_h, opa); - return; - } -#elif (LV_USE_GPU_NXP_VG_LITE) - if(lv_area_get_size(draw_area) >= LV_GPU_NXP_VG_LITE_BLIT_SIZE_LIMIT) { - - lv_gpu_nxp_vglite_blit_info_t blit; - - blit.src = map_buf; - blit.src_width = draw_area_w; - blit.src_height = draw_area_h; - 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; - blit.src_area.y2 = blit.src_area.y1 + draw_area_h; - - blit.dst = disp_buf; - blit.dst_width = lv_area_get_width(disp_area); - blit.dst_height = lv_area_get_height(disp_area); - 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; - blit.dst_area.y2 = blit.dst_area.y1 + draw_area_h; - - blit.opa = opa; - - if(lv_gpu_nxp_vglite_blit(&blit) == 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) { - lv_gpu_stm32_dma2d_copy(disp_buf_first, disp_w, map_buf_first, map_w, draw_area_w, draw_area_h); - return; - } -#endif - - /*Software rendering*/ - for(y = 0; y < draw_area_h; y++) { - lv_memcpy(disp_buf_first, map_buf_first, draw_area_w * sizeof(lv_color_t)); - disp_buf_first += disp_w; - map_buf_first += map_w; - } - } - else { -#if LV_USE_GPU_NXP_PXP - if(lv_area_get_size(draw_area) >= LV_GPU_NXP_PXP_BLIT_OPA_SIZE_LIMIT) { - lv_gpu_nxp_pxp_blit(disp_buf_first, disp_w, map_buf_first, map_w, draw_area_w, draw_area_h, opa); - return; - } -#elif (LV_USE_GPU_NXP_VG_LITE) - if(lv_area_get_size(draw_area) >= LV_GPU_NXP_VG_LITE_BLIT_OPA_SIZE_LIMIT) { - - lv_gpu_nxp_vglite_blit_info_t blit; - - blit.src = map_buf; - blit.src_width = lv_area_get_width(map_area); - blit.src_height = lv_area_get_height(map_area); - 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; - blit.src_area.y2 = blit.src_area.y1 + draw_area_h; - - blit.dst = disp_buf; - blit.dst_width = lv_area_get_width(disp_area); - blit.dst_height = lv_area_get_height(disp_area); - 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; - blit.dst_area.y2 = blit.dst_area.y1 + draw_area_h; - - blit.opa = opa; - - if(lv_gpu_nxp_vglite_blit(&blit) == 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) { - lv_gpu_stm32_dma2d_blend(disp_buf_first, disp_w, map_buf_first, opa, map_w, draw_area_w, draw_area_h); - return; - } -#endif - - /*Software rendering*/ - - for(y = 0; y < draw_area_h; y++) { - for(x = 0; x < draw_area_w; x++) { -#if LV_COLOR_SCREEN_TRANSP - if(disp->driver->screen_transp) { - lv_color_mix_with_alpha(disp_buf_first[x], disp_buf_first[x].ch.alpha, map_buf_first[x], opa, &disp_buf_first[x], - &disp_buf_first[x].ch.alpha); - } - else -#endif - { - disp_buf_first[x] = lv_color_mix(map_buf_first[x], disp_buf_first[x], opa); - } - } - disp_buf_first += disp_w; - map_buf_first += map_w; - } - } - } - /*Masked*/ - else { - /*Only the mask matters*/ - if(opa > LV_OPA_MAX) { - /*Go to the first pixel of the row*/ - - int32_t x_end4 = draw_area_w - 4; - - 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++) { - MAP_NORMAL_MASK_PX(x); - } -#else - for(x = 0; x < draw_area_w && ((lv_uintptr_t)mask_tmp_x & 0x3); x++) { -#if LV_COLOR_SCREEN_TRANSP - MAP_NORMAL_MASK_PX_SCR_TRANSP(x) -#else - MAP_NORMAL_MASK_PX(x) -#endif - } - - uint32_t * mask32 = (uint32_t *)mask_tmp_x; - for(; x < x_end4; x += 4) { - if(*mask32) { - if((*mask32) == 0xFFFFFFFF) { - disp_buf_first[x] = map_buf_first[x]; - disp_buf_first[x + 1] = map_buf_first[x + 1]; - disp_buf_first[x + 2] = map_buf_first[x + 2]; - disp_buf_first[x + 3] = map_buf_first[x + 3]; - } - else { - mask_tmp_x = (const lv_opa_t *)mask32; -#if LV_COLOR_SCREEN_TRANSP - MAP_NORMAL_MASK_PX_SCR_TRANSP(x) - MAP_NORMAL_MASK_PX_SCR_TRANSP(x + 1) - MAP_NORMAL_MASK_PX_SCR_TRANSP(x + 2) - MAP_NORMAL_MASK_PX_SCR_TRANSP(x + 3) -#else - MAP_NORMAL_MASK_PX(x) - MAP_NORMAL_MASK_PX(x + 1) - MAP_NORMAL_MASK_PX(x + 2) - MAP_NORMAL_MASK_PX(x + 3) -#endif - } - } - mask32++; - } - - mask_tmp_x = (const lv_opa_t *)mask32; - for(; x < draw_area_w ; x++) { -#if LV_COLOR_SCREEN_TRANSP - MAP_NORMAL_MASK_PX_SCR_TRANSP(x) -#else - MAP_NORMAL_MASK_PX(x) -#endif - } -#endif - disp_buf_first += disp_w; - mask += draw_area_w; - map_buf_first += map_w; - } - } - /*Handle opa and mask values too*/ - else { - for(y = 0; y < draw_area_h; y++) { - for(x = 0; x < draw_area_w; x++) { - if(mask[x]) { - lv_opa_t opa_tmp = mask[x] >= LV_OPA_MAX ? opa : ((opa * mask[x]) >> 8); -#if LV_COLOR_SCREEN_TRANSP - if(disp->driver->screen_transp) { - lv_color_mix_with_alpha(disp_buf_first[x], disp_buf_first[x].ch.alpha, map_buf_first[x], opa_tmp, &disp_buf_first[x], - &disp_buf_first[x].ch.alpha); - } - else -#endif - { - disp_buf_first[x] = lv_color_mix(map_buf_first[x], disp_buf_first[x], opa_tmp); - } - } - } - disp_buf_first += disp_w; - mask += draw_area_w; - map_buf_first += map_w; - } - } - } -} -#if LV_DRAW_COMPLEX -static void map_blended(const lv_area_t * disp_area, lv_color_t * disp_buf, const lv_area_t * draw_area, - const lv_area_t * map_area, const lv_color_t * map_buf, lv_opa_t opa, - const lv_opa_t * mask, lv_draw_mask_res_t mask_res, lv_blend_mode_t mode) -{ - - /*Get the width of the `disp_area` it will be used to go to the next line*/ - int32_t disp_w = lv_area_get_width(disp_area); - - /*Get the width of the `draw_area` it will be used to go to the next line of the mask*/ - int32_t draw_area_w = lv_area_get_width(draw_area); - - /*Get the width of the `mask_area` it will be used to go to the next line*/ - int32_t map_w = lv_area_get_width(map_area); - - /*Create a temp. disp_buf which always point to current line to draw*/ - lv_color_t * disp_buf_tmp = disp_buf + disp_w * draw_area->y1; - - /*Create a temp. map_buf which always point to current line to draw*/ - const lv_color_t * map_buf_tmp = map_buf + map_w * (draw_area->y1 - (map_area->y1 - disp_area->y1)); - - lv_color_t (*blend_fp)(lv_color_t, lv_color_t, lv_opa_t); - switch(mode) { - case LV_BLEND_MODE_ADDITIVE: - blend_fp = color_blend_true_color_additive; - break; - case LV_BLEND_MODE_SUBTRACTIVE: - blend_fp = color_blend_true_color_subtractive; - break; - case LV_BLEND_MODE_MULTIPLY: - blend_fp = color_blend_true_color_multiply; - break; - default: - LV_LOG_WARN("fill_blended: unsupported blend mode"); - return; - } - - int32_t x; - int32_t y; - - /*Simple fill (maybe with opacity), no masking*/ - if(mask_res == LV_DRAW_MASK_RES_FULL_COVER) { - /*Go to the first px of the row*/ - map_buf_tmp += (draw_area->x1 - (map_area->x1 - disp_area->x1)); - - /*The map will be indexed from `draw_area->x1` so compensate it.*/ - map_buf_tmp -= draw_area->x1; - - for(y = draw_area->y1; y <= draw_area->y2; y++) { - for(x = draw_area->x1; x <= draw_area->x2; x++) { - 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; - } - } - /*Masked*/ - else { - /*The mask is relative to the clipped area. - *In the cycles below mask will be indexed from `draw_area.x1` - *but it corresponds to zero index. So prepare `mask_tmp` accordingly.*/ - const lv_opa_t * mask_tmp = mask - draw_area->x1; - - map_buf_tmp -= draw_area->x1; - for(y = draw_area->y1; y <= draw_area->y2; y++) { - 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); - 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; - map_buf_tmp += map_w; - } - } -} - -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; - fg.full = LV_MIN(tmp, 1); -#else - tmp = bg.ch.red + fg.ch.red; -#if LV_COLOR_DEPTH == 8 - fg.ch.red = LV_MIN(tmp, 7); -#elif LV_COLOR_DEPTH == 16 - fg.ch.red = LV_MIN(tmp, 31); -#elif LV_COLOR_DEPTH == 32 - fg.ch.red = LV_MIN(tmp, 255); -#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 - tmp = bg.ch.green + fg.ch.green; - fg.ch.green = LV_MIN(tmp, 63); -#else - tmp = (bg.ch.green_h << 3) + bg.ch.green_l + (fg.ch.green_h << 3) + fg.ch.green_l; - tmp = LV_MIN(tmp, 63); - fg.ch.green_h = tmp >> 3; - fg.ch.green_l = tmp & 0x7; -#endif - -#elif LV_COLOR_DEPTH == 32 - tmp = bg.ch.green + fg.ch.green; - fg.ch.green = LV_MIN(tmp, 255); -#endif - - tmp = bg.ch.blue + fg.ch.blue; -#if LV_COLOR_DEPTH == 8 - fg.ch.blue = LV_MIN(tmp, 4); -#elif LV_COLOR_DEPTH == 16 - fg.ch.blue = LV_MIN(tmp, 31); -#elif LV_COLOR_DEPTH == 32 - fg.ch.blue = LV_MIN(tmp, 255); -#endif -#endif - - if(opa == LV_OPA_COVER) return fg; - - return lv_color_mix(fg, bg, opa); -} - -static inline lv_color_t color_blend_true_color_subtractive(lv_color_t fg, lv_color_t bg, lv_opa_t opa) -{ - if(opa <= LV_OPA_MIN) return bg; - - int32_t tmp; - tmp = bg.ch.red - fg.ch.red; - fg.ch.red = LV_MAX(tmp, 0); - -#if LV_COLOR_16_SWAP == 0 - tmp = bg.ch.green - fg.ch.green; - fg.ch.green = LV_MAX(tmp, 0); -#else - tmp = (bg.ch.green_h << 3) + bg.ch.green_l + (fg.ch.green_h << 3) + fg.ch.green_l; - tmp = LV_MAX(tmp, 0); - fg.ch.green_h = tmp >> 3; - fg.ch.green_l = tmp & 0x7; -#endif - - tmp = bg.ch.blue - fg.ch.blue; - fg.ch.blue = LV_MAX(tmp, 0); - - if(opa == LV_OPA_COVER) return fg; - - return lv_color_mix(fg, bg, opa); -} - -static inline lv_color_t color_blend_true_color_multiply(lv_color_t fg, lv_color_t bg, lv_opa_t opa) -{ - if(opa <= LV_OPA_MIN) return bg; - -#if LV_COLOR_DEPTH == 32 - fg.ch.red = (fg.ch.red * bg.ch.red) >> 8; - fg.ch.green = (fg.ch.green * bg.ch.green) >> 8; - fg.ch.blue = (fg.ch.blue * bg.ch.blue) >> 8; -#elif LV_COLOR_DEPTH == 16 - fg.ch.red = (fg.ch.red * bg.ch.red) >> 5; - fg.ch.blue = (fg.ch.blue * bg.ch.blue) >> 5; - LV_COLOR_SET_G(fg, (LV_COLOR_GET_G(fg) * LV_COLOR_GET_G(bg)) >> 6); -#elif LV_COLOR_DEPTH == 8 - fg.ch.red = (fg.ch.red * bg.ch.red) >> 3; - fg.ch.green = (fg.ch.green * bg.ch.green) >> 3; - fg.ch.blue = (fg.ch.blue * bg.ch.blue) >> 2; -#endif - - if(opa == LV_OPA_COVER) return fg; - - return lv_color_mix(fg, bg, opa); -} - -#endif - -#endif // LV_USE_GPU_SDL_RENDER diff --git a/src/draw/lv_draw_blend.h b/src/draw/lv_draw_blend.h index a2002f628..cf7e3aff7 100644 --- a/src/draw/lv_draw_blend.h +++ b/src/draw/lv_draw_blend.h @@ -30,15 +30,37 @@ extern "C" { * GLOBAL PROTOTYPES **********************/ -//! @cond Doxygen_Suppress -LV_ATTRIBUTE_FAST_MEM void _lv_blend_fill(const lv_area_t * clip_area, const lv_area_t * fill_area, lv_color_t color, - lv_opa_t * mask, lv_draw_mask_res_t mask_res, lv_opa_t opa, lv_blend_mode_t mode); +/** + * Fill an area with a color in the display's draw buffer. + * @param clip_area the current clip area (absolute coordinate) + * @param fill_area the area to fill (absolute coordinate) + * @param color the fill color + * @param mask an alpha mask to apply on `fill_area` or `NULL` to simply fill. (Interpreted on the fill area) + * @param mask_res LV_MASK_RES_COVER: the mask has only 0xff values (no mask), + * LV_MASK_RES_TRANSP: the mask has only 0x00 values (full transparent), + * LV_MASK_RES_CHANGED: the mask has mixed values + * @param opa overall opacity + * @param mode blend mode from `lv_blend_mode_t` + */ +void lv_draw_blend_fill(const lv_area_t * clip_area, const lv_area_t * fill_area, lv_color_t color, + lv_opa_t * mask, lv_draw_mask_res_t mask_res, lv_opa_t opa, lv_blend_mode_t mode); -LV_ATTRIBUTE_FAST_MEM void _lv_blend_map(const lv_area_t * clip_area, const lv_area_t * map_area, - const lv_color_t * map_buf, - lv_opa_t * mask, lv_draw_mask_res_t mask_res, lv_opa_t opa, lv_blend_mode_t mode); +/** + * Copy a source buffer to the display's draw buffer. + * @param clip_area clip the result to this area (absolute coordinates) + * @param src_area coordinates of the source buffer (absolute coordinates) + * @param src_buf the source buffer + * @param mask an alpha mask to apply on `clip_area` or `NULL` to simply fill. (Interpreted on the clip area) + * @param mask_res LV_MASK_RES_COVER: the mask has only 0xff values (no mask), + * LV_MASK_RES_TRANSP: the mask has only 0x00 values (full transparent), + * LV_MASK_RES_CHANGED: the mask has mixed values + * @param opa overall opacity in 0x00..0xff range + * @param mode blend mode from `lv_blend_mode_t` + */ +void lv_draw_blend_map(const lv_area_t * clip_area, const lv_area_t * src_area, + const lv_color_t * src_buf, + lv_opa_t * mask, lv_draw_mask_res_t mask_res, lv_opa_t opa, lv_blend_mode_t mode); -//! @endcond /********************** * MACROS **********************/ diff --git a/src/draw/lv_draw_img.c b/src/draw/lv_draw_img.c index 35ece92d1..60c7342a8 100644 --- a/src/draw/lv_draw_img.c +++ b/src/draw/lv_draw_img.c @@ -13,11 +13,6 @@ #include "../core/lv_refr.h" #include "../misc/lv_mem.h" #include "../misc/lv_math.h" -#if LV_USE_GPU_STM32_DMA2D - #include "../gpu/lv_gpu_stm32_dma2d.h" -#elif LV_USE_GPU_NXP_PXP - #include "../gpu/lv_gpu_nxp_pxp.h" -#endif /********************* * DEFINES @@ -30,19 +25,12 @@ /********************** * STATIC PROTOTYPES **********************/ -#if LV_USE_EXTERNAL_RENDERER == 0 LV_ATTRIBUTE_FAST_MEM static lv_res_t lv_img_draw_core(const lv_area_t * coords, const lv_area_t * clip_area, const void * src, const lv_draw_img_dsc_t * draw_dsc); -LV_ATTRIBUTE_FAST_MEM static void lv_draw_map(const lv_area_t * map_area, const lv_area_t * clip_area, - const uint8_t * map_p, - const lv_draw_img_dsc_t * draw_dsc, - bool chroma_key, bool alpha_byte); - static void show_error(const lv_area_t * coords, const lv_area_t * clip_area, const char * msg); static void draw_cleanup(_lv_img_cache_entry_t * cache); -#endif /********************** * STATIC VARIABLES @@ -65,7 +53,6 @@ void lv_draw_img_dsc_init(lv_draw_img_dsc_t * dsc) dsc->antialias = LV_COLOR_DEPTH > 8 ? 1 : 0; } -#if LV_USE_EXTERNAL_RENDERER == 0 /** * Draw an image * @param coords the coordinates of the image @@ -92,7 +79,6 @@ void lv_draw_img(const lv_area_t * coords, const lv_area_t * mask, const void * return; } } -#endif //LV_USE_GPU_SDL_RENDER /** * Get the pixel size of a color format in bits @@ -233,7 +219,6 @@ lv_img_src_t lv_img_src_get_type(const void * src) * STATIC FUNCTIONS **********************/ -#if LV_USE_EXTERNAL_RENDERER == 0 LV_ATTRIBUTE_FAST_MEM static lv_res_t lv_img_draw_core(const lv_area_t * coords, const lv_area_t * clip_area, const void * src, const lv_draw_img_dsc_t * draw_dsc) @@ -244,6 +229,8 @@ LV_ATTRIBUTE_FAST_MEM static lv_res_t lv_img_draw_core(const lv_area_t * coords, if(cdsc == NULL) return LV_RES_INV; + + const lv_draw_backend_t * backend = lv_draw_backend_get(); bool chroma_keyed = lv_img_cf_is_chroma_keyed(cdsc->dec_dsc.header.cf); bool alpha_byte = lv_img_cf_has_alpha(cdsc->dec_dsc.header.cf); @@ -278,7 +265,7 @@ LV_ATTRIBUTE_FAST_MEM static lv_res_t lv_img_draw_core(const lv_area_t * coords, return LV_RES_OK; } - lv_draw_map(coords, &mask_com, cdsc->dec_dsc.img_data, draw_dsc, chroma_keyed, alpha_byte); + backend->draw_img(coords, &mask_com, cdsc->dec_dsc.img_data, draw_dsc, chroma_keyed, alpha_byte); } /*The whole uncompressed image is not available. Try to read it line-by-line*/ else { @@ -317,7 +304,7 @@ LV_ATTRIBUTE_FAST_MEM static lv_res_t lv_img_draw_core(const lv_area_t * coords, return LV_RES_INV; } - lv_draw_map(&line, &mask_line, buf, draw_dsc, chroma_keyed, alpha_byte); + backend->draw_img(&line, &mask_line, buf, draw_dsc, chroma_keyed, alpha_byte); line.y1++; line.y2++; y++; @@ -329,319 +316,6 @@ LV_ATTRIBUTE_FAST_MEM static lv_res_t lv_img_draw_core(const lv_area_t * coords, return LV_RES_OK; } -/** - * Draw a color map to the display (image) - * @param map_area coordinates the color map - * @param clip_area the map will drawn only on this area (truncated to draw_buf area) - * @param map_p pointer to a lv_color_t array - * @param draw_dsc pointer to an initialized `lv_draw_img_dsc_t` variable - * @param chroma_key true: enable transparency of LV_IMG_LV_COLOR_TRANSP color pixels - * @param alpha_byte true: extra alpha byte is inserted for every pixel - */ -LV_ATTRIBUTE_FAST_MEM static void lv_draw_map(const lv_area_t * map_area, const lv_area_t * clip_area, - const uint8_t * map_p, - const lv_draw_img_dsc_t * draw_dsc, - bool chroma_key, bool alpha_byte) -{ - /*Use the clip area as draw area*/ - lv_area_t draw_area; - lv_area_copy(&draw_area, clip_area); - - lv_disp_t * disp = _lv_refr_get_disp_refreshing(); - lv_disp_draw_buf_t * draw_buf = lv_disp_get_draw_buf(disp); - const lv_area_t * disp_area = &draw_buf->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; - - bool mask_any = lv_draw_mask_is_any(clip_area); - - /*The simplest case just copy the pixels into the draw_buf*/ - if(!mask_any && draw_dsc->angle == 0 && draw_dsc->zoom == LV_IMG_ZOOM_NONE && - chroma_key == false && alpha_byte == false && draw_dsc->recolor_opa == LV_OPA_TRANSP) { - _lv_blend_map(clip_area, map_area, (lv_color_t *)map_p, NULL, LV_DRAW_MASK_RES_FULL_COVER, draw_dsc->opa, - draw_dsc->blend_mode); - } - -#if LV_USE_GPU_NXP_PXP - /*Simple case without masking and transformations*/ - else if(!mask_any && draw_dsc->angle == 0 && draw_dsc->zoom == LV_IMG_ZOOM_NONE && alpha_byte == false && - chroma_key == true && draw_dsc->recolor_opa == LV_OPA_TRANSP) { /*copy with color keying (+ alpha)*/ - lv_gpu_nxp_pxp_enable_color_key(); - _lv_blend_map(clip_area, map_area, (lv_color_t *)map_p, NULL, LV_DRAW_MASK_RES_FULL_COVER, draw_dsc->opa, - draw_dsc->blend_mode); - lv_gpu_nxp_pxp_disable_color_key(); - } - else if(!mask_any && draw_dsc->angle == 0 && draw_dsc->zoom == LV_IMG_ZOOM_NONE && alpha_byte == false && - chroma_key == false && draw_dsc->recolor_opa != LV_OPA_TRANSP) { /*copy with recolor (+ alpha)*/ - lv_gpu_nxp_pxp_enable_recolor(draw_dsc->recolor, draw_dsc->recolor_opa); - _lv_blend_map(clip_area, map_area, (lv_color_t *)map_p, NULL, LV_DRAW_MASK_RES_FULL_COVER, draw_dsc->opa, - draw_dsc->blend_mode); - lv_gpu_nxp_pxp_disable_recolor(); - } -#endif - /*In the other cases every pixel need to be checked one-by-one*/ - else { - //#if LV_DRAW_COMPLEX - /*The pixel size in byte is different if an alpha byte is added too*/ - uint8_t px_size_byte = alpha_byte ? LV_IMG_PX_SIZE_ALPHA_BYTE : sizeof(lv_color_t); - - /*Go to the first displayed pixel of the map*/ - int32_t map_w = lv_area_get_width(map_area); - const uint8_t * map_buf_tmp = map_p; - map_buf_tmp += map_w * (draw_area.y1 - (map_area->y1 - disp_area->y1)) * px_size_byte; - map_buf_tmp += (draw_area.x1 - (map_area->x1 - disp_area->x1)) * px_size_byte; - - lv_color_t c; - lv_color_t chroma_keyed_color = LV_COLOR_CHROMA_KEY; - uint32_t px_i = 0; - - const uint8_t * map_px; - - lv_coord_t draw_area_h = lv_area_get_height(&draw_area); - lv_coord_t draw_area_w = lv_area_get_width(&draw_area); - - lv_area_t blend_area; - blend_area.x1 = draw_area.x1 + disp_area->x1; - blend_area.x2 = blend_area.x1 + draw_area_w - 1; - blend_area.y1 = disp_area->y1 + draw_area.y1; - blend_area.y2 = blend_area.y1; - - bool transform = draw_dsc->angle != 0 || draw_dsc->zoom != LV_IMG_ZOOM_NONE ? true : false; - /*Simple ARGB image. Handle it as special case because it's very common*/ - if(!mask_any && !transform && !chroma_key && draw_dsc->recolor_opa == LV_OPA_TRANSP && alpha_byte) { -#if LV_USE_GPU_STM32_DMA2D && LV_COLOR_DEPTH == 32 - /*Blend ARGB images directly*/ - if(lv_area_get_size(&draw_area) > 240) { - int32_t disp_w = lv_area_get_width(disp_area); - lv_color_t * disp_buf = draw_buf->buf_act; - lv_color_t * disp_buf_first = disp_buf + disp_w * draw_area.y1 + draw_area.x1; - lv_gpu_stm32_dma2d_blend(disp_buf_first, disp_w, (const lv_color_t *)map_buf_tmp, draw_dsc->opa, map_w, draw_area_w, - draw_area_h); - return; - } -#endif - uint32_t hor_res = (uint32_t) lv_disp_get_hor_res(disp); - uint32_t mask_buf_size = lv_area_get_size(&draw_area) > (uint32_t) hor_res ? hor_res : lv_area_get_size(&draw_area); - lv_color_t * map2 = lv_mem_buf_get(mask_buf_size * sizeof(lv_color_t)); - lv_opa_t * mask_buf = lv_mem_buf_get(mask_buf_size); - - int32_t x; - int32_t y; - for(y = 0; y < draw_area_h; y++) { - map_px = map_buf_tmp; - for(x = 0; x < draw_area_w; x++, map_px += px_size_byte, px_i++) { - lv_opa_t px_opa = map_px[LV_IMG_PX_SIZE_ALPHA_BYTE - 1]; - mask_buf[px_i] = px_opa; - if(px_opa) { -#if LV_COLOR_DEPTH == 8 || LV_COLOR_DEPTH == 1 - map2[px_i].full = map_px[0]; -#elif LV_COLOR_DEPTH == 16 - map2[px_i].full = map_px[0] + (map_px[1] << 8); -#elif LV_COLOR_DEPTH == 32 - map2[px_i].full = *((uint32_t *)map_px); -#endif - } -#if LV_COLOR_DEPTH == 32 - map2[px_i].ch.alpha = 0xFF; -#endif - } - - map_buf_tmp += map_w * px_size_byte; - if(px_i + draw_area_w < mask_buf_size) { - blend_area.y2 ++; - } - else { - _lv_blend_map(clip_area, &blend_area, map2, mask_buf, LV_DRAW_MASK_RES_CHANGED, draw_dsc->opa, draw_dsc->blend_mode); - - blend_area.y1 = blend_area.y2 + 1; - blend_area.y2 = blend_area.y1; - - px_i = 0; - } - } - /*Flush the last part*/ - if(blend_area.y1 != blend_area.y2) { - blend_area.y2--; - _lv_blend_map(clip_area, &blend_area, map2, mask_buf, LV_DRAW_MASK_RES_CHANGED, draw_dsc->opa, draw_dsc->blend_mode); - } - - lv_mem_buf_release(mask_buf); - lv_mem_buf_release(map2); - } - /*Most complicated case: transform or other mask or chroma keyed*/ - else { - /*Build the image and a mask line-by-line*/ - uint32_t hor_res = (uint32_t) lv_disp_get_hor_res(disp); - uint32_t mask_buf_size = lv_area_get_size(&draw_area) > hor_res ? hor_res : lv_area_get_size(&draw_area); - lv_color_t * map2 = lv_mem_buf_get(mask_buf_size * sizeof(lv_color_t)); - lv_opa_t * mask_buf = lv_mem_buf_get(mask_buf_size); - -#if LV_DRAW_COMPLEX - lv_img_transform_dsc_t trans_dsc; - lv_memset_00(&trans_dsc, sizeof(lv_img_transform_dsc_t)); - if(transform) { - lv_img_cf_t cf = LV_IMG_CF_TRUE_COLOR; - if(alpha_byte) cf = LV_IMG_CF_TRUE_COLOR_ALPHA; - else if(chroma_key) cf = LV_IMG_CF_TRUE_COLOR_CHROMA_KEYED; - - trans_dsc.cfg.angle = draw_dsc->angle; - trans_dsc.cfg.zoom = draw_dsc->zoom; - trans_dsc.cfg.src = map_p; - trans_dsc.cfg.src_w = map_w; - trans_dsc.cfg.src_h = lv_area_get_height(map_area);; - trans_dsc.cfg.cf = cf; - trans_dsc.cfg.pivot_x = draw_dsc->pivot.x; - trans_dsc.cfg.pivot_y = draw_dsc->pivot.y; - trans_dsc.cfg.color = draw_dsc->recolor; - trans_dsc.cfg.antialias = draw_dsc->antialias; - - _lv_img_buf_transform_init(&trans_dsc); - } -#endif - uint16_t recolor_premult[3] = {0}; - lv_opa_t recolor_opa_inv = 255 - draw_dsc->recolor_opa; - if(draw_dsc->recolor_opa != 0) { - lv_color_premult(draw_dsc->recolor, draw_dsc->recolor_opa, recolor_premult); - } - - lv_draw_mask_res_t mask_res; - mask_res = (alpha_byte || chroma_key || draw_dsc->angle || - draw_dsc->zoom != LV_IMG_ZOOM_NONE) ? LV_DRAW_MASK_RES_CHANGED : LV_DRAW_MASK_RES_FULL_COVER; - - /*Prepare the `mask_buf`if there are other masks*/ - if(mask_any) { - lv_memset_ff(mask_buf, mask_buf_size); - } - - int32_t x; - int32_t y; -#if LV_DRAW_COMPLEX - int32_t rot_y = disp_area->y1 + draw_area.y1 - map_area->y1; -#endif - for(y = 0; y < draw_area_h; y++) { - map_px = map_buf_tmp; -#if LV_DRAW_COMPLEX - uint32_t px_i_start = px_i; - int32_t rot_x = disp_area->x1 + draw_area.x1 - map_area->x1; -#endif - - for(x = 0; x < draw_area_w; x++, map_px += px_size_byte, px_i++) { - -#if LV_DRAW_COMPLEX - if(transform) { - - /*Transform*/ - bool ret; - ret = _lv_img_buf_transform(&trans_dsc, rot_x + x, rot_y + y); - if(ret == false) { - mask_buf[px_i] = LV_OPA_TRANSP; - continue; - } - else { - mask_buf[px_i] = trans_dsc.res.opa; - c.full = trans_dsc.res.color.full; - } - } - /*No transform*/ - else -#endif - { - if(alpha_byte) { - lv_opa_t px_opa = map_px[LV_IMG_PX_SIZE_ALPHA_BYTE - 1]; - mask_buf[px_i] = px_opa; - if(px_opa == 0) { -#if LV_COLOR_DEPTH == 32 - map2[px_i].full = 0; -#endif - continue; - } - } - else { - mask_buf[px_i] = 0xFF; - } - -#if LV_COLOR_DEPTH == 1 - c.full = map_px[0]; -#elif LV_COLOR_DEPTH == 8 - c.full = map_px[0]; -#elif LV_COLOR_DEPTH == 16 - c.full = map_px[0] + (map_px[1] << 8); -#elif LV_COLOR_DEPTH == 32 - c.full = *((uint32_t *)map_px); - c.ch.alpha = 0xFF; -#endif - if(chroma_key) { - if(c.full == chroma_keyed_color.full) { - mask_buf[px_i] = LV_OPA_TRANSP; -#if LV_COLOR_DEPTH == 32 - map2[px_i].full = 0; -#endif - continue; - } - } - - } - if(draw_dsc->recolor_opa != 0) { - c = lv_color_mix_premult(recolor_premult, c, recolor_opa_inv); - } - - map2[px_i].full = c.full; - } -#if LV_DRAW_COMPLEX - /*Apply the masks if any*/ - if(mask_any) { - lv_draw_mask_res_t mask_res_sub; - mask_res_sub = lv_draw_mask_apply(mask_buf + px_i_start, draw_area.x1 + draw_buf->area.x1, - y + draw_area.y1 + draw_buf->area.y1, - draw_area_w); - if(mask_res_sub == LV_DRAW_MASK_RES_TRANSP) { - lv_memset_00(mask_buf + px_i_start, draw_area_w); - mask_res = LV_DRAW_MASK_RES_CHANGED; - } - else if(mask_res_sub == LV_DRAW_MASK_RES_CHANGED) { - mask_res = LV_DRAW_MASK_RES_CHANGED; - } - } -#endif - - map_buf_tmp += map_w * px_size_byte; - if(px_i + draw_area_w < mask_buf_size) { - blend_area.y2 ++; - } - else { - - _lv_blend_map(clip_area, &blend_area, map2, mask_buf, mask_res, draw_dsc->opa, draw_dsc->blend_mode); - - blend_area.y1 = blend_area.y2 + 1; - blend_area.y2 = blend_area.y1; - - px_i = 0; - mask_res = (alpha_byte || chroma_key || draw_dsc->angle || - draw_dsc->zoom != LV_IMG_ZOOM_NONE) ? LV_DRAW_MASK_RES_CHANGED : LV_DRAW_MASK_RES_FULL_COVER; - - /*Prepare the `mask_buf`if there are other masks*/ - if(mask_any) { - lv_memset_ff(mask_buf, mask_buf_size); - } - } - } - - /*Flush the last part*/ - if(blend_area.y1 != blend_area.y2) { - blend_area.y2--; - _lv_blend_map(clip_area, &blend_area, map2, mask_buf, mask_res, draw_dsc->opa, draw_dsc->blend_mode); - } - - lv_mem_buf_release(mask_buf); - lv_mem_buf_release(map2); - } - } -} static void show_error(const lv_area_t * coords, const lv_area_t * clip_area, const char * msg) { @@ -664,5 +338,3 @@ static void draw_cleanup(_lv_img_cache_entry_t * cache) LV_UNUSED(cache); #endif } - -#endif //LV_USE_GPU_SDL_RENDER \ No newline at end of file diff --git a/src/draw/lv_draw_img.h b/src/draw/lv_draw_img.h index 3608bc00e..883e2be7b 100644 --- a/src/draw/lv_draw_img.h +++ b/src/draw/lv_draw_img.h @@ -15,7 +15,7 @@ extern "C" { *********************/ #include "lv_img_decoder.h" #include "lv_img_buf.h" -#include "lv_draw_blend.h" +#include "../misc/lv_style.h" /********************* * DEFINES diff --git a/src/draw/lv_draw_label.c b/src/draw/lv_draw_label.c index e26cb0c6e..ba5308dca 100644 --- a/src/draw/lv_draw_label.c +++ b/src/draw/lv_draw_label.c @@ -6,6 +6,7 @@ /********************* * INCLUDES *********************/ +#include "lv_draw.h" #include "lv_draw_label.h" #include "../misc/lv_math.h" #include "../hal/lv_hal_disp.h" @@ -13,9 +14,6 @@ #include "../misc/lv_bidi.h" #include "../misc/lv_assert.h" -#if LV_USE_GPU_SDL - #include "../gpu/lv_gpu_sdl.h" -#endif /********************* * DEFINES *********************/ @@ -36,17 +34,6 @@ typedef uint8_t cmd_state_t; * STATIC PROTOTYPES **********************/ -#if LV_USE_EXTERNAL_RENDERER == 0 -LV_ATTRIBUTE_FAST_MEM static void draw_letter_normal(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_DRAW_COMPLEX && LV_USE_FONT_SUBPX -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); -#endif /*LV_DRAW_COMPLEX && LV_USE_FONT_SUBPX*/ -#endif /*LV_USE_EXTERNAL_RENDERER*/ - static uint8_t hex_char_to_num(char hex); /********************** @@ -57,37 +44,6 @@ static uint8_t hex_char_to_num(char hex); * GLOBAL VARIABLES **********************/ -const uint8_t _lv_bpp1_opa_table[2] = {0, 255}; /*Opacity mapping with bpp = 1 (Just for compatibility)*/ -const uint8_t _lv_bpp2_opa_table[4] = {0, 85, 170, 255}; /*Opacity mapping with bpp = 2*/ - -const uint8_t _lv_bpp3_opa_table[8] = {0, 36, 73, 109, /*Opacity mapping with bpp = 3*/ - 146, 182, 219, 255 - }; - -const uint8_t _lv_bpp4_opa_table[16] = {0, 17, 34, 51, /*Opacity mapping with bpp = 4*/ - 68, 85, 102, 119, - 136, 153, 170, 187, - 204, 221, 238, 255 - }; - -const uint8_t _lv_bpp8_opa_table[256] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, - 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, - 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, - 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, - 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, - 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, - 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, - 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, - 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, - 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, - 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, - 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, - 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, - 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, - 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, - 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255 - }; - /********************** * MACROS **********************/ @@ -96,7 +52,7 @@ const uint8_t _lv_bpp8_opa_table[256] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 1 * GLOBAL FUNCTIONS **********************/ -LV_ATTRIBUTE_FAST_MEM void lv_draw_label_dsc_init(lv_draw_label_dsc_t * dsc) +void lv_draw_label_dsc_init(lv_draw_label_dsc_t * dsc) { lv_memset_00(dsc, sizeof(lv_draw_label_dsc_t)); dsc->opa = LV_OPA_COVER; @@ -123,7 +79,6 @@ LV_ATTRIBUTE_FAST_MEM void lv_draw_label(const lv_area_t * coords, const lv_area const char * txt, lv_draw_label_hint_t * hint) { - if(dsc->opa <= LV_OPA_MIN) return; const lv_font_t * font = dsc->font; int32_t w; @@ -395,453 +350,20 @@ LV_ATTRIBUTE_FAST_MEM void lv_draw_label(const lv_area_t * coords, const lv_area LV_ASSERT_MEM_INTEGRITY(); } -#if LV_USE_EXTERNAL_RENDERER == 0 +void lv_draw_letter(const lv_point_t * pos_p, const lv_area_t * clip_area, + const lv_font_t * font_p, + uint32_t letter, + lv_color_t color, lv_opa_t opa, lv_blend_mode_t blend_mode) +{ + + const lv_draw_backend_t * backend = lv_draw_backend_get(); + backend->draw_letter(pos_p, clip_area, font_p, letter, color, opa, blend_mode); +} + /********************** * STATIC FUNCTIONS **********************/ -/** - * Draw a letter in the Virtual Display Buffer - * @param pos_p left-top coordinate of the latter - * @param mask_p the letter will be drawn only on this area (truncated to draw_buf area) - * @param font_p pointer to font - * @param letter a letter to draw - * @param color color of letter - * @param opa opacity of letter (0..255) - */ -LV_ATTRIBUTE_FAST_MEM void lv_draw_letter(const lv_point_t * pos_p, const lv_area_t * clip_area, - const lv_font_t * font_p, - uint32_t letter, - lv_color_t color, lv_opa_t opa, lv_blend_mode_t blend_mode) -{ - if(opa < LV_OPA_MIN) return; - if(opa > LV_OPA_MAX) opa = LV_OPA_COVER; - - if(font_p == NULL) { - LV_LOG_WARN("lv_draw_letter: font is NULL"); - return; - } - - lv_font_glyph_dsc_t g; - bool g_ret = lv_font_get_glyph_dsc(font_p, &g, letter, '\0'); - if(g_ret == false) { - /*Add warning if the dsc is not found - *but do not print warning for non printable ASCII chars (e.g. '\n')*/ - if(letter >= 0x20 && - letter != 0xf8ff && /*LV_SYMBOL_DUMMY*/ - letter != 0x200c) { /*ZERO WIDTH NON-JOINER*/ - LV_LOG_WARN("lv_draw_letter: glyph dsc. not found for U+%X", (unsigned int)letter); - } - return; - } - - /*Don't draw anything if the character is empty. E.g. space*/ - if((g.box_h == 0) || (g.box_w == 0)) return; - - int32_t pos_x = pos_p->x + g.ofs_x; - int32_t pos_y = pos_p->y + (font_p->line_height - font_p->base_line) - g.box_h - g.ofs_y; - - /*If the letter is completely out of mask don't draw it*/ - if(pos_x + g.box_w < clip_area->x1 || - pos_x > clip_area->x2 || - pos_y + g.box_h < clip_area->y1 || - pos_y > clip_area->y2) { - return; - } - - if(g.resolved_font) { - font_p = g.resolved_font; - } - const uint8_t * map_p = lv_font_get_glyph_bitmap(font_p, letter); - if(map_p == NULL) { - LV_LOG_WARN("lv_draw_letter: character's bitmap not found"); - return; - } - - if(font_p->subpx) { -#if LV_DRAW_COMPLEX && LV_USE_FONT_SUBPX - draw_letter_subpx(pos_x, pos_y, &g, clip_area, map_p, color, opa, blend_mode); -#else - LV_LOG_WARN("Can't draw sub-pixel rendered letter because LV_USE_FONT_SUBPX == 0 in lv_conf.h"); -#endif - } - else { - draw_letter_normal(pos_x, pos_y, &g, clip_area, map_p, color, opa, blend_mode); - } -} - -LV_ATTRIBUTE_FAST_MEM static void draw_letter_normal(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) -{ - const uint8_t * bpp_opa_table_p; - uint32_t bitmask_init; - uint32_t bitmask; - uint32_t bpp = g->bpp; - uint32_t shades; - if(bpp == 3) bpp = 4; - - switch(bpp) { - case 1: - bpp_opa_table_p = _lv_bpp1_opa_table; - bitmask_init = 0x80; - shades = 2; - break; - case 2: - bpp_opa_table_p = _lv_bpp2_opa_table; - bitmask_init = 0xC0; - shades = 4; - break; - case 4: - bpp_opa_table_p = _lv_bpp4_opa_table; - bitmask_init = 0xF0; - shades = 16; - break; - case 8: - bpp_opa_table_p = _lv_bpp8_opa_table; - bitmask_init = 0xFF; - shades = 256; - break; /*No opa table, pixel value will be used directly*/ - default: - LV_LOG_WARN("lv_draw_letter: invalid bpp"); - return; /*Invalid bpp. Can't render the letter*/ - } - - static lv_opa_t opa_table[256]; - static lv_opa_t prev_opa = LV_OPA_TRANSP; - static uint32_t prev_bpp = 0; - if(opa < LV_OPA_MAX) { - if(prev_opa != opa || prev_bpp != bpp) { - uint32_t i; - for(i = 0; i < shades; i++) { - opa_table[i] = bpp_opa_table_p[i] == LV_OPA_COVER ? opa : ((bpp_opa_table_p[i] * opa) >> 8); - } - } - bpp_opa_table_p = opa_table; - prev_opa = opa; - prev_bpp = bpp; - } - - int32_t col, row; - int32_t box_w = g->box_w; - int32_t box_h = g->box_h; - int32_t width_bit = box_w * bpp; /*Letter width in bits*/ - - /*Calculate the col/row start/end on the map*/ - int32_t col_start = pos_x >= clip_area->x1 ? 0 : clip_area->x1 - pos_x; - int32_t col_end = pos_x + box_w <= clip_area->x2 ? box_w : clip_area->x2 - pos_x + 1; - int32_t row_start = pos_y >= clip_area->y1 ? 0 : clip_area->y1 - pos_y; - int32_t row_end = pos_y + box_h <= clip_area->y2 ? box_h : clip_area->y2 - pos_y + 1; - - /*Move on the map too*/ - uint32_t bit_ofs = (row_start * width_bit) + (col_start * bpp); - map_p += bit_ofs >> 3; - - uint8_t letter_px; - uint32_t col_bit; - col_bit = bit_ofs & 0x7; /*"& 0x7" equals to "% 8" just faster*/ - - lv_coord_t hor_res = lv_disp_get_hor_res(_lv_refr_get_disp_refreshing()); - uint32_t mask_buf_size = box_w * box_h > hor_res ? hor_res : box_w * box_h; - lv_opa_t * mask_buf = lv_mem_buf_get(mask_buf_size); - int32_t mask_p = 0; - - lv_area_t fill_area; - fill_area.x1 = col_start + pos_x; - fill_area.x2 = col_end + pos_x - 1; - fill_area.y1 = row_start + pos_y; - fill_area.y2 = fill_area.y1; -#if LV_DRAW_COMPLEX - lv_area_t mask_area; - lv_area_copy(&mask_area, &fill_area); - mask_area.y2 = mask_area.y1 + row_end; - bool mask_any = lv_draw_mask_is_any(&mask_area); -#endif - - uint32_t col_bit_max = 8 - bpp; - uint32_t col_bit_row_ofs = (box_w + col_start - col_end) * bpp; - - for(row = row_start ; row < row_end; row++) { -#if LV_DRAW_COMPLEX - int32_t mask_p_start = mask_p; -#endif - bitmask = bitmask_init >> col_bit; - for(col = col_start; col < col_end; col++) { - /*Load the pixel's opacity into the mask*/ - letter_px = (*map_p & bitmask) >> (col_bit_max - col_bit); - if(letter_px) { - mask_buf[mask_p] = bpp_opa_table_p[letter_px]; - } - else { - mask_buf[mask_p] = 0; - } - - /*Go to the next column*/ - if(col_bit < col_bit_max) { - col_bit += bpp; - bitmask = bitmask >> bpp; - } - else { - col_bit = 0; - bitmask = bitmask_init; - map_p++; - } - - /*Next mask byte*/ - mask_p++; - } - -#if LV_DRAW_COMPLEX - /*Apply masks if any*/ - if(mask_any) { - lv_draw_mask_res_t mask_res = lv_draw_mask_apply(mask_buf + mask_p_start, fill_area.x1, fill_area.y2, - lv_area_get_width(&fill_area)); - if(mask_res == LV_DRAW_MASK_RES_TRANSP) { - lv_memset_00(mask_buf + mask_p_start, lv_area_get_width(&fill_area)); - } - } -#endif - - if((uint32_t) mask_p + (col_end - col_start) < mask_buf_size) { - fill_area.y2 ++; - } - else { - _lv_blend_fill(clip_area, &fill_area, - color, mask_buf, LV_DRAW_MASK_RES_CHANGED, LV_OPA_COVER, - blend_mode); - - fill_area.y1 = fill_area.y2 + 1; - fill_area.y2 = fill_area.y1; - mask_p = 0; - } - - col_bit += col_bit_row_ofs; - map_p += (col_bit >> 3); - col_bit = col_bit & 0x7; - } - - /*Flush the last part*/ - if(fill_area.y1 != fill_area.y2) { - fill_area.y2--; - _lv_blend_fill(clip_area, &fill_area, - color, mask_buf, LV_DRAW_MASK_RES_CHANGED, LV_OPA_COVER, - blend_mode); - mask_p = 0; - } - - lv_mem_buf_release(mask_buf); -} - -#if LV_DRAW_COMPLEX && LV_USE_FONT_SUBPX -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) -{ - const uint8_t * bpp_opa_table; - uint32_t bitmask_init; - uint32_t bitmask; - uint32_t bpp = g->bpp; - if(bpp == 3) bpp = 4; - - switch(bpp) { - case 1: - bpp_opa_table = _lv_bpp1_opa_table; - bitmask_init = 0x80; - break; - case 2: - bpp_opa_table = _lv_bpp2_opa_table; - bitmask_init = 0xC0; - break; - case 4: - bpp_opa_table = _lv_bpp4_opa_table; - bitmask_init = 0xF0; - break; - case 8: - bpp_opa_table = _lv_bpp8_opa_table; - bitmask_init = 0xFF; - break; /*No opa table, pixel value will be used directly*/ - default: - LV_LOG_WARN("lv_draw_letter: invalid bpp not found"); - return; /*Invalid bpp. Can't render the letter*/ - } - - int32_t col, row; - - int32_t box_w = g->box_w; - int32_t box_h = g->box_h; - int32_t width_bit = box_w * bpp; /*Letter width in bits*/ - - /*Calculate the col/row start/end on the map*/ - int32_t col_start = pos_x >= clip_area->x1 ? 0 : (clip_area->x1 - pos_x) * 3; - int32_t col_end = pos_x + box_w / 3 <= clip_area->x2 ? box_w : (clip_area->x2 - pos_x + 1) * 3; - int32_t row_start = pos_y >= clip_area->y1 ? 0 : clip_area->y1 - pos_y; - int32_t row_end = pos_y + box_h <= clip_area->y2 ? box_h : clip_area->y2 - pos_y + 1; - - /*Move on the map too*/ - int32_t bit_ofs = (row_start * width_bit) + (col_start * bpp); - map_p += bit_ofs >> 3; - - uint8_t letter_px; - lv_opa_t px_opa; - int32_t col_bit; - col_bit = bit_ofs & 0x7; /*"& 0x7" equals to "% 8" just faster*/ - - lv_area_t map_area; - map_area.x1 = col_start / 3 + pos_x; - map_area.x2 = col_end / 3 + pos_x - 1; - map_area.y1 = row_start + pos_y; - map_area.y2 = map_area.y1; - - if(map_area.x2 <= map_area.x1) return; - - int32_t mask_buf_size = box_w * box_h > _LV_MASK_BUF_MAX_SIZE ? _LV_MASK_BUF_MAX_SIZE : g->box_w * g->box_h; - lv_opa_t * mask_buf = lv_mem_buf_get(mask_buf_size); - int32_t mask_p = 0; - - lv_color_t * color_buf = lv_mem_buf_get(mask_buf_size * sizeof(lv_color_t)); - - lv_disp_t * disp = _lv_refr_get_disp_refreshing(); - lv_disp_draw_buf_t * draw_buf = lv_disp_get_draw_buf(disp); - - int32_t disp_buf_width = lv_area_get_width(&draw_buf->area); - lv_color_t * disp_buf_buf_tmp = draw_buf->buf_act; - - /*Set a pointer on draw_buf to the first pixel of the letter*/ - disp_buf_buf_tmp += ((pos_y - draw_buf->area.y1) * disp_buf_width) + pos_x - draw_buf->area.x1; - - /*If the letter is partially out of mask the move there on draw_buf*/ - disp_buf_buf_tmp += (row_start * disp_buf_width) + col_start / 3; - - lv_area_t mask_area; - lv_area_copy(&mask_area, &map_area); - mask_area.y2 = mask_area.y1 + row_end; - bool mask_any = lv_draw_mask_is_any(&map_area); - uint8_t font_rgb[3]; - -#if LV_COLOR_16_SWAP == 0 - uint8_t txt_rgb[3] = {color.ch.red, color.ch.green, color.ch.blue}; -#else - uint8_t txt_rgb[3] = {color.ch.red, (color.ch.green_h << 3) + color.ch.green_l, color.ch.blue}; -#endif - - for(row = row_start ; row < row_end; row++) { - uint32_t subpx_cnt = 0; - bitmask = bitmask_init >> col_bit; - int32_t mask_p_start = mask_p; - - for(col = col_start; col < col_end; col++) { - /*Load the pixel's opacity into the mask*/ - letter_px = (*map_p & bitmask) >> (8 - col_bit - bpp); - if(letter_px != 0) { - if(opa == LV_OPA_COVER) { - px_opa = bpp == 8 ? letter_px : bpp_opa_table[letter_px]; - } - else { - px_opa = bpp == 8 ? (uint32_t)((uint32_t)letter_px * opa) >> 8 - : (uint32_t)((uint32_t)bpp_opa_table[letter_px] * opa) >> 8; - } - } - else { - px_opa = 0; - } - - font_rgb[subpx_cnt] = px_opa; - - subpx_cnt ++; - if(subpx_cnt == 3) { - subpx_cnt = 0; - - lv_color_t res_color; -#if LV_COLOR_16_SWAP == 0 - uint8_t bg_rgb[3] = {disp_buf_buf_tmp->ch.red, disp_buf_buf_tmp->ch.green, disp_buf_buf_tmp->ch.blue}; -#else - uint8_t bg_rgb[3] = {disp_buf_buf_tmp->ch.red, - (disp_buf_buf_tmp->ch.green_h << 3) + disp_buf_buf_tmp->ch.green_l, - disp_buf_buf_tmp->ch.blue - }; -#endif - -#if LV_FONT_SUBPX_BGR - res_color.ch.blue = (uint32_t)((uint32_t)txt_rgb[0] * font_rgb[0] + (bg_rgb[0] * (255 - font_rgb[0]))) >> 8; - res_color.ch.red = (uint32_t)((uint32_t)txt_rgb[2] * font_rgb[2] + (bg_rgb[2] * (255 - font_rgb[2]))) >> 8; -#else - res_color.ch.red = (uint32_t)((uint16_t)txt_rgb[0] * font_rgb[0] + (bg_rgb[0] * (255 - font_rgb[0]))) >> 8; - res_color.ch.blue = (uint32_t)((uint16_t)txt_rgb[2] * font_rgb[2] + (bg_rgb[2] * (255 - font_rgb[2]))) >> 8; -#endif - -#if LV_COLOR_16_SWAP == 0 - res_color.ch.green = (uint32_t)((uint32_t)txt_rgb[1] * font_rgb[1] + (bg_rgb[1] * (255 - font_rgb[1]))) >> 8; -#else - uint8_t green = (uint32_t)((uint32_t)txt_rgb[1] * font_rgb[1] + (bg_rgb[1] * (255 - font_rgb[1]))) >> 8; - res_color.ch.green_h = green >> 3; - res_color.ch.green_l = green & 0x7; -#endif - -#if LV_COLOR_DEPTH == 32 - res_color.ch.alpha = 0xff; -#endif - - if(font_rgb[0] == 0 && font_rgb[1] == 0 && font_rgb[2] == 0) mask_buf[mask_p] = LV_OPA_TRANSP; - else mask_buf[mask_p] = LV_OPA_COVER; - color_buf[mask_p] = res_color; - - /*Next mask byte*/ - mask_p++; - disp_buf_buf_tmp++; - } - - /*Go to the next column*/ - if(col_bit < (int32_t)(8 - bpp)) { - col_bit += bpp; - bitmask = bitmask >> bpp; - } - else { - col_bit = 0; - bitmask = bitmask_init; - map_p++; - } - } - - /*Apply masks if any*/ - if(mask_any) { - lv_draw_mask_res_t mask_res = lv_draw_mask_apply(mask_buf + mask_p_start, map_area.x1, map_area.y2, - lv_area_get_width(&map_area)); - if(mask_res == LV_DRAW_MASK_RES_TRANSP) { - lv_memset_00(mask_buf + mask_p_start, lv_area_get_width(&map_area)); - } - } - - if((int32_t) mask_p + (col_end - col_start) < mask_buf_size) { - map_area.y2 ++; - } - else { - _lv_blend_map(clip_area, &map_area, color_buf, mask_buf, LV_DRAW_MASK_RES_CHANGED, opa, blend_mode); - - map_area.y1 = map_area.y2 + 1; - map_area.y2 = map_area.y1; - mask_p = 0; - } - - col_bit += ((box_w - col_end) + col_start) * bpp; - - map_p += (col_bit >> 3); - col_bit = col_bit & 0x7; - - /*Next row in draw_buf*/ - disp_buf_buf_tmp += disp_buf_width - (col_end - col_start) / 3; - } - - /*Flush the last part*/ - if(map_area.y1 != map_area.y2) { - map_area.y2--; - _lv_blend_map(clip_area, &map_area, color_buf, mask_buf, LV_DRAW_MASK_RES_CHANGED, opa, blend_mode); - } - - lv_mem_buf_release(mask_buf); - lv_mem_buf_release(color_buf); -} -#endif /*LV_DRAW_COMPLEX && LV_USE_FONT_SUBPX*/ - -#endif /*LV_USE_EXTERNAL_RENDERER*/ /** * Convert a hexadecimal characters to a number (0..15) * @param hex Pointer to a hexadecimal character (0..9, A..F) diff --git a/src/draw/lv_draw_label.h b/src/draw/lv_draw_label.h index f4cc1fb88..6556d22ed 100644 --- a/src/draw/lv_draw_label.h +++ b/src/draw/lv_draw_label.h @@ -13,10 +13,10 @@ extern "C" { /********************* * INCLUDES *********************/ -#include "lv_draw_blend.h" #include "../misc/lv_bidi.h" #include "../misc/lv_txt.h" #include "../misc/lv_color.h" +#include "../misc/lv_style.h" /********************* * DEFINES @@ -67,8 +67,6 @@ typedef struct _lv_draw_label_hint_t { * GLOBAL PROTOTYPES **********************/ -//! @cond Doxygen_Suppress - LV_ATTRIBUTE_FAST_MEM void lv_draw_label_dsc_init(lv_draw_label_dsc_t * dsc); /** @@ -84,18 +82,13 @@ LV_ATTRIBUTE_FAST_MEM void lv_draw_label(const lv_area_t * coords, const lv_area const lv_draw_label_dsc_t * dsc, const char * txt, lv_draw_label_hint_t * hint); -LV_ATTRIBUTE_FAST_MEM void lv_draw_letter(const lv_point_t * pos_p, const lv_area_t * clip_area, - const lv_font_t * font_p, - uint32_t letter, lv_color_t color, lv_opa_t opa, lv_blend_mode_t blend_mode); -//! @endcond +void lv_draw_letter(const lv_point_t * pos_p, const lv_area_t * clip_area, + const lv_font_t * font_p, + uint32_t letter, + lv_color_t color, lv_opa_t opa, lv_blend_mode_t blend_mode); /*********************** * GLOBAL VARIABLES ***********************/ -extern const uint8_t _lv_bpp2_opa_table[]; -extern const uint8_t _lv_bpp3_opa_table[]; -extern const uint8_t _lv_bpp1_opa_table[]; -extern const uint8_t _lv_bpp4_opa_table[]; -extern const uint8_t _lv_bpp8_opa_table[]; /********************** * MACROS diff --git a/src/draw/lv_draw_line.c b/src/draw/lv_draw_line.c index 407a280a7..f0ace451b 100644 --- a/src/draw/lv_draw_line.c +++ b/src/draw/lv_draw_line.c @@ -7,8 +7,6 @@ * INCLUDES *********************/ #include -#include "lv_draw_mask.h" -#include "lv_draw_blend.h" #include "../core/lv_refr.h" #include "../misc/lv_math.h" @@ -24,16 +22,6 @@ * STATIC PROTOTYPES **********************/ -LV_ATTRIBUTE_FAST_MEM static void draw_line_skew(const lv_point_t * point1, const lv_point_t * point2, - const lv_area_t * clip, - const lv_draw_line_dsc_t * dsc); - -LV_ATTRIBUTE_FAST_MEM static void draw_line_hor(const lv_point_t * point1, const lv_point_t * point2, - const lv_area_t * clip, - const lv_draw_line_dsc_t * dsc); -LV_ATTRIBUTE_FAST_MEM static void draw_line_ver(const lv_point_t * point1, const lv_point_t * point2, - const lv_area_t * clip, - const lv_draw_line_dsc_t * dsc); /********************** * STATIC VARIABLES **********************/ @@ -54,442 +42,16 @@ LV_ATTRIBUTE_FAST_MEM void lv_draw_line_dsc_init(lv_draw_line_dsc_t * dsc) dsc->color = lv_color_black(); } -/** - * Draw a line - * @param point1 first point of the line - * @param point2 second point of the line - * @param clip the line will be drawn only in this area - * @param dsc pointer to an initialized `lv_draw_line_dsc_t` variable - */ LV_ATTRIBUTE_FAST_MEM void lv_draw_line(const lv_point_t * point1, const lv_point_t * point2, const lv_area_t * clip, const lv_draw_line_dsc_t * dsc) { if(dsc->width == 0) return; if(dsc->opa <= LV_OPA_MIN) return; - if(point1->x == point2->x && point1->y == point2->y) return; - - lv_area_t clip_line; - clip_line.x1 = LV_MIN(point1->x, point2->x) - dsc->width / 2; - clip_line.x2 = LV_MAX(point1->x, point2->x) + dsc->width / 2; - clip_line.y1 = LV_MIN(point1->y, point2->y) - dsc->width / 2; - clip_line.y2 = LV_MAX(point1->y, point2->y) + dsc->width / 2; - - bool is_common; - is_common = _lv_area_intersect(&clip_line, &clip_line, clip); - if(!is_common) return; - - if(point1->y == point2->y) draw_line_hor(point1, point2, &clip_line, dsc); - else if(point1->x == point2->x) draw_line_ver(point1, point2, &clip_line, dsc); - else draw_line_skew(point1, point2, &clip_line, dsc); - - if(dsc->round_end || dsc->round_start) { - lv_draw_rect_dsc_t cir_dsc; - lv_draw_rect_dsc_init(&cir_dsc); - cir_dsc.bg_color = dsc->color; - cir_dsc.radius = LV_RADIUS_CIRCLE; - cir_dsc.bg_opa = dsc->opa; - - int32_t r = (dsc->width >> 1); - int32_t r_corr = (dsc->width & 1) ? 0 : 1; - lv_area_t cir_area; - - if(dsc->round_start) { - cir_area.x1 = point1->x - r; - cir_area.y1 = point1->y - r; - cir_area.x2 = point1->x + r - r_corr; - cir_area.y2 = point1->y + r - r_corr ; - lv_draw_rect(&cir_area, clip, &cir_dsc); - } - - if(dsc->round_end) { - cir_area.x1 = point2->x - r; - cir_area.y1 = point2->y - r; - cir_area.x2 = point2->x + r - r_corr; - cir_area.y2 = point2->y + r - r_corr ; - lv_draw_rect(&cir_area, clip, &cir_dsc); - } - } + const lv_draw_backend_t * backend = lv_draw_backend_get(); + backend->draw_line(point1, point2, clip, dsc); } /********************** * STATIC FUNCTIONS **********************/ - - -LV_ATTRIBUTE_FAST_MEM static void draw_line_hor(const lv_point_t * point1, const lv_point_t * point2, - const lv_area_t * clip, - const lv_draw_line_dsc_t * dsc) -{ - lv_opa_t opa = dsc->opa; - - int32_t w = dsc->width - 1; - int32_t w_half0 = w >> 1; - int32_t w_half1 = w_half0 + (w & 0x1); /*Compensate rounding error*/ - - - - lv_area_t draw_area; - draw_area.x1 = LV_MIN(point1->x, point2->x); - draw_area.x2 = LV_MAX(point1->x, point2->x) - 1; - draw_area.y1 = point1->y - w_half1; - draw_area.y2 = point1->y + w_half0; - - bool dashed = dsc->dash_gap && dsc->dash_width ? true : false; - bool simple_mode = true; - if(lv_draw_mask_is_any(&draw_area)) simple_mode = false; - else if(dashed) simple_mode = false; - - /*If there is no mask then simply draw a rectangle*/ - if(simple_mode) { - _lv_blend_fill(clip, &draw_area, - dsc->color, NULL, LV_DRAW_MASK_RES_FULL_COVER, opa, - dsc->blend_mode); - } -#if LV_DRAW_COMPLEX - /*If there other mask apply it*/ - else { - lv_disp_t * disp = _lv_refr_get_disp_refreshing(); - lv_disp_draw_buf_t * draw_buf = lv_disp_get_draw_buf(disp); - const lv_area_t * disp_area = &draw_buf->area; - /*Get clipped fill area which is the real draw area. - *It is always the same or inside `fill_area`*/ - bool is_common; - is_common = _lv_area_intersect(&draw_area, clip, &draw_area); - if(!is_common) return; - - /*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); - - lv_area_t fill_area; - fill_area.x1 = draw_area.x1 + disp_area->x1; - fill_area.x2 = draw_area.x2 + disp_area->x1; - fill_area.y1 = draw_area.y1 + disp_area->y1; - fill_area.y2 = fill_area.y1; - - lv_coord_t dash_start = 0; - if(dashed) { - dash_start = (draw_buf->area.x1 + draw_area.x1) % (dsc->dash_gap + dsc->dash_width); - } - - lv_opa_t * mask_buf = lv_mem_buf_get(draw_area_w); - int32_t h; - for(h = draw_area.y1; h <= draw_area.y2; h++) { - lv_memset_ff(mask_buf, draw_area_w); - lv_draw_mask_res_t mask_res = lv_draw_mask_apply(mask_buf, draw_buf->area.x1 + draw_area.x1, draw_buf->area.y1 + h, - draw_area_w); - - if(dashed) { - if(mask_res != LV_DRAW_MASK_RES_TRANSP) { - lv_coord_t dash_cnt = dash_start; - lv_coord_t i; - for(i = 0; i < draw_area_w; i++, dash_cnt++) { - if(dash_cnt <= dsc->dash_width) { - int16_t diff = dsc->dash_width - dash_cnt; - i += diff; - dash_cnt += diff; - } - else if(dash_cnt >= dsc->dash_gap + dsc->dash_width) { - dash_cnt = 0; - } - else { - mask_buf[i] = 0x00; - } - } - - mask_res = LV_DRAW_MASK_RES_CHANGED; - } - } - - _lv_blend_fill(clip, &fill_area, - dsc->color, mask_buf, mask_res, dsc->opa, - dsc->blend_mode); - - fill_area.y1++; - fill_area.y2++; - } - lv_mem_buf_release(mask_buf); - } -#endif /*LV_DRAW_COMPLEX*/ -} - -LV_ATTRIBUTE_FAST_MEM static void draw_line_ver(const lv_point_t * point1, const lv_point_t * point2, - const lv_area_t * clip, - const lv_draw_line_dsc_t * dsc) -{ - lv_opa_t opa = dsc->opa; - - int32_t w = dsc->width - 1; - int32_t w_half0 = w >> 1; - int32_t w_half1 = w_half0 + (w & 0x1); /*Compensate rounding error*/ - - lv_area_t draw_area; - draw_area.x1 = point1->x - w_half1; - draw_area.x2 = point1->x + w_half0; - draw_area.y1 = LV_MIN(point1->y, point2->y); - draw_area.y2 = LV_MAX(point1->y, point2->y) - 1; - - bool dashed = dsc->dash_gap && dsc->dash_width ? true : false; - bool simple_mode = true; - if(lv_draw_mask_is_any(&draw_area)) simple_mode = false; - else if(dashed) simple_mode = false; - - /*If there is no mask then simply draw a rectangle*/ - if(simple_mode) { - _lv_blend_fill(clip, &draw_area, - dsc->color, NULL, LV_DRAW_MASK_RES_FULL_COVER, opa, - dsc->blend_mode); - } - -#if LV_DRAW_COMPLEX - /*If there other mask apply it*/ - else { - lv_disp_t * disp = _lv_refr_get_disp_refreshing(); - lv_disp_draw_buf_t * draw_buf = lv_disp_get_draw_buf(disp); - const lv_area_t * disp_area = &draw_buf->area; - /*Get clipped fill area which is the real draw area. - *It is always the same or inside `fill_area`*/ - bool is_common; - is_common = _lv_area_intersect(&draw_area, clip, &draw_area); - if(!is_common) return; - - /*Now `draw_area` has absolute coordinates. - *Make it relative to `disp_area` to simplify draw to `disp_buf`*/ - draw_area.x1 -= draw_buf->area.x1; - draw_area.y1 -= draw_buf->area.y1; - draw_area.x2 -= draw_buf->area.x1; - draw_area.y2 -= draw_buf->area.y1; - - int32_t draw_area_w = lv_area_get_width(&draw_area); - - lv_area_t fill_area; - fill_area.x1 = draw_area.x1 + disp_area->x1; - fill_area.x2 = draw_area.x2 + disp_area->x1; - fill_area.y1 = draw_area.y1 + disp_area->y1; - fill_area.y2 = fill_area.y1; - - lv_opa_t * mask_buf = lv_mem_buf_get(draw_area_w); - - lv_coord_t dash_start = 0; - if(dashed) { - dash_start = (draw_buf->area.y1 + draw_area.y1) % (dsc->dash_gap + dsc->dash_width); - } - - lv_coord_t dash_cnt = dash_start; - - int32_t h; - for(h = draw_area.y1; h <= draw_area.y2; h++) { - lv_memset_ff(mask_buf, draw_area_w); - lv_draw_mask_res_t mask_res = lv_draw_mask_apply(mask_buf, draw_buf->area.x1 + draw_area.x1, draw_buf->area.y1 + h, - draw_area_w); - - if(dashed) { - if(mask_res != LV_DRAW_MASK_RES_TRANSP) { - if(dash_cnt > dsc->dash_width) { - mask_res = LV_DRAW_MASK_RES_TRANSP; - } - - if(dash_cnt >= dsc->dash_gap + dsc->dash_width) { - dash_cnt = 0; - } - } - dash_cnt ++; - } - - _lv_blend_fill(clip, &fill_area, - dsc->color, mask_buf, mask_res, dsc->opa, - LV_BLEND_MODE_NORMAL); - - fill_area.y1++; - fill_area.y2++; - } - lv_mem_buf_release(mask_buf); - } -#endif /*LV_DRAW_COMPLEX*/ -} - -LV_ATTRIBUTE_FAST_MEM static void draw_line_skew(const lv_point_t * point1, const lv_point_t * point2, - const lv_area_t * clip, - const lv_draw_line_dsc_t * dsc) -{ -#if LV_DRAW_COMPLEX - /*Keep the great y in p1*/ - lv_point_t p1; - lv_point_t p2; - if(point1->y < point2->y) { - p1.y = point1->y; - p2.y = point2->y; - p1.x = point1->x; - p2.x = point2->x; - } - else { - p1.y = point2->y; - p2.y = point1->y; - p1.x = point2->x; - p2.x = point1->x; - } - - int32_t xdiff = p2.x - p1.x; - int32_t ydiff = p2.y - p1.y; - bool flat = LV_ABS(xdiff) > LV_ABS(ydiff) ? true : false; - - static const uint8_t wcorr[] = { - 128, 128, 128, 129, 129, 130, 130, 131, - 132, 133, 134, 135, 137, 138, 140, 141, - 143, 145, 147, 149, 151, 153, 155, 158, - 160, 162, 165, 167, 170, 173, 175, 178, - 181, - }; - - int32_t w = dsc->width; - int32_t wcorr_i = 0; - if(flat) wcorr_i = (LV_ABS(ydiff) << 5) / LV_ABS(xdiff); - else wcorr_i = (LV_ABS(xdiff) << 5) / LV_ABS(ydiff); - - w = (w * wcorr[wcorr_i] + 63) >> 7; /*+ 63 for rounding*/ - int32_t w_half0 = w >> 1; - int32_t w_half1 = w_half0 + (w & 0x1); /*Compensate rounding error*/ - - lv_area_t draw_area; - draw_area.x1 = LV_MIN(p1.x, p2.x) - w; - draw_area.x2 = LV_MAX(p1.x, p2.x) + w; - draw_area.y1 = LV_MIN(p1.y, p2.y) - w; - draw_area.y2 = LV_MAX(p1.y, p2.y) + w; - - /*Get the union of `coords` and `clip`*/ - /*`clip` is already truncated to the `draw_buf` size - *in 'lv_refr_area' function*/ - bool is_common = _lv_area_intersect(&draw_area, &draw_area, clip); - if(is_common == false) return; - - lv_draw_mask_line_param_t mask_left_param; - lv_draw_mask_line_param_t mask_right_param; - lv_draw_mask_line_param_t mask_top_param; - lv_draw_mask_line_param_t mask_bottom_param; - - if(flat) { - if(xdiff > 0) { - lv_draw_mask_line_points_init(&mask_left_param, p1.x, p1.y - w_half0, p2.x, p2.y - w_half0, - LV_DRAW_MASK_LINE_SIDE_LEFT); - lv_draw_mask_line_points_init(&mask_right_param, p1.x, p1.y + w_half1, p2.x, p2.y + w_half1, - LV_DRAW_MASK_LINE_SIDE_RIGHT); - } - else { - lv_draw_mask_line_points_init(&mask_left_param, p1.x, p1.y + w_half1, p2.x, p2.y + w_half1, - LV_DRAW_MASK_LINE_SIDE_LEFT); - lv_draw_mask_line_points_init(&mask_right_param, p1.x, p1.y - w_half0, p2.x, p2.y - w_half0, - LV_DRAW_MASK_LINE_SIDE_RIGHT); - } - } - else { - lv_draw_mask_line_points_init(&mask_left_param, p1.x + w_half1, p1.y, p2.x + w_half1, p2.y, - LV_DRAW_MASK_LINE_SIDE_LEFT); - lv_draw_mask_line_points_init(&mask_right_param, p1.x - w_half0, p1.y, p2.x - w_half0, p2.y, - LV_DRAW_MASK_LINE_SIDE_RIGHT); - } - - /*Use the normal vector for the endings*/ - - int16_t mask_left_id = lv_draw_mask_add(&mask_left_param, NULL); - int16_t mask_right_id = lv_draw_mask_add(&mask_right_param, NULL); - int16_t mask_top_id = LV_MASK_ID_INV; - int16_t mask_bottom_id = LV_MASK_ID_INV; - - if(!dsc->raw_end) { - lv_draw_mask_line_points_init(&mask_top_param, p1.x, p1.y, p1.x - ydiff, p1.y + xdiff, LV_DRAW_MASK_LINE_SIDE_BOTTOM); - lv_draw_mask_line_points_init(&mask_bottom_param, p2.x, p2.y, p2.x - ydiff, p2.y + xdiff, LV_DRAW_MASK_LINE_SIDE_TOP); - mask_top_id = lv_draw_mask_add(&mask_top_param, NULL); - mask_bottom_id = lv_draw_mask_add(&mask_bottom_param, NULL); - } - - lv_disp_t * disp = _lv_refr_get_disp_refreshing(); - lv_disp_draw_buf_t * draw_buf = lv_disp_get_draw_buf(disp); - - const lv_area_t * disp_area = &draw_buf->area; - - /*Store the coordinates of the `draw_a` relative to the draw_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; - - /*The real draw area is around the line. - *It's easy to calculate with steep lines, but the area can be very wide with very flat lines. - *So deal with it only with steep lines.*/ - int32_t draw_area_w = lv_area_get_width(&draw_area); - - /*Draw the background line by line*/ - int32_t h; - uint32_t hor_res = (uint32_t)lv_disp_get_hor_res(disp); - size_t mask_buf_size = LV_MIN(lv_area_get_size(&draw_area), hor_res); - lv_opa_t * mask_buf = lv_mem_buf_get(mask_buf_size); - - lv_area_t fill_area; - fill_area.x1 = draw_area.x1 + disp_area->x1; - fill_area.x2 = draw_area.x2 + disp_area->x1; - fill_area.y1 = draw_area.y1 + disp_area->y1; - fill_area.y2 = fill_area.y1; - - int32_t x = draw_buf->area.x1 + draw_area.x1; - - uint32_t mask_p = 0; - - lv_memset_ff(mask_buf, mask_buf_size); - /*Fill the first row with 'color'*/ - for(h = draw_area.y1 + disp_area->y1; h <= draw_area.y2 + disp_area->y1; h++) { - - lv_draw_mask_res_t mask_res = lv_draw_mask_apply(&mask_buf[mask_p], x, h, draw_area_w); - if(mask_res == LV_DRAW_MASK_RES_TRANSP) { - lv_memset_00(&mask_buf[mask_p], draw_area_w); - } - - mask_p += draw_area_w; - if((uint32_t) mask_p + draw_area_w < mask_buf_size) { - fill_area.y2 ++; - } - else { - _lv_blend_fill(&fill_area, clip, - dsc->color, mask_buf, LV_DRAW_MASK_RES_CHANGED, dsc->opa, - dsc->blend_mode); - - fill_area.y1 = fill_area.y2 + 1; - fill_area.y2 = fill_area.y1; - mask_p = 0; - lv_memset_ff(mask_buf, mask_buf_size); - } - } - - /*Flush the last part*/ - if(fill_area.y1 != fill_area.y2) { - fill_area.y2--; - _lv_blend_fill(&fill_area, clip, - dsc->color, mask_buf, LV_DRAW_MASK_RES_CHANGED, dsc->opa, - dsc->blend_mode); - - } - - 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); - lv_draw_mask_remove_id(mask_bottom_id); -#else - LV_UNUSED(point1); - LV_UNUSED(point2); - LV_UNUSED(clip); - LV_UNUSED(dsc); - LV_LOG_WARN("Can't draw skewed line with LV_DRAW_COMPLEX == 0"); -#endif /*LV_DRAW_COMPLEX*/ -} - diff --git a/src/draw/lv_draw_line.h b/src/draw/lv_draw_line.h index 111bced52..18b4769e1 100644 --- a/src/draw/lv_draw_line.h +++ b/src/draw/lv_draw_line.h @@ -13,7 +13,10 @@ extern "C" { /********************* * INCLUDES *********************/ -#include "lv_draw_blend.h" +#include "../lv_conf_internal.h" +#include "../misc/lv_color.h" +#include "../misc/lv_area.h" +#include "../misc/lv_style.h" /********************* * DEFINES @@ -38,7 +41,8 @@ typedef struct { * GLOBAL PROTOTYPES **********************/ -//! @cond Doxygen_Suppress +LV_ATTRIBUTE_FAST_MEM void lv_draw_line_dsc_init(lv_draw_line_dsc_t * dsc); + /** * Draw a line * @param point1 first point of the line @@ -49,9 +53,6 @@ typedef struct { LV_ATTRIBUTE_FAST_MEM void lv_draw_line(const lv_point_t * point1, const lv_point_t * point2, const lv_area_t * clip, const lv_draw_line_dsc_t * dsc); -LV_ATTRIBUTE_FAST_MEM void lv_draw_line_dsc_init(lv_draw_line_dsc_t * dsc); - -//! @endcond /********************** * MACROS diff --git a/src/draw/lv_draw_mask.h b/src/draw/lv_draw_mask.h index ae9ec759a..5ade95b65 100644 --- a/src/draw/lv_draw_mask.h +++ b/src/draw/lv_draw_mask.h @@ -25,9 +25,6 @@ extern "C" { #define LV_MASK_ID_INV (-1) #if LV_DRAW_COMPLEX # define _LV_MASK_MAX_NUM 16 -# ifndef _LV_MASK_BUF_MAX_SIZE -# define _LV_MASK_BUF_MAX_SIZE 2048 /*Should be >= than the max hor res*/ -# endif #else # define _LV_MASK_MAX_NUM 1 #endif diff --git a/src/draw/lv_draw_rect.c b/src/draw/lv_draw_rect.c index 70fd51b95..b6153c50b 100644 --- a/src/draw/lv_draw_rect.c +++ b/src/draw/lv_draw_rect.c @@ -6,20 +6,13 @@ /********************* * INCLUDES *********************/ +#include "lv_draw.h" #include "lv_draw_rect.h" -#include "lv_draw_blend.h" -#include "lv_draw_mask.h" -#include "../misc/lv_math.h" -#include "../misc/lv_txt_ap.h" -#include "../core/lv_refr.h" #include "../misc/lv_assert.h" /********************* * DEFINES *********************/ -#define SHADOW_UPSCALE_SHIFT 6 -#define SHADOW_ENHANCE 1 -#define SPLIT_LIMIT 50 /********************** * TYPEDEFS @@ -28,43 +21,10 @@ /********************** * STATIC PROTOTYPES **********************/ -#if LV_USE_EXTERNAL_RENDERER == 0 -LV_ATTRIBUTE_FAST_MEM static void draw_bg(const lv_area_t * coords, const lv_area_t * clip_area, - const lv_draw_rect_dsc_t * dsc); -LV_ATTRIBUTE_FAST_MEM static void draw_bg_img(const lv_area_t * coords, const lv_area_t * clip, - const lv_draw_rect_dsc_t * dsc); -LV_ATTRIBUTE_FAST_MEM static void draw_border(const lv_area_t * coords, const lv_area_t * clip, - const lv_draw_rect_dsc_t * dsc); - -static void draw_outline(const lv_area_t * coords, const lv_area_t * clip, const lv_draw_rect_dsc_t * dsc); - -#if LV_DRAW_COMPLEX -LV_ATTRIBUTE_FAST_MEM static void draw_shadow(const lv_area_t * coords, const lv_area_t * clip, - const lv_draw_rect_dsc_t * dsc); -LV_ATTRIBUTE_FAST_MEM static void shadow_draw_corner_buf(const lv_area_t * coords, uint16_t * sh_buf, lv_coord_t s, - lv_coord_t r); -LV_ATTRIBUTE_FAST_MEM static void shadow_blur_corner(lv_coord_t size, lv_coord_t sw, uint16_t * sh_ups_buf); -#endif - -void draw_border_generic(const lv_area_t * clip_area, const lv_area_t * outer_area, const lv_area_t * inner_area, - lv_coord_t rout, lv_coord_t rin, lv_color_t color, lv_opa_t opa, lv_blend_mode_t blend_mode); - -static void draw_border_simple(const lv_area_t * clip, const lv_area_t * outer_area, const lv_area_t * inner_area, - lv_color_t color, lv_opa_t opa); - -#if LV_DRAW_COMPLEX - LV_ATTRIBUTE_FAST_MEM static inline lv_color_t grad_get(const lv_draw_rect_dsc_t * dsc, lv_coord_t s, lv_coord_t i); -#endif -#endif /********************** * STATIC VARIABLES **********************/ -#if defined(LV_SHADOW_CACHE_SIZE) && LV_SHADOW_CACHE_SIZE > 0 - static uint8_t sh_cache[LV_SHADOW_CACHE_SIZE * LV_SHADOW_CACHE_SIZE]; - static int32_t sh_cache_size = -1; - static int32_t sh_cache_r = -1; -#endif /********************** * MACROS @@ -91,7 +51,6 @@ LV_ATTRIBUTE_FAST_MEM void lv_draw_rect_dsc_init(lv_draw_rect_dsc_t * dsc) dsc->border_side = LV_BORDER_SIDE_FULL; } -#if LV_USE_EXTERNAL_RENDERER == 0 /** * Draw a rectangle * @param coords the coordinates of the rectangle @@ -101,16 +60,10 @@ LV_ATTRIBUTE_FAST_MEM void lv_draw_rect_dsc_init(lv_draw_rect_dsc_t * dsc) void lv_draw_rect(const lv_area_t * coords, const lv_area_t * clip, const lv_draw_rect_dsc_t * dsc) { if(lv_area_get_height(coords) < 1 || lv_area_get_width(coords) < 1) return; -#if LV_DRAW_COMPLEX - draw_shadow(coords, clip, dsc); -#endif - draw_bg(coords, clip, dsc); - draw_bg_img(coords, clip, dsc); - draw_border(coords, clip, dsc); - - draw_outline(coords, clip, dsc); + const lv_draw_backend_t * backend = lv_draw_backend_get(); + backend->draw_rect(coords, clip, dsc); LV_ASSERT_MEM_INTEGRITY(); } @@ -118,1226 +71,3 @@ void lv_draw_rect(const lv_area_t * coords, const lv_area_t * clip, const lv_dra /********************** * STATIC FUNCTIONS **********************/ - -LV_ATTRIBUTE_FAST_MEM static void draw_bg(const lv_area_t * coords, const lv_area_t * clip_area, - const lv_draw_rect_dsc_t * dsc) -{ - if(dsc->bg_opa <= LV_OPA_MIN) return; - - lv_area_t coords_bg; - lv_area_copy(&coords_bg, coords); - - /*If the border fully covers make the bg area 1px smaller to avoid artifacts on the corners*/ - if(dsc->border_width > 1 && dsc->border_opa >= LV_OPA_MAX && dsc->radius != 0) { - coords_bg.x1 += (dsc->border_side & LV_BORDER_SIDE_LEFT) ? 1 : 0; - coords_bg.y1 += (dsc->border_side & LV_BORDER_SIDE_TOP) ? 1 : 0; - coords_bg.x2 -= (dsc->border_side & LV_BORDER_SIDE_RIGHT) ? 1 : 0; - coords_bg.y2 -= (dsc->border_side & LV_BORDER_SIDE_BOTTOM) ? 1 : 0; - } - - lv_opa_t opa = dsc->bg_opa >= LV_OPA_MAX ? LV_OPA_COVER : dsc->bg_opa; - lv_grad_dir_t grad_dir = dsc->bg_grad_dir; - if(dsc->bg_color.full == dsc->bg_grad_color.full) grad_dir = LV_GRAD_DIR_NONE; - - bool mask_any = lv_draw_mask_is_any(&coords_bg); - - /*Most simple case: just a plain rectangle*/ - if(!mask_any && dsc->radius == 0 && (grad_dir == LV_GRAD_DIR_NONE)) { - _lv_blend_fill(clip_area, &coords_bg, dsc->bg_color, NULL, - LV_DRAW_MASK_RES_FULL_COVER, opa, dsc->blend_mode); - return; - } - - /*Complex case: there is gradient, mask, or radius*/ -#if LV_DRAW_COMPLEX == 0 - LV_LOG_WARN("Can't draw complex rectangle because LV_DRAW_COMPLEX = 0"); -#else - /*Get clipped fill area which is the real draw area. - *It is always the same or inside `fill_area`*/ - lv_area_t draw_area; - if(!_lv_area_intersect(&draw_area, &coords_bg, clip_area)) return; - - /*Get the real radius. Can't be larger than the half of the shortest side */ - lv_coord_t coords_w = lv_area_get_width(&coords_bg); - lv_coord_t coords_h = lv_area_get_height(&coords_bg); - int32_t short_side = LV_MIN(coords_w, coords_h); - int32_t rout = LV_MIN(dsc->radius, short_side >> 1); - - /*Add a radius mask if there is radius*/ - int32_t draw_area_w = lv_area_get_width(&draw_area); - int16_t mask_rout_id = LV_MASK_ID_INV; - lv_opa_t * mask_buf = NULL; - lv_draw_mask_radius_param_t mask_rout_param; - if(rout > 0 || mask_any) { - mask_buf = lv_mem_buf_get(draw_area_w); - lv_draw_mask_radius_init(&mask_rout_param, &coords_bg, rout, false); - mask_rout_id = lv_draw_mask_add(&mask_rout_param, NULL); - } - - /*In case of horizontal gradient pre-compute a line with a gradient*/ - lv_color_t * grad_map = NULL; - lv_color_t * grad_map_ofs = NULL; - if(grad_dir == LV_GRAD_DIR_HOR) { - grad_map = lv_mem_buf_get(coords_w * sizeof(lv_color_t)); - int32_t i; - for(i = 0; i < coords_w; i++) { - grad_map[i] = grad_get(dsc, coords_w, i); - } - grad_map_ofs = grad_map; - if(dsc->bg_grad_dir == LV_GRAD_DIR_HOR) grad_map_ofs += draw_area.x1 - coords_bg.x1; - } - - int32_t h; - lv_draw_mask_res_t mask_res; - lv_area_t blend_area; - blend_area.x1 = draw_area.x1; - blend_area.x2 = draw_area.x2; - - /*There is another mask too. Draw line by line. */ - if(mask_any) { - for(h = draw_area.y1; h <= draw_area.y2; h++) { - blend_area.y1 = h; - blend_area.y2 = h; - - /* Initialize the mask to opa instead of 0xFF and blend with LV_OPA_COVER. - * It saves calculating the final opa in _lv_blend_fill*/ - lv_memset(mask_buf, opa, draw_area_w); - mask_res = lv_draw_mask_apply(mask_buf, draw_area.x1, h, draw_area_w); - if(mask_res == LV_DRAW_MASK_RES_FULL_COVER) mask_res = LV_DRAW_MASK_RES_CHANGED; - - if(grad_dir == LV_GRAD_DIR_NONE) { - _lv_blend_fill(clip_area, &blend_area, dsc->bg_color, mask_buf, mask_res, LV_OPA_COVER, dsc->blend_mode); - } - else if(grad_dir == LV_GRAD_DIR_HOR) { - _lv_blend_map(clip_area, &blend_area, grad_map_ofs, mask_buf, mask_res, LV_OPA_COVER, dsc->blend_mode); - } - else if(grad_dir == LV_GRAD_DIR_VER) { - lv_color_t c = grad_get(dsc, coords_h, h - coords_bg.y1); - _lv_blend_fill(clip_area, &blend_area, c, mask_buf, mask_res, LV_OPA_COVER, dsc->blend_mode); - } - } - goto bg_clean_up; - } - - - /* Draw the top of the rectangle line by line and mirror it to the bottom. - * If there is no radius this cycle won't run because `h` is always `>= h_end`*/ - blend_area.x1 = draw_area.x1; - blend_area.x2 = draw_area.x2; - for(h = 0; h < rout; h++) { - lv_coord_t top_y = coords_bg.y1 + h; - lv_coord_t bottom_y = coords_bg.y2 - h; - if(top_y < draw_area.y1 && bottom_y > draw_area.y2) continue; /*This line is clipped now*/ - - /* Initialize the mask to opa instead of 0xFF and blend with LV_OPA_COVER. - * It saves calculating the final opa in _lv_blend_fill*/ - lv_memset(mask_buf, opa, draw_area_w); - mask_res = lv_draw_mask_apply(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; - - if(grad_dir == LV_GRAD_DIR_NONE) { - _lv_blend_fill(clip_area, &blend_area, dsc->bg_color, mask_buf, mask_res, LV_OPA_COVER, dsc->blend_mode); - } - else if(grad_dir == LV_GRAD_DIR_HOR) { - _lv_blend_map(clip_area, &blend_area, grad_map_ofs, mask_buf, mask_res, LV_OPA_COVER, dsc->blend_mode); - } - else if(grad_dir == LV_GRAD_DIR_VER) { - lv_color_t c = grad_get(dsc, coords_h, top_y - coords_bg.y1); - _lv_blend_fill(clip_area, &blend_area, c, mask_buf, mask_res, LV_OPA_COVER, dsc->blend_mode); - } - } - - if(bottom_y <= draw_area.y2) { - blend_area.y1 = bottom_y; - blend_area.y2 = bottom_y; - - if(grad_dir == LV_GRAD_DIR_NONE) { - _lv_blend_fill(clip_area, &blend_area, dsc->bg_color, mask_buf, mask_res, LV_OPA_COVER, dsc->blend_mode); - } - else if(grad_dir == LV_GRAD_DIR_HOR) { - _lv_blend_map(clip_area, &blend_area, grad_map_ofs, mask_buf, mask_res, LV_OPA_COVER, dsc->blend_mode); - } - else if(grad_dir == LV_GRAD_DIR_VER) { - lv_color_t c = grad_get(dsc, coords_h, bottom_y - coords_bg.y1); - _lv_blend_fill(clip_area, &blend_area, c, mask_buf, mask_res, LV_OPA_COVER, dsc->blend_mode); - } - } - } - - /* Draw the center of the rectangle.*/ - - /*If no other masks and no gradient, the center is a simple rectangle*/ - if(!mask_any && grad_dir == LV_GRAD_DIR_NONE) { - blend_area.y1 = coords_bg.y1 + rout; - blend_area.y2 = coords_bg.y2 - rout; - _lv_blend_fill(clip_area, &blend_area, dsc->bg_color, mask_buf, LV_DRAW_MASK_RES_FULL_COVER, opa, dsc->blend_mode); - } - /*With gradient and/or mask draw line by line*/ - else { - mask_res = LV_DRAW_MASK_RES_FULL_COVER; - int32_t h_end = coords_bg.y2 - rout; - for(h = coords_bg.y1 + rout; h <= h_end; h++) { - /*If there is no other mask do not apply mask as in the center there is no radius to mask*/ - if(mask_any) { - lv_memset_ff(mask_buf, draw_area_w); - mask_res = lv_draw_mask_apply(mask_buf, draw_area.x1, h, draw_area_w); - } - - blend_area.y1 = h; - blend_area.y2 = h; - if(grad_dir == LV_GRAD_DIR_NONE) { - _lv_blend_fill(clip_area, &blend_area, dsc->bg_color, mask_buf, mask_res, opa, dsc->blend_mode); - } - else if(grad_dir == LV_GRAD_DIR_HOR) { - _lv_blend_map(clip_area, &blend_area, grad_map_ofs, mask_buf, mask_res, opa, dsc->blend_mode); - } - else if(grad_dir == LV_GRAD_DIR_VER) { - lv_color_t c = grad_get(dsc, coords_h, h - coords_bg.y1); - _lv_blend_fill(clip_area, &blend_area, c, mask_buf, mask_res, opa, dsc->blend_mode); - } - } - } - - -bg_clean_up: - if(grad_map) lv_mem_buf_release(grad_map); - if(mask_buf) lv_mem_buf_release(mask_buf); - if(mask_rout_id != LV_MASK_ID_INV) { - lv_draw_mask_remove_id(mask_rout_id); - lv_draw_mask_free_param(&mask_rout_param); - } - -#endif -} - -LV_ATTRIBUTE_FAST_MEM static void draw_bg_img(const lv_area_t * coords, const lv_area_t * clip, - const lv_draw_rect_dsc_t * dsc) -{ - if(dsc->bg_img_src == NULL) return; - if(dsc->bg_img_opa <= LV_OPA_MIN) return; - - lv_img_src_t src_type = lv_img_src_get_type(dsc->bg_img_src); - if(src_type == LV_IMG_SRC_SYMBOL) { - lv_point_t size; - lv_txt_get_size(&size, dsc->bg_img_src, dsc->bg_img_symbol_font, 0, 0, LV_COORD_MAX, LV_TEXT_FLAG_NONE); - lv_area_t a; - a.x1 = coords->x1 + lv_area_get_width(coords) / 2 - size.x / 2; - a.x2 = a.x1 + size.x - 1; - a.y1 = coords->y1 + lv_area_get_height(coords) / 2 - size.y / 2; - a.y2 = a.y1 + size.y - 1; - - lv_draw_label_dsc_t label_draw_dsc; - lv_draw_label_dsc_init(&label_draw_dsc); - label_draw_dsc.font = dsc->bg_img_symbol_font; - label_draw_dsc.color = dsc->bg_img_recolor; - label_draw_dsc.opa = dsc->bg_img_opa; - lv_draw_label(&a, clip, &label_draw_dsc, dsc->bg_img_src, NULL); - } - else { - lv_img_header_t header; - lv_res_t res = lv_img_decoder_get_info(dsc->bg_img_src, &header); - if(res != LV_RES_OK) { - LV_LOG_WARN("Couldn't read the background image"); - return; - } - - lv_draw_img_dsc_t img_dsc; - lv_draw_img_dsc_init(&img_dsc); - img_dsc.blend_mode = dsc->blend_mode; - img_dsc.recolor = dsc->bg_img_recolor; - img_dsc.recolor_opa = dsc->bg_img_recolor_opa; - img_dsc.opa = dsc->bg_img_opa; - - /*Center align*/ - if(dsc->bg_img_tiled == false) { - lv_area_t area; - area.x1 = coords->x1 + lv_area_get_width(coords) / 2 - header.w / 2; - area.y1 = coords->y1 + lv_area_get_height(coords) / 2 - header.h / 2; - area.x2 = area.x1 + header.w - 1; - area.y2 = area.y1 + header.h - 1; - - lv_draw_img(&area, clip, dsc->bg_img_src, &img_dsc); - } - else { - lv_area_t area; - area.y1 = coords->y1; - area.y2 = area.y1 + header.h - 1; - - for(; area.y1 <= coords->y2; area.y1 += header.h, area.y2 += header.h) { - - area.x1 = coords->x1; - area.x2 = area.x1 + header.w - 1; - for(; area.x1 <= coords->x2; area.x1 += header.w, area.x2 += header.w) { - lv_draw_img(&area, clip, dsc->bg_img_src, &img_dsc); - } - } - } - } -} - -LV_ATTRIBUTE_FAST_MEM static void draw_border(const lv_area_t * coords, const lv_area_t * clip, - const lv_draw_rect_dsc_t * dsc) -{ - if(dsc->border_opa <= LV_OPA_MIN) return; - if(dsc->border_width == 0) return; - if(dsc->border_side == LV_BORDER_SIDE_NONE) return; - if(dsc->border_post) 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->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)); - - lv_coord_t rin = rout - dsc->border_width; - if(rin < 0) rin = 0; - - draw_border_generic(clip, coords, &area_inner, rout, rin, dsc->border_color, dsc->border_opa, dsc->blend_mode); - -} - - -#if LV_DRAW_COMPLEX -LV_ATTRIBUTE_FAST_MEM static inline lv_color_t grad_get(const lv_draw_rect_dsc_t * dsc, lv_coord_t s, lv_coord_t i) -{ - int32_t min = (dsc->bg_main_color_stop * s) >> 8; - if(i <= min) return dsc->bg_color; - - int32_t max = (dsc->bg_grad_color_stop * s) >> 8; - if(i >= max) return dsc->bg_grad_color; - - int32_t d = dsc->bg_grad_color_stop - dsc->bg_main_color_stop; - d = (s * d) >> 8; - i -= min; - lv_opa_t mix = (i * 255) / d; - return lv_color_mix(dsc->bg_grad_color, dsc->bg_color, mix); -} - -LV_ATTRIBUTE_FAST_MEM static void draw_shadow(const lv_area_t * coords, const lv_area_t * clip, - const lv_draw_rect_dsc_t * dsc) -{ - /*Check whether the shadow is visible*/ - if(dsc->shadow_width == 0) return; - if(dsc->shadow_opa <= LV_OPA_MIN) return; - - if(dsc->shadow_width == 1 && dsc->shadow_spread <= 0 && - dsc->shadow_ofs_x == 0 && dsc->shadow_ofs_y == 0) { - return; - } - - /*Calculate the rectangle which is blurred to get the shadow in `shadow_area`*/ - lv_area_t core_area; - core_area.x1 = coords->x1 + dsc->shadow_ofs_x - dsc->shadow_spread; - core_area.x2 = coords->x2 + dsc->shadow_ofs_x + dsc->shadow_spread; - core_area.y1 = coords->y1 + dsc->shadow_ofs_y - dsc->shadow_spread; - core_area.y2 = coords->y2 + dsc->shadow_ofs_y + dsc->shadow_spread; - - /*Calculate the bounding box of the shadow*/ - lv_area_t shadow_area; - shadow_area.x1 = core_area.x1 - dsc->shadow_width / 2 - 1; - shadow_area.x2 = core_area.x2 + dsc->shadow_width / 2 + 1; - shadow_area.y1 = core_area.y1 - dsc->shadow_width / 2 - 1; - shadow_area.y2 = core_area.y2 + dsc->shadow_width / 2 + 1; - - lv_opa_t opa = dsc->shadow_opa; - if(opa > LV_OPA_MAX) opa = LV_OPA_COVER; - - /*Get clipped draw area which is the real draw area. - *It is always the same or inside `shadow_area`*/ - lv_area_t draw_area; - if(!_lv_area_intersect(&draw_area, &shadow_area, clip)) return; - - /*Consider 1 px smaller bg to be sure the edge will be covered by the shadow*/ - lv_area_t bg_area; - lv_area_copy(&bg_area, coords); - lv_area_increase(&bg_area, -1, -1); - - /*Get the clamped radius*/ - int32_t r_bg = dsc->radius; - lv_coord_t short_side = LV_MIN(lv_area_get_width(&bg_area), lv_area_get_height(&bg_area)); - if(r_bg > short_side >> 1) r_bg = short_side >> 1; - - /*Get the clamped radius*/ - int32_t r_sh = dsc->radius; - short_side = LV_MIN(lv_area_get_width(&core_area), lv_area_get_height(&core_area)); - if(r_sh > short_side >> 1) r_sh = short_side >> 1; - - /*Get how many pixels are affected by the blur on the corners*/ - int32_t corner_size = dsc->shadow_width + r_sh; - - lv_opa_t * sh_buf; - -#if LV_SHADOW_CACHE_SIZE - if(sh_cache_size == corner_size && sh_cache_r == r_sh) { - /*Use the cache if available*/ - sh_buf = lv_mem_buf_get(corner_size * corner_size); - lv_memcpy(sh_buf, sh_cache, corner_size * corner_size); - } - else { - /*A larger buffer is required for calculation*/ - sh_buf = lv_mem_buf_get(corner_size * corner_size * sizeof(uint16_t)); - shadow_draw_corner_buf(&core_area, (uint16_t *)sh_buf, dsc->shadow_width, r_sh); - - /*Cache the corner if it fits into the cache size*/ - if((uint32_t)corner_size * corner_size < sizeof(sh_cache)) { - lv_memcpy(sh_cache, sh_buf, corner_size * corner_size); - sh_cache_size = corner_size; - sh_cache_r = r_sh; - } - } -#else - sh_buf = lv_mem_buf_get(corner_size * corner_size * sizeof(uint16_t)); - shadow_draw_corner_buf(&core_area, (uint16_t *)sh_buf, dsc->shadow_width, r_sh); -#endif - - /*Skip a lot of masking if the background will cover the shadow that would be masked out*/ - bool mask_any = lv_draw_mask_is_any(&shadow_area); - bool simple = true; - if(mask_any || dsc->bg_opa < LV_OPA_COVER) simple = false; - - /*Create a radius mask to clip remove shadow on the bg area*/ - lv_draw_mask_res_t mask_res; - - lv_draw_mask_radius_param_t mask_rout_param; - int16_t mask_rout_id = LV_MASK_ID_INV; - if(!simple) { - lv_draw_mask_radius_init(&mask_rout_param, &bg_area, r_bg, true); - mask_rout_id = lv_draw_mask_add(&mask_rout_param, NULL); - } - lv_opa_t * mask_buf = lv_mem_buf_get(lv_area_get_width(&shadow_area)); - lv_area_t blend_area; - lv_area_t clip_area_sub; - lv_opa_t ** mask_act; - lv_opa_t * sh_buf_tmp; - lv_coord_t y; - bool simple_sub; - - lv_coord_t w_half = shadow_area.x1 + lv_area_get_width(&shadow_area) / 2; - lv_coord_t h_half = shadow_area.y1 + lv_area_get_height(&shadow_area) / 2; - - /*Draw the corners if they are on the current clip area and not fully covered by the bg*/ - - /*Top right corner*/ - blend_area.x2 = shadow_area.x2; - blend_area.x1 = shadow_area.x2 - corner_size + 1; - blend_area.y1 = shadow_area.y1; - blend_area.y2 = shadow_area.y1 + corner_size - 1; - /*Do not overdraw the top other corners*/ - blend_area.x1 = LV_MAX(blend_area.x1, w_half); - blend_area.y2 = LV_MIN(blend_area.y2, h_half); - - if(_lv_area_intersect(&clip_area_sub, &blend_area, clip) && !_lv_area_is_in(&clip_area_sub, &bg_area, r_bg)) { - lv_coord_t w = lv_area_get_width(&clip_area_sub); - sh_buf_tmp = sh_buf; - sh_buf_tmp += (clip_area_sub.y1 - shadow_area.y1) * corner_size; - sh_buf_tmp += clip_area_sub.x1 - (shadow_area.x2 - corner_size + 1); - - /*Do not mask if out of the bg*/ - if(simple && _lv_area_is_out(&clip_area_sub, &bg_area, r_bg)) simple_sub = true; - else simple_sub = simple; - mask_act = simple_sub ? &sh_buf_tmp : &mask_buf; - if(w > 0) { - mask_res = LV_DRAW_MASK_RES_CHANGED; /*In simple mode it won't be overwritten*/ - for(y = clip_area_sub.y1; y <= clip_area_sub.y2; y++) { - blend_area.y1 = y; - blend_area.y2 = y; - - if(!simple_sub) { - lv_memcpy(mask_buf, sh_buf_tmp, corner_size); - mask_res = lv_draw_mask_apply(mask_buf, clip_area_sub.x1, y, w); - if(mask_res == LV_DRAW_MASK_RES_FULL_COVER) mask_res = LV_DRAW_MASK_RES_CHANGED; - } - _lv_blend_fill(&clip_area_sub, &blend_area, dsc->shadow_color, *mask_act, mask_res, dsc->shadow_opa, dsc->blend_mode); - sh_buf_tmp += corner_size; - } - } - } - - /*Bottom right corner. - *Almost the same as top right just read the lines of `sh_buf` from then end*/ - blend_area.x2 = shadow_area.x2; - blend_area.x1 = shadow_area.x2 - corner_size + 1; - blend_area.y1 = shadow_area.y2 - corner_size + 1; - blend_area.y2 = shadow_area.y2; - /*Do not overdraw the other corners*/ - blend_area.x1 = LV_MAX(blend_area.x1, w_half); - blend_area.y1 = LV_MAX(blend_area.y1, h_half + 1); - - if(_lv_area_intersect(&clip_area_sub, &blend_area, clip) && !_lv_area_is_in(&clip_area_sub, &bg_area, r_bg)) { - lv_coord_t w = lv_area_get_width(&clip_area_sub); - sh_buf_tmp = sh_buf; - sh_buf_tmp += (blend_area.y2 - clip_area_sub.y2) * corner_size; - sh_buf_tmp += clip_area_sub.x1 - (shadow_area.x2 - corner_size + 1); - /*Do not mask if out of the bg*/ - if(simple && _lv_area_is_out(&clip_area_sub, &bg_area, r_bg)) simple_sub = true; - else simple_sub = simple; - mask_act = simple_sub ? &sh_buf_tmp : &mask_buf; - - if(w > 0) { - mask_res = LV_DRAW_MASK_RES_CHANGED; /*In simple mode it won't be overwritten*/ - for(y = clip_area_sub.y2; y >= clip_area_sub.y1; y--) { - blend_area.y1 = y; - blend_area.y2 = y; - - if(!simple_sub) { - lv_memcpy(mask_buf, sh_buf_tmp, corner_size); - mask_res = lv_draw_mask_apply(mask_buf, clip_area_sub.x1, y, w); - if(mask_res == LV_DRAW_MASK_RES_FULL_COVER) mask_res = LV_DRAW_MASK_RES_CHANGED; - } - _lv_blend_fill(&clip_area_sub, &blend_area, dsc->shadow_color, *mask_act, mask_res, dsc->shadow_opa, dsc->blend_mode); - sh_buf_tmp += corner_size; - } - } - } - - /*Top side*/ - blend_area.x1 = shadow_area.x1 + corner_size; - blend_area.x2 = shadow_area.x2 - corner_size; - blend_area.y1 = shadow_area.y1; - blend_area.y2 = shadow_area.y1 + corner_size - 1; - blend_area.y2 = LV_MIN(blend_area.y2, h_half); - - if(_lv_area_intersect(&clip_area_sub, &blend_area, clip) && !_lv_area_is_in(&clip_area_sub, &bg_area, r_bg)) { - lv_coord_t w = lv_area_get_width(&clip_area_sub); - sh_buf_tmp = sh_buf; - sh_buf_tmp += (clip_area_sub.y1 - blend_area.y1) * corner_size; - - /*Do not mask if out of the bg*/ - if(simple && _lv_area_is_out(&clip_area_sub, &bg_area, r_bg)) simple_sub = true; - else simple_sub = simple; - - if(w > 0) { - mask_res = LV_DRAW_MASK_RES_CHANGED; /*In simple mode it won't be overwritten*/ - for(y = clip_area_sub.y1; y <= clip_area_sub.y2; y++) { - blend_area.y1 = y; - blend_area.y2 = y; - - if(!simple_sub) { - lv_memset(mask_buf, sh_buf_tmp[0], w); - mask_res = lv_draw_mask_apply(mask_buf, clip_area_sub.x1, y, w); - if(mask_res == LV_DRAW_MASK_RES_FULL_COVER) mask_res = LV_DRAW_MASK_RES_CHANGED; - _lv_blend_fill(&clip_area_sub, &blend_area, dsc->shadow_color, mask_buf, mask_res, dsc->shadow_opa, dsc->blend_mode); - } - else { - lv_opa_t line_opa = opa == LV_OPA_COVER ? sh_buf_tmp[0] : (sh_buf_tmp[0] * dsc->shadow_opa) >> 8; - _lv_blend_fill(&clip_area_sub, &blend_area, dsc->shadow_color, NULL, LV_DRAW_MASK_RES_FULL_COVER, line_opa, - dsc->blend_mode); - } - sh_buf_tmp += corner_size; - } - } - } - - /*Bottom side*/ - blend_area.x1 = shadow_area.x1 + corner_size; - blend_area.x2 = shadow_area.x2 - corner_size; - blend_area.y1 = shadow_area.y2 - corner_size + 1; - blend_area.y2 = shadow_area.y2; - blend_area.y1 = LV_MAX(blend_area.y1, h_half + 1); - - if(_lv_area_intersect(&clip_area_sub, &blend_area, clip) && !_lv_area_is_in(&clip_area_sub, &bg_area, r_bg)) { - lv_coord_t w = lv_area_get_width(&clip_area_sub); - sh_buf_tmp = sh_buf; - sh_buf_tmp += (blend_area.y2 - clip_area_sub.y2) * corner_size; - if(w > 0) { - mask_res = LV_DRAW_MASK_RES_CHANGED; /*In simple mode it won't be overwritten*/ - for(y = clip_area_sub.y2; y >= clip_area_sub.y1; y--) { - blend_area.y1 = y; - blend_area.y2 = y; - - /*Do not mask if out of the bg*/ - if(simple && _lv_area_is_out(&clip_area_sub, &bg_area, r_bg)) simple_sub = true; - else simple_sub = simple; - - if(!simple_sub) { - lv_memset(mask_buf, sh_buf_tmp[0], w); - mask_res = lv_draw_mask_apply(mask_buf, clip_area_sub.x1, y, w); - if(mask_res == LV_DRAW_MASK_RES_FULL_COVER) mask_res = LV_DRAW_MASK_RES_CHANGED; - _lv_blend_fill(&clip_area_sub, &blend_area, dsc->shadow_color, mask_buf, mask_res, dsc->shadow_opa, dsc->blend_mode); - } - else { - lv_opa_t line_opa = opa == LV_OPA_COVER ? sh_buf_tmp[0] : (sh_buf_tmp[0] * dsc->shadow_opa) >> 8; - _lv_blend_fill(&clip_area_sub, &blend_area, dsc->shadow_color, NULL, LV_DRAW_MASK_RES_FULL_COVER, line_opa, - dsc->blend_mode); - - } - sh_buf_tmp += corner_size; - } - } - } - - /*Right side*/ - blend_area.x1 = shadow_area.x2 - corner_size + 1; - blend_area.x2 = shadow_area.x2; - blend_area.y1 = shadow_area.y1 + corner_size; - blend_area.y2 = shadow_area.y2 - corner_size; - /*Do not overdraw the other corners*/ - blend_area.y1 = LV_MIN(blend_area.y1, h_half + 1); - blend_area.y2 = LV_MAX(blend_area.y2, h_half); - blend_area.x1 = LV_MAX(blend_area.x1, w_half); - - if(_lv_area_intersect(&clip_area_sub, &blend_area, clip) && !_lv_area_is_in(&clip_area_sub, &bg_area, r_bg)) { - lv_coord_t w = lv_area_get_width(&clip_area_sub); - sh_buf_tmp = sh_buf; - sh_buf_tmp += (corner_size - 1) * corner_size; - sh_buf_tmp += clip_area_sub.x1 - (shadow_area.x2 - corner_size + 1); - - /*Do not mask if out of the bg*/ - if(simple && _lv_area_is_out(&clip_area_sub, &bg_area, r_bg)) simple_sub = true; - else simple_sub = simple; - mask_act = simple_sub ? &sh_buf_tmp : &mask_buf; - - if(w > 0) { - mask_res = LV_DRAW_MASK_RES_CHANGED; /*In simple mode it won't be overwritten*/ - for(y = clip_area_sub.y1; y <= clip_area_sub.y2; y++) { - blend_area.y1 = y; - blend_area.y2 = y; - - if(!simple_sub) { - lv_memcpy(mask_buf, sh_buf_tmp, w); - mask_res = lv_draw_mask_apply(mask_buf, clip_area_sub.x1, y, w); - if(mask_res == LV_DRAW_MASK_RES_FULL_COVER) mask_res = LV_DRAW_MASK_RES_CHANGED; - } - _lv_blend_fill(&clip_area_sub, &blend_area, dsc->shadow_color, *mask_act, mask_res, dsc->shadow_opa, dsc->blend_mode); - } - } - } - - /*Mirror the shadow corner buffer horizontally*/ - sh_buf_tmp = sh_buf ; - for(y = 0; y < corner_size; y++) { - int32_t x; - lv_opa_t * start = sh_buf_tmp; - lv_opa_t * end = sh_buf_tmp + corner_size - 1; - for(x = 0; x < corner_size / 2; x++) { - lv_opa_t tmp = *start; - *start = *end; - *end = tmp; - - start++; - end--; - } - sh_buf_tmp += corner_size; - } - - /*Left side*/ - blend_area.x1 = shadow_area.x1; - blend_area.x2 = shadow_area.x1 + corner_size - 1; - blend_area.y1 = shadow_area.y1 + corner_size; - blend_area.y2 = shadow_area.y2 - corner_size; - /*Do not overdraw the other corners*/ - blend_area.y1 = LV_MIN(blend_area.y1, h_half + 1); - blend_area.y2 = LV_MAX(blend_area.y2, h_half); - blend_area.x2 = LV_MIN(blend_area.x2, w_half - 1); - - if(_lv_area_intersect(&clip_area_sub, &blend_area, clip) && !_lv_area_is_in(&clip_area_sub, &bg_area, r_bg)) { - lv_coord_t w = lv_area_get_width(&clip_area_sub); - sh_buf_tmp = sh_buf; - sh_buf_tmp += (corner_size - 1) * corner_size; - sh_buf_tmp += clip_area_sub.x1 - blend_area.x1; - - /*Do not mask if out of the bg*/ - if(simple && _lv_area_is_out(&clip_area_sub, &bg_area, r_bg)) simple_sub = true; - else simple_sub = simple; - mask_act = simple_sub ? &sh_buf_tmp : &mask_buf; - if(w > 0) { - mask_res = LV_DRAW_MASK_RES_CHANGED; /*In simple mode it won't be overwritten*/ - for(y = clip_area_sub.y1; y <= clip_area_sub.y2; y++) { - blend_area.y1 = y; - blend_area.y2 = y; - - if(!simple_sub) { - lv_memcpy(mask_buf, sh_buf_tmp, w); - mask_res = lv_draw_mask_apply(mask_buf, clip_area_sub.x1, y, w); - if(mask_res == LV_DRAW_MASK_RES_FULL_COVER) mask_res = LV_DRAW_MASK_RES_CHANGED; - } - _lv_blend_fill(&clip_area_sub, &blend_area, dsc->shadow_color, *mask_act, mask_res, dsc->shadow_opa, dsc->blend_mode); - } - } - } - - /*Top left corner*/ - blend_area.x1 = shadow_area.x1; - blend_area.x2 = shadow_area.x1 + corner_size - 1; - blend_area.y1 = shadow_area.y1; - blend_area.y2 = shadow_area.y1 + corner_size - 1; - /*Do not overdraw the other corners*/ - blend_area.x2 = LV_MIN(blend_area.x2, w_half - 1); - blend_area.y2 = LV_MIN(blend_area.y2, h_half); - - if(_lv_area_intersect(&clip_area_sub, &blend_area, clip) && !_lv_area_is_in(&clip_area_sub, &bg_area, r_bg)) { - lv_coord_t w = lv_area_get_width(&clip_area_sub); - sh_buf_tmp = sh_buf; - sh_buf_tmp += (clip_area_sub.y1 - blend_area.y1) * corner_size; - sh_buf_tmp += clip_area_sub.x1 - blend_area.x1; - - /*Do not mask if out of the bg*/ - if(simple && _lv_area_is_out(&clip_area_sub, &bg_area, r_bg)) simple_sub = true; - else simple_sub = simple; - mask_act = simple_sub ? &sh_buf_tmp : &mask_buf; - - if(w > 0) { - mask_res = LV_DRAW_MASK_RES_CHANGED; /*In simple mode it won't be overwritten*/ - for(y = clip_area_sub.y1; y <= clip_area_sub.y2; y++) { - blend_area.y1 = y; - blend_area.y2 = y; - - if(!simple_sub) { - lv_memcpy(mask_buf, sh_buf_tmp, corner_size); - mask_res = lv_draw_mask_apply(mask_buf, clip_area_sub.x1, y, w); - if(mask_res == LV_DRAW_MASK_RES_FULL_COVER) mask_res = LV_DRAW_MASK_RES_CHANGED; - } - _lv_blend_fill(&clip_area_sub, &blend_area, dsc->shadow_color, *mask_act, mask_res, dsc->shadow_opa, dsc->blend_mode); - sh_buf_tmp += corner_size; - } - } - } - - /*Bottom left corner. - *Almost the same as bottom right just read the lines of `sh_buf` from then end*/ - blend_area.x1 = shadow_area.x1 ; - blend_area.x2 = shadow_area.x1 + corner_size - 1; - blend_area.y1 = shadow_area.y2 - corner_size + 1; - blend_area.y2 = shadow_area.y2; - /*Do not overdraw the other corners*/ - blend_area.y1 = LV_MAX(blend_area.y1, h_half + 1); - blend_area.x2 = LV_MIN(blend_area.x2, w_half - 1); - - if(_lv_area_intersect(&clip_area_sub, &blend_area, clip) && !_lv_area_is_in(&clip_area_sub, &bg_area, r_bg)) { - lv_coord_t w = lv_area_get_width(&clip_area_sub); - sh_buf_tmp = sh_buf; - sh_buf_tmp += (blend_area.y2 - clip_area_sub.y2) * corner_size; - sh_buf_tmp += clip_area_sub.x1 - blend_area.x1; - - /*Do not mask if out of the bg*/ - if(simple && _lv_area_is_out(&clip_area_sub, &bg_area, r_bg)) simple_sub = true; - else simple_sub = simple; - mask_act = simple_sub ? &sh_buf_tmp : &mask_buf; - if(w > 0) { - mask_res = LV_DRAW_MASK_RES_CHANGED; /*In simple mode it won't be overwritten*/ - for(y = clip_area_sub.y2; y >= clip_area_sub.y1; y--) { - blend_area.y1 = y; - blend_area.y2 = y; - - if(!simple_sub) { - lv_memcpy(mask_buf, sh_buf_tmp, corner_size); - mask_res = lv_draw_mask_apply(mask_buf, clip_area_sub.x1, y, w); - if(mask_res == LV_DRAW_MASK_RES_FULL_COVER) mask_res = LV_DRAW_MASK_RES_CHANGED; - } - _lv_blend_fill(&clip_area_sub, &blend_area, dsc->shadow_color, *mask_act, mask_res, dsc->shadow_opa, dsc->blend_mode); - sh_buf_tmp += corner_size; - } - } - } - - /*Draw the center rectangle.*/ - blend_area.x1 = shadow_area.x1 + corner_size ; - blend_area.x2 = shadow_area.x2 - corner_size; - blend_area.y1 = shadow_area.y1 + corner_size; - blend_area.y2 = shadow_area.y2 - corner_size; - - if(_lv_area_intersect(&clip_area_sub, &blend_area, clip) && !_lv_area_is_in(&clip_area_sub, &bg_area, r_bg)) { - lv_coord_t w = lv_area_get_width(&clip_area_sub); - if(w > 0) { - for(y = clip_area_sub.y1; y <= clip_area_sub.y2; y++) { - blend_area.y1 = y; - blend_area.y2 = y; - - lv_memset_ff(mask_buf, w); - mask_res = lv_draw_mask_apply(mask_buf, clip_area_sub.x1, y, w); - _lv_blend_fill(&clip_area_sub, &blend_area, dsc->shadow_color, mask_buf, mask_res, dsc->shadow_opa, dsc->blend_mode); - } - } - } - - if(!simple) { - lv_draw_mask_free_param(&mask_rout_param); - lv_draw_mask_remove_id(mask_rout_id); - } - lv_mem_buf_release(sh_buf); - lv_mem_buf_release(mask_buf); -} - -/** - * Calculate a blurred corner - * @param coords Coordinates of the shadow - * @param sh_buf a buffer to store the result. Its size should be `(sw + r)^2 * 2` - * @param sw shadow width - * @param r radius - */ -LV_ATTRIBUTE_FAST_MEM static void shadow_draw_corner_buf(const lv_area_t * coords, uint16_t * sh_buf, lv_coord_t sw, - lv_coord_t r) -{ - int32_t sw_ori = sw; - int32_t size = sw_ori + r; - - lv_area_t sh_area; - lv_area_copy(&sh_area, coords); - sh_area.x2 = sw / 2 + r - 1 - ((sw & 1) ? 0 : 1); - sh_area.y1 = sw / 2 + 1; - - sh_area.x1 = sh_area.x2 - lv_area_get_width(coords); - sh_area.y2 = sh_area.y1 + lv_area_get_height(coords); - - lv_draw_mask_radius_param_t mask_param; - lv_draw_mask_radius_init(&mask_param, &sh_area, r, false); - -#if SHADOW_ENHANCE - /*Set half shadow width width because blur will be repeated*/ - if(sw_ori == 1) sw = 1; - else sw = sw_ori >> 1; -#endif - - int32_t y; - lv_opa_t * mask_line = lv_mem_buf_get(size); - uint16_t * sh_ups_tmp_buf = (uint16_t *)sh_buf; - for(y = 0; y < size; y++) { - lv_memset_ff(mask_line, size); - lv_draw_mask_res_t mask_res = mask_param.dsc.cb(mask_line, 0, y, size, &mask_param); - if(mask_res == LV_DRAW_MASK_RES_TRANSP) { - lv_memset_00(sh_ups_tmp_buf, size * sizeof(sh_ups_tmp_buf[0])); - } - else { - int32_t i; - sh_ups_tmp_buf[0] = (mask_line[0] << SHADOW_UPSCALE_SHIFT) / sw; - for(i = 1; i < size; i++) { - if(mask_line[i] == mask_line[i - 1]) sh_ups_tmp_buf[i] = sh_ups_tmp_buf[i - 1]; - else sh_ups_tmp_buf[i] = (mask_line[i] << SHADOW_UPSCALE_SHIFT) / sw; - } - } - - sh_ups_tmp_buf += size; - } - 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; - for(i = 0; i < size * size; i++) { - res_buf[i] = (sh_buf[i] >> SHADOW_UPSCALE_SHIFT); - } - return; - } - - shadow_blur_corner(size, sw, sh_buf); - -#if SHADOW_ENHANCE == 0 - /*The result is required in lv_opa_t not uint16_t*/ - uint32_t x; - lv_opa_t * res_buf = (lv_opa_t *)sh_buf; - for(x = 0; x < size * size; x++) { - res_buf[x] = sh_buf[x]; - } -#else - sw += sw_ori & 1; - if(sw > 1) { - uint32_t i; - uint32_t max_v_div = (LV_OPA_COVER << SHADOW_UPSCALE_SHIFT) / sw; - for(i = 0; i < (uint32_t)size * size; i++) { - if(sh_buf[i] == 0) continue; - else if(sh_buf[i] == LV_OPA_COVER) sh_buf[i] = max_v_div; - else sh_buf[i] = (sh_buf[i] << SHADOW_UPSCALE_SHIFT) / sw; - } - - shadow_blur_corner(size, sw, sh_buf); - } - int32_t x; - lv_opa_t * res_buf = (lv_opa_t *)sh_buf; - for(x = 0; x < size * size; x++) { - res_buf[x] = sh_buf[x]; - } -#endif - -} - -LV_ATTRIBUTE_FAST_MEM static void shadow_blur_corner(lv_coord_t size, lv_coord_t sw, uint16_t * sh_ups_buf) -{ - int32_t s_left = sw >> 1; - int32_t s_right = (sw >> 1); - if((sw & 1) == 0) s_left--; - - /*Horizontal blur*/ - uint16_t * sh_ups_blur_buf = lv_mem_buf_get(size * sizeof(uint16_t)); - - int32_t x; - int32_t y; - - uint16_t * sh_ups_tmp_buf = sh_ups_buf; - - for(y = 0; y < size; y++) { - int32_t v = sh_ups_tmp_buf[size - 1] * sw; - for(x = size - 1; x >= 0; x--) { - sh_ups_blur_buf[x] = v; - - /*Forget the right pixel*/ - uint32_t right_val = 0; - if(x + s_right < size) right_val = sh_ups_tmp_buf[x + s_right]; - v -= right_val; - - /*Add the left pixel*/ - uint32_t left_val; - if(x - s_left - 1 < 0) left_val = sh_ups_tmp_buf[0]; - else left_val = sh_ups_tmp_buf[x - s_left - 1]; - v += left_val; - } - lv_memcpy(sh_ups_tmp_buf, sh_ups_blur_buf, size * sizeof(uint16_t)); - sh_ups_tmp_buf += size; - } - - /*Vertical blur*/ - uint32_t i; - uint32_t max_v = LV_OPA_COVER << SHADOW_UPSCALE_SHIFT; - uint32_t max_v_div = max_v / sw; - for(i = 0; i < (uint32_t)size * size; i++) { - if(sh_ups_buf[i] == 0) continue; - else if(sh_ups_buf[i] == max_v) sh_ups_buf[i] = max_v_div; - else sh_ups_buf[i] = sh_ups_buf[i] / sw; - } - - for(x = 0; x < size; x++) { - sh_ups_tmp_buf = &sh_ups_buf[x]; - int32_t v = sh_ups_tmp_buf[0] * sw; - for(y = 0; y < size ; y++, sh_ups_tmp_buf += size) { - sh_ups_blur_buf[y] = v < 0 ? 0 : (v >> SHADOW_UPSCALE_SHIFT); - - /*Forget the top pixel*/ - uint32_t top_val; - if(y - s_right <= 0) top_val = sh_ups_tmp_buf[0]; - else top_val = sh_ups_buf[(y - s_right) * size + x]; - v -= top_val; - - /*Add the bottom pixel*/ - uint32_t bottom_val; - if(y + s_left + 1 < size) bottom_val = sh_ups_buf[(y + s_left + 1) * size + x]; - else bottom_val = sh_ups_buf[(size - 1) * size + x]; - v += bottom_val; - } - - /*Write back the result into `sh_ups_buf`*/ - sh_ups_tmp_buf = &sh_ups_buf[x]; - for(y = 0; y < size; y++, sh_ups_tmp_buf += size) { - (*sh_ups_tmp_buf) = sh_ups_blur_buf[y]; - } - } - - lv_mem_buf_release(sh_ups_blur_buf); -} - -#endif - -static void draw_outline(const lv_area_t * coords, const lv_area_t * clip, const lv_draw_rect_dsc_t * dsc) -{ - if(dsc->outline_opa <= LV_OPA_MIN) return; - if(dsc->outline_width == 0) return; - - lv_opa_t opa = dsc->outline_opa; - - if(opa > LV_OPA_MAX) opa = LV_OPA_COVER; - - /*Get the inner radius*/ - lv_area_t area_inner; - lv_area_copy(&area_inner, coords); - - /*Bring the outline closer to make sure there is no color bleeding with pad=0*/ - lv_coord_t pad = dsc->outline_pad - 1; - area_inner.x1 -= pad; - area_inner.y1 -= pad; - area_inner.x2 += pad; - area_inner.y2 += pad; - - lv_area_t area_outer; - lv_area_copy(&area_outer, &area_inner); - - area_outer.x1 -= dsc->outline_width; - area_outer.x2 += dsc->outline_width; - area_outer.y1 -= dsc->outline_width; - area_outer.y2 += dsc->outline_width; - - - 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_MIN(inner_w, inner_h); - if(rin > short_side >> 1) rin = short_side >> 1; - - lv_coord_t rout = rin + dsc->outline_width; - - draw_border_generic(clip, &area_outer, &area_inner, rout, rin, dsc->outline_color, dsc->outline_opa, dsc->blend_mode); -} - -void draw_border_generic(const lv_area_t * clip_area, const lv_area_t * outer_area, const lv_area_t * inner_area, - lv_coord_t rout, lv_coord_t rin, lv_color_t color, lv_opa_t opa, lv_blend_mode_t blend_mode) -{ - opa = opa >= LV_OPA_COVER ? LV_OPA_COVER : opa; - - bool mask_any = lv_draw_mask_is_any(outer_area); - - if(!mask_any && rout == 0 && rin == 0) { - draw_border_simple(clip_area, outer_area, inner_area, color, opa); - return; - } - -#if LV_DRAW_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, clip_area)) return; - 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); - - /*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, outer_area, rout, false); - mask_rout_id = lv_draw_mask_add(&mask_rout_param, NULL); - } - - /*Create mask for the inner mask*/ - lv_draw_mask_radius_param_t mask_rin_param; - lv_draw_mask_radius_init(&mask_rin_param, inner_area, rin, true); - int16_t mask_rin_id = lv_draw_mask_add(&mask_rin_param, NULL); - - int32_t h; - lv_draw_mask_res_t mask_res; - lv_area_t blend_area; - - /*Calculate the x and y coordinates where the straight parts area*/ - 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); - lv_coord_t core_w = lv_area_get_width(&core_area); - - bool top_side = outer_area->y1 <= inner_area->y1 ? true : false; - bool bottom_side = outer_area->y2 >= inner_area->y2 ? true : false; - - /*If there is other masks, need to draw line by line*/ - if(mask_any) { - blend_area.x1 = draw_area.x1; - blend_area.x2 = draw_area.x2; - for(h = draw_area.y1; h <= draw_area.y2; h++) { - if(!top_side && h < core_area.y1) continue; - if(!bottom_side && h > core_area.y2) break; - - blend_area.y1 = h; - blend_area.y2 = h; - - lv_memset_ff(mask_buf, draw_area_w); - mask_res = lv_draw_mask_apply(mask_buf, draw_area.x1, h, draw_area_w); - _lv_blend_fill(clip_area, &blend_area, color, mask_buf, mask_res, opa, blend_mode); - } - - lv_draw_mask_free_param(&mask_rin_param); - lv_draw_mask_remove_id(mask_rin_id); - if(mask_rout_id != LV_MASK_ID_INV) { - lv_draw_mask_free_param(&mask_rout_param); - lv_draw_mask_remove_id(mask_rout_id); - } - lv_mem_buf_release(mask_buf); - return; - } - - /*No masks*/ - bool left_side = outer_area->x1 <= inner_area->x1 ? true : false; - bool right_side = outer_area->x2 >= inner_area->x2 ? true : false; - - bool split_hor = true; - if(left_side && right_side && top_side && bottom_side && - core_w < SPLIT_LIMIT) { - split_hor = false; - } - - /*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_blend_fill(clip_area, &blend_area, color, NULL, LV_DRAW_MASK_RES_FULL_COVER, opa, blend_mode); - } - - 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_blend_fill(clip_area, &blend_area, color, NULL, LV_DRAW_MASK_RES_FULL_COVER, opa, blend_mode); - } - - 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_blend_fill(clip_area, &blend_area, color, NULL, LV_DRAW_MASK_RES_FULL_COVER, opa, blend_mode); - } - - 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_blend_fill(clip_area, &blend_area, color, NULL, LV_DRAW_MASK_RES_FULL_COVER, opa, blend_mode); - } - - /*Draw the corners*/ - lv_coord_t blend_w; - - /*Left and right corner together is they close to eachother*/ - 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; - lv_coord_t max_h = LV_MAX(rout, outer_area->y1 - inner_area->y1); - for(h = 0; h < max_h; h++) { - lv_coord_t top_y = outer_area->y1 + h; - lv_coord_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_ff(mask_buf, draw_area_w); - mask_res = lv_draw_mask_apply(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_blend_fill(clip_area, &blend_area, color, mask_buf, mask_res, opa, blend_mode); - } - - if(bottom_y <= draw_area.y2) { - blend_area.y1 = bottom_y; - blend_area.y2 = bottom_y; - _lv_blend_fill(clip_area, &blend_area, color, mask_buf, mask_res, opa, blend_mode); - } - } - } - 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_ff(mask_buf, blend_w); - mask_res = lv_draw_mask_apply(mask_buf, blend_area.x1, h, blend_w); - _lv_blend_fill(clip_area, &blend_area, color, mask_buf, mask_res, opa, blend_mode); - } - } - - 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_ff(mask_buf, blend_w); - mask_res = lv_draw_mask_apply(mask_buf, blend_area.x1, h, blend_w); - _lv_blend_fill(clip_area, &blend_area, color, mask_buf, mask_res, opa, blend_mode); - } - } - } - - /*Right corners*/ - 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_ff(mask_buf, blend_w); - mask_res = lv_draw_mask_apply(mask_buf, blend_area.x1, h, blend_w); - _lv_blend_fill(clip_area, &blend_area, color, mask_buf, mask_res, opa, blend_mode); - } - } - - 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_ff(mask_buf, blend_w); - mask_res = lv_draw_mask_apply(mask_buf, blend_area.x1, h, blend_w); - _lv_blend_fill(clip_area, &blend_area, color, mask_buf, mask_res, opa, blend_mode); - } - } - } - } - - lv_draw_mask_free_param(&mask_rin_param); - lv_draw_mask_remove_id(mask_rin_id); - lv_draw_mask_free_param(&mask_rout_param); - lv_draw_mask_remove_id(mask_rout_id); - lv_mem_buf_release(mask_buf); - -#else /*LV_DRAW_COMPLEX*/ - LV_UNUSED(blend_mode); -#endif /*LV_DRAW_COMPLEX*/ -} - -static void draw_border_simple(const lv_area_t * clip, const lv_area_t * outer_area, const lv_area_t * inner_area, - lv_color_t color, lv_opa_t opa) -{ - bool top_side = outer_area->y1 <= inner_area->y1 ? true : false; - bool bottom_side = outer_area->y2 >= inner_area->y2 ? true : false; - bool left_side = outer_area->x1 <= inner_area->x1 ? true : false; - bool right_side = outer_area->x2 >= inner_area->x2 ? true : false; - - lv_area_t a; - /*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_blend_fill(clip, &a, color, NULL, LV_DRAW_MASK_RES_FULL_COVER, opa, LV_BLEND_MODE_NORMAL); - } - - /*Bottom*/ - a.y1 = inner_area->y2 + 1; - a.y2 = outer_area->y2; - if(bottom_side) { - _lv_blend_fill(clip, &a, color, NULL, LV_DRAW_MASK_RES_FULL_COVER, opa, LV_BLEND_MODE_NORMAL); - } - - /*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_blend_fill(clip, &a, color, NULL, LV_DRAW_MASK_RES_FULL_COVER, opa, LV_BLEND_MODE_NORMAL); - } - - /*Right*/ - a.x1 = inner_area->x2 + 1; - a.x2 = outer_area->x2; - if(right_side) { - _lv_blend_fill(clip, &a, color, NULL, LV_DRAW_MASK_RES_FULL_COVER, opa, LV_BLEND_MODE_NORMAL); - } - -} - -#endif diff --git a/src/draw/lv_draw_rect.h b/src/draw/lv_draw_rect.h index f43a0eafb..c6b1412e5 100644 --- a/src/draw/lv_draw_rect.h +++ b/src/draw/lv_draw_rect.h @@ -13,16 +13,17 @@ extern "C" { /********************* * INCLUDES *********************/ -#include "lv_draw_blend.h" -#include "../font/lv_font.h" +#include "../lv_conf_internal.h" +#include "../misc/lv_color.h" +#include "../misc/lv_area.h" +#include "../misc/lv_style.h" /********************* * DEFINES *********************/ -#define LV_RADIUS_CIRCLE 0x7FFF /**< A very big radius to always draw as circle*/ +#define LV_RADIUS_CIRCLE 0x7FFF /**< A very big radius to always draw as circle*/ LV_EXPORT_CONST_INT(LV_RADIUS_CIRCLE); - /********************** * TYPEDEFS **********************/ @@ -75,7 +76,6 @@ typedef struct { LV_ATTRIBUTE_FAST_MEM void lv_draw_rect_dsc_init(lv_draw_rect_dsc_t * dsc); -//! @endcond /** * Draw a rectangle @@ -85,14 +85,6 @@ LV_ATTRIBUTE_FAST_MEM void lv_draw_rect_dsc_init(lv_draw_rect_dsc_t * dsc); */ void lv_draw_rect(const lv_area_t * coords, const lv_area_t * clip, const lv_draw_rect_dsc_t * dsc); -/** - * Draw a pixel - * @param point the coordinates of the point to draw - * @param mask the pixel will be drawn only in this mask - * @param style pointer to a style - */ -//void lv_draw_px(const lv_point_t * point, const lv_area_t * clip_area, const lv_style_t * style); - /********************** * MACROS **********************/ diff --git a/src/draw/lv_draw_triangle.c b/src/draw/lv_draw_triangle.c index 447e1e10c..7a2d816a4 100644 --- a/src/draw/lv_draw_triangle.c +++ b/src/draw/lv_draw_triangle.c @@ -6,6 +6,7 @@ /********************* * INCLUDES *********************/ +#include "lv_draw.h" #include "lv_draw_triangle.h" #include "../misc/lv_math.h" #include "../misc/lv_mem.h" @@ -34,24 +35,6 @@ * GLOBAL FUNCTIONS **********************/ -/** - * Draw a triangle - * @param points pointer to an array with 3 points - * @param clip_area the triangle will be drawn only in this area - * @param draw_dsc pointer to an initialized `lv_draw_rect_dsc_t` variable - */ -void lv_draw_triangle(const lv_point_t points[], const lv_area_t * clip_area, const lv_draw_rect_dsc_t * draw_dsc) -{ -#if LV_DRAW_COMPLEX - lv_draw_polygon(points, 3, clip_area, draw_dsc); -#else - LV_UNUSED(points); - LV_UNUSED(clip_area); - LV_UNUSED(draw_dsc); - LV_LOG_WARN("Can't draw triangle with LV_DRAW_COMPLEX == 0"); -#endif /*LV_DRAW_COMPLEX*/ -} - /** * Draw a polygon. Only convex polygons are supported * @param points an array of points @@ -62,153 +45,17 @@ void lv_draw_triangle(const lv_point_t points[], const lv_area_t * clip_area, co void lv_draw_polygon(const lv_point_t points[], uint16_t point_cnt, const lv_area_t * clip_area, const lv_draw_rect_dsc_t * draw_dsc) { -#if LV_DRAW_COMPLEX - if(point_cnt < 3) return; - if(points == NULL) return; - /*Join adjacent points if they are on the same coordinate*/ - lv_point_t * p = lv_mem_buf_get(point_cnt * sizeof(lv_point_t)); - if(p == NULL) return; - uint16_t i; - uint16_t pcnt = 0; - p[0] = points[0]; - for(i = 0; i < point_cnt - 1; i++) { - if(points[i].x != points[i + 1].x || points[i].y != points[i + 1].y) { - p[pcnt] = points[i]; - pcnt++; - } - } - /*The first and the last points are also adjacent*/ - if(points[0].x != points[point_cnt - 1].x || points[0].y != points[point_cnt - 1].y) { - p[pcnt] = points[point_cnt - 1]; - pcnt++; - } + const lv_draw_backend_t * backend = lv_draw_backend_get(); + backend->draw_polygon(points, point_cnt, clip_area, draw_dsc); +} - point_cnt = pcnt; - if(point_cnt < 3) { - lv_mem_buf_release(p); - return; - } +void lv_draw_triangle(const lv_point_t points[], const lv_area_t * clip_area, + const lv_draw_rect_dsc_t * draw_dsc) +{ - lv_area_t poly_coords = {.x1 = LV_COORD_MAX, .y1 = LV_COORD_MAX, .x2 = LV_COORD_MIN, .y2 = LV_COORD_MIN}; - - for(i = 0; i < point_cnt; i++) { - poly_coords.x1 = LV_MIN(poly_coords.x1, p[i].x); - poly_coords.y1 = LV_MIN(poly_coords.y1, p[i].y); - poly_coords.x2 = LV_MAX(poly_coords.x2, p[i].x); - poly_coords.y2 = LV_MAX(poly_coords.y2, p[i].y); - } - - bool is_common; - lv_area_t poly_mask; - is_common = _lv_area_intersect(&poly_mask, &poly_coords, clip_area); - if(!is_common) { - lv_mem_buf_release(p); - return; - } - /*Find the lowest point*/ - lv_coord_t y_min = p[0].y; - int16_t y_min_i = 0; - - for(i = 1; i < point_cnt; i++) { - if(p[i].y < y_min) { - y_min = p[i].y; - y_min_i = i; - } - } - - lv_draw_mask_line_param_t * mp = lv_mem_buf_get(sizeof(lv_draw_mask_line_param_t) * point_cnt); - lv_draw_mask_line_param_t * mp_next = mp; - - int32_t i_prev_left = y_min_i; - int32_t i_prev_right = y_min_i; - int32_t i_next_left; - int32_t i_next_right; - uint32_t mask_cnt = 0; - - /*Get the index of the left and right points*/ - i_next_left = y_min_i - 1; - if(i_next_left < 0) i_next_left = point_cnt + i_next_left; - - i_next_right = y_min_i + 1; - if(i_next_right > point_cnt - 1) i_next_right = 0; - - /** - * Check if the order of points is inverted or not. - * The normal case is when the left point is on `y_min_i - 1` - * Explanation: - * if angle(p_left) < angle(p_right) -> inverted - * dy_left/dx_left < dy_right/dx_right - * dy_left * dx_right < dy_right * dx_left - */ - lv_coord_t dxl = p[i_next_left].x - p[y_min_i].x; - lv_coord_t dxr = p[i_next_right].x - p[y_min_i].x; - lv_coord_t dyl = p[i_next_left].y - p[y_min_i].y; - lv_coord_t dyr = p[i_next_right].y - p[y_min_i].y; - - bool inv = false; - if(dyl * dxr < dyr * dxl) inv = true; - - do { - if(!inv) { - i_next_left = i_prev_left - 1; - if(i_next_left < 0) i_next_left = point_cnt + i_next_left; - - i_next_right = i_prev_right + 1; - if(i_next_right > point_cnt - 1) i_next_right = 0; - } - else { - i_next_left = i_prev_left + 1; - if(i_next_left > point_cnt - 1) i_next_left = 0; - - i_next_right = i_prev_right - 1; - if(i_next_right < 0) i_next_right = point_cnt + i_next_right; - } - - if(p[i_next_left].y >= p[i_prev_left].y) { - if(p[i_next_left].y != p[i_prev_left].y && - p[i_next_left].x != p[i_prev_left].x) { - lv_draw_mask_line_points_init(mp_next, p[i_prev_left].x, p[i_prev_left].y, - p[i_next_left].x, p[i_next_left].y, - LV_DRAW_MASK_LINE_SIDE_RIGHT); - lv_draw_mask_add(mp_next, mp); - mp_next++; - } - mask_cnt++; - i_prev_left = i_next_left; - } - - if(mask_cnt == point_cnt) break; - - if(p[i_next_right].y >= p[i_prev_right].y) { - if(p[i_next_right].y != p[i_prev_right].y && - p[i_next_right].x != p[i_prev_right].x) { - - lv_draw_mask_line_points_init(mp_next, p[i_prev_right].x, p[i_prev_right].y, - p[i_next_right].x, p[i_next_right].y, - LV_DRAW_MASK_LINE_SIDE_LEFT); - lv_draw_mask_add(mp_next, mp); - mp_next++; - } - mask_cnt++; - i_prev_right = i_next_right; - } - - } while(mask_cnt < point_cnt); - - lv_draw_rect(&poly_coords, clip_area, draw_dsc); - - lv_draw_mask_remove_custom(mp); - - lv_mem_buf_release(mp); - lv_mem_buf_release(p); -#else - LV_UNUSED(points); - LV_UNUSED(point_cnt); - LV_UNUSED(clip_area); - LV_UNUSED(draw_dsc); - LV_LOG_WARN("Can't draw polygon with LV_DRAW_COMPLEX == 0"); -#endif /*LV_DRAW_COMPLEX*/ + const lv_draw_backend_t * backend = lv_draw_backend_get(); + backend->draw_polygon(points, 3, clip_area, draw_dsc); } /********************** diff --git a/src/draw/sw/lv_draw_sw.c b/src/draw/sw/lv_draw_sw.c new file mode 100644 index 000000000..70fd8ea4c --- /dev/null +++ b/src/draw/sw/lv_draw_sw.c @@ -0,0 +1,59 @@ +/** + * @file lv_draw_sw.c + * + */ + +/********************* + * INCLUDES + *********************/ +#include "../lv_draw.h" +#include "lv_draw_sw.h" + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +/********************** + * STATIC VARIABLES + **********************/ + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +void lv_draw_sw_init(void) +{ + static lv_draw_backend_t backend; + lv_draw_backend_init(&backend); + + backend.draw_arc = lv_draw_sw_arc; + backend.draw_rect = lv_draw_sw_rect; + backend.draw_letter = lv_draw_sw_letter; + backend.draw_img = lv_draw_sw_img; + backend.draw_line = lv_draw_sw_line; + backend.draw_polygon = lv_draw_sw_polygon; + backend.blend_fill = lv_blend_sw_fill; + backend.blend_map = lv_blend_sw_map; + + lv_draw_backend_add(&backend); +} + +/********************** + * STATIC FUNCTIONS + **********************/ diff --git a/src/draw/sw/lv_draw_sw.h b/src/draw/sw/lv_draw_sw.h new file mode 100644 index 000000000..1ec2f46df --- /dev/null +++ b/src/draw/sw/lv_draw_sw.h @@ -0,0 +1,82 @@ +/** + * @file lv_draw_sw.h + * + */ + +#ifndef LV_DRAW_SW_H +#define LV_DRAW_SW_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ +#include "../../misc/lv_area.h" +#include "../../misc/lv_color.h" +#include "../lv_draw_arc.h" +#include "../lv_draw_rect.h" +#include "../lv_draw_mask.h" +#include "../lv_draw_line.h" +#include "../lv_draw_img.h" +#include "../lv_draw_mask.h" +#include "../lv_draw_blend.h" + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +void lv_draw_sw_init(void); + +void lv_draw_sw_arc(lv_coord_t center_x, lv_coord_t center_y, uint16_t radius, uint16_t start_angle, + uint16_t end_angle, + const lv_area_t * clip_area, const lv_draw_arc_dsc_t * dsc); + +void lv_draw_sw_rect(const lv_area_t * coords, const lv_area_t * clip, const lv_draw_rect_dsc_t * dsc); + +void lv_draw_sw_letter(const lv_point_t * pos_p, const lv_area_t * clip_area, + const lv_font_t * font_p, + uint32_t letter, + lv_color_t color, lv_opa_t opa, lv_blend_mode_t blend_mode); + +void lv_draw_sw_img(const lv_area_t * map_area, const lv_area_t * clip_area, + const uint8_t * map_p, + const lv_draw_img_dsc_t * draw_dsc, + bool chroma_key, bool alpha_byte); + +void lv_draw_sw_line(const lv_point_t * point1, const lv_point_t * point2, const lv_area_t * clip, + const lv_draw_line_dsc_t * dsc); + +void lv_draw_sw_polygon(const lv_point_t points[], uint16_t point_cnt, const lv_area_t * clip_area, + const lv_draw_rect_dsc_t * draw_dsc); + + +void lv_blend_sw_fill(lv_color_t * dest_buf, lv_coord_t dest_stride, const lv_area_t * fill_area, + lv_color_t color, lv_opa_t * mask, lv_opa_t opa, lv_blend_mode_t blend_mode); + +void lv_blend_sw_map(lv_color_t * dest_buf, lv_coord_t dest_stride, const lv_area_t * clip_area, + const lv_color_t * src_buf, const lv_area_t * src_area, + lv_opa_t * mask, lv_opa_t opa, lv_blend_mode_t mode); + +/*********************** + * GLOBAL VARIABLES + ***********************/ + +/********************** + * MACROS + **********************/ + +#ifdef __cplusplus +} /*extern "C"*/ +#endif + +#endif /*LV_DRAW_SW_H*/ diff --git a/src/draw/sw/lv_draw_sw.mk b/src/draw/sw/lv_draw_sw.mk new file mode 100644 index 000000000..a9823c254 --- /dev/null +++ b/src/draw/sw/lv_draw_sw.mk @@ -0,0 +1,14 @@ +CSRCS += lv_draw_sw.c +CSRCS += lv_draw_sw_arc.c +CSRCS += lv_draw_sw_blend.c +CSRCS += lv_draw_sw_img.c +CSRCS += lv_draw_sw_letter.c +CSRCS += lv_draw_sw_line.c +CSRCS += lv_draw_sw_rect.c +CSRCS += lv_draw_sw_polygon.c +CSRCS += lv_draw_sw_mask.c + +DEPPATH += --dep-path $(LVGL_DIR)/$(LVGL_DIR_NAME)/src/draw/sw +VPATH += :$(LVGL_DIR)/$(LVGL_DIR_NAME)/src/draw/sw + +CFLAGS += "-I$(LVGL_DIR)/$(LVGL_DIR_NAME)/src/draw/sw" diff --git a/src/draw/sw/lv_draw_sw_arc.c b/src/draw/sw/lv_draw_sw_arc.c new file mode 100644 index 000000000..9c20a68a8 --- /dev/null +++ b/src/draw/sw/lv_draw_sw_arc.c @@ -0,0 +1,467 @@ +/** + * @file lv_draw_arc.c + * + */ + +/********************* + * INCLUDES + *********************/ +#include "lv_draw_sw.h" +#include "../../misc/lv_math.h" +#include "../../misc/lv_log.h" +#include "../../misc/lv_mem.h" + +/********************* + * DEFINES + *********************/ +#define SPLIT_RADIUS_LIMIT 10 /*With radius greater than this the arc will drawn in quarters. A quarter is drawn only if there is arc in it*/ +#define SPLIT_ANGLE_GAP_LIMIT 60 /*With small gaps in the arc don't bother with splitting because there is nothing to skip.*/ + +/********************** + * TYPEDEFS + **********************/ +typedef struct { + lv_coord_t center_x; + lv_coord_t center_y; + lv_coord_t radius; + uint16_t start_angle; + uint16_t end_angle; + uint16_t start_quarter; + uint16_t end_quarter; + lv_coord_t width; + lv_draw_rect_dsc_t * draw_dsc; + const lv_area_t * draw_area; + const lv_area_t * clip_area; +} quarter_draw_dsc_t; + +/********************** + * STATIC PROTOTYPES + **********************/ +#if LV_DRAW_COMPLEX + static void draw_quarter_0(quarter_draw_dsc_t * q); + static void draw_quarter_1(quarter_draw_dsc_t * q); + static void draw_quarter_2(quarter_draw_dsc_t * q); + static void draw_quarter_3(quarter_draw_dsc_t * q); + static void get_rounded_area(int16_t angle, lv_coord_t radius, uint8_t thickness, lv_area_t * res_area); +#endif /*LV_DRAW_COMPLEX*/ + +/********************** + * STATIC VARIABLES + **********************/ + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +void lv_draw_sw_arc(lv_coord_t center_x, lv_coord_t center_y, uint16_t radius, uint16_t start_angle, + uint16_t end_angle, + const lv_area_t * clip_area, const lv_draw_arc_dsc_t * dsc) +{ +#if LV_DRAW_COMPLEX + if(dsc->opa <= LV_OPA_MIN) return; + if(dsc->width == 0) return; + if(start_angle == end_angle) return; + + lv_coord_t width = dsc->width; + if(width > radius) width = radius; + + lv_draw_rect_dsc_t cir_dsc; + lv_draw_rect_dsc_init(&cir_dsc); + cir_dsc.blend_mode = dsc->blend_mode; + if(dsc->img_src) { + cir_dsc.bg_opa = LV_OPA_TRANSP; + cir_dsc.bg_img_src = dsc->img_src; + cir_dsc.bg_img_opa = dsc->opa; + } + else { + cir_dsc.bg_opa = dsc->opa; + cir_dsc.bg_color = dsc->color; + } + + lv_area_t area_out; + area_out.x1 = center_x - radius; + area_out.y1 = center_y - radius; + area_out.x2 = center_x + radius - 1; /*-1 because the center already belongs to the left/bottom part*/ + area_out.y2 = center_y + radius - 1; + + lv_area_t area_in; + lv_area_copy(&area_in, &area_out); + area_in.x1 += dsc->width; + area_in.y1 += dsc->width; + area_in.x2 -= dsc->width; + area_in.y2 -= dsc->width; + + /*Create inner the mask*/ + int16_t mask_in_id = LV_MASK_ID_INV; + lv_draw_mask_radius_param_t mask_in_param; + if(lv_area_get_width(&area_in) > 0 && lv_area_get_height(&area_in) > 0) { + lv_draw_mask_radius_init(&mask_in_param, &area_in, LV_RADIUS_CIRCLE, true); + mask_in_id = lv_draw_mask_add(&mask_in_param, NULL); + } + + lv_draw_mask_radius_param_t mask_out_param; + lv_draw_mask_radius_init(&mask_out_param, &area_out, LV_RADIUS_CIRCLE, false); + int16_t mask_out_id = lv_draw_mask_add(&mask_out_param, NULL); + + /*Draw a full ring*/ + if(start_angle + 360 == end_angle || start_angle == end_angle + 360) { + cir_dsc.radius = LV_RADIUS_CIRCLE; + lv_draw_rect(&area_out, clip_area, &cir_dsc); + + lv_draw_mask_remove_id(mask_out_id); + if(mask_in_id != LV_MASK_ID_INV) lv_draw_mask_remove_id(mask_in_id); + return; + } + + while(start_angle >= 360) start_angle -= 360; + while(end_angle >= 360) end_angle -= 360; + + lv_draw_mask_angle_param_t mask_angle_param; + lv_draw_mask_angle_init(&mask_angle_param, center_x, center_y, start_angle, end_angle); + int16_t mask_angle_id = lv_draw_mask_add(&mask_angle_param, NULL); + + int32_t angle_gap; + if(end_angle > start_angle) { + angle_gap = 360 - (end_angle - start_angle); + } + else { + angle_gap = start_angle - end_angle; + } + if(angle_gap > SPLIT_ANGLE_GAP_LIMIT && radius > SPLIT_RADIUS_LIMIT) { + /*Handle each quarter individually and skip which is empty*/ + quarter_draw_dsc_t q_dsc; + q_dsc.center_x = center_x; + q_dsc.center_y = center_y; + q_dsc.radius = radius; + q_dsc.start_angle = start_angle; + q_dsc.end_angle = end_angle; + q_dsc.start_quarter = (start_angle / 90) & 0x3; + q_dsc.end_quarter = (end_angle / 90) & 0x3; + q_dsc.width = width; + q_dsc.draw_dsc = &cir_dsc; + q_dsc.draw_area = &area_out; + q_dsc.clip_area = clip_area; + + draw_quarter_0(&q_dsc); + draw_quarter_1(&q_dsc); + draw_quarter_2(&q_dsc); + draw_quarter_3(&q_dsc); + } + 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); + if(mask_in_id != LV_MASK_ID_INV) lv_draw_mask_remove_id(mask_in_id); + + if(dsc->rounded) { + + lv_draw_mask_radius_param_t mask_end_param; + + lv_area_t round_area; + get_rounded_area(start_angle, radius, width, &round_area); + round_area.x1 += center_x; + round_area.x2 += center_x; + round_area.y1 += center_y; + round_area.y2 += center_y; + lv_area_t clip_area2; + if(_lv_area_intersect(&clip_area2, clip_area, &round_area)) { + lv_draw_mask_radius_init(&mask_end_param, &round_area, LV_RADIUS_CIRCLE, false); + int16_t mask_end_id = lv_draw_mask_add(&mask_end_param, NULL); + + 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); + round_area.x1 += center_x; + round_area.x2 += center_x; + round_area.y1 += center_y; + round_area.y2 += center_y; + if(_lv_area_intersect(&clip_area2, clip_area, &round_area)) { + lv_draw_mask_radius_init(&mask_end_param, &round_area, LV_RADIUS_CIRCLE, false); + int16_t mask_end_id = lv_draw_mask_add(&mask_end_param, NULL); + + 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 + LV_LOG_WARN("Can't draw arc with LV_DRAW_COMPLEX == 0"); + LV_UNUSED(center_x); + LV_UNUSED(center_y); + LV_UNUSED(radius); + LV_UNUSED(start_angle); + LV_UNUSED(end_angle); + LV_UNUSED(clip_area); + LV_UNUSED(dsc); +#endif /*LV_DRAW_COMPLEX*/ +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +#if LV_DRAW_COMPLEX +static void draw_quarter_0(quarter_draw_dsc_t * q) +{ + lv_area_t quarter_area; + + if(q->start_quarter == 0 && q->end_quarter == 0 && q->start_angle < q->end_angle) { + /*Small arc here*/ + quarter_area.y1 = q->center_y + ((lv_trigo_sin(q->start_angle) * (q->radius - q->width)) >> LV_TRIGO_SHIFT); + quarter_area.x2 = q->center_x + ((lv_trigo_sin(q->start_angle + 90) * (q->radius)) >> LV_TRIGO_SHIFT); + + quarter_area.y2 = q->center_y + ((lv_trigo_sin(q->end_angle) * q->radius) >> LV_TRIGO_SHIFT); + quarter_area.x1 = q->center_x + ((lv_trigo_sin(q->end_angle + 90) * (q->radius - q->width)) >> LV_TRIGO_SHIFT); + + bool ok = _lv_area_intersect(&quarter_area, &quarter_area, q->clip_area); + if(ok) lv_draw_rect(q->draw_area, &quarter_area, q->draw_dsc); + } + else if(q->start_quarter == 0 || q->end_quarter == 0) { + /*Start and/or end arcs here*/ + if(q->start_quarter == 0) { + quarter_area.x1 = q->center_x; + quarter_area.y2 = q->center_y + q->radius; + + quarter_area.y1 = q->center_y + ((lv_trigo_sin(q->start_angle) * (q->radius - q->width)) >> LV_TRIGO_SHIFT); + quarter_area.x2 = q->center_x + ((lv_trigo_sin(q->start_angle + 90) * (q->radius)) >> LV_TRIGO_SHIFT); + + bool ok = _lv_area_intersect(&quarter_area, &quarter_area, q->clip_area); + if(ok) lv_draw_rect(q->draw_area, &quarter_area, q->draw_dsc); + } + if(q->end_quarter == 0) { + quarter_area.x2 = q->center_x + q->radius; + quarter_area.y1 = q->center_y; + + quarter_area.y2 = q->center_y + ((lv_trigo_sin(q->end_angle) * q->radius) >> LV_TRIGO_SHIFT); + quarter_area.x1 = q->center_x + ((lv_trigo_sin(q->end_angle + 90) * (q->radius - q->width)) >> LV_TRIGO_SHIFT); + + bool ok = _lv_area_intersect(&quarter_area, &quarter_area, q->clip_area); + if(ok) lv_draw_rect(q->draw_area, &quarter_area, q->draw_dsc); + } + } + else if((q->start_quarter == q->end_quarter && q->start_quarter != 0 && q->end_angle < q->start_angle) || + (q->start_quarter == 2 && q->end_quarter == 1) || + (q->start_quarter == 3 && q->end_quarter == 2) || + (q->start_quarter == 3 && q->end_quarter == 1)) { + /*Arc crosses here*/ + quarter_area.x1 = q->center_x; + quarter_area.y1 = q->center_y; + quarter_area.x2 = q->center_x + q->radius; + quarter_area.y2 = q->center_y + q->radius; + + bool ok = _lv_area_intersect(&quarter_area, &quarter_area, q->clip_area); + if(ok) lv_draw_rect(q->draw_area, &quarter_area, q->draw_dsc); + } +} + +static void draw_quarter_1(quarter_draw_dsc_t * q) +{ + lv_area_t quarter_area; + + if(q->start_quarter == 1 && q->end_quarter == 1 && q->start_angle < q->end_angle) { + /*Small arc here*/ + quarter_area.y2 = q->center_y + ((lv_trigo_sin(q->start_angle) * (q->radius)) >> LV_TRIGO_SHIFT); + quarter_area.x2 = q->center_x + ((lv_trigo_sin(q->start_angle + 90) * (q->radius - q->width)) >> LV_TRIGO_SHIFT); + + quarter_area.y1 = q->center_y + ((lv_trigo_sin(q->end_angle) * (q->radius - q->width)) >> LV_TRIGO_SHIFT); + quarter_area.x1 = q->center_x + ((lv_trigo_sin(q->end_angle + 90) * (q->radius)) >> LV_TRIGO_SHIFT); + + bool ok = _lv_area_intersect(&quarter_area, &quarter_area, q->clip_area); + if(ok) lv_draw_rect(q->draw_area, &quarter_area, q->draw_dsc); + } + else if(q->start_quarter == 1 || q->end_quarter == 1) { + /*Start and/or end arcs here*/ + if(q->start_quarter == 1) { + quarter_area.x1 = q->center_x - q->radius; + quarter_area.y1 = q->center_y; + + quarter_area.y2 = q->center_y + ((lv_trigo_sin(q->start_angle) * (q->radius)) >> LV_TRIGO_SHIFT); + quarter_area.x2 = q->center_x + ((lv_trigo_sin(q->start_angle + 90) * (q->radius - q->width)) >> LV_TRIGO_SHIFT); + + bool ok = _lv_area_intersect(&quarter_area, &quarter_area, q->clip_area); + if(ok) lv_draw_rect(q->draw_area, &quarter_area, q->draw_dsc); + } + if(q->end_quarter == 1) { + quarter_area.x2 = q->center_x - 1; + quarter_area.y2 = q->center_y + q->radius; + + quarter_area.y1 = q->center_y + ((lv_trigo_sin(q->end_angle) * (q->radius - q->width)) >> LV_TRIGO_SHIFT); + quarter_area.x1 = q->center_x + ((lv_trigo_sin(q->end_angle + 90) * (q->radius)) >> LV_TRIGO_SHIFT); + + bool ok = _lv_area_intersect(&quarter_area, &quarter_area, q->clip_area); + if(ok) lv_draw_rect(q->draw_area, &quarter_area, q->draw_dsc); + } + } + else if((q->start_quarter == q->end_quarter && q->start_quarter != 1 && q->end_angle < q->start_angle) || + (q->start_quarter == 0 && q->end_quarter == 2) || + (q->start_quarter == 0 && q->end_quarter == 3) || + (q->start_quarter == 3 && q->end_quarter == 2)) { + /*Arc crosses here*/ + quarter_area.x1 = q->center_x - q->radius; + quarter_area.y1 = q->center_y; + quarter_area.x2 = q->center_x - 1; + quarter_area.y2 = q->center_y + q->radius; + + bool ok = _lv_area_intersect(&quarter_area, &quarter_area, q->clip_area); + if(ok) lv_draw_rect(q->draw_area, &quarter_area, q->draw_dsc); + } +} + +static void draw_quarter_2(quarter_draw_dsc_t * q) +{ + lv_area_t quarter_area; + + if(q->start_quarter == 2 && q->end_quarter == 2 && q->start_angle < q->end_angle) { + /*Small arc here*/ + quarter_area.x1 = q->center_x + ((lv_trigo_sin(q->start_angle + 90) * (q->radius)) >> LV_TRIGO_SHIFT); + quarter_area.y2 = q->center_y + ((lv_trigo_sin(q->start_angle) * (q->radius - q->width)) >> LV_TRIGO_SHIFT); + + quarter_area.y1 = q->center_y + ((lv_trigo_sin(q->end_angle) * q->radius) >> LV_TRIGO_SHIFT); + quarter_area.x2 = q->center_x + ((lv_trigo_sin(q->end_angle + 90) * (q->radius - q->width)) >> LV_TRIGO_SHIFT); + + bool ok = _lv_area_intersect(&quarter_area, &quarter_area, q->clip_area); + if(ok) lv_draw_rect(q->draw_area, &quarter_area, q->draw_dsc); + } + else if(q->start_quarter == 2 || q->end_quarter == 2) { + /*Start and/or end arcs here*/ + if(q->start_quarter == 2) { + quarter_area.x2 = q->center_x - 1; + quarter_area.y1 = q->center_y - q->radius; + + quarter_area.x1 = q->center_x + ((lv_trigo_sin(q->start_angle + 90) * (q->radius)) >> LV_TRIGO_SHIFT); + quarter_area.y2 = q->center_y + ((lv_trigo_sin(q->start_angle) * (q->radius - q->width)) >> LV_TRIGO_SHIFT); + + bool ok = _lv_area_intersect(&quarter_area, &quarter_area, q->clip_area); + if(ok) lv_draw_rect(q->draw_area, &quarter_area, q->draw_dsc); + } + if(q->end_quarter == 2) { + quarter_area.x1 = q->center_x - q->radius; + quarter_area.y2 = q->center_y - 1; + + quarter_area.x2 = q->center_x + ((lv_trigo_sin(q->end_angle + 90) * (q->radius - q->width)) >> LV_TRIGO_SHIFT); + quarter_area.y1 = q->center_y + ((lv_trigo_sin(q->end_angle) * (q->radius)) >> LV_TRIGO_SHIFT); + + bool ok = _lv_area_intersect(&quarter_area, &quarter_area, q->clip_area); + if(ok) lv_draw_rect(q->draw_area, &quarter_area, q->draw_dsc); + } + } + else if((q->start_quarter == q->end_quarter && q->start_quarter != 2 && q->end_angle < q->start_angle) || + (q->start_quarter == 0 && q->end_quarter == 3) || + (q->start_quarter == 1 && q->end_quarter == 3) || + (q->start_quarter == 1 && q->end_quarter == 0)) { + /*Arc crosses here*/ + quarter_area.x1 = q->center_x - q->radius; + quarter_area.y1 = q->center_y - q->radius; + quarter_area.x2 = q->center_x - 1; + quarter_area.y2 = q->center_y - 1; + + bool ok = _lv_area_intersect(&quarter_area, &quarter_area, q->clip_area); + if(ok) lv_draw_rect(q->draw_area, &quarter_area, q->draw_dsc); + } +} + +static void draw_quarter_3(quarter_draw_dsc_t * q) +{ + lv_area_t quarter_area; + + if(q->start_quarter == 3 && q->end_quarter == 3 && q->start_angle < q->end_angle) { + /*Small arc here*/ + quarter_area.x1 = q->center_x + ((lv_trigo_sin(q->start_angle + 90) * (q->radius - q->width)) >> LV_TRIGO_SHIFT); + quarter_area.y1 = q->center_y + ((lv_trigo_sin(q->start_angle) * (q->radius)) >> LV_TRIGO_SHIFT); + + quarter_area.x2 = q->center_x + ((lv_trigo_sin(q->end_angle + 90) * (q->radius)) >> LV_TRIGO_SHIFT); + quarter_area.y2 = q->center_y + ((lv_trigo_sin(q->end_angle) * (q->radius - q->width)) >> LV_TRIGO_SHIFT); + + bool ok = _lv_area_intersect(&quarter_area, &quarter_area, q->clip_area); + if(ok) lv_draw_rect(q->draw_area, &quarter_area, q->draw_dsc); + } + else if(q->start_quarter == 3 || q->end_quarter == 3) { + /*Start and/or end arcs here*/ + if(q->start_quarter == 3) { + quarter_area.x2 = q->center_x + q->radius; + quarter_area.y2 = q->center_y - 1; + + quarter_area.x1 = q->center_x + ((lv_trigo_sin(q->start_angle + 90) * (q->radius - q->width)) >> LV_TRIGO_SHIFT); + quarter_area.y1 = q->center_y + ((lv_trigo_sin(q->start_angle) * (q->radius)) >> LV_TRIGO_SHIFT); + + bool ok = _lv_area_intersect(&quarter_area, &quarter_area, q->clip_area); + if(ok) lv_draw_rect(q->draw_area, &quarter_area, q->draw_dsc); + } + if(q->end_quarter == 3) { + quarter_area.x1 = q->center_x; + quarter_area.y1 = q->center_y - q->radius; + + quarter_area.x2 = q->center_x + ((lv_trigo_sin(q->end_angle + 90) * (q->radius)) >> LV_TRIGO_SHIFT); + quarter_area.y2 = q->center_y + ((lv_trigo_sin(q->end_angle) * (q->radius - q->width)) >> LV_TRIGO_SHIFT); + + bool ok = _lv_area_intersect(&quarter_area, &quarter_area, q->clip_area); + if(ok) lv_draw_rect(q->draw_area, &quarter_area, q->draw_dsc); + } + } + else if((q->start_quarter == q->end_quarter && q->start_quarter != 3 && q->end_angle < q->start_angle) || + (q->start_quarter == 2 && q->end_quarter == 0) || + (q->start_quarter == 1 && q->end_quarter == 0) || + (q->start_quarter == 2 && q->end_quarter == 1)) { + /*Arc crosses here*/ + quarter_area.x1 = q->center_x; + quarter_area.y1 = q->center_y - q->radius; + quarter_area.x2 = q->center_x + q->radius; + quarter_area.y2 = q->center_y - 1; + + bool ok = _lv_area_intersect(&quarter_area, &quarter_area, q->clip_area); + if(ok) lv_draw_rect(q->draw_area, &quarter_area, q->draw_dsc); + } +} + +static void get_rounded_area(int16_t angle, lv_coord_t radius, uint8_t thickness, lv_area_t * res_area) +{ + const uint8_t ps = 8; + const uint8_t pa = 127; + + int32_t thick_half = thickness / 2; + uint8_t thick_corr = (thickness & 0x01) ? 0 : 1; + + int32_t cir_x; + int32_t cir_y; + + cir_x = ((radius - thick_half) * lv_trigo_sin(90 - angle)) >> (LV_TRIGO_SHIFT - ps); + cir_y = ((radius - thick_half) * lv_trigo_sin(angle)) >> (LV_TRIGO_SHIFT - ps); + + /*Actually the center of the pixel need to be calculated so apply 1/2 px offset*/ + if(cir_x > 0) { + cir_x = (cir_x - pa) >> ps; + res_area->x1 = cir_x - thick_half + thick_corr; + res_area->x2 = cir_x + thick_half; + } + else { + cir_x = (cir_x + pa) >> ps; + res_area->x1 = cir_x - thick_half; + res_area->x2 = cir_x + thick_half - thick_corr; + } + + if(cir_y > 0) { + cir_y = (cir_y - pa) >> ps; + res_area->y1 = cir_y - thick_half + thick_corr; + res_area->y2 = cir_y + thick_half; + } + else { + cir_y = (cir_y + pa) >> ps; + res_area->y1 = cir_y - thick_half; + res_area->y2 = cir_y + thick_half - thick_corr; + } +} + +#endif /*LV_DRAW_COMPLEX*/ diff --git a/src/draw/sw/lv_draw_sw_blend.c b/src/draw/sw/lv_draw_sw_blend.c new file mode 100644 index 000000000..680e665f7 --- /dev/null +++ b/src/draw/sw/lv_draw_sw_blend.c @@ -0,0 +1,754 @@ +/** + * @file lv_draw_blend.c + * + */ + +/********************* + * INCLUDES + *********************/ +#include "lv_draw_sw.h" +#include "../../misc/lv_math.h" +#include "../../hal/lv_hal_disp.h" +#include "../../core/lv_refr.h" + +/********************* + * DEFINES + *********************/ +#define GPU_SIZE_LIMIT 240 + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ + +static void fill_set_px(lv_color_t * dest_buf, lv_coord_t dest_stride, const lv_area_t * area, + lv_color_t color, lv_opa_t opa, const lv_opa_t * mask); + +LV_ATTRIBUTE_FAST_MEM static void fill_normal(lv_color_t * dest_buf, lv_coord_t dest_stride, const lv_area_t * area, + lv_color_t color, lv_opa_t opa, const lv_opa_t * mask); + +#if LV_DRAW_COMPLEX +static void fill_blended(lv_color_t * dest_buf, lv_coord_t dest_stride, const lv_area_t * area, + lv_color_t color, lv_opa_t opa, const lv_opa_t * mask, lv_blend_mode_t mode); +#endif /*LV_DRAW_COMPLEX*/ + + +static void map_set_px(lv_color_t * dest_buf, lv_coord_t dest_stride, const lv_area_t * clip_area, + const lv_color_t * src_buf, const lv_area_t * src_area, + const lv_opa_t * mask, lv_opa_t opa); + +LV_ATTRIBUTE_FAST_MEM static void map_normal(lv_color_t * dest_buf, lv_coord_t dest_stride, const lv_area_t * clip_area, + const lv_color_t * src_buf, const lv_area_t * src_area, + const lv_opa_t * mask, lv_opa_t opa); + +#if LV_DRAW_COMPLEX +static void map_blended(lv_color_t * dest_buf, lv_coord_t dest_stride, const lv_area_t * clip_area, + const lv_color_t * src_buf, const lv_area_t * src_area, + const lv_opa_t * mask, lv_opa_t opa, lv_blend_mode_t mode); + +static inline lv_color_t color_blend_true_color_additive(lv_color_t fg, lv_color_t bg, lv_opa_t opa); +static inline lv_color_t color_blend_true_color_subtractive(lv_color_t fg, lv_color_t bg, lv_opa_t opa); +static inline lv_color_t color_blend_true_color_multiply(lv_color_t fg, lv_color_t bg, lv_opa_t opa); +#endif /*LV_DRAW_COMPLEX*/ + +/********************** + * STATIC VARIABLES + **********************/ + +/********************** + * MACROS + **********************/ +#if LV_COLOR_SCREEN_TRANSP == 0 +#define FILL_NORMAL_MASK_PX(color) \ + if(*mask == LV_OPA_COVER) *dest_buf = color; \ + else *dest_buf = lv_color_mix(color, *dest_buf, *mask); \ + mask++; \ + dest_buf++; + +#else +#define FILL_NORMAL_MASK_PX(color) \ + if(*mask == LV_OPA_COVER) *disp_buf = color; \ + else if(disp->driver->screen_transp) lv_color_mix_with_alpha(*disp_buf, disp_buf->ch.alpha, color, *mask, disp_buf, &disp_buf->ch.alpha); \ + else *disp_buf = lv_color_mix(color, *disp_buf, *mask); \ + mask++; \ + disp_buf++; +#endif + +#define MAP_NORMAL_MASK_PX(x) \ + if(*mask_tmp_x) { \ + if(*mask_tmp_x == LV_OPA_COVER) dest_buf[x] = src_buf[x]; \ + else dest_buf[x] = lv_color_mix(src_buf[x], dest_buf[x], *mask_tmp_x); \ + } \ + mask_tmp_x++; + +#define MAP_NORMAL_MASK_PX_SCR_TRANSP(x) \ + if(*mask_tmp_x) { \ + if(*mask_tmp_x == LV_OPA_COVER) disp_buf[x] = map_buf[x]; \ + else if(disp->driver->screen_transp) lv_color_mix_with_alpha(disp_buf[x], disp_buf[x].ch.alpha, \ + map_buf[x], *mask_tmp_x, &disp_buf[x], &disp_buf[x].ch.alpha); \ + else disp_buf[x] = lv_color_mix(map_buf[x], disp_buf[x], *mask_tmp_x); \ + } \ + mask_tmp_x++; + +/********************** + * GLOBAL FUNCTIONS + **********************/ + + +LV_ATTRIBUTE_FAST_MEM void lv_blend_sw_fill(lv_color_t * dest_buf, lv_coord_t dest_stride, const lv_area_t * fill_area, + lv_color_t color, lv_opa_t * mask, lv_opa_t opa, lv_blend_mode_t blend_mode) +{ + lv_disp_t * disp = _lv_refr_get_disp_refreshing(); + + /*Round the values in the mask if anti-aliasing is disabled*/ + lv_coord_t area_size = lv_area_get_size(fill_area); + if(mask && disp->driver->antialiasing == 0 && mask) { + lv_coord_t i; + for(i = 0; i < area_size; i++) mask[i] = mask[i] > 128 ? LV_OPA_COVER : LV_OPA_TRANSP; + } + + if(disp->driver->set_px_cb) { + fill_set_px(dest_buf, dest_stride, fill_area, color, opa, mask); + } + else if(blend_mode == LV_BLEND_MODE_NORMAL) { + fill_normal(dest_buf, dest_stride, fill_area, color, opa, mask); + } +#if LV_DRAW_COMPLEX + else { + fill_blended(dest_buf, dest_stride, fill_area, color, opa, mask, blend_mode); + } +#endif +} + +void lv_blend_sw_map(lv_color_t * dest_buf, lv_coord_t dest_stride, const lv_area_t * clip_area, + const lv_color_t * src_buf, const lv_area_t * src_area, + lv_opa_t * mask, lv_opa_t opa, lv_blend_mode_t mode) +{ + lv_disp_t * disp = _lv_refr_get_disp_refreshing(); + + /*Round the values in the mask if anti-aliasing is disabled*/ + int32_t area_size = lv_area_get_size(clip_area); + if(mask && disp->driver->antialiasing == 0) { + lv_coord_t i; + for(i = 0; i < area_size; i++) mask[i] = mask[i] > 128 ? LV_OPA_COVER : LV_OPA_TRANSP; + } + + + if(disp->driver->set_px_cb) { + map_set_px(dest_buf, dest_stride, clip_area, src_buf, src_area, mask, opa); + } + else if(mode == LV_BLEND_MODE_NORMAL) { + map_normal(dest_buf, dest_stride, clip_area, src_buf, src_area, mask, opa); + } +#if LV_DRAW_COMPLEX + else { + map_blended(dest_buf, dest_stride, clip_area, src_buf, src_area, mask, opa, mode); + } +#endif +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +static void fill_set_px(lv_color_t * dest_buf, lv_coord_t dest_stride, const lv_area_t * fill_area, + lv_color_t color, lv_opa_t opa, const lv_opa_t * mask) +{ + lv_disp_t * disp = _lv_refr_get_disp_refreshing(); + + int32_t x; + int32_t y; + + if(mask == NULL) { + for(y = fill_area->y1; y <= fill_area->y2; y++) { + for(x = fill_area->x1; x <= fill_area->x2; x++) { + disp->driver->set_px_cb(disp->driver, (void *)dest_buf, dest_stride, x, y, color, opa); + } + } + } + else { + int32_t area_w = lv_area_get_width(fill_area); + int32_t area_h = lv_area_get_height(fill_area); + + for(y = 0; y <= area_w; y++) { + for(x = 0; x <= area_h; x++) { + if(mask[x]) { + disp->driver->set_px_cb(disp->driver, (void *)dest_buf, dest_stride, fill_area->x1 + x, fill_area->y1 + y, color, + (uint32_t)((uint32_t)opa * mask[x]) >> 8); + } + } + mask += area_w; + } + } +} + +LV_ATTRIBUTE_FAST_MEM static void fill_normal(lv_color_t * dest_buf, lv_coord_t dest_stride, + const lv_area_t * fill_area, + lv_color_t color, lv_opa_t opa, const lv_opa_t * mask) +{ + + int32_t area_w = lv_area_get_width(fill_area); + int32_t area_h = lv_area_get_height(fill_area); + + dest_buf += dest_stride * fill_area->y1 + fill_area->x1; + + int32_t x; + int32_t y; + + /*Simple fill (maybe with opacity), no masking*/ + if(mask == NULL) { + if(opa > LV_OPA_MAX) { + for(y = 0; y < area_h; y++) { + lv_color_fill(dest_buf, color, area_w); + dest_buf += dest_stride; + } + } + /*No mask with opacity*/ + else { + lv_color_t last_dest_color = lv_color_black(); + lv_color_t last_res_color = lv_color_mix(color, last_dest_color, opa); + + uint16_t color_premult[3]; + lv_color_premult(color, opa, color_premult); + lv_opa_t opa_inv = 255 - opa; + + for(y = 0; y < area_h; y++) { + for(x = 0; x < area_w; x++) { + if(last_dest_color.full != dest_buf[x].full) { + last_dest_color = dest_buf[x]; + +#if LV_COLOR_SCREEN_TRANSP + if(disp->driver->screen_transp) { + lv_color_mix_with_alpha(dest_buf[x], dest_buf[x].ch.alpha, color, opa, &last_res_color, + &last_res_color.ch.alpha); + } + else +#endif + { + last_res_color = lv_color_mix_premult(color_premult, dest_buf[x], opa_inv); + } + } + dest_buf[x] = last_res_color; + } + dest_buf += dest_stride; + } + } + } + /*Masked*/ + else { + int32_t x_end4 = 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 < area_h; y++) { + for(x = 0; x < area_w && ((lv_uintptr_t)(mask) & 0x3); x++) { + FILL_NORMAL_MASK_PX(color) + } + + for(; x <= x_end4; x += 4) { + uint32_t mask32 = *((uint32_t *)mask); + if(mask32 == 0xFFFFFFFF) { +#if LV_COLOR_DEPTH == 16 + if((lv_uintptr_t)dest_buf & 0x3) { + *(dest_buf + 0) = color; + uint32_t * d = (uint32_t *)(dest_buf + 1); + *d = c32; + *(dest_buf + 3) = color; + } + else { + uint32_t * d = (uint32_t *)dest_buf; + *d = c32; + *(d + 1) = c32; + } +#else + dest_buf[0] = color; + dest_buf[1] = color; + dest_buf[2] = color; + dest_buf[3] = color; +#endif + dest_buf += 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; + dest_buf += 4; + } + } + + for(; x < area_w ; x++) { + FILL_NORMAL_MASK_PX(color) + } + dest_buf += (dest_stride - area_w); + } + } + /*Handle opa and mask values too*/ + else { + /*Buffer the result color to avoid recalculating the same color*/ + lv_color_t last_dest_color; + lv_color_t last_res_color; + lv_opa_t last_mask = LV_OPA_TRANSP; + last_dest_color.full = dest_buf[0].full; + last_res_color.full = dest_buf[0].full; + lv_opa_t opa_tmp = LV_OPA_TRANSP; + + for(y = 0; y < area_h; y++) { + const lv_opa_t * mask_line = mask; + for(x = 0; x < area_w; x++) { + if(*mask_line) { + if(*mask_line != last_mask) opa_tmp = *mask_line == LV_OPA_COVER ? opa : + (uint32_t)((uint32_t)(*mask_line) * opa) >> 8; + if(*mask_line != last_mask || last_dest_color.full != dest_buf[x].full) { +#if LV_COLOR_SCREEN_TRANSP + if(disp->driver->screen_transp) { + lv_color_mix_with_alpha(dest_buf[x], dest_buf[x].ch.alpha, color, opa_tmp, &last_res_color, + &last_res_color.ch.alpha); + } + else +#endif + { + if(opa_tmp == LV_OPA_COVER) last_res_color = color; + else last_res_color = lv_color_mix(color, dest_buf[x], opa_tmp); + } + last_mask = *mask_line; + last_dest_color.full = dest_buf[x].full; + } + dest_buf[x] = last_res_color; + } + mask_line++; + } + dest_buf += dest_stride; + mask += area_w; + } + } + } +} + +#if LV_DRAW_COMPLEX +static void fill_blended(lv_color_t * dest_buf, lv_coord_t dest_stride, const lv_area_t * fill_area, + lv_color_t color, lv_opa_t opa, const lv_opa_t * mask, lv_blend_mode_t mode) +{ + dest_buf += dest_stride * fill_area->y1 + fill_area->x1; + + lv_color_t (*blend_fp)(lv_color_t, lv_color_t, lv_opa_t); + switch(mode) { + case LV_BLEND_MODE_ADDITIVE: + blend_fp = color_blend_true_color_additive; + break; + case LV_BLEND_MODE_SUBTRACTIVE: + blend_fp = color_blend_true_color_subtractive; + break; + case LV_BLEND_MODE_MULTIPLY: + blend_fp = color_blend_true_color_multiply; + break; + default: + LV_LOG_WARN("fill_blended: unsupported blend mode"); + return; + } + + int32_t area_w = lv_area_get_width(fill_area); + int32_t area_h = lv_area_get_height(fill_area); + + int32_t x; + int32_t y; + + /*Simple fill (maybe with opacity), no masking*/ + if(mask == NULL) { + lv_color_t last_dest_color = lv_color_black(); + lv_color_t last_res_color = lv_color_mix(color, last_dest_color, opa); + for(y = 0; y < area_w; y++) { + for(x = 0; x < area_h; x++) { + if(last_dest_color.full != dest_buf[x].full) { + last_dest_color = dest_buf[x]; + last_res_color = blend_fp(color, dest_buf[x], opa); + } + dest_buf[x] = last_res_color; + } + dest_buf += dest_stride; + } + } + /*Masked*/ + else { + + /*Buffer the result color to avoid recalculating the same color*/ + lv_color_t last_dest_color; + lv_color_t last_res_color; + lv_opa_t last_mask = LV_OPA_TRANSP; + last_dest_color.full = dest_buf[0].full; + last_res_color.full = dest_buf[0].full; + + for(y = 0; y < area_h; y++) { + for(x = 0; x < area_w; x++) { + if(mask[x] == 0) continue; + if(mask[x] != last_mask || last_dest_color.full != dest_buf[x].full) { + lv_opa_t opa_tmp = mask[x] >= LV_OPA_MAX ? opa : (uint32_t)((uint32_t)mask[x] * opa) >> 8; + + last_res_color = blend_fp(color, dest_buf[x], opa_tmp); + last_mask = mask[x]; + last_dest_color.full = dest_buf[x].full; + } + dest_buf[x] = last_res_color; + } + dest_buf += dest_stride; + mask += area_w; + } + } +} +#endif + +static void map_set_px(lv_color_t * dest_buf, lv_coord_t dest_stride, const lv_area_t * clip_area, + const lv_color_t * src_buf, const lv_area_t * src_area, + const lv_opa_t * mask, lv_opa_t opa) + +{ + lv_disp_t * disp = _lv_refr_get_disp_refreshing(); + + int32_t clip_w = lv_area_get_width(clip_area); + int32_t clip_h = lv_area_get_height(clip_area); + + int32_t src_stride = lv_area_get_width(src_area); + + dest_buf += dest_stride * clip_area->y1 + clip_area->x1; + + src_buf += src_stride * (clip_area->y1 - src_area->y1); + src_buf += (clip_area->x1 - src_area->x1); + + int32_t x; + int32_t y; + + if(mask == NULL) { + for(y = 0; y < clip_h; y++) { + for(x = 0; x <= clip_w; x++) { + disp->driver->set_px_cb(disp->driver, (void *)dest_buf, dest_stride, clip_area->x1 + x, clip_area->y1 + y, src_buf[x], + opa); + } + src_buf += src_stride; + } + } + else { + for(y = 0; y < clip_h; y++) { + for(x = 0; x <= clip_w; x++) { + if(mask[x]) { + disp->driver->set_px_cb(disp->driver, (void *)dest_buf, dest_stride, clip_area->x1 + x, clip_area->y1 + y, src_buf[x], + (uint32_t)((uint32_t)opa * mask[x]) >> 8); + } + } + mask += clip_w; + src_buf += src_stride; + } + } +} + +LV_ATTRIBUTE_FAST_MEM static void map_normal(lv_color_t * dest_buf, lv_coord_t dest_stride, const lv_area_t * clip_area, + const lv_color_t * src_buf, const lv_area_t * src_area, + const lv_opa_t * mask, lv_opa_t opa) +{ + int32_t clip_w = lv_area_get_width(clip_area); + int32_t clip_h = lv_area_get_height(clip_area); + + int32_t src_stride = lv_area_get_width(src_area); + + dest_buf += dest_stride * clip_area->y1 + clip_area->x1; + + src_buf += src_stride * (clip_area->y1 - src_area->y1); + src_buf += (clip_area->x1 - src_area->x1); + +#if LV_COLOR_SCREEN_TRANSP + lv_disp_t * disp = _lv_refr_get_disp_refreshing(); +#endif + + int32_t x; + int32_t y; + + /*Simple fill (maybe with opacity), no masking*/ + if(mask == NULL) { + if(opa >= LV_OPA_MAX) { + for(y = 0; y < clip_h; y++) { + lv_memcpy(dest_buf, src_buf, clip_w * sizeof(lv_color_t)); + dest_buf += dest_stride; + src_buf += src_stride; + } + } + else { + for(y = 0; y < clip_h; y++) { + for(x = 0; x < clip_w; x++) { +#if LV_COLOR_SCREEN_TRANSP + if(disp->driver->screen_transp) { + lv_color_mix_with_alpha(dest_buf[x], dest_buf[x].ch.alpha, src_buf[x], opa, &dest_buf[x], + &dest_buf[x].ch.alpha); + } + else +#endif + { + dest_buf[x] = lv_color_mix(src_buf[x], dest_buf[x], opa); + } + } + dest_buf += dest_stride; + src_buf += src_stride; + } + } + } + /*Masked*/ + else { + /*Only the mask matters*/ + if(opa > LV_OPA_MAX) { + int32_t x_end4 = clip_w - 4; + + for(y = 0; y < clip_h; y++) { + const lv_opa_t * mask_tmp_x = mask; +#if 0 + for(x = 0; x < clip_w; x++) { + MAP_NORMAL_MASK_PX(x); + } +#else + for(x = 0; x < clip_w && ((lv_uintptr_t)mask_tmp_x & 0x3); x++) { +#if LV_COLOR_SCREEN_TRANSP + MAP_NORMAL_MASK_PX_SCR_TRANSP(x) +#else + MAP_NORMAL_MASK_PX(x) +#endif + } + + uint32_t * mask32 = (uint32_t *)mask_tmp_x; + for(; x < x_end4; x += 4) { + if(*mask32) { + if((*mask32) == 0xFFFFFFFF) { + dest_buf[x] = src_buf[x]; + dest_buf[x + 1] = src_buf[x + 1]; + dest_buf[x + 2] = src_buf[x + 2]; + dest_buf[x + 3] = src_buf[x + 3]; + } + else { + mask_tmp_x = (const lv_opa_t *)mask32; +#if LV_COLOR_SCREEN_TRANSP + MAP_NORMAL_MASK_PX_SCR_TRANSP(x) + MAP_NORMAL_MASK_PX_SCR_TRANSP(x + 1) + MAP_NORMAL_MASK_PX_SCR_TRANSP(x + 2) + MAP_NORMAL_MASK_PX_SCR_TRANSP(x + 3) +#else + MAP_NORMAL_MASK_PX(x) + MAP_NORMAL_MASK_PX(x + 1) + MAP_NORMAL_MASK_PX(x + 2) + MAP_NORMAL_MASK_PX(x + 3) +#endif + } + } + mask32++; + } + + mask_tmp_x = (const lv_opa_t *)mask32; + for(; x < clip_w ; x++) { +#if LV_COLOR_SCREEN_TRANSP + MAP_NORMAL_MASK_PX_SCR_TRANSP(x) +#else + MAP_NORMAL_MASK_PX(x) +#endif + } +#endif + dest_buf += dest_stride; + src_buf += src_stride; + mask += clip_w; + } + } + /*Handle opa and mask values too*/ + else { + for(y = 0; y < clip_h; y++) { + for(x = 0; x < clip_w; x++) { + if(mask[x]) { + lv_opa_t opa_tmp = mask[x] >= LV_OPA_MAX ? opa : ((opa * mask[x]) >> 8); +#if LV_COLOR_SCREEN_TRANSP + if(disp->driver->screen_transp) { + lv_color_mix_with_alpha(dest_buf[x], dest_buf[x].ch.alpha, src_buf[x], opa_tmp, &dest_buf[x], + &dest_buf[x].ch.alpha); + } + else +#endif + { + dest_buf[x] = lv_color_mix(src_buf[x], dest_buf[x], opa_tmp); + } + } + } + dest_buf += dest_stride; + src_buf += src_stride; + mask += clip_w; + } + } + } +} +#if LV_DRAW_COMPLEX +static void map_blended(lv_color_t * dest_buf, lv_coord_t dest_stride, const lv_area_t * clip_area, + const lv_color_t * src_buf, const lv_area_t * src_area, + const lv_opa_t * mask, lv_opa_t opa, lv_blend_mode_t mode) +{ + int32_t clip_w = lv_area_get_width(clip_area); + int32_t clip_h = lv_area_get_height(clip_area); + + int32_t src_stride = lv_area_get_width(src_area); + + dest_buf += dest_stride * clip_area->y1 + clip_area->x1; + + src_buf += src_stride * (clip_area->y1 - src_area->y1); + src_buf += (clip_area->x1 - src_area->x1); + + lv_color_t (*blend_fp)(lv_color_t, lv_color_t, lv_opa_t); + switch(mode) { + case LV_BLEND_MODE_ADDITIVE: + blend_fp = color_blend_true_color_additive; + break; + case LV_BLEND_MODE_SUBTRACTIVE: + blend_fp = color_blend_true_color_subtractive; + break; + case LV_BLEND_MODE_MULTIPLY: + blend_fp = color_blend_true_color_multiply; + break; + default: + LV_LOG_WARN("fill_blended: unsupported blend mode"); + return; + } + + int32_t x; + int32_t y; + + /*Simple fill (maybe with opacity), no masking*/ + if(mask == NULL) { + /*The map will be indexed from `draw_area->x1` so compensate it.*/ + + for(y = 0; y < clip_h; y++) { + for(x = 0; x < clip_w; x++) { + dest_buf[x] = blend_fp(src_buf[x], dest_buf[x], opa); + } + dest_buf += dest_stride; + src_buf += src_stride; + } + } + /*Masked*/ + else { + for(y = 0; y < clip_h; y++) { + for(x = 0; x < clip_w; x++) { + if(mask[x] == 0) continue; + lv_opa_t opa_tmp = mask[x] >= LV_OPA_MAX ? opa : ((opa * mask[x]) >> 8); + dest_buf[x] = blend_fp(src_buf[x], dest_buf[x], opa_tmp); + } + dest_buf += dest_stride; + src_buf += src_stride; + mask += clip_w; + } + } +} + +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; + fg.full = LV_MIN(tmp, 1); +#else + tmp = bg.ch.red + fg.ch.red; +#if LV_COLOR_DEPTH == 8 + fg.ch.red = LV_MIN(tmp, 7); +#elif LV_COLOR_DEPTH == 16 + fg.ch.red = LV_MIN(tmp, 31); +#elif LV_COLOR_DEPTH == 32 + fg.ch.red = LV_MIN(tmp, 255); +#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 + tmp = bg.ch.green + fg.ch.green; + fg.ch.green = LV_MIN(tmp, 63); +#else + tmp = (bg.ch.green_h << 3) + bg.ch.green_l + (fg.ch.green_h << 3) + fg.ch.green_l; + tmp = LV_MIN(tmp, 63); + fg.ch.green_h = tmp >> 3; + fg.ch.green_l = tmp & 0x7; +#endif + +#elif LV_COLOR_DEPTH == 32 + tmp = bg.ch.green + fg.ch.green; + fg.ch.green = LV_MIN(tmp, 255); +#endif + + tmp = bg.ch.blue + fg.ch.blue; +#if LV_COLOR_DEPTH == 8 + fg.ch.blue = LV_MIN(tmp, 4); +#elif LV_COLOR_DEPTH == 16 + fg.ch.blue = LV_MIN(tmp, 31); +#elif LV_COLOR_DEPTH == 32 + fg.ch.blue = LV_MIN(tmp, 255); +#endif +#endif + + if(opa == LV_OPA_COVER) return fg; + + return lv_color_mix(fg, bg, opa); +} + +static inline lv_color_t color_blend_true_color_subtractive(lv_color_t fg, lv_color_t bg, lv_opa_t opa) +{ + if(opa <= LV_OPA_MIN) return bg; + + int32_t tmp; + tmp = bg.ch.red - fg.ch.red; + fg.ch.red = LV_MAX(tmp, 0); + +#if LV_COLOR_16_SWAP == 0 + tmp = bg.ch.green - fg.ch.green; + fg.ch.green = LV_MAX(tmp, 0); +#else + tmp = (bg.ch.green_h << 3) + bg.ch.green_l + (fg.ch.green_h << 3) + fg.ch.green_l; + tmp = LV_MAX(tmp, 0); + fg.ch.green_h = tmp >> 3; + fg.ch.green_l = tmp & 0x7; +#endif + + tmp = bg.ch.blue - fg.ch.blue; + fg.ch.blue = LV_MAX(tmp, 0); + + if(opa == LV_OPA_COVER) return fg; + + return lv_color_mix(fg, bg, opa); +} + +static inline lv_color_t color_blend_true_color_multiply(lv_color_t fg, lv_color_t bg, lv_opa_t opa) +{ + if(opa <= LV_OPA_MIN) return bg; + +#if LV_COLOR_DEPTH == 32 + fg.ch.red = (fg.ch.red * bg.ch.red) >> 8; + fg.ch.green = (fg.ch.green * bg.ch.green) >> 8; + fg.ch.blue = (fg.ch.blue * bg.ch.blue) >> 8; +#elif LV_COLOR_DEPTH == 16 + fg.ch.red = (fg.ch.red * bg.ch.red) >> 5; + fg.ch.blue = (fg.ch.blue * bg.ch.blue) >> 5; + LV_COLOR_SET_G(fg, (LV_COLOR_GET_G(fg) * LV_COLOR_GET_G(bg)) >> 6); +#elif LV_COLOR_DEPTH == 8 + fg.ch.red = (fg.ch.red * bg.ch.red) >> 3; + fg.ch.green = (fg.ch.green * bg.ch.green) >> 3; + fg.ch.blue = (fg.ch.blue * bg.ch.blue) >> 2; +#endif + + if(opa == LV_OPA_COVER) return fg; + + return lv_color_mix(fg, bg, opa); +} + +#endif + diff --git a/src/draw/sw/lv_draw_sw_img.c b/src/draw/sw/lv_draw_sw_img.c new file mode 100644 index 000000000..a00e8d70e --- /dev/null +++ b/src/draw/sw/lv_draw_sw_img.c @@ -0,0 +1,322 @@ +/** + * @file lv_draw_img.c + * + */ + +/********************* + * INCLUDES + *********************/ +#include "lv_draw_sw.h" +#include "../lv_img_cache.h" +#include "../lv_draw_blend.h" +#include "../../hal/lv_hal_disp.h" +#include "../../misc/lv_log.h" +#include "../../core/lv_refr.h" +#include "../../misc/lv_mem.h" +#include "../../misc/lv_math.h" + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ + +/********************** + * STATIC VARIABLES + **********************/ + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +LV_ATTRIBUTE_FAST_MEM void lv_draw_sw_img(const lv_area_t * map_area, const lv_area_t * clip_area, + const uint8_t * map_p, + const lv_draw_img_dsc_t * draw_dsc, + bool chroma_key, bool alpha_byte) +{ + /*Use the clip area as draw area*/ + lv_area_t draw_area; + lv_area_copy(&draw_area, clip_area); + + lv_disp_t * disp = _lv_refr_get_disp_refreshing(); + lv_disp_draw_buf_t * draw_buf = lv_disp_get_draw_buf(disp); + const lv_area_t * disp_area = &draw_buf->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; + + bool mask_any = lv_draw_mask_is_any(clip_area); + + /*The simplest case just copy the pixels into the draw_buf*/ + if(!mask_any && draw_dsc->angle == 0 && draw_dsc->zoom == LV_IMG_ZOOM_NONE && + chroma_key == false && alpha_byte == false && draw_dsc->recolor_opa == LV_OPA_TRANSP) { + lv_draw_blend_map(clip_area, map_area, (lv_color_t *)map_p, NULL, LV_DRAW_MASK_RES_FULL_COVER, draw_dsc->opa, + draw_dsc->blend_mode); + } + /*In the other cases every pixel need to be checked one-by-one*/ + else { + //#if LV_DRAW_COMPLEX + /*The pixel size in byte is different if an alpha byte is added too*/ + uint8_t px_size_byte = alpha_byte ? LV_IMG_PX_SIZE_ALPHA_BYTE : sizeof(lv_color_t); + + /*Go to the first displayed pixel of the map*/ + int32_t map_w = lv_area_get_width(map_area); + const uint8_t * map_buf_tmp = map_p; + map_buf_tmp += map_w * (draw_area.y1 - (map_area->y1 - disp_area->y1)) * px_size_byte; + map_buf_tmp += (draw_area.x1 - (map_area->x1 - disp_area->x1)) * px_size_byte; + + lv_color_t c; + lv_color_t chroma_keyed_color = LV_COLOR_CHROMA_KEY; + uint32_t px_i = 0; + + const uint8_t * map_px; + + lv_coord_t draw_area_h = lv_area_get_height(&draw_area); + lv_coord_t draw_area_w = lv_area_get_width(&draw_area); + + lv_area_t blend_area; + blend_area.x1 = draw_area.x1 + disp_area->x1; + blend_area.x2 = blend_area.x1 + draw_area_w - 1; + blend_area.y1 = disp_area->y1 + draw_area.y1; + blend_area.y2 = blend_area.y1; + + bool transform = draw_dsc->angle != 0 || draw_dsc->zoom != LV_IMG_ZOOM_NONE ? true : false; + /*Simple ARGB image. Handle it as special case because it's very common*/ + if(!mask_any && !transform && !chroma_key && draw_dsc->recolor_opa == LV_OPA_TRANSP && alpha_byte) { + uint32_t hor_res = (uint32_t) lv_disp_get_hor_res(disp); + uint32_t mask_buf_size = lv_area_get_size(&draw_area) > (uint32_t) hor_res ? hor_res : lv_area_get_size(&draw_area); + lv_color_t * map2 = lv_mem_buf_get(mask_buf_size * sizeof(lv_color_t)); + lv_opa_t * mask_buf = lv_mem_buf_get(mask_buf_size); + + int32_t x; + int32_t y; + for(y = 0; y < draw_area_h; y++) { + map_px = map_buf_tmp; + for(x = 0; x < draw_area_w; x++, map_px += px_size_byte, px_i++) { + lv_opa_t px_opa = map_px[LV_IMG_PX_SIZE_ALPHA_BYTE - 1]; + mask_buf[px_i] = px_opa; + if(px_opa) { +#if LV_COLOR_DEPTH == 8 || LV_COLOR_DEPTH == 1 + map2[px_i].full = map_px[0]; +#elif LV_COLOR_DEPTH == 16 + map2[px_i].full = map_px[0] + (map_px[1] << 8); +#elif LV_COLOR_DEPTH == 32 + map2[px_i].full = *((uint32_t *)map_px); +#endif + } +#if LV_COLOR_DEPTH == 32 + map2[px_i].ch.alpha = 0xFF; +#endif + } + + map_buf_tmp += map_w * px_size_byte; + if(px_i + draw_area_w < mask_buf_size) { + blend_area.y2 ++; + } + else { + lv_draw_blend_map(clip_area, &blend_area, map2, mask_buf, LV_DRAW_MASK_RES_CHANGED, draw_dsc->opa, + draw_dsc->blend_mode); + + blend_area.y1 = blend_area.y2 + 1; + blend_area.y2 = blend_area.y1; + + px_i = 0; + } + } + /*Flush the last part*/ + if(blend_area.y1 != blend_area.y2) { + blend_area.y2--; + lv_draw_blend_map(clip_area, &blend_area, map2, mask_buf, LV_DRAW_MASK_RES_CHANGED, draw_dsc->opa, + draw_dsc->blend_mode); + } + + lv_mem_buf_release(mask_buf); + lv_mem_buf_release(map2); + } + /*Most complicated case: transform or other mask or chroma keyed*/ + else { + /*Build the image and a mask line-by-line*/ + uint32_t hor_res = (uint32_t) lv_disp_get_hor_res(disp); + uint32_t mask_buf_size = lv_area_get_size(&draw_area) > hor_res ? hor_res : lv_area_get_size(&draw_area); + lv_color_t * map2 = lv_mem_buf_get(mask_buf_size * sizeof(lv_color_t)); + lv_opa_t * mask_buf = lv_mem_buf_get(mask_buf_size); + +#if LV_DRAW_COMPLEX + lv_img_transform_dsc_t trans_dsc; + lv_memset_00(&trans_dsc, sizeof(lv_img_transform_dsc_t)); + if(transform) { + lv_img_cf_t cf = LV_IMG_CF_TRUE_COLOR; + if(alpha_byte) cf = LV_IMG_CF_TRUE_COLOR_ALPHA; + else if(chroma_key) cf = LV_IMG_CF_TRUE_COLOR_CHROMA_KEYED; + + trans_dsc.cfg.angle = draw_dsc->angle; + trans_dsc.cfg.zoom = draw_dsc->zoom; + trans_dsc.cfg.src = map_p; + trans_dsc.cfg.src_w = map_w; + trans_dsc.cfg.src_h = lv_area_get_height(map_area);; + trans_dsc.cfg.cf = cf; + trans_dsc.cfg.pivot_x = draw_dsc->pivot.x; + trans_dsc.cfg.pivot_y = draw_dsc->pivot.y; + trans_dsc.cfg.color = draw_dsc->recolor; + trans_dsc.cfg.antialias = draw_dsc->antialias; + + _lv_img_buf_transform_init(&trans_dsc); + } +#endif + uint16_t recolor_premult[3] = {0}; + lv_opa_t recolor_opa_inv = 255 - draw_dsc->recolor_opa; + if(draw_dsc->recolor_opa != 0) { + lv_color_premult(draw_dsc->recolor, draw_dsc->recolor_opa, recolor_premult); + } + + lv_draw_mask_res_t mask_res; + mask_res = (alpha_byte || chroma_key || draw_dsc->angle || + draw_dsc->zoom != LV_IMG_ZOOM_NONE) ? LV_DRAW_MASK_RES_CHANGED : LV_DRAW_MASK_RES_FULL_COVER; + + /*Prepare the `mask_buf`if there are other masks*/ + if(mask_any) { + lv_memset_ff(mask_buf, mask_buf_size); + } + + int32_t x; + int32_t y; +#if LV_DRAW_COMPLEX + int32_t rot_y = disp_area->y1 + draw_area.y1 - map_area->y1; +#endif + for(y = 0; y < draw_area_h; y++) { + map_px = map_buf_tmp; +#if LV_DRAW_COMPLEX + uint32_t px_i_start = px_i; + int32_t rot_x = disp_area->x1 + draw_area.x1 - map_area->x1; +#endif + + for(x = 0; x < draw_area_w; x++, map_px += px_size_byte, px_i++) { + +#if LV_DRAW_COMPLEX + if(transform) { + + /*Transform*/ + bool ret; + ret = _lv_img_buf_transform(&trans_dsc, rot_x + x, rot_y + y); + if(ret == false) { + mask_buf[px_i] = LV_OPA_TRANSP; + continue; + } + else { + mask_buf[px_i] = trans_dsc.res.opa; + c.full = trans_dsc.res.color.full; + } + } + /*No transform*/ + else +#endif + { + if(alpha_byte) { + lv_opa_t px_opa = map_px[LV_IMG_PX_SIZE_ALPHA_BYTE - 1]; + mask_buf[px_i] = px_opa; + if(px_opa == 0) { +#if LV_COLOR_DEPTH == 32 + map2[px_i].full = 0; +#endif + continue; + } + } + else { + mask_buf[px_i] = 0xFF; + } + +#if LV_COLOR_DEPTH == 1 + c.full = map_px[0]; +#elif LV_COLOR_DEPTH == 8 + c.full = map_px[0]; +#elif LV_COLOR_DEPTH == 16 + c.full = map_px[0] + (map_px[1] << 8); +#elif LV_COLOR_DEPTH == 32 + c.full = *((uint32_t *)map_px); + c.ch.alpha = 0xFF; +#endif + if(chroma_key) { + if(c.full == chroma_keyed_color.full) { + mask_buf[px_i] = LV_OPA_TRANSP; +#if LV_COLOR_DEPTH == 32 + map2[px_i].full = 0; +#endif + continue; + } + } + + } + if(draw_dsc->recolor_opa != 0) { + c = lv_color_mix_premult(recolor_premult, c, recolor_opa_inv); + } + + map2[px_i].full = c.full; + } +#if LV_DRAW_COMPLEX + /*Apply the masks if any*/ + if(mask_any) { + lv_draw_mask_res_t mask_res_sub; + mask_res_sub = lv_draw_mask_apply(mask_buf + px_i_start, draw_area.x1 + draw_buf->area.x1, + y + draw_area.y1 + draw_buf->area.y1, + draw_area_w); + if(mask_res_sub == LV_DRAW_MASK_RES_TRANSP) { + lv_memset_00(mask_buf + px_i_start, draw_area_w); + mask_res = LV_DRAW_MASK_RES_CHANGED; + } + else if(mask_res_sub == LV_DRAW_MASK_RES_CHANGED) { + mask_res = LV_DRAW_MASK_RES_CHANGED; + } + } +#endif + + map_buf_tmp += map_w * px_size_byte; + if(px_i + draw_area_w < mask_buf_size) { + blend_area.y2 ++; + } + else { + + lv_draw_blend_map(clip_area, &blend_area, map2, mask_buf, mask_res, draw_dsc->opa, draw_dsc->blend_mode); + + blend_area.y1 = blend_area.y2 + 1; + blend_area.y2 = blend_area.y1; + + px_i = 0; + mask_res = (alpha_byte || chroma_key || draw_dsc->angle || + draw_dsc->zoom != LV_IMG_ZOOM_NONE) ? LV_DRAW_MASK_RES_CHANGED : LV_DRAW_MASK_RES_FULL_COVER; + + /*Prepare the `mask_buf`if there are other masks*/ + if(mask_any) { + lv_memset_ff(mask_buf, mask_buf_size); + } + } + } + + /*Flush the last part*/ + if(blend_area.y1 != blend_area.y2) { + blend_area.y2--; + lv_draw_blend_map(clip_area, &blend_area, map2, mask_buf, mask_res, draw_dsc->opa, draw_dsc->blend_mode); + } + + lv_mem_buf_release(mask_buf); + lv_mem_buf_release(map2); + } + } +} + +/********************** + * STATIC FUNCTIONS + **********************/ diff --git a/src/draw/sw/lv_draw_sw_letter.c b/src/draw/sw/lv_draw_sw_letter.c new file mode 100644 index 000000000..7cfd2fedd --- /dev/null +++ b/src/draw/sw/lv_draw_sw_letter.c @@ -0,0 +1,530 @@ +/** + * @file lv_draw_label.c + * + */ + +/********************* + * INCLUDES + *********************/ +#include "lv_draw_sw.h" +#include "../lv_draw_mask.h" +#include "../../hal/lv_hal_disp.h" +#include "../../misc/lv_math.h" +#include "../../misc/lv_assert.h" +#include "../../misc/lv_area.h" +#include "../../misc/lv_style.h" +#include "../../font/lv_font.h" +#include "../../core/lv_refr.h" +#include "../../draw/lv_draw_blend.h" + +/********************* + * DEFINES + *********************/ +#define MASK_BUF_MAX_SIZE 2048 + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ + +LV_ATTRIBUTE_FAST_MEM static void draw_letter_normal(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_DRAW_COMPLEX && LV_USE_FONT_SUBPX +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); +#endif /*LV_DRAW_COMPLEX && LV_USE_FONT_SUBPX*/ + +/********************** + * STATIC VARIABLES + **********************/ + +/********************** + * GLOBAL VARIABLES + **********************/ + +const uint8_t _lv_bpp1_opa_table[2] = {0, 255}; /*Opacity mapping with bpp = 1 (Just for compatibility)*/ +const uint8_t _lv_bpp2_opa_table[4] = {0, 85, 170, 255}; /*Opacity mapping with bpp = 2*/ + +const uint8_t _lv_bpp3_opa_table[8] = {0, 36, 73, 109, /*Opacity mapping with bpp = 3*/ + 146, 182, 219, 255 + }; + +const uint8_t _lv_bpp4_opa_table[16] = {0, 17, 34, 51, /*Opacity mapping with bpp = 4*/ + 68, 85, 102, 119, + 136, 153, 170, 187, + 204, 221, 238, 255 + }; + +const uint8_t _lv_bpp8_opa_table[256] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, + 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, + 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, + 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, + 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, + 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, + 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, + 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, + 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, + 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, + 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, + 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, + 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255 + }; + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +/** + * Draw a letter in the Virtual Display Buffer + * @param pos_p left-top coordinate of the latter + * @param mask_p the letter will be drawn only on this area (truncated to draw_buf area) + * @param font_p pointer to font + * @param letter a letter to draw + * @param color color of letter + * @param opa opacity of letter (0..255) + */ +LV_ATTRIBUTE_FAST_MEM void lv_draw_sw_letter(const lv_point_t * pos_p, const lv_area_t * clip_area, + const lv_font_t * font_p, + uint32_t letter, + lv_color_t color, lv_opa_t opa, lv_blend_mode_t blend_mode) +{ + if(opa < LV_OPA_MIN) return; + if(opa > LV_OPA_MAX) opa = LV_OPA_COVER; + + if(font_p == NULL) { + LV_LOG_WARN("lv_draw_letter: font is NULL"); + return; + } + + lv_font_glyph_dsc_t g; + bool g_ret = lv_font_get_glyph_dsc(font_p, &g, letter, '\0'); + if(g_ret == false) { + /*Add warning if the dsc is not found + *but do not print warning for non printable ASCII chars (e.g. '\n')*/ + if(letter >= 0x20 && + letter != 0xf8ff && /*LV_SYMBOL_DUMMY*/ + letter != 0x200c) { /*ZERO WIDTH NON-JOINER*/ + LV_LOG_WARN("lv_draw_letter: glyph dsc. not found for U+%X", (unsigned int)letter); + } + return; + } + + /*Don't draw anything if the character is empty. E.g. space*/ + if((g.box_h == 0) || (g.box_w == 0)) return; + + int32_t pos_x = pos_p->x + g.ofs_x; + int32_t pos_y = pos_p->y + (font_p->line_height - font_p->base_line) - g.box_h - g.ofs_y; + + /*If the letter is completely out of mask don't draw it*/ + if(pos_x + g.box_w < clip_area->x1 || + pos_x > clip_area->x2 || + pos_y + g.box_h < clip_area->y1 || + pos_y > clip_area->y2) { + return; + } + + const uint8_t * map_p = lv_font_get_glyph_bitmap(font_p, letter); + if(map_p == NULL) { + LV_LOG_WARN("lv_draw_letter: character's bitmap not found"); + return; + } + + if(font_p->subpx) { +#if LV_DRAW_COMPLEX && LV_USE_FONT_SUBPX + draw_letter_subpx(pos_x, pos_y, &g, clip_area, map_p, color, opa, blend_mode); +#else + LV_LOG_WARN("Can't draw sub-pixel rendered letter because LV_USE_FONT_SUBPX == 0 in lv_conf.h"); +#endif + } + else { + draw_letter_normal(pos_x, pos_y, &g, clip_area, map_p, color, opa, blend_mode); + } +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +LV_ATTRIBUTE_FAST_MEM static void draw_letter_normal(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) +{ + const uint8_t * bpp_opa_table_p; + uint32_t bitmask_init; + uint32_t bitmask; + uint32_t bpp = g->bpp; + uint32_t shades; + if(bpp == 3) bpp = 4; + + switch(bpp) { + case 1: + bpp_opa_table_p = _lv_bpp1_opa_table; + bitmask_init = 0x80; + shades = 2; + break; + case 2: + bpp_opa_table_p = _lv_bpp2_opa_table; + bitmask_init = 0xC0; + shades = 4; + break; + case 4: + bpp_opa_table_p = _lv_bpp4_opa_table; + bitmask_init = 0xF0; + shades = 16; + break; + case 8: + bpp_opa_table_p = _lv_bpp8_opa_table; + bitmask_init = 0xFF; + shades = 256; + break; /*No opa table, pixel value will be used directly*/ + default: + LV_LOG_WARN("lv_draw_letter: invalid bpp"); + return; /*Invalid bpp. Can't render the letter*/ + } + + static lv_opa_t opa_table[256]; + static lv_opa_t prev_opa = LV_OPA_TRANSP; + static uint32_t prev_bpp = 0; + if(opa < LV_OPA_MAX) { + if(prev_opa != opa || prev_bpp != bpp) { + uint32_t i; + for(i = 0; i < shades; i++) { + opa_table[i] = bpp_opa_table_p[i] == LV_OPA_COVER ? opa : ((bpp_opa_table_p[i] * opa) >> 8); + } + } + bpp_opa_table_p = opa_table; + prev_opa = opa; + prev_bpp = bpp; + } + + int32_t col, row; + int32_t box_w = g->box_w; + int32_t box_h = g->box_h; + int32_t width_bit = box_w * bpp; /*Letter width in bits*/ + + /*Calculate the col/row start/end on the map*/ + int32_t col_start = pos_x >= clip_area->x1 ? 0 : clip_area->x1 - pos_x; + int32_t col_end = pos_x + box_w <= clip_area->x2 ? box_w : clip_area->x2 - pos_x + 1; + int32_t row_start = pos_y >= clip_area->y1 ? 0 : clip_area->y1 - pos_y; + int32_t row_end = pos_y + box_h <= clip_area->y2 ? box_h : clip_area->y2 - pos_y + 1; + + /*Move on the map too*/ + uint32_t bit_ofs = (row_start * width_bit) + (col_start * bpp); + map_p += bit_ofs >> 3; + + uint8_t letter_px; + uint32_t col_bit; + col_bit = bit_ofs & 0x7; /*"& 0x7" equals to "% 8" just faster*/ + + lv_coord_t hor_res = lv_disp_get_hor_res(_lv_refr_get_disp_refreshing()); + uint32_t mask_buf_size = box_w * box_h > hor_res ? hor_res : box_w * box_h; + lv_opa_t * mask_buf = lv_mem_buf_get(mask_buf_size); + int32_t mask_p = 0; + + lv_area_t fill_area; + fill_area.x1 = col_start + pos_x; + fill_area.x2 = col_end + pos_x - 1; + fill_area.y1 = row_start + pos_y; + fill_area.y2 = fill_area.y1; +#if LV_DRAW_COMPLEX + lv_area_t mask_area; + lv_area_copy(&mask_area, &fill_area); + mask_area.y2 = mask_area.y1 + row_end; + bool mask_any = lv_draw_mask_is_any(&mask_area); +#endif + + uint32_t col_bit_max = 8 - bpp; + uint32_t col_bit_row_ofs = (box_w + col_start - col_end) * bpp; + + for(row = row_start ; row < row_end; row++) { +#if LV_DRAW_COMPLEX + int32_t mask_p_start = mask_p; +#endif + bitmask = bitmask_init >> col_bit; + for(col = col_start; col < col_end; col++) { + /*Load the pixel's opacity into the mask*/ + letter_px = (*map_p & bitmask) >> (col_bit_max - col_bit); + if(letter_px) { + mask_buf[mask_p] = bpp_opa_table_p[letter_px]; + } + else { + mask_buf[mask_p] = 0; + } + + /*Go to the next column*/ + if(col_bit < col_bit_max) { + col_bit += bpp; + bitmask = bitmask >> bpp; + } + else { + col_bit = 0; + bitmask = bitmask_init; + map_p++; + } + + /*Next mask byte*/ + mask_p++; + } + +#if LV_DRAW_COMPLEX + /*Apply masks if any*/ + if(mask_any) { + lv_draw_mask_res_t mask_res = lv_draw_mask_apply(mask_buf + mask_p_start, fill_area.x1, fill_area.y2, + lv_area_get_width(&fill_area)); + if(mask_res == LV_DRAW_MASK_RES_TRANSP) { + lv_memset_00(mask_buf + mask_p_start, lv_area_get_width(&fill_area)); + } + } +#endif + + if((uint32_t) mask_p + (col_end - col_start) < mask_buf_size) { + fill_area.y2 ++; + } + else { + lv_draw_blend_fill(clip_area, &fill_area, + color, mask_buf, LV_DRAW_MASK_RES_CHANGED, LV_OPA_COVER, + blend_mode); + + fill_area.y1 = fill_area.y2 + 1; + fill_area.y2 = fill_area.y1; + mask_p = 0; + } + + col_bit += col_bit_row_ofs; + map_p += (col_bit >> 3); + col_bit = col_bit & 0x7; + } + + /*Flush the last part*/ + if(fill_area.y1 != fill_area.y2) { + fill_area.y2--; + lv_draw_blend_fill(clip_area, &fill_area, + color, mask_buf, LV_DRAW_MASK_RES_CHANGED, LV_OPA_COVER, + blend_mode); + mask_p = 0; + } + + lv_mem_buf_release(mask_buf); +} + +#if LV_DRAW_COMPLEX && LV_USE_FONT_SUBPX +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) +{ + const uint8_t * bpp_opa_table; + uint32_t bitmask_init; + uint32_t bitmask; + uint32_t bpp = g->bpp; + if(bpp == 3) bpp = 4; + + switch(bpp) { + case 1: + bpp_opa_table = _lv_bpp1_opa_table; + bitmask_init = 0x80; + break; + case 2: + bpp_opa_table = _lv_bpp2_opa_table; + bitmask_init = 0xC0; + break; + case 4: + bpp_opa_table = _lv_bpp4_opa_table; + bitmask_init = 0xF0; + break; + case 8: + bpp_opa_table = _lv_bpp8_opa_table; + bitmask_init = 0xFF; + break; /*No opa table, pixel value will be used directly*/ + default: + LV_LOG_WARN("lv_draw_letter: invalid bpp not found"); + return; /*Invalid bpp. Can't render the letter*/ + } + + int32_t col, row; + + int32_t box_w = g->box_w; + int32_t box_h = g->box_h; + int32_t width_bit = box_w * bpp; /*Letter width in bits*/ + + /*Calculate the col/row start/end on the map*/ + int32_t col_start = pos_x >= clip_area->x1 ? 0 : (clip_area->x1 - pos_x) * 3; + int32_t col_end = pos_x + box_w / 3 <= clip_area->x2 ? box_w : (clip_area->x2 - pos_x + 1) * 3; + int32_t row_start = pos_y >= clip_area->y1 ? 0 : clip_area->y1 - pos_y; + int32_t row_end = pos_y + box_h <= clip_area->y2 ? box_h : clip_area->y2 - pos_y + 1; + + /*Move on the map too*/ + int32_t bit_ofs = (row_start * width_bit) + (col_start * bpp); + map_p += bit_ofs >> 3; + + uint8_t letter_px; + lv_opa_t px_opa; + int32_t col_bit; + col_bit = bit_ofs & 0x7; /*"& 0x7" equals to "% 8" just faster*/ + + lv_area_t map_area; + map_area.x1 = col_start / 3 + pos_x; + map_area.x2 = col_end / 3 + pos_x - 1; + map_area.y1 = row_start + pos_y; + map_area.y2 = map_area.y1; + + if(map_area.x2 <= map_area.x1) return; + + int32_t mask_buf_size = box_w * box_h > MASK_BUF_MAX_SIZE ? MASK_BUF_MAX_SIZE : g->box_w * g->box_h; + lv_opa_t * mask_buf = lv_mem_buf_get(mask_buf_size); + int32_t mask_p = 0; + + lv_color_t * color_buf = lv_mem_buf_get(mask_buf_size * sizeof(lv_color_t)); + + lv_disp_t * disp = _lv_refr_get_disp_refreshing(); + lv_disp_draw_buf_t * draw_buf = lv_disp_get_draw_buf(disp); + + int32_t disp_buf_width = lv_area_get_width(&draw_buf->area); + lv_color_t * disp_buf_buf_tmp = draw_buf->buf_act; + + /*Set a pointer on draw_buf to the first pixel of the letter*/ + disp_buf_buf_tmp += ((pos_y - draw_buf->area.y1) * disp_buf_width) + pos_x - draw_buf->area.x1; + + /*If the letter is partially out of mask the move there on draw_buf*/ + disp_buf_buf_tmp += (row_start * disp_buf_width) + col_start / 3; + + lv_area_t mask_area; + lv_area_copy(&mask_area, &map_area); + mask_area.y2 = mask_area.y1 + row_end; + bool mask_any = lv_draw_mask_is_any(&map_area); + uint8_t font_rgb[3]; + +#if LV_COLOR_16_SWAP == 0 + uint8_t txt_rgb[3] = {color.ch.red, color.ch.green, color.ch.blue}; +#else + uint8_t txt_rgb[3] = {color.ch.red, (color.ch.green_h << 3) + color.ch.green_l, color.ch.blue}; +#endif + + for(row = row_start ; row < row_end; row++) { + uint32_t subpx_cnt = 0; + bitmask = bitmask_init >> col_bit; + int32_t mask_p_start = mask_p; + + for(col = col_start; col < col_end; col++) { + /*Load the pixel's opacity into the mask*/ + letter_px = (*map_p & bitmask) >> (8 - col_bit - bpp); + if(letter_px != 0) { + if(opa == LV_OPA_COVER) { + px_opa = bpp == 8 ? letter_px : bpp_opa_table[letter_px]; + } + else { + px_opa = bpp == 8 ? (uint32_t)((uint32_t)letter_px * opa) >> 8 + : (uint32_t)((uint32_t)bpp_opa_table[letter_px] * opa) >> 8; + } + } + else { + px_opa = 0; + } + + font_rgb[subpx_cnt] = px_opa; + + subpx_cnt ++; + if(subpx_cnt == 3) { + subpx_cnt = 0; + + lv_color_t res_color; +#if LV_COLOR_16_SWAP == 0 + uint8_t bg_rgb[3] = {disp_buf_buf_tmp->ch.red, disp_buf_buf_tmp->ch.green, disp_buf_buf_tmp->ch.blue}; +#else + uint8_t bg_rgb[3] = {disp_buf_buf_tmp->ch.red, + (disp_buf_buf_tmp->ch.green_h << 3) + disp_buf_buf_tmp->ch.green_l, + disp_buf_buf_tmp->ch.blue + }; +#endif + +#if LV_FONT_SUBPX_BGR + res_color.ch.blue = (uint32_t)((uint32_t)txt_rgb[0] * font_rgb[0] + (bg_rgb[0] * (255 - font_rgb[0]))) >> 8; + res_color.ch.red = (uint32_t)((uint32_t)txt_rgb[2] * font_rgb[2] + (bg_rgb[2] * (255 - font_rgb[2]))) >> 8; +#else + res_color.ch.red = (uint32_t)((uint16_t)txt_rgb[0] * font_rgb[0] + (bg_rgb[0] * (255 - font_rgb[0]))) >> 8; + res_color.ch.blue = (uint32_t)((uint16_t)txt_rgb[2] * font_rgb[2] + (bg_rgb[2] * (255 - font_rgb[2]))) >> 8; +#endif + +#if LV_COLOR_16_SWAP == 0 + res_color.ch.green = (uint32_t)((uint32_t)txt_rgb[1] * font_rgb[1] + (bg_rgb[1] * (255 - font_rgb[1]))) >> 8; +#else + uint8_t green = (uint32_t)((uint32_t)txt_rgb[1] * font_rgb[1] + (bg_rgb[1] * (255 - font_rgb[1]))) >> 8; + res_color.ch.green_h = green >> 3; + res_color.ch.green_l = green & 0x7; +#endif + +#if LV_COLOR_DEPTH == 32 + res_color.ch.alpha = 0xff; +#endif + + if(font_rgb[0] == 0 && font_rgb[1] == 0 && font_rgb[2] == 0) mask_buf[mask_p] = LV_OPA_TRANSP; + else mask_buf[mask_p] = LV_OPA_COVER; + color_buf[mask_p] = res_color; + + /*Next mask byte*/ + mask_p++; + disp_buf_buf_tmp++; + } + + /*Go to the next column*/ + if(col_bit < (int32_t)(8 - bpp)) { + col_bit += bpp; + bitmask = bitmask >> bpp; + } + else { + col_bit = 0; + bitmask = bitmask_init; + map_p++; + } + } + + /*Apply masks if any*/ + if(mask_any) { + lv_draw_mask_res_t mask_res = lv_draw_mask_apply(mask_buf + mask_p_start, map_area.x1, map_area.y2, + lv_area_get_width(&map_area)); + if(mask_res == LV_DRAW_MASK_RES_TRANSP) { + lv_memset_00(mask_buf + mask_p_start, lv_area_get_width(&map_area)); + } + } + + if((int32_t) mask_p + (col_end - col_start) < mask_buf_size) { + map_area.y2 ++; + } + else { + lv_draw_blend_map(clip_area, &map_area, color_buf, mask_buf, LV_DRAW_MASK_RES_CHANGED, opa, blend_mode); + + map_area.y1 = map_area.y2 + 1; + map_area.y2 = map_area.y1; + mask_p = 0; + } + + col_bit += ((box_w - col_end) + col_start) * bpp; + + map_p += (col_bit >> 3); + col_bit = col_bit & 0x7; + + /*Next row in draw_buf*/ + disp_buf_buf_tmp += disp_buf_width - (col_end - col_start) / 3; + } + + /*Flush the last part*/ + if(map_area.y1 != map_area.y2) { + map_area.y2--; + lv_draw_blend_map(clip_area, &map_area, color_buf, mask_buf, LV_DRAW_MASK_RES_CHANGED, opa, blend_mode); + } + + lv_mem_buf_release(mask_buf); + lv_mem_buf_release(color_buf); +} +#endif /*LV_DRAW_COMPLEX && LV_USE_FONT_SUBPX*/ + diff --git a/src/draw/sw/lv_draw_sw_line.c b/src/draw/sw/lv_draw_sw_line.c new file mode 100644 index 000000000..7173501c0 --- /dev/null +++ b/src/draw/sw/lv_draw_sw_line.c @@ -0,0 +1,485 @@ +/** + * @file lv_draw_line.c + * + */ + +/********************* + * INCLUDES + *********************/ +#include +#include "lv_draw_sw.h" +#include "../../misc/lv_math.h" +#include "../../core/lv_refr.h" + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ + +LV_ATTRIBUTE_FAST_MEM static void draw_line_skew(const lv_point_t * point1, const lv_point_t * point2, + const lv_area_t * clip, + const lv_draw_line_dsc_t * dsc); + +LV_ATTRIBUTE_FAST_MEM static void draw_line_hor(const lv_point_t * point1, const lv_point_t * point2, + const lv_area_t * clip, + const lv_draw_line_dsc_t * dsc); +LV_ATTRIBUTE_FAST_MEM static void draw_line_ver(const lv_point_t * point1, const lv_point_t * point2, + const lv_area_t * clip, + const lv_draw_line_dsc_t * dsc); +/********************** + * STATIC VARIABLES + **********************/ + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +/** + * Draw a line + * @param point1 first point of the line + * @param point2 second point of the line + * @param clip the line will be drawn only in this area + * @param dsc pointer to an initialized `lv_draw_line_dsc_t` variable + */ +LV_ATTRIBUTE_FAST_MEM void lv_draw_sw_line(const lv_point_t * point1, const lv_point_t * point2, const lv_area_t * clip, + const lv_draw_line_dsc_t * dsc) +{ + if(dsc->width == 0) return; + if(dsc->opa <= LV_OPA_MIN) return; + + if(point1->x == point2->x && point1->y == point2->y) return; + + lv_area_t clip_line; + clip_line.x1 = LV_MIN(point1->x, point2->x) - dsc->width / 2; + clip_line.x2 = LV_MAX(point1->x, point2->x) + dsc->width / 2; + clip_line.y1 = LV_MIN(point1->y, point2->y) - dsc->width / 2; + clip_line.y2 = LV_MAX(point1->y, point2->y) + dsc->width / 2; + + bool is_common; + is_common = _lv_area_intersect(&clip_line, &clip_line, clip); + if(!is_common) return; + + if(point1->y == point2->y) draw_line_hor(point1, point2, &clip_line, dsc); + else if(point1->x == point2->x) draw_line_ver(point1, point2, &clip_line, dsc); + else draw_line_skew(point1, point2, &clip_line, dsc); + + if(dsc->round_end || dsc->round_start) { + lv_draw_rect_dsc_t cir_dsc; + lv_draw_rect_dsc_init(&cir_dsc); + cir_dsc.bg_color = dsc->color; + cir_dsc.radius = LV_RADIUS_CIRCLE; + cir_dsc.bg_opa = dsc->opa; + + int32_t r = (dsc->width >> 1); + int32_t r_corr = (dsc->width & 1) ? 0 : 1; + lv_area_t cir_area; + + if(dsc->round_start) { + cir_area.x1 = point1->x - r; + cir_area.y1 = point1->y - r; + cir_area.x2 = point1->x + r - r_corr; + cir_area.y2 = point1->y + r - r_corr ; + lv_draw_rect(&cir_area, clip, &cir_dsc); + } + + if(dsc->round_end) { + cir_area.x1 = point2->x - r; + cir_area.y1 = point2->y - r; + cir_area.x2 = point2->x + r - r_corr; + cir_area.y2 = point2->y + r - r_corr ; + lv_draw_rect(&cir_area, clip, &cir_dsc); + } + } +} + +/********************** + * STATIC FUNCTIONS + **********************/ + + +LV_ATTRIBUTE_FAST_MEM static void draw_line_hor(const lv_point_t * point1, const lv_point_t * point2, + const lv_area_t * clip, + const lv_draw_line_dsc_t * dsc) +{ + lv_opa_t opa = dsc->opa; + + int32_t w = dsc->width - 1; + int32_t w_half0 = w >> 1; + int32_t w_half1 = w_half0 + (w & 0x1); /*Compensate rounding error*/ + + lv_area_t draw_area; + draw_area.x1 = LV_MIN(point1->x, point2->x); + draw_area.x2 = LV_MAX(point1->x, point2->x) - 1; + draw_area.y1 = point1->y - w_half1; + draw_area.y2 = point1->y + w_half0; + + bool dashed = dsc->dash_gap && dsc->dash_width ? true : false; + bool simple_mode = true; + if(lv_draw_mask_is_any(&draw_area)) simple_mode = false; + else if(dashed) simple_mode = false; + + + /*If there is no mask then simply draw a rectangle*/ + if(simple_mode) { + lv_draw_blend_fill(clip, &draw_area, + dsc->color, NULL, LV_DRAW_MASK_RES_FULL_COVER, opa, + dsc->blend_mode); + } +#if LV_DRAW_COMPLEX + /*If there other mask apply it*/ + else { + lv_disp_t * disp = _lv_refr_get_disp_refreshing(); + lv_disp_draw_buf_t * draw_buf = lv_disp_get_draw_buf(disp); + const lv_area_t * disp_area = &draw_buf->area; + /*Get clipped fill area which is the real draw area. + *It is always the same or inside `fill_area`*/ + bool is_common; + is_common = _lv_area_intersect(&draw_area, clip, &draw_area); + if(!is_common) return; + + /*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); + + lv_area_t fill_area; + fill_area.x1 = draw_area.x1 + disp_area->x1; + fill_area.x2 = draw_area.x2 + disp_area->x1; + fill_area.y1 = draw_area.y1 + disp_area->y1; + fill_area.y2 = fill_area.y1; + + lv_coord_t dash_start = 0; + if(dashed) { + dash_start = (draw_buf->area.x1 + draw_area.x1) % (dsc->dash_gap + dsc->dash_width); + } + + lv_opa_t * mask_buf = lv_mem_buf_get(draw_area_w); + int32_t h; + for(h = draw_area.y1; h <= draw_area.y2; h++) { + lv_memset_ff(mask_buf, draw_area_w); + lv_draw_mask_res_t mask_res = lv_draw_mask_apply(mask_buf, draw_buf->area.x1 + draw_area.x1, draw_buf->area.y1 + h, + draw_area_w); + + if(dashed) { + if(mask_res != LV_DRAW_MASK_RES_TRANSP) { + lv_coord_t dash_cnt = dash_start; + lv_coord_t i; + for(i = 0; i < draw_area_w; i++, dash_cnt++) { + if(dash_cnt <= dsc->dash_width) { + int16_t diff = dsc->dash_width - dash_cnt; + i += diff; + dash_cnt += diff; + } + else if(dash_cnt >= dsc->dash_gap + dsc->dash_width) { + dash_cnt = 0; + } + else { + mask_buf[i] = 0x00; + } + } + + mask_res = LV_DRAW_MASK_RES_CHANGED; + } + } + + lv_draw_blend_fill(clip, &fill_area, + dsc->color, mask_buf, mask_res, dsc->opa, + dsc->blend_mode); + + fill_area.y1++; + fill_area.y2++; + } + lv_mem_buf_release(mask_buf); + } +#endif /*LV_DRAW_COMPLEX*/ +} + +LV_ATTRIBUTE_FAST_MEM static void draw_line_ver(const lv_point_t * point1, const lv_point_t * point2, + const lv_area_t * clip, + const lv_draw_line_dsc_t * dsc) +{ + lv_opa_t opa = dsc->opa; + + int32_t w = dsc->width - 1; + int32_t w_half0 = w >> 1; + int32_t w_half1 = w_half0 + (w & 0x1); /*Compensate rounding error*/ + + lv_area_t draw_area; + draw_area.x1 = point1->x - w_half1; + draw_area.x2 = point1->x + w_half0; + draw_area.y1 = LV_MIN(point1->y, point2->y); + draw_area.y2 = LV_MAX(point1->y, point2->y) - 1; + + bool dashed = dsc->dash_gap && dsc->dash_width ? true : false; + bool simple_mode = true; + if(lv_draw_mask_is_any(&draw_area)) simple_mode = false; + else if(dashed) simple_mode = false; + + /*If there is no mask then simply draw a rectangle*/ + if(simple_mode) { + lv_draw_blend_fill(clip, &draw_area, + dsc->color, NULL, LV_DRAW_MASK_RES_FULL_COVER, opa, + dsc->blend_mode); + } + +#if LV_DRAW_COMPLEX + /*If there other mask apply it*/ + else { + lv_disp_t * disp = _lv_refr_get_disp_refreshing(); + lv_disp_draw_buf_t * draw_buf = lv_disp_get_draw_buf(disp); + const lv_area_t * disp_area = &draw_buf->area; + /*Get clipped fill area which is the real draw area. + *It is always the same or inside `fill_area`*/ + bool is_common; + is_common = _lv_area_intersect(&draw_area, clip, &draw_area); + if(!is_common) return; + + /*Now `draw_area` has absolute coordinates. + *Make it relative to `disp_area` to simplify draw to `disp_buf`*/ + draw_area.x1 -= draw_buf->area.x1; + draw_area.y1 -= draw_buf->area.y1; + draw_area.x2 -= draw_buf->area.x1; + draw_area.y2 -= draw_buf->area.y1; + + int32_t draw_area_w = lv_area_get_width(&draw_area); + + lv_area_t fill_area; + fill_area.x1 = draw_area.x1 + disp_area->x1; + fill_area.x2 = draw_area.x2 + disp_area->x1; + fill_area.y1 = draw_area.y1 + disp_area->y1; + fill_area.y2 = fill_area.y1; + + lv_opa_t * mask_buf = lv_mem_buf_get(draw_area_w); + + lv_coord_t dash_start = 0; + if(dashed) { + dash_start = (draw_buf->area.y1 + draw_area.y1) % (dsc->dash_gap + dsc->dash_width); + } + + lv_coord_t dash_cnt = dash_start; + + int32_t h; + for(h = draw_area.y1; h <= draw_area.y2; h++) { + lv_memset_ff(mask_buf, draw_area_w); + lv_draw_mask_res_t mask_res = lv_draw_mask_apply(mask_buf, draw_buf->area.x1 + draw_area.x1, draw_buf->area.y1 + h, + draw_area_w); + + if(dashed) { + if(mask_res != LV_DRAW_MASK_RES_TRANSP) { + if(dash_cnt > dsc->dash_width) { + mask_res = LV_DRAW_MASK_RES_TRANSP; + } + + if(dash_cnt >= dsc->dash_gap + dsc->dash_width) { + dash_cnt = 0; + } + } + dash_cnt ++; + } + + lv_draw_blend_fill(clip, &fill_area, + dsc->color, mask_buf, mask_res, dsc->opa, + LV_BLEND_MODE_NORMAL); + + fill_area.y1++; + fill_area.y2++; + } + lv_mem_buf_release(mask_buf); + } +#endif /*LV_DRAW_COMPLEX*/ +} + +LV_ATTRIBUTE_FAST_MEM static void draw_line_skew(const lv_point_t * point1, const lv_point_t * point2, + const lv_area_t * clip, + const lv_draw_line_dsc_t * dsc) +{ +#if LV_DRAW_COMPLEX + /*Keep the great y in p1*/ + lv_point_t p1; + lv_point_t p2; + if(point1->y < point2->y) { + p1.y = point1->y; + p2.y = point2->y; + p1.x = point1->x; + p2.x = point2->x; + } + else { + p1.y = point2->y; + p2.y = point1->y; + p1.x = point2->x; + p2.x = point1->x; + } + + int32_t xdiff = p2.x - p1.x; + int32_t ydiff = p2.y - p1.y; + bool flat = LV_ABS(xdiff) > LV_ABS(ydiff) ? true : false; + + static const uint8_t wcorr[] = { + 128, 128, 128, 129, 129, 130, 130, 131, + 132, 133, 134, 135, 137, 138, 140, 141, + 143, 145, 147, 149, 151, 153, 155, 158, + 160, 162, 165, 167, 170, 173, 175, 178, + 181, + }; + + int32_t w = dsc->width; + int32_t wcorr_i = 0; + if(flat) wcorr_i = (LV_ABS(ydiff) << 5) / LV_ABS(xdiff); + else wcorr_i = (LV_ABS(xdiff) << 5) / LV_ABS(ydiff); + + w = (w * wcorr[wcorr_i] + 63) >> 7; /*+ 63 for rounding*/ + int32_t w_half0 = w >> 1; + int32_t w_half1 = w_half0 + (w & 0x1); /*Compensate rounding error*/ + + lv_area_t draw_area; + draw_area.x1 = LV_MIN(p1.x, p2.x) - w; + draw_area.x2 = LV_MAX(p1.x, p2.x) + w; + draw_area.y1 = LV_MIN(p1.y, p2.y) - w; + draw_area.y2 = LV_MAX(p1.y, p2.y) + w; + + /*Get the union of `coords` and `clip`*/ + /*`clip` is already truncated to the `draw_buf` size + *in 'lv_refr_area' function*/ + bool is_common = _lv_area_intersect(&draw_area, &draw_area, clip); + if(is_common == false) return; + + lv_draw_mask_line_param_t mask_left_param; + lv_draw_mask_line_param_t mask_right_param; + lv_draw_mask_line_param_t mask_top_param; + lv_draw_mask_line_param_t mask_bottom_param; + + if(flat) { + if(xdiff > 0) { + lv_draw_mask_line_points_init(&mask_left_param, p1.x, p1.y - w_half0, p2.x, p2.y - w_half0, + LV_DRAW_MASK_LINE_SIDE_LEFT); + lv_draw_mask_line_points_init(&mask_right_param, p1.x, p1.y + w_half1, p2.x, p2.y + w_half1, + LV_DRAW_MASK_LINE_SIDE_RIGHT); + } + else { + lv_draw_mask_line_points_init(&mask_left_param, p1.x, p1.y + w_half1, p2.x, p2.y + w_half1, + LV_DRAW_MASK_LINE_SIDE_LEFT); + lv_draw_mask_line_points_init(&mask_right_param, p1.x, p1.y - w_half0, p2.x, p2.y - w_half0, + LV_DRAW_MASK_LINE_SIDE_RIGHT); + } + } + else { + lv_draw_mask_line_points_init(&mask_left_param, p1.x + w_half1, p1.y, p2.x + w_half1, p2.y, + LV_DRAW_MASK_LINE_SIDE_LEFT); + lv_draw_mask_line_points_init(&mask_right_param, p1.x - w_half0, p1.y, p2.x - w_half0, p2.y, + LV_DRAW_MASK_LINE_SIDE_RIGHT); + } + + /*Use the normal vector for the endings*/ + + int16_t mask_left_id = lv_draw_mask_add(&mask_left_param, NULL); + int16_t mask_right_id = lv_draw_mask_add(&mask_right_param, NULL); + int16_t mask_top_id = LV_MASK_ID_INV; + int16_t mask_bottom_id = LV_MASK_ID_INV; + + if(!dsc->raw_end) { + lv_draw_mask_line_points_init(&mask_top_param, p1.x, p1.y, p1.x - ydiff, p1.y + xdiff, LV_DRAW_MASK_LINE_SIDE_BOTTOM); + lv_draw_mask_line_points_init(&mask_bottom_param, p2.x, p2.y, p2.x - ydiff, p2.y + xdiff, LV_DRAW_MASK_LINE_SIDE_TOP); + mask_top_id = lv_draw_mask_add(&mask_top_param, NULL); + mask_bottom_id = lv_draw_mask_add(&mask_bottom_param, NULL); + } + + lv_disp_t * disp = _lv_refr_get_disp_refreshing(); + lv_disp_draw_buf_t * draw_buf = lv_disp_get_draw_buf(disp); + + const lv_area_t * disp_area = &draw_buf->area; + + /*Store the coordinates of the `draw_a` relative to the draw_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; + + /*The real draw area is around the line. + *It's easy to calculate with steep lines, but the area can be very wide with very flat lines. + *So deal with it only with steep lines.*/ + int32_t draw_area_w = lv_area_get_width(&draw_area); + + /*Draw the background line by line*/ + int32_t h; + uint32_t hor_res = (uint32_t)lv_disp_get_hor_res(disp); + size_t mask_buf_size = LV_MIN(lv_area_get_size(&draw_area), hor_res); + lv_opa_t * mask_buf = lv_mem_buf_get(mask_buf_size); + + lv_area_t fill_area; + fill_area.x1 = draw_area.x1 + disp_area->x1; + fill_area.x2 = draw_area.x2 + disp_area->x1; + fill_area.y1 = draw_area.y1 + disp_area->y1; + fill_area.y2 = fill_area.y1; + + int32_t x = draw_buf->area.x1 + draw_area.x1; + + uint32_t mask_p = 0; + + lv_memset_ff(mask_buf, mask_buf_size); + /*Fill the first row with 'color'*/ + for(h = draw_area.y1 + disp_area->y1; h <= draw_area.y2 + disp_area->y1; h++) { + + lv_draw_mask_res_t mask_res = lv_draw_mask_apply(&mask_buf[mask_p], x, h, draw_area_w); + if(mask_res == LV_DRAW_MASK_RES_TRANSP) { + lv_memset_00(&mask_buf[mask_p], draw_area_w); + } + + mask_p += draw_area_w; + if((uint32_t) mask_p + draw_area_w < mask_buf_size) { + fill_area.y2 ++; + } + else { + lv_draw_blend_fill(&fill_area, clip, + dsc->color, mask_buf, LV_DRAW_MASK_RES_CHANGED, dsc->opa, + dsc->blend_mode); + + fill_area.y1 = fill_area.y2 + 1; + fill_area.y2 = fill_area.y1; + mask_p = 0; + lv_memset_ff(mask_buf, mask_buf_size); + } + } + + /*Flush the last part*/ + if(fill_area.y1 != fill_area.y2) { + fill_area.y2--; + lv_draw_blend_fill(&fill_area, clip, + dsc->color, mask_buf, LV_DRAW_MASK_RES_CHANGED, dsc->opa, + dsc->blend_mode); + + } + + 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); + lv_draw_mask_remove_id(mask_bottom_id); +#else + LV_UNUSED(point1); + LV_UNUSED(point2); + LV_UNUSED(clip); + LV_UNUSED(dsc); + LV_LOG_WARN("Can't draw skewed line with LV_DRAW_COMPLEX == 0"); +#endif /*LV_DRAW_COMPLEX*/ +} + diff --git a/src/draw/lv_draw_mask.c b/src/draw/sw/lv_draw_sw_mask.c similarity index 99% rename from src/draw/lv_draw_mask.c rename to src/draw/sw/lv_draw_sw_mask.c index 17f55a898..61ae520a0 100644 --- a/src/draw/lv_draw_mask.c +++ b/src/draw/sw/lv_draw_sw_mask.c @@ -6,12 +6,12 @@ /********************* * INCLUDES *********************/ -#include "lv_draw_mask.h" +#include "lv_draw_sw.h" #if LV_DRAW_COMPLEX -#include "../misc/lv_math.h" -#include "../misc/lv_log.h" -#include "../misc/lv_assert.h" -#include "../misc/lv_gc.h" +#include "../../misc/lv_math.h" +#include "../../misc/lv_log.h" +#include "../../misc/lv_assert.h" +#include "../../misc/lv_gc.h" /********************* * DEFINES diff --git a/src/draw/sw/lv_draw_sw_polygon.c b/src/draw/sw/lv_draw_sw_polygon.c new file mode 100644 index 000000000..696ed45d1 --- /dev/null +++ b/src/draw/sw/lv_draw_sw_polygon.c @@ -0,0 +1,202 @@ +/** + * @file lv_draw_polygon.c + * + */ + +/********************* + * INCLUDES + *********************/ +#include "lv_draw_sw.h" +#include "../../misc/lv_math.h" +#include "../../misc/lv_mem.h" +#include "../../misc/lv_area.h" +#include "../../misc/lv_color.h" +#include "../lv_draw_rect.h" +#include "../lv_draw_mask.h" + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ + +/********************** + * STATIC VARIABLES + **********************/ + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +/** + * Draw a polygon. Only convex polygons are supported + * @param points an array of points + * @param point_cnt number of points + * @param clip_area polygon will be drawn only in this area + * @param draw_dsc pointer to an initialized `lv_draw_rect_dsc_t` variable + */ +void lv_draw_sw_polygon(const lv_point_t points[], uint16_t point_cnt, const lv_area_t * clip_area, + const lv_draw_rect_dsc_t * draw_dsc) +{ +#if LV_DRAW_COMPLEX + if(point_cnt < 3) return; + if(points == NULL) return; + + /*Join adjacent points if they are on the same coordinate*/ + lv_point_t * p = lv_mem_buf_get(point_cnt * sizeof(lv_point_t)); + if(p == NULL) return; + uint16_t i; + uint16_t pcnt = 0; + p[0] = points[0]; + for(i = 0; i < point_cnt - 1; i++) { + if(points[i].x != points[i + 1].x || points[i].y != points[i + 1].y) { + p[pcnt] = points[i]; + pcnt++; + } + } + /*The first and the last points are also adjacent*/ + if(points[0].x != points[point_cnt - 1].x || points[0].y != points[point_cnt - 1].y) { + p[pcnt] = points[point_cnt - 1]; + pcnt++; + } + + point_cnt = pcnt; + if(point_cnt < 3) { + lv_mem_buf_release(p); + return; + } + + lv_area_t poly_coords = {.x1 = LV_COORD_MAX, .y1 = LV_COORD_MAX, .x2 = LV_COORD_MIN, .y2 = LV_COORD_MIN}; + + for(i = 0; i < point_cnt; i++) { + poly_coords.x1 = LV_MIN(poly_coords.x1, p[i].x); + poly_coords.y1 = LV_MIN(poly_coords.y1, p[i].y); + poly_coords.x2 = LV_MAX(poly_coords.x2, p[i].x); + poly_coords.y2 = LV_MAX(poly_coords.y2, p[i].y); + } + + bool is_common; + lv_area_t poly_mask; + is_common = _lv_area_intersect(&poly_mask, &poly_coords, clip_area); + if(!is_common) { + lv_mem_buf_release(p); + return; + } + /*Find the lowest point*/ + lv_coord_t y_min = p[0].y; + int16_t y_min_i = 0; + + for(i = 1; i < point_cnt; i++) { + if(p[i].y < y_min) { + y_min = p[i].y; + y_min_i = i; + } + } + + lv_draw_mask_line_param_t * mp = lv_mem_buf_get(sizeof(lv_draw_mask_line_param_t) * point_cnt); + lv_draw_mask_line_param_t * mp_next = mp; + + int32_t i_prev_left = y_min_i; + int32_t i_prev_right = y_min_i; + int32_t i_next_left; + int32_t i_next_right; + uint32_t mask_cnt = 0; + + /*Get the index of the left and right points*/ + i_next_left = y_min_i - 1; + if(i_next_left < 0) i_next_left = point_cnt + i_next_left; + + i_next_right = y_min_i + 1; + if(i_next_right > point_cnt - 1) i_next_right = 0; + + /** + * Check if the order of points is inverted or not. + * The normal case is when the left point is on `y_min_i - 1` + * Explanation: + * if angle(p_left) < angle(p_right) -> inverted + * dy_left/dx_left < dy_right/dx_right + * dy_left * dx_right < dy_right * dx_left + */ + lv_coord_t dxl = p[i_next_left].x - p[y_min_i].x; + lv_coord_t dxr = p[i_next_right].x - p[y_min_i].x; + lv_coord_t dyl = p[i_next_left].y - p[y_min_i].y; + lv_coord_t dyr = p[i_next_right].y - p[y_min_i].y; + + bool inv = false; + if(dyl * dxr < dyr * dxl) inv = true; + + do { + if(!inv) { + i_next_left = i_prev_left - 1; + if(i_next_left < 0) i_next_left = point_cnt + i_next_left; + + i_next_right = i_prev_right + 1; + if(i_next_right > point_cnt - 1) i_next_right = 0; + } + else { + i_next_left = i_prev_left + 1; + if(i_next_left > point_cnt - 1) i_next_left = 0; + + i_next_right = i_prev_right - 1; + if(i_next_right < 0) i_next_right = point_cnt + i_next_right; + } + + if(p[i_next_left].y >= p[i_prev_left].y) { + if(p[i_next_left].y != p[i_prev_left].y && + p[i_next_left].x != p[i_prev_left].x) { + lv_draw_mask_line_points_init(mp_next, p[i_prev_left].x, p[i_prev_left].y, + p[i_next_left].x, p[i_next_left].y, + LV_DRAW_MASK_LINE_SIDE_RIGHT); + lv_draw_mask_add(mp_next, mp); + mp_next++; + } + mask_cnt++; + i_prev_left = i_next_left; + } + + if(mask_cnt == point_cnt) break; + + if(p[i_next_right].y >= p[i_prev_right].y) { + if(p[i_next_right].y != p[i_prev_right].y && + p[i_next_right].x != p[i_prev_right].x) { + + lv_draw_mask_line_points_init(mp_next, p[i_prev_right].x, p[i_prev_right].y, + p[i_next_right].x, p[i_next_right].y, + LV_DRAW_MASK_LINE_SIDE_LEFT); + lv_draw_mask_add(mp_next, mp); + mp_next++; + } + mask_cnt++; + i_prev_right = i_next_right; + } + + } while(mask_cnt < point_cnt); + + lv_draw_rect(&poly_coords, clip_area, draw_dsc); + + lv_draw_mask_remove_custom(mp); + + lv_mem_buf_release(mp); + lv_mem_buf_release(p); +#else + LV_UNUSED(points); + LV_UNUSED(point_cnt); + LV_UNUSED(clip_area); + LV_UNUSED(draw_dsc); + LV_LOG_WARN("Can't draw polygon with LV_DRAW_COMPLEX == 0"); +#endif /*LV_DRAW_COMPLEX*/ +} + +/********************** + * STATIC FUNCTIONS + **********************/ diff --git a/src/draw/sw/lv_draw_sw_rect.c b/src/draw/sw/lv_draw_sw_rect.c new file mode 100644 index 000000000..8188ce710 --- /dev/null +++ b/src/draw/sw/lv_draw_sw_rect.c @@ -0,0 +1,1331 @@ +/** + * @file lv_draw_rect.c + * + */ + +/********************* + * INCLUDES + *********************/ +#include "lv_draw_sw.h" +#include "../../misc/lv_math.h" +#include "../../misc/lv_txt_ap.h" +#include "../../core/lv_refr.h" +#include "../../misc/lv_assert.h" + +/********************* + * DEFINES + *********************/ +#define SHADOW_UPSCALE_SHIFT 6 +#define SHADOW_ENHANCE 1 +#define SPLIT_LIMIT 50 + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ +#if LV_USE_EXTERNAL_RENDERER == 0 +LV_ATTRIBUTE_FAST_MEM static void draw_bg(const lv_area_t * coords, const lv_area_t * clip_area, + const lv_draw_rect_dsc_t * dsc); +LV_ATTRIBUTE_FAST_MEM static void draw_bg_img(const lv_area_t * coords, const lv_area_t * clip, + const lv_draw_rect_dsc_t * dsc); +LV_ATTRIBUTE_FAST_MEM static void draw_border(const lv_area_t * coords, const lv_area_t * clip, + const lv_draw_rect_dsc_t * dsc); + +static void draw_outline(const lv_area_t * coords, const lv_area_t * clip, const lv_draw_rect_dsc_t * dsc); + +#if LV_DRAW_COMPLEX +LV_ATTRIBUTE_FAST_MEM static void draw_shadow(const lv_area_t * coords, const lv_area_t * clip, + const lv_draw_rect_dsc_t * dsc); +LV_ATTRIBUTE_FAST_MEM static void shadow_draw_corner_buf(const lv_area_t * coords, uint16_t * sh_buf, lv_coord_t s, + lv_coord_t r); +LV_ATTRIBUTE_FAST_MEM static void shadow_blur_corner(lv_coord_t size, lv_coord_t sw, uint16_t * sh_ups_buf); +#endif + +void draw_border_generic(const lv_area_t * clip_area, const lv_area_t * outer_area, const lv_area_t * inner_area, + lv_coord_t rout, lv_coord_t rin, lv_color_t color, lv_opa_t opa, lv_blend_mode_t blend_mode); + +static void draw_border_simple(const lv_area_t * clip, const lv_area_t * outer_area, const lv_area_t * inner_area, + lv_color_t color, lv_opa_t opa); + +#if LV_DRAW_COMPLEX + LV_ATTRIBUTE_FAST_MEM static inline lv_color_t grad_get(const lv_draw_rect_dsc_t * dsc, lv_coord_t s, lv_coord_t i); +#endif +#endif + +/********************** + * STATIC VARIABLES + **********************/ +#if defined(LV_SHADOW_CACHE_SIZE) && LV_SHADOW_CACHE_SIZE > 0 + static uint8_t sh_cache[LV_SHADOW_CACHE_SIZE * LV_SHADOW_CACHE_SIZE]; + static int32_t sh_cache_size = -1; + static int32_t sh_cache_r = -1; +#endif + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +/** + * Draw a rectangle + * @param coords the coordinates of the rectangle + * @param mask the rectangle will be drawn only in this mask + * @param dsc pointer to an initialized `lv_draw_rect_dsc_t` variable + */ +void lv_draw_sw_rect(const lv_area_t * coords, const lv_area_t * clip, const lv_draw_rect_dsc_t * dsc) +{ + if(lv_area_get_height(coords) < 1 || lv_area_get_width(coords) < 1) return; +#if LV_DRAW_COMPLEX + draw_shadow(coords, clip, dsc); +#endif + + draw_bg(coords, clip, dsc); + draw_bg_img(coords, clip, dsc); + + draw_border(coords, clip, dsc); + + draw_outline(coords, clip, dsc); + + LV_ASSERT_MEM_INTEGRITY(); +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +LV_ATTRIBUTE_FAST_MEM static void draw_bg(const lv_area_t * coords, const lv_area_t * clip_area, + const lv_draw_rect_dsc_t * dsc) +{ + if(dsc->bg_opa <= LV_OPA_MIN) return; + + lv_area_t coords_bg; + lv_area_copy(&coords_bg, coords); + + /*If the border fully covers make the bg area 1px smaller to avoid artifacts on the corners*/ + if(dsc->border_width > 1 && dsc->border_opa >= LV_OPA_MAX && dsc->radius != 0) { + coords_bg.x1 += (dsc->border_side & LV_BORDER_SIDE_LEFT) ? 1 : 0; + coords_bg.y1 += (dsc->border_side & LV_BORDER_SIDE_TOP) ? 1 : 0; + coords_bg.x2 -= (dsc->border_side & LV_BORDER_SIDE_RIGHT) ? 1 : 0; + coords_bg.y2 -= (dsc->border_side & LV_BORDER_SIDE_BOTTOM) ? 1 : 0; + } + + lv_opa_t opa = dsc->bg_opa >= LV_OPA_MAX ? LV_OPA_COVER : dsc->bg_opa; + lv_grad_dir_t grad_dir = dsc->bg_grad_dir; + if(dsc->bg_color.full == dsc->bg_grad_color.full) grad_dir = LV_GRAD_DIR_NONE; + + bool mask_any = lv_draw_mask_is_any(&coords_bg); + + /*Most simple case: just a plain rectangle*/ + if(!mask_any && dsc->radius == 0 && (grad_dir == LV_GRAD_DIR_NONE)) { + lv_draw_blend_fill(clip_area, &coords_bg, dsc->bg_color, NULL, + LV_DRAW_MASK_RES_FULL_COVER, opa, dsc->blend_mode); + return; + } + + /*Complex case: there is gradient, mask, or radius*/ +#if LV_DRAW_COMPLEX == 0 + LV_LOG_WARN("Can't draw complex rectangle because LV_DRAW_COMPLEX = 0"); +#else + /*Get clipped fill area which is the real draw area. + *It is always the same or inside `fill_area`*/ + lv_area_t draw_area; + if(!_lv_area_intersect(&draw_area, &coords_bg, clip_area)) return; + + /*Get the real radius. Can't be larger than the half of the shortest side */ + lv_coord_t coords_w = lv_area_get_width(&coords_bg); + lv_coord_t coords_h = lv_area_get_height(&coords_bg); + int32_t short_side = LV_MIN(coords_w, coords_h); + int32_t rout = LV_MIN(dsc->radius, short_side >> 1); + + /*Add a radius mask if there is radius*/ + int32_t draw_area_w = lv_area_get_width(&draw_area); + int16_t mask_rout_id = LV_MASK_ID_INV; + lv_opa_t * mask_buf = NULL; + lv_draw_mask_radius_param_t mask_rout_param; + if(rout > 0 || mask_any) { + mask_buf = lv_mem_buf_get(draw_area_w); + lv_draw_mask_radius_init(&mask_rout_param, &coords_bg, rout, false); + mask_rout_id = lv_draw_mask_add(&mask_rout_param, NULL); + } + + /*In case of horizontal gradient pre-compute a line with a gradient*/ + lv_color_t * grad_map = NULL; + lv_color_t * grad_map_ofs = NULL; + if(grad_dir == LV_GRAD_DIR_HOR) { + grad_map = lv_mem_buf_get(coords_w * sizeof(lv_color_t)); + int32_t i; + for(i = 0; i < coords_w; i++) { + grad_map[i] = grad_get(dsc, coords_w, i); + } + grad_map_ofs = grad_map; + if(dsc->bg_grad_dir == LV_GRAD_DIR_HOR) grad_map_ofs += draw_area.x1 - coords_bg.x1; + } + + int32_t h; + lv_draw_mask_res_t mask_res; + lv_area_t blend_area; + blend_area.x1 = draw_area.x1; + blend_area.x2 = draw_area.x2; + + /*There is another mask too. Draw line by line. */ + if(mask_any) { + for(h = draw_area.y1; h <= draw_area.y2; h++) { + blend_area.y1 = h; + blend_area.y2 = h; + + /* Initialize the mask to opa instead of 0xFF and blend with LV_OPA_COVER. + * It saves calculating the final opa in lv_draw_blend_fill*/ + lv_memset(mask_buf, opa, draw_area_w); + mask_res = lv_draw_mask_apply(mask_buf, draw_area.x1, h, draw_area_w); + if(mask_res == LV_DRAW_MASK_RES_FULL_COVER) mask_res = LV_DRAW_MASK_RES_CHANGED; + + if(grad_dir == LV_GRAD_DIR_NONE) { + lv_draw_blend_fill(clip_area, &blend_area, dsc->bg_color, mask_buf, mask_res, LV_OPA_COVER, dsc->blend_mode); + } + else if(grad_dir == LV_GRAD_DIR_HOR) { + lv_draw_blend_map(clip_area, &blend_area, grad_map_ofs, mask_buf, mask_res, LV_OPA_COVER, dsc->blend_mode); + } + else if(grad_dir == LV_GRAD_DIR_VER) { + lv_color_t c = grad_get(dsc, coords_h, h - coords_bg.y1); + lv_draw_blend_fill(clip_area, &blend_area, c, mask_buf, mask_res, LV_OPA_COVER, dsc->blend_mode); + } + } + goto bg_clean_up; + } + + + /* Draw the top of the rectangle line by line and mirror it to the bottom. + * If there is no radius this cycle won't run because `h` is always `>= h_end`*/ + blend_area.x1 = draw_area.x1; + blend_area.x2 = draw_area.x2; + for(h = 0; h < rout; h++) { + lv_coord_t top_y = coords_bg.y1 + h; + lv_coord_t bottom_y = coords_bg.y2 - h; + if(top_y < draw_area.y1 && bottom_y > draw_area.y2) continue; /*This line is clipped now*/ + + /* Initialize the mask to opa instead of 0xFF and blend with LV_OPA_COVER. + * It saves calculating the final opa in lv_draw_blend_fill*/ + lv_memset(mask_buf, opa, draw_area_w); + mask_res = lv_draw_mask_apply(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; + + if(grad_dir == LV_GRAD_DIR_NONE) { + lv_draw_blend_fill(clip_area, &blend_area, dsc->bg_color, mask_buf, mask_res, LV_OPA_COVER, dsc->blend_mode); + } + else if(grad_dir == LV_GRAD_DIR_HOR) { + lv_draw_blend_map(clip_area, &blend_area, grad_map_ofs, mask_buf, mask_res, LV_OPA_COVER, dsc->blend_mode); + } + else if(grad_dir == LV_GRAD_DIR_VER) { + lv_color_t c = grad_get(dsc, coords_h, top_y - coords_bg.y1); + lv_draw_blend_fill(clip_area, &blend_area, c, mask_buf, mask_res, LV_OPA_COVER, dsc->blend_mode); + } + } + + if(bottom_y <= draw_area.y2) { + blend_area.y1 = bottom_y; + blend_area.y2 = bottom_y; + + if(grad_dir == LV_GRAD_DIR_NONE) { + lv_draw_blend_fill(clip_area, &blend_area, dsc->bg_color, mask_buf, mask_res, LV_OPA_COVER, dsc->blend_mode); + } + else if(grad_dir == LV_GRAD_DIR_HOR) { + lv_draw_blend_map(clip_area, &blend_area, grad_map_ofs, mask_buf, mask_res, LV_OPA_COVER, dsc->blend_mode); + } + else if(grad_dir == LV_GRAD_DIR_VER) { + lv_color_t c = grad_get(dsc, coords_h, bottom_y - coords_bg.y1); + lv_draw_blend_fill(clip_area, &blend_area, c, mask_buf, mask_res, LV_OPA_COVER, dsc->blend_mode); + } + } + } + + /* Draw the center of the rectangle.*/ + + /*If no other masks and no gradient, the center is a simple rectangle*/ + if(!mask_any && grad_dir == LV_GRAD_DIR_NONE) { + blend_area.y1 = coords_bg.y1 + rout; + blend_area.y2 = coords_bg.y2 - rout; + lv_draw_blend_fill(clip_area, &blend_area, dsc->bg_color, mask_buf, LV_DRAW_MASK_RES_FULL_COVER, opa, dsc->blend_mode); + } + /*With gradient and/or mask draw line by line*/ + else { + mask_res = LV_DRAW_MASK_RES_FULL_COVER; + int32_t h_end = coords_bg.y2 - rout; + for(h = coords_bg.y1 + rout; h <= h_end; h++) { + /*If there is no other mask do not apply mask as in the center there is no radius to mask*/ + if(mask_any) { + lv_memset_ff(mask_buf, draw_area_w); + mask_res = lv_draw_mask_apply(mask_buf, draw_area.x1, h, draw_area_w); + } + + blend_area.y1 = h; + blend_area.y2 = h; + if(grad_dir == LV_GRAD_DIR_NONE) { + lv_draw_blend_fill(clip_area, &blend_area, dsc->bg_color, mask_buf, mask_res, opa, dsc->blend_mode); + } + else if(grad_dir == LV_GRAD_DIR_HOR) { + lv_draw_blend_map(clip_area, &blend_area, grad_map_ofs, mask_buf, mask_res, opa, dsc->blend_mode); + } + else if(grad_dir == LV_GRAD_DIR_VER) { + lv_color_t c = grad_get(dsc, coords_h, h - coords_bg.y1); + lv_draw_blend_fill(clip_area, &blend_area, c, mask_buf, mask_res, opa, dsc->blend_mode); + } + } + } + + +bg_clean_up: + if(grad_map) lv_mem_buf_release(grad_map); + if(mask_buf) lv_mem_buf_release(mask_buf); + if(mask_rout_id != LV_MASK_ID_INV) { + lv_draw_mask_remove_id(mask_rout_id); + lv_draw_mask_free_param(&mask_rout_param); + } + +#endif +} + +LV_ATTRIBUTE_FAST_MEM static void draw_bg_img(const lv_area_t * coords, const lv_area_t * clip, + const lv_draw_rect_dsc_t * dsc) +{ + if(dsc->bg_img_src == NULL) return; + if(dsc->bg_img_opa <= LV_OPA_MIN) return; + + lv_img_src_t src_type = lv_img_src_get_type(dsc->bg_img_src); + if(src_type == LV_IMG_SRC_SYMBOL) { + lv_point_t size; + lv_txt_get_size(&size, dsc->bg_img_src, dsc->bg_img_symbol_font, 0, 0, LV_COORD_MAX, LV_TEXT_FLAG_NONE); + lv_area_t a; + a.x1 = coords->x1 + lv_area_get_width(coords) / 2 - size.x / 2; + a.x2 = a.x1 + size.x - 1; + a.y1 = coords->y1 + lv_area_get_height(coords) / 2 - size.y / 2; + a.y2 = a.y1 + size.y - 1; + + lv_draw_label_dsc_t label_draw_dsc; + lv_draw_label_dsc_init(&label_draw_dsc); + label_draw_dsc.font = dsc->bg_img_symbol_font; + label_draw_dsc.color = dsc->bg_img_recolor; + label_draw_dsc.opa = dsc->bg_img_opa; + lv_draw_label(&a, clip, &label_draw_dsc, dsc->bg_img_src, NULL); + } + else { + lv_img_header_t header; + lv_res_t res = lv_img_decoder_get_info(dsc->bg_img_src, &header); + if(res != LV_RES_OK) { + LV_LOG_WARN("Couldn't read the background image"); + return; + } + + lv_draw_img_dsc_t img_dsc; + lv_draw_img_dsc_init(&img_dsc); + img_dsc.blend_mode = dsc->blend_mode; + img_dsc.recolor = dsc->bg_img_recolor; + img_dsc.recolor_opa = dsc->bg_img_recolor_opa; + img_dsc.opa = dsc->bg_img_opa; + + /*Center align*/ + if(dsc->bg_img_tiled == false) { + lv_area_t area; + area.x1 = coords->x1 + lv_area_get_width(coords) / 2 - header.w / 2; + area.y1 = coords->y1 + lv_area_get_height(coords) / 2 - header.h / 2; + area.x2 = area.x1 + header.w - 1; + area.y2 = area.y1 + header.h - 1; + + lv_draw_img(&area, clip, dsc->bg_img_src, &img_dsc); + } + else { + lv_area_t area; + area.y1 = coords->y1; + area.y2 = area.y1 + header.h - 1; + + for(; area.y1 <= coords->y2; area.y1 += header.h, area.y2 += header.h) { + + area.x1 = coords->x1; + area.x2 = area.x1 + header.w - 1; + for(; area.x1 <= coords->x2; area.x1 += header.w, area.x2 += header.w) { + lv_draw_img(&area, clip, dsc->bg_img_src, &img_dsc); + } + } + } + } +} + +LV_ATTRIBUTE_FAST_MEM static void draw_border(const lv_area_t * coords, const lv_area_t * clip, + const lv_draw_rect_dsc_t * dsc) +{ + if(dsc->border_opa <= LV_OPA_MIN) return; + if(dsc->border_width == 0) return; + if(dsc->border_side == LV_BORDER_SIDE_NONE) return; + if(dsc->border_post) 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->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)); + + lv_coord_t rin = rout - dsc->border_width; + if(rin < 0) rin = 0; + + draw_border_generic(clip, coords, &area_inner, rout, rin, dsc->border_color, dsc->border_opa, dsc->blend_mode); + +} + + +#if LV_DRAW_COMPLEX +LV_ATTRIBUTE_FAST_MEM static inline lv_color_t grad_get(const lv_draw_rect_dsc_t * dsc, lv_coord_t s, lv_coord_t i) +{ + int32_t min = (dsc->bg_main_color_stop * s) >> 8; + if(i <= min) return dsc->bg_color; + + int32_t max = (dsc->bg_grad_color_stop * s) >> 8; + if(i >= max) return dsc->bg_grad_color; + + int32_t d = dsc->bg_grad_color_stop - dsc->bg_main_color_stop; + d = (s * d) >> 8; + i -= min; + lv_opa_t mix = (i * 255) / d; + return lv_color_mix(dsc->bg_grad_color, dsc->bg_color, mix); +} + +LV_ATTRIBUTE_FAST_MEM static void draw_shadow(const lv_area_t * coords, const lv_area_t * clip, + const lv_draw_rect_dsc_t * dsc) +{ + /*Check whether the shadow is visible*/ + if(dsc->shadow_width == 0) return; + if(dsc->shadow_opa <= LV_OPA_MIN) return; + + if(dsc->shadow_width == 1 && dsc->shadow_spread <= 0 && + dsc->shadow_ofs_x == 0 && dsc->shadow_ofs_y == 0) { + return; + } + + /*Calculate the rectangle which is blurred to get the shadow in `shadow_area`*/ + lv_area_t core_area; + core_area.x1 = coords->x1 + dsc->shadow_ofs_x - dsc->shadow_spread; + core_area.x2 = coords->x2 + dsc->shadow_ofs_x + dsc->shadow_spread; + core_area.y1 = coords->y1 + dsc->shadow_ofs_y - dsc->shadow_spread; + core_area.y2 = coords->y2 + dsc->shadow_ofs_y + dsc->shadow_spread; + + /*Calculate the bounding box of the shadow*/ + lv_area_t shadow_area; + shadow_area.x1 = core_area.x1 - dsc->shadow_width / 2 - 1; + shadow_area.x2 = core_area.x2 + dsc->shadow_width / 2 + 1; + shadow_area.y1 = core_area.y1 - dsc->shadow_width / 2 - 1; + shadow_area.y2 = core_area.y2 + dsc->shadow_width / 2 + 1; + + lv_opa_t opa = dsc->shadow_opa; + if(opa > LV_OPA_MAX) opa = LV_OPA_COVER; + + /*Get clipped draw area which is the real draw area. + *It is always the same or inside `shadow_area`*/ + lv_area_t draw_area; + if(!_lv_area_intersect(&draw_area, &shadow_area, clip)) return; + + /*Consider 1 px smaller bg to be sure the edge will be covered by the shadow*/ + lv_area_t bg_area; + lv_area_copy(&bg_area, coords); + lv_area_increase(&bg_area, -1, -1); + + /*Get the clamped radius*/ + int32_t r_bg = dsc->radius; + lv_coord_t short_side = LV_MIN(lv_area_get_width(&bg_area), lv_area_get_height(&bg_area)); + if(r_bg > short_side >> 1) r_bg = short_side >> 1; + + /*Get the clamped radius*/ + int32_t r_sh = dsc->radius; + short_side = LV_MIN(lv_area_get_width(&core_area), lv_area_get_height(&core_area)); + if(r_sh > short_side >> 1) r_sh = short_side >> 1; + + /*Get how many pixels are affected by the blur on the corners*/ + int32_t corner_size = dsc->shadow_width + r_sh; + + lv_opa_t * sh_buf; + +#if LV_SHADOW_CACHE_SIZE + if(sh_cache_size == corner_size && sh_cache_r == r_sh) { + /*Use the cache if available*/ + sh_buf = lv_mem_buf_get(corner_size * corner_size); + lv_memcpy(sh_buf, sh_cache, corner_size * corner_size); + } + else { + /*A larger buffer is required for calculation*/ + sh_buf = lv_mem_buf_get(corner_size * corner_size * sizeof(uint16_t)); + shadow_draw_corner_buf(&core_area, (uint16_t *)sh_buf, dsc->shadow_width, r_sh); + + /*Cache the corner if it fits into the cache size*/ + if((uint32_t)corner_size * corner_size < sizeof(sh_cache)) { + lv_memcpy(sh_cache, sh_buf, corner_size * corner_size); + sh_cache_size = corner_size; + sh_cache_r = r_sh; + } + } +#else + sh_buf = lv_mem_buf_get(corner_size * corner_size * sizeof(uint16_t)); + shadow_draw_corner_buf(&core_area, (uint16_t *)sh_buf, dsc->shadow_width, r_sh); +#endif + + /*Skip a lot of masking if the background will cover the shadow that would be masked out*/ + bool mask_any = lv_draw_mask_is_any(&shadow_area); + bool simple = true; + if(mask_any || dsc->bg_opa < LV_OPA_COVER) simple = false; + + /*Create a radius mask to clip remove shadow on the bg area*/ + lv_draw_mask_res_t mask_res; + + lv_draw_mask_radius_param_t mask_rout_param; + int16_t mask_rout_id = LV_MASK_ID_INV; + if(!simple) { + lv_draw_mask_radius_init(&mask_rout_param, &bg_area, r_bg, true); + mask_rout_id = lv_draw_mask_add(&mask_rout_param, NULL); + } + lv_opa_t * mask_buf = lv_mem_buf_get(lv_area_get_width(&shadow_area)); + lv_area_t blend_area; + lv_area_t clip_area_sub; + lv_opa_t ** mask_act; + lv_opa_t * sh_buf_tmp; + lv_coord_t y; + bool simple_sub; + + lv_coord_t w_half = shadow_area.x1 + lv_area_get_width(&shadow_area) / 2; + lv_coord_t h_half = shadow_area.y1 + lv_area_get_height(&shadow_area) / 2; + + /*Draw the corners if they are on the current clip area and not fully covered by the bg*/ + + /*Top right corner*/ + blend_area.x2 = shadow_area.x2; + blend_area.x1 = shadow_area.x2 - corner_size + 1; + blend_area.y1 = shadow_area.y1; + blend_area.y2 = shadow_area.y1 + corner_size - 1; + /*Do not overdraw the top other corners*/ + blend_area.x1 = LV_MAX(blend_area.x1, w_half); + blend_area.y2 = LV_MIN(blend_area.y2, h_half); + + if(_lv_area_intersect(&clip_area_sub, &blend_area, clip) && !_lv_area_is_in(&clip_area_sub, &bg_area, r_bg)) { + lv_coord_t w = lv_area_get_width(&clip_area_sub); + sh_buf_tmp = sh_buf; + sh_buf_tmp += (clip_area_sub.y1 - shadow_area.y1) * corner_size; + sh_buf_tmp += clip_area_sub.x1 - (shadow_area.x2 - corner_size + 1); + + /*Do not mask if out of the bg*/ + if(simple && _lv_area_is_out(&clip_area_sub, &bg_area, r_bg)) simple_sub = true; + else simple_sub = simple; + mask_act = simple_sub ? &sh_buf_tmp : &mask_buf; + if(w > 0) { + mask_res = LV_DRAW_MASK_RES_CHANGED; /*In simple mode it won't be overwritten*/ + for(y = clip_area_sub.y1; y <= clip_area_sub.y2; y++) { + blend_area.y1 = y; + blend_area.y2 = y; + + if(!simple_sub) { + lv_memcpy(mask_buf, sh_buf_tmp, corner_size); + mask_res = lv_draw_mask_apply(mask_buf, clip_area_sub.x1, y, w); + if(mask_res == LV_DRAW_MASK_RES_FULL_COVER) mask_res = LV_DRAW_MASK_RES_CHANGED; + } + lv_draw_blend_fill(&clip_area_sub, &blend_area, dsc->shadow_color, *mask_act, mask_res, dsc->shadow_opa, + dsc->blend_mode); + sh_buf_tmp += corner_size; + } + } + } + + /*Bottom right corner. + *Almost the same as top right just read the lines of `sh_buf` from then end*/ + blend_area.x2 = shadow_area.x2; + blend_area.x1 = shadow_area.x2 - corner_size + 1; + blend_area.y1 = shadow_area.y2 - corner_size + 1; + blend_area.y2 = shadow_area.y2; + /*Do not overdraw the other corners*/ + blend_area.x1 = LV_MAX(blend_area.x1, w_half); + blend_area.y1 = LV_MAX(blend_area.y1, h_half + 1); + + if(_lv_area_intersect(&clip_area_sub, &blend_area, clip) && !_lv_area_is_in(&clip_area_sub, &bg_area, r_bg)) { + lv_coord_t w = lv_area_get_width(&clip_area_sub); + sh_buf_tmp = sh_buf; + sh_buf_tmp += (blend_area.y2 - clip_area_sub.y2) * corner_size; + sh_buf_tmp += clip_area_sub.x1 - (shadow_area.x2 - corner_size + 1); + /*Do not mask if out of the bg*/ + if(simple && _lv_area_is_out(&clip_area_sub, &bg_area, r_bg)) simple_sub = true; + else simple_sub = simple; + mask_act = simple_sub ? &sh_buf_tmp : &mask_buf; + + if(w > 0) { + mask_res = LV_DRAW_MASK_RES_CHANGED; /*In simple mode it won't be overwritten*/ + for(y = clip_area_sub.y2; y >= clip_area_sub.y1; y--) { + blend_area.y1 = y; + blend_area.y2 = y; + + if(!simple_sub) { + lv_memcpy(mask_buf, sh_buf_tmp, corner_size); + mask_res = lv_draw_mask_apply(mask_buf, clip_area_sub.x1, y, w); + if(mask_res == LV_DRAW_MASK_RES_FULL_COVER) mask_res = LV_DRAW_MASK_RES_CHANGED; + } + lv_draw_blend_fill(&clip_area_sub, &blend_area, dsc->shadow_color, *mask_act, mask_res, dsc->shadow_opa, + dsc->blend_mode); + sh_buf_tmp += corner_size; + } + } + } + + /*Top side*/ + blend_area.x1 = shadow_area.x1 + corner_size; + blend_area.x2 = shadow_area.x2 - corner_size; + blend_area.y1 = shadow_area.y1; + blend_area.y2 = shadow_area.y1 + corner_size - 1; + blend_area.y2 = LV_MIN(blend_area.y2, h_half); + + if(_lv_area_intersect(&clip_area_sub, &blend_area, clip) && !_lv_area_is_in(&clip_area_sub, &bg_area, r_bg)) { + lv_coord_t w = lv_area_get_width(&clip_area_sub); + sh_buf_tmp = sh_buf; + sh_buf_tmp += (clip_area_sub.y1 - blend_area.y1) * corner_size; + + /*Do not mask if out of the bg*/ + if(simple && _lv_area_is_out(&clip_area_sub, &bg_area, r_bg)) simple_sub = true; + else simple_sub = simple; + + if(w > 0) { + mask_res = LV_DRAW_MASK_RES_CHANGED; /*In simple mode it won't be overwritten*/ + for(y = clip_area_sub.y1; y <= clip_area_sub.y2; y++) { + blend_area.y1 = y; + blend_area.y2 = y; + + if(!simple_sub) { + lv_memset(mask_buf, sh_buf_tmp[0], w); + mask_res = lv_draw_mask_apply(mask_buf, clip_area_sub.x1, y, w); + if(mask_res == LV_DRAW_MASK_RES_FULL_COVER) mask_res = LV_DRAW_MASK_RES_CHANGED; + lv_draw_blend_fill(&clip_area_sub, &blend_area, dsc->shadow_color, mask_buf, mask_res, dsc->shadow_opa, + dsc->blend_mode); + } + else { + lv_opa_t line_opa = opa == LV_OPA_COVER ? sh_buf_tmp[0] : (sh_buf_tmp[0] * dsc->shadow_opa) >> 8; + lv_draw_blend_fill(&clip_area_sub, &blend_area, dsc->shadow_color, NULL, LV_DRAW_MASK_RES_FULL_COVER, line_opa, + dsc->blend_mode); + } + sh_buf_tmp += corner_size; + } + } + } + + /*Bottom side*/ + blend_area.x1 = shadow_area.x1 + corner_size; + blend_area.x2 = shadow_area.x2 - corner_size; + blend_area.y1 = shadow_area.y2 - corner_size + 1; + blend_area.y2 = shadow_area.y2; + blend_area.y1 = LV_MAX(blend_area.y1, h_half + 1); + + if(_lv_area_intersect(&clip_area_sub, &blend_area, clip) && !_lv_area_is_in(&clip_area_sub, &bg_area, r_bg)) { + lv_coord_t w = lv_area_get_width(&clip_area_sub); + sh_buf_tmp = sh_buf; + sh_buf_tmp += (blend_area.y2 - clip_area_sub.y2) * corner_size; + if(w > 0) { + mask_res = LV_DRAW_MASK_RES_CHANGED; /*In simple mode it won't be overwritten*/ + for(y = clip_area_sub.y2; y >= clip_area_sub.y1; y--) { + blend_area.y1 = y; + blend_area.y2 = y; + + /*Do not mask if out of the bg*/ + if(simple && _lv_area_is_out(&clip_area_sub, &bg_area, r_bg)) simple_sub = true; + else simple_sub = simple; + + if(!simple_sub) { + lv_memset(mask_buf, sh_buf_tmp[0], w); + mask_res = lv_draw_mask_apply(mask_buf, clip_area_sub.x1, y, w); + if(mask_res == LV_DRAW_MASK_RES_FULL_COVER) mask_res = LV_DRAW_MASK_RES_CHANGED; + lv_draw_blend_fill(&clip_area_sub, &blend_area, dsc->shadow_color, mask_buf, mask_res, dsc->shadow_opa, + dsc->blend_mode); + } + else { + lv_opa_t line_opa = opa == LV_OPA_COVER ? sh_buf_tmp[0] : (sh_buf_tmp[0] * dsc->shadow_opa) >> 8; + lv_draw_blend_fill(&clip_area_sub, &blend_area, dsc->shadow_color, NULL, LV_DRAW_MASK_RES_FULL_COVER, line_opa, + dsc->blend_mode); + + } + sh_buf_tmp += corner_size; + } + } + } + + /*Right side*/ + blend_area.x1 = shadow_area.x2 - corner_size + 1; + blend_area.x2 = shadow_area.x2; + blend_area.y1 = shadow_area.y1 + corner_size; + blend_area.y2 = shadow_area.y2 - corner_size; + /*Do not overdraw the other corners*/ + blend_area.y1 = LV_MIN(blend_area.y1, h_half + 1); + blend_area.y2 = LV_MAX(blend_area.y2, h_half); + blend_area.x1 = LV_MAX(blend_area.x1, w_half); + + if(_lv_area_intersect(&clip_area_sub, &blend_area, clip) && !_lv_area_is_in(&clip_area_sub, &bg_area, r_bg)) { + lv_coord_t w = lv_area_get_width(&clip_area_sub); + sh_buf_tmp = sh_buf; + sh_buf_tmp += (corner_size - 1) * corner_size; + sh_buf_tmp += clip_area_sub.x1 - (shadow_area.x2 - corner_size + 1); + + /*Do not mask if out of the bg*/ + if(simple && _lv_area_is_out(&clip_area_sub, &bg_area, r_bg)) simple_sub = true; + else simple_sub = simple; + mask_act = simple_sub ? &sh_buf_tmp : &mask_buf; + + if(w > 0) { + mask_res = LV_DRAW_MASK_RES_CHANGED; /*In simple mode it won't be overwritten*/ + for(y = clip_area_sub.y1; y <= clip_area_sub.y2; y++) { + blend_area.y1 = y; + blend_area.y2 = y; + + if(!simple_sub) { + lv_memcpy(mask_buf, sh_buf_tmp, w); + mask_res = lv_draw_mask_apply(mask_buf, clip_area_sub.x1, y, w); + if(mask_res == LV_DRAW_MASK_RES_FULL_COVER) mask_res = LV_DRAW_MASK_RES_CHANGED; + } + lv_draw_blend_fill(&clip_area_sub, &blend_area, dsc->shadow_color, *mask_act, mask_res, dsc->shadow_opa, + dsc->blend_mode); + } + } + } + + /*Mirror the shadow corner buffer horizontally*/ + sh_buf_tmp = sh_buf ; + for(y = 0; y < corner_size; y++) { + int32_t x; + lv_opa_t * start = sh_buf_tmp; + lv_opa_t * end = sh_buf_tmp + corner_size - 1; + for(x = 0; x < corner_size / 2; x++) { + lv_opa_t tmp = *start; + *start = *end; + *end = tmp; + + start++; + end--; + } + sh_buf_tmp += corner_size; + } + + /*Left side*/ + blend_area.x1 = shadow_area.x1; + blend_area.x2 = shadow_area.x1 + corner_size - 1; + blend_area.y1 = shadow_area.y1 + corner_size; + blend_area.y2 = shadow_area.y2 - corner_size; + /*Do not overdraw the other corners*/ + blend_area.y1 = LV_MIN(blend_area.y1, h_half + 1); + blend_area.y2 = LV_MAX(blend_area.y2, h_half); + blend_area.x2 = LV_MIN(blend_area.x2, w_half - 1); + + if(_lv_area_intersect(&clip_area_sub, &blend_area, clip) && !_lv_area_is_in(&clip_area_sub, &bg_area, r_bg)) { + lv_coord_t w = lv_area_get_width(&clip_area_sub); + sh_buf_tmp = sh_buf; + sh_buf_tmp += (corner_size - 1) * corner_size; + sh_buf_tmp += clip_area_sub.x1 - blend_area.x1; + + /*Do not mask if out of the bg*/ + if(simple && _lv_area_is_out(&clip_area_sub, &bg_area, r_bg)) simple_sub = true; + else simple_sub = simple; + mask_act = simple_sub ? &sh_buf_tmp : &mask_buf; + if(w > 0) { + mask_res = LV_DRAW_MASK_RES_CHANGED; /*In simple mode it won't be overwritten*/ + for(y = clip_area_sub.y1; y <= clip_area_sub.y2; y++) { + blend_area.y1 = y; + blend_area.y2 = y; + + if(!simple_sub) { + lv_memcpy(mask_buf, sh_buf_tmp, w); + mask_res = lv_draw_mask_apply(mask_buf, clip_area_sub.x1, y, w); + if(mask_res == LV_DRAW_MASK_RES_FULL_COVER) mask_res = LV_DRAW_MASK_RES_CHANGED; + } + lv_draw_blend_fill(&clip_area_sub, &blend_area, dsc->shadow_color, *mask_act, mask_res, dsc->shadow_opa, + dsc->blend_mode); + } + } + } + + /*Top left corner*/ + blend_area.x1 = shadow_area.x1; + blend_area.x2 = shadow_area.x1 + corner_size - 1; + blend_area.y1 = shadow_area.y1; + blend_area.y2 = shadow_area.y1 + corner_size - 1; + /*Do not overdraw the other corners*/ + blend_area.x2 = LV_MIN(blend_area.x2, w_half - 1); + blend_area.y2 = LV_MIN(blend_area.y2, h_half); + + if(_lv_area_intersect(&clip_area_sub, &blend_area, clip) && !_lv_area_is_in(&clip_area_sub, &bg_area, r_bg)) { + lv_coord_t w = lv_area_get_width(&clip_area_sub); + sh_buf_tmp = sh_buf; + sh_buf_tmp += (clip_area_sub.y1 - blend_area.y1) * corner_size; + sh_buf_tmp += clip_area_sub.x1 - blend_area.x1; + + /*Do not mask if out of the bg*/ + if(simple && _lv_area_is_out(&clip_area_sub, &bg_area, r_bg)) simple_sub = true; + else simple_sub = simple; + mask_act = simple_sub ? &sh_buf_tmp : &mask_buf; + + if(w > 0) { + mask_res = LV_DRAW_MASK_RES_CHANGED; /*In simple mode it won't be overwritten*/ + for(y = clip_area_sub.y1; y <= clip_area_sub.y2; y++) { + blend_area.y1 = y; + blend_area.y2 = y; + + if(!simple_sub) { + lv_memcpy(mask_buf, sh_buf_tmp, corner_size); + mask_res = lv_draw_mask_apply(mask_buf, clip_area_sub.x1, y, w); + if(mask_res == LV_DRAW_MASK_RES_FULL_COVER) mask_res = LV_DRAW_MASK_RES_CHANGED; + } + lv_draw_blend_fill(&clip_area_sub, &blend_area, dsc->shadow_color, *mask_act, mask_res, dsc->shadow_opa, + dsc->blend_mode); + sh_buf_tmp += corner_size; + } + } + } + + /*Bottom left corner. + *Almost the same as bottom right just read the lines of `sh_buf` from then end*/ + blend_area.x1 = shadow_area.x1 ; + blend_area.x2 = shadow_area.x1 + corner_size - 1; + blend_area.y1 = shadow_area.y2 - corner_size + 1; + blend_area.y2 = shadow_area.y2; + /*Do not overdraw the other corners*/ + blend_area.y1 = LV_MAX(blend_area.y1, h_half + 1); + blend_area.x2 = LV_MIN(blend_area.x2, w_half - 1); + + if(_lv_area_intersect(&clip_area_sub, &blend_area, clip) && !_lv_area_is_in(&clip_area_sub, &bg_area, r_bg)) { + lv_coord_t w = lv_area_get_width(&clip_area_sub); + sh_buf_tmp = sh_buf; + sh_buf_tmp += (blend_area.y2 - clip_area_sub.y2) * corner_size; + sh_buf_tmp += clip_area_sub.x1 - blend_area.x1; + + /*Do not mask if out of the bg*/ + if(simple && _lv_area_is_out(&clip_area_sub, &bg_area, r_bg)) simple_sub = true; + else simple_sub = simple; + mask_act = simple_sub ? &sh_buf_tmp : &mask_buf; + if(w > 0) { + mask_res = LV_DRAW_MASK_RES_CHANGED; /*In simple mode it won't be overwritten*/ + for(y = clip_area_sub.y2; y >= clip_area_sub.y1; y--) { + blend_area.y1 = y; + blend_area.y2 = y; + + if(!simple_sub) { + lv_memcpy(mask_buf, sh_buf_tmp, corner_size); + mask_res = lv_draw_mask_apply(mask_buf, clip_area_sub.x1, y, w); + if(mask_res == LV_DRAW_MASK_RES_FULL_COVER) mask_res = LV_DRAW_MASK_RES_CHANGED; + } + lv_draw_blend_fill(&clip_area_sub, &blend_area, dsc->shadow_color, *mask_act, mask_res, dsc->shadow_opa, + dsc->blend_mode); + sh_buf_tmp += corner_size; + } + } + } + + /*Draw the center rectangle.*/ + blend_area.x1 = shadow_area.x1 + corner_size ; + blend_area.x2 = shadow_area.x2 - corner_size; + blend_area.y1 = shadow_area.y1 + corner_size; + blend_area.y2 = shadow_area.y2 - corner_size; + + if(_lv_area_intersect(&clip_area_sub, &blend_area, clip) && !_lv_area_is_in(&clip_area_sub, &bg_area, r_bg)) { + lv_coord_t w = lv_area_get_width(&clip_area_sub); + if(w > 0) { + for(y = clip_area_sub.y1; y <= clip_area_sub.y2; y++) { + blend_area.y1 = y; + blend_area.y2 = y; + + lv_memset_ff(mask_buf, w); + mask_res = lv_draw_mask_apply(mask_buf, clip_area_sub.x1, y, w); + lv_draw_blend_fill(&clip_area_sub, &blend_area, dsc->shadow_color, mask_buf, mask_res, dsc->shadow_opa, + dsc->blend_mode); + } + } + } + + if(!simple) { + lv_draw_mask_free_param(&mask_rout_param); + lv_draw_mask_remove_id(mask_rout_id); + } + lv_mem_buf_release(sh_buf); + lv_mem_buf_release(mask_buf); +} + +/** + * Calculate a blurred corner + * @param coords Coordinates of the shadow + * @param sh_buf a buffer to store the result. Its size should be `(sw + r)^2 * 2` + * @param sw shadow width + * @param r radius + */ +LV_ATTRIBUTE_FAST_MEM static void shadow_draw_corner_buf(const lv_area_t * coords, uint16_t * sh_buf, lv_coord_t sw, + lv_coord_t r) +{ + int32_t sw_ori = sw; + int32_t size = sw_ori + r; + + lv_area_t sh_area; + lv_area_copy(&sh_area, coords); + sh_area.x2 = sw / 2 + r - 1 - ((sw & 1) ? 0 : 1); + sh_area.y1 = sw / 2 + 1; + + sh_area.x1 = sh_area.x2 - lv_area_get_width(coords); + sh_area.y2 = sh_area.y1 + lv_area_get_height(coords); + + lv_draw_mask_radius_param_t mask_param; + lv_draw_mask_radius_init(&mask_param, &sh_area, r, false); + +#if SHADOW_ENHANCE + /*Set half shadow width width because blur will be repeated*/ + if(sw_ori == 1) sw = 1; + else sw = sw_ori >> 1; +#endif + + int32_t y; + lv_opa_t * mask_line = lv_mem_buf_get(size); + uint16_t * sh_ups_tmp_buf = (uint16_t *)sh_buf; + for(y = 0; y < size; y++) { + lv_memset_ff(mask_line, size); + lv_draw_mask_res_t mask_res = mask_param.dsc.cb(mask_line, 0, y, size, &mask_param); + if(mask_res == LV_DRAW_MASK_RES_TRANSP) { + lv_memset_00(sh_ups_tmp_buf, size * sizeof(sh_ups_tmp_buf[0])); + } + else { + int32_t i; + sh_ups_tmp_buf[0] = (mask_line[0] << SHADOW_UPSCALE_SHIFT) / sw; + for(i = 1; i < size; i++) { + if(mask_line[i] == mask_line[i - 1]) sh_ups_tmp_buf[i] = sh_ups_tmp_buf[i - 1]; + else sh_ups_tmp_buf[i] = (mask_line[i] << SHADOW_UPSCALE_SHIFT) / sw; + } + } + + sh_ups_tmp_buf += size; + } + 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; + for(i = 0; i < size * size; i++) { + res_buf[i] = (sh_buf[i] >> SHADOW_UPSCALE_SHIFT); + } + return; + } + + shadow_blur_corner(size, sw, sh_buf); + +#if SHADOW_ENHANCE == 0 + /*The result is required in lv_opa_t not uint16_t*/ + uint32_t x; + lv_opa_t * res_buf = (lv_opa_t *)sh_buf; + for(x = 0; x < size * size; x++) { + res_buf[x] = sh_buf[x]; + } +#else + sw += sw_ori & 1; + if(sw > 1) { + uint32_t i; + uint32_t max_v_div = (LV_OPA_COVER << SHADOW_UPSCALE_SHIFT) / sw; + for(i = 0; i < (uint32_t)size * size; i++) { + if(sh_buf[i] == 0) continue; + else if(sh_buf[i] == LV_OPA_COVER) sh_buf[i] = max_v_div; + else sh_buf[i] = (sh_buf[i] << SHADOW_UPSCALE_SHIFT) / sw; + } + + shadow_blur_corner(size, sw, sh_buf); + } + int32_t x; + lv_opa_t * res_buf = (lv_opa_t *)sh_buf; + for(x = 0; x < size * size; x++) { + res_buf[x] = sh_buf[x]; + } +#endif + +} + +LV_ATTRIBUTE_FAST_MEM static void shadow_blur_corner(lv_coord_t size, lv_coord_t sw, uint16_t * sh_ups_buf) +{ + int32_t s_left = sw >> 1; + int32_t s_right = (sw >> 1); + if((sw & 1) == 0) s_left--; + + /*Horizontal blur*/ + uint16_t * sh_ups_blur_buf = lv_mem_buf_get(size * sizeof(uint16_t)); + + int32_t x; + int32_t y; + + uint16_t * sh_ups_tmp_buf = sh_ups_buf; + + for(y = 0; y < size; y++) { + int32_t v = sh_ups_tmp_buf[size - 1] * sw; + for(x = size - 1; x >= 0; x--) { + sh_ups_blur_buf[x] = v; + + /*Forget the right pixel*/ + uint32_t right_val = 0; + if(x + s_right < size) right_val = sh_ups_tmp_buf[x + s_right]; + v -= right_val; + + /*Add the left pixel*/ + uint32_t left_val; + if(x - s_left - 1 < 0) left_val = sh_ups_tmp_buf[0]; + else left_val = sh_ups_tmp_buf[x - s_left - 1]; + v += left_val; + } + lv_memcpy(sh_ups_tmp_buf, sh_ups_blur_buf, size * sizeof(uint16_t)); + sh_ups_tmp_buf += size; + } + + /*Vertical blur*/ + uint32_t i; + uint32_t max_v = LV_OPA_COVER << SHADOW_UPSCALE_SHIFT; + uint32_t max_v_div = max_v / sw; + for(i = 0; i < (uint32_t)size * size; i++) { + if(sh_ups_buf[i] == 0) continue; + else if(sh_ups_buf[i] == max_v) sh_ups_buf[i] = max_v_div; + else sh_ups_buf[i] = sh_ups_buf[i] / sw; + } + + for(x = 0; x < size; x++) { + sh_ups_tmp_buf = &sh_ups_buf[x]; + int32_t v = sh_ups_tmp_buf[0] * sw; + for(y = 0; y < size ; y++, sh_ups_tmp_buf += size) { + sh_ups_blur_buf[y] = v < 0 ? 0 : (v >> SHADOW_UPSCALE_SHIFT); + + /*Forget the top pixel*/ + uint32_t top_val; + if(y - s_right <= 0) top_val = sh_ups_tmp_buf[0]; + else top_val = sh_ups_buf[(y - s_right) * size + x]; + v -= top_val; + + /*Add the bottom pixel*/ + uint32_t bottom_val; + if(y + s_left + 1 < size) bottom_val = sh_ups_buf[(y + s_left + 1) * size + x]; + else bottom_val = sh_ups_buf[(size - 1) * size + x]; + v += bottom_val; + } + + /*Write back the result into `sh_ups_buf`*/ + sh_ups_tmp_buf = &sh_ups_buf[x]; + for(y = 0; y < size; y++, sh_ups_tmp_buf += size) { + (*sh_ups_tmp_buf) = sh_ups_blur_buf[y]; + } + } + + lv_mem_buf_release(sh_ups_blur_buf); +} + +#endif + +static void draw_outline(const lv_area_t * coords, const lv_area_t * clip, const lv_draw_rect_dsc_t * dsc) +{ + if(dsc->outline_opa <= LV_OPA_MIN) return; + if(dsc->outline_width == 0) return; + + lv_opa_t opa = dsc->outline_opa; + + if(opa > LV_OPA_MAX) opa = LV_OPA_COVER; + + /*Get the inner radius*/ + lv_area_t area_inner; + lv_area_copy(&area_inner, coords); + + /*Bring the outline closer to make sure there is no color bleeding with pad=0*/ + lv_coord_t pad = dsc->outline_pad - 1; + area_inner.x1 -= pad; + area_inner.y1 -= pad; + area_inner.x2 += pad; + area_inner.y2 += pad; + + lv_area_t area_outer; + lv_area_copy(&area_outer, &area_inner); + + area_outer.x1 -= dsc->outline_width; + area_outer.x2 += dsc->outline_width; + area_outer.y1 -= dsc->outline_width; + area_outer.y2 += dsc->outline_width; + + + 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_MIN(inner_w, inner_h); + if(rin > short_side >> 1) rin = short_side >> 1; + + lv_coord_t rout = rin + dsc->outline_width; + + draw_border_generic(clip, &area_outer, &area_inner, rout, rin, dsc->outline_color, dsc->outline_opa, dsc->blend_mode); +} + +void draw_border_generic(const lv_area_t * clip_area, const lv_area_t * outer_area, const lv_area_t * inner_area, + lv_coord_t rout, lv_coord_t rin, lv_color_t color, lv_opa_t opa, lv_blend_mode_t blend_mode) +{ + opa = opa >= LV_OPA_COVER ? LV_OPA_COVER : opa; + + bool mask_any = lv_draw_mask_is_any(outer_area); + + if(!mask_any && rout == 0 && rin == 0) { + draw_border_simple(clip_area, outer_area, inner_area, color, opa); + return; + } + +#if LV_DRAW_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, clip_area)) return; + 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); + + /*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, outer_area, rout, false); + mask_rout_id = lv_draw_mask_add(&mask_rout_param, NULL); + } + + /*Create mask for the inner mask*/ + lv_draw_mask_radius_param_t mask_rin_param; + lv_draw_mask_radius_init(&mask_rin_param, inner_area, rin, true); + int16_t mask_rin_id = lv_draw_mask_add(&mask_rin_param, NULL); + + int32_t h; + lv_draw_mask_res_t mask_res; + lv_area_t blend_area; + + /*Calculate the x and y coordinates where the straight parts area*/ + 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); + lv_coord_t core_w = lv_area_get_width(&core_area); + + bool top_side = outer_area->y1 <= inner_area->y1 ? true : false; + bool bottom_side = outer_area->y2 >= inner_area->y2 ? true : false; + + /*If there is other masks, need to draw line by line*/ + if(mask_any) { + blend_area.x1 = draw_area.x1; + blend_area.x2 = draw_area.x2; + for(h = draw_area.y1; h <= draw_area.y2; h++) { + if(!top_side && h < core_area.y1) continue; + if(!bottom_side && h > core_area.y2) break; + + blend_area.y1 = h; + blend_area.y2 = h; + + lv_memset_ff(mask_buf, draw_area_w); + mask_res = lv_draw_mask_apply(mask_buf, draw_area.x1, h, draw_area_w); + lv_draw_blend_fill(clip_area, &blend_area, color, mask_buf, mask_res, opa, blend_mode); + } + + lv_draw_mask_free_param(&mask_rin_param); + lv_draw_mask_remove_id(mask_rin_id); + if(mask_rout_id != LV_MASK_ID_INV) { + lv_draw_mask_free_param(&mask_rout_param); + lv_draw_mask_remove_id(mask_rout_id); + } + lv_mem_buf_release(mask_buf); + return; + } + + /*No masks*/ + bool left_side = outer_area->x1 <= inner_area->x1 ? true : false; + bool right_side = outer_area->x2 >= inner_area->x2 ? true : false; + + bool split_hor = true; + if(left_side && right_side && top_side && bottom_side && + core_w < SPLIT_LIMIT) { + split_hor = false; + } + + /*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_blend_fill(clip_area, &blend_area, color, NULL, LV_DRAW_MASK_RES_FULL_COVER, opa, blend_mode); + } + + 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_blend_fill(clip_area, &blend_area, color, NULL, LV_DRAW_MASK_RES_FULL_COVER, opa, blend_mode); + } + + 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_blend_fill(clip_area, &blend_area, color, NULL, LV_DRAW_MASK_RES_FULL_COVER, opa, blend_mode); + } + + 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_blend_fill(clip_area, &blend_area, color, NULL, LV_DRAW_MASK_RES_FULL_COVER, opa, blend_mode); + } + + /*Draw the corners*/ + lv_coord_t blend_w; + + /*Left and right corner together is they close to eachother*/ + 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; + lv_coord_t max_h = LV_MAX(rout, outer_area->y1 - inner_area->y1); + for(h = 0; h < max_h; h++) { + lv_coord_t top_y = outer_area->y1 + h; + lv_coord_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_ff(mask_buf, draw_area_w); + mask_res = lv_draw_mask_apply(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_blend_fill(clip_area, &blend_area, color, mask_buf, mask_res, opa, blend_mode); + } + + if(bottom_y <= draw_area.y2) { + blend_area.y1 = bottom_y; + blend_area.y2 = bottom_y; + lv_draw_blend_fill(clip_area, &blend_area, color, mask_buf, mask_res, opa, blend_mode); + } + } + } + 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_ff(mask_buf, blend_w); + mask_res = lv_draw_mask_apply(mask_buf, blend_area.x1, h, blend_w); + lv_draw_blend_fill(clip_area, &blend_area, color, mask_buf, mask_res, opa, blend_mode); + } + } + + 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_ff(mask_buf, blend_w); + mask_res = lv_draw_mask_apply(mask_buf, blend_area.x1, h, blend_w); + lv_draw_blend_fill(clip_area, &blend_area, color, mask_buf, mask_res, opa, blend_mode); + } + } + } + + /*Right corners*/ + 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_ff(mask_buf, blend_w); + mask_res = lv_draw_mask_apply(mask_buf, blend_area.x1, h, blend_w); + lv_draw_blend_fill(clip_area, &blend_area, color, mask_buf, mask_res, opa, blend_mode); + } + } + + 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_ff(mask_buf, blend_w); + mask_res = lv_draw_mask_apply(mask_buf, blend_area.x1, h, blend_w); + lv_draw_blend_fill(clip_area, &blend_area, color, mask_buf, mask_res, opa, blend_mode); + } + } + } + } + + lv_draw_mask_free_param(&mask_rin_param); + lv_draw_mask_remove_id(mask_rin_id); + lv_draw_mask_free_param(&mask_rout_param); + lv_draw_mask_remove_id(mask_rout_id); + lv_mem_buf_release(mask_buf); + +#else /*LV_DRAW_COMPLEX*/ + LV_UNUSED(blend_mode); +#endif /*LV_DRAW_COMPLEX*/ +} + +static void draw_border_simple(const lv_area_t * clip, const lv_area_t * outer_area, const lv_area_t * inner_area, + lv_color_t color, lv_opa_t opa) +{ + bool top_side = outer_area->y1 <= inner_area->y1 ? true : false; + bool bottom_side = outer_area->y2 >= inner_area->y2 ? true : false; + bool left_side = outer_area->x1 <= inner_area->x1 ? true : false; + bool right_side = outer_area->x2 >= inner_area->x2 ? true : false; + + lv_area_t a; + /*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_blend_fill(clip, &a, color, NULL, LV_DRAW_MASK_RES_FULL_COVER, opa, LV_BLEND_MODE_NORMAL); + } + + /*Bottom*/ + a.y1 = inner_area->y2 + 1; + a.y2 = outer_area->y2; + if(bottom_side) { + lv_draw_blend_fill(clip, &a, color, NULL, LV_DRAW_MASK_RES_FULL_COVER, opa, LV_BLEND_MODE_NORMAL); + } + + /*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_blend_fill(clip, &a, color, NULL, LV_DRAW_MASK_RES_FULL_COVER, opa, LV_BLEND_MODE_NORMAL); + } + + /*Right*/ + a.x1 = inner_area->x2 + 1; + a.x2 = outer_area->x2; + if(right_side) { + lv_draw_blend_fill(clip, &a, color, NULL, LV_DRAW_MASK_RES_FULL_COVER, opa, LV_BLEND_MODE_NORMAL); + } + +} + diff --git a/src/extra/libs/rlottie/lv_rlottie.c b/src/extra/libs/rlottie/lv_rlottie.c index 89d5d1386..dce856d34 100644 --- a/src/extra/libs/rlottie/lv_rlottie.c +++ b/src/extra/libs/rlottie/lv_rlottie.c @@ -86,7 +86,7 @@ lv_obj_t * lv_rlottie_create_from_raw(lv_obj_t * parent, lv_coord_t width, lv_co static void lv_rlottie_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj) { - + LV_UNUSED(class_p); lv_rlottie_t * rlottie = (lv_rlottie_t *) obj; if(rlottie_desc_create) { @@ -130,7 +130,7 @@ static void lv_rlottie_constructor(const lv_obj_class_t * class_p, lv_obj_t * ob static void lv_rlottie_destructor(const lv_obj_class_t * class_p, lv_obj_t * obj) { - + LV_UNUSED(class_p); lv_rlottie_t * rlottie = (lv_rlottie_t *) obj; if(rlottie->animation) { diff --git a/src/gpu/sdl/lv_gpu_sdl_draw_blend.c b/src/gpu/sdl/lv_gpu_sdl_draw_blend.c index f45ae118e..4174ab10c 100644 --- a/src/gpu/sdl/lv_gpu_sdl_draw_blend.c +++ b/src/gpu/sdl/lv_gpu_sdl_draw_blend.c @@ -43,8 +43,8 @@ * GLOBAL FUNCTIONS **********************/ -void _lv_blend_fill(const lv_area_t * clip_area, const lv_area_t * fill_area, lv_color_t color, - lv_opa_t * mask, lv_draw_mask_res_t mask_res, lv_opa_t opa, lv_blend_mode_t mode) +void lv_draw_blend_fill(const lv_area_t * clip_area, const lv_area_t * fill_area, lv_color_t color, + lv_opa_t * mask, lv_draw_mask_res_t mask_res, lv_opa_t opa, lv_blend_mode_t mode) { /*Do not draw transparent things*/ if(opa < LV_OPA_MIN) return; @@ -81,9 +81,9 @@ void _lv_blend_fill(const lv_area_t * clip_area, const lv_area_t * fill_area, lv } } -void _lv_blend_map(const lv_area_t * clip_area, const lv_area_t * map_area, - const lv_color_t * map_buf, lv_opa_t * mask, lv_draw_mask_res_t mask_res, lv_opa_t opa, - lv_blend_mode_t mode) +void lv_draw_blend_map(const lv_area_t * clip_area, const lv_area_t * map_area, + const lv_color_t * map_buf, lv_opa_t * mask, lv_draw_mask_res_t mask_res, lv_opa_t opa, + lv_blend_mode_t mode) { LV_UNUSED(clip_area); LV_UNUSED(map_area);