Skip to content

P7-04 · smudge 引擎:拖动已有颜色

  • Phase / ID: P7 / P7-04
  • Depends on: P7-02(沉积内核)、P7-03(sampleRegion
  • Files: packages/core/src/brush/SmudgeEngine.ts(新)、packages/core/src/brush/BrushPreset.tspackages/core/src/brush/index.tspackages/saier/src/brush/index.tspackages/saier/test/
  • Effort: L

Context

P7 的核心。smudge / color-mixing 笔刷维护一个颜色桶(smudge bucket),沿途取色、留色、再沉积,从而「拖动已有颜色」「两色交界自然过渡」。模型对齐 Krita color smudge / MyPaint:

每个 dab(位置 x,y,半径 r):

  1. 取色S = sampleRegion(layerId, dabRect)P7-03)。
  2. 留色(persistence pbucket = lerp(S, bucket, p)——p 越大颜色拖得越远。
  3. 自色(colorAmount cdeposit = lerp(bucket, brushColor, c)——c=0 纯抹(手指 / blender),c=1 纯上色。
  4. 沉积:以 deposit + density / dilution(P7-02)落 dab。

实现要点:取色必须看到「刚落的上一笔」(D10 已定)

BrushEngine.addPoint 一次返回一批 dab;但 smudge 第 N+1 个 dab 的取色依赖第 N 个 dab 已经画到画布。若沿用「先收集整批 dab 再统一 paint」,同批内取色读到的是落笔前的旧像素 → 快速描边拖不动色。业界(MyPaint / Krita / SAI)一律逐 dab 串行,这不是可选项。

做法D10):SmudgeEngine 注入一个 SurfaceSampler,集成层把绘制循环改为逐 dab 交错——sample → mix → paintDab → 下一颗saier/src/brush 现在是 paintDabs(addPoint(point)) 批量;smudge 路径改为单颗即画即采)。引擎仍「只产 dab」、不持后端句柄,采样经注入接口;确定性成立(采样是 surface 状态的纯函数,surface 状态确定演化)。

已落地(2026-06-30)SmudgeEngine 复用 SimpleBrushEngine 产几何 dab,并通过 prepareDab(dab, sample) 维护 smudge bucket、计算沉积色;saier/src/brush 检测 smudge 引擎后逐 dab 执行 sampleRegion(layerId, fromCircle(localDab), { dab }) → prepareDab → paintDab,因此同批第 N+1 颗 dab 能看到第 N 颗刚写入的 CPU tile 像素。默认预设新增 smudge / blender,RenderTexture 后端因无 sampleRegion 走明确门控。

Steps

  1. 引擎:实现 SmudgeEngine implements BrushEngine(集成层逐 dab 传入 sample),按上式维护 bucket、读 persistence / smudge / colorAmountbeginStroke 初始化桶(建议首点采样初始化,或透明起手,二选一并测)。
  2. 集成saier/src/brush 为 smudge 引擎走逐 dab 交错路径(即画即采),其余笔刷保持批量;smudge 笔迹照常 beginStroke/endStroke/applyPatch(bbox/tile undo 复用)。
  3. 预设 / factorycreateBrushEngineFromPreset 支持 engine: 'smudge'DEFAULT_BRUSH_PRESETSsmudge(纯抹,colorAmount≈0)与 blender 预设。
  4. 后端门控:smudge 需 sampleRegion,tile 后端提供(P7-00 已让 tile 成为默认,D11);显式选 RenderTexture 后端时 smudge 禁用(与 P7-07 协同)。
  5. 单测(浏览器真实 WebGL,参照 clipping-layers.browser.spec.ts):① 在红块上用 smudge 划向空白 → 颜色被拖出带状渐隐;② 红 / 蓝交界处划过 → 中间出现自然过渡的紫;③ persistence 越大拖痕越长;④ colorAmount 越大越接近纯上色;⑤ undo / redo 像素一致;⑥ 同输入确定性。

Acceptance

  • [x] smudge 能拖动已有颜色(拖痕随 persistence 变长)。
  • [x] 两色交界自然过渡(边界采样到中间色并沉积)。
  • [x] smudge 描边可独立 undo / redo,像素一致。
  • [x] 取色看到同批内刚落的上一 dab(交错路径生效);同输入确定性。

Out of scope

  • 湿边 / 纸纹(P7-05 / P7-06);GPU 后端 smudge(D11);UI(P7-07)。

Released under the MPL-2.0 License.