| // Copyright 2022 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| use std::time::Duration; |
| |
| use forma::{ |
| buffer::{layout::LinearLayout, BufferBuilder, BufferLayerCache}, |
| Color, Composition, CpuRenderer, RGB1, |
| }; |
| use wasm_bindgen::{prelude::*, Clamped}; |
| |
| #[path = "../../demo/src/demos/circles.rs"] |
| pub mod circles; |
| #[path = "../../demo/src/demos/rive.rs"] |
| pub mod rive; |
| mod utils; |
| |
| use circles::Circles; |
| use rive::Rive; |
| pub use wasm_bindgen_rayon::init_thread_pool; |
| |
| trait App { |
| fn width(&self) -> usize; |
| fn height(&self) -> usize; |
| fn set_width(&mut self, width: usize); |
| fn set_height(&mut self, height: usize); |
| fn compose(&mut self, composition: &mut Composition, elapsed: Duration, keyboard: &Keyboard); |
| } |
| |
| struct Keyboard; |
| |
| #[wasm_bindgen] |
| pub struct Context { |
| composition: Composition, |
| renderer: CpuRenderer, |
| layout: LinearLayout, |
| layer_cache: BufferLayerCache, |
| app: Box<dyn App>, |
| buffer: Vec<u8>, |
| width: usize, |
| height: usize, |
| was_cleared: bool, |
| } |
| |
| #[wasm_bindgen] |
| pub fn context_new_circles(width: usize, height: usize, count: usize) -> Context { |
| utils::set_panic_hook(); |
| |
| let buffer = vec![0u8; width * 4 * height]; |
| let layout = LinearLayout::new(width, width * 4, height); |
| |
| let composition = Composition::new(); |
| let mut renderer = CpuRenderer::new(); |
| let layer_cache = renderer.create_buffer_layer_cache().unwrap(); |
| |
| let app: Box<dyn App> = Box::new(Circles::new(count)); |
| |
| Context { |
| composition, |
| renderer, |
| layout, |
| layer_cache, |
| app, |
| buffer, |
| width, |
| height, |
| was_cleared: false, |
| } |
| } |
| |
| #[wasm_bindgen] |
| pub fn context_new_rive(width: usize, height: usize, rive_buffer: Vec<u8>) -> Context { |
| utils::set_panic_hook(); |
| |
| let buffer = vec![0u8; width * 4 * height]; |
| let layout = LinearLayout::new(width, width * 4, height); |
| |
| let composition = Composition::new(); |
| let mut renderer = CpuRenderer::new(); |
| let layer_cache = renderer.create_buffer_layer_cache().unwrap(); |
| |
| let mut app: Box<dyn App> = Box::new(Rive::from_buffer(rive_buffer)); |
| app.set_width(width); |
| app.set_height(height); |
| |
| Context { |
| composition, |
| renderer, |
| layout, |
| layer_cache, |
| app, |
| buffer, |
| width, |
| height, |
| was_cleared: false, |
| } |
| } |
| |
| #[wasm_bindgen] |
| pub fn context_draw( |
| context: &mut Context, |
| width: usize, |
| height: usize, |
| elapsed: f64, |
| force_clear: bool, |
| ) -> Clamped<Vec<u8>> { |
| if context.width != width || context.height != height { |
| context.buffer = vec![0u8; width * 4 * height]; |
| context.layout = LinearLayout::new(width, width * 4, height); |
| |
| context.app.set_width(width); |
| context.app.set_height(height); |
| } |
| |
| if force_clear { |
| for pixel in context.buffer.chunks_mut(4) { |
| pixel[0] = 255; |
| pixel[1] = 255; |
| pixel[2] = 255; |
| pixel[3] = 0; |
| } |
| |
| context.was_cleared = true; |
| } else { |
| if context.was_cleared { |
| context.layer_cache.clear(); |
| context.was_cleared = false; |
| } |
| } |
| |
| context.app.compose( |
| &mut context.composition, |
| Duration::from_secs_f64(elapsed / 1000.0), |
| &Keyboard, |
| ); |
| |
| context.renderer.render( |
| &mut context.composition, |
| &mut BufferBuilder::new(&mut context.buffer, &mut context.layout) |
| .layer_cache(context.layer_cache.clone()) |
| .build(), |
| RGB1, |
| Color { r: 1.0, g: 1.0, b: 1.0, a: 0.0 }, |
| None, |
| ); |
| |
| Clamped(context.buffer.clone()) |
| } |