ombre 0.6.7

Shadowy game and graphics library for Rust
Documentation
//! Color types.
use std::fmt;
use std::mem;
use std::mem::ManuallyDrop;
use std::str::FromStr;

use crate::math::Zero;

///////////////////////////////////////////////////////////////////////////
// Rgba8
///////////////////////////////////////////////////////////////////////////

/// RGBA color with 8-bit channels.
#[repr(C)]
#[derive(Copy, Clone, PartialOrd, Ord, PartialEq, Eq, Debug, Default)]
pub struct Rgba8 {
    /// Red channel.
    pub r: u8,
    /// Green channel.
    pub g: u8,
    /// Blue channel.
    pub b: u8,
    /// Alpha channel.
    pub a: u8,
}

impl Rgba8 {
    /// Create a new [`Rgba8`] color from individual channels.
    pub const fn new(r: u8, g: u8, b: u8, a: u8) -> Self {
        Self { r, g, b, a }
    }

    /// Invert the color.
    pub fn invert(self) -> Self {
        Self::new(0xff - self.r, 0xff - self.g, 0xff - self.b, self.a)
    }

    /// Return the color with a changed alpha.
    ///
    /// ```
    /// use ombre::gfx::{Rgba8, Color as _};
    ///
    /// let c = Rgba8::WHITE;
    /// assert_eq!(c.alpha(0x88), Rgba8::new(c.r, c.g, c.b, 0x88))
    /// ```
    pub const fn alpha(self, a: u8) -> Self {
        Self::new(self.r, self.g, self.b, a)
    }

    /// Given a byte slice, returns a slice of [`Rgba8`] values.
    pub fn align<'a, S: 'a, T: AsRef<[S]> + ?Sized>(bytes: &'a T) -> &'a [Rgba8] {
        let bytes = bytes.as_ref();
        let (head, body, tail) = unsafe { bytes.align_to::<Rgba8>() };

        if !(head.is_empty() && tail.is_empty()) {
            panic!("Rgba8::align: input is not a valid `Rgba8` buffer");
        }
        body
    }

    /// Given a byte vector, returns a vector of [`Rgba8`] values.
    pub fn into_vec(bytes: Vec<u8>) -> Vec<Self> {
        if bytes.len() % mem::size_of::<Rgba8>() != 0 {
            panic!("Rgba8::into_vec: input is not a valid `Rgba8` buffer");
        }
        assert_eq!(bytes.capacity() % 4, 0);

        let bytes = ManuallyDrop::new(bytes);
        let ptr = bytes.as_ptr();
        let length = bytes.len() / 4;
        let capacity = bytes.capacity() / 4;

        unsafe { Vec::from_raw_parts(ptr as *mut Rgba8, length, capacity) }
    }
}

impl Zero for Rgba8 {
    const ZERO: Self = Rgba8::TRANSPARENT;

    fn is_zero(&self) -> bool {
        self == &Self::ZERO
    }
}

/// ```
/// use ombre::gfx::Rgba8;
///
/// assert_eq!(format!("{}", Rgba8::new(0xff, 0x0, 0xa, 0xff)), "#ff000a");
/// ```
impl fmt::Display for Rgba8 {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "#{:02x}{:02x}{:02x}", self.r, self.g, self.b)?;
        if self.a != 0xff {
            write!(f, "{:02x}", self.a)?;
        }
        Ok(())
    }
}

/// ```
/// use ombre::gfx::{Rgba8, Rgba, Color as _};
///
/// assert_eq!(Rgba8::from(Rgba::RED), Rgba8::RED);
/// ```
impl From<Rgba<f32>> for Rgba8 {
    fn from(rgba: Rgba<f32>) -> Self {
        Self {
            r: (rgba.r * 255.0).round() as u8,
            g: (rgba.g * 255.0).round() as u8,
            b: (rgba.b * 255.0).round() as u8,
            a: (rgba.a * 255.0).round() as u8,
        }
    }
}

impl From<u32> for Rgba8 {
    fn from(rgba: u32) -> Self {
        unsafe { std::mem::transmute(rgba) }
    }
}

impl From<Rgba8> for u32 {
    fn from(rgba: Rgba8) -> Self {
        unsafe { std::mem::transmute(rgba) }
    }
}

impl FromStr for Rgba8 {
    type Err = std::num::ParseIntError;

