From 8e3480e9525eeefe97f8505fba8e669f7e9dc331 Mon Sep 17 00:00:00 2001 From: Dennis Kobert Date: Thu, 9 Feb 2023 01:17:09 +0100 Subject: [PATCH] Image adjustment nodes restructure (#1013) * Add macro for creation of Map image nodes * Move nodes to adjustments module * Add Saturation and Lightness to hue shift node * Fix raster node macro * Add Threshold Node * Convert all adjustment nodes to new format * Start implementing vibrance node * Remove package-lock.json * Code review --------- Co-authored-by: isiko404 Co-authored-by: Keavon Chambers --- .../document_node_types.rs | 38 ++++- .../node_properties.rs | 12 ++ node-graph/gcore/src/raster.rs | 42 +---- node-graph/gcore/src/raster/adjustments.rs | 150 ++++++++++++++++++ node-graph/gstd/src/raster.rs | 139 +--------------- .../interpreted-executor/src/node_registry.rs | 39 +++-- 6 files changed, 222 insertions(+), 198 deletions(-) create mode 100644 node-graph/gcore/src/raster/adjustments.rs diff --git a/editor/src/messages/portfolio/document/node_graph/node_graph_message_handler/document_node_types.rs b/editor/src/messages/portfolio/document/node_graph/node_graph_message_handler/document_node_types.rs index f2f61736..70dcd432 100644 --- a/editor/src/messages/portfolio/document/node_graph/node_graph_message_handler/document_node_types.rs +++ b/editor/src/messages/portfolio/document/node_graph/node_graph_message_handler/document_node_types.rs @@ -162,7 +162,7 @@ static STATIC_NODES: &[DocumentNodeType] = &[ DocumentNodeType { name: "Grayscale", category: "Image Adjustments", - identifier: NodeImplementation::proto("graphene_std::raster::GrayscaleNode", &[concrete!("Image")]), + identifier: NodeImplementation::proto("graphene_core::raster::GrayscaleNode", &[concrete!("Image")]), inputs: &[DocumentInputType::new("Image", TaggedValue::Image(Image::empty()), true)], outputs: &[FrontendGraphDataType::Raster], properties: node_properties::no_properties, @@ -219,7 +219,7 @@ static STATIC_NODES: &[DocumentNodeType] = &[ DocumentNodeType { name: "Invert RGB", category: "Image Adjustments", - identifier: NodeImplementation::proto("graphene_std::raster::InvertRGBNode", &[concrete!("Image")]), + identifier: NodeImplementation::proto("graphene_core::raster::InvertRGBNode", &[concrete!("Image")]), inputs: &[DocumentInputType::new("Image", TaggedValue::Image(Image::empty()), true)], outputs: &[FrontendGraphDataType::Raster], properties: node_properties::no_properties, @@ -228,7 +228,7 @@ static STATIC_NODES: &[DocumentNodeType] = &[ name: "Hue/Saturation", category: "Image Adjustments", identifier: NodeImplementation::proto( - "graphene_std::raster::HueSaturationNode<_, _, _>", + "graphene_core::raster::HueSaturationNode<_, _, _>", &[concrete!("Image"), concrete!("f64"), concrete!("f64"), concrete!("f64")], ), inputs: &[ @@ -243,7 +243,7 @@ static STATIC_NODES: &[DocumentNodeType] = &[ DocumentNodeType { name: "Brightness/Contrast", category: "Image Adjustments", - identifier: NodeImplementation::proto("graphene_std::raster::BrightnessContrastNode<_, _>", &[concrete!("Image"), concrete!("f64"), concrete!("f64")]), + identifier: NodeImplementation::proto("graphene_core::raster::BrightnessContrastNode<_, _>", &[concrete!("Image"), concrete!("f64"), concrete!("f64")]), inputs: &[ DocumentInputType::new("Image", TaggedValue::Image(Image::empty()), true), DocumentInputType::new("Brightness", TaggedValue::F64(0.), false), @@ -255,7 +255,7 @@ static STATIC_NODES: &[DocumentNodeType] = &[ DocumentNodeType { name: "Gamma", category: "Image Adjustments", - identifier: NodeImplementation::proto("graphene_std::raster::GammaNode<_>", &[concrete!("Image"), concrete!("f64")]), + identifier: NodeImplementation::proto("graphene_core::raster::GammaNode<_>", &[concrete!("Image"), concrete!("f64")]), inputs: &[ DocumentInputType::new("Image", TaggedValue::Image(Image::empty()), true), DocumentInputType::new("Gamma", TaggedValue::F64(1.), false), @@ -263,10 +263,32 @@ static STATIC_NODES: &[DocumentNodeType] = &[ outputs: &[FrontendGraphDataType::Raster], properties: node_properties::adjust_gamma_properties, }, + DocumentNodeType { + name: "Threshold", + category: "Image Adjustments", + identifier: NodeImplementation::proto("graphene_core::raster::ThresholdNode<_>", &[concrete!("Image"), concrete!("f64")]), + inputs: &[ + DocumentInputType::new("Image", TaggedValue::Image(Image::empty()), true), + DocumentInputType::new("Threshold", TaggedValue::F64(1.), false), + ], + outputs: &[FrontendGraphDataType::Raster], + properties: node_properties::adjust_threshold_properties, + }, + DocumentNodeType { + name: "Vibrance", + category: "Image Adjustments", + identifier: NodeImplementation::proto("graphene_core::raster::VibranceNode<_>", &[concrete!("Image"), concrete!("f64")]), + inputs: &[ + DocumentInputType::new("Image", TaggedValue::Image(Image::empty()), true), + DocumentInputType::new("Vibrance", TaggedValue::F64(1.), false), + ], + outputs: &[FrontendGraphDataType::Raster], + properties: node_properties::adjust_vibrance_properties, + }, DocumentNodeType { name: "Opacity", category: "Image Adjustments", - identifier: NodeImplementation::proto("graphene_std::raster::OpacityNode<_>", &[concrete!("Image"), concrete!("f64")]), + identifier: NodeImplementation::proto("graphene_core::raster::OpacityNode<_>", &[concrete!("Image"), concrete!("f64")]), inputs: &[ DocumentInputType::new("Image", TaggedValue::Image(Image::empty()), true), DocumentInputType::new("Factor", TaggedValue::F64(1.), false), @@ -277,7 +299,7 @@ static STATIC_NODES: &[DocumentNodeType] = &[ DocumentNodeType { name: "Posterize", category: "Image Adjustments", - identifier: NodeImplementation::proto("graphene_std::raster::PosterizeNode<_>", &[concrete!("Image"), concrete!("f64")]), + identifier: NodeImplementation::proto("graphene_core::raster::PosterizeNode<_>", &[concrete!("Image"), concrete!("f64")]), inputs: &[ DocumentInputType::new("Image", TaggedValue::Image(Image::empty()), true), DocumentInputType::new("Value", TaggedValue::F64(5.), false), @@ -288,7 +310,7 @@ static STATIC_NODES: &[DocumentNodeType] = &[ DocumentNodeType { name: "Exposure", category: "Image Adjustments", - identifier: NodeImplementation::proto("graphene_std::raster::ExposureNode<_>", &[concrete!("Image"), concrete!("f64")]), + identifier: NodeImplementation::proto("graphene_core::raster::ExposureNode<_>", &[concrete!("Image"), concrete!("f64")]), inputs: &[ DocumentInputType::new("Image", TaggedValue::Image(Image::empty()), true), DocumentInputType::new("Value", TaggedValue::F64(0.), false), diff --git a/editor/src/messages/portfolio/document/node_graph/node_graph_message_handler/node_properties.rs b/editor/src/messages/portfolio/document/node_graph/node_graph_message_handler/node_properties.rs index a59769cf..484a115c 100644 --- a/editor/src/messages/portfolio/document/node_graph/node_graph_message_handler/node_properties.rs +++ b/editor/src/messages/portfolio/document/node_graph/node_graph_message_handler/node_properties.rs @@ -189,6 +189,18 @@ pub fn adjust_gamma_properties(document_node: &DocumentNode, node_id: NodeId, _c vec![LayoutGroup::Row { widgets: gamma }] } +pub fn adjust_threshold_properties(document_node: &DocumentNode, node_id: NodeId, _context: &mut NodePropertiesContext) -> Vec { + let thereshold = number_widget(document_node, node_id, 1, "Threshold", NumberInput::default().min(0.).max(1.), true); + + vec![LayoutGroup::Row { widgets: thereshold }] +} + +pub fn adjust_vibrance_properties(document_node: &DocumentNode, node_id: NodeId, _context: &mut NodePropertiesContext) -> Vec { + let vibrance = number_widget(document_node, node_id, 1, "Vibrance", NumberInput::default().min(-100.).max(100.).unit("%"), true); + + vec![LayoutGroup::Row { widgets: vibrance }] +} + #[cfg(feature = "gpu")] pub fn gpu_map_properties(document_node: &DocumentNode, node_id: NodeId, _context: &mut NodePropertiesContext) -> Vec { let map = text_widget(document_node, node_id, 1, "Map", true); diff --git a/node-graph/gcore/src/raster.rs b/node-graph/gcore/src/raster.rs index 135e2c14..37250a97 100644 --- a/node-graph/gcore/src/raster.rs +++ b/node-graph/gcore/src/raster.rs @@ -5,14 +5,8 @@ use crate::Node; pub mod color; pub use self::color::Color; -#[derive(Debug, Clone, Copy, Default)] -pub struct GrayscaleColorNode; - -#[node_macro::node_fn(GrayscaleColorNode)] -fn grayscale_color_node(input: Color) -> Color { - let avg = (input.r() + input.g() + input.b()) / 3.0; - Color::from_rgbaf32_unchecked(avg, avg, avg, input.a()) -} +pub mod adjustments; +pub use adjustments::*; #[derive(Debug, Default)] pub struct MapNode { @@ -237,36 +231,6 @@ fn brighten_color_node(color: Color, brightness: f32) -> Color { Color::from_rgbaf32_unchecked(per_channel(color.r()), per_channel(color.g()), per_channel(color.b()), color.a()) } -#[derive(Debug)] -pub struct GammaColorNode { - gamma: Gamma, -} - -#[node_macro::node_fn(GammaColorNode)] -fn gamma_color_node(color: Color, gamma: f32) -> Color { - let per_channel = |col: f32| col.powf(gamma); - Color::from_rgbaf32_unchecked(per_channel(color.r()), per_channel(color.g()), per_channel(color.b()), color.a()) -} - -#[cfg(not(target_arch = "spirv"))] -pub use hue_shift::HueShiftColorNode; - -#[cfg(not(target_arch = "spirv"))] -mod hue_shift { - use super::*; - #[derive(Debug)] - pub struct HueShiftColorNode { - angle: Angle, - } - - #[node_macro::node_fn(HueShiftColorNode)] - fn hue_shift_color_node(color: Color, angle: f32) -> Color { - let hue_shift = angle; - let [hue, saturation, lightness, alpha] = color.to_hsla(); - Color::from_hsla(hue + hue_shift / 360., saturation, lightness, alpha) - } -} - #[derive(Debug)] pub struct ForEachNode { map_node: MapNode, @@ -410,7 +374,7 @@ mod test { #[test] fn map_node() { // let array = &mut [Color::from_rgbaf32(1.0, 0.0, 0.0, 1.0).unwrap()]; - GrayscaleColorNode.eval(Color::from_rgbf32_unchecked(1., 0., 0.)); + GrayscaleNode.eval(Color::from_rgbf32_unchecked(1., 0., 0.)); /*let map = ForEachNode(MutWrapper(GrayscaleNode)); (&map).eval(array.iter_mut()); assert_eq!(array[0], Color::from_rgbaf32(0.33333334, 0.33333334, 0.33333334, 1.0).unwrap());*/ diff --git a/node-graph/gcore/src/raster/adjustments.rs b/node-graph/gcore/src/raster/adjustments.rs new file mode 100644 index 00000000..c14239c1 --- /dev/null +++ b/node-graph/gcore/src/raster/adjustments.rs @@ -0,0 +1,150 @@ +use super::Color; +use crate::Node; + +use core::fmt::Debug; + +#[derive(Debug, Clone, Copy, Default)] +pub struct GrayscaleNode; + +#[node_macro::node_fn(GrayscaleNode)] +fn grayscale_color_node(input: Color) -> Color { + let avg = (input.r() + input.g() + input.b()) / 3.0; + map_rgb(input, |_| avg) +} + +#[derive(Debug)] +pub struct GammaNode { + gamma: Gamma, +} + +// https://www.dfstudios.co.uk/articles/programming/image-programming-algorithms/image-processing-algorithms-part-6-gamma-correction/ +#[node_macro::node_fn(GammaNode)] +fn gamma_color_node(color: Color, gamma: f64) -> Color { + let inverse_gamma = 1. / gamma; + let per_channel = |channel: f32| channel.powf(inverse_gamma as f32); + map_rgb(color, per_channel) +} + +#[cfg(not(target_arch = "spirv"))] +pub use hue_shift::HueSaturationNode; + +// TODO: Make this work on GPU so it can be removed from the wrapper module that excludes GPU (it doesn't work because of the modulo) +#[cfg(not(target_arch = "spirv"))] +mod hue_shift { + use super::*; + #[derive(Debug)] + pub struct HueSaturationNode { + hue_shift: Hue, + saturation_shift: Saturation, + lightness_shift: Lightness, + } + + #[node_macro::node_fn(HueSaturationNode)] + fn hue_shift_color_node(color: Color, hue_shift: f64, saturation_shift: f64, lightness_shift: f64) -> Color { + let [hue, saturation, lightness, alpha] = color.to_hsla(); + Color::from_hsla( + (hue + hue_shift as f32 / 360.) % 1., + (saturation + saturation_shift as f32 / 100.).clamp(0., 1.), + (lightness + lightness_shift as f32 / 100.).clamp(0., 1.), + alpha, + ) + } +} + +#[derive(Debug, Clone, Copy)] +pub struct InvertRGBNode; + +#[node_macro::node_fn(InvertRGBNode)] +fn invert_image(color: Color) -> Color { + map_rgb(color, |c| 1. - c) +} + +#[derive(Debug, Clone, Copy)] +pub struct ThresholdNode { + threshold: Threshold, +} + +#[node_macro::node_fn(ThresholdNode)] +fn threshold_node(color: Color, threshold: f64) -> Color { + let avg = (color.r() + color.g() + color.b()) / 3.0; + if avg >= threshold as f32 { + Color::WHITE + } else { + Color::BLACK + } +} + +#[derive(Debug, Clone, Copy)] +pub struct VibranceNode { + vibrance: Vibrance, +} + +// TODO: The current results are incorrect, try implementing this from https://stackoverflow.com/questions/33966121/what-is-the-algorithm-for-vibrance-filters +#[node_macro::node_fn(VibranceNode)] +fn vibrance_node(color: Color, vibrance: f64) -> Color { + let [hue, saturation, lightness, alpha] = color.to_hsla(); + let vibrance = vibrance as f32 / 100.; + let saturation = saturation + vibrance * (1. - saturation); + Color::from_hsla(hue, saturation, lightness, alpha) +} + +#[derive(Debug, Clone, Copy)] +pub struct BrightnessContrastNode { + brightness: Brightness, + contrast: Contrast, +} + +// From https://stackoverflow.com/questions/2976274/adjust-bitmap-image-brightness-contrast-using-c +#[node_macro::node_fn(BrightnessContrastNode)] +fn adjust_image_brightness_and_contrast(color: Color, brightness: f64, contrast: f64) -> Color { + let (brightness, contrast) = (brightness as f32, contrast as f32); + let factor = (259. * (contrast + 255.)) / (255. * (259. - contrast)); + let channel = |channel: f32| ((factor * (channel * 255. + brightness - 128.) + 128.) / 255.).clamp(0., 1.); + map_rgb(color, channel) +} + +#[derive(Debug, Clone, Copy)] +pub struct OpacityNode { + opacity_multiplier: O, +} + +#[node_macro::node_fn(OpacityNode)] +fn image_opacity(color: Color, opacity_multiplier: f64) -> Color { + let opacity_multiplier = opacity_multiplier as f32; + Color::from_rgbaf32_unchecked(color.r(), color.g(), color.b(), color.a() * opacity_multiplier) +} + +#[derive(Debug, Clone, Copy)] +pub struct PosterizeNode

{ + posterize_value: P, +} + +// Based on http://www.axiomx.com/posterize.htm +#[node_macro::node_fn(PosterizeNode)] +fn posterize(color: Color, posterize_value: f64) -> Color { + let posterize_value = posterize_value as f32; + let number_of_areas = posterize_value.recip(); + let size_of_areas = (posterize_value - 1.).recip(); + let channel = |channel: f32| (channel / number_of_areas).floor() * size_of_areas; + map_rgb(color, channel) +} + +#[derive(Debug, Clone, Copy)] +pub struct ExposureNode { + exposure: E, +} + +// Based on https://stackoverflow.com/questions/12166117/what-is-the-math-behind-exposure-adjustment-on-photoshop +#[node_macro::node_fn(ExposureNode)] +fn exposure(color: Color, exposure: f64) -> Color { + let multiplier = 2_f32.powf(exposure as f32); + let channel = |channel: f32| channel * multiplier; + map_rgb(color, channel) +} + +pub fn map_rgba f32>(color: Color, f: F) -> Color { + Color::from_rgbaf32_unchecked(f(color.r()), f(color.g()), f(color.b()), f(color.a())) +} +pub fn map_rgb f32>(color: Color, f: F) -> Color { + Color::from_rgbaf32_unchecked(f(color.r()), f(color.g()), f(color.b()), color.a()) +} diff --git a/node-graph/gstd/src/raster.rs b/node-graph/gstd/src/raster.rs index dda9e9b8..245bcef4 100644 --- a/node-graph/gstd/src/raster.rs +++ b/node-graph/gstd/src/raster.rs @@ -89,26 +89,13 @@ pub fn export_image_node<'i, 's: 'i>() -> impl Node<'i, 's, (Image, &'i str), Ou } */ -#[derive(Debug, Clone, Copy)] -pub struct GrayscaleNode; - -#[node_macro::node_fn(GrayscaleNode)] -fn grayscale_image(image: Image) -> Image { - let mut image = image; - for pixel in &mut image.data { - let avg = (pixel.r() + pixel.g() + pixel.b()) / 3.; - *pixel = Color::from_rgbaf32_unchecked(avg, avg, avg, pixel.a()); - } - image -} - #[derive(Debug, Clone, Copy)] pub struct MapImageNode { map_fn: MapFn, } #[node_macro::node_fn(MapImageNode)] -fn grayscale_image(image: Image, map_fn: &'any_input MapFn) -> Image +fn map_image(image: Image, map_fn: &'any_input MapFn) -> Image where MapFn: for<'any_input> Node<'any_input, Color, Output = Color> + 'input, { @@ -119,135 +106,11 @@ where image } -#[derive(Debug, Clone, Copy)] -pub struct InvertRGBNode; - -#[node_macro::node_fn(InvertRGBNode)] -fn invert_image(mut image: Image) -> Image { - let mut image = image; - for pixel in &mut image.data { - *pixel = Color::from_rgbaf32_unchecked(1. - pixel.r(), 1. - pixel.g(), 1. - pixel.b(), pixel.a()); - } - image -} - -#[derive(Debug, Clone, Copy)] -pub struct HueSaturationNode { - hue_shift: Hue, - saturation_shift: Sat, - lightness_shift: Lit, -} - -#[node_macro::node_fn(HueSaturationNode)] -fn shift_image_hsl(image: Image, hue_shift: f64, saturation_shift: f64, lightness_shift: f64) -> Image { - let mut image = image; - let (hue_shift, saturation_shift, lightness_shift) = (hue_shift as f32, saturation_shift as f32, lightness_shift as f32); - for pixel in &mut image.data { - let [hue, saturation, lightness, alpha] = pixel.to_hsla(); - *pixel = Color::from_hsla( - (hue + hue_shift / 360.) % 1., - (saturation + saturation_shift / 100.).clamp(0., 1.), - (lightness + lightness_shift / 100.).clamp(0., 1.), - alpha, - ); - } - image -} - -#[derive(Debug, Clone, Copy)] -pub struct BrightnessContrastNode { - brightness: Brightness, - contrast: Contrast, -} - -// From https://stackoverflow.com/questions/2976274/adjust-bitmap-image-brightness-contrast-using-c -#[node_macro::node_fn(BrightnessContrastNode)] -fn adjust_image_brightness_and_contrast(image: Image, brightness: f64, contrast: f64) -> Image { - let mut image = image; - let (brightness, contrast) = (brightness as f32, contrast as f32); - let factor = (259. * (contrast + 255.)) / (255. * (259. - contrast)); - let channel = |channel: f32| ((factor * (channel * 255. + brightness - 128.) + 128.) / 255.).clamp(0., 1.); - - for pixel in &mut image.data { - *pixel = Color::from_rgbaf32_unchecked(channel(pixel.r()), channel(pixel.g()), channel(pixel.b()), pixel.a()) - } - image -} - -#[derive(Debug, Clone, Copy)] -pub struct GammaNode { - gamma: G, -} - -// https://www.dfstudios.co.uk/articles/programming/image-programming-algorithms/image-processing-algorithms-part-6-gamma-correction/ -#[node_macro::node_fn(GammaNode)] -fn image_gamma(image: Image, gamma: f64) -> Image { - let mut image = image; - let inverse_gamma = 1. / gamma; - let channel = |channel: f32| channel.powf(inverse_gamma as f32); - for pixel in &mut image.data { - *pixel = Color::from_rgbaf32_unchecked(channel(pixel.r()), channel(pixel.g()), channel(pixel.b()), pixel.a()) - } - image -} - -#[derive(Debug, Clone, Copy)] -pub struct OpacityNode { - opacity_multiplier: O, -} - -#[node_macro::node_fn(OpacityNode)] -fn image_opacity(image: Image, opacity_multiplier: f64) -> Image { - let mut image = image; - let opacity_multiplier = opacity_multiplier as f32; - for pixel in &mut image.data { - *pixel = Color::from_rgbaf32_unchecked(pixel.r(), pixel.g(), pixel.b(), pixel.a() * opacity_multiplier) - } - image -} - -#[derive(Debug, Clone, Copy)] -pub struct PosterizeNode

{ - posterize_value: P, -} - -// Based on http://www.axiomx.com/posterize.htm -#[node_macro::node_fn(PosterizeNode)] -fn posterize(image: Image, posterize_value: f64) -> Image { - let mut image = image; - let posterize_value = posterize_value as f32; - let number_of_areas = posterize_value.recip(); - let size_of_areas = (posterize_value - 1.).recip(); - let channel = |channel: f32| (channel / number_of_areas).floor() * size_of_areas; - for pixel in &mut image.data { - *pixel = Color::from_rgbaf32_unchecked(channel(pixel.r()), channel(pixel.g()), channel(pixel.b()), pixel.a()) - } - image -} - -#[derive(Debug, Clone, Copy)] -pub struct ExposureNode { - exposure: E, -} - -// Based on https://stackoverflow.com/questions/12166117/what-is-the-math-behind-exposure-adjustment-on-photoshop -#[node_macro::node_fn(ExposureNode)] -fn exposure(image: Image, exposure: f64) -> Image { - let mut image = image; - let multiplier = 2f32.powf(exposure as f32); - let channel = |channel: f32| channel * multiplier; - for pixel in &mut image.data { - *pixel = Color::from_rgbaf32_unchecked(channel(pixel.r()), channel(pixel.g()), channel(pixel.b()), pixel.a()) - } - image -} - #[derive(Debug, Clone, Copy)] pub struct ImaginateNode { cached: E, } -// Based on https://stackoverflow.com/questions/12166117/what-is-the-math-behind-exposure-adjustment-on-photoshop #[node_macro::node_fn(ImaginateNode)] fn imaginate(image: Image, cached: Option>) -> Image { info!("Imaginating image with {} pixels", image.data.len()); diff --git a/node-graph/interpreted-executor/src/node_registry.rs b/node-graph/interpreted-executor/src/node_registry.rs index e61187ba..9a47f4f7 100644 --- a/node-graph/interpreted-executor/src/node_registry.rs +++ b/node-graph/interpreted-executor/src/node_registry.rs @@ -26,6 +26,19 @@ macro_rules! register_node { }) }; } +macro_rules! raster_node { + ($path:ty, params: [$($type:ty),*]) => { + ( {NodeIdentifier::new(stringify!($path), &[concrete!("Image"), $(concrete!(stringify!($type))),*])}, + |args| { + let mut args = args.clone(); + args.reverse(); + let node = <$path>::new($(graphene_std::any::input_node::<$type>(args.pop().expect("Not enough arguments provided to construct node"))),*); + let map_node = graphene_std::raster::MapImageNode::new(graphene_core::value::ValueNode::new(node)); + let any: DynAnyNode = graphene_std::any::DynAnyNode::new(graphene_core::value::ValueNode::new(map_node)); + Box::pin(any) as TypeErasedPinned + }) + }; +} //TODO: turn into hashmap static NODE_REGISTRY: &[(NodeIdentifier, NodeConstructor)] = &[ @@ -45,28 +58,28 @@ static NODE_REGISTRY: &[(NodeIdentifier, NodeConstructor)] = &[ register_node!(graphene_core::ops::AddParameterNode<_>, input: &f64, params: [f64]), register_node!(graphene_core::ops::AddParameterNode<_>, input: f64, params: [&f64]), register_node!(graphene_core::ops::AddParameterNode<_>, input: &f64, params: [&f64]), - register_node!(graphene_core::raster::GrayscaleColorNode, input: Color, params: []), - register_node!(graphene_core::raster::BrightenColorNode<_>, input: Color, params: [f32]), - register_node!(graphene_core::raster::HueShiftColorNode<_>, input: Color, params: [f32]), (NodeIdentifier::new("graphene_core::structural::ComposeNode<_, _, _>", &[generic!("T"), generic!("U")]), |args| { let node = ComposeTypeErased::new(args[0], args[1]); node.into_type_erased() }), (NodeIdentifier::new("graphene_core::ops::IdNode", &[generic!("T")]), |_| IdNode::new().into_type_erased()), - register_node!(graphene_std::raster::GrayscaleNode, input: Image, params: []), - register_node!(graphene_std::raster::InvertRGBNode, input: Image, params: []), + // Filters + raster_node!(graphene_core::raster::GrayscaleNode, params: []), + raster_node!(graphene_core::raster::HueSaturationNode<_, _, _>, params: [f64, f64, f64]), + raster_node!(graphene_core::raster::InvertRGBNode, params: []), + raster_node!(graphene_core::raster::ThresholdNode<_>, params: [f64]), + raster_node!(graphene_core::raster::VibranceNode<_>, params: [f64]), + raster_node!(graphene_core::raster::BrightnessContrastNode< _, _>, params: [f64, f64]), + raster_node!(graphene_core::raster::GammaNode<_>, params: [f64]), + raster_node!(graphene_core::raster::OpacityNode<_>, params: [f64]), + raster_node!(graphene_core::raster::PosterizeNode<_>, params: [f64]), + raster_node!(graphene_core::raster::ExposureNode<_>, params: [f64]), (NodeIdentifier::new("graphene_core::structural::MapImageNode", &[]), |args| { let map_fn: DowncastBothNode = DowncastBothNode::new(args[0]); let node = graphene_std::raster::MapImageNode::new(ValueNode::new(map_fn)); let any: DynAnyNode = graphene_std::any::DynAnyNode::new(graphene_core::value::ValueNode::new(node)); any.into_type_erased() }), - register_node!(graphene_std::raster::HueSaturationNode<_, _, _>, input: Image, params: [f64, f64, f64]), - register_node!(graphene_std::raster::BrightnessContrastNode< _, _>, input: Image, params: [f64, f64]), - register_node!(graphene_std::raster::GammaNode<_>, input: Image, params: [f64]), - register_node!(graphene_std::raster::OpacityNode<_>, input: Image, params: [f64]), - register_node!(graphene_std::raster::PosterizeNode<_>, input: Image, params: [f64]), - register_node!(graphene_std::raster::ExposureNode<_>, input: Image, params: [f64]), ( NodeIdentifier::new("graphene_std::raster::ImaginateNode<_>", &[concrete!("Image"), concrete!("Option>")]), |args| { @@ -84,8 +97,7 @@ static NODE_REGISTRY: &[(NodeIdentifier, NodeConstructor)] = &[ let empty: TypeNode<_, (), Image> = TypeNode::new(empty_image.then(CloneNode::new())); //let image = &image as &dyn for<'a> Node<'a, (), Output = &'a Image>; - // dirty hack: we abuse that the cache node will ignore the input if it is - // evaluated a second time + // dirty hack: we abuse that the cache node will ignore the input if it is evaluated a second time let image = empty.then(image).then(ImageRefNode::new()); let window = WindowNode::new(radius, image.clone()); @@ -276,6 +288,7 @@ pub fn constrcut_node<'a>(ident: NodeIdentifier, construction_args: Vec