嵌入式 UI 的资源之痛,Comlua 的字体与图片轻量化方案


做 ESP32、小屏设备 UI 的同行,大概都遇到过这几类问题:

· 想用好看的中文字体,一算体积:完整字库动辄几 MB,Flash 根本装不下;
· 界面只要几十个汉字,却被迫带上整份字库,RAM/Flash 白白被占满;
· 图片用 JPG/PNG 直接上屏,解码慢、占内存,小 MCU 一多图就卡甚至崩溃;
· 设计给的大图不能直接用在 320×240 屏上,自己裁图、改尺寸、转格式,流程散乱又容易出错。

Comlua 在这两块做了针对性的轻量化设计:字体「按需编译」+ 图片「裁剪 + bin 化」,把资源占用和开发效率一起压下来。下面按「现实痛点 → 方案 → 优势与局限」捋一遍,方便你判断是否适合自己的项目。


一、字体轻量:想要什么字,就编译什么字

【现实痛点】

· Flash 吃紧:ESP32-S3 的 Flash 要同时放固件、分区、字库。完整 CJK 字库(哪怕只到基本区)轻松上 MB,多字号、多字重就更夸张,很多项目根本用不起。
· 用多少和带多少不匹配:界面实际可能只用到几十到几百个汉字(菜单、提示、设置项),却被迫链一整套字库,大量字符永远用不到,仍然占空间。
· 维护成本高:换一个字就要重新选范围、重新生成、重新烧录,没有「按字符/按文件」的清晰流程,迭代很痛苦。

【Comlua 的做法】

Comlua 围绕「按需生成」做了一整套能力:

1. 按字符生成
   用 font.py 或字体 GUI(fonts/font_generator_gui.py)时,可以直接输入需要的字符(例如「卡设置SD主页面重启」),只把这些字的字形编译进字体,而不是整段 Unicode 区间。
   结果:界面用 50 个字,字库就只带 50 个字,体积从「几 MB」降到「几十 KB」量级,非常贴合小屏、固定文案的场景。

2. 按代码文件扫字符
   GUI 支持从 .txt / .json / .lua 等文件里自动提取出现的字符,再生成只包含这些字符的字体。
   这样:改文案或加新界面时,重新扫一遍工程里的文案文件 → 重新生成字体 → 体积始终和「实际用到的字」挂钩,不会莫名膨胀。

3. 按 Unicode 范围生成(可选)
   需要覆盖某一段字符(例如 0x4E00–0x9FFF 基本汉字)时,可以用 --unicode-ranges 生成「区间字库」,适合需要较多动态文案、又希望控制体积的项目;同时仍支持多字号、多 bpp(1bpp/4bpp)在体积和质量之间做权衡。

4. 编译时字体 + 运行时 bin 字体
   (1)编译时:字体直接编进固件,不占运行时 RAM,适合固定、少量字体。
   (2)运行时:用 lv_font_conv 生成 .bin 放到 SD 卡,通过 LVGL 的文件系统加载,适合「字体常换、不重烧固件」的场景。

整体上,「想要什么字就编译什么字」在 Comlua 里是可执行、可重复的流程,而不是手写字符集或盲目带全量字库。

【优势小结(字体)】

· 显著减小 Flash/RAM:按字符/按文件生成,体积与真实用量一致。
· 支持从工程文案自动抽字,减少手工维护。
· 可选编译时 / SD 卡 bin,兼顾体积与可更新性。
· 多字号、多 bpp 可配置,在清晰度和空间之间做权衡。

【局限与注意点(字体)】

· 若后续动态显示「未纳入字库」的字符(如用户输入、网络内容),会缺字或 fallback,需要提前规划字符集或预留 fallback 字体。
· 每次增删字符都要重新生成字体并烧录/更新,对「纯静态文案」最友好,对「强动态文本」要设计好字符集和发布流程。


二、图片轻量:裁剪 + bin,更省内存、更高效

【现实痛点】

· 解码与内存:在 MCU 上直接解码 JPG/PNG,需要临时缓冲区,大图或高分辨率很容易把 RAM 吃满,甚至导致崩溃;解码本身也吃 CPU,多图同屏时卡顿明显。
· 尺寸不匹配:设计给的图往往是给手机或大屏的,分辨率远大于 320×240,若不裁剪、不缩放就塞进工程,既占空间又占内存,显示时还要再缩放,得不偿失。
· 格式不统一:有人用 C 数组,有人用 bin,有人直接塞 JPG 路径,工具链散、校验少,经常出现「模拟器有图、真机没有」或「只显示一条色带」之类的问题。

