From ce6f56a7423193108c17ad3464ae79c8d63f8e4c Mon Sep 17 00:00:00 2001 From: _VIFEXTech Date: Thu, 26 Oct 2023 12:47:11 +0800 Subject: [PATCH] feat(image_decoder): add post-process image method (#4636) Signed-off-by: pengyiqiang Co-authored-by: pengyiqiang --- docs/overview/img.rst | 121 ++++++++++++++++++++++ src/draw/lv_image_decoder.c | 1 + src/draw/lv_image_decoder.h | 4 + src/libs/libjpeg_turbo/lv_libjpeg_turbo.c | 6 +- src/libs/libpng/lv_libpng.c | 6 +- src/libs/lodepng/lv_lodepng.c | 8 +- src/misc/lv_cache.h | 5 +- 7 files changed, 140 insertions(+), 11 deletions(-) diff --git a/docs/overview/img.rst b/docs/overview/img.rst index deaaa255c..309990bd8 100644 --- a/docs/overview/img.rst +++ b/docs/overview/img.rst @@ -350,6 +350,127 @@ to open is an animation. lv_image_decoder_close(&dsc); } + +Image post-processing +--------------------- + +Considering that some hardware has special requirements for image formats, +such as alpha premultiplication and stride alignment, most image decoders (such as PNG decoders) +may not directly output image data that meets hardware requirements. + +For this reason, LVGL provides a solution for image post-processing. +First, call a custom post-processing function after ``lv_image_decoder_open`` to adjust the data in the image cache, +and then mark the processing status in ``cache_entry->process_state`` (to avoid repeated post-processing). + +See the detailed code below: + +- Stride alignment and premultiply post-processing example: + +.. code:: c + + /* Define post-processing state */ + typedef enum { + IMAGE_PROCESS_STATE_NONE = 0, + IMAGE_PROCESS_STATE_STRIDE_ALIGNED = 1 << 0, + IMAGE_PROCESS_STATE_PREMULTIPLIED_ALPHA = 1 << 1, + } image_process_state_t; + + lv_result_t my_image_post_process(lv_image_decoder_dsc_t * dsc) + { + lv_color_format_t color_format = dsc->header.cf; + lv_result_t res = LV_RESULT_OK; + + if(color_format == LV_COLOR_FORMAT_ARGB8888) { + lv_cache_lock(); + lv_cache_entry_t * entry = dsc->cache_entry; + + if(!(entry->process_state & IMAGE_PROCESS_STATE_PREMULTIPLIED_ALPHA)) { + lv_color32_t * image = (lv_color32_t *)dsc->img_data; + 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"); + + entry->process_state |= IMAGE_PROCESS_STATE_PREMULTIPLIED_ALPHA; + } + + if(!(entry->process_state & IMAGE_PROCESS_STATE_STRIDE_ALIGNED)) { + lv_coord_t image_w = dsc->header.w; + lv_coord_t image_h = dsc->header.h; + uint32_t width_byte = image_w * lv_color_format_get_size(color_format); + uint32_t stride = lv_draw_buf_width_to_stride(image_w, color_format); + + /* Check stride alignment requirements */ + 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); + 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(lv_coord_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: + lv_cache_unlock(); + } + + return res; + } + +- GPU draw unit example: + +.. code:: c + + void gpu_draw_image(lv_draw_unit_t * draw_unit, const lv_draw_image_dsc_t * draw_dsc, const lv_area_t * coords) + { + ... + lv_image_decoder_dsc_t decoder_dsc; + lv_result_t res = lv_image_decoder_open(&decoder_dsc, draw_dsc->src, draw_dsc->recolor, -1); + if(res != LV_RESULT_OK) { + LV_LOG_ERROR("Failed to open image"); + return; + } + + res = my_image_post_process(&decoder_dsc); + if(res != LV_RESULT_OK) { + LV_LOG_ERROR("Failed to post-process image"); + return; + } + ... + } + .. _image-caching: Image caching diff --git a/src/draw/lv_image_decoder.c b/src/draw/lv_image_decoder.c index a3b9cb92e..9497b18cf 100644 --- a/src/draw/lv_image_decoder.c +++ b/src/draw/lv_image_decoder.c @@ -160,6 +160,7 @@ lv_result_t lv_image_decoder_open(lv_image_decoder_dsc_t * dsc, const void * src dsc->error_msg = NULL; dsc->img_data = NULL; + dsc->cache_entry = NULL; dsc->user_data = NULL; dsc->time_to_open = 0; } diff --git a/src/draw/lv_image_decoder.h b/src/draw/lv_image_decoder.h index ea3a1895f..4b8deb01b 100644 --- a/src/draw/lv_image_decoder.h +++ b/src/draw/lv_image_decoder.h @@ -47,6 +47,7 @@ typedef uint8_t lv_image_src_t; /*Decoder function definitions*/ struct _lv_image_decoder_dsc_t; struct _lv_image_decoder_t; +struct _lv_cache_entry_t; /** * Get info from an image and store in the `header` @@ -132,6 +133,9 @@ typedef struct _lv_image_decoder_dsc_t { * Can be set in `open` function or set NULL.*/ const char * error_msg; + /**Point to cache entry information*/ + struct _lv_cache_entry_t * cache_entry; + /**Store any custom data here is required*/ void * user_data; } lv_image_decoder_dsc_t; diff --git a/src/libs/libjpeg_turbo/lv_libjpeg_turbo.c b/src/libs/libjpeg_turbo/lv_libjpeg_turbo.c index 330b06c99..c91386971 100644 --- a/src/libs/libjpeg_turbo/lv_libjpeg_turbo.c +++ b/src/libs/libjpeg_turbo/lv_libjpeg_turbo.c @@ -180,7 +180,7 @@ static lv_result_t decoder_open(lv_image_decoder_t * decoder, lv_image_decoder_d } dsc->img_data = lv_cache_get_data(cache); - dsc->user_data = cache; + dsc->cache_entry = cache; lv_cache_unlock(); return LV_RESULT_OK; /*If not returned earlier then it failed*/ @@ -196,7 +196,7 @@ static void decoder_close(lv_image_decoder_t * decoder, lv_image_decoder_dsc_t * { LV_UNUSED(decoder); /*Unused*/ lv_cache_lock(); - lv_cache_release(dsc->user_data); + lv_cache_release(dsc->cache_entry); lv_cache_unlock(); } @@ -209,7 +209,7 @@ static lv_result_t try_cache(lv_image_decoder_dsc_t * dsc) lv_cache_entry_t * cache = lv_cache_find(fn, LV_CACHE_SRC_TYPE_STR, 0, 0); if(cache) { dsc->img_data = lv_cache_get_data(cache); - dsc->user_data = cache; /*Save the cache to release it in decoder_close*/ + dsc->cache_entry = cache; /*Save the cache to release it in decoder_close*/ lv_cache_unlock(); return LV_RESULT_OK; } diff --git a/src/libs/libpng/lv_libpng.c b/src/libs/libpng/lv_libpng.c index 2627b505e..a4ec558ff 100644 --- a/src/libs/libpng/lv_libpng.c +++ b/src/libs/libpng/lv_libpng.c @@ -155,7 +155,7 @@ static lv_result_t decoder_open(lv_image_decoder_t * decoder, lv_image_decoder_d } dsc->img_data = lv_cache_get_data(cache); - dsc->user_data = cache; + dsc->cache_entry = cache; lv_cache_unlock(); return LV_RESULT_OK; /*The image is fully decoded. Return with its pointer*/ @@ -172,7 +172,7 @@ static void decoder_close(lv_image_decoder_t * decoder, lv_image_decoder_dsc_t * LV_UNUSED(decoder); /*Unused*/ lv_cache_lock(); - lv_cache_release(dsc->user_data); + lv_cache_release(dsc->cache_entry); lv_cache_unlock(); } @@ -185,7 +185,7 @@ static lv_result_t try_cache(lv_image_decoder_dsc_t * dsc) lv_cache_entry_t * cache = lv_cache_find(fn, LV_CACHE_SRC_TYPE_STR, 0, 0); if(cache) { dsc->img_data = lv_cache_get_data(cache); - dsc->user_data = cache; /*Save the cache to release it in decoder_close*/ + dsc->cache_entry = cache; /*Save the cache to release it in decoder_close*/ lv_cache_unlock(); return LV_RESULT_OK; } diff --git a/src/libs/lodepng/lv_lodepng.c b/src/libs/lodepng/lv_lodepng.c index 5be93d0ef..104e3e384 100644 --- a/src/libs/lodepng/lv_lodepng.c +++ b/src/libs/lodepng/lv_lodepng.c @@ -209,7 +209,7 @@ static lv_result_t decoder_open(lv_image_decoder_t * decoder, lv_image_decoder_d } dsc->img_data = lv_cache_get_data(cache); - dsc->user_data = cache; + dsc->cache_entry = cache; lv_cache_unlock(); return LV_RESULT_OK; /*If not returned earlier then it failed*/ @@ -226,7 +226,7 @@ static void decoder_close(lv_image_decoder_t * decoder, lv_image_decoder_dsc_t * LV_UNUSED(decoder); lv_cache_lock(); - lv_cache_release(dsc->user_data); + lv_cache_release(dsc->cache_entry); lv_cache_unlock(); } @@ -240,7 +240,7 @@ static lv_result_t try_cache(lv_image_decoder_dsc_t * dsc) lv_cache_entry_t * cache = lv_cache_find(fn, LV_CACHE_SRC_TYPE_STR, 0, 0); if(cache) { dsc->img_data = lv_cache_get_data(cache); - dsc->user_data = cache; /*Save the cache to release it in decoder_close*/ + dsc->cache_entry = cache; /*Save the cache to release it in decoder_close*/ lv_cache_unlock(); return LV_RESULT_OK; } @@ -252,7 +252,7 @@ static lv_result_t try_cache(lv_image_decoder_dsc_t * dsc) lv_cache_entry_t * cache = lv_cache_find(img_dsc, LV_CACHE_SRC_TYPE_PTR, 0, 0); if(cache) { dsc->img_data = lv_cache_get_data(cache); - dsc->user_data = cache; /*Save the cache to release it in decoder_close*/ + dsc->cache_entry = cache; /*Save the cache to release it in decoder_close*/ lv_cache_unlock(); return LV_RESULT_OK; } diff --git a/src/misc/lv_cache.h b/src/misc/lv_cache.h index f5b8dfc81..c3e259ee9 100644 --- a/src/misc/lv_cache.h +++ b/src/misc/lv_cache.h @@ -31,7 +31,7 @@ typedef enum { _LV_CACHE_SRC_TYPE_LAST, } lv_cache_src_type_t; -typedef struct { +typedef struct _lv_cache_entry_t { /**The image source or other source related to the cache content.*/ const void * src; @@ -41,6 +41,9 @@ typedef struct { uint32_t param1; uint32_t param2; + /** User processing tag*/ + uint32_t process_state; + /** The data to cache*/ const void * data;