diff --git a/crates/engine/src/image_cmds.rs b/crates/engine/src/image_cmds.rs index 2683b9b0b8..b49ad9ae4f 100644 --- a/crates/engine/src/image_cmds.rs +++ b/crates/engine/src/image_cmds.rs @@ -866,7 +866,13 @@ fn duplicate(s: &mut Session, p: &Value) -> Result { let vals: Vec = buf.px.iter().flat_map(|q| photocraft_raster::from_rgba(&fmt, *q)).collect(); surf.write_region(doc.bounds(), &vals); surf.prune(); - doc.layers = vec![Layer::new("Background", LayerContent::Raster(surf))]; + // As Photoshop: a source with a Background gives one (opaque, locked); otherwise a plain + // "Layer 1" that keeps the composite's transparency. + let has_background = doc.background().is_some(); + let mut merged = Layer::new(if has_background { "Background" } else { "Layer 1" }, LayerContent::Raster(surf)); + merged.locks.transparency = has_background; + merged.locks.position = has_background; + doc.layers = vec![merged]; // A merged-only copy has no original layers for saved comps to restore. // Don't carry dangling comp state into a single-layer flattened document. doc.layer_comps.clear(); @@ -1832,6 +1838,22 @@ mod tests { assert_eq!(doc(&s).depth, SampleType::U8); } + /// Duplicate › Merged Layers Only gives one locked Background when the source has one and a + /// plain "Layer 1" (keeping transparency) when it doesn't, as Photoshop does. + #[test] + fn merged_duplicate_is_a_background_only_when_the_source_has_one() { + let mut s = session(); + s.execute("image.duplicate", json!({"mergedOnly": true})).unwrap(); + let d = doc(&s); + assert!(d.is_background(d.layers[0].id), "source has a Background: the merged copy is one"); + let mut s = Session::new(); + s.execute("file.new", json!({"width": 8, "height": 8, "background": "transparent"})).unwrap(); + s.execute("image.duplicate", json!({"mergedOnly": true})).unwrap(); + let d = doc(&s); + assert!(d.background().is_none(), "transparent source: no Background in the copy"); + assert_eq!(d.layers[0].name, "Layer 1"); + } + #[test] fn duplicate_document() { let mut s = session(); diff --git a/crates/engine/src/photo_cmds.rs b/crates/engine/src/photo_cmds.rs index c729afd0d5..222866fdd0 100644 --- a/crates/engine/src/photo_cmds.rs +++ b/crates/engine/src/photo_cmds.rs @@ -807,7 +807,11 @@ fn merge_to_hdr(s: &mut Session, p: &Value) -> Result { } let mut surf = Surface::new(fmt); surf.write_region(area, &data); - doc.layers.push(Layer::new("Background", LayerContent::Raster(surf))); + // The merged result is opaque: a real Background, as Crop and Straighten makes. + let mut bg = Layer::new("Background", LayerContent::Raster(surf)); + bg.locks.transparency = true; + bg.locks.position = true; + doc.layers.push(bg); let idx = s.add_document(doc, None); let ev0 = exposures[order[0]]; Ok(json!({ @@ -1094,6 +1098,7 @@ mod tests { assert!((ev[1] - 2.0).abs() < 1e-6 && (ev[2] - 4.0).abs() < 1e-6, "{ev:?}"); let d = &s.active().unwrap().doc; assert_eq!(d.depth, SampleType::F32); + assert!(d.is_background(d.layers[0].id), "the merged layer is a real (locked) Background"); let l = d.layers[0].surface().unwrap(); // Linear output: ratios across the ramp follow the radiance. let (a, b) = (l.rgba(20, 30)[1] as f64, l.rgba(80, 30)[1] as f64);