feat(decoder): remove img_data and use decoded draw buffer instead (#4985)

Signed-off-by: Xu Xingliang <xuxingliang@xiaomi.com>
This commit is contained in:
Neo Xu
2024-01-03 20:08:38 +08:00
committed by GitHub
parent 398e181dd3
commit b902600b83
10 changed files with 55 additions and 107 deletions

View File

@@ -258,7 +258,7 @@ open/close the PNG files. It should look like this:
} }
/** /**
* Open a PNG image and decode it into dsc.img_data * Open a PNG image and decode it into dsc.decoded
* @param decoder pointer to the decoder where this function belongs * @param decoder pointer to the decoder where this function belongs
* @param dsc image descriptor * @param dsc image descriptor
* @return LV_RESULT_OK: no error; LV_RESULT_INVALID: can't open the image * @return LV_RESULT_OK: no error; LV_RESULT_INVALID: can't open the image
@@ -270,11 +270,11 @@ open/close the PNG files. It should look like this:
/*Check whether the type `src` is known by the decoder*/ /*Check whether the type `src` is known by the decoder*/
if(is_png(dsc->src) == false) return LV_RESULT_INVALID; if(is_png(dsc->src) == false) return LV_RESULT_INVALID;
/*Decode and store the image. If `dsc->img_data` is `NULL`, the `read_line` function will be called to get the image data line-by-line*/ /*Decode and store the image. If `dsc->decoded` is `NULL`, the `read_line` function will be called to get the image data line-by-line*/
dsc->img_data = my_png_decoder(dsc->src); dsc->decoded = my_png_decoder(dsc->src);
/*Change the color format if required. For PNG usually 'Raw' is fine*/ /*Change the color format if decoded image format is different than original format. For PNG it's usually decoded to ARGB8888 format*/
dsc->header.cf = LV_COLOR_FORMAT_... dsc->decoded.header.cf = LV_COLOR_FORMAT_...
/*Call a binary image decoder function if required. It's not required if `my_png_decoder` opened the image in true color format.*/ /*Call a binary image decoder function if required. It's not required if `my_png_decoder` opened the image in true color format.*/
lv_result_t res = lv_bin_decoder_open(decoder, dsc); lv_result_t res = lv_bin_decoder_open(decoder, dsc);
@@ -316,13 +316,13 @@ So in summary:
- In ``decoder_open``, you should try to open the image source pointed by - In ``decoder_open``, you should try to open the image source pointed by
``dsc->src``. Its type is already in ``dsc->src_type == LV_IMG_SRC_FILE/VARIABLE``. ``dsc->src``. Its type is already in ``dsc->src_type == LV_IMG_SRC_FILE/VARIABLE``.
If this format/type is not supported by the decoder, return :cpp:enumerator:`LV_RESULT_INVALID`. If this format/type is not supported by the decoder, return :cpp:enumerator:`LV_RESULT_INVALID`.
However, if you can open the image, a pointer to the decoded *True color* image should be However, if you can open the image, a pointer to the decoded image should be
set in ``dsc->img_data``. If the format is known, but you don't want to set in ``dsc->decoded``. If the format is known, but you don't want to
decode the entire image (e.g. no memory for it), set ``dsc->img_data = NULL`` and decode the entire image (e.g. no memory for it), set ``dsc->decoded = NULL`` and
use ``decoder_get_area`` to get the image area pixels. use ``decoder_get_area`` to get the image area pixels.
- In ``decoder_close`` you should free all allocated resources. - In ``decoder_close`` you should free all allocated resources.
- ``decoder_get_area`` is optional. In this case you should decode the whole image In - ``decoder_get_area`` is optional. In this case you should decode the whole image In
``decoder_open`` function and store image data in ``dsc->img_data``. ``decoder_open`` function and store image data in ``dsc->decoded``.
Decoding the whole image requires extra memory and some computational overhead. Decoding the whole image requires extra memory and some computational overhead.
@@ -346,7 +346,7 @@ images to tell color of the image.
res = lv_image_decoder_open(&dsc, &my_img_dsc, &args); res = lv_image_decoder_open(&dsc, &my_img_dsc, &args);
if(res == LV_RESULT_OK) { if(res == LV_RESULT_OK) {
/*Do something with `dsc->img_data`*/ /*Do something with `dsc->decoded`. You can copy out the decoded image by `lv_draw_buf_dup(dsc.decoded)`*/
lv_image_decoder_close(&dsc); lv_image_decoder_close(&dsc);
} }
@@ -385,61 +385,26 @@ See the detailed code below:
lv_cache_entry_t * entry = dsc->cache_entry; lv_cache_entry_t * entry = dsc->cache_entry;
if(!(entry->process_state & IMAGE_PROCESS_STATE_PREMULTIPLIED_ALPHA)) { if(!(entry->process_state & IMAGE_PROCESS_STATE_PREMULTIPLIED_ALPHA)) {
lv_color32_t * image = (lv_color32_t *)dsc->img_data; lv_draw_buf_premultiply(dsc->decoded);
uint32_t px_cnt = dsc->header.w * dsc->header.h;
/* premultiply alpha */
while(px_cnt--) {
image->red = LV_UDIV255(image->red * image->alpha);
image->green = LV_UDIV255(image->green * image->alpha);
image->blue = LV_UDIV255(image->blue * image->alpha);
image++;
}
LV_LOG_USER("premultiplied alpha OK"); LV_LOG_USER("premultiplied alpha OK");
entry->process_state |= IMAGE_PROCESS_STATE_PREMULTIPLIED_ALPHA; entry->process_state |= IMAGE_PROCESS_STATE_PREMULTIPLIED_ALPHA;
} }
if(!(entry->process_state & IMAGE_PROCESS_STATE_STRIDE_ALIGNED)) { if(!(entry->process_state & IMAGE_PROCESS_STATE_STRIDE_ALIGNED)) {
int32_t image_w = dsc->header.w; uint32_t stride_expect = lv_draw_buf_width_to_stride(decoded->header.w, decoded->header.cf);
int32_t image_h = dsc->header.h; if(decoded->header.stride != stride_expect) {
uint32_t width_byte = image_w * lv_color_format_get_size(color_format); LV_LOG_WARN("Stride mismatch");
uint32_t stride = lv_draw_buf_width_to_stride(image_w, color_format); lv_draw_buf_t * aligned = lv_draw_buf_adjust_stride(decoded, stride_expect);
if(aligned == NULL) {
LV_LOG_ERROR("No memory for Stride adjust.");
return NULL;
}
/* Check stride alignment requirements */ decoded = aligned;
if(stride != width_byte) { }
LV_LOG_USER("need to realign stride: %" LV_PRIu32 " -> %" LV_PRIu32, width_byte, stride);
const uint8_t * ori_image = lv_cache_get_data(entry); entry->process_state |= IMAGE_PROCESS_STATE_STRIDE_ALIGNED;
size_t size_bytes = stride * dsc->header.h;
uint8_t * new_image = lv_draw_buf_malloc(size_bytes, color_format);
if(!new_image) {
LV_LOG_ERROR("alloc failed");
res = LV_RESULT_INVALID;
goto alloc_failed;
}
/* Replace the image data pointer */
entry->data = new_image;
dsc->img_data = new_image;
/* Copy image data */
const uint8_t * cur = ori_image;
for(int32_t y = 0; y < image_h; y++) {
lv_memcpy(new_image, cur, width_byte);
new_image += stride;
cur += width_byte;
}
/* free memory for old image */
lv_draw_buf_free((void *)ori_image);
}
else {
LV_LOG_USER("no need to realign stride: %" LV_PRIu32, stride);
}
entry->process_state |= IMAGE_PROCESS_STATE_STRIDE_ALIGNED;
} }
alloc_failed: alloc_failed:
@@ -482,7 +447,7 @@ inefficient and detrimental to the user experience.
Therefore, LVGL caches image data. Caching means some Therefore, LVGL caches image data. Caching means some
images will be left open, hence LVGL can quickly access them from images will be left open, hence LVGL can quickly access them from
``dsc->img_data`` instead of needing to decode them again. ``dsc->decoded`` instead of needing to decode them again.
Of course, caching images is resource intensive as it uses more RAM to Of course, caching images is resource intensive as it uses more RAM to
store the decoded image. LVGL tries to optimize the process as much as store the decoded image. LVGL tries to optimize the process as much as

