1use crate::yuv_error::check_rgba_destination;
30use crate::yuv_support::{Rgb30, YuvSourceChannels};
31use crate::{Rgb30ByteOrder, YuvError};
32
33type RgbRa30RowHandler<V> = unsafe fn(src: &[V], dst: &mut [u8]);
34
35#[inline(always)]
36fn default_row_converter<
37 const AR30_LAYOUT: usize,
38 const AR30_BYTE_ORDER: usize,
39 const RGBA_LAYOUT: u8,
40>(
41 src: &[u8],
42 dst: &mut [u8],
43) {
44 let rgba_layout: YuvSourceChannels = RGBA_LAYOUT.into();
45 let ar30_layout: Rgb30 = AR30_LAYOUT.into();
46 for (src, dst) in src
47 .chunks_exact(rgba_layout.get_channels_count())
48 .zip(dst.chunks_exact_mut(4))
49 {
50 let r = src[rgba_layout.get_r_channel_offset()];
51 let g = src[rgba_layout.get_g_channel_offset()];
52 let b = src[rgba_layout.get_b_channel_offset()];
53
54 let r = u16::from_ne_bytes([r, r]) >> 6;
55 let g = u16::from_ne_bytes([g, g]) >> 6;
56 let b = u16::from_ne_bytes([b, b]) >> 6;
57
58 let packed = if rgba_layout.has_alpha() {
59 ar30_layout.pack_w_a::<AR30_BYTE_ORDER>(
60 r as i32,
61 g as i32,
62 b as i32,
63 src[3] as i32 >> 6,
64 )
65 } else {
66 ar30_layout.pack::<AR30_BYTE_ORDER>(r as i32, g as i32, b as i32)
67 };
68 let v_bytes = packed.to_ne_bytes();
69 dst[0] = v_bytes[0];
70 dst[1] = v_bytes[1];
71 dst[2] = v_bytes[2];
72 dst[3] = v_bytes[3];
73 }
74}
75
76#[inline(always)]
77fn default_row_converter_hb<
78 const AR30_LAYOUT: usize,
79 const AR30_BYTE_ORDER: usize,
80 const RGBA_LAYOUT: u8,
81 const BIT_DEPTH: usize,
82>(
83 src: &[u16],
84 dst: &mut [u8],
85) {
86 let rgba_layout: YuvSourceChannels = RGBA_LAYOUT.into();
87 let ar30_layout: Rgb30 = AR30_LAYOUT.into();
88 let target_shift = BIT_DEPTH - 10;
89 let alpha_shift = BIT_DEPTH - 2;
90 for (src, dst) in src
91 .chunks_exact(rgba_layout.get_channels_count())
92 .zip(dst.chunks_exact_mut(4))
93 {
94 let r = src[rgba_layout.get_r_channel_offset()];
95 let g = src[rgba_layout.get_g_channel_offset()];
96 let b = src[rgba_layout.get_b_channel_offset()];
97
98 let r = r >> target_shift;
99 let g = g >> target_shift;
100 let b = b >> target_shift;
101
102 let packed = if rgba_layout.has_alpha() {
103 ar30_layout.pack_w_a::<AR30_BYTE_ORDER>(
104 r as i32,
105 g as i32,
106 b as i32,
107 src[3] as i32 >> alpha_shift,
108 )
109 } else {
110 ar30_layout.pack::<AR30_BYTE_ORDER>(r as i32, g as i32, b as i32)
111 };
112 let v_bytes = packed.to_ne_bytes();
113 dst[0] = v_bytes[0];
114 dst[1] = v_bytes[1];
115 dst[2] = v_bytes[2];
116 dst[3] = v_bytes[3];
117 }
118}
119
120#[cfg(all(
121 any(target_arch = "x86", target_arch = "x86_64"),
122 feature = "nightly_avx512"
123))]
124#[target_feature(enable = "avx512bw")]
125unsafe fn default_row_converter_avx512<
126 const AR30_LAYOUT: usize,
127 const AR30_BYTE_ORDER: usize,
128 const RGBA_LAYOUT: u8,
129>(
130 src: &[u8],
131 dst: &mut [u8],
132) {
133 default_row_converter::<AR30_LAYOUT, AR30_BYTE_ORDER, RGBA_LAYOUT>(src, dst);
134}
135
136#[cfg(all(any(target_arch = "x86", target_arch = "x86_64"), feature = "avx"))]
137#[target_feature(enable = "avx2")]
138unsafe fn default_row_converter_avx2<
139 const AR30_LAYOUT: usize,
140 const AR30_BYTE_ORDER: usize,
141 const RGBA_LAYOUT: u8,
142>(
143 src: &[u8],
144 dst: &mut [u8],
145) {
146 default_row_converter::<AR30_LAYOUT, AR30_BYTE_ORDER, RGBA_LAYOUT>(src, dst);
147}
148
149#[cfg(all(any(target_arch = "x86", target_arch = "x86_64"), feature = "sse"))]
150#[target_feature(enable = "sse4.1")]
151unsafe fn default_row_converter_sse4_1<
152 const AR30_LAYOUT: usize,
153 const AR30_BYTE_ORDER: usize,
154 const RGBA_LAYOUT: u8,
155>(
156 src: &[u8],
157 dst: &mut [u8],
158) {
159 default_row_converter::<AR30_LAYOUT, AR30_BYTE_ORDER, RGBA_LAYOUT>(src, dst);
160}
161
162trait ConverterFactory<V> {
163 fn make_converter<
164 const AR30_LAYOUT: usize,
165 const AR30_BYTE_ORDER: usize,
166 const RGBA_LAYOUT: u8,
167 const BIT_DEPTH: usize,
168 >() -> RgbRa30RowHandler<V>;
169}
170
171impl ConverterFactory<u8> for u8 {
172 fn make_converter<
173 const AR30_LAYOUT: usize,
174 const AR30_BYTE_ORDER: usize,
175 const RGBA_LAYOUT: u8,
176 const BIT_DEPTH: usize,
177 >() -> RgbRa30RowHandler<u8> {
178 #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
179 {
180 #[cfg(feature = "nightly_avx512")]
181 {
182 if std::arch::is_x86_feature_detected!("avx512bw") {
183 return default_row_converter_avx512::<AR30_LAYOUT, AR30_BYTE_ORDER, RGBA_LAYOUT>;
184 }
185 }
186 #[cfg(feature = "avx")]
187 {
188 if std::arch::is_x86_feature_detected!("avx2") {
189 return default_row_converter_avx2::<AR30_LAYOUT, AR30_BYTE_ORDER, RGBA_LAYOUT>;
190 }
191 }
192 #[cfg(feature = "sse")]
193 {
194 if std::arch::is_x86_feature_detected!("sse4.1") {
195 return default_row_converter_sse4_1::<AR30_LAYOUT, AR30_BYTE_ORDER, RGBA_LAYOUT>;
196 }
197 }
198 }
199 default_row_converter::<AR30_LAYOUT, AR30_BYTE_ORDER, RGBA_LAYOUT>
200 }
201}
202
203#[cfg(all(
204 any(target_arch = "x86", target_arch = "x86_64"),
205 feature = "nightly_avx512"
206))]
207#[target_feature(enable = "avx512bw")]
208unsafe fn default_row_converter_hb_avx512<
209 const AR30_LAYOUT: usize,
210 const AR30_BYTE_ORDER: usize,
211 const RGBA_LAYOUT: u8,
212 const BIT_DEPTH: usize,
213>(
214 src: &[u16],
215 dst: &mut [u8],
216) {
217 default_row_converter_hb::<AR30_LAYOUT, AR30_BYTE_ORDER, RGBA_LAYOUT, BIT_DEPTH>(src, dst);
218}
219
220#[cfg(all(any(target_arch = "x86", target_arch = "x86_64"), feature = "avx"))]
221#[target_feature(enable = "avx2")]
222unsafe fn default_row_converter_hb_avx2<
223 const AR30_LAYOUT: usize,
224 const AR30_BYTE_ORDER: usize,
225 const RGBA_LAYOUT: u8,
226 const BIT_DEPTH: usize,
227>(
228 src: &[u16],
229 dst: &mut [u8],
230) {
231 default_row_converter_hb::<AR30_LAYOUT, AR30_BYTE_ORDER, RGBA_LAYOUT, BIT_DEPTH>(src, dst);
232}
233
234#[cfg(all(any(target_arch = "x86", target_arch = "x86_64"), feature = "sse"))]
235#[target_feature(enable = "sse4.1")]
236unsafe fn default_row_converter_hb_sse4_1<
237 const AR30_LAYOUT: usize,
238 const AR30_BYTE_ORDER: usize,
239 const RGBA_LAYOUT: u8,
240 const BIT_DEPTH: usize,
241>(
242 src: &[u16],
243 dst: &mut [u8],
244) {
245 default_row_converter_hb::<AR30_LAYOUT, AR30_BYTE_ORDER, RGBA_LAYOUT, BIT_DEPTH>(src, dst);
246}
247
248impl ConverterFactory<u16> for u16 {
249 fn make_converter<
250 const AR30_LAYOUT: usize,
251 const AR30_BYTE_ORDER: usize,
252 const RGBA_LAYOUT: u8,
253 const BIT_DEPTH: usize,
254 >() -> RgbRa30RowHandler<u16> {
255 #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
256 {
257 #[cfg(feature = "nightly_avx512")]
258 {
259 if std::arch::is_x86_feature_detected!("avx512bw") {
260 return default_row_converter_hb_avx512::<
261 AR30_LAYOUT,
262 AR30_BYTE_ORDER,
263 RGBA_LAYOUT,
264 BIT_DEPTH,
265 >;
266 }
267 }
268 #[cfg(feature = "avx")]
269 {
270 if std::arch::is_x86_feature_detected!("avx2") {
271 return default_row_converter_hb_avx2::<
272 AR30_LAYOUT,
273 AR30_BYTE_ORDER,
274 RGBA_LAYOUT,
275 BIT_DEPTH,
276 >;
277 }
278 }
279 #[cfg(feature = "sse")]
280 {
281 if std::arch::is_x86_feature_detected!("sse4.1") {
282 return default_row_converter_hb_sse4_1::<
283 AR30_LAYOUT,
284 AR30_BYTE_ORDER,
285 RGBA_LAYOUT,
286 BIT_DEPTH,
287 >;
288 }
289 }
290 }
291 default_row_converter_hb::<AR30_LAYOUT, AR30_BYTE_ORDER, RGBA_LAYOUT, BIT_DEPTH>
292 }
293}
294
295#[inline]
296fn rgb_to_ar30_impl<
297 V: Copy + 'static + ConverterFactory<V>,
298 const AR30_LAYOUT: usize,
299 const AR30_BYTE_ORDER: usize,
300 const RGBA_LAYOUT: u8,
301 const BIT_DEPTH: usize,
302>(
303 ar30: &mut [u8],
304 ar30_stride: u32,
305 rgba: &[V],
306 rgba_stride: u32,
307 width: u32,
308 height: u32,
309) -> Result<(), YuvError> {
310 let rgba_layout: YuvSourceChannels = RGBA_LAYOUT.into();
311 check_rgba_destination(ar30, ar30_stride, width, height, 4)?;
312 check_rgba_destination(
313 rgba,
314 rgba_stride,
315 width,
316 height,
317 rgba_layout.get_channels_count(),
318 )?;
319
320 let row_handler = V::make_converter::<AR30_LAYOUT, AR30_BYTE_ORDER, RGBA_LAYOUT, BIT_DEPTH>();
321
322 for (src, dst) in rgba
323 .chunks_exact(rgba_stride as usize)
324 .zip(ar30.chunks_exact_mut(ar30_stride as usize))
325 {
326 let src = &src[0..width as usize * rgba_layout.get_channels_count()];
327 let dst = &mut dst[0..width as usize * 4];
328 unsafe {
329 row_handler(src, dst);
330 }
331 }
332 Ok(())
333}
334
335macro_rules! rgb102_cnv {
336 (
337 $method_name:ident,
338 $ab_format:expr,
339 $ab_f_format:expr,
340 $ab_l_format:expr,
341 $ar_dst: expr,
342 $rgb_src: expr,
343 $rgb_s: expr,
344 $rgb_l: expr,
345 $tpz: ident,
346 $bp: expr
347 ) => {
348 #[doc = concat!("Converts ",$rgb_l," ", stringify!($bp)," bit depth to ",$ab_format, " (", $ab_f_format, ")\n",
349 "# Arguments
350
351* `", $ab_l_format, "`: Dest ", $ab_format, " data
352* `", $ab_l_format, "_stride`: Dest ", $ab_format, " stride
353* `byte_order`: See [Rgb30ByteOrder] for more info
354* `", $rgb_s,"`: Destination ",$rgb_l, stringify!($bp)," data
355* `", $rgb_s,"_stride`: Destination ",$rgb_l, stringify!($bp)," stride
356* `width`: Image width
357* `height`: Image height")]
358 pub fn $method_name(
359 ar30: &mut [u8],
360 ar30_stride: u32,
361 byte_order: Rgb30ByteOrder,
362 rgb: &[$tpz],
363 rgb_stride: u32,
364 width: u32,
365 height: u32,
366 ) -> Result<(), YuvError> {
367 match byte_order {
368 Rgb30ByteOrder::Host => rgb_to_ar30_impl::<
369 $tpz,
370 { $ar_dst as usize },
371 { Rgb30ByteOrder::Host as usize },
372 { $rgb_src as u8 },
373 $bp,
374 >(ar30, ar30_stride, rgb, rgb_stride, width, height),
375 Rgb30ByteOrder::Network => {
376 rgb_to_ar30_impl::<
377 $tpz,
378 { $ar_dst as usize },
379 { Rgb30ByteOrder::Network as usize },
380 { $rgb_src as u8 },
381 $bp,
382 >(ar30, ar30_stride, rgb, rgb_stride, width, height)
383 }
384 }
385 }
386 };
387}
388
389rgb102_cnv!(
390 rgb8_to_ar30,
391 "AR30",
392 "ARGB2101010",
393 "ar30",
394 Rgb30::Ar30,
395 YuvSourceChannels::Rgb,
396 "rgb",
397 "RGB",
398 u8,
399 8
400);
401rgb102_cnv!(
402 rgb8_to_ra30,
403 "RA30",
404 "RGBA1010102",
405 "ra30",
406 Rgb30::Ra30,
407 YuvSourceChannels::Rgb,
408 "rgb",
409 "RGB",
410 u8,
411 8
412);
413rgb102_cnv!(
414 rgba8_to_ar30,
415 "AR30",
416 "ARGB2101010",
417 "ar30",
418 Rgb30::Ar30,
419 YuvSourceChannels::Rgba,
420 "rgba",
421 "RGBA",
422 u8,
423 8
424);
425rgb102_cnv!(
426 rgba8_to_ra30,
427 "RA30",
428 "RGBA1010102",
429 "ra30",
430 Rgb30::Ra30,
431 YuvSourceChannels::Rgba,
432 "rgba",
433 "RGBA",
434 u8,
435 8
436);
437
438rgb102_cnv!(
439 rgb10_to_ar30,
440 "AR30",
441 "ARGB2101010",
442 "ar30",
443 Rgb30::Ar30,
444 YuvSourceChannels::Rgb,
445 "rgb",
446 "RGB",
447 u16,
448 10
449);
450rgb102_cnv!(
451 rgb10_to_ra30,
452 "RA30",
453 "RGBA1010102",
454 "ra30",
455 Rgb30::Ra30,
456 YuvSourceChannels::Rgb,
457 "rgb",
458 "RGB",
459 u16,
460 10
461);
462rgb102_cnv!(
463 rgb12_to_ar30,
464 "AR30",
465 "ARGB2101010",
466 "ar30",
467 Rgb30::Ar30,
468 YuvSourceChannels::Rgb,
469 "rgb",
470 "RGB",
471 u16,
472 12
473);
474rgb102_cnv!(
475 rgb12_to_ra30,
476 "RA30",
477 "RGBA1010102",
478 "ra30",
479 Rgb30::Ra30,
480 YuvSourceChannels::Rgb,
481 "rgb",
482 "RGB",
483 u16,
484 12
485);
486
487rgb102_cnv!(
488 rgba10_to_ar30,
489 "AR30",
490 "ARGB2101010",
491 "ar30",
492 Rgb30::Ar30,
493 YuvSourceChannels::Rgba,
494 "rgba",
495 "RGBA",
496 u16,
497 10
498);
499rgb102_cnv!(
500 rgba10_to_ra30,
501 "RA30",
502 "RGBA1010102",
503 "ra30",
504 Rgb30::Ra30,
505 YuvSourceChannels::Rgba,
506 "rgba",
507 "RGBA",
508 u16,
509 10
510);
511rgb102_cnv!(
512 rgba12_to_ar30,
513 "AR30",
514 "ARGB2101010",
515 "ar30",
516 Rgb30::Ar30,
517 YuvSourceChannels::Rgba,
518 "rgba",
519 "RGBA",
520 u16,
521 12
522);
523rgb102_cnv!(
524 rgba12_to_ra30,
525 "RA30",
526 "RGBA1010102",
527 "ra30",
528 Rgb30::Ra30,
529 YuvSourceChannels::Rgba,
530 "rgba",
531 "RGBA",
532 u16,
533 12
534);
535
536#[cfg(test)]
537mod tests {
538 use super::*;
539 use crate::ar30_to_rgb8;
540 use rand::Rng;
541
542 #[test]
543 fn ar30_rgb_round_trip_host() {
544 let image_width = 64usize;
545 let image_height = 64usize;
546
547 let random_point_x = rand::rng().random_range(0..image_width);
548 let random_point_y = rand::rng().random_range(0..image_height);
549
550 let random_r = rand::rng().random_range(0..255) as u8;
551 let random_g = rand::rng().random_range(0..255) as u8;
552 let random_b = rand::rng().random_range(0..255) as u8;
553
554 const CN: usize = 3;
555
556 let mut source_rgb = vec![0u8; image_width * image_height * CN];
557
558 for chunk in source_rgb.chunks_exact_mut(CN) {
559 chunk[0] = random_r;
560 chunk[1] = random_g;
561 chunk[2] = random_b;
562 }
563
564 let mut dst_ar30 = vec![0u8; image_width * image_height * 4];
565 rgb8_to_ar30(
566 &mut dst_ar30,
567 image_width as u32 * 4,
568 Rgb30ByteOrder::Host,
569 &source_rgb,
570 image_width as u32 * CN as u32,
571 image_width as u32,
572 image_height as u32,
573 )
574 .unwrap();
575
576 ar30_to_rgb8(
577 &dst_ar30,
578 image_width as u32 * 4,
579 Rgb30ByteOrder::Host,
580 &mut source_rgb,
581 image_width as u32 * CN as u32,
582 image_width as u32,
583 image_height as u32,
584 )
585 .unwrap();
586
587 assert_eq!(
588 source_rgb[random_point_x * CN],
589 random_r,
590 "R invalid {}, expected {} Point ({}, {})",
591 source_rgb[random_point_x * CN],
592 random_r,
593 random_point_x,
594 random_point_y
595 );
596 assert_eq!(
597 source_rgb[random_point_x * CN + 1],
598 random_g,
599 "G invalid {}, expected {} Point ({}, {})",
600 source_rgb[random_point_x * CN + 1],
601 random_r,
602 random_point_x,
603 random_point_y
604 );
605 assert_eq!(
606 source_rgb[random_point_x * CN + 2],
607 random_b,
608 "B invalid {}, expected {} Point ({}, {})",
609 source_rgb[random_point_x * CN + 2],
610 random_r,
611 random_point_x,
612 random_point_y
613 );
614 }
615
616 #[test]
617 fn ar30_rgb_round_trip_network() {
618 let image_width = 64usize;
619 let image_height = 64usize;
620
621 let random_point_x = rand::rng().random_range(0..image_width);
622 let random_point_y = rand::rng().random_range(0..image_height);
623
624 let random_r = rand::rng().random_range(0..255) as u8;
625 let random_g = rand::rng().random_range(0..255) as u8;
626 let random_b = rand::rng().random_range(0..255) as u8;
627
628 const CN: usize = 3;
629
630 let mut source_rgb = vec![0u8; image_width * image_height * CN];
631
632 for chunk in source_rgb.chunks_exact_mut(CN) {
633 chunk[0] = random_r;
634 chunk[1] = random_g;
635 chunk[2] = random_b;
636 }
637
638 let mut dst_ar30 = vec![0u8; image_width * image_height * 4];
639 rgb8_to_ar30(
640 &mut dst_ar30,
641 image_width as u32 * 4,
642 Rgb30ByteOrder::Network,
643 &source_rgb,
644 image_width as u32 * CN as u32,
645 image_width as u32,
646 image_height as u32,
647 )
648 .unwrap();
649
650 ar30_to_rgb8(
651 &dst_ar30,
652 image_width as u32 * 4,
653 Rgb30ByteOrder::Network,
654 &mut source_rgb,
655 image_width as u32 * CN as u32,
656 image_width as u32,
657 image_height as u32,
658 )
659 .unwrap();
660
661 assert_eq!(
662 source_rgb[random_point_x * CN],
663 random_r,
664 "R invalid {}, expected {} Point ({}, {})",
665 source_rgb[random_point_x * CN],
666 random_r,
667 random_point_x,
668 random_point_y
669 );
670 assert_eq!(
671 source_rgb[random_point_x * CN + 1],
672 random_g,
673 "G invalid {}, expected {} Point ({}, {})",
674 source_rgb[random_point_x * CN + 1],
675 random_r,
676 random_point_x,
677 random_point_y
678 );
679 assert_eq!(
680 source_rgb[random_point_x * CN + 2],
681 random_b,
682 "B invalid {}, expected {} Point ({}, {})",
683 source_rgb[random_point_x * CN + 2],
684 random_r,
685 random_point_x,
686 random_point_y
687 );
688 }
689}