Use canvas as target for raster rendering (#1256)

* Implement ApplicationIo

* Simplify output duplication logic

* Fix let node initialization for ExtractImageFrame

* Async macros

* Use manual node registry impl

* Fix canvas insertion into the dom
This commit is contained in:
Dennis Kobert
2023-05-30 20:12:59 +02:00
committed by Keavon Chambers
parent 57415b948b
commit 259dcdc628
27 changed files with 810 additions and 259 deletions

View File

@@ -184,7 +184,7 @@ fn static_nodes() -> Vec<DocumentNodeType> {
},
DocumentNodeType {
name: "Downres",
category: "Ignore",
category: "Raster",
identifier: NodeImplementation::DocumentNode(NodeNetwork {
inputs: vec![0],
outputs: vec![NodeOutput::new(1, 0)],
@@ -234,15 +234,87 @@ fn static_nodes() -> Vec<DocumentNodeType> {
DocumentNodeType {
name: "Input Frame",
category: "Ignore",
identifier: NodeImplementation::proto("graphene_core::ExtractImageFrame"),
inputs: vec![DocumentInputType {
name: "In",
data_type: FrontendGraphDataType::General,
default: NodeInput::Network(concrete!(EditorApi)),
}],
outputs: vec![DocumentOutputType {
name: "Image Frame",
data_type: FrontendGraphDataType::Raster,
}],
properties: node_properties::input_properties,
},
DocumentNodeType {
name: "Create Canvas",
category: "Structural",
identifier: NodeImplementation::DocumentNode(NodeNetwork {
inputs: vec![0, 1],
outputs: vec![NodeOutput::new(0, 0), NodeOutput::new(1, 0)],
nodes: [DocumentNode {
name: "Identity".to_string(),
inputs: vec![NodeInput::Network(concrete!(EditorApi))],
implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_core::ExtractImageFrame")),
..Default::default()
}]
inputs: vec![0],
outputs: vec![NodeOutput::new(1, 0)],
nodes: [
DocumentNode {
name: "Create Canvas".to_string(),
inputs: vec![NodeInput::Network(concrete!(EditorApi))],
implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_core::wasm_application_io::CreateSurfaceNode")),
..Default::default()
},
DocumentNode {
name: "Cache".to_string(),
inputs: vec![NodeInput::ShortCircut(concrete!(())), NodeInput::node(0, 0)],
implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_std::memo::CacheNode")),
..Default::default()
},
]
.into_iter()
.enumerate()
.map(|(id, node)| (id as NodeId, node))
.collect(),
..Default::default()
}),
inputs: vec![DocumentInputType {
name: "In",
data_type: FrontendGraphDataType::General,
default: NodeInput::Network(concrete!(EditorApi)),
}],
outputs: vec![DocumentOutputType {
name: "Canvas",
data_type: FrontendGraphDataType::General,
}],
properties: node_properties::input_properties,
},
DocumentNodeType {
name: "Draw Canvas",
category: "Structural",
identifier: NodeImplementation::DocumentNode(NodeNetwork {
inputs: vec![0, 2],
outputs: vec![NodeOutput::new(3, 0)],
nodes: [
DocumentNode {
name: "Convert Image Frame".to_string(),
inputs: vec![NodeInput::Network(concrete!(ImageFrame<Color>))],
implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_core::ops::IntoNode<_, ImageFrame<SRGBA8>>")),
..Default::default()
},
DocumentNode {
name: "Create Canvas".to_string(),
inputs: vec![NodeInput::Network(concrete!(EditorApi))],
implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_core::wasm_application_io::CreateSurfaceNode")),
..Default::default()
},
DocumentNode {
name: "Cache".to_string(),
inputs: vec![NodeInput::ShortCircut(concrete!(())), NodeInput::node(1, 0)],
implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_std::memo::CacheNode")),
..Default::default()
},
DocumentNode {
name: "Draw Canvas".to_string(),
inputs: vec![NodeInput::node(0, 0), NodeInput::node(2, 0)],
implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_core::wasm_application_io::DrawImageFrameNode<_>")),
..Default::default()
},
]
.into_iter()
.enumerate()
.map(|(id, node)| (id as NodeId, node))
@@ -252,14 +324,18 @@ fn static_nodes() -> Vec<DocumentNodeType> {
inputs: vec![
DocumentInputType {
name: "In",
data_type: FrontendGraphDataType::General,
default: NodeInput::Network(concrete!(ImageFrame<Color>)),
data_type: FrontendGraphDataType::Raster,
default: NodeInput::value(TaggedValue::ImageFrame(ImageFrame::empty()), true),
},
DocumentInputType {
name: "In",
data_type: FrontendGraphDataType::General,
default: NodeInput::Network(concrete!(EditorApi)),
},
DocumentInputType::value("Transform", TaggedValue::DAffine2(DAffine2::IDENTITY), false),
],
outputs: vec![DocumentOutputType {
name: "Image Frame",
data_type: FrontendGraphDataType::Raster,
name: "Canvas",
data_type: FrontendGraphDataType::General,
}],
properties: node_properties::input_properties,
},
@@ -267,12 +343,12 @@ fn static_nodes() -> Vec<DocumentNodeType> {
name: "Begin Scope",
category: "Ignore",
identifier: NodeImplementation::DocumentNode(NodeNetwork {
inputs: vec![0, 2],
inputs: vec![0],
outputs: vec![NodeOutput::new(1, 0), NodeOutput::new(2, 0)],
nodes: [
DocumentNode {
name: "SetNode".to_string(),
inputs: vec![NodeInput::Network(concrete!(EditorApi))],
inputs: vec![NodeInput::ShortCircut(concrete!(EditorApi))],
implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_core::ops::SomeNode")),
..Default::default()
},
@@ -284,7 +360,7 @@ fn static_nodes() -> Vec<DocumentNodeType> {
},
DocumentNode {
name: "RefNode".to_string(),
inputs: vec![NodeInput::Network(concrete!(())), NodeInput::lambda(1, 0)],
inputs: vec![NodeInput::ShortCircut(concrete!(())), NodeInput::lambda(1, 0)],
implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_std::memo::RefNode<_, _>")),
..Default::default()
},
@@ -299,7 +375,7 @@ fn static_nodes() -> Vec<DocumentNodeType> {
inputs: vec![DocumentInputType {
name: "In",
data_type: FrontendGraphDataType::Raster,
default: NodeInput::value(TaggedValue::EditorApi(EditorApi::empty()), true),
default: NodeInput::Network(concrete!(EditorApi)),
}],
outputs: vec![
DocumentOutputType {
@@ -1243,24 +1319,39 @@ impl DocumentNodeType {
}
}
pub fn wrap_network_in_scope(network: NodeNetwork) -> NodeNetwork {
// if the network has no inputs, it doesn't need to be wrapped in a scope
if network.inputs.is_empty() {
return network;
pub fn wrap_network_in_scope(mut network: NodeNetwork) -> NodeNetwork {
let node_ids = network.nodes.keys().copied().collect::<Vec<_>>();
network.generate_node_paths(&[]);
for id in node_ids {
network.flatten(id);
}
assert_eq!(network.inputs.len(), 1, "Networks wrapped in scope must have exactly one input");
let input = network.nodes[&network.inputs[0]].inputs.iter().find(|&i| matches!(i, NodeInput::Network(_))).cloned();
let mut network_inputs = Vec::new();
let mut input_type = None;
for (id, node) in network.nodes.iter() {
for (index, input) in node.inputs.iter().enumerate() {
if let NodeInput::Network(_) = input {
if input_type.is_none() {
input_type = Some(input.clone());
}
assert_eq!(input, input_type.as_ref().unwrap(), "Networks wrapped in scope must have the same input type");
network_inputs.push(*id);
}
}
}
let len = network_inputs.len();
network.inputs = network_inputs;
// if the network has no network inputs, it doesn't need to be wrapped in a scope either
let Some(input_type) = input else {
// if the network has no inputs, it doesn't need to be wrapped in a scope
if len == 0 {
return network;
};
}
let inner_network = DocumentNode {
name: "Scope".to_string(),
implementation: DocumentNodeImplementation::Network(network),
inputs: vec![NodeInput::node(0, 1)],
inputs: core::iter::repeat(NodeInput::node(0, 1)).take(len).collect(),
..Default::default()
};
@@ -1268,7 +1359,7 @@ pub fn wrap_network_in_scope(network: NodeNetwork) -> NodeNetwork {
let nodes = vec![
resolve_document_node_type("Begin Scope")
.expect("Begin Scope node type not found")
.to_document_node(vec![input_type], DocumentNodeMetadata::default()),
.to_document_node(vec![input_type.unwrap()], DocumentNodeMetadata::default()),
inner_network,
resolve_document_node_type("End Scope")
.expect("End Scope node type not found")

View File

@@ -41,9 +41,9 @@ impl Default for BrushOptions {
fn default() -> Self {
Self {
diameter: 40.,
hardness: 50.,
hardness: 0.,
flow: 100.,
spacing: 50.,
spacing: 20.,
color: ToolColorOptions::default(),
}
}
@@ -252,7 +252,7 @@ impl BrushToolData {
self.transform = DAffine2::IDENTITY;
matches!(layer.cached_output_data, CachedOutputData::BlobURL(_)).then_some(&self.layer_path)
matches!(layer.cached_output_data, CachedOutputData::BlobURL(_) | CachedOutputData::SurfaceId(_)).then_some(&self.layer_path)
}
fn update_strokes(&self, brush_options: &BrushOptions, responses: &mut VecDeque<Message>) {