Skip to main content

hello/
hello.rs

1//! Draws a few primitives through the SceneBuilder abstraction to validate the
2//! Vello backend end-to-end. Writes `examples/hello.png` in the project root.
3
4use hephaestus::backend::vello::VelloRenderer;
5use hephaestus::brush::Gradient;
6use hephaestus::color::{rgb, rgb8, rgba, Color};
7use hephaestus::stroke::{Cap, Join, Stroke};
8use hephaestus::{
9    Affine, BlendMode, Brush, Compose, FillRule, Mix, Path, PickId, Point, Rect, Renderer,
10    SceneBuilder,
11};
12use kurbo::Shape;
13
14fn main() {
15    let mut renderer = VelloRenderer::new().expect("vello renderer init");
16    let (w, h) = (512u32, 512u32);
17
18    {
19        let scene = renderer.scene();
20
21        // 1. Filled rectangle (solid brush)
22        let rect = Rect::new(40.0, 40.0, 240.0, 200.0).to_path(0.1);
23        let solid: Brush = rgb8(60, 120, 200).into();
24        scene.fill(
25            FillRule::NonZero,
26            Affine::IDENTITY,
27            &solid,
28            None,
29            &rect,
30            PickId::Skip,
31        );
32
33        // 2. Stroked open polyline with round caps/joins
34        let mut poly = Path::new();
35        poly.move_to(Point::new(60.0, 300.0));
36        poly.line_to(Point::new(140.0, 360.0));
37        poly.line_to(Point::new(200.0, 280.0));
38        poly.line_to(Point::new(260.0, 380.0));
39        let stroke = Stroke::new(8.0)
40            .with_caps(Cap::Round)
41            .with_join(Join::Round);
42        let line_brush: Brush = rgb8(220, 220, 220).into();
43        scene.stroke(
44            &stroke,
45            Affine::IDENTITY,
46            &line_brush,
47            None,
48            &poly,
49            PickId::Skip,
50        );
51
52        // 3. Gradient-filled circle
53        let circle = kurbo::Circle::new(Point::new(380.0, 150.0), 90.0).to_path(0.1);
54        let gradient = Gradient::new_linear(Point::new(290.0, 60.0), Point::new(470.0, 240.0))
55            .with_stops([rgb(0.95, 0.55, 0.15), rgb(0.15, 0.05, 0.45)].as_slice());
56        let grad_brush: Brush = gradient.into();
57        scene.fill(
58            FillRule::NonZero,
59            Affine::IDENTITY,
60            &grad_brush,
61            None,
62            &circle,
63            PickId::Skip,
64        );
65
66        // 4. Multiply layer with a translucent square overlapping the rect
67        let layer_clip = Rect::new(0.0, 0.0, w as f64, h as f64).to_path(0.1);
68        scene.push_layer(
69            BlendMode::new(Mix::Multiply, Compose::SrcOver),
70            1.0,
71            Affine::IDENTITY,
72            &layer_clip,
73        );
74        let overlap = Rect::new(160.0, 120.0, 360.0, 320.0).to_path(0.1);
75        let overlap_brush: Brush = rgba(1.0, 0.85, 0.2, 0.7).into();
76        scene.fill(
77            FillRule::NonZero,
78            Affine::IDENTITY,
79            &overlap_brush,
80            None,
81            &overlap,
82            PickId::Skip,
83        );
84        scene.pop_layer();
85    }
86
87    let mut pixels = vec![0u8; (w * h * 4) as usize];
88    let bg: Color = rgb8(20, 22, 28);
89    renderer
90        .render_to_buffer(w, h, bg, &mut pixels)
91        .expect("render");
92
93    let path = std::env::current_dir().unwrap().join("examples/hello.png");
94    hephaestus::image::write_png(&path, w, h, &pixels).expect("write png");
95    println!("wrote {}", path.display());
96}