-
Notifications
You must be signed in to change notification settings - Fork 984
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
42 changed files
with
658 additions
and
677 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
use errors::{anyhow, Result}; | ||
use std::hash::{Hash, Hasher}; | ||
|
||
const DEFAULT_Q_JPG: u8 = 75; | ||
|
||
/// Thumbnail image format | ||
#[derive(Debug, Clone, Copy, PartialEq, Eq)] | ||
pub enum Format { | ||
/// JPEG, The `u8` argument is JPEG quality (in percent). | ||
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(is_lossy: bool, format: &str, quality: Option<u8>) -> Result<Format> { | ||
use Format::*; | ||
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" => { | ||
if is_lossy { | ||
Ok(Jpeg(jpg_quality)) | ||
} else { | ||
Ok(Png) | ||
} | ||
} | ||
"jpeg" | "jpg" => Ok(Jpeg(jpg_quality)), | ||
"png" => Ok(Png), | ||
"webp" => Ok(WebP(quality)), | ||
_ => Err(anyhow!("Invalid image format: {}", format)), | ||
} | ||
} | ||
|
||
pub fn extension(&self) -> &str { | ||
// Kept in sync with RESIZED_FILENAME and op_filename | ||
use Format::*; | ||
|
||
match *self { | ||
Png => "png", | ||
Jpeg(_) => "jpg", | ||
WebP(_) => "webp", | ||
} | ||
} | ||
} | ||
|
||
#[allow(clippy::derive_hash_xor_eq)] | ||
impl Hash for Format { | ||
fn hash<H: Hasher>(&self, hasher: &mut H) { | ||
use Format::*; | ||
|
||
let q = match *self { | ||
Png => 0, | ||
Jpeg(q) => 1001 + q as u16, | ||
WebP(None) => 2000, | ||
WebP(Some(q)) => 2001 + q as u16, | ||
}; | ||
|
||
hasher.write_u16(q); | ||
hasher.write(self.extension().as_bytes()); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
use std::borrow::Cow; | ||
use std::collections::hash_map::DefaultHasher; | ||
use std::hash::{Hash, Hasher}; | ||
use std::path::Path; | ||
|
||
use crate::format::Format; | ||
use crate::ResizeOperation; | ||
use libs::image::DynamicImage; | ||
|
||
/// Apply image rotation based on EXIF data | ||
/// Returns `None` if no transformation is needed | ||
pub fn fix_orientation(img: &DynamicImage, path: &Path) -> Option<DynamicImage> { | ||
let file = std::fs::File::open(path).ok()?; | ||
let mut buf_reader = std::io::BufReader::new(&file); | ||
let exif_reader = exif::Reader::new(); | ||
let exif = exif_reader.read_from_container(&mut buf_reader).ok()?; | ||
let orientation = | ||
exif.get_field(exif::Tag::Orientation, exif::In::PRIMARY)?.value.get_uint(0)?; | ||
match orientation { | ||
// Values are taken from the page 30 of | ||
// https://www.cipa.jp/std/documents/e/DC-008-2012_E.pdf | ||
// For more details check http://sylvana.net/jpegcrop/exif_orientation.html | ||
1 => None, | ||
2 => Some(img.fliph()), | ||
3 => Some(img.rotate180()), | ||
4 => Some(img.flipv()), | ||
5 => Some(img.fliph().rotate270()), | ||
6 => Some(img.rotate90()), | ||
7 => Some(img.fliph().rotate90()), | ||
8 => Some(img.rotate270()), | ||
_ => None, | ||
} | ||
} | ||
|
||
/// We only use the input_path to get the file stem. | ||
/// Hashing the resolved `input_path` would include the absolute path to the image | ||
/// with all filesystem components. | ||
pub fn get_processed_filename( | ||
input_path: &Path, | ||
input_src: &str, | ||
op: &ResizeOperation, | ||
format: &Format, | ||
) -> String { | ||
let mut hasher = DefaultHasher::new(); | ||
hasher.write(input_src.as_ref()); | ||
op.hash(&mut hasher); | ||
format.hash(&mut hasher); | ||
let hash = hasher.finish(); | ||
let filename = input_path | ||
.file_stem() | ||
.map(|s| s.to_string_lossy()) | ||
.unwrap_or_else(|| Cow::Borrowed("unknown")); | ||
|
||
format!("{}.{:016x}.{}", filename, hash, format.extension()) | ||
} |
Oops, something went wrong.