fast image operations
add Image::circle
bendn 2023-09-30
parent e405062 · commit ac3f07c
-rw-r--r--Cargo.toml2
-rw-r--r--src/affine.rs4
-rw-r--r--src/drawing/circle.rs67
-rw-r--r--src/drawing/mod.rs3
-rw-r--r--src/drawing/poly.rs13
-rw-r--r--tdata/circle.imgbufbin0 -> 2500 bytes
-rw-r--r--tdata/circle2.imgbufbin0 -> 2500 bytes
7 files changed, 78 insertions, 11 deletions
diff --git a/Cargo.toml b/Cargo.toml
index 7bfa3f1..8491fda 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "fimg"
-version = "0.4.10"
+version = "0.4.11"
authors = ["bend-n <[email protected]>"]
license = "MIT"
edition = "2021"
diff --git a/src/affine.rs b/src/affine.rs
index 893be81..f4c311e 100644
--- a/src/affine.rs
+++ b/src/affine.rs
@@ -188,9 +188,7 @@ unsafe fn transpose_non_power_of_two<const CHANNELS: usize, T: DerefMut<Target =
for i in 0..size {
for j in i..size {
// SAFETY: caller ensures squarity
- unsafe {
- b.swap_unchecked(i * size + j, j * size + i);
- };
+ unsafe { b.swap_unchecked(i * size + j, j * size + i) };
}
}
}
diff --git a/src/drawing/circle.rs b/src/drawing/circle.rs
new file mode 100644
index 0000000..793dd92
--- /dev/null
+++ b/src/drawing/circle.rs
@@ -0,0 +1,67 @@
+//! draw 2d circles
+use crate::Image;
+use std::ops::DerefMut;
+
+impl<T: DerefMut<Target = [u8]>, const CHANNELS: usize> Image<T, CHANNELS> {
+ /// Draws a circle, using the [Bresenham's circle](https://en.wikipedia.org/wiki/Midpoint_circle_algorithm) algorithm.
+ /// ```
+ /// # use fimg::Image;
+ /// let mut i = Image::alloc(50, 50);
+ /// i.border_circle((25, 25), 20, [255]);
+ /// # assert_eq!(i.buffer(), include_bytes!("../../tdata/circle.imgbuf"));
+ /// ```
+ pub fn border_circle(&mut self, (xc, yc): (i32, i32), radius: i32, c: [u8; CHANNELS]) {
+ let mut x = 0;
+ let mut y = radius;
+ let mut p = 1 - radius;
+ /// bounds the pixels
+ macro_rules! bound {
+ ($($x:expr,$y:expr);+;) => {
+ $(if $x >= 0 && $x < self.width() as i32 && $y >= 0 && $y < self.height() as i32 {
+ // SAFETY: ^
+ unsafe { self.set_pixel($x as u32, $y as u32, c) };
+ })+
+ };
+ }
+ while x <= y {
+ bound! {
+ xc + x, yc + y;
+ xc + y, yc + x;
+ xc - y, yc + x;
+ xc - x, yc + y;
+ xc - x, yc - y;
+ xc - y, yc - x;
+ xc + y, yc - x;
+ xc + x, yc - y;
+ };
+ x += 1;
+ if p < 0 {
+ p += 2 * x + 1;
+ } else {
+ y -= 1;
+ p += 2 * (x - y) + 1;
+ }
+ }
+ }
+
+ /// Draw a filled circle.
+ /// ```
+ /// # use fimg::Image;
+ /// let mut i = Image::alloc(50, 50);
+ /// i.circle((25, 25), 20, [255]);
+ /// # assert_eq!(i.buffer(), include_bytes!("../../tdata/circle2.imgbuf"));
+ /// ```
+ pub fn circle(&mut self, (xc, yc): (i32, i32), radius: i32, c: [u8; CHANNELS]) {
+ for x in -radius..radius {
+ let h = ((radius * radius - x * x) as f32).sqrt().round() as i32;
+ for y in -h..h {
+ let x = x + xc;
+ let y = y + yc;
+ if x >= 0 && x < self.width() as i32 && y >= 0 && y < self.height() as i32 {
+ // SAFETY: ^
+ unsafe { self.set_pixel(x as u32, y as u32, c) };
+ }
+ }
+ }
+ }
+}
diff --git a/src/drawing/mod.rs b/src/drawing/mod.rs
index 87b4b88..ebc4e99 100644
--- a/src/drawing/mod.rs
+++ b/src/drawing/mod.rs
@@ -1,5 +1,6 @@
-//! contains drawing operations, like {line, box, triangle, polygon} drawing
+//! contains drawing operations, like {line, box, triangle, polygon, circle} drawing
mod r#box;
+mod circle;
mod line;
mod poly;
mod tri;
diff --git a/src/drawing/poly.rs b/src/drawing/poly.rs
index e0dd017..3847a56 100644
--- a/src/drawing/poly.rs
+++ b/src/drawing/poly.rs
@@ -86,6 +86,7 @@ impl<T: DerefMut<Target = [u8]>, const CHANNELS: usize> Image<T, CHANNELS> {
}
/// Draws a regular convex polygon with a specified number of sides, a radius, and a rotation (radians).
+ /// Prefer [`Image::circle`] over `poly(.., 600, ..)`.
/// Calls into [`Image::tri`] and [`Image::quad`].
/// ```
/// # use fimg::Image;
@@ -113,7 +114,7 @@ impl<T: DerefMut<Target = [u8]>, const CHANNELS: usize> Image<T, CHANNELS> {
self.tri(
add(trans(space + rotation)),
add(trans(rotation)),
- add(trans(space * 2.0 + rotation)),
+ add(trans(space.mul_add(2.0, rotation))),
c,
);
}
@@ -122,9 +123,9 @@ impl<T: DerefMut<Target = [u8]>, const CHANNELS: usize> Image<T, CHANNELS> {
for i in (0..sides - 1).step_by(2).map(|i| i as f32) {
self.quad(
r((x, y)),
- r(add(trans(space * i + rotation))),
- r(add(trans(space * (i + 1.) + rotation))),
- r(add(trans(space * (i + 2.) + rotation))),
+ r(add(trans(space.mul_add(i, rotation)))),
+ r(add(trans(space.mul_add(i + 1., rotation)))),
+ r(add(trans(space.mul_add(i + 2., rotation)))),
c,
);
}
@@ -134,8 +135,8 @@ impl<T: DerefMut<Target = [u8]>, const CHANNELS: usize> Image<T, CHANNELS> {
// the missing piece
self.tri(
(x, y),
- add(trans(space * i + rotation)),
- add(trans(space * (i + 1.) + rotation)),
+ add(trans(space.mul_add(i, rotation))),
+ add(trans(space.mul_add(i + 1., rotation))),
c,
);
}
diff --git a/tdata/circle.imgbuf b/tdata/circle.imgbuf
new file mode 100644
index 0000000..3235779
--- /dev/null
+++ b/tdata/circle.imgbuf
Binary files differ
diff --git a/tdata/circle2.imgbuf b/tdata/circle2.imgbuf
new file mode 100644
index 0000000..26d22fd
--- /dev/null
+++ b/tdata/circle2.imgbuf
Binary files differ