|
|
@ -9,7 +9,6 @@
|
|
|
|
//! use std::io::BufReader;
|
|
|
|
//! use std::io::BufReader;
|
|
|
|
//! use std::io::Cursor;
|
|
|
|
//! use std::io::Cursor;
|
|
|
|
//!
|
|
|
|
//!
|
|
|
|
//! fn main() {
|
|
|
|
|
|
|
|
//! let file = File::open("tests/assets/test.png").unwrap();
|
|
|
|
//! let file = File::open("tests/assets/test.png").unwrap();
|
|
|
|
//! let reader = BufReader::new(file);
|
|
|
|
//! let reader = BufReader::new(file);
|
|
|
|
//! let mut thumbnails = create_thumbnails(reader, mime::IMAGE_PNG, [ThumbnailSize::Small, ThumbnailSize::Medium]).unwrap();
|
|
|
|
//! let mut thumbnails = create_thumbnails(reader, mime::IMAGE_PNG, [ThumbnailSize::Small, ThumbnailSize::Medium]).unwrap();
|
|
|
@ -17,19 +16,17 @@
|
|
|
|
//! let thumbnail = thumbnails.pop().unwrap();
|
|
|
|
//! let thumbnail = thumbnails.pop().unwrap();
|
|
|
|
//! let mut buf = Cursor::new(Vec::new());
|
|
|
|
//! let mut buf = Cursor::new(Vec::new());
|
|
|
|
//! thumbnail.write_png(&mut buf).unwrap();
|
|
|
|
//! thumbnail.write_png(&mut buf).unwrap();
|
|
|
|
//! }
|
|
|
|
|
|
|
|
//! ```
|
|
|
|
//! ```
|
|
|
|
|
|
|
|
|
|
|
|
use crate::error::ThumbResult;
|
|
|
|
use crate::error::ThumbResult;
|
|
|
|
use image;
|
|
|
|
use image::{DynamicImage, GenericImageView, ImageFormat};
|
|
|
|
use image::imageops::FilterType;
|
|
|
|
|
|
|
|
use image::{DynamicImage, GenericImageView, ImageOutputFormat};
|
|
|
|
|
|
|
|
use mime::Mime;
|
|
|
|
use mime::Mime;
|
|
|
|
use rayon::prelude::*;
|
|
|
|
use rayon::prelude::*;
|
|
|
|
use std::io::{BufRead, Seek, Write};
|
|
|
|
use std::io::{BufRead, Seek, Write};
|
|
|
|
|
|
|
|
|
|
|
|
use crate::formats::get_base_image;
|
|
|
|
use crate::formats::get_base_image;
|
|
|
|
pub use size::ThumbnailSize;
|
|
|
|
pub use size::ThumbnailSize;
|
|
|
|
|
|
|
|
use std::convert::From;
|
|
|
|
|
|
|
|
|
|
|
|
pub mod error;
|
|
|
|
pub mod error;
|
|
|
|
mod formats;
|
|
|
|
mod formats;
|
|
|
@ -41,11 +38,38 @@ pub struct Thumbnail {
|
|
|
|
inner: DynamicImage,
|
|
|
|
inner: DynamicImage,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#[derive(Clone, Debug)]
|
|
|
|
|
|
|
|
pub enum FilterType {
|
|
|
|
|
|
|
|
Nearest,
|
|
|
|
|
|
|
|
Triangle,
|
|
|
|
|
|
|
|
CatmullRom,
|
|
|
|
|
|
|
|
Gaussian,
|
|
|
|
|
|
|
|
Lanczos3,
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
impl FilterType {
|
|
|
|
|
|
|
|
const fn translate_filter(&self) -> image::imageops::FilterType {
|
|
|
|
|
|
|
|
match self {
|
|
|
|
|
|
|
|
Self::Nearest => image::imageops::FilterType::Nearest,
|
|
|
|
|
|
|
|
Self::Triangle => image::imageops::FilterType::Triangle,
|
|
|
|
|
|
|
|
Self::CatmullRom => image::imageops::FilterType::CatmullRom,
|
|
|
|
|
|
|
|
Self::Gaussian => image::imageops::FilterType::Gaussian,
|
|
|
|
|
|
|
|
Self::Lanczos3 => image::imageops::FilterType::Lanczos3,
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
impl From<FilterType> for image::imageops::FilterType {
|
|
|
|
|
|
|
|
fn from(filter_type: FilterType) -> Self {
|
|
|
|
|
|
|
|
filter_type.translate_filter()
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
impl Thumbnail {
|
|
|
|
impl Thumbnail {
|
|
|
|
/// Writes the bytes of the image in a png format
|
|
|
|
/// Writes the bytes of the image in a png format
|
|
|
|
pub fn write_png<W: Write + Seek>(self, writer: &mut W) -> ThumbResult<()> {
|
|
|
|
pub fn write_png<W: Write + Seek>(self, writer: &mut W) -> ThumbResult<()> {
|
|
|
|
let image = DynamicImage::ImageRgba8(self.inner.into_rgba8());
|
|
|
|
let image = DynamicImage::ImageRgba8(self.inner.into_rgba8());
|
|
|
|
image.write_to(writer, ImageOutputFormat::Png)?;
|
|
|
|
image.write_to(writer, ImageFormat::Png)?;
|
|
|
|
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
}
|
|
|
@ -53,7 +77,8 @@ impl Thumbnail {
|
|
|
|
/// Writes the bytes of the image in a jpeg format
|
|
|
|
/// Writes the bytes of the image in a jpeg format
|
|
|
|
pub fn write_jpeg<W: Write + Seek>(self, writer: &mut W, quality: u8) -> ThumbResult<()> {
|
|
|
|
pub fn write_jpeg<W: Write + Seek>(self, writer: &mut W, quality: u8) -> ThumbResult<()> {
|
|
|
|
let image = DynamicImage::ImageRgb8(self.inner.into_rgb8());
|
|
|
|
let image = DynamicImage::ImageRgb8(self.inner.into_rgb8());
|
|
|
|
image.write_to(writer, ImageOutputFormat::Jpeg(quality))?;
|
|
|
|
let mut encoder = image::codecs::jpeg::JpegEncoder::new_with_quality(writer, quality);
|
|
|
|
|
|
|
|
encoder.encode_image(&image)?;
|
|
|
|
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
}
|
|
|
@ -64,6 +89,24 @@ impl Thumbnail {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// Creates thumbnails of the requested sizes for the given reader providing the content as bytes and
|
|
|
|
|
|
|
|
/// the mime describing the contents type
|
|
|
|
|
|
|
|
pub fn create_thumbnails_samplefilter<R: BufRead + Seek, I: IntoIterator<Item = ThumbnailSize>>(
|
|
|
|
|
|
|
|
reader: R,
|
|
|
|
|
|
|
|
mime: Mime,
|
|
|
|
|
|
|
|
sizes: I,
|
|
|
|
|
|
|
|
filter: FilterType,
|
|
|
|
|
|
|
|
) -> ThumbResult<Vec<Thumbnail>> {
|
|
|
|
|
|
|
|
let image = get_base_image(reader, mime)?;
|
|
|
|
|
|
|
|
let sizes: Vec<ThumbnailSize> = sizes.into_iter().collect();
|
|
|
|
|
|
|
|
let thumbnails = resize_images(image, &sizes, filter)
|
|
|
|
|
|
|
|
.into_iter()
|
|
|
|
|
|
|
|
.map(|image| Thumbnail { inner: image })
|
|
|
|
|
|
|
|
.collect();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Ok(thumbnails)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// Creates thumbnails of the requested sizes for the given reader providing the content as bytes and
|
|
|
|
/// Creates thumbnails of the requested sizes for the given reader providing the content as bytes and
|
|
|
|
/// the mime describing the contents type
|
|
|
|
/// the mime describing the contents type
|
|
|
|
pub fn create_thumbnails<R: BufRead + Seek, I: IntoIterator<Item = ThumbnailSize>>(
|
|
|
|
pub fn create_thumbnails<R: BufRead + Seek, I: IntoIterator<Item = ThumbnailSize>>(
|
|
|
@ -73,7 +116,7 @@ pub fn create_thumbnails<R: BufRead + Seek, I: IntoIterator<Item = ThumbnailSize
|
|
|
|
) -> ThumbResult<Vec<Thumbnail>> {
|
|
|
|
) -> ThumbResult<Vec<Thumbnail>> {
|
|
|
|
let image = get_base_image(reader, mime)?;
|
|
|
|
let image = get_base_image(reader, mime)?;
|
|
|
|
let sizes: Vec<ThumbnailSize> = sizes.into_iter().collect();
|
|
|
|
let sizes: Vec<ThumbnailSize> = sizes.into_iter().collect();
|
|
|
|
let thumbnails = resize_images(image, &sizes)
|
|
|
|
let thumbnails = resize_images(image, &sizes, FilterType::Lanczos3)
|
|
|
|
.into_iter()
|
|
|
|
.into_iter()
|
|
|
|
.map(|image| Thumbnail { inner: image })
|
|
|
|
.map(|image| Thumbnail { inner: image })
|
|
|
|
.collect();
|
|
|
|
.collect();
|
|
|
@ -81,12 +124,20 @@ pub fn create_thumbnails<R: BufRead + Seek, I: IntoIterator<Item = ThumbnailSize
|
|
|
|
Ok(thumbnails)
|
|
|
|
Ok(thumbnails)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
fn resize_images(image: DynamicImage, sizes: &[ThumbnailSize]) -> Vec<DynamicImage> {
|
|
|
|
fn resize_images(
|
|
|
|
|
|
|
|
image: DynamicImage,
|
|
|
|
|
|
|
|
sizes: &[ThumbnailSize],
|
|
|
|
|
|
|
|
filter_type: crate::FilterType,
|
|
|
|
|
|
|
|
) -> Vec<DynamicImage> {
|
|
|
|
sizes
|
|
|
|
sizes
|
|
|
|
.into_par_iter()
|
|
|
|
.into_par_iter()
|
|
|
|
.map(|size| {
|
|
|
|
.map(|size| {
|
|
|
|
let (width, height) = size.dimensions();
|
|
|
|
let (width, height) = size.dimensions();
|
|
|
|
image.resize(width, height, FilterType::Lanczos3)
|
|
|
|
image.resize(
|
|
|
|
|
|
|
|
width,
|
|
|
|
|
|
|
|
height,
|
|
|
|
|
|
|
|
image::imageops::FilterType::from(filter_type.clone()),
|
|
|
|
|
|
|
|
)
|
|
|
|
})
|
|
|
|
})
|
|
|
|
.collect()
|
|
|
|
.collect()
|
|
|
|
}
|
|
|
|
}
|
|
|
|