styx_codec/decoder/raw/
rgb48.rs1use styx_core::prelude::*;
2
3#[cfg(feature = "image")]
4use crate::decoder::{ImageDecode, process_to_dynamic};
5use crate::{Codec, CodecDescriptor, CodecError, CodecKind};
6use rayon::prelude::*;
7
8#[cfg(target_arch = "aarch64")]
9#[inline(always)]
10unsafe fn rgb48_row_to_rgb24_neon(src: &[u8], dst: &mut [u8], width: usize, swap_rb: bool) -> bool {
11 use std::arch::aarch64::{uint8x8x3_t, vld3q_u16, vshrn_n_u16, vst3_u8};
12
13 if (src.as_ptr() as usize) & 0x1 != 0 {
15 return false;
16 }
17 debug_assert!(src.len() >= width * 6);
18 debug_assert!(dst.len() >= width * 3);
19
20 let src_u16 = src.as_ptr() as *const u16;
21 let dst_ptr = dst.as_mut_ptr();
22 let mut x = 0usize;
23 while x + 8 <= width {
24 unsafe {
25 let rgb16 = vld3q_u16(src_u16.add(x * 3));
26 let (r16, g16, b16) = if swap_rb {
27 (rgb16.2, rgb16.1, rgb16.0)
28 } else {
29 (rgb16.0, rgb16.1, rgb16.2)
30 };
31 let r8 = vshrn_n_u16(r16, 8);
32 let g8 = vshrn_n_u16(g16, 8);
33 let b8 = vshrn_n_u16(b16, 8);
34 vst3_u8(dst_ptr.add(x * 3), uint8x8x3_t(r8, g8, b8));
35 }
36 x += 8;
37 }
38 for x in x..width {
39 unsafe {
40 let si = x * 6;
41 let r16 = u16::from_le_bytes([*src.get_unchecked(si), *src.get_unchecked(si + 1)]);
42 let g16 = u16::from_le_bytes([*src.get_unchecked(si + 2), *src.get_unchecked(si + 3)]);
43 let b16 = u16::from_le_bytes([*src.get_unchecked(si + 4), *src.get_unchecked(si + 5)]);
44 let (r16, g16, b16) = if swap_rb { (b16, g16, r16) } else { (r16, g16, b16) };
45 let di = x * 3;
46 *dst_ptr.add(di) = (r16 >> 8) as u8;
47 *dst_ptr.add(di + 1) = (g16 >> 8) as u8;
48 *dst_ptr.add(di + 2) = (b16 >> 8) as u8;
49 }
50 }
51 true
52}
53
54pub struct Rgb48ToRgbDecoder {
56 descriptor: CodecDescriptor,
57 pool: BufferPool,
58 swap_rb: bool,
59}
60
61impl Rgb48ToRgbDecoder {
62 pub fn new(
63 input: FourCc,
64 impl_name: &'static str,
65 swap_rb: bool,
66 max_width: u32,
67 max_height: u32,
68 ) -> Self {
69 let bytes = max_width as usize * max_height as usize * 3;
70 Self {
71 descriptor: CodecDescriptor {
72 kind: CodecKind::Decoder,
73 input,
74 output: FourCc::new(*b"RG24"),
75 name: "rgb48torgb24",
76 impl_name,
77 },
78 pool: BufferPool::with_limits(2, bytes, 4),
79 swap_rb,
80 }
81 }
82
83 pub fn decode_into(&self, input: &FrameLease, dst: &mut [u8]) -> Result<FrameMeta, CodecError> {
87 let meta = input.meta();
88 if meta.format.code != self.descriptor.input {
89 return Err(CodecError::FormatMismatch {
90 expected: self.descriptor.input,
91 actual: meta.format.code,
92 });
93 }
94 let plane = input
95 .planes()
96 .into_iter()
97 .next()
98 .ok_or_else(|| CodecError::Codec("rgb48 frame missing plane".into()))?;
99 let width = meta.format.resolution.width.get() as usize;
100 let height = meta.format.resolution.height.get() as usize;
101 let stride = plane.stride().max(width * 6);
102 let required = stride
103 .checked_mul(height)
104 .ok_or_else(|| CodecError::Codec("rgb48 stride overflow".into()))?;
105 if plane.data().len() < required {
106 return Err(CodecError::Codec("rgb48 plane buffer too short".into()));
107 }
108
109 let row_bytes = width
110 .checked_mul(3)
111 .ok_or_else(|| CodecError::Codec("rgb48 output overflow".into()))?;
112 let out_len = row_bytes
113 .checked_mul(height)
114 .ok_or_else(|| CodecError::Codec("rgb48 output overflow".into()))?;
115 if dst.len() < out_len {
116 return Err(CodecError::Codec("rgb48 dst buffer too short".into()));
117 }
118
119 let data = plane.data();
120 let swap_rb = self.swap_rb;
121 dst[..out_len]
122 .par_chunks_mut(row_bytes)
123 .enumerate()
124 .for_each(|(y, dst_line)| {
125 let src_line = &data[y * stride..][..width * 6];
126 #[cfg(target_arch = "aarch64")]
127 unsafe {
128 if rgb48_row_to_rgb24_neon(src_line, dst_line, width, swap_rb) {
129 return;
130 }
131 }
132 for (dst_px, src_px) in dst_line
133 .chunks_exact_mut(3)
134 .zip(src_line.chunks_exact(6))
135 {
136 let r16 = u16::from_le_bytes([src_px[0], src_px[1]]);
137 let g16 = u16::from_le_bytes([src_px[2], src_px[3]]);
138 let b16 = u16::from_le_bytes([src_px[4], src_px[5]]);
139 let (r16, g16, b16) = if swap_rb {
140 (b16, g16, r16)
141 } else {
142 (r16, g16, b16)
143 };
144 dst_px[0] = (r16 >> 8) as u8;
145 dst_px[1] = (g16 >> 8) as u8;
146 dst_px[2] = (b16 >> 8) as u8;
147 }
148 });
149
150 Ok(FrameMeta::new(
151 MediaFormat::new(
152 self.descriptor.output,
153 meta.format.resolution,
154 meta.format.color,
155 ),
156 meta.timestamp,
157 ))
158 }
159}
160
161impl Codec for Rgb48ToRgbDecoder {
162 fn descriptor(&self) -> &CodecDescriptor {
163 &self.descriptor
164 }
165
166 fn process(&self, input: FrameLease) -> Result<FrameLease, CodecError> {
167 let layout = plane_layout_from_dims(
168 input.meta().format.resolution.width,
169 input.meta().format.resolution.height,
170 3,
171 );
172 let mut buf = self.pool.lease();
173 unsafe { buf.resize_uninit(layout.len) };
174 let meta = self.decode_into(&input, buf.as_mut_slice())?;
175
176 Ok(unsafe {
177 FrameLease::single_plane_uninit(meta, buf, layout.len, layout.stride)
178 })
179 }
180}
181
182#[cfg(feature = "image")]
183impl ImageDecode for Rgb48ToRgbDecoder {
184 fn decode_image(&self, frame: FrameLease) -> Result<image::DynamicImage, CodecError> {
185 process_to_dynamic(self, frame)
186 }
187}