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
This commit is contained in:
Gabor Kiss-Vamosi
2021-11-26 12:44:10 +01:00
committed by GitHub
parent a712443a50
commit 8f6b1f8d6c
33 changed files with 4544 additions and 4183 deletions

View File

@@ -32,12 +32,14 @@ void lv_example_img_3(void)
lv_anim_set_values(&a, 0, 3600); lv_anim_set_values(&a, 0, 3600);
lv_anim_set_time(&a, 5000); lv_anim_set_time(&a, 5000);
lv_anim_set_repeat_count(&a, LV_ANIM_REPEAT_INFINITE); 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_exec_cb(&a, set_zoom);
lv_anim_set_values(&a, 128, 256); lv_anim_set_values(&a, 128, 256);
lv_anim_set_playback_time(&a, 3000); 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);
} }

View File

@@ -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/extra/extra.mk
include $(LVGL_DIR)/$(LVGL_DIR_NAME)/src/core/lv_core.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/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/font/lv_font.mk
include $(LVGL_DIR)/$(LVGL_DIR_NAME)/src/gpu/lv_gpu.mk include $(LVGL_DIR)/$(LVGL_DIR_NAME)/src/gpu/lv_gpu.mk
include $(LVGL_DIR)/$(LVGL_DIR_NAME)/src/hal/lv_hal.mk include $(LVGL_DIR)/$(LVGL_DIR_NAME)/src/hal/lv_hal.mk

View File

@@ -114,20 +114,22 @@ void lv_init(void)
_lv_group_init(); _lv_group_init();
#if LV_USE_GPU_STM32_DMA2D lv_draw_init();
/*Initialize DMA2D GPU*/
lv_gpu_stm32_dma2d_init();
#endif
#if LV_USE_GPU_NXP_PXP && LV_USE_GPU_NXP_PXP_AUTO_INIT //#if LV_USE_GPU_STM32_DMA2D
if(lv_gpu_nxp_pxp_init(&pxp_default_cfg) != LV_RES_OK) { // /*Initialize DMA2D GPU*/
LV_LOG_ERROR("PXP init error. STOP.\n"); // lv_gpu_stm32_dma2d_init();
for(; ;) ; //#endif
} //
#endif //#if LV_USE_GPU_NXP_PXP && LV_USE_GPU_NXP_PXP_AUTO_INIT
#if LV_USE_GPU_SDL // if(lv_gpu_nxp_pxp_init(&pxp_default_cfg) != LV_RES_OK) {
lv_gpu_sdl_init(); // LV_LOG_ERROR("PXP init error. STOP.\n");
#endif // for(; ;) ;
// }
//#endif
//#if LV_USE_GPU_SDL
// lv_gpu_sdl_init();
//#endif
_lv_obj_style_init(); _lv_obj_style_init();
_lv_ll_init(&LV_GC_ROOT(_lv_disp_ll), sizeof(lv_disp_t)); _lv_ll_init(&LV_GC_ROOT(_lv_disp_ll), sizeof(lv_disp_t));

66
src/draw/lv_draw.c Normal file
View File

@@ -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
**********************/

View File

@@ -26,7 +26,6 @@ extern "C" {
#include "lv_draw_line.h" #include "lv_draw_line.h"
#include "lv_draw_triangle.h" #include "lv_draw_triangle.h"
#include "lv_draw_arc.h" #include "lv_draw_arc.h"
#include "lv_draw_blend.h"
#include "lv_draw_mask.h" #include "lv_draw_mask.h"
/********************* /*********************
@@ -36,11 +35,76 @@ extern "C" {
/********************** /**********************
* TYPEDEFS * 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 * 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 * GLOBAL VARIABLES
**********************/ **********************/

View File

@@ -1,9 +1,9 @@
CSRCS += lv_draw_arc.c CSRCS += lv_draw_arc.c
CSRCS += lv_draw.c
CSRCS += lv_draw_blend.c CSRCS += lv_draw_blend.c
CSRCS += lv_draw_img.c CSRCS += lv_draw_img.c
CSRCS += lv_draw_label.c CSRCS += lv_draw_label.c
CSRCS += lv_draw_line.c CSRCS += lv_draw_line.c
CSRCS += lv_draw_mask.c
CSRCS += lv_draw_rect.c CSRCS += lv_draw_rect.c
CSRCS += lv_draw_triangle.c CSRCS += lv_draw_triangle.c
CSRCS += lv_img_buf.c CSRCS += lv_img_buf.c

View File

@@ -6,46 +6,20 @@
/********************* /*********************
* INCLUDES * INCLUDES
*********************/ *********************/
#include "lv_draw.h"
#include "lv_draw_arc.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 * 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 * 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 * 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 * STATIC VARIABLES
@@ -59,7 +33,7 @@ typedef struct {
* GLOBAL FUNCTIONS * 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)); lv_memset_00(dsc, sizeof(lv_draw_arc_dsc_t));
dsc->width = 1; 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, 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) 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->opa <= LV_OPA_MIN) return;
if(dsc->width == 0) return; if(dsc->width == 0) return;
if(start_angle == end_angle) return; if(start_angle == end_angle) return;
lv_coord_t width = dsc->width; const lv_draw_backend_t * backend = lv_draw_backend_get();
if(width > radius) width = radius; backend->draw_arc(center_x, center_y, radius, start_angle, end_angle, clip_area, dsc);
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*/
} }
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, 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 * 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*/

