diff options
author | Tavian Barnes <tavianator@tavianator.com> | 2023-10-19 21:42:23 -0400 |
---|---|---|
committer | Tavian Barnes <tavianator@tavianator.com> | 2023-10-19 21:42:23 -0400 |
commit | ea4f7c6b150079c697cab76d1a95c6f6e04ca5c8 (patch) | |
tree | 2716acc0ff9372dc20787abbe47b2db2fbfc40fc /src | |
parent | 8147262c1cc069d095e207b56d3d8e4ef4386a09 (diff) | |
download | kd-forest-ea4f7c6b150079c697cab76d1a95c6f6e04ca5c8.tar.xz |
Implement the Oklab color space
Diffstat (limited to 'src')
-rw-r--r-- | src/color.rs | 75 | ||||
-rw-r--r-- | src/main.rs | 8 |
2 files changed, 81 insertions, 2 deletions
diff --git a/src/color.rs b/src/color.rs index 81d9689..90e68ce 100644 --- a/src/color.rs +++ b/src/color.rs @@ -275,3 +275,78 @@ impl ColorSpace for LuvSpace { Self(sum) } } + +/// [Oklab](https://bottosson.github.io/posts/oklab/) space. +#[derive(Clone, Copy, Debug)] +pub struct OklabSpace([f64; 3]); + +impl Index<usize> for OklabSpace { + type Output = f64; + + fn index(&self, i: usize) -> &f64 { + &self.0[i] + } +} + +impl From<Rgb8> for OklabSpace { + fn from(rgb8: Rgb8) -> Self { + let rgb = RgbSpace::from(rgb8); + + let r = srgb_inv_gamma(rgb[0]); + let g = srgb_inv_gamma(rgb[1]); + let b = srgb_inv_gamma(rgb[2]); + + let l = 0.4122214708 * r + 0.5363325363 * g + 0.0514459929 * b; + let m = 0.2119034982 * r + 0.6806995451 * g + 0.1073969566 * b; + let s = 0.0883024619 * r + 0.2817188376 * g + 0.6299787005 * b; + + let l = l.cbrt(); + let m = m.cbrt(); + let s = s.cbrt(); + + Self([ + 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, + ]) + } +} + +impl Coordinates for OklabSpace { + type Value = f64; + + fn dims(&self) -> usize { + self.0.dims() + } + + fn coord(&self, i: usize) -> f64 { + self.0.coord(i) + } +} + +impl Proximity for OklabSpace { + type Distance = EuclideanDistance<f64>; + + fn distance(&self, other: &Self) -> Self::Distance { + euclidean_distance(&self.0, &other.0) + } +} + +impl Metric for OklabSpace {} + +impl ColorSpace for OklabSpace { + fn average<I: IntoIterator<Item = Self>>(colors: I) -> Self { + let mut sum = [0.0, 0.0, 0.0]; + let mut len: usize = 0; + for color in colors.into_iter() { + for i in 0..3 { + sum[i] += color[i]; + } + len += 1; + } + for s in &mut sum { + *s /= len as f64; + } + Self(sum) + } +} diff --git a/src/main.rs b/src/main.rs index c651b23..09e335a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,7 +5,7 @@ pub mod hilbert; pub mod soft; use crate::color::source::{AllColors, ColorSource, ImageColors}; -use crate::color::{order, ColorSpace, LabSpace, LuvSpace, Rgb8, RgbSpace}; +use crate::color::{order, ColorSpace, LabSpace, LuvSpace, OklabSpace, Rgb8, RgbSpace}; use crate::frontier::image::ImageFrontier; use crate::frontier::mean::MeanFrontier; use crate::frontier::min::MinFrontier; @@ -69,6 +69,8 @@ enum ColorSpaceArg { Lab, /// CIE L*u*v* space. Luv, + /// Oklab space. + Oklab, } /// Error type for this app. @@ -182,7 +184,7 @@ impl Args { (@arg MODE: -l --selection +takes_value possible_value[min mean] "Specify the selection mode") (@arg TARGET: -g --target +takes_value "Place colors on the closest pixels of the TARGET image") ) - (@arg SPACE: -c --("color-space") default_value("Lab") possible_value[RGB Lab Luv] "Use the given color space") + (@arg SPACE: -c --("color-space") default_value("Lab") possible_value[RGB Lab Luv Oklab] "Use the given color space") (@arg WIDTH: -w --width +takes_value "The width of the generated image") (@arg HEIGHT: -h --height +takes_value "The height of the generated image") (@arg X: -x +takes_value "The x coordinate of the first pixel") @@ -254,6 +256,7 @@ impl Args { "RGB" => ColorSpaceArg::Rgb, "Lab" => ColorSpaceArg::Lab, "Luv" => ColorSpaceArg::Luv, + "Oklab" => ColorSpaceArg::Oklab, _ => unreachable!(), }; @@ -334,6 +337,7 @@ impl App { ColorSpaceArg::Rgb => self.paint::<RgbSpace>(colors), ColorSpaceArg::Lab => self.paint::<LabSpace>(colors), ColorSpaceArg::Luv => self.paint::<LuvSpace>(colors), + ColorSpaceArg::Oklab => self.paint::<OklabSpace>(colors), } } |