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