diff options
| author | jacqueline <me@jacqueline.id.au> | 2023-06-01 15:41:47 +1000 |
|---|---|---|
| committer | jacqueline <me@jacqueline.id.au> | 2023-06-01 15:41:47 +1000 |
| commit | dd27c3530432ea0b09f01e604bf577f31d8ef841 (patch) | |
| tree | bbf86cf81a78f0ff0b07f31f1c390db473f26fd3 /lib/lvgl/src/draw/sw | |
| parent | 6fd588e970470b15936187980829916d0dbe77bb (diff) | |
| download | tangara-fw-dd27c3530432ea0b09f01e604bf577f31d8ef841.tar.gz | |
convert lvgl from submodule to a plain old directory
Diffstat (limited to 'lib/lvgl/src/draw/sw')
| m--------- | lib/lvgl | 0 | ||||
| -rw-r--r-- | lib/lvgl/src/draw/sw/lv_draw_sw.c | 108 | ||||
| -rw-r--r-- | lib/lvgl/src/draw/sw/lv_draw_sw.h | 104 | ||||
| -rw-r--r-- | lib/lvgl/src/draw/sw/lv_draw_sw.mk | 17 | ||||
| -rw-r--r-- | lib/lvgl/src/draw/sw/lv_draw_sw_arc.c | 537 | ||||
| -rw-r--r-- | lib/lvgl/src/draw/sw/lv_draw_sw_blend.c | 1039 | ||||
| -rw-r--r-- | lib/lvgl/src/draw/sw/lv_draw_sw_blend.h | 69 | ||||
| -rw-r--r-- | lib/lvgl/src/draw/sw/lv_draw_sw_dither.c | 213 | ||||
| -rw-r--r-- | lib/lvgl/src/draw/sw/lv_draw_sw_dither.h | 70 | ||||
| -rw-r--r-- | lib/lvgl/src/draw/sw/lv_draw_sw_gradient.c | 346 | ||||
| -rw-r--r-- | lib/lvgl/src/draw/sw/lv_draw_sw_gradient.h | 97 | ||||
| -rw-r--r-- | lib/lvgl/src/draw/sw/lv_draw_sw_img.c | 297 | ||||
| -rw-r--r-- | lib/lvgl/src/draw/sw/lv_draw_sw_layer.c | 150 | ||||
| -rw-r--r-- | lib/lvgl/src/draw/sw/lv_draw_sw_letter.c | 573 | ||||
| -rw-r--r-- | lib/lvgl/src/draw/sw/lv_draw_sw_line.c | 443 | ||||
| -rw-r--r-- | lib/lvgl/src/draw/sw/lv_draw_sw_polygon.c | 207 | ||||
| -rw-r--r-- | lib/lvgl/src/draw/sw/lv_draw_sw_rect.c | 1436 | ||||
| -rw-r--r-- | lib/lvgl/src/draw/sw/lv_draw_sw_transform.c | 496 |
18 files changed, 6202 insertions, 0 deletions
diff --git a/lib/lvgl b/lib/lvgl deleted file mode 160000 -Subproject 0732400e7b564dd0e7dc4a924619d8e19c5b23a diff --git a/lib/lvgl/src/draw/sw/lv_draw_sw.c b/lib/lvgl/src/draw/sw/lv_draw_sw.c new file mode 100644 index 00000000..1c0c6d4a --- /dev/null +++ b/lib/lvgl/src/draw/sw/lv_draw_sw.c @@ -0,0 +1,108 @@ +/** + * @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_ctx(lv_disp_drv_t * drv, lv_draw_ctx_t * draw_ctx) +{ + LV_UNUSED(drv); + + lv_draw_sw_ctx_t * draw_sw_ctx = (lv_draw_sw_ctx_t *) draw_ctx; + lv_memset_00(draw_sw_ctx, sizeof(lv_draw_sw_ctx_t)); + + draw_sw_ctx->base_draw.draw_arc = lv_draw_sw_arc; + draw_sw_ctx->base_draw.draw_rect = lv_draw_sw_rect; + draw_sw_ctx->base_draw.draw_bg = lv_draw_sw_bg; + draw_sw_ctx->base_draw.draw_letter = lv_draw_sw_letter; + draw_sw_ctx->base_draw.draw_img_decoded = lv_draw_sw_img_decoded; + draw_sw_ctx->base_draw.draw_line = lv_draw_sw_line; + draw_sw_ctx->base_draw.draw_polygon = lv_draw_sw_polygon; +#if LV_DRAW_COMPLEX + draw_sw_ctx->base_draw.draw_transform = lv_draw_sw_transform; +#endif + draw_sw_ctx->base_draw.wait_for_finish = lv_draw_sw_wait_for_finish; + draw_sw_ctx->base_draw.buffer_copy = lv_draw_sw_buffer_copy; + draw_sw_ctx->base_draw.layer_init = lv_draw_sw_layer_create; + draw_sw_ctx->base_draw.layer_adjust = lv_draw_sw_layer_adjust; + draw_sw_ctx->base_draw.layer_blend = lv_draw_sw_layer_blend; + draw_sw_ctx->base_draw.layer_destroy = lv_draw_sw_layer_destroy; + draw_sw_ctx->blend = lv_draw_sw_blend_basic; + draw_ctx->layer_instance_size = sizeof(lv_draw_sw_layer_ctx_t); +} + +void lv_draw_sw_deinit_ctx(lv_disp_drv_t * drv, lv_draw_ctx_t * draw_ctx) +{ + LV_UNUSED(drv); + + lv_draw_sw_ctx_t * draw_sw_ctx = (lv_draw_sw_ctx_t *) draw_ctx; + lv_memset_00(draw_sw_ctx, sizeof(lv_draw_sw_ctx_t)); +} + +void lv_draw_sw_wait_for_finish(lv_draw_ctx_t * draw_ctx) +{ + LV_UNUSED(draw_ctx); + /*Nothing to wait for*/ +} + +void lv_draw_sw_buffer_copy(lv_draw_ctx_t * draw_ctx, + void * dest_buf, lv_coord_t dest_stride, const lv_area_t * dest_area, + void * src_buf, lv_coord_t src_stride, const lv_area_t * src_area) +{ + LV_UNUSED(draw_ctx); + + lv_color_t * dest_bufc = dest_buf; + lv_color_t * src_bufc = src_buf; + + /*Got the first pixel of each buffer*/ + dest_bufc += dest_stride * dest_area->y1; + dest_bufc += dest_area->x1; + + src_bufc += src_stride * src_area->y1; + src_bufc += src_area->x1; + + uint32_t line_length = lv_area_get_width(dest_area) * sizeof(lv_color_t); + lv_coord_t y; + for(y = dest_area->y1; y <= dest_area->y2; y++) { + lv_memcpy(dest_bufc, src_bufc, line_length); + dest_bufc += dest_stride; + src_bufc += src_stride; + } +} + +/********************** + * STATIC FUNCTIONS + **********************/ diff --git a/lib/lvgl/src/draw/sw/lv_draw_sw.h b/lib/lvgl/src/draw/sw/lv_draw_sw.h new file mode 100644 index 00000000..1618649c --- /dev/null +++ b/lib/lvgl/src/draw/sw/lv_draw_sw.h @@ -0,0 +1,104 @@ +/** + * @file lv_draw_sw.h + * + */ + +#ifndef LV_DRAW_SW_H +#define LV_DRAW_SW_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ +#include "lv_draw_sw_blend.h" +#include "../lv_draw.h" +#include "../../misc/lv_area.h" +#include "../../misc/lv_color.h" +#include "../../hal/lv_hal_disp.h" + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +struct _lv_disp_drv_t; + +typedef struct { + lv_draw_ctx_t base_draw; + + /** Fill an area of the destination buffer with a color*/ + void (*blend)(lv_draw_ctx_t * draw_ctx, const lv_draw_sw_blend_dsc_t * dsc); +} lv_draw_sw_ctx_t; + +typedef struct { + lv_draw_layer_ctx_t base_draw; + + uint32_t buf_size_bytes: 31; + uint32_t has_alpha : 1; +} lv_draw_sw_layer_ctx_t; + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +void lv_draw_sw_init_ctx(struct _lv_disp_drv_t * drv, lv_draw_ctx_t * draw_ctx); +void lv_draw_sw_deinit_ctx(struct _lv_disp_drv_t * drv, lv_draw_ctx_t * draw_ctx); + +void lv_draw_sw_wait_for_finish(lv_draw_ctx_t * draw_ctx); + +void lv_draw_sw_arc(lv_draw_ctx_t * draw_ctx, const lv_draw_arc_dsc_t * dsc, const lv_point_t * center, uint16_t radius, + uint16_t start_angle, uint16_t end_angle); + +void lv_draw_sw_rect(lv_draw_ctx_t * draw_ctx, const lv_draw_rect_dsc_t * dsc, const lv_area_t * coords); + +void lv_draw_sw_bg(lv_draw_ctx_t * draw_ctx, const lv_draw_rect_dsc_t * dsc, const lv_area_t * coords); +void lv_draw_sw_letter(lv_draw_ctx_t * draw_ctx, const lv_draw_label_dsc_t * dsc, const lv_point_t * pos_p, + uint32_t letter); + +LV_ATTRIBUTE_FAST_MEM void lv_draw_sw_img_decoded(struct _lv_draw_ctx_t * draw_ctx, const lv_draw_img_dsc_t * draw_dsc, + const lv_area_t * coords, const uint8_t * src_buf, lv_img_cf_t cf); + +LV_ATTRIBUTE_FAST_MEM void lv_draw_sw_line(struct _lv_draw_ctx_t * draw_ctx, const lv_draw_line_dsc_t * dsc, + const lv_point_t * point1, const lv_point_t * point2); + +void lv_draw_sw_polygon(struct _lv_draw_ctx_t * draw_ctx, const lv_draw_rect_dsc_t * draw_dsc, + const lv_point_t * points, uint16_t point_cnt); + +void lv_draw_sw_buffer_copy(lv_draw_ctx_t * draw_ctx, + void * dest_buf, lv_coord_t dest_stride, const lv_area_t * dest_area, + void * src_buf, lv_coord_t src_stride, const lv_area_t * src_area); + +void lv_draw_sw_transform(lv_draw_ctx_t * draw_ctx, const lv_area_t * dest_area, const void * src_buf, + lv_coord_t src_w, lv_coord_t src_h, lv_coord_t src_stride, + const lv_draw_img_dsc_t * draw_dsc, lv_img_cf_t cf, lv_color_t * cbuf, lv_opa_t * abuf); + +struct _lv_draw_layer_ctx_t * lv_draw_sw_layer_create(struct _lv_draw_ctx_t * draw_ctx, lv_draw_layer_ctx_t * layer_ctx, + lv_draw_layer_flags_t flags); + +void lv_draw_sw_layer_adjust(struct _lv_draw_ctx_t * draw_ctx, struct _lv_draw_layer_ctx_t * layer_ctx, + lv_draw_layer_flags_t flags); + +void lv_draw_sw_layer_blend(struct _lv_draw_ctx_t * draw_ctx, struct _lv_draw_layer_ctx_t * layer_ctx, + const lv_draw_img_dsc_t * draw_dsc); + +void lv_draw_sw_layer_destroy(lv_draw_ctx_t * draw_ctx, lv_draw_layer_ctx_t * layer_ctx); + +/*********************** + * GLOBAL VARIABLES + ***********************/ + +/********************** + * MACROS + **********************/ + +#ifdef __cplusplus +} /*extern "C"*/ +#endif + +#endif /*LV_DRAW_SW_H*/ diff --git a/lib/lvgl/src/draw/sw/lv_draw_sw.mk b/lib/lvgl/src/draw/sw/lv_draw_sw.mk new file mode 100644 index 00000000..4625cbcf --- /dev/null +++ b/lib/lvgl/src/draw/sw/lv_draw_sw.mk @@ -0,0 +1,17 @@ +CSRCS += lv_draw_sw.c +CSRCS += lv_draw_sw_arc.c +CSRCS += lv_draw_sw_blend.c +CSRCS += lv_draw_sw_dither.c +CSRCS += lv_draw_sw_gradient.c +CSRCS += lv_draw_sw_img.c +CSRCS += lv_draw_sw_letter.c +CSRCS += lv_draw_sw_line.c +CSRCS += lv_draw_sw_polygon.c +CSRCS += lv_draw_sw_rect.c +CSRCS += lv_draw_sw_transform.c +CSRCS += lv_draw_sw_layer.c + +DEPPATH += --dep-path $(LVGL_DIR)/$(LVGL_DIR_NAME)/src/draw/sw +VPATH += :$(LVGL_DIR)/$(LVGL_DIR_NAME)/src/draw/sw + +CFLAGS += "-I$(LVGL_DIR)/$(LVGL_DIR_NAME)/src/draw/sw" diff --git a/lib/lvgl/src/draw/sw/lv_draw_sw_arc.c b/lib/lvgl/src/draw/sw/lv_draw_sw_arc.c new file mode 100644 index 00000000..3ed62b6e --- /dev/null +++ b/lib/lvgl/src/draw/sw/lv_draw_sw_arc.c @@ -0,0 +1,537 @@ +/** + * @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" +#include "../lv_draw.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 { + const lv_point_t * center; + 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; + lv_draw_ctx_t * draw_ctx; +} 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_draw_ctx_t * draw_ctx, const lv_draw_arc_dsc_t * dsc, const lv_point_t * center, uint16_t radius, + uint16_t start_angle, uint16_t end_angle) +{ +#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; + bool mask_in_param_valid = false; + 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_param_valid = 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(draw_ctx, &cir_dsc, &area_out); + + lv_draw_mask_remove_id(mask_out_id); + if(mask_in_id != LV_MASK_ID_INV) lv_draw_mask_remove_id(mask_in_id); + + lv_draw_mask_free_param(&mask_out_param); + if(mask_in_param_valid) { + lv_draw_mask_free_param(&mask_in_param); + } + + 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; + } + + const lv_area_t * clip_area_ori = draw_ctx->clip_area; + + 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 = center; + 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.draw_ctx = draw_ctx; + + draw_quarter_0(&q_dsc); + draw_quarter_1(&q_dsc); + draw_quarter_2(&q_dsc); + draw_quarter_3(&q_dsc); + } + else { + lv_draw_rect(draw_ctx, &cir_dsc, &area_out); + } + + lv_draw_mask_free_param(&mask_angle_param); + lv_draw_mask_free_param(&mask_out_param); + if(mask_in_param_valid) { + 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_ori, &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); + + draw_ctx->clip_area = &clip_area2; + lv_draw_rect(draw_ctx, &cir_dsc, &area_out); + 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_ori, &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); + + draw_ctx->clip_area = &clip_area2; + lv_draw_rect(draw_ctx, &cir_dsc, &area_out); + lv_draw_mask_remove_id(mask_end_id); + lv_draw_mask_free_param(&mask_end_param); + } + draw_ctx->clip_area = clip_area_ori; + } +#else + LV_LOG_WARN("Can't draw arc with LV_DRAW_COMPLEX == 0"); + LV_UNUSED(center); + LV_UNUSED(radius); + LV_UNUSED(start_angle); + LV_UNUSED(end_angle); + LV_UNUSED(draw_ctx); + LV_UNUSED(dsc); +#endif /*LV_DRAW_COMPLEX*/ +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +#if LV_DRAW_COMPLEX +static void draw_quarter_0(quarter_draw_dsc_t * q) +{ + const lv_area_t * clip_area_ori = q->draw_ctx->clip_area; + 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, clip_area_ori); + if(ok) { + q->draw_ctx->clip_area = &quarter_area; + lv_draw_rect(q->draw_ctx, q->draw_dsc, q->draw_area); + } + } + 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, clip_area_ori); + if(ok) { + q->draw_ctx->clip_area = &quarter_area; + lv_draw_rect(q->draw_ctx, q->draw_dsc, q->draw_area); + } + } + 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, clip_area_ori); + if(ok) { + q->draw_ctx->clip_area = &quarter_area; + lv_draw_rect(q->draw_ctx, q->draw_dsc, q->draw_area); + } + } + } + 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, clip_area_ori); + if(ok) { + q->draw_ctx->clip_area = &quarter_area; + lv_draw_rect(q->draw_ctx, q->draw_dsc, q->draw_area); + } + } + q->draw_ctx->clip_area = clip_area_ori; +} + +static void draw_quarter_1(quarter_draw_dsc_t * q) +{ + const lv_area_t * clip_area_ori = q->draw_ctx->clip_area; + 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, clip_area_ori); + if(ok) { + q->draw_ctx->clip_area = &quarter_area; + lv_draw_rect(q->draw_ctx, q->draw_dsc, q->draw_area); + } + } + 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, clip_area_ori); + if(ok) { + q->draw_ctx->clip_area = &quarter_area; + lv_draw_rect(q->draw_ctx, q->draw_dsc, q->draw_area); + } + } + 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, clip_area_ori); + if(ok) { + q->draw_ctx->clip_area = &quarter_area; + lv_draw_rect(q->draw_ctx, q->draw_dsc, q->draw_area); + } + } + } + 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, clip_area_ori); + if(ok) { + q->draw_ctx->clip_area = &quarter_area; + lv_draw_rect(q->draw_ctx, q->draw_dsc, q->draw_area); + } + } + q->draw_ctx->clip_area = clip_area_ori; +} + +static void draw_quarter_2(quarter_draw_dsc_t * q) +{ + const lv_area_t * clip_area_ori = q->draw_ctx->clip_area; + 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, clip_area_ori); + if(ok) { + q->draw_ctx->clip_area = &quarter_area; + lv_draw_rect(q->draw_ctx, q->draw_dsc, q->draw_area); + } + } + 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, clip_area_ori); + if(ok) { + q->draw_ctx->clip_area = &quarter_area; + lv_draw_rect(q->draw_ctx, q->draw_dsc, q->draw_area); + } + } + 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, clip_area_ori); + if(ok) { + q->draw_ctx->clip_area = &quarter_area; + lv_draw_rect(q->draw_ctx, q->draw_dsc, q->draw_area); + } + } + } + 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, clip_area_ori); + if(ok) { + q->draw_ctx->clip_area = &quarter_area; + lv_draw_rect(q->draw_ctx, q->draw_dsc, q->draw_area); + } + } + q->draw_ctx->clip_area = clip_area_ori; +} + +static void draw_quarter_3(quarter_draw_dsc_t * q) +{ + const lv_area_t * clip_area_ori = q->draw_ctx->clip_area; + 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, clip_area_ori); + if(ok) { + q->draw_ctx->clip_area = &quarter_area; + lv_draw_rect(q->draw_ctx, q->draw_dsc, q->draw_area); + } + } + 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, clip_area_ori); + if(ok) { + q->draw_ctx->clip_area = &quarter_area; + lv_draw_rect(q->draw_ctx, q->draw_dsc, q->draw_area); + } + } + 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, clip_area_ori); + if(ok) { + q->draw_ctx->clip_area = &quarter_area; + lv_draw_rect(q->draw_ctx, q->draw_dsc, q->draw_area); + } + } + } + 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, clip_area_ori); + if(ok) { + q->draw_ctx->clip_area = &quarter_area; + lv_draw_rect(q->draw_ctx, q->draw_dsc, q->draw_area); + } + } + + q->draw_ctx->clip_area = clip_area_ori; +} + +static void get_rounded_area(int16_t angle, lv_coord_t radius, uint8_t thickness, lv_area_t * res_area) +{ + const uint8_t ps = 8; + const uint8_t pa = 127; + + int32_t thick_half = thickness / 2; + uint8_t thick_corr = (thickness & 0x01) ? 0 : 1; + + int32_t cir_x; + int32_t cir_y; + + cir_x = ((radius - thick_half) * lv_trigo_sin(90 - angle)) >> (LV_TRIGO_SHIFT - ps); + cir_y = ((radius - thick_half) * lv_trigo_sin(angle)) >> (LV_TRIGO_SHIFT - ps); + + /*Actually the center of the pixel need to be calculated so apply 1/2 px offset*/ + if(cir_x > 0) { + cir_x = (cir_x - pa) >> ps; + res_area->x1 = cir_x - thick_half + thick_corr; + res_area->x2 = cir_x + thick_half; + } + else { + cir_x = (cir_x + pa) >> ps; + res_area->x1 = cir_x - thick_half; + res_area->x2 = cir_x + thick_half - thick_corr; + } + + if(cir_y > 0) { + cir_y = (cir_y - pa) >> ps; + res_area->y1 = cir_y - thick_half + thick_corr; + res_area->y2 = cir_y + thick_half; + } + else { + cir_y = (cir_y + pa) >> ps; + res_area->y1 = cir_y - thick_half; + res_area->y2 = cir_y + thick_half - thick_corr; + } +} + +#endif /*LV_DRAW_COMPLEX*/ diff --git a/lib/lvgl/src/draw/sw/lv_draw_sw_blend.c b/lib/lvgl/src/draw/sw/lv_draw_sw_blend.c new file mode 100644 index 00000000..428aba62 --- /dev/null +++ b/lib/lvgl/src/draw/sw/lv_draw_sw_blend.c @@ -0,0 +1,1039 @@ +/** + * @file lv_draw_sw_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 + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ + +static void fill_set_px(lv_color_t * dest_buf, const lv_area_t * blend_area, lv_coord_t dest_stride, + lv_color_t color, lv_opa_t opa, const lv_opa_t * mask, lv_coord_t mask_stide); + +LV_ATTRIBUTE_FAST_MEM static void fill_normal(lv_color_t * dest_buf, const lv_area_t * dest_area, + lv_coord_t dest_stride, lv_color_t color, lv_opa_t opa, const lv_opa_t * mask, lv_coord_t mask_stride); + + +#if LV_COLOR_SCREEN_TRANSP +LV_ATTRIBUTE_FAST_MEM static void fill_argb(lv_color_t * dest_buf, const lv_area_t * dest_area, + lv_coord_t dest_stride, lv_color_t color, lv_opa_t opa, const lv_opa_t * mask, lv_coord_t mask_stride); +#endif /*LV_COLOR_SCREEN_TRANSP*/ + +#if LV_DRAW_COMPLEX +static void fill_blended(lv_color_t * dest_buf, const lv_area_t * dest_area, lv_coord_t dest_stride, lv_color_t color, + lv_opa_t opa, const lv_opa_t * mask, lv_coord_t mask_stride, lv_blend_mode_t blend_mode); +#endif /*LV_DRAW_COMPLEX*/ + +static void map_set_px(lv_color_t * dest_buf, const lv_area_t * dest_area, lv_coord_t dest_stride, + const lv_color_t * src_buf, lv_coord_t src_stride, lv_opa_t opa, const lv_opa_t * mask, lv_coord_t mask_stride); + +LV_ATTRIBUTE_FAST_MEM static void map_normal(lv_color_t * dest_buf, const lv_area_t * dest_area, lv_coord_t dest_stride, + const lv_color_t * src_buf, lv_coord_t src_stride, lv_opa_t opa, const lv_opa_t * mask, lv_coord_t mask_stride); + +#if LV_COLOR_SCREEN_TRANSP +LV_ATTRIBUTE_FAST_MEM static void map_argb(lv_color_t * dest_buf, const lv_area_t * dest_area, lv_coord_t dest_stride, + const lv_color_t * src_buf, lv_coord_t src_stride, lv_opa_t opa, + const lv_opa_t * mask, lv_coord_t mask_stride, lv_blend_mode_t blend_mode); + +#endif /*LV_COLOR_SCREEN_TRANSP*/ + +#if LV_DRAW_COMPLEX +static void map_blended(lv_color_t * dest_buf, const lv_area_t * dest_area, lv_coord_t dest_stride, + const lv_color_t * src_buf, lv_coord_t src_stride, lv_opa_t opa, + const lv_opa_t * mask, lv_coord_t mask_stride, lv_blend_mode_t blend_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 + **********************/ +#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++; + +#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++; + + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +void lv_draw_sw_blend(lv_draw_ctx_t * draw_ctx, const lv_draw_sw_blend_dsc_t * dsc) +{ + /*Do not draw transparent things*/ + if(dsc->opa <= LV_OPA_MIN) return; + + lv_area_t blend_area; + if(!_lv_area_intersect(&blend_area, dsc->blend_area, draw_ctx->clip_area)) return; + + if(draw_ctx->wait_for_finish) draw_ctx->wait_for_finish(draw_ctx); + + ((lv_draw_sw_ctx_t *)draw_ctx)->blend(draw_ctx, dsc); +} + +LV_ATTRIBUTE_FAST_MEM void lv_draw_sw_blend_basic(lv_draw_ctx_t * draw_ctx, const lv_draw_sw_blend_dsc_t * dsc) +{ + const lv_opa_t * mask; + if(dsc->mask_buf == NULL) mask = NULL; + if(dsc->mask_buf && dsc->mask_res == LV_DRAW_MASK_RES_TRANSP) return; + else if(dsc->mask_res == LV_DRAW_MASK_RES_FULL_COVER) mask = NULL; + else mask = dsc->mask_buf; + + lv_coord_t dest_stride = lv_area_get_width(draw_ctx->buf_area); + + lv_area_t blend_area; + if(!_lv_area_intersect(&blend_area, dsc->blend_area, draw_ctx->clip_area)) return; + + lv_disp_t * disp = _lv_refr_get_disp_refreshing(); + lv_color_t * dest_buf = draw_ctx->buf; + if(disp->driver->set_px_cb == NULL) { + if(disp->driver->screen_transp == 0) { + dest_buf += dest_stride * (blend_area.y1 - draw_ctx->buf_area->y1) + (blend_area.x1 - draw_ctx->buf_area->x1); + } + else { + /*With LV_COLOR_DEPTH 16 it means ARGB8565 (3 bytes format)*/ + uint8_t * dest_buf8 = (uint8_t *) dest_buf; + dest_buf8 += dest_stride * (blend_area.y1 - draw_ctx->buf_area->y1) * LV_IMG_PX_SIZE_ALPHA_BYTE; + dest_buf8 += (blend_area.x1 - draw_ctx->buf_area->x1) * LV_IMG_PX_SIZE_ALPHA_BYTE; + dest_buf = (lv_color_t *)dest_buf8; + } + } + + + const lv_color_t * src_buf = dsc->src_buf; + lv_coord_t src_stride; + if(src_buf) { + src_stride = lv_area_get_width(dsc->blend_area); + src_buf += src_stride * (blend_area.y1 - dsc->blend_area->y1) + (blend_area.x1 - dsc->blend_area->x1); + } + else { + src_stride = 0; + } + + lv_coord_t mask_stride; + if(mask) { + mask_stride = lv_area_get_width(dsc->mask_area); + mask += mask_stride * (blend_area.y1 - dsc->mask_area->y1) + (blend_area.x1 - dsc->mask_area->x1); + } + else { + mask_stride = 0; + } + + lv_area_move(&blend_area, -draw_ctx->buf_area->x1, -draw_ctx->buf_area->y1); + + + if(disp->driver->set_px_cb) { + if(dsc->src_buf == NULL) { + fill_set_px(dest_buf, &blend_area, dest_stride, dsc->color, dsc->opa, mask, mask_stride); + } + else { + map_set_px(dest_buf, &blend_area, dest_stride, src_buf, src_stride, dsc->opa, mask, mask_stride); + } + } +#if LV_COLOR_SCREEN_TRANSP + else if(disp->driver->screen_transp) { + if(dsc->src_buf == NULL) { + fill_argb(dest_buf, &blend_area, dest_stride, dsc->color, dsc->opa, mask, mask_stride); + } + else { + map_argb(dest_buf, &blend_area, dest_stride, src_buf, src_stride, dsc->opa, mask, mask_stride, dsc->blend_mode); + } + } +#endif + else if(dsc->blend_mode == LV_BLEND_MODE_NORMAL) { + if(dsc->src_buf == NULL) { + fill_normal(dest_buf, &blend_area, dest_stride, dsc->color, dsc->opa, mask, mask_stride); + } + else { + map_normal(dest_buf, &blend_area, dest_stride, src_buf, src_stride, dsc->opa, mask, mask_stride); + } + } + else { +#if LV_DRAW_COMPLEX + if(dsc->src_buf == NULL) { + fill_blended(dest_buf, &blend_area, dest_stride, dsc->color, dsc->opa, mask, mask_stride, dsc->blend_mode); + } + else { + map_blended(dest_buf, &blend_area, dest_stride, src_buf, src_stride, dsc->opa, mask, mask_stride, dsc->blend_mode); + } +#endif + } +} + + +/********************** + * STATIC FUNCTIONS + **********************/ + +static void fill_set_px(lv_color_t * dest_buf, const lv_area_t * blend_area, lv_coord_t dest_stride, + lv_color_t color, lv_opa_t opa, const lv_opa_t * mask, lv_coord_t mask_stide) +{ + lv_disp_t * disp = _lv_refr_get_disp_refreshing(); + + int32_t x; + int32_t y; + + if(mask == NULL) { + for(y = blend_area->y1; y <= blend_area->y2; y++) { + for(x = blend_area->x1; x <= blend_area->x2; x++) { + disp->driver->set_px_cb(disp->driver, (void *)dest_buf, dest_stride, x, y, color, opa); + } + } + } + else { + int32_t w = lv_area_get_width(blend_area); + int32_t h = lv_area_get_height(blend_area); + + for(y = 0; y < h; y++) { + for(x = 0; x < w; x++) { + if(mask[x]) { + + + disp->driver->set_px_cb(disp->driver, (void *)dest_buf, dest_stride, blend_area->x1 + x, blend_area->y1 + y, color, + (uint32_t)((uint32_t)opa * mask[x]) >> 8); + } + } + mask += mask_stide; + } + } +} + +LV_ATTRIBUTE_FAST_MEM static void fill_normal(lv_color_t * dest_buf, const lv_area_t * dest_area, + lv_coord_t dest_stride, lv_color_t color, lv_opa_t opa, const lv_opa_t * mask, lv_coord_t mask_stride) +{ + int32_t w = lv_area_get_width(dest_area); + int32_t h = lv_area_get_height(dest_area); + + int32_t x; + int32_t y; + + /*No mask*/ + if(mask == NULL) { + if(opa >= LV_OPA_MAX) { + for(y = 0; y < h; y++) { + lv_color_fill(dest_buf, color, w); + dest_buf += dest_stride; + } + } + /*Has 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); + +#if LV_COLOR_MIX_ROUND_OFS == 0 && LV_COLOR_DEPTH == 16 + /*lv_color_mix work with an optimized algorithm with 16 bit color depth. + *However, it introduces some rounded error on opa. + *Introduce the same error here too to make lv_color_premult produces the same result */ + opa = (uint32_t)((uint32_t)opa + 4) >> 3; + opa = opa << 3; +#endif + + uint16_t color_premult[3]; + lv_color_premult(color, opa, color_premult); + lv_opa_t opa_inv = 255 - opa; + + for(y = 0; y < h; y++) { + for(x = 0; x < w; x++) { + if(last_dest_color.full != dest_buf[x].full) { + last_dest_color = dest_buf[x]; + 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 { +#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) { + int32_t x_end4 = w - 4; + for(y = 0; y < h; y++) { + for(x = 0; x < 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 < w ; x++) { + FILL_NORMAL_MASK_PX(color) + } + dest_buf += (dest_stride - w); + mask += (mask_stride - w); + } + } + /*With opacity*/ + 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 < h; y++) { + for(x = 0; x < w; x++) { + if(*mask) { + if(*mask != last_mask) opa_tmp = *mask == LV_OPA_COVER ? opa : + (uint32_t)((uint32_t)(*mask) * opa) >> 8; + if(*mask != last_mask || last_dest_color.full != dest_buf[x].full) { + 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; + last_dest_color.full = dest_buf[x].full; + } + dest_buf[x] = last_res_color; + } + mask++; + } + dest_buf += dest_stride; + mask += (mask_stride - w); + } + } + } +} + +#if LV_COLOR_SCREEN_TRANSP +static inline void set_px_argb(uint8_t * buf, lv_color_t color, lv_opa_t opa) +{ + lv_color_t bg_color; + lv_color_t res_color; + lv_opa_t bg_opa = buf[LV_IMG_PX_SIZE_ALPHA_BYTE - 1]; +#if LV_COLOR_DEPTH == 8 + bg_color.full = buf[0]; + lv_color_mix_with_alpha(bg_color, bg_opa, color, opa, &res_color, &buf[1]); + if(buf[1] <= LV_OPA_MIN) return; + buf[0] = res_color.full; +#elif LV_COLOR_DEPTH == 16 + bg_color.full = buf[0] + (buf[1] << 8); + lv_color_mix_with_alpha(bg_color, bg_opa, color, opa, &res_color, &buf[2]); + if(buf[2] <= LV_OPA_MIN) return; + buf[0] = res_color.full & 0xff; + buf[1] = res_color.full >> 8; +#elif LV_COLOR_DEPTH == 32 + bg_color = *((lv_color_t *)buf); + lv_color_mix_with_alpha(bg_color, bg_opa, color, opa, &res_color, &buf[3]); + if(buf[3] <= LV_OPA_MIN) return; + buf[0] = res_color.ch.blue; + buf[1] = res_color.ch.green; + buf[2] = res_color.ch.red; +#endif +} + +static inline void set_px_argb_blend(uint8_t * buf, lv_color_t color, lv_opa_t opa, lv_color_t (*blend_fp)(lv_color_t, + lv_color_t, lv_opa_t)) +{ + static lv_color_t last_dest_color; + static lv_color_t last_src_color; + static lv_color_t last_res_color; + static uint32_t last_opa = 0xffff; /*Set to an invalid value for first*/ + + lv_color_t bg_color; + + /*Get the BG color*/ +#if LV_COLOR_DEPTH == 8 + if(buf[1] <= LV_OPA_MIN) return; + bg_color.full = buf[0]; +#elif LV_COLOR_DEPTH == 16 + if(buf[2] <= LV_OPA_MIN) return; + bg_color.full = buf[0] + (buf[1] << 8); +#elif LV_COLOR_DEPTH == 32 + if(buf[3] <= LV_OPA_MIN) return; + bg_color = *((lv_color_t *)buf); +#endif + + /*Get the result color*/ + if(last_dest_color.full != bg_color.full || last_src_color.full != color.full || last_opa != opa) { + last_dest_color = bg_color; + last_src_color = color; + last_opa = opa; + last_res_color = blend_fp(last_src_color, last_dest_color, last_opa); + } + + /*Set the result color*/ +#if LV_COLOR_DEPTH == 8 + buf[0] = res_color.full; +#elif LV_COLOR_DEPTH == 16 + buf[0] = last_res_color.full & 0xff; + buf[1] = last_res_color.full >> 8; +#elif LV_COLOR_DEPTH == 32 + buf[0] = last_res_color.ch.blue; + buf[1] = last_res_color.ch.green; + buf[2] = last_res_color.ch.red; +#endif + +} + +LV_ATTRIBUTE_FAST_MEM static void fill_argb(lv_color_t * dest_buf, const lv_area_t * dest_area, + lv_coord_t dest_stride, lv_color_t color, lv_opa_t opa, const lv_opa_t * mask, lv_coord_t mask_stride) +{ + uint8_t * dest_buf8 = (uint8_t *) dest_buf; + int32_t w = lv_area_get_width(dest_area); + int32_t h = lv_area_get_height(dest_area); + + int32_t x; + int32_t y; + + uint8_t ctmp[LV_IMG_PX_SIZE_ALPHA_BYTE]; + lv_memcpy(ctmp, &color, sizeof(lv_color_t)); + ctmp[LV_IMG_PX_SIZE_ALPHA_BYTE - 1] = opa; + + /*No mask*/ + if(mask == NULL) { + if(opa >= LV_OPA_MAX) { + for(x = 0; x < w; x++) { + lv_memcpy(dest_buf8, ctmp, LV_IMG_PX_SIZE_ALPHA_BYTE); + dest_buf8 += LV_IMG_PX_SIZE_ALPHA_BYTE; + } + + dest_buf8 += (dest_stride - w) * LV_IMG_PX_SIZE_ALPHA_BYTE; + + for(y = 1; y < h; y++) { + lv_memcpy(dest_buf8, (uint8_t *) dest_buf, w * LV_IMG_PX_SIZE_ALPHA_BYTE); + dest_buf8 += dest_stride * LV_IMG_PX_SIZE_ALPHA_BYTE; + } + } + /*Has opacity*/ + else { + uint8_t * dest_buf8_row = dest_buf8; + for(y = 0; y < h; y++) { + for(x = 0; x < w; x++) { + set_px_argb(dest_buf8, color, opa); + dest_buf8 += LV_IMG_PX_SIZE_ALPHA_BYTE; + } + dest_buf8_row += dest_stride * LV_IMG_PX_SIZE_ALPHA_BYTE; + dest_buf8 = dest_buf8_row; + } + } + } + /*Masked*/ + else { + /*Only the mask matters*/ + if(opa >= LV_OPA_MAX) { + uint8_t * dest_buf8_row = dest_buf8; + for(y = 0; y < h; y++) { + for(x = 0; x < w; x++) { + set_px_argb(dest_buf8, color, *mask); + mask++; + dest_buf8 += LV_IMG_PX_SIZE_ALPHA_BYTE; + } + dest_buf8_row += dest_stride * LV_IMG_PX_SIZE_ALPHA_BYTE; + dest_buf8 = dest_buf8_row; + } + } + /*With opacity*/ + else { + /*Buffer the result color to avoid recalculating the same color*/ + lv_opa_t last_mask = LV_OPA_TRANSP; + lv_opa_t opa_tmp = LV_OPA_TRANSP; + + uint8_t * dest_buf8_row = dest_buf8; + for(y = 0; y < h; y++) { + for(x = 0; x < w; x++) { + if(*mask) { + if(*mask != last_mask) opa_tmp = *mask == LV_OPA_COVER ? opa : + (uint32_t)((uint32_t)(*mask) * opa) >> 8; + + set_px_argb(dest_buf8, color, opa_tmp); + } + dest_buf8 += LV_IMG_PX_SIZE_ALPHA_BYTE; + mask++; + } + dest_buf8_row += dest_stride * LV_IMG_PX_SIZE_ALPHA_BYTE; + dest_buf8 = dest_buf8_row; + mask += (mask_stride - w); + } + } + } +} +#endif + +#if LV_DRAW_COMPLEX +static void fill_blended(lv_color_t * dest_buf, const lv_area_t * dest_area, + lv_coord_t dest_stride, lv_color_t color, lv_opa_t opa, const lv_opa_t * mask, lv_coord_t mask_stride, + lv_blend_mode_t blend_mode) +{ + + int32_t w = lv_area_get_width(dest_area); + int32_t h = lv_area_get_height(dest_area); + + int32_t x; + int32_t y; + + lv_color_t (*blend_fp)(lv_color_t, lv_color_t, lv_opa_t); + switch(blend_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; + } + + /*Simple fill (maybe with opacity), no masking*/ + if(mask == NULL) { + lv_color_t last_dest_color = dest_buf[0]; + lv_color_t last_res_color = blend_fp(color, dest_buf[0], opa); + for(y = 0; y < h; y++) { + for(x = 0; x < w; 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 = dest_buf[0]; + lv_opa_t opa_tmp = mask[0] >= LV_OPA_MAX ? opa : (uint32_t)((uint32_t)mask[0] * opa) >> 8; + last_res_color = blend_fp(color, last_dest_color, opa_tmp); + + for(y = 0; y < h; y++) { + for(x = 0; x < w; x++) { + if(mask[x] == 0) continue; + if(mask[x] != last_mask || last_dest_color.full != dest_buf[x].full) { + 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 += mask_stride; + } + } +} +#endif + +static void map_set_px(lv_color_t * dest_buf, const lv_area_t * dest_area, lv_coord_t dest_stride, + const lv_color_t * src_buf, lv_coord_t src_stride, lv_opa_t opa, const lv_opa_t * mask, lv_coord_t mask_stride) + +{ + lv_disp_t * disp = _lv_refr_get_disp_refreshing(); + + int32_t w = lv_area_get_width(dest_area); + int32_t h = lv_area_get_height(dest_area); + + int32_t x; + int32_t y; + + if(mask == NULL) { + for(y = 0; y < h; y++) { + for(x = 0; x < w; x++) { + disp->driver->set_px_cb(disp->driver, (void *)dest_buf, dest_stride, dest_area->x1 + x, dest_area->y1 + y, src_buf[x], + opa); + } + src_buf += src_stride; + } + } + else { + for(y = 0; y < h; y++) { + for(x = 0; x < w; x++) { + if(mask[x]) { + disp->driver->set_px_cb(disp->driver, (void *)dest_buf, dest_stride, dest_area->x1 + x, dest_area->y1 + y, src_buf[x], + (uint32_t)((uint32_t)opa * mask[x]) >> 8); + } + } + mask += mask_stride; + src_buf += src_stride; + } + } +} + +LV_ATTRIBUTE_FAST_MEM static void map_normal(lv_color_t * dest_buf, const lv_area_t * dest_area, lv_coord_t dest_stride, + const lv_color_t * src_buf, lv_coord_t src_stride, lv_opa_t opa, const lv_opa_t * mask, lv_coord_t mask_stride) + +{ + int32_t w = lv_area_get_width(dest_area); + int32_t h = lv_area_get_height(dest_area); + + 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 < h; y++) { + lv_memcpy(dest_buf, src_buf, w * sizeof(lv_color_t)); + dest_buf += dest_stride; + src_buf += src_stride; + } + } + else { + for(y = 0; y < h; y++) { + for(x = 0; x < w; x++) { + 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 = w - 4; + + for(y = 0; y < h; y++) { + const lv_opa_t * mask_tmp_x = mask; +#if 0 + for(x = 0; x < w; x++) { + MAP_NORMAL_MASK_PX(x); + } +#else + for(x = 0; x < w && ((lv_uintptr_t)mask_tmp_x & 0x3); x++) { + MAP_NORMAL_MASK_PX(x) + } + + 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; + MAP_NORMAL_MASK_PX(x) + MAP_NORMAL_MASK_PX(x + 1) + MAP_NORMAL_MASK_PX(x + 2) + MAP_NORMAL_MASK_PX(x + 3) + } + } + mask32++; + } + + mask_tmp_x = (const lv_opa_t *)mask32; + for(; x < w ; x++) { + MAP_NORMAL_MASK_PX(x) + } +#endif + dest_buf += dest_stride; + src_buf += src_stride; + mask += mask_stride; + } + } + /*Handle opa and mask values too*/ + else { + for(y = 0; y < h; y++) { + for(x = 0; x < w; x++) { + if(mask[x]) { + lv_opa_t opa_tmp = mask[x] >= LV_OPA_MAX ? opa : ((opa * mask[x]) >> 8); + dest_buf[x] = lv_color_mix(src_buf[x], dest_buf[x], opa_tmp); + } + } + dest_buf += dest_stride; + src_buf += src_stride; + mask += mask_stride; + } + } + } +} + + + +#if LV_COLOR_SCREEN_TRANSP +LV_ATTRIBUTE_FAST_MEM static void map_argb(lv_color_t * dest_buf, const lv_area_t * dest_area, lv_coord_t dest_stride, + const lv_color_t * src_buf, lv_coord_t src_stride, lv_opa_t opa, + const lv_opa_t * mask, lv_coord_t mask_stride, lv_blend_mode_t blend_mode) + +{ + uint8_t * dest_buf8 = (uint8_t *) dest_buf; + + int32_t w = lv_area_get_width(dest_area); + int32_t h = lv_area_get_height(dest_area); + + int32_t x; + int32_t y; + + lv_color_t (*blend_fp)(lv_color_t, lv_color_t, lv_opa_t); + switch(blend_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: + blend_fp = NULL; + } + + /*Simple fill (maybe with opacity), no masking*/ + if(mask == NULL) { + if(opa >= LV_OPA_MAX) { + if(blend_fp == NULL && LV_COLOR_DEPTH == 32) { + for(y = 0; y < h; y++) { + lv_memcpy(dest_buf, src_buf, w * sizeof(lv_color_t)); + dest_buf += dest_stride; + src_buf += src_stride; + } + } + else { + uint8_t * dest_buf8_row = dest_buf8; + for(y = 0; y < h; y++) { + if(blend_fp == NULL) { + for(x = 0; x < w; x++) { + set_px_argb(dest_buf8, src_buf[x], LV_OPA_COVER); + dest_buf8 += LV_IMG_PX_SIZE_ALPHA_BYTE; + } + } + else { + for(x = 0; x < w; x++) { + set_px_argb_blend(dest_buf8, src_buf[x], LV_OPA_COVER, blend_fp); + dest_buf8 += LV_IMG_PX_SIZE_ALPHA_BYTE; + } + } + + dest_buf8_row += dest_stride * LV_IMG_PX_SIZE_ALPHA_BYTE; + dest_buf8 = dest_buf8_row; + src_buf += src_stride; + } + } + } + /*No mask but opacity*/ + else { + uint8_t * dest_buf8_row = dest_buf8; + for(y = 0; y < h; y++) { + if(blend_fp == NULL) { + for(x = 0; x < w; x++) { + set_px_argb(dest_buf8, src_buf[x], opa); + dest_buf8 += LV_IMG_PX_SIZE_ALPHA_BYTE; + } + } + else { + for(x = 0; x < w; x++) { + set_px_argb_blend(dest_buf8, src_buf[x], opa, blend_fp); + dest_buf8 += LV_IMG_PX_SIZE_ALPHA_BYTE; + } + } + + dest_buf8_row += dest_stride * LV_IMG_PX_SIZE_ALPHA_BYTE; + dest_buf8 = dest_buf8_row; + src_buf += src_stride; + } + } + } + /*Masked*/ + else { + /*Only the mask matters*/ + if(opa > LV_OPA_MAX) { + uint8_t * dest_buf8_row = dest_buf8; + for(y = 0; y < h; y++) { + if(blend_fp == NULL) { + for(x = 0; x < w; x++) { + set_px_argb(dest_buf8, src_buf[x], mask[x]); + dest_buf8 += LV_IMG_PX_SIZE_ALPHA_BYTE; + } + } + else { + for(x = 0; x < w; x++) { + set_px_argb_blend(dest_buf8, src_buf[x], mask[x], blend_fp); + dest_buf8 += LV_IMG_PX_SIZE_ALPHA_BYTE; + } + } + dest_buf8_row += dest_stride * LV_IMG_PX_SIZE_ALPHA_BYTE; + dest_buf8 = dest_buf8_row; + src_buf += src_stride; + mask += mask_stride; + } + } + /*Handle opa and mask values too*/ + else { + uint8_t * dest_buf8_row = dest_buf8; + for(y = 0; y < h; y++) { + if(blend_fp == NULL) { + for(x = 0; x < w; x++) { + if(mask[x]) { + lv_opa_t opa_tmp = mask[x] >= LV_OPA_MAX ? opa : ((opa * mask[x]) >> 8); + set_px_argb(dest_buf8, src_buf[x], opa_tmp); + } + dest_buf8 += LV_IMG_PX_SIZE_ALPHA_BYTE; + } + } + else { + for(x = 0; x < w; x++) { + if(mask[x]) { + lv_opa_t opa_tmp = mask[x] >= LV_OPA_MAX ? opa : ((opa * mask[x]) >> 8); + set_px_argb_blend(dest_buf8, src_buf[x], opa_tmp, blend_fp); + } + dest_buf8 += LV_IMG_PX_SIZE_ALPHA_BYTE; + } + } + dest_buf8_row += dest_stride * LV_IMG_PX_SIZE_ALPHA_BYTE; + dest_buf8 = dest_buf8_row; + src_buf += src_stride; + mask += mask_stride; + } + } + } +} +#endif + + +#if LV_DRAW_COMPLEX +static void map_blended(lv_color_t * dest_buf, const lv_area_t * dest_area, lv_coord_t dest_stride, + const lv_color_t * src_buf, lv_coord_t src_stride, lv_opa_t opa, + const lv_opa_t * mask, lv_coord_t mask_stride, lv_blend_mode_t blend_mode) +{ + + int32_t w = lv_area_get_width(dest_area); + int32_t h = lv_area_get_height(dest_area); + + int32_t x; + int32_t y; + + lv_color_t (*blend_fp)(lv_color_t, lv_color_t, lv_opa_t); + switch(blend_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; + } + + lv_color_t last_dest_color; + lv_color_t last_src_color; + /*Simple fill (maybe with opacity), no masking*/ + if(mask == NULL) { + last_dest_color = dest_buf[0]; + last_src_color = src_buf[0]; + lv_color_t last_res_color = blend_fp(last_src_color, last_dest_color, opa); + for(y = 0; y < h; y++) { + for(x = 0; x < w; x++) { + if(last_src_color.full != src_buf[x].full || last_dest_color.full != dest_buf[x].full) { + last_dest_color = dest_buf[x]; + last_src_color = src_buf[x]; + last_res_color = blend_fp(last_src_color, last_dest_color, opa); + } + dest_buf[x] = last_res_color; + } + dest_buf += dest_stride; + src_buf += src_stride; + } + } + /*Masked*/ + else { + last_dest_color = dest_buf[0]; + last_src_color = src_buf[0]; + lv_opa_t last_opa = mask[0] >= LV_OPA_MAX ? opa : ((opa * mask[0]) >> 8); + lv_color_t last_res_color = blend_fp(last_src_color, last_dest_color, last_opa); + for(y = 0; y < h; y++) { + for(x = 0; x < w; x++) { + if(mask[x] == 0) continue; + lv_opa_t opa_tmp = mask[x] >= LV_OPA_MAX ? opa : ((opa * mask[x]) >> 8); + if(last_src_color.full != src_buf[x].full || last_dest_color.full != dest_buf[x].full || last_opa != opa_tmp) { + last_dest_color = dest_buf[x]; + last_src_color = src_buf[x]; + last_opa = opa_tmp; + last_res_color = blend_fp(last_src_color, last_dest_color, last_opa); + } + dest_buf[x] = last_res_color; + } + dest_buf += dest_stride; + src_buf += src_stride; + mask += mask_stride; + } + } +} + +static inline lv_color_t color_blend_true_color_additive(lv_color_t fg, lv_color_t bg, lv_opa_t opa) +{ + + if(opa <= LV_OPA_MIN) return bg; + + uint32_t tmp; +#if LV_COLOR_DEPTH == 1 + tmp = bg.full + fg.full; + fg.full = LV_MIN(tmp, 1); +#else + tmp = bg.ch.red + fg.ch.red; +#if LV_COLOR_DEPTH == 8 + fg.ch.red = LV_MIN(tmp, 7); +#elif LV_COLOR_DEPTH == 16 + fg.ch.red = LV_MIN(tmp, 31); +#elif LV_COLOR_DEPTH == 32 + fg.ch.red = LV_MIN(tmp, 255); +#endif + +#if LV_COLOR_DEPTH == 8 + tmp = bg.ch.green + fg.ch.green; + fg.ch.green = LV_MIN(tmp, 7); +#elif LV_COLOR_DEPTH == 16 +#if LV_COLOR_16_SWAP == 0 + tmp = bg.ch.green + fg.ch.green; + fg.ch.green = LV_MIN(tmp, 63); +#else + tmp = (bg.ch.green_h << 3) + bg.ch.green_l + (fg.ch.green_h << 3) + fg.ch.green_l; + tmp = LV_MIN(tmp, 63); + fg.ch.green_h = tmp >> 3; + fg.ch.green_l = tmp & 0x7; +#endif + +#elif LV_COLOR_DEPTH == 32 + tmp = bg.ch.green + fg.ch.green; + fg.ch.green = LV_MIN(tmp, 255); +#endif + + tmp = bg.ch.blue + fg.ch.blue; +#if LV_COLOR_DEPTH == 8 + fg.ch.blue = LV_MIN(tmp, 4); +#elif LV_COLOR_DEPTH == 16 + fg.ch.blue = LV_MIN(tmp, 31); +#elif LV_COLOR_DEPTH == 32 + fg.ch.blue = LV_MIN(tmp, 255); +#endif +#endif + + if(opa == LV_OPA_COVER) return fg; + + return lv_color_mix(fg, bg, opa); +} + +static inline lv_color_t color_blend_true_color_subtractive(lv_color_t fg, lv_color_t bg, lv_opa_t opa) +{ + if(opa <= LV_OPA_MIN) return bg; + + int32_t tmp; + tmp = bg.ch.red - fg.ch.red; + fg.ch.red = LV_MAX(tmp, 0); + +#if LV_COLOR_16_SWAP == 0 + tmp = bg.ch.green - fg.ch.green; + fg.ch.green = LV_MAX(tmp, 0); +#else + tmp = (bg.ch.green_h << 3) + bg.ch.green_l + (fg.ch.green_h << 3) + fg.ch.green_l; + tmp = LV_MAX(tmp, 0); + fg.ch.green_h = tmp >> 3; + fg.ch.green_l = tmp & 0x7; +#endif + + tmp = bg.ch.blue - fg.ch.blue; + fg.ch.blue = LV_MAX(tmp, 0); + + if(opa == LV_OPA_COVER) return fg; + + return lv_color_mix(fg, bg, opa); +} + +static inline lv_color_t color_blend_true_color_multiply(lv_color_t fg, lv_color_t bg, lv_opa_t opa) +{ + if(opa <= LV_OPA_MIN) return bg; + +#if LV_COLOR_DEPTH == 32 + fg.ch.red = (fg.ch.red * bg.ch.red) >> 8; + fg.ch.green = (fg.ch.green * bg.ch.green) >> 8; + fg.ch.blue = (fg.ch.blue * bg.ch.blue) >> 8; +#elif LV_COLOR_DEPTH == 16 + fg.ch.red = (fg.ch.red * bg.ch.red) >> 5; + fg.ch.blue = (fg.ch.blue * bg.ch.blue) >> 5; + LV_COLOR_SET_G(fg, (LV_COLOR_GET_G(fg) * LV_COLOR_GET_G(bg)) >> 6); +#elif LV_COLOR_DEPTH == 8 + fg.ch.red = (fg.ch.red * bg.ch.red) >> 3; + fg.ch.green = (fg.ch.green * bg.ch.green) >> 3; + fg.ch.blue = (fg.ch.blue * bg.ch.blue) >> 2; +#endif + + if(opa == LV_OPA_COVER) return fg; + + return lv_color_mix(fg, bg, opa); +} + +#endif + diff --git a/lib/lvgl/src/draw/sw/lv_draw_sw_blend.h b/lib/lvgl/src/draw/sw/lv_draw_sw_blend.h new file mode 100644 index 00000000..9a00e533 --- /dev/null +++ b/lib/lvgl/src/draw/sw/lv_draw_sw_blend.h @@ -0,0 +1,69 @@ +/** + * @file lv_draw_sw_blend.h + * + */ + +#ifndef LV_DRAW_SW_BLEND_H +#define LV_DRAW_SW_BLEND_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ +#include "../../misc/lv_color.h" +#include "../../misc/lv_area.h" +#include "../../misc/lv_style.h" +#include "../lv_draw_mask.h" + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +typedef struct { + const lv_area_t * blend_area; /**< The area with absolute coordinates to draw on `draw_ctx->buf` + * will be clipped to `draw_ctx->clip_area` */ + const lv_color_t * src_buf; /**< Pointer to an image to blend. If set `fill_color` is ignored */ + lv_color_t color; /**< Fill color*/ + lv_opa_t * mask_buf; /**< NULL if ignored, or an alpha mask to apply on `blend_area`*/ + lv_draw_mask_res_t mask_res; /**< The result of the previous mask operation */ + const lv_area_t * mask_area; /**< The area of `mask_buf` with absolute coordinates*/ + lv_opa_t opa; /**< The overall opacity*/ + lv_blend_mode_t blend_mode; /**< E.g. LV_BLEND_MODE_ADDITIVE*/ +} lv_draw_sw_blend_dsc_t; + +struct _lv_draw_ctx_t; + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +/** + * Call the blend function of the `draw_ctx`. + * @param draw_ctx pointer to a draw context + * @param dsc pointer to an initialized blend descriptor + */ +void lv_draw_sw_blend(struct _lv_draw_ctx_t * draw_ctx, const lv_draw_sw_blend_dsc_t * dsc); + +/** + * The basic blend function used with software rendering. + * @param draw_ctx pointer to a draw context + * @param dsc pointer to an initialized blend descriptor + */ +LV_ATTRIBUTE_FAST_MEM void lv_draw_sw_blend_basic(struct _lv_draw_ctx_t * draw_ctx, const lv_draw_sw_blend_dsc_t * dsc); + +/********************** + * MACROS + **********************/ + +#ifdef __cplusplus +} /*extern "C"*/ +#endif + +#endif /*LV_DRAW_SW_BLEND_H*/ diff --git a/lib/lvgl/src/draw/sw/lv_draw_sw_dither.c b/lib/lvgl/src/draw/sw/lv_draw_sw_dither.c new file mode 100644 index 00000000..ff9ebf3d --- /dev/null +++ b/lib/lvgl/src/draw/sw/lv_draw_sw_dither.c @@ -0,0 +1,213 @@ +/** + * @file lv_draw_sw_dither.c + * + */ + +/********************* + * INCLUDES + *********************/ +#include "lv_draw_sw_dither.h" +#include "lv_draw_sw_gradient.h" +#include "../../misc/lv_color.h" + +/********************** + * STATIC FUNCTIONS + **********************/ + + +#if _DITHER_GRADIENT + +LV_ATTRIBUTE_FAST_MEM void lv_dither_none(lv_grad_t * grad, lv_coord_t x, lv_coord_t y, lv_coord_t w) +{ + LV_UNUSED(x); + LV_UNUSED(y); + if(grad == NULL || grad->filled) return; + for(lv_coord_t i = 0; i < w; i++) { + grad->map[i] = lv_color_hex(grad->hmap[i].full); + } + grad->filled = 1; +} + +static const uint8_t dither_ordered_threshold_matrix[8 * 8] = { + 0, 48, 12, 60, 3, 51, 15, 63, + 32, 16, 44, 28, 35, 19, 47, 31, + 8, 56, 4, 52, 11, 59, 7, 55, + 40, 24, 36, 20, 43, 27, 39, 23, + 2, 50, 14, 62, 1, 49, 13, 61, + 34, 18, 46, 30, 33, 17, 45, 29, + 10, 58, 6, 54, 9, 57, 5, 53, + 42, 26, 38, 22, 41, 25, 37, 21 +}; /* Shift by 6 to normalize */ + + +LV_ATTRIBUTE_FAST_MEM void lv_dither_ordered_hor(lv_grad_t * grad, lv_coord_t x, lv_coord_t y, lv_coord_t w) +{ + LV_UNUSED(x); + /* For vertical dithering, the error is spread on the next column (and not next line). + Since the renderer is scanline based, it's not obvious what could be used to perform the rendering efficiently. + The algorithm below is based on few assumptions: + 1. An error diffusion algorithm (like Floyd Steinberg) here would be hard to implement since it means that a pixel on column n depends on the pixel on row n + 2. Instead an ordered dithering algorithm shift the value a bit, but the influence only spread from the matrix size (used 8x8 here) + 3. It means that a pixel i,j only depends on the value of a pixel i-7, j-7 to i,j and no other one. + Then we compute a complete row of ordered dither and store it in out. */ + + /*The apply the algorithm for this patch*/ + for(lv_coord_t j = 0; j < w; j++) { + int8_t factor = dither_ordered_threshold_matrix[(y & 7) * 8 + ((j) & 7)] - 32; + lv_color32_t tmp = grad->hmap[LV_CLAMP(0, j - 4, grad->size)]; + lv_color32_t t; + t.ch.red = LV_CLAMP(0, tmp.ch.red + factor, 255); + t.ch.green = LV_CLAMP(0, tmp.ch.green + factor, 255); + t.ch.blue = LV_CLAMP(0, tmp.ch.blue + factor, 255); + + grad->map[j] = lv_color_hex(t.full); + } +} +LV_ATTRIBUTE_FAST_MEM void lv_dither_ordered_ver(lv_grad_t * grad, lv_coord_t x, lv_coord_t y, lv_coord_t w) +{ + /* For vertical dithering, the error is spread on the next column (and not next line). + Since the renderer is scanline based, it's not obvious what could be used to perform the rendering efficiently. + The algorithm below is based on few assumptions: + 1. An error diffusion algorithm (like Floyd Steinberg) here would be hard to implement since it means that a pixel on column n depends on the pixel on row n + 2. Instead an ordered dithering algorithm shift the value a bit, but the influence only spread from the matrix size (used 8x8 here) + 3. It means that a pixel i,j only depends on the value of a pixel i-7, j-7 to i,j and no other one. + Then we compute a complete row of ordered dither and store it in out. */ + + /*Extract patch for working with, selected pseudo randomly*/ + lv_color32_t tmp = grad->hmap[LV_CLAMP(0, y - 4, grad->size)]; + + /*The apply the algorithm for this patch*/ + for(lv_coord_t j = 0; j < 8; j++) { + int8_t factor = dither_ordered_threshold_matrix[(y & 7) * 8 + ((j + x) & 7)] - 32; + lv_color32_t t; + t.ch.red = LV_CLAMP(0, tmp.ch.red + factor, 255); + t.ch.green = LV_CLAMP(0, tmp.ch.green + factor, 255); + t.ch.blue = LV_CLAMP(0, tmp.ch.blue + factor, 255); + + grad->map[j] = lv_color_hex(t.full); + } + /*Finally fill the line*/ + lv_coord_t j = 8; + for(; j < w - 8; j += 8) { + lv_memcpy(grad->map + j, grad->map, 8 * sizeof(*grad->map)); + } + /* Prevent overwriting */ + for(; j < w; j++) { + grad->map[j] = grad->map[j & 7]; + } +} + + +#if LV_DITHER_ERROR_DIFFUSION == 1 +LV_ATTRIBUTE_FAST_MEM void lv_dither_err_diff_hor(lv_grad_t * grad, lv_coord_t xs, lv_coord_t y, lv_coord_t w) +{ + LV_UNUSED(xs); + LV_UNUSED(y); + LV_UNUSED(w); + + /* Implement Floyd Steinberg algorithm, see https://surma.dev/things/ditherpunk/ + Coefs are: x 7 + 3 5 1 + / 16 + Can be implemented as: x (x<<3 - x) + (x<<2 - x) (x<<2+x) x + */ + int coef[4] = {0, 0, 0, 0}; +#define FS_COMPUTE_ERROR(e) { coef[0] = (e<<3) - e; coef[1] = (e<<2) - e; coef[2] = (e<<2) + e; coef[3] = e; } +#define FS_COMPONENTS(A, OP, B, C) A.ch.red = LV_CLAMP(0, A.ch.red OP B.r OP C.r, 255); A.ch.green = LV_CLAMP(0, A.ch.green OP B.g OP C.g, 255); A.ch.blue = LV_CLAMP(0, A.ch.blue OP B.b OP C.b, 255); +#define FS_QUANT_ERROR(e, t, q) { lv_color32_t u; u.full = lv_color_to32(q); e.r = (int8_t)(t.ch.red - u.ch.red); e.g = (int8_t)(t.ch.green - u.ch.green); e.b = (int8_t)(t.ch.blue - u.ch.blue); } + lv_scolor24_t next_px_err = {0}, next_l = {0}, error; + /*First last pixel are not dithered */ + grad->map[0] = lv_color_hex(grad->hmap[0].full); + for(lv_coord_t x = 1; x < grad->size - 1; x++) { + lv_color32_t t = grad->hmap[x]; + lv_color_t q; + /*Add error term*/ + FS_COMPONENTS(t, +, next_px_err, next_l); + next_l = grad->error_acc[x + 1]; + /*Quantify*/ + q = lv_color_hex(t.full); + /*Then compute error*/ + FS_QUANT_ERROR(error, t, q); + /*Dither the error*/ + FS_COMPUTE_ERROR(error.r); + next_px_err.r = coef[0] >> 4; + grad->error_acc[x - 1].r += coef[1] >> 4; + grad->error_acc[x].r += coef[2] >> 4; + grad->error_acc[x + 1].r = coef[3] >> 4; + + FS_COMPUTE_ERROR(error.g); + next_px_err.g = coef[0] >> 4; + grad->error_acc[x - 1].g += coef[1] >> 4; + grad->error_acc[x].g += coef[2] >> 4; + grad->error_acc[x + 1].g = coef[3] >> 4; + + FS_COMPUTE_ERROR(error.b); + next_px_err.b = coef[0] >> 4; + grad->error_acc[x - 1].b += coef[1] >> 4; + grad->error_acc[x].b += coef[2] >> 4; + grad->error_acc[x + 1].b = coef[3] >> 4; + + grad->map[x] = q; + } + grad->map[grad->size - 1] = lv_color_hex(grad->hmap[grad->size - 1].full); +} + +LV_ATTRIBUTE_FAST_MEM void lv_dither_err_diff_ver(lv_grad_t * grad, lv_coord_t xs, lv_coord_t y, lv_coord_t w) +{ + /* Try to implement error diffusion on a vertical gradient and an horizontal map using those tricks: + Since the given hi-resolution gradient (in src) is vertical, the Floyd Steinberg algorithm pass need to be rotated, + so we'll get this instead (from top to bottom): + + A B C + 1 [ ][ ][ ] + 2 [ ][ ][ ] Pixel A2 will spread its error on pixel A3 with coefficient 7, + 3 [ ][ ][ ] Pixel A2 will spread its error on pixel B1 with coefficient 3, B2 with coef 5 and B3 with coef 1 + + When taking into account an arbitrary pixel P(i,j), its added error diffusion term is: + e(i,j) = 1/16 * [ e(i-1,j) * 5 + e(i-1,j+1) * 3 + e(i-1,j-1) * 1 + e(i,j-1) * 7] + + This means that the error term depends on pixel W, NW, N and SW. + If we consider that we are generating the error diffused gradient map from top to bottom, we can remember the previous + line (N, NW) in the term above. Also, we remember the (W) term too since we are computing the gradient map from left to right. + However, the SW term is painful for us, we can't support it (since to get it, we need its own SW term and so on). + Let's remove it and re-dispatch the error factor accordingly so they stays normalized: + e(i,j) ~= 1/16 * [ e(i-1,j) * 6 + e(i-1,j-1) * 1 + e(i,j-1) * 9] + + That's the idea of this pseudo Floyd Steinberg dithering */ +#define FS_APPLY(d, s, c) d.r = (int8_t)(s.r * c) >> 4; d.g = (int8_t)(s.g * c) >> 4; d.b = (int8_t)(s.b * c) >> 4; +#define FS_COMPONENTS3(A, OP, B, b, C, c, D, d) \ + A.ch.red = LV_CLAMP(0, A.ch.red OP ((B.r * b OP C.r * c OP D.r * d) >> 4), 255); \ + A.ch.green = LV_CLAMP(0, A.ch.green OP ((B.r * b OP C.r * c OP D.r * d) >> 4), 255); \ + A.ch.blue = LV_CLAMP(0, A.ch.blue OP ((B.r * b OP C.r * c OP D.r * d) >> 4), 255); + + lv_scolor24_t next_px_err, prev_l = grad->error_acc[0]; + /*Compute the error term for the current pixel (first pixel is never dithered)*/ + if(xs == 0) { + grad->map[0] = lv_color_hex(grad->hmap[y].full); + FS_QUANT_ERROR(next_px_err, grad->hmap[y], grad->map[0]); + } + else { + lv_color_t tmp = lv_color_hex(grad->hmap[y].full); + lv_color32_t t = grad->hmap[y]; + FS_QUANT_ERROR(next_px_err, grad->hmap[y], tmp); + FS_COMPONENTS3(t, +, next_px_err, 6, prev_l, 1, grad->error_acc[0], 9); + grad->map[0] = lv_color_hex(t.full); + } + + for(lv_coord_t x = 1; x < w; x++) { + lv_color32_t t = grad->hmap[y]; + lv_color_t q; + /*Add the current error term*/ + FS_COMPONENTS3(t, +, next_px_err, 6, prev_l, 1, grad->error_acc[x], 9); + prev_l = grad->error_acc[x]; + /*Quantize and compute error term*/ + q = lv_color_hex(t.full); + FS_QUANT_ERROR(next_px_err, t, q); + /*Store error for next line computation*/ + grad->error_acc[x] = next_px_err; + grad->map[x] = q; + } +} +#endif +#endif diff --git a/lib/lvgl/src/draw/sw/lv_draw_sw_dither.h b/lib/lvgl/src/draw/sw/lv_draw_sw_dither.h new file mode 100644 index 00000000..6362c5ac --- /dev/null +++ b/lib/lvgl/src/draw/sw/lv_draw_sw_dither.h @@ -0,0 +1,70 @@ +/** + * @file lv_draw_sw_dither.h + * + */ + +#ifndef LV_DRAW_SW_DITHER_H +#define LV_DRAW_SW_DITHER_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ +#include "../../core/lv_obj_pos.h" + + +/********************* + * DEFINES + *********************/ +#if LV_COLOR_DEPTH < 32 && LV_DITHER_GRADIENT == 1 +#define _DITHER_GRADIENT 1 +#else +#define _DITHER_GRADIENT 0 +#endif + +/********************** + * TYPEDEFS + **********************/ +#if _DITHER_GRADIENT +/*A signed error color component*/ +typedef struct { + int8_t r, g, b; +} lv_scolor24_t; + +struct _lv_gradient_cache_t; +typedef void (*lv_dither_func_t)(struct _lv_gradient_cache_t * grad, lv_coord_t x, lv_coord_t y, lv_coord_t w); + +#endif + + +/********************** + * PROTOTYPES + **********************/ +#if LV_DRAW_COMPLEX +#if _DITHER_GRADIENT +LV_ATTRIBUTE_FAST_MEM void lv_dither_none(struct _lv_gradient_cache_t * grad, lv_coord_t x, lv_coord_t y, lv_coord_t w); + +LV_ATTRIBUTE_FAST_MEM void lv_dither_ordered_hor(struct _lv_gradient_cache_t * grad, const lv_coord_t xs, + const lv_coord_t y, const lv_coord_t w); +LV_ATTRIBUTE_FAST_MEM void lv_dither_ordered_ver(struct _lv_gradient_cache_t * grad, const lv_coord_t xs, + const lv_coord_t y, const lv_coord_t w); + +#if LV_DITHER_ERROR_DIFFUSION == 1 +LV_ATTRIBUTE_FAST_MEM void lv_dither_err_diff_hor(struct _lv_gradient_cache_t * grad, const lv_coord_t xs, + const lv_coord_t y, const lv_coord_t w); +LV_ATTRIBUTE_FAST_MEM void lv_dither_err_diff_ver(struct _lv_gradient_cache_t * grad, const lv_coord_t xs, + const lv_coord_t y, const lv_coord_t w); +#endif /* LV_DITHER_ERROR_DIFFUSION */ + +#endif /* _DITHER_GRADIENT */ +#endif + + +#ifdef __cplusplus +} /*extern "C"*/ +#endif + +#endif diff --git a/lib/lvgl/src/draw/sw/lv_draw_sw_gradient.c b/lib/lvgl/src/draw/sw/lv_draw_sw_gradient.c new file mode 100644 index 00000000..4e466326 --- /dev/null +++ b/lib/lvgl/src/draw/sw/lv_draw_sw_gradient.c @@ -0,0 +1,346 @@ +/** + * @file lv_draw_sw_gradient.c + * + */ + +/********************* + * INCLUDES + *********************/ +#include "lv_draw_sw_gradient.h" +#include "../../misc/lv_gc.h" +#include "../../misc/lv_types.h" + +/********************* + * DEFINES + *********************/ +#if _DITHER_GRADIENT + #define GRAD_CM(r,g,b) LV_COLOR_MAKE32(r,g,b) + #define GRAD_CONV(t, x) t.full = lv_color_to32(x) +#else + #define GRAD_CM(r,g,b) LV_COLOR_MAKE(r,g,b) + #define GRAD_CONV(t, x) t = x +#endif + +#undef ALIGN +#if defined(LV_ARCH_64) + #define ALIGN(X) (((X) + 7) & ~7) +#else + #define ALIGN(X) (((X) + 3) & ~3) +#endif + +#if LV_GRAD_CACHE_DEF_SIZE != 0 && LV_GRAD_CACHE_DEF_SIZE < 256 + #error "LV_GRAD_CACHE_DEF_SIZE is too small" +#endif + +/********************** + * STATIC PROTOTYPES + **********************/ +static lv_grad_t * next_in_cache(lv_grad_t * item); + +typedef lv_res_t (*op_cache_t)(lv_grad_t * c, void * ctx); +static lv_res_t iterate_cache(op_cache_t func, void * ctx, lv_grad_t ** out); +static size_t get_cache_item_size(lv_grad_t * c); +static lv_grad_t * allocate_item(const lv_grad_dsc_t * g, lv_coord_t w, lv_coord_t h); +static lv_res_t find_oldest_item_life(lv_grad_t * c, void * ctx); +static lv_res_t kill_oldest_item(lv_grad_t * c, void * ctx); +static lv_res_t find_item(lv_grad_t * c, void * ctx); +static void free_item(lv_grad_t * c); +static uint32_t compute_key(const lv_grad_dsc_t * g, lv_coord_t w, lv_coord_t h); + + +/********************** + * STATIC VARIABLE + **********************/ +static size_t grad_cache_size = 0; +static uint8_t * grad_cache_end = 0; + +/********************** + * STATIC FUNCTIONS + **********************/ +union void_cast { + const void * ptr; + const uint32_t value; +}; + +static uint32_t compute_key(const lv_grad_dsc_t * g, lv_coord_t size, lv_coord_t w) +{ + union void_cast v; + v.ptr = g; + return (v.value ^ size ^ (w >> 1)); /*Yes, this is correct, it's like a hash that changes if the width changes*/ +} + +static size_t get_cache_item_size(lv_grad_t * c) +{ + size_t s = ALIGN(sizeof(*c)) + ALIGN(c->alloc_size * sizeof(lv_color_t)); +#if _DITHER_GRADIENT + s += ALIGN(c->size * sizeof(lv_color32_t)); +#if LV_DITHER_ERROR_DIFFUSION == 1 + s += ALIGN(c->w * sizeof(lv_scolor24_t)); +#endif +#endif + return s; +} + +static lv_grad_t * next_in_cache(lv_grad_t * item) +{ + if(grad_cache_size == 0) return NULL; + + if(item == NULL) + return (lv_grad_t *)LV_GC_ROOT(_lv_grad_cache_mem); + + size_t s = get_cache_item_size(item); + /*Compute the size for this cache item*/ + if((uint8_t *)item + s >= grad_cache_end) return NULL; + else return (lv_grad_t *)((uint8_t *)item + s); +} + +static lv_res_t iterate_cache(op_cache_t func, void * ctx, lv_grad_t ** out) +{ + lv_grad_t * first = next_in_cache(NULL); + while(first != NULL && first->life) { + if((*func)(first, ctx) == LV_RES_OK) { + if(out != NULL) *out = first; + return LV_RES_OK; + } + first = next_in_cache(first); + } + return LV_RES_INV; +} + +static lv_res_t find_oldest_item_life(lv_grad_t * c, void * ctx) +{ + uint32_t * min_life = (uint32_t *)ctx; + if(c->life < *min_life) *min_life = c->life; + return LV_RES_INV; +} + +static void free_item(lv_grad_t * c) +{ + size_t size = get_cache_item_size(c); + size_t next_items_size = (size_t)(grad_cache_end - (uint8_t *)c) - size; + grad_cache_end -= size; + if(next_items_size) { + uint8_t * old = (uint8_t *)c; + lv_memcpy(c, ((uint8_t *)c) + size, next_items_size); + /* Then need to fix all internal pointers too */ + while((uint8_t *)c != grad_cache_end) { + c->map = (lv_color_t *)(((uint8_t *)c->map) - size); +#if _DITHER_GRADIENT + c->hmap = (lv_color32_t *)(((uint8_t *)c->hmap) - size); +#if LV_DITHER_ERROR_DIFFUSION == 1 + c->error_acc = (lv_scolor24_t *)(((uint8_t *)c->error_acc) - size); +#endif +#endif + c = (lv_grad_t *)(((uint8_t *)c) + get_cache_item_size(c)); + } + lv_memset_00(old + next_items_size, size); + } +} + +static lv_res_t kill_oldest_item(lv_grad_t * c, void * ctx) +{ + uint32_t * min_life = (uint32_t *)ctx; + if(c->life == *min_life) { + /*Found, let's kill it*/ + free_item(c); + return LV_RES_OK; + } + return LV_RES_INV; +} + +static lv_res_t find_item(lv_grad_t * c, void * ctx) +{ + uint32_t * k = (uint32_t *)ctx; + if(c->key == *k) return LV_RES_OK; + return LV_RES_INV; +} + +static lv_grad_t * allocate_item(const lv_grad_dsc_t * g, lv_coord_t w, lv_coord_t h) +{ + lv_coord_t size = g->dir == LV_GRAD_DIR_HOR ? w : h; + lv_coord_t map_size = LV_MAX(w, h); /* The map is being used horizontally (width) unless + no dithering is selected where it's used vertically */ + + size_t req_size = ALIGN(sizeof(lv_grad_t)) + ALIGN(map_size * sizeof(lv_color_t)); +#if _DITHER_GRADIENT + req_size += ALIGN(size * sizeof(lv_color32_t)); +#if LV_DITHER_ERROR_DIFFUSION == 1 + req_size += ALIGN(w * sizeof(lv_scolor24_t)); +#endif +#endif + + size_t act_size = (size_t)(grad_cache_end - LV_GC_ROOT(_lv_grad_cache_mem)); + lv_grad_t * item = NULL; + if(req_size + act_size < grad_cache_size) { + item = (lv_grad_t *)grad_cache_end; + item->not_cached = 0; + } + else { + /*Need to evict items from cache until we find enough space to allocate this one */ + if(req_size <= grad_cache_size) { + while(act_size + req_size > grad_cache_size) { + uint32_t oldest_life = UINT32_MAX; + iterate_cache(&find_oldest_item_life, &oldest_life, NULL); + iterate_cache(&kill_oldest_item, &oldest_life, NULL); + act_size = (size_t)(grad_cache_end - LV_GC_ROOT(_lv_grad_cache_mem)); + } + item = (lv_grad_t *)grad_cache_end; + item->not_cached = 0; + } + else { + /*The cache is too small. Allocate the item manually and free it later.*/ + item = lv_mem_alloc(req_size); + LV_ASSERT_MALLOC(item); + if(item == NULL) return NULL; + item->not_cached = 1; + } + } + + item->key = compute_key(g, size, w); + item->life = 1; + item->filled = 0; + item->alloc_size = map_size; + item->size = size; + if(item->not_cached) { + uint8_t * p = (uint8_t *)item; + item->map = (lv_color_t *)(p + ALIGN(sizeof(*item))); +#if _DITHER_GRADIENT + item->hmap = (lv_color32_t *)(p + ALIGN(sizeof(*item)) + ALIGN(map_size * sizeof(lv_color_t))); +#if LV_DITHER_ERROR_DIFFUSION == 1 + item->error_acc = (lv_scolor24_t *)(p + ALIGN(sizeof(*item)) + ALIGN(size * sizeof(lv_grad_color_t)) + + ALIGN(map_size * sizeof(lv_color_t))); + item->w = w; +#endif +#endif + } + else { + item->map = (lv_color_t *)(grad_cache_end + ALIGN(sizeof(*item))); +#if _DITHER_GRADIENT + item->hmap = (lv_color32_t *)(grad_cache_end + ALIGN(sizeof(*item)) + ALIGN(map_size * sizeof(lv_color_t))); +#if LV_DITHER_ERROR_DIFFUSION == 1 + item->error_acc = (lv_scolor24_t *)(grad_cache_end + ALIGN(sizeof(*item)) + ALIGN(size * sizeof(lv_grad_color_t)) + + ALIGN(map_size * sizeof(lv_color_t))); + item->w = w; +#endif +#endif + grad_cache_end += req_size; + } + return item; +} + + +/********************** + * FUNCTIONS + **********************/ +void lv_gradient_free_cache(void) +{ + lv_mem_free(LV_GC_ROOT(_lv_grad_cache_mem)); + LV_GC_ROOT(_lv_grad_cache_mem) = grad_cache_end = NULL; + grad_cache_size = 0; +} + +void lv_gradient_set_cache_size(size_t max_bytes) +{ + lv_mem_free(LV_GC_ROOT(_lv_grad_cache_mem)); + grad_cache_end = LV_GC_ROOT(_lv_grad_cache_mem) = lv_mem_alloc(max_bytes); + LV_ASSERT_MALLOC(LV_GC_ROOT(_lv_grad_cache_mem)); + lv_memset_00(LV_GC_ROOT(_lv_grad_cache_mem), max_bytes); + grad_cache_size = max_bytes; +} + +lv_grad_t * lv_gradient_get(const lv_grad_dsc_t * g, lv_coord_t w, lv_coord_t h) +{ + /* No gradient, no cache */ + if(g->dir == LV_GRAD_DIR_NONE) return NULL; + + /* Step 0: Check if the cache exist (else create it) */ + static bool inited = false; + if(!inited) { + lv_gradient_set_cache_size(LV_GRAD_CACHE_DEF_SIZE); + inited = true; + } + + /* Step 1: Search cache for the given key */ + lv_coord_t size = g->dir == LV_GRAD_DIR_HOR ? w : h; + uint32_t key = compute_key(g, size, w); + lv_grad_t * item = NULL; + if(iterate_cache(&find_item, &key, &item) == LV_RES_OK) { + item->life++; /* Don't forget to bump the counter */ + return item; + } + + /* Step 2: Need to allocate an item for it */ + item = allocate_item(g, w, h); + if(item == NULL) { + LV_LOG_WARN("Faild to allcoate item for teh gradient"); + return item; + } + + /* Step 3: Fill it with the gradient, as expected */ +#if _DITHER_GRADIENT + for(lv_coord_t i = 0; i < item->size; i++) { + item->hmap[i] = lv_gradient_calculate(g, item->size, i); + } +#if LV_DITHER_ERROR_DIFFUSION == 1 + lv_memset_00(item->error_acc, w * sizeof(lv_scolor24_t)); +#endif +#else + for(lv_coord_t i = 0; i < item->size; i++) { + item->map[i] = lv_gradient_calculate(g, item->size, i); + } +#endif + + return item; +} + +LV_ATTRIBUTE_FAST_MEM lv_grad_color_t lv_gradient_calculate(const lv_grad_dsc_t * dsc, lv_coord_t range, + lv_coord_t frac) +{ + lv_grad_color_t tmp; + lv_color32_t one, two; + /*Clip out-of-bounds first*/ + int32_t min = (dsc->stops[0].frac * range) >> 8; + if(frac <= min) { + GRAD_CONV(tmp, dsc->stops[0].color); + return tmp; + } + + int32_t max = (dsc->stops[dsc->stops_count - 1].frac * range) >> 8; + if(frac >= max) { + GRAD_CONV(tmp, dsc->stops[dsc->stops_count - 1].color); + return tmp; + } + + /*Find the 2 closest stop now*/ + int32_t d = 0; + for(uint8_t i = 1; i < dsc->stops_count; i++) { + int32_t cur = (dsc->stops[i].frac * range) >> 8; + if(frac <= cur) { + one.full = lv_color_to32(dsc->stops[i - 1].color); + two.full = lv_color_to32(dsc->stops[i].color); + min = (dsc->stops[i - 1].frac * range) >> 8; + max = (dsc->stops[i].frac * range) >> 8; + d = max - min; + break; + } + } + + LV_ASSERT(d != 0); + + /*Then interpolate*/ + frac -= min; + lv_opa_t mix = (frac * 255) / d; + lv_opa_t imix = 255 - mix; + + lv_grad_color_t r = GRAD_CM(LV_UDIV255(two.ch.red * mix + one.ch.red * imix), + LV_UDIV255(two.ch.green * mix + one.ch.green * imix), + LV_UDIV255(two.ch.blue * mix + one.ch.blue * imix)); + return r; +} + +void lv_gradient_cleanup(lv_grad_t * grad) +{ + if(grad->not_cached) { + lv_mem_free(grad); + } +} diff --git a/lib/lvgl/src/draw/sw/lv_draw_sw_gradient.h b/lib/lvgl/src/draw/sw/lv_draw_sw_gradient.h new file mode 100644 index 00000000..f5f3215c --- /dev/null +++ b/lib/lvgl/src/draw/sw/lv_draw_sw_gradient.h @@ -0,0 +1,97 @@ +/** + * @file lv_draw_sw_gradient.h + * + */ + +#ifndef LV_DRAW_SW_GRADIENT_H +#define LV_DRAW_SW_GRADIENT_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ +#include "../../misc/lv_color.h" +#include "../../misc/lv_style.h" +#include "lv_draw_sw_dither.h" + +/********************* + * DEFINES + *********************/ +#if LV_GRADIENT_MAX_STOPS < 2 +#error LVGL needs at least 2 stops for gradients. Please increase the LV_GRADIENT_MAX_STOPS +#endif + + +/********************** + * TYPEDEFS + **********************/ +#if _DITHER_GRADIENT +typedef lv_color32_t lv_grad_color_t; +#else +typedef lv_color_t lv_grad_color_t; +#endif + +/** To avoid recomputing gradient for each draw operation, + * it's possible to cache the computation in this structure instance. + * Whenever possible, this structure is reused instead of recomputing the gradient map */ +typedef struct _lv_gradient_cache_t { + uint32_t key; /**< A discriminating key that's built from the drawing operation. + * If the key does not match, the cache item is not used */ + uint32_t life : 30; /**< A life counter that's incremented on usage. Higher counter is + * less likely to be evicted from the cache */ + uint32_t filled : 1; /**< Used to skip dithering in it if already done */ + uint32_t not_cached: 1; /**< The cache was too small so this item is not managed by the cache*/ + lv_color_t * map; /**< The computed gradient low bitdepth color map, points into the + * cache's buffer, no free needed */ + lv_coord_t alloc_size; /**< The map allocated size in colors */ + lv_coord_t size; /**< The computed gradient color map size, in colors */ +#if _DITHER_GRADIENT + lv_color32_t * hmap; /**< If dithering, we need to store the current, high bitdepth gradient + * map too, points to the cache's buffer, no free needed */ +#if LV_DITHER_ERROR_DIFFUSION == 1 + lv_scolor24_t * error_acc; /**< Error diffusion dithering algorithm requires storing the last error + * drawn, points to the cache's buffer, no free needed */ + lv_coord_t w; /**< The error array width in pixels */ +#endif +#endif +} lv_grad_t; + + +/********************** + * PROTOTYPES + **********************/ +/** Compute the color in the given gradient and fraction + * Gradient are specified in a virtual [0-255] range, so this function scales the virtual range to the given range + * @param dsc The gradient descriptor to use + * @param range The range to use in computation. + * @param frac The current part used in the range. frac is in [0; range] + */ +LV_ATTRIBUTE_FAST_MEM lv_grad_color_t lv_gradient_calculate(const lv_grad_dsc_t * dsc, lv_coord_t range, + lv_coord_t frac); + +/** + * Set the gradient cache size + * @param max_bytes Max cahce size + */ +void lv_gradient_set_cache_size(size_t max_bytes); + +/** Free the gradient cache */ +void lv_gradient_free_cache(void); + +/** Get a gradient cache from the given parameters */ +lv_grad_t * lv_gradient_get(const lv_grad_dsc_t * gradient, lv_coord_t w, lv_coord_t h); + +/** + * Clean up the gradient item after it was get with `lv_grad_get_from_cache`. + * @param grad pointer to a gradient + */ +void lv_gradient_cleanup(lv_grad_t * grad); + +#ifdef __cplusplus +} /*extern "C"*/ +#endif + +#endif /*LV_DRAW_GRADIENT_H*/ diff --git a/lib/lvgl/src/draw/sw/lv_draw_sw_img.c b/lib/lvgl/src/draw/sw/lv_draw_sw_img.c new file mode 100644 index 00000000..9578bc7c --- /dev/null +++ b/lib/lvgl/src/draw/sw/lv_draw_sw_img.c @@ -0,0 +1,297 @@ +/** + * @file lv_draw_img.c + * + */ + +/********************* + * INCLUDES + *********************/ +#include "lv_draw_sw.h" +#include "../lv_img_cache.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 + *********************/ +#define MAX_BUF_SIZE (uint32_t) lv_disp_get_hor_res(_lv_refr_get_disp_refreshing()) + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ +static void convert_cb(const lv_area_t * dest_area, const void * src_buf, lv_coord_t src_w, lv_coord_t src_h, + lv_coord_t src_stride, const lv_draw_img_dsc_t * draw_dsc, lv_img_cf_t cf, lv_color_t * cbuf, lv_opa_t * abuf); + +/********************** + * STATIC VARIABLES + **********************/ + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + + +LV_ATTRIBUTE_FAST_MEM void lv_draw_sw_img_decoded(struct _lv_draw_ctx_t * draw_ctx, const lv_draw_img_dsc_t * draw_dsc, + const lv_area_t * coords, const uint8_t * src_buf, lv_img_cf_t cf) +{ + /*Use the clip area as draw area*/ + lv_area_t draw_area; + lv_area_copy(&draw_area, draw_ctx->clip_area); + + bool mask_any = lv_draw_mask_is_any(&draw_area); + bool transform = draw_dsc->angle != 0 || draw_dsc->zoom != LV_IMG_ZOOM_NONE ? true : false; + + lv_area_t blend_area; + lv_draw_sw_blend_dsc_t blend_dsc; + + lv_memset_00(&blend_dsc, sizeof(lv_draw_sw_blend_dsc_t)); + blend_dsc.opa = draw_dsc->opa; + blend_dsc.blend_mode = draw_dsc->blend_mode; + blend_dsc.blend_area = &blend_area; + + /*The simplest case just copy the pixels into the draw_buf*/ + if(!mask_any && !transform && cf == LV_IMG_CF_TRUE_COLOR && draw_dsc->recolor_opa == LV_OPA_TRANSP) { + blend_dsc.src_buf = (const lv_color_t *)src_buf; + + blend_dsc.blend_area = coords; + lv_draw_sw_blend(draw_ctx, &blend_dsc); + } + else if(!mask_any && !transform && cf == LV_IMG_CF_ALPHA_8BIT) { + lv_area_t clipped_coords; + if(!_lv_area_intersect(&clipped_coords, coords, draw_ctx->clip_area)) return; + + blend_dsc.mask_buf = (lv_opa_t *)src_buf; + blend_dsc.mask_area = coords; + blend_dsc.src_buf = NULL; + blend_dsc.color = draw_dsc->recolor; + blend_dsc.mask_res = LV_DRAW_MASK_RES_CHANGED; + + blend_dsc.blend_area = coords; + lv_draw_sw_blend(draw_ctx, &blend_dsc); + } +#if LV_COLOR_DEPTH == 16 + else if(!mask_any && !transform && cf == LV_IMG_CF_RGB565A8 && draw_dsc->recolor_opa == LV_OPA_TRANSP) { + lv_coord_t src_w = lv_area_get_width(coords); + lv_coord_t src_h = lv_area_get_height(coords); + blend_dsc.src_buf = (const lv_color_t *)src_buf; + blend_dsc.mask_buf = (lv_opa_t *)src_buf; + blend_dsc.mask_buf += sizeof(lv_color_t) * src_w * src_h; + blend_dsc.blend_area = coords; + blend_dsc.mask_area = coords; + blend_dsc.mask_res = LV_DRAW_MASK_RES_CHANGED; + lv_draw_sw_blend(draw_ctx, &blend_dsc); + } +#endif + /*In the other cases every pixel need to be checked one-by-one*/ + else { + blend_area.x1 = draw_ctx->clip_area->x1; + blend_area.x2 = draw_ctx->clip_area->x2; + blend_area.y1 = draw_ctx->clip_area->y1; + blend_area.y2 = draw_ctx->clip_area->y2; + + lv_coord_t src_w = lv_area_get_width(coords); + lv_coord_t src_h = lv_area_get_height(coords); + lv_coord_t blend_h = lv_area_get_height(&blend_area); + lv_coord_t blend_w = lv_area_get_width(&blend_area); + + uint32_t max_buf_size = MAX_BUF_SIZE; + uint32_t blend_size = lv_area_get_size(&blend_area); + uint32_t buf_h; + uint32_t buf_w = blend_w; + if(blend_size <= max_buf_size) { + buf_h = blend_h; + } + else { + /*Round to full lines*/ + buf_h = max_buf_size / blend_w; + } + + /*Create buffers and masks*/ + uint32_t buf_size = buf_w * buf_h; + + lv_color_t * rgb_buf = lv_mem_buf_get(buf_size * sizeof(lv_color_t)); + lv_opa_t * mask_buf = lv_mem_buf_get(buf_size); + blend_dsc.mask_buf = mask_buf; + blend_dsc.mask_area = &blend_area; + blend_dsc.mask_res = LV_DRAW_MASK_RES_CHANGED; + blend_dsc.src_buf = rgb_buf; + lv_coord_t y_last = blend_area.y2; + blend_area.y2 = blend_area.y1 + buf_h - 1; + + lv_draw_mask_res_t mask_res_def = (cf != LV_IMG_CF_TRUE_COLOR || draw_dsc->angle || + draw_dsc->zoom != LV_IMG_ZOOM_NONE) ? + LV_DRAW_MASK_RES_CHANGED : LV_DRAW_MASK_RES_FULL_COVER; + blend_dsc.mask_res = mask_res_def; + + while(blend_area.y1 <= y_last) { + /*Apply transformations if any or separate the channels*/ + lv_area_t transform_area; + lv_area_copy(&transform_area, &blend_area); + lv_area_move(&transform_area, -coords->x1, -coords->y1); + if(transform) { + lv_draw_transform(draw_ctx, &transform_area, src_buf, src_w, src_h, src_w, + draw_dsc, cf, rgb_buf, mask_buf); + } + else { + convert_cb(&transform_area, src_buf, src_w, src_h, src_w, draw_dsc, cf, rgb_buf, mask_buf); + } + + /*Apply recolor*/ + if(draw_dsc->recolor_opa > LV_OPA_MIN) { + uint16_t premult_v[3]; + lv_opa_t recolor_opa = draw_dsc->recolor_opa; + lv_color_t recolor = draw_dsc->recolor; + lv_color_premult(recolor, recolor_opa, premult_v); + recolor_opa = 255 - recolor_opa; + uint32_t i; + for(i = 0; i < buf_size; i++) { + rgb_buf[i] = lv_color_mix_premult(premult_v, rgb_buf[i], recolor_opa); + } + } +#if LV_DRAW_COMPLEX + /*Apply the masks if any*/ + if(mask_any) { + lv_coord_t y; + lv_opa_t * mask_buf_tmp = mask_buf; + for(y = blend_area.y1; y <= blend_area.y2; y++) { + lv_draw_mask_res_t mask_res_line; + mask_res_line = lv_draw_mask_apply(mask_buf_tmp, blend_area.x1, y, blend_w); + + if(mask_res_line == LV_DRAW_MASK_RES_TRANSP) { + lv_memset_00(mask_buf_tmp, blend_w); + blend_dsc.mask_res = LV_DRAW_MASK_RES_CHANGED; + } + else if(mask_res_line == LV_DRAW_MASK_RES_CHANGED) { + blend_dsc.mask_res = LV_DRAW_MASK_RES_CHANGED; + } + mask_buf_tmp += blend_w; + } + } +#endif + + /*Blend*/ + lv_draw_sw_blend(draw_ctx, &blend_dsc); + + /*Go the the next lines*/ + blend_area.y1 = blend_area.y2 + 1; + blend_area.y2 = blend_area.y1 + buf_h - 1; + if(blend_area.y2 > y_last) blend_area.y2 = y_last; + } + + lv_mem_buf_release(mask_buf); + lv_mem_buf_release(rgb_buf); + } +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +/* Separate the image channels to RGB and Alpha to match LV_COLOR_DEPTH settings*/ +static void convert_cb(const lv_area_t * dest_area, const void * src_buf, lv_coord_t src_w, lv_coord_t src_h, + lv_coord_t src_stride, const lv_draw_img_dsc_t * draw_dsc, lv_img_cf_t cf, lv_color_t * cbuf, lv_opa_t * abuf) +{ + LV_UNUSED(draw_dsc); + LV_UNUSED(src_h); + LV_UNUSED(src_w); + + const uint8_t * src_tmp8 = (const uint8_t *)src_buf; + lv_coord_t y; + lv_coord_t x; + + if(cf == LV_IMG_CF_TRUE_COLOR || cf == LV_IMG_CF_TRUE_COLOR_CHROMA_KEYED) { + uint32_t px_cnt = lv_area_get_size(dest_area); + lv_memset_ff(abuf, px_cnt); + + src_tmp8 += (src_stride * dest_area->y1 * sizeof(lv_color_t)) + dest_area->x1 * sizeof(lv_color_t); + uint32_t dest_w = lv_area_get_width(dest_area); + uint32_t dest_w_byte = dest_w * sizeof(lv_color_t); + + lv_coord_t src_stride_byte = src_stride * sizeof(lv_color_t); + lv_color_t * cbuf_tmp = cbuf; + for(y = dest_area->y1; y <= dest_area->y2; y++) { + lv_memcpy(cbuf_tmp, src_tmp8, dest_w_byte); + src_tmp8 += src_stride_byte; + cbuf_tmp += dest_w; + } + + /*Make "holes" for with Chroma keying*/ + if(cf == LV_IMG_CF_TRUE_COLOR_CHROMA_KEYED) { + uint32_t i; + lv_color_t chk = LV_COLOR_CHROMA_KEY; +#if LV_COLOR_DEPTH == 8 || LV_COLOR_DEPTH == 1 + uint8_t * cbuf_uint = (uint8_t *)cbuf; + uint8_t chk_v = chk.full; +#elif LV_COLOR_DEPTH == 16 + uint16_t * cbuf_uint = (uint16_t *)cbuf; + uint16_t chk_v = chk.full; +#elif LV_COLOR_DEPTH == 32 + uint32_t * cbuf_uint = (uint32_t *)cbuf; + uint32_t chk_v = chk.full; +#endif + for(i = 0; i < px_cnt; i++) { + if(chk_v == cbuf_uint[i]) abuf[i] = 0x00; + } + } + } + else if(cf == LV_IMG_CF_TRUE_COLOR_ALPHA) { + src_tmp8 += (src_stride * dest_area->y1 * LV_IMG_PX_SIZE_ALPHA_BYTE) + dest_area->x1 * LV_IMG_PX_SIZE_ALPHA_BYTE; + + lv_coord_t src_new_line_step_px = (src_stride - lv_area_get_width(dest_area)); + lv_coord_t src_new_line_step_byte = src_new_line_step_px * LV_IMG_PX_SIZE_ALPHA_BYTE; + + lv_coord_t dest_h = lv_area_get_height(dest_area); + lv_coord_t dest_w = lv_area_get_width(dest_area); + for(y = 0; y < dest_h; y++) { + for(x = 0; x < dest_w; x++) { + abuf[x] = src_tmp8[LV_IMG_PX_SIZE_ALPHA_BYTE - 1]; +#if LV_COLOR_DEPTH == 8 || LV_COLOR_DEPTH == 1 + cbuf[x].full = *src_tmp8; +#elif LV_COLOR_DEPTH == 16 + cbuf[x].full = *src_tmp8 + ((*(src_tmp8 + 1)) << 8); +#elif LV_COLOR_DEPTH == 32 + cbuf[x] = *((lv_color_t *) src_tmp8); + cbuf[x].ch.alpha = 0xff; +#endif + src_tmp8 += LV_IMG_PX_SIZE_ALPHA_BYTE; + + } + cbuf += dest_w; + abuf += dest_w; + src_tmp8 += src_new_line_step_byte; + } + } + else if(cf == LV_IMG_CF_RGB565A8) { + src_tmp8 += (src_stride * dest_area->y1 * sizeof(lv_color_t)) + dest_area->x1 * sizeof(lv_color_t); + + lv_coord_t src_stride_byte = src_stride * sizeof(lv_color_t); + + lv_coord_t dest_h = lv_area_get_height(dest_area); + lv_coord_t dest_w = lv_area_get_width(dest_area); + for(y = 0; y < dest_h; y++) { + lv_memcpy(cbuf, src_tmp8, dest_w * sizeof(lv_color_t)); + cbuf += dest_w; + src_tmp8 += src_stride_byte; + } + + src_tmp8 = (const uint8_t *)src_buf; + src_tmp8 += sizeof(lv_color_t) * src_w * src_h; + src_tmp8 += src_stride * dest_area->y1 + dest_area->x1; + for(y = 0; y < dest_h; y++) { + lv_memcpy(abuf, src_tmp8, dest_w); + abuf += dest_w; + src_tmp8 += src_stride; + } + } +} diff --git a/lib/lvgl/src/draw/sw/lv_draw_sw_layer.c b/lib/lvgl/src/draw/sw/lv_draw_sw_layer.c new file mode 100644 index 00000000..b53c662e --- /dev/null +++ b/lib/lvgl/src/draw/sw/lv_draw_sw_layer.c @@ -0,0 +1,150 @@ +/** + * @file lv_draw_sw_layer.h + * + */ + +/********************* + * INCLUDES + *********************/ +#include "lv_draw_sw.h" +#include "../../hal/lv_hal_disp.h" +#include "../../misc/lv_area.h" +#include "../../core/lv_refr.h" + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ + +/********************** + * STATIC VARIABLES + **********************/ + +/********************** + * GLOBAL VARIABLES + **********************/ + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + + +struct _lv_draw_layer_ctx_t * lv_draw_sw_layer_create(struct _lv_draw_ctx_t * draw_ctx, lv_draw_layer_ctx_t * layer_ctx, + lv_draw_layer_flags_t flags) +{ + if(LV_COLOR_SCREEN_TRANSP == 0 && (flags & LV_DRAW_LAYER_FLAG_HAS_ALPHA)) { + LV_LOG_WARN("Rendering this widget needs LV_COLOR_SCREEN_TRANSP 1"); + return NULL; + } + + lv_draw_sw_layer_ctx_t * layer_sw_ctx = (lv_draw_sw_layer_ctx_t *) layer_ctx; + uint32_t px_size = flags & LV_DRAW_LAYER_FLAG_HAS_ALPHA ? LV_IMG_PX_SIZE_ALPHA_BYTE : sizeof(lv_color_t); + if(flags & LV_DRAW_LAYER_FLAG_CAN_SUBDIVIDE) { + layer_sw_ctx->buf_size_bytes = LV_LAYER_SIMPLE_BUF_SIZE; + uint32_t full_size = lv_area_get_size(&layer_sw_ctx->base_draw.area_full) * px_size; + if(layer_sw_ctx->buf_size_bytes > full_size) layer_sw_ctx->buf_size_bytes = full_size; + layer_sw_ctx->base_draw.buf = lv_mem_alloc(layer_sw_ctx->buf_size_bytes); + if(layer_sw_ctx->base_draw.buf == NULL) { + LV_LOG_WARN("Cannot allocate %"LV_PRIu32" bytes for layer buffer. Allocating %"LV_PRIu32" bytes instead. (Reduced performance)", + (uint32_t)layer_sw_ctx->buf_size_bytes, (uint32_t)LV_LAYER_SIMPLE_FALLBACK_BUF_SIZE * px_size); + layer_sw_ctx->buf_size_bytes = LV_LAYER_SIMPLE_FALLBACK_BUF_SIZE; + layer_sw_ctx->base_draw.buf = lv_mem_alloc(layer_sw_ctx->buf_size_bytes); + if(layer_sw_ctx->base_draw.buf == NULL) { + return NULL; + } + } + layer_sw_ctx->base_draw.area_act = layer_sw_ctx->base_draw.area_full; + layer_sw_ctx->base_draw.area_act.y2 = layer_sw_ctx->base_draw.area_full.y1; + lv_coord_t w = lv_area_get_width(&layer_sw_ctx->base_draw.area_act); + layer_sw_ctx->base_draw.max_row_with_alpha = layer_sw_ctx->buf_size_bytes / w / LV_IMG_PX_SIZE_ALPHA_BYTE; + layer_sw_ctx->base_draw.max_row_with_no_alpha = layer_sw_ctx->buf_size_bytes / w / sizeof(lv_color_t); + } + else { + layer_sw_ctx->base_draw.area_act = layer_sw_ctx->base_draw.area_full; + layer_sw_ctx->buf_size_bytes = lv_area_get_size(&layer_sw_ctx->base_draw.area_full) * px_size; + layer_sw_ctx->base_draw.buf = lv_mem_alloc(layer_sw_ctx->buf_size_bytes); + lv_memset_00(layer_sw_ctx->base_draw.buf, layer_sw_ctx->buf_size_bytes); + layer_sw_ctx->has_alpha = flags & LV_DRAW_LAYER_FLAG_HAS_ALPHA ? 1 : 0; + if(layer_sw_ctx->base_draw.buf == NULL) { + return NULL; + } + + draw_ctx->buf = layer_sw_ctx->base_draw.buf; + draw_ctx->buf_area = &layer_sw_ctx->base_draw.area_act; + draw_ctx->clip_area = &layer_sw_ctx->base_draw.area_act; + + lv_disp_t * disp_refr = _lv_refr_get_disp_refreshing(); + disp_refr->driver->screen_transp = flags & LV_DRAW_LAYER_FLAG_HAS_ALPHA ? 1 : 0; + } + + return layer_ctx; +} + +void lv_draw_sw_layer_adjust(struct _lv_draw_ctx_t * draw_ctx, struct _lv_draw_layer_ctx_t * layer_ctx, + lv_draw_layer_flags_t flags) +{ + + lv_draw_sw_layer_ctx_t * layer_sw_ctx = (lv_draw_sw_layer_ctx_t *) layer_ctx; + lv_disp_t * disp_refr = _lv_refr_get_disp_refreshing(); + if(flags & LV_DRAW_LAYER_FLAG_HAS_ALPHA) { + lv_memset_00(layer_ctx->buf, layer_sw_ctx->buf_size_bytes); + layer_sw_ctx->has_alpha = 1; + disp_refr->driver->screen_transp = 1; + } + else { + layer_sw_ctx->has_alpha = 0; + disp_refr->driver->screen_transp = 0; + } + + draw_ctx->buf = layer_ctx->buf; + draw_ctx->buf_area = &layer_ctx->area_act; + draw_ctx->clip_area = &layer_ctx->area_act; +} + +void lv_draw_sw_layer_blend(struct _lv_draw_ctx_t * draw_ctx, struct _lv_draw_layer_ctx_t * layer_ctx, + const lv_draw_img_dsc_t * draw_dsc) +{ + lv_draw_sw_layer_ctx_t * layer_sw_ctx = (lv_draw_sw_layer_ctx_t *) layer_ctx; + + lv_img_dsc_t img; + img.data = draw_ctx->buf; + img.header.always_zero = 0; + img.header.w = lv_area_get_width(draw_ctx->buf_area); + img.header.h = lv_area_get_height(draw_ctx->buf_area); + img.header.cf = layer_sw_ctx->has_alpha ? LV_IMG_CF_TRUE_COLOR_ALPHA : LV_IMG_CF_TRUE_COLOR; + + /*Restore the original draw_ctx*/ + draw_ctx->buf = layer_ctx->original.buf; + draw_ctx->buf_area = layer_ctx->original.buf_area; + draw_ctx->clip_area = layer_ctx->original.clip_area; + lv_disp_t * disp_refr = _lv_refr_get_disp_refreshing(); + disp_refr->driver->screen_transp = layer_ctx->original.screen_transp; + + /*Blend the layer*/ + lv_draw_img(draw_ctx, draw_dsc, &layer_ctx->area_act, &img); + lv_draw_wait_for_finish(draw_ctx); + lv_img_cache_invalidate_src(&img); +} + +void lv_draw_sw_layer_destroy(lv_draw_ctx_t * draw_ctx, lv_draw_layer_ctx_t * layer_ctx) +{ + LV_UNUSED(draw_ctx); + + lv_mem_free(layer_ctx->buf); +} + + +/********************** + * STATIC FUNCTIONS + **********************/ diff --git a/lib/lvgl/src/draw/sw/lv_draw_sw_letter.c b/lib/lvgl/src/draw/sw/lv_draw_sw_letter.c new file mode 100644 index 00000000..9522888c --- /dev/null +++ b/lib/lvgl/src/draw/sw/lv_draw_sw_letter.c @@ -0,0 +1,573 @@ +/** + * @file lv_draw_sw_letter.c + * + */ + +/********************* + * INCLUDES + *********************/ +#include "lv_draw_sw.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" + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ + +LV_ATTRIBUTE_FAST_MEM static void draw_letter_normal(lv_draw_ctx_t * draw_ctx, const lv_draw_label_dsc_t * dsc, + const lv_point_t * pos, lv_font_glyph_dsc_t * g, const uint8_t * map_p); + + +#if LV_DRAW_COMPLEX && LV_USE_FONT_SUBPX +static void draw_letter_subpx(lv_draw_ctx_t * draw_ctx, const lv_draw_label_dsc_t * dsc, const lv_point_t * pos, + lv_font_glyph_dsc_t * g, const uint8_t * map_p); +#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) + */ +void lv_draw_sw_letter(lv_draw_ctx_t * draw_ctx, const lv_draw_label_dsc_t * dsc, const lv_point_t * pos_p, + uint32_t letter) +{ + lv_font_glyph_dsc_t g; + bool g_ret = lv_font_get_glyph_dsc(dsc->font, &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+%" PRIX32, letter); + +#if LV_USE_FONT_PLACEHOLDER + /* draw placeholder */ + lv_area_t glyph_coords; + lv_draw_rect_dsc_t glyph_dsc; + lv_coord_t begin_x = pos_p->x + g.ofs_x; + lv_coord_t begin_y = pos_p->y + g.ofs_y; + lv_area_set(&glyph_coords, begin_x, begin_y, begin_x + g.box_w, begin_y + g.box_h); + lv_draw_rect_dsc_init(&glyph_dsc); + glyph_dsc.bg_opa = LV_OPA_MIN; + glyph_dsc.outline_opa = LV_OPA_MIN; + glyph_dsc.shadow_opa = LV_OPA_MIN; + glyph_dsc.bg_img_opa = LV_OPA_MIN; + glyph_dsc.border_color = dsc->color; + glyph_dsc.border_width = 1; + draw_ctx->draw_rect(draw_ctx, &glyph_dsc, &glyph_coords); +#endif + } + return; + } + + /*Don't draw anything if the character is empty. E.g. space*/ + if((g.box_h == 0) || (g.box_w == 0)) return; + + lv_point_t gpos; + gpos.x = pos_p->x + g.ofs_x; + gpos.y = pos_p->y + (dsc->font->line_height - dsc->font->base_line) - g.box_h - g.ofs_y; + + /*If the letter is completely out of mask don't draw it*/ + if(gpos.x + g.box_w < draw_ctx->clip_area->x1 || + gpos.x > draw_ctx->clip_area->x2 || + gpos.y + g.box_h < draw_ctx->clip_area->y1 || + gpos.y > draw_ctx->clip_area->y2) { + return; + } + + const uint8_t * map_p = lv_font_get_glyph_bitmap(g.resolved_font, letter); + if(map_p == NULL) { + LV_LOG_WARN("lv_draw_letter: character's bitmap not found"); + return; + } + + if(g.resolved_font->subpx) { +#if LV_DRAW_COMPLEX && LV_USE_FONT_SUBPX + draw_letter_subpx(draw_ctx, dsc, &gpos, &g, map_p); +#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(draw_ctx, dsc, &gpos, &g, map_p); + } +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +LV_ATTRIBUTE_FAST_MEM static void draw_letter_normal(lv_draw_ctx_t * draw_ctx, const lv_draw_label_dsc_t * dsc, + const lv_point_t * pos, lv_font_glyph_dsc_t * g, const uint8_t * map_p) +{ + + const uint8_t * bpp_opa_table_p; + uint32_t bitmask_init; + uint32_t bitmask; + uint32_t bpp = g->bpp; + lv_opa_t opa = dsc->opa; + uint32_t shades; + if(bpp == 3) bpp = 4; + +#if LV_USE_IMGFONT + if(bpp == LV_IMGFONT_BPP) { //is imgfont + lv_area_t fill_area; + fill_area.x1 = pos->x; + fill_area.y1 = pos->y; + fill_area.x2 = pos->x + g->box_w - 1; + fill_area.y2 = pos->y + g->box_h - 1; + lv_draw_img_dsc_t img_dsc; + lv_draw_img_dsc_init(&img_dsc); + img_dsc.angle = 0; + img_dsc.zoom = LV_IMG_ZOOM_NONE; + img_dsc.opa = dsc->opa; + img_dsc.blend_mode = dsc->blend_mode; + lv_draw_img(draw_ctx, &img_dsc, &fill_area, map_p); + return; + } +#endif + + 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 >= draw_ctx->clip_area->x1 ? 0 : draw_ctx->clip_area->x1 - pos->x; + int32_t col_end = pos->x + box_w <= draw_ctx->clip_area->x2 ? box_w : draw_ctx->clip_area->x2 - pos->x + 1; + int32_t row_start = pos->y >= draw_ctx->clip_area->y1 ? 0 : draw_ctx->clip_area->y1 - pos->y; + int32_t row_end = pos->y + box_h <= draw_ctx->clip_area->y2 ? box_h : draw_ctx->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_draw_sw_blend_dsc_t blend_dsc; + lv_memset_00(&blend_dsc, sizeof(blend_dsc)); + blend_dsc.color = dsc->color; + blend_dsc.opa = dsc->opa; + blend_dsc.blend_mode = dsc->blend_mode; + + 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); + blend_dsc.mask_buf = mask_buf; + 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_coord_t fill_w = lv_area_get_width(&fill_area); + 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 + blend_dsc.blend_area = &fill_area; + blend_dsc.mask_area = &fill_area; + + 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) { + blend_dsc.mask_res = lv_draw_mask_apply(mask_buf + mask_p_start, fill_area.x1, fill_area.y2, + fill_w); + if(blend_dsc.mask_res == LV_DRAW_MASK_RES_TRANSP) { + lv_memset_00(mask_buf + mask_p_start, fill_w); + } + } +#endif + + if((uint32_t) mask_p + (col_end - col_start) < mask_buf_size) { + fill_area.y2 ++; + } + else { + blend_dsc.mask_res = LV_DRAW_MASK_RES_CHANGED; + lv_draw_sw_blend(draw_ctx, &blend_dsc); + + 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--; + blend_dsc.mask_res = LV_DRAW_MASK_RES_CHANGED; + lv_draw_sw_blend(draw_ctx, &blend_dsc); + mask_p = 0; + } + + lv_mem_buf_release(mask_buf); +} + +#if LV_DRAW_COMPLEX && LV_USE_FONT_SUBPX +static void draw_letter_subpx(lv_draw_ctx_t * draw_ctx, const lv_draw_label_dsc_t * dsc, const lv_point_t * pos, + lv_font_glyph_dsc_t * g, const uint8_t * map_p) +{ + const uint8_t * bpp_opa_table; + uint32_t bitmask_init; + uint32_t bitmask; + uint32_t bpp = g->bpp; + lv_opa_t opa = dsc->opa; + 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 >= draw_ctx->clip_area->x1 ? 0 : (draw_ctx->clip_area->x1 - pos->x) * 3; + int32_t col_end = pos->x + box_w / 3 <= draw_ctx->clip_area->x2 ? box_w : (draw_ctx->clip_area->x2 - pos->x + 1) * 3; + int32_t row_start = pos->y >= draw_ctx->clip_area->y1 ? 0 : draw_ctx->clip_area->y1 - pos->y; + int32_t row_end = pos->y + box_h <= draw_ctx->clip_area->y2 ? box_h : draw_ctx->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; + + lv_coord_t hor_res = lv_disp_get_hor_res(_lv_refr_get_disp_refreshing()); + int32_t mask_buf_size = box_w * box_h > hor_res ? hor_res : 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)); + + int32_t dest_buf_stride = lv_area_get_width(draw_ctx->buf_area); + lv_color_t * dest_buf_tmp = draw_ctx->buf; + + /*Set a pointer on draw_buf to the first pixel of the letter*/ + dest_buf_tmp += ((pos->y - draw_ctx->buf_area->y1) * dest_buf_stride) + pos->x - draw_ctx->buf_area->x1; + + /*If the letter is partially out of mask the move there on draw_buf*/ + dest_buf_tmp += (row_start * dest_buf_stride) + 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]; + + lv_color_t color = dsc->color; +#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 + + lv_draw_sw_blend_dsc_t blend_dsc; + lv_memset_00(&blend_dsc, sizeof(blend_dsc)); + blend_dsc.blend_area = &map_area; + blend_dsc.mask_area = &map_area; + blend_dsc.src_buf = color_buf; + blend_dsc.mask_buf = mask_buf; + blend_dsc.opa = opa; + blend_dsc.blend_mode = dsc->blend_mode; + + 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_MAX) { + 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] = {dest_buf_tmp->ch.red, dest_buf_tmp->ch.green, dest_buf_tmp->ch.blue}; +#else + uint8_t bg_rgb[3] = {dest_buf_tmp->ch.red, + (dest_buf_tmp->ch.green_h << 3) + dest_buf_tmp->ch.green_l, + dest_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++; + dest_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) { + blend_dsc.mask_res = lv_draw_mask_apply(mask_buf + mask_p_start, map_area.x1, map_area.y2, + lv_area_get_width(&map_area)); + if(blend_dsc.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 { + blend_dsc.mask_res = LV_DRAW_MASK_RES_CHANGED; + lv_draw_sw_blend(draw_ctx, &blend_dsc); + + 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*/ + dest_buf_tmp += dest_buf_stride - (col_end - col_start) / 3; + } + + /*Flush the last part*/ + if(map_area.y1 != map_area.y2) { + map_area.y2--; + blend_dsc.mask_res = LV_DRAW_MASK_RES_CHANGED; + lv_draw_sw_blend(draw_ctx, &blend_dsc); + } + + lv_mem_buf_release(mask_buf); + lv_mem_buf_release(color_buf); +} +#endif /*LV_DRAW_COMPLEX && LV_USE_FONT_SUBPX*/ + diff --git a/lib/lvgl/src/draw/sw/lv_draw_sw_line.c b/lib/lvgl/src/draw/sw/lv_draw_sw_line.c new file mode 100644 index 00000000..73833c16 --- /dev/null +++ b/lib/lvgl/src/draw/sw/lv_draw_sw_line.c @@ -0,0 +1,443 @@ +/** + * @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(struct _lv_draw_ctx_t * draw_ctx, const lv_draw_line_dsc_t * dsc, + const lv_point_t * point1, const lv_point_t * point2); +LV_ATTRIBUTE_FAST_MEM static void draw_line_hor(struct _lv_draw_ctx_t * draw_ctx, const lv_draw_line_dsc_t * dsc, + const lv_point_t * point1, const lv_point_t * point2); +LV_ATTRIBUTE_FAST_MEM static void draw_line_ver(struct _lv_draw_ctx_t * draw_ctx, const lv_draw_line_dsc_t * dsc, + const lv_point_t * point1, const lv_point_t * point2); + +/********************** + * 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(struct _lv_draw_ctx_t * draw_ctx, const lv_draw_line_dsc_t * dsc, + const lv_point_t * point1, const lv_point_t * point2) +{ + 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, draw_ctx->clip_area); + if(!is_common) return; + const lv_area_t * clip_area_ori = draw_ctx->clip_area; + draw_ctx->clip_area = &clip_line; + + if(point1->y == point2->y) draw_line_hor(draw_ctx, dsc, point1, point2); + else if(point1->x == point2->x) draw_line_ver(draw_ctx, dsc, point1, point2); + else draw_line_skew(draw_ctx, dsc, point1, point2); + + 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(draw_ctx, &cir_dsc, &cir_area); + } + + 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(draw_ctx, &cir_dsc, &cir_area); + } + } + + draw_ctx->clip_area = clip_area_ori; +} + +/********************** + * STATIC FUNCTIONS + **********************/ + + +LV_ATTRIBUTE_FAST_MEM static void draw_line_hor(struct _lv_draw_ctx_t * draw_ctx, const lv_draw_line_dsc_t * dsc, + const lv_point_t * point1, const lv_point_t * point2) +{ + 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 blend_area; + blend_area.x1 = LV_MIN(point1->x, point2->x); + blend_area.x2 = LV_MAX(point1->x, point2->x) - 1; + blend_area.y1 = point1->y - w_half1; + blend_area.y2 = point1->y + w_half0; + + bool is_common; + is_common = _lv_area_intersect(&blend_area, &blend_area, draw_ctx->clip_area); + if(!is_common) return; + + bool dashed = dsc->dash_gap && dsc->dash_width ? true : false; + bool simple_mode = true; + if(lv_draw_mask_is_any(&blend_area)) simple_mode = false; + else if(dashed) simple_mode = false; + + lv_draw_sw_blend_dsc_t blend_dsc; + lv_memset_00(&blend_dsc, sizeof(blend_dsc)); + blend_dsc.blend_area = &blend_area; + blend_dsc.color = dsc->color; + blend_dsc.opa = dsc->opa; + + /*If there is no mask then simply draw a rectangle*/ + if(simple_mode) { + lv_draw_sw_blend(draw_ctx, &blend_dsc); + } +#if LV_DRAW_COMPLEX + /*If there other mask apply it*/ + else { + + int32_t blend_area_w = lv_area_get_width(&blend_area); + + lv_coord_t y2 = blend_area.y2; + blend_area.y2 = blend_area.y1; + + lv_coord_t dash_start = 0; + if(dashed) { + dash_start = (blend_area.x1) % (dsc->dash_gap + dsc->dash_width); + } + + lv_opa_t * mask_buf = lv_mem_buf_get(blend_area_w); + blend_dsc.mask_buf = mask_buf; + blend_dsc.mask_area = &blend_area; + int32_t h; + for(h = blend_area.y1; h <= y2; h++) { + lv_memset_ff(mask_buf, blend_area_w); + blend_dsc.mask_res = lv_draw_mask_apply(mask_buf, blend_area.x1, h, blend_area_w); + + if(dashed) { + if(blend_dsc.mask_res != LV_DRAW_MASK_RES_TRANSP) { + lv_coord_t dash_cnt = dash_start; + lv_coord_t i; + for(i = 0; i < blend_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; + } + } + + blend_dsc.mask_res = LV_DRAW_MASK_RES_CHANGED; + } + } + + lv_draw_sw_blend(draw_ctx, &blend_dsc); + + blend_area.y1++; + blend_area.y2++; + } + lv_mem_buf_release(mask_buf); + } +#endif /*LV_DRAW_COMPLEX*/ +} + +LV_ATTRIBUTE_FAST_MEM static void draw_line_ver(struct _lv_draw_ctx_t * draw_ctx, const lv_draw_line_dsc_t * dsc, + const lv_point_t * point1, const lv_point_t * point2) +{ + 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 blend_area; + blend_area.x1 = point1->x - w_half1; + blend_area.x2 = point1->x + w_half0; + blend_area.y1 = LV_MIN(point1->y, point2->y); + blend_area.y2 = LV_MAX(point1->y, point2->y) - 1; + + bool is_common; + is_common = _lv_area_intersect(&blend_area, &blend_area, draw_ctx->clip_area); + if(!is_common) return; + + bool dashed = dsc->dash_gap && dsc->dash_width ? true : false; + bool simple_mode = true; + if(lv_draw_mask_is_any(&blend_area)) simple_mode = false; + else if(dashed) simple_mode = false; + + lv_draw_sw_blend_dsc_t blend_dsc; + lv_memset_00(&blend_dsc, sizeof(blend_dsc)); + blend_dsc.blend_area = &blend_area; + blend_dsc.color = dsc->color; + blend_dsc.opa = dsc->opa; + + /*If there is no mask then simply draw a rectangle*/ + if(simple_mode) { + lv_draw_sw_blend(draw_ctx, &blend_dsc); + } + +#if LV_DRAW_COMPLEX + /*If there other mask apply it*/ + else { + int32_t draw_area_w = lv_area_get_width(&blend_area); + + lv_coord_t y2 = blend_area.y2; + blend_area.y2 = blend_area.y1; + + lv_opa_t * mask_buf = lv_mem_buf_get(draw_area_w); + blend_dsc.mask_buf = mask_buf; + blend_dsc.mask_area = &blend_area; + + lv_coord_t dash_start = 0; + if(dashed) { + dash_start = (blend_area.y1) % (dsc->dash_gap + dsc->dash_width); + } + + lv_coord_t dash_cnt = dash_start; + + int32_t h; + for(h = blend_area.y1; h <= y2; h++) { + lv_memset_ff(mask_buf, draw_area_w); + blend_dsc.mask_res = lv_draw_mask_apply(mask_buf, blend_area.x1, h, draw_area_w); + + if(dashed) { + if(blend_dsc.mask_res != LV_DRAW_MASK_RES_TRANSP) { + if(dash_cnt > dsc->dash_width) { + blend_dsc.mask_res = LV_DRAW_MASK_RES_TRANSP; + } + + if(dash_cnt >= dsc->dash_gap + dsc->dash_width) { + dash_cnt = 0; + } + } + dash_cnt ++; + } + + lv_draw_sw_blend(draw_ctx, &blend_dsc); + + blend_area.y1++; + blend_area.y2++; + } + lv_mem_buf_release(mask_buf); + } +#endif /*LV_DRAW_COMPLEX*/ +} + +LV_ATTRIBUTE_FAST_MEM static void draw_line_skew(struct _lv_draw_ctx_t * draw_ctx, const lv_draw_line_dsc_t * dsc, + const lv_point_t * point1, const lv_point_t * point2) +{ +#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 blend_area; + blend_area.x1 = LV_MIN(p1.x, p2.x) - w; + blend_area.x2 = LV_MAX(p1.x, p2.x) + w; + blend_area.y1 = LV_MIN(p1.y, p2.y) - w; + blend_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(&blend_area, &blend_area, draw_ctx->clip_area); + 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); + } + + /*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(&blend_area); + + /*Draw the background line by line*/ + int32_t h; + uint32_t hor_res = (uint32_t)lv_disp_get_hor_res(_lv_refr_get_disp_refreshing()); + size_t mask_buf_size = LV_MIN(lv_area_get_size(&blend_area), hor_res); + lv_opa_t * mask_buf = lv_mem_buf_get(mask_buf_size); + + lv_coord_t y2 = blend_area.y2; + blend_area.y2 = blend_area.y1; + + uint32_t mask_p = 0; + lv_memset_ff(mask_buf, mask_buf_size); + + lv_draw_sw_blend_dsc_t blend_dsc; + lv_memset_00(&blend_dsc, sizeof(blend_dsc)); + blend_dsc.blend_area = &blend_area; + blend_dsc.color = dsc->color; + blend_dsc.opa = dsc->opa; + blend_dsc.mask_buf = mask_buf; + blend_dsc.mask_area = &blend_area; + + /*Fill the first row with 'color'*/ + for(h = blend_area.y1; h <= y2; h++) { + blend_dsc.mask_res = lv_draw_mask_apply(&mask_buf[mask_p], blend_area.x1, h, draw_area_w); + if(blend_dsc.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) { + blend_area.y2 ++; + } + else { + blend_dsc.mask_res = LV_DRAW_MASK_RES_CHANGED; + lv_draw_sw_blend(draw_ctx, &blend_dsc); + + blend_area.y1 = blend_area.y2 + 1; + blend_area.y2 = blend_area.y1; + mask_p = 0; + lv_memset_ff(mask_buf, mask_buf_size); + } + } + + /*Flush the last part*/ + if(blend_area.y1 != blend_area.y2) { + blend_area.y2--; + blend_dsc.mask_res = LV_DRAW_MASK_RES_CHANGED; + lv_draw_sw_blend(draw_ctx, &blend_dsc); + } + + 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(draw_ctx); + LV_UNUSED(dsc); + LV_LOG_WARN("Can't draw skewed line with LV_DRAW_COMPLEX == 0"); +#endif /*LV_DRAW_COMPLEX*/ +} + diff --git a/lib/lvgl/src/draw/sw/lv_draw_sw_polygon.c b/lib/lvgl/src/draw/sw/lv_draw_sw_polygon.c new file mode 100644 index 00000000..a05b4713 --- /dev/null +++ b/lib/lvgl/src/draw/sw/lv_draw_sw_polygon.c @@ -0,0 +1,207 @@ +/** + * @file lv_draw_sw_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" + +/********************* + * 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(lv_draw_ctx_t * draw_ctx, const lv_draw_rect_dsc_t * draw_dsc, const lv_point_t * points, + uint16_t point_cnt) +{ +#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 clip_area; + is_common = _lv_area_intersect(&clip_area, &poly_coords, draw_ctx->clip_area); + if(!is_common) { + lv_mem_buf_release(p); + return; + } + + const lv_area_t * clip_area_ori = draw_ctx->clip_area; + draw_ctx->clip_area = &clip_area; + + /*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(draw_ctx, draw_dsc, &poly_coords); + + lv_draw_mask_remove_custom(mp); + + lv_mem_buf_release(mp); + lv_mem_buf_release(p); + + draw_ctx->clip_area = clip_area_ori; +#else + LV_UNUSED(points); + LV_UNUSED(point_cnt); + LV_UNUSED(draw_ctx); + LV_UNUSED(draw_dsc); + LV_LOG_WARN("Can't draw polygon with LV_DRAW_COMPLEX == 0"); +#endif /*LV_DRAW_COMPLEX*/ +} + +/********************** + * STATIC FUNCTIONS + **********************/ diff --git a/lib/lvgl/src/draw/sw/lv_draw_sw_rect.c b/lib/lvgl/src/draw/sw/lv_draw_sw_rect.c new file mode 100644 index 00000000..706ec6b5 --- /dev/null +++ b/lib/lvgl/src/draw/sw/lv_draw_sw_rect.c @@ -0,0 +1,1436 @@ +/** + * @file lv_draw_rect.c + * + */ + +/********************* + * INCLUDES + *********************/ +#include "lv_draw_sw.h" +#include "../../misc/lv_math.h" +#include "../../misc/lv_txt_ap.h" +#include "../../core/lv_refr.h" +#include "../../misc/lv_assert.h" +#include "lv_draw_sw_dither.h" + +/********************* + * DEFINES + *********************/ +#define SHADOW_UPSCALE_SHIFT 6 +#define SHADOW_ENHANCE 1 +#define SPLIT_LIMIT 50 + + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ +static void draw_bg(lv_draw_ctx_t * draw_ctx, const lv_draw_rect_dsc_t * dsc, const lv_area_t * coords); +static void draw_bg_img(lv_draw_ctx_t * draw_ctx, const lv_draw_rect_dsc_t * dsc, const lv_area_t * coords); +static void draw_border(lv_draw_ctx_t * draw_ctx, const lv_draw_rect_dsc_t * dsc, const lv_area_t * coords); + +static void draw_outline(lv_draw_ctx_t * draw_ctx, const lv_draw_rect_dsc_t * dsc, const lv_area_t * coords); + +#if LV_DRAW_COMPLEX +LV_ATTRIBUTE_FAST_MEM static void draw_shadow(lv_draw_ctx_t * draw_ctx, const lv_draw_rect_dsc_t * dsc, + const lv_area_t * coords); +LV_ATTRIBUTE_FAST_MEM static void shadow_draw_corner_buf(const lv_area_t * coords, uint16_t * sh_buf, lv_coord_t s, + lv_coord_t r); +LV_ATTRIBUTE_FAST_MEM static void shadow_blur_corner(lv_coord_t size, lv_coord_t sw, uint16_t * sh_ups_buf); +#endif + +void draw_border_generic(lv_draw_ctx_t * draw_ctx, const lv_area_t * outer_area, const lv_area_t * inner_area, + lv_coord_t rout, lv_coord_t rin, lv_color_t color, lv_opa_t opa, lv_blend_mode_t blend_mode); + +static void draw_border_simple(lv_draw_ctx_t * draw_ctx, const lv_area_t * outer_area, const lv_area_t * inner_area, + lv_color_t color, lv_opa_t opa); + + +/********************** + * STATIC VARIABLES + **********************/ +#if defined(LV_SHADOW_CACHE_SIZE) && LV_SHADOW_CACHE_SIZE > 0 + static uint8_t sh_cache[LV_SHADOW_CACHE_SIZE * LV_SHADOW_CACHE_SIZE]; + static int32_t sh_cache_size = -1; + static int32_t sh_cache_r = -1; +#endif + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +void lv_draw_sw_rect(lv_draw_ctx_t * draw_ctx, const lv_draw_rect_dsc_t * dsc, const lv_area_t * coords) +{ +#if LV_DRAW_COMPLEX + draw_shadow(draw_ctx, dsc, coords); +#endif + + draw_bg(draw_ctx, dsc, coords); + draw_bg_img(draw_ctx, dsc, coords); + + draw_border(draw_ctx, dsc, coords); + + draw_outline(draw_ctx, dsc, coords); + + LV_ASSERT_MEM_INTEGRITY(); +} + +void lv_draw_sw_bg(lv_draw_ctx_t * draw_ctx, const lv_draw_rect_dsc_t * dsc, const lv_area_t * coords) +{ +#if LV_COLOR_SCREEN_TRANSP && LV_COLOR_DEPTH == 32 + lv_memset_00(draw_ctx->buf, lv_area_get_size(draw_ctx->buf_area) * sizeof(lv_color_t)); +#endif + + draw_bg(draw_ctx, dsc, coords); + draw_bg_img(draw_ctx, dsc, coords); +} + + +/********************** + * STATIC FUNCTIONS + **********************/ + +static void draw_bg(lv_draw_ctx_t * draw_ctx, const lv_draw_rect_dsc_t * dsc, const lv_area_t * coords) +{ + if(dsc->bg_opa <= LV_OPA_MIN) return; + + lv_area_t bg_coords; + lv_area_copy(&bg_coords, coords); + + /*If the border fully covers make the bg area 1px smaller to avoid artifacts on the corners*/ + if(dsc->border_width > 1 && dsc->border_opa >= LV_OPA_MAX && dsc->radius != 0) { + bg_coords.x1 += (dsc->border_side & LV_BORDER_SIDE_LEFT) ? 1 : 0; + bg_coords.y1 += (dsc->border_side & LV_BORDER_SIDE_TOP) ? 1 : 0; + bg_coords.x2 -= (dsc->border_side & LV_BORDER_SIDE_RIGHT) ? 1 : 0; + bg_coords.y2 -= (dsc->border_side & LV_BORDER_SIDE_BOTTOM) ? 1 : 0; + } + + lv_area_t clipped_coords; + if(!_lv_area_intersect(&clipped_coords, &bg_coords, draw_ctx->clip_area)) return; + + lv_grad_dir_t grad_dir = dsc->bg_grad.dir; + lv_color_t bg_color = grad_dir == LV_GRAD_DIR_NONE ? dsc->bg_color : dsc->bg_grad.stops[0].color; + if(bg_color.full == dsc->bg_grad.stops[1].color.full) grad_dir = LV_GRAD_DIR_NONE; + + bool mask_any = lv_draw_mask_is_any(&bg_coords); + lv_draw_sw_blend_dsc_t blend_dsc = {0}; + blend_dsc.blend_mode = dsc->blend_mode; + blend_dsc.color = bg_color; + + /*Most simple case: just a plain rectangle*/ + if(!mask_any && dsc->radius == 0 && (grad_dir == LV_GRAD_DIR_NONE)) { + blend_dsc.blend_area = &bg_coords; + blend_dsc.opa = dsc->bg_opa; + lv_draw_sw_blend(draw_ctx, &blend_dsc); + return; + } + + /*Complex case: there is gradient, mask, or radius*/ +#if LV_DRAW_COMPLEX == 0 + LV_LOG_WARN("Can't draw complex rectangle because LV_DRAW_COMPLEX = 0"); +#else + lv_opa_t opa = dsc->bg_opa >= LV_OPA_MAX ? LV_OPA_COVER : dsc->bg_opa; + + /*Get the real radius. Can't be larger than the half of the shortest side */ + lv_coord_t coords_bg_w = lv_area_get_width(&bg_coords); + lv_coord_t coords_bg_h = lv_area_get_height(&bg_coords); + int32_t short_side = LV_MIN(coords_bg_w, coords_bg_h); + int32_t rout = LV_MIN(dsc->radius, short_side >> 1); + + /*Add a radius mask if there is radius*/ + int32_t clipped_w = lv_area_get_width(&clipped_coords); + int16_t mask_rout_id = LV_MASK_ID_INV; + lv_opa_t * mask_buf = NULL; + lv_draw_mask_radius_param_t mask_rout_param; + if(rout > 0 || mask_any) { + mask_buf = lv_mem_buf_get(clipped_w); + lv_draw_mask_radius_init(&mask_rout_param, &bg_coords, rout, false); + mask_rout_id = lv_draw_mask_add(&mask_rout_param, NULL); + } + + int32_t h; + + lv_area_t blend_area; + blend_area.x1 = clipped_coords.x1; + blend_area.x2 = clipped_coords.x2; + + blend_dsc.mask_buf = mask_buf; + blend_dsc.blend_area = &blend_area; + blend_dsc.mask_area = &blend_area; + blend_dsc.opa = LV_OPA_COVER; + + + /*Get gradient if appropriate*/ + lv_grad_t * grad = lv_gradient_get(&dsc->bg_grad, coords_bg_w, coords_bg_h); + if(grad && grad_dir == LV_GRAD_DIR_HOR) { + blend_dsc.src_buf = grad->map + clipped_coords.x1 - bg_coords.x1; + } + +#if _DITHER_GRADIENT + lv_dither_mode_t dither_mode = dsc->bg_grad.dither; + lv_dither_func_t dither_func = &lv_dither_none; + lv_coord_t grad_size = coords_bg_w; + if(grad_dir == LV_GRAD_DIR_VER && dither_mode != LV_DITHER_NONE) { + /* When dithering, we are still using a map that's changing from line to line*/ + blend_dsc.src_buf = grad->map; + } + + if(grad && dither_mode == LV_DITHER_NONE) { + grad->filled = 0; /*Should we force refilling it each draw call ?*/ + if(grad_dir == LV_GRAD_DIR_VER) + grad_size = coords_bg_h; + } + else +#if LV_DITHER_ERROR_DIFFUSION + if(dither_mode == LV_DITHER_ORDERED) +#endif + switch(grad_dir) { + case LV_GRAD_DIR_HOR: + dither_func = lv_dither_ordered_hor; + break; + case LV_GRAD_DIR_VER: + dither_func = lv_dither_ordered_ver; + break; + default: + dither_func = NULL; + } + +#if LV_DITHER_ERROR_DIFFUSION + else if(dither_mode == LV_DITHER_ERR_DIFF) + switch(grad_dir) { + case LV_GRAD_DIR_HOR: + dither_func = lv_dither_err_diff_hor; + break; + case LV_GRAD_DIR_VER: + dither_func = lv_dither_err_diff_ver; + break; + default: + dither_func = NULL; + } +#endif +#endif + + /*There is another mask too. Draw line by line. */ + if(mask_any) { + for(h = clipped_coords.y1; h <= clipped_coords.y2; h++) { + blend_area.y1 = h; + blend_area.y2 = h; + + /* Initialize the mask to opa instead of 0xFF and blend with LV_OPA_COVER. + * It saves calculating the final opa in lv_draw_sw_blend*/ + lv_memset(mask_buf, opa, clipped_w); + blend_dsc.mask_res = lv_draw_mask_apply(mask_buf, clipped_coords.x1, h, clipped_w); + if(blend_dsc.mask_res == LV_DRAW_MASK_RES_FULL_COVER) blend_dsc.mask_res = LV_DRAW_MASK_RES_CHANGED; + +#if _DITHER_GRADIENT + if(dither_func) dither_func(grad, blend_area.x1, h - bg_coords.y1, grad_size); +#endif + if(grad_dir == LV_GRAD_DIR_VER) blend_dsc.color = grad->map[h - bg_coords.y1]; + lv_draw_sw_blend(draw_ctx, &blend_dsc); + } + goto bg_clean_up; + } + + + /* Draw the top of the rectangle line by line and mirror it to the bottom. */ + for(h = 0; h < rout; h++) { + lv_coord_t top_y = bg_coords.y1 + h; + lv_coord_t bottom_y = bg_coords.y2 - h; + if(top_y < clipped_coords.y1 && bottom_y > clipped_coords.y2) continue; /*This line is clipped now*/ + + /* Initialize the mask to opa instead of 0xFF and blend with LV_OPA_COVER. + * It saves calculating the final opa in lv_draw_sw_blend*/ + lv_memset(mask_buf, opa, clipped_w); + blend_dsc.mask_res = lv_draw_mask_apply(mask_buf, blend_area.x1, top_y, clipped_w); + if(blend_dsc.mask_res == LV_DRAW_MASK_RES_FULL_COVER) blend_dsc.mask_res = LV_DRAW_MASK_RES_CHANGED; + + if(top_y >= clipped_coords.y1) { + blend_area.y1 = top_y; + blend_area.y2 = top_y; + +#if _DITHER_GRADIENT + if(dither_func) dither_func(grad, blend_area.x1, top_y - bg_coords.y1, grad_size); +#endif + if(grad_dir == LV_GRAD_DIR_VER) blend_dsc.color = grad->map[top_y - bg_coords.y1]; + lv_draw_sw_blend(draw_ctx, &blend_dsc); + } + + if(bottom_y <= clipped_coords.y2) { + blend_area.y1 = bottom_y; + blend_area.y2 = bottom_y; + +#if _DITHER_GRADIENT + if(dither_func) dither_func(grad, blend_area.x1, bottom_y - bg_coords.y1, grad_size); +#endif + if(grad_dir == LV_GRAD_DIR_VER) blend_dsc.color = grad->map[bottom_y - bg_coords.y1]; + lv_draw_sw_blend(draw_ctx, &blend_dsc); + } + } + + /* Draw the center of the rectangle.*/ + + /*If no other masks and no gradient, the center is a simple rectangle*/ + lv_area_t center_coords; + center_coords.x1 = bg_coords.x1; + center_coords.x2 = bg_coords.x2; + center_coords.y1 = bg_coords.y1 + rout; + center_coords.y2 = bg_coords.y2 - rout; + bool mask_any_center = lv_draw_mask_is_any(¢er_coords); + if(!mask_any_center && grad_dir == LV_GRAD_DIR_NONE) { + blend_area.y1 = bg_coords.y1 + rout; + blend_area.y2 = bg_coords.y2 - rout; + blend_dsc.opa = opa; + blend_dsc.mask_buf = NULL; + lv_draw_sw_blend(draw_ctx, &blend_dsc); + } + /*With gradient and/or mask draw line by line*/ + else { + blend_dsc.opa = opa; + blend_dsc.mask_res = LV_DRAW_MASK_RES_FULL_COVER; + int32_t h_end = bg_coords.y2 - rout; + for(h = bg_coords.y1 + rout; h <= h_end; h++) { + /*If there is no other mask do not apply mask as in the center there is no radius to mask*/ + if(mask_any_center) { + lv_memset(mask_buf, opa, clipped_w); + blend_dsc.mask_res = lv_draw_mask_apply(mask_buf, clipped_coords.x1, h, clipped_w); + } + + blend_area.y1 = h; + blend_area.y2 = h; + +#if _DITHER_GRADIENT + if(dither_func) dither_func(grad, blend_area.x1, h - bg_coords.y1, grad_size); +#endif + if(grad_dir == LV_GRAD_DIR_VER) blend_dsc.color = grad->map[h - bg_coords.y1]; + lv_draw_sw_blend(draw_ctx, &blend_dsc); + } + } + + +bg_clean_up: + if(mask_buf) lv_mem_buf_release(mask_buf); + if(mask_rout_id != LV_MASK_ID_INV) { + lv_draw_mask_remove_id(mask_rout_id); + lv_draw_mask_free_param(&mask_rout_param); + } + if(grad) { + lv_gradient_cleanup(grad); + } + +#endif +} + +static void draw_bg_img(lv_draw_ctx_t * draw_ctx, const lv_draw_rect_dsc_t * dsc, const lv_area_t * coords) +{ + if(dsc->bg_img_src == NULL) return; + if(dsc->bg_img_opa <= LV_OPA_MIN) return; + + lv_area_t clip_area; + if(!_lv_area_intersect(&clip_area, coords, draw_ctx->clip_area)) { + return; + } + + const lv_area_t * clip_area_ori = draw_ctx->clip_area; + draw_ctx->clip_area = &clip_area; + + lv_img_src_t src_type = lv_img_src_get_type(dsc->bg_img_src); + if(src_type == LV_IMG_SRC_SYMBOL) { + lv_point_t size; + lv_txt_get_size(&size, dsc->bg_img_src, dsc->bg_img_symbol_font, 0, 0, LV_COORD_MAX, LV_TEXT_FLAG_NONE); + lv_area_t a; + a.x1 = coords->x1 + lv_area_get_width(coords) / 2 - size.x / 2; + a.x2 = a.x1 + size.x - 1; + a.y1 = coords->y1 + lv_area_get_height(coords) / 2 - size.y / 2; + a.y2 = a.y1 + size.y - 1; + + lv_draw_label_dsc_t label_draw_dsc; + lv_draw_label_dsc_init(&label_draw_dsc); + label_draw_dsc.font = dsc->bg_img_symbol_font; + label_draw_dsc.color = dsc->bg_img_recolor; + label_draw_dsc.opa = dsc->bg_img_opa; + lv_draw_label(draw_ctx, &label_draw_dsc, &a, dsc->bg_img_src, NULL); + } + else { + lv_img_header_t header; + lv_res_t res = lv_img_decoder_get_info(dsc->bg_img_src, &header); + if(res == LV_RES_OK) { + lv_draw_img_dsc_t img_dsc; + lv_draw_img_dsc_init(&img_dsc); + img_dsc.blend_mode = dsc->blend_mode; + img_dsc.recolor = dsc->bg_img_recolor; + img_dsc.recolor_opa = dsc->bg_img_recolor_opa; + img_dsc.opa = dsc->bg_img_opa; + + /*Center align*/ + if(dsc->bg_img_tiled == false) { + lv_area_t area; + area.x1 = coords->x1 + lv_area_get_width(coords) / 2 - header.w / 2; + area.y1 = coords->y1 + lv_area_get_height(coords) / 2 - header.h / 2; + area.x2 = area.x1 + header.w - 1; + area.y2 = area.y1 + header.h - 1; + + lv_draw_img(draw_ctx, &img_dsc, &area, dsc->bg_img_src); + } + else { + lv_area_t area; + area.y1 = coords->y1; + area.y2 = area.y1 + header.h - 1; + + for(; area.y1 <= coords->y2; area.y1 += header.h, area.y2 += header.h) { + + area.x1 = coords->x1; + area.x2 = area.x1 + header.w - 1; + for(; area.x1 <= coords->x2; area.x1 += header.w, area.x2 += header.w) { + lv_draw_img(draw_ctx, &img_dsc, &area, dsc->bg_img_src); + } + } + } + } + else { + LV_LOG_WARN("Couldn't read the background image"); + } + } + + draw_ctx->clip_area = clip_area_ori; +} + +static void draw_border(lv_draw_ctx_t * draw_ctx, const lv_draw_rect_dsc_t * dsc, const lv_area_t * coords) +{ + if(dsc->border_opa <= LV_OPA_MIN) return; + if(dsc->border_width == 0) return; + if(dsc->border_side == LV_BORDER_SIDE_NONE) return; + if(dsc->border_post) return; + + int32_t coords_w = lv_area_get_width(coords); + int32_t coords_h = lv_area_get_height(coords); + int32_t rout = dsc->radius; + int32_t short_side = LV_MIN(coords_w, coords_h); + if(rout > short_side >> 1) rout = short_side >> 1; + + /*Get the inner area*/ + lv_area_t area_inner; + lv_area_copy(&area_inner, coords); + area_inner.x1 += ((dsc->border_side & LV_BORDER_SIDE_LEFT) ? dsc->border_width : - (dsc->border_width + rout)); + area_inner.x2 -= ((dsc->border_side & LV_BORDER_SIDE_RIGHT) ? dsc->border_width : - (dsc->border_width + rout)); + area_inner.y1 += ((dsc->border_side & LV_BORDER_SIDE_TOP) ? dsc->border_width : - (dsc->border_width + rout)); + area_inner.y2 -= ((dsc->border_side & LV_BORDER_SIDE_BOTTOM) ? dsc->border_width : - (dsc->border_width + rout)); + + lv_coord_t rin = rout - dsc->border_width; + if(rin < 0) rin = 0; + + draw_border_generic(draw_ctx, coords, &area_inner, rout, rin, dsc->border_color, dsc->border_opa, dsc->blend_mode); + +} + +#if LV_DRAW_COMPLEX +LV_ATTRIBUTE_FAST_MEM static void draw_shadow(lv_draw_ctx_t * draw_ctx, const lv_draw_rect_dsc_t * dsc, + const lv_area_t * coords) +{ + /*Check whether the shadow is visible*/ + if(dsc->shadow_width == 0) return; + if(dsc->shadow_opa <= LV_OPA_MIN) return; + + if(dsc->shadow_width == 1 && dsc->shadow_spread <= 0 && + dsc->shadow_ofs_x == 0 && dsc->shadow_ofs_y == 0) { + return; + } + + /*Calculate the rectangle which is blurred to get the shadow in `shadow_area`*/ + lv_area_t core_area; + core_area.x1 = coords->x1 + dsc->shadow_ofs_x - dsc->shadow_spread; + core_area.x2 = coords->x2 + dsc->shadow_ofs_x + dsc->shadow_spread; + core_area.y1 = coords->y1 + dsc->shadow_ofs_y - dsc->shadow_spread; + core_area.y2 = coords->y2 + dsc->shadow_ofs_y + dsc->shadow_spread; + + /*Calculate the bounding box of the shadow*/ + lv_area_t shadow_area; + shadow_area.x1 = core_area.x1 - dsc->shadow_width / 2 - 1; + shadow_area.x2 = core_area.x2 + dsc->shadow_width / 2 + 1; + shadow_area.y1 = core_area.y1 - dsc->shadow_width / 2 - 1; + shadow_area.y2 = core_area.y2 + dsc->shadow_width / 2 + 1; + + lv_opa_t opa = dsc->shadow_opa; + if(opa > LV_OPA_MAX) opa = LV_OPA_COVER; + + /*Get clipped draw area which is the real draw area. + *It is always the same or inside `shadow_area`*/ + lv_area_t draw_area; + if(!_lv_area_intersect(&draw_area, &shadow_area, draw_ctx->clip_area)) return; + + /*Consider 1 px smaller bg to be sure the edge will be covered by the shadow*/ + lv_area_t bg_area; + lv_area_copy(&bg_area, coords); + lv_area_increase(&bg_area, -1, -1); + + /*Get the clamped radius*/ + int32_t r_bg = dsc->radius; + lv_coord_t short_side = LV_MIN(lv_area_get_width(&bg_area), lv_area_get_height(&bg_area)); + if(r_bg > short_side >> 1) r_bg = short_side >> 1; + + /*Get the clamped radius*/ + int32_t r_sh = dsc->radius; + short_side = LV_MIN(lv_area_get_width(&core_area), lv_area_get_height(&core_area)); + if(r_sh > short_side >> 1) r_sh = short_side >> 1; + + + /*Get how many pixels are affected by the blur on the corners*/ + int32_t corner_size = dsc->shadow_width + r_sh; + + lv_opa_t * sh_buf; + +#if LV_SHADOW_CACHE_SIZE + if(sh_cache_size == corner_size && sh_cache_r == r_sh) { + /*Use the cache if available*/ + sh_buf = lv_mem_buf_get(corner_size * corner_size); + lv_memcpy(sh_buf, sh_cache, corner_size * corner_size); + } + else { + /*A larger buffer is required for calculation*/ + sh_buf = lv_mem_buf_get(corner_size * corner_size * sizeof(uint16_t)); + shadow_draw_corner_buf(&core_area, (uint16_t *)sh_buf, dsc->shadow_width, r_sh); + + /*Cache the corner if it fits into the cache size*/ + if((uint32_t)corner_size * corner_size < sizeof(sh_cache)) { + lv_memcpy(sh_cache, sh_buf, corner_size * corner_size); + sh_cache_size = corner_size; + sh_cache_r = r_sh; + } + } +#else + sh_buf = lv_mem_buf_get(corner_size * corner_size * sizeof(uint16_t)); + shadow_draw_corner_buf(&core_area, (uint16_t *)sh_buf, dsc->shadow_width, r_sh); +#endif + + /*Skip a lot of masking if the background will cover the shadow that would be masked out*/ + bool mask_any = lv_draw_mask_is_any(&shadow_area); + bool simple = true; + if(mask_any || dsc->bg_opa < LV_OPA_COVER || dsc->blend_mode != LV_BLEND_MODE_NORMAL) simple = false; + + /*Create a radius mask to clip remove shadow on the bg area*/ + + lv_draw_mask_radius_param_t mask_rout_param; + int16_t mask_rout_id = LV_MASK_ID_INV; + if(!simple) { + lv_draw_mask_radius_init(&mask_rout_param, &bg_area, r_bg, true); + mask_rout_id = lv_draw_mask_add(&mask_rout_param, NULL); + } + lv_opa_t * mask_buf = lv_mem_buf_get(lv_area_get_width(&shadow_area)); + lv_area_t blend_area; + lv_area_t clip_area_sub; + lv_opa_t * sh_buf_tmp; + lv_coord_t y; + bool simple_sub; + + lv_draw_sw_blend_dsc_t blend_dsc; + lv_memset_00(&blend_dsc, sizeof(blend_dsc)); + blend_dsc.blend_area = &blend_area; + blend_dsc.mask_area = &blend_area; + blend_dsc.mask_buf = mask_buf; + blend_dsc.color = dsc->shadow_color; + blend_dsc.opa = dsc->shadow_opa; + blend_dsc.blend_mode = dsc->blend_mode; + + lv_coord_t w_half = shadow_area.x1 + lv_area_get_width(&shadow_area) / 2; + lv_coord_t h_half = shadow_area.y1 + lv_area_get_height(&shadow_area) / 2; + + /*Draw the corners if they are on the current clip area and not fully covered by the bg*/ + + /*Top right corner*/ + blend_area.x2 = shadow_area.x2; + blend_area.x1 = shadow_area.x2 - corner_size + 1; + blend_area.y1 = shadow_area.y1; + blend_area.y2 = shadow_area.y1 + corner_size - 1; + /*Do not overdraw the other top corners*/ + blend_area.x1 = LV_MAX(blend_area.x1, w_half); + blend_area.y2 = LV_MIN(blend_area.y2, h_half); + + if(_lv_area_intersect(&clip_area_sub, &blend_area, draw_ctx->clip_area) && + !_lv_area_is_in(&clip_area_sub, &bg_area, r_bg)) { + lv_coord_t w = lv_area_get_width(&clip_area_sub); + sh_buf_tmp = sh_buf; + sh_buf_tmp += (clip_area_sub.y1 - shadow_area.y1) * corner_size; + sh_buf_tmp += clip_area_sub.x1 - (shadow_area.x2 - corner_size + 1); + + /*Do not mask if out of the bg*/ + if(simple && _lv_area_is_out(&clip_area_sub, &bg_area, r_bg)) simple_sub = true; + else simple_sub = simple; + if(w > 0) { + blend_dsc.mask_buf = mask_buf; + blend_area.x1 = clip_area_sub.x1; + blend_area.x2 = clip_area_sub.x2; + blend_dsc.mask_res = LV_DRAW_MASK_RES_CHANGED; /*In simple mode it won't be overwritten*/ + for(y = clip_area_sub.y1; y <= clip_area_sub.y2; y++) { + blend_area.y1 = y; + blend_area.y2 = y; + + if(!simple_sub) { + lv_memcpy(mask_buf, sh_buf_tmp, corner_size); + blend_dsc.mask_res = lv_draw_mask_apply(mask_buf, clip_area_sub.x1, y, w); + if(blend_dsc.mask_res == LV_DRAW_MASK_RES_FULL_COVER) blend_dsc.mask_res = LV_DRAW_MASK_RES_CHANGED; + } + else { + blend_dsc.mask_buf = sh_buf_tmp; + } + lv_draw_sw_blend(draw_ctx, &blend_dsc); + sh_buf_tmp += corner_size; + } + } + } + + /*Bottom right corner. + *Almost the same as top right just read the lines of `sh_buf` from then end*/ + blend_area.x2 = shadow_area.x2; + blend_area.x1 = shadow_area.x2 - corner_size + 1; + blend_area.y1 = shadow_area.y2 - corner_size + 1; + blend_area.y2 = shadow_area.y2; + /*Do not overdraw the other corners*/ + blend_area.x1 = LV_MAX(blend_area.x1, w_half); + blend_area.y1 = LV_MAX(blend_area.y1, h_half + 1); + + if(_lv_area_intersect(&clip_area_sub, &blend_area, draw_ctx->clip_area) && + !_lv_area_is_in(&clip_area_sub, &bg_area, r_bg)) { + lv_coord_t w = lv_area_get_width(&clip_area_sub); + sh_buf_tmp = sh_buf; + sh_buf_tmp += (blend_area.y2 - clip_area_sub.y2) * corner_size; + sh_buf_tmp += clip_area_sub.x1 - (shadow_area.x2 - corner_size + 1); + /*Do not mask if out of the bg*/ + if(simple && _lv_area_is_out(&clip_area_sub, &bg_area, r_bg)) simple_sub = true; + else simple_sub = simple; + + if(w > 0) { + blend_dsc.mask_buf = mask_buf; + blend_area.x1 = clip_area_sub.x1; + blend_area.x2 = clip_area_sub.x2; + blend_dsc.mask_res = LV_DRAW_MASK_RES_CHANGED; /*In simple mode it won't be overwritten*/ + for(y = clip_area_sub.y2; y >= clip_area_sub.y1; y--) { + blend_area.y1 = y; + blend_area.y2 = y; + + if(!simple_sub) { + lv_memcpy(mask_buf, sh_buf_tmp, corner_size); + blend_dsc.mask_res = lv_draw_mask_apply(mask_buf, clip_area_sub.x1, y, w); + if(blend_dsc.mask_res == LV_DRAW_MASK_RES_FULL_COVER) blend_dsc.mask_res = LV_DRAW_MASK_RES_CHANGED; + } + else { + blend_dsc.mask_buf = sh_buf_tmp; + } + lv_draw_sw_blend(draw_ctx, &blend_dsc); + sh_buf_tmp += corner_size; + } + } + } + + /*Top side*/ + blend_area.x1 = shadow_area.x1 + corner_size; + blend_area.x2 = shadow_area.x2 - corner_size; + blend_area.y1 = shadow_area.y1; + blend_area.y2 = shadow_area.y1 + corner_size - 1; + blend_area.y2 = LV_MIN(blend_area.y2, h_half); + + if(_lv_area_intersect(&clip_area_sub, &blend_area, draw_ctx->clip_area) && + !_lv_area_is_in(&clip_area_sub, &bg_area, r_bg)) { + lv_coord_t w = lv_area_get_width(&clip_area_sub); + sh_buf_tmp = sh_buf; + sh_buf_tmp += (clip_area_sub.y1 - blend_area.y1) * corner_size; + + /*Do not mask if out of the bg*/ + if(simple && _lv_area_is_out(&clip_area_sub, &bg_area, r_bg)) simple_sub = true; + else simple_sub = simple; + + if(w > 0) { + if(!simple_sub) { + blend_dsc.mask_buf = mask_buf; + } + else { + blend_dsc.mask_buf = NULL; + } + blend_area.x1 = clip_area_sub.x1; + blend_area.x2 = clip_area_sub.x2; + + for(y = clip_area_sub.y1; y <= clip_area_sub.y2; y++) { + blend_area.y1 = y; + blend_area.y2 = y; + + if(!simple_sub) { + lv_memset(mask_buf, sh_buf_tmp[0], w); + blend_dsc.mask_res = lv_draw_mask_apply(mask_buf, clip_area_sub.x1, y, w); + if(blend_dsc.mask_res == LV_DRAW_MASK_RES_FULL_COVER) blend_dsc.mask_res = LV_DRAW_MASK_RES_CHANGED; + lv_draw_sw_blend(draw_ctx, &blend_dsc); + } + else { + blend_dsc.opa = opa == LV_OPA_COVER ? sh_buf_tmp[0] : (sh_buf_tmp[0] * dsc->shadow_opa) >> 8; + lv_draw_sw_blend(draw_ctx, &blend_dsc); + } + sh_buf_tmp += corner_size; + } + } + } + blend_dsc.opa = dsc->shadow_opa; /*Restore*/ + + /*Bottom side*/ + blend_area.x1 = shadow_area.x1 + corner_size; + blend_area.x2 = shadow_area.x2 - corner_size; + blend_area.y1 = shadow_area.y2 - corner_size + 1; + blend_area.y2 = shadow_area.y2; + blend_area.y1 = LV_MAX(blend_area.y1, h_half + 1); + + + if(_lv_area_intersect(&clip_area_sub, &blend_area, draw_ctx->clip_area) && + !_lv_area_is_in(&clip_area_sub, &bg_area, r_bg)) { + lv_coord_t w = lv_area_get_width(&clip_area_sub); + sh_buf_tmp = sh_buf; + sh_buf_tmp += (blend_area.y2 - clip_area_sub.y2) * corner_size; + if(w > 0) { + /*Do not mask if out of the bg*/ + if(simple && _lv_area_is_out(&clip_area_sub, &bg_area, r_bg)) simple_sub = true; + else simple_sub = simple; + + if(!simple_sub) { + blend_dsc.mask_buf = mask_buf; + } + else { + blend_dsc.mask_buf = NULL; + } + blend_area.x1 = clip_area_sub.x1; + blend_area.x2 = clip_area_sub.x2; + + for(y = clip_area_sub.y2; y >= clip_area_sub.y1; y--) { + blend_area.y1 = y; + blend_area.y2 = y; + + /*Do not mask if out of the bg*/ + if(simple && _lv_area_is_out(&clip_area_sub, &bg_area, r_bg)) simple_sub = true; + else simple_sub = simple; + + if(!simple_sub) { + lv_memset(mask_buf, sh_buf_tmp[0], w); + blend_dsc.mask_res = lv_draw_mask_apply(mask_buf, clip_area_sub.x1, y, w); + if(blend_dsc.mask_res == LV_DRAW_MASK_RES_FULL_COVER) blend_dsc.mask_res = LV_DRAW_MASK_RES_CHANGED; + lv_draw_sw_blend(draw_ctx, &blend_dsc); + } + else { + blend_dsc.opa = opa == LV_OPA_COVER ? sh_buf_tmp[0] : (sh_buf_tmp[0] * dsc->shadow_opa) >> 8; + lv_draw_sw_blend(draw_ctx, &blend_dsc); + + } + sh_buf_tmp += corner_size; + } + } + } + + blend_dsc.opa = dsc->shadow_opa; /*Restore*/ + + /*Right side*/ + blend_area.x1 = shadow_area.x2 - corner_size + 1; + blend_area.x2 = shadow_area.x2; + blend_area.y1 = shadow_area.y1 + corner_size; + blend_area.y2 = shadow_area.y2 - corner_size; + /*Do not overdraw the other corners*/ + blend_area.y1 = LV_MIN(blend_area.y1, h_half + 1); + blend_area.y2 = LV_MAX(blend_area.y2, h_half); + blend_area.x1 = LV_MAX(blend_area.x1, w_half); + + if(_lv_area_intersect(&clip_area_sub, &blend_area, draw_ctx->clip_area) && + !_lv_area_is_in(&clip_area_sub, &bg_area, r_bg)) { + lv_coord_t w = lv_area_get_width(&clip_area_sub); + sh_buf_tmp = sh_buf; + sh_buf_tmp += (corner_size - 1) * corner_size; + sh_buf_tmp += clip_area_sub.x1 - (shadow_area.x2 - corner_size + 1); + + /*Do not mask if out of the bg*/ + if(simple && _lv_area_is_out(&clip_area_sub, &bg_area, r_bg)) simple_sub = true; + else simple_sub = simple; + blend_dsc.mask_buf = simple_sub ? sh_buf_tmp : mask_buf; + + if(w > 0) { + blend_area.x1 = clip_area_sub.x1; + blend_area.x2 = clip_area_sub.x2; + blend_dsc.mask_res = LV_DRAW_MASK_RES_CHANGED; /*In simple mode it won't be overwritten*/ + for(y = clip_area_sub.y1; y <= clip_area_sub.y2; y++) { + blend_area.y1 = y; + blend_area.y2 = y; + + if(!simple_sub) { + lv_memcpy(mask_buf, sh_buf_tmp, w); + blend_dsc.mask_res = lv_draw_mask_apply(mask_buf, clip_area_sub.x1, y, w); + if(blend_dsc.mask_res == LV_DRAW_MASK_RES_FULL_COVER) blend_dsc.mask_res = LV_DRAW_MASK_RES_CHANGED; + } + lv_draw_sw_blend(draw_ctx, &blend_dsc); + } + } + } + + /*Mirror the shadow corner buffer horizontally*/ + sh_buf_tmp = sh_buf ; + for(y = 0; y < corner_size; y++) { + int32_t x; + lv_opa_t * start = sh_buf_tmp; + lv_opa_t * end = sh_buf_tmp + corner_size - 1; + for(x = 0; x < corner_size / 2; x++) { + lv_opa_t tmp = *start; + *start = *end; + *end = tmp; + + start++; + end--; + } + sh_buf_tmp += corner_size; + } + + /*Left side*/ + blend_area.x1 = shadow_area.x1; + blend_area.x2 = shadow_area.x1 + corner_size - 1; + blend_area.y1 = shadow_area.y1 + corner_size; + blend_area.y2 = shadow_area.y2 - corner_size; + /*Do not overdraw the other corners*/ + blend_area.y1 = LV_MIN(blend_area.y1, h_half + 1); + blend_area.y2 = LV_MAX(blend_area.y2, h_half); + blend_area.x2 = LV_MIN(blend_area.x2, w_half - 1); + + if(_lv_area_intersect(&clip_area_sub, &blend_area, draw_ctx->clip_area) && + !_lv_area_is_in(&clip_area_sub, &bg_area, r_bg)) { + lv_coord_t w = lv_area_get_width(&clip_area_sub); + sh_buf_tmp = sh_buf; + sh_buf_tmp += (corner_size - 1) * corner_size; + sh_buf_tmp += clip_area_sub.x1 - blend_area.x1; + + /*Do not mask if out of the bg*/ + if(simple && _lv_area_is_out(&clip_area_sub, &bg_area, r_bg)) simple_sub = true; + else simple_sub = simple; + blend_dsc.mask_buf = simple_sub ? sh_buf_tmp : mask_buf; + if(w > 0) { + blend_area.x1 = clip_area_sub.x1; + blend_area.x2 = clip_area_sub.x2; + blend_dsc.mask_res = LV_DRAW_MASK_RES_CHANGED; /*In simple mode it won't be overwritten*/ + for(y = clip_area_sub.y1; y <= clip_area_sub.y2; y++) { + blend_area.y1 = y; + blend_area.y2 = y; + + if(!simple_sub) { + lv_memcpy(mask_buf, sh_buf_tmp, w); + blend_dsc.mask_res = lv_draw_mask_apply(mask_buf, clip_area_sub.x1, y, w); + if(blend_dsc.mask_res == LV_DRAW_MASK_RES_FULL_COVER) blend_dsc.mask_res = LV_DRAW_MASK_RES_CHANGED; + } + + lv_draw_sw_blend(draw_ctx, &blend_dsc); + } + } + } + + /*Top left corner*/ + blend_area.x1 = shadow_area.x1; + blend_area.x2 = shadow_area.x1 + corner_size - 1; + blend_area.y1 = shadow_area.y1; + blend_area.y2 = shadow_area.y1 + corner_size - 1; + /*Do not overdraw the other corners*/ + blend_area.x2 = LV_MIN(blend_area.x2, w_half - 1); + blend_area.y2 = LV_MIN(blend_area.y2, h_half); + + if(_lv_area_intersect(&clip_area_sub, &blend_area, draw_ctx->clip_area) && + !_lv_area_is_in(&clip_area_sub, &bg_area, r_bg)) { + lv_coord_t w = lv_area_get_width(&clip_area_sub); + sh_buf_tmp = sh_buf; + sh_buf_tmp += (clip_area_sub.y1 - blend_area.y1) * corner_size; + sh_buf_tmp += clip_area_sub.x1 - blend_area.x1; + + /*Do not mask if out of the bg*/ + if(simple && _lv_area_is_out(&clip_area_sub, &bg_area, r_bg)) simple_sub = true; + else simple_sub = simple; + blend_dsc.mask_buf = mask_buf; + + if(w > 0) { + blend_area.x1 = clip_area_sub.x1; + blend_area.x2 = clip_area_sub.x2; + blend_dsc.mask_res = LV_DRAW_MASK_RES_CHANGED; /*In simple mode it won't be overwritten*/ + for(y = clip_area_sub.y1; y <= clip_area_sub.y2; y++) { + blend_area.y1 = y; + blend_area.y2 = y; + + if(!simple_sub) { + lv_memcpy(mask_buf, sh_buf_tmp, corner_size); + blend_dsc.mask_res = lv_draw_mask_apply(mask_buf, clip_area_sub.x1, y, w); + if(blend_dsc.mask_res == LV_DRAW_MASK_RES_FULL_COVER) blend_dsc.mask_res = LV_DRAW_MASK_RES_CHANGED; + } + else { + blend_dsc.mask_buf = sh_buf_tmp; + } + + lv_draw_sw_blend(draw_ctx, &blend_dsc); + sh_buf_tmp += corner_size; + } + } + } + + /*Bottom left corner. + *Almost the same as bottom right just read the lines of `sh_buf` from then end*/ + blend_area.x1 = shadow_area.x1 ; + blend_area.x2 = shadow_area.x1 + corner_size - 1; + blend_area.y1 = shadow_area.y2 - corner_size + 1; + blend_area.y2 = shadow_area.y2; + /*Do not overdraw the other corners*/ + blend_area.y1 = LV_MAX(blend_area.y1, h_half + 1); + blend_area.x2 = LV_MIN(blend_area.x2, w_half - 1); + + if(_lv_area_intersect(&clip_area_sub, &blend_area, draw_ctx->clip_area) && + !_lv_area_is_in(&clip_area_sub, &bg_area, r_bg)) { + lv_coord_t w = lv_area_get_width(&clip_area_sub); + sh_buf_tmp = sh_buf; + sh_buf_tmp += (blend_area.y2 - clip_area_sub.y2) * corner_size; + sh_buf_tmp += clip_area_sub.x1 - blend_area.x1; + + /*Do not mask if out of the bg*/ + if(simple && _lv_area_is_out(&clip_area_sub, &bg_area, r_bg)) simple_sub = true; + else simple_sub = simple; + blend_dsc.mask_buf = mask_buf; + if(w > 0) { + blend_area.x1 = clip_area_sub.x1; + blend_area.x2 = clip_area_sub.x2; + blend_dsc.mask_res = LV_DRAW_MASK_RES_CHANGED; /*In simple mode it won't be overwritten*/ + for(y = clip_area_sub.y2; y >= clip_area_sub.y1; y--) { + blend_area.y1 = y; + blend_area.y2 = y; + + if(!simple_sub) { + lv_memcpy(mask_buf, sh_buf_tmp, corner_size); + blend_dsc.mask_res = lv_draw_mask_apply(mask_buf, clip_area_sub.x1, y, w); + if(blend_dsc.mask_res == LV_DRAW_MASK_RES_FULL_COVER) blend_dsc.mask_res = LV_DRAW_MASK_RES_CHANGED; + } + else { + blend_dsc.mask_buf = sh_buf_tmp; + } + lv_draw_sw_blend(draw_ctx, &blend_dsc); + sh_buf_tmp += corner_size; + } + } + } + + /*Draw the center rectangle.*/ + blend_area.x1 = shadow_area.x1 + corner_size ; + blend_area.x2 = shadow_area.x2 - corner_size; + blend_area.y1 = shadow_area.y1 + corner_size; + blend_area.y2 = shadow_area.y2 - corner_size; + blend_dsc.mask_buf = mask_buf; + + if(_lv_area_intersect(&clip_area_sub, &blend_area, draw_ctx->clip_area) && + !_lv_area_is_in(&clip_area_sub, &bg_area, r_bg)) { + lv_coord_t w = lv_area_get_width(&clip_area_sub); + if(w > 0) { + blend_area.x1 = clip_area_sub.x1; + blend_area.x2 = clip_area_sub.x2; + for(y = clip_area_sub.y1; y <= clip_area_sub.y2; y++) { + blend_area.y1 = y; + blend_area.y2 = y; + + lv_memset_ff(mask_buf, w); + blend_dsc.mask_res = lv_draw_mask_apply(mask_buf, clip_area_sub.x1, y, w); + lv_draw_sw_blend(draw_ctx, &blend_dsc); + } + } + } + + if(!simple) { + lv_draw_mask_free_param(&mask_rout_param); + lv_draw_mask_remove_id(mask_rout_id); + } + lv_mem_buf_release(sh_buf); + lv_mem_buf_release(mask_buf); +} + +/** + * Calculate a blurred corner + * @param coords Coordinates of the shadow + * @param sh_buf a buffer to store the result. Its size should be `(sw + r)^2 * 2` + * @param sw shadow width + * @param r radius + */ +LV_ATTRIBUTE_FAST_MEM static void shadow_draw_corner_buf(const lv_area_t * coords, uint16_t * sh_buf, lv_coord_t sw, + lv_coord_t r) +{ + int32_t sw_ori = sw; + int32_t size = sw_ori + r; + + lv_area_t sh_area; + lv_area_copy(&sh_area, coords); + sh_area.x2 = sw / 2 + r - 1 - ((sw & 1) ? 0 : 1); + sh_area.y1 = sw / 2 + 1; + + sh_area.x1 = sh_area.x2 - lv_area_get_width(coords); + sh_area.y2 = sh_area.y1 + lv_area_get_height(coords); + + lv_draw_mask_radius_param_t mask_param; + lv_draw_mask_radius_init(&mask_param, &sh_area, r, false); + +#if SHADOW_ENHANCE + /*Set half shadow width width because blur will be repeated*/ + if(sw_ori == 1) sw = 1; + else sw = sw_ori >> 1; +#endif + + int32_t y; + lv_opa_t * mask_line = lv_mem_buf_get(size); + uint16_t * sh_ups_tmp_buf = (uint16_t *)sh_buf; + for(y = 0; y < size; y++) { + lv_memset_ff(mask_line, size); + lv_draw_mask_res_t mask_res = mask_param.dsc.cb(mask_line, 0, y, size, &mask_param); + if(mask_res == LV_DRAW_MASK_RES_TRANSP) { + lv_memset_00(sh_ups_tmp_buf, size * sizeof(sh_ups_tmp_buf[0])); + } + else { + int32_t i; + sh_ups_tmp_buf[0] = (mask_line[0] << SHADOW_UPSCALE_SHIFT) / sw; + for(i = 1; i < size; i++) { + if(mask_line[i] == mask_line[i - 1]) sh_ups_tmp_buf[i] = sh_ups_tmp_buf[i - 1]; + else sh_ups_tmp_buf[i] = (mask_line[i] << SHADOW_UPSCALE_SHIFT) / sw; + } + } + + sh_ups_tmp_buf += size; + } + lv_mem_buf_release(mask_line); + + lv_draw_mask_free_param(&mask_param); + + if(sw == 1) { + int32_t i; + lv_opa_t * res_buf = (lv_opa_t *)sh_buf; + for(i = 0; i < size * size; i++) { + res_buf[i] = (sh_buf[i] >> SHADOW_UPSCALE_SHIFT); + } + return; + } + + shadow_blur_corner(size, sw, sh_buf); + +#if SHADOW_ENHANCE == 0 + /*The result is required in lv_opa_t not uint16_t*/ + uint32_t x; + lv_opa_t * res_buf = (lv_opa_t *)sh_buf; + for(x = 0; x < size * size; x++) { + res_buf[x] = sh_buf[x]; + } +#else + sw += sw_ori & 1; + if(sw > 1) { + uint32_t i; + uint32_t max_v_div = (LV_OPA_COVER << SHADOW_UPSCALE_SHIFT) / sw; + for(i = 0; i < (uint32_t)size * size; i++) { + if(sh_buf[i] == 0) continue; + else if(sh_buf[i] == LV_OPA_COVER) sh_buf[i] = max_v_div; + else sh_buf[i] = (sh_buf[i] << SHADOW_UPSCALE_SHIFT) / sw; + } + + shadow_blur_corner(size, sw, sh_buf); + } + int32_t x; + lv_opa_t * res_buf = (lv_opa_t *)sh_buf; + for(x = 0; x < size * size; x++) { + res_buf[x] = sh_buf[x]; + } +#endif + +} + +LV_ATTRIBUTE_FAST_MEM static void shadow_blur_corner(lv_coord_t size, lv_coord_t sw, uint16_t * sh_ups_buf) +{ + int32_t s_left = sw >> 1; + int32_t s_right = (sw >> 1); + if((sw & 1) == 0) s_left--; + + /*Horizontal blur*/ + uint16_t * sh_ups_blur_buf = lv_mem_buf_get(size * sizeof(uint16_t)); + + int32_t x; + int32_t y; + + uint16_t * sh_ups_tmp_buf = sh_ups_buf; + + for(y = 0; y < size; y++) { + int32_t v = sh_ups_tmp_buf[size - 1] * sw; + for(x = size - 1; x >= 0; x--) { + sh_ups_blur_buf[x] = v; + + /*Forget the right pixel*/ + uint32_t right_val = 0; + if(x + s_right < size) right_val = sh_ups_tmp_buf[x + s_right]; + v -= right_val; + + /*Add the left pixel*/ + uint32_t left_val; + if(x - s_left - 1 < 0) left_val = sh_ups_tmp_buf[0]; + else left_val = sh_ups_tmp_buf[x - s_left - 1]; + v += left_val; + } + lv_memcpy(sh_ups_tmp_buf, sh_ups_blur_buf, size * sizeof(uint16_t)); + sh_ups_tmp_buf += size; + } + + /*Vertical blur*/ + uint32_t i; + uint32_t max_v = LV_OPA_COVER << SHADOW_UPSCALE_SHIFT; + uint32_t max_v_div = max_v / sw; + for(i = 0; i < (uint32_t)size * size; i++) { + if(sh_ups_buf[i] == 0) continue; + else if(sh_ups_buf[i] == max_v) sh_ups_buf[i] = max_v_div; + else sh_ups_buf[i] = sh_ups_buf[i] / sw; + } + + for(x = 0; x < size; x++) { + sh_ups_tmp_buf = &sh_ups_buf[x]; + int32_t v = sh_ups_tmp_buf[0] * sw; + for(y = 0; y < size ; y++, sh_ups_tmp_buf += size) { + sh_ups_blur_buf[y] = v < 0 ? 0 : (v >> SHADOW_UPSCALE_SHIFT); + + /*Forget the top pixel*/ + uint32_t top_val; + if(y - s_right <= 0) top_val = sh_ups_tmp_buf[0]; + else top_val = sh_ups_buf[(y - s_right) * size + x]; + v -= top_val; + + /*Add the bottom pixel*/ + uint32_t bottom_val; + if(y + s_left + 1 < size) bottom_val = sh_ups_buf[(y + s_left + 1) * size + x]; + else bottom_val = sh_ups_buf[(size - 1) * size + x]; + v += bottom_val; + } + + /*Write back the result into `sh_ups_buf`*/ + sh_ups_tmp_buf = &sh_ups_buf[x]; + for(y = 0; y < size; y++, sh_ups_tmp_buf += size) { + (*sh_ups_tmp_buf) = sh_ups_blur_buf[y]; + } + } + + lv_mem_buf_release(sh_ups_blur_buf); +} +#endif + +static void draw_outline(lv_draw_ctx_t * draw_ctx, const lv_draw_rect_dsc_t * dsc, const lv_area_t * coords) +{ + if(dsc->outline_opa <= LV_OPA_MIN) return; + if(dsc->outline_width == 0) return; + + lv_opa_t opa = dsc->outline_opa; + + if(opa > LV_OPA_MAX) opa = LV_OPA_COVER; + + /*Get the inner radius*/ + lv_area_t area_inner; + lv_area_copy(&area_inner, coords); + + /*Bring the outline closer to make sure there is no color bleeding with pad=0*/ + lv_coord_t pad = dsc->outline_pad - 1; + area_inner.x1 -= pad; + area_inner.y1 -= pad; + area_inner.x2 += pad; + area_inner.y2 += pad; + + lv_area_t area_outer; + lv_area_copy(&area_outer, &area_inner); + + area_outer.x1 -= dsc->outline_width; + area_outer.x2 += dsc->outline_width; + area_outer.y1 -= dsc->outline_width; + area_outer.y2 += dsc->outline_width; + + + int32_t inner_w = lv_area_get_width(&area_inner); + int32_t inner_h = lv_area_get_height(&area_inner); + int32_t rin = dsc->radius; + int32_t short_side = LV_MIN(inner_w, inner_h); + if(rin > short_side >> 1) rin = short_side >> 1; + + lv_coord_t rout = rin + dsc->outline_width; + + draw_border_generic(draw_ctx, &area_outer, &area_inner, rout, rin, dsc->outline_color, dsc->outline_opa, + dsc->blend_mode); +} + +void draw_border_generic(lv_draw_ctx_t * draw_ctx, const lv_area_t * outer_area, const lv_area_t * inner_area, + lv_coord_t rout, lv_coord_t rin, lv_color_t color, lv_opa_t opa, lv_blend_mode_t blend_mode) +{ + opa = opa >= LV_OPA_COVER ? LV_OPA_COVER : opa; + + bool mask_any = lv_draw_mask_is_any(outer_area); + +#if LV_DRAW_COMPLEX + + if(!mask_any && rout == 0 && rin == 0) { + draw_border_simple(draw_ctx, outer_area, inner_area, color, opa); + return; + } + + /*Get clipped draw area which is the real draw area. + *It is always the same or inside `coords`*/ + lv_area_t draw_area; + if(!_lv_area_intersect(&draw_area, outer_area, draw_ctx->clip_area)) return; + int32_t draw_area_w = lv_area_get_width(&draw_area); + + lv_draw_sw_blend_dsc_t blend_dsc; + lv_memset_00(&blend_dsc, sizeof(blend_dsc)); + blend_dsc.mask_buf = lv_mem_buf_get(draw_area_w);; + + + /*Create mask for the outer area*/ + int16_t mask_rout_id = LV_MASK_ID_INV; + lv_draw_mask_radius_param_t mask_rout_param; + if(rout > 0) { + lv_draw_mask_radius_init(&mask_rout_param, outer_area, rout, false); + mask_rout_id = lv_draw_mask_add(&mask_rout_param, NULL); + } + + /*Create mask for the inner mask*/ + lv_draw_mask_radius_param_t mask_rin_param; + lv_draw_mask_radius_init(&mask_rin_param, inner_area, rin, true); + int16_t mask_rin_id = lv_draw_mask_add(&mask_rin_param, NULL); + + int32_t h; + lv_area_t blend_area; + blend_dsc.blend_area = &blend_area; + blend_dsc.mask_area = &blend_area; + blend_dsc.color = color; + blend_dsc.opa = opa; + blend_dsc.blend_mode = blend_mode; + + /*Calculate the x and y coordinates where the straight parts area*/ + lv_area_t core_area; + core_area.x1 = LV_MAX(outer_area->x1 + rout, inner_area->x1); + core_area.x2 = LV_MIN(outer_area->x2 - rout, inner_area->x2); + core_area.y1 = LV_MAX(outer_area->y1 + rout, inner_area->y1); + core_area.y2 = LV_MIN(outer_area->y2 - rout, inner_area->y2); + lv_coord_t core_w = lv_area_get_width(&core_area); + + bool top_side = outer_area->y1 <= inner_area->y1 ? true : false; + bool bottom_side = outer_area->y2 >= inner_area->y2 ? true : false; + + /*If there is other masks, need to draw line by line*/ + if(mask_any) { + blend_area.x1 = draw_area.x1; + blend_area.x2 = draw_area.x2; + for(h = draw_area.y1; h <= draw_area.y2; h++) { + if(!top_side && h < core_area.y1) continue; + if(!bottom_side && h > core_area.y2) break; + + blend_area.y1 = h; + blend_area.y2 = h; + + lv_memset_ff(blend_dsc.mask_buf, draw_area_w); + blend_dsc.mask_res = lv_draw_mask_apply(blend_dsc.mask_buf, draw_area.x1, h, draw_area_w); + lv_draw_sw_blend(draw_ctx, &blend_dsc); + } + + lv_draw_mask_free_param(&mask_rin_param); + lv_draw_mask_remove_id(mask_rin_id); + if(mask_rout_id != LV_MASK_ID_INV) { + lv_draw_mask_free_param(&mask_rout_param); + lv_draw_mask_remove_id(mask_rout_id); + } + lv_mem_buf_release(blend_dsc.mask_buf); + return; + } + + /*No masks*/ + bool left_side = outer_area->x1 <= inner_area->x1 ? true : false; + bool right_side = outer_area->x2 >= inner_area->x2 ? true : false; + + bool split_hor = true; + if(left_side && right_side && top_side && bottom_side && + core_w < SPLIT_LIMIT) { + split_hor = false; + } + + blend_dsc.mask_res = LV_DRAW_MASK_RES_FULL_COVER; + /*Draw the straight lines first if they are long enough*/ + if(top_side && split_hor) { + blend_area.x1 = core_area.x1; + blend_area.x2 = core_area.x2; + blend_area.y1 = outer_area->y1; + blend_area.y2 = inner_area->y1 - 1; + lv_draw_sw_blend(draw_ctx, &blend_dsc); + } + + if(bottom_side && split_hor) { + blend_area.x1 = core_area.x1; + blend_area.x2 = core_area.x2; + blend_area.y1 = inner_area->y2 + 1; + blend_area.y2 = outer_area->y2; + lv_draw_sw_blend(draw_ctx, &blend_dsc); + } + + if(left_side) { + blend_area.x1 = outer_area->x1; + blend_area.x2 = inner_area->x1 - 1; + blend_area.y1 = core_area.y1; + blend_area.y2 = core_area.y2; + lv_draw_sw_blend(draw_ctx, &blend_dsc); + } + + if(right_side) { + blend_area.x1 = inner_area->x2 + 1; + blend_area.x2 = outer_area->x2; + blend_area.y1 = core_area.y1; + blend_area.y2 = core_area.y2; + lv_draw_sw_blend(draw_ctx, &blend_dsc); + } + + /*Draw the corners*/ + lv_coord_t blend_w; + + /*Left and right corner together if they are close to each other*/ + if(!split_hor) { + /*Calculate the top corner and mirror it to the bottom*/ + blend_area.x1 = draw_area.x1; + blend_area.x2 = draw_area.x2; + lv_coord_t max_h = LV_MAX(rout, inner_area->y1 - outer_area->y1); + for(h = 0; h < max_h; h++) { + lv_coord_t top_y = outer_area->y1 + h; + lv_coord_t bottom_y = outer_area->y2 - h; + if(top_y < draw_area.y1 && bottom_y > draw_area.y2) continue; /*This line is clipped now*/ + + lv_memset_ff(blend_dsc.mask_buf, draw_area_w); + blend_dsc.mask_res = lv_draw_mask_apply(blend_dsc.mask_buf, blend_area.x1, top_y, draw_area_w); + + if(top_y >= draw_area.y1) { + blend_area.y1 = top_y; + blend_area.y2 = top_y; + lv_draw_sw_blend(draw_ctx, &blend_dsc); + } + + if(bottom_y <= draw_area.y2) { + blend_area.y1 = bottom_y; + blend_area.y2 = bottom_y; + lv_draw_sw_blend(draw_ctx, &blend_dsc); + } + } + } + else { + /*Left corners*/ + blend_area.x1 = draw_area.x1; + blend_area.x2 = LV_MIN(draw_area.x2, core_area.x1 - 1); + blend_w = lv_area_get_width(&blend_area); + if(blend_w > 0) { + if(left_side || top_side) { + for(h = draw_area.y1; h < core_area.y1; h++) { + blend_area.y1 = h; + blend_area.y2 = h; + + lv_memset_ff(blend_dsc.mask_buf, blend_w); + blend_dsc.mask_res = lv_draw_mask_apply(blend_dsc.mask_buf, blend_area.x1, h, blend_w); + lv_draw_sw_blend(draw_ctx, &blend_dsc); + } + } + + if(left_side || bottom_side) { + for(h = core_area.y2 + 1; h <= draw_area.y2; h++) { + blend_area.y1 = h; + blend_area.y2 = h; + + lv_memset_ff(blend_dsc.mask_buf, blend_w); + blend_dsc.mask_res = lv_draw_mask_apply(blend_dsc.mask_buf, blend_area.x1, h, blend_w); + lv_draw_sw_blend(draw_ctx, &blend_dsc); + } + } + } + + /*Right corners*/ + blend_area.x1 = LV_MAX(draw_area.x1, core_area.x2 + 1); + blend_area.x2 = draw_area.x2; + blend_w = lv_area_get_width(&blend_area); + + if(blend_w > 0) { + if(right_side || top_side) { + for(h = draw_area.y1; h < core_area.y1; h++) { + blend_area.y1 = h; + blend_area.y2 = h; + + lv_memset_ff(blend_dsc.mask_buf, blend_w); + blend_dsc.mask_res = lv_draw_mask_apply(blend_dsc.mask_buf, blend_area.x1, h, blend_w); + lv_draw_sw_blend(draw_ctx, &blend_dsc); + } + } + + if(right_side || bottom_side) { + for(h = core_area.y2 + 1; h <= draw_area.y2; h++) { + blend_area.y1 = h; + blend_area.y2 = h; + + lv_memset_ff(blend_dsc.mask_buf, blend_w); + blend_dsc.mask_res = lv_draw_mask_apply(blend_dsc.mask_buf, blend_area.x1, h, blend_w); + lv_draw_sw_blend(draw_ctx, &blend_dsc); + } + } + } + } + + lv_draw_mask_free_param(&mask_rin_param); + lv_draw_mask_remove_id(mask_rin_id); + lv_draw_mask_free_param(&mask_rout_param); + lv_draw_mask_remove_id(mask_rout_id); + lv_mem_buf_release(blend_dsc.mask_buf); + +#else /*LV_DRAW_COMPLEX*/ + LV_UNUSED(blend_mode); + LV_UNUSED(rout); + LV_UNUSED(rin); + if(!mask_any) { + draw_border_simple(draw_ctx, outer_area, inner_area, color, opa); + return; + } + +#endif /*LV_DRAW_COMPLEX*/ +} +static void draw_border_simple(lv_draw_ctx_t * draw_ctx, const lv_area_t * outer_area, const lv_area_t * inner_area, + lv_color_t color, lv_opa_t opa) +{ + lv_area_t a; + lv_draw_sw_blend_dsc_t blend_dsc; + lv_memset_00(&blend_dsc, sizeof(lv_draw_sw_blend_dsc_t)); + blend_dsc.blend_area = &a; + blend_dsc.color = color; + blend_dsc.opa = opa; + + bool top_side = outer_area->y1 <= inner_area->y1 ? true : false; + bool bottom_side = outer_area->y2 >= inner_area->y2 ? true : false; + bool left_side = outer_area->x1 <= inner_area->x1 ? true : false; + bool right_side = outer_area->x2 >= inner_area->x2 ? true : false; + + + /*Top*/ + a.x1 = outer_area->x1; + a.x2 = outer_area->x2; + a.y1 = outer_area->y1; + a.y2 = inner_area->y1 - 1; + if(top_side) { + lv_draw_sw_blend(draw_ctx, &blend_dsc); + } + + /*Bottom*/ + a.y1 = inner_area->y2 + 1; + a.y2 = outer_area->y2; + if(bottom_side) { + lv_draw_sw_blend(draw_ctx, &blend_dsc); + } + + /*Left*/ + a.x1 = outer_area->x1; + a.x2 = inner_area->x1 - 1; + a.y1 = (top_side) ? inner_area->y1 : outer_area->y1; + a.y2 = (bottom_side) ? inner_area->y2 : outer_area->y2; + if(left_side) { + lv_draw_sw_blend(draw_ctx, &blend_dsc); + } + + /*Right*/ + a.x1 = inner_area->x2 + 1; + a.x2 = outer_area->x2; + if(right_side) { + lv_draw_sw_blend(draw_ctx, &blend_dsc); + } +} + diff --git a/lib/lvgl/src/draw/sw/lv_draw_sw_transform.c b/lib/lvgl/src/draw/sw/lv_draw_sw_transform.c new file mode 100644 index 00000000..80b1e6de --- /dev/null +++ b/lib/lvgl/src/draw/sw/lv_draw_sw_transform.c @@ -0,0 +1,496 @@ +/** + * @file lv_draw_sw_tranform.c + * + */ + +/********************* + * INCLUDES + *********************/ +#include "lv_draw_sw.h" +#include "../../misc/lv_assert.h" +#include "../../misc/lv_area.h" +#include "../../core/lv_refr.h" + +#if LV_DRAW_COMPLEX +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ +typedef struct { + int32_t x_in; + int32_t y_in; + int32_t x_out; + int32_t y_out; + int32_t sinma; + int32_t cosma; + int32_t zoom; + int32_t angle; + int32_t pivot_x_256; + int32_t pivot_y_256; + lv_point_t pivot; +} point_transform_dsc_t; + +/********************** + * STATIC PROTOTYPES + **********************/ +/** + * Transform a point with 1/256 precision (the output coordinates are upscaled by 256) + * @param t pointer to n initialized `point_transform_dsc_t` structure + * @param xin X coordinate to rotate + * @param yin Y coordinate to rotate + * @param xout upscaled, transformed X + * @param yout upscaled, transformed Y + */ +static void transform_point_upscaled(point_transform_dsc_t * t, int32_t xin, int32_t yin, int32_t * xout, + int32_t * yout); + +static void argb_no_aa(const uint8_t * src, lv_coord_t src_w, lv_coord_t src_h, lv_coord_t src_stride, + int32_t xs_ups, int32_t ys_ups, int32_t xs_step, int32_t ys_step, + int32_t x_end, lv_color_t * cbuf, uint8_t * abuf); + +static void rgb_no_aa(const uint8_t * src, lv_coord_t src_w, lv_coord_t src_h, lv_coord_t src_stride, + int32_t xs_ups, int32_t ys_ups, int32_t xs_step, int32_t ys_step, + int32_t x_end, lv_color_t * cbuf, uint8_t * abuf, lv_img_cf_t cf); + +#if LV_COLOR_DEPTH == 16 +static void rgb565a8_no_aa(const uint8_t * src, lv_coord_t src_w, lv_coord_t src_h, lv_coord_t src_stride, + int32_t xs_ups, int32_t ys_ups, int32_t xs_step, int32_t ys_step, + int32_t x_end, lv_color_t * cbuf, uint8_t * abuf); +#endif + +static void argb_and_rgb_aa(const uint8_t * src, lv_coord_t src_w, lv_coord_t src_h, lv_coord_t src_stride, + int32_t xs_ups, int32_t ys_ups, int32_t xs_step, int32_t ys_step, + int32_t x_end, lv_color_t * cbuf, uint8_t * abuf, lv_img_cf_t cf); + +/********************** + * STATIC VARIABLES + **********************/ + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +void lv_draw_sw_transform(lv_draw_ctx_t * draw_ctx, const lv_area_t * dest_area, const void * src_buf, + lv_coord_t src_w, lv_coord_t src_h, lv_coord_t src_stride, + const lv_draw_img_dsc_t * draw_dsc, lv_img_cf_t cf, lv_color_t * cbuf, lv_opa_t * abuf) +{ + LV_UNUSED(draw_ctx); + + point_transform_dsc_t tr_dsc; + tr_dsc.angle = -draw_dsc->angle; + tr_dsc.zoom = (256 * 256) / draw_dsc->zoom; + tr_dsc.pivot = draw_dsc->pivot; + + int32_t angle_low = tr_dsc.angle / 10; + int32_t angle_high = angle_low + 1; + int32_t angle_rem = tr_dsc.angle - (angle_low * 10); + + int32_t s1 = lv_trigo_sin(angle_low); + int32_t s2 = lv_trigo_sin(angle_high); + + int32_t c1 = lv_trigo_sin(angle_low + 90); + int32_t c2 = lv_trigo_sin(angle_high + 90); + + tr_dsc.sinma = (s1 * (10 - angle_rem) + s2 * angle_rem) / 10; + tr_dsc.cosma = (c1 * (10 - angle_rem) + c2 * angle_rem) / 10; + tr_dsc.sinma = tr_dsc.sinma >> (LV_TRIGO_SHIFT - 10); + tr_dsc.cosma = tr_dsc.cosma >> (LV_TRIGO_SHIFT - 10); + tr_dsc.pivot_x_256 = tr_dsc.pivot.x * 256; + tr_dsc.pivot_y_256 = tr_dsc.pivot.y * 256; + + lv_coord_t dest_w = lv_area_get_width(dest_area); + lv_coord_t dest_h = lv_area_get_height(dest_area); + lv_coord_t y; + for(y = 0; y < dest_h; y++) { + int32_t xs1_ups, ys1_ups, xs2_ups, ys2_ups; + + transform_point_upscaled(&tr_dsc, dest_area->x1, dest_area->y1 + y, &xs1_ups, &ys1_ups); + transform_point_upscaled(&tr_dsc, dest_area->x2, dest_area->y1 + y, &xs2_ups, &ys2_ups); + + int32_t xs_diff = xs2_ups - xs1_ups; + int32_t ys_diff = ys2_ups - ys1_ups; + int32_t xs_step_256 = 0; + int32_t ys_step_256 = 0; + if(dest_w > 1) { + xs_step_256 = (256 * xs_diff) / (dest_w - 1); + ys_step_256 = (256 * ys_diff) / (dest_w - 1); + } + int32_t xs_ups = xs1_ups; + int32_t ys_ups = ys1_ups; + + if(draw_dsc->antialias == 0) { + switch(cf) { + case LV_IMG_CF_TRUE_COLOR_ALPHA: + argb_no_aa(src_buf, src_w, src_h, src_stride, xs_ups, ys_ups, xs_step_256, ys_step_256, dest_w, cbuf, abuf); + break; + case LV_IMG_CF_TRUE_COLOR: + case LV_IMG_CF_TRUE_COLOR_CHROMA_KEYED: + rgb_no_aa(src_buf, src_w, src_h, src_stride, xs_ups, ys_ups, xs_step_256, ys_step_256, dest_w, cbuf, abuf, cf); + break; + +#if LV_COLOR_DEPTH == 16 + case LV_IMG_CF_RGB565A8: + rgb565a8_no_aa(src_buf, src_w, src_h, src_stride, xs_ups, ys_ups, xs_step_256, ys_step_256, dest_w, cbuf, abuf); + break; +#endif + default: + break; + } + } + else { + argb_and_rgb_aa(src_buf, src_w, src_h, src_stride, xs_ups, ys_ups, xs_step_256, ys_step_256, dest_w, cbuf, abuf, cf); + } + + cbuf += dest_w; + abuf += dest_w; + } +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +static void rgb_no_aa(const uint8_t * src, lv_coord_t src_w, lv_coord_t src_h, lv_coord_t src_stride, + int32_t xs_ups, int32_t ys_ups, int32_t xs_step, int32_t ys_step, + int32_t x_end, lv_color_t * cbuf, uint8_t * abuf, lv_img_cf_t cf) +{ + int32_t xs_ups_start = xs_ups; + int32_t ys_ups_start = ys_ups; + lv_disp_t * d = _lv_refr_get_disp_refreshing(); + lv_color_t ck = d->driver->color_chroma_key; + + lv_memset_ff(abuf, x_end); + + lv_coord_t x; + for(x = 0; x < x_end; x++) { + xs_ups = xs_ups_start + ((xs_step * x) >> 8); + ys_ups = ys_ups_start + ((ys_step * x) >> 8); + + int32_t xs_int = xs_ups >> 8; + int32_t ys_int = ys_ups >> 8; + if(xs_int < 0 || xs_int >= src_w || ys_int < 0 || ys_int >= src_h) { + abuf[x] = 0x00; + } + else { + +#if LV_COLOR_DEPTH == 8 + const uint8_t * src_tmp = src; + src_tmp += ys_int * src_stride + xs_int; + cbuf[x].full = src_tmp[0]; +#elif LV_COLOR_DEPTH == 16 + const lv_color_t * src_tmp = (const lv_color_t *)src; + src_tmp += ys_int * src_stride + xs_int; + cbuf[x] = *src_tmp; +#elif LV_COLOR_DEPTH == 32 + const uint8_t * src_tmp = src; + src_tmp += (ys_int * src_stride * sizeof(lv_color_t)) + xs_int * sizeof(lv_color_t); + cbuf[x].full = *((uint32_t *)src_tmp); +#endif + } + if(cf == LV_IMG_CF_TRUE_COLOR_CHROMA_KEYED && cbuf[x].full == ck.full) { + abuf[x] = 0x00; + } + } +} + +static void argb_no_aa(const uint8_t * src, lv_coord_t src_w, lv_coord_t src_h, lv_coord_t src_stride, + int32_t xs_ups, int32_t ys_ups, int32_t xs_step, int32_t ys_step, + int32_t x_end, lv_color_t * cbuf, uint8_t * abuf) +{ + int32_t xs_ups_start = xs_ups; + int32_t ys_ups_start = ys_ups; + + lv_coord_t x; + for(x = 0; x < x_end; x++) { + xs_ups = xs_ups_start + ((xs_step * x) >> 8); + ys_ups = ys_ups_start + ((ys_step * x) >> 8); + + int32_t xs_int = xs_ups >> 8; + int32_t ys_int = ys_ups >> 8; + if(xs_int < 0 || xs_int >= src_w || ys_int < 0 || ys_int >= src_h) { + abuf[x] = 0; + } + else { + const uint8_t * src_tmp = src; + src_tmp += (ys_int * src_stride * LV_IMG_PX_SIZE_ALPHA_BYTE) + xs_int * LV_IMG_PX_SIZE_ALPHA_BYTE; + +#if LV_COLOR_DEPTH == 8 + cbuf[x].full = src_tmp[0]; +#elif LV_COLOR_DEPTH == 16 + cbuf[x].full = src_tmp[0] + (src_tmp[1] << 8); +#elif LV_COLOR_DEPTH == 32 + cbuf[x].full = *((uint32_t *)src_tmp); +#endif + abuf[x] = src_tmp[LV_IMG_PX_SIZE_ALPHA_BYTE - 1]; + } + } +} + +#if LV_COLOR_DEPTH == 16 +static void rgb565a8_no_aa(const uint8_t * src, lv_coord_t src_w, lv_coord_t src_h, lv_coord_t src_stride, + int32_t xs_ups, int32_t ys_ups, int32_t xs_step, int32_t ys_step, + int32_t x_end, lv_color_t * cbuf, uint8_t * abuf) +{ + int32_t xs_ups_start = xs_ups; + int32_t ys_ups_start = ys_ups; + + lv_coord_t x; + for(x = 0; x < x_end; x++) { + xs_ups = xs_ups_start + ((xs_step * x) >> 8); + ys_ups = ys_ups_start + ((ys_step * x) >> 8); + + int32_t xs_int = xs_ups >> 8; + int32_t ys_int = ys_ups >> 8; + if(xs_int < 0 || xs_int >= src_w || ys_int < 0 || ys_int >= src_h) { + abuf[x] = 0; + } + else { + const lv_color_t * src_tmp = (const lv_color_t *)src; + src_tmp += ys_int * src_stride + xs_int; + cbuf[x] = *src_tmp; + + const lv_opa_t * a_tmp = src + src_stride * src_h * sizeof(lv_color_t); + a_tmp += ys_int * src_stride + xs_int; + abuf[x] = *a_tmp; + } + } +} +#endif + + +static void argb_and_rgb_aa(const uint8_t * src, lv_coord_t src_w, lv_coord_t src_h, lv_coord_t src_stride, + int32_t xs_ups, int32_t ys_ups, int32_t xs_step, int32_t ys_step, + int32_t x_end, lv_color_t * cbuf, uint8_t * abuf, lv_img_cf_t cf) +{ + int32_t xs_ups_start = xs_ups; + int32_t ys_ups_start = ys_ups; + bool has_alpha; + int32_t px_size; + lv_color_t ck = {0}; + switch(cf) { + case LV_IMG_CF_TRUE_COLOR: + has_alpha = false; + px_size = sizeof(lv_color_t); + break; + case LV_IMG_CF_TRUE_COLOR_ALPHA: + has_alpha = true; + px_size = LV_IMG_PX_SIZE_ALPHA_BYTE; + break; + case LV_IMG_CF_TRUE_COLOR_CHROMA_KEYED: { + has_alpha = true; + px_size = sizeof(lv_color_t); + lv_disp_t * d = _lv_refr_get_disp_refreshing(); + ck = d->driver->color_chroma_key; + break; + } +#if LV_COLOR_DEPTH == 16 + case LV_IMG_CF_RGB565A8: + has_alpha = true; + px_size = sizeof(lv_color_t); + break; +#endif + default: + return; + } + + lv_coord_t x; + for(x = 0; x < x_end; x++) { + xs_ups = xs_ups_start + ((xs_step * x) >> 8); + ys_ups = ys_ups_start + ((ys_step * x) >> 8); + + int32_t xs_int = xs_ups >> 8; + int32_t ys_int = ys_ups >> 8; + + /*Fully out of the image*/ + if(xs_int < 0 || xs_int >= src_w || ys_int < 0 || ys_int >= src_h) { + abuf[x] = 0x00; + continue; + } + + /*Get the direction the hor and ver neighbor + *`fract` will be in range of 0x00..0xFF and `next` (+/-1) indicates the direction*/ + int32_t xs_fract = xs_ups & 0xFF; + int32_t ys_fract = ys_ups & 0xFF; + + int32_t x_next; + int32_t y_next; + if(xs_fract < 0x80) { + x_next = -1; + xs_fract = (0x7F - xs_fract) * 2; + } + else { + x_next = 1; + xs_fract = (xs_fract - 0x80) * 2; + } + if(ys_fract < 0x80) { + y_next = -1; + ys_fract = (0x7F - ys_fract) * 2; + } + else { + y_next = 1; + ys_fract = (ys_fract - 0x80) * 2; + } + + const uint8_t * src_tmp = src; + src_tmp += (ys_int * src_stride * px_size) + xs_int * px_size; + + + if(xs_int + x_next >= 0 && + xs_int + x_next <= src_w - 1 && + ys_int + y_next >= 0 && + ys_int + y_next <= src_h - 1) { + + const uint8_t * px_base = src_tmp; + const uint8_t * px_hor = src_tmp + x_next * px_size; + const uint8_t * px_ver = src_tmp + y_next * src_stride * px_size; + lv_color_t c_base; + lv_color_t c_ver; + lv_color_t c_hor; + + if(has_alpha) { + lv_opa_t a_base; + lv_opa_t a_ver; + lv_opa_t a_hor; + if(cf == LV_IMG_CF_TRUE_COLOR_ALPHA) { + a_base = px_base[LV_IMG_PX_SIZE_ALPHA_BYTE - 1]; + a_ver = px_ver[LV_IMG_PX_SIZE_ALPHA_BYTE - 1]; + a_hor = px_hor[LV_IMG_PX_SIZE_ALPHA_BYTE - 1]; + } +#if LV_COLOR_DEPTH == 16 + else if(cf == LV_IMG_CF_RGB565A8) { + const lv_opa_t * a_tmp = src + src_stride * src_h * sizeof(lv_color_t); + a_base = *(a_tmp + (ys_int * src_stride) + xs_int); + a_hor = *(a_tmp + (ys_int * src_stride) + xs_int + x_next); + a_ver = *(a_tmp + ((ys_int + y_next) * src_stride) + xs_int); + } +#endif + else if(cf == LV_IMG_CF_TRUE_COLOR_CHROMA_KEYED) { + if(((lv_color_t *)px_base)->full == ck.full || + ((lv_color_t *)px_ver)->full == ck.full || + ((lv_color_t *)px_hor)->full == ck.full) { + abuf[x] = 0x00; + continue; + } + else { + a_base = 0xff; + a_ver = 0xff; + a_hor = 0xff; + } + } + else { + a_base = 0xff; + a_ver = 0xff; + a_hor = 0xff; + } + + if(a_ver != a_base) a_ver = ((a_ver * ys_fract) + (a_base * (0x100 - ys_fract))) >> 8; + if(a_hor != a_base) a_hor = ((a_hor * xs_fract) + (a_base * (0x100 - xs_fract))) >> 8; + abuf[x] = (a_ver + a_hor) >> 1; + + if(abuf[x] == 0x00) continue; + +#if LV_COLOR_DEPTH == 8 + c_base.full = px_base[0]; + c_ver.full = px_ver[0]; + c_hor.full = px_hor[0]; +#elif LV_COLOR_DEPTH == 16 + c_base.full = px_base[0] + (px_base[1] << 8); + c_ver.full = px_ver[0] + (px_ver[1] << 8); + c_hor.full = px_hor[0] + (px_hor[1] << 8); +#elif LV_COLOR_DEPTH == 32 + c_base.full = *((uint32_t *)px_base); + c_ver.full = *((uint32_t *)px_ver); + c_hor.full = *((uint32_t *)px_hor); +#endif + } + /*No alpha channel -> RGB*/ + else { + c_base = *((const lv_color_t *) px_base); + c_hor = *((const lv_color_t *) px_hor); + c_ver = *((const lv_color_t *) px_ver); + abuf[x] = 0xff; + } + + if(c_base.full == c_ver.full && c_base.full == c_hor.full) { + cbuf[x] = c_base; + } + else { + c_ver = lv_color_mix(c_ver, c_base, ys_fract); + c_hor = lv_color_mix(c_hor, c_base, xs_fract); + cbuf[x] = lv_color_mix(c_hor, c_ver, LV_OPA_50); + } + } + /*Partially out of the image*/ + else { +#if LV_COLOR_DEPTH == 8 + cbuf[x].full = src_tmp[0]; +#elif LV_COLOR_DEPTH == 16 + cbuf[x].full = src_tmp[0] + (src_tmp[1] << 8); +#elif LV_COLOR_DEPTH == 32 + cbuf[x].full = *((uint32_t *)src_tmp); +#endif + lv_opa_t a; + switch(cf) { + case LV_IMG_CF_TRUE_COLOR_ALPHA: + a = src_tmp[LV_IMG_PX_SIZE_ALPHA_BYTE - 1]; + break; + case LV_IMG_CF_TRUE_COLOR_CHROMA_KEYED: + a = cbuf[x].full == ck.full ? 0x00 : 0xff; + break; +#if LV_COLOR_DEPTH == 16 + case LV_IMG_CF_RGB565A8: + a = *(src + src_stride * src_h * sizeof(lv_color_t) + (ys_int * src_stride) + xs_int); + break; +#endif + default: + a = 0xff; + } + + if((xs_int == 0 && x_next < 0) || (xs_int == src_w - 1 && x_next > 0)) { + abuf[x] = (a * (0xFF - xs_fract)) >> 8; + } + else if((ys_int == 0 && y_next < 0) || (ys_int == src_h - 1 && y_next > 0)) { + abuf[x] = (a * (0xFF - ys_fract)) >> 8; + } + else { + abuf[x] = 0x00; + } + } + } +} + +static void transform_point_upscaled(point_transform_dsc_t * t, int32_t xin, int32_t yin, int32_t * xout, + int32_t * yout) +{ + if(t->angle == 0 && t->zoom == LV_IMG_ZOOM_NONE) { + *xout = xin * 256; + *yout = yin * 256; + return; + } + + xin -= t->pivot.x; + yin -= t->pivot.y; + + if(t->angle == 0) { + *xout = ((int32_t)(xin * t->zoom)) + (t->pivot_x_256); + *yout = ((int32_t)(yin * t->zoom)) + (t->pivot_y_256); + } + else if(t->zoom == LV_IMG_ZOOM_NONE) { + *xout = ((t->cosma * xin - t->sinma * yin) >> 2) + (t->pivot_x_256); + *yout = ((t->sinma * xin + t->cosma * yin) >> 2) + (t->pivot_y_256); + } + else { + *xout = (((t->cosma * xin - t->sinma * yin) * t->zoom) >> 10) + (t->pivot_x_256); + *yout = (((t->sinma * xin + t->cosma * yin) * t->zoom) >> 10) + (t->pivot_y_256); + } +} + +#endif + |
