fast image operations
Diffstat (limited to 'src/drawing/line.rs')
-rw-r--r--src/drawing/line.rs71
1 files changed, 71 insertions, 0 deletions
diff --git a/src/drawing/line.rs b/src/drawing/line.rs
new file mode 100644
index 0000000..24c0df4
--- /dev/null
+++ b/src/drawing/line.rs
@@ -0,0 +1,71 @@
+//! adds a `line` function to Image
+use crate::Image;
+use vecto::Vec2;
+
+impl<T: AsMut<[u8]> + AsRef<[u8]>, const CHANNELS: usize> Image<T, CHANNELS> {
+ /// Draw a half-open line from point a to point b.
+ ///
+ /// Points not in bounds will not be included.
+ pub fn line(&mut self, a: (i32, i32), b: (i32, i32), color: [u8; CHANNELS]) {
+ clipline::Clip::<i32>::from_size(self.width(), self.height())
+ .unwrap() // fixme: panics if width or height > i32::MAX + 1
+ .line_b_proj(a.0, a.1, b.0, b.1)
+ .into_iter()
+ .flatten()
+ .for_each(|(x, y)| {
+ // SAFETY: x, y are clipped to self.
+ unsafe { self.set_pixel(x, y, &color) }
+ });
+ }
+
+ /// Draw a thick line from point a to point b.
+ /// Prefer [`Image::line`] when possible.
+ ///
+ /// Points not in bounds will not be included.
+ ///
+ /// Uses [`Image::quad`].
+ /// ```
+ /// # use fimg::Image;
+ /// let mut i = Image::alloc(10, 10);
+ /// i.thick_line((2.0, 2.0), (8.0, 8.0), 2.0, [255]);
+ /// # assert_eq!(i.buffer(), b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\xff\x00\x00");
+ /// ```
+ pub fn thick_line(
+ &mut self,
+ a: impl Into<Vec2>,
+ b: impl Into<Vec2>,
+ stroke: f32,
+ color: [u8; CHANNELS],
+ ) {
+ let a = a.into();
+ let b = b.into();
+ let w = (a - b).orthogonal().normalized() * (stroke / 2.0);
+ macro_rules! p {
+ ($x:expr) => {
+ #[allow(clippy::cast_possible_truncation)]
+ ($x.x.round() as i32, $x.y.round() as i32)
+ };
+ }
+ // order:
+ // v x1 v x2
+ // [ ]
+ // ^ x3 ^ x4
+ self.quad(
+ p!(a - w), // x1
+ p!(b - w), // x2
+ p!(b + w), // x3
+ p!(a + w), // x4
+ color,
+ );
+ }
+}
+
+#[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\x00\x00\x00\x00\x00\xff\xff\x00\x00\x00\x00\x00\xff\xff\x00\x00\x00\x00\x00"
+ )
+}