问题: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 二进制。
100 lines
4.5 KiB
C
100 lines
4.5 KiB
C
/* test_meow_ui_ft.c — SDK 矢量字(meow_ft_*,§10.1)宿主单测。
|
||
* 覆盖任务要求的五项:加载 / 渲染像素非空 / 宽度测量 / LRU 淘汰 / 降级回退。
|
||
*
|
||
* 编译(宿主 gcc,链真实 gfx/sys/theme/vol/log/notify_ipc + 打桩 font/global + meow_ui_ft + 宿主 freetype):
|
||
* gcc -o test_meow_ui_ft test_meow_ui_ft.c test_host_stub.c \
|
||
* meow_ui_gfx.c meow_ui_sys.c meow_ui_theme.c meow_ui_vol.c meow_ui_log.c meow_notify_ipc.c \
|
||
* meow_ui_ft.c \
|
||
* -I. -Iinclude -I../meow_launcherd -DMEOW_UI_HOST_TEST -pthread vendor/freetype-build-host/libfreetype.a
|
||
* 用法:./test_meow_ui_ft [font.otf] (缺省用仓库 font/src/MiSans-Regular.otf)
|
||
*/
|
||
#include "meow_ui.h"
|
||
#include "meow_ui_internal.h" /* meow_ft_glyph(LRU 淘汰测试直接用底层光栅化) */
|
||
#include <stdio.h>
|
||
#include <stdlib.h>
|
||
#include <string.h>
|
||
|
||
static int fails = 0;
|
||
#define CHECK(c, m) do { if (c) printf(" ok %s\n", m); else { printf("FAIL %s\n", m); fails++; } } while (0)
|
||
|
||
/* 统计 back_buf 中 != bg 的像素数(RGB565 host 16bpp)。 */
|
||
static int count_changed(unsigned char *fb, int pitch, int bg565)
|
||
{
|
||
int n = 0;
|
||
for (int y = 0; y < 720; y++) {
|
||
uint16_t *row = (uint16_t *)(fb + (size_t)y * (size_t)pitch);
|
||
for (int x = 0; x < 480; x++) if (row[x] != (uint16_t)bg565) n++;
|
||
}
|
||
return n;
|
||
}
|
||
|
||
int main(int argc, char **argv)
|
||
{
|
||
const char *font = (argc > 1) ? argv[1] : "/home/Aska/Project/R3ProII/font/src/MiSans-Regular.otf";
|
||
meow_ui_opts o = { 480, 720 };
|
||
CHECK(meow_ui_init(&o) == 0, "meow_ui_init(480x720)");
|
||
int pitch = meow_ui_back_buf_pitch();
|
||
CHECK(pitch == 480 * 2, "back_buf pitch=960");
|
||
CHECK(meow_ui_back_buf_bpp() == 16, "bpp=16");
|
||
unsigned char *fb = meow_ui_back_buf();
|
||
CHECK(fb != NULL, "back_buf non-null");
|
||
|
||
/* ---- 1) 加载 ---- */
|
||
CHECK(!meow_ft_loaded(), "no font loaded initially");
|
||
CHECK(meow_ft_load("nonexistent-font.otf") != 0, "load(bad path) fails");
|
||
CHECK(!meow_ft_loaded(), "bad load leaves unloaded");
|
||
CHECK(meow_ft_load(font) == 0, "meow_ft_load(MiSans)");
|
||
CHECK(meow_ft_loaded(), "meow_ft_loaded after load");
|
||
CHECK(meow_ft_cache_slots() == 512, "default LRU slots=512");
|
||
|
||
/* ---- 2) 渲染像素非空(矢量) ---- */
|
||
meow_ui_clear(0xFFFFFF); /* 白底 */
|
||
int r = meow_ft_text(10, 50, "你好A", 20, 0x000000);
|
||
CHECK(r == 0, "meow_ft_text(vec) returns 0");
|
||
int vec_dark = count_changed(fb, pitch, 0xFFFF);
|
||
printf(" vec dark=%d\n", vec_dark);
|
||
CHECK(vec_dark > 50, "vector render produced non-empty pixels");
|
||
|
||
/* ---- 3) 宽度测量 ---- */
|
||
int w20 = meow_ft_width("你好A", 20);
|
||
int w10 = meow_ft_width("你好A", 10);
|
||
printf(" width(你好A,20)=%d width(...,10)=%d\n", w20, w10);
|
||
CHECK(w20 > 0, "vec width(20)>0");
|
||
CHECK(w20 > w10, "larger px -> larger width");
|
||
CHECK(meow_ft_width("", 20) == 0, "empty string width=0");
|
||
CHECK(meow_ft_width("你", 20) > 0, "CJK width>0");
|
||
/* 单 CJK 字 width ≈ 字号(MiSans CJK 满宽 advance) */
|
||
CHECK(meow_ft_width("你", 20) >= 17 && meow_ft_width("你", 20) <= 23, "CJK adv ~20");
|
||
|
||
/* ---- 4) LRU 淘汰 ---- */
|
||
int got = meow_ft_cache_set_slots(256);
|
||
CHECK(got == 256, "set LRU slots=256");
|
||
CHECK(meow_ft_cache_slots() == 256, "LRU slots now 256");
|
||
for (int i = 0; i < 300; i++) {
|
||
int adv = 0, bw = 0, bh = 0, left = 0, top = 0;
|
||
const uint8_t *bmp = NULL;
|
||
meow_ft_glyph(20, 0x4E00u + (uint32_t)i, &adv, &bmp, &bw, &bh, &left, &top);
|
||
}
|
||
int used = meow_ft_cache_used();
|
||
printf(" lru used=%d slots=256\n", used);
|
||
CHECK(used <= 256, "lru used <= 256 after 300 requests");
|
||
CHECK(used >= 1, "lru used >= 1");
|
||
meow_ft_cache_set_slots(512); /* 还原默认,供后续 */
|
||
|
||
/* ---- 5) 降级回退(无矢量字体 → 位图字库/5x7,不出豆腐) ---- */
|
||
meow_ft_unload();
|
||
CHECK(!meow_ft_loaded(), "after unload not loaded");
|
||
meow_ui_clear(0xFFFFFF);
|
||
r = meow_ft_text(10, 50, "X", 20, 0x000000);
|
||
CHECK(r == -1, "meow_ft_text degrades (returns -1)");
|
||
int deg_dark = count_changed(fb, pitch, 0xFFFF);
|
||
printf(" degrade dark=%d\n", deg_dark);
|
||
CHECK(deg_dark > 0, "degrade still draws (5x7/bitmap, no tofu crash)");
|
||
/* 降级宽度 = 位图字库宽度(宿主无位图库 → 5x7 兜底:len*6*scale(20=2)) */
|
||
CHECK(meow_ft_width("X", 20) == 12, "degrade width via bitmap (5x7) = 12");
|
||
|
||
meow_ui_shutdown();
|
||
printf("test_meow_ui_ft: %s\n", fails ? "FAIL" : "PASS");
|
||
return fails ? 1 : 0;
|
||
}
|