mirror of
https://github.com/storytold/photocraft.git
synced 2026-11-12 00:49:14 -06:00
lift() converted the whole layer to a format with alpha before reading the selection's rectangle, so on a 24 MP Background every Layer via Copy cost about a second whatever the selection's size, and it sampled the selection mask one pixel at a time. It now reads just that rectangle and the mask's rectangle. 6000x4000 Background, 2000x1500 selection, release: Layer via Copy 984 ms -> 30 ms. Select All then Layer via Copy: 212 ms. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
1ae11a4bb1
commit
d64f809e4d
@@ -68,13 +68,26 @@ fn lift(src: &Surface, sel: Option<&Surface>, canvas: Rect) -> Clip {
|
||||
if area.is_empty() {
|
||||
return Clip { surface: out, bounds: Rect::EMPTY };
|
||||
}
|
||||
let conv = src.convert(with_alpha);
|
||||
let mut px = conv.read_region(area);
|
||||
// Only the selected area is read: converting the whole layer (a 24 MP Background gaining
|
||||
// alpha) cost about a second per Layer via Copy (#668).
|
||||
let n = with_alpha.channels();
|
||||
let mut px = if fmt.alpha {
|
||||
src.read_region(area)
|
||||
} else {
|
||||
let opaque = src.read_region(area);
|
||||
let k = fmt.channels();
|
||||
let mut v = Vec::with_capacity(opaque.len() / k.max(1) * n);
|
||||
for p in opaque.chunks_exact(k.max(1)) {
|
||||
v.extend_from_slice(p);
|
||||
v.push(1.0);
|
||||
}
|
||||
v
|
||||
};
|
||||
if let Some(m) = sel {
|
||||
let w = area.width() as usize;
|
||||
for (i, p) in px.chunks_exact_mut(n).enumerate() {
|
||||
p[n - 1] *= m.sample_channel(area.x0 + (i % w) as i32, area.y0 + (i / w) as i32, 0);
|
||||
let mask = m.read_region(area);
|
||||
let mk = m.format().channels().max(1);
|
||||
for (p, a) in px.chunks_exact_mut(n).zip(mask.chunks_exact(mk)) {
|
||||
p[n - 1] *= a[0];
|
||||
}
|
||||
}
|
||||
out.write_region(area, &px);
|
||||
@@ -695,6 +708,35 @@ mod tests {
|
||||
assert_eq!(px(&s, 0, 8, 8)[3], 0.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn layer_via_copy_of_a_background_keeps_colour_and_partial_selection() {
|
||||
// #668: lift reads only the selected area now; the result must be what converting the
|
||||
// whole layer gave: the colour, with the selection's coverage as alpha.
|
||||
for depth in [8, 16, 32] {
|
||||
let mut s = Session::new();
|
||||
s.execute("file.new", json!({"width": 40, "height": 30, "depth": depth, "background": "#336699"})).unwrap();
|
||||
s.edit("partial selection", |doc, _| {
|
||||
let mut m = photocraft_raster::Surface::new(PixelFormat::GRAY8);
|
||||
m.fill_rect(Rect::new(5, 5, 15, 15), &[1.0]);
|
||||
m.fill_rect(Rect::new(15, 5, 20, 15), &[0.5]);
|
||||
doc.selection = Some(m);
|
||||
Ok(())
|
||||
})
|
||||
.unwrap();
|
||||
let id = s.execute("layer.new.layerViaCopy", json!({})).unwrap()["layer"].as_u64().unwrap();
|
||||
let d = s.active().unwrap();
|
||||
let surf = d.doc.layer(LayerId(id)).unwrap().surface().unwrap();
|
||||
assert!(surf.format().alpha, "@{depth}");
|
||||
let full = surf.read_region(Rect::new(6, 6, 7, 7));
|
||||
let half = surf.read_region(Rect::new(16, 6, 17, 7));
|
||||
for (got, want) in full.iter().zip([0.2, 0.4, 0.6, 1.0]) {
|
||||
assert!((got - want).abs() < 0.01, "@{depth}: {full:?}");
|
||||
}
|
||||
assert!((half[3] - 0.5).abs() < 0.01 && (half[0] - 0.2).abs() < 0.01, "@{depth}: {half:?}");
|
||||
assert_eq!(surf.content_bounds(), Rect::new(5, 5, 20, 15), "@{depth}");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cut_clears_and_layer_via_copy_cut() {
|
||||
let mut s = session();
|
||||
|
||||
Reference in New Issue
Block a user