diff options
Diffstat (limited to 'lib/lvgl/src/draw')
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, \ + ©_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, + ©_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, + ©_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, + ©_size, + (color_int *)dest_buf, + dest_stride, + ©_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, + ©_size, + opa); + + __arm_2d_impl_src_msk_copy((color_int *)src_buf, + src_stride, + (uint8_t *)mask, + mask_stride, + ©_size, + (color_int *)dest_buf, + dest_stride, + ©_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, + ©_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, + ©_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, + ©_size, + 0xFF, + blend_dsc.opa); + + __arm_2d_impl_src_msk_copy( + (color_int *)src_buf_tmp, + src_stride, + mask_temp_buf, + src_stride, + ©_size, + (color_int *)dest_buf, + dest_stride, + ©_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, + ©_size, + (color_int *)dest_buf, + dest_stride, + ©_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, + ©_size); + } + else { + __arm_2d_impl_alpha_blending( + (color_int *)src_buf_tmp, + src_stride, + (color_int *)dest_buf, + dest_stride, + ©_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, + ©_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(¶m->start_line, vertex_x, vertex_y, start_angle, start_side); + lv_draw_mask_line_angle_init(¶m->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(¶m->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(¶m->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(¶m->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, ¶m->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, ¢er, 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, ¢er, 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(¶m, &round_area, LV_RADIUS_CIRCLE, false); + + int16_t mask_id = lv_draw_mask_add(¶m, 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, ¢er_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(¢er_coords); + if(!mask_any_center && grad_dir == LV_GRAD_DIR_NONE) { + blend_area.y1 = bg_coords.y1 + rout; + blend_area.y2 = bg_coords.y2 - rout; + blend_dsc.opa = opa; + blend_dsc.mask_buf = NULL; + lv_draw_sw_blend(draw_ctx, &blend_dsc); + } + /*With gradient and/or mask draw line by line*/ + else { + blend_dsc.opa = opa; + blend_dsc.mask_res = LV_DRAW_MASK_RES_FULL_COVER; + int32_t h_end = bg_coords.y2 - rout; + for(h = bg_coords.y1 + rout; h <= h_end; h++) { + /*If there is no other mask do not apply mask as in the center there is no radius to mask*/ + if(mask_any_center) { + lv_memset(mask_buf, opa, clipped_w); + blend_dsc.mask_res = lv_draw_mask_apply(mask_buf, clipped_coords.x1, h, clipped_w); + } + + blend_area.y1 = h; + blend_area.y2 = h; + +#if _DITHER_GRADIENT + if(dither_func) dither_func(grad, blend_area.x1, h - bg_coords.y1, grad_size); +#endif + if(grad_dir == LV_GRAD_DIR_VER) blend_dsc.color = grad->map[h - bg_coords.y1]; + lv_draw_sw_blend(draw_ctx, &blend_dsc); + } + } + + +bg_clean_up: + if(mask_buf) lv_mem_buf_release(mask_buf); + if(mask_rout_id != LV_MASK_ID_INV) { + lv_draw_mask_remove_id(mask_rout_id); + lv_draw_mask_free_param(&mask_rout_param); + } + if(grad) { + lv_gradient_cleanup(grad); + } + +#endif +} + +static void draw_bg_img(lv_draw_ctx_t * draw_ctx, const lv_draw_rect_dsc_t * dsc, const lv_area_t * coords) +{ + if(dsc->bg_img_src == NULL) return; + if(dsc->bg_img_opa <= LV_OPA_MIN) return; + + lv_area_t clip_area; + if(!_lv_area_intersect(&clip_area, coords, draw_ctx->clip_area)) { + return; + } + + const lv_area_t * clip_area_ori = draw_ctx->clip_area; + draw_ctx->clip_area = &clip_area; + + lv_img_src_t src_type = lv_img_src_get_type(dsc->bg_img_src); + if(src_type == LV_IMG_SRC_SYMBOL) { + lv_point_t size; + lv_txt_get_size(&size, dsc->bg_img_src, dsc->bg_img_symbol_font, 0, 0, LV_COORD_MAX, LV_TEXT_FLAG_NONE); + lv_area_t a; + a.x1 = coords->x1 + lv_area_get_width(coords) / 2 - size.x / 2; + a.x2 = a.x1 + size.x - 1; + a.y1 = coords->y1 + lv_area_get_height(coords) / 2 - size.y / 2; + a.y2 = a.y1 + size.y - 1; + + lv_draw_label_dsc_t label_draw_dsc; + lv_draw_label_dsc_init(&label_draw_dsc); + label_draw_dsc.font = dsc->bg_img_symbol_font; + label_draw_dsc.color = dsc->bg_img_recolor; + label_draw_dsc.opa = dsc->bg_img_opa; + lv_draw_label(draw_ctx, &label_draw_dsc, &a, dsc->bg_img_src, NULL); + } + else { + lv_img_header_t header; + lv_res_t res = lv_img_decoder_get_info(dsc->bg_img_src, &header); + if(res == LV_RES_OK) { + lv_draw_img_dsc_t img_dsc; + lv_draw_img_dsc_init(&img_dsc); + img_dsc.blend_mode = dsc->blend_mode; + img_dsc.recolor = dsc->bg_img_recolor; + img_dsc.recolor_opa = dsc->bg_img_recolor_opa; + img_dsc.opa = dsc->bg_img_opa; + + /*Center align*/ + if(dsc->bg_img_tiled == false) { + lv_area_t area; + area.x1 = coords->x1 + lv_area_get_width(coords) / 2 - header.w / 2; + area.y1 = coords->y1 + lv_area_get_height(coords) / 2 - header.h / 2; + area.x2 = area.x1 + header.w - 1; + area.y2 = area.y1 + header.h - 1; + + lv_draw_img(draw_ctx, &img_dsc, &area, dsc->bg_img_src); + } + else { + lv_area_t area; + area.y1 = coords->y1; + area.y2 = area.y1 + header.h - 1; + + for(; area.y1 <= coords->y2; area.y1 += header.h, area.y2 += header.h) { + + area.x1 = coords->x1; + area.x2 = area.x1 + header.w - 1; + for(; area.x1 <= coords->x2; area.x1 += header.w, area.x2 += header.w) { + lv_draw_img(draw_ctx, &img_dsc, &area, dsc->bg_img_src); + } + } + } + } + else { + LV_LOG_WARN("Couldn't read the background image"); + } + } + + draw_ctx->clip_area = clip_area_ori; +} + +static void draw_border(lv_draw_ctx_t * draw_ctx, const lv_draw_rect_dsc_t * dsc, const lv_area_t * coords) +{ + if(dsc->border_opa <= LV_OPA_MIN) return; + if(dsc->border_width == 0) return; + if(dsc->border_side == LV_BORDER_SIDE_NONE) return; + if(dsc->border_post) return; + + int32_t coords_w = lv_area_get_width(coords); + int32_t coords_h = lv_area_get_height(coords); + int32_t rout = dsc->radius; + int32_t short_side = LV_MIN(coords_w, coords_h); + if(rout > short_side >> 1) rout = short_side >> 1; + + /*Get the inner area*/ + lv_area_t area_inner; + lv_area_copy(&area_inner, coords); + area_inner.x1 += ((dsc->border_side & LV_BORDER_SIDE_LEFT) ? dsc->border_width : - (dsc->border_width + rout)); + area_inner.x2 -= ((dsc->border_side & LV_BORDER_SIDE_RIGHT) ? dsc->border_width : - (dsc->border_width + rout)); + area_inner.y1 += ((dsc->border_side & LV_BORDER_SIDE_TOP) ? dsc->border_width : - (dsc->border_width + rout)); + area_inner.y2 -= ((dsc->border_side & LV_BORDER_SIDE_BOTTOM) ? dsc->border_width : - (dsc->border_width + rout)); + + lv_coord_t rin = rout - dsc->border_width; + if(rin < 0) rin = 0; + + draw_border_generic(draw_ctx, coords, &area_inner, rout, rin, dsc->border_color, dsc->border_opa, dsc->blend_mode); + +} + +#if LV_DRAW_COMPLEX +LV_ATTRIBUTE_FAST_MEM static void draw_shadow(lv_draw_ctx_t * draw_ctx, const lv_draw_rect_dsc_t * dsc, + const lv_area_t * coords) +{ + /*Check whether the shadow is visible*/ + if(dsc->shadow_width == 0) return; + if(dsc->shadow_opa <= LV_OPA_MIN) return; + + if(dsc->shadow_width == 1 && dsc->shadow_spread <= 0 && + dsc->shadow_ofs_x == 0 && dsc->shadow_ofs_y == 0) { + return; + } + + /*Calculate the rectangle which is blurred to get the shadow in `shadow_area`*/ + lv_area_t core_area; + core_area.x1 = coords->x1 + dsc->shadow_ofs_x - dsc->shadow_spread; + core_area.x2 = coords->x2 + dsc->shadow_ofs_x + dsc->shadow_spread; + core_area.y1 = coords->y1 + dsc->shadow_ofs_y - dsc->shadow_spread; + core_area.y2 = coords->y2 + dsc->shadow_ofs_y + dsc->shadow_spread; + + /*Calculate the bounding box of the shadow*/ + lv_area_t shadow_area; + shadow_area.x1 = core_area.x1 - dsc->shadow_width / 2 - 1; + shadow_area.x2 = core_area.x2 + dsc->shadow_width / 2 + 1; + shadow_area.y1 = core_area.y1 - dsc->shadow_width / 2 - 1; + shadow_area.y2 = core_area.y2 + dsc->shadow_width / 2 + 1; + + lv_opa_t opa = dsc->shadow_opa; + if(opa > LV_OPA_MAX) opa = LV_OPA_COVER; + + /*Get clipped draw area which is the real draw area. + *It is always the same or inside `shadow_area`*/ + lv_area_t draw_area; + if(!_lv_area_intersect(&draw_area, &shadow_area, draw_ctx->clip_area)) return; + + /*Consider 1 px smaller bg to be sure the edge will be covered by the shadow*/ + lv_area_t bg_area; + lv_area_copy(&bg_area, coords); + lv_area_increase(&bg_area, -1, -1); + + /*Get the clamped radius*/ + int32_t r_bg = dsc->radius; + lv_coord_t short_side = LV_MIN(lv_area_get_width(&bg_area), lv_area_get_height(&bg_area)); + if(r_bg > short_side >> 1) r_bg = short_side >> 1; + + /*Get the clamped radius*/ + int32_t r_sh = dsc->radius; + short_side = LV_MIN(lv_area_get_width(&core_area), lv_area_get_height(&core_area)); + if(r_sh > short_side >> 1) r_sh = short_side >> 1; + + + /*Get how many pixels are affected by the blur on the corners*/ + int32_t corner_size = dsc->shadow_width + r_sh; + + lv_opa_t * sh_buf; + +#if LV_SHADOW_CACHE_SIZE + if(sh_cache_size == corner_size && sh_cache_r == r_sh) { + /*Use the cache if available*/ + sh_buf = lv_mem_buf_get(corner_size * corner_size); + lv_memcpy(sh_buf, sh_cache, corner_size * corner_size); + } + else { + /*A larger buffer is required for calculation*/ + sh_buf = lv_mem_buf_get(corner_size * corner_size * sizeof(uint16_t)); + shadow_draw_corner_buf(&core_area, (uint16_t *)sh_buf, dsc->shadow_width, r_sh); + + /*Cache the corner if it fits into the cache size*/ + if((uint32_t)corner_size * corner_size < sizeof(sh_cache)) { + lv_memcpy(sh_cache, sh_buf, corner_size * corner_size); + sh_cache_size = corner_size; + sh_cache_r = r_sh; + } + } +#else + sh_buf = lv_mem_buf_get(corner_size * corner_size * sizeof(uint16_t)); + shadow_draw_corner_buf(&core_area, (uint16_t *)sh_buf, dsc->shadow_width, r_sh); +#endif + + /*Skip a lot of masking if the background will cover the shadow that would be masked out*/ + bool mask_any = lv_draw_mask_is_any(&shadow_area); + bool simple = true; + if(mask_any || dsc->bg_opa < LV_OPA_COVER || dsc->blend_mode != LV_BLEND_MODE_NORMAL) simple = false; + + /*Create a radius mask to clip remove shadow on the bg area*/ + + lv_draw_mask_radius_param_t mask_rout_param; + int16_t mask_rout_id = LV_MASK_ID_INV; + if(!simple) { + lv_draw_mask_radius_init(&mask_rout_param, &bg_area, r_bg, true); + mask_rout_id = lv_draw_mask_add(&mask_rout_param, NULL); + } + lv_opa_t * mask_buf = lv_mem_buf_get(lv_area_get_width(&shadow_area)); + lv_area_t blend_area; + lv_area_t clip_area_sub; + lv_opa_t * sh_buf_tmp; + lv_coord_t y; + bool simple_sub; + + lv_draw_sw_blend_dsc_t blend_dsc; + lv_memset_00(&blend_dsc, sizeof(blend_dsc)); + blend_dsc.blend_area = &blend_area; + blend_dsc.mask_area = &blend_area; + blend_dsc.mask_buf = mask_buf; + blend_dsc.color = dsc->shadow_color; + blend_dsc.opa = dsc->shadow_opa; + blend_dsc.blend_mode = dsc->blend_mode; + + lv_coord_t w_half = shadow_area.x1 + lv_area_get_width(&shadow_area) / 2; + lv_coord_t h_half = shadow_area.y1 + lv_area_get_height(&shadow_area) / 2; + + /*Draw the corners if they are on the current clip area and not fully covered by the bg*/ + + /*Top right corner*/ + blend_area.x2 = shadow_area.x2; + blend_area.x1 = shadow_area.x2 - corner_size + 1; + blend_area.y1 = shadow_area.y1; + blend_area.y2 = shadow_area.y1 + corner_size - 1; + /*Do not overdraw the other top corners*/ + blend_area.x1 = LV_MAX(blend_area.x1, w_half); + blend_area.y2 = LV_MIN(blend_area.y2, h_half); + + if(_lv_area_intersect(&clip_area_sub, &blend_area, draw_ctx->clip_area) && + !_lv_area_is_in(&clip_area_sub, &bg_area, r_bg)) { + lv_coord_t w = lv_area_get_width(&clip_area_sub); + sh_buf_tmp = sh_buf; + sh_buf_tmp += (clip_area_sub.y1 - shadow_area.y1) * corner_size; + sh_buf_tmp += clip_area_sub.x1 - (shadow_area.x2 - corner_size + 1); + + /*Do not mask if out of the bg*/ + if(simple && _lv_area_is_out(&clip_area_sub, &bg_area, r_bg)) simple_sub = true; + else simple_sub = simple; + if(w > 0) { + blend_dsc.mask_buf = mask_buf; + blend_area.x1 = clip_area_sub.x1; + blend_area.x2 = clip_area_sub.x2; + blend_dsc.mask_res = LV_DRAW_MASK_RES_CHANGED; /*In simple mode it won't be overwritten*/ + for(y = clip_area_sub.y1; y <= clip_area_sub.y2; y++) { + blend_area.y1 = y; + blend_area.y2 = y; + + if(!simple_sub) { + lv_memcpy(mask_buf, sh_buf_tmp, corner_size); + blend_dsc.mask_res = lv_draw_mask_apply(mask_buf, clip_area_sub.x1, y, w); + if(blend_dsc.mask_res == LV_DRAW_MASK_RES_FULL_COVER) blend_dsc.mask_res = LV_DRAW_MASK_RES_CHANGED; + } + else { + blend_dsc.mask_buf = sh_buf_tmp; + } + lv_draw_sw_blend(draw_ctx, &blend_dsc); + sh_buf_tmp += corner_size; + } + } + } + + /*Bottom right corner. + *Almost the same as top right just read the lines of `sh_buf` from then end*/ + blend_area.x2 = shadow_area.x2; + blend_area.x1 = shadow_area.x2 - corner_size + 1; + blend_area.y1 = shadow_area.y2 - corner_size + 1; + blend_area.y2 = shadow_area.y2; + /*Do not overdraw the other corners*/ + blend_area.x1 = LV_MAX(blend_area.x1, w_half); + blend_area.y1 = LV_MAX(blend_area.y1, h_half + 1); + + if(_lv_area_intersect(&clip_area_sub, &blend_area, draw_ctx->clip_area) && + !_lv_area_is_in(&clip_area_sub, &bg_area, r_bg)) { + lv_coord_t w = lv_area_get_width(&clip_area_sub); + sh_buf_tmp = sh_buf; + sh_buf_tmp += (blend_area.y2 - clip_area_sub.y2) * corner_size; + sh_buf_tmp += clip_area_sub.x1 - (shadow_area.x2 - corner_size + 1); + /*Do not mask if out of the bg*/ + if(simple && _lv_area_is_out(&clip_area_sub, &bg_area, r_bg)) simple_sub = true; + else simple_sub = simple; + + if(w > 0) { + blend_dsc.mask_buf = mask_buf; + blend_area.x1 = clip_area_sub.x1; + blend_area.x2 = clip_area_sub.x2; + blend_dsc.mask_res = LV_DRAW_MASK_RES_CHANGED; /*In simple mode it won't be overwritten*/ + for(y = clip_area_sub.y2; y >= clip_area_sub.y1; y--) { + blend_area.y1 = y; + blend_area.y2 = y; + + if(!simple_sub) { + lv_memcpy(mask_buf, sh_buf_tmp, corner_size); + blend_dsc.mask_res = lv_draw_mask_apply(mask_buf, clip_area_sub.x1, y, w); + if(blend_dsc.mask_res == LV_DRAW_MASK_RES_FULL_COVER) blend_dsc.mask_res = LV_DRAW_MASK_RES_CHANGED; + } + else { + blend_dsc.mask_buf = sh_buf_tmp; + } + lv_draw_sw_blend(draw_ctx, &blend_dsc); + sh_buf_tmp += corner_size; + } + } + } + + /*Top side*/ + blend_area.x1 = shadow_area.x1 + corner_size; + blend_area.x2 = shadow_area.x2 - corner_size; + blend_area.y1 = shadow_area.y1; + blend_area.y2 = shadow_area.y1 + corner_size - 1; + blend_area.y2 = LV_MIN(blend_area.y2, h_half); + + if(_lv_area_intersect(&clip_area_sub, &blend_area, draw_ctx->clip_area) && + !_lv_area_is_in(&clip_area_sub, &bg_area, r_bg)) { + lv_coord_t w = lv_area_get_width(&clip_area_sub); + sh_buf_tmp = sh_buf; + sh_buf_tmp += (clip_area_sub.y1 - blend_area.y1) * corner_size; + + /*Do not mask if out of the bg*/ + if(simple && _lv_area_is_out(&clip_area_sub, &bg_area, r_bg)) simple_sub = true; + else simple_sub = simple; + + if(w > 0) { + if(!simple_sub) { + blend_dsc.mask_buf = mask_buf; + } + else { + blend_dsc.mask_buf = NULL; + } + blend_area.x1 = clip_area_sub.x1; + blend_area.x2 = clip_area_sub.x2; + + for(y = clip_area_sub.y1; y <= clip_area_sub.y2; y++) { + blend_area.y1 = y; + blend_area.y2 = y; + + if(!simple_sub) { + lv_memset(mask_buf, sh_buf_tmp[0], w); + blend_dsc.mask_res = lv_draw_mask_apply(mask_buf, clip_area_sub.x1, y, w); + if(blend_dsc.mask_res == LV_DRAW_MASK_RES_FULL_COVER) blend_dsc.mask_res = LV_DRAW_MASK_RES_CHANGED; + lv_draw_sw_blend(draw_ctx, &blend_dsc); + } + else { + blend_dsc.opa = opa == LV_OPA_COVER ? sh_buf_tmp[0] : (sh_buf_tmp[0] * dsc->shadow_opa) >> 8; + lv_draw_sw_blend(draw_ctx, &blend_dsc); + } + sh_buf_tmp += corner_size; + } + } + } + blend_dsc.opa = dsc->shadow_opa; /*Restore*/ + + /*Bottom side*/ + blend_area.x1 = shadow_area.x1 + corner_size; + blend_area.x2 = shadow_area.x2 - corner_size; + blend_area.y1 = shadow_area.y2 - corner_size + 1; + blend_area.y2 = shadow_area.y2; + blend_area.y1 = LV_MAX(blend_area.y1, h_half + 1); + + + if(_lv_area_intersect(&clip_area_sub, &blend_area, draw_ctx->clip_area) && + !_lv_area_is_in(&clip_area_sub, &bg_area, r_bg)) { + lv_coord_t w = lv_area_get_width(&clip_area_sub); + sh_buf_tmp = sh_buf; + sh_buf_tmp += (blend_area.y2 - clip_area_sub.y2) * corner_size; + if(w > 0) { + /*Do not mask if out of the bg*/ + if(simple && _lv_area_is_out(&clip_area_sub, &bg_area, r_bg)) simple_sub = true; + else simple_sub = simple; + + if(!simple_sub) { + blend_dsc.mask_buf = mask_buf; + } + else { + blend_dsc.mask_buf = NULL; + } + blend_area.x1 = clip_area_sub.x1; + blend_area.x2 = clip_area_sub.x2; + + for(y = clip_area_sub.y2; y >= clip_area_sub.y1; y--) { + blend_area.y1 = y; + blend_area.y2 = y; + + /*Do not mask if out of the bg*/ + if(simple && _lv_area_is_out(&clip_area_sub, &bg_area, r_bg)) simple_sub = true; + else simple_sub = simple; + + if(!simple_sub) { + lv_memset(mask_buf, sh_buf_tmp[0], w); + blend_dsc.mask_res = lv_draw_mask_apply(mask_buf, clip_area_sub.x1, y, w); + if(blend_dsc.mask_res == LV_DRAW_MASK_RES_FULL_COVER) blend_dsc.mask_res = LV_DRAW_MASK_RES_CHANGED; + lv_draw_sw_blend(draw_ctx, &blend_dsc); + } + else { + blend_dsc.opa = opa == LV_OPA_COVER ? sh_buf_tmp[0] : (sh_buf_tmp[0] * dsc->shadow_opa) >> 8; + lv_draw_sw_blend(draw_ctx, &blend_dsc); + + } + sh_buf_tmp += corner_size; + } + } + } + + blend_dsc.opa = dsc->shadow_opa; /*Restore*/ + + /*Right side*/ + blend_area.x1 = shadow_area.x2 - corner_size + 1; + blend_area.x2 = shadow_area.x2; + blend_area.y1 = shadow_area.y1 + corner_size; + blend_area.y2 = shadow_area.y2 - corner_size; + /*Do not overdraw the other corners*/ + blend_area.y1 = LV_MIN(blend_area.y1, h_half + 1); + blend_area.y2 = LV_MAX(blend_area.y2, h_half); + blend_area.x1 = LV_MAX(blend_area.x1, w_half); + + if(_lv_area_intersect(&clip_area_sub, &blend_area, draw_ctx->clip_area) && + !_lv_area_is_in(&clip_area_sub, &bg_area, r_bg)) { + lv_coord_t w = lv_area_get_width(&clip_area_sub); + sh_buf_tmp = sh_buf; + sh_buf_tmp += (corner_size - 1) * corner_size; + sh_buf_tmp += clip_area_sub.x1 - (shadow_area.x2 - corner_size + 1); + + /*Do not mask if out of the bg*/ + if(simple && _lv_area_is_out(&clip_area_sub, &bg_area, r_bg)) simple_sub = true; + else simple_sub = simple; + blend_dsc.mask_buf = simple_sub ? sh_buf_tmp : mask_buf; + + if(w > 0) { + blend_area.x1 = clip_area_sub.x1; + blend_area.x2 = clip_area_sub.x2; + blend_dsc.mask_res = LV_DRAW_MASK_RES_CHANGED; /*In simple mode it won't be overwritten*/ + for(y = clip_area_sub.y1; y <= clip_area_sub.y2; y++) { + blend_area.y1 = y; + blend_area.y2 = y; + + if(!simple_sub) { + lv_memcpy(mask_buf, sh_buf_tmp, w); + blend_dsc.mask_res = lv_draw_mask_apply(mask_buf, clip_area_sub.x1, y, w); + if(blend_dsc.mask_res == LV_DRAW_MASK_RES_FULL_COVER) blend_dsc.mask_res = LV_DRAW_MASK_RES_CHANGED; + } + lv_draw_sw_blend(draw_ctx, &blend_dsc); + } + } + } + + /*Mirror the shadow corner buffer horizontally*/ + sh_buf_tmp = sh_buf ; + for(y = 0; y < corner_size; y++) { + int32_t x; + lv_opa_t * start = sh_buf_tmp; + lv_opa_t * end = sh_buf_tmp + corner_size - 1; + for(x = 0; x < corner_size / 2; x++) { + lv_opa_t tmp = *start; + *start = *end; + *end = tmp; + + start++; + end--; + } + sh_buf_tmp += corner_size; + } + + /*Left side*/ + blend_area.x1 = shadow_area.x1; + blend_area.x2 = shadow_area.x1 + corner_size - 1; + blend_area.y1 = shadow_area.y1 + corner_size; + blend_area.y2 = shadow_area.y2 - corner_size; + /*Do not overdraw the other corners*/ + blend_area.y1 = LV_MIN(blend_area.y1, h_half + 1); + blend_area.y2 = LV_MAX(blend_area.y2, h_half); + blend_area.x2 = LV_MIN(blend_area.x2, w_half - 1); + + if(_lv_area_intersect(&clip_area_sub, &blend_area, draw_ctx->clip_area) && + !_lv_area_is_in(&clip_area_sub, &bg_area, r_bg)) { + lv_coord_t w = lv_area_get_width(&clip_area_sub); + sh_buf_tmp = sh_buf; + sh_buf_tmp += (corner_size - 1) * corner_size; + sh_buf_tmp += clip_area_sub.x1 - blend_area.x1; + + /*Do not mask if out of the bg*/ + if(simple && _lv_area_is_out(&clip_area_sub, &bg_area, r_bg)) simple_sub = true; + else simple_sub = simple; + blend_dsc.mask_buf = simple_sub ? sh_buf_tmp : mask_buf; + if(w > 0) { + blend_area.x1 = clip_area_sub.x1; + blend_area.x2 = clip_area_sub.x2; + blend_dsc.mask_res = LV_DRAW_MASK_RES_CHANGED; /*In simple mode it won't be overwritten*/ + for(y = clip_area_sub.y1; y <= clip_area_sub.y2; y++) { + blend_area.y1 = y; + blend_area.y2 = y; + + if(!simple_sub) { + lv_memcpy(mask_buf, sh_buf_tmp, w); + blend_dsc.mask_res = lv_draw_mask_apply(mask_buf, clip_area_sub.x1, y, w); + if(blend_dsc.mask_res == LV_DRAW_MASK_RES_FULL_COVER) blend_dsc.mask_res = LV_DRAW_MASK_RES_CHANGED; + } + + lv_draw_sw_blend(draw_ctx, &blend_dsc); + } + } + } + + /*Top left corner*/ + blend_area.x1 = shadow_area.x1; + blend_area.x2 = shadow_area.x1 + corner_size - 1; + blend_area.y1 = shadow_area.y1; + blend_area.y2 = shadow_area.y1 + corner_size - 1; + /*Do not overdraw the other corners*/ + blend_area.x2 = LV_MIN(blend_area.x2, w_half - 1); + blend_area.y2 = LV_MIN(blend_area.y2, h_half); + + if(_lv_area_intersect(&clip_area_sub, &blend_area, draw_ctx->clip_area) && + !_lv_area_is_in(&clip_area_sub, &bg_area, r_bg)) { + lv_coord_t w = lv_area_get_width(&clip_area_sub); + sh_buf_tmp = sh_buf; + sh_buf_tmp += (clip_area_sub.y1 - blend_area.y1) * corner_size; + sh_buf_tmp += clip_area_sub.x1 - blend_area.x1; + + /*Do not mask if out of the bg*/ + if(simple && _lv_area_is_out(&clip_area_sub, &bg_area, r_bg)) simple_sub = true; + else simple_sub = simple; + blend_dsc.mask_buf = mask_buf; + + if(w > 0) { + blend_area.x1 = clip_area_sub.x1; + blend_area.x2 = clip_area_sub.x2; + blend_dsc.mask_res = LV_DRAW_MASK_RES_CHANGED; /*In simple mode it won't be overwritten*/ + for(y = clip_area_sub.y1; y <= clip_area_sub.y2; y++) { + blend_area.y1 = y; + blend_area.y2 = y; + + if(!simple_sub) { + lv_memcpy(mask_buf, sh_buf_tmp, corner_size); + blend_dsc.mask_res = lv_draw_mask_apply(mask_buf, clip_area_sub.x1, y, w); + if(blend_dsc.mask_res == LV_DRAW_MASK_RES_FULL_COVER) blend_dsc.mask_res = LV_DRAW_MASK_RES_CHANGED; + } + else { + blend_dsc.mask_buf = sh_buf_tmp; + } + + lv_draw_sw_blend(draw_ctx, &blend_dsc); + sh_buf_tmp += corner_size; + } + } + } + + /*Bottom left corner. + *Almost the same as bottom right just read the lines of `sh_buf` from then end*/ + blend_area.x1 = shadow_area.x1 ; + blend_area.x2 = shadow_area.x1 + corner_size - 1; + blend_area.y1 = shadow_area.y2 - corner_size + 1; + blend_area.y2 = shadow_area.y2; + /*Do not overdraw the other corners*/ + blend_area.y1 = LV_MAX(blend_area.y1, h_half + 1); + blend_area.x2 = LV_MIN(blend_area.x2, w_half - 1); + + if(_lv_area_intersect(&clip_area_sub, &blend_area, draw_ctx->clip_area) && + !_lv_area_is_in(&clip_area_sub, &bg_area, r_bg)) { + lv_coord_t w = lv_area_get_width(&clip_area_sub); + sh_buf_tmp = sh_buf; + sh_buf_tmp += (blend_area.y2 - clip_area_sub.y2) * corner_size; + sh_buf_tmp += clip_area_sub.x1 - blend_area.x1; + + /*Do not mask if out of the bg*/ + if(simple && _lv_area_is_out(&clip_area_sub, &bg_area, r_bg)) simple_sub = true; + else simple_sub = simple; + blend_dsc.mask_buf = mask_buf; + if(w > 0) { + blend_area.x1 = clip_area_sub.x1; + blend_area.x2 = clip_area_sub.x2; + blend_dsc.mask_res = LV_DRAW_MASK_RES_CHANGED; /*In simple mode it won't be overwritten*/ + for(y = clip_area_sub.y2; y >= clip_area_sub.y1; y--) { + blend_area.y1 = y; + blend_area.y2 = y; + + if(!simple_sub) { + lv_memcpy(mask_buf, sh_buf_tmp, corner_size); + blend_dsc.mask_res = lv_draw_mask_apply(mask_buf, clip_area_sub.x1, y, w); + if(blend_dsc.mask_res == LV_DRAW_MASK_RES_FULL_COVER) blend_dsc.mask_res = LV_DRAW_MASK_RES_CHANGED; + } + else { + blend_dsc.mask_buf = sh_buf_tmp; + } + lv_draw_sw_blend(draw_ctx, &blend_dsc); + sh_buf_tmp += corner_size; + } + } + } + + /*Draw the center rectangle.*/ + blend_area.x1 = shadow_area.x1 + corner_size ; + blend_area.x2 = shadow_area.x2 - corner_size; + blend_area.y1 = shadow_area.y1 + corner_size; + blend_area.y2 = shadow_area.y2 - corner_size; + blend_dsc.mask_buf = mask_buf; + + if(_lv_area_intersect(&clip_area_sub, &blend_area, draw_ctx->clip_area) && + !_lv_area_is_in(&clip_area_sub, &bg_area, r_bg)) { + lv_coord_t w = lv_area_get_width(&clip_area_sub); + if(w > 0) { + blend_area.x1 = clip_area_sub.x1; + blend_area.x2 = clip_area_sub.x2; + for(y = clip_area_sub.y1; y <= clip_area_sub.y2; y++) { + blend_area.y1 = y; + blend_area.y2 = y; + + lv_memset_ff(mask_buf, w); + blend_dsc.mask_res = lv_draw_mask_apply(mask_buf, clip_area_sub.x1, y, w); + lv_draw_sw_blend(draw_ctx, &blend_dsc); + } + } + } + + if(!simple) { + lv_draw_mask_free_param(&mask_rout_param); + lv_draw_mask_remove_id(mask_rout_id); + } + lv_mem_buf_release(sh_buf); + lv_mem_buf_release(mask_buf); +} + +/** + * Calculate a blurred corner + * @param coords Coordinates of the shadow + * @param sh_buf a buffer to store the result. Its size should be `(sw + r)^2 * 2` + * @param sw shadow width + * @param r radius + */ +LV_ATTRIBUTE_FAST_MEM static void shadow_draw_corner_buf(const lv_area_t * coords, uint16_t * sh_buf, lv_coord_t sw, + lv_coord_t r) +{ + int32_t sw_ori = sw; + int32_t size = sw_ori + r; + + lv_area_t sh_area; + lv_area_copy(&sh_area, coords); + sh_area.x2 = sw / 2 + r - 1 - ((sw & 1) ? 0 : 1); + sh_area.y1 = sw / 2 + 1; + + sh_area.x1 = sh_area.x2 - lv_area_get_width(coords); + sh_area.y2 = sh_area.y1 + lv_area_get_height(coords); + + lv_draw_mask_radius_param_t mask_param; + lv_draw_mask_radius_init(&mask_param, &sh_area, r, false); + +#if SHADOW_ENHANCE + /*Set half shadow width width because blur will be repeated*/ + if(sw_ori == 1) sw = 1; + else sw = sw_ori >> 1; +#endif + + int32_t y; + lv_opa_t * mask_line = lv_mem_buf_get(size); + uint16_t * sh_ups_tmp_buf = (uint16_t *)sh_buf; + for(y = 0; y < size; y++) { + lv_memset_ff(mask_line, size); + lv_draw_mask_res_t mask_res = mask_param.dsc.cb(mask_line, 0, y, size, &mask_param); + if(mask_res == LV_DRAW_MASK_RES_TRANSP) { + lv_memset_00(sh_ups_tmp_buf, size * sizeof(sh_ups_tmp_buf[0])); + } + else { + int32_t i; + sh_ups_tmp_buf[0] = (mask_line[0] << SHADOW_UPSCALE_SHIFT) / sw; + for(i = 1; i < size; i++) { + if(mask_line[i] == mask_line[i - 1]) sh_ups_tmp_buf[i] = sh_ups_tmp_buf[i - 1]; + else sh_ups_tmp_buf[i] = (mask_line[i] << SHADOW_UPSCALE_SHIFT) / sw; + } + } + + sh_ups_tmp_buf += size; + } + lv_mem_buf_release(mask_line); + + lv_draw_mask_free_param(&mask_param); + + if(sw == 1) { + int32_t i; + lv_opa_t * res_buf = (lv_opa_t *)sh_buf; + for(i = 0; i < size * size; i++) { + res_buf[i] = (sh_buf[i] >> SHADOW_UPSCALE_SHIFT); + } + return; + } + + shadow_blur_corner(size, sw, sh_buf); + +#if SHADOW_ENHANCE == 0 + /*The result is required in lv_opa_t not uint16_t*/ + uint32_t x; + lv_opa_t * res_buf = (lv_opa_t *)sh_buf; + for(x = 0; x < size * size; x++) { + res_buf[x] = sh_buf[x]; + } +#else + sw += sw_ori & 1; + if(sw > 1) { + uint32_t i; + uint32_t max_v_div = (LV_OPA_COVER << SHADOW_UPSCALE_SHIFT) / sw; + for(i = 0; i < (uint32_t)size * size; i++) { + if(sh_buf[i] == 0) continue; + else if(sh_buf[i] == LV_OPA_COVER) sh_buf[i] = max_v_div; + else sh_buf[i] = (sh_buf[i] << SHADOW_UPSCALE_SHIFT) / sw; + } + + shadow_blur_corner(size, sw, sh_buf); + } + int32_t x; + lv_opa_t * res_buf = (lv_opa_t *)sh_buf; + for(x = 0; x < size * size; x++) { + res_buf[x] = sh_buf[x]; + } +#endif + +} + +LV_ATTRIBUTE_FAST_MEM static void shadow_blur_corner(lv_coord_t size, lv_coord_t sw, uint16_t * sh_ups_buf) +{ + int32_t s_left = sw >> 1; + int32_t s_right = (sw >> 1); + if((sw & 1) == 0) s_left--; + + /*Horizontal blur*/ + uint16_t * sh_ups_blur_buf = lv_mem_buf_get(size * sizeof(uint16_t)); + + int32_t x; + int32_t y; + + uint16_t * sh_ups_tmp_buf = sh_ups_buf; + + for(y = 0; y < size; y++) { + int32_t v = sh_ups_tmp_buf[size - 1] * sw; + for(x = size - 1; x >= 0; x--) { + sh_ups_blur_buf[x] = v; + + /*Forget the right pixel*/ + uint32_t right_val = 0; + if(x + s_right < size) right_val = sh_ups_tmp_buf[x + s_right]; + v -= right_val; + + /*Add the left pixel*/ + uint32_t left_val; + if(x - s_left - 1 < 0) left_val = sh_ups_tmp_buf[0]; + else left_val = sh_ups_tmp_buf[x - s_left - 1]; + v += left_val; + } + lv_memcpy(sh_ups_tmp_buf, sh_ups_blur_buf, size * sizeof(uint16_t)); + sh_ups_tmp_buf += size; + } + + /*Vertical blur*/ + uint32_t i; + uint32_t max_v = LV_OPA_COVER << SHADOW_UPSCALE_SHIFT; + uint32_t max_v_div = max_v / sw; + for(i = 0; i < (uint32_t)size * size; i++) { + if(sh_ups_buf[i] == 0) continue; + else if(sh_ups_buf[i] == max_v) sh_ups_buf[i] = max_v_div; + else sh_ups_buf[i] = sh_ups_buf[i] / sw; + } + + for(x = 0; x < size; x++) { + sh_ups_tmp_buf = &sh_ups_buf[x]; + int32_t v = sh_ups_tmp_buf[0] * sw; + for(y = 0; y < size ; y++, sh_ups_tmp_buf += size) { + sh_ups_blur_buf[y] = v < 0 ? 0 : (v >> SHADOW_UPSCALE_SHIFT); + + /*Forget the top pixel*/ + uint32_t top_val; + if(y - s_right <= 0) top_val = sh_ups_tmp_buf[0]; + else top_val = sh_ups_buf[(y - s_right) * size + x]; + v -= top_val; + + /*Add the bottom pixel*/ + uint32_t bottom_val; + if(y + s_left + 1 < size) bottom_val = sh_ups_buf[(y + s_left + 1) * size + x]; + else bottom_val = sh_ups_buf[(size - 1) * size + x]; + v += bottom_val; + } + + /*Write back the result into `sh_ups_buf`*/ + sh_ups_tmp_buf = &sh_ups_buf[x]; + for(y = 0; y < size; y++, sh_ups_tmp_buf += size) { + (*sh_ups_tmp_buf) = sh_ups_blur_buf[y]; + } + } + + lv_mem_buf_release(sh_ups_blur_buf); +} +#endif + +static void draw_outline(lv_draw_ctx_t * draw_ctx, const lv_draw_rect_dsc_t * dsc, const lv_area_t * coords) +{ + if(dsc->outline_opa <= LV_OPA_MIN) return; + if(dsc->outline_width == 0) return; + + lv_opa_t opa = dsc->outline_opa; + + if(opa > LV_OPA_MAX) opa = LV_OPA_COVER; + + /*Get the inner radius*/ + lv_area_t area_inner; + lv_area_copy(&area_inner, coords); + + /*Bring the outline closer to make sure there is no color bleeding with pad=0*/ + lv_coord_t pad = dsc->outline_pad - 1; + area_inner.x1 -= pad; + area_inner.y1 -= pad; + area_inner.x2 += pad; + area_inner.y2 += pad; + + lv_area_t area_outer; + lv_area_copy(&area_outer, &area_inner); + + area_outer.x1 -= dsc->outline_width; + area_outer.x2 += dsc->outline_width; + area_outer.y1 -= dsc->outline_width; + area_outer.y2 += dsc->outline_width; + + + int32_t inner_w = lv_area_get_width(&area_inner); + int32_t inner_h = lv_area_get_height(&area_inner); + int32_t rin = dsc->radius; + int32_t short_side = LV_MIN(inner_w, inner_h); + if(rin > short_side >> 1) rin = short_side >> 1; + + lv_coord_t rout = rin + dsc->outline_width; + + draw_border_generic(draw_ctx, &area_outer, &area_inner, rout, rin, dsc->outline_color, dsc->outline_opa, + dsc->blend_mode); +} + +void draw_border_generic(lv_draw_ctx_t * draw_ctx, const lv_area_t * outer_area, const lv_area_t * inner_area, + lv_coord_t rout, lv_coord_t rin, lv_color_t color, lv_opa_t opa, lv_blend_mode_t blend_mode) +{ + opa = opa >= LV_OPA_COVER ? LV_OPA_COVER : opa; + + bool mask_any = lv_draw_mask_is_any(outer_area); + +#if LV_DRAW_COMPLEX + + if(!mask_any && rout == 0 && rin == 0) { + draw_border_simple(draw_ctx, outer_area, inner_area, color, opa); + return; + } + + /*Get clipped draw area which is the real draw area. + *It is always the same or inside `coords`*/ + lv_area_t draw_area; + if(!_lv_area_intersect(&draw_area, outer_area, draw_ctx->clip_area)) return; + int32_t draw_area_w = lv_area_get_width(&draw_area); + + lv_draw_sw_blend_dsc_t blend_dsc; + lv_memset_00(&blend_dsc, sizeof(blend_dsc)); + blend_dsc.mask_buf = lv_mem_buf_get(draw_area_w);; + + + /*Create mask for the outer area*/ + int16_t mask_rout_id = LV_MASK_ID_INV; + lv_draw_mask_radius_param_t mask_rout_param; + if(rout > 0) { + lv_draw_mask_radius_init(&mask_rout_param, outer_area, rout, false); + mask_rout_id = lv_draw_mask_add(&mask_rout_param, NULL); + } + + /*Create mask for the inner mask*/ + lv_draw_mask_radius_param_t mask_rin_param; + lv_draw_mask_radius_init(&mask_rin_param, inner_area, rin, true); + int16_t mask_rin_id = lv_draw_mask_add(&mask_rin_param, NULL); + + int32_t h; + lv_area_t blend_area; + blend_dsc.blend_area = &blend_area; + blend_dsc.mask_area = &blend_area; + blend_dsc.color = color; + blend_dsc.opa = opa; + blend_dsc.blend_mode = blend_mode; + + /*Calculate the x and y coordinates where the straight parts area*/ + lv_area_t core_area; + core_area.x1 = LV_MAX(outer_area->x1 + rout, inner_area->x1); + core_area.x2 = LV_MIN(outer_area->x2 - rout, inner_area->x2); + core_area.y1 = LV_MAX(outer_area->y1 + rout, inner_area->y1); + core_area.y2 = LV_MIN(outer_area->y2 - rout, inner_area->y2); + lv_coord_t core_w = lv_area_get_width(&core_area); + + bool top_side = outer_area->y1 <= inner_area->y1 ? true : false; + bool bottom_side = outer_area->y2 >= inner_area->y2 ? true : false; + + /*If there is other masks, need to draw line by line*/ + if(mask_any) { + blend_area.x1 = draw_area.x1; + blend_area.x2 = draw_area.x2; + for(h = draw_area.y1; h <= draw_area.y2; h++) { + if(!top_side && h < core_area.y1) continue; + if(!bottom_side && h > core_area.y2) break; + + blend_area.y1 = h; + blend_area.y2 = h; + + lv_memset_ff(blend_dsc.mask_buf, draw_area_w); + blend_dsc.mask_res = lv_draw_mask_apply(blend_dsc.mask_buf, draw_area.x1, h, draw_area_w); + lv_draw_sw_blend(draw_ctx, &blend_dsc); + } + + lv_draw_mask_free_param(&mask_rin_param); + lv_draw_mask_remove_id(mask_rin_id); + if(mask_rout_id != LV_MASK_ID_INV) { + lv_draw_mask_free_param(&mask_rout_param); + lv_draw_mask_remove_id(mask_rout_id); + } + lv_mem_buf_release(blend_dsc.mask_buf); + return; + } + + /*No masks*/ + bool left_side = outer_area->x1 <= inner_area->x1 ? true : false; + bool right_side = outer_area->x2 >= inner_area->x2 ? true : false; + + bool split_hor = true; + if(left_side && right_side && top_side && bottom_side && + core_w < SPLIT_LIMIT) { + split_hor = false; + } + + blend_dsc.mask_res = LV_DRAW_MASK_RES_FULL_COVER; + /*Draw the straight lines first if they are long enough*/ + if(top_side && split_hor) { + blend_area.x1 = core_area.x1; + blend_area.x2 = core_area.x2; + blend_area.y1 = outer_area->y1; + blend_area.y2 = inner_area->y1 - 1; + lv_draw_sw_blend(draw_ctx, &blend_dsc); + } + + if(bottom_side && split_hor) { + blend_area.x1 = core_area.x1; + blend_area.x2 = core_area.x2; + blend_area.y1 = inner_area->y2 + 1; + blend_area.y2 = outer_area->y2; + lv_draw_sw_blend(draw_ctx, &blend_dsc); + } + + if(left_side) { + blend_area.x1 = outer_area->x1; + blend_area.x2 = inner_area->x1 - 1; + blend_area.y1 = core_area.y1; + blend_area.y2 = core_area.y2; + lv_draw_sw_blend(draw_ctx, &blend_dsc); + } + + if(right_side) { + blend_area.x1 = inner_area->x2 + 1; + blend_area.x2 = outer_area->x2; + blend_area.y1 = core_area.y1; + blend_area.y2 = core_area.y2; + lv_draw_sw_blend(draw_ctx, &blend_dsc); + } + + /*Draw the corners*/ + lv_coord_t blend_w; + + /*Left and right corner together if they are close to each other*/ + if(!split_hor) { + /*Calculate the top corner and mirror it to the bottom*/ + blend_area.x1 = draw_area.x1; + blend_area.x2 = draw_area.x2; + lv_coord_t max_h = LV_MAX(rout, inner_area->y1 - outer_area->y1); + for(h = 0; h < max_h; h++) { + lv_coord_t top_y = outer_area->y1 + h; + lv_coord_t bottom_y = outer_area->y2 - h; + if(top_y < draw_area.y1 && bottom_y > draw_area.y2) continue; /*This line is clipped now*/ + + lv_memset_ff(blend_dsc.mask_buf, draw_area_w); + blend_dsc.mask_res = lv_draw_mask_apply(blend_dsc.mask_buf, blend_area.x1, top_y, draw_area_w); + + if(top_y >= draw_area.y1) { + blend_area.y1 = top_y; + blend_area.y2 = top_y; + lv_draw_sw_blend(draw_ctx, &blend_dsc); + } + + if(bottom_y <= draw_area.y2) { + blend_area.y1 = bottom_y; + blend_area.y2 = bottom_y; + lv_draw_sw_blend(draw_ctx, &blend_dsc); + } + } + } + else { + /*Left corners*/ + blend_area.x1 = draw_area.x1; + blend_area.x2 = LV_MIN(draw_area.x2, core_area.x1 - 1); + blend_w = lv_area_get_width(&blend_area); + if(blend_w > 0) { + if(left_side || top_side) { + for(h = draw_area.y1; h < core_area.y1; h++) { + blend_area.y1 = h; + blend_area.y2 = h; + + lv_memset_ff(blend_dsc.mask_buf, blend_w); + blend_dsc.mask_res = lv_draw_mask_apply(blend_dsc.mask_buf, blend_area.x1, h, blend_w); + lv_draw_sw_blend(draw_ctx, &blend_dsc); + } + } + + if(left_side || bottom_side) { + for(h = core_area.y2 + 1; h <= draw_area.y2; h++) { + blend_area.y1 = h; + blend_area.y2 = h; + + lv_memset_ff(blend_dsc.mask_buf, blend_w); + blend_dsc.mask_res = lv_draw_mask_apply(blend_dsc.mask_buf, blend_area.x1, h, blend_w); + lv_draw_sw_blend(draw_ctx, &blend_dsc); + } + } + } + + /*Right corners*/ + blend_area.x1 = LV_MAX(draw_area.x1, core_area.x2 + 1); + blend_area.x2 = draw_area.x2; + blend_w = lv_area_get_width(&blend_area); + + if(blend_w > 0) { + if(right_side || top_side) { + for(h = draw_area.y1; h < core_area.y1; h++) { + blend_area.y1 = h; + blend_area.y2 = h; + + lv_memset_ff(blend_dsc.mask_buf, blend_w); + blend_dsc.mask_res = lv_draw_mask_apply(blend_dsc.mask_buf, blend_area.x1, h, blend_w); + lv_draw_sw_blend(draw_ctx, &blend_dsc); + } + } + + if(right_side || bottom_side) { + for(h = core_area.y2 + 1; h <= draw_area.y2; h++) { + blend_area.y1 = h; + blend_area.y2 = h; + + lv_memset_ff(blend_dsc.mask_buf, blend_w); + blend_dsc.mask_res = lv_draw_mask_apply(blend_dsc.mask_buf, blend_area.x1, h, blend_w); + lv_draw_sw_blend(draw_ctx, &blend_dsc); + } + } + } + } + + lv_draw_mask_free_param(&mask_rin_param); + lv_draw_mask_remove_id(mask_rin_id); + lv_draw_mask_free_param(&mask_rout_param); + lv_draw_mask_remove_id(mask_rout_id); + lv_mem_buf_release(blend_dsc.mask_buf); + +#else /*LV_DRAW_COMPLEX*/ + LV_UNUSED(blend_mode); + LV_UNUSED(rout); + LV_UNUSED(rin); + if(!mask_any) { + draw_border_simple(draw_ctx, outer_area, inner_area, color, opa); + return; + } + +#endif /*LV_DRAW_COMPLEX*/ +} +static void draw_border_simple(lv_draw_ctx_t * draw_ctx, const lv_area_t * outer_area, const lv_area_t * inner_area, + lv_color_t color, lv_opa_t opa) +{ + lv_area_t a; + lv_draw_sw_blend_dsc_t blend_dsc; + lv_memset_00(&blend_dsc, sizeof(lv_draw_sw_blend_dsc_t)); + blend_dsc.blend_area = &a; + blend_dsc.color = color; + blend_dsc.opa = opa; + + bool top_side = outer_area->y1 <= inner_area->y1 ? true : false; + bool bottom_side = outer_area->y2 >= inner_area->y2 ? true : false; + bool left_side = outer_area->x1 <= inner_area->x1 ? true : false; + bool right_side = outer_area->x2 >= inner_area->x2 ? true : false; + + + /*Top*/ + a.x1 = outer_area->x1; + a.x2 = outer_area->x2; + a.y1 = outer_area->y1; + a.y2 = inner_area->y1 - 1; + if(top_side) { + lv_draw_sw_blend(draw_ctx, &blend_dsc); + } + + /*Bottom*/ + a.y1 = inner_area->y2 + 1; + a.y2 = outer_area->y2; + if(bottom_side) { + lv_draw_sw_blend(draw_ctx, &blend_dsc); + } + + /*Left*/ + a.x1 = outer_area->x1; + a.x2 = inner_area->x1 - 1; + a.y1 = (top_side) ? inner_area->y1 : outer_area->y1; + a.y2 = (bottom_side) ? inner_area->y2 : outer_area->y2; + if(left_side) { + lv_draw_sw_blend(draw_ctx, &blend_dsc); + } + + /*Right*/ + a.x1 = inner_area->x2 + 1; + a.x2 = outer_area->x2; + if(right_side) { + lv_draw_sw_blend(draw_ctx, &blend_dsc); + } +} + diff --git a/lib/lvgl/src/draw/sw/lv_draw_sw_transform.c b/lib/lvgl/src/draw/sw/lv_draw_sw_transform.c new file mode 100644 index 00000000..80b1e6de --- /dev/null +++ b/lib/lvgl/src/draw/sw/lv_draw_sw_transform.c @@ -0,0 +1,496 @@ +/** + * @file lv_draw_sw_tranform.c + * + */ + +/********************* + * INCLUDES + *********************/ +#include "lv_draw_sw.h" +#include "../../misc/lv_assert.h" +#include "../../misc/lv_area.h" +#include "../../core/lv_refr.h" + +#if LV_DRAW_COMPLEX +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ +typedef struct { + int32_t x_in; + int32_t y_in; + int32_t x_out; + int32_t y_out; + int32_t sinma; + int32_t cosma; + int32_t zoom; + int32_t angle; + int32_t pivot_x_256; + int32_t pivot_y_256; + lv_point_t pivot; +} point_transform_dsc_t; + +/********************** + * STATIC PROTOTYPES + **********************/ +/** + * Transform a point with 1/256 precision (the output coordinates are upscaled by 256) + * @param t pointer to n initialized `point_transform_dsc_t` structure + * @param xin X coordinate to rotate + * @param yin Y coordinate to rotate + * @param xout upscaled, transformed X + * @param yout upscaled, transformed Y + */ +static void transform_point_upscaled(point_transform_dsc_t * t, int32_t xin, int32_t yin, int32_t * xout, + int32_t * yout); + +static void argb_no_aa(const uint8_t * src, lv_coord_t src_w, lv_coord_t src_h, lv_coord_t src_stride, + int32_t xs_ups, int32_t ys_ups, int32_t xs_step, int32_t ys_step, + int32_t x_end, lv_color_t * cbuf, uint8_t * abuf); + +static void rgb_no_aa(const uint8_t * src, lv_coord_t src_w, lv_coord_t src_h, lv_coord_t src_stride, + int32_t xs_ups, int32_t ys_ups, int32_t xs_step, int32_t ys_step, + int32_t x_end, lv_color_t * cbuf, uint8_t * abuf, lv_img_cf_t cf); + +#if LV_COLOR_DEPTH == 16 +static void rgb565a8_no_aa(const uint8_t * src, lv_coord_t src_w, lv_coord_t src_h, lv_coord_t src_stride, + int32_t xs_ups, int32_t ys_ups, int32_t xs_step, int32_t ys_step, + int32_t x_end, lv_color_t * cbuf, uint8_t * abuf); +#endif + +static void argb_and_rgb_aa(const uint8_t * src, lv_coord_t src_w, lv_coord_t src_h, lv_coord_t src_stride, + int32_t xs_ups, int32_t ys_ups, int32_t xs_step, int32_t ys_step, + int32_t x_end, lv_color_t * cbuf, uint8_t * abuf, lv_img_cf_t cf); + +/********************** + * STATIC VARIABLES + **********************/ + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +void lv_draw_sw_transform(lv_draw_ctx_t * draw_ctx, const lv_area_t * dest_area, const void * src_buf, + lv_coord_t src_w, lv_coord_t src_h, lv_coord_t src_stride, + const lv_draw_img_dsc_t * draw_dsc, lv_img_cf_t cf, lv_color_t * cbuf, lv_opa_t * abuf) +{ + LV_UNUSED(draw_ctx); + + point_transform_dsc_t tr_dsc; + tr_dsc.angle = -draw_dsc->angle; + tr_dsc.zoom = (256 * 256) / draw_dsc->zoom; + tr_dsc.pivot = draw_dsc->pivot; + + int32_t angle_low = tr_dsc.angle / 10; + int32_t angle_high = angle_low + 1; + int32_t angle_rem = tr_dsc.angle - (angle_low * 10); + + int32_t s1 = lv_trigo_sin(angle_low); + int32_t s2 = lv_trigo_sin(angle_high); + + int32_t c1 = lv_trigo_sin(angle_low + 90); + int32_t c2 = lv_trigo_sin(angle_high + 90); + + tr_dsc.sinma = (s1 * (10 - angle_rem) + s2 * angle_rem) / 10; + tr_dsc.cosma = (c1 * (10 - angle_rem) + c2 * angle_rem) / 10; + tr_dsc.sinma = tr_dsc.sinma >> (LV_TRIGO_SHIFT - 10); + tr_dsc.cosma = tr_dsc.cosma >> (LV_TRIGO_SHIFT - 10); + tr_dsc.pivot_x_256 = tr_dsc.pivot.x * 256; + tr_dsc.pivot_y_256 = tr_dsc.pivot.y * 256; + + lv_coord_t dest_w = lv_area_get_width(dest_area); + lv_coord_t dest_h = lv_area_get_height(dest_area); + lv_coord_t y; + for(y = 0; y < dest_h; y++) { + int32_t xs1_ups, ys1_ups, xs2_ups, ys2_ups; + + transform_point_upscaled(&tr_dsc, dest_area->x1, dest_area->y1 + y, &xs1_ups, &ys1_ups); + transform_point_upscaled(&tr_dsc, dest_area->x2, dest_area->y1 + y, &xs2_ups, &ys2_ups); + + int32_t xs_diff = xs2_ups - xs1_ups; + int32_t ys_diff = ys2_ups - ys1_ups; + int32_t xs_step_256 = 0; + int32_t ys_step_256 = 0; + if(dest_w > 1) { + xs_step_256 = (256 * xs_diff) / (dest_w - 1); + ys_step_256 = (256 * ys_diff) / (dest_w - 1); + } + int32_t xs_ups = xs1_ups; + int32_t ys_ups = ys1_ups; + + if(draw_dsc->antialias == 0) { + switch(cf) { + case LV_IMG_CF_TRUE_COLOR_ALPHA: + argb_no_aa(src_buf, src_w, src_h, src_stride, xs_ups, ys_ups, xs_step_256, ys_step_256, dest_w, cbuf, abuf); + break; + case LV_IMG_CF_TRUE_COLOR: + case LV_IMG_CF_TRUE_COLOR_CHROMA_KEYED: + rgb_no_aa(src_buf, src_w, src_h, src_stride, xs_ups, ys_ups, xs_step_256, ys_step_256, dest_w, cbuf, abuf, cf); + break; + +#if LV_COLOR_DEPTH == 16 + case LV_IMG_CF_RGB565A8: + rgb565a8_no_aa(src_buf, src_w, src_h, src_stride, xs_ups, ys_ups, xs_step_256, ys_step_256, dest_w, cbuf, abuf); + break; +#endif + default: + break; + } + } + else { + argb_and_rgb_aa(src_buf, src_w, src_h, src_stride, xs_ups, ys_ups, xs_step_256, ys_step_256, dest_w, cbuf, abuf, cf); + } + + cbuf += dest_w; + abuf += dest_w; + } +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +static void rgb_no_aa(const uint8_t * src, lv_coord_t src_w, lv_coord_t src_h, lv_coord_t src_stride, + int32_t xs_ups, int32_t ys_ups, int32_t xs_step, int32_t ys_step, + int32_t x_end, lv_color_t * cbuf, uint8_t * abuf, lv_img_cf_t cf) +{ + int32_t xs_ups_start = xs_ups; + int32_t ys_ups_start = ys_ups; + lv_disp_t * d = _lv_refr_get_disp_refreshing(); + lv_color_t ck = d->driver->color_chroma_key; + + lv_memset_ff(abuf, x_end); + + lv_coord_t x; + for(x = 0; x < x_end; x++) { + xs_ups = xs_ups_start + ((xs_step * x) >> 8); + ys_ups = ys_ups_start + ((ys_step * x) >> 8); + + int32_t xs_int = xs_ups >> 8; + int32_t ys_int = ys_ups >> 8; + if(xs_int < 0 || xs_int >= src_w || ys_int < 0 || ys_int >= src_h) { + abuf[x] = 0x00; + } + else { + +#if LV_COLOR_DEPTH == 8 + const uint8_t * src_tmp = src; + src_tmp += ys_int * src_stride + xs_int; + cbuf[x].full = src_tmp[0]; +#elif LV_COLOR_DEPTH == 16 + const lv_color_t * src_tmp = (const lv_color_t *)src; + src_tmp += ys_int * src_stride + xs_int; + cbuf[x] = *src_tmp; +#elif LV_COLOR_DEPTH == 32 + const uint8_t * src_tmp = src; + src_tmp += (ys_int * src_stride * sizeof(lv_color_t)) + xs_int * sizeof(lv_color_t); + cbuf[x].full = *((uint32_t *)src_tmp); +#endif + } + if(cf == LV_IMG_CF_TRUE_COLOR_CHROMA_KEYED && cbuf[x].full == ck.full) { + abuf[x] = 0x00; + } + } +} + +static void argb_no_aa(const uint8_t * src, lv_coord_t src_w, lv_coord_t src_h, lv_coord_t src_stride, + int32_t xs_ups, int32_t ys_ups, int32_t xs_step, int32_t ys_step, + int32_t x_end, lv_color_t * cbuf, uint8_t * abuf) +{ + int32_t xs_ups_start = xs_ups; + int32_t ys_ups_start = ys_ups; + + lv_coord_t x; + for(x = 0; x < x_end; x++) { + xs_ups = xs_ups_start + ((xs_step * x) >> 8); + ys_ups = ys_ups_start + ((ys_step * x) >> 8); + + int32_t xs_int = xs_ups >> 8; + int32_t ys_int = ys_ups >> 8; + if(xs_int < 0 || xs_int >= src_w || ys_int < 0 || ys_int >= src_h) { + abuf[x] = 0; + } + else { + const uint8_t * src_tmp = src; + src_tmp += (ys_int * src_stride * LV_IMG_PX_SIZE_ALPHA_BYTE) + xs_int * LV_IMG_PX_SIZE_ALPHA_BYTE; + +#if LV_COLOR_DEPTH == 8 + cbuf[x].full = src_tmp[0]; +#elif LV_COLOR_DEPTH == 16 + cbuf[x].full = src_tmp[0] + (src_tmp[1] << 8); +#elif LV_COLOR_DEPTH == 32 + cbuf[x].full = *((uint32_t *)src_tmp); +#endif + abuf[x] = src_tmp[LV_IMG_PX_SIZE_ALPHA_BYTE - 1]; + } + } +} + +#if LV_COLOR_DEPTH == 16 +static void rgb565a8_no_aa(const uint8_t * src, lv_coord_t src_w, lv_coord_t src_h, lv_coord_t src_stride, + int32_t xs_ups, int32_t ys_ups, int32_t xs_step, int32_t ys_step, + int32_t x_end, lv_color_t * cbuf, uint8_t * abuf) +{ + int32_t xs_ups_start = xs_ups; + int32_t ys_ups_start = ys_ups; + + lv_coord_t x; + for(x = 0; x < x_end; x++) { + xs_ups = xs_ups_start + ((xs_step * x) >> 8); + ys_ups = ys_ups_start + ((ys_step * x) >> 8); + + int32_t xs_int = xs_ups >> 8; + int32_t ys_int = ys_ups >> 8; + if(xs_int < 0 || xs_int >= src_w || ys_int < 0 || ys_int >= src_h) { + abuf[x] = 0; + } + else { + const lv_color_t * src_tmp = (const lv_color_t *)src; + src_tmp += ys_int * src_stride + xs_int; + cbuf[x] = *src_tmp; + + const lv_opa_t * a_tmp = src + src_stride * src_h * sizeof(lv_color_t); + a_tmp += ys_int * src_stride + xs_int; + abuf[x] = *a_tmp; + } + } +} +#endif + + +static void argb_and_rgb_aa(const uint8_t * src, lv_coord_t src_w, lv_coord_t src_h, lv_coord_t src_stride, + int32_t xs_ups, int32_t ys_ups, int32_t xs_step, int32_t ys_step, + int32_t x_end, lv_color_t * cbuf, uint8_t * abuf, lv_img_cf_t cf) +{ + int32_t xs_ups_start = xs_ups; + int32_t ys_ups_start = ys_ups; + bool has_alpha; + int32_t px_size; + lv_color_t ck = {0}; + switch(cf) { + case LV_IMG_CF_TRUE_COLOR: + has_alpha = false; + px_size = sizeof(lv_color_t); + break; + case LV_IMG_CF_TRUE_COLOR_ALPHA: + has_alpha = true; + px_size = LV_IMG_PX_SIZE_ALPHA_BYTE; + break; + case LV_IMG_CF_TRUE_COLOR_CHROMA_KEYED: { + has_alpha = true; + px_size = sizeof(lv_color_t); + lv_disp_t * d = _lv_refr_get_disp_refreshing(); + ck = d->driver->color_chroma_key; + break; + } +#if LV_COLOR_DEPTH == 16 + case LV_IMG_CF_RGB565A8: + has_alpha = true; + px_size = sizeof(lv_color_t); + break; +#endif + default: + return; + } + + lv_coord_t x; + for(x = 0; x < x_end; x++) { + xs_ups = xs_ups_start + ((xs_step * x) >> 8); + ys_ups = ys_ups_start + ((ys_step * x) >> 8); + + int32_t xs_int = xs_ups >> 8; + int32_t ys_int = ys_ups >> 8; + + /*Fully out of the image*/ + if(xs_int < 0 || xs_int >= src_w || ys_int < 0 || ys_int >= src_h) { + abuf[x] = 0x00; + continue; + } + + /*Get the direction the hor and ver neighbor + *`fract` will be in range of 0x00..0xFF and `next` (+/-1) indicates the direction*/ + int32_t xs_fract = xs_ups & 0xFF; + int32_t ys_fract = ys_ups & 0xFF; + + int32_t x_next; + int32_t y_next; + if(xs_fract < 0x80) { + x_next = -1; + xs_fract = (0x7F - xs_fract) * 2; + } + else { + x_next = 1; + xs_fract = (xs_fract - 0x80) * 2; + } + if(ys_fract < 0x80) { + y_next = -1; + ys_fract = (0x7F - ys_fract) * 2; + } + else { + y_next = 1; + ys_fract = (ys_fract - 0x80) * 2; + } + + const uint8_t * src_tmp = src; + src_tmp += (ys_int * src_stride * px_size) + xs_int * px_size; + + + if(xs_int + x_next >= 0 && + xs_int + x_next <= src_w - 1 && + ys_int + y_next >= 0 && + ys_int + y_next <= src_h - 1) { + + const uint8_t * px_base = src_tmp; + const uint8_t * px_hor = src_tmp + x_next * px_size; + const uint8_t * px_ver = src_tmp + y_next * src_stride * px_size; + lv_color_t c_base; + lv_color_t c_ver; + lv_color_t c_hor; + + if(has_alpha) { + lv_opa_t a_base; + lv_opa_t a_ver; + lv_opa_t a_hor; + if(cf == LV_IMG_CF_TRUE_COLOR_ALPHA) { + a_base = px_base[LV_IMG_PX_SIZE_ALPHA_BYTE - 1]; + a_ver = px_ver[LV_IMG_PX_SIZE_ALPHA_BYTE - 1]; + a_hor = px_hor[LV_IMG_PX_SIZE_ALPHA_BYTE - 1]; + } +#if LV_COLOR_DEPTH == 16 + else if(cf == LV_IMG_CF_RGB565A8) { + const lv_opa_t * a_tmp = src + src_stride * src_h * sizeof(lv_color_t); + a_base = *(a_tmp + (ys_int * src_stride) + xs_int); + a_hor = *(a_tmp + (ys_int * src_stride) + xs_int + x_next); + a_ver = *(a_tmp + ((ys_int + y_next) * src_stride) + xs_int); + } +#endif + else if(cf == LV_IMG_CF_TRUE_COLOR_CHROMA_KEYED) { + if(((lv_color_t *)px_base)->full == ck.full || + ((lv_color_t *)px_ver)->full == ck.full || + ((lv_color_t *)px_hor)->full == ck.full) { + abuf[x] = 0x00; + continue; + } + else { + a_base = 0xff; + a_ver = 0xff; + a_hor = 0xff; + } + } + else { + a_base = 0xff; + a_ver = 0xff; + a_hor = 0xff; + } + + if(a_ver != a_base) a_ver = ((a_ver * ys_fract) + (a_base * (0x100 - ys_fract))) >> 8; + if(a_hor != a_base) a_hor = ((a_hor * xs_fract) + (a_base * (0x100 - xs_fract))) >> 8; + abuf[x] = (a_ver + a_hor) >> 1; + + if(abuf[x] == 0x00) continue; + +#if LV_COLOR_DEPTH == 8 + c_base.full = px_base[0]; + c_ver.full = px_ver[0]; + c_hor.full = px_hor[0]; +#elif LV_COLOR_DEPTH == 16 + c_base.full = px_base[0] + (px_base[1] << 8); + c_ver.full = px_ver[0] + (px_ver[1] << 8); + c_hor.full = px_hor[0] + (px_hor[1] << 8); +#elif LV_COLOR_DEPTH == 32 + c_base.full = *((uint32_t *)px_base); + c_ver.full = *((uint32_t *)px_ver); + c_hor.full = *((uint32_t *)px_hor); +#endif + } + /*No alpha channel -> RGB*/ + else { + c_base = *((const lv_color_t *) px_base); + c_hor = *((const lv_color_t *) px_hor); + c_ver = *((const lv_color_t *) px_ver); + abuf[x] = 0xff; + } + + if(c_base.full == c_ver.full && c_base.full == c_hor.full) { + cbuf[x] = c_base; + } + else { + c_ver = lv_color_mix(c_ver, c_base, ys_fract); + c_hor = lv_color_mix(c_hor, c_base, xs_fract); + cbuf[x] = lv_color_mix(c_hor, c_ver, LV_OPA_50); + } + } + /*Partially out of the image*/ + else { +#if LV_COLOR_DEPTH == 8 + cbuf[x].full = src_tmp[0]; +#elif LV_COLOR_DEPTH == 16 + cbuf[x].full = src_tmp[0] + (src_tmp[1] << 8); +#elif LV_COLOR_DEPTH == 32 + cbuf[x].full = *((uint32_t *)src_tmp); +#endif + lv_opa_t a; + switch(cf) { + case LV_IMG_CF_TRUE_COLOR_ALPHA: + a = src_tmp[LV_IMG_PX_SIZE_ALPHA_BYTE - 1]; + break; + case LV_IMG_CF_TRUE_COLOR_CHROMA_KEYED: + a = cbuf[x].full == ck.full ? 0x00 : 0xff; + break; +#if LV_COLOR_DEPTH == 16 + case LV_IMG_CF_RGB565A8: + a = *(src + src_stride * src_h * sizeof(lv_color_t) + (ys_int * src_stride) + xs_int); + break; +#endif + default: + a = 0xff; + } + + if((xs_int == 0 && x_next < 0) || (xs_int == src_w - 1 && x_next > 0)) { + abuf[x] = (a * (0xFF - xs_fract)) >> 8; + } + else if((ys_int == 0 && y_next < 0) || (ys_int == src_h - 1 && y_next > 0)) { + abuf[x] = (a * (0xFF - ys_fract)) >> 8; + } + else { + abuf[x] = 0x00; + } + } + } +} + +static void transform_point_upscaled(point_transform_dsc_t * t, int32_t xin, int32_t yin, int32_t * xout, + int32_t * yout) +{ + if(t->angle == 0 && t->zoom == LV_IMG_ZOOM_NONE) { + *xout = xin * 256; + *yout = yin * 256; + return; + } + + xin -= t->pivot.x; + yin -= t->pivot.y; + + if(t->angle == 0) { + *xout = ((int32_t)(xin * t->zoom)) + (t->pivot_x_256); + *yout = ((int32_t)(yin * t->zoom)) + (t->pivot_y_256); + } + else if(t->zoom == LV_IMG_ZOOM_NONE) { + *xout = ((t->cosma * xin - t->sinma * yin) >> 2) + (t->pivot_x_256); + *yout = ((t->sinma * xin + t->cosma * yin) >> 2) + (t->pivot_y_256); + } + else { + *xout = (((t->cosma * xin - t->sinma * yin) * t->zoom) >> 10) + (t->pivot_x_256); + *yout = (((t->sinma * xin + t->cosma * yin) * t->zoom) >> 10) + (t->pivot_y_256); + } +} + +#endif + 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*/ |
