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:
+57
-3
@@ -582,7 +582,23 @@ export global Masking {
|
||||
in property <[PartRow]> parts;
|
||||
/// TRACES: FR-DEV-19b
|
||||
/// What a drag on the photograph does: 0 selects, 1 paints, 2 erases.
|
||||
///
|
||||
/// Written by Rust and read here, like every other `in` property on this
|
||||
/// global: `tool-picked` is what the strip reports, and the answer comes
|
||||
/// back through this. A version of the handler that recorded nothing left
|
||||
/// the strip stuck on "Select" and the brush reachable from no control.
|
||||
in property <int> tool: 0;
|
||||
/// TRACES: FR-DEV-19c
|
||||
/// How the selected layer's mask is drawn over the photograph: 0 not at
|
||||
/// all, then tint, alpha and edge — `dr_pipeline::mask::RevealStyle::ALL`
|
||||
/// shifted by one so that zero can mean off.
|
||||
///
|
||||
/// **Not `overlay-hidden`.** That switch belongs to the region overlay,
|
||||
/// which is a picture of what the *model detected*; this is the mask a
|
||||
/// layer actually resolves to, feather, morphology, invert and all. Two
|
||||
/// overlays that look alike and mean different things is worse than
|
||||
/// either, so they are named apart and switched apart.
|
||||
in property <int> mask-view: 0;
|
||||
/// The brush: radius as a fraction of the frame's shorter edge, hardness,
|
||||
/// and flow.
|
||||
in property <float> brush-radius: 0.05;
|
||||
@@ -630,6 +646,8 @@ export global Masking {
|
||||
|
||||
/// TRACES: FR-DEV-19b
|
||||
callback tool-picked(int);
|
||||
/// TRACES: FR-DEV-19c
|
||||
callback mask-view-picked(int);
|
||||
callback brush-changed(float, float, float);
|
||||
/// TRACES: FR-DEV-19a
|
||||
callback part-selected(string, int);
|
||||
@@ -638,6 +656,8 @@ export global Masking {
|
||||
callback part-added(string, int);
|
||||
|
||||
callback add-gradient(bool);
|
||||
/// TRACES: FR-DEV-19b
|
||||
callback add-brush();
|
||||
/// `true` asks for the colour range rather than the brightness one.
|
||||
callback add-range(bool);
|
||||
callback add-subject(int);
|
||||
@@ -680,6 +700,32 @@ export component MaskPanel inherits Rectangle {
|
||||
picked(i) => { Masking.tool-picked(i); }
|
||||
}
|
||||
|
||||
// TRACES: FR-DEV-19c
|
||||
// **Seeing the mask, which every tool above is unusable without.**
|
||||
// Nobody can refine an edge they are not being shown, and until this
|
||||
// existed the only thing drawn on the photograph was the model's
|
||||
// detections — which know nothing of a layer's feather, its falloff,
|
||||
// its morphology, its invert or its opacity, and nothing at all about
|
||||
// a gradient or a stroke.
|
||||
//
|
||||
// Four chips rather than a toggle because the three styles answer
|
||||
// three different questions and no one of them answers all three: a
|
||||
// tint says whether the right thing is selected, an alpha says where
|
||||
// the edge is, an outline says whether that edge is registered against
|
||||
// detail the other two hide.
|
||||
//
|
||||
// Beside the tool strip and under the same condition, because both
|
||||
// describe one selected mask — and `columns: 2` for the reason
|
||||
// `ChipGrid` exists: four chips in a row would set the width of the
|
||||
// develop column and take every other panel's controls off the edge.
|
||||
if Develop.enabled && Masking.parts.length > 0: Segmented {
|
||||
label: "Show mask";
|
||||
options: ["Off", "Tint", "Alpha", "Edge"];
|
||||
columns: 2;
|
||||
selected: Masking.mask-view;
|
||||
picked(i) => { Masking.mask-view-picked(i); }
|
||||
}
|
||||
|
||||
if Develop.enabled && Masking.tool > 0: SliderRow {
|
||||
label: "Size";
|
||||
value: Masking.brush-radius;
|
||||
@@ -752,10 +798,18 @@ export component MaskPanel inherits Rectangle {
|
||||
// a mask is judged against the photograph under it, and you cannot see
|
||||
// that photograph through the thing describing it.
|
||||
//
|
||||
// Reads as its own action rather than its state: "Show the mask" is
|
||||
// what pressing it will do, not what is currently true.
|
||||
// Reads as its own action rather than its state: "Show what was found"
|
||||
// is what pressing it will do, not what is currently true.
|
||||
//
|
||||
// TRACES: FR-DEV-19c
|
||||
// **Named for what it actually hides**, which is not the mask. This
|
||||
// said "Show the mask" / "Hide the mask" while switching the
|
||||
// false-coloured picture of what the *model detected* — and now that
|
||||
// there is a control which really does show a mask ("Show mask",
|
||||
// above), two things called the same thing and meaning different ones
|
||||
// would be worse than either.
|
||||
if Develop.enabled && Masking.segmented: Button {
|
||||
text: Masking.overlay-hidden ? "Show the mask" : "Hide the mask";
|
||||
text: Masking.overlay-hidden ? "Show what was found" : "Hide what was found";
|
||||
clicked => { Masking.overlay-hidden = !Masking.overlay-hidden; }
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user