diff --git a/Cargo.lock b/Cargo.lock index 4668b0a0a..a5eb3749e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,18 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "adler32" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234" + [[package]] name = "aho-corasick" version = "0.7.18" @@ -73,6 +85,12 @@ dependencies = [ "wasm-bindgen-test", ] +[[package]] +name = "bit_field" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcb6dd1c2376d2e096796e234a70e17e94cc2d5d54ff8ce42b28cef1d0d359a4" + [[package]] name = "bitflags" version = "1.3.2" @@ -95,12 +113,24 @@ version = "1.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cdead85bdec19c194affaeeb670c0e41fe23de31459efd1c174d049269cf02cc" +[[package]] +name = "byteorder" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" + [[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "color_quant" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" + [[package]] name = "console_error_panic_hook" version = "0.1.7" @@ -111,6 +141,69 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "crc32fast" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crossbeam-channel" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2dd04ddaf88237dc3b8d8f9a3c1004b506b54b3313403944054d23c0870c521" +dependencies = [ + "cfg-if", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "715e8152b692bba2d374b53d4875445368fdf21a94751410af607a5ac677d1fc" +dependencies = [ + "cfg-if", + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "045ebe27666471bb549370b4b0b3e51b07f56325befa4284db65fc89c02511b1" +dependencies = [ + "autocfg", + "cfg-if", + "crossbeam-utils", + "memoffset", + "once_cell", + "scopeguard", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51887d4adc7b564537b15adcfb307936f8075dfcd5f00dde9a9f1d29383682bc" +dependencies = [ + "cfg-if", + "once_cell", +] + +[[package]] +name = "deflate" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c86f7e25f518f4b81808a2cf1c50996a61f5c2eb394b2393bd87f2a4780a432f" +dependencies = [ + "adler32", +] + [[package]] name = "derivative" version = "2.2.0" @@ -139,6 +232,12 @@ dependencies = [ "syn", ] +[[package]] +name = "either" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f107b87b6afc2a64fd13cac55fe06d6c8859f12d4b14cbcdd2c67d0976781be" + [[package]] name = "env_logger" version = "0.8.4" @@ -152,12 +251,86 @@ dependencies = [ "termcolor", ] +[[package]] +name = "exr" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14cc0e06fb5f67e5d6beadf3a382fec9baca1aa751c6d5368fdeee7e5932c215" +dependencies = [ + "bit_field", + "deflate", + "flume", + "half", + "inflate", + "lebe", + "smallvec", + "threadpool", +] + +[[package]] +name = "flate2" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f82b0f4c27ad9f8bfd1f3208d882da2b09c301bc1c828fd3a00d0216d2fbbff6" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + +[[package]] +name = "flume" +version = "0.10.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1657b4441c3403d9f7b3409e47575237dac27b1b5726df654a6ecbf92f0f7577" +dependencies = [ + "futures-core", + "futures-sink", + "nanorand", + "pin-project", + "spin", +] + [[package]] name = "fnv" version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "futures-core" +version = "0.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c09fd04b7e4073ac7156a9539b57a484a8ea920f79c7c675d05d289ab6110d3" + +[[package]] +name = "futures-sink" +version = "0.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21163e139fa306126e6eedaf49ecdb4588f939600f0b1e770f4205ee4b7fa868" + +[[package]] +name = "getrandom" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4eb1a864a501629691edf6c15a593b7a51eebaa1e8468e9ddc623de7c9b58ec6" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "wasi", + "wasm-bindgen", +] + +[[package]] +name = "gif" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3edd93c6756b4dfaf2709eafcc345ba2636565295c198a9cfbf75fa5e3e00b06" +dependencies = [ + "color_quant", + "weezl", +] + [[package]] name = "glam" version = "0.17.3" @@ -197,6 +370,7 @@ dependencies = [ "dyn-any", "graph-proc-macros", "graphene-core", + "image", "once_cell", "proc-macro2", "quote", @@ -265,6 +439,12 @@ dependencies = [ "wasm-bindgen-test", ] +[[package]] +name = "half" +version = "1.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7" + [[package]] name = "hermit-abi" version = "0.1.19" @@ -280,12 +460,49 @@ version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" +[[package]] +name = "image" +version = "0.24.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e30ca2ecf7666107ff827a8e481de6a132a9b687ed3bb20bb1c144a36c00964" +dependencies = [ + "bytemuck", + "byteorder", + "color_quant", + "exr", + "gif", + "jpeg-decoder", + "num-rational", + "num-traits", + "png", + "scoped_threadpool", + "tiff", +] + +[[package]] +name = "inflate" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cdb29978cc5797bd8dcc8e5bf7de604891df2a8dc576973d71a281e916db2ff" +dependencies = [ + "adler32", +] + [[package]] name = "itoa" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35" +[[package]] +name = "jpeg-decoder" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9478aa10f73e7528198d75109c8be5cd7d15fb530238040148d5f9a22d4c5b3b" +dependencies = [ + "rayon", +] + [[package]] name = "js-sys" version = "0.3.57" @@ -310,6 +527,12 @@ version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +[[package]] +name = "lebe" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7efd1d698db0759e6ef11a7cd44407407399a910c774dd804c64c032da7826ff" + [[package]] name = "libc" version = "0.2.124" @@ -347,6 +570,54 @@ version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" +[[package]] +name = "memoffset" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" +dependencies = [ + "autocfg", +] + +[[package]] +name = "miniz_oxide" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f5c75688da582b8ffc1f1799e9db273f32133c49e048f614d22ec3256773ccc" +dependencies = [ + "adler", +] + +[[package]] +name = "nanorand" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a51313c5820b0b02bd422f4b44776fbf47961755c74ce64afc73bfad10226c3" +dependencies = [ + "getrandom", +] + +[[package]] +name = "num-integer" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + [[package]] name = "num-traits" version = "0.2.15" @@ -357,12 +628,54 @@ dependencies = [ "libm", ] +[[package]] +name = "num_cpus" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1" +dependencies = [ + "hermit-abi", + "libc", +] + [[package]] name = "once_cell" version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "18a6dbe30758c9f83eb00cbea4ac95966305f5a7772f3f42ebfc7fc7eddbd8e1" +[[package]] +name = "pin-project" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78203e83c48cffbe01e4a2d35d566ca4de445d79a85372fc64e378bfc812a260" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "710faf75e1b33345361201d36d04e98ac1ed8909151a017ed384700836104c74" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "png" +version = "0.17.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc38c0ad57efb786dd57b9864e5b18bae478c00c824dc55a38bbc9da95dde3ba" +dependencies = [ + "bitflags", + "crc32fast", + "deflate", + "miniz_oxide", +] + [[package]] name = "ppv-lite86" version = "0.2.16" @@ -438,6 +751,30 @@ version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" +[[package]] +name = "rayon" +version = "1.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd99e5772ead8baa5215278c9b15bf92087709e9c1b2d1f97cdb5a183c933a7d" +dependencies = [ + "autocfg", + "crossbeam-deque", + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "258bcdb5ac6dad48491bb2992db6b7cf74878b0384908af124823d118c99683f" +dependencies = [ + "crossbeam-channel", + "crossbeam-deque", + "crossbeam-utils", + "num_cpus", +] + [[package]] name = "regex" version = "1.5.5" @@ -494,6 +831,12 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ea6a9290e3c9cf0f18145ef7ffa62d68ee0bf5fcd651017e586dc7fd5da448c2" +[[package]] +name = "scoped_threadpool" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d51f5df5af43ab3f1360b429fa5e0152ac5ce8c0bd6485cae490332e96846a8" + [[package]] name = "scopeguard" version = "1.1.0" @@ -648,6 +991,26 @@ dependencies = [ "syn", ] +[[package]] +name = "threadpool" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d050e60b33d41c19108b32cea32164033a9013fe3b46cbd4457559bfbf77afaa" +dependencies = [ + "num_cpus", +] + +[[package]] +name = "tiff" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7259662e32d1e219321eb309d5f9d898b779769d81b76e762c07c8e5d38fcb65" +dependencies = [ + "flate2", + "jpeg-decoder", + "weezl", +] + [[package]] name = "ttf-parser" version = "0.15.0" @@ -690,6 +1053,12 @@ version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + [[package]] name = "wasm-bindgen" version = "0.2.80" @@ -792,6 +1161,12 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "weezl" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9193164d4de03a926d909d3bc7c30543cecb35400c02114792c2cae20d5e2dbb" + [[package]] name = "winapi" version = "0.3.9" diff --git a/node-graph/gcore/src/ops.rs b/node-graph/gcore/src/ops.rs index 672048dfa..17800ffe2 100644 --- a/node-graph/gcore/src/ops.rs +++ b/node-graph/gcore/src/ops.rs @@ -1,9 +1,10 @@ +use core::marker::PhantomData; use core::ops::Add; use crate::Node; pub struct AddNode; -impl<'n, L: Add, R> Node<'n, (L, R)> for AddNode { +impl<'n, L: Add + 'n, R, O: 'n> Node<'n, (L, R)> for AddNode { type Output = >::Output; fn eval(&'n self, input: (L, R)) -> Self::Output { input.0 + input.1 @@ -52,9 +53,27 @@ impl<'n, T, U: 'n> Node<'n, &'n (T, U)> for SndNode { } } +/// Destructures a Tuple of two values and returns them in reverse order +pub struct SwapNode; +impl<'n, T: 'n, U: 'n> Node<'n, (T, U)> for SwapNode { + type Output = (U, T); + fn eval(&'n self, input: (T, U)) -> Self::Output { + let (a, b) = input; + (b, a) + } +} + +impl<'n, T, U: 'n> Node<'n, &'n (T, U)> for SwapNode { + type Output = (&'n U, &'n T); + fn eval(&'n self, input: &'n (T, U)) -> Self::Output { + let (a, b) = input; + (b, a) + } +} + /// Return a tuple with two instances of the input argument pub struct DupNode; -impl<'n, T: Clone> Node<'n, T> for DupNode { +impl<'n, T: Clone + 'n> Node<'n, T> for DupNode { type Output = (T, T); fn eval(&'n self, input: T) -> Self::Output { (input.clone(), input) //TODO: use Copy/Clone implementation @@ -63,13 +82,46 @@ impl<'n, T: Clone> Node<'n, T> for DupNode { /// Return the Input Argument pub struct IdNode; -impl<'n, T> Node<'n, T> for IdNode { +impl<'n, T: 'n> Node<'n, T> for IdNode { type Output = T; fn eval(&'n self, input: T) -> Self::Output { input } } +pub struct MapResultNode<'n, MN: Node<'n, I>, I, E>(pub MN, pub PhantomData<&'n (I, E)>); + +impl<'n, MN: Node<'n, I>, I, E> Node<'n, Result> for MapResultNode<'n, MN, I, E> { + type Output = Result; + fn eval(&'n self, input: Result) -> Self::Output { + input.map(|x| self.0.eval(x)) + } +} + +impl<'n, MN: Node<'n, I>, I, E> MapResultNode<'n, MN, I, E> { + pub const fn new(mn: MN) -> Self { + Self(mn, PhantomData) + } +} +pub struct FlatMapResultNode<'n, MN: Node<'n, I>, I, E>(pub MN, pub PhantomData<&'n (I, E)>); + +impl<'n, MN: Node<'n, I, Output = Result>, I, O: 'n, E: 'n> Node<'n, Result> for FlatMapResultNode<'n, MN, I, E> { + type Output = Result; + fn eval(&'n self, input: Result) -> Self::Output { + match input.map(|x| self.0.eval(x)) { + Ok(Ok(x)) => Ok(x), + Ok(Err(e)) => Err(e), + Err(e) => Err(e), + } + } +} + +impl<'n, MN: Node<'n, I>, I, E> FlatMapResultNode<'n, MN, I, E> { + pub const fn new(mn: MN) -> Self { + Self(mn, PhantomData) + } +} + #[cfg(test)] mod test { use super::*; diff --git a/node-graph/gcore/src/raster/color.rs b/node-graph/gcore/src/raster/color.rs index 0b4fe2fd1..69f8a2fb7 100644 --- a/node-graph/gcore/src/raster/color.rs +++ b/node-graph/gcore/src/raster/color.rs @@ -134,6 +134,18 @@ impl Color { (self.red, self.green, self.blue, self.alpha) } + /// Return the all components as a u8 slice, first component is red, followed by green, followed by blue, followed by alpha. + /// + /// # Examples + /// ``` + /// use graphene_core::raster::color::Color; + /// let color = Color::from_rgbaf32(0.114, 0.103, 0.98, 0.97).unwrap(); + /// //TODO: Add test + /// ``` + pub fn to_rgba8(&self) -> [u8; 4] { + [(self.red * 255.) as u8, (self.green * 255.) as u8, (self.blue * 255.) as u8, (self.alpha * 255.) as u8] + } + // TODO: Readd formatting /// Creates a color from a 8-character RGBA hex string (without a # prefix). diff --git a/node-graph/gcore/src/structural.rs b/node-graph/gcore/src/structural.rs index 8086a4870..2b5625126 100644 --- a/node-graph/gcore/src/structural.rs +++ b/node-graph/gcore/src/structural.rs @@ -58,3 +58,17 @@ where (input, arg) } } + +pub struct ConsPassInputNode(pub Root); + +impl<'n, Root, L, R> Node<'n, (L, R)> for ConsPassInputNode +where + Root: Node<'n, R>, +{ + type Output = (L, >::Output); + + fn eval(&'n self, input: (L, R)) -> Self::Output { + let arg = self.0.eval(input.1); + (input.0, arg) + } +} diff --git a/node-graph/gstd/Cargo.toml b/node-graph/gstd/Cargo.toml index 5142d6a27..f37ada4e6 100644 --- a/node-graph/gstd/Cargo.toml +++ b/node-graph/gstd/Cargo.toml @@ -24,3 +24,4 @@ once_cell = {version= "1.10", optional = true} syn = {version = "1.0", default-features = false, features = ["parsing", "printing"]} proc-macro2 = {version = "1.0", default-features = false, features = ["proc-macro"]} quote = {version = "1.0", default-features = false } +image = "*" diff --git a/node-graph/gstd/src/lib.rs b/node-graph/gstd/src/lib.rs index eaf068713..09824b48f 100644 --- a/node-graph/gstd/src/lib.rs +++ b/node-graph/gstd/src/lib.rs @@ -1,22 +1,23 @@ -pub mod value; +#![feature(type_alias_impl_trait)] +//pub mod value; pub use graphene_core::{generic, ops /*, structural*/}; -//#[cfg(feature = "memoization")] -//pub mod memo; +#[cfg(feature = "memoization")] +pub mod memo; -//pub mod raster; +pub mod raster; pub use graphene_core::*; -use dyn_any::DynAny; +/*use dyn_any::DynAny; pub type DynNode<'n, T> = &'n (dyn Node<'n, Output = T> + 'n); pub type DynAnyNode<'n> = &'n (dyn Node<'n, Output = &'n dyn DynAny<'n>> + 'n); - pub trait DynamicInput<'n> { fn set_kwarg_by_name(&mut self, name: &str, value: DynAnyNode<'n>); fn set_arg_by_index(&mut self, index: usize, value: DynAnyNode<'n>); } +*/ use quote::quote; use syn::{Expr, ExprPath, Type}; diff --git a/node-graph/gstd/src/main.rs b/node-graph/gstd/src/main.rs index c8f70f91a..1fdca03b9 100644 --- a/node-graph/gstd/src/main.rs +++ b/node-graph/gstd/src/main.rs @@ -8,6 +8,7 @@ a * b }*/ +/* mod mul { use dyn_any::downcast_ref; use graphene_std::{DynAnyNode, DynNode, DynamicInput, Node}; @@ -69,7 +70,7 @@ mod mul { } } // type SNode<'n> = dyn Node<'n, Output = &'n dyn DynAny<'n>>; - +*/ // struct NodeStore<'n>(borrow_stack::FixedSizeStack<'n, Box>>); // impl<'n> NodeStore<'n> { diff --git a/node-graph/gstd/src/memo.rs b/node-graph/gstd/src/memo.rs index 443f87772..5be80d4e3 100644 --- a/node-graph/gstd/src/memo.rs +++ b/node-graph/gstd/src/memo.rs @@ -3,26 +3,26 @@ use once_cell::sync::OnceCell; use std::marker::PhantomData; /// Caches the output of a given Node and acts as a proxy -pub struct CacheNode<'n, CachedNode: Node<'n>> { +pub struct CacheNode<'n, CachedNode: Node<'n, I>, I> { node: CachedNode, cache: OnceCell, _phantom: PhantomData<&'n ()>, } -impl<'n, CashedNode: Node<'n>> Node<'n> for CacheNode<'n, CashedNode> +impl<'n, CashedNode: Node<'n, I>, I> Node<'n, I> for CacheNode<'n, CashedNode, I> where CashedNode::Output: 'n, { type Output = &'n CashedNode::Output; - fn eval(&'n self) -> Self::Output { - self.cache.get_or_init(|| self.node.eval()) + fn eval(&'n self, input: I) -> Self::Output { + self.cache.get_or_init(|| self.node.eval(input)) } } -impl<'n, CachedNode: Node<'n>> CacheNode<'n, CachedNode> { +impl<'n, CachedNode: Node<'n, I>, I> CacheNode<'n, CachedNode, I> { pub fn clear(&'n mut self) { self.cache = OnceCell::new(); } - pub fn new(node: CachedNode) -> CacheNode<'n, CachedNode> { + pub fn new(node: CachedNode) -> CacheNode<'n, CachedNode, I> { CacheNode { node, cache: OnceCell::new(), @@ -30,7 +30,7 @@ impl<'n, CachedNode: Node<'n>> CacheNode<'n, CachedNode> { } } } -impl<'n, CachedNode: Node<'n>> Cache for CacheNode<'n, CachedNode> { +impl<'n, CachedNode: Node<'n, I>, I> Cache for CacheNode<'n, CachedNode, I> { fn clear(&mut self) { self.cache = OnceCell::new(); } diff --git a/node-graph/gstd/src/raster.rs b/node-graph/gstd/src/raster.rs index 782bce567..e17794d21 100644 --- a/node-graph/gstd/src/raster.rs +++ b/node-graph/gstd/src/raster.rs @@ -1,18 +1,197 @@ use core::marker::PhantomData; -use graphene_core::{value::RefNode, value::ValueNode, Node}; +use graphene_core::ops::FlatMapResultNode; +use graphene_core::raster::color::Color; +use graphene_core::structural::{ComposeNode, ConsNode}; +use graphene_core::{generic::FnNode, ops::MapResultNode, structural::After, value::ValueNode, Node}; +use image::Pixel; +use std::path::Path; -pub struct MapNode<'n, IN: Node<'n, Output = I>, I: Iterator, MAP: Fn(&dyn RefNode) -> MN, MN: Node<'n, Output = O> + 'n, S, O: 'n>(pub IN, pub MAP, PhantomData<&'n (I, S)>); +pub struct MapNode<'n, MN: Node<'n, S>, I: IntoIterator, S>(pub MN, PhantomData<&'n (S, I)>); -impl<'n, IN: Node<'n, Output = I>, I: Iterator, MAP: Fn(&dyn RefNode) -> MN, MN: Node<'n, Output = O>, S, O: 'static + Clone> Node<'n> for MapNode<'n, IN, I, MAP, MN, S, O> { - type Output = Vec; - fn eval(&'n self) -> Self::Output { - self.0 - .eval() - .map(|x| { - let map_node = self.1(x as &dyn RefNode); - let result = map_node.eval(); - result.clone() - }) - .collect() +impl<'n, I: IntoIterator, MN: Node<'n, S>, S> Node<'n, I> for MapNode<'n, MN, I, S> { + type Output = Vec; + fn eval(&'n self, input: I) -> Self::Output { + input.into_iter().map(|x| self.0.eval(x)).collect() + } +} + +impl<'n, I: IntoIterator, MN: Node<'n, S>, S> MapNode<'n, MN, I, S> { + pub const fn new(mn: MN) -> Self { + MapNode(mn, PhantomData) + } +} + +pub struct MapImageNode<'n, MN: Node<'n, Color, Output = Color>>(pub MN, PhantomData<&'n ()>); + +impl<'n, MN: Node<'n, Color, Output = Color>> Node<'n, Image> for MapImageNode<'n, MN> { + type Output = Image; + fn eval(&'n 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<'n, Color, Output = Color>> MapImageNode<'n, MN> { + pub const fn new(mn: MN) -> Self { + MapImageNode(mn, PhantomData) + } +} + +#[derive(Debug)] +pub enum Error { + IO(std::io::Error), + Image(image::ImageError), +} + +impl From for Error { + fn from(e: std::io::Error) -> Self { + Error::IO(e) + } +} + +pub trait FileSystem { + fn open>(&self, path: P) -> Result, Error>; +} + +#[derive(Clone)] +pub struct StdFs; +impl FileSystem for StdFs { + fn open>(&self, path: P) -> Result { + Ok(Box::new(std::fs::File::open(path)?)) + } +} +type Reader = Box; + +pub struct FileNode, FS: FileSystem>(PhantomData<(P, FS)>); +impl<'n, P: AsRef, FS: FileSystem> Node<'n, (P, FS)> for FileNode { + type Output = Result; + + fn eval(&'n self, input: (P, FS)) -> Self::Output { + let (path, fs) = input; + fs.open(path) + } +} + +pub struct BufferNode; +impl<'n, Reader: std::io::Read> Node<'n, Reader> for BufferNode { + type Output = Result, Error>; + + fn eval(&'n self, mut reader: Reader) -> Self::Output { + let mut buffer = Vec::new(); + reader.read_to_end(&mut buffer)?; + Ok(buffer) + } +} + +#[derive(Clone)] +pub struct Image { + pub width: u32, + pub height: u32, + pub data: Vec, +} + +impl IntoIterator for Image { + type Item = Color; + type IntoIter = std::vec::IntoIter; + fn into_iter(self) -> Self::IntoIter { + self.data.into_iter() + } +} + +impl<'a> IntoIterator for &'a Image { + type Item = &'a Color; + type IntoIter = std::slice::Iter<'a, Color>; + fn into_iter(self) -> Self::IntoIter { + self.data.iter() + } +} + +pub fn file_node<'n, P: AsRef + 'n>() -> impl Node<'n, P, Output = Result, Error>> { + let fs = ValueNode(StdFs).clone(); + let fs = ConsNode(fs); + let file: ComposeNode> = FileNode(PhantomData).after(fs); + let buffer = FlatMapResultNode::new(BufferNode).after(file); + buffer +} +type Ret<'n> = impl Node<'n, (), Output = u32>; + +pub fn test_node<'n>() -> Ret<'n> { + ValueNode(432).clone() +} + +pub fn image_node<'n, P: AsRef + 'n>() -> impl Node<'n, P, Output = Result> { + let file = file_node(); + let image_loader = FnNode::new(|data: Vec| image::load_from_memory(&data).map_err(Error::Image).map(|image| image.into_rgba32f())); + let image: ComposeNode<'_, P, _, _> = FlatMapResultNode::new(image_loader).after(file); + let convert_image = FnNode::new(|image: image::ImageBuffer<_, _>| { + let data = image + .enumerate_pixels() + .map(|(_, _, pixel): (_, _, &image::Rgba)| { + let c = pixel.channels(); + Color::from_rgbaf32(c[0], c[1], c[2], c[3]).unwrap() + }) + .collect(); + Image { + width: image.width(), + height: image.height(), + data, + } + }); + let image = MapResultNode::new(convert_image).after(image); + image +} + +pub fn export_image_node<'n>() -> impl Node<'n, (Image, &'n str), Output = Result<(), Error>> { + FnNode::new(|input: (Image, &str)| { + let (image, path) = input; + let mut new_image = image::ImageBuffer::new(image.width, image.height); + for ((x, y, pixel), color) in new_image.enumerate_pixels_mut().zip((&image).into_iter()) { + let color: Color = *color; + assert!(x < image.width); + assert!(y < image.height); + *pixel = image::Rgba([color.r(), color.g(), color.b(), color.a()]) + } + new_image.save(path).map_err(Error::Image) + }) +} + +#[cfg(test)] +mod test { + use super::*; + use graphene_core::raster::color::Color; + use graphene_core::raster::GrayscaleNode; + + #[test] + fn map_node() { + let array = [Color::from_rgbaf32(1.0, 0.0, 0.0, 1.0).unwrap()]; + let map = MapNode(GrayscaleNode, PhantomData); + let values = map.eval(array.into_iter()); + assert_eq!(values[0], Color::from_rgbaf32(0.33333334, 0.33333334, 0.33333334, 1.0).unwrap()); + } + + #[test] + fn load_image() { + /*let image = image_node(); + let gray = MapImageNode::new(GrayscaleNode); + + let gray_scale_picture = MapResultNode::new(gray).after(image); + let gray_scale_picture = gray_scale_picture.eval("image"); + */ + let test_node = test_node(); + { + let foo = test_node.eval(()); + std::mem::drop(foo); + } + let export = export_image_node(); + + /*let export = FnNode::new(|input: (&str, &str)| { + let (input, output) = input;*/ + //let picture = gray_scale_picture.eval("/home/dennis/screenshot.png").unwrap().clone(); + //export.eval((picture, "screenshot.png")); + /*}); + export.eval(("screenshot.png", "/home/dennis/screenshot.png"));*/ } }