runmat-plot 0.0.17

GPU-accelerated and static plotting for RunMat with WGPU and Plotters
Documentation
//! Image export (PNG, JPEG, etc.)
//!
//! Static image export functionality.

use std::path::Path;
use wgpu::{Device, Queue, TextureFormat};

/// High-performance image exporter using GPU rendering
pub struct ImageExporter {
    /// GPU device for rendering
    #[allow(dead_code)]
    device: Device,
    /// Command queue
    #[allow(dead_code)]
    queue: Queue,
    /// Surface format
    #[allow(dead_code)]
    format: TextureFormat,
    /// Export settings
    settings: ImageExportSettings,
}

/// Image export configuration
#[derive(Debug, Clone)]
pub struct ImageExportSettings {
    /// Output width in pixels
    pub width: u32,
    /// Output height in pixels  
    pub height: u32,
    /// Samples for anti-aliasing (1, 4, 8, 16)
    pub samples: u32,
    /// Background color [R, G, B, A] (0.0-1.0)
    pub background_color: [f32; 4],
    /// Image quality (0.0-1.0) for lossy formats
    pub quality: f32,
    /// Include metadata in output
    pub include_metadata: bool,
}

/// Supported image formats
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum ImageFormat {
    Png,
    Jpeg,
    WebP,
    Bmp,
}

impl Default for ImageExportSettings {
    fn default() -> Self {
        Self {
            width: 800,
            height: 600,
            samples: 4,                             // 4x MSAA
            background_color: [1.0, 1.0, 1.0, 1.0], // White background
            quality: 0.95,
            include_metadata: true,
        }
    }
}

impl ImageExporter {
    /// Create a new image exporter with GPU acceleration
    pub async fn new() -> Result<Self, String> {
        Self::with_settings(ImageExportSettings::default()).await
    }

    /// Create exporter with custom settings
    pub async fn with_settings(settings: ImageExportSettings) -> Result<Self, String> {
        // Initialize GPU context for headless rendering
        let instance = wgpu::Instance::new(wgpu::InstanceDescriptor::default());

        let adapter = instance
            .request_adapter(&wgpu::RequestAdapterOptions {
                power_preference: wgpu::PowerPreference::HighPerformance,
                compatible_surface: None,
                force_fallback_adapter: false,
            })
            .await
            .ok_or("Failed to find suitable GPU adapter")?;

        let (device, queue) = adapter
            .request_device(&wgpu::DeviceDescriptor::default(), None)
            .await
            .map_err(|e| format!("Failed to create device: {e}"))?;

        Ok(Self {
            device,
            queue,
            format: TextureFormat::Rgba8UnormSrgb,
            settings,
        })
    }

    /// Export figure to PNG file (placeholder implementation)
    pub async fn export_png<P: AsRef<Path>>(
        &self,
        _figure: &mut crate::plots::Figure,
        path: P,
    ) -> Result<(), String> {
        // TODO: Implement actual rendering pipeline integration
        let placeholder_data =
            vec![255u8; (self.settings.width * self.settings.height * 4) as usize];
        self.save_png(&placeholder_data, path).await
    }

    /// Save raw RGBA data as PNG
    async fn save_png<P: AsRef<Path>>(&self, data: &[u8], path: P) -> Result<(), String> {
        use image::{ImageBuffer, Rgba};

        let image =
            ImageBuffer::<Rgba<u8>, _>::from_raw(self.settings.width, self.settings.height, data)
                .ok_or("Failed to create image buffer")?;

        image
            .save(path)
            .map_err(|e| format!("Failed to save PNG: {e}"))?;

        println!("DEBUG: PNG export completed successfully");
        Ok(())
    }

    /// Update export settings
    pub fn set_settings(&mut self, settings: ImageExportSettings) {
        self.settings = settings;
    }

    /// Get current export settings
    pub fn settings(&self) -> &ImageExportSettings {
        &self.settings
    }
}