initial commit

This commit is contained in:
lisk77 2024-10-26 02:15:26 +02:00
commit 6154c72b0e
55 changed files with 9481 additions and 0 deletions

View 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" }

View 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()
}
}

View 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()
}
}

View 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()
}
}

View 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()
}
}

View 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()
}
}

View 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;

View 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
}
}
}

View 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()
}
}

View 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()
}
}

View 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
)
}
}

View 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()
}
}