Show the photographer the mask they are shaping

Nobody can refine an edge they are not being shown. The only thing drawn on
the canvas was the region overlay — a false-coloured picture of what the model
*detected* — which knows nothing of a layer's feather, its falloff, its
morphology, its invert or its opacity, and nothing at all about a gradient, a
range or a stroke. Every control added for mask editing therefore acted on
something invisible, which is why the whole feature reads as absent rather
than as unfinished.

A layer's finished mask now draws over the photograph in one of three styles:
a tint for whether the right thing is selected, an alpha for where the edge
is, an outline for whether that edge is registered against the detail the
other two hide.

The hard part is not the shader. A selection with no adjustment on it changes
no pixel, so it is not active, so it holds no slice of the mask array and is
never rasterised — and that is exactly the layer somebody wants to look at,
for the whole of the time between choosing a subject and deciding what to do
to it. So `MaskStack::rendered` is `active()` plus the layer being looked at,
and the rasteriser, the composer and the distance-field builder all index by
position in it. Which is also why the design's "two uniforms, no recompile" is
not available: a uniform can select a slot, it cannot conjure one.

The reveal is never on the graph. It reaches the pipeline as an argument to
`compose_revealing`, and `compose_for` — which the exporter, the thumbnail and
the neutral probe all call — has no way to ask for one. A flag on the graph
would have been shorter, would have type-checked, and would have been one
forgotten reset away from a red tint baked into an exported file.

And the tools that shape a mask now arm. `Masking.tool` is an `in` property
only Rust may write, and the handler wrote nothing back, so the strip reported
"Select" however many times Paint was pressed and the paint area was never
enabled — the brush, the parts and the whole of FR-DEV-19b reachable from no
control in the application.

The region overlay stands down while a mask is being shown, and its button now
says what it hides: two overlays that look alike and mean different things is
worse than either.
This commit is contained in:
2026-09-10 20:28:06 +02:00
parent 193b35a249
commit c045702a47
10 changed files with 927 additions and 51 deletions
+14
View File
@@ -517,6 +517,20 @@ hardness and flow the photographer sets. Strokes are stored as normalised source
rasterised on the device, so a correction stays on what it was painted on through a crop, a zoom and
an export at any size. A whole stroke is one step in the history.
**FR-DEV-19c — Seeing the mask.** The mask a selected layer actually resolves to shall be drawable
over the photograph, in a tint, as an alpha, or as an outline. What is shown is the finished mask —
every part folded, with the layer's feather, falloff, morphology, invert and opacity applied — and
it is shown for a layer that carries no adjustment yet, which is the state every mask is in for its
first few seconds. No rendered output ever carries it: the reveal is a property of looking at an
edit, not of the edit, so it reaches the pipeline through the composition that draws the canvas and
through no other.
Nobody can refine an edge they are not being shown. Before this the only thing drawn on the
photograph was FR-DEV-3's region overlay — a false-coloured picture of what the *model detected*,
which knows nothing of a layer's shaping and nothing at all about a gradient, a range or a stroke —
so choosing a subject or a category produced a layer whose extent was invisible, and every control
in FR-DEV-19a and FR-DEV-19b acted on something the photographer could not see.
### 3.4 Display and interaction
**FR-DSP-1 — Proxy-resolution rendering.** The develop view renders at the resolution actually