diff --git a/README.md b/README.md index a4ac543c8cfe..256b96440b40 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,6 @@ LVGL provides everything you need to create an embedded GUI with easy-to-use gra **English** | [中文](./README_zh.md) | [Português do Brasil](./README_pt_BR.md) - --- #### Table of content diff --git a/lv_conf_template.h b/lv_conf_template.h index 1ed255dae2f4..0c34c6fd7542 100644 --- a/lv_conf_template.h +++ b/lv_conf_template.h @@ -176,6 +176,9 @@ * GPU *-----------*/ +/*Use TSi's (aka Think Silicon) acceleration library NemaGFX */ +#define LV_USE_NEMA_GFX 0 + /*Use Arm's 2D acceleration library Arm-2D */ #define LV_USE_GPU_ARM2D 0 diff --git a/src/core/lv_obj.c b/src/core/lv_obj.c index 23eef36c9bc7..f8921f05585d 100644 --- a/src/core/lv_obj.c +++ b/src/core/lv_obj.c @@ -42,6 +42,10 @@ #include "../draw/nxp/pxp/lv_gpu_nxp_pxp.h" #endif +#if LV_USE_NEMA_GFX + #include "../draw/nema_gfx/lv_draw_nema_gfx.h" +#endif + /********************* * DEFINES *********************/ @@ -137,6 +141,11 @@ void lv_init(void) PXP_COND_STOP(!lv_gpu_nxp_pxp_init(), "PXP init failed."); #endif +#if LV_USE_NEMA_GFX + /*Initialize NEMA GPU*/ + lv_draw_nema_gfx_init(); +#endif + _lv_obj_style_init(); _lv_ll_init(&LV_GC_ROOT(_lv_disp_ll), sizeof(lv_disp_t)); _lv_ll_init(&LV_GC_ROOT(_lv_indev_ll), sizeof(lv_indev_t)); diff --git a/src/draw/lv_draw.mk b/src/draw/lv_draw.mk index f48f48fe0b3d..c7d54631c1f3 100644 --- a/src/draw/lv_draw.mk +++ b/src/draw/lv_draw.mk @@ -23,3 +23,4 @@ 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 +include $(LVGL_DIR)/$(LVGL_DIR_NAME)/src/draw/nema_gfx/lv_draw_nema_gfx.mk \ No newline at end of file diff --git a/src/draw/nema_gfx/lv_draw_nema_gfx.c b/src/draw/nema_gfx/lv_draw_nema_gfx.c new file mode 100644 index 000000000000..bf7102769e87 --- /dev/null +++ b/src/draw/nema_gfx/lv_draw_nema_gfx.c @@ -0,0 +1,554 @@ +/** + * @file lv_draw_nema_gfx.c + * + */ + +/** + * MIT License + * + * ----------------------------------------------------------------------------- + * Copyright (c) 2008-24 Think Silicon Single Member PC + * ----------------------------------------------------------------------------- + * + * 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 "../../core/lv_refr.h" + +#if LV_USE_NEMA_GFX + +#include "lv_draw_nema_gfx.h" + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ + +static lv_res_t draw_nema_gfx_bg(lv_draw_ctx_t * draw_ctx, const lv_draw_rect_dsc_t * dsc, const lv_area_t * coords); +static inline bool Isargb8565(lv_img_cf_t cf); +static uint32_t lv_cf_to_nema(lv_img_cf_t cf); +static uint32_t skip_pallete(lv_img_cf_t cf); + +/********************** + * STATIC VARIABLES + **********************/ + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +void lv_draw_nema_gfx_init(void) +{ + nema_init(); + return; +} + +static inline bool Isargb8565(lv_img_cf_t cf) +{ +#if LV_COLOR_DEPTH != 32 + if(cf == LV_IMG_CF_TRUE_COLOR_ALPHA) + return true; +#endif + return false; +} + +static uint32_t lv_cf_to_nema(lv_img_cf_t cf) +{ + switch(cf) { + case LV_IMG_CF_ALPHA_1BIT: + return NEMA_A1; + case LV_IMG_CF_ALPHA_2BIT: + return NEMA_A2; + case LV_IMG_CF_ALPHA_4BIT: + return NEMA_A4; + case LV_IMG_CF_ALPHA_8BIT: + return NEMA_A8; + case LV_IMG_CF_INDEXED_1BIT: + return NEMA_L1; + case LV_IMG_CF_INDEXED_2BIT: + return NEMA_L2; + case LV_IMG_CF_INDEXED_4BIT: + return NEMA_L4; + case LV_IMG_CF_INDEXED_8BIT: + return NEMA_L8; + default: + return LV_NEMA_GFX_COLOR_FORMAT; + } +} + +static uint32_t skip_pallete(lv_img_cf_t cf) +{ + switch(cf) { + case LV_IMG_CF_INDEXED_1BIT: + return 2; + case LV_IMG_CF_INDEXED_2BIT: + return 4; + case LV_IMG_CF_INDEXED_4BIT: + return 16; + default: + return 256; + } +} + +void lv_draw_nema_gfx_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); + bool recolor = (dsc->recolor_opa != LV_OPA_TRANSP); + bool transform = dsc->angle != 0 || dsc->zoom != LV_IMG_ZOOM_NONE ? true : false; + + lv_draw_nema_gfx_ctx_t * nema_gfx_draw_ctx = (lv_draw_nema_gfx_ctx_t *)draw_ctx; + + 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); + + lv_coord_t tex_w = lv_area_get_width(coords); + lv_coord_t tex_h = lv_area_get_height(coords); + + lv_area_t clip_area; + lv_area_copy(&clip_area, draw_ctx->clip_area); + lv_area_move(&clip_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 || Isargb8565(cf)) { + 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); + uint32_t blending_mode = NEMA_BL_SIMPLE; + + uint32_t color_format = lv_cf_to_nema(cf); + + nema_buffer_t Shuffle_pallete_bo; + if(color_format == NEMA_L8) { + Shuffle_pallete_bo = nema_buffer_create(256 * 4); + uint32_t * Shuffle_pallete = Shuffle_pallete_bo.base_virt; + for(int i = 0; i < 256; i++) { + int idx = ((i >> 4) | ((i & 0xfU) << 4)); + lv_color32_t col32 = {.full = lv_color_to32(*(lv_color_t *)((uint32_t *)src_buf + idx))}; + Shuffle_pallete[i] = col32.full; + } + } + + if(!mask_any) { + nema_bind_dst_tex((uintptr_t)NEMA_VIRT2PHYS(draw_ctx->buf), lv_area_get_width(draw_ctx->buf_area), + lv_area_get_height(draw_ctx->buf_area), LV_NEMA_GFX_COLOR_FORMAT, + lv_area_get_width(draw_ctx->buf_area)*LV_NEMA_GFX_FORMAT_MULTIPLIER); + nema_set_clip(clip_area.x1, clip_area.y1, lv_area_get_width(&clip_area), lv_area_get_height(&clip_area)); + if(color_format == NEMA_L1 || color_format == NEMA_L2 || color_format == NEMA_L4) { + blending_mode |= NEMA_BLOP_LUT; + nema_bind_lut_tex((uintptr_t)((uint32_t *)src_buf + (skip_pallete(cf))), tex_w, tex_h, color_format, -1, 0, + (uintptr_t)(src_buf), NEMA_BGRA8888); + } + else if(color_format == NEMA_L8) { + blending_mode |= NEMA_BLOP_LUT; + nema_bind_lut_tex((uintptr_t)((uint32_t *)src_buf + (skip_pallete(cf))), tex_w, tex_h, color_format, -1, 0, + Shuffle_pallete_bo.base_phys, NEMA_BGRA8888); + } + else + nema_bind_src_tex((uintptr_t)(src_buf), tex_w, tex_h, color_format, -1, + (dsc->antialias == true) ? NEMA_FILTER_BL : NEMA_FILTER_PS); + + if(recolor) { + lv_color32_t col32 = {.full = lv_color_to32(dsc->recolor)}; + uint32_t color = nema_rgba(col32.ch.red, col32.ch.green, col32.ch.blue, dsc->recolor_opa); + nema_set_recolor_color(color); + blending_mode |= NEMA_BLOP_RECOLOR; + } + + if(lv_img_cf_is_chroma_keyed(cf)) { + blending_mode |= NEMA_BLOP_SRC_CKEY; + lv_color_t ckey = LV_COLOR_CHROMA_KEY; + lv_color32_t col32 = {.full = lv_color_to32(ckey)}; + uint32_t src_color_key = nema_rgba(col32.ch.red, col32.ch.green, col32.ch.blue, 0U); + nema_set_src_color_key(src_color_key); + } + + if(dsc->opa < 255) { + uint32_t rgba = ((uint32_t)dsc->opa << 24U) | ((uint32_t)dsc->opa << 16U) | ((uint32_t)dsc->opa << 8U) | (( + uint32_t)dsc->opa); + nema_set_const_color(rgba); + blending_mode |= NEMA_BLOP_MODULATE_A; + } + + nema_set_blend_blit(blending_mode); + + if(!transform) { + nema_blit_rect((coords->x1 - draw_ctx->buf_area->x1), + (coords->y1 - draw_ctx->buf_area->y1), tex_w, tex_h); + } + else { + + /*Calculate the transformed points*/ + float x0 = (coords->x1 - draw_ctx->buf_area->x1); + float y0 = (coords->y1 - draw_ctx->buf_area->y1); + float x1 = x0 + tex_w ; + float y1 = y0; + float x2 = x0 + tex_w ; + float y2 = y0 + tex_h; + float x3 = x0 ; + float y3 = y0 + tex_h; + + nema_matrix3x3_t m; + nema_mat3x3_load_identity(m); + nema_mat3x3_translate(m, -x0, -y0); + nema_mat3x3_translate(m, -(float)dsc->pivot.x, -(float)dsc->pivot.y); + nema_mat3x3_rotate(m, (dsc->angle / 10.0f)); /* angle is 1/10 degree */ + float scale = 1.f * dsc->zoom / LV_IMG_ZOOM_NONE; + nema_mat3x3_scale(m, (float)scale, (float)scale); + nema_mat3x3_translate(m, (float)dsc->pivot.x, (float)dsc->pivot.y); + nema_mat3x3_translate(m, x0, y0); + + /*Apply Transformation Matrix to Vertices*/ + nema_mat3x3_mul_vec(m, &x0, &y0); + nema_mat3x3_mul_vec(m, &x1, &y1); + nema_mat3x3_mul_vec(m, &x2, &y2); + nema_mat3x3_mul_vec(m, &x3, &y3); + + nema_blit_quad_fit(x0, y0, + x1, y1, + x2, y2, + x3, y3); + } + nema_cl_submit(&(nema_gfx_draw_ctx->cl)); + if(color_format == NEMA_L8) { + nema_cl_wait(&(nema_gfx_draw_ctx->cl)); + nema_buffer_destroy(&Shuffle_pallete_bo); + } + return; + } + /*Wait for GPU to finish previous jobs in case of failure and call SW rendering*/ + nema_cl_wait(&(nema_gfx_draw_ctx->cl)); + lv_draw_sw_img_decoded(draw_ctx, dsc, coords, map_p, cf); + return; +} + + +void lv_draw_nema_gfx_ctx_init(lv_disp_drv_t * drv, lv_draw_ctx_t * draw_ctx) +{ + + lv_draw_sw_init_ctx(drv, draw_ctx); + + lv_draw_nema_gfx_ctx_t * nema_gfx_draw_ctx = (lv_draw_nema_gfx_ctx_t *)draw_ctx; + + nema_gfx_draw_ctx->base_sw_ctx.blend = lv_draw_nema_gfx_blend; + nema_gfx_draw_ctx->base_sw_ctx.base_draw.draw_rect = lv_draw_nema_gfx_rect; + nema_gfx_draw_ctx->base_sw_ctx.base_draw.draw_bg = lv_draw_nema_gfx_bg; + nema_gfx_draw_ctx->base_sw_ctx.base_draw.draw_img_decoded = lv_draw_nema_gfx_img_decoded; + nema_gfx_draw_ctx->base_sw_ctx.base_draw.draw_img = lv_draw_nema_gfx_img; + nema_gfx_draw_ctx->base_sw_ctx.base_draw.draw_letter = lv_draw_nema_gfx_letter; + + //Overide this functions to wait for GPU + nema_gfx_draw_ctx->base_sw_ctx.base_draw.draw_arc = lv_draw_nema_gfx_arc; + nema_gfx_draw_ctx->base_sw_ctx.base_draw.draw_line = lv_draw_nema_gfx_line; + nema_gfx_draw_ctx->base_sw_ctx.base_draw.draw_polygon = lv_draw_nema_gfx_polygon; + + /*Create GPU Command List*/ + nema_gfx_draw_ctx->cl = nema_cl_create(); + /*Bind Command List*/ + nema_cl_bind_circular(&(nema_gfx_draw_ctx->cl)); + +} + +void lv_draw_nema_gfx_ctx_deinit(lv_disp_drv_t * drv, lv_draw_ctx_t * draw_ctx) +{ + LV_UNUSED(drv); + lv_draw_nema_gfx_ctx_t * nema_gfx_draw_ctx = (lv_draw_nema_gfx_ctx_t *) draw_ctx; + nema_cl_destroy(&(nema_gfx_draw_ctx->cl)); +} + + +void lv_draw_nema_gfx_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_draw_nema_gfx_ctx_t * nema_gfx_draw_ctx = (lv_draw_nema_gfx_ctx_t *)draw_ctx; + + lv_area_t blend_area; + + bool render_on_gpu = false; + + /*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); + + if(mask == NULL && dsc->blend_mode == LV_BLEND_MODE_NORMAL) { + + const lv_color_t * src_buf = dsc->src_buf; + + nema_bind_dst_tex((uintptr_t)NEMA_VIRT2PHYS(draw_ctx->buf), lv_area_get_width(draw_ctx->buf_area), + lv_area_get_height(draw_ctx->buf_area), LV_NEMA_GFX_COLOR_FORMAT, + lv_area_get_width(draw_ctx->buf_area)*LV_NEMA_GFX_FORMAT_MULTIPLIER); + + //Set Clipping Area + lv_area_t clip_area; + lv_area_copy(&clip_area, draw_ctx->clip_area); + lv_area_move(&clip_area, -draw_ctx->buf_area->x1, -draw_ctx->buf_area->y1); + + nema_set_clip(clip_area.x1, clip_area.y1, lv_area_get_width(&clip_area), lv_area_get_height(&clip_area)); + + lv_coord_t coords_bg_w = lv_area_get_width(&blend_area); + lv_coord_t coords_bg_h = lv_area_get_height(&blend_area); + + if(src_buf == NULL) { + //Simple Fill + uint8_t opacity; + lv_color32_t col32 = {.full = lv_color_to32(dsc->color)}; + if(dsc->opa < LV_OPA_MAX && dsc->opa > LV_OPA_MIN) { + opacity = (uint8_t)(((uint16_t)col32.ch.alpha * dsc->opa) >> 8); + } + else if(dsc->opa >= LV_OPA_MAX) { + opacity = col32.ch.alpha; + } + + uint32_t color = nema_rgba(col32.ch.red, col32.ch.green, col32.ch.blue, opacity); + + if(opacity < 255U) + nema_set_blend_fill(NEMA_BL_SRC_OVER | NEMA_BLOP_SRC_PREMULT); + else + nema_set_blend_fill(NEMA_BL_SRC_OVER); + + nema_fill_rect(blend_area.x1, blend_area.y1, coords_bg_w, coords_bg_h, color); + render_on_gpu = true; + } + else { //Full Opaque mask + nema_bind_src_tex((uintptr_t)(src_buf), coords_bg_w, coords_bg_h, LV_NEMA_GFX_COLOR_FORMAT, + coords_bg_w * LV_NEMA_GFX_FORMAT_MULTIPLIER, NEMA_FILTER_PS); + if(dsc->opa < 255) { + uint32_t rgba = ((uint32_t)dsc->opa << 24U) | ((uint32_t)dsc->opa << 16U) | ((uint32_t)dsc->opa << 8U) | (( + uint32_t)dsc->opa); + nema_set_const_color(rgba); + nema_set_blend_blit(NEMA_BL_SIMPLE | NEMA_BLOP_MODULATE_A); + } + else + nema_set_blend_blit(NEMA_BL_SIMPLE); + + nema_blit_rect(blend_area.x1, blend_area.y1, coords_bg_w, coords_bg_h); + render_on_gpu = true; + } + } + + if(render_on_gpu) { + nema_cl_submit(&(nema_gfx_draw_ctx->cl)); + } + else { + nema_cl_wait(&(nema_gfx_draw_ctx->cl)); + lv_draw_sw_blend_basic(draw_ctx, dsc); + } + + return; +} + +void lv_draw_nema_gfx_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 nema_dsc; + + lv_memcpy(&nema_dsc, dsc, sizeof(nema_dsc)); +#if LV_DRAW_COMPLEX + /* Draw only the shadow */ + nema_dsc.bg_opa = 0; + nema_dsc.bg_img_opa = 0; + nema_dsc.border_opa = 0; + nema_dsc.outline_opa = 0; + lv_draw_nema_gfx_ctx_t * nema_gfx_draw_ctx = (lv_draw_nema_gfx_ctx_t *)draw_ctx; + nema_cl_wait(&(nema_gfx_draw_ctx->cl)); + + lv_draw_sw_rect(draw_ctx, &nema_dsc, coords); + + /* Draw the background */ + nema_dsc.shadow_opa = 0; + nema_dsc.bg_opa = dsc->bg_opa; + if(draw_nema_gfx_bg(draw_ctx, &nema_dsc, coords) == LV_RES_OK) { + nema_dsc.bg_opa = 0; + } +#endif /*LV_DRAW_COMPLEX*/ + + /* Draw the remaining parts */ + nema_dsc.bg_img_opa = dsc->bg_img_opa; + nema_dsc.border_opa = dsc->border_opa; + nema_dsc.outline_opa = dsc->outline_opa; + + lv_draw_sw_rect(draw_ctx, &nema_dsc, coords); + +} + +void lv_draw_nema_gfx_bg(lv_draw_ctx_t * draw_ctx, const lv_draw_rect_dsc_t * dsc, const lv_area_t * coords) +{ + + bool rendered_by_gpu = false; + lv_draw_rect_dsc_t nema_dsc; + lv_memcpy(&nema_dsc, dsc, sizeof(nema_dsc)); + + // try to render with GPU + rendered_by_gpu = (draw_nema_gfx_bg(draw_ctx, &nema_dsc, coords) == LV_RES_OK); + + if(rendered_by_gpu) { + // rendered with gpu + // continuew with the rest + nema_dsc.bg_opa = 0; + lv_draw_sw_bg(draw_ctx, &nema_dsc, coords); + } + else { + // couldn't draw with gpu + lv_draw_sw_bg(draw_ctx, dsc, coords); + } +} + +lv_res_t draw_nema_gfx_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 + + 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; + + /*Make the blend area relative to the buffer*/ + lv_area_move(&bg_coords, -draw_ctx->buf_area->x1, -draw_ctx->buf_area->y1); + + lv_draw_nema_gfx_ctx_t * nema_gfx_draw_ctx = (lv_draw_nema_gfx_ctx_t *)draw_ctx; + + nema_bind_dst_tex((uintptr_t)NEMA_VIRT2PHYS(draw_ctx->buf), lv_area_get_width(draw_ctx->buf_area), + lv_area_get_height(draw_ctx->buf_area), LV_NEMA_GFX_COLOR_FORMAT, + lv_area_get_width(draw_ctx->buf_area)*LV_NEMA_GFX_FORMAT_MULTIPLIER); + //Set Clipping Area + + lv_area_t clip_area; + lv_area_copy(&clip_area, draw_ctx->clip_area); + lv_area_move(&clip_area, -draw_ctx->buf_area->x1, -draw_ctx->buf_area->y1); + + nema_set_clip(clip_area.x1, clip_area.y1, lv_area_get_width(&clip_area), lv_area_get_height(&clip_area)); + + 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 radius = LV_MIN(dsc->radius, short_side >> 1); + + bool mask_any = lv_draw_mask_is_any(&bg_coords); + + if(!mask_any && (dsc->bg_grad.dir == LV_GRAD_DIR_NONE)) { + + uint8_t opacity; + lv_color32_t bg_col32 = {.full = lv_color_to32(dsc->bg_color)}; + if(dsc->bg_opa < LV_OPA_MAX && dsc->bg_opa > LV_OPA_MIN) { + opacity = (uint8_t)(((uint16_t)bg_col32.ch.alpha * dsc->bg_opa) >> 8); + } + else if(dsc->bg_opa >= LV_OPA_MAX) { + opacity = bg_col32.ch.alpha; + } + + if(opacity < 255U) { + nema_set_blend_fill(NEMA_BL_SIMPLE); + } + else { + nema_set_blend_fill(NEMA_BL_SRC); + } + + uint32_t bg_color = nema_rgba(bg_col32.ch.red, bg_col32.ch.green, bg_col32.ch.blue, opacity); + + if(dsc->radius != 0) + nema_fill_rounded_rect_aa(bg_coords.x1, bg_coords.y1, coords_bg_w, coords_bg_h, radius, bg_color); + else + nema_fill_rect(bg_coords.x1, bg_coords.y1, coords_bg_w, coords_bg_h, bg_color); + + nema_cl_submit(&(nema_gfx_draw_ctx->cl)); + nema_cl_wait(&(nema_gfx_draw_ctx->cl)); + + return LV_RES_OK; + } + return LV_RES_INV; +} + +LV_ATTRIBUTE_FAST_MEM void lv_draw_nema_gfx_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) +{ + lv_draw_nema_gfx_ctx_t * nema_gfx_draw_ctx = (lv_draw_nema_gfx_ctx_t *)draw_ctx; + //Ensure that GPU has no unfinished workload + nema_cl_wait(&(nema_gfx_draw_ctx->cl)); + lv_draw_sw_line(draw_ctx, dsc, point1, point2); +} + +void lv_draw_nema_gfx_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) +{ + lv_draw_nema_gfx_ctx_t * nema_gfx_draw_ctx = (lv_draw_nema_gfx_ctx_t *)draw_ctx; + //Ensure that GPU has no unfinished workload + nema_cl_wait(&(nema_gfx_draw_ctx->cl)); + lv_draw_sw_polygon(draw_ctx, draw_dsc, points, point_cnt); +} + +void lv_draw_nema_gfx_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_nema_gfx_ctx_t * nema_gfx_draw_ctx = (lv_draw_nema_gfx_ctx_t *)draw_ctx; + //Ensure that GPU has no unfinished workload + nema_cl_wait(&(nema_gfx_draw_ctx->cl)); + lv_draw_sw_arc(draw_ctx, dsc, center, radius, start_angle, end_angle); +} + +#endif diff --git a/src/draw/nema_gfx/lv_draw_nema_gfx.h b/src/draw/nema_gfx/lv_draw_nema_gfx.h new file mode 100644 index 000000000000..b5f3a38ab125 --- /dev/null +++ b/src/draw/nema_gfx/lv_draw_nema_gfx.h @@ -0,0 +1,134 @@ +/** + * @file lv_draw_nema_gfx.h + * + */ + +/** + * MIT License + * + * ----------------------------------------------------------------------------- + * Copyright (c) 2008-24 Think Silicon Single Member PC + * ----------------------------------------------------------------------------- + * + * 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_NEMA_GFX_H +#define LV_DRAW_NEMA_GFX_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_NEMA_GFX + +#include "nema_core.h" +#include "nema_utils.h" +#include "nema_error.h" +#include "nema_provisional.h" + + +/********************* + * DEFINES + *********************/ + +#ifndef NEMA_VIRT2PHYS +#define NEMA_VIRT2PHYS +#endif + +/*Color depth: 1 (1 byte per pixel), 8 (RGB332), 16 (RGB565), 32 (ARGB8888)*/ +#if LV_COLOR_DEPTH == 8 +#define LV_NEMA_GFX_COLOR_FORMAT NEMA_RGB332 +#define LV_NEMA_GFX_FORMAT_MULTIPLIER 1 +#elif LV_COLOR_DEPTH == 16 +#define LV_NEMA_GFX_COLOR_FORMAT NEMA_RGB565 +#define LV_NEMA_GFX_FORMAT_MULTIPLIER 2 +#elif LV_COLOR_DEPTH == 32 +#define LV_NEMA_GFX_COLOR_FORMAT NEMA_BGRA8888 +#define LV_NEMA_GFX_FORMAT_MULTIPLIER 4 +#else +/*Can't use GPU with other formats*/ +#endif + +/********************** + * TYPEDEFS + **********************/ + +typedef struct { + lv_draw_sw_ctx_t base_sw_ctx; + /*Add other needed fields here*/ + nema_cmdlist_t cl; +} lv_draw_nema_gfx_ctx_t; + +struct _lv_disp_drv_t; + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +void lv_draw_nema_gfx_init(void); + +void lv_draw_nema_gfx_ctx_init(struct _lv_disp_drv_t * drv, lv_draw_ctx_t * draw_ctx); + +void lv_draw_nema_gfx_ctx_deinit(struct _lv_disp_drv_t * drv, lv_draw_ctx_t * draw_ctx); + +void lv_draw_nema_gfx_blend(lv_draw_ctx_t * draw_ctx, const lv_draw_sw_blend_dsc_t * dsc); + +void lv_draw_nema_gfx_bg(lv_draw_ctx_t * draw_ctx, const lv_draw_rect_dsc_t * dsc, const lv_area_t * coords); + +void lv_draw_nema_gfx_rect(lv_draw_ctx_t * draw_ctx, const lv_draw_rect_dsc_t * dsc, const lv_area_t * coords); + +void lv_draw_nema_gfx_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); + +void lv_draw_nema_gfx_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_nema_gfx_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_nema_gfx_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_nema_gfx_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); + +lv_res_t lv_draw_nema_gfx_img(struct _lv_draw_ctx_t * draw_ctx, const lv_draw_img_dsc_t * dsc, + const lv_area_t * coords, + const void * src); + +/********************** + * MACROS + **********************/ + +#endif /*LV_USE_NEMA_GFX*/ + +#ifdef __cplusplus +} /*extern "C"*/ +#endif + +#endif /*LV_DRAW_NEMA_GFX_H*/ diff --git a/src/draw/nema_gfx/lv_draw_nema_gfx.mk b/src/draw/nema_gfx/lv_draw_nema_gfx.mk new file mode 100644 index 000000000000..d4d59a9a2b59 --- /dev/null +++ b/src/draw/nema_gfx/lv_draw_nema_gfx.mk @@ -0,0 +1,8 @@ +CSRCS += lv_draw_nema_gfx.c +CSRCS += lv_draw_nema_gfx_letter.c +CSRCS += lv_draw_nema_gfx_img.c + +DEPPATH += --dep-path $(LVGL_DIR)/$(LVGL_DIR_NAME)/src/draw/nema_gfx +VPATH += :$(LVGL_DIR)/$(LVGL_DIR_NAME)/src/draw/nema_gfx + +CFLAGS += "-I$(LVGL_DIR)/$(LVGL_DIR_NAME)/src/draw/nema_gfx" diff --git a/src/draw/nema_gfx/lv_draw_nema_gfx_img.c b/src/draw/nema_gfx/lv_draw_nema_gfx_img.c new file mode 100644 index 000000000000..c6e4debc3963 --- /dev/null +++ b/src/draw/nema_gfx/lv_draw_nema_gfx_img.c @@ -0,0 +1,243 @@ +/** + * @file lv_draw_nema_gfx_img.c + * + */ + +/** + * MIT License + * + * ----------------------------------------------------------------------------- + * Copyright (c) 2008-24 Think Silicon Single Member PC + * ----------------------------------------------------------------------------- + * + * 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 "../../core/lv_refr.h" + +#if LV_USE_NEMA_GFX + +#include "lv_draw_nema_gfx.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" +#include "lv_img_buf.h" +#include "../misc/lv_style.h" + +/********************** + * STATIC PROTOTYPES + **********************/ + +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); + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +bool lv_img_cf_alpha_only_format(lv_img_cf_t cf) +{ + bool is_alpha_format = false; + + switch(cf) { + case LV_IMG_CF_ALPHA_1BIT: + case LV_IMG_CF_ALPHA_2BIT: + case LV_IMG_CF_ALPHA_4BIT: + case LV_IMG_CF_ALPHA_8BIT: + is_alpha_format = true; + break; + default: + is_alpha_format = false; + break; + } + + return is_alpha_format; +} + + +bool lv_img_cf_lut_format(lv_img_cf_t cf) +{ + bool is_lut_format = false; + + switch(cf) { + case LV_IMG_CF_INDEXED_1BIT: + case LV_IMG_CF_INDEXED_2BIT: + case LV_IMG_CF_INDEXED_4BIT: + case LV_IMG_CF_INDEXED_8BIT: + is_lut_format = true; + break; + default: + is_lut_format = false; + break; + } + + return is_lut_format; +} + +lv_res_t lv_draw_nema_gfx_img(struct _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_img_cf_alpha_only_format(cdsc->dec_dsc.header.cf)) || + lv_img_cf_lut_format(cdsc->dec_dsc.header.cf)) { + 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; + } + + if(lv_img_cf_alpha_only_format(cdsc->dec_dsc.header.cf)) { + lv_color32_t tex_col32 = {.full = lv_color_to32(cdsc->dec_dsc.color)}; + uint32_t tex_color = nema_rgba(tex_col32.ch.red, tex_col32.ch.green, tex_col32.ch.blue, 0); + nema_set_tex_color(tex_color); + cf = cdsc->dec_dsc.header.cf; + } + + if(lv_img_cf_lut_format(cdsc->dec_dsc.header.cf)) { + cf = cdsc->dec_dsc.header.cf; + } + + const lv_area_t * clip_area_ori = draw_ctx->clip_area; + draw_ctx->clip_area = &clip_com; + uint8_t * img_data = (uint8_t *)(cdsc->dec_dsc.img_data) ? (uint8_t *)(cdsc->dec_dsc.img_data) : (uint8_t *)((( + lv_img_dsc_t *)src)->data); + lv_draw_img_decoded(draw_ctx, draw_dsc, coords, 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) { + 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 +} + +#endif \ No newline at end of file diff --git a/src/draw/nema_gfx/lv_draw_nema_gfx_letter.c b/src/draw/nema_gfx/lv_draw_nema_gfx_letter.c new file mode 100644 index 000000000000..78e52a6a62d8 --- /dev/null +++ b/src/draw/nema_gfx/lv_draw_nema_gfx_letter.c @@ -0,0 +1,330 @@ +/** + * @file lv_draw_nema_gfx_letter.c + * + */ + +/** + * MIT License + * + * ----------------------------------------------------------------------------- + * Copyright (c) 2008-24 Think Silicon Single Member PC + * ----------------------------------------------------------------------------- + * + * 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 "../../hal/lv_hal_disp.h" +#include "../../misc/lv_math.h" +#include "../../misc/lv_assert.h" +#include "../../misc/lv_area.h" +#include "../../misc/lv_style.h" +#include "../../font/lv_font.h" +#include "../../core/lv_refr.h" +#include "lv_draw_nema_gfx.h" + +#if LV_USE_NEMA_GFX +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * FORWARD DECLARATION + **********************/ + +void nema_raster_rect(int x, int y, int w, int h); + +void nema_set_matrix(nema_matrix3x3_t m); + +/********************** + * STATIC PROTOTYPES + **********************/ + +LV_ATTRIBUTE_FAST_MEM static lv_res_t draw_nema_gfx_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); + +LV_ATTRIBUTE_FAST_MEM static lv_res_t draw_nema_gfx_letter_blend(lv_draw_ctx_t * draw_ctx, + const lv_draw_sw_blend_dsc_t * dsc, lv_font_glyph_dsc_t * g); + +/********************** + * STATIC VARIABLES + **********************/ + +/********************** + * GLOBAL VARIABLES + **********************/ + +/********************** + * 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_nema_gfx_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; + } + + if(g.resolved_font->subpx) { +#if LV_DRAW_COMPLEX && LV_USE_FONT_SUBPX + return lv_draw_sw_letter(draw_ctx, dsc, pos_p, letter); +#else + LV_LOG_WARN("Can't draw sub-pixel rendered letter because LV_USE_FONT_SUBPX == 0 in lv_conf.h"); + return ; +#endif + } + + 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(draw_nema_gfx_letter_normal(draw_ctx, dsc, &gpos, &g, map_p) != LV_RES_OK) + return lv_draw_sw_letter(draw_ctx, dsc, pos_p, letter); +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +LV_ATTRIBUTE_FAST_MEM static lv_res_t draw_nema_gfx_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) +{ + + uint32_t bpp = g->bpp; + lv_opa_t opa = dsc->opa; + 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 + + int32_t box_w = g->box_w; + int32_t box_h = g->box_h; + + /*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; + + 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; + blend_dsc.mask_buf = (uint8_t *)map_p; + + /*Initially leave fill area as is, to check for mask_area*/ + 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 = row_end + pos->y - 1; + +#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 + /*After the mask checking change fill area to what NemaGFX wants*/ + fill_area.x1 = pos->x; + fill_area.y1 = pos->y; + + blend_dsc.blend_area = &fill_area; + blend_dsc.mask_area = &fill_area; + +#if LV_DRAW_COMPLEX + /*Apply masks if any*/ + if(mask_any) { + return LV_RES_INV; + } +#endif + lv_res_t result = draw_nema_gfx_letter_blend(draw_ctx, &blend_dsc, g); + return result; +} + +static inline uint8_t _bpp_nema_gfx_format(lv_font_glyph_dsc_t * g) +{ + + uint32_t bpp = g->bpp ; + if(bpp == 3) bpp = 4; + + switch(bpp) { + case 1: + return NEMA_A1; + case 2: + return NEMA_A2; + case 4: + return NEMA_A4; + default: + return NEMA_A8; + } +} + +LV_ATTRIBUTE_FAST_MEM static lv_res_t draw_nema_gfx_letter_blend(lv_draw_ctx_t * draw_ctx, + const lv_draw_sw_blend_dsc_t * dsc, lv_font_glyph_dsc_t * g) +{ + lv_draw_nema_gfx_ctx_t * nema_gfx_draw_ctx = (lv_draw_nema_gfx_ctx_t *)draw_ctx; + + 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 LV_RES_OK; /*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); + + nema_bind_dst_tex((uintptr_t)NEMA_VIRT2PHYS(draw_ctx->buf), lv_area_get_width(draw_ctx->buf_area), + lv_area_get_height(draw_ctx->buf_area), LV_NEMA_GFX_COLOR_FORMAT, + lv_area_get_width(draw_ctx->buf_area)*LV_NEMA_GFX_FORMAT_MULTIPLIER); + + //Set Clipping Area + lv_area_t clip_area; + lv_area_copy(&clip_area, draw_ctx->clip_area); + lv_area_move(&clip_area, -draw_ctx->buf_area->x1, -draw_ctx->buf_area->y1); + + nema_set_clip(clip_area.x1, clip_area.y1, lv_area_get_width(&clip_area), lv_area_get_height(&clip_area)); + + uint8_t opacity; + lv_color32_t col32 = {.full = lv_color_to32(dsc->color)}; + if(dsc->opa < LV_OPA_MAX && dsc->opa > LV_OPA_MIN) { + opacity = (uint8_t)(((uint16_t)col32.ch.alpha * dsc->opa) >> 8); + } + else if(dsc->opa >= LV_OPA_MAX) { + opacity = col32.ch.alpha; + } + + uint32_t color = nema_rgba(col32.ch.red, col32.ch.green, col32.ch.blue, opacity); + + nema_set_tex_color(color); + + lv_coord_t x = dsc->blend_area->x1; + lv_coord_t y = dsc->blend_area->y1; + lv_coord_t w = g->box_w; + lv_coord_t h = g->box_h; + + nema_bind_src_tex((uintptr_t)(dsc->mask_buf), w * h, 1, _bpp_nema_gfx_format(g), -1, 1); + + if(opacity < 255U) { + nema_set_blend_blit(NEMA_BL_SIMPLE | NEMA_BLOP_MODULATE_A); + nema_set_const_color(color); + } + else { + nema_set_blend_blit(NEMA_BL_SIMPLE); + } + + nema_matrix3x3_t m = { + 1, w, -x - (y * w) - (0.5 * w), + 0, 1, 0, + 0, 0, 1 + }; + + nema_set_matrix(m); + nema_raster_rect(x, y, w, h); + + nema_cl_submit(&(nema_gfx_draw_ctx->cl)); + + return LV_RES_OK; + +} +#endif diff --git a/src/hal/lv_hal_disp.c b/src/hal/lv_hal_disp.c index 6f1c9626dced..f395e1d17df0 100644 --- a/src/hal/lv_hal_disp.c +++ b/src/hal/lv_hal_disp.c @@ -26,6 +26,7 @@ #include "../draw/nxp/vglite/lv_draw_vglite.h" #include "../draw/nxp/pxp/lv_draw_pxp.h" #include "../draw/renesas/lv_gpu_d2_ra6m3.h" +#include "../draw/nema_gfx/lv_draw_nema_gfx.h" #if LV_USE_THEME_DEFAULT #include "../extra/themes/default/lv_theme_default.h" @@ -124,6 +125,10 @@ void lv_disp_drv_init(lv_disp_drv_t * driver) driver->draw_ctx_init = lv_draw_arm2d_ctx_init; driver->draw_ctx_deinit = lv_draw_arm2d_ctx_init; driver->draw_ctx_size = sizeof(lv_draw_arm2d_ctx_t); +#elif LV_USE_NEMA_GFX + driver->draw_ctx_init = lv_draw_nema_gfx_ctx_init; + driver->draw_ctx_deinit = lv_draw_nema_gfx_ctx_deinit; + driver->draw_ctx_size = sizeof(lv_draw_nema_gfx_ctx_t); #else driver->draw_ctx_init = lv_draw_sw_init_ctx; driver->draw_ctx_deinit = lv_draw_sw_init_ctx; diff --git a/src/lv_conf_internal.h b/src/lv_conf_internal.h index 0c297081c37e..f7f8fba4ad5e 100644 --- a/src/lv_conf_internal.h +++ b/src/lv_conf_internal.h @@ -416,6 +416,15 @@ * GPU *-----------*/ +/*Use TSi's (aka Think Silicon) acceleration library NemaGFX */ +#ifndef LV_USE_NEMA_GFX + #ifdef CONFIG_LV_USE_NEMA_GFX + #define LV_USE_NEMA_GFX CONFIG_LV_USE_NEMA_GFX + #else + #define LV_USE_NEMA_GFX 0 + #endif +#endif + /*Use Arm's 2D acceleration library Arm-2D */ #ifndef LV_USE_GPU_ARM2D #ifdef CONFIG_LV_USE_GPU_ARM2D