use super::web_render_target::update_size;
use super::{BrowserSurfaceShared, RenderStatistics};
use crate::interop::canvas_helper::RENDER_TARGET_KIND_SOFTWARE;
use crate::interop::{thread_proxy, JsObject};
use ferroui_base::platform::surfaces::{
FramebufferLockProperties, IFramebufferRenderTarget, IPlatformRenderSurfaceRenderTarget,
};
use ferroui_base::platform::{
AlphaFormat, ILockedFramebuffer, PixelFormats, RenderTargetSceneInfo, RetainedFramebuffer,
};
use ferroui_base::Vector;
use std::cell::RefCell;
use std::rc::Rc;
use std::sync::Arc;
use wasm_bindgen::prelude::*;
#[wasm_bindgen(raw_module = "./ferroui.js")]
extern "C" {
#[wasm_bindgen(js_namespace = SoftwareRenderTarget, js_name = staticPutPixelData)]
fn put_pixel_data(js: &JsObject, address: u32, size: i32, width: i32, height: i32);
}
pub struct BrowserSoftwareRenderTarget {
js: JsObject,
}
impl BrowserSoftwareRenderTarget {
pub fn new(js: JsObject) -> Rc<Self> {
Rc::new(Self { js })
}
pub fn create_framebuffer_render_target(
self: &Rc<Self>,
shared: Arc<BrowserSurfaceShared>,
) -> Rc<dyn IFramebufferRenderTarget> {
Rc::new(FramebufferRenderTarget { parent: self.clone(), shared, fb: RefCell::new(None) })
}
fn blit(&self, fb: &RetainedFramebuffer) {
let size = fb.size();
put_pixel_data(&self.js, fb.address() as usize as u32, size.width * size.height * 4, size.width, size.height);
RenderStatistics::frame_presented(
thread_proxy::current_thread(),
RENDER_TARGET_KIND_SOFTWARE,
0,
size.width,
size.height,
);
}
}
struct FramebufferRenderTarget {
parent: Rc<BrowserSoftwareRenderTarget>,
shared: Arc<BrowserSurfaceShared>,
fb: RefCell<Option<Rc<RetainedFramebuffer>>>,
}
impl IPlatformRenderSurfaceRenderTarget for FramebufferRenderTarget {}
impl IFramebufferRenderTarget for FramebufferRenderTarget {
fn lock(&self, _scene_info: &RenderTargetSceneInfo) -> (Rc<dyn ILockedFramebuffer>, FramebufferLockProperties) {
let properties = FramebufferLockProperties::default();
let (size, scaling) = self.shared.size();
update_size(&self.parent.js, size);
let mut fb = self.fb.borrow_mut();
if fb.as_ref().is_none_or(|fb| fb.size() != size) {
if let Some(old) = fb.take() {
old.dispose();
}
*fb = Some(RetainedFramebuffer::new(size, PixelFormats::RGBA8888, AlphaFormat::Premul));
}
let blit_target = Rc::downgrade(&self.parent);
let shared = self.shared.clone();
let locked = fb.as_ref().expect("the framebuffer was just created").lock(
Vector::new(scaling * 96.0, scaling * 96.0),
move |fb| {
if let Some(parent) = blit_target.upgrade() {
parent.blit(fb);
shared.frame_presented();
}
},
);
(locked, properties)
}
fn dispose(&self) {
if let Some(fb) = self.fb.borrow_mut().take() {
fb.dispose();
}
}
}