1
0
mirror of https://github.com/lvgl/lvgl.git synced 2025-01-28 07:03:00 +08:00

feat(libs): add libpng decoder (#4569)

Signed-off-by: pengyiqiang <pengyiqiang@xiaomi.com>
Co-authored-by: pengyiqiang <pengyiqiang@xiaomi.com>
Co-authored-by: Gabor Kiss-Vamosi <kisvegabor@gmail.com>
This commit is contained in:
_VIFEXTech 2023-09-25 16:28:35 +08:00 committed by GitHub
parent 2f67d804ce
commit 9937138392
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 563 additions and 0 deletions

View File

@ -1011,6 +1011,9 @@ menu "LVGL configuration"
config LV_USE_LODEPNG config LV_USE_LODEPNG
bool "PNG decoder library" bool "PNG decoder library"
config LV_USE_LIBPNG
bool "PNG decoder(libpng) library"
config LV_USE_BMP config LV_USE_BMP
bool "BMP decoder library" bool "BMP decoder library"

View File

@ -11,6 +11,7 @@
tjpgd tjpgd
libjpeg_turbo libjpeg_turbo
lodepng lodepng
libpng
gif gif
freetype freetype
tiny_ttf tiny_ttf

42
docs/libs/libpng.rst Normal file
View File

@ -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 <http://www.libpng.org/pub/png/libpng.html>`__.
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`

View File

@ -0,0 +1,6 @@
Open a PNG image from file
--------------------------
.. lv_example:: libs/libpng/lv_example_libpng_1
:language: c

View File

@ -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*/

View File

@ -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

View File

@ -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()

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

View File

@ -19,6 +19,7 @@ extern "C" {
#include "freetype/lv_example_freetype.h" #include "freetype/lv_example_freetype.h"
#include "gif/lv_example_gif.h" #include "gif/lv_example_gif.h"
#include "lodepng/lv_example_lodepng.h" #include "lodepng/lv_example_lodepng.h"
#include "libpng/lv_example_libpng.h"
#include "qrcode/lv_example_qrcode.h" #include "qrcode/lv_example_qrcode.h"
#include "rlottie/lv_example_rlottie.h" #include "rlottie/lv_example_rlottie.h"
#include "tjpgd/lv_example_tjpgd.h" #include "tjpgd/lv_example_tjpgd.h"

View File

@ -579,6 +579,9 @@
/*LODEPNG decoder library*/ /*LODEPNG decoder library*/
#define LV_USE_LODEPNG 0 #define LV_USE_LODEPNG 0
/*PNG decoder(libpng) library*/
#define LV_USE_LIBPNG 0
/*BMP decoder library*/ /*BMP decoder library*/
#define LV_USE_BMP 0 #define LV_USE_BMP 0

1
lvgl.h
View File

@ -93,6 +93,7 @@ extern "C" {
#include "src/libs/bmp/lv_bmp.h" #include "src/libs/bmp/lv_bmp.h"
#include "src/libs/fsdrv/lv_fsdrv.h" #include "src/libs/fsdrv/lv_fsdrv.h"
#include "src/libs/lodepng/lv_lodepng.h" #include "src/libs/lodepng/lv_lodepng.h"
#include "src/libs/libpng/lv_libpng.h"
#include "src/libs/gif/lv_gif.h" #include "src/libs/gif/lv_gif.h"
#include "src/libs/qrcode/lv_qrcode.h" #include "src/libs/qrcode/lv_qrcode.h"
#include "src/libs/tjpgd/lv_tjpgd.h" #include "src/libs/tjpgd/lv_tjpgd.h"

304
src/libs/libpng/lv_libpng.c Normal file
View File

@ -0,0 +1,304 @@
/**
* @file lv_libpng.c
*
*/
/*********************
* INCLUDES
*********************/
#include "../../../lvgl.h"
#if LV_USE_LIBPNG
#include "lv_libpng.h"
#include <png.h>
/*********************
* 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*/

View File

@ -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*/

View File

@ -1943,6 +1943,15 @@
#endif #endif
#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*/ /*BMP decoder library*/
#ifndef LV_USE_BMP #ifndef LV_USE_BMP
#ifdef CONFIG_LV_USE_BMP #ifdef CONFIG_LV_USE_BMP

View File

@ -19,6 +19,7 @@
#include "libs/tjpgd/lv_tjpgd.h" #include "libs/tjpgd/lv_tjpgd.h"
#include "libs/libjpeg_turbo/lv_libjpeg_turbo.h" #include "libs/libjpeg_turbo/lv_libjpeg_turbo.h"
#include "libs/lodepng/lv_lodepng.h" #include "libs/lodepng/lv_lodepng.h"
#include "libs/libpng/lv_libpng.h"
#include "draw/lv_draw.h" #include "draw/lv_draw.h"
#include "misc/lv_cache.h" #include "misc/lv_cache.h"
#include "misc/lv_cache_builtin.h" #include "misc/lv_cache_builtin.h"
@ -234,6 +235,10 @@ void lv_init(void)
lv_lodepng_init(); lv_lodepng_init();
#endif #endif
#if LV_USE_LIBPNG
lv_libpng_init();
#endif
#if LV_USE_TJPGD #if LV_USE_TJPGD
lv_tjpgd_init(); lv_tjpgd_init();
#endif #endif

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

View File

@ -62,6 +62,7 @@
#define LV_FS_MEMFS_LETTER 'M' #define LV_FS_MEMFS_LETTER 'M'
#define LV_USE_LODEPNG 1 #define LV_USE_LODEPNG 1
#define LV_USE_LIBPNG 1
#define LV_USE_BMP 1 #define LV_USE_BMP 1
#define LV_USE_TJPGD 1 #define LV_USE_TJPGD 1
#define LV_USE_LIBJPEG_TURBO 1 #define LV_USE_LIBJPEG_TURBO 1

View File

@ -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

View File

@ -41,6 +41,9 @@ static void create_images(void)
void test_lodepng_1(void) void test_lodepng_1(void)
{ {
/* Temporarily remove libpng decoder */
lv_libpng_deinit();
create_images(); create_images();
TEST_ASSERT_EQUAL_SCREENSHOT("libs/png_1.png"); 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()); TEST_ASSERT_EQUAL(mem_before, lv_test_get_free_mem());
/* Re-add libpng decoder */
lv_libpng_init();
} }
#endif #endif