reader R1: os.meow.reader 书架+TXT 垂直链路骨架

- core: 编码检测(BOM→UTF-8严格→GB18030/Big5统计, Big5与GB重叠按GB18030兜底, §3.5风险注明)
  + GB18030双字节码表(uint16[126][190]=47880B, tools/gen_gb18030_table.py 生成, 生成物入仓)
  + 流式排版器(贪心断行+Kinsoku避头尾+分页+重排锚定, rd_meas_fn 测量回调替 FreeType, 宿主可测)
  + TXT 流式加载器(按段读, 不整文件入内存, \n 恒独立换行可安全分段)
- engine: FreeType 封装(FT_New_Face 按需读文件, NO_HINTING/LIGHT 灰阶, LRU 512 key=码点+字号,
  blend565, rdft_advance 只测宽不栅格化)
- app: 书架(扫 Books/ 识别 5 后缀, 最近读置顶, 空态提示) + 阅读页(W_CUSTOM 画布 3 栏翻页
  +中央菜单占位(字号±/行距±/返回) +顶栏进度%/章节名) + 书桥(段表+flat 页索引, 渲染, 重排回锚)
  + 进度持久化(MeowData/os.meow.reader/progress/, path hash→seg/ch/fs/ls/ts)
- 取舍: 正文字体 FreeType 直读 TF MeowOS/fonts/ TTF/OTF; 无字体回退 SDK 位图字库(meow_ui_text,
  未打包字体(许可/体积), 全 glyph 兜底 R2); 页位图缓存当前+前后各1页(3×691KB), 翻页动画默认无
- 内存实测(宿主 --wrap malloc, 73页书): 书结构 130KB + 当前段刮擦~1.6MB(行/页容量按 n 偏高,R2 收紧)
  + 字形缓存~650KB + 3页位图 2.07MB ≈ 4.45MB 动态, 在 ≤8MB 阅读态预算内
- 测试: 宿主 core 58/58(enc/gb18030金标准/kinsoku/page/reflow/txt); qemu-mipsel ft_smoke PASS
  + book_smoke(12页/渲染有墨/翻页/重排回锚) PASS; reader 交叉 -Wall -Wextra 0 warning
- 产物: reader(mipsel 静态1.2MB)+build_reader.sh(vendor FreeType 可复现); vendor/二进制 gitignore
This commit is contained in:
MeowOS committed 2026-09-01 23:26:42 +08:00
1 parent 33d3059038
commit 553a7d5d9e
26 files changed
+6155

No files matched your search

+71
View File
@@ -0,0 +1,71 @@
/* test/rdft_smoke.c — os.meow.reader FreeType 封装 qemu-mipsel 冒烟。
* 在 mipsel 上初始化 rdft → 载入思源黑体/MiSans OTF → 定字号 → 取 CJK/拉丁字形度量 →
* blend565 到 RGB565 缓冲 → 校验像素变化 → LRU 缓存 512 上限。可跑于 qemu-mipsel(静态)。
*
* 用法:qemu-mipsel ./test/rdft_smoke <font.otf> (缺省用仓库 font/src/MiSans-Regular.otf)
*/
#include "rdft.h"
#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)
int main(int argc, char **argv)
{
const char *font = (argc > 1) ? argv[1] : "/home/Aska/Project/R3ProII/font/src/MiSans-Regular.otf";
rdft *r = NULL;
CHECK(rdft_init(&r) == 0, "rdft_init");
CHECK(r != NULL, "rdft non-null");
CHECK(rdft_load_font(r, font) == 0, "rdft_load_font");
CHECK(rdft_ascender(r) > 0, "ascender>0");
printf(" family=%s ascender=%d descender=%d\n", rdft_family(r), rdft_ascender(r), rdft_descender(r));
rdft_set_px(r, 20);
/* 字形度量:你(U+4F60)/好(U+597D)/A/空格/软连字符 */
const uint32_t cps[] = { 0x4F60, 0x597D, 'A', ' ', 0x00AD, 0x3002 };
for (int i = 0; i < 6; i++) {
int adv = 0, bw = 0, bh = 0, left = 0, top = 0;
const uint8_t *bmp = NULL;
CHECK(rdft_glyph(r, cps[i], &adv, &bmp, &bw, &bh, &left, &top) == 0, "glyph");
printf(" cp=U+%04X adv=%d bmp=%dx%d left=%d top=%d\n", cps[i], adv, bw, bh, left, top);
if (cps[i] == 'A') CHECK(adv > 5, "A advance>5");
if (cps[i] == 0x4F60) CHECK(adv >= 16 && adv <= 22, "CJK adv ~20");
}
/* blend565:把「你」字画进一个 RGB565 缓冲(80×80,白底黑字),校验有像素变黑 */
{
int W = 80, H = 80;
uint16_t *fb = (uint16_t*)malloc((size_t)W * H * 2);
for (int i = 0; i < W * H; i++) fb[i] = 0xFFFF; /* 白 */
int adv = 0, bw = 0, bh = 0, left = 0, top = 0;
const uint8_t *bmp = NULL;
rdft_glyph(r, 0x4F60, &adv, &bmp, &bw, &bh, &left, &top);
int baseline = 30;
rdft_blend565(fb, W * 2, W, H, 10, baseline, bmp, bw, bh, left, top, 0x0000, 0xFFFF);
int dark = 0;
for (int i = 0; i < W * H; i++) if (fb[i] != 0xFFFF) dark++;
CHECK(dark > 10, "blend produced dark pixels");
free(fb);
}
/* LRU 缓存上限:请求 600 个不同码点 → used 应 ≤ 512 */
{
rdft_cache_set_slots(r, 512);
for (uint32_t cp = 0x4E00; cp < 0x4E00 + 600; cp++) {
int adv = 0, bw = 0, bh = 0, left = 0, top = 0;
const uint8_t *bmp = NULL;
rdft_glyph(r, cp, &adv, &bmp, &bw, &bh, &left, &top);
}
int used = rdft_cache_used(r);
printf(" lru used=%d slots=%d\n", used, rdft_cache_slots(r));
CHECK(used <= 512, "lru used <= 512");
CHECK(used >= 1, "lru used >= 1");
}
rdft_free(r);
printf("rdft_smoke: %s\n", fails ? "FAIL" : "PASS");
return fails ? 1 : 0;
}