From 733e11d86dd16242f86656e8c8f8559b6607564c Mon Sep 17 00:00:00 2001 From: nicusorcitu <97085059+nicusorcitu@users.noreply.github.com> Date: Wed, 20 Sep 2023 13:32:24 +0300 Subject: [PATCH] feat(vglite, pxp): add beta version of NXP's PXP and VG-Lite acceleration (#4568) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Nicușor Cîțu --- Kconfig | 20 +- lv_conf_template.h | 6 + src/display/lv_display.c | 1 - src/draw/nxp/pxp/lv_draw_buf_pxp.c | 99 +++ src/draw/nxp/pxp/lv_draw_pxp.c | 317 +++++++++ src/draw/nxp/pxp/lv_draw_pxp.h | 77 +++ src/draw/nxp/pxp/lv_draw_pxp_bg_img.c | 94 +++ src/draw/nxp/pxp/lv_draw_pxp_fill.c | 151 +++++ src/draw/nxp/pxp/lv_draw_pxp_img.c | 364 +++++++++++ src/draw/nxp/pxp/lv_draw_pxp_layer.c | 71 ++ src/draw/nxp/pxp/lv_pxp_cfg.c | 96 +++ src/draw/nxp/pxp/lv_pxp_cfg.h | 103 +++ src/draw/nxp/pxp/lv_pxp_osa.c | 168 +++++ src/draw/nxp/pxp/lv_pxp_osa.h | 61 ++ src/draw/nxp/pxp/lv_pxp_utils.c | 151 +++++ src/draw/nxp/pxp/lv_pxp_utils.h | 65 ++ src/draw/nxp/vglite/lv_draw_buf_vglite.c | 194 ++++++ src/draw/nxp/vglite/lv_draw_vglite.c | 439 +++++++++++++ src/draw/nxp/vglite/lv_draw_vglite.h | 101 +++ src/draw/nxp/vglite/lv_draw_vglite_arc.c | 691 ++++++++++++++++++++ src/draw/nxp/vglite/lv_draw_vglite_bg_img.c | 114 ++++ src/draw/nxp/vglite/lv_draw_vglite_border.c | 175 +++++ src/draw/nxp/vglite/lv_draw_vglite_fill.c | 259 ++++++++ src/draw/nxp/vglite/lv_draw_vglite_img.c | 412 ++++++++++++ src/draw/nxp/vglite/lv_draw_vglite_label.c | 206 ++++++ src/draw/nxp/vglite/lv_draw_vglite_layer.c | 71 ++ src/draw/nxp/vglite/lv_draw_vglite_line.c | 158 +++++ src/draw/nxp/vglite/lv_vglite_buf.c | 117 ++++ src/draw/nxp/vglite/lv_vglite_buf.h | 120 ++++ src/draw/nxp/vglite/lv_vglite_matrix.c | 79 +++ src/draw/nxp/vglite/lv_vglite_matrix.h | 79 +++ src/draw/nxp/vglite/lv_vglite_path.c | 218 ++++++ src/draw/nxp/vglite/lv_vglite_path.h | 92 +++ src/draw/nxp/vglite/lv_vglite_utils.c | 292 +++++++++ src/draw/nxp/vglite/lv_vglite_utils.h | 165 +++++ src/lv_conf_internal.h | 18 + src/lv_init.c | 14 + 37 files changed, 5841 insertions(+), 17 deletions(-) create mode 100644 src/draw/nxp/pxp/lv_draw_buf_pxp.c create mode 100644 src/draw/nxp/pxp/lv_draw_pxp.c create mode 100644 src/draw/nxp/pxp/lv_draw_pxp.h create mode 100644 src/draw/nxp/pxp/lv_draw_pxp_bg_img.c create mode 100644 src/draw/nxp/pxp/lv_draw_pxp_fill.c create mode 100644 src/draw/nxp/pxp/lv_draw_pxp_img.c create mode 100644 src/draw/nxp/pxp/lv_draw_pxp_layer.c create mode 100644 src/draw/nxp/pxp/lv_pxp_cfg.c create mode 100644 src/draw/nxp/pxp/lv_pxp_cfg.h create mode 100644 src/draw/nxp/pxp/lv_pxp_osa.c create mode 100644 src/draw/nxp/pxp/lv_pxp_osa.h create mode 100644 src/draw/nxp/pxp/lv_pxp_utils.c create mode 100644 src/draw/nxp/pxp/lv_pxp_utils.h create mode 100644 src/draw/nxp/vglite/lv_draw_buf_vglite.c create mode 100644 src/draw/nxp/vglite/lv_draw_vglite.c create mode 100644 src/draw/nxp/vglite/lv_draw_vglite.h create mode 100644 src/draw/nxp/vglite/lv_draw_vglite_arc.c create mode 100644 src/draw/nxp/vglite/lv_draw_vglite_bg_img.c create mode 100644 src/draw/nxp/vglite/lv_draw_vglite_border.c create mode 100644 src/draw/nxp/vglite/lv_draw_vglite_fill.c create mode 100644 src/draw/nxp/vglite/lv_draw_vglite_img.c create mode 100644 src/draw/nxp/vglite/lv_draw_vglite_label.c create mode 100644 src/draw/nxp/vglite/lv_draw_vglite_layer.c create mode 100644 src/draw/nxp/vglite/lv_draw_vglite_line.c create mode 100644 src/draw/nxp/vglite/lv_vglite_buf.c create mode 100644 src/draw/nxp/vglite/lv_vglite_buf.h create mode 100644 src/draw/nxp/vglite/lv_vglite_matrix.c create mode 100644 src/draw/nxp/vglite/lv_vglite_matrix.h create mode 100644 src/draw/nxp/vglite/lv_vglite_path.c create mode 100644 src/draw/nxp/vglite/lv_vglite_path.h create mode 100644 src/draw/nxp/vglite/lv_vglite_utils.c create mode 100644 src/draw/nxp/vglite/lv_vglite_utils.h diff --git a/Kconfig b/Kconfig index ae0d617ed..fcd09dd01 100644 --- a/Kconfig +++ b/Kconfig @@ -298,23 +298,11 @@ menu "LVGL configuration" Must be defined to include path of CMSIS header of target processor e.g. "SWM341.h" - config LV_USE_GPU_NXP_PXP - bool "Use NXP's PXP GPU iMX RTxxx platforms." - config LV_USE_GPU_NXP_PXP_AUTO_INIT - bool "Call lv_gpu_nxp_pxp_init() automatically or manually." - depends on LV_USE_GPU_NXP_PXP - help - 1: Add default bare metal and FreeRTOS interrupt handling - routines for PXP (lv_gpu_nxp_pxp_osa.c) and call - lv_gpu_nxp_pxp_init() automatically during lv_init(). - Note that symbol SDK_OS_FREE_RTOS has to be defined in order - to use FreeRTOS OSA, otherwise bare-metal implementation is - selected. - 0: lv_gpu_nxp_pxp_init() has to be called manually before - lv_init(). + config LV_USE_DRAW_VGLITE + bool "Use NXP's VG-Lite GPU on iMX RTxxx platforms." - config LV_USE_GPU_NXP_VG_LITE - bool "Use NXP's VG-Lite GPU iMX RTxxx platforms." + config LV_USE_DRAW_PXP + bool "Use NXP's PXP on iMX RTxxx platforms." config LV_USE_GPU_SDL bool "Use SDL renderer API" diff --git a/lv_conf_template.h b/lv_conf_template.h index a65a20fec..229d196ea 100644 --- a/lv_conf_template.h +++ b/lv_conf_template.h @@ -123,6 +123,12 @@ #endif #endif +/* Use NXP's VG-Lite GPU on iMX RTxxx platforms. */ +#define LV_USE_DRAW_VGLITE 0 + +/* Use NXP's PXP on iMX RTxxx platforms. */ +#define LV_USE_DRAW_PXP 0 + /*================= * OPERATING SYSTEM *=================*/ diff --git a/src/display/lv_display.c b/src/display/lv_display.c index 2fd9de8ed..5ed713ceb 100644 --- a/src/display/lv_display.c +++ b/src/display/lv_display.c @@ -18,7 +18,6 @@ #include "../draw/sw/lv_draw_sw.h" #endif - /********************* * DEFINES *********************/ diff --git a/src/draw/nxp/pxp/lv_draw_buf_pxp.c b/src/draw/nxp/pxp/lv_draw_buf_pxp.c new file mode 100644 index 000000000..581d9ea0b --- /dev/null +++ b/src/draw/nxp/pxp/lv_draw_buf_pxp.c @@ -0,0 +1,99 @@ +/** + * @file lv_draw_buf_pxp.c + * + */ + +/** + * Copyright 2023 NXP + * + * SPDX-License-Identifier: MIT + */ + + +/********************* + * INCLUDES + *********************/ + +#include "lv_draw_pxp.h" + +#if LV_USE_DRAW_PXP +#include "lv_pxp_cfg.h" +#include "lv_pxp_utils.h" + +#include "lvgl_support.h" + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ + +static void _invalidate_cache(lv_draw_buf_t * draw_buf, const char * area); + +static void _pxp_buf_copy(void * dest_buf, uint32_t dest_stride, const lv_area_t * dest_area, + void * src_buf, uint32_t src_stride, const lv_area_t * src_area, lv_color_format_t cf); + +/********************** + * STATIC VARIABLES + **********************/ + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +void lv_draw_buf_pxp_init_handlers(void) +{ + lv_draw_buf_handlers_t * handlers = lv_draw_buf_get_handlers(); + + handlers->invalidate_cache_cb = _invalidate_cache; + handlers->buf_copy_cb = _pxp_buf_copy; +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +static void _invalidate_cache(lv_draw_buf_t * draw_buf, const char * area) +{ + LV_UNUSED(draw_buf); + LV_UNUSED(area); + + DEMO_CleanInvalidateCache(); +} + +void _pxp_buf_copy(void * dest_buf, uint32_t dest_stride, const lv_area_t * dest_area, + void * src_buf, uint32_t src_stride, const lv_area_t * src_area, + lv_color_format_t cf) +{ + lv_pxp_reset(); + + const pxp_pic_copy_config_t picCopyConfig = { + .srcPicBaseAddr = (uint32_t)src_buf, + .srcPitchBytes = src_stride, + .srcOffsetX = src_area->x1, + .srcOffsetY = src_area->y1, + .destPicBaseAddr = (uint32_t)dest_buf, + .destPitchBytes = dest_stride, + .destOffsetX = dest_area->x1, + .destOffsetY = dest_area->y1, + .width = lv_area_get_width(src_area), + .height = lv_area_get_height(src_area), + .pixelFormat = pxp_get_as_px_format(cf) + }; + + PXP_StartPictureCopy(PXP_ID, &picCopyConfig); + + lv_pxp_run(); +} + +#endif /*LV_USE_DRAW_PXP*/ diff --git a/src/draw/nxp/pxp/lv_draw_pxp.c b/src/draw/nxp/pxp/lv_draw_pxp.c new file mode 100644 index 000000000..038f587ea --- /dev/null +++ b/src/draw/nxp/pxp/lv_draw_pxp.c @@ -0,0 +1,317 @@ +/** + * @file lv_draw_pxp.c + * + */ + +/** + * Copyright 2022, 2023 NXP + * + * SPDX-License-Identifier: MIT + */ + + +/********************* + * INCLUDES + *********************/ + +#include "lv_draw_pxp.h" + +#if LV_USE_DRAW_PXP +#include "lv_pxp_cfg.h" +#include "../../../display/lv_display_private.h" + +/********************* + * DEFINES + *********************/ + +#define DRAW_UNIT_ID_PXP 3 + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ + +/* + * Dispatch a task to the PXP unit. + * Return 1 if task was dispatched, 0 otherwise (task not supported). + */ +static int32_t _pxp_dispatch(lv_draw_unit_t * draw_unit, lv_layer_t * layer); + +/* + * Evaluate a task and set the score and preferred PXP unit. + * Return 1 if task is preferred, 0 otherwise (task is not supported). + */ +static int32_t _pxp_evaluate(lv_draw_unit_t * draw_unit, lv_draw_task_t * task); + +#if LV_USE_OS + static void _pxp_render_thread_cb(void * ptr); +#endif + +static void _pxp_execute_drawing(lv_draw_pxp_unit_t * u); + +/********************** + * STATIC PROTOTYPES + **********************/ + +/********************** + * STATIC VARIABLES + **********************/ + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +void lv_draw_pxp_init(void) +{ + lv_draw_buf_pxp_init_handlers(); + + lv_draw_pxp_unit_t * draw_pxp_unit = lv_draw_create_unit(sizeof(lv_draw_pxp_unit_t)); + draw_pxp_unit->base_unit.dispatch_cb = _pxp_dispatch; + draw_pxp_unit->base_unit.evaluate_cb = _pxp_evaluate; + + lv_pxp_init(); + +#if LV_USE_OS + lv_thread_init(&draw_pxp_unit->thread, LV_THREAD_PRIO_HIGH, _pxp_render_thread_cb, 8 * 1024, draw_pxp_unit); +#endif +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +static inline bool _pxp_cf_supported(lv_color_format_t cf) +{ + bool is_cf_supported = (cf == LV_COLOR_FORMAT_RGB565 || cf == LV_COLOR_FORMAT_RGB888 || + cf == LV_COLOR_FORMAT_ARGB8888 || cf == LV_COLOR_FORMAT_XRGB8888); + + return is_cf_supported; +} + +static bool _pxp_draw_img_supported(const lv_draw_image_dsc_t * draw_dsc) +{ + const lv_image_dsc_t * img_dsc = draw_dsc->src; + + bool has_recolor = (draw_dsc->recolor_opa != LV_OPA_TRANSP); + bool has_transform = (draw_dsc->rotation != 0 || draw_dsc->zoom != LV_SCALE_NONE); + + /* Recolor and transformation are not supported at the same time. */ + if(has_recolor && has_transform) + return false; + + bool has_opa = (draw_dsc->opa < (lv_opa_t)LV_OPA_MAX); + bool src_has_alpha = (img_dsc->header.cf == LV_COLOR_FORMAT_ARGB8888); + + /* + * Recolor or transformation for images w/ opa or alpha channel can't + * be obtained in a single PXP configuration. Two steps are required. + */ + if((has_recolor || has_transform) && (has_opa || src_has_alpha)) + return false; + + /* PXP can only rotate at 90x angles. */ + if(draw_dsc->rotation % 900) + return false; + + /* + * 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. + * + * No combination of rotate with flip, scaling or decimation is possible + * if buffer is unaligned. + */ + if(has_transform && (img_dsc->header.w % 16 || img_dsc->header.h % 16)) + return false; + + return true; +} + +static int32_t _pxp_evaluate(lv_draw_unit_t * u, lv_draw_task_t * t) +{ + LV_UNUSED(u); + + switch(t->type) { + case LV_DRAW_TASK_TYPE_FILL: { + const lv_draw_fill_dsc_t * draw_dsc = (lv_draw_fill_dsc_t *) t->draw_dsc; + + /* Most simple case: just a plain rectangle (no radius, no gradient). */ + if((draw_dsc->radius != 0) || (draw_dsc->grad.dir != (lv_grad_dir_t)LV_GRAD_DIR_NONE)) + return 0; + + if(t->preference_score > 70) { + t->preference_score = 70; + t->preferred_draw_unit_id = DRAW_UNIT_ID_PXP; + } + return 1; + } + + case LV_DRAW_TASK_TYPE_BG_IMG: { + const lv_draw_bg_image_dsc_t * draw_dsc = (lv_draw_bg_image_dsc_t *) t->draw_dsc; + lv_image_src_t src_type = lv_image_src_get_type(draw_dsc->src); + + if(src_type == LV_IMAGE_SRC_SYMBOL) + return 0; + + if(!_pxp_cf_supported(draw_dsc->img_header.cf)) + return 0; + + if(t->preference_score > 70) { + t->preference_score = 70; + t->preferred_draw_unit_id = DRAW_UNIT_ID_PXP; + } + return 1; + } + + case LV_DRAW_TASK_TYPE_LAYER: { + const lv_draw_image_dsc_t * draw_dsc = (lv_draw_image_dsc_t *) t->draw_dsc; + lv_layer_t * layer_to_draw = (lv_layer_t *)draw_dsc->src; + lv_draw_buf_t * draw_buf = &layer_to_draw->draw_buf; + + if(!_pxp_cf_supported(draw_buf->color_format)) + return 0; + + if(!_pxp_draw_img_supported(draw_dsc)) + return 0; + + if(t->preference_score > 70) { + t->preference_score = 70; + t->preferred_draw_unit_id = DRAW_UNIT_ID_PXP; + } + return 1; + } + + case LV_DRAW_TASK_TYPE_IMAGE: { + lv_draw_image_dsc_t * draw_dsc = (lv_draw_image_dsc_t *) t->draw_dsc; + const lv_image_dsc_t * img_dsc = draw_dsc->src; + + if(!_pxp_cf_supported(img_dsc->header.cf)) + return 0; + + if(!_pxp_draw_img_supported(draw_dsc)) + return 0; + + if(t->preference_score > 70) { + t->preference_score = 70; + t->preferred_draw_unit_id = DRAW_UNIT_ID_PXP; + } + return 1; + } + default: + return 0; + } + + return 0; +} + +static int32_t _pxp_dispatch(lv_draw_unit_t * draw_unit, lv_layer_t * layer) +{ + lv_draw_pxp_unit_t * draw_pxp_unit = (lv_draw_pxp_unit_t *) draw_unit; + + /* Return immediately if it's busy with draw task. */ + if(draw_pxp_unit->task_act) + return 0; + + /* Return if target buffer format is not supported. */ + if(!_pxp_cf_supported(layer->draw_buf.color_format)) + return 0; + + /* Try to get an ready to draw. */ + lv_draw_task_t * t = lv_draw_get_next_available_task(layer, NULL, DRAW_UNIT_ID_PXP); + + /* Return 0 is no selection, some tasks can be supported only by other units. */ + if(t == NULL || t->preferred_draw_unit_id != DRAW_UNIT_ID_PXP) + return 0; + + void * buf = lv_draw_layer_alloc_buf(layer); + if(buf == NULL) + return -1; + + t->state = LV_DRAW_TASK_STATE_IN_PROGRESS; + draw_pxp_unit->base_unit.target_layer = layer; + draw_pxp_unit->base_unit.clip_area = &t->clip_area; + draw_pxp_unit->task_act = t; + +#if LV_USE_OS + /* Let the render thread work. */ + lv_thread_sync_signal(&draw_pxp_unit->sync); +#else + _pxp_execute_drawing(draw_pxp_unit); + + draw_pxp_unit->task_act->state = LV_DRAW_TASK_STATE_READY; + draw_pxp_unit->task_act = NULL; + + /* The draw unit is free now. Request a new dispatching as it can get a new task. */ + lv_draw_dispatch_request(); +#endif + + return 1; +} + +static void _pxp_execute_drawing(lv_draw_pxp_unit_t * u) +{ + lv_draw_task_t * t = u->task_act; + lv_draw_unit_t * draw_unit = (lv_draw_unit_t *)u; + + /* Invalidate cache */ + lv_layer_t * layer = draw_unit->target_layer; + lv_draw_buf_invalidate_cache(&layer->draw_buf, (const char *)&t->area); + + switch(t->type) { + case LV_DRAW_TASK_TYPE_FILL: + lv_draw_pxp_fill(draw_unit, t->draw_dsc, &t->area); + break; + case LV_DRAW_TASK_TYPE_BG_IMG: + lv_draw_pxp_bg_img(draw_unit, t->draw_dsc, &t->area); + break; + case LV_DRAW_TASK_TYPE_IMAGE: + lv_draw_pxp_img(draw_unit, t->draw_dsc, &t->area); + break; + case LV_DRAW_TASK_TYPE_LAYER: + lv_draw_pxp_layer(draw_unit, t->draw_dsc, &t->area); + break; + default: + break; + } +} + +#if LV_USE_OS +static void _pxp_render_thread_cb(void * ptr) +{ + lv_draw_pxp_unit_t * u = ptr; + + lv_thread_sync_init(&u->sync); + + while(1) { + /* + * Wait for sync if no task received or _draw_task_buf is empty. + * The thread will have to run as much as there are pending tasks. + */ + while(u->task_act == NULL) { + lv_thread_sync_wait(&u->sync); + } + + _pxp_execute_drawing(u); + + /* Cleanup. */ + u->task_act->state = LV_DRAW_TASK_STATE_READY; + u->task_act = NULL; + + /* The draw unit is free now. Request a new dispatching as it can get a new task. */ + lv_draw_dispatch_request(); + } +} +#endif + +#endif /*LV_USE_DRAW_PXP*/ diff --git a/src/draw/nxp/pxp/lv_draw_pxp.h b/src/draw/nxp/pxp/lv_draw_pxp.h new file mode 100644 index 000000000..156eb68e7 --- /dev/null +++ b/src/draw/nxp/pxp/lv_draw_pxp.h @@ -0,0 +1,77 @@ +/** + * @file lv_draw_pxp.h + * + */ + +/** + * Copyright 2022, 2023 NXP + * + * SPDX-License-Identifier: MIT + */ + + +#ifndef LV_DRAW_PXP_H +#define LV_DRAW_PXP_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ + +#include "../../../lv_conf_internal.h" + +#if LV_USE_DRAW_PXP +#include "../../sw/lv_draw_sw.h" + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +typedef lv_layer_t lv_pxp_layer_t; + +typedef struct { + lv_draw_unit_t base_unit; + struct _lv_draw_task_t * task_act; +#if LV_USE_OS + lv_thread_sync_t sync; + lv_thread_t thread; +#endif +} lv_draw_pxp_unit_t; + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +void lv_draw_buf_pxp_init_handlers(void); + +void lv_draw_pxp_init(void); + +void lv_draw_pxp_bg_img(lv_draw_unit_t * draw_unit, const lv_draw_bg_image_dsc_t * dsc, + const lv_area_t * coords); + +void lv_draw_pxp_fill(lv_draw_unit_t * draw_unit, const lv_draw_fill_dsc_t * dsc, + const lv_area_t * coords); + +void lv_draw_pxp_img(lv_draw_unit_t * draw_unit, const lv_draw_image_dsc_t * dsc, + const lv_area_t * coords); + +void lv_draw_pxp_layer(lv_draw_unit_t * draw_unit, const lv_draw_image_dsc_t * draw_dsc, + const lv_area_t * coords); + +/********************** + * MACROS + **********************/ +#endif /*LV_USE_DRAW_PXP*/ + +#ifdef __cplusplus +} /*extern "C"*/ +#endif + +#endif /*LV_DRAW_PXP_H*/ diff --git a/src/draw/nxp/pxp/lv_draw_pxp_bg_img.c b/src/draw/nxp/pxp/lv_draw_pxp_bg_img.c new file mode 100644 index 000000000..88e1ab8ab --- /dev/null +++ b/src/draw/nxp/pxp/lv_draw_pxp_bg_img.c @@ -0,0 +1,94 @@ +/** + * @file lv_draw_pxp_bg_img.c + * + */ + +/** + * Copyright 2023 NXP + * + * SPDX-License-Identifier: MIT + */ + + +/********************* + * INCLUDES + *********************/ + +#include "lv_draw_pxp.h" + +#if LV_USE_DRAW_PXP + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ + +/********************** + * STATIC VARIABLES + **********************/ + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +void lv_draw_pxp_bg_img(lv_draw_unit_t * draw_unit, const lv_draw_bg_image_dsc_t * dsc, + const lv_area_t * coords) +{ + if(dsc->src == NULL) return; + if(dsc->opa <= LV_OPA_MIN) return; + + lv_area_t clip_area; + if(!_lv_area_intersect(&clip_area, coords, draw_unit->clip_area)) { + return; + } + + const lv_area_t * clip_area_ori = draw_unit->clip_area; + draw_unit->clip_area = &clip_area; + + lv_draw_image_dsc_t img_dsc; + lv_draw_image_dsc_init(&img_dsc); + img_dsc.recolor = dsc->recolor; + img_dsc.recolor_opa = dsc->recolor_opa; + img_dsc.opa = dsc->opa; + img_dsc.src = dsc->src; + + /*Center align*/ + if(dsc->tiled == false) { + lv_area_t area; + area.x1 = coords->x1 + lv_area_get_width(coords) / 2 - dsc->img_header.w / 2; + area.y1 = coords->y1 + lv_area_get_height(coords) / 2 - dsc->img_header.h / 2; + area.x2 = area.x1 + dsc->img_header.w - 1; + area.y2 = area.y1 + dsc->img_header.h - 1; + + lv_draw_pxp_img(draw_unit, &img_dsc, &area); + } + else { + lv_area_t area; + area.y1 = coords->y1; + area.y2 = area.y1 + dsc->img_header.h - 1; + + for(; area.y1 <= coords->y2; area.y1 += dsc->img_header.h, area.y2 += dsc->img_header.h) { + + area.x1 = coords->x1; + area.x2 = area.x1 + dsc->img_header.w - 1; + for(; area.x1 <= coords->x2; area.x1 += dsc->img_header.w, area.x2 += dsc->img_header.w) { + lv_draw_pxp_img(draw_unit, &img_dsc, &area); + } + } + } + + draw_unit->clip_area = clip_area_ori; +} + +#endif /*LV_USE_DRAW_PXP*/ diff --git a/src/draw/nxp/pxp/lv_draw_pxp_fill.c b/src/draw/nxp/pxp/lv_draw_pxp_fill.c new file mode 100644 index 000000000..20ce0e274 --- /dev/null +++ b/src/draw/nxp/pxp/lv_draw_pxp_fill.c @@ -0,0 +1,151 @@ +/** + * @file lv_draw_pxp_fill.c + * + */ + +/** + * Copyright 2020-2023 NXP + * + * SPDX-License-Identifier: MIT + */ + + +/********************* + * INCLUDES + *********************/ + +#include "lv_draw_pxp.h" + +#if LV_USE_DRAW_PXP +#include "lv_pxp_cfg.h" +#include "lv_pxp_utils.h" + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ + +static void _pxp_fill(uint8_t * dest_buf, const lv_area_t * dest_area, lv_coord_t dest_stride, + lv_color_format_t dest_cf, const lv_draw_fill_dsc_t * dsc); + +/********************** + * STATIC VARIABLES + **********************/ + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +void lv_draw_pxp_fill(lv_draw_unit_t * draw_unit, const lv_draw_fill_dsc_t * dsc, + const lv_area_t * coords) +{ + if(dsc->opa <= (lv_opa_t)LV_OPA_MIN) + return; + + lv_layer_t * layer = draw_unit->target_layer; + + lv_area_t rel_coords; + lv_area_copy(&rel_coords, coords); + lv_area_move(&rel_coords, -layer->draw_buf_ofs.x, -layer->draw_buf_ofs.y); + + lv_area_t rel_clip_area; + lv_area_copy(&rel_clip_area, draw_unit->clip_area); + lv_area_move(&rel_clip_area, -layer->draw_buf_ofs.x, -layer->draw_buf_ofs.y); + + lv_area_t blend_area; + if(!_lv_area_intersect(&blend_area, &rel_coords, &rel_clip_area)) + return; /*Fully clipped, nothing to do*/ + + uint8_t * dest_buf = layer->draw_buf.buf; + lv_coord_t dest_stride = lv_draw_buf_get_stride(&layer->draw_buf); + lv_color_format_t dest_cf = layer->draw_buf.color_format; + + _pxp_fill(dest_buf, &blend_area, dest_stride, dest_cf, dsc); +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +static void _pxp_fill(uint8_t * dest_buf, const lv_area_t * dest_area, lv_coord_t dest_stride, + lv_color_format_t dest_cf, const lv_draw_fill_dsc_t * dsc) +{ + lv_coord_t dest_w = lv_area_get_width(dest_area); + lv_coord_t dest_h = lv_area_get_height(dest_area); + + lv_pxp_reset(); + + uint8_t px_size = lv_color_format_get_size(dest_cf); + + /*OUT buffer configure*/ + pxp_output_buffer_config_t outputConfig = { + .pixelFormat = pxp_get_out_px_format(dest_cf), + .interlacedMode = kPXP_OutputProgressive, + .buffer0Addr = (uint32_t)(dest_buf + dest_stride * dest_area->y1 + px_size * dest_area->x1), + .buffer1Addr = (uint32_t)NULL, + .pitchBytes = dest_stride, + .width = dest_w, + .height = dest_h + }; + + PXP_SetOutputBufferConfig(PXP_ID, &outputConfig); + + if(dsc->opa >= (lv_opa_t)LV_OPA_MAX) { + /*Simple color fill without opacity - AS disabled*/ + PXP_SetAlphaSurfacePosition(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_get_as_px_format(dest_cf), + .bufferAddr = outputConfig.buffer0Addr, + .pitchBytes = outputConfig.pitchBytes + }; + + PXP_SetAlphaSurfaceBufferConfig(PXP_ID, &asBufferConfig); + PXP_SetAlphaSurfacePosition(PXP_ID, 0U, 0U, dest_w - 1U, dest_h - 1U); + } + + /*Disable PS, use as color generator*/ + PXP_SetProcessSurfacePosition(PXP_ID, 0xFFFFU, 0xFFFFU, 0U, 0U); + PXP_SetProcessSurfaceBackGroundColor(PXP_ID, lv_color_to_u32(dsc->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 = (dsc->opa >= (lv_opa_t)LV_OPA_MAX) ? kPXP_PorterDuffFactorStraight : kPXP_PorterDuffFactorInversed, + .dstGlobalAlpha = dsc->opa, + .srcGlobalAlpha = dsc->opa, + .dstAlphaMode = kPXP_PorterDuffAlphaStraight, /*don't care*/ + .srcAlphaMode = kPXP_PorterDuffAlphaStraight /*don't care*/ + }; + + PXP_SetPorterDuffConfig(PXP_ID, &pdConfig); + + lv_pxp_run(); +} + +#endif /*LV_USE_DRAW_PXP*/ diff --git a/src/draw/nxp/pxp/lv_draw_pxp_img.c b/src/draw/nxp/pxp/lv_draw_pxp_img.c new file mode 100644 index 000000000..69bb061e4 --- /dev/null +++ b/src/draw/nxp/pxp/lv_draw_pxp_img.c @@ -0,0 +1,364 @@ +/** + * @file lv_draw_pxp_img.c + * + */ + +/** + * Copyright 2020-2023 NXP + * + * SPDX-License-Identifier: MIT + */ + + +/********************* + * INCLUDES + *********************/ + +#include "lv_draw_pxp.h" + +#if LV_USE_DRAW_PXP +#include "lv_pxp_cfg.h" +#include "lv_pxp_utils.h" + +#include + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ + +/* Blit w/ recolor for images w/o opa and alpha channel */ +static void _pxp_blit_recolor(uint8_t * dest_buf, const lv_area_t * dest_area, lv_coord_t dest_stride, + lv_color_format_t dest_cf, const uint8_t * src_buf, const lv_area_t * src_area, + lv_coord_t src_stride, lv_color_format_t src_cf, const lv_draw_image_dsc_t * dsc); + +/* Blit w/ transformation for images w/o opa and alpha channel */ +static void _pxp_blit_transform(uint8_t * dest_buf, const lv_area_t * dest_area, lv_coord_t dest_stride, + lv_color_format_t dest_cf, const uint8_t * src_buf, const lv_area_t * src_area, + lv_coord_t src_stride, lv_color_format_t src_cf, const lv_draw_image_dsc_t * dsc); + +/* Blit simple w/ opa and alpha channel */ +static void _pxp_blit(uint8_t * dest_buf, const lv_area_t * dest_area, lv_coord_t dest_stride, + lv_color_format_t dest_cf, const uint8_t * src_buf, const lv_area_t * src_area, + lv_coord_t src_stride, lv_color_format_t src_cf, lv_opa_t opa); + +/********************** + * STATIC VARIABLES + **********************/ + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +void lv_draw_pxp_img(lv_draw_unit_t * draw_unit, const lv_draw_image_dsc_t * dsc, + const lv_area_t * coords) +{ + if(dsc->opa <= (lv_opa_t)LV_OPA_MIN) + return; + + lv_layer_t * layer = draw_unit->target_layer; + const lv_image_dsc_t * img_dsc = dsc->src; + + lv_area_t rel_coords; + lv_area_copy(&rel_coords, coords); + lv_area_move(&rel_coords, -layer->draw_buf_ofs.x, -layer->draw_buf_ofs.y); + + lv_area_t rel_clip_area; + lv_area_copy(&rel_clip_area, draw_unit->clip_area); + lv_area_move(&rel_clip_area, -layer->draw_buf_ofs.x, -layer->draw_buf_ofs.y); + + lv_area_t blend_area; + bool has_transform = dsc->rotation != 0 || dsc->zoom != LV_SCALE_NONE; + if(has_transform) + lv_area_copy(&blend_area, &rel_coords); + else if(!_lv_area_intersect(&blend_area, &rel_coords, &rel_clip_area)) + return; /*Fully clipped, nothing to do*/ + + const uint8_t * src_buf = img_dsc->data; + + lv_area_t src_area; + src_area.x1 = blend_area.x1 - (coords->x1 - layer->draw_buf_ofs.x); + src_area.y1 = blend_area.y1 - (coords->y1 - layer->draw_buf_ofs.y); + src_area.x2 = src_area.x1 + lv_area_get_width(coords) - 1; + src_area.y2 = src_area.y1 + lv_area_get_height(coords) - 1; + lv_coord_t src_stride = img_dsc->header.stride; + lv_color_format_t src_cf = img_dsc->header.cf; + + uint8_t * dest_buf = layer->draw_buf.buf; + lv_coord_t dest_stride = lv_draw_buf_get_stride(&layer->draw_buf); + lv_color_format_t dest_cf = layer->draw_buf.color_format; + bool has_recolor = (dsc->recolor_opa != LV_OPA_TRANSP); + + if(has_recolor && !has_transform) + _pxp_blit_recolor(dest_buf, &blend_area, dest_stride, dest_cf, + src_buf, &src_area, src_stride, src_cf, dsc); + else if(has_transform) + _pxp_blit_transform(dest_buf, &blend_area, dest_stride, dest_cf, + src_buf, &src_area, src_stride, src_cf, dsc); + else + _pxp_blit(dest_buf, &blend_area, dest_stride, dest_cf, + src_buf, &src_area, src_stride, src_cf, dsc->opa); +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +static void _pxp_blit_recolor(uint8_t * dest_buf, const lv_area_t * dest_area, lv_coord_t dest_stride, + lv_color_format_t dest_cf, const uint8_t * src_buf, const lv_area_t * src_area, + lv_coord_t src_stride, lv_color_format_t src_cf, const lv_draw_image_dsc_t * dsc) +{ + + lv_coord_t dest_w = lv_area_get_width(dest_area); + lv_coord_t dest_h = lv_area_get_height(dest_area); + lv_coord_t src_w = lv_area_get_width(src_area); + lv_coord_t src_h = lv_area_get_height(src_area); + + bool src_has_alpha = (src_cf == LV_COLOR_FORMAT_ARGB8888); + uint8_t src_px_size = lv_color_format_get_size(src_cf); + uint8_t dest_px_size = lv_color_format_get_size(dest_cf); + + lv_pxp_reset(); + + /*AS buffer - source image*/ + pxp_as_buffer_config_t asBufferConfig = { + .pixelFormat = pxp_get_as_px_format(src_cf), + .bufferAddr = (uint32_t)(src_buf + src_stride * src_area->y1 + src_px_size * src_area->x1), + .pitchBytes = src_stride + }; + PXP_SetAlphaSurfaceBufferConfig(PXP_ID, &asBufferConfig); + PXP_SetAlphaSurfacePosition(PXP_ID, 0U, 0U, src_w - 1U, src_h - 1U); + + /*Disable PS, use as color generator*/ + PXP_SetProcessSurfacePosition(PXP_ID, 0xFFFFU, 0xFFFFU, 0U, 0U); + PXP_SetProcessSurfaceBackGroundColor(PXP_ID, lv_color_to_u32(dsc->recolor)); + + /*Output buffer*/ + pxp_output_buffer_config_t outputBufferConfig = { + .pixelFormat = pxp_get_out_px_format(dest_cf), + .interlacedMode = kPXP_OutputProgressive, + .buffer0Addr = (uint32_t)(dest_buf + dest_stride * dest_area->y1 + dest_px_size * dest_area->x1), + .buffer1Addr = (uint32_t)0U, + .pitchBytes = dest_stride, + .width = dest_w, + .height = dest_h + }; + PXP_SetOutputBufferConfig(PXP_ID, &outputBufferConfig); + + /** + * 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_PorterDuffColorWithAlpha, + .dstGlobalAlphaMode = kPXP_PorterDuffGlobalAlpha, + .srcGlobalAlphaMode = src_has_alpha ? kPXP_PorterDuffLocalAlpha : kPXP_PorterDuffGlobalAlpha, + .dstFactorMode = kPXP_PorterDuffFactorStraight, + .srcFactorMode = kPXP_PorterDuffFactorInversed, + .dstGlobalAlpha = dsc->recolor_opa, + .srcGlobalAlpha = 0xff, + .dstAlphaMode = kPXP_PorterDuffAlphaStraight, /*don't care*/ + .srcAlphaMode = kPXP_PorterDuffAlphaStraight + }; + PXP_SetPorterDuffConfig(PXP_ID, &pdConfig); + + lv_pxp_run(); +} + +static void _pxp_blit_transform(uint8_t * dest_buf, const lv_area_t * dest_area, lv_coord_t dest_stride, + lv_color_format_t dest_cf, const uint8_t * src_buf, const lv_area_t * src_area, + lv_coord_t src_stride, lv_color_format_t src_cf, const lv_draw_image_dsc_t * dsc) +{ + lv_coord_t src_w = lv_area_get_width(src_area); + lv_coord_t src_h = lv_area_get_height(src_area); + lv_coord_t dest_w = lv_area_get_width(dest_area); + lv_coord_t dest_h = lv_area_get_height(dest_area); + + lv_point_t pivot = dsc->pivot; + /*The offsets are now relative to the transformation result with pivot ULC*/ + lv_coord_t piv_offset_x = 0; + lv_coord_t piv_offset_y = 0; + + lv_coord_t trim_size = 0; + + bool has_rotation = (dsc->rotation != 0); + bool has_scale = (dsc->zoom != LV_SCALE_NONE); + uint8_t src_px_size = lv_color_format_get_size(src_cf); + uint8_t dest_px_size = lv_color_format_get_size(dest_cf); + + lv_pxp_reset(); + + if(has_rotation) { + /*Convert rotation angle and calculate offsets caused by pivot*/ + pxp_rotate_degree_t pxp_angle; + switch(dsc->rotation) { + case 0: + pxp_angle = kPXP_Rotate0; + piv_offset_x = 0; + piv_offset_y = 0; + break; + case 900: + pxp_angle = kPXP_Rotate90; + piv_offset_x = pivot.x + pivot.y - src_h; + piv_offset_y = pivot.y - pivot.x; + break; + case 1800: + pxp_angle = kPXP_Rotate180; + piv_offset_x = 2 * pivot.x - src_w; + piv_offset_y = 2 * pivot.y - src_h; + break; + case 2700: + pxp_angle = kPXP_Rotate270; + piv_offset_x = pivot.x - pivot.y; + piv_offset_y = pivot.x + pivot.y - src_w; + break; + default: + pxp_angle = kPXP_Rotate0; + piv_offset_x = 0; + piv_offset_y = 0; + } + /*PS buffer rotation and decimation does not function at the same time*/ + PXP_SetRotateConfig(PXP_ID, kPXP_RotateOutputBuffer, pxp_angle, kPXP_FlipDisable); + } + + if(has_scale) { + float scale_factor_fp = (float)dsc->zoom / LV_SCALE_NONE; + lv_coord_t scale_factor_int = (lv_coord_t)scale_factor_fp; + + /*Any scale_factor in (k, k + 1] will result in a trim equal to k*/ + if(scale_factor_fp == scale_factor_int) + trim_size = scale_factor_int - 1; + else + trim_size = scale_factor_int; + + dest_w = src_w * scale_factor_fp + trim_size; + dest_h = src_h * scale_factor_fp + trim_size; + + /*Final pivot offset = scale_factor * rotation_pivot_offset + scaling_pivot_offset*/ + piv_offset_x = floor(scale_factor_fp * piv_offset_x) - floor((scale_factor_fp - 1) * pivot.x); + piv_offset_y = floor(scale_factor_fp * piv_offset_y) - floor((scale_factor_fp - 1) * pivot.y); + } + + /*PS buffer - source image*/ + pxp_ps_buffer_config_t psBufferConfig = { + .pixelFormat = pxp_get_ps_px_format(src_cf), + .swapByte = false, + .bufferAddr = (uint32_t)(src_buf + src_stride * src_area->y1 + src_px_size * src_area->x1), + .bufferAddrU = 0, + .bufferAddrV = 0, + .pitchBytes = src_stride + }; + PXP_SetProcessSurfaceBufferConfig(PXP_ID, &psBufferConfig); + PXP_SetProcessSurfacePosition(PXP_ID, 0U, 0U, dest_w - trim_size - 1U, dest_h - trim_size - 1U); + + if(has_scale) + PXP_SetProcessSurfaceScaler(PXP_ID, src_w, src_h, dest_w, dest_h); + + /*AS disabled */ + PXP_SetAlphaSurfacePosition(PXP_ID, 0xFFFFU, 0xFFFFU, 0U, 0U); + + /*Output buffer*/ + pxp_output_buffer_config_t outputBufferConfig = { + .pixelFormat = pxp_get_out_px_format(dest_cf), + .interlacedMode = kPXP_OutputProgressive, + .buffer0Addr = (uint32_t)(dest_buf + dest_stride * (dest_area->y1 + piv_offset_y) + dest_px_size * (dest_area->x1 + piv_offset_x)), + .buffer1Addr = (uint32_t)0U, + .pitchBytes = dest_stride, + .width = dest_w - trim_size, + .height = dest_h - trim_size + }; + PXP_SetOutputBufferConfig(PXP_ID, &outputBufferConfig); + + lv_pxp_run(); +} + +static void _pxp_blit(uint8_t * dest_buf, const lv_area_t * dest_area, lv_coord_t dest_stride, + lv_color_format_t dest_cf, const uint8_t * src_buf, const lv_area_t * src_area, + lv_coord_t src_stride, lv_color_format_t src_cf, lv_opa_t opa) +{ + lv_coord_t dest_w = lv_area_get_width(dest_area); + lv_coord_t dest_h = lv_area_get_height(dest_area); + lv_coord_t src_w = lv_area_get_width(src_area); + lv_coord_t src_h = lv_area_get_height(src_area); + + bool src_has_alpha = (src_cf == LV_COLOR_FORMAT_ARGB8888); + uint8_t src_px_size = lv_color_format_get_size(src_cf); + uint8_t dest_px_size = lv_color_format_get_size(dest_cf); + + lv_pxp_reset(); + + pxp_as_blend_config_t asBlendConfig = { + .alpha = opa, + .invertAlpha = false, + .alphaMode = kPXP_AlphaRop, + .ropMode = kPXP_RopMergeAs + }; + + if(opa >= (lv_opa_t)LV_OPA_MAX && !src_has_alpha) { + /*Simple blit, no effect - Disable PS buffer*/ + PXP_SetProcessSurfacePosition(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_get_ps_px_format(dest_cf), + .swapByte = false, + .bufferAddr = (uint32_t)(dest_buf + dest_stride * dest_area->y1 + dest_px_size * dest_area->x1), + .bufferAddrU = 0U, + .bufferAddrV = 0U, + .pitchBytes = dest_stride + }; + + if(opa >= (lv_opa_t)LV_OPA_MAX) + asBlendConfig.alphaMode = src_has_alpha ? kPXP_AlphaEmbedded : kPXP_AlphaOverride; + else + asBlendConfig.alphaMode = src_has_alpha ? kPXP_AlphaMultiply : kPXP_AlphaOverride; + + PXP_SetProcessSurfaceBufferConfig(PXP_ID, &psBufferConfig); + PXP_SetProcessSurfacePosition(PXP_ID, 0U, 0U, dest_w - 1U, dest_h - 1U); + } + + /*AS buffer - source image*/ + pxp_as_buffer_config_t asBufferConfig = { + .pixelFormat = pxp_get_as_px_format(src_cf), + .bufferAddr = (uint32_t)(src_buf + src_stride * src_area->y1 + src_px_size * src_area->x1), + .pitchBytes = src_stride + }; + PXP_SetAlphaSurfaceBufferConfig(PXP_ID, &asBufferConfig); + PXP_SetAlphaSurfacePosition(PXP_ID, 0U, 0U, src_w - 1U, src_h - 1U); + PXP_SetAlphaSurfaceBlendConfig(PXP_ID, &asBlendConfig); + PXP_EnableAlphaSurfaceOverlayColorKey(PXP_ID, false); + + /*Output buffer.*/ + pxp_output_buffer_config_t outputBufferConfig = { + .pixelFormat = pxp_get_out_px_format(dest_cf), + .interlacedMode = kPXP_OutputProgressive, + .buffer0Addr = (uint32_t)(dest_buf + dest_stride * dest_area->y1 + dest_px_size * dest_area->x1), + .buffer1Addr = (uint32_t)0U, + .pitchBytes = dest_stride, + .width = dest_w, + .height = dest_h + }; + PXP_SetOutputBufferConfig(PXP_ID, &outputBufferConfig); + + lv_pxp_run(); +} + +#endif /*LV_USE_DRAW_PXP*/ diff --git a/src/draw/nxp/pxp/lv_draw_pxp_layer.c b/src/draw/nxp/pxp/lv_draw_pxp_layer.c new file mode 100644 index 000000000..ad7322d50 --- /dev/null +++ b/src/draw/nxp/pxp/lv_draw_pxp_layer.c @@ -0,0 +1,71 @@ +/** + * @file lv_draw_pxp_layer.c + * + */ + +/** + * Copyright 2023 NXP + * + * SPDX-License-Identifier: MIT + */ + + +/********************* + * INCLUDES + *********************/ + +#include "lv_draw_pxp.h" + +#if LV_USE_DRAW_PXP + +#include "../../../stdlib/lv_string.h" + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ + +/********************** + * STATIC VARIABLES + **********************/ + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +void lv_draw_pxp_layer(lv_draw_unit_t * draw_unit, const lv_draw_image_dsc_t * draw_dsc, + const lv_area_t * coords) +{ + lv_layer_t * layer_to_draw = (lv_layer_t *)draw_dsc->src; + + /*It can happen that nothing was draw on a layer and therefore its buffer is not allocated. + *In this case just return. */ + if(layer_to_draw->draw_buf.buf == NULL) + return; + + lv_image_dsc_t img_dsc; + img_dsc.header.w = layer_to_draw->draw_buf.width; + img_dsc.header.h = layer_to_draw->draw_buf.height; + img_dsc.header.cf = layer_to_draw->draw_buf.color_format; + img_dsc.header.always_zero = 0; + img_dsc.data = lv_draw_buf_get_buf(&layer_to_draw->draw_buf); + + lv_draw_image_dsc_t new_draw_dsc; + lv_memcpy(&new_draw_dsc, draw_dsc, sizeof(lv_draw_image_dsc_t)); + new_draw_dsc.src = &img_dsc; + + lv_draw_pxp_img(draw_unit, &new_draw_dsc, coords); +} + +#endif /*LV_USE_DRAW_PXP*/ diff --git a/src/draw/nxp/pxp/lv_pxp_cfg.c b/src/draw/nxp/pxp/lv_pxp_cfg.c new file mode 100644 index 000000000..ef813cb23 --- /dev/null +++ b/src/draw/nxp/pxp/lv_pxp_cfg.c @@ -0,0 +1,96 @@ +/** + * @file lv_pxp_cfg.c + * + */ + +/** + * Copyright 2020-2023 NXP + * + * SPDX-License-Identifier: MIT + */ + + +/********************* + * INCLUDES + *********************/ + +#include "lv_pxp_cfg.h" + +#if LV_USE_DRAW_PXP +#include "lv_pxp_osa.h" + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ + +/********************** + * STATIC VARIABLES + **********************/ + +static pxp_cfg_t * _pxp_cfg; + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +void lv_pxp_init(void) +{ + _pxp_cfg = pxp_get_default_cfg(); + + if(!_pxp_cfg || !_pxp_cfg->pxp_interrupt_deinit || !_pxp_cfg->pxp_interrupt_init || + !_pxp_cfg->pxp_run || !_pxp_cfg->pxp_wait) + LV_LOG_ERROR("PXP configuration error."); + + PXP_Init(PXP_ID); + + PXP_EnableCsc1(PXP_ID, false); /*Disable CSC1, it is enabled by default.*/ + PXP_SetProcessBlockSize(PXP_ID, kPXP_BlockSize16); /*Block size 16x16 for higher performance*/ + + PXP_EnableInterrupts(PXP_ID, kPXP_CompleteInterruptEnable); + + _pxp_cfg->pxp_interrupt_init(); +} + +void lv_pxp_deinit(void) +{ + _pxp_cfg->pxp_interrupt_deinit(); + PXP_DisableInterrupts(PXP_ID, kPXP_CompleteInterruptEnable); + PXP_Deinit(PXP_ID); +} + +void lv_pxp_reset(void) +{ + PXP_ResetControl(PXP_ID); + + PXP_EnableCsc1(PXP_ID, false); /*Disable CSC1, it is enabled by default.*/ + PXP_SetProcessBlockSize(PXP_ID, kPXP_BlockSize16); /*Block size 16x16 for higher performance*/ +} + +void lv_pxp_run(void) +{ + _pxp_cfg->pxp_run(); + _pxp_cfg->pxp_wait(); +} + +void lv_pxp_wait(void) +{ + _pxp_cfg->pxp_wait(); +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +#endif /*LV_USE_DRAW_PXP*/ diff --git a/src/draw/nxp/pxp/lv_pxp_cfg.h b/src/draw/nxp/pxp/lv_pxp_cfg.h new file mode 100644 index 000000000..227307173 --- /dev/null +++ b/src/draw/nxp/pxp/lv_pxp_cfg.h @@ -0,0 +1,103 @@ +/** + * @file lv_pxp_cfg.h + * + */ + +/** + * Copyright 2020-2023 NXP + * + * SPDX-License-Identifier: MIT + */ + + +#ifndef LV_PXP_CFG_H +#define LV_PXP_CFG_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ + +#include "../../../lv_conf_internal.h" + +#if LV_USE_DRAW_PXP +#include "fsl_cache.h" +#include "fsl_pxp.h" + +#include "../../../misc/lv_log.h" + +/********************* + * DEFINES + *********************/ + +/** PXP module instance to use*/ +#define PXP_ID PXP + +/** PXP interrupt line ID*/ +#define PXP_IRQ_ID PXP_IRQn + +/********************** + * TYPEDEFS + **********************/ + +/** + * NXP PXP device configuration. + */ +typedef struct { + /** Callback for PXP interrupt initialization*/ + void (*pxp_interrupt_init)(void); + + /** Callback for PXP interrupt de-initialization*/ + void (*pxp_interrupt_deinit)(void); + + /** Callback for PXP start*/ + void (*pxp_run)(void); + + /** Callback for waiting of PXP completion*/ + void (*pxp_wait)(void); +} pxp_cfg_t; + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +/** + * Reset and initialize PXP device. This function should be called as a part + * of display init sequence. + */ +void lv_pxp_init(void); + +/** + * Disable PXP device. Should be called during display deinit sequence. + */ +void lv_pxp_deinit(void); + +/** + * Reset PXP device. + */ +void lv_pxp_reset(void); + +/** + * Clear cache and start PXP. + */ +void lv_pxp_run(void); + +/** + * Wait for PXP completion. + */ +void lv_pxp_wait(void); + +/********************** + * MACROS + **********************/ + +#endif /*LV_USE_DRAW_PXP*/ + +#ifdef __cplusplus +} /*extern "C"*/ +#endif + +#endif /*LV_PXP_CFG_H*/ diff --git a/src/draw/nxp/pxp/lv_pxp_osa.c b/src/draw/nxp/pxp/lv_pxp_osa.c new file mode 100644 index 000000000..1632e719d --- /dev/null +++ b/src/draw/nxp/pxp/lv_pxp_osa.c @@ -0,0 +1,168 @@ +/** + * @file lv_pxp_osa.c + * + */ + +/** + * Copyright 2020, 2022, 2023 NXP + * + * SPDX-License-Identifier: MIT + */ + + +/********************* + * INCLUDES + *********************/ + +#include "lv_pxp_osa.h" + +#if LV_USE_DRAW_PXP +#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 void _pxp_interrupt_init(void); + +/** + * PXP interrupt de-initialization. + */ +static void _pxp_interrupt_deinit(void); + +/** + * Start the PXP job. + */ +static void _pxp_run(void); + +/** + * Wait for PXP completion. + */ +static void _pxp_wait(void); + +/********************** + * STATIC VARIABLES + **********************/ + +#if defined(SDK_OS_FREE_RTOS) + static SemaphoreHandle_t xPXPIdleSemaphore; +#endif +static volatile bool ucPXPIdle; + +static pxp_cfg_t _pxp_default_cfg = { + .pxp_interrupt_init = _pxp_interrupt_init, + .pxp_interrupt_deinit = _pxp_interrupt_deinit, + .pxp_run = _pxp_run, + .pxp_wait = _pxp_wait, +}; + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +void PXP_IRQHandler(void) +{ +#if defined(SDK_OS_FREE_RTOS) + BaseType_t xHigherPriorityTaskWoken = pdFALSE; +#endif + + if(kPXP_CompleteFlag & PXP_GetStatusFlags(PXP_ID)) { + PXP_ClearStatusFlags(PXP_ID, kPXP_CompleteFlag); +#if defined(SDK_OS_FREE_RTOS) + xSemaphoreGiveFromISR(xPXPIdleSemaphore, &xHigherPriorityTaskWoken); + + /* If xHigherPriorityTaskWoken is now set to pdTRUE then a context switch + should be performed to ensure the interrupt returns directly to the highest + priority task. The macro used for this purpose is dependent on the port in + use and may be called portEND_SWITCHING_ISR(). */ + portYIELD_FROM_ISR(xHigherPriorityTaskWoken); +#else + ucPXPIdle = true; +#endif + } +} + +pxp_cfg_t * pxp_get_default_cfg(void) +{ + return &_pxp_default_cfg; +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +static void _pxp_interrupt_init(void) +{ +#if defined(SDK_OS_FREE_RTOS) + xPXPIdleSemaphore = xSemaphoreCreateBinary(); + if(xPXPIdleSemaphore == NULL) { + LV_LOG_ERROR("xSemaphoreCreateBinary failed!"); + return; + } + + NVIC_SetPriority(PXP_IRQ_ID, configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY + 1); +#endif + ucPXPIdle = true; + + NVIC_EnableIRQ(PXP_IRQ_ID); +} + +static void _pxp_interrupt_deinit(void) +{ + NVIC_DisableIRQ(PXP_IRQ_ID); +#if defined(SDK_OS_FREE_RTOS) + vSemaphoreDelete(xPXPIdleSemaphore); +#endif +} + +/** + * Function to start PXP job. + */ +static void _pxp_run(void) +{ + ucPXPIdle = false; + + PXP_EnableInterrupts(PXP_ID, kPXP_CompleteInterruptEnable); + PXP_Start(PXP_ID); +} + +/** + * Function to wait for PXP completion. + */ +static void _pxp_wait(void) +{ +#if defined(SDK_OS_FREE_RTOS) + /* Return if PXP was never started, otherwise the semaphore will lock forever. */ + if(ucPXPIdle == true) + return; + + if(xSemaphoreTake(xPXPIdleSemaphore, portMAX_DELAY) == pdTRUE) + ucPXPIdle = true; +#else + while(ucPXPIdle == false) { + } +#endif +} + +#endif /*LV_USE_DRAW_PXP*/ diff --git a/src/draw/nxp/pxp/lv_pxp_osa.h b/src/draw/nxp/pxp/lv_pxp_osa.h new file mode 100644 index 000000000..c08d1c050 --- /dev/null +++ b/src/draw/nxp/pxp/lv_pxp_osa.h @@ -0,0 +1,61 @@ +/** + * @file lv_pxp_osa.h + * + */ + +/** + * Copyright 2020, 2022, 2023 NXP + * + * SPDX-License-Identifier: MIT + */ + + +#ifndef LV_PXP_OSA_H +#define LV_PXP_OSA_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ + +#include "../../../lv_conf_internal.h" + +#if LV_USE_DRAW_PXP +#include "lv_pxp_cfg.h" + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +/** + * PXP device interrupt handler. Used to check PXP task completion status. + */ +void PXP_IRQHandler(void); + +/** + * Get the PXP default configuration. + */ +pxp_cfg_t * pxp_get_default_cfg(void); + +/********************** + * MACROS + **********************/ + +#endif /*LV_USE_DRAW_PXP*/ + +#ifdef __cplusplus +} /*extern "C"*/ +#endif + +#endif /*LV_PXP_OSA_H*/ diff --git a/src/draw/nxp/pxp/lv_pxp_utils.c b/src/draw/nxp/pxp/lv_pxp_utils.c new file mode 100644 index 000000000..720edd036 --- /dev/null +++ b/src/draw/nxp/pxp/lv_pxp_utils.c @@ -0,0 +1,151 @@ +/** + * @file lv_pxp_utils.c + * + */ + +/** + * Copyright 2023 NXP + * + * SPDX-License-Identifier: MIT + */ + + +/********************* + * INCLUDES + *********************/ + +#include "lv_pxp_utils.h" + +#if LV_USE_DRAW_PXP + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +pxp_output_pixel_format_t pxp_get_out_px_format(lv_color_format_t cf) +{ + pxp_output_pixel_format_t out_px_format = kPXP_OutputPixelFormatRGB565; + + switch(cf) { + /*2 byte (+alpha) formats*/ + case LV_COLOR_FORMAT_RGB565: + out_px_format = kPXP_OutputPixelFormatRGB565; + break; + case LV_COLOR_FORMAT_RGB565A8: + LV_ASSERT_MSG(false, "Unsupported color format."); + break; + + /*3 byte (+alpha) formats*/ + case LV_COLOR_FORMAT_RGB888: + out_px_format = kPXP_OutputPixelFormatRGB888P; + break; + case LV_COLOR_FORMAT_ARGB8888: + out_px_format = kPXP_OutputPixelFormatARGB8888; + break; + case LV_COLOR_FORMAT_XRGB8888: + out_px_format = kPXP_OutputPixelFormatRGB888; + break; + + default: + LV_ASSERT_MSG(false, "Unsupported color format."); + break; + } + + return out_px_format; +} + +pxp_as_pixel_format_t pxp_get_as_px_format(lv_color_format_t cf) +{ + pxp_as_pixel_format_t as_px_format = kPXP_AsPixelFormatRGB565; + + switch(cf) { + /*2 byte (+alpha) formats*/ + case LV_COLOR_FORMAT_RGB565: + as_px_format = kPXP_AsPixelFormatRGB565; + break; + case LV_COLOR_FORMAT_RGB565A8: + LV_ASSERT_MSG(false, "Unsupported color format."); + break; + + /*3 byte (+alpha) formats*/ + case LV_COLOR_FORMAT_RGB888: + LV_ASSERT_MSG(false, "Unsupported color format."); + break; + case LV_COLOR_FORMAT_ARGB8888: + as_px_format = kPXP_AsPixelFormatARGB8888; + break; + case LV_COLOR_FORMAT_XRGB8888: + as_px_format = kPXP_AsPixelFormatRGB888; + break; + + default: + LV_ASSERT_MSG(false, "Unsupported color format."); + break; + } + + return as_px_format; +} + +pxp_ps_pixel_format_t pxp_get_ps_px_format(lv_color_format_t cf) +{ + pxp_ps_pixel_format_t ps_px_format = kPXP_PsPixelFormatRGB565; + + switch(cf) { + /*2 byte (+alpha) formats*/ + case LV_COLOR_FORMAT_RGB565: + ps_px_format = kPXP_PsPixelFormatRGB565; + break; + case LV_COLOR_FORMAT_RGB565A8: + LV_ASSERT_MSG(false, "Unsupported color format."); + break; + + /*3 byte (+alpha) formats*/ + case LV_COLOR_FORMAT_RGB888: + LV_ASSERT_MSG(false, "Unsupported color format."); + break; + case LV_COLOR_FORMAT_ARGB8888: +#if (!(defined(FSL_FEATURE_PXP_HAS_NO_EXTEND_PIXEL_FORMAT) && FSL_FEATURE_PXP_HAS_NO_EXTEND_PIXEL_FORMAT)) && \ + (!(defined(FSL_FEATURE_PXP_V3) && FSL_FEATURE_PXP_V3)) + ps_px_format = kPXP_PsPixelFormatARGB8888; +#else + LV_ASSERT_MSG(false, "Unsupported color format."); +#endif + break; + case LV_COLOR_FORMAT_XRGB8888: +#if (!(defined(FSL_FEATURE_PXP_HAS_NO_EXTEND_PIXEL_FORMAT) && FSL_FEATURE_PXP_HAS_NO_EXTEND_PIXEL_FORMAT)) && \ + (!(defined(FSL_FEATURE_PXP_V3) && FSL_FEATURE_PXP_V3)) + ps_px_format = kPXP_PsPixelFormatARGB8888; +#else + ps_px_format = kPXP_PsPixelFormatRGB888; +#endif + break; + + default: + LV_ASSERT_MSG(false, "Unsupported color format."); + break; + } + + return ps_px_format; +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +#endif /*LV_USE_DRAW_PXP*/ diff --git a/src/draw/nxp/pxp/lv_pxp_utils.h b/src/draw/nxp/pxp/lv_pxp_utils.h new file mode 100644 index 000000000..d5eb9cadc --- /dev/null +++ b/src/draw/nxp/pxp/lv_pxp_utils.h @@ -0,0 +1,65 @@ +/** + * @file lv_pxp_utils.h + * + */ + +/** + * Copyright 2023 NXP + * + * SPDX-License-Identifier: MIT + */ + + +#ifndef LV_PXP_UTILS_H +#define LV_PXP_UTILS_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ +#include "../../../lv_conf_internal.h" + +#if LV_USE_DRAW_PXP +#include "fsl_pxp.h" +#include "../../../misc/lv_color.h" + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +pxp_output_pixel_format_t pxp_get_out_px_format(lv_color_format_t cf); + +pxp_as_pixel_format_t pxp_get_as_px_format(lv_color_format_t cf); + +pxp_ps_pixel_format_t pxp_get_ps_px_format(lv_color_format_t cf); + +/********************** + * MACROS + **********************/ + +/********************** + * STATIC FUNCTIONS + **********************/ + +#endif /*LV_USE_DRAW_PXP*/ + +#ifdef __cplusplus +} /*extern "C"*/ +#endif + +#endif /*LV_PXP_UTILS_H*/ diff --git a/src/draw/nxp/vglite/lv_draw_buf_vglite.c b/src/draw/nxp/vglite/lv_draw_buf_vglite.c new file mode 100644 index 000000000..fcd42e25c --- /dev/null +++ b/src/draw/nxp/vglite/lv_draw_buf_vglite.c @@ -0,0 +1,194 @@ +/** + * @file lv_draw_buf_vglite.c + * + */ + +/** + * Copyright 2023 NXP + * + * SPDX-License-Identifier: MIT + */ + + +/********************* + * INCLUDES + *********************/ + +#include "lv_draw_vglite.h" + +#if LV_USE_DRAW_VGLITE +#include "lv_vglite_buf.h" +#include "lv_vglite_matrix.h" +#include "lv_vglite_utils.h" + +#include "lvgl_support.h" + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ + +static void * _buf_malloc(size_t size_bytes, lv_color_format_t cf); + +static void * _align_buf(void * buf, lv_color_format_t cf); + +static void _invalidate_cache(lv_draw_buf_t * draw_buf, const char * area); + +static uint32_t _width_to_stride(uint32_t w, lv_color_format_t cf); + +static void * _go_to_xy(lv_draw_buf_t * draw_buf, lv_coord_t x, lv_coord_t y); + +static void _vglite_buf_clear(lv_draw_buf_t * draw_buf, const lv_area_t * area); + +static void _vglite_buf_copy(void * dest_buf, uint32_t dest_stride, const lv_area_t * dest_area, + void * src_buf, uint32_t src_stride, const lv_area_t * src_area, lv_color_format_t cf); + +/********************** + * STATIC VARIABLES + **********************/ + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +void lv_draw_buf_vglite_init_handlers(void) +{ + lv_draw_buf_handlers_t * handlers = lv_draw_buf_get_handlers(); + + handlers->buf_malloc_cb = _buf_malloc; + handlers->align_pointer_cb = _align_buf; + handlers->invalidate_cache_cb = _invalidate_cache; + handlers->width_to_stride_cb = _width_to_stride; + handlers->go_to_xy_cb = _go_to_xy; + handlers->buf_clear_cb = _vglite_buf_clear; + handlers->buf_copy_cb = _vglite_buf_copy; +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +static void * _buf_malloc(size_t size_bytes, lv_color_format_t cf) +{ + uint8_t align_bytes = vglite_get_alignment(cf); + + /*Allocate larger memory to be sure it can be aligned as needed*/ + size_bytes += align_bytes - 1; + + return lv_malloc(size_bytes); +} + +static void * _align_buf(void * buf, lv_color_format_t cf) +{ + uint8_t align_bytes = vglite_get_alignment(cf); + + uint8_t * buf_u8 = buf; + if(buf_u8) { + buf_u8 += align_bytes - 1; + buf_u8 = (uint8_t *)((lv_uintptr_t)buf_u8 & ~(align_bytes - 1)); + } + + return buf_u8; +} + +static void _invalidate_cache(lv_draw_buf_t * draw_buf, const char * area) +{ + LV_UNUSED(draw_buf); + LV_UNUSED(area); + + DEMO_CleanInvalidateCache(); +} + +static uint32_t _width_to_stride(uint32_t w, lv_color_format_t cf) +{ + uint8_t bits_per_pixel = vglite_get_px_size(cf); + uint32_t width_bits = (w * bits_per_pixel + 7) & ~7; + uint32_t width_bytes = width_bits / 8; + uint8_t align_bytes = vglite_get_alignment(cf); + + return (width_bytes + align_bytes - 1) & ~(align_bytes - 1); +} + +static void * _go_to_xy(lv_draw_buf_t * draw_buf, lv_coord_t x, lv_coord_t y) +{ + uint8_t bits_per_pixel = vglite_get_px_size(draw_buf->color_format); + uint32_t stride = lv_draw_buf_get_stride(draw_buf); + uint8_t * buf_tmp = lv_draw_buf_get_buf(draw_buf); + + buf_tmp += stride * y; + buf_tmp += (x * bits_per_pixel) / 8; + + return buf_tmp; +} + +static void _vglite_buf_clear(lv_draw_buf_t * draw_buf, const lv_area_t * area) +{ + uint32_t stride = lv_draw_buf_get_stride(draw_buf); + + /* Set vgbuf structure. */ + vg_lite_buffer_t vgbuf; + vglite_set_buf(&vgbuf, draw_buf->buf, draw_buf->width, draw_buf->height, stride, draw_buf->color_format); + + vg_lite_color_t vgcol = 0; + + vg_lite_rectangle_t rect = { + .x = area->x1, + .y = area->y1, + .width = lv_area_get_width(area), + .height = lv_area_get_height(area) + }; + + vg_lite_error_t err = vg_lite_clear(&vgbuf, &rect, vgcol); + LV_ASSERT_MSG(err == VG_LITE_SUCCESS, "Clear failed."); + + vglite_run(); +} + +static void _vglite_buf_copy(void * dest_buf, uint32_t dest_stride, const lv_area_t * dest_area, + void * src_buf, uint32_t src_stride, const lv_area_t * src_area, + lv_color_format_t cf) +{ + /* Set src_vgbuf structure. */ + vg_lite_buffer_t src_vgbuf; + vglite_set_buf(&src_vgbuf, src_buf, lv_area_get_width(src_area), lv_area_get_height(src_area), src_stride, cf); + + /* Set dest_vgbuf structure. */ + vg_lite_buffer_t dest_vgbuf; + vglite_set_buf(&dest_vgbuf, dest_buf, lv_area_get_width(dest_area), lv_area_get_height(dest_area), dest_stride, cf); + + uint32_t rect[] = { + (uint32_t)src_area->x1, /* start x */ + (uint32_t)src_area->y1, /* start y */ + (uint32_t)lv_area_get_width(src_area), /* width */ + (uint32_t)lv_area_get_height(src_area) /* height */ + }; + + /* Set scissor. */ + vglite_set_scissor(dest_area); + + /* Set vgmatrix. */ + vglite_set_translation_matrix(dest_area); + vg_lite_matrix_t * vgmatrix = vglite_get_matrix(); + + vg_lite_error_t err = vg_lite_blit_rect(&dest_vgbuf, &src_vgbuf, rect, vgmatrix, + VG_LITE_BLEND_NONE, 0xFFFFFFFFU, VG_LITE_FILTER_POINT); + LV_ASSERT_MSG(err == VG_LITE_SUCCESS, "Blit rectangle failed."); + + vglite_run(); + + /* Disable scissor. */ + vglite_disable_scissor(); +} + +#endif /*LV_USE_DRAW_VGLITE*/ diff --git a/src/draw/nxp/vglite/lv_draw_vglite.c b/src/draw/nxp/vglite/lv_draw_vglite.c new file mode 100644 index 000000000..405457242 --- /dev/null +++ b/src/draw/nxp/vglite/lv_draw_vglite.c @@ -0,0 +1,439 @@ +/** + * @file lv_draw_vglite.c + * + */ + +/** + * Copyright 2023 NXP + * + * SPDX-License-Identifier: MIT + */ + + +/********************* + * INCLUDES + *********************/ + +#include "lv_draw_vglite.h" + +#if LV_USE_DRAW_VGLITE +#include "lv_vglite_buf.h" +#include "lv_vglite_utils.h" + +#include "../../../display/lv_display_private.h" + +/********************* + * DEFINES + *********************/ + +#define DRAW_UNIT_ID_VGLITE 2 + +#if LV_USE_OS + #define VGLITE_TASK_BUF_SIZE 10 +#endif + +/********************** + * TYPEDEFS + **********************/ + +#if LV_USE_OS +/** + * Structure of pending vglite draw task + */ +typedef struct _vglite_draw_task_t { + lv_draw_task_t * task; + bool flushed; +} vglite_draw_tasks_t; +#endif + +/********************** + * STATIC PROTOTYPES + **********************/ + +/* + * Dispatch a task to the VGLite unit. + * Return 1 if task was dispatched, 0 otherwise (task not supported). + */ +static int32_t _vglite_dispatch(lv_draw_unit_t * draw_unit, lv_layer_t * layer); + +/* + * Evaluate a task and set the score and preferred VGLite unit. + * Return 1 if task is preferred, 0 otherwise (task is not supported). + */ +static int32_t _vglite_evaluate(lv_draw_unit_t * draw_unit, lv_draw_task_t * task); + +#if LV_USE_OS + static void _vglite_render_thread_cb(void * ptr); +#endif + +static void _vglite_execute_drawing(lv_draw_vglite_unit_t * u); + +/********************** + * STATIC VARIABLES + **********************/ + +#if LV_USE_OS + /* + * Circular buffer to hold the queued and the flushed tasks. + * Two indexes, _head and _tail, are used to signal the beginning + * and the end of the valid tasks that are pending. + */ + static vglite_draw_tasks_t _draw_task_buf[VGLITE_TASK_BUF_SIZE]; + static volatile int _head = 0; + static volatile int _tail = 0; +#endif + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +void lv_draw_vglite_init(void) +{ + lv_draw_buf_vglite_init_handlers(); + + lv_draw_vglite_unit_t * draw_vglite_unit = lv_draw_create_unit(sizeof(lv_draw_vglite_unit_t)); + draw_vglite_unit->base_unit.dispatch_cb = _vglite_dispatch; + draw_vglite_unit->base_unit.evaluate_cb = _vglite_evaluate; + +#if LV_USE_OS + lv_thread_init(&draw_vglite_unit->thread, LV_THREAD_PRIO_HIGH, _vglite_render_thread_cb, 8 * 1024, draw_vglite_unit); +#endif +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +static inline bool _vglite_cf_supported(lv_color_format_t cf) +{ + // Add here the platform specific code for supported formats. + + bool is_cf_unsupported = (cf == LV_COLOR_FORMAT_RGB565A8 || cf == LV_COLOR_FORMAT_RGB888); + + return (!is_cf_unsupported); +} + +static int32_t _vglite_evaluate(lv_draw_unit_t * u, lv_draw_task_t * t) +{ + LV_UNUSED(u); + + switch(t->type) { + case LV_DRAW_TASK_TYPE_FILL: + if(t->preference_score > 80) { + t->preference_score = 80; + t->preferred_draw_unit_id = DRAW_UNIT_ID_VGLITE; + } + return 1; + + case LV_DRAW_TASK_TYPE_LINE: + case LV_DRAW_TASK_TYPE_ARC: + if(t->preference_score > 90) { + t->preference_score = 90; + t->preferred_draw_unit_id = DRAW_UNIT_ID_VGLITE; + } + return 1; + + case LV_DRAW_TASK_TYPE_LABEL: + if(t->preference_score > 95) { + t->preference_score = 95; + t->preferred_draw_unit_id = DRAW_UNIT_ID_VGLITE; + } + return 1; + + case LV_DRAW_TASK_TYPE_BORDER: { + const lv_draw_border_dsc_t * draw_dsc = (lv_draw_border_dsc_t *) t->draw_dsc; + + if(draw_dsc->side != (lv_border_side_t)LV_BORDER_SIDE_FULL) + return 0; + + if(t->preference_score > 90) { + t->preference_score = 90; + t->preferred_draw_unit_id = DRAW_UNIT_ID_VGLITE; + } + return 1; + } + + case LV_DRAW_TASK_TYPE_BG_IMG: { + const lv_draw_bg_image_dsc_t * draw_dsc = (lv_draw_bg_image_dsc_t *) t->draw_dsc; + lv_image_src_t src_type = lv_image_src_get_type(draw_dsc->src); + + if(src_type != LV_IMAGE_SRC_SYMBOL) { + bool has_recolor = (draw_dsc->recolor_opa != LV_OPA_TRANSP); + + if(has_recolor + || (!_vglite_cf_supported(draw_dsc->img_header.cf)) + || (!vglite_buf_aligned(draw_dsc->src, draw_dsc->img_header.stride, draw_dsc->img_header.cf)) + ) + return 0; + } + + if(t->preference_score > 80) { + t->preference_score = 80; + t->preferred_draw_unit_id = DRAW_UNIT_ID_VGLITE; + } + return 1; + } + + case LV_DRAW_TASK_TYPE_LAYER: { + const lv_draw_image_dsc_t * draw_dsc = (lv_draw_image_dsc_t *) t->draw_dsc; + lv_layer_t * layer_to_draw = (lv_layer_t *)draw_dsc->src; + lv_draw_buf_t * draw_buf = &layer_to_draw->draw_buf; + + bool has_recolor = (draw_dsc->recolor_opa != LV_OPA_TRANSP); + + if(has_recolor + || (!_vglite_cf_supported(draw_buf->color_format)) + ) + return 0; + + if(t->preference_score > 80) { + t->preference_score = 80; + t->preferred_draw_unit_id = DRAW_UNIT_ID_VGLITE; + } + return 1; + } + + case LV_DRAW_TASK_TYPE_IMAGE: { + lv_draw_image_dsc_t * draw_dsc = (lv_draw_image_dsc_t *) t->draw_dsc; + const lv_image_dsc_t * img_dsc = draw_dsc->src; + + bool has_recolor = (draw_dsc->recolor_opa != LV_OPA_TRANSP); +#if VGLITE_BLIT_SPLIT_ENABLED + bool has_transform = (draw_dsc->angle != 0 || draw_dsc->zoom != LV_ZOOM_NONE); +#endif + + if(has_recolor +#if VGLITE_BLIT_SPLIT_ENABLED + || has_transform +#endif + || (!_vglite_cf_supported(img_dsc->header.cf)) + || (!vglite_buf_aligned(img_dsc->data, img_dsc->header.stride, img_dsc->header.cf)) + ) + return 0; + + if(t->preference_score > 80) { + t->preference_score = 80; + t->preferred_draw_unit_id = DRAW_UNIT_ID_VGLITE; + } + return 1; + } + default: + return 0; + } + + return 0; +} + +static int32_t _vglite_dispatch(lv_draw_unit_t * draw_unit, lv_layer_t * layer) +{ + lv_draw_vglite_unit_t * draw_vglite_unit = (lv_draw_vglite_unit_t *) draw_unit; + + /* Return immediately if it's busy with draw task. */ + if(draw_vglite_unit->task_act) + return 0; + + /* Return if target buffer format is not supported. + * + * FIXME: Source format and destination format support is different! + */ + if(!_vglite_cf_supported(layer->draw_buf.color_format)) + return 0; + + /* Try to get an ready to draw. */ + lv_draw_task_t * t = lv_draw_get_next_available_task(layer, NULL, DRAW_UNIT_ID_VGLITE); + + /* Return 0 is no selection, some tasks can be supported by other units. */ + if(t == NULL || t->preferred_draw_unit_id != DRAW_UNIT_ID_VGLITE) + return 0; + + void * buf = lv_draw_layer_alloc_buf(layer); + if(buf == NULL) + return -1; + + t->state = LV_DRAW_TASK_STATE_IN_PROGRESS; + draw_vglite_unit->base_unit.target_layer = layer; + draw_vglite_unit->base_unit.clip_area = &t->clip_area; + draw_vglite_unit->task_act = t; + +#if LV_USE_OS + /* Let the render thread work. */ + lv_thread_sync_signal(&draw_vglite_unit->sync); +#else + _vglite_execute_drawing(draw_vglite_unit); + + draw_vglite_unit->task_act->state = LV_DRAW_TASK_STATE_READY; + draw_vglite_unit->task_act = NULL; + + /* The draw unit is free now. Request a new dispatching as it can get a new task. */ + lv_draw_dispatch_request(); +#endif + + return 1; +} + +static void _vglite_execute_drawing(lv_draw_vglite_unit_t * u) +{ + lv_draw_task_t * t = u->task_act; + lv_draw_unit_t * draw_unit = (lv_draw_unit_t *)u; + + /* Set target buffer */ + lv_layer_t * layer = draw_unit->target_layer; + vglite_set_dest_buf(&layer->draw_buf); + + /* Invalidate cache */ + lv_draw_buf_invalidate_cache(&layer->draw_buf, (const char *)&t->area); + + switch(t->type) { + case LV_DRAW_TASK_TYPE_LABEL: + lv_draw_vglite_label(draw_unit, t->draw_dsc, &t->area); + break; + case LV_DRAW_TASK_TYPE_FILL: + lv_draw_vglite_fill(draw_unit, t->draw_dsc, &t->area); + break; + case LV_DRAW_TASK_TYPE_BORDER: + lv_draw_vglite_border(draw_unit, t->draw_dsc, &t->area); + break; + case LV_DRAW_TASK_TYPE_BG_IMG: + lv_draw_vglite_bg_img(draw_unit, t->draw_dsc, &t->area); + break; + case LV_DRAW_TASK_TYPE_IMAGE: + lv_draw_vglite_img(draw_unit, t->draw_dsc, &t->area); + break; + case LV_DRAW_TASK_TYPE_ARC: + lv_draw_vglite_arc(draw_unit, t->draw_dsc, &t->area); + break; + case LV_DRAW_TASK_TYPE_LINE: + lv_draw_vglite_line(draw_unit, t->draw_dsc); + break; + case LV_DRAW_TASK_TYPE_LAYER: + lv_draw_vglite_layer(draw_unit, t->draw_dsc, &t->area); + break; + default: + break; + } + +#if LV_USE_PARALLEL_DRAW_DEBUG + /* Layers manage it for themselves. */ + if(t->type != LV_DRAW_TASK_TYPE_LAYER) { + lv_area_t draw_area; + if(!_lv_area_intersect(&draw_area, &t->area, u->base_unit.clip_area)) + return; + + int32_t idx = 0; + lv_disp_t * disp = _lv_refr_get_disp_refreshing(); + lv_draw_unit_t * draw_unit_tmp = disp->draw_unit_head; + while(draw_unit_tmp != (lv_draw_unit_t *)u) { + draw_unit_tmp = draw_unit_tmp->next; + idx++; + } + lv_draw_rect_dsc_t rect_dsc; + lv_draw_rect_dsc_init(&rect_dsc); + rect_dsc.bg_color = lv_palette_main(idx % _LV_PALETTE_LAST); + rect_dsc.border_color = rect_dsc.bg_color; + rect_dsc.bg_opa = LV_OPA_10; + rect_dsc.border_opa = LV_OPA_80; + rect_dsc.border_width = 1; + lv_draw_vglite_rect((lv_draw_unit_t *)u, &rect_dsc, &draw_area); + + lv_point_t txt_size; + lv_txt_get_size(&txt_size, "W", LV_FONT_DEFAULT, 0, 0, 100, LV_TEXT_FLAG_NONE); + + lv_area_t txt_area; + txt_area.x1 = draw_area.x1; + txt_area.y1 = draw_area.y1; + txt_area.x2 = draw_area.x1 + txt_size.x - 1; + txt_area.y2 = draw_area.y1 + txt_size.y - 1; + + lv_draw_rect_dsc_init(&rect_dsc); + rect_dsc.bg_color = lv_color_white(); + lv_draw_vglite_rect((lv_draw_unit_t *)u, &rect_dsc, &txt_area); + + char buf[8]; + lv_snprintf(buf, sizeof(buf), "%d", idx); + lv_draw_label_dsc_t label_dsc; + lv_draw_label_dsc_init(&label_dsc); + label_dsc.color = lv_color_black(); + label_dsc.text = buf; + lv_draw_vglite_label((lv_draw_unit_t *)u, &label_dsc, &txt_area); + } +#endif +} + +#if LV_USE_OS +static inline void _vglite_queue_task(lv_draw_task_t * task_act) +{ + _draw_task_buf[_tail].task = task_act; + _draw_task_buf[_tail].flushed = false; + _tail = (_tail + 1) % VGLITE_TASK_BUF_SIZE; +} + +static inline void _vglite_signal_task_ready(lv_draw_task_t * task_act) +{ + if(vglite_cmd_buf_is_flushed()) { + int end = (_head < _tail) ? _tail : _tail + VGLITE_TASK_BUF_SIZE; + + for(int i = _head; i < end; i++) { + /* Previous flushed tasks are ready now. */ + if(_draw_task_buf[i % VGLITE_TASK_BUF_SIZE].flushed) { + lv_draw_task_t * task = _draw_task_buf[i % VGLITE_TASK_BUF_SIZE].task; + + /* Signal the ready state to dispatcher. */ + task->state = LV_DRAW_TASK_STATE_READY; + _head = (_head + 1) % VGLITE_TASK_BUF_SIZE; + /* No need to cleanup the tasks in buffer as we advance with the _head. */ + } + else { + /* Those tasks have been flushed now. */ + _draw_task_buf[i % VGLITE_TASK_BUF_SIZE].flushed = true; + } + } + } + + if(task_act) + LV_ASSERT_MSG(_tail != _head, "VGLite task buffer full."); +} + +static void _vglite_render_thread_cb(void * ptr) +{ + lv_draw_vglite_unit_t * u = ptr; + + lv_thread_sync_init(&u->sync); + + while(1) { + /* + * Wait for sync if no task received or _draw_task_buf is empty. + * The thread will have to run as much as there are pending tasks. + */ + while(u->task_act == NULL && _head == _tail) { + lv_thread_sync_wait(&u->sync); + } + + if(u->task_act) { + _vglite_queue_task((void *)u->task_act); + + _vglite_execute_drawing(u); + } + else { + /* + * Update the flush status for last pending tasks. + * vg_lite_flush() will early return if there is nothing to submit. + */ + vglite_run(); + } + + _vglite_signal_task_ready((void *)u->task_act); + + /* Cleanup. */ + u->task_act = NULL; + + /* The draw unit is free now. Request a new dispatching as it can get a new task. */ + lv_draw_dispatch_request(); + } +} +#endif + +#endif /*LV_USE_DRAW_VGLITE*/ diff --git a/src/draw/nxp/vglite/lv_draw_vglite.h b/src/draw/nxp/vglite/lv_draw_vglite.h new file mode 100644 index 000000000..c357f982a --- /dev/null +++ b/src/draw/nxp/vglite/lv_draw_vglite.h @@ -0,0 +1,101 @@ +/** + * @file lv_draw_vglite.h + * + */ + +/** + * Copyright 2023 NXP + * + * SPDX-License-Identifier: MIT + */ + + +#ifndef LV_DRAW_VGLITE_H +#define LV_DRAW_VGLITE_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ + +#include "../../../lv_conf_internal.h" + +#if LV_USE_DRAW_VGLITE +#include "../../sw/lv_draw_sw.h" + +/********************* + * 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 VGLITE_BLIT_SPLIT_ENABLED 1 +#else +#define VGLITE_BLIT_SPLIT_ENABLED 0 +#endif + +/********************** + * TYPEDEFS + **********************/ + +typedef struct { + lv_draw_unit_t base_unit; + struct _lv_draw_task_t * task_act; +#if LV_USE_OS + lv_thread_sync_t sync; + lv_thread_t thread; +#endif +} lv_draw_vglite_unit_t; + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +void lv_draw_buf_vglite_init_handlers(void); + +void lv_draw_vglite_init(void); + +void lv_draw_vglite_arc(lv_draw_unit_t * draw_unit, const lv_draw_arc_dsc_t * dsc, + const lv_area_t * coords); + +void lv_draw_vglite_bg_img(lv_draw_unit_t * draw_unit, const lv_draw_bg_image_dsc_t * dsc, + const lv_area_t * coords); + +void lv_draw_vglite_border(lv_draw_unit_t * draw_unit, const lv_draw_border_dsc_t * dsc, + const lv_area_t * coords); + +void lv_draw_vglite_fill(lv_draw_unit_t * draw_unit, const lv_draw_fill_dsc_t * dsc, + const lv_area_t * coords); + +void lv_draw_vglite_img(lv_draw_unit_t * draw_unit, const lv_draw_image_dsc_t * dsc, + const lv_area_t * coords); + +void lv_draw_vglite_label(lv_draw_unit_t * draw_unit, const lv_draw_label_dsc_t * dsc, + const lv_area_t * coords); + +void lv_draw_vglite_layer(lv_draw_unit_t * draw_unit, const lv_draw_image_dsc_t * draw_dsc, + const lv_area_t * coords); + +void lv_draw_vglite_line(lv_draw_unit_t * draw_unit, const lv_draw_line_dsc_t * dsc); + +/********************** + * MACROS + **********************/ +#endif /*LV_USE_DRAW_VGLITE*/ + +#ifdef __cplusplus +} /*extern "C"*/ +#endif + +#endif /*LV_DRAW_VGLITE_H*/ diff --git a/src/draw/nxp/vglite/lv_draw_vglite_arc.c b/src/draw/nxp/vglite/lv_draw_vglite_arc.c new file mode 100644 index 000000000..12e09c2d7 --- /dev/null +++ b/src/draw/nxp/vglite/lv_draw_vglite_arc.c @@ -0,0 +1,691 @@ +/** + * @file lv_draw_vglite_arc.c + * + */ + +/** + * Copyright 2021-2023 NXP + * + * SPDX-License-Identifier: MIT + */ + + +/********************* + * INCLUDES + *********************/ + +#include "lv_draw_vglite.h" + +#if LV_USE_DRAW_VGLITE +#include "lv_vglite_buf.h" +#include "lv_vglite_path.h" +#include "lv_vglite_utils.h" + +#include "../../../stdlib/lv_string.h" +#include + +/********************* + * 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 + **********************/ + +/** + * Draw arc shape with effects + * + * @param[in] center Arc center with relative coordinates + * @param[in] clip_area Clipping area with relative coordinates to dest buff + * @param[in] dsc Arc description structure (width, rounded ending, opacity) + * + */ +static void _vglite_draw_arc(const lv_point_t * center, const lv_area_t * clip_area, + const lv_draw_arc_dsc_t * dsc); + +/********************** + * STATIC VARIABLES + **********************/ + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +void lv_draw_vglite_arc(lv_draw_unit_t * draw_unit, const lv_draw_arc_dsc_t * dsc, + const lv_area_t * coords) +{ + LV_UNUSED(coords); + + if(dsc->opa <= (lv_opa_t)LV_OPA_MIN) + return; + if(dsc->width == 0) + return; + if(dsc->start_angle == dsc->end_angle) + return; + + lv_layer_t * layer = draw_unit->target_layer; + lv_point_t rel_center = {dsc->center.x - layer->draw_buf_ofs.x, dsc->center.y - layer->draw_buf_ofs.y}; + + lv_area_t rel_clip_area; + lv_area_copy(&rel_clip_area, draw_unit->clip_area); + lv_area_move(&rel_clip_area, -layer->draw_buf_ofs.x, -layer->draw_buf_ofs.y); + + _vglite_draw_arc(&rel_center, &rel_clip_area, dsc); +} + +/********************** + * 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_ASSERT_MSG(false, "Invalid arc quarter."); + 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 = {0}; + 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 = {0}; + 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, const 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, const 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); + } + } +} + +static void _vglite_draw_arc(const lv_point_t * center, const lv_area_t * clip_area, + const lv_draw_arc_dsc_t * dsc) +{ + vg_lite_error_t err = VG_LITE_SUCCESS; + vg_lite_path_t path; + uint16_t start_angle = dsc->start_angle; + uint16_t end_angle = dsc->end_angle; + bool donut = ((end_angle - start_angle) % 360 == 0) ? true : false; + vg_lite_buffer_t * vgbuf = vglite_get_dest_buf(); + + /* path: max size = 16 cubic bezier (7 words each) */ + int32_t arc_path[16 * 7]; + lv_memset(arc_path, 0, sizeof(arc_path)); + + /*** Init path ***/ + lv_coord_t width = dsc->width; /* inner arc radius = outer arc radius - width */ + uint16_t radius = dsc->radius; + + if(width > 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++] = center->x + cp_x; + arc_path[pidx++] = center->y + cp_y; + + /* draw 1-5 outer quarters */ + _add_arc_path(arc_path, &pidx, radius, start_angle, end_angle, 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++] = center->x + cp_x; + arc_path[pidx++] = 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++] = center->x + cp_x; + arc_path[pidx++] = 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 = {center->x + cp_x, 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++] = center->x + cp_x; + arc_path[pidx++] = center->y + cp_y; + } + + /* draw 1-5 inner quarters */ + _add_arc_path(arc_path, &pidx, radius - width, start_angle, end_angle, 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++] = center->x + cp_x; + arc_path[pidx++] = 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 = {center->x + cp_x, 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++] = center->x + cp_x; + arc_path[pidx++] = 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)clip_area->x1, (vg_lite_float_t)clip_area->y1, + ((vg_lite_float_t)clip_area->x2) + 1.0f, ((vg_lite_float_t)clip_area->y2) + 1.0f); + LV_ASSERT_MSG(err == VG_LITE_SUCCESS, "Init path failed."); + + lv_color32_t col32 = lv_color_to_32(dsc->color, dsc->opa); + vg_lite_color_t vgcol = vglite_get_color(col32, false); + + vg_lite_matrix_t matrix; + vg_lite_identity(&matrix); + + vglite_set_scissor(clip_area); + + /*** Draw arc ***/ + err = vg_lite_draw(vgbuf, &path, VG_LITE_FILL_NON_ZERO, &matrix, VG_LITE_BLEND_SRC_OVER, vgcol); + LV_ASSERT_MSG(err == VG_LITE_SUCCESS, "Draw arc failed."); + + vglite_run(); + + vglite_disable_scissor(); + + err = vg_lite_clear_path(&path); + LV_ASSERT_MSG(err == VG_LITE_SUCCESS, "Clear path failed."); +} + +#endif /*LV_USE_DRAW_VGLITE*/ diff --git a/src/draw/nxp/vglite/lv_draw_vglite_bg_img.c b/src/draw/nxp/vglite/lv_draw_vglite_bg_img.c new file mode 100644 index 000000000..995331d53 --- /dev/null +++ b/src/draw/nxp/vglite/lv_draw_vglite_bg_img.c @@ -0,0 +1,114 @@ +/** + * @file lv_draw_vglite_bg_img.c + * + */ + +/** + * Copyright 2022, 2023 NXP + * + * SPDX-License-Identifier: MIT + */ + + +/********************* + * INCLUDES + *********************/ + +#include "lv_draw_vglite.h" + +#if LV_USE_DRAW_VGLITE + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ + +/********************** + * STATIC VARIABLES + **********************/ + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +void lv_draw_vglite_bg_img(lv_draw_unit_t * draw_unit, const lv_draw_bg_image_dsc_t * dsc, + const lv_area_t * coords) +{ + if(dsc->src == NULL) return; + if(dsc->opa <= LV_OPA_MIN) return; + + lv_area_t clip_area; + if(!_lv_area_intersect(&clip_area, coords, draw_unit->clip_area)) { + return; + } + + const lv_area_t * clip_area_ori = draw_unit->clip_area; + draw_unit->clip_area = &clip_area; + + lv_image_src_t src_type = lv_image_src_get_type(dsc->src); + if(src_type == LV_IMAGE_SRC_SYMBOL) { + lv_point_t size; + lv_text_get_size(&size, dsc->src, dsc->font, 0, 0, LV_COORD_MAX, LV_TEXT_FLAG_NONE); + lv_area_t a; + a.x1 = coords->x1 + lv_area_get_width(coords) / 2 - size.x / 2; + a.x2 = a.x1 + size.x - 1; + a.y1 = coords->y1 + lv_area_get_height(coords) / 2 - size.y / 2; + a.y2 = a.y1 + size.y - 1; + + lv_draw_label_dsc_t label_draw_dsc; + lv_draw_label_dsc_init(&label_draw_dsc); + label_draw_dsc.font = dsc->font; + label_draw_dsc.color = dsc->recolor; + label_draw_dsc.opa = dsc->opa; + label_draw_dsc.text = dsc->src; + lv_draw_vglite_label(draw_unit, &label_draw_dsc, &a); + } + else { + lv_draw_image_dsc_t img_dsc; + lv_draw_image_dsc_init(&img_dsc); + img_dsc.recolor = dsc->recolor; + img_dsc.recolor_opa = dsc->recolor_opa; + img_dsc.opa = dsc->opa; + img_dsc.src = dsc->src; + + /*Center align*/ + if(dsc->tiled == false) { + lv_area_t area; + area.x1 = coords->x1 + lv_area_get_width(coords) / 2 - dsc->img_header.w / 2; + area.y1 = coords->y1 + lv_area_get_height(coords) / 2 - dsc->img_header.h / 2; + area.x2 = area.x1 + dsc->img_header.w - 1; + area.y2 = area.y1 + dsc->img_header.h - 1; + + lv_draw_vglite_img(draw_unit, &img_dsc, &area); + } + else { + lv_area_t area; + area.y1 = coords->y1; + area.y2 = area.y1 + dsc->img_header.h - 1; + + for(; area.y1 <= coords->y2; area.y1 += dsc->img_header.h, area.y2 += dsc->img_header.h) { + + area.x1 = coords->x1; + area.x2 = area.x1 + dsc->img_header.w - 1; + for(; area.x1 <= coords->x2; area.x1 += dsc->img_header.w, area.x2 += dsc->img_header.w) { + lv_draw_vglite_img(draw_unit, &img_dsc, &area); + } + } + } + } + + draw_unit->clip_area = clip_area_ori; +} + +#endif /*LV_USE_DRAW_VGLITE*/ diff --git a/src/draw/nxp/vglite/lv_draw_vglite_border.c b/src/draw/nxp/vglite/lv_draw_vglite_border.c new file mode 100644 index 000000000..fd014007f --- /dev/null +++ b/src/draw/nxp/vglite/lv_draw_vglite_border.c @@ -0,0 +1,175 @@ +/** + * @file lv_draw_vglite_border.c + * + */ + +/** + * Copyright 2022, 2023 NXP + * + * SPDX-License-Identifier: MIT + */ + +/********************* + * INCLUDES + *********************/ + +#include "lv_draw_vglite.h" + +#if LV_USE_DRAW_VGLITE +#include "lv_vglite_buf.h" +#include "lv_vglite_path.h" +#include "lv_vglite_utils.h" + +#include + +/********************* + * DEFINES + *********************/ + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ + +/** + * Draw rectangle border/outline shape with effects (rounded corners, opacity) + * + * @param[in] coords Coordinates of the rectangle border/outline (relative to dest buff) + * @param[in] clip_area Clipping area with relative coordinates to dest buff + * @param[in] dsc Description of the rectangle border/outline + * + */ +static void _vglite_draw_border(const lv_area_t * coords, const lv_area_t * clip_area, + const lv_draw_border_dsc_t * dsc); + +/********************** + * STATIC VARIABLES + **********************/ + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +void lv_draw_vglite_border(lv_draw_unit_t * draw_unit, const lv_draw_border_dsc_t * dsc, + const lv_area_t * coords) +{ + if(dsc->opa <= (lv_opa_t)LV_OPA_MIN) + return; + if(dsc->width == 0) + return; + if(dsc->side == (lv_border_side_t)LV_BORDER_SIDE_NONE) + return; + + lv_layer_t * layer = draw_unit->target_layer; + lv_area_t rel_coords; + lv_coord_t width = dsc->width; + + /* Move border inwards to align with software rendered border */ + rel_coords.x1 = coords->x1 + ceil(width / 2.0f); + rel_coords.x2 = coords->x2 - floor(width / 2.0f); + rel_coords.y1 = coords->y1 + ceil(width / 2.0f); + rel_coords.y2 = coords->y2 - floor(width / 2.0f); + + /* Move outline outwards to align with software rendered outline */ + //lv_coord_t outline_pad = dsc->outline_pad - 1; + //rel_coords.x1 = coords->x1 - outline_pad - floor(dsc->outline_width / 2.0f); + //rel_coords.x2 = coords->x2 + outline_pad + ceil(dsc->outline_width / 2.0f); + //rel_coords.y1 = coords->y1 - outline_pad - floor(dsc->outline_width / 2.0f); + //rel_coords.y2 = coords->y2 + outline_pad + ceil(dsc->outline_width / 2.0f); + + lv_area_move(&rel_coords, -layer->draw_buf_ofs.x, -layer->draw_buf_ofs.y); + + lv_area_t rel_clip_area; + lv_area_copy(&rel_clip_area, draw_unit->clip_area); + lv_area_move(&rel_clip_area, -layer->draw_buf_ofs.x, -layer->draw_buf_ofs.y); + + lv_area_t clipped_coords; + if(!_lv_area_intersect(&clipped_coords, &rel_coords, &rel_clip_area)) + return; /*Fully clipped, nothing to do*/ + + _vglite_draw_border(&rel_coords, &rel_clip_area, dsc); +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +static void _vglite_draw_border(const lv_area_t * coords, const lv_area_t * clip_area, + const lv_draw_border_dsc_t * dsc) +{ + vg_lite_error_t err = VG_LITE_SUCCESS; + lv_coord_t radius = dsc->radius; + vg_lite_buffer_t * vgbuf = vglite_get_dest_buf(); + + if(radius < 0) + return; + + lv_coord_t border_half = (lv_coord_t)floor(dsc->width / 2.0f); + if(radius > border_half) + radius = radius - border_half; + + //else { + // /* Draw outline - always has radius, leave the same radius in the circle case */ + // lv_coord_t outline_half = (lv_coord_t)ceil(dsc->outline_width / 2.0f); + // if(radius < (lv_coord_t)LV_RADIUS_CIRCLE - outline_half) + // radius = radius + outline_half; + //} + + vg_lite_cap_style_t cap_style = (radius) ? VG_LITE_CAP_ROUND : VG_LITE_CAP_BUTT; + vg_lite_join_style_t join_style = (radius) ? VG_LITE_JOIN_ROUND : VG_LITE_JOIN_MITER; + + /*** Init path ***/ + int32_t path_data[RECT_PATH_DATA_MAX_SIZE]; + uint32_t path_data_size; + vglite_create_rect_path_data(path_data, &path_data_size, radius, coords); + vg_lite_quality_t path_quality = radius > 0 ? VG_LITE_HIGH : VG_LITE_MEDIUM; + + vg_lite_path_t path; + err = vg_lite_init_path(&path, VG_LITE_S32, path_quality, path_data_size, path_data, + (vg_lite_float_t)clip_area->x1, (vg_lite_float_t)clip_area->y1, + ((vg_lite_float_t)clip_area->x2) + 1.0f, ((vg_lite_float_t)clip_area->y2) + 1.0f); + LV_ASSERT_MSG(err == VG_LITE_SUCCESS, "Init path failed."); + + lv_color32_t col32 = lv_color_to_32(dsc->color, dsc->opa); + vg_lite_color_t vgcol = vglite_get_color(col32, false); + + vg_lite_matrix_t matrix; + vg_lite_identity(&matrix); + + lv_coord_t line_width = dsc->width; + + /*** Draw border ***/ + err = vg_lite_set_draw_path_type(&path, VG_LITE_DRAW_STROKE_PATH); + LV_ASSERT_MSG(err == VG_LITE_SUCCESS, "Set draw path type failed."); + + err = vg_lite_set_stroke(&path, cap_style, join_style, line_width, 8, NULL, 0, 0, vgcol); + LV_ASSERT_MSG(err == VG_LITE_SUCCESS, "Set stroke failed."); + + err = vg_lite_update_stroke(&path); + LV_ASSERT_MSG(err == VG_LITE_SUCCESS, "Update stroke failed."); + + vglite_set_scissor(clip_area); + + err = vg_lite_draw(vgbuf, &path, VG_LITE_FILL_NON_ZERO, &matrix, VG_LITE_BLEND_SRC_OVER, vgcol); + LV_ASSERT_MSG(err == VG_LITE_SUCCESS, "Draw border failed."); + + vglite_run(); + + vglite_disable_scissor(); + + err = vg_lite_clear_path(&path); + LV_ASSERT_MSG(err == VG_LITE_SUCCESS, "Clear path failed."); +} + +#endif /*LV_USE_DRAW_VGLITE*/ diff --git a/src/draw/nxp/vglite/lv_draw_vglite_fill.c b/src/draw/nxp/vglite/lv_draw_vglite_fill.c new file mode 100644 index 000000000..df943891e --- /dev/null +++ b/src/draw/nxp/vglite/lv_draw_vglite_fill.c @@ -0,0 +1,259 @@ +/** + * @file lv_draw_vglite_fill.c + * + */ + +/** + * Copyright 2020-2023 NXP + * + * SPDX-License-Identifier: MIT + */ + + +/********************* + * INCLUDES + *********************/ + +#include "lv_draw_vglite.h" + +#if LV_USE_DRAW_VGLITE +#include "lv_vglite_buf.h" +#include "lv_vglite_path.h" +#include "lv_vglite_utils.h" + +#include "../../../stdlib/lv_string.h" + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ + +/** + * Fill area, with optional opacity. + * + * @param[in] dest_area Area with relative coordinates of destination buffer + * @param[in] dsc Description of the area to fill (color, opa) + * + */ +static void _vglite_fill(const lv_area_t * dest_area, const lv_draw_fill_dsc_t * dsc); + +/** + * Draw rectangle background with effects (rounded corners, gradient) + * + * @param[in] coords Coordinates of the rectangle background (relative to dest buff) + * @param[in] clip_area Clipping area with relative coordinates to dest buff + * @param[in] dsc Description of the rectangle background + * + */ +static void _vglite_draw_rect(const lv_area_t * coords, const lv_area_t * clip_area, + const lv_draw_fill_dsc_t * dsc); + +/********************** + * STATIC VARIABLES + **********************/ + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +void lv_draw_vglite_fill(lv_draw_unit_t * draw_unit, const lv_draw_fill_dsc_t * dsc, + const lv_area_t * coords) +{ + if(dsc->opa <= (lv_opa_t)LV_OPA_MIN) + return; + + lv_layer_t * layer = draw_unit->target_layer; + lv_area_t rel_coords; + lv_area_copy(&rel_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) { + // rel_coords.x1 += (dsc->border_side & LV_BORDER_SIDE_LEFT) ? 1 : 0; + // rel_coords.y1 += (dsc->border_side & LV_BORDER_SIDE_TOP) ? 1 : 0; + // rel_coords.x2 -= (dsc->border_side & LV_BORDER_SIDE_RIGHT) ? 1 : 0; + // rel_coords.y2 -= (dsc->border_side & LV_BORDER_SIDE_BOTTOM) ? 1 : 0; + //} + lv_area_move(&rel_coords, -layer->draw_buf_ofs.x, -layer->draw_buf_ofs.y); + + lv_area_t rel_clip_area; + lv_area_copy(&rel_clip_area, draw_unit->clip_area); + lv_area_move(&rel_clip_area, -layer->draw_buf_ofs.x, -layer->draw_buf_ofs.y); + + lv_area_t clipped_coords; + if(!_lv_area_intersect(&clipped_coords, &rel_coords, &rel_clip_area)) + return; /*Fully clipped, nothing to do*/ + + /* + * Most simple case: just a plain rectangle (no radius, no gradient) + */ + if((dsc->radius == 0) && (dsc->grad.dir == (lv_grad_dir_t)LV_GRAD_DIR_NONE)) + _vglite_fill(&clipped_coords, dsc); + else + _vglite_draw_rect(&rel_coords, &rel_clip_area, dsc); +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +static void _vglite_fill(const lv_area_t * dest_area, const lv_draw_fill_dsc_t * dsc) +{ + vg_lite_error_t err = VG_LITE_SUCCESS; + vg_lite_buffer_t * vgbuf = vglite_get_dest_buf(); + + lv_color32_t col32 = lv_color_to_32(dsc->color, dsc->opa); + vg_lite_color_t vgcol = vglite_get_color(col32, false); + + if(dsc->opa >= (lv_opa_t)LV_OPA_MAX) { /*Opaque fill*/ + vg_lite_rectangle_t rect = { + .x = dest_area->x1, + .y = dest_area->y1, + .width = lv_area_get_width(dest_area), + .height = lv_area_get_height(dest_area) + }; + + err = vg_lite_clear(vgbuf, &rect, vgcol); + LV_ASSERT_MSG(err == VG_LITE_SUCCESS, "Clear failed."); + + vglite_run(); + } + else { /*fill with transparency*/ + + vg_lite_path_t path; + int32_t path_data[] = { /*VG rectangular path*/ + VLC_OP_MOVE, dest_area->x1, dest_area->y1, + VLC_OP_LINE, dest_area->x2 + 1, dest_area->y1, + VLC_OP_LINE, dest_area->x2 + 1, dest_area->y2 + 1, + VLC_OP_LINE, dest_area->x1, dest_area->y2 + 1, + VLC_OP_LINE, dest_area->x1, dest_area->y1, + VLC_OP_END + }; + + err = vg_lite_init_path(&path, VG_LITE_S32, VG_LITE_MEDIUM, sizeof(path_data), path_data, + (vg_lite_float_t) dest_area->x1, (vg_lite_float_t) dest_area->y1, + ((vg_lite_float_t) dest_area->x2) + 1.0f, ((vg_lite_float_t) dest_area->y2) + 1.0f); + LV_ASSERT_MSG(err == VG_LITE_SUCCESS, "Init path failed."); + + 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); + LV_ASSERT_MSG(err == VG_LITE_SUCCESS, "Draw rectangle failed."); + + vglite_run(); + + err = vg_lite_clear_path(&path); + LV_ASSERT_MSG(err == VG_LITE_SUCCESS, "Clear path failed."); + } +} + +static void _vglite_draw_rect(const lv_area_t * coords, const lv_area_t * clip_area, + const lv_draw_fill_dsc_t * dsc) +{ + vg_lite_error_t err = VG_LITE_SUCCESS; + lv_coord_t width = lv_area_get_width(coords); + lv_coord_t height = lv_area_get_height(coords); + lv_coord_t radius = dsc->radius; + vg_lite_buffer_t * vgbuf = vglite_get_dest_buf(); + + if(dsc->radius < 0) + return; + + /*** Init path ***/ + int32_t path_data[RECT_PATH_DATA_MAX_SIZE]; + uint32_t path_data_size; + vglite_create_rect_path_data(path_data, &path_data_size, radius, coords); + vg_lite_quality_t path_quality = dsc->radius > 0 ? VG_LITE_HIGH : VG_LITE_MEDIUM; + + vg_lite_path_t path; + err = vg_lite_init_path(&path, VG_LITE_S32, path_quality, path_data_size, path_data, + (vg_lite_float_t)clip_area->x1, (vg_lite_float_t)clip_area->y1, + ((vg_lite_float_t)clip_area->x2) + 1.0f, ((vg_lite_float_t)clip_area->y2) + 1.0f); + LV_ASSERT_MSG(err == VG_LITE_SUCCESS, "Init path failed."); + + vg_lite_matrix_t matrix; + vg_lite_identity(&matrix); + + /*** Init Color ***/ + lv_color32_t col32 = lv_color_to_32(dsc->color, dsc->opa); + vg_lite_color_t vgcol = vglite_get_color(col32, false); + + vglite_set_scissor(clip_area); + + vg_lite_linear_gradient_t gradient; + bool has_gradient = (dsc->grad.dir != (lv_grad_dir_t)LV_GRAD_DIR_NONE); + + /*** Init Gradient ***/ + if(has_gradient) { + vg_lite_matrix_t * grad_matrix; + + uint32_t colors[2]; + uint32_t stops[2]; + lv_color32_t col32[2]; + + /* Gradient setup */ + uint8_t cnt = LV_MAX(dsc->grad.stops_count, 2); + for(uint8_t i = 0; i < cnt; i++) { + stops[i] = dsc->grad.stops[i].frac; + + col32[i] = lv_color_to_32(dsc->grad.stops[i].color, dsc->opa); + colors[i] = vglite_get_color(col32[i], true); + } + + lv_memset(&gradient, 0, sizeof(vg_lite_linear_gradient_t)); + + err = vg_lite_init_grad(&gradient); + LV_ASSERT_MSG(err == VG_LITE_SUCCESS, "Init gradient failed"); + + err = vg_lite_set_grad(&gradient, cnt, colors, stops); + LV_ASSERT_MSG(err == VG_LITE_SUCCESS, "Set gradient failed."); + + err = vg_lite_update_grad(&gradient); + LV_ASSERT_MSG(err == VG_LITE_SUCCESS, "Update gradient failed."); + + grad_matrix = vg_lite_get_grad_matrix(&gradient); + vg_lite_identity(grad_matrix); + vg_lite_translate((float)coords->x1, (float)coords->y1, grad_matrix); + + if(dsc->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); + } + + err = vg_lite_draw_gradient(vgbuf, &path, VG_LITE_FILL_EVEN_ODD, &matrix, &gradient, VG_LITE_BLEND_SRC_OVER); + LV_ASSERT_MSG(err == VG_LITE_SUCCESS, "Draw gradient failed."); + } + else { + err = vg_lite_draw(vgbuf, &path, VG_LITE_FILL_EVEN_ODD, &matrix, VG_LITE_BLEND_SRC_OVER, vgcol); + LV_ASSERT_MSG(err == VG_LITE_SUCCESS, "Draw rectangle failed."); + } + + vglite_run(); + + vglite_disable_scissor(); + + err = vg_lite_clear_path(&path); + LV_ASSERT_MSG(err == VG_LITE_SUCCESS, "Clear path failed."); + + if(has_gradient) { + err = vg_lite_clear_grad(&gradient); + LV_ASSERT_MSG(err == VG_LITE_SUCCESS, "Clear gradient failed."); + } +} + +#endif /*LV_USE_DRAW_VGLITE*/ diff --git a/src/draw/nxp/vglite/lv_draw_vglite_img.c b/src/draw/nxp/vglite/lv_draw_vglite_img.c new file mode 100644 index 000000000..229fa1ec7 --- /dev/null +++ b/src/draw/nxp/vglite/lv_draw_vglite_img.c @@ -0,0 +1,412 @@ +/** + * @file lv_draw_vglite_blend.c + * + */ + +/** + * Copyright 2020-2023 NXP + * + * SPDX-License-Identifier: MIT + */ + + +/********************* + * INCLUDES + *********************/ + +#include "lv_draw_vglite.h" + +#if LV_USE_DRAW_VGLITE +#include "lv_vglite_buf.h" +#include "lv_vglite_matrix.h" +#include "lv_vglite_utils.h" + +#include "../../../misc/lv_log.h" + +/********************* + * DEFINES + *********************/ + +#if VGLITE_BLIT_SPLIT_ENABLED +/** +* BLIT split threshold - BLITs with width or height higher than this value will +* be done in multiple steps. Value must be multiple of stride alignment in px. +* For most color formats the alignment is 16px (except the index formats). +*/ +#define VGLITE_BLIT_SPLIT_THR 352 + +/* Enable for logging debug traces. */ +#define VGLITE_LOG_TRACE 0 + +#if VGLITE_LOG_TRACE +#define VGLITE_TRACE(fmt, ...) \ + do { \ + LV_LOG(fmt, ##__VA_ARGS__); \ + } while (0) +#else +#define VGLITE_TRACE(fmt, ...) \ + do { \ + } while (0) +#endif +#endif + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ + +/** + * BLock Image Transfer - copy rectangular image from src_buf to dst_buf with effects. + * By default, image is copied directly, with optional opacity. + * + * @param[in] dest_area Destination area with relative coordinates to dest buffer + * @param[in] src_area Source area with relative coordinates to src buffer + * @param[in] opa Opacity + * + */ +static void _vglite_blit_single(const lv_area_t * dest_area, const lv_area_t * src_area, lv_opa_t opa); + +#if VGLITE_BLIT_SPLIT_ENABLED +/** + * Move buffer pointer as close as possible to area, but with respect to alignment requirements. + * + * @param[in] buf Buffer address pointer + * @param[in] area Area with relative coordinates to the buffer + * @param[in] stride Stride of buffer in bytes + * @param[in] cf Color format of buffer + */ +static void _move_buf_close_to_area(void ** buf, lv_area_t * area, uint32_t stride, lv_color_format_t cf); + +/** + * BLock Image Transfer - copy rectangular image from src_buf to dst_buf with effects. + * By default, image is copied directly, with optional opacity. + * + * @param dest_buf Destination buffer + * @param[in] dest_area Destination area with relative coordinates to dest buffer + * @param[in] dest_stride Stride of destination buffer in bytes + * @param[in] dest_cf Color format of destination buffer + * @param[in] src_buf Source buffer + * @param[in] src_area Source area with relative coordinates to src buffer + * @param[in] src_stride Stride of source buffer in bytes + * @param[in] src_cf Color format of source buffer + * @param[in] opa Opacity + * + */ +static void _vglite_blit_split(void * dest_buf, lv_area_t * dest_area, uint32_t dest_stride, lv_color_format_t dest_cf, + const void * src_buf, lv_area_t * src_area, uint32_t src_stride, lv_color_format_t src_cf, + lv_opa_t opa); +#else +/** + * BLock Image Transfer - copy rectangular image from src_buf to dst_buf with transformation. + * By default, image is copied directly, with optional opacity. + * + * @param[in] dest_area Area with relative coordinates to dest buffer + * @param[in] clip_area Clip area with relative coordinates to dest buffer + * @param[in] src_area Source area with relative coordinates to src buffer + * @param[in] dsc Image descriptor + * + */ +static void _vglite_blit_transform(const lv_area_t * dest_area, const lv_area_t * clip_area, + const lv_area_t * src_area, const lv_draw_image_dsc_t * dsc); +#endif /*VGLITE_BLIT_SPLIT_ENABLED*/ + +/********************** + * STATIC VARIABLES + **********************/ + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +void lv_draw_vglite_img(lv_draw_unit_t * draw_unit, const lv_draw_image_dsc_t * dsc, + const lv_area_t * coords) +{ + if(dsc->opa <= (lv_opa_t)LV_OPA_MIN) + return; + + lv_layer_t * layer = draw_unit->target_layer; + const lv_image_dsc_t * img_dsc = dsc->src; + + lv_area_t rel_coords; + lv_area_copy(&rel_coords, coords); + lv_area_move(&rel_coords, -layer->draw_buf_ofs.x, -layer->draw_buf_ofs.y); + + lv_area_t rel_clip_area; + lv_area_copy(&rel_clip_area, draw_unit->clip_area); + lv_area_move(&rel_clip_area, -layer->draw_buf_ofs.x, -layer->draw_buf_ofs.y); + + lv_area_t blend_area; + bool has_transform = dsc->rotation != 0 || dsc->zoom != LV_SCALE_NONE; + if(has_transform) + lv_area_copy(&blend_area, &rel_coords); + else if(!_lv_area_intersect(&blend_area, &rel_coords, &rel_clip_area)) + return; /*Fully clipped, nothing to do*/ + + const void * src_buf = img_dsc->data; + + lv_area_t src_area; + src_area.x1 = blend_area.x1 - (coords->x1 - layer->draw_buf_ofs.x); + src_area.y1 = blend_area.y1 - (coords->y1 - layer->draw_buf_ofs.y); + src_area.x2 = src_area.x1 + lv_area_get_width(coords) - 1; + src_area.y2 = src_area.y1 + lv_area_get_height(coords) - 1; + + lv_color_format_t src_cf = img_dsc->header.cf; + uint32_t src_stride = img_dsc->header.stride; + + /* Set src_vgbuf structure. */ + vglite_set_src_buf(src_buf, lv_area_get_width(&src_area), lv_area_get_height(&src_area), src_stride, src_cf); + +#if VGLITE_BLIT_SPLIT_ENABLED + void * dest_buf = lv_draw_buf_get_buf(&layer->draw_buf); + uint32_t dest_stride = lv_draw_buf_get_stride(&layer->draw_buf); + lv_color_format_t dest_cf = layer->draw_buf.color_format; + + if(!has_transform) + _vglite_blit_split(dest_buf, &blend_area, dest_stride, dest_cf, + src_buf, &src_area, src_stride, src_cf, dsc->opa); +#else + if(has_transform) + _vglite_blit_transform(&blend_area, &rel_clip_area, &src_area, dsc); + else + _vglite_blit_single(&blend_area, &src_area, dsc->opa); +#endif +} + +/********************** + * STATIC FUNCTIONS + **********************/ +static void _vglite_blit(const lv_area_t * src_area, lv_opa_t opa) +{ + vg_lite_error_t err = VG_LITE_SUCCESS; + vg_lite_buffer_t * dst_vgbuf = vglite_get_dest_buf(); + vg_lite_buffer_t * src_vgbuf = vglite_get_src_buf(); + + uint32_t rect[] = { + (uint32_t)src_area->x1, /* start x */ + (uint32_t)src_area->y1, /* start y */ + (uint32_t)lv_area_get_width(src_area), /* width */ + (uint32_t)lv_area_get_height(src_area) /* height */ + }; + + uint32_t color; + vg_lite_blend_t blend; + if(opa >= (lv_opa_t)LV_OPA_MAX) { + color = 0xFFFFFFFFU; + blend = VG_LITE_BLEND_SRC_OVER; + src_vgbuf->transparency_mode = VG_LITE_IMAGE_TRANSPARENT; + } + else { + 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; + } + + vg_lite_matrix_t * vgmatrix = vglite_get_matrix(); + + err = vg_lite_blit_rect(dst_vgbuf, src_vgbuf, rect, vgmatrix, blend, color, VG_LITE_FILTER_POINT); + LV_ASSERT_MSG(err == VG_LITE_SUCCESS, "Blit rectangle failed."); + + vglite_run(); +} + +static void _vglite_blit_single(const lv_area_t * dest_area, const lv_area_t * src_area, lv_opa_t opa) +{ + /* Set scissor. */ + vglite_set_scissor(dest_area); + + /* Set vgmatrix. */ + vglite_set_translation_matrix(dest_area); + + /* Start blit. */ + _vglite_blit(src_area, opa); + + /* Disable scissor. */ + vglite_disable_scissor(); +} + +#if VGLITE_BLIT_SPLIT_ENABLED +static void _move_buf_close_to_area(void ** buf, lv_area_t * area, uint32_t stride, lv_color_format_t cf) +{ + uint8_t ** buf_u8 = (uint8_t **)buf; + uint8_t align_bytes = vglite_get_alignment(cf); + uint8_t bits_per_pixel = vglite_get_px_size(cf); + + uint16_t align_pixels = align_bytes * 8 / bits_per_pixel; + + if(area->x1 >= (lv_coord_t)(area->x1 % align_pixels)) { + uint16_t shift_x = area->x1 - (area->x1 % align_pixels); + + area->x1 -= shift_x; + area->x2 -= shift_x; + *buf_u8 += (shift_x * bits_per_pixel) / 8; + } + + if(area->y1) { + uint16_t shift_y = area->y1; + + area->y1 -= shift_y; + area->y2 -= shift_y; + *buf_u8 += shift_y * stride; + } +} + +static void _vglite_blit_split(void * dest_buf, lv_area_t * dest_area, uint32_t dest_stride, lv_color_format_t dest_cf, + const void * src_buf, lv_area_t * src_area, uint32_t src_stride, lv_color_format_t src_cf, + lv_opa_t opa) +{ + VGLITE_TRACE("Blit " + "Area: ([%d,%d], [%d,%d]) -> ([%d,%d], [%d,%d]) | " + "Size: ([%dx%d] -> [%dx%d]) | " + "Addr: (0x%x -> 0x%x)", + src_area->x1, src_area->y1, src_area->x2, src_area->y2, + dest_area->x1, dest_area->y1, dest_area->x2, dest_area->y2, + lv_area_get_width(src_area), lv_area_get_height(src_area), + lv_area_get_width(dest_area), lv_area_get_height(dest_area), + (uintptr_t)src_buf, (uintptr_t)dest_buf); + + /* Move starting pointers as close as possible to [x1, y1], so coordinates are as small as possible */ + _move_buf_close_to_area((void **)&src_buf, src_area, src_stride, src_cf); + _move_buf_close_to_area(&dest_buf, dest_area, dest_stride, dest_cf); + + /* If we're in limit, do a single BLIT */ + if((src_area->x2 < VGLITE_BLIT_SPLIT_THR) && + (src_area->y2 < VGLITE_BLIT_SPLIT_THR)) { + + /* Set new dest_vgbuf and src_vgbuf memory addresses */ + vglite_set_dest_buf_ptr(dest_buf); + vglite_set_src_buf_ptr(src_buf); + + _vglite_blit_single(dest_area, src_area, opa); + + VGLITE_TRACE("Single " + "Area: ([%d,%d], [%d,%d]) -> ([%d,%d], [%d,%d]) | " + "Size: ([%dx%d] -> [%dx%d]) | " + "Addr: (0x%x -> 0x%x)", + src_area->x1, src_area->y1, src_area->x2, src_area->y2, + dest_area->x1, dest_area->y1, dest_area->x2, dest_area->y2, + lv_area_get_width(src_area), lv_area_get_height(src_area), + lv_area_get_width(dest_area), lv_area_get_height(dest_area), + (uintptr_t)src_buf, (uintptr_t)dest_buf); + }; + + /* Split the BLIT into multiple tiles */ + VGLITE_TRACE("Split " + "Area: ([%d,%d], [%d,%d]) -> ([%d,%d], [%d,%d]) | " + "Size: ([%dx%d] -> [%dx%d]) | " + "Addr: (0x%x -> 0x%x)", + src_area->x1, src_area->y1, src_area->x2, src_area->y2, + dest_area->x1, dest_area->y1, dest_area->x2, dest_area->y2, + lv_area_get_width(src_area), lv_area_get_height(src_area), + lv_area_get_width(dest_area), lv_area_get_height(dest_area), + (uintptr_t)src_buf, (uintptr_t)dest_buf); + + lv_coord_t width = LV_MIN(lv_area_get_width(src_area), lv_area_get_width(dest_area)); + lv_coord_t height = LV_MIN(lv_area_get_height(src_area), lv_area_get_height(dest_area)); + + /* Number of tiles needed */ + uint8_t total_tiles_x = (src_area->x1 + width + VGLITE_BLIT_SPLIT_THR - 1) / + VGLITE_BLIT_SPLIT_THR; + uint8_t total_tiles_y = (src_area->y1 + height + VGLITE_BLIT_SPLIT_THR - 1) / + VGLITE_BLIT_SPLIT_THR; + + uint16_t shift_src_x = src_area->x1; + uint16_t shift_dest_x = dest_area->x1; + + VGLITE_TRACE("X shift: src: %d, dst: %d", shift_src_x, shift_dest_x); + + uint8_t * tile_dest_buf; + lv_area_t tile_dest_area; + const uint8_t * tile_src_buf; + lv_area_t tile_src_area; + + for(uint8_t y = 0; y < total_tiles_y; y++) { + /* y1 always start from 0 */ + tile_src_area.y1 = 0; + + /* Calculate y2 coordinates */ + if(y < total_tiles_y - 1) + tile_src_area.y2 = VGLITE_BLIT_SPLIT_THR - 1; + else + tile_src_area.y2 = height - y * VGLITE_BLIT_SPLIT_THR - 1; + + /* No vertical shift, dest y is always in sync with src y */ + tile_dest_area.y1 = tile_src_area.y1; + tile_dest_area.y2 = tile_src_area.y2; + + /* Advance start pointer for every tile, except the first column (y = 0) */ + tile_src_buf = (uint8_t *)src_buf + y * VGLITE_BLIT_SPLIT_THR * src_stride; + tile_dest_buf = (uint8_t *)dest_buf + y * VGLITE_BLIT_SPLIT_THR * dest_stride; + + for(uint8_t x = 0; x < total_tiles_x; x++) { + /* x1 always start from the same shift */ + tile_src_area.x1 = shift_src_x; + tile_dest_area.x1 = shift_dest_x; + if(x > 0) { + /* Advance start pointer for every tile, except the first raw (x = 0) */ + tile_src_buf += VGLITE_BLIT_SPLIT_THR * vglite_get_px_size(src_cf) / 8; + tile_dest_buf += VGLITE_BLIT_SPLIT_THR * vglite_get_px_size(dest_cf) / 8; + } + + /* Calculate x2 coordinates */ + if(x < total_tiles_x - 1) + tile_src_area.x2 = VGLITE_BLIT_SPLIT_THR - 1; + else + tile_src_area.x2 = width - x * VGLITE_BLIT_SPLIT_THR - 1; + + tile_dest_area.x2 = tile_src_area.x2; + + /* Shift x2 coordinates */ + tile_src_area.x2 += shift_src_x; + tile_dest_area.x2 += shift_dest_x; + + /* Set new dest_vgbuf and src_vgbuf memory addresses */ + vglite_set_dest_buf_ptr(tile_dest_buf); + vglite_set_src_buf_ptr(tile_src_buf); + + _vglite_blit_single(&tile_dest_area, &tile_src_area, opa); + + VGLITE_TRACE("Tile [%d, %d] " + "Area: ([%d,%d], [%d,%d]) -> ([%d,%d], [%d,%d]) | " + "Size: ([%dx%d] -> [%dx%d]) | " + "Addr: (0x%x -> 0x%x)", + x, y, + tile_src_area.x1, tile_src_area.y1, tile_src_area.x2, tile_src_area.y2, + tile_dest_area.x1, tile_dest_area.y1, tile_dest_area.x2, tile_dest_area.y2, + lv_area_get_width(&tile_src_area), lv_area_get_height(&tile_src_area), + lv_area_get_width(&tile_dest_area), lv_area_get_height(&tile_dest_area), + (uintptr_t)tile_src_buf, (uintptr_t)tile_dest_buf); + } + } +} +#else +static void _vglite_blit_transform(const lv_area_t * dest_area, const lv_area_t * clip_area, + const lv_area_t * src_area, const lv_draw_image_dsc_t * dsc) +{ + /* Set scissor. */ + vglite_set_scissor(clip_area); + + /* Set vgmatrix. */ + vglite_set_transformation_matrix(dest_area, dsc); + + /* Start blit. */ + _vglite_blit(src_area, dsc->opa); + + /* Disable scissor. */ + vglite_disable_scissor(); +} +#endif /*VGLITE_BLIT_SPLIT_ENABLED*/ + +#endif /*LV_USE_DRAW_VGLITE*/ diff --git a/src/draw/nxp/vglite/lv_draw_vglite_label.c b/src/draw/nxp/vglite/lv_draw_vglite_label.c new file mode 100644 index 000000000..f19438518 --- /dev/null +++ b/src/draw/nxp/vglite/lv_draw_vglite_label.c @@ -0,0 +1,206 @@ +/** + * @file lv_draw_vglite_label.c + * + */ + +/** + * Copyright 2023 NXP + * + * SPDX-License-Identifier: MIT + */ + + +/********************* + * INCLUDES + *********************/ + +#include "lv_draw_vglite.h" + +#if LV_USE_DRAW_VGLITE +#include "lv_vglite_buf.h" +#include "lv_vglite_matrix.h" +#include "lv_vglite_utils.h" + +#include "../../../stdlib/lv_string.h" + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ + +static void _draw_vglite_letter(lv_draw_unit_t * draw_unit, lv_draw_glyph_dsc_t * glyph_draw_dsc, + lv_draw_fill_dsc_t * fill_draw_dsc, const lv_area_t * fill_area); + + +/** + * Draw letter (character bitmap blend) with optional color and opacity + * + * @param[in] dest_area Area with relative coordinates of destination buffer + * @param[in] mask_buf Mask buffer + * @param[in] mask_area Mask area with relative coordinates of source buffer + * @param[in] mask_stride Stride of mask buffer in bytes + * @param[in] color Color + * @param[in] opa Opacity + * + */ +static void _vglite_draw_letter(const lv_area_t * dest_area, + const void * mask_buf, const lv_area_t * mask_area, uint32_t mask_stride, + lv_color_t color, lv_opa_t opa); + +/********************** + * STATIC VARIABLES + **********************/ + +/********************** + * GLOBAL VARIABLES + **********************/ + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +void lv_draw_vglite_label(lv_draw_unit_t * draw_unit, const lv_draw_label_dsc_t * dsc, + const lv_area_t * coords) +{ + if(dsc->opa <= LV_OPA_MIN) return; + + lv_draw_label_interate_letters(draw_unit, dsc, coords, _draw_vglite_letter); +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +static void _draw_vglite_letter(lv_draw_unit_t * draw_unit, lv_draw_glyph_dsc_t * glyph_draw_dsc, + lv_draw_fill_dsc_t * fill_draw_dsc, const lv_area_t * fill_area) +{ + if(glyph_draw_dsc) { + if(glyph_draw_dsc->bitmap == NULL) { +#if LV_USE_FONT_PLACEHOLDER + /* Draw a placeholder rectangle*/ + lv_draw_border_dsc_t border_draw_dsc; + lv_draw_border_dsc_init(&border_draw_dsc); + border_draw_dsc.opa = glyph_draw_dsc->opa; + border_draw_dsc.color = glyph_draw_dsc->color; + border_draw_dsc.width = 1; + lv_draw_vglite_border(draw_unit, &border_draw_dsc, glyph_draw_dsc->bg_coords); +#endif + } + else if(glyph_draw_dsc->format == LV_DRAW_LETTER_BITMAP_FORMAT_A8) { + /*Do not draw transparent things*/ + if(glyph_draw_dsc->opa <= LV_OPA_MIN) + return; + + lv_layer_t * layer = draw_unit->target_layer; + + lv_area_t blend_area; + if(!_lv_area_intersect(&blend_area, glyph_draw_dsc->letter_coords, draw_unit->clip_area)) + return; + lv_area_move(&blend_area, -layer->draw_buf_ofs.x, -layer->draw_buf_ofs.y); + + //void * dest_buf = lv_draw_buf_go_to_xy(&layer->draw_buf, + // blend_area.x1 - layer->draw_buf_ofs.x, + // blend_area.y1 - layer->draw_buf_ofs.y); + + const uint8_t * mask_buf = glyph_draw_dsc->bitmap; + lv_area_t mask_area; + lv_area_copy(&mask_area, glyph_draw_dsc->letter_coords); + lv_area_move(&mask_area, -layer->draw_buf_ofs.x, -layer->draw_buf_ofs.y); + + uint32_t mask_stride = lv_draw_buf_width_to_stride( + lv_area_get_width(glyph_draw_dsc->letter_coords), + LV_COLOR_FORMAT_A8); + if(mask_buf) { + mask_buf += mask_stride * (blend_area.y1 - glyph_draw_dsc->letter_coords->y1) + + (blend_area.x1 - glyph_draw_dsc->letter_coords->x1); + } + + if(!vglite_buf_aligned(mask_buf, mask_stride, LV_COLOR_FORMAT_A8)) { + /* Draw a placeholder rectangle*/ + lv_draw_border_dsc_t border_draw_dsc; + lv_draw_border_dsc_init(&border_draw_dsc); + border_draw_dsc.opa = glyph_draw_dsc->opa; + border_draw_dsc.color = glyph_draw_dsc->color; + border_draw_dsc.width = 1; + lv_draw_vglite_border(draw_unit, &border_draw_dsc, glyph_draw_dsc->bg_coords); + } + else { + + _vglite_draw_letter(&blend_area, mask_buf, &mask_area, mask_stride, + glyph_draw_dsc->color, glyph_draw_dsc->opa); + } + } + else if(glyph_draw_dsc->format == LV_DRAW_LETTER_BITMAP_FORMAT_IMAGE) { +#if LV_USE_IMGFONT + lv_draw_img_dsc_t img_dsc; + lv_draw_img_dsc_init(&img_dsc); + img_dsc.angle = 0; + img_dsc.zoom = LV_ZOOM_NONE; + img_dsc.opa = glyph_draw_dsc->opa; + img_dsc.src = glyph_draw_dsc->bitmap; + lv_draw_vglite_img(draw_unit, &img_dsc, glyph_draw_dsc->letter_coords); +#endif + } + } + + if(fill_draw_dsc && fill_area) { + lv_draw_vglite_fill(draw_unit, fill_draw_dsc, fill_area); + } +} + +static void _vglite_draw_letter(const lv_area_t * dest_area, + const void * mask_buf, const lv_area_t * mask_area, uint32_t mask_stride, + lv_color_t color, lv_opa_t opa) +{ + vg_lite_error_t err = VG_LITE_SUCCESS; + vg_lite_buffer_t * dst_vgbuf = vglite_get_dest_buf(); + + vg_lite_buffer_t mask_vgbuf; + mask_vgbuf.format = VG_LITE_A8; + mask_vgbuf.tiled = VG_LITE_LINEAR; + mask_vgbuf.image_mode = VG_LITE_MULTIPLY_IMAGE_MODE; + mask_vgbuf.transparency_mode = VG_LITE_IMAGE_TRANSPARENT; + mask_vgbuf.width = (int32_t)lv_area_get_width(mask_area); + mask_vgbuf.height = (int32_t)lv_area_get_height(mask_area); + mask_vgbuf.stride = (int32_t)mask_stride; + + lv_memset(&mask_vgbuf.yuv, 0, sizeof(mask_vgbuf.yuv)); + + mask_vgbuf.memory = (void *)mask_buf; + mask_vgbuf.address = (uint32_t)mask_vgbuf.memory; + mask_vgbuf.handle = NULL; + + uint32_t rect[] = { + (uint32_t)0, /* start x */ + (uint32_t)0, /* start y */ + (uint32_t)lv_area_get_width(mask_area), /* width */ + (uint32_t)lv_area_get_height(mask_area) /* height */ + }; + + lv_color32_t col32 = lv_color_to_32(color, opa); + vg_lite_color_t vgcol = vglite_get_color(col32, false); + + /* Set vgmatrix. */ + vglite_set_translation_matrix(dest_area); + vg_lite_matrix_t * vgmatrix = vglite_get_matrix(); + + /*Blit with font color as paint color*/ + err = vg_lite_blit_rect(dst_vgbuf, &mask_vgbuf, rect, vgmatrix, VG_LITE_BLEND_SRC_OVER, vgcol, + VG_LITE_FILTER_POINT); + LV_ASSERT_MSG(err == VG_LITE_SUCCESS, "Draw letter failed."); + + vglite_run(); +} + +#endif /*LV_USE_DRAW_VGLITE*/ diff --git a/src/draw/nxp/vglite/lv_draw_vglite_layer.c b/src/draw/nxp/vglite/lv_draw_vglite_layer.c new file mode 100644 index 000000000..eb5c1e029 --- /dev/null +++ b/src/draw/nxp/vglite/lv_draw_vglite_layer.c @@ -0,0 +1,71 @@ +/** + * @file lv_draw_vglite_layer.c + * + */ + +/** + * Copyright 2023 NXP + * + * SPDX-License-Identifier: MIT + */ + + +/********************* + * INCLUDES + *********************/ + +#include "lv_draw_vglite.h" + +#if LV_USE_DRAW_VGLITE + +#include "../../../stdlib/lv_string.h" + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ + +/********************** + * STATIC VARIABLES + **********************/ + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +void lv_draw_vglite_layer(lv_draw_unit_t * draw_unit, const lv_draw_image_dsc_t * draw_dsc, + const lv_area_t * coords) +{ + lv_layer_t * layer_to_draw = (lv_layer_t *)draw_dsc->src; + + /*It can happen that nothing was draw on a layer and therefore its buffer is not allocated. + *In this case just return. */ + if(layer_to_draw->draw_buf.buf == NULL) + return; + + lv_image_dsc_t img_dsc; + img_dsc.header.w = layer_to_draw->draw_buf.width; + img_dsc.header.h = layer_to_draw->draw_buf.height; + img_dsc.header.cf = layer_to_draw->draw_buf.color_format; + img_dsc.header.always_zero = 0; + img_dsc.data = lv_draw_buf_get_buf(&layer_to_draw->draw_buf); + + lv_draw_image_dsc_t new_draw_dsc; + lv_memcpy(&new_draw_dsc, draw_dsc, sizeof(lv_draw_image_dsc_t)); + new_draw_dsc.src = &img_dsc; + + lv_draw_vglite_img(draw_unit, &new_draw_dsc, coords); +} + +#endif /*LV_USE_DRAW_VGLITE*/ diff --git a/src/draw/nxp/vglite/lv_draw_vglite_line.c b/src/draw/nxp/vglite/lv_draw_vglite_line.c new file mode 100644 index 000000000..db536aa17 --- /dev/null +++ b/src/draw/nxp/vglite/lv_draw_vglite_line.c @@ -0,0 +1,158 @@ +/** + * @file lv_draw_vglite_line.c + * + */ + +/** + * Copyright 2022, 2023 NXP + * + * SPDX-License-Identifier: MIT + */ + + +/********************* + * INCLUDES + *********************/ + +#include "lv_draw_vglite.h" + +#if LV_USE_DRAW_VGLITE +#include "lv_vglite_buf.h" +#include "lv_vglite_utils.h" + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ + +/** + * Draw line shape with effects + * + * @param[in] point1 Starting point with relative coordinates + * @param[in] point2 Ending point with relative coordinates + * @param[in] clip_area Clipping area with relative coordinates to dest buff + * @param[in] dsc Line description structure (width, rounded ending, opacity, ...) + * + */ +static void _vglite_draw_line(const lv_point_t * point1, const lv_point_t * point2, + const lv_area_t * clip_area, const lv_draw_line_dsc_t * dsc); + +/********************** + * STATIC VARIABLES + **********************/ + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +void lv_draw_vglite_line(lv_draw_unit_t * draw_unit, const lv_draw_line_dsc_t * dsc) +{ + if(dsc->width == 0) + return; + if(dsc->opa <= (lv_opa_t)LV_OPA_MIN) + return; + if(dsc->p1.x == dsc->p2.x && dsc->p1.y == dsc->p2.y) + return; + + lv_layer_t * layer = draw_unit->target_layer; + lv_area_t rel_clip_area; + rel_clip_area.x1 = LV_MIN(dsc->p1.x, dsc->p2.x) - dsc->width / 2; + rel_clip_area.x2 = LV_MAX(dsc->p1.x, dsc->p2.x) + dsc->width / 2; + rel_clip_area.y1 = LV_MIN(dsc->p1.y, dsc->p2.y) - dsc->width / 2; + rel_clip_area.y2 = LV_MAX(dsc->p1.y, dsc->p2.y) + dsc->width / 2; + + if(!_lv_area_intersect(&rel_clip_area, &rel_clip_area, draw_unit->clip_area)) + return; /*Fully clipped, nothing to do*/ + + lv_area_move(&rel_clip_area, -layer->draw_buf_ofs.x, -layer->draw_buf_ofs.y); + + lv_point_t rel_point1 = {dsc->p1.x - layer->draw_buf_ofs.x, dsc->p1.y - layer->draw_buf_ofs.y}; + lv_point_t rel_point2 = {dsc->p2.x - layer->draw_buf_ofs.x, dsc->p2.y - layer->draw_buf_ofs.y}; + + _vglite_draw_line(&rel_point1, &rel_point2, &rel_clip_area, dsc); +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +static void _vglite_draw_line(const lv_point_t * point1, const lv_point_t * point2, + const lv_area_t * clip_area, const lv_draw_line_dsc_t * dsc) +{ + vg_lite_error_t err = VG_LITE_SUCCESS; + vg_lite_path_t path; + vg_lite_buffer_t * vgbuf = vglite_get_dest_buf(); + vg_lite_cap_style_t cap_style = (dsc->round_start || dsc->round_end) ? VG_LITE_CAP_ROUND : VG_LITE_CAP_BUTT; + vg_lite_join_style_t join_style = (dsc->round_start || dsc->round_end) ? VG_LITE_JOIN_ROUND : VG_LITE_JOIN_MITER; + + bool is_dashed = (dsc->dash_width && dsc->dash_gap); + + vg_lite_float_t stroke_dash_pattern[2] = {0, 0}; + uint32_t stroke_dash_count = 0; + vg_lite_float_t stroke_dash_phase = 0; + if(is_dashed) { + stroke_dash_pattern[0] = (vg_lite_float_t)dsc->dash_width; + stroke_dash_pattern[1] = (vg_lite_float_t)dsc->dash_gap; + stroke_dash_count = sizeof(stroke_dash_pattern) / sizeof(vg_lite_float_t); + stroke_dash_phase = (vg_lite_float_t)dsc->dash_width / 2; + } + + /* Choose vglite blend mode based on given lvgl blend mode */ + vg_lite_blend_t vglite_blend_mode = vglite_get_blend_mode(dsc->blend_mode); + + /*** Init path ***/ + lv_coord_t width = dsc->width; + + int32_t line_path[] = { /*VG line path*/ + VLC_OP_MOVE, point1->x, point1->y, + VLC_OP_LINE, point2->x, point2->y, + VLC_OP_END + }; + + err = vg_lite_init_path(&path, VG_LITE_S32, VG_LITE_HIGH, sizeof(line_path), line_path, + (vg_lite_float_t)clip_area->x1, (vg_lite_float_t)clip_area->y1, + ((vg_lite_float_t)clip_area->x2) + 1.0f, ((vg_lite_float_t)clip_area->y2) + 1.0f); + LV_ASSERT_MSG(err == VG_LITE_SUCCESS, "Init path failed."); + + lv_color32_t col32 = lv_color_to_32(dsc->color, dsc->opa); + vg_lite_color_t vgcol = vglite_get_color(col32, false); + + vg_lite_matrix_t matrix; + vg_lite_identity(&matrix); + + /*** Draw line ***/ + err = vg_lite_set_draw_path_type(&path, VG_LITE_DRAW_STROKE_PATH); + LV_ASSERT_MSG(err == VG_LITE_SUCCESS, "Set draw path type failed."); + + err = vg_lite_set_stroke(&path, cap_style, join_style, width, 8, stroke_dash_pattern, stroke_dash_count, + stroke_dash_phase, vgcol); + LV_ASSERT_MSG(err == VG_LITE_SUCCESS, "Set stroke failed."); + + err = vg_lite_update_stroke(&path); + LV_ASSERT_MSG(err == VG_LITE_SUCCESS, "Update stroke failed."); + + vglite_set_scissor(clip_area); + + err = vg_lite_draw(vgbuf, &path, VG_LITE_FILL_NON_ZERO, &matrix, vglite_blend_mode, vgcol); + LV_ASSERT_MSG(err == VG_LITE_SUCCESS, "Draw line failed."); + + vglite_run(); + + vglite_disable_scissor(); + + err = vg_lite_clear_path(&path); + LV_ASSERT_MSG(err == VG_LITE_SUCCESS, "Clear path failed."); +} + +#endif /*LV_USE_DRAW_VGLITE*/ diff --git a/src/draw/nxp/vglite/lv_vglite_buf.c b/src/draw/nxp/vglite/lv_vglite_buf.c new file mode 100644 index 000000000..8d0efcf62 --- /dev/null +++ b/src/draw/nxp/vglite/lv_vglite_buf.c @@ -0,0 +1,117 @@ +/** + * @file lv_vglite_buf.c + * + */ + +/** + * Copyright 2023 NXP + * + * SPDX-License-Identifier: MIT + */ + + +/********************* + * INCLUDES + *********************/ + +#include "lv_vglite_buf.h" + +#if LV_USE_DRAW_VGLITE +#include "lv_vglite_utils.h" + +#include "../../../stdlib/lv_string.h" + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ + +static inline void _set_vgbuf_ptr(vg_lite_buffer_t * vgbuf, void * buf); + +/********************** + * STATIC VARIABLES + **********************/ + +static vg_lite_buffer_t _dest_vgbuf; +static vg_lite_buffer_t _src_vgbuf; + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +vg_lite_buffer_t * vglite_get_dest_buf(void) +{ + return &_dest_vgbuf; +} + +vg_lite_buffer_t * vglite_get_src_buf(void) +{ + return &_src_vgbuf; +} + +void vglite_set_dest_buf_ptr(void * buf) +{ + _set_vgbuf_ptr(&_dest_vgbuf, buf); +} + +void vglite_set_src_buf_ptr(const void * buf) +{ + _set_vgbuf_ptr(&_src_vgbuf, (void *)buf); +} + +void vglite_set_dest_buf(const lv_draw_buf_t * draw_buf) +{ + vglite_set_buf(&_dest_vgbuf, draw_buf->buf, draw_buf->width, draw_buf->height, + lv_draw_buf_get_stride(draw_buf), draw_buf->color_format); +} + +void vglite_set_src_buf(const void * buf, lv_coord_t width, lv_coord_t height, uint32_t stride, + lv_color_format_t cf) +{ + vglite_set_buf(&_src_vgbuf, (void *)buf, width, height, stride, cf); +} + +void vglite_set_buf(vg_lite_buffer_t * vgbuf, void * buf, + lv_coord_t width, lv_coord_t height, uint32_t stride, + lv_color_format_t cf) +{ + vg_lite_buffer_format_t vgformat = vglite_get_buf_format(cf); + + vgbuf->format = vgformat; + 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(&vgbuf->yuv, 0, sizeof(vgbuf->yuv)); + + vgbuf->memory = buf; + vgbuf->address = (uint32_t)vgbuf->memory; + vgbuf->handle = NULL; +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +static inline void _set_vgbuf_ptr(vg_lite_buffer_t * vgbuf, void * buf) +{ + vgbuf->memory = buf; + vgbuf->address = (uint32_t)vgbuf->memory; +} + +#endif /*LV_USE_DRAW_VGLITE*/ diff --git a/src/draw/nxp/vglite/lv_vglite_buf.h b/src/draw/nxp/vglite/lv_vglite_buf.h new file mode 100644 index 000000000..c70d470f7 --- /dev/null +++ b/src/draw/nxp/vglite/lv_vglite_buf.h @@ -0,0 +1,120 @@ +/** + * @file lv_vglite_buf.h + * + */ + +/** + * Copyright 2023 NXP + * + * SPDX-License-Identifier: MIT + */ + + +#ifndef LV_VGLITE_BUF_H +#define LV_VGLITE_BUF_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ +#include "../../../lv_conf_internal.h" + +#if LV_USE_DRAW_VGLITE +#include "../../sw/lv_draw_sw.h" + +#include "vg_lite.h" + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +/** + * Get vglite destination buffer pointer. + * + * @retval The vglite destination buffer + * + */ +vg_lite_buffer_t * vglite_get_dest_buf(void); + +/** + * Get vglite source buffer pointer. + * + * @retval The vglite source buffer + * + */ +vg_lite_buffer_t * vglite_get_src_buf(void); + +/** + * Set vglite destination buffer address only. + * + * @param[in] buf Destination buffer address (does not require alignment for VG_LITE_LINEAR mode) + * + */ +void vglite_set_dest_buf_ptr(void * buf); + +/** + * Set vglite source buffer address only. + * + * @param[in] buf Source buffer address + * + */ +void vglite_set_src_buf_ptr(const void * buf); + +/** + * Set vglite target (destination) buffer. + * + * @param[in] draw_buf Destination draw buffer descriptor + * + */ +void vglite_set_dest_buf(const lv_draw_buf_t * draw_buf); + +/** + * Set vglite source buffer. + * + * @param[in] buf Source buffer address + * @param[in] width Source buffer width + * @param[in] height Source buffer height + * @param[in] stride Source buffer stride in bytes + * @param[in] cf Source buffer color format + * + */ +void vglite_set_src_buf(const void * buf, lv_coord_t width, lv_coord_t height, uint32_t stride, + lv_color_format_t cf); + +/** + * Set vglite buffer. + * + * @param[in] vgbuf Address of the VGLite buffer object + * @param[in] buf Address of the memory for the VGLite buffer + * @param[in] width Buffer width + * @param[in] height Buffer height + * @param[in] stride Buffer stride in bytes + * @param[in] cf Buffer color format + * + */ +void vglite_set_buf(vg_lite_buffer_t * vgbuf, void * buf, + lv_coord_t width, lv_coord_t height, uint32_t stride, + lv_color_format_t cf); + +/********************** + * MACROS + **********************/ + +#endif /*LV_USE_DRAW_VGLITE*/ + +#ifdef __cplusplus +} /*extern "C"*/ +#endif + +#endif /*LV_VGLITE_BUF_H*/ diff --git a/src/draw/nxp/vglite/lv_vglite_matrix.c b/src/draw/nxp/vglite/lv_vglite_matrix.c new file mode 100644 index 000000000..45f8083a4 --- /dev/null +++ b/src/draw/nxp/vglite/lv_vglite_matrix.c @@ -0,0 +1,79 @@ +/** + * @file lv_vglite_matrix.c + * + */ + +/** + * Copyright 2023 NXP + * + * SPDX-License-Identifier: MIT + */ + + +/********************* + * INCLUDES + *********************/ + +#include "lv_vglite_matrix.h" + +#if LV_USE_DRAW_VGLITE + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ + +/********************** + * STATIC VARIABLES + **********************/ + +static vg_lite_matrix_t _vgmatrix; + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +vg_lite_matrix_t * vglite_get_matrix(void) +{ + return &_vgmatrix; +} + +void vglite_set_translation_matrix(const lv_area_t * dest_area) +{ + vg_lite_identity(&_vgmatrix); + vg_lite_translate((vg_lite_float_t)dest_area->x1, (vg_lite_float_t)dest_area->y1, &_vgmatrix); +} + +void vglite_set_transformation_matrix(const lv_area_t * dest_area, const lv_draw_image_dsc_t * dsc) +{ + vglite_set_translation_matrix(dest_area); + + bool has_scale = (dsc->zoom != LV_SCALE_NONE); + bool has_rotation = (dsc->rotation != 0); + + vg_lite_translate(dsc->pivot.x, dsc->pivot.y, &_vgmatrix); + if(has_rotation) + vg_lite_rotate(dsc->rotation / 10.0f, &_vgmatrix); /* angle is 1/10 degree */ + if(has_scale) { + vg_lite_float_t scale = 1.0f * dsc->zoom / LV_SCALE_NONE; + vg_lite_scale(scale, scale, &_vgmatrix); + } + vg_lite_translate(0.0f - dsc->pivot.x, 0.0f - dsc->pivot.y, &_vgmatrix); +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +#endif /*LV_USE_DRAW_VGLITE*/ diff --git a/src/draw/nxp/vglite/lv_vglite_matrix.h b/src/draw/nxp/vglite/lv_vglite_matrix.h new file mode 100644 index 000000000..f05e99e73 --- /dev/null +++ b/src/draw/nxp/vglite/lv_vglite_matrix.h @@ -0,0 +1,79 @@ +/** + * @file lv_vglite_matrix.h + * + */ + +/** + * Copyright 2023 NXP + * + * SPDX-License-Identifier: MIT + */ + + +#ifndef LV_VGLITE_MATRIX_H +#define LV_VGLITE_MATRIX_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ +#include "../../../lv_conf_internal.h" + +#if LV_USE_DRAW_VGLITE +#include "../../sw/lv_draw_sw.h" + +#include "vg_lite.h" + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +vg_lite_matrix_t * vglite_get_matrix(void); + +/** + * Creates matrix that translates to origin of given destination area. + * + * @param[in] dest_area Area with relative coordinates of destination buffer + * + */ +void vglite_set_translation_matrix(const lv_area_t * dest_area); + +/** + * Creates matrix that translates to origin of given destination area with transformation (scale or rotate). + * + * @param[in] dest_area Area with relative coordinates of destination buffer + * @param[in] dsc Image descriptor + * + */ +void vglite_set_transformation_matrix(const lv_area_t * dest_area, const lv_draw_image_dsc_t * dsc); + +/********************** + * MACROS + **********************/ + +/********************** + * STATIC FUNCTIONS + **********************/ + +#endif /*LV_USE_DRAW_VGLITE*/ + +#ifdef __cplusplus +} /*extern "C"*/ +#endif + +#endif /*LV_VGLITE_MATRIX_H*/ diff --git a/src/draw/nxp/vglite/lv_vglite_path.c b/src/draw/nxp/vglite/lv_vglite_path.c new file mode 100644 index 000000000..c682ea5d8 --- /dev/null +++ b/src/draw/nxp/vglite/lv_vglite_path.c @@ -0,0 +1,218 @@ +/** + * @file lv_vglite_path.c + * + */ + +/** + * Copyright 2023 NXP + * + * SPDX-License-Identifier: MIT + */ + + +/********************* + * INCLUDES + *********************/ + +#include "lv_vglite_path.h" + +#if LV_USE_DRAW_VGLITE +#include "vg_lite.h" + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ + +/********************** + * STATIC VARIABLES + **********************/ + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +void vglite_create_rect_path_data(int32_t * path_data, uint32_t * path_data_size, + lv_coord_t radius, + const lv_area_t * coords) +{ + lv_coord_t rect_width = lv_area_get_width(coords); + lv_coord_t rect_height = lv_area_get_height(coords); + + /* Get the final radius. Can't be larger than the half of the shortest side */ + int32_t shortest_side = LV_MIN(rect_width, rect_height); + int32_t final_radius = LV_MIN(radius, shortest_side / 2); + + /* Path data element index */ + uint8_t pidx = 0; + + if((radius == (lv_coord_t)LV_RADIUS_CIRCLE) && (rect_width == rect_height)) { + + /* Get the control point offset for rounded cases */ + int32_t cpoff = (int32_t)((float)final_radius * BEZIER_OPTIM_CIRCLE); + + /* Circle case */ + /* Starting point */ + path_data[pidx++] = VLC_OP_MOVE; + path_data[pidx++] = coords->x1 + final_radius; + path_data[pidx++] = coords->y1; + + /* Top-right arc */ + path_data[pidx++] = VLC_OP_CUBIC_REL; + path_data[pidx++] = cpoff; + path_data[pidx++] = 0; + path_data[pidx++] = final_radius; + path_data[pidx++] = final_radius - cpoff; + path_data[pidx++] = final_radius; + path_data[pidx++] = final_radius; + + /* Bottom-right arc*/ + path_data[pidx++] = VLC_OP_CUBIC_REL; + path_data[pidx++] = 0; + path_data[pidx++] = cpoff; + path_data[pidx++] = cpoff - final_radius; + path_data[pidx++] = final_radius; + path_data[pidx++] = 0 - final_radius; + path_data[pidx++] = final_radius; + + /* Bottom-left arc */ + path_data[pidx++] = VLC_OP_CUBIC_REL; + path_data[pidx++] = 0 - cpoff; + path_data[pidx++] = 0; + path_data[pidx++] = 0 - final_radius; + path_data[pidx++] = cpoff - final_radius; + path_data[pidx++] = 0 - final_radius; + path_data[pidx++] = 0 - final_radius; + + /* Top-left arc*/ + path_data[pidx++] = VLC_OP_CUBIC_REL; + path_data[pidx++] = 0; + path_data[pidx++] = 0 - cpoff; + path_data[pidx++] = final_radius - cpoff; + path_data[pidx++] = 0 - final_radius; + path_data[pidx++] = final_radius; + path_data[pidx++] = 0 - final_radius; + + /* Ending point */ + path_data[pidx++] = VLC_OP_END; + } + else if(radius > 0) { + /* Get the control point offset for rounded cases */ + int32_t cpoff = (int32_t)((float)final_radius * BEZIER_OPTIM_CIRCLE); + + /* Rounded rectangle case */ + /* Starting point */ + path_data[pidx++] = VLC_OP_MOVE; + path_data[pidx++] = coords->x1 + final_radius; + path_data[pidx++] = coords->y1; + + /* Top side */ + path_data[pidx++] = VLC_OP_LINE; + path_data[pidx++] = coords->x2 - final_radius + 1; // Extended for VGLite + path_data[pidx++] = coords->y1; + + /* Top-right corner */ + path_data[pidx++] = VLC_OP_CUBIC_REL; + path_data[pidx++] = cpoff; + path_data[pidx++] = 0; + path_data[pidx++] = final_radius; + path_data[pidx++] = final_radius - cpoff; + path_data[pidx++] = final_radius; + path_data[pidx++] = final_radius; + + /* Right side */ + path_data[pidx++] = VLC_OP_LINE; + path_data[pidx++] = coords->x2 + 1; // Extended for VGLite + path_data[pidx++] = coords->y2 - final_radius + 1; // Extended for VGLite + + /* Bottom-right corner*/ + path_data[pidx++] = VLC_OP_CUBIC_REL; + path_data[pidx++] = 0; + path_data[pidx++] = cpoff; + path_data[pidx++] = cpoff - final_radius; + path_data[pidx++] = final_radius; + path_data[pidx++] = 0 - final_radius; + path_data[pidx++] = final_radius; + + /* Bottom side */ + path_data[pidx++] = VLC_OP_LINE; + path_data[pidx++] = coords->x1 + final_radius; + path_data[pidx++] = coords->y2 + 1; // Extended for VGLite + + /* Bottom-left corner */ + path_data[pidx++] = VLC_OP_CUBIC_REL; + path_data[pidx++] = 0 - cpoff; + path_data[pidx++] = 0; + path_data[pidx++] = 0 - final_radius; + path_data[pidx++] = cpoff - final_radius; + path_data[pidx++] = 0 - final_radius; + path_data[pidx++] = 0 - final_radius; + + /* Left side*/ + path_data[pidx++] = VLC_OP_LINE; + path_data[pidx++] = coords->x1; + path_data[pidx++] = coords->y1 + final_radius; + + /* Top-left corner */ + path_data[pidx++] = VLC_OP_CUBIC_REL; + path_data[pidx++] = 0; + path_data[pidx++] = 0 - cpoff; + path_data[pidx++] = final_radius - cpoff; + path_data[pidx++] = 0 - final_radius; + path_data[pidx++] = final_radius; + path_data[pidx++] = 0 - final_radius; + + /* Ending point */ + path_data[pidx++] = VLC_OP_END; + } + else { + /* Non-rounded rectangle case */ + /* Starting point */ + path_data[pidx++] = VLC_OP_MOVE; + path_data[pidx++] = coords->x1; + path_data[pidx++] = coords->y1; + + /* Top side */ + path_data[pidx++] = VLC_OP_LINE; + path_data[pidx++] = coords->x2 + 1; // Extended for VGLite + path_data[pidx++] = coords->y1; + + /* Right side */ + path_data[pidx++] = VLC_OP_LINE; + path_data[pidx++] = coords->x2 + 1; // Extended for VGLite + path_data[pidx++] = coords->y2 + 1; // Extended for VGLite + + /* Bottom side */ + path_data[pidx++] = VLC_OP_LINE; + path_data[pidx++] = coords->x1; + path_data[pidx++] = coords->y2 + 1; // Extended for VGLite + + /* Left side*/ + path_data[pidx++] = VLC_OP_LINE; + path_data[pidx++] = coords->x1; + path_data[pidx++] = coords->y1; + + /* Ending point */ + path_data[pidx++] = VLC_OP_END; + } + + /* Resulting path size */ + *path_data_size = pidx * sizeof(int32_t); +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +#endif /*LV_USE_DRAW_VGLITE*/ diff --git a/src/draw/nxp/vglite/lv_vglite_path.h b/src/draw/nxp/vglite/lv_vglite_path.h new file mode 100644 index 000000000..842b03543 --- /dev/null +++ b/src/draw/nxp/vglite/lv_vglite_path.h @@ -0,0 +1,92 @@ +/** + * @file lv_vglite_path.h + * + */ + +/** + * Copyright 2023 NXP + * + * SPDX-License-Identifier: MIT + */ + + +#ifndef LV_VGLITE_PATH_H +#define LV_VGLITE_PATH_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ +#include "../../../lv_conf_internal.h" + +#if LV_USE_DRAW_VGLITE +#include "../../sw/lv_draw_sw.h" + +/********************* + * DEFINES + *********************/ + +/* 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 + +/* Path data sizes for different elements */ +#define CUBIC_PATH_DATA_SIZE 7 /* 1 opcode, 6 arguments */ +#define LINE_PATH_DATA_SIZE 3 /* 1 opcode, 2 arguments */ +#define MOVE_PATH_DATA_SIZE 3 /* 1 opcode, 2 arguments */ +#define END_PATH_DATA_SIZE 1 /* 1 opcode, 0 arguments */ +/* Maximum possible rectangle path size + * is in the rounded rectangle case: + * - 1 move for the path start + * - 4 cubics for the corners + * - 4 lines for the sides + * - 1 end for the path end */ +#define RECT_PATH_DATA_MAX_SIZE 1 * MOVE_PATH_DATA_SIZE + 4 * CUBIC_PATH_DATA_SIZE + 4 * LINE_PATH_DATA_SIZE + 1 * END_PATH_DATA_SIZE + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +/** + * Generates path data for rectangle drawing. + * + * @param[in/out] path The path data to initialize + * @param[in/out] path_size The resulting size of the created path data + * @param[in] dsc The style descriptor for the rectangle to be drawn + * @param[in] coords The coordinates of the rectangle to be drawn + * + */ +void vglite_create_rect_path_data(int32_t * path_data, uint32_t * path_data_size, + lv_coord_t radius, + const lv_area_t * coords); + +/********************** + * MACROS + **********************/ + +/********************** + * STATIC FUNCTIONS + **********************/ + +#endif /*LV_USE_DRAW_VGLITE*/ + +#ifdef __cplusplus +} /*extern "C"*/ +#endif + +#endif /*LV_VGLITE_PATH_H*/ diff --git a/src/draw/nxp/vglite/lv_vglite_utils.c b/src/draw/nxp/vglite/lv_vglite_utils.c new file mode 100644 index 000000000..7c976baf3 --- /dev/null +++ b/src/draw/nxp/vglite/lv_vglite_utils.c @@ -0,0 +1,292 @@ +/** + * @file lv_vglite_utils.c + * + */ + +/** + * Copyright 2022, 2023 NXP + * + * SPDX-License-Identifier: MIT + */ + + +/********************* + * INCLUDES + *********************/ + +#include "lv_vglite_utils.h" + +#if LV_USE_DRAW_VGLITE +#include "lv_vglite_buf.h" + +#include "../../../core/lv_refr.h" + +#if LV_USE_OS + #include "vg_lite_gpu.h" +#endif + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ + +/********************** + * STATIC VARIABLES + **********************/ + +#if LV_USE_OS + static volatile bool _cmd_buf_flushed = false; +#endif + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +#if LV_USE_OS +bool vglite_cmd_buf_is_flushed(void) +{ + return _cmd_buf_flushed; +} +#endif + +void vglite_run(void) +{ +#if LV_USE_OS + vg_lite_gpu_state_t gpu_state = vg_lite_get_gpu_state(); + + if(gpu_state == VG_LITE_GPU_BUSY) { + _cmd_buf_flushed = false; + + return; + } +#endif + + /* + * For multithreading version (with OS), we simply flush the command buffer + * and the vglite draw thread will signal the dispatcher for completed tasks. + * Without OS, we process the tasks and signal them as complete one by one. + */ +#if LV_USE_OS + LV_ASSERT_MSG(vg_lite_flush() == VG_LITE_SUCCESS, "Flush failed."); + _cmd_buf_flushed = true; +#else + LV_ASSERT_MSG(vg_lite_finish() == VG_LITE_SUCCESS, "Finish failed."); +#endif +} + +vg_lite_color_t vglite_get_color(lv_color32_t lv_col32, bool gradient) +{ + vg_lite_color_t vg_col32; + + /* Only pre-multiply color if hardware pre-multiplication is not present */ + if(!vg_lite_query_feature(gcFEATURE_BIT_VG_PE_PREMULTIPLY)) { + lv_col32.red = (uint8_t)((lv_col32.red * lv_col32.alpha) >> 8); + lv_col32.green = (uint8_t)((lv_col32.green * lv_col32.alpha) >> 8); + lv_col32.blue = (uint8_t)((lv_col32.blue * lv_col32.alpha) >> 8); + } + + if(!gradient) + /* The color is in ABGR8888 format with red channel in the lower 8 bits. */ + vg_col32 = ((vg_lite_color_t)lv_col32.alpha << 24) | ((vg_lite_color_t)lv_col32.blue << 16) | + ((vg_lite_color_t)lv_col32.green << 8) | (vg_lite_color_t)lv_col32.red; + else + /* The gradient color is in ARGB8888 format with blue channel in the lower 8 bits. */ + vg_col32 = ((vg_lite_color_t)lv_col32.alpha << 24) | ((vg_lite_color_t)lv_col32.red << 16) | + ((vg_lite_color_t)lv_col32.green << 8) | (vg_lite_color_t)lv_col32.blue; + + return vg_col32; +} + +vg_lite_blend_t vglite_get_blend_mode(lv_blend_mode_t lv_blend_mode) +{ + vg_lite_blend_t vg_blend_mode; + + switch(lv_blend_mode) { + case LV_BLEND_MODE_ADDITIVE: + vg_blend_mode = VG_LITE_BLEND_ADDITIVE; + break; + case LV_BLEND_MODE_SUBTRACTIVE: + vg_blend_mode = VG_LITE_BLEND_SUBTRACT; + break; + case LV_BLEND_MODE_MULTIPLY: + vg_blend_mode = VG_LITE_BLEND_MULTIPLY; + break; + default: + vg_blend_mode = VG_LITE_BLEND_SRC_OVER; + break; + } + + return vg_blend_mode; +} + +vg_lite_buffer_format_t vglite_get_buf_format(lv_color_format_t cf) +{ + vg_lite_buffer_format_t vg_buffer_format = VG_LITE_BGR565; + + switch(cf) { + /*<=1 byte (+alpha) formats*/ + case LV_COLOR_FORMAT_L8: + vg_buffer_format = VG_LITE_L8; + break; + case LV_COLOR_FORMAT_A8: + vg_buffer_format = VG_LITE_A8; + break; + case LV_COLOR_FORMAT_I1: + vg_buffer_format = VG_LITE_INDEX_1; + break; + case LV_COLOR_FORMAT_I2: + vg_buffer_format = VG_LITE_INDEX_2; + break; + case LV_COLOR_FORMAT_I4: + vg_buffer_format = VG_LITE_INDEX_4; + break; + case LV_COLOR_FORMAT_I8: + vg_buffer_format = VG_LITE_INDEX_8; + break; + + /*2 byte (+alpha) formats*/ + case LV_COLOR_FORMAT_RGB565: + vg_buffer_format = VG_LITE_BGR565; + break; + case LV_COLOR_FORMAT_RGB565A8: + LV_ASSERT_MSG(false, "Unsupported color format."); + break; + + /*3 byte (+alpha) formats*/ + case LV_COLOR_FORMAT_RGB888: + LV_ASSERT_MSG(false, "Unsupported color format."); + break; + case LV_COLOR_FORMAT_ARGB8888: + vg_buffer_format = VG_LITE_BGRA8888; + break; + case LV_COLOR_FORMAT_XRGB8888: + vg_buffer_format = VG_LITE_BGRX8888; + break; + + default: + LV_ASSERT_MSG(false, "Unsupported color format."); + break; + } + + return vg_buffer_format; +} + +uint8_t vglite_get_px_size(lv_color_format_t cf) +{ + uint8_t bits_per_pixel = LV_COLOR_DEPTH; + + switch(cf) { + case LV_COLOR_FORMAT_I1: + bits_per_pixel = 1; + break; + case LV_COLOR_FORMAT_I2: + bits_per_pixel = 2; + break; + case LV_COLOR_FORMAT_I4: + bits_per_pixel = 4; + break; + case LV_COLOR_FORMAT_I8: + case LV_COLOR_FORMAT_A8: + case LV_COLOR_FORMAT_L8: + bits_per_pixel = 8; + break; + case LV_COLOR_FORMAT_RGB565: + bits_per_pixel = 16; + break; + case LV_COLOR_FORMAT_RGB565A8: + case LV_COLOR_FORMAT_RGB888: + bits_per_pixel = 24; + break; + case LV_COLOR_FORMAT_ARGB8888: + case LV_COLOR_FORMAT_XRGB8888: + bits_per_pixel = 32; + break; + case LV_COLOR_FORMAT_NATIVE_REVERSED: + bits_per_pixel = LV_COLOR_DEPTH; + break; + + default: + LV_ASSERT_MSG(false, "Unsupported buffer format."); + break; + } + + return bits_per_pixel; +} + +uint8_t vglite_get_alignment(lv_color_format_t cf) +{ + uint8_t align_bytes = LV_COLOR_DEPTH / 8 * 16; //16 pixels + + switch(cf) { + case LV_COLOR_FORMAT_I1: + case LV_COLOR_FORMAT_I2: + case LV_COLOR_FORMAT_I4: + align_bytes = 8; + break; + case LV_COLOR_FORMAT_I8: + case LV_COLOR_FORMAT_A8: + case LV_COLOR_FORMAT_L8: + align_bytes = 16; + break; + case LV_COLOR_FORMAT_RGB565: + align_bytes = 32; + break; + case LV_COLOR_FORMAT_RGB565A8: + case LV_COLOR_FORMAT_RGB888: + align_bytes = 48; + break; + case LV_COLOR_FORMAT_ARGB8888: + case LV_COLOR_FORMAT_XRGB8888: + align_bytes = 64; + break; + case LV_COLOR_FORMAT_NATIVE_REVERSED: + align_bytes = LV_COLOR_DEPTH / 8 * 16; + break; + + default: + LV_ASSERT_MSG(false, "Unsupported buffer format."); + break; + } + + return align_bytes; +} + +bool vglite_buf_aligned(const void * buf, uint32_t stride, lv_color_format_t cf) +{ + uint8_t align_bytes = vglite_get_alignment(cf); + + /* No alignment requirement for destination buffer when using mode VG_LITE_LINEAR */ + + /* Test for pointer alignment */ + if((uintptr_t)buf % align_bytes) { + LV_LOG_ERROR("Buffer address (0x%x) not aligned to %d bytes.", + (size_t)buf, align_bytes); + return false; + } + + /* Test for stride alignment */ + if(stride % align_bytes) { + LV_LOG_ERROR("Buffer stride (%d bytes) not aligned to %d bytes.", + stride, align_bytes); + return false; + } + + return true; +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +#endif /*LV_USE_DRAW_VGLITE*/ diff --git a/src/draw/nxp/vglite/lv_vglite_utils.h b/src/draw/nxp/vglite/lv_vglite_utils.h new file mode 100644 index 000000000..edaf6ea78 --- /dev/null +++ b/src/draw/nxp/vglite/lv_vglite_utils.h @@ -0,0 +1,165 @@ +/** + * @file lv_vglite_utils.h + * + */ + +/** + * Copyright 2022, 2023 NXP + * + * SPDX-License-Identifier: MIT + */ + + +#ifndef LV_VGLITE_UTILS_H +#define LV_VGLITE_UTILS_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ +#include "../../../lv_conf_internal.h" + +#if LV_USE_DRAW_VGLITE +#include "../../sw/lv_draw_sw.h" + +#include "vg_lite.h" + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ + +/** + * Enable scissor and set the clipping box. + * + * @param[in] clip_area Clip area with relative coordinates of destination buffer + * + */ +static inline void vglite_set_scissor(const lv_area_t * clip_area); + +/** + * Disable scissor. + * + */ +static inline void vglite_disable_scissor(void); + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +#if LV_USE_OS +/** + * Get VG-Lite command buffer flushed status. + * + */ +bool vglite_cmd_buf_is_flushed(void); +#endif + +/** + * Clear cache and flush command to VG-Lite. + * + */ +void vglite_run(void); + +/** + * Get vglite color. Premultiplies (if not hw already) and swizzles the given + * LVGL 32bit color to obtain vglite color. + * + * @param[in] lv_col32 The initial LVGL 32bit color + * @param[in] gradient True for gradient color + * + * @retval The vglite 32-bit color value: + * + */ +vg_lite_color_t vglite_get_color(lv_color32_t lv_col32, bool gradient); + +/** + * Get vglite blend mode. + * + * @param[in] lv_blend_mode The LVGL blend mode + * + * @retval The vglite blend mode + * + */ +vg_lite_blend_t vglite_get_blend_mode(lv_blend_mode_t lv_blend_mode); + +/** + * Get vglite buffer format. + * + * @param[in] cf Color format + * + * @retval The vglite buffer format + * + */ +vg_lite_buffer_format_t vglite_get_buf_format(lv_color_format_t cf); + +/** + * Get vglite buffer pixel size. + * + * @param[in] cf Color format + * + * @retval Bits per pixel + * + */ +uint8_t vglite_get_px_size(lv_color_format_t cf); + +/** + * Get vglite buffer alignment. + * + * @param[in] cf Color format + * + * @retval Alignment requirement in bytes + * + */ +uint8_t vglite_get_alignment(lv_color_format_t cf); + +/** + * Check memory and stride alignment. + * + * @param[in] buf Buffer address + * @param[in] stride Stride of buffer in bytes + * @param[in] cf Color format - to calculate the expected alignment + * + * @retval true Alignment OK + * + */ +bool vglite_buf_aligned(const void * buf, uint32_t stride, lv_color_format_t cf); + +/********************** + * MACROS + **********************/ + +/********************** + * STATIC FUNCTIONS + **********************/ + +static inline void vglite_set_scissor(const lv_area_t * clip_area) +{ + vg_lite_enable_scissor(); + vg_lite_set_scissor((int32_t)clip_area->x1, (int32_t)clip_area->y1, + (int32_t)lv_area_get_width(clip_area), + (int32_t)lv_area_get_height(clip_area)); +} + +static inline void vglite_disable_scissor(void) +{ + vg_lite_disable_scissor(); +} + +#endif /*LV_USE_DRAW_VGLITE*/ + +#ifdef __cplusplus +} /*extern "C"*/ +#endif + +#endif /*LV_VGLITE_UTILS_H*/ diff --git a/src/lv_conf_internal.h b/src/lv_conf_internal.h index d60c1c3a7..5ca226ece 100644 --- a/src/lv_conf_internal.h +++ b/src/lv_conf_internal.h @@ -319,6 +319,24 @@ #endif #endif +/* Use NXP's VG-Lite GPU on iMX RTxxx platforms. */ +#ifndef LV_USE_DRAW_VGLITE + #ifdef CONFIG_LV_USE_DRAW_VGLITE + #define LV_USE_DRAW_VGLITE CONFIG_LV_USE_DRAW_VGLITE + #else + #define LV_USE_DRAW_VGLITE 0 + #endif +#endif + +/* Use NXP's PXP on iMX RTxxx platforms. */ +#ifndef LV_USE_DRAW_PXP + #ifdef CONFIG_LV_USE_DRAW_PXP + #define LV_USE_DRAW_PXP CONFIG_LV_USE_DRAW_PXP + #else + #define LV_USE_DRAW_PXP 0 + #endif +#endif + /*================= * OPERATING SYSTEM *=================*/ diff --git a/src/lv_init.c b/src/lv_init.c index e4069ed02..588672471 100644 --- a/src/lv_init.c +++ b/src/lv_init.c @@ -23,6 +23,12 @@ #include "misc/lv_cache_builtin.h" #include "misc/lv_async.h" #include "misc/lv_fs.h" +#if LV_USE_DRAW_VGLITE + #include "draw/nxp/vglite/lv_draw_vglite.h" +#endif +#if LV_USE_DRAW_PXP + #include "draw/nxp/pxp/lv_draw_pxp.h" +#endif /********************* * DEFINES @@ -137,6 +143,14 @@ void lv_init(void) lv_draw_sw_init(); #endif +#if LV_USE_DRAW_VGLITE + lv_draw_vglite_init(); +#endif + +#if LV_USE_DRAW_PXP + lv_draw_pxp_init(); +#endif + _lv_obj_style_init(); /*Initialize the screen refresh system*/