    /// Parse a color code of the form `#ffffff` into an
    /// instance of `Rgba8`. The alpha is set to `0xff` if the
    /// string does not contain an alpha channel.
    fn from_str(hex_code: &str) -> Result<Self, Self::Err> {
        let r: u8 = u8::from_str_radix(&hex_code[1..3], 16)?;
        let g: u8 = u8::from_str_radix(&hex_code[3..5], 16)?;
        let b: u8 = u8::from_str_radix(&hex_code[5..7], 16)?;
        let a: u8 = if hex_code.len() >= 9 {
            u8::from_str_radix(&hex_code[7..9], 16)?
        } else {
            0xff
        };
        Ok(Rgba8 { r, g, b, a })
    }
}

//////////////////////////////////////////////////////////////////////////////
// Rgb8
//////////////////////////////////////////////////////////////////////////////

/// An RGB 8-bit color. Used when the alpha value isn't used.
#[repr(C)]
#[derive(Copy, Clone)]
pub struct Rgb8 {
    pub r: u8,
    pub g: u8,
    pub b: u8,
}

impl From<Rgba8> for Rgb8 {
    fn from(rgba: Rgba8) -> Self {
        Self {
            r: rgba.r,
            g: rgba.g,
            b: rgba.b,
        }
    }
}

impl fmt::Display for Rgb8 {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "#{:02X}{:02X}{:02X}", self.r, self.g, self.b)
    }
}

//////////////////////////////////////////////////////////////////////////////
// Rgba
//////////////////////////////////////////////////////////////////////////////

/// A generic RGBA color.
#[repr(C)]
#[derive(Copy, Clone, PartialEq, Debug, Default)]
pub struct Rgba<T> {
    /// Red channel.
    pub r: T,
    /// Green channel.
    pub g: T,
    /// Blue channel.
    pub b: T,
    /// Alpha channel.
    pub a: T,
}

impl<T> Rgba<T> {
    /// Create a new `Rgba` color.
    pub const fn new(r: T, g: T, b: T, a: T) -> Self {
        Self { r, g, b, a }
    }
}

impl Rgba<f32> {
    /// Invert the color.
    pub fn invert(self) -> Self {
        Self::new(1.0 - self.r, 1.0 - self.g, 1.0 - self.b, self.a)
    }
}

impl From<Rgba8> for Rgba<f32> {
    fn from(rgba8: Rgba8) -> Self {
        Self {
            r: (rgba8.r as f32 / 255.0),
            g: (rgba8.g as f32 / 255.0),
            b: (rgba8.b as f32 / 255.0),
            a: (rgba8.a as f32 / 255.0),
        }
    }
}

impl<T> From<Rgba<T>> for [T; 4] {
    fn from(rgba: Rgba<T>) -> Self {
        [rgba.r, rgba.g, rgba.b, rgba.a]
    }
}

/// Implemented by most color types.
pub trait Color {
    /// Pure white.
    const WHITE: Self;
    /// Pute black.
    const BLACK: Self;
    /// Transparent.
    const TRANSPARENT: Self;
    /// Pure red.
    const RED: Self;
    /// Pure green.
    const GREEN: Self;
    /// Pure blue.
    const BLUE: Self;
    /// Yellow.
    const YELLOW: Self;
}

impl Color for Rgba8 {
    const WHITE: Self = Self::new(0xff, 0xff, 0xff, 0xff);
    const BLACK: Self = Self::new(0, 0, 0, 0xff);
    const TRANSPARENT: Self = Self::new(0, 0, 0, 0);
    const RED: Self = Self::new(0xff, 0x0, 0, 0xff);
    const GREEN: Self = Self::new(0, 0xff, 0, 0xff);
    const BLUE: Self = Self::new(0, 0, 0xff, 0xff);
    const YELLOW: Self = Self::new(0xff, 0xff, 0, 0xff);
}

impl Color for Rgba<f32> {
    const WHITE: Self = Rgba::new(1.0, 1.0, 1.0, 1.0);
    const BLACK: Self = Rgba::new(0.0, 0.0, 0.0, 1.0);
    const TRANSPARENT: Self = Rgba::new(0.0, 0.0, 0.0, 0.0);
    const RED: Self = Rgba::new(1.0, 0.0, 0.0, 1.0);
    const GREEN: Self = Rgba::new(0.0, 1.0, 0.0, 1.0);
    const BLUE: Self = Rgba::new(0.0, 0.0, 1.0, 1.0);
    const YELLOW: Self = Self::new(1.0, 1.0, 0.0, 1.0);
}