mirror of
https://github.com/lisk77/comet.git
synced 2025-10-23 21:38:50 +00:00
initial commit
This commit is contained in:
commit
6154c72b0e
55 changed files with 9481 additions and 0 deletions
8
crates/comet_colors/Cargo.toml
Normal file
8
crates/comet_colors/Cargo.toml
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
[package]
|
||||
name = "comet_colors"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
comet_math = { path = "../comet_math" }
|
||||
wgpu = { version = "22.0" }
|
||||
101
crates/comet_colors/src/hsla.rs
Normal file
101
crates/comet_colors/src/hsla.rs
Normal file
|
|
@ -0,0 +1,101 @@
|
|||
use crate::{sRgba, Hsva, Hwba, Laba, Lcha, LinearRgba, Oklaba, Oklcha, Xyza};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct Hsla {
|
||||
hue: f32,
|
||||
saturation: f32,
|
||||
lightness: f32,
|
||||
alpha: f32
|
||||
}
|
||||
|
||||
impl Hsla {
|
||||
pub fn new(hue: f32, saturation: f32, lightness: f32, alpha: f32) -> Self {
|
||||
assert!((0.0..=360.0).contains(&hue) && (0.0..=1.0).contains(&saturation) && (0.0..=1.0).contains(&lightness) && (0.0..=1.0).contains(&alpha), "Hue needs to be in range 0..360\nSaturation needs to be in range 0..1\nLightness needs to be in range 0..1\nAlpha needs to be in range 0..1");
|
||||
Self {
|
||||
hue,
|
||||
saturation,
|
||||
lightness,
|
||||
alpha
|
||||
}
|
||||
}
|
||||
|
||||
pub fn hue(&self) -> f32 {
|
||||
self.hue
|
||||
}
|
||||
|
||||
pub fn saturation(&self) -> f32 {
|
||||
self.saturation
|
||||
}
|
||||
|
||||
pub fn lightness(&self) -> f32 {
|
||||
self.lightness
|
||||
}
|
||||
|
||||
pub fn alpha(&self) -> f32 {
|
||||
self.alpha
|
||||
}
|
||||
|
||||
pub fn from_hsva(hsva: Hsva) -> Self {
|
||||
let lightness = hsva.value() * (1.0 - hsva.saturation() * 0.5);
|
||||
Self {
|
||||
hue: hsva.hue(),
|
||||
saturation: if lightness == 0.0 || lightness == 1.0 { 0.0 } else { (hsva.value() - lightness) / lightness.min(1.0 - lightness) },
|
||||
lightness,
|
||||
alpha: hsva.alpha()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_rgba(rgba: sRgba<f32>) -> Self {
|
||||
rgba
|
||||
.to_hwba()
|
||||
.to_hsva()
|
||||
.to_hsla()
|
||||
|
||||
}
|
||||
|
||||
pub fn to_hsva(&self) -> Hsva {
|
||||
let value = self.lightness() + self.saturation() * self.lightness().min(1.0 - self.lightness());
|
||||
Hsva::new(
|
||||
self.hue(),
|
||||
if value == 0.0 { 0.0 } else { 2.0 * (1.0 - self.lightness() / value) },
|
||||
value,
|
||||
self.alpha()
|
||||
)
|
||||
}
|
||||
|
||||
pub fn to_hwba(&self) -> Hwba {
|
||||
self.to_hsva().to_hwba()
|
||||
}
|
||||
|
||||
pub fn to_rgba(&self) -> sRgba<f32> {
|
||||
self.to_hwba().to_rgba()
|
||||
}
|
||||
|
||||
pub fn to_rgba8(&self) -> sRgba<u8> {
|
||||
self.to_hwba().to_rgba8()
|
||||
}
|
||||
|
||||
pub fn to_linear(&self) -> LinearRgba {
|
||||
self.to_rgba().to_linear()
|
||||
}
|
||||
|
||||
pub fn to_xyza(&self) -> Xyza {
|
||||
self.to_linear().to_xyza()
|
||||
}
|
||||
|
||||
pub fn to_laba(&self) -> Laba {
|
||||
self.to_xyza().to_laba()
|
||||
}
|
||||
|
||||
pub fn to_lcha(&self) -> Lcha {
|
||||
self.to_laba().to_lcha()
|
||||
}
|
||||
|
||||
pub fn to_oklaba(&self) -> Oklaba {
|
||||
self.to_linear().to_oklaba()
|
||||
}
|
||||
|
||||
pub fn to_oklcha(&self) -> Oklcha {
|
||||
self.to_oklaba().to_oklcha()
|
||||
}
|
||||
}
|
||||
99
crates/comet_colors/src/hsva.rs
Normal file
99
crates/comet_colors/src/hsva.rs
Normal file
|
|
@ -0,0 +1,99 @@
|
|||
use crate::{sRgba, Hsla, Hwba, Laba, Lcha, LinearRgba, Oklaba, Oklcha, Xyza};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct Hsva {
|
||||
hue: f32,
|
||||
saturation: f32,
|
||||
value: f32,
|
||||
alpha: f32
|
||||
}
|
||||
|
||||
impl Hsva {
|
||||
pub fn new(hue: f32, saturation: f32, value: f32, alpha: f32) -> Self {
|
||||
assert!((0.0..=360.0).contains(&hue) && (0.0..=1.0).contains(&saturation) && (0.0..=1.0).contains(&value) && (0.0..=1.0).contains(&alpha), "Hue needs to be in range 0..1\nSaturation needs to be in range 0..1\nValue needs to be in range 0..1\nAlpha needs to be in range 0..1");
|
||||
Self {
|
||||
hue,
|
||||
saturation,
|
||||
value,
|
||||
alpha
|
||||
}
|
||||
}
|
||||
|
||||
pub fn hue(&self) -> f32 {
|
||||
self.hue
|
||||
}
|
||||
|
||||
pub fn saturation(&self) -> f32 {
|
||||
self.saturation
|
||||
}
|
||||
|
||||
pub fn value(&self) -> f32 {
|
||||
self.value
|
||||
}
|
||||
|
||||
pub fn alpha(&self) -> f32 {
|
||||
self.alpha
|
||||
}
|
||||
|
||||
pub fn from_hwba(hwba: Hwba) -> Self {
|
||||
Self {
|
||||
hue: hwba.hue(),
|
||||
saturation: 1.0 - hwba.hue() / (1.0 - hwba.blackness()),
|
||||
value: 1.0 - hwba.blackness(),
|
||||
alpha: hwba.alpha()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_hsva(hsla: Hsla) -> Self {
|
||||
let value = hsla.lightness() + hsla.saturation() * hsla.lightness().min(1.0 - hsla.lightness());
|
||||
Self {
|
||||
hue: hsla.hue(),
|
||||
saturation: if value == 0.0 { 0.0 } else { 2.0 * (1.0 - hsla.lightness() / value) },
|
||||
value,
|
||||
alpha: hsla.alpha()
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
pub fn to_hwba(&self) -> Hwba {
|
||||
Hwba::new(self.hue, (1.0 - self.saturation) * self.value, 1.0 - self.value, self.alpha)
|
||||
}
|
||||
|
||||
pub fn to_hsla(&self) -> Hsla {
|
||||
let l = self.value * (1.0 - self.saturation * 0.5);
|
||||
Hsla::new(self.hue, if l == 0.0 || l == 1.0 { 0.0 } else { (self.value - l) / l.min(1.0 - l) }, l, self.alpha)
|
||||
}
|
||||
|
||||
pub fn to_rgba(&self) -> sRgba<f32> {
|
||||
self.to_hwba().to_rgba()
|
||||
}
|
||||
|
||||
pub fn to_rgba8(&self) -> sRgba<u8> {
|
||||
self.to_hwba().to_rgba8()
|
||||
}
|
||||
|
||||
pub fn to_linear(&self) -> LinearRgba {
|
||||
self.to_rgba().to_linear()
|
||||
}
|
||||
|
||||
pub fn to_xyza(&self) -> Xyza {
|
||||
self.to_linear().to_xyza()
|
||||
}
|
||||
|
||||
pub fn to_laba(&self) -> Laba {
|
||||
self.to_xyza().to_laba()
|
||||
}
|
||||
|
||||
pub fn to_lcha(&self) -> Lcha {
|
||||
self.to_laba().to_lcha()
|
||||
}
|
||||
|
||||
pub fn to_oklaba(&self) -> Oklaba {
|
||||
self.to_linear().to_oklaba()
|
||||
}
|
||||
|
||||
pub fn to_oklcha(&self) -> Oklcha {
|
||||
self.to_oklaba().to_oklcha()
|
||||
}
|
||||
}
|
||||
157
crates/comet_colors/src/hwba.rs
Normal file
157
crates/comet_colors/src/hwba.rs
Normal file
|
|
@ -0,0 +1,157 @@
|
|||
use crate::{sRgba, Hsla, Hsva, Laba, Lcha, LinearRgba, Oklaba, Oklcha, Xyza};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct Hwba {
|
||||
hue: f32,
|
||||
whiteness: f32,
|
||||
blackness: f32,
|
||||
alpha: f32
|
||||
}
|
||||
|
||||
impl Hwba {
|
||||
pub fn new(hue: f32, whiteness: f32, blackness: f32, alpha: f32) -> Self {
|
||||
assert!((0.0..=360.0).contains(&hue) && (0.0..=1.0).contains(&whiteness) && (0.0..=1.0).contains(&blackness) && (0.0..=1.0).contains(&alpha), "Hue needs to be in range 0..360\nWhiteness needs to be in range 0..1\nBlackness needs to be in range 0..1\nAlpha needs to be in range 0..1");
|
||||
Self {
|
||||
hue,
|
||||
whiteness,
|
||||
blackness,
|
||||
alpha
|
||||
}
|
||||
}
|
||||
|
||||
pub fn hue(&self) -> f32 {
|
||||
self.hue
|
||||
}
|
||||
|
||||
pub fn whiteness(&self) -> f32 {
|
||||
self.whiteness
|
||||
}
|
||||
|
||||
pub fn blackness(&self) -> f32 {
|
||||
self.blackness
|
||||
}
|
||||
|
||||
pub fn alpha(&self) -> f32 {
|
||||
self.alpha
|
||||
}
|
||||
|
||||
pub fn from_rgba8(&self, rgba: sRgba<u8>) -> Self {
|
||||
let rgba = rgba.to_rbga();
|
||||
self.from_rgba(rgba)
|
||||
}
|
||||
|
||||
pub fn from_rgba(&self, rgba: sRgba<f32>) -> Self {
|
||||
let max_rgb = rgba.red().max(rgba.green()).max(rgba.blue());
|
||||
let whiteness = rgba.red().min(rgba.green()).min(rgba.blue());
|
||||
|
||||
let blackness = 1.0 - max_rgb;
|
||||
let chroma = max_rgb - whiteness;
|
||||
|
||||
let hue = if chroma == 0.0 {
|
||||
0.0
|
||||
}
|
||||
else if max_rgb == rgba.red() {
|
||||
60.0 * ((rgba.green() - rgba.blue()) / chroma % 6.0)
|
||||
}
|
||||
else if max_rgb == rgba.green() {
|
||||
60.0 * ((rgba.blue() - rgba.red()) / chroma + 2.0)
|
||||
}
|
||||
else {
|
||||
60.0 * ((rgba.red() - rgba.green()) / chroma + 4.0)
|
||||
};
|
||||
|
||||
let hue = if hue < 0.0 { hue + 360.0 } else { hue };
|
||||
|
||||
Self {
|
||||
hue,
|
||||
whiteness,
|
||||
blackness,
|
||||
alpha: rgba.alpha()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_hsva(hsva: Hsva) -> Hwba {
|
||||
Hwba::new(
|
||||
hsva.hue(),
|
||||
(1.0 - hsva.saturation()) * hsva.value(),
|
||||
1.0 - hsva.value(),
|
||||
hsva.alpha()
|
||||
)
|
||||
}
|
||||
|
||||
pub fn to_rgba8(&self) -> sRgba<u8> {
|
||||
let rgba = self.to_rgba();
|
||||
|
||||
sRgba::<u8>::new(
|
||||
(rgba.red() * 255.0) as u8,
|
||||
(rgba.green() * 255.0) as u8,
|
||||
(rgba.blue() * 255.0) as u8,
|
||||
(rgba.alpha() * 255.0) as u8
|
||||
)
|
||||
}
|
||||
|
||||
pub fn to_rgba(&self) -> sRgba<f32> {
|
||||
let w = self.whiteness.min(1.0 - self.blackness);
|
||||
let c = 1.0 - self.whiteness - self.blackness;
|
||||
|
||||
let hue = self.hue % 360.0;
|
||||
let h_prime = hue / 60.0;
|
||||
|
||||
let x = c * (1.0 - (h_prime % 2.0 - 1.0).abs());
|
||||
|
||||
let (r1, g1, b1) = match h_prime as u32 {
|
||||
0 => (c, x, 0.0),
|
||||
1 => (x, c, 0.0),
|
||||
2 => (0.0, c, x),
|
||||
3 => (0.0, x, c),
|
||||
4 => (x, 0.0, c),
|
||||
5 => (c, 0.0, x),
|
||||
_ => (0.0, 0.0, 0.0)
|
||||
};
|
||||
|
||||
sRgba::<f32>::new(
|
||||
r1 + w,
|
||||
g1 + w,
|
||||
b1 + w,
|
||||
self.alpha()
|
||||
)
|
||||
}
|
||||
|
||||
pub fn to_hsva(&self) -> Hsva {
|
||||
Hsva::new(
|
||||
self.hue,
|
||||
1.0 - self.hue / (1.0 - self.blackness),
|
||||
1.0 - self.blackness,
|
||||
self.alpha
|
||||
)
|
||||
}
|
||||
|
||||
pub fn to_hsla(&self) -> Hsla {
|
||||
self.to_hsva().to_hsla()
|
||||
}
|
||||
|
||||
pub fn to_linear(&self) -> LinearRgba {
|
||||
self.to_rgba().to_linear()
|
||||
}
|
||||
|
||||
pub fn to_xyza(&self) -> Xyza {
|
||||
self.to_linear().to_xyza()
|
||||
}
|
||||
|
||||
pub fn to_laba(&self) -> Laba {
|
||||
self.to_xyza().to_laba()
|
||||
}
|
||||
|
||||
pub fn to_lcha(&self) -> Lcha {
|
||||
self.to_laba().to_lcha()
|
||||
}
|
||||
|
||||
pub fn to_oklaba(&self) -> Oklaba {
|
||||
self.to_linear().to_oklaba()
|
||||
}
|
||||
|
||||
pub fn to_oklcha(&self) -> Oklcha {
|
||||
self.to_oklaba().to_oklcha()
|
||||
}
|
||||
|
||||
}
|
||||
143
crates/comet_colors/src/laba.rs
Normal file
143
crates/comet_colors/src/laba.rs
Normal file
|
|
@ -0,0 +1,143 @@
|
|||
use crate::{sRgba, Hsla, Hsva, Hwba, Lcha, LinearRgba, Oklaba, Oklcha, Xyza};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct Laba {
|
||||
lightness: f32,
|
||||
a: f32,
|
||||
b: f32,
|
||||
alpha: f32
|
||||
}
|
||||
|
||||
impl Laba {
|
||||
pub fn new(lightness: f32, green_red: f32, blue_yellow: f32, alpha: f32) -> Self {
|
||||
assert!((0.0..=1.5).contains(&lightness) && (-1.5..=1.5).contains(&green_red) && (-1.5..=1.5).contains(&blue_yellow) && (0.0..=1.0).contains(&alpha), "Ligthness needs to be in range 0..1.5\nA needs to be in range -1.5..1.5\nB needs to be in range -1.5..1.5\nAlpha needs to be in range 0..1");
|
||||
Self {
|
||||
lightness,
|
||||
a: green_red,
|
||||
b: blue_yellow,
|
||||
alpha
|
||||
}
|
||||
}
|
||||
|
||||
pub fn lightness(&self) -> f32 {
|
||||
self.lightness
|
||||
}
|
||||
|
||||
pub fn a(&self) -> f32 {
|
||||
self.a
|
||||
}
|
||||
|
||||
pub fn b(&self) -> f32 {
|
||||
self.b
|
||||
}
|
||||
|
||||
pub fn alpha(&self) -> f32 {
|
||||
self.alpha
|
||||
}
|
||||
|
||||
pub fn from_xyza(xyza: Xyza) -> Self {
|
||||
let reference_white = Xyza::new(0.95047, 1.0, 1.08883, 1.0);
|
||||
|
||||
let x_r = xyza.x() / reference_white.x();
|
||||
let y_r = xyza.y() / reference_white.y();
|
||||
let z_r = xyza.z() / reference_white.z();
|
||||
|
||||
let epsilon: f32 = 0.008856;
|
||||
let kappa: f32 = 903.3;
|
||||
|
||||
let f_x = if x_r > epsilon { x_r.cbrt() } else { ( kappa*x_r + 16.0 ) / 116.0 };
|
||||
let f_y = if x_r > epsilon { y_r.cbrt() } else { ( kappa*y_r + 16.0 ) / 116.0 };
|
||||
let f_z = if x_r > epsilon { z_r.cbrt() } else { ( kappa*z_r + 16.0 ) / 116.0 };
|
||||
|
||||
Self {
|
||||
lightness: 1.16*f_y-0.16,
|
||||
a: 5.0*( f_x - f_y ),
|
||||
b: 2.0*( f_y - f_z ),
|
||||
alpha: xyza.alpha()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_lcha(&self) -> Lcha {
|
||||
let atan: f32 = self.b.atan2(self.a);
|
||||
Lcha::new(
|
||||
self.lightness,
|
||||
(self.a*self.a + self.b*self.b).sqrt(),
|
||||
if atan >= 0.0 { atan } else { atan + 360.0 },
|
||||
self.alpha
|
||||
)
|
||||
}
|
||||
|
||||
pub fn to_xyza(&self) -> Xyza{
|
||||
let epsilon: f32 = 0.008856;
|
||||
let kappa: f32 = 903.3;
|
||||
|
||||
let l = 100. * self.lightness;
|
||||
let a = 100. * self.a;
|
||||
let b = 100. * self.b;
|
||||
|
||||
let fy = (l + 16.0) / 116.0;
|
||||
let fx = a / 500.0 + fy;
|
||||
let fz = fy - b / 200.0;
|
||||
let xr = {
|
||||
let fx3 = fx.powf(3.0);
|
||||
|
||||
if fx3 > epsilon {
|
||||
fx3
|
||||
} else {
|
||||
(116.0 * fx - 16.0) / kappa
|
||||
}
|
||||
};
|
||||
let yr = if l > epsilon * kappa {
|
||||
((l + 16.0) / 116.0).powf(3.0)
|
||||
} else {
|
||||
l / kappa
|
||||
};
|
||||
let zr = {
|
||||
let fz3 = fz.powf(3.0);
|
||||
|
||||
if fz3 > epsilon {
|
||||
fz3
|
||||
} else {
|
||||
(116.0 * fz - 16.0) / kappa
|
||||
}
|
||||
};
|
||||
|
||||
let x = xr * 0.95047;
|
||||
let y = yr * 1.0;
|
||||
let z = zr * 1.08883;
|
||||
|
||||
Xyza::new(x, y, z, self.alpha)
|
||||
}
|
||||
|
||||
pub fn to_linear(&self) -> LinearRgba {
|
||||
self.to_xyza().to_linear()
|
||||
}
|
||||
|
||||
pub fn to_oklaba(&self) -> Oklaba {
|
||||
self.to_linear().to_oklaba()
|
||||
}
|
||||
|
||||
pub fn to_oklcha(&self) -> Oklcha {
|
||||
self.to_oklaba().to_oklcha()
|
||||
}
|
||||
|
||||
pub fn to_rgba(&self) -> sRgba<f32> {
|
||||
self.to_linear().to_rgba()
|
||||
}
|
||||
|
||||
pub fn to_rgba8(&self) -> sRgba<u8> {
|
||||
self.to_rgba().to_rgba8()
|
||||
}
|
||||
|
||||
pub fn to_hwba(&self) -> Hwba {
|
||||
self.to_rgba().to_hwba()
|
||||
}
|
||||
|
||||
pub fn to_hsva(&self) -> Hsva {
|
||||
self.to_hwba().to_hsva()
|
||||
}
|
||||
|
||||
pub fn to_hsla(&self) -> Hsla {
|
||||
self.to_hsva().to_hsla()
|
||||
}
|
||||
}
|
||||
93
crates/comet_colors/src/lcha.rs
Normal file
93
crates/comet_colors/src/lcha.rs
Normal file
|
|
@ -0,0 +1,93 @@
|
|||
use crate::{sRgba, Hsla, Hsva, Hwba, Laba, LinearRgba, Oklaba, Oklcha, Xyza};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct Lcha {
|
||||
lightness: f32,
|
||||
chroma: f32,
|
||||
hue: f32,
|
||||
alpha: f32
|
||||
}
|
||||
|
||||
impl Lcha {
|
||||
pub fn new(lightness: f32, chroma: f32, hue: f32, alpha: f32) -> Self {
|
||||
assert!((0.0..=1.5).contains(&lightness) && (0.0..=1.5).contains(&chroma) && (0.0..=360.0).contains(&hue) && (0.0..=1.0).contains(&alpha), "Ligthness needs to be in range 0..1.5\nChroma needs to be in range 0..1.5\nHue needs to be in range 0..360\nAlpha needs to be in range 0..1");
|
||||
Self {
|
||||
lightness,
|
||||
chroma,
|
||||
hue,
|
||||
alpha
|
||||
}
|
||||
}
|
||||
|
||||
pub fn lightness(&self) -> f32 {
|
||||
self.lightness
|
||||
}
|
||||
|
||||
pub fn chroma(&self) -> f32 {
|
||||
self.chroma
|
||||
}
|
||||
|
||||
pub fn hue(&self) -> f32 {
|
||||
self.hue
|
||||
}
|
||||
|
||||
pub fn alpha(&self) -> f32 {
|
||||
self.alpha
|
||||
}
|
||||
|
||||
pub fn from_laba(laba: Laba) -> Self {
|
||||
let atan: f32 = laba.b().atan2(laba.a());
|
||||
Self {
|
||||
lightness: laba.lightness(),
|
||||
chroma: (laba.a()*laba.a() + laba.b()*laba.b()).sqrt(),
|
||||
hue: if atan >= 0.0 { atan } else { atan + 360.0 },
|
||||
alpha: laba.alpha()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_laba(&self) -> Laba {
|
||||
Laba::new(
|
||||
self.lightness,
|
||||
self.chroma * self.hue.cos().to_radians(),
|
||||
self.chroma * self.hue.sin().to_radians(),
|
||||
self.alpha
|
||||
)
|
||||
}
|
||||
|
||||
pub fn to_xyza(&self) -> Xyza {
|
||||
self.to_laba().to_xyza()
|
||||
}
|
||||
|
||||
pub fn to_linear(&self) -> LinearRgba {
|
||||
self.to_xyza().to_linear()
|
||||
}
|
||||
|
||||
pub fn to_oklaba(&self) -> Oklaba {
|
||||
self.to_linear().to_oklaba()
|
||||
}
|
||||
|
||||
pub fn to_oklcha(&self) -> Oklcha {
|
||||
self.to_oklaba().to_oklcha()
|
||||
}
|
||||
|
||||
pub fn to_rgba(&self) -> sRgba<f32> {
|
||||
self.to_linear().to_rgba()
|
||||
}
|
||||
|
||||
pub fn to_rgba8(&self) -> sRgba<u8> {
|
||||
self.to_linear().to_rgba8()
|
||||
}
|
||||
|
||||
pub fn to_hwba(&self) -> Hwba {
|
||||
self.to_rgba().to_hwba()
|
||||
}
|
||||
|
||||
pub fn to_hsva(&self) -> Hsva {
|
||||
self.to_hwba().to_hsva()
|
||||
}
|
||||
|
||||
pub fn to_hsla(&self) -> Hsla {
|
||||
self.to_hsva().to_hsla()
|
||||
}
|
||||
|
||||
}
|
||||
22
crates/comet_colors/src/lib.rs
Normal file
22
crates/comet_colors/src/lib.rs
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
pub use comet_math as math;
|
||||
pub use linear_rgba::*;
|
||||
pub use rgba::*;
|
||||
pub use hwba::*;
|
||||
pub use hsva::*;
|
||||
pub use hsla::*;
|
||||
pub use xyza::*;
|
||||
pub use laba::*;
|
||||
pub use lcha::*;
|
||||
pub use oklaba::*;
|
||||
pub use oklcha::*;
|
||||
|
||||
mod rgba;
|
||||
mod linear_rgba;
|
||||
mod hwba;
|
||||
mod hsva;
|
||||
mod hsla;
|
||||
mod xyza;
|
||||
mod laba;
|
||||
mod lcha;
|
||||
mod oklaba;
|
||||
mod oklcha;
|
||||
135
crates/comet_colors/src/linear_rgba.rs
Normal file
135
crates/comet_colors/src/linear_rgba.rs
Normal file
|
|
@ -0,0 +1,135 @@
|
|||
use wgpu::Color;
|
||||
use crate::{sRgba, Hsla, Hsva, Hwba, Laba, Lcha, Oklaba, Oklcha, Xyza};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct LinearRgba {
|
||||
red: f32,
|
||||
green: f32,
|
||||
blue: f32,
|
||||
alpha: f32
|
||||
}
|
||||
|
||||
impl LinearRgba {
|
||||
pub fn new(red: f32, green: f32, blue: f32, alpha: f32) -> Self {
|
||||
assert!((0.0..=1.0).contains(&red) && (0.0..=1.0).contains(&green) && (0.0..=1.0).contains(&blue) && (0.0..=1.0).contains(&alpha), "Red needs to be in range 0..1\nGreen needs to be in range 0..1\nBlue needs to be in range 0..1\nAlpha needs to be in range 0..1");
|
||||
Self {
|
||||
red,
|
||||
green,
|
||||
blue,
|
||||
alpha
|
||||
}
|
||||
}
|
||||
|
||||
pub fn red(&self) -> f32 {
|
||||
self.red
|
||||
}
|
||||
|
||||
pub fn green(&self) -> f32 {
|
||||
self.green
|
||||
}
|
||||
|
||||
pub fn blue(&self) -> f32 {
|
||||
self.blue
|
||||
}
|
||||
|
||||
pub fn alpha(&self) -> f32 {
|
||||
self.alpha
|
||||
}
|
||||
|
||||
pub fn from_rgba(srgba: sRgba<f32>) -> Self {
|
||||
Self {
|
||||
red: if srgba.red() <= 0.04045 { srgba.red() / 12.92 } else { ( ( srgba.red() + 0.055 ) / 1.055 ).powf(2.4) },
|
||||
green: if srgba.green() <= 0.04045 { srgba.green() / 12.92 } else { ( ( srgba.green() + 0.055 ) / 1.055 ).powf(2.4) },
|
||||
blue: if srgba.blue() <= 0.04045 { srgba.blue() / 12.92 } else { ( ( srgba.blue() + 0.055 ) / 1.055 ).powf(2.4) },
|
||||
alpha: srgba.alpha()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_xyza(xyz: Xyza) -> Self {
|
||||
Self {
|
||||
red: 3.2404542 * xyz.x() + -1.5371385 * xyz.y() + -0.4985314 * xyz.z(),
|
||||
green: -0.9692660 * xyz.x() + 1.8760108 * xyz.y() + 0.0415560 * xyz.z(),
|
||||
blue: 0.0556434 * xyz.x() + -0.2040259 * xyz.y() + 1.0572252 * xyz.z(),
|
||||
alpha: 1.0
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_rgba(&self) -> sRgba<f32> {
|
||||
sRgba::<f32>::new(
|
||||
if self.red <= 0.0031308 { self.red * 12.92 } else { 1.055 * self.red.powf( 1.0 / 2.4 ) - 0.055 },
|
||||
if self.green <= 0.0031308 { self.green * 12.92 } else { 1.055 * self.green.powf( 1.0 / 2.4 ) - 0.055 },
|
||||
if self.blue <= 0.0031308 { self.blue * 12.92 } else { 1.055 * self.blue.powf( 1.0 / 2.4 ) - 0.055 },
|
||||
self.alpha
|
||||
)
|
||||
}
|
||||
|
||||
pub fn to_rgba8(&self) -> sRgba<u8> {
|
||||
let color = self.to_rgba();
|
||||
|
||||
sRgba::<u8>::new(
|
||||
(color.red() * 255.0) as u8,
|
||||
(color.green() * 255.0) as u8,
|
||||
(color.blue() * 255.0) as u8,
|
||||
(color.alpha() * 255.0) as u8,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn to_oklaba(&self) -> Oklaba {
|
||||
let l = 0.4122214708 * self.red + 0.5363325363 * self.green + 0.0514459929 * self.blue;
|
||||
let m = 0.2119034982 * self.red + 0.6806995451 * self.green + 0.1073969566 * self.blue;
|
||||
let s = 0.0883024619 * self.red + 0.2817188376 * self.green + 0.6299787005 * self.blue;
|
||||
|
||||
let l_ = l.cbrt();
|
||||
let m_ = m.cbrt();
|
||||
let s_ = s.cbrt();
|
||||
|
||||
Oklaba::new(
|
||||
0.2104542553*l_ + 0.7936177850*m_ - 0.0040720468*s_,
|
||||
1.9779984951*l_ - 2.4285922050*m_ + 0.4505937099*s_,
|
||||
0.0259040371*l_ + 0.7827717662*m_ - 0.8086757660*s_,
|
||||
self.alpha
|
||||
)
|
||||
}
|
||||
|
||||
pub fn to_oklcha(&self) -> Oklcha {
|
||||
self.to_oklaba().to_oklcha()
|
||||
}
|
||||
|
||||
pub fn to_xyza(&self) -> Xyza {
|
||||
Xyza::new(
|
||||
0.4124564 * self.red + 0.3575761 * self.green + 0.1804375 * self.blue,
|
||||
0.2126729 * self.red + 0.7151522 * self.green + 0.0721750 * self.blue,
|
||||
0.0193339 * self.red + 0.1191920 * self.green + 0.9503041 * self.blue,
|
||||
self.alpha
|
||||
)
|
||||
}
|
||||
|
||||
pub fn to_laba(&self) -> Laba {
|
||||
self.to_xyza().to_laba()
|
||||
}
|
||||
|
||||
pub fn to_lcha(&self) -> Lcha {
|
||||
self.to_laba().to_lcha()
|
||||
}
|
||||
|
||||
pub fn to_hwba(&self) -> Hwba {
|
||||
self.to_rgba().to_hwba()
|
||||
}
|
||||
|
||||
pub fn to_hsva(&self) -> Hsva {
|
||||
self.to_hwba().to_hsva()
|
||||
}
|
||||
|
||||
pub fn to_hsla(&self) -> Hsla {
|
||||
self.to_hsva().to_hsla()
|
||||
}
|
||||
|
||||
pub fn to_wgpu(&self) -> Color {
|
||||
Color {
|
||||
r: self.red as f64,
|
||||
g: self.green as f64,
|
||||
b: self.blue as f64,
|
||||
a: self.alpha as f64
|
||||
}
|
||||
}
|
||||
}
|
||||
112
crates/comet_colors/src/oklaba.rs
Normal file
112
crates/comet_colors/src/oklaba.rs
Normal file
|
|
@ -0,0 +1,112 @@
|
|||
use crate::{sRgba, Hsla, Hsva, Hwba, Laba, Lcha, LinearRgba, Oklcha, Xyza};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct Oklaba {
|
||||
lightness: f32,
|
||||
a: f32,
|
||||
b: f32,
|
||||
alpha: f32
|
||||
}
|
||||
|
||||
impl Oklaba {
|
||||
pub fn new(lightness: f32, green_red: f32, blue_yellow: f32, alpha: f32) -> Self {
|
||||
assert!((0.0..=1.0).contains(&lightness) && (-1.0..=1.0).contains(&green_red) && (-1.0..=1.0).contains(&blue_yellow) && (0.0..=1.0).contains(&alpha), "Ligthness needs to be in range 0..1.0\nA needs to be in range -1.0..1.0\nB needs to be in range -1.0..1.0\nAlpha needs to be in range 0..1");
|
||||
Self {
|
||||
lightness,
|
||||
a: green_red,
|
||||
b: blue_yellow,
|
||||
alpha
|
||||
}
|
||||
}
|
||||
|
||||
pub fn lightness(&self) -> f32 {
|
||||
self.lightness
|
||||
}
|
||||
|
||||
pub fn a(&self) -> f32 {
|
||||
self.a
|
||||
}
|
||||
|
||||
pub fn b(&self) -> f32 {
|
||||
self.b
|
||||
}
|
||||
|
||||
pub fn alpha(&self) -> f32 {
|
||||
self.alpha
|
||||
}
|
||||
|
||||
pub fn from_linear(linear: LinearRgba) -> Self {
|
||||
let l = 0.4122214708 * linear.red() + 0.5363325363 * linear.green() + 0.0514459929 * linear.blue();
|
||||
let m = 0.2119034982 * linear.red() + 0.6806995451 * linear.green() + 0.1073969566 * linear.blue();
|
||||
let s = 0.0883024619 * linear.red() + 0.2817188376 * linear.green() + 0.6299787005 * linear.blue();
|
||||
|
||||
let l_ = l.cbrt();
|
||||
let m_ = m.cbrt();
|
||||
let s_ = s.cbrt();
|
||||
|
||||
Self {
|
||||
lightness: 0.2104542553*l_ + 0.7936177850*m_ - 0.0040720468*s_,
|
||||
a: 1.9779984951*l_ - 2.4285922050*m_ + 0.4505937099*s_,
|
||||
b: 0.0259040371*l_ + 0.7827717662*m_ - 0.8086757660*s_,
|
||||
alpha: linear.alpha()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_linear(&self) -> LinearRgba {
|
||||
let l_ = self.lightness + 0.3963377774 * self.a + 0.2158037573 * self.b;
|
||||
let m_ = self.lightness - 0.1055613458 * self.a - 0.0638541728 * self.b;
|
||||
let s_ = self.lightness - 0.0894841775 * self.a - 1.2914855480 * self.b;
|
||||
|
||||
let l = l_*l_*l_;
|
||||
let m = m_*m_*m_;
|
||||
let s = s_*s_*s_;
|
||||
|
||||
LinearRgba::new(
|
||||
4.0767416621 * l - 3.3077115913 * m + 0.2309699292 * s,
|
||||
-1.2684380046 * l + 2.6097574011 * m - 0.3413193965 * s,
|
||||
-0.0041960863 * l - 0.7034186147 * m + 1.7076147010 * s,
|
||||
self.alpha
|
||||
)
|
||||
}
|
||||
|
||||
pub fn to_rgba(&self) -> sRgba<f32> {
|
||||
self.to_linear().to_rgba()
|
||||
}
|
||||
|
||||
pub fn to_rgba8(&self) -> sRgba<u8> {
|
||||
self.to_rgba().to_rgba8()
|
||||
}
|
||||
|
||||
pub fn to_oklcha(&self) -> Oklcha {
|
||||
Oklcha::new(
|
||||
self.lightness,
|
||||
(self.a*self.a + self.b*self.b).sqrt(),
|
||||
self.b.atan2(self.a),
|
||||
self.alpha
|
||||
)
|
||||
}
|
||||
|
||||
pub fn to_xyza(&self) -> Xyza {
|
||||
self.to_linear().to_xyza()
|
||||
}
|
||||
|
||||
pub fn to_laba(&self) -> Laba {
|
||||
self.to_xyza().to_laba()
|
||||
}
|
||||
|
||||
pub fn to_lcha(&self) -> Lcha {
|
||||
self.to_laba().to_lcha()
|
||||
}
|
||||
|
||||
pub fn to_hwba(&self) -> Hwba {
|
||||
self.to_rgba().to_hwba()
|
||||
}
|
||||
|
||||
pub fn to_hsva(&self) -> Hsva {
|
||||
self.to_hwba().to_hsva()
|
||||
}
|
||||
|
||||
pub fn to_hsla(&self) -> Hsla {
|
||||
self.to_hsva().to_hsla()
|
||||
}
|
||||
}
|
||||
91
crates/comet_colors/src/oklcha.rs
Normal file
91
crates/comet_colors/src/oklcha.rs
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
use crate::{sRgba, Hsla, Hsva, Hwba, Laba, Lcha, LinearRgba, Oklaba, Xyza};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct Oklcha {
|
||||
lightness: f32,
|
||||
chroma: f32,
|
||||
hue: f32,
|
||||
alpha: f32
|
||||
}
|
||||
|
||||
impl Oklcha {
|
||||
pub fn new(lightness: f32, chroma: f32, hue: f32, alpha: f32) -> Self {
|
||||
assert!((0.0..=1.0).contains(&lightness) && (0.0..=1.0).contains(&chroma) && (0.0..=360.0).contains(&hue) && (0.0..=1.0).contains(&alpha), "Ligthness needs to be in range 0..1\nChroma needs to be in range 0..1\nHue needs to be in range 0..360\nAlpha needs to be in range 0..1");
|
||||
Self {
|
||||
lightness,
|
||||
chroma,
|
||||
hue,
|
||||
alpha
|
||||
}
|
||||
}
|
||||
|
||||
pub fn lightness(&self) -> f32 {
|
||||
self.lightness
|
||||
}
|
||||
|
||||
pub fn chroma(&self) -> f32 {
|
||||
self.chroma
|
||||
}
|
||||
|
||||
pub fn hue(&self) -> f32 {
|
||||
self.hue
|
||||
}
|
||||
|
||||
pub fn alpha(&self) -> f32 {
|
||||
self.alpha
|
||||
}
|
||||
|
||||
pub fn from_oklaba(oklaba: Oklaba) -> Self {
|
||||
Self {
|
||||
lightness: oklaba.lightness(),
|
||||
chroma: (oklaba.a()*oklaba.a() + oklaba.b()*oklaba.b()).sqrt(),
|
||||
hue: oklaba.b().atan2(oklaba.a()),
|
||||
alpha: oklaba.alpha()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_oklaba(&self) -> Oklaba {
|
||||
Oklaba::new(
|
||||
self.lightness(),
|
||||
self.chroma() * self.hue().cos(),
|
||||
self.chroma() * self.hue().sin(),
|
||||
self.alpha()
|
||||
)
|
||||
}
|
||||
|
||||
pub fn to_linear(&self) -> LinearRgba {
|
||||
self.to_oklaba().to_linear()
|
||||
}
|
||||
|
||||
pub fn to_xyza(&self) -> Xyza {
|
||||
self.to_linear().to_xyza()
|
||||
}
|
||||
|
||||
pub fn to_laba(&self) -> Laba {
|
||||
self.to_xyza().to_laba()
|
||||
}
|
||||
|
||||
pub fn to_lcha(&self) -> Lcha {
|
||||
self.to_laba().to_lcha()
|
||||
}
|
||||
|
||||
pub fn to_rgba(&self) -> sRgba<f32> {
|
||||
self.to_linear().to_rgba()
|
||||
}
|
||||
|
||||
pub fn to_rgba8(&self) -> sRgba<u8> {
|
||||
self.to_linear().to_rgba8()
|
||||
}
|
||||
|
||||
pub fn to_hwba(&self) -> Hwba {
|
||||
self.to_rgba().to_hwba()
|
||||
}
|
||||
|
||||
pub fn to_hsva(&self) -> Hsva {
|
||||
self.to_hwba().to_hsva()
|
||||
}
|
||||
|
||||
pub fn to_hsla(&self) -> Hsla {
|
||||
self.to_hsva().to_hsla()
|
||||
}
|
||||
}
|
||||
359
crates/comet_colors/src/rgba.rs
Normal file
359
crates/comet_colors/src/rgba.rs
Normal file
|
|
@ -0,0 +1,359 @@
|
|||
use crate::{math::Vec4, Hsla, Hsva, Hwba, Laba, Lcha, LinearRgba, Oklaba, Oklcha, Xyza};
|
||||
|
||||
/// sRGB representation of color
|
||||
/// There are two variants: `sRgba<u8>` and `sRgba<f32>`
|
||||
/// The first one is your standard 0..255 RGB and the second is the normalized version with range 0..1
|
||||
#[allow(non_camel_case_types)]
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct sRgba<T> {
|
||||
red: T,
|
||||
green: T,
|
||||
blue: T,
|
||||
alpha: T,
|
||||
}
|
||||
|
||||
impl sRgba<u8> {
|
||||
pub fn new(red: u8, green: u8, blue: u8, alpha: u8) -> Self {
|
||||
assert!((0..=255).contains(&red) && (0..=255).contains(&green) && (0..=255).contains(&blue) && (0..=255).contains(&alpha), "Red needs to be in range 0..255\nGreen needs to be in range 0..255\nBlue needs to be in range 0..255\nAlpha needs to be in range 0..255");
|
||||
Self {
|
||||
red,
|
||||
green,
|
||||
blue,
|
||||
alpha
|
||||
}
|
||||
}
|
||||
|
||||
pub fn red(&self) -> u8 {
|
||||
self.red
|
||||
}
|
||||
|
||||
pub fn green(&self) -> u8 {
|
||||
self.green
|
||||
}
|
||||
|
||||
pub fn blue(&self) -> u8 {
|
||||
self.blue
|
||||
}
|
||||
|
||||
pub fn alpha(&self) -> u8 {
|
||||
self.alpha
|
||||
}
|
||||
|
||||
pub fn from_hex(hex: &str) -> Self {
|
||||
let hex = hex.trim_start_matches("#");
|
||||
|
||||
if hex.len() != 8 {
|
||||
panic!("The length of the hex string is not equal to 8!");
|
||||
}
|
||||
|
||||
let red = match u8::from_str_radix(&hex[0..2], 16).map_err(|_| "Red part is not a hex value!") {
|
||||
Ok(v) => v,
|
||||
Err(err) => panic!("{}", err)
|
||||
};
|
||||
|
||||
let green = match u8::from_str_radix(&hex[2..4], 16).map_err(|_| "Green part is not a hex value!") {
|
||||
Ok(v) => v,
|
||||
Err(err) => panic!("{}", err)
|
||||
};
|
||||
|
||||
let blue = match u8::from_str_radix(&hex[4..6], 16).map_err(|_| "Blue part is not a hex value!") {
|
||||
Ok(v) => v,
|
||||
Err(err) => panic!("{}", err)
|
||||
};
|
||||
|
||||
let alpha = match u8::from_str_radix(&hex[6..8], 16).map_err(|_| "Alpha part is not a hex value!") {
|
||||
Ok(v) => v,
|
||||
Err(err) => panic!("{}", err)
|
||||
};
|
||||
|
||||
Self {
|
||||
red,
|
||||
green,
|
||||
blue,
|
||||
alpha
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_rgba(rgba: sRgba<f32>) -> Self {
|
||||
rgba.to_rgba8()
|
||||
}
|
||||
|
||||
pub fn from_hwba(hwba: Hwba) -> Self {
|
||||
hwba.to_rgba8()
|
||||
}
|
||||
|
||||
pub fn from_hsva(hvsa: Hsva) -> Self {
|
||||
hvsa.to_rgba8()
|
||||
}
|
||||
|
||||
pub fn from_hsla(hsla: Hsla) -> Self {
|
||||
hsla.to_rgba8()
|
||||
}
|
||||
|
||||
pub fn from_xyza(xyza: Xyza) -> Self {
|
||||
xyza.to_rgba8()
|
||||
}
|
||||
|
||||
pub fn from_laba(laba: Laba) -> Self {
|
||||
laba.to_rgba8()
|
||||
}
|
||||
|
||||
pub fn from_lcha(lcha: Lcha) -> Self {
|
||||
lcha.to_rgba8()
|
||||
}
|
||||
|
||||
pub fn from_oklaba(oklaba: Oklaba) -> Self {
|
||||
oklaba.to_rgba8()
|
||||
}
|
||||
|
||||
pub fn from_oklcha(oklcha: Oklcha) -> Self {
|
||||
oklcha.to_rgba8()
|
||||
}
|
||||
|
||||
pub fn to_rbga(&self) -> sRgba<f32> {
|
||||
sRgba {
|
||||
red: self.red as f32/255.0,
|
||||
green: self.green as f32/255.0,
|
||||
blue: self.blue as f32/255.0,
|
||||
alpha: self.alpha as f32/255.0
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_linear(&self) -> LinearRgba {
|
||||
self.to_rbga().to_linear()
|
||||
}
|
||||
|
||||
pub fn to_hwba(&self) -> Hwba {
|
||||
self.to_rbga().to_hwba()
|
||||
}
|
||||
|
||||
pub fn to_hsva(&self) -> Hsva {
|
||||
self.to_hwba().to_hsva()
|
||||
}
|
||||
|
||||
pub fn to_hsla(&self) -> Hsla {
|
||||
self.to_hsva().to_hsla()
|
||||
}
|
||||
|
||||
pub fn to_oklaba(&self) -> Oklaba {
|
||||
self.to_linear().to_oklaba()
|
||||
}
|
||||
|
||||
pub fn to_vec(&self) -> Vec4 {
|
||||
Vec4::new(
|
||||
self.red as f32,
|
||||
self.green as f32,
|
||||
self.blue as f32,
|
||||
self.alpha as f32
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl sRgba<f32> {
|
||||
pub fn new(red: f32, green: f32, blue: f32, alpha: f32) -> Self {
|
||||
assert!((0.0..1.0).contains(&red) && (0.0..1.0).contains(&green) && (0.0..1.0).contains(&blue) && (0.0..1.0).contains(&alpha), "Red needs to be in range 0..1\nGreen needs to be in range 0..1\nBlue needs to be in range 0..1\nAlpha needs to be in range 0..1");
|
||||
Self {
|
||||
red,
|
||||
green,
|
||||
blue,
|
||||
alpha
|
||||
}
|
||||
}
|
||||
|
||||
pub fn red(&self) -> f32 {
|
||||
self.red
|
||||
}
|
||||
|
||||
pub fn green(&self) -> f32 {
|
||||
self.green
|
||||
}
|
||||
|
||||
pub fn blue(&self) -> f32 {
|
||||
self.blue
|
||||
}
|
||||
|
||||
pub fn alpha(&self) ->f32 {
|
||||
self.alpha
|
||||
}
|
||||
|
||||
pub fn from_hex(hex: &str) -> Self {
|
||||
let hex = hex.trim_start_matches("#");
|
||||
|
||||
if hex.len() != 8 {
|
||||
panic!("The length of the hex string is not equal to 6!");
|
||||
}
|
||||
|
||||
let r = match u8::from_str_radix(&hex[0..2], 16).map_err(|_| "Red is not a hex value!") {
|
||||
Ok(v) => v,
|
||||
Err(err) => panic!("{}", err)
|
||||
};
|
||||
|
||||
let g = match u8::from_str_radix(&hex[2..4], 16).map_err(|_| "Green is not a hex value!") {
|
||||
Ok(v) => v,
|
||||
Err(err) => panic!("{}", err)
|
||||
};
|
||||
|
||||
let b = match u8::from_str_radix(&hex[4..6], 16).map_err(|_| "Blue is not a hex value!") {
|
||||
Ok(v) => v,
|
||||
Err(err) => panic!("{}", err)
|
||||
};
|
||||
|
||||
let a = match u8::from_str_radix(&hex[6..8], 16).map_err(|_| "Alpha is not a hex value!") {
|
||||
Ok(v) => v,
|
||||
Err(err) => panic!("{}", err)
|
||||
};
|
||||
|
||||
Self {
|
||||
red: r as f32 / 255.0,
|
||||
green: g as f32 / 255.0,
|
||||
blue: b as f32 / 255.0,
|
||||
alpha: a as f32 / 255.0
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_linear(linear: LinearRgba) -> Self {
|
||||
Self {
|
||||
red: if linear.red() <= 0.0031308 { 12.92 * linear.red() } else { 1.055 * linear.red().powf(1.0 / 2.4) - 0.055 },
|
||||
green: if linear.green() <= 0.0031308 { 12.92 * linear.green() } else { 1.055 * linear.green().powf(1.0 / 2.4) - 0.055 },
|
||||
blue: if linear.blue() <= 0.0031308 { 12.92 * linear.blue() } else { 1.055 * linear.blue().powf(1.0 / 2.4) - 0.055 },
|
||||
alpha: linear.alpha()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_rgba8(rgba: sRgba<u8>) -> Self {
|
||||
Self {
|
||||
red: rgba.red() as f32 / 255.0,
|
||||
green: rgba.green() as f32 / 255.0,
|
||||
blue: rgba.blue() as f32 / 255.0,
|
||||
alpha: rgba.alpha() as f32 / 255.0
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_hwba(hwba: Hwba) -> Self {
|
||||
hwba.to_rgba()
|
||||
}
|
||||
|
||||
pub fn from_hsva(hvsa: Hsva) -> Self {
|
||||
hvsa.to_rgba()
|
||||
}
|
||||
|
||||
pub fn from_hsla(hsla: Hsla) -> Self {
|
||||
hsla.to_rgba()
|
||||
}
|
||||
|
||||
pub fn from_xyza(xyza: Xyza) -> Self {
|
||||
xyza.to_rgba()
|
||||
}
|
||||
|
||||
pub fn from_laba(laba: Laba) -> Self {
|
||||
laba.to_rgba()
|
||||
}
|
||||
|
||||
pub fn from_lcha(lcha: Lcha) -> Self {
|
||||
lcha.to_rgba()
|
||||
}
|
||||
|
||||
pub fn from_oklaba(oklaba: Oklaba) -> Self {
|
||||
oklaba.to_rgba()
|
||||
}
|
||||
|
||||
pub fn from_oklcha(oklcha: Oklcha) -> Self {
|
||||
oklcha.to_rgba()
|
||||
}
|
||||
|
||||
pub fn to_rgba8(&self) -> sRgba<u8> {
|
||||
sRgba {
|
||||
red: (self.red * 255.0) as u8,
|
||||
green: (self.green * 255.0) as u8,
|
||||
blue: (self.blue * 255.0) as u8,
|
||||
alpha: (self.alpha * 255.0) as u8
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_linear(&self) -> LinearRgba {
|
||||
LinearRgba::new(
|
||||
if self.red() <= 0.04045 { self.red() / 12.92 } else { ( ( self.red() + 0.055 ) / 1.055 ).powf(2.4) },
|
||||
if self.green() <= 0.04045 { self.green() / 12.92 } else { ( ( self.green() + 0.055 ) / 1.055 ).powf(2.4) },
|
||||
if self.blue() <= 0.04045 { self.blue() / 12.92 } else { ( ( self.blue() + 0.055 ) / 1.055 ).powf(2.4) },
|
||||
self.alpha()
|
||||
)
|
||||
}
|
||||
|
||||
pub fn to_oklaba(&self) -> Oklaba {
|
||||
self.to_linear().to_oklaba()
|
||||
}
|
||||
|
||||
pub fn to_oklcha(&self) -> Oklcha {
|
||||
self.to_oklaba().to_oklcha()
|
||||
}
|
||||
|
||||
pub fn to_xyza(&self) -> Xyza {
|
||||
self.to_linear().to_xyza()
|
||||
}
|
||||
|
||||
pub fn to_laba(&self) -> Laba {
|
||||
self.to_xyza().to_laba()
|
||||
}
|
||||
|
||||
pub fn to_lcha(&self) -> Lcha {
|
||||
self.to_laba().to_lcha()
|
||||
}
|
||||
|
||||
pub fn to_hwba(&self) -> Hwba {
|
||||
let w = self.red.min(self.green).min(self.blue);
|
||||
let v = self.red.max(self.green).max(self.blue);
|
||||
let b = 1.0 - v;
|
||||
|
||||
if v == w {
|
||||
return Hwba::new(
|
||||
0.0,
|
||||
w,
|
||||
b,
|
||||
self.alpha()
|
||||
)
|
||||
}
|
||||
|
||||
let f = if self.red == v {
|
||||
(self.green - self.blue) / (v - w)
|
||||
} else if self.green == v {
|
||||
(self.blue - self.red) / (v - w)
|
||||
} else {
|
||||
(self.red - self.green) / (v - w)
|
||||
};
|
||||
|
||||
let h = if self.red == v {
|
||||
(f / 6.0) % 1.0
|
||||
} else if self.green == v {
|
||||
(f + 2.0) / 6.0
|
||||
} else {
|
||||
(f + 4.0) / 6.0
|
||||
};
|
||||
|
||||
let mut h = if h < 0.0 { h + 1.0 } else { h };
|
||||
h *= 360.0;
|
||||
|
||||
Hwba::new(
|
||||
h,
|
||||
w,
|
||||
b,
|
||||
self.alpha() as f32 / 255.0
|
||||
)
|
||||
}
|
||||
|
||||
pub fn to_hsva(&self) -> Hsva {
|
||||
self.to_hwba().to_hsva()
|
||||
}
|
||||
|
||||
pub fn to_hsla(&self) -> Hsla {
|
||||
self.to_hsva().to_hsla()
|
||||
}
|
||||
|
||||
pub fn to_vec(&self) -> Vec4 {
|
||||
Vec4::new(
|
||||
self.red,
|
||||
self.green,
|
||||
self.blue,
|
||||
self.alpha
|
||||
)
|
||||
}
|
||||
}
|
||||
109
crates/comet_colors/src/xyza.rs
Normal file
109
crates/comet_colors/src/xyza.rs
Normal file
|
|
@ -0,0 +1,109 @@
|
|||
use crate::{sRgba, Hsla, Hsva, Hwba, Laba, Lcha, LinearRgba, Oklaba, Oklcha};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct Xyza {
|
||||
x: f32,
|
||||
y: f32,
|
||||
z: f32,
|
||||
alpha: f32
|
||||
}
|
||||
|
||||
impl Xyza {
|
||||
pub fn new(x: f32, y: f32, z: f32, alpha: f32) -> Self {
|
||||
assert!((0.0..=1.0).contains(&x) && (0.0..=1.0).contains(&y) && (0.0..=1.5).contains(&z) && (0.0..=1.0).contains(&alpha), "X needs to be in range 0..1\nY needs to be in range 0..1\nZ needs to be in range 0..1\nAlpha needs to be in range 0..1");
|
||||
Self {
|
||||
x,
|
||||
y,
|
||||
z,
|
||||
alpha
|
||||
}
|
||||
}
|
||||
|
||||
pub fn x(&self) -> f32 {
|
||||
self.x
|
||||
}
|
||||
|
||||
pub fn y(&self) -> f32 {
|
||||
self.y
|
||||
}
|
||||
|
||||
pub fn z(&self) -> f32 {
|
||||
self.z
|
||||
}
|
||||
|
||||
pub fn alpha(&self) -> f32 {
|
||||
self.alpha
|
||||
}
|
||||
|
||||
pub fn from_linear(linear: LinearRgba) -> Self {
|
||||
Self {
|
||||
x: 0.4124564 * linear.red() + 0.3575761 * linear.green() + 0.1804375 * linear.blue(),
|
||||
y: 0.2126729 * linear.red() + 0.7151522 * linear.green() + 0.0721750 * linear.blue(),
|
||||
z: 0.0193339 * linear.red() + 0.1191920 * linear.green() + 0.9503041 * linear.blue(),
|
||||
alpha: linear.alpha()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_linear(&self) -> LinearRgba {
|
||||
LinearRgba::new(
|
||||
3.2404542 * self.x + -1.5371385 * self.y + -0.4985314 * self.z,
|
||||
-0.9692660 * self.x + 1.8760108 * self.y + 0.0415560 * self.z,
|
||||
0.0556434 * self.x + -0.2040259 * self.y + 1.0572252 * self.z,
|
||||
self.alpha
|
||||
)
|
||||
}
|
||||
|
||||
pub fn to_laba(&self) -> Laba {
|
||||
let reference_white = Xyza::new(0.95047, 1.0, 1.08883, 1.0);
|
||||
|
||||
let x_r = self.x / reference_white.x;
|
||||
let y_r = self.y / reference_white.y;
|
||||
let z_r = self.z / reference_white.z;
|
||||
|
||||
let epsilon: f32 = 0.008856;
|
||||
let kappa: f32 = 903.3;
|
||||
|
||||
let f_x = if x_r > epsilon { x_r.cbrt() } else { ( kappa*x_r + 16.0 ) / 116.0 };
|
||||
let f_y = if x_r > epsilon { y_r.cbrt() } else { ( kappa*y_r + 16.0 ) / 116.0 };
|
||||
let f_z = if x_r > epsilon { z_r.cbrt() } else { ( kappa*z_r + 16.0 ) / 116.0 };
|
||||
|
||||
Laba::new(
|
||||
1.16*f_y-0.16,
|
||||
5.0*( f_x - f_y ),
|
||||
2.0*( f_y - f_z ),
|
||||
self.alpha
|
||||
)
|
||||
}
|
||||
|
||||
pub fn to_lcha(&self) -> Lcha {
|
||||
self.to_laba().to_lcha()
|
||||
}
|
||||
|
||||
pub fn to_oklaba(&self) -> Oklaba {
|
||||
self.to_linear().to_oklaba()
|
||||
}
|
||||
|
||||
pub fn to_oklcha(&self) -> Oklcha {
|
||||
self.to_oklaba().to_oklcha()
|
||||
}
|
||||
|
||||
pub fn to_rgba(&self) -> sRgba<f32> {
|
||||
self.to_linear().to_rgba()
|
||||
}
|
||||
|
||||
pub fn to_rgba8(&self) -> sRgba<u8> {
|
||||
self.to_linear().to_rgba8()
|
||||
}
|
||||
|
||||
pub fn to_hwba(&self) -> Hwba {
|
||||
self.to_rgba().to_hwba()
|
||||
}
|
||||
|
||||
pub fn to_hsva(&self) -> Hsva {
|
||||
self.to_hwba().to_hsva()
|
||||
}
|
||||
|
||||
pub fn to_hsla(&self) -> Hsla {
|
||||
self.to_hsva().to_hsla()
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue