sketchbook 0.0.2

Interactive visual applications in Rust
Documentation
/// Wrapper around `palette::Srgba<u8>` for converting from other types.
///
/// ```
/// # use sketchbook::aspects::draw::Color;
/// #
/// // Example of creating color.
/// let color: Color = (255, 100, 20).into();
/// ```
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Copy, Clone, Eq, PartialEq, Debug, Default)]
#[cfg_attr(docsrs, doc(cfg(feature = "color")))]
pub struct Color(
    /// Inner color.
    pub palette::Srgba<u8>,
);

impl Color {
    /// Create color from red, green, blue, and alpha components.
    ///
    /// ```
    /// # use sketchbook::aspects::draw::Color;
    /// const color: Color = Color::new(255, 100, 0, 255);
    /// ```
    #[inline]
    pub const fn new(red: u8, green: u8, blue: u8, alpha: u8) -> Color {
        Color(palette::Alpha {
            color: palette::Srgb {
                red,
                green,
                blue,
                standard: core::marker::PhantomData,
            },
            alpha,
        })
    }

    /// Convert into the inner `palette::Srgba<u8>`.
    ///
    /// This is the same as using `self.0`.
    ///
    /// ```
    /// # use sketchbook::aspects::draw::Color;
    /// let color: Color = (255, 100, 20).into();
    ///
    /// let srgba = color.into_inner();
    ///
    /// assert_eq!(srgba.red, 255);
    /// assert_eq!(srgba.green, 100);
    /// assert_eq!(srgba.blue, 20);
    /// assert_eq!(srgba.alpha, 255);
    /// ```
    #[inline]
    pub const fn into_inner(self) -> palette::Srgba<u8> {
        self.0
    }

    /// Convert to array of uppercase hex chars.
    ///
    /// The order returned is `RRGGBBAA`, where `R` is red, `G` is green, `B` is blue, and `A` is
    /// alpha.
    ///
    /// ```
    /// # use sketchbook::aspects::draw::Color;
    /// let color: Color = (198, 50, 27).into();
    ///
    /// assert_eq!(color.to_hex(), ['C', '6', '3', '2', '1', 'B', 'F', 'F']);
    /// ```
    pub const fn to_hex(&self) -> [char; 8] {
        [
            half_byte_to_hex(self.0.color.red >> 4 & 0x0F),
            half_byte_to_hex(self.0.color.red & 0x0F),
            half_byte_to_hex(self.0.color.green >> 4 & 0x0F),
            half_byte_to_hex(self.0.color.green & 0x0F),
            half_byte_to_hex(self.0.color.blue >> 4 & 0x0F),
            half_byte_to_hex(self.0.color.blue & 0x0F),
            half_byte_to_hex(self.0.alpha >> 4 & 0x0F),
            half_byte_to_hex(self.0.alpha & 0x0F),
        ]
    }
}

/// Convert lower half of byte to uppercase hex char.
const fn half_byte_to_hex(half_byte: u8) -> char {
    match half_byte {
        0 => '0',
        1 => '1',
        2 => '2',
        3 => '3',
        4 => '4',
        5 => '5',
        6 => '6',
        7 => '7',
        8 => '8',
        9 => '9',
        10 => 'A',
        11 => 'B',
        12 => 'C',
        13 => 'D',
        14 => 'E',
        15 => 'F',
        _ => '?',
    }
}

impl Color {
    /// `#FF0000` <span style="color:#FF0000">&#9632;</span>
    pub const RED: Self = Self::new(0xFF, 0x00, 0x00, 0xFF);

    /// `#00FFFF` <span style="color:#00FFFF">&#9632;</span>
    pub const CYAN: Self = Self::new(0x00, 0xFF, 0xFF, 0xFF);

    /// `#0000FF` <span style="color:#0000FF">&#9632;</span>
    pub const BLUE: Self = Self::new(0x00, 0x00, 0xFF, 0xFF);

    /// `#0000A0` <span style="color:#0000A0">&#9632;</span>
    pub const DARK_BLUE: Self = Self::new(0x00, 0x00, 0xA0, 0xFF);

    /// `#ADD8E6` <span style="color:#ADD8E6">&#9632;</span>
    pub const LIGHT_BLUE: Self = Self::new(0xAD, 0xD8, 0xE6, 0xFF);

    /// `#800080` <span style="color:#800080">&#9632;</span>
    pub const PURPLE: Self = Self::new(0x80, 0x00, 0x80, 0xFF);

    /// `#FFFF00` <span style="color:#FFFF00">&#9632;</span>
    pub const YELLOW: Self = Self::new(0xFF, 0xFF, 0x00, 0xFF);

