1 | // font-kit/src/outline.rs |
2 | // |
3 | // Copyright © 2020 The Pathfinder Project Developers. |
4 | // |
5 | // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or |
6 | // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license |
7 | // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your |
8 | // option. This file may not be copied, modified, or distributed |
9 | // except according to those terms. |
10 | |
11 | //! Bézier paths. |
12 | |
13 | use pathfinder_geometry::line_segment::LineSegment2F; |
14 | use pathfinder_geometry::vector::Vector2F; |
15 | use std::mem; |
16 | |
17 | /// Receives Bézier path rendering commands. |
18 | pub trait OutlineSink { |
19 | /// Moves the pen to a point. |
20 | fn move_to(&mut self, to: Vector2F); |
21 | /// Draws a line to a point. |
22 | fn line_to(&mut self, to: Vector2F); |
23 | /// Draws a quadratic Bézier curve to a point. |
24 | fn quadratic_curve_to(&mut self, ctrl: Vector2F, to: Vector2F); |
25 | /// Draws a cubic Bézier curve to a point. |
26 | fn cubic_curve_to(&mut self, ctrl: LineSegment2F, to: Vector2F); |
27 | /// Closes the path, returning to the first point in it. |
28 | fn close(&mut self); |
29 | } |
30 | |
31 | /// A glyph vector outline or path. |
32 | #[derive (Clone, PartialEq, Debug)] |
33 | pub struct Outline { |
34 | /// The individual subpaths that make up this outline. |
35 | pub contours: Vec<Contour>, |
36 | } |
37 | |
38 | /// A single curve or subpath within a glyph outline. |
39 | #[derive (Clone, PartialEq, Debug)] |
40 | pub struct Contour { |
41 | /// Positions of each point. |
42 | /// |
43 | /// This must have the same length as the `flags` field. |
44 | pub positions: Vec<Vector2F>, |
45 | /// Flags that specify what type of point the corresponding position represents. |
46 | /// |
47 | /// This must have the same length as the `positions` field. |
48 | pub flags: Vec<PointFlags>, |
49 | } |
50 | |
51 | bitflags! { |
52 | /// Flags that specify what type of point the corresponding position represents. |
53 | #[derive (Clone, Debug, PartialEq)] |
54 | pub struct PointFlags: u8 { |
55 | /// This point is the control point of a quadratic Bézier curve or the first control point |
56 | /// of a cubic Bézier curve. |
57 | /// |
58 | /// This flag is mutually exclusive with `CONTROL_POINT_1`. |
59 | const CONTROL_POINT_0 = 0x01; |
60 | /// This point is the second control point of a cubic Bézier curve. |
61 | /// |
62 | /// This flag is mutually exclusive with `CONTROL_POINT_0`. |
63 | const CONTROL_POINT_1 = 0x02; |
64 | } |
65 | } |
66 | |
67 | /// Accumulates Bézier path rendering commands into an `Outline` structure. |
68 | #[derive (Clone, Debug)] |
69 | pub struct OutlineBuilder { |
70 | outline: Outline, |
71 | current_contour: Contour, |
72 | } |
73 | |
74 | impl Default for Outline { |
75 | fn default() -> Self { |
76 | Self::new() |
77 | } |
78 | } |
79 | |
80 | impl Outline { |
81 | /// Creates a new empty outline. |
82 | #[inline ] |
83 | pub fn new() -> Outline { |
84 | Outline { contours: vec![] } |
85 | } |
86 | |
87 | /// Sends this outline to an `OutlineSink`. |
88 | pub fn copy_to<S>(&self, sink: &mut S) |
89 | where |
90 | S: OutlineSink, |
91 | { |
92 | for contour: &Contour in &self.contours { |
93 | contour.copy_to(sink); |
94 | } |
95 | } |
96 | } |
97 | |
98 | impl Default for Contour { |
99 | fn default() -> Self { |
100 | Self::new() |
101 | } |
102 | } |
103 | |
104 | impl Contour { |
105 | /// Creates a new empty contour. |
106 | #[inline ] |
107 | pub fn new() -> Contour { |
108 | Contour { |
109 | positions: vec![], |
110 | flags: vec![], |
111 | } |
112 | } |
113 | |
114 | /// Adds a new point with the given flags to the contour. |
115 | #[inline ] |
116 | pub fn push(&mut self, position: Vector2F, flags: PointFlags) { |
117 | self.positions.push(position); |
118 | self.flags.push(flags); |
119 | } |
120 | |
121 | /// Sends this contour to an `OutlineSink`. |
122 | 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 | |
154 | impl Default for OutlineBuilder { |
155 | fn default() -> Self { |
156 | Self::new() |
157 | } |
158 | } |
159 | |
160 | impl OutlineBuilder { |
161 | /// Creates a new empty `OutlineBuilder`. |
162 | #[inline ] |
163 | pub fn new() -> OutlineBuilder { |
164 | OutlineBuilder { |
165 | outline: Outline::new(), |
166 | current_contour: Contour::new(), |
167 | } |
168 | } |
169 | |
170 | /// Consumes this outline builder and returns the resulting outline. |
171 | #[inline ] |
172 | pub fn into_outline(self) -> Outline { |
173 | self.outline |
174 | } |
175 | |
176 | /// Resets the outline builder and returns the old outline. |
177 | #[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, src:Outline::new()) |
182 | } |
183 | } |
184 | |
185 | impl 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 | } |
218 | |