View File

@@ -210,7 +210,7 @@ static void img_decode_and_draw(lv_draw_unit_t * draw_unit, const lv_draw_image_
sup.palette_size = decoder_dsc->palette_size; sup.palette_size = decoder_dsc->palette_size;
/*The whole image is available, just draw it*/ /*The whole image is available, just draw it*/
if(decoder_dsc->decoded || decoder_dsc->img_data) { if(decoder_dsc->decoded) {
draw_core_cb(draw_unit, draw_dsc, decoder_dsc, &sup, img_area, clipped_img_area); draw_core_cb(draw_unit, draw_dsc, decoder_dsc, &sup, img_area, clipped_img_area);
} }
/*Draw in smaller pieces*/ /*Draw in smaller pieces*/

View File

@@ -171,7 +171,6 @@ lv_result_t lv_image_decoder_open(lv_image_decoder_dsc_t * dsc, const void * src
lv_memzero(&dsc->header, sizeof(lv_image_header_t)); lv_memzero(&dsc->header, sizeof(lv_image_header_t));
dsc->error_msg = NULL; dsc->error_msg = NULL;
dsc->img_data = NULL;
dsc->decoded = NULL; dsc->decoded = NULL;
dsc->cache_entry = NULL; dsc->cache_entry = NULL;
dsc->user_data = NULL; dsc->user_data = NULL;

View File

@@ -145,10 +145,8 @@ typedef struct _lv_image_decoder_dsc_t {
/**Info about the opened image: color format, size, etc. MUST be set in `open` function*/ /**Info about the opened image: color format, size, etc. MUST be set in `open` function*/
lv_image_header_t header; lv_image_header_t header;
/** Pointer to a buffer where the image's data (pixels) are stored in a decoded, plain format. /** Pointer to a draw buffer where the image's data (pixels) are stored in a decoded, plain format.
* MUST be set in `open` function*/ * MUST be set in `open` or `get_area_cb`function*/
const uint8_t * img_data;
const lv_draw_buf_t * decoded; /*A draw buffer to described decoded image.*/ const lv_draw_buf_t * decoded; /*A draw buffer to described decoded image.*/
const lv_color32_t * palette; const lv_color32_t * palette;
@@ -175,14 +173,6 @@ typedef struct _lv_image_decoder_dsc_t {
* GLOBAL PROTOTYPES * GLOBAL PROTOTYPES
**********************/ **********************/
/**
* @todo remove it when all decoder migrates to new draw buf interface.
*/
static inline const void * _lv_image_decoder_get_data(const lv_image_decoder_dsc_t * dsc)
{
return dsc->decoded ? dsc->decoded->data : dsc->img_data;
}
/** /**
* Initialize the image decoder module * Initialize the image decoder module
*/ */
@@ -215,7 +205,7 @@ lv_result_t lv_image_decoder_get_info(const void * src, lv_image_header_t * head
* 3) Symbol: E.g. `LV_SYMBOL_OK` * 3) Symbol: E.g. `LV_SYMBOL_OK`
* @param color The color of the image with `LV_COLOR_FORMAT_ALPHA_...` * @param color The color of the image with `LV_COLOR_FORMAT_ALPHA_...`
* @param args args about how the image should be opened. * @param args args about how the image should be opened.
* @return LV_RESULT_OK: opened the image. `dsc->img_data` and `dsc->header` are set. * @return LV_RESULT_OK: opened the image. `dsc->decoded` and `dsc->header` are set.
* LV_RESULT_INVALID: none of the registered image decoders were able to open the image. * LV_RESULT_INVALID: none of the registered image decoders were able to open the image.
*/ */
lv_result_t lv_image_decoder_open(lv_image_decoder_dsc_t * dsc, const void * src, const lv_image_decoder_args_t * args); lv_result_t lv_image_decoder_open(lv_image_decoder_dsc_t * dsc, const void * src, const lv_image_decoder_args_t * args);

View File

@@ -55,14 +55,16 @@ static void img_draw_core(lv_draw_unit_t * u_base, const lv_draw_image_dsc_t * d
const lv_image_decoder_dsc_t * decoder_dsc, lv_draw_image_sup_t * sup, const lv_image_decoder_dsc_t * decoder_dsc, lv_draw_image_sup_t * sup,
const lv_area_t * img_coords, const lv_area_t * clipped_img_area) const lv_area_t * img_coords, const lv_area_t * clipped_img_area)
{ {
if(decoder_dsc->decoded == NULL) return;
lv_draw_dave2d_unit_t * u = (lv_draw_dave2d_unit_t *)u_base; lv_draw_dave2d_unit_t * u = (lv_draw_dave2d_unit_t *)u_base;
bool transformed = draw_dsc->rotation != 0 || draw_dsc->scale_x != LV_SCALE_NONE || bool transformed = draw_dsc->rotation != 0 || draw_dsc->scale_x != LV_SCALE_NONE ||
draw_dsc->scale_y != LV_SCALE_NONE ? true : false; draw_dsc->scale_y != LV_SCALE_NONE ? true : false;
const uint8_t * src_buf = decoder_dsc->img_data; lv_draw_buf_t decoded = decoder_dsc->decoded;
const lv_image_header_t * header = &decoder_dsc->header; const uint8_t * src_buf = decoded->data;
const lv_image_header_t * header = &decoder_dsc->decoded.header;
lv_area_t buffer_area; lv_area_t buffer_area;
lv_area_t draw_area; lv_area_t draw_area;
lv_area_t clipped_area; lv_area_t clipped_area;
@@ -78,14 +80,6 @@ static void img_draw_core(lv_draw_unit_t * u_base, const lv_draw_image_dsc_t * d
uint32_t img_stride = header->stride; uint32_t img_stride = header->stride;
lv_color_format_t cf = header->cf; lv_color_format_t cf = header->cf;
cf = LV_COLOR_FORMAT_IS_INDEXED(cf) ? LV_COLOR_FORMAT_ARGB8888 : cf;
if(decoder_dsc->decoded) {
src_buf = decoder_dsc->decoded->data;
img_stride = decoder_dsc->decoded->header.stride;
cf = decoder_dsc->decoded->header.cf;
}
#if LV_USE_OS #if LV_USE_OS
lv_result_t status; lv_result_t status;
status = lv_mutex_lock(u->pd2Mutex); status = lv_mutex_lock(u->pd2Mutex);
@@ -131,7 +125,7 @@ static void img_draw_core(lv_draw_unit_t * u_base, const lv_draw_image_dsc_t * d
#if defined(RENESAS_CORTEX_M85) #if defined(RENESAS_CORTEX_M85)
#if (BSP_CFG_DCACHE_ENABLED) #if (BSP_CFG_DCACHE_ENABLED)
d1_cacheblockflush(u->d2_handle, 0, src_buf, d1_cacheblockflush(u->d2_handle, 0, src_buf,
img_stride * decoder_dsc->header.h); //Stride is in bytes, not pixels/texels img_stride * header->h); //Stride is in bytes, not pixels/texels
#endif #endif
#endif #endif
@@ -162,14 +156,14 @@ static void img_draw_core(lv_draw_unit_t * u_base, const lv_draw_image_dsc_t * d
lv_point_t p[4] = { //Points in clockwise order lv_point_t p[4] = { //Points in clockwise order
{0, 0}, {0, 0},
{decoder_dsc->header.w - 1, 0}, {header->w - 1, 0},
{decoder_dsc->header.w - 1, decoder_dsc->header.h - 1}, {header->w - 1, header->h - 1},
{0, decoder_dsc->header.h - 1}, {0, header->h - 1},
}; };
d2_settexture(u->d2_handle, (void *)src_buf, d2_settexture(u->d2_handle, (void *)src_buf,
(d2_s32)(img_stride / lv_color_format_get_size(header->cf)), (d2_s32)(img_stride / lv_color_format_get_size(header->cf)),
decoder_dsc->header.w, decoder_dsc->header.h, lv_draw_dave2d_lv_colour_fmt_to_d2_fmt(header->cf)); header->w, header->h, lv_draw_dave2d_lv_colour_fmt_to_d2_fmt(header->cf));
d2_settexturemode(u->d2_handle, d2_tm_filter); d2_settexturemode(u->d2_handle, d2_tm_filter);
d2_setfillmode(u->d2_handle, d2_fm_texture); d2_setfillmode(u->d2_handle, d2_fm_texture);

View File

@@ -131,7 +131,7 @@ void lv_draw_sw_arc(lv_draw_unit_t * draw_unit, const lv_draw_arc_dsc_t * dsc, c
int32_t ofs = decoder_dsc.header.w / 2; int32_t ofs = decoder_dsc.header.w / 2;
lv_area_move(&img_area, dsc->center.x - ofs, dsc->center.y - ofs); lv_area_move(&img_area, dsc->center.x - ofs, dsc->center.y - ofs);
blend_dsc.src_area = &img_area; blend_dsc.src_area = &img_area;
blend_dsc.src_buf = _lv_image_decoder_get_data(&decoder_dsc); blend_dsc.src_buf = decoder_dsc.decoded->data;
blend_dsc.src_color_format = decoder_dsc.decoded->header.cf; blend_dsc.src_color_format = decoder_dsc.decoded->header.cf;
blend_dsc.src_stride = decoder_dsc.decoded->header.stride; blend_dsc.src_stride = decoder_dsc.decoded->header.stride;
} }

View File

@@ -173,18 +173,11 @@ static void img_draw_core(lv_draw_unit_t * draw_unit, const lv_draw_image_dsc_t
draw_dsc->scale_y != LV_SCALE_NONE ? true : false; draw_dsc->scale_y != LV_SCALE_NONE ? true : false;
lv_draw_sw_blend_dsc_t blend_dsc; lv_draw_sw_blend_dsc_t blend_dsc;
const uint8_t * src_buf = decoder_dsc->img_data; const lv_draw_buf_t * decoded = decoder_dsc->decoded;
const lv_image_header_t * header = &decoder_dsc->header; const uint8_t * src_buf = decoded->data;
uint32_t img_stride = header->stride; const lv_image_header_t * header = &decoded->header;
lv_color_format_t cf = header->cf; uint32_t img_stride = decoded->header.stride;
lv_color_format_t cf = decoded->header.cf;
cf = LV_COLOR_FORMAT_IS_INDEXED(cf) ? LV_COLOR_FORMAT_ARGB8888 : cf;
if(decoder_dsc->decoded) {
src_buf = decoder_dsc->decoded->data;
img_stride = decoder_dsc->decoded->header.stride;
cf = decoder_dsc->decoded->header.cf;
}
lv_memzero(&blend_dsc, sizeof(lv_draw_sw_blend_dsc_t)); lv_memzero(&blend_dsc, sizeof(lv_draw_sw_blend_dsc_t));
blend_dsc.opa = draw_dsc->opa; blend_dsc.opa = draw_dsc->opa;

View File

@@ -298,13 +298,13 @@ static void _set_paint_fill_pattern(Tvg_Paint * obj, Tvg_Canvas * canvas, const
return; return;
} }
if(!decoder_dsc.decoded && !decoder_dsc.img_data) { if(!decoder_dsc.decoded) {
lv_image_decoder_close(&decoder_dsc); lv_image_decoder_close(&decoder_dsc);
LV_LOG_ERROR("Image not ready"); LV_LOG_ERROR("Image not ready");
return; return;
} }
const uint8_t * src_buf = _lv_image_decoder_get_data(&decoder_dsc); const uint8_t * src_buf = decoder_dsc.decoded->data;
const lv_image_header_t * header = &decoder_dsc.header; const lv_image_header_t * header = &decoder_dsc.header;
lv_color_format_t cf = header->cf; lv_color_format_t cf = header->cf;

View File

@@ -50,6 +50,7 @@ struct ffmpeg_context_s {
int video_dst_linesize[4]; int video_dst_linesize[4];
enum AVPixelFormat video_dst_pix_fmt; enum AVPixelFormat video_dst_pix_fmt;
bool has_alpha; bool has_alpha;
lv_draw_buf_t draw_buf;
}; };
#pragma pack(1) #pragma pack(1)
@@ -300,7 +301,13 @@ static lv_result_t decoder_open(lv_image_decoder_t * decoder, lv_image_decoder_d
uint8_t * img_data = ffmpeg_get_image_data(ffmpeg_ctx); uint8_t * img_data = ffmpeg_get_image_data(ffmpeg_ctx);
dsc->user_data = ffmpeg_ctx; dsc->user_data = ffmpeg_ctx;
dsc->img_data = img_data; lv_draw_buf_t * decoded = &ffmpeg_ctx->draw_buf;
decoded->header = dsc->header;
decoded->header.flags |= LV_IMAGE_FLAGS_MODIFIABLE;
decoded->data = img_data;
decoded->data_size = (uint32_t)dsc->header.stride * dsc->header.h;
decoded->unaligned_data = NULL;
dsc->decoded = decoded;
/* The image is fully decoded. Return with its pointer */ /* The image is fully decoded. Return with its pointer */
return LV_RESULT_OK; return LV_RESULT_OK;
@@ -575,6 +582,7 @@ static int ffmpeg_get_image_header(const char * filepath,
header->w = video_dec_ctx->width; header->w = video_dec_ctx->width;
header->h = video_dec_ctx->height; header->h = video_dec_ctx->height;
header->cf = has_alpha ? LV_COLOR_FORMAT_ARGB8888 : LV_COLOR_FORMAT_NATIVE; header->cf = has_alpha ? LV_COLOR_FORMAT_ARGB8888 : LV_COLOR_FORMAT_NATIVE;
header->stride = header->w * lv_color_format_get_size(header->cf);
ret = 0; ret = 0;
} }

View File

@@ -251,13 +251,12 @@ static lv_draw_buf_t * decode_png_data(const void * png_data, size_t png_data_si
{ {
unsigned png_width; /*Not used, just required by the decoder*/ unsigned png_width; /*Not used, just required by the decoder*/
unsigned png_height; /*Not used, just required by the decoder*/ unsigned png_height; /*Not used, just required by the decoder*/
uint8_t * img_data = NULL; lv_draw_buf_t * decoded = NULL;
lv_draw_buf_t * decoded;
/*Decode the image in ARGB8888 */ /*Decode the image in ARGB8888 */
unsigned error = lodepng_decode32((unsigned char **)&decoded, &png_width, &png_height, png_data, png_data_size); unsigned error = lodepng_decode32((unsigned char **)&decoded, &png_width, &png_height, png_data, png_data_size);
if(error) { if(error) {
if(img_data != NULL) lv_draw_buf_destroy(decoded); if(decoded != NULL) lv_draw_buf_destroy(decoded);
return NULL; return NULL;
} }