1use super::{
8 super::{
9 graphics::CoordAxis,
10 zone::{PointDisplacement, ZonePointer},
11 },
12 math, Engine, F26Dot6, HintErrorKind, OpResult,
13};
14
15impl Engine<'_> {
16 pub(super) fn op_flippt(&mut self) -> OpResult {
32 let count = self.graphics.loop_counter as usize;
33 self.graphics.loop_counter = 1;
34 if self.graphics.backward_compatibility
37 && self.graphics.did_iup_x
38 && self.graphics.did_iup_y
39 {
40 for _ in 0..count {
41 self.value_stack.pop()?;
42 }
43 return Ok(());
44 }
45 let zone = self.graphics.zone_mut(ZonePointer::Glyph);
46 for _ in 0..count {
47 let p = self.value_stack.pop_usize()?;
48 zone.flip_on_curve(p)?;
49 }
50 Ok(())
51 }
52
53 pub(super) fn op_fliprgon(&mut self) -> OpResult {
67 self.set_on_curve_for_range(true)
68 }
69
70 pub(super) fn op_fliprgoff(&mut self) -> OpResult {
84 self.set_on_curve_for_range(false)
85 }
86
87 pub(super) fn op_shp(&mut self, opcode: u8) -> OpResult {
107 let gs = &mut self.graphics;
108 let PointDisplacement { dx, dy, .. } = gs.point_displacement(opcode)?;
109 let count = gs.loop_counter;
110 gs.loop_counter = 1;
111 for _ in 0..count {
112 let p = self.value_stack.pop_usize()?;
113 gs.move_zp2_point(p, dx, dy, true)?;
114 }
115 Ok(())
116 }
117
118 pub(super) fn op_shc(&mut self, opcode: u8) -> OpResult {
139 let gs = &mut self.graphics;
140 let contour_ix = self.value_stack.pop_usize()?;
141 if !gs.is_pedantic && contour_ix >= gs.zp2().contours.len() {
142 return Ok(());
143 }
144 let point_disp = gs.point_displacement(opcode)?;
145 let start = if contour_ix != 0 {
146 gs.zp2().contour(contour_ix - 1)? as usize + 1
147 } else {
148 0
149 };
150 let end = if gs.zp2.is_twilight() {
151 gs.zp2().points.len()
152 } else {
153 gs.zp2().contour(contour_ix)? as usize + 1
154 };
155 for i in start..end {
156 if point_disp.zone != gs.zp2 || point_disp.point_ix != i {
157 gs.move_zp2_point(i, point_disp.dx, point_disp.dy, true)?;
158 }
159 }
160 Ok(())
161 }
162
163 pub(super) fn op_shz(&mut self, opcode: u8) -> OpResult {
182 let _e = ZonePointer::try_from(self.value_stack.pop()?)?;
183 let gs = &mut self.graphics;
184 let point_disp = gs.point_displacement(opcode)?;
185 let end = if gs.zp2.is_twilight() {
186 gs.zp2().points.len()
187 } else if !gs.zp2().contours.is_empty() {
188 *gs.zp2()
189 .contours
190 .last()
191 .ok_or(HintErrorKind::InvalidContourIndex(0))? as usize
192 + 1
193 } else {
194 0
195 };
196 for i in 0..end {
197 if point_disp.zone != gs.zp2 || i != point_disp.point_ix {
198 gs.move_zp2_point(i, point_disp.dx, point_disp.dy, false)?;
199 }
200 }
201 Ok(())
202 }
203
204 pub(super) fn op_shpix(&mut self) -> OpResult {
222 let gs = &mut self.graphics;
223 let in_twilight = gs.zp0.is_twilight() || gs.zp1.is_twilight() || gs.zp2.is_twilight();
224 let amount = self.value_stack.pop()?;
225 let dx = F26Dot6::from_bits(math::mul14(amount, gs.freedom_vector.x));
226 let dy = F26Dot6::from_bits(math::mul14(amount, gs.freedom_vector.y));
227 let count = gs.loop_counter;
228 gs.loop_counter = 1;
229 let did_iup = gs.did_iup_x && gs.did_iup_y;
230 for _ in 0..count {
231 let p = self.value_stack.pop_usize()?;
232 if gs.backward_compatibility {
233 if in_twilight
234 || (!did_iup
235 && ((gs.is_composite && gs.freedom_vector.y != 0)
236 || gs.zp2().is_touched(p, CoordAxis::Y)?))
237 {
238 gs.move_zp2_point(p, dx, dy, true)?;
239 }
240 } else {
241 gs.move_zp2_point(p, dx, dy, true)?;
242 }
243 }
244 Ok(())
245 }
246
247 pub(super) fn op_msirp(&mut self, opcode: u8) -> OpResult {
267 let gs = &mut self.graphics;
268 let distance = self.value_stack.pop_f26dot6()?;
269 let point_ix = self.value_stack.pop_usize()?;
270 if !gs.is_pedantic && !gs.in_bounds([(gs.zp1, point_ix), (gs.zp0, gs.rp0)]) {
271 return Ok(());
272 }
273 if gs.zp1.is_twilight() {
274 *gs.zp1_mut().point_mut(point_ix)? = gs.zp0().original(gs.rp0)?;
275 gs.move_original(gs.zp1, point_ix, distance)?;
276 *gs.zp1_mut().point_mut(point_ix)? = gs.zp1().original(point_ix)?;
277 }
278 let d = gs.project(gs.zp1().point(point_ix)?, gs.zp0().point(gs.rp0)?);
279 gs.move_point(gs.zp1, point_ix, distance.wrapping_sub(d))?;
280 gs.rp1 = gs.rp0;
281 gs.rp2 = point_ix;
282 if (opcode & 1) != 0 {
283 gs.rp0 = point_ix;
284 }
285 Ok(())
286 }
287
288 pub(super) fn op_mdap(&mut self, opcode: u8) -> OpResult {
306 let gs = &mut self.graphics;
307 let p = self.value_stack.pop_usize()?;
308 if !gs.is_pedantic && !gs.in_bounds([(gs.zp0, p)]) {
309 gs.rp0 = p;
310 gs.rp1 = p;
311 return Ok(());
312 }
313 let distance = if (opcode & 1) != 0 {
314 let cur_dist = gs.project(gs.zp0().point(p)?, Default::default());
315 gs.round(cur_dist) - cur_dist
316 } else {
317 F26Dot6::ZERO
318 };
319 gs.move_point(gs.zp0, p, distance)?;
320 gs.rp0 = p;
321 gs.rp1 = p;
322 Ok(())
323 }
324
325 pub(super) fn op_miap(&mut self, opcode: u8) -> OpResult {
346 let gs = &mut self.graphics;
347 let cvt_entry = self.value_stack.pop_usize()?;
348 let point_ix = self.value_stack.pop_usize()?;
349 let mut distance = self.cvt.get(cvt_entry)?;
350 if gs.zp0.is_twilight() {
351 let fv = gs.freedom_vector;
354 let z = gs.zp0_mut();
355 let original_point = z.original_mut(point_ix)?;
356 original_point.x = F26Dot6::from_bits(math::mul14(distance.to_bits(), fv.x));
357 original_point.y = F26Dot6::from_bits(math::mul14(distance.to_bits(), fv.y));
358 *z.point_mut(point_ix)? = *original_point;
359 }
360 let original_distance = gs.project(gs.zp0().point(point_ix)?, Default::default());
361 if (opcode & 1) != 0 {
362 let delta = (distance.wrapping_sub(original_distance)).abs();
363 if delta > gs.control_value_cutin {
364 distance = original_distance;
365 }
366 distance = gs.round(distance);
367 }
368 gs.move_point(gs.zp0, point_ix, distance.wrapping_sub(original_distance))?;
369 gs.rp0 = point_ix;
370 gs.rp1 = point_ix;
371 Ok(())
372 }
373
374 pub(super) fn op_mdrp(&mut self, opcode: u8) -> OpResult {
399 let gs = &mut self.graphics;
400 let p = self.value_stack.pop_usize()?;
401 if !gs.is_pedantic && !gs.in_bounds([(gs.zp1, p), (gs.zp0, gs.rp0)]) {
402 gs.rp1 = gs.rp0;
403 gs.rp2 = p;
404 if (opcode & 16) != 0 {
405 gs.rp0 = p;
406 }
407 return Ok(());
408 }
409 let mut original_distance = if gs.zp0.is_twilight() || gs.zp1.is_twilight() {
410 gs.dual_project(gs.zp1().original(p)?, gs.zp0().original(gs.rp0)?)
411 } else {
412 let v1 = gs.zp1().unscaled(p);
413 let v2 = gs.zp0().unscaled(gs.rp0);
414 let dist = gs.dual_project_unscaled(v1, v2);
415 F26Dot6::from_bits(math::mul(dist, gs.unscaled_to_pixels()))
416 };
417 let cutin = gs.single_width_cutin;
418 let value = gs.single_width;
419 if cutin > F26Dot6::ZERO
420 && original_distance < value + cutin
421 && original_distance > value - cutin
422 {
423 original_distance = if original_distance >= F26Dot6::ZERO {
424 value
425 } else {
426 -value
427 };
428 }
429 let mut distance = if (opcode & 4) != 0 {
431 gs.round(original_distance)
432 } else {
433 original_distance
434 };
435 if (opcode & 8) != 0 {
437 let min_distance = gs.min_distance;
438 if original_distance >= F26Dot6::ZERO {
439 if distance < min_distance {
440 distance = min_distance;
441 }
442 } else if distance > -min_distance {
443 distance = -min_distance;
444 }
445 }
446 original_distance = gs.project(gs.zp1().point(p)?, gs.zp0().point(gs.rp0)?);
447 gs.move_point(gs.zp1, p, distance.wrapping_sub(original_distance))?;
448 gs.rp1 = gs.rp0;
449 gs.rp2 = p;
450 if (opcode & 16) != 0 {
451 gs.rp0 = p;
452 }
453 Ok(())
454 }
455
456 pub(super) fn op_mirp(&mut self, opcode: u8) -> OpResult {
490 let gs = &mut self.graphics;
491 let n = (self.value_stack.pop()? + 1) as usize;
492 let p = self.value_stack.pop_usize()?;
493 if !gs.is_pedantic
494 && (!gs.in_bounds([(gs.zp1, p), (gs.zp0, gs.rp0)]) || (n > self.cvt.len()))
495 {
496 gs.rp1 = gs.rp0;
497 if (opcode & 16) != 0 {
498 gs.rp0 = p;
499 }
500 gs.rp2 = p;
501 return Ok(());
502 }
503 let mut cvt_distance = if n == 0 {
504 F26Dot6::ZERO
505 } else {
506 self.cvt.get(n - 1)?
507 };
508 let cutin = gs.single_width_cutin;
510 let value = gs.single_width;
511 let mut delta = cvt_distance.wrapping_sub(value).abs();
512 if delta < cutin {
513 cvt_distance = if cvt_distance >= F26Dot6::ZERO {
514 value
515 } else {
516 -value
517 };
518 }
519 if gs.zp1.is_twilight() {
520 let fv = gs.freedom_vector;
521 let point = {
522 let d = cvt_distance.to_bits();
523 let p2 = gs.zp0().original(gs.rp0)?;
524 let p1 = gs.zp1_mut().original_mut(p)?;
525 p1.x = p2.x + F26Dot6::from_bits(math::mul(d, fv.x));
526 p1.y = p2.y + F26Dot6::from_bits(math::mul(d, fv.y));
527 *p1
528 };
529 *gs.zp1_mut().point_mut(p)? = point;
530 }
531 let original_distance = gs.dual_project(gs.zp1().original(p)?, gs.zp0().original(gs.rp0)?);
532 let current_distance = gs.project(gs.zp1().point(p)?, gs.zp0().point(gs.rp0)?);
533 if gs.auto_flip && (original_distance.to_bits() ^ cvt_distance.to_bits()) < 0 {
535 cvt_distance = -cvt_distance;
536 }
537 let mut distance = if (opcode & 4) != 0 {
539 if gs.zp0 == gs.zp1 {
540 delta = cvt_distance.wrapping_sub(original_distance).abs();
541 if delta > gs.control_value_cutin {
542 cvt_distance = original_distance;
543 }
544 }
545 gs.round(cvt_distance)
546 } else {
547 cvt_distance
548 };
549 if (opcode & 8) != 0 {
551 let min_distance = gs.min_distance;
552 if original_distance >= F26Dot6::ZERO {
553 if distance < min_distance {
554 distance = min_distance
555 };
556 } else if distance > -min_distance {
557 distance = -min_distance
558 }
559 }
560 gs.move_point(gs.zp1, p, distance.wrapping_sub(current_distance))?;
561 gs.rp1 = gs.rp0;
562 if (opcode & 16) != 0 {
563 gs.rp0 = p;
564 }
565 gs.rp2 = p;
566 Ok(())
567 }
568
569 pub(super) fn op_alignrp(&mut self) -> OpResult {
584 let gs = &mut self.graphics;
585 let count = gs.loop_counter;
586 gs.loop_counter = 1;
587 for _ in 0..count {
588 let p = self.value_stack.pop_usize()?;
589 let distance = gs.project(gs.zp1().point(p)?, gs.zp0().point(gs.rp0)?);
590 gs.move_point(gs.zp1, p, -distance)?;
591 }
592 Ok(())
593 }
594
595 pub(super) fn op_isect(&mut self) -> OpResult {
612 let gs = &mut self.graphics;
613 let b1 = self.value_stack.pop_usize()?;
614 let b0 = self.value_stack.pop_usize()?;
615 let a1 = self.value_stack.pop_usize()?;
616 let a0 = self.value_stack.pop_usize()?;
617 let point_ix = self.value_stack.pop_usize()?;
618 let [pa0, pa1] = {
622 let z = gs.zp1();
623 [z.point(a0)?, z.point(a1)?].map(|p| p.map(F26Dot6::to_bits))
624 };
625 let [pb0, pb1] = {
626 let z = gs.zp0();
627 [z.point(b0)?, z.point(b1)?].map(|p| p.map(F26Dot6::to_bits))
628 };
629 let dbx = pb1.x - pb0.x;
630 let dby = pb1.y - pb0.y;
631 let dax = pa1.x - pa0.x;
632 let day = pa1.y - pa0.y;
633 let dx = pb0.x - pa0.x;
634 let dy = pb0.y - pa0.y;
635 use math::mul_div;
636 let discriminant = mul_div(dax, -dby, 0x40) + mul_div(day, dbx, 0x40);
637 let dotproduct = mul_div(dax, dbx, 0x40) + mul_div(day, dby, 0x40);
638 if discriminant.wrapping_abs().wrapping_mul(19) > dotproduct.abs() {
651 let v = mul_div(dx, -dby, 0x40) + mul_div(dy, dbx, 0x40);
652 let x = mul_div(v, dax, discriminant);
653 let y = mul_div(v, day, discriminant);
654 let point = gs.zp2_mut().point_mut(point_ix)?;
655 point.x = F26Dot6::from_bits(pa0.x + x);
656 point.y = F26Dot6::from_bits(pa0.y + y);
657 } else {
658 let point = gs.zp2_mut().point_mut(point_ix)?;
659 point.x = F26Dot6::from_bits((pa0.x + pa1.x + pb0.x + pb1.x) / 4);
660 point.y = F26Dot6::from_bits((pa0.y + pa1.y + pb0.y + pb1.y) / 4);
661 }
662 gs.zp2_mut().touch(point_ix, CoordAxis::Both)?;
663 Ok(())
664 }
665
666 pub(super) fn op_alignpts(&mut self) -> OpResult {
680 let p2 = self.value_stack.pop_usize()?;
681 let p1 = self.value_stack.pop_usize()?;
682 let gs = &mut self.graphics;
683 let distance = F26Dot6::from_bits(
684 gs.project(gs.zp0().point(p2)?, gs.zp1().point(p1)?)
685 .to_bits()
686 / 2,
687 );
688 gs.move_point(gs.zp1, p1, distance)?;
689 gs.move_point(gs.zp0, p2, -distance)?;
690 Ok(())
691 }
692
693 pub(super) fn op_ip(&mut self) -> OpResult {
711 let gs = &mut self.graphics;
712 let count = gs.loop_counter;
713 gs.loop_counter = 1;
714 if !gs.is_pedantic && !gs.in_bounds([(gs.zp0, gs.rp1), (gs.zp1, gs.rp2)]) {
715 return Ok(());
716 }
717 let in_twilight = gs.zp0.is_twilight() || gs.zp1.is_twilight() || gs.zp2.is_twilight();
718 let orus_base = if in_twilight {
719 gs.zp0().original(gs.rp1)?
720 } else {
721 gs.zp0().unscaled(gs.rp1).map(F26Dot6::from_bits)
722 };
723 let cur_base = gs.zp0().point(gs.rp1)?;
724 let old_range = if in_twilight {
725 gs.dual_project(gs.zp1().original(gs.rp2)?, orus_base)
726 } else {
727 gs.dual_project(gs.zp1().unscaled(gs.rp2).map(F26Dot6::from_bits), orus_base)
728 };
729 let cur_range = gs.project(gs.zp1().point(gs.rp2)?, cur_base);
730 for _ in 0..count {
731 let point = self.value_stack.pop_usize()?;
732 if !gs.is_pedantic && !gs.in_bounds([(gs.zp2, point)]) {
733 continue;
734 }
735 let original_distance = if in_twilight {
736 gs.dual_project(gs.zp2().original(point)?, orus_base)
737 } else {
738 gs.dual_project(gs.zp2().unscaled(point).map(F26Dot6::from_bits), orus_base)
739 };
740 let cur_distance = gs.project(gs.zp2().point(point)?, cur_base);
741 let new_distance = if original_distance != F26Dot6::ZERO {
742 if old_range != F26Dot6::ZERO {
743 F26Dot6::from_bits(math::mul_div(
744 original_distance.to_bits(),
745 cur_range.to_bits(),
746 old_range.to_bits(),
747 ))
748 } else {
749 original_distance
750 }
751 } else {
752 F26Dot6::ZERO
753 };
754 gs.move_point(gs.zp2, point, new_distance.wrapping_sub(cur_distance))?;
755 }
756 Ok(())
757 }
758
759 pub(super) fn op_iup(&mut self, opcode: u8) -> OpResult {
776 let gs = &mut self.graphics;
777 let axis = if (opcode & 1) != 0 {
778 CoordAxis::X
779 } else {
780 CoordAxis::Y
781 };
782 let mut run = true;
783 if gs.backward_compatibility {
786 if gs.did_iup_x && gs.did_iup_y {
787 run = false;
788 }
789 if axis == CoordAxis::X {
790 gs.did_iup_x = true;
791 } else {
792 gs.did_iup_y = true;
793 }
794 }
795 if run {
796 gs.zone_mut(ZonePointer::Glyph).iup(axis)?;
797 }
798 Ok(())
799 }
800
801 pub(super) fn op_utp(&mut self) -> OpResult {
818 let p = self.value_stack.pop_usize()?;
819 let coord_axis = match (
820 self.graphics.freedom_vector.x != 0,
821 self.graphics.freedom_vector.y != 0,
822 ) {
823 (true, true) => Some(CoordAxis::Both),
824 (true, false) => Some(CoordAxis::X),
825 (false, true) => Some(CoordAxis::Y),
826 (false, false) => None,
827 };
828 if let Some(coord_axis) = coord_axis {
829 self.graphics.zp0_mut().untouch(p, coord_axis)?;
830 }
831 Ok(())
832 }
833
834 fn set_on_curve_for_range(&mut self, on: bool) -> OpResult {
836 let high_point = self.value_stack.pop_usize()?;
837 let low_point = self.value_stack.pop_usize()?;
838 let high_point = high_point
841 .checked_add(1)
842 .ok_or(HintErrorKind::InvalidPointIndex(high_point))?;
843 if self.graphics.backward_compatibility
846 && self.graphics.did_iup_x
847 && self.graphics.did_iup_y
848 {
849 return Ok(());
850 }
851 self.graphics
852 .zone_mut(ZonePointer::Glyph)
853 .set_on_curve(low_point, high_point, on)
854 }
855}
856
857#[cfg(test)]
858mod tests {
859 use super::{super::MockEngine, math, CoordAxis, Engine, ZonePointer};
860 use raw::{
861 tables::glyf::{bytecode::Opcode, PointMarker},
862 types::{F26Dot6, Point},
863 };
864
865 #[test]
866 fn flip_point() {
867 let mut mock = MockEngine::new();
868 let mut engine = mock.engine();
869 let count = 5;
872 engine.value_stack.push(count).unwrap();
874 engine.op_sloop().unwrap();
875 for i in (1..=9).step_by(2) {
877 engine.value_stack.push(i).unwrap();
878 }
879 assert_eq!(engine.value_stack.len(), count as usize);
880 engine.op_flippt().unwrap();
882 let flags = &engine.graphics.zones[1].flags;
883 for i in 0..10 {
884 assert_eq!(flags[i].is_on_curve(), i & 1 != 0);
886 }
887 }
888
889 #[test]
891 fn state_prevents_flip_point() {
892 let mut mock = MockEngine::new();
893 let mut engine = mock.engine();
894 let count = 5;
897 engine.value_stack.push(count).unwrap();
899 engine.op_sloop().unwrap();
900 for i in (1..=9).step_by(2) {
902 engine.value_stack.push(i).unwrap();
903 }
904 assert_eq!(engine.value_stack.len(), count as usize);
905 engine.graphics.backward_compatibility = true;
907 engine.graphics.did_iup_x = true;
908 engine.graphics.did_iup_y = true;
909 engine.op_flippt().unwrap();
911 let flags = &engine.graphics.zones[1].flags;
912 for i in 0..10 {
913 assert!(!flags[i].is_on_curve());
915 }
916 }
917
918 #[test]
919 fn flip_range_on_off() {
920 let mut mock = MockEngine::new();
921 let mut engine = mock.engine();
922 engine.value_stack.push(10).unwrap();
925 engine.value_stack.push(20).unwrap();
926 engine.op_fliprgon().unwrap();
927 for (i, flag) in engine.graphics.zones[1].flags.iter().enumerate() {
928 assert_eq!(flag.is_on_curve(), (10..=20).contains(&i));
929 }
930 engine.value_stack.push(12).unwrap();
932 engine.value_stack.push(15).unwrap();
933 engine.op_fliprgoff().unwrap();
934 for (i, flag) in engine.graphics.zones[1].flags.iter().enumerate() {
935 assert_eq!(
936 flag.is_on_curve(),
937 (10..=11).contains(&i) || (16..=20).contains(&i)
938 );
939 }
940 }
941
942 #[test]
944 fn state_prevents_flip_range_on_off() {
945 let mut mock = MockEngine::new();
946 let mut engine = mock.engine();
947 engine.graphics.backward_compatibility = true;
949 engine.graphics.did_iup_x = true;
950 engine.graphics.did_iup_y = true;
951 engine.value_stack.push(10).unwrap();
954 engine.value_stack.push(20).unwrap();
955 engine.op_fliprgon().unwrap();
956 for flag in engine.graphics.zones[1].flags.iter() {
957 assert!(!flag.is_on_curve());
958 }
959 for flag in engine.graphics.zones[1].flags.iter_mut() {
961 flag.set_on_curve();
962 }
963 engine.value_stack.push(12).unwrap();
965 engine.value_stack.push(15).unwrap();
966 engine.op_fliprgoff().unwrap();
967 for flag in engine.graphics.zones[1].flags.iter() {
968 assert!(flag.is_on_curve());
969 }
970 }
971
972 #[test]
973 fn untouch_point() {
974 let mut mock = MockEngine::new();
975 let mut engine = mock.engine();
976 let count = engine.graphics.zones[1].points.len();
978 for i in 0..count {
979 engine.graphics.zones[1].touch(i, CoordAxis::Both).unwrap();
980 }
981 let mut untouch = |point_ix: usize, fx, fy, marker| {
982 assert!(engine.graphics.zp0().flags[point_ix].has_marker(marker));
983 engine.graphics.freedom_vector.x = fx;
985 engine.graphics.freedom_vector.y = fy;
986 engine.value_stack.push(point_ix as i32).unwrap();
987 engine.op_utp().unwrap();
988 assert!(!engine.graphics.zp0().flags[point_ix].has_marker(marker));
989 };
990 untouch(0, 1, 0, PointMarker::TOUCHED_X);
992 untouch(1, 0, 1, PointMarker::TOUCHED_Y);
994 untouch(2, 1, 1, PointMarker::TOUCHED);
996 }
997
998 #[test]
999 fn shp() {
1000 let mut mock = MockEngine::new();
1001 let mut engine = mock.engine();
1002 set_test_vectors(&mut engine);
1003 engine.graphics.backward_compatibility = false;
1004 engine.graphics.zp0 = ZonePointer::Glyph;
1005 engine.graphics.zp2 = ZonePointer::Glyph;
1006 engine.graphics.rp2 = 1;
1007 let point = engine.graphics.zones[1].point_mut(1).unwrap();
1008 point.x = F26Dot6::from_bits(132);
1009 point.y = F26Dot6::from_bits(-256);
1010 engine.value_stack.push(1).unwrap();
1011 engine.op_shp(0).unwrap();
1012 let point = engine.graphics.zones[1].point(1).unwrap();
1013 assert_eq!(point.map(F26Dot6::to_bits), Point::new(136, -254));
1014 }
1015
1016 #[test]
1017 fn shc() {
1018 let mut mock = MockEngine::new();
1019 let mut engine = mock.engine();
1020 set_test_vectors(&mut engine);
1021 engine.graphics.backward_compatibility = false;
1022 engine.graphics.zp0 = ZonePointer::Glyph;
1023 engine.graphics.zp2 = ZonePointer::Glyph;
1024 engine.graphics.rp2 = 1;
1025 let point = engine.graphics.zones[1].point_mut(1).unwrap();
1026 point.x = F26Dot6::from_bits(132);
1027 point.y = F26Dot6::from_bits(-256);
1028 engine.value_stack.push(0).unwrap();
1029 engine.op_shc(0).unwrap();
1030 let points = engine.graphics.zones[1]
1031 .points
1032 .iter()
1033 .map(|p| p.map(F26Dot6::to_bits))
1034 .take(3)
1035 .collect::<Vec<_>>();
1036 assert_eq!(
1037 points,
1038 &[Point::new(4, 2), Point::new(132, -256), Point::new(4, 2),]
1039 );
1040 }
1041
1042 #[test]
1043 fn shz() {
1044 let mut mock = MockEngine::new();
1045 let mut engine = mock.engine();
1046 set_test_vectors(&mut engine);
1047 engine.graphics.backward_compatibility = false;
1048 engine.graphics.zp0 = ZonePointer::Glyph;
1049 engine.graphics.zp2 = ZonePointer::Glyph;
1050 engine.graphics.rp2 = 1;
1051 let point = engine.graphics.zones[1].point_mut(1).unwrap();
1052 point.x = F26Dot6::from_bits(132);
1053 point.y = F26Dot6::from_bits(-256);
1054 engine.value_stack.push(0).unwrap();
1055 engine.op_shz(0).unwrap();
1056 let points = engine.graphics.zones[1]
1057 .points
1058 .iter()
1059 .map(|p| p.map(F26Dot6::to_bits))
1060 .take(3)
1061 .collect::<Vec<_>>();
1062 assert_eq!(
1063 points,
1064 &[Point::new(4, 2), Point::new(132, -256), Point::new(4, 2),]
1065 );
1066 }
1067
1068 #[test]
1069 fn shpix() {
1070 let mut mock = MockEngine::new();
1071 let mut engine = mock.engine();
1072 set_test_vectors(&mut engine);
1073 engine.graphics.backward_compatibility = false;
1074 engine.graphics.zp2 = ZonePointer::Glyph;
1075 let point = engine.graphics.zones[1].point_mut(1).unwrap();
1076 point.x = F26Dot6::from_bits(132);
1077 point.y = F26Dot6::from_bits(-256);
1078 engine.value_stack.push(1).unwrap();
1080 engine.value_stack.push(42).unwrap();
1082 engine.op_shpix().unwrap();
1083 let point = engine.graphics.zones[1].point(1).unwrap();
1084 assert_eq!(point.map(F26Dot6::to_bits), Point::new(170, -237));
1085 }
1086
1087 #[test]
1088 fn msirp() {
1089 let mut mock = MockEngine::new();
1090 let mut engine = mock.engine();
1091 set_test_vectors(&mut engine);
1092 engine.graphics.backward_compatibility = false;
1093 engine.graphics.zp0 = ZonePointer::Glyph;
1094 engine.graphics.zp1 = ZonePointer::Glyph;
1095 let point = engine.graphics.zones[1].point_mut(1).unwrap();
1096 point.x = F26Dot6::from_bits(132);
1097 point.y = F26Dot6::from_bits(-256);
1098 engine.value_stack.push(1).unwrap();
1100 engine.value_stack.push(-42).unwrap();
1102 engine.op_msirp(0).unwrap();
1103 let point = engine.graphics.zones[1].point(1).unwrap();
1104 assert_eq!(point.map(F26Dot6::to_bits), Point::new(91, -277));
1105 assert_eq!(engine.graphics.rp0, 0);
1106 engine.value_stack.push(4).unwrap();
1108 engine.value_stack.push(0).unwrap();
1109 engine.op_msirp(1).unwrap();
1110 assert_eq!(engine.graphics.rp0, 4);
1111 }
1112
1113 #[test]
1114 fn mdap() {
1115 let mut mock = MockEngine::new();
1116 let mut engine = mock.engine();
1117 set_test_vectors(&mut engine);
1118 engine.graphics.backward_compatibility = false;
1119 engine.graphics.zp0 = ZonePointer::Glyph;
1120 engine.set_point_f26dot6(1, 1, (132, -256));
1122 engine.value_stack.push(1).unwrap();
1123 engine.op_mdap(1).unwrap();
1124 let point = engine.graphics.zones[1].point(1).unwrap();
1125 assert_eq!(point.map(F26Dot6::to_bits), Point::new(128, -258));
1126 engine.set_point_f26dot6(1, 2, (132, -256));
1128 engine.value_stack.push(2).unwrap();
1129 engine.op_mdap(0).unwrap();
1130 let point = engine.graphics.zones[1].point(2).unwrap();
1131 assert_eq!(point.map(F26Dot6::to_bits), Point::new(132, -256));
1132 }
1133
1134 #[test]
1135 fn miap() {
1136 let mut mock = MockEngine::new();
1137 let mut engine = mock.engine();
1138 set_test_vectors(&mut engine);
1139 engine.graphics.backward_compatibility = false;
1140 engine.graphics.zp0 = ZonePointer::Glyph;
1141 engine.cvt.set(1, F26Dot6::from_f64(0.75)).unwrap();
1143 engine.set_point_f26dot6(1, 1, (132, -256));
1145 engine.value_stack.push(1).unwrap();
1146 engine.value_stack.push(1).unwrap();
1147 engine.op_miap(1).unwrap();
1148 let point = engine.graphics.zones[1].point(1).unwrap();
1149 assert_eq!(point.map(F26Dot6::to_bits), Point::new(186, -229));
1150 engine.set_point_f26dot6(1, 2, (132, -256));
1152 engine.value_stack.push(2).unwrap();
1153 engine.value_stack.push(1).unwrap();
1154 engine.op_miap(0).unwrap();
1155 let point = engine.graphics.zones[1].point(2).unwrap();
1156 assert_eq!(point.map(F26Dot6::to_bits), Point::new(171, -236));
1157 }
1158
1159 #[test]
1162 fn mdrp_rp0() {
1163 let mut mock = MockEngine::new();
1164 let mut engine = mock.engine();
1165 engine.graphics.rp0 = 0;
1166 engine.value_stack.push(1).unwrap();
1168 engine.op_mdrp(Opcode::MDRP00000 as _).unwrap();
1169 assert_eq!(engine.graphics.rp0, 0);
1170 engine.value_stack.push(1).unwrap();
1172 engine.op_mdrp(Opcode::MDRP10000 as _).unwrap();
1173 assert_eq!(engine.graphics.rp0, 1);
1174 }
1175
1176 #[test]
1179 fn mdrp_mindist() {
1180 let mut mock = MockEngine::new();
1181 let mut engine = mock.engine();
1182 set_test_vectors(&mut engine);
1183 engine.graphics.backward_compatibility = false;
1184 engine.graphics.zp0 = ZonePointer::Glyph;
1185 engine.set_point_f26dot6(1, 1, (132, -256));
1187 engine.value_stack.push(1).unwrap();
1188 engine.op_mdrp(Opcode::MDRP00000 as _).unwrap();
1189 let point = engine.graphics.zones[1].point(1).unwrap();
1190 assert_eq!(point.map(F26Dot6::to_bits), Point::new(128, -258));
1191 engine.set_point_f26dot6(1, 2, (132, -256));
1193 engine.value_stack.push(2).unwrap();
1194 engine.op_mdrp(Opcode::MDRP01000 as _).unwrap();
1195 let point = engine.graphics.zones[1].point(2).unwrap();
1196 assert_eq!(point.map(F26Dot6::to_bits), Point::new(186, -229));
1197 }
1198
1199 #[test]
1201 fn mdrp_round() {
1202 let mut mock = MockEngine::new();
1203 let mut engine = mock.engine();
1204 set_test_vectors(&mut engine);
1205 engine.graphics.backward_compatibility = false;
1206 engine.graphics.zp0 = ZonePointer::Glyph;
1207 engine.op_rthg().unwrap();
1208 engine.set_point_f26dot6(1, 1, (132, -231));
1210 engine.value_stack.push(1).unwrap();
1211 engine.op_mdrp(Opcode::MDRP00000 as _).unwrap();
1212 let point = engine.graphics.zones[1].point(1).unwrap();
1213 assert_eq!(point.map(F26Dot6::to_bits), Point::new(119, -238));
1214 engine.set_point_f26dot6(1, 2, (132, -231));
1216 engine.value_stack.push(2).unwrap();
1217 engine.op_mdrp(Opcode::MDRP00100 as _).unwrap();
1218 let point = engine.graphics.zones[1].point(2).unwrap();
1219 assert_eq!(point.map(F26Dot6::to_bits), Point::new(147, -223));
1220 }
1221
1222 #[test]
1225 fn mirp_rp0() {
1226 let mut mock = MockEngine::new();
1227 let mut engine = mock.engine();
1228 engine.graphics.rp0 = 0;
1229 engine.value_stack.push(1).unwrap();
1231 engine.value_stack.push(1).unwrap();
1232 engine.op_mirp(Opcode::MIRP00000 as _).unwrap();
1233 assert_eq!(engine.graphics.rp0, 0);
1234 engine.value_stack.push(1).unwrap();
1236 engine.value_stack.push(1).unwrap();
1237 engine.op_mirp(Opcode::MIRP10000 as _).unwrap();
1238 assert_eq!(engine.graphics.rp0, 1);
1239 }
1240
1241 #[test]
1244 fn mirp_mindist() {
1245 let mut mock = MockEngine::new();
1246 let mut engine = mock.engine();
1247 set_test_vectors(&mut engine);
1248 engine.graphics.backward_compatibility = false;
1249 engine.graphics.zp0 = ZonePointer::Glyph;
1250 engine.cvt.set(1, F26Dot6::from_f64(0.75)).unwrap();
1252 engine.set_point_f26dot6(1, 1, (132, -256));
1254 engine.value_stack.push(1).unwrap();
1255 engine.value_stack.push(1).unwrap();
1256 engine.op_mirp(Opcode::MIRP00000 as _).unwrap();
1257 let point = engine.graphics.zones[1].point(1).unwrap();
1258 assert_eq!(point.map(F26Dot6::to_bits), Point::new(171, -236));
1259 engine.set_point_f26dot6(1, 2, (132, -256));
1261 engine.value_stack.push(2).unwrap();
1262 engine.value_stack.push(1).unwrap();
1263 engine.op_mirp(Opcode::MIRP01000 as _).unwrap();
1264 let point = engine.graphics.zones[1].point(2).unwrap();
1265 assert_eq!(point.map(F26Dot6::to_bits), Point::new(186, -229));
1266 }
1267
1268 #[test]
1270 fn mirp_round() {
1271 let mut mock = MockEngine::new();
1272 let mut engine = mock.engine();
1273 set_test_vectors(&mut engine);
1274 engine.graphics.backward_compatibility = false;
1275 engine.graphics.zp0 = ZonePointer::Glyph;
1276 engine.cvt.set(1, F26Dot6::from_f64(0.75)).unwrap();
1278 engine.op_rthg().unwrap();
1279 engine.set_point_f26dot6(1, 1, (132, -231));
1281 engine.value_stack.push(1).unwrap();
1282 engine.value_stack.push(1).unwrap();
1283 engine.op_mirp(Opcode::MIRP00000 as _).unwrap();
1284 let point = engine.graphics.zones[1].point(1).unwrap();
1285 assert_eq!(point.map(F26Dot6::to_bits), Point::new(162, -216));
1286 engine.set_point_f26dot6(1, 2, (132, -231));
1288 engine.value_stack.push(2).unwrap();
1289 engine.value_stack.push(1).unwrap();
1290 engine.op_mirp(Opcode::MIRP00100 as _).unwrap();
1291 let point = engine.graphics.zones[1].point(2).unwrap();
1292 assert_eq!(point.map(F26Dot6::to_bits), Point::new(147, -223));
1293 }
1294
1295 #[test]
1296 fn alignrp() {
1297 let mut mock = MockEngine::new();
1298 let mut engine = mock.engine();
1299 set_test_vectors(&mut engine);
1300 engine.graphics.backward_compatibility = false;
1301 engine.graphics.zp0 = ZonePointer::Glyph;
1302 engine.graphics.zp1 = ZonePointer::Glyph;
1303 engine.graphics.rp0 = 0;
1304 engine.set_point_f26dot6(1, 0, (132, -231));
1305 engine.set_point_f26dot6(1, 1, (-72, 109));
1306 engine.value_stack.push(1).unwrap();
1307 engine.op_alignrp().unwrap();
1308 let point = engine.graphics.zones[1].point(1).unwrap();
1309 assert_eq!(point.map(F26Dot6::to_bits), Point::new(-45, 122));
1310 }
1311
1312 #[test]
1313 fn isect() {
1314 let mut mock = MockEngine::new();
1315 let mut engine = mock.engine();
1316 engine.graphics.zp0 = ZonePointer::Glyph;
1317 engine.graphics.zp1 = ZonePointer::Glyph;
1318 engine.graphics.rp0 = 0;
1319 engine.set_point_f26dot6(1, 0, (0, 0));
1321 engine.set_point_f26dot6(1, 1, (100, 100));
1322 engine.set_point_f26dot6(1, 2, (0, 100));
1324 engine.set_point_f26dot6(1, 3, (100, 0));
1325 for ix in [4, 0, 1, 2, 3] {
1328 engine.value_stack.push(ix).unwrap();
1329 }
1330 engine.op_isect().unwrap();
1331 let point = engine.graphics.zones[1].point(4).unwrap();
1332 assert_eq!(point.map(F26Dot6::to_bits), Point::new(50, 50));
1333 }
1334
1335 #[test]
1336 fn alignpts() {
1337 let mut mock = MockEngine::new();
1338 let mut engine = mock.engine();
1339 set_test_vectors(&mut engine);
1340 engine.graphics.backward_compatibility = false;
1341 engine.graphics.zp0 = ZonePointer::Glyph;
1342 engine.graphics.zp1 = ZonePointer::Glyph;
1343 engine.set_point_f26dot6(1, 0, (132, -231));
1344 engine.set_point_f26dot6(1, 1, (-72, 109));
1345 engine.value_stack.push(0).unwrap();
1346 engine.value_stack.push(1).unwrap();
1347 engine.op_alignpts().unwrap();
1348 let p1 = engine.graphics.zones[1].point(0).unwrap();
1349 let p2 = engine.graphics.zones[1].point(1).unwrap();
1350 assert_eq!(p1.map(F26Dot6::to_bits), Point::new(119, -238));
1351 assert_eq!(p2.map(F26Dot6::to_bits), Point::new(-59, 116));
1352 }
1353
1354 #[test]
1355 fn ip() {
1356 let mut mock = MockEngine::new();
1357 let mut engine = mock.engine();
1358 set_test_vectors(&mut engine);
1359 engine.graphics.backward_compatibility = false;
1360 engine.graphics.zp0 = ZonePointer::Glyph;
1361 engine.graphics.zp1 = ZonePointer::Glyph;
1362 engine.graphics.zp2 = ZonePointer::Glyph;
1363 engine.graphics.rp1 = 2;
1364 engine.graphics.rp2 = 3;
1365 engine.set_point_f26dot6(1, 2, (72, -109));
1366 engine.set_point_f26dot6(1, 1, (132, -231));
1367 engine.value_stack.push(1).unwrap();
1368 engine.op_ip().unwrap();
1369 let point = engine.graphics.zones[1].point(1).unwrap();
1370 assert_eq!(point.map(F26Dot6::to_bits), Point::new(147, -223));
1371 }
1372
1373 #[test]
1374 fn iup_flags() {
1375 let mut mock = MockEngine::new();
1378 let mut engine = mock.engine();
1379 assert!(!engine.graphics.did_iup_x);
1380 assert!(!engine.graphics.did_iup_y);
1381 engine.op_iup(0).unwrap();
1383 assert!(!engine.graphics.did_iup_x);
1384 assert!(engine.graphics.did_iup_y);
1385 engine.op_iup(1).unwrap();
1387 assert!(engine.graphics.did_iup_x);
1388 assert!(engine.graphics.did_iup_y);
1389 }
1390
1391 #[test]
1394 fn flip_region_avoid_overflow() {
1395 let mut mock = MockEngine::new();
1396 let mut engine = mock.engine();
1397 engine.value_stack.push(1).unwrap();
1398 engine.value_stack.push(-1).unwrap();
1399 let _ = engine.set_on_curve_for_range(true);
1401 }
1402
1403 fn set_test_vectors(engine: &mut Engine) {
1404 let v = math::normalize14(100, 50);
1405 engine.graphics.proj_vector = v;
1406 engine.graphics.dual_proj_vector = v;
1407 engine.graphics.freedom_vector = v;
1408 engine.graphics.update_projection_state();
1409 }
1410
1411 impl Engine<'_> {
1412 fn set_point_f26dot6(&mut self, zone_ix: usize, point_ix: usize, xy: (i32, i32)) {
1413 let p = self.graphics.zones[zone_ix].point_mut(point_ix).unwrap();
1414 p.x = F26Dot6::from_bits(xy.0);
1415 p.y = F26Dot6::from_bits(xy.1);
1416 }
1417 }
1418}