Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,7 @@ impl<'a> ModifyInputsContext<'a> {
let fill = resolve_document_node_type("Fill").expect("Fill node does not exist").default_node_template();
let transform = resolve_document_node_type("Transform").expect("Transform node does not exist").default_node_template();
let text = resolve_document_node_type("Text").expect("Text node does not exist").node_template_input_override([
Some(NodeInput::scope("editor-api")),
Some(NodeInput::scope("font-cache")),
Some(NodeInput::value(TaggedValue::String(text), false)),
Some(NodeInput::value(TaggedValue::Font(font), false)),
Some(NodeInput::value(TaggedValue::F64(typesetting.font_size), false)),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1317,7 +1317,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
implementation: DocumentNodeImplementation::ProtoNode(text::text::IDENTIFIER),
call_argument: concrete!(Context),
inputs: vec![
NodeInput::scope("editor-api"),
NodeInput::scope("font-cache"),
NodeInput::value(TaggedValue::String("Lorem ipsum".to_string()), false),
NodeInput::value(
TaggedValue::Font(Font::new(graphene_std::consts::DEFAULT_FONT_FAMILY.into(), graphene_std::consts::DEFAULT_FONT_STYLE.into())),
Expand Down
6 changes: 4 additions & 2 deletions editor/src/messages/portfolio/portfolio_message_handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ use graphene_std::subpath::BezierHandles;
use graphene_std::text::Font;
use graphene_std::vector::misc::HandleId;
use graphene_std::vector::{PointId, SegmentId, Vector, VectorModificationType};
use std::sync::Arc;
use std::vec;

#[derive(ExtractField)]
Expand Down Expand Up @@ -351,8 +352,9 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageContext<'_>> for Portfolio
data,
} => {
let font = Font::new(font_family, font_style);

self.persistent_data.font_cache.insert(font, preview_url, data);
let mut font_cache = self.persistent_data.font_cache.as_ref().clone();
font_cache.insert(font, preview_url, data);
self.persistent_data.font_cache = Arc::new(font_cache);
self.executor.update_font_cache(self.persistent_data.font_cache.clone());
for document_id in self.document_ids.iter() {
let node_to_inspect = self.node_to_inspect();
Expand Down
3 changes: 2 additions & 1 deletion editor/src/messages/portfolio/utility_types.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
use graphene_std::text::FontCache;
use std::sync::Arc;

#[derive(Debug, Default)]
pub struct PersistentData {
pub font_cache: FontCache,
pub font_cache: Arc<FontCache>,
pub use_vello: bool,
}

Expand Down
3 changes: 2 additions & 1 deletion editor/src/node_graph_executor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ use graphene_std::transform::Footprint;
use graphene_std::vector::Vector;
use graphene_std::wasm_application_io::RenderOutputType;
use interpreted_executor::dynamic_executor::ResolvedDocumentNodeTypesDelta;
use std::sync::Arc;

mod runtime_io;
pub use runtime_io::NodeRuntimeIO;
Expand Down Expand Up @@ -89,7 +90,7 @@ impl NodeGraphExecutor {
execution_id
}

pub fn update_font_cache(&self, font_cache: FontCache) {
pub fn update_font_cache(&self, font_cache: Arc<FontCache>) {
self.runtime_io.send(GraphRuntimeRequest::FontCacheUpdate(font_cache)).expect("Failed to send font cache update");
}

Expand Down
20 changes: 7 additions & 13 deletions editor/src/node_graph_executor/runtime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ pub struct NodeRuntime {
update_thumbnails: bool,

editor_api: Arc<WasmEditorApi>,
font_cache: Arc<FontCache>,

node_graph_errors: GraphErrors,
monitor_nodes: Vec<Vec<NodeId>>,

Expand All @@ -60,7 +62,7 @@ pub struct NodeRuntime {
pub enum GraphRuntimeRequest {
GraphUpdate(GraphUpdate),
ExecutionRequest(ExecutionRequest),
FontCacheUpdate(FontCache),
FontCacheUpdate(Arc<FontCache>),
EditorPreferencesUpdate(EditorPreferences),
}

Expand Down Expand Up @@ -113,14 +115,15 @@ impl NodeRuntime {
update_thumbnails: true,

editor_api: WasmEditorApi {
font_cache: FontCache::default(),
editor_preferences: Box::new(EditorPreferences::default()),
node_graph_message_sender: Box::new(InternalNodeGraphUpdateSender(sender)),

application_io: None,
}
.into(),

font_cache: Arc::new(FontCache::default()),

node_graph_errors: Vec::new(),
monitor_nodes: Vec::new(),

Expand All @@ -139,7 +142,6 @@ impl NodeRuntime {
application_io: Some(WasmApplicationIo::new().await.into()),
#[cfg(any(test, not(target_family = "wasm")))]
application_io: Some(WasmApplicationIo::new_offscreen().await.into()),
font_cache: self.editor_api.font_cache.clone(),
node_graph_message_sender: Box::new(self.sender.clone()),
editor_preferences: Box::new(self.editor_preferences.clone()),
}
Expand All @@ -163,13 +165,7 @@ impl NodeRuntime {
for request in requests {
match request {
GraphRuntimeRequest::FontCacheUpdate(font_cache) => {
self.editor_api = WasmEditorApi {
font_cache,
application_io: self.editor_api.application_io.clone(),
node_graph_message_sender: Box::new(self.sender.clone()),
editor_preferences: Box::new(self.editor_preferences.clone()),
}
.into();
self.font_cache = font_cache;
if let Some(graph) = self.old_graph.clone() {
// We ignore this result as compilation errors should have been reported in an earlier iteration
let _ = self.update_network(graph).await;
Expand All @@ -178,7 +174,6 @@ impl NodeRuntime {
GraphRuntimeRequest::EditorPreferencesUpdate(preferences) => {
self.editor_preferences = preferences.clone();
self.editor_api = WasmEditorApi {
font_cache: self.editor_api.font_cache.clone(),
application_io: self.editor_api.application_io.clone(),
node_graph_message_sender: Box::new(self.sender.clone()),
editor_preferences: Box::new(preferences),
Expand Down Expand Up @@ -240,7 +235,7 @@ impl NodeRuntime {
async fn update_network(&mut self, mut graph: NodeNetwork) -> Result<ResolvedDocumentNodeTypesDelta, String> {
preprocessor::expand_network(&mut graph, &self.substitutions);

let scoped_network = wrap_network_in_scope(graph, self.editor_api.clone());
let scoped_network = wrap_network_in_scope(graph, self.editor_api.clone(), self.font_cache.clone());

// We assume only one output
assert_eq!(scoped_network.exports.len(), 1, "Graph with multiple outputs not yet handled");
Expand Down Expand Up @@ -408,7 +403,6 @@ pub async fn replace_application_io(application_io: WasmApplicationIo) {
let mut node_runtime = NODE_RUNTIME.lock();
if let Some(node_runtime) = &mut *node_runtime {
node_runtime.editor_api = WasmEditorApi {
font_cache: node_runtime.editor_api.font_cache.clone(),
application_io: Some(application_io.into()),
node_graph_message_sender: Box::new(node_runtime.sender.clone()),
editor_preferences: Box::new(node_runtime.editor_preferences.clone()),
Expand Down
10 changes: 2 additions & 8 deletions node-graph/gapplication-io/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
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;
Expand Down Expand Up @@ -262,8 +261,6 @@ impl GetEditorPreferences for DummyPreferences {
}

pub struct EditorApi<Io> {
/// 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<Arc<Io>>,
pub node_graph_message_sender: Box<dyn NodeGraphUpdateSender + Send + Sync>,
Expand All @@ -276,7 +273,6 @@ 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),
Expand All @@ -286,7 +282,6 @@ impl<Io: Default> Default for EditorApi<Io> {

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);
Expand All @@ -295,16 +290,15 @@ impl<Io> Hash for EditorApi<Io> {

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)
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()
f.debug_struct("EditorApi").finish()
}
}

Expand Down
2 changes: 2 additions & 0 deletions node-graph/gcore/src/consts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,5 @@ pub const LAYER_OUTLINE_STROKE_WEIGHT: f64 = 0.5;
// Fonts
pub const DEFAULT_FONT_FAMILY: &str = "Cabin";
pub const DEFAULT_FONT_STYLE: &str = "Regular (400)";
pub const SOURCE_SANS_PRO_FAMILY: &str = "Source Sans Pro";
pub const SOURCE_SANS_PRO_STYLE: &str = "Regular (400)";
29 changes: 28 additions & 1 deletion node-graph/gcore/src/text/font_cache.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
use dyn_any::DynAny;
use std::collections::HashMap;

use crate::consts::{SOURCE_SANS_PRO_FAMILY, SOURCE_SANS_PRO_STYLE};

/// A font type (storing font family and font style and an optional preview URL)
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, Hash, PartialEq, Eq, DynAny, specta::Type)]
pub struct Font {
Expand All @@ -20,14 +22,30 @@ impl Default for Font {
}
}
/// A cache of all loaded font data and preview urls along with the default font (send from `init_app` in `editor_api.rs`)
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, Default, PartialEq, DynAny)]
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, PartialEq, DynAny)]
pub struct FontCache {
/// Actual font file data used for rendering a font
font_file_data: HashMap<Font, Vec<u8>>,
/// Web font preview URLs used for showing fonts when live editing
preview_urls: HashMap<Font, String>,
}

impl Default for FontCache {
fn default() -> Self {
let font = Font::new(SOURCE_SANS_PRO_FAMILY.to_string(), SOURCE_SANS_PRO_STYLE.to_string());
// Load Source Sans Pro font data
// TODO: Grab this from the node_modules folder (either with `include_bytes!` or ideally at runtime) instead of checking the font file into the repo.
// TODO: And maybe use the WOFF2 version (if it's supported) for its smaller, compressed file size.
const FONT_DATA: &[u8] = include_bytes!("source-sans-pro-regular.ttf");
let mut font_file_data = HashMap::new();
font_file_data.insert(font, FONT_DATA.to_vec());
Self {
font_file_data,
preview_urls: HashMap::new(),
}
}
}

impl FontCache {
/// Returns the font family name if the font is cached, otherwise returns the fallback font family name if that is cached
pub fn resolve_font<'a>(&'a self, font: &'a Font) -> Option<&'a Font> {
Expand Down Expand Up @@ -60,6 +78,15 @@ impl FontCache {
pub fn get_preview_url(&self, font: &Font) -> Option<&String> {
self.preview_urls.get(font)
}

/// Get the default font (Source Sans Pro) which is loaded from disk
pub fn source_sans_pro(&self) -> &Vec<u8> {
self.get(&Font {
font_family: SOURCE_SANS_PRO_FAMILY.to_string(),
font_style: SOURCE_SANS_PRO_STYLE.to_string(),
})
.expect("Source sans pro must be added to font cache")
}
}

impl std::hash::Hash for FontCache {
Expand Down
Binary file not shown.
12 changes: 9 additions & 3 deletions node-graph/graph-craft/src/document/value.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ use graphene_brush::brush_stroke::BrushStroke;
use graphene_core::raster::Image;
use graphene_core::raster_types::{CPU, Raster};
use graphene_core::table::Table;
use graphene_core::text::FontCache;
use graphene_core::transform::ReferencePoint;
use graphene_core::uuid::NodeId;
use graphene_core::vector::Vector;
Expand Down Expand Up @@ -38,7 +39,9 @@ macro_rules! tagged_value {
RenderOutput(RenderOutput),
SurfaceFrame(SurfaceFrame),
#[serde(skip)]
EditorApi(Arc<WasmEditorApi>)
EditorApi(Arc<WasmEditorApi>),
#[serde(skip)]
FontCache(Arc<FontCache>),
}

// We must manually implement hashing because some values are floats and so do not reproducibly hash (see FakeHash below)
Expand All @@ -52,6 +55,7 @@ macro_rules! tagged_value {
Self::RenderOutput(x) => x.hash(state),
Self::SurfaceFrame(x) => x.hash(state),
Self::EditorApi(x) => x.hash(state),
Self::FontCache(x) => x.hash(state),
}
}
}
Expand All @@ -64,6 +68,7 @@ macro_rules! tagged_value {
Self::RenderOutput(x) => Box::new(x),
Self::SurfaceFrame(x) => Box::new(x),
Self::EditorApi(x) => Box::new(x),
Self::FontCache(x) => Box::new(x),
}
}
/// Converts to a Arc<dyn Any + Send + Sync + 'static>
Expand All @@ -74,6 +79,7 @@ macro_rules! tagged_value {
Self::RenderOutput(x) => Arc::new(x),
Self::SurfaceFrame(x) => Arc::new(x),
Self::EditorApi(x) => Arc::new(x),
Self::FontCache(x) => Arc::new(x),
}
}
/// Creates a graphene_core::Type::Concrete(TypeDescriptor { .. }) with the type of the value inside the tagged value
Expand All @@ -83,7 +89,8 @@ macro_rules! tagged_value {
$( Self::$identifier(_) => concrete!($ty), )*
Self::RenderOutput(_) => concrete!(RenderOutput),
Self::SurfaceFrame(_) => concrete!(SurfaceFrame),
Self::EditorApi(_) => concrete!(&WasmEditorApi)
Self::EditorApi(_) => concrete!(&WasmEditorApi),
Self::FontCache(_) => concrete!(Arc<FontCache>),
}
}
/// Attempts to downcast the dynamic type to a tagged value
Expand All @@ -97,7 +104,6 @@ macro_rules! tagged_value {
x if x == TypeId::of::<RenderOutput>() => Ok(TaggedValue::RenderOutput(*downcast(input).unwrap())),
x if x == TypeId::of::<SurfaceFrame>() => Ok(TaggedValue::SurfaceFrame(*downcast(input).unwrap())),


_ => Err(format!("Cannot convert {:?} to TaggedValue", DynAny::type_name(input.as_ref()))),
}
}
Expand Down
5 changes: 3 additions & 2 deletions node-graph/graphene-cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,6 @@ async fn main() -> Result<(), Box<dyn Error>> {

let preferences = EditorPreferences { use_vello: true };
let editor_api = Arc::new(WasmEditorApi {
font_cache: FontCache::default(),
application_io: Some(application_io.into()),
node_graph_message_sender: Box::new(UpdateLogger {}),
editor_preferences: Box::new(preferences),
Expand Down Expand Up @@ -184,7 +183,9 @@ fn compile_graph(document_string: String, editor_api: Arc<WasmEditorApi>) -> Res
let substitutions = preprocessor::generate_node_substitutions();
preprocessor::expand_network(&mut network, &substitutions);

let wrapped_network = wrap_network_in_scope(network.clone(), editor_api);
let font_cache = Arc::new(FontCache::default());

let wrapped_network = wrap_network_in_scope(network.clone(), editor_api, font_cache);

let compiler = Compiler {};
compiler.compile_single(wrapped_network).map_err(|x| x.into())
Expand Down
7 changes: 4 additions & 3 deletions node-graph/gstd/src/text.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
use graph_craft::wasm_application_io::WasmEditorApi;
use std::sync::Arc;

pub use graphene_core::text::*;
use graphene_core::{Ctx, table::Table, vector::Vector};

#[node_macro::node(category(""))]
fn text<'i: 'n>(
_: impl Ctx,
editor: &'i WasmEditorApi,
font_cache: Arc<FontCache>,
text: String,
font_name: Font,
#[unit(" px")]
Expand Down Expand Up @@ -38,7 +39,7 @@ fn text<'i: 'n>(
align,
};

let font_data = editor.font_cache.get(&font_name).map(|f| load_font(f));
let font_data = font_cache.get(&font_name).map(|f| load_font(f));

to_path(&text, font_data, typesetting, per_glyph_instances)
}
4 changes: 3 additions & 1 deletion node-graph/interpreted-executor/benches/benchmark_util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,15 @@ use futures::executor::block_on;
use graph_craft::proto::ProtoNetwork;
use graph_craft::util::{DEMO_ART, compile, load_from_name};
use graphene_std::application_io::EditorApi;
use graphene_std::text::FontCache;
use interpreted_executor::dynamic_executor::DynamicExecutor;
use interpreted_executor::util::wrap_network_in_scope;

pub fn setup_network(name: &str) -> (DynamicExecutor, ProtoNetwork) {
let network = load_from_name(name);
let editor_api = std::sync::Arc::new(EditorApi::default());
let network = wrap_network_in_scope(network, editor_api);
let font_cache = std::sync::Arc::new(FontCache::default());
let network = wrap_network_in_scope(network, editor_api, font_cache);
let proto_network = compile(network);
let executor = block_on(DynamicExecutor::new(proto_network.clone())).unwrap();
(executor, proto_network)
Expand Down
Loading
Loading