async_cuda_npp/
region.rs

1/// Represents subregion of image.
2#[derive(Debug, Clone, Copy, PartialEq, Default)]
3pub enum Region {
4    #[default]
5    Full,
6    Rectangle {
7        x: usize,
8        y: usize,
9        width: usize,
10        height: usize,
11    },
12}
13
14impl Region {
15    /// Create new [`Region`] that covers the whole image.
16    #[inline]
17    pub fn full() -> Self {
18        Region::Full
19    }
20
21    /// Create new partial [`Region`] with normalized width and height.
22    ///
23    /// If the `width` or `height` is less than 2, it will be set to 2 to produce a region that
24    /// is valid when used with the NPP API.
25    ///
26    /// # Arguments
27    ///
28    /// * `topleft` - Coordinates of top left corner of the region.
29    /// * `dims` - Dimensions of the region.
30    #[inline]
31    pub fn rectangle_normalized(topleft: (usize, usize), dims: (usize, usize)) -> Self {
32        let (x, y) = topleft;
33        let (width, height) = dims;
34        Self::Rectangle {
35            x,
36            y,
37            width: width.max(2),
38            height: height.max(2),
39        }
40    }
41
42    /// Resolve the actual values for `x`, `y`, `width` and `height` of the box, even if when it is
43    /// `Region::Full`. To compute these, the outer `width` and `height` are required.
44    ///
45    /// # Arguments
46    ///
47    /// * `width` - Outer width.
48    /// * `height` - Outer height.
49    ///
50    /// # Return value
51    ///
52    /// Region coordinates `x`, `y`, `width` and `height`.
53    pub fn resolve_to_xywh(&self, width: usize, height: usize) -> (usize, usize, usize, usize) {
54        match self {
55            Region::Full => (0, 0, width, height),
56            Region::Rectangle {
57                x,
58                y,
59                width,
60                height,
61            } => (*x, *y, *width, *height),
62        }
63    }
64
65    /// Whether or not the region is of type `Region::Full`.
66    pub fn is_full(&self) -> bool {
67        matches!(self, Region::Full)
68    }
69}
70
71impl std::fmt::Display for Region {
72    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
73        match self {
74            Region::Full => write!(f, "[full]"),
75            // This formats to something like this:
76            //
77            // ```
78            // [x: 10, y: 10, width: 80, height: 40]
79            // ```
80            Region::Rectangle {
81                x,
82                y,
83                width,
84                height,
85            } => write!(f, "[x: {x}, y: {y}, width: {width}, height: {height}]",),
86        }
87    }
88}
89
90#[cfg(test)]
91mod tests {
92    use super::*;
93
94    #[test]
95    fn test_new_full() {
96        assert_eq!(Region::full(), Region::Full);
97        assert!(Region::full().is_full());
98    }
99
100    #[test]
101    fn test_new_rectangle_normalized() {
102        assert_eq!(
103            Region::rectangle_normalized((1, 2), (3, 4)),
104            Region::Rectangle {
105                x: 1,
106                y: 2,
107                width: 3,
108                height: 4
109            }
110        );
111        assert_eq!(
112            Region::rectangle_normalized((1, 2), (0, 1)),
113            Region::Rectangle {
114                x: 1,
115                y: 2,
116                width: 2,
117                height: 2
118            }
119        );
120        assert!(!Region::rectangle_normalized((1, 2), (3, 4)).is_full());
121    }
122
123    #[test]
124    fn test_resolve_region() {
125        let region = Region::Rectangle {
126            x: 8,
127            y: 10,
128            width: 12,
129            height: 16,
130        };
131        assert_eq!(region.resolve_to_xywh(20, 20), (8, 10, 12, 16));
132    }
133
134    #[test]
135    fn test_resolve_full() {
136        let region = Region::Full;
137        assert_eq!(region.resolve_to_xywh(10, 20), (0, 0, 10, 20));
138    }
139}