Add an example with raqote (#118)

* Add an example with raqote

* Fix raqote dependency
This commit is contained in:
Jay Oster 2020-09-18 03:59:05 -07:00 committed by GitHub
parent 11dca72955
commit 6c36b3955b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 248 additions and 0 deletions

View file

@ -29,6 +29,7 @@ Rapidly prototype a simple 2D game, pixel-based animations, software renderers,
- [Minimal example with SDL2](./examples/minimal-sdl2)
- [Minimal example with `winit`](./examples/minimal-winit)
- [Pixel Invaders](./examples/invaders)
- [`raqote` example](./examples/raqote-winit)
## Troubleshooting

View file

@ -0,0 +1,19 @@
[package]
name = "raqote-winit"
version = "0.1.0"
authors = ["Jay Oster <jay@kodewerx.org>"]
edition = "2018"
publish = false
[features]
optimize = ["log/release_max_level_warn"]
default = ["optimize"]
[dependencies]
env_logger = "0.7"
euclid = "0.20"
log = "0.4"
pixels = { path = "../.." }
raqote = { version = "0.8", default-features = false }
winit = "0.22"
winit_input_helper = "0.6"

View file

@ -0,0 +1,15 @@
# Hello Raqote
![Hello Raqote](../../img/raqote-winit.png)
`raqote` example with `winit`.
## Running
```bash
cargo run --release --package raqote-winit
```
## About
This example uses `raqote` to rasterize and animate a few paths.

View file

@ -0,0 +1,89 @@
#![deny(clippy::all)]
#![forbid(unsafe_code)]
use crate::shapes::Shapes;
use log::error;
use pixels::{Error, Pixels, SurfaceTexture};
use std::time::Instant;
use winit::dpi::LogicalSize;
use winit::event::{Event, VirtualKeyCode};
use winit::event_loop::{ControlFlow, EventLoop};
use winit::window::WindowBuilder;
use winit_input_helper::WinitInputHelper;
mod shapes;
const WIDTH: u32 = 400;
const HEIGHT: u32 = 400;
fn main() -> Result<(), Error> {
env_logger::init();
let event_loop = EventLoop::new();
let mut input = WinitInputHelper::new();
let window = {
let size = LogicalSize::new(WIDTH as f64, HEIGHT as f64);
WindowBuilder::new()
.with_title("Hello Raqote")
.with_inner_size(size)
.with_min_inner_size(size)
.build(&event_loop)
.unwrap()
};
let (mut pixels, mut shapes) = {
let window_size = window.inner_size();
let surface_texture = SurfaceTexture::new(window_size.width, window_size.height, &window);
let pixels = Pixels::new(WIDTH, HEIGHT, surface_texture)?;
let shapes = Shapes::new(window_size.width, window_size.height);
(pixels, shapes)
};
let mut now = Instant::now();
event_loop.run(move |event, _, control_flow| {
// Draw the current frame
if let Event::RedrawRequested(_) = event {
for (dst, &src) in pixels
.get_frame()
.chunks_exact_mut(4)
.zip(shapes.get_frame().iter())
{
dst[0] = (src >> 16) as u8;
dst[1] = (src >> 8) as u8;
dst[2] = src as u8;
dst[3] = (src >> 24) as u8;
}
if pixels
.render()
.map_err(|e| error!("pixels.render() failed: {}", e))
.is_err()
{
*control_flow = ControlFlow::Exit;
return;
}
}
// Handle input events
if input.update(event) {
// Close events
if input.key_pressed(VirtualKeyCode::Escape) || input.quit() {
*control_flow = ControlFlow::Exit;
return;
}
// Resize the window
if let Some(size) = input.window_resized() {
pixels.resize(size.width, size.height);
}
// Update internal state and request a redraw
shapes.draw(now.elapsed().as_secs_f32());
window.request_redraw();
now = Instant::now();
}
});
}

View file

@ -0,0 +1,124 @@
use euclid::{Angle, Vector2D};
use raqote::*;
/// A context that can draw vector shapes
pub struct Shapes {
// The main draw target
dt: DrawTarget,
// Center
cx: f32,
cy: f32,
// Background color
r: f32,
g: f32,
b: f32,
// Two paths rotate in opposite directions
t1: Transform,
t2: Transform,
}
impl Shapes {
/// Create a new Shapes
pub fn new(width: u32, height: u32) -> Self {
Self {
dt: DrawTarget::new(width as i32, height as i32),
cx: (width / 2) as f32,
cy: (width / 2) as f32,
r: 0.0,
g: 0.34,
b: 0.88,
t1: Transform::identity(),
t2: Transform::identity(),
}
}
/// Gain access to the underlying pixels
pub fn get_frame(&self) -> &[u32] {
self.dt.get_data()
}
/// Draw all of the shapes
pub fn draw(&mut self, delta: f32) {
// This is not a good way to blend colors.
// But it's fine for a demo.
self.r += delta;
self.g += delta * 0.71;
self.b += delta * 1.33;
self.dt.clear(SolidSource {
g: ((self.g.sin() + 1.0) * 127.5).round() as u8,
r: ((self.r.sin() + 1.0) * 127.5).round() as u8,
b: ((self.b.sin() + 1.0) * 127.5).round() as u8,
a: 0xff,
});
self.t1 = self.t1.post_translate(Vector2D::new(-self.cx, -self.cy));
self.t1 = self.t1.post_rotate(Angle { radians: delta });
self.t1 = self.t1.post_translate(Vector2D::new(self.cx, self.cy));
self.dt.set_transform(&self.t1);
let mut pb = PathBuilder::new();
pb.move_to(100., 10.);
pb.cubic_to(150., 40., 175., 0., 200., 10.);
pb.quad_to(120., 100., 80., 200.);
pb.quad_to(150., 180., 300., 300.);
pb.close();
let path = pb.finish();
let gradient = Source::new_radial_gradient(
Gradient {
stops: vec![
GradientStop {
position: 0.2,
color: Color::new(0xff, 0, 0xff, 0),
},
GradientStop {
position: 0.8,
color: Color::new(0xff, 0xff, 0xff, 0xff),
},
GradientStop {
position: 1.,
color: Color::new(0xff, 0xff, 0, 0xff),
},
],
},
Point::new(150., 150.),
128.,
Spread::Pad,
);
self.dt.fill(&path, &gradient, &DrawOptions::new());
self.t2 = self.t2.post_translate(Vector2D::new(-self.cx, -self.cy));
self.t2 = self.t2.post_rotate(Angle { radians: -delta });
self.t2 = self.t2.post_translate(Vector2D::new(self.cx, self.cy));
self.dt.set_transform(&self.t2);
let mut pb = PathBuilder::new();
pb.move_to(100., 100.);
pb.line_to(300., 300.);
pb.line_to(200., 300.);
let path = pb.finish();
self.dt.stroke(
&path,
&Source::Solid(SolidSource {
r: 0x0,
g: 0x0,
b: 0x80,
a: 0x80,
}),
&StrokeStyle {
cap: LineCap::Round,
join: LineJoin::Round,
width: 10.,
miter_limit: 2.,
dash_array: vec![10., 18.],
dash_offset: 16.,
},
&DrawOptions::new(),
);
}
}

BIN
img/raqote-winit.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB