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
98pub fn f16_to_f32(h: u16) -> f32 {
102 let sign = if (h >> 15) & 1 == 1 { -1.0 } else { 1.0 };
103 let exp = (h >> 10) & 0x1f;
104 let mant = (h & 0x3ff) as f32;
105 let val = match exp {
106 0 => mant * powi(2.0, -24),
107 0x1f => {
108 if mant == 0.0 {
109 f32::INFINITY
110 } else {
111 f32::NAN
112 }
113 }
114 _ => (1.0 + mant / 1024.0) * powi(2.0, exp as i32 - 15),
115 };
116 sign * val
117}
118
119fn linear_to_srgb8(c: f32) -> u8 {
121 if c.is_nan() {
122 return 0;
123 }
124 let c = c.clamp(0.0, 1.0);
125 let s = if c <= 0.0031308 {
126 12.92 * c
127 } else {
128 1.055 * powf(c, 1.0 / 2.4) - 0.055
129 };
130 unorm_to_u8(s)
131}
132
133fn unorm_to_u8(c: f32) -> u8 {
135 if c.is_nan() {
136 return 0;
137 }
138 (c.clamp(0.0, 1.0) * 255.0 + 0.5) as u8
139}
140
141fn u10_to_u8(v: u32) -> u8 {
143 ((v * 255 + 511) / 1023) as u8
144}
145
146#[cfg(test)]
147mod tests {
148 use super::*;
149 use alloc::vec;
150
151 #[test]
152 fn f16_round_trips_reference_values() {
153 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()); }
161
162 #[test]
163 fn bgra8_is_swizzled_and_made_opaque() {
164 let raw = [10u8, 20, 30, 40];
166 let out = decode_to_rgba8(&raw, PixelLayout::Bgra8);
167 assert_eq!(out, vec![30, 20, 10, 255]);
168 }
169
170 #[test]
171 fn rgba8_passes_through_with_forced_alpha() {
172 let raw = [30u8, 20, 10, 40];
173 let out = decode_to_rgba8(&raw, PixelLayout::Rgba8);
174 assert_eq!(out, vec![30, 20, 10, 255]);
175 }
176
177 #[test]
178 fn scrgb_float_applies_srgb_oetf() {
179 let mut raw = Vec::new();
181 for h in [0x3c00u16, 0x3800, 0x0000, 0x3c00] {
182 raw.extend_from_slice(&h.to_le_bytes());
183 }
184 let out = decode_to_rgba8(&raw, PixelLayout::Rgba16F { scrgb: true });
185 assert_eq!(out[0], 255); assert!((out[1] as i32 - 188).abs() <= 1); assert_eq!(out[2], 0); assert_eq!(out[3], 255); }
190
191 #[test]
192 fn scrgb_float_clamps_out_of_range() {
193 let mut raw = Vec::new();
195 for h in [0x4000u16, 0xbc00, 0x0000, 0x3c00] {
196 raw.extend_from_slice(&h.to_le_bytes());
197 }
198 let out = decode_to_rgba8(&raw, PixelLayout::Rgba16F { scrgb: true });
199 assert_eq!(out[0], 255); assert_eq!(out[1], 0); }
202
203 #[test]
204 fn pq_float_passes_code_values_through() {
205 let mut raw = Vec::new();
207 for h in [0x3c00u16, 0x3800, 0x0000, 0x3c00] {
208 raw.extend_from_slice(&h.to_le_bytes());
209 }
210 let out = decode_to_rgba8(&raw, PixelLayout::Rgba16F { scrgb: false });
211 assert_eq!(out[0], 255); assert_eq!(out[1], 128); assert_eq!(out[2], 0); assert_eq!(out[3], 255);
215 }
216
217 #[test]
218 fn a2b10g10r10_unpacks_channels() {
219 let v: u32 = 1023 | (1023 << 20) | (3 << 30);
221 let out = decode_to_rgba8(&v.to_le_bytes(), PixelLayout::A2b10g10r10);
222 assert_eq!(out, vec![255, 0, 255, 255]);
223 }
224}