From 3eeeeb488b84e7226a4010bee501646dd9113b81 Mon Sep 17 00:00:00 2001 From: DongHun Kwak Date: Thu, 6 Apr 2023 16:21:14 +0900 Subject: [PATCH 1/1] Import loop9 0.1.3 --- .cargo_vcs_info.json | 5 ++ Cargo.toml | 27 ++++++++++ Cargo.toml.orig | 17 ++++++ README.md | 5 ++ src/lib.rs | 147 +++++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 201 insertions(+) create mode 100644 .cargo_vcs_info.json create mode 100644 Cargo.toml create mode 100644 Cargo.toml.orig create mode 100644 README.md create mode 100644 src/lib.rs diff --git a/.cargo_vcs_info.json b/.cargo_vcs_info.json new file mode 100644 index 0000000..1540f85 --- /dev/null +++ b/.cargo_vcs_info.json @@ -0,0 +1,5 @@ +{ + "git": { + "sha1": "7211660b2c3eed51c35ee5513648ea837b7ba48b" + } +} diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..9a85aba --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,27 @@ +# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO +# +# When uploading crates to the registry Cargo will automatically +# "normalize" Cargo.toml files for maximal compatibility +# with all versions of Cargo and also rewrite `path` dependencies +# to registry (e.g., crates.io) dependencies +# +# If you believe there's an error in this file please file an +# issue against the rust-lang/cargo repository. If you're +# editing this file be aware that the upstream Cargo.toml +# will likely look very different (and much more reasonable) + +[package] +edition = "2018" +name = "loop9" +version = "0.1.3" +authors = ["Kornel "] +description = "Tiny helper function to visit every pixel in the image together with its neighboring pixels. Duplicates pixels on the edges." +homepage = "https://lib.rs/loop9" +readme = "README.md" +keywords = ["convolve", "blur", "iterate"] +categories = ["graphics"] +license = "MIT" +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] +[dependencies.imgref] +version = "1.7.0" diff --git a/Cargo.toml.orig b/Cargo.toml.orig new file mode 100644 index 0000000..bcefdf5 --- /dev/null +++ b/Cargo.toml.orig @@ -0,0 +1,17 @@ +[package] +authors = ["Kornel "] +description = "Tiny helper function to visit every pixel in the image together with its neighboring pixels. Duplicates pixels on the edges." +license = "MIT" +name = "loop9" +readme = "README.md" +version = "0.1.3" +edition = "2018" +homepage = "https://lib.rs/loop9" +categories = ["graphics"] +keywords = ["convolve", "blur", "iterate"] + +[dependencies] +imgref = "1.7.0" + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] diff --git a/README.md b/README.md new file mode 100644 index 0000000..fa0cd40 --- /dev/null +++ b/README.md @@ -0,0 +1,5 @@ +# 9 neighboring pixels iterator + +Tiny helper function to visit every pixel in the image together with its neighboring pixels. Duplicates pixels on the edges. + +Ideal for implementing small image convolutions like blur, sharpening or edge detection. diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..2353c48 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,147 @@ +use core::ops::Deref; +use imgref::*; + +/// Previous, current, and next pixel or row +#[derive(Copy,Clone,Debug,Eq,PartialEq)] +#[repr(C)] +pub struct Triple { + pub prev: T, + pub curr: T, + pub next: T, +} + +impl Triple { + #[inline(always)] + pub fn new(prev: T, curr: T, next: T) -> Self { + Triple {prev, curr, next} + } +} + +impl AsRef<[T]> for Triple { + #[inline(always)] + fn as_ref(&self) -> &[T] { + unsafe { + std::slice::from_raw_parts(&self.prev as *const T, 3) + } + } +} + +impl Deref for Triple { + type Target = [T]; + #[inline(always)] + fn deref(&self) -> &Self::Target { + self.as_ref() + } +} + +impl Triple { + /// Add next item, and shift others (prev is gone, prev = current) + /// If item is None, it'll copy the last one instead. + #[inline(always)] + pub fn advance(self, next: Option) -> Self { + let next = if let Some(next) = next {next} else {self.next}; + Triple { + prev: self.curr, + curr: self.next, + next, + } + } +} + +/// Loop over 9 neighboring pixels in the image described by `ImgRef` (`Img.as_ref()`) +/// +/// The callback is: (x, y, previous_row, current_row, next_row) +#[inline(always)] +pub fn loop9_img(img: ImgRef<'_, Pixel>, cb: Callback) + where Pixel: Copy, Callback: FnMut(usize, usize, Triple,Triple,Triple) +{ + loop9(img, 0, 0, img.width(), img.height(), cb) +} + +/// Loop over 9 neighboring pixels in the left/top/width/height fragment of the image described by `ImgRef` (`Img.as_ref()`) +/// +/// The callback is: (x, y, previous_row, current_row, next_row) +#[inline] +pub fn loop9(img: ImgRef<'_, Pixel>, left: usize, top: usize, width: usize, height: usize, mut cb: Callback) + where Pixel: Copy, Callback: FnMut(usize, usize, Triple,Triple,Triple) +{ + let max_width = img.width(); + let max_height = img.height(); + let stride = img.stride(); + let data = img.buf().as_ref(); + let t = top * stride; + let mut row = Triple { + prev: &data[t..t+max_width], + curr: &data[t..t+max_width], + next: &data[t..t+max_width], + }; + for y in top..top+height { + row = row.advance(if y+1 < max_height {let t=(y+1)*stride; Some(&data[t..t+max_width])} else {None}); + let mut tp; + let mut tn = row.prev[left]; + let mut tc = row.prev[left.saturating_sub(1)]; + let mut mp; + let mut mn = row.curr[left]; + let mut mc = row.curr[left.saturating_sub(1)]; + let mut bp; + let mut bn = row.next[left]; + let mut bc = row.next[left.saturating_sub(1)]; + for x in left..left+width { + tp = tc; + tc = tn; + tn = if x+1 < max_width {row.prev[x+1]} else {tc}; + mp = mc; + mc = mn; + mn = if x+1 < max_width {row.curr[x+1]} else {mc}; + bp = bc; + bc = bn; + bn = if x+1 < max_width {row.next[x+1]} else {bc}; + cb(x-left,y-top,Triple::new(tp,tc,tn),Triple::new(mp,mc,mn),Triple::new(bp,bc,bn)); + } + } +} + + +#[test] +fn test_loop9() { + let src = vec![ + 1, 2, 3, 4, + 5, 6, 7, 8, + 9,10,11,12, + 13,14,15,16, + ]; + let img = Img::new(src.clone(), 4, 4); + assert_eq!(4, img.width()); + assert_eq!(4, img.stride()); + assert_eq!(4, img.height()); + + let check = |l,t,w,h,exp|{ + let mut res = Vec::new(); + loop9(img.as_ref(), l,t,w,h, |_x,_y,_top,mid,_bot| res.push(mid.curr)); + assert_eq!(exp, res); + }; + + check(0,0,4,4, src); + + check(0,0,4,1, vec![1, 2, 3, 4]); + + check(0,3,4,1, vec![13,14,15,16]); + + check(0,0,3,3, vec![ + 1, 2, 3, + 5, 6, 7, + 9,10,11, + ]); + + check(0,0,1,1,vec![1]); + check(1,0,1,1,vec![2]); + check(2,0,1,1,vec![3]); + check(3,0,1,1,vec![4]); + + check(1,0,3,4,vec![ + 2, 3, 4, + 6, 7, 8, + 10,11,12, + 14,15,16, + ]); +} -- 2.7.4