summaryrefslogtreecommitdiff
path: root/lib/lvgl/src/draw
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
parent6fd588e970470b15936187980829916d0dbe77bb (diff)
downloadtangara-fw-dd27c3530432ea0b09f01e604bf577f31d8ef841.tar.gz
convert lvgl from submodule to a plain old directory
Diffstat (limited to 'lib/lvgl/src/draw')
m---------lib/lvgl0
-rw-r--r--lib/lvgl/src/draw/arm2d/lv_draw_arm2d.mk6
-rw-r--r--lib/lvgl/src/draw/arm2d/lv_gpu_arm2d.c1376
-rw-r--r--lib/lvgl/src/draw/arm2d/lv_gpu_arm2d.h51
-rw-r--r--lib/lvgl/src/draw/lv_draw.c53
-rw-r--r--lib/lvgl/src/draw/lv_draw.h218
-rw-r--r--lib/lvgl/src/draw/lv_draw.mk25
-rw-r--r--lib/lvgl/src/draw/lv_draw_arc.c152
-rw-r--r--lib/lvgl/src/draw/lv_draw_arc.h83
-rw-r--r--lib/lvgl/src/draw/lv_draw_img.c364
-rw-r--r--lib/lvgl/src/draw/lv_draw_img.h104
-rw-r--r--lib/lvgl/src/draw/lv_draw_label.c417
-rw-r--r--lib/lvgl/src/draw/lv_draw_label.h100
-rw-r--r--lib/lvgl/src/draw/lv_draw_layer.c93
-rw-r--r--lib/lvgl/src/draw/lv_draw_layer.h83
-rw-r--r--lib/lvgl/src/draw/lv_draw_line.c56
-rw-r--r--lib/lvgl/src/draw/lv_draw_line.h67
-rw-r--r--lib/lvgl/src/draw/lv_draw_mask.c1530
-rw-r--r--lib/lvgl/src/draw/lv_draw_mask.h394
-rw-r--r--lib/lvgl/src/draw/lv_draw_rect.c73
-rw-r--r--lib/lvgl/src/draw/lv_draw_rect.h96
-rw-r--r--lib/lvgl/src/draw/lv_draw_transform.c54
-rw-r--r--lib/lvgl/src/draw/lv_draw_transform.h44
-rw-r--r--lib/lvgl/src/draw/lv_draw_triangle.c52
-rw-r--r--lib/lvgl/src/draw/lv_draw_triangle.h42
-rw-r--r--lib/lvgl/src/draw/lv_img_buf.c463
-rw-r--r--lib/lvgl/src/draw/lv_img_buf.h249
-rw-r--r--lib/lvgl/src/draw/lv_img_cache.c215
-rw-r--r--lib/lvgl/src/draw/lv_img_cache.h78
-rw-r--r--lib/lvgl/src/draw/lv_img_decoder.c705
-rw-r--r--lib/lvgl/src/draw/lv_img_decoder.h274
-rw-r--r--lib/lvgl/src/draw/nxp/lv_draw_nxp.mk9
-rw-r--r--lib/lvgl/src/draw/nxp/lv_gpu_nxp.c418
-rw-r--r--lib/lvgl/src/draw/nxp/lv_gpu_nxp.h71
-rw-r--r--lib/lvgl/src/draw/nxp/pxp/lv_draw_nxp_pxp.mk8
-rw-r--r--lib/lvgl/src/draw/nxp/pxp/lv_draw_pxp_blend.c632
-rw-r--r--lib/lvgl/src/draw/nxp/pxp/lv_draw_pxp_blend.h143
-rw-r--r--lib/lvgl/src/draw/nxp/pxp/lv_gpu_nxp_pxp.c116
-rw-r--r--lib/lvgl/src/draw/nxp/pxp/lv_gpu_nxp_pxp.h153
-rw-r--r--lib/lvgl/src/draw/nxp/pxp/lv_gpu_nxp_pxp_osa.c164
-rw-r--r--lib/lvgl/src/draw/nxp/pxp/lv_gpu_nxp_pxp_osa.h78
-rw-r--r--lib/lvgl/src/draw/nxp/vglite/lv_draw_nxp_vglite.mk9
-rw-r--r--lib/lvgl/src/draw/nxp/vglite/lv_draw_vglite_arc.c699
-rw-r--r--lib/lvgl/src/draw/nxp/vglite/lv_draw_vglite_arc.h79
-rw-r--r--lib/lvgl/src/draw/nxp/vglite/lv_draw_vglite_blend.c618
-rw-r--r--lib/lvgl/src/draw/nxp/vglite/lv_draw_vglite_blend.h149
-rw-r--r--lib/lvgl/src/draw/nxp/vglite/lv_draw_vglite_rect.c244
-rw-r--r--lib/lvgl/src/draw/nxp/vglite/lv_draw_vglite_rect.h77
-rw-r--r--lib/lvgl/src/draw/nxp/vglite/lv_gpu_nxp_vglite.c153
-rw-r--r--lib/lvgl/src/draw/nxp/vglite/lv_gpu_nxp_vglite.h185
-rw-r--r--lib/lvgl/src/draw/sdl/README.md28
-rw-r--r--lib/lvgl/src/draw/sdl/lv_draw_sdl.c103
-rw-r--r--lib/lvgl/src/draw/sdl/lv_draw_sdl.h96
-rw-r--r--lib/lvgl/src/draw/sdl/lv_draw_sdl.mk19
-rw-r--r--lib/lvgl/src/draw/sdl/lv_draw_sdl_arc.c238
-rw-r--r--lib/lvgl/src/draw/sdl/lv_draw_sdl_bg.c106
-rw-r--r--lib/lvgl/src/draw/sdl/lv_draw_sdl_composite.c262
-rw-r--r--lib/lvgl/src/draw/sdl/lv_draw_sdl_composite.h73
-rw-r--r--lib/lvgl/src/draw/sdl/lv_draw_sdl_img.c467
-rw-r--r--lib/lvgl/src/draw/sdl/lv_draw_sdl_img.h72
-rw-r--r--lib/lvgl/src/draw/sdl/lv_draw_sdl_label.c176
-rw-r--r--lib/lvgl/src/draw/sdl/lv_draw_sdl_layer.c141
-rw-r--r--lib/lvgl/src/draw/sdl/lv_draw_sdl_layer.h55
-rw-r--r--lib/lvgl/src/draw/sdl/lv_draw_sdl_line.c157
-rw-r--r--lib/lvgl/src/draw/sdl/lv_draw_sdl_mask.c84
-rw-r--r--lib/lvgl/src/draw/sdl/lv_draw_sdl_mask.h51
-rw-r--r--lib/lvgl/src/draw/sdl/lv_draw_sdl_polygon.c133
-rw-r--r--lib/lvgl/src/draw/sdl/lv_draw_sdl_priv.h72
-rw-r--r--lib/lvgl/src/draw/sdl/lv_draw_sdl_rect.c712
-rw-r--r--lib/lvgl/src/draw/sdl/lv_draw_sdl_rect.h75
-rw-r--r--lib/lvgl/src/draw/sdl/lv_draw_sdl_stack_blur.c249
-rw-r--r--lib/lvgl/src/draw/sdl/lv_draw_sdl_stack_blur.h46
-rw-r--r--lib/lvgl/src/draw/sdl/lv_draw_sdl_texture_cache.c178
-rw-r--r--lib/lvgl/src/draw/sdl/lv_draw_sdl_texture_cache.h102
-rw-r--r--lib/lvgl/src/draw/sdl/lv_draw_sdl_utils.c183
-rw-r--r--lib/lvgl/src/draw/sdl/lv_draw_sdl_utils.h65
-rw-r--r--lib/lvgl/src/draw/stm32_dma2d/lv_draw_stm32_dma2d.mk6
-rw-r--r--lib/lvgl/src/draw/stm32_dma2d/lv_gpu_stm32_dma2d.c265
-rw-r--r--lib/lvgl/src/draw/stm32_dma2d/lv_gpu_stm32_dma2d.h70
-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
-rw-r--r--lib/lvgl/src/draw/swm341_dma2d/lv_draw_swm341_dma2d.mk6
-rw-r--r--lib/lvgl/src/draw/swm341_dma2d/lv_gpu_swm341_dma2d.c241
-rw-r--r--lib/lvgl/src/draw/swm341_dma2d/lv_gpu_swm341_dma2d.h64
99 files changed, 22319 insertions, 0 deletions
diff --git a/lib/lvgl b/lib/lvgl
deleted file mode 160000
-Subproject 0732400e7b564dd0e7dc4a924619d8e19c5b23a
diff --git a/lib/lvgl/src/draw/arm2d/lv_draw_arm2d.mk b/lib/lvgl/src/draw/arm2d/lv_draw_arm2d.mk
new file mode 100644
index 00000000..17219b07
--- /dev/null
+++ b/lib/lvgl/src/draw/arm2d/lv_draw_arm2d.mk
@@ -0,0 +1,6 @@
+CSRCS += lv_gpu_arm2d.c
+
+DEPPATH += --dep-path $(LVGL_DIR)/$(LVGL_DIR_NAME)/src/draw/arm2d
+VPATH += :$(LVGL_DIR)/$(LVGL_DIR_NAME)/src/draw/arm2d
+
+CFLAGS += "-I$(LVGL_DIR)/$(LVGL_DIR_NAME)/src/draw/arm2d"
diff --git a/lib/lvgl/src/draw/arm2d/lv_gpu_arm2d.c b/lib/lvgl/src/draw/arm2d/lv_gpu_arm2d.c
new file mode 100644
index 00000000..7777fe21
--- /dev/null
+++ b/lib/lvgl/src/draw/arm2d/lv_gpu_arm2d.c
@@ -0,0 +1,1376 @@
+/**
+ * @file lv_gpu_arm2d.c
+ *
+ */
+
+/*********************
+ * INCLUDES
+ *********************/
+#if defined(__clang__)
+ #pragma clang diagnostic ignored "-Wunknown-warning-option"
+ #pragma clang diagnostic ignored "-Wreserved-identifier"
+ #pragma clang diagnostic ignored "-Wincompatible-pointer-types-discards-qualifiers"
+ #pragma clang diagnostic ignored "-Wmissing-variable-declarations"
+ #pragma clang diagnostic ignored "-Wcast-qual"
+ #pragma clang diagnostic ignored "-Wcast-align"
+ #pragma clang diagnostic ignored "-Wextra-semi-stmt"
+ #pragma clang diagnostic ignored "-Wsign-conversion"
+ #pragma clang diagnostic ignored "-Wunused-function"
+ #pragma clang diagnostic ignored "-Wimplicit-int-float-conversion"
+ #pragma clang diagnostic ignored "-Wdouble-promotion"
+ #pragma clang diagnostic ignored "-Wunused-parameter"
+ #pragma clang diagnostic ignored "-Wimplicit-float-conversion"
+ #pragma clang diagnostic ignored "-Wimplicit-int-conversion"
+ #pragma clang diagnostic ignored "-Wtautological-pointer-compare"
+ #pragma clang diagnostic ignored "-Wsign-compare"
+ #pragma clang diagnostic ignored "-Wfloat-conversion"
+ #pragma clang diagnostic ignored "-Wmissing-prototypes"
+ #pragma clang diagnostic ignored "-Wpadded"
+ #pragma clang diagnostic ignored "-Wundef"
+ #pragma clang diagnostic ignored "-Wdeclaration-after-statement"
+ #pragma clang diagnostic ignored "-Wdisabled-macro-expansion"
+ #pragma clang diagnostic ignored "-Wunused-variable"
+ #pragma clang diagnostic ignored "-Wunused-but-set-variable"
+ #pragma clang diagnostic ignored "-Wint-conversion"
+#endif
+
+
+#include "lv_gpu_arm2d.h"
+#include "../../core/lv_refr.h"
+
+#if LV_USE_GPU_ARM2D
+#include "arm_2d.h"
+#include "__arm_2d_impl.h"
+
+
+#if defined(__IS_COMPILER_ARM_COMPILER_5__)
+ #pragma diag_suppress 174,177,188,68,513,144,1296
+#elif defined(__IS_COMPILER_IAR__)
+ #pragma diag_suppress=Pa093
+#elif defined(__IS_COMPILER_GCC__)
+ #pragma GCC diagnostic ignored "-Wdiscarded-qualifiers"
+#endif
+
+/*********************
+ * DEFINES
+ *********************/
+#if ( !defined(__ARM_2D_CFG_SUPPORT_COLOUR_CHANNEL_ACCESS__) \
+ || !__ARM_2D_CFG_SUPPORT_COLOUR_CHANNEL_ACCESS__) \
+&& LV_COLOR_DEPTH == 32 \
+&& !defined(__ARM_2D_LVGL_CFG_NO_WARNING__)
+#warning Please set macro __ARM_2D_CFG_SUPPORT_COLOUR_CHANNEL_ACCESS__ to 1 to get more acceleration opportunities. Or you can define macro __ARM_2D_LVGL_CFG_NO_WARNING__ to suppress this warning.
+#endif
+
+#define MAX_BUF_SIZE (uint32_t) lv_disp_get_hor_res(_lv_refr_get_disp_refreshing())
+
+#if LV_COLOR_DEPTH == 16
+#define arm_2d_fill_colour arm_2d_rgb16_fill_colour
+#define arm_2d_fill_colour_with_alpha arm_2d_rgb565_fill_colour_with_alpha
+#define arm_2d_fill_colour_with_mask arm_2d_rgb565_fill_colour_with_mask
+#define arm_2d_fill_colour_with_mask_and_opacity \
+ arm_2d_rgb565_fill_colour_with_mask_and_opacity
+#define arm_2d_tile_copy arm_2d_rgb16_tile_copy
+#define arm_2d_alpha_blending arm_2d_rgb565_alpha_blending
+#define arm_2d_tile_copy_with_src_mask arm_2d_rgb565_tile_copy_with_src_mask
+#define arm_2d_color_t arm_2d_color_rgb565_t
+
+/* arm-2d direct mode apis */
+#define __arm_2d_impl_colour_filling __arm_2d_impl_rgb16_colour_filling
+#define __arm_2d_impl_colour_filling_with_opacity \
+ __arm_2d_impl_rgb565_colour_filling_with_opacity
+#define __arm_2d_impl_colour_filling_mask \
+ __arm_2d_impl_rgb565_colour_filling_mask
+#define __arm_2d_impl_colour_filling_mask_opacity \
+ __arm_2d_impl_rgb565_colour_filling_mask_opacity
+#define __arm_2d_impl_copy __arm_2d_impl_rgb16_copy
+#define __arm_2d_impl_alpha_blending __arm_2d_impl_rgb565_alpha_blending
+#define __arm_2d_impl_src_msk_copy __arm_2d_impl_rgb565_src_msk_copy
+#define __arm_2d_impl_src_chn_msk_copy __arm_2d_impl_rgb565_src_chn_msk_copy
+#define __arm_2d_impl_cl_key_copy __arm_2d_impl_rgb16_cl_key_copy
+#define __arm_2d_impl_alpha_blending_colour_keying \
+ __arm_2d_impl_rgb565_alpha_blending_colour_keying
+#define arm_2d_tile_transform_with_src_mask_and_opacity \
+ arm_2d_rgb565_tile_transform_with_src_mask_and_opacity
+#define arm_2d_tile_transform_with_opacity \
+ arm_2d_rgb565_tile_transform_with_opacity
+
+#define __ARM_2D_PIXEL_BLENDING_OPA __ARM_2D_PIXEL_BLENDING_OPA_RGB565
+
+#define color_int uint16_t
+
+#elif LV_COLOR_DEPTH == 32
+#define arm_2d_fill_colour arm_2d_rgb32_fill_colour
+#define arm_2d_fill_colour_with_alpha arm_2d_cccn888_fill_colour_with_alpha
+#define arm_2d_fill_colour_with_mask arm_2d_cccn888_fill_colour_with_mask
+#define arm_2d_fill_colour_with_mask_and_opacity \
+ arm_2d_cccn888_fill_colour_with_mask_and_opacity
+#define arm_2d_tile_copy arm_2d_rgb32_tile_copy
+#define arm_2d_alpha_blending arm_2d_cccn888_alpha_blending
+#define arm_2d_tile_copy_with_src_mask arm_2d_cccn888_tile_copy_with_src_mask
+#define arm_2d_color_t arm_2d_color_cccn888_t
+
+/* arm-2d direct mode apis */
+#define __arm_2d_impl_colour_filling __arm_2d_impl_rgb32_colour_filling
+#define __arm_2d_impl_colour_filling_with_opacity \
+ __arm_2d_impl_cccn888_colour_filling_with_opacity
+#define __arm_2d_impl_colour_filling_mask \
+ __arm_2d_impl_cccn888_colour_filling_mask
+#define __arm_2d_impl_colour_filling_mask_opacity \
+ __arm_2d_impl_cccn888_colour_filling_mask_opacity
+#define __arm_2d_impl_copy __arm_2d_impl_rgb32_copy
+#define __arm_2d_impl_alpha_blending __arm_2d_impl_cccn888_alpha_blending
+#define __arm_2d_impl_src_msk_copy __arm_2d_impl_cccn888_src_msk_copy
+#define __arm_2d_impl_src_chn_msk_copy __arm_2d_impl_cccn888_src_chn_msk_copy
+#define __arm_2d_impl_cl_key_copy __arm_2d_impl_rgb32_cl_key_copy
+#define __arm_2d_impl_alpha_blending_colour_keying \
+ __arm_2d_impl_cccn888_alpha_blending_colour_keying
+#define arm_2d_tile_transform_with_src_mask_and_opacity \
+ arm_2d_cccn888_tile_transform_with_src_mask_and_opacity
+#define arm_2d_tile_transform_with_opacity \
+ arm_2d_cccn888_tile_transform_with_opacity
+
+#define __ARM_2D_PIXEL_BLENDING_OPA __ARM_2D_PIXEL_BLENDING_OPA_CCCN888
+
+#define color_int uint32_t
+
+#else
+#error The specified LV_COLOR_DEPTH is not supported by this version of lv_gpu_arm2d.c.
+#endif
+
+/* *INDENT-OFF* */
+#define __PREPARE_LL_ACCELERATION__() \
+ int32_t src_stride = lv_area_get_width(coords); \
+ \
+ uint8_t px_size_byte = cf == LV_IMG_CF_TRUE_COLOR_ALPHA \
+ ? LV_IMG_PX_SIZE_ALPHA_BYTE \
+ : sizeof(lv_color_t); \
+ \
+ const uint8_t * src_buf_tmp = src_buf; \
+ src_buf_tmp += src_stride \
+ * (draw_area.y1 - coords->y1) \
+ * px_size_byte; \
+ src_buf_tmp += (draw_area.x1 - coords->x1) * px_size_byte; \
+ \
+ lv_area_t blend_area2; \
+ if(!_lv_area_intersect(&blend_area2, \
+ &draw_area, \
+ draw_ctx->clip_area)) return; \
+ \
+ int32_t w = lv_area_get_width(&blend_area2); \
+ int32_t h = lv_area_get_height(&blend_area2); \
+ \
+ lv_coord_t dest_stride = lv_area_get_width(draw_ctx->buf_area); \
+ \
+ lv_color_t * dest_buf = draw_ctx->buf; \
+ dest_buf += dest_stride * (blend_area2.y1 - draw_ctx->buf_area->y1) \
+ + (blend_area2.x1 - draw_ctx->buf_area->x1); \
+ \
+ arm_2d_size_t copy_size = { \
+ .iWidth = lv_area_get_width(&blend_area2), \
+ .iHeight = lv_area_get_height(&blend_area2), \
+ }
+
+#define __PREPARE_TARGET_TILE__(__blend_area) \
+ static arm_2d_tile_t target_tile; \
+ static arm_2d_region_t target_region; \
+ \
+ lv_color_t * dest_buf = draw_ctx->buf; \
+ \
+ target_tile = (arm_2d_tile_t) { \
+ .tRegion = { \
+ .tSize = { \
+ .iWidth = lv_area_get_width(draw_ctx->buf_area), \
+ .iHeight = lv_area_get_height(draw_ctx->buf_area), \
+ }, \
+ }, \
+ .tInfo.bIsRoot = true, \
+ .phwBuffer = (uint16_t *)draw_ctx->buf, \
+ }; \
+ \
+ target_region = (arm_2d_region_t) { \
+ .tLocation = { \
+ .iX = (__blend_area).x1 - draw_ctx->buf_area->x1, \
+ .iY = (__blend_area).y1 - draw_ctx->buf_area->y1, \
+ }, \
+ .tSize = { \
+ .iWidth = lv_area_get_width(&(__blend_area)), \
+ .iHeight = lv_area_get_height(&(__blend_area)), \
+ }, \
+ }
+
+#define __PREPARE_SOURCE_TILE__(__dsc, __blend_area) \
+ static arm_2d_tile_t source_tile_orig; \
+ static arm_2d_tile_t source_tile; \
+ const lv_color_t * src_buf = (__dsc)->src_buf; \
+ if (src_buf) { \
+ source_tile_orig = (arm_2d_tile_t) { \
+ .tRegion = { \
+ .tSize = { \
+ .iWidth = lv_area_get_width((__dsc)->blend_area), \
+ .iHeight = lv_area_get_height((__dsc)->blend_area), \
+ }, \
+ }, \
+ .tInfo.bIsRoot = true, \
+ .phwBuffer = (uint16_t *)src_buf, \
+ }; \
+ \
+ arm_2d_tile_generate_child( \
+ &source_tile_orig, \
+ (arm_2d_region_t []) { \
+ { \
+ .tLocation = { \
+ .iX = (__blend_area).x1 - (__dsc)->blend_area->x1, \
+ .iY = (__blend_area).y1 - (__dsc)->blend_area->y1, \
+ }, \
+ .tSize = source_tile_orig.tRegion.tSize, \
+ } \
+ }, \
+ &source_tile, \
+ false); \
+ source_tile.tInfo.bDerivedResource = true; \
+ }
+
+#define __PREPARE_MASK_TILE__(__dsc, __blend_area, __mask, __is_chn) \
+ static arm_2d_tile_t mask_tile_orig; \
+ static arm_2d_tile_t mask_tile; \
+ if(NULL != (__mask)) { \
+ mask_tile_orig = (arm_2d_tile_t) { \
+ .tRegion = { \
+ .tSize = { \
+ .iWidth = lv_area_get_width((__dsc)->mask_area), \
+ .iHeight = lv_area_get_height((__dsc)->mask_area), \
+ }, \
+ }, \
+ .tInfo = { \
+ .bIsRoot = true, \
+ .bHasEnforcedColour = true, \
+ .tColourInfo = { \
+ .chScheme = (__is_chn) ? ARM_2D_CHANNEL_8in32 \
+ : ARM_2D_COLOUR_8BIT, \
+ }, \
+ }, \
+ .pchBuffer = ((uint8_t *)(__mask)) + (__is_chn) ? 3 : 0, \
+ }; \
+ \
+ arm_2d_tile_generate_child( \
+ &mask_tile_orig, \
+ (arm_2d_region_t []) { \
+ { \
+ .tLocation = { \
+ .iX = (__dsc)->mask_area->x1 - (__blend_area).x1, \
+ .iY = (__dsc)->mask_area->y1 - (__blend_area).y1, \
+ }, \
+ .tSize = mask_tile_orig.tRegion.tSize, \
+ } \
+ }, \
+ &mask_tile, \
+ false); \
+ mask_tile.tInfo.bDerivedResource = true; \
+ }
+/* *INDENT-ON* */
+
+/* *INDENT-OFF* */
+#define __RECOLOUR_WRAPPER(...) \
+ do { \
+ lv_color_t *rgb_tmp_buf = NULL; \
+ if(draw_dsc->recolor_opa > LV_OPA_MIN) { \
+ rgb_tmp_buf \
+ = lv_mem_buf_get(src_w * src_h * sizeof(lv_color_t)); \
+ if (NULL == rgb_tmp_buf) { \
+ LV_LOG_WARN( \
+ "Failed to allocate memory for accelerating recolour, " \
+ "use normal route instead."); \
+ break; \
+ } \
+ lv_memcpy(rgb_tmp_buf, src_buf, src_w * src_h * sizeof(lv_color_t));\
+ arm_2d_size_t copy_size = { \
+ .iWidth = src_w, \
+ .iHeight = src_h, \
+ }; \
+ /* apply re-colour */ \
+ __arm_2d_impl_colour_filling_with_opacity( \
+ (color_int *)rgb_tmp_buf, \
+ src_w, \
+ &copy_size, \
+ (color_int)draw_dsc->recolor.full, \
+ draw_dsc->recolor_opa); \
+ \
+ /* replace src_buf for the following operation */ \
+ src_buf = (const uint8_t *)rgb_tmp_buf; \
+ } \
+ __VA_ARGS__ \
+ if (NULL != rgb_tmp_buf) { \
+ lv_mem_buf_release(rgb_tmp_buf); \
+ } \
+ } while(0);
+/* *INDENT-ON* */
+
+/**********************
+ * TYPEDEFS
+ **********************/
+
+/**********************
+ * STATIC PROTOTYPES
+ **********************/
+
+#if __ARM_2D_HAS_HW_ACC__
+LV_ATTRIBUTE_FAST_MEM
+static bool lv_draw_arm2d_fill_colour(const arm_2d_tile_t * target_tile,
+ const arm_2d_region_t * region,
+ lv_color_t color,
+ lv_opa_t opa,
+ const arm_2d_tile_t * mask_tile);
+
+LV_ATTRIBUTE_FAST_MEM
+static bool lv_draw_arm2d_tile_copy(const arm_2d_tile_t * target_tile,
+ const arm_2d_region_t * region,
+ arm_2d_tile_t * source_tile,
+ lv_opa_t opa,
+ arm_2d_tile_t * mask_tile);
+#else
+
+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_ATTRIBUTE_FAST_MEM
+static bool arm_2d_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);
+
+LV_ATTRIBUTE_FAST_MEM
+static bool arm_2d_copy_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);
+#endif
+
+LV_ATTRIBUTE_FAST_MEM
+static void lv_draw_arm2d_blend(lv_draw_ctx_t * draw_ctx, const lv_draw_sw_blend_dsc_t * dsc);
+LV_ATTRIBUTE_FAST_MEM
+static void lv_gpu_arm2d_wait_cb(lv_draw_ctx_t * draw_ctx);
+LV_ATTRIBUTE_FAST_MEM
+static void lv_draw_arm2d_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);
+
+/**********************
+ * STATIC VARIABLES
+ **********************/
+
+/**********************
+ * MACROS
+ **********************/
+
+/**********************
+ * GLOBAL FUNCTIONS
+ **********************/
+
+void lv_draw_arm2d_ctx_init(lv_disp_drv_t * drv, lv_draw_ctx_t * draw_ctx)
+{
+ arm_2d_init();
+
+ lv_draw_sw_init_ctx(drv, draw_ctx);
+
+ lv_draw_arm2d_ctx_t * arm2d_draw_ctx = (lv_draw_sw_ctx_t *)draw_ctx;
+
+ arm2d_draw_ctx->blend = lv_draw_arm2d_blend;
+ arm2d_draw_ctx->base_draw.wait_for_finish = lv_gpu_arm2d_wait_cb;
+
+#if !__ARM_2D_HAS_HW_ACC__
+ arm2d_draw_ctx->base_draw.draw_img_decoded = lv_draw_arm2d_img_decoded;
+#endif
+
+}
+
+void lv_draw_arm2d_ctx_deinit(lv_disp_drv_t * drv, lv_draw_ctx_t * draw_ctx)
+{
+ LV_UNUSED(drv);
+ LV_UNUSED(draw_ctx);
+}
+
+extern void test_flush(lv_color_t * color_p);
+
+#if __ARM_2D_HAS_HW_ACC__
+LV_ATTRIBUTE_FAST_MEM
+static void lv_draw_arm2d_blend(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_area_t blend_area;
+ if(!_lv_area_intersect(&blend_area, dsc->blend_area, draw_ctx->clip_area)) {
+ return;
+ }
+
+ bool is_accelerated = false;
+
+ if(dsc->blend_mode == LV_BLEND_MODE_NORMAL
+ && lv_area_get_size(&blend_area) > 100) {
+
+ __PREPARE_TARGET_TILE__(blend_area);
+ __PREPARE_SOURCE_TILE__(dsc, blend_area);
+ __PREPARE_MASK_TILE__(dsc, blend_area, mask, false);
+
+ if(src_buf) {
+ is_accelerated = lv_draw_arm2d_tile_copy(
+ &target_tile,
+ &target_region,
+ &source_tile,
+ dsc->opa,
+ (NULL == mask) ? NULL : &mask_tile);
+ }
+ else {
+ is_accelerated = lv_draw_arm2d_fill_colour(
+ &target_tile,
+ &target_region,
+ dsc->color,
+ dsc->opa,
+ (NULL == mask) ? NULL : &mask_tile);
+ }
+ }
+
+ if(!is_accelerated) {
+ lv_draw_sw_blend_basic(draw_ctx, dsc);
+ }
+}
+
+
+LV_ATTRIBUTE_FAST_MEM
+static bool lv_draw_arm2d_fill_colour(const arm_2d_tile_t * target_tile,
+ const arm_2d_region_t * region,
+ lv_color_t color,
+ lv_opa_t opa,
+ const arm_2d_tile_t * mask_tile)
+{
+ arm_fsm_rt_t result = (arm_fsm_rt_t)ARM_2D_ERR_NONE;
+
+ if(NULL == mask_tile) {
+ if(opa >= LV_OPA_MAX) {
+ result = arm_2d_fill_colour(target_tile, region, color.full);
+ }
+ else {
+#if LV_COLOR_SCREEN_TRANSP
+ return false;
+#else
+ result = arm_2d_fill_colour_with_alpha(
+ target_tile,
+ region,
+ (arm_2d_color_t) {
+ color.full
+ },
+ opa);
+#endif
+ }
+ }
+ else {
+
+ if(opa >= LV_OPA_MAX) {
+ result = arm_2d_fill_colour_with_mask(
+ target_tile,
+ region,
+ mask_tile,
+ (arm_2d_color_t) {
+ color.full
+ });
+ }
+ else {
+#if LV_COLOR_SCREEN_TRANSP
+ return false;
+#else
+ result = arm_2d_fill_colour_with_mask_and_opacity(
+ target_tile,
+ region,
+ mask_tile,
+ (arm_2d_color_t) {
+ color.full
+ },
+ opa);
+#endif
+ }
+ }
+
+ if(result < 0) {
+ /* error detected */
+ return false;
+ }
+
+ return true;
+
+}
+
+LV_ATTRIBUTE_FAST_MEM
+static bool lv_draw_arm2d_tile_copy(const arm_2d_tile_t * target_tile,
+ const arm_2d_region_t * region,
+ arm_2d_tile_t * source_tile,
+ lv_opa_t opa,
+ arm_2d_tile_t * mask_tile)
+{
+ arm_fsm_rt_t result = (arm_fsm_rt_t)ARM_2D_ERR_NONE;
+
+ if(NULL == mask_tile) {
+ if(opa >= LV_OPA_MAX) {
+ result = arm_2d_tile_copy(source_tile,
+ target_tile,
+ region,
+ ARM_2D_CP_MODE_COPY);
+ }
+#if LV_COLOR_SCREEN_TRANSP
+ else {
+ return false; /* not supported */
+ }
+#else
+ else {
+ result = arm_2d_alpha_blending(source_tile,
+ target_tile,
+ region,
+ opa);
+ }
+#endif
+ }
+ else {
+#if LV_COLOR_SCREEN_TRANSP
+ return false; /* not support */
+#else
+
+ if(opa >= LV_OPA_MAX) {
+ result = arm_2d_tile_copy_with_src_mask(source_tile,
+ mask_tile,
+ target_tile,
+ region,
+ ARM_2D_CP_MODE_COPY);
+ }
+ else {
+ return false;
+ }
+#endif
+ }
+
+ if(result < 0) {
+ /* error detected */
+ return false;
+ }
+
+ return true;
+}
+
+static void lv_gpu_arm2d_wait_cb(lv_draw_ctx_t * draw_ctx)
+{
+ lv_disp_t * disp = _lv_refr_get_disp_refreshing();
+
+ arm_2d_op_wait_async(NULL);
+ if(disp->driver && disp->driver->wait_cb) {
+ disp->driver->wait_cb(disp->driver);
+ }
+ lv_draw_sw_wait_for_finish(draw_ctx);
+}
+#else
+
+
+LV_ATTRIBUTE_FAST_MEM
+static void lv_draw_arm2d_blend(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();
+
+ bool is_accelerated = false;
+ do {
+ if(NULL != disp->driver->set_px_cb) {
+ break;
+ }
+
+ lv_color_t * dest_buf = draw_ctx->buf;
+ dest_buf += dest_stride * (blend_area.y1 - draw_ctx->buf_area->y1)
+ + (blend_area.x1 - draw_ctx->buf_area->x1);
+
+ 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(dsc->src_buf == NULL) {
+ if(dsc->blend_mode == LV_BLEND_MODE_NORMAL) {
+ is_accelerated = arm_2d_fill_normal(dest_buf,
+ &blend_area,
+ dest_stride,
+ dsc->color,
+ dsc->opa,
+ mask,
+ mask_stride);
+ }
+#if LV_DRAW_COMPLEX
+ else {
+ break;
+ }
+#endif
+ }
+ else {
+
+ if(dsc->blend_mode == LV_BLEND_MODE_NORMAL) {
+ is_accelerated = arm_2d_copy_normal(dest_buf,
+ &blend_area,
+ dest_stride,
+ src_buf,
+ src_stride,
+ dsc->opa,
+ mask,
+ mask_stride);
+ }
+#if LV_DRAW_COMPLEX
+ else {
+ break;
+ }
+#endif
+ }
+ } while(0);
+
+ if(!is_accelerated) lv_draw_sw_blend_basic(draw_ctx, dsc);
+}
+
+LV_ATTRIBUTE_FAST_MEM
+static bool arm_2d_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)
+{
+ arm_2d_size_t target_size = {
+ .iWidth = lv_area_get_width(dest_area),
+ .iHeight = lv_area_get_height(dest_area),
+ };
+
+ /*No mask*/
+ if(mask == NULL) {
+ if(opa >= LV_OPA_MAX) {
+ __arm_2d_impl_colour_filling((color_int *)dest_buf,
+ dest_stride,
+ &target_size,
+ color.full);
+ }
+ /*Has opacity*/
+ else {
+#if LV_COLOR_SCREEN_TRANSP
+ return false;
+#else
+ __arm_2d_impl_colour_filling_with_opacity((color_int *)dest_buf,
+ dest_stride,
+ &target_size,
+ color.full,
+ opa);
+#endif
+ }
+ }
+ /*Masked*/
+ else {
+ /*Only the mask matters*/
+ if(opa >= LV_OPA_MAX) {
+ __arm_2d_impl_colour_filling_mask((color_int *)dest_buf,
+ dest_stride,
+ (uint8_t *)mask,
+ mask_stride,
+ &target_size,
+ color.full);
+ }
+ /*With opacity*/
+ else {
+#if LV_COLOR_SCREEN_TRANSP
+ return false;
+#else
+ __arm_2d_impl_colour_filling_mask_opacity((color_int *)dest_buf,
+ dest_stride,
+ (uint8_t *)mask,
+ mask_stride,
+ &target_size,
+ color.full,
+ opa);
+#endif
+ }
+ }
+
+ return true;
+}
+
+
+LV_ATTRIBUTE_FAST_MEM
+static bool arm_2d_copy_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);
+
+ arm_2d_size_t copy_size = {
+ .iWidth = lv_area_get_width(dest_area),
+ .iHeight = lv_area_get_height(dest_area),
+ };
+
+#if LV_COLOR_SCREEN_TRANSP
+ lv_disp_t * disp = _lv_refr_get_disp_refreshing();
+#endif
+
+ /*Simple fill (maybe with opacity), no masking*/
+ if(mask == NULL) {
+ if(opa >= LV_OPA_MAX) {
+ __arm_2d_impl_copy((color_int *)src_buf,
+ src_stride,
+ (color_int *)dest_buf,
+ dest_stride,
+ &copy_size);
+ }
+ else {
+#if LV_COLOR_SCREEN_TRANSP
+ return false;
+#else
+ __arm_2d_impl_alpha_blending((color_int *)src_buf,
+ src_stride,
+ (color_int *)dest_buf,
+ dest_stride,
+ &copy_size,
+ opa);
+#endif
+ }
+ }
+ /*Masked*/
+ else {
+ /*Only the mask matters*/
+ if(opa > LV_OPA_MAX) {
+#if LV_COLOR_SCREEN_TRANSP
+ return false;
+#else
+ __arm_2d_impl_src_msk_copy((color_int *)src_buf,
+ src_stride,
+ (uint8_t *)mask,
+ mask_stride,
+ &copy_size,
+ (color_int *)dest_buf,
+ dest_stride,
+ &copy_size);
+#endif
+ }
+ /*Handle opa and mask values too*/
+ else {
+#if LV_COLOR_SCREEN_TRANSP
+ return false;
+#else
+ __arm_2d_impl_gray8_alpha_blending((uint8_t *)mask,
+ mask_stride,
+ (uint8_t *)mask,
+ mask_stride,
+ &copy_size,
+ opa);
+
+ __arm_2d_impl_src_msk_copy((color_int *)src_buf,
+ src_stride,
+ (uint8_t *)mask,
+ mask_stride,
+ &copy_size,
+ (color_int *)dest_buf,
+ dest_stride,
+ &copy_size);
+#endif
+ }
+ }
+
+ return true;
+}
+
+LV_ATTRIBUTE_FAST_MEM
+static void lv_draw_arm2d_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) {
+ 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;
+
+ bool is_accelerated = false;
+
+ if(!transform) {
+ if(LV_IMG_CF_TRUE_COLOR_CHROMA_KEYED == cf) {
+ /* copy with colour keying */
+
+ /* *INDENT-OFF* */
+ __RECOLOUR_WRAPPER(
+
+ lv_color_t chrome_key = LV_COLOR_CHROMA_KEY;
+ /* calculate new chrome-key colour */
+ if(draw_dsc->recolor_opa > LV_OPA_MIN) {
+ __ARM_2D_PIXEL_BLENDING_OPA(
+ (color_int *) & (draw_dsc->recolor.full),
+ (color_int *) & (chrome_key.full),
+ draw_dsc->recolor_opa
+ );
+ }
+
+ __PREPARE_LL_ACCELERATION__();
+
+ if(blend_dsc.opa >= LV_OPA_MAX) {
+ __arm_2d_impl_cl_key_copy(
+ (color_int *)src_buf_tmp,
+ src_stride,
+ (color_int *)dest_buf,
+ dest_stride,
+ &copy_size,
+ (color_int)chrome_key.full);
+ }
+ else {
+ __arm_2d_impl_alpha_blending_colour_keying(
+ (color_int *)src_buf_tmp,
+ src_stride,
+ (color_int *)dest_buf,
+ dest_stride,
+ &copy_size,
+ blend_dsc.opa,
+ (color_int)chrome_key.full);
+ }
+ is_accelerated = true;
+ )
+ /* *INDENT-ON* */
+ }
+ else if((LV_COLOR_DEPTH == 32)
+ && !mask_any
+ && (cf == LV_IMG_CF_TRUE_COLOR_ALPHA)) {
+ /* accelerate copy-with-source-masks-and-opacity */
+
+ /* *INDENT-OFF* */
+ __RECOLOUR_WRAPPER(
+ __PREPARE_LL_ACCELERATION__();
+
+ uint8_t * mask_temp_buf = NULL;
+ if(blend_dsc.opa < LV_OPA_MAX) {
+ mask_temp_buf = lv_mem_buf_get(copy_size.iHeight * copy_size.iWidth);
+ if(NULL == mask_temp_buf) {
+ LV_LOG_WARN(
+ "Failed to allocate memory for alpha mask,"
+ " use normal route instead.");
+ break;
+ }
+ lv_memset_00(mask_temp_buf, copy_size.iHeight * copy_size.iWidth);
+
+ __arm_2d_impl_gray8_colour_filling_channel_mask_opacity(
+ mask_temp_buf,
+ src_stride,
+ (uint32_t *)
+ ((uintptr_t)src_buf_tmp + LV_IMG_PX_SIZE_ALPHA_BYTE - 1),
+ src_stride,
+ &copy_size,
+ 0xFF,
+ blend_dsc.opa);
+
+ __arm_2d_impl_src_msk_copy(
+ (color_int *)src_buf_tmp,
+ src_stride,
+ mask_temp_buf,
+ src_stride,
+ &copy_size,
+ (color_int *)dest_buf,
+ dest_stride,
+ &copy_size);
+
+ lv_mem_buf_release(mask_temp_buf);
+ }
+ else {
+ __arm_2d_impl_src_chn_msk_copy(
+ (color_int *)src_buf_tmp,
+ src_stride,
+ (uint32_t *)
+ ((uintptr_t)src_buf_tmp + LV_IMG_PX_SIZE_ALPHA_BYTE - 1),
+ src_stride,
+ &copy_size,
+ (color_int *)dest_buf,
+ dest_stride,
+ &copy_size);
+ }
+
+ is_accelerated = true;
+ )
+ /* *INDENT-ON* */
+ }
+ else if(!mask_any && (cf == LV_IMG_CF_TRUE_COLOR)) {
+ /* accelerate copy-with-source-masks-and-opacity */
+
+ /* *INDENT-OFF* */
+ __RECOLOUR_WRAPPER(
+ __PREPARE_LL_ACCELERATION__();
+
+ if(blend_dsc.opa >= LV_OPA_MAX) {
+ __arm_2d_impl_copy(
+ (color_int *)src_buf_tmp,
+ src_stride,
+ (color_int *)dest_buf,
+ dest_stride,
+ &copy_size);
+ }
+ else {
+ __arm_2d_impl_alpha_blending(
+ (color_int *)src_buf_tmp,
+ src_stride,
+ (color_int *)dest_buf,
+ dest_stride,
+ &copy_size,
+ blend_dsc.opa);
+ }
+ is_accelerated = true;
+ )
+ /* *INDENT-ON* */
+ }
+ }
+ else if(!mask_any
+#if defined(__ARM_2D_HAS_ANTI_ALIAS_TRANSFORM__) && __ARM_2D_HAS_ANTI_ALIAS_TRANSFORM__
+ && (draw_dsc->antialias == 1)
+#else
+ && (draw_dsc->antialias == 0)
+#endif
+ && (draw_dsc->recolor_opa == LV_OPA_TRANSP)
+ && (((LV_IMG_CF_TRUE_COLOR_CHROMA_KEYED == cf)
+ || (LV_IMG_CF_TRUE_COLOR == cf))
+#if defined(__ARM_2D_CFG_SUPPORT_COLOUR_CHANNEL_ACCESS__) && __ARM_2D_CFG_SUPPORT_COLOUR_CHANNEL_ACCESS__
+ || ((LV_IMG_CF_TRUE_COLOR_ALPHA == cf)
+ && (LV_COLOR_DEPTH == 32))
+#endif
+ )
+ ) {
+
+ /* *INDENT-OFF* */
+ __RECOLOUR_WRAPPER(
+ /* accelerate transform without re-color */
+
+ static arm_2d_tile_t target_tile_origin;
+ static arm_2d_tile_t target_tile;
+ arm_2d_region_t clip_region;
+ static arm_2d_region_t target_region;
+
+ lv_color_t * dest_buf = draw_ctx->buf;
+
+ target_tile_origin = (arm_2d_tile_t) {
+ .tRegion = {
+ .tSize = {
+ .iWidth = lv_area_get_width(draw_ctx->buf_area),
+ .iHeight = lv_area_get_height(draw_ctx->buf_area),
+ },
+ },
+ .tInfo.bIsRoot = true,
+ .phwBuffer = (uint16_t *)draw_ctx->buf,
+ };
+
+ clip_region = (arm_2d_region_t) {
+ .tLocation = {
+ .iX = draw_ctx->clip_area->x1 - draw_ctx->buf_area->x1,
+ .iY = draw_ctx->clip_area->y1 - draw_ctx->buf_area->y1,
+ },
+ .tSize = {
+ .iWidth = lv_area_get_width(draw_ctx->clip_area),
+ .iHeight = lv_area_get_height(draw_ctx->clip_area),
+ },
+ };
+
+ arm_2d_tile_generate_child(&target_tile_origin,
+ &clip_region,
+ &target_tile,
+ false);
+
+ target_region = (arm_2d_region_t) {
+ .tLocation = {
+ .iX = coords->x1 - draw_ctx->clip_area->x1,
+ .iY = coords->y1 - draw_ctx->clip_area->y1,
+ },
+ .tSize = {
+ .iWidth = lv_area_get_width(coords),
+ .iHeight = lv_area_get_height(coords),
+ },
+ };
+
+ static arm_2d_tile_t source_tile;
+
+ source_tile = (arm_2d_tile_t) {
+ .tRegion = {
+ .tSize = {
+ .iWidth = src_w,
+ .iHeight = src_h,
+ },
+ },
+ .tInfo.bIsRoot = true,
+ .pchBuffer = (uint8_t *)src_buf,
+ };
+
+ static arm_2d_tile_t mask_tile;
+ mask_tile = source_tile;
+
+ mask_tile.tInfo.bHasEnforcedColour = true;
+ mask_tile.tInfo.tColourInfo.chScheme = ARM_2D_CHANNEL_8in32;
+ mask_tile.pchBuffer += 3;
+
+ static arm_2d_location_t source_center, target_center;
+ source_center.iX = draw_dsc->pivot.x;
+ source_center.iY = draw_dsc->pivot.y;
+
+
+ if((LV_IMG_CF_TRUE_COLOR_CHROMA_KEYED == cf) ||
+ (LV_IMG_CF_TRUE_COLOR == cf)) {
+ arm_2d_tile_transform_with_opacity(
+ &source_tile,
+ &target_tile,
+ &target_region,
+ source_center,
+ ARM_2D_ANGLE((draw_dsc->angle / 10.0f)),
+ draw_dsc->zoom / 256.0f,
+ (color_int)LV_COLOR_CHROMA_KEY.full,
+ blend_dsc.opa);
+
+ is_accelerated = true;
+ }
+ #if defined(__ARM_2D_CFG_SUPPORT_COLOUR_CHANNEL_ACCESS__) \
+ && __ARM_2D_CFG_SUPPORT_COLOUR_CHANNEL_ACCESS__
+ else if((LV_IMG_CF_TRUE_COLOR_ALPHA == cf) &&
+ (LV_COLOR_DEPTH == 32)) {
+ arm_2d_tile_transform_with_src_mask_and_opacity(
+ &source_tile,
+ &mask_tile,
+ &target_tile,
+ &target_region,
+ source_center,
+ ARM_2D_ANGLE((draw_dsc->angle / 10.0f)),
+ draw_dsc->zoom / 256.0f,
+ blend_dsc.opa);
+
+ is_accelerated = true;
+ }
+ #endif
+ )
+ /* *INDENT-ON* */
+ }
+
+ /* *INDENT-OFF* */
+ if(!is_accelerated) 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) {
+ arm_2d_size_t copy_size = {
+ .iWidth = buf_w,
+ .iHeight = buf_h,
+ };
+
+ /* apply re-colour */
+ __arm_2d_impl_colour_filling_with_opacity(
+ (color_int *)rgb_buf,
+ buf_w,
+ &copy_size,
+ (color_int)draw_dsc->recolor.full,
+ draw_dsc->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 void lv_gpu_arm2d_wait_cb(lv_draw_ctx_t * draw_ctx)
+{
+ lv_disp_t * disp = _lv_refr_get_disp_refreshing();
+
+ arm_2d_op_wait_async(NULL);
+ if(disp->driver && disp->driver->wait_cb) {
+ disp->driver->wait_cb(disp->driver);
+ }
+ lv_draw_sw_wait_for_finish(draw_ctx);
+}
+
+
+#endif
+
+
+/**********************
+ * 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;
+ }
+ }
+}
+
+#if 0
+static void invalidate_cache(void)
+{
+ lv_disp_t * disp = _lv_refr_get_disp_refreshing();
+ if(disp->driver->clean_dcache_cb) disp->driver->clean_dcache_cb(disp->driver);
+ else {
+#if __CORTEX_M >= 0x07
+ if((SCB->CCR) & (uint32_t)SCB_CCR_DC_Msk)
+ SCB_CleanInvalidateDCache();
+#endif
+ }
+}
+#endif
+
+#endif
diff --git a/lib/lvgl/src/draw/arm2d/lv_gpu_arm2d.h b/lib/lvgl/src/draw/arm2d/lv_gpu_arm2d.h
new file mode 100644
index 00000000..50fa5a89
--- /dev/null
+++ b/lib/lvgl/src/draw/arm2d/lv_gpu_arm2d.h
@@ -0,0 +1,51 @@
+/**
+ * @file lv_gpu_arm2d.h
+ *
+ */
+
+#ifndef LV_GPU_ARM2D_H
+#define LV_GPU_ARM2D_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*********************
+ * INCLUDES
+ *********************/
+#include "../../misc/lv_color.h"
+#include "../../hal/lv_hal_disp.h"
+#include "../sw/lv_draw_sw.h"
+
+#if LV_USE_GPU_ARM2D
+
+/*********************
+ * DEFINES
+ *********************/
+
+/**********************
+ * TYPEDEFS
+ **********************/
+typedef lv_draw_sw_ctx_t lv_draw_arm2d_ctx_t;
+
+struct _lv_disp_drv_t;
+
+/**********************
+ * GLOBAL PROTOTYPES
+ **********************/
+
+void lv_draw_arm2d_ctx_init(struct _lv_disp_drv_t * drv, lv_draw_ctx_t * draw_ctx);
+
+void lv_draw_arm2d_ctx_deinit(struct _lv_disp_drv_t * drv, lv_draw_ctx_t * draw_ctx);
+
+/**********************
+ * MACROS
+ **********************/
+
+#endif /*LV_USE_GPU_ARM2D*/
+
+#ifdef __cplusplus
+} /*extern "C"*/
+#endif
+
+#endif /*LV_GPU_ARM2D_H*/
diff --git a/lib/lvgl/src/draw/lv_draw.c b/lib/lvgl/src/draw/lv_draw.c
new file mode 100644
index 00000000..823f7076
--- /dev/null
+++ b/lib/lvgl/src/draw/lv_draw.c
@@ -0,0 +1,53 @@
+/**
+ * @file lv_draw.c
+ *
+ */
+
+/*********************
+ * INCLUDES
+ *********************/
+#include "lv_draw.h"
+#include "sw/lv_draw_sw.h"
+
+/*********************
+ * DEFINES
+ *********************/
+
+/**********************
+ * TYPEDEFS
+ **********************/
+
+/**********************
+ * STATIC PROTOTYPES
+ **********************/
+
+/**********************
+ * STATIC VARIABLES
+ **********************/
+
+/**********************
+ * STATIC VARIABLES
+ **********************/
+
+/**********************
+ * MACROS
+ **********************/
+
+/**********************
+ * GLOBAL FUNCTIONS
+ **********************/
+
+void lv_draw_init(void)
+{
+ /*Nothing to init now*/
+}
+
+void lv_draw_wait_for_finish(lv_draw_ctx_t * draw_ctx)
+{
+ if(draw_ctx->wait_for_finish) draw_ctx->wait_for_finish(draw_ctx);
+}
+
+/**********************
+ * STATIC FUNCTIONS
+ **********************/
+
diff --git a/lib/lvgl/src/draw/lv_draw.h b/lib/lvgl/src/draw/lv_draw.h
new file mode 100644
index 00000000..80b62e9f
--- /dev/null
+++ b/lib/lvgl/src/draw/lv_draw.h
@@ -0,0 +1,218 @@
+/**
+ * @file lv_draw.h
+ *
+ */
+
+#ifndef LV_DRAW_H
+#define LV_DRAW_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*********************
+ * INCLUDES
+ *********************/
+#include "../lv_conf_internal.h"
+
+#include "../misc/lv_style.h"
+#include "../misc/lv_txt.h"
+#include "lv_img_decoder.h"
+#include "lv_img_cache.h"
+
+#include "lv_draw_rect.h"
+#include "lv_draw_label.h"
+#include "lv_draw_img.h"
+#include "lv_draw_line.h"
+#include "lv_draw_triangle.h"
+#include "lv_draw_arc.h"
+#include "lv_draw_mask.h"
+#include "lv_draw_transform.h"
+#include "lv_draw_layer.h"
+
+/*********************
+ * DEFINES
+ *********************/
+
+/**********************
+ * TYPEDEFS
+ **********************/
+
+typedef struct {
+ void * user_data;
+} lv_draw_mask_t;
+
+typedef struct _lv_draw_layer_ctx_t {
+ lv_area_t area_full;
+ lv_area_t area_act;
+ lv_coord_t max_row_with_alpha;
+ lv_coord_t max_row_with_no_alpha;
+ void * buf;
+ struct {
+ const lv_area_t * clip_area;
+ lv_area_t * buf_area;
+ void * buf;
+ bool screen_transp;
+ } original;
+} lv_draw_layer_ctx_t;
+
+typedef struct _lv_draw_ctx_t {
+ /**
+ * Pointer to a buffer to draw into
+ */
+ void * buf;
+
+ /**
+ * The position and size of `buf` (absolute coordinates)
+ */
+ lv_area_t * buf_area;
+
+ /**
+ * The current clip area with absolute coordinates, always the same or smaller than `buf_area`
+ */
+ const lv_area_t * clip_area;
+
+
+ void (*draw_rect)(struct _lv_draw_ctx_t * draw_ctx, const lv_draw_rect_dsc_t * dsc, const lv_area_t * coords);
+
+ void (*draw_arc)(struct _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 (*draw_img_decoded)(struct _lv_draw_ctx_t * draw_ctx, const lv_draw_img_dsc_t * dsc,
+ const lv_area_t * coords, const uint8_t * map_p, lv_img_cf_t color_format);
+
+ lv_res_t (*draw_img)(struct _lv_draw_ctx_t * draw_ctx, const lv_draw_img_dsc_t * draw_dsc,
+ const lv_area_t * coords, const void * src);
+
+ void (*draw_letter)(struct _lv_draw_ctx_t * draw_ctx, const lv_draw_label_dsc_t * dsc, const lv_point_t * pos_p,
+ uint32_t letter);
+
+
+ void (*draw_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 (*draw_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);
+
+
+ /**
+ * Get an area of a transformed image (zoomed and/or rotated)
+ * @param draw_ctx pointer to a draw context
+ * @param dest_area get this area of the result image. It assumes that the original image is placed to the 0;0 position.
+ * @param src_buf the source image
+ * @param src_w width of the source image in [px]
+ * @param src_h height of the source image in [px]
+ * @param src_stride the stride in [px].
+ * @param draw_dsc an `lv_draw_img_dsc_t` descriptor containing the transformation parameters
+ * @param cf the color format of `src_buf`
+ * @param cbuf place the colors of the pixels on `dest_area` here in RGB format
+ * @param abuf place the opacity of the pixels on `dest_area` here
+ */
+ void (*draw_transform)(struct _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);
+
+ /**
+ * Replace the buffer with a rect without decoration like radius or borders
+ */
+ void (*draw_bg)(struct _lv_draw_ctx_t * draw_ctx, const lv_draw_rect_dsc_t * draw_dsc, const lv_area_t * coords);
+
+ /**
+ * Wait until all background operations are finished. (E.g. GPU operations)
+ */
+ void (*wait_for_finish)(struct _lv_draw_ctx_t * draw_ctx);
+
+ /**
+ * Copy an area from buffer to an other
+ * @param draw_ctx pointer to a draw context
+ * @param dest_buf copy the buffer into this buffer
+ * @param dest_stride the width of the dest_buf in pixels
+ * @param dest_area the destination area
+ * @param src_buf copy from this buffer
+ * @param src_stride the width of src_buf in pixels
+ * @param src_area the source area.
+ *
+ * @note dest_area and src_area must have the same width and height
+ * but can have different x and y position.
+ * @note dest_area and src_area must be clipped to the real dimensions of the buffers
+ */
+ void (*buffer_copy)(struct _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);
+
+ /**
+ * Initialize a new layer context.
+ * The original buffer and area data are already saved from `draw_ctx` to `layer_ctx`
+ * @param draw_ctx pointer to the current draw context
+ * @param layer_area the coordinates of the layer
+ * @param flags OR-ed flags from @lv_draw_layer_flags_t
+ * @return pointer to the layer context, or NULL on error
+ */
+ struct _lv_draw_layer_ctx_t * (*layer_init)(struct _lv_draw_ctx_t * draw_ctx, struct _lv_draw_layer_ctx_t * layer_ctx,
+ lv_draw_layer_flags_t flags);
+
+ /**
+ * Adjust the layer_ctx and/or draw_ctx based on the `layer_ctx->area_act`.
+ * It's called only if flags has `LV_DRAW_LAYER_FLAG_CAN_SUBDIVIDE`
+ * @param draw_ctx pointer to the current draw context
+ * @param layer_ctx pointer to a layer context
+ * @param flags OR-ed flags from @lv_draw_layer_flags_t
+ */
+ void (*layer_adjust)(struct _lv_draw_ctx_t * draw_ctx, struct _lv_draw_layer_ctx_t * layer_ctx,
+ lv_draw_layer_flags_t flags);
+
+ /**
+ * Blend a rendered layer to `layer_ctx->area_act`
+ * @param draw_ctx pointer to the current draw context
+ * @param layer_ctx pointer to a layer context
+ * @param draw_dsc pointer to an image draw descriptor
+ */
+ void (*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);
+
+ /**
+ * Destroy a layer context. The original buffer and area data of the `draw_ctx` will be restored
+ * and the `layer_ctx` itself will be freed automatically.
+ * @param draw_ctx pointer to the current draw context
+ * @param layer_ctx pointer to a layer context
+ */
+ void (*layer_destroy)(struct _lv_draw_ctx_t * draw_ctx, lv_draw_layer_ctx_t * layer_ctx);
+
+ /**
+ * Size of a layer context in bytes.
+ */
+ size_t layer_instance_size;
+
+#if LV_USE_USER_DATA
+ void * user_data;
+#endif
+
+} lv_draw_ctx_t;
+
+/**********************
+ * GLOBAL PROTOTYPES
+ **********************/
+
+void lv_draw_init(void);
+
+
+void lv_draw_wait_for_finish(lv_draw_ctx_t * draw_ctx);
+
+/**********************
+ * GLOBAL VARIABLES
+ **********************/
+
+/**********************
+ * MACROS
+ **********************/
+
+/**********************
+ * POST INCLUDES
+ *********************/
+
+#ifdef __cplusplus
+} /*extern "C"*/
+#endif
+
+#endif /*LV_DRAW_H*/
diff --git a/lib/lvgl/src/draw/lv_draw.mk b/lib/lvgl/src/draw/lv_draw.mk
new file mode 100644
index 00000000..f48f48fe
--- /dev/null
+++ b/lib/lvgl/src/draw/lv_draw.mk
@@ -0,0 +1,25 @@
+CSRCS += lv_draw_arc.c
+CSRCS += lv_draw.c
+CSRCS += lv_draw_img.c
+CSRCS += lv_draw_label.c
+CSRCS += lv_draw_line.c
+CSRCS += lv_draw_mask.c
+CSRCS += lv_draw_rect.c
+CSRCS += lv_draw_transform.c
+CSRCS += lv_draw_layer.c
+CSRCS += lv_draw_triangle.c
+CSRCS += lv_img_buf.c
+CSRCS += lv_img_cache.c
+CSRCS += lv_img_decoder.c
+
+DEPPATH += --dep-path $(LVGL_DIR)/$(LVGL_DIR_NAME)/src/draw
+VPATH += :$(LVGL_DIR)/$(LVGL_DIR_NAME)/src/draw
+
+CFLAGS += "-I$(LVGL_DIR)/$(LVGL_DIR_NAME)/src/draw"
+
+include $(LVGL_DIR)/$(LVGL_DIR_NAME)/src/draw/arm2d/lv_draw_arm2d.mk
+include $(LVGL_DIR)/$(LVGL_DIR_NAME)/src/draw/nxp/lv_draw_nxp.mk
+include $(LVGL_DIR)/$(LVGL_DIR_NAME)/src/draw/sdl/lv_draw_sdl.mk
+include $(LVGL_DIR)/$(LVGL_DIR_NAME)/src/draw/stm32_dma2d/lv_draw_stm32_dma2d.mk
+include $(LVGL_DIR)/$(LVGL_DIR_NAME)/src/draw/sw/lv_draw_sw.mk
+include $(LVGL_DIR)/$(LVGL_DIR_NAME)/src/draw/swm341_dma2d/lv_draw_swm341_dma2d.mk
diff --git a/lib/lvgl/src/draw/lv_draw_arc.c b/lib/lvgl/src/draw/lv_draw_arc.c
new file mode 100644
index 00000000..b806a002
--- /dev/null
+++ b/lib/lvgl/src/draw/lv_draw_arc.c
@@ -0,0 +1,152 @@
+/**
+ * @file lv_draw_arc.c
+ *
+ */
+
+/*********************
+ * INCLUDES
+ *********************/
+#include "lv_draw.h"
+#include "lv_draw_arc.h"
+
+/*********************
+ * DEFINES
+ *********************/
+
+/**********************
+ * TYPEDEFS
+ **********************/
+
+/**********************
+ * STATIC PROTOTYPES
+ **********************/
+
+/**********************
+ * STATIC VARIABLES
+ **********************/
+
+/**********************
+ * MACROS
+ **********************/
+
+/**********************
+ * GLOBAL FUNCTIONS
+ **********************/
+
+void lv_draw_arc_dsc_init(lv_draw_arc_dsc_t * dsc)
+{
+ lv_memset_00(dsc, sizeof(lv_draw_arc_dsc_t));
+ dsc->width = 1;
+ dsc->opa = LV_OPA_COVER;
+ dsc->color = lv_color_black();
+}
+
+void lv_draw_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(dsc->opa <= LV_OPA_MIN) return;
+ if(dsc->width == 0) return;
+ if(start_angle == end_angle) return;
+
+ draw_ctx->draw_arc(draw_ctx, dsc, center, radius, start_angle, end_angle);
+
+ // const lv_draw_backend_t * backend = lv_draw_backend_get();
+ // backend->draw_arc(center_x, center_y, radius, start_angle, end_angle, clip_area, dsc);
+}
+
+void lv_draw_arc_get_area(lv_coord_t x, lv_coord_t y, uint16_t radius, uint16_t start_angle, uint16_t end_angle,
+ lv_coord_t w, bool rounded, lv_area_t * area)
+{
+ lv_coord_t rout = radius;
+
+ /*Special case: full arc invalidation */
+ if(end_angle == start_angle + 360) {
+ area->x1 = x - rout;
+ area->y1 = y - rout;
+ area->x2 = x + rout;
+ area->y2 = y + rout;
+ return;
+ }
+
+ if(start_angle > 360) start_angle -= 360;
+ if(end_angle > 360) end_angle -= 360;
+
+ lv_coord_t rin = radius - w;
+ lv_coord_t extra_area = rounded ? w / 2 + 1 : 0;
+ uint8_t start_quarter = start_angle / 90;
+ uint8_t end_quarter = end_angle / 90;
+
+ /*360 deg still counts as quarter 3 (360 / 90 would be 4)*/
+ if(start_quarter == 4) start_quarter = 3;
+ if(end_quarter == 4) end_quarter = 3;
+
+ if(start_quarter == end_quarter && start_angle <= end_angle) {
+ if(start_quarter == 0) {
+ area->y1 = y + ((lv_trigo_sin(start_angle) * rin) >> LV_TRIGO_SHIFT) - extra_area;
+ area->x2 = x + ((lv_trigo_sin(start_angle + 90) * rout) >> LV_TRIGO_SHIFT) + extra_area;
+
+ area->y2 = y + ((lv_trigo_sin(end_angle) * rout) >> LV_TRIGO_SHIFT) + extra_area;
+ area->x1 = x + ((lv_trigo_sin(end_angle + 90) * rin) >> LV_TRIGO_SHIFT) - extra_area;
+ }
+ else if(start_quarter == 1) {
+ area->y2 = y + ((lv_trigo_sin(start_angle) * rout) >> LV_TRIGO_SHIFT) + extra_area;
+ area->x2 = x + ((lv_trigo_sin(start_angle + 90) * rin) >> LV_TRIGO_SHIFT) + extra_area;
+
+ area->y1 = y + ((lv_trigo_sin(end_angle) * rin) >> LV_TRIGO_SHIFT) - extra_area;
+ area->x1 = x + ((lv_trigo_sin(end_angle + 90) * rout) >> LV_TRIGO_SHIFT) - extra_area;
+ }
+ else if(start_quarter == 2) {
+ area->x1 = x + ((lv_trigo_sin(start_angle + 90) * rout) >> LV_TRIGO_SHIFT) - extra_area;
+ area->y2 = y + ((lv_trigo_sin(start_angle) * rin) >> LV_TRIGO_SHIFT) + extra_area;
+
+ area->y1 = y + ((lv_trigo_sin(end_angle) * rout) >> LV_TRIGO_SHIFT) - extra_area;
+ area->x2 = x + ((lv_trigo_sin(end_angle + 90) * rin) >> LV_TRIGO_SHIFT) + extra_area;
+ }
+ else if(start_quarter == 3) {
+ area->x1 = x + ((lv_trigo_sin(start_angle + 90) * rin) >> LV_TRIGO_SHIFT) - extra_area;
+ area->y1 = y + ((lv_trigo_sin(start_angle) * rout) >> LV_TRIGO_SHIFT) - extra_area;
+
+ area->x2 = x + ((lv_trigo_sin(end_angle + 90) * rout) >> LV_TRIGO_SHIFT) + extra_area;
+ area->y2 = y + ((lv_trigo_sin(end_angle) * rin) >> LV_TRIGO_SHIFT) + extra_area;
+ }
+ }
+ else if(start_quarter == 0 && end_quarter == 1) {
+ area->x1 = x + ((lv_trigo_sin(end_angle + 90) * rout) >> LV_TRIGO_SHIFT) - extra_area;
+ area->y1 = y + ((LV_MIN(lv_trigo_sin(end_angle),
+ lv_trigo_sin(start_angle)) * rin) >> LV_TRIGO_SHIFT) - extra_area;
+ area->x2 = x + ((lv_trigo_sin(start_angle + 90) * rout) >> LV_TRIGO_SHIFT) + extra_area;
+ area->y2 = y + rout + extra_area;
+ }
+ else if(start_quarter == 1 && end_quarter == 2) {
+ area->x1 = x - rout - extra_area;
+ area->y1 = y + ((lv_trigo_sin(end_angle) * rout) >> LV_TRIGO_SHIFT) - extra_area;
+ area->x2 = x + ((LV_MAX(lv_trigo_sin(start_angle + 90),
+ lv_trigo_sin(end_angle + 90)) * rin) >> LV_TRIGO_SHIFT) + extra_area;
+ area->y2 = y + ((lv_trigo_sin(start_angle) * rout) >> LV_TRIGO_SHIFT) + extra_area;
+ }
+ else if(start_quarter == 2 && end_quarter == 3) {
+ area->x1 = x + ((lv_trigo_sin(start_angle + 90) * rout) >> LV_TRIGO_SHIFT) - extra_area;
+ area->y1 = y - rout - extra_area;
+ area->x2 = x + ((lv_trigo_sin(end_angle + 90) * rout) >> LV_TRIGO_SHIFT) + extra_area;
+ area->y2 = y + (LV_MAX(lv_trigo_sin(end_angle) * rin,
+ lv_trigo_sin(start_angle) * rin) >> LV_TRIGO_SHIFT) + extra_area;
+ }
+ else if(start_quarter == 3 && end_quarter == 0) {
+ area->x1 = x + ((LV_MIN(lv_trigo_sin(end_angle + 90),
+ lv_trigo_sin(start_angle + 90)) * rin) >> LV_TRIGO_SHIFT) - extra_area;
+ area->y1 = y + ((lv_trigo_sin(start_angle) * rout) >> LV_TRIGO_SHIFT) - extra_area;
+ area->x2 = x + rout + extra_area;
+ area->y2 = y + ((lv_trigo_sin(end_angle) * rout) >> LV_TRIGO_SHIFT) + extra_area;
+
+ }
+ else {
+ area->x1 = x - rout;
+ area->y1 = y - rout;
+ area->x2 = x + rout;
+ area->y2 = y + rout;
+ }
+}
+
+/**********************
+ * STATIC FUNCTIONS
+ **********************/
diff --git a/lib/lvgl/src/draw/lv_draw_arc.h b/lib/lvgl/src/draw/lv_draw_arc.h
new file mode 100644
index 00000000..8783f131
--- /dev/null
+++ b/lib/lvgl/src/draw/lv_draw_arc.h
@@ -0,0 +1,83 @@
+/**
+ * @file lv_draw_arc.h
+ *
+ */
+
+#ifndef LV_DRAW_ARC_H
+#define LV_DRAW_ARC_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*********************
+ * INCLUDES
+ *********************/
+#include "../lv_conf_internal.h"
+#include "../misc/lv_color.h"
+#include "../misc/lv_area.h"
+#include "../misc/lv_style.h"
+
+/*********************
+ * DEFINES
+ *********************/
+
+/**********************
+ * TYPEDEFS
+ **********************/
+typedef struct {
+ lv_color_t color;
+ lv_coord_t width;
+ uint16_t start_angle;
+ uint16_t end_angle;
+ const void * img_src;
+ lv_opa_t opa;
+ lv_blend_mode_t blend_mode : 2;
+ uint8_t rounded : 1;
+} lv_draw_arc_dsc_t;
+
+struct _lv_draw_ctx_t;
+
+/**********************
+ * GLOBAL PROTOTYPES
+ **********************/
+
+void lv_draw_arc_dsc_init(lv_draw_arc_dsc_t * dsc);
+
+/**
+ * Draw an arc. (Can draw pie too with great thickness.)
+ * @param center_x the x coordinate of the center of the arc
+ * @param center_y the y coordinate of the center of the arc
+ * @param radius the radius of the arc
+ * @param mask the arc will be drawn only in this mask
+ * @param start_angle the start angle of the arc (0 deg on the bottom, 90 deg on the right)
+ * @param end_angle the end angle of the arc
+ * @param clip_area the arc will be drawn only in this area
+ * @param dsc pointer to an initialized `lv_draw_line_dsc_t` variable
+ */
+void lv_draw_arc(struct _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);
+
+/**
+ * Get an area the should be invalidated when the arcs angle changed between start_angle and end_ange
+ * @param x the x coordinate of the center of the arc
+ * @param y the y coordinate of the center of the arc
+ * @param radius the radius of the arc
+ * @param start_angle the start angle of the arc (0 deg on the bottom, 90 deg on the right)
+ * @param end_angle the end angle of the arc
+ * @param w width of the arc
+ * @param rounded true: the arc is rounded
+ * @param area store the area to invalidate here
+ */
+void lv_draw_arc_get_area(lv_coord_t x, lv_coord_t y, uint16_t radius, uint16_t start_angle, uint16_t end_angle,
+ lv_coord_t w, bool rounded, lv_area_t * area);
+
+/**********************
+ * MACROS
+ **********************/
+
+#ifdef __cplusplus
+} /*extern "C"*/
+#endif
+
+#endif /*LV_DRAW_ARC_H*/
diff --git a/lib/lvgl/src/draw/lv_draw_img.c b/lib/lvgl/src/draw/lv_draw_img.c
new file mode 100644
index 00000000..41dc0f03
--- /dev/null
+++ b/lib/lvgl/src/draw/lv_draw_img.c
@@ -0,0 +1,364 @@
+/**
+ * @file lv_draw_img.c
+ *
+ */
+
+/*********************
+ * INCLUDES
+ *********************/
+#include "lv_draw_img.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
+ *********************/
+
+/**********************
+ * TYPEDEFS
+ **********************/
+
+/**********************
+ * STATIC PROTOTYPES
+ **********************/
+LV_ATTRIBUTE_FAST_MEM static lv_res_t decode_and_draw(lv_draw_ctx_t * draw_ctx, const lv_draw_img_dsc_t * draw_dsc,
+ const lv_area_t * coords, const void * src);
+
+static void show_error(lv_draw_ctx_t * draw_ctx, const lv_area_t * coords, const char * msg);
+static void draw_cleanup(_lv_img_cache_entry_t * cache);
+
+/**********************
+ * STATIC VARIABLES
+ **********************/
+
+/**********************
+ * MACROS
+ **********************/
+
+/**********************
+ * GLOBAL FUNCTIONS
+ **********************/
+
+void lv_draw_img_dsc_init(lv_draw_img_dsc_t * dsc)
+{
+ lv_memset_00(dsc, sizeof(lv_draw_img_dsc_t));
+ dsc->recolor = lv_color_black();
+ dsc->opa = LV_OPA_COVER;
+ dsc->zoom = LV_IMG_ZOOM_NONE;
+ dsc->antialias = LV_COLOR_DEPTH > 8 ? 1 : 0;
+}
+
+/**
+ * Draw an image
+ * @param coords the coordinates of the image
+ * @param mask the image will be drawn only in this area
+ * @param src pointer to a lv_color_t array which contains the pixels of the image
+ * @param dsc pointer to an initialized `lv_draw_img_dsc_t` variable
+ */
+void lv_draw_img(lv_draw_ctx_t * draw_ctx, const lv_draw_img_dsc_t * dsc, const lv_area_t * coords, const void * src)
+{
+ if(src == NULL) {
+ LV_LOG_WARN("Image draw: src is NULL");
+ show_error(draw_ctx, coords, "No\ndata");
+ return;
+ }
+
+ if(dsc->opa <= LV_OPA_MIN) return;
+
+ lv_res_t res;
+ if(draw_ctx->draw_img) {
+ res = draw_ctx->draw_img(draw_ctx, dsc, coords, src);
+ }
+ else {
+ res = decode_and_draw(draw_ctx, dsc, coords, src);
+ }
+
+ if(res == LV_RES_INV) {
+ LV_LOG_WARN("Image draw error");
+ show_error(draw_ctx, coords, "No\ndata");
+ return;
+ }
+}
+
+/**
+ * Get the pixel size of a color format in bits
+ * @param cf a color format (`LV_IMG_CF_...`)
+ * @return the pixel size in bits
+ */
+uint8_t lv_img_cf_get_px_size(lv_img_cf_t cf)
+{
+ uint8_t px_size = 0;
+
+ switch(cf) {
+ case LV_IMG_CF_UNKNOWN:
+ case LV_IMG_CF_RAW:
+ px_size = 0;
+ break;
+ case LV_IMG_CF_TRUE_COLOR:
+ case LV_IMG_CF_TRUE_COLOR_CHROMA_KEYED:
+ px_size = LV_COLOR_SIZE;
+ break;
+ case LV_IMG_CF_TRUE_COLOR_ALPHA:
+ px_size = LV_IMG_PX_SIZE_ALPHA_BYTE << 3;
+ break;
+ case LV_IMG_CF_INDEXED_1BIT:
+ case LV_IMG_CF_ALPHA_1BIT:
+ px_size = 1;
+ break;
+ case LV_IMG_CF_INDEXED_2BIT:
+ case LV_IMG_CF_ALPHA_2BIT:
+ px_size = 2;
+ break;
+ case LV_IMG_CF_INDEXED_4BIT:
+ case LV_IMG_CF_ALPHA_4BIT:
+ px_size = 4;
+ break;
+ case LV_IMG_CF_INDEXED_8BIT:
+ case LV_IMG_CF_ALPHA_8BIT:
+ px_size = 8;
+ break;
+ default:
+ px_size = 0;
+ break;
+ }
+
+ return px_size;
+}
+
+/**
+ * Check if a color format is chroma keyed or not
+ * @param cf a color format (`LV_IMG_CF_...`)
+ * @return true: chroma keyed; false: not chroma keyed
+ */
+bool lv_img_cf_is_chroma_keyed(lv_img_cf_t cf)
+{
+ bool is_chroma_keyed = false;
+
+ switch(cf) {
+ case LV_IMG_CF_TRUE_COLOR_CHROMA_KEYED:
+ case LV_IMG_CF_RAW_CHROMA_KEYED:
+ is_chroma_keyed = true;
+ break;
+
+ default:
+ is_chroma_keyed = false;
+ break;
+ }
+
+ return is_chroma_keyed;
+}
+
+/**
+ * Check if a color format has alpha channel or not
+ * @param cf a color format (`LV_IMG_CF_...`)
+ * @return true: has alpha channel; false: doesn't have alpha channel
+ */
+bool lv_img_cf_has_alpha(lv_img_cf_t cf)
+{
+ bool has_alpha = false;
+
+ switch(cf) {
+ case LV_IMG_CF_TRUE_COLOR_ALPHA:
+ case LV_IMG_CF_RAW_ALPHA:
+ case LV_IMG_CF_INDEXED_1BIT:
+ case LV_IMG_CF_INDEXED_2BIT:
+ case LV_IMG_CF_INDEXED_4BIT:
+ case LV_IMG_CF_INDEXED_8BIT:
+ case LV_IMG_CF_ALPHA_1BIT:
+ case LV_IMG_CF_ALPHA_2BIT:
+ case LV_IMG_CF_ALPHA_4BIT:
+ case LV_IMG_CF_ALPHA_8BIT:
+ has_alpha = true;
+ break;
+ default:
+ has_alpha = false;
+ break;
+ }
+
+ return has_alpha;
+}
+
+/**
+ * Get the type of an image source
+ * @param src pointer to an image source:
+ * - pointer to an 'lv_img_t' variable (image stored internally and compiled into the code)
+ * - a path to a file (e.g. "S:/folder/image.bin")
+ * - or a symbol (e.g. LV_SYMBOL_CLOSE)
+ * @return type of the image source LV_IMG_SRC_VARIABLE/FILE/SYMBOL/UNKNOWN
+ */
+lv_img_src_t lv_img_src_get_type(const void * src)
+{
+ lv_img_src_t img_src_type = LV_IMG_SRC_UNKNOWN;
+
+ if(src == NULL) return img_src_type;
+ const uint8_t * u8_p = src;
+
+ /*The first byte shows the type of the image source*/
+ if(u8_p[0] >= 0x20 && u8_p[0] <= 0x7F) {
+ img_src_type = LV_IMG_SRC_FILE; /*If it's an ASCII character then it's file name*/
+ }
+ else if(u8_p[0] >= 0x80) {
+ img_src_type = LV_IMG_SRC_SYMBOL; /*Symbols begins after 0x7F*/
+ }
+ else {
+ img_src_type = LV_IMG_SRC_VARIABLE; /*`lv_img_dsc_t` is draw to the first byte < 0x20*/
+ }
+
+ if(LV_IMG_SRC_UNKNOWN == img_src_type) {
+ LV_LOG_WARN("lv_img_src_get_type: unknown image type");
+ }
+
+ return img_src_type;
+}
+
+void lv_draw_img_decoded(lv_draw_ctx_t * draw_ctx, const lv_draw_img_dsc_t * dsc,
+ const lv_area_t * coords, const uint8_t * map_p, lv_img_cf_t color_format)
+{
+ if(draw_ctx->draw_img_decoded == NULL) return;
+
+ draw_ctx->draw_img_decoded(draw_ctx, dsc, coords, map_p, color_format);
+}
+
+/**********************
+ * STATIC FUNCTIONS
+ **********************/
+
+LV_ATTRIBUTE_FAST_MEM static lv_res_t decode_and_draw(lv_draw_ctx_t * draw_ctx, const lv_draw_img_dsc_t * draw_dsc,
+ const lv_area_t * coords, const void * src)
+{
+ if(draw_dsc->opa <= LV_OPA_MIN) return LV_RES_OK;
+
+ _lv_img_cache_entry_t * cdsc = _lv_img_cache_open(src, draw_dsc->recolor, draw_dsc->frame_id);
+
+ if(cdsc == NULL) return LV_RES_INV;
+
+ lv_img_cf_t cf;
+ if(lv_img_cf_is_chroma_keyed(cdsc->dec_dsc.header.cf)) cf = LV_IMG_CF_TRUE_COLOR_CHROMA_KEYED;
+ else if(LV_IMG_CF_ALPHA_8BIT == cdsc->dec_dsc.header.cf) cf = LV_IMG_CF_ALPHA_8BIT;
+ else if(LV_IMG_CF_RGB565A8 == cdsc->dec_dsc.header.cf) cf = LV_IMG_CF_RGB565A8;
+ else if(lv_img_cf_has_alpha(cdsc->dec_dsc.header.cf)) cf = LV_IMG_CF_TRUE_COLOR_ALPHA;
+ else cf = LV_IMG_CF_TRUE_COLOR;
+
+ if(cf == LV_IMG_CF_ALPHA_8BIT) {
+ if(draw_dsc->angle || draw_dsc->zoom != LV_IMG_ZOOM_NONE) {
+ /* resume normal method */
+ cf = LV_IMG_CF_TRUE_COLOR_ALPHA;
+ cdsc->dec_dsc.img_data = NULL;
+ }
+ }
+
+ if(cdsc->dec_dsc.error_msg != NULL) {
+ LV_LOG_WARN("Image draw error");
+
+ show_error(draw_ctx, coords, cdsc->dec_dsc.error_msg);
+ }
+ /*The decoder could open the image and gave the entire uncompressed image.
+ *Just draw it!*/
+ else if(cdsc->dec_dsc.img_data) {
+ lv_area_t map_area_rot;
+ lv_area_copy(&map_area_rot, coords);
+ if(draw_dsc->angle || draw_dsc->zoom != LV_IMG_ZOOM_NONE) {
+ int32_t w = lv_area_get_width(coords);
+ int32_t h = lv_area_get_height(coords);
+
+ _lv_img_buf_get_transformed_area(&map_area_rot, w, h, draw_dsc->angle, draw_dsc->zoom, &draw_dsc->pivot);
+
+ map_area_rot.x1 += coords->x1;
+ map_area_rot.y1 += coords->y1;
+ map_area_rot.x2 += coords->x1;
+ map_area_rot.y2 += coords->y1;
+ }
+
+ lv_area_t clip_com; /*Common area of mask and coords*/
+ bool union_ok;
+ union_ok = _lv_area_intersect(&clip_com, draw_ctx->clip_area, &map_area_rot);
+ /*Out of mask. There is nothing to draw so the image is drawn successfully.*/
+ if(union_ok == false) {
+ draw_cleanup(cdsc);
+ return LV_RES_OK;
+ }
+
+ const lv_area_t * clip_area_ori = draw_ctx->clip_area;
+ draw_ctx->clip_area = &clip_com;
+ lv_draw_img_decoded(draw_ctx, draw_dsc, coords, cdsc->dec_dsc.img_data, cf);
+ draw_ctx->clip_area = clip_area_ori;
+ }
+ /*The whole uncompressed image is not available. Try to read it line-by-line*/
+ else {
+ lv_area_t mask_com; /*Common area of mask and coords*/
+ bool union_ok;
+ union_ok = _lv_area_intersect(&mask_com, draw_ctx->clip_area, coords);
+ /*Out of mask. There is nothing to draw so the image is drawn successfully.*/
+ if(union_ok == false) {
+ draw_cleanup(cdsc);
+ return LV_RES_OK;
+ }
+
+ int32_t width = lv_area_get_width(&mask_com);
+
+ uint8_t * buf = lv_mem_buf_get(lv_area_get_width(&mask_com) *
+ LV_IMG_PX_SIZE_ALPHA_BYTE); /*+1 because of the possible alpha byte*/
+
+ const lv_area_t * clip_area_ori = draw_ctx->clip_area;
+ lv_area_t line;
+ lv_area_copy(&line, &mask_com);
+ lv_area_set_height(&line, 1);
+ int32_t x = mask_com.x1 - coords->x1;
+ int32_t y = mask_com.y1 - coords->y1;
+ int32_t row;
+ lv_res_t read_res;
+ for(row = mask_com.y1; row <= mask_com.y2; row++) {
+ lv_area_t mask_line;
+ union_ok = _lv_area_intersect(&mask_line, clip_area_ori, &line);
+ if(union_ok == false) continue;
+
+ read_res = lv_img_decoder_read_line(&cdsc->dec_dsc, x, y, width, buf);
+ if(read_res != LV_RES_OK) {
+ lv_img_decoder_close(&cdsc->dec_dsc);
+ LV_LOG_WARN("Image draw can't read the line");
+ lv_mem_buf_release(buf);
+ draw_cleanup(cdsc);
+ draw_ctx->clip_area = clip_area_ori;
+ return LV_RES_INV;
+ }
+
+ draw_ctx->clip_area = &mask_line;
+ lv_draw_img_decoded(draw_ctx, draw_dsc, &line, buf, cf);
+ line.y1++;
+ line.y2++;
+ y++;
+ }
+ draw_ctx->clip_area = clip_area_ori;
+ lv_mem_buf_release(buf);
+ }
+
+ draw_cleanup(cdsc);
+ return LV_RES_OK;
+}
+
+
+static void show_error(lv_draw_ctx_t * draw_ctx, const lv_area_t * coords, const char * msg)
+{
+ lv_draw_rect_dsc_t rect_dsc;
+ lv_draw_rect_dsc_init(&rect_dsc);
+ rect_dsc.bg_color = lv_color_white();
+ lv_draw_rect(draw_ctx, &rect_dsc, coords);
+
+ lv_draw_label_dsc_t label_dsc;
+ lv_draw_label_dsc_init(&label_dsc);
+ lv_draw_label(draw_ctx, &label_dsc, coords, msg, NULL);
+}
+
+static void draw_cleanup(_lv_img_cache_entry_t * cache)
+{
+ /*Automatically close images with no caching*/
+#if LV_IMG_CACHE_DEF_SIZE == 0
+ lv_img_decoder_close(&cache->dec_dsc);
+#else
+ LV_UNUSED(cache);
+#endif
+}
diff --git a/lib/lvgl/src/draw/lv_draw_img.h b/lib/lvgl/src/draw/lv_draw_img.h
new file mode 100644
index 00000000..a88a33ca
--- /dev/null
+++ b/lib/lvgl/src/draw/lv_draw_img.h
@@ -0,0 +1,104 @@
+/**
+ * @file lv_draw_img.h
+ *
+ */
+
+#ifndef LV_DRAW_IMG_H
+#define LV_DRAW_IMG_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*********************
+ * INCLUDES
+ *********************/
+#include "lv_img_decoder.h"
+#include "lv_img_buf.h"
+#include "../misc/lv_style.h"
+
+/*********************
+ * DEFINES
+ *********************/
+
+/**********************
+ * MACROS
+ **********************/
+
+/**********************
+ * TYPEDEFS
+ **********************/
+
+typedef struct {
+
+ int16_t angle;
+ uint16_t zoom;
+ lv_point_t pivot;
+
+ lv_color_t recolor;
+ lv_opa_t recolor_opa;
+
+ lv_opa_t opa;
+ lv_blend_mode_t blend_mode : 4;
+
+ int32_t frame_id;
+ uint8_t antialias : 1;
+} lv_draw_img_dsc_t;
+
+struct _lv_draw_ctx_t;
+
+/**********************
+ * GLOBAL PROTOTYPES
+ **********************/
+
+void lv_draw_img_dsc_init(lv_draw_img_dsc_t * dsc);
+/**
+ * Draw an image
+ * @param coords the coordinates of the image
+ * @param mask the image will be drawn only in this area
+ * @param src pointer to a lv_color_t array which contains the pixels of the image
+ * @param dsc pointer to an initialized `lv_draw_img_dsc_t` variable
+ */
+void lv_draw_img(struct _lv_draw_ctx_t * draw_ctx, const lv_draw_img_dsc_t * dsc, const lv_area_t * coords,
+ const void * src);
+
+
+void lv_draw_img_decoded(struct _lv_draw_ctx_t * draw_ctx, const lv_draw_img_dsc_t * dsc,
+ const lv_area_t * coords, const uint8_t * map_p, lv_img_cf_t color_format);
+
+/**
+ * Get the type of an image source
+ * @param src pointer to an image source:
+ * - pointer to an 'lv_img_t' variable (image stored internally and compiled into the code)
+ * - a path to a file (e.g. "S:/folder/image.bin")
+ * - or a symbol (e.g. LV_SYMBOL_CLOSE)
+ * @return type of the image source LV_IMG_SRC_VARIABLE/FILE/SYMBOL/UNKNOWN
+ */
+lv_img_src_t lv_img_src_get_type(const void * src);
+
+/**
+ * Get the pixel size of a color format in bits
+ * @param cf a color format (`LV_IMG_CF_...`)
+ * @return the pixel size in bits
+ */
+uint8_t lv_img_cf_get_px_size(lv_img_cf_t cf);
+
+/**
+ * Check if a color format is chroma keyed or not
+ * @param cf a color format (`LV_IMG_CF_...`)
+ * @return true: chroma keyed; false: not chroma keyed
+ */
+bool lv_img_cf_is_chroma_keyed(lv_img_cf_t cf);
+
+/**
+ * Check if a color format has alpha channel or not
+ * @param cf a color format (`LV_IMG_CF_...`)
+ * @return true: has alpha channel; false: doesn't have alpha channel
+ */
+bool lv_img_cf_has_alpha(lv_img_cf_t cf);
+
+#ifdef __cplusplus
+} /*extern "C"*/
+#endif
+
+#endif /*LV_DRAW_IMG_H*/
diff --git a/lib/lvgl/src/draw/lv_draw_label.c b/lib/lvgl/src/draw/lv_draw_label.c
new file mode 100644
index 00000000..7f80df45
--- /dev/null
+++ b/lib/lvgl/src/draw/lv_draw_label.c
@@ -0,0 +1,417 @@
+/**
+ * @file lv_draw_label.c
+ *
+ */
+
+/*********************
+ * INCLUDES
+ *********************/
+#include "lv_draw.h"
+#include "lv_draw_label.h"
+#include "../misc/lv_math.h"
+#include "../hal/lv_hal_disp.h"
+#include "../core/lv_refr.h"
+#include "../misc/lv_bidi.h"
+#include "../misc/lv_assert.h"
+
+/*********************
+ * DEFINES
+ *********************/
+#define LABEL_RECOLOR_PAR_LENGTH 6
+#define LV_LABEL_HINT_UPDATE_TH 1024 /*Update the "hint" if the label's y coordinates have changed more then this*/
+
+/**********************
+ * TYPEDEFS
+ **********************/
+enum {
+ CMD_STATE_WAIT,
+ CMD_STATE_PAR,
+ CMD_STATE_IN,
+};
+typedef uint8_t cmd_state_t;
+
+/**********************
+ * STATIC PROTOTYPES
+ **********************/
+
+static uint8_t hex_char_to_num(char hex);
+
+/**********************
+ * STATIC VARIABLES
+ **********************/
+
+/**********************
+ * GLOBAL VARIABLES
+ **********************/
+
+/**********************
+ * MACROS
+ **********************/
+
+/**********************
+ * GLOBAL FUNCTIONS
+ **********************/
+
+void lv_draw_label_dsc_init(lv_draw_label_dsc_t * dsc)
+{
+ lv_memset_00(dsc, sizeof(lv_draw_label_dsc_t));
+ dsc->opa = LV_OPA_COVER;
+ dsc->color = lv_color_black();
+ dsc->font = LV_FONT_DEFAULT;
+ dsc->sel_start = LV_DRAW_LABEL_NO_TXT_SEL;
+ dsc->sel_end = LV_DRAW_LABEL_NO_TXT_SEL;
+ dsc->sel_color = lv_color_black();
+ dsc->sel_bg_color = lv_palette_main(LV_PALETTE_BLUE);
+ dsc->bidi_dir = LV_BASE_DIR_LTR;
+}
+
+/**
+ * Write a text
+ * @param coords coordinates of the label
+ * @param mask the label will be drawn only in this area
+ * @param dsc pointer to draw descriptor
+ * @param txt `\0` terminated text to write
+ * @param hint pointer to a `lv_draw_label_hint_t` variable.
+ * It is managed by the draw to speed up the drawing of very long texts (thousands of lines).
+ */
+LV_ATTRIBUTE_FAST_MEM void lv_draw_label(lv_draw_ctx_t * draw_ctx, const lv_draw_label_dsc_t * dsc,
+ const lv_area_t * coords, const char * txt, lv_draw_label_hint_t * hint)
+{
+ if(dsc->opa <= LV_OPA_MIN) return;
+ if(dsc->font == NULL) {
+ LV_LOG_WARN("dsc->font == NULL");
+ return;
+ }
+
+ if(draw_ctx->draw_letter == NULL) {
+ LV_LOG_WARN("draw->draw_letter == NULL (there is no function to draw letters)");
+ return;
+ }
+
+ lv_draw_label_dsc_t dsc_mod = *dsc;
+
+ const lv_font_t * font = dsc->font;
+ int32_t w;
+
+ /*No need to waste processor time if string is empty*/
+ if(txt == NULL || txt[0] == '\0')
+ return;
+
+ lv_area_t clipped_area;
+ bool clip_ok = _lv_area_intersect(&clipped_area, coords, draw_ctx->clip_area);
+ if(!clip_ok) return;
+
+ lv_text_align_t align = dsc->align;
+ lv_base_dir_t base_dir = dsc->bidi_dir;
+
+ lv_bidi_calculate_align(&align, &base_dir, txt);
+
+ if((dsc->flag & LV_TEXT_FLAG_EXPAND) == 0) {
+ /*Normally use the label's width as width*/
+ w = lv_area_get_width(coords);
+ }
+ else {
+ /*If EXPAND is enabled then not limit the text's width to the object's width*/
+ lv_point_t p;
+ lv_txt_get_size(&p, txt, dsc->font, dsc->letter_space, dsc->line_space, LV_COORD_MAX,
+ dsc->flag);
+ w = p.x;
+ }
+
+ int32_t line_height_font = lv_font_get_line_height(font);
+ int32_t line_height = line_height_font + dsc->line_space;
+
+ /*Init variables for the first line*/
+ int32_t line_width = 0;
+ lv_point_t pos;
+ pos.x = coords->x1;
+ pos.y = coords->y1;
+
+ int32_t x_ofs = 0;
+ int32_t y_ofs = 0;
+ x_ofs = dsc->ofs_x;
+ y_ofs = dsc->ofs_y;
+ pos.y += y_ofs;
+
+ uint32_t line_start = 0;
+ int32_t last_line_start = -1;
+
+ /*Check the hint to use the cached info*/
+ if(hint && y_ofs == 0 && coords->y1 < 0) {
+ /*If the label changed too much recalculate the hint.*/
+ if(LV_ABS(hint->coord_y - coords->y1) > LV_LABEL_HINT_UPDATE_TH - 2 * line_height) {
+ hint->line_start = -1;
+ }
+ last_line_start = hint->line_start;
+ }
+
+ /*Use the hint if it's valid*/
+ if(hint && last_line_start >= 0) {
+ line_start = last_line_start;
+ pos.y += hint->y;
+ }
+
+ uint32_t line_end = line_start + _lv_txt_get_next_line(&txt[line_start], font, dsc->letter_space, w, NULL, dsc->flag);
+
+ /*Go the first visible line*/
+ while(pos.y + line_height_font < draw_ctx->clip_area->y1) {
+ /*Go to next line*/
+ line_start = line_end;
+ line_end += _lv_txt_get_next_line(&txt[line_start], font, dsc->letter_space, w, NULL, dsc->flag);
+ pos.y += line_height;
+
+ /*Save at the threshold coordinate*/
+ if(hint && pos.y >= -LV_LABEL_HINT_UPDATE_TH && hint->line_start < 0) {
+ hint->line_start = line_start;
+ hint->y = pos.y - coords->y1;
+ hint->coord_y = coords->y1;
+ }
+
+ if(txt[line_start] == '\0') return;
+ }
+
+ /*Align to middle*/
+ if(align == LV_TEXT_ALIGN_CENTER) {
+ line_width = lv_txt_get_width(&txt[line_start], line_end - line_start, font, dsc->letter_space, dsc->flag);
+
+ pos.x += (lv_area_get_width(coords) - line_width) / 2;
+
+ }
+ /*Align to the right*/
+ else if(align == LV_TEXT_ALIGN_RIGHT) {
+ line_width = lv_txt_get_width(&txt[line_start], line_end - line_start, font, dsc->letter_space, dsc->flag);
+ pos.x += lv_area_get_width(coords) - line_width;
+ }
+ uint32_t sel_start = dsc->sel_start;
+ uint32_t sel_end = dsc->sel_end;
+ if(sel_start > sel_end) {
+ uint32_t tmp = sel_start;
+ sel_start = sel_end;
+ sel_end = tmp;
+ }
+ lv_draw_line_dsc_t line_dsc;
+
+ if((dsc->decor & LV_TEXT_DECOR_UNDERLINE) || (dsc->decor & LV_TEXT_DECOR_STRIKETHROUGH)) {
+ lv_draw_line_dsc_init(&line_dsc);
+ line_dsc.color = dsc->color;
+ line_dsc.width = font->underline_thickness ? font->underline_thickness : 1;
+ line_dsc.opa = dsc->opa;
+ line_dsc.blend_mode = dsc->blend_mode;
+ }
+
+ cmd_state_t cmd_state = CMD_STATE_WAIT;
+ uint32_t i;
+ uint32_t par_start = 0;
+ lv_color_t recolor = lv_color_black();
+ lv_color_t color = lv_color_black();
+ int32_t letter_w;
+
+ lv_draw_rect_dsc_t draw_dsc_sel;
+ lv_draw_rect_dsc_init(&draw_dsc_sel);
+ draw_dsc_sel.bg_color = dsc->sel_bg_color;
+
+ int32_t pos_x_start = pos.x;
+ /*Write out all lines*/
+ while(txt[line_start] != '\0') {
+ pos.x += x_ofs;
+
+ /*Write all letter of a line*/
+ cmd_state = CMD_STATE_WAIT;
+ i = 0;
+#if LV_USE_BIDI
+ char * bidi_txt = lv_mem_buf_get(line_end - line_start + 1);
+ _lv_bidi_process_paragraph(txt + line_start, bidi_txt, line_end - line_start, base_dir, NULL, 0);
+#else
+ const char * bidi_txt = txt + line_start;
+#endif
+
+ while(i < line_end - line_start) {
+ uint32_t logical_char_pos = 0;
+ if(sel_start != 0xFFFF && sel_end != 0xFFFF) {
+#if LV_USE_BIDI
+ logical_char_pos = _lv_txt_encoded_get_char_id(txt, line_start);
+ uint32_t t = _lv_txt_encoded_get_char_id(bidi_txt, i);
+ logical_char_pos += _lv_bidi_get_logical_pos(bidi_txt, NULL, line_end - line_start, base_dir, t, NULL);
+#else
+ logical_char_pos = _lv_txt_encoded_get_char_id(txt, line_start + i);
+#endif
+ }
+
+ uint32_t letter;
+ uint32_t letter_next;
+ _lv_txt_encoded_letter_next_2(bidi_txt, &letter, &letter_next, &i);
+ /*Handle the re-color command*/
+ if((dsc->flag & LV_TEXT_FLAG_RECOLOR) != 0) {
+ if(letter == (uint32_t)LV_TXT_COLOR_CMD[0]) {
+ if(cmd_state == CMD_STATE_WAIT) { /*Start char*/
+ par_start = i;
+ cmd_state = CMD_STATE_PAR;
+ continue;
+ }
+ else if(cmd_state == CMD_STATE_PAR) { /*Other start char in parameter escaped cmd. char*/
+ cmd_state = CMD_STATE_WAIT;
+ }
+ else if(cmd_state == CMD_STATE_IN) { /*Command end*/
+ cmd_state = CMD_STATE_WAIT;
+ continue;
+ }
+ }
+
+ /*Skip the color parameter and wait the space after it*/
+ if(cmd_state == CMD_STATE_PAR) {
+ if(letter == ' ') {
+ /*Get the parameter*/
+ if(i - par_start == LABEL_RECOLOR_PAR_LENGTH + 1) {
+ char buf[LABEL_RECOLOR_PAR_LENGTH + 1];
+ lv_memcpy_small(buf, &bidi_txt[par_start], LABEL_RECOLOR_PAR_LENGTH);
+ buf[LABEL_RECOLOR_PAR_LENGTH] = '\0';
+ int r, g, b;
+ r = (hex_char_to_num(buf[0]) << 4) + hex_char_to_num(buf[1]);
+ g = (hex_char_to_num(buf[2]) << 4) + hex_char_to_num(buf[3]);
+ b = (hex_char_to_num(buf[4]) << 4) + hex_char_to_num(buf[5]);
+ recolor = lv_color_make(r, g, b);
+ }
+ else {
+ recolor.full = dsc->color.full;
+ }
+ cmd_state = CMD_STATE_IN; /*After the parameter the text is in the command*/
+ }
+ continue;
+ }
+ }
+
+ color = dsc->color;
+
+ if(cmd_state == CMD_STATE_IN) color = recolor;
+
+ letter_w = lv_font_get_glyph_width(font, letter, letter_next);
+
+ if(sel_start != 0xFFFF && sel_end != 0xFFFF) {
+ if(logical_char_pos >= sel_start && logical_char_pos < sel_end) {
+ lv_area_t sel_coords;
+ sel_coords.x1 = pos.x;
+ sel_coords.y1 = pos.y;
+ sel_coords.x2 = pos.x + letter_w + dsc->letter_space - 1;
+ sel_coords.y2 = pos.y + line_height - 1;
+ lv_draw_rect(draw_ctx, &draw_dsc_sel, &sel_coords);
+ color = dsc->sel_color;
+ }
+ }
+
+ dsc_mod.color = color;
+ lv_draw_letter(draw_ctx, &dsc_mod, &pos, letter);
+
+ if(letter_w > 0) {
+ pos.x += letter_w + dsc->letter_space;
+ }
+ }
+
+ if(dsc->decor & LV_TEXT_DECOR_STRIKETHROUGH) {
+ lv_point_t p1;
+ lv_point_t p2;
+ p1.x = pos_x_start;
+ p1.y = pos.y + (dsc->font->line_height / 2) + line_dsc.width / 2;
+ p2.x = pos.x;
+ p2.y = p1.y;
+ line_dsc.color = color;
+ lv_draw_line(draw_ctx, &line_dsc, &p1, &p2);
+ }
+
+ if(dsc->decor & LV_TEXT_DECOR_UNDERLINE) {
+ lv_point_t p1;
+ lv_point_t p2;
+ p1.x = pos_x_start;
+ p1.y = pos.y + dsc->font->line_height - dsc->font->base_line - font->underline_position;
+ p2.x = pos.x;
+ p2.y = p1.y;
+ line_dsc.color = color;
+ lv_draw_line(draw_ctx, &line_dsc, &p1, &p2);
+ }
+
+#if LV_USE_BIDI
+ lv_mem_buf_release(bidi_txt);
+ bidi_txt = NULL;
+#endif
+ /*Go to next line*/
+ line_start = line_end;
+ line_end += _lv_txt_get_next_line(&txt[line_start], font, dsc->letter_space, w, NULL, dsc->flag);
+
+ pos.x = coords->x1;
+ /*Align to middle*/
+ if(align == LV_TEXT_ALIGN_CENTER) {
+ line_width =
+ lv_txt_get_width(&txt[line_start], line_end - line_start, font, dsc->letter_space, dsc->flag);
+
+ pos.x += (lv_area_get_width(coords) - line_width) / 2;
+
+ }
+ /*Align to the right*/
+ else if(align == LV_TEXT_ALIGN_RIGHT) {
+ line_width =
+ lv_txt_get_width(&txt[line_start], line_end - line_start, font, dsc->letter_space, dsc->flag);
+ pos.x += lv_area_get_width(coords) - line_width;
+ }
+
+ /*Go the next line position*/
+ pos.y += line_height;
+
+ if(pos.y > draw_ctx->clip_area->y2) return;
+ }
+
+ LV_ASSERT_MEM_INTEGRITY();
+}
+
+void lv_draw_letter(lv_draw_ctx_t * draw_ctx, const lv_draw_label_dsc_t * dsc, const lv_point_t * pos_p,
+ uint32_t letter)
+{
+ draw_ctx->draw_letter(draw_ctx, dsc, pos_p, letter);
+}
+
+
+/**********************
+ * STATIC FUNCTIONS
+ **********************/
+
+/**
+ * Convert a hexadecimal characters to a number (0..15)
+ * @param hex Pointer to a hexadecimal character (0..9, A..F)
+ * @return the numerical value of `hex` or 0 on error
+ */
+static uint8_t hex_char_to_num(char hex)
+{
+ uint8_t result = 0;
+
+ if(hex >= '0' && hex <= '9') {
+ result = hex - '0';
+ }
+ else {
+ if(hex >= 'a') hex -= 'a' - 'A'; /*Convert to upper case*/
+
+ switch(hex) {
+ case 'A':
+ result = 10;
+ break;
+ case 'B':
+ result = 11;
+ break;
+ case 'C':
+ result = 12;
+ break;
+ case 'D':
+ result = 13;
+ break;
+ case 'E':
+ result = 14;
+ break;
+ case 'F':
+ result = 15;
+ break;
+ default:
+ result = 0;
+ break;
+ }
+ }
+
+ return result;
+}
+
diff --git a/lib/lvgl/src/draw/lv_draw_label.h b/lib/lvgl/src/draw/lv_draw_label.h
new file mode 100644
index 00000000..de72eddf
--- /dev/null
+++ b/lib/lvgl/src/draw/lv_draw_label.h
@@ -0,0 +1,100 @@
+/**
+ * @file lv_draw_label.h
+ *
+ */
+
+#ifndef LV_DRAW_LABEL_H
+#define LV_DRAW_LABEL_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*********************
+ * INCLUDES
+ *********************/
+#include "../misc/lv_bidi.h"
+#include "../misc/lv_txt.h"
+#include "../misc/lv_color.h"
+#include "../misc/lv_style.h"
+
+/*********************
+ * DEFINES
+ *********************/
+#define LV_DRAW_LABEL_NO_TXT_SEL (0xFFFF)
+
+/**********************
+ * TYPEDEFS
+ **********************/
+
+typedef struct {
+ const lv_font_t * font;
+ uint32_t sel_start;
+ uint32_t sel_end;
+ lv_color_t color;
+ lv_color_t sel_color;
+ lv_color_t sel_bg_color;
+ lv_coord_t line_space;
+ lv_coord_t letter_space;
+ lv_coord_t ofs_x;
+ lv_coord_t ofs_y;
+ lv_opa_t opa;
+ lv_base_dir_t bidi_dir;
+ lv_text_align_t align;
+ lv_text_flag_t flag;
+ lv_text_decor_t decor : 3;
+ lv_blend_mode_t blend_mode: 3;
+} lv_draw_label_dsc_t;
+
+/** Store some info to speed up drawing of very large texts
+ * It takes a lot of time to get the first visible character because
+ * all the previous characters needs to be checked to calculate the positions.
+ * This structure stores an earlier (e.g. at -1000 px) coordinate and the index of that line.
+ * Therefore the calculations can start from here.*/
+typedef struct _lv_draw_label_hint_t {
+ /** Index of the line at `y` coordinate*/
+ int32_t line_start;
+
+ /** Give the `y` coordinate of the first letter at `line start` index. Relative to the label's coordinates*/
+ int32_t y;
+
+ /** The 'y1' coordinate of the label when the hint was saved.
+ * Used to invalidate the hint if the label has moved too much.*/
+ int32_t coord_y;
+} lv_draw_label_hint_t;
+
+struct _lv_draw_ctx_t;
+/**********************
+ * GLOBAL PROTOTYPES
+ **********************/
+
+LV_ATTRIBUTE_FAST_MEM void lv_draw_label_dsc_init(lv_draw_label_dsc_t * dsc);
+
+/**
+ * Write a text
+ * @param coords coordinates of the label
+ * @param mask the label will be drawn only in this area
+ * @param dsc pointer to draw descriptor
+ * @param txt `\0` terminated text to write
+ * @param hint pointer to a `lv_draw_label_hint_t` variable.
+ * It is managed by the draw to speed up the drawing of very long texts (thousands of lines).
+ */
+LV_ATTRIBUTE_FAST_MEM void lv_draw_label(struct _lv_draw_ctx_t * draw_ctx, const lv_draw_label_dsc_t * dsc,
+ const lv_area_t * coords, const char * txt, lv_draw_label_hint_t * hint);
+
+void lv_draw_letter(struct _lv_draw_ctx_t * draw_ctx, const lv_draw_label_dsc_t * dsc, const lv_point_t * pos_p,
+ uint32_t letter);
+
+/***********************
+ * GLOBAL VARIABLES
+ ***********************/
+
+/**********************
+ * MACROS
+ **********************/
+
+#ifdef __cplusplus
+} /*extern "C"*/
+#endif
+
+#endif /*LV_DRAW_LABEL_H*/
diff --git a/lib/lvgl/src/draw/lv_draw_layer.c b/lib/lvgl/src/draw/lv_draw_layer.c
new file mode 100644
index 00000000..da35682d
--- /dev/null
+++ b/lib/lvgl/src/draw/lv_draw_layer.c
@@ -0,0 +1,93 @@
+/**
+ * @file lv_draw_layer.c
+ *
+ */
+
+/*********************
+ * INCLUDES
+ *********************/
+#include "lv_draw.h"
+#include "lv_draw_arc.h"
+#include "../core/lv_refr.h"
+
+/*********************
+ * DEFINES
+ *********************/
+
+/**********************
+ * TYPEDEFS
+ **********************/
+
+/**********************
+ * STATIC PROTOTYPES
+ **********************/
+
+/**********************
+ * STATIC VARIABLES
+ **********************/
+
+/**********************
+ * MACROS
+ **********************/
+
+/**********************
+ * GLOBAL FUNCTIONS
+ **********************/
+
+lv_draw_layer_ctx_t * lv_draw_layer_create(lv_draw_ctx_t * draw_ctx, const lv_area_t * layer_area,
+ lv_draw_layer_flags_t flags)
+{
+ if(draw_ctx->layer_init == NULL) return NULL;
+
+ lv_draw_layer_ctx_t * layer_ctx = lv_mem_alloc(draw_ctx->layer_instance_size);
+ LV_ASSERT_MALLOC(layer_ctx);
+ if(layer_ctx == NULL) {
+ LV_LOG_WARN("Couldn't allocate a new layer context");
+ return NULL;
+ }
+
+ lv_memset_00(layer_ctx, draw_ctx->layer_instance_size);
+
+ lv_disp_t * disp_refr = _lv_refr_get_disp_refreshing();
+ layer_ctx->original.buf = draw_ctx->buf;
+ layer_ctx->original.buf_area = draw_ctx->buf_area;
+ layer_ctx->original.clip_area = draw_ctx->clip_area;
+ layer_ctx->original.screen_transp = disp_refr->driver->screen_transp;
+ layer_ctx->area_full = *layer_area;
+
+ lv_draw_layer_ctx_t * init_layer_ctx = draw_ctx->layer_init(draw_ctx, layer_ctx, flags);
+ if(NULL == init_layer_ctx) {
+ lv_mem_free(layer_ctx);
+ }
+ return init_layer_ctx;
+}
+
+void lv_draw_layer_adjust(struct _lv_draw_ctx_t * draw_ctx, struct _lv_draw_layer_ctx_t * layer_ctx,
+ lv_draw_layer_flags_t flags)
+{
+ if(draw_ctx->layer_adjust) draw_ctx->layer_adjust(draw_ctx, layer_ctx, flags);
+}
+
+void lv_draw_layer_blend(struct _lv_draw_ctx_t * draw_ctx, struct _lv_draw_layer_ctx_t * layer_ctx,
+ lv_draw_img_dsc_t * draw_dsc)
+{
+ if(draw_ctx->layer_blend) draw_ctx->layer_blend(draw_ctx, layer_ctx, draw_dsc);
+}
+
+void lv_draw_layer_destroy(lv_draw_ctx_t * draw_ctx, lv_draw_layer_ctx_t * layer_ctx)
+{
+
+ lv_draw_wait_for_finish(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;
+
+ if(draw_ctx->layer_destroy) draw_ctx->layer_destroy(draw_ctx, layer_ctx);
+ lv_mem_free(layer_ctx);
+}
+
+/**********************
+ * STATIC FUNCTIONS
+ **********************/
diff --git a/lib/lvgl/src/draw/lv_draw_layer.h b/lib/lvgl/src/draw/lv_draw_layer.h
new file mode 100644
index 00000000..cd64149c
--- /dev/null
+++ b/lib/lvgl/src/draw/lv_draw_layer.h
@@ -0,0 +1,83 @@
+/**
+ * @file lv_draw_layer.h
+ *
+ */
+
+#ifndef LV_DRAW_LAYER_H
+#define LV_DRAW_LAYER_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*********************
+ * INCLUDES
+ *********************/
+#include "../lv_conf_internal.h"
+
+/*********************
+ * DEFINES
+ *********************/
+
+/**********************
+ * TYPEDEFS
+ **********************/
+struct _lv_draw_ctx_t;
+struct _lv_draw_layer_ctx_t;
+
+typedef enum {
+ LV_DRAW_LAYER_FLAG_NONE,
+ LV_DRAW_LAYER_FLAG_HAS_ALPHA,
+ LV_DRAW_LAYER_FLAG_CAN_SUBDIVIDE,
+} lv_draw_layer_flags_t;
+
+/**********************
+ * GLOBAL PROTOTYPES
+ **********************/
+
+/**
+ * Create a new layer context. It is used to start and independent rendering session
+ * with the current draw_ctx
+ * @param draw_ctx pointer to the current draw context
+ * @param layer_area the coordinates of the layer
+ * @param flags OR-ed flags from @lv_draw_layer_flags_t
+ * @return pointer to the layer context, or NULL on error
+ */
+struct _lv_draw_layer_ctx_t * lv_draw_layer_create(struct _lv_draw_ctx_t * draw_ctx, const lv_area_t * layer_area,
+ lv_draw_layer_flags_t flags);
+
+/**
+ * Adjust the layer_ctx and/or draw_ctx based on the `layer_ctx->area_act`.
+ * It's called only if flags has `LV_DRAW_LAYER_FLAG_CAN_SUBDIVIDE`
+ * @param draw_ctx pointer to the current draw context
+ * @param layer_ctx pointer to a layer context
+ * @param flags OR-ed flags from @lv_draw_layer_flags_t
+ */
+void lv_draw_layer_adjust(struct _lv_draw_ctx_t * draw_ctx, struct _lv_draw_layer_ctx_t * layer_ctx,
+ lv_draw_layer_flags_t flags);
+
+/**
+ * Blend a rendered layer to `layer_ctx->area_act`
+ * @param draw_ctx pointer to the current draw context
+ * @param layer_ctx pointer to a layer context
+ * @param draw_dsc pointer to an image draw descriptor
+ */
+void lv_draw_layer_blend(struct _lv_draw_ctx_t * draw_ctx, struct _lv_draw_layer_ctx_t * layer_ctx,
+ lv_draw_img_dsc_t * draw_dsc);
+
+/**
+ * Destroy a layer context.
+ * @param draw_ctx pointer to the current draw context
+ * @param layer_ctx pointer to a layer context
+ */
+void lv_draw_layer_destroy(struct _lv_draw_ctx_t * draw_ctx, struct _lv_draw_layer_ctx_t * layer_ctx);
+
+/**********************
+ * MACROS
+ **********************/
+
+#ifdef __cplusplus
+} /*extern "C"*/
+#endif
+
+#endif /*LV_DRAW_LAYER_H*/
diff --git a/lib/lvgl/src/draw/lv_draw_line.c b/lib/lvgl/src/draw/lv_draw_line.c
new file mode 100644
index 00000000..b4844dbd
--- /dev/null
+++ b/lib/lvgl/src/draw/lv_draw_line.c
@@ -0,0 +1,56 @@
+/**
+ * @file lv_draw_line.c
+ *
+ */
+
+/*********************
+ * INCLUDES
+ *********************/
+#include <stdbool.h>
+#include "../core/lv_refr.h"
+#include "../misc/lv_math.h"
+
+/*********************
+ * DEFINES
+ *********************/
+
+/**********************
+ * TYPEDEFS
+ **********************/
+
+/**********************
+ * STATIC PROTOTYPES
+ **********************/
+
+/**********************
+ * STATIC VARIABLES
+ **********************/
+
+/**********************
+ * MACROS
+ **********************/
+
+/**********************
+ * GLOBAL FUNCTIONS
+ **********************/
+
+LV_ATTRIBUTE_FAST_MEM void lv_draw_line_dsc_init(lv_draw_line_dsc_t * dsc)
+{
+ lv_memset_00(dsc, sizeof(lv_draw_line_dsc_t));
+ dsc->width = 1;
+ dsc->opa = LV_OPA_COVER;
+ dsc->color = lv_color_black();
+}
+
+LV_ATTRIBUTE_FAST_MEM void lv_draw_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;
+
+ draw_ctx->draw_line(draw_ctx, dsc, point1, point2);
+}
+
+/**********************
+ * STATIC FUNCTIONS
+ **********************/
diff --git a/lib/lvgl/src/draw/lv_draw_line.h b/lib/lvgl/src/draw/lv_draw_line.h
new file mode 100644
index 00000000..d82ea51b
--- /dev/null
+++ b/lib/lvgl/src/draw/lv_draw_line.h
@@ -0,0 +1,67 @@
+/**
+ * @file lv_draw_line.h
+ *
+ */
+
+#ifndef LV_DRAW_LINE_H
+#define LV_DRAW_LINE_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*********************
+ * INCLUDES
+ *********************/
+#include "../lv_conf_internal.h"
+#include "../misc/lv_color.h"
+#include "../misc/lv_area.h"
+#include "../misc/lv_style.h"
+
+/*********************
+ * DEFINES
+ *********************/
+
+/**********************
+ * TYPEDEFS
+ **********************/
+typedef struct {
+ lv_color_t color;
+ lv_coord_t width;
+ lv_coord_t dash_width;
+ lv_coord_t dash_gap;
+ lv_opa_t opa;
+ lv_blend_mode_t blend_mode : 2;
+ uint8_t round_start : 1;
+ uint8_t round_end : 1;
+ uint8_t raw_end : 1; /*Do not bother with perpendicular line ending if it's not visible for any reason*/
+} lv_draw_line_dsc_t;
+
+struct _lv_draw_ctx_t;
+
+/**********************
+ * GLOBAL PROTOTYPES
+ **********************/
+
+LV_ATTRIBUTE_FAST_MEM void lv_draw_line_dsc_init(lv_draw_line_dsc_t * dsc);
+
+/**
+ * Draw a line
+ * @param point1 first point of the line
+ * @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
+ */
+void lv_draw_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);
+
+
+/**********************
+ * MACROS
+ **********************/
+
+#ifdef __cplusplus
+} /*extern "C"*/
+#endif
+
+#endif /*LV_DRAW_LINE_H*/
diff --git a/lib/lvgl/src/draw/lv_draw_mask.c b/lib/lvgl/src/draw/lv_draw_mask.c
new file mode 100644
index 00000000..2170da94
--- /dev/null
+++ b/lib/lvgl/src/draw/lv_draw_mask.c
@@ -0,0 +1,1530 @@
+/**
+ * @file lv_mask.c
+ *
+ */
+
+/*********************
+ * INCLUDES
+ *********************/
+#include "lv_draw.h"
+#if LV_DRAW_COMPLEX
+#include "../misc/lv_math.h"
+#include "../misc/lv_log.h"
+#include "../misc/lv_assert.h"
+#include "../misc/lv_gc.h"
+
+/*********************
+ * DEFINES
+ *********************/
+#define CIRCLE_CACHE_LIFE_MAX 1000
+#define CIRCLE_CACHE_AGING(life, r) life = LV_MIN(life + (r < 16 ? 1 : (r >> 4)), 1000)
+
+/**********************
+ * TYPEDEFS
+ **********************/
+
+/**********************
+ * STATIC PROTOTYPES
+ **********************/
+LV_ATTRIBUTE_FAST_MEM static lv_draw_mask_res_t lv_draw_mask_line(lv_opa_t * mask_buf, lv_coord_t abs_x,
+ lv_coord_t abs_y, lv_coord_t len,
+ lv_draw_mask_line_param_t * param);
+LV_ATTRIBUTE_FAST_MEM static lv_draw_mask_res_t lv_draw_mask_radius(lv_opa_t * mask_buf, lv_coord_t abs_x,
+ lv_coord_t abs_y, lv_coord_t len,
+ lv_draw_mask_radius_param_t * param);
+LV_ATTRIBUTE_FAST_MEM static lv_draw_mask_res_t lv_draw_mask_angle(lv_opa_t * mask_buf, lv_coord_t abs_x,
+ lv_coord_t abs_y, lv_coord_t len,
+ lv_draw_mask_angle_param_t * param);
+LV_ATTRIBUTE_FAST_MEM static lv_draw_mask_res_t lv_draw_mask_fade(lv_opa_t * mask_buf, lv_coord_t abs_x,
+ lv_coord_t abs_y, lv_coord_t len,
+ lv_draw_mask_fade_param_t * param);
+LV_ATTRIBUTE_FAST_MEM static lv_draw_mask_res_t lv_draw_mask_map(lv_opa_t * mask_buf, lv_coord_t abs_x,
+ lv_coord_t abs_y, lv_coord_t len,
+ lv_draw_mask_map_param_t * param);
+LV_ATTRIBUTE_FAST_MEM static lv_draw_mask_res_t lv_draw_mask_polygon(lv_opa_t * mask_buf, lv_coord_t abs_x,
+ lv_coord_t abs_y, lv_coord_t len,
+ lv_draw_mask_polygon_param_t * param);
+
+LV_ATTRIBUTE_FAST_MEM static lv_draw_mask_res_t line_mask_flat(lv_opa_t * mask_buf, lv_coord_t abs_x, lv_coord_t abs_y,
+ lv_coord_t len,
+ lv_draw_mask_line_param_t * p);
+LV_ATTRIBUTE_FAST_MEM static lv_draw_mask_res_t line_mask_steep(lv_opa_t * mask_buf, lv_coord_t abs_x, lv_coord_t abs_y,
+ lv_coord_t len,
+ lv_draw_mask_line_param_t * p);
+
+static void circ_init(lv_point_t * c, lv_coord_t * tmp, lv_coord_t radius);
+static bool circ_cont(lv_point_t * c);
+static void circ_next(lv_point_t * c, lv_coord_t * tmp);
+static void circ_calc_aa4(_lv_draw_mask_radius_circle_dsc_t * c, lv_coord_t radius);
+static lv_opa_t * get_next_line(_lv_draw_mask_radius_circle_dsc_t * c, lv_coord_t y, lv_coord_t * len,
+ lv_coord_t * x_start);
+LV_ATTRIBUTE_FAST_MEM static inline lv_opa_t mask_mix(lv_opa_t mask_act, lv_opa_t mask_new);
+
+/**********************
+ * STATIC VARIABLES
+ **********************/
+
+/**********************
+ * MACROS
+ **********************/
+
+/**********************
+ * GLOBAL FUNCTIONS
+ **********************/
+
+/**
+ * Add a draw mask. Everything drawn after it (until removing the mask) will be affected by the mask.
+ * @param param an initialized mask parameter. Only the pointer is saved.
+ * @param custom_id a custom pointer to identify the mask. Used in `lv_draw_mask_remove_custom`.
+ * @return the an integer, the ID of the mask. Can be used in `lv_draw_mask_remove_id`.
+ */
+int16_t lv_draw_mask_add(void * param, void * custom_id)
+{
+ /*Look for a free entry*/
+ uint8_t i;
+ for(i = 0; i < _LV_MASK_MAX_NUM; i++) {
+ if(LV_GC_ROOT(_lv_draw_mask_list[i]).param == NULL) break;
+ }
+
+ if(i >= _LV_MASK_MAX_NUM) {
+ LV_LOG_WARN("lv_mask_add: no place to add the mask");
+ return LV_MASK_ID_INV;
+ }
+
+ LV_GC_ROOT(_lv_draw_mask_list[i]).param = param;
+ LV_GC_ROOT(_lv_draw_mask_list[i]).custom_id = custom_id;
+
+ return i;
+}
+
+/**
+ * Apply the added buffers on a line. Used internally by the library's drawing routines.
+ * @param mask_buf store the result mask here. Has to be `len` byte long. Should be initialized with `0xFF`.
+ * @param abs_x absolute X coordinate where the line to calculate start
+ * @param abs_y absolute Y coordinate where the line to calculate start
+ * @param len length of the line to calculate (in pixel count)
+ * @return One of these values:
+ * - `LV_DRAW_MASK_RES_FULL_TRANSP`: the whole line is transparent. `mask_buf` is not set to zero
+ * - `LV_DRAW_MASK_RES_FULL_COVER`: the whole line is fully visible. `mask_buf` is unchanged
+ * - `LV_DRAW_MASK_RES_CHANGED`: `mask_buf` has changed, it shows the desired opacity of each pixel in the given line
+ */
+LV_ATTRIBUTE_FAST_MEM lv_draw_mask_res_t lv_draw_mask_apply(lv_opa_t * mask_buf, lv_coord_t abs_x, lv_coord_t abs_y,
+ lv_coord_t len)
+{
+ bool changed = false;
+ _lv_draw_mask_common_dsc_t * dsc;
+
+ _lv_draw_mask_saved_t * m = LV_GC_ROOT(_lv_draw_mask_list);
+
+ while(m->param) {
+ dsc = m->param;
+ lv_draw_mask_res_t res = LV_DRAW_MASK_RES_FULL_COVER;
+ res = dsc->cb(mask_buf, abs_x, abs_y, len, (void *)m->param);
+ if(res == LV_DRAW_MASK_RES_TRANSP) return LV_DRAW_MASK_RES_TRANSP;
+ else if(res == LV_DRAW_MASK_RES_CHANGED) changed = true;
+
+ m++;
+ }
+
+ return changed ? LV_DRAW_MASK_RES_CHANGED : LV_DRAW_MASK_RES_FULL_COVER;
+}
+
+/**
+ * Apply the specified buffers on a line. Used internally by the library's drawing routines.
+ * @param mask_buf store the result mask here. Has to be `len` byte long. Should be initialized with `0xFF`.
+ * @param abs_x absolute X coordinate where the line to calculate start
+ * @param abs_y absolute Y coordinate where the line to calculate start
+ * @param len length of the line to calculate (in pixel count)
+ * @param ids ID array of added buffers
+ * @param ids_count number of ID array
+ * @return One of these values:
+ * - `LV_DRAW_MASK_RES_FULL_TRANSP`: the whole line is transparent. `mask_buf` is not set to zero
+ * - `LV_DRAW_MASK_RES_FULL_COVER`: the whole line is fully visible. `mask_buf` is unchanged
+ * - `LV_DRAW_MASK_RES_CHANGED`: `mask_buf` has changed, it shows the desired opacity of each pixel in the given line
+ */
+LV_ATTRIBUTE_FAST_MEM lv_draw_mask_res_t lv_draw_mask_apply_ids(lv_opa_t * mask_buf, lv_coord_t abs_x, lv_coord_t abs_y,
+ lv_coord_t len, const int16_t * ids, int16_t ids_count)
+{
+ bool changed = false;
+ _lv_draw_mask_common_dsc_t * dsc;
+
+ for(int i = 0; i < ids_count; i++) {
+ int16_t id = ids[i];
+ if(id == LV_MASK_ID_INV) continue;
+ dsc = LV_GC_ROOT(_lv_draw_mask_list[id]).param;
+ if(!dsc) continue;
+ lv_draw_mask_res_t res = LV_DRAW_MASK_RES_FULL_COVER;
+ res = dsc->cb(mask_buf, abs_x, abs_y, len, dsc);
+ if(res == LV_DRAW_MASK_RES_TRANSP) return LV_DRAW_MASK_RES_TRANSP;
+ else if(res == LV_DRAW_MASK_RES_CHANGED) changed = true;
+ }
+
+ return changed ? LV_DRAW_MASK_RES_CHANGED : LV_DRAW_MASK_RES_FULL_COVER;
+}
+
+/**
+ * Remove a mask with a given ID
+ * @param id the ID of the mask. Returned by `lv_draw_mask_add`
+ * @return the parameter of the removed mask.
+ * If more masks have `custom_id` ID then the last mask's parameter will be returned
+ */
+void * lv_draw_mask_remove_id(int16_t id)
+{
+ _lv_draw_mask_common_dsc_t * p = NULL;
+
+ if(id != LV_MASK_ID_INV) {
+ p = LV_GC_ROOT(_lv_draw_mask_list[id]).param;
+ LV_GC_ROOT(_lv_draw_mask_list[id]).param = NULL;
+ LV_GC_ROOT(_lv_draw_mask_list[id]).custom_id = NULL;
+ }
+
+ return p;
+}
+
+/**
+ * Remove all mask with a given custom ID
+ * @param custom_id a pointer used in `lv_draw_mask_add`
+ * @return return the parameter of the removed mask.
+ * If more masks have `custom_id` ID then the last mask's parameter will be returned
+ */
+void * lv_draw_mask_remove_custom(void * custom_id)
+{
+ _lv_draw_mask_common_dsc_t * p = NULL;
+ uint8_t i;
+ for(i = 0; i < _LV_MASK_MAX_NUM; i++) {
+ if(LV_GC_ROOT(_lv_draw_mask_list[i]).custom_id == custom_id) {
+ p = LV_GC_ROOT(_lv_draw_mask_list[i]).param;
+ lv_draw_mask_remove_id(i);
+ }
+ }
+ return p;
+}
+
+/**
+ * Free the data from the parameter.
+ * It's called inside `lv_draw_mask_remove_id` and `lv_draw_mask_remove_custom`
+ * Needs to be called only in special cases when the mask is not added by `lv_draw_mask_add`
+ * and not removed by `lv_draw_mask_remove_id` or `lv_draw_mask_remove_custom`
+ * @param p pointer to a mask parameter
+ */
+void lv_draw_mask_free_param(void * p)
+{
+ _lv_draw_mask_common_dsc_t * pdsc = p;
+ if(pdsc->type == LV_DRAW_MASK_TYPE_RADIUS) {
+ lv_draw_mask_radius_param_t * radius_p = (lv_draw_mask_radius_param_t *) p;
+ if(radius_p->circle) {
+ if(radius_p->circle->life < 0) {
+ lv_mem_free(radius_p->circle->cir_opa);
+ lv_mem_free(radius_p->circle);
+ }
+ else {
+ radius_p->circle->used_cnt--;
+ }
+ }
+ }
+ else if(pdsc->type == LV_DRAW_MASK_TYPE_POLYGON) {
+ lv_draw_mask_polygon_param_t * poly_p = (lv_draw_mask_polygon_param_t *) p;
+ lv_mem_free(poly_p->cfg.points);
+ }
+}
+
+void _lv_draw_mask_cleanup(void)
+{
+ uint8_t i;
+ for(i = 0; i < LV_CIRCLE_CACHE_SIZE; i++) {
+ if(LV_GC_ROOT(_lv_circle_cache[i]).buf) {
+ lv_mem_free(LV_GC_ROOT(_lv_circle_cache[i]).buf);
+ }
+ lv_memset_00(&LV_GC_ROOT(_lv_circle_cache[i]), sizeof(LV_GC_ROOT(_lv_circle_cache[i])));
+ }
+}
+
+/**
+ * Count the currently added masks
+ * @return number of active masks
+ */
+LV_ATTRIBUTE_FAST_MEM uint8_t lv_draw_mask_get_cnt(void)
+{
+ uint8_t cnt = 0;
+ uint8_t i;
+ for(i = 0; i < _LV_MASK_MAX_NUM; i++) {
+ if(LV_GC_ROOT(_lv_draw_mask_list[i]).param) cnt++;
+ }
+ return cnt;
+}
+
+bool lv_draw_mask_is_any(const lv_area_t * a)
+{
+ if(a == NULL) return LV_GC_ROOT(_lv_draw_mask_list[0]).param ? true : false;
+
+ uint8_t i;
+ for(i = 0; i < _LV_MASK_MAX_NUM; i++) {
+ _lv_draw_mask_common_dsc_t * comm_param = LV_GC_ROOT(_lv_draw_mask_list[i]).param;
+ if(comm_param == NULL) continue;
+ if(comm_param->type == LV_DRAW_MASK_TYPE_RADIUS) {
+ lv_draw_mask_radius_param_t * radius_param = LV_GC_ROOT(_lv_draw_mask_list[i]).param;
+ if(radius_param->cfg.outer) {
+ if(!_lv_area_is_out(a, &radius_param->cfg.rect, radius_param->cfg.radius)) return true;
+ }
+ else {
+ if(!_lv_area_is_in(a, &radius_param->cfg.rect, radius_param->cfg.radius)) return true;
+ }
+ }
+ else {
+ return true;
+ }
+ }
+
+ return false;
+
+}
+
+/**
+ *Initialize a line mask from two points.
+ * @param param pointer to a `lv_draw_mask_param_t` to initialize
+ * @param p1x X coordinate of the first point of the line
+ * @param p1y Y coordinate of the first point of the line
+ * @param p2x X coordinate of the second point of the line
+ * @param p2y y coordinate of the second point of the line
+ * @param side and element of `lv_draw_mask_line_side_t` to describe which side to keep.
+ * With `LV_DRAW_MASK_LINE_SIDE_LEFT/RIGHT` and horizontal line all pixels are kept
+ * With `LV_DRAW_MASK_LINE_SIDE_TOP/BOTTOM` and vertical line all pixels are kept
+ */
+void lv_draw_mask_line_points_init(lv_draw_mask_line_param_t * param, lv_coord_t p1x, lv_coord_t p1y, lv_coord_t p2x,
+ lv_coord_t p2y, lv_draw_mask_line_side_t side)
+{
+ lv_memset_00(param, sizeof(lv_draw_mask_line_param_t));
+
+ if(p1y == p2y && side == LV_DRAW_MASK_LINE_SIDE_BOTTOM) {
+ p1y--;
+ p2y--;
+ }
+
+ if(p1y > p2y) {
+ lv_coord_t t;
+ t = p2x;
+ p2x = p1x;
+ p1x = t;
+
+ t = p2y;
+ p2y = p1y;
+ p1y = t;
+ }
+
+ param->cfg.p1.x = p1x;
+ param->cfg.p1.y = p1y;
+ param->cfg.p2.x = p2x;
+ param->cfg.p2.y = p2y;
+ param->cfg.side = side;
+
+ param->origo.x = p1x;
+ param->origo.y = p1y;
+ param->flat = (LV_ABS(p2x - p1x) > LV_ABS(p2y - p1y)) ? 1 : 0;
+ param->yx_steep = 0;
+ param->xy_steep = 0;
+ param->dsc.cb = (lv_draw_mask_xcb_t)lv_draw_mask_line;
+ param->dsc.type = LV_DRAW_MASK_TYPE_LINE;
+
+ int32_t dx = p2x - p1x;
+ int32_t dy = p2y - p1y;
+
+ if(param->flat) {
+ /*Normalize the steep. Delta x should be relative to delta x = 1024*/
+ int32_t m;
+
+ if(dx) {
+ m = (1L << 20) / dx; /*m is multiplier to normalize y (upscaled by 1024)*/
+ param->yx_steep = (m * dy) >> 10;
+ }
+
+ if(dy) {
+ m = (1L << 20) / dy; /*m is multiplier to normalize x (upscaled by 1024)*/
+ param->xy_steep = (m * dx) >> 10;
+ }
+ param->steep = param->yx_steep;
+ }
+ else {
+ /*Normalize the steep. Delta y should be relative to delta x = 1024*/
+ int32_t m;
+
+ if(dy) {
+ m = (1L << 20) / dy; /*m is multiplier to normalize x (upscaled by 1024)*/
+ param->xy_steep = (m * dx) >> 10;
+ }
+
+ if(dx) {
+ m = (1L << 20) / dx; /*m is multiplier to normalize x (upscaled by 1024)*/
+ param->yx_steep = (m * dy) >> 10;
+ }
+ param->steep = param->xy_steep;
+ }
+
+ if(param->cfg.side == LV_DRAW_MASK_LINE_SIDE_LEFT) param->inv = 0;
+ else if(param->cfg.side == LV_DRAW_MASK_LINE_SIDE_RIGHT) param->inv = 1;
+ else if(param->cfg.side == LV_DRAW_MASK_LINE_SIDE_TOP) {
+ if(param->steep > 0) param->inv = 1;
+ else param->inv = 0;
+ }
+ else if(param->cfg.side == LV_DRAW_MASK_LINE_SIDE_BOTTOM) {
+ if(param->steep > 0) param->inv = 0;
+ else param->inv = 1;
+ }
+
+ param->spx = param->steep >> 2;
+ if(param->steep < 0) param->spx = -param->spx;
+}
+
+/**
+ *Initialize a line mask from a point and an angle.
+ * @param param pointer to a `lv_draw_mask_param_t` to initialize
+ * @param px X coordinate of a point of the line
+ * @param py X coordinate of a point of the line
+ * @param angle right 0 deg, bottom: 90
+ * @param side and element of `lv_draw_mask_line_side_t` to describe which side to keep.
+ * With `LV_DRAW_MASK_LINE_SIDE_LEFT/RIGHT` and horizontal line all pixels are kept
+ * With `LV_DRAW_MASK_LINE_SIDE_TOP/BOTTOM` and vertical line all pixels are kept
+ */
+void lv_draw_mask_line_angle_init(lv_draw_mask_line_param_t * param, lv_coord_t p1x, lv_coord_t py, int16_t angle,
+ lv_draw_mask_line_side_t side)
+{
+ /*Find an optimal degree.
+ *lv_mask_line_points_init will swap the points to keep the smaller y in p1
+ *Theoretically a line with `angle` or `angle+180` is the same only the points are swapped
+ *Find the degree which keeps the origo in place*/
+ if(angle > 180) angle -= 180; /*> 180 will swap the origo*/
+
+ int32_t p2x;
+ int32_t p2y;
+
+ p2x = (lv_trigo_sin(angle + 90) >> 5) + p1x;
+ p2y = (lv_trigo_sin(angle) >> 5) + py;
+
+ lv_draw_mask_line_points_init(param, p1x, py, p2x, p2y, side);
+}
+
+/**
+ * Initialize an angle mask.
+ * @param param pointer to a `lv_draw_mask_param_t` to initialize
+ * @param vertex_x X coordinate of the angle vertex (absolute coordinates)
+ * @param vertex_y Y coordinate of the angle vertex (absolute coordinates)
+ * @param start_angle start angle in degrees. 0 deg on the right, 90 deg, on the bottom
+ * @param end_angle end angle
+ */
+void lv_draw_mask_angle_init(lv_draw_mask_angle_param_t * param, lv_coord_t vertex_x, lv_coord_t vertex_y,
+ lv_coord_t start_angle, lv_coord_t end_angle)
+{
+ lv_draw_mask_line_side_t start_side;
+ lv_draw_mask_line_side_t end_side;
+
+ /*Constrain the input angles*/
+ if(start_angle < 0)
+ start_angle = 0;
+ else if(start_angle > 359)
+ start_angle = 359;
+
+ if(end_angle < 0)
+ end_angle = 0;
+ else if(end_angle > 359)
+ end_angle = 359;
+
+ if(end_angle < start_angle) {
+ param->delta_deg = 360 - start_angle + end_angle;
+ }
+ else {
+ param->delta_deg = LV_ABS(end_angle - start_angle);
+ }
+
+ param->cfg.start_angle = start_angle;
+ param->cfg.end_angle = end_angle;
+ param->cfg.vertex_p.x = vertex_x;
+ param->cfg.vertex_p.y = vertex_y;
+ param->dsc.cb = (lv_draw_mask_xcb_t)lv_draw_mask_angle;
+ param->dsc.type = LV_DRAW_MASK_TYPE_ANGLE;
+
+ LV_ASSERT_MSG(start_angle >= 0 && start_angle <= 360, "Unexpected start angle");
+
+ if(start_angle >= 0 && start_angle < 180) {
+ start_side = LV_DRAW_MASK_LINE_SIDE_LEFT;
+ }
+ else
+ start_side = LV_DRAW_MASK_LINE_SIDE_RIGHT; /*silence compiler*/
+
+ LV_ASSERT_MSG(end_angle >= 0 && start_angle <= 360, "Unexpected end angle");
+
+ if(end_angle >= 0 && end_angle < 180) {
+ end_side = LV_DRAW_MASK_LINE_SIDE_RIGHT;
+ }
+ else if(end_angle >= 180 && end_angle < 360) {
+ end_side = LV_DRAW_MASK_LINE_SIDE_LEFT;
+ }
+ else
+ end_side = LV_DRAW_MASK_LINE_SIDE_RIGHT; /*silence compiler*/
+
+ lv_draw_mask_line_angle_init(&param->start_line, vertex_x, vertex_y, start_angle, start_side);
+ lv_draw_mask_line_angle_init(&param->end_line, vertex_x, vertex_y, end_angle, end_side);
+}
+
+/**
+ * Initialize a fade mask.
+ * @param param pointer to an `lv_draw_mask_radius_param_t` to initialize
+ * @param rect coordinates of the rectangle to affect (absolute coordinates)
+ * @param radius radius of the rectangle
+ * @param inv true: keep the pixels inside the rectangle; keep the pixels outside of the rectangle
+ */
+void lv_draw_mask_radius_init(lv_draw_mask_radius_param_t * param, const lv_area_t * rect, lv_coord_t radius, bool inv)
+{
+ lv_coord_t w = lv_area_get_width(rect);
+ lv_coord_t h = lv_area_get_height(rect);
+ int32_t short_side = LV_MIN(w, h);
+ if(radius > short_side >> 1) radius = short_side >> 1;
+ if(radius < 0) radius = 0;
+
+ lv_area_copy(&param->cfg.rect, rect);
+ param->cfg.radius = radius;
+ param->cfg.outer = inv ? 1 : 0;
+ param->dsc.cb = (lv_draw_mask_xcb_t)lv_draw_mask_radius;
+ param->dsc.type = LV_DRAW_MASK_TYPE_RADIUS;
+
+ if(radius == 0) {
+ param->circle = NULL;
+ return;
+ }
+
+ uint32_t i;
+
+ /*Try to reuse a circle cache entry*/
+ for(i = 0; i < LV_CIRCLE_CACHE_SIZE; i++) {
+ if(LV_GC_ROOT(_lv_circle_cache[i]).radius == radius) {
+ LV_GC_ROOT(_lv_circle_cache[i]).used_cnt++;
+ CIRCLE_CACHE_AGING(LV_GC_ROOT(_lv_circle_cache[i]).life, radius);
+ param->circle = &LV_GC_ROOT(_lv_circle_cache[i]);
+ return;
+ }
+ }
+
+ /*If not found find a free entry with lowest life*/
+ _lv_draw_mask_radius_circle_dsc_t * entry = NULL;
+ for(i = 0; i < LV_CIRCLE_CACHE_SIZE; i++) {
+ if(LV_GC_ROOT(_lv_circle_cache[i]).used_cnt == 0) {
+ if(!entry) entry = &LV_GC_ROOT(_lv_circle_cache[i]);
+ else if(LV_GC_ROOT(_lv_circle_cache[i]).life < entry->life) entry = &LV_GC_ROOT(_lv_circle_cache[i]);
+ }
+ }
+
+ if(!entry) {
+ entry = lv_mem_alloc(sizeof(_lv_draw_mask_radius_circle_dsc_t));
+ LV_ASSERT_MALLOC(entry);
+ lv_memset_00(entry, sizeof(_lv_draw_mask_radius_circle_dsc_t));
+ entry->life = -1;
+ }
+ else {
+ entry->used_cnt++;
+ entry->life = 0;
+ CIRCLE_CACHE_AGING(entry->life, radius);
+ }
+
+ param->circle = entry;
+
+ circ_calc_aa4(param->circle, radius);
+}
+
+/**
+ * Initialize a fade mask.
+ * @param param pointer to a `lv_draw_mask_param_t` to initialize
+ * @param coords coordinates of the area to affect (absolute coordinates)
+ * @param opa_top opacity on the top
+ * @param y_top at which coordinate start to change to opacity to `opa_bottom`
+ * @param opa_bottom opacity at the bottom
+ * @param y_bottom at which coordinate reach `opa_bottom`.
+ */
+void lv_draw_mask_fade_init(lv_draw_mask_fade_param_t * param, const lv_area_t * coords, lv_opa_t opa_top,
+ lv_coord_t y_top,
+ lv_opa_t opa_bottom, lv_coord_t y_bottom)
+{
+ lv_area_copy(&param->cfg.coords, coords);
+ param->cfg.opa_top = opa_top;
+ param->cfg.opa_bottom = opa_bottom;
+ param->cfg.y_top = y_top;
+ param->cfg.y_bottom = y_bottom;
+ param->dsc.cb = (lv_draw_mask_xcb_t)lv_draw_mask_fade;
+ param->dsc.type = LV_DRAW_MASK_TYPE_FADE;
+}
+
+/**
+ * Initialize a map mask.
+ * @param param pointer to a `lv_draw_mask_param_t` to initialize
+ * @param coords coordinates of the map (absolute coordinates)
+ * @param map array of bytes with the mask values
+ */
+void lv_draw_mask_map_init(lv_draw_mask_map_param_t * param, const lv_area_t * coords, const lv_opa_t * map)
+{
+ lv_area_copy(&param->cfg.coords, coords);
+ param->cfg.map = map;
+ param->dsc.cb = (lv_draw_mask_xcb_t)lv_draw_mask_map;
+ param->dsc.type = LV_DRAW_MASK_TYPE_MAP;
+}
+
+void lv_draw_mask_polygon_init(lv_draw_mask_polygon_param_t * param, const lv_point_t * points, uint16_t point_cnt)
+{
+ /*Join adjacent points if they are on the same coordinate*/
+ lv_point_t * p = lv_mem_alloc(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++;
+ }
+ param->cfg.points = p;
+ param->cfg.point_cnt = pcnt;
+ param->dsc.cb = (lv_draw_mask_xcb_t)lv_draw_mask_polygon;
+ param->dsc.type = LV_DRAW_MASK_TYPE_POLYGON;
+}
+
+/**********************
+ * STATIC FUNCTIONS
+ **********************/
+
+LV_ATTRIBUTE_FAST_MEM static lv_draw_mask_res_t lv_draw_mask_line(lv_opa_t * mask_buf, lv_coord_t abs_x,
+ lv_coord_t abs_y, lv_coord_t len,
+ lv_draw_mask_line_param_t * p)
+{
+ /*Make to points relative to the vertex*/
+ abs_y -= p->origo.y;
+ abs_x -= p->origo.x;
+
+ /*Handle special cases*/
+ if(p->steep == 0) {
+ /*Horizontal*/
+ if(p->flat) {
+ /*Non sense: Can't be on the right/left of a horizontal line*/
+ if(p->cfg.side == LV_DRAW_MASK_LINE_SIDE_LEFT ||
+ p->cfg.side == LV_DRAW_MASK_LINE_SIDE_RIGHT) return LV_DRAW_MASK_RES_FULL_COVER;
+ else if(p->cfg.side == LV_DRAW_MASK_LINE_SIDE_TOP && abs_y + 1 < 0) return LV_DRAW_MASK_RES_FULL_COVER;
+ else if(p->cfg.side == LV_DRAW_MASK_LINE_SIDE_BOTTOM && abs_y > 0) return LV_DRAW_MASK_RES_FULL_COVER;
+ else {
+ return LV_DRAW_MASK_RES_TRANSP;
+ }
+ }
+ /*Vertical*/
+ else {
+ /*Non sense: Can't be on the top/bottom of a vertical line*/
+ if(p->cfg.side == LV_DRAW_MASK_LINE_SIDE_TOP ||
+ p->cfg.side == LV_DRAW_MASK_LINE_SIDE_BOTTOM) return LV_DRAW_MASK_RES_FULL_COVER;
+ else if(p->cfg.side == LV_DRAW_MASK_LINE_SIDE_RIGHT && abs_x > 0) return LV_DRAW_MASK_RES_FULL_COVER;
+ else if(p->cfg.side == LV_DRAW_MASK_LINE_SIDE_LEFT) {
+ if(abs_x + len < 0) return LV_DRAW_MASK_RES_FULL_COVER;
+ else {
+ int32_t k = - abs_x;
+ if(k < 0) return LV_DRAW_MASK_RES_TRANSP;
+ if(k >= 0 && k < len) lv_memset_00(&mask_buf[k], len - k);
+ return LV_DRAW_MASK_RES_CHANGED;
+ }
+ }
+ else {
+ if(abs_x + len < 0) return LV_DRAW_MASK_RES_TRANSP;
+ else {
+ int32_t k = - abs_x;
+ if(k < 0) k = 0;
+ if(k >= len) return LV_DRAW_MASK_RES_TRANSP;
+ else if(k >= 0 && k < len) lv_memset_00(&mask_buf[0], k);
+ return LV_DRAW_MASK_RES_CHANGED;
+ }
+ }
+ }
+ }
+
+ lv_draw_mask_res_t res;
+ if(p->flat) {
+ res = line_mask_flat(mask_buf, abs_x, abs_y, len, p);
+ }
+ else {
+ res = line_mask_steep(mask_buf, abs_x, abs_y, len, p);
+ }
+
+ return res;
+}
+
+LV_ATTRIBUTE_FAST_MEM static lv_draw_mask_res_t line_mask_flat(lv_opa_t * mask_buf, lv_coord_t abs_x, lv_coord_t abs_y,
+ lv_coord_t len,
+ lv_draw_mask_line_param_t * p)
+{
+
+ int32_t y_at_x;
+ y_at_x = (int32_t)((int32_t)p->yx_steep * abs_x) >> 10;
+
+ if(p->yx_steep > 0) {
+ if(y_at_x > abs_y) {
+ if(p->inv) {
+ return LV_DRAW_MASK_RES_FULL_COVER;
+ }
+ else {
+ return LV_DRAW_MASK_RES_TRANSP;
+ }
+ }
+ }
+ else {
+ if(y_at_x < abs_y) {
+ if(p->inv) {
+ return LV_DRAW_MASK_RES_FULL_COVER;
+ }
+ else {
+ return LV_DRAW_MASK_RES_TRANSP;
+ }
+ }
+ }
+
+ /*At the end of the mask if the limit line is smaller than the mask's y.
+ *Then the mask is in the "good" area*/
+ y_at_x = (int32_t)((int32_t)p->yx_steep * (abs_x + len)) >> 10;
+ if(p->yx_steep > 0) {
+ if(y_at_x < abs_y) {
+ if(p->inv) {
+ return LV_DRAW_MASK_RES_TRANSP;
+ }
+ else {
+ return LV_DRAW_MASK_RES_FULL_COVER;
+ }
+ }
+ }
+ else {
+ if(y_at_x > abs_y) {
+ if(p->inv) {
+ return LV_DRAW_MASK_RES_TRANSP;
+ }
+ else {
+ return LV_DRAW_MASK_RES_FULL_COVER;
+ }
+ }
+ }
+
+ int32_t xe;
+ if(p->yx_steep > 0) xe = ((abs_y * 256) * p->xy_steep) >> 10;
+ else xe = (((abs_y + 1) * 256) * p->xy_steep) >> 10;
+
+ int32_t xei = xe >> 8;
+ int32_t xef = xe & 0xFF;
+
+ int32_t px_h;
+ if(xef == 0) px_h = 255;
+ else px_h = 255 - (((255 - xef) * p->spx) >> 8);
+ int32_t k = xei - abs_x;
+ lv_opa_t m;
+
+ if(xef) {
+ if(k >= 0 && k < len) {
+ m = 255 - (((255 - xef) * (255 - px_h)) >> 9);
+ if(p->inv) m = 255 - m;
+ mask_buf[k] = mask_mix(mask_buf[k], m);
+ }
+ k++;
+ }
+
+ while(px_h > p->spx) {
+ if(k >= 0 && k < len) {
+ m = px_h - (p->spx >> 1);
+ if(p->inv) m = 255 - m;
+ mask_buf[k] = mask_mix(mask_buf[k], m);
+ }
+ px_h -= p->spx;
+ k++;
+ if(k >= len) break;
+ }
+
+ if(k < len && k >= 0) {
+ int32_t x_inters = (px_h * p->xy_steep) >> 10;
+ m = (x_inters * px_h) >> 9;
+ if(p->yx_steep < 0) m = 255 - m;
+ if(p->inv) m = 255 - m;
+ mask_buf[k] = mask_mix(mask_buf[k], m);
+ }
+
+ if(p->inv) {
+ k = xei - abs_x;
+ if(k > len) {
+ return LV_DRAW_MASK_RES_TRANSP;
+ }
+ if(k >= 0) {
+ lv_memset_00(&mask_buf[0], k);
+ }
+ }
+ else {
+ k++;
+ if(k < 0) {
+ return LV_DRAW_MASK_RES_TRANSP;
+ }
+ if(k <= len) {
+ lv_memset_00(&mask_buf[k], len - k);
+ }
+ }
+
+ return LV_DRAW_MASK_RES_CHANGED;
+}
+
+LV_ATTRIBUTE_FAST_MEM static lv_draw_mask_res_t line_mask_steep(lv_opa_t * mask_buf, lv_coord_t abs_x, lv_coord_t abs_y,
+ lv_coord_t len,
+ lv_draw_mask_line_param_t * p)
+{
+ int32_t k;
+ int32_t x_at_y;
+ /*At the beginning of the mask if the limit line is greater than the mask's y.
+ *Then the mask is in the "wrong" area*/
+ x_at_y = (int32_t)((int32_t)p->xy_steep * abs_y) >> 10;
+ if(p->xy_steep > 0) x_at_y++;
+ if(x_at_y < abs_x) {
+ if(p->inv) {
+ return LV_DRAW_MASK_RES_FULL_COVER;
+ }
+ else {
+ return LV_DRAW_MASK_RES_TRANSP;
+ }
+ }
+
+ /*At the end of the mask if the limit line is smaller than the mask's y.
+ *Then the mask is in the "good" area*/
+ x_at_y = (int32_t)((int32_t)p->xy_steep * (abs_y)) >> 10;
+ if(x_at_y > abs_x + len) {
+ if(p->inv) {
+ return LV_DRAW_MASK_RES_TRANSP;
+ }
+ else {
+ return LV_DRAW_MASK_RES_FULL_COVER;
+ }
+ }
+
+ /*X start*/
+ int32_t xs = ((abs_y * 256) * p->xy_steep) >> 10;
+ int32_t xsi = xs >> 8;
+ int32_t xsf = xs & 0xFF;
+
+ /*X end*/
+ int32_t xe = (((abs_y + 1) * 256) * p->xy_steep) >> 10;
+ int32_t xei = xe >> 8;
+ int32_t xef = xe & 0xFF;
+
+ lv_opa_t m;
+
+ k = xsi - abs_x;
+ if(xsi != xei && (p->xy_steep < 0 && xsf == 0)) {
+ xsf = 0xFF;
+ xsi = xei;
+ k--;
+ }
+
+ if(xsi == xei) {
+ if(k >= 0 && k < len) {
+ m = (xsf + xef) >> 1;
+ if(p->inv) m = 255 - m;
+ mask_buf[k] = mask_mix(mask_buf[k], m);
+ }
+ k++;
+
+ if(p->inv) {
+ k = xsi - abs_x;
+ if(k >= len) {
+ return LV_DRAW_MASK_RES_TRANSP;
+ }
+ if(k >= 0) lv_memset_00(&mask_buf[0], k);
+
+ }
+ else {
+ if(k > len) k = len;
+ if(k == 0) return LV_DRAW_MASK_RES_TRANSP;
+ else if(k > 0) lv_memset_00(&mask_buf[k], len - k);
+ }
+
+ }
+ else {
+ int32_t y_inters;
+ if(p->xy_steep < 0) {
+ y_inters = (xsf * (-p->yx_steep)) >> 10;
+ if(k >= 0 && k < len) {
+ m = (y_inters * xsf) >> 9;
+ if(p->inv) m = 255 - m;
+ mask_buf[k] = mask_mix(mask_buf[k], m);
+ }
+ k--;
+
+ int32_t x_inters = ((255 - y_inters) * (-p->xy_steep)) >> 10;
+
+ if(k >= 0 && k < len) {
+ m = 255 - (((255 - y_inters) * x_inters) >> 9);
+ if(p->inv) m = 255 - m;
+ mask_buf[k] = mask_mix(mask_buf[k], m);
+ }
+
+ k += 2;
+
+ if(p->inv) {
+ k = xsi - abs_x - 1;
+
+ if(k > len) k = len;
+ else if(k > 0) lv_memset_00(&mask_buf[0], k);
+
+ }
+ else {
+ if(k > len) return LV_DRAW_MASK_RES_FULL_COVER;
+ if(k >= 0) lv_memset_00(&mask_buf[k], len - k);
+ }
+
+ }
+ else {
+ y_inters = ((255 - xsf) * p->yx_steep) >> 10;
+ if(k >= 0 && k < len) {
+ m = 255 - ((y_inters * (255 - xsf)) >> 9);
+ if(p->inv) m = 255 - m;
+ mask_buf[k] = mask_mix(mask_buf[k], m);
+ }
+
+ k++;
+
+ int32_t x_inters = ((255 - y_inters) * p->xy_steep) >> 10;
+ if(k >= 0 && k < len) {
+ m = ((255 - y_inters) * x_inters) >> 9;
+ if(p->inv) m = 255 - m;
+ mask_buf[k] = mask_mix(mask_buf[k], m);
+ }
+ k++;
+
+ if(p->inv) {
+ k = xsi - abs_x;
+ if(k > len) return LV_DRAW_MASK_RES_TRANSP;
+ if(k >= 0) lv_memset_00(&mask_buf[0], k);
+
+ }
+ else {
+ if(k > len) k = len;
+ if(k == 0) return LV_DRAW_MASK_RES_TRANSP;
+ else if(k > 0) lv_memset_00(&mask_buf[k], len - k);
+ }
+ }
+ }
+
+ return LV_DRAW_MASK_RES_CHANGED;
+}
+
+LV_ATTRIBUTE_FAST_MEM static lv_draw_mask_res_t lv_draw_mask_angle(lv_opa_t * mask_buf, lv_coord_t abs_x,
+ lv_coord_t abs_y, lv_coord_t len,
+ lv_draw_mask_angle_param_t * p)
+{
+ int32_t rel_y = abs_y - p->cfg.vertex_p.y;
+ int32_t rel_x = abs_x - p->cfg.vertex_p.x;
+
+ if(p->cfg.start_angle < 180 && p->cfg.end_angle < 180 &&
+ p->cfg.start_angle != 0 && p->cfg.end_angle != 0 &&
+ p->cfg.start_angle > p->cfg.end_angle) {
+
+ if(abs_y < p->cfg.vertex_p.y) {
+ return LV_DRAW_MASK_RES_FULL_COVER;
+ }
+
+ /*Start angle mask can work only from the end of end angle mask*/
+ int32_t end_angle_first = (rel_y * p->end_line.xy_steep) >> 10;
+ int32_t start_angle_last = ((rel_y + 1) * p->start_line.xy_steep) >> 10;
+
+ /*Do not let the line end cross the vertex else it will affect the opposite part*/
+ if(p->cfg.start_angle > 270 && p->cfg.start_angle <= 359 && start_angle_last < 0) start_angle_last = 0;
+ else if(p->cfg.start_angle > 0 && p->cfg.start_angle <= 90 && start_angle_last < 0) start_angle_last = 0;
+ else if(p->cfg.start_angle > 90 && p->cfg.start_angle < 270 && start_angle_last > 0) start_angle_last = 0;
+
+ if(p->cfg.end_angle > 270 && p->cfg.end_angle <= 359 && start_angle_last < 0) start_angle_last = 0;
+ else if(p->cfg.end_angle > 0 && p->cfg.end_angle <= 90 && start_angle_last < 0) start_angle_last = 0;
+ else if(p->cfg.end_angle > 90 && p->cfg.end_angle < 270 && start_angle_last > 0) start_angle_last = 0;
+
+ int32_t dist = (end_angle_first - start_angle_last) >> 1;
+
+ lv_draw_mask_res_t res1 = LV_DRAW_MASK_RES_FULL_COVER;
+ lv_draw_mask_res_t res2 = LV_DRAW_MASK_RES_FULL_COVER;
+
+ int32_t tmp = start_angle_last + dist - rel_x;
+ if(tmp > len) tmp = len;
+ if(tmp > 0) {
+ res1 = lv_draw_mask_line(&mask_buf[0], abs_x, abs_y, tmp, &p->start_line);
+ if(res1 == LV_DRAW_MASK_RES_TRANSP) {
+ lv_memset_00(&mask_buf[0], tmp);
+ }
+ }
+
+ if(tmp > len) tmp = len;
+ if(tmp < 0) tmp = 0;
+ res2 = lv_draw_mask_line(&mask_buf[tmp], abs_x + tmp, abs_y, len - tmp, &p->end_line);
+ if(res2 == LV_DRAW_MASK_RES_TRANSP) {
+ lv_memset_00(&mask_buf[tmp], len - tmp);
+ }
+ if(res1 == res2) return res1;
+ else return LV_DRAW_MASK_RES_CHANGED;
+ }
+ else if(p->cfg.start_angle > 180 && p->cfg.end_angle > 180 && p->cfg.start_angle > p->cfg.end_angle) {
+
+ if(abs_y > p->cfg.vertex_p.y) {
+ return LV_DRAW_MASK_RES_FULL_COVER;
+ }
+
+ /*Start angle mask can work only from the end of end angle mask*/
+ int32_t end_angle_first = (rel_y * p->end_line.xy_steep) >> 10;
+ int32_t start_angle_last = ((rel_y + 1) * p->start_line.xy_steep) >> 10;
+
+ /*Do not let the line end cross the vertex else it will affect the opposite part*/
+ if(p->cfg.start_angle > 270 && p->cfg.start_angle <= 359 && start_angle_last < 0) start_angle_last = 0;
+ else if(p->cfg.start_angle > 0 && p->cfg.start_angle <= 90 && start_angle_last < 0) start_angle_last = 0;
+ else if(p->cfg.start_angle > 90 && p->cfg.start_angle < 270 && start_angle_last > 0) start_angle_last = 0;
+
+ if(p->cfg.end_angle > 270 && p->cfg.end_angle <= 359 && start_angle_last < 0) start_angle_last = 0;
+ else if(p->cfg.end_angle > 0 && p->cfg.end_angle <= 90 && start_angle_last < 0) start_angle_last = 0;
+ else if(p->cfg.end_angle > 90 && p->cfg.end_angle < 270 && start_angle_last > 0) start_angle_last = 0;
+
+ int32_t dist = (end_angle_first - start_angle_last) >> 1;
+
+ lv_draw_mask_res_t res1 = LV_DRAW_MASK_RES_FULL_COVER;
+ lv_draw_mask_res_t res2 = LV_DRAW_MASK_RES_FULL_COVER;
+
+ int32_t tmp = start_angle_last + dist - rel_x;
+ if(tmp > len) tmp = len;
+ if(tmp > 0) {
+ res1 = lv_draw_mask_line(&mask_buf[0], abs_x, abs_y, tmp, (lv_draw_mask_line_param_t *)&p->end_line);
+ if(res1 == LV_DRAW_MASK_RES_TRANSP) {
+ lv_memset_00(&mask_buf[0], tmp);
+ }
+ }
+
+ if(tmp > len) tmp = len;
+ if(tmp < 0) tmp = 0;
+ res2 = lv_draw_mask_line(&mask_buf[tmp], abs_x + tmp, abs_y, len - tmp, (lv_draw_mask_line_param_t *)&p->start_line);
+ if(res2 == LV_DRAW_MASK_RES_TRANSP) {
+ lv_memset_00(&mask_buf[tmp], len - tmp);
+ }
+ if(res1 == res2) return res1;
+ else return LV_DRAW_MASK_RES_CHANGED;
+ }
+ else {
+
+ lv_draw_mask_res_t res1 = LV_DRAW_MASK_RES_FULL_COVER;
+ lv_draw_mask_res_t res2 = LV_DRAW_MASK_RES_FULL_COVER;
+
+ if(p->cfg.start_angle == 180) {
+ if(abs_y < p->cfg.vertex_p.y) res1 = LV_DRAW_MASK_RES_FULL_COVER;
+ else res1 = LV_DRAW_MASK_RES_UNKNOWN;
+ }
+ else if(p->cfg.start_angle == 0) {
+ if(abs_y < p->cfg.vertex_p.y) res1 = LV_DRAW_MASK_RES_UNKNOWN;
+ else res1 = LV_DRAW_MASK_RES_FULL_COVER;
+ }
+ else if((p->cfg.start_angle < 180 && abs_y < p->cfg.vertex_p.y) ||
+ (p->cfg.start_angle > 180 && abs_y >= p->cfg.vertex_p.y)) {
+ res1 = LV_DRAW_MASK_RES_UNKNOWN;
+ }
+ else {
+ res1 = lv_draw_mask_line(mask_buf, abs_x, abs_y, len, &p->start_line);
+ }
+
+ if(p->cfg.end_angle == 180) {
+ if(abs_y < p->cfg.vertex_p.y) res2 = LV_DRAW_MASK_RES_UNKNOWN;
+ else res2 = LV_DRAW_MASK_RES_FULL_COVER;
+ }
+ else if(p->cfg.end_angle == 0) {
+ if(abs_y < p->cfg.vertex_p.y) res2 = LV_DRAW_MASK_RES_FULL_COVER;
+ else res2 = LV_DRAW_MASK_RES_UNKNOWN;
+ }
+ else if((p->cfg.end_angle < 180 && abs_y < p->cfg.vertex_p.y) ||
+ (p->cfg.end_angle > 180 && abs_y >= p->cfg.vertex_p.y)) {
+ res2 = LV_DRAW_MASK_RES_UNKNOWN;
+ }
+ else {
+ res2 = lv_draw_mask_line(mask_buf, abs_x, abs_y, len, &p->end_line);
+ }
+
+ if(res1 == LV_DRAW_MASK_RES_TRANSP || res2 == LV_DRAW_MASK_RES_TRANSP) return LV_DRAW_MASK_RES_TRANSP;
+ else if(res1 == LV_DRAW_MASK_RES_UNKNOWN && res2 == LV_DRAW_MASK_RES_UNKNOWN) return LV_DRAW_MASK_RES_TRANSP;
+ else if(res1 == LV_DRAW_MASK_RES_FULL_COVER && res2 == LV_DRAW_MASK_RES_FULL_COVER) return LV_DRAW_MASK_RES_FULL_COVER;
+ else return LV_DRAW_MASK_RES_CHANGED;
+ }
+}
+
+
+
+LV_ATTRIBUTE_FAST_MEM static lv_draw_mask_res_t lv_draw_mask_radius(lv_opa_t * mask_buf, lv_coord_t abs_x,
+ lv_coord_t abs_y, lv_coord_t len,
+ lv_draw_mask_radius_param_t * p)
+{
+ bool outer = p->cfg.outer;
+ int32_t radius = p->cfg.radius;
+ lv_area_t rect;
+ lv_area_copy(&rect, &p->cfg.rect);
+
+ if(outer == false) {
+ if((abs_y < rect.y1 || abs_y > rect.y2)) {
+ return LV_DRAW_MASK_RES_TRANSP;
+ }
+ }
+ else {
+ if(abs_y < rect.y1 || abs_y > rect.y2) {
+ return LV_DRAW_MASK_RES_FULL_COVER;
+ }
+ }
+
+ if((abs_x >= rect.x1 + radius && abs_x + len <= rect.x2 - radius) ||
+ (abs_y >= rect.y1 + radius && abs_y <= rect.y2 - radius)) {
+ if(outer == false) {
+ /*Remove the edges*/
+ int32_t last = rect.x1 - abs_x;
+ if(last > len) return LV_DRAW_MASK_RES_TRANSP;
+ if(last >= 0) {
+ lv_memset_00(&mask_buf[0], last);
+ }
+
+ int32_t first = rect.x2 - abs_x + 1;
+ if(first <= 0) return LV_DRAW_MASK_RES_TRANSP;
+ else if(first < len) {
+ lv_memset_00(&mask_buf[first], len - first);
+ }
+ if(last == 0 && first == len) return LV_DRAW_MASK_RES_FULL_COVER;
+ else return LV_DRAW_MASK_RES_CHANGED;
+ }
+ else {
+ int32_t first = rect.x1 - abs_x;
+ if(first < 0) first = 0;
+ if(first <= len) {
+ int32_t last = rect.x2 - abs_x - first + 1;
+ if(first + last > len) last = len - first;
+ if(last >= 0) {
+ lv_memset_00(&mask_buf[first], last);
+ }
+ }
+ }
+ return LV_DRAW_MASK_RES_CHANGED;
+ }
+ // printf("exec: x:%d.. %d, y:%d: r:%d, %s\n", abs_x, abs_x + len - 1, abs_y, p->cfg.radius, p->cfg.outer ? "inv" : "norm");
+
+
+ // if( abs_x == 276 && abs_x + len - 1 == 479 && abs_y == 63 && p->cfg.radius == 5 && p->cfg.outer == 1) {
+ // char x = 0;
+ // }
+ //exec: x:276.. 479, y:63: r:5, inv)
+
+ int32_t k = rect.x1 - abs_x; /*First relevant coordinate on the of the mask*/
+ int32_t w = lv_area_get_width(&rect);
+ int32_t h = lv_area_get_height(&rect);
+ abs_x -= rect.x1;
+ abs_y -= rect.y1;
+
+ lv_coord_t aa_len;
+ lv_coord_t x_start;
+ lv_coord_t cir_y;
+ if(abs_y < radius) {
+ cir_y = radius - abs_y - 1;
+ }
+ else {
+ cir_y = abs_y - (h - radius);
+ }
+ lv_opa_t * aa_opa = get_next_line(p->circle, cir_y, &aa_len, &x_start);
+ lv_coord_t cir_x_right = k + w - radius + x_start;
+ lv_coord_t cir_x_left = k + radius - x_start - 1;
+ lv_coord_t i;
+
+ if(outer == false) {
+ for(i = 0; i < aa_len; i++) {
+ lv_opa_t opa = aa_opa[aa_len - i - 1];
+ if(cir_x_right + i >= 0 && cir_x_right + i < len) {
+ mask_buf[cir_x_right + i] = mask_mix(opa, mask_buf[cir_x_right + i]);
+ }
+ if(cir_x_left - i >= 0 && cir_x_left - i < len) {
+ mask_buf[cir_x_left - i] = mask_mix(opa, mask_buf[cir_x_left - i]);
+ }
+ }
+
+ /*Clean the right side*/
+ cir_x_right = LV_CLAMP(0, cir_x_right + i, len);
+ lv_memset_00(&mask_buf[cir_x_right], len - cir_x_right);
+
+ /*Clean the left side*/
+ cir_x_left = LV_CLAMP(0, cir_x_left - aa_len + 1, len);
+ lv_memset_00(&mask_buf[0], cir_x_left);
+ }
+ else {
+ for(i = 0; i < aa_len; i++) {
+ lv_opa_t opa = 255 - (aa_opa[aa_len - 1 - i]);
+ if(cir_x_right + i >= 0 && cir_x_right + i < len) {
+ mask_buf[cir_x_right + i] = mask_mix(opa, mask_buf[cir_x_right + i]);
+ }
+ if(cir_x_left - i >= 0 && cir_x_left - i < len) {
+ mask_buf[cir_x_left - i] = mask_mix(opa, mask_buf[cir_x_left - i]);
+ }
+ }
+
+ lv_coord_t clr_start = LV_CLAMP(0, cir_x_left + 1, len);
+ lv_coord_t clr_len = LV_CLAMP(0, cir_x_right - clr_start, len - clr_start);
+ lv_memset_00(&mask_buf[clr_start], clr_len);
+ }
+
+ return LV_DRAW_MASK_RES_CHANGED;
+}
+
+LV_ATTRIBUTE_FAST_MEM static lv_draw_mask_res_t lv_draw_mask_fade(lv_opa_t * mask_buf, lv_coord_t abs_x,
+ lv_coord_t abs_y, lv_coord_t len,
+ lv_draw_mask_fade_param_t * p)
+{
+ if(abs_y < p->cfg.coords.y1) return LV_DRAW_MASK_RES_FULL_COVER;
+ if(abs_y > p->cfg.coords.y2) return LV_DRAW_MASK_RES_FULL_COVER;
+ if(abs_x + len < p->cfg.coords.x1) return LV_DRAW_MASK_RES_FULL_COVER;
+ if(abs_x > p->cfg.coords.x2) return LV_DRAW_MASK_RES_FULL_COVER;
+
+ if(abs_x + len > p->cfg.coords.x2) len -= abs_x + len - p->cfg.coords.x2 - 1;
+
+ if(abs_x < p->cfg.coords.x1) {
+ int32_t x_ofs = 0;
+ x_ofs = p->cfg.coords.x1 - abs_x;
+ len -= x_ofs;
+ mask_buf += x_ofs;
+ }
+
+ int32_t i;
+
+ if(abs_y <= p->cfg.y_top) {
+ for(i = 0; i < len; i++) {
+ mask_buf[i] = mask_mix(mask_buf[i], p->cfg.opa_top);
+ }
+ return LV_DRAW_MASK_RES_CHANGED;
+ }
+ else if(abs_y >= p->cfg.y_bottom) {
+ for(i = 0; i < len; i++) {
+ mask_buf[i] = mask_mix(mask_buf[i], p->cfg.opa_bottom);
+ }
+ return LV_DRAW_MASK_RES_CHANGED;
+ }
+ else {
+ /*Calculate the opa proportionally*/
+ int16_t opa_diff = p->cfg.opa_bottom - p->cfg.opa_top;
+ int32_t y_diff = p->cfg.y_bottom - p->cfg.y_top + 1;
+ lv_opa_t opa_act = (int32_t)((int32_t)(abs_y - p->cfg.y_top) * opa_diff) / y_diff;
+ opa_act += p->cfg.opa_top;
+
+ for(i = 0; i < len; i++) {
+ mask_buf[i] = mask_mix(mask_buf[i], opa_act);
+ }
+ return LV_DRAW_MASK_RES_CHANGED;
+ }
+}
+
+LV_ATTRIBUTE_FAST_MEM static lv_draw_mask_res_t lv_draw_mask_map(lv_opa_t * mask_buf, lv_coord_t abs_x,
+ lv_coord_t abs_y, lv_coord_t len,
+ lv_draw_mask_map_param_t * p)
+{
+ /*Handle out of the mask cases*/
+ if(abs_y < p->cfg.coords.y1) return LV_DRAW_MASK_RES_FULL_COVER;
+ if(abs_y > p->cfg.coords.y2) return LV_DRAW_MASK_RES_FULL_COVER;
+ if(abs_x + len < p->cfg.coords.x1) return LV_DRAW_MASK_RES_FULL_COVER;
+ if(abs_x > p->cfg.coords.x2) return LV_DRAW_MASK_RES_FULL_COVER;
+
+ /*Got to the current row in the map*/
+ const lv_opa_t * map_tmp = p->cfg.map;
+ map_tmp += (abs_y - p->cfg.coords.y1) * lv_area_get_width(&p->cfg.coords);
+
+ if(abs_x + len > p->cfg.coords.x2) len -= abs_x + len - p->cfg.coords.x2 - 1;
+
+ if(abs_x < p->cfg.coords.x1) {
+ int32_t x_ofs = 0;
+ x_ofs = p->cfg.coords.x1 - abs_x;
+ len -= x_ofs;
+ mask_buf += x_ofs;
+ }
+ else {
+ map_tmp += (abs_x - p->cfg.coords.x1);
+ }
+
+ int32_t i;
+ for(i = 0; i < len; i++) {
+ mask_buf[i] = mask_mix(mask_buf[i], map_tmp[i]);
+ }
+
+ return LV_DRAW_MASK_RES_CHANGED;
+}
+
+LV_ATTRIBUTE_FAST_MEM static lv_draw_mask_res_t lv_draw_mask_polygon(lv_opa_t * mask_buf, lv_coord_t abs_x,
+ lv_coord_t abs_y, lv_coord_t len,
+ lv_draw_mask_polygon_param_t * param)
+{
+ uint16_t i;
+ struct {
+ lv_point_t p1;
+ lv_point_t p2;
+ } lines[2], tmp;
+ uint16_t line_cnt = 0;
+ lv_memset_00(&lines, sizeof(lines));
+ int psign_prev = 0;
+ for(i = 0; i < param->cfg.point_cnt; i++) {
+ lv_point_t p1 = param->cfg.points[i];
+ lv_point_t p2 = param->cfg.points[i + 1 < param->cfg.point_cnt ? i + 1 : 0];
+ int pdiff = p1.y - p2.y, psign = pdiff / LV_ABS(pdiff);
+ if(pdiff > 0) {
+ if(abs_y > p1.y || abs_y < p2.y) continue;
+ lines[line_cnt].p1 = p2;
+ lines[line_cnt].p2 = p1;
+ }
+ else {
+ if(abs_y < p1.y || abs_y > p2.y) continue;
+ lines[line_cnt].p1 = p1;
+ lines[line_cnt].p2 = p2;
+ }
+ if(psign_prev && psign_prev == psign) continue;
+ psign_prev = psign;
+ line_cnt++;
+ if(line_cnt == 2) break;
+ }
+ if(line_cnt != 2) return LV_DRAW_MASK_RES_TRANSP;
+ if(lines[0].p1.x > lines[1].p1.x || lines[0].p2.x > lines[1].p2.x) {
+ tmp = lines[0];
+ lines[0] = lines[1];
+ lines[1] = tmp;
+ }
+ lv_draw_mask_line_param_t line_p;
+ lv_draw_mask_line_points_init(&line_p, lines[0].p1.x, lines[0].p1.y, lines[0].p2.x, lines[0].p2.y,
+ LV_DRAW_MASK_LINE_SIDE_RIGHT);
+ if(line_p.steep == 0 && line_p.flat) {
+ lv_coord_t x1 = LV_MIN(lines[0].p1.x, lines[0].p2.x);
+ lv_coord_t x2 = LV_MAX(lines[0].p1.x, lines[0].p2.x);
+ for(i = 0; i < len; i++) {
+ mask_buf[i] = mask_mix(mask_buf[i], (abs_x + i >= x1 && abs_x + i <= x2) * 0xFF);
+ }
+ lv_draw_mask_free_param(&line_p);
+ return LV_DRAW_MASK_RES_CHANGED;
+ }
+ lv_draw_mask_res_t res1 = lv_draw_mask_line(mask_buf, abs_x, abs_y, len, &line_p);
+ lv_draw_mask_free_param(&line_p);
+ if(res1 == LV_DRAW_MASK_RES_TRANSP) {
+ return LV_DRAW_MASK_RES_TRANSP;
+ }
+ lv_draw_mask_line_points_init(&line_p, lines[1].p1.x, lines[1].p1.y, lines[1].p2.x, lines[1].p2.y,
+ LV_DRAW_MASK_LINE_SIDE_LEFT);
+ if(line_p.steep == 0 && line_p.flat) {
+ lv_coord_t x1 = LV_MIN(lines[1].p1.x, lines[1].p2.x);
+ lv_coord_t x2 = LV_MAX(lines[1].p1.x, lines[1].p2.x);
+ for(i = 0; i < len; i++) {
+ mask_buf[i] = mask_mix(mask_buf[i], (abs_x + i >= x1 && abs_x + i <= x2) * 0xFF);
+ }
+ lv_draw_mask_free_param(&line_p);
+ return LV_DRAW_MASK_RES_CHANGED;
+ }
+ lv_draw_mask_res_t res2 = lv_draw_mask_line(mask_buf, abs_x, abs_y, len, &line_p);
+ lv_draw_mask_free_param(&line_p);
+ if(res2 == LV_DRAW_MASK_RES_TRANSP) {
+ return LV_DRAW_MASK_RES_TRANSP;
+ }
+ if(res1 == LV_DRAW_MASK_RES_CHANGED || res2 == LV_DRAW_MASK_RES_CHANGED) return LV_DRAW_MASK_RES_CHANGED;
+ return res1;
+}
+/**
+ * Initialize the circle drawing
+ * @param c pointer to a point. The coordinates will be calculated here
+ * @param tmp point to a variable. It will store temporary data
+ * @param radius radius of the circle
+ */
+static void circ_init(lv_point_t * c, lv_coord_t * tmp, lv_coord_t radius)
+{
+ c->x = radius;
+ c->y = 0;
+ *tmp = 1 - radius;
+}
+
+/**
+ * Test the circle drawing is ready or not
+ * @param c same as in circ_init
+ * @return true if the circle is not ready yet
+ */
+static bool circ_cont(lv_point_t * c)
+{
+ return c->y <= c->x ? true : false;
+}
+
+/**
+ * Get the next point from the circle
+ * @param c same as in circ_init. The next point stored here.
+ * @param tmp same as in circ_init.
+ */
+static void circ_next(lv_point_t * c, lv_coord_t * tmp)
+{
+
+ if(*tmp <= 0) {
+ (*tmp) += 2 * c->y + 3; /*Change in decision criterion for y -> y+1*/
+ }
+ else {
+ (*tmp) += 2 * (c->y - c->x) + 5; /*Change for y -> y+1, x -> x-1*/
+ c->x--;
+ }
+ c->y++;
+}
+
+static void circ_calc_aa4(_lv_draw_mask_radius_circle_dsc_t * c, lv_coord_t radius)
+{
+ if(radius == 0) return;
+ c->radius = radius;
+
+ /*Allocate buffers*/
+ if(c->buf) lv_mem_free(c->buf);
+
+ c->buf = lv_mem_alloc(radius * 6 + 6); /*Use uint16_t for opa_start_on_y and x_start_on_y*/
+ LV_ASSERT_MALLOC(c->buf);
+ c->cir_opa = c->buf;
+ c->opa_start_on_y = (uint16_t *)(c->buf + 2 * radius + 2);
+ c->x_start_on_y = (uint16_t *)(c->buf + 4 * radius + 4);
+
+ /*Special case, handle manually*/
+ if(radius == 1) {
+ c->cir_opa[0] = 180;
+ c->opa_start_on_y[0] = 0;
+ c->opa_start_on_y[1] = 1;
+ c->x_start_on_y[0] = 0;
+ return;
+ }
+
+ lv_coord_t * cir_x = lv_mem_buf_get((radius + 1) * 2 * 2 * sizeof(lv_coord_t));
+ lv_coord_t * cir_y = &cir_x[(radius + 1) * 2];
+
+ uint32_t y_8th_cnt = 0;
+ lv_point_t cp;
+ lv_coord_t tmp;
+ circ_init(&cp, &tmp, radius * 4); /*Upscale by 4*/
+ int32_t i;
+
+ uint32_t x_int[4];
+ uint32_t x_fract[4];
+ lv_coord_t cir_size = 0;
+ x_int[0] = cp.x >> 2;
+ x_fract[0] = 0;
+
+ /*Calculate an 1/8 circle*/
+ while(circ_cont(&cp)) {
+ /*Calculate 4 point of the circle */
+ for(i = 0; i < 4; i++) {
+ circ_next(&cp, &tmp);
+ if(circ_cont(&cp) == false) break;
+ x_int[i] = cp.x >> 2;
+ x_fract[i] = cp.x & 0x3;
+ }
+ if(i != 4) break;
+
+ /*All lines on the same x when downscaled*/
+ if(x_int[0] == x_int[3]) {
+ cir_x[cir_size] = x_int[0];
+ cir_y[cir_size] = y_8th_cnt;
+ c->cir_opa[cir_size] = x_fract[0] + x_fract[1] + x_fract[2] + x_fract[3];
+ c->cir_opa[cir_size] *= 16;
+ cir_size++;
+ }
+ /*Second line on new x when downscaled*/
+ else if(x_int[0] != x_int[1]) {
+ cir_x[cir_size] = x_int[0];
+ cir_y[cir_size] = y_8th_cnt;
+ c->cir_opa[cir_size] = x_fract[0];
+ c->cir_opa[cir_size] *= 16;
+ cir_size++;
+
+ cir_x[cir_size] = x_int[0] - 1;
+ cir_y[cir_size] = y_8th_cnt;
+ c->cir_opa[cir_size] = 1 * 4 + x_fract[1] + x_fract[2] + x_fract[3];;
+ c->cir_opa[cir_size] *= 16;
+ cir_size++;
+ }
+ /*Third line on new x when downscaled*/
+ else if(x_int[0] != x_int[2]) {
+ cir_x[cir_size] = x_int[0];
+ cir_y[cir_size] = y_8th_cnt;
+ c->cir_opa[cir_size] = x_fract[0] + x_fract[1];
+ c->cir_opa[cir_size] *= 16;
+ cir_size++;
+
+ cir_x[cir_size] = x_int[0] - 1;
+ cir_y[cir_size] = y_8th_cnt;
+ c->cir_opa[cir_size] = 2 * 4 + x_fract[2] + x_fract[3];;
+ c->cir_opa[cir_size] *= 16;
+ cir_size++;
+ }
+ /*Forth line on new x when downscaled*/
+ else {
+ cir_x[cir_size] = x_int[0];
+ cir_y[cir_size] = y_8th_cnt;
+ c->cir_opa[cir_size] = x_fract[0] + x_fract[1] + x_fract[2];
+ c->cir_opa[cir_size] *= 16;
+ cir_size++;
+
+ cir_x[cir_size] = x_int[0] - 1;
+ cir_y[cir_size] = y_8th_cnt;
+ c->cir_opa[cir_size] = 3 * 4 + x_fract[3];;
+ c->cir_opa[cir_size] *= 16;
+ cir_size++;
+ }
+
+ y_8th_cnt++;
+ }
+
+ /*The point on the 1/8 circle is special, calculate it manually*/
+ int32_t mid = radius * 723;
+ int32_t mid_int = mid >> 10;
+ if(cir_x[cir_size - 1] != mid_int || cir_y[cir_size - 1] != mid_int) {
+ int32_t tmp_val = mid - (mid_int << 10);
+ if(tmp_val <= 512) {
+ tmp_val = tmp_val * tmp_val * 2;
+ tmp_val = tmp_val >> (10 + 6);
+ }
+ else {
+ tmp_val = 1024 - tmp_val;
+ tmp_val = tmp_val * tmp_val * 2;
+ tmp_val = tmp_val >> (10 + 6);
+ tmp_val = 15 - tmp_val;
+ }
+
+ cir_x[cir_size] = mid_int;
+ cir_y[cir_size] = mid_int;
+ c->cir_opa[cir_size] = tmp_val;
+ c->cir_opa[cir_size] *= 16;
+ cir_size++;
+ }
+
+ /*Build the second octet by mirroring the first*/
+ for(i = cir_size - 2; i >= 0; i--, cir_size++) {
+ cir_x[cir_size] = cir_y[i];
+ cir_y[cir_size] = cir_x[i];
+ c->cir_opa[cir_size] = c->cir_opa[i];
+ }
+
+ lv_coord_t y = 0;
+ i = 0;
+ c->opa_start_on_y[0] = 0;
+ while(i < cir_size) {
+ c->opa_start_on_y[y] = i;
+ c->x_start_on_y[y] = cir_x[i];
+ for(; cir_y[i] == y && i < (int32_t)cir_size; i++) {
+ c->x_start_on_y[y] = LV_MIN(c->x_start_on_y[y], cir_x[i]);
+ }
+ y++;
+ }
+
+ lv_mem_buf_release(cir_x);
+}
+
+static lv_opa_t * get_next_line(_lv_draw_mask_radius_circle_dsc_t * c, lv_coord_t y, lv_coord_t * len,
+ lv_coord_t * x_start)
+{
+ *len = c->opa_start_on_y[y + 1] - c->opa_start_on_y[y];
+ *x_start = c->x_start_on_y[y];
+ return &c->cir_opa[c->opa_start_on_y[y]];
+}
+
+
+LV_ATTRIBUTE_FAST_MEM static inline lv_opa_t mask_mix(lv_opa_t mask_act, lv_opa_t mask_new)
+{
+ if(mask_new >= LV_OPA_MAX) return mask_act;
+ if(mask_new <= LV_OPA_MIN) return 0;
+
+ return LV_UDIV255(mask_act * mask_new);// >> 8);
+}
+
+
+#endif /*LV_DRAW_COMPLEX*/
diff --git a/lib/lvgl/src/draw/lv_draw_mask.h b/lib/lvgl/src/draw/lv_draw_mask.h
new file mode 100644
index 00000000..b7e4e1cd
--- /dev/null
+++ b/lib/lvgl/src/draw/lv_draw_mask.h
@@ -0,0 +1,394 @@
+/**
+ * @file lv_draw_mask.h
+ *
+ */
+
+#ifndef LV_DRAW_MASK_H
+#define LV_DRAW_MASK_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/*********************
+ * INCLUDES
+ *********************/
+#include <stdbool.h>
+#include "../misc/lv_area.h"
+#include "../misc/lv_color.h"
+#include "../misc/lv_math.h"
+
+/*********************
+ * DEFINES
+ *********************/
+#define LV_MASK_ID_INV (-1)
+#if LV_DRAW_COMPLEX
+# define _LV_MASK_MAX_NUM 16
+#else
+# define _LV_MASK_MAX_NUM 1
+#endif
+
+/**********************
+ * TYPEDEFS
+ **********************/
+
+enum {
+ LV_DRAW_MASK_RES_TRANSP,
+ LV_DRAW_MASK_RES_FULL_COVER,
+ LV_DRAW_MASK_RES_CHANGED,
+ LV_DRAW_MASK_RES_UNKNOWN
+};
+
+typedef uint8_t lv_draw_mask_res_t;
+
+typedef struct {
+ void * param;
+ void * custom_id;
+} _lv_draw_mask_saved_t;
+
+typedef _lv_draw_mask_saved_t _lv_draw_mask_saved_arr_t[_LV_MASK_MAX_NUM];
+
+
+
+#if LV_DRAW_COMPLEX == 0
+static inline uint8_t lv_draw_mask_get_cnt(void)
+{
+ return 0;
+}
+
+static inline bool lv_draw_mask_is_any(const lv_area_t * a)
+{
+ LV_UNUSED(a);
+ return false;
+}
+
+#endif
+
+#if LV_DRAW_COMPLEX
+
+enum {
+ LV_DRAW_MASK_TYPE_LINE,
+ LV_DRAW_MASK_TYPE_ANGLE,
+ LV_DRAW_MASK_TYPE_RADIUS,
+ LV_DRAW_MASK_TYPE_FADE,
+ LV_DRAW_MASK_TYPE_MAP,
+ LV_DRAW_MASK_TYPE_POLYGON,
+};
+
+typedef uint8_t lv_draw_mask_type_t;
+
+enum {
+ LV_DRAW_MASK_LINE_SIDE_LEFT = 0,
+ LV_DRAW_MASK_LINE_SIDE_RIGHT,
+ LV_DRAW_MASK_LINE_SIDE_TOP,
+ LV_DRAW_MASK_LINE_SIDE_BOTTOM,
+};
+
+/**
+ * A common callback type for every mask type.
+ * Used internally by the library.
+ */
+typedef lv_draw_mask_res_t (*lv_draw_mask_xcb_t)(lv_opa_t * mask_buf, lv_coord_t abs_x, lv_coord_t abs_y,
+ lv_coord_t len,
+ void * p);
+
+typedef uint8_t lv_draw_mask_line_side_t;
+
+typedef struct {
+ lv_draw_mask_xcb_t cb;
+ lv_draw_mask_type_t type;
+} _lv_draw_mask_common_dsc_t;
+
+typedef struct {
+ /*The first element must be the common descriptor*/
+ _lv_draw_mask_common_dsc_t dsc;
+
+ struct {
+ /*First point*/
+ lv_point_t p1;
+
+ /*Second point*/
+ lv_point_t p2;
+
+ /*Which side to keep?*/
+ lv_draw_mask_line_side_t side : 2;
+ } cfg;
+
+ /*A point of the line*/
+ lv_point_t origo;
+
+ /*X / (1024*Y) steepness (X is 0..1023 range). What is the change of X in 1024 Y?*/
+ int32_t xy_steep;
+
+ /*Y / (1024*X) steepness (Y is 0..1023 range). What is the change of Y in 1024 X?*/
+ int32_t yx_steep;
+
+ /*Helper which stores yx_steep for flat lines and xy_steep for steep (non flat) lines*/
+ int32_t steep;
+
+ /*Steepness in 1 px in 0..255 range. Used only by flat lines.*/
+ int32_t spx;
+
+ /*1: It's a flat line? (Near to horizontal)*/
+ uint8_t flat : 1;
+
+ /*Invert the mask. The default is: Keep the left part.
+ *It is used to select left/right/top/bottom*/
+ uint8_t inv: 1;
+} lv_draw_mask_line_param_t;
+
+typedef struct {
+ /*The first element must be the common descriptor*/
+ _lv_draw_mask_common_dsc_t dsc;
+
+ struct {
+ lv_point_t vertex_p;
+ lv_coord_t start_angle;
+ lv_coord_t end_angle;
+ } cfg;
+
+ lv_draw_mask_line_param_t start_line;
+ lv_draw_mask_line_param_t end_line;
+ uint16_t delta_deg;
+} lv_draw_mask_angle_param_t;
+
+typedef struct {
+ uint8_t * buf;
+ lv_opa_t * cir_opa; /*Opacity of values on the circumference of an 1/4 circle*/
+ uint16_t * x_start_on_y; /*The x coordinate of the circle for each y value*/
+ uint16_t * opa_start_on_y; /*The index of `cir_opa` for each y value*/
+ int32_t life; /*How many times the entry way used*/
+ uint32_t used_cnt; /*Like a semaphore to count the referencing masks*/
+ lv_coord_t radius; /*The radius of the entry*/
+} _lv_draw_mask_radius_circle_dsc_t;
+
+typedef _lv_draw_mask_radius_circle_dsc_t _lv_draw_mask_radius_circle_dsc_arr_t[LV_CIRCLE_CACHE_SIZE];
+
+typedef struct {
+ /*The first element must be the common descriptor*/
+ _lv_draw_mask_common_dsc_t dsc;
+
+ struct {
+ lv_area_t rect;
+ lv_coord_t radius;
+ /*Invert the mask. 0: Keep the pixels inside.*/
+ uint8_t outer: 1;
+ } cfg;
+
+ _lv_draw_mask_radius_circle_dsc_t * circle;
+} lv_draw_mask_radius_param_t;
+
+
+typedef struct {
+ /*The first element must be the common descriptor*/
+ _lv_draw_mask_common_dsc_t dsc;
+
+ struct {
+ lv_area_t coords;
+ lv_coord_t y_top;
+ lv_coord_t y_bottom;
+ lv_opa_t opa_top;
+ lv_opa_t opa_bottom;
+ } cfg;
+
+} lv_draw_mask_fade_param_t;
+
+
+typedef struct _lv_draw_mask_map_param_t {
+ /*The first element must be the common descriptor*/
+ _lv_draw_mask_common_dsc_t dsc;
+
+ struct {
+ lv_area_t coords;
+ const lv_opa_t * map;
+ } cfg;
+} lv_draw_mask_map_param_t;
+
+typedef struct {
+ /*The first element must be the common descriptor*/
+ _lv_draw_mask_common_dsc_t dsc;
+
+ struct {
+ lv_point_t * points;
+ uint16_t point_cnt;
+ } cfg;
+} lv_draw_mask_polygon_param_t;
+
+
+/**********************
+ * GLOBAL PROTOTYPES
+ **********************/
+
+/**
+ * Add a draw mask. Everything drawn after it (until removing the mask) will be affected by the mask.
+ * @param param an initialized mask parameter. Only the pointer is saved.
+ * @param custom_id a custom pointer to identify the mask. Used in `lv_draw_mask_remove_custom`.
+ * @return the an integer, the ID of the mask. Can be used in `lv_draw_mask_remove_id`.
+ */
+int16_t lv_draw_mask_add(void * param, void * custom_id);
+
+//! @cond Doxygen_Suppress
+
+/**
+ * Apply the added buffers on a line. Used internally by the library's drawing routines.
+ * @param mask_buf store the result mask here. Has to be `len` byte long. Should be initialized with `0xFF`.
+ * @param abs_x absolute X coordinate where the line to calculate start
+ * @param abs_y absolute Y coordinate where the line to calculate start
+ * @param len length of the line to calculate (in pixel count)
+ * @return One of these values:
+ * - `LV_DRAW_MASK_RES_FULL_TRANSP`: the whole line is transparent. `mask_buf` is not set to zero
+ * - `LV_DRAW_MASK_RES_FULL_COVER`: the whole line is fully visible. `mask_buf` is unchanged
+ * - `LV_DRAW_MASK_RES_CHANGED`: `mask_buf` has changed, it shows the desired opacity of each pixel in the given line
+ */
+LV_ATTRIBUTE_FAST_MEM lv_draw_mask_res_t lv_draw_mask_apply(lv_opa_t * mask_buf, lv_coord_t abs_x, lv_coord_t abs_y,
+ lv_coord_t len);
+
+/**
+ * Apply the specified buffers on a line. Used internally by the library's drawing routines.
+ * @param mask_buf store the result mask here. Has to be `len` byte long. Should be initialized with `0xFF`.
+ * @param abs_x absolute X coordinate where the line to calculate start
+ * @param abs_y absolute Y coordinate where the line to calculate start
+ * @param len length of the line to calculate (in pixel count)
+ * @param ids ID array of added buffers
+ * @param ids_count number of ID array
+ * @return One of these values:
+ * - `LV_DRAW_MASK_RES_FULL_TRANSP`: the whole line is transparent. `mask_buf` is not set to zero
+ * - `LV_DRAW_MASK_RES_FULL_COVER`: the whole line is fully visible. `mask_buf` is unchanged
+ * - `LV_DRAW_MASK_RES_CHANGED`: `mask_buf` has changed, it shows the desired opacity of each pixel in the given line
+ */
+LV_ATTRIBUTE_FAST_MEM lv_draw_mask_res_t lv_draw_mask_apply_ids(lv_opa_t * mask_buf, lv_coord_t abs_x, lv_coord_t abs_y,
+ lv_coord_t len, const int16_t * ids, int16_t ids_count);
+
+//! @endcond
+
+/**
+ * Remove a mask with a given ID
+ * @param id the ID of the mask. Returned by `lv_draw_mask_add`
+ * @return the parameter of the removed mask.
+ * If more masks have `custom_id` ID then the last mask's parameter will be returned
+ */
+void * lv_draw_mask_remove_id(int16_t id);
+
+/**
+ * Remove all mask with a given custom ID
+ * @param custom_id a pointer used in `lv_draw_mask_add`
+ * @return return the parameter of the removed mask.
+ * If more masks have `custom_id` ID then the last mask's parameter will be returned
+ */
+void * lv_draw_mask_remove_custom(void * custom_id);
+
+/**
+ * Free the data from the parameter.
+ * It's called inside `lv_draw_mask_remove_id` and `lv_draw_mask_remove_custom`
+ * Needs to be called only in special cases when the mask is not added by `lv_draw_mask_add`
+ * and not removed by `lv_draw_mask_remove_id` or `lv_draw_mask_remove_custom`
+ * @param p pointer to a mask parameter
+ */
+void lv_draw_mask_free_param(void * p);
+
+/**
+ * Called by LVGL the rendering of a screen is ready to clean up
+ * the temporal (cache) data of the masks
+ */
+void _lv_draw_mask_cleanup(void);
+
+//! @cond Doxygen_Suppress
+
+/**
+ * Count the currently added masks
+ * @return number of active masks
+ */
+LV_ATTRIBUTE_FAST_MEM uint8_t lv_draw_mask_get_cnt(void);
+
+
+/**
+ * Check if there is any added draw mask
+ * @param a an area to test for affecting masks.
+ * @return true: there is t least 1 draw mask; false: there are no draw masks
+ */
+bool lv_draw_mask_is_any(const lv_area_t * a);
+
+//! @endcond
+
+/**
+ *Initialize a line mask from two points.
+ * @param param pointer to a `lv_draw_mask_param_t` to initialize
+ * @param p1x X coordinate of the first point of the line
+ * @param p1y Y coordinate of the first point of the line
+ * @param p2x X coordinate of the second point of the line
+ * @param p2y y coordinate of the second point of the line
+ * @param side and element of `lv_draw_mask_line_side_t` to describe which side to keep.
+ * With `LV_DRAW_MASK_LINE_SIDE_LEFT/RIGHT` and horizontal line all pixels are kept
+ * With `LV_DRAW_MASK_LINE_SIDE_TOP/BOTTOM` and vertical line all pixels are kept
+ */
+void lv_draw_mask_line_points_init(lv_draw_mask_line_param_t * param, lv_coord_t p1x, lv_coord_t p1y, lv_coord_t p2x,
+ lv_coord_t p2y, lv_draw_mask_line_side_t side);
+
+/**
+ *Initialize a line mask from a point and an angle.
+ * @param param pointer to a `lv_draw_mask_param_t` to initialize
+ * @param px X coordinate of a point of the line
+ * @param py X coordinate of a point of the line
+ * @param angle right 0 deg, bottom: 90
+ * @param side and element of `lv_draw_mask_line_side_t` to describe which side to keep.
+ * With `LV_DRAW_MASK_LINE_SIDE_LEFT/RIGHT` and horizontal line all pixels are kept
+ * With `LV_DRAW_MASK_LINE_SIDE_TOP/BOTTOM` and vertical line all pixels are kept
+ */
+void lv_draw_mask_line_angle_init(lv_draw_mask_line_param_t * param, lv_coord_t p1x, lv_coord_t py, int16_t angle,
+ lv_draw_mask_line_side_t side);
+
+/**
+ * Initialize an angle mask.
+ * @param param pointer to a `lv_draw_mask_param_t` to initialize
+ * @param vertex_x X coordinate of the angle vertex (absolute coordinates)
+ * @param vertex_y Y coordinate of the angle vertex (absolute coordinates)
+ * @param start_angle start angle in degrees. 0 deg on the right, 90 deg, on the bottom
+ * @param end_angle end angle
+ */
+void lv_draw_mask_angle_init(lv_draw_mask_angle_param_t * param, lv_coord_t vertex_x, lv_coord_t vertex_y,
+ lv_coord_t start_angle, lv_coord_t end_angle);
+
+/**
+ * Initialize a fade mask.
+ * @param param pointer to an `lv_draw_mask_radius_param_t` to initialize
+ * @param rect coordinates of the rectangle to affect (absolute coordinates)
+ * @param radius radius of the rectangle
+ * @param inv true: keep the pixels inside the rectangle; keep the pixels outside of the rectangle
+ */
+void lv_draw_mask_radius_init(lv_draw_mask_radius_param_t * param, const lv_area_t * rect, lv_coord_t radius, bool inv);
+
+/**
+ * Initialize a fade mask.
+ * @param param pointer to a `lv_draw_mask_param_t` to initialize
+ * @param coords coordinates of the area to affect (absolute coordinates)
+ * @param opa_top opacity on the top
+ * @param y_top at which coordinate start to change to opacity to `opa_bottom`
+ * @param opa_bottom opacity at the bottom
+ * @param y_bottom at which coordinate reach `opa_bottom`.
+ */
+void lv_draw_mask_fade_init(lv_draw_mask_fade_param_t * param, const lv_area_t * coords, lv_opa_t opa_top,
+ lv_coord_t y_top,
+ lv_opa_t opa_bottom, lv_coord_t y_bottom);
+
+/**
+ * Initialize a map mask.
+ * @param param pointer to a `lv_draw_mask_param_t` to initialize
+ * @param coords coordinates of the map (absolute coordinates)
+ * @param map array of bytes with the mask values
+ */
+void lv_draw_mask_map_init(lv_draw_mask_map_param_t * param, const lv_area_t * coords, const lv_opa_t * map);
+
+void lv_draw_mask_polygon_init(lv_draw_mask_polygon_param_t * param, const lv_point_t * points, uint16_t point_cnt);
+
+#endif /*LV_DRAW_COMPLEX*/
+
+/**********************
+ * MACROS
+ **********************/
+
+#ifdef __cplusplus
+} /*extern "C"*/
+#endif
+
+#endif /*LV_DRAW_MASK_H*/
diff --git a/lib/lvgl/src/draw/lv_draw_rect.c b/lib/lvgl/src/draw/lv_draw_rect.c
new file mode 100644
index 00000000..f34854d9
--- /dev/null
+++ b/lib/lvgl/src/draw/lv_draw_rect.c
@@ -0,0 +1,73 @@
+/**
+ * @file lv_draw_rect.c
+ *
+ */
+
+/*********************
+ * INCLUDES
+ *********************/
+#include "lv_draw.h"
+#include "lv_draw_rect.h"
+#include "../misc/lv_assert.h"
+
+/*********************
+ * DEFINES
+ *********************/
+
+/**********************
+ * TYPEDEFS
+ **********************/
+
+/**********************
+ * STATIC PROTOTYPES
+ **********************/
+
+/**********************
+ * STATIC VARIABLES
+ **********************/
+
+/**********************
+ * MACROS
+ **********************/
+
+/**********************
+ * GLOBAL FUNCTIONS
+ **********************/
+
+LV_ATTRIBUTE_FAST_MEM void lv_draw_rect_dsc_init(lv_draw_rect_dsc_t * dsc)
+{
+ lv_memset_00(dsc, sizeof(lv_draw_rect_dsc_t));
+ dsc->bg_color = lv_color_white();
+ dsc->bg_grad.stops[0].color = lv_color_white();
+ dsc->bg_grad.stops[1].color = lv_color_black();
+ dsc->bg_grad.stops[1].frac = 0xFF;
+ dsc->bg_grad.stops_count = 2;
+ dsc->border_color = lv_color_black();
+ dsc->shadow_color = lv_color_black();
+ dsc->bg_img_symbol_font = LV_FONT_DEFAULT;
+ dsc->bg_opa = LV_OPA_COVER;
+ dsc->bg_img_opa = LV_OPA_COVER;
+ dsc->outline_opa = LV_OPA_COVER;
+ dsc->border_opa = LV_OPA_COVER;
+ dsc->shadow_opa = LV_OPA_COVER;
+ dsc->border_side = LV_BORDER_SIDE_FULL;
+}
+
+/**
+ * Draw a rectangle
+ * @param coords the coordinates of the rectangle
+ * @param mask the rectangle will be drawn only in this mask
+ * @param dsc pointer to an initialized `lv_draw_rect_dsc_t` variable
+ */
+void lv_draw_rect(lv_draw_ctx_t * draw_ctx, const lv_draw_rect_dsc_t * dsc, const lv_area_t * coords)
+{
+ if(lv_area_get_height(coords) < 1 || lv_area_get_width(coords) < 1) return;
+
+ draw_ctx->draw_rect(draw_ctx, dsc, coords);
+
+ LV_ASSERT_MEM_INTEGRITY();
+}
+
+/**********************
+ * STATIC FUNCTIONS
+ **********************/
diff --git a/lib/lvgl/src/draw/lv_draw_rect.h b/lib/lvgl/src/draw/lv_draw_rect.h
new file mode 100644
index 00000000..1583e3e6
--- /dev/null
+++ b/lib/lvgl/src/draw/lv_draw_rect.h
@@ -0,0 +1,96 @@
+/**
+ * @file lv_draw_rect.h
+ *
+ */
+
+#ifndef LV_DRAW_RECT_H
+#define LV_DRAW_RECT_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*********************
+ * INCLUDES
+ *********************/
+#include "../lv_conf_internal.h"
+#include "../misc/lv_color.h"
+#include "../misc/lv_area.h"
+#include "../misc/lv_style.h"
+#include "sw/lv_draw_sw_gradient.h"
+
+/*********************
+ * DEFINES
+ *********************/
+#define LV_RADIUS_CIRCLE 0x7FFF /**< A very big radius to always draw as circle*/
+LV_EXPORT_CONST_INT(LV_RADIUS_CIRCLE);
+
+/**********************
+ * TYPEDEFS
+ **********************/
+
+typedef struct {
+ lv_coord_t radius;
+ lv_blend_mode_t blend_mode;
+
+ /*Background*/
+ lv_opa_t bg_opa;
+ lv_color_t bg_color; /**< First element of a gradient is a color, so it maps well here*/
+ lv_grad_dsc_t bg_grad;
+
+ /*Background img*/
+ const void * bg_img_src;
+ const void * bg_img_symbol_font;
+ lv_color_t bg_img_recolor;
+ lv_opa_t bg_img_opa;
+ lv_opa_t bg_img_recolor_opa;
+ uint8_t bg_img_tiled;
+
+ /*Border*/
+ lv_color_t border_color;
+ lv_coord_t border_width;
+ lv_opa_t border_opa;
+ uint8_t border_post : 1; /*There is a border it will be drawn later.*/
+ lv_border_side_t border_side : 5;
+
+ /*Outline*/
+ lv_color_t outline_color;
+ lv_coord_t outline_width;
+ lv_coord_t outline_pad;
+ lv_opa_t outline_opa;
+
+ /*Shadow*/
+ lv_color_t shadow_color;
+ lv_coord_t shadow_width;
+ lv_coord_t shadow_ofs_x;
+ lv_coord_t shadow_ofs_y;
+ lv_coord_t shadow_spread;
+ lv_opa_t shadow_opa;
+} lv_draw_rect_dsc_t;
+
+struct _lv_draw_ctx_t;
+
+/**********************
+ * GLOBAL PROTOTYPES
+ **********************/
+
+LV_ATTRIBUTE_FAST_MEM void lv_draw_rect_dsc_init(lv_draw_rect_dsc_t * dsc);
+
+
+/**
+ * Draw a rectangle
+ * @param coords the coordinates of the rectangle
+ * @param clip the rectangle will be drawn only in this area
+ * @param dsc pointer to an initialized `lv_draw_rect_dsc_t` variable
+ */
+void lv_draw_rect(struct _lv_draw_ctx_t * draw_ctx, const lv_draw_rect_dsc_t * dsc, const lv_area_t * coords);
+
+/**********************
+ * MACROS
+ **********************/
+
+#ifdef __cplusplus
+} /*extern "C"*/
+#endif
+
+#endif /*LV_DRAW_RECT_H*/
diff --git a/lib/lvgl/src/draw/lv_draw_transform.c b/lib/lvgl/src/draw/lv_draw_transform.c
new file mode 100644
index 00000000..580351d6
--- /dev/null
+++ b/lib/lvgl/src/draw/lv_draw_transform.c
@@ -0,0 +1,54 @@
+/**
+ * @file lv_draw_transform.c
+ *
+ */
+
+/*********************
+ * INCLUDES
+ *********************/
+#include "lv_draw.h"
+#include "lv_draw_transform.h"
+#include "../misc/lv_assert.h"
+#include "../misc/lv_area.h"
+
+/*********************
+ * DEFINES
+ *********************/
+
+/**********************
+ * TYPEDEFS
+ **********************/
+
+/**********************
+ * STATIC PROTOTYPES
+ **********************/
+
+/**********************
+ * STATIC VARIABLES
+ **********************/
+
+/**********************
+ * MACROS
+ **********************/
+
+/**********************
+ * GLOBAL FUNCTIONS
+ **********************/
+void lv_draw_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_ASSERT_NULL(draw_ctx);
+ if(draw_ctx->draw_transform == NULL) {
+ LV_LOG_WARN("draw_ctx->draw_transform == NULL");
+ return;
+ }
+
+ draw_ctx->draw_transform(draw_ctx, dest_area, src_buf, src_w, src_h, src_stride, draw_dsc, cf, cbuf, abuf);
+
+}
+
+
+/**********************
+ * STATIC FUNCTIONS
+ **********************/
diff --git a/lib/lvgl/src/draw/lv_draw_transform.h b/lib/lvgl/src/draw/lv_draw_transform.h
new file mode 100644
index 00000000..1926c2fc
--- /dev/null
+++ b/lib/lvgl/src/draw/lv_draw_transform.h
@@ -0,0 +1,44 @@
+/**
+ * @file lv_draw_transform.h
+ *
+ */
+
+#ifndef LV_DRAW_TRANSFORM_H
+#define LV_DRAW_TRANSFORM_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*********************
+ * INCLUDES
+ *********************/
+#include "../lv_conf_internal.h"
+#include "../misc/lv_area.h"
+
+/*********************
+ * DEFINES
+ *********************/
+
+/**********************
+ * TYPEDEFS
+ **********************/
+struct _lv_draw_ctx_t;
+
+/**********************
+ * GLOBAL PROTOTYPES
+ **********************/
+
+void lv_draw_transform(struct _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);
+
+/**********************
+ * MACROS
+ **********************/
+
+#ifdef __cplusplus
+} /*extern "C"*/
+#endif
+
+#endif /*LV_DRAW_TRANSFORM_H*/
diff --git a/lib/lvgl/src/draw/lv_draw_triangle.c b/lib/lvgl/src/draw/lv_draw_triangle.c
new file mode 100644
index 00000000..42b4d779
--- /dev/null
+++ b/lib/lvgl/src/draw/lv_draw_triangle.c
@@ -0,0 +1,52 @@
+/**
+ * @file lv_draw_triangle.c
+ *
+ */
+
+/*********************
+ * INCLUDES
+ *********************/
+#include "lv_draw.h"
+#include "lv_draw_triangle.h"
+#include "../misc/lv_math.h"
+#include "../misc/lv_mem.h"
+
+/*********************
+ * DEFINES
+ *********************/
+
+/**********************
+ * TYPEDEFS
+ **********************/
+
+/**********************
+ * STATIC PROTOTYPES
+ **********************/
+
+/**********************
+ * STATIC VARIABLES
+ **********************/
+
+/**********************
+ * MACROS
+ **********************/
+
+/**********************
+ * GLOBAL FUNCTIONS
+ **********************/
+
+void lv_draw_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)
+{
+ draw_ctx->draw_polygon(draw_ctx, draw_dsc, points, point_cnt);
+}
+
+void lv_draw_triangle(struct _lv_draw_ctx_t * draw_ctx, const lv_draw_rect_dsc_t * draw_dsc, const lv_point_t points[])
+{
+
+ draw_ctx->draw_polygon(draw_ctx, draw_dsc, points, 3);
+}
+
+/**********************
+ * STATIC FUNCTIONS
+ **********************/
diff --git a/lib/lvgl/src/draw/lv_draw_triangle.h b/lib/lvgl/src/draw/lv_draw_triangle.h
new file mode 100644
index 00000000..e8d85750
--- /dev/null
+++ b/lib/lvgl/src/draw/lv_draw_triangle.h
@@ -0,0 +1,42 @@
+/**
+ * @file lv_draw_triangle.h
+ *
+ */
+
+#ifndef LV_DRAW_TRIANGLE_H
+#define LV_DRAW_TRIANGLE_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*********************
+ * INCLUDES
+ *********************/
+#include "lv_draw_rect.h"
+
+/*********************
+ * DEFINES
+ *********************/
+
+/**********************
+ * TYPEDEFS
+ **********************/
+
+/**********************
+ * GLOBAL PROTOTYPES
+ **********************/
+
+void lv_draw_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_triangle(struct _lv_draw_ctx_t * draw_ctx, const lv_draw_rect_dsc_t * draw_dsc, const lv_point_t points[]);
+/**********************
+ * MACROS
+ **********************/
+
+#ifdef __cplusplus
+} /*extern "C"*/
+#endif
+
+#endif /*LV_DRAW_TRIANGLE_H*/
diff --git a/lib/lvgl/src/draw/lv_img_buf.c b/lib/lvgl/src/draw/lv_img_buf.c
new file mode 100644
index 00000000..4c939dd8
--- /dev/null
+++ b/lib/lvgl/src/draw/lv_img_buf.c
@@ -0,0 +1,463 @@
+/**
+ * @file lv_img_buf.c
+ *
+ */
+
+/*********************
+ * INCLUDES
+ *********************/
+#include <stddef.h>
+#include <string.h>
+#include "lv_img_buf.h"
+#include "lv_draw_img.h"
+#include "../misc/lv_math.h"
+#include "../misc/lv_log.h"
+#include "../misc/lv_mem.h"
+
+/*********************
+ * DEFINES
+ *********************/
+
+/**********************
+ * TYPEDEFS
+ **********************/
+
+/**********************
+ * STATIC PROTOTYPES
+ **********************/
+
+/**********************
+ * STATIC VARIABLES
+ **********************/
+
+/**********************
+ * MACROS
+ **********************/
+
+/**********************
+ * GLOBAL FUNCTIONS
+ **********************/
+
+/**
+ * Get the color of an image's pixel
+ * @param dsc an image descriptor
+ * @param x x coordinate of the point to get
+ * @param y x coordinate of the point to get
+ * @param color the color of the image. In case of `LV_IMG_CF_ALPHA_1/2/4/8` this color is used.
+ * Not used in other cases.
+ * @param safe true: check out of bounds
+ * @return color of the point
+ */
+lv_color_t lv_img_buf_get_px_color(lv_img_dsc_t * dsc, lv_coord_t x, lv_coord_t y, lv_color_t color)
+{
+ lv_color_t p_color = lv_color_black();
+ uint8_t * buf_u8 = (uint8_t *)dsc->data;
+
+ if(dsc->header.cf == LV_IMG_CF_TRUE_COLOR || dsc->header.cf == LV_IMG_CF_TRUE_COLOR_CHROMA_KEYED ||
+ dsc->header.cf == LV_IMG_CF_TRUE_COLOR_ALPHA || dsc->header.cf == LV_IMG_CF_RGB565A8) {
+ uint8_t px_size = lv_img_cf_get_px_size(dsc->header.cf) >> 3;
+ uint32_t px = dsc->header.w * y * px_size + x * px_size;
+ lv_memcpy_small(&p_color, &buf_u8[px], sizeof(lv_color_t));
+#if LV_COLOR_SIZE == 32
+ p_color.ch.alpha = 0xFF; /*Only the color should be get so use a default alpha value*/
+#endif
+ }
+ else if(dsc->header.cf == LV_IMG_CF_INDEXED_1BIT) {
+ buf_u8 += 4 * 2;
+ uint8_t bit = x & 0x7;
+ x = x >> 3;
+
+ /*Get the current pixel.
+ *dsc->header.w + 7 means rounding up to 8 because the lines are byte aligned
+ *so the possible real width are 8, 16, 24 ...*/
+ uint32_t px = ((dsc->header.w + 7) >> 3) * y + x;
+ p_color.full = (buf_u8[px] & (1 << (7 - bit))) >> (7 - bit);
+ }
+ else if(dsc->header.cf == LV_IMG_CF_INDEXED_2BIT) {
+ buf_u8 += 4 * 4;
+ uint8_t bit = (x & 0x3) * 2;
+ x = x >> 2;
+
+ /*Get the current pixel.
+ *dsc->header.w + 3 means rounding up to 4 because the lines are byte aligned
+ *so the possible real width are 4, 8, 12 ...*/
+ uint32_t px = ((dsc->header.w + 3) >> 2) * y + x;
+ p_color.full = (buf_u8[px] & (3 << (6 - bit))) >> (6 - bit);
+ }
+ else if(dsc->header.cf == LV_IMG_CF_INDEXED_4BIT) {
+ buf_u8 += 4 * 16;
+ uint8_t bit = (x & 0x1) * 4;
+ x = x >> 1;
+
+ /*Get the current pixel.
+ *dsc->header.w + 1 means rounding up to 2 because the lines are byte aligned
+ *so the possible real width are 2, 4, 6 ...*/
+ uint32_t px = ((dsc->header.w + 1) >> 1) * y + x;
+ p_color.full = (buf_u8[px] & (0xF << (4 - bit))) >> (4 - bit);
+ }
+ else if(dsc->header.cf == LV_IMG_CF_INDEXED_8BIT) {
+ buf_u8 += 4 * 256;
+ uint32_t px = dsc->header.w * y + x;
+ p_color.full = buf_u8[px];
+ }
+ else if(dsc->header.cf == LV_IMG_CF_ALPHA_1BIT || dsc->header.cf == LV_IMG_CF_ALPHA_2BIT ||
+ dsc->header.cf == LV_IMG_CF_ALPHA_4BIT || dsc->header.cf == LV_IMG_CF_ALPHA_8BIT) {
+ p_color = color;
+ }
+ return p_color;
+}
+
+/**
+ * Get the alpha value of an image's pixel
+ * @param dsc pointer to an image descriptor
+ * @param x x coordinate of the point to set
+ * @param y x coordinate of the point to set
+ * @param safe true: check out of bounds
+ * @return alpha value of the point
+ */
+lv_opa_t lv_img_buf_get_px_alpha(lv_img_dsc_t * dsc, lv_coord_t x, lv_coord_t y)
+{
+ uint8_t * buf_u8 = (uint8_t *)dsc->data;
+
+ if(dsc->header.cf == LV_IMG_CF_TRUE_COLOR_ALPHA) {
+ uint32_t px = dsc->header.w * y * LV_IMG_PX_SIZE_ALPHA_BYTE + x * LV_IMG_PX_SIZE_ALPHA_BYTE;
+ return buf_u8[px + LV_IMG_PX_SIZE_ALPHA_BYTE - 1];
+ }
+ else if(dsc->header.cf == LV_IMG_CF_ALPHA_1BIT) {
+ uint8_t bit = x & 0x7;
+ x = x >> 3;
+
+ /*Get the current pixel.
+ *dsc->header.w + 7 means rounding up to 8 because the lines are byte aligned
+ *so the possible real width are 8 ,16, 24 ...*/
+ uint32_t px = ((dsc->header.w + 7) >> 3) * y + x;
+ uint8_t px_opa = (buf_u8[px] & (1 << (7 - bit))) >> (7 - bit);
+ return px_opa ? LV_OPA_TRANSP : LV_OPA_COVER;
+ }
+ else if(dsc->header.cf == LV_IMG_CF_ALPHA_2BIT) {
+ const uint8_t opa_table[4] = {0, 85, 170, 255}; /*Opacity mapping with bpp = 2*/
+
+ uint8_t bit = (x & 0x3) * 2;
+ x = x >> 2;
+
+ /*Get the current pixel.
+ *dsc->header.w + 4 means rounding up to 8 because the lines are byte aligned
+ *so the possible real width are 4 ,8, 12 ...*/
+ uint32_t px = ((dsc->header.w + 3) >> 2) * y + x;
+ uint8_t px_opa = (buf_u8[px] & (3 << (6 - bit))) >> (6 - bit);
+ return opa_table[px_opa];
+ }
+ else if(dsc->header.cf == LV_IMG_CF_ALPHA_4BIT) {
+ const uint8_t opa_table[16] = {0, 17, 34, 51, /*Opacity mapping with bpp = 4*/
+ 68, 85, 102, 119, 136, 153, 170, 187, 204, 221, 238, 255
+ };
+
+ uint8_t bit = (x & 0x1) * 4;
+ x = x >> 1;
+
+ /*Get the current pixel.
+ *dsc->header.w + 1 means rounding up to 8 because the lines are byte aligned
+ *so the possible real width are 2 ,4, 6 ...*/
+ uint32_t px = ((dsc->header.w + 1) >> 1) * y + x;
+ uint8_t px_opa = (buf_u8[px] & (0xF << (4 - bit))) >> (4 - bit);
+ return opa_table[px_opa];
+ }
+ else if(dsc->header.cf == LV_IMG_CF_ALPHA_8BIT) {
+ uint32_t px = dsc->header.w * y + x;
+ return buf_u8[px];
+ }
+
+ return LV_OPA_COVER;
+}
+
+/**
+ * Set the alpha value of a pixel of an image. The color won't be affected
+ * @param dsc pointer to an image descriptor
+ * @param x x coordinate of the point to set
+ * @param y x coordinate of the point to set
+ * @param opa the desired opacity
+ * @param safe true: check out of bounds
+ */
+void lv_img_buf_set_px_alpha(lv_img_dsc_t * dsc, lv_coord_t x, lv_coord_t y, lv_opa_t opa)
+{
+ uint8_t * buf_u8 = (uint8_t *)dsc->data;
+
+ if(dsc->header.cf == LV_IMG_CF_TRUE_COLOR_ALPHA) {
+ uint8_t px_size = lv_img_cf_get_px_size(dsc->header.cf) >> 3;
+ uint32_t px = dsc->header.w * y * px_size + x * px_size;
+ buf_u8[px + px_size - 1] = opa;
+ }
+ else if(dsc->header.cf == LV_IMG_CF_ALPHA_1BIT) {
+ opa = opa >> 7; /*opa -> [0,1]*/
+ uint8_t bit = x & 0x7;
+ x = x >> 3;
+
+ /*Get the current pixel.
+ *dsc->header.w + 7 means rounding up to 8 because the lines are byte aligned
+ *so the possible real width are 8 ,16, 24 ...*/
+ uint32_t px = ((dsc->header.w + 7) >> 3) * y + x;
+ buf_u8[px] = buf_u8[px] & ~(1 << (7 - bit));
+ buf_u8[px] = buf_u8[px] | ((opa & 0x1) << (7 - bit));
+ }
+ else if(dsc->header.cf == LV_IMG_CF_ALPHA_2BIT) {
+ opa = opa >> 6; /*opa -> [0,3]*/
+ uint8_t bit = (x & 0x3) * 2;
+ x = x >> 2;
+
+ /*Get the current pixel.
+ *dsc->header.w + 4 means rounding up to 8 because the lines are byte aligned
+ *so the possible real width are 4 ,8, 12 ...*/
+ uint32_t px = ((dsc->header.w + 3) >> 2) * y + x;
+ buf_u8[px] = buf_u8[px] & ~(3 << (6 - bit));
+ buf_u8[px] = buf_u8[px] | ((opa & 0x3) << (6 - bit));
+ }
+ else if(dsc->header.cf == LV_IMG_CF_ALPHA_4BIT) {
+ opa = opa >> 4; /*opa -> [0,15]*/
+ uint8_t bit = (x & 0x1) * 4;
+ x = x >> 1;
+
+ /*Get the current pixel.
+ *dsc->header.w + 1 means rounding up to 8 because the lines are byte aligned
+ *so the possible real width are 2 ,4, 6 ...*/
+ uint32_t px = ((dsc->header.w + 1) >> 1) * y + x;
+ buf_u8[px] = buf_u8[px] & ~(0xF << (4 - bit));
+ buf_u8[px] = buf_u8[px] | ((opa & 0xF) << (4 - bit));
+ }
+ else if(dsc->header.cf == LV_IMG_CF_ALPHA_8BIT) {
+ uint32_t px = dsc->header.w * y + x;
+ buf_u8[px] = opa;
+ }
+}
+
+/**
+ * Set the color of a pixel of an image. The alpha channel won't be affected.
+ * @param dsc pointer to an image descriptor
+ * @param x x coordinate of the point to set
+ * @param y x coordinate of the point to set
+ * @param c color of the point
+ * @param safe true: check out of bounds
+ */
+void lv_img_buf_set_px_color(lv_img_dsc_t * dsc, lv_coord_t x, lv_coord_t y, lv_color_t c)
+{
+ uint8_t * buf_u8 = (uint8_t *)dsc->data;
+
+ if(dsc->header.cf == LV_IMG_CF_TRUE_COLOR || dsc->header.cf == LV_IMG_CF_TRUE_COLOR_CHROMA_KEYED) {
+ uint8_t px_size = lv_img_cf_get_px_size(dsc->header.cf) >> 3;
+ uint32_t px = dsc->header.w * y * px_size + x * px_size;
+ lv_memcpy_small(&buf_u8[px], &c, px_size);
+ }
+ else if(dsc->header.cf == LV_IMG_CF_TRUE_COLOR_ALPHA) {
+ uint8_t px_size = lv_img_cf_get_px_size(dsc->header.cf) >> 3;
+ uint32_t px = dsc->header.w * y * px_size + x * px_size;
+ lv_memcpy_small(&buf_u8[px], &c, px_size - 1); /*-1 to not overwrite the alpha value*/
+ }
+ else if(dsc->header.cf == LV_IMG_CF_INDEXED_1BIT) {
+ buf_u8 += sizeof(lv_color32_t) * 2; /*Skip the palette*/
+
+ uint8_t bit = x & 0x7;
+ x = x >> 3;
+
+ /*Get the current pixel.
+ *dsc->header.w + 7 means rounding up to 8 because the lines are byte aligned
+ *so the possible real width are 8 ,16, 24 ...*/
+ uint32_t px = ((dsc->header.w + 7) >> 3) * y + x;
+ buf_u8[px] = buf_u8[px] & ~(1 << (7 - bit));
+ buf_u8[px] = buf_u8[px] | ((c.full & 0x1) << (7 - bit));
+ }
+ else if(dsc->header.cf == LV_IMG_CF_INDEXED_2BIT) {
+ buf_u8 += sizeof(lv_color32_t) * 4; /*Skip the palette*/
+ uint8_t bit = (x & 0x3) * 2;
+ x = x >> 2;
+
+ /*Get the current pixel.
+ *dsc->header.w + 3 means rounding up to 4 because the lines are byte aligned
+ *so the possible real width are 4, 8 ,12 ...*/
+ uint32_t px = ((dsc->header.w + 3) >> 2) * y + x;
+
+ buf_u8[px] = buf_u8[px] & ~(3 << (6 - bit));
+ buf_u8[px] = buf_u8[px] | ((c.full & 0x3) << (6 - bit));
+ }
+ else if(dsc->header.cf == LV_IMG_CF_INDEXED_4BIT) {
+ buf_u8 += sizeof(lv_color32_t) * 16; /*Skip the palette*/
+ uint8_t bit = (x & 0x1) * 4;
+ x = x >> 1;
+
+ /*Get the current pixel.
+ *dsc->header.w + 1 means rounding up to 2 because the lines are byte aligned
+ *so the possible real width are 2 ,4, 6 ...*/
+ uint32_t px = ((dsc->header.w + 1) >> 1) * y + x;
+ buf_u8[px] = buf_u8[px] & ~(0xF << (4 - bit));
+ buf_u8[px] = buf_u8[px] | ((c.full & 0xF) << (4 - bit));
+ }
+ else if(dsc->header.cf == LV_IMG_CF_INDEXED_8BIT) {
+ buf_u8 += sizeof(lv_color32_t) * 256; /*Skip the palette*/
+ uint32_t px = dsc->header.w * y + x;
+ buf_u8[px] = c.full;
+ }
+}
+
+/**
+ * Set the palette color of an indexed image. Valid only for `LV_IMG_CF_INDEXED1/2/4/8`
+ * @param dsc pointer to an image descriptor
+ * @param id the palette color to set:
+ * - for `LV_IMG_CF_INDEXED1`: 0..1
+ * - for `LV_IMG_CF_INDEXED2`: 0..3
+ * - for `LV_IMG_CF_INDEXED4`: 0..15
+ * - for `LV_IMG_CF_INDEXED8`: 0..255
+ * @param c the color to set
+ */
+void lv_img_buf_set_palette(lv_img_dsc_t * dsc, uint8_t id, lv_color_t c)
+{
+ if((dsc->header.cf == LV_IMG_CF_ALPHA_1BIT && id > 1) || (dsc->header.cf == LV_IMG_CF_ALPHA_2BIT && id > 3) ||
+ (dsc->header.cf == LV_IMG_CF_ALPHA_4BIT && id > 15) || (dsc->header.cf == LV_IMG_CF_ALPHA_8BIT)) {
+ LV_LOG_WARN("lv_img_buf_set_px_alpha: invalid 'id'");
+ return;
+ }
+
+ lv_color32_t c32;
+ c32.full = lv_color_to32(c);
+ uint8_t * buf = (uint8_t *)dsc->data;
+ lv_memcpy_small(&buf[id * sizeof(c32)], &c32, sizeof(c32));
+}
+
+/**
+ * Allocate an image buffer in RAM
+ * @param w width of image
+ * @param h height of image
+ * @param cf a color format (`LV_IMG_CF_...`)
+ * @return an allocated image, or NULL on failure
+ */
+lv_img_dsc_t * lv_img_buf_alloc(lv_coord_t w, lv_coord_t h, lv_img_cf_t cf)
+{
+ /*Allocate image descriptor*/
+ lv_img_dsc_t * dsc = lv_mem_alloc(sizeof(lv_img_dsc_t));
+ if(dsc == NULL)
+ return NULL;
+
+ lv_memset_00(dsc, sizeof(lv_img_dsc_t));
+
+ /*Get image data size*/
+ dsc->data_size = lv_img_buf_get_img_size(w, h, cf);
+ if(dsc->data_size == 0) {
+ lv_mem_free(dsc);
+ return NULL;
+ }
+
+ /*Allocate raw buffer*/
+ dsc->data = lv_mem_alloc(dsc->data_size);
+ if(dsc->data == NULL) {
+ lv_mem_free(dsc);
+ return NULL;
+ }
+ lv_memset_00((uint8_t *)dsc->data, dsc->data_size);
+
+ /*Fill in header*/
+ dsc->header.always_zero = 0;
+ dsc->header.w = w;
+ dsc->header.h = h;
+ dsc->header.cf = cf;
+ return dsc;
+}
+
+/**
+ * Free an allocated image buffer
+ * @param dsc image buffer to free
+ */
+void lv_img_buf_free(lv_img_dsc_t * dsc)
+{
+ if(dsc != NULL) {
+ if(dsc->data != NULL)
+ lv_mem_free((void *)dsc->data);
+
+ lv_mem_free(dsc);
+ }
+}
+
+/**
+ * Get the memory consumption of a raw bitmap, given color format and dimensions.
+ * @param w width
+ * @param h height
+ * @param cf color format
+ * @return size in bytes
+ */
+uint32_t lv_img_buf_get_img_size(lv_coord_t w, lv_coord_t h, lv_img_cf_t cf)
+{
+ switch(cf) {
+ case LV_IMG_CF_TRUE_COLOR:
+ return LV_IMG_BUF_SIZE_TRUE_COLOR(w, h);
+ case LV_IMG_CF_TRUE_COLOR_ALPHA:
+ case LV_IMG_CF_RGB565A8:
+ return LV_IMG_BUF_SIZE_TRUE_COLOR_ALPHA(w, h);
+ case LV_IMG_CF_TRUE_COLOR_CHROMA_KEYED:
+ return LV_IMG_BUF_SIZE_TRUE_COLOR_CHROMA_KEYED(w, h);
+ case LV_IMG_CF_ALPHA_1BIT:
+ return LV_IMG_BUF_SIZE_ALPHA_1BIT(w, h);
+ case LV_IMG_CF_ALPHA_2BIT:
+ return LV_IMG_BUF_SIZE_ALPHA_2BIT(w, h);
+ case LV_IMG_CF_ALPHA_4BIT:
+ return LV_IMG_BUF_SIZE_ALPHA_4BIT(w, h);
+ case LV_IMG_CF_ALPHA_8BIT:
+ return LV_IMG_BUF_SIZE_ALPHA_8BIT(w, h);
+ case LV_IMG_CF_INDEXED_1BIT:
+ return LV_IMG_BUF_SIZE_INDEXED_1BIT(w, h);
+ case LV_IMG_CF_INDEXED_2BIT:
+ return LV_IMG_BUF_SIZE_INDEXED_2BIT(w, h);
+ case LV_IMG_CF_INDEXED_4BIT:
+ return LV_IMG_BUF_SIZE_INDEXED_4BIT(w, h);
+ case LV_IMG_CF_INDEXED_8BIT:
+ return LV_IMG_BUF_SIZE_INDEXED_8BIT(w, h);
+ default:
+ return 0;
+ }
+}
+
+/**
+ * Get the area of a rectangle if its rotated and scaled
+ * @param res store the coordinates here
+ * @param w width of the rectangle to transform
+ * @param h height of the rectangle to transform
+ * @param angle angle of rotation
+ * @param zoom zoom, (256 no zoom)
+ * @param pivot x,y pivot coordinates of rotation
+ */
+void _lv_img_buf_get_transformed_area(lv_area_t * res, lv_coord_t w, lv_coord_t h, int16_t angle, uint16_t zoom,
+ const lv_point_t * pivot)
+{
+#if LV_DRAW_COMPLEX
+ if(angle == 0 && zoom == LV_IMG_ZOOM_NONE) {
+ res->x1 = 0;
+ res->y1 = 0;
+ res->x2 = w - 1;
+ res->y2 = h - 1;
+ return;
+ }
+
+ lv_point_t p[4] = {
+ {0, 0},
+ {w, 0},
+ {0, h},
+ {w, h},
+ };
+ lv_point_transform(&p[0], angle, zoom, pivot);
+ lv_point_transform(&p[1], angle, zoom, pivot);
+ lv_point_transform(&p[2], angle, zoom, pivot);
+ lv_point_transform(&p[3], angle, zoom, pivot);
+ res->x1 = LV_MIN4(p[0].x, p[1].x, p[2].x, p[3].x) - 2;
+ res->x2 = LV_MAX4(p[0].x, p[1].x, p[2].x, p[3].x) + 2;
+ res->y1 = LV_MIN4(p[0].y, p[1].y, p[2].y, p[3].y) - 2;
+ res->y2 = LV_MAX4(p[0].y, p[1].y, p[2].y, p[3].y) + 2;
+
+#else
+ LV_UNUSED(angle);
+ LV_UNUSED(zoom);
+ LV_UNUSED(pivot);
+ res->x1 = 0;
+ res->y1 = 0;
+ res->x2 = w - 1;
+ res->y2 = h - 1;
+#endif
+}
+
+/**********************
+ * STATIC FUNCTIONS
+ **********************/
diff --git a/lib/lvgl/src/draw/lv_img_buf.h b/lib/lvgl/src/draw/lv_img_buf.h
new file mode 100644
index 00000000..1be7bb65
--- /dev/null
+++ b/lib/lvgl/src/draw/lv_img_buf.h
@@ -0,0 +1,249 @@
+/**
+ * @file lv_img_buf.h
+ *
+ */
+
+#ifndef LV_IMG_BUF_H
+#define LV_IMG_BUF_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*********************
+ * INCLUDES
+ *********************/
+#include <stdbool.h>
+#include "../misc/lv_color.h"
+#include "../misc/lv_area.h"
+
+/*********************
+ * DEFINES
+ *********************/
+/*If image pixels contains alpha we need to know how much byte is a pixel*/
+#if LV_COLOR_DEPTH == 1 || LV_COLOR_DEPTH == 8
+#define LV_IMG_PX_SIZE_ALPHA_BYTE 2
+#elif LV_COLOR_DEPTH == 16
+#define LV_IMG_PX_SIZE_ALPHA_BYTE 3
+#elif LV_COLOR_DEPTH == 32
+#define LV_IMG_PX_SIZE_ALPHA_BYTE 4
+#endif
+
+#define LV_IMG_BUF_SIZE_TRUE_COLOR(w, h) ((LV_COLOR_SIZE / 8) * w * h)
+#define LV_IMG_BUF_SIZE_TRUE_COLOR_CHROMA_KEYED(w, h) ((LV_COLOR_SIZE / 8) * w * h)
+#define LV_IMG_BUF_SIZE_TRUE_COLOR_ALPHA(w, h) (LV_IMG_PX_SIZE_ALPHA_BYTE * w * h)
+
+/*+ 1: to be sure no fractional row*/
+#define LV_IMG_BUF_SIZE_ALPHA_1BIT(w, h) ((((w / 8) + 1) * h))
+#define LV_IMG_BUF_SIZE_ALPHA_2BIT(w, h) ((((w / 4) + 1) * h))
+#define LV_IMG_BUF_SIZE_ALPHA_4BIT(w, h) ((((w / 2) + 1) * h))
+#define LV_IMG_BUF_SIZE_ALPHA_8BIT(w, h) ((w * h))
+
+/*4 * X: for palette*/
+#define LV_IMG_BUF_SIZE_INDEXED_1BIT(w, h) (LV_IMG_BUF_SIZE_ALPHA_1BIT(w, h) + 4 * 2)
+#define LV_IMG_BUF_SIZE_INDEXED_2BIT(w, h) (LV_IMG_BUF_SIZE_ALPHA_2BIT(w, h) + 4 * 4)
+#define LV_IMG_BUF_SIZE_INDEXED_4BIT(w, h) (LV_IMG_BUF_SIZE_ALPHA_4BIT(w, h) + 4 * 16)
+#define LV_IMG_BUF_SIZE_INDEXED_8BIT(w, h) (LV_IMG_BUF_SIZE_ALPHA_8BIT(w, h) + 4 * 256)
+
+#define _LV_ZOOM_INV_UPSCALE 5
+
+/**********************
+ * TYPEDEFS
+ **********************/
+
+/*Image color format*/
+enum {
+ LV_IMG_CF_UNKNOWN = 0,
+
+ LV_IMG_CF_RAW, /**< Contains the file as it is. Needs custom decoder function*/
+ LV_IMG_CF_RAW_ALPHA, /**< Contains the file as it is. The image has alpha. Needs custom decoder
+ function*/
+ LV_IMG_CF_RAW_CHROMA_KEYED, /**< Contains the file as it is. The image is chroma keyed. Needs
+ custom decoder function*/
+
+ LV_IMG_CF_TRUE_COLOR, /**< Color format and depth should match with LV_COLOR settings*/
+ LV_IMG_CF_TRUE_COLOR_ALPHA, /**< Same as `LV_IMG_CF_TRUE_COLOR` but every pixel has an alpha byte*/
+ LV_IMG_CF_TRUE_COLOR_CHROMA_KEYED, /**< Same as `LV_IMG_CF_TRUE_COLOR` but LV_COLOR_TRANSP pixels
+ will be transparent*/
+
+ LV_IMG_CF_INDEXED_1BIT, /**< Can have 2 different colors in a palette (can't be chroma keyed)*/
+ LV_IMG_CF_INDEXED_2BIT, /**< Can have 4 different colors in a palette (can't be chroma keyed)*/
+ LV_IMG_CF_INDEXED_4BIT, /**< Can have 16 different colors in a palette (can't be chroma keyed)*/
+ LV_IMG_CF_INDEXED_8BIT, /**< Can have 256 different colors in a palette (can't be chroma keyed)*/
+
+ LV_IMG_CF_ALPHA_1BIT, /**< Can have one color and it can be drawn or not*/
+ LV_IMG_CF_ALPHA_2BIT, /**< Can have one color but 4 different alpha value*/
+ LV_IMG_CF_ALPHA_4BIT, /**< Can have one color but 16 different alpha value*/
+ LV_IMG_CF_ALPHA_8BIT, /**< Can have one color but 256 different alpha value*/
+
+ LV_IMG_CF_RGB888,
+ LV_IMG_CF_RGBA8888,
+ LV_IMG_CF_RGBX8888,
+ LV_IMG_CF_RGB565,
+ LV_IMG_CF_RGBA5658,
+ LV_IMG_CF_RGB565A8,
+
+ LV_IMG_CF_RESERVED_15, /**< Reserved for further use.*/
+ LV_IMG_CF_RESERVED_16, /**< Reserved for further use.*/
+ LV_IMG_CF_RESERVED_17, /**< Reserved for further use.*/
+ LV_IMG_CF_RESERVED_18, /**< Reserved for further use.*/
+ LV_IMG_CF_RESERVED_19, /**< Reserved for further use.*/
+ LV_IMG_CF_RESERVED_20, /**< Reserved for further use.*/
+ LV_IMG_CF_RESERVED_21, /**< Reserved for further use.*/
+ LV_IMG_CF_RESERVED_22, /**< Reserved for further use.*/
+ LV_IMG_CF_RESERVED_23, /**< Reserved for further use.*/
+
+ LV_IMG_CF_USER_ENCODED_0, /**< User holder encoding format.*/
+ LV_IMG_CF_USER_ENCODED_1, /**< User holder encoding format.*/
+ LV_IMG_CF_USER_ENCODED_2, /**< User holder encoding format.*/
+ LV_IMG_CF_USER_ENCODED_3, /**< User holder encoding format.*/
+ LV_IMG_CF_USER_ENCODED_4, /**< User holder encoding format.*/
+ LV_IMG_CF_USER_ENCODED_5, /**< User holder encoding format.*/
+ LV_IMG_CF_USER_ENCODED_6, /**< User holder encoding format.*/
+ LV_IMG_CF_USER_ENCODED_7, /**< User holder encoding format.*/
+};
+typedef uint8_t lv_img_cf_t;
+
+
+/**
+ * The first 8 bit is very important to distinguish the different source types.
+ * For more info see `lv_img_get_src_type()` in lv_img.c
+ * On big endian systems the order is reversed so cf and always_zero must be at
+ * the end of the struct.
+ */
+#if LV_BIG_ENDIAN_SYSTEM
+typedef struct {
+
+ uint32_t h : 11; /*Height of the image map*/
+ uint32_t w : 11; /*Width of the image map*/
+ uint32_t reserved : 2; /*Reserved to be used later*/
+ uint32_t always_zero : 3; /*It the upper bits of the first byte. Always zero to look like a
+ non-printable character*/
+ uint32_t cf : 5; /*Color format: See `lv_img_color_format_t`*/
+
+} lv_img_header_t;
+#else
+typedef struct {
+
+ uint32_t cf : 5; /*Color format: See `lv_img_color_format_t`*/
+ uint32_t always_zero : 3; /*It the upper bits of the first byte. Always zero to look like a
+ non-printable character*/
+
+ uint32_t reserved : 2; /*Reserved to be used later*/
+
+ uint32_t w : 11; /*Width of the image map*/
+ uint32_t h : 11; /*Height of the image map*/
+} lv_img_header_t;
+#endif
+
+/** Image header it is compatible with
+ * the result from image converter utility*/
+typedef struct {
+ lv_img_header_t header; /**< A header describing the basics of the image*/
+ uint32_t data_size; /**< Size of the image in bytes*/
+ const uint8_t * data; /**< Pointer to the data of the image*/
+} lv_img_dsc_t;
+
+/**********************
+ * GLOBAL PROTOTYPES
+ **********************/
+
+/**
+ * Allocate an image buffer in RAM
+ * @param w width of image
+ * @param h height of image
+ * @param cf a color format (`LV_IMG_CF_...`)
+ * @return an allocated image, or NULL on failure
+ */
+lv_img_dsc_t * lv_img_buf_alloc(lv_coord_t w, lv_coord_t h, lv_img_cf_t cf);
+
+/**
+ * Get the color of an image's pixel
+ * @param dsc an image descriptor
+ * @param x x coordinate of the point to get
+ * @param y x coordinate of the point to get
+ * @param color the color of the image. In case of `LV_IMG_CF_ALPHA_1/2/4/8` this color is used.
+ * Not used in other cases.
+ * @param safe true: check out of bounds
+ * @return color of the point
+ */
+lv_color_t lv_img_buf_get_px_color(lv_img_dsc_t * dsc, lv_coord_t x, lv_coord_t y, lv_color_t color);
+
+/**
+ * Get the alpha value of an image's pixel
+ * @param dsc pointer to an image descriptor
+ * @param x x coordinate of the point to set
+ * @param y x coordinate of the point to set
+ * @param safe true: check out of bounds
+ * @return alpha value of the point
+ */
+lv_opa_t lv_img_buf_get_px_alpha(lv_img_dsc_t * dsc, lv_coord_t x, lv_coord_t y);
+
+/**
+ * Set the color of a pixel of an image. The alpha channel won't be affected.
+ * @param dsc pointer to an image descriptor
+ * @param x x coordinate of the point to set
+ * @param y x coordinate of the point to set
+ * @param c color of the point
+ * @param safe true: check out of bounds
+ */
+void lv_img_buf_set_px_color(lv_img_dsc_t * dsc, lv_coord_t x, lv_coord_t y, lv_color_t c);
+
+/**
+ * Set the alpha value of a pixel of an image. The color won't be affected
+ * @param dsc pointer to an image descriptor
+ * @param x x coordinate of the point to set
+ * @param y x coordinate of the point to set
+ * @param opa the desired opacity
+ * @param safe true: check out of bounds
+ */
+void lv_img_buf_set_px_alpha(lv_img_dsc_t * dsc, lv_coord_t x, lv_coord_t y, lv_opa_t opa);
+
+/**
+ * Set the palette color of an indexed image. Valid only for `LV_IMG_CF_INDEXED1/2/4/8`
+ * @param dsc pointer to an image descriptor
+ * @param id the palette color to set:
+ * - for `LV_IMG_CF_INDEXED1`: 0..1
+ * - for `LV_IMG_CF_INDEXED2`: 0..3
+ * - for `LV_IMG_CF_INDEXED4`: 0..15
+ * - for `LV_IMG_CF_INDEXED8`: 0..255
+ * @param c the color to set
+ */
+void lv_img_buf_set_palette(lv_img_dsc_t * dsc, uint8_t id, lv_color_t c);
+
+/**
+ * Free an allocated image buffer
+ * @param dsc image buffer to free
+ */
+void lv_img_buf_free(lv_img_dsc_t * dsc);
+
+/**
+ * Get the memory consumption of a raw bitmap, given color format and dimensions.
+ * @param w width
+ * @param h height
+ * @param cf color format
+ * @return size in bytes
+ */
+uint32_t lv_img_buf_get_img_size(lv_coord_t w, lv_coord_t h, lv_img_cf_t cf);
+
+/**
+ * Get the area of a rectangle if its rotated and scaled
+ * @param res store the coordinates here
+ * @param w width of the rectangle to transform
+ * @param h height of the rectangle to transform
+ * @param angle angle of rotation
+ * @param zoom zoom, (256 no zoom)
+ * @param pivot x,y pivot coordinates of rotation
+ */
+void _lv_img_buf_get_transformed_area(lv_area_t * res, lv_coord_t w, lv_coord_t h, int16_t angle, uint16_t zoom,
+ const lv_point_t * pivot);
+
+/**********************
+ * MACROS
+ **********************/
+
+#ifdef __cplusplus
+} /*extern "C"*/
+#endif
+
+#endif /*LV_IMG_BUF_H*/
diff --git a/lib/lvgl/src/draw/lv_img_cache.c b/lib/lvgl/src/draw/lv_img_cache.c
new file mode 100644
index 00000000..2caf5121
--- /dev/null
+++ b/lib/lvgl/src/draw/lv_img_cache.c
@@ -0,0 +1,215 @@
+/**
+ * @file lv_img_cache.c
+ *
+ */
+
+/*********************
+ * INCLUDES
+ *********************/
+#include "../misc/lv_assert.h"
+#include "lv_img_cache.h"
+#include "lv_img_decoder.h"
+#include "lv_draw_img.h"
+#include "../hal/lv_hal_tick.h"
+#include "../misc/lv_gc.h"
+
+/*********************
+ * DEFINES
+ *********************/
+/*Decrement life with this value on every open*/
+#define LV_IMG_CACHE_AGING 1
+
+/*Boost life by this factor (multiply time_to_open with this value)*/
+#define LV_IMG_CACHE_LIFE_GAIN 1
+
+/*Don't let life to be greater than this limit because it would require a lot of time to
+ * "die" from very high values*/
+#define LV_IMG_CACHE_LIFE_LIMIT 1000
+
+/**********************
+ * TYPEDEFS
+ **********************/
+
+/**********************
+ * STATIC PROTOTYPES
+ **********************/
+#if LV_IMG_CACHE_DEF_SIZE
+ static bool lv_img_cache_match(const void * src1, const void * src2);
+#endif
+
+/**********************
+ * STATIC VARIABLES
+ **********************/
+#if LV_IMG_CACHE_DEF_SIZE
+ static uint16_t entry_cnt;
+#endif
+
+/**********************
+ * MACROS
+ **********************/
+
+/**********************
+ * GLOBAL FUNCTIONS
+ **********************/
+
+/**
+ * Open an image using the image decoder interface and cache it.
+ * The image will be left open meaning if the image decoder open callback allocated memory then it will remain.
+ * The image is closed if a new image is opened and the new image takes its place in the cache.
+ * @param src source of the image. Path to file or pointer to an `lv_img_dsc_t` variable
+ * @param color color The color of the image with `LV_IMG_CF_ALPHA_...`
+ * @return pointer to the cache entry or NULL if can open the image
+ */
+_lv_img_cache_entry_t * _lv_img_cache_open(const void * src, lv_color_t color, int32_t frame_id)
+{
+ /*Is the image cached?*/
+ _lv_img_cache_entry_t * cached_src = NULL;
+
+#if LV_IMG_CACHE_DEF_SIZE
+ if(entry_cnt == 0) {
+ LV_LOG_WARN("lv_img_cache_open: the cache size is 0");
+ return NULL;
+ }
+
+ _lv_img_cache_entry_t * cache = LV_GC_ROOT(_lv_img_cache_array);
+
+ /*Decrement all lifes. Make the entries older*/
+ uint16_t i;
+ for(i = 0; i < entry_cnt; i++) {
+ if(cache[i].life > INT32_MIN + LV_IMG_CACHE_AGING) {
+ cache[i].life -= LV_IMG_CACHE_AGING;
+ }
+ }
+
+ for(i = 0; i < entry_cnt; i++) {
+ if(color.full == cache[i].dec_dsc.color.full &&
+ frame_id == cache[i].dec_dsc.frame_id &&
+ lv_img_cache_match(src, cache[i].dec_dsc.src)) {
+ /*If opened increment its life.
+ *Image difficult to open should live longer to keep avoid frequent their recaching.
+ *Therefore increase `life` with `time_to_open`*/
+ cached_src = &cache[i];
+ cached_src->life += cached_src->dec_dsc.time_to_open * LV_IMG_CACHE_LIFE_GAIN;
+ if(cached_src->life > LV_IMG_CACHE_LIFE_LIMIT) cached_src->life = LV_IMG_CACHE_LIFE_LIMIT;
+ LV_LOG_TRACE("image source found in the cache");
+ break;
+ }
+ }
+
+ /*The image is not cached then cache it now*/
+ if(cached_src) return cached_src;
+
+ /*Find an entry to reuse. Select the entry with the least life*/
+ cached_src = &cache[0];
+ for(i = 1; i < entry_cnt; i++) {
+ if(cache[i].life < cached_src->life) {
+ cached_src = &cache[i];
+ }
+ }
+
+ /*Close the decoder to reuse if it was opened (has a valid source)*/
+ if(cached_src->dec_dsc.src) {
+ lv_img_decoder_close(&cached_src->dec_dsc);
+ LV_LOG_INFO("image draw: cache miss, close and reuse an entry");
+ }
+ else {
+ LV_LOG_INFO("image draw: cache miss, cached to an empty entry");
+ }
+#else
+ cached_src = &LV_GC_ROOT(_lv_img_cache_single);
+#endif
+ /*Open the image and measure the time to open*/
+ uint32_t t_start = lv_tick_get();
+ lv_res_t open_res = lv_img_decoder_open(&cached_src->dec_dsc, src, color, frame_id);
+ if(open_res == LV_RES_INV) {
+ LV_LOG_WARN("Image draw cannot open the image resource");
+ lv_memset_00(cached_src, sizeof(_lv_img_cache_entry_t));
+ cached_src->life = INT32_MIN; /*Make the empty entry very "weak" to force its us*/
+ return NULL;
+ }
+
+ cached_src->life = 0;
+
+ /*If `time_to_open` was not set in the open function set it here*/
+ if(cached_src->dec_dsc.time_to_open == 0) {
+ cached_src->dec_dsc.time_to_open = lv_tick_elaps(t_start);
+ }
+
+ if(cached_src->dec_dsc.time_to_open == 0) cached_src->dec_dsc.time_to_open = 1;
+
+ return cached_src;
+}
+
+/**
+ * Set the number of images to be cached.
+ * More cached images mean more opened image at same time which might mean more memory usage.
+ * E.g. if 20 PNG or JPG images are open in the RAM they consume memory while opened in the cache.
+ * @param new_entry_cnt number of image to cache
+ */
+void lv_img_cache_set_size(uint16_t new_entry_cnt)
+{
+#if LV_IMG_CACHE_DEF_SIZE == 0
+ LV_UNUSED(new_entry_cnt);
+ LV_LOG_WARN("Can't change cache size because it's disabled by LV_IMG_CACHE_DEF_SIZE = 0");
+#else
+ if(LV_GC_ROOT(_lv_img_cache_array) != NULL) {
+ /*Clean the cache before free it*/
+ lv_img_cache_invalidate_src(NULL);
+ lv_mem_free(LV_GC_ROOT(_lv_img_cache_array));
+ }
+
+ /*Reallocate the cache*/
+ LV_GC_ROOT(_lv_img_cache_array) = lv_mem_alloc(sizeof(_lv_img_cache_entry_t) * new_entry_cnt);
+ LV_ASSERT_MALLOC(LV_GC_ROOT(_lv_img_cache_array));
+ if(LV_GC_ROOT(_lv_img_cache_array) == NULL) {
+ entry_cnt = 0;
+ return;
+ }
+ entry_cnt = new_entry_cnt;
+
+ /*Clean the cache*/
+ lv_memset_00(LV_GC_ROOT(_lv_img_cache_array), entry_cnt * sizeof(_lv_img_cache_entry_t));
+#endif
+}
+
+/**
+ * Invalidate an image source in the cache.
+ * Useful if the image source is updated therefore it needs to be cached again.
+ * @param src an image source path to a file or pointer to an `lv_img_dsc_t` variable.
+ */
+void lv_img_cache_invalidate_src(const void * src)
+{
+ LV_UNUSED(src);
+#if LV_IMG_CACHE_DEF_SIZE
+ _lv_img_cache_entry_t * cache = LV_GC_ROOT(_lv_img_cache_array);
+
+ uint16_t i;
+ for(i = 0; i < entry_cnt; i++) {
+ if(src == NULL || lv_img_cache_match(src, cache[i].dec_dsc.src)) {
+ if(cache[i].dec_dsc.src != NULL) {
+ lv_img_decoder_close(&cache[i].dec_dsc);
+ }
+
+ lv_memset_00(&cache[i], sizeof(_lv_img_cache_entry_t));
+ }
+ }
+#endif
+}
+
+/**********************
+ * STATIC FUNCTIONS
+ **********************/
+
+#if LV_IMG_CACHE_DEF_SIZE
+static bool lv_img_cache_match(const void * src1, const void * src2)
+{
+ lv_img_src_t src_type = lv_img_src_get_type(src1);
+ if(src_type == LV_IMG_SRC_VARIABLE)
+ return src1 == src2;
+ if(src_type != LV_IMG_SRC_FILE)
+ return false;
+ if(lv_img_src_get_type(src2) != LV_IMG_SRC_FILE)
+ return false;
+ return strcmp(src1, src2) == 0;
+}
+#endif
diff --git a/lib/lvgl/src/draw/lv_img_cache.h b/lib/lvgl/src/draw/lv_img_cache.h
new file mode 100644
index 00000000..dc0c5d98
--- /dev/null
+++ b/lib/lvgl/src/draw/lv_img_cache.h
@@ -0,0 +1,78 @@
+/**
+ * @file lv_img_cache.h
+ *
+ */
+
+#ifndef LV_IMG_CACHE_H
+#define LV_IMG_CACHE_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*********************
+ * INCLUDES
+ *********************/
+#include "lv_img_decoder.h"
+
+/*********************
+ * DEFINES
+ *********************/
+
+/**********************
+ * TYPEDEFS
+ **********************/
+
+/**
+ * When loading images from the network it can take a long time to download and decode the image.
+ *
+ * To avoid repeating this heavy load images can be cached.
+ */
+typedef struct {
+ lv_img_decoder_dsc_t dec_dsc; /**< Image information*/
+
+ /** Count the cache entries's life. Add `time_to_open` to `life` when the entry is used.
+ * Decrement all lifes by one every in every ::lv_img_cache_open.
+ * If life == 0 the entry can be reused*/
+ int32_t life;
+} _lv_img_cache_entry_t;
+
+/**********************
+ * GLOBAL PROTOTYPES
+ **********************/
+
+/**
+ * Open an image using the image decoder interface and cache it.
+ * The image will be left open meaning if the image decoder open callback allocated memory then it will remain.
+ * The image is closed if a new image is opened and the new image takes its place in the cache.
+ * @param src source of the image. Path to file or pointer to an `lv_img_dsc_t` variable
+ * @param color The color of the image with `LV_IMG_CF_ALPHA_...`
+ * @param frame_id the index of the frame. Used only with animated images, set 0 for normal images
+ * @return pointer to the cache entry or NULL if can open the image
+ */
+_lv_img_cache_entry_t * _lv_img_cache_open(const void * src, lv_color_t color, int32_t frame_id);
+
+/**
+ * Set the number of images to be cached.
+ * More cached images mean more opened image at same time which might mean more memory usage.
+ * E.g. if 20 PNG or JPG images are open in the RAM they consume memory while opened in the cache.
+ * @param new_entry_cnt number of image to cache
+ */
+void lv_img_cache_set_size(uint16_t new_slot_num);
+
+/**
+ * Invalidate an image source in the cache.
+ * Useful if the image source is updated therefore it needs to be cached again.
+ * @param src an image source path to a file or pointer to an `lv_img_dsc_t` variable.
+ */
+void lv_img_cache_invalidate_src(const void * src);
+
+/**********************
+ * MACROS
+ **********************/
+
+#ifdef __cplusplus
+} /*extern "C"*/
+#endif
+
+#endif /*LV_IMG_CACHE_H*/
diff --git a/lib/lvgl/src/draw/lv_img_decoder.c b/lib/lvgl/src/draw/lv_img_decoder.c
new file mode 100644
index 00000000..13050b8b
--- /dev/null
+++ b/lib/lvgl/src/draw/lv_img_decoder.c
@@ -0,0 +1,705 @@
+/**
+ * @file lv_img_decoder.c
+ *
+ */
+
+/*********************
+ * INCLUDES
+ *********************/
+#include "lv_img_decoder.h"
+#include "../misc/lv_assert.h"
+#include "../draw/lv_draw_img.h"
+#include "../misc/lv_ll.h"
+#include "../misc/lv_gc.h"
+
+/*********************
+ * DEFINES
+ *********************/
+#define CF_BUILT_IN_FIRST LV_IMG_CF_TRUE_COLOR
+#define CF_BUILT_IN_LAST LV_IMG_CF_RGB565A8
+
+/**********************
+ * TYPEDEFS
+ **********************/
+
+typedef struct {
+ lv_fs_file_t f;
+ lv_color_t * palette;
+ lv_opa_t * opa;
+} lv_img_decoder_built_in_data_t;
+
+/**********************
+ * STATIC PROTOTYPES
+ **********************/
+static lv_res_t lv_img_decoder_built_in_line_true_color(lv_img_decoder_dsc_t * dsc, lv_coord_t x, lv_coord_t y,
+ lv_coord_t len, uint8_t * buf);
+static lv_res_t lv_img_decoder_built_in_line_alpha(lv_img_decoder_dsc_t * dsc, lv_coord_t x, lv_coord_t y,
+ lv_coord_t len, uint8_t * buf);
+static lv_res_t lv_img_decoder_built_in_line_indexed(lv_img_decoder_dsc_t * dsc, lv_coord_t x, lv_coord_t y,
+ lv_coord_t len, uint8_t * buf);
+
+/**********************
+ * STATIC VARIABLES
+ **********************/
+
+/**********************
+ * MACROS
+ **********************/
+
+/**********************
+ * GLOBAL FUNCTIONS
+ **********************/
+
+/**
+ * Initialize the image decoder module
+ */
+void _lv_img_decoder_init(void)
+{
+ _lv_ll_init(&LV_GC_ROOT(_lv_img_decoder_ll), sizeof(lv_img_decoder_t));
+
+ lv_img_decoder_t * decoder;
+
+ /*Create a decoder for the built in color format*/
+ decoder = lv_img_decoder_create();
+ LV_ASSERT_MALLOC(decoder);
+ if(decoder == NULL) {
+ LV_LOG_WARN("lv_img_decoder_init: out of memory");
+ return;
+ }
+
+ lv_img_decoder_set_info_cb(decoder, lv_img_decoder_built_in_info);
+ lv_img_decoder_set_open_cb(decoder, lv_img_decoder_built_in_open);
+ lv_img_decoder_set_read_line_cb(decoder, lv_img_decoder_built_in_read_line);
+ lv_img_decoder_set_close_cb(decoder, lv_img_decoder_built_in_close);
+}
+
+/**
+ * Get information about an image.
+ * Try the created image decoder one by one. Once one is able to get info that info will be used.
+ * @param src the image source. E.g. file name or variable.
+ * @param header the image info will be stored here
+ * @return LV_RES_OK: success; LV_RES_INV: wasn't able to get info about the image
+ */
+lv_res_t lv_img_decoder_get_info(const void * src, lv_img_header_t * header)
+{
+ lv_memset_00(header, sizeof(lv_img_header_t));
+
+ if(src == NULL) return LV_RES_INV;
+
+ lv_img_src_t src_type = lv_img_src_get_type(src);
+ if(src_type == LV_IMG_SRC_VARIABLE) {
+ const lv_img_dsc_t * img_dsc = src;
+ if(img_dsc->data == NULL) return LV_RES_INV;
+ }
+
+ lv_res_t res = LV_RES_INV;
+ lv_img_decoder_t * d;
+ _LV_LL_READ(&LV_GC_ROOT(_lv_img_decoder_ll), d) {
+ if(d->info_cb) {
+ res = d->info_cb(d, src, header);
+ if(res == LV_RES_OK) break;
+ }
+ }
+
+ return res;
+}
+
+lv_res_t lv_img_decoder_open(lv_img_decoder_dsc_t * dsc, const void * src, lv_color_t color, int32_t frame_id)
+{
+ lv_memset_00(dsc, sizeof(lv_img_decoder_dsc_t));
+
+ if(src == NULL) return LV_RES_INV;
+ lv_img_src_t src_type = lv_img_src_get_type(src);
+ if(src_type == LV_IMG_SRC_VARIABLE) {
+ const lv_img_dsc_t * img_dsc = src;
+ if(img_dsc->data == NULL) return LV_RES_INV;
+ }
+
+ dsc->color = color;
+ dsc->src_type = src_type;
+ dsc->frame_id = frame_id;
+
+ if(dsc->src_type == LV_IMG_SRC_FILE) {
+ size_t fnlen = strlen(src);
+ dsc->src = lv_mem_alloc(fnlen + 1);
+ LV_ASSERT_MALLOC(dsc->src);
+ if(dsc->src == NULL) {
+ LV_LOG_WARN("lv_img_decoder_open: out of memory");
+ return LV_RES_INV;
+ }
+ strcpy((char *)dsc->src, src);
+ }
+ else {
+ dsc->src = src;
+ }
+
+ lv_res_t res = LV_RES_INV;
+
+ lv_img_decoder_t * decoder;
+ _LV_LL_READ(&LV_GC_ROOT(_lv_img_decoder_ll), decoder) {
+ /*Info and Open callbacks are required*/
+ if(decoder->info_cb == NULL || decoder->open_cb == NULL) continue;
+
+ res = decoder->info_cb(decoder, src, &dsc->header);
+ if(res != LV_RES_OK) continue;
+
+ dsc->decoder = decoder;
+ res = decoder->open_cb(decoder, dsc);
+
+ /*Opened successfully. It is a good decoder for this image source*/
+ if(res == LV_RES_OK) return res;
+
+ /*Prepare for the next loop*/
+ lv_memset_00(&dsc->header, sizeof(lv_img_header_t));
+
+ dsc->error_msg = NULL;
+ dsc->img_data = NULL;
+ dsc->user_data = NULL;
+ dsc->time_to_open = 0;
+ }
+
+ if(dsc->src_type == LV_IMG_SRC_FILE)
+ lv_mem_free((void *)dsc->src);
+
+ return res;
+}
+
+/**
+ * Read a line from an opened image
+ * @param dsc pointer to `lv_img_decoder_dsc_t` used in `lv_img_decoder_open`
+ * @param x start X coordinate (from left)
+ * @param y start Y coordinate (from top)
+ * @param len number of pixels to read
+ * @param buf store the data here
+ * @return LV_RES_OK: success; LV_RES_INV: an error occurred
+ */
+lv_res_t lv_img_decoder_read_line(lv_img_decoder_dsc_t * dsc, lv_coord_t x, lv_coord_t y, lv_coord_t len, uint8_t * buf)
+{
+ lv_res_t res = LV_RES_INV;
+ if(dsc->decoder->read_line_cb) res = dsc->decoder->read_line_cb(dsc->decoder, dsc, x, y, len, buf);
+
+ return res;
+}
+
+/**
+ * Close a decoding session
+ * @param dsc pointer to `lv_img_decoder_dsc_t` used in `lv_img_decoder_open`
+ */
+void lv_img_decoder_close(lv_img_decoder_dsc_t * dsc)
+{
+ if(dsc->decoder) {
+ if(dsc->decoder->close_cb) dsc->decoder->close_cb(dsc->decoder, dsc);
+
+ if(dsc->src_type == LV_IMG_SRC_FILE) {
+ lv_mem_free((void *)dsc->src);
+ dsc->src = NULL;
+ }
+ }
+}
+
+/**
+ * Create a new image decoder
+ * @return pointer to the new image decoder
+ */
+lv_img_decoder_t * lv_img_decoder_create(void)
+{
+ lv_img_decoder_t * decoder;
+ decoder = _lv_ll_ins_head(&LV_GC_ROOT(_lv_img_decoder_ll));
+ LV_ASSERT_MALLOC(decoder);
+ if(decoder == NULL) return NULL;
+
+ lv_memset_00(decoder, sizeof(lv_img_decoder_t));
+
+ return decoder;
+}
+
+/**
+ * Delete an image decoder
+ * @param decoder pointer to an image decoder
+ */
+void lv_img_decoder_delete(lv_img_decoder_t * decoder)
+{
+ _lv_ll_remove(&LV_GC_ROOT(_lv_img_decoder_ll), decoder);
+ lv_mem_free(decoder);
+}
+
+/**
+ * Set a callback to get information about the image
+ * @param decoder pointer to an image decoder
+ * @param info_cb a function to collect info about an image (fill an `lv_img_header_t` struct)
+ */
+void lv_img_decoder_set_info_cb(lv_img_decoder_t * decoder, lv_img_decoder_info_f_t info_cb)
+{
+ decoder->info_cb = info_cb;
+}
+
+/**
+ * Set a callback to open an image
+ * @param decoder pointer to an image decoder
+ * @param open_cb a function to open an image
+ */
+void lv_img_decoder_set_open_cb(lv_img_decoder_t * decoder, lv_img_decoder_open_f_t open_cb)
+{
+ decoder->open_cb = open_cb;
+}
+
+/**
+ * Set a callback to a decoded line of an image
+ * @param decoder pointer to an image decoder
+ * @param read_line_cb a function to read a line of an image
+ */
+void lv_img_decoder_set_read_line_cb(lv_img_decoder_t * decoder, lv_img_decoder_read_line_f_t read_line_cb)
+{
+ decoder->read_line_cb = read_line_cb;
+}
+
+/**
+ * Set a callback to close a decoding session. E.g. close files and free other resources.
+ * @param decoder pointer to an image decoder
+ * @param close_cb a function to close a decoding session
+ */
+void lv_img_decoder_set_close_cb(lv_img_decoder_t * decoder, lv_img_decoder_close_f_t close_cb)
+{
+ decoder->close_cb = close_cb;
+}
+
+/**
+ * Get info about a built-in image
+ * @param decoder the decoder where this function belongs
+ * @param src the image source: pointer to an `lv_img_dsc_t` variable, a file path or a symbol
+ * @param header store the image data here
+ * @return LV_RES_OK: the info is successfully stored in `header`; LV_RES_INV: unknown format or other error.
+ */
+lv_res_t lv_img_decoder_built_in_info(lv_img_decoder_t * decoder, const void * src, lv_img_header_t * header)
+{
+ LV_UNUSED(decoder); /*Unused*/
+
+ lv_img_src_t src_type = lv_img_src_get_type(src);
+ if(src_type == LV_IMG_SRC_VARIABLE) {
+ lv_img_cf_t cf = ((lv_img_dsc_t *)src)->header.cf;
+ if(cf < CF_BUILT_IN_FIRST || cf > CF_BUILT_IN_LAST) return LV_RES_INV;
+
+ header->w = ((lv_img_dsc_t *)src)->header.w;
+ header->h = ((lv_img_dsc_t *)src)->header.h;
+ header->cf = ((lv_img_dsc_t *)src)->header.cf;
+ }
+ else if(src_type == LV_IMG_SRC_FILE) {
+ /*Support only "*.bin" files*/
+ if(strcmp(lv_fs_get_ext(src), "bin")) return LV_RES_INV;
+
+ lv_fs_file_t f;
+ lv_fs_res_t res = lv_fs_open(&f, src, LV_FS_MODE_RD);
+ if(res == LV_FS_RES_OK) {
+ uint32_t rn;
+ res = lv_fs_read(&f, header, sizeof(lv_img_header_t), &rn);
+ lv_fs_close(&f);
+ if(res != LV_FS_RES_OK || rn != sizeof(lv_img_header_t)) {
+ LV_LOG_WARN("Image get info get read file header");
+ return LV_RES_INV;
+ }
+ }
+
+ if(header->cf < CF_BUILT_IN_FIRST || header->cf > CF_BUILT_IN_LAST) return LV_RES_INV;
+ }
+ else if(src_type == LV_IMG_SRC_SYMBOL) {
+ /*The size depend on the font but it is unknown here. It should be handled outside of the
+ *function*/
+ header->w = 1;
+ header->h = 1;
+ /*Symbols always have transparent parts. Important because of cover check in the draw
+ *function. The actual value doesn't matter because lv_draw_label will draw it*/
+ header->cf = LV_IMG_CF_ALPHA_1BIT;
+ }
+ else {
+ LV_LOG_WARN("Image get info found unknown src type");
+ return LV_RES_INV;
+ }
+ return LV_RES_OK;
+}
+
+/**
+ * Open a built in image
+ * @param decoder the decoder where this function belongs
+ * @param dsc pointer to decoder descriptor. `src`, `color` are already initialized in it.
+ * @return LV_RES_OK: the info is successfully stored in `header`; LV_RES_INV: unknown format or other error.
+ */
+lv_res_t lv_img_decoder_built_in_open(lv_img_decoder_t * decoder, lv_img_decoder_dsc_t * dsc)
+{
+ /*Open the file if it's a file*/
+ if(dsc->src_type == LV_IMG_SRC_FILE) {
+ /*Support only "*.bin" files*/
+ if(strcmp(lv_fs_get_ext(dsc->src), "bin")) return LV_RES_INV;
+
+ lv_fs_file_t f;
+ lv_fs_res_t res = lv_fs_open(&f, dsc->src, LV_FS_MODE_RD);
+ if(res != LV_FS_RES_OK) {
+ LV_LOG_WARN("Built-in image decoder can't open the file");
+ return LV_RES_INV;
+ }
+
+ /*If the file was open successfully save the file descriptor*/
+ if(dsc->user_data == NULL) {
+ dsc->user_data = lv_mem_alloc(sizeof(lv_img_decoder_built_in_data_t));
+ LV_ASSERT_MALLOC(dsc->user_data);
+ if(dsc->user_data == NULL) {
+ LV_LOG_ERROR("img_decoder_built_in_open: out of memory");
+ lv_fs_close(&f);
+ return LV_RES_INV;
+ }
+ lv_memset_00(dsc->user_data, sizeof(lv_img_decoder_built_in_data_t));
+ }
+
+ lv_img_decoder_built_in_data_t * user_data = dsc->user_data;
+ lv_memcpy_small(&user_data->f, &f, sizeof(f));
+ }
+ else if(dsc->src_type == LV_IMG_SRC_VARIABLE) {
+ /*The variables should have valid data*/
+ if(((lv_img_dsc_t *)dsc->src)->data == NULL) {
+ return LV_RES_INV;
+ }
+ }
+
+ lv_img_cf_t cf = dsc->header.cf;
+ /*Process true color formats*/
+ if(cf == LV_IMG_CF_TRUE_COLOR || cf == LV_IMG_CF_TRUE_COLOR_ALPHA ||
+ cf == LV_IMG_CF_TRUE_COLOR_CHROMA_KEYED || cf == LV_IMG_CF_RGB565A8 ||
+ cf == LV_IMG_CF_ALPHA_8BIT) {
+ if(dsc->src_type == LV_IMG_SRC_VARIABLE) {
+ /*In case of uncompressed formats the image stored in the ROM/RAM.
+ *So simply give its pointer*/
+ dsc->img_data = ((lv_img_dsc_t *)dsc->src)->data;
+ return LV_RES_OK;
+ }
+ else {
+ /*If it's a file it need to be read line by line later*/
+ return LV_RES_OK;
+ }
+ }
+ /*Process indexed images. Build a palette*/
+ else if(cf == LV_IMG_CF_INDEXED_1BIT || cf == LV_IMG_CF_INDEXED_2BIT || cf == LV_IMG_CF_INDEXED_4BIT ||
+ cf == LV_IMG_CF_INDEXED_8BIT) {
+ uint8_t px_size = lv_img_cf_get_px_size(cf);
+ uint32_t palette_size = 1 << px_size;
+
+ /*Allocate the palette*/
+ if(dsc->user_data == NULL) {
+ dsc->user_data = lv_mem_alloc(sizeof(lv_img_decoder_built_in_data_t));
+ LV_ASSERT_MALLOC(dsc->user_data);
+ if(dsc->user_data == NULL) {
+ LV_LOG_ERROR("img_decoder_built_in_open: out of memory");
+ return LV_RES_INV;
+ }
+ lv_memset_00(dsc->user_data, sizeof(lv_img_decoder_built_in_data_t));
+ }
+
+ lv_img_decoder_built_in_data_t * user_data = dsc->user_data;
+ user_data->palette = lv_mem_alloc(palette_size * sizeof(lv_color_t));
+ LV_ASSERT_MALLOC(user_data->palette);
+ user_data->opa = lv_mem_alloc(palette_size * sizeof(lv_opa_t));
+ LV_ASSERT_MALLOC(user_data->opa);
+ if(user_data->palette == NULL || user_data->opa == NULL) {
+ LV_LOG_ERROR("img_decoder_built_in_open: out of memory");
+ lv_img_decoder_built_in_close(decoder, dsc);
+ return LV_RES_INV;
+ }
+
+ if(dsc->src_type == LV_IMG_SRC_FILE) {
+ /*Read the palette from file*/
+ lv_fs_seek(&user_data->f, 4, LV_FS_SEEK_SET); /*Skip the header*/
+ lv_color32_t cur_color;
+ uint32_t i;
+ for(i = 0; i < palette_size; i++) {
+ lv_fs_read(&user_data->f, &cur_color, sizeof(lv_color32_t), NULL);
+ user_data->palette[i] = lv_color_make(cur_color.ch.red, cur_color.ch.green, cur_color.ch.blue);
+ user_data->opa[i] = cur_color.ch.alpha;
+ }
+ }
+ else {
+ /*The palette begins in the beginning of the image data. Just point to it.*/
+ lv_color32_t * palette_p = (lv_color32_t *)((lv_img_dsc_t *)dsc->src)->data;
+
+ uint32_t i;
+ for(i = 0; i < palette_size; i++) {
+ user_data->palette[i] = lv_color_make(palette_p[i].ch.red, palette_p[i].ch.green, palette_p[i].ch.blue);
+ user_data->opa[i] = palette_p[i].ch.alpha;
+ }
+ }
+
+ return LV_RES_OK;
+ }
+ /*Alpha indexed images.*/
+ else if(cf == LV_IMG_CF_ALPHA_1BIT || cf == LV_IMG_CF_ALPHA_2BIT || cf == LV_IMG_CF_ALPHA_4BIT) {
+ return LV_RES_OK; /*Nothing to process*/
+ }
+ /*Unknown format. Can't decode it.*/
+ else {
+ /*Free the potentially allocated memories*/
+ lv_img_decoder_built_in_close(decoder, dsc);
+
+ LV_LOG_WARN("Image decoder open: unknown color format");
+ return LV_RES_INV;
+ }
+}
+
+/**
+ * Decode `len` pixels starting from the given `x`, `y` coordinates and store them in `buf`.
+ * Required only if the "open" function can't return with the whole decoded pixel array.
+ * @param decoder pointer to the decoder the function associated with
+ * @param dsc pointer to decoder descriptor
+ * @param x start x coordinate
+ * @param y start y coordinate
+ * @param len number of pixels to decode
+ * @param buf a buffer to store the decoded pixels
+ * @return LV_RES_OK: ok; LV_RES_INV: failed
+ */
+lv_res_t lv_img_decoder_built_in_read_line(lv_img_decoder_t * decoder, lv_img_decoder_dsc_t * dsc, lv_coord_t x,
+ lv_coord_t y, lv_coord_t len, uint8_t * buf)
+{
+ LV_UNUSED(decoder); /*Unused*/
+
+ lv_res_t res = LV_RES_INV;
+
+ if(dsc->header.cf == LV_IMG_CF_TRUE_COLOR || dsc->header.cf == LV_IMG_CF_TRUE_COLOR_ALPHA ||
+ dsc->header.cf == LV_IMG_CF_TRUE_COLOR_CHROMA_KEYED) {
+ /*For TRUE_COLOR images read line required only for files.
+ *For variables the image data was returned in `open`*/
+ if(dsc->src_type == LV_IMG_SRC_FILE) {
+ res = lv_img_decoder_built_in_line_true_color(dsc, x, y, len, buf);
+ }
+ }
+ else if(dsc->header.cf == LV_IMG_CF_ALPHA_1BIT || dsc->header.cf == LV_IMG_CF_ALPHA_2BIT ||
+ dsc->header.cf == LV_IMG_CF_ALPHA_4BIT || dsc->header.cf == LV_IMG_CF_ALPHA_8BIT) {
+ res = lv_img_decoder_built_in_line_alpha(dsc, x, y, len, buf);
+ }
+ else if(dsc->header.cf == LV_IMG_CF_INDEXED_1BIT || dsc->header.cf == LV_IMG_CF_INDEXED_2BIT ||
+ dsc->header.cf == LV_IMG_CF_INDEXED_4BIT || dsc->header.cf == LV_IMG_CF_INDEXED_8BIT) {
+ res = lv_img_decoder_built_in_line_indexed(dsc, x, y, len, buf);
+ }
+ else {
+ LV_LOG_WARN("Built-in image decoder read not supports the color format");
+ return LV_RES_INV;
+ }
+
+ return res;
+}
+
+/**
+ * Close the pending decoding. Free resources etc.
+ * @param decoder pointer to the decoder the function associated with
+ * @param dsc pointer to decoder descriptor
+ */
+void lv_img_decoder_built_in_close(lv_img_decoder_t * decoder, lv_img_decoder_dsc_t * dsc)
+{
+ LV_UNUSED(decoder); /*Unused*/
+
+ lv_img_decoder_built_in_data_t * user_data = dsc->user_data;
+ if(user_data) {
+ if(dsc->src_type == LV_IMG_SRC_FILE) {
+ lv_fs_close(&user_data->f);
+ }
+ if(user_data->palette) lv_mem_free(user_data->palette);
+ if(user_data->opa) lv_mem_free(user_data->opa);
+
+ lv_mem_free(user_data);
+ dsc->user_data = NULL;
+ }
+}
+
+/**********************
+ * STATIC FUNCTIONS
+ **********************/
+
+static lv_res_t lv_img_decoder_built_in_line_true_color(lv_img_decoder_dsc_t * dsc, lv_coord_t x, lv_coord_t y,
+ lv_coord_t len, uint8_t * buf)
+{
+ lv_img_decoder_built_in_data_t * user_data = dsc->user_data;
+ lv_fs_res_t res;
+ uint8_t px_size = lv_img_cf_get_px_size(dsc->header.cf);
+
+ uint32_t pos = ((y * dsc->header.w + x) * px_size) >> 3;
+ pos += 4; /*Skip the header*/
+ res = lv_fs_seek(&user_data->f, pos, LV_FS_SEEK_SET);
+ if(res != LV_FS_RES_OK) {
+ LV_LOG_WARN("Built-in image decoder seek failed");
+ return LV_RES_INV;
+ }
+ uint32_t btr = len * (px_size >> 3);
+ uint32_t br = 0;
+ res = lv_fs_read(&user_data->f, buf, btr, &br);
+ if(res != LV_FS_RES_OK || btr != br) {
+ LV_LOG_WARN("Built-in image decoder read failed");
+ return LV_RES_INV;
+ }
+
+ return LV_RES_OK;
+}
+
+static lv_res_t lv_img_decoder_built_in_line_alpha(lv_img_decoder_dsc_t * dsc, lv_coord_t x, lv_coord_t y,
+ lv_coord_t len, uint8_t * buf)
+{
+ const lv_opa_t alpha1_opa_table[2] = {0, 255}; /*Opacity mapping with bpp = 1 (Just for compatibility)*/
+ const lv_opa_t alpha2_opa_table[4] = {0, 85, 170, 255}; /*Opacity mapping with bpp = 2*/
+ const lv_opa_t alpha4_opa_table[16] = {0, 17, 34, 51, /*Opacity mapping with bpp = 4*/
+ 68, 85, 102, 119, 136, 153, 170, 187, 204, 221, 238, 255
+ };
+
+ /*Simply fill the buffer with the color. Later only the alpha value will be modified.*/
+ lv_color_t bg_color = dsc->color;
+ lv_coord_t i;
+ for(i = 0; i < len; i++) {
+#if LV_COLOR_DEPTH == 8 || LV_COLOR_DEPTH == 1
+ buf[i * LV_IMG_PX_SIZE_ALPHA_BYTE] = bg_color.full;
+#elif LV_COLOR_DEPTH == 16
+ /*Because of Alpha byte 16 bit color can start on odd address which can cause crash*/
+ buf[i * LV_IMG_PX_SIZE_ALPHA_BYTE] = bg_color.full & 0xFF;
+ buf[i * LV_IMG_PX_SIZE_ALPHA_BYTE + 1] = (bg_color.full >> 8) & 0xFF;
+#elif LV_COLOR_DEPTH == 32
+ *((uint32_t *)&buf[i * LV_IMG_PX_SIZE_ALPHA_BYTE]) = bg_color.full;
+#else
+#error "Invalid LV_COLOR_DEPTH. Check it in lv_conf.h"
+#endif
+ }
+
+ const lv_opa_t * opa_table = NULL;
+ uint8_t px_size = lv_img_cf_get_px_size(dsc->header.cf);
+ uint16_t mask = (1 << px_size) - 1; /*E.g. px_size = 2; mask = 0x03*/
+
+ lv_coord_t w = 0;
+ uint32_t ofs = 0;
+ int8_t pos = 0;
+ switch(dsc->header.cf) {
+ case LV_IMG_CF_ALPHA_1BIT:
+ w = (dsc->header.w + 7) >> 3; /*E.g. w = 20 -> w = 2 + 1*/
+ ofs += w * y + (x >> 3); /*First pixel*/
+ pos = 7 - (x & 0x7);
+ opa_table = alpha1_opa_table;
+ break;
+ case LV_IMG_CF_ALPHA_2BIT:
+ w = (dsc->header.w + 3) >> 2; /*E.g. w = 13 -> w = 3 + 1 (bytes)*/
+ ofs += w * y + (x >> 2); /*First pixel*/
+ pos = 6 - (x & 0x3) * 2;
+ opa_table = alpha2_opa_table;
+ break;
+ case LV_IMG_CF_ALPHA_4BIT:
+ w = (dsc->header.w + 1) >> 1; /*E.g. w = 13 -> w = 6 + 1 (bytes)*/
+ ofs += w * y + (x >> 1); /*First pixel*/
+ pos = 4 - (x & 0x1) * 4;
+ opa_table = alpha4_opa_table;
+ break;
+ case LV_IMG_CF_ALPHA_8BIT:
+ w = dsc->header.w; /*E.g. x = 7 -> w = 7 (bytes)*/
+ ofs += w * y + x; /*First pixel*/
+ pos = 0;
+ break;
+ }
+
+ lv_img_decoder_built_in_data_t * user_data = dsc->user_data;
+ uint8_t * fs_buf = lv_mem_buf_get(w);
+ if(fs_buf == NULL) return LV_RES_INV;
+
+ const uint8_t * data_tmp = NULL;
+ if(dsc->src_type == LV_IMG_SRC_VARIABLE) {
+ const lv_img_dsc_t * img_dsc = dsc->src;
+
+ data_tmp = img_dsc->data + ofs;
+ }
+ else {
+ lv_fs_seek(&user_data->f, ofs + 4, LV_FS_SEEK_SET); /*+4 to skip the header*/
+ lv_fs_read(&user_data->f, fs_buf, w, NULL);
+ data_tmp = fs_buf;
+ }
+
+ for(i = 0; i < len; i++) {
+ uint8_t val_act = (*data_tmp >> pos) & mask;
+
+ buf[i * LV_IMG_PX_SIZE_ALPHA_BYTE + LV_IMG_PX_SIZE_ALPHA_BYTE - 1] =
+ dsc->header.cf == LV_IMG_CF_ALPHA_8BIT ? val_act : opa_table[val_act];
+
+ pos -= px_size;
+ if(pos < 0) {
+ pos = 8 - px_size;
+ data_tmp++;
+ }
+ }
+ lv_mem_buf_release(fs_buf);
+ return LV_RES_OK;
+}
+
+static lv_res_t lv_img_decoder_built_in_line_indexed(lv_img_decoder_dsc_t * dsc, lv_coord_t x, lv_coord_t y,
+ lv_coord_t len, uint8_t * buf)
+{
+ uint8_t px_size = lv_img_cf_get_px_size(dsc->header.cf);
+ uint16_t mask = (1 << px_size) - 1; /*E.g. px_size = 2; mask = 0x03*/
+
+ lv_coord_t w = 0;
+ int8_t pos = 0;
+ uint32_t ofs = 0;
+ switch(dsc->header.cf) {
+ case LV_IMG_CF_INDEXED_1BIT:
+ w = (dsc->header.w + 7) >> 3; /*E.g. w = 20 -> w = 2 + 1*/
+ ofs += w * y + (x >> 3); /*First pixel*/
+ ofs += 8; /*Skip the palette*/
+ pos = 7 - (x & 0x7);
+ break;
+ case LV_IMG_CF_INDEXED_2BIT:
+ w = (dsc->header.w + 3) >> 2; /*E.g. w = 13 -> w = 3 + 1 (bytes)*/
+ ofs += w * y + (x >> 2); /*First pixel*/
+ ofs += 16; /*Skip the palette*/
+ pos = 6 - (x & 0x3) * 2;
+ break;
+ case LV_IMG_CF_INDEXED_4BIT:
+ w = (dsc->header.w + 1) >> 1; /*E.g. w = 13 -> w = 6 + 1 (bytes)*/
+ ofs += w * y + (x >> 1); /*First pixel*/
+ ofs += 64; /*Skip the palette*/
+ pos = 4 - (x & 0x1) * 4;
+ break;
+ case LV_IMG_CF_INDEXED_8BIT:
+ w = dsc->header.w; /*E.g. x = 7 -> w = 7 (bytes)*/
+ ofs += w * y + x; /*First pixel*/
+ ofs += 1024; /*Skip the palette*/
+ pos = 0;
+ break;
+ }
+
+ lv_img_decoder_built_in_data_t * user_data = dsc->user_data;
+
+ uint8_t * fs_buf = lv_mem_buf_get(w);
+ if(fs_buf == NULL) return LV_RES_INV;
+ const uint8_t * data_tmp = NULL;
+ if(dsc->src_type == LV_IMG_SRC_VARIABLE) {
+ const lv_img_dsc_t * img_dsc = dsc->src;
+ data_tmp = img_dsc->data + ofs;
+ }
+ else {
+ lv_fs_seek(&user_data->f, ofs + 4, LV_FS_SEEK_SET); /*+4 to skip the header*/
+ lv_fs_read(&user_data->f, fs_buf, w, NULL);
+ data_tmp = fs_buf;
+ }
+
+ lv_coord_t i;
+ for(i = 0; i < len; i++) {
+ uint8_t val_act = (*data_tmp >> pos) & mask;
+
+ lv_color_t color = user_data->palette[val_act];
+#if LV_COLOR_DEPTH == 8 || LV_COLOR_DEPTH == 1
+ buf[i * LV_IMG_PX_SIZE_ALPHA_BYTE] = color.full;
+#elif LV_COLOR_DEPTH == 16
+ /*Because of Alpha byte 16 bit color can start on odd address which can cause crash*/
+ buf[i * LV_IMG_PX_SIZE_ALPHA_BYTE] = color.full & 0xFF;
+ buf[i * LV_IMG_PX_SIZE_ALPHA_BYTE + 1] = (color.full >> 8) & 0xFF;
+#elif LV_COLOR_DEPTH == 32
+ *((uint32_t *)&buf[i * LV_IMG_PX_SIZE_ALPHA_BYTE]) = color.full;
+#else
+#error "Invalid LV_COLOR_DEPTH. Check it in lv_conf.h"
+#endif
+ buf[i * LV_IMG_PX_SIZE_ALPHA_BYTE + LV_IMG_PX_SIZE_ALPHA_BYTE - 1] = user_data->opa[val_act];
+
+ pos -= px_size;
+ if(pos < 0) {
+ pos = 8 - px_size;
+ data_tmp++;
+ }
+ }
+ lv_mem_buf_release(fs_buf);
+ return LV_RES_OK;
+}
diff --git a/lib/lvgl/src/draw/lv_img_decoder.h b/lib/lvgl/src/draw/lv_img_decoder.h
new file mode 100644
index 00000000..9dc84dd5
--- /dev/null
+++ b/lib/lvgl/src/draw/lv_img_decoder.h
@@ -0,0 +1,274 @@
+/**
+ * @file lv_img_decoder.h
+ *
+ */
+
+#ifndef LV_IMG_DECODER_H
+#define LV_IMG_DECODER_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*********************
+ * INCLUDES
+ *********************/
+#include "../lv_conf_internal.h"
+
+#include <stdint.h>
+#include "lv_img_buf.h"
+#include "../misc/lv_fs.h"
+#include "../misc/lv_types.h"
+#include "../misc/lv_area.h"
+
+/*********************
+ * DEFINES
+ *********************/
+
+/**********************
+ * TYPEDEFS
+ **********************/
+
+/**
+ * Source of image.*/
+enum {
+ LV_IMG_SRC_VARIABLE, /** Binary/C variable*/
+ LV_IMG_SRC_FILE, /** File in filesystem*/
+ LV_IMG_SRC_SYMBOL, /** Symbol (@ref lv_symbol_def.h)*/
+ LV_IMG_SRC_UNKNOWN, /** Unknown source*/
+};
+
+typedef uint8_t lv_img_src_t;
+
+/*Decoder function definitions*/
+struct _lv_img_decoder_dsc_t;
+struct _lv_img_decoder_t;
+
+/**
+ * Get info from an image and store in the `header`
+ * @param src the image source. Can be a pointer to a C array or a file name (Use
+ * `lv_img_src_get_type` to determine the type)
+ * @param header store the info here
+ * @return LV_RES_OK: info written correctly; LV_RES_INV: failed
+ */
+typedef lv_res_t (*lv_img_decoder_info_f_t)(struct _lv_img_decoder_t * decoder, const void * src,
+ lv_img_header_t * header);
+
+/**
+ * Open an image for decoding. Prepare it as it is required to read it later
+ * @param decoder pointer to the decoder the function associated with
+ * @param dsc pointer to decoder descriptor. `src`, `color` are already initialized in it.
+ */
+typedef lv_res_t (*lv_img_decoder_open_f_t)(struct _lv_img_decoder_t * decoder, struct _lv_img_decoder_dsc_t * dsc);
+
+/**
+ * Decode `len` pixels starting from the given `x`, `y` coordinates and store them in `buf`.
+ * Required only if the "open" function can't return with the whole decoded pixel array.
+ * @param decoder pointer to the decoder the function associated with
+ * @param dsc pointer to decoder descriptor
+ * @param x start x coordinate
+ * @param y start y coordinate
+ * @param len number of pixels to decode
+ * @param buf a buffer to store the decoded pixels
+ * @return LV_RES_OK: ok; LV_RES_INV: failed
+ */
+typedef lv_res_t (*lv_img_decoder_read_line_f_t)(struct _lv_img_decoder_t * decoder, struct _lv_img_decoder_dsc_t * dsc,
+ lv_coord_t x, lv_coord_t y, lv_coord_t len, uint8_t * buf);
+
+/**
+ * Close the pending decoding. Free resources etc.
+ * @param decoder pointer to the decoder the function associated with
+ * @param dsc pointer to decoder descriptor
+ */
+typedef void (*lv_img_decoder_close_f_t)(struct _lv_img_decoder_t * decoder, struct _lv_img_decoder_dsc_t * dsc);
+
+
+typedef struct _lv_img_decoder_t {
+ lv_img_decoder_info_f_t info_cb;
+ lv_img_decoder_open_f_t open_cb;
+ lv_img_decoder_read_line_f_t read_line_cb;
+ lv_img_decoder_close_f_t close_cb;
+
+#if LV_USE_USER_DATA
+ void * user_data;
+#endif
+} lv_img_decoder_t;
+
+
+/**Describe an image decoding session. Stores data about the decoding*/
+typedef struct _lv_img_decoder_dsc_t {
+ /**The decoder which was able to open the image source*/
+ lv_img_decoder_t * decoder;
+
+ /**The image source. A file path like "S:my_img.png" or pointer to an `lv_img_dsc_t` variable*/
+ const void * src;
+
+ /**Color to draw the image. USed when the image has alpha channel only*/
+ lv_color_t color;
+
+ /**Frame of the image, using with animated images*/
+ int32_t frame_id;
+
+ /**Type of the source: file or variable. Can be set in `open` function if required*/
+ lv_img_src_t src_type;
+
+ /**Info about the opened image: color format, size, etc. MUST be set in `open` function*/
+ lv_img_header_t header;
+
+ /** Pointer to a buffer where the image's data (pixels) are stored in a decoded, plain format.
+ * MUST be set in `open` function*/
+ const uint8_t * img_data;
+
+ /** How much time did it take to open the image. [ms]
+ * If not set `lv_img_cache` will measure and set the time to open*/
+ uint32_t time_to_open;
+
+ /**A text to display instead of the image when the image can't be opened.
+ * Can be set in `open` function or set NULL.*/
+ const char * error_msg;
+
+ /**Store any custom data here is required*/
+ void * user_data;
+} lv_img_decoder_dsc_t;
+
+/**********************
+ * GLOBAL PROTOTYPES
+ **********************/
+
+/**
+ * Initialize the image decoder module
+ */
+void _lv_img_decoder_init(void);
+
+/**
+ * Get information about an image.
+ * Try the created image decoder one by one. Once one is able to get info that info will be used.
+ * @param src the image source. Can be
+ * 1) File name: E.g. "S:folder/img1.png" (The drivers needs to registered via `lv_fs_drv_register()`)
+ * 2) Variable: Pointer to an `lv_img_dsc_t` variable
+ * 3) Symbol: E.g. `LV_SYMBOL_OK`
+ * @param header the image info will be stored here
+ * @return LV_RES_OK: success; LV_RES_INV: wasn't able to get info about the image
+ */
+lv_res_t lv_img_decoder_get_info(const void * src, lv_img_header_t * header);
+
+/**
+ * Open an image.
+ * Try the created image decoders one by one. Once one is able to open the image that decoder is saved in `dsc`
+ * @param dsc describes a decoding session. Simply a pointer to an `lv_img_decoder_dsc_t` variable.
+ * @param src the image source. Can be
+ * 1) File name: E.g. "S:folder/img1.png" (The drivers needs to registered via `lv_fs_drv_register())`)
+ * 2) Variable: Pointer to an `lv_img_dsc_t` variable
+ * 3) Symbol: E.g. `LV_SYMBOL_OK`
+ * @param color The color of the image with `LV_IMG_CF_ALPHA_...`
+ * @param frame_id the index of the frame. Used only with animated images, set 0 for normal images
+ * @return LV_RES_OK: opened the image. `dsc->img_data` and `dsc->header` are set.
+ * LV_RES_INV: none of the registered image decoders were able to open the image.
+ */
+lv_res_t lv_img_decoder_open(lv_img_decoder_dsc_t * dsc, const void * src, lv_color_t color, int32_t frame_id);
+
+/**
+ * Read a line from an opened image
+ * @param dsc pointer to `lv_img_decoder_dsc_t` used in `lv_img_decoder_open`
+ * @param x start X coordinate (from left)
+ * @param y start Y coordinate (from top)
+ * @param len number of pixels to read
+ * @param buf store the data here
+ * @return LV_RES_OK: success; LV_RES_INV: an error occurred
+ */
+lv_res_t lv_img_decoder_read_line(lv_img_decoder_dsc_t * dsc, lv_coord_t x, lv_coord_t y, lv_coord_t len,
+ uint8_t * buf);
+
+/**
+ * Close a decoding session
+ * @param dsc pointer to `lv_img_decoder_dsc_t` used in `lv_img_decoder_open`
+ */
+void lv_img_decoder_close(lv_img_decoder_dsc_t * dsc);
+
+/**
+ * Create a new image decoder
+ * @return pointer to the new image decoder
+ */
+lv_img_decoder_t * lv_img_decoder_create(void);
+
+/**
+ * Delete an image decoder
+ * @param decoder pointer to an image decoder
+ */
+void lv_img_decoder_delete(lv_img_decoder_t * decoder);
+
+/**
+ * Set a callback to get information about the image
+ * @param decoder pointer to an image decoder
+ * @param info_cb a function to collect info about an image (fill an `lv_img_header_t` struct)
+ */
+void lv_img_decoder_set_info_cb(lv_img_decoder_t * decoder, lv_img_decoder_info_f_t info_cb);
+
+/**
+ * Set a callback to open an image
+ * @param decoder pointer to an image decoder
+ * @param open_cb a function to open an image
+ */
+void lv_img_decoder_set_open_cb(lv_img_decoder_t * decoder, lv_img_decoder_open_f_t open_cb);
+
+/**
+ * Set a callback to a decoded line of an image
+ * @param decoder pointer to an image decoder
+ * @param read_line_cb a function to read a line of an image
+ */
+void lv_img_decoder_set_read_line_cb(lv_img_decoder_t * decoder, lv_img_decoder_read_line_f_t read_line_cb);
+
+/**
+ * Set a callback to close a decoding session. E.g. close files and free other resources.
+ * @param decoder pointer to an image decoder
+ * @param close_cb a function to close a decoding session
+ */
+void lv_img_decoder_set_close_cb(lv_img_decoder_t * decoder, lv_img_decoder_close_f_t close_cb);
+
+/**
+ * Get info about a built-in image
+ * @param decoder the decoder where this function belongs
+ * @param src the image source: pointer to an `lv_img_dsc_t` variable, a file path or a symbol
+ * @param header store the image data here
+ * @return LV_RES_OK: the info is successfully stored in `header`; LV_RES_INV: unknown format or other error.
+ */
+lv_res_t lv_img_decoder_built_in_info(lv_img_decoder_t * decoder, const void * src, lv_img_header_t * header);
+
+/**
+ * Open a built in image
+ * @param decoder the decoder where this function belongs
+ * @param dsc pointer to decoder descriptor. `src`, `style` are already initialized in it.
+ * @return LV_RES_OK: the info is successfully stored in `header`; LV_RES_INV: unknown format or other error.
+ */
+lv_res_t lv_img_decoder_built_in_open(lv_img_decoder_t * decoder, lv_img_decoder_dsc_t * dsc);
+
+/**
+ * Decode `len` pixels starting from the given `x`, `y` coordinates and store them in `buf`.
+ * Required only if the "open" function can't return with the whole decoded pixel array.
+ * @param decoder pointer to the decoder the function associated with
+ * @param dsc pointer to decoder descriptor
+ * @param x start x coordinate
+ * @param y start y coordinate
+ * @param len number of pixels to decode
+ * @param buf a buffer to store the decoded pixels
+ * @return LV_RES_OK: ok; LV_RES_INV: failed
+ */
+lv_res_t lv_img_decoder_built_in_read_line(lv_img_decoder_t * decoder, lv_img_decoder_dsc_t * dsc, lv_coord_t x,
+ lv_coord_t y, lv_coord_t len, uint8_t * buf);
+
+/**
+ * Close the pending decoding. Free resources etc.
+ * @param decoder pointer to the decoder the function associated with
+ * @param dsc pointer to decoder descriptor
+ */
+void lv_img_decoder_built_in_close(lv_img_decoder_t * decoder, lv_img_decoder_dsc_t * dsc);
+
+/**********************
+ * MACROS
+ **********************/
+
+#ifdef __cplusplus
+} /*extern "C"*/
+#endif
+
+#endif /*LV_IMG_DECODER_H*/
diff --git a/lib/lvgl/src/draw/nxp/lv_draw_nxp.mk b/lib/lvgl/src/draw/nxp/lv_draw_nxp.mk
new file mode 100644
index 00000000..17371ac9
--- /dev/null
+++ b/lib/lvgl/src/draw/nxp/lv_draw_nxp.mk
@@ -0,0 +1,9 @@
+CSRCS += lv_gpu_nxp.c
+
+DEPPATH += --dep-path $(LVGL_DIR)/$(LVGL_DIR_NAME)/src/draw/nxp
+VPATH += :$(LVGL_DIR)/$(LVGL_DIR_NAME)/src/draw/nxp
+
+CFLAGS += "-I$(LVGL_DIR)/$(LVGL_DIR_NAME)/src/draw/nxp"
+
+include $(LVGL_DIR)/$(LVGL_DIR_NAME)/src/draw/nxp/pxp/lv_draw_nxp_pxp.mk
+include $(LVGL_DIR)/$(LVGL_DIR_NAME)/src/draw/nxp/vglite/lv_draw_nxp_vglite.mk
diff --git a/lib/lvgl/src/draw/nxp/lv_gpu_nxp.c b/lib/lvgl/src/draw/nxp/lv_gpu_nxp.c
new file mode 100644
index 00000000..46da9334
--- /dev/null
+++ b/lib/lvgl/src/draw/nxp/lv_gpu_nxp.c
@@ -0,0 +1,418 @@
+/**
+ * @file lv_gpu_nxp.c
+ *
+ */
+
+/**
+ * MIT License
+ *
+ * Copyright 2022 NXP
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+ * the Software, and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next paragraph)
+ * shall be included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
+ * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
+ * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+/*********************
+ * INCLUDES
+ *********************/
+
+#include "lv_gpu_nxp.h"
+
+#if LV_USE_GPU_NXP_PXP || LV_USE_GPU_NXP_VG_LITE
+
+/*
+ * allow to use both PXP and VGLITE
+
+ * both 2D accelerators can be used at the same time:
+ * thus VGLITE can be used to accelerate widget drawing
+ * while PXP accelerates Blit & Fill operations.
+ */
+#if LV_USE_GPU_NXP_PXP
+ #include "pxp/lv_draw_pxp_blend.h"
+#endif
+#if LV_USE_GPU_NXP_VG_LITE
+ #include "vglite/lv_draw_vglite_blend.h"
+ #include "vglite/lv_draw_vglite_rect.h"
+ #include "vglite/lv_draw_vglite_arc.h"
+#endif
+
+#if LV_COLOR_DEPTH != 32
+ #include "../../core/lv_refr.h"
+#endif
+
+/*********************
+ * DEFINES
+ *********************/
+
+/**********************
+ * TYPEDEFS
+ **********************/
+
+/**********************
+ * STATIC PROTOTYPES
+ **********************/
+
+static void lv_draw_nxp_img_decoded(lv_draw_ctx_t * draw_ctx, const lv_draw_img_dsc_t * dsc,
+ const lv_area_t * coords, const uint8_t * map_p, lv_img_cf_t cf);
+
+static void lv_draw_nxp_blend(lv_draw_ctx_t * draw_ctx, const lv_draw_sw_blend_dsc_t * dsc);
+
+static void lv_draw_nxp_rect(lv_draw_ctx_t * draw_ctx, const lv_draw_rect_dsc_t * dsc, const lv_area_t * coords);
+
+static lv_res_t draw_nxp_bg(lv_draw_ctx_t * draw_ctx, const lv_draw_rect_dsc_t * dsc, const lv_area_t * coords);
+
+static void lv_draw_nxp_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);
+
+/**********************
+ * STATIC VARIABLES
+ **********************/
+
+/**********************
+ * MACROS
+ **********************/
+
+/**********************
+ * GLOBAL FUNCTIONS
+ **********************/
+
+void lv_draw_nxp_ctx_init(lv_disp_drv_t * drv, lv_draw_ctx_t * draw_ctx)
+{
+ lv_draw_sw_init_ctx(drv, draw_ctx);
+
+ lv_draw_nxp_ctx_t * nxp_draw_ctx = (lv_draw_sw_ctx_t *)draw_ctx;
+
+ nxp_draw_ctx->base_draw.draw_arc = lv_draw_nxp_arc;
+ nxp_draw_ctx->base_draw.draw_rect = lv_draw_nxp_rect;
+ nxp_draw_ctx->base_draw.draw_img_decoded = lv_draw_nxp_img_decoded;
+ nxp_draw_ctx->blend = lv_draw_nxp_blend;
+ //nxp_draw_ctx->base_draw.wait_for_finish = lv_draw_nxp_wait_cb;
+}
+
+void lv_draw_nxp_ctx_deinit(lv_disp_drv_t * drv, lv_draw_ctx_t * draw_ctx)
+{
+ lv_draw_sw_deinit_ctx(drv, draw_ctx);
+}
+
+/**********************
+ * STATIC FUNCTIONS
+ **********************/
+
+/**
+ * During rendering, LVGL might initializes new draw_ctxs and start drawing into
+ * a separate buffer (called layer). If the content to be rendered has "holes",
+ * e.g. rounded corner, LVGL temporarily sets the disp_drv.screen_transp flag.
+ * It means the renderers should draw into an ARGB buffer.
+ * With 32 bit color depth it's not a big problem but with 16 bit color depth
+ * the target pixel format is ARGB8565 which is not supported by the GPU.
+ * In this case, the NXP callbacks should fallback to SW rendering.
+ */
+static inline bool need_argb8565_support()
+{
+#if LV_COLOR_DEPTH != 32
+ lv_disp_t * disp = _lv_refr_get_disp_refreshing();
+
+ if(disp->driver->screen_transp == 1)
+ return true;
+#endif
+
+ return false;
+}
+
+static void lv_draw_nxp_blend(lv_draw_ctx_t * draw_ctx, const lv_draw_sw_blend_dsc_t * dsc)
+{
+ lv_area_t blend_area;
+
+ /*Let's get the blend area which is the intersection of the area to fill and the clip area.*/
+ if(!_lv_area_intersect(&blend_area, dsc->blend_area, draw_ctx->clip_area))
+ return; /*Fully clipped, nothing to do*/
+
+ /*Make the blend area relative to the buffer*/
+ lv_area_move(&blend_area, -draw_ctx->buf_area->x1, -draw_ctx->buf_area->y1);
+
+ bool done = false;
+
+ /*Fill/Blend only non masked, normal blended*/
+ if(dsc->mask_buf == NULL && dsc->blend_mode == LV_BLEND_MODE_NORMAL && !need_argb8565_support()) {
+ lv_color_t * dest_buf = draw_ctx->buf;
+ lv_coord_t dest_stride = lv_area_get_width(draw_ctx->buf_area);
+#if LV_USE_GPU_NXP_VG_LITE
+ lv_coord_t dest_width = lv_area_get_width(draw_ctx->buf_area);
+ lv_coord_t dest_height = lv_area_get_height(draw_ctx->buf_area);
+#endif
+
+ const lv_color_t * src_buf = dsc->src_buf;
+
+ if(src_buf == NULL) {
+#if LV_USE_GPU_NXP_PXP
+ done = (lv_gpu_nxp_pxp_fill(dest_buf, dest_stride, &blend_area,
+ dsc->color, dsc->opa) == LV_RES_OK);
+ if(!done)
+ PXP_LOG_TRACE("PXP fill failed. Fallback.");
+
+#endif
+#if LV_USE_GPU_NXP_VG_LITE
+ if(!done) {
+ done = (lv_gpu_nxp_vglite_fill(dest_buf, dest_width, dest_height, &blend_area,
+ dsc->color, dsc->opa) == LV_RES_OK);
+ if(!done)
+ VG_LITE_LOG_TRACE("VG-Lite fill failed. Fallback.");
+ }
+#endif
+ }
+ else {
+#if LV_USE_GPU_NXP_PXP
+ done = (lv_gpu_nxp_pxp_blit(dest_buf, &blend_area, dest_stride, src_buf, dsc->blend_area,
+ dsc->opa, LV_DISP_ROT_NONE) == LV_RES_OK);
+ if(!done)
+ PXP_LOG_TRACE("PXP blit failed. Fallback.");
+#endif
+#if LV_USE_GPU_NXP_VG_LITE
+ if(!done) {
+ lv_gpu_nxp_vglite_blit_info_t blit;
+ lv_coord_t src_stride = lv_area_get_width(dsc->blend_area);
+
+ blit.src = src_buf;
+ blit.src_width = lv_area_get_width(dsc->blend_area);
+ blit.src_height = lv_area_get_height(dsc->blend_area);
+ blit.src_stride = src_stride * (int32_t)sizeof(lv_color_t);
+ blit.src_area.x1 = (blend_area.x1 - (dsc->blend_area->x1 - draw_ctx->buf_area->x1));
+ blit.src_area.y1 = (blend_area.y1 - (dsc->blend_area->y1 - draw_ctx->buf_area->y1));
+ blit.src_area.x2 = blit.src_area.x1 + blit.src_width - 1;
+ blit.src_area.y2 = blit.src_area.y1 + blit.src_height - 1;
+
+ blit.dst = dest_buf;
+ blit.dst_width = dest_width;
+ blit.dst_height = dest_height;
+ blit.dst_stride = dest_stride * (int32_t)sizeof(lv_color_t);
+ blit.dst_area.x1 = blend_area.x1;
+ blit.dst_area.y1 = blend_area.y1;
+ blit.dst_area.x2 = blend_area.x2;
+ blit.dst_area.y2 = blend_area.y2;
+
+ blit.opa = dsc->opa;
+ blit.zoom = LV_IMG_ZOOM_NONE;
+ blit.angle = 0;
+
+ done = (lv_gpu_nxp_vglite_blit(&blit) == LV_RES_OK);
+
+ if(!done)
+ VG_LITE_LOG_TRACE("VG-Lite blit failed. Fallback.");
+ }
+#endif
+ }
+ }
+
+ if(!done)
+ lv_draw_sw_blend_basic(draw_ctx, dsc);
+}
+
+static void lv_draw_nxp_img_decoded(lv_draw_ctx_t * draw_ctx, const lv_draw_img_dsc_t * dsc,
+ const lv_area_t * coords, const uint8_t * map_p, 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);
+#if LV_USE_GPU_NXP_VG_LITE
+ bool recolor = (dsc->recolor_opa != LV_OPA_TRANSP);
+#endif
+#if LV_USE_GPU_NXP_PXP
+ bool scale = (dsc->zoom != LV_IMG_ZOOM_NONE);
+#endif
+ bool done = false;
+
+ lv_area_t blend_area;
+ /*Let's get the blend area which is the intersection of the area to fill and the clip area.*/
+ if(!_lv_area_intersect(&blend_area, coords, draw_ctx->clip_area))
+ return; /*Fully clipped, nothing to do*/
+
+ /*Make the blend area relative to the buffer*/
+ lv_area_move(&blend_area, -draw_ctx->buf_area->x1, -draw_ctx->buf_area->y1);
+
+ const lv_color_t * src_buf = (const lv_color_t *)map_p;
+ if(!src_buf) {
+ lv_draw_sw_img_decoded(draw_ctx, dsc, coords, map_p, cf);
+ return;
+ }
+
+ lv_color_t * dest_buf = draw_ctx->buf;
+ lv_coord_t dest_stride = lv_area_get_width(draw_ctx->buf_area);
+
+#if LV_USE_GPU_NXP_PXP
+ if(!mask_any && !scale && !need_argb8565_support()
+#if LV_COLOR_DEPTH!=32
+ && !lv_img_cf_has_alpha(cf)
+#endif
+ ) {
+ done = (lv_gpu_nxp_pxp_blit_transform(dest_buf, &blend_area, dest_stride, src_buf, coords,
+ dsc, cf) == LV_RES_OK);
+ if(!done)
+ PXP_LOG_TRACE("PXP blit transform failed. Fallback.");
+ }
+#endif
+
+#if LV_USE_GPU_NXP_VG_LITE
+ if(!done && !mask_any && !need_argb8565_support() &&
+ !lv_img_cf_is_chroma_keyed(cf) && !recolor
+#if LV_COLOR_DEPTH!=32
+ && !lv_img_cf_has_alpha(cf)
+#endif
+ ) {
+ lv_gpu_nxp_vglite_blit_info_t blit;
+ lv_coord_t src_stride = lv_area_get_width(coords);
+
+ blit.src = src_buf;
+ blit.src_width = lv_area_get_width(coords);
+ blit.src_height = lv_area_get_height(coords);
+ blit.src_stride = src_stride * (int32_t)sizeof(lv_color_t);
+ blit.src_area.x1 = (blend_area.x1 - (coords->x1 - draw_ctx->buf_area->x1));
+ blit.src_area.y1 = (blend_area.y1 - (coords->y1 - draw_ctx->buf_area->y1));
+ blit.src_area.x2 = blit.src_area.x1 + blit.src_width - 1;
+ blit.src_area.y2 = blit.src_area.y1 + blit.src_height - 1;
+
+ blit.dst = dest_buf;
+ blit.dst_width = lv_area_get_width(draw_ctx->buf_area);
+ blit.dst_height = lv_area_get_height(draw_ctx->buf_area);
+ blit.dst_stride = dest_stride * (int32_t)sizeof(lv_color_t);
+ blit.dst_area.x1 = blend_area.x1;
+ blit.dst_area.y1 = blend_area.y1;
+ blit.dst_area.x2 = blend_area.x2;
+ blit.dst_area.y2 = blend_area.y2;
+
+ blit.opa = dsc->opa;
+ blit.angle = dsc->angle;
+ blit.pivot = dsc->pivot;
+ blit.zoom = dsc->zoom;
+
+ done = (lv_gpu_nxp_vglite_blit_transform(&blit) == LV_RES_OK);
+
+ if(!done)
+ VG_LITE_LOG_TRACE("VG-Lite blit transform failed. Fallback.");
+ }
+#endif
+
+ if(!done)
+ lv_draw_sw_img_decoded(draw_ctx, dsc, coords, map_p, cf);
+}
+
+static void lv_draw_nxp_rect(lv_draw_ctx_t * draw_ctx, const lv_draw_rect_dsc_t * dsc, const lv_area_t * coords)
+{
+ bool done = false;
+ lv_draw_rect_dsc_t nxp_dsc;
+
+ lv_memcpy(&nxp_dsc, dsc, sizeof(nxp_dsc));
+#if LV_DRAW_COMPLEX
+ /* Draw only the shadow */
+ nxp_dsc.bg_opa = 0;
+ nxp_dsc.bg_img_opa = 0;
+ nxp_dsc.border_opa = 0;
+ nxp_dsc.outline_opa = 0;
+
+ lv_draw_sw_rect(draw_ctx, &nxp_dsc, coords);
+
+ /* Draw the background */
+ nxp_dsc.shadow_opa = 0;
+ nxp_dsc.bg_opa = dsc->bg_opa;
+ done = (draw_nxp_bg(draw_ctx, &nxp_dsc, coords) == LV_RES_OK);
+#endif /*LV_DRAW_COMPLEX*/
+
+ /* Draw the remaining parts */
+ nxp_dsc.shadow_opa = 0;
+ if(done)
+ nxp_dsc.bg_opa = 0;
+ nxp_dsc.bg_img_opa = dsc->bg_img_opa;
+ nxp_dsc.border_opa = dsc->border_opa;
+ nxp_dsc.outline_opa = dsc->outline_opa;
+
+ lv_draw_sw_rect(draw_ctx, &nxp_dsc, coords);
+}
+
+static lv_res_t draw_nxp_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_RES_INV;
+
+ 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_t)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_RES_INV;
+
+ 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);
+
+ /*
+ * Most simple case: just a plain rectangle (no mask, no radius, no gradient)
+ * shall fallback to lv_draw_sw_blend().
+ *
+ * Complex case: gradient or radius but no mask.
+ */
+ if(!mask_any && ((dsc->radius != 0) || (grad_dir != LV_GRAD_DIR_NONE)) && !need_argb8565_support()) {
+#if LV_USE_GPU_NXP_VG_LITE
+ lv_res_t res = lv_gpu_nxp_vglite_draw_bg(draw_ctx, dsc, &bg_coords);
+ if(res != LV_RES_OK)
+ VG_LITE_LOG_TRACE("VG-Lite draw bg failed. Fallback.");
+
+ return res;
+#endif
+ }
+
+ return LV_RES_INV;
+}
+
+static void lv_draw_nxp_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)
+{
+ bool done = false;
+
+#if LV_DRAW_COMPLEX
+ if(dsc->opa <= LV_OPA_MIN)
+ return;
+ if(dsc->width == 0)
+ return;
+ if(start_angle == end_angle)
+ return;
+
+#if LV_USE_GPU_NXP_VG_LITE
+ if(!need_argb8565_support()) {
+ done = (lv_gpu_nxp_vglite_draw_arc(draw_ctx, dsc, center, (int32_t)radius,
+ (int32_t)start_angle, (int32_t)end_angle) == LV_RES_OK);
+ if(!done)
+ VG_LITE_LOG_TRACE("VG-Lite draw arc failed. Fallback.");
+ }
+#endif
+#endif/*LV_DRAW_COMPLEX*/
+
+ if(!done)
+ lv_draw_sw_arc(draw_ctx, dsc, center, radius, start_angle, end_angle);
+}
+
+#endif /*LV_USE_GPU_NXP_PXP || LV_USE_GPU_NXP_VG_LITE*/
diff --git a/lib/lvgl/src/draw/nxp/lv_gpu_nxp.h b/lib/lvgl/src/draw/nxp/lv_gpu_nxp.h
new file mode 100644
index 00000000..899aff25
--- /dev/null
+++ b/lib/lvgl/src/draw/nxp/lv_gpu_nxp.h
@@ -0,0 +1,71 @@
+/**
+ * @file lv_gpu_nxp.h
+ *
+ */
+
+/**
+ * MIT License
+ *
+ * Copyright 2022 NXP
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+ * the Software, and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next paragraph)
+ * shall be included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
+ * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
+ * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+#ifndef LV_GPU_NXP_H
+#define LV_GPU_NXP_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*********************
+ * INCLUDES
+ *********************/
+
+#include "../../lv_conf_internal.h"
+#if LV_USE_GPU_NXP_PXP || LV_USE_GPU_NXP_VG_LITE
+#include "../sw/lv_draw_sw.h"
+
+/*********************
+ * DEFINES
+ *********************/
+
+/**********************
+ * TYPEDEFS
+ **********************/
+typedef lv_draw_sw_ctx_t lv_draw_nxp_ctx_t;
+
+/**********************
+ * GLOBAL PROTOTYPES
+ **********************/
+
+void lv_draw_nxp_ctx_init(struct _lv_disp_drv_t * drv, lv_draw_ctx_t * draw_ctx);
+
+void lv_draw_nxp_ctx_deinit(struct _lv_disp_drv_t * drv, lv_draw_ctx_t * draw_ctx);
+
+/**********************
+ * MACROS
+ **********************/
+#endif /*LV_USE_GPU_NXP_PXP || LV_USE_GPU_NXP_VG_LITE*/
+
+#ifdef __cplusplus
+} /*extern "C"*/
+#endif
+
+#endif /*LV_GPU_NXP_H*/
diff --git a/lib/lvgl/src/draw/nxp/pxp/lv_draw_nxp_pxp.mk b/lib/lvgl/src/draw/nxp/pxp/lv_draw_nxp_pxp.mk
new file mode 100644
index 00000000..ff475a19
--- /dev/null
+++ b/lib/lvgl/src/draw/nxp/pxp/lv_draw_nxp_pxp.mk
@@ -0,0 +1,8 @@
+CSRCS += lv_draw_pxp_blend.c
+CSRCS += lv_gpu_nxp_pxp_osa.c
+CSRCS += lv_gpu_nxp_pxp.c
+
+DEPPATH += --dep-path $(LVGL_DIR)/$(LVGL_DIR_NAME)/src/draw/nxp/pxp
+VPATH += :$(LVGL_DIR)/$(LVGL_DIR_NAME)/src/draw/nxp/pxp
+
+CFLAGS += "-I$(LVGL_DIR)/$(LVGL_DIR_NAME)/src/draw/nxp/pxp"
diff --git a/lib/lvgl/src/draw/nxp/pxp/lv_draw_pxp_blend.c b/lib/lvgl/src/draw/nxp/pxp/lv_draw_pxp_blend.c
new file mode 100644
index 00000000..c0a6ecaf
--- /dev/null
+++ b/lib/lvgl/src/draw/nxp/pxp/lv_draw_pxp_blend.c
@@ -0,0 +1,632 @@
+/**
+ * @file lv_draw_pxp_blend.c
+ *
+ */
+
+/**
+ * MIT License
+ *
+ * Copyright 2020-2022 NXP
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+ * the Software, and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next paragraph)
+ * shall be included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
+ * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
+ * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+/*********************
+ * INCLUDES
+ *********************/
+
+#include "lv_draw_pxp_blend.h"
+
+#if LV_USE_GPU_NXP_PXP
+
+/*********************
+ * DEFINES
+ *********************/
+
+#if LV_COLOR_16_SWAP
+ #error Color swap not implemented. Disable LV_COLOR_16_SWAP feature.
+#endif
+
+#if LV_COLOR_DEPTH==16
+ #define PXP_OUT_PIXEL_FORMAT kPXP_OutputPixelFormatRGB565
+ #define PXP_AS_PIXEL_FORMAT kPXP_AsPixelFormatRGB565
+ #define PXP_PS_PIXEL_FORMAT kPXP_PsPixelFormatRGB565
+#elif LV_COLOR_DEPTH==32
+ #define PXP_OUT_PIXEL_FORMAT kPXP_OutputPixelFormatARGB8888
+ #define PXP_AS_PIXEL_FORMAT kPXP_AsPixelFormatARGB8888
+ #define PXP_PS_PIXEL_FORMAT kPXP_PsPixelFormatRGB888
+#elif
+ #error Only 16bit and 32bit color depth are supported. Set LV_COLOR_DEPTH to 16 or 32.
+#endif
+
+#if defined (__alpha__) || defined (__ia64__) || defined (__x86_64__) \
+ || defined (_WIN64) || defined (__LP64__) || defined (__LLP64__)
+ #define ALIGN_SIZE 8
+#else
+ #define ALIGN_SIZE 4
+#endif
+
+/**********************
+ * TYPEDEFS
+ **********************/
+
+/**********************
+ * STATIC PROTOTYPES
+ **********************/
+
+/**
+ * BLock Image Transfer - copy rectangular image from src buffer to dst buffer
+ * with combination of transformation (rotation, scale, recolor) and opacity, alpha channel
+ * or color keying. This requires two steps. First step is used for transformation into
+ * a temporary buffer and the second one will handle the color format or opacity.
+ *
+ * @param[in/out] dest_buf destination buffer
+ * @param[in] dest_area area to be copied from src_buf to dst_buf
+ * @param[in] dest_stride width (stride) of destination buffer in pixels
+ * @param[in] src_buf source buffer
+ * @param[in] src_area source area with absolute coordinates to draw on destination buffer
+ * @param[in] dsc image descriptor
+ * @param[in] cf color format
+ * @retval LV_RES_OK Fill completed
+ * @retval LV_RES_INV Error occurred (\see LV_GPU_NXP_PXP_LOG_ERRORS)
+ */
+static lv_res_t lv_gpu_nxp_pxp_blit_opa(lv_color_t * dest_buf, const lv_area_t * dest_area, lv_coord_t dest_stride,
+ const lv_color_t * src_buf, const lv_area_t * src_area,
+ const lv_draw_img_dsc_t * dsc, lv_img_cf_t cf);
+
+/**
+ * BLock Image Transfer - copy rectangular image from src buffer to dst buffer
+ * with transformation and full opacity.
+ *
+ * @param[in/out] dest_buf destination buffer
+ * @param[in] dest_area area to be copied from src_buf to dst_buf
+ * @param[in] dest_stride width (stride) of destination buffer in pixels
+ * @param[in] src_buf source buffer
+ * @param[in] src_area source area with absolute coordinates to draw on destination buffer
+ * @param[in] dsc image descriptor
+ * @param[in] cf color format
+ * @retval LV_RES_OK Fill completed
+ * @retval LV_RES_INV Error occurred (\see LV_GPU_NXP_PXP_LOG_ERRORS)
+ */
+static lv_res_t lv_gpu_nxp_pxp_blit_cover(lv_color_t * dest_buf, const lv_area_t * dest_area,
+ lv_coord_t dest_stride,
+ const lv_color_t * src_buf, const lv_area_t * src_area,
+ const lv_draw_img_dsc_t * dsc, lv_img_cf_t cf);
+
+/**
+ * BLock Image Transfer - copy rectangular image from src buffer to dst buffer
+ * without transformation but handling color format or opacity.
+ *
+ * @param[in/out] dest_buf destination buffer
+ * @param[in] dest_area area to be copied from src_buf to dst_buf
+ * @param[in] dest_stride width (stride) of destination buffer in pixels
+ * @param[in] src_buf source buffer
+ * @param[in] src_area source area with absolute coordinates to draw on destination buffer
+ * @param[in] dsc image descriptor
+ * @param[in] cf color format
+ * @retval LV_RES_OK Fill completed
+ * @retval LV_RES_INV Error occurred (\see LV_GPU_NXP_PXP_LOG_ERRORS)
+ */
+static lv_res_t lv_gpu_nxp_pxp_blit_cf(lv_color_t * dest_buf, const lv_area_t * dest_area,
+ lv_coord_t dest_stride,
+ const lv_color_t * src_buf, const lv_area_t * src_area,
+ const lv_draw_img_dsc_t * dsc, lv_img_cf_t cf);
+
+/**********************
+ * STATIC VARIABLES
+ **********************/
+
+/**********************
+ * MACROS
+ **********************/
+
+#define ROUND_UP(x, align) ((x + (align - 1)) & ~(align - 1))
+
+/**********************
+ * GLOBAL FUNCTIONS
+ **********************/
+
+lv_res_t lv_gpu_nxp_pxp_fill(lv_color_t * dest_buf, lv_coord_t dest_stride, const lv_area_t * fill_area,
+ lv_color_t color, lv_opa_t opa)
+{
+ uint32_t area_size = lv_area_get_size(fill_area);
+ lv_coord_t area_w = lv_area_get_width(fill_area);
+ lv_coord_t area_h = lv_area_get_height(fill_area);
+
+ if(opa >= (lv_opa_t)LV_OPA_MAX) {
+ if(area_size < LV_GPU_NXP_PXP_FILL_SIZE_LIMIT) {
+ PXP_LOG_TRACE("Area size %d smaller than limit %d.", area_size, LV_GPU_NXP_PXP_FILL_SIZE_LIMIT);
+ return LV_RES_INV;
+ }
+ }
+ else {
+ if(area_size < LV_GPU_NXP_PXP_FILL_OPA_SIZE_LIMIT) {
+ PXP_LOG_TRACE("Area size %d smaller than limit %d.", area_size, LV_GPU_NXP_PXP_FILL_OPA_SIZE_LIMIT);
+ return LV_RES_INV;
+ }
+ }
+
+ PXP_Init(LV_GPU_NXP_PXP_ID);
+ PXP_EnableCsc1(LV_GPU_NXP_PXP_ID, false); /*Disable CSC1, it is enabled by default.*/
+ PXP_SetProcessBlockSize(LV_GPU_NXP_PXP_ID, kPXP_BlockSize16); /*Block size 16x16 for higher performance*/
+
+ /*OUT buffer configure*/
+ pxp_output_buffer_config_t outputConfig = {
+ .pixelFormat = PXP_OUT_PIXEL_FORMAT,
+ .interlacedMode = kPXP_OutputProgressive,
+ .buffer0Addr = (uint32_t)(dest_buf + dest_stride * fill_area->y1 + fill_area->x1),
+ .buffer1Addr = (uint32_t)NULL,
+ .pitchBytes = dest_stride * sizeof(lv_color_t),
+ .width = area_w,
+ .height = area_h
+ };
+
+ PXP_SetOutputBufferConfig(LV_GPU_NXP_PXP_ID, &outputConfig);
+
+ if(opa >= (lv_opa_t)LV_OPA_MAX) {
+ /*Simple color fill without opacity - AS disabled*/
+ PXP_SetAlphaSurfacePosition(LV_GPU_NXP_PXP_ID, 0xFFFFU, 0xFFFFU, 0U, 0U);
+
+ }
+ else {
+ /*Fill with opacity - AS used as source (same as OUT)*/
+ pxp_as_buffer_config_t asBufferConfig = {
+ .pixelFormat = PXP_AS_PIXEL_FORMAT,
+ .bufferAddr = (uint32_t)outputConfig.buffer0Addr,
+ .pitchBytes = outputConfig.pitchBytes
+ };
+
+ PXP_SetAlphaSurfaceBufferConfig(LV_GPU_NXP_PXP_ID, &asBufferConfig);
+ PXP_SetAlphaSurfacePosition(LV_GPU_NXP_PXP_ID, 0U, 0U, area_w, area_h);
+ }
+
+ /*Disable PS, use as color generator*/
+ PXP_SetProcessSurfacePosition(LV_GPU_NXP_PXP_ID, 0xFFFFU, 0xFFFFU, 0U, 0U);
+ PXP_SetProcessSurfaceBackGroundColor(LV_GPU_NXP_PXP_ID, lv_color_to32(color));
+
+ /**
+ * Configure Porter-Duff blending - src settings are unused for fill without opacity (opa = 0xff).
+ *
+ * Note: srcFactorMode and dstFactorMode are inverted in fsl_pxp.h:
+ * srcFactorMode is actually applied on PS alpha value
+ * dstFactorMode is actually applied on AS alpha value
+ */
+ pxp_porter_duff_config_t pdConfig = {
+ .enable = 1,
+ .dstColorMode = kPXP_PorterDuffColorNoAlpha,
+ .srcColorMode = kPXP_PorterDuffColorNoAlpha,
+ .dstGlobalAlphaMode = kPXP_PorterDuffGlobalAlpha,
+ .srcGlobalAlphaMode = kPXP_PorterDuffGlobalAlpha,
+ .dstFactorMode = kPXP_PorterDuffFactorStraight,
+ .srcFactorMode = (opa >= (lv_opa_t)LV_OPA_MAX) ? kPXP_PorterDuffFactorStraight : kPXP_PorterDuffFactorInversed,
+ .dstGlobalAlpha = opa,
+ .srcGlobalAlpha = opa,
+ .dstAlphaMode = kPXP_PorterDuffAlphaStraight, /*don't care*/
+ .srcAlphaMode = kPXP_PorterDuffAlphaStraight /*don't care*/
+ };
+
+ PXP_SetPorterDuffConfig(LV_GPU_NXP_PXP_ID, &pdConfig);
+
+ lv_gpu_nxp_pxp_run(); /*Start PXP task*/
+
+ return LV_RES_OK;
+}
+
+lv_res_t lv_gpu_nxp_pxp_blit(lv_color_t * dest_buf, const lv_area_t * dest_area, lv_coord_t dest_stride,
+ const lv_color_t * src_buf, const lv_area_t * src_area, lv_opa_t opa, lv_disp_rot_t angle)
+{
+ uint32_t dest_size = lv_area_get_size(dest_area);
+ lv_coord_t dest_w = lv_area_get_width(dest_area);
+ lv_coord_t dest_h = lv_area_get_height(dest_area);
+
+ if(opa >= (lv_opa_t)LV_OPA_MAX) {
+ if(dest_size < LV_GPU_NXP_PXP_BLIT_SIZE_LIMIT) {
+ PXP_LOG_TRACE("Area size %d smaller than limit %d.", dest_size, LV_GPU_NXP_PXP_BLIT_SIZE_LIMIT);
+ return LV_RES_INV;
+ }
+ }
+ else {
+ if(dest_size < LV_GPU_NXP_PXP_BLIT_OPA_SIZE_LIMIT) {
+ PXP_LOG_TRACE("Area size %d smaller than limit %d.", dest_size, LV_GPU_NXP_PXP_BLIT_OPA_SIZE_LIMIT);
+ return LV_RES_INV;
+ }
+ }
+
+ PXP_Init(LV_GPU_NXP_PXP_ID);
+ PXP_EnableCsc1(LV_GPU_NXP_PXP_ID, false); /*Disable CSC1, it is enabled by default.*/
+ PXP_SetProcessBlockSize(LV_GPU_NXP_PXP_ID, kPXP_BlockSize16); /*block size 16x16 for higher performance*/
+
+ /* convert rotation angle */
+ pxp_rotate_degree_t pxp_rot;
+ switch(angle) {
+ case LV_DISP_ROT_NONE:
+ pxp_rot = kPXP_Rotate0;
+ break;
+ case LV_DISP_ROT_90:
+ pxp_rot = kPXP_Rotate90;
+ break;
+ case LV_DISP_ROT_180:
+ pxp_rot = kPXP_Rotate180;
+ break;
+ case LV_DISP_ROT_270:
+ pxp_rot = kPXP_Rotate270;
+ break;
+ default:
+ pxp_rot = kPXP_Rotate0;
+ break;
+ }
+ PXP_SetRotateConfig(LV_GPU_NXP_PXP_ID, kPXP_RotateOutputBuffer, pxp_rot, kPXP_FlipDisable);
+
+ pxp_as_blend_config_t asBlendConfig = {
+ .alpha = opa,
+ .invertAlpha = false,
+ .alphaMode = kPXP_AlphaRop,
+ .ropMode = kPXP_RopMergeAs
+ };
+
+ if(opa >= (lv_opa_t)LV_OPA_MAX) {
+ /*Simple blit, no effect - Disable PS buffer*/
+ PXP_SetProcessSurfacePosition(LV_GPU_NXP_PXP_ID, 0xFFFFU, 0xFFFFU, 0U, 0U);
+ }
+ else {
+ pxp_ps_buffer_config_t psBufferConfig = {
+ .pixelFormat = PXP_PS_PIXEL_FORMAT,
+ .swapByte = false,
+ .bufferAddr = (uint32_t)(dest_buf + dest_stride * dest_area->y1 + dest_area->x1),
+ .bufferAddrU = 0U,
+ .bufferAddrV = 0U,
+ .pitchBytes = dest_stride * sizeof(lv_color_t)
+ };
+
+ asBlendConfig.alphaMode = kPXP_AlphaOverride;
+
+ PXP_SetProcessSurfaceBufferConfig(LV_GPU_NXP_PXP_ID, &psBufferConfig);
+ PXP_SetProcessSurfacePosition(LV_GPU_NXP_PXP_ID, 0U, 0U, dest_w - 1, dest_h - 1);
+ }
+
+ lv_coord_t src_stride = lv_area_get_width(src_area);
+
+ /*AS buffer - source image*/
+ pxp_as_buffer_config_t asBufferConfig = {
+ .pixelFormat = PXP_AS_PIXEL_FORMAT,
+ .bufferAddr = (uint32_t)src_buf,
+ .pitchBytes = src_stride * sizeof(lv_color_t)
+ };
+ PXP_SetAlphaSurfaceBufferConfig(LV_GPU_NXP_PXP_ID, &asBufferConfig);
+ PXP_SetAlphaSurfacePosition(LV_GPU_NXP_PXP_ID, 0U, 0U, dest_w - 1U, dest_h - 1U);
+ PXP_SetAlphaSurfaceBlendConfig(LV_GPU_NXP_PXP_ID, &asBlendConfig);
+ PXP_EnableAlphaSurfaceOverlayColorKey(LV_GPU_NXP_PXP_ID, false);
+
+ /*Output buffer.*/
+ pxp_output_buffer_config_t outputBufferConfig = {
+ .pixelFormat = (pxp_output_pixel_format_t)PXP_OUT_PIXEL_FORMAT,
+ .interlacedMode = kPXP_OutputProgressive,
+ .buffer0Addr = (uint32_t)(dest_buf + dest_stride * dest_area->y1 + dest_area->x1),
+ .buffer1Addr = (uint32_t)0U,
+ .pitchBytes = dest_stride * sizeof(lv_color_t),
+ .width = dest_w,
+ .height = dest_h
+ };
+ PXP_SetOutputBufferConfig(LV_GPU_NXP_PXP_ID, &outputBufferConfig);
+
+ lv_gpu_nxp_pxp_run(); /* Start PXP task */
+
+ return LV_RES_OK;
+}
+
+lv_res_t lv_gpu_nxp_pxp_blit_transform(lv_color_t * dest_buf, const lv_area_t * dest_area, lv_coord_t dest_stride,
+ const lv_color_t * src_buf, const lv_area_t * src_area,
+ const lv_draw_img_dsc_t * dsc, lv_img_cf_t cf)
+{
+ uint32_t dest_size = lv_area_get_size(dest_area);
+
+ if(dsc->opa >= (lv_opa_t)LV_OPA_MAX) {
+ if(dest_size < LV_GPU_NXP_PXP_BLIT_SIZE_LIMIT) {
+ PXP_LOG_TRACE("Area size %d smaller than limit %d.", dest_size, LV_GPU_NXP_PXP_BLIT_SIZE_LIMIT);
+ return LV_RES_INV;
+ }
+ }
+ else {
+ if(dest_size < LV_GPU_NXP_PXP_BLIT_OPA_SIZE_LIMIT) {
+ PXP_LOG_TRACE("Area size %d smaller than limit %d.", dest_size, LV_GPU_NXP_PXP_BLIT_OPA_SIZE_LIMIT);
+ return LV_RES_INV;
+ }
+ }
+
+ bool recolor = (dsc->recolor_opa != LV_OPA_TRANSP);
+ bool rotation = (dsc->angle != 0);
+
+ if(rotation) {
+ if(dsc->angle != 0 && dsc->angle != 900 && dsc->angle != 1800 && dsc->angle != 2700) {
+ PXP_LOG_TRACE("Rotation angle %d is not supported. PXP can rotate only 90x angle.", dsc->angle);
+ return LV_RES_INV;
+ }
+ }
+
+ if(recolor || rotation) {
+ if(dsc->opa >= (lv_opa_t)LV_OPA_MAX && !lv_img_cf_has_alpha(cf) && !lv_img_cf_is_chroma_keyed(cf))
+ return lv_gpu_nxp_pxp_blit_cover(dest_buf, dest_area, dest_stride, src_buf, src_area, dsc, cf);
+ else
+ /*Recolor and/or rotation with alpha or opacity is done in two steps.*/
+ return lv_gpu_nxp_pxp_blit_opa(dest_buf, dest_area, dest_stride, src_buf, src_area, dsc, cf);
+ }
+
+ return lv_gpu_nxp_pxp_blit_cf(dest_buf, dest_area, dest_stride, src_buf, src_area, dsc, cf);
+}
+
+/**********************
+ * STATIC FUNCTIONS
+ **********************/
+
+static lv_res_t lv_gpu_nxp_pxp_blit_opa(lv_color_t * dest_buf, const lv_area_t * dest_area, lv_coord_t dest_stride,
+ const lv_color_t * src_buf, const lv_area_t * src_area,
+ const lv_draw_img_dsc_t * dsc, lv_img_cf_t cf)
+{
+ lv_coord_t dest_w = lv_area_get_width(dest_area);
+ lv_coord_t dest_h = lv_area_get_height(dest_area);
+ lv_res_t res;
+ uint32_t size = dest_w * dest_h * sizeof(lv_color_t);
+
+ if(ROUND_UP(size, ALIGN_SIZE) >= LV_MEM_SIZE)
+ PXP_RETURN_INV("Insufficient memory for temporary buffer. Please increase LV_MEM_SIZE.");
+
+ lv_color_t * tmp_buf = (lv_color_t *)lv_mem_buf_get(size);
+ if(!tmp_buf)
+ PXP_RETURN_INV("Allocating temporary buffer failed.");
+
+ const lv_area_t tmp_area = {
+ .x1 = 0,
+ .y1 = 0,
+ .x2 = dest_w - 1,
+ .y2 = dest_h - 1
+ };
+
+ /*Step 1: Transform with full opacity to temporary buffer*/
+ res = lv_gpu_nxp_pxp_blit_cover(tmp_buf, &tmp_area, dest_w, src_buf, src_area, dsc, cf);
+ if(res != LV_RES_OK) {
+ PXP_LOG_TRACE("Blit cover with full opacity failed.");
+ lv_mem_buf_release(tmp_buf);
+
+ return res;
+ }
+
+ /*Step 2: Blit temporary results with required opacity to output*/
+ res = lv_gpu_nxp_pxp_blit_cf(dest_buf, dest_area, dest_stride, tmp_buf, &tmp_area, dsc, cf);
+
+ /*Clean-up memory*/
+ lv_mem_buf_release(tmp_buf);
+
+ return res;
+}
+
+static lv_res_t lv_gpu_nxp_pxp_blit_cover(lv_color_t * dest_buf, const lv_area_t * dest_area,
+ lv_coord_t dest_stride,
+ const lv_color_t * src_buf, const lv_area_t * src_area,
+ const lv_draw_img_dsc_t * dsc, lv_img_cf_t cf)
+{
+ lv_coord_t dest_w = lv_area_get_width(dest_area);
+ lv_coord_t dest_h = lv_area_get_height(dest_area);
+
+ bool recolor = (dsc->recolor_opa != LV_OPA_TRANSP);
+ bool rotation = (dsc->angle != 0);
+
+ PXP_Init(LV_GPU_NXP_PXP_ID);
+ PXP_EnableCsc1(LV_GPU_NXP_PXP_ID, false); /*Disable CSC1, it is enabled by default.*/
+ PXP_SetProcessBlockSize(LV_GPU_NXP_PXP_ID, kPXP_BlockSize16); /*block size 16x16 for higher performance*/
+
+ if(rotation) {
+ /*
+ * PXP is set to process 16x16 blocks to optimize the system for memory
+ * bandwidth and image processing time.
+ * The output engine essentially truncates any output pixels after the
+ * desired number of pixels has been written.
+ * When rotating a source image and the output is not divisible by the block
+ * size, the incorrect pixels could be truncated and the final output image
+ * can look shifted.
+ */
+ if(lv_area_get_width(src_area) % 16 || lv_area_get_height(src_area) % 16) {
+ PXP_LOG_TRACE("Rotation is not supported for image w/o alignment to block size 16x16.");
+ return LV_RES_INV;
+ }
+
+ /*Convert rotation angle*/
+ pxp_rotate_degree_t pxp_rot;
+ switch(dsc->angle) {
+ case 0:
+ pxp_rot = kPXP_Rotate0;
+ break;
+ case 900:
+ pxp_rot = kPXP_Rotate90;
+ break;
+ case 1800:
+ pxp_rot = kPXP_Rotate180;
+ break;
+ case 2700:
+ pxp_rot = kPXP_Rotate270;
+ break;
+ default:
+ PXP_LOG_TRACE("Rotation angle %d is not supported. PXP can rotate only 90x angle.", dsc->angle);
+ return LV_RES_INV;
+ }
+ PXP_SetRotateConfig(LV_GPU_NXP_PXP_ID, kPXP_RotateOutputBuffer, pxp_rot, kPXP_FlipDisable);
+ }
+
+ lv_coord_t src_stride = lv_area_get_width(src_area);
+
+ /*AS buffer - source image*/
+ pxp_as_buffer_config_t asBufferConfig = {
+ .pixelFormat = PXP_AS_PIXEL_FORMAT,
+ .bufferAddr = (uint32_t)src_buf,
+ .pitchBytes = src_stride * sizeof(lv_color_t)
+ };
+ PXP_SetAlphaSurfaceBufferConfig(LV_GPU_NXP_PXP_ID, &asBufferConfig);
+ PXP_SetAlphaSurfacePosition(LV_GPU_NXP_PXP_ID, 0U, 0U, dest_w - 1U, dest_h - 1U);
+
+ /*Disable PS buffer*/
+ PXP_SetProcessSurfacePosition(LV_GPU_NXP_PXP_ID, 0xFFFFU, 0xFFFFU, 0U, 0U);
+ if(recolor)
+ /*Use as color generator*/
+ PXP_SetProcessSurfaceBackGroundColor(LV_GPU_NXP_PXP_ID, lv_color_to32(dsc->recolor));
+
+ /*Output buffer*/
+ pxp_output_buffer_config_t outputBufferConfig = {
+ .pixelFormat = (pxp_output_pixel_format_t)PXP_OUT_PIXEL_FORMAT,
+ .interlacedMode = kPXP_OutputProgressive,
+ .buffer0Addr = (uint32_t)(dest_buf + dest_stride * dest_area->y1 + dest_area->x1),
+ .buffer1Addr = (uint32_t)0U,
+ .pitchBytes = dest_stride * sizeof(lv_color_t),
+ .width = dest_w,
+ .height = dest_h
+ };
+ PXP_SetOutputBufferConfig(LV_GPU_NXP_PXP_ID, &outputBufferConfig);
+
+ if(recolor || lv_img_cf_has_alpha(cf)) {
+ /**
+ * Configure Porter-Duff blending.
+ *
+ * Note: srcFactorMode and dstFactorMode are inverted in fsl_pxp.h:
+ * srcFactorMode is actually applied on PS alpha value
+ * dstFactorMode is actually applied on AS alpha value
+ */
+ pxp_porter_duff_config_t pdConfig = {
+ .enable = 1,
+ .dstColorMode = kPXP_PorterDuffColorWithAlpha,
+ .srcColorMode = kPXP_PorterDuffColorNoAlpha,
+ .dstGlobalAlphaMode = kPXP_PorterDuffGlobalAlpha,
+ .srcGlobalAlphaMode = lv_img_cf_has_alpha(cf) ? kPXP_PorterDuffLocalAlpha : kPXP_PorterDuffGlobalAlpha,
+ .dstFactorMode = kPXP_PorterDuffFactorStraight,
+ .srcFactorMode = kPXP_PorterDuffFactorInversed,
+ .dstGlobalAlpha = recolor ? dsc->recolor_opa : 0x00,
+ .srcGlobalAlpha = 0xff,
+ .dstAlphaMode = kPXP_PorterDuffAlphaStraight, /*don't care*/
+ .srcAlphaMode = kPXP_PorterDuffAlphaStraight
+ };
+ PXP_SetPorterDuffConfig(LV_GPU_NXP_PXP_ID, &pdConfig);
+ }
+
+ lv_gpu_nxp_pxp_run(); /*Start PXP task*/
+
+ return LV_RES_OK;
+}
+
+static lv_res_t lv_gpu_nxp_pxp_blit_cf(lv_color_t * dest_buf, const lv_area_t * dest_area,
+ lv_coord_t dest_stride,
+ const lv_color_t * src_buf, const lv_area_t * src_area,
+ const lv_draw_img_dsc_t * dsc, lv_img_cf_t cf)
+{
+ lv_coord_t dest_w = lv_area_get_width(dest_area);
+ lv_coord_t dest_h = lv_area_get_height(dest_area);
+
+ PXP_Init(LV_GPU_NXP_PXP_ID);
+ PXP_EnableCsc1(LV_GPU_NXP_PXP_ID, false); /*Disable CSC1, it is enabled by default.*/
+ PXP_SetProcessBlockSize(LV_GPU_NXP_PXP_ID, kPXP_BlockSize16); /*block size 16x16 for higher performance*/
+
+ pxp_as_blend_config_t asBlendConfig = {
+ .alpha = dsc->opa,
+ .invertAlpha = false,
+ .alphaMode = kPXP_AlphaRop,
+ .ropMode = kPXP_RopMergeAs
+ };
+
+ if(dsc->opa >= (lv_opa_t)LV_OPA_MAX && !lv_img_cf_is_chroma_keyed(cf) && !lv_img_cf_has_alpha(cf)) {
+ /*Simple blit, no effect - Disable PS buffer*/
+ PXP_SetProcessSurfacePosition(LV_GPU_NXP_PXP_ID, 0xFFFFU, 0xFFFFU, 0U, 0U);
+ }
+ else {
+ /*PS must be enabled to fetch background pixels.
+ PS and OUT buffers are the same, blend will be done in-place*/
+ pxp_ps_buffer_config_t psBufferConfig = {
+ .pixelFormat = PXP_PS_PIXEL_FORMAT,
+ .swapByte = false,
+ .bufferAddr = (uint32_t)(dest_buf + dest_stride * dest_area->y1 + dest_area->x1),
+ .bufferAddrU = 0U,
+ .bufferAddrV = 0U,
+ .pitchBytes = dest_stride * sizeof(lv_color_t)
+ };
+ if(dsc->opa >= (lv_opa_t)LV_OPA_MAX) {
+ asBlendConfig.alphaMode = lv_img_cf_has_alpha(cf) ? kPXP_AlphaEmbedded : kPXP_AlphaOverride;
+ }
+ else {
+ asBlendConfig.alphaMode = lv_img_cf_has_alpha(cf) ? kPXP_AlphaMultiply : kPXP_AlphaOverride;
+ }
+ PXP_SetProcessSurfaceBufferConfig(LV_GPU_NXP_PXP_ID, &psBufferConfig);
+ PXP_SetProcessSurfacePosition(LV_GPU_NXP_PXP_ID, 0U, 0U, dest_w - 1, dest_h - 1);
+ }
+
+ lv_coord_t src_stride = lv_area_get_width(src_area);
+
+ /*AS buffer - source image*/
+ pxp_as_buffer_config_t asBufferConfig = {
+ .pixelFormat = PXP_AS_PIXEL_FORMAT,
+ .bufferAddr = (uint32_t)src_buf,
+ .pitchBytes = src_stride * sizeof(lv_color_t)
+ };
+ PXP_SetAlphaSurfaceBufferConfig(LV_GPU_NXP_PXP_ID, &asBufferConfig);
+ PXP_SetAlphaSurfacePosition(LV_GPU_NXP_PXP_ID, 0U, 0U, dest_w - 1U, dest_h - 1U);
+ PXP_SetAlphaSurfaceBlendConfig(LV_GPU_NXP_PXP_ID, &asBlendConfig);
+
+ if(lv_img_cf_is_chroma_keyed(cf)) {
+ lv_color_t colorKeyLow = LV_COLOR_CHROMA_KEY;
+ lv_color_t colorKeyHigh = LV_COLOR_CHROMA_KEY;
+
+ bool recolor = (dsc->recolor_opa != LV_OPA_TRANSP);
+
+ if(recolor) {
+ /* New color key after recoloring */
+ lv_color_t colorKey = lv_color_mix(dsc->recolor, LV_COLOR_CHROMA_KEY, dsc->recolor_opa);
+
+ LV_COLOR_SET_R(colorKeyLow, colorKey.ch.red != 0 ? colorKey.ch.red - 1 : 0);
+ LV_COLOR_SET_G(colorKeyLow, colorKey.ch.green != 0 ? colorKey.ch.green - 1 : 0);
+ LV_COLOR_SET_B(colorKeyLow, colorKey.ch.blue != 0 ? colorKey.ch.blue - 1 : 0);
+
+#if LV_COLOR_DEPTH==16
+ LV_COLOR_SET_R(colorKeyHigh, colorKey.ch.red != 0x1f ? colorKey.ch.red + 1 : 0x1f);
+ LV_COLOR_SET_G(colorKeyHigh, colorKey.ch.green != 0x3f ? colorKey.ch.green + 1 : 0x3f);
+ LV_COLOR_SET_B(colorKeyHigh, colorKey.ch.blue != 0x1f ? colorKey.ch.blue + 1 : 0x1f);
+#else /*LV_COLOR_DEPTH==32*/
+ LV_COLOR_SET_R(colorKeyHigh, colorKey.ch.red != 0xff ? colorKey.ch.red + 1 : 0xff);
+ LV_COLOR_SET_G(colorKeyHigh, colorKey.ch.green != 0xff ? colorKey.ch.green + 1 : 0xff);
+ LV_COLOR_SET_B(colorKeyHigh, colorKey.ch.blue != 0xff ? colorKey.ch.blue + 1 : 0xff);
+#endif
+ }
+
+ PXP_SetAlphaSurfaceOverlayColorKey(LV_GPU_NXP_PXP_ID, lv_color_to32(colorKeyLow),
+ lv_color_to32(colorKeyHigh));
+ }
+
+ PXP_EnableAlphaSurfaceOverlayColorKey(LV_GPU_NXP_PXP_ID, lv_img_cf_is_chroma_keyed(cf));
+
+ /*Output buffer.*/
+ pxp_output_buffer_config_t outputBufferConfig = {
+ .pixelFormat = (pxp_output_pixel_format_t)PXP_OUT_PIXEL_FORMAT,
+ .interlacedMode = kPXP_OutputProgressive,
+ .buffer0Addr = (uint32_t)(dest_buf + dest_stride * dest_area->y1 + dest_area->x1),
+ .buffer1Addr = (uint32_t)0U,
+ .pitchBytes = dest_stride * sizeof(lv_color_t),
+ .width = dest_w,
+ .height = dest_h
+ };
+ PXP_SetOutputBufferConfig(LV_GPU_NXP_PXP_ID, &outputBufferConfig);
+
+ lv_gpu_nxp_pxp_run(); /* Start PXP task */
+
+ return LV_RES_OK;
+}
+
+#endif /*LV_USE_GPU_NXP_PXP*/
diff --git a/lib/lvgl/src/draw/nxp/pxp/lv_draw_pxp_blend.h b/lib/lvgl/src/draw/nxp/pxp/lv_draw_pxp_blend.h
new file mode 100644
index 00000000..43a6440d
--- /dev/null
+++ b/lib/lvgl/src/draw/nxp/pxp/lv_draw_pxp_blend.h
@@ -0,0 +1,143 @@
+/**
+ * @file lv_draw_pxp_blend.h
+ *
+ */
+
+/**
+ * MIT License
+ *
+ * Copyright 2020-2022 NXP
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+ * the Software, and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next paragraph)
+ * shall be included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
+ * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
+ * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+#ifndef LV_DRAW_PXP_BLEND_H
+#define LV_DRAW_PXP_BLEND_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*********************
+ * INCLUDES
+ *********************/
+
+#include "../../../lv_conf_internal.h"
+
+#if LV_USE_GPU_NXP_PXP
+#include "lv_gpu_nxp_pxp.h"
+#include "../../sw/lv_draw_sw.h"
+
+/*********************
+ * DEFINES
+ *********************/
+
+#ifndef LV_GPU_NXP_PXP_BLIT_SIZE_LIMIT
+/** Minimum area (in pixels) for image copy with 100% opacity to be handled by PXP*/
+#define LV_GPU_NXP_PXP_BLIT_SIZE_LIMIT 5000
+#endif
+
+#ifndef LV_GPU_NXP_PXP_BLIT_OPA_SIZE_LIMIT
+/** Minimum area (in pixels) for image copy with transparency to be handled by PXP*/
+#define LV_GPU_NXP_PXP_BLIT_OPA_SIZE_LIMIT 5000
+#endif
+
+#ifndef LV_GPU_NXP_PXP_BUFF_SYNC_BLIT_SIZE_LIMIT
+/** Minimum invalidated area (in pixels) to be synchronized by PXP during buffer sync */
+#define LV_GPU_NXP_PXP_BUFF_SYNC_BLIT_SIZE_LIMIT 5000
+#endif
+
+#ifndef LV_GPU_NXP_PXP_FILL_SIZE_LIMIT
+/** Minimum area (in pixels) to be filled by PXP with 100% opacity*/
+#define LV_GPU_NXP_PXP_FILL_SIZE_LIMIT 5000
+#endif
+
+#ifndef LV_GPU_NXP_PXP_FILL_OPA_SIZE_LIMIT
+/** Minimum area (in pixels) to be filled by PXP with transparency*/
+#define LV_GPU_NXP_PXP_FILL_OPA_SIZE_LIMIT 5000
+#endif
+
+/**********************
+ * TYPEDEFS
+ **********************/
+
+/**********************
+ * GLOBAL PROTOTYPES
+ **********************/
+
+/**
+ * Fill area, with optional opacity.
+ *
+ * @param[in/out] dest_buf destination buffer
+ * @param[in] dest_stride width (stride) of destination buffer in pixels
+ * @param[in] fill_area area to fill
+ * @param[in] color color
+ * @param[in] opa transparency of the color
+ * @retval LV_RES_OK Fill completed
+ * @retval LV_RES_INV Error occurred (\see LV_GPU_NXP_PXP_LOG_ERRORS)
+ */
+lv_res_t lv_gpu_nxp_pxp_fill(lv_color_t * dest_buf, lv_coord_t dest_stride, const lv_area_t * fill_area,
+ lv_color_t color, lv_opa_t opa);
+
+/**
+ * BLock Image Transfer - copy rectangular image from src_buf to dst_buf with effects.
+ * By default, image is copied directly, with optional opacity. This function can also
+ * rotate the display output buffer to a specified angle (90x step).
+ *
+ * @param[in/out] dest_buf destination buffer
+ * @param[in] dest_area destination area
+ * @param[in] dest_stride width (stride) of destination buffer in pixels
+ * @param[in] src_buf source buffer
+ * @param[in] src_area source area with absolute coordinates to draw on destination buffer
+ * @param[in] opa opacity of the result
+ * @param[in] angle display rotation angle (90x)
+ * @retval LV_RES_OK Fill completed
+ * @retval LV_RES_INV Error occurred (\see LV_GPU_NXP_PXP_LOG_ERRORS)
+ */
+lv_res_t lv_gpu_nxp_pxp_blit(lv_color_t * dest_buf, const lv_area_t * dest_area, lv_coord_t dest_stride,
+ const lv_color_t * src_buf, const lv_area_t * src_area, lv_opa_t opa, lv_disp_rot_t angle);
+
+/**
+ * BLock Image Transfer - copy rectangular image from src_buf to dst_buf with transformation.
+ *
+ *
+ * @param[in/out] dest_buf destination buffer
+ * @param[in] dest_area destination area
+ * @param[in] dest_stride width (stride) of destination buffer in pixels
+ * @param[in] src_buf source buffer
+ * @param[in] src_area source area with absolute coordinates to draw on destination buffer
+ * @param[in] dsc image descriptor
+ * @param[in] cf color format
+ * @retval LV_RES_OK Fill completed
+ * @retval LV_RES_INV Error occurred (\see LV_GPU_NXP_PXP_LOG_ERRORS)
+ */
+lv_res_t lv_gpu_nxp_pxp_blit_transform(lv_color_t * dest_buf, const lv_area_t * dest_area, lv_coord_t dest_stride,
+ const lv_color_t * src_buf, const lv_area_t * src_area, const lv_draw_img_dsc_t * dsc, lv_img_cf_t cf);
+
+/**********************
+ * MACROS
+ **********************/
+
+#endif /*LV_USE_GPU_NXP_PXP*/
+
+#ifdef __cplusplus
+} /*extern "C"*/
+#endif
+
+#endif /*LV_DRAW_PXP_BLEND_H*/
diff --git a/lib/lvgl/src/draw/nxp/pxp/lv_gpu_nxp_pxp.c b/lib/lvgl/src/draw/nxp/pxp/lv_gpu_nxp_pxp.c
new file mode 100644
index 00000000..94d242a0
--- /dev/null
+++ b/lib/lvgl/src/draw/nxp/pxp/lv_gpu_nxp_pxp.c
@@ -0,0 +1,116 @@
+/**
+ * @file lv_gpu_nxp_pxp.c
+ *
+ */
+
+/**
+ * MIT License
+ *
+ * Copyright 2020-2022 NXP
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+ * the Software, and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next paragraph)
+ * shall be included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
+ * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
+ * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+/*********************
+ * INCLUDES
+ *********************/
+
+#include "lv_gpu_nxp_pxp.h"
+
+#if LV_USE_GPU_NXP_PXP
+#include "lv_gpu_nxp_pxp_osa.h"
+#include "../../../core/lv_refr.h"
+
+/*********************
+ * DEFINES
+ *********************/
+
+/**********************
+ * TYPEDEFS
+ **********************/
+
+/**********************
+ * STATIC PROTOTYPES
+ **********************/
+
+/**
+ * Clean & invalidate cache.
+ */
+static void invalidate_cache(void);
+
+/**********************
+ * STATIC VARIABLES
+ **********************/
+
+static lv_nxp_pxp_cfg_t * pxp_cfg;
+
+/**********************
+ * MACROS
+ **********************/
+
+/**********************
+ * GLOBAL FUNCTIONS
+ **********************/
+
+lv_res_t lv_gpu_nxp_pxp_init(void)
+{
+ pxp_cfg = lv_gpu_nxp_pxp_get_cfg();
+
+ if(!pxp_cfg || !pxp_cfg->pxp_interrupt_deinit || !pxp_cfg->pxp_interrupt_init || !pxp_cfg->pxp_run)
+ PXP_RETURN_INV("PXP configuration error.");
+
+ PXP_Init(LV_GPU_NXP_PXP_ID);
+ PXP_EnableCsc1(LV_GPU_NXP_PXP_ID, false); /*Disable CSC1, it is enabled by default.*/
+ PXP_EnableInterrupts(LV_GPU_NXP_PXP_ID, kPXP_CompleteInterruptEnable);
+
+ if(pxp_cfg->pxp_interrupt_init() != LV_RES_OK) {
+ PXP_Deinit(LV_GPU_NXP_PXP_ID);
+ PXP_RETURN_INV("PXP interrupt init failed.");
+ }
+
+ return LV_RES_OK;
+}
+
+void lv_gpu_nxp_pxp_deinit(void)
+{
+ pxp_cfg->pxp_interrupt_deinit();
+ PXP_DisableInterrupts(PXP, kPXP_CompleteInterruptEnable);
+ PXP_Deinit(LV_GPU_NXP_PXP_ID);
+}
+
+void lv_gpu_nxp_pxp_run(void)
+{
+ /*Clean & invalidate cache*/
+ invalidate_cache();
+
+ pxp_cfg->pxp_run();
+}
+
+/**********************
+ * STATIC FUNCTIONS
+ **********************/
+
+static void invalidate_cache(void)
+{
+ lv_disp_t * disp = _lv_refr_get_disp_refreshing();
+ if(disp->driver->clean_dcache_cb)
+ disp->driver->clean_dcache_cb(disp->driver);
+}
+
+#endif /*LV_USE_GPU_NXP_PXP*/
diff --git a/lib/lvgl/src/draw/nxp/pxp/lv_gpu_nxp_pxp.h b/lib/lvgl/src/draw/nxp/pxp/lv_gpu_nxp_pxp.h
new file mode 100644
index 00000000..e695d8f1
--- /dev/null
+++ b/lib/lvgl/src/draw/nxp/pxp/lv_gpu_nxp_pxp.h
@@ -0,0 +1,153 @@
+/**
+ * @file lv_gpu_nxp_pxp.h
+ *
+ */
+
+/**
+ * MIT License
+ *
+ * Copyright 2020-2022 NXP
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+ * the Software, and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next paragraph)
+ * shall be included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
+ * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
+ * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+#ifndef LV_GPU_NXP_PXP_H
+#define LV_GPU_NXP_PXP_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*********************
+ * INCLUDES
+ *********************/
+
+#include "../../../lv_conf_internal.h"
+
+#if LV_USE_GPU_NXP_PXP
+#include "fsl_cache.h"
+#include "fsl_pxp.h"
+
+#include "../../../misc/lv_log.h"
+
+/*********************
+ * DEFINES
+ *********************/
+
+/** PXP module instance to use*/
+#define LV_GPU_NXP_PXP_ID PXP
+
+/** PXP interrupt line ID*/
+#define LV_GPU_NXP_PXP_IRQ_ID PXP_IRQn
+
+#ifndef LV_GPU_NXP_PXP_LOG_ERRORS
+/** Enable logging of PXP errors (\see LV_LOG_ERROR)*/
+#define LV_GPU_NXP_PXP_LOG_ERRORS 1
+#endif
+
+#ifndef LV_GPU_NXP_PXP_LOG_TRACES
+/** Enable logging of PXP errors (\see LV_LOG_ERROR)*/
+#define LV_GPU_NXP_PXP_LOG_TRACES 0
+#endif
+
+/**********************
+ * TYPEDEFS
+ **********************/
+
+/**
+ * NXP PXP device configuration - call-backs used for
+ * interrupt init/wait/deinit.
+ */
+typedef struct {
+ /** Callback for PXP interrupt initialization*/
+ lv_res_t (*pxp_interrupt_init)(void);
+
+ /** Callback for PXP interrupt de-initialization*/
+ void (*pxp_interrupt_deinit)(void);
+
+ /** Callback that should start PXP and wait for operation complete*/
+ void (*pxp_run)(void);
+} lv_nxp_pxp_cfg_t;
+
+/**********************
+ * GLOBAL PROTOTYPES
+ **********************/
+
+/**
+ * Reset and initialize PXP device. This function should be called as a part
+ * of display init sequence.
+ *
+ * @retval LV_RES_OK PXP init completed
+ * @retval LV_RES_INV Error occurred (\see LV_GPU_NXP_PXP_LOG_ERRORS)
+ */
+lv_res_t lv_gpu_nxp_pxp_init(void);
+
+/**
+ * Disable PXP device. Should be called during display deinit sequence.
+ */
+void lv_gpu_nxp_pxp_deinit(void);
+
+/**
+ * Start PXP job and wait for completion.
+ */
+void lv_gpu_nxp_pxp_run(void);
+
+/**********************
+ * MACROS
+ **********************/
+
+#define PXP_COND_STOP(cond, txt) \
+ do { \
+ if (cond) { \
+ LV_LOG_ERROR("%s. STOP!", txt); \
+ for ( ; ; ); \
+ } \
+ } while(0)
+
+#if LV_GPU_NXP_PXP_LOG_ERRORS
+#define PXP_RETURN_INV(fmt, ...) \
+ do { \
+ LV_LOG_ERROR(fmt, ##__VA_ARGS__); \
+ return LV_RES_INV; \
+ } while (0)
+#else
+#define PXP_RETURN_INV(fmt, ...) \
+ do { \
+ return LV_RES_INV; \
+ }while(0)
+#endif /*LV_GPU_NXP_PXP_LOG_ERRORS*/
+
+#if LV_GPU_NXP_PXP_LOG_TRACES
+#define PXP_LOG_TRACE(fmt, ...) \
+ do { \
+ LV_LOG_ERROR(fmt, ##__VA_ARGS__); \
+ } while (0)
+#else
+#define PXP_LOG_TRACE(fmt, ...) \
+ do { \
+ } while (0)
+#endif /*LV_GPU_NXP_PXP_LOG_TRACES*/
+
+#endif /*LV_USE_GPU_NXP_PXP*/
+
+#ifdef __cplusplus
+} /*extern "C"*/
+#endif
+
+#endif /*LV_GPU_NXP_PXP_H*/
diff --git a/lib/lvgl/src/draw/nxp/pxp/lv_gpu_nxp_pxp_osa.c b/lib/lvgl/src/draw/nxp/pxp/lv_gpu_nxp_pxp_osa.c
new file mode 100644
index 00000000..c4b8dbe5
--- /dev/null
+++ b/lib/lvgl/src/draw/nxp/pxp/lv_gpu_nxp_pxp_osa.c
@@ -0,0 +1,164 @@
+/**
+ * @file lv_gpu_nxp_pxp_osa.c
+ *
+ */
+
+/**
+ * MIT License
+ *
+ * Copyright 2020, 2022 NXP
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+ * the Software, and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next paragraph)
+ * shall be included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
+ * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
+ * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+/*********************
+ * INCLUDES
+ *********************/
+
+#include "lv_gpu_nxp_pxp_osa.h"
+
+#if LV_USE_GPU_NXP_PXP && LV_USE_GPU_NXP_PXP_AUTO_INIT
+#include "../../../misc/lv_log.h"
+#include "fsl_pxp.h"
+
+#if defined(SDK_OS_FREE_RTOS)
+ #include "FreeRTOS.h"
+ #include "semphr.h"
+#endif
+
+/*********************
+ * DEFINES
+ *********************/
+
+/**********************
+ * TYPEDEFS
+ **********************/
+
+/**********************
+ * STATIC PROTOTYPES
+ **********************/
+
+/**
+ * PXP interrupt initialization.
+ */
+static lv_res_t _lv_gpu_nxp_pxp_interrupt_init(void);
+
+/**
+ * PXP interrupt de-initialization.
+ */
+static void _lv_gpu_nxp_pxp_interrupt_deinit(void);
+
+/**
+ * Start the PXP job and wait for task completion.
+ */
+static void _lv_gpu_nxp_pxp_run(void);
+
+/**********************
+ * STATIC VARIABLES
+ **********************/
+
+#if defined(SDK_OS_FREE_RTOS)
+ static SemaphoreHandle_t s_pxpIdle;
+#else
+ static volatile bool s_pxpIdle;
+#endif
+
+static lv_nxp_pxp_cfg_t pxp_default_cfg = {
+ .pxp_interrupt_init = _lv_gpu_nxp_pxp_interrupt_init,
+ .pxp_interrupt_deinit = _lv_gpu_nxp_pxp_interrupt_deinit,
+ .pxp_run = _lv_gpu_nxp_pxp_run
+};
+
+/**********************
+ * MACROS
+ **********************/
+
+/**********************
+ * GLOBAL FUNCTIONS
+ **********************/
+
+void PXP_IRQHandler(void)
+{
+#if defined(SDK_OS_FREE_RTOS)
+ BaseType_t taskAwake = pdFALSE;
+#endif
+
+ if(kPXP_CompleteFlag & PXP_GetStatusFlags(LV_GPU_NXP_PXP_ID)) {
+ PXP_ClearStatusFlags(LV_GPU_NXP_PXP_ID, kPXP_CompleteFlag);
+#if defined(SDK_OS_FREE_RTOS)
+ xSemaphoreGiveFromISR(s_pxpIdle, &taskAwake);
+ portYIELD_FROM_ISR(taskAwake);
+#else
+ s_pxpIdle = true;
+#endif
+ }
+}
+
+lv_nxp_pxp_cfg_t * lv_gpu_nxp_pxp_get_cfg(void)
+{
+ return &pxp_default_cfg;
+}
+
+/**********************
+ * STATIC FUNCTIONS
+ **********************/
+
+static lv_res_t _lv_gpu_nxp_pxp_interrupt_init(void)
+{
+#if defined(SDK_OS_FREE_RTOS)
+ s_pxpIdle = xSemaphoreCreateBinary();
+ if(s_pxpIdle == NULL)
+ return LV_RES_INV;
+
+ NVIC_SetPriority(LV_GPU_NXP_PXP_IRQ_ID, configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY + 1);
+#else
+ s_pxpIdle = true;
+#endif
+
+ NVIC_EnableIRQ(LV_GPU_NXP_PXP_IRQ_ID);
+
+ return LV_RES_OK;
+}
+
+static void _lv_gpu_nxp_pxp_interrupt_deinit(void)
+{
+ NVIC_DisableIRQ(LV_GPU_NXP_PXP_IRQ_ID);
+#if defined(SDK_OS_FREE_RTOS)
+ vSemaphoreDelete(s_pxpIdle);
+#endif
+}
+
+static void _lv_gpu_nxp_pxp_run(void)
+{
+#if !defined(SDK_OS_FREE_RTOS)
+ s_pxpIdle = false;
+#endif
+
+ PXP_EnableInterrupts(LV_GPU_NXP_PXP_ID, kPXP_CompleteInterruptEnable);
+ PXP_Start(LV_GPU_NXP_PXP_ID);
+
+#if defined(SDK_OS_FREE_RTOS)
+ PXP_COND_STOP(!xSemaphoreTake(s_pxpIdle, portMAX_DELAY), "xSemaphoreTake failed.");
+#else
+ while(s_pxpIdle == false) {
+ }
+#endif
+}
+
+#endif /*LV_USE_GPU_NXP_PXP && LV_USE_GPU_NXP_PXP_AUTO_INIT*/
diff --git a/lib/lvgl/src/draw/nxp/pxp/lv_gpu_nxp_pxp_osa.h b/lib/lvgl/src/draw/nxp/pxp/lv_gpu_nxp_pxp_osa.h
new file mode 100644
index 00000000..5c87824a
--- /dev/null
+++ b/lib/lvgl/src/draw/nxp/pxp/lv_gpu_nxp_pxp_osa.h
@@ -0,0 +1,78 @@
+/**
+ * @file lv_gpu_nxp_pxp_osa.h
+ *
+ */
+
+/**
+ * MIT License
+ *
+ * Copyright 2020, 2022 NXP
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+ * the Software, and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next paragraph)
+ * shall be included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
+ * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
+ * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+#ifndef LV_GPU_NXP_PXP_OSA_H
+#define LV_GPU_NXP_PXP_OSA_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*********************
+ * INCLUDES
+ *********************/
+
+#include "../../../lv_conf_internal.h"
+
+#if LV_USE_GPU_NXP_PXP && LV_USE_GPU_NXP_PXP_AUTO_INIT
+#include "lv_gpu_nxp_pxp.h"
+
+/*********************
+ * DEFINES
+ *********************/
+
+/**********************
+ * TYPEDEFS
+ **********************/
+
+/**********************
+ * GLOBAL PROTOTYPES
+ **********************/
+
+/**
+ * PXP device interrupt handler. Used to check PXP task completion status.
+ */
+void PXP_IRQHandler(void);
+
+/**
+ * Helper function to get the PXP default configuration.
+ */
+lv_nxp_pxp_cfg_t * lv_gpu_nxp_pxp_get_cfg(void);
+
+/**********************
+ * MACROS
+ **********************/
+
+#endif /*LV_USE_GPU_NXP_PXP && LV_USE_GPU_NXP_PXP_AUTO_INIT*/
+
+#ifdef __cplusplus
+} /*extern "C"*/
+#endif
+
+#endif /*LV_GPU_NXP_PXP_OSA_H*/
diff --git a/lib/lvgl/src/draw/nxp/vglite/lv_draw_nxp_vglite.mk b/lib/lvgl/src/draw/nxp/vglite/lv_draw_nxp_vglite.mk
new file mode 100644
index 00000000..c84e2e47
--- /dev/null
+++ b/lib/lvgl/src/draw/nxp/vglite/lv_draw_nxp_vglite.mk
@@ -0,0 +1,9 @@
+CSRCS += lv_draw_vglite_arc.c
+CSRCS += lv_draw_vglite_blend.c
+CSRCS += lv_draw_vglite_rect.c
+CSRCS += lv_gpu_nxp_vglite.c
+
+DEPPATH += --dep-path $(LVGL_DIR)/$(LVGL_DIR_NAME)/src/draw/nxp/vglite
+VPATH += :$(LVGL_DIR)/$(LVGL_DIR_NAME)/src/draw/nxp/vglite
+
+CFLAGS += "-I$(LVGL_DIR)/$(LVGL_DIR_NAME)/src/draw/nxp/vglite"
diff --git a/lib/lvgl/src/draw/nxp/vglite/lv_draw_vglite_arc.c b/lib/lvgl/src/draw/nxp/vglite/lv_draw_vglite_arc.c
new file mode 100644
index 00000000..194f03d8
--- /dev/null
+++ b/lib/lvgl/src/draw/nxp/vglite/lv_draw_vglite_arc.c
@@ -0,0 +1,699 @@
+/**
+ * @file lv_draw_vglite_arc.c
+ *
+ */
+
+/**
+ * MIT License
+ *
+ * Copyright 2021, 2022 NXP
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+ * the Software, and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next paragraph)
+ * shall be included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
+ * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
+ * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+/*********************
+ * INCLUDES
+ *********************/
+
+#include "lv_draw_vglite_arc.h"
+
+#if LV_USE_GPU_NXP_VG_LITE
+#include "math.h"
+
+/*********************
+ * DEFINES
+ *********************/
+
+#define T_FRACTION 16384.0f
+
+#define DICHOTO_ITER 5
+
+static const uint16_t TperDegree[90] = {
+ 0, 174, 348, 522, 697, 873, 1049, 1226, 1403, 1581,
+ 1759, 1938, 2117, 2297, 2477, 2658, 2839, 3020, 3202, 3384,
+ 3567, 3749, 3933, 4116, 4300, 4484, 4668, 4852, 5037, 5222,
+ 5407, 5592, 5777, 5962, 6148, 6334, 6519, 6705, 6891, 7077,
+ 7264, 7450, 7636, 7822, 8008, 8193, 8378, 8564, 8750, 8936,
+ 9122, 9309, 9495, 9681, 9867, 10052, 10238, 10424, 10609, 10794,
+ 10979, 11164, 11349, 11534, 11718, 11902, 12086, 12270, 12453, 12637,
+ 12819, 13002, 13184, 13366, 13547, 13728, 13909, 14089, 14269, 14448,
+ 14627, 14805, 14983, 15160, 15337, 15513, 15689, 15864, 16038, 16212
+};
+
+/**********************
+ * TYPEDEFS
+ **********************/
+
+/* intermediate arc params */
+typedef struct _vg_arc {
+ int32_t angle; /* angle <90deg */
+ int32_t quarter; /* 0-3 counter-clockwise */
+ int32_t rad; /* radius */
+ int32_t p0x; /* point P0 */
+ int32_t p0y;
+ int32_t p1x; /* point P1 */
+ int32_t p1y;
+ int32_t p2x; /* point P2 */
+ int32_t p2y;
+ int32_t p3x; /* point P3 */
+ int32_t p3y;
+} vg_arc;
+
+typedef struct _cubic_cont_pt {
+ float p0;
+ float p1;
+ float p2;
+ float p3;
+} cubic_cont_pt;
+
+/**********************
+ * STATIC PROTOTYPES
+ **********************/
+
+static void rotate_point(int32_t angle, int32_t * x, int32_t * y);
+static void add_arc_path(int32_t * arc_path, int * pidx, int32_t radius,
+ int32_t start_angle, int32_t end_angle, lv_point_t center, bool cw);
+
+/**********************
+ * STATIC VARIABLES
+ **********************/
+
+/**********************
+ * MACROS
+ **********************/
+
+/**********************
+ * GLOBAL FUNCTIONS
+ **********************/
+
+lv_res_t lv_gpu_nxp_vglite_draw_arc(lv_draw_ctx_t * draw_ctx, const lv_draw_arc_dsc_t * dsc, const lv_point_t * center,
+ int32_t radius, int32_t start_angle, int32_t end_angle)
+{
+
+ vg_lite_buffer_t vgbuf;
+ vg_lite_error_t err = VG_LITE_SUCCESS;
+ lv_color32_t col32 = {.full = lv_color_to32(dsc->color)}; /*Convert color to RGBA8888*/
+ lv_coord_t dest_width = lv_area_get_width(draw_ctx->buf_area);
+ lv_coord_t dest_height = lv_area_get_height(draw_ctx->buf_area);
+ vg_lite_path_t path;
+ vg_lite_color_t vgcol; /* vglite takes ABGR */
+ vg_lite_matrix_t matrix;
+ lv_opa_t opa = dsc->opa;
+ bool donut = ((end_angle - start_angle) % 360 == 0) ? true : false;
+ lv_point_t clip_center = {center->x - draw_ctx->buf_area->x1, center->y - draw_ctx->buf_area->y1};
+
+ /* path: max size = 16 cubic bezier (7 words each) */
+ int32_t arc_path[16 * 7];
+ lv_memset_00(arc_path, sizeof(arc_path));
+
+ /*** Init destination buffer ***/
+ if(lv_vglite_init_buf(&vgbuf, (uint32_t)dest_width, (uint32_t)dest_height, (uint32_t)dest_width * sizeof(lv_color_t),
+ (const lv_color_t *)draw_ctx->buf, false) != LV_RES_OK)
+ VG_LITE_RETURN_INV("Init buffer failed.");
+
+ /*** Init path ***/
+ lv_coord_t width = dsc->width; /* inner arc radius = outer arc radius - width */
+ if(width > (lv_coord_t)radius)
+ width = radius;
+
+ int pidx = 0;
+ int32_t cp_x, cp_y; /* control point coords */
+
+ /* first control point of curve */
+ cp_x = radius;
+ cp_y = 0;
+ rotate_point(start_angle, &cp_x, &cp_y);
+ arc_path[pidx++] = VLC_OP_MOVE;
+ arc_path[pidx++] = clip_center.x + cp_x;
+ arc_path[pidx++] = clip_center.y + cp_y;
+
+ /* draw 1-5 outer quarters */
+ add_arc_path(arc_path, &pidx, radius, start_angle, end_angle, clip_center, true);
+
+ if(donut) {
+ /* close outer circle */
+ cp_x = radius;
+ cp_y = 0;
+ rotate_point(start_angle, &cp_x, &cp_y);
+ arc_path[pidx++] = VLC_OP_LINE;
+ arc_path[pidx++] = clip_center.x + cp_x;
+ arc_path[pidx++] = clip_center.y + cp_y;
+ /* start inner circle */
+ cp_x = radius - width;
+ cp_y = 0;
+ rotate_point(start_angle, &cp_x, &cp_y);
+ arc_path[pidx++] = VLC_OP_MOVE;
+ arc_path[pidx++] = clip_center.x + cp_x;
+ arc_path[pidx++] = clip_center.y + cp_y;
+
+ }
+ else if(dsc->rounded != 0U) { /* 1st rounded arc ending */
+ cp_x = radius - width / 2;
+ cp_y = 0;
+ rotate_point(end_angle, &cp_x, &cp_y);
+ lv_point_t round_center = {clip_center.x + cp_x, clip_center.y + cp_y};
+ add_arc_path(arc_path, &pidx, width / 2, end_angle, (end_angle + 180),
+ round_center, true);
+
+ }
+ else { /* 1st flat ending */
+ cp_x = radius - width;
+ cp_y = 0;
+ rotate_point(end_angle, &cp_x, &cp_y);
+ arc_path[pidx++] = VLC_OP_LINE;
+ arc_path[pidx++] = clip_center.x + cp_x;
+ arc_path[pidx++] = clip_center.y + cp_y;
+ }
+
+ /* draw 1-5 inner quarters */
+ add_arc_path(arc_path, &pidx, radius - width, start_angle, end_angle, clip_center, false);
+
+ /* last control point of curve */
+ if(donut) { /* close the loop */
+ cp_x = radius - width;
+ cp_y = 0;
+ rotate_point(start_angle, &cp_x, &cp_y);
+ arc_path[pidx++] = VLC_OP_LINE;
+ arc_path[pidx++] = clip_center.x + cp_x;
+ arc_path[pidx++] = clip_center.y + cp_y;
+
+ }
+ else if(dsc->rounded != 0U) { /* 2nd rounded arc ending */
+ cp_x = radius - width / 2;
+ cp_y = 0;
+ rotate_point(start_angle, &cp_x, &cp_y);
+ lv_point_t round_center = {clip_center.x + cp_x, clip_center.y + cp_y};
+ add_arc_path(arc_path, &pidx, width / 2, (start_angle + 180), (start_angle + 360),
+ round_center, true);
+
+ }
+ else { /* 2nd flat ending */
+ cp_x = radius;
+ cp_y = 0;
+ rotate_point(start_angle, &cp_x, &cp_y);
+ arc_path[pidx++] = VLC_OP_LINE;
+ arc_path[pidx++] = clip_center.x + cp_x;
+ arc_path[pidx++] = clip_center.y + cp_y;
+ }
+
+ arc_path[pidx++] = VLC_OP_END;
+
+ err = vg_lite_init_path(&path, VG_LITE_S32, VG_LITE_HIGH, (uint32_t)pidx * sizeof(int32_t), arc_path,
+ (vg_lite_float_t) draw_ctx->clip_area->x1, (vg_lite_float_t) draw_ctx->clip_area->y1,
+ ((vg_lite_float_t) draw_ctx->clip_area->x2) + 1.0f, ((vg_lite_float_t) draw_ctx->clip_area->y2) + 1.0f);
+ VG_LITE_ERR_RETURN_INV(err, "Init path failed.");
+
+ /* set rotation angle */
+ vg_lite_identity(&matrix);
+
+ if(opa <= (lv_opa_t)LV_OPA_MAX) {
+ /* Only pre-multiply color if hardware pre-multiplication is not present */
+ if(!vg_lite_query_feature(gcFEATURE_BIT_VG_PE_PREMULTIPLY)) {
+ col32.ch.red = (uint8_t)(((uint16_t)col32.ch.red * opa) >> 8);
+ col32.ch.green = (uint8_t)(((uint16_t)col32.ch.green * opa) >> 8);
+ col32.ch.blue = (uint8_t)(((uint16_t)col32.ch.blue * opa) >> 8);
+ }
+ col32.ch.alpha = opa;
+ }
+
+#if LV_COLOR_DEPTH==16
+ vgcol = col32.full;
+#else /*LV_COLOR_DEPTH==32*/
+ vgcol = ((uint32_t)col32.ch.alpha << 24) | ((uint32_t)col32.ch.blue << 16) | ((uint32_t)col32.ch.green << 8) |
+ (uint32_t)col32.ch.red;
+#endif
+
+ /*Clean & invalidate cache*/
+ lv_vglite_invalidate_cache();
+
+ /*** Draw arc ***/
+ err = vg_lite_draw(&vgbuf, &path, VG_LITE_FILL_NON_ZERO, &matrix, VG_LITE_BLEND_SRC_OVER, vgcol);
+ VG_LITE_ERR_RETURN_INV(err, "Draw arc failed.");
+
+ err = vg_lite_finish();
+ VG_LITE_ERR_RETURN_INV(err, "Finish failed.");
+
+ err = vg_lite_clear_path(&path);
+ VG_LITE_ERR_RETURN_INV(err, "Clear path failed.");
+
+ return LV_RES_OK;
+}
+
+/**********************
+ * STATIC FUNCTIONS
+ **********************/
+
+static void copy_arc(vg_arc * dst, vg_arc * src)
+{
+ dst->quarter = src->quarter;
+ dst->rad = src->rad;
+ dst->angle = src->angle;
+ dst->p0x = src->p0x;
+ dst->p1x = src->p1x;
+ dst->p2x = src->p2x;
+ dst->p3x = src->p3x;
+ dst->p0y = src->p0y;
+ dst->p1y = src->p1y;
+ dst->p2y = src->p2y;
+ dst->p3y = src->p3y;
+}
+
+/**
+ * Rotate the point according given rotation angle rotation center is 0,0
+ */
+static void rotate_point(int32_t angle, int32_t * x, int32_t * y)
+{
+ int32_t ori_x = *x;
+ int32_t ori_y = *y;
+ int16_t alpha = (int16_t)angle;
+ *x = ((lv_trigo_cos(alpha) * ori_x) / LV_TRIGO_SIN_MAX) - ((lv_trigo_sin(alpha) * ori_y) / LV_TRIGO_SIN_MAX);
+ *y = ((lv_trigo_sin(alpha) * ori_x) / LV_TRIGO_SIN_MAX) + ((lv_trigo_cos(alpha) * ori_y) / LV_TRIGO_SIN_MAX);
+}
+
+/**
+ * Set full arc control points depending on quarter.
+ * Control points match the best approximation of a circle.
+ * Arc Quarter position is:
+ * Q2 | Q3
+ * ---+---
+ * Q1 | Q0
+ */
+static void set_full_arc(vg_arc * fullarc)
+{
+ /* the tangent lenght for the bezier circle approx */
+ float tang = ((float)fullarc->rad) * BEZIER_OPTIM_CIRCLE;
+ switch(fullarc->quarter) {
+ case 0:
+ /* first quarter */
+ fullarc->p0x = fullarc->rad;
+ fullarc->p0y = 0;
+ fullarc->p1x = fullarc->rad;
+ fullarc->p1y = (int32_t)tang;
+ fullarc->p2x = (int32_t)tang;
+ fullarc->p2y = fullarc->rad;
+ fullarc->p3x = 0;
+ fullarc->p3y = fullarc->rad;
+ break;
+ case 1:
+ /* second quarter */
+ fullarc->p0x = 0;
+ fullarc->p0y = fullarc->rad;
+ fullarc->p1x = 0 - (int32_t)tang;
+ fullarc->p1y = fullarc->rad;
+ fullarc->p2x = 0 - fullarc->rad;
+ fullarc->p2y = (int32_t)tang;
+ fullarc->p3x = 0 - fullarc->rad;
+ fullarc->p3y = 0;
+ break;
+ case 2:
+ /* third quarter */
+ fullarc->p0x = 0 - fullarc->rad;
+ fullarc->p0y = 0;
+ fullarc->p1x = 0 - fullarc->rad;
+ fullarc->p1y = 0 - (int32_t)tang;
+ fullarc->p2x = 0 - (int32_t)tang;
+ fullarc->p2y = 0 - fullarc->rad;
+ fullarc->p3x = 0;
+ fullarc->p3y = 0 - fullarc->rad;
+ break;
+ case 3:
+ /* fourth quarter */
+ fullarc->p0x = 0;
+ fullarc->p0y = 0 - fullarc->rad;
+ fullarc->p1x = (int32_t)tang;
+ fullarc->p1y = 0 - fullarc->rad;
+ fullarc->p2x = fullarc->rad;
+ fullarc->p2y = 0 - (int32_t)tang;
+ fullarc->p3x = fullarc->rad;
+ fullarc->p3y = 0;
+ break;
+ default:
+ LV_LOG_ERROR("Invalid arc quarter value.");
+ break;
+ }
+}
+
+/**
+ * Linear interpolation between two points 'a' and 'b'
+ * 't' parameter is the proportion ratio expressed in range [0 ; T_FRACTION ]
+ */
+static inline float lerp(float coord_a, float coord_b, uint16_t t)
+{
+ float tf = (float)t;
+ return ((T_FRACTION - tf) * coord_a + tf * coord_b) / T_FRACTION;
+}
+
+/**
+ * Computes a point of bezier curve given 't' param
+ */
+static inline float comp_bezier_point(float t, cubic_cont_pt cp)
+{
+ float t_sq = t * t;
+ float inv_t_sq = (1.0f - t) * (1.0f - t);
+ float apt = (1.0f - t) * inv_t_sq * cp.p0 + 3.0f * inv_t_sq * t * cp.p1 + 3.0f * (1.0f - t) * t_sq * cp.p2 + t * t_sq *
+ cp.p3;
+ return apt;
+}
+
+/**
+ * Find parameter 't' in curve at point 'pt'
+ * proceed by dichotomy on only 1 dimension,
+ * works only if the curve is monotonic
+ * bezier curve is defined by control points [p0 p1 p2 p3]
+ * 'dec' tells if curve is decreasing (true) or increasing (false)
+ */
+static uint16_t get_bez_t_from_pos(float pt, cubic_cont_pt cp, bool dec)
+{
+ /* initialize dichotomy with boundary 't' values */
+ float t_low = 0.0f;
+ float t_mid = 0.5f;
+ float t_hig = 1.0f;
+ float a_pt;
+ /* dichotomy loop */
+ for(int i = 0; i < DICHOTO_ITER; i++) {
+ a_pt = comp_bezier_point(t_mid, cp);
+ /* check mid-point position on bezier curve versus targeted point */
+ if((a_pt > pt) != dec) {
+ t_hig = t_mid;
+ }
+ else {
+ t_low = t_mid;
+ }
+ /* define new 't' param for mid-point */
+ t_mid = (t_low + t_hig) / 2.0f;
+ }
+ /* return parameter 't' in integer range [0 ; T_FRACTION] */
+ return (uint16_t)floorf(t_mid * T_FRACTION + 0.5f);
+}
+
+/**
+ * Gives relative coords of the control points
+ * for the sub-arc starting at angle with given angle span
+ */
+static void get_subarc_control_points(vg_arc * arc, int32_t span)
+{
+ vg_arc fullarc;
+ fullarc.angle = arc->angle;
+ fullarc.quarter = arc->quarter;
+ fullarc.rad = arc->rad;
+ set_full_arc(&fullarc);
+
+ /* special case of full arc */
+ if(arc->angle == 90) {
+ copy_arc(arc, &fullarc);
+ return;
+ }
+
+ /* compute 1st arc using the geometric construction of curve */
+ uint16_t t2 = TperDegree[arc->angle + span];
+
+ /* lerp for A */
+ float a2x = lerp((float)fullarc.p0x, (float)fullarc.p1x, t2);
+ float a2y = lerp((float)fullarc.p0y, (float)fullarc.p1y, t2);
+ /* lerp for B */
+ float b2x = lerp((float)fullarc.p1x, (float)fullarc.p2x, t2);
+ float b2y = lerp((float)fullarc.p1y, (float)fullarc.p2y, t2);
+ /* lerp for C */
+ float c2x = lerp((float)fullarc.p2x, (float)fullarc.p3x, t2);
+ float c2y = lerp((float)fullarc.p2y, (float)fullarc.p3y, t2);
+
+ /* lerp for D */
+ float d2x = lerp(a2x, b2x, t2);
+ float d2y = lerp(a2y, b2y, t2);
+ /* lerp for E */
+ float e2x = lerp(b2x, c2x, t2);
+ float e2y = lerp(b2y, c2y, t2);
+
+ float pt2x = lerp(d2x, e2x, t2);
+ float pt2y = lerp(d2y, e2y, t2);
+
+ /* compute sub-arc using the geometric construction of curve */
+ uint16_t t1 = TperDegree[arc->angle];
+
+ /* lerp for A */
+ float a1x = lerp((float)fullarc.p0x, (float)fullarc.p1x, t1);
+ float a1y = lerp((float)fullarc.p0y, (float)fullarc.p1y, t1);
+ /* lerp for B */
+ float b1x = lerp((float)fullarc.p1x, (float)fullarc.p2x, t1);
+ float b1y = lerp((float)fullarc.p1y, (float)fullarc.p2y, t1);
+ /* lerp for C */
+ float c1x = lerp((float)fullarc.p2x, (float)fullarc.p3x, t1);
+ float c1y = lerp((float)fullarc.p2y, (float)fullarc.p3y, t1);
+
+ /* lerp for D */
+ float d1x = lerp(a1x, b1x, t1);
+ float d1y = lerp(a1y, b1y, t1);
+ /* lerp for E */
+ float e1x = lerp(b1x, c1x, t1);
+ float e1y = lerp(b1y, c1y, t1);
+
+ float pt1x = lerp(d1x, e1x, t1);
+ float pt1y = lerp(d1y, e1y, t1);
+
+ /* find the 't3' parameter for point P(t1) on the sub-arc [P0 A2 D2 P(t2)] using dichotomy
+ * use position of x axis only */
+ uint16_t t3;
+ t3 = get_bez_t_from_pos(pt1x,
+ (cubic_cont_pt) {
+ .p0 = ((float)fullarc.p0x), .p1 = a2x, .p2 = d2x, .p3 = pt2x
+ },
+ (bool)(pt2x < (float)fullarc.p0x));
+
+ /* lerp for B */
+ float b3x = lerp(a2x, d2x, t3);
+ float b3y = lerp(a2y, d2y, t3);
+ /* lerp for C */
+ float c3x = lerp(d2x, pt2x, t3);
+ float c3y = lerp(d2y, pt2y, t3);
+
+ /* lerp for E */
+ float e3x = lerp(b3x, c3x, t3);
+ float e3y = lerp(b3y, c3y, t3);
+
+ arc->p0x = (int32_t)floorf(0.5f + pt1x);
+ arc->p0y = (int32_t)floorf(0.5f + pt1y);
+ arc->p1x = (int32_t)floorf(0.5f + e3x);
+ arc->p1y = (int32_t)floorf(0.5f + e3y);
+ arc->p2x = (int32_t)floorf(0.5f + c3x);
+ arc->p2y = (int32_t)floorf(0.5f + c3y);
+ arc->p3x = (int32_t)floorf(0.5f + pt2x);
+ arc->p3y = (int32_t)floorf(0.5f + pt2y);
+}
+
+/**
+ * Gives relative coords of the control points
+ */
+static void get_arc_control_points(vg_arc * arc, bool start)
+{
+ vg_arc fullarc;
+ fullarc.angle = arc->angle;
+ fullarc.quarter = arc->quarter;
+ fullarc.rad = arc->rad;
+ set_full_arc(&fullarc);
+
+ /* special case of full arc */
+ if(arc->angle == 90) {
+ copy_arc(arc, &fullarc);
+ return;
+ }
+
+ /* compute sub-arc using the geometric construction of curve */
+ uint16_t t = TperDegree[arc->angle];
+ /* lerp for A */
+ float ax = lerp((float)fullarc.p0x, (float)fullarc.p1x, t);
+ float ay = lerp((float)fullarc.p0y, (float)fullarc.p1y, t);
+ /* lerp for B */
+ float bx = lerp((float)fullarc.p1x, (float)fullarc.p2x, t);
+ float by = lerp((float)fullarc.p1y, (float)fullarc.p2y, t);
+ /* lerp for C */
+ float cx = lerp((float)fullarc.p2x, (float)fullarc.p3x, t);
+ float cy = lerp((float)fullarc.p2y, (float)fullarc.p3y, t);
+
+ /* lerp for D */
+ float dx = lerp(ax, bx, t);
+ float dy = lerp(ay, by, t);
+ /* lerp for E */
+ float ex = lerp(bx, cx, t);
+ float ey = lerp(by, cy, t);
+
+ /* sub-arc's control points are tangents of DeCasteljau's algorithm */
+ if(start) {
+ arc->p0x = (int32_t)floorf(0.5f + lerp(dx, ex, t));
+ arc->p0y = (int32_t)floorf(0.5f + lerp(dy, ey, t));
+ arc->p1x = (int32_t)floorf(0.5f + ex);
+ arc->p1y = (int32_t)floorf(0.5f + ey);
+ arc->p2x = (int32_t)floorf(0.5f + cx);
+ arc->p2y = (int32_t)floorf(0.5f + cy);
+ arc->p3x = fullarc.p3x;
+ arc->p3y = fullarc.p3y;
+ }
+ else {
+ arc->p0x = fullarc.p0x;
+ arc->p0y = fullarc.p0y;
+ arc->p1x = (int32_t)floorf(0.5f + ax);
+ arc->p1y = (int32_t)floorf(0.5f + ay);
+ arc->p2x = (int32_t)floorf(0.5f + dx);
+ arc->p2y = (int32_t)floorf(0.5f + dy);
+ arc->p3x = (int32_t)floorf(0.5f + lerp(dx, ex, t));
+ arc->p3y = (int32_t)floorf(0.5f + lerp(dy, ey, t));
+ }
+}
+
+/**
+ * Add the arc control points into the path data for vglite,
+ * taking into account the real center of the arc (translation).
+ * arc_path: (in/out) the path data array for vglite
+ * pidx: (in/out) index of last element added in arc_path
+ * q_arc: (in) the arc data containing control points
+ * center: (in) the center of the circle in draw coordinates
+ * cw: (in) true if arc is clockwise
+ */
+static void add_split_arc_path(int32_t * arc_path, int * pidx, vg_arc * q_arc, lv_point_t center, bool cw)
+{
+ /* assumes first control point already in array arc_path[] */
+ int idx = *pidx;
+ if(cw) {
+#if BEZIER_DBG_CONTROL_POINTS
+ arc_path[idx++] = VLC_OP_LINE;
+ arc_path[idx++] = q_arc->p1x + center.x;
+ arc_path[idx++] = q_arc->p1y + center.y;
+ arc_path[idx++] = VLC_OP_LINE;
+ arc_path[idx++] = q_arc->p2x + center.x;
+ arc_path[idx++] = q_arc->p2y + center.y;
+ arc_path[idx++] = VLC_OP_LINE;
+ arc_path[idx++] = q_arc->p3x + center.x;
+ arc_path[idx++] = q_arc->p3y + center.y;
+#else
+ arc_path[idx++] = VLC_OP_CUBIC;
+ arc_path[idx++] = q_arc->p1x + center.x;
+ arc_path[idx++] = q_arc->p1y + center.y;
+ arc_path[idx++] = q_arc->p2x + center.x;
+ arc_path[idx++] = q_arc->p2y + center.y;
+ arc_path[idx++] = q_arc->p3x + center.x;
+ arc_path[idx++] = q_arc->p3y + center.y;
+#endif
+ }
+ else { /* reverse points order when counter-clockwise */
+#if BEZIER_DBG_CONTROL_POINTS
+ arc_path[idx++] = VLC_OP_LINE;
+ arc_path[idx++] = q_arc->p2x + center.x;
+ arc_path[idx++] = q_arc->p2y + center.y;
+ arc_path[idx++] = VLC_OP_LINE;
+ arc_path[idx++] = q_arc->p1x + center.x;
+ arc_path[idx++] = q_arc->p1y + center.y;
+ arc_path[idx++] = VLC_OP_LINE;
+ arc_path[idx++] = q_arc->p0x + center.x;
+ arc_path[idx++] = q_arc->p0y + center.y;
+#else
+ arc_path[idx++] = VLC_OP_CUBIC;
+ arc_path[idx++] = q_arc->p2x + center.x;
+ arc_path[idx++] = q_arc->p2y + center.y;
+ arc_path[idx++] = q_arc->p1x + center.x;
+ arc_path[idx++] = q_arc->p1y + center.y;
+ arc_path[idx++] = q_arc->p0x + center.x;
+ arc_path[idx++] = q_arc->p0y + center.y;
+#endif
+ }
+ /* update index i n path array*/
+ *pidx = idx;
+}
+
+static void add_arc_path(int32_t * arc_path, int * pidx, int32_t radius,
+ int32_t start_angle, int32_t end_angle, lv_point_t center, bool cw)
+{
+ /* set number of arcs to draw */
+ vg_arc q_arc;
+ int32_t start_arc_angle = start_angle % 90;
+ int32_t end_arc_angle = end_angle % 90;
+ int32_t inv_start_arc_angle = (start_arc_angle > 0) ? (90 - start_arc_angle) : 0;
+ int32_t nbarc = (end_angle - start_angle - inv_start_arc_angle - end_arc_angle) / 90;
+ q_arc.rad = radius;
+
+ /* handle special case of start & end point in the same quarter */
+ if(((start_angle / 90) == (end_angle / 90)) && (nbarc <= 0)) {
+ q_arc.quarter = (start_angle / 90) % 4;
+ q_arc.angle = start_arc_angle;
+ get_subarc_control_points(&q_arc, end_arc_angle - start_arc_angle);
+ add_split_arc_path(arc_path, pidx, &q_arc, center, cw);
+ return;
+ }
+
+ if(cw) {
+ /* partial starting arc */
+ if(start_arc_angle > 0) {
+ q_arc.quarter = (start_angle / 90) % 4;
+ q_arc.angle = start_arc_angle;
+ /* get cubic points relative to center */
+ get_arc_control_points(&q_arc, true);
+ /* put cubic points in arc_path */
+ add_split_arc_path(arc_path, pidx, &q_arc, center, cw);
+ }
+ /* full arcs */
+ for(int32_t q = 0; q < nbarc ; q++) {
+ q_arc.quarter = (q + ((start_angle + 89) / 90)) % 4;
+ q_arc.angle = 90;
+ /* get cubic points relative to center */
+ get_arc_control_points(&q_arc, true); /* 2nd parameter 'start' ignored */
+ /* put cubic points in arc_path */
+ add_split_arc_path(arc_path, pidx, &q_arc, center, cw);
+ }
+ /* partial ending arc */
+ if(end_arc_angle > 0) {
+ q_arc.quarter = (end_angle / 90) % 4;
+ q_arc.angle = end_arc_angle;
+ /* get cubic points relative to center */
+ get_arc_control_points(&q_arc, false);
+ /* put cubic points in arc_path */
+ add_split_arc_path(arc_path, pidx, &q_arc, center, cw);
+ }
+
+ }
+ else { /* counter clockwise */
+
+ /* partial ending arc */
+ if(end_arc_angle > 0) {
+ q_arc.quarter = (end_angle / 90) % 4;
+ q_arc.angle = end_arc_angle;
+ /* get cubic points relative to center */
+ get_arc_control_points(&q_arc, false);
+ /* put cubic points in arc_path */
+ add_split_arc_path(arc_path, pidx, &q_arc, center, cw);
+ }
+ /* full arcs */
+ for(int32_t q = nbarc - 1; q >= 0; q--) {
+ q_arc.quarter = (q + ((start_angle + 89) / 90)) % 4;
+ q_arc.angle = 90;
+ /* get cubic points relative to center */
+ get_arc_control_points(&q_arc, true); /* 2nd parameter 'start' ignored */
+ /* put cubic points in arc_path */
+ add_split_arc_path(arc_path, pidx, &q_arc, center, cw);
+ }
+ /* partial starting arc */
+ if(start_arc_angle > 0) {
+ q_arc.quarter = (start_angle / 90) % 4;
+ q_arc.angle = start_arc_angle;
+ /* get cubic points relative to center */
+ get_arc_control_points(&q_arc, true);
+ /* put cubic points in arc_path */
+ add_split_arc_path(arc_path, pidx, &q_arc, center, cw);
+ }
+ }
+}
+
+#endif /*LV_USE_GPU_NXP_VG_LITE*/
diff --git a/lib/lvgl/src/draw/nxp/vglite/lv_draw_vglite_arc.h b/lib/lvgl/src/draw/nxp/vglite/lv_draw_vglite_arc.h
new file mode 100644
index 00000000..98ba8a3d
--- /dev/null
+++ b/lib/lvgl/src/draw/nxp/vglite/lv_draw_vglite_arc.h
@@ -0,0 +1,79 @@
+/**
+ * @file lv_draw_vglite_arc.h
+ *
+ */
+
+/**
+ * MIT License
+ *
+ * Copyright 2021, 2022 NXP
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+ * the Software, and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next paragraph)
+ * shall be included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
+ * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
+ * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+#ifndef LV_DRAW_VGLITE_ARC_H
+#define LV_DRAW_VGLITE_ARC_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*********************
+ * INCLUDES
+ *********************/
+#include "../../../lv_conf_internal.h"
+
+#if LV_USE_GPU_NXP_VG_LITE
+#include "lv_gpu_nxp_vglite.h"
+
+/*********************
+ * DEFINES
+ *********************/
+
+/**********************
+ * TYPEDEFS
+ **********************/
+
+/**********************
+ * GLOBAL PROTOTYPES
+ **********************/
+
+/***
+ * Draw arc shape with effects
+ * @param draw_ctx drawing context
+ * @param dsc the arc description structure (width, rounded ending, opacity)
+ * @param center the coordinates of the arc center
+ * @param radius the radius of external arc
+ * @param start_angle the starting angle in degrees
+ * @param end_angle the ending angle in degrees
+ */
+lv_res_t lv_gpu_nxp_vglite_draw_arc(lv_draw_ctx_t * draw_ctx, const lv_draw_arc_dsc_t * dsc, const lv_point_t * center,
+ int32_t radius, int32_t start_angle, int32_t end_angle);
+
+/**********************
+ * MACROS
+ **********************/
+
+#endif /*LV_USE_GPU_NXP_VG_LITE*/
+
+#ifdef __cplusplus
+} /*extern "C"*/
+#endif
+
+#endif /*LV_DRAW_VGLITE_ARC_H*/
diff --git a/lib/lvgl/src/draw/nxp/vglite/lv_draw_vglite_blend.c b/lib/lvgl/src/draw/nxp/vglite/lv_draw_vglite_blend.c
new file mode 100644
index 00000000..b59b143b
--- /dev/null
+++ b/lib/lvgl/src/draw/nxp/vglite/lv_draw_vglite_blend.c
@@ -0,0 +1,618 @@
+/**
+ * @file lv_draw_vglite_blend.c
+ *
+ */
+
+/**
+ * MIT License
+ *
+ * Copyright 2020-2022 NXP
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+ * the Software, and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next paragraph)
+ * shall be included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
+ * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
+ * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+/*********************
+ * INCLUDES
+ *********************/
+
+#include "lv_draw_vglite_blend.h"
+
+#if LV_USE_GPU_NXP_VG_LITE
+
+/*********************
+ * DEFINES
+ *********************/
+
+/* Enable BLIT quality degradation workaround for RT595, recommended for screen's dimension > 352 pixels */
+#define RT595_BLIT_WRKRND_ENABLED 1
+
+/* Internal compound symbol */
+#if (defined(CPU_MIMXRT595SFFOB) || defined(CPU_MIMXRT595SFFOB_cm33) || \
+ defined(CPU_MIMXRT595SFFOC) || defined(CPU_MIMXRT595SFFOC_cm33)) && \
+ RT595_BLIT_WRKRND_ENABLED
+#define VG_LITE_BLIT_SPLIT_ENABLED 1
+#else
+#define VG_LITE_BLIT_SPLIT_ENABLED 0
+#endif
+
+/**
+ * BLIT split threshold - BLITs with width or height higher than this value will be done
+ * in multiple steps. Value must be 16-aligned. Don't change.
+ */
+#define LV_GPU_NXP_VG_LITE_BLIT_SPLIT_THR 352
+
+
+/**********************
+ * TYPEDEFS
+ **********************/
+
+/**********************
+ * STATIC PROTOTYPES
+ **********************/
+
+/**
+ * BLock Image Transfer - single direct BLIT.
+ *
+ * @param[in] blit Description of the transfer
+ * @retval LV_RES_OK Transfer complete
+ * @retval LV_RES_INV Error occurred (\see LV_GPU_NXP_VG_LITE_LOG_ERRORS)
+ */
+static lv_res_t _lv_gpu_nxp_vglite_blit_single(lv_gpu_nxp_vglite_blit_info_t * blit);
+
+#if VG_LITE_BLIT_SPLIT_ENABLED
+
+ /**
+ * Move buffer pointer as close as possible to area, but with respect to alignment requirements. X-axis only.
+ *
+ * @param[in,out] area Area to be updated
+ * @param[in,out] buf Pointer to be updated
+ */
+ static void _align_x(lv_area_t * area, lv_color_t ** buf);
+
+ /**
+ * Move buffer pointer to the area start and update variables, Y-axis only.
+ *
+ * @param[in,out] area Area to be updated
+ * @param[in,out] buf Pointer to be updated
+ * @param[in] stridePx Buffer stride in pixels
+ */
+ static void _align_y(lv_area_t * area, lv_color_t ** buf, uint32_t stridePx);
+
+ /**
+ * Software BLIT as a fall-back scenario.
+ *
+ * @param[in] blit BLIT configuration
+ */
+ static void _sw_blit(lv_gpu_nxp_vglite_blit_info_t * blit);
+
+ /**
+ * Verify BLIT structure - widths, stride, pointer alignment
+ *
+ * @param[in] blit BLIT configuration
+ * @retval LV_RES_OK
+ * @retval LV_RES_INV Error occurred (\see LV_GPU_NXP_VG_LITE_LOG_ERRORS)
+ */
+ static lv_res_t _lv_gpu_nxp_vglite_check_blit(lv_gpu_nxp_vglite_blit_info_t * blit);
+
+ /**
+ * BLock Image Transfer - split BLIT.
+ *
+ * @param[in] blit BLIT configuration
+ * @retval LV_RES_OK Transfer complete
+ * @retval LV_RES_INV Error occurred (\see LV_GPU_NXP_VG_LITE_LOG_ERRORS)
+ */
+ static lv_res_t _lv_gpu_nxp_vglite_blit_split(lv_gpu_nxp_vglite_blit_info_t * blit);
+#endif
+
+/**********************
+ * STATIC VARIABLES
+ **********************/
+
+/**********************
+ * MACROS
+ **********************/
+
+/**********************
+ * GLOBAL FUNCTIONS
+ **********************/
+
+lv_res_t lv_gpu_nxp_vglite_fill(lv_color_t * dest_buf, lv_coord_t dest_width, lv_coord_t dest_height,
+ const lv_area_t * fill_area, lv_color_t color, lv_opa_t opa)
+{
+ uint32_t area_size = lv_area_get_size(fill_area);
+ lv_coord_t area_w = lv_area_get_width(fill_area);
+ lv_coord_t area_h = lv_area_get_height(fill_area);
+
+ if(opa >= (lv_opa_t)LV_OPA_MAX) {
+ if(area_size < LV_GPU_NXP_VG_LITE_FILL_SIZE_LIMIT)
+ VG_LITE_RETURN_INV("Area size %d smaller than limit %d.", area_size, LV_GPU_NXP_VG_LITE_FILL_SIZE_LIMIT);
+ }
+ else {
+ if(area_size < LV_GPU_NXP_VG_LITE_FILL_OPA_SIZE_LIMIT)
+ VG_LITE_RETURN_INV("Area size %d smaller than limit %d.", area_size, LV_GPU_NXP_VG_LITE_FILL_OPA_SIZE_LIMIT);
+ }
+
+ vg_lite_buffer_t vgbuf;
+ vg_lite_rectangle_t rect;
+ vg_lite_error_t err = VG_LITE_SUCCESS;
+ lv_color32_t col32 = {.full = lv_color_to32(color)}; /*Convert color to RGBA8888*/
+ vg_lite_color_t vgcol; /* vglite takes ABGR */
+
+ if(lv_vglite_init_buf(&vgbuf, (uint32_t)dest_width, (uint32_t)dest_height, (uint32_t)dest_width * sizeof(lv_color_t),
+ (const lv_color_t *)dest_buf, false) != LV_RES_OK)
+ VG_LITE_RETURN_INV("Init buffer failed.");
+
+ if(opa >= (lv_opa_t)LV_OPA_MAX) { /*Opaque fill*/
+ rect.x = fill_area->x1;
+ rect.y = fill_area->y1;
+ rect.width = area_w;
+ rect.height = area_h;
+
+ /*Clean & invalidate cache*/
+ lv_vglite_invalidate_cache();
+
+#if LV_COLOR_DEPTH==16
+ vgcol = col32.full;
+#else /*LV_COLOR_DEPTH==32*/
+ vgcol = ((uint32_t)col32.ch.alpha << 24) | ((uint32_t)col32.ch.blue << 16) | ((uint32_t)col32.ch.green << 8) |
+ (uint32_t)col32.ch.red;
+#endif
+
+ err = vg_lite_clear(&vgbuf, &rect, vgcol);
+ VG_LITE_ERR_RETURN_INV(err, "Clear failed.");
+
+ err = vg_lite_finish();
+ VG_LITE_ERR_RETURN_INV(err, "Finish failed.");
+ }
+ else { /*fill with transparency*/
+
+ vg_lite_path_t path;
+ int32_t path_data[] = { /*VG rectangular path*/
+ VLC_OP_MOVE, fill_area->x1, fill_area->y1,
+ VLC_OP_LINE, fill_area->x2 + 1, fill_area->y1,
+ VLC_OP_LINE, fill_area->x2 + 1, fill_area->y2 + 1,
+ VLC_OP_LINE, fill_area->x1, fill_area->y2 + 1,
+ VLC_OP_LINE, fill_area->x1, fill_area->y1,
+ VLC_OP_END
+ };
+
+ err = vg_lite_init_path(&path, VG_LITE_S32, VG_LITE_LOW, sizeof(path_data), path_data,
+ (vg_lite_float_t) fill_area->x1, (vg_lite_float_t) fill_area->y1,
+ ((vg_lite_float_t) fill_area->x2) + 1.0f, ((vg_lite_float_t) fill_area->y2) + 1.0f);
+ VG_LITE_ERR_RETURN_INV(err, "Init path failed.");
+
+ /* Only pre-multiply color if hardware pre-multiplication is not present */
+ if(!vg_lite_query_feature(gcFEATURE_BIT_VG_PE_PREMULTIPLY)) {
+ col32.ch.red = (uint8_t)(((uint16_t)col32.ch.red * opa) >> 8);
+ col32.ch.green = (uint8_t)(((uint16_t)col32.ch.green * opa) >> 8);
+ col32.ch.blue = (uint8_t)(((uint16_t)col32.ch.blue * opa) >> 8);
+ }
+ col32.ch.alpha = opa;
+
+#if LV_COLOR_DEPTH==16
+ vgcol = col32.full;
+#else /*LV_COLOR_DEPTH==32*/
+ vgcol = ((uint32_t)col32.ch.alpha << 24) | ((uint32_t)col32.ch.blue << 16) | ((uint32_t)col32.ch.green << 8) |
+ (uint32_t)col32.ch.red;
+#endif
+
+ /*Clean & invalidate cache*/
+ lv_vglite_invalidate_cache();
+
+ vg_lite_matrix_t matrix;
+ vg_lite_identity(&matrix);
+
+ /*Draw rectangle*/
+ err = vg_lite_draw(&vgbuf, &path, VG_LITE_FILL_EVEN_ODD, &matrix, VG_LITE_BLEND_SRC_OVER, vgcol);
+ VG_LITE_ERR_RETURN_INV(err, "Draw rectangle failed.");
+
+ err = vg_lite_finish();
+ VG_LITE_ERR_RETURN_INV(err, "Finish failed.");
+
+ err = vg_lite_clear_path(&path);
+ VG_LITE_ERR_RETURN_INV(err, "Clear path failed.");
+ }
+
+ return LV_RES_OK;
+}
+
+lv_res_t lv_gpu_nxp_vglite_blit(lv_gpu_nxp_vglite_blit_info_t * blit)
+{
+ uint32_t dest_size = lv_area_get_size(&blit->dst_area);
+
+ if(blit->opa >= (lv_opa_t)LV_OPA_MAX) {
+ if(dest_size < LV_GPU_NXP_VG_LITE_BLIT_SIZE_LIMIT)
+ VG_LITE_RETURN_INV("Area size %d smaller than limit %d.", dest_size, LV_GPU_NXP_VG_LITE_BLIT_SIZE_LIMIT);
+ }
+ else {
+ if(dest_size < LV_GPU_NXP_VG_LITE_BLIT_OPA_SIZE_LIMIT)
+ VG_LITE_RETURN_INV("Area size %d smaller than limit %d.", dest_size, LV_GPU_NXP_VG_LITE_BLIT_OPA_SIZE_LIMIT);
+ }
+
+#if VG_LITE_BLIT_SPLIT_ENABLED
+ return _lv_gpu_nxp_vglite_blit_split(blit);
+#endif /* non RT595 */
+
+ /* Just pass down */
+ return _lv_gpu_nxp_vglite_blit_single(blit);
+}
+
+lv_res_t lv_gpu_nxp_vglite_blit_transform(lv_gpu_nxp_vglite_blit_info_t * blit)
+{
+ uint32_t dest_size = lv_area_get_size(&blit->dst_area);
+
+ if(blit->opa >= (lv_opa_t)LV_OPA_MAX) {
+ if(dest_size < LV_GPU_NXP_VG_LITE_BLIT_SIZE_LIMIT)
+ VG_LITE_RETURN_INV("Area size %d smaller than limit %d.", dest_size, LV_GPU_NXP_VG_LITE_BLIT_SIZE_LIMIT);
+ }
+ else {
+ if(dest_size < LV_GPU_NXP_VG_LITE_BLIT_OPA_SIZE_LIMIT)
+ VG_LITE_RETURN_INV("Area size %d smaller than limit %d.", dest_size, LV_GPU_NXP_VG_LITE_BLIT_OPA_SIZE_LIMIT);
+ }
+
+ return _lv_gpu_nxp_vglite_blit_single(blit);
+}
+
+/**********************
+ * STATIC FUNCTIONS
+ **********************/
+
+#if VG_LITE_BLIT_SPLIT_ENABLED
+static lv_res_t _lv_gpu_nxp_vglite_blit_split(lv_gpu_nxp_vglite_blit_info_t * blit)
+{
+ lv_res_t rv = LV_RES_INV;
+
+ if(_lv_gpu_nxp_vglite_check_blit(blit) != LV_RES_OK) {
+ PRINT_BLT("Blit check failed\n");
+ return LV_RES_INV;
+ }
+
+ PRINT_BLT("BLIT from: "
+ "Area: %03d,%03d - %03d,%03d "
+ "Addr: %d\n\n",
+ blit->src_area.x1, blit->src_area.y1,
+ blit->src_area.x2, blit->src_area.y2,
+ (uintptr_t) blit->src);
+
+ PRINT_BLT("BLIT to: "
+ "Area: %03d,%03d - %03d,%03d "
+ "Addr: %d\n\n",
+ blit->dst_area.x1, blit->dst_area.y1,
+ blit->dst_area.x2, blit->dst_area.y2,
+ (uintptr_t) blit->src);
+
+ /* Stage 1: Move starting pointers as close as possible to [x1, y1], so coordinates are as small as possible. */
+ _align_x(&blit->src_area, (lv_color_t **)&blit->src);
+ _align_y(&blit->src_area, (lv_color_t **)&blit->src, blit->src_stride / sizeof(lv_color_t));
+ _align_x(&blit->dst_area, (lv_color_t **)&blit->dst);
+ _align_y(&blit->dst_area, (lv_color_t **)&blit->dst, blit->dst_stride / sizeof(lv_color_t));
+
+ /* Stage 2: If we're in limit, do a single BLIT */
+ if((blit->src_area.x2 < LV_GPU_NXP_VG_LITE_BLIT_SPLIT_THR) &&
+ (blit->src_area.y2 < LV_GPU_NXP_VG_LITE_BLIT_SPLIT_THR)) {
+ PRINT_BLT("Simple blit!\n");
+ return _lv_gpu_nxp_vglite_blit_single(blit);
+ };
+
+ /* Stage 3: Split the BLIT into multiple tiles */
+ PRINT_BLT("Split blit!\n");
+
+ PRINT_BLT("Blit "
+ "([%03d,%03d], [%03d,%03d]) -> "
+ "([%03d,%03d], [%03d,%03d]) | "
+ "([%03dx%03d] -> [%03dx%03d]) | "
+ "A:(%d -> %d)\n",
+ blit->src_area.x1, blit->src_area.y1, blit->src_area.x2, blit->src_area.y2,
+ blit->dst_area.x1, blit->dst_area.y1, blit->dst_area.x2, blit->dst_area.y2,
+ lv_area_get_width(&blit->src_area), lv_area_get_height(&blit->src_area),
+ lv_area_get_width(&blit->dst_area), lv_area_get_height(&blit->dst_area),
+ (uintptr_t) blit->src, (uintptr_t) blit->dst);
+
+
+ lv_coord_t totalWidth = lv_area_get_width(&blit->src_area);
+ lv_coord_t totalHeight = lv_area_get_height(&blit->src_area);
+
+ lv_gpu_nxp_vglite_blit_info_t tileBlit;
+
+ /* Number of tiles needed */
+ int totalTilesX = (blit->src_area.x1 + totalWidth + LV_GPU_NXP_VG_LITE_BLIT_SPLIT_THR - 1) /
+ LV_GPU_NXP_VG_LITE_BLIT_SPLIT_THR;
+ int totalTilesY = (blit->src_area.y1 + totalHeight + LV_GPU_NXP_VG_LITE_BLIT_SPLIT_THR - 1) /
+ LV_GPU_NXP_VG_LITE_BLIT_SPLIT_THR;
+
+ /* src and dst buffer shift against each other. Src buffer real data [0,0] may start actually at [3,0] in buffer, as
+ * the buffer pointer has to be aligned, while dst buffer real data [0,0] may start at [1,0] in buffer. alignment may be
+ * different */
+ int shiftSrcX = (blit->src_area.x1 > blit->dst_area.x1) ? (blit->src_area.x1 - blit->dst_area.x1) : 0;
+ int shiftDstX = (blit->src_area.x1 < blit->dst_area.x1) ? (blit->dst_area.x1 - blit->src_area.x1) : 0;
+
+ PRINT_BLT("\n");
+ PRINT_BLT("Align shift: src: %d, dst: %d\n", shiftSrcX, shiftDstX);
+
+ tileBlit = *blit;
+
+ for(int tileY = 0; tileY < totalTilesY; tileY++) {
+
+ tileBlit.src_area.y1 = 0; /* no vertical alignment, always start from 0 */
+ tileBlit.src_area.y2 = totalHeight - tileY * LV_GPU_NXP_VG_LITE_BLIT_SPLIT_THR - 1;
+ if(tileBlit.src_area.y2 >= LV_GPU_NXP_VG_LITE_BLIT_SPLIT_THR) {
+ tileBlit.src_area.y2 = LV_GPU_NXP_VG_LITE_BLIT_SPLIT_THR - 1; /* Should never happen */
+ }
+ tileBlit.src = blit->src + tileY * LV_GPU_NXP_VG_LITE_BLIT_SPLIT_THR * blit->src_stride / sizeof(
+ lv_color_t); /* stride in px! */
+
+ tileBlit.dst_area.y1 = tileBlit.src_area.y1; /* y has no alignment, always in sync with src */
+ tileBlit.dst_area.y2 = tileBlit.src_area.y2;
+
+ tileBlit.dst = blit->dst + tileY * LV_GPU_NXP_VG_LITE_BLIT_SPLIT_THR * blit->dst_stride / sizeof(
+ lv_color_t); /* stride in px! */
+
+ for(int tileX = 0; tileX < totalTilesX; tileX++) {
+
+ if(tileX == 0) {
+ /* 1st tile is special - there may be a gap between buffer start pointer
+ * and area.x1 value, as the pointer has to be aligned.
+ * tileBlit.src pointer - keep init value from Y-loop.
+ * Also, 1st tile start is not shifted! shift is applied from 2nd tile */
+ tileBlit.src_area.x1 = blit->src_area.x1;
+ tileBlit.dst_area.x1 = blit->dst_area.x1;
+ }
+ else {
+ /* subsequent tiles always starts from 0, but shifted*/
+ tileBlit.src_area.x1 = 0 + shiftSrcX;
+ tileBlit.dst_area.x1 = 0 + shiftDstX;
+ /* and advance start pointer + 1 tile size */
+ tileBlit.src += LV_GPU_NXP_VG_LITE_BLIT_SPLIT_THR;
+ tileBlit.dst += LV_GPU_NXP_VG_LITE_BLIT_SPLIT_THR;
+ }
+
+ /* Clip tile end coordinates */
+ tileBlit.src_area.x2 = totalWidth + blit->src_area.x1 - tileX * LV_GPU_NXP_VG_LITE_BLIT_SPLIT_THR - 1;
+ if(tileBlit.src_area.x2 >= LV_GPU_NXP_VG_LITE_BLIT_SPLIT_THR) {
+ tileBlit.src_area.x2 = LV_GPU_NXP_VG_LITE_BLIT_SPLIT_THR - 1;
+ }
+
+ tileBlit.dst_area.x2 = totalWidth + blit->dst_area.x1 - tileX * LV_GPU_NXP_VG_LITE_BLIT_SPLIT_THR - 1;
+ if(tileBlit.dst_area.x2 >= LV_GPU_NXP_VG_LITE_BLIT_SPLIT_THR) {
+ tileBlit.dst_area.x2 = LV_GPU_NXP_VG_LITE_BLIT_SPLIT_THR - 1;
+ }
+
+ if(tileX < (totalTilesX - 1)) {
+ /* And adjust end coords if shifted, but not for last tile! */
+ tileBlit.src_area.x2 += shiftSrcX;
+ tileBlit.dst_area.x2 += shiftDstX;
+ }
+
+ rv = _lv_gpu_nxp_vglite_blit_single(&tileBlit);
+
+#if BLIT_DBG_AREAS
+ lv_vglite_dbg_draw_rectangle((lv_color_t *) tileBlit.dst, tileBlit.dst_width, tileBlit.dst_height, &tileBlit.dst_area,
+ LV_COLOR_RED);
+ lv_vglite_dbg_draw_rectangle((lv_color_t *) tileBlit.src, tileBlit.src_width, tileBlit.src_height, &tileBlit.src_area,
+ LV_COLOR_GREEN);
+#endif
+
+ PRINT_BLT("Tile [%d, %d]: "
+ "([%d,%d], [%d,%d]) -> "
+ "([%d,%d], [%d,%d]) | "
+ "([%dx%d] -> [%dx%d]) | "
+ "A:(0x%8X -> 0x%8X) %s\n",
+ tileX, tileY,
+ tileBlit.src_area.x1, tileBlit.src_area.y1, tileBlit.src_area.x2, tileBlit.src_area.y2,
+ tileBlit.dst_area.x1, tileBlit.dst_area.y1, tileBlit.dst_area.x2, tileBlit.dst_area.y2,
+ lv_area_get_width(&tileBlit.src_area), lv_area_get_height(&tileBlit.src_area),
+ lv_area_get_width(&tileBlit.dst_area), lv_area_get_height(&tileBlit.dst_area),
+ (uintptr_t) tileBlit.src, (uintptr_t) tileBlit.dst,
+ rv == LV_RES_OK ? "OK!" : "!!! FAILED !!!");
+
+ if(rv != LV_RES_OK) { /* if anything goes wrong... */
+#if LV_GPU_NXP_VG_LITE_LOG_ERRORS
+ LV_LOG_ERROR("Split blit failed. Trying SW blit instead.");
+#endif
+ _sw_blit(&tileBlit);
+ rv = LV_RES_OK; /* Don't report error, as SW BLIT was performed */
+ }
+
+ }
+ PRINT_BLT(" \n");
+ }
+
+ return rv; /* should never fail */
+}
+#endif /* VG_LITE_BLIT_SPLIT_ENABLED */
+
+static lv_res_t _lv_gpu_nxp_vglite_blit_single(lv_gpu_nxp_vglite_blit_info_t * blit)
+{
+ vg_lite_buffer_t src_vgbuf, dst_vgbuf;
+ vg_lite_error_t err = VG_LITE_SUCCESS;
+ uint32_t rect[4];
+ vg_lite_float_t scale = 1.0;
+
+ if(blit == NULL) {
+ /*Wrong parameter*/
+ return LV_RES_INV;
+ }
+
+ if(blit->opa < (lv_opa_t) LV_OPA_MIN) {
+ return LV_RES_OK; /*Nothing to BLIT*/
+ }
+
+ /*Wrap src/dst buffer into VG-Lite buffer*/
+ if(lv_vglite_init_buf(&src_vgbuf, (uint32_t)blit->src_width, (uint32_t)blit->src_height, (uint32_t)blit->src_stride,
+ blit->src, true) != LV_RES_OK)
+ VG_LITE_RETURN_INV("Init buffer failed.");
+
+ if(lv_vglite_init_buf(&dst_vgbuf, (uint32_t)blit->dst_width, (uint32_t)blit->dst_height, (uint32_t)blit->dst_stride,
+ blit->dst, false) != LV_RES_OK)
+ VG_LITE_RETURN_INV("Init buffer failed.");
+
+ rect[0] = (uint32_t)blit->src_area.x1; /* start x */
+ rect[1] = (uint32_t)blit->src_area.y1; /* start y */
+ rect[2] = (uint32_t)blit->src_area.x2 - (uint32_t)blit->src_area.x1 + 1U; /* width */
+ rect[3] = (uint32_t)blit->src_area.y2 - (uint32_t)blit->src_area.y1 + 1U; /* height */
+
+ vg_lite_matrix_t matrix;
+ vg_lite_identity(&matrix);
+ vg_lite_translate((vg_lite_float_t)blit->dst_area.x1, (vg_lite_float_t)blit->dst_area.y1, &matrix);
+
+ if((blit->angle != 0) || (blit->zoom != LV_IMG_ZOOM_NONE)) {
+ vg_lite_translate(blit->pivot.x, blit->pivot.y, &matrix);
+ vg_lite_rotate(blit->angle / 10.0f, &matrix); /* angle is 1/10 degree */
+ scale = 1.0f * blit->zoom / LV_IMG_ZOOM_NONE;
+ vg_lite_scale(scale, scale, &matrix);
+ vg_lite_translate(0.0f - blit->pivot.x, 0.0f - blit->pivot.y, &matrix);
+ }
+
+ /*Clean & invalidate cache*/
+ lv_vglite_invalidate_cache();
+
+ uint32_t color;
+ vg_lite_blend_t blend;
+ if(blit->opa >= (lv_opa_t)LV_OPA_MAX) {
+ color = 0xFFFFFFFFU;
+ blend = VG_LITE_BLEND_SRC_OVER;
+ src_vgbuf.transparency_mode = VG_LITE_IMAGE_TRANSPARENT;
+ }
+ else {
+ uint32_t opa = (uint32_t)blit->opa;
+ if(vg_lite_query_feature(gcFEATURE_BIT_VG_PE_PREMULTIPLY)) {
+ color = (opa << 24) | 0x00FFFFFFU;
+ }
+ else {
+ color = (opa << 24) | (opa << 16) | (opa << 8) | opa;
+ }
+ blend = VG_LITE_BLEND_SRC_OVER;
+ src_vgbuf.image_mode = VG_LITE_MULTIPLY_IMAGE_MODE;
+ src_vgbuf.transparency_mode = VG_LITE_IMAGE_TRANSPARENT;
+ }
+
+ err = vg_lite_blit_rect(&dst_vgbuf, &src_vgbuf, rect, &matrix, blend, color, VG_LITE_FILTER_POINT);
+ VG_LITE_ERR_RETURN_INV(err, "Blit rectangle failed.");
+
+ err = vg_lite_finish();
+ VG_LITE_ERR_RETURN_INV(err, "Finish failed.");
+
+ return LV_RES_OK;
+}
+
+#if VG_LITE_BLIT_SPLIT_ENABLED
+
+static void _sw_blit(lv_gpu_nxp_vglite_blit_info_t * blit)
+{
+ int x, y;
+
+ lv_coord_t w = lv_area_get_width(&blit->src_area);
+ lv_coord_t h = lv_area_get_height(&blit->src_area);
+
+ int32_t srcStridePx = blit->src_stride / (int32_t)sizeof(lv_color_t);
+ int32_t dstStridePx = blit->dst_stride / (int32_t)sizeof(lv_color_t);
+
+ lv_color_t * src = (lv_color_t *)blit->src + blit->src_area.y1 * srcStridePx + blit->src_area.x1;
+ lv_color_t * dst = (lv_color_t *)blit->dst + blit->dst_area.y1 * dstStridePx + blit->dst_area.x1;
+
+ if(blit->opa >= (lv_opa_t)LV_OPA_MAX) {
+ /* simple copy */
+ for(y = 0; y < h; y++) {
+ lv_memcpy(dst, src, (uint32_t)w * sizeof(lv_color_t));
+ src += srcStridePx;
+ dst += dstStridePx;
+ }
+ }
+ else if(blit->opa >= LV_OPA_MIN) {
+ /* alpha blending */
+ for(y = 0; y < h; y++) {
+ for(x = 0; x < w; x++) {
+ dst[x] = lv_color_mix(src[x], dst[x], blit->opa);
+ }
+ src += srcStridePx;
+ dst += dstStridePx;
+ }
+ }
+}
+
+static lv_res_t _lv_gpu_nxp_vglite_check_blit(lv_gpu_nxp_vglite_blit_info_t * blit)
+{
+
+ /* Test for minimal width */
+ if(lv_area_get_width(&blit->src_area) < (lv_coord_t)LV_GPU_NXP_VG_LITE_STRIDE_ALIGN_PX)
+ VG_LITE_RETURN_INV("Src area width (%d) is smaller than required (%d).", lv_area_get_width(&blit->src_area),
+ LV_GPU_NXP_VG_LITE_STRIDE_ALIGN_PX);
+
+ /* Test for minimal width */
+ if(lv_area_get_width(&blit->dst_area) < (lv_coord_t)LV_GPU_NXP_VG_LITE_STRIDE_ALIGN_PX)
+ VG_LITE_RETURN_INV("Dest area width (%d) is smaller than required (%d).", lv_area_get_width(&blit->dst_area),
+ LV_GPU_NXP_VG_LITE_STRIDE_ALIGN_PX);
+
+ /* Test for pointer alignment */
+ if((((uintptr_t) blit->src) % LV_ATTRIBUTE_MEM_ALIGN_SIZE) != 0x0)
+ VG_LITE_RETURN_INV("Src buffer ptr (0x%X) not aligned to %d.", (size_t) blit->src, LV_ATTRIBUTE_MEM_ALIGN_SIZE);
+
+ /* No alignment requirement for destination pixel buffer when using mode VG_LITE_LINEAR */
+
+ /* Test for stride alignment */
+ if((blit->src_stride % (LV_GPU_NXP_VG_LITE_STRIDE_ALIGN_PX * LV_COLOR_DEPTH / 8)) != 0x0)
+ VG_LITE_RETURN_INV("Src buffer stride (%d px) not aligned to %d px.", blit->src_stride,
+ LV_GPU_NXP_VG_LITE_STRIDE_ALIGN_PX);
+
+ /* Test for stride alignment */
+ if((blit->dst_stride % (LV_GPU_NXP_VG_LITE_STRIDE_ALIGN_PX * LV_COLOR_DEPTH / 8)) != 0x0)
+ VG_LITE_RETURN_INV("Dest buffer stride (%d px) not aligned to %d px.", blit->dst_stride,
+ LV_GPU_NXP_VG_LITE_STRIDE_ALIGN_PX);
+
+ if((lv_area_get_width(&blit->src_area) != lv_area_get_width(&blit->dst_area)) ||
+ (lv_area_get_height(&blit->src_area) != lv_area_get_height(&blit->dst_area)))
+ VG_LITE_RETURN_INV("Src and dest buffer areas are not equal.");
+
+ return LV_RES_OK;
+}
+
+static void _align_x(lv_area_t * area, lv_color_t ** buf)
+{
+
+ int alignedAreaStartPx = area->x1 - (area->x1 % (LV_ATTRIBUTE_MEM_ALIGN_SIZE * 8 / LV_COLOR_DEPTH));
+ VG_LITE_COND_STOP(alignedAreaStartPx < 0, "Negative X alignment.");
+
+ area->x1 -= alignedAreaStartPx;
+ area->x2 -= alignedAreaStartPx;
+ *buf += alignedAreaStartPx;
+}
+
+static void _align_y(lv_area_t * area, lv_color_t ** buf, uint32_t stridePx)
+{
+ int LineToAlignMem;
+ int alignedAreaStartPy;
+ /* find how many lines of pixels will respect memory alignment requirement */
+ if(stridePx % (uint32_t)LV_ATTRIBUTE_MEM_ALIGN_SIZE == 0U) {
+ alignedAreaStartPy = area->y1;
+ }
+ else {
+ LineToAlignMem = LV_ATTRIBUTE_MEM_ALIGN_SIZE / (sizeof(lv_color_t) * LV_GPU_NXP_VG_LITE_STRIDE_ALIGN_PX);
+ VG_LITE_COND_STOP(LV_ATTRIBUTE_MEM_ALIGN_SIZE % (sizeof(lv_color_t) * LV_GPU_NXP_VG_LITE_STRIDE_ALIGN_PX),
+ "Complex case: need gcd function.");
+ alignedAreaStartPy = area->y1 - (area->y1 % LineToAlignMem);
+ VG_LITE_COND_STOP(alignedAreaStartPy < 0, "Negative Y alignment.");
+ }
+
+ area->y1 -= alignedAreaStartPy;
+ area->y2 -= alignedAreaStartPy;
+ *buf += (uint32_t)alignedAreaStartPy * stridePx;
+}
+#endif /*VG_LITE_BLIT_SPLIT_ENABLED*/
+
+#endif /*LV_USE_GPU_NXP_VG_LITE*/
diff --git a/lib/lvgl/src/draw/nxp/vglite/lv_draw_vglite_blend.h b/lib/lvgl/src/draw/nxp/vglite/lv_draw_vglite_blend.h
new file mode 100644
index 00000000..bc448c65
--- /dev/null
+++ b/lib/lvgl/src/draw/nxp/vglite/lv_draw_vglite_blend.h
@@ -0,0 +1,149 @@
+/**
+ * @file lv_draw_vglite_blend.h
+ *
+ */
+
+/**
+ * MIT License
+ *
+ * Copyright 2020-2022 NXP
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+ * the Software, and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next paragraph)
+ * shall be included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
+ * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
+ * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+#ifndef LV_DRAW_VGLITE_BLEND_H
+#define LV_DRAW_VGLITE_BLEND_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*********************
+ * INCLUDES
+ *********************/
+
+#include "../../../lv_conf_internal.h"
+
+#if LV_USE_GPU_NXP_VG_LITE
+#include "lv_gpu_nxp_vglite.h"
+
+/*********************
+ * DEFINES
+ *********************/
+
+#ifndef LV_GPU_NXP_VG_LITE_FILL_SIZE_LIMIT
+/** Minimum area (in pixels) to be filled by VG-Lite with 100% opacity*/
+#define LV_GPU_NXP_VG_LITE_FILL_SIZE_LIMIT 5000
+#endif
+
+#ifndef LV_GPU_NXP_VG_LITE_FILL_OPA_SIZE_LIMIT
+/** Minimum area (in pixels) to be filled by VG-Lite with transparency*/
+#define LV_GPU_NXP_VG_LITE_FILL_OPA_SIZE_LIMIT 5000
+#endif
+
+#ifndef LV_GPU_NXP_VG_LITE_BLIT_SIZE_LIMIT
+/** Minimum area (in pixels) for image copy with 100% opacity to be handled by VG-Lite*/
+#define LV_GPU_NXP_VG_LITE_BLIT_SIZE_LIMIT 5000
+#endif
+
+#ifndef LV_GPU_NXP_VG_LITE_BUFF_SYNC_BLIT_SIZE_LIMIT
+/** Minimum invalidated area (in pixels) to be synchronized by VG-Lite during buffer sync */
+#define LV_GPU_NXP_VG_LITE_BUFF_SYNC_BLIT_SIZE_LIMIT 5000
+#endif
+
+#ifndef LV_GPU_NXP_VG_LITE_BLIT_OPA_SIZE_LIMIT
+/** Minimum area (in pixels) for image copy with transparency to be handled by VG-Lite*/
+#define LV_GPU_NXP_VG_LITE_BLIT_OPA_SIZE_LIMIT 5000
+#endif
+
+/**********************
+ * TYPEDEFS
+ **********************/
+
+/**
+ * BLock Image Transfer descriptor structure
+ */
+typedef struct {
+
+ const lv_color_t * src; /**< Source buffer pointer (must be aligned on 32 bytes)*/
+ lv_area_t src_area; /**< Area to be copied from source*/
+ lv_coord_t src_width; /**< Source buffer width*/
+ lv_coord_t src_height; /**< Source buffer height*/
+ int32_t src_stride; /**< Source buffer stride in bytes (must be aligned on 16 px)*/
+
+ const lv_color_t * dst; /**< Destination buffer pointer (must be aligned on 32 bytes)*/
+ lv_area_t dst_area; /**< Target area in destination buffer (must be the same as src_area)*/
+ lv_coord_t dst_width; /**< Destination buffer width*/
+ lv_coord_t dst_height; /**< Destination buffer height*/
+ int32_t dst_stride; /**< Destination buffer stride in bytes (must be aligned on 16 px)*/
+
+ lv_opa_t opa; /**< Opacity - alpha mix (0 = source not copied, 255 = 100% opaque)*/
+ uint32_t angle; /**< Rotation angle (1/10 of degree)*/
+ uint32_t zoom; /**< 256 = no zoom (1:1 scale ratio)*/
+ lv_point_t pivot; /**< The coordinates of rotation pivot in source image buffer*/
+} lv_gpu_nxp_vglite_blit_info_t;
+
+/**********************
+ * GLOBAL PROTOTYPES
+ **********************/
+
+/**
+ * Fill area, with optional opacity.
+ *
+ * @param[in/out] dest_buf Destination buffer pointer (must be aligned on 32 bytes)
+ * @param[in] dest_width Destination buffer width in pixels (must be aligned on 16 px)
+ * @param[in] dest_height Destination buffer height in pixels
+ * @param[in] fill_area Area to be filled
+ * @param[in] color Fill color
+ * @param[in] opa Opacity (255 = full, 128 = 50% background/50% color, 0 = no fill)
+ * @retval LV_RES_OK Fill completed
+ * @retval LV_RES_INV Error occurred (\see LV_GPU_NXP_VG_LITE_LOG_ERRORS)
+ */
+lv_res_t lv_gpu_nxp_vglite_fill(lv_color_t * dest_buf, lv_coord_t dest_width, lv_coord_t dest_height,
+ const lv_area_t * fill_area, lv_color_t color, lv_opa_t opa);
+
+/**
+ * BLock Image Transfer.
+ *
+ * @param[in] blit Description of the transfer
+ * @retval LV_RES_OK Transfer complete
+ * @retval LV_RES_INV Error occurred (\see LV_GPU_NXP_VG_LITE_LOG_ERRORS)
+ */
+lv_res_t lv_gpu_nxp_vglite_blit(lv_gpu_nxp_vglite_blit_info_t * blit);
+
+/**
+ * BLock Image Transfer with transformation.
+ *
+ * @param[in] blit Description of the transfer
+ * @retval LV_RES_OK Transfer complete
+ * @retval LV_RES_INV Error occurred (\see LV_GPU_NXP_VG_LITE_LOG_ERRORS)
+ */
+lv_res_t lv_gpu_nxp_vglite_blit_transform(lv_gpu_nxp_vglite_blit_info_t * blit);
+
+/**********************
+ * MACROS
+ **********************/
+
+#endif /*LV_USE_GPU_NXP_VG_LITE*/
+
+#ifdef __cplusplus
+} /*extern "C"*/
+#endif
+
+#endif /*LV_DRAW_VGLITE_BLEND_H*/
diff --git a/lib/lvgl/src/draw/nxp/vglite/lv_draw_vglite_rect.c b/lib/lvgl/src/draw/nxp/vglite/lv_draw_vglite_rect.c
new file mode 100644
index 00000000..bc1d55c8
--- /dev/null
+++ b/lib/lvgl/src/draw/nxp/vglite/lv_draw_vglite_rect.c
@@ -0,0 +1,244 @@
+/**
+ * @file lv_draw_vglite_rect.c
+ *
+ */
+
+/**
+ * MIT License
+ *
+ * Copyright 2021, 2022 NXP
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+ * the Software, and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next paragraph)
+ * shall be included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
+ * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
+ * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+/*********************
+ * INCLUDES
+ *********************/
+
+#include "lv_draw_vglite_rect.h"
+
+#if LV_USE_GPU_NXP_VG_LITE
+
+/*********************
+ * DEFINES
+ *********************/
+
+/**********************
+ * TYPEDEFS
+ **********************/
+
+/**********************
+ * STATIC PROTOTYPES
+ **********************/
+
+/**********************
+ * STATIC VARIABLES
+ **********************/
+
+/**********************
+ * MACROS
+ **********************/
+
+/**********************
+ * GLOBAL FUNCTIONS
+ **********************/
+
+lv_res_t lv_gpu_nxp_vglite_draw_bg(lv_draw_ctx_t * draw_ctx, const lv_draw_rect_dsc_t * dsc, const lv_area_t * coords)
+{
+ vg_lite_buffer_t vgbuf;
+ vg_lite_error_t err = VG_LITE_SUCCESS;
+ lv_coord_t dest_width = lv_area_get_width(draw_ctx->buf_area);
+ lv_coord_t dest_height = lv_area_get_height(draw_ctx->buf_area);
+ vg_lite_path_t path;
+ vg_lite_color_t vgcol; /* vglite takes ABGR */
+ vg_lite_matrix_t matrix;
+ lv_coord_t width = lv_area_get_width(coords);
+ lv_coord_t height = lv_area_get_height(coords);
+ vg_lite_linear_gradient_t gradient;
+ vg_lite_matrix_t * grad_matrix;
+
+ if(dsc->radius < 0)
+ return LV_RES_INV;
+
+ /* Make areas relative to draw buffer */
+ lv_area_t rel_coords;
+ lv_area_copy(&rel_coords, coords);
+ lv_area_move(&rel_coords, -draw_ctx->buf_area->x1, -draw_ctx->buf_area->y1);
+
+ lv_area_t rel_clip;
+ lv_area_copy(&rel_clip, draw_ctx->clip_area);
+ lv_area_move(&rel_clip, -draw_ctx->buf_area->x1, -draw_ctx->buf_area->y1);
+
+ /*** Init destination buffer ***/
+ if(lv_vglite_init_buf(&vgbuf, (uint32_t)dest_width, (uint32_t)dest_height, (uint32_t)dest_width * sizeof(lv_color_t),
+ (const lv_color_t *)draw_ctx->buf, false) != LV_RES_OK)
+ VG_LITE_RETURN_INV("Init buffer failed.");
+
+ /*** Init path ***/
+ int32_t rad = dsc->radius;
+ if(dsc->radius == LV_RADIUS_CIRCLE) {
+ rad = (width > height) ? height / 2 : width / 2;
+ }
+
+ if((dsc->radius == LV_RADIUS_CIRCLE) && (width == height)) {
+ float tang = ((float)rad * BEZIER_OPTIM_CIRCLE);
+ int32_t cpoff = (int32_t)tang;
+ int32_t circle_path[] = { /*VG circle path*/
+ VLC_OP_MOVE, rel_coords.x1 + rad, rel_coords.y1,
+ VLC_OP_CUBIC_REL, cpoff, 0, rad, rad - cpoff, rad, rad, /* top-right */
+ VLC_OP_CUBIC_REL, 0, cpoff, cpoff - rad, rad, 0 - rad, rad, /* bottom-right */
+ VLC_OP_CUBIC_REL, 0 - cpoff, 0, 0 - rad, cpoff - rad, 0 - rad, 0 - rad, /* bottom-left */
+ VLC_OP_CUBIC_REL, 0, 0 - cpoff, rad - cpoff, 0 - rad, rad, 0 - rad, /* top-left */
+ VLC_OP_END
+ };
+ err = vg_lite_init_path(&path, VG_LITE_S32, VG_LITE_HIGH, sizeof(circle_path), circle_path,
+ (vg_lite_float_t) rel_clip.x1, (vg_lite_float_t) rel_clip.y1,
+ ((vg_lite_float_t) rel_clip.x2) + 1.0f, ((vg_lite_float_t) rel_clip.y2) + 1.0f);
+ }
+ else if(dsc->radius > 0) {
+ float tang = ((float)rad * BEZIER_OPTIM_CIRCLE);
+ int32_t cpoff = (int32_t)tang;
+ int32_t rounded_path[] = { /*VG rounded rectangular path*/
+ VLC_OP_MOVE, rel_coords.x1 + rad, rel_coords.y1,
+ VLC_OP_LINE, rel_coords.x2 - rad + 1, rel_coords.y1, /* top */
+ VLC_OP_CUBIC_REL, cpoff, 0, rad, rad - cpoff, rad, rad, /* top-right */
+ VLC_OP_LINE, rel_coords.x2 + 1, rel_coords.y2 - rad + 1, /* right */
+ VLC_OP_CUBIC_REL, 0, cpoff, cpoff - rad, rad, 0 - rad, rad, /* bottom-right */
+ VLC_OP_LINE, rel_coords.x1 + rad, rel_coords.y2 + 1, /* bottom */
+ VLC_OP_CUBIC_REL, 0 - cpoff, 0, 0 - rad, cpoff - rad, 0 - rad, 0 - rad, /* bottom-left */
+ VLC_OP_LINE, rel_coords.x1, rel_coords.y1 + rad, /* left */
+ VLC_OP_CUBIC_REL, 0, 0 - cpoff, rad - cpoff, 0 - rad, rad, 0 - rad, /* top-left */
+ VLC_OP_END
+ };
+ err = vg_lite_init_path(&path, VG_LITE_S32, VG_LITE_HIGH, sizeof(rounded_path), rounded_path,
+ (vg_lite_float_t) rel_clip.x1, (vg_lite_float_t) rel_clip.y1,
+ ((vg_lite_float_t) rel_clip.x2) + 1.0f, ((vg_lite_float_t) rel_clip.y2) + 1.0f);
+ }
+ else {
+ int32_t rect_path[] = { /*VG rectangular path*/
+ VLC_OP_MOVE, rel_coords.x1, rel_coords.y1,
+ VLC_OP_LINE, rel_coords.x2 + 1, rel_coords.y1,
+ VLC_OP_LINE, rel_coords.x2 + 1, rel_coords.y2 + 1,
+ VLC_OP_LINE, rel_coords.x1, rel_coords.y2 + 1,
+ VLC_OP_LINE, rel_coords.x1, rel_coords.y1,
+ VLC_OP_END
+ };
+ err = vg_lite_init_path(&path, VG_LITE_S32, VG_LITE_LOW, sizeof(rect_path), rect_path,
+ (vg_lite_float_t) rel_clip.x1, (vg_lite_float_t) rel_clip.y1,
+ ((vg_lite_float_t) rel_clip.x2) + 1.0f, ((vg_lite_float_t) rel_clip.y2) + 1.0f);
+ }
+
+ VG_LITE_ERR_RETURN_INV(err, "Init path failed.");
+ vg_lite_identity(&matrix);
+
+ /*** Init Color/Gradient ***/
+ if(dsc->bg_grad.dir != (lv_grad_dir_t)LV_GRAD_DIR_NONE) {
+ uint32_t colors[2];
+ uint32_t stops[2];
+ lv_color32_t col32[2];
+
+ /* Gradient setup */
+ uint8_t cnt = MAX(dsc->bg_grad.stops_count, 2);
+ for(uint8_t i = 0; i < cnt; i++) {
+ col32[i].full = lv_color_to32(dsc->bg_grad.stops[i].color); /*Convert color to RGBA8888*/
+ stops[i] = dsc->bg_grad.stops[i].frac;
+#if LV_COLOR_DEPTH==16
+ colors[i] = ((uint32_t)col32[i].ch.alpha << 24) | ((uint32_t)col32[i].ch.blue << 16) |
+ ((uint32_t)col32[i].ch.green << 8) | (uint32_t)col32[i].ch.red;
+#else /*LV_COLOR_DEPTH==32*/
+ /* watchout: red and blue color components are inverted versus vg_lite_color_t order */
+ colors[i] = ((uint32_t)col32[i].ch.alpha << 24) | ((uint32_t)col32[i].ch.red << 16) |
+ ((uint32_t)col32[i].ch.green << 8) | (uint32_t)col32[i].ch.blue;
+#endif
+ }
+
+ lv_memset_00(&gradient, sizeof(vg_lite_linear_gradient_t));
+
+ err = vg_lite_init_grad(&gradient);
+ VG_LITE_ERR_RETURN_INV(err, "Init gradient failed");
+
+ err = vg_lite_set_grad(&gradient, cnt, colors, stops);
+ VG_LITE_ERR_RETURN_INV(err, "Set gradient failed.");
+
+ err = vg_lite_update_grad(&gradient);
+ VG_LITE_ERR_RETURN_INV(err, "Update gradient failed.");
+
+ grad_matrix = vg_lite_get_grad_matrix(&gradient);
+ vg_lite_identity(grad_matrix);
+ vg_lite_translate((float)rel_coords.x1, (float)rel_coords.y1, grad_matrix);
+
+ if(dsc->bg_grad.dir == (lv_grad_dir_t)LV_GRAD_DIR_VER) {
+ vg_lite_scale(1.0f, (float)height / 256.0f, grad_matrix);
+ vg_lite_rotate(90.0f, grad_matrix);
+ }
+ else { /*LV_GRAD_DIR_HOR*/
+ vg_lite_scale((float)width / 256.0f, 1.0f, grad_matrix);
+ }
+ }
+
+ lv_opa_t bg_opa = dsc->bg_opa;
+ lv_color32_t bg_col32 = {.full = lv_color_to32(dsc->bg_color)}; /*Convert color to RGBA8888*/
+ if(bg_opa <= (lv_opa_t)LV_OPA_MAX) {
+ /* Only pre-multiply color if hardware pre-multiplication is not present */
+ if(!vg_lite_query_feature(gcFEATURE_BIT_VG_PE_PREMULTIPLY)) {
+ bg_col32.ch.red = (uint8_t)(((uint16_t)bg_col32.ch.red * bg_opa) >> 8);
+ bg_col32.ch.green = (uint8_t)(((uint16_t)bg_col32.ch.green * bg_opa) >> 8);
+ bg_col32.ch.blue = (uint8_t)(((uint16_t)bg_col32.ch.blue * bg_opa) >> 8);
+ }
+ bg_col32.ch.alpha = bg_opa;
+ }
+
+#if LV_COLOR_DEPTH==16
+ vgcol = bg_col32.full;
+#else /*LV_COLOR_DEPTH==32*/
+ vgcol = ((uint32_t)bg_col32.ch.alpha << 24) | ((uint32_t)bg_col32.ch.blue << 16) |
+ ((uint32_t)bg_col32.ch.green << 8) | (uint32_t)bg_col32.ch.red;
+#endif
+
+ /*Clean & invalidate cache*/
+ lv_vglite_invalidate_cache();
+
+ /*** Draw rectangle ***/
+ if(dsc->bg_grad.dir == (lv_grad_dir_t)LV_GRAD_DIR_NONE) {
+ err = vg_lite_draw(&vgbuf, &path, VG_LITE_FILL_EVEN_ODD, &matrix, VG_LITE_BLEND_SRC_OVER, vgcol);
+ }
+ else {
+ err = vg_lite_draw_gradient(&vgbuf, &path, VG_LITE_FILL_EVEN_ODD, &matrix, &gradient, VG_LITE_BLEND_SRC_OVER);
+ }
+ VG_LITE_ERR_RETURN_INV(err, "Draw gradient failed.");
+
+ err = vg_lite_finish();
+ VG_LITE_ERR_RETURN_INV(err, "Finish failed.");
+
+ err = vg_lite_clear_path(&path);
+ VG_LITE_ERR_RETURN_INV(err, "Clear path failed.");
+
+ if(dsc->bg_grad.dir != (lv_grad_dir_t)LV_GRAD_DIR_NONE) {
+ err = vg_lite_clear_grad(&gradient);
+ VG_LITE_ERR_RETURN_INV(err, "Clear gradient failed.");
+ }
+
+ return LV_RES_OK;
+}
+
+/**********************
+ * STATIC FUNCTIONS
+ **********************/
+
+#endif /*LV_USE_GPU_NXP_VG_LITE*/
diff --git a/lib/lvgl/src/draw/nxp/vglite/lv_draw_vglite_rect.h b/lib/lvgl/src/draw/nxp/vglite/lv_draw_vglite_rect.h
new file mode 100644
index 00000000..d708e7b4
--- /dev/null
+++ b/lib/lvgl/src/draw/nxp/vglite/lv_draw_vglite_rect.h
@@ -0,0 +1,77 @@
+/**
+ * @file lv_draw_vglite_rect.h
+ *
+ */
+
+/**
+ * MIT License
+ *
+ * Copyright 2021, 2022 NXP
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+ * the Software, and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next paragraph)
+ * shall be included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
+ * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
+ * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+#ifndef LV_DRAW_VGLITE_RECT_H
+#define LV_DRAW_VGLITE_RECT_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*********************
+ * INCLUDES
+ *********************/
+#include "../../../lv_conf_internal.h"
+
+#if LV_USE_GPU_NXP_VG_LITE
+#include "lv_gpu_nxp_vglite.h"
+#include "../../lv_draw_rect.h"
+
+/*********************
+ * DEFINES
+ *********************/
+
+/**********************
+ * TYPEDEFS
+ **********************/
+
+/**********************
+ * GLOBAL PROTOTYPES
+ **********************/
+
+/**
+ * Draw rectangle shape with effects (rounded corners, gradient)
+ *
+ * @param draw_ctx drawing context
+ * @param dsc description of the rectangle
+ * @param coords the area where rectangle is clipped
+ */
+lv_res_t lv_gpu_nxp_vglite_draw_bg(lv_draw_ctx_t * draw_ctx, const lv_draw_rect_dsc_t * dsc, const lv_area_t * coords);
+
+/**********************
+ * MACROS
+ **********************/
+
+#endif /*LV_USE_GPU_NXP_VG_LITE*/
+
+#ifdef __cplusplus
+} /*extern "C"*/
+#endif
+
+#endif /*LV_DRAW_VGLITE_RECT_H*/
diff --git a/lib/lvgl/src/draw/nxp/vglite/lv_gpu_nxp_vglite.c b/lib/lvgl/src/draw/nxp/vglite/lv_gpu_nxp_vglite.c
new file mode 100644
index 00000000..f65ec1d4
--- /dev/null
+++ b/lib/lvgl/src/draw/nxp/vglite/lv_gpu_nxp_vglite.c
@@ -0,0 +1,153 @@
+/**
+ * @file lv_gpu_nxp_vglite.c
+ *
+ */
+
+/**
+ * MIT License
+ *
+ * Copyright 2020-2022 NXP
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+ * the Software, and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next paragraph)
+ * shall be included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
+ * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
+ * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+/*********************
+ * INCLUDES
+ *********************/
+
+#include "lv_gpu_nxp_vglite.h"
+
+#if LV_USE_GPU_NXP_VG_LITE
+#include "../../../core/lv_refr.h"
+#if BLIT_DBG_AREAS
+ #include "lv_draw_vglite_blend.h"
+#endif
+
+/*********************
+ * DEFINES
+ *********************/
+
+#if LV_COLOR_DEPTH==16
+ #define VG_LITE_PX_FMT VG_LITE_RGB565
+#elif LV_COLOR_DEPTH==32
+ #define VG_LITE_PX_FMT VG_LITE_BGRA8888
+#else
+ #error Only 16bit and 32bit color depth are supported. Set LV_COLOR_DEPTH to 16 or 32.
+#endif
+
+/**********************
+ * TYPEDEFS
+ **********************/
+
+/**********************
+ * STATIC PROTOTYPES
+ **********************/
+
+/**********************
+ * STATIC VARIABLES
+ **********************/
+
+/**********************
+ * MACROS
+ **********************/
+
+/**********************
+ * GLOBAL FUNCTIONS
+ **********************/
+
+lv_res_t lv_vglite_init_buf(vg_lite_buffer_t * vgbuf, uint32_t width, uint32_t height, uint32_t stride,
+ const lv_color_t * ptr, bool source)
+{
+ /*Test for memory alignment*/
+ if((((uintptr_t)ptr) % (uintptr_t)LV_ATTRIBUTE_MEM_ALIGN_SIZE) != (uintptr_t)0x0U)
+ VG_LITE_RETURN_INV("%s buffer (0x%x) not aligned to %d.", source ? "Src" : "Dest",
+ (size_t) ptr, LV_ATTRIBUTE_MEM_ALIGN_SIZE);
+
+ /*Test for stride alignment*/
+ if(source && (stride % (LV_GPU_NXP_VG_LITE_STRIDE_ALIGN_PX * sizeof(lv_color_t))) != 0x0U)
+ VG_LITE_RETURN_INV("Src buffer stride (%d bytes) not aligned to %d bytes.", stride,
+ LV_GPU_NXP_VG_LITE_STRIDE_ALIGN_PX * sizeof(lv_color_t));
+
+ vgbuf->format = VG_LITE_PX_FMT;
+ vgbuf->tiled = VG_LITE_LINEAR;
+ vgbuf->image_mode = VG_LITE_NORMAL_IMAGE_MODE;
+ vgbuf->transparency_mode = VG_LITE_IMAGE_OPAQUE;
+
+ vgbuf->width = (int32_t)width;
+ vgbuf->height = (int32_t)height;
+ vgbuf->stride = (int32_t)stride;
+
+ lv_memset_00(&vgbuf->yuv, sizeof(vgbuf->yuv));
+
+ vgbuf->memory = (void *)ptr;
+ vgbuf->address = (uint32_t)vgbuf->memory;
+ vgbuf->handle = NULL;
+
+ return LV_RES_OK;
+}
+
+#if BLIT_DBG_AREAS
+void lv_vglite_dbg_draw_rectangle(lv_color_t * dest_buf, lv_coord_t dest_width, lv_coord_t dest_height,
+ lv_area_t * fill_area, lv_color_t color)
+{
+ lv_area_t a;
+
+ /* top line */
+ a.x1 = fill_area->x1;
+ a.x2 = fill_area->x2;
+ a.y1 = fill_area->y1;
+ a.y2 = fill_area->y1;
+ lv_gpu_nxp_vglite_fill(dest_buf, dest_width, dest_height, &a, color, LV_OPA_COVER);
+
+
+ /* bottom line */
+ a.x1 = fill_area->x1;
+ a.x2 = fill_area->x2;
+ a.y1 = fill_area->y2;
+ a.y2 = fill_area->y2;
+ lv_gpu_nxp_vglite_fill(dest_buf, dest_width, dest_height, &a, color, LV_OPA_COVER);
+
+ /* left line */
+ a.x1 = fill_area->x1;
+ a.x2 = fill_area->x1;
+ a.y1 = fill_area->y1;
+ a.y2 = fill_area->y2;
+ lv_gpu_nxp_vglite_fill(dest_buf, dest_width, dest_height, &a, color, LV_OPA_COVER);
+
+ /* right line */
+ a.x1 = fill_area->x2;
+ a.x2 = fill_area->x2;
+ a.y1 = fill_area->y1;
+ a.y2 = fill_area->y2;
+ lv_gpu_nxp_vglite_fill(dest_buf, dest_width, dest_height, &a, color, LV_OPA_COVER);
+}
+#endif /* BLIT_DBG_AREAS */
+
+void lv_vglite_invalidate_cache(void)
+{
+ lv_disp_t * disp = _lv_refr_get_disp_refreshing();
+ if(disp->driver->clean_dcache_cb)
+ disp->driver->clean_dcache_cb(disp->driver);
+}
+
+/**********************
+ * STATIC FUNCTIONS
+ **********************/
+
+#endif /*LV_USE_GPU_NXP_VG_LITE*/
diff --git a/lib/lvgl/src/draw/nxp/vglite/lv_gpu_nxp_vglite.h b/lib/lvgl/src/draw/nxp/vglite/lv_gpu_nxp_vglite.h
new file mode 100644
index 00000000..c22cae18
--- /dev/null
+++ b/lib/lvgl/src/draw/nxp/vglite/lv_gpu_nxp_vglite.h
@@ -0,0 +1,185 @@
+/**
+ * @file lv_gpu_nxp_vglite.h
+ *
+ */
+
+/**
+ * MIT License
+ *
+ * Copyright 2020-2022 NXP
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+ * the Software, and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next paragraph)
+ * shall be included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
+ * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
+ * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+#ifndef LV_GPU_NXP_VGLITE_H
+#define LV_GPU_NXP_VGLITE_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*********************
+ * INCLUDES
+ *********************/
+#include "../../../lv_conf_internal.h"
+
+#if LV_USE_GPU_NXP_VG_LITE
+#include "vg_lite.h"
+#include "../../sw/lv_draw_sw.h"
+#include "../../../misc/lv_log.h"
+#include "fsl_debug_console.h"
+
+/*********************
+ * DEFINES
+ *********************/
+
+/** Use this symbol as limit to disable feature (value has to be larger than supported resolution) */
+#define LV_GPU_NXP_VG_LITE_FEATURE_DISABLED (1920*1080+1)
+
+/** Stride in px required by VG-Lite HW. Don't change this. */
+#define LV_GPU_NXP_VG_LITE_STRIDE_ALIGN_PX 16U
+
+#ifndef LV_GPU_NXP_VG_LITE_LOG_ERRORS
+/** Enable logging of VG-Lite errors (\see LV_LOG_ERROR)*/
+#define LV_GPU_NXP_VG_LITE_LOG_ERRORS 1
+#endif
+
+#ifndef LV_GPU_NXP_VG_LITE_LOG_TRACES
+/** Enable logging of VG-Lite errors (\see LV_LOG_ERROR)*/
+#define LV_GPU_NXP_VG_LITE_LOG_TRACES 0
+#endif
+
+/* Draw rectangles around BLIT tiles */
+#define BLIT_DBG_AREAS 0
+
+/* Print detailed info to SDK console (NOT to LVGL log system) */
+#define BLIT_DBG_VERBOSE 0
+
+/* Verbose debug print */
+#if BLIT_DBG_VERBOSE
+#define PRINT_BLT PRINTF
+#else
+#define PRINT_BLT(...)
+#endif
+
+/* The optimal Bezier control point offset for radial unit
+ * see: https://spencermortensen.com/articles/bezier-circle/
+ **/
+#define BEZIER_OPTIM_CIRCLE 0.551915024494f
+
+/* Draw lines for control points of Bezier curves */
+#define BEZIER_DBG_CONTROL_POINTS 0
+
+/**********************
+ * TYPEDEFS
+ **********************/
+
+/**********************
+ * GLOBAL PROTOTYPES
+ **********************/
+
+/**
+ * Fills vg_lite_buffer_t structure according given parameters.
+ *
+ * @param[in/out] vgbuf Buffer structure to be filled
+ * @param[in] width Width of buffer in pixels
+ * @param[in] height Height of buffer in pixels
+ * @param[in] stride Stride of the buffer in bytes
+ * @param[in] ptr Pointer to the buffer (must be aligned according VG-Lite requirements)
+ * @param[in] source Boolean to check if this is a source buffer
+ */
+lv_res_t lv_vglite_init_buf(vg_lite_buffer_t * vgbuf, uint32_t width, uint32_t height, uint32_t stride,
+ const lv_color_t * ptr, bool source);
+
+#if BLIT_DBG_AREAS
+/**
+ * Draw a simple rectangle, 1 px line width.
+ *
+ * @param dest_buf Destination buffer
+ * @param dest_width Destination buffer width (must be aligned on 16px)
+ * @param dest_height Destination buffer height
+ * @param fill_area Rectangle coordinates
+ * @param color Rectangle color
+ */
+void lv_vglite_dbg_draw_rectangle(lv_color_t * dest_buf, lv_coord_t dest_width, lv_coord_t dest_height,
+ lv_area_t * fill_area, lv_color_t color);
+#endif
+
+/**
+ * Clean & invalidate cache.
+ */
+void lv_vglite_invalidate_cache(void);
+
+/**********************
+ * MACROS
+ **********************/
+
+#define VG_LITE_COND_STOP(cond, txt) \
+ do { \
+ if (cond) { \
+ LV_LOG_ERROR("%s. STOP!", txt); \
+ for ( ; ; ); \
+ } \
+ } while(0)
+
+#if LV_GPU_NXP_VG_LITE_LOG_ERRORS
+#define VG_LITE_ERR_RETURN_INV(err, fmt, ...) \
+ do { \
+ if(err != VG_LITE_SUCCESS) { \
+ LV_LOG_ERROR(fmt, ##__VA_ARGS__); \
+ return LV_RES_INV; \
+ } \
+ } while (0)
+#else
+#define VG_LITE_ERR_RETURN_INV(err, fmt, ...) \
+ do { \
+ if(err != VG_LITE_SUCCESS) { \
+ return LV_RES_INV; \
+ } \
+ }while(0)
+#endif /*LV_GPU_NXP_VG_LITE_LOG_ERRORS*/
+
+#if LV_GPU_NXP_VG_LITE_LOG_TRACES
+#define VG_LITE_LOG_TRACE(fmt, ...) \
+ do { \
+ LV_LOG_ERROR(fmt, ##__VA_ARGS__); \
+ } while (0)
+
+#define VG_LITE_RETURN_INV(fmt, ...) \
+ do { \
+ LV_LOG_ERROR(fmt, ##__VA_ARGS__); \
+ return LV_RES_INV; \
+ } while (0)
+#else
+#define VG_LITE_LOG_TRACE(fmt, ...) \
+ do { \
+ } while (0)
+#define VG_LITE_RETURN_INV(fmt, ...) \
+ do { \
+ return LV_RES_INV; \
+ }while(0)
+#endif /*LV_GPU_NXP_VG_LITE_LOG_TRACES*/
+
+#endif /*LV_USE_GPU_NXP_VG_LITE*/
+
+#ifdef __cplusplus
+} /*extern "C"*/
+#endif
+
+#endif /*LV_GPU_NXP_VGLITE_H*/
diff --git a/lib/lvgl/src/draw/sdl/README.md b/lib/lvgl/src/draw/sdl/README.md
new file mode 100644
index 00000000..4415ffac
--- /dev/null
+++ b/lib/lvgl/src/draw/sdl/README.md
@@ -0,0 +1,28 @@
+# SDL_Renderer Based Drawing Functions
+
+In LVGL, drawing was performed by CPU. To improve drawing performance on platforms with GPU,
+we should perform drawing operations on GPU if possible.
+
+This implementation has moved most bitmap blending and drawing procedures to utilize SDL_Renderer,
+which takes advantages of hardware acceleration APIs like DirectX or OpenGL.
+
+This implementation can be also considered as a reference implementation, for contributors wants to
+develop accelerated drawing functions with other APIs such as OpenGL/OpenGL ES.
+
+## Caveats
+`lv_draw_arc`, `lv_draw_line` is not enabled, due to incomplete implementation. So lines and arcs will
+have significant impact to drawing performances.
+
+Performance of this implementation still has room to improve. Or we should use more powerful APIs
+such as OpenGL.
+
+## Notices for files
+
+### `lv_draw_sdl_stack_blur.c`
+
+Contains modified code from [android-stackblur](https://github.com/kikoso/android-stackblur) project.
+Apache License 2.0
+
+### `lv_draw_sdl_lru.c`/`lv_draw_sdl_lru.h`
+
+Contains modified code from [C-LRU-Cache](https://github.com/willcannings/C-LRU-Cache) project. No license defined. \ No newline at end of file
diff --git a/lib/lvgl/src/draw/sdl/lv_draw_sdl.c b/lib/lvgl/src/draw/sdl/lv_draw_sdl.c
new file mode 100644
index 00000000..e3cdf577
--- /dev/null
+++ b/lib/lvgl/src/draw/sdl/lv_draw_sdl.c
@@ -0,0 +1,103 @@
+/**
+ * @file lv_draw_sdl.c
+ *
+ */
+
+/*********************
+ * INCLUDES
+ *********************/
+
+
+#include "../../lv_conf_internal.h"
+
+#if LV_USE_GPU_SDL
+
+#include "lv_draw_sdl.h"
+#include "lv_draw_sdl_utils.h"
+#include "lv_draw_sdl_texture_cache.h"
+#include "lv_draw_sdl_layer.h"
+
+/*********************
+ * DEFINES
+ *********************/
+void lv_draw_sdl_draw_rect(lv_draw_ctx_t * draw_ctx, const lv_draw_rect_dsc_t * dsc, const lv_area_t * coords);
+
+lv_res_t lv_draw_sdl_img_core(lv_draw_ctx_t * draw_ctx, const lv_draw_img_dsc_t * draw_dsc,
+ const lv_area_t * coords, const void * src);
+
+void lv_draw_sdl_draw_letter(lv_draw_ctx_t * draw_ctx, const lv_draw_label_dsc_t * dsc, const lv_point_t * pos_p,
+ uint32_t letter);
+
+void lv_draw_sdl_draw_line(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_sdl_draw_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_sdl_polygon(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_sdl_draw_bg(lv_draw_ctx_t * draw_ctx, const lv_draw_rect_dsc_t * dsc, const lv_area_t * coords);
+
+/**********************
+ * TYPEDEFS
+ **********************/
+
+/**********************
+ * STATIC PROTOTYPES
+ **********************/
+
+/**********************
+ * STATIC VARIABLES
+ **********************/
+
+/**********************
+ * MACROS
+ **********************/
+
+/**********************
+ * GLOBAL FUNCTIONS
+ **********************/
+
+void lv_draw_sdl_init_ctx(lv_disp_drv_t * disp_drv, lv_draw_ctx_t * draw_ctx)
+{
+ _lv_draw_sdl_utils_init();
+ lv_memset_00(draw_ctx, sizeof(lv_draw_sdl_ctx_t));
+ draw_ctx->draw_rect = lv_draw_sdl_draw_rect;
+ draw_ctx->draw_img = lv_draw_sdl_img_core;
+ draw_ctx->draw_letter = lv_draw_sdl_draw_letter;
+ draw_ctx->draw_line = lv_draw_sdl_draw_line;
+ draw_ctx->draw_arc = lv_draw_sdl_draw_arc;
+ draw_ctx->draw_polygon = lv_draw_sdl_polygon;
+ draw_ctx->draw_bg = lv_draw_sdl_draw_bg;
+ draw_ctx->layer_init = lv_draw_sdl_layer_init;
+ draw_ctx->layer_blend = lv_draw_sdl_layer_blend;
+ draw_ctx->layer_destroy = lv_draw_sdl_layer_destroy;
+ draw_ctx->layer_instance_size = sizeof(lv_draw_sdl_layer_ctx_t);
+ lv_draw_sdl_ctx_t * draw_ctx_sdl = (lv_draw_sdl_ctx_t *) draw_ctx;
+ draw_ctx_sdl->renderer = ((lv_draw_sdl_drv_param_t *) disp_drv->user_data)->renderer;
+ draw_ctx_sdl->internals = lv_mem_alloc(sizeof(lv_draw_sdl_context_internals_t));
+ lv_memset_00(draw_ctx_sdl->internals, sizeof(lv_draw_sdl_context_internals_t));
+ lv_draw_sdl_texture_cache_init(draw_ctx_sdl);
+}
+
+void lv_draw_sdl_deinit_ctx(lv_disp_drv_t * disp_drv, lv_draw_ctx_t * draw_ctx)
+{
+ lv_draw_sdl_ctx_t * draw_ctx_sdl = (lv_draw_sdl_ctx_t *) draw_ctx;
+ lv_draw_sdl_texture_cache_deinit(draw_ctx_sdl);
+ lv_mem_free(draw_ctx_sdl->internals);
+ _lv_draw_sdl_utils_deinit();
+}
+
+SDL_Texture * lv_draw_sdl_create_screen_texture(SDL_Renderer * renderer, lv_coord_t hor, lv_coord_t ver)
+{
+ SDL_Texture * texture = SDL_CreateTexture(renderer, LV_DRAW_SDL_TEXTURE_FORMAT, SDL_TEXTUREACCESS_TARGET, hor, ver);
+ SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND);
+ return texture;
+}
+
+/**********************
+ * STATIC FUNCTIONS
+ **********************/
+
+#endif /*LV_USE_GPU_SDL*/
diff --git a/lib/lvgl/src/draw/sdl/lv_draw_sdl.h b/lib/lvgl/src/draw/sdl/lv_draw_sdl.h
new file mode 100644
index 00000000..9b44a7b2
--- /dev/null
+++ b/lib/lvgl/src/draw/sdl/lv_draw_sdl.h
@@ -0,0 +1,96 @@
+/**
+ * @file lv_draw_sdl.h
+ *
+ */
+
+#ifndef LV_DRAW_SDL_H
+#define LV_DRAW_SDL_H
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*********************
+ * INCLUDES
+ *********************/
+#include "../../lv_conf_internal.h"
+
+#if LV_USE_GPU_SDL
+
+#include LV_GPU_SDL_INCLUDE_PATH
+
+#include "../lv_draw.h"
+#include "../../core/lv_disp.h"
+
+/*********************
+ * DEFINES
+ *********************/
+
+#if SDL_BYTEORDER == SDL_BIG_ENDIAN
+#define LV_DRAW_SDL_TEXTURE_FORMAT SDL_PIXELFORMAT_ARGB8888
+#else
+#define LV_DRAW_SDL_TEXTURE_FORMAT SDL_PIXELFORMAT_RGBA8888
+#endif
+
+/**********************
+ * TYPEDEFS
+ **********************/
+
+struct lv_draw_sdl_context_internals_t;
+
+typedef struct {
+ /**
+ * Render for display driver
+ */
+ SDL_Renderer * renderer;
+ void * user_data;
+} lv_draw_sdl_drv_param_t;
+
+typedef struct {
+ lv_draw_ctx_t base_draw;
+ SDL_Renderer * renderer;
+ struct lv_draw_sdl_context_internals_t * internals;
+} lv_draw_sdl_ctx_t;
+
+/**********************
+ * GLOBAL PROTOTYPES
+ **********************/
+
+void lv_draw_sdl_init_ctx(lv_disp_drv_t * disp_drv, lv_draw_ctx_t * draw_ctx);
+
+/**
+ * @brief Free caches
+ *
+ */
+void lv_draw_sdl_deinit_ctx(lv_disp_drv_t * disp_drv, lv_draw_ctx_t * draw_ctx);
+
+SDL_Texture * lv_draw_sdl_create_screen_texture(SDL_Renderer * renderer, lv_coord_t hor, lv_coord_t ver);
+
+/*======================
+ * Add/remove functions
+ *=====================*/
+
+/*=====================
+ * Setter functions
+ *====================*/
+
+/*=====================
+ * Getter functions
+ *====================*/
+
+/*=====================
+ * Other functions
+ *====================*/
+
+/**********************
+ * MACROS
+ **********************/
+
+#endif /*LV_USE_GPU_SDL*/
+
+#ifdef __cplusplus
+} /*extern "C"*/
+#endif
+
+#endif /*LV_DRAW_SDL_H*/
diff --git a/lib/lvgl/src/draw/sdl/lv_draw_sdl.mk b/lib/lvgl/src/draw/sdl/lv_draw_sdl.mk
new file mode 100644
index 00000000..c5c28b66
--- /dev/null
+++ b/lib/lvgl/src/draw/sdl/lv_draw_sdl.mk
@@ -0,0 +1,19 @@
+CSRCS += lv_draw_sdl.c
+CSRCS += lv_draw_sdl_arc.c
+CSRCS += lv_draw_sdl_bg.c
+CSRCS += lv_draw_sdl_composite.c
+CSRCS += lv_draw_sdl_img.c
+CSRCS += lv_draw_sdl_label.c
+CSRCS += lv_draw_sdl_line.c
+CSRCS += lv_draw_sdl_mask.c
+CSRCS += lv_draw_sdl_polygon.c
+CSRCS += lv_draw_sdl_rect.c
+CSRCS += lv_draw_sdl_stack_blur.c
+CSRCS += lv_draw_sdl_texture_cache.c
+CSRCS += lv_draw_sdl_utils.c
+CSRCS += lv_draw_sdl_layer.c
+
+DEPPATH += --dep-path $(LVGL_DIR)/$(LVGL_DIR_NAME)/src/draw/sdl
+VPATH += :$(LVGL_DIR)/$(LVGL_DIR_NAME)/src/draw/sdl
+
+CFLAGS += "-I$(LVGL_DIR)/$(LVGL_DIR_NAME)/src/draw/sdl"
diff --git a/lib/lvgl/src/draw/sdl/lv_draw_sdl_arc.c b/lib/lvgl/src/draw/sdl/lv_draw_sdl_arc.c
new file mode 100644
index 00000000..5786eaeb
--- /dev/null
+++ b/lib/lvgl/src/draw/sdl/lv_draw_sdl_arc.c
@@ -0,0 +1,238 @@
+/**
+ * @file lv_draw_sdl_arc.c
+ *
+ */
+
+/*********************
+ * INCLUDES
+ *********************/
+#include "../../lv_conf_internal.h"
+
+#if LV_USE_GPU_SDL
+
+#include "lv_draw_sdl.h"
+#include "lv_draw_sdl_utils.h"
+#include "lv_draw_sdl_texture_cache.h"
+#include "lv_draw_sdl_composite.h"
+
+/*********************
+ * DEFINES
+ *********************/
+
+/**********************
+ * TYPEDEFS
+ **********************/
+
+/**********************
+ * STATIC PROTOTYPES
+ **********************/
+
+/**********************
+ * STATIC VARIABLES
+ **********************/
+
+/**********************
+ * MACROS
+ **********************/
+
+static void dump_masks(SDL_Texture * texture, const lv_area_t * coords, const int16_t * ids, int16_t ids_count,
+ const int16_t * caps);
+
+static void get_cap_area(int16_t angle, lv_coord_t thickness, uint16_t radius, const lv_point_t * center,
+ lv_area_t * out);
+
+/**********************
+ * GLOBAL FUNCTIONS
+ **********************/
+void lv_draw_sdl_draw_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)
+{
+ lv_draw_sdl_ctx_t * ctx = (lv_draw_sdl_ctx_t *) draw_ctx;
+
+ 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 draw_area;
+ if(!_lv_area_intersect(&draw_area, &area_out, draw_ctx->clip_area)) {
+ return;
+ }
+
+ 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;
+
+
+ while(start_angle >= 360) start_angle -= 360;
+ while(end_angle >= 360) end_angle -= 360;
+
+ int16_t mask_ids[3] = {LV_MASK_ID_INV, LV_MASK_ID_INV, LV_MASK_ID_INV}, mask_ids_count = 1;
+ int16_t cap_ids[2] = {LV_MASK_ID_INV, LV_MASK_ID_INV};
+
+ lv_draw_mask_radius_param_t mask_out_param;
+ lv_draw_mask_radius_init(&mask_out_param, &area_out, LV_RADIUS_CIRCLE, false);
+ mask_ids[0] = lv_draw_mask_add(&mask_out_param, NULL);
+
+ lv_draw_mask_radius_param_t mask_in_param;
+ if(lv_area_get_width(&area_in) > 0 && lv_area_get_height(&area_in) > 0) {
+ lv_draw_mask_radius_init(&mask_in_param, &area_in, LV_RADIUS_CIRCLE, true);
+ mask_ids[1] = lv_draw_mask_add(&mask_in_param, NULL);
+ mask_ids_count++;
+ }
+
+ lv_draw_mask_angle_param_t mask_angle_param;
+ if((start_angle - end_angle) % 360) {
+ lv_draw_mask_angle_init(&mask_angle_param, center->x, center->y, start_angle, end_angle);
+ mask_ids[2] = lv_draw_mask_add(&mask_angle_param, NULL);
+ mask_ids_count++;
+ }
+
+ lv_draw_mask_radius_param_t cap_start_param, cap_end_param;
+ if(mask_ids_count == 3 && dsc->rounded) {
+ lv_area_t start_area, end_area;
+ get_cap_area((int16_t) start_angle, dsc->width, radius, center, &start_area);
+ get_cap_area((int16_t) end_angle, dsc->width, radius, center, &end_area);
+ lv_draw_mask_radius_init(&cap_start_param, &start_area, dsc->width / 2, false);
+ cap_ids[0] = lv_draw_mask_add(&cap_start_param, NULL);
+ lv_draw_mask_radius_init(&cap_end_param, &end_area, dsc->width / 2, false);
+ cap_ids[1] = lv_draw_mask_add(&cap_end_param, NULL);
+ }
+
+ lv_coord_t w = lv_area_get_width(&draw_area), h = lv_area_get_height(&draw_area);
+ SDL_Texture * texture = lv_draw_sdl_composite_texture_obtain(ctx, LV_DRAW_SDL_COMPOSITE_TEXTURE_ID_STREAM1, w, h);
+ SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND);
+ dump_masks(texture, &draw_area, mask_ids, mask_ids_count, cap_ids[0] != LV_MASK_ID_INV ? cap_ids : NULL);
+
+ lv_draw_mask_remove_id(mask_ids[0]);
+ lv_draw_mask_free_param(&mask_out_param);
+
+ if(mask_ids_count > 1) {
+ lv_draw_mask_remove_id(mask_ids[1]);
+ lv_draw_mask_free_param(&mask_in_param);
+ }
+
+ if(mask_ids_count > 2) {
+ lv_draw_mask_remove_id(mask_ids[2]);
+ lv_draw_mask_free_param(&mask_angle_param);
+ }
+
+ if(cap_ids[0] != LV_MASK_ID_INV) {
+ lv_draw_mask_remove_id(cap_ids[0]);
+ lv_draw_mask_remove_id(cap_ids[1]);
+ lv_draw_mask_free_param(&cap_start_param);
+ lv_draw_mask_free_param(&cap_end_param);
+ }
+
+ SDL_Rect srcrect = {0, 0, w, h}, dstrect;
+ lv_area_to_sdl_rect(&draw_area, &dstrect);
+ SDL_Color color;
+ lv_color_to_sdl_color(&dsc->color, &color);
+ SDL_SetTextureColorMod(texture, color.r, color.g, color.b);
+ SDL_SetTextureAlphaMod(texture, dsc->opa);
+ SDL_RenderCopy(ctx->renderer, texture, &srcrect, &dstrect);
+}
+
+/**********************
+ * STATIC FUNCTIONS
+ **********************/
+
+static void dump_masks(SDL_Texture * texture, const lv_area_t * coords, const int16_t * ids, int16_t ids_count,
+ const int16_t * caps)
+{
+ lv_coord_t w = lv_area_get_width(coords), h = lv_area_get_height(coords);
+ SDL_assert(w > 0 && h > 0);
+ SDL_Rect rect = {0, 0, w, h};
+ uint8_t * pixels;
+ int pitch;
+ if(SDL_LockTexture(texture, &rect, (void **) &pixels, &pitch) != 0) return;
+
+ lv_opa_t * line_buf = lv_mem_buf_get(rect.w);
+ for(lv_coord_t y = 0; y < rect.h; y++) {
+ lv_memset_ff(line_buf, rect.w);
+ lv_coord_t abs_x = (lv_coord_t) coords->x1, abs_y = (lv_coord_t)(y + coords->y1), len = (lv_coord_t) rect.w;
+ lv_draw_mask_res_t res;
+ res = lv_draw_mask_apply_ids(line_buf, abs_x, abs_y, len, ids, ids_count);
+ if(res == LV_DRAW_MASK_RES_TRANSP) {
+ lv_memset_00(&pixels[y * pitch], 4 * rect.w);
+ }
+ else if(res == LV_DRAW_MASK_RES_FULL_COVER) {
+ lv_memset_ff(&pixels[y * pitch], 4 * rect.w);
+ }
+ else {
+ for(int x = 0; x < rect.w; x++) {
+ uint8_t * pixel = &pixels[y * pitch + x * 4];
+ *pixel = line_buf[x];
+ pixel[1] = pixel[2] = pixel[3] = 0xFF;
+ }
+ }
+ if(caps) {
+ for(int i = 0; i < 2; i++) {
+ lv_memset_ff(line_buf, rect.w);
+ res = lv_draw_mask_apply_ids(line_buf, abs_x, abs_y, len, &caps[i], 1);
+ if(res == LV_DRAW_MASK_RES_TRANSP) {
+ /* Ignore */
+ }
+ else if(res == LV_DRAW_MASK_RES_FULL_COVER) {
+ lv_memset_ff(&pixels[y * pitch], 4 * rect.w);
+ }
+ else {
+ for(int x = 0; x < rect.w; x++) {
+ uint8_t * pixel = &pixels[y * pitch + x * 4];
+ uint16_t old_opa = line_buf[x] + *pixel;
+ *pixel = LV_MIN(old_opa, 0xFF);
+ pixel[1] = pixel[2] = pixel[3] = 0xFF;
+ }
+ }
+ }
+ }
+ }
+ lv_mem_buf_release(line_buf);
+ SDL_UnlockTexture(texture);
+}
+
+static void get_cap_area(int16_t angle, lv_coord_t thickness, uint16_t radius, const lv_point_t * center,
+ lv_area_t * out)
+{
+ 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((int16_t)(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;
+ out->x1 = cir_x - thick_half + thick_corr;
+ out->x2 = cir_x + thick_half;
+ }
+ else {
+ cir_x = (cir_x + pa) >> ps;
+ out->x1 = cir_x - thick_half;
+ out->x2 = cir_x + thick_half - thick_corr;
+ }
+
+ if(cir_y > 0) {
+ cir_y = (cir_y - pa) >> ps;
+ out->y1 = cir_y - thick_half + thick_corr;
+ out->y2 = cir_y + thick_half;
+ }
+ else {
+ cir_y = (cir_y + pa) >> ps;
+ out->y1 = cir_y - thick_half;
+ out->y2 = cir_y + thick_half - thick_corr;
+ }
+ lv_area_move(out, center->x, center->y);
+}
+
+#endif /*LV_USE_GPU_SDL*/
diff --git a/lib/lvgl/src/draw/sdl/lv_draw_sdl_bg.c b/lib/lvgl/src/draw/sdl/lv_draw_sdl_bg.c
new file mode 100644
index 00000000..48e0f6b1
--- /dev/null
+++ b/lib/lvgl/src/draw/sdl/lv_draw_sdl_bg.c
@@ -0,0 +1,106 @@
+/**
+ * @file lv_draw_sdl_bg.c
+ *
+ */
+
+/*********************
+ * INCLUDES
+ *********************/
+
+#include "../../lv_conf_internal.h"
+
+#if LV_USE_GPU_SDL
+
+#include "../lv_draw_rect.h"
+#include "../lv_draw_img.h"
+#include "../lv_draw_label.h"
+#include "../lv_draw_mask.h"
+#include "../../core/lv_refr.h"
+#include "lv_draw_sdl_utils.h"
+#include "lv_draw_sdl_texture_cache.h"
+#include "lv_draw_sdl_composite.h"
+
+/*********************
+ * DEFINES
+ *********************/
+
+/**********************
+ * TYPEDEFS
+ **********************/
+
+/**********************
+ * STATIC PROTOTYPES
+ **********************/
+
+static void draw_bg_color(lv_draw_sdl_ctx_t * ctx, const lv_area_t * coords, const lv_area_t * draw_area,
+ const lv_draw_rect_dsc_t * dsc);
+
+static void draw_bg_img(lv_draw_sdl_ctx_t * ctx, const lv_area_t * coords, const lv_area_t * draw_area,
+ const lv_draw_rect_dsc_t * dsc);
+
+/**********************
+ * STATIC VARIABLES
+ **********************/
+
+/**********************
+ * MACROS
+ **********************/
+
+/**********************
+ * GLOBAL FUNCTIONS
+ **********************/
+
+void lv_draw_sdl_draw_bg(lv_draw_ctx_t * draw_ctx, const lv_draw_rect_dsc_t * dsc, const lv_area_t * coords)
+{
+ const lv_area_t * clip = draw_ctx->clip_area;
+ lv_draw_sdl_ctx_t * ctx = (lv_draw_sdl_ctx_t *) draw_ctx;
+ /* Coords will be translated so coords will start at (0,0) */
+ lv_area_t t_area;
+ bool has_content = _lv_area_intersect(&t_area, coords, clip);
+
+ /* Shadows and outlines will also draw in extended area */
+ if(has_content) {
+ if(dsc->bg_img_src) {
+ draw_bg_img(ctx, coords, &t_area, dsc);
+ }
+ else {
+ draw_bg_color(ctx, coords, &t_area, dsc);
+ }
+ }
+
+}
+
+/**********************
+ * STATIC FUNCTIONS
+ **********************/
+
+static void draw_bg_color(lv_draw_sdl_ctx_t * ctx, const lv_area_t * coords, const lv_area_t * draw_area,
+ const lv_draw_rect_dsc_t * dsc)
+{
+ SDL_Color bg_color;
+ lv_color_to_sdl_color(&dsc->bg_color, &bg_color);
+ SDL_SetRenderDrawBlendMode(ctx->renderer, SDL_BLENDMODE_NONE);
+ SDL_SetRenderDrawColor(ctx->renderer, bg_color.r, bg_color.g, bg_color.b, dsc->bg_opa);
+
+ SDL_Rect rect;
+ lv_area_to_sdl_rect(draw_area, &rect);
+ SDL_RenderFillRect(ctx->renderer, &rect);
+
+ SDL_SetRenderDrawBlendMode(ctx->renderer, SDL_BLENDMODE_BLEND);
+}
+
+static void draw_bg_img(lv_draw_sdl_ctx_t * ctx, const lv_area_t * coords, const lv_area_t * draw_area,
+ const lv_draw_rect_dsc_t * dsc)
+{
+ SDL_SetRenderDrawBlendMode(ctx->renderer, SDL_BLENDMODE_NONE);
+ SDL_SetRenderDrawColor(ctx->renderer, 0, 0, 0, 0);
+
+ SDL_Rect rect;
+ lv_area_to_sdl_rect(draw_area, &rect);
+ SDL_RenderFillRect(ctx->renderer, &rect);
+
+ SDL_SetRenderDrawBlendMode(ctx->renderer, SDL_BLENDMODE_BLEND);
+ lv_draw_rect((lv_draw_ctx_t *) ctx, dsc, coords);
+}
+
+#endif /*LV_USE_GPU_SDL*/
diff --git a/lib/lvgl/src/draw/sdl/lv_draw_sdl_composite.c b/lib/lvgl/src/draw/sdl/lv_draw_sdl_composite.c
new file mode 100644
index 00000000..4d0603d7
--- /dev/null
+++ b/lib/lvgl/src/draw/sdl/lv_draw_sdl_composite.c
@@ -0,0 +1,262 @@
+/**
+ * @file lv_draw_sdl_composite.c
+ *
+ */
+
+/*********************
+ * INCLUDES
+ *********************/
+#include "../../lv_conf_internal.h"
+
+#if LV_USE_GPU_SDL
+
+#include "../../misc/lv_gc.h"
+#include "../../core/lv_refr.h"
+#include "lv_draw_sdl_composite.h"
+#include "lv_draw_sdl_utils.h"
+#include "lv_draw_sdl_priv.h"
+#include "lv_draw_sdl_texture_cache.h"
+
+/*********************
+ * DEFINES
+ *********************/
+
+/**********************
+ * TYPEDEFS
+ **********************/
+
+typedef struct {
+ lv_sdl_cache_key_magic_t magic;
+ lv_draw_sdl_composite_texture_id_t type;
+} composite_key_t;
+
+/**********************
+ * STATIC PROTOTYPES
+ **********************/
+
+static composite_key_t mask_key_create(lv_draw_sdl_composite_texture_id_t type);
+
+static lv_coord_t next_pow_of_2(lv_coord_t num);
+
+static void dump_masks(SDL_Texture * texture, const lv_area_t * coords);
+/**********************
+ * STATIC VARIABLES
+ **********************/
+
+/**********************
+ * MACROS
+ **********************/
+
+/**********************
+ * GLOBAL FUNCTIONS
+ **********************/
+
+bool lv_draw_sdl_composite_begin(lv_draw_sdl_ctx_t * ctx, const lv_area_t * coords_in, const lv_area_t * clip_in,
+ const lv_area_t * extension, lv_blend_mode_t blend_mode, lv_area_t * coords_out,
+ lv_area_t * clip_out, lv_area_t * apply_area)
+{
+ lv_area_t full_coords = *coords_in;
+
+ /* Normalize full_coords */
+ if(full_coords.x1 > full_coords.x2) {
+ lv_coord_t x2 = full_coords.x2;
+ full_coords.x2 = full_coords.x1;
+ full_coords.x1 = x2;
+ }
+ if(full_coords.y1 > full_coords.y2) {
+ lv_coord_t y2 = full_coords.y2;
+ full_coords.y2 = full_coords.y1;
+ full_coords.y1 = y2;
+ }
+
+ if(extension) {
+ full_coords.x1 -= extension->x1;
+ full_coords.x2 += extension->x2;
+ full_coords.y1 -= extension->y1;
+ full_coords.y2 += extension->y2;
+ }
+
+ if(!_lv_area_intersect(apply_area, &full_coords, clip_in)) return false;
+ bool has_mask = lv_draw_mask_is_any(apply_area);
+
+ const bool draw_mask = has_mask && LV_GPU_SDL_CUSTOM_BLEND_MODE;
+ const bool draw_blend = blend_mode != LV_BLEND_MODE_NORMAL;
+ if(draw_mask || draw_blend) {
+ lv_draw_sdl_context_internals_t * internals = ctx->internals;
+ LV_ASSERT(internals->mask == NULL && internals->composition == NULL && internals->target_backup == NULL);
+
+ lv_coord_t w = lv_area_get_width(apply_area), h = lv_area_get_height(apply_area);
+ internals->composition = lv_draw_sdl_composite_texture_obtain(ctx, LV_DRAW_SDL_COMPOSITE_TEXTURE_ID_TARGET0, w, h);
+ /* Don't need to worry about integral overflow */
+ lv_coord_t ofs_x = (lv_coord_t) - apply_area->x1, ofs_y = (lv_coord_t) - apply_area->y1;
+ /* Offset draw area to start with (0,0) of coords */
+ lv_area_move(coords_out, ofs_x, ofs_y);
+ lv_area_move(clip_out, ofs_x, ofs_y);
+ internals->target_backup = SDL_GetRenderTarget(ctx->renderer);
+ SDL_SetRenderTarget(ctx->renderer, internals->composition);
+ SDL_SetRenderDrawColor(ctx->renderer, 255, 255, 255, 0);
+ /* SDL_RenderClear is not working properly, so we overwrite the target with solid color */
+ SDL_SetRenderDrawBlendMode(ctx->renderer, SDL_BLENDMODE_NONE);
+ SDL_RenderFillRect(ctx->renderer, NULL);
+ SDL_SetRenderDrawBlendMode(ctx->renderer, SDL_BLENDMODE_BLEND);
+#if LV_GPU_SDL_CUSTOM_BLEND_MODE
+ internals->mask = lv_draw_sdl_composite_texture_obtain(ctx, LV_DRAW_SDL_COMPOSITE_TEXTURE_ID_STREAM0, w, h);
+ dump_masks(internals->mask, apply_area);
+#endif
+ }
+ else if(has_mask) {
+ /* Fallback mask handling. This will at least make bars looks less bad */
+ for(uint8_t i = 0; i < _LV_MASK_MAX_NUM; i++) {
+ _lv_draw_mask_common_dsc_t * comm_param = LV_GC_ROOT(_lv_draw_mask_list[i]).param;
+ if(comm_param == NULL) continue;
+ switch(comm_param->type) {
+ case LV_DRAW_MASK_TYPE_RADIUS: {
+ const lv_draw_mask_radius_param_t * param = (const lv_draw_mask_radius_param_t *) comm_param;
+ if(param->cfg.outer) break;
+ _lv_area_intersect(clip_out, apply_area, &param->cfg.rect);
+ break;
+ }
+ default:
+ break;
+ }
+ }
+ }
+ return has_mask;
+}
+
+void lv_draw_sdl_composite_end(lv_draw_sdl_ctx_t * ctx, const lv_area_t * apply_area, lv_blend_mode_t blend_mode)
+{
+ lv_draw_sdl_context_internals_t * internals = ctx->internals;
+ SDL_Rect src_rect = {0, 0, lv_area_get_width(apply_area), lv_area_get_height(apply_area)};
+#if LV_GPU_SDL_CUSTOM_BLEND_MODE
+ if(internals->mask) {
+ SDL_BlendMode mode = SDL_ComposeCustomBlendMode(SDL_BLENDFACTOR_ZERO, SDL_BLENDFACTOR_ONE,
+ SDL_BLENDOPERATION_ADD, SDL_BLENDFACTOR_ZERO,
+ SDL_BLENDFACTOR_SRC_ALPHA, SDL_BLENDOPERATION_ADD);
+ SDL_SetTextureBlendMode(internals->mask, mode);
+ SDL_RenderCopy(ctx->renderer, internals->mask, &src_rect, &src_rect);
+ }
+#endif
+
+ /* Shapes are drawn on composite layer when mask or blend mode is present */
+ if(internals->composition) {
+ SDL_Rect dst_rect;
+ lv_area_to_sdl_rect(apply_area, &dst_rect);
+
+ SDL_SetRenderTarget(ctx->renderer, internals->target_backup);
+ switch(blend_mode) {
+ case LV_BLEND_MODE_NORMAL:
+ SDL_SetTextureBlendMode(internals->composition, SDL_BLENDMODE_BLEND);
+ break;
+ case LV_BLEND_MODE_ADDITIVE:
+ SDL_SetTextureBlendMode(internals->composition, SDL_BLENDMODE_ADD);
+ break;
+#if LV_GPU_SDL_CUSTOM_BLEND_MODE
+ case LV_BLEND_MODE_SUBTRACTIVE: {
+ SDL_BlendMode mode = SDL_ComposeCustomBlendMode(SDL_BLENDFACTOR_ONE, SDL_BLENDFACTOR_ONE,
+ SDL_BLENDOPERATION_SUBTRACT, SDL_BLENDFACTOR_ONE,
+ SDL_BLENDFACTOR_ONE, SDL_BLENDOPERATION_SUBTRACT);
+ SDL_SetTextureBlendMode(internals->composition, mode);
+ break;
+ }
+ case LV_BLEND_MODE_MULTIPLY: {
+ SDL_BlendMode mode = SDL_ComposeCustomBlendMode(SDL_BLENDFACTOR_ZERO, SDL_BLENDFACTOR_SRC_COLOR,
+ SDL_BLENDOPERATION_ADD, SDL_BLENDFACTOR_ZERO,
+ SDL_BLENDFACTOR_DST_ALPHA, SDL_BLENDOPERATION_ADD);
+ SDL_SetTextureBlendMode(internals->composition, mode);
+ break;
+ }
+#endif
+ default:
+ LV_LOG_WARN("Doesn't support blend mode %d", blend_mode);
+ SDL_SetTextureBlendMode(internals->composition, SDL_BLENDMODE_BLEND);
+ /* Unsupported yet */
+ break;
+ }
+ SDL_RenderCopy(ctx->renderer, internals->composition, &src_rect, &dst_rect);
+ }
+
+ internals->mask = internals->composition = internals->target_backup = NULL;
+}
+
+SDL_Texture * lv_draw_sdl_composite_texture_obtain(lv_draw_sdl_ctx_t * ctx, lv_draw_sdl_composite_texture_id_t id,
+ lv_coord_t w, lv_coord_t h)
+{
+ lv_point_t * tex_size = NULL;
+ composite_key_t mask_key = mask_key_create(id);
+ SDL_Texture * result = lv_draw_sdl_texture_cache_get_with_userdata(ctx, &mask_key, sizeof(composite_key_t), NULL,
+ (void **) &tex_size);
+ if(!result || tex_size->x < w || tex_size->y < h) {
+ lv_coord_t size = next_pow_of_2(LV_MAX(w, h));
+ int access = SDL_TEXTUREACCESS_STREAMING;
+ if(id >= LV_DRAW_SDL_COMPOSITE_TEXTURE_ID_TRANSFORM0) {
+ access = SDL_TEXTUREACCESS_TARGET;
+ }
+ else if(id >= LV_DRAW_SDL_COMPOSITE_TEXTURE_ID_TARGET0) {
+ access = SDL_TEXTUREACCESS_TARGET;
+ }
+ result = SDL_CreateTexture(ctx->renderer, LV_DRAW_SDL_TEXTURE_FORMAT, access, size, size);
+ tex_size = lv_mem_alloc(sizeof(lv_point_t));
+ tex_size->x = tex_size->y = size;
+ lv_draw_sdl_texture_cache_put_advanced(ctx, &mask_key, sizeof(composite_key_t), result, tex_size, lv_mem_free, 0);
+ }
+ return result;
+}
+
+/**********************
+ * STATIC FUNCTIONS
+ **********************/
+
+static composite_key_t mask_key_create(lv_draw_sdl_composite_texture_id_t type)
+{
+ composite_key_t key;
+ /* VERY IMPORTANT! Padding between members is uninitialized, so we have to wipe them manually */
+ SDL_memset(&key, 0, sizeof(key));
+ key.magic = LV_GPU_CACHE_KEY_MAGIC_MASK;
+ key.type = type;
+ return key;
+}
+
+static lv_coord_t next_pow_of_2(lv_coord_t num)
+{
+ lv_coord_t n = 128;
+ while(n < num && n < 16384) {
+ n = n << 1;
+ }
+ return n;
+}
+
+static void dump_masks(SDL_Texture * texture, const lv_area_t * coords)
+{
+ lv_coord_t w = lv_area_get_width(coords), h = lv_area_get_height(coords);
+ SDL_assert(w > 0 && h > 0);
+ SDL_Rect rect = {0, 0, w, h};
+ uint8_t * pixels;
+ int pitch;
+ if(SDL_LockTexture(texture, &rect, (void **) &pixels, &pitch) != 0) return;
+
+ lv_opa_t * line_buf = lv_mem_buf_get(rect.w);
+ for(lv_coord_t y = 0; y < rect.h; y++) {
+ lv_memset_ff(line_buf, rect.w);
+ lv_coord_t abs_x = (lv_coord_t) coords->x1, abs_y = (lv_coord_t)(y + coords->y1), len = (lv_coord_t) rect.w;
+ lv_draw_mask_res_t res;
+ res = lv_draw_mask_apply(line_buf, abs_x, abs_y, len);
+ if(res == LV_DRAW_MASK_RES_TRANSP) {
+ lv_memset_00(&pixels[y * pitch], 4 * rect.w);
+ }
+ else if(res == LV_DRAW_MASK_RES_FULL_COVER) {
+ lv_memset_ff(&pixels[y * pitch], 4 * rect.w);
+ }
+ else {
+ for(int x = 0; x < rect.w; x++) {
+ const size_t idx = y * pitch + x * 4;
+ pixels[idx] = line_buf[x];
+ pixels[idx + 1] = pixels[idx + 2] = pixels[idx + 3] = 0xFF;
+ }
+ }
+ }
+ lv_mem_buf_release(line_buf);
+ SDL_UnlockTexture(texture);
+}
+
+#endif /*LV_USE_GPU_SDL*/
diff --git a/lib/lvgl/src/draw/sdl/lv_draw_sdl_composite.h b/lib/lvgl/src/draw/sdl/lv_draw_sdl_composite.h
new file mode 100644
index 00000000..72a2daef
--- /dev/null
+++ b/lib/lvgl/src/draw/sdl/lv_draw_sdl_composite.h
@@ -0,0 +1,73 @@
+/**
+ * @file lv_draw_sdl_composite.h
+ *
+ */
+
+#ifndef LV_DRAW_SDL_COMPOSITE_H
+#define LV_DRAW_SDL_COMPOSITE_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*********************
+ * INCLUDES
+ *********************/
+
+#include "../../lv_conf_internal.h"
+
+#include LV_GPU_SDL_INCLUDE_PATH
+
+#include "lv_draw_sdl.h"
+#include "../../misc/lv_area.h"
+#include "../../misc/lv_color.h"
+
+/*********************
+ * DEFINES
+ *********************/
+
+/**********************
+ * TYPEDEFS
+ **********************/
+
+typedef enum lv_draw_sdl_composite_texture_id_t {
+ LV_DRAW_SDL_COMPOSITE_TEXTURE_ID_STREAM0,
+ LV_DRAW_SDL_COMPOSITE_TEXTURE_ID_STREAM1,
+ LV_DRAW_SDL_COMPOSITE_TEXTURE_ID_TARGET0,
+ LV_DRAW_SDL_COMPOSITE_TEXTURE_ID_TARGET1,
+ LV_DRAW_SDL_COMPOSITE_TEXTURE_ID_TRANSFORM0,
+} lv_draw_sdl_composite_texture_id_t;
+
+/**********************
+ * GLOBAL PROTOTYPES
+ **********************/
+
+/**
+ * Begin drawing with mask. Render target will be switched to a temporary texture,
+ * and drawing coordinates may get clipped or translated
+ * @param coords_in Original coordinates
+ * @param clip_in Original clip area
+ * @param extension Useful for shadows or outlines, can be NULL
+ * @param coords_out Translated coords
+ * @param clip_out Translated clip area
+ * @param apply_area Area of actual composited texture will be drawn
+ * @return true if there are any mask needs to be drawn, false otherwise
+ */
+bool lv_draw_sdl_composite_begin(lv_draw_sdl_ctx_t * ctx, const lv_area_t * coords_in, const lv_area_t * clip_in,
+ const lv_area_t * extension, lv_blend_mode_t blend_mode, lv_area_t * coords_out,
+ lv_area_t * clip_out, lv_area_t * apply_area);
+
+void lv_draw_sdl_composite_end(lv_draw_sdl_ctx_t * ctx, const lv_area_t * apply_area, lv_blend_mode_t blend_mode);
+
+SDL_Texture * lv_draw_sdl_composite_texture_obtain(lv_draw_sdl_ctx_t * ctx, lv_draw_sdl_composite_texture_id_t id,
+ lv_coord_t w, lv_coord_t h);
+
+/**********************
+ * MACROS
+ **********************/
+
+#ifdef __cplusplus
+} /*extern "C"*/
+#endif
+
+#endif /*LV_DRAW_SDL_COMPOSITE_H*/
diff --git a/lib/lvgl/src/draw/sdl/lv_draw_sdl_img.c b/lib/lvgl/src/draw/sdl/lv_draw_sdl_img.c
new file mode 100644
index 00000000..3c955d84
--- /dev/null
+++ b/lib/lvgl/src/draw/sdl/lv_draw_sdl_img.c
@@ -0,0 +1,467 @@
+/**
+ * @file lv_draw_sdl_img.c
+ *
+ */
+
+/*********************
+ * INCLUDES
+ *********************/
+
+#include "../../lv_conf_internal.h"
+
+#if LV_USE_GPU_SDL
+
+#include "../lv_draw_img.h"
+#include "../lv_img_cache.h"
+#include "../lv_draw_mask.h"
+#include "../../misc/lv_lru.h"
+#include "../../misc/lv_gc.h"
+
+#include "lv_draw_sdl_img.h"
+#include "lv_draw_sdl_utils.h"
+#include "lv_draw_sdl_texture_cache.h"
+#include "lv_draw_sdl_composite.h"
+#include "lv_draw_sdl_rect.h"
+#include "lv_draw_sdl_layer.h"
+
+/*********************
+ * DEFINES
+ *********************/
+
+/**********************
+ * TYPEDEFS
+ **********************/
+
+typedef struct {
+ lv_sdl_cache_key_magic_t magic;
+ const SDL_Texture * texture;
+ lv_coord_t w, h, radius;
+} lv_draw_img_rounded_key_t;
+
+enum {
+ ROUNDED_IMG_PART_LEFT = 0,
+ ROUNDED_IMG_PART_HCENTER = 1,
+ ROUNDED_IMG_PART_RIGHT = 2,
+ ROUNDED_IMG_PART_TOP = 3,
+ ROUNDED_IMG_PART_VCENTER = 4,
+ ROUNDED_IMG_PART_BOTTOM = 5,
+};
+
+enum {
+ ROUNDED_IMG_CORNER_TOP_LEFT = 0,
+ ROUNDED_IMG_CORNER_TOP_RIGHT = 1,
+ ROUNDED_IMG_CORNER_BOTTOM_RIGHT = 2,
+ ROUNDED_IMG_CORNER_BOTTOM_LEFT = 3,
+};
+
+/**********************
+ * STATIC PROTOTYPES
+ **********************/
+
+static SDL_Texture * upload_img_texture(SDL_Renderer * renderer, lv_img_decoder_dsc_t * dsc);
+
+static SDL_Texture * upload_img_texture_fallback(SDL_Renderer * renderer, lv_img_decoder_dsc_t * dsc);
+
+static bool check_mask_simple_radius(const lv_area_t * coords, lv_coord_t * radius);
+
+static void draw_img_simple(lv_draw_sdl_ctx_t * ctx, SDL_Texture * texture, const lv_draw_sdl_img_header_t * header,
+ const lv_draw_img_dsc_t * draw_dsc, const lv_area_t * coords, const lv_area_t * clip);
+
+static void draw_img_rounded(lv_draw_sdl_ctx_t * ctx, SDL_Texture * texture, const lv_draw_sdl_img_header_t * header,
+ const lv_draw_img_dsc_t * draw_dsc, const lv_area_t * coords, const lv_area_t * clip,
+ lv_coord_t radius);
+
+static SDL_Texture * img_rounded_frag_obtain(lv_draw_sdl_ctx_t * ctx, SDL_Texture * texture,
+ const lv_draw_sdl_img_header_t * header, int w, int h, lv_coord_t radius);
+
+static lv_draw_img_rounded_key_t rounded_key_create(const SDL_Texture * texture, lv_coord_t w, lv_coord_t h,
+ lv_coord_t radius);
+
+static void calc_draw_part(SDL_Texture * texture, const lv_draw_sdl_img_header_t * header, const lv_area_t * coords,
+ const lv_area_t * clip, SDL_Rect * clipped_src, SDL_Rect * clipped_dst);
+/**********************
+ * STATIC VARIABLES
+ **********************/
+
+/**********************
+ * MACROS
+ **********************/
+
+
+static void apply_recolor_opa(SDL_Texture * texture, const lv_draw_img_dsc_t * draw_dsc);
+
+/**********************
+ * GLOBAL FUNCTIONS
+ **********************/
+
+lv_res_t lv_draw_sdl_img_core(lv_draw_ctx_t * draw_ctx, const lv_draw_img_dsc_t * draw_dsc,
+ const lv_area_t * coords, const void * src)
+{
+ const lv_area_t * clip = draw_ctx->clip_area;
+ lv_draw_sdl_ctx_t * ctx = (lv_draw_sdl_ctx_t *) draw_ctx;
+
+ size_t key_size;
+ lv_draw_sdl_cache_key_head_img_t * key = lv_draw_sdl_texture_img_key_create(src, draw_dsc->frame_id, &key_size);
+ bool texture_found = false;
+ lv_draw_sdl_img_header_t * header = NULL;
+ SDL_Texture * texture = lv_draw_sdl_texture_cache_get_with_userdata(ctx, key, key_size, &texture_found,
+ (void **) &header);
+ if(!texture_found) {
+ lv_draw_sdl_img_load_texture(ctx, key, key_size, src, draw_dsc->frame_id, &texture, &header);
+ }
+ SDL_free(key);
+ if(!texture) {
+ return LV_RES_INV;
+ }
+
+ lv_area_t zoomed_cords;
+ _lv_img_buf_get_transformed_area(&zoomed_cords, lv_area_get_width(coords), lv_area_get_height(coords), 0,
+ draw_dsc->zoom, &draw_dsc->pivot);
+ lv_area_move(&zoomed_cords, coords->x1, coords->y1);
+
+ /* When in > 0, draw simple radius */
+ lv_coord_t radius = 0;
+ /* Coords will be translated so coords will start at (0,0) */
+ lv_area_t t_coords = zoomed_cords, t_clip = *clip, apply_area;
+
+ bool has_composite = false;
+
+ if(!check_mask_simple_radius(&t_coords, &radius)) {
+ has_composite = lv_draw_sdl_composite_begin(ctx, &zoomed_cords, clip, NULL, draw_dsc->blend_mode,
+ &t_coords, &t_clip, &apply_area);
+ }
+
+ lv_draw_sdl_transform_areas_offset(ctx, has_composite, &apply_area, &t_coords, &t_clip);
+
+ SDL_Rect clip_rect, coords_rect;
+ lv_area_to_sdl_rect(&t_clip, &clip_rect);
+ lv_area_to_sdl_rect(&t_coords, &coords_rect);
+
+ SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND);
+
+ if(radius > 0) {
+ draw_img_rounded(ctx, texture, header, draw_dsc, &t_coords, &t_clip, radius);
+ }
+ else {
+ draw_img_simple(ctx, texture, header, draw_dsc, &t_coords, &t_clip);
+ }
+
+ lv_draw_sdl_composite_end(ctx, &apply_area, draw_dsc->blend_mode);
+
+ return LV_RES_OK;
+}
+
+static void calc_draw_part(SDL_Texture * texture, const lv_draw_sdl_img_header_t * header, const lv_area_t * coords,
+ const lv_area_t * clip, SDL_Rect * clipped_src, SDL_Rect * clipped_dst)
+{
+ double x = 0, y = 0, w, h;
+ if(SDL_RectEmpty(&header->rect)) {
+ Uint32 format = 0;
+ int access = 0, tw, th;
+ SDL_QueryTexture(texture, &format, &access, &tw, &th);
+ w = tw;
+ h = th;
+ }
+ else {
+ x = header->rect.x;
+ y = header->rect.y;
+ w = header->rect.w;
+ h = header->rect.h;
+ }
+ if(clip) {
+ lv_area_t clipped_area;
+ _lv_area_intersect(&clipped_area, coords, clip);
+ lv_area_to_sdl_rect(&clipped_area, clipped_dst);
+ }
+ else {
+ lv_area_to_sdl_rect(coords, clipped_dst);
+ }
+ lv_coord_t coords_w = lv_area_get_width(coords), coords_h = lv_area_get_height(coords);
+ clipped_src->x = (int)(x + (clipped_dst->x - coords->x1) * w / coords_w);
+ clipped_src->y = (int)(y + (clipped_dst->y - coords->y1) * h / coords_h);
+ clipped_src->w = (int)(w - (coords_w - clipped_dst->w) * w / coords_w);
+ clipped_src->h = (int)(h - (coords_h - clipped_dst->h) * h / coords_h);
+}
+
+bool lv_draw_sdl_img_load_texture(lv_draw_sdl_ctx_t * ctx, lv_draw_sdl_cache_key_head_img_t * key, size_t key_size,
+ const void * src, int32_t frame_id, SDL_Texture ** texture,
+ lv_draw_sdl_img_header_t ** header)
+{
+ _lv_img_cache_entry_t * cdsc = _lv_img_cache_open(src, lv_color_white(), frame_id);
+ lv_draw_sdl_cache_flag_t tex_flags = 0;
+ SDL_Rect rect;
+ SDL_memset(&rect, 0, sizeof(SDL_Rect));
+ if(cdsc) {
+ lv_img_decoder_dsc_t * dsc = &cdsc->dec_dsc;
+ if(dsc->user_data && SDL_memcmp(dsc->user_data, LV_DRAW_SDL_DEC_DSC_TEXTURE_HEAD, 8) == 0) {
+ lv_draw_sdl_dec_dsc_userdata_t * ptr = (lv_draw_sdl_dec_dsc_userdata_t *) dsc->user_data;
+ *texture = ptr->texture;
+ rect = ptr->rect;
+ if(ptr->texture_managed) {
+ tex_flags |= LV_DRAW_SDL_CACHE_FLAG_MANAGED;
+ }
+ ptr->texture_referenced = true;
+ }
+ else {
+ *texture = upload_img_texture(ctx->renderer, dsc);
+ }
+#if LV_IMG_CACHE_DEF_SIZE == 0
+ lv_img_decoder_close(dsc);
+#endif
+ }
+ if(texture && cdsc) {
+ *header = SDL_malloc(sizeof(lv_draw_sdl_img_header_t));
+ SDL_memcpy(&(*header)->base, &cdsc->dec_dsc.header, sizeof(lv_img_header_t));
+ (*header)->rect = rect;
+ lv_draw_sdl_texture_cache_put_advanced(ctx, key, key_size, *texture, *header, SDL_free, tex_flags);
+ }
+ else {
+ lv_draw_sdl_texture_cache_put(ctx, key, key_size, NULL);
+ return false;
+ }
+ return true;
+}
+
+/**********************
+ * STATIC FUNCTIONS
+ **********************/
+
+static SDL_Texture * upload_img_texture(SDL_Renderer * renderer, lv_img_decoder_dsc_t * dsc)
+{
+ if(!dsc->img_data) {
+ return upload_img_texture_fallback(renderer, dsc);
+ }
+ bool chroma_keyed = dsc->header.cf == (uint32_t) LV_IMG_CF_TRUE_COLOR_CHROMA_KEYED;
+ int h = (int) dsc->header.h;
+ int w = (int) dsc->header.w;
+ void * data = (void *) dsc->img_data;
+ Uint32 rmask = 0x00FF0000;
+ Uint32 gmask = 0x0000FF00;
+ Uint32 bmask = 0x000000FF;
+ Uint32 amask = 0xFF000000;
+ if(chroma_keyed) {
+ amask = 0x00;
+ }
+ SDL_Surface * surface = SDL_CreateRGBSurfaceFrom(data, w, h, LV_COLOR_DEPTH, w * LV_COLOR_DEPTH / 8,
+ rmask, gmask, bmask, amask);
+ SDL_SetColorKey(surface, chroma_keyed, lv_color_to32(LV_COLOR_CHROMA_KEY));
+ SDL_Texture * texture = SDL_CreateTextureFromSurface(renderer, surface);
+ SDL_FreeSurface(surface);
+ return texture;
+}
+
+static SDL_Texture * upload_img_texture_fallback(SDL_Renderer * renderer, lv_img_decoder_dsc_t * dsc)
+{
+ lv_coord_t h = (lv_coord_t) dsc->header.h;
+ lv_coord_t w = (lv_coord_t) dsc->header.w;
+ uint8_t * data = lv_mem_buf_get(w * h * sizeof(lv_color_t));
+ for(lv_coord_t y = 0; y < h; y++) {
+ lv_img_decoder_read_line(dsc, 0, y, w, &data[y * w * sizeof(lv_color_t)]);
+ }
+ Uint32 rmask = 0x00FF0000;
+ Uint32 gmask = 0x0000FF00;
+ Uint32 bmask = 0x000000FF;
+ Uint32 amask = 0xFF000000;
+ SDL_Surface * surface = SDL_CreateRGBSurfaceFrom(data, w, h, LV_COLOR_DEPTH, w * LV_COLOR_DEPTH / 8,
+ rmask, gmask, bmask, amask);
+ SDL_SetColorKey(surface, SDL_TRUE, lv_color_to32(LV_COLOR_CHROMA_KEY));
+ SDL_Texture * texture = SDL_CreateTextureFromSurface(renderer, surface);
+ SDL_FreeSurface(surface);
+ lv_mem_buf_release(data);
+ return texture;
+}
+
+/**
+ * Check if there is only one radius mask
+ * @param radius Set to radius value if the only mask is a radius mask
+ * @return true if the only mask is a radius mask
+ */
+static bool check_mask_simple_radius(const lv_area_t * coords, lv_coord_t * radius)
+{
+ if(lv_draw_mask_get_cnt() != 1) return false;
+ for(uint8_t i = 0; i < _LV_MASK_MAX_NUM; i++) {
+ _lv_draw_mask_common_dsc_t * param = LV_GC_ROOT(_lv_draw_mask_list[i]).param;
+ if(param->type == LV_DRAW_MASK_TYPE_RADIUS) {
+ lv_draw_mask_radius_param_t * rparam = (lv_draw_mask_radius_param_t *) param;
+ if(rparam->cfg.outer) return false;
+ if(!_lv_area_is_equal(&rparam->cfg.rect, coords)) return false;
+ *radius = rparam->cfg.radius;
+ return true;
+ }
+ }
+ return false;
+}
+
+static void draw_img_simple(lv_draw_sdl_ctx_t * ctx, SDL_Texture * texture, const lv_draw_sdl_img_header_t * header,
+ const lv_draw_img_dsc_t * draw_dsc, const lv_area_t * coords, const lv_area_t * clip)
+{
+ apply_recolor_opa(texture, draw_dsc);
+ SDL_Point pivot = {.x = draw_dsc->pivot.x, .y = draw_dsc->pivot.y};
+
+ /*Image needs to be rotated, so we have to use clip rect which is slower*/
+ if(draw_dsc->angle != 0) {
+ /* No radius, set clip here */
+ SDL_Rect clip_rect;
+ lv_area_to_sdl_rect(clip, &clip_rect);
+ SDL_RenderSetClipRect(ctx->renderer, &clip_rect);
+ }
+ SDL_Rect src_rect, dst_rect;
+ calc_draw_part(texture, header, coords, clip, &src_rect, &dst_rect);
+ SDL_RenderCopyEx(ctx->renderer, texture, &src_rect, &dst_rect, draw_dsc->angle, &pivot, SDL_FLIP_NONE);
+ if(draw_dsc->angle != 0) {
+ SDL_RenderSetClipRect(ctx->renderer, NULL);
+ }
+}
+
+
+static void draw_img_rounded(lv_draw_sdl_ctx_t * ctx, SDL_Texture * texture, const lv_draw_sdl_img_header_t * header,
+ const lv_draw_img_dsc_t * draw_dsc, const lv_area_t * coords, const lv_area_t * clip,
+ lv_coord_t radius)
+{
+ const int w = lv_area_get_width(coords), h = lv_area_get_height(coords);
+ lv_coord_t real_radius = LV_MIN3(radius, w, h);
+ SDL_Texture * frag = img_rounded_frag_obtain(ctx, texture, header, w, h, real_radius);
+ apply_recolor_opa(frag, draw_dsc);
+ lv_draw_sdl_rect_bg_frag_draw_corners(ctx, frag, real_radius, coords, clip, true);
+
+ apply_recolor_opa(texture, draw_dsc);
+
+ SDL_Rect src_rect, dst_rect;
+ /* Draw 3 parts */
+ lv_area_t clip_tmp, part;
+ calc_draw_part(texture, header, coords, NULL, &src_rect, &dst_rect);
+ for(int i = w > h ? ROUNDED_IMG_PART_LEFT : ROUNDED_IMG_PART_TOP, j = i + 3; i <= j; i++) {
+ switch(i) {
+ case ROUNDED_IMG_PART_LEFT:
+ lv_area_set(&part, coords->x1, coords->y1 + radius, coords->x1 + radius - 1, coords->y2 - radius);
+ break;
+ case ROUNDED_IMG_PART_HCENTER:
+ lv_area_set(&part, coords->x1 + radius, coords->y1, coords->x2 - radius, coords->y2);
+ break;
+ case ROUNDED_IMG_PART_RIGHT:
+ lv_area_set(&part, coords->x2 - radius + 1, coords->y1 + radius, coords->x2, coords->y2 - radius);
+ break;
+ case ROUNDED_IMG_PART_TOP:
+ lv_area_set(&part, coords->x1 + radius, coords->y1, coords->x2 - radius, coords->y1 + radius - 1);
+ break;
+ case ROUNDED_IMG_PART_VCENTER:
+ lv_area_set(&part, coords->x1 + radius, coords->y2 - radius + 1, coords->x2 - radius, coords->y2);
+ break;
+ case ROUNDED_IMG_PART_BOTTOM:
+ lv_area_set(&part, coords->x1, coords->y1 + radius, coords->x2, coords->y2 - radius);
+ break;
+ default:
+ break;
+ }
+ if(!_lv_area_intersect(&clip_tmp, &part, clip)) continue;
+ SDL_Rect clip_rect;
+ lv_area_to_sdl_rect(&clip_tmp, &clip_rect);
+ SDL_RenderSetClipRect(ctx->renderer, &clip_rect);
+ SDL_RenderCopy(ctx->renderer, texture, &src_rect, &dst_rect);
+ }
+ SDL_RenderSetClipRect(ctx->renderer, NULL);
+}
+
+static void apply_recolor_opa(SDL_Texture * texture, const lv_draw_img_dsc_t * draw_dsc)
+{
+ if(draw_dsc->recolor_opa > LV_OPA_TRANSP) {
+ /* Draw with mixed recolor */
+ lv_color_t recolor = lv_color_mix(draw_dsc->recolor, lv_color_white(), draw_dsc->recolor_opa);
+ SDL_SetTextureColorMod(texture, recolor.ch.red, recolor.ch.green, recolor.ch.blue);
+ }
+ else {
+ /* Draw with no recolor */
+ SDL_SetTextureColorMod(texture, 0xFF, 0xFF, 0xFF);
+ }
+ SDL_SetTextureAlphaMod(texture, draw_dsc->opa);
+}
+
+static SDL_Texture * img_rounded_frag_obtain(lv_draw_sdl_ctx_t * ctx, SDL_Texture * texture,
+ const lv_draw_sdl_img_header_t * header, int w, int h, lv_coord_t radius)
+{
+ lv_draw_img_rounded_key_t key = rounded_key_create(texture, w, h, radius);
+ SDL_Texture * mask_frag = lv_draw_sdl_rect_bg_frag_obtain(ctx, radius);
+ SDL_Texture * img_frag = lv_draw_sdl_texture_cache_get(ctx, &key, sizeof(key), NULL);
+ if(img_frag == NULL) {
+ const lv_coord_t full_frag_size = radius * 2 + 3;
+ img_frag = SDL_CreateTexture(ctx->renderer, LV_DRAW_SDL_TEXTURE_FORMAT, SDL_TEXTUREACCESS_TARGET,
+ full_frag_size, full_frag_size);
+ SDL_SetTextureBlendMode(img_frag, SDL_BLENDMODE_BLEND);
+ SDL_Texture * old_target = SDL_GetRenderTarget(ctx->renderer);
+ SDL_SetRenderTarget(ctx->renderer, img_frag);
+ SDL_SetRenderDrawColor(ctx->renderer, 0, 0, 0, 0);
+ /* SDL_RenderClear is not working properly, so we overwrite the target with solid color */
+ SDL_SetRenderDrawBlendMode(ctx->renderer, SDL_BLENDMODE_NONE);
+ SDL_RenderFillRect(ctx->renderer, NULL);
+ SDL_SetRenderDrawBlendMode(ctx->renderer, SDL_BLENDMODE_BLEND);
+
+ lv_area_t coords = {0, 0, w - 1, h - 1}, clip;
+ lv_area_t frag_coords = {0, 0, full_frag_size - 1, full_frag_size - 1};
+ lv_draw_sdl_rect_bg_frag_draw_corners(ctx, mask_frag, radius, &frag_coords, NULL, false);
+
+ SDL_SetTextureAlphaMod(texture, 0xFF);
+ SDL_SetTextureColorMod(texture, 0xFF, 0xFF, 0xFF);
+#if LV_GPU_SDL_CUSTOM_BLEND_MODE
+ SDL_BlendMode blend_mode = SDL_ComposeCustomBlendMode(SDL_BLENDFACTOR_ONE, SDL_BLENDFACTOR_ZERO,
+ SDL_BLENDOPERATION_ADD, SDL_BLENDFACTOR_DST_ALPHA,
+ SDL_BLENDFACTOR_ZERO, SDL_BLENDOPERATION_ADD);
+ SDL_SetTextureBlendMode(texture, blend_mode);
+#else
+ SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_MOD);
+#endif
+ SDL_Rect srcrect, cliprect, dstrect = {0, 0, radius, radius};
+
+ cliprect.w = cliprect.h = radius;
+ for(int i = 0; i <= ROUNDED_IMG_CORNER_BOTTOM_LEFT; i++) {
+ switch(i) {
+ case ROUNDED_IMG_CORNER_TOP_LEFT:
+ cliprect.x = 0;
+ cliprect.y = 0;
+ lv_area_align(&frag_coords, &coords, LV_ALIGN_TOP_LEFT, 0, 0);
+ break;
+ case ROUNDED_IMG_CORNER_TOP_RIGHT:
+ cliprect.x = full_frag_size - radius;
+ cliprect.y = 0;
+ lv_area_align(&frag_coords, &coords, LV_ALIGN_TOP_RIGHT, 0, 0);
+ break;
+ case ROUNDED_IMG_CORNER_BOTTOM_RIGHT:
+ cliprect.x = full_frag_size - radius;
+ cliprect.y = full_frag_size - radius;
+ lv_area_align(&frag_coords, &coords, LV_ALIGN_BOTTOM_RIGHT, 0, 0);
+ break;
+ case ROUNDED_IMG_CORNER_BOTTOM_LEFT:
+ cliprect.x = 0;
+ cliprect.y = full_frag_size - radius;
+ lv_area_align(&frag_coords, &coords, LV_ALIGN_BOTTOM_LEFT, 0, 0);
+ break;
+ default:
+ break;
+ }
+ calc_draw_part(texture, header, &coords, NULL, &srcrect, &dstrect);
+ SDL_RenderSetClipRect(ctx->renderer, &cliprect);
+ SDL_RenderCopy(ctx->renderer, texture, &srcrect, &dstrect);
+ }
+ SDL_RenderSetClipRect(ctx->renderer, NULL);
+
+ SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND);
+
+ SDL_SetRenderTarget(ctx->renderer, old_target);
+ lv_draw_sdl_texture_cache_put(ctx, &key, sizeof(key), img_frag);
+ }
+ return img_frag;
+}
+
+static lv_draw_img_rounded_key_t rounded_key_create(const SDL_Texture * texture, lv_coord_t w, lv_coord_t h,
+ lv_coord_t radius)
+{
+ lv_draw_img_rounded_key_t key;
+ SDL_memset(&key, 0, sizeof(key));
+ key.magic = LV_GPU_CACHE_KEY_MAGIC_IMG_ROUNDED_CORNERS;
+ key.texture = texture;
+ key.w = w;
+ key.h = h;
+ key.radius = radius;
+ return key;
+}
+
+#endif /*LV_USE_GPU_SDL*/
diff --git a/lib/lvgl/src/draw/sdl/lv_draw_sdl_img.h b/lib/lvgl/src/draw/sdl/lv_draw_sdl_img.h
new file mode 100644
index 00000000..0e270277
--- /dev/null
+++ b/lib/lvgl/src/draw/sdl/lv_draw_sdl_img.h
@@ -0,0 +1,72 @@
+/**
+ * @file lv_draw_sdl_img.h
+ *
+ */
+
+#ifndef LV_DRAW_SDL_IMG_H
+#define LV_DRAW_SDL_IMG_H
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*********************
+ * INCLUDES
+ *********************/
+#include "../../lv_conf_internal.h"
+
+#if LV_USE_GPU_SDL
+
+#include LV_GPU_SDL_INCLUDE_PATH
+
+#include "../lv_draw.h"
+
+#include "lv_draw_sdl_texture_cache.h"
+
+/*********************
+ * DEFINES
+ *********************/
+
+/**********************
+ * TYPEDEFS
+ **********************/
+
+typedef struct lv_draw_sdl_img_header_t {
+ lv_img_header_t base;
+ SDL_Rect rect;
+} lv_draw_sdl_img_header_t;
+
+/**********************
+ * GLOBAL PROTOTYPES
+ **********************/
+
+/*======================
+ * Add/remove functions
+ *=====================*/
+
+/*=====================
+ * Setter functions
+ *====================*/
+
+/*=====================
+ * Getter functions
+ *====================*/
+
+/*=====================
+ * Other functions
+ *====================*/
+bool lv_draw_sdl_img_load_texture(lv_draw_sdl_ctx_t * ctx, lv_draw_sdl_cache_key_head_img_t * key, size_t key_size,
+ const void * src, int32_t frame_id, SDL_Texture ** texture,
+ lv_draw_sdl_img_header_t ** header);
+/**********************
+ * MACROS
+ **********************/
+
+#endif /*LV_USE_GPU_SDL*/
+
+#ifdef __cplusplus
+} /*extern "C"*/
+#endif
+
+#endif /*LV_DRAW_SDL_IMG_H*/
diff --git a/lib/lvgl/src/draw/sdl/lv_draw_sdl_label.c b/lib/lvgl/src/draw/sdl/lv_draw_sdl_label.c
new file mode 100644
index 00000000..b8c79ae4
--- /dev/null
+++ b/lib/lvgl/src/draw/sdl/lv_draw_sdl_label.c
@@ -0,0 +1,176 @@
+/**
+ * @file lv_draw_sdl_label.c
+ *
+ */
+
+/*********************
+ * INCLUDES
+ *********************/
+
+#include "../../lv_conf_internal.h"
+
+#if LV_USE_GPU_SDL
+
+#include LV_GPU_SDL_INCLUDE_PATH
+
+#include "../lv_draw_label.h"
+#include "../../misc/lv_utils.h"
+
+#include "lv_draw_sdl_utils.h"
+#include "lv_draw_sdl_texture_cache.h"
+#include "lv_draw_sdl_composite.h"
+#include "lv_draw_sdl_layer.h"
+
+/*********************
+ * DEFINES
+ *********************/
+
+/**********************
+ * TYPEDEFS
+ **********************/
+
+typedef struct {
+ lv_sdl_cache_key_magic_t magic;
+ const lv_font_t * font_p;
+ uint32_t letter;
+} lv_font_glyph_key_t;
+
+/**********************
+ * STATIC PROTOTYPES
+ **********************/
+
+static lv_font_glyph_key_t font_key_glyph_create(const lv_font_t * font_p, uint32_t letter);
+
+
+/**********************
+ * STATIC VARIABLES
+ **********************/
+
+/**********************
+ * MACROS
+ **********************/
+
+/**********************
+ * GLOBAL FUNCTIONS
+ **********************/
+
+void lv_draw_sdl_draw_letter(lv_draw_ctx_t * draw_ctx, const lv_draw_label_dsc_t * dsc, const lv_point_t * pos_p,
+ uint32_t letter)
+{
+ const lv_area_t * clip_area = draw_ctx->clip_area;
+ const lv_font_t * font_p = dsc->font;
+ lv_opa_t opa = dsc->opa;
+ lv_color_t color = dsc->color;
+ if(opa < LV_OPA_MIN) return;
+ if(opa > LV_OPA_MAX) opa = LV_OPA_COVER;
+
+ if(font_p == NULL) {
+ LV_LOG_WARN("lv_draw_letter: font is NULL");
+ return;
+ }
+
+ lv_font_glyph_dsc_t g;
+ bool g_ret = lv_font_get_glyph_dsc(font_p, &g, letter, '\0');
+ if(g_ret == false) {
+ /*Add warning if the dsc is not found
+ *but do not print warning for non printable ASCII chars (e.g. '\n')*/
+ if(letter >= 0x20 &&
+ letter != 0xf8ff && /*LV_SYMBOL_DUMMY*/
+ letter != 0x200c) { /*ZERO WIDTH NON-JOINER*/
+ LV_LOG_WARN("lv_draw_letter: glyph dsc. not found for U+%X", letter);
+
+ /* 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);
+ }
+ return;
+ }
+
+ /*Don't draw anything if the character is empty. E.g. space*/
+ if((g.box_h == 0) || (g.box_w == 0)) return;
+
+ int32_t pos_x = pos_p->x + g.ofs_x;
+ int32_t pos_y = pos_p->y + (font_p->line_height - font_p->base_line) - g.box_h - g.ofs_y;
+
+ const lv_area_t letter_area = {pos_x, pos_y, pos_x + g.box_w - 1, pos_y + g.box_h - 1};
+ lv_area_t draw_area;
+
+ /*If the letter is completely out of mask don't draw it*/
+ if(!_lv_area_intersect(&draw_area, &letter_area, clip_area)) {
+ return;
+ }
+
+ lv_draw_sdl_ctx_t * ctx = (lv_draw_sdl_ctx_t *) draw_ctx;
+ SDL_Renderer * renderer = ctx->renderer;
+
+ lv_font_glyph_key_t glyph_key = font_key_glyph_create(font_p, letter);
+ bool glyph_found = false;
+ SDL_Texture * texture = lv_draw_sdl_texture_cache_get(ctx, &glyph_key, sizeof(glyph_key), &glyph_found);
+ if(!glyph_found) {
+ if(g.resolved_font) {
+ font_p = g.resolved_font;
+ }
+ const uint8_t * bmp = lv_font_get_glyph_bitmap(font_p, letter);
+ uint8_t * buf = lv_mem_alloc(g.box_w * g.box_h);
+ lv_sdl_to_8bpp(buf, bmp, g.box_w, g.box_h, g.box_w, g.bpp);
+ SDL_Surface * mask = lv_sdl_create_opa_surface(buf, g.box_w, g.box_h, g.box_w);
+ texture = SDL_CreateTextureFromSurface(renderer, mask);
+ SDL_FreeSurface(mask);
+ lv_mem_free(buf);
+ lv_draw_sdl_texture_cache_put(ctx, &glyph_key, sizeof(glyph_key), texture);
+ }
+ if(!texture) {
+ return;
+ }
+
+ lv_area_t t_letter = letter_area, t_clip = *clip_area, apply_area;
+ bool has_composite = lv_draw_sdl_composite_begin(ctx, &letter_area, clip_area, NULL, dsc->blend_mode, &t_letter,
+ &t_clip, &apply_area);
+
+ lv_draw_sdl_transform_areas_offset(ctx, has_composite, &apply_area, &t_letter, &t_clip);
+
+ /*If the letter is completely out of mask don't draw it*/
+ if(!_lv_area_intersect(&draw_area, &t_letter, &t_clip)) {
+ return;
+ }
+ SDL_Rect srcrect, dstrect;
+ lv_area_to_sdl_rect(&draw_area, &dstrect);
+ srcrect.x = draw_area.x1 - t_letter.x1;
+ srcrect.y = draw_area.y1 - t_letter.y1;
+ srcrect.w = dstrect.w;
+ srcrect.h = dstrect.h;
+ SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND);
+ SDL_SetTextureAlphaMod(texture, opa);
+ SDL_SetTextureColorMod(texture, color.ch.red, color.ch.green, color.ch.blue);
+ SDL_RenderCopy(renderer, texture, &srcrect, &dstrect);
+
+ lv_draw_sdl_composite_end(ctx, &apply_area, dsc->blend_mode);
+}
+
+/**********************
+ * STATIC FUNCTIONS
+ **********************/
+
+static lv_font_glyph_key_t font_key_glyph_create(const lv_font_t * font_p, uint32_t letter)
+{
+ lv_font_glyph_key_t key;
+ /* VERY IMPORTANT! Padding between members is uninitialized, so we have to wipe them manually */
+ SDL_memset(&key, 0, sizeof(key));
+ key.magic = LV_GPU_CACHE_KEY_MAGIC_FONT_GLYPH;
+ key.font_p = font_p;
+ key.letter = letter;
+ return key;
+}
+
+#endif /*LV_USE_GPU_SDL*/
diff --git a/lib/lvgl/src/draw/sdl/lv_draw_sdl_layer.c b/lib/lvgl/src/draw/sdl/lv_draw_sdl_layer.c
new file mode 100644
index 00000000..ae5c327a
--- /dev/null
+++ b/lib/lvgl/src/draw/sdl/lv_draw_sdl_layer.c
@@ -0,0 +1,141 @@
+/**
+ * @file lv_draw_sdl_refr.c
+ *
+ */
+
+/*********************
+ * INCLUDES
+ *********************/
+#include "../../lv_conf_internal.h"
+
+#if LV_USE_GPU_SDL
+
+#include "../../core/lv_refr.h"
+
+#include "lv_draw_sdl.h"
+#include "lv_draw_sdl_priv.h"
+#include "lv_draw_sdl_composite.h"
+#include "lv_draw_sdl_utils.h"
+#include "lv_draw_sdl_layer.h"
+
+/*********************
+ * DEFINES
+ *********************/
+
+/**********************
+ * TYPEDEFS
+ **********************/
+
+/**********************
+ * STATIC PROTOTYPES
+ **********************/
+
+/**********************
+ * STATIC VARIABLES
+ **********************/
+
+/**********************
+ * MACROS
+ **********************/
+
+/**********************
+ * GLOBAL FUNCTIONS
+ **********************/
+
+lv_draw_layer_ctx_t * lv_draw_sdl_layer_init(lv_draw_ctx_t * draw_ctx, lv_draw_layer_ctx_t * layer_ctx,
+ lv_draw_layer_flags_t flags)
+{
+ lv_draw_sdl_ctx_t * ctx = (lv_draw_sdl_ctx_t *) draw_ctx;
+ SDL_Renderer * renderer = ctx->renderer;
+
+ lv_draw_sdl_layer_ctx_t * transform_ctx = (lv_draw_sdl_layer_ctx_t *) layer_ctx;
+
+ transform_ctx->flags = flags;
+ transform_ctx->orig_target = SDL_GetRenderTarget(renderer);
+
+ lv_coord_t target_w = lv_area_get_width(&layer_ctx->area_full);
+ lv_coord_t target_h = lv_area_get_height(&layer_ctx->area_full);
+
+ enum lv_draw_sdl_composite_texture_id_t texture_id = LV_DRAW_SDL_COMPOSITE_TEXTURE_ID_TRANSFORM0 +
+ ctx->internals->transform_count;
+ transform_ctx->target = lv_draw_sdl_composite_texture_obtain(ctx, texture_id, target_w, target_h);
+ transform_ctx->target_rect.x = 0;
+ transform_ctx->target_rect.y = 0;
+ transform_ctx->target_rect.w = target_w;
+ transform_ctx->target_rect.h = target_h;
+
+ layer_ctx->max_row_with_alpha = target_h;
+ layer_ctx->max_row_with_no_alpha = target_h;
+
+ SDL_SetTextureBlendMode(transform_ctx->target, SDL_BLENDMODE_BLEND);
+ SDL_SetRenderTarget(renderer, transform_ctx->target);
+
+ /* SDL_RenderClear is not working properly, so we overwrite the target with solid color */
+ SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_NONE);
+ SDL_SetRenderDrawColor(renderer, 0, 0, 0, 0);
+ SDL_RenderFillRect(renderer, NULL);
+ SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_BLEND);
+
+ /* Set proper drawing context for transform layer */
+ ctx->internals->transform_count += 1;
+ draw_ctx->buf_area = &layer_ctx->area_full;
+ draw_ctx->clip_area = &layer_ctx->area_full;
+
+ return layer_ctx;
+}
+
+void lv_draw_sdl_layer_blend(lv_draw_ctx_t * draw_ctx, lv_draw_layer_ctx_t * layer_ctx,
+ const lv_draw_img_dsc_t * draw_dsc)
+{
+ lv_draw_sdl_ctx_t * ctx = (lv_draw_sdl_ctx_t *) draw_ctx;
+ lv_draw_sdl_layer_ctx_t * transform_ctx = (lv_draw_sdl_layer_ctx_t *) layer_ctx;
+
+ SDL_Renderer * renderer = ctx->renderer;
+
+ SDL_Rect trans_rect;
+
+ if(transform_ctx->flags & LV_DRAW_LAYER_FLAG_CAN_SUBDIVIDE) {
+ lv_area_zoom_to_sdl_rect(&layer_ctx->area_act, &trans_rect, draw_dsc->zoom, &draw_dsc->pivot);
+ }
+ else {
+ lv_area_zoom_to_sdl_rect(&layer_ctx->area_full, &trans_rect, draw_dsc->zoom, &draw_dsc->pivot);
+ }
+
+ SDL_SetRenderTarget(renderer, transform_ctx->orig_target);
+
+ /*Render off-screen texture, transformed*/
+ SDL_Rect clip_rect;
+ lv_area_to_sdl_rect(layer_ctx->original.clip_area, &clip_rect);
+ SDL_Point center = {.x = draw_dsc->pivot.x, .y = draw_dsc->pivot.y};
+ SDL_RenderSetClipRect(renderer, &clip_rect);
+ SDL_SetTextureAlphaMod(transform_ctx->target, draw_dsc->opa);
+ SDL_RenderCopyEx(renderer, transform_ctx->target, &transform_ctx->target_rect, &trans_rect,
+ draw_dsc->angle, &center, SDL_FLIP_NONE);
+ SDL_RenderSetClipRect(renderer, NULL);
+}
+
+void lv_draw_sdl_layer_destroy(lv_draw_ctx_t * draw_ctx, lv_draw_layer_ctx_t * layer_ctx)
+{
+ lv_draw_sdl_ctx_t * ctx = (lv_draw_sdl_ctx_t *) draw_ctx;
+ ctx->internals->transform_count -= 1;
+}
+
+void lv_draw_sdl_transform_areas_offset(lv_draw_sdl_ctx_t * ctx, bool has_composite, lv_area_t * apply_area,
+ lv_area_t * coords, lv_area_t * clip)
+{
+ if(ctx->internals->transform_count == 0) {
+ return;
+ }
+ lv_area_t * area = ctx->base_draw.buf_area;
+ lv_area_move(coords, -area->x1, -area->y1);
+ lv_area_move(clip, -area->x1, -area->y1);
+ if(has_composite) {
+ lv_area_move(apply_area, -area->x1, -area->y1);
+ }
+}
+
+/**********************
+ * STATIC FUNCTIONS
+ **********************/
+
+#endif /*LV_USE_GPU_SDL*/
diff --git a/lib/lvgl/src/draw/sdl/lv_draw_sdl_layer.h b/lib/lvgl/src/draw/sdl/lv_draw_sdl_layer.h
new file mode 100644
index 00000000..b60303c4
--- /dev/null
+++ b/lib/lvgl/src/draw/sdl/lv_draw_sdl_layer.h
@@ -0,0 +1,55 @@
+/**
+ * @file lv_draw_sdl_refr.h
+ *
+ */
+
+#ifndef LV_TEMPL_H
+#define LV_TEMPL_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*********************
+ * INCLUDES
+ *********************/
+#include "lv_draw_sdl.h"
+
+/*********************
+ * DEFINES
+ *********************/
+
+/**********************
+ * TYPEDEFS
+ **********************/
+typedef struct _lv_draw_sdl_layer_ctx_t {
+ lv_draw_layer_ctx_t base;
+
+ SDL_Texture * orig_target;
+ SDL_Texture * target;
+ SDL_Rect target_rect;
+ lv_draw_layer_flags_t flags;
+} lv_draw_sdl_layer_ctx_t;
+/**********************
+ * GLOBAL PROTOTYPES
+ **********************/
+
+lv_draw_layer_ctx_t * lv_draw_sdl_layer_init(lv_draw_ctx_t * draw_ctx, lv_draw_layer_ctx_t * layer_ctx,
+ lv_draw_layer_flags_t flags);
+
+void lv_draw_sdl_layer_blend(lv_draw_ctx_t * draw_ctx, lv_draw_layer_ctx_t * transform_ctx,
+ const lv_draw_img_dsc_t * draw_dsc);
+
+void lv_draw_sdl_layer_destroy(lv_draw_ctx_t * draw_ctx, lv_draw_layer_ctx_t * layer_ctx);
+
+void lv_draw_sdl_transform_areas_offset(lv_draw_sdl_ctx_t * ctx, bool has_composite, lv_area_t * apply_area,
+ lv_area_t * coords, lv_area_t * clip);
+/**********************
+ * MACROS
+ **********************/
+
+#ifdef __cplusplus
+} /*extern "C"*/
+#endif
+
+#endif /*LV_TEMPL_H*/
diff --git a/lib/lvgl/src/draw/sdl/lv_draw_sdl_line.c b/lib/lvgl/src/draw/sdl/lv_draw_sdl_line.c
new file mode 100644
index 00000000..3a15d6d3
--- /dev/null
+++ b/lib/lvgl/src/draw/sdl/lv_draw_sdl_line.c
@@ -0,0 +1,157 @@
+/**
+ * @file lv_draw_sdl_line.c
+ *
+ */
+
+/*********************
+ * INCLUDES
+ *********************/
+#include "../../lv_conf_internal.h"
+
+#if LV_USE_GPU_SDL
+
+#include "lv_draw_sdl.h"
+#include "lv_draw_sdl_utils.h"
+#include "lv_draw_sdl_texture_cache.h"
+#include "lv_draw_sdl_composite.h"
+#include "lv_draw_sdl_mask.h"
+
+/*********************
+ * DEFINES
+ *********************/
+#define ROUND_START 0x01
+#define ROUND_END 0x02
+/**********************
+ * TYPEDEFS
+ **********************/
+
+typedef struct {
+ lv_sdl_cache_key_magic_t magic;
+ lv_coord_t length;
+ lv_coord_t width;
+ uint8_t round;
+} lv_draw_line_key_t;
+
+/**********************
+ * STATIC PROTOTYPES
+ **********************/
+
+/**********************
+ * STATIC VARIABLES
+ **********************/
+
+/**********************
+ * MACROS
+ **********************/
+
+static lv_draw_line_key_t line_key_create(const lv_draw_line_dsc_t * dsc, lv_coord_t length);
+
+static SDL_Texture * line_texture_create(lv_draw_sdl_ctx_t * sdl_ctx, const lv_draw_line_dsc_t * dsc,
+ lv_coord_t length);
+
+/**********************
+ * GLOBAL FUNCTIONS
+ **********************/
+void lv_draw_sdl_draw_line(lv_draw_ctx_t * draw_ctx, const lv_draw_line_dsc_t * dsc, const lv_point_t * point1,
+ const lv_point_t * point2)
+{
+ lv_draw_sdl_ctx_t * sdl_ctx = (lv_draw_sdl_ctx_t *) draw_ctx;
+ SDL_Renderer * renderer = sdl_ctx->renderer;
+ lv_coord_t x1 = point1->x, x2 = point2->x, y1 = point1->y, y2 = point2->y;
+ double length = SDL_sqrt(SDL_pow(x2 - x1, 2) + SDL_pow(y2 - y1, 2));
+ if(length - (long) length > 0.5) {
+ length = (long) length + 1;
+ }
+
+ double angle = SDL_atan2(y2 - y1, x2 - x1) * 180 / M_PI;
+ lv_draw_line_key_t key = line_key_create(dsc, (lv_coord_t) length);
+ SDL_Texture * texture = lv_draw_sdl_texture_cache_get(sdl_ctx, &key, sizeof(key), NULL);
+ if(!texture) {
+ texture = line_texture_create(sdl_ctx, dsc, (lv_coord_t) length);
+ lv_draw_sdl_texture_cache_put(sdl_ctx, &key, sizeof(key), texture);
+ }
+
+ lv_area_t coords = {x1, y1, x2, y2};
+ const lv_area_t * clip = draw_ctx->clip_area;
+
+ SDL_Rect coords_r, clip_r;
+ lv_area_to_sdl_rect(&coords, &coords_r);
+ lv_area_to_sdl_rect(clip, &clip_r);
+
+ lv_area_t t_coords = coords, t_clip = *clip, apply_area;
+ lv_area_t extension = {dsc->width / 2, dsc->width / 2, dsc->width / 2, dsc->width / 2};
+ lv_draw_sdl_composite_begin(sdl_ctx, &coords, clip, &extension, dsc->blend_mode, &t_coords, &t_clip,
+ &apply_area);
+
+ SDL_Color color;
+ lv_color_to_sdl_color(&dsc->color, &color);
+
+ SDL_SetTextureColorMod(texture, color.r, color.g, color.b);
+ SDL_SetTextureAlphaMod(texture, dsc->opa);
+ SDL_Rect srcrect = {0, 0, (int) length + dsc->width + 2, dsc->width + 2},
+ dstrect = {t_coords.x1 - 1 - dsc->width / 2, t_coords.y1 - 1, srcrect.w, srcrect.h};
+ SDL_Point center = {1 + dsc->width / 2, 1 + dsc->width / 2};
+
+ SDL_Rect clip_rect;
+ lv_area_to_sdl_rect(&t_clip, &clip_rect);
+ if(!SDL_RectEquals(&clip_rect, &dstrect) || angle != 0) {
+ SDL_RenderSetClipRect(renderer, &clip_rect);
+ }
+ SDL_RenderCopyEx(renderer, texture, &srcrect, &dstrect, angle, &center, 0);
+ SDL_RenderSetClipRect(renderer, NULL);
+
+ lv_draw_sdl_composite_end(sdl_ctx, &apply_area, dsc->blend_mode);
+}
+
+/**********************
+ * STATIC FUNCTIONS
+ **********************/
+
+static lv_draw_line_key_t line_key_create(const lv_draw_line_dsc_t * dsc, lv_coord_t length)
+{
+ lv_draw_line_key_t key;
+ lv_memset_00(&key, sizeof(lv_draw_line_key_t));
+ key.magic = LV_GPU_CACHE_KEY_MAGIC_LINE;
+ key.length = length;
+ key.width = dsc->width;
+ key.round = (dsc->round_start ? ROUND_START : 0) | (dsc->round_end ? ROUND_END : 0);
+ return key;
+}
+
+static SDL_Texture * line_texture_create(lv_draw_sdl_ctx_t * sdl_ctx, const lv_draw_line_dsc_t * dsc, lv_coord_t length)
+{
+ SDL_Texture * texture = SDL_CreateTexture(sdl_ctx->renderer, LV_DRAW_SDL_TEXTURE_FORMAT, SDL_TEXTUREACCESS_TARGET,
+ length + dsc->width + 2, dsc->width + 2);
+ SDL_Texture * target = SDL_GetRenderTarget(sdl_ctx->renderer);
+ SDL_SetRenderTarget(sdl_ctx->renderer, texture);
+ SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND);
+ SDL_SetRenderDrawColor(sdl_ctx->renderer, 0xFF, 0xFF, 0xFF, 0x0);
+ /* SDL_RenderClear is not working properly, so we overwrite the target with solid color */
+ SDL_SetRenderDrawBlendMode(sdl_ctx->renderer, SDL_BLENDMODE_NONE);
+ SDL_RenderFillRect(sdl_ctx->renderer, NULL);
+ SDL_SetRenderDrawBlendMode(sdl_ctx->renderer, SDL_BLENDMODE_BLEND);
+ SDL_SetRenderDrawColor(sdl_ctx->renderer, 0xFF, 0xFF, 0xFF, 0xFF);
+ SDL_Rect line_rect = {1 + dsc->width / 2, 1, length, dsc->width};
+ SDL_RenderFillRect(sdl_ctx->renderer, &line_rect);
+ if(dsc->round_start || dsc->round_end) {
+ lv_draw_mask_radius_param_t param;
+ lv_area_t round_area = {0, 0, dsc->width - 1, dsc->width - 1};
+ lv_draw_mask_radius_init(&param, &round_area, LV_RADIUS_CIRCLE, false);
+
+ int16_t mask_id = lv_draw_mask_add(&param, NULL);
+ SDL_Texture * round_texture = lv_draw_sdl_mask_dump_texture(sdl_ctx->renderer, &round_area, &mask_id, 1);
+ lv_draw_mask_remove_id(mask_id);
+
+ SDL_Rect round_src = {0, 0, dsc->width, dsc->width};
+ SDL_Rect round_dst = {line_rect.x - dsc->width / 2, 1, dsc->width, dsc->width};
+ SDL_RenderCopy(sdl_ctx->renderer, round_texture, &round_src, &round_dst);
+ round_dst.x = line_rect.w + dsc->width / 2;
+ SDL_RenderCopy(sdl_ctx->renderer, round_texture, &round_src, &round_dst);
+ SDL_DestroyTexture(round_texture);
+ }
+
+ SDL_SetRenderTarget(sdl_ctx->renderer, target);
+ return texture;
+}
+
+#endif /*LV_USE_GPU_SDL*/
diff --git a/lib/lvgl/src/draw/sdl/lv_draw_sdl_mask.c b/lib/lvgl/src/draw/sdl/lv_draw_sdl_mask.c
new file mode 100644
index 00000000..7bb5bbb3
--- /dev/null
+++ b/lib/lvgl/src/draw/sdl/lv_draw_sdl_mask.c
@@ -0,0 +1,84 @@
+/**
+ * @file lv_draw_sdl_mask.c
+ *
+ */
+
+/*********************
+ * INCLUDES
+ *********************/
+#include "../../lv_conf_internal.h"
+
+#if LV_USE_GPU_SDL
+
+#include "../../misc/lv_gc.h"
+#include "lv_draw_sdl_mask.h"
+#include "lv_draw_sdl_utils.h"
+
+/*********************
+ * DEFINES
+ *********************/
+#ifndef HAVE_SDL_CUSTOM_BLEND_MODE
+ #define HAVE_SDL_CUSTOM_BLEND_MODE (SDL_VERSION_ATLEAST(2, 0, 6))
+#endif
+
+/**********************
+ * TYPEDEFS
+ **********************/
+
+/**********************
+ * STATIC PROTOTYPES
+ **********************/
+
+/**********************
+ * STATIC VARIABLES
+ **********************/
+
+/**********************
+ * MACROS
+ **********************/
+
+/**********************
+ * GLOBAL FUNCTIONS
+ **********************/
+
+lv_opa_t * lv_draw_sdl_mask_dump_opa(const lv_area_t * coords, const int16_t * ids, int16_t ids_count)
+{
+ SDL_assert(coords->x2 >= coords->x1);
+ SDL_assert(coords->y2 >= coords->y1);
+ lv_coord_t w = lv_area_get_width(coords), h = lv_area_get_height(coords);
+ lv_opa_t * mask_buf = lv_mem_buf_get(w * h);
+ for(lv_coord_t y = 0; y < h; y++) {
+ lv_opa_t * line_buf = &mask_buf[y * w];
+ lv_memset_ff(line_buf, w);
+ lv_coord_t abs_x = (lv_coord_t) coords->x1, abs_y = (lv_coord_t)(y + coords->y1), len = (lv_coord_t) w;
+ lv_draw_mask_res_t res;
+ if(ids) {
+ res = lv_draw_mask_apply_ids(line_buf, abs_x, abs_y, len, ids, ids_count);
+ }
+ else {
+ res = lv_draw_mask_apply(line_buf, abs_x, abs_y, len);
+ }
+ if(res == LV_DRAW_MASK_RES_TRANSP) {
+ lv_memset_00(line_buf, w);
+ }
+ }
+ return mask_buf;
+}
+
+SDL_Texture * lv_draw_sdl_mask_dump_texture(SDL_Renderer * renderer, const lv_area_t * coords, const int16_t * ids,
+ int16_t ids_count)
+{
+ lv_coord_t w = lv_area_get_width(coords), h = lv_area_get_height(coords);
+ lv_opa_t * mask_buf = lv_draw_sdl_mask_dump_opa(coords, ids, ids_count);
+ SDL_Surface * surface = lv_sdl_create_opa_surface(mask_buf, w, h, w);
+ lv_mem_buf_release(mask_buf);
+ SDL_Texture * texture = SDL_CreateTextureFromSurface(renderer, surface);
+ SDL_FreeSurface(surface);
+ return texture;
+}
+
+/**********************
+ * STATIC FUNCTIONS
+ **********************/
+
+#endif /*LV_USE_GPU_SDL*/
diff --git a/lib/lvgl/src/draw/sdl/lv_draw_sdl_mask.h b/lib/lvgl/src/draw/sdl/lv_draw_sdl_mask.h
new file mode 100644
index 00000000..a562d73d
--- /dev/null
+++ b/lib/lvgl/src/draw/sdl/lv_draw_sdl_mask.h
@@ -0,0 +1,51 @@
+/**
+ * @file lv_draw_sdl_mask.h
+ *
+ */
+
+#ifndef LV_DRAW_SDL_MASK_H
+#define LV_DRAW_SDL_MASK_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*********************
+ * INCLUDES
+ *********************/
+
+#include "../../lv_conf_internal.h"
+
+#include LV_GPU_SDL_INCLUDE_PATH
+
+#include "lv_draw_sdl.h"
+#include "../../misc/lv_area.h"
+#include "../../misc/lv_color.h"
+
+/*********************
+ * DEFINES
+ *********************/
+
+/**********************
+ * TYPEDEFS
+ **********************/
+
+/**********************
+ * GLOBAL PROTOTYPES
+ **********************/
+
+lv_opa_t * lv_draw_sdl_mask_dump_opa(const lv_area_t * coords, const int16_t * ids, int16_t ids_count);
+
+SDL_Texture * lv_draw_sdl_mask_dump_texture(SDL_Renderer * renderer, const lv_area_t * coords, const int16_t * ids,
+ int16_t ids_count);
+
+
+/**********************
+ * MACROS
+ **********************/
+
+#ifdef __cplusplus
+} /*extern "C"*/
+#endif
+
+#endif /*LV_DRAW_SDL_MASK_H*/
diff --git a/lib/lvgl/src/draw/sdl/lv_draw_sdl_polygon.c b/lib/lvgl/src/draw/sdl/lv_draw_sdl_polygon.c
new file mode 100644
index 00000000..c5df50cb
--- /dev/null
+++ b/lib/lvgl/src/draw/sdl/lv_draw_sdl_polygon.c
@@ -0,0 +1,133 @@
+/**
+ * @file lv_draw_sdl_polygon.c
+ *
+ */
+
+/*********************
+ * INCLUDES
+ *********************/
+#include "../../lv_conf_internal.h"
+
+#if LV_USE_GPU_SDL
+
+#include "lv_draw_sdl.h"
+#include "lv_draw_sdl_utils.h"
+#include "lv_draw_sdl_texture_cache.h"
+#include "lv_draw_sdl_composite.h"
+
+/*********************
+ * DEFINES
+ *********************/
+
+/**********************
+ * TYPEDEFS
+ **********************/
+
+/**********************
+ * STATIC PROTOTYPES
+ **********************/
+
+/**********************
+ * STATIC VARIABLES
+ **********************/
+
+/**********************
+ * MACROS
+ **********************/
+
+static void dump_masks(SDL_Texture * texture, const lv_area_t * coords);
+
+/**********************
+ * GLOBAL FUNCTIONS
+ **********************/
+void lv_draw_sdl_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(point_cnt < 3) return;
+ if(points == NULL) return;
+
+ lv_draw_mask_polygon_param_t polygon_param;
+ lv_draw_mask_polygon_init(&polygon_param, points, point_cnt);
+
+ if(polygon_param.cfg.point_cnt < 3) {
+ lv_draw_mask_free_param(&polygon_param);
+ return;
+ }
+
+ lv_area_t poly_coords = {.x1 = LV_COORD_MAX, .y1 = LV_COORD_MAX, .x2 = LV_COORD_MIN, .y2 = LV_COORD_MIN};
+
+ uint16_t i;
+ for(i = 0; i < point_cnt; i++) {
+ poly_coords.x1 = LV_MIN(poly_coords.x1, polygon_param.cfg.points[i].x);
+ poly_coords.y1 = LV_MIN(poly_coords.y1, polygon_param.cfg.points[i].y);
+ poly_coords.x2 = LV_MAX(poly_coords.x2, polygon_param.cfg.points[i].x);
+ poly_coords.y2 = LV_MAX(poly_coords.y2, polygon_param.cfg.points[i].y);
+ }
+
+ bool is_common;
+ lv_area_t draw_area;
+ is_common = _lv_area_intersect(&draw_area, &poly_coords, draw_ctx->clip_area);
+ if(!is_common) {
+ lv_draw_mask_free_param(&polygon_param);
+ return;
+ }
+
+ lv_draw_sdl_ctx_t * ctx = (lv_draw_sdl_ctx_t *) draw_ctx;
+
+ int16_t mask_id = lv_draw_mask_add(&polygon_param, NULL);
+
+ lv_coord_t w = lv_area_get_width(&draw_area), h = lv_area_get_height(&draw_area);
+ SDL_Texture * texture = lv_draw_sdl_composite_texture_obtain(ctx, LV_DRAW_SDL_COMPOSITE_TEXTURE_ID_STREAM1, w, h);
+ SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND);
+ dump_masks(texture, &draw_area);
+
+ lv_draw_mask_remove_id(mask_id);
+ lv_draw_mask_free_param(&polygon_param);
+
+ SDL_Rect srcrect = {0, 0, w, h}, dstrect;
+ lv_area_to_sdl_rect(&draw_area, &dstrect);
+ SDL_Color color;
+ lv_color_to_sdl_color(&draw_dsc->bg_color, &color);
+ SDL_SetTextureColorMod(texture, color.r, color.g, color.b);
+ SDL_SetTextureAlphaMod(texture, draw_dsc->bg_opa);
+ SDL_RenderCopy(ctx->renderer, texture, &srcrect, &dstrect);
+}
+
+/**********************
+ * STATIC FUNCTIONS
+ **********************/
+
+static void dump_masks(SDL_Texture * texture, const lv_area_t * coords)
+{
+ lv_coord_t w = lv_area_get_width(coords), h = lv_area_get_height(coords);
+ SDL_assert(w > 0 && h > 0);
+ SDL_Rect rect = {0, 0, w, h};
+ uint8_t * pixels;
+ int pitch;
+ if(SDL_LockTexture(texture, &rect, (void **) &pixels, &pitch) != 0) return;
+
+ lv_opa_t * line_buf = lv_mem_buf_get(rect.w);
+ for(lv_coord_t y = 0; y < rect.h; y++) {
+ lv_memset_ff(line_buf, rect.w);
+ lv_coord_t abs_x = (lv_coord_t) coords->x1, abs_y = (lv_coord_t)(y + coords->y1), len = (lv_coord_t) rect.w;
+ lv_draw_mask_res_t res;
+ res = lv_draw_mask_apply(line_buf, abs_x, abs_y, len);
+ if(res == LV_DRAW_MASK_RES_TRANSP) {
+ lv_memset_00(&pixels[y * pitch], 4 * rect.w);
+ }
+ else if(res == LV_DRAW_MASK_RES_FULL_COVER) {
+ lv_memset_ff(&pixels[y * pitch], 4 * rect.w);
+ }
+ else {
+ for(int x = 0; x < rect.w; x++) {
+ uint8_t * pixel = &pixels[y * pitch + x * 4];
+ *pixel = line_buf[x];
+ pixel[1] = pixel[2] = pixel[3] = 0xFF;
+ }
+ }
+ }
+ lv_mem_buf_release(line_buf);
+ SDL_UnlockTexture(texture);
+}
+
+#endif /*LV_USE_GPU_SDL*/
diff --git a/lib/lvgl/src/draw/sdl/lv_draw_sdl_priv.h b/lib/lvgl/src/draw/sdl/lv_draw_sdl_priv.h
new file mode 100644
index 00000000..24a87621
--- /dev/null
+++ b/lib/lvgl/src/draw/sdl/lv_draw_sdl_priv.h
@@ -0,0 +1,72 @@
+/**
+ * @file lv_draw_sdl_priv.h
+ *
+ */
+
+#ifndef LV_DRAW_SDL_PRIV_H
+#define LV_DRAW_SDL_PRIV_H
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*********************
+ * INCLUDES
+ *********************/
+#include "../../lv_conf_internal.h"
+
+#if LV_USE_GPU_SDL
+
+#include LV_GPU_SDL_INCLUDE_PATH
+
+#include "../lv_draw.h"
+#include "../../misc/lv_lru.h"
+
+/*********************
+ * DEFINES
+ *********************/
+
+/**********************
+ * TYPEDEFS
+ **********************/
+
+typedef struct lv_draw_sdl_context_internals_t {
+ lv_lru_t * texture_cache;
+ SDL_Texture * mask;
+ SDL_Texture * composition;
+ SDL_Texture * target_backup;
+ uint8_t transform_count;
+} lv_draw_sdl_context_internals_t;
+
+/**********************
+ * GLOBAL PROTOTYPES
+ **********************/
+
+/*======================
+ * Add/remove functions
+ *=====================*/
+
+/*=====================
+ * Setter functions
+ *====================*/
+
+/*=====================
+ * Getter functions
+ *====================*/
+
+/*=====================
+ * Other functions
+ *====================*/
+
+/**********************
+ * MACROS
+ **********************/
+
+#endif /*LV_USE_GPU_SDL*/
+
+#ifdef __cplusplus
+} /*extern "C"*/
+#endif
+
+#endif /*LV_DRAW_SDL_PRIV_H*/
diff --git a/lib/lvgl/src/draw/sdl/lv_draw_sdl_rect.c b/lib/lvgl/src/draw/sdl/lv_draw_sdl_rect.c
new file mode 100644
index 00000000..a303ac76
--- /dev/null
+++ b/lib/lvgl/src/draw/sdl/lv_draw_sdl_rect.c
@@ -0,0 +1,712 @@
+/**
+ * @file lv_draw_sdl_rect.c
+ *
+ */
+
+/*********************
+ * INCLUDES
+ *********************/
+
+#include "../../lv_conf_internal.h"
+
+#if LV_USE_GPU_SDL
+
+#include "../lv_draw_rect.h"
+#include "../lv_draw_img.h"
+#include "../lv_draw_label.h"
+#include "../lv_draw_mask.h"
+#include "../../core/lv_refr.h"
+#include "lv_draw_sdl_utils.h"
+#include "lv_draw_sdl_texture_cache.h"
+#include "lv_draw_sdl_composite.h"
+#include "lv_draw_sdl_mask.h"
+#include "lv_draw_sdl_stack_blur.h"
+#include "lv_draw_sdl_layer.h"
+
+/*********************
+ * DEFINES
+ *********************/
+
+/**********************
+ * TYPEDEFS
+ **********************/
+
+typedef struct {
+ lv_sdl_cache_key_magic_t magic;
+ lv_coord_t radius;
+ lv_coord_t size;
+} lv_draw_rect_bg_key_t;
+
+typedef struct {
+ lv_sdl_cache_key_magic_t magic;
+ lv_coord_t radius;
+ lv_coord_t size;
+ lv_coord_t blur;
+} lv_draw_rect_shadow_key_t;
+
+typedef struct {
+ lv_sdl_cache_key_magic_t magic;
+ lv_coord_t rout, rin;
+ lv_area_t offsets;
+} lv_draw_rect_border_key_t;
+
+/**********************
+ * STATIC PROTOTYPES
+ **********************/
+
+static void draw_bg_color(lv_draw_sdl_ctx_t * ctx, const lv_area_t * coords, const lv_area_t * draw_area,
+ const lv_draw_rect_dsc_t * dsc);
+
+static void draw_bg_img(lv_draw_sdl_ctx_t * ctx, const lv_area_t * coords, const lv_area_t * draw_area,
+ const lv_draw_rect_dsc_t * dsc);
+
+static void draw_border(lv_draw_sdl_ctx_t * ctx, const lv_area_t * coords, const lv_area_t * draw_area,
+ const lv_draw_rect_dsc_t * dsc);
+
+static void draw_shadow(lv_draw_sdl_ctx_t * ctx, const lv_area_t * coords, const lv_area_t * clip,
+ const lv_draw_rect_dsc_t * dsc);
+
+static void draw_outline(lv_draw_sdl_ctx_t * ctx, const lv_area_t * coords, const lv_area_t * clip,
+ const lv_draw_rect_dsc_t * dsc);
+
+static void draw_border_generic(lv_draw_sdl_ctx_t * ctx, const lv_area_t * outer_area, const lv_area_t * inner_area,
+ const lv_area_t * clip, lv_coord_t rout, lv_coord_t rin, lv_color_t color, lv_opa_t opa,
+ lv_blend_mode_t blend_mode);
+
+static void frag_render_borders(SDL_Renderer * renderer, SDL_Texture * frag, lv_coord_t frag_size,
+ const lv_area_t * coords, const lv_area_t * clipped, bool full);
+
+static void frag_render_center(SDL_Renderer * renderer, SDL_Texture * frag, lv_coord_t frag_size,
+ const lv_area_t * coords, const lv_area_t * clipped, bool full);
+
+static lv_draw_rect_bg_key_t rect_bg_key_create(lv_coord_t radius, lv_coord_t size);
+
+static lv_draw_rect_shadow_key_t rect_shadow_key_create(lv_coord_t radius, lv_coord_t size, lv_coord_t blur);
+
+static lv_draw_rect_border_key_t rect_border_key_create(lv_coord_t rout, lv_coord_t rin, const lv_area_t * outer_area,
+ const lv_area_t * inner_area);
+
+/**********************
+ * STATIC VARIABLES
+ **********************/
+
+/**********************
+ * MACROS
+ **********************/
+#define SKIP_BORDER(dsc) ((dsc)->border_opa <= LV_OPA_MIN || (dsc)->border_width == 0 || (dsc)->border_side == LV_BORDER_SIDE_NONE || (dsc)->border_post)
+#define SKIP_SHADOW(dsc) ((dsc)->shadow_width == 0 || (dsc)->shadow_opa <= LV_OPA_MIN || ((dsc)->shadow_width == 1 && (dsc)->shadow_spread <= 0 && (dsc)->shadow_ofs_x == 0 && (dsc)->shadow_ofs_y == 0))
+#define SKIP_IMAGE(dsc) ((dsc)->bg_img_src == NULL || (dsc)->bg_img_opa <= LV_OPA_MIN)
+#define SKIP_OUTLINE(dsc) ((dsc)->outline_opa <= LV_OPA_MIN || (dsc)->outline_width == 0)
+
+/**********************
+ * GLOBAL FUNCTIONS
+ **********************/
+
+void lv_draw_sdl_draw_rect(lv_draw_ctx_t * draw_ctx, const lv_draw_rect_dsc_t * dsc, const lv_area_t * coords)
+{
+ const lv_area_t * clip = draw_ctx->clip_area;
+ lv_draw_sdl_ctx_t * ctx = (lv_draw_sdl_ctx_t *) draw_ctx;
+
+ lv_area_t extension = {0, 0, 0, 0};
+ if(!SKIP_SHADOW(dsc)) {
+ lv_coord_t ext = (lv_coord_t)(dsc->shadow_spread - dsc->shadow_width / 2 + 1);
+ extension.x1 = LV_MAX(extension.x1, -dsc->shadow_ofs_x + ext);
+ extension.x2 = LV_MAX(extension.x2, dsc->shadow_ofs_x + ext);
+ extension.y1 = LV_MAX(extension.y1, -dsc->shadow_ofs_y + ext);
+ extension.y2 = LV_MAX(extension.y2, dsc->shadow_ofs_y + ext);
+ }
+ if(!SKIP_OUTLINE(dsc)) {
+ lv_coord_t ext = (lv_coord_t)(dsc->outline_pad - 1 + dsc->outline_width);
+ extension.x1 = LV_MAX(extension.x1, ext);
+ extension.x2 = LV_MAX(extension.x2, ext);
+ extension.y1 = LV_MAX(extension.y1, ext);
+ extension.y2 = LV_MAX(extension.y2, ext);
+ }
+ /* Coords will be translated so coords will start at (0,0) */
+ lv_area_t t_coords = *coords, t_clip = *clip, apply_area, t_area;
+ bool has_composite = lv_draw_sdl_composite_begin(ctx, coords, clip, &extension, dsc->blend_mode, &t_coords, &t_clip,
+ &apply_area);
+
+ lv_draw_sdl_transform_areas_offset(ctx, has_composite, &apply_area, &t_coords, &t_clip);
+
+ bool has_content = _lv_area_intersect(&t_area, &t_coords, &t_clip);
+
+ SDL_Rect clip_rect;
+ lv_area_to_sdl_rect(&t_clip, &clip_rect);
+ draw_shadow(ctx, &t_coords, &t_clip, dsc);
+ /* Shadows and outlines will also draw in extended area */
+ if(has_content) {
+ draw_bg_color(ctx, &t_coords, &t_area, dsc);
+ draw_bg_img(ctx, &t_coords, &t_area, dsc);
+ draw_border(ctx, &t_coords, &t_area, dsc);
+ }
+ draw_outline(ctx, &t_coords, &t_clip, dsc);
+
+ lv_draw_sdl_composite_end(ctx, &apply_area, dsc->blend_mode);
+}
+
+SDL_Texture * lv_draw_sdl_rect_bg_frag_obtain(lv_draw_sdl_ctx_t * ctx, lv_coord_t radius)
+{
+ lv_draw_rect_bg_key_t key = rect_bg_key_create(radius, radius);
+ lv_area_t coords = {0, 0, radius * 2 - 1, radius * 2 - 1};
+ lv_area_t coords_frag = {0, 0, radius - 1, radius - 1};
+ SDL_Texture * texture = lv_draw_sdl_texture_cache_get(ctx, &key, sizeof(key), NULL);
+ if(texture == NULL) {
+ lv_draw_mask_radius_param_t mask_rout_param;
+ lv_draw_mask_radius_init(&mask_rout_param, &coords, radius, false);
+ int16_t mask_id = lv_draw_mask_add(&mask_rout_param, NULL);
+ texture = lv_draw_sdl_mask_dump_texture(ctx->renderer, &coords_frag, &mask_id, 1);
+ lv_draw_mask_remove_id(mask_id);
+ SDL_assert(texture);
+ lv_draw_sdl_texture_cache_put(ctx, &key, sizeof(key), texture);
+ }
+ return texture;
+}
+
+void lv_draw_sdl_rect_bg_frag_draw_corners(lv_draw_sdl_ctx_t * ctx, SDL_Texture * frag, lv_coord_t frag_size,
+ const lv_area_t * coords, const lv_area_t * clip, bool full)
+{
+ if(!clip) clip = coords;
+ lv_area_t corner_area, dst_area;
+ /* Upper left */
+ corner_area.x1 = coords->x1;
+ corner_area.y1 = coords->y1;
+ corner_area.x2 = coords->x1 + frag_size - 1;
+ corner_area.y2 = coords->y1 + frag_size - 1;
+ if(_lv_area_intersect(&dst_area, &corner_area, clip)) {
+ SDL_Rect dst_rect;
+ lv_area_to_sdl_rect(&dst_area, &dst_rect);
+
+ lv_coord_t dw = lv_area_get_width(&dst_area), dh = lv_area_get_height(&dst_area);
+ lv_coord_t sx = (lv_coord_t)(dst_area.x1 - corner_area.x1), sy = (lv_coord_t)(dst_area.y1 - corner_area.y1);
+ SDL_Rect src_rect = {sx, sy, dw, dh};
+ SDL_RenderCopy(ctx->renderer, frag, &src_rect, &dst_rect);
+ }
+ /* Upper right, clip right edge if too big */
+ corner_area.x1 = LV_MAX(coords->x2 - frag_size + 1, coords->x1 + frag_size);
+ corner_area.x2 = coords->x2;
+ if(_lv_area_intersect(&dst_area, &corner_area, clip)) {
+ SDL_Rect dst_rect;
+ lv_area_to_sdl_rect(&dst_area, &dst_rect);
+
+ lv_coord_t dw = lv_area_get_width(&dst_area), dh = lv_area_get_height(&dst_area);
+ if(full) {
+ lv_coord_t sx = (lv_coord_t)(dst_area.x1 - corner_area.x1),
+ sy = (lv_coord_t)(dst_area.y1 - corner_area.y1);
+ SDL_Rect src_rect = {frag_size + 3 + sx, sy, dw, dh};
+ SDL_RenderCopy(ctx->renderer, frag, &src_rect, &dst_rect);
+ }
+ else {
+ SDL_Rect src_rect = {corner_area.x2 - dst_area.x2, dst_area.y1 - corner_area.y1, dw, dh};
+ SDL_RenderCopyEx(ctx->renderer, frag, &src_rect, &dst_rect, 0, NULL, SDL_FLIP_HORIZONTAL);
+ }
+ }
+ /* Lower right, clip bottom edge if too big */
+ corner_area.y1 = LV_MAX(coords->y2 - frag_size + 1, coords->y1 + frag_size);
+ corner_area.y2 = coords->y2;
+ if(_lv_area_intersect(&dst_area, &corner_area, clip)) {
+ SDL_Rect dst_rect;
+ lv_area_to_sdl_rect(&dst_area, &dst_rect);
+
+ lv_coord_t dw = lv_area_get_width(&dst_area), dh = lv_area_get_height(&dst_area);
+ if(full) {
+ lv_coord_t sx = (lv_coord_t)(dst_area.x1 - corner_area.x1),
+ sy = (lv_coord_t)(dst_area.y1 - corner_area.y1);
+ SDL_Rect src_rect = {frag_size + 3 + sx, frag_size + 3 + sy, dw, dh};
+ SDL_RenderCopy(ctx->renderer, frag, &src_rect, &dst_rect);
+ }
+ else {
+ SDL_Rect src_rect = {corner_area.x2 - dst_area.x2, corner_area.y2 - dst_area.y2, dw, dh};
+ SDL_RenderCopyEx(ctx->renderer, frag, &src_rect, &dst_rect, 0, NULL, SDL_FLIP_HORIZONTAL | SDL_FLIP_VERTICAL);
+ }
+ }
+ /* Lower left, right edge should not be clip */
+ corner_area.x1 = coords->x1;
+ corner_area.x2 = coords->x1 + frag_size - 1;
+ if(_lv_area_intersect(&dst_area, &corner_area, clip)) {
+ SDL_Rect dst_rect;
+ lv_area_to_sdl_rect(&dst_area, &dst_rect);
+
+ lv_coord_t dw = lv_area_get_width(&dst_area), dh = lv_area_get_height(&dst_area);
+ if(full) {
+ lv_coord_t sx = (lv_coord_t)(dst_area.x1 - corner_area.x1),
+ sy = (lv_coord_t)(dst_area.y1 - corner_area.y1);
+ SDL_Rect src_rect = {sx, frag_size + 3 + sy, dw, dh};
+ SDL_RenderCopy(ctx->renderer, frag, &src_rect, &dst_rect);
+ }
+ else {
+ SDL_Rect src_rect = {dst_area.x1 - corner_area.x1, corner_area.y2 - dst_area.y2, dw, dh};
+ SDL_RenderCopyEx(ctx->renderer, frag, &src_rect, &dst_rect, 0, NULL, SDL_FLIP_VERTICAL);
+ }
+ }
+}
+
+
+/**********************
+ * STATIC FUNCTIONS
+ **********************/
+
+static void draw_bg_color(lv_draw_sdl_ctx_t * ctx, const lv_area_t * coords, const lv_area_t * draw_area,
+ const lv_draw_rect_dsc_t * dsc)
+{
+ if(dsc->bg_opa == 0) {
+ return;
+ }
+ SDL_Color bg_color;
+ lv_color_to_sdl_color(&dsc->bg_color, &bg_color);
+ lv_coord_t radius = dsc->radius;
+ if(radius <= 0) {
+ SDL_Rect rect;
+ lv_area_to_sdl_rect(draw_area, &rect);
+ SDL_SetRenderDrawColor(ctx->renderer, bg_color.r, bg_color.g, bg_color.b, dsc->bg_opa);
+ SDL_SetRenderDrawBlendMode(ctx->renderer, SDL_BLENDMODE_BLEND);
+ SDL_RenderFillRect(ctx->renderer, &rect);
+ return;
+ }
+
+ /*A small texture with a quarter of the rect is enough*/
+ lv_coord_t bg_w = lv_area_get_width(coords), bg_h = lv_area_get_height(coords);
+ lv_coord_t real_radius = LV_MIN3(bg_w / 2, bg_h / 2, radius);
+ SDL_Texture * texture = lv_draw_sdl_rect_bg_frag_obtain(ctx, real_radius);
+
+ SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND);
+ SDL_SetTextureAlphaMod(texture, dsc->bg_opa);
+ SDL_SetTextureColorMod(texture, bg_color.r, bg_color.g, bg_color.b);
+ lv_draw_sdl_rect_bg_frag_draw_corners(ctx, texture, real_radius, coords, draw_area, false);
+ frag_render_borders(ctx->renderer, texture, real_radius, coords, draw_area, false);
+ frag_render_center(ctx->renderer, texture, real_radius, coords, draw_area, false);
+}
+
+static void draw_bg_img(lv_draw_sdl_ctx_t * ctx, const lv_area_t * coords, const lv_area_t * draw_area,
+ const lv_draw_rect_dsc_t * dsc)
+{
+ if(SKIP_IMAGE(dsc)) return;
+
+ lv_img_src_t src_type = lv_img_src_get_type(dsc->bg_img_src);
+ if(src_type == LV_IMG_SRC_SYMBOL) {
+ lv_point_t size;
+ lv_txt_get_size(&size, dsc->bg_img_src, dsc->bg_img_symbol_font, 0, 0, LV_COORD_MAX, LV_TEXT_FLAG_NONE);
+ lv_area_t a;
+ a.x1 = coords->x1 + lv_area_get_width(coords) / 2 - size.x / 2;
+ a.x2 = a.x1 + size.x - 1;
+ a.y1 = coords->y1 + lv_area_get_height(coords) / 2 - size.y / 2;
+ a.y2 = a.y1 + size.y - 1;
+
+ lv_draw_label_dsc_t label_draw_dsc;
+ lv_draw_label_dsc_init(&label_draw_dsc);
+ label_draw_dsc.font = dsc->bg_img_symbol_font;
+ label_draw_dsc.color = dsc->bg_img_recolor;
+ label_draw_dsc.opa = dsc->bg_img_opa;
+ lv_draw_label((lv_draw_ctx_t *) ctx, &label_draw_dsc, &a, dsc->bg_img_src, NULL);
+ }
+ else {
+ lv_img_header_t header;
+ size_t key_size;
+ lv_draw_sdl_cache_key_head_img_t * key = lv_draw_sdl_texture_img_key_create(dsc->bg_img_src, 0, &key_size);
+ bool key_found;
+ lv_img_header_t * cache_header = NULL;
+ SDL_Texture * texture = lv_draw_sdl_texture_cache_get_with_userdata(ctx, key, key_size, &key_found,
+ (void **) &cache_header);
+ SDL_free(key);
+ if(texture) {
+ header = *cache_header;
+ }
+ else if(key_found || lv_img_decoder_get_info(dsc->bg_img_src, &header) != LV_RES_OK) {
+ /* When cache hit but with negative result, use default decoder. If still fail, return.*/
+ LV_LOG_WARN("Couldn't read the background image");
+ return;
+ }
+
+ lv_draw_img_dsc_t img_dsc;
+ lv_draw_img_dsc_init(&img_dsc);
+ img_dsc.blend_mode = dsc->blend_mode;
+ img_dsc.recolor = dsc->bg_img_recolor;
+ img_dsc.recolor_opa = dsc->bg_img_recolor_opa;
+ img_dsc.opa = dsc->bg_img_opa;
+ img_dsc.frame_id = 0;
+
+ int16_t radius_mask_id = LV_MASK_ID_INV;
+ lv_draw_mask_radius_param_t radius_param;
+ if(dsc->radius > 0) {
+ lv_draw_mask_radius_init(&radius_param, coords, dsc->radius, false);
+ radius_mask_id = lv_draw_mask_add(&radius_param, NULL);
+ }
+
+ /*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((lv_draw_ctx_t *) 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((lv_draw_ctx_t *) ctx, &img_dsc, &area, dsc->bg_img_src);
+ }
+ }
+ }
+
+ if(radius_mask_id != LV_MASK_ID_INV) {
+ lv_draw_mask_remove_id(radius_mask_id);
+ lv_draw_mask_free_param(&radius_param);
+ }
+ }
+}
+
+static void draw_shadow(lv_draw_sdl_ctx_t * ctx, const lv_area_t * coords, const lv_area_t * clip,
+ const lv_draw_rect_dsc_t * dsc)
+{
+ /*Check whether the shadow is visible*/
+ if(SKIP_SHADOW(dsc)) return;
+
+ lv_coord_t sw = dsc->shadow_width;
+
+ 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;
+
+ lv_area_t shadow_area;
+ shadow_area.x1 = core_area.x1 - sw / 2 - 1;
+ shadow_area.x2 = core_area.x2 + sw / 2 + 1;
+ shadow_area.y1 = core_area.y1 - sw / 2 - 1;
+ shadow_area.y2 = core_area.y2 + sw / 2 + 1;
+
+ lv_opa_t opa = dsc->shadow_opa;
+
+ if(opa > LV_OPA_MAX) opa = LV_OPA_COVER;
+
+ /*Get clipped draw area which is the real draw area.
+ *It is always the same or inside `shadow_area`*/
+ lv_area_t draw_area;
+ if(!_lv_area_intersect(&draw_area, &shadow_area, clip)) return;
+
+ SDL_Rect core_area_rect;
+ lv_area_to_sdl_rect(&shadow_area, &core_area_rect);
+
+ lv_coord_t radius = dsc->radius;
+ /* No matter how big the shadow is, what we need is just a corner */
+ lv_coord_t frag_size = LV_MIN3(lv_area_get_width(&core_area) / 2, lv_area_get_height(&core_area) / 2,
+ LV_MAX(sw / 2, radius));
+
+ /* This is how big the corner is after blurring */
+ lv_coord_t blur_growth = (lv_coord_t)(sw / 2 + 1);
+
+ lv_coord_t blur_frag_size = (lv_coord_t)(frag_size + blur_growth);
+
+ lv_draw_rect_shadow_key_t key = rect_shadow_key_create(radius, frag_size, sw);
+
+ SDL_Texture * texture = lv_draw_sdl_texture_cache_get(ctx, &key, sizeof(key), NULL);
+ if(texture == NULL) {
+ lv_area_t mask_area = {blur_growth, blur_growth}, mask_area_blurred = {0, 0};
+ lv_area_set_width(&mask_area, frag_size * 2);
+ lv_area_set_height(&mask_area, frag_size * 2);
+ lv_area_set_width(&mask_area_blurred, blur_frag_size * 2);
+ lv_area_set_height(&mask_area_blurred, blur_frag_size * 2);
+
+ lv_draw_mask_radius_param_t mask_rout_param;
+ lv_draw_mask_radius_init(&mask_rout_param, &mask_area, radius, false);
+ int16_t mask_id = lv_draw_mask_add(&mask_rout_param, NULL);
+ lv_opa_t * mask_buf = lv_draw_sdl_mask_dump_opa(&mask_area_blurred, &mask_id, 1);
+ lv_stack_blur_grayscale(mask_buf, lv_area_get_width(&mask_area_blurred), lv_area_get_height(&mask_area_blurred),
+ sw / 2 + sw % 2);
+ texture = lv_sdl_create_opa_texture(ctx->renderer, mask_buf, blur_frag_size, blur_frag_size,
+ lv_area_get_width(&mask_area_blurred));
+ lv_mem_buf_release(mask_buf);
+ lv_draw_mask_remove_id(mask_id);
+ SDL_assert(texture);
+ lv_draw_sdl_texture_cache_put(ctx, &key, sizeof(key), texture);
+ }
+
+ SDL_Color shadow_color;
+ lv_color_to_sdl_color(&dsc->shadow_color, &shadow_color);
+ SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND);
+ SDL_SetTextureAlphaMod(texture, opa);
+ SDL_SetTextureColorMod(texture, shadow_color.r, shadow_color.g, shadow_color.b);
+
+ lv_draw_sdl_rect_bg_frag_draw_corners(ctx, texture, blur_frag_size, &shadow_area, clip, false);
+ frag_render_borders(ctx->renderer, texture, blur_frag_size, &shadow_area, clip, false);
+ frag_render_center(ctx->renderer, texture, blur_frag_size, &shadow_area, clip, false);
+}
+
+
+static void draw_border(lv_draw_sdl_ctx_t * ctx, const lv_area_t * coords, const lv_area_t * draw_area,
+ const lv_draw_rect_dsc_t * dsc)
+{
+ if(SKIP_BORDER(dsc)) return;
+
+ SDL_Color border_color;
+ lv_color_to_sdl_color(&dsc->border_color, &border_color);
+
+ lv_coord_t coords_w = lv_area_get_width(coords), coords_h = lv_area_get_height(coords);
+ lv_coord_t short_side = LV_MIN(coords_w, coords_h);
+ lv_coord_t rout = LV_MIN(dsc->radius, short_side / 2);/*Get the inner area*/
+ lv_area_t area_inner;
+ lv_area_copy(&area_inner, coords);// lv_area_increase(&area_inner, 1, 1);
+ 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 = LV_MAX(rout - dsc->border_width, 0);
+ draw_border_generic(ctx, coords, &area_inner, draw_area, rout, rin, dsc->border_color, dsc->border_opa,
+ dsc->blend_mode);
+}
+
+static void draw_outline(lv_draw_sdl_ctx_t * ctx, const lv_area_t * coords, const lv_area_t * clip,
+ const lv_draw_rect_dsc_t * dsc)
+{
+ if(SKIP_OUTLINE(dsc)) 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;
+
+ lv_area_t draw_area;
+ if(!_lv_area_intersect(&draw_area, &area_outer, clip)) return;
+
+ int32_t inner_w = lv_area_get_width(&area_inner);
+ int32_t inner_h = lv_area_get_height(&area_inner);
+ lv_coord_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(ctx, &area_outer, &area_inner, clip, rout, rin, dsc->outline_color, dsc->outline_opa,
+ dsc->blend_mode);
+}
+
+static void draw_border_generic(lv_draw_sdl_ctx_t * ctx, const lv_area_t * outer_area, const lv_area_t * inner_area,
+ const lv_area_t * clip, 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;
+
+ SDL_Renderer * renderer = ctx->renderer;
+
+ lv_draw_rect_border_key_t key = rect_border_key_create(rout, rin, outer_area, inner_area);
+ lv_coord_t radius = LV_MIN3(rout, lv_area_get_width(outer_area) / 2, lv_area_get_height(outer_area) / 2);
+ lv_coord_t max_side = LV_MAX4(key.offsets.x1, key.offsets.y1, -key.offsets.x2, -key.offsets.y2);
+ lv_coord_t frag_size = LV_MAX(radius, max_side);
+ SDL_Texture * texture = lv_draw_sdl_texture_cache_get(ctx, &key, sizeof(key), NULL);
+ if(texture == NULL) {
+ /* Create a mask texture with size of (frag_size * 2 + 3) */
+ const lv_area_t frag_area = {0, 0, frag_size * 2 + 2, frag_size * 2 + 2};
+
+ /*Create mask for the outer area*/
+ int16_t mask_ids[2] = {LV_MASK_ID_INV, LV_MASK_ID_INV};
+ lv_draw_mask_radius_param_t mask_rout_param;
+ if(rout > 0) {
+ lv_draw_mask_radius_init(&mask_rout_param, &frag_area, rout, false);
+ mask_ids[0] = lv_draw_mask_add(&mask_rout_param, NULL);
+ }
+
+ /*Create mask for the inner mask*/
+ if(rin < 0) rin = 0;
+ const lv_area_t frag_inner_area = {frag_area.x1 + key.offsets.x1, frag_area.y1 + key.offsets.y1,
+ frag_area.x2 + key.offsets.x2, frag_area.y2 + key.offsets.y2
+ };
+ lv_draw_mask_radius_param_t mask_rin_param;
+ lv_draw_mask_radius_init(&mask_rin_param, &frag_inner_area, rin, true);
+ mask_ids[1] = lv_draw_mask_add(&mask_rin_param, NULL);
+
+ texture = lv_draw_sdl_mask_dump_texture(renderer, &frag_area, mask_ids, 2);
+
+ lv_draw_mask_remove_id(mask_ids[1]);
+ lv_draw_mask_remove_id(mask_ids[0]);
+ SDL_assert(texture);
+ lv_draw_sdl_texture_cache_put(ctx, &key, sizeof(key), texture);
+ }
+
+ SDL_Rect outer_rect;
+ lv_area_to_sdl_rect(outer_area, &outer_rect);
+ SDL_Color color_sdl;
+ lv_color_to_sdl_color(&color, &color_sdl);
+
+ SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND);
+ SDL_SetTextureAlphaMod(texture, opa);
+ SDL_SetTextureColorMod(texture, color_sdl.r, color_sdl.g, color_sdl.b);
+
+ lv_draw_sdl_rect_bg_frag_draw_corners(ctx, texture, frag_size, outer_area, clip, true);
+ frag_render_borders(renderer, texture, frag_size, outer_area, clip, true);
+}
+
+static void frag_render_borders(SDL_Renderer * renderer, SDL_Texture * frag, lv_coord_t frag_size,
+ const lv_area_t * coords, const lv_area_t * clipped, bool full)
+{
+ lv_area_t border_area, dst_area;
+ /* Top border */
+ border_area.x1 = coords->x1 + frag_size;
+ border_area.y1 = coords->y1;
+ border_area.x2 = coords->x2 - frag_size;
+ border_area.y2 = coords->y1 + frag_size - 1;
+ if(_lv_area_intersect(&dst_area, &border_area, clipped)) {
+ SDL_Rect dst_rect;
+ lv_area_to_sdl_rect(&dst_area, &dst_rect);
+
+ lv_coord_t sy = (lv_coord_t)(dst_area.y1 - border_area.y1);
+ if(full) {
+ SDL_Rect src_rect = {frag_size + 1, sy, 1, lv_area_get_height(&dst_area)};
+ SDL_RenderCopy(renderer, frag, &src_rect, &dst_rect);
+ }
+ else {
+ SDL_Rect src_rect = {frag_size - 1, sy, 1, lv_area_get_height(&dst_area)};
+ SDL_RenderCopy(renderer, frag, &src_rect, &dst_rect);
+ }
+ }
+ /* Bottom border */
+ border_area.y1 = LV_MAX(coords->y2 - frag_size + 1, coords->y1 + frag_size);
+ border_area.y2 = coords->y2;
+ if(_lv_area_intersect(&dst_area, &border_area, clipped)) {
+ SDL_Rect dst_rect;
+ lv_area_to_sdl_rect(&dst_area, &dst_rect);
+
+ lv_coord_t dh = lv_area_get_height(&dst_area);
+ if(full) {
+ lv_coord_t sy = (lv_coord_t)(dst_area.y1 - border_area.y1);
+ SDL_Rect src_rect = {frag_size + 1, frag_size + 3 + sy, 1, dh};
+ SDL_RenderCopy(renderer, frag, &src_rect, &dst_rect);
+ }
+ else {
+ lv_coord_t sy = (lv_coord_t)(border_area.y2 - dst_area.y2);
+ SDL_Rect src_rect = {frag_size - 1, sy, 1, dh};
+ SDL_RenderCopyEx(renderer, frag, &src_rect, &dst_rect, 0, NULL, SDL_FLIP_VERTICAL);
+ }
+ }
+ /* Left border */
+ border_area.x1 = coords->x1;
+ border_area.y1 = coords->y1 + frag_size;
+ border_area.x2 = coords->x1 + frag_size - 1;
+ border_area.y2 = coords->y2 - frag_size;
+ if(_lv_area_intersect(&dst_area, &border_area, clipped)) {
+ SDL_Rect dst_rect;
+ lv_area_to_sdl_rect(&dst_area, &dst_rect);
+
+ lv_coord_t dw = lv_area_get_width(&dst_area);
+ lv_coord_t sx = (lv_coord_t)(dst_area.x1 - border_area.x1);
+ if(full) {
+ SDL_Rect src_rect = {sx, frag_size + 1, dw, 1};
+ SDL_RenderCopy(renderer, frag, &src_rect, &dst_rect);
+ }
+ else {
+ SDL_Rect src_rect = {sx, frag_size - 1, dw, 1};
+ SDL_RenderCopy(renderer, frag, &src_rect, &dst_rect);
+ }
+ }
+ /* Right border */
+ border_area.x1 = LV_MAX(coords->x2 - frag_size + 1, coords->x1 + frag_size);
+ border_area.x2 = coords->x2;
+ if(_lv_area_intersect(&dst_area, &border_area, clipped)) {
+ SDL_Rect dst_rect;
+ lv_area_to_sdl_rect(&dst_area, &dst_rect);
+
+ lv_coord_t dw = lv_area_get_width(&dst_area);
+ if(full) {
+ lv_coord_t sx = (lv_coord_t)(dst_area.x1 - border_area.x1);
+ SDL_Rect src_rect = {frag_size + 3 + sx, frag_size + 1, dw, 1};
+ SDL_RenderCopy(renderer, frag, &src_rect, &dst_rect);
+ }
+ else {
+ lv_coord_t sx = (lv_coord_t)(border_area.x2 - dst_area.x2);
+ SDL_Rect src_rect = {sx, frag_size - 1, dw, 1};
+ SDL_RenderCopyEx(renderer, frag, &src_rect, &dst_rect, 0, NULL, SDL_FLIP_HORIZONTAL);
+ }
+ }
+}
+
+static void frag_render_center(SDL_Renderer * renderer, SDL_Texture * frag, lv_coord_t frag_size,
+ const lv_area_t * coords,
+ const lv_area_t * clipped, bool full)
+{
+ lv_area_t center_area = {
+ coords->x1 + frag_size,
+ coords->y1 + frag_size,
+ coords->x2 - frag_size,
+ coords->y2 - frag_size,
+ };
+ if(center_area.x2 < center_area.x1 || center_area.y2 < center_area.y1) return;
+ lv_area_t draw_area;
+ if(!_lv_area_intersect(&draw_area, &center_area, clipped)) {
+ return;
+ }
+ SDL_Rect dst_rect;
+ lv_area_to_sdl_rect(&draw_area, &dst_rect);
+ if(full) {
+ SDL_Rect src_rect = {frag_size, frag_size, 1, 1};
+ SDL_RenderCopy(renderer, frag, &src_rect, &dst_rect);
+ }
+ else {
+ SDL_Rect src_rect = {frag_size - 1, frag_size - 1, 1, 1};
+ SDL_RenderCopy(renderer, frag, &src_rect, &dst_rect);
+ }
+}
+
+static lv_draw_rect_bg_key_t rect_bg_key_create(lv_coord_t radius, lv_coord_t size)
+{
+ lv_draw_rect_bg_key_t key;
+ SDL_memset(&key, 0, sizeof(key));
+ key.magic = LV_GPU_CACHE_KEY_MAGIC_RECT_BG;
+ key.radius = radius;
+ key.size = size;
+ return key;
+}
+
+static lv_draw_rect_shadow_key_t rect_shadow_key_create(lv_coord_t radius, lv_coord_t size, lv_coord_t blur)
+{
+ lv_draw_rect_shadow_key_t key;
+ SDL_memset(&key, 0, sizeof(key));
+ key.magic = LV_GPU_CACHE_KEY_MAGIC_RECT_SHADOW;
+ key.radius = radius;
+ key.size = size;
+ key.blur = blur;
+ return key;
+}
+
+static lv_draw_rect_border_key_t rect_border_key_create(lv_coord_t rout, lv_coord_t rin, const lv_area_t * outer_area,
+ const lv_area_t * inner_area)
+{
+ lv_draw_rect_border_key_t key;
+ /* VERY IMPORTANT! Padding between members is uninitialized, so we have to wipe them manually */
+ SDL_memset(&key, 0, sizeof(key));
+ key.magic = LV_GPU_CACHE_KEY_MAGIC_RECT_BORDER;
+ key.rout = rout;
+ key.rin = rin;
+ key.offsets.x1 = inner_area->x1 - outer_area->x1;
+ key.offsets.x2 = inner_area->x2 - outer_area->x2;
+ key.offsets.y1 = inner_area->y1 - outer_area->y1;
+ key.offsets.y2 = inner_area->y2 - outer_area->y2;
+ return key;
+}
+
+#endif /*LV_USE_GPU_SDL*/
diff --git a/lib/lvgl/src/draw/sdl/lv_draw_sdl_rect.h b/lib/lvgl/src/draw/sdl/lv_draw_sdl_rect.h
new file mode 100644
index 00000000..1e9be344
--- /dev/null
+++ b/lib/lvgl/src/draw/sdl/lv_draw_sdl_rect.h
@@ -0,0 +1,75 @@
+/**
+ * @file lv_draw_sdl_rect.h
+ *
+ */
+
+#ifndef LV_DRAW_SDL_RECT_H
+#define LV_DRAW_SDL_RECT_H
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*********************
+ * INCLUDES
+ *********************/
+#include "../../lv_conf_internal.h"
+
+#if LV_USE_GPU_SDL
+
+#include LV_GPU_SDL_INCLUDE_PATH
+
+#include "../lv_draw.h"
+
+#include "lv_draw_sdl_texture_cache.h"
+
+/*********************
+ * DEFINES
+ *********************/
+
+/**********************
+ * TYPEDEFS
+ **********************/
+
+typedef struct lv_draw_sdl_rect_header_t {
+ lv_img_header_t base;
+ SDL_Rect rect;
+} lv_draw_sdl_rect_header_t;
+
+/**********************
+ * GLOBAL PROTOTYPES
+ **********************/
+
+/*======================
+ * Add/remove functions
+ *=====================*/
+
+/*=====================
+ * Setter functions
+ *====================*/
+
+/*=====================
+ * Getter functions
+ *====================*/
+
+/*=====================
+ * Other functions
+ *====================*/
+
+SDL_Texture * lv_draw_sdl_rect_bg_frag_obtain(lv_draw_sdl_ctx_t * ctx, lv_coord_t radius);
+
+void lv_draw_sdl_rect_bg_frag_draw_corners(lv_draw_sdl_ctx_t * ctx, SDL_Texture * frag, lv_coord_t frag_size,
+ const lv_area_t * coords, const lv_area_t * clip, bool full);
+
+/**********************
+ * MACROS
+ **********************/
+
+#endif /*LV_USE_GPU_SDL*/
+
+#ifdef __cplusplus
+} /*extern "C"*/
+#endif
+
+#endif /*LV_DRAW_SDL_RECT_H*/
diff --git a/lib/lvgl/src/draw/sdl/lv_draw_sdl_stack_blur.c b/lib/lvgl/src/draw/sdl/lv_draw_sdl_stack_blur.c
new file mode 100644
index 00000000..1c411b0e
--- /dev/null
+++ b/lib/lvgl/src/draw/sdl/lv_draw_sdl_stack_blur.c
@@ -0,0 +1,249 @@
+/**
+ * @file lv_draw_sdl_stack_blur.c
+ *
+ */
+
+/*********************
+ * INCLUDES
+ *********************/
+#include "lv_draw_sdl_stack_blur.h"
+
+#if LV_USE_GPU_SDL
+/*********************
+ * DEFINES
+ *********************/
+
+/**********************
+ * TYPEDEFS
+ **********************/
+
+/**********************
+ * STATIC PROTOTYPES
+ **********************/
+
+static void
+stack_blur_job(lv_opa_t * src, unsigned int w, unsigned int h, unsigned int radius, int cores, int core, int step);
+
+/**********************
+ * STATIC VARIABLES
+ **********************/
+
+// Based heavily on http://vitiy.info/Code/stackblur.cpp
+// See http://vitiy.info/stackblur-algorithm-multi-threaded-blur-for-cpp/
+// Stack Blur Algorithm by Mario Klingemann <mario@quasimondo.com>
+
+static unsigned short const stackblur_mul[255] = {
+ 512, 512, 456, 512, 328, 456, 335, 512, 405, 328, 271, 456, 388, 335, 292, 512,
+ 454, 405, 364, 328, 298, 271, 496, 456, 420, 388, 360, 335, 312, 292, 273, 512,
+ 482, 454, 428, 405, 383, 364, 345, 328, 312, 298, 284, 271, 259, 496, 475, 456,
+ 437, 420, 404, 388, 374, 360, 347, 335, 323, 312, 302, 292, 282, 273, 265, 512,
+ 497, 482, 468, 454, 441, 428, 417, 405, 394, 383, 373, 364, 354, 345, 337, 328,
+ 320, 312, 305, 298, 291, 284, 278, 271, 265, 259, 507, 496, 485, 475, 465, 456,
+ 446, 437, 428, 420, 412, 404, 396, 388, 381, 374, 367, 360, 354, 347, 341, 335,
+ 329, 323, 318, 312, 307, 302, 297, 292, 287, 282, 278, 273, 269, 265, 261, 512,
+ 505, 497, 489, 482, 475, 468, 461, 454, 447, 441, 435, 428, 422, 417, 411, 405,
+ 399, 394, 389, 383, 378, 373, 368, 364, 359, 354, 350, 345, 341, 337, 332, 328,
+ 324, 320, 316, 312, 309, 305, 301, 298, 294, 291, 287, 284, 281, 278, 274, 271,
+ 268, 265, 262, 259, 257, 507, 501, 496, 491, 485, 480, 475, 470, 465, 460, 456,
+ 451, 446, 442, 437, 433, 428, 424, 420, 416, 412, 408, 404, 400, 396, 392, 388,
+ 385, 381, 377, 374, 370, 367, 363, 360, 357, 354, 350, 347, 344, 341, 338, 335,
+ 332, 329, 326, 323, 320, 318, 315, 312, 310, 307, 304, 302, 299, 297, 294, 292,
+ 289, 287, 285, 282, 280, 278, 275, 273, 271, 269, 267, 265, 263, 261, 259
+};
+
+static unsigned char const stackblur_shr[255] = {
+ 9, 11, 12, 13, 13, 14, 14, 15, 15, 15, 15, 16, 16, 16, 16, 17,
+ 17, 17, 17, 17, 17, 17, 18, 18, 18, 18, 18, 18, 18, 18, 18, 19,
+ 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 20, 20, 20,
+ 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 22, 22, 22, 22, 22, 22,
+ 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22,
+ 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 23,
+ 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23,
+ 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23,
+ 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23,
+ 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
+ 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
+ 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
+ 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
+ 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24
+};
+
+/**********************
+ * MACROS
+ **********************/
+
+/**********************
+ * GLOBAL FUNCTIONS
+ **********************/
+
+void lv_stack_blur_grayscale(lv_opa_t * buf, uint16_t w, uint16_t h, uint16_t r)
+{
+ stack_blur_job(buf, w, h, r, 1, 0, 1);
+ stack_blur_job(buf, w, h, r, 1, 0, 2);
+}
+
+/**********************
+ * STATIC FUNCTIONS
+ **********************/
+
+static void stack_blur_job(lv_opa_t * src, unsigned int w, unsigned int h, unsigned int radius, int cores, int core,
+ int step)
+{
+ if(radius < 2 || radius > 254) {
+ /* Silently ignore bad radius */
+ return;
+ }
+
+ unsigned int x, y, xp, yp, i;
+ unsigned int sp;
+ unsigned int stack_start;
+ unsigned char * stack_ptr;
+
+ lv_opa_t * src_ptr;
+ lv_opa_t * dst_ptr;
+
+ unsigned long sum_r;
+ unsigned long sum_in_r;
+ unsigned long sum_out_r;
+
+ unsigned int wm = w - 1;
+ unsigned int hm = h - 1;
+ unsigned int stride = w;
+ unsigned int div = (radius * 2) + 1;
+ unsigned int mul_sum = stackblur_mul[radius];
+ unsigned char shr_sum = stackblur_shr[radius];
+ unsigned char stack[254 * 2 + 1];
+
+ if(step == 1) {
+ unsigned int minY = core * h / cores;
+ unsigned int maxY = (core + 1) * h / cores;
+
+ for(y = minY; y < maxY; y++) {
+ sum_r =
+ sum_in_r =
+ sum_out_r = 0;
+
+ src_ptr = src + stride * y; // start of line (0,y)
+
+ for(i = 0; i <= radius; i++) {
+ stack_ptr = &stack[i];
+ stack_ptr[0] = src_ptr[0];
+ sum_r += src_ptr[0] * (i + 1);
+ sum_out_r += src_ptr[0];
+ }
+
+
+ for(i = 1; i <= radius; i++) {
+ if(i <= wm) src_ptr += 1;
+ stack_ptr = &stack[i + radius];
+ stack_ptr[0] = src_ptr[0];
+ sum_r += src_ptr[0] * (radius + 1 - i);
+ sum_in_r += src_ptr[0];
+ }
+
+
+ sp = radius;
+ xp = radius;
+ if(xp > wm) xp = wm;
+ src_ptr = src + (xp + y * w); // img.pix_ptr(xp, y);
+ dst_ptr = src + y * stride; // img.pix_ptr(0, y);
+ for(x = 0; x < w; x++) {
+ dst_ptr[0] = LV_CLAMP((sum_r * mul_sum) >> shr_sum, 0, 255);
+ dst_ptr += 1;
+
+ sum_r -= sum_out_r;
+
+ stack_start = sp + div - radius;
+ if(stack_start >= div) stack_start -= div;
+ stack_ptr = &stack[stack_start];
+
+ sum_out_r -= stack_ptr[0];
+
+ if(xp < wm) {
+ src_ptr += 1;
+ ++xp;
+ }
+
+ stack_ptr[0] = src_ptr[0];
+
+ sum_in_r += src_ptr[0];
+ sum_r += sum_in_r;
+
+ ++sp;
+ if(sp >= div) sp = 0;
+ stack_ptr = &stack[sp];
+
+ sum_out_r += stack_ptr[0];
+ sum_in_r -= stack_ptr[0];
+ }
+
+ }
+ }
+
+ // step 2
+ if(step == 2) {
+ unsigned int minX = core * w / cores;
+ unsigned int maxX = (core + 1) * w / cores;
+
+ for(x = minX; x < maxX; x++) {
+ sum_r =
+ sum_in_r =
+ sum_out_r = 0;
+
+ src_ptr = src + x; // x,0
+ for(i = 0; i <= radius; i++) {
+ stack_ptr = &stack[i];
+ stack_ptr[0] = src_ptr[0];
+ sum_r += src_ptr[0] * (i + 1);
+ sum_out_r += src_ptr[0];
+ }
+ for(i = 1; i <= radius; i++) {
+ if(i <= hm) src_ptr += stride; // +stride
+
+ stack_ptr = &stack[i + radius];
+ stack_ptr[0] = src_ptr[0];
+ sum_r += src_ptr[0] * (radius + 1 - i);
+ sum_in_r += src_ptr[0];
+ }
+
+ sp = radius;
+ yp = radius;
+ if(yp > hm) yp = hm;
+ src_ptr = src + (x + yp * w); // img.pix_ptr(x, yp);
+ dst_ptr = src + x; // img.pix_ptr(x, 0);
+ for(y = 0; y < h; y++) {
+ dst_ptr[0] = LV_CLAMP((sum_r * mul_sum) >> shr_sum, 0, 255);
+ dst_ptr += stride;
+
+ sum_r -= sum_out_r;
+
+ stack_start = sp + div - radius;
+ if(stack_start >= div) stack_start -= div;
+ stack_ptr = &stack[stack_start];
+
+ sum_out_r -= stack_ptr[0];
+
+ if(yp < hm) {
+ src_ptr += stride; // stride
+ ++yp;
+ }
+
+ stack_ptr[0] = src_ptr[0];
+
+ sum_in_r += src_ptr[0];
+ sum_r += sum_in_r;
+
+ ++sp;
+ if(sp >= div) sp = 0;
+ stack_ptr = &stack[sp];
+
+ sum_out_r += stack_ptr[0];
+ sum_in_r -= stack_ptr[0];
+ }
+ }
+ }
+}
+
+#endif /*LV_USE_GPU_SDL*/
diff --git a/lib/lvgl/src/draw/sdl/lv_draw_sdl_stack_blur.h b/lib/lvgl/src/draw/sdl/lv_draw_sdl_stack_blur.h
new file mode 100644
index 00000000..413b1c94
--- /dev/null
+++ b/lib/lvgl/src/draw/sdl/lv_draw_sdl_stack_blur.h
@@ -0,0 +1,46 @@
+/**
+ * @file lv_draw_sdl_stack_blur.h
+ *
+ */
+#ifndef LV_DRAW_SDL_STACK_BLUR_H
+#define LV_DRAW_SDL_STACK_BLUR_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*********************
+ * INCLUDES
+ *********************/
+
+#include "../../lv_conf_internal.h"
+
+#if LV_USE_GPU_SDL
+
+#include "../../misc/lv_color.h"
+
+/*********************
+ * DEFINES
+ *********************/
+
+/**********************
+ * TYPEDEFS
+ **********************/
+
+/**********************
+ * GLOBAL PROTOTYPES
+ **********************/
+
+void lv_stack_blur_grayscale(lv_opa_t * buf, uint16_t w, uint16_t h, uint16_t r);
+
+/**********************
+ * MACROS
+ **********************/
+
+#endif /*LV_USE_GPU_SDL*/
+
+#ifdef __cplusplus
+} /*extern "C"*/
+#endif
+
+#endif /*LV_DRAW_SDL_STACK_BLUR_H*/
diff --git a/lib/lvgl/src/draw/sdl/lv_draw_sdl_texture_cache.c b/lib/lvgl/src/draw/sdl/lv_draw_sdl_texture_cache.c
new file mode 100644
index 00000000..6845addf
--- /dev/null
+++ b/lib/lvgl/src/draw/sdl/lv_draw_sdl_texture_cache.c
@@ -0,0 +1,178 @@
+/**
+ * @file lv_draw_sdl_texture_cache.c
+ *
+ */
+
+/*********************
+ * INCLUDES
+ *********************/
+
+#include "../../lv_conf_internal.h"
+
+#if LV_USE_GPU_SDL
+
+#include "lv_draw_sdl_texture_cache.h"
+
+#include "lv_draw_sdl_utils.h"
+
+/*********************
+ * DEFINES
+ *********************/
+
+/**********************
+ * TYPEDEFS
+ **********************/
+
+typedef struct {
+ SDL_Texture * texture;
+ void * userdata;
+ lv_lru_free_t * userdata_free;
+ lv_draw_sdl_cache_flag_t flags;
+} draw_cache_value_t;
+
+typedef struct {
+ lv_sdl_cache_key_magic_t magic;
+} temp_texture_key_t;
+
+typedef struct {
+ lv_coord_t width, height;
+} temp_texture_userdata_t;
+
+static void draw_cache_free_value(draw_cache_value_t *);
+
+static draw_cache_value_t * draw_cache_get_entry(lv_draw_sdl_ctx_t * ctx, const void * key, size_t key_length,
+ bool * found);
+/**********************
+ * STATIC VARIABLES
+ **********************/
+
+/**********************
+ * MACROS
+ **********************/
+
+/**********************
+ * GLOBAL FUNCTIONS
+ **********************/
+
+void lv_draw_sdl_texture_cache_init(lv_draw_sdl_ctx_t * ctx)
+{
+ ctx->internals->texture_cache = lv_lru_create(LV_GPU_SDL_LRU_SIZE, 65536,
+ (lv_lru_free_t *) draw_cache_free_value, NULL);
+}
+
+void lv_draw_sdl_texture_cache_deinit(lv_draw_sdl_ctx_t * ctx)
+{
+ lv_lru_del(ctx->internals->texture_cache);
+}
+
+SDL_Texture * lv_draw_sdl_texture_cache_get(lv_draw_sdl_ctx_t * ctx, const void * key, size_t key_length, bool * found)
+{
+ return lv_draw_sdl_texture_cache_get_with_userdata(ctx, key, key_length, found, NULL);
+}
+
+SDL_Texture * lv_draw_sdl_texture_cache_get_with_userdata(lv_draw_sdl_ctx_t * ctx, const void * key, size_t key_length,
+ bool * found, void ** userdata)
+{
+ draw_cache_value_t * value = draw_cache_get_entry(ctx, key, key_length, found);
+ if(!value) return NULL;
+ if(userdata) {
+ *userdata = value->userdata;
+ }
+ return value->texture;
+}
+
+void lv_draw_sdl_texture_cache_put(lv_draw_sdl_ctx_t * ctx, const void * key, size_t key_length, SDL_Texture * texture)
+{
+ lv_draw_sdl_texture_cache_put_advanced(ctx, key, key_length, texture, NULL, NULL, 0);
+}
+
+void lv_draw_sdl_texture_cache_put_advanced(lv_draw_sdl_ctx_t * ctx, const void * key, size_t key_length,
+ SDL_Texture * texture, void * userdata, void userdata_free(void *),
+ lv_draw_sdl_cache_flag_t flags)
+{
+ lv_lru_t * lru = ctx->internals->texture_cache;
+ draw_cache_value_t * value = SDL_malloc(sizeof(draw_cache_value_t));
+ value->texture = texture;
+ value->userdata = userdata;
+ value->userdata_free = userdata_free;
+ value->flags = flags;
+ if(!texture) {
+ lv_lru_set(lru, key, key_length, value, 1);
+ return;
+ }
+ if(flags & LV_DRAW_SDL_CACHE_FLAG_MANAGED) {
+ /* Managed texture doesn't count into cache size */
+ LV_LOG_INFO("cache texture %p", texture);
+ lv_lru_set(lru, key, key_length, value, 1);
+ return;
+ }
+ Uint32 format;
+ int access, width, height;
+ if(SDL_QueryTexture(texture, &format, &access, &width, &height) != 0) {
+ return;
+ }
+ LV_LOG_INFO("cache texture %p, %d*%d@%dbpp", texture, width, height, SDL_BITSPERPIXEL(format));
+ lv_lru_set(lru, key, key_length, value, width * height * SDL_BITSPERPIXEL(format) / 8);
+}
+
+lv_draw_sdl_cache_key_head_img_t * lv_draw_sdl_texture_img_key_create(const void * src, int32_t frame_id, size_t * size)
+{
+ lv_draw_sdl_cache_key_head_img_t header;
+ /* VERY IMPORTANT! Padding between members is uninitialized, so we have to wipe them manually */
+ SDL_memset(&header, 0, sizeof(header));
+ header.magic = LV_GPU_CACHE_KEY_MAGIC_IMG;
+ header.type = lv_img_src_get_type(src);
+ header.frame_id = frame_id;
+ void * key;
+ size_t key_size;
+ if(header.type == LV_IMG_SRC_FILE || header.type == LV_IMG_SRC_SYMBOL) {
+ size_t srclen = SDL_strlen(src);
+ key_size = sizeof(header) + srclen;
+ key = SDL_malloc(key_size);
+ SDL_memcpy(key, &header, sizeof(header));
+ /*Copy string content as key value*/
+ SDL_memcpy(key + sizeof(header), src, srclen);
+ }
+ else {
+ key_size = sizeof(header) + sizeof(void *);
+ key = SDL_malloc(key_size);
+ SDL_memcpy(key, &header, sizeof(header));
+ /*Copy address number as key value*/
+ SDL_memcpy(key + sizeof(header), &src, sizeof(void *));
+ }
+ *size = key_size;
+ return (lv_draw_sdl_cache_key_head_img_t *) key;
+}
+
+static void draw_cache_free_value(draw_cache_value_t * value)
+{
+ if(value->texture && !(value->flags & LV_DRAW_SDL_CACHE_FLAG_MANAGED)) {
+ LV_LOG_INFO("destroy texture %p", value->texture);
+ SDL_DestroyTexture(value->texture);
+ }
+ if(value->userdata_free) {
+ value->userdata_free(value->userdata);
+ }
+ SDL_free(value);
+}
+
+static draw_cache_value_t * draw_cache_get_entry(lv_draw_sdl_ctx_t * ctx, const void * key, size_t key_length,
+ bool * found)
+{
+ lv_lru_t * lru = ctx->internals->texture_cache;
+ draw_cache_value_t * value = NULL;
+ lv_lru_get(lru, key, key_length, (void **) &value);
+ if(!value) {
+ if(found) {
+ *found = false;
+ }
+ return NULL;
+ }
+ if(found) {
+ *found = true;
+ }
+ return value;
+}
+
+#endif /*LV_USE_GPU_SDL*/
+
diff --git a/lib/lvgl/src/draw/sdl/lv_draw_sdl_texture_cache.h b/lib/lvgl/src/draw/sdl/lv_draw_sdl_texture_cache.h
new file mode 100644
index 00000000..dc8b578e
--- /dev/null
+++ b/lib/lvgl/src/draw/sdl/lv_draw_sdl_texture_cache.h
@@ -0,0 +1,102 @@
+/**
+ * @file lv_draw_sdl_texture_cache.h
+ *
+ */
+
+#ifndef LV_DRAW_SDL_TEXTURE_CACHE_H
+#define LV_DRAW_SDL_TEXTURE_CACHE_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*********************
+ * INCLUDES
+ *********************/
+
+#include "../../lv_conf_internal.h"
+
+#if LV_USE_GPU_SDL
+
+#include LV_GPU_SDL_INCLUDE_PATH
+#include "lv_draw_sdl.h"
+#include "lv_draw_sdl_priv.h"
+#include "../../draw/lv_img_decoder.h"
+#include "../../misc/lv_area.h"
+
+/*********************
+ * DEFINES
+ *********************/
+
+#define LV_DRAW_SDL_DEC_DSC_TEXTURE_HEAD "@LVSDLTex"
+
+/**********************
+ * TYPEDEFS
+ **********************/
+
+typedef struct {
+ char head[8];
+ SDL_Texture * texture;
+ SDL_Rect rect;
+ bool texture_managed;
+ bool texture_referenced;
+} lv_draw_sdl_dec_dsc_userdata_t;
+
+typedef enum {
+ LV_GPU_CACHE_KEY_MAGIC_ARC = 0x01,
+ LV_GPU_CACHE_KEY_MAGIC_IMG = 0x11,
+ LV_GPU_CACHE_KEY_MAGIC_IMG_ROUNDED_CORNERS = 0x12,
+ LV_GPU_CACHE_KEY_MAGIC_LINE = 0x21,
+ LV_GPU_CACHE_KEY_MAGIC_RECT_BG = 0x31,
+ LV_GPU_CACHE_KEY_MAGIC_RECT_SHADOW = 0x32,
+ LV_GPU_CACHE_KEY_MAGIC_RECT_BORDER = 0x33,
+ LV_GPU_CACHE_KEY_MAGIC_FONT_GLYPH = 0x41,
+ LV_GPU_CACHE_KEY_MAGIC_MASK = 0x51,
+} lv_sdl_cache_key_magic_t;
+
+typedef enum {
+ LV_DRAW_SDL_CACHE_FLAG_NONE = 0,
+ LV_DRAW_SDL_CACHE_FLAG_MANAGED = 1,
+} lv_draw_sdl_cache_flag_t;
+
+typedef struct {
+ lv_sdl_cache_key_magic_t magic;
+ lv_img_src_t type;
+ int32_t frame_id;
+} lv_draw_sdl_cache_key_head_img_t;
+
+/**********************
+ * GLOBAL PROTOTYPES
+ **********************/
+
+void lv_draw_sdl_texture_cache_init(lv_draw_sdl_ctx_t * ctx);
+
+void lv_draw_sdl_texture_cache_deinit(lv_draw_sdl_ctx_t * ctx);
+
+/**
+ * Find cached texture by key. The texture can be destroyed during usage.
+ */
+SDL_Texture * lv_draw_sdl_texture_cache_get(lv_draw_sdl_ctx_t * ctx, const void * key, size_t key_length, bool * found);
+
+SDL_Texture * lv_draw_sdl_texture_cache_get_with_userdata(lv_draw_sdl_ctx_t * ctx, const void * key, size_t key_length,
+ bool * found, void ** userdata);
+
+void lv_draw_sdl_texture_cache_put(lv_draw_sdl_ctx_t * ctx, const void * key, size_t key_length, SDL_Texture * texture);
+
+void lv_draw_sdl_texture_cache_put_advanced(lv_draw_sdl_ctx_t * ctx, const void * key, size_t key_length,
+ SDL_Texture * texture, void * userdata, void userdata_free(void *),
+ lv_draw_sdl_cache_flag_t flags);
+
+lv_draw_sdl_cache_key_head_img_t * lv_draw_sdl_texture_img_key_create(const void * src, int32_t frame_id,
+ size_t * size);
+
+/**********************
+ * MACROS
+ **********************/
+#endif /*LV_USE_GPU_SDL*/
+
+#ifdef __cplusplus
+} /*extern "C"*/
+#endif
+
+#endif /*LV_DRAW_SDL_TEXTURE_CACHE_H*/
diff --git a/lib/lvgl/src/draw/sdl/lv_draw_sdl_utils.c b/lib/lvgl/src/draw/sdl/lv_draw_sdl_utils.c
new file mode 100644
index 00000000..3ca0fad4
--- /dev/null
+++ b/lib/lvgl/src/draw/sdl/lv_draw_sdl_utils.c
@@ -0,0 +1,183 @@
+/**
+ * @file lv_draw_sdl_utils.c
+ *
+ */
+
+/*********************
+ * INCLUDES
+ *********************/
+#include "../../lv_conf_internal.h"
+
+#if LV_USE_GPU_SDL
+
+#include "lv_draw_sdl_utils.h"
+
+#include "../lv_draw.h"
+#include "../lv_draw_label.h"
+#include "../../core/lv_refr.h"
+
+/*********************
+ * DEFINES
+ *********************/
+
+/**********************
+ * TYPEDEFS
+ **********************/
+
+/**********************
+ * STATIC PROTOTYPES
+ **********************/
+/**********************
+ * STATIC VARIABLES
+ **********************/
+extern const uint8_t _lv_bpp1_opa_table[2];
+extern const uint8_t _lv_bpp2_opa_table[4];
+extern const uint8_t _lv_bpp4_opa_table[16];
+extern const uint8_t _lv_bpp8_opa_table[256];
+
+static int utils_init_count = 0;
+static SDL_Palette * lv_sdl_palette_grayscale8 = NULL;
+
+/**********************
+ * MACROS
+ **********************/
+
+/**********************
+ * GLOBAL FUNCTIONS
+ **********************/
+
+void _lv_draw_sdl_utils_init()
+{
+ utils_init_count++;
+ if(utils_init_count > 1) {
+ return;
+ }
+ lv_sdl_palette_grayscale8 = lv_sdl_alloc_palette_for_bpp(_lv_bpp8_opa_table, 8);
+}
+
+void _lv_draw_sdl_utils_deinit()
+{
+ if(utils_init_count == 0) {
+ return;
+ }
+ utils_init_count--;
+ if(utils_init_count == 0) {
+ SDL_FreePalette(lv_sdl_palette_grayscale8);
+ lv_sdl_palette_grayscale8 = NULL;
+ }
+}
+
+void lv_area_to_sdl_rect(const lv_area_t * in, SDL_Rect * out)
+{
+ out->x = in->x1;
+ out->y = in->y1;
+ out->w = in->x2 - in->x1 + 1;
+ out->h = in->y2 - in->y1 + 1;
+}
+
+void lv_color_to_sdl_color(const lv_color_t * in, SDL_Color * out)
+{
+#if LV_COLOR_DEPTH == 32
+ out->a = in->ch.alpha;
+ out->r = in->ch.red;
+ out->g = in->ch.green;
+ out->b = in->ch.blue;
+#else
+ uint32_t color32 = lv_color_to32(*in);
+ lv_color32_t * color32_t = (lv_color32_t *) &color32;
+ out->a = color32_t->ch.alpha;
+ out->r = color32_t->ch.red;
+ out->g = color32_t->ch.green;
+ out->b = color32_t->ch.blue;
+#endif
+}
+
+void lv_area_zoom_to_sdl_rect(const lv_area_t * in, SDL_Rect * out, uint16_t zoom, const lv_point_t * pivot)
+{
+ if(zoom == LV_IMG_ZOOM_NONE) {
+ lv_area_to_sdl_rect(in, out);
+ return;
+ }
+ lv_area_t tmp;
+ _lv_img_buf_get_transformed_area(&tmp, lv_area_get_width(in), lv_area_get_height(in), 0, zoom, pivot);
+ lv_area_move(&tmp, in->x1, in->y1);
+ lv_area_to_sdl_rect(&tmp, out);
+}
+
+SDL_Palette * lv_sdl_alloc_palette_for_bpp(const uint8_t * mapping, uint8_t bpp)
+{
+ SDL_assert(bpp >= 1 && bpp <= 8);
+ int color_cnt = 1 << bpp;
+ SDL_Palette * result = SDL_AllocPalette(color_cnt);
+ SDL_Color palette[256];
+ for(int i = 0; i < color_cnt; i++) {
+ palette[i].r = palette[i].g = palette[i].b = 0xFF;
+ palette[i].a = mapping ? mapping[i] : i;
+ }
+ SDL_SetPaletteColors(result, palette, 0, color_cnt);
+ return result;
+}
+
+SDL_Surface * lv_sdl_create_opa_surface(lv_opa_t * opa, lv_coord_t width, lv_coord_t height, lv_coord_t stride)
+{
+ SDL_Surface * indexed = SDL_CreateRGBSurfaceFrom(opa, width, height, 8, stride, 0, 0, 0, 0);
+ SDL_SetSurfacePalette(indexed, lv_sdl_palette_grayscale8);
+ SDL_Surface * converted = SDL_ConvertSurfaceFormat(indexed, LV_DRAW_SDL_TEXTURE_FORMAT, 0);
+ SDL_FreeSurface(indexed);
+ return converted;
+}
+
+SDL_Texture * lv_sdl_create_opa_texture(SDL_Renderer * renderer, lv_opa_t * pixels, lv_coord_t width,
+ lv_coord_t height, lv_coord_t stride)
+{
+ SDL_Surface * indexed = lv_sdl_create_opa_surface(pixels, width, height, stride);
+ SDL_Texture * texture = SDL_CreateTextureFromSurface(renderer, indexed);
+ SDL_FreeSurface(indexed);
+ return texture;
+}
+
+void lv_sdl_to_8bpp(uint8_t * dest, const uint8_t * src, int width, int height, int stride, uint8_t bpp)
+{
+ int src_len = width * height;
+ int cur = 0;
+ int curbit;
+ uint8_t opa_mask;
+ const uint8_t * opa_table;
+ switch(bpp) {
+ case 1:
+ opa_mask = 0x1;
+ opa_table = _lv_bpp1_opa_table;
+ break;
+ case 2:
+ opa_mask = 0x4;
+ opa_table = _lv_bpp2_opa_table;
+ break;
+ case 4:
+ opa_mask = 0xF;
+ opa_table = _lv_bpp4_opa_table;
+ break;
+ case 8:
+ opa_mask = 0xFF;
+ opa_table = _lv_bpp8_opa_table;
+ break;
+ default:
+ return;
+ }
+ /* Does this work well on big endian systems? */
+ while(cur < src_len) {
+ curbit = 8 - bpp;
+ uint8_t src_byte = src[cur * bpp / 8];
+ while(curbit >= 0 && cur < src_len) {
+ uint8_t src_bits = opa_mask & (src_byte >> curbit);
+ dest[(cur / width * stride) + (cur % width)] = opa_table[src_bits];
+ curbit -= bpp;
+ cur++;
+ }
+ }
+}
+
+/**********************
+ * STATIC FUNCTIONS
+ **********************/
+
+#endif /*LV_USE_GPU_SDL*/
diff --git a/lib/lvgl/src/draw/sdl/lv_draw_sdl_utils.h b/lib/lvgl/src/draw/sdl/lv_draw_sdl_utils.h
new file mode 100644
index 00000000..9afae687
--- /dev/null
+++ b/lib/lvgl/src/draw/sdl/lv_draw_sdl_utils.h
@@ -0,0 +1,65 @@
+/**
+ * @file lv_draw_sdl_utils.h
+ *
+ */
+#ifndef LV_DRAW_SDL_UTILS_H
+#define LV_DRAW_SDL_UTILS_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*********************
+ * INCLUDES
+ *********************/
+
+#include "../../lv_conf_internal.h"
+#if LV_USE_GPU_SDL
+
+#include "lv_draw_sdl.h"
+#include "../../misc/lv_color.h"
+#include "../../misc/lv_area.h"
+
+#include LV_GPU_SDL_INCLUDE_PATH
+
+/*********************
+ * DEFINES
+ *********************/
+
+/**********************
+ * TYPEDEFS
+ **********************/
+
+/**********************
+ * GLOBAL PROTOTYPES
+ **********************/
+
+void _lv_draw_sdl_utils_init();
+
+void _lv_draw_sdl_utils_deinit();
+
+void lv_area_to_sdl_rect(const lv_area_t * in, SDL_Rect * out);
+
+void lv_color_to_sdl_color(const lv_color_t * in, SDL_Color * out);
+
+void lv_area_zoom_to_sdl_rect(const lv_area_t * in, SDL_Rect * out, uint16_t zoom, const lv_point_t * pivot);
+
+SDL_Palette * lv_sdl_alloc_palette_for_bpp(const uint8_t * mapping, uint8_t bpp);
+
+SDL_Surface * lv_sdl_create_opa_surface(lv_opa_t * opa, lv_coord_t width, lv_coord_t height, lv_coord_t stride);
+
+SDL_Texture * lv_sdl_create_opa_texture(SDL_Renderer * renderer, lv_opa_t * pixels, lv_coord_t width,
+ lv_coord_t height, lv_coord_t stride);
+
+void lv_sdl_to_8bpp(uint8_t * dest, const uint8_t * src, int width, int height, int stride, uint8_t bpp);
+
+/**********************
+ * MACROS
+ **********************/
+
+#endif /*LV_USE_GPU_SDL*/
+#ifdef __cplusplus
+} /*extern "C"*/
+#endif
+
+#endif /*LV_DRAW_SDL_UTILS_H*/
diff --git a/lib/lvgl/src/draw/stm32_dma2d/lv_draw_stm32_dma2d.mk b/lib/lvgl/src/draw/stm32_dma2d/lv_draw_stm32_dma2d.mk
new file mode 100644
index 00000000..8ed00b01
--- /dev/null
+++ b/lib/lvgl/src/draw/stm32_dma2d/lv_draw_stm32_dma2d.mk
@@ -0,0 +1,6 @@
+CSRCS += lv_gpu_stm32_dma2d.c
+
+DEPPATH += --dep-path $(LVGL_DIR)/$(LVGL_DIR_NAME)/src/draw/stm32_dma2d
+VPATH += :$(LVGL_DIR)/$(LVGL_DIR_NAME)/src/draw/stm32_dma2d
+
+CFLAGS += "-I$(LVGL_DIR)/$(LVGL_DIR_NAME)/src/draw/stm32_dma2d"
diff --git a/lib/lvgl/src/draw/stm32_dma2d/lv_gpu_stm32_dma2d.c b/lib/lvgl/src/draw/stm32_dma2d/lv_gpu_stm32_dma2d.c
new file mode 100644
index 00000000..4eb1940e
--- /dev/null
+++ b/lib/lvgl/src/draw/stm32_dma2d/lv_gpu_stm32_dma2d.c
@@ -0,0 +1,265 @@
+/**
+ * @file lv_gpu_stm32_dma2d.c
+ *
+ */
+
+/*********************
+ * INCLUDES
+ *********************/
+#include "lv_gpu_stm32_dma2d.h"
+#include "../../core/lv_refr.h"
+
+#if LV_USE_GPU_STM32_DMA2D
+
+#include LV_GPU_DMA2D_CMSIS_INCLUDE
+
+/*********************
+ * DEFINES
+ *********************/
+
+#if LV_COLOR_16_SWAP
+ // TODO: F7 has red blue swap bit in control register for all layers and output
+ #error "Can't use DMA2D with LV_COLOR_16_SWAP 1"
+#endif
+
+#if LV_COLOR_DEPTH == 8
+ #error "Can't use DMA2D with LV_COLOR_DEPTH == 8"
+#endif
+
+#if LV_COLOR_DEPTH == 16
+ #define LV_DMA2D_COLOR_FORMAT LV_DMA2D_RGB565
+#elif LV_COLOR_DEPTH == 32
+ #define LV_DMA2D_COLOR_FORMAT LV_DMA2D_ARGB8888
+#else
+ /*Can't use GPU with other formats*/
+#endif
+
+/**********************
+ * TYPEDEFS
+ **********************/
+
+/**********************
+ * STATIC PROTOTYPES
+ **********************/
+
+static void lv_draw_stm32_dma2d_blend_fill(lv_color_t * dest_buf, lv_coord_t dest_stride, const lv_area_t * fill_area,
+ lv_color_t color);
+
+
+static void lv_draw_stm32_dma2d_blend_map(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);
+
+static void lv_draw_stm32_dma2d_img_decoded(lv_draw_ctx_t * draw, const lv_draw_img_dsc_t * dsc,
+ const lv_area_t * coords, const uint8_t * map_p, lv_img_cf_t color_format);
+
+
+static void invalidate_cache(void);
+
+/**********************
+ * STATIC VARIABLES
+ **********************/
+
+/**********************
+ * MACROS
+ **********************/
+
+/**********************
+ * GLOBAL FUNCTIONS
+ **********************/
+
+/**
+ * Turn on the peripheral and set output color mode, this only needs to be done once
+ */
+void lv_draw_stm32_dma2d_init(void)
+{
+ /*Enable DMA2D clock*/
+#if defined(STM32F4) || defined(STM32F7)
+ RCC->AHB1ENR |= RCC_AHB1ENR_DMA2DEN;
+#elif defined(STM32H7)
+ RCC->AHB3ENR |= RCC_AHB3ENR_DMA2DEN;
+#else
+# warning "LVGL can't enable the clock of DMA2D"
+#endif
+
+ /*Wait for hardware access to complete*/
+ __asm volatile("DSB\n");
+
+ /*Delay after setting peripheral clock*/
+ volatile uint32_t temp = RCC->AHB1ENR;
+ LV_UNUSED(temp);
+
+ /*set output colour mode*/
+ DMA2D->OPFCCR = LV_DMA2D_COLOR_FORMAT;
+}
+
+
+void lv_draw_stm32_dma2d_ctx_init(lv_disp_drv_t * drv, lv_draw_ctx_t * draw_ctx)
+{
+
+ lv_draw_sw_init_ctx(drv, draw_ctx);
+
+ lv_draw_stm32_dma2d_ctx_t * dma2d_draw_ctx = (lv_draw_sw_ctx_t *)draw_ctx;
+
+ dma2d_draw_ctx->blend = lv_draw_stm32_dma2d_blend;
+ // dma2d_draw_ctx->base_draw.draw_img_decoded = lv_draw_stm32_dma2d_img_decoded;
+ dma2d_draw_ctx->base_draw.wait_for_finish = lv_gpu_stm32_dma2d_wait_cb;
+ dma2d_draw_ctx->base_draw.buffer_copy = lv_draw_stm32_dma2d_buffer_copy;
+
+}
+
+void lv_draw_stm32_dma2d_ctx_deinit(lv_disp_drv_t * drv, lv_draw_ctx_t * draw_ctx)
+{
+ LV_UNUSED(drv);
+ LV_UNUSED(draw_ctx);
+}
+
+
+void lv_draw_stm32_dma2d_blend(lv_draw_ctx_t * draw_ctx, const lv_draw_sw_blend_dsc_t * dsc)
+{
+ lv_area_t blend_area;
+ if(!_lv_area_intersect(&blend_area, dsc->blend_area, draw_ctx->clip_area)) return;
+
+ bool done = false;
+
+ if(dsc->mask_buf == NULL && dsc->blend_mode == LV_BLEND_MODE_NORMAL && lv_area_get_size(&blend_area) > 100) {
+ lv_coord_t dest_stride = lv_area_get_width(draw_ctx->buf_area);
+
+ lv_color_t * dest_buf = draw_ctx->buf;
+ dest_buf += dest_stride * (blend_area.y1 - draw_ctx->buf_area->y1) + (blend_area.x1 - draw_ctx->buf_area->x1);
+
+ const lv_color_t * src_buf = dsc->src_buf;
+ if(src_buf) {
+ lv_draw_sw_blend_basic(draw_ctx, dsc);
+ lv_coord_t src_stride;
+ 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);
+ lv_area_move(&blend_area, -draw_ctx->buf_area->x1, -draw_ctx->buf_area->y1);
+ lv_draw_stm32_dma2d_blend_map(dest_buf, &blend_area, dest_stride, src_buf, src_stride, dsc->opa);
+ done = true;
+ }
+ else if(dsc->opa >= LV_OPA_MAX) {
+ lv_area_move(&blend_area, -draw_ctx->buf_area->x1, -draw_ctx->buf_area->y1);
+ lv_draw_stm32_dma2d_blend_fill(dest_buf, dest_stride, &blend_area, dsc->color);
+ done = true;
+ }
+ }
+
+ if(!done) lv_draw_sw_blend_basic(draw_ctx, dsc);
+}
+
+void lv_draw_stm32_dma2d_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_draw_stm32_dma2d_blend_map(dest_buf, dest_area, dest_stride, src_buf, src_stride, LV_OPA_MAX);
+}
+
+
+static void lv_draw_stm32_dma2d_img_decoded(lv_draw_ctx_t * draw_ctx, const lv_draw_img_dsc_t * dsc,
+ const lv_area_t * coords, const uint8_t * map_p, lv_img_cf_t color_format)
+{
+ /*TODO basic ARGB8888 image can be handles here*/
+
+ lv_draw_sw_img_decoded(draw_ctx, dsc, coords, map_p, color_format);
+}
+
+static void lv_draw_stm32_dma2d_blend_fill(lv_color_t * dest_buf, lv_coord_t dest_stride, const lv_area_t * fill_area,
+ lv_color_t color)
+{
+ /*Simply fill an area*/
+ int32_t area_w = lv_area_get_width(fill_area);
+ int32_t area_h = lv_area_get_height(fill_area);
+ invalidate_cache();
+
+ DMA2D->CR = 0x30000;
+ DMA2D->OMAR = (uint32_t)dest_buf;
+ /*as input color mode is same as output we don't need to convert here do we?*/
+ DMA2D->OCOLR = color.full;
+ DMA2D->OOR = dest_stride - area_w;
+ DMA2D->NLR = (area_w << DMA2D_NLR_PL_Pos) | (area_h << DMA2D_NLR_NL_Pos);
+
+ /*start transfer*/
+ DMA2D->CR |= DMA2D_CR_START_Msk;
+
+}
+
+
+static void lv_draw_stm32_dma2d_blend_map(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)
+{
+
+ /*Simple copy*/
+ int32_t dest_w = lv_area_get_width(dest_area);
+ int32_t dest_h = lv_area_get_height(dest_area);
+
+ invalidate_cache();
+ if(opa >= LV_OPA_MAX) {
+ DMA2D->CR = 0;
+ /*copy output colour mode, this register controls both input and output colour format*/
+ DMA2D->FGPFCCR = LV_DMA2D_COLOR_FORMAT;
+ DMA2D->FGMAR = (uint32_t)src_buf;
+ DMA2D->FGOR = src_stride - dest_w;
+ DMA2D->OMAR = (uint32_t)dest_buf;
+ DMA2D->OOR = dest_stride - dest_w;
+ DMA2D->NLR = (dest_w << DMA2D_NLR_PL_Pos) | (dest_h << DMA2D_NLR_NL_Pos);
+
+ /*start transfer*/
+ DMA2D->CR |= DMA2D_CR_START_Msk;
+ }
+ else {
+ DMA2D->CR = 0x20000;
+
+ DMA2D->BGPFCCR = LV_DMA2D_COLOR_FORMAT;
+ DMA2D->BGMAR = (uint32_t)dest_buf;
+ DMA2D->BGOR = dest_stride - dest_w;
+
+ DMA2D->FGPFCCR = (uint32_t)LV_DMA2D_COLOR_FORMAT
+ /*alpha mode 2, replace with foreground * alpha value*/
+ | (2 << DMA2D_FGPFCCR_AM_Pos)
+ /*alpha value*/
+ | (opa << DMA2D_FGPFCCR_ALPHA_Pos);
+ DMA2D->FGMAR = (uint32_t)src_buf;
+ DMA2D->FGOR = src_stride - dest_w;
+
+ DMA2D->OMAR = (uint32_t)dest_buf;
+ DMA2D->OOR = dest_stride - dest_w;
+ DMA2D->NLR = (dest_w << DMA2D_NLR_PL_Pos) | (dest_h << DMA2D_NLR_NL_Pos);
+
+ /*start transfer*/
+ DMA2D->CR |= DMA2D_CR_START_Msk;
+ }
+}
+
+void lv_gpu_stm32_dma2d_wait_cb(lv_draw_ctx_t * draw_ctx)
+{
+ lv_disp_t * disp = _lv_refr_get_disp_refreshing();
+ if(disp->driver && disp->driver->wait_cb) {
+ while(DMA2D->CR & DMA2D_CR_START_Msk) {
+ disp->driver->wait_cb(disp->driver);
+ }
+ }
+ else {
+ while(DMA2D->CR & DMA2D_CR_START_Msk);
+ }
+ lv_draw_sw_wait_for_finish(draw_ctx);
+
+}
+
+/**********************
+ * STATIC FUNCTIONS
+ **********************/
+
+static void invalidate_cache(void)
+{
+ lv_disp_t * disp = _lv_refr_get_disp_refreshing();
+ if(disp->driver->clean_dcache_cb) disp->driver->clean_dcache_cb(disp->driver);
+ else {
+#if __CORTEX_M >= 0x07
+ if((SCB->CCR) & (uint32_t)SCB_CCR_DC_Msk)
+ SCB_CleanInvalidateDCache();
+#endif
+ }
+}
+
+#endif
diff --git a/lib/lvgl/src/draw/stm32_dma2d/lv_gpu_stm32_dma2d.h b/lib/lvgl/src/draw/stm32_dma2d/lv_gpu_stm32_dma2d.h
new file mode 100644
index 00000000..fa7070e2
--- /dev/null
+++ b/lib/lvgl/src/draw/stm32_dma2d/lv_gpu_stm32_dma2d.h
@@ -0,0 +1,70 @@
+/**
+ * @file lv_gpu_stm32_dma2d.h
+ *
+ */
+
+#ifndef LV_GPU_STM32_DMA2D_H
+#define LV_GPU_STM32_DMA2D_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*********************
+ * INCLUDES
+ *********************/
+#include "../../misc/lv_color.h"
+#include "../../hal/lv_hal_disp.h"
+#include "../sw/lv_draw_sw.h"
+
+#if LV_USE_GPU_STM32_DMA2D
+
+/*********************
+ * DEFINES
+ *********************/
+
+#define LV_DMA2D_ARGB8888 0
+#define LV_DMA2D_RGB888 1
+#define LV_DMA2D_RGB565 2
+#define LV_DMA2D_ARGB1555 3
+#define LV_DMA2D_ARGB4444 4
+
+/**********************
+ * TYPEDEFS
+ **********************/
+typedef lv_draw_sw_ctx_t lv_draw_stm32_dma2d_ctx_t;
+
+struct _lv_disp_drv_t;
+
+/**********************
+ * GLOBAL PROTOTYPES
+ **********************/
+
+/**
+ * Turn on the peripheral and set output color mode, this only needs to be done once
+ */
+void lv_draw_stm32_dma2d_init(void);
+
+void lv_draw_stm32_dma2d_ctx_init(struct _lv_disp_drv_t * drv, lv_draw_ctx_t * draw_ctx);
+
+void lv_draw_stm32_dma2d_ctx_deinit(struct _lv_disp_drv_t * drv, lv_draw_ctx_t * draw_ctx);
+
+void lv_draw_stm32_dma2d_blend(lv_draw_ctx_t * draw_ctx, const lv_draw_sw_blend_dsc_t * dsc);
+
+void lv_draw_stm32_dma2d_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_gpu_stm32_dma2d_wait_cb(lv_draw_ctx_t * draw_ctx);
+
+/**********************
+ * MACROS
+ **********************/
+
+#endif /*LV_USE_GPU_STM32_DMA2D*/
+
+#ifdef __cplusplus
+} /*extern "C"*/
+#endif
+
+#endif /*LV_GPU_STM32_DMA2D_H*/
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
+
diff --git a/lib/lvgl/src/draw/swm341_dma2d/lv_draw_swm341_dma2d.mk b/lib/lvgl/src/draw/swm341_dma2d/lv_draw_swm341_dma2d.mk
new file mode 100644
index 00000000..bc19e380
--- /dev/null
+++ b/lib/lvgl/src/draw/swm341_dma2d/lv_draw_swm341_dma2d.mk
@@ -0,0 +1,6 @@
+CSRCS += lv_gpu_swm341_dma2d.c
+
+DEPPATH += --dep-path $(LVGL_DIR)/$(LVGL_DIR_NAME)/src/draw/swm341_dma2d
+VPATH += :$(LVGL_DIR)/$(LVGL_DIR_NAME)/src/draw/swm341_dma2d
+
+CFLAGS += "-I$(LVGL_DIR)/$(LVGL_DIR_NAME)/src/draw/swm341_dma2d"
diff --git a/lib/lvgl/src/draw/swm341_dma2d/lv_gpu_swm341_dma2d.c b/lib/lvgl/src/draw/swm341_dma2d/lv_gpu_swm341_dma2d.c
new file mode 100644
index 00000000..74a53946
--- /dev/null
+++ b/lib/lvgl/src/draw/swm341_dma2d/lv_gpu_swm341_dma2d.c
@@ -0,0 +1,241 @@
+/**
+ * @file lv_gpu_swm341_dma2d.c
+ *
+ */
+
+/*********************
+ * INCLUDES
+ *********************/
+#include "lv_gpu_swm341_dma2d.h"
+#include "../../core/lv_refr.h"
+
+#if LV_USE_GPU_SWM341_DMA2D
+
+#include LV_GPU_SWM341_DMA2D_INCLUDE
+
+/*********************
+ * DEFINES
+ *********************/
+
+#if LV_COLOR_16_SWAP
+ #error "Can't use DMA2D with LV_COLOR_16_SWAP 1"
+#endif
+
+#if LV_COLOR_DEPTH == 8
+ #error "Can't use DMA2D with LV_COLOR_DEPTH == 8"
+#endif
+
+#if LV_COLOR_DEPTH == 16
+ #define LV_DMA2D_COLOR_FORMAT LV_SWM341_DMA2D_RGB565
+#elif LV_COLOR_DEPTH == 32
+ #define LV_DMA2D_COLOR_FORMAT LV_SWM341_DMA2D_ARGB8888
+#else
+ /*Can't use GPU with other formats*/
+#endif
+
+/**********************
+ * TYPEDEFS
+ **********************/
+
+/**********************
+ * STATIC PROTOTYPES
+ **********************/
+
+static void lv_draw_swm341_dma2d_blend_fill(lv_color_t * dest_buf, lv_coord_t dest_stride, const lv_area_t * fill_area,
+ lv_color_t color);
+
+static void lv_draw_swm341_dma2d_blend_map(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);
+
+static void lv_draw_swm341_dma2d_img_decoded(lv_draw_ctx_t * draw, const lv_draw_img_dsc_t * dsc,
+ const lv_area_t * coords, const uint8_t * map_p, lv_img_cf_t color_format);
+
+/**********************
+ * STATIC VARIABLES
+ **********************/
+
+/**********************
+ * MACROS
+ **********************/
+
+/**********************
+ * GLOBAL FUNCTIONS
+ **********************/
+
+/**
+ * Turn on the peripheral and set output color mode, this only needs to be done once
+ */
+void lv_draw_swm341_dma2d_init(void)
+{
+ /*Enable DMA2D clock*/
+ SYS->CLKEN0 |= (1 << SYS_CLKEN0_DMA2D_Pos);
+
+ DMA2D->CR &= ~DMA2D_CR_WAIT_Msk;
+ DMA2D->CR |= (CyclesPerUs << DMA2D_CR_WAIT_Pos);
+
+ DMA2D->IF = 0xFF;
+ DMA2D->IE = (0 << DMA2D_IE_DONE_Pos);
+
+ /*set output colour mode*/
+ DMA2D->L[DMA2D_LAYER_OUT].PFCCR = (LV_DMA2D_COLOR_FORMAT << DMA2D_PFCCR_CFMT_Pos);
+}
+
+void lv_draw_swm341_dma2d_ctx_init(lv_disp_drv_t * drv, lv_draw_ctx_t * draw_ctx)
+{
+
+ lv_draw_sw_init_ctx(drv, draw_ctx);
+
+ lv_draw_swm341_dma2d_ctx_t * dma2d_draw_ctx = (lv_draw_sw_ctx_t *)draw_ctx;
+
+ dma2d_draw_ctx->blend = lv_draw_swm341_dma2d_blend;
+ // dma2d_draw_ctx->base_draw.draw_img_decoded = lv_draw_swm341_dma2d_img_decoded;
+ dma2d_draw_ctx->base_draw.wait_for_finish = lv_gpu_swm341_dma2d_wait_cb;
+}
+
+void lv_draw_swm341_dma2d_ctx_deinit(lv_disp_drv_t * drv, lv_draw_ctx_t * draw_ctx)
+{
+ LV_UNUSED(drv);
+ LV_UNUSED(draw_ctx);
+}
+
+void lv_draw_swm341_dma2d_blend(lv_draw_ctx_t * draw_ctx, const lv_draw_sw_blend_dsc_t * dsc)
+{
+ lv_area_t blend_area;
+ if(!_lv_area_intersect(&blend_area, dsc->blend_area, draw_ctx->clip_area))
+ return;
+
+ bool done = false;
+
+ if(dsc->mask_buf == NULL && dsc->blend_mode == LV_BLEND_MODE_NORMAL && lv_area_get_size(&blend_area) > 100) {
+ lv_coord_t dest_stride = lv_area_get_width(draw_ctx->buf_area);
+
+ lv_color_t * dest_buf = draw_ctx->buf;
+ dest_buf += dest_stride * (blend_area.y1 - draw_ctx->buf_area->y1) + (blend_area.x1 - draw_ctx->buf_area->x1);
+
+ const lv_color_t * src_buf = dsc->src_buf;
+ if(src_buf) {
+ lv_draw_sw_blend_basic(draw_ctx, dsc);
+ lv_coord_t src_stride;
+ 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);
+ lv_area_move(&blend_area, -draw_ctx->buf_area->x1, -draw_ctx->buf_area->y1);
+ lv_draw_swm341_dma2d_blend_map(dest_buf, &blend_area, dest_stride, src_buf, src_stride, dsc->opa);
+ done = true;
+ }
+ else if(dsc->opa >= LV_OPA_MAX) {
+ lv_area_move(&blend_area, -draw_ctx->buf_area->x1, -draw_ctx->buf_area->y1);
+ lv_draw_swm341_dma2d_blend_fill(dest_buf, dest_stride, &blend_area, dsc->color);
+ done = true;
+ }
+ }
+
+ if(!done) lv_draw_sw_blend_basic(draw_ctx, dsc);
+}
+
+static void lv_draw_swm341_dma2d_img_decoded(lv_draw_ctx_t * draw_ctx, const lv_draw_img_dsc_t * dsc,
+ const lv_area_t * coords, const uint8_t * map_p, lv_img_cf_t color_format)
+{
+ /*TODO basic ARGB8888 image can be handles here*/
+
+ lv_draw_sw_img_decoded(draw_ctx, dsc, coords, map_p, color_format);
+}
+
+static void lv_draw_swm341_dma2d_blend_fill(lv_color_t * dest_buf, lv_coord_t dest_stride, const lv_area_t * fill_area,
+ lv_color_t color)
+{
+ /*Simply fill an area*/
+ int32_t area_w = lv_area_get_width(fill_area);
+ int32_t area_h = lv_area_get_height(fill_area);
+
+#if 1
+ DMA2D->L[DMA2D_LAYER_OUT].COLOR = color.full;
+
+ DMA2D->L[DMA2D_LAYER_OUT].MAR = (uint32_t)dest_buf;
+ DMA2D->L[DMA2D_LAYER_OUT].OR = dest_stride - area_w;
+ DMA2D->NLR = ((area_w - 1) << DMA2D_NLR_NPIXEL_Pos) | ((area_h - 1) << DMA2D_NLR_NLINE_Pos);
+
+ /*start transfer*/
+ DMA2D->CR &= ~DMA2D_CR_MODE_Msk;
+ DMA2D->CR |= (3 << DMA2D_CR_MODE_Pos) |
+ (1 << DMA2D_CR_START_Pos);
+#else
+ for(uint32_t y = 0; y < area_h; y++) {
+ for(uint32_t x = 0; x < area_w; x++) {
+ dest_buf[y * dest_stride + x] = color;
+ }
+ }
+#endif
+}
+
+static void lv_draw_swm341_dma2d_blend_map(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)
+{
+
+ /*Simple copy*/
+ int32_t dest_w = lv_area_get_width(dest_area);
+ int32_t dest_h = lv_area_get_height(dest_area);
+
+ if(opa >= LV_OPA_MAX) {
+#if 1
+ /*copy output colour mode, this register controls both input and output colour format*/
+ DMA2D->L[DMA2D_LAYER_FG].MAR = (uint32_t)src_buf;
+ DMA2D->L[DMA2D_LAYER_FG].OR = src_stride - dest_w;
+ DMA2D->L[DMA2D_LAYER_FG].PFCCR = (LV_DMA2D_COLOR_FORMAT << DMA2D_PFCCR_CFMT_Pos);
+
+ DMA2D->L[DMA2D_LAYER_OUT].MAR = (uint32_t)dest_buf;
+ DMA2D->L[DMA2D_LAYER_OUT].OR = dest_stride - dest_w;
+
+ DMA2D->NLR = ((dest_w - 1) << DMA2D_NLR_NPIXEL_Pos) | ((dest_h - 1) << DMA2D_NLR_NLINE_Pos);
+
+ /*start transfer*/
+ DMA2D->CR &= ~DMA2D_CR_MODE_Msk;
+ DMA2D->CR |= (0 << DMA2D_CR_MODE_Pos) |
+ (1 << DMA2D_CR_START_Pos);
+#else
+ lv_color_t temp_buf[1024];
+ for(uint32_t y = 0; y < dest_h; y++) {
+ memcpy(temp_buf, &src_buf[y * src_stride], dest_w * sizeof(lv_color_t));
+ memcpy(&dest_buf[y * dest_stride], temp_buf, dest_w * sizeof(lv_color_t));
+ }
+#endif
+ }
+ else {
+ DMA2D->L[DMA2D_LAYER_FG].MAR = (uint32_t)src_buf;
+ DMA2D->L[DMA2D_LAYER_FG].OR = src_stride - dest_w;
+ DMA2D->L[DMA2D_LAYER_FG].PFCCR = (LV_DMA2D_COLOR_FORMAT << DMA2D_PFCCR_CFMT_Pos)
+ /*alpha mode 2, replace with foreground * alpha value*/
+ | (2 << DAM2D_PFCCR_AMODE_Pos)
+ /*alpha value*/
+ | (opa << DMA2D_PFCCR_ALPHA_Pos);
+
+ DMA2D->L[DMA2D_LAYER_BG].MAR = (uint32_t)dest_buf;
+ DMA2D->L[DMA2D_LAYER_BG].OR = dest_stride - dest_w;
+ DMA2D->L[DMA2D_LAYER_BG].PFCCR = (LV_DMA2D_COLOR_FORMAT << DMA2D_PFCCR_CFMT_Pos);
+
+ DMA2D->L[DMA2D_LAYER_OUT].MAR = (uint32_t)dest_buf;
+ DMA2D->L[DMA2D_LAYER_OUT].OR = dest_stride - dest_w;
+
+ DMA2D->NLR = ((dest_w - 1) << DMA2D_NLR_NPIXEL_Pos) | ((dest_h - 1) << DMA2D_NLR_NLINE_Pos);
+
+ /*start transfer*/
+ DMA2D->CR &= ~DMA2D_CR_MODE_Msk;
+ DMA2D->CR |= (2 << DMA2D_CR_MODE_Pos) |
+ (1 << DMA2D_CR_START_Pos);
+ }
+}
+
+void lv_gpu_swm341_dma2d_wait_cb(lv_draw_ctx_t * draw_ctx)
+{
+ lv_disp_t * disp = _lv_refr_get_disp_refreshing();
+ if(disp->driver && disp->driver->wait_cb) {
+ while(DMA2D->CR & DMA2D_CR_START_Msk) {
+ disp->driver->wait_cb(disp->driver);
+ }
+ }
+ else {
+ while(DMA2D->CR & DMA2D_CR_START_Msk);
+ }
+ lv_draw_sw_wait_for_finish(draw_ctx);
+}
+
+#endif
diff --git a/lib/lvgl/src/draw/swm341_dma2d/lv_gpu_swm341_dma2d.h b/lib/lvgl/src/draw/swm341_dma2d/lv_gpu_swm341_dma2d.h
new file mode 100644
index 00000000..20b89226
--- /dev/null
+++ b/lib/lvgl/src/draw/swm341_dma2d/lv_gpu_swm341_dma2d.h
@@ -0,0 +1,64 @@
+/**
+ * @file lv_gpu_swm341_dma2d.h
+ *
+ */
+
+#ifndef LV_GPU_SWM341_DMA2D_H
+#define LV_GPU_SWM341_DMA2D_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*********************
+ * INCLUDES
+ *********************/
+#include "../../misc/lv_color.h"
+#include "../../hal/lv_hal_disp.h"
+#include "../sw/lv_draw_sw.h"
+
+#if LV_USE_GPU_SWM341_DMA2D
+
+/*********************
+ * DEFINES
+ *********************/
+
+#define LV_SWM341_DMA2D_ARGB8888 0
+#define LV_SWM341_DMA2D_RGB888 1
+#define LV_SWM341_DMA2D_RGB565 2
+
+/**********************
+ * TYPEDEFS
+ **********************/
+typedef lv_draw_sw_ctx_t lv_draw_swm341_dma2d_ctx_t;
+
+struct _lv_disp_drv_t;
+
+/**********************
+ * GLOBAL PROTOTYPES
+ **********************/
+
+/**
+ * Turn on the peripheral and set output color mode, this only needs to be done once
+ */
+void lv_draw_swm341_dma2d_init(void);
+
+void lv_draw_swm341_dma2d_ctx_init(struct _lv_disp_drv_t * drv, lv_draw_ctx_t * draw_ctx);
+
+void lv_draw_swm341_dma2d_ctx_deinit(struct _lv_disp_drv_t * drv, lv_draw_ctx_t * draw_ctx);
+
+void lv_draw_swm341_dma2d_blend(lv_draw_ctx_t * draw_ctx, const lv_draw_sw_blend_dsc_t * dsc);
+
+void lv_gpu_swm341_dma2d_wait_cb(lv_draw_ctx_t * draw_ctx);
+
+/**********************
+ * MACROS
+ **********************/
+
+#endif /*LV_USE_GPU_SWM341_DMA2D*/
+
+#ifdef __cplusplus
+} /*extern "C"*/
+#endif
+
+#endif /*LV_GPU_SWM341_DMA2D_H*/