1use std::path::Path;
2use std::sync::Arc;
3
4use anyhow::{Context, Result};
5
6use crate::output;
7use crate::pipeline;
8use crate::types::{ProcessConfig, ProcessedImage};
9
10pub struct ImageConverter {
11 downscale: usize,
12 quality: u8,
13 apply_debayer: bool,
14 preview_mode: bool,
15 thread_pool: Option<Arc<rayon::ThreadPool>>,
16}
17
18impl ImageConverter {
19 pub fn new() -> Self {
20 ImageConverter {
21 downscale: 1,
22 quality: 95,
23 apply_debayer: true,
24 preview_mode: false,
25 thread_pool: None,
26 }
27 }
28
29 pub fn with_downscale(mut self, factor: usize) -> Self {
30 self.downscale = factor;
31 self
32 }
33
34 pub fn with_quality(mut self, quality: u8) -> Self {
35 self.quality = quality.clamp(1, 100);
36 self
37 }
38
39 pub fn without_debayer(mut self) -> Self {
40 self.apply_debayer = false;
41 self
42 }
43
44 pub fn with_preview_mode(mut self) -> Self {
45 self.preview_mode = true;
46 self
47 }
48
49 pub fn with_thread_pool(mut self, pool: Arc<rayon::ThreadPool>) -> Self {
50 self.thread_pool = Some(pool);
51 self
52 }
53
54 pub fn process<P: AsRef<Path>>(&self, input_path: P) -> Result<ProcessedImage> {
59 let config = ProcessConfig {
60 downscale_factor: self.downscale,
61 jpeg_quality: self.quality,
62 apply_debayer: self.apply_debayer,
63 preview_mode: self.preview_mode,
64 auto_stretch: true,
65 };
66
67 let path = input_path.as_ref();
68 match &self.thread_pool {
69 Some(pool) => pool.install(|| pipeline::process_image(path, &config)),
70 None => pipeline::process_image(path, &config),
71 }
72 .context("Image processing failed")
73 }
74
75 pub fn convert<P: AsRef<Path>, Q: AsRef<Path>>(
77 &self,
78 input_path: P,
79 output_path: Q,
80 ) -> Result<()> {
81 let image = self.process(&input_path)?;
82
83 output::save_image(&image, output_path.as_ref(), self.quality)
84 .context("Image save failed")?;
85
86 Ok(())
87 }
88}
89
90impl Default for ImageConverter {
91 fn default() -> Self {
92 Self::new()
93 }
94}