font_kit/
outline.rs

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    #[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)]
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    /// 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 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    /// 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
154impl Default for OutlineBuilder {
155    fn default() -> Self {
156        Self::new()
157    }
158}
159
160impl 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, 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}