fix(heal): cut the whole brush out of the mask, so the repair takes its colour from the photo and not from the spot it is repairing

This commit is contained in:
2026-10-15 00:55:01 +07:00
parent 678d3811fc
commit 8809eb84b0
2 changed files with 51 additions and 4 deletions
+15 -4
View File
@@ -67,19 +67,30 @@ function num(v: unknown, fallback: number): number {
}
// The first of the fill's two passes: the photo with the painted pixels taken
// out of it. The colour of every pixel is kept and its alpha drops to what the
// brush did not touch, which is what turns the mask into a hole the second pass
// can see the rim of. Written as premultiplied — rgb times that alpha — because
// out of it. The colour of every pixel is kept and its alpha drops to nothing
// over the brush, which is what turns the mask into a hole the second pass can
// see the rim of. Written as premultiplied — rgb times that alpha — because
// that is what every surface here holds, and the blur below is built on it: a
// blur of premultiplied pixels is a weighted average of the pixels that are
// still there, with the hole weighing nothing.
//
// The cut is the WHOLE stroke, and it is a step rather than the mask's own fade.
// The fill below reads the rim of this hole, so whatever is left standing inside
// it is weighed into the repair. Cut at the mask's fade instead — `a = 1 -
// mask.a`, the obvious reading of "take the mask out" — and the outer quarter of
// every spot stays behind at partial weight. A brush aimed at a blemish has the
// blemish's own rim in exactly that band, so the repair came back in the colour
// of the thing being repaired: on a grey ground with a red blemish, the obvious
// cut is 4.1 levels off the ground and the step is 1.1 (measured, the cut
// variants in heal-colour-pass.mjs). The threshold is lightcraft's own: the
// pixels a heal may read its tone from are the ones at alpha < 0.02 (spots.rs).
export const HEAL_CUT_SKSL = `
uniform shader img;
uniform shader mask;
half4 main(float2 pos) {
half4 c = img.eval(pos);
half a = 1.0 - mask.eval(pos).a;
half a = mask.eval(pos).a < 0.02 ? 1.0 : 0.0;
half3 straight = c.a > 0.0001 ? c.rgb / c.a : c.rgb;
return half4(straight * a, a);
}