use image::{DynamicImage, Rgba};
use imageproc::geometric_transformations::{
Border, Interpolation, Projection, rotate_about_center, warp,
};
use crate::state::{EditState, Keystone};
use super::{color, exposure, filters, masks, sharpness, spots};
pub fn apply(img: &DynamicImage, state: &EditState) -> DynamicImage {
let mut out = img.clone();
if !state.spots.is_empty() {
out = spots::apply(&out, &state.spots);
}
out = apply_geometry(out, state);
out = exposure::apply(out, state);
out = color::apply(out, state);
out = filters::apply(out, state);
out = masks::apply(out, state, img.width(), img.height());
out = sharpness::apply(out, state);
out
}
pub fn apply_geometry(img: DynamicImage, state: &EditState) -> DynamicImage {
let mut out = img;
if state.straighten.abs() > 0.01 {
let rgba = out.to_rgba8();
let rotated = rotate_about_center(
&rgba,
state.straighten.to_radians(),
Interpolation::Bilinear,
Border::Constant(Rgba([0u8, 0u8, 0u8, 255u8])),
);
out = DynamicImage::ImageRgba8(rotated);
}
if state.keystone.vertical.abs() > 0.001 || state.keystone.horizontal.abs() > 0.001 {
out = apply_keystone(out, &state.keystone);
}
out = match state.rotate.rem_euclid(360) {
90 => out.rotate90(),
180 => out.rotate180(),
270 => out.rotate270(),
_ => out,
};
if state.flip_h {
out = out.fliph();
}
if state.flip_v {
out = out.flipv();
}
if let Some(ref crop) = state.crop {
let w = out.width() as f32;
let h = out.height() as f32;
let cx = (crop.x * w) as u32;
let cy = (crop.y * h) as u32;
let cw = (crop.width * w).min(w - cx as f32) as u32;
let ch = (crop.height * h).min(h - cy as f32) as u32;
if cw > 0 && ch > 0 {
out = out.crop_imm(cx, cy, cw, ch);
}
}
out
}
fn apply_keystone(img: DynamicImage, keystone: &Keystone) -> DynamicImage {
let rgba = img.to_rgba8();
let w = rgba.width() as f32;
let h = rgba.height() as f32;
let v = keystone.vertical;
let hz = keystone.horizontal;
let src: [(f32, f32); 4] = [(0.0, 0.0), (w, 0.0), (w, h), (0.0, h)];
let dst: [(f32, f32); 4] = [
(v.max(0.0) * w, hz.max(0.0) * h),
(w - v.max(0.0) * w, (-hz).max(0.0) * h),
(w - (-v).max(0.0) * w, h - (-hz).max(0.0) * h),
((-v).max(0.0) * w, h - hz.max(0.0) * h),
];
let Some(projection) = Projection::from_control_points(src, dst) else {
return img;
};
let warped = warp(
&rgba,
projection,
Interpolation::Bilinear,
Border::Constant(Rgba([0, 0, 0, 255])),
);
DynamicImage::ImageRgba8(warped)
}