1 //! Logical functions.
2 //!
3 //! Implements 11 instructions.
4 //!
5 //! See <https://learn.microsoft.com/en-us/typography/opentype/spec/tt_instructions#logical-functions>
6 
7 use super::{Engine, OpResult};
8 
9 impl<'a> Engine<'a> {
10     /// Less than.
11     ///
12     /// LT[] (0x50)
13     ///
14     /// Pops: e1, e2
15     /// Pushes: Boolean value
16     ///
17     /// First pops e2, then pops e1 off the stack and compares them: if e1 is
18     /// less than e2, 1, signifying TRUE, is pushed onto the stack. If e1 is
19     /// not less than e2, 0, signifying FALSE, is placed onto the stack.
20     ///
21     /// See <https://learn.microsoft.com/en-us/typography/opentype/spec/tt_instructions#less-than>
22     /// and <https://gitlab.freedesktop.org/freetype/freetype/-/blob/57617782464411201ce7bbc93b086c1b4d7d84a5/src/truetype/ttinterp.c#L2721>
op_lt(&mut self) -> OpResult23     pub(super) fn op_lt(&mut self) -> OpResult {
24         self.value_stack.apply_binary(|a, b| Ok((a < b) as i32))
25     }
26 
27     /// Less than or equal.
28     ///
29     /// LTEQ[] (0x51)
30     ///
31     /// Pops: e1, e2
32     /// Pushes: Boolean value
33     ///
34     /// Pops e2 and e1 off the stack and compares them. If e1 is less than or
35     /// equal to e2, 1, signifying TRUE, is pushed onto the stack. If e1 is
36     /// not less than or equal to e2, 0, signifying FALSE, is placed onto the
37     /// stack.
38     ///
39     /// See <https://learn.microsoft.com/en-us/typography/opentype/spec/tt_instructions#less-than-or-equal>
40     /// and <https://gitlab.freedesktop.org/freetype/freetype/-/blob/57617782464411201ce7bbc93b086c1b4d7d84a5/src/truetype/ttinterp.c#L2734>
op_lteq(&mut self) -> OpResult41     pub(super) fn op_lteq(&mut self) -> OpResult {
42         self.value_stack.apply_binary(|a, b| Ok((a <= b) as i32))
43     }
44 
45     /// Greather than.
46     ///
47     /// GT[] (0x52)
48     ///
49     /// Pops: e1, e2
50     /// Pushes: Boolean value
51     ///
52     /// First pops e2 then pops e1 off the stack and compares them. If e1 is
53     /// greater than e2, 1, signifying TRUE, is pushed onto the stack. If e1
54     /// is not greater than e2, 0, signifying FALSE, is placed onto the stack.
55     ///
56     /// See <https://learn.microsoft.com/en-us/typography/opentype/spec/tt_instructions#greater-than>
57     /// and <https://gitlab.freedesktop.org/freetype/freetype/-/blob/57617782464411201ce7bbc93b086c1b4d7d84a5/src/truetype/ttinterp.c#L2747>
op_gt(&mut self) -> OpResult58     pub(super) fn op_gt(&mut self) -> OpResult {
59         self.value_stack.apply_binary(|a, b| Ok((a > b) as i32))
60     }
61 
62     /// Greater than or equal.
63     ///
64     /// GTEQ[] (0x53)
65     ///
66     /// Pops: e1, e2
67     /// Pushes: Boolean value
68     ///
69     /// Pops e1 and e2 off the stack and compares them. If e1 is greater than
70     /// or equal to e2, 1, signifying TRUE, is pushed onto the stack. If e1
71     /// is not greater than or equal to e2, 0, signifying FALSE, is placed
72     /// onto the stack.
73     ///
74     /// See <https://learn.microsoft.com/en-us/typography/opentype/spec/tt_instructions#greater-than-or-equal>
75     /// and <https://gitlab.freedesktop.org/freetype/freetype/-/blob/57617782464411201ce7bbc93b086c1b4d7d84a5/src/truetype/ttinterp.c#L2760>
op_gteq(&mut self) -> OpResult76     pub(super) fn op_gteq(&mut self) -> OpResult {
77         self.value_stack.apply_binary(|a, b| Ok((a >= b) as i32))
78     }
79 
80     /// Equal.
81     ///
82     /// EQ[] (0x54)
83     ///
84     /// Pops: e1, e2
85     /// Pushes: Boolean value
86     ///
87     /// Pops e1 and e2 off the stack and compares them. If they are equal, 1,
88     /// signifying TRUE is pushed onto the stack. If they are not equal, 0,
89     /// signifying FALSE is placed onto the stack.
90     ///
91     /// See <https://learn.microsoft.com/en-us/typography/opentype/spec/tt_instructions#equal>
92     /// and <https://gitlab.freedesktop.org/freetype/freetype/-/blob/57617782464411201ce7bbc93b086c1b4d7d84a5/src/truetype/ttinterp.c#L2773>
op_eq(&mut self) -> OpResult93     pub(super) fn op_eq(&mut self) -> OpResult {
94         self.value_stack.apply_binary(|a, b| Ok((a == b) as i32))
95     }
96 
97     /// Not equal.
98     ///
99     /// NEQ[] (0x55)
100     ///
101     /// Pops: e1, e2
102     /// Pushes: Boolean value
103     ///
104     /// Pops e1 and e2 from the stack and compares them. If they are not equal,
105     /// 1, signifying TRUE, is pushed onto the stack. If they are equal, 0,
106     /// signifying FALSE, is placed on the stack.
107     ///
108     /// See <https://learn.microsoft.com/en-us/typography/opentype/spec/tt_instructions#not-equal>
109     /// and <https://gitlab.freedesktop.org/freetype/freetype/-/blob/57617782464411201ce7bbc93b086c1b4d7d84a5/src/truetype/ttinterp.c#L2786>
op_neq(&mut self) -> OpResult110     pub(super) fn op_neq(&mut self) -> OpResult {
111         self.value_stack.apply_binary(|a, b| Ok((a != b) as i32))
112     }
113 
114     /// Odd.
115     ///
116     /// ODD[] (0x56)
117     ///
118     /// Pops: e1
119     /// Pushes: Boolean value
120     ///
121     /// Tests whether the number at the top of the stack is odd. Pops e1 from
122     /// the stack and rounds it as specified by the round_state before testing
123     /// it. After the value is rounded, it is shifted from a fixed point value
124     /// to an integer value (any fractional values are ignored). If the integer
125     /// value is odd, one, signifying TRUE, is pushed onto the stack. If it is
126     /// even, zero, signifying FALSE is placed onto the stack.
127     ///
128     /// See <https://learn.microsoft.com/en-us/typography/opentype/spec/tt_instructions#odd>
129     /// and <https://gitlab.freedesktop.org/freetype/freetype/-/blob/57617782464411201ce7bbc93b086c1b4d7d84a5/src/truetype/ttinterp.c#L2799>
op_odd(&mut self) -> OpResult130     pub(super) fn op_odd(&mut self) -> OpResult {
131         let round_state = self.graphics_state.round_state;
132         self.value_stack
133             .apply_unary(|e1| Ok((round_state.round(e1) & 127 == 64) as i32))
134     }
135 
136     /// Even.
137     ///
138     /// EVEN[] (0x57)
139     ///
140     /// Pops: e1
141     /// Pushes: Boolean value
142     ///
143     /// Tests whether the number at the top of the stack is even. Pops e1 off
144     /// the stack and rounds it as specified by the round_state before testing
145     /// it. If the rounded number is even, one, signifying TRUE, is pushed onto
146     /// the stack if it is odd, zero, signifying FALSE, is placed onto the
147     /// stack.
148     ///
149     /// See <https://learn.microsoft.com/en-us/typography/opentype/spec/tt_instructions#even>
150     /// and <https://gitlab.freedesktop.org/freetype/freetype/-/blob/57617782464411201ce7bbc93b086c1b4d7d84a5/src/truetype/ttinterp.c#L2813>
op_even(&mut self) -> OpResult151     pub(super) fn op_even(&mut self) -> OpResult {
152         let round_state = self.graphics_state.round_state;
153         self.value_stack
154             .apply_unary(|e1| Ok((round_state.round(e1) & 127 == 0) as i32))
155     }
156 
157     /// Logical and.
158     ///
159     /// AND[] (0x5A)
160     ///
161     /// Pops: e1, e2
162     /// Pushes: Boolean value
163     ///
164     /// Pops e1 and e2 off the stack and pushes onto the stack the result of a
165     /// logical and of the two elements. Zero is returned if either or both of
166     /// the elements are FALSE (have the value zero). One is returned if both
167     /// elements are TRUE (have a non zero value).
168     ///
169     /// See <https://learn.microsoft.com/en-us/typography/opentype/spec/tt_instructions#logical-and>
170     /// and <https://gitlab.freedesktop.org/freetype/freetype/-/blob/57617782464411201ce7bbc93b086c1b4d7d84a5/src/truetype/ttinterp.c#L2827>
op_and(&mut self) -> OpResult171     pub(super) fn op_and(&mut self) -> OpResult {
172         self.value_stack
173             .apply_binary(|a, b| Ok((a != 0 && b != 0) as i32))
174     }
175 
176     /// Logical or.
177     ///
178     /// OR[] (0x5B)
179     ///
180     /// Pops: e1, e2
181     /// Pushes: Boolean value
182     ///
183     /// Pops e1 and e2 off the stack and pushes onto the stack the result of a
184     /// logical or operation between the two elements. Zero is returned if both
185     /// of the elements are FALSE. One is returned if either one or both of the
186     /// elements are TRUE (has a nonzero value).
187     ///
188     /// See <https://learn.microsoft.com/en-us/typography/opentype/spec/tt_instructions#logical-or>
189     /// and <https://gitlab.freedesktop.org/freetype/freetype/-/blob/57617782464411201ce7bbc93b086c1b4d7d84a5/src/truetype/ttinterp.c#L2840>
op_or(&mut self) -> OpResult190     pub(super) fn op_or(&mut self) -> OpResult {
191         self.value_stack
192             .apply_binary(|a, b| Ok((a != 0 || b != 0) as i32))
193     }
194 
195     /// Logical not.
196     ///
197     /// NOT[] (0x5C)
198     ///
199     /// Pops: e
200     /// Pushes: (not e): logical negation of e
201     ///
202     /// Pops e off the stack and returns the result of a logical NOT operation
203     /// performed on e. If originally zero, one is pushed onto the stack if
204     /// originally nonzero, zero is pushed onto the stack.
205     ///
206     /// See <https://learn.microsoft.com/en-us/typography/opentype/spec/tt_instructions#logical-not>
207     /// and <https://gitlab.freedesktop.org/freetype/freetype/-/blob/57617782464411201ce7bbc93b086c1b4d7d84a5/src/truetype/ttinterp.c#L2853>
op_not(&mut self) -> OpResult208     pub(super) fn op_not(&mut self) -> OpResult {
209         self.value_stack.apply_unary(|e| Ok((e == 0) as i32))
210     }
211 }
212 
213 #[cfg(test)]
214 mod tests {
215     use super::super::MockEngine;
216 
217     #[test]
compare_ops()218     fn compare_ops() {
219         let mut mock = MockEngine::new();
220         let mut engine = mock.engine();
221         for a in -10..=10 {
222             for b in -10..=10 {
223                 let input = &[a, b];
224                 engine.test_exec(input, a < b, |engine| {
225                     engine.op_lt().unwrap();
226                 });
227                 engine.test_exec(input, a <= b, |engine| {
228                     engine.op_lteq().unwrap();
229                 });
230                 engine.test_exec(input, a > b, |engine| {
231                     engine.op_gt().unwrap();
232                 });
233                 engine.test_exec(input, a >= b, |engine| {
234                     engine.op_gteq().unwrap();
235                 });
236                 engine.test_exec(input, a == b, |engine| {
237                     engine.op_eq().unwrap();
238                 });
239                 engine.test_exec(input, a != b, |engine| {
240                     engine.op_neq().unwrap();
241                 });
242             }
243         }
244     }
245 
246     #[test]
parity_ops()247     fn parity_ops() {
248         let mut mock = MockEngine::new();
249         let mut engine = mock.engine();
250         // These operate on 26.6 so values are multiple of 64
251         let cases = [
252             // (input, is_even)
253             (0, true),
254             (64, false),
255             (128, true),
256             (192, false),
257             (256, true),
258             (57, false),
259             (-128, true),
260         ];
261         for (input, is_even) in cases {
262             engine.test_exec(&[input], is_even, |engine| {
263                 engine.op_even().unwrap();
264             });
265         }
266         for (input, is_even) in cases {
267             engine.test_exec(&[input], !is_even, |engine| {
268                 engine.op_odd().unwrap();
269             });
270         }
271     }
272 
273     #[test]
not_op()274     fn not_op() {
275         let mut mock = MockEngine::new();
276         let mut engine = mock.engine();
277         engine.test_exec(&[0], 1, |engine| {
278             engine.op_not().unwrap();
279         });
280         engine.test_exec(&[234234], 0, |engine| {
281             engine.op_not().unwrap();
282         });
283     }
284 
285     #[test]
and_or_ops()286     fn and_or_ops() {
287         let mut mock = MockEngine::new();
288         let mut engine = mock.engine();
289         for a in -10..=10 {
290             for b in -10..=10 {
291                 let input = &[a, b];
292                 let a = a != 0;
293                 let b = b != 0;
294                 engine.test_exec(input, a && b, |engine| {
295                     engine.op_and().unwrap();
296                 });
297                 engine.test_exec(input, a || b, |engine| {
298                     engine.op_or().unwrap();
299                 });
300             }
301         }
302     }
303 }
304