1use crate::effects::{BlendMode, ColorFilter};
2use crate::geometry::CornerRadii;
3use crate::gradients::{Brush, Shader};
4use incular_core::finite_non_negative;
5use incular_core::{Color, Lerp, Offset};
6
7#[derive(Clone, Copy, Debug, PartialEq)]
8pub struct Border {
9 pub width: f32,
10 pub color: Color,
11}
12impl Border {
13 #[must_use]
14 pub fn new(width: f32, color: Color) -> Self {
15 Self {
16 width: width.max(0.),
17 color,
18 }
19 }
20}
21#[derive(Clone, Debug, Default, PartialEq)]
22pub struct Decoration {
23 pub background: Option<Brush>,
24 pub border: Option<Border>,
25 pub border_radius: CornerRadii,
26}
27#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
28pub enum FillRule {
29 NonZero,
30 EvenOdd,
31}
32#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
33pub enum LineCap {
34 Butt,
35 Round,
36 Square,
37}
38#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
39pub enum LineJoin {
40 Miter,
41 Round,
42 Bevel,
43}
44#[derive(Clone, Copy, Debug, PartialEq)]
45pub struct Stroke {
46 pub width: f32,
47 pub cap: LineCap,
48 pub join: LineJoin,
49 pub miter_limit: f32,
50}
51impl Default for Stroke {
52 fn default() -> Self {
53 Self {
54 width: 1.,
55 cap: LineCap::Butt,
56 join: LineJoin::Miter,
57 miter_limit: 4.,
58 }
59 }
60}
61
62#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
64pub enum PaintStyle {
65 #[default]
66 Fill,
67 Stroke,
68}
69
70#[derive(Clone, Debug, PartialEq)]
77pub struct Paint {
78 brush: Brush,
79 style: PaintStyle,
80 stroke: Stroke,
81 blend_mode: BlendMode,
82 color_filter: Option<ColorFilter>,
83}
84
85impl Default for Paint {
86 fn default() -> Self {
87 Self {
88 brush: Brush::Solid(Color::BLACK),
89 style: PaintStyle::Fill,
90 stroke: Stroke::default(),
91 blend_mode: BlendMode::SrcOver,
92 color_filter: None,
93 }
94 }
95}
96
97impl Paint {
98 #[must_use]
99 pub fn new() -> Self {
100 Self::default()
101 }
102
103 #[must_use]
104 pub fn brush(mut self, brush: impl Into<Brush>) -> Self {
105 self.brush = brush.into();
106 self
107 }
108
109 #[must_use]
110 pub fn shader(self, shader: impl Into<Shader>) -> Self {
111 self.brush(shader)
112 }
113
114 #[must_use]
115 pub fn color(self, color: Color) -> Self {
116 self.brush(color)
117 }
118
119 #[must_use]
120 pub fn style(mut self, style: PaintStyle) -> Self {
121 self.style = style;
122 self
123 }
124
125 #[must_use]
126 pub fn fill(self) -> Self {
127 self.style(PaintStyle::Fill)
128 }
129
130 #[must_use]
131 pub fn stroke(self) -> Self {
132 self.style(PaintStyle::Stroke)
133 }
134
135 #[must_use]
136 pub fn stroke_settings(mut self, stroke: Stroke) -> Self {
137 self.stroke = Stroke {
138 width: finite_non_negative(stroke.width),
139 cap: stroke.cap,
140 join: stroke.join,
141 miter_limit: finite_non_negative(stroke.miter_limit),
142 };
143 self
144 }
145
146 #[must_use]
147 pub fn stroke_width(mut self, width: f32) -> Self {
148 self.stroke.width = finite_non_negative(width);
149 self
150 }
151
152 #[must_use]
153 pub fn blend_mode(mut self, blend_mode: BlendMode) -> Self {
154 self.blend_mode = blend_mode;
155 self
156 }
157
158 #[must_use]
159 pub fn color_filter(mut self, color_filter: Option<ColorFilter>) -> Self {
160 self.color_filter = color_filter;
161 self
162 }
163
164 #[must_use]
165 pub fn brush_value(&self) -> &Brush {
166 &self.brush
167 }
168
169 #[must_use]
170 pub const fn style_value(&self) -> PaintStyle {
171 self.style
172 }
173
174 #[must_use]
175 pub const fn stroke_value(&self) -> Stroke {
176 self.stroke
177 }
178
179 #[must_use]
180 pub const fn blend_mode_value(&self) -> BlendMode {
181 self.blend_mode
182 }
183
184 #[must_use]
185 pub fn color_filter_value(&self) -> Option<ColorFilter> {
186 self.color_filter
187 }
188}
189
190#[derive(Clone, Copy, Debug, Default, PartialEq)]
194pub struct Shadow {
195 pub color: Color,
196 pub offset: Offset,
197 pub blur_radius: f32,
198}
199
200impl Shadow {
201 #[must_use]
202 pub const fn new(color: Color, offset: Offset, blur_radius: f32) -> Self {
203 Self {
204 color,
205 offset,
206 blur_radius: if blur_radius.is_finite() && blur_radius > 0. {
207 blur_radius
208 } else {
209 0.
210 },
211 }
212 }
213}
214
215impl Lerp for Shadow {
216 fn lerp(&self, other: &Self, t: f32) -> Self {
217 Self::new(
218 self.color.lerp(&other.color, t),
219 self.offset.lerp(&other.offset, t),
220 self.blur_radius.lerp(&other.blur_radius, t),
221 )
222 }
223}