use gamut_core::{Error, ImageRef, Result, Rgb8};
fn rgb_to_gbr_planes(rgb: &[u8], n: usize) -> [Vec<u8>; 3] {
let mut g = vec![0u8; n];
let mut b = vec![0u8; n];
let mut r = vec![0u8; n];
for i in 0..n {
r[i] = rgb[i * 3];
g[i] = rgb[i * 3 + 1];
b[i] = rgb[i * 3 + 2];
}
[g, b, r]
}
#[derive(Debug, Clone)]
pub struct Planar8 {
width: u32,
height: u32,
planes: [Vec<u8>; 3],
}
impl Planar8 {
pub fn from_rgb8_identity(rgb: &[u8], width: u32, height: u32) -> Result<Self> {
let n = width as usize * height as usize;
if rgb.len() != n * 3 {
return Err(Error::InvalidInput(
"rgb buffer length != width * height * 3",
));
}
Ok(Self {
width,
height,
planes: rgb_to_gbr_planes(rgb, n),
})
}
#[must_use]
pub fn from_rgb8_identity_view(img: ImageRef<'_, Rgb8>) -> Self {
let (width, height) = (img.width(), img.height());
let n = width as usize * height as usize;
Self {
width,
height,
planes: rgb_to_gbr_planes(img.as_samples(), n),
}
}
pub fn from_planes(width: u32, height: u32, planes: [Vec<u8>; 3]) -> Result<Self> {
let n = width as usize * height as usize;
if planes.iter().any(|p| p.len() != n) {
return Err(Error::InvalidInput("plane length != width * height"));
}
Ok(Self {
width,
height,
planes,
})
}
#[must_use]
pub fn to_rgb8_identity(&self) -> Vec<u8> {
let n = self.width as usize * self.height as usize;
let (g, b, r) = (&self.planes[0], &self.planes[1], &self.planes[2]);
let mut out = vec![0u8; n * 3];
for i in 0..n {
out[i * 3] = r[i];
out[i * 3 + 1] = g[i];
out[i * 3 + 2] = b[i];
}
out
}
#[must_use]
pub fn width(&self) -> u32 {
self.width
}
#[must_use]
pub fn height(&self) -> u32 {
self.height
}
#[must_use]
pub fn plane(&self, index: usize) -> &[u8] {
&self.planes[index]
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn rgb_identity_plane_order() {
let p = Planar8::from_rgb8_identity(&[10, 20, 30], 1, 1).unwrap();
assert_eq!(p.plane(0), &[20]);
assert_eq!(p.plane(1), &[30]);
assert_eq!(p.plane(2), &[10]);
}
#[test]
fn rgb_roundtrip() {
let rgb: Vec<u8> = (0..=200u8).cycle().take(2 * 3 * 3).collect(); let p = Planar8::from_rgb8_identity(&rgb, 3, 2).unwrap();
assert_eq!(p.width(), 3);
assert_eq!(p.height(), 2);
assert_eq!(p.to_rgb8_identity(), rgb);
}
#[test]
fn wrong_length_errors() {
assert!(Planar8::from_rgb8_identity(&[0, 1, 2, 3], 1, 1).is_err());
}
#[test]
fn view_ctor_matches_slice_ctor() {
let rgb: Vec<u8> = (0..=200u8).cycle().take(3 * 2 * 3).collect(); let from_slice = Planar8::from_rgb8_identity(&rgb, 3, 2).unwrap();
let view = ImageRef::<Rgb8>::new(&rgb, gamut_core::Dimensions::new(3, 2).unwrap()).unwrap();
let from_view = Planar8::from_rgb8_identity_view(view);
assert_eq!((from_view.width(), from_view.height()), (3, 2));
for i in 0..3 {
assert_eq!(from_view.plane(i), from_slice.plane(i));
}
}
}