summaryrefslogtreecommitdiff
path: root/lib/lvgl/src/draw/sw
diff options
context:
space:
mode:
authorjacqueline <me@jacqueline.id.au>2023-06-01 15:41:47 +1000
committerjacqueline <me@jacqueline.id.au>2023-06-01 15:41:47 +1000
commitdd27c3530432ea0b09f01e604bf577f31d8ef841 (patch)
treebbf86cf81a78f0ff0b07f31f1c390db473f26fd3 /lib/lvgl/src/draw/sw
parent6fd588e970470b15936187980829916d0dbe77bb (diff)
downloadtangara-fw-dd27c3530432ea0b09f01e604bf577f31d8ef841.tar.gz
convert lvgl from submodule to a plain old directory
Diffstat (limited to 'lib/lvgl/src/draw/sw')
m---------lib/lvgl0
-rw-r--r--lib/lvgl/src/draw/sw/lv_draw_sw.c108
-rw-r--r--lib/lvgl/src/draw/sw/lv_draw_sw.h104
-rw-r--r--lib/lvgl/src/draw/sw/lv_draw_sw.mk17
-rw-r--r--lib/lvgl/src/draw/sw/lv_draw_sw_arc.c537
-rw-r--r--lib/lvgl/src/draw/sw/lv_draw_sw_blend.c1039
-rw-r--r--lib/lvgl/src/draw/sw/lv_draw_sw_blend.h69
-rw-r--r--lib/lvgl/src/draw/sw/lv_draw_sw_dither.c213
-rw-r--r--lib/lvgl/src/draw/sw/lv_draw_sw_dither.h70
-rw-r--r--lib/lvgl/src/draw/sw/lv_draw_sw_gradient.c346
-rw-r--r--lib/lvgl/src/draw/sw/lv_draw_sw_gradient.h97
-rw-r--r--lib/lvgl/src/draw/sw/lv_draw_sw_img.c297
-rw-r--r--lib/lvgl/src/draw/sw/lv_draw_sw_layer.c150
-rw-r--r--lib/lvgl/src/draw/sw/lv_draw_sw_letter.c573
-rw-r--r--lib/lvgl/src/draw/sw/lv_draw_sw_line.c443
-rw-r--r--lib/lvgl/src/draw/sw/lv_draw_sw_polygon.c207
-rw-r--r--lib/lvgl/src/draw/sw/lv_draw_sw_rect.c1436
-rw-r--r--lib/lvgl/src/draw/sw/lv_draw_sw_transform.c496
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(&center_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
+