diff --git a/Cargo.lock b/Cargo.lock index 9c24aae..660f91a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -163,7 +163,7 @@ checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" [[package]] name = "colorutils-rs" -version = "0.4.13" +version = "0.4.14" dependencies = [ "erydanos", "half", @@ -223,9 +223,9 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "erydanos" -version = "0.2.3" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a140744bdb5b8777d9714a8d6a72c5e58d4eb2b0c3c8a85c8bada86efd9fa21" +checksum = "785ae30e7d15b2b2f145686b6ec50aa64e44adc8a09b463175be687211dab7e7" dependencies = [ "num-traits", ] diff --git a/Cargo.toml b/Cargo.toml index d0d17d8..520638b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,7 +2,7 @@ workspace = { members = ["src/app"] } [package] name = "colorutils-rs" -version = "0.4.13" +version = "0.4.14" edition = "2021" description = "High performance utilities for color format handling and conversion." readme = "README.md" @@ -16,7 +16,7 @@ repository = "https://github.com/awxkee/colorutils-rs" exclude = ["*.jpg"] [dependencies] -erydanos = "0.2.3" +erydanos = "0.2.6" half = "2.4.1" [features] diff --git a/src/app/src/main.rs b/src/app/src/main.rs index 5992c39..886f508 100644 --- a/src/app/src/main.rs +++ b/src/app/src/main.rs @@ -1,3 +1,10 @@ +/* + * // Copyright 2024 (c) the Radzivon Bartoshyk. All rights reserved. + * // + * // Use of this source code is governed by a BSD-style + * // license that can be found in the LICENSE file. + */ + use std::time::Instant; use image::io::Reader as ImageReader; @@ -14,13 +21,21 @@ pub const fn shuffle(z: u32, y: u32, x: u32, w: u32) -> i32 { } fn main() { - let r = 140; - let g = 164; - let b = 177; + let r = 126; + let g = 126; + let b = 126; let rgb = Rgb::::new(r, g, b); - let hsl = rgb.to_hsl(); + let hsl = rgb.to_lab(); println!("RGB {:?}", rgb); - println!("HSL {:?}", hsl); + println!("RGB 0,0,0 {:?}", hsl); + println!( + "RGB 127,127,127 {:?}", + Rgb::::new(127, 127, 127).to_lab() + ); + println!( + "RGB 255,255,255 {:?}", + Rgb::::new(255, 255, 255).to_lab() + ); println!("Back RGB {:?}", hsl.to_rgb8()); let img = ImageReader::open("./assets/beach_horizon.jpg") @@ -58,7 +73,7 @@ fn main() { lab_store.resize(width as usize * components * height as usize, 0f32); let src_stride = width * components as u32; let start_time = Instant::now(); - rgb_to_lch( + rgb_to_lab( src_bytes, src_stride, &mut lab_store, @@ -92,7 +107,7 @@ fn main() { // } let start_time = Instant::now(); - lch_to_rgb( + lab_to_srgb( &lab_store, store_stride as u32, &mut dst_slice, diff --git a/src/avx/cie.rs b/src/avx/cie.rs index 1dd5f7b..7dc7244 100644 --- a/src/avx/cie.rs +++ b/src/avx/cie.rs @@ -1,3 +1,10 @@ +/* + * // Copyright 2024 (c) the Radzivon Bartoshyk. All rights reserved. + * // + * // Use of this source code is governed by a BSD-style + * // license that can be found in the LICENSE file. + */ + use crate::avx::math::*; use crate::avx::{_mm256_cube_ps, _mm256_prefer_fma_ps, _mm256_select_ps}; use crate::luv::{ diff --git a/src/avx/from_sigmoidal.rs b/src/avx/from_sigmoidal.rs index b2a03e6..9d60c06 100644 --- a/src/avx/from_sigmoidal.rs +++ b/src/avx/from_sigmoidal.rs @@ -1,3 +1,10 @@ +/* + * // Copyright 2024 (c) the Radzivon Bartoshyk. All rights reserved. + * // + * // Use of this source code is governed by a BSD-style + * // license that can be found in the LICENSE file. + */ + use crate::avx::sigmoidal::avx_sigmoidal_to_rgb; use crate::avx::{ avx2_deinterleave_rgb_ps, avx2_deinterleave_rgba_ps, avx2_interleave_rgb, diff --git a/src/avx/gamma_curves.rs b/src/avx/gamma_curves.rs index ae96e4e..2402d5c 100644 --- a/src/avx/gamma_curves.rs +++ b/src/avx/gamma_curves.rs @@ -1,3 +1,10 @@ +/* + * // Copyright 2024 (c) the Radzivon Bartoshyk. All rights reserved. + * // + * // Use of this source code is governed by a BSD-style + * // license that can be found in the LICENSE file. + */ + use crate::avx::math::*; #[allow(unused_imports)] use crate::gamma_curves::TransferFunction; diff --git a/src/avx/linear_to_image.rs b/src/avx/linear_to_image.rs index e4f05bd..12bc3ca 100644 --- a/src/avx/linear_to_image.rs +++ b/src/avx/linear_to_image.rs @@ -1,3 +1,10 @@ +/* + * // Copyright 2024 (c) the Radzivon Bartoshyk. All rights reserved. + * // + * // Use of this source code is governed by a BSD-style + * // license that can be found in the LICENSE file. + */ + use crate::avx::gamma_curves::get_avx_gamma_transfer; use crate::avx::{ avx2_deinterleave_rgb_ps, avx2_deinterleave_rgba_ps, avx2_interleave_rgb, diff --git a/src/avx/math.rs b/src/avx/math.rs index 5490e0f..dc1d319 100644 --- a/src/avx/math.rs +++ b/src/avx/math.rs @@ -1,3 +1,10 @@ +/* + * // Copyright 2024 (c) the Radzivon Bartoshyk. All rights reserved. + * // + * // Use of this source code is governed by a BSD-style + * // license that can be found in the LICENSE file. + */ + #[cfg(target_arch = "x86")] use std::arch::x86::*; #[cfg(target_arch = "x86_64")] diff --git a/src/avx/mod.rs b/src/avx/mod.rs index e9a8d89..feed24c 100644 --- a/src/avx/mod.rs +++ b/src/avx/mod.rs @@ -1,5 +1,5 @@ /* - * // Copyright (c) the Radzivon Bartoshyk. All rights reserved. + * // Copyright 2024 (c) the Radzivon Bartoshyk. All rights reserved. * // * // Use of this source code is governed by a BSD-style * // license that can be found in the LICENSE file. diff --git a/src/avx/sigmoidal.rs b/src/avx/sigmoidal.rs index b184a96..8a33a33 100644 --- a/src/avx/sigmoidal.rs +++ b/src/avx/sigmoidal.rs @@ -1,3 +1,10 @@ +/* + * // Copyright 2024 (c) the Radzivon Bartoshyk. All rights reserved. + * // + * // Use of this source code is governed by a BSD-style + * // license that can be found in the LICENSE file. + */ + use crate::avx::{_mm256_exp_ps, _mm256_log_ps, _mm256_neg_ps, _mm256_select_ps}; #[cfg(target_arch = "x86")] use std::arch::x86::*; diff --git a/src/avx/support.rs b/src/avx/support.rs index 296dd45..cebae6f 100644 --- a/src/avx/support.rs +++ b/src/avx/support.rs @@ -1,5 +1,5 @@ /* - * // Copyright (c) the Radzivon Bartoshyk. All rights reserved. + * // Copyright 2024 (c) the Radzivon Bartoshyk. All rights reserved. * // * // Use of this source code is governed by a BSD-style * // license that can be found in the LICENSE file. diff --git a/src/avx/to_linear.rs b/src/avx/to_linear.rs index b0c6821..ff8f0ad 100644 --- a/src/avx/to_linear.rs +++ b/src/avx/to_linear.rs @@ -1,3 +1,10 @@ +/* + * // Copyright 2024 (c) the Radzivon Bartoshyk. All rights reserved. + * // + * // Use of this source code is governed by a BSD-style + * // license that can be found in the LICENSE file. + */ + use crate::avx::gamma_curves::get_avx2_linear_transfer; use crate::avx::{ avx2_deinterleave_rgb_epi8, avx2_deinterleave_rgba_epi8, avx2_interleave_rgb_ps, diff --git a/src/avx/to_sigmoidal.rs b/src/avx/to_sigmoidal.rs index a146334..1dabe20 100644 --- a/src/avx/to_sigmoidal.rs +++ b/src/avx/to_sigmoidal.rs @@ -1,3 +1,10 @@ +/* + * // Copyright 2024 (c) the Radzivon Bartoshyk. All rights reserved. + * // + * // Use of this source code is governed by a BSD-style + * // license that can be found in the LICENSE file. + */ + use crate::avx::sigmoidal::avx_rgb_to_sigmoidal; use crate::avx::{ avx2_deinterleave_rgb_epi8, avx2_deinterleave_rgba_epi8, avx2_interleave_rgb_ps, diff --git a/src/avx/to_xyz_lab.rs b/src/avx/to_xyz_lab.rs index f29abc6..d2d8ab8 100644 --- a/src/avx/to_xyz_lab.rs +++ b/src/avx/to_xyz_lab.rs @@ -1,3 +1,10 @@ +/* + * // Copyright 2024 (c) the Radzivon Bartoshyk. All rights reserved. + * // + * // Use of this source code is governed by a BSD-style + * // license that can be found in the LICENSE file. + */ + #[cfg(target_arch = "x86")] use std::arch::x86::*; #[cfg(target_arch = "x86_64")] diff --git a/src/avx/utils.rs b/src/avx/utils.rs index 80d9b59..ff42d69 100644 --- a/src/avx/utils.rs +++ b/src/avx/utils.rs @@ -1,3 +1,10 @@ +/* + * // Copyright 2024 (c) the Radzivon Bartoshyk. All rights reserved. + * // + * // Use of this source code is governed by a BSD-style + * // license that can be found in the LICENSE file. + */ + #[cfg(target_arch = "x86")] use std::arch::x86::*; #[cfg(target_arch = "x86_64")] diff --git a/src/avx/xyz_lab_to_image.rs b/src/avx/xyz_lab_to_image.rs index 8d442fa..e758e09 100644 --- a/src/avx/xyz_lab_to_image.rs +++ b/src/avx/xyz_lab_to_image.rs @@ -1,3 +1,10 @@ +/* + * // Copyright 2024 (c) the Radzivon Bartoshyk. All rights reserved. + * // + * // Use of this source code is governed by a BSD-style + * // license that can be found in the LICENSE file. + */ + use crate::avx::cie::{avx_lab_to_xyz, avx_lch_to_xyz, avx_luv_to_xyz}; use crate::avx::gamma_curves::get_avx_gamma_transfer; use crate::avx::{ diff --git a/src/avx/xyza_laba_to_image.rs b/src/avx/xyza_laba_to_image.rs index 6f3b8db..30aa248 100644 --- a/src/avx/xyza_laba_to_image.rs +++ b/src/avx/xyza_laba_to_image.rs @@ -1,3 +1,10 @@ +/* + * // Copyright 2024 (c) the Radzivon Bartoshyk. All rights reserved. + * // + * // Use of this source code is governed by a BSD-style + * // license that can be found in the LICENSE file. + */ + #[cfg(target_arch = "x86")] use std::arch::x86::*; #[cfg(target_arch = "x86_64")] diff --git a/src/concat_alpha.rs b/src/concat_alpha.rs index e4d034a..09d4219 100644 --- a/src/concat_alpha.rs +++ b/src/concat_alpha.rs @@ -1,3 +1,10 @@ +/* + * // Copyright 2024 (c) the Radzivon Bartoshyk. All rights reserved. + * // + * // Use of this source code is governed by a BSD-style + * // license that can be found in the LICENSE file. + */ + #[cfg(all( any(target_arch = "x86_64", target_arch = "x86"), target_feature = "avx2" diff --git a/src/euclidean.rs b/src/euclidean.rs new file mode 100644 index 0000000..663e770 --- /dev/null +++ b/src/euclidean.rs @@ -0,0 +1,11 @@ +/* + * // Copyright 2024 (c) the Radzivon Bartoshyk. All rights reserved. + * // + * // Use of this source code is governed by a BSD-style + * // license that can be found in the LICENSE file. + */ + +/// Trait that implements Euclidean distance for color +pub trait EuclideanDistance { + fn euclidean_distance(&self, other: Self) -> f32; +} diff --git a/src/gamma_curves.rs b/src/gamma_curves.rs index e61171b..eb2d475 100644 --- a/src/gamma_curves.rs +++ b/src/gamma_curves.rs @@ -1,4 +1,11 @@ -#[inline(always)] +/* + * // Copyright 2024 (c) the Radzivon Bartoshyk. All rights reserved. + * // + * // Use of this source code is governed by a BSD-style + * // license that can be found in the LICENSE file. + */ + +#[inline] /// Linear transfer function for sRGB pub fn srgb_to_linear(gamma: f32) -> f32 { return if gamma < 0f32 { @@ -12,7 +19,7 @@ pub fn srgb_to_linear(gamma: f32) -> f32 { }; } -#[inline(always)] +#[inline] /// Gamma transfer function for sRGB pub fn srgb_from_linear(linear: f32) -> f32 { return if linear < 0.0f32 { @@ -26,7 +33,7 @@ pub fn srgb_from_linear(linear: f32) -> f32 { }; } -#[inline(always)] +#[inline] /// Linear transfer function for Rec.709 pub fn rec709_to_linear(gamma: f32) -> f32 { return if gamma < 0.0f32 { @@ -40,7 +47,7 @@ pub fn rec709_to_linear(gamma: f32) -> f32 { }; } -#[inline(always)] +#[inline] /// Gamma transfer function for Rec.709 pub fn rec709_from_linear(linear: f32) -> f32 { return if linear < 0.0f32 { @@ -66,25 +73,25 @@ pub fn pure_gamma_function(x: f32, gamma: f32) -> f32 { } } -#[inline(always)] +#[inline] /// Pure gamma transfer function for gamma 2.2 pub fn gamma2p2_from_linear(linear: f32) -> f32 { pure_gamma_function(linear, 1f32 / 2.2f32) } -#[inline(always)] +#[inline] /// Linear transfer function for gamma 2.2 pub fn gamma2p2_to_linear(gamma: f32) -> f32 { pure_gamma_function(gamma, 2.2f32) } -#[inline(always)] +#[inline] /// Pure gamma transfer function for gamma 2.8 pub fn gamma2p8_from_linear(linear: f32) -> f32 { pure_gamma_function(linear, 1f32 / 2.8f32) } -#[inline(always)] +#[inline] /// Linear transfer function for gamma 2.8 pub fn gamma2p8_to_linear(gamma: f32) -> f32 { pure_gamma_function(gamma, 2.8f32) @@ -104,7 +111,7 @@ pub enum TransferFunction { } impl TransferFunction { - #[inline(always)] + #[inline] pub fn get_linearize_function(&self) -> fn(f32) -> f32 { match self { TransferFunction::Srgb => srgb_to_linear, @@ -114,7 +121,7 @@ impl TransferFunction { } } - #[inline(always)] + #[inline] pub fn get_gamma_function(&self) -> fn(f32) -> f32 { match self { TransferFunction::Srgb => srgb_from_linear, diff --git a/src/hsl.rs b/src/hsl.rs index d831822..9238f04 100644 --- a/src/hsl.rs +++ b/src/hsl.rs @@ -1,3 +1,9 @@ +/* + * // Copyright 2024 (c) the Radzivon Bartoshyk. All rights reserved. + * // + * // Use of this source code is governed by a BSD-style + * // license that can be found in the LICENSE file. + */ use crate::rgb::Rgb; #[derive(Debug, Copy, Clone, PartialOrd, PartialEq)] diff --git a/src/hsv.rs b/src/hsv.rs index c10a948..31a4e3c 100644 --- a/src/hsv.rs +++ b/src/hsv.rs @@ -1,3 +1,9 @@ +/* + * // Copyright 2024 (c) the Radzivon Bartoshyk. All rights reserved. + * // + * // Use of this source code is governed by a BSD-style + * // license that can be found in the LICENSE file. + */ use crate::rgb::Rgb; #[derive(Debug, Copy, Clone, PartialOrd, PartialEq)] @@ -22,11 +28,11 @@ impl Hsv { v: l as f32 * HSV_PERCENTAGE_SCALE, } } - + #[inline] pub fn from_components(h: f32, s: f32, v: f32) -> Hsv { Hsv { h, s, v } } - + #[inline] pub fn from(rgb: &Rgb) -> Hsv { let (h, s, v) = rgb_to_hsv( rgb.r as f32 * HSV_U8_SCALE, @@ -35,7 +41,7 @@ impl Hsv { ); return Hsv { h, s, v }; } - + #[inline] pub fn to_rgb8(&self) -> Rgb { let (rf, gf, bf) = hsv_to_rgb(self.h, self.s, self.v); return Rgb { @@ -44,32 +50,32 @@ impl Hsv { b: (bf * 255f32) as u8, }; } - + #[inline] pub fn get_hue(&self) -> f32 { self.h } - + #[inline] pub fn get_saturation(&self) -> f32 { self.s } - + #[inline] pub fn get_value(&self) -> f32 { self.v } - + #[inline] pub fn get_hue_p(&self) -> u16 { self.h.max(0f32).min(360f32) as u16 } - + #[inline] pub fn get_saturation_p(&self) -> u16 { (self.s * 100f32).max(0f32).min(100f32) as u16 } - + #[inline] pub fn get_value_p(&self) -> u16 { (self.v * 100f32).max(0f32).min(100f32) as u16 } } - +#[inline] fn rgb_to_hsv(r: f32, g: f32, b: f32) -> (f32, f32, f32) { let c_max = r.max(g).max(b); let c_min = r.min(g).min(b); @@ -100,6 +106,7 @@ fn rgb_to_hsv(r: f32, g: f32, b: f32) -> (f32, f32, f32) { (h, s, v) } +#[inline] fn hsv_to_rgb(h: f32, s: f32, v: f32) -> (f32, f32, f32) { let c = v * s; let h_prime = (h / 60f32) % 6f32; diff --git a/src/hsv_to_image.rs b/src/hsv_to_image.rs index 853c162..486d853 100644 --- a/src/hsv_to_image.rs +++ b/src/hsv_to_image.rs @@ -1,3 +1,10 @@ +/* + * // Copyright 2024 (c) the Radzivon Bartoshyk. All rights reserved. + * // + * // Use of this source code is governed by a BSD-style + * // license that can be found in the LICENSE file. + */ + use std::slice; use crate::image::ImageConfiguration; diff --git a/src/image.rs b/src/image.rs index c24d20c..d8e29cc 100644 --- a/src/image.rs +++ b/src/image.rs @@ -1,3 +1,10 @@ +/* + * // Copyright 2024 (c) the Radzivon Bartoshyk. All rights reserved. + * // + * // Use of this source code is governed by a BSD-style + * // license that can be found in the LICENSE file. + */ + #[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq)] pub enum ImageConfiguration { Rgb = 0, diff --git a/src/image_to_hsv.rs b/src/image_to_hsv.rs index fcc57a9..6cc518d 100644 --- a/src/image_to_hsv.rs +++ b/src/image_to_hsv.rs @@ -1,3 +1,10 @@ +/* + * // Copyright 2024 (c) the Radzivon Bartoshyk. All rights reserved. + * // + * // Use of this source code is governed by a BSD-style + * // license that can be found in the LICENSE file. + */ + use crate::image::ImageConfiguration; use crate::image_to_hsv_support::HsvTarget; #[cfg(all( diff --git a/src/image_to_hsv_support.rs b/src/image_to_hsv_support.rs index a6decd3..d878578 100644 --- a/src/image_to_hsv_support.rs +++ b/src/image_to_hsv_support.rs @@ -1,3 +1,10 @@ +/* + * // Copyright 2024 (c) the Radzivon Bartoshyk. All rights reserved. + * // + * // Use of this source code is governed by a BSD-style + * // license that can be found in the LICENSE file. + */ + #[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq)] pub enum HsvTarget { HSV, diff --git a/src/image_to_linear.rs b/src/image_to_linear.rs index 5b3d333..f3adc6b 100644 --- a/src/image_to_linear.rs +++ b/src/image_to_linear.rs @@ -1,3 +1,10 @@ +/* + * // Copyright 2024 (c) the Radzivon Bartoshyk. All rights reserved. + * // + * // Use of this source code is governed by a BSD-style + * // license that can be found in the LICENSE file. + */ + #[cfg(all( any(target_arch = "x86_64", target_arch = "x86"), target_feature = "avx2" diff --git a/src/image_to_linear_u8.rs b/src/image_to_linear_u8.rs index 9d16b35..91c40ba 100644 --- a/src/image_to_linear_u8.rs +++ b/src/image_to_linear_u8.rs @@ -1,3 +1,10 @@ +/* + * // Copyright 2024 (c) the Radzivon Bartoshyk. All rights reserved. + * // + * // Use of this source code is governed by a BSD-style + * // license that can be found in the LICENSE file. + */ + use std::slice; use crate::gamma_curves::TransferFunction; diff --git a/src/image_to_sigmoidal.rs b/src/image_to_sigmoidal.rs index 303196b..a115d5b 100644 --- a/src/image_to_sigmoidal.rs +++ b/src/image_to_sigmoidal.rs @@ -1,3 +1,10 @@ +/* + * // Copyright 2024 (c) the Radzivon Bartoshyk. All rights reserved. + * // + * // Use of this source code is governed by a BSD-style + * // license that can be found in the LICENSE file. + */ + #[cfg(all( any(target_arch = "x86_64", target_arch = "x86"), target_feature = "avx2" diff --git a/src/image_to_xyz_lab.rs b/src/image_to_xyz_lab.rs index 510600d..f478280 100644 --- a/src/image_to_xyz_lab.rs +++ b/src/image_to_xyz_lab.rs @@ -1,3 +1,10 @@ +/* + * // Copyright 2024 (c) the Radzivon Bartoshyk. All rights reserved. + * // + * // Use of this source code is governed by a BSD-style + * // license that can be found in the LICENSE file. + */ + #[cfg(all( any(target_arch = "x86_64", target_arch = "x86"), target_feature = "avx2" diff --git a/src/image_xyza_laba.rs b/src/image_xyza_laba.rs index 60b08d7..45a9978 100644 --- a/src/image_xyza_laba.rs +++ b/src/image_xyza_laba.rs @@ -1,3 +1,10 @@ +/* + * // Copyright 2024 (c) the Radzivon Bartoshyk. All rights reserved. + * // + * // Use of this source code is governed by a BSD-style + * // license that can be found in the LICENSE file. + */ + use crate::image::ImageConfiguration; #[cfg(all( any(target_arch = "aarch64", target_arch = "arm"), diff --git a/src/lab.rs b/src/lab.rs index f1dcf99..d235bae 100644 --- a/src/lab.rs +++ b/src/lab.rs @@ -1,5 +1,15 @@ +/* + * // Copyright 2024 (c) the Radzivon Bartoshyk. All rights reserved. + * // + * // Use of this source code is governed by a BSD-style + * // license that can be found in the LICENSE file. + */ +use erydanos::Euclidean3DDistance; + use crate::rgb::Rgb; +use crate::taxicab::TaxicabDistance; use crate::xyz::Xyz; +use crate::EuclideanDistance; /// Represents CIELAB color space. #[derive(Copy, Clone, Debug, Default, PartialOrd, PartialEq)] @@ -87,3 +97,15 @@ impl Lab { self.to_rgb8() } } + +impl EuclideanDistance for Lab { + fn euclidean_distance(&self, other: Lab) -> f32 { + (self.l - other.l).hypot3(self.a - other.a, self.b - other.b) + } +} + +impl TaxicabDistance for Lab { + fn taxicab_distance(&self, other: Self) -> f32 { + (self.a - other.a).hypot(self.b - other.b) + (self.l - other.l).abs() + } +} diff --git a/src/lib.rs b/src/lib.rs index 7cba515..a2c2154 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,9 +1,17 @@ +/* + * // Copyright 2024 (c) the Radzivon Bartoshyk. All rights reserved. + * // + * // Use of this source code is governed by a BSD-style + * // license that can be found in the LICENSE file. + */ + #[cfg(all( any(target_arch = "x86_64", target_arch = "x86"), target_feature = "avx2" ))] mod avx; mod concat_alpha; +mod euclidean; mod gamma_curves; mod hsl; mod hsv; @@ -37,6 +45,7 @@ mod sigmoidal_to_image; target_feature = "sse4.1" ))] mod sse; +mod taxicab; mod xyz; mod xyz_lab_to_image; mod xyz_target; @@ -108,6 +117,7 @@ pub use xyza_laba_to_image::luv_with_alpha_to_rgba; pub use xyza_laba_to_image::xyz_with_alpha_to_bgra; pub use xyza_laba_to_image::xyz_with_alpha_to_rgba; +pub use euclidean::EuclideanDistance; pub use image_to_sigmoidal::bgra_to_sigmoidal; pub use image_to_sigmoidal::rgb_to_sigmoidal; pub use image_to_sigmoidal::rgba_to_sigmoidal; @@ -116,3 +126,4 @@ pub use sigmoidal::Sigmoidal; pub use sigmoidal_to_image::sigmoidal_to_bgra; pub use sigmoidal_to_image::sigmoidal_to_rgb; pub use sigmoidal_to_image::sigmoidal_to_rgba; +pub use taxicab::TaxicabDistance; diff --git a/src/linear_to_image.rs b/src/linear_to_image.rs index 59166c7..46dc7e0 100644 --- a/src/linear_to_image.rs +++ b/src/linear_to_image.rs @@ -1,3 +1,10 @@ +/* + * // Copyright 2024 (c) the Radzivon Bartoshyk. All rights reserved. + * // + * // Use of this source code is governed by a BSD-style + * // license that can be found in the LICENSE file. + */ + #[cfg(all( any(target_arch = "x86_64", target_arch = "x86"), target_feature = "avx2" diff --git a/src/linear_to_image_u8.rs b/src/linear_to_image_u8.rs index fb8098e..9d8178a 100644 --- a/src/linear_to_image_u8.rs +++ b/src/linear_to_image_u8.rs @@ -1,3 +1,10 @@ +/* + * // Copyright 2024 (c) the Radzivon Bartoshyk. All rights reserved. + * // + * // Use of this source code is governed by a BSD-style + * // license that can be found in the LICENSE file. + */ + use std::slice; use crate::gamma_curves::TransferFunction; diff --git a/src/linear_to_planar.rs b/src/linear_to_planar.rs index 2b5a9c8..188bce1 100644 --- a/src/linear_to_planar.rs +++ b/src/linear_to_planar.rs @@ -1,3 +1,10 @@ +/* + * // Copyright 2024 (c) the Radzivon Bartoshyk. All rights reserved. + * // + * // Use of this source code is governed by a BSD-style + * // license that can be found in the LICENSE file. + */ + #[cfg(all( any(target_arch = "aarch64", target_arch = "arm"), target_feature = "neon" diff --git a/src/luv.rs b/src/luv.rs index f89e093..69cc2fc 100644 --- a/src/luv.rs +++ b/src/luv.rs @@ -1,3 +1,10 @@ +/* + * // Copyright 2024 (c) the Radzivon Bartoshyk. All rights reserved. + * // + * // Use of this source code is governed by a BSD-style + * // license that can be found in the LICENSE file. + */ + //! # Luv /// Struct representing a color in CIE LUV, a.k.a. L\*u\*v\*, color space #[derive(Debug, Copy, Clone, Default, PartialOrd)] @@ -49,6 +56,8 @@ const D65_XYZ: [f32; 3] = [95.047f32, 100.0f32, 108.883f32]; use crate::rgb::Rgb; use crate::rgba::Rgba; use crate::xyz::Xyz; +use crate::EuclideanDistance; +use erydanos::Euclidean3DDistance; pub(crate) const LUV_WHITE_U_PRIME: f32 = 4.0f32 * D65_XYZ[1] / (D65_XYZ[0] + 15.0 * D65_XYZ[1] + 3.0 * D65_XYZ[2]); @@ -184,3 +193,15 @@ impl PartialEq for LCh { } } } + +impl EuclideanDistance for Luv { + fn euclidean_distance(&self, other: Luv) -> f32 { + (self.l - other.l).hypot3(self.u - other.u, self.v - other.v) + } +} + +impl EuclideanDistance for LCh { + fn euclidean_distance(&self, other: LCh) -> f32 { + (self.l - other.l).hypot3(self.c - other.c, self.h - other.h) + } +} diff --git a/src/neon/cie.rs b/src/neon/cie.rs index 32d5fe1..e176b87 100644 --- a/src/neon/cie.rs +++ b/src/neon/cie.rs @@ -1,10 +1,17 @@ +/* + * // Copyright 2024 (c) the Radzivon Bartoshyk. All rights reserved. + * // + * // Use of this source code is governed by a BSD-style + * // license that can be found in the LICENSE file. + */ + use crate::luv::{ LUV_CUTOFF_FORWARD_Y, LUV_MULTIPLIER_FORWARD_Y, LUV_MULTIPLIER_INVERSE_Y, LUV_WHITE_U_PRIME, LUV_WHITE_V_PRIME, }; use crate::neon::math::{prefer_vfmaq_f32, vcolorq_matrix_f32, vcubeq_f32}; +use erydanos::{vatan2q_f32, vcbrtq_fast_f32, vcosq_f32, vhypotq_fast_f32, vsinq_f32}; use std::arch::aarch64::*; -use erydanos::{vatan2q_f32, vcbrtq_f32, vcosq_f32, vhypotq_fast_f32, vsinq_f32}; #[inline(always)] pub(crate) unsafe fn neon_triple_to_xyz( @@ -49,7 +56,7 @@ pub(crate) unsafe fn neon_triple_to_luv( ); let nan_mask = vceqzq_f32(den); let l_low_mask = vcltq_f32(y, vdupq_n_f32(LUV_CUTOFF_FORWARD_Y)); - let y_cbrt = vcbrtq_f32(y); + let y_cbrt = vcbrtq_fast_f32(y); let l = vbslq_f32( l_low_mask, vmulq_n_f32(y, LUV_MULTIPLIER_FORWARD_Y), @@ -73,9 +80,9 @@ pub(crate) unsafe fn neon_triple_to_lab( ) -> (float32x4_t, float32x4_t, float32x4_t) { let x = vmulq_n_f32(x, 100f32 / 95.047f32); let z = vmulq_n_f32(z, 100f32 / 108.883f32); - let cbrt_x = vcbrtq_f32(x); - let cbrt_y = vcbrtq_f32(y); - let cbrt_z = vcbrtq_f32(z); + let cbrt_x = vcbrtq_fast_f32(x); + let cbrt_y = vcbrtq_fast_f32(y); + let cbrt_z = vcbrtq_fast_f32(z); let s_1 = vdupq_n_f32(16f32 / 116f32); let s_2 = vdupq_n_f32(7.787f32); let lower_x = prefer_vfmaq_f32(s_1, s_2, x); diff --git a/src/neon/colors.rs b/src/neon/colors.rs index 19c3a04..991f1bd 100644 --- a/src/neon/colors.rs +++ b/src/neon/colors.rs @@ -1,5 +1,13 @@ -use crate::neon::math::{prefer_vfmaq_f32, vfmodq_f32}; +/* + * // Copyright 2024 (c) the Radzivon Bartoshyk. All rights reserved. + * // + * // Use of this source code is governed by a BSD-style + * // license that can be found in the LICENSE file. + */ + +use crate::neon::math::{prefer_vfmaq_f32}; use std::arch::aarch64::*; +use erydanos::vfmodq_f32; #[inline(always)] pub unsafe fn neon_hsl_to_rgb( diff --git a/src/neon/from_sigmoidal.rs b/src/neon/from_sigmoidal.rs index cd375c1..8c38123 100644 --- a/src/neon/from_sigmoidal.rs +++ b/src/neon/from_sigmoidal.rs @@ -1,3 +1,10 @@ +/* + * // Copyright 2024 (c) the Radzivon Bartoshyk. All rights reserved. + * // + * // Use of this source code is governed by a BSD-style + * // license that can be found in the LICENSE file. + */ + use crate::image::ImageConfiguration; use crate::neon::sigmoidal::neon_sigmoidal_to_rgb; use std::arch::aarch64::*; diff --git a/src/neon/gamma_curves.rs b/src/neon/gamma_curves.rs index 1b02668..50b22b3 100644 --- a/src/neon/gamma_curves.rs +++ b/src/neon/gamma_curves.rs @@ -1,3 +1,10 @@ +/* + * // Copyright 2024 (c) the Radzivon Bartoshyk. All rights reserved. + * // + * // Use of this source code is governed by a BSD-style + * // license that can be found in the LICENSE file. + */ + use crate::gamma_curves::TransferFunction; use crate::neon::math::vpowq_n_f32; use std::arch::aarch64::*; diff --git a/src/neon/hsv_to_image.rs b/src/neon/hsv_to_image.rs index 1a38af4..cf456da 100644 --- a/src/neon/hsv_to_image.rs +++ b/src/neon/hsv_to_image.rs @@ -1,3 +1,10 @@ +/* + * // Copyright 2024 (c) the Radzivon Bartoshyk. All rights reserved. + * // + * // Use of this source code is governed by a BSD-style + * // license that can be found in the LICENSE file. + */ + use std::arch::aarch64::*; use crate::image::ImageConfiguration; diff --git a/src/neon/image_to_hsv.rs b/src/neon/image_to_hsv.rs index 958eb9a..6a36bcb 100644 --- a/src/neon/image_to_hsv.rs +++ b/src/neon/image_to_hsv.rs @@ -1,3 +1,10 @@ +/* + * // Copyright 2024 (c) the Radzivon Bartoshyk. All rights reserved. + * // + * // Use of this source code is governed by a BSD-style + * // license that can be found in the LICENSE file. + */ + use crate::image::ImageConfiguration; use crate::image_to_hsv_support::HsvTarget; use crate::neon::{neon_rgb_to_hsl, neon_rgb_to_hsv}; diff --git a/src/neon/linear_to_image.rs b/src/neon/linear_to_image.rs index 156bb38..0656e60 100644 --- a/src/neon/linear_to_image.rs +++ b/src/neon/linear_to_image.rs @@ -1,3 +1,10 @@ +/* + * // Copyright 2024 (c) the Radzivon Bartoshyk. All rights reserved. + * // + * // Use of this source code is governed by a BSD-style + * // license that can be found in the LICENSE file. + */ + use crate::image::ImageConfiguration; use crate::neon::*; use crate::TransferFunction; diff --git a/src/neon/linear_to_planar.rs b/src/neon/linear_to_planar.rs index d882038..445614a 100644 --- a/src/neon/linear_to_planar.rs +++ b/src/neon/linear_to_planar.rs @@ -1,3 +1,10 @@ +/* + * // Copyright 2024 (c) the Radzivon Bartoshyk. All rights reserved. + * // + * // Use of this source code is governed by a BSD-style + * // license that can be found in the LICENSE file. + */ + use std::arch::aarch64::*; use crate::neon::get_neon_gamma_transfer; diff --git a/src/neon/math.rs b/src/neon/math.rs index b1a7ad4..88a9621 100644 --- a/src/neon/math.rs +++ b/src/neon/math.rs @@ -1,17 +1,12 @@ -use std::arch::aarch64::*; -use erydanos::vpowq_fast_f32; +/* + * // Copyright 2024 (c) the Radzivon Bartoshyk. All rights reserved. + * // + * // Use of this source code is governed by a BSD-style + * // license that can be found in the LICENSE file. + */ -#[inline(always)] -#[allow(dead_code)] -pub(crate) unsafe fn vfmodq_f32(a: float32x4_t, b: float32x4_t) -> float32x4_t { - let dividend_vec = a; - let divisor_vec = b; - let division = vmulq_f32(dividend_vec, vrecpeq_f32(divisor_vec)); - let int_part = vcvtq_f32_s32(vcvtq_s32_f32(division)); - let product = vmulq_f32(int_part, divisor_vec); - let remainder = vsubq_f32(dividend_vec, product); - remainder -} +use erydanos::vpowq_fast_f32; +use std::arch::aarch64::*; #[inline(always)] #[allow(dead_code)] @@ -53,254 +48,6 @@ unsafe fn vtaylor_polyq_f32( return res; } -#[inline(always)] -#[allow(dead_code)] -pub unsafe fn vrintq_s32(d: float32x4_t) -> int32x4_t { - return vcvtq_s32_f32(vaddq_f32( - d, - vreinterpretq_f32_u32(vorrq_u32( - vandq_u32( - vreinterpretq_u32_f32(d), - vreinterpretq_u32_f32(vdupq_n_f32(-0.0f32)), - ), - vreinterpretq_u32_f32(vdupq_n_f32(0.5f32)), - )), - )); -} - -#[inline(always)] -#[allow(dead_code)] -pub unsafe fn vfloorq_f32(x: float32x4_t) -> float32x4_t { - let const_1 = vdupq_n_f32(1f32); - - let z = vcvtq_s32_f32(x); - let r = vcvtq_f32_s32(z); - - return vbslq_f32(vcgtq_f32(r, x), vsubq_f32(r, const_1), r); -} - -#[inline(always)] -pub unsafe fn vexpq_f32(x: float32x4_t) -> float32x4_t { - vexpq_f32_ulp1_5::(x) -} - -#[inline(always)] -#[allow(dead_code)] -/// Low precision exp(x), ULP ~5 -unsafe fn vexpq_f32_impl_ulp5(x: float32x4_t) -> float32x4_t { - let l2e = vdupq_n_f32(std::f32::consts::LOG2_E); /* log2(e) */ - let c0 = vdupq_n_f32(0.3371894346f32); - let c1 = vdupq_n_f32(0.657636276f32); - let c2 = vdupq_n_f32(1.00172476f32); - - /* exp(x) = 2^i * 2^f; i = floor (log2(e) * x), 0 <= f <= 1 */ - let t = vmulq_f32(x, l2e); /* t = log2(e) * x */ - let e = vfloorq_f32(t); /* floor(t) */ - let i = vcvtq_s32_f32(e); /* (int)floor(t) */ - let f = vsubq_f32(t, e); /* f = t - floor(t) */ - let mut p = c0; /* c0 */ - p = prefer_vfmaq_f32(c1, p, f); /* c0 * f + c1 */ - p = prefer_vfmaq_f32(c2, p, f); /* p = (c0 * f + c1) * f + c2 ~= 2^f */ - let j = vshlq_n_s32::<23>(i); /* i << 23 */ - let r = vreinterpretq_f32_s32(vaddq_s32(j, vreinterpretq_s32_f32(p))); /* r = p * 2^i*/ - if PROCESS_NAN { - let inf = vdupq_n_f32(f32::INFINITY); - let max_input = vdupq_n_f32(88.72283f32); // Approximately ln(2^127.5) - let min_input = vdupq_n_f32(-87.33654f32); // Approximately ln(2^-125) - let poly = vbslq_f32(vcltq_f32(x, min_input), vdupq_n_f32(0f32), r); - let poly = vbslq_f32(vcgtq_f32(x, max_input), inf, poly); - return poly; - } else { - return r; - } -} - -#[inline(always)] -#[allow(dead_code)] -pub unsafe fn vexpq_f32_ulp1_5(x: float32x4_t) -> float32x4_t { - let c1 = vreinterpretq_f32_u32(vdupq_n_u32(0x3f7ffff6)); // x^1: 0x1.ffffecp-1f - let c2 = vreinterpretq_f32_u32(vdupq_n_u32(0x3efffedb)); // x^2: 0x1.fffdb6p-2f - let c3 = vreinterpretq_f32_u32(vdupq_n_u32(0x3e2aaf33)); // x^3: 0x1.555e66p-3f - let c4 = vreinterpretq_f32_u32(vdupq_n_u32(0x3d2b9f17)); // x^4: 0x1.573e2ep-5f - let c5 = vreinterpretq_f32_u32(vdupq_n_u32(0x3c072010)); // x^5: 0x1.0e4020p-7f - - let shift = vreinterpretq_f32_u32(vdupq_n_u32(0x4b00007f)); // 2^23 + 127 = 0x1.0000fep23f - let inv_ln2 = vreinterpretq_f32_u32(vdupq_n_u32(0x3fb8aa3b)); // 1 / ln(2) = 0x1.715476p+0f - let neg_ln2_hi = vreinterpretq_f32_u32(vdupq_n_u32(0xbf317200)); // -ln(2) from bits -1 to -19: -0x1.62e400p-1f - let neg_ln2_lo = vreinterpretq_f32_u32(vdupq_n_u32(0xb5bfbe8e)); // -ln(2) from bits -20 to -42: -0x1.7f7d1cp-20f - - // Range reduction: - // e^x = 2^n * e^r - // where: - // n = floor(x / ln(2)) - // r = x - n * ln(2) - // - // By adding x / ln(2) with 2^23 + 127 (shift): - // * As FP32 fraction part only has 23-bits, the addition of 2^23 + 127 forces decimal part - // of x / ln(2) out of the result. The integer part of x / ln(2) (i.e. n) + 127 will occupy - // the whole fraction part of z in FP32 format. - // Subtracting 2^23 + 127 (shift) from z will result in the integer part of x / ln(2) - // (i.e. n) because the decimal part has been pushed out and lost. - // * The addition of 127 makes the FP32 fraction part of z ready to be used as the exponent - // in FP32 format. Left shifting z by 23 bits will result in 2^n. - let z = prefer_vfmaq_f32(shift, x, inv_ln2); - let n = vsubq_f32(z, shift); - let scale = vreinterpretq_f32_u32(vshlq_n_u32::<23>(vreinterpretq_u32_f32(z))); // 2^n - - // The calculation of n * ln(2) is done using 2 steps to achieve accuracy beyond FP32. - // This outperforms longer Taylor series (3-4 tabs) both in terms of accuracy and performance. - let r_hi = prefer_vfmaq_f32(x, n, neg_ln2_hi); - let r = prefer_vfmaq_f32(r_hi, n, neg_ln2_lo); - - // Compute the truncated Taylor series of e^r. - // poly = scale * (1 + c1 * r + c2 * r^2 + c3 * r^3 + c4 * r^4 + c5 * r^5) - let r2 = vmulq_f32(r, r); - - let p1 = vmulq_f32(c1, r); - let p23 = prefer_vfmaq_f32(c2, c3, r); - let p45 = prefer_vfmaq_f32(c4, c5, r); - let p2345 = prefer_vfmaq_f32(p23, p45, r2); - let p12345 = prefer_vfmaq_f32(p1, p2345, r2); - - let mut poly = prefer_vfmaq_f32(scale, p12345, scale); - - // Handle underflow and overflow. - if HANDLE_NAN { - let inf = vdupq_n_f32(f32::INFINITY); - let max_input = vdupq_n_f32(88.37f32); // Approximately ln(2^127.5) - let min_input = vdupq_n_f32(-86.64f32); // Approximately ln(2^-125) - let zero = vdupq_n_f32(0f32); - poly = vbslq_f32(vcltq_f32(x, min_input), zero, poly); - poly = vbslq_f32(vcgtq_f32(x, max_input), inf, poly); - } - - return poly; -} - -#[inline(always)] -#[allow(dead_code)] -/// High precision exp. ULP = 1.0 -pub unsafe fn vexpq_f32_ulp1(d: float32x4_t) -> float32x4_t { - let q = vrintq_s32(vmulq_n_f32(d, std::f32::consts::LOG2_E)); - - let mut s = vmlafq_f32(vcvtq_f32_s32(q), vdupq_n_f32(-std::f32::consts::LN_2), d); - s = vmlafq_f32( - vcvtq_f32_s32(q), - vdupq_n_f32(-1.428606765330187045e-06f32), - s, - ); - - let mut u = vdupq_n_f32(0.000198527617612853646278381f32); - u = vmlafq_f32(u, s, vdupq_n_f32(0.00139304355252534151077271f32)); - u = vmlafq_f32(u, s, vdupq_n_f32(0.00833336077630519866943359f32)); - u = vmlafq_f32(u, s, vdupq_n_f32(0.0416664853692054748535156f32)); - u = vmlafq_f32(u, s, vdupq_n_f32(0.166666671633720397949219f32)); - u = vmlafq_f32(u, s, vdupq_n_f32(0.5f32)); - - u = vaddq_f32(vdupq_n_f32(1.0f32), vmlafq_f32(vmulq_f32(s, s), u, s)); - - u = vldexp2q_f32(u, q); - - u = vreinterpretq_f32_u32(vbicq_u32( - vreinterpretq_u32_f32(u), - vcltq_f32(d, vdupq_n_f32(-104f32)), - )); - u = vbslq_f32( - vcltq_f32(vdupq_n_f32(100f32), d), - vdupq_n_f32(f32::INFINITY), - u, - ); - u -} - -#[inline(always)] -pub unsafe fn vlogq_f32(x: float32x4_t) -> float32x4_t { - let nan_mask = vclezq_f32(x); - let const_ln127 = vdupq_n_s32(127); // 127 - let const_ln2 = vdupq_n_f32(std::f32::consts::LN_2); // ln(2) - - // Extract exponent - let m = vsubq_s32( - vreinterpretq_s32_u32(vshrq_n_u32::<23>(vreinterpretq_u32_f32(x))), - const_ln127, - ); - let val = vreinterpretq_f32_s32(vsubq_s32(vreinterpretq_s32_f32(x), vshlq_n_s32::<23>(m))); - - // Polynomial Approximation - let mut poly = vtaylor_polyq_f32( - val, - vdupq_n_f32(-2.29561495781f32), - vdupq_n_f32(-2.47071170807f32), - vdupq_n_f32(-5.68692588806f32), - vdupq_n_f32(-0.165253549814f32), - vdupq_n_f32(5.17591238022f32), - vdupq_n_f32(0.844007015228f32), - vdupq_n_f32(4.58445882797f32), - vdupq_n_f32(0.0141278216615f32), - ); - - // Reconstruct - poly = prefer_vfmaq_f32(poly, vcvtq_f32_s32(m), const_ln2); - - if HANDLE_NAN { - poly = vbslq_f32(nan_mask, vdupq_n_f32(-f32::INFINITY), poly); - } else { - poly = vbslq_f32(nan_mask, vdupq_n_f32(0f32), poly); - } - - return poly; -} - -#[inline(always)] -#[allow(dead_code)] -pub unsafe fn visnanq_f32(x: float32x4_t) -> uint32x4_t { - return vmvnq_u32(vceqq_f32(x, x)); -} - -#[inline(always)] -#[allow(dead_code)] -pub unsafe fn vispinfq_f32(d: float32x4_t) -> uint32x4_t { - return vceqq_f32(d, vdupq_n_f32(f32::INFINITY)); -} - -#[inline(always)] -#[allow(dead_code)] -/// High precision log ULP = 3.5 -pub unsafe fn vlogq_f32_ulp35(d: float32x4_t) -> float32x4_t { - let o = vceqq_f32(d, vdupq_n_f32(f32::MIN)); - let m = (1i64 << 32i64) as f32; - let d = vbslq_f32(o, vmulq_f32(d, vdupq_n_f32(m * m)), d); - let e = vilogbk_vi2_vf(vmulq_f32(d, vdupq_n_f32(1.0f32 / 0.75f32))); - let m = vldexp2q_f32(d, vnegq_s32(e)); - let e = vbslq_s32(o, vsubq_s32(e, vdupq_n_s32(64)), e); - - let mut x = vdivq_f32( - vsubq_f32(m, vdupq_n_f32(1.0f32)), - vaddq_f32(vdupq_n_f32(1.0f32), m), - ); - let x2 = vmulq_f32(x, x); - - let mut t = vdupq_n_f32(0.2392828464508056640625f32); - t = vmlafq_f32(t, x2, vdupq_n_f32(0.28518211841583251953125f32)); - t = vmlafq_f32(t, x2, vdupq_n_f32(0.400005877017974853515625f32)); - t = vmlafq_f32(t, x2, vdupq_n_f32(0.666666686534881591796875f32)); - t = vmlafq_f32(t, x2, vdupq_n_f32(2.0f32)); - - x = vmlafq_f32( - x, - t, - vmulq_f32(vdupq_n_f32(std::f32::consts::LN_2), vcvtq_f32_s32(e)), - ); - x = vbslq_f32(vispinfq_f32(d), vdupq_n_f32(f32::NAN), x); - x = vbslq_f32( - vorrq_u32(vcltq_f32(d, vdupq_n_f32(0f32)), visnanq_f32(d)), - vdupq_n_f32(f32::NAN), - x, - ); - x = vbslq_f32(vceqq_f32(d, vdupq_n_f32(0f32)), vdupq_n_f32(-f32::NAN), x); - return x; -} - #[inline(always)] pub unsafe fn vpowjq_f32(val: float32x4_t, n: float32x4_t) -> float32x4_t { vpowq_fast_f32(val, n) diff --git a/src/neon/mod.rs b/src/neon/mod.rs index 0e9dc03..b8f32d1 100644 --- a/src/neon/mod.rs +++ b/src/neon/mod.rs @@ -1,3 +1,10 @@ +/* + * // Copyright 2024 (c) the Radzivon Bartoshyk. All rights reserved. + * // + * // Use of this source code is governed by a BSD-style + * // license that can be found in the LICENSE file. + */ + mod cie; mod colors; mod from_sigmoidal; diff --git a/src/neon/planar_to_linear.rs b/src/neon/planar_to_linear.rs index 6b71d8d..8b5c202 100644 --- a/src/neon/planar_to_linear.rs +++ b/src/neon/planar_to_linear.rs @@ -1,3 +1,10 @@ +/* + * // Copyright 2024 (c) the Radzivon Bartoshyk. All rights reserved. + * // + * // Use of this source code is governed by a BSD-style + * // license that can be found in the LICENSE file. + */ + use crate::gamma_curves::TransferFunction; use crate::neon::*; use std::arch::aarch64::*; diff --git a/src/neon/sigmoidal.rs b/src/neon/sigmoidal.rs index e1af8aa..df55d2f 100644 --- a/src/neon/sigmoidal.rs +++ b/src/neon/sigmoidal.rs @@ -1,6 +1,12 @@ -use std::arch::aarch64::*; +/* + * // Copyright 2024 (c) the Radzivon Bartoshyk. All rights reserved. + * // + * // Use of this source code is governed by a BSD-style + * // license that can be found in the LICENSE file. + */ -use crate::neon::math::{vexpq_f32, vlogq_f32}; +use erydanos::{vexpq_f32, vlnq_fast_f32}; +use std::arch::aarch64::*; #[inline(always)] pub(crate) unsafe fn neon_color_to_sigmoidal(x: float32x4_t) -> float32x4_t { @@ -19,7 +25,7 @@ pub(crate) unsafe fn neon_sigmoidal_to_color(x: float32x4_t) -> float32x4_t { let k = vmulq_f32(x, vrecpeq_f32(den)); let zeros = vdupq_n_f32(0f32); let zero_mask_2 = vcleq_f32(k, zeros); - let ln = vlogq_f32::(k); + let ln = vlnq_fast_f32(k); let rs = vbslq_f32(vandq_u32(zero_mask_1, zero_mask_2), zeros, ln); return rs; } diff --git a/src/neon/to_linear.rs b/src/neon/to_linear.rs index d7ebd22..3f9a5cf 100644 --- a/src/neon/to_linear.rs +++ b/src/neon/to_linear.rs @@ -1,3 +1,10 @@ +/* + * // Copyright 2024 (c) the Radzivon Bartoshyk. All rights reserved. + * // + * // Use of this source code is governed by a BSD-style + * // license that can be found in the LICENSE file. + */ + use crate::gamma_curves::TransferFunction; use crate::image::ImageConfiguration; use crate::neon::*; diff --git a/src/neon/to_linear_u8.rs b/src/neon/to_linear_u8.rs index 0202595..6f74976 100644 --- a/src/neon/to_linear_u8.rs +++ b/src/neon/to_linear_u8.rs @@ -1,3 +1,10 @@ +/* + * // Copyright 2024 (c) the Radzivon Bartoshyk. All rights reserved. + * // + * // Use of this source code is governed by a BSD-style + * // license that can be found in the LICENSE file. + */ + pub mod neon_image_linear_to_u8 { use crate::image::ImageConfiguration; use std::arch::aarch64::*; diff --git a/src/neon/to_sigmoidal.rs b/src/neon/to_sigmoidal.rs index 610d886..6f4420b 100644 --- a/src/neon/to_sigmoidal.rs +++ b/src/neon/to_sigmoidal.rs @@ -1,3 +1,10 @@ +/* + * // Copyright 2024 (c) the Radzivon Bartoshyk. All rights reserved. + * // + * // Use of this source code is governed by a BSD-style + * // license that can be found in the LICENSE file. + */ + use crate::image::ImageConfiguration; use crate::neon::sigmoidal::neon_rgb_to_sigmoidal; use std::arch::aarch64::*; diff --git a/src/neon/to_xyz_lab.rs b/src/neon/to_xyz_lab.rs index 5d2489a..cc15faa 100644 --- a/src/neon/to_xyz_lab.rs +++ b/src/neon/to_xyz_lab.rs @@ -1,3 +1,10 @@ +/* + * // Copyright 2024 (c) the Radzivon Bartoshyk. All rights reserved. + * // + * // Use of this source code is governed by a BSD-style + * // license that can be found in the LICENSE file. + */ + use crate::gamma_curves::TransferFunction; use crate::image::ImageConfiguration; use crate::neon::cie::{ diff --git a/src/neon/to_xyza_laba.rs b/src/neon/to_xyza_laba.rs index 8cec0e5..5a5e7f1 100644 --- a/src/neon/to_xyza_laba.rs +++ b/src/neon/to_xyza_laba.rs @@ -1,3 +1,10 @@ +/* + * // Copyright 2024 (c) the Radzivon Bartoshyk. All rights reserved. + * // + * // Use of this source code is governed by a BSD-style + * // license that can be found in the LICENSE file. + */ + use crate::gamma_curves::TransferFunction; use crate::image::ImageConfiguration; use crate::neon::cie::{ diff --git a/src/neon/xyz_lab_to_image.rs b/src/neon/xyz_lab_to_image.rs index 8041bfd..8fbfa9a 100644 --- a/src/neon/xyz_lab_to_image.rs +++ b/src/neon/xyz_lab_to_image.rs @@ -1,3 +1,10 @@ +/* + * // Copyright 2024 (c) the Radzivon Bartoshyk. All rights reserved. + * // + * // Use of this source code is governed by a BSD-style + * // license that can be found in the LICENSE file. + */ + use crate::image::ImageConfiguration; use crate::neon::cie::{neon_lab_to_xyz, neon_lch_to_xyz, neon_luv_to_xyz}; use crate::neon::math::*; diff --git a/src/neon/xyza_laba_to_image.rs b/src/neon/xyza_laba_to_image.rs index 876b8b2..c88e024 100644 --- a/src/neon/xyza_laba_to_image.rs +++ b/src/neon/xyza_laba_to_image.rs @@ -1,3 +1,10 @@ +/* + * // Copyright 2024 (c) the Radzivon Bartoshyk. All rights reserved. + * // + * // Use of this source code is governed by a BSD-style + * // license that can be found in the LICENSE file. + */ + use crate::image::ImageConfiguration; use crate::neon::cie::{neon_lab_to_xyz, neon_lch_to_xyz, neon_luv_to_xyz}; use crate::neon::math::vcolorq_matrix_f32; diff --git a/src/planar_to_linear.rs b/src/planar_to_linear.rs index 380c0d8..9ad7e5e 100644 --- a/src/planar_to_linear.rs +++ b/src/planar_to_linear.rs @@ -1,3 +1,10 @@ +/* + * // Copyright 2024 (c) the Radzivon Bartoshyk. All rights reserved. + * // + * // Use of this source code is governed by a BSD-style + * // license that can be found in the LICENSE file. + */ + #[cfg(all( any(target_arch = "aarch64", target_arch = "arm"), target_feature = "neon" diff --git a/src/rgb.rs b/src/rgb.rs index 16e0770..5096a04 100644 --- a/src/rgb.rs +++ b/src/rgb.rs @@ -1,7 +1,15 @@ +/* + * // Copyright 2024 (c) the Radzivon Bartoshyk. All rights reserved. + * // + * // Use of this source code is governed by a BSD-style + * // license that can be found in the LICENSE file. + */ +use crate::euclidean::EuclideanDistance; use crate::hsv::Hsv; use crate::lab::Lab; use crate::luv::Luv; use crate::{Hsl, LCh, Sigmoidal}; +use erydanos::Euclidean3DDistance; #[derive(Debug, PartialOrd, PartialEq, Clone, Copy)] /// Represents any RGB values, Rgb, Rgb etc. @@ -40,7 +48,7 @@ impl Rgb { LCh::from_rgb(self) } - #[inline(always)] + #[inline] pub fn to_rgb_f32(&self) -> Rgb { const SCALE: f32 = 1f32 / 255f32; Rgb::::new( @@ -50,21 +58,21 @@ impl Rgb { ) } - #[inline(always)] + #[inline] pub fn to_sigmoidal(&self) -> Sigmoidal { Sigmoidal::from_rgb(self) } } impl From> for Rgb { - #[inline(always)] + #[inline] fn from(value: Rgb) -> Self { value.to_u8() } } impl Rgb { - #[inline(always)] + #[inline] pub fn apply(&self, gen: fn(f32) -> f32) -> Self { Self { r: gen(self.r), @@ -73,7 +81,7 @@ impl Rgb { } } - #[inline(always)] + #[inline] pub fn to_u8(&self) -> Rgb { Rgb::::new( (self.r * 255f32).max(0f32).round().min(255f32) as u8, @@ -84,7 +92,7 @@ impl Rgb { } impl From for Rgb { - #[inline(always)] + #[inline] fn from(value: Sigmoidal) -> Self { value.to_rgb() } @@ -95,3 +103,27 @@ impl Rgb { Rgb { r, g, b } } } + +impl EuclideanDistance for Rgb { + fn euclidean_distance(&self, other: Rgb) -> f32 { + (self.r as f32 - other.r as f32).hypot3( + self.g as f32 - other.g as f32, + self.b as f32 - other.b as f32, + ) + } +} + +impl EuclideanDistance for Rgb { + fn euclidean_distance(&self, other: Rgb) -> f32 { + (self.r - other.r).hypot3(self.g - other.g, self.b - other.b) + } +} + +impl EuclideanDistance for Rgb { + fn euclidean_distance(&self, other: Rgb) -> f32 { + (self.r as f32 - other.r as f32).hypot3( + self.g as f32 - other.g as f32, + self.b as f32 - other.b as f32, + ) + } +} diff --git a/src/rgb_expand.rs b/src/rgb_expand.rs index 12026cc..e2dd105 100644 --- a/src/rgb_expand.rs +++ b/src/rgb_expand.rs @@ -1,3 +1,10 @@ +/* + * // Copyright 2024 (c) the Radzivon Bartoshyk. All rights reserved. + * // + * // Use of this source code is governed by a BSD-style + * // license that can be found in the LICENSE file. + */ + #[cfg(all( any(target_arch = "x86_64", target_arch = "x86"), target_feature = "avx2" diff --git a/src/rgba.rs b/src/rgba.rs index f17abc6..9d54791 100644 --- a/src/rgba.rs +++ b/src/rgba.rs @@ -1,3 +1,10 @@ +/* + * // Copyright 2024 (c) the Radzivon Bartoshyk. All rights reserved. + * // + * // Use of this source code is governed by a BSD-style + * // license that can be found in the LICENSE file. + */ + use crate::rgb::Rgb; use half::f16; @@ -79,6 +86,7 @@ pub trait ToRgbaF32 { } impl ToRgbaF32 for Rgba { + #[inline] fn to_rgba_f32(&self) -> Rgba { const SCALE_U8: f32 = 1f32 / 255f32; return Rgba::::new( @@ -91,6 +99,7 @@ impl ToRgbaF32 for Rgba { } impl ToRgba8 for Rgba { + #[inline] fn to_rgba8(&self) -> Rgba { return Rgba { r: (self.r * 255f32).min(255f32).max(0f32) as u8, @@ -102,6 +111,7 @@ impl ToRgba8 for Rgba { } impl ToRgba8 for Rgba { + #[inline] fn to_rgba8(&self) -> Rgba { return Rgba { r: (self.r.to_f32() * 255f32).min(255f32).max(0f32) as u8, @@ -113,6 +123,7 @@ impl ToRgba8 for Rgba { } impl ToRgbaF16 for Rgba { + #[inline] fn to_rgba_f16(&self) -> Rgba { Rgba { r: f16::from_f32(self.r), @@ -126,6 +137,7 @@ impl ToRgbaF16 for Rgba { static SCALE_U8_F32: f32 = 1f32 / 255f32; impl ToRgbaF16 for Rgba { + #[inline] fn to_rgba_f16(&self) -> Rgba { Rgba { r: f16::from_f32(self.r as f32 * SCALE_U8_F32), @@ -174,6 +186,7 @@ impl ToRgbaF16 for Rgb565 { } impl ToRgb565 for Rgba { + #[inline] fn to_rgb_565(&self) -> Rgb565 { let red565 = ((self.r as u16) >> 3) << 11; let green565 = ((self.g as u16) >> 2) << 5; @@ -185,6 +198,7 @@ impl ToRgb565 for Rgba { } impl ToRgb565 for Rgba { + #[inline] fn to_rgb_565(&self) -> Rgb565 { let red5 = (self.r.to_f32() * 31f32).max(31f32).min(0f32) as u16; let green6 = (self.g.to_f32() * 63f32).max(63f32).min(0f32) as u16; @@ -196,6 +210,7 @@ impl ToRgb565 for Rgba { } impl ToRgb565 for Rgba { + #[inline] fn to_rgb_565(&self) -> Rgb565 { let red5 = (self.r * 31f32).max(31f32).min(0f32) as u16; let green6 = (self.g * 63f32).max(63f32).min(0f32) as u16; @@ -213,12 +228,14 @@ pub struct Rgba1010102 { } impl Rgba1010102 { + #[inline] pub fn new(color: u32) -> Rgba1010102 { Rgba1010102 { rgba: color } } } impl ToRgba8 for Rgba1010102 { + #[inline] fn to_rgba8(&self) -> Rgba { let mask = (1u32 << 10u32) - 1u32; let r = (self.rgba) & mask; @@ -238,6 +255,7 @@ static SCALE_RGBA10: f32 = 1f32 / 1023f32; static SCALE_RGBA10ALPHA: f32 = 1f32 / 3f32; impl ToRgbaF16 for Rgba1010102 { + #[inline] fn to_rgba_f16(&self) -> Rgba { let mask = (1u32 << 10u32) - 1u32; let r = (self.rgba) & mask; @@ -259,6 +277,7 @@ pub trait ToRgba1010102 { } impl ToRgba1010102 for Rgba { + #[inline] fn to_rgba1010102(&self) -> Rgba1010102 { let r = (self.r as u32) << 2; let g = (self.g as u32) << 2; @@ -270,6 +289,7 @@ impl ToRgba1010102 for Rgba { } impl ToRgba1010102 for Rgba { + #[inline] fn to_rgba1010102(&self) -> Rgba1010102 { let r = (self.r.to_f32() * 1023f32).min(1023f32).max(0f32) as u32; let g = (self.g.to_f32() * 1023f32).min(1023f32).max(0f32) as u32; diff --git a/src/sigmoidal.rs b/src/sigmoidal.rs index feb986b..1a2d18e 100644 --- a/src/sigmoidal.rs +++ b/src/sigmoidal.rs @@ -1,3 +1,10 @@ +/* + * // Copyright 2024 (c) the Radzivon Bartoshyk. All rights reserved. + * // + * // Use of this source code is governed by a BSD-style + * // license that can be found in the LICENSE file. + */ + use crate::Rgb; #[derive(Debug, PartialOrd, PartialEq, Copy, Clone)] @@ -10,7 +17,7 @@ pub struct Sigmoidal { pub sb: f32, } -#[inline(always)] +#[inline] fn to_sigmoidal(x: f32) -> f32 { let den = 1f32 + (-x).exp(); if den == 0f32 { @@ -19,7 +26,7 @@ fn to_sigmoidal(x: f32) -> f32 { return 1f32 / den; } -#[inline(always)] +#[inline] fn inverse_sigmoidal(x: f32) -> f32 { let den = 1f32 - x; if den == 0f32 { @@ -37,7 +44,7 @@ impl Sigmoidal { Sigmoidal { sr, sg, sb } } - #[inline(always)] + #[inline] pub fn from_rgb(rgb: &Rgb) -> Self { let normalized = rgb.to_rgb_f32(); Sigmoidal::new( @@ -47,7 +54,7 @@ impl Sigmoidal { ) } - #[inline(always)] + #[inline] pub fn to_rgb(&self) -> Rgb { let rgb_normalized = Rgb::new( inverse_sigmoidal(self.sr), @@ -59,14 +66,14 @@ impl Sigmoidal { } impl From> for Sigmoidal { - #[inline(always)] + #[inline] fn from(value: Rgb) -> Self { Sigmoidal::from_rgb(&value) } } impl From> for Sigmoidal { - #[inline(always)] + #[inline] fn from(value: Rgb) -> Self { Sigmoidal::new( to_sigmoidal(value.r), diff --git a/src/sigmoidal_to_image.rs b/src/sigmoidal_to_image.rs index e6e5c7a..e54a562 100644 --- a/src/sigmoidal_to_image.rs +++ b/src/sigmoidal_to_image.rs @@ -1,3 +1,10 @@ +/* + * // Copyright 2024 (c) the Radzivon Bartoshyk. All rights reserved. + * // + * // Use of this source code is governed by a BSD-style + * // license that can be found in the LICENSE file. + */ + #[cfg(all( any(target_arch = "x86_64", target_arch = "x86"), target_feature = "avx2" diff --git a/src/sse/cie.rs b/src/sse/cie.rs index d4c8d93..7a24386 100644 --- a/src/sse/cie.rs +++ b/src/sse/cie.rs @@ -1,16 +1,20 @@ +/* + * // Copyright 2024 (c) the Radzivon Bartoshyk. All rights reserved. + * // + * // Use of this source code is governed by a BSD-style + * // license that can be found in the LICENSE file. + */ + use crate::luv::{ LUV_CUTOFF_FORWARD_Y, LUV_MULTIPLIER_FORWARD_Y, LUV_MULTIPLIER_INVERSE_Y, LUV_WHITE_U_PRIME, LUV_WHITE_V_PRIME, }; -use crate::sse::{ - _mm_color_matrix_ps, _mm_cube_ps, - _mm_prefer_fma_ps, _mm_select_ps, -}; +use crate::sse::{_mm_color_matrix_ps, _mm_cube_ps, _mm_prefer_fma_ps, _mm_select_ps}; +use erydanos::{_mm_atan2_ps, _mm_cbrt_fast_ps, _mm_cos_ps, _mm_hypot_ps, _mm_sin_ps}; #[cfg(target_arch = "x86")] use std::arch::x86::*; #[cfg(target_arch = "x86_64")] use std::arch::x86_64::*; -use erydanos::{_mm_atan2_ps, _mm_cbrt_ps, _mm_cos_ps, _mm_hypot_ps, _mm_sin_ps}; #[inline(always)] pub(crate) unsafe fn sse_triple_to_xyz( @@ -56,7 +60,7 @@ pub(crate) unsafe fn sse_triple_to_luv( ); let nan_mask = _mm_cmpeq_ps(den, _mm_set1_ps(0f32)); let l_low_mask = _mm_cmplt_ps(y, _mm_set1_ps(LUV_CUTOFF_FORWARD_Y)); - let y_cbrt = _mm_cbrt_ps(y); + let y_cbrt = _mm_cbrt_fast_ps(y); let l = _mm_select_ps( l_low_mask, _mm_mul_ps(y, _mm_set1_ps(LUV_MULTIPLIER_FORWARD_Y)), @@ -81,9 +85,9 @@ pub(crate) unsafe fn sse_triple_to_lab( let x = _mm_mul_ps(x, _mm_set1_ps(100f32 / 95.047f32)); let y = _mm_mul_ps(y, _mm_set1_ps(100f32 / 100f32)); let z = _mm_mul_ps(z, _mm_set1_ps(100f32 / 108.883f32)); - let cbrt_x = _mm_cbrt_ps(x); - let cbrt_y = _mm_cbrt_ps(y); - let cbrt_z = _mm_cbrt_ps(z); + let cbrt_x = _mm_cbrt_fast_ps(x); + let cbrt_y = _mm_cbrt_fast_ps(y); + let cbrt_z = _mm_cbrt_fast_ps(z); let s_1 = _mm_set1_ps(16.0 / 116.0); let s_2 = _mm_set1_ps(7.787); let lower_x = _mm_prefer_fma_ps(s_1, s_2, x); diff --git a/src/sse/color.rs b/src/sse/color.rs index 84be872..2bd84db 100644 --- a/src/sse/color.rs +++ b/src/sse/color.rs @@ -1,9 +1,16 @@ +/* + * // Copyright 2024 (c) the Radzivon Bartoshyk. All rights reserved. + * // + * // Use of this source code is governed by a BSD-style + * // license that can be found in the LICENSE file. + */ + use crate::sse::{_mm_prefer_fma_ps, _mm_select_ps}; +use erydanos::{_mm_abs_ps, _mm_fmod_ps}; #[cfg(target_arch = "x86")] use std::arch::x86::*; #[cfg(target_arch = "x86_64")] use std::arch::x86_64::*; -use erydanos::{_mm_abs_ps, _mm_fmod_ps}; #[inline(always)] pub unsafe fn sse_hsl_to_rgb( diff --git a/src/sse/from_sigmoidal.rs b/src/sse/from_sigmoidal.rs index ad6ffb2..abd3b10 100644 --- a/src/sse/from_sigmoidal.rs +++ b/src/sse/from_sigmoidal.rs @@ -1,3 +1,10 @@ +/* + * // Copyright 2024 (c) the Radzivon Bartoshyk. All rights reserved. + * // + * // Use of this source code is governed by a BSD-style + * // license that can be found in the LICENSE file. + */ + #[cfg(target_arch = "x86")] use std::arch::x86::*; #[cfg(target_arch = "x86_64")] diff --git a/src/sse/from_xyz_lab.rs b/src/sse/from_xyz_lab.rs index 8b13789..da922f2 100644 --- a/src/sse/from_xyz_lab.rs +++ b/src/sse/from_xyz_lab.rs @@ -1 +1,6 @@ - +/* + * // Copyright 2024 (c) the Radzivon Bartoshyk. All rights reserved. + * // + * // Use of this source code is governed by a BSD-style + * // license that can be found in the LICENSE file. + */ diff --git a/src/sse/gamma_curves.rs b/src/sse/gamma_curves.rs index 92196fd..df62fb0 100644 --- a/src/sse/gamma_curves.rs +++ b/src/sse/gamma_curves.rs @@ -1,3 +1,10 @@ +/* + * // Copyright 2024 (c) the Radzivon Bartoshyk. All rights reserved. + * // + * // Use of this source code is governed by a BSD-style + * // license that can be found in the LICENSE file. + */ + use crate::gamma_curves::TransferFunction; use crate::sse::*; #[cfg(target_arch = "x86")] diff --git a/src/sse/hsv_to_image.rs b/src/sse/hsv_to_image.rs index 6f8e807..09a793d 100644 --- a/src/sse/hsv_to_image.rs +++ b/src/sse/hsv_to_image.rs @@ -1,3 +1,10 @@ +/* + * // Copyright 2024 (c) the Radzivon Bartoshyk. All rights reserved. + * // + * // Use of this source code is governed by a BSD-style + * // license that can be found in the LICENSE file. + */ + use crate::image::ImageConfiguration; use crate::image_to_hsv_support::HsvTarget; use crate::sse::color::{sse_hsl_to_rgb, sse_hsv_to_rgb}; diff --git a/src/sse/image_to_hsv.rs b/src/sse/image_to_hsv.rs index df0df6b..56d7e62 100644 --- a/src/sse/image_to_hsv.rs +++ b/src/sse/image_to_hsv.rs @@ -1,3 +1,10 @@ +/* + * // Copyright 2024 (c) the Radzivon Bartoshyk. All rights reserved. + * // + * // Use of this source code is governed by a BSD-style + * // license that can be found in the LICENSE file. + */ + use crate::image::ImageConfiguration; use crate::image_to_hsv_support::HsvTarget; use crate::sse::color::{sse_rgb_to_hsl, sse_rgb_to_hsv}; diff --git a/src/sse/image_to_linear_u8.rs b/src/sse/image_to_linear_u8.rs index e506b64..e286f0a 100644 --- a/src/sse/image_to_linear_u8.rs +++ b/src/sse/image_to_linear_u8.rs @@ -1,3 +1,10 @@ +/* + * // Copyright 2024 (c) the Radzivon Bartoshyk. All rights reserved. + * // + * // Use of this source code is governed by a BSD-style + * // license that can be found in the LICENSE file. + */ + #[cfg(any(target_arch = "x86_64", target_arch = "x86"))] pub mod sse_image_to_linear_unsigned { use crate::image::ImageConfiguration; diff --git a/src/sse/linear_to_image.rs b/src/sse/linear_to_image.rs index c6ddc16..7b244f9 100644 --- a/src/sse/linear_to_image.rs +++ b/src/sse/linear_to_image.rs @@ -1,3 +1,10 @@ +/* + * // Copyright 2024 (c) the Radzivon Bartoshyk. All rights reserved. + * // + * // Use of this source code is governed by a BSD-style + * // license that can be found in the LICENSE file. + */ + use crate::image::ImageConfiguration; use crate::sse::*; use crate::TransferFunction; diff --git a/src/sse/linear_to_planar.rs b/src/sse/linear_to_planar.rs index 19eb088..0b2d160 100644 --- a/src/sse/linear_to_planar.rs +++ b/src/sse/linear_to_planar.rs @@ -1,3 +1,10 @@ +/* + * // Copyright 2024 (c) the Radzivon Bartoshyk. All rights reserved. + * // + * // Use of this source code is governed by a BSD-style + * // license that can be found in the LICENSE file. + */ + use crate::sse::{_mm_loadu_ps_x4, _mm_storeu_si128_x4, get_sse_gamma_transfer}; use crate::TransferFunction; #[cfg(target_arch = "x86")] diff --git a/src/sse/math.rs b/src/sse/math.rs index 94bfef1..93de61f 100644 --- a/src/sse/math.rs +++ b/src/sse/math.rs @@ -1,3 +1,10 @@ +/* + * // Copyright 2024 (c) the Radzivon Bartoshyk. All rights reserved. + * // + * // Use of this source code is governed by a BSD-style + * // license that can be found in the LICENSE file. + */ + #[cfg(target_arch = "x86")] use std::arch::x86::*; #[cfg(target_arch = "x86_64")] @@ -22,28 +29,6 @@ pub unsafe fn _mm_prefer_fma_ps(a: __m128, b: __m128, c: __m128) -> __m128 { return _mm_fmadd_ps(b, c, a); } -#[inline] -unsafe fn _mm_taylorpoly_ps( - x: __m128, - poly0: __m128, - poly1: __m128, - poly2: __m128, - poly3: __m128, - poly4: __m128, - poly5: __m128, - poly6: __m128, - poly7: __m128, -) -> __m128 { - let a = _mm_prefer_fma_ps(poly0, poly4, x); - let b = _mm_prefer_fma_ps(poly2, poly6, x); - let c = _mm_prefer_fma_ps(poly1, poly5, x); - let d = _mm_prefer_fma_ps(poly3, poly7, x); - let x2 = _mm_mul_ps(x, x); - let x4 = _mm_mul_ps(x2, x2); - let res = _mm_prefer_fma_ps(_mm_prefer_fma_ps(a, b, x2), _mm_prefer_fma_ps(c, d, x2), x4); - return res; -} - #[inline(always)] pub unsafe fn _mm_select_ps(mask: __m128, true_vals: __m128, false_vals: __m128) -> __m128 { _mm_blendv_ps(false_vals, true_vals, mask) @@ -161,36 +146,3 @@ pub unsafe fn _mm_color_matrix_ps( let new_b = _mm_prefer_fma_ps(_mm_prefer_fma_ps(_mm_mul_ps(g, c8), b, c9), r, c7); (new_r, new_g, new_b) } - -#[inline(always)] -pub unsafe fn _mm_poly4_ps( - x: __m128, - x2: __m128, - c3: __m128, - c2: __m128, - c1: __m128, - c0: __m128, -) -> __m128 { - _mm_fmaf_ps(x2, _mm_fmaf_ps(x, c3, c2), _mm_fmaf_ps(x, c1, c0)) -} - -#[inline(always)] -pub unsafe fn _mm_poly8q_ps( - x: __m128, - x2: __m128, - x4: __m128, - c7: __m128, - c6: __m128, - c5: __m128, - c4: __m128, - c3: __m128, - c2: __m128, - c1: __m128, - c0: __m128, -) -> __m128 { - _mm_fmaf_ps( - x4, - _mm_poly4_ps(x, x2, c7, c6, c5, c4), - _mm_poly4_ps(x, x2, c3, c2, c1, c0), - ) -} diff --git a/src/sse/mod.rs b/src/sse/mod.rs index 4803b21..9f7905b 100644 --- a/src/sse/mod.rs +++ b/src/sse/mod.rs @@ -1,5 +1,5 @@ /* - * // Copyright (c) the Radzivon Bartoshyk. All rights reserved. + * // Copyright 2024 (c) the Radzivon Bartoshyk. All rights reserved. * // * // Use of this source code is governed by a BSD-style * // license that can be found in the LICENSE file. @@ -33,11 +33,11 @@ mod xyz_lab_to_image; mod cie; mod from_sigmoidal; +mod linear_to_planar; +mod planar_to_linear; mod sigmoidal; mod to_sigmoidal; mod xyza_laba_to_image; -mod planar_to_linear; -mod linear_to_planar; pub use from_sigmoidal::sse_from_sigmoidal_row; pub use gamma_curves::*; @@ -45,7 +45,9 @@ pub use hsv_to_image::*; pub use image_to_hsv::*; pub use image_to_linear_u8::*; pub use linear_to_image::*; +pub use linear_to_planar::sse_linear_plane_to_gamma; pub use math::*; +pub use planar_to_linear::sse_plane_to_linear; pub use support::*; pub use to_linear::*; pub use to_sigmoidal::sse_image_to_sigmoidal_row; @@ -53,5 +55,3 @@ pub use to_xyz_lab::*; pub use to_xyza_laba::*; pub use xyz_lab_to_image::*; pub use xyza_laba_to_image::*; -pub use planar_to_linear::sse_plane_to_linear; -pub use linear_to_planar::sse_linear_plane_to_gamma; \ No newline at end of file diff --git a/src/sse/planar_to_linear.rs b/src/sse/planar_to_linear.rs index 4eba72d..8be0869 100644 --- a/src/sse/planar_to_linear.rs +++ b/src/sse/planar_to_linear.rs @@ -1,3 +1,10 @@ +/* + * // Copyright 2024 (c) the Radzivon Bartoshyk. All rights reserved. + * // + * // Use of this source code is governed by a BSD-style + * // license that can be found in the LICENSE file. + */ + use crate::sse::{_mm_loadu_si128_x4, _mm_storeu_ps_x4, get_sse_linear_transfer}; use crate::TransferFunction; #[cfg(target_arch = "x86")] diff --git a/src/sse/sigmoidal.rs b/src/sse/sigmoidal.rs index f6916a7..77b40d0 100644 --- a/src/sse/sigmoidal.rs +++ b/src/sse/sigmoidal.rs @@ -1,9 +1,16 @@ +/* + * // Copyright 2024 (c) the Radzivon Bartoshyk. All rights reserved. + * // + * // Use of this source code is governed by a BSD-style + * // license that can be found in the LICENSE file. + */ + use crate::sse::{_mm_neg_ps, _mm_select_ps}; +use erydanos::{_mm_exp_ps, _mm_ln_fast_ps}; #[cfg(target_arch = "x86")] use std::arch::x86::*; #[cfg(target_arch = "x86_64")] use std::arch::x86_64::*; -use erydanos::{_mm_exp_ps, _mm_ln_fast_ps}; #[inline(always)] pub(crate) unsafe fn sse_color_to_sigmoidal(x: __m128) -> __m128 { diff --git a/src/sse/support.rs b/src/sse/support.rs index b05b5ef..78a62ce 100644 --- a/src/sse/support.rs +++ b/src/sse/support.rs @@ -1,3 +1,10 @@ +/* + * // Copyright 2024 (c) the Radzivon Bartoshyk. All rights reserved. + * // + * // Use of this source code is governed by a BSD-style + * // license that can be found in the LICENSE file. + */ + #[cfg(target_arch = "x86")] use std::arch::x86::*; #[cfg(target_arch = "x86_64")] @@ -385,8 +392,8 @@ pub unsafe fn _mm_loadu_ps_x4(ptr: *const f32) -> (__m128, __m128, __m128, __m12 #[inline(always)] pub unsafe fn _mm_storeu_si128_x4(ptr: *mut u8, set: (__m128i, __m128i, __m128i, __m128i)) { - _mm_storeu_si128(ptr as * mut __m128i, set.0); - _mm_storeu_si128(ptr.add(16) as * mut __m128i, set.1); - _mm_storeu_si128(ptr.add(32) as * mut __m128i, set.2); - _mm_storeu_si128(ptr.add(48) as * mut __m128i, set.3); -} \ No newline at end of file + _mm_storeu_si128(ptr as *mut __m128i, set.0); + _mm_storeu_si128(ptr.add(16) as *mut __m128i, set.1); + _mm_storeu_si128(ptr.add(32) as *mut __m128i, set.2); + _mm_storeu_si128(ptr.add(48) as *mut __m128i, set.3); +} diff --git a/src/sse/to_linear.rs b/src/sse/to_linear.rs index 628d299..d0339a9 100644 --- a/src/sse/to_linear.rs +++ b/src/sse/to_linear.rs @@ -1,3 +1,10 @@ +/* + * // Copyright 2024 (c) the Radzivon Bartoshyk. All rights reserved. + * // + * // Use of this source code is governed by a BSD-style + * // license that can be found in the LICENSE file. + */ + use crate::gamma_curves::TransferFunction; use crate::image::ImageConfiguration; use crate::sse::*; diff --git a/src/sse/to_sigmoidal.rs b/src/sse/to_sigmoidal.rs index 32b95ce..88a2b7e 100644 --- a/src/sse/to_sigmoidal.rs +++ b/src/sse/to_sigmoidal.rs @@ -1,3 +1,10 @@ +/* + * // Copyright 2024 (c) the Radzivon Bartoshyk. All rights reserved. + * // + * // Use of this source code is governed by a BSD-style + * // license that can be found in the LICENSE file. + */ + #[cfg(target_arch = "x86")] use std::arch::x86::*; #[cfg(target_arch = "x86_64")] diff --git a/src/sse/to_xyz_lab.rs b/src/sse/to_xyz_lab.rs index 9c374ee..81f2ce0 100644 --- a/src/sse/to_xyz_lab.rs +++ b/src/sse/to_xyz_lab.rs @@ -1,3 +1,10 @@ +/* + * // Copyright 2024 (c) the Radzivon Bartoshyk. All rights reserved. + * // + * // Use of this source code is governed by a BSD-style + * // license that can be found in the LICENSE file. + */ + use crate::gamma_curves::TransferFunction; use crate::image::ImageConfiguration; use crate::sse::cie::{sse_triple_to_lab, sse_triple_to_lch, sse_triple_to_luv, sse_triple_to_xyz}; diff --git a/src/sse/to_xyza_laba.rs b/src/sse/to_xyza_laba.rs index 02255f7..5b0f6df 100644 --- a/src/sse/to_xyza_laba.rs +++ b/src/sse/to_xyza_laba.rs @@ -1,3 +1,10 @@ +/* + * // Copyright 2024 (c) the Radzivon Bartoshyk. All rights reserved. + * // + * // Use of this source code is governed by a BSD-style + * // license that can be found in the LICENSE file. + */ + use crate::gamma_curves::TransferFunction; use crate::image::ImageConfiguration; use crate::sse::cie::{sse_triple_to_lab, sse_triple_to_lch, sse_triple_to_luv, sse_triple_to_xyz}; diff --git a/src/sse/xyz_lab_to_image.rs b/src/sse/xyz_lab_to_image.rs index 7d7f4a4..80a1fa6 100644 --- a/src/sse/xyz_lab_to_image.rs +++ b/src/sse/xyz_lab_to_image.rs @@ -1,3 +1,10 @@ +/* + * // Copyright 2024 (c) the Radzivon Bartoshyk. All rights reserved. + * // + * // Use of this source code is governed by a BSD-style + * // license that can be found in the LICENSE file. + */ + use crate::image::ImageConfiguration; use crate::sse::cie::{sse_lab_to_xyz, sse_lch_to_xyz, sse_luv_to_xyz}; use crate::sse::{ diff --git a/src/sse/xyza_laba_to_image.rs b/src/sse/xyza_laba_to_image.rs index 13c746c..e6e9b4b 100644 --- a/src/sse/xyza_laba_to_image.rs +++ b/src/sse/xyza_laba_to_image.rs @@ -1,3 +1,10 @@ +/* + * // Copyright 2024 (c) the Radzivon Bartoshyk. All rights reserved. + * // + * // Use of this source code is governed by a BSD-style + * // license that can be found in the LICENSE file. + */ + use crate::image::ImageConfiguration; use crate::sse::cie::{sse_lab_to_xyz, sse_lch_to_xyz, sse_luv_to_xyz}; use crate::sse::{ diff --git a/src/taxicab.rs b/src/taxicab.rs new file mode 100644 index 0000000..fc73fda --- /dev/null +++ b/src/taxicab.rs @@ -0,0 +1,11 @@ +/* + * // Copyright 2024 (c) the Radzivon Bartoshyk. All rights reserved. + * // + * // Use of this source code is governed by a BSD-style + * // license that can be found in the LICENSE file. + */ + +/// Computes taxicab distance for color, better works for L*a*b +pub trait TaxicabDistance { + fn taxicab_distance(&self, other: Self) -> f32; +} diff --git a/src/xyz.rs b/src/xyz.rs index 392b42a..db68fa9 100644 --- a/src/xyz.rs +++ b/src/xyz.rs @@ -1,6 +1,13 @@ +/* + * // Copyright 2024 (c) the Radzivon Bartoshyk. All rights reserved. + * // + * // Use of this source code is governed by a BSD-style + * // license that can be found in the LICENSE file. + */ use crate::gamma_curves::TransferFunction; use crate::rgb::Rgb; -use crate::{SRGB_TO_XYZ_D65, XYZ_TO_SRGB_D65}; +use crate::{EuclideanDistance, SRGB_TO_XYZ_D65, XYZ_TO_SRGB_D65}; +use erydanos::Euclidean3DDistance; /// A CIE 1931 XYZ color. #[derive(Copy, Clone, Debug, Default)] @@ -39,7 +46,7 @@ static XYZ_SCALE_U8: f32 = 1f32 / 255f32; /// if you need this multiply by yourself or use `scaled` impl Xyz { /// This functions always use sRGB transfer function and Rec.601 primaries with D65 White point - #[inline(always)] + #[inline] pub fn from_srgb(rgb: &Rgb) -> Self { Xyz::from_rgb(rgb, &SRGB_TO_XYZ_D65, TransferFunction::Srgb) } @@ -48,21 +55,29 @@ impl Xyz { /// # Arguments /// * `matrix` - Transformation matrix from RGB to XYZ, for example `SRGB_TO_XYZ_D65` /// * `transfer_function` - Transfer functions for current colorspace - #[inline(always)] + #[inline] pub fn from_rgb( rgb: &Rgb, matrix: &[[f32; 3]; 3], transfer_function: TransferFunction, ) -> Self { let linear_function = transfer_function.get_linearize_function(); - let r = linear_function(rgb.r as f32 * XYZ_SCALE_U8); - let g = linear_function(rgb.g as f32 * XYZ_SCALE_U8); - let b = linear_function(rgb.b as f32 * XYZ_SCALE_U8); - Self::new( - matrix[0][0] * r + matrix[0][1] * g + matrix[0][2] * b, - matrix[1][0] * r + matrix[1][1] * g + matrix[1][2] * b, - matrix[2][0] * r + matrix[2][1] * g + matrix[2][2] * b, - ) + unsafe { + let r = linear_function(rgb.r as f32 * XYZ_SCALE_U8); + let g = linear_function(rgb.g as f32 * XYZ_SCALE_U8); + let b = linear_function(rgb.b as f32 * XYZ_SCALE_U8); + Self::new( + (*(*matrix.get_unchecked(0)).get_unchecked(0)) * r + + (*(*matrix.get_unchecked(0)).get_unchecked(1)) * g + + (*(*matrix.get_unchecked(0)).get_unchecked(2)) * b, + (*(*matrix.get_unchecked(1)).get_unchecked(0)) * r + + (*(*matrix.get_unchecked(1)).get_unchecked(1)) * g + + (*(*matrix.get_unchecked(1)).get_unchecked(2)) * b, + (*(*matrix.get_unchecked(2)).get_unchecked(0)) * r + + (*(*matrix.get_unchecked(2)).get_unchecked(1)) * g + + (*(*matrix.get_unchecked(2)).get_unchecked(2)) * b, + ) + } } pub fn scaled(&self) -> (f32, f32, f32) { @@ -80,18 +95,32 @@ impl Xyz { /// # Arguments /// * `matrix` - Transformation matrix from RGB to XYZ, for example `SRGB_TO_XYZ_D65` /// * `transfer_function` - Transfer functions for current colorspace - #[inline(always)] + #[inline] pub fn to_rgb(&self, matrix: &[[f32; 3]; 3], transfer_function: TransferFunction) -> Rgb { let gamma_function = transfer_function.get_gamma_function(); let x = self.x; let y = self.y; let z = self.z; - let r = x * matrix[0][0] + y * matrix[0][1] + z * matrix[0][2]; - let g = x * matrix[1][0] + y * matrix[1][1] + z * matrix[1][2]; - let b = x * matrix[2][0] + y * matrix[2][1] + z * matrix[2][2]; - let r = 255f32 * gamma_function(r); - let g = 255f32 * gamma_function(g); - let b = 255f32 * gamma_function(b); - Rgb::new(r as u8, g as u8, b as u8) + unsafe { + let r = x * (*(*matrix.get_unchecked(0)).get_unchecked(0)) + + y * (*(*matrix.get_unchecked(0)).get_unchecked(1)) + + z * (*(*matrix.get_unchecked(0)).get_unchecked(2)); + let g = x * (*(*matrix.get_unchecked(1)).get_unchecked(0)) + + y * (*(*matrix.get_unchecked(1)).get_unchecked(1)) + + z * (*(*matrix.get_unchecked(1)).get_unchecked(2)); + let b = x * (*(*matrix.get_unchecked(2)).get_unchecked(0)) + + y * (*(*matrix.get_unchecked(2)).get_unchecked(1)) + + z * (*(*matrix.get_unchecked(2)).get_unchecked(2)); + let r = 255f32 * gamma_function(r); + let g = 255f32 * gamma_function(g); + let b = 255f32 * gamma_function(b); + Rgb::new(r as u8, g as u8, b as u8) + } + } +} + +impl EuclideanDistance for Xyz { + fn euclidean_distance(&self, other: Xyz) -> f32 { + (self.x - other.x).hypot3(self.y - other.y, self.z - other.z) } } diff --git a/src/xyz_lab_to_image.rs b/src/xyz_lab_to_image.rs index c471365..3e7ebff 100644 --- a/src/xyz_lab_to_image.rs +++ b/src/xyz_lab_to_image.rs @@ -1,3 +1,10 @@ +/* + * // Copyright 2024 (c) the Radzivon Bartoshyk. All rights reserved. + * // + * // Use of this source code is governed by a BSD-style + * // license that can be found in the LICENSE file. + */ + #[cfg(all( any(target_arch = "x86_64", target_arch = "x86"), target_feature = "avx2" diff --git a/src/xyz_target.rs b/src/xyz_target.rs index 66a251c..018182f 100644 --- a/src/xyz_target.rs +++ b/src/xyz_target.rs @@ -1,3 +1,10 @@ +/* + * // Copyright 2024 (c) the Radzivon Bartoshyk. All rights reserved. + * // + * // Use of this source code is governed by a BSD-style + * // license that can be found in the LICENSE file. + */ + #[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq)] pub enum XyzTarget { LAB = 0, diff --git a/src/xyz_transform.rs b/src/xyz_transform.rs index 9b95285..054246a 100644 --- a/src/xyz_transform.rs +++ b/src/xyz_transform.rs @@ -1,3 +1,10 @@ +/* + * // Copyright 2024 (c) the Radzivon Bartoshyk. All rights reserved. + * // + * // Use of this source code is governed by a BSD-style + * // license that can be found in the LICENSE file. + */ + /// sRGB to XYZ transformation matrix, D65 White point pub const SRGB_TO_XYZ_D65: [[f32; 3]; 3] = [ [0.4124564f32, 0.3575761f32, 0.1804375f32], diff --git a/src/xyza_laba_to_image.rs b/src/xyza_laba_to_image.rs index bd67409..9130194 100644 --- a/src/xyza_laba_to_image.rs +++ b/src/xyza_laba_to_image.rs @@ -1,3 +1,10 @@ +/* + * // Copyright 2024 (c) the Radzivon Bartoshyk. All rights reserved. + * // + * // Use of this source code is governed by a BSD-style + * // license that can be found in the LICENSE file. + */ + #[cfg(all( any(target_arch = "x86_64", target_arch = "x86"), target_feature = "avx2"