【Comlua 的做法】

Comlua 的图片方案围绕「先裁剪/缩放,再转成 LVGL 原生 bin」:

1. 自带图片生成器 GUI(images/image_generator_gui.py)
   (1)选择源图(PNG/JPEG)后,可勾选裁剪,在预览里用鼠标框选有效区域,只保留需要的部分再导出。
   (2)支持自定义宽高(缩放),方便把大图压到目标分辨率,避免「大图小用」。
   这样:设计稿大图 → 在工具里裁好、缩好 → 再转 bin,一条链路完成,减少手工用别的软件裁图再贴回项目的步骤。

2. 输出 LVGL 的 .bin 格式
   (1)使用 lv_img_conv 生成二进制 .bin(如 CF_TRUE_COLOR + ARGB8565),而不是 C 数组文本。
   (2)设备端直接按 LVGL 的 bin 描述子加载,无需在 MCU 上做 JPG/PNG 解码,省掉解码器和临时缓冲,内存占用更可控,渲染也更稳定。
   (3)项目里还提供 check_bin.py,可检查生成的 .bin 是否为合法二进制(避免误用 C 数组文本导致只显示色带等问题)。

3. 模拟器与真机路径一致
   (1)模拟器会优先找同名的原始图(如 .jpg/.png)做显示,真机用同路径下的 .bin。
   (2)同一套布局和路径,开发时在 PC 上看图、真机用 bin,减少「在设备上才发现图错」的返工。

整体上,「裁剪 + 缩放 + 统一 bin」既控制了单张图的大小和显示区域,又统一了格式和工具链,更适合资源紧张的嵌入式屏。

【优势小结(图片)】

· 裁剪 + 缩放一体化,大图可裁成「刚好一屏或一控件」再用,省空间、省内存。
· bin 格式无需在设备上解码 JPG/PNG,降低 RAM 峰值和卡顿风险。
· 与 LVGL 原生格式一致,加载路径清晰,便于排查问题。
· 提供 bin 校验脚本,避免误用文本格式导致的显示异常。

【局限与注意点(图片)】

· bin 为未压缩位图,单张体积会比高质量 JPG 大,需要从「数量 + 分辨率 + 裁剪」综合控制,避免 SD/Flash 被图占满。
· 裁剪、尺寸、CF 格式(raw/indexed/alpha)需要在制作时定好,后期若改尺寸一般需重新生成 bin。


三、综合看:适合谁,不适合谁

【更适合】

· 小屏、固定或半固定文案的 ESP32-S3 UI(菜单、设置、状态提示等)。
· 非常在意 Flash/RAM,希望字体和图片「用多少占多少」的项目。
· 已有 JSON + Lua 的 Activity 架构,希望资源管线(字体 + 图片)也轻量化、可重复。

【需要额外考虑】

· 大量用户自定义输入、网络下发文案:字体要提前规划字符集或 fallback。
· 大量高清图、且存储非常紧:需在「图的数量、分辨率、是否用索引/alpha」上做权衡,必要时配合压缩或外部存储策略。


四、总结一句话

Comlua 在字体上做到「想要什么字就编译什么字」,在图片上做到「裁剪 + 统一 bin、设备端不解码」,直击嵌入式 UI 里「字库太大、图片又大又吃内存」的痛点;工具链(字体 GUI + 图片 GUI + bin 校验)也围绕这两点打通,方便在真实项目里落地。若你正在为 ESP32 小屏的字体体积和图片内存发愁,可以按上述思路对照自己的场景,看是否值得把 Comlua 的字体与图片管线纳入技术选型。

目前Comlua只在gitlab上开源了,最新版本也只适配了部分厂商,不过我研究了一下发现它把很多都抽象成组件和模块了,大部分硬件关键引脚定义也有专门的配置文件,应该适配一下还算简单,你可以fork 项目试试。

 

Logo

助力广东及东莞地区开发者,代码托管、在线学习与竞赛、技术交流与分享、资源共享、职业发展,成为松山湖开发者首选的工作与学习平台

更多推荐