Skip to content

Roadmap:P0–P9 执行计划

按阶段推进,逐阶段提交。每阶段给出:目标 / 范围 / 产出文件 / 验收标准(尽量可自动化)/ 明确不做的事。 阅读前请先读 OverviewDecisions;接口契约见 Interfaces;验收方法见 Testing

优先级映射原始建议:P0↔抽象 core、P1↔RasterLayer+Surface、P2↔dirty 更新、P3↔tile undo、P4↔输入、P5↔笔刷种类、P6↔锁透明 / 剪贴 / 蒙版、P7↔混色、P8↔文件、P9↔外部 brush engine。下表把它们重排为可增量交付的顺序,并插入 v8 迁移与 shodo 收割。

P0 — Pixi v8 迁移 + 测试地基(前置,必做)

📋 细化任务卡:tasks/,codex 按卡逐个执行、逐个验收。

现实校正(已用源码 + 实编译验证)

依赖已是 v8.19,pnpm build:lib 能通过。v8 保留beginFill / drawCircle / lineStyle / endFillapp.viewdeprecated 垫片——它们能 build、能跑(仅 console 警告)。

唯一阻断运行的是 Application async-init:v8 的 new Application(options) 只打印 deprecation、不创建 renderer,而当前构造函数同步读 app.renderer.resolutionpainter.tsboard/index.ts)→ 运行时 throw。

所以 P0 硬核只有两件:修 Application 引导 + 建测试地基。Graphics 的 v7 写法不必在 P0 迁——P1 会用 raster 引擎删掉 brush/eraser 的 Graphics,迁了白迁。

  • 目标:让 createPainter() 在 v8 真正跑起来(修 Application init)+ 建可自动化验收的测试地基。只修阻断点,不重构行为。
  • 范围(必做)
    • painter.tsApplicationawait app.init({ canvas, ... }),把同步访问 app.renderer / app.screen 的代码下移到 init 之后(P0-02,P0 唯一的运行阻断修复)。
    • 重建 test/:headless 跑「画一笔 → extract pixels → 断言非空 + 包围盒」(P0-06)。
  • 范围(可选 / deprecation 清理,不阻断)
    • 长寿命 overlay(canvas mask、board、layers 选框 / 手柄)的 v7 Graphicsnamelabel:消除警告,但可延后(P0-04)。
    • brush / eraser 的 Graphics:P0 不迁,留给 P1 删除(P0-03 标为可选)。
  • 验收examples/vue 能画线 / 橡皮 / 撤销且无运行时 throwpnpm test ≥1 像素断言通过。
  • 不做:任何 raster 重构;为将被 P1 删除的 brush/eraser Graphics 做迁移。

P1 — RasterLayer + RenderTexture 后端 + 真橡皮(架构分水岭)

  • 目标:笔迹从「Graphics 累加」改为「戳印渲染进图层 RenderTexture」;引入真正的栅格图层与真橡皮。
  • 范围
    • coreBrushEngine(先 SimpleBrushEngine:spacing + 圆 dab + pressure→size/opacity)、SurfaceBackend 接口、Document / RasterLayerUndoManager
    • pixiRenderTextureBackendpaintDabrenderer.render(dabSprite, { renderTexture, clear: false });erase 用 erase blend / destination-out 写入图层 RT)、PixiLayerRenderer(图层 RT → Sprite)。
    • 撤销:endStroke 截取受影响包围盒的 before / after 位图(StrokePatch),applyPatch 回贴。
    • pixi-painter 内部把 brush / eraser 实现切到新管线(保留旧 API 形态)。
    • core 暴露 headless controller 表面setTool / brush.setColor / brush.setSize / on('tool:change' | 'brush:change')),为 UI 薄皮化铺路(见 D7)。
  • 产出packages/core/packages/pixi/ 初版。
  • 验收
    • 连续画 5000 个 dab,stage 子节点数不随笔数增长(不再是每笔一个 Graphics)。
    • 橡皮在透明背景上能擦出透明(extract alpha=0)。
    • 撤销 / 重做后图层像素与操作前 / 后一致(golden-image 比对)。
  • 不做:tile、混色、smudge、带 transform 图层的绘画。

P2 — TiledSurface + tile undo(仅当需要大画布 / 低内存撤销时)

  • 目标:把 SurfaceBackend 换成 256×256 tile 实现,撤销变成 tile before / after patch,dirty tile 批量上传。
  • 范围core/surface/{TiledSurface,Tile,DirtyRect}pixi/PixiTileTextureBackend(CPU tile buffer → 每帧 requestAnimationFrame 批量更新脏 tile 的 Pixi Texture);UndoManager patch 改 TilePatch[]
  • 验收:4096×4096 画布连续涂抹,内存平稳;撤销只恢复脏 tile;一帧内多 dab 合并为一次上传(可用计数器断言)。
  • 不做:每帧全图上传、cacheAsTexture 用在活动绘画层。

P3 — 输入质感:跟手(可与 P1 / P2 并行)

  • 目标:让线条「跟手、顺、压感舒服」。
  • 范围core/input/PointerSampler(coalesced events)、PressureCurve(可配置;鼠标无压感 fallback 策略,不要硬编码 0.5)、Stabilizer(先 moving average + exponential smoothing,再做 rope / lazy-brush)、min/max size & opacity、spacing、taper in/out、anti-alias。
  • 收割 shodostroke-engine.ts 的 4 点滑动平均、velocity→size 曲线、加速度出锋 → 抽成 StabilizerCalligraphyEngine 的复用件。
  • 验收:stabilizer 强度 0 / 轻 / 中 / 强四档可调且肉眼可见差异;慢速画圆无明显锯齿 / 抖动;回放同一组输入点像素稳定(确定性)。

