1pub trait Sample:
11 Copy + Clone + PartialOrd + Default + Send + Sync + 'static + Into<f32> + FromF32
12{
13 const MAX: Self;
15
16 const MIN: Self;
18
19 const BIT_DEPTH: u8;
21
22 fn clamp_sample(self) -> Self;
24
25 fn from_normalized(v: f32) -> Self;
27
28 fn to_normalized(self) -> f32;
30}
31
32pub trait FromF32 {
34 fn from_f32(v: f32) -> Self;
35}
36
37impl FromF32 for u8 {
38 #[inline]
39 fn from_f32(v: f32) -> Self {
40 v.round().clamp(0.0, 255.0) as u8
41 }
42}
43
44impl FromF32 for u16 {
45 #[inline]
46 fn from_f32(v: f32) -> Self {
47 v.round().clamp(0.0, 65535.0) as u16
48 }
49}
50
51impl FromF32 for f32 {
52 #[inline]
53 fn from_f32(v: f32) -> Self {
54 v
55 }
56}
57
58impl Sample for u8 {
59 const MAX: Self = 255;
60 const MIN: Self = 0;
61 const BIT_DEPTH: u8 = 8;
62
63 #[inline]
64 fn clamp_sample(self) -> Self {
65 self }
67
68 #[inline]
69 fn from_normalized(v: f32) -> Self {
70 (v * 255.0).round().clamp(0.0, 255.0) as u8
71 }
72
73 #[inline]
74 fn to_normalized(self) -> f32 {
75 self as f32 / 255.0
76 }
77}
78
79impl Sample for u16 {
80 const MAX: Self = 65535;
81 const MIN: Self = 0;
82 const BIT_DEPTH: u8 = 16;
83
84 #[inline]
85 fn clamp_sample(self) -> Self {
86 self }
88
89 #[inline]
90 fn from_normalized(v: f32) -> Self {
91 (v * 65535.0).round().clamp(0.0, 65535.0) as u16
92 }
93
94 #[inline]
95 fn to_normalized(self) -> f32 {
96 self as f32 / 65535.0
97 }
98}
99
100impl Sample for f32 {
101 const MAX: Self = 1.0;
102 const MIN: Self = 0.0;
103 const BIT_DEPTH: u8 = 32;
104
105 #[inline]
106 fn clamp_sample(self) -> Self {
107 self.clamp(0.0, 1.0)
108 }
109
110 #[inline]
111 fn from_normalized(v: f32) -> Self {
112 v
113 }
114
115 #[inline]
116 fn to_normalized(self) -> f32 {
117 self
118 }
119}
120
121#[derive(Debug, Clone, Copy, PartialEq)]
123pub struct Rgb<S: Sample> {
124 pub r: S,
125 pub g: S,
126 pub b: S,
127}
128
129impl<S: Sample> Rgb<S> {
130 #[inline]
132 pub fn new(r: S, g: S, b: S) -> Self {
133 Self { r, g, b }
134 }
135
136 #[inline]
138 pub fn to_f32(self) -> Rgb<f32> {
139 Rgb {
140 r: self.r.to_normalized(),
141 g: self.g.to_normalized(),
142 b: self.b.to_normalized(),
143 }
144 }
145
146 #[inline]
148 pub fn from_f32(src: Rgb<f32>) -> Self {
149 Rgb {
150 r: S::from_normalized(src.r),
151 g: S::from_normalized(src.g),
152 b: S::from_normalized(src.b),
153 }
154 }
155}
156
157impl<S: Sample> Default for Rgb<S> {
158 fn default() -> Self {
159 Self {
160 r: S::default(),
161 g: S::default(),
162 b: S::default(),
163 }
164 }
165}
166
167#[derive(Debug, Clone, Copy, PartialEq)]
169pub struct Rgba<S: Sample> {
170 pub r: S,
171 pub g: S,
172 pub b: S,
173 pub a: S,
174}
175
176impl<S: Sample> Rgba<S> {
177 #[inline]
179 pub fn new(r: S, g: S, b: S, a: S) -> Self {
180 Self { r, g, b, a }
181 }
182
183 #[inline]
185 pub fn to_rgb(self) -> Rgb<S> {
186 Rgb::new(self.r, self.g, self.b)
187 }
188}
189
190impl<S: Sample> Default for Rgba<S> {
191 fn default() -> Self {
192 Self {
193 r: S::default(),
194 g: S::default(),
195 b: S::default(),
196 a: S::MAX,
197 }
198 }
199}
200
201pub type Rgb8 = Rgb<u8>;
203pub type Rgb16 = Rgb<u16>;
204pub type RgbF32 = Rgb<f32>;
205pub type Rgba8 = Rgba<u8>;
206pub type Rgba16 = Rgba<u16>;
207pub type RgbaF32 = Rgba<f32>;
208
209pub fn rgb16_from_interleaved(data: &[u16]) -> Vec<Rgb16> {
211 debug_assert!(data.len().is_multiple_of(3));
212 data.chunks_exact(3)
213 .map(|c| Rgb16::new(c[0], c[1], c[2]))
214 .collect()
215}
216
217pub fn rgb16_to_interleaved(pixels: &[Rgb16]) -> Vec<u16> {
219 let mut data = Vec::with_capacity(pixels.len() * 3);
220 for p in pixels {
221 data.push(p.r);
222 data.push(p.g);
223 data.push(p.b);
224 }
225 data
226}
227
228#[cfg(test)]
229mod tests {
230 use super::*;
231
232 #[test]
233 fn test_u8_sample() {
234 assert_eq!(u8::MAX, 255);
235 assert_eq!(u8::from_normalized(1.0), 255);
236 assert_eq!(u8::from_normalized(0.0), 0);
237 assert_eq!(u8::from_normalized(0.5), 128);
238 assert!((128u8.to_normalized() - 0.502).abs() < 0.01);
239 }
240
241 #[test]
242 fn test_u16_sample() {
243 assert_eq!(u16::MAX, 65535);
244 assert_eq!(u16::from_normalized(1.0), 65535);
245 assert_eq!(u16::from_normalized(0.0), 0);
246 let half = u16::from_normalized(0.5);
247 assert!((half as i32 - 32768).abs() <= 1);
248 }
249
250 #[test]
251 fn test_f32_sample() {
252 assert_eq!(f32::from_normalized(0.5), 0.5);
253 assert_eq!((0.5f32).to_normalized(), 0.5);
254 assert_eq!((1.5f32).clamp_sample(), 1.0);
255 assert_eq!((-0.5f32).clamp_sample(), 0.0);
256 }
257
258 #[test]
259 fn test_rgb_pixel() {
260 let p = Rgb16::new(1000, 2000, 3000);
261 assert_eq!(p.r, 1000);
262 assert_eq!(p.g, 2000);
263 assert_eq!(p.b, 3000);
264 }
265
266 #[test]
267 fn test_rgb_roundtrip() {
268 let p = Rgb16::new(1000, 2000, 3000);
269 let f = p.to_f32();
270 let back = Rgb16::from_f32(f);
271 assert!((back.r as i32 - 1000).abs() <= 1);
272 assert!((back.g as i32 - 2000).abs() <= 1);
273 assert!((back.b as i32 - 3000).abs() <= 1);
274 }
275
276 #[test]
277 fn test_rgba_default() {
278 let p = Rgba16::default();
279 assert_eq!(p.r, 0);
280 assert_eq!(p.g, 0);
281 assert_eq!(p.b, 0);
282 assert_eq!(p.a, 65535);
283 }
284
285 #[test]
286 fn test_interleaved_roundtrip() {
287 let data = vec![100u16, 200, 300, 400, 500, 600];
288 let pixels = rgb16_from_interleaved(&data);
289 assert_eq!(pixels.len(), 2);
290 assert_eq!(pixels[0], Rgb16::new(100, 200, 300));
291 let back = rgb16_to_interleaved(&pixels);
292 assert_eq!(back, data);
293 }
294}