Skip to main content

mq_edit/renderer/
image_manager.rs

1use image::DynamicImage;
2use std::cell::RefCell;
3use std::collections::HashMap;
4use std::path::{Path, PathBuf};
5
6/// Manages image loading and caching for the editor
7pub struct ImageManager {
8    /// Cache of loaded images (uses RefCell for interior mutability)
9    cache: RefCell<HashMap<PathBuf, DynamicImage>>,
10    /// Base path for resolving relative image paths
11    base_path: Option<PathBuf>,
12}
13
14impl ImageManager {
15    pub fn new() -> Self {
16        Self {
17            cache: RefCell::new(HashMap::new()),
18            base_path: None,
19        }
20    }
21
22    /// Set the base path for resolving relative image paths
23    pub fn set_base_path(&mut self, path: PathBuf) {
24        self.base_path = Some(path);
25    }
26
27    /// Load an image from the given path (uses interior mutability)
28    fn load_image(&self, path: &str) -> Result<DynamicImage, String> {
29        let resolved_path = self.resolve_path(path)?;
30
31        // Check if image is already cached
32        let mut cache = self.cache.borrow_mut();
33        if !cache.contains_key(&resolved_path) {
34            // Load the image
35            let img =
36                image::open(&resolved_path).map_err(|e| format!("Failed to load image: {}", e))?;
37            cache.insert(resolved_path.clone(), img);
38        }
39
40        cache
41            .get(&resolved_path)
42            .cloned()
43            .ok_or_else(|| "Image not found in cache".to_string())
44    }
45
46    /// Resolve a path (absolute or relative to base path)
47    fn resolve_path(&self, path: &str) -> Result<PathBuf, String> {
48        let path = Path::new(path);
49
50        // If absolute path, use as-is
51        if path.is_absolute() {
52            return Ok(path.to_path_buf());
53        }
54
55        // If relative, resolve against base path
56        if let Some(base) = &self.base_path {
57            let mut resolved = base.clone();
58            resolved.pop(); // Remove filename to get directory
59            resolved.push(path);
60            Ok(resolved)
61        } else {
62            // No base path, use current directory
63            std::env::current_dir()
64                .map(|cwd| cwd.join(path))
65                .map_err(|e| format!("Failed to get current directory: {}", e))
66        }
67    }
68
69    /// Check if an image exists and can be loaded
70    pub fn can_load_image(&self, path: &str) -> bool {
71        self.load_image(path).is_ok()
72    }
73
74    /// Clear the image cache
75    pub fn clear_cache(&self) {
76        self.cache.borrow_mut().clear();
77    }
78
79    /// Get image dimensions
80    pub fn get_dimensions(&self, path: &str) -> Result<(u32, u32), String> {
81        let img = self.load_image(path)?;
82        Ok((img.width(), img.height()))
83    }
84}
85
86impl Default for ImageManager {
87    fn default() -> Self {
88        Self::new()
89    }
90}