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}