2 releases

0.3.1 Mar 11, 2023
0.3.0 Aug 3, 2021

#153 in Rendering

Download history 5/week @ 2023-12-15 7/week @ 2023-12-22 2/week @ 2023-12-29 2/week @ 2024-01-05 3/week @ 2024-01-12 4/week @ 2024-02-09 20/week @ 2024-02-16 32/week @ 2024-02-23 25/week @ 2024-03-01 26/week @ 2024-03-08 21/week @ 2024-03-15 22/week @ 2024-03-22 49/week @ 2024-03-29

121 downloads per month
Used in flo_draw

Apache-2.0

405KB
6.5K SLoC

flo_draw

If you want to render some 2D graphics in Rust to screen right now without having to deal with the usual palaver involved with setting up a graphics context in a UI framework, flo_draw is the crate you need.

use flo_draw::*;
use flo_canvas::*;

///
/// Displays a filled circle in a window
///
pub fn main() {
    with_2d_graphics(|| {
        // Create a window
        let canvas      = create_drawing_window("Circle");

        // Draw a circle
        canvas.draw(|gc| {
            // Set up the canvas
            gc.canvas_height(1000.0);
            gc.center_region(0.0, 0.0, 1000.0, 1000.0);

            // Draw a circle
            gc.new_path();
            gc.circle(500.0, 500.0, 250.0);

            gc.fill_color(Color::Rgba(0.3, 0.6, 0.8, 1.0));
            gc.fill();

            gc.line_width(6.0);
            gc.stroke_color(Color::Rgba(0.0, 0.0, 0.0, 1.0));
            gc.stroke();
        });
    });
}

flo_draw also comes with a powerful set of 2D graphics libraries and has a flexible stream-based API that can make light work of many tasks that other libraries can make a meal of.

Motivation

Screenshot

While building FlowBetween, I found I needed a few when it came to rendering graphics: a platform-agnostic API and a way to render bitmap files that's not tied to any one platform. When debugging it, I found another thing I really wanted was a simple way to just throw up a window and start drawing graphics in it. This used to be quite simple in the 1980s (as demonstated in the screenshot) but the rise of the GUI and 3D accelleration has made rendering graphics increasingly difficult.

flo_draw takes the 2D graphics crates created for FlowBetween and adds an API to take all of the hassle out of the task of making them work.

About the libraries

This is a set of libraries that provide a 2D rendering framework for Rust. It provides on and off-screen rendering and an abstraction API. You might want to read the guide for some in-depth discussion of what can be achieved with the libraries in this repository.

  • flo_draw is a library that renders 2D graphics on-screen via glutin
  • flo_canvas provides a way to describe 2D drawing operations without being tied to any particular rendering implementation
  • flo_render is an abstraction API that converts low-level rendering instructions to a graphics API (OpenGL and Metal are supported)
  • flo_render_canvas converts the instructions described in flo_canvas to instructions for flo_render (using lyon for the tessellation)
  • flo_render_gl_offscreen helps flo_render by providing system-specific initialisation instructions for offscreen rendering

There are some other implementations of the flo_canvas protocol that are not yet packaged up conveniently: in particular, canvas.js allows rendering to an HTML canvas, and FlowBetween contains implementations for Quartz and Cairo.

Why use these crates?

Apart from the main 'party-piece' trick of popping up a window to render 2D graphics whenever it's needed, flo_draw and its companion crates have a number of other tricks up their sleeves:

  • Fully interactive: handle mouse and keyboard events to create graphical demonstrates
  • Flexible but straightforward text layout engine (which can render text to vectors if required)
  • Draw to bitmaps as well as to the screen
  • OpenGL accelerated 2D graphics
  • Stream-based API allows for easily composing effects on top of existing rendering instructions
  • Layers provide a way to partially update previously rendered graphics
  • Sprites provide a way to quickly re-render complex objects
  • Rendering from multiple threads (especially easy if each thread has its own layer)
  • Stream-based API allows easy redirection of the rendering instructions to the graphics API of your choice (both 2D APIs and GPU APIs)

Getting started

The flo_draw library is the best place to start, it provides a very easy way to render things on-screen:

use flo_draw::*;
use flo_canvas::*;

pub fn main() {
    with_2d_graphics(|| {
        let canvas = create_canvas_window("Hello, triangle");

        canvas.draw(|gc| {
            gc.clear_canvas(Color::Rgba(0.0, 0.4, 0.4, 1.0));
            gc.canvas_height(1000.0);
            gc.center_region(0.0, 0.0, 1000.0, 1000.0);

            gc.new_path();
            gc.move_to(200.0, 200.0);
            gc.line_to(800.0, 200.0);
            gc.line_to(500.0, 800.0);
            gc.line_to(200.0, 200.0);

            gc.fill_color(Color::Rgba(0.0, 0.0, 0.8, 1.0));
            gc.fill();
        });
    });
}

Examples

See the examples folder in the draw and render_canvas subdirectories for some more things that can be done with the library.

Screenshot

Wibble Mandelbrot Gradient Text layout

Feature flags

There are a couple of feature flags that can be used to choose rendering engines or enable or disable features:

  • render-opengl - the default renderer, using the OpenGL API. Right now this provides the highest performance on the widest range of possible hardware.
  • render-wgpu - uses WGPU instead of OpenGL. WGPU can use a wide range of backends but is currently quite low performing on several systems
  • profile - output some performance metrics to the console on each frame
  • wgpu-profiler - add in the WGPU profiler (somewhat unreliable)

Companion crates

flo_draw was developed alongside several other crates, which may be of interest when developing software that uses the canvas:

  • flo_curves provides a lot of functionality for manipulating bezier curves.
  • flo_stream provides pubsub and generator streams, which are useful for distributing events around an application. (See the vectoroids example for a way to use a generator stream as a game clock)
  • desync provides a simpler way to write asynchronous code than traditional threads
  • flo_binding provides a way to convert between state changes and message streams, used in flo_draw to update the window configuration
  • flo_scene is a toolkit for building larger pieces of software from components that communicate by exchanging messages or monitoring properties.

Version 0.4

This is version 0.4 of flo_draw.

Future versions will incorporate more rendering targets. FlowBetween has Quartz, Cairo and HTML canvas targets so those are very likely, and some sort of non-accelerated version of the offscreen renderer is also a likely addition. Version 0.4 will likely add some more pipes for drawing streams: for example, a stream to simplify the rendering instructions so they match up to more conventional 2D graphics libraries more closely.

There are a few known issues with 0.4: dashed lines don't work too well either in this version.

Flo drawing on a window


lib.rs:

Events

flo_draw is currently based on glutin, but uses its own event structure: this is to make it so that it's possible for future versions to replace glutin easily if that ever proves to be necessary, and to support easy porting of code using flo_draw to other windowing systems. This also isolates software implemented using flo_draw from changes to glutin.

Dependencies

~4.5MB
~63K SLoC