use crate::{css, types::*, Color};
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct Rgb {
pub r: f64,
pub g: f64,
pub b: f64,
}
impl Color for Rgb {
fn hex(&self) -> String {
format!("#{:06X}", u32::from(*self) >> 8)
}
}
impl Eq for Rgb {}
#[allow(clippy::derive_hash_xor_eq)]
impl std::hash::Hash for Rgb {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.r.to_bits().hash(state);
self.g.to_bits().hash(state);
self.b.to_bits().hash(state);
}
}
impl From<[u8; 3]> for Rgb {
fn from(array: [u8; 3]) -> Self {
Self {
r: array[0] as f64 / 255.0,
g: array[1] as f64 / 255.0,
b: array[2] as f64 / 255.0,
}
}
}
impl From<Rgb> for [u8; 3] {
fn from(color: Rgb) -> Self {
[
(color.r * 255.0).round() as u8,
(color.g * 255.0).round() as u8,
(color.b * 255.0).round() as u8,
]
}
}
impl From<u32> for Rgb {
fn from(int: u32) -> Self {
let r = (int >> 24) as u8;
let g = (int >> 16) as u8;
let b = (int >> 8) as u8;
Self::from([r, g, b])
}
}
impl From<Rgb> for u32 {
fn from(color: Rgb) -> u32 {
let [r, g, b]: [u8; 3] = color.into();
((r as u32) << 24) | ((g as u32) << 16) | ((b as u32) << 8)
}
}
impl From<[f64; 3]> for Rgb {
fn from(array: [f64; 3]) -> Self {
Self {
r: array[0],
g: array[1],
b: array[2],
}
}
}
impl From<Rgb> for [f64; 3] {
fn from(color: Rgb) -> Self {
[color.r, color.g, color.b]
}
}
impl From<&str> for Rgb {
fn from(string: &str) -> Self {
let string = string.strip_prefix('#').unwrap_or(string);
const EXPECT_MSG: &str = "invalid hexadecimal string";
let r = u8::from_str_radix(&string[0..2], 16).expect(EXPECT_MSG);
let g = u8::from_str_radix(&string[2..4], 16).expect(EXPECT_MSG);
let b = u8::from_str_radix(&string[4..6], 16).expect(EXPECT_MSG);
Self {
r: r as f64 / 255.0,
g: g as f64 / 255.0,
b: b as f64 / 255.0,
}
}
}
impl From<Hsv> for Rgb {
fn from(other: Hsv) -> Self {
let c = other.v * other.s;
let h1 = other.h / 60.0;
let x = c * (1.0 - (h1 % 2.0 - 1.0).abs());
let (r1, g1, b1) = neighboring(c, x, h1);
let m = other.v - c;
let (r, g, b) = (r1 + m, g1 + m, b1 + m);
Self { r, g, b }
}
}
impl From<Hsl> for Rgb {
fn from(other: Hsl) -> Self {
let c = (1.0 - (2.0 * other.l - 1.0).abs()) * other.s;
let h1 = other.h / 60.0;
let x = c * (1.0 - (h1 % 2.0 - 1.0).abs());
let (r1, g1, b1) = neighboring(c, x, h1);
let m = other.l - (c / 2.0);
let (r, g, b) = (r1 + m, g1 + m, b1 + m);
Self { r, g, b }
}
}
impl From<Rgba> for Rgb {
fn from(other: Rgba) -> Self {
Self {
r: other.r,
g: other.g,
b: other.b,
}
}
}
impl From<Hsva> for Rgb {
fn from(other: Hsva) -> Self {
Self::from(Hsv::from(other))
}
}
impl From<Hsla> for Rgb {
fn from(other: Hsla) -> Self {
Self::from(Hsl::from(other))
}
}
impl TryFrom<&css::CssColorNotation> for Rgb {
type Error = css::Error;
fn try_from(other: &css::CssColorNotation) -> css::Result<Self> {
match other.format {
css::CssColorType::Rgb | css::CssColorType::Rgba => Ok(Self {
r: css::css_number_to_rgb_channel(
other.values.get(0).ok_or(css::Error::InvalidCssParams)?,
),
g: css::css_number_to_rgb_channel(
other.values.get(1).ok_or(css::Error::InvalidCssParams)?,
),
b: css::css_number_to_rgb_channel(
other.values.get(2).ok_or(css::Error::InvalidCssParams)?,
),
}),
_ => Err(css::Error::WrongCssFormat),
}
}
}
impl From<Rgb> for css::CssColorNotation {
fn from(other: Rgb) -> Self {
Self {
format: css::CssColorType::Rgb,
values: vec![
css::CssNumber::Float(other.r * 255.0),
css::CssNumber::Float(other.g * 255.0),
css::CssNumber::Float(other.b * 255.0),
],
}
}
}
#[cfg(feature = "wgpu")]
impl From<wgpu::Color> for Rgb {
fn from(other: wgpu::Color) -> Rgb {
Rgb::from([other.r, other.g, other.b])
}
}
#[cfg(feature = "wgpu")]
impl From<Rgb> for wgpu::Color {
fn from(other: Rgb) -> wgpu::Color {
wgpu::Color {
r: other.r,
g: other.g,
b: other.b,
a: 1.0,
}
}
}
fn neighboring(c: f64, x: f64, h1: f64) -> (f64, f64, f64) {
match () {
_ if (0.0..1.0).contains(&h1) => (c, x, 0.0),
_ if (1.0..2.0).contains(&h1) => (x, c, 0.0),
_ if (2.0..3.0).contains(&h1) => (0.0, c, x),
_ if (3.0..4.0).contains(&h1) => (0.0, x, c),
_ if (4.0..5.0).contains(&h1) => (x, 0.0, c),
_ if (5.0..6.0).contains(&h1) => (c, 0.0, x),
_ => (0.0, 0.0, 0.0),
}
}