styx_codec/decoder/raw/
bgra.rs

1use 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 bgra_row_to_rgb24_neon(src: &[u8], dst: &mut [u8], width: usize) {
11    use std::arch::aarch64::{uint8x16x3_t, vld4q_u8, vst3q_u8};
12    debug_assert!(src.len() >= width * 4);
13    debug_assert!(dst.len() >= width * 3);
14
15    let src_ptr = src.as_ptr();
16    let dst_ptr = dst.as_mut_ptr();
17
18    let mut x = 0usize;
19    while x + 16 <= width {
20        unsafe {
21            let bgra = vld4q_u8(src_ptr.add(x * 4));
22            let rgb = uint8x16x3_t(bgra.2, bgra.1, bgra.0);
23            vst3q_u8(dst_ptr.add(x * 3), rgb);
24        }
25        x += 16;
26    }
27    for x in x..width {
28        unsafe {
29            let si = x * 4;
30            let di = x * 3;
31            let b = *src_ptr.add(si);
32            let g = *src_ptr.add(si + 1);
33            let r = *src_ptr.add(si + 2);
34            *dst_ptr.add(di) = r;
35            *dst_ptr.add(di + 1) = g;
36            *dst_ptr.add(di + 2) = b;
37        }
38    }
39}
40
41/// BGRA → RGB24 decoder (drops alpha and reorders channels).
42pub struct BgraToRgbDecoder {
43    descriptor: CodecDescriptor,
44    pool: BufferPool,
45}
46
47impl BgraToRgbDecoder {
48    pub fn new(max_width: u32, max_height: u32) -> Self {
49        let bytes = max_width as usize * max_height as usize * 3;
50        Self::with_input(
51            BufferPool::with_limits(2, bytes, 4),
52            FourCc::new(*b"BGRA"),
53            "bgra-strip",
54        )
55    }
56
57    pub fn with_pool(pool: BufferPool) -> Self {
58        Self::with_input(pool, FourCc::new(*b"BGRA"), "bgra-strip")
59    }
60
61    pub fn with_input(pool: BufferPool, input: FourCc, impl_name: &'static str) -> Self {
62        Self {
63            descriptor: CodecDescriptor {
64                kind: CodecKind::Decoder,
65                input,
66                output: FourCc::new(*b"RG24"),
67                name: "bgra2rgb",
68                impl_name,
69            },
70            pool,
71        }
72    }
73
74    pub fn with_input_for_max(
75        input: FourCc,
76        impl_name: &'static str,
77        max_width: u32,
78        max_height: u32,
79    ) -> Self {
80        let bytes = max_width as usize * max_height as usize * 3;
81        Self::with_input(BufferPool::with_limits(2, bytes, 4), input, impl_name)
82    }
83
84    /// Decode into a caller-provided tightly-packed RGB24 buffer.
85    ///
86    /// `dst` must be at least `width * height * 3` bytes.
87    pub fn decode_into(&self, input: &FrameLease, dst: &mut [u8]) -> Result<FrameMeta, CodecError> {
88        let meta = input.meta();
89        if meta.format.code != self.descriptor.input {
90            return Err(CodecError::FormatMismatch {
91                expected: self.descriptor.input,
92                actual: meta.format.code,
93            });
94        }
95        let plane = input
96            .planes()
97            .into_iter()
98            .next()
99            .ok_or_else(|| CodecError::Codec("bgra frame missing plane".into()))?;
100
101        let width = meta.format.resolution.width.get() as usize;
102        let height = meta.format.resolution.height.get() as usize;
103        let stride = plane.stride().max(width * 4);
104        let required = stride
105            .checked_mul(height)
106            .ok_or_else(|| CodecError::Codec("bgra stride overflow".into()))?;
107        if plane.data().len() < required {
108            return Err(CodecError::Codec("bgra plane buffer too short".into()));
109        }
110
111        let row_bytes = width * 3;
112        let out_len = row_bytes
113            .checked_mul(height)
114            .ok_or_else(|| CodecError::Codec("bgra output overflow".into()))?;
115        if dst.len() < out_len {
116            return Err(CodecError::Codec("bgra dst buffer too short".into()));
117        }
118
119        let src = plane.data();
120        dst[..out_len]
121            .par_chunks_mut(row_bytes)
122            .enumerate()
123            .for_each(|(y, dst_line)| {
124                let src_line = &src[y * stride..][..width * 4];
125                #[cfg(target_arch = "aarch64")]
126                unsafe {
127                    bgra_row_to_rgb24_neon(src_line, dst_line, width);
128                    return;
129                }
130                #[cfg(not(target_arch = "aarch64"))]
131                {
132                    for (dst_px, src_px) in dst_line
133                        .chunks_exact_mut(3)
134                        .zip(src_line.chunks_exact(4))
135                    {
136                        dst_px[0] = src_px[2];
137                        dst_px[1] = src_px[1];
138                        dst_px[2] = src_px[0];
139                    }
140                }
141            });
142
143        Ok(FrameMeta::new(
144            MediaFormat::new(
145                self.descriptor.output,
146                meta.format.resolution,
147                meta.format.color,
148            ),
149            meta.timestamp,
150        ))
151    }
152}
153
154impl Codec for BgraToRgbDecoder {
155    fn descriptor(&self) -> &CodecDescriptor {
156        &self.descriptor
157    }
158
159    fn process(&self, input: FrameLease) -> Result<FrameLease, CodecError> {
160        let layout = plane_layout_from_dims(
161            input.meta().format.resolution.width,
162            input.meta().format.resolution.height,
163            3,
164        );
165        let mut buf = self.pool.lease();
166        unsafe { buf.resize_uninit(layout.len) };
167        let meta = self.decode_into(&input, buf.as_mut_slice())?;
168
169        Ok(unsafe {
170            FrameLease::single_plane_uninit(
171                meta,
172                buf,
173                layout.len,
174                layout.stride,
175            )
176        })
177    }
178}
179
180#[cfg(feature = "image")]
181impl ImageDecode for BgraToRgbDecoder {
182    fn decode_image(&self, frame: FrameLease) -> Result<image::DynamicImage, CodecError> {
183        process_to_dynamic(self, frame)
184    }
185}