diff options
| author | Maze Winther <mazewinther@gmail.com> | 2026-04-07 01:09:13 +0200 |
|---|---|---|
| committer | Maze Winther <mazewinther@gmail.com> | 2026-04-07 01:09:13 +0200 |
| commit | e4b67094e757917588fb624a30aa046667403add (patch) | |
| tree | 55452692eac69a5a63b58f2a5cdb701dd7023d68 /rust/wasm/src/effects.rs | |
| parent | 79df73643185c02493364a077402726d1fa7b9ec (diff) | |
| download | opencut-e4b67094e757917588fb624a30aa046667403add.tar.gz | |
refactor: split effects and masks into dedicated rust crates, introduce MediaTime and FrameRate
Diffstat (limited to 'rust/wasm/src/effects.rs')
| -rw-r--r-- | rust/wasm/src/effects.rs | 101 |
1 files changed, 101 insertions, 0 deletions
diff --git a/rust/wasm/src/effects.rs b/rust/wasm/src/effects.rs new file mode 100644 index 00000000..1f5c2947 --- /dev/null +++ b/rust/wasm/src/effects.rs @@ -0,0 +1,101 @@ +#![cfg(target_arch = "wasm32")] + +use effects::{ApplyEffectsOptions, EffectPass, UniformValue}; +use gpu::wgpu; +use js_sys::Object; +use serde::Deserialize; +use wasm_bindgen::{JsCast, JsValue, prelude::wasm_bindgen}; + +use crate::gpu::{ + import_canvas_texture, read_offscreen_canvas_property, read_serde_property, read_u32_property, + render_texture_to_canvas, with_gpu_runtime, +}; + +struct ApplyEffectPassesOptions { + source: wgpu::web_sys::OffscreenCanvas, + width: u32, + height: u32, + passes: Vec<EffectPassInput>, +} + +#[derive(Deserialize)] +#[serde(rename_all = "camelCase")] +struct EffectPassInput { + shader: String, + uniforms: Vec<EffectUniformInput>, +} + +#[derive(Deserialize)] +#[serde(rename_all = "camelCase")] +struct EffectUniformInput { + name: String, + value: Vec<f32>, +} + +#[wasm_bindgen(js_name = applyEffectPasses)] +pub fn apply_effect_passes(options: JsValue) -> Result<wgpu::web_sys::OffscreenCanvas, JsValue> { + let ApplyEffectPassesOptions { + source, + width, + height, + passes, + } = parse_apply_effect_passes_options(options)?; + + with_gpu_runtime(|runtime| { + let source_texture = import_canvas_texture( + &runtime.context, + &source, + width, + height, + "effects-input-texture", + ); + let effect_passes = map_effect_passes(passes); + let result_texture = runtime + .effects + .apply( + &runtime.context, + ApplyEffectsOptions { + source: &source_texture, + width, + height, + passes: &effect_passes, + }, + ) + .map_err(|error| JsValue::from_str(&error.to_string()))?; + render_texture_to_canvas(&runtime.context, &result_texture, width, height) + }) +} + +fn map_effect_passes(effect_passes: Vec<EffectPassInput>) -> Vec<EffectPass> { + effect_passes + .into_iter() + .map(|pass| EffectPass { + shader: pass.shader, + uniforms: pass + .uniforms + .into_iter() + .map(|uniform| { + let value = if uniform.value.len() == 1 { + UniformValue::Number(uniform.value[0]) + } else { + UniformValue::Vector(uniform.value) + }; + (uniform.name, value) + }) + .collect(), + }) + .collect() +} + +fn parse_apply_effect_passes_options(value: JsValue) -> Result<ApplyEffectPassesOptions, JsValue> { + let object: Object = value + .dyn_into() + .map_err(|_| JsValue::from_str("applyEffectPasses expects an options object"))?; + + Ok(ApplyEffectPassesOptions { + source: read_offscreen_canvas_property(&object, "source")?, + width: read_u32_property(&object, "width")?, + height: read_u32_property(&object, "height")?, + passes: read_serde_property(&object, "passes")?, + }) +} |
