yuvutils_rs/
from_identity.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, 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            // All channels on identity should use Y range
352            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
421/// Convert YUV Identity Matrix ( aka 'GBR ) to RGB
422///
423/// This function takes GBR planar format data with 8-bit precision,
424/// and converts it to RGB format with 8-bit per channel precision.
425///
426/// # Arguments
427///
428/// * `image` - Source GBR image.
429/// * `rgb` - A slice to store the RGB data.
430/// * `rgb_stride` - The stride (components per row) for the RGB.
431/// * `range` - Yuv values range.
432///
433/// # Panics
434///
435/// This function panics if the lengths of the planes or the input RGB data are not valid based
436/// on the specified width, height, and strides is provided.
437///
438pub 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
447/// Convert YUV Identity Matrix ( aka 'GBR ) to BGR
448///
449/// This function takes GBR planar format data with 8-bit precision,
450/// and converts it to BGR format with 8-bit per channel precision.
451///
452/// # Arguments
453///
454/// * `image` - Source GBR image.
455/// * `bgr` - A slice to store the BGR data.
456/// * `bgr_stride` - The stride (components per row) for the BGR.
457/// * `range` - Yuv values range.
458///
459/// # Panics
460///
461/// This function panics if the lengths of the planes or the input BGR data are not valid based
462/// on the specified width, height, and strides is provided.
463///
464pub 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
473/// Convert YUV Identity Matrix ( aka 'GBR ) to RGBA
474///
475/// This function takes GBR planar format data with 8-bit precision,
476/// and converts it to RGBA format with 8-bit per channel precision.
477///
478/// # Arguments
479///
480/// * `image` - Source GBR image.
481/// * `rgba` - A slice to store the RGBA data.
482/// * `rgba_stride` - The stride (components per row) for the RGBA.
483/// * `range` - Yuv values range.
484///
485/// # Panics
486///
487/// This function panics if the lengths of the planes or the input RGBA data are not valid based
488/// on the specified width, height, and strides is provided.
489///
490pub 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
499/// Convert YUV Identity Matrix ( aka 'GBR ) to BGRA
500///
501/// This function takes GBR planar format data with 8-bit precision,
502/// and converts it to BGRA format with 8-bit per channel precision.
503///
504/// # Arguments
505///
506/// * `image` - Source GBR image.
507/// * `rgba` - A slice to store the BGRA data.
508/// * `rgba_stride` - The stride (components per row) for the BGRA.
509/// * `range` - Yuv values range.
510///
511/// # Panics
512///
513/// This function panics if the lengths of the planes or the input BGRA data are not valid based
514/// on the specified width, height, and strides is provided.
515///
516pub 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
525/// Convert GBR 12 bit-depth to RGB
526///
527/// This function takes GBR planar format data with 12 bit precision,
528/// and converts it to RGB format with 12 bit per channel precision.
529///
530/// # Arguments
531///
532/// * `image` - Source GBR image.
533/// * `rgb` - A slice to store the RGB data.
534/// * `rgb_stride` - The stride (components per row) for the RGB.
535/// * `range` - Yuv values range.
536///
537/// # Panics
538///
539/// This function panics if the lengths of the planes or the input RGB data are not valid based
540/// on the specified width, height, and strides is provided.
541///
542pub 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
553/// Convert YUV Identity Matrix ( aka 'GBR ) to RGB
554///
555/// This function takes GBR planar format data with 8+ bit precision,
556/// and converts it to RGB format with 8+ bit per channel precision.
557///
558/// # Arguments
559///
560/// * `image` - Source GBR image.
561/// * `rgb` - A slice to store the RGB data.
562/// * `rgb_stride` - The stride (components per row) for the RGB.
563/// * `bit_depth` - YUV and RGB bit depth, only 10 and 12 is supported.
564/// * `range` - Yuv values range.
565///
566/// # Panics
567///
568/// This function panics if the lengths of the planes or the input RGB data are not valid based
569/// on the specified width, height, and strides is provided.
570///
571pub 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
582/// Convert GBR10 to RGBA10
583///
584/// This function takes GBR planar format data with 10 bit precision,
585/// and converts it to RGBA format with 10 bit per channel precision.
586///
587/// # Arguments
588///
589/// * `image` - Source GBR image.
590/// * `rgba` - A slice to store the RGBA data.
591/// * `rgba_stride` - The stride (components per row) for the RGBA.
592/// * `range` - Yuv values range.
593///
594/// # Panics
595///
596/// This function panics if the lengths of the planes or the input RGBA data are not valid based
597/// on the specified width, height, and strides is provided.
598///
599pub 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
613/// Convert GBR12 to RGBA12
614///
615/// This function takes GBR planar format data with 12 bit precision,
616/// and converts it to RGBA format with 12 bit per channel precision.
617///
618/// # Arguments
619///
620/// * `image` - Source GBR image.
621/// * `rgba` - A slice to store the RGBA data.
622/// * `rgba_stride` - The stride (components per row) for the RGBA.
623/// * `range` - Yuv values range.
624///
625/// # Panics
626///
627/// This function panics if the lengths of the planes or the input RGBA data are not valid based
628/// on the specified width, height, and strides is provided.
629///
630pub 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
644/// Convert GBR 14 bit-depth to RGB14
645///
646/// This function takes GBR planar format data with 14 bit precision,
647/// and converts it to RGB format with 14 bit per channel precision.
648///
649/// # Arguments
650///
651/// * `image` - Source GBR image.
652/// * `rgb` - A slice to store the RGB data.
653/// * `rgb_stride` - The stride (components per row) for the RGB.
654/// * `range` - Yuv values range.
655///
656/// # Panics
657///
658/// This function panics if the lengths of the planes or the input RGB data are not valid based
659/// on the specified width, height, and strides is provided.
660///
661pub 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
672/// Convert GBR14 to RGBA14
673///
674/// This function takes GBR planar format data with 14 bit precision,
675/// and converts it to RGBA format with 14 bit per channel precision.
676///
677/// # Arguments
678///
679/// * `image` - Source GBR image.
680/// * `rgba` - A slice to store the RGBA data.
681/// * `rgba_stride` - The stride (components per row) for the RGBA.
682/// * `range` - Yuv values range.
683///
684/// # Panics
685///
686/// This function panics if the lengths of the planes or the input RGBA data are not valid based
687/// on the specified width, height, and strides is provided.
688///
689pub 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
703/// Convert GBR16 bit-depth to RGB16
704///
705/// This function takes GBR planar format data with 16 bit precision,
706/// and converts it to RGB format with 16 bit per channel precision.
707///
708/// # Arguments
709///
710/// * `image` - Source GBR image.
711/// * `rgb` - A slice to store the RGB data.
712/// * `rgb_stride` - The stride (components per row) for the RGB.
713/// * `range` - Yuv values range.
714///
715/// # Panics
716///
717/// This function panics if the lengths of the planes or the input RGB data are not valid based
718/// on the specified width, height, and strides is provided.
719///
720pub 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
731/// Convert GBR16 to RGBA16
732///
733/// This function takes GBR planar format data with 16 bit precision,
734/// and converts it to RGBA format with 16 bit per channel precision.
735///
736/// # Arguments
737///
738/// * `image` - Source GBR image.
739/// * `rgba` - A slice to store the RGBA data.
740/// * `rgba_stride` - The stride (components per row) for the RGBA.
741/// * `range` - Yuv values range.
742///
743/// # Panics
744///
745/// This function panics if the lengths of the planes or the input RGBA data are not valid based
746/// on the specified width, height, and strides is provided.
747///
748pub 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}