1use pathfinder_geometry::line_segment::LineSegment2F;
14use pathfinder_geometry::vector::Vector2F;
15use std::mem;
16
17pub trait OutlineSink {
19 fn move_to(&mut self, to: Vector2F);
21 fn line_to(&mut self, to: Vector2F);
23 fn quadratic_curve_to(&mut self, ctrl: Vector2F, to: Vector2F);
25 fn cubic_curve_to(&mut self, ctrl: LineSegment2F, to: Vector2F);
27 fn close(&mut self);
29}
30
31#[derive(Clone, PartialEq, Debug)]
33pub struct Outline {
34 pub contours: Vec<Contour>,
36}
37
38#[derive(Clone, PartialEq, Debug)]
40pub struct Contour {
41 pub positions: Vec<Vector2F>,
45 pub flags: Vec<PointFlags>,
49}
50
51bitflags! {
52 #[derive(Clone, Debug, PartialEq)]
54 pub struct PointFlags: u8 {
55 const CONTROL_POINT_0 = 0x01;
60 const CONTROL_POINT_1 = 0x02;
64 }
65}
66
67#[derive(Clone, Debug)]
69pub struct OutlineBuilder {
70 outline: Outline,
71 current_contour: Contour,
72}
73
74impl Default for Outline {
75 fn default() -> Self {
76 Self::new()
77 }
78}
79
80impl Outline {
81 #[inline]
83 pub fn new() -> Outline {
84 Outline { contours: vec![] }
85 }
86
87 pub fn copy_to<S>(&self, sink: &mut S)
89 where
90 S: OutlineSink,
91 {
92 for contour in &self.contours {
93 contour.copy_to(sink);
94 }
95 }
96}
97
98impl Default for Contour {
99 fn default() -> Self {
100 Self::new()
101 }
102}
103
104impl Contour {
105 #[inline]
107 pub fn new() -> Contour {
108 Contour {
109 positions: vec![],
110 flags: vec![],
111 }
112 }
113
114 #[inline]
116 pub fn push(&mut self, position: Vector2F, flags: PointFlags) {
117 self.positions.push(position);
118 self.flags.push(flags);
119 }
120
121 pub fn copy_to<S>(&self, sink: &mut S)
123 where
124 S: OutlineSink,
125 {
126 debug_assert_eq!(self.positions.len(), self.flags.len());
127 if self.positions.is_empty() {
128 return;
129 }
130 sink.move_to(self.positions[0]);
131
132 let mut iter = self.positions[1..].iter().zip(self.flags[1..].iter());
133 while let Some((&position_0, flags_0)) = iter.next() {
134 if flags_0.is_empty() {
135 sink.line_to(position_0);
136 continue;
137 }
138
139 let (&position_1, flags_1) = iter.next().expect("Invalid outline!");
140 if flags_1.is_empty() {
141 sink.quadratic_curve_to(position_0, position_1);
142 continue;
143 }
144
145 let (&position_2, flags_2) = iter.next().expect("Invalid outline!");
146 debug_assert!(flags_2.is_empty());
147 sink.cubic_curve_to(LineSegment2F::new(position_0, position_1), position_2);
148 }
149
150 sink.close();
151 }
152}
153
154impl Default for OutlineBuilder {
155 fn default() -> Self {
156 Self::new()
157 }
158}
159
160impl OutlineBuilder {
161 #[inline]
163 pub fn new() -> OutlineBuilder {
164 OutlineBuilder {
165 outline: Outline::new(),
166 current_contour: Contour::new(),
167 }
168 }
169
170 #[inline]
172 pub fn into_outline(self) -> Outline {
173 self.outline
174 }
175
176 #[inline]
178 pub fn take_outline(&mut self) -> Outline {
179 assert!(self.current_contour.positions.is_empty());
180 self.current_contour = Contour::new();
181 mem::replace(&mut self.outline, Outline::new())
182 }
183}
184
185impl OutlineSink for OutlineBuilder {
186 #[inline]
187 fn move_to(&mut self, to: Vector2F) {
188 self.current_contour.push(to, PointFlags::empty());
189 }
190
191 #[inline]
192 fn line_to(&mut self, to: Vector2F) {
193 self.current_contour.push(to, PointFlags::empty());
194 }
195
196 #[inline]
197 fn quadratic_curve_to(&mut self, ctrl: Vector2F, to: Vector2F) {
198 self.current_contour.push(ctrl, PointFlags::CONTROL_POINT_0);
199 self.current_contour.push(to, PointFlags::empty());
200 }
201
202 #[inline]
203 fn cubic_curve_to(&mut self, ctrl: LineSegment2F, to: Vector2F) {
204 self.current_contour
205 .push(ctrl.from(), PointFlags::CONTROL_POINT_0);
206 self.current_contour
207 .push(ctrl.to(), PointFlags::CONTROL_POINT_1);
208 self.current_contour.push(to, PointFlags::empty());
209 }
210
211 #[inline]
212 fn close(&mut self) {
213 self.outline
214 .contours
215 .push(mem::replace(&mut self.current_contour, Contour::new()));
216 }
217}