394 lines
11 KiB
C
394 lines
11 KiB
C
/**
|
|
* @file lv_vg_lite_decoder.c
|
|
*
|
|
*/
|
|
|
|
/*********************
|
|
* INCLUDES
|
|
*********************/
|
|
|
|
#include "../lv_image_decoder_private.h"
|
|
#include "lv_vg_lite_decoder.h"
|
|
|
|
#if LV_USE_DRAW_VG_LITE
|
|
|
|
#include "lv_vg_lite_utils.h"
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include "../../core/lv_global.h"
|
|
|
|
/*********************
|
|
* DEFINES
|
|
*********************/
|
|
|
|
#define DECODER_NAME "VG_LITE"
|
|
|
|
#define image_cache_draw_buf_handlers &(LV_GLOBAL_DEFAULT()->image_cache_draw_buf_handlers)
|
|
|
|
/* VG_LITE_INDEX1, 2, and 4 require endian flipping + bit flipping,
|
|
* so for simplicity, they are uniformly converted to I8 for display.
|
|
*/
|
|
#define DEST_IMG_FORMAT LV_COLOR_FORMAT_I8
|
|
#define IS_CONV_INDEX_FORMAT(cf) (cf == LV_COLOR_FORMAT_I1 || cf == LV_COLOR_FORMAT_I2 || cf == LV_COLOR_FORMAT_I4)
|
|
|
|
/* Since the palette and index image are next to each other,
|
|
* the palette size needs to be aligned to ensure that the image is aligned.
|
|
*/
|
|
#define DEST_IMG_OFFSET \
|
|
LV_VG_LITE_ALIGN(LV_COLOR_INDEXED_PALETTE_SIZE(DEST_IMG_FORMAT) * sizeof(lv_color32_t), LV_DRAW_BUF_ALIGN)
|
|
|
|
/**********************
|
|
* TYPEDEFS
|
|
**********************/
|
|
|
|
/**********************
|
|
* STATIC PROTOTYPES
|
|
**********************/
|
|
|
|
static lv_result_t decoder_info(lv_image_decoder_t * decoder, lv_image_decoder_dsc_t * src, lv_image_header_t * header);
|
|
static lv_result_t decoder_open(lv_image_decoder_t * decoder, lv_image_decoder_dsc_t * dsc);
|
|
static void decoder_close(lv_image_decoder_t * decoder, lv_image_decoder_dsc_t * dsc);
|
|
static void image_color32_pre_mul(lv_color32_t * img_data, uint32_t px_size);
|
|
|
|
/**********************
|
|
* STATIC VARIABLES
|
|
**********************/
|
|
|
|
/**********************
|
|
* MACROS
|
|
**********************/
|
|
|
|
/**********************
|
|
* GLOBAL FUNCTIONS
|
|
**********************/
|
|
|
|
void lv_vg_lite_decoder_init(void)
|
|
{
|
|
lv_image_decoder_t * decoder = lv_image_decoder_create();
|
|
lv_image_decoder_set_info_cb(decoder, decoder_info);
|
|
lv_image_decoder_set_open_cb(decoder, decoder_open);
|
|
lv_image_decoder_set_close_cb(decoder, decoder_close);
|
|
|
|
decoder->name = DECODER_NAME;
|
|
}
|
|
|
|
void lv_vg_lite_decoder_deinit(void)
|
|
{
|
|
lv_image_decoder_t * dec = NULL;
|
|
while((dec = lv_image_decoder_get_next(dec)) != NULL) {
|
|
if(dec->info_cb == decoder_info) {
|
|
lv_image_decoder_delete(dec);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**********************
|
|
* STATIC FUNCTIONS
|
|
**********************/
|
|
|
|
static void image_color32_pre_mul(lv_color32_t * img_data, uint32_t px_size)
|
|
{
|
|
while(px_size--) {
|
|
lv_color_premultiply(img_data);
|
|
img_data++;
|
|
}
|
|
}
|
|
|
|
static uint32_t image_stride(const lv_image_header_t * header)
|
|
{
|
|
/* use stride in header */
|
|
if(header->stride) {
|
|
return header->stride;
|
|
}
|
|
|
|
/* guess stride */
|
|
uint32_t ori_stride = header->w * lv_color_format_get_bpp(header->cf);
|
|
ori_stride = (ori_stride + 7) >> 3; /*Round up*/
|
|
return ori_stride;
|
|
}
|
|
|
|
static void image_decode_to_index8_line(uint8_t * dest, const uint8_t * src, int32_t w_px,
|
|
lv_color_format_t color_format)
|
|
{
|
|
uint8_t px_size;
|
|
uint16_t mask;
|
|
|
|
int8_t shift = 0;
|
|
switch(color_format) {
|
|
case LV_COLOR_FORMAT_I1:
|
|
px_size = 1;
|
|
shift = 7;
|
|
break;
|
|
case LV_COLOR_FORMAT_I2:
|
|
px_size = 2;
|
|
shift = 6;
|
|
break;
|
|
case LV_COLOR_FORMAT_I4:
|
|
px_size = 4;
|
|
shift = 4;
|
|
break;
|
|
case LV_COLOR_FORMAT_I8:
|
|
lv_memcpy(dest, src, w_px);
|
|
return;
|
|
default:
|
|
LV_ASSERT_FORMAT_MSG(false, "Unsupported color format: %d", color_format);
|
|
return;
|
|
}
|
|
|
|
mask = (1 << px_size) - 1; /*E.g. px_size = 2; mask = 0x03*/
|
|
|
|
for(int32_t i = 0; i < w_px; i++) {
|
|
uint8_t val_act = (*src >> shift) & mask;
|
|
dest[i] = val_act;
|
|
|
|
shift -= px_size;
|
|
if(shift < 0) {
|
|
shift = 8 - px_size;
|
|
src++;
|
|
}
|
|
}
|
|
}
|
|
|
|
static lv_result_t decoder_info(lv_image_decoder_t * decoder, lv_image_decoder_dsc_t * dsc, lv_image_header_t * header)
|
|
{
|
|
lv_result_t res = lv_bin_decoder_info(decoder, dsc, header);
|
|
if(res != LV_RESULT_OK) {
|
|
return res;
|
|
}
|
|
|
|
if(!IS_CONV_INDEX_FORMAT(header->cf)) {
|
|
return LV_RESULT_INVALID;
|
|
}
|
|
|
|
if(header->flags & LV_IMAGE_FLAGS_COMPRESSED) {
|
|
LV_LOG_WARN("NOT Supported compressed index format: %d", header->cf);
|
|
return LV_RESULT_INVALID;
|
|
}
|
|
|
|
header->cf = DEST_IMG_FORMAT;
|
|
return LV_RESULT_OK;
|
|
}
|
|
|
|
static lv_result_t decoder_open_variable(lv_image_decoder_t * decoder, lv_image_decoder_dsc_t * dsc)
|
|
{
|
|
LV_UNUSED(decoder); /*Unused*/
|
|
|
|
lv_draw_buf_t src_img_buf;
|
|
lv_draw_buf_from_image(&src_img_buf, dsc->src);
|
|
|
|
/* Since dsc->header.cf is uniformly set to I8,
|
|
* the original format is obtained from src for conversion.
|
|
*/
|
|
lv_color_format_t src_cf = src_img_buf.header.cf;
|
|
|
|
int32_t width = dsc->header.w;
|
|
int32_t height = dsc->header.h;
|
|
|
|
/* create draw buf */
|
|
lv_draw_buf_t * draw_buf = lv_draw_buf_create_ex(image_cache_draw_buf_handlers, width, height, DEST_IMG_FORMAT,
|
|
LV_STRIDE_AUTO);
|
|
if(draw_buf == NULL) {
|
|
return LV_RESULT_INVALID;
|
|
}
|
|
|
|
lv_draw_buf_clear(draw_buf, NULL);
|
|
dsc->decoded = draw_buf;
|
|
|
|
uint32_t src_stride = image_stride(&src_img_buf.header);
|
|
uint32_t dest_stride = draw_buf->header.stride;
|
|
|
|
/*In case of uncompressed formats the image stored in the ROM/RAM.
|
|
*So simply give its pointer*/
|
|
const uint8_t * src = ((lv_image_dsc_t *)dsc->src)->data;
|
|
uint8_t * dest = draw_buf->data;
|
|
|
|
/* index format only */
|
|
uint32_t palette_size = LV_COLOR_INDEXED_PALETTE_SIZE(src_cf);
|
|
LV_ASSERT(palette_size > 0);
|
|
|
|
uint32_t palette_size_bytes = palette_size * sizeof(lv_color32_t);
|
|
|
|
/* copy palette */
|
|
lv_memcpy(dest, src, palette_size_bytes);
|
|
|
|
if(dsc->args.premultiply) {
|
|
/* pre-multiply palette */
|
|
image_color32_pre_mul((lv_color32_t *)dest, palette_size);
|
|
draw_buf->header.flags |= LV_IMAGE_FLAGS_PREMULTIPLIED;
|
|
}
|
|
|
|
/* move to index image map */
|
|
src += palette_size_bytes;
|
|
dest += DEST_IMG_OFFSET;
|
|
|
|
/* copy index image */
|
|
for(int32_t y = 0; y < height; y++) {
|
|
image_decode_to_index8_line(dest, src, width, src_cf);
|
|
src += src_stride;
|
|
dest += dest_stride;
|
|
}
|
|
|
|
return LV_RESULT_OK;
|
|
}
|
|
|
|
static lv_result_t decoder_open_file(lv_image_decoder_t * decoder, lv_image_decoder_dsc_t * dsc)
|
|
{
|
|
LV_UNUSED(decoder); /*Unused*/
|
|
|
|
uint32_t width = dsc->header.w;
|
|
uint32_t height = dsc->header.h;
|
|
const char * path = dsc->src;
|
|
uint8_t * src_temp = NULL;
|
|
|
|
lv_fs_file_t file;
|
|
lv_fs_res_t res = lv_fs_open(&file, path, LV_FS_MODE_RD);
|
|
if(res != LV_FS_RES_OK) {
|
|
LV_LOG_ERROR("open %s failed", path);
|
|
return LV_RESULT_INVALID;
|
|
}
|
|
|
|
/* get real src header */
|
|
lv_image_header_t src_header;
|
|
uint32_t header_br = 0;
|
|
res = lv_fs_read(&file, &src_header, sizeof(src_header), &header_br);
|
|
if(res != LV_FS_RES_OK || header_br != sizeof(src_header)) {
|
|
LV_LOG_ERROR("read %s lv_image_header_t failed", path);
|
|
lv_fs_close(&file);
|
|
return LV_RESULT_INVALID;
|
|
}
|
|
|
|
lv_draw_buf_t * draw_buf = lv_draw_buf_create_ex(image_cache_draw_buf_handlers, width, height, DEST_IMG_FORMAT,
|
|
LV_STRIDE_AUTO);
|
|
if(draw_buf == NULL) {
|
|
lv_fs_close(&file);
|
|
return LV_RESULT_INVALID;
|
|
}
|
|
|
|
lv_draw_buf_clear(draw_buf, NULL);
|
|
|
|
/* get stride */
|
|
uint32_t src_stride = image_stride(&src_header);
|
|
uint32_t dest_stride = draw_buf->header.stride;
|
|
|
|
dsc->decoded = draw_buf;
|
|
uint8_t * dest = draw_buf->data;
|
|
|
|
/* index format only */
|
|
uint32_t palette_size = LV_COLOR_INDEXED_PALETTE_SIZE(src_header.cf);
|
|
if(palette_size == 0) {
|
|
LV_LOG_ERROR("file %s invalid palette size: %" LV_PRIu32, path, palette_size);
|
|
goto failed;
|
|
}
|
|
|
|
uint32_t palette_size_bytes = palette_size * sizeof(lv_color32_t);
|
|
|
|
/* read palette */
|
|
uint32_t palette_br = 0;
|
|
res = lv_fs_read(&file, dest, palette_size_bytes, &palette_br);
|
|
if(res != LV_FS_RES_OK || palette_br != palette_size_bytes) {
|
|
LV_LOG_ERROR("read %s (palette: %" LV_PRIu32 ", br: %" LV_PRIu32 ") failed",
|
|
path, palette_size_bytes, palette_br);
|
|
goto failed;
|
|
}
|
|
|
|
if(dsc->args.premultiply) {
|
|
/* pre-multiply palette */
|
|
image_color32_pre_mul((lv_color32_t *)dest, palette_size);
|
|
draw_buf->header.flags |= LV_IMAGE_FLAGS_PREMULTIPLIED;
|
|
}
|
|
|
|
src_temp = lv_malloc(src_stride);
|
|
if(src_temp == NULL) {
|
|
LV_LOG_ERROR("malloc src_temp failed");
|
|
goto failed;
|
|
}
|
|
|
|
/* move to index image map */
|
|
dest += DEST_IMG_OFFSET;
|
|
|
|
for(uint32_t y = 0; y < height; y++) {
|
|
uint32_t br = 0;
|
|
res = lv_fs_read(&file, src_temp, src_stride, &br);
|
|
if(res != LV_FS_RES_OK || br != src_stride) {
|
|
LV_LOG_ERROR("read %s (y: %" LV_PRIu32 ", src_stride: %" LV_PRIu32 ", br: %" LV_PRIu32 ") failed",
|
|
path, y, src_stride, br);
|
|
goto failed;
|
|
}
|
|
|
|
/* convert to index8 */
|
|
image_decode_to_index8_line(dest, src_temp, width, src_header.cf);
|
|
dest += dest_stride;
|
|
}
|
|
|
|
lv_free(src_temp);
|
|
|
|
lv_fs_close(&file);
|
|
return LV_RESULT_OK;
|
|
|
|
failed:
|
|
if(src_temp) {
|
|
lv_free(src_temp);
|
|
}
|
|
lv_fs_close(&file);
|
|
lv_draw_buf_destroy(draw_buf);
|
|
dsc->decoded = NULL;
|
|
|
|
return LV_RESULT_INVALID;
|
|
}
|
|
|
|
/**
|
|
* Decode an image using the vg_lite gpu.
|
|
* @param decoder pointer to the decoder
|
|
* @param dsc pointer to the decoder descriptor
|
|
* @return LV_RESULT_OK: no error; LV_RESULT_INVALID: can't open the image
|
|
*/
|
|
static lv_result_t decoder_open(lv_image_decoder_t * decoder, lv_image_decoder_dsc_t * dsc)
|
|
{
|
|
lv_result_t res = LV_RESULT_INVALID;
|
|
|
|
switch(dsc->src_type) {
|
|
case LV_IMAGE_SRC_VARIABLE:
|
|
res = decoder_open_variable(decoder, dsc);
|
|
break;
|
|
case LV_IMAGE_SRC_FILE:
|
|
res = decoder_open_file(decoder, dsc);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if(dsc->args.no_cache) return res;
|
|
|
|
/*If the image cache is disabled, just return the decoded image*/
|
|
if(!lv_image_cache_is_enabled()) return res;
|
|
|
|
/*Add the decoded image to the cache*/
|
|
if(res == LV_RESULT_OK) {
|
|
lv_image_cache_data_t search_key;
|
|
search_key.src_type = dsc->src_type;
|
|
search_key.src = dsc->src;
|
|
search_key.slot.size = dsc->decoded->data_size;
|
|
|
|
lv_cache_entry_t * entry = lv_image_decoder_add_to_cache(decoder, &search_key, dsc->decoded, NULL);
|
|
|
|
if(entry == NULL) {
|
|
lv_draw_buf_destroy((lv_draw_buf_t *)dsc->decoded);
|
|
dsc->decoded = NULL;
|
|
return LV_RESULT_INVALID;
|
|
}
|
|
dsc->cache_entry = entry;
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
static void decoder_close(lv_image_decoder_t * decoder, lv_image_decoder_dsc_t * dsc)
|
|
{
|
|
LV_UNUSED(decoder); /*Unused*/
|
|
|
|
if(dsc->args.no_cache || !lv_image_cache_is_enabled()) lv_draw_buf_destroy((lv_draw_buf_t *)dsc->decoded);
|
|
}
|
|
|
|
#endif /*LV_USE_DRAW_VG_LITE*/
|