From 40f45d7b7e35dd4937abe6b5ce16b6256b4f1eeb Mon Sep 17 00:00:00 2001 From: shan Date: Thu, 29 Sep 2022 10:52:58 -0700 Subject: Adds linear gradient support to 2D meshes in the canvas widget. --- graphics/src/gradient.rs | 23 ++++++ graphics/src/layer.rs | 47 +++++++++-- graphics/src/lib.rs | 2 + graphics/src/primitive.rs | 5 +- graphics/src/shader.rs | 42 ++++++++++ graphics/src/transformation.rs | 8 +- graphics/src/triangle.rs | 18 ++-- graphics/src/widget/canvas.rs | 6 +- graphics/src/widget/canvas/fill.rs | 33 +++++--- graphics/src/widget/canvas/frame.rs | 113 +++++++++----------------- graphics/src/widget/canvas/gradient.rs | 21 +++++ graphics/src/widget/canvas/gradient/linear.rs | 73 +++++++++++++++++ graphics/src/widget/canvas/stroke.rs | 26 ++++-- 13 files changed, 307 insertions(+), 110 deletions(-) create mode 100644 graphics/src/gradient.rs create mode 100644 graphics/src/shader.rs create mode 100644 graphics/src/widget/canvas/gradient.rs create mode 100644 graphics/src/widget/canvas/gradient/linear.rs (limited to 'graphics/src') diff --git a/graphics/src/gradient.rs b/graphics/src/gradient.rs new file mode 100644 index 00000000..4d1eff62 --- /dev/null +++ b/graphics/src/gradient.rs @@ -0,0 +1,23 @@ +//! For creating a Gradient. + +use iced_native::Color; +use crate::widget::canvas::gradient::Linear; + +#[derive(Debug, Clone, PartialEq)] +/// A fill which transitions colors progressively along a direction, either linearly, radially, +/// or conically. +pub enum Gradient { + /// A linear gradient interpolates colors along a direction from its [`start`] to its [`end`] + /// point. + Linear(Linear), +} + + +#[derive(Debug, Clone, Copy, PartialEq)] +/// A point along the gradient vector where the specified [`color`] is unmixed. +pub struct ColorStop { + /// Offset along the gradient vector. + pub offset: f32, + /// The color of the gradient at the specified [`offset`]. + pub color: Color, +} \ No newline at end of file diff --git a/graphics/src/layer.rs b/graphics/src/layer.rs index af545713..b7731922 100644 --- a/graphics/src/layer.rs +++ b/graphics/src/layer.rs @@ -7,9 +7,10 @@ use crate::{ use iced_native::image; use iced_native::svg; +use crate::shader::Shader; /// A group of primitives that should be clipped together. -#[derive(Debug, Clone)] +#[derive(Debug)] pub struct Layer<'a> { /// The clipping bounds of the [`Layer`]. pub bounds: Rectangle, @@ -18,7 +19,7 @@ pub struct Layer<'a> { pub quads: Vec, /// The triangle meshes of the [`Layer`]. - pub meshes: Vec>, + pub meshes: Meshes<'a>, /// The text of the [`Layer`]. pub text: Vec>, @@ -33,7 +34,7 @@ impl<'a> Layer<'a> { Self { bounds, quads: Vec::new(), - meshes: Vec::new(), + meshes: Meshes(Vec::new()), text: Vec::new(), images: Vec::new(), } @@ -159,7 +160,11 @@ impl<'a> Layer<'a> { border_color: border_color.into_linear(), }); } - Primitive::Mesh2D { buffers, size } => { + Primitive::Mesh2D { + buffers, + size, + shader, + } => { let layer = &mut layers[current_layer]; let bounds = Rectangle::new( @@ -169,11 +174,14 @@ impl<'a> Layer<'a> { // Only draw visible content if let Some(clip_bounds) = layer.bounds.intersection(&bounds) { - layer.meshes.push(Mesh { - origin: Point::new(translation.x, translation.y), - buffers, - clip_bounds, - }); + layer.meshes.0.push( + Mesh { + origin: Point::new(translation.x, translation.y), + buffers, + clip_bounds, + shader, + } + ); } } Primitive::Clip { bounds, content } => { @@ -270,6 +278,9 @@ pub struct Mesh<'a> { /// The clipping bounds of the [`Mesh`]. pub clip_bounds: Rectangle, + + /// The shader of the [`Mesh`]. + pub shader: &'a Shader, } /// A paragraph of text. @@ -323,3 +334,21 @@ unsafe impl bytemuck::Zeroable for Quad {} #[allow(unsafe_code)] unsafe impl bytemuck::Pod for Quad {} + +#[derive(Debug)] +/// A collection of meshes. +pub struct Meshes<'a>(pub Vec>); + +impl<'a> Meshes<'a> { + /// Returns the number of total vertices & total indices of all [`Mesh`]es. + pub fn attribute_count(&self) -> (usize, usize) { + self.0 + .iter() + .map(|Mesh { buffers, .. }| { + (buffers.vertices.len(), buffers.indices.len()) + }) + .fold((0, 0), |(total_v, total_i), (v, i)| { + (total_v + v, total_i + i) + }) + } +} \ No newline at end of file diff --git a/graphics/src/lib.rs b/graphics/src/lib.rs index 11082472..ce9b1b07 100644 --- a/graphics/src/lib.rs +++ b/graphics/src/lib.rs @@ -35,6 +35,8 @@ pub mod renderer; pub mod triangle; pub mod widget; pub mod window; +pub mod shader; +pub mod gradient; pub use antialiasing::Antialiasing; pub use backend::Backend; diff --git a/graphics/src/primitive.rs b/graphics/src/primitive.rs index 5f7a344d..4f79a74c 100644 --- a/graphics/src/primitive.rs +++ b/graphics/src/primitive.rs @@ -2,7 +2,7 @@ use iced_native::image; use iced_native::svg; use iced_native::{Background, Color, Font, Rectangle, Size, Vector}; -use crate::alignment; +use crate::{alignment, shader}; use crate::triangle; use std::sync::Arc; @@ -88,6 +88,9 @@ pub enum Primitive { /// /// Any geometry that falls out of this region will be clipped. size: Size, + + /// The shader of the mesh + shader: shader::Shader, }, /// A cached primitive. /// diff --git a/graphics/src/shader.rs b/graphics/src/shader.rs new file mode 100644 index 00000000..b9071c74 --- /dev/null +++ b/graphics/src/shader.rs @@ -0,0 +1,42 @@ +//! Supported shaders; + +use crate::{Color, widget}; +use crate::gradient::Gradient; +use crate::widget::canvas::{FillStyle, StrokeStyle}; + +#[derive(Debug, Clone)] +/// Supported shaders for primitives. +pub enum Shader { + /// Fill a primitive with a solid color. + Solid(Color), + /// Fill a primitive with an interpolated color. + Gradient(Gradient) +} + +impl <'a> Into for StrokeStyle<'a> { + fn into(self) -> Shader { + match self { + StrokeStyle::Solid(color) => Shader::Solid(color), + StrokeStyle::Gradient(gradient) => gradient.clone().into() + } + } +} + +impl <'a> Into for FillStyle<'a> { + fn into(self) -> Shader { + match self { + FillStyle::Solid(color) => Shader::Solid(color), + FillStyle::Gradient(gradient) => gradient.clone().into() + } + } +} + +impl <'a> Into for widget::canvas::Gradient { + fn into(self) -> Shader { + match self { + widget::canvas::Gradient::Linear(linear) => { + Shader::Gradient(Gradient::Linear(linear)) + } + } + } +} \ No newline at end of file diff --git a/graphics/src/transformation.rs b/graphics/src/transformation.rs index 2a19caed..03f453db 100644 --- a/graphics/src/transformation.rs +++ b/graphics/src/transformation.rs @@ -8,7 +8,7 @@ pub struct Transformation(Mat4); impl Transformation { /// Get the identity transformation. pub fn identity() -> Transformation { - Transformation(Mat4::identity()) + Transformation(Mat4::IDENTITY) } /// Creates an orthographic projection. @@ -51,3 +51,9 @@ impl From for [f32; 16] { *t.as_ref() } } + +impl Into for Transformation { + fn into(self) -> Mat4 { + self.0 + } +} \ No newline at end of file diff --git a/graphics/src/triangle.rs b/graphics/src/triangle.rs index 05028f51..92709fe2 100644 --- a/graphics/src/triangle.rs +++ b/graphics/src/triangle.rs @@ -6,20 +6,22 @@ use bytemuck::{Pod, Zeroable}; pub struct Mesh2D { /// The vertices of the mesh pub vertices: Vec, - /// The list of vertex indices that defines the triangles of the mesh. - /// - /// Therefore, this list should always have a length that is a multiple of - /// 3. pub indices: Vec, } -/// A two-dimensional vertex with some color in __linear__ RGBA. +/// A two-dimensional vertex. #[derive(Copy, Clone, Debug, Zeroable, Pod)] #[repr(C)] pub struct Vertex2D { - /// The vertex position + /// The vertex position in 2D space. pub position: [f32; 2], - /// The vertex color in __linear__ RGBA. - pub color: [f32; 4], +} + +/// Convert from lyon's position data to Iced's Vertex2D type. +impl Vertex2D { + /// Converts from [`lyon::math::Point`] to [`Vertex2D`]. Used for generating primitives. + pub fn from(points: Vec) -> Vec { + points.iter().map(|p| Vertex2D { position: [p.x, p.y]}).collect() + } } diff --git a/graphics/src/widget/canvas.rs b/graphics/src/widget/canvas.rs index 88403fd7..09aad98d 100644 --- a/graphics/src/widget/canvas.rs +++ b/graphics/src/widget/canvas.rs @@ -5,6 +5,7 @@ //! and more! pub mod event; +pub mod gradient; pub mod path; mod cache; @@ -19,12 +20,13 @@ mod text; pub use cache::Cache; pub use cursor::Cursor; pub use event::Event; -pub use fill::{Fill, FillRule}; +pub use fill::{Fill, FillRule, FillStyle}; pub use frame::Frame; pub use geometry::Geometry; +pub use gradient::Gradient; pub use path::Path; pub use program::Program; -pub use stroke::{LineCap, LineDash, LineJoin, Stroke}; +pub use stroke::{LineCap, LineDash, LineJoin, Stroke, StrokeStyle}; pub use text::Text; use crate::{Backend, Primitive, Renderer}; diff --git a/graphics/src/widget/canvas/fill.rs b/graphics/src/widget/canvas/fill.rs index 56495435..02d2311f 100644 --- a/graphics/src/widget/canvas/fill.rs +++ b/graphics/src/widget/canvas/fill.rs @@ -1,12 +1,14 @@ use iced_native::Color; +use crate::widget::canvas::Gradient; + /// The style used to fill geometry. -#[derive(Debug, Clone, Copy)] -pub struct Fill { - /// The color used to fill geometry. +#[derive(Debug, Clone)] +pub struct Fill<'a> { + /// The color or gradient of the fill. /// - /// By default, it is set to `BLACK`. - pub color: Color, + /// By default, it is set to [`FillStyle::Solid`] `BLACK`. + pub style: FillStyle<'a>, /// The fill rule defines how to determine what is inside and what is /// outside of a shape. @@ -19,24 +21,33 @@ pub struct Fill { pub rule: FillRule, } -impl Default for Fill { - fn default() -> Fill { +impl <'a> Default for Fill<'a> { + fn default() -> Fill<'a> { Fill { - color: Color::BLACK, + style: FillStyle::Solid(Color::BLACK), rule: FillRule::NonZero, } } } -impl From for Fill { - fn from(color: Color) -> Fill { +impl<'a> From for Fill<'a> { + fn from(color: Color) -> Fill<'a> { Fill { - color, + style: FillStyle::Solid(color), ..Fill::default() } } } +/// The color or gradient of a [`Fill`]. +#[derive(Debug, Clone)] +pub enum FillStyle<'a> { + /// A solid color + Solid(Color), + /// A color gradient + Gradient(&'a Gradient), +} + /// The fill rule defines how to determine what is inside and what is outside of /// a shape. /// diff --git a/graphics/src/widget/canvas/frame.rs b/graphics/src/widget/canvas/frame.rs index 516539ca..8845bc6a 100644 --- a/graphics/src/widget/canvas/frame.rs +++ b/graphics/src/widget/canvas/frame.rs @@ -3,11 +3,13 @@ use std::borrow::Cow; use iced_native::{Point, Rectangle, Size, Vector}; use crate::triangle; -use crate::widget::canvas::path; -use crate::widget::canvas::{Fill, Geometry, Path, Stroke, Text}; +use crate::widget::canvas::{path, Fill, Geometry, Path, Stroke, Text}; use crate::Primitive; +use crate::shader::Shader; +use crate::triangle::Vertex2D; use lyon::tessellation; +use lyon::tessellation::geometry_builder::Positions; /// The frame of a [`Canvas`]. /// @@ -15,7 +17,7 @@ use lyon::tessellation; #[allow(missing_debug_implementations)] pub struct Frame { size: Size, - buffers: lyon::tessellation::VertexBuffers, + buffers: Vec<(tessellation::VertexBuffers, Shader)>, primitives: Vec, transforms: Transforms, fill_tessellator: tessellation::FillTessellator, @@ -42,7 +44,7 @@ impl Frame { pub fn new(size: Size) -> Frame { Frame { size, - buffers: lyon::tessellation::VertexBuffers::new(), + buffers: Vec::new(), primitives: Vec::new(), transforms: Transforms { previous: Vec::new(), @@ -82,18 +84,18 @@ impl Frame { /// Draws the given [`Path`] on the [`Frame`] by filling it with the /// provided style. - pub fn fill(&mut self, path: &Path, fill: impl Into) { - let Fill { color, rule } = fill.into(); + pub fn fill<'a>(&mut self, path: &Path, fill: impl Into>) { + let Fill { style, rule } = fill.into(); - let mut buffers = tessellation::BuffersBuilder::new( - &mut self.buffers, - FillVertex(color.into_linear()), - ); + let mut buf = tessellation::VertexBuffers::new(); + + let mut buffers = + tessellation::BuffersBuilder::new(&mut buf, Positions); let options = tessellation::FillOptions::default().with_fill_rule(rule.into()); - let result = if self.transforms.current.is_identity { + if self.transforms.current.is_identity { self.fill_tessellator.tessellate_path( path.raw(), &options, @@ -107,25 +109,24 @@ impl Frame { &options, &mut buffers, ) - }; + }.expect("Tessellate path."); - result.expect("Tessellate path"); + self.buffers.push((buf, style.into())) } /// Draws an axis-aligned rectangle given its top-left corner coordinate and /// its `Size` on the [`Frame`] by filling it with the provided style. - pub fn fill_rectangle( + pub fn fill_rectangle<'a>( &mut self, top_left: Point, size: Size, - fill: impl Into, + fill: impl Into>, ) { - let Fill { color, rule } = fill.into(); + let Fill { style, rule } = fill.into(); + + let mut buf = tessellation::VertexBuffers::new(); - let mut buffers = tessellation::BuffersBuilder::new( - &mut self.buffers, - FillVertex(color.into_linear()), - ); + let mut buffers = tessellation::BuffersBuilder::new(&mut buf, Positions); let top_left = self.transforms.current.raw.transform_point( @@ -147,6 +148,8 @@ impl Frame { &mut buffers, ) .expect("Fill rectangle"); + + self.buffers.push((buf, style.into())) } /// Draws the stroke of the given [`Path`] on the [`Frame`] with the @@ -154,10 +157,9 @@ impl Frame { pub fn stroke<'a>(&mut self, path: &Path, stroke: impl Into>) { let stroke = stroke.into(); - let mut buffers = tessellation::BuffersBuilder::new( - &mut self.buffers, - StrokeVertex(stroke.color.into_linear()), - ); + let mut buf = tessellation::VertexBuffers::new(); + + let mut buffers = tessellation::BuffersBuilder::new(&mut buf, Positions); let mut options = tessellation::StrokeOptions::default(); options.line_width = stroke.width; @@ -171,7 +173,7 @@ impl Frame { Cow::Owned(path::dashed(path, stroke.line_dash)) }; - let result = if self.transforms.current.is_identity { + if self.transforms.current.is_identity { self.stroke_tessellator.tessellate_path( path.raw(), &options, @@ -185,9 +187,9 @@ impl Frame { &options, &mut buffers, ) - }; + }.expect("Stroke path"); - result.expect("Stroke path"); + self.buffers.push((buf, stroke.style.into())) } /// Draws the characters of the given [`Text`] on the [`Frame`], filling @@ -206,8 +208,6 @@ impl Frame { /// /// [`Canvas`]: crate::widget::Canvas pub fn fill_text(&mut self, text: impl Into) { - use std::f32; - let text = text.into(); let position = if self.transforms.current.is_identity { @@ -331,52 +331,19 @@ impl Frame { } fn into_primitives(mut self) -> Vec { - if !self.buffers.indices.is_empty() { - self.primitives.push(Primitive::Mesh2D { - buffers: triangle::Mesh2D { - vertices: self.buffers.vertices, - indices: self.buffers.indices, - }, - size: self.size, - }); + for (buffer, shader) in self.buffers { + if !buffer.indices.is_empty() { + self.primitives.push(Primitive::Mesh2D { + buffers: triangle::Mesh2D { + vertices: Vertex2D::from(buffer.vertices), + indices: buffer.indices, + }, + size: self.size, + shader, + }) + } } self.primitives } } - -struct FillVertex([f32; 4]); - -impl lyon::tessellation::FillVertexConstructor - for FillVertex -{ - fn new_vertex( - &mut self, - vertex: lyon::tessellation::FillVertex<'_>, - ) -> triangle::Vertex2D { - let position = vertex.position(); - - triangle::Vertex2D { - position: [position.x, position.y], - color: self.0, - } - } -} - -struct StrokeVertex([f32; 4]); - -impl lyon::tessellation::StrokeVertexConstructor - for StrokeVertex -{ - fn new_vertex( - &mut self, - vertex: lyon::tessellation::StrokeVertex<'_, '_>, - ) -> triangle::Vertex2D { - let position = vertex.position(); - - triangle::Vertex2D { - position: [position.x, position.y], - color: self.0, - } - } -} diff --git a/graphics/src/widget/canvas/gradient.rs b/graphics/src/widget/canvas/gradient.rs new file mode 100644 index 00000000..7d2daabc --- /dev/null +++ b/graphics/src/widget/canvas/gradient.rs @@ -0,0 +1,21 @@ +//! Define a color gradient. +use iced_native::Point; + +pub mod linear; + +pub use linear::Linear; + +/// A gradient that can be used in the style of [`super::Fill`] or [`super::Stroke`]. +#[derive(Debug, Clone)] +pub enum Gradient { + /// A linear gradient + Linear(Linear), + //TODO: radial, conical +} + +impl Gradient { + /// Creates a new linear [`linear::Builder`]. + pub fn linear(start: Point, end: Point) -> linear::Builder { + linear::Builder::new(start, end) + } +} \ No newline at end of file diff --git a/graphics/src/widget/canvas/gradient/linear.rs b/graphics/src/widget/canvas/gradient/linear.rs new file mode 100644 index 00000000..37533e19 --- /dev/null +++ b/graphics/src/widget/canvas/gradient/linear.rs @@ -0,0 +1,73 @@ +//! A linear color gradient. +use iced_native::{Color, Point}; + +use crate::gradient::ColorStop; + +use super::Gradient; + +/// A linear gradient that can be used in the style of [`super::Fill`] or [`super::Stroke`]. +#[derive(Debug, Clone, PartialEq)] +pub struct Linear { + /// The point where the linear gradient begins. + pub start: Point, + /// The point where the linear gradient ends. + pub end: Point, + /// [`ColorStop`]s along the linear gradient path. + pub color_stops: Vec, +} + +/// A [`Linear`] builder. +#[derive(Debug)] +pub struct Builder { + start: Point, + end: Point, + stops: Vec<(f32, Color)>, + valid: bool, +} + +impl Builder { + /// Creates a new [`Builder`]. + pub fn new(start: Point, end: Point) -> Self { + Self { + start, + end, + stops: vec![], + valid: true, + } + } + + /// Adds a new stop, defined by an offset and a color, to the gradient. + /// + /// `offset` must be between `0.0` and `1.0`. + pub fn add_stop(mut self, offset: f32, color: Color) -> Self { + if !(0.0..=1.0).contains(&offset) { + self.valid = false; + } + + self.stops.push((offset, color)); + self + } + + /// Builds the linear [`Gradient`] of this [`Builder`]. + /// + /// Returns `None` if no stops were added to the builder or + /// if stops not between 0.0 and 1.0 were added. + pub fn build(self) -> Option { + if self.stops.is_empty() || !self.valid { + return None; + } + + Some(Gradient::Linear(Linear { + start: self.start, + end: self.end, + color_stops: self + .stops + .into_iter() + .map(|f| ColorStop { + offset: f.0, + color: f.1, + }) + .collect(), + })) + } +} diff --git a/graphics/src/widget/canvas/stroke.rs b/graphics/src/widget/canvas/stroke.rs index 6accc2fb..c319b398 100644 --- a/graphics/src/widget/canvas/stroke.rs +++ b/graphics/src/widget/canvas/stroke.rs @@ -1,10 +1,14 @@ use iced_native::Color; +use crate::widget::canvas::Gradient; + /// The style of a stroke. -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone)] pub struct Stroke<'a> { - /// The color of the stroke. - pub color: Color, + /// The color or gradient of the stroke. + /// + /// By default, it is set to [`StrokeStyle::Solid`] `BLACK`. + pub style: StrokeStyle<'a>, /// The distance between the two edges of the stroke. pub width: f32, /// The shape to be used at the end of open subpaths when they are stroked. @@ -19,7 +23,10 @@ pub struct Stroke<'a> { impl<'a> Stroke<'a> { /// Sets the color of the [`Stroke`]. pub fn with_color(self, color: Color) -> Self { - Stroke { color, ..self } + Stroke { + style: StrokeStyle::Solid(color), + ..self + } } /// Sets the width of the [`Stroke`]. @@ -41,7 +48,7 @@ impl<'a> Stroke<'a> { impl<'a> Default for Stroke<'a> { fn default() -> Self { Stroke { - color: Color::BLACK, + style: StrokeStyle::Solid(Color::BLACK), width: 1.0, line_cap: LineCap::default(), line_join: LineJoin::default(), @@ -50,6 +57,15 @@ impl<'a> Default for Stroke<'a> { } } +/// The color or gradient of a [`Stroke`]. +#[derive(Debug, Clone, Copy)] +pub enum StrokeStyle<'a> { + /// A solid color + Solid(Color), + /// A color gradient + Gradient(&'a Gradient), +} + /// The shape used at the end of open subpaths when they are stroked. #[derive(Debug, Clone, Copy)] pub enum LineCap { -- cgit From 734557bda5924e563a9f0b39aca37d5953fcda5c Mon Sep 17 00:00:00 2001 From: shan Date: Thu, 29 Sep 2022 14:01:57 -0700 Subject: Fixed issue where stops could be declared out of order in the builder but must be sorted before being passed to shader. --- graphics/src/widget/canvas/gradient/linear.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'graphics/src') diff --git a/graphics/src/widget/canvas/gradient/linear.rs b/graphics/src/widget/canvas/gradient/linear.rs index 37533e19..1dc7e3a8 100644 --- a/graphics/src/widget/canvas/gradient/linear.rs +++ b/graphics/src/widget/canvas/gradient/linear.rs @@ -57,17 +57,17 @@ impl Builder { return None; } + let mut stops: Vec = self.stops.clone().into_iter().map(|f| ColorStop { + offset: f.0, + color: f.1 + }).collect(); + + stops.sort_by(|a, b| a.offset.partial_cmp(&b.offset).unwrap()); + Some(Gradient::Linear(Linear { start: self.start, end: self.end, - color_stops: self - .stops - .into_iter() - .map(|f| ColorStop { - offset: f.0, - color: f.1, - }) - .collect(), + color_stops: stops })) } } -- cgit From 5d0fffc626928177239336757507b986b081b878 Mon Sep 17 00:00:00 2001 From: shan Date: Fri, 30 Sep 2022 10:27:00 -0700 Subject: Fixed some importing issues since you can use a Shader::Gradient outside a Canvas widget, where it was previously only accessible. --- graphics/src/gradient.rs | 92 +++++++++++++++++++++++++-- graphics/src/shader.rs | 25 +------- graphics/src/triangle.rs | 10 +-- graphics/src/widget/canvas.rs | 16 ++--- graphics/src/widget/canvas/fill.rs | 13 +++- graphics/src/widget/canvas/frame.rs | 7 +- graphics/src/widget/canvas/gradient.rs | 21 ------ graphics/src/widget/canvas/gradient/linear.rs | 73 --------------------- graphics/src/widget/canvas/stroke.rs | 13 +++- 9 files changed, 123 insertions(+), 147 deletions(-) delete mode 100644 graphics/src/widget/canvas/gradient.rs delete mode 100644 graphics/src/widget/canvas/gradient/linear.rs (limited to 'graphics/src') diff --git a/graphics/src/gradient.rs b/graphics/src/gradient.rs index 4d1eff62..fa57842b 100644 --- a/graphics/src/gradient.rs +++ b/graphics/src/gradient.rs @@ -1,18 +1,17 @@ //! For creating a Gradient. - use iced_native::Color; -use crate::widget::canvas::gradient::Linear; +use crate::gradient::linear::Linear; +use crate::Point; #[derive(Debug, Clone, PartialEq)] -/// A fill which transitions colors progressively along a direction, either linearly, radially, -/// or conically. +/// A fill which transitions colors progressively along a direction, either linearly, radially (TBD), +/// or conically (TBD). pub enum Gradient { /// A linear gradient interpolates colors along a direction from its [`start`] to its [`end`] /// point. Linear(Linear), } - #[derive(Debug, Clone, Copy, PartialEq)] /// A point along the gradient vector where the specified [`color`] is unmixed. pub struct ColorStop { @@ -20,4 +19,85 @@ pub struct ColorStop { pub offset: f32, /// The color of the gradient at the specified [`offset`]. pub color: Color, -} \ No newline at end of file +} + +impl Gradient { + /// Creates a new linear [`linear::Builder`]. + pub fn linear(start: Point, end: Point) -> linear::Builder { + linear::Builder::new(start, end) + } +} + +/// Linear gradient builder & definition. +pub mod linear { + use crate::gradient::{ColorStop, Gradient}; + use crate::{Color, Point}; + + /// A linear gradient that can be used in the style of [`super::Fill`] or [`super::Stroke`]. + #[derive(Debug, Clone, PartialEq)] + pub struct Linear { + /// The point where the linear gradient begins. + pub start: Point, + /// The point where the linear gradient ends. + pub end: Point, + /// [`ColorStop`]s along the linear gradient path. + pub color_stops: Vec, + } + + /// A [`Linear`] builder. + #[derive(Debug)] + pub struct Builder { + start: Point, + end: Point, + stops: Vec<(f32, Color)>, + valid: bool, + } + + impl Builder { + /// Creates a new [`Builder`]. + pub fn new(start: Point, end: Point) -> Self { + Self { + start, + end, + stops: vec![], + valid: true, + } + } + + /// Adds a new stop, defined by an offset and a color, to the gradient. + /// + /// `offset` must be between `0.0` and `1.0`. + pub fn add_stop(mut self, offset: f32, color: Color) -> Self { + if !(0.0..=1.0).contains(&offset) { + self.valid = false; + } + + self.stops.push((offset, color)); + self + } + + /// Builds the linear [`Gradient`] of this [`Builder`]. + /// + /// Returns `None` if no stops were added to the builder or + /// if stops not between 0.0 and 1.0 were added. + pub fn build(self) -> Option { + if self.stops.is_empty() || !self.valid { + return None; + } + + let mut stops: Vec = self.stops.clone().into_iter().map(|f| ColorStop { + offset: f.0, + color: f.1 + }).collect(); + + stops.sort_by(|a, b| a.offset.partial_cmp(&b.offset).unwrap()); + + Some(Gradient::Linear(Linear { + start: self.start, + end: self.end, + color_stops: stops + })) + } + } +} + diff --git a/graphics/src/shader.rs b/graphics/src/shader.rs index b9071c74..69679e9b 100644 --- a/graphics/src/shader.rs +++ b/graphics/src/shader.rs @@ -1,8 +1,7 @@ //! Supported shaders; -use crate::{Color, widget}; +use crate::Color; use crate::gradient::Gradient; -use crate::widget::canvas::{FillStyle, StrokeStyle}; #[derive(Debug, Clone)] /// Supported shaders for primitives. @@ -13,28 +12,10 @@ pub enum Shader { Gradient(Gradient) } -impl <'a> Into for StrokeStyle<'a> { +impl <'a> Into for Gradient { fn into(self) -> Shader { match self { - StrokeStyle::Solid(color) => Shader::Solid(color), - StrokeStyle::Gradient(gradient) => gradient.clone().into() - } - } -} - -impl <'a> Into for FillStyle<'a> { - fn into(self) -> Shader { - match self { - FillStyle::Solid(color) => Shader::Solid(color), - FillStyle::Gradient(gradient) => gradient.clone().into() - } - } -} - -impl <'a> Into for widget::canvas::Gradient { - fn into(self) -> Shader { - match self { - widget::canvas::Gradient::Linear(linear) => { + Gradient::Linear(linear) => { Shader::Gradient(Gradient::Linear(linear)) } } diff --git a/graphics/src/triangle.rs b/graphics/src/triangle.rs index 92709fe2..8f82c5df 100644 --- a/graphics/src/triangle.rs +++ b/graphics/src/triangle.rs @@ -16,12 +16,4 @@ pub struct Mesh2D { pub struct Vertex2D { /// The vertex position in 2D space. pub position: [f32; 2], -} - -/// Convert from lyon's position data to Iced's Vertex2D type. -impl Vertex2D { - /// Converts from [`lyon::math::Point`] to [`Vertex2D`]. Used for generating primitives. - pub fn from(points: Vec) -> Vec { - points.iter().map(|p| Vertex2D { position: [p.x, p.y]}).collect() - } -} +} \ No newline at end of file diff --git a/graphics/src/widget/canvas.rs b/graphics/src/widget/canvas.rs index 09aad98d..95c962af 100644 --- a/graphics/src/widget/canvas.rs +++ b/graphics/src/widget/canvas.rs @@ -5,7 +5,6 @@ //! and more! pub mod event; -pub mod gradient; pub mod path; mod cache; @@ -23,7 +22,6 @@ pub use event::Event; pub use fill::{Fill, FillRule, FillStyle}; pub use frame::Frame; pub use geometry::Geometry; -pub use gradient::Gradient; pub use path::Path; pub use program::Program; pub use stroke::{LineCap, LineDash, LineJoin, Stroke, StrokeStyle}; @@ -47,16 +45,12 @@ use std::marker::PhantomData; /// If you want to get a quick overview, here's how we can draw a simple circle: /// /// ```no_run -/// # mod iced { -/// # pub mod widget { -/// # pub use iced_graphics::widget::canvas; -/// # } -/// # pub use iced_native::{Color, Rectangle, Theme}; -/// # } -/// use iced::widget::canvas::{self, Canvas, Cursor, Fill, Frame, Geometry, Path, Program}; -/// use iced::{Color, Rectangle, Theme}; -/// /// // First, we define the data we need for drawing +/// use iced_graphics::{Color, Rectangle}; +/// use iced_graphics::widget::Canvas; +/// use iced_graphics::widget::canvas::{Cursor, Frame, Geometry, Path, Program}; +/// use iced_style::Theme; +/// /// #[derive(Debug)] /// struct Circle { /// radius: f32, diff --git a/graphics/src/widget/canvas/fill.rs b/graphics/src/widget/canvas/fill.rs index 02d2311f..60029e03 100644 --- a/graphics/src/widget/canvas/fill.rs +++ b/graphics/src/widget/canvas/fill.rs @@ -1,6 +1,6 @@ use iced_native::Color; - -use crate::widget::canvas::Gradient; +use crate::gradient::Gradient; +use crate::shader::Shader; /// The style used to fill geometry. #[derive(Debug, Clone)] @@ -48,6 +48,15 @@ pub enum FillStyle<'a> { Gradient(&'a Gradient), } +impl <'a> Into for FillStyle<'a> { + fn into(self) -> Shader { + match self { + FillStyle::Solid(color) => Shader::Solid(color), + FillStyle::Gradient(gradient) => gradient.clone().into() + } + } +} + /// The fill rule defines how to determine what is inside and what is outside of /// a shape. /// diff --git a/graphics/src/widget/canvas/frame.rs b/graphics/src/widget/canvas/frame.rs index 8845bc6a..30239b2a 100644 --- a/graphics/src/widget/canvas/frame.rs +++ b/graphics/src/widget/canvas/frame.rs @@ -335,7 +335,7 @@ impl Frame { if !buffer.indices.is_empty() { self.primitives.push(Primitive::Mesh2D { buffers: triangle::Mesh2D { - vertices: Vertex2D::from(buffer.vertices), + vertices: vertices_from(buffer.vertices), indices: buffer.indices, }, size: self.size, @@ -347,3 +347,8 @@ impl Frame { self.primitives } } + +/// Converts from [`lyon::math::Point`] to [`Vertex2D`]. Used for generating primitives. +fn vertices_from(points: Vec) -> Vec { + points.iter().map(|p| Vertex2D { position: [p.x, p.y]}).collect() +} \ No newline at end of file diff --git a/graphics/src/widget/canvas/gradient.rs b/graphics/src/widget/canvas/gradient.rs deleted file mode 100644 index 7d2daabc..00000000 --- a/graphics/src/widget/canvas/gradient.rs +++ /dev/null @@ -1,21 +0,0 @@ -//! Define a color gradient. -use iced_native::Point; - -pub mod linear; - -pub use linear::Linear; - -/// A gradient that can be used in the style of [`super::Fill`] or [`super::Stroke`]. -#[derive(Debug, Clone)] -pub enum Gradient { - /// A linear gradient - Linear(Linear), - //TODO: radial, conical -} - -impl Gradient { - /// Creates a new linear [`linear::Builder`]. - pub fn linear(start: Point, end: Point) -> linear::Builder { - linear::Builder::new(start, end) - } -} \ No newline at end of file diff --git a/graphics/src/widget/canvas/gradient/linear.rs b/graphics/src/widget/canvas/gradient/linear.rs deleted file mode 100644 index 1dc7e3a8..00000000 --- a/graphics/src/widget/canvas/gradient/linear.rs +++ /dev/null @@ -1,73 +0,0 @@ -//! A linear color gradient. -use iced_native::{Color, Point}; - -use crate::gradient::ColorStop; - -use super::Gradient; - -/// A linear gradient that can be used in the style of [`super::Fill`] or [`super::Stroke`]. -#[derive(Debug, Clone, PartialEq)] -pub struct Linear { - /// The point where the linear gradient begins. - pub start: Point, - /// The point where the linear gradient ends. - pub end: Point, - /// [`ColorStop`]s along the linear gradient path. - pub color_stops: Vec, -} - -/// A [`Linear`] builder. -#[derive(Debug)] -pub struct Builder { - start: Point, - end: Point, - stops: Vec<(f32, Color)>, - valid: bool, -} - -impl Builder { - /// Creates a new [`Builder`]. - pub fn new(start: Point, end: Point) -> Self { - Self { - start, - end, - stops: vec![], - valid: true, - } - } - - /// Adds a new stop, defined by an offset and a color, to the gradient. - /// - /// `offset` must be between `0.0` and `1.0`. - pub fn add_stop(mut self, offset: f32, color: Color) -> Self { - if !(0.0..=1.0).contains(&offset) { - self.valid = false; - } - - self.stops.push((offset, color)); - self - } - - /// Builds the linear [`Gradient`] of this [`Builder`]. - /// - /// Returns `None` if no stops were added to the builder or - /// if stops not between 0.0 and 1.0 were added. - pub fn build(self) -> Option { - if self.stops.is_empty() || !self.valid { - return None; - } - - let mut stops: Vec = self.stops.clone().into_iter().map(|f| ColorStop { - offset: f.0, - color: f.1 - }).collect(); - - stops.sort_by(|a, b| a.offset.partial_cmp(&b.offset).unwrap()); - - Some(Gradient::Linear(Linear { - start: self.start, - end: self.end, - color_stops: stops - })) - } -} diff --git a/graphics/src/widget/canvas/stroke.rs b/graphics/src/widget/canvas/stroke.rs index c319b398..ed82f189 100644 --- a/graphics/src/widget/canvas/stroke.rs +++ b/graphics/src/widget/canvas/stroke.rs @@ -1,6 +1,6 @@ use iced_native::Color; - -use crate::widget::canvas::Gradient; +use crate::gradient::Gradient; +use crate::shader::Shader; /// The style of a stroke. #[derive(Debug, Clone)] @@ -66,6 +66,15 @@ pub enum StrokeStyle<'a> { Gradient(&'a Gradient), } +impl <'a> Into for StrokeStyle<'a> { + fn into(self) -> Shader { + match self { + StrokeStyle::Solid(color) => Shader::Solid(color), + StrokeStyle::Gradient(gradient) => gradient.clone().into() + } + } +} + /// The shape used at the end of open subpaths when they are stroked. #[derive(Debug, Clone, Copy)] pub enum LineCap { -- cgit From 6e7b3ced0b1daf368e44e181ecdb4ae529877eb6 Mon Sep 17 00:00:00 2001 From: shan Date: Tue, 4 Oct 2022 18:24:46 -0700 Subject: Reworked wgpu buffers, updated glow side to have proper transform location storage, attempting to fix visibility modifiers, implemented some of the feedback received in initial PR. --- graphics/src/gradient.rs | 2 +- graphics/src/layer.rs | 32 +++++++++++++------------------- graphics/src/widget/canvas.rs | 4 ++-- graphics/src/widget/canvas/fill.rs | 14 +++++++------- graphics/src/widget/canvas/stroke.rs | 14 +++++++------- 5 files changed, 30 insertions(+), 36 deletions(-) (limited to 'graphics/src') diff --git a/graphics/src/gradient.rs b/graphics/src/gradient.rs index fa57842b..0c394e8b 100644 --- a/graphics/src/gradient.rs +++ b/graphics/src/gradient.rs @@ -1,6 +1,6 @@ //! For creating a Gradient. use iced_native::Color; -use crate::gradient::linear::Linear; +pub use crate::gradient::linear::Linear; use crate::Point; #[derive(Debug, Clone, PartialEq)] diff --git a/graphics/src/layer.rs b/graphics/src/layer.rs index b7731922..096c50dc 100644 --- a/graphics/src/layer.rs +++ b/graphics/src/layer.rs @@ -19,7 +19,7 @@ pub struct Layer<'a> { pub quads: Vec, /// The triangle meshes of the [`Layer`]. - pub meshes: Meshes<'a>, + pub meshes: Vec>, /// The text of the [`Layer`]. pub text: Vec>, @@ -34,7 +34,7 @@ impl<'a> Layer<'a> { Self { bounds, quads: Vec::new(), - meshes: Meshes(Vec::new()), + meshes: Vec::new(), text: Vec::new(), images: Vec::new(), } @@ -174,7 +174,7 @@ impl<'a> Layer<'a> { // Only draw visible content if let Some(clip_bounds) = layer.bounds.intersection(&bounds) { - layer.meshes.0.push( + layer.meshes.push( Mesh { origin: Point::new(translation.x, translation.y), buffers, @@ -335,20 +335,14 @@ unsafe impl bytemuck::Zeroable for Quad {} #[allow(unsafe_code)] unsafe impl bytemuck::Pod for Quad {} -#[derive(Debug)] -/// A collection of meshes. -pub struct Meshes<'a>(pub Vec>); - -impl<'a> Meshes<'a> { - /// Returns the number of total vertices & total indices of all [`Mesh`]es. - pub fn attribute_count(&self) -> (usize, usize) { - self.0 - .iter() - .map(|Mesh { buffers, .. }| { - (buffers.vertices.len(), buffers.indices.len()) - }) - .fold((0, 0), |(total_v, total_i), (v, i)| { - (total_v + v, total_i + i) - }) - } +/// Returns the number of total vertices & total indices of all [`Mesh`]es. +pub fn attribute_count_of<'a>(meshes: &'a [Mesh<'a>]) -> (usize, usize) { + meshes + .iter() + .map(|Mesh { buffers, .. }| { + (buffers.vertices.len(), buffers.indices.len()) + }) + .fold((0, 0), |(total_v, total_i), (v, i)| { + (total_v + v, total_i + i) + }) } \ No newline at end of file diff --git a/graphics/src/widget/canvas.rs b/graphics/src/widget/canvas.rs index 95c962af..f6929e97 100644 --- a/graphics/src/widget/canvas.rs +++ b/graphics/src/widget/canvas.rs @@ -19,12 +19,12 @@ mod text; pub use cache::Cache; pub use cursor::Cursor; pub use event::Event; -pub use fill::{Fill, FillRule, FillStyle}; +pub use fill::{Fill, FillRule, Style}; pub use frame::Frame; pub use geometry::Geometry; pub use path::Path; pub use program::Program; -pub use stroke::{LineCap, LineDash, LineJoin, Stroke, StrokeStyle}; +pub use stroke::{LineCap, LineDash, LineJoin, Stroke}; pub use text::Text; use crate::{Backend, Primitive, Renderer}; diff --git a/graphics/src/widget/canvas/fill.rs b/graphics/src/widget/canvas/fill.rs index 60029e03..6f10505c 100644 --- a/graphics/src/widget/canvas/fill.rs +++ b/graphics/src/widget/canvas/fill.rs @@ -8,7 +8,7 @@ pub struct Fill<'a> { /// The color or gradient of the fill. /// /// By default, it is set to [`FillStyle::Solid`] `BLACK`. - pub style: FillStyle<'a>, + pub style: Style<'a>, /// The fill rule defines how to determine what is inside and what is /// outside of a shape. @@ -24,7 +24,7 @@ pub struct Fill<'a> { impl <'a> Default for Fill<'a> { fn default() -> Fill<'a> { Fill { - style: FillStyle::Solid(Color::BLACK), + style: Style::Solid(Color::BLACK), rule: FillRule::NonZero, } } @@ -33,7 +33,7 @@ impl <'a> Default for Fill<'a> { impl<'a> From for Fill<'a> { fn from(color: Color) -> Fill<'a> { Fill { - style: FillStyle::Solid(color), + style: Style::Solid(color), ..Fill::default() } } @@ -41,18 +41,18 @@ impl<'a> From for Fill<'a> { /// The color or gradient of a [`Fill`]. #[derive(Debug, Clone)] -pub enum FillStyle<'a> { +pub enum Style<'a> { /// A solid color Solid(Color), /// A color gradient Gradient(&'a Gradient), } -impl <'a> Into for FillStyle<'a> { +impl <'a> Into for Style<'a> { fn into(self) -> Shader { match self { - FillStyle::Solid(color) => Shader::Solid(color), - FillStyle::Gradient(gradient) => gradient.clone().into() + Style::Solid(color) => Shader::Solid(color), + Style::Gradient(gradient) => gradient.clone().into() } } } diff --git a/graphics/src/widget/canvas/stroke.rs b/graphics/src/widget/canvas/stroke.rs index ed82f189..7ce5ff1d 100644 --- a/graphics/src/widget/canvas/stroke.rs +++ b/graphics/src/widget/canvas/stroke.rs @@ -8,7 +8,7 @@ pub struct Stroke<'a> { /// The color or gradient of the stroke. /// /// By default, it is set to [`StrokeStyle::Solid`] `BLACK`. - pub style: StrokeStyle<'a>, + pub style: Style<'a>, /// The distance between the two edges of the stroke. pub width: f32, /// The shape to be used at the end of open subpaths when they are stroked. @@ -24,7 +24,7 @@ impl<'a> Stroke<'a> { /// Sets the color of the [`Stroke`]. pub fn with_color(self, color: Color) -> Self { Stroke { - style: StrokeStyle::Solid(color), + style: Style::Solid(color), ..self } } @@ -48,7 +48,7 @@ impl<'a> Stroke<'a> { impl<'a> Default for Stroke<'a> { fn default() -> Self { Stroke { - style: StrokeStyle::Solid(Color::BLACK), + style: Style::Solid(Color::BLACK), width: 1.0, line_cap: LineCap::default(), line_join: LineJoin::default(), @@ -59,18 +59,18 @@ impl<'a> Default for Stroke<'a> { /// The color or gradient of a [`Stroke`]. #[derive(Debug, Clone, Copy)] -pub enum StrokeStyle<'a> { +pub enum Style<'a> { /// A solid color Solid(Color), /// A color gradient Gradient(&'a Gradient), } -impl <'a> Into for StrokeStyle<'a> { +impl <'a> Into for Style<'a> { fn into(self) -> Shader { match self { - StrokeStyle::Solid(color) => Shader::Solid(color), - StrokeStyle::Gradient(gradient) => gradient.clone().into() + Style::Solid(color) => Shader::Solid(color), + Style::Gradient(gradient) => gradient.clone().into() } } } -- cgit From 30432cbade3d9b25c4df62656a7494db3f4ea82a Mon Sep 17 00:00:00 2001 From: shan Date: Wed, 5 Oct 2022 10:49:58 -0700 Subject: Readjusted namespaces, removed Geometry example as it's no longer relevant. --- graphics/src/gradient.rs | 75 +---------------------- graphics/src/gradient/linear.rs | 71 ++++++++++++++++++++++ graphics/src/layer.rs | 111 ++++------------------------------- graphics/src/layer/image.rs | 23 ++++++++ graphics/src/layer/mesh.rs | 39 ++++++++++++ graphics/src/layer/quad.rs | 30 ++++++++++ graphics/src/layer/text.rs | 26 ++++++++ graphics/src/lib.rs | 1 - graphics/src/primitive.rs | 5 +- graphics/src/shader.rs | 23 -------- graphics/src/widget/canvas.rs | 8 ++- graphics/src/widget/canvas/fill.rs | 16 ++--- graphics/src/widget/canvas/frame.rs | 32 ++++++---- graphics/src/widget/canvas/stroke.rs | 10 ++-- 14 files changed, 247 insertions(+), 223 deletions(-) create mode 100644 graphics/src/gradient/linear.rs create mode 100644 graphics/src/layer/image.rs create mode 100644 graphics/src/layer/mesh.rs create mode 100644 graphics/src/layer/quad.rs create mode 100644 graphics/src/layer/text.rs delete mode 100644 graphics/src/shader.rs (limited to 'graphics/src') diff --git a/graphics/src/gradient.rs b/graphics/src/gradient.rs index 0c394e8b..33453c67 100644 --- a/graphics/src/gradient.rs +++ b/graphics/src/gradient.rs @@ -1,4 +1,6 @@ //! For creating a Gradient. +mod linear; + use iced_native::Color; pub use crate::gradient::linear::Linear; use crate::Point; @@ -28,76 +30,3 @@ impl Gradient { } } -/// Linear gradient builder & definition. -pub mod linear { - use crate::gradient::{ColorStop, Gradient}; - use crate::{Color, Point}; - - /// A linear gradient that can be used in the style of [`super::Fill`] or [`super::Stroke`]. - #[derive(Debug, Clone, PartialEq)] - pub struct Linear { - /// The point where the linear gradient begins. - pub start: Point, - /// The point where the linear gradient ends. - pub end: Point, - /// [`ColorStop`]s along the linear gradient path. - pub color_stops: Vec, - } - - /// A [`Linear`] builder. - #[derive(Debug)] - pub struct Builder { - start: Point, - end: Point, - stops: Vec<(f32, Color)>, - valid: bool, - } - - impl Builder { - /// Creates a new [`Builder`]. - pub fn new(start: Point, end: Point) -> Self { - Self { - start, - end, - stops: vec![], - valid: true, - } - } - - /// Adds a new stop, defined by an offset and a color, to the gradient. - /// - /// `offset` must be between `0.0` and `1.0`. - pub fn add_stop(mut self, offset: f32, color: Color) -> Self { - if !(0.0..=1.0).contains(&offset) { - self.valid = false; - } - - self.stops.push((offset, color)); - self - } - - /// Builds the linear [`Gradient`] of this [`Builder`]. - /// - /// Returns `None` if no stops were added to the builder or - /// if stops not between 0.0 and 1.0 were added. - pub fn build(self) -> Option { - if self.stops.is_empty() || !self.valid { - return None; - } - - let mut stops: Vec = self.stops.clone().into_iter().map(|f| ColorStop { - offset: f.0, - color: f.1 - }).collect(); - - stops.sort_by(|a, b| a.offset.partial_cmp(&b.offset).unwrap()); - - Some(Gradient::Linear(Linear { - start: self.start, - end: self.end, - color_stops: stops - })) - } - } -} - diff --git a/graphics/src/gradient/linear.rs b/graphics/src/gradient/linear.rs new file mode 100644 index 00000000..00f94adc --- /dev/null +++ b/graphics/src/gradient/linear.rs @@ -0,0 +1,71 @@ +//! Linear gradient builder & definition. + +use crate::gradient::{ColorStop, Gradient}; +use crate::{Color, Point}; + +/// A linear gradient that can be used in the style of [`super::Fill`] or [`super::Stroke`]. +#[derive(Debug, Clone, PartialEq)] +pub struct Linear { + /// The point where the linear gradient begins. + pub start: Point, + /// The point where the linear gradient ends. + pub end: Point, + /// [`ColorStop`]s along the linear gradient path. + pub color_stops: Vec, +} + +/// A [`Linear`] builder. +#[derive(Debug)] +pub struct Builder { + start: Point, + end: Point, + stops: Vec<(f32, Color)>, + valid: bool, +} + +impl Builder { + /// Creates a new [`Builder`]. + pub fn new(start: Point, end: Point) -> Self { + Self { + start, + end, + stops: vec![], + valid: true, + } + } + + /// Adds a new stop, defined by an offset and a color, to the gradient. + /// + /// `offset` must be between `0.0` and `1.0`. + pub fn add_stop(mut self, offset: f32, color: Color) -> Self { + if !(0.0..=1.0).contains(&offset) { + self.valid = false; + } + + self.stops.push((offset, color)); + self + } + + /// Builds the linear [`Gradient`] of this [`Builder`]. + /// + /// Returns `None` if no stops were added to the builder or + /// if stops not between 0.0 and 1.0 were added. + pub fn build(self) -> Option { + if self.stops.is_empty() || !self.valid { + return None; + } + + let mut stops: Vec = self.stops.clone().into_iter().map(|f| ColorStop { + offset: f.0, + color: f.1 + }).collect(); + + stops.sort_by(|a, b| a.offset.partial_cmp(&b.offset).unwrap()); + + Some(Gradient::Linear(Linear { + start: self.start, + end: self.end, + color_stops: stops + })) + } +} \ No newline at end of file diff --git a/graphics/src/layer.rs b/graphics/src/layer.rs index 096c50dc..65e70cb3 100644 --- a/graphics/src/layer.rs +++ b/graphics/src/layer.rs @@ -1,13 +1,17 @@ //! Organize rendering primitives into a flattened list of layers. +pub mod mesh; +mod quad; +mod text; +mod image; + use crate::alignment; -use crate::triangle; use crate::{ Background, Font, Point, Primitive, Rectangle, Size, Vector, Viewport, }; - -use iced_native::image; -use iced_native::svg; -use crate::shader::Shader; +pub use crate::layer::image::Image; +pub use crate::layer::mesh::Mesh; +pub use crate::layer::quad::Quad; +pub use crate::layer::text::Text; /// A group of primitives that should be clipped together. #[derive(Debug)] @@ -163,7 +167,7 @@ impl<'a> Layer<'a> { Primitive::Mesh2D { buffers, size, - shader, + style, } => { let layer = &mut layers[current_layer]; @@ -179,7 +183,7 @@ impl<'a> Layer<'a> { origin: Point::new(translation.x, translation.y), buffers, clip_bounds, - shader, + style, } ); } @@ -242,99 +246,6 @@ 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`]. - pub position: [f32; 2], - - /// The size of the [`Quad`]. - pub size: [f32; 2], - - /// The color of the [`Quad`], in __linear RGB__. - pub color: [f32; 4], - - /// The border color of the [`Quad`], in __linear RGB__. - pub border_color: [f32; 4], - - /// The border radius of the [`Quad`]. - pub border_radius: f32, - - /// The border width of the [`Quad`]. - pub border_width: f32, -} - -/// A mesh of triangles. -#[derive(Debug, Clone, Copy)] -pub struct Mesh<'a> { - /// The origin of the vertices of the [`Mesh`]. - pub origin: Point, - - /// The vertex and index buffers of the [`Mesh`]. - pub buffers: &'a triangle::Mesh2D, - - /// The clipping bounds of the [`Mesh`]. - pub clip_bounds: Rectangle, - - /// The shader of the [`Mesh`]. - pub shader: &'a Shader, -} - -/// A paragraph of text. -#[derive(Debug, Clone, Copy)] -pub struct Text<'a> { - /// The content of the [`Text`]. - pub content: &'a str, - - /// The layout bounds of the [`Text`]. - pub bounds: Rectangle, - - /// The color of the [`Text`], in __linear RGB_. - pub color: [f32; 4], - - /// The size of the [`Text`]. - pub size: f32, - - /// The font of the [`Text`]. - pub font: Font, - - /// The horizontal alignment of the [`Text`]. - pub horizontal_alignment: alignment::Horizontal, - - /// The vertical alignment of the [`Text`]. - pub vertical_alignment: alignment::Vertical, -} - -/// 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 {} - /// Returns the number of total vertices & total indices of all [`Mesh`]es. pub fn attribute_count_of<'a>(meshes: &'a [Mesh<'a>]) -> (usize, usize) { meshes diff --git a/graphics/src/layer/image.rs b/graphics/src/layer/image.rs new file mode 100644 index 00000000..387b60ed --- /dev/null +++ b/graphics/src/layer/image.rs @@ -0,0 +1,23 @@ +use iced_native::{image, svg}; +use crate::Rectangle; + +/// 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, + }, +} \ No newline at end of file diff --git a/graphics/src/layer/mesh.rs b/graphics/src/layer/mesh.rs new file mode 100644 index 00000000..a025675a --- /dev/null +++ b/graphics/src/layer/mesh.rs @@ -0,0 +1,39 @@ +//! A collection of triangle primitives. + +use crate::{Color, Point, Rectangle, triangle}; +use crate::gradient::Gradient; + +/// A mesh of triangles. +#[derive(Debug, Clone, Copy)] +pub struct Mesh<'a> { + /// The origin of the vertices of the [`Mesh`]. + pub origin: Point, + + /// The vertex and index buffers of the [`Mesh`]. + pub buffers: &'a triangle::Mesh2D, + + /// The clipping bounds of the [`Mesh`]. + pub clip_bounds: Rectangle, + + /// The shader of the [`Mesh`]. + pub style: &'a Style, +} + +#[derive(Debug, Clone)] +/// Supported shaders for primitives. +pub enum Style { + /// Fill a primitive with a solid color. + Solid(Color), + /// Fill a primitive with an interpolated color. + Gradient(Gradient) +} + +impl <'a> Into