mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-27 15:38:11 +08:00
Add some raster protonodes
This commit is contained in:
Generated
+8
@@ -235,6 +235,12 @@ dependencies = [
|
|||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "dyn-clone"
|
||||||
|
version = "1.0.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4f94fa09c2aeea5b8839e414b7b841bf429fd25b9c522116ac97ee87856d88b2"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "either"
|
name = "either"
|
||||||
version = "1.8.0"
|
version = "1.8.0"
|
||||||
@@ -349,6 +355,7 @@ version = "0.1.0"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"borrow_stack",
|
"borrow_stack",
|
||||||
"dyn-any",
|
"dyn-any",
|
||||||
|
"dyn-clone",
|
||||||
"graphene-core",
|
"graphene-core",
|
||||||
"graphene-std",
|
"graphene-std",
|
||||||
"num-traits",
|
"num-traits",
|
||||||
@@ -381,6 +388,7 @@ version = "0.1.0"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"borrow_stack",
|
"borrow_stack",
|
||||||
"dyn-any",
|
"dyn-any",
|
||||||
|
"dyn-clone",
|
||||||
"graph-proc-macros",
|
"graph-proc-macros",
|
||||||
"graphene-core",
|
"graphene-core",
|
||||||
"image",
|
"image",
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
use dyn_any::{DynAny, StaticType};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
/// Structure that represents a color.
|
/// Structure that represents a color.
|
||||||
@@ -5,7 +6,7 @@ use serde::{Deserialize, Serialize};
|
|||||||
/// The other components (RGB) are stored as `f32` that range from `0.0` up to `f32::MAX`,
|
/// The other components (RGB) are stored as `f32` that range from `0.0` up to `f32::MAX`,
|
||||||
/// the values encode the brightness of each channel proportional to the light intensity in cd/m² (nits) in HDR, and `0.0` (black) to `1.0` (white) in SDR color.
|
/// the values encode the brightness of each channel proportional to the light intensity in cd/m² (nits) in HDR, and `0.0` (black) to `1.0` (white) in SDR color.
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Default, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Copy, PartialEq, Default, Serialize, Deserialize, DynAny)]
|
||||||
pub struct Color {
|
pub struct Color {
|
||||||
red: f32,
|
red: f32,
|
||||||
green: f32,
|
green: f32,
|
||||||
|
|||||||
@@ -12,3 +12,4 @@ graphene-std = { path = "../gstd" }
|
|||||||
dyn-any = { path = "../../libraries/dyn-any" }
|
dyn-any = { path = "../../libraries/dyn-any" }
|
||||||
num-traits = "0.2"
|
num-traits = "0.2"
|
||||||
borrow_stack = { path = "../borrow_stack" }
|
borrow_stack = { path = "../borrow_stack" }
|
||||||
|
dyn-clone = "1.0"
|
||||||
|
|||||||
@@ -4,8 +4,11 @@ use borrow_stack::FixedSizeStack;
|
|||||||
use dyn_clone::DynClone;
|
use dyn_clone::DynClone;
|
||||||
use graphene_core::generic::FnNode;
|
use graphene_core::generic::FnNode;
|
||||||
use graphene_core::ops::{AddNode, IdNode};
|
use graphene_core::ops::{AddNode, IdNode};
|
||||||
|
use graphene_core::raster::color::Color;
|
||||||
use graphene_core::structural::{ConsNode, Then};
|
use graphene_core::structural::{ConsNode, Then};
|
||||||
use graphene_core::{AsRefNode, Node};
|
use graphene_core::{AsRefNode, Node};
|
||||||
|
use graphene_std::any::DowncastBothNode;
|
||||||
|
use graphene_std::raster::Image;
|
||||||
use graphene_std::{
|
use graphene_std::{
|
||||||
any::{Any, DowncastNode, DynAnyNode, IntoTypeErasedNode, TypeErasedNode},
|
any::{Any, DowncastNode, DynAnyNode, IntoTypeErasedNode, TypeErasedNode},
|
||||||
document::{ConstructionArgs, ProtoNode, ProtoNodeInput},
|
document::{ConstructionArgs, ProtoNode, ProtoNodeInput},
|
||||||
@@ -52,12 +55,19 @@ static NODE_REGISTRY: &[(NodeIdentifier, fn(ProtoNode, &FixedSizeStack<TypeErase
|
|||||||
},
|
},
|
||||||
|proto_node, stack| {
|
|proto_node, stack| {
|
||||||
let node_id = proto_node.input.unwrap_node() as usize;
|
let node_id = proto_node.input.unwrap_node() as usize;
|
||||||
stack.push_fn(move |nodes| {
|
if let ConstructionArgs::Nodes(cons_node_arg) = proto_node.construction_args {
|
||||||
let pre_node = nodes.get(node_id).unwrap();
|
stack.push_fn(move |nodes| {
|
||||||
let downcast: DowncastNode<_, &u32> = DowncastNode::new(pre_node);
|
let pre_node = nodes.get(node_id).unwrap();
|
||||||
let dynanynode: DynAnyNode<_, u32, _, _> = DynAnyNode::new(ConsNode(downcast, PhantomData));
|
let cons_node_arg = nodes.get(cons_node_arg[0] as usize).unwrap();
|
||||||
dynanynode.into_box()
|
|
||||||
})
|
let cons_node = ConsNode::new(DowncastNode::<_, u32>::new(cons_node_arg));
|
||||||
|
let node: DynAnyNode<_, u32, _, _> = DynAnyNode::new(cons_node);
|
||||||
|
let node = (pre_node).then(node);
|
||||||
|
node.into_type_erased()
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
@@ -79,7 +89,7 @@ static NODE_REGISTRY: &[(NodeIdentifier, fn(ProtoNode, &FixedSizeStack<TypeErase
|
|||||||
types: &["Any<'n>"],
|
types: &["Any<'n>"],
|
||||||
},
|
},
|
||||||
|proto_node, stack| {
|
|proto_node, stack| {
|
||||||
stack.push_fn(|nodes| {
|
stack.push_fn(|_nodes| {
|
||||||
if let ConstructionArgs::Value(value) = proto_node.construction_args {
|
if let ConstructionArgs::Value(value) = proto_node.construction_args {
|
||||||
let node = FnNode::new(move |_| value.clone() as Any<'static>);
|
let node = FnNode::new(move |_| value.clone() as Any<'static>);
|
||||||
node.into_type_erased()
|
node.into_type_erased()
|
||||||
@@ -89,16 +99,199 @@ static NODE_REGISTRY: &[(NodeIdentifier, fn(ProtoNode, &FixedSizeStack<TypeErase
|
|||||||
})
|
})
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
(
|
||||||
|
NodeIdentifier {
|
||||||
|
name: "graphene_core::raster::GrayscaleNode",
|
||||||
|
types: &["Color"],
|
||||||
|
},
|
||||||
|
|proto_node, stack| {
|
||||||
|
stack.push_fn(|nodes| {
|
||||||
|
let node = DynAnyNode::new(graphene_core::raster::GrayscaleNode);
|
||||||
|
|
||||||
|
if let ProtoNodeInput::Node(pre_id) = proto_node.input {
|
||||||
|
let pre_node = nodes.get(pre_id as usize).unwrap();
|
||||||
|
(pre_node).then(node).into_type_erased()
|
||||||
|
} else {
|
||||||
|
node.into_type_erased()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
),
|
||||||
|
(
|
||||||
|
NodeIdentifier {
|
||||||
|
name: "graphene_std::raster::MapImageNode",
|
||||||
|
types: &["Image"],
|
||||||
|
},
|
||||||
|
|proto_node, stack| {
|
||||||
|
let node_id = proto_node.input.unwrap_node() as usize;
|
||||||
|
if let ConstructionArgs::Nodes(operation_node_id) = proto_node.construction_args {
|
||||||
|
stack.push_fn(move |nodes| {
|
||||||
|
let pre_node = nodes.get(node_id).unwrap();
|
||||||
|
|
||||||
|
let operation_node = nodes.get(operation_node_id[0] as usize).unwrap();
|
||||||
|
let operation_node: DowncastBothNode<_, Color, Color> = DowncastBothNode::new(operation_node);
|
||||||
|
let map_node = DynAnyNode::new(graphene_std::raster::MapImageNode::new(operation_node));
|
||||||
|
|
||||||
|
let node = (pre_node).then(map_node);
|
||||||
|
|
||||||
|
node.into_type_erased()
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
(
|
||||||
|
NodeIdentifier {
|
||||||
|
name: "graphene_std::raster::image_node",
|
||||||
|
types: &["&str"],
|
||||||
|
},
|
||||||
|
|_proto_node, stack| {
|
||||||
|
stack.push_fn(|_nodes| {
|
||||||
|
let image = FnNode::new(|s: &str| graphene_std::raster::image_node::<&str>().eval(s).unwrap());
|
||||||
|
let node: DynAnyNode<_, &str, _, _> = DynAnyNode::new(image);
|
||||||
|
node.into_type_erased()
|
||||||
|
})
|
||||||
|
},
|
||||||
|
),
|
||||||
|
(
|
||||||
|
NodeIdentifier {
|
||||||
|
name: "graphene_std::raster::export_image_node",
|
||||||
|
types: &["Image", "&str"],
|
||||||
|
},
|
||||||
|
|proto_node, stack| {
|
||||||
|
stack.push_fn(|nodes| {
|
||||||
|
let pre_node = nodes.get(proto_node.input.unwrap_node() as usize).unwrap();
|
||||||
|
|
||||||
|
let image = FnNode::new(|input: (Image, &str)| graphene_std::raster::export_image_node().eval(input).unwrap());
|
||||||
|
let node: DynAnyNode<_, (Image, &str), _, _> = DynAnyNode::new(image);
|
||||||
|
let node = (pre_node).then(node);
|
||||||
|
node.into_type_erased()
|
||||||
|
})
|
||||||
|
},
|
||||||
|
),
|
||||||
|
(
|
||||||
|
NodeIdentifier {
|
||||||
|
name: "graphene_core::structural::ConsNode",
|
||||||
|
types: &["Image", "&str"],
|
||||||
|
},
|
||||||
|
|proto_node, stack| {
|
||||||
|
let node_id = proto_node.input.unwrap_node() as usize;
|
||||||
|
if let ConstructionArgs::Nodes(cons_node_arg) = proto_node.construction_args {
|
||||||
|
stack.push_fn(move |nodes| {
|
||||||
|
let pre_node = nodes.get(node_id).unwrap();
|
||||||
|
let cons_node_arg = nodes.get(cons_node_arg[0] as usize).unwrap();
|
||||||
|
|
||||||
|
let cons_node = ConsNode::new(DowncastNode::<_, &str>::new(cons_node_arg));
|
||||||
|
let node: DynAnyNode<_, Image, _, _> = DynAnyNode::new(cons_node);
|
||||||
|
let node = (pre_node).then(node);
|
||||||
|
node.into_type_erased()
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
];
|
];
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod protograph_testing {
|
||||||
|
use borrow_stack::BorrowStack;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
/*#[test]
|
/// Lookup a node by th suffix of the name (for testing only)
|
||||||
fn test() {
|
fn simple_lookup(suffix: &str) -> &(NodeIdentifier, fn(ProtoNode, &FixedSizeStack<TypeErasedNode<'static>>)) {
|
||||||
let nodes = [TypeErasedNode(Box::new(42u32))];
|
NODE_REGISTRY.iter().find(|node| node.0.name.ends_with(suffix)).unwrap()
|
||||||
let node = NODE_REGISTRY[0].1(node, &nodes);
|
}
|
||||||
assert_eq!(node.eval(()), 42);
|
|
||||||
}*/
|
#[test]
|
||||||
|
fn add_values() {
|
||||||
|
let stack = FixedSizeStack::new(256);
|
||||||
|
let val_1_protonode = ProtoNode::value("name".to_string(), ConstructionArgs::Value(Box::new(2u32)));
|
||||||
|
simple_lookup("ValueNode").1(val_1_protonode, &stack);
|
||||||
|
|
||||||
|
let val_2_protonode = ProtoNode::value("name".to_string(), ConstructionArgs::Value(Box::new(40u32)));
|
||||||
|
simple_lookup("ValueNode").1(val_2_protonode, &stack);
|
||||||
|
|
||||||
|
let cons_protonode = ProtoNode {
|
||||||
|
construction_args: ConstructionArgs::Nodes(vec![1]),
|
||||||
|
input: ProtoNodeInput::Node(0),
|
||||||
|
name: "todo!()".to_string(),
|
||||||
|
};
|
||||||
|
simple_lookup("ConsNode").1(cons_protonode, &stack);
|
||||||
|
|
||||||
|
let add_protonode = ProtoNode {
|
||||||
|
construction_args: ConstructionArgs::None,
|
||||||
|
input: ProtoNodeInput::Node(2),
|
||||||
|
name: "todo!()".to_string(),
|
||||||
|
};
|
||||||
|
simple_lookup("AddNode").1(add_protonode, &stack);
|
||||||
|
|
||||||
|
let result = unsafe { stack.get()[3].eval(Box::new(())) };
|
||||||
|
let val = *dyn_any::downcast::<u32>(result).unwrap();
|
||||||
|
assert_eq!(val, 42);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn greyscale_colour() {
|
||||||
|
let stack = FixedSizeStack::new(256);
|
||||||
|
let val_protonode = ProtoNode::value("name".to_string(), ConstructionArgs::Value(Box::new(Color::from_rgb8(10, 20, 30))));
|
||||||
|
simple_lookup("ValueNode").1(val_protonode, &stack);
|
||||||
|
|
||||||
|
let greyscale_protonode = ProtoNode {
|
||||||
|
construction_args: ConstructionArgs::None,
|
||||||
|
input: ProtoNodeInput::Node(0),
|
||||||
|
name: "todo!()".to_string(),
|
||||||
|
};
|
||||||
|
simple_lookup("GrayscaleNode").1(greyscale_protonode, &stack);
|
||||||
|
|
||||||
|
let result = unsafe { stack.get()[1].eval(Box::new(())) };
|
||||||
|
let val = *dyn_any::downcast::<Color>(result).unwrap();
|
||||||
|
assert_eq!(val, Color::from_rgb8(20, 20, 20));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn load_image() {
|
||||||
|
let stack = FixedSizeStack::new(256);
|
||||||
|
let image_protonode = ProtoNode {
|
||||||
|
construction_args: ConstructionArgs::None,
|
||||||
|
input: ProtoNodeInput::None,
|
||||||
|
name: "todo!()".to_string(),
|
||||||
|
};
|
||||||
|
simple_lookup("image_node").1(image_protonode, &stack);
|
||||||
|
|
||||||
|
let result = unsafe { stack.get()[0].eval(Box::new("../gstd/test-image-1.png")) };
|
||||||
|
let image = *dyn_any::downcast::<Image>(result).unwrap();
|
||||||
|
assert_eq!(image.height, 240);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn greyscale_map_image() {
|
||||||
|
let stack = FixedSizeStack::new(256);
|
||||||
|
let image_protonode = ProtoNode {
|
||||||
|
construction_args: ConstructionArgs::None,
|
||||||
|
input: ProtoNodeInput::None,
|
||||||
|
name: "todo!()".to_string(),
|
||||||
|
};
|
||||||
|
simple_lookup("image_node").1(image_protonode, &stack);
|
||||||
|
|
||||||
|
let greyscale_protonode = ProtoNode {
|
||||||
|
construction_args: ConstructionArgs::None,
|
||||||
|
input: ProtoNodeInput::None,
|
||||||
|
name: "todo!()".to_string(),
|
||||||
|
};
|
||||||
|
simple_lookup("GrayscaleNode").1(greyscale_protonode, &stack);
|
||||||
|
|
||||||
|
let image_map_protonode = ProtoNode {
|
||||||
|
construction_args: ConstructionArgs::Nodes(vec![1]),
|
||||||
|
input: ProtoNodeInput::Node(0),
|
||||||
|
name: "todo!()".to_string(),
|
||||||
|
};
|
||||||
|
simple_lookup("MapImageNode").1(image_map_protonode, &stack);
|
||||||
|
|
||||||
|
let result = unsafe { stack.get()[2].eval(Box::new("../gstd/test-image-1.png")) };
|
||||||
|
let image = *dyn_any::downcast::<Image>(result).unwrap();
|
||||||
|
assert!(!image.data.iter().any(|c| c.r() != c.b() || c.b() != c.g()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,3 +26,4 @@ proc-macro2 = {version = "1.0", default-features = false, features = ["proc-macr
|
|||||||
quote = {version = "1.0", default-features = false }
|
quote = {version = "1.0", default-features = false }
|
||||||
image = "*"
|
image = "*"
|
||||||
rand_chacha = "0.3.1"
|
rand_chacha = "0.3.1"
|
||||||
|
dyn-clone = "1.0"
|
||||||
|
|||||||
@@ -144,6 +144,36 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Boxes the input and downcasts the output.
|
||||||
|
/// Wraps around a node taking Box<dyn DynAny> and returning Box<dyn DynAny>
|
||||||
|
pub struct DowncastBothNode<N, I: StaticType, O: StaticType>(pub N, pub PhantomData<(I, O)>);
|
||||||
|
impl<N: Copy + Clone, I: StaticType, O: StaticType> Clone for DowncastBothNode<N, I, O> {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
Self(self.0, self.1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<N: Copy + Clone, I: StaticType, O: StaticType> Copy for DowncastBothNode<N, I, O> {}
|
||||||
|
|
||||||
|
impl<'n, N, I: 'n + StaticType, O: 'n + StaticType> Node<I> for DowncastBothNode<N, I, O>
|
||||||
|
where
|
||||||
|
N: Node<Any<'n>, Output = Any<'n>>,
|
||||||
|
{
|
||||||
|
type Output = O;
|
||||||
|
fn eval(self, input: I) -> Self::Output {
|
||||||
|
let input = Box::new(input) as Box<dyn DynAny>;
|
||||||
|
let output = self.0.eval(input);
|
||||||
|
*dyn_any::downcast(output).unwrap_or_else(|| panic!("DowncastBothNode Output: {}", fmt_error::<O>()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<'n, N, I: StaticType, O: StaticType> DowncastBothNode<N, I, O>
|
||||||
|
where
|
||||||
|
N: Node<Any<'n>>,
|
||||||
|
{
|
||||||
|
pub fn new(n: N) -> Self {
|
||||||
|
DowncastBothNode(n, PhantomData)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
/// If we store a `Box<dyn RefNode>` in the stack then the origional DynAnyNode is dropped (because it is not stored by reference)
|
/// If we store a `Box<dyn RefNode>` in the stack then the origional DynAnyNode is dropped (because it is not stored by reference)
|
||||||
/// This trait is implemented directly by `DynAnyNode` so this means the borrow stack will hold by value
|
/// This trait is implemented directly by `DynAnyNode` so this means the borrow stack will hold by value
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
use core::marker::PhantomData;
|
use core::marker::PhantomData;
|
||||||
|
use dyn_any::{DynAny, StaticType};
|
||||||
use graphene_core::ops::FlatMapResultNode;
|
use graphene_core::ops::FlatMapResultNode;
|
||||||
use graphene_core::raster::color::Color;
|
use graphene_core::raster::color::Color;
|
||||||
use graphene_core::structural::{ComposeNode, ConsNode};
|
use graphene_core::structural::{ComposeNode, ConsNode};
|
||||||
@@ -23,6 +24,17 @@ impl<I: IntoIterator<Item = S>, MN: Node<S>, S> MapNode<MN, I, S> {
|
|||||||
|
|
||||||
pub struct MapImageNode<MN: Node<Color, Output = Color> + Copy>(pub MN);
|
pub struct MapImageNode<MN: Node<Color, Output = Color> + Copy>(pub MN);
|
||||||
|
|
||||||
|
impl<MN: Node<Color, Output = Color> + Copy> Node<Image> for MapImageNode<MN> {
|
||||||
|
type Output = Image;
|
||||||
|
fn eval(self, input: Image) -> Self::Output {
|
||||||
|
Image {
|
||||||
|
width: input.width,
|
||||||
|
height: input.height,
|
||||||
|
data: input.data.iter().map(|x| self.0.eval(*x)).collect(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<'n, MN: Node<Color, Output = Color> + Copy> Node<Image> for &'n MapImageNode<MN> {
|
impl<'n, MN: Node<Color, Output = Color> + Copy> Node<Image> for &'n MapImageNode<MN> {
|
||||||
type Output = Image;
|
type Output = Image;
|
||||||
fn eval(self, input: Image) -> Self::Output {
|
fn eval(self, input: Image) -> Self::Output {
|
||||||
@@ -40,7 +52,7 @@ impl<MN: Node<Color, Output = Color> + Copy> MapImageNode<MN> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, DynAny)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
IO(std::io::Error),
|
IO(std::io::Error),
|
||||||
Image(image::ImageError),
|
Image(image::ImageError),
|
||||||
@@ -86,7 +98,7 @@ impl<Reader: std::io::Read> Node<Reader> for BufferNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone, DynAny)]
|
||||||
pub struct Image {
|
pub struct Image {
|
||||||
pub width: u32,
|
pub width: u32,
|
||||||
pub height: u32,
|
pub height: u32,
|
||||||
|
|||||||
Reference in New Issue
Block a user