Skip to content

Commit

Permalink
docs(example): simplify window example a lot by using the 'pixel-game…
Browse files Browse the repository at this point in the history
…-lib' library
  • Loading branch information
tversteeg committed Mar 10, 2024
1 parent dd7acd8 commit a432198
Show file tree
Hide file tree
Showing 2 changed files with 75 additions and 161 deletions.
21 changes: 4 additions & 17 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,27 +18,14 @@ blit = ["dep:blit"]
[dependencies]
multiversion = "0.7.3"
thiserror = "1.0.57"

# `blit` feature
blit = { version = "0.8.5", optional = true, default-features = false }
pixel-game-lib = { version = "0.8.0", default-features = false }

[dev-dependencies]
winit = "0.28.7"
log = "0.4.21"
web-time = "1.1.0"
blit = "0.8.5"
image = { version = "0.24.9", default-features = false, features = ["png"] }
pixels = "0.13.0"
bytemuck = "1.14.3"

[target.'cfg(not(target_arch = "wasm32"))'.dev-dependencies]
criterion = "0.5.1"
pollster = "0.3.0"

[target.'cfg(target_arch = "wasm32")'.dev-dependencies]
web-sys = { version = "0.3.69", features = ["CanvasRenderingContext2d", "Document", "Element", "HtmlCanvasElement", "ImageData", "Window"] }
wasm-bindgen = "0.2.92"
wasm-bindgen-futures = "0.4.42"
console_log = { version = "1.0.0", features = ["wasm-bindgen", "color"] }
console_error_panic_hook = "0.1.7"
pixel-game-lib = "0.8.0"

