1use crate::math::{powf, powi};
10use alloc::vec::Vec;
11
12#[derive(Clone, Copy, PartialEq, Eq, Debug)]
16pub enum PixelLayout {
17 Bgra8,
20 Rgba8,
22 Rgba16F {
25 scrgb: bool,
27 },
28 A2b10g10r10,
31}
32
33pub fn decode_to_rgba8(raw: &[u8], layout: PixelLayout) -> Vec<u8> {
37 match layout {
38 PixelLayout::Bgra8 => decode_8bit(raw, true),
39 PixelLayout::Rgba8 => decode_8bit(raw, false),
40 PixelLayout::Rgba16F { scrgb } => decode_rgba16f(raw, scrgb),
41 PixelLayout::A2b10g10r10 => decode_a2b10g10r10(raw),
42 }
43}
44
45fn decode_8bit(raw: &[u8], bgra: bool) -> Vec<u8> {
48 let mut out = Vec::with_capacity(raw.len());
49 for px in raw.chunks_exact(4) {
50 if bgra {
51 out.extend_from_slice(&[px[2], px[1], px[0], 255]);
52 } else {
53 out.extend_from_slice(&[px[0], px[1], px[2], 255]);
54 }
55 }
56 out
57}
58
59fn decode_rgba16f(raw: &[u8], scrgb: bool) -> Vec<u8> {
64 let mut out = Vec::with_capacity(raw.len() / 2);
65 for px in raw.chunks_exact(8) {
66 let r = f16_to_f32(u16::from_le_bytes([px[0], px[1]]));
67 let g = f16_to_f32(u16::from_le_bytes([px[2], px[3]]));
68 let b = f16_to_f32(u16::from_le_bytes([px[4], px[5]]));
69 if scrgb {
70 out.extend_from_slice(&[
71 linear_to_srgb8(r),
72 linear_to_srgb8(g),
73 linear_to_srgb8(b),
74 255,
75 ]);
76 } else {
77 out.extend_from_slice(&[unorm_to_u8(r), unorm_to_u8(g), unorm_to_u8(b), 255]);
78 }
79 }
80 out
81}
82
83fn decode_a2b10g10r10(raw: &[u8]) -> Vec<u8> {
87 let mut out = Vec::with_capacity(raw.len());
88 for px in raw.chunks_exact(4) {
89 let v = u32::from_le_bytes([px[0], px[1], px[2], px[3]]);
90 let r = v & 0x3ff;
91 let g = (v >> 10) & 0x3ff;
92 let b = (v >> 20) & 0x3ff;
93 out.extend_from_slice(&[u10_to_u8(r), u10_to_u8(g), u10_to_u8(b), 255]);
94 }
95 out
96}
97
98fn f16_to_f32(h: u16) -> f32 {
101 let sign = if (h >> 15) & 1 == 1 { -1.0 } else { 1.0 };
102 let exp = (h >> 10) & 0x1f;
103 let mant = (h & 0x3ff) as f32;
104 let val = match exp {
105 0 => mant * powi(2.0, -24),
106 0x1f => {
107 if mant == 0.0 {
108 f32::INFINITY
109 } else {
110 f32::NAN
111 }
112 }
113 _ => (1.0 + mant / 1024.0) * powi(2.0, exp as i32 - 15),
114 };
115 sign * val
116}
117
118fn linear_to_srgb8(c: f32) -> u8 {
120 if c.is_nan() {
121 return 0;
122 }
123 let c = c.clamp(0.0, 1.0);
124 let s = if c <= 0.0031308 {
125 12.92 * c
126 } else {
127 1.055 * powf(c, 1.0 / 2.4) - 0.055
128 };
129 unorm_to_u8(s)
130}
131
132fn unorm_to_u8(c: f32) -> u8 {
134 if c.is_nan() {
135 return 0;
136 }
137 (c.clamp(0.0, 1.0) * 255.0 + 0.5) as u8
138}
139
140fn u10_to_u8(v: u32) -> u8 {
142 ((v * 255 + 511) / 1023) as u8
143}
144
145#[cfg(test)]
146mod tests {
147 use super::*;
148 use alloc::vec;
149
150 #[test]
151 fn f16_round_trips_reference_values() {
152 assert_eq!(f16_to_f32(0x0000), 0.0); assert_eq!(f16_to_f32(0x3c00), 1.0); assert_eq!(f16_to_f32(0x3800), 0.5); assert_eq!(f16_to_f32(0x4000), 2.0); assert_eq!(f16_to_f32(0xbc00), -1.0); assert!(f16_to_f32(0x7c00).is_infinite()); assert!(f16_to_f32(0x7e00).is_nan()); }
160
161 #[test]
162 fn bgra8_is_swizzled_and_made_opaque() {
163 let raw = [10u8, 20, 30, 40];
165 let out = decode_to_rgba8(&raw, PixelLayout::Bgra8);
166 assert_eq!(out, vec![30, 20, 10, 255]);
167 }
168
169 #[test]
170 fn rgba8_passes_through_with_forced_alpha() {
171 let raw = [30u8, 20, 10, 40];
172 let out = decode_to_rgba8(&raw, PixelLayout::Rgba8);
173 assert_eq!(out, vec![30, 20, 10, 255]);
174 }
175
176 #[test]
177 fn scrgb_float_applies_srgb_oetf() {
178 let mut raw = Vec::new();
180 for h in [0x3c00u16, 0x3800, 0x0000, 0x3c00] {
181 raw.extend_from_slice(&h.to_le_bytes());
182 }
183 let out = decode_to_rgba8(&raw, PixelLayout::Rgba16F { scrgb: true });
184 assert_eq!(out[0], 255); assert!((out[1] as i32 - 188).abs() <= 1); assert_eq!(out[2], 0); assert_eq!(out[3], 255); }
189
190 #[test]
191 fn scrgb_float_clamps_out_of_range() {
192 let mut raw = Vec::new();
194 for h in [0x4000u16, 0xbc00, 0x0000, 0x3c00] {
195 raw.extend_from_slice(&h.to_le_bytes());
196 }
197 let out = decode_to_rgba8(&raw, PixelLayout::Rgba16F { scrgb: true });
198 assert_eq!(out[0], 255); assert_eq!(out[1], 0); }
201
202 #[test]
203 fn pq_float_passes_code_values_through() {
204 let mut raw = Vec::new();
206 for h in [0x3c00u16, 0x3800, 0x0000, 0x3c00] {
207 raw.extend_from_slice(&h.to_le_bytes());
208 }
209 let out = decode_to_rgba8(&raw, PixelLayout::Rgba16F { scrgb: false });
210 assert_eq!(out[0], 255); assert_eq!(out[1], 128); assert_eq!(out[2], 0); assert_eq!(out[3], 255);
214 }
215
216 #[test]
217 fn a2b10g10r10_unpacks_channels() {
218 let v: u32 = 1023 | (1023 << 20) | (3 << 30);
220 let out = decode_to_rgba8(&v.to_le_bytes(), PixelLayout::A2b10g10r10);
221 assert_eq!(out, vec![255, 0, 255, 255]);
222 }
223}