1use crate::Error;
2
3#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
9pub struct Size {
10 pub width: u32,
12 pub height: u32,
14}
15
16impl Size {
17 pub fn new(width: u32, height: u32) -> Self {
19 Self { width, height }
20 }
21
22 pub fn pixels(&self) -> u64 {
24 self.width as u64 * self.height as u64
25 }
26
27 pub(crate) fn validate(&self, what: &str) -> Result<(), Error> {
33 self.validate_nonzero(what)?;
34 if !self.width.is_multiple_of(2) || !self.height.is_multiple_of(2) {
35 return Err(Error::Codec(anyhow::anyhow!("{what} {self}: dimensions must be even")));
36 }
37 Ok(())
38 }
39
40 pub(crate) fn validate_nonzero(&self, what: &str) -> Result<(), Error> {
44 if self.width == 0 || self.height == 0 {
45 return Err(Error::Codec(anyhow::anyhow!(
46 "{what} {self}: dimensions must be non-zero"
47 )));
48 }
49 Ok(())
50 }
51}
52
53impl std::fmt::Display for Size {
54 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
55 write!(f, "{}x{}", self.width, self.height)
56 }
57}
58
59impl From<(u32, u32)> for Size {
60 fn from((width, height): (u32, u32)) -> Self {
61 Self::new(width, height)
62 }
63}
64
65#[cfg(test)]
66mod tests {
67 use super::*;
68
69 #[test]
70 fn validate_rejects_odd_and_zero() {
71 assert!(Size::new(320, 240).validate("frame").is_ok());
72 assert!(Size::new(0, 240).validate("frame").is_err());
73 assert!(Size::new(320, 0).validate("frame").is_err());
74 assert!(Size::new(321, 240).validate("frame").is_err());
75 assert!(Size::new(320, 241).validate("frame").is_err());
76 }
77
78 #[test]
79 fn display_reads_as_a_resolution() {
80 assert_eq!(Size::new(1920, 1080).to_string(), "1920x1080");
81 }
82}