iced-yoda/graphics/src/layer.rs
2025-08-19 08:06:25 +02:00

221 lines
7.1 KiB
Rust

//! 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;
/// Returns the current bounds of the [`Layer`].
fn bounds(&self) -> Rectangle;
/// 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);
/// Returns the start level of the [`Layer`].
///
/// A level is a "sublayer" index inside of a [`Layer`].
///
/// A [`Layer`] may draw multiple primitive types in a certain order.
/// The level represents the lowest index of the primitive types it
/// contains.
///
/// Two layers A and B can therefore be merged if they have the same bounds,
/// and the end level of A is lower or equal than the start level of B.
fn start(&self) -> usize;
/// Returns the end level of the [`Layer`].
fn end(&self) -> usize;
/// Merges a [`Layer`] with the current one.
fn merge(&mut self, _layer: &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 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 [`Stack`].
pub fn flush(&mut self) {
self.layers[self.current].flush();
}
/// Performs layer merging wherever possible.
///
/// Flushes and settles any primitives in the [`Stack`].
pub fn merge(&mut self) {
self.flush();
// These are the layers left to process
let mut left = self.active_count;
// There must be at least 2 or more layers to merge
while left > 1 {
// We set our target as the topmost layer left to process
let mut current = left - 1;
let mut target = &self.layers[current];
let mut target_start = target.start();
let mut target_index = current;
// We scan downwards for a contiguous block of mergeable layer candidates
while current > 0 {
let candidate = &self.layers[current - 1];
let start = candidate.start();
let end = candidate.end();
// We skip empty layers
if end == 0 {
current -= 1;
continue;
}
// Candidate can be merged if primitive sublayers do not overlap with
// previous targets and the clipping bounds match
if end > target_start || candidate.bounds() != target.bounds() {
break;
}
// Candidate is not empty and can be merged into
target = candidate;
target_start = start;
target_index = current;
current -= 1;
}
// We merge all the layers scanned into the target
//
// Since we use `target_index` instead of `current`, we
// deliberately avoid merging into empty layers.
//
// If no candidates were mergeable, this is a no-op.
let (head, tail) = self.layers.split_at_mut(target_index + 1);
let layer = &mut head[target_index];
for middle in &mut tail[0..left - target_index - 1] {
layer.merge(middle);
}
// Empty layers found after the target can be skipped
left = current;
}
}
/// Clears the layers of the [`Stack`], allowing reuse.
///
/// It resizes the base layer bounds to the `new_bounds`.
///
/// This will normally keep layer allocations for future drawing operations.
pub fn reset(&mut self, new_bounds: Rectangle) {
for layer in self.layers[..self.active_count].iter_mut() {
layer.reset();
}
self.layers[0].resize(new_bounds);
self.current = 0;
self.active_count = 1;
self.previous.clear();
}
}
impl<T: Layer> Default for Stack<T> {
fn default() -> Self {
Self::new()
}
}