mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-16 23:08:05 +08:00
Improve backwards compatability robustness of serde-based document format
This commit is contained in:
@@ -56,7 +56,7 @@ fn add_network() -> NodeNetwork {
|
||||
}},
|
||||
))"#,
|
||||
TaggedValue::BlendMode(BlendMode::Normal).to_primitive_string(),
|
||||
TaggedValue::F32(1.0).to_primitive_string(),
|
||||
TaggedValue::F64(1.).to_primitive_string(),
|
||||
),
|
||||
concrete![Color],
|
||||
))],
|
||||
|
||||
@@ -80,12 +80,25 @@ pub struct ProtoNodeIdentifier {
|
||||
pub name: Cow<'static, str>,
|
||||
}
|
||||
|
||||
fn migrate_type_descriptor_names<'de, D: serde::Deserializer<'de>>(deserializer: D) -> Result<Cow<'static, str>, D::Error> {
|
||||
use serde::Deserialize;
|
||||
|
||||
// Rename "f32" to "f64"
|
||||
let name = String::deserialize(deserializer)?;
|
||||
let name = match name.as_str() {
|
||||
"f32" => "f64".to_string(),
|
||||
_ => name,
|
||||
};
|
||||
Ok(Cow::Owned(name))
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Eq, specta::Type)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
pub struct TypeDescriptor {
|
||||
#[cfg_attr(feature = "serde", serde(skip))]
|
||||
#[specta(skip)]
|
||||
pub id: Option<TypeId>,
|
||||
#[serde(deserialize_with = "migrate_type_descriptor_names")]
|
||||
pub name: Cow<'static, str>,
|
||||
#[serde(default)]
|
||||
pub size: usize,
|
||||
|
||||
@@ -3,8 +3,7 @@ use crate::proto::{ConstructionArgs, ProtoNetwork, ProtoNode, ProtoNodeInput};
|
||||
|
||||
use dyn_any::{DynAny, StaticType};
|
||||
pub use graphene_core::uuid::generate_uuid;
|
||||
use graphene_core::vector::VectorData;
|
||||
use graphene_core::{ArtboardGroup, GraphicGroup, ProtoNodeIdentifier, Type};
|
||||
use graphene_core::{ProtoNodeIdentifier, Type};
|
||||
|
||||
use glam::IVec2;
|
||||
use std::collections::hash_map::DefaultHasher;
|
||||
@@ -51,6 +50,15 @@ fn return_true() -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
// TODO: Eventually remove this (probably starting late 2024)
|
||||
fn migrate_layer_to_merge<'de, D: serde::Deserializer<'de>>(deserializer: D) -> Result<String, D::Error> {
|
||||
let mut s: String = serde::Deserialize::deserialize(deserializer)?;
|
||||
if s == "Layer" {
|
||||
s = "Merge".to_string();
|
||||
}
|
||||
Ok(s)
|
||||
}
|
||||
|
||||
/// An instance of a [`DocumentNodeDefinition`] that has been instantiated in a [`NodeNetwork`].
|
||||
/// Currently, when an instance is made, it lives all on its own without any lasting connection to the definition.
|
||||
/// But we will want to change it in the future so it merely references its definition.
|
||||
@@ -62,6 +70,7 @@ pub struct DocumentNode {
|
||||
pub alias: String,
|
||||
// TODO: Replace this name with a reference to the [`DocumentNodeDefinition`] node definition to use the name from there instead.
|
||||
/// The name of the node definition, as originally set by [`DocumentNodeDefinition`], used to display in the UI and to display the appropriate properties.
|
||||
#[serde(deserialize_with = "migrate_layer_to_merge")]
|
||||
pub name: String,
|
||||
/// The inputs to a node, which are either:
|
||||
/// - From other nodes within this graph [`NodeInput::Node`],
|
||||
@@ -467,6 +476,7 @@ pub enum DocumentNodeImplementation {
|
||||
/// This describes a (document) node implemented as a proto node.
|
||||
///
|
||||
/// A proto node identifier which can be found in `node_registry.rs`.
|
||||
#[serde(alias = "Unresolved")] // TODO: Eventually remove this alias (probably starting late 2024)
|
||||
ProtoNode(ProtoNodeIdentifier),
|
||||
/// The Extract variant is a tag which tells the compilation process to do something special. It invokes language-level functionality built for use by the ExtractNode to enable metaprogramming.
|
||||
/// When the ExtractNode is compiled, it gets replaced by a value node containing a representation of the source code for the function/lambda of the document node that's fed into the ExtractNode
|
||||
@@ -536,12 +546,15 @@ pub struct NodeNetwork {
|
||||
/// The list of nodes that are imported into this network from the parent network.
|
||||
/// Each import is a reference to which node within this network that the input is connected to.
|
||||
/// Presently, only one is supported— use an Identity node to split an input to multiple user nodes (although this could, and should, be changed in the future).
|
||||
#[serde(alias = "inputs")] // TODO: Eventually remove this alias (probably starting late 2024)
|
||||
pub imports: Vec<NodeId>,
|
||||
/// The list of data outputs that are exported from this network to the parent network.
|
||||
/// Each export is a reference to a node within this network, paired with its output index, that is the source of the network's exported data.
|
||||
#[serde(alias = "outputs")] // TODO: Eventually remove this alias (probably starting late 2024)
|
||||
pub exports: Vec<NodeOutput>,
|
||||
/// The list of all nodes in this network.
|
||||
pub nodes: HashMap<NodeId, DocumentNode>,
|
||||
#[serde(skip)]
|
||||
/// In the case when another node is previewed (chosen by the user as a temporary output), this stores what it previously was so it can be restored later.
|
||||
pub previous_outputs: Option<Vec<NodeOutput>>,
|
||||
}
|
||||
@@ -1536,7 +1549,7 @@ mod test {
|
||||
NodeId(1),
|
||||
DocumentNode {
|
||||
name: "Nested network".into(),
|
||||
inputs: vec![NodeInput::value(TaggedValue::F32(1.), false), NodeInput::value(TaggedValue::F32(2.), false)],
|
||||
inputs: vec![NodeInput::value(TaggedValue::F64(1.), false), NodeInput::value(TaggedValue::F64(2.), false)],
|
||||
implementation: DocumentNodeImplementation::Network(two_node_identity()),
|
||||
..Default::default()
|
||||
},
|
||||
|
||||
@@ -21,7 +21,7 @@ pub enum TaggedValue {
|
||||
String(String),
|
||||
U32(u32),
|
||||
U64(u64),
|
||||
F32(f32),
|
||||
#[serde(alias = "F32")] // TODO: Eventually remove this alias (probably starting late 2024)
|
||||
F64(f64),
|
||||
Bool(bool),
|
||||
UVec2(UVec2),
|
||||
@@ -43,6 +43,7 @@ pub enum TaggedValue {
|
||||
Fill(graphene_core::vector::style::Fill),
|
||||
Stroke(graphene_core::vector::style::Stroke),
|
||||
F64Array4([f64; 4]),
|
||||
#[serde(alias = "VecF32")] // TODO: Eventually remove this alias (probably starting late 2024)
|
||||
VecF64(Vec<f64>),
|
||||
VecDVec2(Vec<DVec2>),
|
||||
RedGreenBlue(graphene_core::raster::RedGreenBlue),
|
||||
@@ -87,7 +88,6 @@ impl Hash for TaggedValue {
|
||||
Self::String(x) => x.hash(state),
|
||||
Self::U32(x) => x.hash(state),
|
||||
Self::U64(x) => x.hash(state),
|
||||
Self::F32(x) => x.to_bits().hash(state),
|
||||
Self::F64(x) => x.to_bits().hash(state),
|
||||
Self::Bool(x) => x.hash(state),
|
||||
Self::UVec2(x) => x.to_array().iter().for_each(|x| x.hash(state)),
|
||||
@@ -168,7 +168,6 @@ impl<'a> TaggedValue {
|
||||
TaggedValue::String(x) => Box::new(x),
|
||||
TaggedValue::U32(x) => Box::new(x),
|
||||
TaggedValue::U64(x) => Box::new(x),
|
||||
TaggedValue::F32(x) => Box::new(x),
|
||||
TaggedValue::F64(x) => Box::new(x),
|
||||
TaggedValue::Bool(x) => Box::new(x),
|
||||
TaggedValue::UVec2(x) => Box::new(x),
|
||||
@@ -232,7 +231,6 @@ impl<'a> TaggedValue {
|
||||
TaggedValue::String(x) => format!("\"{x}\""),
|
||||
TaggedValue::U32(x) => x.to_string() + "_u32",
|
||||
TaggedValue::U64(x) => x.to_string() + "_u64",
|
||||
TaggedValue::F32(x) => x.to_string() + "_f32",
|
||||
TaggedValue::F64(x) => x.to_string() + "_f64",
|
||||
TaggedValue::Bool(x) => x.to_string(),
|
||||
TaggedValue::BlendMode(x) => "BlendMode::".to_string() + &x.to_string(),
|
||||
@@ -247,7 +245,6 @@ impl<'a> TaggedValue {
|
||||
TaggedValue::String(_) => concrete!(String),
|
||||
TaggedValue::U32(_) => concrete!(u32),
|
||||
TaggedValue::U64(_) => concrete!(u64),
|
||||
TaggedValue::F32(_) => concrete!(f32),
|
||||
TaggedValue::F64(_) => concrete!(f64),
|
||||
TaggedValue::Bool(_) => concrete!(bool),
|
||||
TaggedValue::UVec2(_) => concrete!(UVec2),
|
||||
@@ -314,7 +311,6 @@ impl<'a> TaggedValue {
|
||||
x if x == TypeId::of::<String>() => Ok(TaggedValue::String(*downcast(input).unwrap())),
|
||||
x if x == TypeId::of::<u32>() => Ok(TaggedValue::U32(*downcast(input).unwrap())),
|
||||
x if x == TypeId::of::<u64>() => Ok(TaggedValue::U64(*downcast(input).unwrap())),
|
||||
x if x == TypeId::of::<f32>() => Ok(TaggedValue::F32(*downcast(input).unwrap())),
|
||||
x if x == TypeId::of::<f64>() => Ok(TaggedValue::F64(*downcast(input).unwrap())),
|
||||
x if x == TypeId::of::<bool>() => Ok(TaggedValue::Bool(*downcast(input).unwrap())),
|
||||
x if x == TypeId::of::<UVec2>() => Ok(TaggedValue::UVec2(*downcast(input).unwrap())),
|
||||
|
||||
@@ -421,7 +421,7 @@ pub struct BlendGpuImageNode<Background, B, O> {
|
||||
}
|
||||
|
||||
#[node_macro::node_fn(BlendGpuImageNode)]
|
||||
async fn blend_gpu_image(foreground: ImageFrame<Color>, background: ImageFrame<Color>, blend_mode: BlendMode, opacity: f32) -> ImageFrame<Color> {
|
||||
async fn blend_gpu_image(foreground: ImageFrame<Color>, background: ImageFrame<Color>, blend_mode: BlendMode, opacity: f64) -> ImageFrame<Color> {
|
||||
let foreground_size = DVec2::new(foreground.image.width as f64, foreground.image.height as f64);
|
||||
let background_size = DVec2::new(background.image.width as f64, background.image.height as f64);
|
||||
// Transforms a point from the background image to the foreground image
|
||||
@@ -457,7 +457,7 @@ async fn blend_gpu_image(foreground: ImageFrame<Color>, background: ImageFrame<C
|
||||
i1[(_global_index.y * i0 + _global_index.x) as usize],
|
||||
))"#,
|
||||
TaggedValue::BlendMode(blend_mode).to_primitive_string(),
|
||||
TaggedValue::F32(opacity).to_primitive_string(),
|
||||
TaggedValue::F64(opacity).to_primitive_string(),
|
||||
),
|
||||
concrete![Color],
|
||||
))],
|
||||
|
||||
@@ -419,7 +419,7 @@ fn node_registry() -> HashMap<ProtoNodeIdentifier, HashMap<NodeIOTypes, NodeCons
|
||||
Box::pin(async move {
|
||||
let background: DowncastBothNode<(), ImageFrame<Color>> = DowncastBothNode::new(args[0].clone());
|
||||
let blend_mode: DowncastBothNode<(), BlendMode> = DowncastBothNode::new(args[1].clone());
|
||||
let opacity: DowncastBothNode<(), f32> = DowncastBothNode::new(args[2].clone());
|
||||
let opacity: DowncastBothNode<(), f64> = DowncastBothNode::new(args[2].clone());
|
||||
let node = graphene_std::gpu_nodes::BlendGpuImageNode::new(background, blend_mode, opacity);
|
||||
let any: DynAnyNode<ImageFrame<Color>, _, _> = graphene_std::any::DynAnyNode::new(node);
|
||||
|
||||
@@ -429,7 +429,7 @@ fn node_registry() -> HashMap<ProtoNodeIdentifier, HashMap<NodeIOTypes, NodeCons
|
||||
NodeIOTypes::new(
|
||||
concrete!(ImageFrame<Color>),
|
||||
concrete!(ImageFrame<Color>),
|
||||
vec![fn_type!(ImageFrame<Color>), fn_type!(BlendMode), fn_type!(f32)],
|
||||
vec![fn_type!(ImageFrame<Color>), fn_type!(BlendMode), fn_type!(f64)],
|
||||
),
|
||||
)],
|
||||
vec![(
|
||||
|
||||
Reference in New Issue
Block a user