From 755810bf8e0efa18ffe34a7db5510e137bd4f5f5 Mon Sep 17 00:00:00 2001 From: Neo Xu Date: Sat, 23 Dec 2023 04:49:42 +0800 Subject: [PATCH] feat(bin_decoder): post process decoded image to fulfill decoder's args (#5082) Signed-off-by: Xu Xingliang --- src/draw/lv_draw_buf.c | 89 +++++++++++++++++++++++++-- src/draw/lv_draw_buf.h | 21 +++++++ src/draw/lv_image_buf.h | 5 ++ src/draw/lv_image_decoder.c | 61 ++++++++++++++---- src/draw/lv_image_decoder.h | 11 +++- src/draw/sw/lv_draw_sw_arc.c | 4 +- src/draw/sw/lv_draw_sw_vector.c | 3 +- src/libs/bin_decoder/lv_bin_decoder.c | 38 ++++++------ src/misc/lv_color.h | 14 +++++ src/others/snapshot/lv_snapshot.c | 4 +- 10 files changed, 209 insertions(+), 41 deletions(-) diff --git a/src/draw/lv_draw_buf.c b/src/draw/lv_draw_buf.c index cb484bf89..67454042c 100644 --- a/src/draw/lv_draw_buf.c +++ b/src/draw/lv_draw_buf.c @@ -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 **********************/ diff --git a/src/draw/lv_draw_buf.h b/src/draw/lv_draw_buf.h index c8354c058..72577a04a 100644 --- a/src/draw/lv_draw_buf.h +++ b/src/draw/lv_draw_buf.h @@ -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`. diff --git a/src/draw/lv_image_buf.h b/src/draw/lv_image_buf.h index d3482cc5f..c2774db91 100644 --- a/src/draw/lv_image_buf.h +++ b/src/draw/lv_image_buf.h @@ -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. */ diff --git a/src/draw/lv_image_decoder.c b/src/draw/lv_image_decoder.c index 74b4e8a03..bdfb32b8d 100644 --- a/src/draw/lv_image_decoder.c +++ b/src/draw/lv_image_decoder.c @@ -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; } } - diff --git a/src/draw/lv_image_decoder.h b/src/draw/lv_image_decoder.h index e1112abfb..3b15b73b2 100644 --- a/src/draw/lv_image_decoder.h +++ b/src/draw/lv_image_decoder.h @@ -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 **********************/ diff --git a/src/draw/sw/lv_draw_sw_arc.c b/src/draw/sw/lv_draw_sw_arc.c index 3e0d4a50a..6030e7057 100644 --- a/src/draw/sw/lv_draw_sw_arc.c +++ b/src/draw/sw/lv_draw_sw_arc.c @@ -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; diff --git a/src/draw/sw/lv_draw_sw_vector.c b/src/draw/sw/lv_draw_sw_vector.c index 06f0a0a43..15d9c1ba4 100644 --- a/src/draw/sw/lv_draw_sw_vector.c +++ b/src/draw/sw/lv_draw_sw_vector.c @@ -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; diff --git a/src/libs/bin_decoder/lv_bin_decoder.c b/src/libs/bin_decoder/lv_bin_decoder.c index f2db7ff42..3fd71db63 100644 --- a/src/libs/bin_decoder/lv_bin_decoder.c +++ b/src/libs/bin_decoder/lv_bin_decoder.c @@ -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(); diff --git a/src/misc/lv_color.h b/src/misc/lv_color.h index 25eb06b10..ee2603fed 100644 --- a/src/misc/lv_color.h +++ b/src/misc/lv_color.h @@ -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 **********************/ diff --git a/src/others/snapshot/lv_snapshot.c b/src/others/snapshot/lv_snapshot.c index 10446d320..1562e9e1c 100644 --- a/src/others/snapshot/lv_snapshot.c +++ b/src/others/snapshot/lv_snapshot.c @@ -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));