Rust library for hardware accelerated drawing of 2D shapes, images, and text, with an easy to use API.

Overview

Speedy2D

Crate Documentation CI

Hardware-accelerated drawing of shapes, images, and text, with an easy to use API.

Speedy2D aims to be:

  • The simplest Rust API for creating a window, rendering graphics/text, and handling input
  • Compatible with any device supporting OpenGL 2.0+ or OpenGL ES 2.0+
  • Very fast

Supports Windows, Mac, and Linux. Should also work on Android and iOS (for rendering only).

By default, Speedy2D contains support for setting up a window with an OpenGL context, and receiving input events. If you'd like to handle this yourself, and use Speedy2D only for rendering, you can disable the windowing feature.

Useful Links

Example code

The example projects can be run using cargo run --example=hello_world (just change hello_world to the name of the example source file).

Screenshot

Quick Start

Step 1: Add Speedy2D to your Cargo.toml dependencies:

[dependencies]
speedy2d = "1.0.5"

Step 2: Create a window:

use speedy2d::Window;

let window = Window::new_centered("Title", (640, 480)).unwrap();

Step 3: Create a struct implementing the WindowHandler trait. Override whichever callbacks you're interested in, for example on_draw(), on_mouse_move(), or on_key_down().

use speedy2d::color::Color;
use speedy2d::window::{WindowHandler, WindowHelper};
use speedy2d::Graphics2D;

struct MyWindowHandler {}

impl WindowHandler for MyWindowHandler
{
    fn on_draw(&mut self, helper: &mut WindowHelper, graphics: &mut Graphics2D)
    {
        graphics.clear_screen(Color::from_rgb(0.8, 0.9, 1.0));
        graphics.draw_circle((100.0, 100.0), 75.0, Color::BLUE);

        // Request that we draw another frame once this one has finished
        helper.request_redraw();
    }

   // If desired, on_mouse_move(), on_key_down(), etc...
}

Step 4: Finally, start the event loop by passing your new WindowHandler to the run_loop() function.

window.run_loop(MyWindowHandler{});

That's it!

For a more detailed getting started guide, including a full list of WindowHandler callbacks, and how to render text, go to docs.rs/speedy2d.

The full code of the above example is below for your convenience:

use speedy2d::color::Color;
use speedy2d::{Graphics2D, Window};
use speedy2d::window::{WindowHandler, WindowHelper};

fn main() {
    let window = Window::new_centered("Title", (640, 480)).unwrap();
    window.run_loop(MyWindowHandler{});
}

struct MyWindowHandler {}

impl WindowHandler for MyWindowHandler
{
    fn on_draw(&mut self, helper: &mut WindowHelper, graphics: &mut Graphics2D)
    {
        graphics.clear_screen(Color::from_rgb(0.8, 0.9, 1.0));
        graphics.draw_circle((100.0, 100.0), 75.0, Color::BLUE);
        helper.request_redraw();
    }
}

Alternative: Managing the GL context yourself

If you'd rather handle the window creation and OpenGL context management yourself, simply disable Speedy2D's windowing feature in your Cargo.toml file, and create a context as follows:

use speedy2d::GLRenderer;

let mut renderer = unsafe {
    GLRenderer::new_for_current_context((640, 480))
}.unwrap();

Then, draw a frame using GLRenderer::draw_frame():

renderer.draw_frame(|graphics| {
    graphics.clear_screen(Color::WHITE);
    graphics.draw_circle((100.0, 100.0), 75.0, Color::BLUE);
});

License

Speedy2D is licensed under the Apache license, version 2.0. See LICENSE for more details.

Contributing

Pull requests for Speedy2D are always welcome. Please ensure the following checks pass locally before submitting:

cargo test
cargo test --no-default-features --lib --examples --tests
cargo clippy
cargo +nightly fmt -- --check
cargo doc

Some tests require the ability to create a headless OpenGL context.

