Skip to main content

otf_pixels_codec_avif/av1/
plane.rs

1//! Sample planes for reconstruction.
2//!
3//! AV1 reconstruction is the most index-dense code in this crate by a wide
4//! margin: prediction reads a block's above row and left column, transforms
5//! write a block back, and every one of those touches samples by coordinate.
6//! The workspace forbids raw slice indexing in production code, so rather than
7//! spread `.get()?` chains across every predictor and transform, the bounds
8//! reasoning is concentrated here — the same tactic `TileMut` uses for the
9//! engine's own hot paths.
10//!
11//! A [`Plane`] stores one component's samples as `u16` (enough for 8-, 10- and
12//! 12-bit) in a tight row-major buffer. Reads that stray outside the plane are
13//! answered by replicating the nearest edge, which is exactly what AV1 intra
14//! prediction wants when a neighbour is off the frame.
15
16/// One component's reconstructed samples.
17#[derive(Debug, Clone)]
18pub struct Plane {
19    data: Vec<u16>,
20    width: usize,
21    height: usize,
22}
23
24impl Plane {
25    /// A zero-filled plane of the given size.
26    #[must_use]
27    pub fn new(width: usize, height: usize) -> Self {
28        Self {
29            data: vec![0; width.saturating_mul(height)],
30            width,
31            height,
32        }
33    }
34
35    /// Plane width in samples.
36    #[must_use]
37    pub fn width(&self) -> usize {
38        self.width
39    }
40
41    /// Plane height in samples.
42    #[must_use]
43    pub fn height(&self) -> usize {
44        self.height
45    }
46
47    /// The sample at `(x, y)`, or `None` if outside the plane.
48    #[must_use]
49    pub fn get(&self, x: usize, y: usize) -> Option<u16> {
50        if x >= self.width || y >= self.height {
51            return None;
52        }
53        self.data.get(y * self.width + x).copied()
54    }
55
56    /// Set the sample at `(x, y)`; out-of-range writes are dropped.
57    pub fn set(&mut self, x: usize, y: usize, value: u16) {
58        if x < self.width && y < self.height {
59            if let Some(slot) = self.data.get_mut(y * self.width + x) {
60                *slot = value;
61            }
62        }
63    }
64
65    /// A read-only view of row `y`, or `None` if out of range.
66    #[must_use]
67    pub fn row(&self, y: usize) -> Option<&[u16]> {
68        if y >= self.height {
69            return None;
70        }
71        self.data.get(y * self.width..y * self.width + self.width)
72    }
73
74    /// The sample at signed `(x, y)`, clamping the coordinates to the nearest
75    /// edge. This is how intra prediction reads neighbours that may lie off the
76    /// frame: the edge sample is replicated rather than treated as an error.
77    ///
78    /// The plane must be non-empty; a zero-sized plane yields 0.
79    #[must_use]
80    pub fn sample_clamped(&self, x: isize, y: isize) -> u16 {
81        if self.width == 0 || self.height == 0 {
82            return 0;
83        }
84        let cx = x.clamp(0, self.width as isize - 1) as usize;
85        let cy = y.clamp(0, self.height as isize - 1) as usize;
86        self.data.get(cy * self.width + cx).copied().unwrap_or(0)
87    }
88
89    /// Copy the whole plane out as a row-major `u16` buffer. Used by tests and
90    /// by the plane-to-RGB conversion.
91    #[must_use]
92    pub fn samples(&self) -> &[u16] {
93        &self.data
94    }
95}
96
97#[cfg(test)]
98#[allow(
99    clippy::unwrap_used,
100    clippy::indexing_slicing,
101    clippy::panic,
102    reason = "tests operate on known-good values and assert shapes directly"
103)]
104mod tests {
105    use super::*;
106
107    #[test]
108    fn set_and_get_round_trip_within_bounds() {
109        let mut p = Plane::new(4, 3);
110        p.set(2, 1, 300);
111        assert_eq!(p.get(2, 1), Some(300));
112        assert_eq!(p.get(0, 0), Some(0));
113        assert_eq!(p.get(4, 0), None);
114        assert_eq!(p.get(0, 3), None);
115    }
116
117    #[test]
118    fn out_of_range_writes_are_dropped() {
119        let mut p = Plane::new(2, 2);
120        p.set(5, 5, 999);
121        assert_eq!(p.samples(), &[0, 0, 0, 0]);
122    }
123
124    #[test]
125    fn clamped_sampling_replicates_the_edge() {
126        let mut p = Plane::new(3, 2);
127        for y in 0..2 {
128            for x in 0..3 {
129                p.set(x, y, (10 * y + x) as u16);
130            }
131        }
132        // Inside.
133        assert_eq!(p.sample_clamped(1, 1), 11);
134        // Left/above of the plane clamps to (0, 0).
135        assert_eq!(p.sample_clamped(-4, -9), 0);
136        // Past the right/bottom clamps to the far corner (x=2, y=1) = 12.
137        assert_eq!(p.sample_clamped(99, 99), 12);
138        // Mixed: past the right edge on row 0.
139        assert_eq!(p.sample_clamped(99, 0), 2);
140    }
141
142    #[test]
143    fn row_returns_exactly_the_width() {
144        let mut p = Plane::new(3, 2);
145        p.set(0, 1, 7);
146        p.set(2, 1, 9);
147        assert_eq!(p.row(1), Some(&[7, 0, 9][..]));
148        assert_eq!(p.row(2), None);
149    }
150}