summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTavian Barnes <tavianator@tavianator.com>2023-10-19 21:42:23 -0400
committerTavian Barnes <tavianator@tavianator.com>2023-10-19 21:42:23 -0400
commitea4f7c6b150079c697cab76d1a95c6f6e04ca5c8 (patch)
tree2716acc0ff9372dc20787abbe47b2db2fbfc40fc
parent8147262c1cc069d095e207b56d3d8e4ef4386a09 (diff)
downloadkd-forest-ea4f7c6b150079c697cab76d1a95c6f6e04ca5c8.tar.xz
Implement the Oklab color space
-rw-r--r--src/color.rs75
-rw-r--r--src/main.rs8
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),
}
}