diff --git a/Cargo.toml b/Cargo.toml index 83c23ee..33ae3e8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,6 +34,6 @@ version = "0.7.0" tempdir = "0.3.7" criterion = "0.3" -#[[bench]] -#name = "image" -#harness = false +[[bench]] +name = "image" +harness = false diff --git a/benches/image.rs b/benches/image.rs new file mode 100644 index 0000000..6f1da19 --- /dev/null +++ b/benches/image.rs @@ -0,0 +1,26 @@ +use criterion::{black_box, criterion_group, criterion_main, Criterion}; + +use image::imageops::FilterType; + +use photosync::library::load_image; +use photosync::library::resize; + +pub fn criterion_benchmark(c: &mut Criterion) { + const TEST_IMAGE_PATH: &'static str = "testdata/image.jpg"; + let img = load_image(TEST_IMAGE_PATH).expect("failed to load test image"); + c.bench_function("Load image", |b| { + b.iter(|| black_box(load_image(TEST_IMAGE_PATH))) + }); + c.bench_function("Resize image full Nearest", |b| { + b.iter(|| black_box(resize(&img, (None, None), FilterType::Nearest))) + }); + c.bench_function("Resize image full CatmullRom", |b| { + b.iter(|| black_box(resize(&img, (None, None), FilterType::CatmullRom))) + }); + c.bench_function("Resize image full Lanczos3", |b| { + b.iter(|| black_box(resize(&img, (None, None), FilterType::Lanczos3))) + }); +} + +criterion_group!(benches, criterion_benchmark); +criterion_main!(benches); diff --git a/src/library.rs b/src/library.rs index 47deebe..3fa8a67 100644 --- a/src/library.rs +++ b/src/library.rs @@ -7,8 +7,10 @@ use std::sync::Arc; use google_photoslibrary1 as photos; use image::imageops::FilterType; +use image::DynamicImage; use image::GenericImageView; use image::ImageFormat; +use image::ImageResult; use log::error; use log::info; use log::warn; @@ -28,6 +30,37 @@ pub struct Library { cache_db: Arc, } +pub fn load_image

(path: P) -> ImageResult +where + P: AsRef, +{ + image::io::Reader::open(&path)? + .with_guessed_format()? + .decode() +} + +pub fn resize( + img: &DynamicImage, + dimensions: (Option, Option), + filter: FilterType, +) -> DynamicImage { + let (w, h) = dimensions; + let (orig_w, orig_h) = img.dimensions(); + let (w, h) = match (w, h) { + (Some(w), Some(h)) => (w, h), + (Some(w), None) => (w, orig_h * w / orig_w), + (None, Some(h)) => (orig_w * h / orig_h, h), + (None, None) => (orig_w, orig_h), + }; + img.resize_to_fill(w, h, filter) +} + +pub fn save_to_jpeg_bytes(img: DynamicImage) -> ImageResult> { + let mut buf = Vec::new(); + img.write_to(&mut buf, ImageFormat::Jpeg)?; + Ok(buf) +} + impl Library { pub fn new(root: PathBuf) -> Result> { let db = DB::open_default(root.join("cache"))?; @@ -155,7 +188,7 @@ impl Library { pub fn generate_thumbnail( &self, media_items_id: &str, - (w, h): (Option, Option), + dimensions: (Option, Option), filter: FilterType, ) -> Result, io::Error> { match self.original(&media_items_id) { @@ -167,21 +200,11 @@ impl Library { )) } Some(path) => { - let orig_img = image::io::Reader::open(&path)? - .with_guessed_format()? - .decode() - .map_err(|e| io::Error::new(io::ErrorKind::Other, e))?; - let (orig_w, orig_h) = orig_img.dimensions(); - let (w, h) = match (w, h) { - (Some(w), Some(h)) => (w, h), - (Some(w), None) => (w, orig_h * w / orig_w), - (None, Some(h)) => (orig_w * h / orig_h, h), - (None, None) => (orig_w, orig_h), - }; - let img = orig_img.resize_to_fill(w, h, filter); - let mut buf = Vec::new(); - img.write_to(&mut buf, ImageFormat::Jpeg) - .map_err(|e| io::Error::new(io::ErrorKind::Other, e))?; + let orig_img = + load_image(&path).map_err(|e| io::Error::new(io::ErrorKind::Other, e))?; + let img = resize(&orig_img, dimensions, filter); + let buf = + save_to_jpeg_bytes(img).map_err(|e| io::Error::new(io::ErrorKind::Other, e))?; Ok(buf) } } diff --git a/testdata/image.jpg b/testdata/image.jpg new file mode 100644 index 0000000..f6c4168 Binary files /dev/null and b/testdata/image.jpg differ