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
13use pathfinder_geometry::line_segment::LineSegment2F;
14use pathfinder_geometry::vector::Vector2F;
15use std::mem;
16
17/// Receives Bézier path rendering commands.
18pub 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)]
33pub 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)]
40pub 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
51bitflags! {
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)]
68pub struct OutlineBuilder {
69 outline: Outline,
70 current_contour: Contour,
71}
72
73impl 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
91impl 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
141impl 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
166impl 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