skrifa/outline/glyf/hint/
projection.rs
1use super::graphics::{CoordAxis, GraphicsState};
4use raw::types::{F26Dot6, Point};
5
6impl GraphicsState<'_> {
7 pub fn update_projection_state(&mut self) {
9 const ONE: i32 = 0x4000;
11 if self.freedom_vector.x == ONE {
16 self.fdotp = self.proj_vector.x;
17 } else if self.freedom_vector.y == ONE {
18 self.fdotp = self.proj_vector.y;
19 } else {
20 let px = self.proj_vector.x;
21 let py = self.proj_vector.y;
22 let fx = self.freedom_vector.x;
23 let fy = self.freedom_vector.y;
24 self.fdotp = (px * fx + py * fy) >> 14;
25 }
26 self.proj_axis = CoordAxis::Both;
27 if self.proj_vector.x == ONE {
28 self.proj_axis = CoordAxis::X;
29 } else if self.proj_vector.y == ONE {
30 self.proj_axis = CoordAxis::Y;
31 }
32 self.dual_proj_axis = CoordAxis::Both;
33 if self.dual_proj_vector.x == ONE {
34 self.dual_proj_axis = CoordAxis::X;
35 } else if self.dual_proj_vector.y == ONE {
36 self.dual_proj_axis = CoordAxis::Y;
37 }
38 self.freedom_axis = CoordAxis::Both;
39 if self.fdotp == ONE {
40 if self.freedom_vector.x == ONE {
41 self.freedom_axis = CoordAxis::X;
42 } else if self.freedom_vector.y == ONE {
43 self.freedom_axis = CoordAxis::Y;
44 }
45 }
46 if self.fdotp.abs() < 0x400 {
49 self.fdotp = ONE;
50 }
51 }
52
53 #[inline(always)]
56 pub fn project(&self, v1: Point<F26Dot6>, v2: Point<F26Dot6>) -> F26Dot6 {
57 match self.proj_axis {
58 CoordAxis::X => v1.x - v2.x,
60 CoordAxis::Y => v1.y - v2.y,
62 CoordAxis::Both => {
63 let dx = v1.x - v2.x;
65 let dy = v1.y - v2.y;
66 F26Dot6::from_bits(dot14(
67 dx.to_bits(),
68 dy.to_bits(),
69 self.proj_vector.x,
70 self.proj_vector.y,
71 ))
72 }
73 }
74 }
75
76 #[inline(always)]
79 pub fn dual_project(&self, v1: Point<F26Dot6>, v2: Point<F26Dot6>) -> F26Dot6 {
80 match self.dual_proj_axis {
81 CoordAxis::X => v1.x - v2.x,
83 CoordAxis::Y => v1.y - v2.y,
85 CoordAxis::Both => {
86 let dx = v1.x - v2.x;
88 let dy = v1.y - v2.y;
89 F26Dot6::from_bits(dot14(
90 dx.to_bits(),
91 dy.to_bits(),
92 self.dual_proj_vector.x,
93 self.dual_proj_vector.y,
94 ))
95 }
96 }
97 }
98
99 #[inline(always)]
102 pub fn dual_project_unscaled(&self, v1: Point<i32>, v2: Point<i32>) -> i32 {
103 match self.dual_proj_axis {
104 CoordAxis::X => v1.x - v2.x,
106 CoordAxis::Y => v1.y - v2.y,
108 CoordAxis::Both => {
109 let dx = v1.x - v2.x;
111 let dy = v1.y - v2.y;
112 dot14(dx, dy, self.dual_proj_vector.x, self.dual_proj_vector.y)
113 }
114 }
115 }
116}
117
118fn dot14(ax: i32, ay: i32, bx: i32, by: i32) -> i32 {
120 let mut v1 = ax as i64 * bx as i64;
121 let v2 = ay as i64 * by as i64;
122 v1 += v2;
123 v1 += 0x2000 + (v1 >> 63);
124 (v1 >> 14) as i32
125}
126
127#[cfg(test)]
128mod tests {
129 use super::{super::math, CoordAxis, F26Dot6, GraphicsState, Point};
130
131 #[test]
132 fn project_one_axis() {
133 let mut state = GraphicsState {
134 proj_vector: math::normalize14(1, 0),
135 ..Default::default()
136 };
137 state.update_projection_state();
138 assert_eq!(state.proj_axis, CoordAxis::X);
139 assert_eq!(state.proj_vector, Point::new(0x4000, 0));
140 let cases = &[
141 (Point::new(0, 0), Point::new(0, 0), 0),
142 (Point::new(100, 100), Point::new(0, 0), 100),
143 (Point::new(42, 100), Point::new(100, 0), -58),
144 (Point::new(0, 0), Point::new(100, 100), -100),
145 ];
146 test_project_cases(&state, cases);
147 }
148
149 #[test]
150 fn project_both_axes() {
151 let mut state = GraphicsState {
152 proj_vector: math::normalize14(0x4000, 0x4000),
153 ..Default::default()
154 };
155 state.update_projection_state();
156 assert_eq!(state.proj_axis, CoordAxis::Both);
157 let cases = &[
158 (Point::new(0, 0), Point::new(0, 0), 0),
159 (Point::new(100, 100), Point::new(0, 0), 141),
160 (Point::new(42, 100), Point::new(100, 0), 30),
161 (Point::new(0, 0), Point::new(100, 100), -141),
162 ];
163 test_project_cases(&state, cases);
164 }
165
166 fn test_project_cases(state: &GraphicsState, cases: &[(Point<i32>, Point<i32>, i32)]) {
167 for (v1, v2, expected) in cases.iter().copied() {
168 let v1 = v1.map(F26Dot6::from_bits);
169 let v2 = v2.map(F26Dot6::from_bits);
170 let result = state.project(v1, v2).to_bits();
171 assert_eq!(
172 result, expected,
173 "project({v1:?}, {v2:?}) = {result} (expected {expected})"
174 );
175 }
176 }
177}