mod swizzle_simd;
#[derive(Clone, Copy)]
pub struct Rgba4 {
pub r: usize,
pub g: usize,
pub b: usize,
pub a: usize,
}
pub const RGBA_POS: Rgba4 = Rgba4 {
r: 0,
g: 1,
b: 2,
a: 3,
};
pub const BGRA_POS: Rgba4 = Rgba4 {
r: 2,
g: 1,
b: 0,
a: 3,
};
pub const ARGB_POS: Rgba4 = Rgba4 {
r: 1,
g: 2,
b: 3,
a: 0,
};
pub const ABGR_POS: Rgba4 = Rgba4 {
r: 3,
g: 2,
b: 1,
a: 0,
};
#[derive(Clone, Copy)]
pub struct Rgb3 {
pub r: usize,
pub g: usize,
pub b: usize,
}
pub const RGB_POS: Rgb3 = Rgb3 { r: 0, g: 1, b: 2 };
pub const BGR_POS: Rgb3 = Rgb3 { r: 2, g: 1, b: 0 };
pub fn swizzle3(src: &[u8], src_pos: Rgb3, dst: &mut [u8], dst_pos: Rgb3, pixels: usize) {
debug_assert!(src.len() >= pixels * 3 && dst.len() >= pixels * 3);
let mut perm = [0u8; 3];
perm[dst_pos.r] = src_pos.r as u8;
perm[dst_pos.g] = src_pos.g as u8;
perm[dst_pos.b] = src_pos.b as u8;
swizzle_simd::swizzle3_perm(src, dst, pixels, perm);
}
pub fn swizzle4(src: &[u8], src_pos: Rgba4, dst: &mut [u8], dst_pos: Rgba4, pixels: usize) {
debug_assert!(src.len() >= pixels * 4 && dst.len() >= pixels * 4);
let mut perm = [0u8; 4];
perm[dst_pos.r] = src_pos.r as u8;
perm[dst_pos.g] = src_pos.g as u8;
perm[dst_pos.b] = src_pos.b as u8;
perm[dst_pos.a] = src_pos.a as u8;
swizzle_simd::swizzle4_perm(src, dst, pixels, perm);
}
pub fn rgb3_to_rgba4(src: &[u8], src_pos: Rgb3, dst: &mut [u8], dst_pos: Rgba4, pixels: usize) {
let mut perm3 = [0xFFu8; 4];
perm3[dst_pos.r] = src_pos.r as u8;
perm3[dst_pos.g] = src_pos.g as u8;
perm3[dst_pos.b] = src_pos.b as u8;
swizzle_simd::rgb3_to_rgba4_perm(src, dst, pixels, perm3);
}
pub fn rgba4_to_rgb3(src: &[u8], src_pos: Rgba4, dst: &mut [u8], dst_pos: Rgb3, pixels: usize) {
let mut perm4 = [0u8; 3];
perm4[dst_pos.r] = src_pos.r as u8;
perm4[dst_pos.g] = src_pos.g as u8;
perm4[dst_pos.b] = src_pos.b as u8;
swizzle_simd::rgba4_to_rgb3_perm(src, dst, pixels, perm4);
}
pub fn rgb48_to_rgb24(src: &[u8], dst: &mut [u8], pixels: usize) {
if crate::simd_dispatch::has_avx2() {
#[cfg(target_arch = "x86_64")]
unsafe {
rgb48_to_rgb24_avx2(src, dst, pixels);
return;
}
}
for i in 0..pixels {
dst[i * 3] = src[i * 6 + 1];
dst[i * 3 + 1] = src[i * 6 + 3];
dst[i * 3 + 2] = src[i * 6 + 5];
}
}
#[cfg(target_arch = "x86_64")]
#[target_feature(enable = "avx2")]
unsafe fn rgb48_to_rgb24_avx2(src: &[u8], dst: &mut [u8], pixels: usize) {
use core::arch::x86_64::*;
const SHUF: [u8; 16] = [
1, 3, 5, 7, 9, 11, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
];
let shuf = _mm_loadu_si128(SHUF.as_ptr() as *const __m128i);
let simd_pixels = pixels.saturating_sub(6);
let chunks = simd_pixels / 2;
for c in 0..chunks {
let soff = c * 12;
let doff = c * 6;
let v = _mm_loadu_si128(src.as_ptr().add(soff) as *const __m128i);
let out = _mm_shuffle_epi8(v, shuf);
_mm_storeu_si128(dst.as_mut_ptr().add(doff) as *mut __m128i, out);
}
let tail_start = chunks * 2;
for i in tail_start..pixels {
dst[i * 3] = src[i * 6 + 1];
dst[i * 3 + 1] = src[i * 6 + 3];
dst[i * 3 + 2] = src[i * 6 + 5];
}
}
pub fn rgb24_to_rgb48(src: &[u8], dst: &mut [u8], pixels: usize) {
for i in 0..pixels {
for c in 0..3 {
let b = src[i * 3 + c];
let v: u16 = (b as u16) << 8 | (b as u16);
let off = i * 6 + c * 2;
dst[off] = (v & 0xFF) as u8;
dst[off + 1] = (v >> 8) as u8;
}
}
}
pub fn rgba64_to_rgba(src: &[u8], dst: &mut [u8], pixels: usize) {
for i in 0..pixels {
dst[i * 4] = src[i * 8 + 1];
dst[i * 4 + 1] = src[i * 8 + 3];
dst[i * 4 + 2] = src[i * 8 + 5];
dst[i * 4 + 3] = src[i * 8 + 7];
}
}
pub fn rgba_to_rgba64(src: &[u8], dst: &mut [u8], pixels: usize) {
for i in 0..pixels {
for c in 0..4 {
let b = src[i * 4 + c];
let v: u16 = (b as u16) << 8 | (b as u16);
let off = i * 8 + c * 2;
dst[off] = (v & 0xFF) as u8;
dst[off + 1] = (v >> 8) as u8;
}
}
}
pub fn rgb48_to_rgba64(src: &[u8], dst: &mut [u8], pixels: usize) {
for i in 0..pixels {
dst[i * 8..i * 8 + 6].copy_from_slice(&src[i * 6..i * 6 + 6]);
dst[i * 8 + 6] = 0xFF;
dst[i * 8 + 7] = 0xFF;
}
}
pub fn rgba64_to_rgb48(src: &[u8], dst: &mut [u8], pixels: usize) {
for i in 0..pixels {
dst[i * 6..i * 6 + 6].copy_from_slice(&src[i * 8..i * 8 + 6]);
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn rgb48_to_rgb24_does_not_write_past_dst() {
for pixels in 1..=64 {
let mut src = vec![0u8; pixels * 6];
for (i, b) in src.iter_mut().enumerate() {
*b = (i & 0xff) as u8;
}
let mut dst = vec![0u8; pixels * 3];
rgb48_to_rgb24(&src, &mut dst, pixels);
for i in 0..pixels {
assert_eq!(dst[i * 3], src[i * 6 + 1], "px {i}/{pixels} R");
assert_eq!(dst[i * 3 + 1], src[i * 6 + 3], "px {i}/{pixels} G");
assert_eq!(dst[i * 3 + 2], src[i * 6 + 5], "px {i}/{pixels} B");
}
}
}
#[test]
fn rgb48_to_rgb24_alloc_churn_does_not_corrupt_heap() {
for _ in 0..2000 {
let pixels = 32;
let src = vec![0xa5u8; pixels * 6];
let mut dst = vec![0u8; pixels * 3];
rgb48_to_rgb24(&src, &mut dst, pixels);
drop(dst);
}
}
}