use crate::random;
use conversion::{calculate_channels, clamp_levels, convert_levels, maxes};
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
use std::fmt;
pub mod constants;
pub mod conversion;
pub mod ops;
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
#[must_use]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum Mode {
Rgb,
Hsb,
Hsl,
}
use Mode::{Hsb, Hsl, Rgb};
#[derive(Debug, Copy, Clone)]
#[must_use]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Color {
mode: Mode,
channels: [u8; 4],
}
#[macro_export]
macro_rules! color {
($gray:expr) => {
rgb!($gray)
};
($gray:expr, $a:expr$(,)?) => {
rgb!($gray, $a)
};
($r:expr, $g:expr, $b:expr$(,)?) => {
rgb!($r, $g, $b)
};
($r:expr, $g:expr, $b:expr, $a:expr$(,)?) => {
rgb!($r, $g, $b, $a)
};
}
#[doc(alias = "color")]
#[macro_export]
macro_rules! rgb {
($gray:expr) => {
rgb!($gray, $gray, $gray)
};
($gray:expr, $a:expr$(,)?) => {
rgb!($gray, $gray, $gray, $a)
};
($r:expr, $g:expr, $b:expr$(,)?) => {
rgb!($r, $g, $b, 255)
};
($r:expr, $g:expr, $b:expr, $a:expr$(,)?) => {
$crate::prelude::Color::rgba($r, $g, $b, $a)
};
}
#[macro_export]
macro_rules! hsb {
($gray:expr) => {
hsb!(0.0, 0.0, $gray)
};
($gray:expr, $a:expr$(,)?) => {
hsb!(0.0, 0.0, $gray, $a)
};
($h:expr, $s:expr, $b:expr$(,)?) => {
hsb!($h, $s, $b, 1.0)
};
($h:expr, $s:expr, $b:expr, $a:expr$(,)?) => {
$crate::prelude::Color::hsba($h, $s, $b, $a)
};
}
#[macro_export]
macro_rules! hsl {
($gray:expr) => {
hsl!(0.0, 0.0, $gray)
};
($gray:expr, $a:expr$(,)?) => {
hsl!(0.0, 0.0, $gray, $a)
};
($h:expr, $s:expr, $l:expr$(,)?) => {
hsl!($h, $s, $l, 1.0)
};
($h:expr, $s:expr, $l:expr, $a:expr$(,)?) => {
$crate::prelude::Color::hsla($h, $s, $l, $a)
};
}
impl Color {
#[inline]
pub const fn new(r: u8, g: u8, b: u8) -> Self {
Self::rgb(r, g, b)
}
#[inline]
pub const fn new_alpha(r: u8, g: u8, b: u8, a: u8) -> Self {
Self::rgba(r, g, b, a)
}
pub fn with_mode<T: Into<f64>>(mode: Mode, v1: T, v2: T, v3: T) -> Self {
let [_, _, _, alpha_max] = maxes(mode);
Self::with_mode_alpha(mode, v1.into(), v2.into(), v3.into(), alpha_max)
}
pub fn with_mode_alpha<T: Into<f64>>(mode: Mode, v1: T, v2: T, v3: T, alpha: T) -> Self {
let [v1, v2, v3, alpha] = [v1.into(), v2.into(), v3.into(), alpha.into()];
let channels = if mode == Rgb {
[v1 as u8, v2 as u8, v3 as u8, alpha as u8]
} else {
let [v1_max, v2_max, v3_max, alpha_max] = maxes(mode);
let levels = clamp_levels([v1 / v1_max, v2 / v2_max, v3 / v3_max, alpha / alpha_max]);
let levels = convert_levels(levels, mode, Rgb);
calculate_channels(levels)
};
Self { mode, channels }
}
#[doc(alias = "new")]
#[inline]
pub const fn rgb(r: u8, g: u8, b: u8) -> Self {
Self {
mode: Rgb,
channels: [r, g, b, 255],
}
}
#[doc(alias = "new_alpha")]
#[inline]
pub const fn rgba(r: u8, g: u8, b: u8, a: u8) -> Self {
Self {
mode: Rgb,
channels: [r, g, b, a],
}
}
#[inline]
pub fn hsb<T: Into<f64>>(h: T, s: T, b: T) -> Self {
Self::with_mode(Hsb, h, s, b)
}
#[inline]
pub fn hsba<T: Into<f64>>(h: T, s: T, b: T, a: T) -> Self {
Self::with_mode_alpha(Hsb, h, s, b, a)
}
#[inline]
pub fn hsl<T: Into<f64>>(h: T, s: T, l: T) -> Self {
Self::with_mode(Hsl, h, s, l)
}
#[inline]
pub fn hsla<T: Into<f64>>(h: T, s: T, l: T, a: T) -> Self {
Self::with_mode_alpha(Hsl, h, s, l, a)
}
pub fn from_levels<T: Into<f64>>(mode: Mode, v1: T, v2: T, v3: T, alpha: T) -> Self {
let mut levels = [v1.into(), v2.into(), v3.into(), alpha.into()];
for v in &mut levels {
*v = (*v).clamp(0.0, 1.0);
}
Self {
mode,
channels: calculate_channels(levels),
}
}
#[inline]
pub fn random() -> Self {
Self::rgb(random!(255), random!(255), random!(255))
}
#[inline]
pub fn random_alpha() -> Self {
Self::rgba(random!(255), random!(255), random!(255), random!(255))
}
#[inline]
#[must_use]
pub const fn as_hex(&self) -> u32 {
let [r, g, b, _] = self.channels();
u32::from_be_bytes([0, r, g, b])
}
#[inline]
#[must_use]
pub const fn as_hex_alpha(&self) -> u32 {
u32::from_be_bytes(self.channels())
}
#[inline]
#[must_use]
pub const fn maxes(&self) -> [f64; 4] {
maxes(self.mode)
}
#[inline]
#[must_use]
pub fn levels(&self) -> [f64; 4] {
let [r, g, b, a] = self.channels;
let [r_max, g_max, b_max, a_max] = maxes(Rgb);
let levels = clamp_levels([
f64::from(r) / r_max,
f64::from(g) / g_max,
f64::from(b) / b_max,
f64::from(a) / a_max,
]);
convert_levels(levels, Rgb, self.mode)
}
#[inline]
pub fn set_levels(&mut self, levels: [f64; 4]) {
let levels = clamp_levels(levels);
self.update_channels(levels, self.mode);
}
#[inline]
#[must_use]
pub const fn channels(&self) -> [u8; 4] {
self.channels
}
#[inline]
pub const fn mode(&self) -> Mode {
self.mode
}
#[inline]
pub fn set_mode(&mut self, mode: Mode) {
self.mode = mode;
}
#[inline]
#[must_use]
pub const fn red(&self) -> u8 {
self.channels[0]
}
#[inline]
pub fn set_red(&mut self, r: u8) {
self.channels[0] = r;
}
#[inline]
#[must_use]
pub const fn green(&self) -> u8 {
self.channels[1]
}
#[inline]
pub fn set_green(&mut self, g: u8) {
self.channels[1] = g;
}
#[inline]
#[must_use]
pub const fn blue(&self) -> u8 {
self.channels[2]
}
#[inline]
pub fn set_blue(&mut self, b: u8) {
self.channels[2] = b;
}
#[inline]
#[must_use]
pub const fn alpha(&self) -> u8 {
self.channels[3]
}
#[inline]
pub fn set_alpha(&mut self, a: u8) {
self.channels[3] = a;
}
#[inline]
#[must_use]
pub fn hue(&self) -> f64 {
let maxes = maxes(Hsb);
let levels = convert_levels(self.levels(), self.mode, Hsb);
levels[0] * maxes[0]
}
#[inline]
pub fn set_hue<H: Into<f64>>(&mut self, h: H) {
let maxes = maxes(Hsb);
let mut levels = convert_levels(self.levels(), self.mode, Hsb);
levels[0] = h.into() / maxes[0];
self.update_channels(levels, Hsb);
}
#[inline]
#[must_use]
pub fn saturation(&self) -> f64 {
let maxes = maxes(Hsb);
let levels = convert_levels(self.levels(), self.mode, Hsb);
levels[1] * maxes[1]
}
#[inline]
pub fn set_saturation<S: Into<f64>>(&mut self, s: S) {
let mode = match self.mode {
Hsb | Hsl => self.mode,
Rgb => Hsb,
};
let maxes = maxes(mode);
let mut levels = convert_levels(self.levels(), self.mode, mode);
levels[1] = s.into() / maxes[1];
self.update_channels(levels, mode);
}
#[inline]
#[must_use]
pub fn brightness(&self) -> f64 {
let maxes = maxes(Hsb);
let levels = convert_levels(self.levels(), self.mode, Hsb);
levels[2] * maxes[2]
}
#[inline]
pub fn set_brightness<B: Into<f64>>(&mut self, b: B) {
let maxes = maxes(Hsb);
let mut levels = convert_levels(self.levels(), self.mode, Hsb);
levels[2] = b.into() / maxes[2];
self.update_channels(levels, Hsb);
}
#[inline]
#[must_use]
pub fn lightness(&self) -> f64 {
let maxes = maxes(Hsl);
let levels = convert_levels(self.levels(), self.mode, Hsl);
levels[2] * maxes[2]
}
#[inline]
pub fn set_lightness<L: Into<f64>>(&mut self, l: L) {
let maxes = maxes(Hsl);
let mut levels = convert_levels(self.levels(), self.mode, Hsl);
levels[2] = l.into() / maxes[2];
self.update_channels(levels, Hsl);
}
#[inline]
#[must_use]
pub fn to_vec(self) -> Vec<u8> {
self.channels.to_vec()
}
}
impl Default for Color {
fn default() -> Self {
Self::rgb(0, 0, 0)
}
}
impl fmt::Display for Color {
#[allow(clippy::many_single_char_names)]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let [r, g, b, a] = self.channels();
write!(f, "[{r}, {g}, {b}, {a}]")
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_constructors() {
let expected = |mode| Color {
mode,
channels: [0, 0, 0, 255],
};
assert_eq!(Color::with_mode(Rgb, 0.0, 0.0, 0.0), expected(Rgb));
assert_eq!(
Color::with_mode_alpha(Rgb, 0.0, 0.0, 0.0, 255.0),
expected(Rgb)
);
assert_eq!(Color::with_mode(Hsb, 0.0, 0.0, 0.0), expected(Hsb));
assert_eq!(
Color::with_mode_alpha(Hsb, 0.0, 0.0, 0.0, 1.0),
expected(Hsb)
);
assert_eq!(Color::with_mode(Hsl, 0.0, 0.0, 0.0), expected(Hsl));
assert_eq!(
Color::with_mode_alpha(Hsl, 0.0, 0.0, 0.0, 1.0),
expected(Hsl)
);
}
}