[[example]]
name = "window"
Expand Down
215 changes: 71 additions & 144 deletions examples/window.rs
Original file line number Diff line number Diff line change
@@ -1,160 +1,87 @@
use blit::{prelude::Size, Blit, BlitOptions, ToBlitBuffer};
use pixels::{PixelsBuilder, SurfaceTexture};
use rotsprite::Rotsprite;
use web_time::SystemTime;
use winit::{
dpi::LogicalSize,
event::{Event, WindowEvent},
event_loop::{ControlFlow, EventLoop},
window::WindowBuilder,
use std::time::SystemTime;

use blit::{prelude::Size, Blit, BlitBuffer, BlitOptions, ToBlitBuffer};
use pixel_game_lib::{
canvas::Canvas,
vek::{Extent2, Vec2},
window::{Input, KeyCode, WindowConfig},
PixelGame,
};
use rotsprite::Rotsprite;

/// Mask color of the source image.
const MASK_COLOR: u32 = 0xFF_FF_FF;

// Window settings
const DST_SIZE: Size = Size {
width: 200,
height: 200,
};

async fn run() {
// Load the image from disk
let img = image::load_from_memory(include_bytes!("./threeforms.png"))
.unwrap()
.to_rgb8()
.to_blit_buffer_with_mask_color(MASK_COLOR);
log::info!("Loaded RGBA image with size {:?}", img.size());
/// State wrapping the rotation.
struct State {
/// Current rotation of the sprite.
rotation: f64,
/// Last time since rotation.
last_time: SystemTime,
/// The actual image.
img: BlitBuffer,
/// The last rotated variant.
rotated: BlitBuffer,
}

// Setup a winit window
let size = LogicalSize::new(
DST_SIZE.width as f64 * 2.0 + 10.0,
DST_SIZE.height as f64 * 2.0 + 10.0,
);
let event_loop = EventLoop::new();
let mut window_builder = WindowBuilder::new()
.with_title("Rotsprite")
.with_inner_size(size);
impl PixelGame for State {
fn update(&mut self, input: &Input, _mouse_pos: Option<Vec2<usize>>, _dt: f32) -> bool {
// Rotate every second
if self.last_time.elapsed().unwrap().as_secs_f64() >= 1.0 {
self.last_time = SystemTime::now();

// Setup the WASM canvas if running on the browser
#[cfg(target_arch = "wasm32")]
{
use winit::platform::web::WindowBuilderExtWebSys;
self.rotation += 15.0;

window_builder = window_builder.with_canvas(Some(wasm::setup_canvas()));
}
// Rotate the sprite
self.rotated = self
.img
.rotsprite((self.rotation / 15.0).round() * 15.0)
.unwrap();

let window = window_builder.build(&event_loop).unwrap();
println!(
"Rotated sprite in {}ms",
self.last_time.elapsed().unwrap().as_millis()
);
}

let mut pixels = {
let surface_texture =
SurfaceTexture::new(DST_SIZE.width * 2 + 10, DST_SIZE.height * 2 + 10, &window);
PixelsBuilder::new(DST_SIZE.width, DST_SIZE.height, surface_texture)
.clear_color(pixels::wgpu::Color {
r: 0.3,
g: 0.1,
b: 0.3,
a: 1.0,
})
.build_async()
.await
// Exit when escape is pressed
input.key_pressed(KeyCode::Escape)
}
.unwrap();

// Rotate a tiny bit every frame
let mut rotation = 0.0f64;

event_loop.run(move |event, _, control_flow| {
*control_flow = ControlFlow::Wait;

match event {
Event::RedrawRequested(window_id) if window_id == window.id() => {
let mut buffer = bytemuck::cast_slice_mut(pixels.frame_mut());
buffer.fill(0);

let now = SystemTime::now();

// Rotate the sprite
let rotated_blit_buffer = img.rotsprite((rotation / 15.0).round() * 15.0).unwrap();

log::info!("Rotated sprite in {}ms", now.elapsed().unwrap().as_millis());

// Draw the rotated sprite
rotated_blit_buffer.blit(&mut buffer, DST_SIZE, &BlitOptions::new());

rotation += 0.5;

// Blit draws the pixels in RGBA format, but the pixels crate expects BGRA, so convert it
pixels.frame_mut().chunks_exact_mut(4).for_each(|color| {
let (r, g, b, a) = (color[0], color[1], color[2], color[3]);

color[0] = b;
color[1] = g;
color[2] = r;
color[3] = a;
});
fn render(&mut self, canvas: &mut Canvas<'_>) {
// Reset the canvas
canvas.fill(0xFFFFFFFF);

if let Err(err) = pixels.render() {
log::error!("Pixels error:\n{err}");
}
}
Event::MainEventsCleared => {
// Animate the next frame
window.request_redraw();
}
Event::WindowEvent {
event: WindowEvent::CloseRequested,
window_id,
} if window_id == window.id() => {
*control_flow = ControlFlow::Exit;
}
_ => {}
}
});
}

fn main() {
#[cfg(target_arch = "wasm32")]
{
std::panic::set_hook(Box::new(console_error_panic_hook::hook));
console_log::init_with_level(log::Level::Info).expect("error initializing logger");

wasm_bindgen_futures::spawn_local(run());
}
#[cfg(not(target_arch = "wasm32"))]
{
pollster::block_on(run());
// Draw the rotated sprite
let canvas_size = Size::new(canvas.width(), canvas.height());
self.rotated
.blit(canvas.raw_buffer(), canvas_size, &BlitOptions::new());
}
}

#[cfg(target_arch = "wasm32")]
mod wasm {
use wasm_bindgen::JsCast;
use web_sys::HtmlCanvasElement;

/// Attach the winit window to a canvas.
pub fn setup_canvas() -> HtmlCanvasElement {
log::debug!("Binding window to HTML canvas");

let window = web_sys::window().unwrap();

let document = window.document().unwrap();
let body = document.body().unwrap();
body.style().set_css_text("text-align: center");

let canvas = document
.create_element("canvas")
.unwrap()
.dyn_into::<HtmlCanvasElement>()
.unwrap();

canvas.set_id("canvas");
body.append_child(&canvas).unwrap();
canvas.style().set_css_text("display:block; margin: auto");

let header = document.create_element("h2").unwrap();
header.set_text_content(Some("Rotsprite"));
body.append_child(&header).unwrap();

canvas
}
/// Open the empty window.
fn main() {
// Load the image from disk
let img = image::load_from_memory(include_bytes!("./threeforms.png"))
.unwrap()
.to_rgb8()
.to_blit_buffer_with_mask_color(MASK_COLOR);
let rotated = img.clone();

// Active modifiable state
let state = State {
rotation: 0.0,
last_time: SystemTime::now(),
img,
rotated,
};

// Window configuration with huge pixels
let window_config = WindowConfig {
buffer_size: Extent2::new(200, 200),
scaling: 2,
..Default::default()
};

state.run(window_config).expect("Error running game");
}

0 comments on commit a432198

Please sign in to comment.