View File

@@ -13,7 +13,10 @@ extern "C" {
/********************* /*********************
* INCLUDES * 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 * DEFINES
@@ -35,7 +38,7 @@ typedef struct {
* GLOBAL PROTOTYPES * 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.) * Draw an arc. (Can draw pie too with great thickness.)

File diff suppressed because it is too large Load Diff

View File

@@ -30,15 +30,37 @@ extern "C" {
* GLOBAL PROTOTYPES * 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, * Fill an area with a color in the display's draw buffer.
lv_opa_t * mask, lv_draw_mask_res_t mask_res, lv_opa_t opa, lv_blend_mode_t mode); * @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, * Copy a source buffer to the display's draw buffer.
lv_opa_t * mask, lv_draw_mask_res_t mask_res, lv_opa_t opa, lv_blend_mode_t mode); * @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 * MACROS
**********************/ **********************/

View File

@@ -13,11 +13,6 @@
#include "../core/lv_refr.h" #include "../core/lv_refr.h"
#include "../misc/lv_mem.h" #include "../misc/lv_mem.h"
#include "../misc/lv_math.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 * DEFINES
@@ -30,19 +25,12 @@
/********************** /**********************
* STATIC PROTOTYPES * 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, 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 void * src,
const lv_draw_img_dsc_t * draw_dsc); 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 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); static void draw_cleanup(_lv_img_cache_entry_t * cache);
#endif
/********************** /**********************
* STATIC VARIABLES * 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; dsc->antialias = LV_COLOR_DEPTH > 8 ? 1 : 0;
} }
#if LV_USE_EXTERNAL_RENDERER == 0
/** /**
* Draw an image * Draw an image
* @param coords the coordinates of the 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; return;
} }
} }
#endif //LV_USE_GPU_SDL_RENDER
/** /**
* Get the pixel size of a color format in bits * 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 * 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, 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 void * src,
const lv_draw_img_dsc_t * draw_dsc) 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; 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 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); 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; 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*/ /*The whole uncompressed image is not available. Try to read it line-by-line*/
else { 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; 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.y1++;
line.y2++; line.y2++;
y++; 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; 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) 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); LV_UNUSED(cache);
#endif #endif
} }
#endif //LV_USE_GPU_SDL_RENDER

View File

