/* 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 #include #include 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; }