1#![allow(dead_code)]
4
5use super::DrawError;
6use crate::collections::SmallVec;
7use core::ops::Range;
8use raw::{
9 tables::glyf::PointFlags,
10 types::{F26Dot6, Point},
11};
12
13#[derive(Copy, Clone, Default, Debug)]
14pub(super) struct UnscaledPoint {
15 pub x: i16,
16 pub y: i16,
17 pub flags: PointFlags,
18 pub is_contour_start: bool,
19}
20
21impl UnscaledPoint {
22 pub fn from_glyf_point(
23 point: Point<F26Dot6>,
24 flags: PointFlags,
25 is_contour_start: bool,
26 ) -> Self {
27 let point = point.map(|x| (x.to_bits() >> 6) as i16);
28 Self {
29 x: point.x,
30 y: point.y,
31 flags: flags.without_markers(),
32 is_contour_start,
33 }
34 }
35
36 pub fn is_on_curve(self) -> bool {
37 self.flags.is_on_curve()
38 }
39}
40
41pub(super) trait UnscaledOutlineSink {
42 fn try_reserve(&mut self, additional: usize) -> Result<(), DrawError>;
43 fn push(&mut self, point: UnscaledPoint) -> Result<(), DrawError>;
44 fn extend(&mut self, points: impl IntoIterator<Item = UnscaledPoint>) -> Result<(), DrawError> {
45 for point in points.into_iter() {
46 self.push(point)?;
47 }
48 Ok(())
49 }
50}
51
52pub(super) struct UnscaledOutlineBuf<const INLINE_CAP: usize>(SmallVec<UnscaledPoint, INLINE_CAP>);
54
55impl<const INLINE_CAP: usize> UnscaledOutlineBuf<INLINE_CAP> {
56 pub fn new() -> Self {
57 Self(SmallVec::new())
58 }
59
60 pub fn clear(&mut self) {
61 self.0.clear();
62 }
63
64 pub fn as_ref(&self) -> UnscaledOutlineRef {
65 UnscaledOutlineRef {
66 points: self.0.as_slice(),
67 }
68 }
69}
70
71impl<const INLINE_CAP: usize> UnscaledOutlineSink for UnscaledOutlineBuf<INLINE_CAP> {
72 fn try_reserve(&mut self, additional: usize) -> Result<(), DrawError> {
73 if !self.0.try_reserve(additional) {
74 Err(DrawError::InsufficientMemory)
75 } else {
76 Ok(())
77 }
78 }
79
80 fn push(&mut self, point: UnscaledPoint) -> Result<(), DrawError> {
81 self.0.push(point);
82 Ok(())
83 }
84}
85
86#[derive(Copy, Clone, Debug)]
87pub(super) struct UnscaledOutlineRef<'a> {
88 pub points: &'a [UnscaledPoint],
89}
90
91impl UnscaledOutlineRef<'_> {
92 pub fn find_last_contour(
100 &self,
101 mut f: impl FnMut(&UnscaledPoint) -> bool,
102 ) -> Option<(Range<usize>, usize)> {
103 if self.points.is_empty() {
104 return None;
105 }
106 let mut best_contour = 0..0;
107 let mut best_point = 0;
109 let mut cur_contour = 0..0;
110 let mut found_best_in_cur_contour = false;
111 for (point_ix, point) in self.points.iter().enumerate() {
112 if point.is_contour_start {
113 if found_best_in_cur_contour {
114 best_contour = cur_contour;
115 }
116 cur_contour = point_ix..point_ix;
117 found_best_in_cur_contour = false;
118 match self.points.get(point_ix + 1) {
120 Some(next_point) if next_point.is_contour_start => continue,
121 None => continue,
122 _ => {}
123 }
124 }
125 cur_contour.end += 1;
126 if f(point) {
127 best_point = point_ix - cur_contour.start;
128 found_best_in_cur_contour = true;
129 }
130 }
131 if found_best_in_cur_contour {
132 best_contour = cur_contour;
133 }
134 if !best_contour.is_empty() {
135 Some((best_contour, best_point))
136 } else {
137 None
138 }
139 }
140}
141
142pub(super) struct UnscaledPenAdapter<'a, T> {
145 sink: &'a mut T,
146 failed: bool,
147}
148
149impl<'a, T> UnscaledPenAdapter<'a, T> {
150 pub fn new(sink: &'a mut T) -> Self {
151 Self {
152 sink,
153 failed: false,
154 }
155 }
156
157 pub fn finish(self) -> Result<(), DrawError> {
158 if self.failed {
159 Err(DrawError::InsufficientMemory)
160 } else {
161 Ok(())
162 }
163 }
164}
165
166impl<T> UnscaledPenAdapter<'_, T>
167where
168 T: UnscaledOutlineSink,
169{
170 fn push(&mut self, x: f32, y: f32, flags: PointFlags, is_contour_start: bool) {
171 if self
172 .sink
173 .push(UnscaledPoint {
174 x: x as i16,
175 y: y as i16,
176 flags,
177 is_contour_start,
178 })
179 .is_err()
180 {
181 self.failed = true;
182 }
183 }
184}
185
186impl<T: UnscaledOutlineSink> super::OutlinePen for UnscaledPenAdapter<'_, T> {
187 fn move_to(&mut self, x: f32, y: f32) {
188 self.push(x, y, PointFlags::on_curve(), true);
189 }
190
191 fn line_to(&mut self, x: f32, y: f32) {
192 self.push(x, y, PointFlags::on_curve(), false);
193 }
194
195 fn quad_to(&mut self, cx0: f32, cy0: f32, x: f32, y: f32) {
196 self.push(cx0, cy0, PointFlags::off_curve_quad(), false);
197 self.push(x, y, PointFlags::on_curve(), false);
198 }
199
200 fn curve_to(&mut self, cx0: f32, cy0: f32, cx1: f32, cy1: f32, x: f32, y: f32) {
201 self.push(cx0, cy0, PointFlags::off_curve_cubic(), false);
202 self.push(cx1, cy1, PointFlags::off_curve_cubic(), false);
203 self.push(x, y, PointFlags::on_curve(), false);
204 }
205
206 fn close(&mut self) {}
207}
208
209#[cfg(test)]
210mod tests {
211 use super::*;
212 use crate::{prelude::LocationRef, MetadataProvider};
213 use raw::{types::GlyphId, FontRef};
214
215 #[test]
216 fn read_glyf_outline() {
217 let font = FontRef::new(font_test_data::MATERIAL_SYMBOLS_SUBSET).unwrap();
218 let glyph = font.outline_glyphs().get(GlyphId::new(5)).unwrap();
219 let mut outline = UnscaledOutlineBuf::<32>::new();
220 glyph
221 .draw_unscaled(LocationRef::default(), None, &mut outline)
222 .unwrap();
223 let outline = outline.as_ref();
224 let expected = [
225 (400, 80, 1),
227 (400, 360, 1),
228 (320, 360, 1),
229 (320, 600, 1),
230 (320, 633, 0),
231 (367, 680, 0),
232 (400, 680, 1),
233 (560, 680, 1),
234 (593, 680, 0),
235 (640, 633, 0),
236 (640, 600, 1),
237 (640, 360, 1),
238 (560, 360, 1),
239 (560, 80, 1),
240 (480, 720, 1),
242 (447, 720, 0),
243 (400, 767, 0),
244 (400, 800, 1),
245 (400, 833, 0),
246 (447, 880, 0),
247 (480, 880, 1),
248 (513, 880, 0),
249 (560, 833, 0),
250 (560, 800, 1),
251 (560, 767, 0),
252 (513, 720, 0),
253 ];
254 let points = outline
255 .points
256 .iter()
257 .map(|point| (point.x, point.y, point.flags.to_bits()))
258 .collect::<Vec<_>>();
259 assert_eq!(points, expected);
260 }
261
262 #[test]
263 #[cfg(feature = "spec_next")]
264 fn read_cubic_glyf_outline() {
265 let font = FontRef::new(font_test_data::CUBIC_GLYF).unwrap();
266 let glyph = font.outline_glyphs().get(GlyphId::new(2)).unwrap();
267 let mut outline = UnscaledOutlineBuf::<32>::new();
268 glyph
269 .draw_unscaled(LocationRef::default(), None, &mut outline)
270 .unwrap();
271 let outline = outline.as_ref();
272 let expected = [
273 (278, 710, 1),
275 (278, 470, 1),
276 (300, 500, 128),
277 (800, 500, 128),
278 (998, 470, 1),
279 (998, 710, 1),
280 ];
281 let points = outline
282 .points
283 .iter()
284 .map(|point| (point.x, point.y, point.flags.to_bits()))
285 .collect::<Vec<_>>();
286 assert_eq!(points, expected);
287 }
288
289 #[test]
290 fn read_cff_outline() {
291 let font = FontRef::new(font_test_data::CANTARELL_VF_TRIMMED).unwrap();
292 let glyph = font.outline_glyphs().get(GlyphId::new(2)).unwrap();
293 let mut outline = UnscaledOutlineBuf::<32>::new();
294 glyph
295 .draw_unscaled(LocationRef::default(), None, &mut outline)
296 .unwrap();
297 let outline = outline.as_ref();
298 let expected = [
299 (83, 0, 1),
301 (163, 0, 1),
302 (163, 482, 1),
303 (83, 482, 1),
304 (124, 595, 1),
306 (160, 595, 128),
307 (181, 616, 128),
308 (181, 652, 1),
309 (181, 688, 128),
310 (160, 709, 128),
311 (124, 709, 1),
312 (88, 709, 128),
313 (67, 688, 128),
314 (67, 652, 1),
315 (67, 616, 128),
316 (88, 595, 128),
317 (124, 595, 1),
318 ];
319 let points = outline
320 .points
321 .iter()
322 .map(|point| (point.x, point.y, point.flags.to_bits()))
323 .collect::<Vec<_>>();
324 assert_eq!(points, expected);
325 }
326
327 #[test]
328 fn find_vertical_extrema() {
329 let font = FontRef::new(font_test_data::MATERIAL_SYMBOLS_SUBSET).unwrap();
330 let glyph = font.outline_glyphs().get(GlyphId::new(5)).unwrap();
331 let mut outline = UnscaledOutlineBuf::<32>::new();
332 glyph
333 .draw_unscaled(LocationRef::default(), None, &mut outline)
334 .unwrap();
335 let outline = outline.as_ref();
336 let mut top_y = None;
338 let (top_contour, top_point_ix) = outline
339 .find_last_contour(|point| {
340 if top_y.is_none() || Some(point.y) > top_y {
341 top_y = Some(point.y);
342 true
343 } else {
344 false
345 }
346 })
347 .unwrap();
348 assert_eq!(top_contour, 14..26);
349 assert_eq!(top_point_ix, 5);
350 assert_eq!(top_y, Some(880));
351 let mut bottom_y = None;
353 let (bottom_contour, bottom_point_ix) = outline
354 .find_last_contour(|point| {
355 if bottom_y.is_none() || Some(point.y) < bottom_y {
356 bottom_y = Some(point.y);
357 true
358 } else {
359 false
360 }
361 })
362 .unwrap();
363 assert_eq!(bottom_contour, 0..14);
364 assert_eq!(bottom_point_ix, 0);
365 assert_eq!(bottom_y, Some(80));
366 }
367}