1use embedded_graphics_core::pixelcolor::Rgb565;
2
3#[cfg(not(feature = "std"))]
4use crate::math::F32Ext as _;
5
6#[derive(Clone, Copy, Debug, PartialEq, Eq)]
7pub enum RenderQuality {
8 Low,
9 Medium,
10 High,
11}
12
13#[derive(Clone, Copy, Debug, PartialEq, Eq)]
14pub enum AntiAliasMode {
15 None,
16 Coverage,
17 Subpixel,
18}
19
20#[derive(Clone, Copy, Debug, PartialEq, Eq)]
21pub enum StrokeCap {
22 Butt,
23 Round,
24}
25
26#[derive(Clone, Copy, Debug, PartialEq, Eq)]
27pub enum StrokeJoin {
28 Miter,
29 Round,
30}
31
32#[derive(Clone, Copy, Debug, PartialEq, Eq)]
33pub struct StrokeStyle {
34 pub color: Rgb565,
35 pub width: u8,
36 pub antialias: bool,
37 pub antialias_mode: AntiAliasMode,
38 pub cap: StrokeCap,
39 pub join: StrokeJoin,
40}
41
42impl StrokeStyle {
43 pub const fn new(color: Rgb565) -> Self {
44 Self {
45 color,
46 width: 1,
47 antialias: false,
48 antialias_mode: AntiAliasMode::None,
49 cap: StrokeCap::Butt,
50 join: StrokeJoin::Miter,
51 }
52 }
53
54 pub const fn with_width(mut self, width: u8) -> Self {
55 self.width = if width == 0 { 1 } else { width };
56 self
57 }
58
59 pub const fn with_antialias(mut self, antialias: bool) -> Self {
60 self.antialias = antialias;
61 if antialias {
62 if let AntiAliasMode::None = self.antialias_mode {
63 self.antialias_mode = AntiAliasMode::Coverage;
64 }
65 }
66 if !antialias {
67 self.antialias_mode = AntiAliasMode::None;
68 }
69 self
70 }
71
72 pub const fn with_antialias_mode(mut self, mode: AntiAliasMode) -> Self {
73 self.antialias_mode = mode;
74 self.antialias = !matches!(mode, AntiAliasMode::None);
75 self
76 }
77
78 pub const fn with_cap(mut self, cap: StrokeCap) -> Self {
79 self.cap = cap;
80 self
81 }
82
83 pub const fn with_join(mut self, join: StrokeJoin) -> Self {
84 self.join = join;
85 self
86 }
87}
88
89#[derive(Clone, Copy, Debug, PartialEq)]
90pub struct Transform2D {
91 pub m11: f32,
92 pub m12: f32,
93 pub m21: f32,
94 pub m22: f32,
95 pub tx: f32,
96 pub ty: f32,
97}
98
99impl Transform2D {
100 pub const IDENTITY: Self = Self {
101 m11: 1.0,
102 m12: 0.0,
103 m21: 0.0,
104 m22: 1.0,
105 tx: 0.0,
106 ty: 0.0,
107 };
108
109 pub const fn translation(x: f32, y: f32) -> Self {
110 Self {
111 tx: x,
112 ty: y,
113 ..Self::IDENTITY
114 }
115 }
116
117 pub const fn scale(x: f32, y: f32) -> Self {
118 Self {
119 m11: x,
120 m22: y,
121 ..Self::IDENTITY
122 }
123 }
124
125 pub fn rotation(deg: f32) -> Self {
126 let r = deg.to_radians();
127 Self {
128 m11: r.cos(),
129 m12: -r.sin(),
130 m21: r.sin(),
131 m22: r.cos(),
132 ..Self::IDENTITY
133 }
134 }
135
136 pub fn skew(x_deg: f32, y_deg: f32) -> Self {
137 Self {
138 m12: x_deg.to_radians().tan(),
139 m21: y_deg.to_radians().tan(),
140 ..Self::IDENTITY
141 }
142 }
143
144 pub fn then(self, rhs: Self) -> Self {
145 Self {
146 m11: self.m11 * rhs.m11 + self.m12 * rhs.m21,
147 m12: self.m11 * rhs.m12 + self.m12 * rhs.m22,
148 m21: self.m21 * rhs.m11 + self.m22 * rhs.m21,
149 m22: self.m21 * rhs.m12 + self.m22 * rhs.m22,
150 tx: self.m11 * rhs.tx + self.m12 * rhs.ty + self.tx,
151 ty: self.m21 * rhs.tx + self.m22 * rhs.ty + self.ty,
152 }
153 }
154
155 #[inline(always)]
156 pub fn is_identity(self) -> bool {
157 self.m11 == 1.0
158 && self.m12 == 0.0
159 && self.m21 == 0.0
160 && self.m22 == 1.0
161 && self.tx == 0.0
162 && self.ty == 0.0
163 }
164
165 #[inline(always)]
166 pub fn apply(self, x: i32, y: i32) -> (i32, i32) {
167 if self.is_identity() {
168 (x, y)
169 } else {
170 let xf = x as f32;
171 let yf = y as f32;
172 (
173 (self.m11 * xf + self.m12 * yf + self.tx).round() as i32,
174 (self.m21 * xf + self.m22 * yf + self.ty).round() as i32,
175 )
176 }
177 }
178
179 #[inline(always)]
180 pub fn apply_f32(self, x: f32, y: f32) -> (f32, f32) {
181 if self.is_identity() {
182 (x, y)
183 } else {
184 (
185 self.m11 * x + self.m12 * y + self.tx,
186 self.m21 * x + self.m22 * y + self.ty,
187 )
188 }
189 }
190
191 pub fn inverse(self) -> Option<Self> {
192 let det = self.m11 * self.m22 - self.m12 * self.m21;
193 if det.abs() < 1e-7 {
194 return None;
195 }
196 let inv_det = 1.0 / det;
197 let m11 = self.m22 * inv_det;
198 let m12 = -self.m12 * inv_det;
199 let m21 = -self.m21 * inv_det;
200 let m22 = self.m11 * inv_det;
201 let tx = (self.m12 * self.ty - self.m22 * self.tx) * inv_det;
202 let ty = (self.m21 * self.tx - self.m11 * self.ty) * inv_det;
203 Some(Self {
204 m11,
205 m12,
206 m21,
207 m22,
208 tx,
209 ty,
210 })
211 }
212}
213
214#[derive(Clone, Copy, Debug, PartialEq)]
216pub enum PathVerb {
217 MoveTo(embedded_graphics_core::geometry::Point),
218 LineTo(embedded_graphics_core::geometry::Point),
219 QuadTo {
220 control: embedded_graphics_core::geometry::Point,
221 to: embedded_graphics_core::geometry::Point,
222 },
223 CubicTo {
224 control1: embedded_graphics_core::geometry::Point,
225 control2: embedded_graphics_core::geometry::Point,
226 to: embedded_graphics_core::geometry::Point,
227 },
228 Close,
229}
230
231#[derive(Clone, Copy, Debug, PartialEq)]
233pub struct VectorPath<const N: usize> {
234 pub verbs: [PathVerb; N],
235 pub len: usize,
236}
237
238impl<const N: usize> Default for VectorPath<N> {
239 fn default() -> Self {
240 Self::new()
241 }
242}
243
244impl<const N: usize> VectorPath<N> {
245 pub const fn new() -> Self {
246 Self {
247 verbs: [PathVerb::Close; N],
248 len: 0,
249 }
250 }
251
252 pub fn is_empty(&self) -> bool {
253 self.len == 0
254 }
255
256 pub fn len(&self) -> usize {
257 self.len
258 }
259
260 pub fn push(&mut self, verb: PathVerb) -> bool {
261 if self.len < N {
262 self.verbs[self.len] = verb;
263 self.len += 1;
264 true
265 } else {
266 false
267 }
268 }
269
270 pub fn move_to(&mut self, pt: embedded_graphics_core::geometry::Point) -> &mut Self {
271 self.push(PathVerb::MoveTo(pt));
272 self
273 }
274
275 pub fn line_to(&mut self, pt: embedded_graphics_core::geometry::Point) -> &mut Self {
276 self.push(PathVerb::LineTo(pt));
277 self
278 }
279
280 pub fn quad_to(
281 &mut self,
282 control: embedded_graphics_core::geometry::Point,
283 to: embedded_graphics_core::geometry::Point,
284 ) -> &mut Self {
285 self.push(PathVerb::QuadTo { control, to });
286 self
287 }
288
289 pub fn cubic_to(
290 &mut self,
291 control1: embedded_graphics_core::geometry::Point,
292 control2: embedded_graphics_core::geometry::Point,
293 to: embedded_graphics_core::geometry::Point,
294 ) -> &mut Self {
295 self.push(PathVerb::CubicTo {
296 control1,
297 control2,
298 to,
299 });
300 self
301 }
302
303 pub fn close(&mut self) -> &mut Self {
304 self.push(PathVerb::Close);
305 self
306 }
307
308 pub fn verbs(&self) -> &[PathVerb] {
309 &self.verbs[..self.len]
310 }
311}