Skip to content

Commit 9f9dee2

Browse files
committed
Added OkLch
1 parent a30ea28 commit 9f9dee2

File tree

13 files changed

+600
-186
lines changed

13 files changed

+600
-186
lines changed

Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ workspace = { members = ["src/app"] }
22

33
[package]
44
name = "colorutils-rs"
5-
version = "0.5.2"
5+
version = "0.5.3"
66
edition = "2021"
77
description = "High performance utilities for color format handling and conversion."
88
readme = "README.md"

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ Allows conversion between
1111
- [x] XYZ
1212
- [x] Sigmoidal
1313
- [x] Oklab
14+
- [x] Oklch
1415
- [x] Jzazbz
1516
- [x] Jzczhz
1617

src/app/src/main.rs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -66,14 +66,13 @@ fn main() {
6666
lab_store.resize(width as usize * components * height as usize, 0f32);
6767
let src_stride = width * components as u32;
6868
let start_time = Instant::now();
69-
rgb_to_jzazbz(
69+
rgb_to_oklch(
7070
src_bytes,
7171
src_stride,
7272
&mut lab_store,
7373
store_stride as u32,
7474
width,
7575
height,
76-
200f32,
7776
TransferFunction::Srgb,
7877
);
7978
let elapsed_time = start_time.elapsed();
@@ -102,14 +101,13 @@ fn main() {
102101
// }
103102

104103
let start_time = Instant::now();
105-
jzazbz_to_rgb(
104+
oklch_to_rgb(
106105
&lab_store,
107106
store_stride as u32,
108107
&mut dst_slice,
109108
src_stride,
110109
width,
111110
height,
112-
200f32,
113111
TransferFunction::Srgb,
114112
);
115113

src/image_to_oklab.rs

Lines changed: 164 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,34 @@ use crate::image::ImageConfiguration;
1010
target_feature = "neon"
1111
))]
1212
use crate::neon::neon_image_to_oklab;
13+
use crate::oklch::Oklch;
1314
#[cfg(all(
1415
any(target_arch = "x86_64", target_arch = "x86"),
1516
target_feature = "sse4.1"
1617
))]
1718
use crate::sse::sse_image_to_oklab;
1819
use crate::{Oklab, Rgb, TransferFunction};
1920

21+
#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq)]
22+
pub(crate) enum OklabTarget {
23+
OKLAB = 0,
24+
OKLCH = 1,
25+
}
26+
27+
impl From<u8> for OklabTarget {
28+
fn from(value: u8) -> Self {
29+
match value {
30+
0 => OklabTarget::OKLAB,
31+
1 => OklabTarget::OKLCH,
32+
_ => {
33+
panic!("Not implemented")
34+
}
35+
}
36+
}
37+
}
38+
2039
#[inline(always)]
21-
fn channels_to_oklab<const CHANNELS_CONFIGURATION: u8>(
40+
fn channels_to_oklab<const CHANNELS_CONFIGURATION: u8, const TARGET: u8>(
2241
src: &[u8],
2342
src_stride: u32,
2443
dst: &mut [f32],
@@ -27,6 +46,7 @@ fn channels_to_oklab<const CHANNELS_CONFIGURATION: u8>(
2746
height: u32,
2847
transfer_function: TransferFunction,
2948
) {
49+
let target: OklabTarget = TARGET.into();
3050
let image_configuration: ImageConfiguration = CHANNELS_CONFIGURATION.into();
3151

3252
let channels = image_configuration.get_channels_count();
@@ -40,15 +60,15 @@ fn channels_to_oklab<const CHANNELS_CONFIGURATION: u8>(
4060
target_feature = "neon"
4161
))]
4262
{
43-
_wide_row_handle = Some(neon_image_to_oklab::<CHANNELS_CONFIGURATION>);
63+
_wide_row_handle = Some(neon_image_to_oklab::<CHANNELS_CONFIGURATION, TARGET>);
4464
}
4565

4666
#[cfg(all(
4767
any(target_arch = "x86_64", target_arch = "x86"),
4868
target_feature = "sse4.1"
4969
))]
5070
if is_x86_feature_detected!("sse4.1") {
51-
_wide_row_handle = Some(sse_image_to_oklab::<CHANNELS_CONFIGURATION>);
71+
_wide_row_handle = Some(sse_image_to_oklab::<CHANNELS_CONFIGURATION, TARGET>);
5272
}
5373

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

94114
let rgb = Rgb::<u8>::new(r, g, b);
95-
let oklab = Oklab::from_rgb(rgb, transfer_function);
96-
97115
let dst_store = unsafe { dst_ptr.add(px) };
98116

