WebP support in resize_image (#1360)
* Removing unused webpl * Adding clarification comment * Updating documentation * Adding webp
This commit is contained in:
parent
d4db249997
commit
8eac5a5994
36
Cargo.lock
generated
36
Cargo.lock
generated
|
@ -212,6 +212,9 @@ name = "cc"
|
|||
version = "1.0.66"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4c0496836a84f8d0495758516b8621a622beb77c0fed418570e50764093ced48"
|
||||
dependencies = [
|
||||
"jobserver",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cedarwood"
|
||||
|
@ -1017,9 +1020,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "image"
|
||||
version = "0.23.12"
|
||||
version = "0.23.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7ce04077ead78e39ae8610ad26216aed811996b043d47beed5090db674f9e9b5"
|
||||
checksum = "293f07a1875fa7e9c5897b51aa68b2d8ed8271b87e1a44cb64b9c3d98aabbc0d"
|
||||
dependencies = [
|
||||
"bytemuck",
|
||||
"byteorder",
|
||||
|
@ -1045,6 +1048,7 @@ dependencies = [
|
|||
"regex",
|
||||
"tera",
|
||||
"utils",
|
||||
"webp",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1112,6 +1116,15 @@ dependencies = [
|
|||
"regex",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "jobserver"
|
||||
version = "0.1.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5c71313ebb9439f74b00d9d2dcec36440beaf57a6aa0623068441dd7cd81a7f2"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "jpeg-decoder"
|
||||
version = "0.1.22"
|
||||
|
@ -1209,6 +1222,15 @@ dependencies = [
|
|||
"utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libwebp-sys"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3e70c064738b35a28fd6f991d27c0d9680353641d167ae3702a8228dd8272ef6"
|
||||
dependencies = [
|
||||
"cc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lindera"
|
||||
version = "0.3.5"
|
||||
|
@ -3114,6 +3136,16 @@ dependencies = [
|
|||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "webp"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ea1f2bd35e46165ef40a7fd74f33f64f2912ad92593fbfc5ec75eb2604cfd7b5"
|
||||
dependencies = [
|
||||
"image",
|
||||
"libwebp-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "webpki"
|
||||
version = "0.21.4"
|
||||
|
|
|
@ -10,6 +10,7 @@ regex = "1.0"
|
|||
tera = "1"
|
||||
image = "0.23"
|
||||
rayon = "1"
|
||||
webp="0.1.1"
|
||||
|
||||
errors = { path = "../errors" }
|
||||
utils = { path = "../utils" }
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
use std::collections::hash_map::DefaultHasher;
|
||||
use std::{collections::hash_map::DefaultHasher, io::Write};
|
||||
use std::collections::hash_map::Entry as HEntry;
|
||||
use std::collections::HashMap;
|
||||
use std::fs::{self, File};
|
||||
use std::hash::{Hash, Hasher};
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use image::imageops::FilterType;
|
||||
use image::{EncodableLayout, imageops::FilterType};
|
||||
use image::{GenericImageView, ImageOutputFormat};
|
||||
use lazy_static::lazy_static;
|
||||
use rayon::prelude::*;
|
||||
|
@ -18,7 +18,7 @@ static RESIZED_SUBDIR: &str = "processed_images";
|
|||
|
||||
lazy_static! {
|
||||
pub static ref RESIZED_FILENAME: Regex =
|
||||
Regex::new(r#"([0-9a-f]{16})([0-9a-f]{2})[.](jpg|png)"#).unwrap();
|
||||
Regex::new(r#"([0-9a-f]{16})([0-9a-f]{2})[.](jpg|png|webp)"#).unwrap();
|
||||
}
|
||||
|
||||
/// Describes the precise kind of a resize operation
|
||||
|
@ -132,6 +132,7 @@ impl Hash for ResizeOp {
|
|||
}
|
||||
}
|
||||
}
|
||||
const DEFAULT_Q_JPG: u8 = 75;
|
||||
|
||||
/// Thumbnail image format
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
|
@ -140,22 +141,26 @@ pub enum Format {
|
|||
Jpeg(u8),
|
||||
/// PNG
|
||||
Png,
|
||||
/// WebP, The `u8` argument is WebP quality (in percent), None meaning lossless.
|
||||
WebP(Option<u8>),
|
||||
}
|
||||
|
||||
impl Format {
|
||||
pub fn from_args(source: &str, format: &str, quality: u8) -> Result<Format> {
|
||||
pub fn from_args(source: &str, format: &str, quality: Option<u8>) -> Result<Format> {
|
||||
use Format::*;
|
||||
|
||||
assert!(quality > 0 && quality <= 100, "Jpeg quality must be within the range [1; 100]");
|
||||
|
||||
if let Some(quality) = quality {
|
||||
assert!(quality > 0 && quality <= 100, "Quality must be within the range [1; 100]");
|
||||
}
|
||||
let jpg_quality = quality.unwrap_or(DEFAULT_Q_JPG);
|
||||
match format {
|
||||
"auto" => match Self::is_lossy(source) {
|
||||
Some(true) => Ok(Jpeg(quality)),
|
||||
Some(true) => Ok(Jpeg(jpg_quality)),
|
||||
Some(false) => Ok(Png),
|
||||
None => Err(format!("Unsupported image file: {}", source).into()),
|
||||
},
|
||||
"jpeg" | "jpg" => Ok(Jpeg(quality)),
|
||||
"jpeg" | "jpg" => Ok(Jpeg(jpg_quality)),
|
||||
"png" => Ok(Png),
|
||||
"webp" => Ok(WebP(quality)),
|
||||
_ => Err(format!("Invalid image format: {}", format).into()),
|
||||
}
|
||||
}
|
||||
|
@ -170,6 +175,8 @@ impl Format {
|
|||
"png" => Some(false),
|
||||
"gif" => Some(false),
|
||||
"bmp" => Some(false),
|
||||
// It is assumed that webp is lossless, but it can be both
|
||||
"webp" => Some(false),
|
||||
_ => None,
|
||||
})
|
||||
.unwrap_or(None)
|
||||
|
@ -182,6 +189,7 @@ impl Format {
|
|||
match *self {
|
||||
Png => "png",
|
||||
Jpeg(_) => "jpg",
|
||||
WebP(_) => "webp"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -194,6 +202,8 @@ impl Hash for Format {
|
|||
let q = match *self {
|
||||
Png => 0,
|
||||
Jpeg(q) => q,
|
||||
WebP(None) => 0,
|
||||
WebP(Some(q)) => q
|
||||
};
|
||||
|
||||
hasher.write_u8(q);
|
||||
|
@ -232,7 +242,7 @@ impl ImageOp {
|
|||
width: Option<u32>,
|
||||
height: Option<u32>,
|
||||
format: &str,
|
||||
quality: u8,
|
||||
quality: Option<u8>,
|
||||
) -> Result<ImageOp> {
|
||||
let op = ResizeOp::from_args(op, width, height)?;
|
||||
let format = Format::from_args(&source, format, quality)?;
|
||||
|
@ -303,6 +313,19 @@ impl ImageOp {
|
|||
Format::Jpeg(q) => {
|
||||
img.write_to(&mut f, ImageOutputFormat::Jpeg(q))?;
|
||||
}
|
||||
Format::WebP(q) => {
|
||||
let encoder = webp::Encoder::from_image(&img);
|
||||
let memory = match q {
|
||||
Some(q) => {
|
||||
encoder.encode(q as f32 / 100.)
|
||||
}
|
||||
None => {
|
||||
encoder.encode_lossless()
|
||||
}
|
||||
};
|
||||
let mut bytes = memory.as_bytes();
|
||||
f.write_all(&mut bytes)?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
|
|
@ -221,7 +221,6 @@ impl ResizeImage {
|
|||
|
||||
static DEFAULT_OP: &str = "fill";
|
||||
static DEFAULT_FMT: &str = "auto";
|
||||
const DEFAULT_Q: u8 = 75;
|
||||
|
||||
impl TeraFn for ResizeImage {
|
||||
fn call(&self, args: &HashMap<String, Value>) -> Result<Value> {
|
||||
|
@ -248,11 +247,12 @@ impl TeraFn for ResizeImage {
|
|||
.unwrap_or_else(|| DEFAULT_FMT.to_string());
|
||||
|
||||
let quality =
|
||||
optional_arg!(u8, args.get("quality"), "`resize_image`: `quality` must be a number")
|
||||
.unwrap_or(DEFAULT_Q);
|
||||
optional_arg!(u8, args.get("quality"), "`resize_image`: `quality` must be a number");
|
||||
if let Some(quality) = quality {
|
||||
if quality == 0 || quality > 100 {
|
||||
return Err("`resize_image`: `quality` must be in range 1-100".to_string().into());
|
||||
}
|
||||
}
|
||||
|
||||
let mut imageproc = self.imageproc.lock().unwrap();
|
||||
if !imageproc.source_exists(&path) {
|
||||
|
|
|
@ -28,10 +28,11 @@ resize_image(path, width, height, op, format, quality)
|
|||
- `"auto"`
|
||||
- `"jpg"`
|
||||
- `"png"`
|
||||
- `"webp"`
|
||||
|
||||
The default is `"auto"`, this means that the format is chosen based on input image format.
|
||||
JPEG is chosen for JPEGs and other lossy formats, and PNG is chosen for PNGs and other lossless formats.
|
||||
- `quality` (_optional_): JPEG quality of the resized image, in percent. Only used when encoding JPEGs; default value is `75`.
|
||||
- `quality` (_optional_): JPEG or WebP quality of the resized image, in percent. Only used when encoding JPEGs or WebPs; for JPEG default value is `75`, for WebP default is lossless.
|
||||
|
||||
### Image processing and return value
|
||||
|
||||
|
|
Loading…
Reference in a new issue