1 //! Arithmetic and math instructions.
2 //!
3 //! Implements 10 instructions.
4 //!
5 //! See <https://learn.microsoft.com/en-us/typography/opentype/spec/tt_instructions#arithmetic-and-math-instructions>
6 
7 use super::{super::math, Engine, HintErrorKind, OpResult};
8 
9 impl<'a> Engine<'a> {
10     /// ADD[] (0x60)
11     ///
12     /// Pops: n1, n2 (F26Dot6)
13     /// Pushes: (n2 + n1)
14     ///
15     /// Pops n1 and n2 off the stack and pushes the sum of the two elements
16     /// onto the stack.
17     ///
18     /// See <https://learn.microsoft.com/en-us/typography/opentype/spec/tt_instructions#add>
19     /// and <https://gitlab.freedesktop.org/freetype/freetype/-/blob/57617782464411201ce7bbc93b086c1b4d7d84a5/src/truetype/ttinterp.c#L2866>
op_add(&mut self) -> OpResult20     pub(super) fn op_add(&mut self) -> OpResult {
21         self.value_stack.apply_binary(|a, b| Ok(a.wrapping_add(b)))
22     }
23 
24     /// SUB[] (0x61)
25     ///
26     /// Pops: n1, n2 (F26Dot6)
27     /// Pushes: (n2 - n1)
28     ///
29     /// Pops n1 and n2 off the stack and pushes the difference of the two
30     /// elements onto the stack.
31     ///
32     /// See <https://learn.microsoft.com/en-us/typography/opentype/spec/tt_instructions#subtract>
33     /// and <https://gitlab.freedesktop.org/freetype/freetype/-/blob/57617782464411201ce7bbc93b086c1b4d7d84a5/src/truetype/ttinterp.c#L2879>
op_sub(&mut self) -> OpResult34     pub(super) fn op_sub(&mut self) -> OpResult {
35         self.value_stack.apply_binary(|a, b| Ok(a.wrapping_sub(b)))
36     }
37 
38     /// DIV[] (0x62)
39     ///
40     /// Pops: n1, n2 (F26Dot6)
41     /// Pushes: (n2 / n1)
42     ///
43     /// Pops n1 and n2 off the stack and pushes onto the stack the quotient
44     /// obtained by dividing n2 by n1. Note that this truncates rather than
45     /// rounds the value.
46     ///
47     /// See <https://learn.microsoft.com/en-us/typography/opentype/spec/tt_instructions#divide>
48     /// and <https://gitlab.freedesktop.org/freetype/freetype/-/blob/57617782464411201ce7bbc93b086c1b4d7d84a5/src/truetype/ttinterp.c#L2892>
op_div(&mut self) -> OpResult49     pub(super) fn op_div(&mut self) -> OpResult {
50         self.value_stack.apply_binary(|a, b| {
51             if b == 0 {
52                 Err(HintErrorKind::DivideByZero)
53             } else {
54                 Ok(math::mul_div_no_round(a, 64, b))
55             }
56         })
57     }
58 
59     /// MUL[] (0x63)
60     ///
61     /// Pops: n1, n2 (F26Dot6)
62     /// Pushes: (n2 * n1)
63     ///
64     /// Pops n1 and n2 off the stack and pushes onto the stack the product of
65     /// the two elements.
66     ///
67     /// See <https://learn.microsoft.com/en-us/typography/opentype/spec/tt_instructions#multiply>
68     /// and <https://gitlab.freedesktop.org/freetype/freetype/-/blob/57617782464411201ce7bbc93b086c1b4d7d84a5/src/truetype/ttinterp.c#L2909>
op_mul(&mut self) -> OpResult69     pub(super) fn op_mul(&mut self) -> OpResult {
70         self.value_stack
71             .apply_binary(|a, b| Ok(math::mul_div(a, b, 64)))
72     }
73 
74     /// ABS[] (0x64)
75     ///
76     /// Pops: n
77     /// Pushes: |n|: absolute value of n (F26Dot6)
78     ///
79     /// Pops n off the stack and pushes onto the stack the absolute value of n.
80     ///
81     /// See <https://learn.microsoft.com/en-us/typography/opentype/spec/tt_instructions#absolute-value>
82     /// and <https://gitlab.freedesktop.org/freetype/freetype/-/blob/57617782464411201ce7bbc93b086c1b4d7d84a5/src/truetype/ttinterp.c#L2922>
op_abs(&mut self) -> OpResult83     pub(super) fn op_abs(&mut self) -> OpResult {
84         self.value_stack.apply_unary(|n| Ok(n.wrapping_abs()))
85     }
86 
87     /// NEG[] (0x65)
88     ///
89     /// Pops: n1
90     /// Pushes: -n1: negation of n1 (F26Dot6)
91     ///
92     /// This instruction pops n1 off the stack and pushes onto the stack the
93     /// negated value of n1.
94     ///
95     /// See <https://learn.microsoft.com/en-us/typography/opentype/spec/tt_instructions#negate>
96     /// and <https://gitlab.freedesktop.org/freetype/freetype/-/blob/57617782464411201ce7bbc93b086c1b4d7d84a5/src/truetype/ttinterp.c#L2936>
op_neg(&mut self) -> OpResult97     pub(super) fn op_neg(&mut self) -> OpResult {
98         self.value_stack.apply_unary(|n1| Ok(n1.wrapping_neg()))
99     }
100 
101     /// FLOOR[] (0x66)
102     ///
103     /// Pops: n1: number whose floor is desired (F26Dot6)
104     /// Pushes: n: floor of n1 (F26Dot6)
105     ///
106     /// Pops n1 and returns n, the greatest integer value less than or equal to n1.
107     ///
108     /// See <https://learn.microsoft.com/en-us/typography/opentype/spec/tt_instructions#floor>
109     /// and <https://gitlab.freedesktop.org/freetype/freetype/-/blob/57617782464411201ce7bbc93b086c1b4d7d84a5/src/truetype/ttinterp.c#L2949>
op_floor(&mut self) -> OpResult110     pub(super) fn op_floor(&mut self) -> OpResult {
111         self.value_stack.apply_unary(|n1| Ok(math::floor(n1)))
112     }
113 
114     /// CEILING[] (0x67)
115     ///
116     /// Pops: n1: number whose ceiling is desired (F26Dot6)
117     /// Pushes: n: ceiling of n1 (F26Dot6)
118     ///
119     /// Pops n1 and returns n, the least integer value greater than or equal to n1.
120     ///
121     /// See <https://learn.microsoft.com/en-us/typography/opentype/spec/tt_instructions#ceiling>
122     /// and <https://gitlab.freedesktop.org/freetype/freetype/-/blob/57617782464411201ce7bbc93b086c1b4d7d84a5/src/truetype/ttinterp.c#L2962>
op_ceiling(&mut self) -> OpResult123     pub(super) fn op_ceiling(&mut self) -> OpResult {
124         self.value_stack.apply_unary(|n1| Ok(math::ceil(n1)))
125     }
126 
127     /// MAX[] (0x8B)
128     ///
129     /// Pops: e1, e2
130     /// Pushes: maximum of e1 and e2
131     ///
132     /// Pops two elements, e1 and e2, from the stack and pushes the larger of
133     /// these two quantities onto the stack.
134     ///
135     /// See <https://learn.microsoft.com/en-us/typography/opentype/spec/tt_instructions#maximum-of-top-two-stack-elements>
136     /// and <https://gitlab.freedesktop.org/freetype/freetype/-/blob/57617782464411201ce7bbc93b086c1b4d7d84a5/src/truetype/ttinterp.c#L3171>
op_max(&mut self) -> OpResult137     pub(super) fn op_max(&mut self) -> OpResult {
138         self.value_stack.apply_binary(|a, b| Ok(a.max(b)))
139     }
140 
141     /// MIN[] (0x8C)
142     ///
143     /// Pops: e1, e2
144     /// Pushes: minimum of e1 and e2
145     ///
146     /// Pops two elements, e1 and e2, from the stack and pushes the smaller
147     /// of these two quantities onto the stack.
148     ///
149     /// See <https://learn.microsoft.com/en-us/typography/opentype/spec/tt_instructions#minimum-of-top-two-stack-elements>
150     /// and <https://gitlab.freedesktop.org/freetype/freetype/-/blob/57617782464411201ce7bbc93b086c1b4d7d84a5/src/truetype/ttinterp.c#L3185>
op_min(&mut self) -> OpResult151     pub(super) fn op_min(&mut self) -> OpResult {
152         self.value_stack.apply_binary(|a, b| Ok(a.min(b)))
153     }
154 }
155 
156 #[cfg(test)]
157 mod tests {
158     use super::{super::MockEngine, math, HintErrorKind};
159 
160     /// Test the binary operations that don't require fixed point
161     /// arithmetic.
162     #[test]
simple_binops()163     fn simple_binops() {
164         let mut mock = MockEngine::new();
165         let mut engine = mock.engine();
166         for a in -10..=10 {
167             for b in -10..=10 {
168                 let input = &[a, b];
169                 engine.test_exec(input, a + b, |engine| {
170                     engine.op_add().unwrap();
171                 });
172                 engine.test_exec(input, a - b, |engine| {
173                     engine.op_sub().unwrap();
174                 });
175                 engine.test_exec(input, a.max(b), |engine| {
176                     engine.op_max().unwrap();
177                 });
178                 engine.test_exec(input, a.min(b), |engine| {
179                     engine.op_min().unwrap();
180                 });
181             }
182         }
183     }
184 
185     /// Test the unary operations that don't require fixed point
186     /// arithmetic.
187     #[test]
simple_unops()188     fn simple_unops() {
189         let mut mock = MockEngine::new();
190         let mut engine = mock.engine();
191         for a in -10..=10 {
192             let input = &[a];
193             engine.test_exec(input, -a, |engine| {
194                 engine.op_neg().unwrap();
195             });
196             engine.test_exec(input, a.abs(), |engine| {
197                 engine.op_abs().unwrap();
198             });
199         }
200     }
201 
202     #[test]
f26dot6_binops()203     fn f26dot6_binops() {
204         let mut mock = MockEngine::new();
205         let mut engine = mock.engine();
206         for a in -10..=10 {
207             for b in -10..=10 {
208                 let a = a * 64 + 30;
209                 let b = b * 64 - 30;
210                 let input = &[a, b];
211                 engine.test_exec(input, math::mul_div(a, b, 64), |engine| {
212                     engine.op_mul().unwrap();
213                 });
214                 if b != 0 {
215                     engine.test_exec(input, math::mul_div_no_round(a, 64, b), |engine| {
216                         engine.op_div().unwrap();
217                     });
218                 } else {
219                     engine.value_stack.push(a).unwrap();
220                     engine.value_stack.push(b).unwrap();
221                     assert!(matches!(engine.op_div(), Err(HintErrorKind::DivideByZero)));
222                 }
223             }
224         }
225     }
226 
227     #[test]
f26dot6_unops()228     fn f26dot6_unops() {
229         let mut mock = MockEngine::new();
230         let mut engine = mock.engine();
231         for a in -10..=10 {
232             for b in -10..=10 {
233                 let a = a * 64 + b;
234                 let input = &[a];
235                 engine.test_exec(input, math::floor(a), |engine| {
236                     engine.op_floor().unwrap();
237                 });
238                 engine.test_exec(input, math::ceil(a), |engine| {
239                     engine.op_ceiling().unwrap();
240                 });
241             }
242         }
243     }
244 }
245