Lightningbeam/lightningbeam-ui/lightningbeam-core/src/actions/raster_fill.rs

88 lines
2.7 KiB
Rust

//! Raster flood-fill action — records and undoes a paint bucket fill on a RasterLayer.
use crate::action::Action;
use crate::actions::raster_diff::RasterDiff;
use crate::document::Document;
use crate::layer::AnyLayer;
use uuid::Uuid;
pub struct RasterFillAction {
layer_id: Uuid,
time: f64,
width: u32,
height: u32,
name: String,
diff: RasterDiff,
/// Full post-fill buffer, kept only for the first `execute` (commit); see
/// `RasterStrokeAction::full_after`.
full_after: Option<Vec<u8>>,
}
impl RasterFillAction {
pub fn new(
layer_id: Uuid,
time: f64,
buffer_before: Vec<u8>,
buffer_after: Vec<u8>,
width: u32,
height: u32,
) -> Self {
let diff = RasterDiff::compute(&buffer_before, &buffer_after, width, height);
Self { layer_id, time, width, height, name: "Flood fill".to_string(),
diff, full_after: Some(buffer_after) }
}
pub fn with_description(mut self, name: &str) -> Self {
self.name = name.to_string();
self
}
}
impl Action for RasterFillAction {
fn execute(&mut self, document: &mut Document) -> Result<(), String> {
let layer = document.get_layer_mut(&self.layer_id)
.ok_or_else(|| format!("Layer {} not found", self.layer_id))?;
let raster = match layer {
AnyLayer::Raster(rl) => rl,
_ => return Err("Not a raster layer".to_string()),
};
let _ = (self.width, self.height);
let kf = raster
.keyframe_at_mut(self.time)
.ok_or_else(|| format!("No raster keyframe at/before t={}", self.time))?;
if let Some(full) = self.full_after.take() {
kf.raw_pixels = full;
} else {
self.diff.apply_after(&mut kf.raw_pixels);
}
kf.texture_dirty = true;
kf.dirty = true;
Ok(())
}
fn rollback(&mut self, document: &mut Document) -> Result<(), String> {
let layer = document.get_layer_mut(&self.layer_id)
.ok_or_else(|| format!("Layer {} not found", self.layer_id))?;
let raster = match layer {
AnyLayer::Raster(rl) => rl,
_ => return Err("Not a raster layer".to_string()),
};
let _ = (self.width, self.height);
let kf = raster
.keyframe_at_mut(self.time)
.ok_or_else(|| format!("No raster keyframe at/before t={}", self.time))?;
self.diff.apply_before(&mut kf.raw_pixels);
kf.texture_dirty = true;
kf.dirty = true;
Ok(())
}
fn description(&self) -> String {
self.name.clone()
}
fn raster_resident_hint(&self) -> Option<(Uuid, f64)> {
Some((self.layer_id, self.time))
}
}