Dzbj_ESP32-S3/main/ui/screens/ui_ScreenImg.c
Rdzleo 9223fd5a7d feat: BLE传输优化 + GIF条件编译 + 自定义GIF播放器
一、BLE 蓝牙优化
- 设备名称改为动态名称 Airhub_MAC(基于BLE MAC地址)
- 广播数据拆分为 ADV + Scan Response 两包
- 图片接收完成后数据直通显示(跳过SPIFFS重读,减少200-500ms延迟)
- BLE耗时操作(NVS写入+导航显示)转移到独立FreeRTOS任务,避免BTC_TASK栈溢出
- 缩短BLE连接间隔(min=7.5ms, max=20ms),提升传输吞吐量
- 减少传输日志输出(每100包打印一次),提升传输速度

二、显示性能优化
- LVGL绘制缓冲区从DMA 30行改为PSRAM 120行大缓冲,减少flush次数
- CPU最大频率从160MHz提升到240MHz,提升解码性能

三、GIF动图支持(条件编译,当前默认关闭)
- 实现自定义GIF播放器:Palette LUT查表 + TRUE_COLOR无Alpha + 后台线程解码流水线
- 使用 #if LV_USE_GIF 条件编译包裹所有GIF代码,sdkconfig中CONFIG_LV_USE_GIF=n时零开销
- 启用GIF时需设置 CONFIG_LV_USE_GIF=y 即可

四、图片管理优化
- BLE接收新图片后直接追加到列表(避免重扫SPIFFS目录)
- SPIFFS图片扫描支持.gif扩展名(条件编译控制)

五、文档更新
- 设备运行日志:GIF性能瓶颈分析与优化方案

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-27 09:43:02 +08:00

