Skip to main content

ad_core/
pixel_cast.rs

1/// Trait for casting f64 values to/from typed pixel values.
2/// Integer types clamp and round; float types pass through directly.
3pub trait PixelCast: Copy + Default + 'static {
4    fn from_f64(v: f64) -> Self;
5    fn to_f64(self) -> f64;
6}
7
8macro_rules! impl_pixel_cast_int {
9    ($t:ty) => {
10        impl PixelCast for $t {
11            #[inline]
12            fn from_f64(v: f64) -> Self {
13                if v.is_nan() {
14                    return 0;
15                }
16                v.round().clamp(Self::MIN as f64, Self::MAX as f64) as Self
17            }
18            #[inline]
19            fn to_f64(self) -> f64 {
20                self as f64
21            }
22        }
23    };
24}
25
26impl_pixel_cast_int!(i8);
27impl_pixel_cast_int!(u8);
28impl_pixel_cast_int!(i16);
29impl_pixel_cast_int!(u16);
30impl_pixel_cast_int!(i32);
31impl_pixel_cast_int!(u32);
32impl_pixel_cast_int!(i64);
33impl_pixel_cast_int!(u64);
34
35impl PixelCast for f32 {
36    #[inline]
37    fn from_f64(v: f64) -> Self {
38        v as f32
39    }
40    #[inline]
41    fn to_f64(self) -> f64 {
42        self as f64
43    }
44}
45
46impl PixelCast for f64 {
47    #[inline]
48    fn from_f64(v: f64) -> Self {
49        v
50    }
51    #[inline]
52    fn to_f64(self) -> f64 {
53        self
54    }
55}
56
57/// Dispatch on NDDataBuffer variant, binding the inner Vec to `$v`.
58/// The body `$body` is monomorphized for each type.
59#[macro_export]
60macro_rules! with_buffer {
61    ($buffer:expr, |$v:ident| $body:expr) => {
62        match $buffer {
63            $crate::ndarray::NDDataBuffer::I8($v) => { $body }
64            $crate::ndarray::NDDataBuffer::U8($v) => { $body }
65            $crate::ndarray::NDDataBuffer::I16($v) => { $body }
66            $crate::ndarray::NDDataBuffer::U16($v) => { $body }
67            $crate::ndarray::NDDataBuffer::I32($v) => { $body }
68            $crate::ndarray::NDDataBuffer::U32($v) => { $body }
69            $crate::ndarray::NDDataBuffer::I64($v) => { $body }
70            $crate::ndarray::NDDataBuffer::U64($v) => { $body }
71            $crate::ndarray::NDDataBuffer::F32($v) => { $body }
72            $crate::ndarray::NDDataBuffer::F64($v) => { $body }
73        }
74    };
75}
76
77/// Same as with_buffer! but gives mutable access.
78#[macro_export]
79macro_rules! with_buffer_mut {
80    ($buffer:expr, |$v:ident| $body:expr) => {
81        match $buffer {
82            $crate::ndarray::NDDataBuffer::I8($v) => { $body }
83            $crate::ndarray::NDDataBuffer::U8($v) => { $body }
84            $crate::ndarray::NDDataBuffer::I16($v) => { $body }
85            $crate::ndarray::NDDataBuffer::U16($v) => { $body }
86            $crate::ndarray::NDDataBuffer::I32($v) => { $body }
87            $crate::ndarray::NDDataBuffer::U32($v) => { $body }
88            $crate::ndarray::NDDataBuffer::I64($v) => { $body }
89            $crate::ndarray::NDDataBuffer::U64($v) => { $body }
90            $crate::ndarray::NDDataBuffer::F32($v) => { $body }
91            $crate::ndarray::NDDataBuffer::F64($v) => { $body }
92        }
93    };
94}
95
96pub use with_buffer;
97pub use with_buffer_mut;
98
99#[cfg(test)]
100mod tests {
101    use super::*;
102    use crate::ndarray::NDDataBuffer;
103
104    #[test]
105    fn test_u8_from_f64_normal() {
106        assert_eq!(u8::from_f64(128.7), 129);
107        assert_eq!(u8::from_f64(0.0), 0);
108        assert_eq!(u8::from_f64(255.0), 255);
109    }
110
111    #[test]
112    fn test_u8_from_f64_overflow() {
113        assert_eq!(u8::from_f64(300.0), 255);
114        assert_eq!(u8::from_f64(-10.0), 0);
115    }
116
117    #[test]
118    fn test_u8_from_f64_nan() {
119        assert_eq!(u8::from_f64(f64::NAN), 0);
120    }
121
122    #[test]
123    fn test_i16_from_f64_clamp() {
124        assert_eq!(i16::from_f64(40000.0), i16::MAX);
125        assert_eq!(i16::from_f64(-40000.0), i16::MIN);
126    }
127
128    #[test]
129    fn test_u16_from_f64_negative() {
130        assert_eq!(u16::from_f64(-5.0), 0);
131    }
132
133    #[test]
134    fn test_f32_roundtrip() {
135        let v = 3.14f64;
136        let cast = f32::from_f64(v);
137        assert!((cast - 3.14f32).abs() < 1e-5);
138        assert!((cast.to_f64() - v).abs() < 1e-5);
139    }
140
141    #[test]
142    fn test_f64_identity() {
143        let v = 1.23456789012345;
144        assert_eq!(f64::from_f64(v), v);
145        assert_eq!(v.to_f64(), v);
146    }
147
148    #[test]
149    fn test_i64_large_values() {
150        assert_eq!(i64::from_f64(1e18), 1_000_000_000_000_000_000i64);
151    }
152
153    #[test]
154    fn test_u32_nan() {
155        assert_eq!(u32::from_f64(f64::NAN), 0);
156    }
157
158    #[test]
159    fn test_with_buffer_macro() {
160        let buf = NDDataBuffer::U8(vec![1, 2, 3]);
161        let sum: f64 = with_buffer!(&buf, |v| {
162            v.iter().map(|x| PixelCast::to_f64(*x)).sum()
163        });
164        assert_eq!(sum, 6.0);
165    }
166}