extern crate cairo; extern crate palette; extern crate rand; use rand::Rng; use cairo::Gradient; const WIDTH: i32 = 3840; const HEIGHT: i32 = 2160; fn cloud(ctx: &cairo::Context, rng: &mut rand::ThreadRng) { let x_scale = rng.gen::() * 2.0 + 1.0; let x = (rng.gen::() * WIDTH as f64) / x_scale; let y = rng.gen::() * HEIGHT as f64; let num_circles = 2 + (rng.gen::() % 2); let vec: Vec<(f64, f64)> = (0..num_circles).map(|_| { let width = 60.0 + rng.gen::() * 200.0; let dist = 30.0 + rng.gen::() * 30.0; (width, dist) }).collect(); let surf = cairo::Context::new(&ctx.get_target().create_similar(cairo::Content::Alpha, WIDTH, HEIGHT)); surf.save(); let mut max_y = 0.0; let mut offset = x; surf.scale(x_scale, 1.0); surf.set_source_rgba(1.0, 1.0, 1.0, 1.0); for (width, dist) in vec { surf.arc(offset, y, width, 0.0, 3.14159 * 2.0); surf.fill(); offset = offset + width + dist; max_y = if width > max_y { width } else { max_y }; } surf.set_source_rgba(0.0, 0.0, 0.0, 0.0); surf.set_operator(cairo::Operator::Source); surf.rectangle(x - 260.0, y, offset + 260.0, max_y); surf.fill(); surf.restore(); surf.set_source_rgba(1.0, 1.0, 1.0, 0.8); ctx.mask(&cairo::SurfacePattern::create(&surf.get_target())); } fn shinedies(ctx: &cairo::Context, rng: &mut rand::ThreadRng) { let r = rng.gen::(); for _ in 0..20 { let surf = cairo::Context::new(&ctx.get_target().create_similar(cairo::Content::Alpha, WIDTH, HEIGHT)); surf.save(); surf.scale(0.5, 0.5); surf.translate(rng.gen::() * WIDTH as f64 * 2.0, rng.gen::() * HEIGHT as f64 * 2.0); surf.rotate(r); surf.set_source_rgba(1.0, 1.0, 1.0, 1.0); let w = 300.0 + rng.gen::() * 1200.0; let r = rng.gen::() * 300.0; surf.arc(0.0, 0.0, r, 0.0, 3.14159 * 2.0); surf.fill(); surf.arc(w, 0.0, r, 0.0, 3.14159 * 2.0); surf.fill(); surf.rectangle(0.0, -r, w, r * 2.0); surf.fill(); surf.restore(); ctx.set_source_rgba(1.0, 1.0, 1.0, 0.3); ctx.mask(&cairo::SurfacePattern::create(&surf.get_target())); } } fn stars(ctx: &cairo::Context, rng: &mut rand::ThreadRng) { ctx.set_source_rgba(1.0, 1.0, 1.0, 0.8); for _ in 0..200 { let x = rng.gen::() * WIDTH as f64; let y = rng.gen::() * HEIGHT as f64; let s = 4.0 + rng.gen::() * 6.0 as f64; ctx.arc(x, y, s, 0.0, 3.14159 * 2.0); ctx.fill(); } } fn get_sky_colors(rng: &mut rand::ThreadRng) -> (palette::Rgb, palette::Rgb) { use palette::{RgbHue, Hsv, Hue}; let default_dark: Hsv = Hsv::from(palette::Rgb::new(0.188, 0.333, 0.51)); let default_light: Hsv = Hsv::from(palette::Rgb::new(0.482, 0.749, 0.839)); let shift = RgbHue::from_radians(rng.gen::() * 3.14159 * 2.0); (default_dark.shift_hue(shift).into(), default_light.shift_hue(shift).into()) } fn main() { let surf = cairo::ImageSurface::create( cairo::Format::Rgb24, WIDTH, HEIGHT ).unwrap(); let mut rng = rand::thread_rng(); let ctx = cairo::Context::new(&surf); let w = rng.gen::() % 3; ctx.set_source_rgb( if w == 0 { 0.4 } else { 0.2 }, if w == 1 { 0.4 } else { 0.2 }, if w == 2 { 0.4 } else { 0.2 }, ); ctx.paint(); let g = cairo::LinearGradient::new( 0.0, 0.0, 0.0, HEIGHT as f64 ); let (dark, light) = get_sky_colors(&mut rng); g.add_color_stop_rgb(0.0, dark.red as f64, dark.green as f64, dark.blue as f64); g.add_color_stop_rgb(1.0, light.red as f64, light.green as f64, light.blue as f64); ctx.set_source(&g); ctx.paint(); stars(&ctx, &mut rng); for _ in 0..10 { cloud(&ctx, &mut rng); } shinedies(&ctx, &mut rng); { let mut f = std::fs::File::create("image.png").unwrap(); surf.write_to_png(&mut f).unwrap(); } }