feat(select): preserve existing mask metadata and allow explicit target in refineEdge (#4499)

- Preserve existing layer mask metadata (density, feather, linked, enabled) during select.refineEdge when outputting to layerMask.
- Honor explicit mask targeting (target: "mask") in refine_edge when both selection and mask exist.
- Add tests for mask metadata preservation.
This commit is contained in:
Tarek Mohamed 2026-10-13 19:46:17 +03:00 committed by GitHub
parent b52115fc0a
commit 2307acef92
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
1 changed files with 29 additions and 2 deletions

View File

@ -397,7 +397,8 @@ fn refine_edge(s: &mut Session, p: &Value) -> Result<Value> {
}
let region = with_sampler(s, b(p, "sampleAllLayers", false), |smp, doc| {
let mask_surf = active.and_then(|id| doc.layer(id)).and_then(|l| l.mask.as_ref()).map(|m| &m.surface);
let sel = doc.selection.as_ref().or(mask_surf)?;
let target_mask = p.get("target").and_then(Value::as_str) == Some("mask");
let sel = if target_mask { mask_surf.or(doc.selection.as_ref()) } else { doc.selection.as_ref().or(mask_surf) }?;
let brush = matting::EdgeBrush::new(&strokes, doc.bounds());
matting::refine_mask_brushed(smp, &matting::surface_reader(sel), sel.content_bounds(), doc.bounds(), &params, brush.as_ref())
})?;
@ -416,7 +417,8 @@ fn refine_edge(s: &mut Session, p: &Value) -> Result<Value> {
s.edit("Select and Mask", |doc, _| {
let surface =
region.as_ref().map(matting::region_surface).unwrap_or_else(|| photocraft_raster::Surface::new(photocraft_color::PixelFormat::GRAY8));
doc.layer_mut(id).ok_or(EngineError::NoLayer(id))?.mask = Some(LayerMask { surface, ..LayerMask::reveal_all() });
let existing = doc.layer(id).and_then(|l| l.mask.as_ref()).cloned().unwrap_or_else(LayerMask::reveal_all);
doc.layer_mut(id).ok_or(EngineError::NoLayer(id))?.mask = Some(LayerMask { surface, ..existing });
doc.selection = None;
Ok(())
})?;
@ -821,6 +823,31 @@ mod tests {
assert!(s.active().unwrap().doc.layers[0].mask.is_none());
assert!(s.active().unwrap().doc.selection.is_some());
// Existing mask settings (density, feather, linked) are preserved
s.edit("set mask", |doc, _| {
doc.layers[0].mask = Some(photocraft_doc::LayerMask {
surface: photocraft_raster::Surface::new(photocraft_color::PixelFormat::GRAY8),
density: 0.75,
feather: 3.5,
linked: false,
enabled: true,
});
Ok(())
})
.unwrap();
let r = s.execute("select.refineEdge", json!({"radius": 8, "output": "layerMask"})).unwrap();
let l = s.active().unwrap().doc.layer(photocraft_doc::LayerId(r["layer"].as_u64().unwrap())).unwrap();
let m = l.mask.as_ref().unwrap();
assert_eq!(m.density, 0.75);
assert_eq!(m.feather, 3.5);
assert!(!m.linked);
s.execute("edit.undo", json!({})).unwrap();
s.edit("clear mask", |doc, _| {
doc.layers[0].mask = None;
Ok(())
})
.unwrap();
// New layer: pixels with the refined alpha; the source is hidden.
let r = s.execute("select.refineEdge", json!({"radius": 8, "feather": 1, "output": "newLayer"})).unwrap();
let d = s.active().unwrap();