fast image operations
-rw-r--r--Cargo.toml3
-rw-r--r--src/cat.pngbin0 -> 2533832 bytes
-rw-r--r--src/drawing/box.rs2
-rw-r--r--src/lib.rs24
-rw-r--r--src/scale.rs41
-rw-r--r--src/small_cat.pngbin0 -> 91214 bytes
6 files changed, 68 insertions, 2 deletions
diff --git a/Cargo.toml b/Cargo.toml
index fc94d39..d1554b0 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -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
new file mode 100644
index 0000000..361804d
--- /dev/null
+++ b/src/cat.png
Binary files differ
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> {
diff --git a/src/lib.rs b/src/lib.rs
index e72ea42..9de0643 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -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
new file mode 100644
index 0000000..3b30807
--- /dev/null
+++ b/src/small_cat.png
Binary files differ