Show a mask as soon as it is made
Benchmarks / CPU and I/O (per commit) (push) Successful in 3m52s
Benchmarks / Frame budget (on demand) (push) Skipped
Build and test / Desktop (Linux) (push) Failing after 39m53s
Build and test / Layer separation (push) Successful in 1m0s
🐳 Android image / Build and push (push) Successful in 4s
Build and test / android-image (push) Successful in 4s
Traceability / Requirement traces (push) Successful in 46s
Build and test / Android (aarch64) (push) Successful in 25m19s
Benchmarks / CPU and I/O (per commit) (push) Successful in 3m52s
Benchmarks / Frame budget (on demand) (push) Skipped
Build and test / Desktop (Linux) (push) Failing after 39m53s
Build and test / Layer separation (push) Successful in 1m0s
🐳 Android image / Build and push (push) Successful in 4s
Build and test / android-image (push) Successful in 4s
Traceability / Requirement traces (push) Successful in 46s
Build and test / Android (aarch64) (push) Successful in 25m19s
Choosing a category is asking what it selected, and for a subject or a category that question had no other answer on screen: the model's outline is not derivable from anything visible, a fresh layer carries no adjustment to judge it by, and the list it was chosen from says "architecture 23%" without saying which 23%. The control that draws the mask existed but had to be found and pressed, a panel's height away from the list the choice was made in. So a new layer arrives with its mask showing, from the resting position only. Somebody who has chosen the alpha or the outline keeps it, and nothing re-arms in the background — every caller is a press that asked for a new mask. That makes the canvas depend on how a layer arrived, which is correct and worth stating: a session that has just made a mask draws a frame that a session which read the same mask out of a sidecar does not. Viewing state is not edit state and does not travel in a file, and `a_stored_mask_renders_exactly_what_the_model_rendered` now says so at both ends.
This commit is contained in:
@@ -2523,6 +2523,28 @@ impl DevelopSession {
|
|||||||
.and_then(|i| RevealStyle::ALL.get(i).copied());
|
.and_then(|i| RevealStyle::ALL.get(i).copied());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// TRACES: FR-DEV-19c
|
||||||
|
/// Show the mask of a layer that has just been made.
|
||||||
|
///
|
||||||
|
/// **Making a mask is asking what it selected**, and for a subject or a
|
||||||
|
/// category that question has no other answer: the model's outline is not
|
||||||
|
/// derivable from anything on screen, the layer carries no adjustment yet,
|
||||||
|
/// and the list it was chosen from says "architecture 23%" and nothing
|
||||||
|
/// about *which* 23%. So the mask appears with the layer rather than
|
||||||
|
/// waiting to be asked for a second time.
|
||||||
|
///
|
||||||
|
/// Only from the resting position, and only on an explicit action — the
|
||||||
|
/// same rule arming a brush follows. Somebody who has switched to the
|
||||||
|
/// alpha or the outline keeps it, and this never re-arms in the background;
|
||||||
|
/// the trap `Masking.overlay-hidden` documents is an automatic reveal that
|
||||||
|
/// undoes a switch a photographer turned off, and every caller of this is
|
||||||
|
/// a press that asked for a new mask.
|
||||||
|
fn show_new_mask(&mut self) {
|
||||||
|
if self.reveal_style.is_none() {
|
||||||
|
self.reveal_style = Some(dr_pipeline::mask::RevealStyle::Tint);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ----------------------------------------------------------------------
|
// ----------------------------------------------------------------------
|
||||||
// The region overlay
|
// The region overlay
|
||||||
// ----------------------------------------------------------------------
|
// ----------------------------------------------------------------------
|
||||||
@@ -3356,6 +3378,7 @@ impl DevelopSession {
|
|||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
self.active_masks = vec![id.clone()];
|
self.active_masks = vec![id.clone()];
|
||||||
|
self.show_new_mask();
|
||||||
self.history
|
self.history
|
||||||
.record(&self.graph, Edit::Action(labels::step::MASK_ADDED));
|
.record(&self.graph, Edit::Action(labels::step::MASK_ADDED));
|
||||||
Some(id)
|
Some(id)
|
||||||
@@ -3399,6 +3422,7 @@ impl DevelopSession {
|
|||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
self.active_masks = vec![id.clone()];
|
self.active_masks = vec![id.clone()];
|
||||||
|
self.show_new_mask();
|
||||||
self.history
|
self.history
|
||||||
.record(&self.graph, Edit::Action(labels::step::MASK_ADDED));
|
.record(&self.graph, Edit::Action(labels::step::MASK_ADDED));
|
||||||
Some(id)
|
Some(id)
|
||||||
@@ -3429,6 +3453,7 @@ impl DevelopSession {
|
|||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
self.active_masks = vec![id.clone()];
|
self.active_masks = vec![id.clone()];
|
||||||
|
self.show_new_mask();
|
||||||
self.history
|
self.history
|
||||||
.record(&self.graph, Edit::Action(labels::step::MASK_ADDED));
|
.record(&self.graph, Edit::Action(labels::step::MASK_ADDED));
|
||||||
Some(id)
|
Some(id)
|
||||||
@@ -3460,6 +3485,7 @@ impl DevelopSession {
|
|||||||
}
|
}
|
||||||
self.active_masks = vec![id.clone()];
|
self.active_masks = vec![id.clone()];
|
||||||
self.active_part = 0;
|
self.active_part = 0;
|
||||||
|
self.show_new_mask();
|
||||||
self.history
|
self.history
|
||||||
.record(&self.graph, Edit::Action(labels::step::MASK_ADDED));
|
.record(&self.graph, Edit::Action(labels::step::MASK_ADDED));
|
||||||
Some(id)
|
Some(id)
|
||||||
@@ -3486,6 +3512,7 @@ impl DevelopSession {
|
|||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
self.active_masks = vec![id.clone()];
|
self.active_masks = vec![id.clone()];
|
||||||
|
self.show_new_mask();
|
||||||
self.history
|
self.history
|
||||||
.record(&self.graph, Edit::Action(labels::step::MASK_ADDED));
|
.record(&self.graph, Edit::Action(labels::step::MASK_ADDED));
|
||||||
Some(id)
|
Some(id)
|
||||||
@@ -6543,6 +6570,16 @@ mod tests {
|
|||||||
|
|
||||||
let mut live = session_with_a_left_half_subject(&ctx);
|
let mut live = session_with_a_left_half_subject(&ctx);
|
||||||
let id = live.add_subject_mask(0).expect("a subject layer");
|
let id = live.add_subject_mask(0).expect("a subject layer");
|
||||||
|
// TRACES: FR-DEV-19c
|
||||||
|
// Making a mask shows it (`show_new_mask`), and this test is about the
|
||||||
|
// pixels the *edit* produces. Put away here rather than left on, and
|
||||||
|
// the asymmetry is the point rather than an inconvenience: the reveal
|
||||||
|
// is how somebody is looking at a photograph, so a session that has
|
||||||
|
// just made a layer legitimately draws a frame that a session which
|
||||||
|
// read the same layer out of a file does not. Both of those are
|
||||||
|
// correct, and only one of them is what a stored raster has to
|
||||||
|
// reproduce.
|
||||||
|
live.set_mask_view(0);
|
||||||
live.graph
|
live.graph
|
||||||
.masks_mut()
|
.masks_mut()
|
||||||
.get_mut(&id)
|
.get_mut(&id)
|
||||||
@@ -6569,6 +6606,16 @@ mod tests {
|
|||||||
"the point: nothing has run a model in this session"
|
"the point: nothing has run a model in this session"
|
||||||
);
|
);
|
||||||
reopened.apply_version(parsed.default_version().expect("a version"));
|
reopened.apply_version(parsed.default_version().expect("a version"));
|
||||||
|
// TRACES: FR-DEV-19c
|
||||||
|
// And a sidecar carries no viewing state: a photograph reopened is not
|
||||||
|
// reopened with its masks tinted red. Checked here because this is the
|
||||||
|
// one test that puts an edit through a file and renders both ends, so
|
||||||
|
// it is where the property would first go wrong.
|
||||||
|
assert_eq!(
|
||||||
|
reopened.mask_view(),
|
||||||
|
0,
|
||||||
|
"restoring an edit must not turn an overlay on"
|
||||||
|
);
|
||||||
let from_the_file = read_back(&ctx, &reopened.render(64, 64).expect("render"));
|
let from_the_file = read_back(&ctx, &reopened.render(64, 64).expect("render"));
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
|||||||
@@ -1027,14 +1027,11 @@ pub(crate) fn wire(
|
|||||||
// until something is painted into it, so a press that made a row
|
// until something is painted into it, so a press that made a row
|
||||||
// and left the pointer in "select" would be indistinguishable from
|
// and left the pointer in "select" would be indistinguishable from
|
||||||
// a button that did nothing. The same reasoning as `on_part_added`
|
// a button that did nothing. The same reasoning as `on_part_added`
|
||||||
// below, and the tool's own handler turns the mask view on.
|
// below. Showing the mask is `DevelopSession::show_new_mask`'s
|
||||||
|
// job and every route to a new layer goes through it, so only the
|
||||||
|
// tool is set here.
|
||||||
if made {
|
if made {
|
||||||
w.global::<Masking>().set_tool(1);
|
w.global::<Masking>().set_tool(1);
|
||||||
if let Some(s) = session.borrow_mut().as_mut() {
|
|
||||||
if s.mask_view() == 0 {
|
|
||||||
s.set_mask_view(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
sync(&w, &session);
|
sync(&w, &session);
|
||||||
sync_rows(&w, &rows, &session);
|
sync_rows(&w, &rows, &session);
|
||||||
|
|||||||
Reference in New Issue
Block a user