skrifa/outline/glyf/hint/engine/
round.rs

1//! Compensating for the engine characteristics (rounding).
2//!
3//! Implements 4 instructions.
4//!
5//! See <https://learn.microsoft.com/en-us/typography/opentype/spec/tt_instructions#compensating-for-the-engine-characteristics>
6
7use super::{Engine, OpResult};
8
9impl Engine<'_> {
10    /// Round value.
11    ///
12    /// ROUND\[ab\] (0x68 - 0x6B)
13    ///
14    /// Pops: n1
15    /// Pushes: n2
16    ///
17    /// Rounds a value according to the state variable round_state while
18    /// compensating for the engine. n1 is popped off the stack and,
19    /// depending on the engine characteristics, is increased or decreased
20    /// by a set amount. The number obtained is then rounded and pushed
21    /// back onto the stack as n2.
22    ///
23    /// See <https://learn.microsoft.com/en-us/typography/opentype/spec/tt_instructions#round-value>
24    /// and <https://gitlab.freedesktop.org/freetype/freetype/-/blob/57617782464411201ce7bbc93b086c1b4d7d84a5/src/truetype/ttinterp.c#L3143>
25    pub(super) fn op_round(&mut self) -> OpResult {
26        let n1 = self.value_stack.pop_f26dot6()?;
27        let n2 = self.graphics.round(n1);
28        self.value_stack.push(n2.to_bits())
29    }
30}
31
32#[cfg(test)]
33mod tests {
34    use super::super::{super::round::RoundMode, MockEngine};
35
36    #[test]
37    fn round() {
38        let mut mock = MockEngine::new();
39        let mut engine = mock.engine();
40        use RoundMode::*;
41        let cases = [
42            (Grid, &[(0, 0), (32, 64), (-32, -64), (64, 64), (50, 64)]),
43            (
44                HalfGrid,
45                &[(0, 32), (32, 32), (-32, -32), (64, 96), (50, 32)],
46            ),
47            (
48                DoubleGrid,
49                &[(0, 0), (32, 32), (-32, -32), (64, 64), (50, 64)],
50            ),
51            (DownToGrid, &[(0, 0), (32, 0), (-32, 0), (64, 64), (50, 0)]),
52            (
53                UpToGrid,
54                &[(0, 0), (32, 64), (-32, -64), (64, 64), (50, 64)],
55            ),
56            (Off, &[(0, 0), (32, 32), (-32, -32), (64, 64), (50, 50)]),
57        ];
58        for (mode, values) in cases {
59            match mode {
60                Grid => engine.op_rtg().unwrap(),
61                HalfGrid => engine.op_rthg().unwrap(),
62                DoubleGrid => engine.op_rtdg().unwrap(),
63                DownToGrid => engine.op_rdtg().unwrap(),
64                UpToGrid => engine.op_rutg().unwrap(),
65                Off => engine.op_roff().unwrap(),
66                _ => unreachable!(),
67            }
68            for (input, expected) in values {
69                engine.value_stack.push(*input).unwrap();
70                engine.op_round().unwrap();
71                let result = engine.value_stack.pop().unwrap();
72                assert_eq!(*expected, result);
73            }
74        }
75    }
76}