1use crate::definitions::Image;
2use crate::drawing::Canvas;
3use image::{GenericImage, ImageBuffer};
4use std::i32;
5
6#[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#[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}