use dyn_any::{DynAny, StaticType, StaticTypeSized}; use glam::{DAffine2, UVec2}; use graphene_core::text::FontCache; use graphene_core::transform::Footprint; use graphene_core::vector::style::ViewMode; use std::fmt::Debug; use std::future::Future; use std::hash::{Hash, Hasher}; use std::pin::Pin; use std::ptr::addr_of; use std::sync::Arc; use std::time::Duration; #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)] pub struct SurfaceId(pub u64); impl std::fmt::Display for SurfaceId { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.write_fmt(format_args!("{}", self.0)) } } #[derive(Debug, Clone, Copy, PartialEq, serde::Serialize, serde::Deserialize)] pub struct SurfaceFrame { pub surface_id: SurfaceId, pub resolution: UVec2, pub transform: DAffine2, } impl Hash for SurfaceFrame { fn hash(&self, state: &mut H) { self.surface_id.hash(state); self.transform.to_cols_array().iter().for_each(|x| x.to_bits().hash(state)); } } unsafe impl StaticType for SurfaceFrame { type Static = SurfaceFrame; } pub trait Size { fn size(&self) -> UVec2; } #[cfg(target_arch = "wasm32")] impl Size for web_sys::HtmlCanvasElement { fn size(&self) -> UVec2 { UVec2::new(self.width(), self.height()) } } #[derive(Debug, Clone)] pub struct ImageTexture { #[cfg(feature = "wgpu")] pub texture: Arc, #[cfg(not(feature = "wgpu"))] pub texture: (), } impl Hash for ImageTexture { #[cfg(feature = "wgpu")] fn hash(&self, state: &mut H) { self.texture.hash(state); } #[cfg(not(feature = "wgpu"))] fn hash(&self, _state: &mut H) {} } impl PartialEq for ImageTexture { fn eq(&self, other: &Self) -> bool { #[cfg(feature = "wgpu")] { self.texture == other.texture } #[cfg(not(feature = "wgpu"))] { self.texture == other.texture } } } unsafe impl StaticType for ImageTexture { type Static = ImageTexture; } #[cfg(feature = "wgpu")] impl Size for ImageTexture { fn size(&self) -> UVec2 { UVec2::new(self.texture.width(), self.texture.height()) } } impl From> for SurfaceFrame { fn from(x: SurfaceHandleFrame) -> Self { Self { surface_id: x.surface_handle.window_id, transform: x.transform, resolution: x.surface_handle.surface.size(), } } } #[derive(Clone, Debug, PartialEq, Eq)] pub struct SurfaceHandle { pub window_id: SurfaceId, pub surface: Surface, } // #[cfg(target_arch = "wasm32")] // unsafe impl Send for SurfaceHandle {} // #[cfg(target_arch = "wasm32")] // unsafe impl Sync for SurfaceHandle {} impl Size for SurfaceHandle { fn size(&self) -> UVec2 { self.surface.size() } } unsafe impl StaticType for SurfaceHandle { type Static = SurfaceHandle; } #[derive(Clone, Debug, PartialEq)] pub struct SurfaceHandleFrame { pub surface_handle: Arc>, pub transform: DAffine2, } unsafe impl StaticType for SurfaceHandleFrame { type Static = SurfaceHandleFrame; } #[cfg(feature = "wasm")] pub type WasmSurfaceHandle = SurfaceHandle; #[cfg(feature = "wasm")] pub type WasmSurfaceHandleFrame = SurfaceHandleFrame; // TODO: think about how to automatically clean up memory /* impl<'a, Surface> Drop for SurfaceHandle<'a, Surface> { fn drop(&mut self) { self.application_io.destroy_surface(self.surface_id) } }*/ #[cfg(target_arch = "wasm32")] pub type ResourceFuture = Pin, ApplicationError>>>>; #[cfg(not(target_arch = "wasm32"))] pub type ResourceFuture = Pin, ApplicationError>> + Send>>; pub trait ApplicationIo { type Surface; type Executor; fn window(&self) -> Option>; fn create_window(&self) -> SurfaceHandle; fn destroy_window(&self, surface_id: SurfaceId); fn gpu_executor(&self) -> Option<&Self::Executor> { None } fn load_resource(&self, url: impl AsRef) -> Result; } impl ApplicationIo for &T { type Surface = T::Surface; type Executor = T::Executor; fn window(&self) -> Option> { (**self).window() } fn create_window(&self) -> SurfaceHandle { (**self).create_window() } fn destroy_window(&self, surface_id: SurfaceId) { (**self).destroy_window(surface_id) } fn gpu_executor(&self) -> Option<&T::Executor> { (**self).gpu_executor() } fn load_resource<'a>(&self, url: impl AsRef) -> Result { (**self).load_resource(url) } } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum ApplicationError { NotFound, InvalidUrl, } #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] pub enum NodeGraphUpdateMessage {} pub trait NodeGraphUpdateSender { fn send(&self, message: NodeGraphUpdateMessage); } impl NodeGraphUpdateSender for std::sync::Mutex { fn send(&self, message: NodeGraphUpdateMessage) { self.lock().as_mut().unwrap().send(message) } } pub trait GetEditorPreferences { fn use_vello(&self) -> bool; } #[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)] pub enum ExportFormat { #[default] Svg, Png { transparent: bool, }, Jpeg, Canvas, } #[derive(Debug, Default, Clone, Copy, PartialEq, DynAny, serde::Serialize, serde::Deserialize)] pub struct TimingInformation { pub time: f64, pub animation_time: Duration, } #[derive(Debug, Default, Clone, Copy, PartialEq, DynAny, serde::Serialize, serde::Deserialize)] pub struct RenderConfig { pub viewport: Footprint, pub export_format: ExportFormat, pub time: TimingInformation, pub view_mode: ViewMode, pub hide_artboards: bool, pub for_export: bool, } struct Logger; impl NodeGraphUpdateSender for Logger { fn send(&self, message: NodeGraphUpdateMessage) { log::warn!("dispatching message with fallback node graph update sender {:?}", message); } } struct DummyPreferences; impl GetEditorPreferences for DummyPreferences { fn use_vello(&self) -> bool { false } } pub struct EditorApi { /// Font data (for rendering text) made available to the graph through the [`WasmEditorApi`]. pub font_cache: FontCache, /// Gives access to APIs like a rendering surface (native window handle or HTML5 canvas) and WGPU (which becomes WebGPU on web). pub application_io: Option>, pub node_graph_message_sender: Box, /// Editor preferences made available to the graph through the [`WasmEditorApi`]. pub editor_preferences: Box, } impl Eq for EditorApi {} impl Default for EditorApi { fn default() -> Self { Self { font_cache: FontCache::default(), application_io: None, node_graph_message_sender: Box::new(Logger), editor_preferences: Box::new(DummyPreferences), } } } impl Hash for EditorApi { fn hash(&self, state: &mut H) { self.font_cache.hash(state); self.application_io.as_ref().map_or(0, |io| io.as_ref() as *const _ as usize).hash(state); (self.node_graph_message_sender.as_ref() as *const dyn NodeGraphUpdateSender).hash(state); (self.editor_preferences.as_ref() as *const dyn GetEditorPreferences).hash(state); } } impl PartialEq for EditorApi { fn eq(&self, other: &Self) -> bool { self.font_cache == other.font_cache && self.application_io.as_ref().map_or(0, |io| addr_of!(io) as usize) == other.application_io.as_ref().map_or(0, |io| addr_of!(io) as usize) && std::ptr::eq(self.node_graph_message_sender.as_ref() as *const _, other.node_graph_message_sender.as_ref() as *const _) && std::ptr::eq(self.editor_preferences.as_ref() as *const _, other.editor_preferences.as_ref() as *const _) } } impl Debug for EditorApi { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("EditorApi").field("font_cache", &self.font_cache).finish() } } unsafe impl StaticType for EditorApi { type Static = EditorApi; }