feat: implement layer mask editing support with scratch buffer and coordinate normalization
This commit is contained in:
@@ -307,6 +307,29 @@ impl Document {
|
||||
}));
|
||||
}
|
||||
|
||||
pub fn push_mask_draw_snapshot_subrect(
|
||||
&mut self,
|
||||
layer_idx: usize,
|
||||
before_pixels: Vec<u8>,
|
||||
after_pixels: Vec<u8>,
|
||||
bounds: (u32, u32, u32, u32),
|
||||
description: String,
|
||||
) {
|
||||
if layer_idx >= self.layers.len() {
|
||||
return;
|
||||
}
|
||||
if before_pixels == after_pixels {
|
||||
return;
|
||||
}
|
||||
self.history.push(Box::new(MaskDrawAction {
|
||||
layer_index: layer_idx,
|
||||
before_pixels,
|
||||
after_pixels,
|
||||
bounds: Some([bounds.0, bounds.1, bounds.2, bounds.3]),
|
||||
description,
|
||||
}));
|
||||
}
|
||||
|
||||
/// Legacy full-layer snapshot (used by non-brush operations: filter, fill, etc.)
|
||||
pub fn push_draw_snapshot(
|
||||
&mut self,
|
||||
@@ -339,6 +362,31 @@ impl Document {
|
||||
}));
|
||||
}
|
||||
|
||||
pub fn push_mask_draw_snapshot(
|
||||
&mut self,
|
||||
layer_idx: usize,
|
||||
before_pixels: Vec<u8>,
|
||||
description: String,
|
||||
) {
|
||||
if layer_idx >= self.layers.len() {
|
||||
return;
|
||||
}
|
||||
let layer = &self.layers[layer_idx];
|
||||
if let Some(ref mask) = layer.mask_pixels {
|
||||
let after_pixels = mask.clone();
|
||||
if before_pixels == after_pixels {
|
||||
return;
|
||||
}
|
||||
self.history.push(Box::new(MaskDrawAction {
|
||||
layer_index: layer_idx,
|
||||
before_pixels,
|
||||
after_pixels,
|
||||
bounds: None,
|
||||
description,
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
/// Clears the undo/redo history stack.
|
||||
///
|
||||
/// **Purpose:** Removes all recorded actions so a freshly loaded document
|
||||
@@ -735,6 +783,76 @@ impl UndoableAction<Vec<Layer>> for DrawAction {
|
||||
}
|
||||
}
|
||||
|
||||
struct MaskDrawAction {
|
||||
layer_index: usize,
|
||||
before_pixels: Vec<u8>,
|
||||
after_pixels: Vec<u8>,
|
||||
bounds: Option<[u32; 4]>,
|
||||
description: String,
|
||||
}
|
||||
|
||||
impl UndoableAction<Vec<Layer>> for MaskDrawAction {
|
||||
fn undo(&mut self, layers: &mut Vec<Layer>) {
|
||||
if let Some(layer) = layers.get_mut(self.layer_index) {
|
||||
if layer.mask_pixels.is_none() {
|
||||
layer.mask_pixels = Some(vec![255; (layer.width * layer.height) as usize]);
|
||||
}
|
||||
if let Some(ref mut mask) = layer.mask_pixels {
|
||||
if let Some([x0, y0, x1, y1]) = self.bounds {
|
||||
let dst_w = layer.width;
|
||||
let rw = x1 - x0;
|
||||
for row in 0..(y1 - y0) {
|
||||
let src_start = (row * rw) as usize;
|
||||
let dst_start = (((y0 + row) * dst_w + x0)) as usize;
|
||||
let len = (rw) as usize;
|
||||
if src_start + len <= self.before_pixels.len()
|
||||
&& dst_start + len <= mask.len()
|
||||
{
|
||||
mask[dst_start..dst_start + len]
|
||||
.copy_from_slice(&self.before_pixels[src_start..src_start + len]);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
mask.clone_from(&self.before_pixels);
|
||||
}
|
||||
}
|
||||
layer.dirty = true;
|
||||
layer.effects_dirty.store(true, std::sync::atomic::Ordering::Release);
|
||||
}
|
||||
}
|
||||
fn redo(&mut self, layers: &mut Vec<Layer>) {
|
||||
if let Some(layer) = layers.get_mut(self.layer_index) {
|
||||
if layer.mask_pixels.is_none() {
|
||||
layer.mask_pixels = Some(vec![255; (layer.width * layer.height) as usize]);
|
||||
}
|
||||
if let Some(ref mut mask) = layer.mask_pixels {
|
||||
if let Some([x0, y0, x1, y1]) = self.bounds {
|
||||
let dst_w = layer.width;
|
||||
let rw = x1 - x0;
|
||||
for row in 0..(y1 - y0) {
|
||||
let src_start = (row * rw) as usize;
|
||||
let dst_start = (((y0 + row) * dst_w + x0)) as usize;
|
||||
let len = (rw) as usize;
|
||||
if src_start + len <= self.after_pixels.len()
|
||||
&& dst_start + len <= mask.len()
|
||||
{
|
||||
mask[dst_start..dst_start + len]
|
||||
.copy_from_slice(&self.after_pixels[src_start..src_start + len]);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
mask.clone_from(&self.after_pixels);
|
||||
}
|
||||
}
|
||||
layer.dirty = true;
|
||||
layer.effects_dirty.store(true, std::sync::atomic::Ordering::Release);
|
||||
}
|
||||
}
|
||||
fn description(&self) -> String {
|
||||
self.description.clone()
|
||||
}
|
||||
}
|
||||
|
||||
struct LayerVisibilityAction {
|
||||
layer_index: usize,
|
||||
old_visible: bool,
|
||||
|
||||
Reference in New Issue
Block a user