Comments
  • Added support for clipping area, fixes #17.

    Added support for clipping area, fixes #17.

    This PR introduces the possibility to set clipping area: https://github.com/QuantumBadger/Speedy2D/issues/17 It works almost perfectly, except for these points:

    1. I didn't find the way to flip coordinates vertically, so that bottom-left corner to become top-left. @QuantumBadger, can you help with this?
    2. The second - I encounter some artifacts in a form of clipped area with small unclipped rectangle inside of this area. I will prepare a video to show. But this artifact shows up only on the second paint of my GUI, after that all is good. Mystery.
    opened by Revertron 14
  • RGBA Image issues

    RGBA Image issues

    I'm running into to some unexpected behavior when trying to draw images with an alpha channel:

    OS: Windows 10

    speedy2d = { version = "1.0.7", git = "https://github.com/QuantumBadger/Speedy2D", default-features = true }
    image = "0.23.12"
    

    source image: image

    result: image

    Note that this png is opaque. However trying to create an image that definitely has an alpha channel, I get an app crash with (exit code: 0xc000041d)

    rust-logo

    fn has_alpha(img: DynamicImage) -> bool{
        img.color().has_alpha()
    }
    

    I created this helper function to determine if an image has an alpha. While the smiey image it returns false, with the rust logo it returns true. I then match that bool to speedy2d::image::ImageDataType.

    So in summary, when passed a PNG that does not have an alpha channel, the image is not being drawn correctly. When passed a png with an alpha channel, I'm getting an unhandled crash.

    I created a repo demonstrating these issues here

    opened by wyhinton 8
  • Missing lifetime specifier for the `WindowHandler` trait

    Missing lifetime specifier for the `WindowHandler` trait

    I nee to ensure that a property has the same lifetime as my WindowHandler implementation :

    impl<'a> WindowHandler<EditorEvent> for EditorWindowHandler<'a>
    

    But if I try to specify a lifetime I get this error :

    error[E0308]: method not compatible with trait
     note: expected fn pointer `fn(&mut EditorWindowHandler<'a>, &mut WindowHelper<EditorEvent>, _)`
              found fn pointer `fn(&'a mut EditorWindowHandler<'a>, &mut WindowHelper<EditorEvent>, _)`
    

    I think that the problem is that the WindowHandler trait is declared with anonymous lifetime specifier.

    opened by d0rianb 6
  • Font rendering problem: Lines below characters on dark backgrounds

    Font rendering problem: Lines below characters on dark backgrounds

    The problem

    When drawing text on dark backgrounds, there are visible Lines below the text in the same color as the text. This happens even in the hello world example when changing the background color to black. The examples use the Karla font, but the same thing happened with all other fonts as well.

    Examples:

    grafik grafik

    Steps to reproduce

    1. Get the hello world example
    2. Set the clear color to BLACK and the text color to WHITE
    3. Run the program

    Environment

    • OS: Windows
    • Speedy2d version: 1.1.0
    • Rust version: 1.52.1
    opened by dnlmlr 6
  • Run on MacOS 11.1 fails with SuitableContextNotFound error

    Run on MacOS 11.1 fails with SuitableContextNotFound error

    Just testing out a simple starter app using the sample code included in the README. Unwrapping the result of Window::new_centered panics with the error SuitableContextNotFound.

    I did also experiment with building a window without vsync, but the results were the same.

    opened by lukesutton 6
  • WindowHandler lifetime issue

    WindowHandler lifetime issue

    I can't figure out how to use references in struct belonging to the WindowHandler because of lifetime errors : error[E0759]: `self` has an anonymous lifetime `'_` but it needs to satisfy a `'static` lifetime requirement'. The WindowHandler lifetime is suppose to be static because it lives as long as the program lives but there is no way to specify it to the compiler. I try by changing the 'static lifetime on the Context into anonymous but it's still the same error. Is it any solution ? A simple replication of my issue is down below :

    use speedy2d::window::{KeyScancode, VirtualKeyCode, WindowHandler, WindowHelper};
    use speedy2d::Window;
    
    fn main() {
        let window = Window::new_centered("Speedy2D: test", (800, 600)).unwrap();
    
        window.run_loop(MyWindowHandler {
            context: Context { blocks: vec![Block {}], selected_block: &Block {} },
        })
    }
    
    struct Block;
    
    struct Context<'ctx> {
        blocks: Vec<Block>,
        selected_block: &'ctx Block,
    }
    
    impl<'ctx> Context<'ctx> {
        pub fn add_selected_block(&'ctx mut self) {
            self.selected_block = &self.blocks[0];
        }
    }
    
    struct MyWindowHandler {
        context: Context<'static>,
    }
    
    impl WindowHandler for MyWindowHandler {
        fn on_key_down(&mut self, _helper: &mut WindowHelper, _virtual_key_code: Option<VirtualKeyCode>, _scancode: KeyScancode) {
            self.context.add_selected_block();
        }
    }
    
    opened by d0rianb 4
  • feat: add +=, -=, *= and /= operations to Vector2

    feat: add +=, -=, *= and /= operations to Vector2

    I've added op_assign operations for Vector2:

    • += and -= are working for Vector2<T> += Vector2<T>
    • *= and /= are working for Vector2<T> *= T

    I'm not sure that other assigning math operations (reminder/bits) are needed, but for += I'm sure (#65)

    opened by amarao 4
  • Added polygon drawing support.

    Added polygon drawing support.

    I assume the reason this library does not already accept drawing polygons is because it requires triangulation. I had a shot a remedying that. I will admit my knowledge of the inner workings of the library are sketchy at best, and my implementation is not ideal, especially in performance, but I figure anything is better than nothing.

    opened by chilipepperhott 4
  • Implement transparent window creation

    Implement transparent window creation

    • Extended the WindowCreationOptions wrapper by adding a transparent option
    • The transparent option maps directly to the winit::WindowBuilder option of the same name
    • Updated the glutin dependency to from 0.26 to 0.28 in order to avoid a transparency related bug
    opened by dnlmlr 3
  • Multithreading and UserEventSender

    Multithreading and UserEventSender

    Hey,

    as I understand you can use the UserEventSender class to send events from other threads, but I do not understand how to actually do it. I tried the following:

        fn on_start(&mut self, helper: &mut WindowHelper<String>, info: WindowStartupInfo) {
            log::info!("Got on_start callback: {:?}", info);
            helper.set_cursor_visible(false);
            helper.set_resizable(false);
    
            thread::spawn(|| {
                loop {
                    // Request that we draw another frame once this one has finished
                    self.redraw_event_sender.send_event("redraw".to_string());
                    thread::sleep(Duration::from_millis(15));
                }
            });
        }
    

    But I get following error: *mut winapi::shared::windef::HWND__ cannot be shared between threads safely within UserEventSender<String>, the trait Sync is not implemented for *mut winapi::shared::windef::HWND__ required because of the requirements on the impl of Send for &UserEventSender<String> required because it appears within the type [closure@src\main.rs:80:23: 86:10]rustcE0277 main.rs(1, 1): required because it appears within the type winit::platform_impl::platform::event_loop::EventLoopProxy<String> main.rs(1, 1): required because it appears within the type winit::event_loop::EventLoopProxy<String> main.rs(1, 1): required because it appears within the type UserEventSender<String> mod.rs(627, 8): required by a bound in spawn Sender<String> cannot be shared between threads safely within UserEventSender<String>, the trait Sync is not implemented for Sender<String> required because of the requirements on the impl of Send for &UserEventSender<String> required because it appears within the type [closure@src\main.rs:80:23: 86:10]rustcE0277 main.rs(1, 1): required because it appears within the type winit::platform_impl::platform::event_loop::EventLoopProxy<String> main.rs(1, 1): required because it appears within the type winit::event_loop::EventLoopProxy<String> main.rs(1, 1): required because it appears within the type UserEventSender<String> mod.rs(627, 8): required by a bound in spawn

    I also tried to use the move keyword, but then I get an other error: self has an anonymous lifetime '_ but it needs to satisfy a 'static lifetime requirement ...is captured here...rustcE0759 main.rs(75, 17): this data with an anonymous lifetime '_... main.rs(80, 9): ...and is required to live as long as 'static here.

    But I can´t change self to static because then the method signature would be wrong. Therefore I wanted to ask how you send events from other threads and maybe where it is ideally to spawn the thread.

    opened by f-fuchs 3
  • Wayland Support

    Wayland Support

    On wayland (specifically sway in my case), speedy2D 1.1.0 immediately errorrs out when a new window is created.

    backtrace: traceback.txt main.rs (github doesn't support attaching .rs files so I had to edit the file extension): main.txt

    Wayland support would be nice to have.

    opened by RiedleroD 3
  • Docs fail without the `image` feature

    Docs fail without the `image` feature

    Hello,

    If I compile speedy2d with default-features = false and features = ["windowing"], I get this error when compiling the docs:

    Documenting speedy2d v1.9.0
    error[E0432]: unresolved import `image`
      --> /home/prokop/.cargo/registry/src/github.com-1ecc6299db9ec823/speedy2d-1.9.0/src/renderer2d.rs:22:5
       |
    22 |     image::GenericImageView,
       |     ^^^^^ help: a similar path exists: `crate::image`
       |
       = note: `use` statements changed in Rust 2018; read more at <https://doc.rust-lang.org/edition-guide/rust-2018/module-system/path-clarity.html>
    
    error: Compilation failed, aborting rustdoc
    
    For more information about this error, try `rustc --explain E0432`.
    error: could not document `speedy2d`
    
    Caused by:
      process didn't exit successfully: `rustdoc --edition=2018 --crate-type lib --crate-name speedy2d /home/prokop/.cargo/registry/src/github.com-1ecc6299db9ec823/speedy2d-1.9.0/src/lib.rs --cap-lints allow -o /home/prokop/code/lebka/target/doc --cfg 'feature="glutin"' --cfg 'feature="windowing"' --error-format=json --json=diagnostic-rendered-ansi,artifacts,future-incompat --diagnostic-width=174 -C metadata=0f4b06b1efd68122 -L dependency=/home/prokop/code/lebka/target/debug/deps --extern backtrace=/home/prokop/code/lebka/target/debug/deps/libbacktrace-f976e4d006c8cf86.rmeta --extern earcutr=/home/prokop/code/lebka/target/debug/deps/libearcutr-a8bb252a308801ba.rmeta --extern gl=/home/prokop/code/lebka/target/debug/deps/libgl-ce71ddbef9510c40.rmeta --extern glow=/home/prokop/code/lebka/target/debug/deps/libglow-a36acde715df8845.rmeta --extern glutin=/home/prokop/code/lebka/target/debug/deps/libglutin-707d45c57c3d3961.rmeta --extern log=/home/prokop/code/lebka/target/debug/deps/liblog-e08e6005f620851e.rmeta --extern num_traits=/home/prokop/code/lebka/target/debug/deps/libnum_traits-7108a4925dc5891c.rmeta --extern rusttype=/home/prokop/code/lebka/target/debug/deps/librusttype-47f71766fd78dc97.rmeta --extern smallvec=/home/prokop/code/lebka/target/debug/deps/libsmallvec-95884ad3df096f0f.rmeta --extern unicode_normalization=/home/prokop/code/lebka/target/debug/deps/libunicode_normalization-06320636213135cd.rmeta --crate-version 1.9.0` (exit status: 1)
    

    If I add the image feature the error goes away.

    opened by ProkopRandacek 0
  • Added mouse passthrough window option

    Added mouse passthrough window option

    Closes https://github.com/QuantumBadger/Speedy2D/issues/68

    Turns out bumping the version of glutin to 0.29.1 makes this function available: self.window_context.window().set_cursor_hittest(!passthrough).unwrap(); which makes mouse passthrough events possible. WindowCreationOptions now has .with_mouse_passthrough(bool)

    However with then newer version of Glutin, cursor grab is changed from a bool to an enum CursorGrabMode. I did a somewhat ugly hack for this so I might need some advice on the best way to fix it up.

    opened by mgalos999 1
  • Mouse passthrough

    Mouse passthrough

    I'd love to be able to use Speedy2D as an overlay. Transparency and always on top has been implemented.

    But is there a way to make all mouse clicks pass through the window as if it weren't there?

    This feature exists in winit https://github.com/rust-windowing/winit/pull/2232 as set_cursor_hittest

    opened by mgalos999 0
  • Use trunk instead of just for WebGL example

    Use trunk instead of just for WebGL example

    Currently, the WebGL example is compiled using the justfile. This works, but for a beginner it may be less reproducible due to how involved the justfile configuration is.

    Without any changes to the code or configuration, trunk can be used to enable hot reloading and automatic serving of the built files:

    cd examples/webgl
    cargo install trunk
    trunk serve --open
    

    trunk can also be used to build the example without serving: trunk build [--release]

    The only thing that needs changing are a few lines in the documentation, and perhaps abridging the justfile

    opened by campbellcole 0
  • Added drag and drop file support

    Added drag and drop file support

    I introduce the glutin::event::WindowEvent::{DroppedFile, HoveredFile, HoveredFileCancelled} into speedy2d. I added a new method: on_file_drag to the WindowHandler trait.

    opened by d0rianb 3
  • Mismatch in window size and image size

    Mismatch in window size and image size

    I've been using speedy2d for my emulator. It's been working great up until recently

    I recently updated my development machine to Ubuntu 22.04. After that speedy2d started giving a PrimaryMonitorNotFound error.

    I tried updating my version of speedy2d to the latest (1.7.0) and now it is working again, but now a different problem is happening. I've attached a picture showing the problem https://imgur.com/a/dvl80nD

    The image I'm drawing to the window is smaller than the window size even though they should be the same size. The amount that it is different I think is the same amount of the UI scaling I have set for my window manager. This use to work fine before I upgraded.

    Here is the code where I am making the window https://github.com/bobbobbio/come_boy/blob/master/library/src/rendering/speedy.rs

    In the example image I was running it with a scale of 1 to make it even more clear. I also confirmed that if I adjust my UI scaling to 100% the problem goes away

    Thanks for reading. Sorry if there is something I am missing.

    opened by bobbobbio 5
Owner
On the internet, nobody knows you're a badger.
null
Tests a wide variety of N64 features, from common to hardware quirks. Written in Rust. Executes quickly.

n64-systemtest Tests a wide variety of N64 features, from common to hardware quirks. Written in Rust. Executes quickly. n64-systemtest is a test rom t

null 37 Jan 7, 2023
A Rust-like Hardware Description Language transpiled to Verilog

Introduction This projects attempts to create a Rust-like hardware description language. Note that this has nothing to do with Rust itself, it just ha

Benjamin Stürz 4 Sep 1, 2022
ShakeFlow: Functional Hardware Description with Latency-Insensitive Interface Combinators

ShakeFlow: Functional Hardware Description with Latency-Insensitive Interface Combinators This repository contains the artifact for the following pape

KAIST Concurrency & Parallelism Laboratory 36 Feb 16, 2023
A simpler and 5x faster alternative to HashMap in Rust, which doesn't use hashing and doesn't use heap

At least 5x faster alternative of HashMap, for very small maps. It is also faster than FxHashMap, hashbrown, ArrayMap, and nohash-hasher. The smaller

Yegor Bugayenko 12 Apr 19, 2023
Small, clean, easy to use programming language

Thistle A modern, simplistic multi-paradigm language supporting object-oriented features Hello World! import IO object Main def main(): unit

null 7 Apr 13, 2022
derive(Code) simplifies error handling by providing an easy-to-use enumeration of error codes

enum-code Introduction enum-code is a derive macro for enum types. This library generates code that associates error codes with error types. It can be

Bay 5 Jun 14, 2023
List public items (public API) of Rust library crates. Enables diffing public API between releases.

cargo wrapper for this library You probably want the cargo wrapper to this library. See https://github.com/Enselic/cargo-public-items. public_items Li

Martin Nordholts 20 Dec 26, 2022
jq for images - experimental

iq - a little tool for image manipulation Inspired by `jq`, iq is an experimental tool for manipulating images through expressions which transform pix

Michael Giba 33 Nov 27, 2022
Uma lib para a API do Brasil API (para o Rust)

Uma lib para a API do BrasilAPI (para o Rust) Features CEP (Zip code) DDD Bank CNPJ IBGE Feriados Nacionais Tabela FIPE ISBN Registros de domínios br

Pedro Augusto 6 Dec 13, 2022
Higher-level toolkit for MSDF text rendering

MSDF Toolkit Higher-level toolkit for MSDF text rendering About MSDF - an abbreviation of Multi-channel Signed Distance Field. In short, an efficient

null 2 Aug 19, 2022
A crate for converting an ASCII text string or file to a single unicode character

A crate for converting an ASCII text string or file to a single unicode character. Also provides a macro to embed encoded source code into a Rust source file. Can also do the same to Python code while still letting the code run as before by wrapping it in a decoder.

Johanna Sörngård 17 Dec 31, 2022
TUI (Text User Interface) - Get Instant feedback for your sh commands

Bashtastic Visualizer TUI (Text User Interface) - Get Instant feedback for your sh commands. Explore and play with your queries ??. The idea of this p

Alfredo Suarez 7 Nov 26, 2023
Rust explained using easy English

Update 22 December 2020: mdBook can be found here. 28 November 2020: Now also available in simplified Chinese thanks to kumakichi! 1 February 2021: No

null 7.3k Jan 3, 2023
Extracting archives made easy for Rust 🦀

Decompress A library that supports decompression of archives in multiple formats, inspired by ergonomics from Node's decompress. Includes a default st

Rusty Ferris Club 6 Dec 20, 2022
VoceChat is a superlight rust written social server. Easy integration to your site/app.

Re-decentralized the Internet through personal cloud computing. VoceChat is the lightest chat server prioritizes private hosting! Easy integratation t

Privoce 134 Feb 22, 2023
`memory_pages` is a small library provinig a cross-platform API to request pages from kernel with certain premisions

memory_pages: High level API for low level memory management While using low-level memory management in a project can provide substantial benefits, it

null 14 Mar 30, 2023
An API for getting questions from http://either.io implemented fully in Rust, using reqwest and some regex magic. Provides asynchronous and blocking clients respectively.

eithers_rust An API for getting questions from http://either.io implemented fully in Rust, using reqwest and some regex magic. Provides asynchronous a

null 2 Oct 24, 2021
rust database for you to use and help me make!

Welcome To Rust Database! What is this? this is a database for you to git clone and use in your project! Why should i use it? It is fast and it takes

Carghai74 2 Dec 4, 2022
An attempt to start documenting the rust sdk for temporal and how to use it following some of the examples in typescript

This is an attempt to start documenting the rust sdk for temporal and how to use it following some of the examples in typescript.

Cosm 5 May 24, 2023