wallswitch 0.60.8

randomly selects wallpapers for multiple monitors
Documentation
//! Cosmic Aurora wave generator overlay.
//!
//! This module implements a procedural overlay mimicking polar aurora wave filaments.
//! It processes pixels in parallel across logical CPU cores and blends the glowing waves
//! using gamma-corrected linear color space to prevent dark-boundary artifacts.

use crate::effects::partition_rows;
use crate::{ColorRGB, NEON_PALETTES, NeonColor, get_random_integer};
use image::RgbImage;
use std::thread;

impl crate::effects::ImageEffect for AuroraGenerator {
    /// Renders the wave-like aurora in-place over the active background memory buffer.
    fn apply(&self, rgb_img: &mut RgbImage) {
        let (width, height) = rgb_img.dimensions();
        let w_f = width as f32;
        let h_f = height as f32;

        let contrast_color = self.color_palette.to_array();
        let (mut rows, _) = partition_rows(rgb_img);

        let cores = thread::available_parallelism()
            .map(|n| n.get())
            .unwrap_or(4);
        let chunk_size = (rows.len() / cores).max(1);

        let inv_w = 1.0 / w_f;
        let inv_h = 1.0 / h_f;

        let adjusted_density = self.density * self.aspect_ratio_density_multiplier;

        let params = AuroraParams {
            density_u: adjusted_density * 1.5 * inv_w,
            density_w_coeff: adjusted_density * inv_w,
            density_w4_coeff: adjusted_density * 1.2,
            inv_w,
        };

        let density_v_coeff = self.density * 2.0;
        let density_val = self.density;

        thread::scope(|scope| {
            for chunk in rows.chunks_mut(chunk_size) {
                let params_ref = &params;
                scope.spawn(move || {
                    for (y, row_data) in chunk.iter_mut() {
                        let y_f = *y as f32;
                        let v = y_f * inv_h;

                        let row_state = AuroraRowState {
                            v,
                            w2: (v * density_v_coeff).cos(),
                            v_density: v * density_val,
                            v_sq: v * v,
                        };

                        // Use safe chunks_exact_mut to eliminate explicit index tracking (idx = x * 3)
                        for (x, pixel_slice) in row_data.chunks_exact_mut(3).enumerate() {
                            let x_f = x as f32;
                            let alpha = Self::calculate_aurora_alpha(x_f, params_ref, &row_state);

                            if alpha > 0.01 {
                                // Load background pixel mapping [0..255] u8 range to [0.0..1.0] linear scale
                                let mut pixel_color = ColorRGB::from_slice(pixel_slice);

                                let r_aurora = contrast_color[0];
                                let g_aurora = contrast_color[1];
                                let b_aurora = contrast_color[2];

                                // High-performance component-wise blending and gamma correction using ColorRGB operations
                                let bg_linear = pixel_color.squared();
                                let aurora_linear =
                                    ColorRGB::new(r_aurora, g_aurora, b_aurora).squared();
                                let blended_linear = aurora_linear.lerp(&bg_linear, alpha);

                                pixel_color = blended_linear.sqrt();

                                // Persist processed color channels back to the image buffer
                                pixel_color.write_to_slice(pixel_slice);
                            }
                        }
                    }
                });
            }
        });
    }

    /// Returns a formatting diagnostic string about the active generator.
    fn info(&self) -> String {
        format!(
            "overlay (density = {}), color: {}",
            self.density, self.color_palette
        )
    }
}

/// Parameters for wave-frequency math operations in the Cosmic Aurora effect.
pub struct AuroraParams {
    /// Density coefficient for horizontal frequency mapping.
    pub density_u: f32,
    /// Density coefficient for wave scaling.
    pub density_w_coeff: f32,
    /// Exponent coefficient for radial math calculations.
    pub density_w4_coeff: f32,
    /// Inverted width of the image.
    pub inv_w: f32,
}

/// Dynamic calculations computed per image row to optimize thread execution.
pub struct AuroraRowState {
    /// Vertical viewport parameter.
    pub v: f32,
    /// Cached cosine calculation for density.
    pub w2: f32,
    /// Cached vertical density scalar.
    pub v_density: f32,
    /// Cached squared vertical offset coordinate.
    pub v_sq: f32,
}

/// A procedural generator for rendering wave-like Cosmic Aurora overlays.
pub struct AuroraGenerator {
    /// The base color palette selected for the neon glow.
    pub color_palette: NeonColor,
    /// Base density frequency scaling factor.
    pub density: f32,
    /// Density multiplier tailored to the active aspect ratio.
    pub aspect_ratio_density_multiplier: f32,
}

impl AuroraGenerator {
    /// Generates a randomized Aurora configuration using the target monitor's dimensions.
    pub fn random(monitor: &crate::Monitor) -> Self {
        let width = monitor.resolution.width as u32;
        let height = monitor.resolution.height as u32;

        let p_idx: usize = get_random_integer(0, NEON_PALETTES.len() - 1);
        let color_palette = NEON_PALETTES[p_idx];

        // Base density scalar
        let density: f32 = get_random_integer(4, 8);

        // Adjust horizontal density to prevent stretching on wider/narrower displays
        let aspect_ratio = width as f32 / height as f32;
        let aspect_ratio_density_multiplier = if aspect_ratio > 1.0 {
            aspect_ratio.sqrt()
        } else {
            1.0
        };

        Self {
            color_palette,
            density,
            aspect_ratio_density_multiplier,
        }
    }

    #[inline(always)]
    fn calculate_aurora_alpha(x_f: f32, params: &AuroraParams, row: &AuroraRowState) -> f32 {
        let u = x_f * params.inv_w;

        let w1 = (x_f * params.density_u).sin();
        let w3 = (x_f * params.density_w_coeff + row.v_density).sin();
        let w4 = ((u * u + row.v_sq).sqrt() * params.density_w4_coeff).cos();

        let val = (w1 + row.w2 + w3 + w4) * 0.25;

        // Smooth, anti-aliasing sine wave mapping instead of narrow, noisy power spikes
        let wave = (val * std::f32::consts::PI).sin() * 0.5 + 0.5;
        let intensity = wave.powi(2); // Perfect organic ambient curtain light falloff

        let dx = (u - 0.5) * 2.0;
        let dy = (row.v - 0.5) * 2.0;
        let edge_fade = (1.0 - (dx * dx + dy * dy).sqrt() * 0.45).clamp(0.0, 1.0);

        intensity * edge_fade * 0.65
    }
}

#[cfg(test)]
mod tests_aurora {
    use super::*;
    use crate::core::Monitor;

    #[test]
    fn test_aurora_generator_random() {
        let monitor = Monitor::default();
        let aurora = AuroraGenerator::random(&monitor);
        assert!(aurora.density >= 4.0 && aurora.density <= 8.0);
    }
}