imageproc/drawing/
cross.rs

1use crate::definitions::Image;
2use crate::drawing::Canvas;
3use image::{GenericImage, ImageBuffer};
4use std::i32;
5
6/// Draws a colored cross on an image in place.
7///
8/// Handles coordinates outside image bounds.
9#[rustfmt::skip]
10pub fn draw_cross_mut<C>(canvas: &mut C, color: C::Pixel, x: i32, y: i32)
11where
12    C: Canvas
13{
14    let (width, height) = canvas.dimensions();
15    let idx = |x, y| (3 * (y + 1) + x + 1) as usize;
16    let stencil = [0u8, 1u8, 0u8,
17                   1u8, 1u8, 1u8,
18                   0u8, 1u8, 0u8];
19
20    for sy in -1..2 {
21        let iy = y + sy;
22        if iy < 0 || iy >= height as i32 {
23            continue;
24        }
25
26        for sx in -1..2 {
27            let ix = x + sx;
28            if ix < 0 || ix >= width as i32 {
29                continue;
30            }
31
32            if stencil[idx(sx, sy)] == 1u8 {
33                canvas.draw_pixel(ix as u32, iy as u32, color);
34            }
35        }
36    }
37}
38
39/// Draws a colored cross on a new copy of an image.
40///
41/// Handles coordinates outside image bounds.
42#[must_use = "the function does not modify the original image"]
43pub fn draw_cross<I>(image: &I, color: I::Pixel, x: i32, y: i32) -> Image<I::Pixel>
44where
45    I: GenericImage,
46{
47    let mut out = ImageBuffer::new(image.width(), image.height());
48    out.copy_from(image, 0, 0).unwrap();
49    draw_cross_mut(&mut out, color, x, y);
50    out
51}
52
53#[cfg(test)]
54mod tests {
55    use super::*;
56    use image::{GrayImage, Luma};
57
58    #[test]
59    fn test_draw_corner_inside_bounds() {
60        let image = GrayImage::from_pixel(5, 5, Luma([1u8]));
61
62        let expected = gray_image!(
63          1, 1, 1, 1, 1;
64          1, 1, 2, 1, 1;
65          1, 2, 2, 2, 1;
66          1, 1, 2, 1, 1;
67          1, 1, 1, 1, 1);
68
69        assert_pixels_eq!(draw_cross(&image, Luma([2u8]), 2, 2), expected);
70    }
71
72    #[test]
73    fn test_draw_corner_partially_outside_left() {
74        let image = GrayImage::from_pixel(5, 5, Luma([1u8]));
75
76        let expected = gray_image!(
77            1, 1, 1, 1, 1;
78            2, 1, 1, 1, 1;
79            2, 2, 1, 1, 1;
80            2, 1, 1, 1, 1;
81            1, 1, 1, 1, 1);
82
83        assert_pixels_eq!(draw_cross(&image, Luma([2u8]), 0, 2), expected);
84    }
85
86    #[test]
87    fn test_draw_corner_partially_outside_right() {
88        let image = GrayImage::from_pixel(5, 5, Luma([1u8]));
89
90        let expected = gray_image!(
91            1, 1, 1, 1, 1;
92            1, 1, 1, 1, 2;
93            1, 1, 1, 2, 2;
94            1, 1, 1, 1, 2;
95            1, 1, 1, 1, 1);
96
97        assert_pixels_eq!(draw_cross(&image, Luma([2u8]), 4, 2), expected);
98    }
99
100    #[test]
101    fn test_draw_corner_partially_outside_bottom() {
102        let image = GrayImage::from_pixel(5, 5, Luma([1u8]));
103
104        let expected = gray_image!(
105            1, 1, 1, 1, 1;
106            1, 1, 1, 1, 1;
107            1, 1, 1, 1, 1;
108            1, 1, 3, 1, 1;
109            1, 3, 3, 3, 1);
110
111        assert_pixels_eq!(draw_cross(&image, Luma([3u8]), 2, 4), expected);
112    }
113
114    #[test]
115    fn test_draw_corner_partially_outside_top() {
116        let image = GrayImage::from_pixel(5, 5, Luma([1u8]));
117
118        let expected = gray_image!(
119            1, 9, 9, 9, 1;
120            1, 1, 9, 1, 1;
121            1, 1, 1, 1, 1;
122            1, 1, 1, 1, 1;
123            1, 1, 1, 1, 1);
124
125        assert_pixels_eq!(draw_cross(&image, Luma([9u8]), 2, 0), expected);
126    }
127
128    #[test]
129    fn test_draw_corner_outside_bottom() {
130        let image = GrayImage::from_pixel(5, 5, Luma([1u8]));
131
132        let expected = gray_image!(
133            1, 1, 1, 1, 1;
134            1, 1, 1, 1, 1;
135            1, 1, 1, 1, 1;
136            1, 1, 1, 1, 1;
137            9, 1, 1, 1, 1);
138
139        assert_pixels_eq!(draw_cross(&image, Luma([9u8]), 0, 5), expected);
140    }
141
142    #[test]
143    fn test_draw_corner_outside_right() {
144        let image = GrayImage::from_pixel(5, 5, Luma([1u8]));
145
146        let expected = gray_image!(
147            1, 1, 1, 1, 9;
148            1, 1, 1, 1, 1;
149            1, 1, 1, 1, 1;
150            1, 1, 1, 1, 1;
151            1, 1, 1, 1, 1);
152
153        assert_pixels_eq!(draw_cross(&image, Luma([9u8]), 5, 0), expected);
154    }
155}