fast image operations
add image scaling
| -rw-r--r-- | Cargo.toml | 3 | ||||
| -rw-r--r-- | src/cat.png | bin | 0 -> 2533832 bytes | |||
| -rw-r--r-- | src/drawing/box.rs | 2 | ||||
| -rw-r--r-- | src/lib.rs | 24 | ||||
| -rw-r--r-- | src/scale.rs | 41 | ||||
| -rw-r--r-- | src/small_cat.png | bin | 0 -> 91214 bytes |
6 files changed, 68 insertions, 2 deletions
@@ -1,11 +1,12 @@ [package] name = "fimg" -version = "0.4.0" +version = "0.4.1" authors = ["bend-n <[email protected]>"] license = "MIT" edition = "2021" description = "fast image operations" repository = "https://github.com/bend-n/fimg" +exclude = ["src/cat.png", "src/small_cat.png", "benches/"] [dependencies] png = { version = "0.17", features = ["unstable"], optional = true } diff --git a/src/cat.png b/src/cat.png Binary files differnew file mode 100644 index 0000000..361804d --- /dev/null +++ b/src/cat.png diff --git a/src/drawing/box.rs b/src/drawing/box.rs index 294569a..15f183f 100644 --- a/src/drawing/box.rs +++ b/src/drawing/box.rs @@ -1,4 +1,4 @@ -//! Box<cat> +//! `Box<cat>` use crate::Image; impl<const CHANNELS: usize> Image<&mut [u8], CHANNELS> { @@ -29,6 +29,7 @@ mod affine; pub mod builder; mod drawing; mod overlay; +pub mod scale; pub use overlay::{Overlay, OverlayAt}; /// like assert!(), but causes undefined behaviour at runtime when the condition is not met. @@ -361,6 +362,29 @@ macro_rules! save { }; } +impl<const CHANNELS: usize> Image<Vec<u8>, CHANNELS> { + #[cfg(feature = "save")] + /// Open a PNG image + pub fn open(f: impl AsRef<std::path::Path>) -> Self { + let p = std::fs::File::open(f).unwrap(); + let r = std::io::BufReader::new(p); + let dec = png::Decoder::new(r); + let mut reader = dec.read_info().unwrap(); + let mut buf = vec![0; reader.output_buffer_size()]; + let info = reader.next_frame(&mut buf).unwrap(); + use png::ColorType::*; + match info.color_type { + Indexed | Grayscale => { + assert_eq!(CHANNELS, 1, "indexed | grayscale requires one channel") + } + Rgb => assert_eq!(CHANNELS, 3, "rgb requires three channels"), + Rgba => assert_eq!(CHANNELS, 4, "rgba requires four channels"), + GrayscaleAlpha => assert_eq!(CHANNELS, 2, "ya requires two channels"), + } + Self::build(info.width, info.height).buf(buf) + } +} + save!(3 == Rgb("RGB")); save!(4 == Rgba("RGBA")); save!(2 == GrayscaleAlpha("YA")); diff --git a/src/scale.rs b/src/scale.rs new file mode 100644 index 0000000..0ba614b --- /dev/null +++ b/src/scale.rs @@ -0,0 +1,41 @@ +//! holds scaling operations, at current only the Nearest Neighbor +use crate::Image; + +/// [Nearest Neighbor](https://en.wikipedia.org/wiki/Nearest-neighbor_interpolation) image scaling algorithm implementation. +/// Use [`Nearest::scale`]. +pub struct Nearest; +impl Nearest { + /// Resize a image. + /// # Safety + /// + /// `image` must be as big or bigger than `width`, `height. + pub unsafe fn scale<const N: usize>( + image: Image<&[u8], N>, + width: u32, + height: u32, + ) -> Image<Vec<u8>, N> { + let x_scale = image.width() as f32 / width as f32; + let y_scale = image.height() as f32 / height as f32; + let mut out = Image::alloc(width, height); + for y in 0..height { + for x in 0..width { + let x1 = ((x as f32 + 0.5) * x_scale).floor() as u32; + let y1 = ((y as f32 + 0.5) * y_scale).floor() as u32; + // SAFETY: i asked the caller to make sure its ok + let px = unsafe { image.pixel(x1, y1) }; + // SAFETY: were looping over the width and height of out. its ok. + unsafe { out.set_pixel(x, y, px) }; + } + } + out + } +} + +#[test] +fn test_nearest() { + let i = Image::<_, 3>::open("src/cat.png"); + assert_eq!( + unsafe { Nearest::scale(i.as_ref(), 268, 178) }.buffer, + Image::<_, 3>::open("src/small_cat.png").buffer + ); +} diff --git a/src/small_cat.png b/src/small_cat.png Binary files differnew file mode 100644 index 0000000..3b30807 --- /dev/null +++ b/src/small_cat.png |