colorutils_rs/
image_to_linear.rs

1/*
2 * // Copyright 2024 (c) the Radzivon Bartoshyk. All rights reserved.
3 * //
4 * // Use of this source code is governed by a BSD-style
5 * // license that can be found in the LICENSE file.
6 */
7use crate::gamma_curves::TransferFunction;
8use crate::image::ImageConfiguration;
9use crate::Rgb;
10#[cfg(feature = "rayon")]
11use rayon::iter::{IndexedParallelIterator, ParallelIterator};
12#[cfg(feature = "rayon")]
13use rayon::prelude::{ParallelSlice, ParallelSliceMut};
14use std::slice;
15
16#[allow(clippy::type_complexity)]
17fn channels_to_linear<const CHANNELS_CONFIGURATION: u8, const USE_ALPHA: bool>(
18    src: &[u8],
19    src_stride: u32,
20    dst: &mut [f32],
21    dst_stride: u32,
22    width: u32,
23    height: u32,
24    transfer_function: TransferFunction,
25) {
26    let image_configuration: ImageConfiguration = CHANNELS_CONFIGURATION.into();
27    if USE_ALPHA && !image_configuration.has_alpha() {
28        panic!("Alpha may be set only on images with alpha");
29    }
30
31    let channels = image_configuration.get_channels_count();
32
33    let mut lut_table = vec![0f32; 256];
34    for (i, lut) in lut_table.iter_mut().enumerate() {
35        *lut = transfer_function.linearize(i as f32 * (1. / 255.0));
36    }
37
38    let dst_slice_safe_align = unsafe {
39        slice::from_raw_parts_mut(
40            dst.as_mut_ptr() as *mut u8,
41            dst_stride as usize * height as usize,
42        )
43    };
44
45    let iter;
46
47    #[cfg(feature = "rayon")]
48    {
49        iter = dst_slice_safe_align
50            .par_chunks_exact_mut(dst_stride as usize)
51            .zip(src.par_chunks_exact(src_stride as usize));
52    }
53
54    #[cfg(not(feature = "rayon"))]
55    {
56        iter = dst_slice_safe_align
57            .chunks_exact_mut(dst_stride as usize)
58            .zip(src.chunks_exact(src_stride as usize));
59    }
60
61    iter.for_each(|(dst_row, src_row)| unsafe {
62        let mut _cx = 0usize;
63
64        let src_ptr = src_row.as_ptr();
65        let dst_ptr = dst_row.as_mut_ptr() as *mut f32;
66
67        for x in _cx..width as usize {
68            let px = x * channels;
69            let dst = dst_ptr.add(px);
70            let src = src_ptr.add(px);
71            let r = src
72                .add(image_configuration.get_r_channel_offset())
73                .read_unaligned();
74            let g = src
75                .add(image_configuration.get_g_channel_offset())
76                .read_unaligned();
77            let b = src
78                .add(image_configuration.get_b_channel_offset())
79                .read_unaligned();
80
81            let rgb = Rgb::<u8>::new(r, g, b);
82
83            dst.add(image_configuration.get_r_channel_offset())
84                .write_unaligned(*lut_table.get_unchecked(rgb.r as usize));
85            dst.add(image_configuration.get_g_channel_offset())
86                .write_unaligned(*lut_table.get_unchecked(rgb.g as usize));
87            dst.add(image_configuration.get_b_channel_offset())
88                .write_unaligned(*lut_table.get_unchecked(rgb.b as usize));
89
90            if USE_ALPHA && image_configuration.has_alpha() {
91                let a = src
92                    .add(image_configuration.get_a_channel_offset())
93                    .read_unaligned();
94                let a_lin = a as f32 * (1f32 / 255f32);
95                dst.add(image_configuration.get_a_channel_offset())
96                    .write_unaligned(a_lin);
97            }
98        }
99    });
100}
101
102/// This function converts RGB to linear colorspace
103///
104/// This function converts RGB to linear color space. This is much more effective than naive direct transformation
105///
106/// # Arguments
107/// * `src` - A slice contains RGB data
108/// * `src_stride` - Bytes per row for src data.
109/// * `width` - Image width
110/// * `height` - Image height
111/// * `dst` - A mutable slice to receive linear data
112/// * `dst_stride` - Bytes per row for dst data
113/// * `transfer_function` - Transfer function from gamma to linear space. If you don't have specific pick `Srgb`
114pub fn rgb_to_linear(
115    src: &[u8],
116    src_stride: u32,
117    dst: &mut [f32],
118    dst_stride: u32,
119    width: u32,
120    height: u32,
121    transfer_function: TransferFunction,
122) {
123    channels_to_linear::<{ ImageConfiguration::Rgb as u8 }, false>(
124        src,
125        src_stride,
126        dst,
127        dst_stride,
128        width,
129        height,
130        transfer_function,
131    );
132}
133
134/// This function converts RGBA to liner color space
135///
136/// This function converts RGBA to Linear, Alpha channel is normalized. This is much more effective than naive direct transformation
137///
138/// # Arguments
139/// * `src` - A slice contains RGBA data
140/// * `src_stride` - Bytes per row for src data.
141/// * `width` - Image width
142/// * `height` - Image height
143/// * `dst` - A mutable slice to receive Linear data
144/// * `dst_stride` - Bytes per row for dst data
145/// * `transfer_function` - Transfer function from gamma to linear space. If you don't have specific pick `Srgb`
146pub fn rgba_to_linear(
147    src: &[u8],
148    src_stride: u32,
149    dst: &mut [f32],
150    dst_stride: u32,
151    width: u32,
152    height: u32,
153    transfer_function: TransferFunction,
154) {
155    channels_to_linear::<{ ImageConfiguration::Rgba as u8 }, true>(
156        src,
157        src_stride,
158        dst,
159        dst_stride,
160        width,
161        height,
162        transfer_function,
163    );
164}
165
166/// This function converts BGRA to Linear.
167///
168/// This function converts BGRA to Linear, alpha channel is normalized. This is much more effective than naive direct transformation
169///
170/// # Arguments
171/// * `src` - A slice contains BGRA data
172/// * `src_stride` - Bytes per row for src data.
173/// * `width` - Image width
174/// * `height` - Image height
175/// * `dst` - A mutable slice to receive linear data
176/// * `dst_stride` - Bytes per row for dst data
177/// * `transfer_function` - Transfer function from gamma to linear space. If you don't have specific pick `Srgb`
178pub fn bgra_to_linear(
179    src: &[u8],
180    src_stride: u32,
181    dst: &mut [f32],
182    dst_stride: u32,
183    width: u32,
184    height: u32,
185    transfer_function: TransferFunction,
186) {
187    channels_to_linear::<{ ImageConfiguration::Bgra as u8 }, true>(
188        src,
189        src_stride,
190        dst,
191        dst_stride,
192        width,
193        height,
194        transfer_function,
195    );
196}
197
198/// This function converts BGR to linear
199///
200/// This function converts BGR to linear color space. This is much more effective than naive direct transformation
201///
202/// # Arguments
203/// * `src` - A slice contains BGR data
204/// * `src_stride` - Bytes per row for src data.
205/// * `width` - Image width
206/// * `height` - Image height
207/// * `dst` - A mutable slice to receive Linear data
208/// * `dst_stride` - Bytes per row for dst data
209/// * `transfer_function` - Transfer function from gamma to linear space. If you don't have specific pick `Srgb`
210pub fn bgr_to_linear(
211    src: &[u8],
212    src_stride: u32,
213    dst: &mut [f32],
214    dst_stride: u32,
215    width: u32,
216    height: u32,
217    transfer_function: TransferFunction,
218) {
219    channels_to_linear::<{ ImageConfiguration::Bgr as u8 }, false>(
220        src,
221        src_stride,
222        dst,
223        dst_stride,
224        width,
225        height,
226        transfer_function,
227    );
228}