fast image operations
-rw-r--r--src/drawing/line.rs174
-rw-r--r--src/drawing/mod.rs2
-rw-r--r--src/lib.rs1
3 files changed, 177 insertions, 0 deletions
diff --git a/src/drawing/line.rs b/src/drawing/line.rs
new file mode 100644
index 0000000..c48f923
--- /dev/null
+++ b/src/drawing/line.rs
@@ -0,0 +1,174 @@
+//! adds a `line` function to Image
+#![allow(clippy::missing_docs_in_private_items)]
+use crate::Image;
+use std::iter::Iterator;
+
+/// taken from https://github.com/mbr/bresenham-rs/
+pub struct Bresenham {
+ x: i32,
+ y: i32,
+ dx: i32,
+ dy: i32,
+ x1: i32,
+ diff: i32,
+ octant: Octant,
+}
+
+#[derive(Copy, Clone)]
+struct Octant(u8);
+
+impl Octant {
+ #[inline]
+ const fn from_points(start: (i32, i32), end: (i32, i32)) -> Octant {
+ let mut dx = end.0 - start.0;
+ let mut dy = end.1 - start.1;
+
+ let mut octant = 0;
+
+ if dy < 0 {
+ dx = -dx;
+ dy = -dy;
+ octant += 4;
+ }
+
+ if dx < 0 {
+ let tmp = dx;
+ dx = dy;
+ dy = -tmp;
+ octant += 2
+ }
+
+ if dx < dy {
+ octant += 1
+ }
+
+ Octant(octant)
+ }
+
+ #[inline]
+ const fn to_octant0(self, p: (i32, i32)) -> (i32, i32) {
+ match self.0 {
+ 0 => (p.0, p.1),
+ 1 => (p.1, p.0),
+ 2 => (p.1, -p.0),
+ 3 => (-p.0, p.1),
+ 4 => (-p.0, -p.1),
+ 5 => (-p.1, -p.0),
+ 6 => (-p.1, p.0),
+ 7 => (p.0, -p.1),
+ _ => unreachable!(),
+ }
+ }
+
+ #[inline]
+ #[allow(clippy::wrong_self_convention)]
+ fn from_octant0(self, p: (i32, i32)) -> (i32, i32) {
+ match self.0 {
+ 0 => (p.0, p.1),
+ 1 => (p.1, p.0),
+ 2 => (-p.1, p.0),
+ 3 => (-p.0, p.1),
+ 4 => (-p.0, -p.1),
+ 5 => (-p.1, -p.0),
+ 6 => (p.1, -p.0),
+ 7 => (p.0, -p.1),
+ _ => unreachable!(),
+ }
+ }
+}
+
+impl Bresenham {
+ /// Creates a new iterator. Yields intermediate points between `start`
+ /// and `end`. Includes `start` and `end`.
+ #[inline]
+ pub const fn new(start: (i32, i32), end: (i32, i32)) -> Bresenham {
+ let octant = Octant::from_points(start, end);
+
+ let start = octant.to_octant0(start);
+ let end = octant.to_octant0(end);
+
+ let dx = end.0 - start.0;
+ let dy = end.1 - start.1;
+
+ Bresenham {
+ x: start.0,
+ y: start.1,
+ dy,
+ dx,
+ x1: end.0,
+ diff: dy - dx,
+ octant,
+ }
+ }
+}
+
+impl Iterator for Bresenham {
+ type Item = (i32, i32);
+
+ #[inline]
+ fn next(&mut self) -> Option<Self::Item> {
+ if self.x > self.x1 {
+ return None;
+ }
+
+ let p = (self.x, self.y);
+
+ if self.diff >= 0 {
+ self.y += 1;
+ self.diff -= self.dx;
+ }
+
+ self.diff += self.dy;
+
+ // loop inc
+ self.x += 1;
+
+ Some(self.octant.from_octant0(p))
+ }
+}
+
+impl<const CHANNELS: usize> Image<&mut [u8], CHANNELS> {
+ /// Draw a line from point a to point b
+ ///
+ /// Points not in bounds will not be included.
+ ///
+ /// Uses [bresenshams](https://en.wikipedia.org/wiki/Bresenham%27s_line_algorithm) line algorithm.
+ pub fn line(&mut self, a: (i32, i32), b: (i32, i32), color: [u8; CHANNELS]) {
+ for (x, y) in Bresenham::new(a, b).map(|(x, y)| (x as u32, y as u32)) {
+ if x < self.width() && y < self.height() {
+ // SAFETY: bound are checked ^
+ unsafe { self.set_pixel(x, y, color) };
+ }
+ }
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn bresenham() {
+ macro_rules! test_bresenham {
+ ($a:expr, $b:expr => [$(($x:expr, $y:expr)),+]) => {{
+ let mut bi = Bresenham::new($a, $b);
+ $(assert_eq!(bi.next(), Some(($x, $y)));)+
+ assert_eq!(bi.next(), None);
+ }}
+ }
+ test_bresenham!((6, 4), (0, 1) => [(6, 4), (5, 4), (4, 3), (3, 3), (2, 2), (1, 2), (0, 1)]);
+ test_bresenham!((2, 3), (2, 6) => [(2, 3), (2, 4), (2, 5), (2, 6)]);
+ test_bresenham!((2, 3), (5, 3) => [(2, 3), (3, 3), (4, 3), (5, 3)]);
+ test_bresenham!((0, 1), (6, 4) => [(0, 1), (1, 1), (2, 2), (3, 2), (4, 3), (5, 3), (6, 4)]);
+ }
+
+ #[test]
+ fn line() {
+ let mut a = Image::build(5, 5).alloc();
+ a.as_mut().line((0, 1), (6, 4), [255]);
+ assert_eq!(
+ a.buffer,
+ b"\x00\x00\x00\x00\x00\xff\xff\x00\x00\x00\x00\x00\xff\xff\x00\x00\x00\x00\x00\xff\x00\x00\x00\x00\x00"
+ )
+ }
+}
diff --git a/src/drawing/mod.rs b/src/drawing/mod.rs
new file mode 100644
index 0000000..18bbc5d
--- /dev/null
+++ b/src/drawing/mod.rs
@@ -0,0 +1,2 @@
+//! contains drawing operations, like line drawing
+mod line;
diff --git a/src/lib.rs b/src/lib.rs
index 2bbfa9e..e72ea42 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -27,6 +27,7 @@ use std::{num::NonZeroU32, slice::SliceIndex};
mod affine;
pub mod builder;
+mod drawing;
mod overlay;
pub use overlay::{Overlay, OverlayAt};