From 1e98d959dbaf20de80b242b09c08fb43ff03d0a5 Mon Sep 17 00:00:00 2001 From: bendn Date: Wed, 1 Nov 2023 10:21:24 +0700 Subject: [PATCH] use clipline crate --- Cargo.toml | 1 + benches/drawing.rs | 10 +- src/convert.rs | 4 +- src/drawing/line.rs | 175 +++-------------------------------- src/drawing/poly.rs | 6 +- tdata/border_pentagon.imgbuf | Bin 10000 -> 10000 bytes tdata/enneagon.imgbuf | Bin 90000 -> 90000 bytes 7 files changed, 29 insertions(+), 167 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index ed1b715..87f4489 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,6 +18,7 @@ vecto = "0.1.0" umath = "0.0.7" fr = { version = "0.1.1", package = "fer", optional = true } stackblur-iter = { version = "0.2.0", features = ["simd"], optional = true } +clipline = "0.1.1" [dev-dependencies] iai = { git = "https://github.com/bend-n/iai.git" } diff --git a/benches/drawing.rs b/benches/drawing.rs index 8375bf3..61bf4d9 100644 --- a/benches/drawing.rs +++ b/benches/drawing.rs @@ -2,6 +2,12 @@ use fimg::*; fn tri() { let mut i: Image<_, 4> = fimg::make!(4 channels 1000 x 1000).boxed(); i.tri((0., 0.), (1000., 500.), (0., 999.), [255, 255, 255, 255]); - std::hint::black_box(i); + iai::black_box(i); } -iai::main!(tri); +fn line() { + let mut i: Image<_, 4> = fimg::make!(4 channels 500 x 750).boxed(); + i.line((-50, 20), (550, 800), [255, 165, 0, 255]); + i.save("z.png"); + iai::black_box(i); +} +iai::main!(tri, line); diff --git a/src/convert.rs b/src/convert.rs index 6f44116..8a6d7a4 100644 --- a/src/convert.rs +++ b/src/convert.rs @@ -64,12 +64,12 @@ boxconv!(4 => 2); boxconv!(4 => 3); #[inline] -fn pack([r, g, b, a]: [u8; 4]) -> u32 { +const fn pack([r, g, b, a]: [u8; 4]) -> u32 { ((a as u32) << 24) | ((r as u32) << 16) | ((g as u32) << 8) | (b as u32) } #[inline] -fn unpack(n: u32) -> [u8; 4] { +const fn unpack(n: u32) -> [u8; 4] { [ ((n >> 16) & 0xFF) as u8, ((n >> 8) & 0xFF) as u8, diff --git a/src/drawing/line.rs b/src/drawing/line.rs index e1dde96..ef30d08 100644 --- a/src/drawing/line.rs +++ b/src/drawing/line.rs @@ -1,146 +1,21 @@ //! adds a `line` function to Image -#![allow(clippy::missing_docs_in_private_items)] use crate::Image; -use std::iter::Iterator; use vecto::Vec2; -/// taken from [bresenham-rs](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)) -> Self { - 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; - } - - Self(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)) -> Self { - 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; - - Self { - 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 { - 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 + AsRef<[u8]>, const CHANNELS: usize> Image { /// 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) }; - } - } + clipline::clipline( + ((a.0 as isize, a.1 as isize), (b.0 as isize, b.1 as isize)), + ( + (0, 0), + (self.width() as isize - 1, self.height() as isize - 1), + ), + // SAFETY: clipline clips + |x, y| unsafe { self.set_pixel(x as u32, y as u32, color) }, + ); } /// Draw a thick line from point a to point b. @@ -185,32 +60,12 @@ impl + AsRef<[u8]>, const CHANNELS: usize> Image { } } -#[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!( +#[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" + 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" ) - } } diff --git a/src/drawing/poly.rs b/src/drawing/poly.rs index 0b8f3ac..31632f3 100644 --- a/src/drawing/poly.rs +++ b/src/drawing/poly.rs @@ -14,7 +14,7 @@ impl + AsRef<[u8]>, const CHANNELS: usize> Image { /// # use fimg::Image; /// let mut i = Image::alloc(10, 10); /// i.points(&[(1, 8), (3, 1), (8, 1), (6, 6), (8, 8), (1, 8)], [255]); - /// # assert_eq!(i.buffer(), b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\xff\xff\x00\x00\x00\xff\xff\xff\xff\xff\xff\xff\x00\x00\x00\xff\xff\xff\xff\xff\xff\xff\x00\x00\x00\xff\xff\xff\xff\xff\xff\x00\x00\x00\xff\xff\xff\xff\xff\xff\xff\x00\x00\x00\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\xff\xff\xff\xff\xff\xff\xff\x00\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"); + /// # assert_eq!(i.buffer(), b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\xff\xff\xff\xff\xff\xff\x00\x00\x00\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\xff\xff\xff\xff\xff\x00\x00\x00\x00\xff\xff\xff\xff\xff\xff\xff\x00\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"); /// ``` pub fn points(&mut self, poly: &[(i32, i32)], c: [u8; CHANNELS]) { if poly.len() <= 1 { @@ -110,7 +110,7 @@ impl + AsRef<[u8]>, const CHANNELS: usize> Image { match sides { 3 => { let space = TAU / 3.0; - self.tri( + self.tri::( trans(space + rotation) + pos, trans(rotation) + pos, trans(madd(space, 2.0, rotation)) + pos, @@ -132,7 +132,7 @@ impl + AsRef<[u8]>, const CHANNELS: usize> Image { if sides % 2 != 0 && sides > 4 { let i = (sides - 1) as f32; // the missing piece - self.tri( + self.tri::( pos, trans(madd(space, i, rotation)) + pos, trans(madd(space, i + 1., rotation)) + pos, diff --git a/tdata/border_pentagon.imgbuf b/tdata/border_pentagon.imgbuf index 39004de8952c9cfc9e637a86a79d0035677228f1..a71102750531d1ccc8df4cf07eeb9a63371dc55f 100644 GIT binary patch literal 10000 zcmeHIQLe)v4D0Mln^ao+i>G+3pN?JTtPZQ z;2{AVNrQR}=s_e*6k2FH2U;YQ4$(gTVn5Jtc~uN5h~Yv}hXR zD59l-NZYhI%(B}u-zI6=m@%}8EyLHAF~b#N&G3Z0D#Njec@IA3kDsjv8N-{rEn{?% zm+g_>j`~kP`#nm7+Wn1B(uL2}a0EWa+xxA<@d$GUA|5xzFK_+LlSKw8GfK0afg~H) z!ny|{)@Ar2Y@azYI*lsVmT`+lu|H|dA3)Jjq6m644Ng>%Q&G7zQFKTsA4|NpX09*K zaJT`XA}{DC2U-XO3$maF09_Vc&(8INx^OH+w5 znfEBoeKurC57JJ3aa#MZOUwm~UE~0^GAR34UqJd~28fZ1} E0U-Zz#{d8T literal 10000 zcmeH{S(3vr2t;%LOFNPiHVY{TaFu5Ye<5i48N9q+b=3xH1GRzLKy9ElP#gH;8#s>R z-U%9@U-v;i{x?DxpojUBTmbou5zsSFBOb*FjA5N4f-o`=vHRMd?+$b;FA__E%7w7a5kr_5K?E|I z58I6h5iwN=`FJ3M9*y;uL5W&5B+&qZp$7{34GeN6fetJXGRlke&Vej=L=KdtoFgI| z9(|24GLcR#a(HjtQL0*-2{hEavFkJ`25JNUZ3AzQXoj)? diff --git a/tdata/enneagon.imgbuf b/tdata/enneagon.imgbuf index cf4a7972e41b6180ccd43f1dc29e1e07e4e2b46e..57bb6b3214e7a38c31851455b3982bc92d7d1fb9 100644 GIT binary patch delta 1030 zcmYk*T}V@L7{_r=+aSzjfn5Y%1Q7&w5#*gju#h0AMs*QHBte7&shF%64CmGh4y%o` zoGzWS!l_wJ|H~1CGpHak8VS)w7hQA_1SLdt5meuwI|sb_JkNh;|D8QA$%-#o@fFj% z?0lQo{My}UrTh)V^_l$}t)-NIFL9#@27H{aCvI|LAMqAqk44aI2Lpd_C;qgTL#_A? z1Na5^;U_$VAMhwH;W4b@aeRj-a1l@9YdnRo@HD=_5I)CFtl$}ZR&KAenk}*}vNE2> z5?;a??8ag_>a9K1OE$&n6)fO&ypMf&7yB`fH*o}0m_-{0F^w6#jRQE~n87spAlha* z^wP2q22yrDGR(m(9C6Iy4f0X!$1$@IUgtHVk-uyB2Co&HAFoJWdHPJ+N8wVj%ES>e4_Cnl4)@l6n;d+8|X7gf)k6#a) zYPDr8Pj9!lQdj6o_iA~Oe0Da@=e6Ia@&{r?61~{NRqBNwkh^~*PCh|?6$^L`$EC?f zxFi=*(nme+Q4bDk#mS>vEUDtT#Y~c`xGHiMRdv4jJv3x?bgaCf+aL8yeH%jcQTf2Y!R6i8_Q~x=nqLcZ6v5`JFp|-n-|X>&|Alvl$a@Md@j2 zX_35jYF;O!6_Mkc)Ss}kSuYglXxIBa?38GyG}p%c#5@}diTO6>6ANw3BOb7EBeBHB z9Ac@BnZ#2Pb+)e@=e4us99B?&#Y^}ZFXKm@EWd&u@M^-Bt#&B=tN1FP-7>yVpqy2y42m+f<5>Q`%G*xp$PpZS(D|_zThCAJi;M- zh%d1*We`WG8*t1d7e*A}H4FEQFIb>VSumKc1EEP)YH=F<|7!xFuI+50(o9S)%3iJ$ z4toeUOl5gXXa>@`PRL(t!m?T{FMDwn+3f#ols-u=2orL4?+nFdC*i!d2l_o z!8;g7`)c}RP>TD!?>H|$s&rX!oCVp5>}Yw1vg2WD>40>iDdl4m^Yb=+{sH