Skip to content

Commit

Permalink
Refactor image proc
Browse files Browse the repository at this point in the history
Closes #2066
  • Loading branch information
Keats committed Jan 25, 2023
1 parent 67b4eb8 commit d435f00
Show file tree
Hide file tree
Showing 36 changed files with 598 additions and 666 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ This will error if 2 values are set
- Remove built-ins shortcodes
- Having a file called `index.md` in a folder with a `_index.md` is now an error
- Ignore temp files from vim/emacs/macos/etc as well as files without extensions when getting colocated assets
- Now integrates the file stem of the original file into the processed images filename: {stem}.{hash}.{extension}

### Other

Expand Down
66 changes: 66 additions & 0 deletions components/imageproc/src/format.rs
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());
}
}
55 changes: 55 additions & 0 deletions components/imageproc/src/helpers.rs
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())
}
Loading

0 comments on commit d435f00

Please sign in to comment.