1#[cfg(any(target_arch = "x86_64", target_arch = "x86"))]
8use crate::avx::avx_image_to_oklab;
9use crate::image::ImageConfiguration;
10#[cfg(all(target_arch = "aarch64", target_feature = "neon"))]
11use crate::neon::neon_image_to_oklab;
12use crate::oklch::Oklch;
13#[cfg(any(target_arch = "x86_64", target_arch = "x86"))]
14use crate::sse::sse_image_to_oklab;
15use crate::{
16 bgr_to_linear, bgra_to_linear, rgb_to_linear, rgba_to_linear, Oklab, Rgb, TransferFunction,
17};
18#[cfg(feature = "rayon")]
19use rayon::iter::ParallelIterator;
20#[cfg(feature = "rayon")]
21use rayon::prelude::ParallelSliceMut;
22use std::slice;
23
24#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq)]
25pub(crate) enum OklabTarget {
26 Oklab = 0,
27 Oklch = 1,
28}
29
30impl From<u8> for OklabTarget {
31 fn from(value: u8) -> Self {
32 match value {
33 0 => OklabTarget::Oklab,
34 1 => OklabTarget::Oklch,
35 _ => {
36 panic!("Not implemented")
37 }
38 }
39 }
40}
41
42#[allow(clippy::type_complexity)]
43fn channels_to_oklab<const CHANNELS_CONFIGURATION: u8, const TARGET: u8>(
44 src: &[u8],
45 src_stride: u32,
46 dst: &mut [f32],
47 dst_stride: u32,
48 width: u32,
49 height: u32,
50 transfer_function: TransferFunction,
51) {
52 let target: OklabTarget = TARGET.into();
53 let image_configuration: ImageConfiguration = CHANNELS_CONFIGURATION.into();
54
55 let channels = image_configuration.get_channels_count();
56
57 let callee = match image_configuration {
58 ImageConfiguration::Rgb => rgb_to_linear,
59 ImageConfiguration::Rgba => rgba_to_linear,
60 ImageConfiguration::Bgra => bgra_to_linear,
61 ImageConfiguration::Bgr => bgr_to_linear,
62 };
63
64 callee(
65 src,
66 src_stride,
67 dst,
68 dst_stride,
69 width,
70 height,
71 transfer_function,
72 );
73
74 let mut _wide_row_handle: Option<unsafe fn(usize, u32, *mut f32, usize) -> usize> = None;
75
76 #[cfg(all(target_arch = "aarch64", target_feature = "neon"))]
77 {
78 _wide_row_handle = Some(neon_image_to_oklab::<CHANNELS_CONFIGURATION, TARGET>);
79 }
80
81 #[cfg(any(target_arch = "x86_64", target_arch = "x86"))]
82 if std::arch::is_x86_feature_detected!("sse4.1") {
83 _wide_row_handle = Some(sse_image_to_oklab::<CHANNELS_CONFIGURATION, TARGET>);
84 }
85
86 #[cfg(any(target_arch = "x86_64", target_arch = "x86"))]
87 if std::arch::is_x86_feature_detected!("avx2") {
88 _wide_row_handle = Some(avx_image_to_oklab::<CHANNELS_CONFIGURATION, TARGET>);
89 }
90
91 let dst_slice_safe_align = unsafe {
92 slice::from_raw_parts_mut(
93 dst.as_mut_ptr() as *mut u8,
94 dst_stride as usize * height as usize,
95 )
96 };
97
98 let iter;
99 #[cfg(feature = "rayon")]
100 {
101 iter = dst_slice_safe_align.par_chunks_exact_mut(dst_stride as usize);
102 }
103
104 #[cfg(not(feature = "rayon"))]
105 {
106 iter = dst_slice_safe_align.chunks_exact_mut(dst_stride as usize);
107 }
108
109 iter.for_each(|dst| unsafe {
110 let mut _cx = 0usize;
111
112 let dst_ptr = dst.as_mut_ptr() as *mut f32;
113
114 if let Some(dispatcher) = _wide_row_handle {
115 _cx = dispatcher(_cx, width, dst_ptr, 0)
116 }
117
118 for x in _cx..width as usize {
119 let px = x * channels;
120
121 let src = dst_ptr.add(px);
122 let r = src
123 .add(image_configuration.get_r_channel_offset())
124 .read_unaligned();
125 let g = src
126 .add(image_configuration.get_g_channel_offset())
127 .read_unaligned();
128 let b = src
129 .add(image_configuration.get_b_channel_offset())
130 .read_unaligned();
131
132 let rgb = Rgb::<f32>::new(r, g, b);
133 let dst_store = dst_ptr.add(px);
134
135 match target {
136 OklabTarget::Oklab => {
137 let oklab = Oklab::from_linear_rgb(rgb);
138 dst_store.write_unaligned(oklab.l);
139 dst_store.add(1).write_unaligned(oklab.a);
140 dst_store.add(2).write_unaligned(oklab.b);
141 }
142 OklabTarget::Oklch => {
143 let oklch = Oklch::from_linear_rgb(rgb);
144 dst_store.write_unaligned(oklch.l);
145 dst_store.add(1).write_unaligned(oklch.c);
146 dst_store.add(2).write_unaligned(oklch.h);
147 }
148 }
149
150 if image_configuration.has_alpha() {
151 let a = src
152 .add(image_configuration.get_a_channel_offset())
153 .read_unaligned();
154 dst_store.add(3).write_unaligned(a);
155 }
156 }
157 });
158}
159
160pub fn rgb_to_oklab(
171 src: &[u8],
172 src_stride: u32,
173 dst: &mut [f32],
174 dst_stride: u32,
175 width: u32,
176 height: u32,
177 transfer_function: TransferFunction,
178) {
179 channels_to_oklab::<{ ImageConfiguration::Rgb as u8 }, { OklabTarget::Oklab as u8 }>(
180 src,
181 src_stride,
182 dst,
183 dst_stride,
184 width,
185 height,
186 transfer_function,
187 );
188}
189
190pub fn rgba_to_oklab(
201 src: &[u8],
202 src_stride: u32,
203 dst: &mut [f32],
204 dst_stride: u32,
205 width: u32,
206 height: u32,
207 transfer_function: TransferFunction,
208) {
209 channels_to_oklab::<{ ImageConfiguration::Rgba as u8 }, { OklabTarget::Oklab as u8 }>(
210 src,
211 src_stride,
212 dst,
213 dst_stride,
214 width,
215 height,
216 transfer_function,
217 );
218}
219
220pub fn bgra_to_oklab(
231 src: &[u8],
232 src_stride: u32,
233 dst: &mut [f32],
234 dst_stride: u32,
235 width: u32,
236 height: u32,
237 transfer_function: TransferFunction,
238) {
239 channels_to_oklab::<{ ImageConfiguration::Bgra as u8 }, { OklabTarget::Oklab as u8 }>(
240 src,
241 src_stride,
242 dst,
243 dst_stride,
244 width,
245 height,
246 transfer_function,
247 );
248}
249
250pub fn bgr_to_oklab(
261 src: &[u8],
262 src_stride: u32,
263 dst: &mut [f32],
264 dst_stride: u32,
265 width: u32,
266 height: u32,
267 transfer_function: TransferFunction,
268) {
269 channels_to_oklab::<{ ImageConfiguration::Bgr as u8 }, { OklabTarget::Oklab as u8 }>(
270 src,
271 src_stride,
272 dst,
273 dst_stride,
274 width,
275 height,
276 transfer_function,
277 );
278}
279
280pub fn rgb_to_oklch(
291 src: &[u8],
292 src_stride: u32,
293 dst: &mut [f32],
294 dst_stride: u32,
295 width: u32,
296 height: u32,
297 transfer_function: TransferFunction,
298) {
299 channels_to_oklab::<{ ImageConfiguration::Rgb as u8 }, { OklabTarget::Oklch as u8 }>(
300 src,
301 src_stride,
302 dst,
303 dst_stride,
304 width,
305 height,
306 transfer_function,
307 );
308}
309
310pub fn rgba_to_oklch(
321 src: &[u8],
322 src_stride: u32,
323 dst: &mut [f32],
324 dst_stride: u32,
325 width: u32,
326 height: u32,
327 transfer_function: TransferFunction,
328) {
329 channels_to_oklab::<{ ImageConfiguration::Rgba as u8 }, { OklabTarget::Oklch as u8 }>(
330 src,
331 src_stride,
332 dst,
333 dst_stride,
334 width,
335 height,
336 transfer_function,
337 );
338}
339
340pub fn bgra_to_oklch(
351 src: &[u8],
352 src_stride: u32,
353 dst: &mut [f32],
354 dst_stride: u32,
355 width: u32,
356 height: u32,
357 transfer_function: TransferFunction,
358) {
359 channels_to_oklab::<{ ImageConfiguration::Bgra as u8 }, { OklabTarget::Oklch as u8 }>(
360 src,
361 src_stride,
362 dst,
363 dst_stride,
364 width,
365 height,
366 transfer_function,
367 );
368}
369
370pub fn bgr_to_oklch(
381 src: &[u8],
382 src_stride: u32,
383 dst: &mut [f32],
384 dst_stride: u32,
385 width: u32,
386 height: u32,
387 transfer_function: TransferFunction,
388) {
389 channels_to_oklab::<{ ImageConfiguration::Bgr as u8 }, { OklabTarget::Oklch as u8 }>(
390 src,
391 src_stride,
392 dst,
393 dst_stride,
394 width,
395 height,
396 transfer_function,
397 );
398}