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 | pub struct PointFlags: u8 { |
54 | /// This point is the control point of a quadratic Bézier curve or the first control point |
55 | /// of a cubic Bézier curve. |
56 | /// |
57 | /// This flag is mutually exclusive with `CONTROL_POINT_1`. |
58 | const CONTROL_POINT_0 = 0x01; |
59 | /// This point is the second control point of a cubic Bézier curve. |
60 | /// |
61 | /// This flag is mutually exclusive with `CONTROL_POINT_0`. |
62 | const CONTROL_POINT_1 = 0x02; |
63 | } |
64 | } |
65 | |
66 | /// Accumulates Bézier path rendering commands into an `Outline` structure. |
67 | #[derive (Clone, Debug)] |
68 | pub struct OutlineBuilder { |
69 | outline: Outline, |
70 | current_contour: Contour, |
71 | } |
72 | |
73 | impl Outline { |
74 | /// Creates a new empty outline. |
75 | #[inline ] |
76 | pub fn new() -> Outline { |
77 | Outline { contours: vec![] } |
78 | } |
79 | |
80 | /// Sends this outline to an `OutlineSink`. |
81 | pub fn copy_to<S>(&self, sink: &mut S) |
82 | where |
83 | S: OutlineSink, |
84 | { |
85 | for contour: &Contour in &self.contours { |
86 | contour.copy_to(sink); |
87 | } |
88 | } |
89 | } |
90 | |
91 | impl Contour { |
92 | /// Creates a new empty contour. |
93 | #[inline ] |
94 | pub fn new() -> Contour { |
95 | Contour { |
96 | positions: vec![], |
97 | flags: vec![], |
98 | } |
99 | } |
100 | |
101 | /// Adds a new point with the given flags to the contour. |
102 | #[inline ] |
103 | pub fn push(&mut self, position: Vector2F, flags: PointFlags) { |
104 | self.positions.push(position); |
105 | self.flags.push(flags); |
106 | } |
107 | |
108 | /// Sends this contour to an `OutlineSink`. |
109 | pub fn copy_to<S>(&self, sink: &mut S) |
110 | where |
111 | S: OutlineSink, |
112 | { |
113 | debug_assert_eq!(self.positions.len(), self.flags.len()); |
114 | if self.positions.is_empty() { |
115 | return; |
116 | } |
117 | sink.move_to(self.positions[0]); |
118 | |
119 | let mut iter = self.positions[1..].iter().zip(self.flags[1..].iter()); |
120 | while let Some((&position_0, &flags_0)) = iter.next() { |
121 | if flags_0 == PointFlags::empty() { |
122 | sink.line_to(position_0); |
123 | continue; |
124 | } |
125 | |
126 | let (&position_1, &flags_1) = iter.next().expect("Invalid outline!" ); |
127 | if flags_1 == PointFlags::empty() { |
128 | sink.quadratic_curve_to(position_0, position_1); |
129 | continue; |
130 | } |
131 | |
132 | let (&position_2, &flags_2) = iter.next().expect("Invalid outline!" ); |
133 | debug_assert_eq!(flags_2, PointFlags::empty()); |
134 | sink.cubic_curve_to(LineSegment2F::new(position_0, position_1), position_2); |
135 | } |
136 | |
137 | sink.close(); |
138 | } |
139 | } |
140 | |
141 | impl OutlineBuilder { |
142 | /// Creates a new empty `OutlineBuilder`. |
143 | #[inline ] |
144 | pub fn new() -> OutlineBuilder { |
145 | OutlineBuilder { |
146 | outline: Outline::new(), |
147 | current_contour: Contour::new(), |
148 | } |
149 | } |
150 | |
151 | /// Consumes this outline builder and returns the resulting outline. |
152 | #[inline ] |
153 | pub fn into_outline(self) -> Outline { |
154 | self.outline |
155 | } |
156 | |
157 | /// Resets the outline builder and returns the old outline. |
158 | #[inline ] |
159 | pub fn take_outline(&mut self) -> Outline { |
160 | assert!(self.current_contour.positions.is_empty()); |
161 | self.current_contour = Contour::new(); |
162 | mem::replace(&mut self.outline, src:Outline::new()) |
163 | } |
164 | } |
165 | |
166 | impl OutlineSink for OutlineBuilder { |
167 | #[inline ] |
168 | fn move_to(&mut self, to: Vector2F) { |
169 | self.current_contour.push(to, PointFlags::empty()); |
170 | } |
171 | |
172 | #[inline ] |
173 | fn line_to(&mut self, to: Vector2F) { |
174 | self.current_contour.push(to, PointFlags::empty()); |
175 | } |
176 | |
177 | #[inline ] |
178 | fn quadratic_curve_to(&mut self, ctrl: Vector2F, to: Vector2F) { |
179 | self.current_contour.push(ctrl, PointFlags::CONTROL_POINT_0); |
180 | self.current_contour.push(to, PointFlags::empty()); |
181 | } |
182 | |
183 | #[inline ] |
184 | fn cubic_curve_to(&mut self, ctrl: LineSegment2F, to: Vector2F) { |
185 | self.current_contour |
186 | .push(ctrl.from(), PointFlags::CONTROL_POINT_0); |
187 | self.current_contour |
188 | .push(ctrl.to(), PointFlags::CONTROL_POINT_1); |
189 | self.current_contour.push(to, PointFlags::empty()); |
190 | } |
191 | |
192 | #[inline ] |
193 | fn close(&mut self) { |
194 | self.outline |
195 | .contours |
196 | .push(mem::replace(&mut self.current_contour, Contour::new())); |
197 | } |
198 | } |
199 | |