feat(sdk): SDK 矢量字(meow_ft_*,§10.1)——上收阅读器 FreeType 管线

问题:SDK 位图字库(meow_font)只有离散字号档位,全字形 atlas 稳态 17.3MiB
(27MB 整机装不下)→ 大字库/大字号必出豆腐。阅读器 R1/R2 的 FreeType 管线已真机验证
(MiSans OTF),本 commit 将其上收 SDK,让任意 App 用上无级字号 + CJK 全字形。

设计(追加式,与位图字库并存):
- meow_ui_ft.c(独立 .o):meow_ft_load/unload/loaded/text/width/line_height/
  ascender/descender/cache_set_slots/cache_slots/cache_used。底层按 READER_ENGINE_NOTES §5:
  FT_New_Face(path) 按需读文件(零整载 anon);灰阶 FT_LOAD_RENDER|NO_HINTING|TARGET_LIGHT;
  glyph LRU key=(px<<32)|cp,默认 512(~0.46MB),可配 256/1024;测宽不栅格化。
- meow_ui_gfx.c 只加两个薄封装 meow_gfx_ft_blend_px/dirty(写 back_buf + 标脏),
  使 meow_ui_ft.o 不引入新依赖——「不调 meow_ft_* 的 App 零增量」(freetype 不拉入,已验证)。
- 降级:加载失败/无卡 → meow_ft_loaded()==0 → meow_ft_text 自动改走 meow_ui_text(位图字库,
  5x7 兜底),绝不豆腐不崩;meow_ft_width/line_height 降级位图字库度量。
- FreeType 2.14.3 vendor 进 patch_v2/sdk/vendor/(gitignore)+ build_sdk.sh 可复现拉取+裁剪
  构建(-DFT_DISABLE_ZLIB/BZIP2/PNG/HARFBUZZ/BROTLI=TRUE,静态 musl),freetype .o 编入
  meow_ui.a,App 链接命令不变(meow_ui.a libz.a -lpthread)。

验证:
- 宿主 test_meow_ui_ft 5 项全 PASS(加载/渲染像素非空/宽度测量/LRU 淘汰/降级回退)。
- 全量 SDK 回归:widgets/sdk_fixes/dispatch/notif_gesture/overlay_suppress/listvirt/box/anim/
  scroll/ime_fix/pinyin/notif_open/sb_collapse/theme/notify_ipc/audio_host/vol/str/config/
  sdk_input_keys 全 PASS。
- 交叉 -Wall -Wextra 全 SDK TU 0 warning;qemu-mipsel 冒烟 PASS(mipsel FreeType 真光栅化)。
- meow_ui.a 新 md5 6ff5be9455f9e197b35fa120be012041(未重链不部署 App,四 App 免重链)。

MEOW_APP_SDK.md 补 §10.1 矢量字一节 + 公共头索引/§9 构建约定更新;.gitignore 忽略
patch_v2/sdk/vendor/ 与 test_meow_ui_ft 二进制。
This commit is contained in:
AskaEth committed 2026-09-02 09:51:23 +08:00
1 parent 121d30ec95
commit 54750a63c5
9 files changed
+542 -6

No files matched your search

