1
0
mirror of https://github.com/lvgl/lvgl.git synced 2025-01-28 07:03:00 +08:00

feat(bin_decoder): post process decoded image to fulfill decoder's args (#5082)

Signed-off-by: Xu Xingliang <xuxingliang@xiaomi.com>
This commit is contained in:
Neo Xu 2023-12-23 04:49:42 +08:00 committed by GitHub
parent 2fa9847201
commit 755810bf8e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 209 additions and 41 deletions

View File

@ -166,7 +166,7 @@ lv_draw_buf_t * lv_draw_buf_create(uint32_t w, uint32_t h, lv_color_format_t cf,
draw_buf->header.w = w;
draw_buf->header.h = h;
draw_buf->header.cf = cf;
draw_buf->header.flags = LV_IMAGE_FLAGS_MODIFIABLE;
draw_buf->header.flags = LV_IMAGE_FLAGS_MODIFIABLE | LV_IMAGE_FLAGS_ALLOCATED;
draw_buf->header.stride = stride;
draw_buf->data = lv_draw_buf_align(buf, cf);
draw_buf->unaligned_data = buf;
@ -174,14 +174,32 @@ lv_draw_buf_t * lv_draw_buf_create(uint32_t w, uint32_t h, lv_color_format_t cf,
return draw_buf;
}
lv_draw_buf_t * lv_draw_buf_dup(const lv_draw_buf_t * draw_buf)
{
const lv_image_header_t * header = &draw_buf->header;
lv_draw_buf_t * new_buf = lv_draw_buf_create(header->w, header->h, header->cf, header->stride);
if(new_buf == NULL) return NULL;
/*Choose the smaller size to copy*/
uint32_t size = LV_MIN(draw_buf->data_size, new_buf->data_size);
/*Copy image data*/
lv_memcpy(new_buf->data, draw_buf->data, size);
return new_buf;
}
void lv_draw_buf_destroy(lv_draw_buf_t * buf)
{
LV_ASSERT_NULL(buf);
if(buf == NULL) return;
if(buf->header.flags & LV_IMAGE_FLAGS_MODIFIABLE)
lv_draw_buf_free(buf->unaligned_data);
lv_free(buf);
if(buf->header.flags & LV_IMAGE_FLAGS_ALLOCATED) {
lv_draw_buf_free(buf->unaligned_data);
lv_free(buf);
}
else {
LV_LOG_ERROR("draw buffer is not allocated, ignored");
}
}
void * lv_draw_buf_goto_xy(lv_draw_buf_t * buf, uint32_t x, uint32_t y)
@ -230,6 +248,69 @@ lv_draw_buf_t * lv_draw_buf_adjust_stride(const lv_draw_buf_t * src, uint32_t st
return dst;
}
lv_result_t lv_draw_buf_premultiply(lv_draw_buf_t * draw_buf)
{
LV_ASSERT_NULL(draw_buf);
if(draw_buf == NULL) return LV_RESULT_INVALID;
if(draw_buf->header.flags & LV_IMAGE_FLAGS_PREMULTIPLIED) return LV_RESULT_INVALID;
if((draw_buf->header.flags & LV_IMAGE_FLAGS_MODIFIABLE) == 0) {
LV_LOG_WARN("draw buf is not modifiable: 0x%04x", draw_buf->header.flags);
return LV_RESULT_INVALID;
}
/*Premultiply color with alpha, do case by case by judging color format*/
lv_color_format_t cf = draw_buf->header.cf;
if(LV_COLOR_FORMAT_IS_INDEXED(cf)) {
int size = LV_COLOR_INDEXED_PALETTE_SIZE(cf);
lv_color32_t * palette = (lv_color32_t *)draw_buf->data;
for(int i = 0; i < size; i++) {
lv_color_premultiply(&palette[i]);
}
}
else if(cf == LV_COLOR_FORMAT_ARGB8888) {
uint32_t h = draw_buf->header.h;
uint32_t w = draw_buf->header.w;
uint32_t stride = draw_buf->header.stride;
uint8_t * line = (uint8_t *)draw_buf->data;
for(uint32_t y = 0; y < h; y++) {
lv_color32_t * pixel = (lv_color32_t *)line;
for(uint32_t x = 0; x < w; x++) {
lv_color_premultiply(pixel);
pixel++;
}
line += stride;
}
}
else if(cf == LV_COLOR_FORMAT_RGB565A8) {
uint32_t h = draw_buf->header.h;
uint32_t w = draw_buf->header.w;
uint32_t stride = draw_buf->header.stride;
uint32_t alpha_stride = stride / 2;
uint8_t * line = (uint8_t *)draw_buf->data;
lv_opa_t * alpha = (lv_opa_t *)(line + stride * h);
for(uint32_t y = 0; y < h; y++) {
lv_color16_t * pixel = (lv_color16_t *)line;
for(uint32_t x = 0; x < w; x++) {
lv_color16_premultiply(pixel, alpha[x]);
pixel++;
}
line += stride;
alpha += alpha_stride;
}
}
else if(LV_COLOR_FORMAT_IS_ALPHA_ONLY(cf)) {
/*Pass*/
}
else {
LV_LOG_WARN("draw buf has no alpha, cf: %d", cf);
}
draw_buf->header.flags |= LV_IMAGE_FLAGS_PREMULTIPLIED;
return LV_RESULT_OK;
}
/**********************
* STATIC FUNCTIONS
**********************/

View File

@ -181,6 +181,13 @@ void lv_draw_buf_copy(void * dest_buf, uint32_t dest_w, uint32_t dest_h, const l
*/
lv_draw_buf_t * lv_draw_buf_create(uint32_t w, uint32_t h, lv_color_format_t cf, uint32_t stride);
/**
* Duplicate a draw buf with same image size, stride and color format. Copy the image data too.
* @param draw_buf the draw buf to duplicate
* @return the duplicated draw buf on success, NULL if failed
*/
lv_draw_buf_t * lv_draw_buf_dup(const lv_draw_buf_t * draw_buf);
/**
* Destroy a draw buf by free the actual buffer if it's marked as LV_IMAGE_FLAGS_MODIFIABLE in header.
* Then free the lv_draw_buf_t struct.
@ -197,6 +204,20 @@ void * lv_draw_buf_goto_xy(lv_draw_buf_t * buf, uint32_t x, uint32_t y);
*/
lv_draw_buf_t * lv_draw_buf_adjust_stride(const lv_draw_buf_t * src, uint32_t stride);
/**
* Premultiply draw buffer color with alpha channel.
* If it's already premultiplied, return directly.
* Only color formats with alpha channel will be processed.
*
* @return LV_RESULT_OK: premultiply success
*/
lv_result_t lv_draw_buf_premultiply(lv_draw_buf_t * draw_buf);
static inline bool lv_draw_buf_has_flag(lv_draw_buf_t * draw_buf, lv_image_flags_t flag)
{
return draw_buf->header.flags & flag;
}
/**
* As of now, draw buf share same definition as `lv_image_dsc_t`.
* And is interchangeable with `lv_image_dsc_t`.

View File

@ -75,6 +75,11 @@ typedef enum _lv_image_flags_t {
*/
LV_IMAGE_FLAGS_COMPRESSED = (1 << 3),
/**
* The image is alloced from heap, thus should be freed after use.
*/
LV_IMAGE_FLAGS_ALLOCATED = (1 << 4),
/**
* Flags reserved for user, lvgl won't use these bits.
*/

View File

@ -115,17 +115,14 @@ lv_result_t lv_image_decoder_open(lv_image_decoder_dsc_t * dsc, const void * src
lv_image_decoder_t * decoder;
lv_image_decoder_args_t * args_copy = NULL;
static const lv_image_decoder_args_t def_args = {
.stride_align = LV_DRAW_BUF_STRIDE_ALIGN != 1,
.premultiply = false,
.no_cache = false,
};
/*Make a copy of args */
if(args) {
args_copy = lv_malloc(sizeof(lv_image_decoder_args_t));
LV_ASSERT_MALLOC(args_copy);
if(args_copy == NULL) {
LV_LOG_WARN("Out of memory");
return LV_RESULT_INVALID;
}
lv_memcpy(args_copy, args, sizeof(lv_image_decoder_args_t));
dsc->args = args_copy;
}
dsc->args = args ? *args : def_args;
_LV_LL_READ(img_decoder_ll_p, decoder) {
/*Info and Open callbacks are required*/
@ -174,7 +171,6 @@ void lv_image_decoder_close(lv_image_decoder_dsc_t * dsc)
{
if(dsc->decoder) {
if(dsc->decoder->close_cb) dsc->decoder->close_cb(dsc->decoder, dsc);
if(dsc->args) lv_free(dsc->args);
if(dsc->src_type == LV_IMAGE_SRC_FILE) {
lv_free((void *)dsc->src);
@ -233,6 +229,48 @@ void lv_image_decoder_set_close_cb(lv_image_decoder_t * decoder, lv_image_decode
decoder->close_cb = close_cb;
}
lv_draw_buf_t * lv_image_decoder_post_process(lv_image_decoder_dsc_t * dsc, lv_draw_buf_t * decoded)
{
if(decoded == NULL) return NULL; /*No need to adjust*/
lv_image_decoder_args_t * args = &dsc->args;
if(args->stride_align && decoded->header.cf != LV_COLOR_FORMAT_RGB565A8) {
uint32_t stride_expect = lv_draw_buf_width_to_stride(decoded->header.w, decoded->header.cf);
if(decoded->header.stride != stride_expect) {
LV_LOG_WARN("Stride mismatch");
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;
}
decoded = aligned;
}
}
/*Premultiply alpha channel*/
if(args->premultiply
&& !lv_draw_buf_has_flag(decoded, LV_IMAGE_FLAGS_PREMULTIPLIED) /*Hasn't done yet*/
) {
LV_LOG_WARN("Alpha premultiply.");
if(lv_draw_buf_has_flag(decoded, LV_IMAGE_FLAGS_MODIFIABLE)) {
/*Do it directly*/
lv_draw_buf_premultiply(decoded);
}
else {
decoded = lv_draw_buf_dup(decoded);
if(decoded == NULL) {
LV_LOG_ERROR("No memory for premulitplying.");
return NULL;
}
lv_draw_buf_premultiply(decoded);
}
}
return decoded;
}
static uint32_t img_width_to_stride(lv_image_header_t * header)
{
if(header->cf == LV_COLOR_FORMAT_RGB565A8) {
@ -242,4 +280,3 @@ static uint32_t img_width_to_stride(lv_image_header_t * header)
return ((uint32_t)header->w * lv_color_format_get_bpp(header->cf) + 7) >> 3;
}
}

View File

@ -121,7 +121,7 @@ typedef struct _lv_image_decoder_dsc_t {
lv_image_decoder_t * decoder;
/*A copy of parameters of how this image is decoded*/
lv_image_decoder_args_t * args;
lv_image_decoder_args_t args;
/**The image source. A file path like "S:my_img.png" or pointer to an `lv_image_dsc_t` variable*/
const void * src;
@ -268,6 +268,15 @@ void lv_image_decoder_set_get_area_cb(lv_image_decoder_t * decoder, lv_image_dec
*/
void lv_image_decoder_set_close_cb(lv_image_decoder_t * decoder, lv_image_decoder_close_f_t close_cb);
/**
* Check the decoded image, make any modification if decoder `args` requires.
* @note A new draw buf will be allocated if provided `decoded` is not modifiable or stride mismatch etc.
* @param dsc pointer to a decoder descriptor
* @param decoded pointer to a decoded image to post process to meet dsc->args requirement.
* @return post processed draw buffer, when it differs with `decoded`, it's newly allocated.
*/
lv_draw_buf_t * lv_image_decoder_post_process(lv_image_decoder_dsc_t * dsc, lv_draw_buf_t * decoded);
/**********************
* MACROS
**********************/

View File

@ -132,8 +132,8 @@ void lv_draw_sw_arc(lv_draw_unit_t * draw_unit, const lv_draw_arc_dsc_t * dsc, c
lv_area_move(&img_area, dsc->center.x - ofs, dsc->center.y - ofs);
blend_dsc.src_area = &img_area;
blend_dsc.src_buf = _lv_image_decoder_get_data(&decoder_dsc);
blend_dsc.src_color_format = decoder_dsc.header.cf;
blend_dsc.src_stride = decoder_dsc.header.stride;
blend_dsc.src_color_format = decoder_dsc.decoded->header.cf;
blend_dsc.src_stride = decoder_dsc.decoded->header.stride;
}
lv_opa_t * circle_mask = NULL;

View File

@ -291,7 +291,8 @@ static void _set_paint_fill_pattern(Tvg_Paint * obj, Tvg_Canvas * canvas, const
const lv_matrix_t * m)
{
lv_image_decoder_dsc_t decoder_dsc;
lv_result_t res = lv_image_decoder_open(&decoder_dsc, p->src, NULL);
lv_image_decoder_args_t args = { 0 };
lv_result_t res = lv_image_decoder_open(&decoder_dsc, p->src, &args);
if(res != LV_RESULT_OK) {
LV_LOG_ERROR("Failed to open image");
return;

View File

@ -160,7 +160,9 @@ lv_result_t lv_bin_decoder_info(lv_image_decoder_t * decoder, const void * src,
}
/*For backward compatibility, all images are not premultiplied for now.*/
header->flags &= ~LV_IMAGE_FLAGS_PREMULTIPLIED;
if(header->magic != LV_IMAGE_HEADER_MAGIC) {
header->flags &= ~LV_IMAGE_FLAGS_PREMULTIPLIED;
}
return LV_RESULT_OK;
}
@ -276,24 +278,6 @@ lv_result_t lv_bin_decoder_open(lv_image_decoder_t * decoder, lv_image_decoder_d
decoded->header.stride = dsc->header.stride;
}
/**
* @todo need to convert c-array image stride if not match
*
* lv_draw_buf_create(); //create new draw buf that meets requirement
* lv_draw_buf_copy(); //copy from c-array image to new draw buf
*/
uint32_t stride_expect = lv_draw_buf_width_to_stride(dsc->header.w, dsc->header.cf);
if(dsc->header.stride != stride_expect) {
LV_LOG_WARN("Stride mismatch");
#if 0
/**
* @fixme ignore for now
*/
free_decoder_data(dsc);
return LV_RESULT_INVALID;
#endif
}
res = LV_RESULT_OK;
}
}
@ -305,6 +289,22 @@ lv_result_t lv_bin_decoder_open(lv_image_decoder_t * decoder, lv_image_decoder_d
if(dsc->decoded == NULL) return LV_RESULT_OK; /*Need to read via get_area_cb*/
lv_draw_buf_t * decoded = (lv_draw_buf_t *)dsc->decoded;
lv_draw_buf_t * adjusted = lv_image_decoder_post_process(dsc, decoded);
if(adjusted == NULL) {
free_decoder_data(dsc);
return LV_RESULT_INVALID;
}
/*The adjusted draw buffer is newly allocated.*/
if(adjusted != decoded) {
free_decoder_data(dsc);
decoder_data_t * decoder_data = get_decoder_data(dsc);
decoder_data->decoded = adjusted; /*Now this new buffer need to be free'd on decoder close*/
}
dsc->decoded = adjusted;
/*Add it to cache*/
t = lv_tick_elaps(t);
lv_cache_lock();

View File

@ -407,6 +407,20 @@ static inline lv_color_t lv_color_black(void)
return lv_color_make(0x00, 0x00, 0x00);
}
static inline void lv_color_premultiply(lv_color32_t * c)
{
c->red = LV_OPA_MIX2(c->red, c->alpha);
c->green = LV_OPA_MIX2(c->green, c->alpha);
c->blue = LV_OPA_MIX2(c->blue, c->alpha);
}
static inline void lv_color16_premultiply(lv_color16_t * c, lv_opa_t a)
{
c->red = LV_OPA_MIX2(c->red, a);
c->green = LV_OPA_MIX2(c->green, a);
c->blue = LV_OPA_MIX2(c->blue, a);
}
/**********************
* MACROS
**********************/

View File

@ -100,13 +100,13 @@ lv_result_t lv_snapshot_take_to_buf(lv_obj_t * obj, lv_color_format_t cf, lv_ima
lv_area_increase(&snapshot_area, ext_size, ext_size);
lv_memzero(buf, buf_size);
lv_memzero(dsc, sizeof(lv_image_dsc_t));
dsc->data = buf;
dsc->data_size = buf_size_needed;
/*Keep header flags unchanged, because we don't know if it's allocated or not.*/
dsc->header.w = w;
dsc->header.h = h;
dsc->header.cf = cf;
dsc->header.flags = LV_IMAGE_FLAGS_MODIFIABLE;
dsc->header.magic = LV_IMAGE_HEADER_MAGIC;
lv_layer_t layer;
lv_memzero(&layer, sizeof(layer));