222 lines
8.2 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// This file was generated by SquareLine Studio
// SquareLine Studio version: SquareLine Studio 1.6.0
// LVGL version: 8.3.11
// Project name: Lcd_Pro
#include "../ui.h"
#include "ui_ScreenSet.h" // 引入ScreenSet的函数声明
#include "../../pages/include/pages.h" // 引入图片管理函数
#include "esp_log.h" // 用于日志输出
extern void init_spiffs_image_list(void);
extern void update_ui_ImgBle(const char *img_name);
extern void free_spiffs_image_list(void);
extern const char* get_next_image(void);
extern const char* get_prev_image(void);
lv_obj_t *ui_ScreenImg = NULL;
lv_obj_t *ui_ImgBle = NULL;
lv_obj_t *ui_ContainerDle = NULL;
lv_obj_t *ui_ImageDel = NULL;
lv_obj_t *ui_ImageReturn = NULL;
// 标志:是否首次加载
static bool first_load = true;
// 标志:是否需要显示 ContainerDle只有从 ScreenSet 点击 ImgDelete 时才为 true
static bool should_show_container = false;
// 显示 ContainerDle
void ui_ScreenImg_show_delete_container(void) {
should_show_container = true; // 设置标志,表示需要显示
if (ui_ContainerDle) {
lv_obj_clear_flag(ui_ContainerDle, LV_OBJ_FLAG_HIDDEN);
}
}
// 隐藏 ContainerDle
void ui_ScreenImg_hide_delete_container(void) {
should_show_container = false; // 清除标志
if (ui_ContainerDle) {
lv_obj_add_flag(ui_ContainerDle, LV_OBJ_FLAG_HIDDEN);
}
}
// ImageReturn 点击事件:隐藏容器
void ui_event_ImageReturn(lv_event_t * e) {
lv_event_code_t event_code = lv_event_get_code(e);
if (event_code == LV_EVENT_CLICKED) {
ui_ScreenImg_hide_delete_container();
}
}
// ImageDel 点击事件:删除当前图片
void ui_event_ImageDel(lv_event_t * e) {
lv_event_code_t event_code = lv_event_get_code(e);
if (event_code == LV_EVENT_CLICKED) {
// 删除当前图片
if (delete_current_image()) {
// 删除成功,隐藏容器
ui_ScreenImg_hide_delete_container();
// 获取下一张图片(内部已更新索引)
const char *next_img = get_current_image();
if (next_img) {
// 显示下一张图片
update_ui_ImgBle(next_img);
} else {
// 没有图片了,可以显示提示信息
// 这里暂不处理,保持当前界面
}
} else {
// 删除失败,仍然隐藏容器
ui_ScreenImg_hide_delete_container();
}
}
}
// event funtions
void ui_event_ScreenImg( lv_event_t * e) {
lv_event_code_t event_code = lv_event_get_code(e);
// 界面加载完成事件:每次进入都显示当前图片
if ( event_code == LV_EVENT_SCREEN_LOADED ) {
// 初始化图片列表内部有guard不会重复初始化
init_spiffs_image_list();
// 每次进入界面都显示当前图片支持BLE导航和手势切换回来
const char *current_img = get_current_image();
if (current_img) {
update_ui_ImgBle(current_img);
} else {
ESP_LOGI("ScreenImg", "SPIFFS无可用图片显示默认UI图片");
}
// 每次加载界面时检查是否需要显示 ContainerDle
// 只有从 ScreenSet 点击 ImgDelete 时 should_show_container 才为 true
if (should_show_container) {
// 需要显示,保持显示状态(已在 ui_ScreenImg_show_delete_container 中显示)
should_show_container = false; // 清除标志(下次进入默认不显示)
} else {
// 不需要显示,确保隐藏
ui_ScreenImg_hide_delete_container();
}
}
if ( event_code == LV_EVENT_GESTURE && lv_indev_get_gesture_dir(lv_indev_get_act()) == LV_DIR_TOP ) {
lv_indev_wait_release(lv_indev_get_act());
// 离开界面前清理资源和隐藏容器
#if LV_USE_GIF
pages_cleanup_gif();
#endif
ui_ScreenImg_hide_delete_container();
_ui_screen_change( &ui_ScreenHome, LV_SCR_LOAD_ANIM_NONE, 0, 0, &ui_ScreenHome_screen_init);
}
if ( event_code == LV_EVENT_GESTURE && lv_indev_get_gesture_dir(lv_indev_get_act()) == LV_DIR_BOTTOM ) {
lv_indev_wait_release(lv_indev_get_act());
// 离开界面前清理资源和隐藏容器
#if LV_USE_GIF
pages_cleanup_gif();
#endif
ui_ScreenImg_hide_delete_container();
// 设置返回到Img界面
ui_ScreenSet_set_previous(&ui_ScreenImg, &ui_ScreenImg_screen_init);
_ui_screen_change( &ui_ScreenSet, LV_SCR_LOAD_ANIM_NONE, 0, 0, &ui_ScreenSet_screen_init);
}
if ( event_code == LV_EVENT_GESTURE && lv_indev_get_gesture_dir(lv_indev_get_act()) == LV_DIR_LEFT ) {
lv_indev_wait_release(lv_indev_get_act());
const char *next_img = get_next_image();
if(next_img) {
update_ui_ImgBle(next_img);
}
}
if ( event_code == LV_EVENT_GESTURE && lv_indev_get_gesture_dir(lv_indev_get_act()) == LV_DIR_RIGHT ) {
lv_indev_wait_release(lv_indev_get_act());
const char *prev_img = get_prev_image();
if(prev_img) {
update_ui_ImgBle(prev_img);
}
}
}
// build funtions
void ui_ScreenImg_screen_init(void)
{
ui_ScreenImg = lv_obj_create(NULL);
lv_obj_clear_flag( ui_ScreenImg, LV_OBJ_FLAG_SCROLLABLE ); /// Flags
ui_ImgBle = lv_img_create(ui_ScreenImg);
lv_img_set_src(ui_ImgBle, &ui_img_s1_png);
lv_obj_set_width( ui_ImgBle, 360);
lv_obj_set_height( ui_ImgBle, 360);
lv_obj_set_align( ui_ImgBle, LV_ALIGN_CENTER );
lv_obj_set_flex_flow(ui_ImgBle,LV_FLEX_FLOW_ROW);
lv_obj_set_flex_align(ui_ImgBle, LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_START);
lv_obj_add_flag( ui_ImgBle, LV_OBJ_FLAG_ADV_HITTEST ); /// Flags
lv_obj_clear_flag( ui_ImgBle, LV_OBJ_FLAG_SCROLLABLE ); /// Flags
ui_ContainerDle = lv_obj_create(ui_ScreenImg);
lv_obj_remove_style_all(ui_ContainerDle);
lv_obj_set_width( ui_ContainerDle, 360);
lv_obj_set_height( ui_ContainerDle, 360); // 圆形容器
lv_obj_set_x( ui_ContainerDle, 0 );
lv_obj_set_y( ui_ContainerDle, 240 ); // 向下偏移 240px显示顶部 1/3
lv_obj_set_align( ui_ContainerDle, LV_ALIGN_CENTER );
lv_obj_clear_flag( ui_ContainerDle, LV_OBJ_FLAG_CLICKABLE | LV_OBJ_FLAG_SCROLLABLE );
// 设置圆形和裁剪
lv_obj_set_style_radius(ui_ContainerDle, 180, LV_PART_MAIN); // 圆角半径 180
lv_obj_set_style_clip_corner(ui_ContainerDle, true, LV_PART_MAIN); // 裁剪圆角外内容
lv_obj_set_style_bg_color(ui_ContainerDle, lv_color_hex(0x000000), LV_PART_MAIN);
lv_obj_set_style_bg_opa(ui_ContainerDle, 180, LV_PART_MAIN); // 半透明180/255 约 70%
lv_obj_add_flag( ui_ContainerDle, LV_OBJ_FLAG_HIDDEN ); // 默认隐藏
ui_ImageDel = lv_img_create(ui_ContainerDle);
lv_img_set_src(ui_ImageDel, &ui_img_s13_png);
lv_obj_set_width( ui_ImageDel, LV_SIZE_CONTENT);
lv_obj_set_height( ui_ImageDel, LV_SIZE_CONTENT);
lv_obj_set_x( ui_ImageDel, -60 ); // 左侧位置
lv_obj_set_y( ui_ImageDel, -120 ); // 容器顶部区域(屏幕底部可见区域)
lv_obj_set_align( ui_ImageDel, LV_ALIGN_CENTER );
lv_obj_add_flag( ui_ImageDel, LV_OBJ_FLAG_CLICKABLE ); // 可点击
lv_obj_clear_flag( ui_ImageDel, LV_OBJ_FLAG_SCROLLABLE );
ui_ImageReturn = lv_img_create(ui_ContainerDle);
lv_img_set_src(ui_ImageReturn, &ui_img_s14_png);
lv_obj_set_width( ui_ImageReturn, LV_SIZE_CONTENT);
lv_obj_set_height( ui_ImageReturn, LV_SIZE_CONTENT);
lv_obj_set_x( ui_ImageReturn, 60 ); // 右侧位置
lv_obj_set_y( ui_ImageReturn, -120 ); // 容器顶部区域(屏幕底部可见区域)
lv_obj_set_align( ui_ImageReturn, LV_ALIGN_CENTER );
lv_obj_add_flag( ui_ImageReturn, LV_OBJ_FLAG_CLICKABLE ); // 可点击
lv_obj_clear_flag( ui_ImageReturn, LV_OBJ_FLAG_SCROLLABLE );
// 添加点击事件回调
lv_obj_add_event_cb(ui_ImageDel, ui_event_ImageDel, LV_EVENT_ALL, NULL);
lv_obj_add_event_cb(ui_ImageReturn, ui_event_ImageReturn, LV_EVENT_ALL, NULL);
lv_obj_add_event_cb(ui_ScreenImg, ui_event_ScreenImg, LV_EVENT_ALL, NULL);
// 注意:不在此处加载图片,延迟到 LV_EVENT_SCREEN_LOADED 事件中加载
// 这样避免ui_init()时触发渲染导致开机闪烁
}
void ui_ScreenImg_screen_destroy(void)
{
#if LV_USE_GIF
pages_cleanup_gif();
#endif
if (ui_ScreenImg) lv_obj_del(ui_ScreenImg);
// NULL screen variables
ui_ScreenImg= NULL;
ui_ImgBle= NULL;
ui_ContainerDle= NULL;
ui_ImageDel= NULL;
ui_ImageReturn= NULL;
// 重置首次加载标志,下次进入时重新初始化
first_load = true;
free_spiffs_image_list();
}