1pub 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#[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#[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}