use pixstage::{PixstageRgba, SurfaceTexture};
use std::cell::RefCell;
use std::rc::Rc;
use std::sync::Arc;
use winit::{
application::ApplicationHandler,
event::{ElementState, KeyEvent, WindowEvent},
event_loop::{ActiveEventLoop, ControlFlow, EventLoop},
keyboard::{Key, NamedKey},
window::{Window, WindowId},
};
#[cfg(target_arch = "wasm32")]
use wasm_bindgen::prelude::*;
fn draw_scene(stage: &mut PixstageRgba) {
stage.clear([0, 0, 0, 255]);
let red = [255u8, 0, 0, 255];
for x in 50..=150 {
for y in 50..=150 {
stage.set_pixel(x, y, red);
}
}
draw_line(stage, 30, 30, 170, 170, [255, 255, 0, 255]);
}
fn draw_line(stage: &mut PixstageRgba, x0: u32, y0: u32, x1: u32, y1: u32, color: [u8; 4]) {
let mut steep = false;
let mut x0 = x0 as i32;
let mut x1 = x1 as i32;
let mut y0 = y0 as i32;
let mut y1 = y1 as i32;
if (x0 - x1).abs() < (y0 - y1).abs() {
steep = true;
std::mem::swap(&mut x0, &mut y0);
std::mem::swap(&mut x1, &mut y1);
}
if x0 > x1 {
std::mem::swap(&mut x0, &mut x1);
std::mem::swap(&mut y0, &mut y1);
}
let dx = x1 - x0;
let dy = y1 - y0;
let derror2 = dy.abs() * 2;
let mut error2 = 0;
let mut y = y0;
for x in x0..=x1 {
if steep {
stage.set_pixel(y.max(0) as u32, x.max(0) as u32, color);
} else {
stage.set_pixel(x.max(0) as u32, y.max(0) as u32, color);
}
error2 += derror2;
if error2 > dx {
y += if y1 > y0 { 1 } else { -1 };
error2 -= dx * 2;
}
}
}
#[cfg(target_arch = "wasm32")]
fn setup_canvas(window: &Window) {
use winit::platform::web::WindowExtWebSys;
let _ = web_sys::window()
.and_then(|win| win.document())
.map(|doc| {
let canvas = window.canvas().unwrap();
let mut web_width = 800.0f32;
let ratio = 1.0;
match doc.get_element_by_id("wasm-example") {
Some(dst) => {
web_width = dst.client_width() as f32;
let _ = dst.append_child(&web_sys::Element::from(canvas));
}
None => {
canvas.style().set_css_text(
"background-color: black; display: block; margin: 20px auto;",
);
let _ = doc
.body()
.map(|body| body.append_child(&web_sys::Element::from(canvas)));
}
};
let canvas = window.canvas().unwrap();
let web_height = web_width / ratio;
let scale_factor = window.scale_factor() as f32;
canvas.set_width((web_width * scale_factor) as u32);
canvas.set_height((web_height * scale_factor) as u32);
let _ = canvas.style().set_css_text(
&(canvas.style().css_text()
+ &format!("width: {}px; height: {}px", web_width, web_height)),
);
})
.expect("Couldn't append canvas to document body.");
}
#[derive(Debug)]
struct AppState {
buffer_width: u32,
buffer_height: u32,
scale: u32,
window: Option<Arc<Window>>,
stage: Option<PixstageRgba<'static>>,
}
#[derive(Clone, Debug)]
struct App {
state: Rc<RefCell<AppState>>,
}
impl App {
fn new(buffer_width: u32, buffer_height: u32, scale: u32) -> Self {
Self {
state: Rc::new(RefCell::new(AppState {
buffer_width,
buffer_height,
scale,
window: None,
stage: None,
})),
}
}
}
impl ApplicationHandler for App {
fn resumed(&mut self, event_loop: &ActiveEventLoop) {
if self.state.borrow().window.is_some() {
return;
}
let (buffer_width, buffer_height, scale) = {
let state = self.state.borrow();
(state.buffer_width, state.buffer_height, state.scale)
};
let window_attributes = Window::default_attributes()
.with_inner_size(winit::dpi::PhysicalSize::new(
buffer_width * scale,
buffer_height * scale,
))
.with_title("Pixstage")
.with_resizable(true);
let window = Arc::new(event_loop.create_window(window_attributes).unwrap());
#[cfg(target_arch = "wasm32")]
setup_canvas(&window);
self.state.borrow_mut().window = Some(window.clone());
#[cfg(not(target_arch = "wasm32"))]
{
let size = window.inner_size();
let surface_texture =
SurfaceTexture::new(size.width.max(1), size.height.max(1), window.clone()).unwrap();
let stage = pollster::block_on(PixstageRgba::new_async(
buffer_width,
buffer_height,
surface_texture,
))
.unwrap();
self.state.borrow_mut().stage = Some(stage);
window.request_redraw();
}
#[cfg(target_arch = "wasm32")]
{
let state = self.state.clone();
let window = window.clone();
wasm_bindgen_futures::spawn_local(async move {
let size = window.inner_size();
let surface_texture =
SurfaceTexture::new(size.width.max(1), size.height.max(1), window.clone())
.unwrap();
let stage = PixstageRgba::new_async(buffer_width, buffer_height, surface_texture)
.await
.unwrap();
state.borrow_mut().stage = Some(stage);
window.request_redraw();
});
}
}
fn window_event(
&mut self,
event_loop: &ActiveEventLoop,
_window_id: WindowId,
event: WindowEvent,
) {
let mut state = self.state.borrow_mut();
let Some(window) = state.window.clone() else {
return;
};
match event {
WindowEvent::CloseRequested => event_loop.exit(),
WindowEvent::KeyboardInput {
event:
KeyEvent {
logical_key: Key::Named(NamedKey::Escape),
state: ElementState::Pressed,
..
},
..
} => event_loop.exit(),
WindowEvent::Resized(physical_size) => {
if let Some(stage) = state.stage.as_mut() {
if physical_size.width > 0 && physical_size.height > 0 {
stage.resize_surface(physical_size.width, physical_size.height);
}
window.request_redraw();
}
}
WindowEvent::RedrawRequested => {
if let Some(stage) = state.stage.as_mut() {
draw_scene(stage);
window.pre_present_notify();
if let Err(error) = stage.render() {
eprintln!("{error:?}");
}
window.request_redraw();
}
}
_ => {}
}
}
fn about_to_wait(&mut self, event_loop: &ActiveEventLoop) {
event_loop.set_control_flow(ControlFlow::Wait);
}
}
#[cfg(not(target_arch = "wasm32"))]
fn main() {
env_logger::init();
let event_loop = EventLoop::new().unwrap();
let mut app = App::new(200, 200, 4);
event_loop.run_app(&mut app).unwrap();
}
#[cfg(target_arch = "wasm32")]
#[wasm_bindgen(start)]
pub fn main_web() {
std::panic::set_hook(Box::new(console_error_panic_hook::hook));
console_log::init_with_level(log::Level::Warn).expect("Couldn't initialize logger");
use winit::platform::web::EventLoopExtWebSys;
let event_loop = EventLoop::new().unwrap();
let app = App::new(200, 200, 4);
event_loop.spawn_app(app);
}