+41 -3
View File
@@ -27,7 +27,7 @@
| `meow_ui_log.h` | 应用日志 | §9 |
| `meow_ui_notify.h` | 应用通知 | §9.1 |
| `meow_ui_config.h` | 应用配置读写 | §9.2 |
| `meow_ui_font.h` | 字体 | §5.7 卡片字体列表 + §10 字体分层 |
| `meow_ui_font.h` | 字体 | §5.7 卡片字体列表 + §10 字体分层 + §10.1 矢量字 `meow_ft_*` |
| `meow_ui_audio.h` | 独占音频客户端 | §11 |
| `meow_ui_str.h` | 字符串资源/i18n | §13 `meow_str`/`meow_strf`/`meow_str_set_locale` |
@@ -1210,9 +1210,11 @@ patch_v2/sdk/ # SDK 源码
├── stb_image.h / stb_image.c # 单头文件(vendor)
patch_v2/meow_launcherd/meow_font.a # 复用(静态链接)
```
构建出 `patch_v2/sdk/meow_ui.a`;应用编译链(zig-mipsel-cc)额外链 `meow_ui.a meow_font.a libz.a -lpthread`。
构建出 `patch_v2/sdk/meow_ui.a`;应用编译链(zig-mipsel-cc)额外链 `meow_ui.a meow_font.a libz.a -lpthread`(`meow_font.a` 已内联进 `meow_ui.a`,故实际只需 `meow_ui.a libz.a -lpthread`)。
> **构建耦合(已知设计,非缺陷)**:`build_sdk.sh` 用 `-I../meow_launcherd` 引 `meow_ipc.h`(`meow_notify_ipc.c` 复用共享协议头,同一份、杜绝两端漂移),故 SDK 编译期依赖 launcher 头、**非独立库**;**运行时无碍** —— App 只链 `meow_ui.a`(含 meow_font.o),不链入 launcher 符号,故 app 侧无 launcher 依赖。
> **构建耦合(已知设计,非缺陷)**:`build_sdk.sh` 用 `-I../meow_launcherd` 引 `meow_ipc.h`(`meow_notify_ipc.c` 复用共享协议头,同一份、杜绝两端漂移),故 SDK 编译期依赖 launcher 头、**非独立库**;**运行时无碍** —— App 只链 `meow_ui.a`(含 meow_font.o + freetype .o + meow_ui_ft.o),不链入 launcher 符号,故 app 侧无 launcher 依赖。
>
> **矢量字(§10.1)不改变 App 链接命令**:FreeType 裁剪后 .o 编入 `meow_ui.a`(`build_sdk.sh` vendor 拉取 + `-DFT_DISABLE_*` 裁剪,静态 musl);App 不调 `meow_ft_*` → `meow_ui_ft.o` 不拉取 → freetype 零增量;调 `meow_ft_*` → 自动拉入,App 无需额外 `-l` 任何 freetype/zlib/png 库。
**app.json 扩展字段**(向后兼容,缺省即旧行为):
```json
@@ -1242,6 +1244,42 @@ patch_v2/meow_launcherd/meow_font.a # 复用(静态链接)
- **B2(衔接阶段 D)**:输入/导航后端换成 IPC + M3 导航栈,应用框架字段 `framework=1` 的应用自动升级为"导航栈承载",裸应用保持兼容。
- **依赖**:`meow_present/back_buf/dirty`(M2.16 地基,✅)、`meow_font`(✅)、`blend565`(✅)——B1 全部基于现有能力,**无需新的硬件依赖**;仅新增 stb_image(单头文件)与输入线程。
### 10.1 矢量字(`meow_ft_*`,2026-09-02 追加)
**定位(SDK 演进的最后一块)**:SDK 位图字库(`meow_font`)只有**离散字号档位**,且全字形 atlas 稳态 17.3MiB(`FONT_MEMORY_DISCIPLINE.md` 实测)在 27MB 整机装不下 → 大字库/大字号场景必然出豆腐。阅读器 R1/R2 的 FreeType 管线已在真机验证(MiSans OTF 渲染,见 `READER_ENGINE_NOTES §5`)。本组 API 把该管线**上收 SDK**,让任意 App 用上**无级字号 + CJK 全字形**(解决豆腐类问题的根)。
**与位图字库并存(关键约定)**:App **显式**调用 `meow_ft_*` 才走矢量;既有 `meow_ui_text` 路径**一字不改**(默认仍是 `meow_font`)。`meow_ft_*` 全组是**追加 API**,不删改任何既有签名。
**API(`include/meow_ui_font.h`,公共)**:
```c
int meow_ft_load(const char *path); /* 载入 TTF/OTF;0=成功;重载清缓存 */
void meow_ft_unload(void); /* 释放矢量字体(可再 load) */
int meow_ft_loaded(void); /* 1=已载入;0=text/width 降级位图 */
int meow_ft_text(int x, int y, const char *utf8, int px, uint32_t color);
/* 矢量渲染;0=矢量;-1=无字体→已降级位图 */
int meow_ft_width(const char *utf8, int px); /* 矢量宽度(px,advance 累加,不栅格化) */
int meow_ft_line_height(int px); /* ascender - descender + 1 */
int meow_ft_ascender(int px); int meow_ft_descender(int px);
int meow_ft_cache_set_slots(int slots); /* glyph LRU 容量(默认 512;256 省内存 / 1024 提命中) */
int meow_ft_cache_slots(void); int meow_ft_cache_used(void);
```
- 坐标约定:`(x,y)=行首升线顶(同 meow_ui_text);baseline = y + meow_ft_ascender(px)`;px=字号(无级);color=24-bit RGB(内部按 back_buf 底色逐像素 blend565 反锯齿)。
- 渲染/测宽:`meow_ft_text`/`meow_ft_width` 逐码点(UTF-8),从 LRU 缓存取字形(**灰阶 `FT_LOAD_RENDER|FT_LOAD_NO_HINTING|FT_LOAD_TARGET_LIGHT`**,按当前 back_buf 底色 blend565);`meow_ft_width` 用 `FT_Load_Glyph` 不 RENDER(`FT_LOAD_NO_HINTING|FT_LOAD_TARGET_LIGHT`),不占 LRU 槽。
**降级回退(绝不豆腐不崩)**:加载失败 / 无卡 / 无字体文件 / 未调用 `meow_ft_load` → `meow_ft_loaded()==0`;此时 `meow_ft_text` **自动改走** `meow_ui_text`(位图字库 → 5x7 兜底),`meow_ft_width/line_height` 降级为位图字库度量,返回 `-1` 供 App 感知。App 若坚持 vector(缺字时)应先用 `meow_ft_loaded()` 判可用性。
**内存纪律**(目标单 App **≤1.5MB 增量**):
- `FT_New_Face(path)` **按需读文件**(`stream->base=NULL`,不整文件载入 → 零大块 anon;TTF/OTF disk-backed,可被内核回收重读)。**不做** mmap 后 `FT_New_Memory_Face`(会把整文件映射进地址空间)。
- anon 只含 FT 结构(~几十 KB)+ glyph LRU 位图。**glyph LRU 512 默认(≈0.46MB:28px CJK 约 900B/字),可配 256/1024**;容量设小/换字号/换字体时清空重建(key=`(px<<32)|cp`,不同字号可共存)。
- **增量只发生在 App 调用 `meow_ft_*` 时**:`meow_ui_ft.o` 是 meow_ui.a 独立成员,链接器仅在 App 引用 `meow_ft_*` 符号时拉取它及随之的 FreeType .o;不调 `meow_ft_*` 的 App **零增量**(`meow_ui_ft.o`/freetype 不拉入)。已验证:不调 ft 的 App 链接产物无任何 FT 符号。
- FreeType 走 `sdk/vendor/`(**gitignore**,`build_sdk.sh` 可复现拉取 + 裁剪构建:`-DFT_DISABLE_ZLIB/BZIP2/PNG/HARFBUZZ/BROTLI=TRUE`,静态 musl);无需 zlib/png,App 链接命令**不变**(仍 `meow_ui.a libz.a -lpthread`)。
**构建**:`build_sdk.sh` 增加 §0 FreeType vendor 拉取 + 裁剪构建(产物 `sdk/vendor/freetype-build/libfreetype.a`,其 .o 编入 meow_ui.a)。SDK 自身 `-Os -static -Wall -Wextra` 交叉 0 warning(clang 不报既有 gcc 的 misleading-indentation)。
**License**:FreeType 2.14.3(FreeType License,BSD 风格宽松许可;静态链入闭源固件无 LGPL 式 relink 义务,与阅读器 vendor 同源同版本)。NOTICE 同 `patch_v2/reader_app/NOTICE` 的 FreeType 条目。
---
## 11. 硬件约束(务必让应用作者知道)