reader(R2): EPUB/FB2/MOBI/CBZ 加载器落地
- rdsrc 统一文档源抽象:TXT/FB2/EPUB/MOBI/CBZ 都产出「UTF-8 段序列+目录+元数据+图片」, 与 R1 rdtxt 段契约一致;book.c 改喂 rd_src(文本)或图页(CBZ)。 - EPUB:miniz 解 ZIP → 自写 rdxml 回调解析 OPF(spine/manifest)+NCX/nav → 正文 XHTML 用 rdhtml 逐行粗解析(坏标签/未闭合/实体容错,不建 DOM),章节按 RDSRC_SEG_MAX 分片。 - FB2:rdxml 严格解析抽 section 段落 + base64 内嵌图;段题进正文+目录。 - MOBI/AZW3:vendor libmobi 最小调用面(mobi_init→load→parse_kf7/8→parse_rawml_opt→ flow/resources),HTML 走 rdhtml 同款粗解析;LGPL-3.0 合规见 NOTICE(版权+全文+锁定 commit)。 - CBZ:miniz 解 ZIP → 自然序图片序列,rdimg(stb_image) 解码+降采样,阅读页图页模式。 - 目录显示与跳转(EPUB NCX/FB2 section/MOBI 标题);书架开放四格式;进度按 (段,字符偏移)(TXT/FB2/EPUB/MOBI)/页号(CBZ)。 - 宿主单测 loaders_smoke(四格式抽取/目录/图片/base64/页序)+ qemu-mipsel formats_smoke/book_smoke;R1 test_reader_core 58/0 不回归。 - build_reader.sh 集成 miniz/libmobi/stb/zlib 头;NOTICE 入仓;交叉 -Wall -Wextra 0 warning。 新 reader md5: e29036599300eb277b1c960a75b65dbf
This commit is contained in:
1 parent
10690bc348
commit
0294553096
27 files changed
+3440
-193
No files matched your search
@@ -17,7 +17,7 @@
|
||||
#include <time.h>
|
||||
|
||||
#define RD_MENU_H 64
|
||||
#define RD_MENU_BTN 5
|
||||
#define RD_MENU_BTN 6
|
||||
|
||||
static rd_book *g_book;
|
||||
static int g_reader_active;
|
||||
@@ -29,6 +29,8 @@ static int g_fs, g_ls;
|
||||
static uint16_t *g_page_buf[RD_PAGE_CACHE]; /* [0]=prev [1]=cur [2]=next(FreeType 页位图) */
|
||||
static int g_has_font; /* 1=FreeType 路径;0=SDK 字库兜底 */
|
||||
static int g_menu_open;
|
||||
static int g_dir_open; /* 目录覆盖层 */
|
||||
static int g_dir_top, g_dir_row_h; /* 目录列表几何(rdsrc 目录项) */
|
||||
static char g_font_path[400];
|
||||
static int g_notice_ms;
|
||||
|
||||
@@ -125,7 +127,7 @@ static void reader_canvas_render(meow_widget *w, const meow_theme *th, void *ctx
|
||||
meow_ui_clear(g_page_bg);
|
||||
if (g_book) {
|
||||
int page = rd_book_current(g_book);
|
||||
if (g_has_font && g_page_buf[RD_PAGE_SLOT_CUR]) {
|
||||
if ((g_has_font || rd_book_is_image(g_book)) && g_page_buf[RD_PAGE_SLOT_CUR]) {
|
||||
/* 翻页 blit:行拷贝当前页位图 → back_buf(局部脏区由 flush_region 上屏) */
|
||||
unsigned char *bb = meow_ui_back_buf();
|
||||
int pitch = meow_ui_back_buf_pitch();
|
||||
@@ -154,15 +156,41 @@ static void reader_canvas_render(meow_widget *w, const meow_theme *th, void *ctx
|
||||
if (g_menu_open) {
|
||||
int y = g_ph - RD_MENU_H;
|
||||
meow_ui_fill_rect_alpha(0, y, g_pw, RD_MENU_H, 0x000000, 160);
|
||||
/* 5 按钮:返回 | 字号− | 字号+ | 行距− | 行距+ */
|
||||
/* 6 按钮:返回 | 目录 | 字− | 字+ | 距− | 距+ */
|
||||
int bw = g_pw / RD_MENU_BTN;
|
||||
const char *labels[RD_MENU_BTN] = { "返回", "字-", "字+", "距-", "距+" };
|
||||
const char *labels[RD_MENU_BTN] = { "返回", "目录", "字-", "字+", "距-", "距+" };
|
||||
for (int i = 0; i < RD_MENU_BTN; i++) {
|
||||
int bx = i * bw;
|
||||
meow_ui_fill_rect(bx + 4, y + 12, bw - 8, RD_MENU_H - 24, th->surfaceContainer);
|
||||
meow_ui_text_center(bx + bw / 2, y + (RD_MENU_H - 20) / 2 - 6, labels[i], 20, th->fg);
|
||||
}
|
||||
}
|
||||
if (g_dir_open && g_book) {
|
||||
/* 目录覆盖层:半透明底 + 条目列表(当前行高亮) */
|
||||
int n = rd_book_toc_n(g_book);
|
||||
if (n > 0) {
|
||||
int top = g_top + 8;
|
||||
int row = 34;
|
||||
int maxshow = (g_ph - top - 70) / row;
|
||||
if (maxshow < 1) maxshow = 1;
|
||||
if (maxshow > n) maxshow = n;
|
||||
g_dir_top = top; g_dir_row_h = row;
|
||||
meow_ui_fill_rect_alpha(0, g_top, g_pw, g_ph - g_top, 0x000000, 150);
|
||||
char t[96]; int sg; int32_t cch;
|
||||
int page = rd_book_current(g_book);
|
||||
for (int i = 0; i < maxshow; i++) {
|
||||
if (rd_book_toc(g_book, i, t, sizeof t, &sg, &cch) != 0) continue;
|
||||
int y0 = top + i * row;
|
||||
if (page == sg) meow_ui_fill_rect(0, y0, g_pw, row - 4, th->surfaceContainer);
|
||||
meow_ui_text(24, y0, t, 20, th->fg);
|
||||
meow_ui_draw_rect(12, y0 + row - 6, g_pw - 24, 1, th->outline);
|
||||
}
|
||||
meow_ui_text(20, g_ph - 34, "点条目跳转 · 点空白关闭", 16, th->dim);
|
||||
} else {
|
||||
g_dir_open = 0;
|
||||
meow_ui_text(20, g_top + 40, "本书无目录", 20, th->dim);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void reader_redraw(void)
|
||||
@@ -181,7 +209,7 @@ static void reader_free_page_bufs(void)
|
||||
static int reader_refill_page(void)
|
||||
{
|
||||
if (!g_book) return -1;
|
||||
if (g_has_font && g_page_buf[RD_PAGE_SLOT_CUR]) {
|
||||
if ((g_has_font || rd_book_is_image(g_book)) && g_page_buf[RD_PAGE_SLOT_CUR]) {
|
||||
uint16_t bg = rgb565(g_page_bg), fg = rgb565(g_page_fg);
|
||||
int cur = rd_book_current(g_book);
|
||||
int np = rd_book_pages(g_book);
|
||||
@@ -230,7 +258,7 @@ void reader_open(const char *path)
|
||||
}
|
||||
chapter_name();
|
||||
|
||||
if (g_has_font) {
|
||||
if (g_has_font || rd_book_is_image(g_book)) {
|
||||
reader_free_page_bufs();
|
||||
for (int i = 0; i < RD_PAGE_CACHE; i++)
|
||||
g_page_buf[i] = (uint16_t *)malloc((size_t)g_pw * (size_t)g_ph * 2);
|
||||
@@ -244,7 +272,7 @@ void reader_open(const char *path)
|
||||
|
||||
reader_refill_page();
|
||||
|
||||
g_menu_open = 0;
|
||||
g_menu_open = 0; g_dir_open = 0;
|
||||
if (g_reader_active) reader_redraw();
|
||||
}
|
||||
|
||||
@@ -269,7 +297,7 @@ static void reader_on_destroy(meow_view *v, void *ctx)
|
||||
if (g_book) rd_book_close(g_book);
|
||||
g_book = NULL;
|
||||
reader_free_page_bufs();
|
||||
g_menu_open = 0; g_reader_active = 0;
|
||||
g_menu_open = 0; g_dir_open = 0; g_reader_active = 0;
|
||||
}
|
||||
|
||||
meow_view *reader_view_create(void)
|
||||
@@ -287,6 +315,30 @@ int reader_is_active(void) { return g_reader_active; }
|
||||
void reader_on_tap(int x, int y)
|
||||
{
|
||||
if (!g_book) { return; }
|
||||
if (g_dir_open) {
|
||||
/* 目录覆盖层:点条目跳转,点空白关闭 */
|
||||
int n = rd_book_toc_n(g_book);
|
||||
if (n > 0 && x >= 0 && y >= g_dir_top) {
|
||||
int row = g_dir_row_h > 0 ? g_dir_row_h : 34;
|
||||
int idx = (y - g_dir_top) / row;
|
||||
if (idx < n && idx >= 0) {
|
||||
char t[96]; int sg; int32_t cch;
|
||||
if (rd_book_toc(g_book, idx, t, sizeof t, &sg, &cch) == 0) {
|
||||
if (g_book && sg >= 0) {
|
||||
rd_book_jump_to_anchor(g_book, sg, cch, g_fs, g_ls);
|
||||
g_dir_open = 0; g_menu_open = 0;
|
||||
snprintf(g_chapter, sizeof g_chapter, "%s", t);
|
||||
reader_refill_page();
|
||||
reader_redraw();
|
||||
reader_reflow_persist();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
g_dir_open = 0; reader_redraw();
|
||||
return;
|
||||
}
|
||||
if (g_menu_open) {
|
||||
/* 菜单按钮命中 */
|
||||
int y0 = g_ph - RD_MENU_H;
|
||||
@@ -299,10 +351,14 @@ void reader_on_tap(int x, int y)
|
||||
reader_reflow_persist();
|
||||
meow_view_page_pop();
|
||||
return;
|
||||
case 1: if (g_fs > 14) { g_fs -= 2; reader_reflow(); } break;
|
||||
case 2: if (g_fs < 40) { g_fs += 2; reader_reflow(); } break;
|
||||
case 3: if (g_ls > 100) { g_ls -= 5; reader_reflow(); } break;
|
||||
case 4: if (g_ls < 200) { g_ls += 5; reader_reflow(); } break;
|
||||
case 1: /* 目录 */
|
||||
if (rd_book_toc_n(g_book) > 0) { g_dir_open = 1; g_menu_open = 0; reader_redraw(); }
|
||||
else { g_menu_open = 0; reader_redraw(); }
|
||||
return;
|
||||
case 2: if (g_fs > 14) { g_fs -= 2; reader_reflow(); } break;
|
||||
case 3: if (g_fs < 40) { g_fs += 2; reader_reflow(); } break;
|
||||
case 4: if (g_ls > 100) { g_ls -= 5; reader_reflow(); } break;
|
||||
case 5: if (g_ls < 200) { g_ls += 5; reader_reflow(); } break;
|
||||
}
|
||||
if (y >= y0) return;
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user