Skip to content

ADR 0002 — Decouple Reader Semantics, Image Resources, and Rendering Backends(阅读器语义、图像资源与渲染后端解耦) ​

  • 状态:Accepted
  • 日期:2026-09-16
  • 实施状态:
    • WebGPU 分支隔离:已完成(feat/webgpu-reader,作为 UPSTREAM-TRACKED 资产)
    • Phase 0A 渲染器基线(Renderer Baseline):已完成(Completed,固定夹具真机实测数据见下,Compose Scene 决胜成立)
    • Phase 0B 生产阅读器基准(Production Reader Benchmark):已完成(Completed,真实本地解码与两级资源状态机真机实测数据见下,ComposeScene 优势确立)
    • PoC A (ReaderScene 几何抽象):已完成(Completed)
    • PoC B (Webtoon 视口实验:Compose Scene vs. View Scene):已完成(Completed,根据决胜原则选定 ComposeSceneRenderer)
    • Phase 1 (图像解码决策与切片瓦片引擎):
      • Phase 1A(纯 Kotlin 解码决策智能):已完成(Completed)
      • Phase 1B(图源与切片运行时):已完成(Completed)
      • Phase 1C(渲染器集成与管线适配):已完成(Completed,Compose 瓦片无重组刷新与无缝拓扑渲染闭环)
      • Phase 1D(真机超长条漫效能与长图画质基准):已完成(Completed)
    • Phase 2 (生产化功能对齐与多章节视口锚定):
      • Phase 2A (页面间隔、跨章节平滑扩缩窗与无感锚定、统一错误重试交互、低内存优化):已完成(Completed)
      • Phase 2B (2D 契约抽象、双向锚定补偿与默认全量推广):已完成(Completed)
      • Phase 2C (横向连续瀑布流场景与对称性定理:HorizontalReaderScene):已完成(Completed)
      • Phase 2C2 (横向连续场景 Compose 宿主交互:ComposeSceneHorizontalReader):已完成(Completed)
    • Phase 3 (分页与双页场景引擎与统一宿主):
      • Phase 3A (纯 Kotlin 分页几何模型与排版调度:PagedReaderScene、PagedSpreadResolver、PagedSnapResolver):已完成(Completed)
      • Phase 3B (分页影子模式校验器与等价性测试:PagedShadowValidator、PagedShadowParityTest 与运行时采样):已完成(Completed)
      • Phase 3C (现代分页场景 Compose 宿主:ComposeScenePagedReader,单双页统一架构、弹性吸附、缩放与 Draw Phase 呈现):已完成(Completed)
      • Phase 3D (生产路由接线、交互测试套件验证与 ADR 归档):已完成(Completed)
      • Phase 3E (图像呈现 Parity 补齐与生产门控解耦:SceneImagePresentationCoordinator、可见切片请求、多 LOD 缩放、动图生命周期与独立安全门控):已完成(Completed)
    • Scene Reader 功能集成:已全量覆盖 Webtoon 纵向瀑布流、横向连续流(LTR/RTL)、单页离散分页(LTR/RTL/Vertical)与双页并页模式,纯 Draw Phase 渲染、可见切片多 LOD 驱动与零跳动锚定全部落地
    • 翻页动画样式(ReaderAnimation):已完成 SLIDE / COVER / CURL 三样式在场景分页宿主的 Draw Phase 实现,并以 resolveComposeReaderPageTransform 为 oracle 做同输入同输出 parity(见 docs/architecture/reader-scene-closure-plan-2026-09.md)
  • 方向与门控的边界声明(ReaderMode 属 legacy 投影):ReaderMode 同时编码"布局模式"与"阅读方向" (STANDARD / REVERSED 同属分页布局),这是 legacy 偏好模型的遗留耦合。Scene core 不得继承该耦合: ReaderCore 只接收 SceneReadingDirection 参数,模式到方向的折算集中在配置层 (reader/ui/config/SceneReadingDirectionResolver.kt)。因此连续横向的 RTL 不是新增一个 ReaderMode 枚举值, 而是独立偏好 isContinuousHorizontalReversed(AppSettings.KEY_READER_CONTINUOUS_HORIZONTAL_REVERSED), 并在阅读选项面板中仅当该模式被选中时显示。若将来整理 settings schema 为 LayoutMode × ReadingDirection, 迁移点只有这一个解析函数。
  • 关联分支:feat/webgpu-reader(WebGPU 成果隔离保存与上游追踪)、devel(基线主干)
  • 核心准则:ReaderCore owns semantics; ImagePipeline owns image policy; Renderer owns presentation.(ReaderCore 掌管阅读语义;ImagePipeline 掌管图像策略;Renderer 掌管呈现)

一、 背景与动因 ​

