pel 0.1.0

OpenGL backed framebuffer
Documentation
use std::{f32, ops::Mul};

// Pel indices.
// Current format - ARGB.
const A: usize = 0;
const R: usize = 1;
const G: usize = 2;
const B: usize = 3;

// Error for mapping f32 to u8 as accurately as possible while focusing on performance.
const EPSILON: f32 = f32::EPSILON * 100f32;

/// Pixel.
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
#[repr(transparent)]
pub struct Pel(u32);

impl Pel {
    pub fn a(self) -> u8 {
        self.0.to_le_bytes()[A]
    }

    pub fn r(self) -> u8 {
        self.0.to_le_bytes()[R]
    }

    pub fn g(self) -> u8 {
        self.0.to_le_bytes()[G]
    }

    pub fn b(self) -> u8 {
        self.0.to_le_bytes()[B]
    }
}

impl Default for Pel {
    fn default() -> Self {
        [255u8, 0, 0, 0].into()
    }
}

impl From<u32> for Pel {
    fn from(color: u32) -> Self {
        Self(color)
    }
}

impl From<Pel> for u32 {
    fn from(pel: Pel) -> Self {
        pel.0
    }
}

impl From<[u8; 4]> for Pel {
    fn from(bytes: [u8; 4]) -> Self {
        Self(u32::from_le_bytes(bytes))
    }
}

impl From<Pel> for [u8; 4] {
    fn from(pel: Pel) -> Self {
        pel.0.to_le_bytes()
    }
}

impl From<[f32; 4]> for Pel {
    fn from(floats: [f32; 4]) -> Self {
        Self::from([
            floats[A].mul(256f32 - EPSILON) as u8,
            floats[R].mul(256f32 - EPSILON) as u8,
            floats[G].mul(256f32 - EPSILON) as u8,
            floats[B].mul(256f32 - EPSILON) as u8,
        ])
    }
}

impl From<Pel> for [f32; 4] {
    fn from(pel: Pel) -> Self {
        let bytes: [u8; 4] = pel.into();
        [
            bytes[A] as f32 / 255f32,
            bytes[R] as f32 / 255f32,
            bytes[G] as f32 / 255f32,
            bytes[B] as f32 / 255f32,
        ]
    }
}