1use crate::yuv_error::check_rgba_destination;
30use crate::yuv_support::YuvSourceChannels;
31use crate::YuvError;
32
33pub(crate) trait ShuffleConverter<V: Copy, const SRC: u8, const DST: u8> {
34 fn convert(&self, src: &[V], dst: &mut [V], width: usize);
35}
36
37trait ShuffleConverterFactory<V> {
38 fn make_converter<const SRC: u8, const DST: u8>() -> Box<dyn ShuffleConverter<V, SRC, DST>>;
39}
40
41struct Rgba8DefaultConverter<const SRC: u8, const DST: u8> {}
42
43impl<const SRC: u8, const DST: u8> Default for Rgba8DefaultConverter<SRC, DST> {
44 fn default() -> Self {
45 Rgba8DefaultConverter {}
46 }
47}
48
49impl<const SRC: u8, const DST: u8> ShuffleConverter<u8, SRC, DST>
50 for Rgba8DefaultConverter<SRC, DST>
51{
52 fn convert(&self, src: &[u8], dst: &mut [u8], _: usize) {
53 let src_channels: YuvSourceChannels = SRC.into();
54 let dst_channels: YuvSourceChannels = DST.into();
55 for (dst, src) in dst
56 .chunks_exact_mut(dst_channels.get_channels_count())
57 .zip(src.chunks_exact(src_channels.get_channels_count()))
58 {
59 dst[dst_channels.get_r_channel_offset()] = src[src_channels.get_r_channel_offset()];
60 dst[dst_channels.get_g_channel_offset()] = src[src_channels.get_g_channel_offset()];
61 dst[dst_channels.get_b_channel_offset()] = src[src_channels.get_b_channel_offset()];
62 if dst_channels.has_alpha() {
63 let a = if src_channels.has_alpha() {
64 src[src_channels.get_a_channel_offset()]
65 } else {
66 255
67 };
68 dst[dst_channels.get_a_channel_offset()] = a;
69 }
70 }
71 }
72}
73
74impl ShuffleConverterFactory<u8> for u8 {
75 #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
76 fn make_converter<const SRC: u8, const DST: u8>() -> Box<dyn ShuffleConverter<u8, SRC, DST>> {
77 let mut _converter: Box<dyn ShuffleConverter<u8, SRC, DST>> =
78 Box::new(Rgba8DefaultConverter::default());
79 #[cfg(feature = "avx")]
80 if std::arch::is_x86_feature_detected!("avx2") {
81 let src_channels: YuvSourceChannels = SRC.into();
82 let dst_channels: YuvSourceChannels = DST.into();
83 use crate::avx2::{ShuffleConverterAvx2, ShuffleQTableConverterAvx2};
84 if src_channels.get_channels_count() == 4 && dst_channels.get_channels_count() == 4 {
85 _converter = Box::new(ShuffleQTableConverterAvx2::<SRC, DST>::create());
86 } else {
87 _converter = Box::new(ShuffleConverterAvx2::<SRC, DST>::default());
88 }
89 return _converter;
90 }
91 #[cfg(feature = "sse")]
92 {
93 use crate::sse::{ShuffleConverterSse, ShuffleQTableConverterSse};
94 let src_channels: YuvSourceChannels = SRC.into();
95 let dst_channels: YuvSourceChannels = DST.into();
96 if std::arch::is_x86_feature_detected!("sse4.1") {
97 if src_channels.get_channels_count() == 4 && dst_channels.get_channels_count() == 4
98 {
99 _converter = Box::new(ShuffleQTableConverterSse::<SRC, DST>::create());
100 } else {
101 _converter = Box::new(ShuffleConverterSse::<SRC, DST>::default());
102 }
103 return _converter;
104 }
105 }
106 _converter
107 }
108
109 #[cfg(all(target_arch = "aarch64", target_feature = "neon"))]
110 fn make_converter<const SRC: u8, const DST: u8>() -> Box<dyn ShuffleConverter<u8, SRC, DST>> {
111 use crate::neon::ShuffleConverterNeon;
112 Box::new(ShuffleConverterNeon::<SRC, DST>::default())
113 }
114
115 #[cfg(not(any(
116 all(target_arch = "aarch64", target_feature = "neon"),
117 any(target_arch = "x86", target_arch = "x86_64")
118 )))]
119 fn make_converter<const SRC: u8, const DST: u8>() -> Box<dyn ShuffleConverter<u8, SRC, DST>> {
120 Box::new(Rgba8DefaultConverter::<SRC, DST>::default())
121 }
122}
123
124fn shuffle_impl<
126 V: Copy + ShuffleConverterFactory<V>,
127 const SRC: u8,
128 const DST: u8,
129 const BIT_DEPTH: usize,
130>(
131 src: &[V],
132 src_stride: u32,
133 dst: &mut [V],
134 dst_stride: u32,
135 width: u32,
136 height: u32,
137) -> Result<(), YuvError> {
138 let src_channels: YuvSourceChannels = SRC.into();
139 let dst_channels: YuvSourceChannels = DST.into();
140 check_rgba_destination(
141 src,
142 src_stride,
143 width,
144 height,
145 src_channels.get_channels_count(),
146 )?;
147 check_rgba_destination(
148 dst,
149 dst_stride,
150 width,
151 height,
152 dst_channels.get_channels_count(),
153 )?;
154
155 let converter = V::make_converter::<SRC, DST>();
156
157 for (dst, src) in dst
158 .chunks_exact_mut(dst_stride as usize)
159 .zip(src.chunks_exact(src_stride as usize))
160 {
161 let dst = &mut dst[0..dst_channels.get_channels_count() * width as usize];
162 let src = &src[0..src_channels.get_channels_count() * width as usize];
163 converter.convert(src, dst, dst_stride as usize);
164 }
165
166 Ok(())
167}
168
169pub fn rgba_to_bgra(
183 src: &[u8],
184 src_stride: u32,
185 dst: &mut [u8],
186 dst_stride: u32,
187 width: u32,
188 height: u32,
189) -> Result<(), YuvError> {
190 shuffle_impl::<u8, { YuvSourceChannels::Rgba as u8 }, { YuvSourceChannels::Bgra as u8 }, 8>(
191 src, src_stride, dst, dst_stride, width, height,
192 )
193}
194
195pub fn rgba_to_bgr(
209 src: &[u8],
210 src_stride: u32,
211 dst: &mut [u8],
212 dst_stride: u32,
213 width: u32,
214 height: u32,
215) -> Result<(), YuvError> {
216 shuffle_impl::<u8, { YuvSourceChannels::Rgba as u8 }, { YuvSourceChannels::Bgr as u8 }, 8>(
217 src, src_stride, dst, dst_stride, width, height,
218 )
219}
220
221pub fn rgba_to_rgb(
235 src: &[u8],
236 src_stride: u32,
237 dst: &mut [u8],
238 dst_stride: u32,
239 width: u32,
240 height: u32,
241) -> Result<(), YuvError> {
242 shuffle_impl::<u8, { YuvSourceChannels::Rgba as u8 }, { YuvSourceChannels::Rgb as u8 }, 8>(
243 src, src_stride, dst, dst_stride, width, height,
244 )
245}
246
247pub fn rgb_to_bgra(
261 src: &[u8],
262 src_stride: u32,
263 dst: &mut [u8],
264 dst_stride: u32,
265 width: u32,
266 height: u32,
267) -> Result<(), YuvError> {
268 shuffle_impl::<u8, { YuvSourceChannels::Rgb as u8 }, { YuvSourceChannels::Bgra as u8 }, 8>(
269 src, src_stride, dst, dst_stride, width, height,
270 )
271}
272
273pub fn rgb_to_bgr(
287 src: &[u8],
288 src_stride: u32,
289 dst: &mut [u8],
290 dst_stride: u32,
291 width: u32,
292 height: u32,
293) -> Result<(), YuvError> {
294 shuffle_impl::<u8, { YuvSourceChannels::Rgb as u8 }, { YuvSourceChannels::Bgr as u8 }, 8>(
295 src, src_stride, dst, dst_stride, width, height,
296 )
297}
298
299pub fn bgr_to_rgb(
313 src: &[u8],
314 src_stride: u32,
315 dst: &mut [u8],
316 dst_stride: u32,
317 width: u32,
318 height: u32,
319) -> Result<(), YuvError> {
320 shuffle_impl::<u8, { YuvSourceChannels::Bgr as u8 }, { YuvSourceChannels::Rgb as u8 }, 8>(
321 src, src_stride, dst, dst_stride, width, height,
322 )
323}
324
325pub fn bgr_to_rgba(
339 src: &[u8],
340 src_stride: u32,
341 dst: &mut [u8],
342 dst_stride: u32,
343 width: u32,
344 height: u32,
345) -> Result<(), YuvError> {
346 shuffle_impl::<u8, { YuvSourceChannels::Bgr as u8 }, { YuvSourceChannels::Rgba as u8 }, 8>(
347 src, src_stride, dst, dst_stride, width, height,
348 )
349}
350
351pub fn bgr_to_bgra(
365 src: &[u8],
366 src_stride: u32,
367 dst: &mut [u8],
368 dst_stride: u32,
369 width: u32,
370 height: u32,
371) -> Result<(), YuvError> {
372 shuffle_impl::<u8, { YuvSourceChannels::Bgr as u8 }, { YuvSourceChannels::Bgra as u8 }, 8>(
373 src, src_stride, dst, dst_stride, width, height,
374 )
375}
376
377pub fn rgb_to_rgba(
391 src: &[u8],
392 src_stride: u32,
393 dst: &mut [u8],
394 dst_stride: u32,
395 width: u32,
396 height: u32,
397) -> Result<(), YuvError> {
398 shuffle_impl::<u8, { YuvSourceChannels::Rgb as u8 }, { YuvSourceChannels::Rgba as u8 }, 8>(
399 src, src_stride, dst, dst_stride, width, height,
400 )
401}
402
403pub fn bgra_to_rgba(
417 src: &[u8],
418 src_stride: u32,
419 dst: &mut [u8],
420 dst_stride: u32,
421 width: u32,
422 height: u32,
423) -> Result<(), YuvError> {
424 shuffle_impl::<u8, { YuvSourceChannels::Bgra as u8 }, { YuvSourceChannels::Rgba as u8 }, 8>(
425 src, src_stride, dst, dst_stride, width, height,
426 )
427}
428
429pub fn bgra_to_rgb(
443 src: &[u8],
444 src_stride: u32,
445 dst: &mut [u8],
446 dst_stride: u32,
447 width: u32,
448 height: u32,
449) -> Result<(), YuvError> {
450 shuffle_impl::<u8, { YuvSourceChannels::Bgra as u8 }, { YuvSourceChannels::Rgb as u8 }, 8>(
451 src, src_stride, dst, dst_stride, width, height,
452 )
453}
454
455pub fn bgra_to_bgr(
469 src: &[u8],
470 src_stride: u32,
471 dst: &mut [u8],
472 dst_stride: u32,
473 width: u32,
474 height: u32,
475) -> Result<(), YuvError> {
476 shuffle_impl::<u8, { YuvSourceChannels::Bgra as u8 }, { YuvSourceChannels::Bgr as u8 }, 8>(
477 src, src_stride, dst, dst_stride, width, height,
478 )
479}
480
481#[cfg(test)]
482mod tests {
483 use super::*;
484
485 #[test]
486 fn check_4_chan_reshuffling() {
487 let image_width = 256usize;
488 let image_height = 256usize;
489
490 const CHANNELS: usize = 4;
491
492 let mut image_rgba = vec![0u8; image_width * image_height * CHANNELS];
493 let mut image_bgra = vec![0u8; image_width * image_height * CHANNELS];
494
495 let center = image_width / 2 * CHANNELS + (image_height / 2) * CHANNELS * image_width;
496
497 image_rgba[center] = 127;
498 image_rgba[center + 1] = 17;
499 image_rgba[center + 2] = 2;
500 image_rgba[center + 3] = 90;
501
502 rgba_to_bgra(
503 &image_rgba,
504 CHANNELS as u32 * image_width as u32,
505 &mut image_bgra,
506 CHANNELS as u32 * image_width as u32,
507 image_width as u32,
508 image_height as u32,
509 )
510 .unwrap();
511
512 assert_eq!(image_bgra[center], 2);
513 assert_eq!(image_bgra[center + 1], 17);
514 assert_eq!(image_bgra[center + 2], 127);
515 assert_eq!(image_bgra[center + 3], 90);
516 }
517
518 #[test]
519 fn check_3_to_4_chan_reshuffling() {
520 let image_width = 256usize;
521 let image_height = 256usize;
522
523 const SRC: usize = 3;
524 const DST: usize = 4;
525
526 let mut image_rgb = vec![0u8; image_width * image_height * SRC];
527 let mut image_bgra = vec![0u8; image_width * image_height * DST];
528
529 let center_src = image_width / 2 * SRC + (image_height / 2) * SRC * image_width;
530 let center_dst = image_width / 2 * DST + (image_height / 2) * DST * image_width;
531
532 image_rgb[center_src] = 127;
533 image_rgb[center_src + 1] = 17;
534 image_rgb[center_src + 2] = 2;
535 image_rgb[center_src + 3] = 90;
536
537 rgb_to_bgra(
538 &image_rgb,
539 SRC as u32 * image_width as u32,
540 &mut image_bgra,
541 DST as u32 * image_width as u32,
542 image_width as u32,
543 image_height as u32,
544 )
545 .unwrap();
546
547 assert_eq!(image_bgra[center_dst], 2);
548 assert_eq!(image_bgra[center_dst + 1], 17);
549 assert_eq!(image_bgra[center_dst + 2], 127);
550 assert_eq!(image_bgra[center_dst + 3], 255);
551 }
552
553 #[test]
554 fn check_4_to_3_chan_reshuffling() {
555 let image_width = 256usize;
556 let image_height = 256usize;
557
558 const SRC: usize = 4;
559 const DST: usize = 3;
560
561 let mut image_rgba = vec![0u8; image_width * image_height * SRC];
562 let mut image_bgr = vec![0u8; image_width * image_height * DST];
563
564 let center_src = image_width / 2 * SRC + (image_height / 2) * SRC * image_width;
565 let center_dst = image_width / 2 * DST + (image_height / 2) * DST * image_width;
566
567 image_rgba[center_src] = 127;
568 image_rgba[center_src + 1] = 17;
569 image_rgba[center_src + 2] = 2;
570 image_rgba[center_src + 3] = 90;
571
572 rgba_to_bgr(
573 &image_rgba,
574 SRC as u32 * image_width as u32,
575 &mut image_bgr,
576 DST as u32 * image_width as u32,
577 image_width as u32,
578 image_height as u32,
579 )
580 .unwrap();
581
582 assert_eq!(image_bgr[center_dst], 2);
583 assert_eq!(image_bgr[center_dst + 1], 17);
584 assert_eq!(image_bgr[center_dst + 2], 127);
585 }
586}