From 720e7756f2afe30706b6b1a7fbde86b9f15e1d8c Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Tue, 19 May 2020 22:55:12 +0200 Subject: Move `Layer` to `iced_graphics` --- graphics/src/layer.rs | 266 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 266 insertions(+) create mode 100644 graphics/src/layer.rs (limited to 'graphics/src/layer.rs') diff --git a/graphics/src/layer.rs b/graphics/src/layer.rs new file mode 100644 index 00000000..ae9c6ce0 --- /dev/null +++ b/graphics/src/layer.rs @@ -0,0 +1,266 @@ +use crate::image; +use crate::svg; +use crate::triangle; +use crate::{ + Background, Font, HorizontalAlignment, Point, Primitive, Rectangle, Size, + Vector, VerticalAlignment, Viewport, +}; + +pub struct Layer<'a> { + pub bounds: Rectangle, + pub quads: Vec, + pub meshes: Vec>, + pub text: Vec>, + pub images: Vec, +} + +impl<'a> Layer<'a> { + pub fn new(bounds: Rectangle) -> Self { + Self { + bounds, + quads: Vec::new(), + meshes: Vec::new(), + text: Vec::new(), + images: Vec::new(), + } + } + + pub fn overlay(lines: &'a [impl AsRef], viewport: &Viewport) -> Self { + let (width, height) = viewport.dimensions(); + + let mut overlay = Layer::new(Rectangle { + x: 0, + y: 0, + width, + height, + }); + + 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: [0.9, 0.9, 0.9, 1.0], + size: 20.0, + font: Font::Default, + horizontal_alignment: HorizontalAlignment::Left, + vertical_alignment: VerticalAlignment::Top, + }; + + overlay.text.push(text); + + overlay.text.push(Text { + bounds: text.bounds + Vector::new(-1.0, -1.0), + color: [0.0, 0.0, 0.0, 1.0], + ..text + }); + } + + overlay + } + + pub(crate) fn intersection( + &self, + rectangle: Rectangle, + ) -> Option> { + let layer_bounds: Rectangle = self.bounds.into(); + + layer_bounds.intersection(&rectangle).map(Into::into) + } + + pub fn generate( + primitive: &'a Primitive, + viewport: &Viewport, + ) -> Vec { + let mut layers = Vec::new(); + let (width, height) = viewport.dimensions(); + + layers.push(Layer::new(Rectangle { + x: 0, + y: 0, + width, + height, + })); + + Self::process_primitive(&mut layers, Vector::new(0.0, 0.0), primitive); + + layers + } + + fn process_primitive( + layers: &mut Vec, + translation: Vector, + primitive: &'a Primitive, + ) { + match primitive { + Primitive::None => {} + Primitive::Group { primitives } => { + // TODO: Inspect a bit and regroup (?) + for primitive in primitives { + Self::process_primitive(layers, translation, primitive) + } + } + Primitive::Text { + content, + bounds, + size, + color, + font, + horizontal_alignment, + vertical_alignment, + } => { + let layer = layers.last_mut().unwrap(); + + layer.text.push(Text { + content, + bounds: *bounds + translation, + size: *size, + color: color.into_linear(), + font: *font, + horizontal_alignment: *horizontal_alignment, + vertical_alignment: *vertical_alignment, + }); + } + Primitive::Quad { + bounds, + background, + border_radius, + border_width, + border_color, + } => { + let layer = layers.last_mut().unwrap(); + + // TODO: Move some of these computations to the GPU (?) + layer.quads.push(Quad { + position: [ + bounds.x + translation.x, + bounds.y + translation.y, + ], + scale: [bounds.width, bounds.height], + color: match background { + Background::Color(color) => color.into_linear(), + }, + border_radius: *border_radius as f32, + border_width: *border_width as f32, + border_color: border_color.into_linear(), + }); + } + Primitive::Mesh2D { buffers, size } => { + let layer = layers.last_mut().unwrap(); + + let bounds = Rectangle::new( + Point::new(translation.x, translation.y), + *size, + ); + + // Only draw visible content + if let Some(clip_bounds) = layer.intersection(bounds) { + layer.meshes.push(Mesh { + origin: Point::new(translation.x, translation.y), + buffers, + clip_bounds: clip_bounds.into(), + }); + } + } + Primitive::Clip { + bounds, + offset, + content, + } => { + let layer = layers.last_mut().unwrap(); + + // Only draw visible content + if let Some(clip_bounds) = + layer.intersection(*bounds + translation) + { + let clip_layer = Layer::new(clip_bounds.into()); + let new_layer = Layer::new(layer.bounds); + + layers.push(clip_layer); + Self::process_primitive( + layers, + translation + - Vector::new(offset.x as f32, offset.y as f32), + content, + ); + layers.push(new_layer); + } + } + Primitive::Translate { + translation: new_translation, + content, + } => { + Self::process_primitive( + layers, + translation + *new_translation, + &content, + ); + } + Primitive::Cached { cache } => { + Self::process_primitive(layers, translation, &cache); + } + Primitive::Image { handle, bounds } => { + let layer = layers.last_mut().unwrap(); + + layer.images.push(Image::Raster { + handle: handle.clone(), + bounds: *bounds + translation, + }); + } + Primitive::Svg { handle, bounds } => { + let layer = layers.last_mut().unwrap(); + + layer.images.push(Image::Vector { + handle: handle.clone(), + bounds: *bounds + translation, + }); + } + } + } +} + +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct Quad { + pub position: [f32; 2], + pub scale: [f32; 2], + pub color: [f32; 4], + pub border_color: [f32; 4], + pub border_radius: f32, + pub border_width: f32, +} + +#[derive(Debug, Clone, Copy)] +pub struct Mesh<'a> { + pub origin: Point, + pub buffers: &'a triangle::Mesh2D, + pub clip_bounds: Rectangle, +} + +#[derive(Debug, Clone, Copy)] +pub struct Text<'a> { + pub content: &'a str, + pub bounds: Rectangle, + pub color: [f32; 4], + pub size: f32, + pub font: Font, + pub horizontal_alignment: HorizontalAlignment, + pub vertical_alignment: VerticalAlignment, +} + +#[derive(Debug, Clone)] +pub enum Image { + Raster { + handle: image::Handle, + bounds: Rectangle, + }, + Vector { + handle: svg::Handle, + bounds: Rectangle, + }, +} + +unsafe impl bytemuck::Zeroable for Quad {} +unsafe impl bytemuck::Pod for Quad {} -- cgit From a1a5fcfd46622d5b18d1716aa2adb4659835ccf3 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Wed, 20 May 2020 20:28:35 +0200 Subject: Refactor `Viewport` and `Compositor` --- graphics/src/layer.rs | 45 +++++++++++++-------------------------------- 1 file changed, 13 insertions(+), 32 deletions(-) (limited to 'graphics/src/layer.rs') diff --git a/graphics/src/layer.rs b/graphics/src/layer.rs index ae9c6ce0..916b5c83 100644 --- a/graphics/src/layer.rs +++ b/graphics/src/layer.rs @@ -7,7 +7,7 @@ use crate::{ }; pub struct Layer<'a> { - pub bounds: Rectangle, + pub bounds: Rectangle, pub quads: Vec, pub meshes: Vec>, pub text: Vec>, @@ -15,7 +15,7 @@ pub struct Layer<'a> { } impl<'a> Layer<'a> { - pub fn new(bounds: Rectangle) -> Self { + pub fn new(bounds: Rectangle) -> Self { Self { bounds, quads: Vec::new(), @@ -26,14 +26,8 @@ impl<'a> Layer<'a> { } pub fn overlay(lines: &'a [impl AsRef], viewport: &Viewport) -> Self { - let (width, height) = viewport.dimensions(); - - let mut overlay = Layer::new(Rectangle { - x: 0, - y: 0, - width, - height, - }); + let mut overlay = + Layer::new(Rectangle::with_size(viewport.logical_size())); for (i, line) in lines.iter().enumerate() { let text = Text { @@ -61,28 +55,14 @@ impl<'a> Layer<'a> { overlay } - pub(crate) fn intersection( - &self, - rectangle: Rectangle, - ) -> Option> { - let layer_bounds: Rectangle = self.bounds.into(); - - layer_bounds.intersection(&rectangle).map(Into::into) - } - pub fn generate( primitive: &'a Primitive, viewport: &Viewport, ) -> Vec { - let mut layers = Vec::new(); - let (width, height) = viewport.dimensions(); + let first_layer = + Layer::new(Rectangle::with_size(viewport.logical_size())); - layers.push(Layer::new(Rectangle { - x: 0, - y: 0, - width, - height, - })); + let mut layers = vec![first_layer]; Self::process_primitive(&mut layers, Vector::new(0.0, 0.0), primitive); @@ -156,11 +136,11 @@ impl<'a> Layer<'a> { ); // Only draw visible content - if let Some(clip_bounds) = layer.intersection(bounds) { + if let Some(clip_bounds) = layer.bounds.intersection(&bounds) { layer.meshes.push(Mesh { origin: Point::new(translation.x, translation.y), buffers, - clip_bounds: clip_bounds.into(), + clip_bounds, }); } } @@ -170,12 +150,13 @@ impl<'a> Layer<'a> { content, } => { let layer = layers.last_mut().unwrap(); + let translated_bounds = *bounds + translation; // Only draw visible content if let Some(clip_bounds) = - layer.intersection(*bounds + translation) + layer.bounds.intersection(&translated_bounds) { - let clip_layer = Layer::new(clip_bounds.into()); + let clip_layer = Layer::new(clip_bounds); let new_layer = Layer::new(layer.bounds); layers.push(clip_layer); @@ -236,7 +217,7 @@ pub struct Quad { pub struct Mesh<'a> { pub origin: Point, pub buffers: &'a triangle::Mesh2D, - pub clip_bounds: Rectangle, + pub clip_bounds: Rectangle, } #[derive(Debug, Clone, Copy)] -- cgit From e0e4ee73feead3f05730625c7e1917b63f0b384e Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Thu, 21 May 2020 00:37:47 +0200 Subject: Implement `iced_glutin` :tada: --- graphics/src/layer.rs | 1 + 1 file changed, 1 insertion(+) (limited to 'graphics/src/layer.rs') diff --git a/graphics/src/layer.rs b/graphics/src/layer.rs index 916b5c83..59b792f0 100644 --- a/graphics/src/layer.rs +++ b/graphics/src/layer.rs @@ -6,6 +6,7 @@ use crate::{ Vector, VerticalAlignment, Viewport, }; +#[derive(Debug, Clone)] pub struct Layer<'a> { pub bounds: Rectangle, pub quads: Vec, -- cgit From 2ca7e3c4b0cb293adebf9a9bf9a26191069d495d Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Thu, 28 May 2020 01:37:59 +0200 Subject: Write documentation for `iced_graphics` --- graphics/src/layer.rs | 115 +++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 113 insertions(+), 2 deletions(-) (limited to 'graphics/src/layer.rs') diff --git a/graphics/src/layer.rs b/graphics/src/layer.rs index 59b792f0..6aca738e 100644 --- a/graphics/src/layer.rs +++ b/graphics/src/layer.rs @@ -1,3 +1,4 @@ +//! Organize rendering primitives into a flattened list of layers. use crate::image; use crate::svg; use crate::triangle; @@ -6,16 +7,39 @@ use crate::{ Vector, VerticalAlignment, Viewport, }; +/// A group of primitives that should be clipped together. #[derive(Debug, Clone)] pub struct Layer<'a> { + /// The clipping bounds of the [`Layer`]. + /// + /// [`Layer`]: struct.Layer.html pub bounds: Rectangle, + + /// The quads of the [`Layer`]. + /// + /// [`Layer`]: struct.Layer.html pub quads: Vec, + + /// The triangle meshes of the [`Layer`]. + /// + /// [`Layer`]: struct.Layer.html pub meshes: Vec>, + + /// The text of the [`Layer`]. + /// + /// [`Layer`]: struct.Layer.html pub text: Vec>, + + /// The images of the [`Layer`]. + /// + /// [`Layer`]: struct.Layer.html pub images: Vec, } impl<'a> Layer<'a> { + /// Creates a new [`Layer`] with the given clipping bounds. + /// + /// [`Layer`]: struct.Layer.html pub fn new(bounds: Rectangle) -> Self { Self { bounds, @@ -26,6 +50,11 @@ impl<'a> Layer<'a> { } } + /// Creates a new [`Layer`] for the provided overlay text. + /// + /// This can be useful for displaying debug information. + /// + /// [`Layer`]: struct.Layer.html pub fn overlay(lines: &'a [impl AsRef], viewport: &Viewport) -> Self { let mut overlay = Layer::new(Rectangle::with_size(viewport.logical_size())); @@ -56,6 +85,10 @@ impl<'a> Layer<'a> { overlay } + /// Distributes the given [`Primitive`] and generates a list of layers based + /// on its contents. + /// + /// [`Primitive`]: ../enum.Primitive.html pub fn generate( primitive: &'a Primitive, viewport: &Viewport, @@ -119,7 +152,7 @@ impl<'a> Layer<'a> { bounds.x + translation.x, bounds.y + translation.y, ], - scale: [bounds.width, bounds.height], + size: [bounds.width, bounds.height], color: match background { Background::Color(color) => color.into_linear(), }, @@ -203,46 +236,124 @@ impl<'a> Layer<'a> { } } +/// A colored rectangle with a border. +/// +/// This type can be directly uploaded to GPU memory. #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct Quad { + /// The position of the [`Quad`]. + /// + /// [`Quad`]: struct.Quad.html pub position: [f32; 2], - pub scale: [f32; 2], + + /// The size of the [`Quad`]. + /// + /// [`Quad`]: struct.Quad.html + pub size: [f32; 2], + + /// The color of the [`Quad`], in __linear RGB__. + /// + /// [`Quad`]: struct.Quad.html pub color: [f32; 4], + + /// The border color of the [`Quad`], in __linear RGB__. + /// + /// [`Quad`]: struct.Quad.html pub border_color: [f32; 4], + + /// The border radius of the [`Quad`]. + /// + /// [`Quad`]: struct.Quad.html pub border_radius: f32, + + /// The border width of the [`Quad`]. + /// + /// [`Quad`]: struct.Quad.html pub border_width: f32, } +/// A mesh of triangles. #[derive(Debug, Clone, Copy)] pub struct Mesh<'a> { + /// The origin of the vertices of the [`Mesh`]. + /// + /// [`Mesh`]: struct.Mesh.html pub origin: Point, + + /// The vertex and index buffers of the [`Mesh`]. + /// + /// [`Mesh`]: struct.Mesh.html pub buffers: &'a triangle::Mesh2D, + + /// The clipping bounds of the [`Mesh`]. + /// + /// [`Mesh`]: struct.Mesh.html pub clip_bounds: Rectangle, } +/// A paragraph of text. #[derive(Debug, Clone, Copy)] pub struct Text<'a> { + /// The content of the [`Text`]. + /// + /// [`Text`]: struct.Text.html pub content: &'a str, + + /// The layout bounds of the [`Text`]. + /// + /// [`Text`]: struct.Text.html pub bounds: Rectangle, + + /// The color of the [`Text`], in __linear RGB_. + /// + /// [`Text`]: struct.Text.html pub color: [f32; 4], + + /// The size of the [`Text`]. + /// + /// [`Text`]: struct.Text.html pub size: f32, + + /// The font of the [`Text`]. + /// + /// [`Text`]: struct.Text.html pub font: Font, + + /// The horizontal alignment of the [`Text`]. + /// + /// [`Text`]: struct.Text.html pub horizontal_alignment: HorizontalAlignment, + + /// The vertical alignment of the [`Text`]. + /// + /// [`Text`]: struct.Text.html pub vertical_alignment: VerticalAlignment, } +/// A raster or vector image. #[derive(Debug, Clone)] pub enum Image { + /// A raster image. Raster { + /// The handle of a raster image. handle: image::Handle, + + /// The bounds of the image. bounds: Rectangle, }, + /// A vector image. Vector { + /// The handle of a vector image. handle: svg::Handle, + + /// The bounds of the image. bounds: Rectangle, }, } +#[allow(unsafe_code)] unsafe impl bytemuck::Zeroable for Quad {} + +#[allow(unsafe_code)] unsafe impl bytemuck::Pod for Quad {} -- cgit