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 reserve(&mut self, additional: usize);
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 reserve(&mut self, additional: usize) {
73 self.0.reserve(additional);
74 }
75
76 fn push(&mut self, point: UnscaledPoint) -> Result<(), DrawError> {
77 self.0.push(point);
78 Ok(())
79 }
80}
81
82#[derive(Copy, Clone, Debug)]
83pub(super) struct UnscaledOutlineRef<'a> {
84 pub points: &'a [UnscaledPoint],
85}
86
87impl UnscaledOutlineRef<'_> {
88 pub fn find_last_contour(
96 &self,
97 mut f: impl FnMut(&UnscaledPoint) -> bool,
98 ) -> Option<(Range<usize>, usize)> {
99 if self.points.is_empty() {
100 return None;
101 }
102 let mut best_contour = 0..0;
103 let mut best_point = 0;
105 let mut cur_contour = 0..0;
106 let mut found_best_in_cur_contour = false;
107 for (point_ix, point) in self.points.iter().enumerate() {
108 if point.is_contour_start {
109 if found_best_in_cur_contour {
110 best_contour = cur_contour;
111 }
112 cur_contour = point_ix..point_ix;
113 found_best_in_cur_contour = false;
114 match self.points.get(point_ix + 1) {
116 Some(next_point) if next_point.is_contour_start => continue,
117 None => continue,
118 _ => {}
119 }
120 }
121 cur_contour.end += 1;
122 if f(point) {
123 best_point = point_ix - cur_contour.start;
124 found_best_in_cur_contour = true;
125 }
126 }
127 if found_best_in_cur_contour {
128 best_contour = cur_contour;
129 }
130 if !best_contour.is_empty() {
131 Some((best_contour, best_point))
132 } else {
133 None
134 }
135 }
136}
137
138#[derive(Copy, Clone)]
139enum PendingElement {
140 Line([f32; 2]),
141 Cubic([f32; 6]),
142}
143
144pub(super) struct UnscaledPenAdapter<'a, T> {
147 sink: &'a mut T,
148 failed: bool,
149 last_start: Option<(f32, f32)>,
150 pending: Option<PendingElement>,
151}
152
153impl<'a, T> UnscaledPenAdapter<'a, T> {
154 pub fn new(sink: &'a mut T) -> Self {
155 Self {
156 sink,
157 failed: false,
158 last_start: None,
159 pending: None,
160 }
161 }
162}
163
164impl<T> UnscaledPenAdapter<'_, T>
165where
166 T: UnscaledOutlineSink,
167{
168 fn push(&mut self, x: f32, y: f32, flags: PointFlags, is_contour_start: bool) {
169 if self
170 .sink
171 .push(UnscaledPoint {
172 x: x as i16,
173 y: y as i16,
174 flags,
175 is_contour_start,
176 })
177 .is_err()
178 {
179 self.failed = true;
180 }
181 }
182
183 fn flush_pending(&mut self, for_close: bool) {
184 if let Some(element) = self.pending.take() {
185 let [x, y] = match element {
186 PendingElement::Line([x, y]) => [x, y],
187 PendingElement::Cubic([x0, y0, x1, y1, x, y]) => {
188 self.push(x0, y0, PointFlags::off_curve_cubic(), false);
189 self.push(x1, y1, PointFlags::off_curve_cubic(), false);
190 [x, y]
191 }
192 };
193 if !for_close || self.last_start != Some((x, y)) {
194 self.push(x, y, PointFlags::on_curve(), false);
195 }
196 }
197 }
198
199 pub fn finish(mut self) -> Result<(), DrawError> {
200 self.flush_pending(true);
201 if self.failed {
202 Err(DrawError::InsufficientMemory)
203 } else {
204 Ok(())
205 }
206 }
207}
208
209impl<T: UnscaledOutlineSink> super::OutlinePen for UnscaledPenAdapter<'_, T> {
210 fn move_to(&mut self, x: f32, y: f32) {
211 self.push(x, y, PointFlags::on_curve(), true);
212 self.last_start = Some((x, y));
213 }
214
215 fn line_to(&mut self, x: f32, y: f32) {
216 self.flush_pending(false);
217 self.pending = Some(PendingElement::Line([x, y]));
218 }
219
220 fn quad_to(&mut self, cx0: f32, cy0: f32, x: f32, y: f32) {
221 self.flush_pending(false);
222 self.push(cx0, cy0, PointFlags::off_curve_quad(), false);
223 self.push(x, y, PointFlags::on_curve(), false);
224 }
225
226 fn curve_to(&mut self, cx0: f32, cy0: f32, cx1: f32, cy1: f32, x: f32, y: f32) {
227 self.flush_pending(false);
228 self.pending = Some(PendingElement::Cubic([cx0, cy0, cx1, cy1, x, y]));
229 }
230
231 fn close(&mut self) {
232 self.flush_pending(true);
233 }
234}
235
236#[cfg(test)]
237mod tests {
238 use super::*;
239 use crate::{outline::OutlinePen, prelude::LocationRef, MetadataProvider};
240 use raw::{types::GlyphId, FontRef};
241
242 #[test]
243 fn read_glyf_outline() {
244 let font = FontRef::new(font_test_data::MATERIAL_SYMBOLS_SUBSET).unwrap();
245 let glyph = font.outline_glyphs().get(GlyphId::new(5)).unwrap();
246 let mut outline = UnscaledOutlineBuf::<32>::new();
247 glyph
248 .draw_unscaled(LocationRef::default(), None, &mut outline)
249 .unwrap();
250 let outline = outline.as_ref();
251 let expected = [
252 (400, 80, 1),
254 (400, 360, 1),
255 (320, 360, 1),
256 (320, 600, 1),
257 (320, 633, 0),
258 (367, 680, 0),
259 (400, 680, 1),
260 (560, 680, 1),
261 (593, 680, 0),
262 (640, 633, 0),
263 (640, 600, 1),
264 (640, 360, 1),
265 (560, 360, 1),
266 (560, 80, 1),
267 (480, 720, 1),
269 (447, 720, 0),
270 (400, 767, 0),
271 (400, 800, 1),
272 (400, 833, 0),
273 (447, 880, 0),
274 (480, 880, 1),
275 (513, 880, 0),
276 (560, 833, 0),
277 (560, 800, 1),
278 (560, 767, 0),
279 (513, 720, 0),
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 #[cfg(feature = "spec_next")]
291 fn read_cubic_glyf_outline() {
292 let font = FontRef::new(font_test_data::CUBIC_GLYF).unwrap();
293 let glyph = font.outline_glyphs().get(GlyphId::new(2)).unwrap();
294 let mut outline = UnscaledOutlineBuf::<32>::new();
295 glyph
296 .draw_unscaled(LocationRef::default(), None, &mut outline)
297 .unwrap();
298 let outline = outline.as_ref();
299 let expected = [
300 (278, 710, 1),
302 (278, 470, 1),
303 (300, 500, 128),
304 (800, 500, 128),
305 (998, 470, 1),
306 (998, 710, 1),
307 ];
308 let points = outline
309 .points
310 .iter()
311 .map(|point| (point.x, point.y, point.flags.to_bits()))
312 .collect::<Vec<_>>();
313 assert_eq!(points, expected);
314 }
315
316 #[test]
317 fn read_cff_outline() {
318 let font = FontRef::new(font_test_data::CANTARELL_VF_TRIMMED).unwrap();
319 let glyph = font.outline_glyphs().get(GlyphId::new(2)).unwrap();
320 let mut outline = UnscaledOutlineBuf::<32>::new();
321 glyph
322 .draw_unscaled(LocationRef::default(), None, &mut outline)
323 .unwrap();
324 let outline = outline.as_ref();
325 let expected = [
326 (83, 0, 1),
328 (163, 0, 1),
329 (163, 482, 1),
330 (83, 482, 1),
331 (124, 595, 1),
333 (160, 595, 128),
334 (181, 616, 128),
335 (181, 652, 1),
336 (181, 688, 128),
337 (160, 709, 128),
338 (124, 709, 1),
339 (88, 709, 128),
340 (67, 688, 128),
341 (67, 652, 1),
342 (67, 616, 128),
343 (88, 595, 128),
344 ];
345 let points = outline
346 .points
347 .iter()
348 .map(|point| (point.x, point.y, point.flags.to_bits()))
349 .collect::<Vec<_>>();
350 assert_eq!(points, expected);
351 }
352
353 #[test]
354 fn find_vertical_extrema() {
355 let font = FontRef::new(font_test_data::MATERIAL_SYMBOLS_SUBSET).unwrap();
356 let glyph = font.outline_glyphs().get(GlyphId::new(5)).unwrap();
357 let mut outline = UnscaledOutlineBuf::<32>::new();
358 glyph
359 .draw_unscaled(LocationRef::default(), None, &mut outline)
360 .unwrap();
361 let outline = outline.as_ref();
362 let mut top_y = None;
364 let (top_contour, top_point_ix) = outline
365 .find_last_contour(|point| {
366 if top_y.is_none() || Some(point.y) > top_y {
367 top_y = Some(point.y);
368 true
369 } else {
370 false
371 }
372 })
373 .unwrap();
374 assert_eq!(top_contour, 14..26);
375 assert_eq!(top_point_ix, 5);
376 assert_eq!(top_y, Some(880));
377 let mut bottom_y = None;
379 let (bottom_contour, bottom_point_ix) = outline
380 .find_last_contour(|point| {
381 if bottom_y.is_none() || Some(point.y) < bottom_y {
382 bottom_y = Some(point.y);
383 true
384 } else {
385 false
386 }
387 })
388 .unwrap();
389 assert_eq!(bottom_contour, 0..14);
390 assert_eq!(bottom_point_ix, 0);
391 assert_eq!(bottom_y, Some(80));
392 }
393
394 #[test]
398 fn omit_unnecessary_trailing_oncurves() {
399 let mut outline = UnscaledOutlineBuf::<64>::new();
400 let mut pen = UnscaledPenAdapter::new(&mut outline);
401 pen.move_to(0.5, 1.5);
402 pen.line_to(1.0, 2.0);
403 pen.line_to(0.5, 1.5);
405 pen.close();
406 pen.move_to(5.0, 6.0);
407 pen.curve_to(1.0, 1.0, 2.0, 2.0, 2.0, 3.0);
408 pen.curve_to(1.0, 1.0, 2.0, 2.0, 5.0, 6.0);
410 pen.close();
411 pen.move_to(5.0, 6.0);
412 pen.curve_to(1.0, 1.0, 2.0, 2.0, 2.0, 3.0);
414 pen.close();
415 pen.finish().unwrap();
416 let on_curves = outline
418 .0
419 .iter()
420 .map(|point| point.flags.is_on_curve())
421 .collect::<Vec<_>>();
422 #[rustfmt::skip]
423 let expected_on_curves = [
424 true, true, true, false, false, true, false, false, true, false, false, true, ];
439 assert_eq!(on_curves, expected_on_curves);
440 }
441}