Rendering

Now that we have a canvas, we can start drawing things! To keep things organized, let's create a render function that will do all the rendering:

fn main() {
    let (context, gl_display, window, surface) = ...;
    // [...]
    let mut canvas = ..;

    render(&context, &surface, &window, &mut canvas);
}

fn render<T: Renderer>(
    context: &PossiblyCurrentContext,
    surface: &Surface<WindowSurface>,
    window: &Window,
    canvas: &mut Canvas<T>,
) {}

In render, first let's make sure that the canvas has the right size – it should match the dimensions and DPI of the window:

let size = window.inner_size();
canvas.set_size(size.width, size.height, window.scale_factor() as f32);

Next, let's do some actual drawing. As an example, we'll fill a smol red rectangle:

canvas.clear_rect(30, 30, 30, 30, Color::rgbf(1., 0., 0.));

clear_rect fills a rectangle. The first 2 parameters specify its position, and the next 2 specify the dimensions of the rectangle.

Color::rgbf is one of the functions that lets you create a Color. The three parameters correspond to the Red, Green and Blue values in the range 0..1.

Even if you consider your minimalist abstract masterpiece complete, there's actually some more code we need to write. We have to call canvas.flush() to tell the renderer to execute all drawing commands. Then, we must call swap_buffers to display what we've rendered:

canvas.flush();
surface.swap_buffers(context).expect("Could not swap buffers");

The render function is finished, but if you run your program, you won't get to look at it for very long – as soon as render completes, the program exits. To fix this, let's freeze the program with an infinite loop {}.

Our program now looks like this:

use std::num::NonZeroU32;

use femtovg::renderer::OpenGl;
use femtovg::{Canvas, Color, Renderer};
use glutin::surface::Surface;
use glutin::{context::PossiblyCurrentContext, display::Display};
use glutin_winit::DisplayBuilder;
use raw_window_handle::HasRawWindowHandle;
use winit::event_loop::EventLoop;
use winit::window::WindowBuilder;
use winit::{dpi::PhysicalSize, window::Window};

use glutin::{
    config::ConfigTemplateBuilder,
    context::ContextAttributesBuilder,
    display::GetGlDisplay,
    prelude::*,
    surface::{SurfaceAttributesBuilder, WindowSurface},
};

fn main() {
    let event_loop = EventLoop::new();
    let (context, gl_display, window, surface) = create_window(&event_loop);

    let renderer = unsafe { OpenGl::new_from_function_cstr(|s| gl_display.get_proc_address(s) as *const _) }
        .expect("Cannot create renderer");

    let mut canvas = Canvas::new(renderer).expect("Cannot create canvas");
    canvas.set_size(1000, 600, window.scale_factor() as f32);

    render(&context, &surface, &window, &mut canvas);

    loop {}
}

fn create_window(event_loop: &EventLoop<()>) -> (PossiblyCurrentContext, Display, Window, Surface<WindowSurface>) {
    let window_builder = WindowBuilder::new()
        .with_inner_size(PhysicalSize::new(1000., 600.))
        .with_title("Femtovg");

    let template = ConfigTemplateBuilder::new().with_alpha_size(8);

    let display_builder = DisplayBuilder::new().with_window_builder(Some(window_builder));

    let (window, gl_config) = display_builder
        .build(event_loop, template, |mut configs| configs.next().unwrap())
        .unwrap();

    let window = window.unwrap();

    let gl_display = gl_config.display();

    let context_attributes = ContextAttributesBuilder::new().build(Some(window.raw_window_handle()));

    let mut not_current_gl_context =
        Some(unsafe { gl_display.create_context(&gl_config, &context_attributes).unwrap() });

    let attrs = SurfaceAttributesBuilder::<WindowSurface>::new().build(
        window.raw_window_handle(),
        NonZeroU32::new(1000).unwrap(),
        NonZeroU32::new(600).unwrap(),
    );

    let surface = unsafe { gl_config.display().create_window_surface(&gl_config, &attrs).unwrap() };

    (
        not_current_gl_context.take().unwrap().make_current(&surface).unwrap(),
        gl_display,
        window,
        surface,
    )
}

fn render<T: Renderer>(
    context: &PossiblyCurrentContext,
    surface: &Surface<WindowSurface>,
    window: &Window,
    canvas: &mut Canvas<T>,
) {
    // Make sure the canvas has the right size:
    let size = window.inner_size();
    canvas.set_size(size.width, size.height, window.scale_factor() as f32);

    // Make smol red rectangle
    canvas.clear_rect(30, 30, 30, 30, Color::rgbf(1., 0., 0.));

    // Tell renderer to execute all drawing commands
    canvas.flush();

    // Display what we've just rendered
    surface.swap_buffers(context).expect("Could not swap buffers");
}

And when we run it, we see the red square we rendered:

Window titled Femtovg containing a small red square on a black background