    /// `#00FF00` <span style="color:#00FF00">&#9632;</span>
    pub const LIME: Self = Self::new(0x00, 0xFF, 0x00, 0xFF);

    /// `#FF00FF` <span style="color:#FF00FF">&#9632;</span>
    pub const MAGENTA: Self = Self::new(0xFF, 0x00, 0xFF, 0xFF);

    /// `#FFFFFF` <span style="color:#FFFFFF">&#9632;</span>
    pub const WHITE: Self = Self::new(0xFF, 0xFF, 0xFF, 0xFF);

    /// `#C0C0C0` <span style="color:#C0C0C0">&#9632;</span>
    pub const SLIVER: Self = Self::new(0xC0, 0xC0, 0xC0, 0xFF);

    /// `#808080` <span style="color:#808080">&#9632;</span>
    pub const GRAY: Self = Self::new(0x80, 0x80, 0x80, 0xFF);

    /// `#000000` <span style="color:#000000">&#9632;</span>
    pub const BLACK: Self = Self::new(0x00, 0x00, 0x00, 0xFF);

    /// `#FFA500` <span style="color:#FFA500">&#9632;</span>
    pub const ORANGE: Self = Self::new(0xFF, 0xA5, 0x00, 0xFF);

    /// `#A52A2A` <span style="color:#A52A2A">&#9632;</span>
    pub const BROWN: Self = Self::new(0xA5, 0x2A, 0x2A, 0xFF);

    /// `#800000` <span style="color:#800000">&#9632;</span>
    pub const MAROON: Self = Self::new(0x80, 0x00, 0x00, 0xFF);

    /// `#008000` <span style="color:#008000">&#9632;</span>
    pub const GREEN: Self = Self::new(0x00, 0x80, 0x00, 0xFF);

    /// `#808000` <span style="color:#808000">&#9632;</span>
    pub const OLIVE: Self = Self::new(0x80, 0x80, 0x00, 0xFF);
}

/// Interpreted as a gray scale color without alpha component.
///
/// ```
/// # use sketchbook::aspects::draw::Color;
/// let color: Color = 100_u8.into();
///
/// assert_eq!(color.0.red, 0x64);
/// assert_eq!(color.0.green, 0x64);
/// assert_eq!(color.0.blue, 0x64);
/// assert_eq!(color.0.alpha, 0xFF);
/// ```
impl From<u8> for Color {
    #[inline]
    fn from(gray: u8) -> Self {
        Self(palette::Srgba::new(gray, gray, gray, 255))
    }
}

/// Interpreted as a gray scale color with alpha component.
///
/// ```
/// # use sketchbook::aspects::draw::Color;
/// let color: Color = (100, 200).into();
///
/// assert_eq!(color.0.red, 0x64);
/// assert_eq!(color.0.green, 0x64);
/// assert_eq!(color.0.blue, 0x64);
/// assert_eq!(color.0.alpha, 0xC8);
/// ```
impl From<(u8, u8)> for Color {
    #[inline]
    fn from(color: (u8, u8)) -> Self {
        let (gray, alpha) = color;
        Self(palette::Srgba::new(gray, gray, gray, alpha))
    }
}

/// Interpreted as a RGB color without alpha component.
///
/// ```
/// # use sketchbook::aspects::draw::Color;
/// let color: Color = (255, 100, 00).into();
///
/// assert_eq!(color.0.red, 0xFF);
/// assert_eq!(color.0.green, 0x64);
/// assert_eq!(color.0.blue, 0x00);
/// assert_eq!(color.0.alpha, 0xFF);
/// ```
impl From<(u8, u8, u8)> for Color {
    #[inline]
    fn from(color: (u8, u8, u8)) -> Self {
        let (red, green, blue) = color;
        Self(palette::Srgba::new(red, green, blue, 255))
    }
}

/// Interpreted as a RGB color with alpha component.
///
/// ```
/// # use sketchbook::aspects::draw::Color;
/// let color: Color = (255, 100, 00, 200).into();
///
/// assert_eq!(color.0.red, 0xFF);
/// assert_eq!(color.0.green, 0x64);
/// assert_eq!(color.0.blue, 0x00);
/// assert_eq!(color.0.alpha, 0xC8);
/// ```
impl From<(u8, u8, u8, u8)> for Color {
    #[inline]
    fn from(color: (u8, u8, u8, u8)) -> Self {
        let (red, green, blue, alpha) = color;
        Self(palette::Srgba::new(red, green, blue, alpha))
    }
}

