fix(studio): a heal stroke is one mask, and the fill comes from its rim
The dust brush had two faults and the second was hidden behind the first. A stroke was a row of independent repairs. Every circle the brush laid down found a patch of its own somewhere else in the frame and pasted it, so a stroke of twenty spots was twenty pastes with twenty rims to match, and what showed on the photo was the run of circles itself. The mask was their union on paper but not on the picture. And the mask was not a stroke either. The brush only ever dropped a spot where the pointer's own sample fell, and a fast drag reports a sample every few centimetres of screen — several brush radii apart. Circles that far apart do not touch, so their union was the row of dots the tool was complained about. The segment the pointer travelled is now walked at the brush's own spacing and a spot is laid at every step, whatever speed the stroke was drawn at. With the mask one shape, the patch had nothing left to justify it. The fill is now a push-pull pyramid over the whole mask: the painted pixels are cut out of the frame as a hole, the remaining pixels are halved and halved again until a level is small enough that no hole is left in it, and each level is drawn back over the one below it. What comes up out of that is the photo where the photo was, and the light of the mask's own rim carried inward where it was not — nothing is copied from anywhere, and a repair matches the grade, the blur and the grain around it because those are the pixels it was made from. Non-destructive as before: the recipe still holds the circles, and the preview and the full-size export run the two passes in engine/exportEngine.ts, the shaders and the mask stops in shared/utils/heal.ts. A recipe written by the old brush still loads; the patch coordinates it stored are simply not read any more.
This commit is contained in:
@@ -698,10 +698,9 @@ button.hint:disabled { cursor: default; }
|
||||
/* FX's brush — HEAL and MOSAIC, one layer. It takes the pointer the way the
|
||||
eyedropper's does, so a click paints and a drag paints a run instead of
|
||||
panning; the circle under the cursor is the size the wheel has set, the trail
|
||||
is the stroke being drawn, and every spot on the photo keeps its own circle.
|
||||
A HEAL spot shows the pair the renderer works with — the hole it fills, and
|
||||
the patch it borrowed; a MOSAIC spot has nothing beside it, since what it
|
||||
covers is gone. */
|
||||
is the stroke being drawn, and every spot on the photo keeps a print of its
|
||||
own. A repair and a hidden patch look the same here — what a spot means is
|
||||
the renderer's business, and neither of them borrows a second circle. */
|
||||
.brush-layer {
|
||||
position: absolute;
|
||||
border-radius: var(--radius-sm);
|
||||
@@ -710,8 +709,7 @@ button.hint:disabled { cursor: default; }
|
||||
}
|
||||
.brush-set { position: absolute; inset: 0; pointer-events: none; }
|
||||
.brush-cursor,
|
||||
.brush-spot,
|
||||
.brush-source {
|
||||
.brush-spot {
|
||||
position: absolute;
|
||||
border-radius: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
@@ -739,14 +737,14 @@ button.hint:disabled { cursor: default; }
|
||||
stroke-opacity: 0.45;
|
||||
}
|
||||
.brush-spot { border: 1px solid rgba(255, 255, 255, 0.85); box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.45); }
|
||||
.brush-source { border: 1px dashed rgba(255, 255, 255, 0.7); }
|
||||
/* The spots a stroke left are drawn as the one mark it was painted as: a band
|
||||
of the brush's own width through them, in the same translucent white with the
|
||||
same dark hairline the circles wear, so the band reads on a white photo too.
|
||||
The dashed one is the patch HEAL borrowed, which is a band of its own and is
|
||||
meant to look borrowed. A stroke is one path, so where it crosses itself its
|
||||
translucency is laid once rather than twice — that doubling-up is exactly
|
||||
what a row of overlapping circles looks like. */
|
||||
of the brush's own width through them, in the same white with the same dark
|
||||
hairline the circles wear, so the band reads on a white photo too. The wash
|
||||
stays faint — the hairline is what carries it on a white photo, and a heavier
|
||||
wash turns into an opaque stripe over a warm one.
|
||||
A stroke is one path, so where it crosses itself its translucency is laid once
|
||||
rather than twice — that doubling-up is exactly what a row of overlapping
|
||||
circles looks like. */
|
||||
.brush-band {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
@@ -758,21 +756,18 @@ button.hint:disabled { cursor: default; }
|
||||
}
|
||||
.brush-band path {
|
||||
fill: none;
|
||||
stroke: rgba(255, 255, 255, 0.18);
|
||||
stroke: rgba(255, 255, 255, 0.08);
|
||||
stroke-linecap: round;
|
||||
stroke-linejoin: round;
|
||||
}
|
||||
.brush-band.band-src path { stroke: rgba(255, 255, 255, 0.12); stroke-dasharray: 5 5; }
|
||||
/* A spot the band stands for: its box on the photo is still the spot's, its own
|
||||
edge is the band's now. */
|
||||
.brush-spot[data-band],
|
||||
.brush-source[data-band] { border-color: transparent; box-shadow: none; }
|
||||
.brush-spot[data-band] { border-color: transparent; box-shadow: none; }
|
||||
/* A repair the pointer is not on and no press has chosen: the ring would be one
|
||||
more circle on a photo that already carries as many repairs as the user laid,
|
||||
so an idle spot is the patch it borrowed and a faint print of the place it
|
||||
mended — soft, because it is a record of an edit rather than something to
|
||||
aim at, and it is the ring under the pointer that says the spot can be taken
|
||||
hold of again. */
|
||||
so an idle spot is a faint print of the mark it made — soft, because it is a
|
||||
record of an edit rather than something to aim at, and it is the ring under
|
||||
the pointer that says the spot can be taken hold of again. */
|
||||
.brush-spot[data-idle] {
|
||||
border-color: transparent;
|
||||
background: rgba(127, 127, 127, 0.14);
|
||||
|
||||
Reference in New Issue
Block a user