yuvutils_rs/
rgb16_to_yuv_p16.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 */
29use crate::internals::{ProcessedOffset, WideRowForward420Handler, WideRowForwardHandler};
30
31use crate::yuv_error::check_rgba_destination;
32use crate::yuv_support::{
33    get_forward_transform, get_yuv_range, CbCrForwardTransform, ToIntegerTransform, YuvChromaRange,
34    YuvChromaSubsampling, YuvSourceChannels,
35};
36use crate::{
37    YuvBytesPacking, YuvEndianness, YuvError, YuvPlanarImageMut, YuvRange, YuvStandardMatrix,
38};
39#[cfg(feature = "rayon")]
40use rayon::iter::{IndexedParallelIterator, ParallelIterator};
41#[cfg(feature = "rayon")]
42use rayon::prelude::{ParallelSlice, ParallelSliceMut};
43
44#[inline(always)]
45fn transform_integer<const ENDIANNESS: u8, const BYTES_POSITION: u8, const BIT_DEPTH: usize>(
46    v: i32,
47) -> u16 {
48    let endianness: YuvEndianness = ENDIANNESS.into();
49    let bytes_position: YuvBytesPacking = BYTES_POSITION.into();
50    let packing: i32 = 16 - BIT_DEPTH as i32;
51    let packed_bytes = match bytes_position {
52        YuvBytesPacking::MostSignificantBytes => v << packing,
53        YuvBytesPacking::LeastSignificantBytes => v,
54    } as u16;
55    match endianness {
56        #[cfg(feature = "big_endian")]
57        YuvEndianness::BigEndian => packed_bytes.to_be(),
58        YuvEndianness::LittleEndian => packed_bytes.to_le(),
59    }
60}
61
62type RgbEncoderHandler = Option<
63    unsafe fn(
64        transform: &CbCrForwardTransform<i32>,
65        range: &YuvChromaRange,
66        y_plane: &mut [u16],
67        u_plane: &mut [u16],
68        v_plane: &mut [u16],
69        rgba: &[u16],
70        start_cx: usize,
71        start_ux: usize,
72        width: usize,
73    ) -> ProcessedOffset,
74>;
75
76type RgbEncoder420Handler = Option<
77    unsafe fn(
78        transform: &CbCrForwardTransform<i32>,
79        range: &YuvChromaRange,
80        y_plane0: &mut [u16],
81        y_plane1: &mut [u16],
82        u_plane: &mut [u16],
83        v_plane: &mut [u16],
84        rgba0: &[u16],
85        rgba1: &[u16],
86        start_cx: usize,
87        start_ux: usize,
88        width: usize,
89    ) -> ProcessedOffset,
90>;
91
92struct RgbEncoder<
93    const ORIGIN_CHANNELS: u8,
94    const SAMPLING: u8,
95    const ENDIANNESS: u8,
96    const BYTES_POSITION: u8,
97    const BIT_DEPTH: usize,
98    const PRECISION: i32,
99> {
100    handler: RgbEncoderHandler,
101}
102
103struct RgbEncoder420<
104    const ORIGIN_CHANNELS: u8,
105    const SAMPLING: u8,
106    const ENDIANNESS: u8,
107    const BYTES_POSITION: u8,
108    const BIT_DEPTH: usize,
109    const PRECISION: i32,
110> {
111    handler: RgbEncoder420Handler,
112}
113
114impl<
115        const ORIGIN_CHANNELS: u8,
116        const SAMPLING: u8,
117        const ENDIANNESS: u8,
118        const BYTES_POSITION: u8,
119        const BIT_DEPTH: usize,
120        const PRECISION: i32,
121    > Default
122    for RgbEncoder<ORIGIN_CHANNELS, SAMPLING, ENDIANNESS, BYTES_POSITION, BIT_DEPTH, PRECISION>
123{
124    fn default() -> Self {
125        if PRECISION != 15 {
126            return RgbEncoder { handler: None };
127        }
128        #[cfg(all(target_arch = "aarch64", target_feature = "neon"))]
129        {
130            #[cfg(feature = "rdm")]
131            if BIT_DEPTH == 10 {
132                let is_rdm_available = std::arch::is_aarch64_feature_detected!("rdm");
133                if is_rdm_available {
134                    use crate::neon::neon_rgba_to_yuv_p16_rdm;
135                    return RgbEncoder {
136                        handler: Some(
137                            neon_rgba_to_yuv_p16_rdm::<
138                                ORIGIN_CHANNELS,
139                                SAMPLING,
140                                ENDIANNESS,
141                                BYTES_POSITION,
142                                PRECISION,
143                                BIT_DEPTH,
144                            >,
145                        ),
146                    };
147                }
148            }
149
150            use crate::neon::neon_rgba_to_yuv_p16;
151            RgbEncoder {
152                handler: Some(
153                    neon_rgba_to_yuv_p16::<
154                        ORIGIN_CHANNELS,
155                        SAMPLING,
156                        ENDIANNESS,
157                        BYTES_POSITION,
158                        PRECISION,
159                        BIT_DEPTH,
160                    >,
161                ),
162            }
163        }
164        #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
165        {
166            #[cfg(feature = "nightly_avx512")]
167            {
168                let use_avx512 = std::arch::is_x86_feature_detected!("avx512bw");
169                if use_avx512 && BIT_DEPTH <= 15 {
170                    use crate::avx512bw::avx512_rgba_to_yuv_p16;
171                    return RgbEncoder {
172                        handler: Some(
173                            avx512_rgba_to_yuv_p16::<
174                                ORIGIN_CHANNELS,
175                                SAMPLING,
176                                ENDIANNESS,
177                                BYTES_POSITION,
178                                PRECISION,
179                                BIT_DEPTH,
180                            >,
181                        ),
182                    };
183                }
184            }
185            #[cfg(feature = "avx")]
186            {
187                let use_avx = std::arch::is_x86_feature_detected!("avx2");
188                if use_avx {
189                    if BIT_DEPTH <= 15 {
190                        use crate::avx2::avx_rgba_to_yuv_p16;
191                        return RgbEncoder {
192                            handler: Some(
193                                avx_rgba_to_yuv_p16::<
194                                    ORIGIN_CHANNELS,
195                                    SAMPLING,
196                                    ENDIANNESS,
197                                    BYTES_POSITION,
198                                    PRECISION,
199                                    BIT_DEPTH,
200                                >,
201                            ),
202                        };
203                    } else {
204                        use crate::avx2::avx_rgba_to_yuv_p16_d16;
205                        return RgbEncoder {
206                            handler: Some(
207                                avx_rgba_to_yuv_p16_d16::<
208                                    ORIGIN_CHANNELS,
209                                    SAMPLING,
210                                    ENDIANNESS,
211                                    BYTES_POSITION,
212                                    PRECISION,
213                                    BIT_DEPTH,
214                                >,
215                            ),
216                        };
217                    }
218                }
219            }
220            #[cfg(feature = "sse")]
221            {
222                let use_sse = std::arch::is_x86_feature_detected!("sse4.1");
223                if use_sse && BIT_DEPTH <= 15 {
224                    use crate::sse::sse_rgba_to_yuv_p16;
225                    return RgbEncoder {
226                        handler: Some(
227                            sse_rgba_to_yuv_p16::<
228                                ORIGIN_CHANNELS,
229                                SAMPLING,
230                                ENDIANNESS,
231                                BYTES_POSITION,
232                                PRECISION,
233                                BIT_DEPTH,
234                            >,
235                        ),
236                    };
237                }
238            }
239        }
240        #[cfg(not(all(target_arch = "aarch64", target_feature = "neon")))]
241        RgbEncoder { handler: None }
242    }
243}
244
245macro_rules! define_handler_impl {
246    ($struct_name:ident) => {
247        impl<
248                const ORIGIN_CHANNELS: u8,
249                const SAMPLING: u8,
250                const ENDIANNESS: u8,
251                const BYTES_POSITION: u8,
252                const BIT_DEPTH: usize,
253                const PRECISION: i32,
254            > WideRowForwardHandler<u16, i32>
255            for $struct_name<
256                ORIGIN_CHANNELS,
257                SAMPLING,
258                ENDIANNESS,
259                BYTES_POSITION,
260                BIT_DEPTH,
261                PRECISION,
262            >
263        {
264            fn handle_row(
265                &self,
266                y_plane: &mut [u16],
267                u_plane: &mut [u16],
268                v_plane: &mut [u16],
269                rgba: &[u16],
270                width: u32,
271                chroma: YuvChromaRange,
272                transform: &CbCrForwardTransform<i32>,
273            ) -> ProcessedOffset {
274                if let Some(handler) = self.handler {
275                    unsafe {
276                        return handler(
277                            transform,
278                            &chroma,
279                            y_plane,
280                            u_plane,
281                            v_plane,
282                            rgba,
283                            0,
284                            0,
285                            width as usize,
286                        );
287                    }
288                }
289                ProcessedOffset { cx: 0, ux: 0 }
290            }
291        }
292    };
293}
294
295define_handler_impl!(RgbEncoder);
296
297impl<
298        const ORIGIN_CHANNELS: u8,
299        const SAMPLING: u8,
300        const ENDIANNESS: u8,
301        const BYTES_POSITION: u8,
302        const BIT_DEPTH: usize,
303        const PRECISION: i32,
304    > Default
305    for RgbEncoder420<ORIGIN_CHANNELS, SAMPLING, ENDIANNESS, BYTES_POSITION, BIT_DEPTH, PRECISION>
306{
307    fn default() -> Self {
308        if PRECISION != 15 {
309            return RgbEncoder420 { handler: None };
310        }
311        assert_eq!(PRECISION, 15);
312        let sampling: YuvChromaSubsampling = SAMPLING.into();
313        if sampling != YuvChromaSubsampling::Yuv420 {
314            return RgbEncoder420 { handler: None };
315        }
316        assert_eq!(sampling, YuvChromaSubsampling::Yuv420);
317        #[cfg(all(target_arch = "aarch64", target_feature = "neon"))]
318        {
319            #[cfg(feature = "rdm")]
320            {
321                let is_rdm_available = std::arch::is_aarch64_feature_detected!("rdm");
322                if is_rdm_available && BIT_DEPTH == 10 {
323                    use crate::neon::neon_rgba_to_yuv_p16_rdm_420;
324                    return RgbEncoder420 {
325                        handler: Some(
326                            neon_rgba_to_yuv_p16_rdm_420::<
327                                ORIGIN_CHANNELS,
328                                ENDIANNESS,
329                                BYTES_POSITION,
330                                PRECISION,
331                                BIT_DEPTH,
332                            >,
333                        ),
334                    };
335                }
336            }
337            use crate::neon::neon_rgba_to_yuv_p16_420;
338            RgbEncoder420 {
339                handler: Some(
340                    neon_rgba_to_yuv_p16_420::<
341                        ORIGIN_CHANNELS,
342                        ENDIANNESS,
343                        BYTES_POSITION,
344                        PRECISION,
345                        BIT_DEPTH,
346                    >,
347                ),
348            }
349        }
350        #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
351        {
352            #[cfg(feature = "nightly_avx512")]
353            {
354                let use_avx512 = std::arch::is_x86_feature_detected!("avx512bw");
355                if use_avx512 && BIT_DEPTH <= 15 {
356                    use crate::avx512bw::avx512_rgba_to_yuv_p16_420;
357                    return RgbEncoder420 {
358                        handler: Some(
359                            avx512_rgba_to_yuv_p16_420::<
360                                ORIGIN_CHANNELS,
361                                ENDIANNESS,
362                                BYTES_POSITION,
363                                PRECISION,
364                                BIT_DEPTH,
365                            >,
366                        ),
367                    };
368                }
369            }
370            #[cfg(feature = "avx")]
371            {
372                let use_avx = std::arch::is_x86_feature_detected!("avx2");
373                if use_avx {
374                    if BIT_DEPTH <= 15 {
375                        use crate::avx2::avx_rgba_to_yuv_p16_420;
376                        return RgbEncoder420 {
377                            handler: Some(
378                                avx_rgba_to_yuv_p16_420::<
379                                    ORIGIN_CHANNELS,
380                                    ENDIANNESS,
381                                    BYTES_POSITION,
382                                    PRECISION,
383                                    BIT_DEPTH,
384                                >,
385                            ),
386                        };
387                    } else if BIT_DEPTH <= 16 {
388                        use crate::avx2::avx_rgba_to_yuv_p16_420_d16;
389                        return RgbEncoder420 {
390                            handler: Some(
391                                avx_rgba_to_yuv_p16_420_d16::<
392                                    ORIGIN_CHANNELS,
393                                    ENDIANNESS,
394                                    BYTES_POSITION,
395                                    PRECISION,
396                                    BIT_DEPTH,
397                                >,
398                            ),
399                        };
400                    }
401                }
402            }
403            #[cfg(feature = "sse")]
404            {
405                let use_sse = std::arch::is_x86_feature_detected!("sse4.1");
406                if use_sse && BIT_DEPTH <= 15 {
407                    use crate::sse::sse_rgba_to_yuv_p16_420;
408                    return RgbEncoder420 {
409                        handler: Some(
410                            sse_rgba_to_yuv_p16_420::<
411                                ORIGIN_CHANNELS,
412                                ENDIANNESS,
413                                BYTES_POSITION,
414                                PRECISION,
415                                BIT_DEPTH,
416                            >,
417                        ),
418                    };
419                }
420            }
421        }
422        #[cfg(not(all(target_arch = "aarch64", target_feature = "neon")))]
423        RgbEncoder420 { handler: None }
424    }
425}
426
427macro_rules! impl_wide_row_forward_handler {
428    ($struct_name:ident) => {
429        impl<
430                const ORIGIN_CHANNELS: u8,
431                const SAMPLING: u8,
432                const ENDIANNESS: u8,
433                const BYTES_POSITION: u8,
434                const BIT_DEPTH: usize,
435                const PRECISION: i32,
436            > WideRowForward420Handler<u16, i32>
437            for $struct_name<
438                ORIGIN_CHANNELS,
439                SAMPLING,
440                ENDIANNESS,
441                BYTES_POSITION,
442                BIT_DEPTH,
443                PRECISION,
444            >
445        {
446            fn handle_row(
447                &self,
448                y_plane0: &mut [u16],
449                y_plane1: &mut [u16],
450                u_plane: &mut [u16],
451                v_plane: &mut [u16],
452                rgba0: &[u16],
453                rgba1: &[u16],
454                width: u32,
455                chroma: YuvChromaRange,
456                transform: &CbCrForwardTransform<i32>,
457            ) -> ProcessedOffset {
458                if let Some(handler) = self.handler {
459                    unsafe {
460                        return handler(
461                            transform,
462                            &chroma,
463                            y_plane0,
464                            y_plane1,
465                            u_plane,
466                            v_plane,
467                            rgba0,
468                            rgba1,
469                            0,
470                            0,
471                            width as usize,
472                        );
473                    }
474                }
475                ProcessedOffset { cx: 0, ux: 0 }
476            }
477        }
478    };
479}
480
481impl_wide_row_forward_handler!(RgbEncoder420);
482
483fn rgbx_to_yuv_ant<
484    const ORIGIN_CHANNELS: u8,
485    const SAMPLING: u8,
486    const ENDIANNESS: u8,
487    const BYTES_POSITION: u8,
488    const BIT_DEPTH: usize,
489    const PRECISION: i32,
490>(
491    image: &mut YuvPlanarImageMut<u16>,
492    rgba: &[u16],
493    rgba_stride: u32,
494    range: YuvRange,
495    matrix: YuvStandardMatrix,
496    handler: impl WideRowForwardHandler<u16, i32> + Send + Sync,
497    handler420: impl WideRowForward420Handler<u16, i32> + Send + Sync,
498) -> Result<(), YuvError> {
499    let chroma_subsampling: YuvChromaSubsampling = SAMPLING.into();
500    let src_chans: YuvSourceChannels = ORIGIN_CHANNELS.into();
501    let channels = src_chans.get_channels_count();
502
503    image.check_constraints(chroma_subsampling)?;
504    check_rgba_destination(rgba, rgba_stride, image.width, image.height, channels)?;
505
506    let range = get_yuv_range(BIT_DEPTH as u32, range);
507    let kr_kb = matrix.get_kr_kb();
508    let max_range = (1u32 << BIT_DEPTH) - 1u32;
509    let transform_precise =
510        get_forward_transform(max_range, range.range_y, range.range_uv, kr_kb.kr, kr_kb.kb);
511
512    let transform = transform_precise.to_integers(PRECISION as u32);
513    let rnd_const: i32 = (1 << (PRECISION - 1)) - 1;
514    let bias_y = range.bias_y as i32 * (1 << PRECISION) + rnd_const;
515    let bias_uv = range.bias_uv as i32 * (1 << PRECISION) + rnd_const;
516
517    let process_halved_chroma_row = |y_plane: &mut [u16],
518                                     u_plane: &mut [u16],
519                                     v_plane: &mut [u16],
520                                     rgba| {
521        let processed_offset = handler.handle_row(
522            y_plane,
523            u_plane,
524            v_plane,
525            rgba,
526            image.width,
527            range,
528            &transform,
529        );
530        let cx = processed_offset.cx;
531        if cx != image.width as usize {
532            for (((y_dst, u_dst), v_dst), rgba) in y_plane
533                .chunks_exact_mut(2)
534                .zip(u_plane.iter_mut())
535                .zip(v_plane.iter_mut())
536                .zip(rgba.chunks_exact(channels * 2))
537                .skip(cx / 2)
538            {
539                let r0 = rgba[src_chans.get_r_channel_offset()] as i32;
540                let g0 = rgba[src_chans.get_g_channel_offset()] as i32;
541                let b0 = rgba[src_chans.get_b_channel_offset()] as i32;
542                let y_0 = (r0 * transform.yr + g0 * transform.yg + b0 * transform.yb + bias_y)
543                    >> PRECISION;
544                y_dst[0] = transform_integer::<ENDIANNESS, BYTES_POSITION, BIT_DEPTH>(y_0);
545
546                let r1 = rgba[channels + src_chans.get_r_channel_offset()] as i32;
547                let g1 = rgba[channels + src_chans.get_g_channel_offset()] as i32;
548                let b1 = rgba[channels + src_chans.get_b_channel_offset()] as i32;
549                let y_1 = (r1 * transform.yr + g1 * transform.yg + b1 * transform.yb + bias_y)
550                    >> PRECISION;
551                y_dst[1] = transform_integer::<ENDIANNESS, BYTES_POSITION, BIT_DEPTH>(y_1);
552
553                let r = (r0 + r1 + 1) >> 1;
554                let g = (g0 + g1 + 1) >> 1;
555                let b = (b0 + b1 + 1) >> 1;
556
557                let cb = (r * transform.cb_r + g * transform.cb_g + b * transform.cb_b + bias_uv)
558                    >> PRECISION;
559                let cr = (r * transform.cr_r + g * transform.cr_g + b * transform.cr_b + bias_uv)
560                    >> PRECISION;
561                *u_dst = transform_integer::<ENDIANNESS, BYTES_POSITION, BIT_DEPTH>(cb);
562                *v_dst = transform_integer::<ENDIANNESS, BYTES_POSITION, BIT_DEPTH>(cr);
563            }
564
565            if image.width & 1 != 0 {
566                let rgb_last = rgba.chunks_exact(channels * 2).remainder();
567                let r0 = rgb_last[src_chans.get_r_channel_offset()] as i32;
568                let g0 = rgb_last[src_chans.get_g_channel_offset()] as i32;
569                let b0 = rgb_last[src_chans.get_b_channel_offset()] as i32;
570
571                let y_last = y_plane.last_mut().unwrap();
572                let u_last = u_plane.last_mut().unwrap();
573                let v_last = v_plane.last_mut().unwrap();
574
575                let y_0 = (r0 * transform.yr + g0 * transform.yg + b0 * transform.yb + bias_y)
576                    >> PRECISION;
577                *y_last = transform_integer::<ENDIANNESS, BYTES_POSITION, BIT_DEPTH>(y_0);
578
579                let cb =
580                    (r0 * transform.cb_r + g0 * transform.cb_g + b0 * transform.cb_b + bias_uv)
581                        >> PRECISION;
582                let cr =
583                    (r0 * transform.cr_r + g0 * transform.cr_g + b0 * transform.cr_b + bias_uv)
584                        >> PRECISION;
585                *u_last = transform_integer::<ENDIANNESS, BYTES_POSITION, BIT_DEPTH>(cb);
586                *v_last = transform_integer::<ENDIANNESS, BYTES_POSITION, BIT_DEPTH>(cr);
587            }
588        }
589    };
590
591    let process_double_chroma_row = |y_plane0: &mut [u16],
592                                     y_plane1: &mut [u16],
593                                     u_plane: &mut [u16],
594                                     v_plane: &mut [u16],
595                                     rgba0: &[u16],
596                                     rgba1: &[u16]| {
597        let processed_offset = handler420.handle_row(
598            y_plane0,
599            y_plane1,
600            u_plane,
601            v_plane,
602            rgba0,
603            rgba1,
604            image.width,
605            range,
606            &transform,
607        );
608        let cx = processed_offset.cx;
609
610        for (((((y_dst0, y_dst1), u_dst), v_dst), rgba0), rgba1) in y_plane0
611            .chunks_exact_mut(2)
612            .zip(y_plane1.chunks_exact_mut(2))
613            .zip(u_plane.iter_mut())
614            .zip(v_plane.iter_mut())
615            .zip(rgba0.chunks_exact(channels * 2))
616            .zip(rgba1.chunks_exact(channels * 2))
617            .skip(cx / 2)
618        {
619            let r00 = rgba0[src_chans.get_r_channel_offset()] as i32;
620            let g00 = rgba0[src_chans.get_g_channel_offset()] as i32;
621            let b00 = rgba0[src_chans.get_b_channel_offset()] as i32;
622            let y_00 = (r00 * transform.yr + g00 * transform.yg + b00 * transform.yb + bias_y)
623                >> PRECISION;
624            y_dst0[0] = transform_integer::<ENDIANNESS, BYTES_POSITION, BIT_DEPTH>(y_00);
625
626            let rgba01 = &rgba0[channels..channels * 2];
627            let r01 = rgba01[src_chans.get_r_channel_offset()] as i32;
628            let g01 = rgba01[src_chans.get_g_channel_offset()] as i32;
629            let b01 = rgba01[src_chans.get_b_channel_offset()] as i32;
630            let y_01 = (r01 * transform.yr + g01 * transform.yg + b01 * transform.yb + bias_y)
631                >> PRECISION;
632            y_dst0[1] = transform_integer::<ENDIANNESS, BYTES_POSITION, BIT_DEPTH>(y_01);
633
634            let r10 = rgba1[src_chans.get_r_channel_offset()] as i32;
635            let g10 = rgba1[src_chans.get_g_channel_offset()] as i32;
636            let b10 = rgba1[src_chans.get_b_channel_offset()] as i32;
637            let y_10 = (r10 * transform.yr + g10 * transform.yg + b10 * transform.yb + bias_y)
638                >> PRECISION;
639            y_dst1[0] = transform_integer::<ENDIANNESS, BYTES_POSITION, BIT_DEPTH>(y_10);
640
641            let rgba11 = &rgba1[channels..channels * 2];
642            let r11 = rgba11[src_chans.get_r_channel_offset()] as i32;
643            let g11 = rgba11[src_chans.get_g_channel_offset()] as i32;
644            let b11 = rgba11[src_chans.get_b_channel_offset()] as i32;
645            let y_11 = (r01 * transform.yr + g01 * transform.yg + b01 * transform.yb + bias_y)
646                >> PRECISION;
647            y_dst1[1] = transform_integer::<ENDIANNESS, BYTES_POSITION, BIT_DEPTH>(y_11);
648
649            let r = (r00 + r01 + r10 + r11 + 2) >> 2;
650            let g = (g00 + g01 + g10 + g11 + 2) >> 2;
651            let b = (b00 + b01 + b10 + b11 + 2) >> 2;
652
653            let cb = (r * transform.cb_r + g * transform.cb_g + b * transform.cb_b + bias_uv)
654                >> PRECISION;
655            let cr = (r * transform.cr_r + g * transform.cr_g + b * transform.cr_b + bias_uv)
656                >> PRECISION;
657            *u_dst = transform_integer::<ENDIANNESS, BYTES_POSITION, BIT_DEPTH>(cb);
658            *v_dst = transform_integer::<ENDIANNESS, BYTES_POSITION, BIT_DEPTH>(cr);
659        }
660
661        if image.width & 1 != 0 {
662            let rgb_last0 = rgba0.chunks_exact(channels * 2).remainder();
663            let r0 = rgb_last0[src_chans.get_r_channel_offset()] as i32;
664            let g0 = rgb_last0[src_chans.get_g_channel_offset()] as i32;
665            let b0 = rgb_last0[src_chans.get_b_channel_offset()] as i32;
666
667            let rgb_last1 = rgba1.chunks_exact(channels * 2).remainder();
668            let r1 = rgb_last1[src_chans.get_r_channel_offset()] as i32;
669            let g1 = rgb_last1[src_chans.get_g_channel_offset()] as i32;
670            let b1 = rgb_last1[src_chans.get_b_channel_offset()] as i32;
671
672            let y0_last = y_plane0.last_mut().unwrap();
673            let y1_last = y_plane1.last_mut().unwrap();
674            let u_last = u_plane.last_mut().unwrap();
675            let v_last = v_plane.last_mut().unwrap();
676
677            let y_0 =
678                (r0 * transform.yr + g0 * transform.yg + b0 * transform.yb + bias_y) >> PRECISION;
679            *y0_last = transform_integer::<ENDIANNESS, BYTES_POSITION, BIT_DEPTH>(y_0);
680
681            let y_1 =
682                (r1 * transform.yr + g1 * transform.yg + b1 * transform.yb + bias_y) >> PRECISION;
683            *y1_last = transform_integer::<ENDIANNESS, BYTES_POSITION, BIT_DEPTH>(y_1);
684
685            let r = (r0 + r1 + 1) >> 1;
686            let g = (g0 + g1 + 1) >> 1;
687            let b = (b0 + b1 + 1) >> 1;
688
689            let cb = (r * transform.cb_r + g * transform.cb_g + b * transform.cb_b + bias_uv)
690                >> PRECISION;
691            let cr = (r * transform.cr_r + g * transform.cr_g + b * transform.cr_b + bias_uv)
692                >> PRECISION;
693            *u_last = transform_integer::<ENDIANNESS, BYTES_POSITION, BIT_DEPTH>(cb);
694            *v_last = transform_integer::<ENDIANNESS, BYTES_POSITION, BIT_DEPTH>(cr);
695        }
696    };
697
698    let y_plane = image.y_plane.borrow_mut();
699    let u_plane = image.u_plane.borrow_mut();
700    let v_plane = image.v_plane.borrow_mut();
701    let y_stride = image.y_stride as usize;
702    let u_stride = image.u_stride as usize;
703    let v_stride = image.v_stride as usize;
704
705    if chroma_subsampling == YuvChromaSubsampling::Yuv444 {
706        let iter;
707        #[cfg(feature = "rayon")]
708        {
709            iter = y_plane
710                .par_chunks_exact_mut(y_stride)
711                .zip(u_plane.par_chunks_exact_mut(u_stride))
712                .zip(v_plane.par_chunks_exact_mut(v_stride))
713                .zip(rgba.par_chunks_exact(rgba_stride as usize));
714        }
715        #[cfg(not(feature = "rayon"))]
716        {
717            iter = y_plane
718                .chunks_exact_mut(y_stride)
719                .zip(u_plane.chunks_exact_mut(u_stride))
720                .zip(v_plane.chunks_exact_mut(v_stride))
721                .zip(rgba.chunks_exact(rgba_stride as usize));
722        }
723        iter.for_each(|(((y_dst, u_plane), v_plane), rgba)| {
724            let y_dst = &mut y_dst[0..image.width as usize];
725            let processed_offset = handler.handle_row(
726                y_dst,
727                u_plane,
728                v_plane,
729                rgba,
730                image.width,
731                range,
732                &transform,
733            );
734
735            let cx = processed_offset.cx;
736
737            if cx != image.width as usize {
738                for (((y_dst, u_dst), v_dst), rgba) in y_dst
739                    .iter_mut()
740                    .zip(u_plane.iter_mut())
741                    .zip(v_plane.iter_mut())
742                    .zip(rgba.chunks_exact(channels))
743                    .skip(cx)
744                {
745                    let r0 = rgba[src_chans.get_r_channel_offset()] as i32;
746                    let g0 = rgba[src_chans.get_g_channel_offset()] as i32;
747                    let b0 = rgba[src_chans.get_b_channel_offset()] as i32;
748                    let y_0 = (r0 * transform.yr + g0 * transform.yg + b0 * transform.yb + bias_y)
749                        >> PRECISION;
750                    *y_dst = transform_integer::<ENDIANNESS, BYTES_POSITION, BIT_DEPTH>(y_0);
751
752                    let cb =
753                        (r0 * transform.cb_r + g0 * transform.cb_g + b0 * transform.cb_b + bias_uv)
754                            >> PRECISION;
755                    let cr =
756                        (r0 * transform.cr_r + g0 * transform.cr_g + b0 * transform.cr_b + bias_uv)
757                            >> PRECISION;
758                    *u_dst = transform_integer::<ENDIANNESS, BYTES_POSITION, BIT_DEPTH>(cb);
759                    *v_dst = transform_integer::<ENDIANNESS, BYTES_POSITION, BIT_DEPTH>(cr);
760                }
761            }
762        });
763    } else if chroma_subsampling == YuvChromaSubsampling::Yuv422 {
764        let iter;
765        #[cfg(feature = "rayon")]
766        {
767            iter = y_plane
768                .par_chunks_exact_mut(y_stride)
769                .zip(u_plane.par_chunks_exact_mut(u_stride))
770                .zip(v_plane.par_chunks_exact_mut(v_stride))
771                .zip(rgba.par_chunks_exact(rgba_stride as usize));
772        }
773        #[cfg(not(feature = "rayon"))]
774        {
775            iter = y_plane
776                .chunks_exact_mut(y_stride)
777                .zip(u_plane.chunks_exact_mut(u_stride))
778                .zip(v_plane.chunks_exact_mut(v_stride))
779                .zip(rgba.chunks_exact(rgba_stride as usize));
780        }
781
782        iter.for_each(|(((y_plane, u_plane), v_plane), rgba)| {
783            process_halved_chroma_row(y_plane, u_plane, v_plane, rgba);
784        });
785    } else if chroma_subsampling == YuvChromaSubsampling::Yuv420 {
786        let iter;
787        #[cfg(feature = "rayon")]
788        {
789            iter = y_plane
790                .par_chunks_exact_mut(y_stride * 2)
791                .zip(u_plane.par_chunks_exact_mut(u_stride))
792                .zip(v_plane.par_chunks_exact_mut(v_stride))
793                .zip(rgba.par_chunks_exact(rgba_stride as usize * 2));
794        }
795        #[cfg(not(feature = "rayon"))]
796        {
797            iter = y_plane
798                .chunks_exact_mut(y_stride * 2)
799                .zip(u_plane.chunks_exact_mut(u_stride))
800                .zip(v_plane.chunks_exact_mut(v_stride))
801                .zip(rgba.chunks_exact(rgba_stride as usize * 2));
802        }
803        iter.for_each(|(((y_plane, u_plane), v_plane), rgba)| {
804            let (y_plane0, y_plane1) = y_plane.split_at_mut(y_stride);
805            let (rgba0, rgba1) = rgba.split_at(rgba_stride as usize);
806            process_double_chroma_row(
807                &mut y_plane0[0..image.width as usize],
808                &mut y_plane1[0..image.width as usize],
809                &mut u_plane[0..(image.width as usize).div_ceil(2)],
810                &mut v_plane[0..(image.width as usize).div_ceil(2)],
811                &rgba0[0..image.width as usize * channels],
812                &rgba1[0..image.width as usize * channels],
813            );
814        });
815
816        if image.height & 1 != 0 {
817            let remainder_y_plane = y_plane.chunks_exact_mut(y_stride * 2).into_remainder();
818            let remainder_rgba = rgba.chunks_exact(rgba_stride as usize * 2).remainder();
819            let u_plane = u_plane.chunks_exact_mut(u_stride).last().unwrap();
820            let v_plane = v_plane.chunks_exact_mut(v_stride).last().unwrap();
821            process_halved_chroma_row(
822                &mut remainder_y_plane[0..image.width as usize],
823                &mut u_plane[0..(image.width as usize).div_ceil(2)],
824                &mut v_plane[0..(image.width as usize).div_ceil(2)],
825                &remainder_rgba[0..image.width as usize * channels],
826            );
827        }
828    } else {
829        unreachable!();
830    }
831
832    Ok(())
833}
834
835macro_rules! d_cvn {
836    ($method: ident, $px_fmt: expr,
837    $sampling: expr,
838    $yuv_name: expr, $rgb_name: expr,
839    $rgb_small: expr, $bit_depth: expr,
840    $endianness: expr) => {
841        #[doc = concat!("Convert ", $rgb_name, " image data to ", $yuv_name, " format with ", $bit_depth, " bit depth.
842
843This function performs ", $rgb_name, stringify!($bit_depth), " to ",$yuv_name," conversion and stores the result in ", $yuv_name," format,
844with separate planes for Y (luminance), U (chrominance), and V (chrominance) components.
845
846# Arguments
847
848* `planar_image` - Target planar image.
849* `",$rgb_small,"` - The input ", $rgb_name," image data slice.
850* `",$rgb_small,"_stride` - The stride (components per row) for the ", $rgb_name ," image data.
851* `range` - The YUV range (limited or full).
852* `matrix` - The YUV standard matrix (BT.601 or BT.709 or BT.2020 or other).
853
854# Panics
855
856This function panics if the lengths of the planes or the input RGBA data are not valid based
857on the specified width, height, and strides, or if invalid YUV range or matrix is provided.
858")]
859        pub fn $method(
860            planar_image: &mut YuvPlanarImageMut<u16>,
861            rgba: &[u16],
862            rgba_stride: u32,
863            range: YuvRange,
864            matrix: YuvStandardMatrix,
865        ) -> Result<(), YuvError> {
866            rgbx_to_yuv_ant::<
867                { $px_fmt as u8 },
868                { $sampling as u8 },
869                { $endianness as u8 },
870                { YuvBytesPacking::LeastSignificantBytes as u8 },
871                $bit_depth,
872                15,
873            >(planar_image, rgba, rgba_stride, range, matrix,
874              RgbEncoder::<{ $px_fmt as u8 }, { $sampling as u8 }, { $endianness as u8 },
875                            { YuvBytesPacking::LeastSignificantBytes as u8 }, $bit_depth, 15>::default(),
876              RgbEncoder420::<{ $px_fmt as u8 }, { $sampling as u8 }, { $endianness as u8 },
877                            { YuvBytesPacking::LeastSignificantBytes as u8 }, $bit_depth, 15>::default())
878        }
879    };
880}
881
882d_cvn!(
883    rgba10_to_i010,
884    YuvSourceChannels::Rgba,
885    YuvChromaSubsampling::Yuv420,
886    "I010",
887    "RGBA10",
888    "rgba10",
889    10,
890    YuvEndianness::LittleEndian
891);
892#[cfg(feature = "big_endian")]
893d_cvn!(
894    rgba10_to_i010_be,
895    YuvSourceChannels::Rgba,
896    YuvChromaSubsampling::Yuv420,
897    "I010",
898    "RGBA10",
899    "rgba10",
900    10,
901    YuvEndianness::BigEndian
902);
903
904d_cvn!(
905    rgb10_to_i010,
906    YuvSourceChannels::Rgb,
907    YuvChromaSubsampling::Yuv420,
908    "I010",
909    "RGB10",
910    "rgb10",
911    10,
912    YuvEndianness::LittleEndian
913);
914#[cfg(feature = "big_endian")]
915d_cvn!(
916    rgb10_to_i010_be,
917    YuvSourceChannels::Rgb,
918    YuvChromaSubsampling::Yuv420,
919    "I010",
920    "RGB10",
921    "rgb10",
922    10,
923    YuvEndianness::BigEndian
924);
925
926d_cvn!(
927    rgba10_to_i210,
928    YuvSourceChannels::Rgba,
929    YuvChromaSubsampling::Yuv422,
930    "I210",
931    "RGBA10",
932    "rgba10",
933    10,
934    YuvEndianness::LittleEndian
935);
936#[cfg(feature = "big_endian")]
937d_cvn!(
938    rgba10_to_i210_be,
939    YuvSourceChannels::Rgba,
940    YuvChromaSubsampling::Yuv422,
941    "I210",
942    "RGBA10",
943    "rgba10",
944    10,
945    YuvEndianness::BigEndian
946);
947
948d_cvn!(
949    rgb10_to_i210,
950    YuvSourceChannels::Rgb,
951    YuvChromaSubsampling::Yuv422,
952    "I210",
953    "RGB10",
954    "rgb10",
955    10,
956    YuvEndianness::LittleEndian
957);
958#[cfg(feature = "big_endian")]
959d_cvn!(
960    rgb10_to_i210_be,
961    YuvSourceChannels::Rgb,
962    YuvChromaSubsampling::Yuv422,
963    "I210",
964    "RGB10",
965    "rgb10",
966    10,
967    YuvEndianness::BigEndian
968);
969
970d_cvn!(
971    rgba10_to_i410,
972    YuvSourceChannels::Rgba,
973    YuvChromaSubsampling::Yuv444,
974    "I410",
975    "RGBA10",
976    "rgba10",
977    10,
978    YuvEndianness::LittleEndian
979);
980#[cfg(feature = "big_endian")]
981d_cvn!(
982    rgba10_to_i410_be,
983    YuvSourceChannels::Rgba,
984    YuvChromaSubsampling::Yuv444,
985    "I410",
986    "RGBA10",
987    "rgba10",
988    10,
989    YuvEndianness::BigEndian
990);
991
992d_cvn!(
993    rgb10_to_i410,
994    YuvSourceChannels::Rgb,
995    YuvChromaSubsampling::Yuv444,
996    "I410",
997    "RGB10",
998    "rgb10",
999    10,
1000    YuvEndianness::LittleEndian
1001);
1002#[cfg(feature = "big_endian")]
1003d_cvn!(
1004    rgb10_to_i410_be,
1005    YuvSourceChannels::Rgb,
1006    YuvChromaSubsampling::Yuv444,
1007    "I410",
1008    "RGB10",
1009    "rgb10",
1010    10,
1011    YuvEndianness::BigEndian
1012);
1013
1014d_cvn!(
1015    rgba12_to_i012,
1016    YuvSourceChannels::Rgba,
1017    YuvChromaSubsampling::Yuv420,
1018    "I012",
1019    "RGBA12",
1020    "rgba12",
1021    12,
1022    YuvEndianness::LittleEndian
1023);
1024#[cfg(feature = "big_endian")]
1025d_cvn!(
1026    rgba12_to_i012_be,
1027    YuvSourceChannels::Rgba,
1028    YuvChromaSubsampling::Yuv420,
1029    "I012",
1030    "RGBA12",
1031    "rgba12",
1032    12,
1033    YuvEndianness::BigEndian
1034);
1035
1036d_cvn!(
1037    rgb12_to_i012,
1038    YuvSourceChannels::Rgb,
1039    YuvChromaSubsampling::Yuv420,
1040    "I012",
1041    "RGB12",
1042    "rgb12",
1043    12,
1044    YuvEndianness::LittleEndian
1045);
1046#[cfg(feature = "big_endian")]
1047d_cvn!(
1048    rgb12_to_i012_be,
1049    YuvSourceChannels::Rgb,
1050    YuvChromaSubsampling::Yuv420,
1051    "I012",
1052    "RGB12",
1053    "rgb12",
1054    12,
1055    YuvEndianness::BigEndian
1056);
1057
1058d_cvn!(
1059    rgba12_to_i212,
1060    YuvSourceChannels::Rgba,
1061    YuvChromaSubsampling::Yuv422,
1062    "I212",
1063    "RGBA12",
1064    "rgba12",
1065    12,
1066    YuvEndianness::LittleEndian
1067);
1068#[cfg(feature = "big_endian")]
1069d_cvn!(
1070    rgba12_to_i212_be,
1071    YuvSourceChannels::Rgba,
1072    YuvChromaSubsampling::Yuv420,
1073    "I212",
1074    "RGBA12",
1075    "rgba12",
1076    12,
1077    YuvEndianness::BigEndian
1078);
1079
1080d_cvn!(
1081    rgb12_to_i212,
1082    YuvSourceChannels::Rgb,
1083    YuvChromaSubsampling::Yuv422,
1084    "I212",
1085    "RGB12",
1086    "rgb12",
1087    12,
1088    YuvEndianness::LittleEndian
1089);
1090#[cfg(feature = "big_endian")]
1091d_cvn!(
1092    rgb12_to_i212_be,
1093    YuvSourceChannels::Rgb,
1094    YuvChromaSubsampling::Yuv422,
1095    "I212",
1096    "RGB12",
1097    "rgb12",
1098    12,
1099    YuvEndianness::BigEndian
1100);
1101
1102d_cvn!(
1103    rgba12_to_i412,
1104    YuvSourceChannels::Rgba,
1105    YuvChromaSubsampling::Yuv444,
1106    "I412",
1107    "RGBA12",
1108    "rgba12",
1109    12,
1110    YuvEndianness::LittleEndian
1111);
1112#[cfg(feature = "big_endian")]
1113d_cvn!(
1114    rgba12_to_i412_be,
1115    YuvSourceChannels::Rgba,
1116    YuvChromaSubsampling::Yuv444,
1117    "I412",
1118    "RGBA12",
1119    "rgba12",
1120    12,
1121    YuvEndianness::BigEndian
1122);
1123
1124d_cvn!(
1125    rgb12_to_i412,
1126    YuvSourceChannels::Rgb,
1127    YuvChromaSubsampling::Yuv444,
1128    "I412",
1129    "RGB12",
1130    "rgb12",
1131    12,
1132    YuvEndianness::LittleEndian
1133);
1134#[cfg(feature = "big_endian")]
1135d_cvn!(
1136    rgb12_to_i412_be,
1137    YuvSourceChannels::Rgb,
1138    YuvChromaSubsampling::Yuv444,
1139    "I412",
1140    "RGB12",
1141    "rgb12",
1142    12,
1143    YuvEndianness::BigEndian
1144);
1145// 14-bit
1146d_cvn!(
1147    rgba14_to_i014,
1148    YuvSourceChannels::Rgba,
1149    YuvChromaSubsampling::Yuv420,
1150    "I014",
1151    "RGBA14",
1152    "rgba14",
1153    14,
1154    YuvEndianness::LittleEndian
1155);
1156#[cfg(feature = "big_endian")]
1157d_cvn!(
1158    rgba14_to_i014_be,
1159    YuvSourceChannels::Rgba,
1160    YuvChromaSubsampling::Yuv420,
1161    "I014",
1162    "RGBA14",
1163    "rgba14",
1164    14,
1165    YuvEndianness::BigEndian
1166);
1167
1168d_cvn!(
1169    rgb14_to_i014,
1170    YuvSourceChannels::Rgb,
1171    YuvChromaSubsampling::Yuv420,
1172    "I014",
1173    "RGB14",
1174    "rgb14",
1175    14,
1176    YuvEndianness::LittleEndian
1177);
1178#[cfg(feature = "big_endian")]
1179d_cvn!(
1180    rgb14_to_i014_be,
1181    YuvSourceChannels::Rgb,
1182    YuvChromaSubsampling::Yuv420,
1183    "I014",
1184    "RGB14",
1185    "rgb14",
1186    14,
1187    YuvEndianness::BigEndian
1188);
1189
1190d_cvn!(
1191    rgba14_to_i214,
1192    YuvSourceChannels::Rgba,
1193    YuvChromaSubsampling::Yuv422,
1194    "I214",
1195    "RGBA14",
1196    "rgba14",
1197    14,
1198    YuvEndianness::LittleEndian
1199);
1200#[cfg(feature = "big_endian")]
1201d_cvn!(
1202    rgba14_to_i214_be,
1203    YuvSourceChannels::Rgba,
1204    YuvChromaSubsampling::Yuv422,
1205    "I214",
1206    "RGBA14",
1207    "rgba14",
1208    14,
1209    YuvEndianness::BigEndian
1210);
1211d_cvn!(
1212    rgb14_to_i214,
1213    YuvSourceChannels::Rgb,
1214    YuvChromaSubsampling::Yuv422,
1215    "I214",
1216    "RGB14",
1217    "rgb14",
1218    14,
1219    YuvEndianness::LittleEndian
1220);
1221#[cfg(feature = "big_endian")]
1222d_cvn!(
1223    rgb14_to_i214_be,
1224    YuvSourceChannels::Rgb,
1225    YuvChromaSubsampling::Yuv422,
1226    "I214",
1227    "RGB14",
1228    "rgb14",
1229    14,
1230    YuvEndianness::BigEndian
1231);
1232
1233d_cvn!(
1234    rgba14_to_i414,
1235    YuvSourceChannels::Rgba,
1236    YuvChromaSubsampling::Yuv444,
1237    "I414",
1238    "RGBA14",
1239    "rgba14",
1240    14,
1241    YuvEndianness::LittleEndian
1242);
1243#[cfg(feature = "big_endian")]
1244d_cvn!(
1245    rgba14_to_i414_be,
1246    YuvSourceChannels::Rgba,
1247    YuvChromaSubsampling::Yuv444,
1248    "I414",
1249    "RGBA14",
1250    "rgba14",
1251    14,
1252    YuvEndianness::BigEndian
1253);
1254
1255d_cvn!(
1256    rgb14_to_i414,
1257    YuvSourceChannels::Rgb,
1258    YuvChromaSubsampling::Yuv444,
1259    "I414",
1260    "RGB14",
1261    "rgb14",
1262    14,
1263    YuvEndianness::LittleEndian
1264);
1265#[cfg(feature = "big_endian")]
1266d_cvn!(
1267    rgb14_to_i414_be,
1268    YuvSourceChannels::Rgb,
1269    YuvChromaSubsampling::Yuv444,
1270    "I414",
1271    "RGB14",
1272    "rgb14",
1273    14,
1274    YuvEndianness::BigEndian
1275);
1276//16-bit
1277d_cvn!(
1278    rgba16_to_i016,
1279    YuvSourceChannels::Rgba,
1280    YuvChromaSubsampling::Yuv420,
1281    "I016",
1282    "RGBA16",
1283    "rgba16",
1284    16,
1285    YuvEndianness::LittleEndian
1286);
1287#[cfg(feature = "big_endian")]
1288d_cvn!(
1289    rgba16_to_i016_be,
1290    YuvSourceChannels::Rgba,
1291    YuvChromaSubsampling::Yuv420,
1292    "I016",
1293    "RGBA16",
1294    "rgba16",
1295    16,
1296    YuvEndianness::BigEndian
1297);
1298
1299d_cvn!(
1300    rgb16_to_i016,
1301    YuvSourceChannels::Rgb,
1302    YuvChromaSubsampling::Yuv420,
1303    "I016",
1304    "RGB16",
1305    "rgb16",
1306    16,
1307    YuvEndianness::LittleEndian
1308);
1309#[cfg(feature = "big_endian")]
1310d_cvn!(
1311    rgb16_to_i016_be,
1312    YuvSourceChannels::Rgb,
1313    YuvChromaSubsampling::Yuv420,
1314    "I016",
1315    "RGB16",
1316    "rgb16",
1317    16,
1318    YuvEndianness::BigEndian
1319);
1320
1321d_cvn!(
1322    rgba16_to_i216,
1323    YuvSourceChannels::Rgba,
1324    YuvChromaSubsampling::Yuv422,
1325    "I216",
1326    "RGBA16",
1327    "rgba16",
1328    16,
1329    YuvEndianness::LittleEndian
1330);
1331#[cfg(feature = "big_endian")]
1332d_cvn!(
1333    rgba16_to_i216_be,
1334    YuvSourceChannels::Rgba,
1335    YuvChromaSubsampling::Yuv422,
1336    "I216",
1337    "RGBA16",
1338    "rgba16",
1339    16,
1340    YuvEndianness::BigEndian
1341);
1342d_cvn!(
1343    rgb16_to_i216,
1344    YuvSourceChannels::Rgb,
1345    YuvChromaSubsampling::Yuv422,
1346    "I216",
1347    "RGB16",
1348    "rgb16",
1349    16,
1350    YuvEndianness::LittleEndian
1351);
1352#[cfg(feature = "big_endian")]
1353d_cvn!(
1354    rgb16_to_i216_be,
1355    YuvSourceChannels::Rgb,
1356    YuvChromaSubsampling::Yuv422,
1357    "I216",
1358    "RGB16",
1359    "rgb16",
1360    16,
1361    YuvEndianness::BigEndian
1362);
1363
1364d_cvn!(
1365    rgba16_to_i416,
1366    YuvSourceChannels::Rgba,
1367    YuvChromaSubsampling::Yuv444,
1368    "I416",
1369    "RGBA16",
1370    "rgba16",
1371    16,
1372    YuvEndianness::LittleEndian
1373);
1374#[cfg(feature = "big_endian")]
1375d_cvn!(
1376    rgba16_to_i416_be,
1377    YuvSourceChannels::Rgba,
1378    YuvChromaSubsampling::Yuv444,
1379    "I416",
1380    "RGBA16",
1381    "rgba16",
1382    16,
1383    YuvEndianness::BigEndian
1384);
1385
1386d_cvn!(
1387    rgb16_to_i416,
1388    YuvSourceChannels::Rgb,
1389    YuvChromaSubsampling::Yuv444,
1390    "I416",
1391    "RGB16",
1392    "rgb16",
1393    16,
1394    YuvEndianness::LittleEndian
1395);
1396#[cfg(feature = "big_endian")]
1397d_cvn!(
1398    rgb16_to_i416_be,
1399    YuvSourceChannels::Rgb,
1400    YuvChromaSubsampling::Yuv444,
1401    "I416",
1402    "RGB16",
1403    "rgb16",
1404    16,
1405    YuvEndianness::BigEndian
1406);