@@ -15,7 +15,7 @@ extern "C" {
*********************/ *********************/
#include "lv_img_decoder.h" #include "lv_img_decoder.h"
#include "lv_img_buf.h" #include "lv_img_buf.h"
#include "lv_draw_blend.h" #include "../misc/lv_style.h"
/********************* /*********************
* DEFINES * DEFINES

View File

@@ -6,6 +6,7 @@
/********************* /*********************
* INCLUDES * INCLUDES
*********************/ *********************/
#include "lv_draw.h"
#include "lv_draw_label.h" #include "lv_draw_label.h"
#include "../misc/lv_math.h" #include "../misc/lv_math.h"
#include "../hal/lv_hal_disp.h" #include "../hal/lv_hal_disp.h"
@@ -13,9 +14,6 @@
#include "../misc/lv_bidi.h" #include "../misc/lv_bidi.h"
#include "../misc/lv_assert.h" #include "../misc/lv_assert.h"
#if LV_USE_GPU_SDL
#include "../gpu/lv_gpu_sdl.h"
#endif
/********************* /*********************
* DEFINES * DEFINES
*********************/ *********************/
@@ -36,17 +34,6 @@ typedef uint8_t cmd_state_t;
* STATIC PROTOTYPES * 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); static uint8_t hex_char_to_num(char hex);
/********************** /**********************
@@ -57,37 +44,6 @@ static uint8_t hex_char_to_num(char hex);
* GLOBAL 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 * 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 * 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)); lv_memset_00(dsc, sizeof(lv_draw_label_dsc_t));
dsc->opa = LV_OPA_COVER; 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, const char * txt,
lv_draw_label_hint_t * hint) lv_draw_label_hint_t * hint)
{ {
if(dsc->opa <= LV_OPA_MIN) return; if(dsc->opa <= LV_OPA_MIN) return;
const lv_font_t * font = dsc->font; const lv_font_t * font = dsc->font;
int32_t w; 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(); 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 * 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) * Convert a hexadecimal characters to a number (0..15)
* @param hex Pointer to a hexadecimal character (0..9, A..F) * @param hex Pointer to a hexadecimal character (0..9, A..F)

View File

@@ -13,10 +13,10 @@ extern "C" {
/********************* /*********************
* INCLUDES * INCLUDES
*********************/ *********************/
#include "lv_draw_blend.h"
#include "../misc/lv_bidi.h" #include "../misc/lv_bidi.h"
#include "../misc/lv_txt.h" #include "../misc/lv_txt.h"
#include "../misc/lv_color.h" #include "../misc/lv_color.h"
#include "../misc/lv_style.h"
/********************* /*********************
* DEFINES * DEFINES
@@ -67,8 +67,6 @@ typedef struct _lv_draw_label_hint_t {
* GLOBAL PROTOTYPES * GLOBAL PROTOTYPES
**********************/ **********************/
//! @cond Doxygen_Suppress
LV_ATTRIBUTE_FAST_MEM void lv_draw_label_dsc_init(lv_draw_label_dsc_t * dsc); 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 lv_draw_label_dsc_t * dsc,
const char * txt, lv_draw_label_hint_t * hint); 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, void lv_draw_letter(const lv_point_t * pos_p, const lv_area_t * clip_area,
const lv_font_t * font_p, const lv_font_t * font_p,
uint32_t letter, lv_color_t color, lv_opa_t opa, lv_blend_mode_t blend_mode); uint32_t letter,
//! @endcond lv_color_t color, lv_opa_t opa, lv_blend_mode_t blend_mode);
/*********************** /***********************
* GLOBAL VARIABLES * 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 * MACROS

View File

@@ -7,8 +7,6 @@
* INCLUDES * INCLUDES
*********************/ *********************/
#include <stdbool.h> #include <stdbool.h>
#include "lv_draw_mask.h"
#include "lv_draw_blend.h"
#include "../core/lv_refr.h" #include "../core/lv_refr.h"
#include "../misc/lv_math.h" #include "../misc/lv_math.h"
@@ -24,16 +22,6 @@
* STATIC PROTOTYPES * 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 * 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(); 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, 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) const lv_draw_line_dsc_t * dsc)
{ {
if(dsc->width == 0) return; if(dsc->width == 0) return;
if(dsc->opa <= LV_OPA_MIN) return; if(dsc->opa <= LV_OPA_MIN) return;
if(point1->x == point2->x && point1->y == point2->y) return; const lv_draw_backend_t * backend = lv_draw_backend_get();
backend->draw_line(point1, point2, clip, dsc);
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 * 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*/
}

View File

@@ -13,7 +13,10 @@ extern "C" {
/********************* /*********************
* INCLUDES * 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 * DEFINES
@@ -38,7 +41,8 @@ typedef struct {
* GLOBAL PROTOTYPES * GLOBAL PROTOTYPES
**********************/ **********************/
//! @cond Doxygen_Suppress LV_ATTRIBUTE_FAST_MEM void lv_draw_line_dsc_init(lv_draw_line_dsc_t * dsc);
/** /**
* Draw a line * Draw a line
* @param point1 first point of the 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, 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); 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 * MACROS

View File

@@ -25,9 +25,6 @@ extern "C" {
#define LV_MASK_ID_INV (-1) #define LV_MASK_ID_INV (-1)
#if LV_DRAW_COMPLEX #if LV_DRAW_COMPLEX
# define _LV_MASK_MAX_NUM 16 # 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 #else
# define _LV_MASK_MAX_NUM 1 # define _LV_MASK_MAX_NUM 1
#endif #endif

File diff suppressed because it is too large Load Diff

View File

@@ -13,16 +13,17 @@ extern "C" {
/********************* /*********************
* INCLUDES * INCLUDES
*********************/ *********************/
#include "lv_draw_blend.h" #include "../lv_conf_internal.h"
#include "../font/lv_font.h" #include "../misc/lv_color.h"
#include "../misc/lv_area.h"
#include "../misc/lv_style.h"
/********************* /*********************
* DEFINES * 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); LV_EXPORT_CONST_INT(LV_RADIUS_CIRCLE);
/********************** /**********************
* TYPEDEFS * TYPEDEFS
**********************/ **********************/
@@ -75,7 +76,6 @@ typedef struct {
LV_ATTRIBUTE_FAST_MEM void lv_draw_rect_dsc_init(lv_draw_rect_dsc_t * dsc); LV_ATTRIBUTE_FAST_MEM void lv_draw_rect_dsc_init(lv_draw_rect_dsc_t * dsc);
//! @endcond
/** /**
* Draw a rectangle * 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); 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 * MACROS
**********************/ **********************/

View File

@@ -6,6 +6,7 @@
/********************* /*********************
* INCLUDES * INCLUDES
*********************/ *********************/
#include "lv_draw.h"
#include "lv_draw_triangle.h" #include "lv_draw_triangle.h"
#include "../misc/lv_math.h" #include "../misc/lv_math.h"
#include "../misc/lv_mem.h" #include "../misc/lv_mem.h"
@@ -34,24 +35,6 @@
* GLOBAL FUNCTIONS * 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 * Draw a polygon. Only convex polygons are supported
* @param points an array of points * @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, 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) 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*/ const lv_draw_backend_t * backend = lv_draw_backend_get();
lv_point_t * p = lv_mem_buf_get(point_cnt * sizeof(lv_point_t)); backend->draw_polygon(points, point_cnt, clip_area, draw_dsc);
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; void lv_draw_triangle(const lv_point_t points[], const lv_area_t * clip_area,
if(point_cnt < 3) { const lv_draw_rect_dsc_t * draw_dsc)
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}; const lv_draw_backend_t * backend = lv_draw_backend_get();
backend->draw_polygon(points, 3, clip_area, draw_dsc);
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*/
} }
/********************** /**********************

59
src/draw/sw/lv_draw_sw.c Normal file
View File

@@ -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
**********************/

