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
142#[derive(Copy, Clone)]
143enum PendingElement {
144 Line([f32; 2]),
145 Cubic([f32; 6]),
146}
147
148pub(super) struct UnscaledPenAdapter<'a, T> {
151 sink: &'a mut T,
152 failed: bool,
153 last_start: Option<(f32, f32)>,
154 pending: Option<PendingElement>,
155}
156
157impl<'a, T> UnscaledPenAdapter<'a, T> {
158 pub fn new(sink: &'a mut T) -> Self {
159 Self {
160 sink,
161 failed: false,
162 last_start: None,
163 pending: None,
164 }
165 }
166}
167
168impl<T> UnscaledPenAdapter<'_, T>
169where
170 T: UnscaledOutlineSink,
171{
172 fn push(&mut self, x: f32, y: f32, flags: PointFlags, is_contour_start: bool) {
173 if self
174 .sink
175 .push(UnscaledPoint {
176 x: x as i16,
177 y: y as i16,
178 flags,
179 is_contour_start,
180 })
181 .is_err()
182 {
183 self.failed = true;
184 }
185 }
186
187 fn flush_pending(&mut self, for_close: bool) {
188 if let Some(element) = self.pending.take() {
189 let [x, y] = match element {
190 PendingElement::Line([x, y]) => [x, y],
191 PendingElement::Cubic([x0, y0, x1, y1, x, y]) => {
192 self.push(x0, y0, PointFlags::off_curve_cubic(), false);
193 self.push(x1, y1, PointFlags::off_curve_cubic(), false);
194 [x, y]
195 }
196 };
197 if !for_close || self.last_start != Some((x, y)) {
198 self.push(x, y, PointFlags::on_curve(), false);
199 }
200 }
201 }
202
203 pub fn finish(mut self) -> Result<(), DrawError> {
204 self.flush_pending(true);
205 if self.failed {
206 Err(DrawError::InsufficientMemory)
207 } else {
208 Ok(())
209 }
210 }
211}
212
213impl<T: UnscaledOutlineSink> super::OutlinePen for UnscaledPenAdapter<'_, T> {
214 fn move_to(&mut self, x: f32, y: f32) {
215 self.push(x, y, PointFlags::on_curve(), true);
216 self.last_start = Some((x, y));
217 }
218
219 fn line_to(&mut self, x: f32, y: f32) {
220 self.flush_pending(false);
221 self.pending = Some(PendingElement::Line([x, y]));
222 }
223
224 fn quad_to(&mut self, cx0: f32, cy0: f32, x: f32, y: f32) {
225 self.flush_pending(false);
226 self.push(cx0, cy0, PointFlags::off_curve_quad(), false);
227 self.push(x, y, PointFlags::on_curve(), false);
228 }
229
230 fn curve_to(&mut self, cx0: f32, cy0: f32, cx1: f32, cy1: f32, x: f32, y: f32) {
231 self.flush_pending(false);
232 self.pending = Some(PendingElement::Cubic([cx0, cy0, cx1, cy1, x, y]));
233 }
234
235 fn close(&mut self) {
236 self.flush_pending(true);
237 }
238}
239
240#[cfg(test)]
241mod tests {
242 use super::*;
243 use crate::{outline::OutlinePen, prelude::LocationRef, MetadataProvider};
244 use raw::{types::GlyphId, FontRef};
245
246 #[test]
247 fn read_glyf_outline() {
248 let font = FontRef::new(font_test_data::MATERIAL_SYMBOLS_SUBSET).unwrap();
249 let glyph = font.outline_glyphs().get(GlyphId::new(5)).unwrap();
250 let mut outline = UnscaledOutlineBuf::<32>::new();
251 glyph
252 .draw_unscaled(LocationRef::default(), None, &mut outline)
253 .unwrap();
254 let outline = outline.as_ref();
255 let expected = [
256 (400, 80, 1),
258 (400, 360, 1),
259 (320, 360, 1),
260 (320, 600, 1),
261 (320, 633, 0),
262 (367, 680, 0),
263 (400, 680, 1),
264 (560, 680, 1),
265 (593, 680, 0),
266 (640, 633, 0),
267 (640, 600, 1),
268 (640, 360, 1),
269 (560, 360, 1),
270 (560, 80, 1),
271 (480, 720, 1),
273 (447, 720, 0),
274 (400, 767, 0),
275 (400, 800, 1),
276 (400, 833, 0),
277 (447, 880, 0),
278 (480, 880, 1),
279 (513, 880, 0),
280 (560, 833, 0),
281 (560, 800, 1),
282 (560, 767, 0),
283 (513, 720, 0),
284 ];
285 let points = outline
286 .points
287 .iter()
288 .map(|point| (point.x, point.y, point.flags.to_bits()))
289 .collect::<Vec<_>>();
290 assert_eq!(points, expected);
291 }
292
293 #[test]
294 #[cfg(feature = "spec_next")]
295 fn read_cubic_glyf_outline() {
296 let font = FontRef::new(font_test_data::CUBIC_GLYF).unwrap();
297 let glyph = font.outline_glyphs().get(GlyphId::new(2)).unwrap();
298 let mut outline = UnscaledOutlineBuf::<32>::new();
299 glyph
300 .draw_unscaled(LocationRef::default(), None, &mut outline)
301 .unwrap();
302 let outline = outline.as_ref();
303 let expected = [
304 (278, 710, 1),
306 (278, 470, 1),
307 (300, 500, 128),
308 (800, 500, 128),
309 (998, 470, 1),
310 (998, 710, 1),
311 ];
312 let points = outline
313 .points
314 .iter()
315 .map(|point| (point.x, point.y, point.flags.to_bits()))
316 .collect::<Vec<_>>();
317 assert_eq!(points, expected);
318 }
319
320 #[test]
321 fn read_cff_outline() {
322 let font = FontRef::new(font_test_data::CANTARELL_VF_TRIMMED).unwrap();
323 let glyph = font.outline_glyphs().get(GlyphId::new(2)).unwrap();
324 let mut outline = UnscaledOutlineBuf::<32>::new();
325 glyph
326 .draw_unscaled(LocationRef::default(), None, &mut outline)
327 .unwrap();
328 let outline = outline.as_ref();
329 let expected = [
330 (83, 0, 1),
332 (163, 0, 1),
333 (163, 482, 1),
334 (83, 482, 1),
335 (124, 595, 1),
337 (160, 595, 128),
338 (181, 616, 128),
339 (181, 652, 1),
340 (181, 688, 128),
341 (160, 709, 128),
342 (124, 709, 1),
343 (88, 709, 128),
344 (67, 688, 128),
345 (67, 652, 1),
346 (67, 616, 128),
347 (88, 595, 128),
348 ];
349 let points = outline
350 .points
351 .iter()
352 .map(|point| (point.x, point.y, point.flags.to_bits()))
353 .collect::<Vec<_>>();
354 assert_eq!(points, expected);
355 }
356
357 #[test]
358 fn find_vertical_extrema() {
359 let font = FontRef::new(font_test_data::MATERIAL_SYMBOLS_SUBSET).unwrap();
360 let glyph = font.outline_glyphs().get(GlyphId::new(5)).unwrap();
361 let mut outline = UnscaledOutlineBuf::<32>::new();
362 glyph
363 .draw_unscaled(LocationRef::default(), None, &mut outline)
364 .unwrap();
365 let outline = outline.as_ref();
366 let mut top_y = None;
368 let (top_contour, top_point_ix) = outline
369 .find_last_contour(|point| {
370 if top_y.is_none() || Some(point.y) > top_y {
371 top_y = Some(point.y);
372 true
373 } else {
374 false
375 }
376 })
377 .unwrap();
378 assert_eq!(top_contour, 14..26);
379 assert_eq!(top_point_ix, 5);
380 assert_eq!(top_y, Some(880));
381 let mut bottom_y = None;
383 let (bottom_contour, bottom_point_ix) = outline
384 .find_last_contour(|point| {
385 if bottom_y.is_none() || Some(point.y) < bottom_y {
386 bottom_y = Some(point.y);
387 true
388 } else {
389 false
390 }
391 })
392 .unwrap();
393 assert_eq!(bottom_contour, 0..14);
394 assert_eq!(bottom_point_ix, 0);
395 assert_eq!(bottom_y, Some(80));
396 }
397
398 #[test]
402 fn omit_unnecessary_trailing_oncurves() {
403 let mut outline = UnscaledOutlineBuf::<64>::new();
404 let mut pen = UnscaledPenAdapter::new(&mut outline);
405 pen.move_to(0.5, 1.5);
406 pen.line_to(1.0, 2.0);
407 pen.line_to(0.5, 1.5);
409 pen.close();
410 pen.move_to(5.0, 6.0);
411 pen.curve_to(1.0, 1.0, 2.0, 2.0, 2.0, 3.0);
412 pen.curve_to(1.0, 1.0, 2.0, 2.0, 5.0, 6.0);
414 pen.close();
415 pen.move_to(5.0, 6.0);
416 pen.curve_to(1.0, 1.0, 2.0, 2.0, 2.0, 3.0);
418 pen.close();
419 pen.finish().unwrap();
420 let on_curves = outline
422 .0
423 .iter()
424 .map(|point| point.flags.is_on_curve())
425 .collect::<Vec<_>>();
426 #[rustfmt::skip]
427 let expected_on_curves = [
428 true, true, true, false, false, true, false, false, true, false, false, true, ];
443 assert_eq!(on_curves, expected_on_curves);
444 }
445}