yuvutils_rs/
from_identity_alpha.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#![forbid(unsafe_code)]
30use crate::numerics::qrshr;
31use crate::yuv_error::check_rgba_destination;
32use crate::yuv_support::{get_yuv_range, YuvSourceChannels};
33use crate::{YuvChromaSubsampling, YuvError, YuvPlanarImageWithAlpha, YuvRange};
34use num_traits::AsPrimitive;
35#[cfg(feature = "rayon")]
36use rayon::iter::{IndexedParallelIterator, ParallelIterator};
37#[cfg(feature = "rayon")]
38use rayon::prelude::{ParallelSlice, ParallelSliceMut};
39use std::fmt::Debug;
40use std::mem::size_of;
41use std::ops::Sub;
42
43#[inline]
44fn gbr_to_rgbx_alpha_impl<
45    V: Copy + AsPrimitive<J> + 'static + Sized + Debug + Send + Sync,
46    J: Copy + Sub<Output = J> + AsPrimitive<i32> + Send + Sync,
47    const CHANNELS: u8,
48    const BIT_DEPTH: usize,
49>(
50    image: &YuvPlanarImageWithAlpha<V>,
51    rgba: &mut [V],
52    rgba_stride: u32,
53    yuv_range: YuvRange,
54) -> Result<(), YuvError>
55where
56    i32: AsPrimitive<V>,
57    u32: AsPrimitive<J>,
58{
59    let destination_channels: YuvSourceChannels = CHANNELS.into();
60    let channels = destination_channels.get_channels_count();
61    assert_eq!(
62        channels, 4,
63        "GBRA -> RGBA is implemented only on 4 channels"
64    );
65    assert!(
66        (8..=16).contains(&BIT_DEPTH),
67        "Invalid bit depth is provided"
68    );
69    assert!(
70        if BIT_DEPTH > 8 {
71            size_of::<V>() == 2
72        } else {
73            size_of::<V>() == 1
74        },
75        "Unsupported bit depth and data type combination"
76    );
77    let y_plane = image.y_plane;
78    let u_plane = image.u_plane;
79    let v_plane = image.v_plane;
80    let y_stride = image.y_stride as usize;
81    let u_stride = image.u_stride as usize;
82    let v_stride = image.v_stride as usize;
83    let height = image.height;
84
85    image.check_constraints(YuvChromaSubsampling::Yuv444)?;
86    check_rgba_destination(rgba, rgba_stride, image.width, height, channels)?;
87
88    let y_iter;
89    let rgb_iter;
90    let u_iter;
91    let v_iter;
92    let a_iter;
93
94    #[cfg(feature = "rayon")]
95    {
96        y_iter = y_plane.par_chunks_exact(y_stride);
97        rgb_iter = rgba.par_chunks_exact_mut(rgba_stride as usize);
98        u_iter = u_plane.par_chunks_exact(u_stride);
99        v_iter = v_plane.par_chunks_exact(v_stride);
100        a_iter = image.a_plane.par_chunks_exact(image.a_stride as usize);
101    }
102    #[cfg(not(feature = "rayon"))]
103    {
104        y_iter = y_plane.chunks_exact(y_stride);
105        rgb_iter = rgba.chunks_exact_mut(rgba_stride as usize);
106        u_iter = u_plane.chunks_exact(u_stride);
107        v_iter = v_plane.chunks_exact(v_stride);
108        a_iter = image.a_plane.chunks_exact(image.a_stride as usize);
109    }
110
111    match yuv_range {
112        YuvRange::Limited => {
113            const PRECISION: i32 = 13;
114            // All channels on identity should use Y range
115            let range = get_yuv_range(BIT_DEPTH as u32, yuv_range);
116            let range_rgba = (1 << BIT_DEPTH) - 1;
117            let y_coef = ((range_rgba as f32 / range.range_y as f32) * (1 << PRECISION) as f32)
118                .round() as i32;
119            let y_bias = range.bias_y.as_();
120
121            let iter = y_iter.zip(u_iter).zip(v_iter).zip(rgb_iter).zip(a_iter);
122            iter.for_each(|((((y_src, u_src), v_src), rgb), a_src)| {
123                let y_src = &y_src[0..image.width as usize];
124                let rgb_chunks = rgb.chunks_exact_mut(channels);
125
126                for ((((&y_src, &u_src), &v_src), rgb_dst), &a_src) in y_src
127                    .iter()
128                    .zip(u_src)
129                    .zip(v_src)
130                    .zip(rgb_chunks)
131                    .zip(a_src)
132                {
133                    rgb_dst[destination_channels.get_r_channel_offset()] =
134                        qrshr::<PRECISION, BIT_DEPTH>((v_src.as_() - y_bias).as_() * y_coef).as_();
135                    rgb_dst[destination_channels.get_g_channel_offset()] =
136                        qrshr::<PRECISION, BIT_DEPTH>((y_src.as_() - y_bias).as_() * y_coef).as_();
137                    rgb_dst[destination_channels.get_b_channel_offset()] =
138                        qrshr::<PRECISION, BIT_DEPTH>((u_src.as_() - y_bias).as_() * y_coef).as_();
139                    rgb_dst[destination_channels.get_a_channel_offset()] = a_src;
140                }
141            });
142        }
143        YuvRange::Full => {
144            let iter = y_iter.zip(u_iter).zip(v_iter).zip(rgb_iter).zip(a_iter);
145            iter.for_each(|((((y_src, u_src), v_src), rgb), a_src)| {
146                let y_src = &y_src[0..image.width as usize];
147                let rgb_chunks = rgb.chunks_exact_mut(channels);
148
149                for ((((&y_src, &u_src), &v_src), rgb_dst), &a_src) in y_src
150                    .iter()
151                    .zip(u_src)
152                    .zip(v_src)
153                    .zip(rgb_chunks)
154                    .zip(a_src)
155                {
156                    rgb_dst[destination_channels.get_r_channel_offset()] = v_src;
157                    rgb_dst[destination_channels.get_g_channel_offset()] = y_src;
158                    rgb_dst[destination_channels.get_b_channel_offset()] = u_src;
159                    rgb_dst[destination_channels.get_a_channel_offset()] = a_src;
160                }
161            });
162        }
163    }
164
165    Ok(())
166}
167
168/// Convert GBR8A with alpha channel to RGBA
169///
170/// This function takes GBR interleaved format data with 8-bit precision,
171/// and converts it to RGBA format with 8-bit per channel precision.
172///
173/// # Arguments
174///
175/// * `image` - Source GBR image.
176/// * `rgba` - A slice to store the RGBA plane data.
177/// * `rgba_stride` - The stride (components per row) for the RGBA plane.
178/// * `range` - Yuv values range.
179///
180/// # Panics
181///
182/// This function panics if the lengths of the planes or the input RGBA data are not valid based
183/// on the specified width, height, and strides is provided.
184///
185pub fn gbr_with_alpha_to_rgba(
186    image: &YuvPlanarImageWithAlpha<u8>,
187    rgb: &mut [u8],
188    rgb_stride: u32,
189    range: YuvRange,
190) -> Result<(), YuvError> {
191    gbr_to_rgbx_alpha_impl::<u8, i16, { YuvSourceChannels::Rgba as u8 }, 8>(
192        image, rgb, rgb_stride, range,
193    )
194}
195
196/// Convert GBR8A with alpha channel to BGRA
197///
198/// This function takes GBR interleaved format data with 8-bit precision,
199/// and converts it to BGRA format with 8-bit per channel precision.
200///
201/// # Arguments
202///
203/// * `image` - Source GBR image.
204/// * `rgba` - A slice to store the BGRA plane data.
205/// * `rgba_stride` - The stride (components per row) for the BGRA plane.
206/// * `range` - Yuv values range.
207///
208/// # Panics
209///
210/// This function panics if the lengths of the planes or the input BGRA data are not valid based
211/// on the specified width, height, and strides is provided.
212///
213pub fn gbr_with_alpha_to_bgra(
214    image: &YuvPlanarImageWithAlpha<u8>,
215    rgb: &mut [u8],
216    rgb_stride: u32,
217    range: YuvRange,
218) -> Result<(), YuvError> {
219    gbr_to_rgbx_alpha_impl::<u8, i16, { YuvSourceChannels::Bgra as u8 }, 8>(
220        image, rgb, rgb_stride, range,
221    )
222}
223
224/// Convert GBRA12 with alpha channel to RGBA12
225///
226/// This function takes GBR interleaved format data with 12 bit precision,
227/// and converts it to RGBA format with 12 bit per channel precision.
228///
229/// # Arguments
230///
231/// * `image` - Source GBR image.
232/// * `rgba` - A slice to store the RGBA plane data.
233/// * `rgba_stride` - The stride (components per row) for the RGBA plane.
234/// * `bit_depth` - YUV and RGB bit depth
235/// * `range` - Yuv values range.
236///
237/// # Panics
238///
239/// This function panics if the lengths of the planes or the input RGBA data are not valid based
240/// on the specified width, height, and strides is provided.
241///
242pub fn gb12_alpha_to_rgba12(
243    image: &YuvPlanarImageWithAlpha<u16>,
244    rgba: &mut [u16],
245    rgba_stride: u32,
246    range: YuvRange,
247) -> Result<(), YuvError> {
248    gbr_to_rgbx_alpha_impl::<u16, i16, { YuvSourceChannels::Rgba as u8 }, 12>(
249        image,
250        rgba,
251        rgba_stride,
252        range,
253    )
254}
255
256/// Convert GBRA10 with alpha channel to RGBA10
257///
258/// This function takes GBR interleaved format data with 10 bit precision,
259/// and converts it to RGBA format with 10 bit per channel precision.
260///
261/// # Arguments
262///
263/// * `image` - Source GBR image.
264/// * `rgba` - A slice to store the RGBA plane data.
265/// * `rgba_stride` - The stride (components per row) for the RGBA plane.
266/// * `bit_depth` - YUV and RGB bit depth
267/// * `range` - Yuv values range.
268///
269/// # Panics
270///
271/// This function panics if the lengths of the planes or the input RGBA data are not valid based
272/// on the specified width, height, and strides is provided.
273///
274pub fn gb10_alpha_to_rgba10(
275    image: &YuvPlanarImageWithAlpha<u16>,
276    rgba: &mut [u16],
277    rgba_stride: u32,
278    range: YuvRange,
279) -> Result<(), YuvError> {
280    gbr_to_rgbx_alpha_impl::<u16, i16, { YuvSourceChannels::Rgba as u8 }, 10>(
281        image,
282        rgba,
283        rgba_stride,
284        range,
285    )
286}
287
288/// Convert GBRA14 with alpha channel to RGBA14
289///
290/// This function takes GBR interleaved format data with 14 bit precision,
291/// and converts it to RGBA format with 14 bit per channel precision.
292///
293/// # Arguments
294///
295/// * `image` - Source GBR image.
296/// * `rgba` - A slice to store the RGBA plane data.
297/// * `rgba_stride` - The stride (components per row) for the RGBA plane.
298/// * `bit_depth` - YUV and RGB bit depth
299/// * `range` - Yuv values range.
300///
301/// # Panics
302///
303/// This function panics if the lengths of the planes or the input RGBA data are not valid based
304/// on the specified width, height, and strides is provided.
305///
306pub fn gb14_alpha_to_rgba14(
307    image: &YuvPlanarImageWithAlpha<u16>,
308    rgba: &mut [u16],
309    rgba_stride: u32,
310    range: YuvRange,
311) -> Result<(), YuvError> {
312    gbr_to_rgbx_alpha_impl::<u16, i16, { YuvSourceChannels::Rgba as u8 }, 14>(
313        image,
314        rgba,
315        rgba_stride,
316        range,
317    )
318}
319
320/// Convert GBRA16 with alpha channel to RGBA16
321///
322/// This function takes GBR interleaved format data with 16 bit precision,
323/// and converts it to RGBA format with 16 bit per channel precision.
324///
325/// # Arguments
326///
327/// * `image` - Source GBR image.
328/// * `rgba` - A slice to store the RGBA plane data.
329/// * `rgba_stride` - The stride (components per row) for the RGBA plane.
330/// * `bit_depth` - YUV and RGB bit depth
331/// * `range` - Yuv values range.
332///
333/// # Panics
334///
335/// This function panics if the lengths of the planes or the input RGBA data are not valid based
336/// on the specified width, height, and strides is provided.
337///
338pub fn gb16_alpha_to_rgba16(
339    image: &YuvPlanarImageWithAlpha<u16>,
340    rgba: &mut [u16],
341    rgba_stride: u32,
342    range: YuvRange,
343) -> Result<(), YuvError> {
344    gbr_to_rgbx_alpha_impl::<u16, i32, { YuvSourceChannels::Rgba as u8 }, 16>(
345        image,
346        rgba,
347        rgba_stride,
348        range,
349    )
350}