use std::num::NonZeroU32;
use super::Duration;
#[macro_export]
macro_rules! fps {
($frames:expr) => {
$crate::FrameRate::per_second(
const {
match ::std::num::NonZeroU32::new($frames) {
Some(frames) => frames,
None => panic!("frames per second must be nonzero"),
}
},
)
};
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct FrameRate {
numerator: NonZeroU32,
denominator: NonZeroU32,
}
impl FrameRate {
pub const NTSC_FILM: Self = Self::new(non_zero(24_000), non_zero(1_001));
pub const NTSC: Self = Self::new(non_zero(30_000), non_zero(1_001));
pub const NTSC_DOUBLE: Self = Self::new(non_zero(60_000), non_zero(1_001));
pub fn try_new(numerator: u32, denominator: u32) -> Result<Self, crate::ValidationError> {
match (NonZeroU32::new(numerator), NonZeroU32::new(denominator)) {
(Some(numerator), Some(denominator)) => Ok(Self::new(numerator, denominator)),
_ => Err(crate::ValidationError::invalid(
"frame_rate",
"ratio",
"frame-rate numerator and denominator must be nonzero",
)),
}
}
pub const fn new(numerator: NonZeroU32, denominator: NonZeroU32) -> Self {
let mut common = numerator.get();
let mut remainder = denominator.get();
while remainder != 0 {
let next = common % remainder;
common = remainder;
remainder = next;
}
Self {
numerator: non_zero(numerator.get() / common),
denominator: non_zero(denominator.get() / common),
}
}
pub const fn per_second(frames: NonZeroU32) -> Self {
Self {
numerator: frames,
denominator: NonZeroU32::MIN,
}
}
pub const fn numerator(self) -> u32 {
self.numerator.get()
}
pub const fn denominator(self) -> u32 {
self.denominator.get()
}
pub fn frame_duration(self) -> Duration {
Duration::from_frame(1, self)
}
}
impl From<NonZeroU32> for FrameRate {
fn from(frames_per_second: NonZeroU32) -> Self {
Self::per_second(frames_per_second)
}
}
const fn non_zero(value: u32) -> NonZeroU32 {
match NonZeroU32::new(value) {
Some(value) => value,
None => panic!("frame-rate component must be nonzero"),
}
}