Skip to content

Commit

Permalink
Added Oklab
Browse files Browse the repository at this point in the history
  • Loading branch information
awxkee committed Jul 20, 2024
1 parent 426ac0e commit 9fe91b0
Show file tree
Hide file tree
Showing 17 changed files with 1,746 additions and 12 deletions.
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ workspace = { members = ["src/app"] }

[package]
name = "colorutils-rs"
version = "0.4.14"
version = "0.4.15"
edition = "2021"
description = "High performance utilities for color format handling and conversion."
readme = "README.md"
Expand Down
6 changes: 4 additions & 2 deletions src/app/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,13 +73,14 @@ 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_lab(
rgb_to_oklab(
src_bytes,
src_stride,
&mut lab_store,
store_stride as u32,
width,
height,
TransferFunction::Srgb,
);
let elapsed_time = start_time.elapsed();
// Print the elapsed time in milliseconds
Expand Down Expand Up @@ -107,13 +108,14 @@ fn main() {
// }

let start_time = Instant::now();
lab_to_srgb(
oklab_to_rgb(
&lab_store,
store_stride as u32,
&mut dst_slice,
src_stride,
width,
height,
TransferFunction::Srgb,
);

let elapsed_time = start_time.elapsed();
Expand Down
254 changes: 254 additions & 0 deletions src/image_to_oklab.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,254 @@
/*
* // 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"),
target_feature = "neon"
))]
use crate::neon::neon_image_to_oklab;
#[cfg(all(
any(target_arch = "x86_64", target_arch = "x86"),
target_feature = "sse4.1"
))]
use crate::sse::sse_image_to_oklab;
use crate::{Oklab, Rgb, TransferFunction};

#[inline(always)]
fn channels_to_oklab<const CHANNELS_CONFIGURATION: u8>(
src: &[u8],
src_stride: u32,
dst: &mut [f32],
dst_stride: u32,
width: u32,
height: u32,
transfer_function: TransferFunction,
) {
let image_configuration: ImageConfiguration = CHANNELS_CONFIGURATION.into();

let channels = image_configuration.get_channels_count();

#[cfg(all(
any(target_arch = "x86_64", target_arch = "x86"),
target_feature = "sse4.1"
))]
let mut _has_sse = false;

#[cfg(all(
any(target_arch = "x86_64", target_arch = "x86"),
target_feature = "sse4.1"
))]
if is_x86_feature_detected!("sse4.1") {
_has_sse = true;
}

let mut src_offset = 0usize;
let mut dst_offset = 0usize;

for _ in 0..height as usize {
let mut _cx = 0usize;

let src_ptr = unsafe { src.as_ptr().add(src_offset) };
let dst_ptr = unsafe { (dst.as_mut_ptr() as *mut u8).add(dst_offset) as *mut f32 };

#[cfg(all(
any(target_arch = "aarch64", target_arch = "arm"),
target_feature = "neon"
))]
unsafe {
_cx = neon_image_to_oklab::<CHANNELS_CONFIGURATION>(
_cx,
src.as_ptr(),
src_offset,
width,
dst.as_mut_ptr(),
dst_offset,
transfer_function,
)
}

#[cfg(all(
any(target_arch = "x86_64", target_arch = "x86"),
target_feature = "sse4.1"
))]
unsafe {
if _has_sse {
_cx = sse_image_to_oklab::<CHANNELS_CONFIGURATION>(
_cx,
src.as_ptr(),
src_offset,
width,
dst.as_mut_ptr(),
dst_offset,
transfer_function,
)
}
}

for x in _cx..width as usize {
let px = x * channels;

let src = unsafe { src_ptr.add(px) };
let r = unsafe {
src.add(image_configuration.get_r_channel_offset())
.read_unaligned()
};
let g = unsafe {
src.add(image_configuration.get_g_channel_offset())
.read_unaligned()
};
let b = unsafe {
src.add(image_configuration.get_b_channel_offset())
.read_unaligned()
};

let rgb = Rgb::<u8>::new(r, g, b);
let oklab = Oklab::from_rgb(rgb, transfer_function);

let dst_store = unsafe { dst_ptr.add(px) };

unsafe {
dst_store.write_unaligned(oklab.l);
dst_store.add(1).write_unaligned(oklab.a);
dst_store.add(2).write_unaligned(oklab.b);
}

if image_configuration.has_alpha() {
let a = unsafe {
src.add(image_configuration.get_a_channel_offset())
.read_unaligned()
};
let a_lin = a as f32 * (1f32 / 255f32);
unsafe {
dst_store.add(3).write_unaligned(a_lin);
}
}
}

src_offset += src_stride as usize;
dst_offset += dst_stride as usize;
}
}

/// This function converts RGB to Oklab against D65 white point. This is much more effective than naive direct transformation
///
/// # Arguments
/// * `src` - A slice contains RGB data
/// * `src_stride` - Bytes per row for src data.
/// * `width` - Image width
/// * `height` - Image height
/// * `dst` - A mutable slice to receive LAB(a) data
/// * `dst_stride` - Bytes per row for dst data
/// * `transfer_function` - transfer function to linear colorspace
pub fn rgb_to_oklab(
src: &[u8],
src_stride: u32,
dst: &mut [f32],
dst_stride: u32,
width: u32,
height: u32,
transfer_function: TransferFunction,
) {
channels_to_oklab::<{ ImageConfiguration::Rgb as u8 }>(
src,
src_stride,
dst,
dst_stride,
width,
height,
transfer_function,
);
}

/// This function converts RGBA to Oklab against D65 white point and preserving and normalizing alpha channels keeping it at last positions. This is much more effective than naive direct transformation
///
/// # Arguments
/// * `src` - A slice contains RGBA data
/// * `src_stride` - Bytes per row for src data.
/// * `width` - Image width
/// * `height` - Image height
/// * `dst` - A mutable slice to receive LAB(a) data
/// * `dst_stride` - Bytes per row for dst data
/// * `transfer_function` - transfer function to linear colorspace
pub fn rgba_to_oklab(
src: &[u8],
src_stride: u32,
dst: &mut [f32],
dst_stride: u32,
width: u32,
height: u32,
transfer_function: TransferFunction,
) {
channels_to_oklab::<{ ImageConfiguration::Rgba as u8 }>(
src,
src_stride,
dst,
dst_stride,
width,
height,
transfer_function,
);
}

/// This function converts BGRA to Oklab against D65 white point and preserving and normalizing alpha channels keeping it at last positions. This is much more effective than naive direct transformation
///
/// # Arguments
/// * `src` - A slice contains BGRA data
/// * `src_stride` - Bytes per row for src data.
/// * `width` - Image width
/// * `height` - Image height
/// * `dst` - A mutable slice to receive LAB(a) data
/// * `dst_stride` - Bytes per row for dst data
/// * `transfer_function` - transfer function to linear colorspace
pub fn bgra_to_oklab(
src: &[u8],
src_stride: u32,
dst: &mut [f32],
dst_stride: u32,
width: u32,
height: u32,
transfer_function: TransferFunction,
) {
channels_to_oklab::<{ ImageConfiguration::Bgra as u8 }>(
src,
src_stride,
dst,
dst_stride,
width,
height,
transfer_function,
);
}

/// This function converts BGR to Oklab against D65 white point. This is much more effective than naive direct transformation
///
/// # Arguments
/// * `src` - A slice contains BGR data
/// * `src_stride` - Bytes per row for src data.
/// * `width` - Image width
/// * `height` - Image height
/// * `dst` - A mutable slice to receive LAB(a) data
/// * `dst_stride` - Bytes per row for dst data
/// * `transfer_function` - transfer function to linear colorspace
pub fn bgr_to_oklab(
src: &[u8],
src_stride: u32,
dst: &mut [f32],
dst_stride: u32,
width: u32,
height: u32,
transfer_function: TransferFunction,
) {
channels_to_oklab::<{ ImageConfiguration::Bgr as u8 }>(
src,
src_stride,
dst,
dst_stride,
width,
height,
transfer_function,
);
}
6 changes: 1 addition & 5 deletions src/image_xyza_laba.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,6 @@ fn channels_to_xyz_with_alpha<const CHANNELS_CONFIGURATION: u8, const TARGET: u8
}
}

const CHANNELS: usize = 4;

let channels = image_configuration.get_channels_count();

for _ in 0..height as usize {
Expand Down Expand Up @@ -119,7 +117,7 @@ fn channels_to_xyz_with_alpha<const CHANNELS_CONFIGURATION: u8, const TARGET: u8
};

let rgb = Rgb::<u8>::new(r, g, b);
let px = x * CHANNELS;
let px = x * channels;
let dst_store = unsafe { dst_ptr.add(px) };
match target {
XyzTarget::LAB => {
Expand Down Expand Up @@ -180,8 +178,6 @@ fn channels_to_xyz_with_alpha<const CHANNELS_CONFIGURATION: u8, const TARGET: u8
/// * `height` - Image height
/// * `dst` - A mutable slice to receive LAB(a) data
/// * `dst_stride` - Bytes per row for dst data
/// * `a_plane` - A mutable slice to receive XYZ data
/// * `a_stride` - Bytes per row for dst data
pub fn rgba_to_lab_with_alpha(
src: &[u8],
src_stride: u32,
Expand Down
12 changes: 12 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ mod image_to_hsv;
mod image_to_hsv_support;
mod image_to_linear;
mod image_to_linear_u8;
mod image_to_oklab;
mod image_to_sigmoidal;
mod image_to_xyz_lab;
mod image_xyza_laba;
Expand All @@ -34,6 +35,8 @@ mod luv;
target_feature = "neon"
))]
mod neon;
mod oklab;
mod oklab_to_image;
pub mod planar_to_linear;
mod rgb;
mod rgb_expand;
Expand Down Expand Up @@ -118,9 +121,18 @@ 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_oklab::bgr_to_oklab;
pub use image_to_oklab::bgra_to_oklab;
pub use image_to_oklab::rgb_to_oklab;
pub use image_to_oklab::rgba_to_oklab;
pub use image_to_sigmoidal::bgra_to_sigmoidal;
pub use image_to_sigmoidal::rgb_to_sigmoidal;
pub use image_to_sigmoidal::rgba_to_sigmoidal;
pub use oklab::Oklab;
pub use oklab_to_image::oklab_to_bgr;
pub use oklab_to_image::oklab_to_bgra;
pub use oklab_to_image::oklab_to_rgb;
pub use oklab_to_image::oklab_to_rgba;
pub use rgb_expand::*;
pub use sigmoidal::Sigmoidal;
pub use sigmoidal_to_image::sigmoidal_to_bgra;
Expand Down
4 changes: 2 additions & 2 deletions src/neon/colors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@
* // license that can be found in the LICENSE file.
*/

use crate::neon::math::{prefer_vfmaq_f32};
use std::arch::aarch64::*;
use crate::neon::math::prefer_vfmaq_f32;
use erydanos::vfmodq_f32;
use std::arch::aarch64::*;

#[inline(always)]
pub unsafe fn neon_hsl_to_rgb(
Expand Down
Loading

0 comments on commit 9fe91b0

Please sign in to comment.