#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum PixelFormat {
Rgba8,
Bgra8,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum AlphaType {
Premultiplied,
Straight,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct PixelLayout {
pub size: [u32; 2],
pub row_bytes: u32,
pub format: PixelFormat,
pub alpha: AlphaType,
}
impl PixelLayout {
pub fn packed(size: [u32; 2], format: PixelFormat, alpha: AlphaType) -> Self {
Self {
size,
row_bytes: size[0].saturating_mul(4),
format,
alpha,
}
}
pub fn byte_len(self) -> Result<usize, ImageError> {
let width = self.size[0]
.checked_mul(4)
.ok_or(ImageError::InvalidLayout)?;
if self.size.contains(&0) || self.row_bytes < width {
return Err(ImageError::InvalidLayout);
}
usize::try_from(u64::from(self.row_bytes) * u64::from(self.size[1]))
.map_err(|_| ImageError::InvalidLayout)
}
}
pub struct PixelBuffer {
layout: PixelLayout,
pixels: Vec<u8>,
}
impl PixelBuffer {
pub fn new(layout: PixelLayout, pixels: Vec<u8>) -> Result<Self, ImageError> {
if pixels.len() != layout.byte_len()? {
return Err(ImageError::InvalidLayout);
}
Ok(Self { layout, pixels })
}
pub fn layout(&self) -> PixelLayout {
self.layout
}
pub fn bytes(&self) -> &[u8] {
&self.pixels
}
pub fn into_bytes(self) -> Vec<u8> {
self.pixels
}
pub(crate) fn into_premultiplied_rgba(self) -> Vec<u8> {
let Self { layout, mut pixels } = self;
let row = layout.size[0] as usize * 4;
for y in 0..layout.size[1] as usize {
let from = y * layout.row_bytes as usize;
pixels.copy_within(from..from + row, y * row);
normalize_row(&mut pixels[y * row..(y + 1) * row], layout);
}
pixels.truncate(row * layout.size[1] as usize);
pixels
}
}
fn normalize_row(row: &mut [u8], layout: PixelLayout) {
for pixel in row.as_chunks_mut::<4>().0 {
if layout.format == PixelFormat::Bgra8 {
pixel.swap(0, 2);
}
if layout.alpha == AlphaType::Straight {
premultiply(pixel);
}
}
}
fn premultiply(pixel: &mut [u8]) {
let alpha = u16::from(pixel[3]);
for channel in &mut pixel[..3] {
*channel = ((u16::from(*channel) * alpha + 127) / 255) as u8;
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum ImageError {
InvalidLayout,
TooLarge,
IncompatibleTexture,
WrongDevice,
UnsupportedBackend,
}
impl std::fmt::Display for ImageError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{self:?}")
}
}
impl std::error::Error for ImageError {}