use std::ops::Mul;
use bytemuck::{Pod, Zeroable};
use peniko::kurbo;
#[derive(Copy, Clone, Debug, PartialEq, Pod, Zeroable)]
#[repr(C)]
pub struct Transform {
pub matrix: [f32; 4],
pub translation: [f32; 2],
}
impl Transform {
pub const IDENTITY: Self = Self {
matrix: [1.0, 0.0, 0.0, 1.0],
translation: [0.0; 2],
};
pub fn from_kurbo(transform: &kurbo::Affine) -> Self {
let c = transform.as_coeffs().map(|x| x as f32);
Self {
matrix: [c[0], c[1], c[2], c[3]],
translation: [c[4], c[5]],
}
}
pub fn to_kurbo(&self) -> kurbo::Affine {
kurbo::Affine::new(
[
self.matrix[0],
self.matrix[1],
self.matrix[2],
self.matrix[3],
self.translation[0],
self.translation[1],
]
.map(|x| x as f64),
)
}
}
impl Mul for Transform {
type Output = Self;
#[inline]
fn mul(self, other: Self) -> Self {
Self {
matrix: [
self.matrix[0] * other.matrix[0] + self.matrix[2] * other.matrix[1],
self.matrix[1] * other.matrix[0] + self.matrix[3] * other.matrix[1],
self.matrix[0] * other.matrix[2] + self.matrix[2] * other.matrix[3],
self.matrix[1] * other.matrix[2] + self.matrix[3] * other.matrix[3],
],
translation: [
self.matrix[0] * other.translation[0]
+ self.matrix[2] * other.translation[1]
+ self.translation[0],
self.matrix[1] * other.translation[0]
+ self.matrix[3] * other.translation[1]
+ self.translation[1],
],
}
}
}
pub fn point_to_f32(point: kurbo::Point) -> [f32; 2] {
[point.x as f32, point.y as f32]
}
pub(crate) fn f32_to_f16(val: f32) -> u16 {
const INF_32: u32 = 255 << 23;
const INF_16: u32 = 31 << 23;
const MAGIC: u32 = 15 << 23;
const SIGN_MASK: u32 = 0x8000_0000_u32;
const ROUND_MASK: u32 = !0xFFF_u32;
let u = val.to_bits();
let sign = u & SIGN_MASK;
let u = u ^ sign;
let output: u16 = if u >= INF_32 {
if u > INF_32 { 0x7E00 } else { 0x7C00 }
} else {
let mut u = u & ROUND_MASK;
u = (f32::from_bits(u) * f32::from_bits(MAGIC)).to_bits();
u = u.overflowing_sub(ROUND_MASK).0;
if u > INF_16 {
u = INF_16;
}
(u >> 13) as u16 };
output | (sign >> 16) as u16
}
pub fn f16_to_f32(bits: u16) -> f32 {
let bits = bits as u32;
const MAGIC: u32 = 113 << 23;
const SHIFTED_EXP: u32 = 0x7c00 << 13;
let mut o = (bits & 0x7fff) << 13; let exp = SHIFTED_EXP & o; o += (127 - 15) << 23;
if exp == SHIFTED_EXP {
o += (128 - 16) << 23; } else if exp == 0 {
o += 1 << 23; o = (f32::from_bits(o) - f32::from_bits(MAGIC)).to_bits(); }
f32::from_bits(o | ((bits & 0x8000) << 16)) }
#[cfg(test)]
mod tests {
use super::{f16_to_f32, f32_to_f16};
#[test]
fn test_f32_to_f16_simple() {
let input: f32 = std::f32::consts::PI;
let output: u16 = f32_to_f16(input);
assert_eq!(0x4248_u16, output); }
#[test]
fn test_f32_to_f16_nan_overflow() {
let input: f32 = f32::from_bits(0x7F800001_u32);
assert!(input.is_nan());
let output: u16 = f32_to_f16(input);
assert_eq!(0x7E00, output);
}
#[test]
fn test_f32_to_f16_inf() {
let input: f32 = f32::from_bits(0x7F800000_u32);
assert!(input.is_infinite());
let output: u16 = f32_to_f16(input);
assert_eq!(0x7C00, output);
}
#[test]
fn test_f32_to_f16_exponent_rebias() {
let input: f32 = 0.00003051758;
let output: u16 = f32_to_f16(input);
assert_eq!(0x0200, output); }
#[test]
fn test_f32_to_f16_exponent_overflow() {
let input: f32 = 1.701412e38;
let output: u16 = f32_to_f16(input);
assert_eq!(0x7C00, output); }
#[test]
fn test_f32_to_f16_exponent_overflow_neg_inf() {
let input: f32 = -1.701412e38;
let output: u16 = f32_to_f16(input);
assert_eq!(0xFC00, output); }
#[test]
fn test_f16_to_f32_simple() {
let input: u16 = 0x4248_u16;
let output: f32 = f16_to_f32(input);
assert_eq!(3.140625, output);
}
#[test]
fn test_f16_to_f32_inf() {
let input: u16 = 0x7C00;
let output = f16_to_f32(input);
assert!(output.is_infinite());
}
#[test]
fn test_f16_to_f32_neg_inf() {
let input: u16 = 0xFC00;
let output = f16_to_f32(input);
assert!(output.is_infinite());
}
#[test]
fn test_f16_to_f32_inf_roundtrip() {
let input: u16 = 0x7C00;
let output = f32_to_f16(f16_to_f32(input));
assert_eq!(input, output);
}
#[test]
fn test_f16_to_f32_neg_inf_roundtrip() {
let input: u16 = 0xFC00;
let output = f32_to_f16(f16_to_f32(input));
assert_eq!(input, output);
}
#[test]
fn test_f16_to_f32_nan() {
let input: u16 = 0x7C01;
let output = f16_to_f32(input);
assert!(output.is_nan());
}
#[test]
fn test_f16_to_f32_nan_roundtrip() {
let input: u16 = 0x7C01;
let output = f16_to_f32(f32_to_f16(f16_to_f32(input)));
assert!(output.is_nan());
}
#[test]
fn test_f16_to_f32_large_pos_roundtrip() {
let input: u16 = 0x7BFF; let output = f32_to_f16(f16_to_f32(input));
assert_eq!(input, output);
}
#[test]
fn test_f16_to_f32_large_neg_roundtrip() {
let input: u16 = 0xFBFF; let output = f32_to_f16(f16_to_f32(input));
assert_eq!(input, output);
}
#[test]
fn test_f16_to_f32_small_pos_roundtrip() {
let input: u16 = 0x0001; let output = f32_to_f16(f16_to_f32(input));
assert_eq!(input, output);
}
#[test]
fn test_f16_to_f32_small_neg_roundtrip() {
let input: u16 = 0x8001; let output = f32_to_f16(f16_to_f32(input));
assert_eq!(input, output);
}
#[test]
fn test_f16_to_f32_roundtrip() {
const EPS: f32 = 0.001;
let input: f32 = std::f32::consts::PI;
assert!((input - f16_to_f32(f32_to_f16(input))).abs() < EPS);
}
}