Skip to content

Commit

Permalink
Added OkLch
Browse files Browse the repository at this point in the history
  • Loading branch information
awxkee committed Jul 22, 2024
1 parent a30ea28 commit 9f9dee2
Show file tree
Hide file tree
Showing 13 changed files with 600 additions and 186 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.5.2"
version = "0.5.3"
edition = "2021"
description = "High performance utilities for color format handling and conversion."
readme = "README.md"
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ Allows conversion between
- [x] XYZ
- [x] Sigmoidal
- [x] Oklab
- [x] Oklch
- [x] Jzazbz
- [x] Jzczhz

Expand Down
6 changes: 2 additions & 4 deletions src/app/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,14 +66,13 @@ 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_jzazbz(
rgb_to_oklch(
src_bytes,
src_stride,
&mut lab_store,
store_stride as u32,
width,
height,
200f32,
TransferFunction::Srgb,
);
let elapsed_time = start_time.elapsed();
Expand Down Expand Up @@ -102,14 +101,13 @@ fn main() {
// }

let start_time = Instant::now();
jzazbz_to_rgb(
oklch_to_rgb(
&lab_store,
store_stride as u32,
&mut dst_slice,
src_stride,
width,
height,
200f32,
TransferFunction::Srgb,
);

Expand Down
177 changes: 164 additions & 13 deletions src/image_to_oklab.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,34 @@ use crate::image::ImageConfiguration;
target_feature = "neon"
))]
use crate::neon::neon_image_to_oklab;
use crate::oklch::Oklch;
#[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};

#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq)]
pub(crate) enum OklabTarget {
OKLAB = 0,
OKLCH = 1,
}

impl From<u8> for OklabTarget {
fn from(value: u8) -> Self {
match value {
0 => OklabTarget::OKLAB,
1 => OklabTarget::OKLCH,
_ => {
panic!("Not implemented")
}
}
}
}

#[inline(always)]
fn channels_to_oklab<const CHANNELS_CONFIGURATION: u8>(
fn channels_to_oklab<const CHANNELS_CONFIGURATION: u8, const TARGET: u8>(
src: &[u8],
src_stride: u32,
dst: &mut [f32],
Expand All @@ -27,6 +46,7 @@ fn channels_to_oklab<const CHANNELS_CONFIGURATION: u8>(
height: u32,
transfer_function: TransferFunction,
) {
let target: OklabTarget = TARGET.into();
let image_configuration: ImageConfiguration = CHANNELS_CONFIGURATION.into();

let channels = image_configuration.get_channels_count();
Expand All @@ -40,15 +60,15 @@ fn channels_to_oklab<const CHANNELS_CONFIGURATION: u8>(
target_feature = "neon"
))]
{
_wide_row_handle = Some(neon_image_to_oklab::<CHANNELS_CONFIGURATION>);
_wide_row_handle = Some(neon_image_to_oklab::<CHANNELS_CONFIGURATION, TARGET>);
}

#[cfg(all(
any(target_arch = "x86_64", target_arch = "x86"),
target_feature = "sse4.1"
))]
if is_x86_feature_detected!("sse4.1") {
_wide_row_handle = Some(sse_image_to_oklab::<CHANNELS_CONFIGURATION>);
_wide_row_handle = Some(sse_image_to_oklab::<CHANNELS_CONFIGURATION, TARGET>);
}

let mut src_offset = 0usize;
Expand Down Expand Up @@ -92,14 +112,25 @@ fn channels_to_oklab<const CHANNELS_CONFIGURATION: u8>(
};

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);
match target {
OklabTarget::OKLAB => {
let oklab = Oklab::from_rgb(rgb, transfer_function);
unsafe {
dst_store.write_unaligned(oklab.l);
dst_store.add(1).write_unaligned(oklab.a);
dst_store.add(2).write_unaligned(oklab.b);
}
}
OklabTarget::OKLCH => {
let oklch = Oklch::from_rgb(rgb, transfer_function);
unsafe {
dst_store.write_unaligned(oklch.l);
dst_store.add(1).write_unaligned(oklch.c);
dst_store.add(2).write_unaligned(oklch.h);
}
}
}

