From 6c438cb19a50ba4c669459c2a06d0a94cd8dc31d Mon Sep 17 00:00:00 2001
From: M1K3Y <anantnrg@gmail.com>
Date: Mon, 26 Feb 2024 20:56:53 +0530
Subject: [PATCH] Add simpler example (#461)

* Finish example

* Some formatting changes

* Add more shapes

* Implement fixes

* Bring back the useful comment

* Inline the run function

* Add Copyright notice
---
 Cargo.toml                  |   1 +
 examples/shapes/Cargo.toml  |  17 ++++
 examples/shapes/src/main.rs | 186 ++++++++++++++++++++++++++++++++++++
 3 files changed, 204 insertions(+)
 create mode 100644 examples/shapes/Cargo.toml
 create mode 100644 examples/shapes/src/main.rs

diff --git a/Cargo.toml b/Cargo.toml
index b01826f85..629c25be8 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -13,6 +13,7 @@ members = [
     "examples/with_bevy",
     "examples/run_wasm",
     "examples/scenes",
+    "examples/shapes",
 ]
 
 [workspace.package]
diff --git a/examples/shapes/Cargo.toml b/examples/shapes/Cargo.toml
new file mode 100644
index 000000000..048fa22e2
--- /dev/null
+++ b/examples/shapes/Cargo.toml
@@ -0,0 +1,17 @@
+[package]
+name = "shapes"
+edition.workspace = true
+version.workspace = true
+license.workspace = true
+repository.workspace = true
+publish = false
+
+# The dependencies here are independent from the workspace versions
+[dependencies]
+anyhow = "1.0.80"
+pollster = "0.3.0"
+wgpu = "0.19.1"
+winit = "0.29.10"
+vello = { path = "../../" }
+# When using, replace the above line with this:
+#vello = { git = "https://github.com/linebender/vello" }
diff --git a/examples/shapes/src/main.rs b/examples/shapes/src/main.rs
new file mode 100644
index 000000000..5517677a7
--- /dev/null
+++ b/examples/shapes/src/main.rs
@@ -0,0 +1,186 @@
+// Copyright 2024 the Vello Authors
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
+use anyhow::Result;
+use std::num::NonZeroUsize;
+use std::sync::Arc;
+use vello::kurbo::{Circle, Ellipse, Line, RoundedRect, Stroke};
+use vello::peniko::Color;
+use vello::util::RenderSurface;
+use vello::RendererOptions;
+use vello::{kurbo::Affine, util::RenderContext, AaConfig, Renderer, Scene};
+use winit::{dpi::LogicalSize, window::WindowBuilder};
+use winit::{event::*, event_loop::ControlFlow};
+use winit::{event_loop::EventLoop, window::Window};
+
+// Simple struct to hold the state of the renderer
+pub struct RenderState<'s> {
+    // The fields MUST be in this order, so that the surface is dropped before the window
+    surface: RenderSurface<'s>,
+    window: Arc<Window>,
+}
+
+// Helper function that creates a Winit window and returns it (wrapped in an Arc for thread safety)
+fn create_window(event_loop: &winit::event_loop::EventLoopWindowTarget<()>) -> Arc<Window> {
+    Arc::new(
+        WindowBuilder::new()
+            .with_inner_size(LogicalSize::new(1044, 800))
+            .with_resizable(true)
+            .with_title("Vello Shapes")
+            .build(event_loop)
+            .unwrap(),
+    )
+}
+
+fn main() -> Result<()> {
+    let event_loop = EventLoop::new()?;
+    let mut render_cx = RenderContext::new().unwrap();
+    let mut renderers: Vec<Option<Renderer>> = vec![];
+    let mut render_state = None::<RenderState>;
+    // Cache a window so that it can be reused when the app is resumed after being suspended
+    let mut cached_window = None;
+    let mut scene = Scene::new();
+
+    event_loop
+        .run(move |event, event_loop| match event {
+            Event::WindowEvent {
+                ref event,
+                window_id,
+            } => {
+                let Some(render_state) = &mut render_state else {
+                    return;
+                };
+                if render_state.window.id() != window_id {
+                    return;
+                }
+                match event {
+                    WindowEvent::CloseRequested => event_loop.exit(),
+                    WindowEvent::Resized(size) => {
+                        // Resize the surface when the window is resized
+                        render_cx.resize_surface(&mut render_state.surface, size.width, size.height)
+                    }
+                    WindowEvent::RedrawRequested => {
+                        // This is where all the rendering happens
+
+                        // Get the window size
+                        let width = render_state.surface.config.width;
+                        let height = render_state.surface.config.height;
+                        // Get a handle to the device
+                        let device_handle = &render_cx.devices[render_state.surface.dev_id];
+                        // Use the render_state to retrieve the surface to draw to
+                        let surface_texture = render_state
+                            .surface
+                            .surface
+                            .get_current_texture()
+                            .expect("failed to get surface texture");
+
+                        // Define the render parameters.
+                        let render_params = vello::RenderParams {
+                            // Background color
+                            base_color: Color::BLACK,
+                            // Width
+                            width,
+                            // Height
+                            height,
+                            // Antialiasing method to use. Other methods: AaConfig::Area, AaConfig::Msaa8
+                            antialiasing_method: AaConfig::Msaa16,
+                        };
+
+                        // Create some shapes!
+                        let stroke = Stroke::new(6.0);
+
+                        let rect = RoundedRect::new(10.0, 10.0, 240.0, 240.0, 20.0);
+                        let rect_stroke_color = Color::rgb(0.9804, 0.702, 0.5294);
+
+                        let circle = Circle::new((420.0, 200.0), 120.0);
+                        let circle_fill_color = Color::rgb(0.9529, 0.5451, 0.6588);
+
+                        let ellipse = Ellipse::new((250.0, 420.0), (100.0, 160.0), -90.0);
+                        let ellipse_fill_color = Color::rgb(0.7961, 0.651, 0.9686);
+
+                        let line = Line::new((260.0, 20.0), (620.0, 100.0));
+                        let line_stroke_color = Color::rgb(0.5373, 0.7059, 0.9804);
+
+                        scene.reset();
+
+                        // Draw the shapes!
+                        scene.stroke(&stroke, Affine::IDENTITY, rect_stroke_color, None, &rect);
+                        scene.fill(
+                            vello::peniko::Fill::NonZero,
+                            Affine::IDENTITY,
+                            circle_fill_color,
+                            None,
+                            &circle,
+                        );
+                        scene.fill(
+                            vello::peniko::Fill::NonZero,
+                            Affine::IDENTITY,
+                            ellipse_fill_color,
+                            None,
+                            &ellipse,
+                        );
+                        scene.stroke(&stroke, Affine::IDENTITY, line_stroke_color, None, &line);
+
+                        // Render to the surface
+                        vello::block_on_wgpu(
+                            &device_handle.device,
+                            renderers[render_state.surface.dev_id]
+                                .as_mut()
+                                .unwrap()
+                                .render_to_surface_async(
+                                    &device_handle.device,
+                                    &device_handle.queue,
+                                    &scene,
+                                    &surface_texture,
+                                    &render_params,
+                                ),
+                        )
+                        .expect("failed to render to surface");
+                        surface_texture.present();
+                        device_handle.device.poll(wgpu::Maintain::Poll);
+                    }
+                    _ => {}
+                }
+            }
+            Event::Suspended => {
+                if let Some(render_state) = render_state.take() {
+                    cached_window = Some(render_state.window);
+                }
+                event_loop.set_control_flow(ControlFlow::Wait);
+            }
+            Event::Resumed => {
+                let Option::None = render_state else { return };
+                // Get the window
+                let window = cached_window
+                    .take()
+                    .unwrap_or_else(|| create_window(event_loop));
+                let size = window.inner_size();
+                let surface_future =
+                    render_cx.create_surface(window.clone(), size.width, size.height);
+                // Create a surface
+                let surface = pollster::block_on(surface_future).expect("Error creating surface");
+                render_state = {
+                    let render_state = RenderState { window, surface };
+                    renderers.resize_with(render_cx.devices.len(), || None);
+                    let id = render_state.surface.dev_id;
+                    renderers[id].get_or_insert_with(|| {
+                        Renderer::new(
+                            &render_cx.devices[id].device,
+                            RendererOptions {
+                                surface_format: Some(render_state.surface.format),
+                                use_cpu: false,
+                                antialiasing_support: vello::AaSupport::all(),
+                                num_init_threads: NonZeroUsize::new(1),
+                            },
+                        )
+                        .expect("Could create renderer")
+                    });
+                    Some(render_state)
+                };
+                event_loop.set_control_flow(ControlFlow::Poll);
+            }
+            _ => {}
+        })
+        .expect("Couldn't run event loop");
+    Ok(())
+}