From dd27c3530432ea0b09f01e604bf577f31d8ef841 Mon Sep 17 00:00:00 2001 From: jacqueline Date: Thu, 1 Jun 2023 15:41:47 +1000 Subject: convert lvgl from submodule to a plain old directory --- lib/lvgl | 1 - lib/lvgl/src/draw/nxp/lv_draw_nxp.mk | 9 + lib/lvgl/src/draw/nxp/lv_gpu_nxp.c | 418 ++++++++++++ lib/lvgl/src/draw/nxp/lv_gpu_nxp.h | 71 +++ lib/lvgl/src/draw/nxp/pxp/lv_draw_nxp_pxp.mk | 8 + lib/lvgl/src/draw/nxp/pxp/lv_draw_pxp_blend.c | 632 +++++++++++++++++++ lib/lvgl/src/draw/nxp/pxp/lv_draw_pxp_blend.h | 143 +++++ lib/lvgl/src/draw/nxp/pxp/lv_gpu_nxp_pxp.c | 116 ++++ lib/lvgl/src/draw/nxp/pxp/lv_gpu_nxp_pxp.h | 153 +++++ lib/lvgl/src/draw/nxp/pxp/lv_gpu_nxp_pxp_osa.c | 164 +++++ lib/lvgl/src/draw/nxp/pxp/lv_gpu_nxp_pxp_osa.h | 78 +++ lib/lvgl/src/draw/nxp/vglite/lv_draw_nxp_vglite.mk | 9 + lib/lvgl/src/draw/nxp/vglite/lv_draw_vglite_arc.c | 699 +++++++++++++++++++++ lib/lvgl/src/draw/nxp/vglite/lv_draw_vglite_arc.h | 79 +++ .../src/draw/nxp/vglite/lv_draw_vglite_blend.c | 618 ++++++++++++++++++ .../src/draw/nxp/vglite/lv_draw_vglite_blend.h | 149 +++++ lib/lvgl/src/draw/nxp/vglite/lv_draw_vglite_rect.c | 244 +++++++ lib/lvgl/src/draw/nxp/vglite/lv_draw_vglite_rect.h | 77 +++ lib/lvgl/src/draw/nxp/vglite/lv_gpu_nxp_vglite.c | 153 +++++ lib/lvgl/src/draw/nxp/vglite/lv_gpu_nxp_vglite.h | 185 ++++++ 20 files changed, 4005 insertions(+), 1 deletion(-) delete mode 160000 lib/lvgl create mode 100644 lib/lvgl/src/draw/nxp/lv_draw_nxp.mk create mode 100644 lib/lvgl/src/draw/nxp/lv_gpu_nxp.c create mode 100644 lib/lvgl/src/draw/nxp/lv_gpu_nxp.h create mode 100644 lib/lvgl/src/draw/nxp/pxp/lv_draw_nxp_pxp.mk create mode 100644 lib/lvgl/src/draw/nxp/pxp/lv_draw_pxp_blend.c create mode 100644 lib/lvgl/src/draw/nxp/pxp/lv_draw_pxp_blend.h create mode 100644 lib/lvgl/src/draw/nxp/pxp/lv_gpu_nxp_pxp.c create mode 100644 lib/lvgl/src/draw/nxp/pxp/lv_gpu_nxp_pxp.h create mode 100644 lib/lvgl/src/draw/nxp/pxp/lv_gpu_nxp_pxp_osa.c create mode 100644 lib/lvgl/src/draw/nxp/pxp/lv_gpu_nxp_pxp_osa.h create mode 100644 lib/lvgl/src/draw/nxp/vglite/lv_draw_nxp_vglite.mk create mode 100644 lib/lvgl/src/draw/nxp/vglite/lv_draw_vglite_arc.c create mode 100644 lib/lvgl/src/draw/nxp/vglite/lv_draw_vglite_arc.h create mode 100644 lib/lvgl/src/draw/nxp/vglite/lv_draw_vglite_blend.c create mode 100644 lib/lvgl/src/draw/nxp/vglite/lv_draw_vglite_blend.h create mode 100644 lib/lvgl/src/draw/nxp/vglite/lv_draw_vglite_rect.c create mode 100644 lib/lvgl/src/draw/nxp/vglite/lv_draw_vglite_rect.h create mode 100644 lib/lvgl/src/draw/nxp/vglite/lv_gpu_nxp_vglite.c create mode 100644 lib/lvgl/src/draw/nxp/vglite/lv_gpu_nxp_vglite.h (limited to 'lib/lvgl/src/draw/nxp') diff --git a/lib/lvgl b/lib/lvgl deleted file mode 160000 index 0732400e..00000000 --- a/lib/lvgl +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 0732400e7b564dd0e7dc4a924619d8e19c5b23a0 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*/ -- cgit v1.2.3