use std::fmt;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum PixelFormat {
Rgb24,
Rgba,
Bgr24,
Bgra,
Yuv420p,
Yuv422p,
Yuv444p,
Nv12,
Nv21,
Yuv420p10le,
Yuv422p10le,
Yuv444p10le,
Yuva444p10le,
P010le,
Gray8,
Gbrpf32le,
Other(u32),
}
impl PixelFormat {
#[must_use]
pub const fn name(&self) -> &'static str {
match self {
Self::Rgb24 => "rgb24",
Self::Rgba => "rgba",
Self::Bgr24 => "bgr24",
Self::Bgra => "bgra",
Self::Yuv420p => "yuv420p",
Self::Yuv422p => "yuv422p",
Self::Yuv444p => "yuv444p",
Self::Nv12 => "nv12",
Self::Nv21 => "nv21",
Self::Yuv420p10le => "yuv420p10le",
Self::Yuv422p10le => "yuv422p10le",
Self::Yuv444p10le => "yuv444p10le",
Self::Yuva444p10le => "yuva444p10le",
Self::P010le => "p010le",
Self::Gray8 => "gray8",
Self::Gbrpf32le => "gbrpf32le",
Self::Other(_) => "unknown",
}
}
#[must_use]
pub const fn num_planes(&self) -> usize {
match self {
Self::Yuv420p
| Self::Yuv422p
| Self::Yuv444p
| Self::Yuv420p10le
| Self::Yuv422p10le
| Self::Yuv444p10le
| Self::Gbrpf32le => 3,
Self::Yuva444p10le => 4,
Self::Nv12 | Self::Nv21 | Self::P010le => 2,
Self::Rgb24 | Self::Rgba | Self::Bgr24 | Self::Bgra | Self::Gray8 | Self::Other(_) => 1,
}
}
#[must_use]
#[inline]
pub const fn plane_count(&self) -> usize {
self.num_planes()
}
#[must_use]
pub const fn is_packed(&self) -> bool {
matches!(
self,
Self::Rgb24 | Self::Rgba | Self::Bgr24 | Self::Bgra | Self::Gray8
)
}
#[must_use]
pub const fn is_planar(&self) -> bool {
!self.is_packed()
}
#[must_use]
pub const fn has_alpha(&self) -> bool {
matches!(self, Self::Rgba | Self::Bgra | Self::Yuva444p10le)
}
#[must_use]
pub const fn is_rgb(&self) -> bool {
matches!(
self,
Self::Rgb24 | Self::Rgba | Self::Bgr24 | Self::Bgra | Self::Gbrpf32le
)
}
#[must_use]
pub const fn is_yuv(&self) -> bool {
matches!(
self,
Self::Yuv420p
| Self::Yuv422p
| Self::Yuv444p
| Self::Nv12
| Self::Nv21
| Self::Yuv420p10le
| Self::Yuv422p10le
| Self::Yuv444p10le
| Self::Yuva444p10le
| Self::P010le
)
}
#[must_use]
pub const fn bits_per_pixel(&self) -> Option<usize> {
match self {
Self::Rgb24 | Self::Bgr24 => Some(24),
Self::Rgba | Self::Bgra => Some(32),
Self::Gray8 => Some(8),
_ => None,
}
}
#[must_use]
pub const fn bytes_per_pixel(&self) -> usize {
match self {
Self::Gray8 => 1,
Self::Yuv420p
| Self::Nv12
| Self::Nv21
| Self::Yuv420p10le
| Self::P010le
| Self::Yuv422p
| Self::Yuv422p10le => 2,
Self::Rgb24 | Self::Bgr24 | Self::Yuv444p | Self::Yuv444p10le => 3,
Self::Gbrpf32le => 12,
Self::Yuva444p10le | Self::Rgba | Self::Bgra | Self::Other(_) => 4,
}
}
#[must_use]
pub const fn is_high_bit_depth(&self) -> bool {
matches!(
self,
Self::Yuv420p10le
| Self::Yuv422p10le
| Self::Yuv444p10le
| Self::Yuva444p10le
| Self::P010le
| Self::Gbrpf32le
)
}
#[must_use]
pub const fn bit_depth(&self) -> usize {
match self {
Self::Yuv420p10le
| Self::Yuv422p10le
| Self::Yuv444p10le
| Self::Yuva444p10le
| Self::P010le => 10,
Self::Gbrpf32le => 32,
_ => 8,
}
}
}
impl fmt::Display for PixelFormat {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.name())
}
}
impl Default for PixelFormat {
fn default() -> Self {
Self::Yuv420p
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_format_names() {
assert_eq!(PixelFormat::Rgb24.name(), "rgb24");
assert_eq!(PixelFormat::Rgba.name(), "rgba");
assert_eq!(PixelFormat::Bgr24.name(), "bgr24");
assert_eq!(PixelFormat::Bgra.name(), "bgra");
assert_eq!(PixelFormat::Yuv420p.name(), "yuv420p");
assert_eq!(PixelFormat::Yuv422p.name(), "yuv422p");
assert_eq!(PixelFormat::Yuv444p.name(), "yuv444p");
assert_eq!(PixelFormat::Nv12.name(), "nv12");
assert_eq!(PixelFormat::Nv21.name(), "nv21");
assert_eq!(PixelFormat::Yuv420p10le.name(), "yuv420p10le");
assert_eq!(PixelFormat::P010le.name(), "p010le");
assert_eq!(PixelFormat::Gray8.name(), "gray8");
assert_eq!(PixelFormat::Other(999).name(), "unknown");
}
#[test]
fn test_plane_count() {
assert_eq!(PixelFormat::Rgb24.num_planes(), 1);
assert_eq!(PixelFormat::Rgba.num_planes(), 1);
assert_eq!(PixelFormat::Bgr24.num_planes(), 1);
assert_eq!(PixelFormat::Bgra.num_planes(), 1);
assert_eq!(PixelFormat::Gray8.num_planes(), 1);
assert_eq!(PixelFormat::Yuv420p.num_planes(), 3);
assert_eq!(PixelFormat::Yuv422p.num_planes(), 3);
assert_eq!(PixelFormat::Yuv444p.num_planes(), 3);
assert_eq!(PixelFormat::Yuv420p10le.num_planes(), 3);
assert_eq!(PixelFormat::Nv12.num_planes(), 2);
assert_eq!(PixelFormat::Nv21.num_planes(), 2);
assert_eq!(PixelFormat::P010le.num_planes(), 2);
assert_eq!(PixelFormat::Yuv420p.plane_count(), 3);
}
#[test]
fn test_packed_vs_planar() {
assert!(PixelFormat::Rgb24.is_packed());
assert!(PixelFormat::Rgba.is_packed());
assert!(PixelFormat::Bgr24.is_packed());
assert!(PixelFormat::Bgra.is_packed());
assert!(PixelFormat::Gray8.is_packed());
assert!(!PixelFormat::Rgb24.is_planar());
assert!(PixelFormat::Yuv420p.is_planar());
assert!(PixelFormat::Yuv422p.is_planar());
assert!(PixelFormat::Yuv444p.is_planar());
assert!(PixelFormat::Nv12.is_planar());
assert!(PixelFormat::Nv21.is_planar());
assert!(!PixelFormat::Yuv420p.is_packed());
}
#[test]
fn test_has_alpha() {
assert!(PixelFormat::Rgba.has_alpha());
assert!(PixelFormat::Bgra.has_alpha());
assert!(!PixelFormat::Rgb24.has_alpha());
assert!(!PixelFormat::Bgr24.has_alpha());
assert!(!PixelFormat::Yuv420p.has_alpha());
assert!(!PixelFormat::Gray8.has_alpha());
}
#[test]
fn test_is_rgb() {
assert!(PixelFormat::Rgb24.is_rgb());
assert!(PixelFormat::Rgba.is_rgb());
assert!(PixelFormat::Bgr24.is_rgb());
assert!(PixelFormat::Bgra.is_rgb());
assert!(!PixelFormat::Yuv420p.is_rgb());
assert!(!PixelFormat::Nv12.is_rgb());
assert!(!PixelFormat::Gray8.is_rgb());
}
#[test]
fn test_is_yuv() {
assert!(PixelFormat::Yuv420p.is_yuv());
assert!(PixelFormat::Yuv422p.is_yuv());
assert!(PixelFormat::Yuv444p.is_yuv());
assert!(PixelFormat::Nv12.is_yuv());
assert!(PixelFormat::Nv21.is_yuv());
assert!(PixelFormat::Yuv420p10le.is_yuv());
assert!(PixelFormat::P010le.is_yuv());
assert!(!PixelFormat::Rgb24.is_yuv());
assert!(!PixelFormat::Rgba.is_yuv());
assert!(!PixelFormat::Gray8.is_yuv());
}
#[test]
fn test_bits_per_pixel() {
assert_eq!(PixelFormat::Rgb24.bits_per_pixel(), Some(24));
assert_eq!(PixelFormat::Bgr24.bits_per_pixel(), Some(24));
assert_eq!(PixelFormat::Rgba.bits_per_pixel(), Some(32));
assert_eq!(PixelFormat::Bgra.bits_per_pixel(), Some(32));
assert_eq!(PixelFormat::Gray8.bits_per_pixel(), Some(8));
assert_eq!(PixelFormat::Yuv420p.bits_per_pixel(), None);
assert_eq!(PixelFormat::Nv12.bits_per_pixel(), None);
}
#[test]
fn test_bytes_per_pixel() {
assert_eq!(PixelFormat::Rgb24.bytes_per_pixel(), 3);
assert_eq!(PixelFormat::Bgr24.bytes_per_pixel(), 3);
assert_eq!(PixelFormat::Rgba.bytes_per_pixel(), 4);
assert_eq!(PixelFormat::Bgra.bytes_per_pixel(), 4);
assert_eq!(PixelFormat::Gray8.bytes_per_pixel(), 1);
assert_eq!(PixelFormat::Yuv420p.bytes_per_pixel(), 2);
assert_eq!(PixelFormat::Nv12.bytes_per_pixel(), 2);
assert_eq!(PixelFormat::Nv21.bytes_per_pixel(), 2);
assert_eq!(PixelFormat::Yuv422p.bytes_per_pixel(), 2);
assert_eq!(PixelFormat::Yuv444p.bytes_per_pixel(), 3);
assert_eq!(PixelFormat::Yuv420p10le.bytes_per_pixel(), 2);
assert_eq!(PixelFormat::P010le.bytes_per_pixel(), 2);
}
#[test]
fn test_high_bit_depth() {
assert!(PixelFormat::Yuv420p10le.is_high_bit_depth());
assert!(PixelFormat::P010le.is_high_bit_depth());
assert!(!PixelFormat::Yuv420p.is_high_bit_depth());
assert!(!PixelFormat::Rgba.is_high_bit_depth());
}
#[test]
fn test_bit_depth() {
assert_eq!(PixelFormat::Rgba.bit_depth(), 8);
assert_eq!(PixelFormat::Yuv420p.bit_depth(), 8);
assert_eq!(PixelFormat::Yuv420p10le.bit_depth(), 10);
assert_eq!(PixelFormat::P010le.bit_depth(), 10);
}
#[test]
fn test_display() {
assert_eq!(format!("{}", PixelFormat::Yuv420p), "yuv420p");
assert_eq!(format!("{}", PixelFormat::Rgba), "rgba");
assert_eq!(format!("{}", PixelFormat::Other(123)), "unknown");
}
#[test]
fn test_default() {
assert_eq!(PixelFormat::default(), PixelFormat::Yuv420p);
}
#[test]
fn test_debug() {
assert_eq!(format!("{:?}", PixelFormat::Rgba), "Rgba");
assert_eq!(format!("{:?}", PixelFormat::Yuv420p), "Yuv420p");
assert_eq!(format!("{:?}", PixelFormat::Other(42)), "Other(42)");
}
#[test]
fn test_equality_and_hash() {
use std::collections::HashSet;
assert_eq!(PixelFormat::Rgba, PixelFormat::Rgba);
assert_ne!(PixelFormat::Rgba, PixelFormat::Bgra);
assert_eq!(PixelFormat::Other(1), PixelFormat::Other(1));
assert_ne!(PixelFormat::Other(1), PixelFormat::Other(2));
let mut set = HashSet::new();
set.insert(PixelFormat::Rgba);
set.insert(PixelFormat::Yuv420p);
assert!(set.contains(&PixelFormat::Rgba));
assert!(!set.contains(&PixelFormat::Bgra));
}
#[test]
fn test_copy() {
let format = PixelFormat::Yuv420p;
let copied = format;
assert_eq!(format, copied);
assert_eq!(format.name(), copied.name());
}
#[test]
fn gbrpf32le_name_should_return_gbrpf32le() {
assert_eq!(PixelFormat::Gbrpf32le.name(), "gbrpf32le");
}
#[test]
fn gbrpf32le_should_have_three_planes() {
assert_eq!(PixelFormat::Gbrpf32le.num_planes(), 3);
assert_eq!(PixelFormat::Gbrpf32le.plane_count(), 3);
}
#[test]
fn gbrpf32le_should_be_planar_not_packed() {
assert!(!PixelFormat::Gbrpf32le.is_packed());
assert!(PixelFormat::Gbrpf32le.is_planar());
}
#[test]
fn gbrpf32le_should_be_rgb_family() {
assert!(PixelFormat::Gbrpf32le.is_rgb());
assert!(!PixelFormat::Gbrpf32le.is_yuv());
}
#[test]
fn gbrpf32le_should_not_have_alpha() {
assert!(!PixelFormat::Gbrpf32le.has_alpha());
}
#[test]
fn gbrpf32le_should_have_twelve_bytes_per_pixel() {
assert_eq!(PixelFormat::Gbrpf32le.bytes_per_pixel(), 12);
}
#[test]
fn gbrpf32le_should_be_high_bit_depth() {
assert!(PixelFormat::Gbrpf32le.is_high_bit_depth());
}
#[test]
fn gbrpf32le_should_have_bit_depth_32() {
assert_eq!(PixelFormat::Gbrpf32le.bit_depth(), 32);
}
#[test]
fn gbrpf32le_bits_per_pixel_should_be_none() {
assert_eq!(PixelFormat::Gbrpf32le.bits_per_pixel(), None);
}
}