use oden_js::{module, ContextRef, Error, Result, Value}; use std::sync::atomic::{AtomicU32, Ordering}; use std::sync::mpsc::Sender; use std::sync::Arc; #[derive(Debug)] pub struct PrintCommand { pub text: String, } #[derive(Debug)] pub struct ClearCommand { pub color: [f64; 4], } #[derive(Debug)] pub struct SpriteCommand { pub x: f32, pub y: f32, pub w: f32, pub h: f32, pub u: f32, pub v: f32, pub sw: f32, pub sh: f32, } #[derive(Debug)] pub struct CreateTextureCommand { pub id: u32, pub image: image::DynamicImage, pub label: Option, } #[derive(Debug)] pub enum GraphicsCommand { Clear(ClearCommand), Print(PrintCommand), Sprite(SpriteCommand), CreateTexture(CreateTextureCommand), UseTexture(u32), EndFrame, } struct GraphicsImpl { next_texture_id: AtomicU32, sender: Sender, } impl GraphicsImpl { pub fn new(sender: Sender) -> Self { GraphicsImpl { sender, next_texture_id: AtomicU32::new(0), } } fn print(&self, text: String) -> () { let _ = self .sender .send(GraphicsCommand::Print(PrintCommand { text })); } fn cls(&self, r: f64, g: f64, b: f64) -> () { let _ = self.sender.send(GraphicsCommand::Clear(ClearCommand { color: [r, g, b, 1.0], })); } fn spr(&self, x: f32, y: f32, w: f32, h: f32, u: f32, v: f32, sw: f32, sh: f32) { let _ = self.sender.send(GraphicsCommand::Sprite(SpriteCommand { x, y, w, h, u, v, sw, sh, })); } fn create_texture( &self, context: &ContextRef, buffer: Value, label: Option, ) -> Result { let bytes = buffer.get_array_buffer(context)?; let image = match image::load_from_memory(&bytes) { Ok(i) => i, Err(e) => return Err(Error::RustFunctionError(format!("{e}"))), }; let id = self.next_texture_id.fetch_add(1, Ordering::SeqCst); let _ = self .sender .send(GraphicsCommand::CreateTexture(CreateTextureCommand { id, image, label, })); Ok(id) } fn use_texture(&self, id: u32) { let _ = self.sender.send(GraphicsCommand::UseTexture(id)); } } pub struct GraphicsAPI { gfx: Arc, } impl GraphicsAPI { pub fn define(ctx: &ContextRef, sender: Sender) -> oden_js::Result { let gfx = Arc::new(GraphicsImpl::new(sender)); let mut builder = module::native::NativeModuleBuilder::new(ctx); { let gfx = gfx.clone(); builder.export( "print", ctx.new_fn(move |_: &ContextRef, t: String| gfx.print(t))?, )?; } { let gfx = gfx.clone(); builder.export( "cls", ctx.new_fn(move |_: &ContextRef, r: f64, g: f64, b: f64| gfx.cls(r, g, b))?, )?; } { let gfx = gfx.clone(); builder.export( "spr", ctx.new_fn( move |_: &ContextRef, x: f32, y: f32, w: f32, h: f32, u: f32, v: f32, sw: f32, sh: f32| gfx.spr(x, y, w, h, u, v, sw, sh), )?, )?; } { let gfx = gfx.clone(); builder.export( "use_texture", ctx.new_fn(move |_: &ContextRef, id: u32| gfx.use_texture(id))?, )?; } { let gfx = gfx.clone(); builder.export( "create_texture", ctx.new_fn( move |c: &ContextRef, buffer: Value, label: Option| { gfx.create_texture(c, buffer, label) }, )?, )?; } builder.build("graphics-core")?; Ok(GraphicsAPI { gfx }) } pub fn end_frame(&self) { let _ = self.gfx.sender.send(GraphicsCommand::EndFrame); } }