Skip to main content

chess_corners_core/
imageview.rs

1/// Minimal grayscale view for refinement without taking a dependency on `image`.
2#[derive(Copy, Clone, Debug)]
3pub struct ImageView<'a> {
4    pub data: &'a [u8],
5    pub width: usize,
6    pub height: usize,
7    /// Origin of the view in the coordinate system of the response map / base image.
8    pub origin: [i32; 2],
9}
10
11impl<'a> ImageView<'a> {
12    pub fn from_u8_slice(width: usize, height: usize, data: &'a [u8]) -> Option<Self> {
13        if width.checked_mul(height)? != data.len() {
14            return None;
15        }
16        Some(Self {
17            data,
18            width,
19            height,
20            origin: [0, 0],
21        })
22    }
23
24    pub fn with_origin(
25        width: usize,
26        height: usize,
27        data: &'a [u8],
28        origin: [i32; 2],
29    ) -> Option<Self> {
30        Self::from_u8_slice(width, height, data).map(|mut view| {
31            view.origin = origin;
32            view
33        })
34    }
35
36    #[inline]
37    pub fn supports_patch(&self, cx: i32, cy: i32, radius: i32) -> bool {
38        if self.width == 0 || self.height == 0 {
39            return false;
40        }
41
42        let gx = cx + self.origin[0];
43        let gy = cy + self.origin[1];
44        let min_x = 0;
45        let min_y = 0;
46        let max_x = self.width as i32 - 1;
47        let max_y = self.height as i32 - 1;
48        gx - radius >= min_x && gy - radius >= min_y && gx + radius <= max_x && gy + radius <= max_y
49    }
50
51    #[inline]
52    pub fn sample(&self, gx: i32, gy: i32) -> f32 {
53        if self.width == 0 || self.height == 0 {
54            return 0.0;
55        }
56        let gx = gx + self.origin[0];
57        let gy = gy + self.origin[1];
58        let lx = gx.clamp(0, self.width.saturating_sub(1) as i32) as usize;
59        let ly = gy.clamp(0, self.height.saturating_sub(1) as i32) as usize;
60        self.data[ly * self.width + lx] as f32
61    }
62}