1pub mod adjustments;
8pub mod consts;
9#[cfg(feature = "exif")]
10pub mod exif;
11pub mod history;
12pub mod localization;
13pub mod state;
14pub mod transform;
15pub mod utils;
16use crate::history::History;
17use crate::state::TransformState;
18use anyhow::Result;
19use image::{DynamicImage, GenericImageView};
20use std::path::{Path, PathBuf};
21use std::sync::Arc;
22
23#[derive(Debug, Clone, Copy, PartialEq)]
28pub struct Adjustments {
29 pub brightness: f32,
31 pub contrast: f32,
33 pub exposure: f32,
35 pub fade: f32,
37 pub grain: f32,
39 pub hue: f32,
41 pub noise: f32,
43 pub saturation: f32,
45 pub vibrance: f32,
47 pub warmth: f32,
49}
50
51impl Default for Adjustments {
52 fn default() -> Self {
53 Self {
54 brightness: 0.0,
55 contrast: 0.0,
56 exposure: 0.0,
57 fade: 0.0,
58 grain: 0.0,
59 hue: 0.0,
60 noise: 0.0,
61 saturation: 0.0,
62 vibrance: 0.0,
63 warmth: 0.0,
64 }
65 }
66}
67
68#[derive(Debug, Clone)]
73pub struct HdimImage {
74 pub path: PathBuf,
76 pub data: Arc<DynamicImage>,
78 pub width: u32,
80 pub height: u32,
82 pub adjustments: Adjustments,
84 pub history: History,
86}
87
88impl HdimImage {
89 pub fn from_path(path: &Path) -> Result<Self> {
105 let data = image::open(path)?;
106 let (width, height) = data.dimensions();
107 let adjustments = Adjustments::default();
108 let data_arc = Arc::new(data);
109
110 Ok(HdimImage {
111 path: path.to_path_buf(),
112 data: data_arc.clone(),
113 width,
114 height,
115 adjustments,
116 history: History::new(data_arc, adjustments),
117 })
118 }
119
120 pub fn record_state(&mut self) {
122 self.history
123 .record_state(self.data.clone(), self.adjustments);
124 }
125
126 pub fn undo(&mut self) -> bool {
128 if let Some(state) = self.history.undo() {
129 self.data = state.data;
130 self.adjustments = state.adjustments;
131 let (width, height) = self.data.dimensions();
132 self.width = width;
133 self.height = height;
134 true
135 } else {
136 false
137 }
138 }
139
140 pub fn redo(&mut self) -> bool {
142 if let Some(state) = self.history.redo() {
143 self.data = state.data;
144 self.adjustments = state.adjustments;
145 let (width, height) = self.data.dimensions();
146 self.width = width;
147 self.height = height;
148 true
149 } else {
150 false
151 }
152 }
153
154 pub fn transform_image(&mut self, transform: &TransformState) {
156 let new_data = transform::apply_transform(&self.data, transform);
157 let (width, height) = new_data.dimensions();
158 self.data = Arc::new(new_data);
159 self.width = width;
160 self.height = height;
161 self.record_state();
163 }
164
165 pub fn apply_adjustments(&self) -> DynamicImage {
182 let mut adjusted_image = (*self.data).clone();
183 let adj = self.adjustments;
184
185 adjusted_image = self.apply_light_adjustments(adjusted_image, &adj);
187 adjusted_image = self.apply_color_adjustments(adjusted_image, &adj);
188 adjusted_image = self.apply_effect_adjustments(adjusted_image, &adj);
189
190 adjusted_image
191 }
192
193 fn apply_light_adjustments(&self, mut image: DynamicImage, adj: &Adjustments) -> DynamicImage {
195 if adj.exposure != 0.0 {
196 image = adjustments::exposure::apply_exposure(&image, adj.exposure);
197 }
198 if adj.brightness != 0.0 {
199 image = adjustments::brightness::apply_brightness(&image, adj.brightness);
200 }
201 if adj.contrast != 0.0 {
202 image = adjustments::contrast::apply_contrast(&image, adj.contrast);
203 }
204 image
205 }
206
207 fn apply_color_adjustments(&self, mut image: DynamicImage, adj: &Adjustments) -> DynamicImage {
209 if adj.warmth != 0.0 {
210 image = adjustments::warmth::apply_warmth(&image, adj.warmth);
211 }
212 if adj.vibrance != 0.0 {
213 image = adjustments::vibrance::apply_vibrance(&image, adj.vibrance);
214 }
215 if adj.saturation != 0.0 {
216 image = adjustments::saturation::apply_saturation(&image, adj.saturation);
217 }
218 if adj.hue != 0.0 {
219 image = adjustments::hue::apply_hue(&image, adj.hue);
220 }
221 image
222 }
223
224 fn apply_effect_adjustments(&self, mut image: DynamicImage, adj: &Adjustments) -> DynamicImage {
226 if adj.fade != 0.0 {
227 image = adjustments::fade::apply_fade(&image, adj.fade);
228 }
229 if adj.grain != 0.0 {
230 image = adjustments::grain::apply_grain(&image, adj.grain);
231 }
232 if adj.noise != 0.0 {
233 image = adjustments::noise::apply_noise(&image, adj.noise);
234 }
235 image
236 }
237
238 pub fn save_with_exif(
253 &self,
254 path: &Path,
255 format: image::ImageFormat,
256 strip: bool,
257 ) -> Result<()> {
258 let adjusted_image = self.apply_adjustments();
259
260 #[cfg(feature = "exif")]
261 {
262 use img_parts::ImageEXIF;
263 if let Some(exif_bytes) = exif::get_exif_bytes_for_save(&self.path, strip)? {
264 let mut buffer = std::io::Cursor::new(Vec::new());
265 adjusted_image.write_to(&mut buffer, format)?;
266 let mut bytes = buffer.into_inner();
267
268 if format == image::ImageFormat::Jpeg {
269 let mut jpeg = img_parts::jpeg::Jpeg::from_bytes(bytes.into())?;
270 jpeg.set_exif(Some(exif_bytes.into()));
271 let mut out = Vec::new();
272 jpeg.encoder().write_to(&mut out)?;
273 bytes = out;
274 } else if format == image::ImageFormat::Png {
275 let mut png = img_parts::png::Png::from_bytes(bytes.into())?;
276 png.set_exif(Some(exif_bytes.into()));
277 let mut out = Vec::new();
278 png.encoder().write_to(&mut out)?;
279 bytes = out;
280 }
281
282 std::fs::write(path, bytes)?;
283 return Ok(());
284 }
285 }
286
287 adjusted_image.save_with_format(path, format)?;
288 Ok(())
289 }
290}
291
292#[derive(Debug, Clone, Copy, PartialEq)]
294pub struct Size {
295 pub width: u32,
297 pub height: u32,
299}
300
301pub fn calculate_resize(image: &DynamicImage, max_size: Size) -> Size {
306 let (width, height) = image.dimensions();
307
308 let target_width = max_size.width;
311 let target_height = max_size.height * 2;
312
313 let width_ratio = target_width as f64 / width as f64;
314 let height_ratio = target_height as f64 / height as f64;
315 let ratio = width_ratio.min(height_ratio);
316
317 Size {
318 width: (width as f64 * ratio) as u32,
319 height: (height as f64 * ratio) as u32,
320 }
321}