From a9638fa59c0a47aed51854dea5d7394c97496351 Mon Sep 17 00:00:00 2001 From: AskaEth Date: Tue, 10 Nov 2026 13:22:35 +0800 Subject: [PATCH] =?UTF-8?q?perf(reader):=20M30=20=E5=88=A0=E6=8E=89?= =?UTF-8?q?=E6=97=A0=E4=BA=BA=E6=B6=88=E8=B4=B9=E7=9A=84=E4=B8=8B=E4=B8=80?= =?UTF-8?q?=E9=A1=B5=E9=A2=84=E5=8F=96=E6=A7=BD=EF=BC=88refill=20=E6=B8=B2?= =?UTF-8?q?=E6=9F=93=E5=87=8F=E5=8D=8A=E3=80=81=E5=B8=B8=E9=A9=BB=20?= =?UTF-8?q?=E2=88=920.69MB=EF=BC=89+=20=E5=9B=9E=E4=B9=A6=E6=9E=B6?= =?UTF-8?q?=E7=BD=AE=E9=A1=B6=E5=88=9A=E8=AF=BB=E5=AE=8C=E7=9A=84=E4=B9=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- patch_v2/reader_app/app.json | 2 +- patch_v2/reader_app/app/reader.c | 57 ++++++++++++++------------- patch_v2/reader_app/app/shelf.c | 36 +++++++++++++++-- patch_v2/reader_app/app/shelf_model.c | 9 ++++- patch_v2/reader_app/app/shelf_model.h | 3 ++ 5 files changed, 73 insertions(+), 34 deletions(-) diff --git a/patch_v2/reader_app/app.json b/patch_v2/reader_app/app.json index 145389a..ec31343 100644 --- a/patch_v2/reader_app/app.json +++ b/patch_v2/reader_app/app.json @@ -1 +1 @@ -{"name":"Reader","exec":"/usr/data/mnt/sd_0/MeowAPPs/os.meow.reader/reader","package":"os.meow.reader","version":"0.1.2","version_code":3,"framework":1,"fonts":"common","author":"MeowOS","icon":"icon.png","volume_page":true} +{"name":"Reader","exec":"/usr/data/mnt/sd_0/MeowAPPs/os.meow.reader/reader","package":"os.meow.reader","version":"0.1.3","version_code":4,"framework":1,"fonts":"common","author":"MeowOS","icon":"icon.png","volume_page":true} diff --git a/patch_v2/reader_app/app/reader.c b/patch_v2/reader_app/app/reader.c index 137f43b..06fbc2e 100644 --- a/patch_v2/reader_app/app/reader.c +++ b/patch_v2/reader_app/app/reader.c @@ -29,9 +29,10 @@ static int g_reader_active; static meow_widget *g_canvas; static int g_pw, g_ph, g_top, g_mx, g_my; static int g_fs, g_ls; -#define RD_PAGE_CACHE 2 -#define RD_PAGE_SLOT_CUR 0 -static uint16_t *g_page_buf[RD_PAGE_CACHE]; /* [0]=cur [1]=next(FreeType 页位图;B.1 降 3→2 槽,省 1 页 0.69MB) */ +/* M30:只保留当前页位图。原先的 next 预取槽全工程无消费者(翻页走 rd_book_turn + 重渲染 + * 当前页,不换槽),每页白渲染一页(真机 ~220ms)+ 常驻 480×720×2B = 0.69MB。 */ +static uint16_t *g_page_buf; /* 当前页位图(FreeType/图片路径) */ +static int g_page_valid; /* 1=位图内容即当前页(开书 refill 后 on_enter 复用,免二次渲染) */ static int g_has_font; /* 1=FreeType 路径;0=SDK 字库兜底 */ static int g_menu_open; static int g_ui_open; /* 「界面」排版面板(B) */ @@ -791,9 +792,9 @@ static void reader_canvas_render(meow_widget *w, const meow_theme *th, void *ctx chapter_name(); /* 每帧同步顶栏/页底章节名(就绪后取章名,否则文件名) */ if (g_book) { int page = rd_book_current(g_book); - if ((g_has_font || rd_book_is_image(g_book)) && g_page_buf[RD_PAGE_SLOT_CUR]) { + if ((g_has_font || rd_book_is_image(g_book)) && g_page_buf) { /* 翻页 blit:行拷贝当前页位图 → back_buf(局部脏区由 flush_region 上屏) */ - blit_rows(g_page_buf[RD_PAGE_SLOT_CUR], 0, 0, g_ph); + blit_rows(g_page_buf, 0, 0, g_ph); } else { rd_book_render_text(g_book, page, reader_draw_line_atlas, NULL); } @@ -884,37 +885,35 @@ static void reader_redraw(void) static void reader_free_page_bufs(void) { - for (int i = 0; i < RD_PAGE_CACHE; i++) { free(g_page_buf[i]); g_page_buf[i] = NULL; } + free(g_page_buf); g_page_buf = NULL; + g_page_valid = 0; if (g_anim_old) { free(g_anim_old); g_anim_old = NULL; } } -/* 分配 FreeType 页位图槽(cur+next 两槽)。已备当前槽则幂等返回(开书/重入共用)。 +/* 分配当前页位图。已备则幂等返回(开书/重入共用)。 * 动画快照 g_anim_old 不在此常驻:翻页时懒分配、动画完成即释放(见 turn_animated/anim_done)。 * 分配失败 → g_has_font=0,走字库兜底路径。 */ static void reader_alloc_page_bufs(void) { if (!(g_has_font || rd_book_is_image(g_book))) return; - if (g_page_buf[RD_PAGE_SLOT_CUR]) return; /* 已备当前槽(重入)→ 幂等 */ - 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); - if (!g_page_buf[RD_PAGE_SLOT_CUR]) { reader_free_page_bufs(); g_has_font = 0; } + if (g_page_buf) return; /* 已备(重入)→ 幂等 */ + g_page_buf = (uint16_t *)malloc((size_t)g_pw * (size_t)g_ph * 2); + g_page_valid = 0; + if (!g_page_buf) g_has_font = 0; } -/* 渲染当前页 + 下一预取页到 2 槽缓存(B.1:降 3→2,去 prev 槽;动画档 无/滑动/淡入 R1 默认无)。 */ +/* 渲染当前页到位图(M30:不再预渲染下一页——那一槽没有消费者)。 */ static int reader_refill_page(void) { if (!g_book) return -1; - if ((g_has_font || rd_book_is_image(g_book)) && g_page_buf[RD_PAGE_SLOT_CUR]) { + if ((g_has_font || rd_book_is_image(g_book)) && g_page_buf) { 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); - int slots[RD_PAGE_CACHE] = { cur, cur + 1 }; - for (int i = 0; i < RD_PAGE_CACHE; i++) { - int p = slots[i]; - if (p >= 0 && p < np) rd_book_render(g_book, p, g_page_buf[i], g_pw * 2, bg, fg); - else for (int y = 0; y < g_ph; y++) /* 空白槽填背景 */ - for (int x = 0; x < g_pw; x++) g_page_buf[i][(size_t)y * g_pw + x] = bg; - } + if (cur >= 0 && cur < np) rd_book_render(g_book, cur, g_page_buf, g_pw * 2, bg, fg); + else for (int y = 0; y < g_ph; y++) /* 无页可画:填背景 */ + for (int x = 0; x < g_pw; x++) g_page_buf[(size_t)y * g_pw + x] = bg; + g_page_valid = 1; return 0; } return 0; /* 兜底路径:render_text 直绘 */ @@ -924,7 +923,7 @@ static int reader_refill_page(void) static void reader_anim_update(meow_anim *a, float v, void *ctx) { (void)a; (void)ctx; - if (!g_book || !g_anim_old || !g_page_buf[RD_PAGE_SLOT_CUR]) return; + if (!g_book || !g_anim_old || !g_page_buf) return; int off = (int)(v * g_pw); int y0 = rect_y0(), y1 = rect_y1(); meow_ui_fill_rect(0, y0, g_pw, y1 - y0, g_page_bg); /* 内容区清底色 */ @@ -932,7 +931,7 @@ static void reader_anim_update(meow_anim *a, float v, void *ctx) if (g_anim_dir == 1) { old_x = -off; new_x = g_pw - off; } /* 下一页:旧左移/新右入 */ else { old_x = off; new_x = off - g_pw; } /* 上一页:旧右移/新左入 */ blit_rows(g_anim_old, old_x, y0, y1); - blit_rows(g_page_buf[RD_PAGE_SLOT_CUR], new_x, y0, y1); + blit_rows(g_page_buf, new_x, y0, y1); meow_ui_flush_region(0, y0, g_pw, y1 - y0); } static void reader_anim_done(meow_anim *a, int canceled, void *ctx) @@ -948,7 +947,7 @@ static void reader_turn_animated(int dir) if (!g_book) return; if (g_anim) { meow_anim_cancel(g_anim); g_anim = NULL; } /* 取消进行中动画,重开 */ /* 无页位图(atlas 兜底):瞬切 */ - if (!(g_has_font || rd_book_is_image(g_book)) || !g_page_buf[RD_PAGE_SLOT_CUR]) { + if (!(g_has_font || rd_book_is_image(g_book)) || !g_page_buf) { rd_book_turn(g_book, dir); g_prog_dirty = 1; reader_refill_page(); reader_redraw(); reader_reflow_persist(); @@ -956,7 +955,7 @@ static void reader_turn_animated(int dir) } if (!g_anim_old) g_anim_old = (uint16_t *)malloc((size_t)g_pw * g_ph * 2); if (!g_anim_old) { rd_book_turn(g_book, dir); g_prog_dirty = 1; reader_refill_page(); reader_redraw(); return; } - memcpy(g_anim_old, g_page_buf[RD_PAGE_SLOT_CUR], (size_t)g_pw * g_ph * 2); + memcpy(g_anim_old, g_page_buf, (size_t)g_pw * g_ph * 2); rd_book_turn(g_book, dir); g_prog_dirty = 1; reader_refill_page(); @@ -1005,7 +1004,7 @@ static void reader_scrub_preview(int page) meow_ui_text_right(g_pw - 16, fy + 1, ft, 16, g_page_fg); } /* 右缘条区清残留(恢复页面内容)再画条 + 预览框;无页位图(字库兜底)则跳过恢复 */ - if (g_page_buf[RD_PAGE_SLOT_CUR]) blit_sub(g_page_buf[RD_PAGE_SLOT_CUR], g_pw - 120, y0, 120, y1 - y0); + if (g_page_buf) blit_sub(g_page_buf, g_pw - 120, y0, 120, y1 - y0); reader_draw_scrub(th); if (g_show_footer) meow_ui_flush_region(0, g_ph - RD_FOOT_H, g_pw, RD_FOOT_H); meow_ui_flush_region(g_pw - 120, y0, 120, y1 - y0); @@ -1092,7 +1091,7 @@ int reader_open(const char *path) if (g_has_font || rd_book_is_image(g_book)) { reader_free_page_bufs(); /* 丢弃上一本书的页位图(重开/换书) */ - reader_alloc_page_bufs(); /* 重建 cur+next 两槽;g_anim_old 翻页时懒分配 */ + reader_alloc_page_bufs(); /* 重建当前页位图;g_anim_old 翻页时懒分配 */ } /* 复位动画/刮擦/覆盖层状态(跨书) */ @@ -1150,7 +1149,9 @@ static void reader_on_enter(meow_view *v, void *ctx) g_reader_active = 1; if (g_book) { reader_alloc_page_bufs(); /* 离开已释放页位图 → 重建;未释放则幂等返回 */ - reader_refill_page(); + /* 开书路径 reader_open 已渲染好当前页(g_page_valid=1)→ 这里不再重复渲染; + * 从其它页返回(leave 释放过位图)才 refill。 */ + if (!g_page_valid) reader_refill_page(); chapter_name(); } reader_redraw(); @@ -1159,7 +1160,7 @@ static void reader_on_leave(meow_view *v, void *ctx) { (void)v; (void)ctx; reader_reflow_persist(); /* 离开保存进度 */ - reader_free_page_bufs(); /* 离开阅读页释放页位图(cur+next 两槽+动画快照)→ on_enter 重建 */ + reader_free_page_bufs(); /* 离开阅读页释放页位图(当前页 + 动画快照)→ on_enter 重建 */ g_reader_active = 0; } static void reader_on_destroy(meow_view *v, void *ctx) diff --git a/patch_v2/reader_app/app/shelf.c b/patch_v2/reader_app/app/shelf.c index 50a72bc..5bc65db 100644 --- a/patch_v2/reader_app/app/shelf.c +++ b/patch_v2/reader_app/app/shelf.c @@ -17,8 +17,9 @@ * M29 性能(用户反馈「几百本书首页卡得飞起」): * - **扫描结果进程内缓存**:只在本进程首次进入书架时扫 Books/;从阅读页返回不再重扫 * (真机 exFAT 全量扫描 3.1s,是「回书架冻结」的唯一大头)。顶栏「刷新」强制重扫。 - * 行为变化:新放进 Books/ 的书、以及刚读完那本书的「最近阅读置顶」需点一次「刷新」才更新。 + * 行为变化:新放进 Books/ 的书需点一次「刷新」才出现。 * - **行数多时关圆角容器**(见 SHELF_ROUND_MAX_ROWS):圆角卡逐像素填充,595 本时滑一步 46ms。 + * M30:M29 的「刚读完不置顶」缺口补上(只刷一本书的 ts + 内存重排,不重扫全盘)。 */ #include "../reader_internal.h" #include "progress.h" @@ -39,6 +40,8 @@ static shelf_book g_books[SHELF_MAX_BOOKS]; static int g_nbooks; static int g_scanned; /* 1=本进程已扫过 Books/(进书架缓存;「刷新」动作强制重扫) */ static int g_active; +/* M30:刚打开过的书(返回书架时只刷这一本的 ts 并重排,避免为「最近阅读置顶」重扫全盘)。 */ +static char g_just_read[400]; static meow_widget *g_list; /* listview(布局=list) */ static meow_lazygrid *g_grid; /* lazygrid(布局=grid) */ static meow_widget *g_appbar; /* meow_topappbar */ @@ -111,6 +114,23 @@ static void build_rows(void) g_nrows = shelf_build_rows(g_books, g_nbooks, g_group_folder, g_rows, SHELF_MAX_ROWS); } +/* M30:从阅读页返回时把「刚读完」置顶。只读这一本的进度文件 + 重排内存里的 g_books, + * 不 opendir/不遍历目录(对比全量重扫真机 3s)。重排后 on_enter 照常 build_rows + 重建控件。 */ +static void promote_just_read(void) +{ + if (!g_just_read[0]) return; + for (int i = 0; i < g_nbooks; i++) { + if (strcmp(g_books[i].path, g_just_read) != 0) continue; + uint64_t ts = shelf_ts_of(g_books[i].path); + if (ts > g_books[i].ts) { + g_books[i].ts = ts; + shelf_sort_recent(g_books, g_nbooks); + } + break; + } + g_just_read[0] = 0; +} + /* ---- list 数据源(vsource + 段头;container on) ---- */ static int shelf_list_count(void *ctx) { (void)ctx; return g_nrows; } static void shelf_list_bind(int idx, meow_lv_item *item, void *ctx) @@ -147,7 +167,10 @@ static void shelf_list_tapcb(int idx, int op, void *ctx) if (bi < 0 || bi >= g_nbooks) return; /* 点书开读:reader_open 打开书(含页位图)→ push ACT_READER 让阅读页成为 view activity, * 驱动 reader_on_enter/on_leave(重建/释放页位图);「返回书架」/左缘返回走 page_pop 回书架。 */ - if (reader_open(g_books[bi].path) == 0) meow_view_page_push(ACT_READER); + if (reader_open(g_books[bi].path) == 0) { + snprintf(g_just_read, sizeof g_just_read, "%s", g_books[bi].path); /* 返回时置顶用 */ + meow_view_page_push(ACT_READER); + } } /* ---- grid 数据源(meow_lazygrid,4 列封面卡) ---- */ @@ -204,7 +227,10 @@ static void shelf_grid_tapcb(int idx, void *ctx) { (void)ctx; if (idx < 0 || idx >= g_nbooks) return; - if (reader_open(g_books[idx].path) == 0) meow_view_page_push(ACT_READER); + if (reader_open(g_books[idx].path) == 0) { + snprintf(g_just_read, sizeof g_just_read, "%s", g_books[idx].path); /* 返回时置顶用 */ + meow_view_page_push(ACT_READER); + } } /* ---- 控件装配(按当前布局重建 list / grid) ---- */ @@ -274,9 +300,11 @@ static void shelf_on_enter(meow_view *v, void *ctx) g_group_folder = (c && strcmp(c->shelf_group, "folder") == 0) ? 1 : 0; /* 书架缓存:本进程只在首次进入时扫一次 Books/(从阅读页返回不再重扫)。 * 扫描在真机是 3.1s(exFAT 434 目录 opendir/readdir),是「回书架冻结」的唯一大头。 - * 代价:新放入的书 / 刚读完的书不会自动置顶,需点顶栏「刷新」(强制重扫)。 + * 代价:新放入的书不会自动出现,需点顶栏「刷新」(强制重扫)。 + * M30:刚读完那本例外——promote_just_read() 只读它一个进度文件并重排,低成本置顶。 * 提示条(超上限截断 / 超深目录)语义不变:首次扫描时算好并保留在 g_notice*。 */ if (!g_scanned) scan_books(); + else promote_just_read(); build_rows(); const meow_theme *th = meow_theme_get(); g_bg = th->bg; g_fg = th->fg; g_dim = th->dim; diff --git a/patch_v2/reader_app/app/shelf_model.c b/patch_v2/reader_app/app/shelf_model.c index 93b3ab7..a33a993 100644 --- a/patch_v2/reader_app/app/shelf_model.c +++ b/patch_v2/reader_app/app/shelf_model.c @@ -62,6 +62,13 @@ static int cmp_recent_then_name(const void *a, const void *b) return strcasecmp(x->title, y->title); } +/* 最近阅读排序(扫描后 / 刚读完置顶共用同一口径)。 */ +void shelf_sort_recent(shelf_book *books, int nbooks) +{ + if (!books || nbooks < 2) return; + qsort(books, (size_t)nbooks, sizeof(shelf_book), cmp_recent_then_name); +} + typedef struct scan_ctx { shelf_book *books; int cap; @@ -177,7 +184,7 @@ int shelf_scan_books(const char *root, shelf_book *books, int cap, scan_ctx c; c.books = books; c.cap = cap; c.n = 0; c.truncated = 0; c.depth_skipped = 0; c.ts_fn = ts_fn; scan_dir(&c, root, 0, "", ""); - qsort(books, (size_t)c.n, sizeof(shelf_book), cmp_recent_then_name); + shelf_sort_recent(books, c.n); if (rep) { rep->nbooks = c.n; rep->truncated = c.truncated; diff --git a/patch_v2/reader_app/app/shelf_model.h b/patch_v2/reader_app/app/shelf_model.h index 0d8bf35..029a0fd 100644 --- a/patch_v2/reader_app/app/shelf_model.h +++ b/patch_v2/reader_app/app/shelf_model.h @@ -51,6 +51,9 @@ typedef struct shelf_row { /* 进度时间戳查询(生产=rdprog_load;测试/基线=NULL,全部 ts=0)。 */ typedef uint64_t (*shelf_ts_fn)(const char *path); +/* 最近阅读排序:ts 降序,同 ts 按书名(不区分大小写)。扫描后与「刚读完置顶」共用。 */ +void shelf_sort_recent(shelf_book *books, int nbooks); + typedef struct shelf_scan_report { int nbooks; int truncated; /* 1=有书因 SHELF_MAX_BOOKS 上限被丢弃 */