Split Color into HSLA and RGBA representations
This commit is contained in:
parent
cdf057f8d0
commit
639fa99abc
178
src/color/mod.rs
178
src/color/mod.rs
@ -22,21 +22,79 @@ macro_rules! clamp {
|
|||||||
|
|
||||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||||
pub(crate) struct Color {
|
pub(crate) struct Color {
|
||||||
|
kind: ColorKind,
|
||||||
|
repr: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Color {
|
||||||
|
pub fn new_rgba(
|
||||||
|
red: Number,
|
||||||
|
green: Number,
|
||||||
|
blue: Number,
|
||||||
|
alpha: Number,
|
||||||
|
repr: String,
|
||||||
|
) -> Color {
|
||||||
|
Color {
|
||||||
|
kind: ColorKind::new_rgba(red, green, blue, alpha),
|
||||||
|
repr,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||||
|
enum ColorKind {
|
||||||
|
Rgba(Rgba),
|
||||||
|
Hsla(Hsla),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ColorKind {
|
||||||
|
pub fn new_rgba(red: Number, green: Number, blue: Number, alpha: Number) -> Self {
|
||||||
|
ColorKind::Rgba(Rgba::new(red, green, blue, alpha))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||||
|
struct Rgba {
|
||||||
red: Number,
|
red: Number,
|
||||||
green: Number,
|
green: Number,
|
||||||
blue: Number,
|
blue: Number,
|
||||||
alpha: Number,
|
alpha: Number,
|
||||||
repr: String,
|
}
|
||||||
|
|
||||||
|
impl Rgba {
|
||||||
|
pub fn new(red: Number, green: Number, blue: Number, alpha: Number) -> Self {
|
||||||
|
Rgba {
|
||||||
|
red,
|
||||||
|
green,
|
||||||
|
blue,
|
||||||
|
alpha,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn alpha(&self) -> Number {
|
||||||
|
self.alpha.clone()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||||
|
struct Hsla {
|
||||||
|
hue: Number,
|
||||||
|
saturation: Number,
|
||||||
|
luminance: Number,
|
||||||
|
alpha: Number,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Hsla {
|
||||||
|
pub fn alpha(&self) -> Number {
|
||||||
|
self.alpha.clone()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// RGBA color functions
|
// RGBA color functions
|
||||||
impl Color {
|
impl Color {
|
||||||
pub fn new(red: u8, green: u8, blue: u8, alpha: u8, repr: String) -> Self {
|
pub fn new(red: u8, green: u8, blue: u8, alpha: u8, repr: String) -> Self {
|
||||||
Color {
|
Color {
|
||||||
red: red.into(),
|
kind: ColorKind::new_rgba(red.into(), green.into(), blue.into(), alpha.into()),
|
||||||
green: green.into(),
|
|
||||||
blue: blue.into(),
|
|
||||||
alpha: alpha.into(),
|
|
||||||
repr,
|
repr,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -69,25 +127,28 @@ impl Color {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let repr = repr(&red, &green, &blue, &alpha);
|
let repr = repr(&red, &green, &blue, &alpha);
|
||||||
Color {
|
Color::new_rgba(red, green, blue, alpha, repr)
|
||||||
red,
|
|
||||||
green,
|
|
||||||
blue,
|
|
||||||
alpha,
|
|
||||||
repr,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn red(&self) -> Number {
|
pub fn red(&self) -> Number {
|
||||||
self.red.clone()
|
match &self.kind {
|
||||||
|
ColorKind::Rgba(c) => c.red.clone(),
|
||||||
|
ColorKind::Hsla(c) => todo!(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn blue(&self) -> Number {
|
pub fn blue(&self) -> Number {
|
||||||
self.blue.clone()
|
match &self.kind {
|
||||||
|
ColorKind::Rgba(c) => c.blue.clone(),
|
||||||
|
ColorKind::Hsla(c) => todo!(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn green(&self) -> Number {
|
pub fn green(&self) -> Number {
|
||||||
self.green.clone()
|
match &self.kind {
|
||||||
|
ColorKind::Rgba(c) => c.green.clone(),
|
||||||
|
ColorKind::Hsla(c) => todo!(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Mix two colors together with weight
|
/// Mix two colors together with weight
|
||||||
@ -96,7 +157,7 @@ impl Color {
|
|||||||
pub fn mix(self, other: Color, weight: Number) -> Self {
|
pub fn mix(self, other: Color, weight: Number) -> Self {
|
||||||
let weight = clamp!(weight, 0, 100);
|
let weight = clamp!(weight, 0, 100);
|
||||||
let normalized_weight = weight.clone() * Number::from(2) - Number::from(1);
|
let normalized_weight = weight.clone() * Number::from(2) - Number::from(1);
|
||||||
let alpha_distance = self.alpha.clone() - other.alpha.clone();
|
let alpha_distance = self.alpha() - other.alpha();
|
||||||
|
|
||||||
let combined_weight1 =
|
let combined_weight1 =
|
||||||
if normalized_weight.clone() * alpha_distance.clone() == Number::from(-1) {
|
if normalized_weight.clone() * alpha_distance.clone() == Number::from(-1) {
|
||||||
@ -109,10 +170,10 @@ impl Color {
|
|||||||
let weight2 = Number::from(1) - weight1.clone();
|
let weight2 = Number::from(1) - weight1.clone();
|
||||||
|
|
||||||
Color::from_rgba(
|
Color::from_rgba(
|
||||||
self.red * weight1.clone() + other.red * weight2.clone(),
|
self.red() * weight1.clone() + other.red() * weight2.clone(),
|
||||||
self.green * weight1.clone() + other.green * weight2.clone(),
|
self.green() * weight1.clone() + other.green() * weight2.clone(),
|
||||||
self.blue * weight1 + other.blue * weight2,
|
self.blue() * weight1 + other.blue() * weight2,
|
||||||
self.alpha * weight.clone() + other.alpha * (Number::from(1) - weight),
|
self.alpha() * weight.clone() + other.alpha() * (Number::from(1) - weight),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -122,9 +183,9 @@ impl Color {
|
|||||||
impl Color {
|
impl Color {
|
||||||
/// Calculate hue from RGBA values
|
/// Calculate hue from RGBA values
|
||||||
pub fn hue(&self) -> Number {
|
pub fn hue(&self) -> Number {
|
||||||
let red = self.red.clone() / Number::from(255);
|
let red = self.red() / Number::from(255);
|
||||||
let green = self.green.clone() / Number::from(255);
|
let green = self.green() / Number::from(255);
|
||||||
let blue = self.blue.clone() / Number::from(255);
|
let blue = self.blue() / Number::from(255);
|
||||||
let min = red.clone().min(green.clone().min(blue.clone()));
|
let min = red.clone().min(green.clone().min(blue.clone()));
|
||||||
let max = red.clone().max(green.clone().max(blue.clone()));
|
let max = red.clone().max(green.clone().max(blue.clone()));
|
||||||
if min == max {
|
if min == max {
|
||||||
@ -148,9 +209,9 @@ impl Color {
|
|||||||
|
|
||||||
/// Calculate saturation from RGBA values
|
/// Calculate saturation from RGBA values
|
||||||
pub fn saturation(&self) -> Number {
|
pub fn saturation(&self) -> Number {
|
||||||
let red = self.red.clone() / Number::from(255);
|
let red = self.red() / Number::from(255);
|
||||||
let green = self.green.clone() / Number::from(255);
|
let green = self.green() / Number::from(255);
|
||||||
let blue = self.blue.clone() / Number::from(255);
|
let blue = self.blue() / Number::from(255);
|
||||||
|
|
||||||
let min = red.clone().min(green.clone().min(blue.clone()));
|
let min = red.clone().min(green.clone().min(blue.clone()));
|
||||||
let max = red.max(green.max(blue));
|
let max = red.max(green.max(blue));
|
||||||
@ -171,18 +232,18 @@ impl Color {
|
|||||||
|
|
||||||
/// Calculate luminance from RGBA values
|
/// Calculate luminance from RGBA values
|
||||||
pub fn lightness(&self) -> Number {
|
pub fn lightness(&self) -> Number {
|
||||||
let red = self.red.clone() / Number::from(255);
|
let red = self.red() / Number::from(255);
|
||||||
let green = self.green.clone() / Number::from(255);
|
let green = self.green() / Number::from(255);
|
||||||
let blue = self.blue.clone() / Number::from(255);
|
let blue = self.blue() / Number::from(255);
|
||||||
let min = red.clone().min(green.clone().min(blue.clone()));
|
let min = red.clone().min(green.clone().min(blue.clone()));
|
||||||
let max = red.max(green.max(blue));
|
let max = red.max(green.max(blue));
|
||||||
(((min + max) / Number::from(2)) * Number::from(100)).round()
|
(((min + max) / Number::from(2)) * Number::from(100)).round()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn as_hsla(&self) -> (Number, Number, Number, Number) {
|
pub fn as_hsla(&self) -> (Number, Number, Number, Number) {
|
||||||
let red = self.red.clone() / Number::from(255);
|
let red = self.red() / Number::from(255);
|
||||||
let green = self.green.clone() / Number::from(255);
|
let green = self.green() / Number::from(255);
|
||||||
let blue = self.blue.clone() / Number::from(255);
|
let blue = self.blue() / Number::from(255);
|
||||||
let min = red.clone().min(green.clone().min(blue.clone()));
|
let min = red.clone().min(green.clone().min(blue.clone()));
|
||||||
let max = red.clone().max(green.clone().max(blue.clone()));
|
let max = red.clone().max(green.clone().max(blue.clone()));
|
||||||
|
|
||||||
@ -268,13 +329,7 @@ impl Color {
|
|||||||
};
|
};
|
||||||
let val = luminance * Number::from(255);
|
let val = luminance * Number::from(255);
|
||||||
let repr = repr(&val, &val, &val, &alpha);
|
let repr = repr(&val, &val, &val, &alpha);
|
||||||
return Color {
|
return Color::new_rgba(val.clone(), val.clone(), val, alpha, repr);
|
||||||
red: val.clone(),
|
|
||||||
green: val.clone(),
|
|
||||||
blue: val,
|
|
||||||
alpha,
|
|
||||||
repr,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
let temporary_1 = if luminance.clone() < Number::ratio(1, 2) {
|
let temporary_1 = if luminance.clone() < Number::ratio(1, 2) {
|
||||||
luminance.clone() * (Number::from(1) + saturation)
|
luminance.clone() * (Number::from(1) + saturation)
|
||||||
@ -324,13 +379,7 @@ impl Color {
|
|||||||
channel!(blue, temporary_b, temporary_1, temporary_2);
|
channel!(blue, temporary_b, temporary_1, temporary_2);
|
||||||
|
|
||||||
let repr = repr(&red, &green, &blue, &alpha);
|
let repr = repr(&red, &green, &blue, &alpha);
|
||||||
Color {
|
Color::new_rgba(red, green, blue, alpha, repr)
|
||||||
red,
|
|
||||||
green,
|
|
||||||
blue,
|
|
||||||
alpha,
|
|
||||||
repr,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn invert(&self, weight: Number) -> Self {
|
pub fn invert(&self, weight: Number) -> Self {
|
||||||
@ -341,17 +390,11 @@ impl Color {
|
|||||||
} else {
|
} else {
|
||||||
weight
|
weight
|
||||||
};
|
};
|
||||||
let red = Number::from(u8::max_value()) - self.red.clone() * weight.clone();
|
let red = Number::from(u8::max_value()) - self.red() * weight.clone();
|
||||||
let green = Number::from(u8::max_value()) - self.green.clone() * weight.clone();
|
let green = Number::from(u8::max_value()) - self.green() * weight.clone();
|
||||||
let blue = Number::from(u8::max_value()) - self.blue.clone() * weight;
|
let blue = Number::from(u8::max_value()) - self.blue() * weight;
|
||||||
let repr = repr(&red, &green, &blue, &self.alpha);
|
let repr = repr(&red, &green, &blue, &self.alpha());
|
||||||
Color {
|
Color::new_rgba(red, green, blue, self.alpha(), repr)
|
||||||
red,
|
|
||||||
green,
|
|
||||||
blue,
|
|
||||||
alpha: self.alpha.clone(),
|
|
||||||
repr,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn complement(&self) -> Self {
|
pub fn complement(&self) -> Self {
|
||||||
@ -368,7 +411,10 @@ impl Color {
|
|||||||
/// Opacity color functions
|
/// Opacity color functions
|
||||||
impl Color {
|
impl Color {
|
||||||
pub fn alpha(&self) -> Number {
|
pub fn alpha(&self) -> Number {
|
||||||
let a = self.alpha.clone();
|
let a = match &self.kind {
|
||||||
|
ColorKind::Rgba(c) => c.alpha(),
|
||||||
|
ColorKind::Hsla(c) => c.alpha(),
|
||||||
|
};
|
||||||
if a > Number::from(1) {
|
if a > Number::from(1) {
|
||||||
a / Number::from(255)
|
a / Number::from(255)
|
||||||
} else {
|
} else {
|
||||||
@ -378,21 +424,21 @@ impl Color {
|
|||||||
|
|
||||||
/// Change `alpha` to value given
|
/// Change `alpha` to value given
|
||||||
pub fn with_alpha(self, alpha: Number) -> Self {
|
pub fn with_alpha(self, alpha: Number) -> Self {
|
||||||
Color::from_rgba(self.red, self.green, self.blue, alpha)
|
Color::from_rgba(self.red(), self.green(), self.blue(), alpha)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Makes a color more opaque.
|
/// Makes a color more opaque.
|
||||||
/// Takes a color and a number between 0 and 1,
|
/// Takes a color and a number between 0 and 1,
|
||||||
/// and returns a color with the opacity increased by that amount.
|
/// and returns a color with the opacity increased by that amount.
|
||||||
pub fn fade_in(self, amount: Number) -> Self {
|
pub fn fade_in(self, amount: Number) -> Self {
|
||||||
Color::from_rgba(self.red, self.green, self.blue, self.alpha + amount)
|
Color::from_rgba(self.red(), self.green(), self.blue(), self.alpha() + amount)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Makes a color more transparent.
|
/// Makes a color more transparent.
|
||||||
/// Takes a color and a number between 0 and 1,
|
/// Takes a color and a number between 0 and 1,
|
||||||
/// and returns a color with the opacity decreased by that amount.
|
/// and returns a color with the opacity decreased by that amount.
|
||||||
pub fn fade_out(self, amount: Number) -> Self {
|
pub fn fade_out(self, amount: Number) -> Self {
|
||||||
Color::from_rgba(self.red, self.green, self.blue, self.alpha - amount)
|
Color::from_rgba(self.red(), self.green(), self.blue(), self.alpha() - amount)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -401,12 +447,10 @@ impl Color {
|
|||||||
pub fn to_ie_hex_str(&self) -> String {
|
pub fn to_ie_hex_str(&self) -> String {
|
||||||
format!(
|
format!(
|
||||||
"#{:02X}{:02X}{:02X}{:02X}",
|
"#{:02X}{:02X}{:02X}{:02X}",
|
||||||
(self.alpha.clone() * Number::from(255))
|
(self.alpha() * Number::from(255)).round().to_integer(),
|
||||||
.round()
|
self.red().to_integer(),
|
||||||
.to_integer(),
|
self.green().to_integer(),
|
||||||
self.red.to_integer(),
|
self.blue().to_integer()
|
||||||
self.green.to_integer(),
|
|
||||||
self.blue.to_integer()
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user