From ea4f7c6b150079c697cab76d1a95c6f6e04ca5c8 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Thu, 19 Oct 2023 21:42:23 -0400 Subject: Implement the Oklab color space --- src/color.rs | 75 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 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 for OklabSpace { + type Output = f64; + + fn index(&self, i: usize) -> &f64 { + &self.0[i] + } +} + +impl From 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; + + fn distance(&self, other: &Self) -> Self::Distance { + euclidean_distance(&self.0, &other.0) + } +} + +impl Metric for OklabSpace {} + +impl ColorSpace for OklabSpace { + fn average>(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::(colors), ColorSpaceArg::Lab => self.paint::(colors), ColorSpaceArg::Luv => self.paint::(colors), + ColorSpaceArg::Oklab => self.paint::(colors), } } -- cgit v1.2.3