1#[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
1092pub 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
1129pub 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
1166pub 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
1203pub 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
1240pub 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
1277pub 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
1314pub 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
1351pub 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
1388pub 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
1425pub 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
1462pub 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
1499pub 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}