From 99371383921a5606625ce01d6ffe2902a8bc6a5d Mon Sep 17 00:00:00 2001 From: _VIFEXTech Date: Mon, 25 Sep 2023 16:28:35 +0800 Subject: [PATCH] feat(libs): add libpng decoder (#4569) Signed-off-by: pengyiqiang Co-authored-by: pengyiqiang Co-authored-by: Gabor Kiss-Vamosi --- Kconfig | 3 + docs/libs/index.rst | 1 + docs/libs/libpng.rst | 42 +++ examples/libs/libpng/index.rst | 6 + examples/libs/libpng/lv_example_libpng.h | 38 +++ examples/libs/libpng/lv_example_libpng_1.c | 31 ++ examples/libs/libpng/lv_example_libpng_1.py | 11 + examples/libs/libpng/png_demo.png | Bin 0 -> 20608 bytes examples/libs/lv_example_libs.h | 1 + lv_conf_template.h | 3 + lvgl.h | 1 + src/libs/libpng/lv_libpng.c | 304 ++++++++++++++++++++ src/libs/libpng/lv_libpng.h | 48 ++++ src/lv_conf_internal.h | 9 + src/lv_init.c | 5 + tests/ref_imgs/libs/png_2.png | Bin 0 -> 4600 bytes tests/src/lv_test_conf_full.h | 1 + tests/src/test_cases/libs/test_libpng.c | 54 ++++ tests/src/test_cases/libs/test_lodepng.c | 5 + 19 files changed, 563 insertions(+) create mode 100644 docs/libs/libpng.rst create mode 100644 examples/libs/libpng/index.rst create mode 100644 examples/libs/libpng/lv_example_libpng.h create mode 100644 examples/libs/libpng/lv_example_libpng_1.c create mode 100755 examples/libs/libpng/lv_example_libpng_1.py create mode 100644 examples/libs/libpng/png_demo.png create mode 100644 src/libs/libpng/lv_libpng.c create mode 100644 src/libs/libpng/lv_libpng.h create mode 100644 tests/ref_imgs/libs/png_2.png create mode 100644 tests/src/test_cases/libs/test_libpng.c diff --git a/Kconfig b/Kconfig index 8705894da..7fd6b5527 100644 --- a/Kconfig +++ b/Kconfig @@ -1011,6 +1011,9 @@ menu "LVGL configuration" config LV_USE_LODEPNG bool "PNG decoder library" + config LV_USE_LIBPNG + bool "PNG decoder(libpng) library" + config LV_USE_BMP bool "BMP decoder library" diff --git a/docs/libs/index.rst b/docs/libs/index.rst index eea3a9034..b92201e3a 100644 --- a/docs/libs/index.rst +++ b/docs/libs/index.rst @@ -11,6 +11,7 @@ tjpgd libjpeg_turbo lodepng + libpng gif freetype tiny_ttf diff --git a/docs/libs/libpng.rst b/docs/libs/libpng.rst new file mode 100644 index 000000000..b8bb0d0ec --- /dev/null +++ b/docs/libs/libpng.rst @@ -0,0 +1,42 @@ +============== +libpng decoder +============== + +libpng is the official PNG reference library. It supports almost all PNG features, is extensible, and has been extensively tested for over 28 years. +Detailed introduction: `libpng `__. + +Install +------- + +.. code:: bash + + sudo apt install libpng-dev + +Add libpng to your project +-------------------------- + +.. code:: cmake + + find_package(PNG REQUIRED) + include_directories(${PNG_INCLUDE_DIR}) + target_link_libraries(${PROJECT_NAME} PRIVATE ${PNG_LIBRARIES}) + +Usage +----- + +Enable :c:macro:`LV_USE_LIBPNG` in ``lv_conf.h``. + +See the examples below. +It should be noted that each image of this decoder needs to consume ``image width x image height x 4`` bytes of RAM, +and it needs to be combined with the ref:`image-caching` feature to ensure that the memory usage is within a reasonable range. + +Example +------- + +.. include:: ../examples/libs/libpng/index.rst + +API +--- + +:ref:`libpng` + diff --git a/examples/libs/libpng/index.rst b/examples/libs/libpng/index.rst new file mode 100644 index 000000000..4604fa7c9 --- /dev/null +++ b/examples/libs/libpng/index.rst @@ -0,0 +1,6 @@ +Open a PNG image from file +-------------------------- + +.. lv_example:: libs/libpng/lv_example_libpng_1 + :language: c + diff --git a/examples/libs/libpng/lv_example_libpng.h b/examples/libs/libpng/lv_example_libpng.h new file mode 100644 index 000000000..f41004ca0 --- /dev/null +++ b/examples/libs/libpng/lv_example_libpng.h @@ -0,0 +1,38 @@ +/** + * @file lv_example_libpng.h + * + */ + +#ifndef LV_EXAMPLE_LIBPNG_H +#define LV_EXAMPLE_LIBPNG_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * GLOBAL PROTOTYPES + **********************/ +void lv_example_libpng_1(void); + +/********************** + * MACROS + **********************/ + +#ifdef __cplusplus +} /*extern "C"*/ +#endif + +#endif /*LV_EXAMPLE_LIBPNG_H*/ diff --git a/examples/libs/libpng/lv_example_libpng_1.c b/examples/libs/libpng/lv_example_libpng_1.c new file mode 100644 index 000000000..cd1a3214b --- /dev/null +++ b/examples/libs/libpng/lv_example_libpng_1.c @@ -0,0 +1,31 @@ +#include "../../lv_examples.h" +#if LV_BUILD_EXAMPLES + +#if LV_USE_LIBPNG + +/** + * Open a PNG image from a file + */ +void lv_example_libpng_1(void) +{ + lv_obj_t * img; + + img = lv_image_create(lv_scr_act()); + /* Assuming a File system is attached to letter 'A' + * E.g. set LV_USE_FS_STDIO 'A' in lv_conf.h */ + lv_image_set_src(img, "A:lvgl/examples/libs/libpng/png_demo.png"); + lv_obj_center(img); +} + +#else + +void lv_example_libpng_1(void) +{ + lv_obj_t * label = lv_label_create(lv_scr_act()); + lv_label_set_text(label, "LibPNG is not installed"); + lv_obj_center(label); +} + +#endif + +#endif diff --git a/examples/libs/libpng/lv_example_libpng_1.py b/examples/libs/libpng/lv_example_libpng_1.py new file mode 100755 index 000000000..8103f2590 --- /dev/null +++ b/examples/libs/libpng/lv_example_libpng_1.py @@ -0,0 +1,11 @@ +#!/opt/bin/lv_micropython -i +import lvgl as lv +import display_driver +import fs_driver + +fs_drv = lv.fs_drv_t() +fs_driver.fs_register(fs_drv, 'S') + +image = lv.image(lv.scr_act()) +image.set_src("S:png_demo.png") +image.center() diff --git a/examples/libs/libpng/png_demo.png b/examples/libs/libpng/png_demo.png new file mode 100644 index 0000000000000000000000000000000000000000..d8dbbb78eea870323e6849ddf55895ab002ef601 GIT binary patch literal 20608 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 z

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 + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ +static lv_result_t decoder_info(lv_image_decoder_t * decoder, const void * src, lv_image_header_t * header); +static lv_result_t decoder_open(lv_image_decoder_t * decoder, lv_image_decoder_dsc_t * dsc); +static void decoder_close(lv_image_decoder_t * decoder, lv_image_decoder_dsc_t * dsc); +static const void * decode_png_file(const char * filename); +static lv_result_t try_cache(lv_image_decoder_dsc_t * dsc); + +/********************** + * STATIC VARIABLES + **********************/ + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +/** + * Register the PNG decoder functions in LVGL + */ +void lv_libpng_init(void) +{ + lv_image_decoder_t * dec = lv_image_decoder_create(); + lv_image_decoder_set_info_cb(dec, decoder_info); + lv_image_decoder_set_open_cb(dec, decoder_open); + lv_image_decoder_set_close_cb(dec, decoder_close); +} + +void lv_libpng_deinit(void) +{ + lv_image_decoder_t * dec = NULL; + while((dec = lv_image_decoder_get_next(dec)) != NULL) { + if(dec->info_cb == decoder_info) { + lv_image_decoder_delete(dec); + break; + } + } +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +/** + * Get info about a PNG image + * @param src can be file name or pointer to a C array + * @param header store the info here + * @return LV_RESULT_OK: no error; LV_RESULT_INVALID: can't get the info + */ +static lv_result_t decoder_info(lv_image_decoder_t * decoder, const void * src, lv_image_header_t * header) +{ + LV_UNUSED(decoder); /*Unused*/ + lv_image_src_t src_type = lv_image_src_get_type(src); /*Get the source type*/ + + /*If it's a PNG file...*/ + if(src_type == LV_IMAGE_SRC_FILE) { + const char * fn = src; + + lv_fs_file_t f; + lv_fs_res_t res = lv_fs_open(&f, fn, LV_FS_MODE_RD); + if(res != LV_FS_RES_OK) return LV_RESULT_INVALID; + + /* Read the width and height from the file. They have a constant location: + * [16..19]: width + * [20..23]: height + */ + uint8_t buf[24]; + uint32_t rn; + lv_fs_read(&f, buf, sizeof(buf), &rn); + lv_fs_close(&f); + + if(rn != sizeof(buf)) return LV_RESULT_INVALID; + + const uint8_t magic[] = {0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a}; + if(memcmp(buf, magic, sizeof(magic)) != 0) return LV_RESULT_INVALID; + + uint32_t * size = (uint32_t *)&buf[16]; + /*Save the data in the header*/ + header->always_zero = 0; + header->cf = LV_COLOR_FORMAT_ARGB8888; + /*The width and height are stored in Big endian format so convert them to little endian*/ + header->w = (lv_coord_t)((size[0] & 0xff000000) >> 24) + ((size[0] & 0x00ff0000) >> 8); + header->h = (lv_coord_t)((size[1] & 0xff000000) >> 24) + ((size[1] & 0x00ff0000) >> 8); + + return LV_RESULT_OK; + } + + return LV_RESULT_INVALID; /*If didn't succeeded earlier then it's an error*/ +} + + +/** + * Open a PNG image and return the decided image + * @param src can be file name or pointer to a C array + * @param style style of the image object (unused now but certain formats might use it) + * @return pointer to the decoded image or `LV_IMAGE_DECODER_OPEN_FAIL` if failed + */ +static lv_result_t decoder_open(lv_image_decoder_t * decoder, lv_image_decoder_dsc_t * dsc) +{ + LV_UNUSED(decoder); /*Unused*/ + + /*Check the cache first*/ + if(try_cache(dsc) == LV_RESULT_OK) return LV_RESULT_OK; + + /*If it's a PNG file...*/ + if(dsc->src_type == LV_IMAGE_SRC_FILE) { + const char * fn = dsc->src; + lv_cache_lock(); + lv_cache_entry_t * cache = lv_cache_add(dsc->header.w * dsc->header.h * sizeof(uint32_t)); + if(cache == NULL) { + lv_cache_unlock(); + return LV_RESULT_INVALID; + } + + uint32_t t = lv_tick_get(); + const void * decoded_img = decode_png_file(fn); + t = lv_tick_elaps(t); + cache->weight = t; + cache->data = decoded_img; + cache->free_data = 1; + if(dsc->src_type == LV_IMAGE_SRC_FILE) { + cache->src = lv_strdup(dsc->src); + cache->src_type = LV_CACHE_SRC_TYPE_STR; + cache->free_src = 1; + } + else { + cache->src_type = LV_CACHE_SRC_TYPE_PTR; + cache->src = dsc->src; + } + + dsc->img_data = lv_cache_get_data(cache); + dsc->user_data = cache; + + lv_cache_unlock(); + return LV_RESULT_OK; /*The image is fully decoded. Return with its pointer*/ + } + + return LV_RESULT_INVALID; /*If not returned earlier then it failed*/ +} + +/** + * Free the allocated resources + */ +static void decoder_close(lv_image_decoder_t * decoder, lv_image_decoder_dsc_t * dsc) +{ + LV_UNUSED(decoder); /*Unused*/ + + lv_cache_lock(); + lv_cache_release(dsc->user_data); + lv_cache_unlock(); +} + +static lv_result_t try_cache(lv_image_decoder_dsc_t * dsc) +{ + lv_cache_lock(); + if(dsc->src_type == LV_IMAGE_SRC_FILE) { + const char * fn = dsc->src; + + 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*/ + lv_cache_unlock(); + return LV_RESULT_OK; + } + } + + lv_cache_unlock(); + return LV_RESULT_INVALID; +} + +static uint8_t * alloc_file(const char * filename, uint32_t * size) +{ + uint8_t * data = NULL; + lv_fs_file_t f; + uint32_t data_size; + uint32_t rn; + lv_fs_res_t res; + + *size = 0; + + res = lv_fs_open(&f, filename, LV_FS_MODE_RD); + if(res != LV_FS_RES_OK) { + LV_LOG_WARN("can't open %s", filename); + return NULL; + } + + res = lv_fs_seek(&f, 0, LV_FS_SEEK_END); + if(res != LV_FS_RES_OK) { + goto failed; + } + + res = lv_fs_tell(&f, &data_size); + if(res != LV_FS_RES_OK) { + goto failed; + } + + res = lv_fs_seek(&f, 0, LV_FS_SEEK_SET); + if(res != LV_FS_RES_OK) { + goto failed; + } + + /*Read file to buffer*/ + data = lv_malloc(data_size); + if(data == NULL) { + LV_LOG_WARN("malloc failed for data"); + goto failed; + } + + res = lv_fs_read(&f, data, data_size, &rn); + + if(res == LV_FS_RES_OK && rn == data_size) { + *size = rn; + } + else { + LV_LOG_WARN("read file failed"); + lv_free(data); + data = NULL; + } + +failed: + lv_fs_close(&f); + + return data; +} + +static const void * decode_png_file(const char * filename) +{ + int ret; + + /*Prepare png_image*/ + png_image image; + lv_memzero(&image, sizeof(image)); + image.version = PNG_IMAGE_VERSION; + + uint32_t data_size; + uint8_t * data = alloc_file(filename, &data_size); + if(data == NULL) { + LV_LOG_WARN("can't load file: %s", filename); + return NULL; + } + + /*Ready to read file*/ + ret = png_image_begin_read_from_memory(&image, data, data_size); + if(!ret) { + LV_LOG_ERROR("png file: %s read failed: %d", filename, ret); + lv_free(data); + return NULL; + } + + /*Set color format*/ + image.format = PNG_FORMAT_BGRA; + + /*Alloc image buffer*/ + size_t image_size = PNG_IMAGE_SIZE(image); + void * image_data = lv_draw_buf_malloc(image_size, LV_COLOR_FORMAT_ARGB8888); + + if(image_data) { + /*Start decoding*/ + ret = png_image_finish_read(&image, NULL, image_data, 0, NULL); + if(!ret) { + LV_LOG_ERROR("png decode failed: %d", ret); + lv_draw_buf_free(image_data); + image_data = NULL; + } + } + else { + LV_LOG_ERROR("png alloc %zu failed", image_size); + } + + /*free decoder*/ + png_image_free(&image); + lv_free(data); + + return image_data; +} + +#endif /*LV_USE_LIBPNG*/ diff --git a/src/libs/libpng/lv_libpng.h b/src/libs/libpng/lv_libpng.h new file mode 100644 index 000000000..12794585d --- /dev/null +++ b/src/libs/libpng/lv_libpng.h @@ -0,0 +1,48 @@ +/** + * @file lv_libpng.h + * + */ + +#ifndef LV_LIBPNG_H +#define LV_LIBPNG_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ +#include "../../lv_conf_internal.h" +#if LV_USE_LIBPNG + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +/** + * Register the PNG decoder functions in LVGL + */ +void lv_libpng_init(void); + +void lv_libpng_deinit(void); + +/********************** + * MACROS + **********************/ + +#endif /*LV_USE_LIBPNG*/ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /*LV_LIBPNG_H*/ diff --git a/src/lv_conf_internal.h b/src/lv_conf_internal.h index ba6efedd6..fd8b478d5 100644 --- a/src/lv_conf_internal.h +++ b/src/lv_conf_internal.h @@ -1943,6 +1943,15 @@ #endif #endif +/*PNG decoder(libpng) library*/ +#ifndef LV_USE_LIBPNG + #ifdef CONFIG_LV_USE_LIBPNG + #define LV_USE_LIBPNG CONFIG_LV_USE_LIBPNG + #else + #define LV_USE_LIBPNG 0 + #endif +#endif + /*BMP decoder library*/ #ifndef LV_USE_BMP #ifdef CONFIG_LV_USE_BMP diff --git a/src/lv_init.c b/src/lv_init.c index 7daa29b3b..1f6de44eb 100644 --- a/src/lv_init.c +++ b/src/lv_init.c @@ -19,6 +19,7 @@ #include "libs/tjpgd/lv_tjpgd.h" #include "libs/libjpeg_turbo/lv_libjpeg_turbo.h" #include "libs/lodepng/lv_lodepng.h" +#include "libs/libpng/lv_libpng.h" #include "draw/lv_draw.h" #include "misc/lv_cache.h" #include "misc/lv_cache_builtin.h" @@ -234,6 +235,10 @@ void lv_init(void) lv_lodepng_init(); #endif +#if LV_USE_LIBPNG + lv_libpng_init(); +#endif + #if LV_USE_TJPGD lv_tjpgd_init(); #endif diff --git a/tests/ref_imgs/libs/png_2.png b/tests/ref_imgs/libs/png_2.png new file mode 100644 index 0000000000000000000000000000000000000000..6ad03d1897c703576581f71a092d6ec7b8236fff GIT binary patch literal 4600 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 H=-0miu{Hel literal 0 HcmV?d00001 diff --git a/tests/src/lv_test_conf_full.h b/tests/src/lv_test_conf_full.h index 29485e8d4..eff31a1a3 100644 --- a/tests/src/lv_test_conf_full.h +++ b/tests/src/lv_test_conf_full.h @@ -62,6 +62,7 @@ #define LV_FS_MEMFS_LETTER 'M' #define LV_USE_LODEPNG 1 +#define LV_USE_LIBPNG 1 #define LV_USE_BMP 1 #define LV_USE_TJPGD 1 #define LV_USE_LIBJPEG_TURBO 1 diff --git a/tests/src/test_cases/libs/test_libpng.c b/tests/src/test_cases/libs/test_libpng.c new file mode 100644 index 000000000..b64fc2a44 --- /dev/null +++ b/tests/src/test_cases/libs/test_libpng.c @@ -0,0 +1,54 @@ +#if LV_BUILD_TEST +#include "../lvgl.h" + +#include "unity/unity.h" +#include "lv_test_helpers.h" + +void setUp(void) +{ + /* Function run before every test */ +} + +void tearDown(void) +{ + /* Function run after every test */ +} + +static void create_images(void) +{ + lv_obj_clean(lv_scr_act()); + + lv_obj_t * img; + + img = lv_img_create(lv_scr_act()); + lv_img_set_src(img, "A:src/test_assets/test_img_lvgl_logo.png"); + lv_obj_center(img); +} + +void test_libpng_1(void) +{ + /* Temporarily remove lodepng decoder */ + lv_lodepng_deinit(); + + create_images(); + + TEST_ASSERT_EQUAL_SCREENSHOT("libs/png_2.png"); + + + uint32_t mem_before = lv_test_get_free_mem(); + for(uint32_t i = 0; i < 20; i++) { + create_images(); + + lv_obj_invalidate(lv_scr_act()); + lv_refr_now(NULL); + } + + TEST_ASSERT_EQUAL_SCREENSHOT("libs/png_2.png"); + + TEST_ASSERT_EQUAL(mem_before, lv_test_get_free_mem()); + + /* Re-add lodepng decoder */ + lv_lodepng_init(); +} + +#endif diff --git a/tests/src/test_cases/libs/test_lodepng.c b/tests/src/test_cases/libs/test_lodepng.c index 4fe88a845..d5c391e56 100644 --- a/tests/src/test_cases/libs/test_lodepng.c +++ b/tests/src/test_cases/libs/test_lodepng.c @@ -41,6 +41,9 @@ static void create_images(void) void test_lodepng_1(void) { + /* Temporarily remove libpng decoder */ + lv_libpng_deinit(); + create_images(); TEST_ASSERT_EQUAL_SCREENSHOT("libs/png_1.png"); @@ -58,6 +61,8 @@ void test_lodepng_1(void) TEST_ASSERT_EQUAL(mem_before, lv_test_get_free_mem()); + /* Re-add libpng decoder */ + lv_libpng_init(); } #endif