pub trait Sample:
Copy + Clone + PartialOrd + Default + Send + Sync + 'static + Into<f32> + FromF32
{
const MAX: Self;
const MIN: Self;
const BIT_DEPTH: u8;
fn clamp_sample(self) -> Self;
fn from_normalized(v: f32) -> Self;
fn to_normalized(self) -> f32;
}
pub trait FromF32 {
fn from_f32(v: f32) -> Self;
}
impl FromF32 for u8 {
#[inline]
fn from_f32(v: f32) -> Self {
v.round().clamp(0.0, 255.0) as u8
}
}
impl FromF32 for u16 {
#[inline]
fn from_f32(v: f32) -> Self {
v.round().clamp(0.0, 65535.0) as u16
}
}
impl FromF32 for f32 {
#[inline]
fn from_f32(v: f32) -> Self {
v
}
}
impl Sample for u8 {
const MAX: Self = 255;
const MIN: Self = 0;
const BIT_DEPTH: u8 = 8;
#[inline]
fn clamp_sample(self) -> Self {
self }
#[inline]
fn from_normalized(v: f32) -> Self {
(v * 255.0).round().clamp(0.0, 255.0) as u8
}
#[inline]
fn to_normalized(self) -> f32 {
self as f32 / 255.0
}
}
impl Sample for u16 {
const MAX: Self = 65535;
const MIN: Self = 0;
const BIT_DEPTH: u8 = 16;
#[inline]
fn clamp_sample(self) -> Self {
self }
#[inline]
fn from_normalized(v: f32) -> Self {
(v * 65535.0).round().clamp(0.0, 65535.0) as u16
}
#[inline]
fn to_normalized(self) -> f32 {
self as f32 / 65535.0
}
}
impl Sample for f32 {
const MAX: Self = 1.0;
const MIN: Self = 0.0;
const BIT_DEPTH: u8 = 32;
#[inline]
fn clamp_sample(self) -> Self {
self.clamp(0.0, 1.0)
}
#[inline]
fn from_normalized(v: f32) -> Self {
v
}
#[inline]
fn to_normalized(self) -> f32 {
self
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Rgb<S: Sample> {
pub r: S,
pub g: S,
pub b: S,
}
impl<S: Sample> Rgb<S> {
#[inline]
pub fn new(r: S, g: S, b: S) -> Self {
Self { r, g, b }
}
#[inline]
pub fn to_f32(self) -> Rgb<f32> {
Rgb {
r: self.r.to_normalized(),
g: self.g.to_normalized(),
b: self.b.to_normalized(),
}
}
#[inline]
pub fn from_f32(src: Rgb<f32>) -> Self {
Rgb {
r: S::from_normalized(src.r),
g: S::from_normalized(src.g),
b: S::from_normalized(src.b),
}
}
}
impl<S: Sample> Default for Rgb<S> {
fn default() -> Self {
Self {
r: S::default(),
g: S::default(),
b: S::default(),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Rgba<S: Sample> {
pub r: S,
pub g: S,
pub b: S,
pub a: S,
}
impl<S: Sample> Rgba<S> {
#[inline]
pub fn new(r: S, g: S, b: S, a: S) -> Self {
Self { r, g, b, a }
}
#[inline]
pub fn to_rgb(self) -> Rgb<S> {
Rgb::new(self.r, self.g, self.b)
}
}
impl<S: Sample> Default for Rgba<S> {
fn default() -> Self {
Self {
r: S::default(),
g: S::default(),
b: S::default(),
a: S::MAX,
}
}
}
pub type Rgb8 = Rgb<u8>;
pub type Rgb16 = Rgb<u16>;
pub type RgbF32 = Rgb<f32>;
pub type Rgba8 = Rgba<u8>;
pub type Rgba16 = Rgba<u16>;
pub type RgbaF32 = Rgba<f32>;
pub fn rgb16_from_interleaved(data: &[u16]) -> Vec<Rgb16> {
debug_assert!(data.len().is_multiple_of(3));
data.chunks_exact(3)
.map(|c| Rgb16::new(c[0], c[1], c[2]))
.collect()
}
pub fn rgb16_to_interleaved(pixels: &[Rgb16]) -> Vec<u16> {
let mut data = Vec::with_capacity(pixels.len() * 3);
for p in pixels {
data.push(p.r);
data.push(p.g);
data.push(p.b);
}
data
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_u8_sample() {
assert_eq!(u8::MAX, 255);
assert_eq!(u8::from_normalized(1.0), 255);
assert_eq!(u8::from_normalized(0.0), 0);
assert_eq!(u8::from_normalized(0.5), 128);
assert!((128u8.to_normalized() - 0.502).abs() < 0.01);
}
#[test]
fn test_u16_sample() {
assert_eq!(u16::MAX, 65535);
assert_eq!(u16::from_normalized(1.0), 65535);
assert_eq!(u16::from_normalized(0.0), 0);
let half = u16::from_normalized(0.5);
assert!((half as i32 - 32768).abs() <= 1);
}
#[test]
fn test_f32_sample() {
assert_eq!(f32::from_normalized(0.5), 0.5);
assert_eq!((0.5f32).to_normalized(), 0.5);
assert_eq!((1.5f32).clamp_sample(), 1.0);
assert_eq!((-0.5f32).clamp_sample(), 0.0);
}
#[test]
fn test_rgb_pixel() {
let p = Rgb16::new(1000, 2000, 3000);
assert_eq!(p.r, 1000);
assert_eq!(p.g, 2000);
assert_eq!(p.b, 3000);
}
#[test]
fn test_rgb_roundtrip() {
let p = Rgb16::new(1000, 2000, 3000);
let f = p.to_f32();
let back = Rgb16::from_f32(f);
assert!((back.r as i32 - 1000).abs() <= 1);
assert!((back.g as i32 - 2000).abs() <= 1);
assert!((back.b as i32 - 3000).abs() <= 1);
}
#[test]
fn test_rgba_default() {
let p = Rgba16::default();
assert_eq!(p.r, 0);
assert_eq!(p.g, 0);
assert_eq!(p.b, 0);
assert_eq!(p.a, 65535);
}
#[test]
fn test_interleaved_roundtrip() {
let data = vec![100u16, 200, 300, 400, 500, 600];
let pixels = rgb16_from_interleaved(&data);
assert_eq!(pixels.len(), 2);
assert_eq!(pixels[0], Rgb16::new(100, 200, 300));
let back = rgb16_to_interleaved(&pixels);
assert_eq!(back, data);
}
}