|
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<H: Hasher>(&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<wgpu::Texture>, |
|
#[cfg(not(feature = "wgpu"))] |
|
pub texture: (), |
|
} |
|
|
|
impl Hash for ImageTexture { |
|
#[cfg(feature = "wgpu")] |
|
fn hash<H: Hasher>(&self, state: &mut H) { |
|
self.texture.hash(state); |
|
} |
|
#[cfg(not(feature = "wgpu"))] |
|
fn hash<H: Hasher>(&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<S: Size> From<SurfaceHandleFrame<S>> for SurfaceFrame { |
|
fn from(x: SurfaceHandleFrame<S>) -> 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<Surface> { |
|
pub window_id: SurfaceId, |
|
pub surface: Surface, |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
impl<S: Size> Size for SurfaceHandle<S> { |
|
fn size(&self) -> UVec2 { |
|
self.surface.size() |
|
} |
|
} |
|
|
|
unsafe impl<T: 'static> StaticType for SurfaceHandle<T> { |
|
type Static = SurfaceHandle<T>; |
|
} |
|
|
|
#[derive(Clone, Debug, PartialEq)] |
|
pub struct SurfaceHandleFrame<Surface> { |
|
pub surface_handle: Arc<SurfaceHandle<Surface>>, |
|
pub transform: DAffine2, |
|
} |
|
|
|
unsafe impl<T: 'static> StaticType for SurfaceHandleFrame<T> { |
|
type Static = SurfaceHandleFrame<T>; |
|
} |
|
|
|
#[cfg(feature = "wasm")] |
|
pub type WasmSurfaceHandle = SurfaceHandle<web_sys::HtmlCanvasElement>; |
|
#[cfg(feature = "wasm")] |
|
pub type WasmSurfaceHandleFrame = SurfaceHandleFrame<web_sys::HtmlCanvasElement>; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#[cfg(target_arch = "wasm32")] |
|
pub type ResourceFuture = Pin<Box<dyn Future<Output = Result<Arc<[u8]>, ApplicationError>>>>; |
|
#[cfg(not(target_arch = "wasm32"))] |
|
pub type ResourceFuture = Pin<Box<dyn Future<Output = Result<Arc<[u8]>, ApplicationError>> + Send>>; |
|
|
|
pub trait ApplicationIo { |
|
type Surface; |
|
type Executor; |
|
fn window(&self) -> Option<SurfaceHandle<Self::Surface>>; |
|
fn create_window(&self) -> SurfaceHandle<Self::Surface>; |
|
fn destroy_window(&self, surface_id: SurfaceId); |
|
fn gpu_executor(&self) -> Option<&Self::Executor> { |
|
None |
|
} |
|
fn load_resource(&self, url: impl AsRef<str>) -> Result<ResourceFuture, ApplicationError>; |
|
} |
|
|
|
impl<T: ApplicationIo> ApplicationIo for &T { |
|
type Surface = T::Surface; |
|
type Executor = T::Executor; |
|
|
|
fn window(&self) -> Option<SurfaceHandle<Self::Surface>> { |
|
(**self).window() |
|
} |
|
|
|
fn create_window(&self) -> SurfaceHandle<T::Surface> { |
|
(**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<str>) -> Result<ResourceFuture, ApplicationError> { |
|
(**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<T: NodeGraphUpdateSender> NodeGraphUpdateSender for std::sync::Mutex<T> { |
|
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<Io> { |
|
|
|
pub font_cache: FontCache, |
|
|
|
pub application_io: Option<Arc<Io>>, |
|
pub node_graph_message_sender: Box<dyn NodeGraphUpdateSender + Send + Sync>, |
|
|
|
pub editor_preferences: Box<dyn GetEditorPreferences + Send + Sync>, |
|
} |
|
|
|
impl<Io> Eq for EditorApi<Io> {} |
|
|
|
impl<Io: Default> Default for EditorApi<Io> { |
|
fn default() -> Self { |
|
Self { |
|
font_cache: FontCache::default(), |
|
application_io: None, |
|
node_graph_message_sender: Box::new(Logger), |
|
editor_preferences: Box::new(DummyPreferences), |
|
} |
|
} |
|
} |
|
|
|
impl<Io> Hash for EditorApi<Io> { |
|
fn hash<H: Hasher>(&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<Io> PartialEq for EditorApi<Io> { |
|
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<T> Debug for EditorApi<T> { |
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { |
|
f.debug_struct("EditorApi").field("font_cache", &self.font_cache).finish() |
|
} |
|
} |
|
|
|
unsafe impl<T: StaticTypeSized> StaticType for EditorApi<T> { |
|
type Static = EditorApi<T::Static>; |
|
} |
|
|