1use crate::util::impl_fmt_with;
2
3macro_rules! impl_fixed_point_number {
4 (
5 $(#[$outer:meta])*
6 $vis:vis struct $Name:ident(
7 $int:ty as [u8; $bytes:literal];
8 $integer_bits:literal | $fraction_bits:literal as $fp:ty
9 );
10 DENOM = $d_denom:literal;
11 STEP = $d_step:literal;
12 MIN = $d_min:literal;
13 MAX = $d_max:literal;
14 PRECISION = $d_precision:literal;
15 ) => {
16 #[doc = concat!("The [OpenType ", stringify!($Name), "][spec] type, a ")]
17 #[doc = concat!(stringify!($integer_bits), ".", stringify!($fraction_bits), "-bit")]
18 $(#[$outer])*
22 #[derive(Copy, Hash)]
23 #[derive_const(Clone, PartialEq, Eq)]
24 #[repr(transparent)]
25 $vis struct $Name([u8; $bytes]);
26
27 const _: () = {
28 assert!(size_of::<$int>() == $bytes);
29 assert!($integer_bits + $fraction_bits == <$int>::BITS);
30 };
31
32 impl $Name {
33 const F_STEP: $fp = 1.0 / (1 << $fraction_bits) as $fp;
34 const F_MIN: $fp = -(1 << ($integer_bits - 1)) as $fp;
35 const F_MAX_EXCLUSIVE: $fp = (1 << ($integer_bits - 1)) as $fp;
36 const F_MAX: $fp = Self::F_MAX_EXCLUSIVE - Self::F_STEP;
37
38 #[doc = concat!("The difference between adjacent [`", stringify!($Name), "`] values.")]
39 #[doc = concat!("# use ttf_view::types::", stringify!($Name), ";")]
44 #[doc = concat!("assert_eq!(", stringify!($Name), "::STEP, ", stringify!($d_step), ");")]
45 pub const STEP: Self = Self::new(Self::F_STEP).unwrap();
47
48 #[doc = concat!("# use ttf_view::types::", stringify!($Name), ";")]
54 #[doc = concat!("assert_eq!(", stringify!($Name), "::MIN, ", stringify!($d_min), ");")]
55 pub const MIN: Self = Self::new(Self::F_MIN).unwrap();
57 #[doc = concat!("# use ttf_view::types::", stringify!($Name), ";")]
63 #[doc = concat!("assert_eq!(", stringify!($Name), "::MAX, ", stringify!($d_max), ");")]
64 pub const MAX: Self = Self::new(Self::F_MAX).unwrap();
66
67 #[doc = concat!("# use ttf_view::types::", stringify!($Name), ";")]
73 #[doc = concat!("assert_eq!(", stringify!($Name), "::PRECISION, ", stringify!($d_precision), ");")]
74 pub const PRECISION: u32 = (Self::F_STEP.recip() as u32).ilog10();
76
77 #[doc = concat!("Creates a [`", stringify!($Name), "`] from [`", stringify!($fp), "`].")]
78 #[doc = concat!("use ttf_view::types::", stringify!($Name), ";")]
83 #[doc = concat!("assert_eq!(", stringify!($Name), "::new(-0.125).unwrap(), -0.125);")]
85 #[doc = concat!("assert_eq!(", stringify!($Name), "::new(1.99609375).unwrap(), 1.99609375);")]
86 #[doc = concat!("assert_eq!(", stringify!($Name), "::new(123456.78), None);")]
87 #[doc = concat!("// numbers are rounded towards the closest value representable by ", stringify!($Name))]
89 #[doc = concat!("assert_eq!(", stringify!($Name), "::new(0.5156247).unwrap(), 0.515625);")]
90 #[doc = concat!("assert_eq!(", stringify!($Name), "::new(0.5156250).unwrap(), 0.515625);")]
91 #[doc = concat!("assert_eq!(", stringify!($Name), "::new(0.5156254).unwrap(), 0.515625);")]
92 pub const fn new(num: $fp) -> Option<Self> {
94 if matches!(num, Self::F_MIN..Self::F_MAX_EXCLUSIVE) {
95 Some(unsafe { Self::new_unchecked(num) })
96 } else {
97 None
98 }
99 }
100 #[doc = concat!("Creates a [`", stringify!($Name), "`] from [`", stringify!($fp), "`] without checks.")]
101 pub const unsafe fn new_unchecked(num: $fp) -> Self {
104 debug_assert!(matches!(num, Self::F_MIN..Self::F_MAX_EXCLUSIVE));
105
106 Self(((num / Self::F_STEP).round() as $int).to_be_bytes())
109 }
110
111 #[doc = concat!("Creates a [`", stringify!($Name), "`] from big-endian bytes.")]
112 pub const fn from_be_bytes(bytes: [u8; $bytes]) -> Self {
115 Self(bytes)
116 }
117 #[doc = concat!("Gets this [`", stringify!($Name), "`]'s big-endian bytes.")]
118 pub const fn to_be_bytes(self) -> [u8; $bytes] {
121 self.0
122 }
123
124 #[doc = concat!("Returns this [`", stringify!($Name), "`] fraction's numerator")]
125 #[doc = concat!("(`", stringify!($Name), "` represented as <math><mfrac><mi>numerator</mi><mn>", stringify!($d_denom), "</mn></mfrac></math>).")]
126 pub const fn frac_num(&self) -> $int {
129 <$int>::from_be_bytes(self.0)
130 }
131 #[doc = concat!("Returns this [`", stringify!($Name), "`]'s value as [`", stringify!($fp), "`].")]
132 pub const fn get(&self) -> $fp {
135 self.frac_num() as $fp * Self::F_STEP
136 }
137 #[doc = concat!("Rounds this [`", stringify!($Name), "`]'s value to [`PRECISION`][Self::PRECISION] decimal places.")]
138 pub const fn round_to_precision(&self) -> $fp {
141 const SCALE: $fp = 10u32.pow($Name::PRECISION) as $fp;
142 (self.get() * SCALE).round() / SCALE
143 }
144 }
145
146 impl_fmt_with! {
147 Debug, Display, LowerExp, UpperExp:
148 |x: &$Name| x.get()
149 }
150
151 const impl PartialOrd for $Name {
152 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
153 Some(self.cmp(other))
154 }
155 }
156 const impl Ord for $Name {
157 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
158 self.frac_num().cmp(&other.frac_num())
159 }
160 }
161
162 const impl PartialEq<$fp> for $Name {
163 fn eq(&self, other: &$fp) -> bool {
164 self.get().eq(other)
165 }
166 }
167 const impl PartialOrd<$fp> for $Name {
168 fn partial_cmp(&self, other: &$fp) -> Option<std::cmp::Ordering> {
169 self.get().partial_cmp(other)
170 }
171 }
172
173 impl std::str::FromStr for $Name {
174 type Err = ();
175 fn from_str(s: &str) -> Result<Self, Self::Err> {
176 <$fp>::from_str(s).or(Err(())).and_then($Name::try_from)
177 }
178 }
179 const impl TryFrom<$fp> for $Name {
180 type Error = ();
181 fn try_from(value: $fp) -> Result<$Name, Self::Error> {
182 Self::new(value).ok_or(())
183 }
184 }
185 const impl From<$Name> for $fp {
186 fn from(value: $Name) -> Self {
187 value.get()
188 }
189 }
190 }
191}
192
193impl_fixed_point_number! {
195 pub struct Fixed(i32 as [u8; 4]; 16|16 as f64);
196 DENOM = 65536;
197 STEP = 0.0000152587890625;
198 MIN = -32768.0;
199 MAX = 32767.99998474121;
200 PRECISION = 4;
201}
202impl_fixed_point_number! {
203 pub struct F2DOT14(i16 as [u8; 2]; 2|14 as f32);
204 DENOM = 16384;
205 STEP = 0.000061035156;
206 MIN = -2.0;
207 MAX = 1.999939;
208 PRECISION = 4;
209}
210
211#[cfg(test)]
212mod tests {
213 use super::*;
214
215 #[test]
216 fn precision() {
217 let f = Fixed::from_be_bytes(0x0001999A_u32.to_be_bytes());
219 assert_eq!(format!("{}", f), "1.600006103515625");
220 assert_eq!(format!("{}", f.round_to_precision()), "1.6");
221
222 assert_eq!(format!("{:.0}", f), "2");
223 assert_eq!(format!("{:.1}", f), "1.6");
224 assert_eq!(format!("{:.3}", f), "1.600");
225 assert_eq!(format!("{:.7}", f), "1.6000061");
226
227 assert_eq!(Fixed::new(32767.999999999996).unwrap().to_be_bytes(), [0x7F, 0xFF, 0xFF, 0xFF]);
229 assert_eq!(Fixed::new(32768.0), None);
230 assert_eq!(F2DOT14::new(1.9999999).unwrap().to_be_bytes(), [0x7F, 0xFF]);
231 assert_eq!(F2DOT14::new(2.0), None);
232
233 assert_eq!(Fixed::new(-32768.0).unwrap().to_be_bytes(), [0x80, 0x00, 0x00, 0x00]);
235 assert_eq!(Fixed::new(-32768.00000000001), None);
236 assert_eq!(F2DOT14::new(-2.0).unwrap().to_be_bytes(), [0x80, 0x00]);
237 assert_eq!(F2DOT14::new(-2.0000002), None);
238 }
239
240 #[test]
241 fn fixed() {
242 assert_eq!(Fixed::F_MIN, -32768.0);
244 assert_eq!(Fixed::F_MAX_EXCLUSIVE, 32768.0);
245 assert_eq!(Fixed::F_MAX, 32767.99998474121);
246
247 let nums: [(u32, f64); _] = [
249 (0x7FFF_FFFF, 32767.999985),
250 (0x7FFF_FF00, 32767.996094),
251 (0x7FFF_2000, 32767.125000),
252 (0x7FFF_0000, 32767.000000),
253 (0x0040_0100, 64.003906),
254 (0x0040_0000, 64.000000),
255 (0x0001_0000, 1.000000),
256 (0x0000_0001, 0.000015),
257 (0x0000_0000, 0.000000),
258 (0xFFFF_0000, -1.000000),
259 (0xFFBF_FF00, -64.003906),
260 (0x8000_0000, -32768.000000),
261 ];
262
263 for (raw, fp) in nums {
264 let real = Fixed::new(fp).unwrap().frac_num() as u32;
265 assert_eq!(real, raw, "{real:#X} != {raw:#X} ({fp})");
266
267 let real = Fixed::new(fp).unwrap().get();
268 let diff = (real - fp).abs();
269 assert!(diff <= 0.1 * Fixed::F_STEP, "{real} != {fp} (Δ={diff})");
270 }
271 }
272
273 #[test]
274 fn f2dot14() {
275 assert_eq!(F2DOT14::F_MIN, -2.0);
277 assert_eq!(F2DOT14::F_MAX_EXCLUSIVE, 2.0);
278 assert_eq!(F2DOT14::F_MAX, 1.999939);
279
280 let nums: [(u16, f32); _] = [
282 (0x7FFF, 1.999939),
283 (0x7000, 1.750000),
284 (0x0085, 0.008118),
285 (0x0002, 0.000122),
286 (0x0001, 0.000061),
287 (0x0000, 0.000000),
288 (0xFFFF, -0.000061),
289 (0xFFFE, -0.000122),
290 (0xFF7B, -0.008118),
291 (0x8000, -2.000000),
292 ];
293
294 for (raw, fp) in nums {
295 let real = F2DOT14::new(fp).unwrap().frac_num() as u16;
296 assert_eq!(real, raw, "{real:#X} != {raw:#X} ({fp})");
297
298 let real = F2DOT14::new(fp).unwrap().get();
299 let diff = (real - fp).abs();
300 assert!(diff <= 0.1 * F2DOT14::F_STEP, "{real} != {fp} (Δ={diff})");
301 }
302 }
303}