Skip to main content

astroimage/
converter.rs

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    rgba_output: bool,
16    thread_pool: Option<Arc<rayon::ThreadPool>>,
17}
18
19impl ImageConverter {
20    pub fn new() -> Self {
21        ImageConverter {
22            downscale: 1,
23            quality: 95,
24            apply_debayer: true,
25            preview_mode: false,
26            rgba_output: false,
27            thread_pool: None,
28        }
29    }
30
31    pub fn with_downscale(mut self, factor: usize) -> Self {
32        self.downscale = factor;
33        self
34    }
35
36    pub fn with_quality(mut self, quality: u8) -> Self {
37        self.quality = quality.clamp(1, 100);
38        self
39    }
40
41    pub fn without_debayer(mut self) -> Self {
42        self.apply_debayer = false;
43        self
44    }
45
46    pub fn with_preview_mode(mut self) -> Self {
47        self.preview_mode = true;
48        self
49    }
50
51    /// Output RGBA (4 bytes/pixel) instead of RGB, suitable for HTML Canvas `ImageData`.
52    pub fn with_rgba_output(mut self) -> Self {
53        self.rgba_output = true;
54        self
55    }
56
57    pub fn with_thread_pool(mut self, pool: Arc<rayon::ThreadPool>) -> Self {
58        self.thread_pool = Some(pool);
59        self
60    }
61
62    /// Process a FITS/XISF image and return raw pixel data without writing to disk.
63    ///
64    /// Returns a `ProcessedImage` containing interleaved RGB u8 bytes,
65    /// suitable for display in a GUI, web backend, or further processing.
66    pub fn process<P: AsRef<Path>>(&self, input_path: P) -> Result<ProcessedImage> {
67        let config = ProcessConfig {
68            downscale_factor: self.downscale,
69            jpeg_quality: self.quality,
70            apply_debayer: self.apply_debayer,
71            preview_mode: self.preview_mode,
72            auto_stretch: true,
73            rgba_output: self.rgba_output,
74        };
75
76        let path = input_path.as_ref();
77        match &self.thread_pool {
78            Some(pool) => pool.install(|| pipeline::process_image(path, &config)),
79            None => pipeline::process_image(path, &config),
80        }
81        .context("Image processing failed")
82    }
83
84    /// Process a FITS/XISF image and save the result as JPEG or PNG.
85    pub fn convert<P: AsRef<Path>, Q: AsRef<Path>>(
86        &self,
87        input_path: P,
88        output_path: Q,
89    ) -> Result<()> {
90        let image = self.process(&input_path)?;
91
92        output::save_image(&image, output_path.as_ref(), self.quality)
93            .context("Image save failed")?;
94
95        Ok(())
96    }
97}
98
99impl Default for ImageConverter {
100    fn default() -> Self {
101        Self::new()
102    }
103}