From a750364e270c52a5e4c594fa55e8d1e9dc6d58e3 Mon Sep 17 00:00:00 2001 From: asonix Date: Wed, 16 Dec 2020 18:20:06 -0600 Subject: [PATCH] Expose cropping --- Cargo.lock | 2 +- Cargo.toml | 2 +- README.md | 5 ++++ src/processor.rs | 77 ++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 84 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 20f8ef8..e51f718 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1449,7 +1449,7 @@ checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" [[package]] name = "pict-rs" -version = "0.3.0-alpha.1" +version = "0.3.0-alpha.2" dependencies = [ "actix-form-data", "actix-fs", diff --git a/Cargo.toml b/Cargo.toml index 2217d51..935d69b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "pict-rs" description = "A simple image hosting service" -version = "0.3.0-alpha.1" +version = "0.3.0-alpha.2" authors = ["asonix "] license = "AGPL-3.0" readme = "README.md" diff --git a/README.md b/README.md index ad14e6c..acb57cd 100644 --- a/README.md +++ b/README.md @@ -128,6 +128,11 @@ pict-rs offers the following endpoints: square using raw pixel sampling - `resize={int}`: produce a thumbnail of the image fitting inside an `{int}` by `{int}` square using a Lanczos2 filter. This is slower than sampling but looks a bit better in some cases + - `crop={int-w}x{int-h}`: produce a cropped version of the image with an `{int-w}` by `{int-h}` + aspect ratio. The resulting crop will be centered on the image. Either the width or height + of the image will remain full-size, depending on the image's aspect ratio and the requested + aspect ratio. For example, a 1600x900 image cropped with a 1x1 aspect ratio will become 900x900. A + 1600x1100 image cropped with a 16x9 aspect ratio will become 1600x900. Supported `ext` file extensions include `png`, `jpg`, and `webp` diff --git a/src/processor.rs b/src/processor.rs index 72fee67..d510a7c 100644 --- a/src/processor.rs +++ b/src/processor.rs @@ -170,6 +170,82 @@ impl Processor for Resize { } } +pub(crate) struct Crop(usize, usize); + +impl Processor for Crop { + fn name() -> &'static str + where + Self: Sized, + { + "crop" + } + + fn is_processor(s: &str) -> bool { + s == Self::name() + } + + fn parse(_: &str, v: &str) -> Option> { + let mut iter = v.split('x'); + let first = iter.next()?; + let second = iter.next()?; + + let width = first.parse::().ok()?; + let height = second.parse::().ok()?; + + if width == 0 || height == 0 { + return None; + } + + if width > 20 || height > 20 { + return None; + } + + Some(Box::new(Crop(width, height))) + } + + fn path(&self, mut path: PathBuf) -> PathBuf { + path.push(Self::name()); + path.push(format!("{}x{}", self.0, self.1)); + path + } + + fn process(&self, wand: &mut MagickWand) -> Result<(), UploadError> { + let width = wand.get_image_width(); + let height = wand.get_image_height(); + + // 16x9 becomes 16/9, which is bigger than 16/10. a bigger number means a wider image + // + // Crop ratios bigger than Image ratios mean cropping the image's height and leaving the + // width alone. + let img_ratio = width as f64 / height as f64; + let crop_ratio = self.0 as f64 / self.1 as f64; + + let final_width; + let final_height; + + let x_offset; + let y_offset; + + if crop_ratio > img_ratio { + final_height = (width as f64 / self.0 as f64 * self.1 as f64) as usize; + final_width = width; + + x_offset = 0; + y_offset = ((height - final_height) as f64 / 2.0) as isize; + } else { + final_height = height; + final_width = (height as f64 / self.1 as f64 * self.0 as f64) as usize; + + x_offset = ((width - final_width) as f64 / 2.0) as isize; + y_offset = 0; + } + + wand.op(|w| w.crop_image(final_width, final_height, x_offset, y_offset))?; + + Ok(()) + } +} + pub(crate) struct Blur(f64); impl Processor for Blur { @@ -236,6 +312,7 @@ pub(crate) fn build_chain(args: &[(String, String)]) -> ProcessChain { parse!(Identity, k, v); parse!(Thumbnail, k, v); parse!(Resize, k, v); + parse!(Crop, k, v); parse!(Blur, k, v); debug!("Skipping {}: {}, invalid", k, v);