use bytemuck::{Pod, Zeroable};
use half::f16;
#[repr(C, align(4))]
#[derive(PartialEq, Eq, Clone, Copy, Debug, Pod, Zeroable)]
pub struct RGBE8 {
pub r: u8,
pub g: u8,
pub b: u8,
pub e: u8,
}
#[repr(transparent)]
#[derive(PartialEq, Eq, Clone, Copy, Debug, Pod, Zeroable)]
pub struct RGB9E5(pub u32);
#[repr(C, align(8))]
#[derive(PartialEq, Clone, Copy, Debug, Pod, Zeroable)]
pub struct RGBA16F {
pub r: f16,
pub g: f16,
pub b: f16,
pub a: f16,
}
impl RGB9E5 {
pub fn pack(rgb: [f32;3]) -> Self {
const MAX_F14:f32 = (0x1FFu32 << 7) as f32;
const MIN_NORM_F14:f32 = 1.0 / ((1u32 << 16) as f32);
let r = rgb[0].clamp(0.0, MAX_F14);
let g = rgb[1].clamp(0.0, MAX_F14);
let b = rgb[2].clamp(0.0, MAX_F14);
let max_channel = MIN_NORM_F14.max(r).max(g).max(b);
let bias_bits = (max_channel.to_bits() + 0x07804000) & 0x7F800000;
let bias = f32::from_bits(bias_bits);
let r_bits = (r + bias).to_bits() & 0x1ff;
let g_bits = (g + bias).to_bits() & 0x1ff;
let b_bits = (b + bias).to_bits() & 0x1ff;
let e_bits = bias_bits.wrapping_shr(4) + 0x10000000;
RGB9E5(e_bits | (b_bits << 18) | (g_bits << 9) | r_bits)
}
pub fn unpack(self) -> [f32;3] {
let bias = (((self.0 & 0xf8000000) >> 27) as f32 - 15.0).exp2();
let r = ((self.0) & 0x000001ff) as f32 * bias / 512.0;
let g = ((self.0 >> 9) & 0x000001ff) as f32 * bias / 512.0;
let b = ((self.0 >> 18) & 0x000001ff) as f32 * bias / 512.0;
[r, g, b]
}
}
impl RGBE8 {
pub fn pack(rgb: [f32;3]) -> Self {
let max_channel = f32::MIN_POSITIVE.max(rgb[0]).max(rgb[1]).max(rgb[2]);
let bias = f32::from_bits((max_channel.to_bits() + 0x00808000) & 0x7F800000);
let r = ((rgb[0] / bias) * 256.0).round().clamp(0.0,255.0) as u8;
let g = ((rgb[1] / bias) * 256.0).round().clamp(0.0,255.0) as u8;
let b = ((rgb[2] / bias) * 256.0).round().clamp(0.0,255.0) as u8;
let e = ((bias.to_bits() >> 23) + 1).clamp(0,255) as u8;
RGBE8{r, g, b, e}
}
pub fn unpack(self) -> [f32;3] {
let bias = ((self.e as f32) - 128.0).exp2();
let r = ((self.r as f32) / 256.0) * bias;
let g = ((self.g as f32) / 256.0) * bias;
let b = ((self.b as f32) / 256.0) * bias;
[r,g,b]
}
pub fn repack_rgb9e5(self) -> RGB9E5 {
let e = (self.e as i32) - 128;
if (-15..=15).contains(&e) {
let e5 = (e + 15) as u32;
let r = self.r as u32;
let g = self.g as u32;
let b = self.b as u32;
RGB9E5((e5 << 27) | (b << 19) | (g << 10) | (r << 1))
} else {
RGB9E5::pack(self.unpack())
}
}
}
impl RGBA16F {
pub fn from_f32(c: [f32; 4]) -> Self {
Self {
r: f16::from_f32(c[0]),
g: f16::from_f32(c[1]),
b: f16::from_f32(c[2]),
a: f16::from_f32(c[3]),
}
}
pub fn into_rgb9e5(self) -> RGB9E5 {
RGB9E5::pack([self.r.to_f32(), self.g.to_f32(), self.b.to_f32()])
}
pub fn into_rgbe8(self) -> RGBE8 {
RGBE8::pack([self.r.to_f32(), self.g.to_f32(), self.b.to_f32()])
}
}
impl From<RGBA16F> for [f32; 4] {
fn from(color: RGBA16F) -> Self {
[color.r.to_f32(), color.g.to_f32(), color.b.to_f32(), color.a.to_f32()]
}
}
impl From<RGBE8> for [f32; 3] {
fn from(color: RGBE8) -> Self {
color.unpack()
}
}
impl From<RGB9E5> for [f32; 3] {
fn from(color: RGB9E5) -> Self {
color.unpack()
}
}
impl From<RGB9E5> for RGBA16F {
fn from(color: RGB9E5) -> Self {
let col32 = color.unpack();
RGBA16F::from_f32([col32[0], col32[1], col32[2], 1.0])
}
}