Skip to main content

moq_video/
size.rs

1use crate::Error;
2
3/// A frame resolution in pixels.
4///
5/// Names the pair that [`decode::Config::resize`](crate::decode::Config::resize)
6/// and [`Frame::resize`](crate::Frame::resize) both take, so
7/// width and height can't be swapped at a call site.
8#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
9pub struct Size {
10	/// Width in pixels.
11	pub width: u32,
12	/// Height in pixels.
13	pub height: u32,
14}
15
16impl Size {
17	/// A size of `width` x `height` pixels.
18	pub fn new(width: u32, height: u32) -> Self {
19		Self { width, height }
20	}
21
22	/// Total pixels. Can't overflow: the widest `u32` square still fits a `u64`.
23	pub fn pixels(&self) -> u64 {
24		self.width as u64 * self.height as u64
25	}
26
27	/// Reject anything the I420 pipeline can't represent.
28	///
29	/// I420 chroma is subsampled 2x2, so every stage (encode, decode, resize)
30	/// needs even, non-zero dimensions. Checking here keeps the rule in one place
31	/// instead of re-deriving it at each boundary.
32	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	/// The half of [`Size::validate`] that is not about chroma, for the surfaces
41	/// that hold RGB and so tolerate odd dimensions (a render target sized to a
42	/// window).
43	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}