99-
unsafe {
100-
dst_store.write_unaligned(oklab.l);
101-
dst_store.add(1).write_unaligned(oklab.a);
102-
dst_store.add(2).write_unaligned(oklab.b);
117+
match target {
118+
OklabTarget::OKLAB => {
119+
let oklab = Oklab::from_rgb(rgb, transfer_function);
120+
unsafe {
121+
dst_store.write_unaligned(oklab.l);
122+
dst_store.add(1).write_unaligned(oklab.a);
123+
dst_store.add(2).write_unaligned(oklab.b);
124+
}
125+
}
126+
OklabTarget::OKLCH => {
127+
let oklch = Oklch::from_rgb(rgb, transfer_function);
128+
unsafe {
129+
dst_store.write_unaligned(oklch.l);
130+
dst_store.add(1).write_unaligned(oklch.c);
131+
dst_store.add(2).write_unaligned(oklch.h);
132+
}
133+
}
103134
}
104135

105136
if image_configuration.has_alpha() {
@@ -138,7 +169,7 @@ pub fn rgb_to_oklab(
138169
height: u32,
139170
transfer_function: TransferFunction,
140171
) {
141-
channels_to_oklab::<{ ImageConfiguration::Rgb as u8 }>(
172+
channels_to_oklab::<{ ImageConfiguration::Rgb as u8 }, { OklabTarget::OKLAB as u8 }>(
142173
src,
143174
src_stride,
144175
dst,
@@ -168,7 +199,7 @@ pub fn rgba_to_oklab(
168199
height: u32,
169200
transfer_function: TransferFunction,
170201
) {
171-
channels_to_oklab::<{ ImageConfiguration::Rgba as u8 }>(
202+
channels_to_oklab::<{ ImageConfiguration::Rgba as u8 }, { OklabTarget::OKLAB as u8 }>(
172203
src,
173204
src_stride,
174205
dst,
@@ -198,7 +229,7 @@ pub fn bgra_to_oklab(
198229
height: u32,
199230
transfer_function: TransferFunction,
200231
) {
201-
channels_to_oklab::<{ ImageConfiguration::Bgra as u8 }>(
232+
channels_to_oklab::<{ ImageConfiguration::Bgra as u8 }, { OklabTarget::OKLAB as u8 }>(
202233
src,
203234
src_stride,
204235
dst,
@@ -228,7 +259,127 @@ pub fn bgr_to_oklab(
228259
height: u32,
229260
transfer_function: TransferFunction,
230261
) {
231-
channels_to_oklab::<{ ImageConfiguration::Bgr as u8 }>(
262+
channels_to_oklab::<{ ImageConfiguration::Bgr as u8 }, { OklabTarget::OKLAB as u8 }>(
263+
src,
264+
src_stride,
265+
dst,
266+
dst_stride,
267+
width,
268+
height,
269+
transfer_function,
270+
);
271+
}
272+
273+
/// This function converts RGB to Oklch against D65 white point. This is much more effective than naive direct transformation
274+
///
275+
/// # Arguments
276+
/// * `src` - A slice contains RGB data
277+
/// * `src_stride` - Bytes per row for src data.
278+
/// * `width` - Image width
279+
/// * `height` - Image height
280+
/// * `dst` - A mutable slice to receive LCH(a) data
281+
/// * `dst_stride` - Bytes per row for dst data
282+
/// * `transfer_function` - transfer function to linear colorspace
283+
pub fn rgb_to_oklch(
284+
src: &[u8],
285+
src_stride: u32,
286+
dst: &mut [f32],
287+
dst_stride: u32,
288+
width: u32,
289+
height: u32,
290+
transfer_function: TransferFunction,
291+
) {
292+
channels_to_oklab::<{ ImageConfiguration::Rgb as u8 }, { OklabTarget::OKLCH as u8 }>(
293+
src,
294+
src_stride,
295+
dst,
296+
dst_stride,
297+
width,
298+
height,
299+
transfer_function,
300+
);
301+
}
302+
303+
/// 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
304+
///
305+
/// # Arguments
306+
/// * `src` - A slice contains RGBA data
307+
/// * `src_stride` - Bytes per row for src data.
308+
/// * `width` - Image width
309+
/// * `height` - Image height
310+
/// * `dst` - A mutable slice to receive LCH(a) data
311+
/// * `dst_stride` - Bytes per row for dst data
312+
/// * `transfer_function` - transfer function to linear colorspace
313+
pub fn rgba_to_oklch(
314+
src: &[u8],
315+
src_stride: u32,
316+
dst: &mut [f32],
317+
dst_stride: u32,
318+
width: u32,
319+
height: u32,
320+
transfer_function: TransferFunction,
321+
) {
322+
channels_to_oklab::<{ ImageConfiguration::Rgba as u8 }, { OklabTarget::OKLCH as u8 }>(
323+
src,
324+
src_stride,
325+
dst,
326+
dst_stride,
327+
width,
328+
height,
329+
transfer_function,
330+
);
331+
}
332+
333+
/// 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
334+
///
335+
/// # Arguments
336+
/// * `src` - A slice contains BGRA data
337+
/// * `src_stride` - Bytes per row for src data.
338+
/// * `width` - Image width
339+
/// * `height` - Image height
340+
/// * `dst` - A mutable slice to receive LCH(a) data
341+
/// * `dst_stride` - Bytes per row for dst data
342+
/// * `transfer_function` - transfer function to linear colorspace
343+
pub fn bgra_to_oklch(
344+
src: &[u8],
345+
src_stride: u32,
346+
dst: &mut [f32],
347+
dst_stride: u32,
348+
width: u32,
349+
height: u32,
350+
transfer_function: TransferFunction,
351+
) {
352+
channels_to_oklab::<{ ImageConfiguration::Bgra as u8 }, { OklabTarget::OKLCH as u8 }>(
353+
src,
354+
src_stride,
355+
dst,
356+
dst_stride,
357+
width,
358+
height,
359+
transfer_function,
360+
);
361+
}
362+
363+
/// This function converts BGR to Oklch against D65 white point. This is much more effective than naive direct transformation
364+
///
365+
/// # Arguments
366+
/// * `src` - A slice contains BGR data
367+
/// * `src_stride` - Bytes per row for src data.
368+
/// * `width` - Image width
369+
/// * `height` - Image height
370+
/// * `dst` - A mutable slice to receive LCH(a) data
371+
/// * `dst_stride` - Bytes per row for dst data
372+
/// * `transfer_function` - transfer function to linear colorspace
373+
pub fn bgr_to_oklch(
374+
src: &[u8],
375+
src_stride: u32,
376+
dst: &mut [f32],
377+
dst_stride: u32,
378+
width: u32,
379+
height: u32,
380+
transfer_function: TransferFunction,
381+
) {
382+
channels_to_oklab::<{ ImageConfiguration::Bgr as u8 }, { OklabTarget::OKLCH as u8 }>(
232383
src,
233384
src_stride,
234385
dst,

src/lib.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ mod luv;
4141
mod neon;
4242
mod oklab;
4343
mod oklab_to_image;
44+
mod oklch;
4445
pub mod planar_to_linear;
4546
mod rgb;
4647
mod rgb_expand;
@@ -134,9 +135,13 @@ pub use image_to_jzazbz::rgb_to_jzczhz;
134135
pub use image_to_jzazbz::rgba_to_jzazbz;
135136
pub use image_to_jzazbz::rgba_to_jzczhz;
136137
pub use image_to_oklab::bgr_to_oklab;
138+
pub use image_to_oklab::bgr_to_oklch;
137139
pub use image_to_oklab::bgra_to_oklab;
140+
pub use image_to_oklab::bgra_to_oklch;
138141
pub use image_to_oklab::rgb_to_oklab;
142+
pub use image_to_oklab::rgb_to_oklch;
139143
pub use image_to_oklab::rgba_to_oklab;
144+
pub use image_to_oklab::rgba_to_oklch;
140145
pub use image_to_sigmoidal::bgra_to_sigmoidal;
141146
pub use image_to_sigmoidal::rgb_to_sigmoidal;
142147
pub use image_to_sigmoidal::rgba_to_sigmoidal;
@@ -155,6 +160,10 @@ pub use oklab_to_image::oklab_to_bgr;
155160
pub use oklab_to_image::oklab_to_bgra;
156161
pub use oklab_to_image::oklab_to_rgb;
157162
pub use oklab_to_image::oklab_to_rgba;
163+
pub use oklab_to_image::oklch_to_bgr;
164+
pub use oklab_to_image::oklch_to_bgra;
165+
pub use oklab_to_image::oklch_to_rgb;
166+
pub use oklab_to_image::oklch_to_rgba;
158167
pub use rgb_expand::*;
159168
pub use sigmoidal::Sigmoidal;
160169
pub use sigmoidal_to_image::sigmoidal_to_bgra;

0 commit comments

Comments
 (0)