Upgrade Playlist Features
This commit is contained in:
@@ -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.
|
||||
|
||||
17
main/display/lvgl_display/gif/README.md
Normal file
17
main/display/lvgl_display/gif/README.md
Normal 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
@@ -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 */
|
||||
|
||||
@@ -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*/
|
||||
|
||||
@@ -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_));
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user