diff options
Diffstat (limited to 'graphics/src/layer.rs')
-rw-r--r-- | graphics/src/layer.rs | 144 |
1 files changed, 144 insertions, 0 deletions
diff --git a/graphics/src/layer.rs b/graphics/src/layer.rs new file mode 100644 index 00000000..c9a818fb --- /dev/null +++ b/graphics/src/layer.rs @@ -0,0 +1,144 @@ +//! Draw and stack layers of graphical primitives. +use crate::core::{Rectangle, Transformation}; + +/// A layer of graphical primitives. +/// +/// Layers normally dictate a set of primitives that are +/// rendered in a specific order. +pub trait Layer: Default { + /// Creates a new [`Layer`] with the given bounds. + fn with_bounds(bounds: Rectangle) -> Self; + + /// Flushes and settles any pending group of primitives in the [`Layer`]. + /// + /// This will be called when a [`Layer`] is finished. It allows layers to efficiently + /// record primitives together and defer grouping until the end. + fn flush(&mut self); + + /// Resizes the [`Layer`] to the given bounds. + fn resize(&mut self, bounds: Rectangle); + + /// Clears all the layers contents and resets its bounds. + fn reset(&mut self); +} + +/// A stack of layers used for drawing. +#[derive(Debug)] +pub struct Stack<T: Layer> { + layers: Vec<T>, + transformations: Vec<Transformation>, + previous: Vec<usize>, + current: usize, + active_count: usize, +} + +impl<T: Layer> Stack<T> { + /// Creates a new empty [`Stack`]. + pub fn new() -> Self { + Self { + layers: vec![T::default()], + transformations: vec![Transformation::IDENTITY], + previous: vec![], + current: 0, + active_count: 1, + } + } + + /// Returns a mutable reference to the current [`Layer`] of the [`Stack`], together with + /// the current [`Transformation`]. + #[inline] + pub fn current_mut(&mut self) -> (&mut T, Transformation) { + let transformation = self.transformation(); + + (&mut self.layers[self.current], transformation) + } + + /// Returns the current [`Transformation`] of the [`Stack`]. + #[inline] + pub fn transformation(&self) -> Transformation { + self.transformations.last().copied().unwrap() + } + + /// Pushes a new clipping region in the [`Stack`]; creating a new layer in the + /// process. + pub fn push_clip(&mut self, bounds: Rectangle) { + self.previous.push(self.current); + + self.current = self.active_count; + self.active_count += 1; + + let bounds = bounds * self.transformation(); + + if self.current == self.layers.len() { + self.layers.push(T::with_bounds(bounds)); + } else { + self.layers[self.current].resize(bounds); + } + } + + /// Pops the current clipping region from the [`Stack`] and restores the previous one. + /// + /// The current layer will be recorded for drawing. + pub fn pop_clip(&mut self) { + self.flush(); + + self.current = self.previous.pop().unwrap(); + } + + /// Pushes a new [`Transformation`] in the [`Stack`]. + /// + /// Future drawing operations will be affected by this new [`Transformation`] until + /// it is popped using [`pop_transformation`]. + /// + /// [`pop_transformation`]: Self::pop_transformation + pub fn push_transformation(&mut self, transformation: Transformation) { + self.transformations + .push(self.transformation() * transformation); + } + + /// Pops the current [`Transformation`] in the [`Stack`]. + pub fn pop_transformation(&mut self) { + let _ = self.transformations.pop(); + } + + /// Returns an iterator over mutable references to the layers in the [`Stack`]. + pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut T> { + self.flush(); + + self.layers[..self.active_count].iter_mut() + } + + /// Returns an iterator over immutable references to the layers in the [`Stack`]. + pub fn iter(&self) -> impl Iterator<Item = &T> { + self.layers[..self.active_count].iter() + } + + /// Returns the slice of layers in the [`Stack`]. + pub fn as_slice(&self) -> &[T] { + &self.layers[..self.active_count] + } + + /// Flushes and settles any primitives in the current layer of the [`Stack`]. + pub fn flush(&mut self) { + self.layers[self.current].flush(); + } + + /// Clears the layers of the [`Stack`], allowing reuse. + /// + /// This will normally keep layer allocations for future drawing operations. + pub fn clear(&mut self) { + for layer in self.layers[..self.active_count].iter_mut() { + layer.reset(); + } + + self.current = 0; + self.active_count = 1; + self.previous.clear(); + } +} + +impl<T: Layer> Default for Stack<T> { + fn default() -> Self { + Self::new() + } +} |