Upgrade Playlist Features

This commit is contained in:
2025-12-09 17:20:01 +08:00
parent 577990de69
commit 8bd2780688
683 changed files with 91812 additions and 81260 deletions

View File

@@ -1,2 +1,2 @@
All of the source code and documentation for gifdec is released into the
public domain and provided without warranty of any kind.
All of the source code and documentation for gifdec is released into the
public domain and provided without warranty of any kind.

View File

@@ -0,0 +1,17 @@
# 说明 / Description
## 中文
本目录代码移植自 LVGL 的 GIF 程序。
主要修复和改进:
- 修复了透明背景问题
- 兼容了 87a 版本的 GIF 格式
## English
The code in this directory is ported from LVGL's GIF program.
Main fixes and improvements:
- Fixed transparent background issues
- Added compatibility for GIF 87a version format

File diff suppressed because it is too large Load Diff

View File

@@ -1,68 +1,68 @@
#ifndef GIFDEC_H
#define GIFDEC_H
#ifdef __cplusplus
extern "C" {
#endif
#include <lvgl.h>
#include <stdint.h>
typedef struct _gd_Palette {
int size;
uint8_t colors[0x100 * 3];
} gd_Palette;
typedef struct _gd_GCE {
uint16_t delay;
uint8_t tindex;
uint8_t disposal;
int input;
int transparency;
} gd_GCE;
typedef struct _gd_GIF {
lv_fs_file_t fd;
const char * data;
uint8_t is_file;
uint32_t f_rw_p;
int32_t anim_start;
uint16_t width, height;
uint16_t depth;
int32_t loop_count;
gd_GCE gce;
gd_Palette * palette;
gd_Palette lct, gct;
void (*plain_text)(
struct _gd_GIF * gif, uint16_t tx, uint16_t ty,
uint16_t tw, uint16_t th, uint8_t cw, uint8_t ch,
uint8_t fg, uint8_t bg
);
void (*comment)(struct _gd_GIF * gif);
void (*application)(struct _gd_GIF * gif, char id[8], char auth[3]);
uint16_t fx, fy, fw, fh;
uint8_t bgindex;
uint8_t * canvas, * frame;
#if LV_GIF_CACHE_DECODE_DATA
uint8_t *lzw_cache;
#endif
} gd_GIF;
gd_GIF * gd_open_gif_file(const char * fname);
gd_GIF * gd_open_gif_data(const void * data);
void gd_render_frame(gd_GIF * gif, uint8_t * buffer);
int gd_get_frame(gd_GIF * gif);
void gd_rewind(gd_GIF * gif);
void gd_close_gif(gd_GIF * gif);
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /* GIFDEC_H */
#ifndef GIFDEC_H
#define GIFDEC_H
#ifdef __cplusplus
extern "C" {
#endif
#include <lvgl.h>
#include <stdint.h>
typedef struct _gd_Palette {
int size;
uint8_t colors[0x100 * 3];
} gd_Palette;
typedef struct _gd_GCE {
uint16_t delay;
uint8_t tindex;
uint8_t disposal;
int input;
int transparency;
} gd_GCE;
typedef struct _gd_GIF {
lv_fs_file_t fd;
const char * data;
uint8_t is_file;
uint32_t f_rw_p;
int32_t anim_start;
uint16_t width, height;
uint16_t depth;
int32_t loop_count;
gd_GCE gce;
gd_Palette * palette;
gd_Palette lct, gct;
void (*plain_text)(
struct _gd_GIF * gif, uint16_t tx, uint16_t ty,
uint16_t tw, uint16_t th, uint8_t cw, uint8_t ch,
uint8_t fg, uint8_t bg
);
void (*comment)(struct _gd_GIF * gif);
void (*application)(struct _gd_GIF * gif, char id[8], char auth[3]);
uint16_t fx, fy, fw, fh;
uint8_t bgindex;
uint8_t * canvas, * frame;
#if LV_GIF_CACHE_DECODE_DATA
uint8_t *lzw_cache;
#endif
} gd_GIF;
gd_GIF * gd_open_gif_file(const char * fname);
gd_GIF * gd_open_gif_data(const void * data);
void gd_render_frame(gd_GIF * gif, uint8_t * buffer);
int gd_get_frame(gd_GIF * gif);
void gd_rewind(gd_GIF * gif);
void gd_close_gif(gd_GIF * gif);
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /* GIFDEC_H */

View File

@@ -1,140 +1,140 @@
/**
* @file gifdec_mve.h
*
*/
#ifndef GIFDEC_MVE_H
#define GIFDEC_MVE_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include <stdint.h>
#include "../../misc/lv_color.h"
/*********************
* DEFINES
*********************/
#define GIFDEC_FILL_BG(dst, w, h, stride, color, opa) \
_gifdec_fill_bg_mve(dst, w, h, stride, color, opa)
#define GIFDEC_RENDER_FRAME(dst, w, h, stride, frame, pattern, tindex) \
_gifdec_render_frame_mve(dst, w, h, stride, frame, pattern, tindex)
/**********************
* MACROS
**********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* GLOBAL PROTOTYPES
**********************/
static inline void _gifdec_fill_bg_mve(uint8_t * dst, uint16_t w, uint16_t h, uint16_t stride, uint8_t * color,
uint8_t opa)
{
lv_color32_t c = lv_color32_make(*(color + 0), *(color + 1), *(color + 2), opa);
uint32_t color_32 = *(uint32_t *)&c;
__asm volatile(
".p2align 2 \n"
"vdup.32 q0, %[src] \n"
"3: \n"
"mov r0, %[dst] \n"
"wlstp.32 lr, %[w], 1f \n"
"2: \n"
"vstrw.32 q0, [r0], #16 \n"
"letp lr, 2b \n"
"1: \n"
"add %[dst], %[iTargetStride] \n"
"subs %[h], #1 \n"
"bne 3b \n"
: [dst] "+r"(dst),
[h] "+r"(h)
: [src] "r"(color_32),
[w] "r"(w),
[iTargetStride] "r"(stride * sizeof(uint32_t))
: "r0", "q0", "memory", "r14", "cc");
}
static inline void _gifdec_render_frame_mve(uint8_t * dst, uint16_t w, uint16_t h, uint16_t stride, uint8_t * frame,
uint8_t * pattern, uint16_t tindex)
{
if(w == 0 || h == 0) {
return;
}
__asm volatile(
"vmov.u16 q3, #255 \n"
"vshl.u16 q3, q3, #8 \n" /* left shift 8 for a*/
"mov r0, #2 \n"
"vidup.u16 q6, r0, #4 \n" /* [2, 6, 10, 14, 18, 22, 26, 30] */
"mov r0, #0 \n"
"vidup.u16 q7, r0, #4 \n" /* [0, 4, 8, 12, 16, 20, 24, 28] */
"3: \n"
"mov r1, %[dst] \n"
"mov r2, %[frame] \n"
"wlstp.16 lr, %[w], 1f \n"
"2: \n"
"mov r0, #3 \n"
"vldrb.u16 q4, [r2], #8 \n"
"vmul.u16 q5, q4, r0 \n"
"mov r0, #1 \n"
"vldrb.u16 q2, [%[pattern], q5] \n" /* load 8 pixel r*/
"vadd.u16 q5, q5, r0 \n"
"vldrb.u16 q1, [%[pattern], q5] \n" /* load 8 pixel g*/
"vadd.u16 q5, q5, r0 \n"
"vldrb.u16 q0, [%[pattern], q5] \n" /* load 8 pixel b*/
"vshl.u16 q1, q1, #8 \n" /* left shift 8 for g*/
"vorr.u16 q0, q0, q1 \n" /* make 8 pixel gb*/
"vorr.u16 q1, q2, q3 \n" /* make 8 pixel ar*/
"vcmp.i16 ne, q4, %[tindex] \n"
"vpstt \n"
"vstrht.16 q0, [r1, q7] \n"
"vstrht.16 q1, [r1, q6] \n"
"add r1, r1, #32 \n"
"letp lr, 2b \n"
"1: \n"
"mov r0, %[stride], LSL #2 \n"
"add %[dst], r0 \n"
"add %[frame], %[stride] \n"
"subs %[h], #1 \n"
"bne 3b \n"
: [dst] "+r"(dst),
[frame] "+r"(frame),
[h] "+r"(h)
: [pattern] "r"(pattern),
[w] "r"(w),
[stride] "r"(stride),
[tindex] "r"(tindex)
: "r0", "r1", "r2", "q0", "q1", "q2", "q3", "q4", "q5", "q6", "q7", "memory", "r14", "cc");
}
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*GIFDEC_MVE_H*/
/**
* @file gifdec_mve.h
*
*/
#ifndef GIFDEC_MVE_H
#define GIFDEC_MVE_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include <stdint.h>
#include "../../misc/lv_color.h"
/*********************
* DEFINES
*********************/
#define GIFDEC_FILL_BG(dst, w, h, stride, color, opa) \
_gifdec_fill_bg_mve(dst, w, h, stride, color, opa)
#define GIFDEC_RENDER_FRAME(dst, w, h, stride, frame, pattern, tindex) \
_gifdec_render_frame_mve(dst, w, h, stride, frame, pattern, tindex)
/**********************
* MACROS
**********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* GLOBAL PROTOTYPES
**********************/
static inline void _gifdec_fill_bg_mve(uint8_t * dst, uint16_t w, uint16_t h, uint16_t stride, uint8_t * color,
uint8_t opa)
{
lv_color32_t c = lv_color32_make(*(color + 0), *(color + 1), *(color + 2), opa);
uint32_t color_32 = *(uint32_t *)&c;
__asm volatile(
".p2align 2 \n"
"vdup.32 q0, %[src] \n"
"3: \n"
"mov r0, %[dst] \n"
"wlstp.32 lr, %[w], 1f \n"
"2: \n"
"vstrw.32 q0, [r0], #16 \n"
"letp lr, 2b \n"
"1: \n"
"add %[dst], %[iTargetStride] \n"
"subs %[h], #1 \n"
"bne 3b \n"
: [dst] "+r"(dst),
[h] "+r"(h)
: [src] "r"(color_32),
[w] "r"(w),
[iTargetStride] "r"(stride * sizeof(uint32_t))
: "r0", "q0", "memory", "r14", "cc");
}
static inline void _gifdec_render_frame_mve(uint8_t * dst, uint16_t w, uint16_t h, uint16_t stride, uint8_t * frame,
uint8_t * pattern, uint16_t tindex)
{
if(w == 0 || h == 0) {
return;
}
__asm volatile(
"vmov.u16 q3, #255 \n"
"vshl.u16 q3, q3, #8 \n" /* left shift 8 for a*/
"mov r0, #2 \n"
"vidup.u16 q6, r0, #4 \n" /* [2, 6, 10, 14, 18, 22, 26, 30] */
"mov r0, #0 \n"
"vidup.u16 q7, r0, #4 \n" /* [0, 4, 8, 12, 16, 20, 24, 28] */
"3: \n"
"mov r1, %[dst] \n"
"mov r2, %[frame] \n"
"wlstp.16 lr, %[w], 1f \n"
"2: \n"
"mov r0, #3 \n"
"vldrb.u16 q4, [r2], #8 \n"
"vmul.u16 q5, q4, r0 \n"
"mov r0, #1 \n"
"vldrb.u16 q2, [%[pattern], q5] \n" /* load 8 pixel r*/
"vadd.u16 q5, q5, r0 \n"
"vldrb.u16 q1, [%[pattern], q5] \n" /* load 8 pixel g*/
"vadd.u16 q5, q5, r0 \n"
"vldrb.u16 q0, [%[pattern], q5] \n" /* load 8 pixel b*/
"vshl.u16 q1, q1, #8 \n" /* left shift 8 for g*/
"vorr.u16 q0, q0, q1 \n" /* make 8 pixel gb*/
"vorr.u16 q1, q2, q3 \n" /* make 8 pixel ar*/
"vcmp.i16 ne, q4, %[tindex] \n"
"vpstt \n"
"vstrht.16 q0, [r1, q7] \n"
"vstrht.16 q1, [r1, q6] \n"
"add r1, r1, #32 \n"
"letp lr, 2b \n"
"1: \n"
"mov r0, %[stride], LSL #2 \n"
"add %[dst], r0 \n"
"add %[frame], %[stride] \n"
"subs %[h], #1 \n"
"bne 3b \n"
: [dst] "+r"(dst),
[frame] "+r"(frame),
[h] "+r"(h)
: [pattern] "r"(pattern),
[w] "r"(w),
[stride] "r"(stride),
[tindex] "r"(tindex)
: "r0", "r1", "r2", "q0", "q1", "q2", "q3", "q4", "q5", "q6", "q7", "memory", "r14", "cc");
}
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*GIFDEC_MVE_H*/

View File

@@ -1,208 +1,208 @@
#include "lvgl_gif.h"
#include <esp_log.h>
#include <cstring>
#define TAG "LvglGif"
LvglGif::LvglGif(const lv_img_dsc_t* img_dsc)
: gif_(nullptr), timer_(nullptr), last_call_(0), playing_(false), loaded_(false) {
if (!img_dsc || !img_dsc->data) {
ESP_LOGE(TAG, "Invalid image descriptor");
return;
}
gif_ = gd_open_gif_data(img_dsc->data);
if (!gif_) {
ESP_LOGE(TAG, "Failed to open GIF from image descriptor");
return;
}
// Setup LVGL image descriptor
memset(&img_dsc_, 0, sizeof(img_dsc_));
img_dsc_.header.magic = LV_IMAGE_HEADER_MAGIC;
img_dsc_.header.flags = LV_IMAGE_FLAGS_MODIFIABLE;
img_dsc_.header.cf = LV_COLOR_FORMAT_ARGB8888;
img_dsc_.header.w = gif_->width;
img_dsc_.header.h = gif_->height;
img_dsc_.header.stride = gif_->width * 4;
img_dsc_.data = gif_->canvas;
img_dsc_.data_size = gif_->width * gif_->height * 4;
// Render first frame
if (gif_->canvas) {
gd_render_frame(gif_, gif_->canvas);
}
loaded_ = true;
ESP_LOGD(TAG, "GIF loaded from image descriptor: %dx%d", gif_->width, gif_->height);
}
// Destructor
LvglGif::~LvglGif() {
Cleanup();
}
// LvglImage interface implementation
const lv_img_dsc_t* LvglGif::image_dsc() const {
if (!loaded_) {
return nullptr;
}
return &img_dsc_;
}
// Animation control methods
void LvglGif::Start() {
if (!loaded_ || !gif_) {
ESP_LOGW(TAG, "GIF not loaded, cannot start");
return;
}
if (!timer_) {
timer_ = lv_timer_create([](lv_timer_t* timer) {
LvglGif* gif_obj = static_cast<LvglGif*>(lv_timer_get_user_data(timer));
gif_obj->NextFrame();
}, 10, this);
}
if (timer_) {
playing_ = true;
last_call_ = lv_tick_get();
lv_timer_resume(timer_);
lv_timer_reset(timer_);
// Render first frame
NextFrame();
ESP_LOGD(TAG, "GIF animation started");
}
}
void LvglGif::Pause() {
if (timer_) {
playing_ = false;
lv_timer_pause(timer_);
ESP_LOGD(TAG, "GIF animation paused");
}
}
void LvglGif::Resume() {
if (!loaded_ || !gif_) {
ESP_LOGW(TAG, "GIF not loaded, cannot resume");
return;
}
if (timer_) {
playing_ = true;
lv_timer_resume(timer_);
ESP_LOGD(TAG, "GIF animation resumed");
}
}
void LvglGif::Stop() {
if (timer_) {
playing_ = false;
lv_timer_pause(timer_);
}
if (gif_) {
gd_rewind(gif_);
NextFrame();
ESP_LOGD(TAG, "GIF animation stopped and rewound");
}
}
bool LvglGif::IsPlaying() const {
return playing_;
}
bool LvglGif::IsLoaded() const {
return loaded_;
}
int32_t LvglGif::GetLoopCount() const {
if (!loaded_ || !gif_) {
return -1;
}
return gif_->loop_count;
}
void LvglGif::SetLoopCount(int32_t count) {
if (!loaded_ || !gif_) {
ESP_LOGW(TAG, "GIF not loaded, cannot set loop count");
return;
}
gif_->loop_count = count;
}
uint16_t LvglGif::width() const {
if (!loaded_ || !gif_) {
return 0;
}
return gif_->width;
}
uint16_t LvglGif::height() const {
if (!loaded_ || !gif_) {
return 0;
}
return gif_->height;
}
void LvglGif::SetFrameCallback(std::function<void()> callback) {
frame_callback_ = callback;
}
void LvglGif::NextFrame() {
if (!loaded_ || !gif_ || !playing_) {
return;
}
// Check if enough time has passed for the next frame
uint32_t elapsed = lv_tick_elaps(last_call_);
if (elapsed < gif_->gce.delay * 10) {
return;
}
last_call_ = lv_tick_get();
// Get next frame
int has_next = gd_get_frame(gif_);
if (has_next == 0) {
// Animation finished, pause timer
playing_ = false;
if (timer_) {
lv_timer_pause(timer_);
}
ESP_LOGD(TAG, "GIF animation completed");
}
// Render current frame
if (gif_->canvas) {
gd_render_frame(gif_, gif_->canvas);
// Call frame callback if set
if (frame_callback_) {
frame_callback_();
}
}
}
void LvglGif::Cleanup() {
// Stop and delete timer
if (timer_) {
lv_timer_delete(timer_);
timer_ = nullptr;
}
// Close GIF decoder
if (gif_) {
gd_close_gif(gif_);
gif_ = nullptr;
}
playing_ = false;
loaded_ = false;
// Clear image descriptor
memset(&img_dsc_, 0, sizeof(img_dsc_));
}
#include "lvgl_gif.h"
#include <esp_log.h>
#include <cstring>
#define TAG "LvglGif"
LvglGif::LvglGif(const lv_img_dsc_t* img_dsc)
: gif_(nullptr), timer_(nullptr), last_call_(0), playing_(false), loaded_(false) {
if (!img_dsc || !img_dsc->data) {
ESP_LOGE(TAG, "Invalid image descriptor");
return;
}
gif_ = gd_open_gif_data(img_dsc->data);
if (!gif_) {
ESP_LOGE(TAG, "Failed to open GIF from image descriptor");
return;
}
// Setup LVGL image descriptor
memset(&img_dsc_, 0, sizeof(img_dsc_));
img_dsc_.header.magic = LV_IMAGE_HEADER_MAGIC;
img_dsc_.header.flags = LV_IMAGE_FLAGS_MODIFIABLE;
img_dsc_.header.cf = LV_COLOR_FORMAT_ARGB8888;
img_dsc_.header.w = gif_->width;
img_dsc_.header.h = gif_->height;
img_dsc_.header.stride = gif_->width * 4;
img_dsc_.data = gif_->canvas;
img_dsc_.data_size = gif_->width * gif_->height * 4;
// Render first frame
if (gif_->canvas) {
gd_render_frame(gif_, gif_->canvas);
}
loaded_ = true;
ESP_LOGD(TAG, "GIF loaded from image descriptor: %dx%d", gif_->width, gif_->height);
}
// Destructor
LvglGif::~LvglGif() {
Cleanup();
}
// LvglImage interface implementation
const lv_img_dsc_t* LvglGif::image_dsc() const {
if (!loaded_) {
return nullptr;
}
return &img_dsc_;
}
// Animation control methods
void LvglGif::Start() {
if (!loaded_ || !gif_) {
ESP_LOGW(TAG, "GIF not loaded, cannot start");
return;
}
if (!timer_) {
timer_ = lv_timer_create([](lv_timer_t* timer) {
LvglGif* gif_obj = static_cast<LvglGif*>(lv_timer_get_user_data(timer));
gif_obj->NextFrame();
}, 10, this);
}
if (timer_) {
playing_ = true;
last_call_ = lv_tick_get();
lv_timer_resume(timer_);
lv_timer_reset(timer_);
// Render first frame
NextFrame();
ESP_LOGD(TAG, "GIF animation started");
}
}
void LvglGif::Pause() {
if (timer_) {
playing_ = false;
lv_timer_pause(timer_);
ESP_LOGD(TAG, "GIF animation paused");
}
}
void LvglGif::Resume() {
if (!loaded_ || !gif_) {
ESP_LOGW(TAG, "GIF not loaded, cannot resume");
return;
}
if (timer_) {
playing_ = true;
lv_timer_resume(timer_);
ESP_LOGD(TAG, "GIF animation resumed");
}
}
void LvglGif::Stop() {
if (timer_) {
playing_ = false;
lv_timer_pause(timer_);
}
if (gif_) {
gd_rewind(gif_);
NextFrame();
ESP_LOGD(TAG, "GIF animation stopped and rewound");
}
}
bool LvglGif::IsPlaying() const {
return playing_;
}
bool LvglGif::IsLoaded() const {
return loaded_;
}
int32_t LvglGif::GetLoopCount() const {
if (!loaded_ || !gif_) {
return -1;
}
return gif_->loop_count;
}
void LvglGif::SetLoopCount(int32_t count) {
if (!loaded_ || !gif_) {
ESP_LOGW(TAG, "GIF not loaded, cannot set loop count");
return;
}
gif_->loop_count = count;
}
uint16_t LvglGif::width() const {
if (!loaded_ || !gif_) {
return 0;
}
return gif_->width;
}
uint16_t LvglGif::height() const {
if (!loaded_ || !gif_) {
return 0;
}
return gif_->height;
}
void LvglGif::SetFrameCallback(std::function<void()> callback) {
frame_callback_ = callback;
}
void LvglGif::NextFrame() {
if (!loaded_ || !gif_ || !playing_) {
return;
}
// Check if enough time has passed for the next frame
uint32_t elapsed = lv_tick_elaps(last_call_);
if (elapsed < gif_->gce.delay * 10) {
return;
}
last_call_ = lv_tick_get();
// Get next frame
int has_next = gd_get_frame(gif_);
if (has_next == 0) {
// Animation finished, pause timer
playing_ = false;
if (timer_) {
lv_timer_pause(timer_);
}
ESP_LOGD(TAG, "GIF animation completed");
}
// Render current frame
if (gif_->canvas) {
gd_render_frame(gif_, gif_->canvas);
// Call frame callback if set
if (frame_callback_) {
frame_callback_();
}
}
}
void LvglGif::Cleanup() {
// Stop and delete timer
if (timer_) {
lv_timer_delete(timer_);
timer_ = nullptr;
}
// Close GIF decoder
if (gif_) {
gd_close_gif(gif_);
gif_ = nullptr;
}
playing_ = false;
loaded_ = false;
// Clear image descriptor
memset(&img_dsc_, 0, sizeof(img_dsc_));
}

View File

@@ -1,101 +1,101 @@
#pragma once
#include "../lvgl_image.h"
#include "gifdec.h"
#include <lvgl.h>
#include <memory>
#include <functional>
/**
* C++ implementation of LVGL GIF widget
* Provides GIF animation functionality using gifdec library
*/
class LvglGif {
public:
explicit LvglGif(const lv_img_dsc_t* img_dsc);
virtual ~LvglGif();
// LvglImage interface implementation
virtual const lv_img_dsc_t* image_dsc() const;
/**
* Start/restart GIF animation
*/
void Start();
/**
* Pause GIF animation
*/
void Pause();
/**
* Resume GIF animation
*/
void Resume();
/**
* Stop GIF animation and rewind to first frame
*/
void Stop();
/**
* Check if GIF is currently playing
*/
bool IsPlaying() const;
/**
* Check if GIF was loaded successfully
*/
bool IsLoaded() const;
/**
* Get loop count
*/
int32_t GetLoopCount() const;
/**
* Set loop count
*/
void SetLoopCount(int32_t count);
/**
* Get GIF dimensions
*/
uint16_t width() const;
uint16_t height() const;
/**
* Set frame update callback
*/
void SetFrameCallback(std::function<void()> callback);
private:
// GIF decoder instance
gd_GIF* gif_;
// LVGL image descriptor
lv_img_dsc_t img_dsc_;
// Animation timer
lv_timer_t* timer_;
// Last frame update time
uint32_t last_call_;
// Animation state
bool playing_;
bool loaded_;
// Frame update callback
std::function<void()> frame_callback_;
/**
* Update to next frame
*/
void NextFrame();
/**
* Cleanup resources
*/
void Cleanup();
};
#pragma once
#include "../lvgl_image.h"
#include "gifdec.h"
#include <lvgl.h>
#include <memory>
#include <functional>
/**
* C++ implementation of LVGL GIF widget
* Provides GIF animation functionality using gifdec library
*/
class LvglGif {
public:
explicit LvglGif(const lv_img_dsc_t* img_dsc);
virtual ~LvglGif();
// LvglImage interface implementation
virtual const lv_img_dsc_t* image_dsc() const;
/**
* Start/restart GIF animation
*/
void Start();
/**
* Pause GIF animation
*/
void Pause();
/**
* Resume GIF animation
*/
void Resume();
/**
* Stop GIF animation and rewind to first frame
*/
void Stop();
/**
* Check if GIF is currently playing
*/
bool IsPlaying() const;
/**
* Check if GIF was loaded successfully
*/
bool IsLoaded() const;
/**
* Get loop count
*/
int32_t GetLoopCount() const;
/**
* Set loop count
*/
void SetLoopCount(int32_t count);
/**
* Get GIF dimensions
*/
uint16_t width() const;
uint16_t height() const;
/**
* Set frame update callback
*/
void SetFrameCallback(std::function<void()> callback);
private:
// GIF decoder instance
gd_GIF* gif_;
// LVGL image descriptor
lv_img_dsc_t img_dsc_;
// Animation timer
lv_timer_t* timer_;
// Last frame update time
uint32_t last_call_;
// Animation state
bool playing_;
bool loaded_;
// Frame update callback
std::function<void()> frame_callback_;
/**
* Update to next frame
*/
void NextFrame();
/**
* Cleanup resources
*/
void Cleanup();
};