lvgl_cpp/lvgl/draw/nxp_vglite/lv_gpu_nxp_vglite.c

771 lines
28 KiB
C

/**
* @file lv_gpu_nxp_vglite.c
*
*/
/**
* MIT License
*
* Copyright (c) 2020 NXP
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next paragraph)
* shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_gpu_nxp_vglite.h"
#if LV_USE_GPU_NXP_VG_LITE
#include "lvgl.h"
#include "../misc/lv_log.h"
#include "fsl_cache.h"
#include "vg_lite.h"
#include "fsl_debug_console.h"
/*********************
* DEFINES
*********************/
#if LV_COLOR_DEPTH==16
#define VGLITE_PX_FMT VG_LITE_RGB565
#else
#error Only 16bit color depth is supported. Set LV_COLOR_DEPTH to 16.
#endif
/* Enable BLIT quality degradation workaround for RT595 */
#define RT595_BLIT_WRKRND_ENABLED 1
/* If LV_HOR_RES_MAX/LV_VER_RES_MAX is higher than this value, workaround will be enabled */
#define RT595_BLIT_WRKRND_THR 352
/* Print detailed info to SDK console (NOT to LVGL log system) */
#define BLIT_DBG_VERBOSE 0
/* Draw rectangles around BLIT tiles */
#define BLIT_DBG_AREAS 0
/* Redirect PRINT to SDK PRINTF */
#define PRINT PRINTF
/* Verbose debug print */
#if BLIT_DBG_VERBOSE
#define PRINT_BLT PRINTF
#else
#define PRINT_BLT(...)
#endif
/* Internal compound symbol */
#if (defined(CPU_MIMXRT595SFFOB) || defined(CPU_MIMXRT595SFFOB_cm33) || \
defined(CPU_MIMXRT595SFFOC) || defined(CPU_MIMXRT595SFFOC_cm33)) && \
((LV_HOR_RES_MAX > RT595_BLIT_WRKRND_THR) || (LV_VER_RES_MAX > RT595_BLIT_WRKRND_THR)) && \
RT595_BLIT_WRKRND_ENABLED
#define _BLIT_SPLIT_ENABLED 1
#else
#define _BLIT_SPLIT_ENABLED 0
#endif
/* BLIT split threshold - BLITs with width or height higher than this value will be done
* in multiple steps. Value must be 16-aligned. Don't change.
* */
#define LV_GPU_NXP_VG_LITE_BLIT_SPLIT_THR 352
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static lv_res_t _init_vg_buf(vg_lite_buffer_t * dst, uint32_t width, uint32_t height, uint32_t stride,
const lv_color_t * ptr, bool source);
static lv_res_t _lv_gpu_nxp_vglite_blit_single(lv_gpu_nxp_vglite_blit_info_t * blit);
#if _BLIT_SPLIT_ENABLED
static void _align_x(lv_area_t * area, lv_color_t ** buf);
static void _align_y(lv_area_t * area, lv_color_t ** buf, uint32_t stridePx);
static void _sw_blit(lv_gpu_nxp_vglite_blit_info_t * blit);
#if BLIT_DBG_AREAS
static void _draw_rectangle(lv_color_t * dest_buf, lv_coord_t dest_width, lv_coord_t dest_height,
lv_area_t * fill_area, lv_color_t color);
#endif
static lv_res_t _lv_gpu_nxp_vglite_check_blit(lv_gpu_nxp_vglite_blit_info_t * blit);
#endif
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
#define CHECK(cond, txt) \
do { \
if (cond) { \
PRINT("%s. STOP!\n", txt); \
for ( ; ; ); \
} \
} while(0)
/**********************
* GLOBAL FUNCTIONS
**********************/
/***
* Fills rectangular area in buffer.
* @param[in] dest_buf Destination buffer pointer (must be aligned on 32 bytes)
* @param[in] dest_width Destination buffer width in pixels (must be aligned on 16 px)
* @param[in] dest_height Destination buffer height in pixels
* @param[in] fill_area Area to be filled
* @param[in] color Fill color
* @param[in] opa Opacity (255 = full, 128 = 50% background/50% color, 0 = no fill)
* @retval LV_RES_OK Fill completed
* @retval LV_RES_INV Error occurred (\see LV_GPU_NXP_VG_LITE_LOG_ERRORS)
*/
lv_res_t lv_gpu_nxp_vglite_fill(lv_color_t * dest_buf, lv_coord_t dest_width, lv_coord_t dest_height,
const lv_area_t * fill_area, lv_color_t color, lv_opa_t opa)
{
vg_lite_buffer_t rt;
vg_lite_rectangle_t rect;
vg_lite_error_t err = VG_LITE_SUCCESS;
lv_color32_t col32 = {.full = lv_color_to32(color)}; /*Convert color to RGBA8888*/
lv_disp_t * disp = _lv_refr_get_disp_refreshing();
if(_init_vg_buf(&rt, (uint32_t) dest_width, (uint32_t) dest_height, (uint32_t) dest_width * sizeof(lv_color_t),
(const lv_color_t *) dest_buf, false) != LV_RES_OK) {
#if LV_GPU_NXP_VG_LITE_LOG_ERRORS
LV_LOG_ERROR("init_vg_buf reported error. Fill failed.");
#endif
return LV_RES_INV;
}
if(opa >= (lv_opa_t) LV_OPA_MAX) { /*Opaque fill*/
rect.x = fill_area->x1;
rect.y = fill_area->y1;
rect.width = (int32_t) fill_area->x2 - (int32_t) fill_area->x1 + 1;
rect.height = (int32_t) fill_area->y2 - (int32_t) fill_area->y1 + 1;
if(disp != NULL && disp->driver->clean_dcache_cb != NULL) { /*Clean & invalidate cache*/
disp->driver->clean_dcache_cb(disp->driver);
}
err = vg_lite_clear(&rt, &rect, col32.full);
if(err != VG_LITE_SUCCESS) {
#if LV_GPU_NXP_VG_LITE_LOG_ERRORS
LV_LOG_ERROR("vg_lite_clear reported error. Fill failed.");
#endif
return LV_RES_INV;
}
err = vg_lite_finish();
if(err != VG_LITE_SUCCESS) {
#if LV_GPU_NXP_VG_LITE_LOG_ERRORS
LV_LOG_ERROR("vg_lite_finish reported error. Fill failed.");
#endif
return LV_RES_INV;
}
}
else { /*fill with transparency*/
vg_lite_path_t path;
lv_color32_t colMix;
int16_t path_data[] = { /*VG rectangular path*/
VLC_OP_MOVE, fill_area->x1, fill_area->y1,
VLC_OP_LINE, fill_area->x2 + 1, fill_area->y1,
VLC_OP_LINE, fill_area->x2 + 1, fill_area->y2 + 1,
VLC_OP_LINE, fill_area->x1, fill_area->y2 + 1,
VLC_OP_LINE, fill_area->x1, fill_area->y1,
VLC_OP_END
};
err = vg_lite_init_path(&path, VG_LITE_S16, VG_LITE_LOW, sizeof(path_data), path_data,
(vg_lite_float_t) fill_area->x1, (vg_lite_float_t) fill_area->y1, ((vg_lite_float_t) fill_area->x2) + 1.0f,
((vg_lite_float_t) fill_area->y2) + 1.0f);
if(err != VG_LITE_SUCCESS) {
#if LV_GPU_NXP_VG_LITE_LOG_ERRORS
LV_LOG_ERROR("vg_lite_init_path() failed.");
#endif
return LV_RES_INV;
}
colMix.ch.red = (uint8_t)(((uint16_t)col32.ch.red * opa) >> 8); /*Pre-multiply color*/
colMix.ch.green = (uint8_t)(((uint16_t)col32.ch.green * opa) >> 8);
colMix.ch.blue = (uint8_t)(((uint16_t)col32.ch.blue * opa) >> 8);
colMix.ch.alpha = opa;
if((disp != NULL) && (disp->driver->clean_dcache_cb != NULL)) { /*Clean & invalidate cache*/
disp->driver->clean_dcache_cb(disp->driver);
}
vg_lite_matrix_t matrix;
vg_lite_identity(&matrix);
/*Draw rectangle*/
err = vg_lite_draw(&rt, &path, VG_LITE_FILL_EVEN_ODD, &matrix, VG_LITE_BLEND_SRC_OVER, colMix.full);
if(err != VG_LITE_SUCCESS) {
#if LV_GPU_NXP_VG_LITE_LOG_ERRORS
LV_LOG_ERROR("vg_lite_draw() failed.");
#endif
vg_lite_clear_path(&path);
return LV_RES_INV;
}
err = vg_lite_finish();
if(err != VG_LITE_SUCCESS) {
#if LV_GPU_NXP_VG_LITE_LOG_ERRORS
LV_LOG_ERROR("vg_lite_finish() failed.");
#endif
return LV_RES_INV;
}
err = vg_lite_clear_path(&path);
if(err != VG_LITE_SUCCESS) {
#if LV_GPU_NXP_VG_LITE_LOG_ERRORS
LV_LOG_ERROR("vg_lite_clear_path() failed.");
#endif
return LV_RES_INV;
}
}
if(err == VG_LITE_SUCCESS) {
return LV_RES_OK;
}
else {
#if LV_GPU_NXP_VG_LITE_LOG_ERRORS
LV_LOG_ERROR("VG Lite Fill failed.");
#endif
return LV_RES_INV;
}
}
/***
* BLock Image Transfer.
* @param[in] blit Description of the transfer
* @retval LV_RES_OK Transfer complete
* @retval LV_RES_INV Error occurred (\see LV_GPU_NXP_VG_LITE_LOG_ERRORS)
*/
lv_res_t lv_gpu_nxp_vglite_blit(lv_gpu_nxp_vglite_blit_info_t * blit)
{
#if _BLIT_SPLIT_ENABLED
lv_res_t rv = LV_RES_INV;
if(_lv_gpu_nxp_vglite_check_blit(blit) != LV_RES_OK) {
PRINT_BLT("Blit check failed\n");
return LV_RES_INV;
}
PRINT_BLT("BLIT from: "
"Area: %03d,%03d - %03d,%03d "
"Addr: %d\n\n",
blit->src_area.x1, blit->src_area.y1,
blit->src_area.x2, blit->src_area.y2,
(uintptr_t) blit->src);
PRINT_BLT("BLIT to: "
"Area: %03d,%03d - %03d,%03d "
"Addr: %d\n\n",
blit->dst_area.x1, blit->dst_area.y1,
blit->dst_area.x2, blit->dst_area.y2,
(uintptr_t) blit->src);
/* Stage 1: Move starting pointers as close as possible to [x1, y1], so coordinates are as small as possible. */
_align_x(&blit->src_area, (lv_color_t **)&blit->src);
_align_y(&blit->src_area, (lv_color_t **)&blit->src, blit->src_stride / sizeof(lv_color_t));
_align_x(&blit->dst_area, (lv_color_t **)&blit->dst);
_align_y(&blit->dst_area, (lv_color_t **)&blit->dst, blit->dst_stride / sizeof(lv_color_t));
/* Stage 2: If we're in limit, do a single BLIT */
if((blit->src_area.x2 < LV_GPU_NXP_VG_LITE_BLIT_SPLIT_THR) &&
(blit->src_area.y2 < LV_GPU_NXP_VG_LITE_BLIT_SPLIT_THR)) {
PRINT_BLT("Simple blit!\n");
return _lv_gpu_nxp_vglite_blit_single(blit);
};
/* Stage 3: Split the BLIT into multiple tiles */
PRINT_BLT("Split blit!\n");
PRINT_BLT("Blit "
"([%03d,%03d], [%03d,%03d]) -> "
"([%03d,%03d], [%03d,%03d]) | "
"([%03dx%03d] -> [%03dx%03d]) | "
"A:(%d -> %d)\n",
blit->src_area.x1, blit->src_area.y1, blit->src_area.x2, blit->src_area.y2,
blit->dst_area.x1, blit->dst_area.y1, blit->dst_area.x2, blit->dst_area.y2,
lv_area_get_width(&blit->src_area), lv_area_get_height(&blit->src_area),
lv_area_get_width(&blit->dst_area), lv_area_get_height(&blit->dst_area),
(uintptr_t) blit->src, (uintptr_t) blit->dst);
uint32_t totalWidth = lv_area_get_width(&blit->src_area);
uint32_t totalHeight = lv_area_get_height(&blit->src_area);
lv_gpu_nxp_vglite_blit_info_t tileBlit;
/* Number of tiles needed */
int totalTilesX = (blit->src_area.x1 + totalWidth + LV_GPU_NXP_VG_LITE_BLIT_SPLIT_THR - 1) /
LV_GPU_NXP_VG_LITE_BLIT_SPLIT_THR;
int totalTilesY = (blit->src_area.y1 + totalHeight + LV_GPU_NXP_VG_LITE_BLIT_SPLIT_THR - 1) /
LV_GPU_NXP_VG_LITE_BLIT_SPLIT_THR;
/* src and dst buffer shift against each other. Src buffer real data [0,0] may start actually at [3,0] in buffer, as
* the buffer pointer has to be aligned, while dst buffer real data [0,0] may start at [1,0] in buffer. alignment may be
* different */
int shiftSrcX = (blit->src_area.x1 > blit->dst_area.x1) ? (blit->src_area.x1 - blit->dst_area.x1) : 0;
int shiftDstX = (blit->src_area.x1 < blit->dst_area.x1) ? (blit->dst_area.x1 - blit->src_area.x1) : 0;
PRINT_BLT("\n");
PRINT_BLT("Align shift: src: %d, dst: %d\n", shiftSrcX, shiftDstX);
tileBlit = *blit;
for(int tileY = 0; tileY < totalTilesY; tileY++) {
tileBlit.src_area.y1 = 0; /* no vertical alignment, always start from 0 */
tileBlit.src_area.y2 = totalHeight - tileY * LV_GPU_NXP_VG_LITE_BLIT_SPLIT_THR - 1;
if(tileBlit.src_area.y2 >= LV_GPU_NXP_VG_LITE_BLIT_SPLIT_THR) {
tileBlit.src_area.y2 = LV_GPU_NXP_VG_LITE_BLIT_SPLIT_THR - 1; /* Should never happen */
}
tileBlit.src = blit->src + tileY * LV_GPU_NXP_VG_LITE_BLIT_SPLIT_THR * blit->src_stride / sizeof(
lv_color_t); /* stride in px! */
tileBlit.dst_area.y1 = tileBlit.src_area.y1; /* y has no alignment, always in sync with src */
tileBlit.dst_area.y2 = tileBlit.src_area.y2;
tileBlit.dst = blit->dst + tileY * LV_GPU_NXP_VG_LITE_BLIT_SPLIT_THR * blit->dst_stride / sizeof(
lv_color_t); /* stride in px! */
for(int tileX = 0; tileX < totalTilesX; tileX++) {
if(tileX == 0) {
/* 1st tile is special - there may be a gap between buffer start pointer
* and area.x1 value, as the pointer has to be aligned.
* tileBlit.src pointer - keep init value from Y-loop.
* Also, 1st tile start is not shifted! shift is applied from 2nd tile */
tileBlit.src_area.x1 = blit->src_area.x1;
tileBlit.dst_area.x1 = blit->dst_area.x1;
}
else {
/* subsequent tiles always starts from 0, but shifted*/
tileBlit.src_area.x1 = 0 + shiftSrcX;
tileBlit.dst_area.x1 = 0 + shiftDstX;
/* and advance start pointer + 1 tile size */
tileBlit.src += LV_GPU_NXP_VG_LITE_BLIT_SPLIT_THR;
tileBlit.dst += LV_GPU_NXP_VG_LITE_BLIT_SPLIT_THR;
}
/* Clip tile end coordinates */
tileBlit.src_area.x2 = totalWidth + blit->src_area.x1 - tileX * LV_GPU_NXP_VG_LITE_BLIT_SPLIT_THR - 1;
if(tileBlit.src_area.x2 >= LV_GPU_NXP_VG_LITE_BLIT_SPLIT_THR) {
tileBlit.src_area.x2 = LV_GPU_NXP_VG_LITE_BLIT_SPLIT_THR - 1;
}
tileBlit.dst_area.x2 = totalWidth + blit->dst_area.x1 - tileX * LV_GPU_NXP_VG_LITE_BLIT_SPLIT_THR - 1;
if(tileBlit.dst_area.x2 >= LV_GPU_NXP_VG_LITE_BLIT_SPLIT_THR) {
tileBlit.dst_area.x2 = LV_GPU_NXP_VG_LITE_BLIT_SPLIT_THR - 1;
}
if(tileX < (totalTilesX - 1)) {
/* And adjust end coords if shifted, but not for last tile! */
tileBlit.src_area.x2 += shiftSrcX;
tileBlit.dst_area.x2 += shiftDstX;
}
rv = _lv_gpu_nxp_vglite_blit_single(&tileBlit);
#if BLIT_DBG_AREAS
_draw_rectangle((lv_color_t *) tileBlit.dst, tileBlit.dst_width, tileBlit.dst_height, &tileBlit.dst_area, LV_COLOR_RED);
_draw_rectangle((lv_color_t *) tileBlit.src, tileBlit.src_width, tileBlit.src_height, &tileBlit.src_area,
LV_COLOR_GREEN);
#endif
PRINT_BLT("Tile [%d, %d]: "
"([%d,%d], [%d,%d]) -> "
"([%d,%d], [%d,%d]) | "
"([%dx%d] -> [%dx%d]) | "
"A:(0x%8X -> 0x%8X) %s\n",
tileX, tileY,
tileBlit.src_area.x1, tileBlit.src_area.y1, tileBlit.src_area.x2, tileBlit.src_area.y2,
tileBlit.dst_area.x1, tileBlit.dst_area.y1, tileBlit.dst_area.x2, tileBlit.dst_area.y2,
lv_area_get_width(&tileBlit.src_area), lv_area_get_height(&tileBlit.src_area),
lv_area_get_width(&tileBlit.dst_area), lv_area_get_height(&tileBlit.dst_area),
(uintptr_t) tileBlit.src, (uintptr_t) tileBlit.dst,
rv == LV_RES_OK ? "OK!" : "!!! FAILED !!!");
if(rv != LV_RES_OK) { /* if anything goes wrong... */
#if LV_GPU_NXP_VG_LITE_LOG_ERRORS
LV_LOG_ERROR("Split BLIT failed. Trying SW BLIT instead.");
#endif
_sw_blit(&tileBlit);
rv = LV_RES_OK; /* Don't report error, as SW BLIT was performed */
}
}
PRINT_BLT(" \n");
}
return rv; /* should never fail */
#else /* non RT595 */
/* Just pass down */
return _lv_gpu_nxp_vglite_blit_single(blit);
#endif
}
/**********************
* STATIC FUNCTIONS
**********************/
/***
* BLock Image Transfer - single direct BLIT.
* @param[in] blit Description of the transfer
* @retval LV_RES_OK Transfer complete
* @retval LV_RES_INV Error occurred (\see LV_GPU_NXP_VG_LITE_LOG_ERRORS)
*/
static lv_res_t _lv_gpu_nxp_vglite_blit_single(lv_gpu_nxp_vglite_blit_info_t * blit)
{
vg_lite_buffer_t src_vgbuf, dst_vgbuf;
vg_lite_error_t err = VG_LITE_SUCCESS;
uint32_t rect[4];
lv_disp_t * disp = _lv_refr_get_disp_refreshing();
if(blit == NULL) {
/*Wrong parameter*/
return LV_RES_INV;
}
if(blit->opa < (lv_opa_t) LV_OPA_MIN) {
return LV_RES_OK; /*Nothing to BLIT*/
}
/*Wrap src/dst buffer into VG-Lite buffer*/
if(_init_vg_buf(&src_vgbuf, (uint32_t) blit->src_width, (uint32_t) blit->src_height, (uint32_t) blit->src_stride,
blit->src, true) != LV_RES_OK) {
#if LV_GPU_NXP_VG_LITE_LOG_ERRORS
LV_LOG_ERROR("init_vg_buf reported error. BLIT failed.");
#endif
return LV_RES_INV;
}
if(_init_vg_buf(&dst_vgbuf, (uint32_t) blit->dst_width, (uint32_t) blit->dst_height, (uint32_t) blit->dst_stride,
blit->dst, false) != LV_RES_OK) {
#if LV_GPU_NXP_VG_LITE_LOG_ERRORS
LV_LOG_ERROR("init_vg_buf reported error. BLIT failed.");
#endif
return LV_RES_INV;
}
rect[0] = (uint32_t) blit->src_area.x1; /* start x */
rect[1] = (uint32_t) blit->src_area.y1; /* start y */
rect[2] = (uint32_t) blit->src_area.x2 - (uint32_t) blit->src_area.x1 + 1U; /* width */
rect[3] = (uint32_t) blit->src_area.y2 - (uint32_t) blit->src_area.y1 + 1U; /* height */
vg_lite_matrix_t matrix;
vg_lite_identity(&matrix);
vg_lite_translate((vg_lite_float_t)blit->dst_area.x1, (vg_lite_float_t)blit->dst_area.y1, &matrix);
if((disp != NULL) && (disp->driver->clean_dcache_cb != NULL)) { /*Clean & invalidate cache*/
disp->driver->clean_dcache_cb(disp->driver);
}
uint32_t color;
vg_lite_blend_t blend;
if(blit->opa >= (uint8_t) LV_OPA_MAX) {
color = 0x0;
blend = VG_LITE_BLEND_NONE;
}
else {
uint32_t opa = (uint32_t) blit->opa;
color = (opa << 24) | (opa << 16) | (opa << 8) | opa;
blend = VG_LITE_BLEND_SRC_OVER;
src_vgbuf.image_mode = VG_LITE_MULTIPLY_IMAGE_MODE;
}
err = vg_lite_blit_rect(&dst_vgbuf, &src_vgbuf, rect, &matrix, blend, color, VG_LITE_FILTER_POINT);
if(err != VG_LITE_SUCCESS) {
#if LV_GPU_NXP_VG_LITE_LOG_ERRORS
LV_LOG_ERROR("vg_lite_blit_rect() failed.");
#endif
return LV_RES_INV;
}
err = vg_lite_finish();
if(err != VG_LITE_SUCCESS) {
#if LV_GPU_NXP_VG_LITE_LOG_ERRORS
LV_LOG_ERROR("vg_lite_finish() failed.");
#endif
return LV_RES_INV;
}
if(err == VG_LITE_SUCCESS) {
return LV_RES_OK;
}
else {
#if LV_GPU_NXP_VG_LITE_LOG_ERRORS
LV_LOG_ERROR("vg_lite_blit_rect or vg_lite_finish reported error. BLIT failed.");
#endif
return LV_RES_INV;
}
}
/***
* Fills vg_lite_buffer_t structure according given parameters.
* @param[out] dst Buffer structure to be filled
* @param[in] width Width of buffer in pixels
* @param[in] height Height of buffer in pixels
* @param[in] stride Stride of the buffer in bytes
* @param[in] ptr Pointer to the buffer (must be aligned according VG-Lite requirements)
*/
static lv_res_t _init_vg_buf(vg_lite_buffer_t * dst, uint32_t width, uint32_t height, uint32_t stride,
const lv_color_t * ptr, bool source)
{
if((((uintptr_t)ptr) % (uintptr_t)LV_ATTRIBUTE_MEM_ALIGN_SIZE) != 0x0U) { /*Test for alignment*/
#if LV_GPU_NXP_VG_LITE_LOG_ERRORS
LV_LOG_ERROR("ptr (0x%X) not aligned to %d.", (size_t) ptr, LV_ATTRIBUTE_MEM_ALIGN_SIZE);
#endif
return LV_RES_INV;
}
if(source &&
(stride % (LV_GPU_NXP_VG_LITE_STRIDE_ALIGN_PX * sizeof(lv_color_t))) != 0x0U) { /*Test for stride alignment*/
#if LV_GPU_NXP_VG_LITE_LOG_ERRORS
LV_LOG_ERROR("Buffer stride (%d px) not aligned to %d bytes.", stride,
LV_GPU_NXP_VG_LITE_STRIDE_ALIGN_PX * sizeof(lv_color_t));
#endif
return LV_RES_INV;
}
dst->format = VGLITE_PX_FMT;
dst->tiled = VG_LITE_LINEAR;
dst->image_mode = VG_LITE_NORMAL_IMAGE_MODE;
dst->transparency_mode = VG_LITE_IMAGE_OPAQUE;
dst->width = (int32_t) width;
dst->height = (int32_t) height;
dst->stride = (int32_t) stride;
void * r_ptr = memset(&dst->yuv, 0, sizeof(dst->yuv));
if(r_ptr == NULL) {
return LV_RES_INV;
}
dst->memory = (void *)ptr;
dst->address = (uint32_t) dst->memory;
dst->handle = NULL;
return LV_RES_OK;
}
#if _BLIT_SPLIT_ENABLED
/**
* Software BLIT as a fall-back scenario
* @param[in] blit BLIT configuration
*/
static void _sw_blit(lv_gpu_nxp_vglite_blit_info_t * blit)
{
int x, y;
lv_coord_t w = lv_area_get_width(&blit->src_area);
lv_coord_t h = lv_area_get_height(&blit->src_area);
uint32_t srcStridePx = blit->src_stride / sizeof(lv_color_t);
uint32_t dstStridePx = blit->dst_stride / sizeof(lv_color_t);
lv_color_t * src = (lv_color_t *)blit->src + blit->src_area.y1 * srcStridePx + blit->src_area.x1;
lv_color_t * dst = (lv_color_t *)blit->dst + blit->dst_area.y1 * dstStridePx + blit->dst_area.x1;
if(blit->opa >= LV_OPA_MAX) {
/* simple copy */
for(y = 0; y < h; y++) {
_lv_memcpy(dst, src, w * sizeof(lv_color_t));
src += srcStridePx;
dst += dstStridePx;
}
}
else if(blit->opa >= LV_OPA_MIN) {
/* alpha blending */
for(y = 0; y < h; y++) {
for(x = 0; x < w; x++) {
dst[x] = lv_color_mix(src[x], dst[x], blit->opa);
}
src += srcStridePx;
dst += dstStridePx;
}
}
}
/**
* Verify BLIT structure - widths, stride, pointer alignment
* @param[in] blit
* @return
*/
static lv_res_t _lv_gpu_nxp_vglite_check_blit(lv_gpu_nxp_vglite_blit_info_t * blit)
{
if(lv_area_get_width(&blit->src_area) < LV_GPU_NXP_VG_LITE_STRIDE_ALIGN_PX) { /* Test for minimal width */
#if LV_GPU_NXP_VG_LITE_LOG_ERRORS
LV_LOG_ERROR("source area width (%d) is smaller than required (%d).", lv_area_get_width(&blit->src_area),
LV_GPU_NXP_VG_LITE_STRIDE_ALIGN_PX);
#endif
return LV_RES_INV;
}
if(lv_area_get_width(&blit->dst_area) < LV_GPU_NXP_VG_LITE_STRIDE_ALIGN_PX) { /* Test for minimal width */
#if LV_GPU_NXP_VG_LITE_LOG_ERRORS
LV_LOG_ERROR("destination area width (%d) is smaller than required (%d).", lv_area_get_width(&blit->dst_area),
LV_GPU_NXP_VG_LITE_STRIDE_ALIGN_PX);
#endif
return LV_RES_INV;
}
if((((uintptr_t) blit->src) % LV_ATTRIBUTE_MEM_ALIGN_SIZE) != 0x0) { /* Test for pointer alignment */
#if LV_GPU_NXP_VG_LITE_LOG_ERRORS
LV_LOG_ERROR("source buffer ptr (0x%X) not aligned to %d.", (size_t) blit->src, LV_ATTRIBUTE_MEM_ALIGN_SIZE);
#endif
return LV_RES_INV;
}
/* No alignment requirement for destination pixel buffer when using mode VG_LITE_LINEAR */
if((blit->src_stride % (LV_GPU_NXP_VG_LITE_STRIDE_ALIGN_PX * LV_COLOR_DEPTH / 8)) !=
0x0) { /* Test for stride alignment */
#if LV_GPU_NXP_VG_LITE_LOG_ERRORS
LV_LOG_ERROR("source buffer stride (%d px) not aligned to %d px.", blit->src_stride,
LV_GPU_NXP_VG_LITE_STRIDE_ALIGN_PX);
#endif
return LV_RES_INV;
}
if((blit->dst_stride % (LV_GPU_NXP_VG_LITE_STRIDE_ALIGN_PX * LV_COLOR_DEPTH / 8)) !=
0x0) { /* Test for stride alignment */
#if LV_GPU_NXP_VG_LITE_LOG_ERRORS
LV_LOG_ERROR("destination buffer stride (%d px) not aligned to %d px.", blit->dst_stride,
LV_GPU_NXP_VG_LITE_STRIDE_ALIGN_PX);
#endif
return LV_RES_INV;
}
if((lv_area_get_width(&blit->src_area) != lv_area_get_width(&blit->dst_area)) ||
(lv_area_get_height(&blit->src_area) != lv_area_get_height(&blit->dst_area))) {
#if LV_GPU_NXP_VG_LITE_LOG_ERRORS
LV_LOG_ERROR("source and destination buffer areas are not equal.");
#endif
return LV_RES_INV;
}
return LV_RES_OK;
}
/***
* Move buffer pointer as close as possible to area, but with respect to alignment requirements. X-axis only.
* @param[in,out] area Area to be updated
* @param[in,out] buf Pointer to be updated
*/
static void _align_x(lv_area_t * area, lv_color_t ** buf)
{
int alignedAreaStartPx = area->x1 - (area->x1 % (LV_ATTRIBUTE_MEM_ALIGN_SIZE * 8 / LV_COLOR_DEPTH));
CHECK(alignedAreaStartPx < 0, "Should never happen.");
area->x1 -= alignedAreaStartPx;
area->x2 -= alignedAreaStartPx;
*buf += alignedAreaStartPx;
}
/***
* Move buffer pointer to the area start and update variables, Y-axis only.
* @param[in,out] area Area to be updated
* @param[in,out] buf Pointer to be updated
* @param[in] stridePx Buffer stride in pixels
*/
static void _align_y(lv_area_t * area, lv_color_t ** buf, uint32_t stridePx)
{
int LineToAlignMem;
int alignedAreaStartPy;
/* find how many lines of pixels will respect memory alignment requirement */
if(stridePx % LV_ATTRIBUTE_MEM_ALIGN_SIZE == 0) {
alignedAreaStartPy = area->y1;
}
else {
LineToAlignMem = LV_ATTRIBUTE_MEM_ALIGN_SIZE / (sizeof(lv_color_t) * LV_GPU_NXP_VG_LITE_STRIDE_ALIGN_PX);
CHECK(LV_ATTRIBUTE_MEM_ALIGN_SIZE % (sizeof(lv_color_t) * LV_GPU_NXP_VG_LITE_STRIDE_ALIGN_PX) != 0,
"Complex case: need gcd function.");
alignedAreaStartPy = area->y1 - (area->y1 % LineToAlignMem);
CHECK(alignedAreaStartPy < 0, "Should never happen.");
}
area->y1 -= alignedAreaStartPy;
area->y2 -= alignedAreaStartPy;
*buf += alignedAreaStartPy * stridePx;
}
#if BLIT_DBG_AREAS
/***
* Draws a simple rectangle, 1 px line width.
* @param dest_buf Destination buffer
* @param dest_width Destination buffer width (must be aligned on 16px)
* @param dest_height Destination buffer height
* @param fill_area Rectangle coordinates
* @param color Rectangle color
*/
static void _draw_rectangle(lv_color_t * dest_buf, lv_coord_t dest_width, lv_coord_t dest_height,
lv_area_t * fill_area, lv_color_t color)
{
lv_area_t a;
/* top line */
a.x1 = fill_area->x1;
a.x2 = fill_area->x2;
a.y1 = fill_area->y1;
a.y2 = fill_area->y1;
lv_gpu_nxp_vglite_fill(dest_buf, dest_width, dest_height, &a, color, LV_OPA_COVER);
/* bottom line */
a.x1 = fill_area->x1;
a.x2 = fill_area->x2;
a.y1 = fill_area->y2;
a.y2 = fill_area->y2;
lv_gpu_nxp_vglite_fill(dest_buf, dest_width, dest_height, &a, color, LV_OPA_COVER);
/* left line */
a.x1 = fill_area->x1;
a.x2 = fill_area->x1;
a.y1 = fill_area->y1;
a.y2 = fill_area->y2;
lv_gpu_nxp_vglite_fill(dest_buf, dest_width, dest_height, &a, color, LV_OPA_COVER);
/* right line */
a.x1 = fill_area->x2;
a.x2 = fill_area->x2;
a.y1 = fill_area->y1;
a.y2 = fill_area->y2;
lv_gpu_nxp_vglite_fill(dest_buf, dest_width, dest_height, &a, color, LV_OPA_COVER);
}
#endif /* BLIT_DBG_AREAS */
#endif /* _BLIT_SPLIT_ENABLED */
#endif /*LV_USE_GPU_NXP_VG_LITE*/