if image_configuration.has_alpha() {
Expand Down Expand Up @@ -138,7 +169,7 @@ pub fn rgb_to_oklab(
height: u32,
transfer_function: TransferFunction,
) {
channels_to_oklab::<{ ImageConfiguration::Rgb as u8 }>(
channels_to_oklab::<{ ImageConfiguration::Rgb as u8 }, { OklabTarget::OKLAB as u8 }>(
src,
src_stride,
dst,
Expand Down Expand Up @@ -168,7 +199,7 @@ pub fn rgba_to_oklab(
height: u32,
transfer_function: TransferFunction,
) {
channels_to_oklab::<{ ImageConfiguration::Rgba as u8 }>(
channels_to_oklab::<{ ImageConfiguration::Rgba as u8 }, { OklabTarget::OKLAB as u8 }>(
src,
src_stride,
dst,
Expand Down Expand Up @@ -198,7 +229,7 @@ pub fn bgra_to_oklab(
height: u32,
transfer_function: TransferFunction,
) {
channels_to_oklab::<{ ImageConfiguration::Bgra as u8 }>(
channels_to_oklab::<{ ImageConfiguration::Bgra as u8 }, { OklabTarget::OKLAB as u8 }>(
src,
src_stride,
dst,
Expand Down Expand Up @@ -228,7 +259,127 @@ pub fn bgr_to_oklab(
height: u32,
transfer_function: TransferFunction,
) {
channels_to_oklab::<{ ImageConfiguration::Bgr as u8 }>(
channels_to_oklab::<{ ImageConfiguration::Bgr as u8 }, { OklabTarget::OKLAB as u8 }>(
src,
src_stride,
dst,
dst_stride,
width,
height,
transfer_function,
);
}

/// This function converts RGB to Oklch 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 LCH(a) data
/// * `dst_stride` - Bytes per row for dst data
/// * `transfer_function` - transfer function to linear colorspace
pub fn rgb_to_oklch(
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 }, { OklabTarget::OKLCH as u8 }>(
src,
src_stride,
dst,
dst_stride,
width,
height,
transfer_function,
);
}

/// This function converts RGBA to Oklch 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 LCH(a) data
/// * `dst_stride` - Bytes per row for dst data
/// * `transfer_function` - transfer function to linear colorspace
pub fn rgba_to_oklch(
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 }, { OklabTarget::OKLCH as u8 }>(
src,
src_stride,
dst,
dst_stride,
width,
height,
transfer_function,
);
}

/// This function converts BGRA to Oklch 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 LCH(a) data
/// * `dst_stride` - Bytes per row for dst data
/// * `transfer_function` - transfer function to linear colorspace
pub fn bgra_to_oklch(
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 }, { OklabTarget::OKLCH as u8 }>(
src,
src_stride,
dst,
dst_stride,
width,
height,
transfer_function,
);
}

/// This function converts BGR to Oklch 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 LCH(a) data
/// * `dst_stride` - Bytes per row for dst data
/// * `transfer_function` - transfer function to linear colorspace
pub fn bgr_to_oklch(
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 }, { OklabTarget::OKLCH as u8 }>(
src,
src_stride,
dst,
Expand Down
9 changes: 9 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ mod luv;
mod neon;
mod oklab;
mod oklab_to_image;
mod oklch;
pub mod planar_to_linear;
mod rgb;
mod rgb_expand;
Expand Down Expand Up @@ -134,9 +135,13 @@ pub use image_to_jzazbz::rgb_to_jzczhz;
pub use image_to_jzazbz::rgba_to_jzazbz;
pub use image_to_jzazbz::rgba_to_jzczhz;
pub use image_to_oklab::bgr_to_oklab;
pub use image_to_oklab::bgr_to_oklch;
pub use image_to_oklab::bgra_to_oklab;
pub use image_to_oklab::bgra_to_oklch;
pub use image_to_oklab::rgb_to_oklab;
pub use image_to_oklab::rgb_to_oklch;
pub use image_to_oklab::rgba_to_oklab;
pub use image_to_oklab::rgba_to_oklch;
pub use image_to_sigmoidal::bgra_to_sigmoidal;
pub use image_to_sigmoidal::rgb_to_sigmoidal;
pub use image_to_sigmoidal::rgba_to_sigmoidal;
Expand All @@ -155,6 +160,10 @@ 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 oklab_to_image::oklch_to_bgr;
pub use oklab_to_image::oklch_to_bgra;
pub use oklab_to_image::oklch_to_rgb;
pub use oklab_to_image::oklch_to_rgba;
pub use rgb_expand::*;
pub use sigmoidal::Sigmoidal;
pub use sigmoidal_to_image::sigmoidal_to_bgra;
Expand Down
Loading

0 comments on commit 9f9dee2

Please sign in to comment.