yuvutils_rs/
rgb_to_y.rs

1/*
2 * Copyright (c) Radzivon Bartoshyk, 10/2024. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without modification,
5 * are permitted provided that the following conditions are met:
6 *
7 * 1.  Redistributions of source code must retain the above copyright notice, this
8 * list of conditions and the following disclaimer.
9 *
10 * 2.  Redistributions in binary form must reproduce the above copyright notice,
11 * this list of conditions and the following disclaimer in the documentation
12 * and/or other materials provided with the distribution.
13 *
14 * 3.  Neither the name of the copyright holder nor the names of its
15 * contributors may be used to endorse or promote products derived from
16 * this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
25 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
26 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29#[cfg(all(
30    any(target_arch = "x86", target_arch = "x86_64"),
31    feature = "nightly_avx512"
32))]
33use crate::avx512bw::avx512_row_rgb_to_y;
34use crate::images::YuvGrayImageMut;
35#[cfg(all(target_arch = "aarch64", target_feature = "neon"))]
36use crate::neon::neon_rgb_to_y_row;
37use crate::yuv_error::check_rgba_destination;
38use crate::yuv_support::*;
39use crate::YuvError;
40#[cfg(feature = "rayon")]
41use rayon::iter::{IndexedParallelIterator, ParallelIterator};
42#[cfg(feature = "rayon")]
43use rayon::prelude::{ParallelSlice, ParallelSliceMut};
44
45// Chroma subsampling always assumed as YUV 400
46fn rgbx_to_y<const ORIGIN_CHANNELS: u8>(
47    gray_image: &mut YuvGrayImageMut<u8>,
48    rgba: &[u8],
49    rgba_stride: u32,
50    range: YuvRange,
51    matrix: YuvStandardMatrix,
52) -> Result<(), YuvError> {
53    let source_channels: YuvSourceChannels = ORIGIN_CHANNELS.into();
54    let channels = source_channels.get_channels_count();
55
56    check_rgba_destination(
57        rgba,
58        rgba_stride,
59        gray_image.width,
60        gray_image.height,
61        channels,
62    )?;
63    gray_image.check_constraints()?;
64
65    let chroma_range = get_yuv_range(8, range);
66    let kr_kb = matrix.get_kr_kb();
67    const PRECISION: i32 = 13;
68
69    let transform = search_forward_transform(PRECISION, 8, range, matrix, chroma_range, kr_kb);
70
71    const ROUNDING_CONST_BIAS: i32 = (1 << (PRECISION - 1)) - 1;
72    let bias_y = chroma_range.bias_y as i32 * (1 << PRECISION) + ROUNDING_CONST_BIAS;
73
74    #[cfg(all(any(target_arch = "x86", target_arch = "x86_64"), feature = "sse"))]
75    let use_sse = std::arch::is_x86_feature_detected!("sse4.1");
76    #[cfg(all(any(target_arch = "x86", target_arch = "x86_64"), feature = "avx"))]
77    let use_avx = std::arch::is_x86_feature_detected!("avx2");
78    #[cfg(all(
79        any(target_arch = "x86", target_arch = "x86_64"),
80        feature = "nightly_avx512"
81    ))]
82    let use_avx512 = std::arch::is_x86_feature_detected!("avx512bw");
83    #[cfg(all(
84        any(target_arch = "x86", target_arch = "x86_64"),
85        feature = "nightly_avx512"
86    ))]
87    let use_vbmi = std::arch::is_x86_feature_detected!("avx512vbmi");
88    #[cfg(all(
89        any(target_arch = "x86", target_arch = "x86_64"),
90        feature = "nightly_avx512"
91    ))]
92    let avx512_dispatch = if use_vbmi {
93        avx512_row_rgb_to_y::<ORIGIN_CHANNELS, true>
94    } else {
95        avx512_row_rgb_to_y::<ORIGIN_CHANNELS, false>
96    };
97    #[cfg(all(target_arch = "aarch64", target_feature = "neon"))]
98    let neon_handler = if std::arch::is_aarch64_feature_detected!("rdm") {
99        #[cfg(feature = "rdm")]
100        {
101            use crate::neon::neon_rgb_to_y_rdm;
102            neon_rgb_to_y_rdm::<ORIGIN_CHANNELS>
103        }
104        #[cfg(not(feature = "rdm"))]
105        {
106            neon_rgb_to_y_row::<ORIGIN_CHANNELS, PRECISION>
107        }
108    } else {
109        neon_rgb_to_y_row::<ORIGIN_CHANNELS, PRECISION>
110    };
111
112    let iter;
113    #[cfg(feature = "rayon")]
114    {
115        iter = gray_image
116            .y_plane
117            .borrow_mut()
118            .par_chunks_exact_mut(gray_image.y_stride as usize)
119            .zip(rgba.par_chunks_exact(rgba_stride as usize));
120    }
121    #[cfg(not(feature = "rayon"))]
122    {
123        iter = gray_image
124            .y_plane
125            .borrow_mut()
126            .chunks_exact_mut(gray_image.y_stride as usize)
127            .zip(rgba.chunks_exact(rgba_stride as usize));
128    }
129
130    iter.for_each(|(y_plane, rgba)| {
131        let mut _cx = 0usize;
132
133        let y_plane = &mut y_plane[0..gray_image.width as usize];
134
135        #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
136        {
137            #[cfg(feature = "nightly_avx512")]
138            if use_avx512 {
139                let processed_offset = avx512_dispatch(
140                    &transform,
141                    &chroma_range,
142                    y_plane,
143                    rgba,
144                    _cx,
145                    gray_image.width as usize,
146                );
147                _cx = processed_offset;
148            }
149            #[cfg(feature = "avx")]
150            if use_avx {
151                use crate::avx2::avx2_rgb_to_y_row;
152                let processed_offset = avx2_rgb_to_y_row::<ORIGIN_CHANNELS, PRECISION>(
153                    &transform,
154                    &chroma_range,
155                    y_plane,
156                    rgba,
157                    _cx,
158                    gray_image.width as usize,
159                );
160                _cx = processed_offset;
161            }
162            #[cfg(feature = "sse")]
163            if use_sse {
164                use crate::sse::sse_rgb_to_y;
165                let processed_offset = sse_rgb_to_y::<ORIGIN_CHANNELS, PRECISION>(
166                    &transform,
167                    &chroma_range,
168                    y_plane,
169                    rgba,
170                    _cx,
171                    gray_image.width as usize,
172                );
173                _cx = processed_offset;
174            }
175        }
176
177        #[cfg(all(target_arch = "aarch64", target_feature = "neon"))]
178        unsafe {
179            _cx = neon_handler(
180                &transform,
181                &chroma_range,
182                y_plane.as_mut_ptr(),
183                rgba,
184                _cx,
185                gray_image.width as usize,
186            );
187        }
188
189        for (y_dst, rgba) in y_plane
190            .iter_mut()
191            .zip(rgba.chunks_exact(channels))
192            .skip(_cx)
193        {
194            let r = rgba[source_channels.get_r_channel_offset()] as i32;
195            let g = rgba[source_channels.get_g_channel_offset()] as i32;
196            let b = rgba[source_channels.get_b_channel_offset()] as i32;
197            let y = (r * transform.yr + g * transform.yg + b * transform.yb + bias_y) >> PRECISION;
198            *y_dst = y as u8;
199        }
200    });
201
202    Ok(())
203}
204
205/// Convert RGB image data to YUV 400 planar format.
206///
207/// This function performs RGB to YUV conversion and stores the result in YUV400 planar format,
208/// with Y (luminance) plane
209///
210/// # Arguments
211///
212/// * `gray_image` - Target gray image.
213/// * `rgb` - The input RGB image data slice.
214/// * `rgb_stride` - The stride (elements per row) for the RGB image data.
215/// * `range` - The YUV range (limited or full).
216/// * `matrix` - The YUV standard matrix (BT.601 or BT.709 or BT.2020 or other).
217///
218/// # Panics
219///
220/// This function panics if the lengths of the planes or the input RGB data are not valid based
221/// on the specified width, height, and strides, or if invalid YUV range or matrix is provided.
222///
223pub fn rgb_to_yuv400(
224    gray_image: &mut YuvGrayImageMut<u8>,
225    rgb: &[u8],
226    rgb_stride: u32,
227    range: YuvRange,
228    matrix: YuvStandardMatrix,
229) -> Result<(), YuvError> {
230    rgbx_to_y::<{ YuvSourceChannels::Rgb as u8 }>(gray_image, rgb, rgb_stride, range, matrix)
231}
232
233/// Convert RGBA image data to YUV 400 planar format.
234///
235/// This function performs RGBA to YUV conversion and stores the result in YUV400 planar format,
236/// with Y (luminance) plane
237///
238/// # Arguments
239///
240/// * `gray_image` - Target gray image.
241/// * `rgba` - The input RGBA image data slice.
242/// * `rgba_stride` - The stride (components per row) for the RGBA image data.
243/// * `range` - The YUV range (limited or full).
244/// * `matrix` - The YUV standard matrix (BT.601 or BT.709 or BT.2020 or other).
245///
246/// # Panics
247///
248/// This function panics if the lengths of the planes or the input RGBA data are not valid based
249/// on the specified width, height, and strides, or if invalid YUV range or matrix is provided.
250///
251pub fn rgba_to_yuv400(
252    gray_image: &mut YuvGrayImageMut<u8>,
253    rgba: &[u8],
254    rgba_stride: u32,
255    range: YuvRange,
256    matrix: YuvStandardMatrix,
257) -> Result<(), YuvError> {
258    rgbx_to_y::<{ YuvSourceChannels::Rgba as u8 }>(gray_image, rgba, rgba_stride, range, matrix)
259}
260
261/// Convert BGRA image data to YUV 400 planar format.
262///
263/// This function performs BGRA to YUV conversion and stores the result in YUV420 planar format,
264/// with Y (luminance) plane
265///
266/// # Arguments
267///
268/// * `gray_image` - Target gray image.
269/// * `bgra` - The input BGRA image data slice.
270/// * `bgra_stride` - The stride (components per row) for the BGRA image data.
271/// * `range` - The YUV range (limited or full).
272/// * `matrix` - The YUV standard matrix (BT.601 or BT.709 or BT.2020 or other).
273///
274/// # Panics
275///
276/// This function panics if the lengths of the planes or the input BGRA data are not valid based
277/// on the specified width, height, and strides, or if invalid YUV range or matrix is provided.
278///
279pub fn bgra_to_yuv400(
280    gray_image: &mut YuvGrayImageMut<u8>,
281    bgra: &[u8],
282    bgra_stride: u32,
283    range: YuvRange,
284    matrix: YuvStandardMatrix,
285) -> Result<(), YuvError> {
286    rgbx_to_y::<{ YuvSourceChannels::Bgra as u8 }>(gray_image, bgra, bgra_stride, range, matrix)
287}
288
289/// Convert BGR image data to YUV 400 planar format.
290///
291/// This function performs BGR to YUV conversion and stores the result in YUV400 planar format,
292/// with Y (luminance) plane
293///
294/// # Arguments
295///
296/// * `gray_image` - Target gray image.
297/// * `bgr` - The input BGR image data slice.
298/// * `bgr_stride` - The stride (components per row) for the RGB image data.
299/// * `range` - The YUV range (limited or full).
300/// * `matrix` - The YUV standard matrix (BT.601 or BT.709 or BT.2020 or other).
301///
302/// # Panics
303///
304/// This function panics if the lengths of the planes or the input RGB data are not valid based
305/// on the specified width, height, and strides, or if invalid YUV range or matrix is provided.
306///
307pub fn bgr_to_yuv400(
308    gray_image: &mut YuvGrayImageMut<u8>,
309    rgb: &[u8],
310    rgb_stride: u32,
311    range: YuvRange,
312    matrix: YuvStandardMatrix,
313) -> Result<(), YuvError> {
314    rgbx_to_y::<{ YuvSourceChannels::Bgr as u8 }>(gray_image, rgb, rgb_stride, range, matrix)
315}