#![allow(dead_code)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum PixelFormat {
Yuv420p,
Yuv422p,
Yuv444p,
Nv12,
Rgb24,
Rgba32,
P010,
}
impl PixelFormat {
#[must_use]
pub const fn bit_depth(&self) -> u8 {
match self {
Self::P010 => 10,
_ => 8,
}
}
#[must_use]
pub const fn plane_count(&self) -> usize {
match self {
Self::Yuv420p | Self::Yuv422p | Self::Yuv444p => 3,
Self::Nv12 | Self::P010 => 2,
Self::Rgb24 | Self::Rgba32 => 1,
}
}
#[allow(clippy::cast_precision_loss)]
#[must_use]
pub fn bytes_per_pixel_approx(&self) -> f32 {
match self {
Self::Yuv420p | Self::Nv12 => 1.5_f32,
Self::Yuv422p => 2.0_f32,
Self::Yuv444p | Self::Rgb24 | Self::P010 => 3.0_f32,
Self::Rgba32 => 4.0_f32,
}
}
#[must_use]
pub const fn is_planar(&self) -> bool {
matches!(
self,
Self::Yuv420p | Self::Yuv422p | Self::Yuv444p | Self::Nv12 | Self::P010
)
}
#[must_use]
pub const fn is_yuv(&self) -> bool {
matches!(
self,
Self::Yuv420p | Self::Yuv422p | Self::Yuv444p | Self::Nv12 | Self::P010
)
}
#[must_use]
pub const fn has_alpha(&self) -> bool {
matches!(self, Self::Rgba32)
}
#[must_use]
pub fn all() -> &'static [Self] {
&[
Self::Yuv420p,
Self::Yuv422p,
Self::Yuv444p,
Self::Nv12,
Self::Rgb24,
Self::Rgba32,
Self::P010,
]
}
}
#[derive(Debug, Clone)]
pub struct PixelFormatInfo {
pub format: PixelFormat,
pub name: &'static str,
pub planes: usize,
pub bit_depth: u8,
pub planar: bool,
}
impl PixelFormatInfo {
#[must_use]
pub fn new(format: PixelFormat) -> Self {
let name = match format {
PixelFormat::Yuv420p => "yuv420p",
PixelFormat::Yuv422p => "yuv422p",
PixelFormat::Yuv444p => "yuv444p",
PixelFormat::Nv12 => "nv12",
PixelFormat::Rgb24 => "rgb24",
PixelFormat::Rgba32 => "rgba",
PixelFormat::P010 => "p010le",
};
Self {
planes: format.plane_count(),
bit_depth: format.bit_depth(),
planar: format.is_planar(),
format,
name,
}
}
#[must_use]
pub fn is_planar(&self) -> bool {
self.planar
}
#[must_use]
pub fn name(&self) -> &str {
self.name
}
#[allow(
clippy::cast_precision_loss,
clippy::cast_possible_truncation,
clippy::cast_sign_loss
)]
#[must_use]
pub fn frame_size_bytes(&self, width: u32, height: u32) -> usize {
let pixels = width as f32 * height as f32;
let bpp = self.format.bytes_per_pixel_approx();
(pixels * bpp) as usize
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_yuv420p_planes() {
assert_eq!(PixelFormat::Yuv420p.plane_count(), 3);
}
#[test]
fn test_nv12_planes() {
assert_eq!(PixelFormat::Nv12.plane_count(), 2);
}
#[test]
fn test_rgb24_planes() {
assert_eq!(PixelFormat::Rgb24.plane_count(), 1);
}
#[test]
fn test_rgba32_planes() {
assert_eq!(PixelFormat::Rgba32.plane_count(), 1);
}
#[test]
fn test_p010_bit_depth() {
assert_eq!(PixelFormat::P010.bit_depth(), 10);
}
#[test]
fn test_yuv420p_bit_depth() {
assert_eq!(PixelFormat::Yuv420p.bit_depth(), 8);
}
#[test]
fn test_is_planar_yuv420p() {
assert!(PixelFormat::Yuv420p.is_planar());
}
#[test]
fn test_is_planar_rgb24_false() {
assert!(!PixelFormat::Rgb24.is_planar());
}
#[test]
fn test_is_planar_rgba32_false() {
assert!(!PixelFormat::Rgba32.is_planar());
}
#[test]
fn test_has_alpha_rgba32() {
assert!(PixelFormat::Rgba32.has_alpha());
}
#[test]
fn test_has_alpha_rgb24_false() {
assert!(!PixelFormat::Rgb24.has_alpha());
}
#[test]
fn test_is_yuv() {
assert!(PixelFormat::Yuv420p.is_yuv());
assert!(PixelFormat::Nv12.is_yuv());
assert!(!PixelFormat::Rgb24.is_yuv());
}
#[test]
fn test_bytes_per_pixel_yuv420p() {
let bpp = PixelFormat::Yuv420p.bytes_per_pixel_approx();
assert!((bpp - 1.5).abs() < 1e-6);
}
#[test]
fn test_bytes_per_pixel_rgba32() {
let bpp = PixelFormat::Rgba32.bytes_per_pixel_approx();
assert!((bpp - 4.0).abs() < 1e-6);
}
#[test]
fn test_all_formats_count() {
assert_eq!(PixelFormat::all().len(), 7);
}
#[test]
fn test_pixel_format_info_new_yuv420p() {
let info = PixelFormatInfo::new(PixelFormat::Yuv420p);
assert_eq!(info.planes, 3);
assert!(info.is_planar());
assert_eq!(info.name(), "yuv420p");
}
#[test]
fn test_pixel_format_info_new_rgba32() {
let info = PixelFormatInfo::new(PixelFormat::Rgba32);
assert_eq!(info.planes, 1);
assert!(!info.is_planar());
assert_eq!(info.name(), "rgba");
}
#[test]
fn test_frame_size_bytes_yuv420p() {
let info = PixelFormatInfo::new(PixelFormat::Yuv420p);
let size = info.frame_size_bytes(1920, 1080);
assert_eq!(size, 3_110_400);
}
#[test]
fn test_frame_size_bytes_rgb24() {
let info = PixelFormatInfo::new(PixelFormat::Rgb24);
let size = info.frame_size_bytes(1920, 1080);
assert_eq!(size, 6_220_800);
}
#[test]
fn test_equality() {
assert_eq!(PixelFormat::Yuv420p, PixelFormat::Yuv420p);
assert_ne!(PixelFormat::Yuv420p, PixelFormat::Yuv422p);
}
}