use std::io;
use std::sync::Arc;
use super::{pixels, Color as _, Point2D, Rect, Rgba8, Size};
use crate::math::Zero;
pub const FILE_EXTENSION: &str = "rgba";
#[derive(Debug, Clone)]
pub struct Image {
pub size: Size<u32>,
pub pixels: Arc<[Rgba8]>,
}
impl Eq for Image {}
impl PartialEq for Image {
fn eq(&self, other: &Self) -> bool {
(Arc::ptr_eq(&self.pixels, &other.pixels) && self.size == other.size)
|| self.pixels == other.pixels
}
}
pub enum ImageError {
Decoding,
}
impl Image {
pub fn new(pixels: impl Into<Arc<[Rgba8]>>, size: impl Into<Size<u32>>) -> Self {
let size = size.into();
let pixels = pixels.into();
assert_eq!(size.area() as usize, pixels.len());
Self { size, pixels }
}
pub fn empty() -> Self {
Self {
size: Size::ZERO,
pixels: Arc::new([]),
}
}
pub fn scaled(&self, factor: u32) -> Self {
let scaled = pixels::scale(&self.pixels, self.size.w, self.size.h, factor);
Self {
size: self.size * factor,
pixels: scaled.into(),
}
}
pub fn rect(&self) -> Rect<u32> {
Rect::origin(self.size)
}
pub fn blank(size: impl Into<Size<u32>>) -> Self {
let size = size.into();
Self::new(vec![Rgba8::TRANSPARENT; size.area() as usize], size)
}
pub fn write(&self, mut w: impl io::Write) -> Result<usize, io::Error> {
let mut n = 0;
let (head, texels, tail) = unsafe { self.pixels.align_to::<u8>() };
assert!(head.is_empty() && tail.is_empty());
n += w.write(&[b'R', b'G', b'B', b'A'])?;
n += w.write(&u32::to_be_bytes(self.size.w))?;
n += w.write(&u32::to_be_bytes(self.size.h))?;
n += w.write(texels)?;
w.flush()?;
Ok(n)
}
pub fn sample(&self, point: Point2D<u32>) -> Option<&Rgba8> {
let offset = self.size.w * point.y + point.x;
self.pixels.get(offset as usize)
}
}
impl TryFrom<&[u8]> for Image {
type Error = ImageError;
fn try_from(bytes: &[u8]) -> Result<Self, Self::Error> {
let tail = if let &[b'R', b'G', b'B', b'A', ref tail @ ..] = bytes {
tail
} else {
return Err(ImageError::Decoding);
};
let (tail, width) = if let &[a, b, c, d, ref tail @ ..] = tail {
(tail, u32::from_be_bytes([a, b, c, d]))
} else {
return Err(ImageError::Decoding);
};
let (tail, height) = if let &[a, b, c, d, ref tail @ ..] = tail {
(tail, u32::from_be_bytes([a, b, c, d]))
} else {
return Err(ImageError::Decoding);
};
let (head, texels, tail) = unsafe { tail.align_to::<Rgba8>() };
if !head.is_empty() {
return Err(ImageError::Decoding);
}
if !tail.is_empty() {
return Err(ImageError::Decoding);
}
assert_eq!(texels.len(), (width * height) as usize);
Ok(Self::new(texels.to_vec(), Size::new(width, height)))
}
}