feat(image_decoder): add post-process image method (#4636)

Signed-off-by: pengyiqiang <pengyiqiang@xiaomi.com>
Co-authored-by: pengyiqiang <pengyiqiang@xiaomi.com>
This commit is contained in:
_VIFEXTech
2023-10-26 12:47:11 +08:00
committed by GitHub
parent e869d76f53
commit ce6f56a742
7 changed files with 140 additions and 11 deletions

View File

@@ -350,6 +350,127 @@ to open is an animation.
lv_image_decoder_close(&dsc); 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:
Image caching Image caching

View File

@@ -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->error_msg = NULL;
dsc->img_data = NULL; dsc->img_data = NULL;
dsc->cache_entry = NULL;
dsc->user_data = NULL; dsc->user_data = NULL;
dsc->time_to_open = 0; dsc->time_to_open = 0;
} }

View File

@@ -47,6 +47,7 @@ typedef uint8_t lv_image_src_t;
/*Decoder function definitions*/ /*Decoder function definitions*/
struct _lv_image_decoder_dsc_t; struct _lv_image_decoder_dsc_t;
struct _lv_image_decoder_t; struct _lv_image_decoder_t;
struct _lv_cache_entry_t;
/** /**
* Get info from an image and store in the `header` * 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.*/ * Can be set in `open` function or set NULL.*/
const char * error_msg; const char * error_msg;
/**Point to cache entry information*/
struct _lv_cache_entry_t * cache_entry;
/**Store any custom data here is required*/ /**Store any custom data here is required*/
void * user_data; void * user_data;
} lv_image_decoder_dsc_t; } lv_image_decoder_dsc_t;

View File

@@ -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->img_data = lv_cache_get_data(cache);
dsc->user_data = cache; dsc->cache_entry = cache;
lv_cache_unlock(); lv_cache_unlock();
return LV_RESULT_OK; /*If not returned earlier then it failed*/ 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_UNUSED(decoder); /*Unused*/
lv_cache_lock(); lv_cache_lock();
lv_cache_release(dsc->user_data); lv_cache_release(dsc->cache_entry);
lv_cache_unlock(); 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); lv_cache_entry_t * cache = lv_cache_find(fn, LV_CACHE_SRC_TYPE_STR, 0, 0);
if(cache) { if(cache) {
dsc->img_data = lv_cache_get_data(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(); lv_cache_unlock();
return LV_RESULT_OK; return LV_RESULT_OK;
} }

View File

@@ -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->img_data = lv_cache_get_data(cache);
dsc->user_data = cache; dsc->cache_entry = cache;
lv_cache_unlock(); lv_cache_unlock();
return LV_RESULT_OK; /*The image is fully decoded. Return with its pointer*/ 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_UNUSED(decoder); /*Unused*/
lv_cache_lock(); lv_cache_lock();
lv_cache_release(dsc->user_data); lv_cache_release(dsc->cache_entry);
lv_cache_unlock(); 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); lv_cache_entry_t * cache = lv_cache_find(fn, LV_CACHE_SRC_TYPE_STR, 0, 0);
if(cache) { if(cache) {
dsc->img_data = lv_cache_get_data(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(); lv_cache_unlock();
return LV_RESULT_OK; return LV_RESULT_OK;
} }

View File

@@ -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->img_data = lv_cache_get_data(cache);
dsc->user_data = cache; dsc->cache_entry = cache;
lv_cache_unlock(); lv_cache_unlock();
return LV_RESULT_OK; /*If not returned earlier then it failed*/ 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_UNUSED(decoder);
lv_cache_lock(); lv_cache_lock();
lv_cache_release(dsc->user_data); lv_cache_release(dsc->cache_entry);
lv_cache_unlock(); 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); lv_cache_entry_t * cache = lv_cache_find(fn, LV_CACHE_SRC_TYPE_STR, 0, 0);
if(cache) { if(cache) {
dsc->img_data = lv_cache_get_data(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(); lv_cache_unlock();
return LV_RESULT_OK; 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); lv_cache_entry_t * cache = lv_cache_find(img_dsc, LV_CACHE_SRC_TYPE_PTR, 0, 0);
if(cache) { if(cache) {
dsc->img_data = lv_cache_get_data(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(); lv_cache_unlock();
return LV_RESULT_OK; return LV_RESULT_OK;
} }

View File

@@ -31,7 +31,7 @@ typedef enum {
_LV_CACHE_SRC_TYPE_LAST, _LV_CACHE_SRC_TYPE_LAST,
} lv_cache_src_type_t; } lv_cache_src_type_t;
typedef struct { typedef struct _lv_cache_entry_t {
/**The image source or other source related to the cache content.*/ /**The image source or other source related to the cache content.*/
const void * src; const void * src;
@@ -41,6 +41,9 @@ typedef struct {
uint32_t param1; uint32_t param1;
uint32_t param2; uint32_t param2;
/** User processing tag*/
uint32_t process_state;
/** The data to cache*/ /** The data to cache*/
const void * data; const void * data;