bs_trace/image/
ppm.rs

1use const_default::ConstDefault;
2use std::fmt::Display;
3
4#[derive(Debug, Clone, Copy)]
5/// An RGB pixel with 24-bit depth.
6pub struct Pixel {
7    /// Red value of this pixel.
8    pub r: u8,
9    /// Green value of this pixel.
10    pub g: u8,
11    /// Blue value of this pixel.
12    pub b: u8,
13}
14
15impl ConstDefault for Pixel {
16    /// Black.
17    const DEFAULT: Pixel = Pixel::new(0, 0, 0);
18}
19
20impl Display for Pixel {
21    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
22        write!(f, "{}\t{}\t{}", self.r, self.g, self.b)
23    }
24}
25
26/// A NetPBM Portable PixMap (P3; PPM) image.
27pub struct Ppm<const W: usize, const H: usize> {
28    rows: Vec<Vec<Pixel>>,
29}
30
31impl Pixel {
32    /// Constructs a new pixel from the given red, green, and blue values.
33    pub const fn new(r: u8, g: u8, b: u8) -> Self {
34        Self { r, g, b }
35    }
36}
37
38impl<const W: usize, const H: usize> Ppm<W, H> {
39    /// Constructs a PPM image from the given row data.
40    ///
41    /// This method will panic if there are not precisely `H` rows, or any of the rows do not contain precisely `W` pixels.
42    pub fn new(rows: Vec<Vec<Pixel>>) -> Self {
43        assert_eq!(rows.len(), H);
44        for row in rows.iter() {
45            assert_eq!(row.len(), W);
46        }
47
48        unsafe { Self::new_unchecked(rows) }
49    }
50
51    /// Constructs a PPM image from the given row data.
52    ///
53    /// # Safety
54    ///
55    /// It is the responsibility of the caller to ensure that `rows` contains precisely `H`
56    /// rows, and that each row contains precisely `W` pixels.
57    pub unsafe fn new_unchecked(rows: Vec<Vec<Pixel>>) -> Self {
58        Self { rows }
59    }
60
61    /// Generates a PPM image using the given function in each pixel's (x,y) coordinates -- the
62    /// top-left corner of the image is (0,0), x grows to the right in the positive direction,
63    /// and y grows downwards in the positive direction.
64    pub fn gen<F>(f: F) -> Self
65    where
66        F: Fn(usize, usize) -> Pixel,
67    {
68        let mut rows = Vec::<Vec<Pixel>>::with_capacity(H);
69        for y in 0..H {
70            let mut row_data = Vec::<Pixel>::with_capacity(W);
71            for x in 0..W {
72                row_data.push(f(x, y));
73            }
74            rows.push(row_data);
75        }
76
77        unsafe { Self::new_unchecked(rows) }
78    }
79}
80
81impl<const W: usize, const H: usize> Display for Ppm<W, H> {
82    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
83        writeln!(f, "P3")?;
84        writeln!(f, "{}\t{}", W, H)?;
85        writeln!(f, "255")?;
86        for row in self.rows.iter() {
87            for pixel in row.iter() {
88                writeln!(f, "{}", pixel)?;
89            }
90        }
91
92        Ok(())
93    }
94}
95
96impl IntoIterator for Pixel {
97    type Item = u8;
98    type IntoIter = std::array::IntoIter<u8, 3>;
99
100    fn into_iter(self) -> Self::IntoIter {
101        [self.r, self.g, self.b].into_iter()
102    }
103}