/// Interpreted as a gray scale color without alpha component.
///
/// Value `gray` should be in the range `0.0..=1.0`.
/// A panic will **not** happen if the value is outside of this range.
/// Instead, a color will be returned, but it's component values are not specified by this impl.
///
/// ```
/// # use sketchbook::aspects::draw::Color;
/// let color: Color = 0.5.into();
///
/// assert_eq!(color.0.red, 0x7F);
/// assert_eq!(color.0.green, 0x7F);
/// assert_eq!(color.0.blue, 0x7F);
/// assert_eq!(color.0.alpha, 0xFF);
/// ```
impl From<f32> for Color {
    #[inline]
    fn from(gray: f32) -> Self {
        Self(palette::Srgba::new(
            (gray * 255.0) as u8,
            (gray * 255.0) as u8,
            (gray * 255.0) as u8,
            255,
        ))
    }
}

/// Interpreted as a gray scale color with alpha component.
///
/// Values `gray` and `alpha` should be in the range `0.0..=1.0`.
/// A panic will **not** happen if any of the values are outside of this range.
/// Instead, a color will be returned, but it's component values are not specified by this impl.
///
/// ```
/// # use sketchbook::aspects::draw::Color;
/// let color: Color = (0.5, 0.8).into();
///
/// assert_eq!(color.0.red, 0x7F);
/// assert_eq!(color.0.green, 0x7F);
/// assert_eq!(color.0.blue, 0x7F);
/// assert_eq!(color.0.alpha, 0xCC);
/// ```
impl From<(f32, f32)> for Color {
    #[inline]
    fn from(color: (f32, f32)) -> Self {
        let (gray, alpha) = color;
        Self(palette::Srgba::new(
            (gray * 255.0) as u8,
            (gray * 255.0) as u8,
            (gray * 255.0) as u8,
            (alpha * 255.0) as u8,
        ))
    }
}

/// Interpreted as a RGB color without alpha component.
///
/// Values `red`, `green`, and `blue` should be in the range `0.0..=1.0`.
/// A panic will **not** happen if any of the values are outside of this range.
/// Instead, a color will be returned, but it's component values are not specified by this impl.
///
/// ```
/// # use sketchbook::aspects::draw::Color;
/// let color: Color = (1.0, 0.5, 0.0).into();
///
/// assert_eq!(color.0.red, 0xFF);
/// assert_eq!(color.0.green, 0x7F);
/// assert_eq!(color.0.blue, 0x00);
/// assert_eq!(color.0.alpha, 0xFF);
/// ```
impl From<(f32, f32, f32)> for Color {
    #[inline]
    fn from(color: (f32, f32, f32)) -> Self {
        let (red, green, blue) = color;
        Self(palette::Srgba::new(
            (red * 255.0) as u8,
            (green * 255.0) as u8,
            (blue * 255.0) as u8,
            255,
        ))
    }
}

/// Interpreted as a RGB color with alpha component.
///
/// Values `red`, `green`, `blue`, and `alpha` should be in the range `0.0..=1.0`.
/// A panic will **not** happen if any of the values are outside of this range;
/// Instead, a color will be returned, but it's component values are not specified by this impl.
///
/// ```
/// # use sketchbook::aspects::draw::Color;
/// let color: Color = (1.0, 0.5, 0.0, 0.8).into();
///
/// assert_eq!(color.0.red, 0xFF);
/// assert_eq!(color.0.green, 0x7F);
/// assert_eq!(color.0.blue, 0x00);
/// assert_eq!(color.0.alpha, 0xCC);
/// ```
impl From<(f32, f32, f32, f32)> for Color {
    #[inline]
    fn from(color: (f32, f32, f32, f32)) -> Self {
        let (red, green, blue, alpha) = color;
        Self(palette::Srgba::new(
            (red * 255.0) as u8,
            (green * 255.0) as u8,
            (blue * 255.0) as u8,
            (alpha * 255.0) as u8,
        ))
    }
}

/// Interpreted as packed RGB color with alpha component.
///
/// The expected component order is `0xRRGGBBAA`, where `R` is red, `G` is green, `B` is blue, and `A` is alpha, .
///
/// ```
/// # use sketchbook::aspects::draw::Color;
/// let color: Color = 0x89ABCDEF_u32.into();
///
/// assert_eq!(color.0.red, 0x89);
/// assert_eq!(color.0.green, 0xAB);
/// assert_eq!(color.0.blue, 0xCD);
/// assert_eq!(color.0.alpha, 0xEF);
/// ```
impl From<u32> for Color {
    #[inline]
    fn from(hex: u32) -> Self {
        Self(palette::Srgba::from(hex))
    }
}