pub mod adjustments;
pub mod consts;
#[cfg(feature = "exif")]
pub mod exif;
pub mod history;
pub mod localization;
pub mod state;
pub mod transform;
pub mod utils;
use crate::history::History;
use crate::state::TransformState;
use anyhow::Result;
use image::{DynamicImage, GenericImageView};
use std::path::{Path, PathBuf};
use std::sync::Arc;
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Adjustments {
pub brightness: f32,
pub contrast: f32,
pub exposure: f32,
pub fade: f32,
pub grain: f32,
pub hue: f32,
pub noise: f32,
pub saturation: f32,
pub vibrance: f32,
pub warmth: f32,
}
impl Default for Adjustments {
fn default() -> Self {
Self {
brightness: 0.0,
contrast: 0.0,
exposure: 0.0,
fade: 0.0,
grain: 0.0,
hue: 0.0,
noise: 0.0,
saturation: 0.0,
vibrance: 0.0,
warmth: 0.0,
}
}
}
#[derive(Debug, Clone)]
pub struct HdimImage {
pub path: PathBuf,
pub data: Arc<DynamicImage>,
pub width: u32,
pub height: u32,
pub adjustments: Adjustments,
pub history: History,
}
impl HdimImage {
pub fn from_path(path: &Path) -> Result<Self> {
let data = image::open(path)?;
let (width, height) = data.dimensions();
let adjustments = Adjustments::default();
let data_arc = Arc::new(data);
Ok(HdimImage {
path: path.to_path_buf(),
data: data_arc.clone(),
width,
height,
adjustments,
history: History::new(data_arc, adjustments),
})
}
pub fn record_state(&mut self) {
self.history
.record_state(self.data.clone(), self.adjustments);
}
pub fn undo(&mut self) -> bool {
if let Some(state) = self.history.undo() {
self.data = state.data;
self.adjustments = state.adjustments;
let (width, height) = self.data.dimensions();
self.width = width;
self.height = height;
true
} else {
false
}
}
pub fn redo(&mut self) -> bool {
if let Some(state) = self.history.redo() {
self.data = state.data;
self.adjustments = state.adjustments;
let (width, height) = self.data.dimensions();
self.width = width;
self.height = height;
true
} else {
false
}
}
pub fn transform_image(&mut self, transform: &TransformState) {
let new_data = transform::apply_transform(&self.data, transform);
let (width, height) = new_data.dimensions();
self.data = Arc::new(new_data);
self.width = width;
self.height = height;
self.record_state();
}
pub fn apply_adjustments(&self) -> DynamicImage {
let mut adjusted_image = (*self.data).clone();
let adj = self.adjustments;
adjusted_image = self.apply_light_adjustments(adjusted_image, &adj);
adjusted_image = self.apply_color_adjustments(adjusted_image, &adj);
adjusted_image = self.apply_effect_adjustments(adjusted_image, &adj);
adjusted_image
}
fn apply_light_adjustments(&self, mut image: DynamicImage, adj: &Adjustments) -> DynamicImage {
if adj.exposure != 0.0 {
image = adjustments::exposure::apply_exposure(&image, adj.exposure);
}
if adj.brightness != 0.0 {
image = adjustments::brightness::apply_brightness(&image, adj.brightness);
}
if adj.contrast != 0.0 {
image = adjustments::contrast::apply_contrast(&image, adj.contrast);
}
image
}
fn apply_color_adjustments(&self, mut image: DynamicImage, adj: &Adjustments) -> DynamicImage {
if adj.warmth != 0.0 {
image = adjustments::warmth::apply_warmth(&image, adj.warmth);
}
if adj.vibrance != 0.0 {
image = adjustments::vibrance::apply_vibrance(&image, adj.vibrance);
}
if adj.saturation != 0.0 {
image = adjustments::saturation::apply_saturation(&image, adj.saturation);
}
if adj.hue != 0.0 {
image = adjustments::hue::apply_hue(&image, adj.hue);
}
image
}
fn apply_effect_adjustments(&self, mut image: DynamicImage, adj: &Adjustments) -> DynamicImage {
if adj.fade != 0.0 {
image = adjustments::fade::apply_fade(&image, adj.fade);
}
if adj.grain != 0.0 {
image = adjustments::grain::apply_grain(&image, adj.grain);
}
if adj.noise != 0.0 {
image = adjustments::noise::apply_noise(&image, adj.noise);
}
image
}
pub fn save_with_exif(
&self,
path: &Path,
format: image::ImageFormat,
strip: bool,
) -> Result<()> {
let adjusted_image = self.apply_adjustments();
#[cfg(feature = "exif")]
{
use img_parts::ImageEXIF;
if let Some(exif_bytes) = exif::get_exif_bytes_for_save(&self.path, strip)? {
let mut buffer = std::io::Cursor::new(Vec::new());
adjusted_image.write_to(&mut buffer, format)?;
let mut bytes = buffer.into_inner();
if format == image::ImageFormat::Jpeg {
let mut jpeg = img_parts::jpeg::Jpeg::from_bytes(bytes.into())?;
jpeg.set_exif(Some(exif_bytes.into()));
let mut out = Vec::new();
jpeg.encoder().write_to(&mut out)?;
bytes = out;
} else if format == image::ImageFormat::Png {
let mut png = img_parts::png::Png::from_bytes(bytes.into())?;
png.set_exif(Some(exif_bytes.into()));
let mut out = Vec::new();
png.encoder().write_to(&mut out)?;
bytes = out;
}
std::fs::write(path, bytes)?;
return Ok(());
}
}
adjusted_image.save_with_format(path, format)?;
Ok(())
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Size {
pub width: u32,
pub height: u32,
}
pub fn calculate_resize(image: &DynamicImage, max_size: Size) -> Size {
let (width, height) = image.dimensions();
let target_width = max_size.width;
let target_height = max_size.height * 2;
let width_ratio = target_width as f64 / width as f64;
let height_ratio = target_height as f64 / height as f64;
let ratio = width_ratio.min(height_ratio);
Size {
width: (width as f64 * ratio) as u32,
height: (height as f64 * ratio) as u32,
}
}