perf: Enable 512KB LVGL image cache on PSRAM and move modified lvgl to local components

This commit is contained in:
Adolfo Reyna
2026-07-09 22:27:55 -04:00
parent df93252281
commit c4ec7ead55
3999 changed files with 813776 additions and 0 deletions
@@ -0,0 +1,92 @@
/**
* @file lv_draw_buf_g2d.c
*
*/
/**
* Copyright 2024 NXP
*
* SPDX-License-Identifier: MIT
*/
/*********************
* INCLUDES
*********************/
#include "lv_draw_g2d.h"
#if LV_USE_DRAW_G2D
#include "../../lv_draw_buf_private.h"
#include "g2d.h"
#include "lv_g2d_buf_map.h"
#include "lv_g2d_utils.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static void * _buf_malloc(size_t size_bytes, lv_color_format_t color_format);
static void _buf_free(void * buf);
static void _invalidate_cache(const lv_draw_buf_t * draw_buf, const lv_area_t * area);
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
void lv_draw_buf_g2d_init_handlers(void)
{
lv_draw_buf_handlers_t * handlers = lv_draw_buf_get_handlers();
handlers->buf_malloc_cb = _buf_malloc;
handlers->buf_free_cb = _buf_free;
handlers->invalidate_cache_cb = _invalidate_cache;
}
/**********************
* STATIC FUNCTIONS
**********************/
static void * _buf_malloc(size_t size_bytes, lv_color_format_t color_format)
{
LV_UNUSED(color_format);
size_bytes += LV_DRAW_BUF_ALIGN - 1;
struct g2d_buf * buf = g2d_alloc(size_bytes, 1);
G2D_ASSERT_MSG(buf, "Failed to alloc buffer.");
g2d_insert_buf_map(buf->buf_vaddr, buf);
return buf->buf_vaddr;
}
static void _buf_free(void * buf)
{
g2d_free_item(buf);
}
static void _invalidate_cache(const lv_draw_buf_t * draw_buf, const lv_area_t * area)
{
LV_UNUSED(area);
struct g2d_buf * buf = g2d_search_buf_map(draw_buf->data);
G2D_ASSERT_MSG(buf, "Failed to find buffer in map.");
g2d_cache_op(buf, G2D_CACHE_FLUSH);
}
#endif /*LV_USE_DRAW_G2D*/
@@ -0,0 +1,336 @@
/**
* @file lv_draw_g2d.c
*
*/
/**
* Copyright 2024 NXP
*
* SPDX-License-Identifier: MIT
*/
#include "lv_draw_g2d.h"
#if LV_USE_DRAW_G2D
#include "../../../misc/lv_area_private.h"
#include "g2d.h"
#include "lv_g2d_buf_map.h"
/*********************
* DEFINES
*********************/
#define DRAW_UNIT_ID_G2D 8
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
/**
* Evaluate a task and set the score and preferred G2D unit.
* Return 1 if task is preferred, 0 otherwise (task is not supported).
*/
static int32_t _g2d_evaluate(lv_draw_unit_t * draw_unit, lv_draw_task_t * task);
/**
* Dispatch a task to the G2D unit.
* Return 1 if task was dispatched, 0 otherwise (task not supported).
*/
static int32_t _g2d_dispatch(lv_draw_unit_t * draw_unit, lv_layer_t * layer);
/**
* Delete the G2D draw unit.
*/
static int32_t _g2d_delete(lv_draw_unit_t * draw_unit);
#if LV_USE_G2D_DRAW_THREAD
static void _g2d_render_thread_cb(void * ptr);
#endif
static void _g2d_execute_drawing(lv_draw_task_t * t);
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
void lv_draw_g2d_init(void)
{
lv_draw_buf_g2d_init_handlers();
lv_draw_g2d_unit_t * draw_g2d_unit = lv_draw_create_unit(sizeof(lv_draw_g2d_unit_t));
draw_g2d_unit->base_unit.evaluate_cb = _g2d_evaluate;
draw_g2d_unit->base_unit.dispatch_cb = _g2d_dispatch;
draw_g2d_unit->base_unit.delete_cb = _g2d_delete;
draw_g2d_unit->base_unit.name = "G2D";
g2d_create_buf_map();
if(g2d_open(&draw_g2d_unit->g2d_handle)) {
LV_LOG_ERROR("g2d_open fail.\n");
}
#if LV_USE_G2D_DRAW_THREAD
lv_draw_sw_thread_dsc_t * thread_dsc = &draw_g2d_unit->thread_dsc;
thread_dsc->idx = 0;
thread_dsc->draw_unit = (void *) draw_g2d_unit;
lv_thread_init(&thread_dsc->thread, "g2ddraw", LV_DRAW_THREAD_PRIO, _g2d_render_thread_cb, LV_DRAW_THREAD_STACK_SIZE,
thread_dsc);
#endif
}
void lv_draw_g2d_deinit(void)
{
g2d_free_buf_map();
}
/**********************
* STATIC FUNCTIONS
**********************/
static inline bool _g2d_dest_cf_supported(lv_color_format_t cf)
{
bool is_cf_supported = false;
switch(cf) {
case LV_COLOR_FORMAT_ARGB8888:
case LV_COLOR_FORMAT_XRGB8888:
is_cf_supported = true;
break;
default:
break;
}
return is_cf_supported;
}
static inline bool _g2d_src_cf_supported(lv_draw_unit_t * drawunit, lv_color_format_t cf)
{
bool is_cf_supported = false;
switch(cf) {
case LV_COLOR_FORMAT_ARGB8888:
case LV_COLOR_FORMAT_XRGB8888:
is_cf_supported = true;
break;
case LV_COLOR_FORMAT_RGB565: {
int32_t hw_pxp = 0;
lv_draw_g2d_unit_t * u = (lv_draw_g2d_unit_t *)drawunit;
g2d_query_hardware(u->g2d_handle, G2D_HARDWARE_PXP, &hw_pxp);
if(!hw_pxp) {
is_cf_supported = true;
}
}
break;
default:
break;
}
return is_cf_supported;
}
static bool _g2d_draw_img_supported(const lv_draw_image_dsc_t * draw_dsc)
{
bool is_tiled = draw_dsc->tile;
/* Tiled image (repeat image) is currently not supported. */
if(is_tiled)
return false;
bool has_recolor = (draw_dsc->recolor_opa > LV_OPA_MIN);
bool has_rotation = (draw_dsc->rotation != 0);
/* Recolor or rotation are not supported. */
if(has_recolor || has_rotation)
return false;
return true;
}
static int32_t _g2d_evaluate(lv_draw_unit_t * u, lv_draw_task_t * t)
{
LV_UNUSED(u);
const lv_draw_dsc_base_t * draw_dsc_base = (lv_draw_dsc_base_t *) t->draw_dsc;
lv_draw_buf_t * draw_buf = draw_dsc_base->layer->draw_buf;
if(!_g2d_dest_cf_supported(draw_dsc_base->layer->color_format))
return 0;
if(draw_buf && !g2d_search_buf_map(draw_buf->data))
return 0;
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_G2D;
}
return 1;
}
case LV_DRAW_TASK_TYPE_IMAGE: {
const lv_draw_image_dsc_t * draw_dsc = (lv_draw_image_dsc_t *) t->draw_dsc;
if(!_g2d_src_cf_supported(u, draw_dsc->header.cf))
return 0;
if(!_g2d_draw_img_supported(draw_dsc))
return 0;
if(t->preference_score > 70) {
t->preference_score = 70;
t->preferred_draw_unit_id = DRAW_UNIT_ID_G2D;
}
return 1;
}
default:
return 0;
}
return 0;
}
static int32_t _g2d_dispatch(lv_draw_unit_t * draw_unit, lv_layer_t * layer)
{
lv_draw_g2d_unit_t * draw_g2d_unit = (lv_draw_g2d_unit_t *) draw_unit;
#if LV_USE_OS
lv_draw_sw_thread_dsc_t * thread_dsc = &draw_g2d_unit->thread_dsc;
/* Return immediately if it's busy with draw task. */
if(thread_dsc->task_act)
return 0;
#else
/* Return immediately if it's busy with draw task. */
if(draw_g2d_unit->task_act)
return 0;
#endif
/* Try to get an ready to draw. */
lv_draw_task_t * t = lv_draw_get_available_task(layer, NULL, DRAW_UNIT_ID_G2D);
if(t == NULL || t->preferred_draw_unit_id != DRAW_UNIT_ID_G2D)
return LV_DRAW_UNIT_IDLE;
if(lv_draw_layer_alloc_buf(layer) == NULL)
return LV_DRAW_UNIT_IDLE;
t->state = LV_DRAW_TASK_STATE_IN_PROGRESS;
t->draw_unit = draw_unit;
#if LV_USE_G2D_DRAW_THREAD
thread_dsc->task_act = t;
/* Let the render thread work. */
if(thread_dsc->inited)
lv_thread_sync_signal(&thread_dsc->sync);
#else
draw_g2d_unit->task_act = t;
_g2d_execute_drawing(t);
draw_g2d_unit->task_act->state = LV_DRAW_TASK_STATE_READY;
draw_g2d_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 int32_t _g2d_delete(lv_draw_unit_t * draw_unit)
{
lv_draw_g2d_unit_t * draw_g2d_unit = (lv_draw_g2d_unit_t *) draw_unit;
lv_result_t res = LV_RESULT_OK;
#if LV_USE_G2D_DRAW_THREAD
lv_draw_sw_thread_dsc_t * thread_dsc = &draw_g2d_unit->thread_dsc;
LV_LOG_INFO("Cancel G2D draw thread.");
thread_dsc->exit_status = true;
if(thread_dsc->inited)
lv_thread_sync_signal(&thread_dsc->sync);
res = lv_thread_delete(&thread_dsc->thread);
#endif
g2d_close(draw_g2d_unit->g2d_handle);
return res;
}
static void _g2d_execute_drawing(lv_draw_task_t * t)
{
lv_layer_t * layer = t->target_layer;
lv_draw_buf_t * draw_buf = layer->draw_buf;
/* Invalidate only the drawing area */
lv_draw_buf_invalidate_cache(draw_buf, NULL);
switch(t->type) {
case LV_DRAW_TASK_TYPE_FILL:
lv_draw_g2d_fill(t);
break;
case LV_DRAW_TASK_TYPE_IMAGE:
lv_draw_g2d_img(t);
break;
default:
break;
}
}
#if LV_USE_G2D_DRAW_THREAD
static void _g2d_render_thread_cb(void * ptr)
{
lv_draw_sw_thread_dsc_t * thread_dsc = ptr;
lv_thread_sync_init(&thread_dsc->sync);
thread_dsc->inited = true;
while(1) {
/* Wait for sync if there is no task set. */
while(thread_dsc->task_act == NULL) {
if(thread_dsc->exit_status)
break;
lv_thread_sync_wait(&thread_dsc->sync);
}
if(thread_dsc->exit_status) {
LV_LOG_INFO("Ready to exit G2D draw thread.");
break;
}
_g2d_execute_drawing(thread_dsc->task_act);
/* Signal the ready state to dispatcher. */
thread_dsc->task_act->state = LV_DRAW_TASK_STATE_READY;
/* Cleanup. */
thread_dsc->task_act = NULL;
/* The draw unit is free now. Request a new dispatching as it can get a new task. */
lv_draw_dispatch_request();
}
thread_dsc->inited = false;
lv_thread_sync_delete(&thread_dsc->sync);
LV_LOG_INFO("Exit G2D draw thread.");
}
#endif
#endif /*LV_USE_DRAW_G2D*/
@@ -0,0 +1,71 @@
/**
* @file lv_draw_g2d.h
*
*/
/**
* Copyright 2024 NXP
*
* SPDX-License-Identifier: MIT
*/
#ifndef LV_DRAW_G2D_H
#define LV_DRAW_G2D_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../../../lv_conf_internal.h"
#if LV_USE_DRAW_G2D
#include "../../sw/lv_draw_sw_private.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
typedef struct lv_draw_g2d_unit {
lv_draw_unit_t base_unit;
#if LV_USE_OS
lv_draw_sw_thread_dsc_t thread_dsc;
#else
lv_draw_task_t * task_act;
#endif
void * g2d_handle;
} lv_draw_g2d_unit_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
void lv_draw_g2d_init(void);
void lv_draw_g2d_deinit(void);
void lv_draw_buf_g2d_init_handlers(void);
void lv_draw_g2d_fill(lv_draw_task_t * t);
void lv_draw_g2d_img(lv_draw_task_t * t);
/**********************
* MACROS
**********************/
#endif /*LV_USE_DRAW_G2D*/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_DRAW_G2D_H*/
@@ -0,0 +1,178 @@
/**
* @file lv_draw_g2d_fill.c
*
*/
/**
* Copyright 2024 NXP
*
* SPDX-License-Identifier: MIT
*/
/*********************
* INCLUDES
*********************/
#include "lv_draw_g2d.h"
#if LV_USE_DRAW_G2D
#include <stdlib.h>
#include "g2d.h"
#include "../../../misc/lv_area_private.h"
#include "lv_g2d_buf_map.h"
#include "lv_g2d_utils.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
/* Blit simple w/ opa and alpha channel */
static void _g2d_fill(void * g2d_handle, struct g2d_surface * dst_surf);
static void _g2d_fill_with_opa(void * g2d_handle, struct g2d_surface * dst_surf, struct g2d_surface * src_surf);
static void _g2d_set_src_surf(struct g2d_surface * src_surf, struct g2d_buf * buf, const lv_area_t * area,
lv_color_t color, lv_opa_t opa);
static void _g2d_set_dst_surf(struct g2d_surface * dst_surf, struct g2d_buf * buf, const lv_area_t * area,
int32_t stride, lv_color_t color);
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
void lv_draw_g2d_fill(lv_draw_task_t * t)
{
lv_draw_fill_dsc_t * dsc = t->draw_dsc;
if(dsc->opa <= (lv_opa_t)LV_OPA_MIN)
return;
lv_draw_g2d_unit_t * u = (lv_draw_g2d_unit_t *)t->draw_unit;
lv_layer_t * layer = t->target_layer;
lv_draw_buf_t * draw_buf = layer->draw_buf;
lv_area_t * coords = &t->area;
lv_area_t rel_coords;
lv_area_copy(&rel_coords, coords);
lv_area_move(&rel_coords, -layer->buf_area.x1, -layer->buf_area.y1);
lv_area_t rel_clip_area;
lv_area_copy(&rel_clip_area, &t->clip_area);
lv_area_move(&rel_clip_area, -layer->buf_area.x1, -layer->buf_area.y1);
lv_area_t blend_area;
if(!lv_area_intersect(&blend_area, &rel_coords, &rel_clip_area))
return; /*Fully clipped, nothing to do*/
/* G2D takes stride in pixels. */
int32_t stride = draw_buf->header.stride / (lv_color_format_get_bpp(draw_buf->header.cf) / 8);
/* Destination buffer */
struct g2d_buf * dst_buf = g2d_search_buf_map(draw_buf->data);
bool has_opa = (dsc->opa < (lv_opa_t)LV_OPA_MAX);
struct g2d_surface dst_surf;
_g2d_set_dst_surf(&dst_surf, dst_buf, &blend_area, stride, dsc->color);
if(has_opa) {
struct g2d_buf * tmp_buf = g2d_alloc(lv_area_get_width(&blend_area) * lv_area_get_height(&blend_area) * sizeof(
lv_color32_t), 1);
G2D_ASSERT_MSG(tmp_buf, "Failed to alloc temporary buffer.");
struct g2d_surface src_surf;
_g2d_set_src_surf(&src_surf, tmp_buf, &blend_area, dsc->color, dsc->opa);
_g2d_fill_with_opa(u->g2d_handle, &dst_surf, &src_surf);
g2d_free(tmp_buf);
}
else {
_g2d_fill(u->g2d_handle, &dst_surf);
}
}
/**********************
* STATIC FUNCTIONS
**********************/
static void _g2d_set_src_surf(struct g2d_surface * src_surf, struct g2d_buf * buf, const lv_area_t * area,
lv_color_t color, lv_opa_t opa)
{
int32_t width = lv_area_get_width(area);
int32_t height = lv_area_get_height(area);
src_surf->format = G2D_RGBA8888;
src_surf->left = 0;
src_surf->top = 0;
src_surf->right = width;
src_surf->bottom = height;
src_surf->stride = width;
src_surf->width = width;
src_surf->height = height;
src_surf->planes[0] = buf->buf_paddr;
src_surf->rot = G2D_ROTATION_0;
src_surf->clrcolor = g2d_rgba_to_u32(color);
src_surf->global_alpha = opa;
src_surf->blendfunc = G2D_ONE | G2D_PRE_MULTIPLIED_ALPHA;
}
static void _g2d_set_dst_surf(struct g2d_surface * dst_surf, struct g2d_buf * buf, const lv_area_t * area,
int32_t stride, lv_color_t color)
{
int32_t width = lv_area_get_width(area);
int32_t height = lv_area_get_height(area);
dst_surf->format = G2D_RGBA8888;
dst_surf->left = area->x1;
dst_surf->top = area->y1;
dst_surf->right = area->x1 + width;
dst_surf->bottom = area->y1 + height;
dst_surf->stride = stride;
dst_surf->width = width;
dst_surf->height = height;
dst_surf->planes[0] = buf->buf_paddr;
dst_surf->rot = G2D_ROTATION_0;
dst_surf->clrcolor = g2d_rgba_to_u32(color);
dst_surf->global_alpha = 0xff;
dst_surf->blendfunc = G2D_ONE_MINUS_SRC_ALPHA | G2D_PRE_MULTIPLIED_ALPHA;
}
static void _g2d_fill_with_opa(void * g2d_handle, struct g2d_surface * dst_surf, struct g2d_surface * src_surf)
{
g2d_clear(g2d_handle, src_surf);
g2d_enable(g2d_handle, G2D_BLEND);
g2d_enable(g2d_handle, G2D_GLOBAL_ALPHA);
g2d_blit(g2d_handle, src_surf, dst_surf);
g2d_finish(g2d_handle);
g2d_disable(g2d_handle, G2D_GLOBAL_ALPHA);
g2d_disable(g2d_handle, G2D_BLEND);
}
static void _g2d_fill(void * g2d_handle, struct g2d_surface * dst_surf)
{
g2d_clear(g2d_handle, dst_surf);
g2d_finish(g2d_handle);
}
#endif /*LV_USE_DRAW_G2D*/
@@ -0,0 +1,214 @@
/**
* @file lv_draw_g2d_img.c
*
*/
/**
* Copyright 2024 NXP
*
* SPDX-License-Identifier: MIT
*/
/*********************
* INCLUDES
*********************/
#include "lv_draw_g2d.h"
#if LV_USE_DRAW_G2D
#include <stdlib.h>
#include <math.h>
#include "g2d.h"
#include "../../../misc/lv_area_private.h"
#include "lv_g2d_utils.h"
#include "lv_g2d_buf_map.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static struct g2d_buf * _g2d_handle_src_buf(const lv_image_dsc_t * data);
static void _g2d_set_src_surf(struct g2d_surface * src_surf, struct g2d_buf * buf, const lv_area_t * area,
int32_t stride, lv_color_format_t cf, lv_opa_t opa);
static void _g2d_set_dst_surf(struct g2d_surface * dst_surf, struct g2d_buf * buf, const lv_area_t * area,
int32_t stride, lv_color_format_t cf, const lv_draw_image_dsc_t * dsc);
/* Blit simple w/ opa and alpha channel */
static void _g2d_blit(void * g2d_handle, struct g2d_surface * dst_surf, struct g2d_surface * src_surf);
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
void lv_draw_g2d_img(lv_draw_task_t * t)
{
lv_draw_image_dsc_t * dsc = t->draw_dsc;
if(dsc->opa <= (lv_opa_t)LV_OPA_MIN)
return;
lv_draw_g2d_unit_t * u = (lv_draw_g2d_unit_t *)t->draw_unit;
lv_layer_t * layer = t->target_layer;
lv_draw_buf_t * draw_buf = layer->draw_buf;
const lv_image_dsc_t * img_dsc = dsc->src;
lv_area_t * coords = &t->area;
lv_area_t rel_coords;
lv_area_copy(&rel_coords, coords);
lv_area_move(&rel_coords, -layer->buf_area.x1, -layer->buf_area.y1);
lv_area_t clip_area;
lv_area_copy(&clip_area, &t->clip_area);
lv_area_move(&clip_area, -layer->buf_area.x1, -layer->buf_area.y1);
lv_area_t blend_area;
bool has_transform = (dsc->rotation != 0 || dsc->scale_x != LV_SCALE_NONE || dsc->scale_y != LV_SCALE_NONE);
if(has_transform)
lv_area_copy(&blend_area, &rel_coords);
else if(!lv_area_intersect(&blend_area, &rel_coords, &clip_area))
return; /*Fully clipped, nothing to do*/
lv_area_t src_area;
src_area.x1 = blend_area.x1 - (coords->x1 - layer->buf_area.x1);
src_area.y1 = blend_area.y1 - (coords->y1 - layer->buf_area.y1);
src_area.x2 = src_area.x1 + lv_area_get_width(&blend_area);
src_area.y2 = src_area.y1 + lv_area_get_height(&blend_area);
int32_t src_stride = img_dsc->header.stride / (lv_color_format_get_bpp(img_dsc->header.cf) / 8);
lv_color_format_t src_cf = img_dsc->header.cf;
/* Source image */
struct g2d_buf * src_buf = _g2d_handle_src_buf(img_dsc);
/* Destination buffer */
struct g2d_buf * dst_buf = g2d_search_buf_map(draw_buf->data);
/* G2D takes stride in pixels. */
int32_t dest_stride = draw_buf->header.stride / (lv_color_format_get_bpp(draw_buf->header.cf) / 8);
lv_color_format_t dest_cf = draw_buf->header.cf;
struct g2d_surface src_surf;
struct g2d_surface dst_surf;
_g2d_set_src_surf(&src_surf, src_buf, &src_area, src_stride, src_cf, dsc->opa);
_g2d_set_dst_surf(&dst_surf, dst_buf, &blend_area, dest_stride, dest_cf, dsc);
_g2d_blit(u->g2d_handle, &dst_surf, &src_surf);
}
/**********************
* STATIC FUNCTIONS
**********************/
static struct g2d_buf * _g2d_handle_src_buf(const lv_image_dsc_t * img_dsc)
{
struct g2d_buf * src_buf = g2d_search_buf_map((void *)img_dsc->data);
if(src_buf == NULL) {
src_buf = g2d_alloc(img_dsc->data_size, 1);
G2D_ASSERT_MSG(src_buf, "Failed to alloc source buffer.");
memcpy((uint8_t *)src_buf->buf_vaddr, img_dsc->data, img_dsc->data_size);
g2d_cache_op(src_buf, G2D_CACHE_FLUSH);
g2d_insert_buf_map((void *)img_dsc->data, src_buf);
}
return src_buf;
}
static void _g2d_set_src_surf(struct g2d_surface * src_surf, struct g2d_buf * buf, const lv_area_t * area,
int32_t stride, lv_color_format_t cf, lv_opa_t opa)
{
src_surf->format = g2d_get_buf_format(cf);
src_surf->left = area->x1;
src_surf->top = area->y1;
src_surf->right = area->x2;
src_surf->bottom = area->y2;
src_surf->stride = stride;
src_surf->width = area->x2 - area->x1;
src_surf->height = area->y2 - area->y1;
src_surf->planes[0] = buf->buf_paddr;
src_surf->rot = G2D_ROTATION_0;
src_surf->clrcolor = g2d_rgba_to_u32(lv_color_black());
src_surf->global_alpha = opa;
src_surf->blendfunc = G2D_ONE | G2D_PRE_MULTIPLIED_ALPHA;
}
static void _g2d_set_dst_surf(struct g2d_surface * dst_surf, struct g2d_buf * buf, const lv_area_t * area,
int32_t stride, lv_color_format_t cf, const lv_draw_image_dsc_t * dsc)
{
int32_t width = lv_area_get_width(area);
int32_t height = lv_area_get_height(area);
lv_point_t pivot = dsc->pivot;
/*The offsets are now relative to the transformation result with pivot ULC*/
int32_t piv_offset_x = 0;
int32_t piv_offset_y = 0;
int32_t trim_x = 0;
int32_t trim_y = 0;
int32_t dest_w;
int32_t dest_h;
float fp_scale_x = (float)dsc->scale_x / LV_SCALE_NONE;
float fp_scale_y = (float)dsc->scale_y / LV_SCALE_NONE;
int32_t int_scale_x = (int32_t)fp_scale_x;
int32_t int_scale_y = (int32_t)fp_scale_y;
/*Any scale_factor in (k, k + 1] will result in a trim equal to k*/
trim_x = (fp_scale_x == int_scale_x) ? int_scale_x - 1 : int_scale_x;
trim_y = (fp_scale_y == int_scale_y) ? int_scale_y - 1 : int_scale_y;
dest_w = width * fp_scale_x + trim_x;
dest_h = height * fp_scale_y + trim_y;
/*Final pivot offset = scale_factor * rotation_pivot_offset + scaling_pivot_offset*/
piv_offset_x = floor(fp_scale_x * piv_offset_x) - floor((fp_scale_x - 1) * pivot.x);
piv_offset_y = floor(fp_scale_y * piv_offset_y) - floor((fp_scale_y - 1) * pivot.y);
dst_surf->format = g2d_get_buf_format(cf);
dst_surf->left = area->x1 + piv_offset_x;
dst_surf->top = area->y1 + piv_offset_y;
dst_surf->right = area->x1 + piv_offset_x + dest_w - trim_x;
dst_surf->bottom = area->y1 + piv_offset_y + dest_h - trim_y;
dst_surf->stride = stride;
dst_surf->width = dest_w - trim_x;
dst_surf->height = dest_h - trim_y;
dst_surf->planes[0] = buf->buf_paddr;
dst_surf->rot = G2D_ROTATION_0;
dst_surf->clrcolor = g2d_rgba_to_u32(lv_color_black());
dst_surf->global_alpha = 0xff;
dst_surf->blendfunc = G2D_ONE_MINUS_SRC_ALPHA | G2D_PRE_MULTIPLIED_ALPHA;
}
static void _g2d_blit(void * g2d_handle, struct g2d_surface * dst_surf, struct g2d_surface * src_surf)
{
g2d_enable(g2d_handle, G2D_BLEND);
g2d_enable(g2d_handle, G2D_GLOBAL_ALPHA);
g2d_blit(g2d_handle, src_surf, dst_surf);
g2d_finish(g2d_handle);
g2d_disable(g2d_handle, G2D_GLOBAL_ALPHA);
g2d_disable(g2d_handle, G2D_BLEND);
}
#endif /*LV_USE_DRAW_G2D*/
@@ -0,0 +1,247 @@
/**
* @file lv_g2d_buf_map.c
*
*/
/**
* Copyright 2024 NXP
*
* SPDX-License-Identifier: MIT
*/
#include "lv_g2d_buf_map.h"
#if LV_USE_DRAW_G2D
#include <stdio.h>
#include "lv_g2d_utils.h"
#include "g2d.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static unsigned long _map_hash_function(void * ptr);
static lv_map_item_t * _map_create_item(void * key, struct g2d_buf * value);
static void _map_free_item(lv_map_item_t * item);
static void _handle_collision(unsigned long index, lv_map_item_t * item);
/**********************
* STATIC VARIABLES
**********************/
static lv_buf_map_t * table;
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
void g2d_create_buf_map(void)
{
table = (lv_buf_map_t *) lv_malloc(sizeof(lv_buf_map_t));
table->size = LV_G2D_HASH_TABLE_SIZE;
table->count = 0;
table->items = (lv_map_item_t **) lv_malloc_zeroed(table->size * sizeof(lv_map_item_t *));
table->overflow_list = (lv_array_t **) lv_malloc_zeroed(table->size * sizeof(lv_array_t *));
}
void g2d_free_buf_map(void)
{
for(int i = 0; i < table->size; i++) {
if(table->overflow_list[i]) {
lv_array_deinit(table->overflow_list[i]);
lv_free(table->overflow_list[i]);
}
lv_map_item_t * item = table->items[i];
if(item != NULL)
_map_free_item(item);
}
lv_free(table->items);
lv_free(table->overflow_list);
lv_free(table);
}
void g2d_insert_buf_map(void * key, struct g2d_buf * value)
{
lv_map_item_t * item = _map_create_item(key, value);
int index = _map_hash_function(key);
if(table->items[index] == NULL) {
/* Key not found. */
if(table->count == table->size) {
/* Table is full. */
_map_free_item(item);
G2D_ASSERT_MSG(false, "Hash table is full.");
return;
}
}
else {
if(table->items[index]->key == key) {
/* Key already exists, update value. */
table->items[index]->value = value;
return;
}
else {
/* Handle the collision */
_handle_collision(index, item);
return;
}
}
/* Insert item. */
table->items[index] = item;
table->count++;
}
struct g2d_buf * g2d_search_buf_map(void * key)
{
int index = _map_hash_function(key);
lv_map_item_t * item = table->items[index];
lv_array_t * list = (lv_array_t *)table->overflow_list[index];
if(item == NULL)
return NULL;
if(item->key == key)
return item->value;
if(list == NULL)
return NULL;
for(uint32_t i = 0; i < lv_array_size(list); i++) {
item = (lv_map_item_t *)lv_array_at(list, i);
if(item->key == key)
return item->value;
}
return NULL;
}
void g2d_free_item(void * key)
{
/* Delete an item from the table. */
int index = _map_hash_function(key);
lv_map_item_t * item = table->items[index];
lv_array_t * list = (lv_array_t *)table->overflow_list[index];
if(item == NULL) {
return;
}
else if(list == NULL && item->key == key) {
/* No collision chain, just remove item. */
table->items[index] = NULL;
_map_free_item(item);
table->count--;
return;
}
else if(list != NULL) {
/* Collision chain exists. */
for(uint32_t i = 0; i < lv_array_size(list); i++) {
item = (lv_map_item_t *)lv_array_at(list, i);
if(item->key == key) {
lv_array_remove(list, i);
return;
}
}
}
}
void g2d_print_table(void)
{
LV_LOG("\nHash Table\n-------------------\n");
for(int i = 0; i < table->size; i++) {
if(table->items[i]) {
LV_LOG("Index:%d, Key:%p, Value:%p\n", i, table->items[i] -> key, (void *)table->items[i]->value);
if(table->overflow_list[i]) {
for(uint32_t j = 0 ; j < lv_array_size(table->overflow_list[i]); j++) {
lv_map_item_t * item = (lv_map_item_t *)lv_array_at(table->overflow_list[i], j);
LV_LOG("Index:%d, Key:%p, Value:%p\n", i, item -> key, (void *)item->value);
}
}
}
}
LV_LOG("-------------------\n\n");
}
/**********************
* STATIC FUNCTIONS
**********************/
static unsigned long _map_hash_function(void * ptr)
{
unsigned long i = 0;
char str[64];
G2D_ASSERT_MSG(ptr, "Key is null.");
sprintf(str, "%p", ptr);
for(int j = 0; str[j]; j++)
i += str[j];
return i % LV_G2D_HASH_TABLE_SIZE;
}
static void _handle_collision(unsigned long index, lv_map_item_t * item)
{
if(table->overflow_list[index] == NULL) {
/* Create the list. */
lv_array_t * list = (lv_array_t *) lv_malloc(sizeof(lv_array_t));;
lv_array_init(list, LV_ARRAY_DEFAULT_CAPACITY, sizeof(lv_map_item_t));
lv_array_push_back(list, item);
table->overflow_list[index] = list;
return;
}
else {
lv_array_t * list = (lv_array_t *)table->overflow_list[index];
for(uint32_t i = 0; i < lv_array_size(list); i++) {
lv_map_item_t * it = (lv_map_item_t *)lv_array_at(list, i);
if(it->key == item->key) {
/* Key exists, update value. */
it->value = item->value;
return;
}
}
/* Insert to the list. */
lv_array_push_back(table->overflow_list[index], item);
return;
}
}
static lv_map_item_t * _map_create_item(void * key, struct g2d_buf * value)
{
lv_map_item_t * item = (lv_map_item_t *) lv_malloc(sizeof(lv_map_item_t));
G2D_ASSERT_MSG(item, "Failed to alloc item.");
item->key = key;
item->value = value;
return item;
}
static void _map_free_item(lv_map_item_t * item)
{
/* Also free the g2d_buf. */
g2d_free(item->value);
item->key = NULL;
item->value = NULL;
lv_free(item);
item = NULL;
}
#endif /*LV_USE_DRAW_G2D*/
@@ -0,0 +1,81 @@
/**
* @file lv_g2d_buf_map.h
*
*/
/**
* Copyright 2024 NXP
*
* SPDX-License-Identifier: MIT
*/
#ifndef LV_G2D_BUF_MAP_H
#define LV_G2D_BUF_MAP_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../../../lv_conf_internal.h"
#if LV_USE_DRAW_G2D
#include "../../../misc/lv_array.h"
#include <string.h>
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/* Map item definition. */
typedef struct lv_map_item {
/* Virtual address buffer. */
void * key;
struct g2d_buf * value;
} lv_map_item_t;
/*Buf map definition. */
typedef struct lv_buf_map {
lv_map_item_t ** items;
lv_array_t ** overflow_list;
int size;
int count;
} lv_buf_map_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
void g2d_create_buf_map(void);
void g2d_free_buf_map(void);
void g2d_insert_buf_map(void * key, struct g2d_buf * value);
struct g2d_buf * g2d_search_buf_map(void * key);
void g2d_free_item(void * key);
void g2d_print_table(void);
/**********************
* MACROS
**********************/
#endif /*LV_USE_DRAW_G2D*/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /* LV_G2D_BUF_MAP_H */
@@ -0,0 +1,97 @@
/**
* @file lv_g2d_utils.c
*
*/
/**
* Copyright 2024 NXP
*
* SPDX-License-Identifier: MIT
*/
/*********************
* INCLUDES
*********************/
#include "lv_g2d_utils.h"
#if LV_USE_DRAW_G2D
#include "lv_g2d_buf_map.h"
#include "lv_draw_g2d.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
g2d_format_t g2d_get_buf_format(lv_color_format_t cf)
{
g2d_format_t color_f = G2D_RGB565;
switch(cf) {
case LV_COLOR_FORMAT_RGB565:
color_f = G2D_RGB565;
break;
case LV_COLOR_FORMAT_ARGB8888:
color_f = G2D_BGRA8888;
break;
case LV_COLOR_FORMAT_XRGB8888:
color_f = G2D_BGRX8888;
break;
case LV_COLOR_FORMAT_RGB888:
color_f = G2D_BGR888;
break;
case LV_COLOR_FORMAT_NV12:
color_f = G2D_NV12;
break;
case LV_COLOR_FORMAT_I420:
color_f = G2D_I420;
break;
case LV_COLOR_FORMAT_NV21:
color_f = G2D_NV21;
break;
case LV_COLOR_FORMAT_YUY2:
color_f = G2D_YUYV;
break;
case LV_COLOR_FORMAT_UYVY:
color_f = G2D_UYVY;
break;
default:
G2D_ASSERT_MSG(false, "Unsupported color format.");
break;
}
return color_f;
}
uint32_t g2d_rgba_to_u32(lv_color_t color)
{
return (uint32_t)((color.red) + (color.green << 8) + (color.blue << 16) + ((uint32_t)0xff << 24));
}
int32_t g2d_get_buf_fd(const lv_draw_buf_t * draw_buf)
{
struct g2d_buf * buf = g2d_search_buf_map(draw_buf->data);
G2D_ASSERT_MSG(buf, "Failed to find buffer in map.");
return g2d_buf_export_fd(buf);
}
/**********************
* STATIC FUNCTIONS
**********************/
#endif /*LV_USE_DRAW_G2D*/
@@ -0,0 +1,72 @@
/**
* @file lv_g2d_utils.h
*
*/
/**
* Copyright 2024 NXP
*
* SPDX-License-Identifier: MIT
*/
#ifndef LV_G2D_UTILS_H
#define LV_G2D_UTILS_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../../../lv_conf_internal.h"
#if LV_USE_DRAW_G2D
#include "../../sw/lv_draw_sw_private.h"
#include "g2d.h"
#include "g2dExt.h"
/*********************
* DEFINES
*********************/
#if LV_USE_G2D_ASSERT
#define G2D_ASSERT(expr) LV_ASSERT(expr)
#else
#define G2D_ASSERT(expr)
#endif
#define G2D_ASSERT_MSG(expr, msg) \
do { \
if(!(expr)) { \
LV_LOG_ERROR(msg); \
G2D_ASSERT(false); \
} \
} while(0)
/**********************
* TYPEDEFS
**********************/
typedef enum g2d_format g2d_format_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
g2d_format_t g2d_get_buf_format(lv_color_format_t cf);
uint32_t g2d_rgba_to_u32(lv_color_t color);
int32_t g2d_get_buf_fd(const lv_draw_buf_t * draw_buf);
/**********************
* MACROS
**********************/
#endif /*LV_USE_DRAW_G2D*/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_G2D_UTILS_H*/
@@ -0,0 +1,117 @@
/**
* @file lv_draw_buf_pxp.c
*
*/
/**
* Copyright 2023-2024 NXP
*
* SPDX-License-Identifier: MIT
*/
/*********************
* INCLUDES
*********************/
#include "lv_draw_pxp.h"
#if LV_USE_PXP
#if LV_USE_DRAW_PXP
#include "../../lv_draw_buf_private.h"
#include "lv_pxp_cfg.h"
#include "lv_pxp_utils.h"
#include "lvgl_support.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static void _invalidate_cache(const lv_draw_buf_t * draw_buf, const lv_area_t * area);
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
void lv_draw_buf_pxp_init_handlers(void)
{
lv_draw_buf_handlers_t * handlers = lv_draw_buf_get_handlers();
lv_draw_buf_handlers_t * font_handlers = lv_draw_buf_get_font_handlers();
lv_draw_buf_handlers_t * image_handlers = lv_draw_buf_get_image_handlers();
handlers->invalidate_cache_cb = _invalidate_cache;
font_handlers->invalidate_cache_cb = _invalidate_cache;
image_handlers->invalidate_cache_cb = _invalidate_cache;
}
/**********************
* STATIC FUNCTIONS
**********************/
static void _invalidate_cache(const lv_draw_buf_t * draw_buf, const lv_area_t * area)
{
const lv_image_header_t * header = &draw_buf->header;
uint32_t stride = header->stride;
lv_color_format_t cf = header->cf;
if(area->y1 == 0) {
uint32_t size = stride * lv_area_get_height(area);
/* Invalidate full buffer. */
DEMO_CleanInvalidateCacheByAddr((void *)draw_buf->data, size);
return;
}
const uint8_t * buf_u8 = draw_buf->data;
/* ARM require a 32 byte aligned address. */
uint8_t align_bytes = 32;
uint8_t bits_per_pixel = lv_color_format_get_bpp(cf);
uint16_t align_pixels = align_bytes * 8 / bits_per_pixel;
uint16_t offset_x = 0;
if(area->x1 >= (int32_t)(area->x1 % align_pixels)) {
uint16_t shift_x = area->x1 - (area->x1 % align_pixels);
offset_x = area->x1 - shift_x;
buf_u8 += (shift_x * bits_per_pixel) / 8;
}
if(area->y1) {
uint16_t shift_y = area->y1;
buf_u8 += shift_y * stride;
}
/* Area to clear can start from a different offset in buffer.
* Invalidate the area line by line.
*/
uint16_t line_pixels = offset_x + lv_area_get_width(area);
uint16_t line_size = (line_pixels * bits_per_pixel) / 8;
uint16_t area_height = lv_area_get_height(area);
for(uint16_t y = 0; y < area_height; y++) {
const void * line_addr = buf_u8 + y * stride;
DEMO_CleanInvalidateCacheByAddr((void *)line_addr, line_size);
}
}
#endif /*LV_USE_DRAW_PXP*/
#endif /*LV_USE_PXP*/
@@ -0,0 +1,501 @@
/**
* @file lv_draw_pxp.c
*
*/
/**
* Copyright 2022-2024 NXP
*
* SPDX-License-Identifier: MIT
*/
/*********************
* INCLUDES
*********************/
#include "lv_draw_pxp.h"
#if LV_USE_PXP
#if LV_USE_DRAW_PXP || LV_USE_ROTATE_PXP
#include "lv_pxp_cfg.h"
#include "lv_pxp_utils.h"
#if LV_USE_PARALLEL_DRAW_DEBUG
#include "../../../core/lv_global.h"
#endif
/*********************
* DEFINES
*********************/
#define DRAW_UNIT_ID_PXP 3
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
/*
* 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);
/*
* 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);
/*
* Delete the PXP draw unit.
*/
static int32_t _pxp_delete(lv_draw_unit_t * draw_unit);
#if LV_USE_PXP_DRAW_THREAD
static void _pxp_render_thread_cb(void * ptr);
#endif
static void _pxp_execute_drawing(lv_draw_pxp_unit_t * u);
/**********************
* STATIC PROTOTYPES
**********************/
/**********************
* STATIC VARIABLES
**********************/
#if LV_USE_PARALLEL_DRAW_DEBUG
#define _draw_info LV_GLOBAL_DEFAULT()->draw_info
#endif
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
void lv_draw_pxp_init(void)
{
lv_pxp_init();
#if LV_USE_DRAW_PXP
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.evaluate_cb = _pxp_evaluate;
draw_pxp_unit->base_unit.dispatch_cb = _pxp_dispatch;
draw_pxp_unit->base_unit.delete_cb = _pxp_delete;
draw_pxp_unit->base_unit.name = "NXP_PXP";
#if LV_USE_PXP_DRAW_THREAD
lv_thread_init(&draw_pxp_unit->thread, "pxpdraw", LV_DRAW_THREAD_PRIO, _pxp_render_thread_cb, 2 * 1024, draw_pxp_unit);
#endif
#endif /*LV_USE_DRAW_PXP*/
}
void lv_draw_pxp_deinit(void)
{
lv_pxp_deinit();
}
void lv_draw_pxp_rotate(const void * src_buf, void * dest_buf, int32_t src_width, int32_t src_height,
int32_t src_stride, int32_t dest_stride, lv_display_rotation_t rotation,
lv_color_format_t cf)
{
lv_pxp_reset();
/* Convert rotation angle
* To be in sync with CPU, the received angle is counterclockwise
* and the PXP constants are for clockwise rotation
*
* counterclockwise clockwise
* LV_DISPLAY_ROTATION_90 -> kPXP_Rotate270
* LV_DISPLAY_ROTATION_270 -> kPXP_Rotate90
*/
pxp_rotate_degree_t pxp_rotation;
switch(rotation) {
case LV_DISPLAY_ROTATION_0:
pxp_rotation = kPXP_Rotate0;
break;
case LV_DISPLAY_ROTATION_270:
pxp_rotation = kPXP_Rotate90;
break;
case LV_DISPLAY_ROTATION_180:
pxp_rotation = kPXP_Rotate180;
break;
case LV_DISPLAY_ROTATION_90:
pxp_rotation = kPXP_Rotate270;
break;
default:
pxp_rotation = kPXP_Rotate0;
break;
}
PXP_SetRotateConfig(PXP_ID, kPXP_RotateOutputBuffer, pxp_rotation, kPXP_FlipDisable);
/*Simple blit, no effect - Disable PS buffer*/
PXP_SetProcessSurfacePosition(PXP_ID, 0xFFFFU, 0xFFFFU, 0U, 0U);
/*AS buffer - source image*/
pxp_as_buffer_config_t asBufferConfig = {
.pixelFormat = pxp_get_as_px_format(cf),
.bufferAddr = (uint32_t)src_buf,
.pitchBytes = src_stride
};
PXP_SetAlphaSurfaceBufferConfig(PXP_ID, &asBufferConfig);
PXP_SetAlphaSurfacePosition(PXP_ID, 0U, 0U, src_width - 1U, src_height - 1U);
PXP_EnableAlphaSurfaceOverlayColorKey(PXP_ID, false);
/*Output buffer.*/
pxp_output_buffer_config_t outputBufferConfig = {
.pixelFormat = pxp_get_out_px_format(cf),
.interlacedMode = kPXP_OutputProgressive,
.buffer0Addr = (uint32_t)dest_buf,
.buffer1Addr = (uint32_t)0U,
.pitchBytes = dest_stride,
.width = src_width,
.height = src_height
};
PXP_SetOutputBufferConfig(PXP_ID, &outputBufferConfig);
lv_pxp_run();
}
/**********************
* STATIC FUNCTIONS
**********************/
#if LV_USE_DRAW_PXP
static inline bool _pxp_src_cf_supported(lv_color_format_t cf)
{
bool is_cf_supported = false;
switch(cf) {
case LV_COLOR_FORMAT_RGB565:
case LV_COLOR_FORMAT_ARGB8888:
case LV_COLOR_FORMAT_XRGB8888:
is_cf_supported = true;
break;
default:
break;
}
return is_cf_supported;
}
static inline bool _pxp_dest_cf_supported(lv_color_format_t cf)
{
bool is_cf_supported = false;
switch(cf) {
case LV_COLOR_FORMAT_RGB565:
case LV_COLOR_FORMAT_RGB888:
case LV_COLOR_FORMAT_ARGB8888:
case LV_COLOR_FORMAT_XRGB8888:
is_cf_supported = true;
break;
default:
break;
}
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 is_tiled = draw_dsc->tile;
/* Tiled image (repeat image) is currently not supported. */
if(is_tiled)
return false;
bool has_recolor = (draw_dsc->recolor_opa > LV_OPA_MIN);
bool has_transform = (draw_dsc->rotation != 0 || draw_dsc->scale_x != LV_SCALE_NONE ||
draw_dsc->scale_y != 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);
const lv_draw_dsc_base_t * draw_dsc_base = (lv_draw_dsc_base_t *) t->draw_dsc;
if(!_pxp_dest_cf_supported(draw_dsc_base->layer->color_format))
return 0;
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_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;
if(!_pxp_src_cf_supported(layer_to_draw->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(img_dsc->header.cf >= LV_COLOR_FORMAT_PROPRIETARY_START)
return 0;
if((!_pxp_src_cf_supported(img_dsc->header.cf)) ||
(!pxp_buf_aligned(img_dsc->data, img_dsc->header.stride)))
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;
/* Try to get an ready to draw. */
lv_draw_task_t * t = lv_draw_get_available_task(layer, NULL, DRAW_UNIT_ID_PXP);
if(t == NULL || t->preferred_draw_unit_id != DRAW_UNIT_ID_PXP)
return LV_DRAW_UNIT_IDLE;
if(lv_draw_layer_alloc_buf(layer) == NULL)
return LV_DRAW_UNIT_IDLE;
t->state = LV_DRAW_TASK_STATE_IN_PROGRESS;
draw_pxp_unit->task_act = t;
#if LV_USE_PXP_DRAW_THREAD
/* Let the render thread work. */
if(draw_pxp_unit->inited)
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 int32_t _pxp_delete(lv_draw_unit_t * draw_unit)
{
#if LV_USE_PXP_DRAW_THREAD
lv_draw_pxp_unit_t * draw_pxp_unit = (lv_draw_pxp_unit_t *) draw_unit;
LV_LOG_INFO("Cancel PXP draw thread.");
draw_pxp_unit->exit_status = true;
if(draw_pxp_unit->inited)
lv_thread_sync_signal(&draw_pxp_unit->sync);
lv_result_t res = lv_thread_delete(&draw_pxp_unit->thread);
return res;
#else
LV_UNUSED(draw_unit);
return 0;
#endif
}
static void _pxp_execute_drawing(lv_draw_pxp_unit_t * u)
{
lv_draw_task_t * t = u->task_act;
lv_layer_t * layer = t->target_layer;
lv_draw_buf_t * draw_buf = layer->draw_buf;
lv_area_t draw_area;
if(!lv_area_intersect(&draw_area, &t->area, &t->clip_area))
return; /*Fully clipped, nothing to do*/
/* Make area relative to the buffer */
lv_area_move(&draw_area, -layer->buf_area.x1, -layer->buf_area.y1);
/* Invalidate only the drawing area */
lv_draw_buf_invalidate_cache(draw_buf, &draw_area);
#if LV_USE_PARALLEL_DRAW_DEBUG
t->draw_unit = &u->base_unit;
#endif
switch(t->type) {
case LV_DRAW_TASK_TYPE_FILL:
lv_draw_pxp_fill(t);
break;
case LV_DRAW_TASK_TYPE_IMAGE:
lv_draw_pxp_img(t);
break;
case LV_DRAW_TASK_TYPE_LAYER:
lv_draw_pxp_layer(t);
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, &t->clip_area))
return;
int32_t idx = u->base_unit.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_sw_fill((lv_draw_unit_t *)u, &rect_dsc, &draw_area);
lv_point_t txt_size;
lv_text_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_sw_fill((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_sw_label((lv_draw_unit_t *)u, &label_dsc, &txt_area);
}
#endif
}
#if LV_USE_PXP_DRAW_THREAD
static void _pxp_render_thread_cb(void * ptr)
{
lv_draw_pxp_unit_t * u = ptr;
lv_thread_sync_init(&u->sync);
u->inited = true;
while(1) {
/* Wait for sync if there is no task set. */
while(u->task_act == NULL) {
if(u->exit_status)
break;
lv_thread_sync_wait(&u->sync);
}
if(u->exit_status) {
LV_LOG_INFO("Ready to exit PXP draw thread.");
break;
}
_pxp_execute_drawing(u);
/* Signal the ready state to dispatcher. */
u->task_act->state = LV_DRAW_TASK_STATE_READY;
/* 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();
}
u->inited = false;
lv_thread_sync_delete(&u->sync);
LV_LOG_INFO("Exit PXP draw thread.");
}
#endif
#endif /*LV_USE_DRAW_PXP*/
#endif /*LV_USE_DRAW_PXP || LV_USE_ROTATE_PXP*/
#endif /*LV_USE_PXP*/
@@ -0,0 +1,82 @@
/**
* @file lv_draw_pxp.h
*
*/
/**
* Copyright 2022-2024 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_PXP
#if LV_USE_DRAW_PXP || LV_USE_ROTATE_PXP
#include "../../lv_draw_private.h"
#include "../../../display/lv_display_private.h"
#include "../../../misc/lv_area_private.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
typedef struct lv_draw_pxp_unit_t {
lv_draw_unit_t base_unit;
lv_draw_task_t * task_act;
#if LV_USE_OS
lv_thread_sync_t sync;
lv_thread_t thread;
volatile bool inited;
volatile bool exit_status;
#endif
} lv_draw_pxp_unit_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
void lv_draw_pxp_init(void);
void lv_draw_pxp_deinit(void);
void lv_draw_pxp_rotate(const void * src_buf, void * dest_buf, int32_t src_width, int32_t src_height,
int32_t src_stride, int32_t dest_stride, lv_display_rotation_t rotation,
lv_color_format_t cf);
#if LV_USE_DRAW_PXP
void lv_draw_buf_pxp_init_handlers(void);
void lv_draw_pxp_fill(lv_draw_task_t * t);
void lv_draw_pxp_img(lv_draw_task_t * t);
void lv_draw_pxp_layer(lv_draw_task_t * t);
/**********************
* MACROS
**********************/
#endif /*LV_USE_DRAW_PXP*/
#endif /*LV_USE_DRAW_PXP || LV_USE_ROTATE_PXP*/
#endif /*LV_USE_PXP*/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_DRAW_PXP_H*/
@@ -0,0 +1,151 @@
/**
* @file lv_draw_pxp_fill.c
*
*/
/**
* Copyright 2020-2024 NXP
*
* SPDX-License-Identifier: MIT
*/
/*********************
* INCLUDES
*********************/
#include "lv_draw_pxp.h"
#if LV_USE_PXP
#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, int32_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_task_t * t)
{
const lv_draw_fill_dsc_t * dsc = t->draw_dsc;
const lv_area_t * coords = &t->area;
if(dsc->opa <= (lv_opa_t)LV_OPA_MIN)
return;
lv_layer_t * layer = t->target_layer;
lv_draw_buf_t * draw_buf = layer->draw_buf;
lv_area_t rel_coords;
lv_area_copy(&rel_coords, coords);
lv_area_move(&rel_coords, -layer->buf_area.x1, -layer->buf_area.y1);
lv_area_t rel_clip_area;
lv_area_copy(&rel_clip_area, &t->clip_area);
lv_area_move(&rel_clip_area, -layer->buf_area.x1, -layer->buf_area.y1);
lv_area_t blend_area;
if(!lv_area_intersect(&blend_area, &rel_coords, &rel_clip_area))
return; /*Fully clipped, nothing to do*/
_pxp_fill(draw_buf->data, &blend_area, draw_buf->header.stride, draw_buf->header.cf, dsc);
}
/**********************
* STATIC FUNCTIONS
**********************/
static void _pxp_fill(uint8_t * dest_buf, const lv_area_t * dest_area, int32_t dest_stride,
lv_color_format_t dest_cf, const lv_draw_fill_dsc_t * dsc)
{
int32_t dest_w = lv_area_get_width(dest_area);
int32_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*/
#endif /*LV_USE_PXP*/
@@ -0,0 +1,369 @@
/**
* @file lv_draw_pxp_img.c
*
*/
/**
* Copyright 2020-2024 NXP
*
* SPDX-License-Identifier: MIT
*/
/*********************
* INCLUDES
*********************/
#include "lv_draw_pxp.h"
#if LV_USE_PXP
#if LV_USE_DRAW_PXP
#include "lv_pxp_cfg.h"
#include "lv_pxp_utils.h"
#include <math.h>
/*********************
* 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, int32_t dest_stride,
lv_color_format_t dest_cf, const uint8_t * src_buf, const lv_area_t * src_area,
int32_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, int32_t dest_stride,
lv_color_format_t dest_cf, const uint8_t * src_buf, const lv_area_t * src_area,
int32_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, int32_t dest_stride,
lv_color_format_t dest_cf, const uint8_t * src_buf, const lv_area_t * src_area,
int32_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_task_t * t)
{
const lv_draw_image_dsc_t * dsc = t->draw_dsc;
const lv_area_t * coords = &t->area;
if(dsc->opa <= (lv_opa_t)LV_OPA_MIN)
return;
lv_layer_t * layer = t->target_layer;
lv_draw_buf_t * draw_buf = layer->draw_buf;
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->buf_area.x1, -layer->buf_area.y1);
lv_area_t rel_clip_area;
lv_area_copy(&rel_clip_area, &t->clip_area);
lv_area_move(&rel_clip_area, -layer->buf_area.x1, -layer->buf_area.y1);
lv_area_t blend_area;
bool has_transform = (dsc->rotation != 0 || dsc->scale_x != LV_SCALE_NONE || dsc->scale_y != 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->buf_area.x1);
src_area.y1 = blend_area.y1 - (coords->y1 - layer->buf_area.y1);
src_area.x2 = src_area.x1 + lv_area_get_width(&blend_area) - 1;
src_area.y2 = src_area.y1 + lv_area_get_height(&blend_area) - 1;
int32_t src_stride = img_dsc->header.stride;
lv_color_format_t src_cf = img_dsc->header.cf;
uint8_t * dest_buf = draw_buf->data;
int32_t dest_stride = draw_buf->header.stride;
lv_color_format_t dest_cf = draw_buf->header.cf;
bool has_recolor = (dsc->recolor_opa > LV_OPA_MIN);
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, int32_t dest_stride,
lv_color_format_t dest_cf, const uint8_t * src_buf, const lv_area_t * src_area,
int32_t src_stride, lv_color_format_t src_cf, const lv_draw_image_dsc_t * dsc)
{
int32_t dest_w = lv_area_get_width(dest_area);
int32_t dest_h = lv_area_get_height(dest_area);
int32_t src_w = lv_area_get_width(src_area);
int32_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, int32_t dest_stride,
lv_color_format_t dest_cf, const uint8_t * src_buf, const lv_area_t * src_area,
int32_t src_stride, lv_color_format_t src_cf, const lv_draw_image_dsc_t * dsc)
{
int32_t src_w = lv_area_get_width(src_area);
int32_t src_h = lv_area_get_height(src_area);
int32_t dest_w = lv_area_get_width(dest_area);
int32_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*/
int32_t piv_offset_x = 0;
int32_t piv_offset_y = 0;
int32_t trim_x = 0;
int32_t trim_y = 0;
bool has_rotation = (dsc->rotation != 0);
bool has_scale = (dsc->scale_x != LV_SCALE_NONE || dsc->scale_y != 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 fp_scale_x = (float)dsc->scale_x / LV_SCALE_NONE;
float fp_scale_y = (float)dsc->scale_y / LV_SCALE_NONE;
int32_t int_scale_x = (int32_t)fp_scale_x;
int32_t int_scale_y = (int32_t)fp_scale_y;
/*Any scale_factor in (k, k + 1] will result in a trim equal to k*/
trim_x = (fp_scale_x == int_scale_x) ? int_scale_x - 1 : int_scale_x;
trim_y = (fp_scale_y == int_scale_y) ? int_scale_y - 1 : int_scale_y;
dest_w = src_w * fp_scale_x + trim_x;
dest_h = src_h * fp_scale_y + trim_y;
/*Final pivot offset = scale_factor * rotation_pivot_offset + scaling_pivot_offset*/
piv_offset_x = floorf(fp_scale_x * piv_offset_x) - floorf((fp_scale_x - 1) * pivot.x);
piv_offset_y = floorf(fp_scale_y * piv_offset_y) - floorf((fp_scale_y - 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_x - 1U, dest_h - trim_y - 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_x,
.height = dest_h - trim_y
};
PXP_SetOutputBufferConfig(PXP_ID, &outputBufferConfig);
lv_pxp_run();
}
static void _pxp_blit(uint8_t * dest_buf, const lv_area_t * dest_area, int32_t dest_stride,
lv_color_format_t dest_cf, const uint8_t * src_buf, const lv_area_t * src_area,
int32_t src_stride, lv_color_format_t src_cf, lv_opa_t opa)
{
int32_t dest_w = lv_area_get_width(dest_area);
int32_t dest_h = lv_area_get_height(dest_area);
int32_t src_w = lv_area_get_width(src_area);
int32_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*/
#endif /*LV_USE_PXP*/
@@ -0,0 +1,157 @@
/**
* @file lv_draw_pxp_layer.c
*
*/
/**
* Copyright 2023-2024 NXP
*
* SPDX-License-Identifier: MIT
*/
/*********************
* INCLUDES
*********************/
#include "lv_draw_pxp.h"
#if LV_USE_PXP
#if LV_USE_DRAW_PXP
#include "../../../stdlib/lv_string.h"
#if LV_USE_PARALLEL_DRAW_DEBUG
#include "../../../core/lv_global.h"
#endif
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
/**********************
* STATIC VARIABLES
**********************/
#if LV_USE_PARALLEL_DRAW_DEBUG
#define _draw_info LV_GLOBAL_DEFAULT()->draw_info
#endif
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
void lv_draw_pxp_layer(lv_draw_task_t * t)
{
lv_draw_image_dsc_t * draw_dsc = t->draw_dsc;
lv_layer_t * layer_to_draw = (lv_layer_t *)draw_dsc->src;
const lv_draw_buf_t * draw_buf = layer_to_draw->draw_buf;
/* It can happen that nothing was draw on a layer and therefore its buffer is not allocated.
* In this case just return.
*/
if(draw_buf == NULL)
return;
const lv_area_t area_to_draw = {
.x1 = 0,
.y1 = 0,
.x2 = draw_buf->header.w - 1,
.y2 = draw_buf->header.h - 1
};
lv_draw_buf_invalidate_cache(draw_buf, &area_to_draw);
lv_draw_image_dsc_t new_draw_dsc = *draw_dsc;
new_draw_dsc.src = draw_buf;
t->draw_dsc = &new_draw_dsc;
lv_draw_pxp_img(t);
t->draw_dsc = draw_dsc;
#if LV_USE_LAYER_DEBUG || LV_USE_PARALLEL_DRAW_DEBUG
const lv_area_t * coords = &t->area;
lv_area_t area_rot;
lv_area_copy(&area_rot, coords);
if(draw_dsc->rotation || draw_dsc->scale_x != LV_SCALE_NONE || draw_dsc->scale_y != LV_SCALE_NONE) {
int32_t w = lv_area_get_width(coords);
int32_t h = lv_area_get_height(coords);
lv_image_buf_get_transformed_area(&area_rot, w, h, draw_dsc->rotation, draw_dsc->scale_x, draw_dsc->scale_y,
&draw_dsc->pivot);
area_rot.x1 += coords->x1;
area_rot.y1 += coords->y1;
area_rot.x2 += coords->x1;
area_rot.y2 += coords->y1;
}
lv_area_t draw_area;
if(!lv_area_intersect(&draw_area, &area_rot, &t->clip_area)) return;
#endif
#if LV_USE_LAYER_DEBUG
lv_draw_fill_dsc_t fill_dsc;
lv_draw_fill_dsc_init(&fill_dsc);
fill_dsc.color = lv_color_hex(layer_to_draw->color_format == LV_COLOR_FORMAT_ARGB8888 ? 0xff0000 : 0x00ff00);
fill_dsc.opa = LV_OPA_20;
lv_draw_sw_fill(t, &fill_dsc, &area_rot);
lv_draw_border_dsc_t border_dsc;
lv_draw_border_dsc_init(&border_dsc);
border_dsc.color = fill_dsc.color;
border_dsc.opa = LV_OPA_60;
border_dsc.width = 2;
lv_draw_sw_border(t, &border_dsc, &area_rot);
#endif
#if LV_USE_PARALLEL_DRAW_DEBUG
int32_t idx = t->draw_unit->idx;
lv_draw_fill_dsc_t fill_dsc;
lv_draw_rect_dsc_init(&fill_dsc);
fill_dsc.color = lv_palette_main(idx % LV_PALETTE_LAST);
fill_dsc.opa = LV_OPA_10;
lv_draw_sw_fill(t, &fill_dsc, &area_rot);
lv_draw_border_dsc_t border_dsc;
lv_draw_border_dsc_init(&border_dsc);
border_dsc.color = lv_palette_main(idx % LV_PALETTE_LAST);
border_dsc.opa = LV_OPA_100;
border_dsc.width = 2;
lv_draw_sw_border(t, &border_dsc, &area_rot);
lv_point_t txt_size;
lv_text_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.x2 = draw_area.x1 + txt_size.x - 1;
txt_area.y2 = draw_area.y2;
txt_area.y1 = draw_area.y2 - txt_size.y + 1;
lv_draw_fill_dsc_init(&fill_dsc);
fill_dsc.color = lv_color_black();
lv_draw_sw_fill(t, &fill_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_white();
label_dsc.text = buf;
lv_draw_sw_label(t, &label_dsc, &txt_area);
#endif
}
#endif /*LV_USE_DRAW_PXP*/
#endif /*LV_USE_PXP*/
@@ -0,0 +1,93 @@
/**
* @file lv_pxp_cfg.c
*
*/
/**
* Copyright 2020-2023 NXP
*
* SPDX-License-Identifier: MIT
*/
/*********************
* INCLUDES
*********************/
#include "lv_pxp_cfg.h"
#if LV_USE_PXP
#if LV_USE_DRAW_PXP || LV_USE_ROTATE_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();
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 || LV_USE_ROTATE_PXP*/
#endif /*LV_USE_PXP*/
@@ -0,0 +1,104 @@
/**
* @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_PXP
#if LV_USE_DRAW_PXP || LV_USE_ROTATE_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 || LV_USE_ROTATE_PXP*/
#endif /*LV_USE_PXP*/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_PXP_CFG_H*/
@@ -0,0 +1,188 @@
/**
* @file lv_pxp_osa.c
*
*/
/**
* Copyright 2020, 2022-2023 NXP
*
* SPDX-License-Identifier: MIT
*/
/*********************
* INCLUDES
*********************/
#include "lv_pxp_osa.h"
#if LV_USE_PXP
#if LV_USE_DRAW_PXP || LV_USE_ROTATE_PXP
#include "lv_pxp_utils.h"
#include "../../../misc/lv_log.h"
#include "../../../osal/lv_os.h"
#include "fsl_pxp.h"
#if defined(SDK_OS_FREE_RTOS)
#include "FreeRTOS.h"
#endif
#if defined(__ZEPHYR__)
#include <zephyr/kernel.h>
#include <zephyr/irq.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);
#if defined(__ZEPHYR__)
/**
* Interrupt handler for Zephyr IRQ
*/
static void _pxp_zephyr_irq_handler(void *);
#endif
/**********************
* STATIC VARIABLES
**********************/
#if LV_USE_OS
static lv_thread_sync_t pxp_sync;
#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(kPXP_CompleteFlag & PXP_GetStatusFlags(PXP_ID)) {
PXP_ClearStatusFlags(PXP_ID, kPXP_CompleteFlag);
#if LV_USE_OS
lv_thread_sync_signal_isr(&pxp_sync);
#else
ucPXPIdle = true;
#endif
}
}
pxp_cfg_t * pxp_get_default_cfg(void)
{
return &_pxp_default_cfg;
}
/**********************
* STATIC FUNCTIONS
**********************/
#if defined(__ZEPHYR__)
static void _pxp_zephyr_irq_handler(void *)
{
PXP_IRQHandler();
}
#endif
static void _pxp_interrupt_init(void)
{
#if LV_USE_OS
if(lv_thread_sync_init(&pxp_sync) != LV_RESULT_OK) {
PXP_ASSERT_MSG(false, "Failed to init thread_sync.");
}
#endif
#if defined(__ZEPHYR__)
IRQ_CONNECT(DT_IRQN(DT_NODELABEL(pxp)), CONFIG_LV_Z_PXP_INTERRUPT_PRIORITY, _pxp_zephyr_irq_handler, NULL, 0);
irq_enable(DT_IRQN(DT_NODELABEL(pxp)));
#elif defined(SDK_OS_FREE_RTOS)
NVIC_SetPriority(PXP_IRQ_ID, configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY + 1);
#endif
#if !defined(__ZEPHYR__)
NVIC_EnableIRQ(PXP_IRQ_ID);
#endif
ucPXPIdle = true;
}
static void _pxp_interrupt_deinit(void)
{
#if defined(__ZEPHYR__)
irq_disable(DT_IRQN(DT_NODELABEL(pxp)));
#else
NVIC_DisableIRQ(PXP_IRQ_ID);
#endif
#if LV_USE_OS
lv_thread_sync_delete(&pxp_sync);
#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(ucPXPIdle == true)
return;
#if LV_USE_OS
if(lv_thread_sync_wait(&pxp_sync) == LV_RESULT_OK)
ucPXPIdle = true;
#else
while(ucPXPIdle == false) {
}
#endif
}
#endif /*LV_USE_DRAW_PXP || LV_USE_ROTATE_PXP*/
#endif /*LV_USE_PXP*/
@@ -0,0 +1,62 @@
/**
* @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_PXP
#if LV_USE_DRAW_PXP || LV_USE_ROTATE_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 || LV_USE_ROTATE_PXP*/
#endif /*LV_USE_PXP*/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_PXP_OSA_H*/
@@ -0,0 +1,149 @@
/**
* @file lv_pxp_utils.c
*
*/
/**
* Copyright 2023-2024 NXP
*
* SPDX-License-Identifier: MIT
*/
/*********************
* INCLUDES
*********************/
#include "lv_pxp_utils.h"
#if LV_USE_PXP
#if LV_USE_DRAW_PXP || LV_USE_ROTATE_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) {
case LV_COLOR_FORMAT_RGB565:
out_px_format = kPXP_OutputPixelFormatRGB565;
break;
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:
PXP_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) {
case LV_COLOR_FORMAT_RGB565:
as_px_format = kPXP_AsPixelFormatRGB565;
break;
case LV_COLOR_FORMAT_RGB888:
PXP_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:
PXP_ASSERT_MSG(false, "Unsupported color format.");
break;
}
return as_px_format;
}
#if LV_USE_DRAW_PXP
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) {
case LV_COLOR_FORMAT_RGB565:
ps_px_format = kPXP_PsPixelFormatRGB565;
break;
case LV_COLOR_FORMAT_RGB888:
PXP_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
PXP_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:
PXP_ASSERT_MSG(false, "Unsupported color format.");
break;
}
return ps_px_format;
}
bool pxp_buf_aligned(const void * buf, uint32_t stride)
{
/* Test for pointer alignment */
if((uintptr_t)buf % 64)
return false;
/* Test for invalid stride (no stride alignment required) */
if(stride == 0)
return false;
return true;
}
/**********************
* STATIC FUNCTIONS
**********************/
#endif /*LV_USE_DRAW_PXP*/
#endif /*LV_USE_DRAW_PXP || LV_USE_ROTATE_PXP*/
#endif /*LV_USE_PXP*/
@@ -0,0 +1,84 @@
/**
* @file lv_pxp_utils.h
*
*/
/**
* Copyright 2023-2024 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_PXP
#if LV_USE_DRAW_PXP || LV_USE_ROTATE_PXP
#include "fsl_pxp.h"
#include "../../../misc/lv_color.h"
/*********************
* DEFINES
*********************/
#if LV_USE_PXP_ASSERT
#define PXP_ASSERT(expr) LV_ASSERT(expr)
#else
#define PXP_ASSERT(expr)
#endif
#define PXP_ASSERT_MSG(expr, msg) \
do { \
if(!(expr)) { \
LV_LOG_ERROR(msg); \
PXP_ASSERT(false); \
} \
} while(0)
/**********************
* 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);
#if LV_USE_DRAW_PXP
pxp_ps_pixel_format_t pxp_get_ps_px_format(lv_color_format_t cf);
bool pxp_buf_aligned(const void * buf, uint32_t stride);
/**********************
* MACROS
**********************/
/**********************
* STATIC FUNCTIONS
**********************/
#endif /*LV_USE_DRAW_PXP*/
#endif /*LV_USE_DRAW_PXP || LV_USE_ROTATE_PXP*/
#endif /*LV_USE_PXP*/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_PXP_UTILS_H*/
@@ -0,0 +1,131 @@
/**
* @file lv_draw_buf_vglite.c
*
*/
/**
* Copyright 2023-2024 NXP
*
* SPDX-License-Identifier: MIT
*/
/*********************
* INCLUDES
*********************/
#include "lv_draw_vglite.h"
#if LV_USE_DRAW_VGLITE
#include "../../lv_draw_buf_private.h"
#include "lv_vglite_buf.h"
#include "lv_vglite_utils.h"
#include "lvgl_support.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static void _invalidate_cache(const lv_draw_buf_t * draw_buf, const lv_area_t * area);
static uint32_t _width_to_stride(uint32_t w, 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();
lv_draw_buf_handlers_t * font_handlers = lv_draw_buf_get_font_handlers();
lv_draw_buf_handlers_t * image_handlers = lv_draw_buf_get_image_handlers();
handlers->invalidate_cache_cb = _invalidate_cache;
font_handlers->invalidate_cache_cb = _invalidate_cache;
image_handlers->invalidate_cache_cb = _invalidate_cache;
handlers->width_to_stride_cb = _width_to_stride;
font_handlers->width_to_stride_cb = _width_to_stride;
image_handlers->width_to_stride_cb = _width_to_stride;
}
/**********************
* STATIC FUNCTIONS
**********************/
static void _invalidate_cache(const lv_draw_buf_t * draw_buf, const lv_area_t * area)
{
const lv_image_header_t * header = &draw_buf->header;
uint32_t stride = header->stride;
lv_color_format_t cf = header->cf;
if(area->y1 == 0) {
uint32_t size = stride * lv_area_get_height(area);
/* Invalidate full buffer. */
DEMO_CleanInvalidateCacheByAddr((void *)draw_buf->data, size);
return;
}
const uint8_t * buf_u8 = draw_buf->data;
/* ARM require a 32 byte aligned address. */
uint8_t align_bytes = 32;
uint8_t bits_per_pixel = lv_color_format_get_bpp(cf);
uint16_t align_pixels = align_bytes * 8 / bits_per_pixel;
uint16_t offset_x = 0;
if(area->x1 >= (int32_t)(area->x1 % align_pixels)) {
uint16_t shift_x = area->x1 - (area->x1 % align_pixels);
offset_x = area->x1 - shift_x;
buf_u8 += (shift_x * bits_per_pixel) / 8;
}
if(area->y1) {
uint16_t shift_y = area->y1;
buf_u8 += shift_y * stride;
}
/* Area to clear can start from a different offset in buffer.
* Invalidate the area line by line.
*/
uint16_t line_pixels = offset_x + lv_area_get_width(area);
uint16_t line_size = (line_pixels * bits_per_pixel) / 8;
uint16_t area_height = lv_area_get_height(area);
for(uint16_t y = 0; y < area_height; y++) {
const void * line_addr = buf_u8 + y * stride;
DEMO_CleanInvalidateCacheByAddr((void *)line_addr, line_size);
}
}
static uint32_t _width_to_stride(uint32_t w, lv_color_format_t cf)
{
uint8_t bits_per_pixel = lv_color_format_get_bpp(cf);
uint32_t width_bits = LV_ROUND_UP(w * bits_per_pixel, 8);
uint32_t width_bytes = width_bits / 8;
uint8_t align_bytes = vglite_get_stride_alignment(cf);
return LV_ROUND_UP(width_bytes, align_bytes);
}
#endif /*LV_USE_DRAW_VGLITE*/
@@ -0,0 +1,638 @@
/**
* @file lv_draw_vglite.c
*
*/
/**
* Copyright 2023-2024 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 "../../../core/lv_global.h"
/*********************
* DEFINES
*********************/
#define DRAW_UNIT_ID_VGLITE 2
#if LV_USE_VGLITE_DRAW_ASYNC
#define VGLITE_TASK_BUF_SIZE 100
#endif
/**********************
* TYPEDEFS
**********************/
#if LV_USE_VGLITE_DRAW_ASYNC
/**
* Structure of pending vglite draw task
*/
typedef struct _vglite_flush_task {
vglite_draw_task_t * task;
bool flushed;
} vglite_flush_task_t;
#endif
/**********************
* STATIC PROTOTYPES
**********************/
static inline void _vglite_cleanup_task(vglite_draw_task_t * task);
/*
* Evaluate a task and set the score and preferred VGLite draw 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);
/*
* Dispatch (assign) a task to VGLite draw unit (itself).
* 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);
/*
* Wait for VG-Lite draw unit to finish.
*/
#if LV_USE_VGLITE_DRAW_ASYNC
static int32_t _vglite_wait_for_finish(lv_draw_unit_t * draw_unit);
#endif
/*
* Delete the VGLite draw unit.
*/
static int32_t _vglite_delete(lv_draw_unit_t * draw_unit);
#if LV_USE_VGLITE_DRAW_THREAD
static void _vglite_render_thread_cb(void * ptr);
#endif
static void _vglite_execute_drawing(lv_draw_vglite_unit_t * u);
/**********************
* STATIC VARIABLES
**********************/
#define _draw_info LV_GLOBAL_DEFAULT()->draw_info
#if LV_USE_VGLITE_DRAW_ASYNC
/*
* 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_flush_task_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.evaluate_cb = _vglite_evaluate;
draw_vglite_unit->base_unit.dispatch_cb = _vglite_dispatch;
#if LV_USE_VGLITE_DRAW_ASYNC
draw_vglite_unit->base_unit.wait_for_finish_cb = _vglite_wait_for_finish;
#endif
draw_vglite_unit->base_unit.delete_cb = _vglite_delete;
draw_vglite_unit->base_unit.name = "NXP_VGLITE";
#if LV_USE_VGLITE_DRAW_THREAD
lv_thread_init(&draw_vglite_unit->thread, "vglitedraw", LV_DRAW_THREAD_PRIO, _vglite_render_thread_cb, 4 * 1024,
draw_vglite_unit);
#endif
}
void lv_draw_vglite_deinit(void)
{
}
/**********************
* STATIC FUNCTIONS
**********************/
static inline bool _vglite_src_cf_supported(lv_color_format_t cf)
{
bool is_cf_supported = false;
switch(cf) {
#if CHIPID == 0x255 || CHIPID == 0x555
case LV_COLOR_FORMAT_I1:
case LV_COLOR_FORMAT_I2:
case LV_COLOR_FORMAT_I4:
case LV_COLOR_FORMAT_I8:
#endif
case LV_COLOR_FORMAT_A4:
case LV_COLOR_FORMAT_A8:
case LV_COLOR_FORMAT_L8:
case LV_COLOR_FORMAT_RGB565:
#if CHIPID == 0x555
case LV_COLOR_FORMAT_ARGB8565:
case LV_COLOR_FORMAT_RGB888:
#endif
case LV_COLOR_FORMAT_ARGB8888:
case LV_COLOR_FORMAT_XRGB8888:
is_cf_supported = true;
break;
default:
break;
}
return is_cf_supported;
}
static inline bool _vglite_dest_cf_supported(lv_color_format_t cf)
{
bool is_cf_supported = false;
switch(cf) {
case LV_COLOR_FORMAT_A8:
#if CHIPID == 0x255 || CHIPID == 0x555
case LV_COLOR_FORMAT_L8:
#endif
case LV_COLOR_FORMAT_RGB565:
#if CHIPID == 0x555
case LV_COLOR_FORMAT_ARGB8565:
case LV_COLOR_FORMAT_RGB888:
#endif
case LV_COLOR_FORMAT_ARGB8888:
case LV_COLOR_FORMAT_XRGB8888:
is_cf_supported = true;
break;
default:
break;
}
return is_cf_supported;
}
static int32_t _vglite_evaluate(lv_draw_unit_t * u, lv_draw_task_t * t)
{
LV_UNUSED(u);
const lv_draw_dsc_base_t * draw_dsc_base = (lv_draw_dsc_base_t *) t->draw_dsc;
if(!_vglite_dest_cf_supported(draw_dsc_base->layer->color_format))
return 0;
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:
case LV_DRAW_TASK_TYPE_TRIANGLE:
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: {
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_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;
#if LV_USE_VGLITE_BLIT_SPLIT
bool has_transform = (draw_dsc->rotation != 0 || draw_dsc->scale_x != LV_SCALE_NONE ||
draw_dsc->scale_y != LV_SCALE_NONE);
#endif
if(!_vglite_src_cf_supported(layer_to_draw->color_format)
#if LV_USE_VGLITE_BLIT_SPLIT
|| has_transform
#endif
)
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;
if(img_dsc->header.cf >= LV_COLOR_FORMAT_PROPRIETARY_START) {
return 0;
}
#if CHIPID == 0x255
if(draw_dsc->tile)
return 0;
#endif
#if LV_USE_VGLITE_BLIT_SPLIT
bool has_transform = (draw_dsc->rotation != 0 || draw_dsc->scale_x != LV_SCALE_NONE ||
draw_dsc->scale_y != LV_SCALE_NONE);
bool is_tiled = draw_dsc->tile;
#endif
if((!_vglite_src_cf_supported(img_dsc->header.cf))
#if LV_USE_VGLITE_BLIT_SPLIT
|| has_transform || is_tiled
#endif
|| (!vglite_src_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 draw unit is busy. */
if(draw_vglite_unit->task_act
#if LV_USE_VGLITE_DRAW_ASYNC
|| draw_vglite_unit->wait_for_finish
#endif
)
return 0;
/* Try to get an ready to draw. */
lv_draw_task_t * t = lv_draw_get_available_task(layer, NULL, DRAW_UNIT_ID_VGLITE);
if(t == NULL)
return LV_DRAW_UNIT_IDLE;
if(t->preferred_draw_unit_id != DRAW_UNIT_ID_VGLITE) {
/* Let the preferred known unit to draw this task. */
if(t->preferred_draw_unit_id != LV_DRAW_UNIT_NONE) {
return LV_DRAW_UNIT_IDLE;
}
else {
/* Fake unsupported tasks as ready. */
t->state = LV_DRAW_TASK_STATE_READY;
/* Request a new dispatching as it can get a new task. */
lv_draw_dispatch_request();
return 1;
}
}
vglite_draw_task_t * vglite_task = lv_malloc_zeroed(sizeof(vglite_draw_task_t));
LV_ASSERT_MALLOC(vglite_task);
vglite_task->t = t;
if(lv_draw_layer_alloc_buf(layer) == NULL)
return LV_DRAW_UNIT_IDLE;
t->state = LV_DRAW_TASK_STATE_IN_PROGRESS;
draw_vglite_unit->task_act = vglite_task;
#if LV_USE_VGLITE_DRAW_THREAD
/* Let the render thread work. */
if(draw_vglite_unit->inited)
lv_thread_sync_signal(&draw_vglite_unit->sync);
#else
_vglite_execute_drawing(draw_vglite_unit);
draw_vglite_unit->task_act->t->state = LV_DRAW_TASK_STATE_READY;
_vglite_cleanup_task(draw_vglite_unit->task_act);
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;
}
#if LV_USE_VGLITE_DRAW_ASYNC
static int32_t _vglite_wait_for_finish(lv_draw_unit_t * draw_unit)
{
lv_draw_vglite_unit_t * draw_vglite_unit = (lv_draw_vglite_unit_t *) draw_unit;
draw_vglite_unit->wait_for_finish = true;
/* Signal draw unit to finish its tasks and return READY state after completion. */
if(draw_vglite_unit->inited)
lv_thread_sync_signal(&draw_vglite_unit->sync);
/* Wait for finish now. */
lv_draw_dispatch_wait_for_request();
return 1;
}
#endif
static int32_t _vglite_delete(lv_draw_unit_t * draw_unit)
{
#if LV_USE_VGLITE_DRAW_THREAD
lv_draw_vglite_unit_t * draw_vglite_unit = (lv_draw_vglite_unit_t *) draw_unit;
LV_LOG_INFO("Cancel VGLite draw thread.");
draw_vglite_unit->exit_status = true;
if(draw_vglite_unit->inited)
lv_thread_sync_signal(&draw_vglite_unit->sync);
lv_result_t res = lv_thread_delete(&draw_vglite_unit->thread);
return res;
#else
LV_UNUSED(draw_unit);
return 1;
#endif
}
static void _vglite_execute_drawing(lv_draw_vglite_unit_t * u)
{
vglite_draw_task_t * vglite_task = u->task_act;
lv_layer_t * layer = vglite_task->t->target_layer;
lv_draw_buf_t * draw_buf = layer->draw_buf;
/* Set target buffer */
vglite_set_dest_buf(draw_buf->data, draw_buf->header.w, draw_buf->header.h, draw_buf->header.stride,
draw_buf->header.cf);
lv_area_t clip_area;
lv_area_copy(&clip_area, &vglite_task->t->clip_area);
lv_area_move(&clip_area, -layer->buf_area.x1, -layer->buf_area.y1);
lv_area_t draw_area;
lv_area_copy(&draw_area, &vglite_task->t->area);
lv_area_move(&draw_area, -layer->buf_area.x1, -layer->buf_area.y1);
if(!lv_area_intersect(&draw_area, &draw_area, &clip_area))
return; /*Fully clipped, nothing to do*/
if(_draw_info.unit_cnt > 1)
lv_draw_buf_invalidate_cache(draw_buf, &draw_area);
#if LV_USE_PARALLEL_DRAW_DEBUG
/* remember draw unit for debug purposes */
vglite_task->t->draw_unit = &u->base_unit;
#endif
/* Set scissor area, excluding the split blit case */
#if LV_USE_VGLITE_BLIT_SPLIT
if(vglite_task->t->type != LV_DRAW_TASK_TYPE_IMAGE || vglite_task->t->type != LV_DRAW_TASK_TYPE_LAYER)
#endif
vglite_set_scissor(&clip_area);
switch(vglite_task->t->type) {
case LV_DRAW_TASK_TYPE_LABEL:
lv_draw_vglite_label(vglite_task);
break;
case LV_DRAW_TASK_TYPE_FILL:
lv_draw_vglite_fill(vglite_task);
break;
case LV_DRAW_TASK_TYPE_BORDER:
lv_draw_vglite_border(vglite_task);
break;
case LV_DRAW_TASK_TYPE_IMAGE:
lv_draw_vglite_img(vglite_task);
break;
case LV_DRAW_TASK_TYPE_ARC:
lv_draw_vglite_arc(vglite_task);
break;
case LV_DRAW_TASK_TYPE_LINE:
lv_draw_vglite_line(vglite_task);
break;
case LV_DRAW_TASK_TYPE_LAYER:
lv_draw_vglite_layer(vglite_task);
break;
case LV_DRAW_TASK_TYPE_TRIANGLE:
lv_draw_vglite_triangle(vglite_task);
break;
default:
break;
}
/* Disable scissor */
vglite_set_scissor(&layer->buf_area);
#if LV_USE_PARALLEL_DRAW_DEBUG
/*Layers manage it for themselves*/
if(vglite_task->t->type != LV_DRAW_TASK_TYPE_LAYER) {
lv_area_t draw_area;
if(!lv_area_intersect(&draw_area, &vglite_task->t->area, &vglite_task->t->clip_area))
return;
int32_t idx = u->base_unit.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_sw_fill(vglite_task->t, &rect_dsc, &draw_area);
lv_point_t txt_size;
lv_text_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_sw_fill(vglite_task->t, &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_sw_label(vglite_task->t, &label_dsc, &txt_area);
}
#endif
}
static inline void _vglite_cleanup_task(vglite_draw_task_t * task)
{
if(task->path != NULL) {
VGLITE_CHECK_ERROR(vg_lite_clear_path(task->path));
lv_free(task->path);
}
if(task->gradient != NULL) {
VGLITE_CHECK_ERROR(vg_lite_clear_grad(task->gradient));
lv_free(task->gradient);
}
if(task->path_data != NULL)
lv_free(task->path_data);
lv_free(task);
}
#if LV_USE_VGLITE_DRAW_ASYNC
static inline void _vglite_queue_task(vglite_draw_task_t * task)
{
VGLITE_ASSERT_MSG(((_tail + 1) % VGLITE_TASK_BUF_SIZE) != _head, "VGLite task buffer full.");
_draw_task_buf[_tail].task = task;
_draw_task_buf[_tail].flushed = false;
_tail = (_tail + 1) % VGLITE_TASK_BUF_SIZE;
}
static inline void _vglite_signal_task_ready(vglite_draw_task_t * task)
{
/* Signal the ready state to dispatcher. */
task->t->state = LV_DRAW_TASK_STATE_READY;
_head = (_head + 1) % VGLITE_TASK_BUF_SIZE;
_vglite_cleanup_task(task);
/* No need to cleanup the tasks in buffer as we advance with the _head. */
}
static inline void _vglite_signal_all_task_ready(void)
{
int end = (_head <= _tail) ? _tail : _tail + VGLITE_TASK_BUF_SIZE;
for(int i = _head; i < end; i++) {
vglite_draw_task_t * task = _draw_task_buf[i % VGLITE_TASK_BUF_SIZE].task;
_vglite_signal_task_ready(task);
}
}
static inline void _vglite_signal_flushed_task_ready(void)
{
if(vglite_cmd_buf_is_flushed()) {
int end = (_head <= _tail) ? _tail : _tail + VGLITE_TASK_BUF_SIZE;
for(int i = _head; i < end; i++) {
if(_draw_task_buf[i % VGLITE_TASK_BUF_SIZE].flushed) {
vglite_draw_task_t * task = _draw_task_buf[i % VGLITE_TASK_BUF_SIZE].task;
_vglite_signal_task_ready(task);
}
else {
/* Those tasks have been flushed now. */
_draw_task_buf[i % VGLITE_TASK_BUF_SIZE].flushed = true;
}
}
}
}
#endif
#if LV_USE_VGLITE_DRAW_THREAD
static void _vglite_render_thread_cb(void * ptr)
{
lv_draw_vglite_unit_t * u = ptr;
lv_thread_sync_init(&u->sync);
u->inited = true;
while(1) {
/* Wait for sync if there is no task set. */
while(u->task_act == NULL
#if LV_USE_VGLITE_DRAW_ASYNC
/*
* Wait for sync if wait_for_finish is triggered.
* The thread will have to run and mark as complete any pending tasks.
*/
&& !u->wait_for_finish
#endif
) {
if(u->exit_status)
break;
lv_thread_sync_wait(&u->sync);
}
if(u->exit_status) {
LV_LOG_INFO("Ready to exit VGLite draw thread.");
break;
}
if(u->task_act) {
#if LV_USE_VGLITE_DRAW_ASYNC
_vglite_queue_task((void *)u->task_act);
#endif
_vglite_execute_drawing(u);
}
#if LV_USE_VGLITE_DRAW_ASYNC
if(u->wait_for_finish) {
vglite_wait_for_finish();
_vglite_signal_all_task_ready();
}
else { /* u->task_act */
_vglite_signal_flushed_task_ready();
}
#else
/* Signal the ready state to dispatcher. */
u->task_act->t->state = LV_DRAW_TASK_STATE_READY;
_vglite_cleanup_task(u->task_act);
#endif
/* Cleanup draw unit running condition. */
#if LV_USE_VGLITE_DRAW_ASYNC
if(u->wait_for_finish)
u->wait_for_finish = false;
else
u->task_act = NULL;
#else
u->task_act = NULL;
#endif
/* The draw unit is free now. Request a new dispatching as it can get a new task. */
lv_draw_dispatch_request();
}
u->inited = false;
lv_thread_sync_delete(&u->sync);
LV_LOG_INFO("Exit VGLite draw thread.");
}
#endif
#endif /*LV_USE_DRAW_VGLITE*/
@@ -0,0 +1,101 @@
/**
* @file lv_draw_vglite.h
*
*/
/**
* Copyright 2023-2024 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 "../../lv_draw_private.h"
#include "../../../display/lv_display_private.h"
#include "../../../misc/lv_area_private.h"
#include "../../lv_draw_triangle.h"
#include "../../lv_draw_label.h"
#include "../../lv_draw_image.h"
#include "../../lv_draw_line.h"
#include "../../lv_draw_arc.h"
#include "vg_lite.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
typedef struct vglite_draw_task {
lv_draw_task_t * t;
vg_lite_path_t * path;
vg_lite_linear_gradient_t * gradient;
int32_t * path_data;
} vglite_draw_task_t;
typedef struct lv_draw_vglite_unit {
lv_draw_unit_t base_unit;
vglite_draw_task_t * task_act;
#if LV_USE_OS
lv_thread_sync_t sync;
lv_thread_t thread;
volatile bool inited;
volatile bool exit_status;
#endif
#if LV_USE_VGLITE_DRAW_ASYNC
volatile bool wait_for_finish;
#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_deinit(void);
void lv_draw_vglite_arc(vglite_draw_task_t * vglite_task);
void lv_draw_vglite_border(vglite_draw_task_t * vglite_task);
void lv_draw_vglite_fill(vglite_draw_task_t * vglite_task);
void lv_draw_vglite_img(vglite_draw_task_t * vglite_task);
void lv_draw_vglite_label(vglite_draw_task_t * vglite_task);
void lv_draw_vglite_layer(vglite_draw_task_t * vglite_task);
void lv_draw_vglite_line(vglite_draw_task_t * vglite_task);
void lv_draw_vglite_triangle(vglite_draw_task_t * vglite_task);
/**********************
* MACROS
**********************/
#endif /*LV_USE_DRAW_VGLITE*/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_DRAW_VGLITE_H*/
@@ -0,0 +1,684 @@
/**
* @file lv_draw_vglite_arc.c
*
*/
/**
* Copyright 2021-2024 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 <math.h>
/*********************
* DEFINES
*********************/
#define T_FRACTION 16384.0f
#define DICHOTO_ITER 5
static const uint16_t TperDegree[90] = {
0, 174, 348, 522, 697, 873, 1049, 1226, 1403, 1581,
1759, 1938, 2117, 2297, 2477, 2658, 2839, 3020, 3202, 3384,
3567, 3749, 3933, 4116, 4300, 4484, 4668, 4852, 5037, 5222,
5407, 5592, 5777, 5962, 6148, 6334, 6519, 6705, 6891, 7077,
7264, 7450, 7636, 7822, 8008, 8193, 8378, 8564, 8750, 8936,
9122, 9309, 9495, 9681, 9867, 10052, 10238, 10424, 10609, 10794,
10979, 11164, 11349, 11534, 11718, 11902, 12086, 12270, 12453, 12637,
12819, 13002, 13184, 13366, 13547, 13728, 13909, 14089, 14269, 14448,
14627, 14805, 14983, 15160, 15337, 15513, 15689, 15864, 16038, 16212
};
/**********************
* TYPEDEFS
**********************/
/* intermediate arc params */
typedef struct _vg_arc {
uint32_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 Clip area with relative coordinates to dest buff
* @param[in] dsc Arc description structure (width, rounded ending, opacity)
*
*/
static void _vglite_draw_arc(vglite_draw_task_t * vglite_task, 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(vglite_draw_task_t * vglite_task)
{
lv_draw_task_t * t = vglite_task->t;
const lv_draw_arc_dsc_t * dsc = t->draw_dsc;
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 = t->target_layer;
lv_point_t center = {dsc->center.x - layer->buf_area.x1, dsc->center.y - layer->buf_area.y1};
lv_area_t clip_area;
lv_area_copy(&clip_area, &t->clip_area);
lv_area_move(&clip_area, -layer->buf_area.x1, -layer->buf_area.y1);
_vglite_draw_arc(vglite_task, &center, &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(int16_t angle, int32_t * x, int32_t * y)
{
int32_t ori_x = *x;
int32_t ori_y = *y;
int16_t alpha = 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 length 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:
VGLITE_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, uint32_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, uint32_t * pidx, vg_arc * q_arc, const lv_point_t * center, bool cw)
{
/* assumes first control point already in array arc_path[] */
uint32_t 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, uint32_t * pidx, uint32_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;
uint32_t start_arc_angle = start_angle % 90;
uint32_t end_arc_angle = end_angle % 90;
uint32_t inv_start_arc_angle = (start_arc_angle > 0) ? (90 - start_arc_angle) : 0;
uint32_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(uint32_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(vglite_draw_task_t * vglite_task, const lv_point_t * center,
const lv_area_t * clip_area, const lv_draw_arc_dsc_t * dsc)
{
vg_lite_path_t * path = lv_malloc_zeroed(sizeof(vg_lite_path_t));
LV_ASSERT_MALLOC(path);
vglite_task->path = path;
int16_t start_angle = dsc->start_angle;
int16_t end_angle = dsc->end_angle;
/* be sure end_angle > start_angle */
if(end_angle < start_angle)
end_angle += 360;
bool donut = ((end_angle - start_angle) % 360 == 0) ? true : false;
vg_lite_buffer_t * dest_buf = vglite_get_dest_buf();
int32_t * arc_path = lv_malloc_zeroed(ARC_PATH_DATA_MAX_SIZE * sizeof(int32_t));
LV_ASSERT_MALLOC(arc_path);
vglite_task->path_data = arc_path;
/*** Init path ***/
int32_t width = dsc->width; /* inner arc radius = outer arc radius - width */
uint16_t radius = dsc->radius;
if(width > radius)
width = radius;
uint32_t 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;
VGLITE_CHECK_ERROR(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_color32_t col32 = lv_color_to_32(dsc->color, dsc->opa);
vg_lite_color_t vgcol = vglite_get_color(col32, false);
/*** Draw arc ***/
VGLITE_CHECK_ERROR(vg_lite_draw(dest_buf, path, VG_LITE_FILL_NON_ZERO, NULL, VG_LITE_BLEND_SRC_OVER, vgcol));
vglite_run();
}
#endif /*LV_USE_DRAW_VGLITE*/
@@ -0,0 +1,248 @@
/**
* @file lv_draw_vglite_border.c
*
*/
/**
* Copyright 2022-2024 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 <math.h>
/*********************
* DEFINES
*********************/
/*********************
* DEFINES
*********************/
/*** Define maximum numbers of rectangles needed to clip partial borders ***/
#define MAX_NUM_RECTANGLES 4
/**********************
* TYPEDEFS
**********************/
typedef struct scissoring_rects {
vg_lite_rectangle_t rect[MAX_NUM_RECTANGLES];
uint32_t num_rect;
} scissoring_rects_t ;
/**********************
* 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 Clip area with relative coordinates to dest buff
* @param[in] dsc Description of the rectangle border/outline
*
*/
static void _vglite_draw_border(vglite_draw_task_t * task, const lv_area_t * coords,
const lv_area_t * clip_area, const lv_draw_border_dsc_t * dsc);
/**
* Create scissor area based on the border side
*
* @param[in] coords Coordinates of the rectangle border/outline (relative to dest buff)
* @param[in] line_width Width of the line
* @param[in] border_side Sides of the border
* @param[in] radius Radius of the border
* @param[out] scissoring_rects Struct that contains the array of scissors
*
*/
static void _border_set_scissoring(const lv_area_t * coords, int32_t line_width,
uint32_t border_side, int32_t radius,
scissoring_rects_t * scissoring_rects);
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
void lv_draw_vglite_border(vglite_draw_task_t * vglite_task)
{
const lv_draw_border_dsc_t * dsc = vglite_task->t->draw_dsc;
const lv_area_t * coords = &vglite_task->t->area;
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 = vglite_task->t->target_layer;
lv_area_t inward_coords;
int32_t width = dsc->width;
/* Move border inwards to align with software rendered border */
inward_coords.x1 = coords->x1 + ceil(width / 2.0f);
inward_coords.x2 = coords->x2 - floor(width / 2.0f);
inward_coords.y1 = coords->y1 + ceil(width / 2.0f);
inward_coords.y2 = coords->y2 - floor(width / 2.0f);
lv_area_move(&inward_coords, -layer->buf_area.x1, -layer->buf_area.y1);
lv_area_t clip_area;
lv_area_copy(&clip_area, &vglite_task->t->clip_area);
lv_area_move(&clip_area, -layer->buf_area.x1, -layer->buf_area.y1);
lv_area_t clipped_coords;
if(!lv_area_intersect(&clipped_coords, &inward_coords, &clip_area))
return; /*Fully clipped, nothing to do*/
_vglite_draw_border(vglite_task, &inward_coords, &clip_area, dsc);
}
/**********************
* STATIC FUNCTIONS
**********************/
static void _border_set_scissoring(const lv_area_t * coords, int32_t line_width,
uint32_t border_side, int32_t radius,
scissoring_rects_t * scissoring_rects)
{
int32_t rect_width = coords->x2 - coords->x1;
int32_t rect_height = coords->y2 - coords->y1;
int32_t shortest_side = LV_MIN(rect_width, rect_height);
int32_t final_radius = LV_MIN(radius, shortest_side / 2);
if(border_side & LV_BORDER_SIDE_TOP) {
scissoring_rects->rect[scissoring_rects->num_rect].x = coords->x1 - ceil(line_width / 2.0f);
scissoring_rects->rect[scissoring_rects->num_rect].y = coords->y1 - ceil(line_width / 2.0f);
scissoring_rects->rect[scissoring_rects->num_rect].width = coords->x2 - coords->x1 + line_width;
scissoring_rects->rect[scissoring_rects->num_rect].height = final_radius + ceil(line_width / 2.0f);
scissoring_rects->num_rect++;
}
if(border_side & LV_BORDER_SIDE_LEFT) {
scissoring_rects->rect[scissoring_rects->num_rect].x = coords->x1 - ceil(line_width / 2.0f);
scissoring_rects->rect[scissoring_rects->num_rect].y = coords->y1 - ceil(line_width / 2.0f);
scissoring_rects->rect[scissoring_rects->num_rect].width = final_radius + ceil(line_width / 2.0f);
scissoring_rects->rect[scissoring_rects->num_rect].height = coords->y2 - coords->y1 + line_width + 1;
scissoring_rects->num_rect++;
}
if(border_side & LV_BORDER_SIDE_RIGHT) {
scissoring_rects->rect[scissoring_rects->num_rect].x = coords->x2 - final_radius + 1;
scissoring_rects->rect[scissoring_rects->num_rect].y = coords->y1 - ceil(line_width / 2.0f);
scissoring_rects->rect[scissoring_rects->num_rect].width = final_radius + ceil(line_width / 2.0f);
scissoring_rects->rect[scissoring_rects->num_rect].height = coords->y2 - coords->y1 + line_width + 1;
scissoring_rects->num_rect++;
}
if(border_side & LV_BORDER_SIDE_BOTTOM) {
scissoring_rects->rect[scissoring_rects->num_rect].x = coords->x1 - ceil(line_width / 2.0f);
scissoring_rects->rect[scissoring_rects->num_rect].y = coords->y2 - final_radius + 1;
scissoring_rects->rect[scissoring_rects->num_rect].width = coords->x2 - coords->x1 + line_width;
scissoring_rects->rect[scissoring_rects->num_rect].height = final_radius + ceil(line_width / 2.0f);
scissoring_rects->num_rect++;
}
}
static void _vglite_draw_border(vglite_draw_task_t * vglite_task, const lv_area_t * coords,
const lv_area_t * clip_area, const lv_draw_border_dsc_t * dsc)
{
int32_t radius = dsc->radius;
vg_lite_buffer_t * buf = vglite_get_dest_buf();
if(radius < 0)
return;
int32_t border_half = (int32_t)floor(dsc->width / 2.0f);
if(radius > border_half)
radius = radius - border_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 = lv_malloc_zeroed(RECT_PATH_DATA_MAX_SIZE * sizeof(int32_t));
LV_ASSERT_MALLOC(path_data);
vglite_task->path_data = path_data;
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 = lv_malloc_zeroed(sizeof(vg_lite_path_t));
LV_ASSERT_MALLOC(path);
vglite_task->path = path;
VGLITE_CHECK_ERROR(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_color32_t col32 = lv_color_to_32(dsc->color, dsc->opa);
vg_lite_color_t vgcol = vglite_get_color(col32, false);
int32_t line_width = dsc->width;
lv_border_side_t border_side = dsc->side;
scissoring_rects_t scissoring_rects;
scissoring_rects.num_rect = 0;
bool has_vg_mask_feat = vg_lite_query_feature(gcFEATURE_BIT_VG_MASK);
VGLITE_CHECK_ERROR(vg_lite_set_draw_path_type(path, VG_LITE_DRAW_STROKE_PATH));
VGLITE_CHECK_ERROR(vg_lite_set_stroke(path, cap_style, join_style, line_width, 8, NULL, 0, 0, vgcol));
VGLITE_CHECK_ERROR(vg_lite_update_stroke(path));
if(border_side != LV_BORDER_SIDE_FULL) {
_border_set_scissoring(coords, line_width, border_side, radius, &scissoring_rects);
if(has_vg_mask_feat) {
/*** Enable scissor and apply scissor rects ***/
VGLITE_CHECK_ERROR(vg_lite_enable_scissor());
VGLITE_CHECK_ERROR(vg_lite_scissor_rects(buf, scissoring_rects.num_rect, scissoring_rects.rect));
/*** Draw border ***/
VGLITE_CHECK_ERROR(vg_lite_draw(buf, path, VG_LITE_FILL_NON_ZERO, NULL, VG_LITE_BLEND_SRC_OVER, vgcol));
/*** Disable scissor ***/
VGLITE_CHECK_ERROR(vg_lite_disable_scissor());
}
else {
for(uint32_t i = 0; i < scissoring_rects.num_rect; i++) {
VGLITE_CHECK_ERROR(vg_lite_set_scissor(scissoring_rects.rect[i].x, scissoring_rects.rect[i].y,
scissoring_rects.rect[i].x + scissoring_rects.rect[i].width,
scissoring_rects.rect[i].y + scissoring_rects.rect[i].height));
VGLITE_CHECK_ERROR(vg_lite_draw(buf, path, VG_LITE_FILL_NON_ZERO, NULL, VG_LITE_BLEND_SRC_OVER, vgcol));
}
}
}
else {
/*** Draw border ***/
VGLITE_CHECK_ERROR(vg_lite_draw(buf, path, VG_LITE_FILL_NON_ZERO, NULL, VG_LITE_BLEND_SRC_OVER, vgcol));
}
vglite_run();
}
#endif /*LV_USE_DRAW_VGLITE*/
@@ -0,0 +1,269 @@
/**
* @file lv_draw_vglite_fill.c
*
*/
/**
* Copyright 2020-2024 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
**********************/
/**
* Set rectangle path data
*
* @param[in/out] path_data Coordinates of the rectangle
* @param[in/out] path_data_size Size of path_data (bytes)
* @param[in] p Points of the rectangle
*
*/
static void _vglite_set_rectangle(int32_t * path_data, uint32_t * path_data_size,
const lv_area_t * dest_area);
/**
* 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(vglite_draw_task_t * task, 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 Clip area with relative coordinates to dest buff
* @param[in] dsc Description of the rectangle background
*
*/
static void _vglite_draw_rect(vglite_draw_task_t * task, 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(vglite_draw_task_t * vglite_task)
{
const lv_draw_fill_dsc_t * dsc = vglite_task->t->draw_dsc;
const lv_area_t * coords = &vglite_task->t->area;
if(dsc->opa <= (lv_opa_t)LV_OPA_MIN)
return;
lv_layer_t * layer = vglite_task->t->target_layer;
lv_area_t relative_coords;
lv_area_copy(&relative_coords, coords);
lv_area_move(&relative_coords, -layer->buf_area.x1, -layer->buf_area.y1);
lv_area_t clip_area;
lv_area_copy(&clip_area, &vglite_task->t->clip_area);
lv_area_move(&clip_area, -layer->buf_area.x1, -layer->buf_area.y1);
lv_area_t clipped_coords;
if(!lv_area_intersect(&clipped_coords, &relative_coords, &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(vglite_task, &clipped_coords, dsc);
else
_vglite_draw_rect(vglite_task, &relative_coords, &clip_area, dsc);
}
/**********************
* STATIC FUNCTIONS
**********************/
static void _vglite_set_rectangle(int32_t * path_data, uint32_t * path_data_size, const lv_area_t * dest_area)
{
uint32_t pidx = 0;
path_data[pidx++] = VLC_OP_MOVE;
path_data[pidx++] = dest_area->x1;
path_data[pidx++] = dest_area->y1;
path_data[pidx++] = VLC_OP_LINE;
path_data[pidx++] = dest_area->x2 + 1;
path_data[pidx++] = dest_area->y1;
path_data[pidx++] = VLC_OP_LINE;
path_data[pidx++] = dest_area->x2 + 1;
path_data[pidx++] = dest_area->y2 + 1;
path_data[pidx++] = VLC_OP_LINE;
path_data[pidx++] = dest_area->x1;
path_data[pidx++] = dest_area->y2 + 1;
path_data[pidx++] = VLC_OP_LINE;
path_data[pidx++] = dest_area->x1;
path_data[pidx++] = dest_area->y1;
path_data[pidx++] = VLC_OP_END;
*path_data_size = pidx * sizeof(int32_t);
}
static void _vglite_fill(vglite_draw_task_t * vglite_task, const lv_area_t * dest_area,
const lv_draw_fill_dsc_t * dsc)
{
vg_lite_buffer_t * dest_buf = 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)
};
VGLITE_CHECK_ERROR(vg_lite_clear(dest_buf, &rect, vgcol));
vglite_run();
}
else { /*fill with transparency*/
vg_lite_path_t * path = lv_malloc_zeroed(sizeof(vg_lite_path_t));
LV_ASSERT_MALLOC(path);
vglite_task->path = path;
uint32_t path_data_size;
int32_t * path_data = lv_malloc_zeroed(16 * sizeof(int32_t));
LV_ASSERT_MALLOC(path_data);
vglite_task->path_data = path_data;
_vglite_set_rectangle(path_data, &path_data_size, dest_area);
VGLITE_CHECK_ERROR(vg_lite_init_path(path, VG_LITE_S32, VG_LITE_MEDIUM, path_data_size, 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));
/*Draw rectangle*/
VGLITE_CHECK_ERROR(vg_lite_draw(dest_buf, path, VG_LITE_FILL_EVEN_ODD, NULL, VG_LITE_BLEND_SRC_OVER, vgcol));
vglite_run();
}
}
static void _vglite_draw_rect(vglite_draw_task_t * vglite_task, const lv_area_t * coords,
const lv_area_t * clip_area, const lv_draw_fill_dsc_t * dsc)
{
int32_t width = lv_area_get_width(coords);
int32_t height = lv_area_get_height(coords);
int32_t radius = dsc->radius;
lv_opa_t opa = dsc->opa;
vg_lite_buffer_t * dest_buf = vglite_get_dest_buf();
if(dsc->radius < 0)
return;
/*** Init path ***/
int32_t * path_data = lv_malloc_zeroed(RECT_PATH_DATA_MAX_SIZE * sizeof(int32_t));
LV_ASSERT_MALLOC(path_data);
vglite_task->path_data = path_data;
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 = lv_malloc_zeroed(sizeof(vg_lite_path_t));
LV_ASSERT_MALLOC(path);
vglite_task->path = path;
VGLITE_CHECK_ERROR(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));
/*** Init Color ***/
lv_color32_t col32 = lv_color_to_32(dsc->color, opa);
vg_lite_color_t vgcol = vglite_get_color(col32, false);
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) {
gradient = lv_malloc_zeroed(sizeof(vg_lite_linear_gradient_t));
LV_ASSERT_MALLOC(gradient);
vglite_task->gradient = gradient;
vg_lite_matrix_t * grad_matrix;
vg_lite_uint32_t colors[LV_GRADIENT_MAX_STOPS];
vg_lite_uint32_t stops[LV_GRADIENT_MAX_STOPS];
lv_color32_t col32[LV_GRADIENT_MAX_STOPS];
/* Gradient setup */
vg_lite_uint32_t cnt = LV_MIN(dsc->grad.stops_count, LV_GRADIENT_MAX_STOPS);
lv_opa_t opa;
for(uint8_t i = 0; i < cnt; i++) {
stops[i] = dsc->grad.stops[i].frac;
opa = LV_OPA_MIX2(dsc->grad.stops[i].opa, dsc->opa);
col32[i] = lv_color_to_32(dsc->grad.stops[i].color, opa);
colors[i] = vglite_get_color(col32[i], true);
}
VGLITE_CHECK_ERROR(vg_lite_init_grad(gradient));
VGLITE_CHECK_ERROR(vg_lite_set_grad(gradient, cnt, colors, stops));
VGLITE_CHECK_ERROR(vg_lite_update_grad(gradient));
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);
}
VGLITE_CHECK_ERROR(vg_lite_draw_gradient(dest_buf, path, VG_LITE_FILL_EVEN_ODD, NULL, gradient,
VG_LITE_BLEND_SRC_OVER));
}
else {
VGLITE_CHECK_ERROR(vg_lite_draw(dest_buf, path, VG_LITE_FILL_EVEN_ODD, NULL, VG_LITE_BLEND_SRC_OVER, vgcol));
}
vglite_run();
}
#endif /*LV_USE_DRAW_VGLITE*/
@@ -0,0 +1,449 @@
/**
* @file lv_draw_vglite_blend.c
*
*/
/**
* Copyright 2020-2024 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 "lv_vglite_path.h"
#include "../../../misc/lv_log.h"
/*********************
* DEFINES
*********************/
#if LV_USE_VGLITE_BLIT_SPLIT
/**
* 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 /*LV_USE_VGLITE_BLIT_SPLIT*/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
#if LV_USE_VGLITE_BLIT_SPLIT
/**
* 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 dest_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] dsc Image descriptor
*
*/
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,
const lv_draw_image_dsc_t * dsc);
#endif /*LV_USE_VGLITE_BLIT_SPLIT*/
/**
* VGlite blit - fill a path with an image pattern
*
*
* @param[in] dest_area Destination area with relative coordinates to dest buffer
* @param[in] clip_area Clip area with relative coordinates to dest buff
* @param[in] coords Coordinates of the image (relative to dest buff)
* @param[in] dsc Image descriptor
*
*/
static void _vglite_draw_pattern(vglite_draw_task_t * vglite_task, const lv_area_t * clip_area,
const lv_area_t * coords,
const lv_draw_image_dsc_t * dsc);
/**
* BLock Image Transfer - copy rectangular image from src_buf to dest_buf with or without effects.
*
* @param[in] src_area Source area with relative coordinates to src buffer
* @param[in] dsc Image descriptor
*
*/
static void _vglite_blit(const lv_area_t * src_area, const lv_draw_image_dsc_t * dsc);
static vg_lite_color_t _vglite_recolor(const lv_draw_image_dsc_t * dsc);
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
void lv_draw_vglite_img(vglite_draw_task_t * vglite_task)
{
const lv_draw_image_dsc_t * dsc = vglite_task->t->draw_dsc;
const lv_area_t * coords = &vglite_task->t->area;
if(dsc->opa <= (lv_opa_t)LV_OPA_MIN)
return;
lv_layer_t * layer = vglite_task->t->target_layer;
const lv_image_dsc_t * img_dsc = dsc->src;
lv_area_t relative_coords;
lv_area_copy(&relative_coords, coords);
lv_area_move(&relative_coords, -layer->buf_area.x1, -layer->buf_area.y1);
lv_area_t clip_area;
lv_area_copy(&clip_area, &vglite_task->t->clip_area);
lv_area_move(&clip_area, -layer->buf_area.x1, -layer->buf_area.y1);
lv_area_t blend_area;
bool has_transform = (dsc->rotation != 0 || dsc->scale_x != LV_SCALE_NONE || dsc->scale_y != LV_SCALE_NONE);
if(has_transform)
lv_area_copy(&blend_area, &relative_coords);
else if(!lv_area_intersect(&blend_area, &relative_coords, &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->buf_area.x1);
src_area.y1 = blend_area.y1 - (coords->y1 - layer->buf_area.y1);
src_area.x2 = img_dsc->header.w - 1;
src_area.y2 = img_dsc->header.h - 1;
lv_color_format_t src_cf = img_dsc->header.cf;
uint32_t src_stride = img_dsc->header.stride;
/* Set src_buf structure. */
vglite_set_src_buf(src_buf, img_dsc->header.w, img_dsc->header.h, src_stride, src_cf);
#if LV_USE_VGLITE_BLIT_SPLIT
void * dest_buf = layer->draw_buf->data;
uint32_t dest_stride = layer->draw_buf->header.stride;
lv_color_format_t dest_cf = layer->draw_buf->header.cf;
if(!has_transform)
_vglite_blit_split(dest_buf, &blend_area, dest_stride, dest_cf,
src_buf, &src_area, src_stride, src_cf, dsc);
#else
vglite_set_transformation_matrix(&blend_area, dsc);
bool is_tiled = dsc->tile;
if(is_tiled)
_vglite_draw_pattern(vglite_task, &clip_area, &relative_coords, dsc);
else
_vglite_blit(&src_area, dsc);
#endif /*LV_USE_VGLITE_BLIT_SPLIT*/
}
/**********************
* STATIC FUNCTIONS
**********************/
static void _vglite_blit(const lv_area_t * src_area, const lv_draw_image_dsc_t * dsc)
{
vg_lite_buffer_t * dest_buf = vglite_get_dest_buf();
vg_lite_buffer_t * src_buf = vglite_get_src_buf();
vg_lite_rectangle_t rect = {
.x = (vg_lite_int32_t)src_area->x1,
.y = (vg_lite_int32_t)src_area->y1,
.width = (vg_lite_int32_t)lv_area_get_width(src_area),
.height = (vg_lite_int32_t)lv_area_get_height(src_area)
};
src_buf->image_mode = VG_LITE_MULTIPLY_IMAGE_MODE;
src_buf->transparency_mode = VG_LITE_IMAGE_TRANSPARENT;
vg_lite_color_t vgcol = _vglite_recolor(dsc);
vg_lite_matrix_t * matrix = vglite_get_matrix();
vg_lite_blend_t vgblend = vglite_get_blend_mode(dsc->blend_mode);
VGLITE_CHECK_ERROR(vg_lite_blit_rect(dest_buf, src_buf, &rect, matrix, vgblend, vgcol, VG_LITE_FILTER_POINT));
vglite_run();
}
#if LV_USE_VGLITE_BLIT_SPLIT
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_stride_alignment(cf);
uint8_t bits_per_pixel = lv_color_format_get_bpp(cf);
uint16_t align_pixels = align_bytes * 8 / bits_per_pixel;
if(area->x1 >= (int32_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,
const lv_draw_image_dsc_t * dsc)
{
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);
/* Set clip area */
vglite_set_scissor(dest_area);
/* 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_buf and src_buf memory addresses */
vglite_set_dest_buf_ptr(dest_buf);
vglite_set_src_buf_ptr(src_buf);
vglite_set_transformation_matrix(dest_area, dsc);
_vglite_blit(src_area, dsc);
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);
return;
};
/* 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);
int32_t width = LV_MIN(lv_area_get_width(src_area), lv_area_get_width(dest_area));
int32_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 * lv_color_format_get_bpp(src_cf) / 8;
tile_dest_buf += VGLITE_BLIT_SPLIT_THR * lv_color_format_get_bpp(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_buf and src_buf memory addresses */
vglite_set_dest_buf_ptr(tile_dest_buf);
vglite_set_src_buf_ptr(tile_src_buf);
vglite_set_transformation_matrix(&tile_dest_area, dsc);
_vglite_blit(&tile_src_area, dsc);
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);
}
}
}
#endif /*LV_USE_VGLITE_BLIT_SPLIT*/
static void _vglite_draw_pattern(vglite_draw_task_t * vglite_task, const lv_area_t * clip_area,
const lv_area_t * coords,
const lv_draw_image_dsc_t * dsc)
{
/* Target buffer */
vg_lite_buffer_t * dest_buf = vglite_get_dest_buf();
/* Path to draw */
int32_t * path_data = lv_malloc_zeroed(RECT_PATH_DATA_MAX_SIZE * sizeof(int32_t));
LV_ASSERT_MALLOC(path_data);
vglite_task->path_data = path_data;
uint32_t path_data_size;
vglite_create_rect_path_data(path_data, &path_data_size, dsc->clip_radius, coords);
vg_lite_quality_t path_quality = VG_LITE_MEDIUM;
vg_lite_path_t * path = lv_malloc_zeroed(sizeof(vg_lite_path_t));
LV_ASSERT_MALLOC(path);
vglite_task->path = path;
VGLITE_CHECK_ERROR(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));
/* Pattern Image */
vg_lite_buffer_t * src_buf = vglite_get_src_buf();
src_buf->image_mode = VG_LITE_MULTIPLY_IMAGE_MODE;
src_buf->transparency_mode = VG_LITE_IMAGE_TRANSPARENT;
/* Pattern matrix */
vg_lite_matrix_t matrix;
vg_lite_identity(&matrix);
vg_lite_translate((vg_lite_float_t)dsc->image_area.x1, (vg_lite_float_t)dsc->image_area.y1, &matrix);
/* Blend mode */
vg_lite_blend_t vgblend = vglite_get_blend_mode(dsc->blend_mode);
vg_lite_color_t vgcol = _vglite_recolor(dsc);
/* Filter */
bool has_transform = (dsc->rotation != 0 || dsc->scale_x != LV_SCALE_NONE || dsc->scale_y != LV_SCALE_NONE);
vg_lite_filter_t filter = has_transform ? VG_LITE_FILTER_BI_LINEAR : VG_LITE_FILTER_POINT;
/* Draw Pattern */
VGLITE_CHECK_ERROR(vg_lite_draw_pattern(dest_buf, path, VG_LITE_FILL_NON_ZERO, NULL,
src_buf, &matrix, vgblend, VG_LITE_PATTERN_REPEAT,
0, vgcol, filter));
vglite_run();
}
static vg_lite_color_t _vglite_recolor(const lv_draw_image_dsc_t * dsc)
{
lv_color_t color;
lv_opa_t opa;
bool has_recolor = (dsc->recolor_opa > LV_OPA_MIN);
if(has_recolor) {
color = dsc->recolor;
opa = LV_OPA_MIX2(dsc->recolor_opa, dsc->opa);
}
else {
color.red = 0xFF;
color.green = 0xFF;
color.blue = 0xFF;
opa = dsc->opa;
}
lv_color32_t col32 = lv_color_to_32(color, opa);
return vglite_get_color(col32, false);
}
#endif /*LV_USE_DRAW_VGLITE*/
@@ -0,0 +1,270 @@
/**
* @file lv_draw_vglite_label.c
*
*/
/**
* Copyright 2023-2024 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 "../../lv_draw_label_private.h"
#include "../../../stdlib/lv_string.h"
#include "../../../font/lv_font_fmt_txt_private.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static void _draw_vglite_letter(lv_draw_task_t * t, 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] mask_area Mask area with relative coordinates of source buffer
* @param[in] color Color
* @param[in] opa Opacity
*
*/
static void _vglite_draw_letter(const lv_area_t * mask_area, lv_color_t color, lv_opa_t opa);
/**********************
* STATIC VARIABLES
**********************/
static bool _use_static_bitmap = false;
/**********************
* GLOBAL VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
void lv_draw_vglite_label(vglite_draw_task_t * vglite_task)
{
const lv_draw_label_dsc_t * dsc = vglite_task->t->draw_dsc;
const lv_area_t * coords = &vglite_task->t->area;
if(dsc->opa <= LV_OPA_MIN) return;
lv_font_fmt_txt_dsc_t * fdsc = (lv_font_fmt_txt_dsc_t *)dsc->font->dsc;
bool is_src_buf_aligned = vglite_src_buf_aligned(fdsc->glyph_bitmap, fdsc->stride, LV_COLOR_FORMAT_A8);
bool has_static_bitmap = lv_font_has_static_bitmap(dsc->font);
_use_static_bitmap = is_src_buf_aligned & has_static_bitmap;
lv_draw_label_iterate_characters(vglite_task->t, dsc, coords, _draw_vglite_letter);
}
/**********************
* STATIC FUNCTIONS
**********************/
static void _draw_vglite_letter(lv_draw_task_t * t, 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) {
switch(glyph_draw_dsc->format) {
case LV_FONT_GLYPH_FORMAT_NONE: {
#if LV_USE_FONT_PLACEHOLDER
/* Draw a placeholder rectangle*/
vglite_draw_task_t * vglite_task = lv_malloc_zeroed(sizeof(vglite_draw_task_t));
LV_ASSERT_MALLOC(vglite_task);
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;
vglite_task->t = t;
vglite_task->t->draw_dsc = &border_draw_dsc;
vglite_task->t->area = *glyph_draw_dsc->bg_coords;
lv_draw_vglite_border(vglite_task);
/** Cleanup for vglite_task */
VGLITE_CHECK_ERROR(vg_lite_finish());
if(vglite_task->path) {
VGLITE_CHECK_ERROR(vg_lite_clear_path(vglite_task->path));
lv_free(vglite_task->path_data);
lv_free(vglite_task->path);
}
lv_free(vglite_task);
#endif
}
break;
case LV_FONT_GLYPH_FORMAT_A1 ... LV_FONT_GLYPH_FORMAT_A8: {
/*Do not draw transparent things*/
if(glyph_draw_dsc->opa <= LV_OPA_MIN)
return;
lv_layer_t * layer = t->target_layer;
lv_area_t blend_area;
if(!lv_area_intersect(&blend_area, glyph_draw_dsc->letter_coords, &t->clip_area))
return;
lv_area_move(&blend_area, -layer->buf_area.x1, -layer->buf_area.y1);
const void * mask_buf = NULL;
const lv_draw_buf_t * draw_buf = NULL;
uint32_t mask_stride;
if(!_use_static_bitmap) {
draw_buf = lv_font_get_glyph_bitmap(glyph_draw_dsc->g, glyph_draw_dsc->_draw_buf);
if(draw_buf != NULL) {
mask_buf = draw_buf->data;
}
else {
return;
}
mask_stride = draw_buf->header.stride;
}
else {
glyph_draw_dsc->g->req_raw_bitmap = 1;
mask_buf = lv_font_get_glyph_static_bitmap(glyph_draw_dsc->g);
mask_stride = glyph_draw_dsc->g->stride;
}
uint32_t mask_width = lv_area_get_width(glyph_draw_dsc->letter_coords);
uint32_t mask_height = lv_area_get_height(glyph_draw_dsc->letter_coords);
lv_area_t mask_area;
mask_area.x1 = blend_area.x1 - (glyph_draw_dsc->letter_coords->x1 - layer->buf_area.x1);
mask_area.y1 = blend_area.y1 - (glyph_draw_dsc->letter_coords->y1 - layer->buf_area.y1);
mask_area.x2 = mask_width - 1;
mask_area.y2 = mask_height - 1;
/* Set src_buf structure. */
vglite_set_src_buf(mask_buf, mask_width, mask_height, mask_stride, LV_COLOR_FORMAT_A8);
/* Set matrix. */
vglite_set_translation_matrix(&blend_area);
if(!_use_static_bitmap)
lv_draw_buf_invalidate_cache(draw_buf, &mask_area);
_vglite_draw_letter(&mask_area, glyph_draw_dsc->color, glyph_draw_dsc->opa);
}
break;
case LV_FONT_GLYPH_FORMAT_IMAGE: {
#if LV_USE_IMGFONT
glyph_draw_dsc->glyph_data = lv_font_get_glyph_bitmap(glyph_draw_dsc->g, glyph_draw_dsc->_draw_buf);
vglite_draw_task_t * vglite_task = lv_malloc_zeroed(sizeof(vglite_draw_task_t));
LV_ASSERT_MALLOC(vglite_task);
lv_draw_image_dsc_t img_dsc;
lv_draw_image_dsc_init(&img_dsc);
img_dsc.opa = glyph_draw_dsc->opa;
img_dsc.src = glyph_draw_dsc->glyph_data;
void * old_dsc = t->draw_dsc;
lv_area_t old_area = t->area;
t->draw_dsc = &img_dsc;
t->area = *glyph_draw_dsc->letter_coords;
vglite_task->t = t;
lv_draw_vglite_img(vglite_task);
/** Cleanup for vglite_task */
vg_lite_finish();
if(vglite_task->path) {
VGLITE_CHECK_ERROR(vg_lite_clear_path(vglite_task->path));
lv_free(vglite_task->path_data);
lv_free(vglite_task->path);
}
lv_free(vglite_task);
t->draw_dsc = old_dsc;
t->area = old_area;
#endif
}
break;
default:
break;
}
}
if(fill_draw_dsc && fill_area) {
vglite_draw_task_t * vglite_task = lv_malloc_zeroed(sizeof(vglite_draw_task_t));
LV_ASSERT_MALLOC(vglite_task);
t->draw_dsc = fill_draw_dsc;
t->area = *fill_area;
vglite_task->t = t;
lv_draw_vglite_fill(vglite_task);
/** Cleanup for vglite_task */
vg_lite_finish();
if(vglite_task->path) {
VGLITE_CHECK_ERROR(vg_lite_clear_path(vglite_task->path));
lv_free(vglite_task->path_data);
lv_free(vglite_task->path);
}
if(vglite_task->gradient) {
VGLITE_CHECK_ERROR(vg_lite_clear_grad(vglite_task->gradient));
lv_free(vglite_task->gradient);
}
lv_free(vglite_task);
}
}
static void _vglite_draw_letter(const lv_area_t * mask_area, lv_color_t color, lv_opa_t opa)
{
vg_lite_buffer_t * dest_buf = vglite_get_dest_buf();
vg_lite_buffer_t * mask_buf = vglite_get_src_buf();
mask_buf->image_mode = VG_LITE_MULTIPLY_IMAGE_MODE;
mask_buf->transparency_mode = VG_LITE_IMAGE_TRANSPARENT;
vg_lite_rectangle_t rect = {
.x = (vg_lite_int32_t)mask_area->x1,
.y = (vg_lite_int32_t)mask_area->y1,
.width = (vg_lite_int32_t)lv_area_get_width(mask_area),
.height = (vg_lite_int32_t)lv_area_get_height(mask_area)
};
lv_color32_t col32 = lv_color_to_32(color, opa);
vg_lite_color_t vgcol = vglite_get_color(col32, false);
vg_lite_matrix_t * matrix = vglite_get_matrix();
/*Blit with font color as paint color*/
VGLITE_CHECK_ERROR(vg_lite_blit_rect(dest_buf, mask_buf, &rect, matrix, VG_LITE_BLEND_SRC_OVER, vgcol,
VG_LITE_FILTER_POINT));
vglite_run();
}
#endif /*LV_USE_DRAW_VGLITE*/
@@ -0,0 +1,156 @@
/**
* @file lv_draw_vglite_layer.c
*
*/
/**
* Copyright 2023-2024 NXP
*
* SPDX-License-Identifier: MIT
*/
/*********************
* INCLUDES
*********************/
#include "lv_draw_vglite.h"
#if LV_USE_DRAW_VGLITE
#include "../../../stdlib/lv_string.h"
#include "../../../core/lv_global.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
/**********************
* STATIC VARIABLES
**********************/
#define _draw_info LV_GLOBAL_DEFAULT()->draw_info
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
void lv_draw_vglite_layer(vglite_draw_task_t * vglite_task)
{
lv_draw_image_dsc_t * draw_dsc = vglite_task->t->draw_dsc;
lv_layer_t * layer_to_draw = (lv_layer_t *)draw_dsc->src;
const lv_draw_buf_t * draw_buf = layer_to_draw->draw_buf;
/* It can happen that nothing was draw on a layer and therefore its buffer is not allocated.
* In this case just return.
*/
if(draw_buf == NULL)
return;
if(_draw_info.unit_cnt > 1) {
const lv_area_t area_to_draw = {
.x1 = 0,
.y1 = 0,
.x2 = draw_buf->header.w - 1,
.y2 = draw_buf->header.h - 1
};
lv_draw_buf_invalidate_cache(draw_buf, &area_to_draw);
}
lv_draw_image_dsc_t new_draw_dsc = *draw_dsc;
new_draw_dsc.src = draw_buf;
vglite_task->t->draw_dsc = &new_draw_dsc;
lv_draw_vglite_img(vglite_task);
vglite_task->t->draw_dsc = draw_dsc;
#if LV_USE_LAYER_DEBUG || LV_USE_PARALLEL_DRAW_DEBUG
const lv_area_t * coords = &vglite_task->t->area;
lv_area_t area_rot;
lv_area_copy(&area_rot, coords);
bool has_transform = (draw_dsc->rotation != 0 || draw_dsc->scale_x != LV_SCALE_NONE ||
draw_dsc->scale_y != LV_SCALE_NONE);
if(has_transform) {
int32_t w = lv_area_get_width(coords);
int32_t h = lv_area_get_height(coords);
lv_image_buf_get_transformed_area(&area_rot, w, h, draw_dsc->rotation, draw_dsc->scale_x, draw_dsc->scale_y,
&draw_dsc->pivot);
area_rot.x1 += coords->x1;
area_rot.y1 += coords->y1;
area_rot.x2 += coords->x1;
area_rot.y2 += coords->y1;
}
lv_area_t draw_area;
if(!lv_area_intersect(&draw_area, &area_rot, &vglite_task->t->clip_area)) return;
#endif
#if LV_USE_LAYER_DEBUG
lv_draw_fill_dsc_t fill_dsc;
lv_draw_fill_dsc_init(&fill_dsc);
fill_dsc.color = lv_color_hex(layer_to_draw->color_format == LV_COLOR_FORMAT_ARGB8888 ? 0xff0000 : 0x00ff00);
fill_dsc.opa = LV_OPA_20;
lv_draw_sw_fill(vglite_task->t, &fill_dsc, &area_rot);
lv_draw_border_dsc_t border_dsc;
lv_draw_border_dsc_init(&border_dsc);
border_dsc.color = fill_dsc.color;
border_dsc.opa = LV_OPA_60;
border_dsc.width = 2;
lv_draw_sw_border(vglite_task->t, &border_dsc, &area_rot);
#endif
#if LV_USE_PARALLEL_DRAW_DEBUG
int32_t idx = vglite_task->t->draw_unit->idx;
lv_draw_fill_dsc_t fill_dsc;
lv_draw_rect_dsc_init(&fill_dsc);
fill_dsc.color = lv_palette_main(idx % LV_PALETTE_LAST);
fill_dsc.opa = LV_OPA_10;
lv_draw_sw_fill(vglite_task->t, &fill_dsc, &area_rot);
lv_draw_border_dsc_t border_dsc;
lv_draw_border_dsc_init(&border_dsc);
border_dsc.color = lv_palette_main(idx % LV_PALETTE_LAST);
border_dsc.opa = LV_OPA_100;
border_dsc.width = 2;
lv_draw_sw_border(vglite_task->t, &border_dsc, &area_rot);
lv_point_t txt_size;
lv_text_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.x2 = draw_area.x1 + txt_size.x - 1;
txt_area.y2 = draw_area.y2;
txt_area.y1 = draw_area.y2 - txt_size.y + 1;
lv_draw_fill_dsc_init(&fill_dsc);
fill_dsc.color = lv_color_black();
lv_draw_sw_fill(vglite_task->t, &fill_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_white();
label_dsc.text = buf;
lv_draw_sw_label(vglite_task->t, &label_dsc, &txt_area);
#endif
}
#endif /*LV_USE_DRAW_VGLITE*/
@@ -0,0 +1,169 @@
/**
* @file lv_draw_vglite_line.c
*
*/
/**
* Copyright 2022-2024 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
**********************/
/**
* Set line path data
*
* @param[in/out] line_path Coordinates of the line
* @param[in/out] path_data_size Size of path_data (bytes)
* @param[in] p1 First point of the line
* @patam[in] p2 Second point of the line
*
*/
static void _vglite_set_line(int32_t * line_path, uint32_t * path_data_size,
const lv_point_t * point1, const lv_point_t * point2);
/**
* 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 Clip area with relative coordinates to dest buff
* @param[in] dsc Line description structure (width, rounded ending, opacity, ...)
*
*/
static void _vglite_draw_line(vglite_draw_task_t * vglite_task, 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(vglite_draw_task_t * vglite_task)
{
const lv_draw_line_dsc_t * dsc = vglite_task->t->draw_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 = vglite_task->t->target_layer;
lv_area_t clip_area;
clip_area.x1 = LV_MIN(dsc->p1.x, dsc->p2.x) - dsc->width / 2;
clip_area.x2 = LV_MAX(dsc->p1.x, dsc->p2.x) + dsc->width / 2;
clip_area.y1 = LV_MIN(dsc->p1.y, dsc->p2.y) - dsc->width / 2;
clip_area.y2 = LV_MAX(dsc->p1.y, dsc->p2.y) + dsc->width / 2;
if(!lv_area_intersect(&clip_area, &clip_area, &vglite_task->t->clip_area))
return; /*Fully clipped, nothing to do*/
lv_area_move(&clip_area, -layer->buf_area.x1, -layer->buf_area.y1);
lv_point_t point1 = {dsc->p1.x - layer->buf_area.x1, dsc->p1.y - layer->buf_area.y1};
lv_point_t point2 = {dsc->p2.x - layer->buf_area.x1, dsc->p2.y - layer->buf_area.y1};
_vglite_draw_line(vglite_task, &point1, &point2, &clip_area, dsc);
}
/**********************
* STATIC FUNCTIONS
**********************/
static void _vglite_set_line(int32_t * line_path, uint32_t * path_data_size, const lv_point_t * point1,
const lv_point_t * point2)
{
uint32_t pidx = 0;
line_path[pidx++] = VLC_OP_MOVE;
line_path[pidx++] = point1->x;
line_path[pidx++] = point1->y;
line_path[pidx++] = VLC_OP_LINE;
line_path[pidx++] = point2->x;
line_path[pidx++] = point2->y;
line_path[pidx++] = VLC_OP_END;
*path_data_size = pidx * sizeof(int32_t);
}
static void _vglite_draw_line(vglite_draw_task_t * vglite_task, 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_path_t * path = lv_malloc_zeroed(sizeof(vg_lite_path_t));
LV_ASSERT_MALLOC(path);
vglite_task->path = path;
vg_lite_buffer_t * dest_buf = 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;
}
/*** Init path ***/
int32_t width = dsc->width;
uint32_t path_data_size = 0;
int32_t * line_path = lv_malloc_zeroed(7 * sizeof(int32_t));
LV_ASSERT_MALLOC(line_path);
vglite_task->path_data = line_path;
_vglite_set_line(line_path, &path_data_size, point1, point2);
VGLITE_CHECK_ERROR(vg_lite_init_path(path, VG_LITE_S32, VG_LITE_HIGH, path_data_size, 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_color32_t col32 = lv_color_to_32(dsc->color, dsc->opa);
vg_lite_color_t vgcol = vglite_get_color(col32, false);
/*** Draw line ***/
VGLITE_CHECK_ERROR(vg_lite_set_draw_path_type(path, VG_LITE_DRAW_STROKE_PATH));
VGLITE_CHECK_ERROR(vg_lite_set_stroke(path, cap_style, join_style, width, 8, stroke_dash_pattern, stroke_dash_count,
stroke_dash_phase, vgcol));
VGLITE_CHECK_ERROR(vg_lite_update_stroke(path));
VGLITE_CHECK_ERROR(vg_lite_draw(dest_buf, path, VG_LITE_FILL_NON_ZERO, NULL, VG_LITE_BLEND_SRC_OVER, vgcol));
vglite_run();
}
#endif /*LV_USE_DRAW_VGLITE*/
@@ -0,0 +1,214 @@
/**
* @file lv_draw_vglite_triangle.c
*
*/
/**
* Copyright 2023-2024 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
**********************/
/**
* Set triangle path data
*
* @param[in/out] path_data Coordinates of the triangle
* @param[in/out] path_data_size Size of path_data (bytes)
* @param[in] p Points of the triangle
*
*/
static void _vglite_set_triangle(int32_t * path_data, uint32_t * path_data_size,
const lv_point_precise_t * p);
/**
* Draw triangle shape with effects (opacity, gradient)
*
* @param[in] vglite_task The current vglite task
* @param[in] coords Coordinates of the triangle (relative to dest buff)
* @param[in] clip_area Clipping area with relative coordinates to dest buff
* @param[in] dsc Description of the triangle
*
*/
static void _vglite_draw_triangle(vglite_draw_task_t * vglite_task, const lv_area_t * coords,
const lv_area_t * clip_area,
const lv_draw_triangle_dsc_t * dsc);
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
void lv_draw_vglite_triangle(vglite_draw_task_t * vglite_task)
{
const lv_draw_triangle_dsc_t * dsc = vglite_task->t->draw_dsc;
if(dsc->opa <= (lv_opa_t)LV_OPA_MIN)
return;
lv_layer_t * layer = vglite_task->t->target_layer;
lv_area_t clip_area;
lv_area_copy(&clip_area, &vglite_task->t->clip_area);
lv_area_move(&clip_area, -layer->buf_area.x1, -layer->buf_area.y1);
lv_area_t coords;
coords.x1 = (int32_t)LV_MIN3(dsc->p[0].x, dsc->p[1].x, dsc->p[2].x);
coords.y1 = (int32_t)LV_MIN3(dsc->p[0].y, dsc->p[1].y, dsc->p[2].y);
coords.x2 = (int32_t)LV_MAX3(dsc->p[0].x, dsc->p[1].x, dsc->p[2].x);
coords.y2 = (int32_t)LV_MAX3(dsc->p[0].y, dsc->p[1].y, dsc->p[2].y);
lv_area_move(&coords, -layer->buf_area.x1, -layer->buf_area.y1);
lv_area_t clipped_coords;
if(!lv_area_intersect(&clipped_coords, &coords, &clip_area))
return; /* Fully clipped, nothing to do */
_vglite_draw_triangle(vglite_task, &coords, &clip_area, dsc);
}
/**********************
* STATIC FUNCTIONS
**********************/
static void _vglite_set_triangle(int32_t * path_data, uint32_t * path_data_size, const lv_point_precise_t * p)
{
uint32_t pidx = 0;
path_data[pidx++] = VLC_OP_MOVE;
path_data[pidx++] = p[0].x;
path_data[pidx++] = p[0].y;
path_data[pidx++] = VLC_OP_LINE;
path_data[pidx++] = p[1].x;
path_data[pidx++] = p[1].y;
path_data[pidx++] = VLC_OP_LINE;
path_data[pidx++] = p[2].x;
path_data[pidx++] = p[2].y;
path_data[pidx++] = VLC_OP_LINE;
path_data[pidx++] = p[0].x;
path_data[pidx++] = p[0].y;
path_data[pidx++] = VLC_OP_END;
*path_data_size = pidx * sizeof(int32_t);
}
static void _vglite_draw_triangle(vglite_draw_task_t * vglite_task, const lv_area_t * coords,
const lv_area_t * clip_area,
const lv_draw_triangle_dsc_t * dsc)
{
vg_lite_buffer_t * dest_buf = vglite_get_dest_buf();
lv_area_t tri_area;
tri_area.x1 = (int32_t)LV_MIN3(dsc->p[0].x, dsc->p[1].x, dsc->p[2].x);
tri_area.y1 = (int32_t)LV_MIN3(dsc->p[0].y, dsc->p[1].y, dsc->p[2].y);
tri_area.x2 = (int32_t)LV_MAX3(dsc->p[0].x, dsc->p[1].x, dsc->p[2].x);
tri_area.y2 = (int32_t)LV_MAX3(dsc->p[0].y, dsc->p[1].y, dsc->p[2].y);
uint32_t width = lv_area_get_width(&tri_area);
uint32_t height = lv_area_get_height(&tri_area);
/* Init path */
uint32_t path_data_size;
int32_t * triangle_path = lv_malloc_zeroed(13 * sizeof(int32_t));
LV_ASSERT_MALLOC(triangle_path);
vglite_task->path_data = triangle_path;
_vglite_set_triangle(triangle_path, &path_data_size, dsc->p);
vg_lite_path_t * path = lv_malloc_zeroed(sizeof(vg_lite_path_t));
LV_ASSERT_MALLOC(path);
vglite_task->path = path;
VGLITE_CHECK_ERROR(vg_lite_init_path(path, VG_LITE_S32, VG_LITE_HIGH, path_data_size, triangle_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));
/* Init Color */
lv_color32_t col32 = lv_color_to_32(dsc->color, dsc->opa);
vg_lite_color_t vgcol = vglite_get_color(col32, false);
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) {
gradient = lv_malloc_zeroed(sizeof(vg_lite_linear_gradient_t));
LV_ASSERT_MALLOC(gradient);
vglite_task->gradient = gradient;
vg_lite_matrix_t * grad_matrix;
vg_lite_uint32_t colors[LV_GRADIENT_MAX_STOPS];
vg_lite_uint32_t stops[LV_GRADIENT_MAX_STOPS];
lv_color32_t col32[LV_GRADIENT_MAX_STOPS];
/* Gradient Setup */
vg_lite_uint32_t cnt = LV_MIN(dsc->grad.stops_count, LV_GRADIENT_MAX_STOPS);
lv_opa_t opa;
for(uint8_t i = 0; i < cnt; i++) {
stops[i] = dsc->grad.stops[i].frac;
opa = LV_OPA_MIX2(dsc->grad.stops[i].opa, dsc->opa);
col32[i] = lv_color_to_32(dsc->grad.stops[i].color, opa);
colors[i] = vglite_get_color(col32[i], true);
}
VGLITE_CHECK_ERROR(vg_lite_init_grad(gradient));
VGLITE_CHECK_ERROR(vg_lite_set_grad(gradient, cnt, colors, stops));
VGLITE_CHECK_ERROR(vg_lite_update_grad(gradient));
grad_matrix = vg_lite_get_grad_matrix(gradient);
VGLITE_CHECK_ERROR(vg_lite_identity(grad_matrix));
VGLITE_CHECK_ERROR(vg_lite_translate((float)coords->x1, (float)coords->y1, grad_matrix));
if(dsc->grad.dir == (lv_grad_dir_t)LV_GRAD_DIR_VER) {
VGLITE_CHECK_ERROR(vg_lite_scale(1.0f, (float)height / 256.0f, grad_matrix));
VGLITE_CHECK_ERROR(vg_lite_rotate(90.0f, grad_matrix));
}
else { /*LV_GRAD_DIR_HOR*/
VGLITE_CHECK_ERROR(vg_lite_scale((float)width / 256.0f, 1.0f, grad_matrix));
}
VGLITE_CHECK_ERROR(vg_lite_draw_gradient(dest_buf, path, VG_LITE_FILL_EVEN_ODD, NULL, gradient,
VG_LITE_BLEND_SRC_OVER));
}
else {
VGLITE_CHECK_ERROR(vg_lite_draw(dest_buf, path, VG_LITE_FILL_EVEN_ODD, NULL, VG_LITE_BLEND_SRC_OVER, vgcol));
}
vglite_run();
}
#endif /*LV_USE_DRAW_VGLITE*/
@@ -0,0 +1,116 @@
/**
* @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_buf;
static vg_lite_buffer_t _src_buf;
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
vg_lite_buffer_t * vglite_get_dest_buf(void)
{
return &_dest_buf;
}
vg_lite_buffer_t * vglite_get_src_buf(void)
{
return &_src_buf;
}
void vglite_set_dest_buf_ptr(void * buf)
{
_set_vgbuf_ptr(&_dest_buf, buf);
}
void vglite_set_src_buf_ptr(const void * buf)
{
_set_vgbuf_ptr(&_src_buf, (void *)buf);
}
void vglite_set_dest_buf(const void * buf, uint32_t width, uint32_t height, uint32_t stride,
lv_color_format_t cf)
{
vglite_set_buf(&_dest_buf, (void *)buf, width, height, stride, cf);
}
void vglite_set_src_buf(const void * buf, uint32_t width, uint32_t height, uint32_t stride,
lv_color_format_t cf)
{
vglite_set_buf(&_src_buf, (void *)buf, width, height, stride, cf);
}
void vglite_set_buf(vg_lite_buffer_t * vgbuf, void * buf,
uint32_t width, uint32_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_memzero(&vgbuf->yuv, 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*/
@@ -0,0 +1,124 @@
/**
* @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 "../../lv_draw.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 destination buffer.
*
* @param[in] buf Destination buffer address
* @param[in] width Destination buffer width
* @param[in] height Destination buffer height
* @param[in] stride Destination buffer stride in bytes
* @param[in] cf Destination buffer color format
*
*/
void vglite_set_dest_buf(const void * buf, uint32_t width, uint32_t height, uint32_t stride,
lv_color_format_t cf);
/**
* 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, uint32_t width, uint32_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,
uint32_t width, uint32_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*/
@@ -0,0 +1,81 @@
/**
* @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 _matrix;
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
vg_lite_matrix_t * vglite_get_matrix(void)
{
return &_matrix;
}
void vglite_set_translation_matrix(const lv_area_t * dest_area)
{
vg_lite_identity(&_matrix);
vg_lite_translate((vg_lite_float_t)dest_area->x1, (vg_lite_float_t)dest_area->y1, &_matrix);
}
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->scale_x != LV_SCALE_NONE || dsc->scale_y != LV_SCALE_NONE);
bool has_rotation = (dsc->rotation != 0);
if(has_scale || has_rotation) {
vg_lite_translate(dsc->pivot.x, dsc->pivot.y, &_matrix);
if(has_rotation)
vg_lite_rotate(dsc->rotation / 10.0f, &_matrix); /* angle is 1/10 degree */
if(has_scale) {
vg_lite_float_t scale_x = 1.0f * dsc->scale_x / LV_SCALE_NONE;
vg_lite_float_t scale_y = 1.0f * dsc->scale_y / LV_SCALE_NONE;
vg_lite_scale(scale_x, scale_y, &_matrix);
}
vg_lite_translate(0.0f - dsc->pivot.x, 0.0f - dsc->pivot.y, &_matrix);
}
}
/**********************
* STATIC FUNCTIONS
**********************/
#endif /*LV_USE_DRAW_VGLITE*/
@@ -0,0 +1,80 @@
/**
* @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 "../../lv_draw_image.h"
#include "../../lv_draw.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*/
@@ -0,0 +1,217 @@
/**
* @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,
int32_t radius,
const lv_area_t * coords)
{
int32_t rect_width = lv_area_get_width(coords);
int32_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 == (int32_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*/
@@ -0,0 +1,98 @@
/**
* @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 "../../lv_draw.h"
#include "../../lv_draw_triangle.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)
/* Maximum possible arc path size
* is in the rounded arc case:
* - 1 move for the path start
* - 16 cubics for the arc (5 inner, 5 outer) and corners (3 per corner)
* - 1 end for the path end */
#define ARC_PATH_DATA_MAX_SIZE (1 * MOVE_PATH_DATA_SIZE + 16 * CUBIC_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,
int32_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*/
@@ -0,0 +1,286 @@
/**
* @file lv_vglite_utils.c
*
*/
/**
* Copyright 2022-2024 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"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
/**********************
* STATIC VARIABLES
**********************/
#if LV_USE_VGLITE_DRAW_ASYNC
static volatile bool _cmd_buf_flushed = false;
#endif
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
const char * vglite_error_to_string(vg_lite_error_t error)
{
switch(error) {
ENUM_TO_STRING(VG_LITE_SUCCESS);
ENUM_TO_STRING(VG_LITE_INVALID_ARGUMENT);
ENUM_TO_STRING(VG_LITE_OUT_OF_MEMORY);
ENUM_TO_STRING(VG_LITE_NO_CONTEXT);
ENUM_TO_STRING(VG_LITE_TIMEOUT);
ENUM_TO_STRING(VG_LITE_OUT_OF_RESOURCES);
ENUM_TO_STRING(VG_LITE_GENERIC_IO);
ENUM_TO_STRING(VG_LITE_NOT_SUPPORT);
ENUM_TO_STRING(VG_LITE_ALREADY_EXISTS);
ENUM_TO_STRING(VG_LITE_NOT_ALIGNED);
ENUM_TO_STRING(VG_LITE_FLEXA_TIME_OUT);
ENUM_TO_STRING(VG_LITE_FLEXA_HANDSHAKE_FAIL);
default:
break;
}
return "VG_LITE_UKNOWN_ERROR";
}
#if LV_USE_VGLITE_DRAW_ASYNC
bool vglite_cmd_buf_is_flushed(void)
{
return _cmd_buf_flushed;
}
#endif
void vglite_run(void)
{
#if LV_USE_VGLITE_DRAW_ASYNC
vg_lite_uint32_t gpu_idle = 0;
VGLITE_CHECK_ERROR(vg_lite_get_parameter(VG_LITE_GPU_IDLE_STATE, 1, (vg_lite_pointer)&gpu_idle));
if(!gpu_idle) {
_cmd_buf_flushed = false;
return;
}
#endif
/*
* If LV_USE_VGLITE_DRAW_ASYNC is enabled, simply flush the command buffer and the
* vglite draw thread will signal asynchronous the dispatcher for completed tasks.
* Without draw async, process the tasks and signal them as complete one by one.
*/
#if LV_USE_VGLITE_DRAW_ASYNC
VGLITE_CHECK_ERROR(vg_lite_flush());
_cmd_buf_flushed = true;
#else
VGLITE_CHECK_ERROR(vg_lite_finish());
#endif
}
#if LV_USE_VGLITE_DRAW_ASYNC
void vglite_wait_for_finish(void)
{
VGLITE_CHECK_ERROR(vg_lite_finish());
}
#endif
vg_lite_color_t vglite_get_color(lv_color32_t lv_col32, bool gradient)
{
vg_lite_color_t vg_col32;
/* Pre-multiply alpha */
lv_col32.red = LV_UDIV255(lv_col32.red * lv_col32.alpha);
lv_col32.green = LV_UDIV255(lv_col32.green * lv_col32.alpha);
lv_col32.blue = LV_UDIV255(lv_col32.blue * lv_col32.alpha);
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 = VG_LITE_BLEND_NONE;
if(vg_lite_query_feature(gcFEATURE_BIT_VG_LVGL_SUPPORT)) {
switch(lv_blend_mode) {
case LV_BLEND_MODE_NORMAL:
vg_blend_mode = VG_LITE_BLEND_NORMAL_LVGL;
break;
case LV_BLEND_MODE_ADDITIVE:
vg_blend_mode = VG_LITE_BLEND_ADDITIVE_LVGL;
break;
case LV_BLEND_MODE_SUBTRACTIVE:
vg_blend_mode = VG_LITE_BLEND_SUBTRACT_LVGL;
break;
case LV_BLEND_MODE_MULTIPLY:
vg_blend_mode = VG_LITE_BLEND_MULTIPLY_LVGL;
break;
default:
VGLITE_ASSERT_MSG(false, "Unsupported blend mode.");
break;
}
}
else {
switch(lv_blend_mode) {
case LV_BLEND_MODE_NORMAL:
vg_blend_mode = VG_LITE_BLEND_SRC_OVER;
break;
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:
VGLITE_ASSERT_MSG(false, "Unsupported blend mode.");
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) {
case LV_COLOR_FORMAT_L8:
vg_buffer_format = VG_LITE_L8;
break;
case LV_COLOR_FORMAT_A4:
vg_buffer_format = VG_LITE_A4;
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;
case LV_COLOR_FORMAT_RGB565:
vg_buffer_format = VG_LITE_BGR565;
break;
case LV_COLOR_FORMAT_ARGB8565:
vg_buffer_format = VG_LITE_BGRA5658;
break;
case LV_COLOR_FORMAT_RGB888:
vg_buffer_format = VG_LITE_BGR888;
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:
VGLITE_ASSERT_MSG(false, "Unsupported color format.");
break;
}
return vg_buffer_format;
}
uint8_t vglite_get_stride_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:
case LV_COLOR_FORMAT_A4:
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_ARGB8565:
case LV_COLOR_FORMAT_RGB888:
align_bytes = 48;
break;
case LV_COLOR_FORMAT_ARGB8888:
case LV_COLOR_FORMAT_XRGB8888:
align_bytes = 64;
break;
default:
VGLITE_ASSERT_MSG(false, "Unsupported buffer format.");
break;
}
return align_bytes;
}
bool vglite_src_buf_aligned(const void * buf, uint32_t stride, lv_color_format_t cf)
{
/* No alignment requirement for destination buffer when using mode VG_LITE_LINEAR */
/* Test for pointer alignment */
if((uintptr_t)buf % LV_ATTRIBUTE_MEM_ALIGN_SIZE)
return false;
/* Test for stride alignment */
if(stride == 0 || stride % vglite_get_stride_alignment(cf))
return false;
return true;
}
/**********************
* STATIC FUNCTIONS
**********************/
#endif /*LV_USE_DRAW_VGLITE*/
@@ -0,0 +1,183 @@
/**
* @file lv_vglite_utils.h
*
*/
/**
* Copyright 2022-2024 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 "../../lv_draw.h"
#include "vg_lite.h"
#include "vg_lite_options.h"
/*********************
* DEFINES
*********************/
#define ENUM_TO_STRING(e) \
case (e): \
return #e
#if LV_USE_VGLITE_ASSERT
#define VGLITE_ASSERT(expr) LV_ASSERT(expr)
#else
#define VGLITE_ASSERT(expr)
#endif
#define VGLITE_ASSERT_MSG(expr, msg) \
do { \
if(!(expr)) { \
LV_LOG_ERROR(msg); \
VGLITE_ASSERT(false); \
} \
} while(0)
#if LV_USE_VGLITE_CHECK_ERROR
#define VGLITE_CHECK_ERROR(function) \
do { \
vg_lite_error_t error = function; \
if(error != VG_LITE_SUCCESS) { \
LV_LOG_ERROR("Execute '" #function "' error(%d): %s", \
(int)error, vglite_error_to_string(error)); \
VGLITE_ASSERT(false); \
} \
} while (0)
#else
#define VGLITE_CHECK_ERROR(function) function
#endif
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
/**
* 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);
/**********************
* GLOBAL PROTOTYPES
**********************/
const char * vglite_error_to_string(vg_lite_error_t error);
#if LV_USE_VGLITE_DRAW_ASYNC
/**
* Get VG-Lite command buffer flushed status.
*
*/
bool vglite_cmd_buf_is_flushed(void);
#endif
/**
* Flush command to VG-Lite.
*
*/
void vglite_run(void);
/**
* Wait for VG-Lite finish.
*
*/
#if LV_USE_VGLITE_DRAW_ASYNC
void vglite_wait_for_finish(void);
#endif
/**
* 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 stride alignment.
*
* @param[in] cf Color format
*
* @retval Alignment requirement in bytes
*
*/
uint8_t vglite_get_stride_alignment(lv_color_format_t cf);
/**
* Check source start address 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_src_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_set_scissor(clip_area->x1, clip_area->y1, clip_area->x2 + 1, clip_area->y2 + 1);
}
#endif /*LV_USE_DRAW_VGLITE*/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_VGLITE_UTILS_H*/