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