summaryrefslogtreecommitdiffstats
path: root/graphics
diff options
context:
space:
mode:
authorLibravatar shan <shankern@protonmail.com>2022-09-29 10:52:58 -0700
committerLibravatar shan <shankern@protonmail.com>2022-09-29 11:15:35 -0700
commit40f45d7b7e35dd4937abe6b5ce16b6256b4f1eeb (patch)
tree38ffc5dd6bae5da4da3b93664dfe27e024dfa261 /graphics
parent97f385e093711c269df315b28f76e66e0220e22a (diff)
downloadiced-40f45d7b7e35dd4937abe6b5ce16b6256b4f1eeb.tar.gz
iced-40f45d7b7e35dd4937abe6b5ce16b6256b4f1eeb.tar.bz2
iced-40f45d7b7e35dd4937abe6b5ce16b6256b4f1eeb.zip
Adds linear gradient support to 2D meshes in the canvas widget.
Diffstat (limited to 'graphics')
-rw-r--r--graphics/Cargo.toml2
-rw-r--r--graphics/src/gradient.rs23
-rw-r--r--graphics/src/layer.rs47
-rw-r--r--graphics/src/lib.rs2
-rw-r--r--graphics/src/primitive.rs5
-rw-r--r--graphics/src/shader.rs42
-rw-r--r--graphics/src/transformation.rs8
-rw-r--r--graphics/src/triangle.rs18
-rw-r--r--graphics/src/widget/canvas.rs6
-rw-r--r--graphics/src/widget/canvas/fill.rs33
-rw-r--r--graphics/src/widget/canvas/frame.rs113
-rw-r--r--graphics/src/widget/canvas/gradient.rs21
-rw-r--r--graphics/src/widget/canvas/gradient/linear.rs73
-rw-r--r--graphics/src/widget/canvas/stroke.rs26
14 files changed, 308 insertions, 111 deletions
diff --git a/graphics/Cargo.toml b/graphics/Cargo.toml
index 49d4d9c6..ff6fcd4a 100644
--- a/graphics/Cargo.toml
+++ b/graphics/Cargo.toml
@@ -19,7 +19,7 @@ font-icons = []
opengl = []
[dependencies]
-glam = "0.10"
+glam = "0.21.3"
raw-window-handle = "0.4"
thiserror = "1.0"
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<Quad>,
/// The triangle meshes of the [`Layer`].
- pub meshes: Vec<Mesh<'a>>,
+ pub meshes: Meshes<'a>,
/// The text of the [`Layer`].
pub text: Vec<Text<'a>>,
@@ -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<f32>,
+
+ /// 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<Mesh<'a>>);
+
+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<Shader> for StrokeStyle<'a> {
+ fn into(self) -> Shader {
+ match self {
+ StrokeStyle::Solid(color) => Shader::Solid(color),
+ StrokeStyle::Gradient(gradient) => gradient.clone().into()
+ }
+ }
+}
+
+impl <'a> Into<Shader> for FillStyle<'a> {
+ fn into(self) -> Shader {
+ match self {
+ FillStyle::Solid(color) => Shader::Solid(color),
+ FillStyle::Gradient(gradient) => gradient.clone().into()
+ }
+ }
+}
+
+impl <'a> Into<Shader> 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<Transformation> for [f32; 16] {
*t.as_ref()
}
}
+
+impl Into<Mat4> 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<Vertex2D>,
-
/// 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<u32>,
}
-/// 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<lyon::math::Point>) -> Vec<Vertex2D> {
+ 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<Color> for Fill {
- fn from(color: Color) -> Fill {
+impl<'a> From<Color> 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<triangle::Vertex2D, u32>,
+ buffers: Vec<(tessellation::VertexBuffers<lyon::math::Point, u32>, Shader)>,
primitives: Vec<Primitive>,
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<Fill>) {
- let Fill { color, rule } = fill.into();
+ pub fn fill<'a>(&mut self, path: &Path, fill: impl Into<Fill<'a>>) {
+ 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>,
+ fill: impl Into<Fill<'a>>,
) {
- 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<Stroke<'a>>) {
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<Text>) {
- 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<Primitive> {
- 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<triangle::Vertex2D>
- 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<triangle::Vertex2D>
- 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<ColorStop>,
+}
+
+/// 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<Gradient> {
+ 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 {