Files
MeowOS-SDK/tuku/logic/reader/nav.c
T
AskaEth 23c9db73df feat(tuku): 阅读器左右翻页核心——页源契约/预解码窗口/跟手翻页/方向感知 tap
- 页源契约(logic/reader):count/meta/open/close + 目录册(文件夹即书)为第一个实现;
  meta 由纯 C 图片头探测(PNG IHDR/JPEG SOF/BMP)提供原始尺寸,条漫可先排版再取图,
  zip 册(M9/M11)只需另写 mr_book_ops,两种消费方式零改动复用
- 翻页引擎:pthread 后台解码窗口=当前±1 页(目录册 downscale 到屏幕长边),翻页只 blit
  缓存页图 + meow_anim 150ms 滑动吸附;未命中立即切页显示加载态、不在翻页路径解码;
  只 flush 内容区;decode/flip 首帧/翻页完成/进出内存全部 meow_log 打点
- 阅读页 UI:fit 三档(适宽/适高/原始,TopAppBar 动作循环,标题显示页码 x/y)+
  双击以点击点为锚放大(临时缓冲超上限自动降倍率)+ 单指拖拽/滑动步进平移;
  tap 三分区方向感知(RTL 默认:左=下一页,LTR 反之),中区切 chrome;
  左缘返回交给 app.c、屏底热区交给 SDK 回桌面,两者都不拦截
- 跟手:app.c 仅补 2 处极小路由(ACT_READER 的 down/move),reader 自行算位移;
  屏底 28px/左缘 24px 不进拖动,竖向主导放弃横滑
- 内存纪律:驻留 ≤3 张 downscale 页图;放大时窗口收缩到当前页;on_leave 停解码线程、
  释放全部页图/册索引/TopAppBar,并记录 VmRSS/MemAvailable 前后对比
- 测试/构建:新增 test_reader_core(契约边界/空册/越界/重复 open-close/首末页/头探测/
  方向语义/fit/平移夹取,108 项);run_tests.sh 与 build_tuku.sh 源列表接线;
  宿主 46+108 全 PASS,交叉构建静态 MIPS32r2 ELF、-Wall -Wextra 零警告
2026-11-04 20:58:05 +08:00

121 lines
3.1 KiB
C

/* logic/reader/nav.c — 阅读导航纯逻辑实现(见 nav.h 的方向语义说明)。 */
#include "logic/reader/nav.h"
int mr_nav_tap_zone(int x, int view_w)
{
if (view_w <= 0) return MR_ZONE_MIDDLE;
if (x < view_w / 3) return MR_ZONE_LEFT;
if (x >= (view_w * 2) / 3) return MR_ZONE_RIGHT;
return MR_ZONE_MIDDLE;
}
int mr_nav_tap_delta(int rtl, int zone)
{
if (zone == MR_ZONE_MIDDLE) return 0;
if (rtl) return (zone == MR_ZONE_LEFT) ? 1 : -1; /* RTL:左进右退 */
return (zone == MR_ZONE_LEFT) ? -1 : 1; /* LTR:右进左退 */
}
int mr_nav_swipe_delta(int rtl, int dx)
{
if (dx == 0) return 0;
if (rtl) return dx > 0 ? 1 : -1;
return dx > 0 ? -1 : 1;
}
int mr_nav_clamp_page(int page, int count)
{
if (count <= 0) return -1;
if (page < 0) return 0;
if (page >= count) return count - 1;
return page;
}
int mr_nav_commit_dist(int view_w)
{
int t = view_w / 4;
if (t < 60) t = 60;
if (t > 160) t = 160;
return t;
}
int mr_nav_commit(int dx, int view_w)
{
int adx = dx < 0 ? -dx : dx;
return adx >= mr_nav_commit_dist(view_w);
}
int mr_nav_fit_size(int fit, int src_w, int src_h, int vw, int vh,
int *out_w, int *out_h)
{
if (!out_w || !out_h || src_w <= 0 || src_h <= 0 || vw <= 0 || vh <= 0) return -1;
long long w, h;
if (fit == MR_FIT_ORIGINAL) {
w = src_w;
h = src_h;
} else if (fit == MR_FIT_HEIGHT) {
h = vh;
w = (long long)src_w * vh / src_h;
} else {
w = vw;
h = (long long)src_h * vw / src_w;
}
if (w < 1) w = 1;
if (h < 1) h = 1;
*out_w = (int)w;
*out_h = (int)h;
return 0;
}
int mr_nav_zoom_size(int w, int h, int zoom_pct, int *out_w, int *out_h)
{
if (!out_w || !out_h || w <= 0 || h <= 0 || zoom_pct <= 0) return -1;
long long ww = (long long)w * zoom_pct / 100;
long long hh = (long long)h * zoom_pct / 100;
if (ww < 1) ww = 1;
if (hh < 1) hh = 1;
*out_w = (int)ww;
*out_h = (int)hh;
return 0;
}
void mr_nav_pan_limit(int content_w, int content_h, int vw, int vh,
int *lim_x, int *lim_y)
{
int lx = content_w - vw;
int ly = content_h - vh;
if (lx < 0) lx = 0;
if (ly < 0) ly = 0;
/* 平移量是「中心偏移」:可左右各移动溢出量的一半,两边才都看得到。 */
if (lim_x) *lim_x = lx / 2;
if (lim_y) *lim_y = ly / 2;
}
void mr_nav_pan_clamp(int content_w, int content_h, int vw, int vh,
int *pan_x, int *pan_y)
{
int lx, ly;
mr_nav_pan_limit(content_w, content_h, vw, vh, &lx, &ly);
if (pan_x) {
if (*pan_x > lx) *pan_x = lx;
if (*pan_x < -lx) *pan_x = -lx;
}
if (pan_y) {
if (*pan_y > ly) *pan_y = ly;
if (*pan_y < -ly) *pan_y = -ly;
}
}
int mr_nav_slide_from(int rtl, int delta, int view_w)
{
if (!delta) return 0;
int from = rtl ? -delta : delta;
return from * view_w;
}
int mr_nav_fit_next(int fit)
{
if (fit < 0 || fit >= MR_FIT_COUNT) return MR_FIT_WIDTH;
return (fit + 1) % MR_FIT_COUNT;
}