1use read_fonts::{
4 tables::glyf::{PointFlags, PointMarker},
5 types::{F26Dot6, Point},
6};
7
8use super::{
9 error::HintErrorKind,
10 graphics::{CoordAxis, GraphicsState},
11 math,
12};
13
14use HintErrorKind::{InvalidPointIndex, InvalidPointRange};
15
16#[derive(Copy, Clone, PartialEq, Default, Debug)]
20#[repr(u8)]
21pub enum ZonePointer {
22 Twilight = 0,
23 #[default]
24 Glyph = 1,
25}
26
27impl ZonePointer {
28 pub fn is_twilight(self) -> bool {
29 self == Self::Twilight
30 }
31}
32
33impl TryFrom<i32> for ZonePointer {
34 type Error = HintErrorKind;
35
36 fn try_from(value: i32) -> Result<Self, Self::Error> {
37 match value {
38 0 => Ok(Self::Twilight),
39 1 => Ok(Self::Glyph),
40 _ => Err(HintErrorKind::InvalidZoneIndex(value)),
41 }
42 }
43}
44
45#[derive(Default, Debug)]
49pub struct Zone<'a> {
50 pub unscaled: &'a [Point<i32>],
52 pub original: &'a mut [Point<F26Dot6>],
54 pub points: &'a mut [Point<F26Dot6>],
56 pub flags: &'a mut [PointFlags],
57 pub contours: &'a [u16],
58}
59
60impl<'a> Zone<'a> {
61 pub fn new(
63 unscaled: &'a [Point<i32>],
64 original: &'a mut [Point<F26Dot6>],
65 points: &'a mut [Point<F26Dot6>],
66 flags: &'a mut [PointFlags],
67 contours: &'a [u16],
68 ) -> Self {
69 Self {
70 unscaled,
71 original,
72 points,
73 flags,
74 contours,
75 }
76 }
77
78 pub fn point(&self, index: usize) -> Result<Point<F26Dot6>, HintErrorKind> {
79 self.points
80 .get(index)
81 .copied()
82 .ok_or(InvalidPointIndex(index))
83 }
84
85 pub fn point_mut(&mut self, index: usize) -> Result<&mut Point<F26Dot6>, HintErrorKind> {
86 self.points.get_mut(index).ok_or(InvalidPointIndex(index))
87 }
88
89 pub fn original(&self, index: usize) -> Result<Point<F26Dot6>, HintErrorKind> {
90 self.original
91 .get(index)
92 .copied()
93 .ok_or(InvalidPointIndex(index))
94 }
95
96 pub fn original_mut(&mut self, index: usize) -> Result<&mut Point<F26Dot6>, HintErrorKind> {
97 self.original.get_mut(index).ok_or(InvalidPointIndex(index))
98 }
99
100 pub fn unscaled(&self, index: usize) -> Point<i32> {
101 self.unscaled.get(index).copied().unwrap_or_default()
105 }
106
107 pub fn contour(&self, index: usize) -> Result<u16, HintErrorKind> {
108 self.contours
109 .get(index)
110 .copied()
111 .ok_or(HintErrorKind::InvalidContourIndex(index))
112 }
113
114 pub fn touch(&mut self, index: usize, axis: CoordAxis) -> Result<(), HintErrorKind> {
115 let flag = self.flags.get_mut(index).ok_or(InvalidPointIndex(index))?;
116 flag.set_marker(axis.touched_marker());
117 Ok(())
118 }
119
120 pub fn untouch(&mut self, index: usize, axis: CoordAxis) -> Result<(), HintErrorKind> {
121 let flag = self.flags.get_mut(index).ok_or(InvalidPointIndex(index))?;
122 flag.clear_marker(axis.touched_marker());
123 Ok(())
124 }
125
126 pub fn is_touched(&self, index: usize, axis: CoordAxis) -> Result<bool, HintErrorKind> {
127 let flag = self.flags.get(index).ok_or(InvalidPointIndex(index))?;
128 Ok(flag.has_marker(axis.touched_marker()))
129 }
130
131 pub fn flip_on_curve(&mut self, index: usize) -> Result<(), HintErrorKind> {
132 let flag = self.flags.get_mut(index).ok_or(InvalidPointIndex(index))?;
133 flag.flip_on_curve();
134 Ok(())
135 }
136
137 pub fn set_on_curve(
138 &mut self,
139 start: usize,
140 end: usize,
141 on: bool,
142 ) -> Result<(), HintErrorKind> {
143 let flags = self
144 .flags
145 .get_mut(start..end)
146 .ok_or(InvalidPointRange(start, end))?;
147 if on {
148 for flag in flags {
149 flag.set_on_curve();
150 }
151 } else {
152 for flag in flags {
153 flag.clear_on_curve();
154 }
155 }
156 Ok(())
157 }
158
159 pub fn iup(&mut self, axis: CoordAxis) -> Result<(), HintErrorKind> {
163 let mut point = 0;
164 for i in 0..self.contours.len() {
165 let mut end_point = self.contour(i)? as usize;
166 let first_point = point;
167 if end_point >= self.points.len() {
168 end_point = self.points.len() - 1;
169 }
170 while point <= end_point && !self.is_touched(point, axis)? {
171 point += 1;
172 }
173 if point <= end_point {
174 let first_touched = point;
175 let mut cur_touched = point;
176 point += 1;
177 while point <= end_point {
178 if self.is_touched(point, axis)? {
179 self.iup_interpolate(axis, cur_touched + 1, point - 1, cur_touched, point)?;
180 cur_touched = point;
181 }
182 point += 1;
183 }
184 if cur_touched == first_touched {
185 self.iup_shift(axis, first_point, end_point, cur_touched)?;
186 } else {
187 self.iup_interpolate(
188 axis,
189 cur_touched + 1,
190 end_point,
191 cur_touched,
192 first_touched,
193 )?;
194 if first_touched > 0 {
195 self.iup_interpolate(
196 axis,
197 first_point,
198 first_touched - 1,
199 cur_touched,
200 first_touched,
201 )?;
202 }
203 }
204 }
205 }
206 Ok(())
207 }
208
209 fn iup_shift(
214 &mut self,
215 axis: CoordAxis,
216 p1: usize,
217 p2: usize,
218 p: usize,
219 ) -> Result<(), HintErrorKind> {
220 if p1 > p2 || p1 > p || p > p2 {
221 return Ok(());
222 }
223 macro_rules! shift_coord {
224 ($coord:ident) => {
225 let delta = self.point(p)?.$coord - self.original(p)?.$coord;
226 if delta != F26Dot6::ZERO {
227 let (first, second) = self
228 .points
229 .get_mut(p1..=p2)
230 .ok_or(InvalidPointRange(p1, p2 + 1))?
231 .split_at_mut(p - p1);
232 for point in first
233 .iter_mut()
234 .chain(second.get_mut(1..).ok_or(InvalidPointIndex(p - p1))?)
235 {
236 point.$coord += delta;
237 }
238 }
239 };
240 }
241 if axis == CoordAxis::X {
242 shift_coord!(x);
243 } else {
244 shift_coord!(y);
245 }
246 Ok(())
247 }
248
249 fn iup_interpolate(
254 &mut self,
255 axis: CoordAxis,
256 p1: usize,
257 p2: usize,
258 mut ref1: usize,
259 mut ref2: usize,
260 ) -> Result<(), HintErrorKind> {
261 if p1 > p2 {
262 return Ok(());
263 }
264 let max_points = self.points.len();
265 if ref1 >= max_points || ref2 >= max_points {
266 return Ok(());
267 }
268 macro_rules! interpolate_coord {
269 ($coord:ident) => {
270 let mut orus1 = self.unscaled(ref1).$coord;
271 let mut orus2 = self.unscaled(ref2).$coord;
272 if orus1 > orus2 {
273 use core::mem::swap;
274 swap(&mut orus1, &mut orus2);
275 swap(&mut ref1, &mut ref2);
276 }
277 let org1 = self.original(ref1)?.$coord;
278 let org2 = self.original(ref2)?.$coord;
279 let cur1 = self.point(ref1)?.$coord;
280 let cur2 = self.point(ref2)?.$coord;
281 let delta1 = cur1 - org1;
282 let delta2 = cur2 - org2;
283 let iter = self
284 .original
285 .get(p1..=p2)
286 .ok_or(InvalidPointRange(p1, p2 + 1))?
287 .iter()
288 .zip(
289 self.unscaled
290 .get(p1..=p2)
291 .ok_or(InvalidPointRange(p1, p2 + 1))?,
292 )
293 .zip(
294 self.points
295 .get_mut(p1..=p2)
296 .ok_or(InvalidPointRange(p1, p2 + 1))?,
297 );
298 if cur1 == cur2 || orus1 == orus2 {
299 for ((orig, _unscaled), point) in iter {
300 let a = orig.$coord;
301 point.$coord = if a <= org1 {
302 a + delta1
303 } else if a >= org2 {
304 a + delta2
305 } else {
306 cur1
307 };
308 }
309 } else {
310 let scale = math::div((cur2 - cur1).to_bits(), orus2 - orus1);
311 for ((orig, unscaled), point) in iter {
312 let a = orig.$coord;
313 point.$coord = if a <= org1 {
314 a + delta1
315 } else if a >= org2 {
316 a + delta2
317 } else {
318 cur1 + F26Dot6::from_bits(math::mul(unscaled.$coord - orus1, scale))
319 };
320 }
321 }
322 };
323 }
324 if axis == CoordAxis::X {
325 interpolate_coord!(x);
326 } else {
327 interpolate_coord!(y);
328 }
329 Ok(())
330 }
331}
332
333impl<'a> GraphicsState<'a> {
334 pub fn in_bounds<const N: usize>(&self, pairs: [(ZonePointer, usize); N]) -> bool {
337 for (zp, index) in pairs {
338 if index > self.zone(zp).points.len() {
339 return false;
340 }
341 }
342 true
343 }
344
345 #[inline(always)]
346 pub fn zone(&self, pointer: ZonePointer) -> &Zone<'a> {
347 &self.zones[pointer as usize]
348 }
349
350 #[inline(always)]
351 pub fn zone_mut(&mut self, pointer: ZonePointer) -> &mut Zone<'a> {
352 &mut self.zones[pointer as usize]
353 }
354
355 #[inline(always)]
356 pub fn zp0(&self) -> &Zone<'a> {
357 self.zone(self.zp0)
358 }
359
360 #[inline(always)]
361 pub fn zp0_mut(&mut self) -> &mut Zone<'a> {
362 self.zone_mut(self.zp0)
363 }
364
365 #[inline(always)]
366 pub fn zp1(&self) -> &Zone {
367 self.zone(self.zp1)
368 }
369
370 #[inline(always)]
371 pub fn zp1_mut(&mut self) -> &mut Zone<'a> {
372 self.zone_mut(self.zp1)
373 }
374
375 #[inline(always)]
376 pub fn zp2(&self) -> &Zone {
377 self.zone(self.zp2)
378 }
379
380 #[inline(always)]
381 pub fn zp2_mut(&mut self) -> &mut Zone<'a> {
382 self.zone_mut(self.zp2)
383 }
384}
385
386impl GraphicsState<'_> {
387 pub(crate) fn move_original(
390 &mut self,
391 zone: ZonePointer,
392 point_ix: usize,
393 distance: F26Dot6,
394 ) -> Result<(), HintErrorKind> {
395 let fv = self.freedom_vector;
396 let fdotp = self.fdotp;
397 let axis = self.freedom_axis;
398 let point = self.zone_mut(zone).original_mut(point_ix)?;
399 match axis {
400 CoordAxis::X => point.x += distance,
401 CoordAxis::Y => point.y += distance,
402 CoordAxis::Both => {
403 let distance = distance.to_bits();
404 if fv.x != 0 {
405 point.x += F26Dot6::from_bits(math::mul_div(distance, fv.x, fdotp));
406 }
407 if fv.y != 0 {
408 point.y += F26Dot6::from_bits(math::mul_div(distance, fv.y, fdotp));
409 }
410 }
411 }
412 Ok(())
413 }
414
415 pub(crate) fn move_point(
418 &mut self,
419 zone: ZonePointer,
420 point_ix: usize,
421 distance: F26Dot6,
422 ) -> Result<(), HintErrorKind> {
423 let back_compat = self.backward_compatibility;
432 let back_compat_and_did_iup = back_compat && self.did_iup_x && self.did_iup_y;
433 let zone = &mut self.zones[zone as usize];
434 let point = zone.point_mut(point_ix)?;
435 match self.freedom_axis {
436 CoordAxis::X => {
437 if !back_compat {
438 point.x += distance;
439 }
440 zone.touch(point_ix, CoordAxis::X)?;
441 }
442 CoordAxis::Y => {
443 if !back_compat_and_did_iup {
444 point.y += distance;
445 }
446 zone.touch(point_ix, CoordAxis::Y)?;
447 }
448 CoordAxis::Both => {
449 let fv = self.freedom_vector;
451 let distance = distance.to_bits();
452 if fv.x != 0 {
453 if !back_compat {
454 point.x += F26Dot6::from_bits(math::mul_div(distance, fv.x, self.fdotp));
455 }
456 zone.touch(point_ix, CoordAxis::X)?;
457 }
458 if fv.y != 0 {
459 if !back_compat_and_did_iup {
460 zone.point_mut(point_ix)?.y +=
461 F26Dot6::from_bits(math::mul_div(distance, fv.y, self.fdotp));
462 }
463 zone.touch(point_ix, CoordAxis::Y)?;
464 }
465 }
466 }
467 Ok(())
468 }
469
470 pub(crate) fn move_zp2_point(
477 &mut self,
478 point_ix: usize,
479 dx: F26Dot6,
480 dy: F26Dot6,
481 do_touch: bool,
482 ) -> Result<(), HintErrorKind> {
483 let back_compat = self.backward_compatibility;
485 let back_compat_and_did_iup = back_compat && self.did_iup_x && self.did_iup_y;
486 let fv = self.freedom_vector;
487 let zone = self.zp2_mut();
488 if fv.x != 0 {
489 if !back_compat {
490 zone.point_mut(point_ix)?.x += dx;
491 }
492 if do_touch {
493 zone.touch(point_ix, CoordAxis::X)?;
494 }
495 }
496 if fv.y != 0 {
497 if !back_compat_and_did_iup {
498 zone.point_mut(point_ix)?.y += dy;
499 }
500 if do_touch {
501 zone.touch(point_ix, CoordAxis::Y)?;
502 }
503 }
504 Ok(())
505 }
506
507 pub(crate) fn point_displacement(
510 &mut self,
511 opcode: u8,
512 ) -> Result<PointDisplacement, HintErrorKind> {
513 let (zone, point_ix) = if (opcode & 1) != 0 {
514 (self.zp0, self.rp1)
515 } else {
516 (self.zp1, self.rp2)
517 };
518 let zone_data = self.zone(zone);
519 let point = zone_data.point(point_ix)?;
520 let original_point = zone_data.original(point_ix)?;
521 let distance = self.project(point, original_point);
522 let fv = self.freedom_vector;
523 let dx = F26Dot6::from_bits(math::mul_div(distance.to_bits(), fv.x, self.fdotp));
524 let dy = F26Dot6::from_bits(math::mul_div(distance.to_bits(), fv.y, self.fdotp));
525 Ok(PointDisplacement {
526 zone,
527 point_ix,
528 dx,
529 dy,
530 })
531 }
532}
533
534#[derive(PartialEq, Debug)]
535pub(crate) struct PointDisplacement {
536 pub zone: ZonePointer,
537 pub point_ix: usize,
538 pub dx: F26Dot6,
539 pub dy: F26Dot6,
540}
541
542impl CoordAxis {
543 fn touched_marker(self) -> PointMarker {
544 match self {
545 CoordAxis::Both => PointMarker::TOUCHED,
546 CoordAxis::X => PointMarker::TOUCHED_X,
547 CoordAxis::Y => PointMarker::TOUCHED_Y,
548 }
549 }
550}
551
552#[cfg(test)]
553mod tests {
554 use super::{math, CoordAxis, GraphicsState, PointDisplacement, Zone, ZonePointer};
555 use raw::{
556 tables::glyf::{PointFlags, PointMarker},
557 types::{F26Dot6, Point},
558 };
559
560 #[test]
561 fn flip_on_curve_point() {
562 let on_curve = PointFlags::on_curve();
563 let off_curve = PointFlags::off_curve_quad();
564 let mut zone = Zone {
565 unscaled: &mut [],
566 original: &mut [],
567 points: &mut [],
568 contours: &[],
569 flags: &mut [on_curve, off_curve, off_curve, on_curve],
570 };
571 for i in 0..4 {
572 zone.flip_on_curve(i).unwrap();
573 }
574 assert_eq!(zone.flags, &[off_curve, on_curve, on_curve, off_curve]);
575 }
576
577 #[test]
578 fn set_on_curve_regions() {
579 let on_curve = PointFlags::on_curve();
580 let off_curve = PointFlags::off_curve_quad();
581 let mut zone = Zone {
582 unscaled: &mut [],
583 original: &mut [],
584 points: &mut [],
585 contours: &[],
586 flags: &mut [on_curve, off_curve, off_curve, on_curve],
587 };
588 zone.set_on_curve(0, 2, true).unwrap();
589 zone.set_on_curve(2, 4, false).unwrap();
590 assert_eq!(zone.flags, &[on_curve, on_curve, off_curve, off_curve]);
591 }
592
593 #[test]
594 fn iup_shift() {
595 let [untouched, touched] = point_markers();
596 let mut original = f26dot6_points([(0, 0), (10, 10), (20, 20)]);
598 let mut points = f26dot6_points([(-5, -20), (10, 10), (20, 20)]);
599 let mut zone = Zone {
600 unscaled: &mut [],
601 original: &mut original,
602 points: &mut points,
603 contours: &[3],
604 flags: &mut [touched, untouched, untouched],
605 };
606 zone.iup(CoordAxis::X).unwrap();
607 assert_eq!(zone.points, &f26dot6_points([(-5, -20), (5, 10), (15, 20)]),);
608 zone.iup(CoordAxis::Y).unwrap();
609 assert_eq!(zone.points, &f26dot6_points([(-5, -20), (5, -10), (15, 0)]),);
610 }
611
612 #[test]
613 fn iup_interpolate() {
614 let [untouched, touched] = point_markers();
615 let mut original = f26dot6_points([(0, 0), (10, 10), (20, 20)]);
617 let mut points = f26dot6_points([(-5, -20), (10, 10), (27, 56)]);
618 let mut zone = Zone {
619 unscaled: &mut [
620 Point::new(0, 0),
621 Point::new(500, 500),
622 Point::new(1000, 1000),
623 ],
624 original: &mut original,
625 points: &mut points,
626 contours: &[3],
627 flags: &mut [touched, untouched, touched],
628 };
629 zone.iup(CoordAxis::X).unwrap();
630 assert_eq!(
631 zone.points,
632 &f26dot6_points([(-5, -20), (11, 10), (27, 56)]),
633 );
634 zone.iup(CoordAxis::Y).unwrap();
635 assert_eq!(
636 zone.points,
637 &f26dot6_points([(-5, -20), (11, 18), (27, 56)]),
638 );
639 }
640
641 #[test]
642 fn move_point_x() {
643 let mut mock = MockGraphicsState::new();
644 let mut gs = mock.graphics_state(100, 0);
645 let point_ix = 0;
646 let orig_x = gs.zones[1].point(point_ix).unwrap().x;
647 let dx = F26Dot6::from_bits(10);
648 gs.move_point(ZonePointer::Glyph, 0, dx).unwrap();
650 assert_eq!(orig_x, gs.zones[1].point(point_ix).unwrap().x);
651 gs.backward_compatibility = false;
653 gs.move_point(ZonePointer::Glyph, 0, dx).unwrap();
654 let new_x = gs.zones[1].point(point_ix).unwrap().x;
655 assert_ne!(orig_x, new_x);
656 assert_eq!(new_x, orig_x + dx)
657 }
658
659 #[test]
660 fn move_point_y() {
661 let mut mock = MockGraphicsState::new();
662 let mut gs = mock.graphics_state(0, 100);
663 let point_ix = 0;
664 let orig_y = gs.zones[1].point(point_ix).unwrap().y;
665 let dy = F26Dot6::from_bits(10);
666 gs.did_iup_x = true;
669 gs.did_iup_y = true;
670 gs.move_point(ZonePointer::Glyph, 0, dy).unwrap();
671 assert_eq!(orig_y, gs.zones[1].point(point_ix).unwrap().y);
672 gs.did_iup_x = false;
674 gs.did_iup_y = false;
675 gs.move_point(ZonePointer::Glyph, 0, dy).unwrap();
676 let new_y = gs.zones[1].point(point_ix).unwrap().y;
677 assert_ne!(orig_y, new_y);
678 assert_eq!(new_y, orig_y + dy)
679 }
680
681 #[test]
682 fn move_point_x_and_y() {
683 let mut mock = MockGraphicsState::new();
684 let mut gs = mock.graphics_state(100, 50);
685 let point_ix = 0;
686 let orig_point = gs.zones[1].point(point_ix).unwrap();
687 let dist = F26Dot6::from_bits(10);
688 gs.did_iup_x = true;
690 gs.did_iup_y = true;
691 gs.move_point(ZonePointer::Glyph, 0, dist).unwrap();
692 assert_eq!(orig_point, gs.zones[1].point(point_ix).unwrap());
693 gs.backward_compatibility = false;
695 gs.did_iup_x = false;
696 gs.did_iup_y = false;
697 gs.move_point(ZonePointer::Glyph, 0, dist).unwrap();
698 let point = gs.zones[1].point(point_ix).unwrap();
699 assert_eq!(point.map(F26Dot6::to_bits), Point::new(4, -16));
700 }
701
702 #[test]
703 fn move_original_x() {
704 let mut mock = MockGraphicsState::new();
705 let mut gs = mock.graphics_state(100, 0);
706 let point_ix = 0;
707 let orig_x = gs.zones[1].original(point_ix).unwrap().x;
708 let dx = F26Dot6::from_bits(10);
709 gs.move_original(ZonePointer::Glyph, 0, dx).unwrap();
710 let new_x = gs.zones[1].original(point_ix).unwrap().x;
711 assert_eq!(new_x, orig_x + dx)
712 }
713
714 #[test]
715 fn move_original_y() {
716 let mut mock = MockGraphicsState::new();
717 let mut gs = mock.graphics_state(0, 100);
718 let point_ix = 0;
719 let orig_y = gs.zones[1].original(point_ix).unwrap().y;
720 let dy = F26Dot6::from_bits(10);
721 gs.move_original(ZonePointer::Glyph, 0, dy).unwrap();
722 let new_y = gs.zones[1].original(point_ix).unwrap().y;
723 assert_eq!(new_y, orig_y + dy)
724 }
725
726 #[test]
727 fn move_original_x_and_y() {
728 let mut mock = MockGraphicsState::new();
729 let mut gs = mock.graphics_state(100, 50);
730 let point_ix = 0;
731 let dist = F26Dot6::from_bits(10);
732 gs.move_original(ZonePointer::Glyph, 0, dist).unwrap();
733 let point = gs.zones[1].original(point_ix).unwrap();
734 assert_eq!(point.map(F26Dot6::to_bits), Point::new(9, 4));
735 }
736
737 #[test]
738 fn move_zp2_point() {
739 let mut mock = MockGraphicsState::new();
740 let mut gs = mock.graphics_state(100, 50);
741 gs.zp2 = ZonePointer::Glyph;
742 let point_ix = 0;
743 let orig_point = gs.zones[1].point(point_ix).unwrap();
744 let dx = F26Dot6::from_bits(10);
745 let dy = F26Dot6::from_bits(-10);
746 gs.did_iup_x = true;
748 gs.did_iup_y = true;
749 gs.move_zp2_point(point_ix, dx, dy, false).unwrap();
750 assert_eq!(orig_point, gs.zones[1].point(point_ix).unwrap());
751 gs.backward_compatibility = false;
753 gs.did_iup_x = false;
754 gs.did_iup_y = false;
755 gs.move_zp2_point(point_ix, dx, dy, false).unwrap();
756 let point = gs.zones[1].point(point_ix).unwrap();
757 assert_eq!(point, orig_point + Point::new(dx, dy));
758 }
759
760 #[test]
761 fn point_displacement() {
762 let mut mock = MockGraphicsState::new();
763 let mut gs = mock.graphics_state(100, 50);
764 gs.zp0 = ZonePointer::Glyph;
765 gs.rp1 = 0;
766 assert_eq!(
767 gs.point_displacement(1).unwrap(),
768 PointDisplacement {
769 zone: ZonePointer::Glyph,
770 point_ix: 0,
771 dx: F26Dot6::from_f64(-0.1875),
772 dy: F26Dot6::from_f64(-0.09375),
773 }
774 );
775 gs.rp2 = 2;
776 assert_eq!(
777 gs.point_displacement(0).unwrap(),
778 PointDisplacement {
779 zone: ZonePointer::Glyph,
780 point_ix: 2,
781 dx: F26Dot6::from_f64(0.390625),
782 dy: F26Dot6::from_f64(0.203125),
783 }
784 );
785 }
786
787 struct MockGraphicsState {
788 points: [Point<F26Dot6>; 3],
789 original: [Point<F26Dot6>; 3],
790 contours: [u16; 1],
791 flags: [PointFlags; 3],
792 }
793
794 impl MockGraphicsState {
795 fn new() -> Self {
796 Self {
797 points: f26dot6_points([(-5, -20), (10, 10), (20, 20)]),
798 original: f26dot6_points([(0, 0), (10, 10), (20, -42)]),
799 flags: [PointFlags::default(); 3],
800 contours: [3],
801 }
802 }
803
804 fn graphics_state(&mut self, fv_x: i32, fv_y: i32) -> GraphicsState {
805 let glyph = Zone {
806 unscaled: &mut [],
807 original: &mut self.original,
808 points: &mut self.points,
809 contours: &self.contours,
810 flags: &mut self.flags,
811 };
812 let v = math::normalize14(fv_x, fv_y);
813 let mut gs = GraphicsState {
814 zones: [Zone::default(), glyph],
815 freedom_vector: v,
816 proj_vector: v,
817 zp0: ZonePointer::Glyph,
818 ..Default::default()
819 };
820 gs.update_projection_state();
821 gs
822 }
823 }
824
825 fn point_markers() -> [PointFlags; 2] {
826 let untouched = PointFlags::default();
827 let mut touched = untouched;
828 touched.set_marker(PointMarker::TOUCHED);
829 [untouched, touched]
830 }
831
832 fn f26dot6_points<const N: usize>(points: [(i32, i32); N]) -> [Point<F26Dot6>; N] {
833 points.map(|point| Point::new(F26Dot6::from_bits(point.0), F26Dot6::from_bits(point.1)))
834 }
835}