Give every mask an eye and a colour, and put the brush where the mask is
The first build of seeing a mask showed the selected layer's, in one global style, from a strip at the top of the panel. It answered the wrong question and answered it somewhere nobody looked. What a photographer asks of two masks is how they meet — where the sky's edge sits against the building's — and that needs both on screen at once, in colours that can be told apart. So each row of the stack has an eye, drawn in the colour its mask is shown in, and each mask has six swatches to choose that colour from. Several can be open at once; a new one comes up open, in the first colour nothing else is using. The style — tint, alpha, outline — is the one setting that stays global, above the stack, because three styles at once are three pictures that cannot be read against each other. Alpha now draws every shown mask, each in its colour, on black. In the pipeline a `Reveal` is a list of `(layer, colour)` rather than one layer, and every reveal block carries its own colour. The brush moves too. Select, Paint and Erase and the three sliders under them sat at the top of the panel, appeared only once a row was selected, and said nothing about which mask they acted on — so "how do I paint" and "how do I correct the model's outline" both had the same answer and nobody found it. They sit under the selected mask's parts now, beside the swatches, and on a subject or a category the hint says what a stroke there does: it becomes a part of this mask, joined to the model's, and can be taken out again. Eyes and colours are viewing state, on the session and not on the layer, so a photograph reopened has every eye closed — the stored-mask round-trip test asserts it.
This commit is contained in:
@@ -849,14 +849,7 @@ fn a_selection_with_no_adjustment_can_still_be_seen() {
|
||||
"the fixture must be a selection with nothing done to it"
|
||||
);
|
||||
|
||||
let pixels = render_revealing(
|
||||
&ctx,
|
||||
&stack,
|
||||
&Reveal {
|
||||
layer: "m1".into(),
|
||||
style: RevealStyle::Alpha,
|
||||
},
|
||||
);
|
||||
let pixels = render_revealing(&ctx, &stack, &Reveal::one("m1", RevealStyle::Alpha));
|
||||
|
||||
assert!(
|
||||
luma_at(&pixels, SIZE / 2, SIZE / 2) > 200,
|
||||
@@ -904,14 +897,7 @@ fn a_tint_colours_the_mask_and_leaves_the_rest_alone() {
|
||||
let mut stack = MaskStack::new();
|
||||
stack.push(MaskLayer::new("m1", middle()));
|
||||
|
||||
let pixels = render_revealing(
|
||||
&ctx,
|
||||
&stack,
|
||||
&Reveal {
|
||||
layer: "m1".into(),
|
||||
style: RevealStyle::Tint,
|
||||
},
|
||||
);
|
||||
let pixels = render_revealing(&ctx, &stack, &Reveal::one("m1", RevealStyle::Tint));
|
||||
|
||||
let at = |x: u32, y: u32| {
|
||||
let i = ((y * SIZE + x) * 4) as usize;
|
||||
@@ -947,14 +933,7 @@ fn an_outline_draws_the_boundary_and_not_the_interior() {
|
||||
let mut stack = MaskStack::new();
|
||||
stack.push(MaskLayer::new("m1", middle()));
|
||||
|
||||
let pixels = render_revealing(
|
||||
&ctx,
|
||||
&stack,
|
||||
&Reveal {
|
||||
layer: "m1".into(),
|
||||
style: RevealStyle::Edge,
|
||||
},
|
||||
);
|
||||
let pixels = render_revealing(&ctx, &stack, &Reveal::one("m1", RevealStyle::Edge));
|
||||
|
||||
// Where the line landed, along the row through the centre. Searched
|
||||
// rather than sampled at one place: the radial's edge crosses this row
|
||||
@@ -984,3 +963,62 @@ fn an_outline_draws_the_boundary_and_not_the_interior() {
|
||||
"and so is the picture outside it"
|
||||
);
|
||||
}
|
||||
|
||||
/// TRACES: FR-DEV-19c
|
||||
/// Two masks shown at once come out in two colours, each where its own mask
|
||||
/// is — which is what makes "where do these meet" a question the screen can
|
||||
/// answer.
|
||||
#[test]
|
||||
fn two_shown_masks_are_drawn_each_in_its_own_colour() {
|
||||
use dr_pipeline::mask::RevealedLayer;
|
||||
|
||||
let Some(ctx) = ctx() else {
|
||||
eprintln!("no adapter; skipping");
|
||||
return;
|
||||
};
|
||||
|
||||
// A left half and a right half, as two brush layers with one fat dab each.
|
||||
let half = |id: &str, x: f32| {
|
||||
let mut layer = MaskLayer::new(id, MaskSource::brush());
|
||||
paint(&mut layer, 0, false, &[(x, 0.5)]);
|
||||
layer
|
||||
};
|
||||
let mut stack = MaskStack::new();
|
||||
stack.push(half("left", 0.2));
|
||||
stack.push(half("right", 0.8));
|
||||
|
||||
let reveal = Reveal {
|
||||
layers: vec![
|
||||
RevealedLayer {
|
||||
layer: "left".into(),
|
||||
colour: [1.0, 0.0, 0.0],
|
||||
},
|
||||
RevealedLayer {
|
||||
layer: "right".into(),
|
||||
colour: [0.0, 0.0, 1.0],
|
||||
},
|
||||
],
|
||||
style: RevealStyle::Alpha,
|
||||
};
|
||||
let pixels = render_revealing(&ctx, &stack, &reveal);
|
||||
|
||||
let at = |x: u32| {
|
||||
let i = ((SIZE / 2 * SIZE + x) * 4) as usize;
|
||||
(pixels[i], pixels[i + 1], pixels[i + 2])
|
||||
};
|
||||
let (r, _, b) = at(SIZE / 5);
|
||||
assert!(
|
||||
r > 200 && b < 40,
|
||||
"the left mask reads red, got r={r} b={b}"
|
||||
);
|
||||
let (r, _, b) = at(SIZE * 4 / 5);
|
||||
assert!(
|
||||
b > 200 && r < 40,
|
||||
"the right mask reads blue, got r={r} b={b}"
|
||||
);
|
||||
let (r, g, b) = at(SIZE / 2);
|
||||
assert!(
|
||||
r < 40 && g < 40 && b < 40,
|
||||
"between them, alpha shows black: ({r}, {g}, {b})"
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user