feat(pen): brush opacity + highlighter multiply
Some checks failed
CI / Windows build (push) Has been cancelled

Honor each brush's opacity/blend so the brushes feel distinct
(closes TODO(brush-opacity)).

- Shared paint resolver: a stroke's color alpha is multiplied by its
  brush opacity; ballpoint/pencil opacity is tied to pressure
  (per-stroke average this increment) so a ballpoint reads lighter
  than a solid fountain pen.
- Highlighter paints with BlendMode.multiply and draws once, so
  cross-stroke overlap darkens like a real marker while self-overlap
  doesn't.
- Applied across BOTH render paths (PenCanvas static/live painters and
  the PDF _PageOverlayPainter).

Pencil paper-grain texture still deferred (TODO brush-texture); brush
kind is not yet serialized (TODO brush-persist — next). analyze clean,
tests green.
This commit is contained in:
2026-06-24 23:27:11 +08:00
parent 24d13642fd
commit 6c2dd71b82
7 changed files with 349 additions and 65 deletions

View File

@@ -39,13 +39,7 @@ class LiveInkPainter extends CustomPainter {
isComplete: false, thinning: thinning);
if (path.getBounds().isEmpty) return;
canvas.drawPath(
path,
Paint()
..color = Color(stroke.color)
..style = PaintingStyle.fill
..isAntiAlias = true,
);
canvas.drawPath(path, paintForEditorStroke(stroke));
}
@override

View File

@@ -58,13 +58,10 @@ class StaticInkPainter extends CustomPainter {
final path = buildStrokeOutline(stroke, pageSize,
isComplete: true, thinning: thinning);
if (path.getBounds().isEmpty) continue;
rec.drawPath(
path,
Paint()
..color = Color(stroke.color)
..style = PaintingStyle.fill
..isAntiAlias = true,
);
// One drawPath per stroke ⇒ highlighter self-overlap never darkens;
// cross-stroke overlap darkens via BlendMode.multiply (closes
// TODO(brush-opacity); shared resolver with the live + PenCanvas paths).
rec.drawPath(path, paintForEditorStroke(stroke));
}
return recorder.endRecording();
});