Kototoro 当前的漫画阅读器主要基于 Jetpack Compose 与 Telephoto(Zoomable/Subsampling)构建。近期在探索引入 Mihon 最新 WebGPU 渲染器(ca.mpreg:webgpuviewer)的过程中,团队深入触及了移动端漫画阅读器的深水区瓶颈与架构矛盾:

  1. WebGPU Continuous 的成熟化成本与上游状态:
    • 上游 Mihon 的 High Quality Renderer(WebGPU)已覆盖分页与 Continuous 阅读模式,并提供更直接的 GPU 渲染路径与可编程着色器(3D 翻页动效、Catmull-Rom 缩放、LUT 色彩滤镜)。
    • 但截至 2026-09-16,其 Continuous viewer 仍存在惯性滚动微弱(Mihon #3780)与快速导航闪烁(Mihon #3779)等公开问题,且上游对超长条漫与 GPU 纹理管理的迭代仍非常频繁。
    • 因此,Kototoro 暂不承担在本地自行推进 Continuous WebGPU 成熟化的巨大成本,而是采取跟踪上游策略;在 Webtoon 场景下暂保持回退,待上游稳定后再行评估。
  2. 移动端驱动与合成层兼容代价:
    • 依赖 NDK/Dawn/Vulkan 的原生阅读视口在 Android 碎片化设备(Adreno/Mali 早期驱动、MIUI/HyperOS、Android 14 SurfaceControl 打洞与合成)上面临冷启动白屏、看门狗误判、生命周期回收等极其繁重的系统级兼容负担。
  3. 通用 UI 列表虚拟化与二维漫画视口的抽象差异:
    • Webtoon 本质是连续二维页面场景,而 LazyColumn 是通用的惰性列表抽象。虽然 LazyColumn 已对可见项进行虚拟化,并不会为整个章节建立完整 UI 树,但其 item composition、measurement、placement、prefetch 和逐项图片/缩放生命周期仍属于通用 UI 列表模型。
    • 对于只需要“视口与页面矩形求交 + 图像绘制”的漫画阅读场景,专用二维场景(2D Scene)存在进一步减少框架工作量、提高资源生命周期确定性的空间。
    • 巨幅图片(如 4000×20000 像素的条漫单页)若无视口自适应分级(LOD)与按需分块解码(Tiling),整张解码将造成数百兆显存突增、频繁 GC 卡顿甚至 OOM。
  4. 后端与业务状态紧耦合:
    • 既有实现中,页码进度、手势判定、页面排布与具体 UI 组件/渲染器深度绑定。若每引入一个新渲染后端(如 Compose、WebGPU、Canvas)都需要重新实现一套完整的状态与图片加载逻辑,系统维护成本将不可持续。

因此,亟需确立一套高内聚、低耦合、渲染后端可插拔的现代漫画阅读器核心架构。


二、 核心决策:三层主权模型(Three-Tier Sovereignty) ​

阅读器体系严格划分为三层,各层拥有独立主权,严禁越界:

text
┌──────────────────────────────────────────────────────────────────┐
│                      Kototoro Reader UI                          │
│        (Compose Chrome: TopBar, BottomBar, Sheets, OCR UI)       │
└────────────────────────────────┬─────────────────────────────────┘
                                 │ 低频语义通知 (PageChanged, Zoom)
                                 ▼
┌──────────────────────────────────────────────────────────────────┐
│ 1. Reader Semantics 主权 (ReaderCore)                           │
│ ────────────────────────────────────────────────────────────     │
│   ReaderScene / ReaderLayout / ReaderViewport / ReaderCamera     │
│   ReaderProgressResolver / VisibleRegionResolver                 │
│                                                                  │
│   输出纯几何/状态帧: ReaderFrame(viewport, List<VisibleNode>)     │
└────────────────────────────────┬─────────────────────────────────┘
                                 │ 预测请求 (PrefetchRequest) & 视口帧
                                 ▼
┌──────────────────────────────────────────────────────────────────┐
│ 2. Image Resource 主权 (ImagePipeline)                          │
│ ────────────────────────────────────────────────────────────     │
│   DecodePlanner / LodPolicy / TileManager / ResourceStore        │
│   资源预算 (Memory/Disk Cache) & 解码调度                         │
│                                                                  │
│   输出多态资源: ReaderImageAsset (BitmapAsset | TileSet | Encoded) │
└────────────────────────────────┬─────────────────────────────────┘
                                 │ 针对各后端特化的呈现资产
                                 ▼
┌──────────────────────────────────────────────────────────────────┐
│ 3. Rendering 主权 (ReaderRenderer)                              │
│ ────────────────────────────────────────────────────────────     │
│   ┌──────────────────────────┬────────────────┬──────────────┐   │
│   │   ComposeSceneRenderer   │ ViewScene      │    WebGPU    │   │
│   │   [PRIMARY CANDIDATE]    │ [CONTROL]      │[UPSTREAM-TRK]│   │
│   │ DrawModifierNode/Graphics│ Custom View    │ Mihon 翻页/  │   │
│   │ Layer (跳过 Composition) │ onDraw(Canvas) │ 着色器特效   │   │
│   └──────────────────────────┴────────────────┴──────────────┘   │
└──────────────────────────────────────────────────────────────────┘

1. 第一层:Reader Semantics 主权 (ReaderCore) ​

  • 职责:维护二维漫画虚拟世界坐标系,负责页面几何排列、视口相交计算、阅读进度结算与运动预测。
  • 输出:纯几何与语义帧 ReaderFrame,告知下游“当前视口内应当在哪些矩形位置绘制哪些页面的哪些区域”。
  • 绝对禁忌:绝不知晓图片文件的下载、解码、路径与失败状态。

2. 第二层:Image Resource 主权 (ImagePipeline) ​

  • 职责:决定“如何准备像素”。根据视口物理尺寸、设备 RAM 等级、缩放倍率计算动态 LOD 采样率,管理分块切片(Tiling)、执行预取请求及资源调度队列与内存缓存。
  • 输出:多态呈现资产 ReaderImageAsset。
  • 绝对禁忌:不持有任何 Android UI 组件或阅读器章节/进度状态。

3. 第三层:Rendering 主权 (ReaderRenderer) ​

  • 职责:决定“如何把像素画在屏幕上”,掌管具体的视觉呈现。
  • 战略重心更新:
    • 首选主攻候选:ComposeSceneRenderer。利用 Compose 现代高性能特性(DrawModifierNode、GraphicsLayer 保留显示列表、Draw phase 状态读取),将高频变换限制在 Draw 阶段,彻底跳过 Composition 与 Layout。
    • 对照参考候选:AndroidViewSceneRenderer。作为标准单 View Canvas.onDraw 的性能基准对照组。
    • 决策决胜原则(Tie-Breaker):若 Compose Scene 与 View Scene 在关键帧指标与内存上无显著质差,坚决优先采纳 Compose Scene,以消除 AndroidView 互操作、生命周期桥接、手势穿透与合成层黑盒问题。
  • 绝对禁忌:不得成为阅读进度、页面布局和章节语义的 Source of Truth。

三、 关键架构约束 ​

为确保长期演进不发生设计漂移,确立以下 4 项刚性约束:

约束 1:ReaderCore 绝不知晓图片加载状态(几何稳定性与锚定修正) ​

  • ReaderCore 仅消费 PageGeometryHint 构建连续虚拟几何体,不感知几何尺寸来自于网络 Header、本地元数据、历史缓存还是算法估算:
    kotlin
    sealed interface PageGeometryHint {
        data class Exact(val width: Int, val height: Int) : PageGeometryHint
        data class AspectRatio(val ratio: Float) : PageGeometryHint
        data class Estimated(val ratio: Float) : PageGeometryHint
    }
  • 图片下载中、等待解码或解码失败,Scene 几何排版骨架完全独立;若资源不可用,Renderer 仅负责在对应几何区域绘制占位块。
  • 锚定修正(Anchored Correction):当估算尺寸(Estimated)被精确尺寸(Exact)修正引起总高度变化时,通过 Viewport Anchor Compensation 保持当前主要可见页面内的绝对像素偏移,避免长图展开时按比例把读者推过新出现的内容。屏幕旋转等主动 relayout 才可以选择归一化语义锚点。

约束 2:ImagePipeline 职责细分,运动遥测与 ARR 协作 ​

  • 严禁将 ImagePipeline 做成巨型上帝对象。内部明确拆分为:
    • ImageSource:原始字节流与资源句柄;
    • DecodePlanner:按像素用途(PixelUsage: DISPLAY_ONLY | CPU_READ_REQUIRED | REGION_TILE)精细化解码规格;
    • ResourceStore:缓存生命周期;
    • DecodeExecutor:执行解码产出目标资产。
  • 运动遥测(ViewportMotion)的双重用途:
    kotlin
    data class ViewportMotion(
        val velocityX: Float,
        val velocityY: Float,
        val isDragging: Boolean,
        val timestampNanos: Long,
    )
    数据流分支为:
    1. ViewportMotion -> ReaderCore/Prediction -> PrefetchRequest -> ImagePipeline(驱动预测预加载);
    2. ViewportMotion -> PlatformPresentationAdapter(协作 Android 15/16 自适应刷新率 ARR:Compose 映射至 preferredFrameRate,View 映射至 frameContentVelocity),在高速滑动时拉升 120Hz,静止时降频降功耗。

约束 3:Renderer 拥有“呈现状态”,而非“语义状态” ​

  • Renderer 允许且应当拥有后端特有的绘制与保留资源状态(如 Compose 的 GraphicsLayer、DrawModifierNode,Canvas 的 Paint、Matrix,WebGPU 的 TextureCache)。
  • 高频 Camera 变换(位移/缩放)严格约束在 Draw Phase(或 GraphicsLayer.matrix)消费,严禁逆向触发父级重组或布局。
  • 严禁 Renderer 内部私自维护 currentChapter、currentPageProgress、isDoublePage 等阅读语义字段。

约束 4:多态呈现资产(句柄化),拒绝为统一而二次拷贝 ​

  • ReaderImageAsset 采用密封接口(Sealed Interface),描述资源的访问句柄而非强行具象化到堆内存:
    kotlin
    sealed interface ReaderImageAsset {
        data class EncodedAsset(
            val source: ImageSourceHandle, // FileSource, BufferSource, StreamSource
            val metadata: ImageMetadata,
        ) : ReaderImageAsset
    
        data class BitmapAsset(
            val bitmap: Bitmap,
            val level: Int,
            val sourceRegion: IntRect?,
        ) : ReaderImageAsset
    
        data class TileSet(
            val overview: Bitmap?,
            val tiles: TileProvider,
        ) : ReaderImageAsset
    }
  • 各 Renderer 可向 Pipeline 声明其 preferredRepresentation(如 WebGPU 声明 ENCODED,Canvas/Compose 声明 BITMAP 或 TILE_SET)。严禁将 EncodedAsset 设计为单纯的 ByteArray,以避免大图在 Java Heap 与 JNI 之间发生数十兆的冗余拷贝。

四、 代码组织规划(Package 先行,暂缓拆 Module) ​

初期避免 Gradle 模块爆炸,直接在 app/src/main/kotlin/org/skepsun/kototoro/reader/ 下建立清晰的目录隔离:

text
reader/
├── core/                  // 纯 Kotlin,零 Android 依赖(禁用 android.graphics.*、androidx.compose.* 等几何与 UI 类)
│   ├── FloatRect.kt       // 跨平台自包含几何基础类型
│   ├── IntSize.kt
│   ├── PageId.kt
│   ├── PageGeometryHint.kt
│   ├── ReaderViewport.kt
│   ├── ReaderScene.kt
│   ├── VisibleNode.kt
│   ├── ReaderFrame.kt
│   ├── ViewportMotion.kt
│   ├── VisibleRegionResolver.kt
│   └── ReaderProgressSnapshot.kt
│
├── image/                 // 图像资源决策与流水线
│   ├── ReaderImageAsset.kt
│   ├── ReaderImagePipeline.kt
│   ├── ReaderLodPolicy.kt
│   ├── ReaderTileManager.kt
│   ├── PixelUsage.kt
│   └── DecodePlanner.kt
│
├── render/                // 可插拔渲染器实现
│   ├── ReaderRenderer.kt
│   ├── compose/           // Telephoto 现有实现 + 新 ComposeSceneRenderer (DrawModifierNode/GraphicsLayer)
│   ├── canvas/            // 对照组 AndroidViewSceneRenderer (View onDraw)
│   └── webgpu/            // 隔离的 WebGPU 适配器
│
└── ui/                    // Activity, ViewModel, Chrome, Menu, Settings

测试原则:core/ 下的几何相交计算(VisibleRegionResolver)、锚定修正(AnchoredCorrection)、页面排布与进度结算必须具备高分支覆盖度与边界值单元测试,不追求全包盲目的 100% 形式主义覆盖率。


五、 验证路线图与评判准则 ​

阶段规划 ​

  1. 分支隔离(已完成):
    • 将现有 13 个 WebGPU 提交完整封存在 feat/webgpu-reader 分支,状态标记为 UPSTREAM-TRACKED。
    • devel 恢复纯净,消除 NDK 依赖与冷启动监控对主干的干扰。
  2. Phase 0:多维基准建立(Benchmark Baseline):
    • Phase 0A:渲染器基线(Renderer Baseline,已完成):
      • 在 ARR / 120Hz 物理测试机上运行固定渲染夹具(100 页条漫,隔离 UI/Layout/Renderer 抽象开销);
      • 依据真机实测数据执行 Tie-Breaker 决策:ComposeSceneRenderer P99 Overrun 达到 -5.2ms,彻底消除通用列表掉帧,胜出 View 对照组。
    • Phase 0B:生产阅读器基准(Production Reader Benchmark,已完成):
      • 升级至完整生产链路:包含 72 页真实局部确定性图片集、真实 Coil 解码与页面变换(PageTransformation)、双向资源状态机(PRESENTATION_READY ↔ SOURCE_READY 降级与驱逐)及 120Hz 刷新率意图协同;
      • 执行两套模式:
        • Primary Mode:CompilationMode.Partial(BaselineProfileMode.UseIfAvailable),拟合生产安装与 Baseline Profile 场景;
        • Diagnostic Mode:CompilationMode.Full,消除 JIT 噪音,观察纯理论上限;
      • 涵盖突发与持续多轮遍历测试:
        • Burst Benchmark(24 次连续大跨度 Fling):考察 P99 逾期、Frame CPU 耗时、显存峰值与 Presentation 资产界限;
        • Sustained Benchmark(2 轮双向 72 页全章节往复遍历,1000+ 帧):记录平稳态内存(MemoryUsageMetric.Last)、Native RSS Anon 衰减与资源双向降级回收行为。
  3. PoC A:Scene 几何抽象:
    • 实现纯几何的 ReaderScene,让现有阅读器与新 Scene 并行计算,验证页面几何、可见集合及阅读语义与现有行为等价(浮点几何允许定义明确的 epsilon 容差;若发现旧实现缺陷,以显式行为变更记录处理而非机械迁就旧 bug)。
  4. PoC B:Webtoon 视口实验(战略 A/B 对照):
    • 保持功能极简(仅垂直滚动、无缩放、无 OCR、固定图集),构建两个极小渲染器:
      • Candidate A(首选主力):ComposeSceneRenderer
        • 测试 Immediate(DrawModifierNode / drawBehind)与 Retained(单页独立 GraphicsLayer)两种机制;
      • Candidate B(参考对照):AndroidViewSceneRenderer(单 View onDraw(Canvas));
    • 归因分析:验证性能红利是否本质源自“脱离通用 LazyColumn 抽象与建立专属 2D Scene”,而非“必须退回 Android View”。

评估指标与 Go/No-Go 准则 ​

评判标准相对于 Phase 0 建立的 Baseline 定义,统一纳入刷新率意图(preferredFrameRate 协同):

评估维度核心主指标 (Primary)辅助指标 (Secondary)达到标准(GO)放弃或退回(NO-GO)
帧稳定性frameOverrunMs P95/P99frameDurationCpuMs P95/P99尾部逾期时间(P99 Overrun)显著缩短,消除极端掉帧尖峰Overrun 无明显改善,依然频发错过 Hardware Deadline
卡顿率Deadline Miss RateWorst-frame Trace严重丢帧率明显下降掉帧率持平或恶化
Java 内存Heap MaxAllocations / GC CountFling 期间垃圾回收暂停次数与瞬时分配量趋近于零内存抖动未减,GC 依然打断渲染管线
Native 内存RSS Anon MaxRSS Shmem内存峰值有界平稳存在非托管内存堆积或泄漏风险
GPU 显存memoryGpuKb MaxTexture Upload Trace纹理显存平稳有界,无主线程 Stall突发显存占用过大导致 OOM
功耗与持续性Energy / Battery DrainThermal / Headroom Drift长时间滚动能耗平稳,无剧烈热降频持续功耗异常飙升,迅速触发系统温控降频
系统复杂度代码增量 (LOC) / 异常率OEM-specific Workarounds逻辑清晰,无特定厂商驱动黑洞与穿孔 Bug代码量膨胀严重,引入新的系统级黑盒缺陷

Go/No-Go 判定与实测决胜结果:

  • 优先要求在 P99 frameOverrunMs、GC/Allocation 次数或内存峰值中至少一项出现显著改善,且其他关键指标不存在明显回归;
  • 若 ComposeSceneRenderer 表现与 AndroidViewSceneRenderer 相当,直接采纳 Compose 方案,终止 View 方案的进一步扩张。

Phase 0A 固定夹具真机实测数据(Android 16 / SDK 37, 100 页条漫夹具,Full Compilation) ​

渲染器候选frameDurationCpuMs P50P90P95P99frameOverrunMs P99判定结论
lazy (通用 LazyColumn 对照组)1.3 ms5.5 ms5.6 ms7.0 ms+0.3 ms (逾期错过 deadline)基线存在掉帧尖峰
compose_scene (ComposeSceneRenderer)1.9 ms3.2 ms3.6 ms6.8 ms-5.2 ms (安全余量 5.2ms)显著改善,彻底消除 P99 逾期
view_scene (AndroidViewSceneView 参考组)1.9 ms3.3 ms3.6 ms4.1 ms-8.8 ms (安全余量 8.8ms)极小差距,但引入 View 互操作成本

决胜决策(Tie-Breaker Executed):

  • ComposeSceneRenderer 将通用列表的 P99 逾期(+0.3ms)彻底消除至安全区间(-5.2ms),且 P90/P95 帧耗时与原生 View Canvas 完全持平(3.2ms/3.6ms vs 3.3ms/3.6ms);
  • 依据決胜原则,正式确定 ComposeSceneRenderer 为阅读器主攻架构,ViewScene 仅作为参考对照存在,不进行进一步业务功能扩张。

Phase 0B 生产图片管线与真实解码真机实测数据(Android 16 / SDK 37, 72 页变高条漫图集) ​

测试机型:Xiaomi Redmi Note 12 Turbo (warsaw),高通第二代骁龙 7+ (Snapdragon 7+ Gen 2),Android 16 (SDK 37),HyperOS,请求并验证 120Hz 物理刷新率。 测试夹具:ReaderProductionBenchmarkActivity 生成 72 页确定性本地条漫图集(files/reader-benchmark/v1/),包含 800×1440、800×2400、800×1280、1200×800 等多分辨率真实尺寸抖动,走完整 Coil 磁盘解码、ComposeReaderPageTransformation 裁剪与 KototoroImagePipelineAdapter 双向状态机。 同步机制:通过首帧双 Choreographer 回调向测试套件发送跨进程广播,严格等待第一屏真实解码渲染完成才开始计量。

1. Burst 突发滑动基准(Primary Mode: CompilationMode.Partial, 5 次迭代,24 次连续 Fling) ​
指标维度Legacy ComposeWebtoonReaderComposeSceneWebtoonReader改善幅度与结论
frameDurationCpuMs P504.30 ms2.70 ms-37.2%(帧 CPU 中位耗时大幅降低)
frameDurationCpuMs P906.70 ms4.60 ms-31.3%
frameDurationCpuMs P957.70 ms4.90 ms-36.4%
frameDurationCpuMs P9910.20 ms6.20 ms-39.2%(CPU P99 稳固在 120Hz 8.33ms 预算内)
frameOverrunMs P99-2.40 ms-5.90 ms+3.50 ms 硬件截止期安全余量(抗突发卡顿鲁棒性显著提升)
Active Presentation Assets (Max / Last)N/A (未做管线受控)10.0 / 9.0严格有界(无论快速滑行多远,活跃 Bitmap 资产固定在保留窗口内)
GPU Memory Max (Median)130.1 MB133.7 MB+3.6 MB(Lookahead 预测窗口预加载纹理带来合理的受控开销)
Heap Size Max (Median)92.3 MB91.8 MB基本持平(-0.5 MB)
2. Sustained 持续多轮往复遍历基准(2 轮双向 72 页遍历,1000+ 渲染帧) ​
指标维度Legacy ComposeWebtoonReaderComposeSceneWebtoonReader改善幅度与结论
平稳态匿名内存 (RssAnon.Last)331.9 MB213.2 MB-35.8%(-118.7 MB 稳态驻留降低)
峰值匿名内存 (RssAnon.Max)353.0 MB236.1 MB-33.1%(-116.9 MB 峰值节省)
frameDurationCpuMs P503.80 ms2.40 ms-36.8%
frameDurationCpuMs P998.80 ms5.60 ms-36.4%(持续长程滑动下维持绝对平稳)
frameOverrunMs P99-3.60 ms-6.90 ms+3.30 ms 硬件截止期安全余量
Active Presentation Assets 状态机N/A峰值 11.0 → 静止 8.0验证双向状态机(离开视口自动从 Presentation 降级为 Source)
3. Diagnostic 全量预编译基准(Diagnostic Mode: CompilationMode.Full, 5 次迭代) ​
指标维度Legacy ComposeWebtoonReaderComposeSceneWebtoonReader改善幅度
frameDurationCpuMs P502.53 ms1.74 ms-31.2%
frameDurationCpuMs P995.62 ms3.97 ms-29.4%

阶段结论(Phase 0B Conclusion):

  • 帧渲染余量显著扩增:frameDurationCpuMs 衡量 UI/RenderThread 的 CPU 执行耗时,frameOverrunMs 决定是否错过显示硬件 VSYNC Deadline。在 120Hz 刷新率下,旧版 ComposeWebtoonReader 的 P99 CPU 达到 10.20ms(逼近显示周期),P99 Overrun 为 -2.40ms;而 ComposeSceneWebtoonReader 将 P99 CPU 压降至 6.20ms(-39.2%),P99 Overrun 进一步拓宽至 -5.90ms(突发)与 -6.90ms(长程),为系统调度与突发波动留出了 +3.3~3.5ms 的硬件截止期安全缓冲;
  • 内存架构闭环验证:双向 PRESENTATION_READY ↔ SOURCE_READY 状态机经受住了 72 页全图往复长程遍历的严苛考验。在本次 72 页双向重复遍历中,Scene Reader 的 presentation working set 始终保持在 8~11 个 asset,RSS Anon 未随遍历轮次呈现 Legacy 路径的高位驻留(Sustained 稳态 RSS Anon 降低 35.8% / 118.7 MB,峰值降低 33.1% / 116.9 MB)。

Phase 1 实施进度(图像解码决策与切片瓦片引擎) ​

Phase 1A:解码决策智能(Decode Intelligence,已完成,commit 4410872b2)

  • 纯 Kotlin 决策核心,零 Android/UI 依赖:IntRect / PixelUsage / DecodeAllocatorPolicy / RendererCapabilities(Unknown → 首帧 Resolved,保守回退 4096)/ TilePolicy(策略与硬件能力拆分)/ ImageSourceMetadata / ImageSourceGeometry(Crop contentRect + EXIF 方向的逻辑→源图坐标映射)/ ReaderLodPolicy(Target-Based Sampling:targetDecodeWidthPx = min(source, ceil(display × scale × overscan)),2 的幂 sampleSize,滞后防抖)/ DecodePlan(Single / SampledSingle / Tiled / AnimatedSingle + AnimatedFallback 超限安全降级)/ DecodePlanner(多维决策矩阵)。
  • 测试:IntRectTest / ReaderLodPolicyTest / DecodePlannerTest / ImageSourceGeometryTest 全绿(纯 JVM)。

Phase 0C/P0 修复:Warm Backend Switch 首屏模糊(commit 81e5e946a)

  • 根因:getCachedAsset() 把未知质量的 Coil memoryCache 位图直接包装为 ComposeImage 并 storeAsset 晋级为 PRESENTATION_READY 资产;资源窗口看到已有 ComposeImage 即跳过重解码,首屏持续低清拉伸,直至页面离开再回到 presentation window 才触发正式解码。
  • 修复:拆分"几何探测"与"正式呈现资产晋级"两个职责 —— 新增 ReaderImagePipeline.probeCachedDimensions(pageId) 仅供 PageGeometryHint.Exact 使用(零资产副作用);getCachedAsset() 不再晋级 memoryCache 条目(回落 Encoded),presentation 一律经 acquireAsset() 正式解码。新增 Trace.setCounter("Reader.PresentationWidthPx") 供后续质量基准沿用。
  • 验证:回归测试 warm backend switch from legacy with low-res memoryCache entry does not pollute presentation assets(160×480 低清 cache → Encoded → PRESENTATION_READY 触发 1080×3240 正式解码);ReaderImageAssetTest 14/14 全绿;真机复测模糊现象消失。

Phase 1B:图源与切片运行时(Source + Tile Runtime,已完成)

  • RegionDecodeSource / TileDecodeSession(计划 1.4 命名):区域解码源与会话抽象;会话复用 = 每页仅一次原生解码器解析,任意长条图的全部瓦片共享同一 TileDecodeSession。解码坐标均为原始编码空间(EXIF / Crop / Split 由几何层折算)。
  • TileGrid:逻辑页 → 源图坐标变换(Crop contentRect → Split 左右半页 → EXIF 方向旋转)。Gutter 与 sampleSize 联动(计划 1.6):sourceGutterPx = outputGutterPx × sampleSize,源图解码区域向外扩展 gutter 消除独立采样相位差导致的黑缝;屏幕目标渲染矩形(logicalRect)严格拓扑拼接不重叠。TileKey 以 TileKind.LATTICE / OVERVIEW 区分格点瓦片与整页 LOD0 底带,键空间零冲突。
  • TileMemoryBudget(四级智能驱逐):保留级 VISIBLE(钉驻,压力下永不驱逐)→ NEARBY(前瞻窗口)→ STANDBY(首次脱离需求)→ CACHE(二次脱离);同级按 LRU;时钟可注入(确定性测试);超预算的 VISIBLE 突发允许临时超额(宁可超预算不可丢可见像素)。
  • ReaderTileManager:请求驱动编排 —— 会话复用(putIfAbsent 注册竞态收敛)、瓦片任务 LAZY 启动去重、脱离需求的在飞任务取消、驱逐回调(payload 释放钩子 payloadReleaser,1C 可接 Bitmap.recycle())、会话打开失败去重上报且支持重试(Result 包装阻断结构化并发向父级 scope 传播业务失败)。
  • AndroidRegionDecoderFactory:BitmapRegionDecoder API 26~37 兼容构造(SDK ≥ S 走非废弃重载);URI 三态分发(content+zip:// 流式扫描 / zip://+ZipFile 直读条目 / 通用 contentResolver.openInputStream),与既有 ZipSubSamplingImageSource / NativeSubSamplingImageSource 的取流策略对齐;EXIF 旋转一次性折入 ImageSourceGeometry;ARGB_8888 SOFTWARE 解码对齐 PixelUsage.REGION_TILE 的分配策略;ReentrantReadWriteLock 守护 decode 与 recycle 的关闭顺序。
  • 测试:TileGridTest(13)/ TileMemoryBudgetTest(9)/ ReaderTileManagerTest(11)全绿,共 33 例纯 JVM(fake session 模拟 Android 解码器:会话复用、四级驱逐、在飞取消、失败重试、overview 钉驻、缺源去重上报)。

Phase 1C:渲染器集成与管线适配(Renderer Integration & Pipeline Adapter,已完成)

  • TileStore 抽象与无重组刷新桥:
    • 新增 [TileStore](file:///e:/kototoro_demo/Kototoro/app/src/main/kotlin/org/skepsun/kototoro/reader/image/TileStore.kt) 接口解耦查询与监听,ReaderTileManager 实现此接口。
    • 新增 [TileDrawModifierNode](file:///e:/kototoro_demo/Kototoro/app/src/main/kotlin/org/skepsun/kototoro/reader/render/compose/TileDrawModifierNode.kt):通过 Modifier.tileDrawBridge(tileStore) 挂载到根节点,监听 onTileReady 与 onTileDropped 事件并在 Dispatchers.Main.immediate 上直接调用 invalidateDraw(),实现零 Recomposition / 零 Layout 的纯绘制期重绘调度。
  • ComposeSceneRenderer 多态分块渲染:
    • 支持 [ReaderImageAsset.Tiled](file:///e:/kototoro_demo/Kototoro/app/src/main/kotlin/org/skepsun/kototoro/reader/image/ReaderImageAsset.kt) 呈现:
      1. LOD0 Overview 底带:在格点瓦片就绪前平铺全页低分辨率预览,避免白屏;
      2. 高精度格点瓦片(Lattice Tiles):通过 [TiledPageDrawMath](file:///e:/kototoro_demo/Kototoro/app/src/main/kotlin/org/skepsun/kototoro/reader/render/compose/ComposeSceneRenderer.kt) 精确反向裁剪 decodeRegion 外扩的采样 Gutter,无缝拓扑拼接;
      3. 几何与旋转兼容:根据 ImageSourceGeometry.orientationDegrees 执行 pivot 旋转,支持横屏/EXIF 旋转及左右双页切分(TileSplit)。
  • KototoroImagePipelineAdapter 管线闭环:
    • 接入 RegionDecoderFactory、DecodePlanner、actualTileManager;
    • acquireAsset 自动决策:当 DecodePlanner.plan 返回 DecodePlan.Tiled 时,生成 ReaderImageAsset.Tiled 并请求 LOD0 Overview,自动上报 composePipeline.onImageDecoded;
    • 动态瓦片请求:在 updateResourceWindow 中对视口内的可见 Tiled 节点折算逻辑坐标并调用 adapter.requestTiles(pageId, visibleLogical);
    • 完善双向生命周期:离开保留窗口触发 evictAsset 或降级 downgradeToSource 时调用 actualTileManager.releasePage(pageId),及时回收会话与 Bitmaps。
  • 自动化测试验证:
    • [TileDrawModifierNodeTest](file:///e:/kototoro_demo/Kototoro/app/src/test/kotlin/org/skepsun/kototoro/reader/render/compose/TileDrawModifierNodeTest.kt)(6 例):验证节点挂载/解挂、Store 动态变更切换监听、Modifier 链构造;
    • [ComposeSceneRendererTiledTest](file:///e:/kototoro_demo/Kototoro/app/src/test/kotlin/org/skepsun/kototoro/reader/render/compose/ComposeSceneRendererTiledTest.kt)(3 例):验证内层瓦片 Gutter 裁剪、sampleSize 降采样比例折算、右半页双页切分偏移计算;
    • [KototoroImagePipelineAdapterTiledTest](file:///e:/kototoro_demo/Kototoro/app/src/test/kotlin/org/skepsun/kototoro/reader/image/KototoroImagePipelineAdapterTiledTest.kt)(6 例):验证 ReaderPageSplit 到 TileSplit 映射、Tiled 资产生成与 Overview 预取、多页切分、requestTiles 转发、页面驱逐清理、异常降级单图;
    • [ReaderImageAssetTest](file:///e:/kototoro_demo/Kototoro/app/src/test/kotlin/org/skepsun/kototoro/reader/image/ReaderImageAssetTest.kt)(14 例全绿):验证与现有单图/缓存管线及 Warm switch 完全兼容零回归。
    • 全套共 29 例 Phase 1C/Asset 纯 JVM 测试 100% 通过。

Phase 1D:真机超长条漫效能与长图画质基准(已完成):

  • 测试环境:Xiaomi Redmi Note 12 Turbo (Snapdragon 7+ Gen 2), Android 17 (SDK 37), HyperOS, 物理 120Hz 刷新率, 12 页超长条漫图集(高度 12,000px ~ 40,000px,宽度 1080px,累计纵向跨度 290,000+ 像素,单张原始 Bitmap 展开最高达 172.8MB,超过 16384px GPU 硬件纹理极限),CompilationMode.Full(), 5 轮迭代大跨度连续 Fling。
超长条漫实测数据对比(Ultra-Long Webtoon Benchmark: 12k ~ 40k px Strips) ​
指标维度Legacy ComposeWebtoonReaderComposeSceneWebtoonReader (Tiled)优势与结论
GPU 显存中位峰值 (GpuMaxKb.Median)321,136 KB (~313.6 MB)121,908 KB (~119.0 MB)-62.0% 显存压降 (-194.6 MB)!传统单图架构引发显存暴涨,Scene 仅将视口内 1024x1024 瓦片载入显存,严格受限
GPU 显存最大峰值 (GpuMaxKb.Max)335,440 KB (~327.6 MB)122,212 KB (~119.3 MB)-63.6% 显存节省 (-213.2 MB)
frameDurationCpuMs P502.72 ms1.32 ms-51.5%(CPU 执行耗时减半)
frameDurationCpuMs P904.13 ms2.48 ms-40.0%
frameDurationCpuMs P954.34 ms3.10 ms-28.6%
frameDurationCpuMs P996.43 ms4.48 ms-30.3%(稳居 120Hz 8.33ms 预算的一半以内)
frameOverrunMs P50-9.96 ms-11.02 ms截止期余量扩大 1.06ms
frameOverrunMs P99-5.52 ms-6.22 ms截止期安全余量扩大 0.70ms(P99 口径:≥99% 帧未超时;不据此断言零掉帧,见 改进计划 §2)
有效绘制吞吐 (frameCount.Median)248.0 帧409.0 帧+64.9% 有效帧吞吐(手势滑动丝滑平顺,无主线程掉帧卡顿)
Active Presentation AssetsN/A (未跟踪)10.0 / 9.0 (Max / Last)严格有界(视口之外的格点瓦片与单图被 TileMemoryBudget 及时回收)
持续往复遍历数据对比(Sustained Traversal Benchmark: 72 页 2 轮全图 80 次滑动,1200+ 帧) ​
指标维度Legacy ComposeWebtoonReaderComposeSceneWebtoonReader优势与结论
稳态匿名内存 (RssAnon.Last)370.7 MB258.9 MB-30.2% (-111.8 MB 稳态驻留降低)
frameDurationCpuMs P502.54 ms1.93 ms-24.0%
frameDurationCpuMs P995.02 ms4.10 ms-18.3%
有效绘制吞吐 (frameCount.Median)1,010.5 帧1,236.5 帧+22.4% 吞吐提升
Active Presentation AssetsN/A峰值 10.5 → 静止 7.0验证双向状态机(离开视口自动回收)

Phase 1 总体结论: 从 Phase 1A 解码决策到 1B 切片运行时、1C 无重组刷新桥、1D 真机超长图极限压测,完整闭环证明了 ADR 0002 架构设计的正确性与卓越效能: 在应对 10,000 ~ 40,000 像素极高条漫长图时,不仅打破了 Android GPU 16384px 的硬件纹理上限天花板,同时斩获 62% 的显存节省 与 50%+ 的 CPU 帧执行耗时压降,在 120Hz 高刷物理设备上实现了无白屏的连续呈现(P99 口径下未见截止期超时;最慢约 1% 帧不在该口径内,见 改进计划 §2)。

Phase 2 实施进度(生产化功能对齐与多章节视口锚定) ​

Phase 2A:生产级功能对齐与跨章节无感锚定(已完成)

  • 页面间隙(Page Gaps):
    • VerticalReaderScene 构造注入 pageSpacingPx: Int = 0,排版算法在连续页面间插入物理间隔,总高度精确涵盖间隙;
    • 二分查找视口相交时,视口落在两页间隙时平滑归属,无丢帧或索引崩溃;
    • ComposeSceneWebtoonReader 接入 isGapsEnabled 与 R.dimen.webtoon_pages_gap,支持动态开启与关闭。
  • 跨章节平滑扩缩窗与无感锚定(Seamless Cross-Chapter Window Expansion & Preserved Anchoring):
    • VerticalReaderScene.updatePages(newPages, currentViewport):
      1. 保留 Exact 几何尺寸:动态换章或窗口滑动时,保留已成功解码的精确尺寸,严禁退回 Estimated 默认估算;
      2. 精准滚动位移补偿:前向插入上一章节或后向扩展下一章节时,以当前视口主导阅读页面为锚点计算新旧坐标差 deltaY,由 scrollState.snapBy(deltaY) 实施绝对像素级补偿,用户眼前画面零跳跃、零白屏、零状态重置。
  • 统一错误与重试交互(Unified Error & Retry UX):
    • SceneReaderLoadStatus 对齐 Kototoro 统一的 ReaderPageError 组件;
    • 完整串联 onRetryError、onShowErrorDetails 与 resolveErrorStringId,消除占位 UI,实现与其他阅读器完全一致的错误处理与重试弹窗交互。
  • 内存优化模式对齐(Reader Optimization):
    • KototoroImagePipelineAdapter 接入 isReaderOptimizationEnabled,在开启时将 Coil 图像请求的 memoryCachePolicy 设为 DISABLED,完全由 Reader 专用状态机与 TileMemoryBudget 管控缓存,杜绝底层图片库的双重内存驻留。
  • 自动化测试验证:
    • VerticalReaderSceneTest 新增 3 项核心用例:
      • lays out pages with pageSpacingPx between pages
      • updatePages preserves existing exact hints and anchors prepended pages with zero jump
      • updatePages appending pages returns zero deltaY
    • 全套 Reader JVM 单元测试(46 例)100% 通过。

Phase 2B:2D 契约抽象、双向锚定补偿与默认全量推广(已完成)

  • 2D 通用场景契约:抽离 MutableReaderScene 与 SceneReadingDirection(TOP_TO_BOTTOM、LEFT_TO_RIGHT、RIGHT_TO_LEFT),定义通用的 AnchorCompensation(deltaX, deltaY) 双向坐标补偿机制。
  • 全量默认启用验证:在 Webtoon 模式下将 isExperimentalSceneReaderEnabled 设为生产默认值,验证架构长久稳定性。

Phase 2C & 2C2:横向连续瀑布流场景与宿主交互(已完成)

  • 横向连续场景引擎 (HorizontalReaderScene):
    • 严格满足 Invariant I1,纯 Kotlin 实现无 Android 依赖;
    • 支持 LTR(从左至右)与 RTL(日漫从右至左)对称布局;
    • 高性能 $O(\log N)$ 二分查找视口相交区域,实时派发 ReaderFrame 与 ReaderProgressSnapshot;
    • 动态估算替换精确几何尺寸时的 X 轴零跳动锚定补偿(Zero-CLS)。
  • 横向连续宿主 (ComposeSceneHorizontalReader / ComposeHorizontalSceneRenderer):
    • Draw Phase 纯绘制限制,跳过 Composition/Layout;
    • 物理惯性滑动、双向拉动切章与自适应刷新率(ARR)集成。

Phase 3 实施进度(现代分页场景引擎与统一宿主) ​

Phase 3A:纯 Kotlin 分页几何模型与排版调度(已完成)

  • 核心模型:
    • PagedReaderScene:实现 MutableReaderScene,负责分页与并页插槽管理、视口求交与平移结算;
    • PagedSpreadResolver:实现插槽排布算法,严格遵循 Kototoro 业务规范:
      1. 跨章隔离:不同章节页面绝对不合并在同一插槽;
      2. 宽页与封面独立:宽高比 > 1.15 的宽页(WidePagePolicy)或封面偏移页独占单插槽;
      3. 日漫 RTL 镜像排布:在 RTL 模式下,插槽内先读页居右、后读页居左,阅读顺序严格保序;
    • PagedSnapResolver:纯数学离散吸附解析器,根据位移比例(默认 20%)与归一化滑动速度(默认 0.5f)解析目标插槽。
  • 单元测试:PagedReaderSceneTest、PagedSpreadResolverTest、PagedSnapResolverTest 100% 覆盖。

Phase 3B:分页影子模式校验器与等价性验证(已完成)

  • 影子校验器 (PagedShadowValidator):
    • 针对生产真实 ReaderPage 列表,并行比对既有 DoublePageSpreadModel 与现代 PagedSpreadResolver 的排页、跨章隔离与锚点;
    • 在既有 ComposeDoublePageReader 中加入无感运行时采样(sampleRuntimeParity),单次开销 < 0.1ms;
  • 等价性套件 (PagedShadowParityTest):
    • 覆盖奇数/偶数章节、封面偏移开启/关闭、跨章节混合图集、日漫 RTL 翻页及 100 轮随机模糊测试(Fuzz testing),达成 100% 零差异。

Phase 3C:现代分页场景 Compose 宿主 (ComposeScenePagedReader)(已完成)

  • 单双页统一架构:
    • 将单页模式(Standard LTR、Reversed RTL、Vertical Paged)与双页并页模式统一由 ComposeScenePagedReader 承载;
    • 结合 PagedReaderScene 与 drawFrameNodes,滚动平移与翻页过渡严格约束在 Draw Phase,彻底跳过 Composition 与 Layout。
  • 交互与手势:
    • 单指拖拽驱动插槽无缝平滑滑动;
    • 手势释放由 PagedSnapResolver 结算目标并触发平滑弹性吸附动画;
    • 双指缩放(1x ~ 5x)与双击缩放切换;
    • 视口两端 Pull 手势触发上一章/下一章切换。
  • 管线与零跳动补偿:
    • 接入 KototoroImagePipelineAdapter,维护插槽级资源窗口(当前插槽 PRESENTATION_READY,相邻插槽 SOURCE_READY,移出插槽及时淘汰);
    • 尺寸解析时通过 AnchorCompensation 实施零 CLS 视觉补偿。

Phase 3D:生产路由接线、交互测试与 ADR 归档(已完成)

  • 生产分发路由 (ComposeReaderScreenRoot.kt):
    • 在 isExperimentalSceneReaderEnabled 开启时,分发至现代 ComposeScenePagedReader;
    • 在开关关闭时,100% 无损回落至既有 ComposeDoublePageReader 与 ComposePagedReader;
  • 交互集成测试 (ComposeScenePagedInteractionTest.kt):
    • 覆盖离散插槽隔离、双页并页跨章阻断、RTL 日漫左右排布、吸附阈值、过渡求交与动态更新补偿。全套测试 100% 通过。

Phase 3E:图像呈现 Parity 补齐与生产门控解耦(已完成)

  • 架构抽离与去重 (SceneImagePresentationCoordinator):
    • 抽取统一呈现协调器,解决分页场景超大图(如 $6000 \times 9000$)因未请求可见切片而仅渲染 LOD0 Overview 导致发糊的核心 Blocker;
    • 统一 Webtoon、Horizontal 与 Paged 三大场景宿主的 Frame 可见切片计算(coordinateVisibleTiles)与视口逆向变换数学(computeVisibleBounds),消除重复实现。
  • 分辨率感知多 LOD 缩放(Resolution-aware Multi-LOD):
    • 接入 150ms Camera Settle 防抖与 ReaderCameraSnapshot,高倍放大($2\times \sim 5\times$)时自动降级采样率并请求高清视口瓦片,缩放回归 1x 时自动释放过采样切片。
  • 绘制生命周期闭环 (TileDrawBridge & AnimatedDrawBridge):
    • 挂载 .tileDrawBridge(adapter.tileStore),后台解码切片就绪时直接触发局部 Draw-phase 重绘,跳过 Compose Recomposition;
    • 挂载 AnimatedDrawBridge 并传入 drawFrameNodes,打通 GIF / Animated WebP 的逐帧回调、可见时启动、不可见时暂停的生命周期。
  • 设置与排版 Parity:
    • PagedSpreadConfig 与 PagedSpreadResolver 全面支持 ZoomMode(FIT_CENTER、FIT_WIDTH、FIT_HEIGHT、KEEP_START);
    • 支持 ReaderAnimation(NONE 时立即 snap,动画模式平滑过渡);
    • 支持 readerBackground 与 bookBackgroundTint。
  • 生产门控安全解耦:
    • 新增 isExperimentalPagedSceneReaderEnabled(默认 false),与成熟且已默认开启的 Webtoon / Horizontal 场景解耦;
    • 保证未手动开启分页实验开关的用户 100% 继续使用稳定的 Pager + Telephoto,零生产回退风险。
    • 2026-09-21 更新(交付 45):门控收敛为按系列独立的两个开关,宿主选择由唯一纯函数 resolveSceneReaderEnabled(mode, isDoublePage, webtoonSceneReader, pagedSceneReader) (reader/ui/config/SceneReaderGate.kt)决定 —— 双页与单页/上下取分页开关,WEBTOON 取条漫开关; 此前写成 isExperimentalSceneReaderEnabled && isExperimentalPagedSceneReaderEnabled,使条漫开关 成为分页宿主的隐藏总闸(关掉条漫渲染器会连带关掉分页场景)。两个开关同时暴露在阅读器更多面板的 「阅读」页(ComposeReaderOptionsSheet),与设置页共用同一份 pref,默认值不变。 CONTINUOUS_HORIZONTAL 无 legacy 渲染器,属 scene-only,不参与该路由(详见 SceneReaderGate KDoc)。

Phase 3F:交互溢出、自适应背景与物理基准对齐(功能已实现,真机基准待验收)

  • 基线溢出平移数学解算器 (PagedPanBoundsResolver):
    • 严格保持 I1(ReaderCore 零 Android / Compose 依赖),提取纯数学解算器;
    • 彻底重构手势判定,将旧有的 canvasScale > 1f 单纯缩放判定升级为基于 transformedContentBounds > viewport 的轴向精确解算;
    • 解决 FIT_WIDTH(高超出视口)与 FIT_HEIGHT(宽超出视口)在 $1\times$ 默认比例下无法单指拖动查看边缘的交互 Blocker;
    • 支持单页与双页排版模式下的起始点对齐(resolveInitialOverflowOffset),翻至新页时自然对齐至页首(LTR/Vertical 从顶部/左侧开始,RTL 从右侧开始)。
  • 主轴溢出手势交接 (PagedDragState):
    • 先消费内容平移,到达边缘后仅将剩余位移用于翻页;反向拖动先收回翻页位移,再恢复内容平移。
    • 内容平移不触发翻页 fling;交接时重置翻页速度记录,多指缩放介入时取消翻页与拉动切章状态。
    • JVM 回归覆盖位移分配、方向、反向交接和松手目标计算;不替代真实指针事件与设备交互验收。
  • ReaderBackground.AUTO 自适应背景采样:
    • 在 ComposeScenePagedReader 中无缝消费 retainedAssets 内存位图(ComposeImage、AndroidBitmap、Tiled.overviewBitmap);
    • 调用 ReaderAutoBackground.resolve() 与 resolveDoublePageBackground(),零额外磁盘/网络 IO 开销完成单/双页边缘背景色采样与书籍色调合成。
    • 书籍色调仅应用于 AUTO 背景,固定白色等用户指定背景不受影响。
  • 用户设置开关暴露:
    • 在 ReaderSettingsScreen 补充 reader_experimental_paged_scene_engine 开关项与中英文文案,允许测试人员与用户自主开启或回滚。
  • 物理机基准对齐矩阵(Hardware Parity Matrix):
    • 以下为待执行的验收矩阵,本阶段尚未记录 Paged 真机实测结果;Paged 实验开关保持默认关闭。
    • 确立进入生产默认开启的 4 组真机对照场景:
      1. 普通漫画单页 1x(CPU P99 帧耗时与 Baseline Profile 稳态);
      2. $6000 \times 9000$ 超大图 1x(LOD0 Overview 基础切片就绪与防白屏);
      3. $6000 \times 9000$ 高倍放大(2.5x / 5x Settle 防抖与高清切片加载延迟及显存上限);
      4. 双页跨章节持续往复翻页(RSS Anon 内存泄露审计与 GC 频率)。
  • 尚未完成的动画一致性:DEFAULT、ADVANCED、SIMULATION 当前共用滑动过渡,尚不具备独立动画样式。

六、 Non-Goals(第一阶段明确不做的事项) ​

为了控制工程范围,第一阶段严格禁止发散:

  1. 不重写现有阅读器的全部交互与手势;
  2. 不重写物理惯性引擎(Physics,通过 ViewportMotion 承接);
  3. 不在第一阶段直接开发完整的 Tile 动态切片与 LOD 高级引擎;
  4. 不在第一阶段废弃或替换现有的 Telephoto 单页/双页阅读模式;
  5. 不自行实现或重构 WebGPU 的 Continuous 条漫模式(交由上游 Mihon 推进);
  6. 不将 LowLatencyCanvasView(前台双缓冲/画笔向)纳入 Webtoon 实验。

七、 架构不变量(Architectural Invariants) ​

未来所有与阅读器相关的代码提交与代码审查(PR Review),必须严格核对是否违背以下 6 条不变量:

  • I1: ReaderCore MUST NOT depend on Android UI toolkit or renderer APIs, nor Android platform geometry types (android.graphics.Rect/RectF/Matrix, androidx.compose.ui.geometry.*).(ReaderCore 绝不得依赖 Android UI 工具包、渲染器 API 或 Android 平台几何类,必须使用自包含的跨平台几何抽象)
  • I2: ImagePipeline MUST NOT own chapter progress, reading direction, spread semantics, or viewport UI state.(ImagePipeline 绝不得持有章节进度、阅读方向、双页语义或视口 UI 状态)
  • I3: Renderer MUST NOT be the source of truth for reader semantics.(Renderer 绝不得成为阅读语义的真相之源)
  • I4: Renderer-specific resource representations ARE allowed.(允许 Renderer 拥有特化的资源表征形态)
  • I5: Scene geometry MUST remain computable when image pixels are unavailable.(在图片像素不可用时,Scene 几何结构必须依然保持可计算)
  • I6: Backend replacement MUST NOT require rewriting progress/layout semantics.(替换渲染后端绝不得要求重写进度或布局语义)

Documentation for Kototoro