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:
@@ -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
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user