1#![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, YuvPlanarImage, 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::marker::PhantomData;
41use std::mem::size_of;
42use std::ops::Sub;
43
44type TypicalHandlerLimited = fn(
45 g_plane: &[u8],
46 b_plane: &[u8],
47 r_plane: &[u8],
48 rgba: &mut [u8],
49 start_cx: usize,
50 width: usize,
51 y_bias: i32,
52 y_coeff: i32,
53) -> usize;
54
55type TypicalHandler = fn(
56 g_plane: &[u8],
57 b_plane: &[u8],
58 r_plane: &[u8],
59 rgba: &mut [u8],
60 start_cx: usize,
61 width: usize,
62) -> usize;
63
64struct WideRowGbrProcessor<T, const DEST: u8, const BIT_DEPTH: usize> {
65 _phantom: PhantomData<T>,
66 handler: Option<TypicalHandler>,
67}
68
69impl<T, const DEST: u8, const BIT_DEPTH: usize> Default
70 for WideRowGbrProcessor<T, DEST, BIT_DEPTH>
71{
72 fn default() -> Self {
73 #[cfg(all(target_arch = "aarch64", target_feature = "neon"))]
74 {
75 use crate::neon::yuv_to_rgba_row_full;
76 WideRowGbrProcessor {
77 _phantom: Default::default(),
78 handler: Some(yuv_to_rgba_row_full::<DEST>),
79 }
80 }
81
82 #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
83 {
84 #[cfg(feature = "avx")]
85 {
86 if std::arch::is_x86_feature_detected!("avx2") {
87 use crate::avx2::avx_yuv_to_rgba_row_full;
88 return WideRowGbrProcessor {
89 _phantom: Default::default(),
90 handler: Some(avx_yuv_to_rgba_row_full::<DEST>),
91 };
92 }
93 }
94 #[cfg(feature = "sse")]
95 {
96 if std::arch::is_x86_feature_detected!("sse4.1") {
97 use crate::sse::sse_yuv_to_rgba_row_full;
98 return WideRowGbrProcessor {
99 _phantom: Default::default(),
100 handler: Some(sse_yuv_to_rgba_row_full::<DEST>),
101 };
102 }
103 }
104 }
105
106 #[cfg(not(all(target_arch = "aarch64", target_feature = "neon")))]
107 WideRowGbrProcessor {
108 _phantom: PhantomData,
109 handler: None,
110 }
111 }
112}
113
114struct WideRowGbrLimitedProcessor<T, const DEST: u8, const BIT_DEPTH: usize, const PRECISION: i32> {
115 _phantom: PhantomData<T>,
116 handler: Option<TypicalHandlerLimited>,
117}
118
119impl<T, const DEST: u8, const BIT_DEPTH: usize, const PRECISION: i32> Default
120 for WideRowGbrLimitedProcessor<T, DEST, BIT_DEPTH, PRECISION>
121{
122 fn default() -> Self {
123 if PRECISION != 13 {
124 return WideRowGbrLimitedProcessor {
125 _phantom: Default::default(),
126 handler: None,
127 };
128 }
129 assert_eq!(PRECISION, 13);
130 #[cfg(all(target_arch = "aarch64", target_feature = "neon"))]
131 {
132 #[cfg(feature = "rdm")]
133 if std::arch::is_aarch64_feature_detected!("rdm") {
134 use crate::neon::yuv_to_rgba_row_limited_rdm;
135 return WideRowGbrLimitedProcessor {
136 _phantom: Default::default(),
137 handler: Some(yuv_to_rgba_row_limited_rdm::<DEST>),
138 };
139 }
140 use crate::neon::yuv_to_rgba_row_limited;
141 WideRowGbrLimitedProcessor {
142 _phantom: Default::default(),
143 handler: Some(yuv_to_rgba_row_limited::<DEST, PRECISION>),
144 }
145 }
146 #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
147 {
148 #[cfg(all(any(target_arch = "x86", target_arch = "x86_64"), feature = "avx"))]
149 {
150 if std::arch::is_x86_feature_detected!("avx2") {
151 use crate::avx2::avx_yuv_to_rgba_row_limited;
152 return WideRowGbrLimitedProcessor {
153 _phantom: Default::default(),
154 handler: Some(avx_yuv_to_rgba_row_limited::<DEST>),
155 };
156 }
157 }
158 #[cfg(all(any(target_arch = "x86", target_arch = "x86_64"), feature = "sse"))]
159 {
160 if std::arch::is_x86_feature_detected!("sse4.1") {
161 use crate::sse::sse_yuv_to_rgba_row_limited;
162 return WideRowGbrLimitedProcessor {
163 _phantom: Default::default(),
164 handler: Some(sse_yuv_to_rgba_row_limited::<DEST>),
165 };
166 }
167 }
168 }
169 #[cfg(not(all(target_arch = "aarch64", target_feature = "neon")))]
170 WideRowGbrLimitedProcessor {
171 _phantom: Default::default(),
172 handler: None,
173 }
174 }
175}
176
177trait FullRangeWideRow<V> {
178 fn handle_row(
179 &self,
180 g_plane: &[V],
181 b_plane: &[V],
182 r_plane: &[V],
183 rgba: &mut [V],
184 start_cx: usize,
185 width: usize,
186 ) -> usize;
187}
188
189trait LimitedRangeWideRow<V> {
190 fn handle_row(
191 &self,
192 g_plane: &[V],
193 b_plane: &[V],
194 r_plane: &[V],
195 rgba: &mut [V],
196 start_cx: usize,
197 width: usize,
198 y_bias: i32,
199 y_coeff: i32,
200 ) -> usize;
201}
202
203impl<const DEST: u8, const BIT_DEPTH: usize> FullRangeWideRow<u8>
204 for WideRowGbrProcessor<u8, DEST, BIT_DEPTH>
205{
206 fn handle_row(
207 &self,
208 _g_plane: &[u8],
209 _b_plane: &[u8],
210 _r_plane: &[u8],
211 _rgba: &mut [u8],
212 _start_cx: usize,
213 _width: usize,
214 ) -> usize {
215 if let Some(handler) = self.handler {
216 return handler(_g_plane, _b_plane, _r_plane, _rgba, 0, _width);
217 }
218 0
219 }
220}
221
222impl<const DEST: u8, const BIT_DEPTH: usize> FullRangeWideRow<u16>
223 for WideRowGbrProcessor<u16, DEST, BIT_DEPTH>
224{
225 fn handle_row(
226 &self,
227 _g_plane: &[u16],
228 _b_plane: &[u16],
229 _r_plane: &[u16],
230 _rgba: &mut [u16],
231 _start_cx: usize,
232 _width: usize,
233 ) -> usize {
234 0
235 }
236}
237
238impl<const DEST: u8, const BIT_DEPTH: usize, const PRECISION: i32> LimitedRangeWideRow<u8>
239 for WideRowGbrLimitedProcessor<u8, DEST, BIT_DEPTH, PRECISION>
240{
241 fn handle_row(
242 &self,
243 _g_plane: &[u8],
244 _b_plane: &[u8],
245 _r_plane: &[u8],
246 _rgba: &mut [u8],
247 _start_cx: usize,
248 _width: usize,
249 _y_bias: i32,
250 _y_coeff: i32,
251 ) -> usize {
252 if let Some(handler) = self.handler {
253 return handler(
254 _g_plane, _b_plane, _r_plane, _rgba, 0, _width, _y_bias, _y_coeff,
255 );
256 }
257 0
258 }
259}
260
261impl<const DEST: u8, const BIT_DEPTH: usize, const PRECISION: i32> LimitedRangeWideRow<u16>
262 for WideRowGbrLimitedProcessor<u16, DEST, BIT_DEPTH, PRECISION>
263{
264 fn handle_row(
265 &self,
266 _g_plane: &[u16],
267 _b_plane: &[u16],
268 _r_plane: &[u16],
269 _rgba: &mut [u16],
270 _start_cx: usize,
271 _width: usize,
272 _y_bias: i32,
273 _y_coeff: i32,
274 ) -> usize {
275 0
276 }
277}
278
279#[inline]
280fn gbr_to_rgbx_impl<
281 V: Copy + AsPrimitive<J> + 'static + Sized + Debug + Send + Sync,
282 J: Copy + Sub<Output = J> + AsPrimitive<i32> + Sync + Send,
283 const CHANNELS: u8,
284 const BIT_DEPTH: usize,
285>(
286 image: &YuvPlanarImage<V>,
287 rgba: &mut [V],
288 rgba_stride: u32,
289 yuv_range: YuvRange,
290) -> Result<(), YuvError>
291where
292 i32: AsPrimitive<V>,
293 u32: AsPrimitive<J>,
294 WideRowGbrProcessor<V, CHANNELS, BIT_DEPTH>: FullRangeWideRow<V>,
295 WideRowGbrLimitedProcessor<V, CHANNELS, BIT_DEPTH, 13>: LimitedRangeWideRow<V>,
296{
297 let cn: YuvSourceChannels = CHANNELS.into();
298 let channels = cn.get_channels_count();
299 assert!(
300 channels == 3 || channels == 4,
301 "GBR -> RGB is implemented only on 3 and 4 channels"
302 );
303 assert!(
304 (8..=16).contains(&BIT_DEPTH),
305 "Invalid bit depth is provided"
306 );
307 assert!(
308 if BIT_DEPTH > 8 {
309 size_of::<V>() == 2
310 } else {
311 size_of::<V>() == 1
312 },
313 "Unsupported bit depth and data type combination"
314 );
315 let y_plane = image.y_plane;
316 let u_plane = image.u_plane;
317 let v_plane = image.v_plane;
318 let y_stride = image.y_stride as usize;
319 let u_stride = image.u_stride as usize;
320 let v_stride = image.v_stride as usize;
321 let height = image.height;
322
323 image.check_constraints(YuvChromaSubsampling::Yuv444)?;
324 check_rgba_destination(rgba, rgba_stride, image.width, height, channels)?;
325
326 let max_value = (1 << BIT_DEPTH) - 1;
327
328 let y_iter;
329 let rgb_iter;
330 let u_iter;
331 let v_iter;
332
333 #[cfg(feature = "rayon")]
334 {
335 y_iter = y_plane.par_chunks_exact(y_stride);
336 rgb_iter = rgba.par_chunks_exact_mut(rgba_stride as usize);
337 u_iter = u_plane.par_chunks_exact(u_stride);
338 v_iter = v_plane.par_chunks_exact(v_stride);
339 }
340 #[cfg(not(feature = "rayon"))]
341 {
342 y_iter = y_plane.chunks_exact(y_stride);
343 rgb_iter = rgba.chunks_exact_mut(rgba_stride as usize);
344 u_iter = u_plane.chunks_exact(u_stride);
345 v_iter = v_plane.chunks_exact(v_stride);
346 }
347
348 match yuv_range {
349 YuvRange::Limited => {
350 const PRECISION: i32 = 13;
351 let range = get_yuv_range(BIT_DEPTH as u32, yuv_range);
353 let range_rgba = (1 << BIT_DEPTH) - 1;
354 let y_coef = ((range_rgba as f32 / range.range_y as f32) * (1 << PRECISION) as f32)
355 .round() as i32;
356 let y_bias: J = range.bias_y.as_();
357 let jy_bias = range.bias_y as i32;
358
359 let iter = y_iter.zip(u_iter).zip(v_iter).zip(rgb_iter);
360
361 let wide_handler = WideRowGbrLimitedProcessor::<V, CHANNELS, BIT_DEPTH, 13>::default();
362
363 iter.for_each(|(((y_src, u_src), v_src), rgb)| {
364 let y_src = &y_src[0..image.width as usize];
365
366 let cx = wide_handler.handle_row(
367 y_src,
368 u_src,
369 v_src,
370 rgb,
371 0,
372 image.width as usize,
373 jy_bias,
374 y_coef,
375 );
376
377 let rgb_chunks = rgb.chunks_exact_mut(channels);
378
379 for (((&y_src, &u_src), &v_src), rgb_dst) in
380 y_src.iter().zip(u_src).zip(v_src).zip(rgb_chunks).skip(cx)
381 {
382 rgb_dst[cn.get_r_channel_offset()] =
383 qrshr::<PRECISION, BIT_DEPTH>((v_src.as_() - y_bias).as_() * y_coef).as_();
384 rgb_dst[cn.get_g_channel_offset()] =
385 qrshr::<PRECISION, BIT_DEPTH>((y_src.as_() - y_bias).as_() * y_coef).as_();
386 rgb_dst[cn.get_b_channel_offset()] =
387 qrshr::<PRECISION, BIT_DEPTH>((u_src.as_() - y_bias).as_() * y_coef).as_();
388 if channels == 4 {
389 rgb_dst[cn.get_a_channel_offset()] = max_value.as_();
390 }
391 }
392 });
393 }
394 YuvRange::Full => {
395 let wide_handler = WideRowGbrProcessor::<V, CHANNELS, BIT_DEPTH>::default();
396 let iter = y_iter.zip(u_iter).zip(v_iter).zip(rgb_iter);
397 iter.for_each(|(((y_src, u_src), v_src), rgb)| {
398 let y_src = &y_src[0..image.width as usize];
399
400 let cx = wide_handler.handle_row(y_src, u_src, v_src, rgb, 0, image.width as usize);
401
402 let rgb_chunks = rgb.chunks_exact_mut(channels);
403
404 for (((&y_src, &u_src), &v_src), rgb_dst) in
405 y_src.iter().zip(u_src).zip(v_src).zip(rgb_chunks).skip(cx)
406 {
407 rgb_dst[cn.get_r_channel_offset()] = v_src;
408 rgb_dst[cn.get_g_channel_offset()] = y_src;
409 rgb_dst[cn.get_b_channel_offset()] = u_src;
410 if channels == 4 {
411 rgb_dst[cn.get_a_channel_offset()] = max_value.as_();
412 }
413 }
414 });
415 }
416 }
417
418 Ok(())
419}
420
421pub fn gbr_to_rgb(
439 image: &YuvPlanarImage<u8>,
440 rgb: &mut [u8],
441 rgb_stride: u32,
442 range: YuvRange,
443) -> Result<(), YuvError> {
444 gbr_to_rgbx_impl::<u8, i16, { YuvSourceChannels::Rgb as u8 }, 8>(image, rgb, rgb_stride, range)
445}
446
447pub fn gbr_to_bgr(
465 image: &YuvPlanarImage<u8>,
466 bgr: &mut [u8],
467 bgr_stride: u32,
468 range: YuvRange,
469) -> Result<(), YuvError> {
470 gbr_to_rgbx_impl::<u8, i16, { YuvSourceChannels::Bgr as u8 }, 8>(image, bgr, bgr_stride, range)
471}
472
473pub fn gbr_to_rgba(
491 image: &YuvPlanarImage<u8>,
492 rgb: &mut [u8],
493 rgb_stride: u32,
494 range: YuvRange,
495) -> Result<(), YuvError> {
496 gbr_to_rgbx_impl::<u8, i16, { YuvSourceChannels::Rgba as u8 }, 8>(image, rgb, rgb_stride, range)
497}
498
499pub fn gbr_to_bgra(
517 image: &YuvPlanarImage<u8>,
518 rgb: &mut [u8],
519 rgb_stride: u32,
520 range: YuvRange,
521) -> Result<(), YuvError> {
522 gbr_to_rgbx_impl::<u8, i16, { YuvSourceChannels::Bgra as u8 }, 8>(image, rgb, rgb_stride, range)
523}
524
525pub fn gb12_to_rgb12(
543 image: &YuvPlanarImage<u16>,
544 rgb: &mut [u16],
545 rgb_stride: u32,
546 range: YuvRange,
547) -> Result<(), YuvError> {
548 gbr_to_rgbx_impl::<u16, i16, { YuvSourceChannels::Rgb as u8 }, 12>(
549 image, rgb, rgb_stride, range,
550 )
551}
552
553pub fn gb10_to_rgb10(
572 image: &YuvPlanarImage<u16>,
573 rgb: &mut [u16],
574 rgb_stride: u32,
575 range: YuvRange,
576) -> Result<(), YuvError> {
577 gbr_to_rgbx_impl::<u16, i16, { YuvSourceChannels::Rgb as u8 }, 10>(
578 image, rgb, rgb_stride, range,
579 )
580}
581
582pub fn gb10_to_rgba10(
600 image: &YuvPlanarImage<u16>,
601 rgba: &mut [u16],
602 rgba_stride: u32,
603 range: YuvRange,
604) -> Result<(), YuvError> {
605 gbr_to_rgbx_impl::<u16, i16, { YuvSourceChannels::Rgba as u8 }, 10>(
606 image,
607 rgba,
608 rgba_stride,
609 range,
610 )
611}
612
613pub fn gb12_to_rgba12(
631 image: &YuvPlanarImage<u16>,
632 rgba: &mut [u16],
633 rgba_stride: u32,
634 range: YuvRange,
635) -> Result<(), YuvError> {
636 gbr_to_rgbx_impl::<u16, i16, { YuvSourceChannels::Rgba as u8 }, 12>(
637 image,
638 rgba,
639 rgba_stride,
640 range,
641 )
642}
643
644pub fn gb14_to_rgb14(
662 image: &YuvPlanarImage<u16>,
663 rgb: &mut [u16],
664 rgb_stride: u32,
665 range: YuvRange,
666) -> Result<(), YuvError> {
667 gbr_to_rgbx_impl::<u16, i16, { YuvSourceChannels::Rgb as u8 }, 14>(
668 image, rgb, rgb_stride, range,
669 )
670}
671
672pub fn gb14_to_rgba14(
690 image: &YuvPlanarImage<u16>,
691 rgba: &mut [u16],
692 rgba_stride: u32,
693 range: YuvRange,
694) -> Result<(), YuvError> {
695 gbr_to_rgbx_impl::<u16, i16, { YuvSourceChannels::Rgba as u8 }, 14>(
696 image,
697 rgba,
698 rgba_stride,
699 range,
700 )
701}
702
703pub fn gb16_to_rgb16(
721 image: &YuvPlanarImage<u16>,
722 rgb: &mut [u16],
723 rgb_stride: u32,
724 range: YuvRange,
725) -> Result<(), YuvError> {
726 gbr_to_rgbx_impl::<u16, i32, { YuvSourceChannels::Rgb as u8 }, 16>(
727 image, rgb, rgb_stride, range,
728 )
729}
730
731pub fn gb16_to_rgba16(
749 image: &YuvPlanarImage<u16>,
750 rgba: &mut [u16],
751 rgba_stride: u32,
752 range: YuvRange,
753) -> Result<(), YuvError> {
754 gbr_to_rgbx_impl::<u16, i32, { YuvSourceChannels::Rgba as u8 }, 16>(
755 image,
756 rgba,
757 rgba_stride,
758 range,
759 )
760}