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