use image::{ImageBuffer, Rgba, RgbaImage};
use std::path::Path;
const BACKGROUND: [u8; 4] = [16, 22, 26, 255]; const PRIMARY: [u8; 4] = [255, 107, 53, 255]; const SECONDARY: [u8; 4] = [94, 234, 212, 255]; const WHITE: [u8; 4] = [255, 255, 255, 255];
pub fn generate_app_icons() -> anyhow::Result<()> {
std::fs::create_dir_all("icons")?;
let sizes = [32, 128, 256, 512, 1024];
for size in sizes {
let icon = create_inferno_icon(size);
let path = format!("icons/{}x{}.png", size, size);
icon.save(&path)?;
println!("Generated icon: {}", path);
if size <= 128 {
let retina_path = format!("icons/{}x{}@2x.png", size, size);
let retina_icon = create_inferno_icon(size * 2);
retina_icon.save(&retina_path)?;
println!("Generated retina icon: {}", retina_path);
}
}
let main_icon = create_inferno_icon(512);
main_icon.save("icons/icon.png")?;
println!("Generated main icon: icons/icon.png");
generate_ico_file()?;
generate_icns_file()?;
Ok(())
}
fn create_inferno_icon(size: u32) -> RgbaImage {
let mut img = ImageBuffer::new(size, size);
for pixel in img.pixels_mut() {
*pixel = Rgba(BACKGROUND);
}
let center = size as f32 / 2.0;
let radius = size as f32 * 0.35;
draw_flame_icon(&mut img, center, center, radius);
img
}
fn draw_flame_icon(img: &mut RgbaImage, cx: f32, cy: f32, radius: f32) {
let (width, height) = img.dimensions();
for y in 0..height {
for x in 0..width {
let dx = x as f32 - cx;
let dy = y as f32 - cy;
let distance = (dx * dx + dy * dy).sqrt();
if distance < radius {
let normalized_distance = distance / radius;
let angle = dy.atan2(dx);
let flame_factor = create_flame_pattern(normalized_distance, angle, cy - y as f32);
if flame_factor > 0.0 {
let color = interpolate_flame_color(flame_factor, normalized_distance);
img.put_pixel(x, y, Rgba(color));
}
}
}
}
}
fn create_flame_pattern(distance: f32, angle: f32, height_offset: f32) -> f32 {
let base_radius = 1.0 - distance;
let height_factor = if height_offset > 0.0 {
1.0 - (height_offset / 100.0).min(0.8)
} else {
1.0
};
let wave = (angle * 3.0).sin() * 0.1 * distance;
let flame_factor = base_radius * height_factor + wave;
if flame_factor > 0.3 {
flame_factor
} else {
0.0
}
}
fn interpolate_flame_color(flame_factor: f32, distance: f32) -> [u8; 4] {
let t = flame_factor.clamp(0.0, 1.0);
let d = distance.clamp(0.0, 1.0);
if t > 0.8 {
let blend = (t - 0.8) / 0.2;
interpolate_color(WHITE, PRIMARY, blend)
} else if t > 0.5 {
let blend = (t - 0.5) / 0.3;
interpolate_color(PRIMARY, SECONDARY, blend)
} else {
let blend = t / 0.5;
let mut color = SECONDARY;
color[3] = (255.0 * blend * (1.0 - d * 0.5)) as u8;
color
}
}
fn interpolate_color(color1: [u8; 4], color2: [u8; 4], t: f32) -> [u8; 4] {
let t = t.clamp(0.0, 1.0);
[
(color1[0] as f32 * (1.0 - t) + color2[0] as f32 * t) as u8,
(color1[1] as f32 * (1.0 - t) + color2[1] as f32 * t) as u8,
(color1[2] as f32 * (1.0 - t) + color2[2] as f32 * t) as u8,
(color1[3] as f32 * (1.0 - t) + color2[3] as f32 * t) as u8,
]
}
fn generate_ico_file() -> anyhow::Result<()> {
if Path::new("icons/256x256.png").exists() {
std::fs::copy("icons/256x256.png", "icons/icon.ico")?;
println!("Generated ICO file: icons/icon.ico");
}
Ok(())
}
fn generate_icns_file() -> anyhow::Result<()> {
if Path::new("icons/512x512.png").exists() {
std::fs::copy("icons/512x512.png", "icons/icon.icns")?;
println!("Generated ICNS file: icons/icon.icns");
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_icon_generation() {
let result = generate_app_icons();
assert!(result.is_ok());
}
#[test]
fn test_flame_pattern() {
let pattern = create_flame_pattern(0.5, 0.0, 10.0);
assert!(pattern >= 0.0);
}
}