diff options
Diffstat (limited to 'wgpu/src/layer.rs')
-rw-r--r-- | wgpu/src/layer.rs | 283 |
1 files changed, 283 insertions, 0 deletions
diff --git a/wgpu/src/layer.rs b/wgpu/src/layer.rs new file mode 100644 index 00000000..b8f32db1 --- /dev/null +++ b/wgpu/src/layer.rs @@ -0,0 +1,283 @@ +//! Organize rendering primitives into a flattened list of layers. +mod image; +mod text; + +pub mod mesh; + +pub use image::Image; +pub use mesh::Mesh; +pub use text::Text; + +use crate::core; +use crate::core::alignment; +use crate::core::{Color, Font, Point, Rectangle, Size, Vector}; +use crate::graphics; +use crate::graphics::color; +use crate::graphics::Viewport; +use crate::primitive::{self, Primitive}; +use crate::quad::{self, Quad}; + +/// A group of primitives that should be clipped together. +#[derive(Debug)] +pub struct Layer<'a> { + /// The clipping bounds of the [`Layer`]. + pub bounds: Rectangle, + + /// The quads of the [`Layer`]. + pub quads: quad::Batch, + + /// The triangle meshes of the [`Layer`]. + pub meshes: Vec<Mesh<'a>>, + + /// The text of the [`Layer`]. + pub text: Vec<Text<'a>>, + + /// The images of the [`Layer`]. + pub images: Vec<Image>, +} + +impl<'a> Layer<'a> { + /// Creates a new [`Layer`] with the given clipping bounds. + pub fn new(bounds: Rectangle) -> Self { + Self { + bounds, + quads: quad::Batch::default(), + meshes: Vec::new(), + text: Vec::new(), + images: Vec::new(), + } + } + + /// Creates a new [`Layer`] for the provided overlay text. + /// + /// This can be useful for displaying debug information. + pub fn overlay(lines: &'a [impl AsRef<str>], viewport: &Viewport) -> Self { + let mut overlay = + Layer::new(Rectangle::with_size(viewport.logical_size())); + + for (i, line) in lines.iter().enumerate() { + let text = Text { + content: line.as_ref(), + bounds: Rectangle::new( + Point::new(11.0, 11.0 + 25.0 * i as f32), + Size::INFINITY, + ), + color: Color::new(0.9, 0.9, 0.9, 1.0), + size: 20.0, + line_height: core::text::LineHeight::default(), + font: Font::MONOSPACE, + horizontal_alignment: alignment::Horizontal::Left, + vertical_alignment: alignment::Vertical::Top, + shaping: core::text::Shaping::Basic, + }; + + overlay.text.push(text); + + overlay.text.push(Text { + bounds: text.bounds + Vector::new(-1.0, -1.0), + color: Color::BLACK, + ..text + }); + } + + overlay + } + + /// Distributes the given [`Primitive`] and generates a list of layers based + /// on its contents. + pub fn generate( + primitives: &'a [Primitive], + viewport: &Viewport, + ) -> Vec<Self> { + let first_layer = + Layer::new(Rectangle::with_size(viewport.logical_size())); + + let mut layers = vec![first_layer]; + + for primitive in primitives { + Self::process_primitive( + &mut layers, + Vector::new(0.0, 0.0), + primitive, + 0, + ); + } + + layers + } + + fn process_primitive( + layers: &mut Vec<Self>, + translation: Vector, + primitive: &'a Primitive, + current_layer: usize, + ) { + match primitive { + Primitive::Text { + content, + bounds, + size, + line_height, + color, + font, + horizontal_alignment, + vertical_alignment, + shaping, + } => { + let layer = &mut layers[current_layer]; + + layer.text.push(Text { + content, + bounds: *bounds + translation, + size: *size, + line_height: *line_height, + color: *color, + font: *font, + horizontal_alignment: *horizontal_alignment, + vertical_alignment: *vertical_alignment, + shaping: *shaping, + }); + } + Primitive::Quad { + bounds, + background, + border_radius, + border_width, + border_color, + } => { + let layer = &mut layers[current_layer]; + + let quad = Quad { + position: [ + bounds.x + translation.x, + bounds.y + translation.y, + ], + size: [bounds.width, bounds.height], + border_color: color::pack(*border_color), + border_radius: *border_radius, + border_width: *border_width, + }; + + layer.quads.add(quad, background); + } + Primitive::Image { handle, bounds } => { + let layer = &mut layers[current_layer]; + + layer.images.push(Image::Raster { + handle: handle.clone(), + bounds: *bounds + translation, + }); + } + Primitive::Svg { + handle, + color, + bounds, + } => { + let layer = &mut layers[current_layer]; + + layer.images.push(Image::Vector { + handle: handle.clone(), + color: *color, + bounds: *bounds + translation, + }); + } + Primitive::Group { primitives } => { + // TODO: Inspect a bit and regroup (?) + for primitive in primitives { + Self::process_primitive( + layers, + translation, + primitive, + current_layer, + ) + } + } + Primitive::Clip { bounds, content } => { + let layer = &mut layers[current_layer]; + let translated_bounds = *bounds + translation; + + // Only draw visible content + if let Some(clip_bounds) = + layer.bounds.intersection(&translated_bounds) + { + let clip_layer = Layer::new(clip_bounds); + layers.push(clip_layer); + + Self::process_primitive( + layers, + translation, + content, + layers.len() - 1, + ); + } + } + Primitive::Translate { + translation: new_translation, + content, + } => { + Self::process_primitive( + layers, + translation + *new_translation, + content, + current_layer, + ); + } + Primitive::Cache { content } => { + Self::process_primitive( + layers, + translation, + content, + current_layer, + ); + } + Primitive::Custom(custom) => match custom { + primitive::Custom::Mesh(mesh) => match mesh { + graphics::Mesh::Solid { buffers, size } => { + let layer = &mut layers[current_layer]; + + let bounds = Rectangle::new( + Point::new(translation.x, translation.y), + *size, + ); + + // Only draw visible content + if let Some(clip_bounds) = + layer.bounds.intersection(&bounds) + { + layer.meshes.push(Mesh::Solid { + origin: Point::new( + translation.x, + translation.y, + ), + buffers, + clip_bounds, + }); + } + } + graphics::Mesh::Gradient { buffers, size } => { + let layer = &mut layers[current_layer]; + + let bounds = Rectangle::new( + Point::new(translation.x, translation.y), + *size, + ); + + // Only draw visible content + if let Some(clip_bounds) = + layer.bounds.intersection(&bounds) + { + layer.meshes.push(Mesh::Gradient { + origin: Point::new( + translation.x, + translation.y, + ), + buffers, + clip_bounds, + }); + } + } + }, + }, + } + } +} |