1use image::GenericImageView;
2
3#[inline]
5pub fn in_bounds<I: GenericImageView>(image: &I, x: i32, y: i32) -> bool {
6 x >= 0 && y >= 0 && x < image.width() as i32 && y < image.height() as i32
7}
8
9#[inline]
11pub fn get_pixel<I: GenericImageView>(image: &I, x: i32, y: i32) -> Option<I::Pixel> {
12 in_bounds(image, x, y).then(|| unsafe { image.unsafe_get_pixel(x as u32, y as u32) })
13}
14
15#[inline]
17pub fn clamp_pixel<I: GenericImageView>(image: &I, x: i32, y: i32) -> I::Pixel {
18 unsafe {
19 image.unsafe_get_pixel(
20 x.clamp(0, image.width() as i32 - 1) as u32,
21 y.clamp(0, image.height() as i32 - 1) as u32,
22 )
23 }
24}
25
26#[inline]
28pub unsafe fn clamp_pixel_unchecked<I: GenericImageView>(image: &I, x: i32, y: i32) -> I::Pixel {
29 image.unsafe_get_pixel(
30 (x.max(0) as u32).min(image.width() - 1),
31 (y.max(0) as u32).min(image.height() - 1),
32 )
33}
34
35#[cfg(test)]
36mod tests {
37 use image::GrayImage;
38
39 use super::*;
40
41 #[test]
42 fn in_bounds_for_empty_image() {
43 let image = GrayImage::new(0, 0);
44 for (x, y) in [(0, 0), (-1, -1), (1, 1), (1, 0), (0, 1), (-1, 0), (0, -1)] {
45 assert!(!in_bounds(&image, x, y));
46 }
47 }
48
49 #[test]
50 fn in_bounds_for_non_empty_image() {
51 let image = GrayImage::new(1, 1);
52
53 assert!(in_bounds(&image, 0, 0));
54 for (x, y) in [(-1, -1), (1, 1), (1, 0), (0, 1), (-1, 0), (0, -1)] {
55 assert!(!in_bounds(&image, x, y));
56 }
57 }
58
59 #[test]
60 fn lookup_pixel_for_empty_image() {
61 let image = GrayImage::new(0, 0);
62 for (x, y) in [(0, 0), (-1, -1), (1, 1), (1, 0), (0, 1), (-1, 0), (0, -1)] {
63 assert!(get_pixel(&image, x, y).is_none());
64 }
65 }
66
67 #[test]
68 fn lookup_pixel_for_non_empty_image() {
69 let image = GrayImage::from_pixel(1, 1, [255].into());
70
71 assert!(get_pixel(&image, -1, -1).is_none());
72 assert!(get_pixel(&image, 1, 1).is_none());
73 assert!(get_pixel(&image, 0, 0).is_some());
74 assert_eq!(
75 get_pixel(&image, 0, 0),
76 image.get_pixel_checked(0, 0).copied()
77 );
78 }
79
80 #[test]
81 #[should_panic]
82 fn clamp_pixel_for_empty_image() {
83 let image = GrayImage::new(0, 0);
84 clamp_pixel(&image, 0, 0);
85 }
86
87 #[test]
88 fn clamp_pixel_for_non_empty_image() {
89 let image = GrayImage::from_vec(2, 2, vec![32, 64, 128, 255]).unwrap();
90 let (w, h) = (image.width() as i32, image.height() as i32);
91 let (b, r) = (h - 1, w - 1);
92
93 assert_eq!(&clamp_pixel(&image, -1, -1), image.get_pixel(0, 0));
95 assert_eq!(&clamp_pixel(&image, 0, -1), image.get_pixel(0, 0));
96 assert_eq!(&clamp_pixel(&image, -1, 0), image.get_pixel(0, 0));
97
98 assert_eq!(&clamp_pixel(&image, w, b), image.get_pixel(1, 1));
100 assert_eq!(&clamp_pixel(&image, w, b), image.get_pixel(1, 1));
101 assert_eq!(&clamp_pixel(&image, r, h), image.get_pixel(1, 1));
102
103 assert_eq!(&clamp_pixel(&image, w, 0), image.get_pixel(1, 0));
105 assert_eq!(&clamp_pixel(&image, r, -1), image.get_pixel(1, 0));
106 assert_eq!(&clamp_pixel(&image, w, -1), image.get_pixel(1, 0));
107
108 assert_eq!(&clamp_pixel(&image, -1, b), image.get_pixel(0, 1));
110 assert_eq!(&clamp_pixel(&image, -1, h), image.get_pixel(0, 1));
111 assert_eq!(&clamp_pixel(&image, 0, h), image.get_pixel(0, 1));
112
113 assert_eq!(&clamp_pixel(&image, 0, 0), image.get_pixel(0, 0));
115 assert_eq!(&clamp_pixel(&image, r, 0), image.get_pixel(1, 0));
116 assert_eq!(&clamp_pixel(&image, 0, b), image.get_pixel(0, 1));
117 assert_eq!(&clamp_pixel(&image, r, b), image.get_pixel(1, 1));
118 }
119
120 #[test]
121 fn clamp_pixel_for_non_empty_image_unsafe() {
122 let image = GrayImage::from_vec(2, 2, vec![32, 64, 128, 255]).unwrap();
123 let (w, h) = (image.width() as i32, image.height() as i32);
124 let (b, r) = (h - 1, w - 1);
125
126 unsafe {
127 assert_eq!(
129 &clamp_pixel_unchecked(&image, -1, -1),
130 image.get_pixel(0, 0)
131 );
132 assert_eq!(&clamp_pixel_unchecked(&image, 0, -1), image.get_pixel(0, 0));
133 assert_eq!(&clamp_pixel_unchecked(&image, -1, 0), image.get_pixel(0, 0));
134
135 assert_eq!(&clamp_pixel_unchecked(&image, w, b), image.get_pixel(1, 1));
137 assert_eq!(&clamp_pixel_unchecked(&image, w, b), image.get_pixel(1, 1));
138 assert_eq!(&clamp_pixel_unchecked(&image, r, h), image.get_pixel(1, 1));
139
140 assert_eq!(&clamp_pixel_unchecked(&image, w, 0), image.get_pixel(1, 0));
142 assert_eq!(&clamp_pixel_unchecked(&image, r, -1), image.get_pixel(1, 0));
143 assert_eq!(&clamp_pixel_unchecked(&image, w, -1), image.get_pixel(1, 0));
144
145 assert_eq!(&clamp_pixel_unchecked(&image, -1, b), image.get_pixel(0, 1));
147 assert_eq!(&clamp_pixel_unchecked(&image, -1, h), image.get_pixel(0, 1));
148 assert_eq!(&clamp_pixel_unchecked(&image, 0, h), image.get_pixel(0, 1));
149
150 assert_eq!(&clamp_pixel_unchecked(&image, 0, 0), image.get_pixel(0, 0));
152 assert_eq!(&clamp_pixel_unchecked(&image, r, 0), image.get_pixel(1, 0));
153 assert_eq!(&clamp_pixel_unchecked(&image, 0, b), image.get_pixel(0, 1));
154 assert_eq!(&clamp_pixel_unchecked(&image, r, b), image.get_pixel(1, 1));
155 }
156 }
157}