#[non_exhaustive]
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
pub enum ColorPrimaries {
#[default]
Srgb,
DisplayP3,
Bt2020,
}
#[non_exhaustive]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum PixelFormat {
Srgb8Rgb,
Srgb8Rgba,
Srgb8Bgra,
Srgb16Rgba,
LinearF32Rgba,
}
impl PixelFormat {
#[inline]
pub fn bytes_per_pixel(self) -> usize {
match self {
Self::Srgb8Rgb => 3,
Self::Srgb8Rgba | Self::Srgb8Bgra => 4,
Self::Srgb16Rgba => 8,
Self::LinearF32Rgba => 16,
}
}
#[inline]
pub fn has_alpha(self) -> bool {
!matches!(self, Self::Srgb8Rgb)
}
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
pub enum AlphaMode {
Opaque,
#[default]
Unknown,
Straight,
}
impl AlphaMode {
#[inline]
pub fn is_straight(self) -> bool {
matches!(self, Self::Unknown | Self::Straight)
}
}
pub trait ImageSource: Sync {
fn width(&self) -> usize;
fn height(&self) -> usize;
fn pixel_format(&self) -> PixelFormat;
fn alpha_mode(&self) -> AlphaMode;
fn color_primaries(&self) -> ColorPrimaries {
ColorPrimaries::Srgb
}
fn row_bytes(&self, y: usize) -> &[u8];
}
#[derive(Clone, Copy, Debug)]
pub struct RgbSlice<'a> {
data: &'a [[u8; 3]],
width: usize,
height: usize,
}
impl<'a> RgbSlice<'a> {
pub fn try_new(
data: &'a [[u8; 3]],
width: usize,
height: usize,
) -> Result<Self, crate::ZensimError> {
if data.len() < width * height {
return Err(crate::ZensimError::InvalidDataLength);
}
Ok(Self {
data,
width,
height,
})
}
pub fn new(data: &'a [[u8; 3]], width: usize, height: usize) -> Self {
Self::try_new(data, width, height).expect("RgbSlice: data length < width*height")
}
}
impl ImageSource for RgbSlice<'_> {
#[inline]
fn width(&self) -> usize {
self.width
}
#[inline]
fn height(&self) -> usize {
self.height
}
#[inline]
fn pixel_format(&self) -> PixelFormat {
PixelFormat::Srgb8Rgb
}
#[inline]
fn alpha_mode(&self) -> AlphaMode {
AlphaMode::Opaque
}
#[inline]
fn row_bytes(&self, y: usize) -> &[u8] {
let start = y * self.width;
let row = &self.data[start..start + self.width];
bytemuck::cast_slice(row)
}
}
#[derive(Clone, Copy, Debug)]
pub struct RgbaSlice<'a> {
data: &'a [[u8; 4]],
width: usize,
height: usize,
alpha_mode: AlphaMode,
}
impl<'a> RgbaSlice<'a> {
pub fn try_new(
data: &'a [[u8; 4]],
width: usize,
height: usize,
) -> Result<Self, crate::ZensimError> {
Self::try_with_alpha_mode(data, width, height, AlphaMode::Straight)
}
pub fn new(data: &'a [[u8; 4]], width: usize, height: usize) -> Self {
Self::try_new(data, width, height).expect("RgbaSlice: data length < width*height")
}
pub fn try_with_alpha_mode(
data: &'a [[u8; 4]],
width: usize,
height: usize,
alpha_mode: AlphaMode,
) -> Result<Self, crate::ZensimError> {
if data.len() < width * height {
return Err(crate::ZensimError::InvalidDataLength);
}
Ok(Self {
data,
width,
height,
alpha_mode,
})
}
pub fn with_alpha_mode(
data: &'a [[u8; 4]],
width: usize,
height: usize,
alpha_mode: AlphaMode,
) -> Self {
Self::try_with_alpha_mode(data, width, height, alpha_mode)
.expect("RgbaSlice: data length < width*height")
}
}
impl ImageSource for RgbaSlice<'_> {
#[inline]
fn width(&self) -> usize {
self.width
}
#[inline]
fn height(&self) -> usize {
self.height
}
#[inline]
fn pixel_format(&self) -> PixelFormat {
PixelFormat::Srgb8Rgba
}
#[inline]
fn alpha_mode(&self) -> AlphaMode {
self.alpha_mode
}
#[inline]
fn row_bytes(&self, y: usize) -> &[u8] {
let start = y * self.width;
let row = &self.data[start..start + self.width];
bytemuck::cast_slice(row)
}
}
#[derive(Clone, Copy, Debug)]
pub struct StridedBytes<'a> {
data: &'a [u8],
width: usize,
height: usize,
stride: usize,
pixel_format: PixelFormat,
alpha_mode: AlphaMode,
color_primaries: ColorPrimaries,
}
impl<'a> StridedBytes<'a> {
pub fn try_new(
data: &'a [u8],
width: usize,
height: usize,
stride: usize,
pixel_format: PixelFormat,
) -> Result<Self, crate::ZensimError> {
Self::try_with_alpha_mode(
data,
width,
height,
stride,
pixel_format,
AlphaMode::Unknown,
)
}
pub fn new(
data: &'a [u8],
width: usize,
height: usize,
stride: usize,
pixel_format: PixelFormat,
) -> Self {
Self::try_new(data, width, height, stride, pixel_format)
.expect("StridedBytes: invalid stride or data length")
}
pub fn try_with_alpha_mode(
data: &'a [u8],
width: usize,
height: usize,
stride: usize,
pixel_format: PixelFormat,
alpha_mode: AlphaMode,
) -> Result<Self, crate::ZensimError> {
let bpp = pixel_format.bytes_per_pixel();
let min_stride = width * bpp;
if stride < min_stride {
return Err(crate::ZensimError::InvalidStride);
}
if height > 0 {
let required = (height - 1) * stride + min_stride;
if data.len() < required {
return Err(crate::ZensimError::InvalidDataLength);
}
}
Ok(Self {
data,
width,
height,
stride,
pixel_format,
alpha_mode,
color_primaries: ColorPrimaries::Srgb,
})
}
pub fn with_alpha_mode(
data: &'a [u8],
width: usize,
height: usize,
stride: usize,
pixel_format: PixelFormat,
alpha_mode: AlphaMode,
) -> Self {
Self::try_with_alpha_mode(data, width, height, stride, pixel_format, alpha_mode)
.expect("StridedBytes: invalid stride or data length")
}
pub fn with_color_primaries(mut self, primaries: ColorPrimaries) -> Self {
self.color_primaries = primaries;
self
}
}
impl ImageSource for StridedBytes<'_> {
#[inline]
fn width(&self) -> usize {
self.width
}
#[inline]
fn height(&self) -> usize {
self.height
}
#[inline]
fn pixel_format(&self) -> PixelFormat {
self.pixel_format
}
#[inline]
fn alpha_mode(&self) -> AlphaMode {
self.alpha_mode
}
#[inline]
fn color_primaries(&self) -> ColorPrimaries {
self.color_primaries
}
#[inline]
fn row_bytes(&self, y: usize) -> &[u8] {
let start = y * self.stride;
let bpp = self.pixel_format.bytes_per_pixel();
&self.data[start..start + self.width * bpp]
}
}
#[cfg(feature = "imgref")]
mod imgref_impls {
use super::*;
impl ImageSource for imgref::ImgRef<'_, rgb::Rgb<u8>> {
#[inline]
fn width(&self) -> usize {
imgref::Img::width(self)
}
#[inline]
fn height(&self) -> usize {
imgref::Img::height(self)
}
#[inline]
fn pixel_format(&self) -> PixelFormat {
PixelFormat::Srgb8Rgb
}
#[inline]
fn alpha_mode(&self) -> AlphaMode {
AlphaMode::Opaque
}
#[inline]
fn row_bytes(&self, y: usize) -> &[u8] {
let stride = imgref::Img::stride(self); let buf = imgref::Img::buf(self);
let start = y * stride;
let w = imgref::Img::width(self);
bytemuck::cast_slice(&buf[start..start + w])
}
}
impl ImageSource for imgref::ImgRef<'_, rgb::Rgba<u8>> {
#[inline]
fn width(&self) -> usize {
imgref::Img::width(self)
}
#[inline]
fn height(&self) -> usize {
imgref::Img::height(self)
}
#[inline]
fn pixel_format(&self) -> PixelFormat {
PixelFormat::Srgb8Rgba
}
#[inline]
fn alpha_mode(&self) -> AlphaMode {
AlphaMode::Unknown
}
#[inline]
fn row_bytes(&self, y: usize) -> &[u8] {
let stride = imgref::Img::stride(self); let buf = imgref::Img::buf(self);
let start = y * stride;
let w = imgref::Img::width(self);
bytemuck::cast_slice(&buf[start..start + w])
}
}
}