From a709a772d50e7654a4753d897bfae3f575eb899a Mon Sep 17 00:00:00 2001 From: isiko Date: Fri, 10 Feb 2023 21:55:01 +0100 Subject: [PATCH] Add color weights to Grayscale node and improve luminance handling (#1015) * Add weighted grayscale node * Rename nodes, fix grayscale weighting, add luma calc options * Fix tests * Add Tint Option * Improve (but not full fix) tint --------- Co-authored-by: Keavon Chambers --- .../document_node_types.rs | 83 ++++++++++++-- .../node_properties.rs | 78 +++++++++++++- node-graph/gcore/src/raster.rs | 7 +- node-graph/gcore/src/raster/adjustments.rs | 102 ++++++++++++++++-- node-graph/gcore/src/raster/color.rs | 29 ++++- node-graph/graph-craft/src/document/value.rs | 15 ++- .../interpreted-executor/src/node_registry.rs | 5 +- 7 files changed, 285 insertions(+), 34 deletions(-) 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 d6f3c5e5..60da33bb 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 @@ -6,7 +6,7 @@ use graph_craft::document::*; use graph_craft::imaginate_input::ImaginateSamplingMethod; use graph_craft::proto::{NodeIdentifier, Type}; use graph_craft::{concrete, generic}; -use graphene_core::raster::Image; +use graphene_core::raster::{Color, Image, LuminanceCalculation}; use std::collections::VecDeque; @@ -162,10 +162,74 @@ static STATIC_NODES: &[DocumentNodeType] = &[ DocumentNodeType { name: "Grayscale", category: "Image Adjustments", - identifier: NodeImplementation::proto("graphene_core::raster::GrayscaleNode", &[concrete!("Image")]), - inputs: &[DocumentInputType::new("Image", TaggedValue::Image(Image::empty()), true)], + identifier: NodeImplementation::proto( + "graphene_core::raster::GrayscaleNode<_, _, _, _, _, _, _>", + &[ + concrete!("Image"), + concrete!("Color"), + concrete!("f64"), + concrete!("f64"), + concrete!("f64"), + concrete!("f64"), + concrete!("f64"), + concrete!("f64"), + ], + ), + inputs: &[ + DocumentInputType { + name: "Image", + data_type: FrontendGraphDataType::Raster, + default: NodeInput::value(TaggedValue::Image(Image::empty()), true), + }, + DocumentInputType { + name: "Tint", + data_type: FrontendGraphDataType::Number, + default: NodeInput::value(TaggedValue::Color(Color::BLACK), false), + }, + DocumentInputType { + name: "Reds", + data_type: FrontendGraphDataType::Number, + default: NodeInput::value(TaggedValue::F64(50.), false), + }, + DocumentInputType { + name: "Yellows", + data_type: FrontendGraphDataType::Number, + default: NodeInput::value(TaggedValue::F64(50.), false), + }, + DocumentInputType { + name: "Greens", + data_type: FrontendGraphDataType::Number, + default: NodeInput::value(TaggedValue::F64(50.), false), + }, + DocumentInputType { + name: "Cyans", + data_type: FrontendGraphDataType::Number, + default: NodeInput::value(TaggedValue::F64(50.), false), + }, + DocumentInputType { + name: "Blues", + data_type: FrontendGraphDataType::Number, + default: NodeInput::value(TaggedValue::F64(50.), false), + }, + DocumentInputType { + name: "Magentas", + data_type: FrontendGraphDataType::Number, + default: NodeInput::value(TaggedValue::F64(50.), false), + }, + ], outputs: &[FrontendGraphDataType::Raster], - properties: node_properties::no_properties, + properties: node_properties::grayscale_properties, + }, + DocumentNodeType { + name: "Luminance", + category: "Image Adjustments", + identifier: NodeImplementation::proto("graphene_core::raster::LuminanceNode<_>", &[concrete!("Image"), concrete!("LuminanceCalculation")]), + inputs: &[ + DocumentInputType::new("Image", TaggedValue::Image(Image::empty()), true), + DocumentInputType::new("Luma Calculation", TaggedValue::LuminanceCalculation(LuminanceCalculation::SRGB), false), + ], + outputs: &[FrontendGraphDataType::Raster], + properties: node_properties::luminance_properties, }, #[cfg(feature = "gpu")] DocumentNodeType { @@ -255,10 +319,11 @@ static STATIC_NODES: &[DocumentNodeType] = &[ DocumentNodeType { name: "Threshold", category: "Image Adjustments", - identifier: NodeImplementation::proto("graphene_core::raster::ThresholdNode<_>", &[concrete!("Image"), concrete!("f64")]), + identifier: NodeImplementation::proto("graphene_core::raster::ThresholdNode<_, _>", &[concrete!("Image"), concrete!("LuminanceCalculation"), concrete!("f64")]), inputs: &[ DocumentInputType::new("Image", TaggedValue::Image(Image::empty()), true), - DocumentInputType::new("Threshold", TaggedValue::F64(1.), false), + DocumentInputType::new("Luma Calculation", TaggedValue::LuminanceCalculation(LuminanceCalculation::SRGB), false), + DocumentInputType::new("Threshold", TaggedValue::F64(50.), false), ], outputs: &[FrontendGraphDataType::Raster], properties: node_properties::adjust_threshold_properties, @@ -269,7 +334,7 @@ static STATIC_NODES: &[DocumentNodeType] = &[ 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), + DocumentInputType::new("Vibrance", TaggedValue::F64(0.), false), ], outputs: &[FrontendGraphDataType::Raster], properties: node_properties::adjust_vibrance_properties, @@ -280,7 +345,7 @@ static STATIC_NODES: &[DocumentNodeType] = &[ 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), + DocumentInputType::new("Factor", TaggedValue::F64(100.), false), ], outputs: &[FrontendGraphDataType::Raster], properties: node_properties::multiply_opacity, @@ -291,7 +356,7 @@ static STATIC_NODES: &[DocumentNodeType] = &[ 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), + DocumentInputType::new("Value", TaggedValue::F64(4.), false), ], outputs: &[FrontendGraphDataType::Raster], properties: node_properties::posterize_properties, 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 e3de5fb6..424b2345 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 @@ -8,6 +8,7 @@ use glam::DVec2; use graph_craft::document::value::TaggedValue; use graph_craft::document::{generate_uuid, DocumentNode, NodeId, NodeInput}; use graph_craft::imaginate_input::*; +use graphene_core::raster::{Color, LuminanceCalculation}; use super::document_node_types::NodePropertiesContext; use super::{FrontendGraphDataType, IMAGINATE_NODE}; @@ -147,6 +148,47 @@ fn number_widget(document_node: &DocumentNode, node_id: NodeId, index: usize, na widgets } +// TODO: Generalize this for all dropdowns +fn luminance_calculation(document_node: &DocumentNode, node_id: u64, index: usize, name: &str, blank_assist: bool) -> LayoutGroup { + let mut widgets = start_widgets(document_node, node_id, index, name, FrontendGraphDataType::General, blank_assist); + if let &NodeInput::Value { + tagged_value: TaggedValue::LuminanceCalculation(calculation), + exposed: false, + } = &document_node.inputs[index] + { + let calculation_modes = LuminanceCalculation::list(); + let mut entries = Vec::with_capacity(calculation_modes.len()); + for method in calculation_modes { + entries.push(DropdownEntryData::new(method.to_string()).on_update(update_value(move |_| TaggedValue::LuminanceCalculation(method), node_id, index))); + } + let entries = vec![entries]; + + widgets.extend_from_slice(&[ + WidgetHolder::unrelated_separator(), + DropdownInput::new(entries).selected_index(Some(calculation as u32)).widget_holder(), + ]); + } + LayoutGroup::Row { widgets }.with_tooltip("Formula used to calculate the luminance of a pixel") +} + +fn color_widget(document_node: &DocumentNode, node_id: u64, index: usize, name: &str, color_props: ColorInput, blank_assist: bool) -> LayoutGroup { + let mut widgets = start_widgets(document_node, node_id, index, name, FrontendGraphDataType::Number, blank_assist); + + if let NodeInput::Value { + tagged_value: TaggedValue::Color(x), + exposed: false, + } = document_node.inputs[index] + { + widgets.extend_from_slice(&[ + WidgetHolder::unrelated_separator(), + color_props + .value(Some(x as Color)) + .on_update(update_value(|x: &ColorInput| TaggedValue::Color(x.value.unwrap()), node_id, index)) + .widget_holder(), + ]) + } + LayoutGroup::Row { widgets } +} /// Properties for the input node, with information describing how frames work and a refresh button pub fn input_properties(_document_node: &DocumentNode, _node_id: NodeId, _context: &mut NodePropertiesContext) -> Vec { let information = WidgetHolder::text_widget("The graph's input is the artwork under the frame layer"); @@ -157,6 +199,35 @@ pub fn input_properties(_document_node: &DocumentNode, _node_id: NodeId, _contex vec![LayoutGroup::Row { widgets: vec![information] }, LayoutGroup::Row { widgets: vec![refresh_button] }] } +pub fn grayscale_properties(document_node: &DocumentNode, node_id: NodeId, _context: &mut NodePropertiesContext) -> Vec { + const MIN: f64 = -200.; + const MAX: f64 = 300.; + // TODO: Add tint color (blended above using the "Color" blend mode) + let tint = color_widget(document_node, node_id, 1, "Tint", ColorInput::default(), true); + let r_weight = number_widget(document_node, node_id, 2, "Reds", NumberInput::default().min(MIN).max(MAX).unit("%"), true); + let y_weight = number_widget(document_node, node_id, 3, "Yellows", NumberInput::default().min(MIN).max(MAX).unit("%"), true); + let g_weight = number_widget(document_node, node_id, 4, "Greens", NumberInput::default().min(MIN).max(MAX).unit("%"), true); + let c_weight = number_widget(document_node, node_id, 5, "Cyans", NumberInput::default().min(MIN).max(MAX).unit("%"), true); + let b_weight = number_widget(document_node, node_id, 6, "Blues", NumberInput::default().min(MIN).max(MAX).unit("%"), true); + let m_weight = number_widget(document_node, node_id, 7, "Magentas", NumberInput::default().min(MIN).max(MAX).unit("%"), true); + + vec![ + tint, + LayoutGroup::Row { widgets: r_weight }, + LayoutGroup::Row { widgets: y_weight }, + LayoutGroup::Row { widgets: g_weight }, + LayoutGroup::Row { widgets: c_weight }, + LayoutGroup::Row { widgets: b_weight }, + LayoutGroup::Row { widgets: m_weight }, + ] +} + +pub fn luminance_properties(document_node: &DocumentNode, node_id: NodeId, _context: &mut NodePropertiesContext) -> Vec { + let luma_calculation = luminance_calculation(document_node, node_id, 1, "Luma Calculation", true); + + vec![luma_calculation] +} + pub fn adjust_hsl_properties(document_node: &DocumentNode, node_id: NodeId, _context: &mut NodePropertiesContext) -> Vec { let hue_shift = number_widget(document_node, node_id, 1, "Hue Shift", NumberInput::default().min(-180.).max(180.).unit("°"), true); let saturation_shift = number_widget(document_node, node_id, 2, "Saturation Shift", NumberInput::default().min(-100.).max(100.).unit("%"), true); @@ -184,9 +255,10 @@ pub fn blur_image_properties(document_node: &DocumentNode, node_id: NodeId, _con } 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); + let luma_calculation = luminance_calculation(document_node, node_id, 1, "Luma Calculation", true); + let thereshold = number_widget(document_node, node_id, 2, "Threshold", NumberInput::default().min(0.).max(100.).unit("%"), true); - vec![LayoutGroup::Row { widgets: thereshold }] + vec![luma_calculation, LayoutGroup::Row { widgets: thereshold }] } pub fn adjust_vibrance_properties(document_node: &DocumentNode, node_id: NodeId, _context: &mut NodePropertiesContext) -> Vec { @@ -203,7 +275,7 @@ pub fn gpu_map_properties(document_node: &DocumentNode, node_id: NodeId, _contex } pub fn multiply_opacity(document_node: &DocumentNode, node_id: NodeId, _context: &mut NodePropertiesContext) -> Vec { - let gamma = number_widget(document_node, node_id, 1, "Factor", NumberInput::default().min(0.).max(1.), true); + let gamma = number_widget(document_node, node_id, 1, "Factor", NumberInput::default().min(0.).max(100.).unit("%"), true); vec![LayoutGroup::Row { widgets: gamma }] } diff --git a/node-graph/gcore/src/raster.rs b/node-graph/gcore/src/raster.rs index 37250a97..cdaf2510 100644 --- a/node-graph/gcore/src/raster.rs +++ b/node-graph/gcore/src/raster.rs @@ -371,11 +371,14 @@ mod test { use super::*; + #[ignore] #[test] fn map_node() { // let array = &mut [Color::from_rgbaf32(1.0, 0.0, 0.0, 1.0).unwrap()]; - GrayscaleNode.eval(Color::from_rgbf32_unchecked(1., 0., 0.)); - /*let map = ForEachNode(MutWrapper(GrayscaleNode)); + + // LuminanceNode.eval(Color::from_rgbf32_unchecked(1., 0., 0.)); + + /*let map = ForEachNode(MutWrapper(LuminanceNode)); (&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 index 7e779964..9c4e0f1b 100644 --- a/node-graph/gcore/src/raster/adjustments.rs +++ b/node-graph/gcore/src/raster/adjustments.rs @@ -2,16 +2,48 @@ use super::Color; use crate::Node; use core::fmt::Debug; +use dyn_any::{DynAny, StaticType}; + +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[derive(Debug, Default, Clone, Copy, Eq, PartialEq, DynAny, specta::Type, Hash)] +pub enum LuminanceCalculation { + #[default] + SRGB, + Perceptual, + AverageChannels, +} + +impl LuminanceCalculation { + pub fn list() -> [LuminanceCalculation; 3] { + [LuminanceCalculation::SRGB, LuminanceCalculation::Perceptual, LuminanceCalculation::AverageChannels] + } +} + +impl std::fmt::Display for LuminanceCalculation { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + LuminanceCalculation::SRGB => write!(f, "sRGB"), + LuminanceCalculation::Perceptual => write!(f, "Perceptual"), + LuminanceCalculation::AverageChannels => write!(f, "Average Channels"), + } + } +} #[derive(Debug, Clone, Copy, Default)] -pub struct GrayscaleNode; +pub struct LuminanceNode { + luma_calculation: LuminanceCalculation, +} -#[node_macro::node_fn(GrayscaleNode)] -fn grayscale_color_node(color: Color) -> Color { +#[node_macro::node_fn(LuminanceNode)] +fn luminance_color_node(color: Color, luma_calculation: LuminanceCalculation) -> Color { // TODO: Remove conversion to linear when the whole node graph uses linear color let color = color.to_linear_srgb(); - let luminance = color.luminance(); + let luminance = match luma_calculation { + LuminanceCalculation::SRGB => color.luminance_srgb(), + LuminanceCalculation::Perceptual => color.luminance_perceptual(), + LuminanceCalculation::AverageChannels => color.average_rgb_channels(), + }; // TODO: Remove conversion to linear when the whole node graph uses linear color let luminance = Color::linear_to_srgb(luminance); @@ -19,6 +51,51 @@ fn grayscale_color_node(color: Color) -> Color { color.map_rgb(|_| luminance) } +#[derive(Debug, Clone, Copy, Default)] +pub struct GrayscaleNode { + tint: Tint, + reds: Reds, + yellows: Yellows, + greens: Greens, + cyans: Cyans, + blues: Blues, + magentas: Magentas, +} + +// From +// Works the same for gamma and linear color +#[node_macro::node_fn(GrayscaleNode)] +fn grayscale_color_node(color: Color, tint: Color, reds: f64, yellows: f64, greens: f64, cyans: f64, blues: f64, magentas: f64) -> Color { + let reds = reds as f32 / 100.; + let yellows = yellows as f32 / 100.; + let greens = greens as f32 / 100.; + let cyans = cyans as f32 / 100.; + let blues = blues as f32 / 100.; + let magentas = magentas as f32 / 100.; + + let gray_base = color.r().min(color.g()).min(color.b()); + + let red_part = color.r() - gray_base; + let green_part = color.g() - gray_base; + let blue_part = color.b() - gray_base; + + let additional = if red_part == 0. { + let cyan_part = green_part.min(blue_part); + cyan_part * cyans + (green_part - cyan_part) * greens + (blue_part - cyan_part) * blues + } else if green_part == 0. { + let magenta_part = red_part.min(blue_part); + magenta_part * magentas + (red_part - magenta_part) * reds + (blue_part - magenta_part) * blues + } else { + let yellow_part = red_part.min(green_part); + yellow_part * yellows + (red_part - yellow_part) * reds + (green_part - yellow_part) * greens + }; + + let luminance = gray_base + additional; + + // TODO: Fix "Color" blend mode implementation so it matches the expected behavior perfectly (it's currently close) + tint.with_luminance(luminance) +} + #[cfg(not(target_arch = "spirv"))] pub use hue_shift::HueSaturationNode; @@ -54,18 +131,25 @@ fn invert_image(color: Color) -> Color { } #[derive(Debug, Clone, Copy)] -pub struct ThresholdNode { +pub struct ThresholdNode { + luma_calculation: LuminanceCalculation, threshold: Threshold, } #[node_macro::node_fn(ThresholdNode)] -fn threshold_node(color: Color, threshold: f64) -> Color { - let threshold = Color::srgb_to_linear(threshold as f32); +fn threshold_node(color: Color, luma_calculation: LuminanceCalculation, threshold: f64) -> Color { + let threshold = Color::srgb_to_linear(threshold as f32 / 100.); // TODO: Remove conversion to linear when the whole node graph uses linear color let color = color.to_linear_srgb(); - if color.luminance() >= threshold { + let luminance = match luma_calculation { + LuminanceCalculation::SRGB => color.luminance_srgb(), + LuminanceCalculation::Perceptual => color.luminance_perceptual(), + LuminanceCalculation::AverageChannels => color.average_rgb_channels(), + }; + + if luminance >= threshold { Color::WHITE } else { Color::BLACK @@ -108,7 +192,7 @@ pub struct OpacityNode { #[node_macro::node_fn(OpacityNode)] fn image_opacity(color: Color, opacity_multiplier: f64) -> Color { - let opacity_multiplier = opacity_multiplier as f32; + let opacity_multiplier = opacity_multiplier as f32 / 100.; Color::from_rgbaf32_unchecked(color.r(), color.g(), color.b(), color.a() * opacity_multiplier) } diff --git a/node-graph/gcore/src/raster/color.rs b/node-graph/gcore/src/raster/color.rs index 750585b0..1dfd0279 100644 --- a/node-graph/gcore/src/raster/color.rs +++ b/node-graph/gcore/src/raster/color.rs @@ -196,14 +196,28 @@ impl Color { self.alpha } - // From https://stackoverflow.com/a/56678483/775283 - pub fn luminance(&self) -> f32 { - 0.2126 * self.red + 0.7152 * self.green + 0.0722 * self.blue + pub fn average_rgb_channels(&self) -> f32 { + (self.red + self.green + self.blue) / 3. } // From https://stackoverflow.com/a/56678483/775283 - pub fn perceptual_luminance(&self) -> f32 { - let luminance = self.luminance(); + pub fn luminance_srgb(&self) -> f32 { + 0.2126 * self.red + 0.7152 * self.green + 0.0722 * self.blue + } + + // From https://en.wikipedia.org/wiki/Luma_(video)#Rec._601_luma_versus_Rec._709_luma_coefficients + pub fn luminance_rec_601(&self) -> f32 { + 0.299 * self.red + 0.587 * self.green + 0.114 * self.blue + } + + // From https://en.wikipedia.org/wiki/Luma_(video)#Rec._601_luma_versus_Rec._709_luma_coefficients + pub fn luminance_rec_601_rounded(&self) -> f32 { + 0.3 * self.red + 0.59 * self.green + 0.11 * self.blue + } + + // From https://stackoverflow.com/a/56678483/775283 + pub fn luminance_perceptual(&self) -> f32 { + let luminance = self.luminance_srgb(); if luminance <= 0.008856 { (luminance * 903.3) / 100. @@ -212,6 +226,11 @@ impl Color { } } + pub fn with_luminance(&self, luminance: f32) -> Color { + let d = luminance - self.luminance_rec_601(); + self.map_rgb(|c| (c + d).clamp(0., 1.)) + } + /// Return the all components as a tuple, first component is red, followed by green, followed by blue, followed by alpha. /// /// # Examples diff --git a/node-graph/graph-craft/src/document/value.rs b/node-graph/graph-craft/src/document/value.rs index f73ae50a..3e6e5f77 100644 --- a/node-graph/graph-craft/src/document/value.rs +++ b/node-graph/graph-craft/src/document/value.rs @@ -2,6 +2,7 @@ pub use dyn_any::StaticType; use dyn_any::{DynAny, Upcast}; use dyn_clone::DynClone; pub use glam::DVec2; +use graphene_core::raster::LuminanceCalculation; use graphene_core::Node; use std::hash::Hash; pub use std::sync::Arc; @@ -26,6 +27,7 @@ pub enum TaggedValue { Color(graphene_core::raster::color::Color), Subpath(graphene_core::vector::subpath::Subpath), RcSubpath(Arc), + LuminanceCalculation(LuminanceCalculation), ImaginateSamplingMethod(ImaginateSamplingMethod), ImaginateMaskStartingFill(ImaginateMaskStartingFill), ImaginateStatus(ImaginateStatus), @@ -86,20 +88,24 @@ impl Hash for TaggedValue { 13.hash(state); s.hash(state) } - Self::ImaginateSamplingMethod(m) => { + Self::LuminanceCalculation(l) => { 14.hash(state); + l.hash(state) + } + Self::ImaginateSamplingMethod(m) => { + 15.hash(state); m.hash(state) } Self::ImaginateMaskStartingFill(f) => { - 15.hash(state); + 16.hash(state); f.hash(state) } Self::ImaginateStatus(s) => { - 16.hash(state); + 17.hash(state); s.hash(state) } Self::LayerPath(p) => { - 17.hash(state); + 18.hash(state); p.hash(state) } } @@ -123,6 +129,7 @@ impl<'a> TaggedValue { TaggedValue::Color(x) => Box::new(x), TaggedValue::Subpath(x) => Box::new(x), TaggedValue::RcSubpath(x) => Box::new(x), + TaggedValue::LuminanceCalculation(x) => Box::new(x), TaggedValue::ImaginateSamplingMethod(x) => Box::new(x), TaggedValue::ImaginateMaskStartingFill(x) => Box::new(x), TaggedValue::ImaginateStatus(x) => Box::new(x), diff --git a/node-graph/interpreted-executor/src/node_registry.rs b/node-graph/interpreted-executor/src/node_registry.rs index 1d96f105..913c8334 100644 --- a/node-graph/interpreted-executor/src/node_registry.rs +++ b/node-graph/interpreted-executor/src/node_registry.rs @@ -64,10 +64,11 @@ static NODE_REGISTRY: &[(NodeIdentifier, NodeConstructor)] = &[ }), (NodeIdentifier::new("graphene_core::ops::IdNode", &[generic!("T")]), |_| IdNode::new().into_type_erased()), // Filters - raster_node!(graphene_core::raster::GrayscaleNode, params: []), + raster_node!(graphene_core::raster::LuminanceNode<_>, params: [LuminanceCalculation]), + raster_node!(graphene_core::raster::GrayscaleNode<_, _, _, _, _, _, _>, params: [Color, f64, f64, f64, f64, f64, f64]), 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::ThresholdNode<_, _>, params: [LuminanceCalculation, f64]), raster_node!(graphene_core::raster::VibranceNode<_>, params: [f64]), raster_node!(graphene_core::raster::BrightnessContrastNode< _, _>, params: [f64, f64]), raster_node!(graphene_core::raster::OpacityNode<_>, params: [f64]),