use crate::clip::ClippedRect;
use crate::stride::{RGB, RGBA};
use bytemuck::{cast_slice, cast_slice_mut};
pub use glam::Vec4;
use std::ops::Deref;
pub fn rgb8_to_rgba32_in_place(src: &[u8], dst: &mut [Vec4]) {
cast_slice::<u8, [u8; RGB]>(src)
.iter()
.zip(dst)
.for_each(|(src, dst)| {
dst.x = src[0] as f32 / 255.;
dst.y = src[1] as f32 / 255.;
dst.z = src[2] as f32 / 255.;
dst.w = 1.;
})
}
pub fn rgba8_to_rgba32_in_place(src: &[u8], dst: &mut [Vec4]) {
cast_slice::<u8, [u8; RGBA]>(src)
.iter()
.zip(dst)
.for_each(|(src, dst)| {
dst.x = src[0] as f32 / 255.;
dst.y = src[1] as f32 / 255.;
dst.z = src[2] as f32 / 255.;
dst.w = src[3] as f32 / 255.;
});
}
pub fn rgba32_to_rgb8_in_place(src: &[Vec4], dst: &mut [u8]) {
src.iter()
.zip(cast_slice_mut::<u8, [u8; RGB]>(dst))
.for_each(|(src, dst)| {
dst[0] = (src.x * 255.).ceil() as u8;
dst[1] = (src.y * 255.).ceil() as u8;
dst[2] = (src.z * 255.).ceil() as u8;
})
}
pub fn rgba32_to_rgba8_in_place(src: &[Vec4], dst: &mut [u8]) {
src.iter()
.zip(cast_slice_mut::<u8, [u8; RGBA]>(dst))
.for_each(|(src, dst)| {
dst[0] = (src.x * 255.).ceil() as u8;
dst[1] = (src.y * 255.).ceil() as u8;
dst[2] = (src.z * 255.).ceil() as u8;
dst[3] = (src.w * 255.).ceil() as u8;
})
}
pub fn rgb8_to_rgba32(src: &[u8]) -> Vec<Vec4> {
let mut dst = vec![Vec4::default(); src.len() / RGB];
rgb8_to_rgba32_in_place(src, &mut dst);
dst
}
pub fn rgba8_to_rgba32(src: &[u8]) -> Vec<Vec4> {
let mut dst = vec![Vec4::default(); src.len() / RGBA];
rgba8_to_rgba32_in_place(src, &mut dst);
dst
}
pub fn rgba32_to_rgb8(src: &[Vec4]) -> Vec<u8> {
let mut dst = vec![0; src.len() * RGB];
rgba32_to_rgb8_in_place(src, &mut dst);
dst
}
pub fn rgba32_to_rgba8(src: &[Vec4]) -> Vec<u8> {
let mut dst = vec![0; src.len() * RGBA];
rgba32_to_rgba8_in_place(src, &mut dst);
dst
}
pub const fn rgb8_to_rgba32_color(color: &[u8; 3]) -> Vec4 {
Vec4::new(
color[0] as f32 / 255.,
color[1] as f32 / 255.,
color[2] as f32 / 255.,
1.,
)
}
pub const fn rgba8_to_rgba32_color(color: &[u8; 4]) -> Vec4 {
Vec4::new(
color[0] as f32 / 255.,
color[1] as f32 / 255.,
color[2] as f32 / 255.,
color[3] as f32 / 255.,
)
}
#[inline]
pub fn rgba32_to_rgb8_color(color: &Vec4) -> [u8; 3] {
let color = color.deref();
[
(color.x * 255.).ceil() as u8,
(color.y * 255.).ceil() as u8,
(color.z * 255.).ceil() as u8,
]
}
#[inline]
pub fn rgba32_to_rgba8_color(color: &Vec4) -> [u8; 4] {
let color = color.deref();
[
(color.x * 255.).ceil() as u8,
(color.y * 255.).ceil() as u8,
(color.z * 255.).ceil() as u8,
(color.w * 255.).ceil() as u8,
]
}
pub fn overlay_rgb8(src: &[u8], dst: &mut [Vec4], rect: &ClippedRect, alpha: u8) -> Vec<Vec4> {
if alpha > 0 {
let src = rgb8_to_rgba32(src);
(0..rect.src_size_clipped.h).for_each(|src_y| {
let src_index = get_index(0, src_y, rect.src_size.w);
let dst_index = get_index(
rect.dst_position_clipped.x,
rect.dst_position_clipped.y + src_y,
rect.dst_size.w,
);
src[src_index..src_index + rect.src_size_clipped.w]
.iter()
.zip(dst[dst_index..dst_index + rect.src_size_clipped.w].iter_mut())
.for_each(|(src, dst)| {
if alpha == 255 {
dst.x = src.x;
dst.y = src.y;
dst.z = src.z;
dst.w = 1.;
} else {
overlay_pixel(src, dst);
}
});
});
src
} else {
Vec::default()
}
}
pub fn overlay_rgba8(src: &[u8], dst: &mut [Vec4], rect: &ClippedRect) -> Vec<Vec4> {
let src = rgba8_to_rgba32(src);
overlay_rgba32(&src, dst, rect);
src
}
pub fn overlay_rgba32(src: &[Vec4], dst: &mut [Vec4], rect: &ClippedRect) {
(0..rect.src_size_clipped.h).for_each(|src_y| {
let src_index = get_index(0, src_y, rect.src_size.w);
let dst_index = get_index(
rect.dst_position_clipped.x,
rect.dst_position_clipped.y + src_y,
rect.dst_size.w,
);
src[src_index..src_index + rect.src_size_clipped.w]
.iter()
.zip(dst[dst_index..dst_index + rect.src_size_clipped.w].iter_mut())
.for_each(|(src, dst)| {
overlay_pixel(src, dst);
});
});
}
#[inline]
pub fn overlay_pixel(src: &Vec4, dst: &mut Vec4) {
let src_a = src.w;
*dst = *src * src_a + *dst * (1. - src_a);
}
const fn get_index(x: usize, y: usize, w: usize) -> usize {
x + y * w
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{PositionI, Size};
#[test]
fn test_rgb8_to_rgba32() {
let src = [[100, 70, 200]; 1024];
let casted = cast_slice::<[u8; 3], u8>(&src);
let vec4s = rgb8_to_rgba32(casted);
assert_eq!(vec4s.len(), src.len());
casted
.iter()
.zip(rgba32_to_rgb8(&vec4s))
.for_each(|(a, b)| assert_eq!(*a, b));
}
#[test]
fn test_rgba8_to_rgba32() {
let src = [[100, 70, 200, 90]; 1024];
let casted = cast_slice::<[u8; 4], u8>(&src);
let vec4s = rgba8_to_rgba32(casted);
assert_eq!(vec4s.len(), src.len());
casted
.iter()
.zip(rgba32_to_rgba8(&vec4s))
.for_each(|(a, b)| assert_eq!(*a, b));
}
#[test]
fn test_rgb8_overlay() {
let src = [[100, 70, 200]; 1024];
let src_casted = cast_slice::<[u8; 3], u8>(&src);
let dst = [[100, 70, 200, 90]; 1024];
let dst_casted = cast_slice::<[u8; 4], u8>(&dst);
let mut dst_vec4s = rgba8_to_rgba32(dst_casted);
let size = Size { w: 32, h: 32 };
let position = PositionI::default();
dst_casted
.iter()
.zip(rgba32_to_rgba8(&dst_vec4s))
.for_each(|(a, b)| assert_eq!(*a, b));
let rect = ClippedRect::new(position, size, size).unwrap();
overlay_rgb8(src_casted, &mut dst_vec4s, &rect, 50);
cast_slice::<u8, [u8; 4]>(&rgba32_to_rgba8(&dst_vec4s))
.into_iter()
.for_each(|pixel| assert_eq!(*pixel, [100, 70, 200, 173]));
let src = [[255, 255, 200]; 1024];
let src_casted = cast_slice::<[u8; 3], u8>(&src);
overlay_rgb8(src_casted, &mut dst_vec4s, &rect, 255);
cast_slice::<u8, [u8; 4]>(&rgba32_to_rgba8(&dst_vec4s))
.into_iter()
.for_each(|pixel| assert_eq!(*pixel, [255, 255, 200, 255]));
}
}