82
src/draw/sw/lv_draw_sw.h Normal file
View File

@@ -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*/

14
src/draw/sw/lv_draw_sw.mk Normal file
View File

@@ -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"

View File

@@ -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*/

View File

@@ -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

View File

@@ -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
**********************/

View File

@@ -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*/

View File

@@ -0,0 +1,485 @@
/**
* @file lv_draw_line.c
*
*/
/*********************
* INCLUDES
*********************/
#include <stdbool.h>
#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*/
}

View File

@@ -6,12 +6,12 @@
/********************* /*********************
* INCLUDES * INCLUDES
*********************/ *********************/
#include "lv_draw_mask.h" #include "lv_draw_sw.h"
#if LV_DRAW_COMPLEX #if LV_DRAW_COMPLEX
#include "../misc/lv_math.h" #include "../../misc/lv_math.h"
#include "../misc/lv_log.h" #include "../../misc/lv_log.h"
#include "../misc/lv_assert.h" #include "../../misc/lv_assert.h"
#include "../misc/lv_gc.h" #include "../../misc/lv_gc.h"
/********************* /*********************
* DEFINES * DEFINES

View File

@@ -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
**********************/

File diff suppressed because it is too large Load Diff

View File

@@ -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) 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; lv_rlottie_t * rlottie = (lv_rlottie_t *) obj;
if(rlottie_desc_create) { 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) 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; lv_rlottie_t * rlottie = (lv_rlottie_t *) obj;
if(rlottie->animation) { if(rlottie->animation) {

View File

@@ -43,8 +43,8 @@
* GLOBAL FUNCTIONS * GLOBAL FUNCTIONS
**********************/ **********************/
void _lv_blend_fill(const lv_area_t * clip_area, const lv_area_t * fill_area, lv_color_t color, 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_opa_t * mask, lv_draw_mask_res_t mask_res, lv_opa_t opa, lv_blend_mode_t mode)
{ {
/*Do not draw transparent things*/ /*Do not draw transparent things*/
if(opa < LV_OPA_MIN) return; 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, 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, 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_blend_mode_t mode)
{ {
LV_UNUSED(clip_area); LV_UNUSED(clip_area);
LV_UNUSED(map_area); LV_UNUSED(map_area);