Flipper Zero 固件资产(Firmware Assets)完全指南:编译流程、命名规则与目录结构解析
【免费下载链接】flipperzero-firmwareFlipper Zero firmware source code项目地址: https://gitcode.com/GitHub_Trending/fl/flipperzero-firmware
导读:本文以 Flipper Zero 固件仓库根目录的 assets/ReadMe.md 为主体,系统讲解固件资产的编译入口、图标与动画的命名规范、资产目录职责划分,并结合 scripts/assets.py、assets/SConscript 等源码深入剖析 PNG 图标到 C 数组、Dolphin 动画到独立资源包的底层实现。读完本文,你将掌握
./fbt icons proto dolphin_internal dolphin_blocking dolphin_ext resources一条命令背后的完整资产管线,能够为固件正确新增图标与动画资产,并理解 assets/dolphin/ReadMe.md 所定义的 Dolphin 动画配置文件格式。
一、什么是 Firmware Assets
在 Flipper Zero 固件仓库中,"资产(Assets)"泛指所有随固件打包的非代码资源,包括:
- 图标(Icons):界面按钮、状态栏、应用菜单使用的单帧位图,源文件为 PNG;
- 动画(Animations):多帧序列(Icons 动画)与桌面海豚(Dolphin)动画,由多张 PNG 帧组合而成;
- Protobuf 定义:RPC 与存储等模块通信所用的
.proto与.options文件; - Slideshow:桌面系统一次性播放的引导/更新幻灯片;
- Dolphin 游戏资产:分 internal / blocking / external 三类的桌面海豚动画资源。
这些资产集中在仓库的assets/目录,构建时被编译成 C 头文件/源文件(进入固件镜像)或被打包为资源文件(部署到 SD 卡)。
二、编译入口:一条命令完成全部资产构建
官方文档给出了资产编译的统一命令:
./fbt icons proto dolphin_internal dolphin_blocking dolphin_ext resources该命令实际包含 6 个构建目标(alias),其对应的真实构建逻辑在 assets/SConscript 中定义:
| 命令目标 | SConscript 对应别名 | 实际产物 |
|---|---|---|
icons | assetsenv.Alias("icons", icons) | 全部 PNG 图标/动画编译为assets_icons.[c,h],进入固件 |
proto | assetsenv.Alias("proto", proto) | protobuf/*.proto经 nanopb 生成.c/.h,同时生成protobuf_version.h(对应proto_ver别名) |
dolphin_internal | assetsenv.Alias("dolphin_internal", dolphin_internal) | 空闲海豚动画编译为assets_dolphin_internal.[h,c] |
dolphin_blocking | assetsenv.Alias("dolphin_blocking", dolphin_blocking) | 阻塞式系统通知动画编译为assets_dolphin_blocking.[h,c] |
dolphin_ext | assetsenv.Alias("dolphin_ext", dolphin_external) | 外部海豚动画打包为 SD 卡资源目录 |
resources | 由DolphinExtBuilder与后续打包步骤共同产出 | SD 卡资源文件(含 Manifest) |
其中dolphin_ext仅在IS_BASE_FIRMWARE为真时构建(assets/SConscript),即只有基础固件才生成可部署到 SD 卡的外部动画资源。构建产物统一输出到assets/compiled/工作目录(SConscript 中ASSETS_WORK_DIR=env.Dir("compiled")),最终由assetslib = assetsenv.Library("${FW_LIB_NAME}", assets_parts)汇总为一个名为assets的静态库参与固件链接。
提示:
./fbt是仓库根目录的构建封装脚本(见 fbt),在 Linux 下直接执行即可,Windows 对应fbt.cmd。
三、图标与动画命名规则(Asset naming rules)
官方文档规定图片与动画资产文件名必须符合以下三段式结构:
NAME_VARIANT_SIZE各段含义如下:
NAME(必填):资产名,使用 CamelCase 驼峰命名,仅允许字符[A-Za-z0-9],不允许特殊符号;VARIANT(可选):图标变体,用于表示状态或渲染条件,例如active(激活)、inactive(未激活)、inverted(反色);SIZE(必填):像素尺寸。正方形如10、20、24;长方形使用宽x高格式,如10x8、19x5。
命名完成后,图标文件名会被自动加上I_前缀,动画文件名自动加上A_前缀,并统一汇入生成的icon.h与icon.c。这一规则可以在 scripts/assets.py 的icons()函数中看到完整实现:
- 普通图标:
icon_name = "I_" + "_".join(filename.split(".")[:-1]),即去掉.png扩展名、用_连接各段、再补I_前缀; - 动画(目录内含
frame_rate文件):icon_name = "A_" + os.path.split(dirpath)[1],即取目录名加A_前缀,目录名中的-会替换为_。
以仓库真实文件为例,assets/icons/StatusBar 目录下的命名完全符合该规范:
Alert_9x8.png→I_Alert_9x8Battery_26x8.png→I_Battery_26x8Bluetooth_Connected_16x8.png→I_Bluetooth_Connected_16x8(VARIANT 为Connected)Charging-lightning_9x10.png→I_Charging_lightning_9x10(文件名中的-被替换为_)
单帧图标 vs 多帧动画的判定
scripts/assets.py 的遍历逻辑以目录中是否存在名为frame_rate的文件来区分两种类型:
- 图标:目录内直接放置 PNG 文件,每张图生成
const uint8_t {name}[] = {...}帧数据与const Icon {name} = {...}图标描述符,frame_count固定为 1; - 动画:目录内放置
frame_XX.png帧序列与一个frame_rate文本文件(内容为帧率数值,如 assets/icons/Animations/Levelup1_128x64/frame_rate 中的2)。所有帧必须尺寸一致(脚本通过assert width == temp_width强制校验),并按文件名排序生成帧数组,frame_rate从文件读取,frame_count为帧数。
生成产物格式
最终icon.c/icon.h的骨架如下(模板定义见 scripts/assets.py):
// icon.h #pragma once #include <gui/icon.h> extern const Icon I_Alert_9x8; extern const Icon A_Levelup1_128x64; // icon.c #include "assets_icons.h" #include <gui/icon_i.h> const uint8_t I_Alert_9x8_0[] = {...}; // 帧数据 const uint8_t* const _I_Alert_9x8[] = {_I_Alert_9x8_0}; // 帧指针数组 const Icon I_Alert_9x8 = {.width=9,.height=8,.frame_count=1,.frame_rate=0,.frames=_I_Alert_9x8};四、PNG 到 C 数组的底层实现原理
scripts/flipper/assets/icon.py 实现了图标转换的完整管线,共 4 步:
- PNG → 1-bit XBM:优先使用 Pillow 将 PNG 转换为 1 位色深的 XBM 格式(
im.convert("1")+ImageOps.invert),Pillow 缺失时回退到 ImageMagick 的convertCLI; - XBM 解析:从 XBM 文本中读取
width、height与逐字节像素数据; - Heatshrink 压缩:将像素数据用 heatshrink(窗口 8、前瞻 4)压缩,优先使用
heatshrink2Python 模块,缺失时回退到heatshrink命令行工具; - 编码选择:比较压缩前后长度,若压缩后更小(含 2 字节长度头)则采用
\x01\x00 + 压缩数据格式,否则使用未压缩的\x00 + 原始数据(icon.py)。
此外脚本会对图像尺寸做上限校验:MAX_IMAGE_WIDTH = 2**16 - 1、MAX_IMAGE_HEIGHT = 2**16 - 1(scripts/assets.py),超出即报错退出。
五、Dolphin 与游戏资产:按等级分组的扩展规则
官方文档指出,Dolphin(桌面海豚)资产与游戏资产的命名规则与图标动画相同,但额外要求"按等级(level)分组,且等级作为NAME的前缀"。以 assets/dolphin/internal 目录为例,真实动画目录名L1_Tv_128x47、L1_NoSd_128x49即由L1(等级 1)+Tv/NoSd(名称)+128x47/128x49(尺寸)组成。
Dolphin 资产分为三部分(详见 assets/dolphin/ReadMe.md):
| 类型 | 用途 | 产物 |
|---|---|---|
| blocking | 阻塞式系统通知动画(如低电量、无 SD 卡) | 打包进assets_dolphin_blocking.[h,c] |
| internal | 空闲状态的内置海豚动画 | 转换为assets_dolphin_internal.[h,c] |
| external | 空闲状态的外部海豚动画 | 打包到资源目录,部署至 SD 卡 |
Dolphin 动画目录的必备文件
每个 Dolphin 动画目录包含三类文件(见 assets/dolphin/internal/L1_NoSd_128x49):
manifest.txt:动画枚举清单,用于随机动画选择,是 Dolphin 的起始入口;meta.txt:描述动画如何绘制(尺寸、帧序列、气泡对话框等);frame_X.png:动画帧位图,编号从 0 开始。
manifest.txt 格式
采用 Flipper Format File(有序键值对)格式,头部固定为:
Filetype: Flipper Animation Manifest Version: 1每条动画记录包含以下字段(仓库真实示例见 assets/dolphin/internal/manifest.txt):
| 键 | 含义 | 仓库示例值 |
|---|---|---|
Name | 动画名,必须与动画目录名完全一致 | L1_NoSd_128x49 |
Min butthurt/Max butthurt | 海豚"不爽值"(butthurt)的允许区间 | 0/14 |
Min level/Max level | 海豚等级的允许区间;若为 0,则该动画不参与随机空闲选择,只能按名称精确触发 | 1/3 |
Weight | 随机选择时被选中的权重 | 3、6 |
文档特别说明:某些动画可以被排除在随机选择之外(如L1_NoSd_128x49这类需要特定硬件状态触发的动画)。
meta.txt 格式
同样为 Flipper Format File,头部固定为:
Filetype: Flipper Animation Version: 1字段包括(仓库真实示例见 assets/dolphin/internal/L1_NoSd_128x49/meta.txt):
| 键 | 含义 |
|---|---|
Width/Height | 动画宽高(宽 ≤ 128,高 ≤ 64) |
Passive frames | 被动状态位图帧数 |
Active frames | 主动状态位图帧数(可为 0) |
Frames order | 帧播放顺序,前 N 个为被动帧、后 M 个为主动帧;X 对应frame_X.bm帧,可重复引用同一帧 |
Active cycles | 主动帧的重复周期数,例如主动帧为 6、7 且 Active cycles=3,则完整主动段播放6 7 6 7 6 7;被动 + 主动的完整周期称为total period |
Frame rate | 每秒播放帧数 |
Duration | 单次动画播放总秒数 |
Active cooldown | 退出主动模式后、再次进入主动模式前需要等待的秒数 |
Bubble slots | 气泡(对话框)序列的数量 |
Slot | 同一序列气泡的分组号 |
X/Y | 气泡左上角坐标 |
Text | 气泡文本,换行用\n |
AlignH/AlignV | 气泡角落的水平(Left/Center/Right)与垂直(Top/Center/Bottom)对齐方式 |
StartFrame/EndFrame | 气泡在完整周期内显示所覆盖的帧索引范围 |
帧索引的换算方法
文档给出了一个关键示例帮助理解帧索引(frame indexes)与真实帧顺序(real frames order)的关系。假设配置为:
Passive frames: 6 Active frames: 2 Frames order: 0 1 2 3 4 5 6 7 Active cycles: 4则索引换算如下:
passive(6) active (2 * 4) Real frames order: 0 1 2 3 4 5 6 7 6 7 6 7 6 7 Frames indexes: 0 1 2 3 4 5 6 7 8 9 10 11 12 13即:被动段 6 帧依次播放后,主动段的 2 帧按Active cycles重复 4 次,共 8 帧;用于StartFrame/EndFrame的气泡帧索引为累计后的全局索引(0–13)。
六、资产目录结构总览
官方文档给出了assets/目录的结构划分,结合仓库实际布局可汇总如下:
assets/ ├── ReadMe.md # 本文档 ├── SConscript # 资产构建脚本(SCons 集成) ├── dolphin/ # Dolphin 游戏资产源 │ ├── blocking/ # 阻塞式通知动画 → 编译进固件 │ ├── external/ # 外部空闲动画 → 打包到 SD 卡 resources │ ├── internal/ # 内置空闲动画 → 编译进固件 │ └── ReadMe.md # Dolphin 动画格式规范 ├── icons/ # 图标/动画源(PNG + frame_rate) │ ├── StatusBar/ # 状态栏图标,如 I_Battery_26x8 │ ├── Animations/ # 多帧动画目录,如 A_Levelup1_128x64 │ └── MainMenu/ NFC/ SubGhz/ ... # 按功能分组的图标 ├── protobuf/ # Protobuf 源(.proto + .options + Changelog) └── slideshow/ # 桌面一次性幻灯片(first_start、update_default)各目录的构建去向(官方文档 + assets/SConscript 双重印证):
dolphin→ 输出到 build 目录的compiled与resources两个文件夹(分别对应编译进固件与部署到 SD 卡);icons→ 输出到 build 目录的compiled文件夹(生成assets_icons.[c,h]);protobuf→ 输出到 build 目录的compiled文件夹(生成 C 代码与版本头);slideshow→ 桌面的单次播放幻灯片资源。
七、重要注意事项
官方文档强调了一条容易被忽视的构建约束:
不要引入未使用的资产,编译器不会剥离未使用的资产。
这意味着每一张 PNG、每一个动画目录最终都会以 C 数组形式被链接进固件镜像(图标走I_/A_前缀进入icon.c,Dolphin 动画走DolphinSymBuilder进入assets_dolphin_*.c)。从 assets/SConscript 可以看出,所有资产部分(icons、proto、dolphin_blocking、dolphin_internal、proto_ver)被统一收进名为assets的静态库,随固件链接,因此资产会直接占用固件 Flash 空间。新增资产前应评估其体积与必要性,尽量复用 assets/icons/Common 等目录下的通用图标。
八、实战:为固件新增一个图标或动画
综合上述规则,新增资产的完整流程如下:
新增单帧图标:
- 准备 1-bit(黑白)PNG,按
NAME_VARIANT_SIZE.png命名,如MyIcon_Active_10x8.png; - 放入 assets/icons 下对应功能目录(如
Common)或自建目录; - 执行
./fbt icons(或完整的./fbt icons proto dolphin_internal dolphin_blocking dolphin_ext resources)重新生成assets_icons.[c,h]; - 在代码中通过生成的符号
I_MyIcon_Active_10x8引用(含I_前缀),该符号声明于生成的icon.h,类型为const Icon。
新增多帧动画:
- 自建目录,命名如
MyAnim_10x8(目录名即动画名); - 放入尺寸一致的
frame_00.png、frame_01.png... 帧序列; - 在目录内创建
frame_rate文件,内容为整数帧率(如2,参考 assets/icons/Animations/Levelup1_128x64/frame_rate); - 重新编译后通过
A_MyAnim_10x8符号引用。
新增 Dolphin 动画:
- 在 assets/dolphin/internal(或 blocking/external)下创建
Lx_Name_宽x高/目录; - 放入
frame_0.png、frame_1.png... 帧与meta.txt(按第五节格式填写); - 将动画条目追加到对应目录的
manifest.txt; - 重新执行
./fbt dolphin_internal或完整构建命令,internal/blocking 类型编译进固件,external 类型由dolphin_ext打包至 SD 卡 resources。
说明:external 类型资产的打包依赖
IS_BASE_FIRMWARE构建选项与 SD 卡部署流程,普通固件构建默认只处理 internal 与 blocking 类型。
九、延伸阅读
- assets/dolphin/ReadMe.md:Dolphin 动画三类资源(blocking/internal/external)与 manifest.txt、meta.txt 的完整格式规范,是本文第五节内容的原始出处;
- assets/SConscript:资产构建与固件静态库的 SCons 集成逻辑;
- scripts/assets.py:资产处理命令行工具(
icons、manifest、copro、dolphin四个子命令)与图标/动画生成实现; - scripts/flipper/assets/icon.py:PNG → 1-bit XBM → heatshrink 压缩 → C 数组的转换管线;
- fbt:固件构建封装脚本,资产编译命令
./fbt ...的入口。
通过本文,你可以从"按文档放置资产文件"跨越到"理解资产如何被编译、压缩、打包并最终进入固件镜像或 SD 卡资源",为深度定制 Flipper Zero 固件打下扎实基础。
【免费下载链接】flipperzero-firmwareFlipper Zero firmware source code项目地址: https://gitcode.com/GitHub_Trending/fl/flipperzero-firmware
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考