P4 — 笔刷家族

  • 范围SimpleBrushEngine 扩展为 pen / pencil / airbrush / marker(spacing、硬度、流量累积、戳印纹理 tipId);shodo 的毛笔作为 CalligraphyEngine 接入同一 BrushEngine 接口。
  • 验收:≥4 种笔刷在同一 UI 切换;airbrush 停驻时浓度随时间累积;marker 半透明叠加正确。

P5 — 图层栈能力

  • 范围Document 支持多 RasterLayer、opacity、显隐、顺序、blend mode(显示用 Pixi blend,存储是各自像素);图层缩略图。
  • UI 脱离库静态(见 D7):把 @saier/vuev-model="PainterBrush.color"PainterBrush.enablePressure 等直接操纵静态字段改为走 controller API;图层面板读 document.layers 而非 painter.canvas.container.children
  • bootstrap 去重(见 D8):把画板装配(createPainter + await init() + 默认图层 / 工具 + extract 接线)抽成 @saier/vueusePainter() composable,examples/vuesite 都消费它——消除当前各写一套 bootstrap 的重复(正是 site demo 漏调 v8 init() 而坏掉的根因;快修已落,结构性去重在此)。
  • 验收:图层面板增删 / 排序 / 改透明度即时反映;导出合成结果正确;UI 不再直接引用 PainterBrush.* 静态字段;examples/vuesite 共用同一 usePainter(无重复 bootstrap)。

P6 — 锁透明 / 剪贴 / 蒙版 / 带 transform 图层绘画

  • 范围:lock alpha、clipping layer、layer mask;补齐图层逆变换绘画
  • 验收:锁透明下只改已有像素的颜色不扩边;剪贴图层只在下层不透明区显示;在被缩放 / 旋转的图层上落点准确。

P7 — 绘画的灵魂:混色 / smudge / 水彩

  • 范围(依赖 P2 tile):brush density、paint amount、color mixing、dilution、persistence、smudge、paper texture、edge softness;先 stamp brush(笔尖 mask + spacing + opacity accumulation + blend kernel),再做取色混合。
  • 验收:smudge 能拖动已有颜色;两色交界自然过渡;湿边 / 纸纹可开关。

P8 — 文件 / 序列化

  • 范围:PNG 导出(已有 extract 基础);工程文件(图层 + 元数据);笔迹回放格式收割 shodo tablet.ts{X,Y,T,P} + 操作流,做成可存储 / 回放 / 协作基础;PSD 导出为可选。
  • 验收:保存 → 读取还原图层;同一笔迹回放像素一致。

P9 — 可选:接 libmypaint / Hokusai 风格引擎

  • 范围:把 BrushEngine 作为插槽,预留 MyPaintBrushEngineWasm / ExperimentalWatercolorEngine;研究 .myb 兼容。
  • 验收:能以一个 WASM / 实验引擎实现 BrushEngine 接口并在 UI 中切换,不改 core 其它部分。

里程碑节奏(建议)

M1 = P0            // v8 迁移 + 测试地基(解锁一切)
M2 = P1 (+P3 部分) // RenderTexture 图层 + 真橡皮 + 基础 stabilizer —— “能当线稿工具用”
M3 = P2 + P4 + P5  // tile + 笔刷家族 + 图层栈 —— “像绘画软件”
M4 = P6 + P7       // 锁透明 / 蒙版 + 混色 —— “有专业绘画手感”
M5 = P8 (+P9)      // 文件 / 回放(+ 外部引擎)

每个 M 结束:demo 可用 + 验收标准全过 + 一次提交 / 发版。

Risks & 排序回顾

对整体排序的一次自检(诚实版)。总判断:排序合理,关键路径 P0-02 → P1 → … 正确,D1「RenderTexture 先行、tile 后置」让早期可交付。已知风险与校准:

  1. P1 偏重、风险前置:P1 一次引入两包 + 新渲染 + 撤销 + 集成(9 卡),M1→M2 跨度大。缓解:P1-05 先做成独立 spike(脱离集成验证 RenderTexture 绘制 + undo),过了再做 P1-08 集成。
  2. 两套 dab 光栅化的代价:P1 在 GPU 光栅化 dab,P2 又在 CPU 重写一套(P2-02)。这是 D1 阶段化的已知成本——用「早交付」换「重复一次光栅器」。若产品一开始就要混色 / 大画布,应考虑直接 tile-first(重排 P1/P2)。
  3. 手感(P3)被压在 P1 之后跟手 / stabilizer 是这类绘画工具的灵魂,却排在大重构后。建议把 P3 的 stabilizer / pressure curve 提前在现有管线做一个最小验证(P0 之后、P1 期间并行),尽早回答「手感对不对」这个产品级问题。
  4. 命名 / 包分层(见 D8):已定——品牌 saier、scope @saier/*shodo 拼写已修;包拆分采最小拆分@saier/core@saier/pixi,依赖图强制解耦)+ lockstep 版本;已发布包实改名(pixi-painter→saiercontrols→@saier/vue)留作 restructure、动手前确认。
  5. 跨切面缺口:① 触屏 / 手势(双指缩放 vs 画)未显式排期 → 并入 P3 输入层;② 性能预算(dab/s、上传 / 帧、内存曲线)已在 P2 / Testing 出现但无专卡 → 作为各 verify 卡的硬指标持续盯;③ DOM UI 无障碍 → 留待 P5。

这些都不阻断当前推进。P0 / P1 照常,唯第 4 点(restructure & naming)建议尽快拍板。

Released under the MPL-2.0 License.