Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

27 落笔

✅ 已验证 · 全部在 Krita 6.0.2.1 + PyQt6 + Xvfb 上实测

Node.paint*

node.paintLine(QPoint(x0, y0), QPoint(x1, y1), pressure0, pressure1)
node.paintPath(QPainterPath(...))
node.paintPolygon([QPointF(...), ...], strokeStyle, fillStyle)
node.paintRectangle(QRectF(...), strokeStyle, fillStyle)
node.paintEllipse(QRectF(...), strokeStyle, fillStyle)
node.paintAbility()          # "PAINT" / "VECTOR" / "UNPAINTABLE"

当前 brush preset + 当前前景色view.setCurrentBrushPreset / setForeGroundColor / setBrushSize / setPaintingOpacity)。

  • 画在你传的那个 node 上,与 active node 无关。 setActiveNode(L2)n1.paintLine(...) → n1 变 5438 px,L2 零变化。
  • pressure 真的驱动 brush engine:两头收尖的 pressure 曲线,粗细剖面 14 → 29 → 29 → 25 px。段间接缝不可见,可以逐段 paintLine 做 taper。
  • paintLineQPoint(整数),paintPathQPointF(浮点)。canvas.line() 用分段 paintPath 同时拿到浮点精度和粗细变化。
  • fillStyle 默认 "None",要填充传 "ForegroundColor"填充是二值的,不抗锯齿(边界带偏差 0.498)。
  • group 的 paintAbility()"UNPAINTABLE"

canvas.Session 的封装

方法做什么
stroke(pts, pressures, node)逐段 paintLine,pressure 线性过渡
path(pts, node, smooth=True)一次 paintPath,quadTo 平滑
line(pts, width, node, segments=3)分 3 段 paintPath,每段换笔径。段数不是越多越好(5 段 aliased 2.6%,12 段 14.5%)
dab(x, y, pressure, node)一个点
brush(color, size, opacity, flow, preset)不 pump 的快路径,相同值跳过
set_preset(name, size, opacity, flow)子串匹配,找不到打印警告;带 pump
set_color(r, g, b)带 pump

stroke()path() 的选择要看几何:长平滑曲线上 path() 干净 23–47%;短线段上反过来stroke() K90 0.032 最好,path() 0.0437 最差),点少时 quadTo 会改变形状。

吞吐

8192×12288,单层空白文档(probes/probe_stroke_budget.py):

操作吞吐
paintLine(~300px)450–850 笔/秒,与 size 无关
paintPath(8 点)310–390 笔/秒
不 pump 换色 + paintLine605 笔/秒
带 pump 换色 + paintLine71 笔/秒

真实图层树上(307 节点):path() 297 笔/秒,stroke() 逐段 130。每次调用约 233 KB 撤销栈,清不掉——预算是内存(第 19 章)。

8K 上填一块边缘平滑的色块

四条路同题对比(probes/probe_fill.py,判据是边界带里 |覆盖率−解析真值|,完美约 0.02–0.08,二值约 0.5):

路子边界带偏差耗时
矢量层 addShapesFromSvg0.08281.5s
select_mask(抗锯齿 mask)+ 42 笔0.33056.4s
select_mask(二值)+ 42 笔0.36681.4s
paintPolygon(fill)0.49810.8s
不加约束大笔硬涂0.4361

只有矢量层接近解析级。select_mask 留不住分数覆盖率,paintPolygon 是二值填充。

擦除

「用笔擦」做不到:view.setEraserMode(True)setCurrentBlendingMode("erase")node.setBlendingMode("erase")paintLine 全部无效(擦掉 0 px)。唯一有效的是清 alpha(s.erase(box=, mask=))。

这条判错过一次:一次测量显示 erase 擦掉了 2198 px。那是读到过期像素的伪影——换成读到稳定再判,三种齐刷刷是 0。

异步读数

image 操作是异步的。waitForDone() + refreshProjection() + pump 不保证 pixelData() 已更新。判「有没有生效」要循环读到连续两次相同(s.settle() / s.stable())。读一次就下结论可能得到相反答案。