- 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
87 lines
3.3 KiB
C
87 lines
3.3 KiB
C
/* test/book_smoke.c — os.meow.reader 书桥全链路 qemu-mipsel 冒烟。
|
|
* 开一本 TXT(UTF-8,含章节)→ 探测编码 → 建段+页索引 → FreeType 渲染首页(校验非底像素)→
|
|
* 翻页/锚定 → 改字号重排回锚 → 再渲染;全程不碰 SDK(book.c 只依赖 core+engine)。
|
|
* 跑于 qemu-mipsel(静态)。用法:qemu-mipsel ./test/book_smoke_mipsel <font.otf>
|
|
*/
|
|
#include "book.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)
|
|
|
|
static void write_txt(const char *path)
|
|
{
|
|
FILE *f = fopen(path, "wb");
|
|
fprintf(f, "第一章 测试编码与排版\n");
|
|
for (int i = 0; i < 60; i++) {
|
|
fprintf(f, "第%d段:这是一段用于验证流式排版与 FreeType 渲染的中文正文内容。"
|
|
"它包含汉字、标点,以及一些 English words 混合排版。"
|
|
"断行、避头尾与分页行为都要正确。\n", i + 1);
|
|
}
|
|
fprintf(f, "\nChapter two: the quick brown fox jumps over the lazy dog.\n");
|
|
fclose(f);
|
|
}
|
|
|
|
int main(int argc, char **argv)
|
|
{
|
|
const char *font = (argc > 1) ? argv[1] : "/home/Aska/Project/R3ProII/font/src/MiSans-Regular.otf";
|
|
const char *txt = "/tmp/rdbook_smoke.txt";
|
|
write_txt(txt);
|
|
|
|
rd_book *b = NULL;
|
|
int rc = rd_book_open(&b, txt, font, 20, 130, 480, 720, 16, 20, 62);
|
|
CHECK(rc == 0, "rd_book_open");
|
|
if (rc != 0) { printf("open rc=%d\n", rc); return 1; }
|
|
|
|
int np = rd_book_pages(b);
|
|
printf(" pages=%d basename=%s enc=%s\n", np, rd_book_basename(b), rd_book_encname(b));
|
|
CHECK(np > 0, "pages>0");
|
|
|
|
/* 渲染首页:校验有非背景像素 */
|
|
int W = 480, H = 720;
|
|
uint16_t *buf = (uint16_t *)malloc((size_t)W * H * 2);
|
|
uint16_t bg = 0xFFFF, fg = 0x0000;
|
|
rc = rd_book_render(b, 0, buf, W * 2, bg, fg);
|
|
CHECK(rc == 0, "render page0");
|
|
int inked = 0;
|
|
for (int i = 0; i < W * H; i++) if (buf[i] != bg) inked++;
|
|
printf(" page0 inked=%d px\n", inked);
|
|
CHECK(inked > 100, "page0 has ink");
|
|
|
|
/* 锚定当前页 */
|
|
int seg = -1; int32_t ch = -1;
|
|
rd_book_anchor(b, &seg, &ch);
|
|
CHECK(seg >= 0 && ch >= 0, "anchor valid");
|
|
|
|
/* 翻页 */
|
|
int cur = rd_book_current(b);
|
|
int np_total = rd_book_pages(b);
|
|
int nxt = rd_book_turn(b, 1);
|
|
CHECK(np_total > 1 || nxt == 0, "pages>1 or single-page clamps");
|
|
if (np_total > 1) CHECK(nxt == cur + 1, "turn fwd +1");
|
|
/* 回退 */
|
|
rd_book_turn(b, -1);
|
|
CHECK(rd_book_current(b) == cur, "turn back to cur");
|
|
|
|
/* 改字号(20→24) 重排回锚到含 (seg,ch) 的页 */
|
|
int np2 = rd_book_jump_to_anchor(b, seg, ch, 24, 130);
|
|
CHECK(np2 >= 0 && np2 < rd_book_pages(b), "reflow lands in range");
|
|
int seg2 = -1; int32_t ch2 = -1;
|
|
rd_book_anchor(b, &seg2, &ch2);
|
|
CHECK(seg2 == seg, "reflow keeps segment");
|
|
/* 重排后渲染 */
|
|
rc = rd_book_render(b, rd_book_current(b), buf, W * 2, bg, fg);
|
|
CHECK(rc == 0, "render after reflow");
|
|
|
|
/* 行距调整再排 */
|
|
int np3 = rd_book_set_size(b, 20, 150);
|
|
CHECK(np3 >= 0 && np3 < rd_book_pages(b), "set_size reflow ok");
|
|
|
|
free(buf);
|
|
rd_book_close(b);
|
|
printf("book_smoke: %s\n", fails ? "FAIL" : "PASS");
|
|
return fails ? 1 : 0;
|
|
}
|