mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-18 18:38:05 +08:00
Add some additional image effect nodes (#869)
* Move the Subpath type to graphene-std * Add the transform subpath node * Delete selected nodes * Inserting node list on right click * Add several bitmap manipulator nodes * Convert add node to use f64 * Add posterize node * Rename names randomly * Fix naming * Exposure node * Fix typo * Adjust exposure node range * Comment out vector nodes * Adjust exposure range again * Posterise as ints * Rename input * Use >= in the to hsl function
This commit is contained in:
@@ -58,6 +58,7 @@ impl DocumentNode {
|
||||
}
|
||||
|
||||
fn resolve_proto_node(mut self) -> ProtoNode {
|
||||
assert_ne!(self.inputs.len(), 0, "Resolving document node {:#?} with no inputs", self);
|
||||
let first = self.inputs.remove(0);
|
||||
if let DocumentNodeImplementation::Unresolved(fqn) = self.implementation {
|
||||
let (input, mut args) = match first {
|
||||
@@ -102,6 +103,9 @@ pub enum NodeInput {
|
||||
}
|
||||
|
||||
impl NodeInput {
|
||||
pub const fn value(tagged_value: value::TaggedValue, exposed: bool) -> Self {
|
||||
Self::Value { tagged_value, exposed }
|
||||
}
|
||||
fn map_ids(&mut self, f: impl Fn(NodeId) -> NodeId) {
|
||||
if let NodeInput::Node(id) = self {
|
||||
*self = NodeInput::Node(f(*id))
|
||||
|
||||
@@ -1,28 +1,40 @@
|
||||
use dyn_any::StaticType;
|
||||
|
||||
use dyn_clone::DynClone;
|
||||
|
||||
use dyn_any::{DynAny, Upcast};
|
||||
use dyn_clone::DynClone;
|
||||
use glam::DVec2;
|
||||
use std::sync::Arc;
|
||||
|
||||
/// A type that is known, allowing serialization (serde::Deserialize is not object safe)
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
pub enum TaggedValue {
|
||||
None,
|
||||
String(String),
|
||||
U32(u32),
|
||||
F32(f32),
|
||||
F64(f64),
|
||||
Bool(bool),
|
||||
DVec2(DVec2),
|
||||
Image(graphene_std::raster::Image),
|
||||
Color(graphene_core::raster::color::Color),
|
||||
Subpath(graphene_std::vector::subpath::Subpath),
|
||||
RcSubpath(Arc<graphene_std::vector::subpath::Subpath>),
|
||||
}
|
||||
|
||||
impl TaggedValue {
|
||||
pub fn to_value(self) -> Value {
|
||||
match self {
|
||||
TaggedValue::None => Box::new(()),
|
||||
TaggedValue::String(x) => Box::new(x),
|
||||
TaggedValue::U32(x) => Box::new(x),
|
||||
TaggedValue::F32(x) => Box::new(x),
|
||||
TaggedValue::F64(x) => Box::new(x),
|
||||
TaggedValue::Bool(x) => Box::new(x),
|
||||
TaggedValue::DVec2(x) => Box::new(x),
|
||||
TaggedValue::Image(x) => Box::new(x),
|
||||
TaggedValue::Color(x) => Box::new(x),
|
||||
TaggedValue::Subpath(x) => Box::new(x),
|
||||
TaggedValue::RcSubpath(x) => Box::new(x),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
use std::borrow::Cow;
|
||||
|
||||
use borrow_stack::FixedSizeStack;
|
||||
use glam::DVec2;
|
||||
use graphene_core::generic::FnNode;
|
||||
use graphene_core::ops::AddNode;
|
||||
use graphene_core::raster::color::Color;
|
||||
@@ -9,6 +10,7 @@ use graphene_core::Node;
|
||||
use graphene_std::any::DowncastBothNode;
|
||||
use graphene_std::any::{Any, DowncastNode, DynAnyNode, IntoTypeErasedNode, TypeErasedNode};
|
||||
use graphene_std::raster::Image;
|
||||
use graphene_std::vector::subpath::Subpath;
|
||||
|
||||
use crate::proto::Type;
|
||||
use crate::proto::{ConstructionArgs, NodeIdentifier, ProtoNode, ProtoNodeInput, Type::Concrete};
|
||||
@@ -48,8 +50,8 @@ static NODE_REGISTRY: &[(NodeIdentifier, NodeConstructor)] = &[
|
||||
stack.push_fn(move |nodes| {
|
||||
let ConstructionArgs::Nodes(construction_nodes) = proto_node.construction_args else { unreachable!("Add Node constructed with out rhs input node") };
|
||||
let value_node = nodes.get(construction_nodes[0] as usize).unwrap();
|
||||
let input_node: DowncastBothNode<_, (), f32> = DowncastBothNode::new(value_node);
|
||||
let node: DynAnyNode<_, f32, _, _> = DynAnyNode::new(ConsNode::new(input_node).then(graphene_core::ops::AddNode));
|
||||
let input_node: DowncastBothNode<_, (), f64> = DowncastBothNode::new(value_node);
|
||||
let node: DynAnyNode<_, f64, _, _> = DynAnyNode::new(ConsNode::new(input_node).then(graphene_core::ops::AddNode));
|
||||
|
||||
if let ProtoNodeInput::Node(node_id) = proto_node.input {
|
||||
let pre_node = nodes.get(node_id as usize).unwrap();
|
||||
@@ -297,14 +299,28 @@ static NODE_REGISTRY: &[(NodeIdentifier, NodeConstructor)] = &[
|
||||
}
|
||||
})
|
||||
}),
|
||||
(NodeIdentifier::new("graphene_std::raster::InvertImageColorNode", &[]), |proto_node, stack| {
|
||||
stack.push_fn(move |nodes| {
|
||||
let node = DynAnyNode::new(graphene_std::raster::InvertImageColorNode);
|
||||
|
||||
if let ProtoNodeInput::Node(node_id) = proto_node.input {
|
||||
let pre_node = nodes.get(node_id as usize).unwrap();
|
||||
(pre_node).then(node).into_type_erased()
|
||||
} else {
|
||||
node.into_type_erased()
|
||||
}
|
||||
})
|
||||
}),
|
||||
(
|
||||
NodeIdentifier::new("graphene_std::raster::HueShiftImage", &[Type::Concrete(Cow::Borrowed("&TypeErasedNode"))]),
|
||||
NodeIdentifier::new("graphene_std::raster::ShiftImageHSLNode", &[Type::Concrete(Cow::Borrowed("&TypeErasedNode"))]),
|
||||
|proto_node, stack| {
|
||||
stack.push_fn(move |nodes| {
|
||||
let ConstructionArgs::Nodes(construction_nodes) = proto_node.construction_args else { unreachable!("Hue Shift Image Node constructed with out shift input node") };
|
||||
let value_node = nodes.get(construction_nodes[0] as usize).unwrap();
|
||||
let input_node: DowncastBothNode<_, (), f32> = DowncastBothNode::new(value_node);
|
||||
let node = DynAnyNode::new(graphene_std::raster::HueShiftImage::new(input_node));
|
||||
let ConstructionArgs::Nodes(construction_nodes) = proto_node.construction_args else { unreachable!("ShiftImageHSLNode Node constructed without inputs") };
|
||||
|
||||
let hue: DowncastBothNode<_, (), f64> = DowncastBothNode::new(nodes.get(construction_nodes[0] as usize).unwrap());
|
||||
let saturation: DowncastBothNode<_, (), f64> = DowncastBothNode::new(nodes.get(construction_nodes[1] as usize).unwrap());
|
||||
let luminance: DowncastBothNode<_, (), f64> = DowncastBothNode::new(nodes.get(construction_nodes[2] as usize).unwrap());
|
||||
let node = DynAnyNode::new(graphene_std::raster::ShiftImageHSLNode::new(hue, saturation, luminance));
|
||||
|
||||
if let ProtoNodeInput::Node(node_id) = proto_node.input {
|
||||
let pre_node = nodes.get(node_id as usize).unwrap();
|
||||
@@ -316,13 +332,82 @@ static NODE_REGISTRY: &[(NodeIdentifier, NodeConstructor)] = &[
|
||||
},
|
||||
),
|
||||
(
|
||||
NodeIdentifier::new("graphene_std::raster::BrightenImageNode", &[Type::Concrete(Cow::Borrowed("&TypeErasedNode"))]),
|
||||
NodeIdentifier::new("graphene_std::raster::ImageBrightnessAndContrast", &[Type::Concrete(Cow::Borrowed("&TypeErasedNode"))]),
|
||||
|proto_node, stack| {
|
||||
stack.push_fn(move |nodes| {
|
||||
let ConstructionArgs::Nodes(construction_nodes) = proto_node.construction_args else { unreachable!("Brighten Image Node constructed with out brighten input node") };
|
||||
let value_node = nodes.get(construction_nodes[0] as usize).unwrap();
|
||||
let input_node: DowncastBothNode<_, (), f32> = DowncastBothNode::new(value_node);
|
||||
let node = DynAnyNode::new(graphene_std::raster::BrightenImageNode::new(input_node));
|
||||
let ConstructionArgs::Nodes(construction_nodes) = proto_node.construction_args else { unreachable!("ImageBrightnessAndContrast Node constructed without inputs") };
|
||||
|
||||
let brightness: DowncastBothNode<_, (), f64> = DowncastBothNode::new(nodes.get(construction_nodes[0] as usize).unwrap());
|
||||
let contrast: DowncastBothNode<_, (), f64> = DowncastBothNode::new(nodes.get(construction_nodes[1] as usize).unwrap());
|
||||
let node = DynAnyNode::new(graphene_std::raster::ImageBrightnessAndContrast::new(brightness, contrast));
|
||||
|
||||
if let ProtoNodeInput::Node(node_id) = proto_node.input {
|
||||
let pre_node = nodes.get(node_id as usize).unwrap();
|
||||
(pre_node).then(node).into_type_erased()
|
||||
} else {
|
||||
node.into_type_erased()
|
||||
}
|
||||
})
|
||||
},
|
||||
),
|
||||
(
|
||||
NodeIdentifier::new("graphene_std::raster::ImageGammaNode", &[Type::Concrete(Cow::Borrowed("&TypeErasedNode"))]),
|
||||
|proto_node, stack| {
|
||||
stack.push_fn(move |nodes| {
|
||||
let ConstructionArgs::Nodes(construction_nodes) = proto_node.construction_args else { unreachable!("ImageGammaNode Node constructed without inputs") };
|
||||
let gamma: DowncastBothNode<_, (), f64> = DowncastBothNode::new(nodes.get(construction_nodes[0] as usize).unwrap());
|
||||
let node = DynAnyNode::new(graphene_std::raster::ImageGammaNode::new(gamma));
|
||||
|
||||
if let ProtoNodeInput::Node(node_id) = proto_node.input {
|
||||
let pre_node = nodes.get(node_id as usize).unwrap();
|
||||
(pre_node).then(node).into_type_erased()
|
||||
} else {
|
||||
node.into_type_erased()
|
||||
}
|
||||
})
|
||||
},
|
||||
),
|
||||
(
|
||||
NodeIdentifier::new("graphene_std::raster::ImageOpacityNode", &[Type::Concrete(Cow::Borrowed("&TypeErasedNode"))]),
|
||||
|proto_node, stack| {
|
||||
stack.push_fn(move |nodes| {
|
||||
let ConstructionArgs::Nodes(construction_nodes) = proto_node.construction_args else { unreachable!("ImageOpacityNode Node constructed without inputs") };
|
||||
let opacity: DowncastBothNode<_, (), f64> = DowncastBothNode::new(nodes.get(construction_nodes[0] as usize).unwrap());
|
||||
let node = DynAnyNode::new(graphene_std::raster::ImageOpacityNode::new(opacity));
|
||||
|
||||
if let ProtoNodeInput::Node(node_id) = proto_node.input {
|
||||
let pre_node = nodes.get(node_id as usize).unwrap();
|
||||
(pre_node).then(node).into_type_erased()
|
||||
} else {
|
||||
node.into_type_erased()
|
||||
}
|
||||
})
|
||||
},
|
||||
),
|
||||
(
|
||||
NodeIdentifier::new("graphene_std::raster::Posterize", &[Type::Concrete(Cow::Borrowed("&TypeErasedNode"))]),
|
||||
|proto_node, stack| {
|
||||
stack.push_fn(move |nodes| {
|
||||
let ConstructionArgs::Nodes(construction_nodes) = proto_node.construction_args else { unreachable!("Posterize Node constructed without inputs") };
|
||||
let value: DowncastBothNode<_, (), f64> = DowncastBothNode::new(nodes.get(construction_nodes[0] as usize).unwrap());
|
||||
let node = DynAnyNode::new(graphene_std::raster::Posterize::new(value));
|
||||
|
||||
if let ProtoNodeInput::Node(node_id) = proto_node.input {
|
||||
let pre_node = nodes.get(node_id as usize).unwrap();
|
||||
(pre_node).then(node).into_type_erased()
|
||||
} else {
|
||||
node.into_type_erased()
|
||||
}
|
||||
})
|
||||
},
|
||||
),
|
||||
(
|
||||
NodeIdentifier::new("graphene_std::raster::ExposureNode", &[Type::Concrete(Cow::Borrowed("&TypeErasedNode"))]),
|
||||
|proto_node, stack| {
|
||||
stack.push_fn(move |nodes| {
|
||||
let ConstructionArgs::Nodes(construction_nodes) = proto_node.construction_args else { unreachable!("ExposureNode constructed without inputs") };
|
||||
let value: DowncastBothNode<_, (), f64> = DowncastBothNode::new(nodes.get(construction_nodes[0] as usize).unwrap());
|
||||
let node = DynAnyNode::new(graphene_std::raster::ExposureNode::new(value));
|
||||
|
||||
if let ProtoNodeInput::Node(node_id) = proto_node.input {
|
||||
let pre_node = nodes.get(node_id as usize).unwrap();
|
||||
@@ -378,6 +463,45 @@ static NODE_REGISTRY: &[(NodeIdentifier, NodeConstructor)] = &[
|
||||
}
|
||||
},
|
||||
),
|
||||
(NodeIdentifier::new("graphene_std::vector::generator_nodes::UnitCircleGenerator", &[]), |_proto_node, stack| {
|
||||
stack.push_fn(|_nodes| DynAnyNode::new(graphene_std::vector::generator_nodes::UnitCircleGenerator).into_type_erased())
|
||||
}),
|
||||
(NodeIdentifier::new("graphene_std::vector::generator_nodes::UnitSquareGenerator", &[]), |_proto_node, stack| {
|
||||
stack.push_fn(|_nodes| DynAnyNode::new(graphene_std::vector::generator_nodes::UnitSquareGenerator).into_type_erased())
|
||||
}),
|
||||
(NodeIdentifier::new("graphene_std::vector::generator_nodes::BlitSubpath", &[]), |proto_node, stack| {
|
||||
stack.push_fn(move |nodes| {
|
||||
let ConstructionArgs::Nodes(construction_nodes) = proto_node.construction_args else { unreachable!("BlitSubpath without subpath input") };
|
||||
let value_node = nodes.get(construction_nodes[0] as usize).unwrap();
|
||||
let input_node: DowncastBothNode<_, (), Subpath> = DowncastBothNode::new(value_node);
|
||||
let node = DynAnyNode::new(graphene_std::vector::generator_nodes::BlitSubpath::new(input_node));
|
||||
|
||||
if let ProtoNodeInput::Node(node_id) = proto_node.input {
|
||||
let pre_node = nodes.get(node_id as usize).unwrap();
|
||||
(pre_node).then(node).into_type_erased()
|
||||
} else {
|
||||
node.into_type_erased()
|
||||
}
|
||||
})
|
||||
}),
|
||||
(NodeIdentifier::new("graphene_std::vector::generator_nodes::TransformSubpathNode", &[]), |proto_node, stack| {
|
||||
stack.push_fn(move |nodes| {
|
||||
let ConstructionArgs::Nodes(construction_nodes) = proto_node.construction_args else { unreachable!("TransformSubpathNode without subpath input") };
|
||||
let translate_node: DowncastBothNode<_, (), DVec2> = DowncastBothNode::new(nodes.get(construction_nodes[0] as usize).unwrap());
|
||||
let rotate_node: DowncastBothNode<_, (), f64> = DowncastBothNode::new(nodes.get(construction_nodes[1] as usize).unwrap());
|
||||
let scale_node: DowncastBothNode<_, (), DVec2> = DowncastBothNode::new(nodes.get(construction_nodes[2] as usize).unwrap());
|
||||
let shear_node: DowncastBothNode<_, (), DVec2> = DowncastBothNode::new(nodes.get(construction_nodes[3] as usize).unwrap());
|
||||
|
||||
let node = DynAnyNode::new(graphene_std::vector::generator_nodes::TransformSubpathNode::new(translate_node, rotate_node, scale_node, shear_node));
|
||||
|
||||
if let ProtoNodeInput::Node(node_id) = proto_node.input {
|
||||
let pre_node = nodes.get(node_id as usize).unwrap();
|
||||
(pre_node).then(node).into_type_erased()
|
||||
} else {
|
||||
node.into_type_erased()
|
||||
}
|
||||
})
|
||||
}),
|
||||
];
|
||||
|
||||
pub fn push_node(proto_node: ProtoNode, stack: &FixedSizeStack<TypeErasedNode<'static>>) {
|
||||
|
||||
Reference in New Issue
Block a user