refactor(gif): add new API and add full testcase (#9452)

Signed-off-by: liujp <liujp@xiaomi.com>
Co-authored-by: Neo Xu <neo.xu1990@gmail.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
Lemon
2026-01-05 12:31:01 +08:00
committed by GitHub
parent 918586afe3
commit ee51a43fcd
48 changed files with 1736 additions and 53 deletions
+6 -4
View File
@@ -13,11 +13,11 @@
#ifndef __ANIMATEDGIF__
#define __ANIMATEDGIF__
#include "../../../../misc/lv_fs.h"
#include "../../../../lv_conf_internal.h"
#include LV_STDINT_INCLUDE
#include LV_LIMITS_INCLUDE
#include "../../../../stdlib/lv_string.h"
#if LV_USE_GIF
#include "../../../../misc/lv_fs.h"
//
// GIF Animator
@@ -224,4 +224,6 @@ typedef struct gif_image_tag
#define BIGINT int32_t
#define BIGUINT uint32_t
#endif // LV_USE_GIF
#endif // __ANIMATEDGIF__
+23 -4
View File
@@ -20,6 +20,11 @@
//===========================================================================
#include "AnimatedGIF.h"
#if LV_USE_GIF
#include "../../../../misc/lv_log.h"
#include "../../../../stdlib/lv_string.h"
static const unsigned char cGIFBits[9] = {1,4,4,4,8,8,8,8,8}; // convert odd bpp values to ones we can handle
// forward references
@@ -58,14 +63,28 @@ int GIF_openFile(GIFIMAGE *pGIF, const char *szFilename, GIF_DRAW_CALLBACK *pfnD
pGIF->pfnDraw = pfnDraw;
pGIF->pfnOpen = NULL;
pGIF->pfnClose = closeFile;
if (LV_FS_RES_OK != lv_fs_open(&pGIF->GIFFile.fHandle, szFilename, LV_FS_MODE_RD))
return 0;
lv_fs_res_t res = lv_fs_open(&pGIF->GIFFile.fHandle, szFilename, LV_FS_MODE_RD);
if (res != LV_FS_RES_OK) {
pGIF->iError = GIF_FILE_NOT_OPEN;
LV_LOG_WARN("Failed to open file: %s, res: %d", szFilename, res);
return 0;
}
lv_fs_seek(&pGIF->GIFFile.fHandle, 0, LV_FS_SEEK_END);
uint32_t pos;
lv_fs_tell(&pGIF->GIFFile.fHandle, &pos);
pGIF->GIFFile.iSize = pos;
lv_fs_seek(&pGIF->GIFFile.fHandle, 0, LV_FS_SEEK_SET);
return GIFInit(pGIF);
int ret = GIFInit(pGIF);
if (!ret) {
LV_LOG_WARN("Failed to initialize GIF from file: %s, err: %d", szFilename, pGIF->iError);
lv_fs_close(&pGIF->GIFFile.fHandle);
}
return ret;
} /* GIF_openFile() */
void GIF_close(GIFIMAGE *pGIF)
@@ -1501,4 +1520,4 @@ init_codetable:
// return -1;
} /* DecodeLZW() */
#endif // LV_USE_GIF
+227 -42
View File
File diff suppressed because it is too large Load Diff
+31 -3
View File
@@ -15,9 +15,7 @@ extern "C" {
*********************/
#include "../../lv_conf_internal.h"
#include "../../misc/lv_types.h"
#include LV_STDBOOL_INCLUDE
#include LV_STDINT_INCLUDE
#if LV_USE_GIF
#include "../../misc/lv_color.h"
@@ -97,6 +95,36 @@ int32_t lv_gif_get_loop_count(lv_obj_t * obj);
*/
void lv_gif_set_loop_count(lv_obj_t * obj, int32_t count);
/**
* Set whether to decode invisible object.
* @param obj pointer to a gif object
* @param auto_pause true: auto pause when invisible, false: don't auto pause
*/
void lv_gif_set_auto_pause_invisible(lv_obj_t * obj, bool auto_pause);
/**
* Get gif width & height
* @param src pointer to a gif file
* @param w pointer to store width
* @param h pointer to store height
* @return true: success; false: failed
*/
bool lv_gif_get_size(const char * src, uint16_t * w, uint16_t * h);
/**
* Get frame count of the GIF.
* @param obj pointer to a gif object
* @return frame count of the GIF
*/
int32_t lv_gif_get_frame_count(lv_obj_t * obj);
/**
* Get the current frame index of the GIF.
* @param obj pointer to a gif object
* @return current frame index of the GIF
*/
int32_t lv_gif_get_current_frame_index(lv_obj_t * obj);
/**********************
* MACROS
**********************/
+1
View File
@@ -322,6 +322,7 @@ add_library(test_common
src/test_assets/test_font_3_bin.c
src/test_assets/test_img_caret_down.c
src/test_assets/test_arc_bg.c
src/test_assets/test_img_bulb_gif.c
src/test_assets/test_img_lvgl_logo_png.c
src/test_assets/test_img_lvgl_logo_jpg.c
src/test_assets/test_img_emoji_F617.c
Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 141 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 141 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

File diff suppressed because it is too large Load Diff
Binary file not shown.

After

Width:  |  Height:  |  Size: 110 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 110 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 110 KiB

+322
View File
@@ -0,0 +1,322 @@
#if LV_BUILD_TEST
#include "../lvgl.h"
#include "../../lvgl_private.h"
#include "unity/unity.h"
static lv_obj_t * active_screen = NULL;
static bool event_triggered = false;
void setUp(void)
{
active_screen = lv_screen_active();
event_triggered = false;
}
void tearDown(void)
{
lv_obj_clean(active_screen);
}
static lv_obj_t * create_gif_image(const char * src)
{
lv_obj_t * gif = lv_gif_create(active_screen);
lv_gif_set_src(gif, src);
lv_obj_set_align(gif, LV_ALIGN_CENTER);
bool opened = lv_gif_is_loaded(gif);
TEST_ASSERT_TRUE(opened);
return gif;
}
/* Common event handler for all the consecutive test cases. */
static void event_handler(lv_event_t * e)
{
lv_event_code_t code = lv_event_get_code(e);
if(code == LV_EVENT_READY) {
event_triggered = true;
}
}
static void gif_pause_index(int32_t pause_index)
{
lv_obj_t * gif = create_gif_image("A:src/test_assets/totoro_transparent.gif");
int32_t frame_count = lv_gif_get_frame_count(gif);
TEST_ASSERT_LESS_THAN(frame_count, pause_index);
while(lv_gif_get_current_frame_index(gif) != pause_index) {
lv_test_wait(10);
}
lv_gif_pause(gif);
}
void test_gif_decode_normal(void)
{
gif_pause_index(1);
TEST_ASSERT_EQUAL_SCREENSHOT("libs/gif_frame_0.png");
lv_obj_clean(active_screen);
gif_pause_index(7);
TEST_ASSERT_EQUAL_SCREENSHOT("libs/gif_frame_1.png");
lv_obj_clean(active_screen);
gif_pause_index(13);
TEST_ASSERT_EQUAL_SCREENSHOT("libs/gif_frame_2.png");
lv_obj_clean(active_screen);
}
void test_gif_decode_ignore_invisible(void)
{
lv_obj_t * gif = lv_gif_create(active_screen);
lv_obj_set_align(gif, LV_ALIGN_CENTER);
lv_gif_set_auto_pause_invisible(gif, true);
lv_obj_add_flag(gif, LV_OBJ_FLAG_HIDDEN);
lv_gif_set_src(gif, "A:src/test_assets/totoro_transparent.gif");
TEST_ASSERT_EQUAL_SCREENSHOT("libs/gif_frame_invisible.png");
lv_gif_set_auto_pause_invisible(gif, false);
lv_obj_remove_flag(gif, LV_OBJ_FLAG_HIDDEN);
lv_gif_set_src(gif, "A:src/test_assets/totoro_transparent.gif");
TEST_ASSERT_EQUAL_SCREENSHOT("libs/gif_frame_visible.png");
}
void test_gif_get_size(void)
{
uint16_t w = 0;
uint16_t h = 0;
bool res = lv_gif_get_size("A:src/test_assets/totoro_transparent.gif", &w, &h);
TEST_ASSERT_TRUE(res);
TEST_ASSERT_EQUAL(300, w);
TEST_ASSERT_EQUAL(300, h);
res = lv_gif_get_size("A:src/test_assets/test_img_lvgl_logo.png", &w, &h);
TEST_ASSERT_FALSE(res);
TEST_ASSERT_EQUAL(0, w);
TEST_ASSERT_EQUAL(0, h);
}
void test_gif_set_get_loop_count(void)
{
lv_obj_t * gif = create_gif_image("A:src/test_assets/totoro_transparent.gif");
lv_gif_set_loop_count(gif, 2);
TEST_ASSERT_EQUAL(2, lv_gif_get_loop_count(gif));
}
void test_gif_restart(void)
{
lv_obj_t * gif = create_gif_image("A:src/test_assets/totoro_transparent.gif");
lv_gif_restart(gif);
TEST_ASSERT_EQUAL(0, lv_gif_get_current_frame_index(gif));
}
void test_gif_pause_and_resume(void)
{
lv_obj_t * gif = create_gif_image("A:src/test_assets/totoro_transparent.gif");
lv_gif_pause(gif);
lv_test_wait(50);
lv_gif_resume(gif);
while(lv_gif_get_current_frame_index(gif) != 10) {
lv_test_wait(10);
}
TEST_ASSERT_EQUAL(10, lv_gif_get_current_frame_index(gif));
}
void test_gif_blend_to_xrgb8888(void)
{
lv_obj_t * gif = create_gif_image("A:src/test_assets/totoro_transparent.gif");
lv_gif_set_color_format(gif, LV_COLOR_FORMAT_XRGB8888);
TEST_ASSERT_EQUAL_SCREENSHOT("libs/gif_xrgb8888.png");
}
void test_gif_blend_to_argb8888(void)
{
lv_obj_t * gif = create_gif_image("A:src/test_assets/totoro_transparent.gif");
lv_gif_set_color_format(gif, LV_COLOR_FORMAT_ARGB8888);
TEST_ASSERT_EQUAL_SCREENSHOT("libs/gif_argb8888.png");
gif = create_gif_image("A:src/test_assets/totoro_no_transparency.gif");
lv_gif_set_color_format(gif, LV_COLOR_FORMAT_ARGB8888);
TEST_ASSERT_EQUAL_SCREENSHOT("libs/gif_argb8888_no_transparency.png");
gif = create_gif_image("A:src/test_assets/totoro_no_disposalmethod.gif");
lv_gif_set_color_format(gif, LV_COLOR_FORMAT_ARGB8888);
TEST_ASSERT_EQUAL_SCREENSHOT("libs/gif_argb8888_no_disposalmethod.png");
}
void test_gif_blend_to_rgb565(void)
{
lv_obj_t * gif = create_gif_image("A:src/test_assets/totoro_transparent.gif");
lv_gif_set_color_format(gif, LV_COLOR_FORMAT_RGB565);
TEST_ASSERT_EQUAL_SCREENSHOT("libs/gif_rgb565.png");
gif = create_gif_image("A:src/test_assets/totoro_no_transparency.gif");
lv_gif_set_color_format(gif, LV_COLOR_FORMAT_RGB565);
TEST_ASSERT_EQUAL_SCREENSHOT("libs/gif_rgb565_no_transparency.png");
gif = create_gif_image("A:src/test_assets/totoro_no_disposalmethod.gif");
lv_gif_set_color_format(gif, LV_COLOR_FORMAT_RGB565);
TEST_ASSERT_EQUAL_SCREENSHOT("libs/gif_rgb565_no_disposalmethod.png");
}
void test_gif_blend_to_rgb888(void)
{
lv_obj_t * gif = create_gif_image("A:src/test_assets/totoro_transparent.gif");
lv_gif_set_color_format(gif, LV_COLOR_FORMAT_RGB888);
TEST_ASSERT_EQUAL_SCREENSHOT("libs/gif_rgb888.png");
gif = create_gif_image("A:src/test_assets/totoro_no_transparency.gif");
lv_gif_set_color_format(gif, LV_COLOR_FORMAT_RGB888);
TEST_ASSERT_EQUAL_SCREENSHOT("libs/gif_rgb888_no_transparency.png");
gif = create_gif_image("A:src/test_assets/totoro_no_disposalmethod.gif");
lv_gif_set_color_format(gif, LV_COLOR_FORMAT_RGB888);
TEST_ASSERT_EQUAL_SCREENSHOT("libs/gif_rgb888_no_disposalmethod.png");
}
void test_gif_blend_to_i8(void)
{
lv_obj_t * gif = create_gif_image("A:src/test_assets/totoro_transparent.gif");
lv_gif_set_color_format(gif, LV_COLOR_FORMAT_I8);
TEST_ASSERT_EQUAL_SCREENSHOT("libs/gif_i8.png");
}
void test_gif_loop_limit(void)
{
lv_obj_t * gif = lv_gif_create(active_screen);
lv_obj_add_event_cb(gif, event_handler, LV_EVENT_READY, NULL);
lv_obj_set_align(gif, LV_ALIGN_CENTER);
lv_gif_set_src(gif, "A:src/test_assets/totoro_one_frame.gif");
TEST_ASSERT_EQUAL_SCREENSHOT("libs/gif_one_frame.png");
TEST_ASSERT_TRUE(event_triggered);
}
void test_gif_loop_once(void)
{
lv_obj_t * gif = lv_gif_create(active_screen);
lv_obj_set_align(gif, LV_ALIGN_CENTER);
lv_gif_set_src(gif, "A:src/test_assets/totoro_one_frame_loop_once.gif");
TEST_ASSERT_EQUAL_SCREENSHOT("libs/gif_one_frame_loop_once.png");
}
void test_gif_src_type_variable(void)
{
LV_IMAGE_DECLARE(test_img_bulb_gif);
lv_obj_t * gif = lv_gif_create(active_screen);
lv_gif_set_src(gif, &test_img_bulb_gif);
bool opened = lv_gif_is_loaded(gif);
TEST_ASSERT_TRUE(opened);
}
void test_gif_restart_not_loaded(void)
{
lv_obj_t * gif = lv_gif_create(active_screen);
lv_gif_restart(gif);
TEST_ASSERT_EQUAL(-1, lv_gif_get_current_frame_index(gif));
}
void test_gif_resume_not_loaded(void)
{
lv_obj_t * gif = lv_gif_create(active_screen);
lv_gif_resume(gif);
TEST_ASSERT_EQUAL(-1, lv_gif_get_current_frame_index(gif));
}
void test_gif_set_loop_count_not_loaded(void)
{
lv_obj_t * gif = lv_gif_create(active_screen);
lv_gif_set_loop_count(gif, 5);
TEST_ASSERT_EQUAL(-1, lv_gif_get_current_frame_index(gif));
}
void test_gif_get_loop_count_not_loaded(void)
{
lv_obj_t * gif = lv_gif_create(active_screen);
TEST_ASSERT_EQUAL(-1, lv_gif_get_loop_count(gif));
}
void test_gif_get_frame_count_not_loaded(void)
{
lv_obj_t * gif = lv_gif_create(active_screen);
TEST_ASSERT_EQUAL(-1, lv_gif_get_frame_count(gif));
}
void test_gif_set_src_null(void)
{
lv_obj_t * gif = lv_gif_create(active_screen);
lv_obj_set_align(gif, LV_ALIGN_CENTER);
lv_gif_set_src(gif, NULL);
bool opened = lv_gif_is_loaded(gif);
TEST_ASSERT_FALSE(opened);
lv_gif_set_src(gif, "A:src/test_assets/totoro_transparent.gif");
opened = lv_gif_is_loaded(gif);
TEST_ASSERT_TRUE(opened);
lv_gif_set_src(gif, NULL);
opened = lv_gif_is_loaded(gif);
TEST_ASSERT_FALSE(opened);
}
#endif