//! 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>, } impl RasterFillAction { pub fn new( layer_id: Uuid, time: f64, buffer_before: Vec, buffer_after: Vec, 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)) } }