1use crate::definitions::Image;
2use crate::drawing::draw_if_in_bounds;
3use crate::drawing::line::draw_line_segment_mut;
4use crate::drawing::Canvas;
5use image::{GenericImage, ImageBuffer};
6use std::f32;
7use std::i32;
8
9#[must_use = "the function does not modify the original image"]
20pub fn draw_hollow_ellipse<I>(
21 image: &I,
22 center: (i32, i32),
23 width_radius: i32,
24 height_radius: i32,
25 color: I::Pixel,
26) -> Image<I::Pixel>
27where
28 I: GenericImage,
29{
30 let mut out = ImageBuffer::new(image.width(), image.height());
31 out.copy_from(image, 0, 0).unwrap();
32 draw_hollow_ellipse_mut(&mut out, center, width_radius, height_radius, color);
33 out
34}
35
36pub fn draw_hollow_ellipse_mut<C>(
47 canvas: &mut C,
48 center: (i32, i32),
49 width_radius: i32,
50 height_radius: i32,
51 color: C::Pixel,
52) where
53 C: Canvas,
54{
55 if width_radius == height_radius {
57 draw_hollow_circle_mut(canvas, center, width_radius, color);
58 return;
59 }
60
61 let draw_quad_pixels = |x0: i32, y0: i32, x: i32, y: i32| {
62 draw_if_in_bounds(canvas, x0 + x, y0 + y, color);
63 draw_if_in_bounds(canvas, x0 - x, y0 + y, color);
64 draw_if_in_bounds(canvas, x0 + x, y0 - y, color);
65 draw_if_in_bounds(canvas, x0 - x, y0 - y, color);
66 };
67
68 draw_ellipse(draw_quad_pixels, center, width_radius, height_radius);
69}
70
71#[must_use = "the function does not modify the original image"]
82pub fn draw_filled_ellipse<I>(
83 image: &I,
84 center: (i32, i32),
85 width_radius: i32,
86 height_radius: i32,
87 color: I::Pixel,
88) -> Image<I::Pixel>
89where
90 I: GenericImage,
91{
92 let mut out = ImageBuffer::new(image.width(), image.height());
93 out.copy_from(image, 0, 0).unwrap();
94 draw_filled_ellipse_mut(&mut out, center, width_radius, height_radius, color);
95 out
96}
97
98pub fn draw_filled_ellipse_mut<C>(
109 canvas: &mut C,
110 center: (i32, i32),
111 width_radius: i32,
112 height_radius: i32,
113 color: C::Pixel,
114) where
115 C: Canvas,
116{
117 if width_radius == height_radius {
119 draw_filled_circle_mut(canvas, center, width_radius, color);
120 return;
121 }
122
123 let draw_line_pairs = |x0: i32, y0: i32, x: i32, y: i32| {
124 draw_line_segment_mut(
125 canvas,
126 ((x0 - x) as f32, (y0 + y) as f32),
127 ((x0 + x) as f32, (y0 + y) as f32),
128 color,
129 );
130 draw_line_segment_mut(
131 canvas,
132 ((x0 - x) as f32, (y0 - y) as f32),
133 ((x0 + x) as f32, (y0 - y) as f32),
134 color,
135 );
136 };
137
138 draw_ellipse(draw_line_pairs, center, width_radius, height_radius);
139}
140
141fn draw_ellipse<F>(mut render_func: F, center: (i32, i32), width_radius: i32, height_radius: i32)
145where
146 F: FnMut(i32, i32, i32, i32),
147{
148 let (x0, y0) = center;
149 let w2 = width_radius * width_radius;
150 let h2 = height_radius * height_radius;
151 let mut x = 0;
152 let mut y = height_radius;
153 let mut px = 0;
154 let mut py = 2 * w2 * y;
155
156 render_func(x0, y0, x, y);
157
158 let mut p = (h2 - (w2 * height_radius)) as f32 + (0.25 * w2 as f32);
160 while px < py {
161 x += 1;
162 px += 2 * h2;
163 if p < 0.0 {
164 p += (h2 + px) as f32;
165 } else {
166 y -= 1;
167 py += -2 * w2;
168 p += (h2 + px - py) as f32;
169 }
170
171 render_func(x0, y0, x, y);
172 }
173
174 p = (h2 as f32) * (x as f32 + 0.5).powi(2) + (w2 * (y - 1).pow(2)) as f32 - (w2 * h2) as f32;
176 while y > 0 {
177 y -= 1;
178 py += -2 * w2;
179 if p > 0.0 {
180 p += (w2 - py) as f32;
181 } else {
182 x += 1;
183 px += 2 * h2;
184 p += (w2 - py + px) as f32;
185 }
186
187 render_func(x0, y0, x, y);
188 }
189}
190
191#[must_use = "the function does not modify the original image"]
195pub fn draw_hollow_circle<I>(
196 image: &I,
197 center: (i32, i32),
198 radius: i32,
199 color: I::Pixel,
200) -> Image<I::Pixel>
201where
202 I: GenericImage,
203{
204 let mut out = ImageBuffer::new(image.width(), image.height());
205 out.copy_from(image, 0, 0).unwrap();
206 draw_hollow_circle_mut(&mut out, center, radius, color);
207 out
208}
209
210pub fn draw_hollow_circle_mut<C>(canvas: &mut C, center: (i32, i32), radius: i32, color: C::Pixel)
214where
215 C: Canvas,
216{
217 let mut x = 0i32;
218 let mut y = radius;
219 let mut p = 1 - radius;
220 let x0 = center.0;
221 let y0 = center.1;
222
223 while x <= y {
224 draw_if_in_bounds(canvas, x0 + x, y0 + y, color);
225 draw_if_in_bounds(canvas, x0 + y, y0 + x, color);
226 draw_if_in_bounds(canvas, x0 - y, y0 + x, color);
227 draw_if_in_bounds(canvas, x0 - x, y0 + y, color);
228 draw_if_in_bounds(canvas, x0 - x, y0 - y, color);
229 draw_if_in_bounds(canvas, x0 - y, y0 - x, color);
230 draw_if_in_bounds(canvas, x0 + y, y0 - x, color);
231 draw_if_in_bounds(canvas, x0 + x, y0 - y, color);
232
233 x += 1;
234 if p < 0 {
235 p += 2 * x + 1;
236 } else {
237 y -= 1;
238 p += 2 * (x - y) + 1;
239 }
240 }
241}
242
243pub fn draw_filled_circle_mut<C>(canvas: &mut C, center: (i32, i32), radius: i32, color: C::Pixel)
247where
248 C: Canvas,
249{
250 let mut x = 0i32;
251 let mut y = radius;
252 let mut p = 1 - radius;
253 let x0 = center.0;
254 let y0 = center.1;
255
256 while x <= y {
257 draw_line_segment_mut(
258 canvas,
259 ((x0 - x) as f32, (y0 + y) as f32),
260 ((x0 + x) as f32, (y0 + y) as f32),
261 color,
262 );
263 draw_line_segment_mut(
264 canvas,
265 ((x0 - y) as f32, (y0 + x) as f32),
266 ((x0 + y) as f32, (y0 + x) as f32),
267 color,
268 );
269 draw_line_segment_mut(
270 canvas,
271 ((x0 - x) as f32, (y0 - y) as f32),
272 ((x0 + x) as f32, (y0 - y) as f32),
273 color,
274 );
275 draw_line_segment_mut(
276 canvas,
277 ((x0 - y) as f32, (y0 - x) as f32),
278 ((x0 + y) as f32, (y0 - x) as f32),
279 color,
280 );
281
282 x += 1;
283 if p < 0 {
284 p += 2 * x + 1;
285 } else {
286 y -= 1;
287 p += 2 * (x - y) + 1;
288 }
289 }
290}
291
292#[must_use = "the function does not modify the original image"]
296pub fn draw_filled_circle<I>(
297 image: &I,
298 center: (i32, i32),
299 radius: i32,
300 color: I::Pixel,
301) -> Image<I::Pixel>
302where
303 I: GenericImage,
304{
305 let mut out = ImageBuffer::new(image.width(), image.height());
306 out.copy_from(image, 0, 0).unwrap();
307 draw_filled_circle_mut(&mut out, center, radius, color);
308 out
309}
310
311#[cfg(test)]
312mod tests {
313 use image::{GrayImage, Luma};
314
315 macro_rules! bench_hollow_ellipse {
316 ($name:ident, $center:expr, $width_radius:expr, $height_radius:expr) => {
317 #[bench]
318 fn $name(b: &mut test::Bencher) {
319 use super::draw_hollow_ellipse_mut;
320
321 let mut image = GrayImage::new(500, 500);
322 let color = Luma([50u8]);
323 b.iter(|| {
324 draw_hollow_ellipse_mut(
325 &mut image,
326 $center,
327 $width_radius,
328 $height_radius,
329 color,
330 );
331 test::black_box(&image);
332 });
333 }
334 };
335 }
336
337 bench_hollow_ellipse!(bench_bench_hollow_ellipse_circle, (200, 200), 80, 80);
338 bench_hollow_ellipse!(bench_bench_hollow_ellipse_vertical, (200, 200), 40, 100);
339 bench_hollow_ellipse!(bench_bench_hollow_ellipse_horizontal, (200, 200), 100, 40);
340
341 macro_rules! bench_filled_ellipse {
342 ($name:ident, $center:expr, $width_radius:expr, $height_radius:expr) => {
343 #[bench]
344 fn $name(b: &mut test::Bencher) {
345 use super::draw_filled_ellipse_mut;
346
347 let mut image = GrayImage::new(500, 500);
348 let color = Luma([50u8]);
349 b.iter(|| {
350 draw_filled_ellipse_mut(
351 &mut image,
352 $center,
353 $width_radius,
354 $height_radius,
355 color,
356 );
357 test::black_box(&image);
358 });
359 }
360 };
361 }
362
363 bench_filled_ellipse!(bench_bench_filled_ellipse_circle, (200, 200), 80, 80);
364 bench_filled_ellipse!(bench_bench_filled_ellipse_vertical, (200, 200), 40, 100);
365 bench_filled_ellipse!(bench_bench_filled_ellipse_horizontal, (200, 200), 100, 40);
366}