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()?.wrapping_add(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.wrapping_sub(pb0.x);
630 let dby = pb1.y.wrapping_sub(pb0.y);
631 let dax = pa1.x.wrapping_sub(pa0.x);
632 let day = pa1.y.wrapping_sub(pa0.y);
633 let dx = pb0.x.wrapping_sub(pa0.x);
634 let dy = pb0.y.wrapping_sub(pa0.y);
635 use math::mul_div;
636 let discriminant = mul_div(dax, -dby, 0x40).wrapping_add(mul_div(day, dbx, 0x40));
637 let dotproduct = mul_div(dax, dbx, 0x40).wrapping_add(mul_div(day, dby, 0x40));
638 if discriminant.wrapping_abs().wrapping_mul(19) > dotproduct.wrapping_abs() {
651 let v = mul_div(dx, -dby, 0x40).wrapping_add(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.wrapping_add(x));
656 point.y = F26Dot6::from_bits(pa0.y.wrapping_add(y));
657 } else {
658 let point = gs.zp2_mut().point_mut(point_ix)?;
659 point.x = F26Dot6::from_bits(
660 (pa0.x
661 .wrapping_add(pa1.x)
662 .wrapping_add(pb0.x)
663 .wrapping_add(pb1.x))
664 / 4,
665 );
666 point.y = F26Dot6::from_bits(
667 (pa0.y
668 .wrapping_add(pa1.y)
669 .wrapping_add(pb0.y)
670 .wrapping_add(pb1.y))
671 / 4,
672 );
673 }
674 gs.zp2_mut().touch(point_ix, CoordAxis::Both)?;
675 Ok(())
676 }
677
678 pub(super) fn op_alignpts(&mut self) -> OpResult {
692 let p2 = self.value_stack.pop_usize()?;
693 let p1 = self.value_stack.pop_usize()?;
694 let gs = &mut self.graphics;
695 let distance = F26Dot6::from_bits(
696 gs.project(gs.zp0().point(p2)?, gs.zp1().point(p1)?)
697 .to_bits()
698 / 2,
699 );
700 gs.move_point(gs.zp1, p1, distance)?;
701 gs.move_point(gs.zp0, p2, -distance)?;
702 Ok(())
703 }
704
705 pub(super) fn op_ip(&mut self) -> OpResult {
723 let gs = &mut self.graphics;
724 let count = gs.loop_counter;
725 gs.loop_counter = 1;
726 if !gs.is_pedantic && !gs.in_bounds([(gs.zp0, gs.rp1), (gs.zp1, gs.rp2)]) {
727 return Ok(());
728 }
729 let in_twilight = gs.zp0.is_twilight() || gs.zp1.is_twilight() || gs.zp2.is_twilight();
730 let orus_base = if in_twilight {
731 gs.zp0().original(gs.rp1)?
732 } else {
733 gs.zp0().unscaled(gs.rp1).map(F26Dot6::from_bits)
734 };
735 let cur_base = gs.zp0().point(gs.rp1)?;
736 let old_range = if in_twilight {
737 gs.dual_project(gs.zp1().original(gs.rp2)?, orus_base)
738 } else {
739 gs.dual_project(gs.zp1().unscaled(gs.rp2).map(F26Dot6::from_bits), orus_base)
740 };
741 let cur_range = gs.project(gs.zp1().point(gs.rp2)?, cur_base);
742 for _ in 0..count {
743 let point = self.value_stack.pop_usize()?;
744 if !gs.is_pedantic && !gs.in_bounds([(gs.zp2, point)]) {
745 continue;
746 }
747 let original_distance = if in_twilight {
748 gs.dual_project(gs.zp2().original(point)?, orus_base)
749 } else {
750 gs.dual_project(gs.zp2().unscaled(point).map(F26Dot6::from_bits), orus_base)
751 };
752 let cur_distance = gs.project(gs.zp2().point(point)?, cur_base);
753 let new_distance = if original_distance != F26Dot6::ZERO {
754 if old_range != F26Dot6::ZERO {
755 F26Dot6::from_bits(math::mul_div(
756 original_distance.to_bits(),
757 cur_range.to_bits(),
758 old_range.to_bits(),
759 ))
760 } else {
761 original_distance
762 }
763 } else {
764 F26Dot6::ZERO
765 };
766 gs.move_point(gs.zp2, point, new_distance.wrapping_sub(cur_distance))?;
767 }
768 Ok(())
769 }
770
771 pub(super) fn op_iup(&mut self, opcode: u8) -> OpResult {
788 let gs = &mut self.graphics;
789 let axis = if (opcode & 1) != 0 {
790 CoordAxis::X
791 } else {
792 CoordAxis::Y
793 };
794 let mut run = true;
795 if gs.backward_compatibility {
798 if gs.did_iup_x && gs.did_iup_y {
799 run = false;
800 }
801 if axis == CoordAxis::X {
802 gs.did_iup_x = true;
803 } else {
804 gs.did_iup_y = true;
805 }
806 }
807 if run {
808 gs.zone_mut(ZonePointer::Glyph).iup(axis)?;
809 }
810 Ok(())
811 }
812
813 pub(super) fn op_utp(&mut self) -> OpResult {
830 let p = self.value_stack.pop_usize()?;
831 let coord_axis = match (
832 self.graphics.freedom_vector.x != 0,
833 self.graphics.freedom_vector.y != 0,
834 ) {
835 (true, true) => Some(CoordAxis::Both),
836 (true, false) => Some(CoordAxis::X),
837 (false, true) => Some(CoordAxis::Y),
838 (false, false) => None,
839 };
840 if let Some(coord_axis) = coord_axis {
841 self.graphics.zp0_mut().untouch(p, coord_axis)?;
842 }
843 Ok(())
844 }
845
846 fn set_on_curve_for_range(&mut self, on: bool) -> OpResult {
848 let high_point = self.value_stack.pop_usize()?;
849 let low_point = self.value_stack.pop_usize()?;
850 let high_point = high_point
853 .checked_add(1)
854 .ok_or(HintErrorKind::InvalidPointIndex(high_point))?;
855 if self.graphics.backward_compatibility
858 && self.graphics.did_iup_x
859 && self.graphics.did_iup_y
860 {
861 return Ok(());
862 }
863 self.graphics
864 .zone_mut(ZonePointer::Glyph)
865 .set_on_curve(low_point, high_point, on)
866 }
867}
868
869#[cfg(test)]
870mod tests {
871 use super::{super::MockEngine, math, CoordAxis, Engine, ZonePointer};
872 use raw::{
873 tables::glyf::{bytecode::Opcode, PointMarker},
874 types::{F26Dot6, Point},
875 };
876
877 #[test]
878 fn flip_point() {
879 let mut mock = MockEngine::new();
880 let mut engine = mock.engine();
881 let count = 5;
884 engine.value_stack.push(count).unwrap();
886 engine.op_sloop().unwrap();
887 for i in (1..=9).step_by(2) {
889 engine.value_stack.push(i).unwrap();
890 }
891 assert_eq!(engine.value_stack.len(), count as usize);
892 engine.op_flippt().unwrap();
894 let flags = &engine.graphics.zones[1].flags;
895 for i in 0..10 {
896 assert_eq!(flags[i].is_on_curve(), i & 1 != 0);
898 }
899 }
900
901 #[test]
903 fn state_prevents_flip_point() {
904 let mut mock = MockEngine::new();
905 let mut engine = mock.engine();
906 let count = 5;
909 engine.value_stack.push(count).unwrap();
911 engine.op_sloop().unwrap();
912 for i in (1..=9).step_by(2) {
914 engine.value_stack.push(i).unwrap();
915 }
916 assert_eq!(engine.value_stack.len(), count as usize);
917 engine.graphics.backward_compatibility = true;
919 engine.graphics.did_iup_x = true;
920 engine.graphics.did_iup_y = true;
921 engine.op_flippt().unwrap();
923 let flags = &engine.graphics.zones[1].flags;
924 for i in 0..10 {
925 assert!(!flags[i].is_on_curve());
927 }
928 }
929
930 #[test]
931 fn flip_range_on_off() {
932 let mut mock = MockEngine::new();
933 let mut engine = mock.engine();
934 engine.value_stack.push(10).unwrap();
937 engine.value_stack.push(20).unwrap();
938 engine.op_fliprgon().unwrap();
939 for (i, flag) in engine.graphics.zones[1].flags.iter().enumerate() {
940 assert_eq!(flag.is_on_curve(), (10..=20).contains(&i));
941 }
942 engine.value_stack.push(12).unwrap();
944 engine.value_stack.push(15).unwrap();
945 engine.op_fliprgoff().unwrap();
946 for (i, flag) in engine.graphics.zones[1].flags.iter().enumerate() {
947 assert_eq!(
948 flag.is_on_curve(),
949 (10..=11).contains(&i) || (16..=20).contains(&i)
950 );
951 }
952 }
953
954 #[test]
956 fn state_prevents_flip_range_on_off() {
957 let mut mock = MockEngine::new();
958 let mut engine = mock.engine();
959 engine.graphics.backward_compatibility = true;
961 engine.graphics.did_iup_x = true;
962 engine.graphics.did_iup_y = true;
963 engine.value_stack.push(10).unwrap();
966 engine.value_stack.push(20).unwrap();
967 engine.op_fliprgon().unwrap();
968 for flag in engine.graphics.zones[1].flags.iter() {
969 assert!(!flag.is_on_curve());
970 }
971 for flag in engine.graphics.zones[1].flags.iter_mut() {
973 flag.set_on_curve();
974 }
975 engine.value_stack.push(12).unwrap();
977 engine.value_stack.push(15).unwrap();
978 engine.op_fliprgoff().unwrap();
979 for flag in engine.graphics.zones[1].flags.iter() {
980 assert!(flag.is_on_curve());
981 }
982 }
983
984 #[test]
985 fn untouch_point() {
986 let mut mock = MockEngine::new();
987 let mut engine = mock.engine();
988 let count = engine.graphics.zones[1].points.len();
990 for i in 0..count {
991 engine.graphics.zones[1].touch(i, CoordAxis::Both).unwrap();
992 }
993 let mut untouch = |point_ix: usize, fx, fy, marker| {
994 assert!(engine.graphics.zp0().flags[point_ix].has_marker(marker));
995 engine.graphics.freedom_vector.x = fx;
997 engine.graphics.freedom_vector.y = fy;
998 engine.value_stack.push(point_ix as i32).unwrap();
999 engine.op_utp().unwrap();
1000 assert!(!engine.graphics.zp0().flags[point_ix].has_marker(marker));
1001 };
1002 untouch(0, 1, 0, PointMarker::TOUCHED_X);
1004 untouch(1, 0, 1, PointMarker::TOUCHED_Y);
1006 untouch(2, 1, 1, PointMarker::TOUCHED);
1008 }
1009
1010 #[test]
1011 fn shp() {
1012 let mut mock = MockEngine::new();
1013 let mut engine = mock.engine();
1014 set_test_vectors(&mut engine);
1015 engine.graphics.backward_compatibility = false;
1016 engine.graphics.zp0 = ZonePointer::Glyph;
1017 engine.graphics.zp2 = ZonePointer::Glyph;
1018 engine.graphics.rp2 = 1;
1019 let point = engine.graphics.zones[1].point_mut(1).unwrap();
1020 point.x = F26Dot6::from_bits(132);
1021 point.y = F26Dot6::from_bits(-256);
1022 engine.value_stack.push(1).unwrap();
1023 engine.op_shp(0).unwrap();
1024 let point = engine.graphics.zones[1].point(1).unwrap();
1025 assert_eq!(point.map(F26Dot6::to_bits), Point::new(136, -254));
1026 }
1027
1028 #[test]
1029 fn shc() {
1030 let mut mock = MockEngine::new();
1031 let mut engine = mock.engine();
1032 set_test_vectors(&mut engine);
1033 engine.graphics.backward_compatibility = false;
1034 engine.graphics.zp0 = ZonePointer::Glyph;
1035 engine.graphics.zp2 = ZonePointer::Glyph;
1036 engine.graphics.rp2 = 1;
1037 let point = engine.graphics.zones[1].point_mut(1).unwrap();
1038 point.x = F26Dot6::from_bits(132);
1039 point.y = F26Dot6::from_bits(-256);
1040 engine.value_stack.push(0).unwrap();
1041 engine.op_shc(0).unwrap();
1042 let points = engine.graphics.zones[1]
1043 .points
1044 .iter()
1045 .map(|p| p.map(F26Dot6::to_bits))
1046 .take(3)
1047 .collect::<Vec<_>>();
1048 assert_eq!(
1049 points,
1050 &[Point::new(4, 2), Point::new(132, -256), Point::new(4, 2),]
1051 );
1052 }
1053
1054 #[test]
1055 fn shz() {
1056 let mut mock = MockEngine::new();
1057 let mut engine = mock.engine();
1058 set_test_vectors(&mut engine);
1059 engine.graphics.backward_compatibility = false;
1060 engine.graphics.zp0 = ZonePointer::Glyph;
1061 engine.graphics.zp2 = ZonePointer::Glyph;
1062 engine.graphics.rp2 = 1;
1063 let point = engine.graphics.zones[1].point_mut(1).unwrap();
1064 point.x = F26Dot6::from_bits(132);
1065 point.y = F26Dot6::from_bits(-256);
1066 engine.value_stack.push(0).unwrap();
1067 engine.op_shz(0).unwrap();
1068 let points = engine.graphics.zones[1]
1069 .points
1070 .iter()
1071 .map(|p| p.map(F26Dot6::to_bits))
1072 .take(3)
1073 .collect::<Vec<_>>();
1074 assert_eq!(
1075 points,
1076 &[Point::new(4, 2), Point::new(132, -256), Point::new(4, 2),]
1077 );
1078 }
1079
1080 #[test]
1081 fn shpix() {
1082 let mut mock = MockEngine::new();
1083 let mut engine = mock.engine();
1084 set_test_vectors(&mut engine);
1085 engine.graphics.backward_compatibility = false;
1086 engine.graphics.zp2 = ZonePointer::Glyph;
1087 let point = engine.graphics.zones[1].point_mut(1).unwrap();
1088 point.x = F26Dot6::from_bits(132);
1089 point.y = F26Dot6::from_bits(-256);
1090 engine.value_stack.push(1).unwrap();
1092 engine.value_stack.push(42).unwrap();
1094 engine.op_shpix().unwrap();
1095 let point = engine.graphics.zones[1].point(1).unwrap();
1096 assert_eq!(point.map(F26Dot6::to_bits), Point::new(170, -237));
1097 }
1098
1099 #[test]
1100 fn msirp() {
1101 let mut mock = MockEngine::new();
1102 let mut engine = mock.engine();
1103 set_test_vectors(&mut engine);
1104 engine.graphics.backward_compatibility = false;
1105 engine.graphics.zp0 = ZonePointer::Glyph;
1106 engine.graphics.zp1 = ZonePointer::Glyph;
1107 let point = engine.graphics.zones[1].point_mut(1).unwrap();
1108 point.x = F26Dot6::from_bits(132);
1109 point.y = F26Dot6::from_bits(-256);
1110 engine.value_stack.push(1).unwrap();
1112 engine.value_stack.push(-42).unwrap();
1114 engine.op_msirp(0).unwrap();
1115 let point = engine.graphics.zones[1].point(1).unwrap();
1116 assert_eq!(point.map(F26Dot6::to_bits), Point::new(91, -277));
1117 assert_eq!(engine.graphics.rp0, 0);
1118 engine.value_stack.push(4).unwrap();
1120 engine.value_stack.push(0).unwrap();
1121 engine.op_msirp(1).unwrap();
1122 assert_eq!(engine.graphics.rp0, 4);
1123 }
1124
1125 #[test]
1126 fn mdap() {
1127 let mut mock = MockEngine::new();
1128 let mut engine = mock.engine();
1129 set_test_vectors(&mut engine);
1130 engine.graphics.backward_compatibility = false;
1131 engine.graphics.zp0 = ZonePointer::Glyph;
1132 engine.set_point_f26dot6(1, 1, (132, -256));
1134 engine.value_stack.push(1).unwrap();
1135 engine.op_mdap(1).unwrap();
1136 let point = engine.graphics.zones[1].point(1).unwrap();
1137 assert_eq!(point.map(F26Dot6::to_bits), Point::new(128, -258));
1138 engine.set_point_f26dot6(1, 2, (132, -256));
1140 engine.value_stack.push(2).unwrap();
1141 engine.op_mdap(0).unwrap();
1142 let point = engine.graphics.zones[1].point(2).unwrap();
1143 assert_eq!(point.map(F26Dot6::to_bits), Point::new(132, -256));
1144 }
1145
1146 #[test]
1147 fn miap() {
1148 let mut mock = MockEngine::new();
1149 let mut engine = mock.engine();
1150 set_test_vectors(&mut engine);
1151 engine.graphics.backward_compatibility = false;
1152 engine.graphics.zp0 = ZonePointer::Glyph;
1153 engine.cvt.set(1, F26Dot6::from_f64(0.75)).unwrap();
1155 engine.set_point_f26dot6(1, 1, (132, -256));
1157 engine.value_stack.push(1).unwrap();
1158 engine.value_stack.push(1).unwrap();
1159 engine.op_miap(1).unwrap();
1160 let point = engine.graphics.zones[1].point(1).unwrap();
1161 assert_eq!(point.map(F26Dot6::to_bits), Point::new(186, -229));
1162 engine.set_point_f26dot6(1, 2, (132, -256));
1164 engine.value_stack.push(2).unwrap();
1165 engine.value_stack.push(1).unwrap();
1166 engine.op_miap(0).unwrap();
1167 let point = engine.graphics.zones[1].point(2).unwrap();
1168 assert_eq!(point.map(F26Dot6::to_bits), Point::new(171, -236));
1169 }
1170
1171 #[test]
1174 fn mdrp_rp0() {
1175 let mut mock = MockEngine::new();
1176 let mut engine = mock.engine();
1177 engine.graphics.rp0 = 0;
1178 engine.value_stack.push(1).unwrap();
1180 engine.op_mdrp(Opcode::MDRP00000 as _).unwrap();
1181 assert_eq!(engine.graphics.rp0, 0);
1182 engine.value_stack.push(1).unwrap();
1184 engine.op_mdrp(Opcode::MDRP10000 as _).unwrap();
1185 assert_eq!(engine.graphics.rp0, 1);
1186 }
1187
1188 #[test]
1191 fn mdrp_mindist() {
1192 let mut mock = MockEngine::new();
1193 let mut engine = mock.engine();
1194 set_test_vectors(&mut engine);
1195 engine.graphics.backward_compatibility = false;
1196 engine.graphics.zp0 = ZonePointer::Glyph;
1197 engine.set_point_f26dot6(1, 1, (132, -256));
1199 engine.value_stack.push(1).unwrap();
1200 engine.op_mdrp(Opcode::MDRP00000 as _).unwrap();
1201 let point = engine.graphics.zones[1].point(1).unwrap();
1202 assert_eq!(point.map(F26Dot6::to_bits), Point::new(128, -258));
1203 engine.set_point_f26dot6(1, 2, (132, -256));
1205 engine.value_stack.push(2).unwrap();
1206 engine.op_mdrp(Opcode::MDRP01000 as _).unwrap();
1207 let point = engine.graphics.zones[1].point(2).unwrap();
1208 assert_eq!(point.map(F26Dot6::to_bits), Point::new(186, -229));
1209 }
1210
1211 #[test]
1213 fn mdrp_round() {
1214 let mut mock = MockEngine::new();
1215 let mut engine = mock.engine();
1216 set_test_vectors(&mut engine);
1217 engine.graphics.backward_compatibility = false;
1218 engine.graphics.zp0 = ZonePointer::Glyph;
1219 engine.op_rthg().unwrap();
1220 engine.set_point_f26dot6(1, 1, (132, -231));
1222 engine.value_stack.push(1).unwrap();
1223 engine.op_mdrp(Opcode::MDRP00000 as _).unwrap();
1224 let point = engine.graphics.zones[1].point(1).unwrap();
1225 assert_eq!(point.map(F26Dot6::to_bits), Point::new(119, -238));
1226 engine.set_point_f26dot6(1, 2, (132, -231));
1228 engine.value_stack.push(2).unwrap();
1229 engine.op_mdrp(Opcode::MDRP00100 as _).unwrap();
1230 let point = engine.graphics.zones[1].point(2).unwrap();
1231 assert_eq!(point.map(F26Dot6::to_bits), Point::new(147, -223));
1232 }
1233
1234 #[test]
1237 fn mirp_rp0() {
1238 let mut mock = MockEngine::new();
1239 let mut engine = mock.engine();
1240 engine.graphics.rp0 = 0;
1241 engine.value_stack.push(1).unwrap();
1243 engine.value_stack.push(1).unwrap();
1244 engine.op_mirp(Opcode::MIRP00000 as _).unwrap();
1245 assert_eq!(engine.graphics.rp0, 0);
1246 engine.value_stack.push(1).unwrap();
1248 engine.value_stack.push(1).unwrap();
1249 engine.op_mirp(Opcode::MIRP10000 as _).unwrap();
1250 assert_eq!(engine.graphics.rp0, 1);
1251 }
1252
1253 #[test]
1256 fn mirp_mindist() {
1257 let mut mock = MockEngine::new();
1258 let mut engine = mock.engine();
1259 set_test_vectors(&mut engine);
1260 engine.graphics.backward_compatibility = false;
1261 engine.graphics.zp0 = ZonePointer::Glyph;
1262 engine.cvt.set(1, F26Dot6::from_f64(0.75)).unwrap();
1264 engine.set_point_f26dot6(1, 1, (132, -256));
1266 engine.value_stack.push(1).unwrap();
1267 engine.value_stack.push(1).unwrap();
1268 engine.op_mirp(Opcode::MIRP00000 as _).unwrap();
1269 let point = engine.graphics.zones[1].point(1).unwrap();
1270 assert_eq!(point.map(F26Dot6::to_bits), Point::new(171, -236));
1271 engine.set_point_f26dot6(1, 2, (132, -256));
1273 engine.value_stack.push(2).unwrap();
1274 engine.value_stack.push(1).unwrap();
1275 engine.op_mirp(Opcode::MIRP01000 as _).unwrap();
1276 let point = engine.graphics.zones[1].point(2).unwrap();
1277 assert_eq!(point.map(F26Dot6::to_bits), Point::new(186, -229));
1278 }
1279
1280 #[test]
1282 fn mirp_round() {
1283 let mut mock = MockEngine::new();
1284 let mut engine = mock.engine();
1285 set_test_vectors(&mut engine);
1286 engine.graphics.backward_compatibility = false;
1287 engine.graphics.zp0 = ZonePointer::Glyph;
1288 engine.cvt.set(1, F26Dot6::from_f64(0.75)).unwrap();
1290 engine.op_rthg().unwrap();
1291 engine.set_point_f26dot6(1, 1, (132, -231));
1293 engine.value_stack.push(1).unwrap();
1294 engine.value_stack.push(1).unwrap();
1295 engine.op_mirp(Opcode::MIRP00000 as _).unwrap();
1296 let point = engine.graphics.zones[1].point(1).unwrap();
1297 assert_eq!(point.map(F26Dot6::to_bits), Point::new(162, -216));
1298 engine.set_point_f26dot6(1, 2, (132, -231));
1300 engine.value_stack.push(2).unwrap();
1301 engine.value_stack.push(1).unwrap();
1302 engine.op_mirp(Opcode::MIRP00100 as _).unwrap();
1303 let point = engine.graphics.zones[1].point(2).unwrap();
1304 assert_eq!(point.map(F26Dot6::to_bits), Point::new(147, -223));
1305 }
1306
1307 #[test]
1308 fn mirp_does_not_panic_with_overflow() {
1309 let mut mock = MockEngine::new();
1310 let mut engine = mock.engine();
1311 engine.value_stack.push(i32::MAX).unwrap();
1312 engine.op_mirp(Opcode::MIRP10000 as _).unwrap();
1314 }
1315
1316 #[test]
1317 fn alignrp() {
1318 let mut mock = MockEngine::new();
1319 let mut engine = mock.engine();
1320 set_test_vectors(&mut engine);
1321 engine.graphics.backward_compatibility = false;
1322 engine.graphics.zp0 = ZonePointer::Glyph;
1323 engine.graphics.zp1 = ZonePointer::Glyph;
1324 engine.graphics.rp0 = 0;
1325 engine.set_point_f26dot6(1, 0, (132, -231));
1326 engine.set_point_f26dot6(1, 1, (-72, 109));
1327 engine.value_stack.push(1).unwrap();
1328 engine.op_alignrp().unwrap();
1329 let point = engine.graphics.zones[1].point(1).unwrap();
1330 assert_eq!(point.map(F26Dot6::to_bits), Point::new(-45, 122));
1331 }
1332
1333 #[test]
1334 fn isect() {
1335 let mut mock = MockEngine::new();
1336 let mut engine = mock.engine();
1337 engine.graphics.zp0 = ZonePointer::Glyph;
1338 engine.graphics.zp1 = ZonePointer::Glyph;
1339 engine.graphics.rp0 = 0;
1340 engine.set_point_f26dot6(1, 0, (0, 0));
1342 engine.set_point_f26dot6(1, 1, (100, 100));
1343 engine.set_point_f26dot6(1, 2, (0, 100));
1345 engine.set_point_f26dot6(1, 3, (100, 0));
1346 for ix in [4, 0, 1, 2, 3] {
1349 engine.value_stack.push(ix).unwrap();
1350 }
1351 engine.op_isect().unwrap();
1352 let point = engine.graphics.zones[1].point(4).unwrap();
1353 assert_eq!(point.map(F26Dot6::to_bits), Point::new(50, 50));
1354 }
1355
1356 #[test]
1357 fn isect_does_not_panic_with_extreme_coords() {
1358 let mut mock = MockEngine::new();
1359 let mut engine = mock.engine();
1360 engine.graphics.zp0 = ZonePointer::Glyph;
1361 engine.graphics.zp1 = ZonePointer::Glyph;
1362 engine.graphics.zp2 = ZonePointer::Glyph;
1363 engine.set_point_f26dot6(1, 0, (i32::MIN, i32::MAX));
1365 engine.set_point_f26dot6(1, 1, (i32::MAX, i32::MIN));
1366 engine.set_point_f26dot6(1, 2, (i32::MAX, i32::MAX));
1367 engine.set_point_f26dot6(1, 3, (i32::MIN, i32::MIN));
1368 for ix in [4, 0, 1, 2, 3] {
1369 engine.value_stack.push(ix).unwrap();
1370 }
1371 engine.op_isect().unwrap();
1373 }
1374
1375 #[test]
1376 fn alignpts() {
1377 let mut mock = MockEngine::new();
1378 let mut engine = mock.engine();
1379 set_test_vectors(&mut engine);
1380 engine.graphics.backward_compatibility = false;
1381 engine.graphics.zp0 = ZonePointer::Glyph;
1382 engine.graphics.zp1 = ZonePointer::Glyph;
1383 engine.set_point_f26dot6(1, 0, (132, -231));
1384 engine.set_point_f26dot6(1, 1, (-72, 109));
1385 engine.value_stack.push(0).unwrap();
1386 engine.value_stack.push(1).unwrap();
1387 engine.op_alignpts().unwrap();
1388 let p1 = engine.graphics.zones[1].point(0).unwrap();
1389 let p2 = engine.graphics.zones[1].point(1).unwrap();
1390 assert_eq!(p1.map(F26Dot6::to_bits), Point::new(119, -238));
1391 assert_eq!(p2.map(F26Dot6::to_bits), Point::new(-59, 116));
1392 }
1393
1394 #[test]
1395 fn ip() {
1396 let mut mock = MockEngine::new();
1397 let mut engine = mock.engine();
1398 set_test_vectors(&mut engine);
1399 engine.graphics.backward_compatibility = false;
1400 engine.graphics.zp0 = ZonePointer::Glyph;
1401 engine.graphics.zp1 = ZonePointer::Glyph;
1402 engine.graphics.zp2 = ZonePointer::Glyph;
1403 engine.graphics.rp1 = 2;
1404 engine.graphics.rp2 = 3;
1405 engine.set_point_f26dot6(1, 2, (72, -109));
1406 engine.set_point_f26dot6(1, 1, (132, -231));
1407 engine.value_stack.push(1).unwrap();
1408 engine.op_ip().unwrap();
1409 let point = engine.graphics.zones[1].point(1).unwrap();
1410 assert_eq!(point.map(F26Dot6::to_bits), Point::new(147, -223));
1411 }
1412
1413 #[test]
1414 fn iup_flags() {
1415 let mut mock = MockEngine::new();
1418 let mut engine = mock.engine();
1419 assert!(!engine.graphics.did_iup_x);
1420 assert!(!engine.graphics.did_iup_y);
1421 engine.op_iup(0).unwrap();
1423 assert!(!engine.graphics.did_iup_x);
1424 assert!(engine.graphics.did_iup_y);
1425 engine.op_iup(1).unwrap();
1427 assert!(engine.graphics.did_iup_x);
1428 assert!(engine.graphics.did_iup_y);
1429 }
1430
1431 #[test]
1434 fn flip_region_avoid_overflow() {
1435 let mut mock = MockEngine::new();
1436 let mut engine = mock.engine();
1437 engine.value_stack.push(1).unwrap();
1438 engine.value_stack.push(-1).unwrap();
1439 let _ = engine.set_on_curve_for_range(true);
1441 }
1442
1443 fn set_test_vectors(engine: &mut Engine) {
1444 let v = math::normalize14(100, 50);
1445 engine.graphics.proj_vector = v;
1446 engine.graphics.dual_proj_vector = v;
1447 engine.graphics.freedom_vector = v;
1448 engine.graphics.update_projection_state();
1449 }
1450
1451 impl Engine<'_> {
1452 fn set_point_f26dot6(&mut self, zone_ix: usize, point_ix: usize, xy: (i32, i32)) {
1453 let p = self.graphics.zones[zone_ix].point_mut(point_ix).unwrap();
1454 p.x = F26Dot6::from_bits(xy.0);
1455 p.y = F26Dot6::from_bits(xy.1);
1456 }
1457 }
1458}