From d51dc352bf501d7dd01354bef4bf3ce072c0bee0 Mon Sep 17 00:00:00 2001 From: isiko404 Date: Wed, 15 Mar 2023 18:51:17 +0100 Subject: [PATCH 1/3] Add MaskImageNode --- .../document_node_types.rs | 11 ++++++ .../node_properties.rs | 6 ++++ node-graph/gstd/src/raster.rs | 36 +++++++++++++++++++ .../interpreted-executor/src/node_registry.rs | 1 + 4 files changed, 54 insertions(+) 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 db09412b546..a04fc3006cf 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 @@ -286,6 +286,17 @@ fn static_nodes() -> Vec { outputs: vec![DocumentOutputType::new("Image", FrontendGraphDataType::Raster)], properties: |_document_node, _node_id, _context| node_properties::string_properties("Creates an embedded image with the given transform"), }, + DocumentNodeType { + name: "Mask Node", + category: "Image Adjustments", + identifier: NodeImplementation::proto("graphene_std::raster::MaskImageNode<_>"), + inputs: vec![ + DocumentInputType::value("Image", TaggedValue::ImageFrame(ImageFrame::empty()), true), + DocumentInputType::value("Mask", TaggedValue::ImageFrame(ImageFrame::empty()), true), + ], + outputs: vec![DocumentOutputType::new("Image", FrontendGraphDataType::Raster)], + properties: node_properties::mask_properties, + }, DocumentNodeType { name: "Blend Node", category: "Image Adjustments", 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 67ee2251ef8..7f94b1db895 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 @@ -477,6 +477,12 @@ pub fn blend_properties(document_node: &DocumentNode, node_id: NodeId, _context: vec![backdrop, blend_mode, LayoutGroup::Row { widgets: opacity }] } +pub fn mask_properties(document_node: &DocumentNode, node_id: NodeId, _context: &mut NodePropertiesContext) -> Vec { + let mask = color_widget(document_node, node_id, 1, "Backdrop", ColorInput::default(), true); + + vec![mask] +} + pub fn luminance_properties(document_node: &DocumentNode, node_id: NodeId, _context: &mut NodePropertiesContext) -> Vec { let luminance_calc = luminance_calculation(document_node, node_id, 1, "Luminance Calc", true); diff --git a/node-graph/gstd/src/raster.rs b/node-graph/gstd/src/raster.rs index 1bc3ed8b8d4..8d8f10827ac 100644 --- a/node-graph/gstd/src/raster.rs +++ b/node-graph/gstd/src/raster.rs @@ -194,6 +194,42 @@ fn compute_transformed_bounding_box(transform: DAffine2) -> Bbox { } } +#[derive(Debug, Clone, Copy)] +pub struct MaskImageNode { + mask: Mask, +} + +#[node_macro::node_fn(MaskImageNode)] +fn mask_image(mut image: ImageFrame, mask: ImageFrame) -> ImageFrame { + let image_size = DVec2::new(image.image.width as f64, image.image.height as f64); + let mask_size = DVec2::new(mask.image.width as f64, mask.image.height as f64); + + if mask_size == DVec2::ZERO { + log::debug!("{}", mask.image.data.len()); + return image; + } + + // Transforms a point from the background image to the forground image + let bg_to_fg = DAffine2::from_scale(mask_size) * mask.transform.inverse() * image.transform * DAffine2::from_scale(1. / image_size); + + for y in 0..image.image.height { + for x in 0..image.image.width { + let image_point = DVec2::new(x as f64, y as f64); + let mut mask_point = bg_to_fg.transform_point2(image_point); + mask_point = mask_point.clamp(DVec2::ZERO, mask_size); + + let image_pixel = image.get_mut(x as usize, y as usize); + let mask_pixel = mask.sample(mask_point); + + let alpha = image_pixel.a() * mask_pixel.r(); + + *image_pixel = Color::from_rgbaf32(image_pixel.r(), image_pixel.g(), image_pixel.b(), alpha).unwrap(); + } + } + + image +} + #[derive(Debug, Clone, Copy)] pub struct BlendImageNode { background: Background, diff --git a/node-graph/interpreted-executor/src/node_registry.rs b/node-graph/interpreted-executor/src/node_registry.rs index 59d968ad434..a20e8d97650 100644 --- a/node-graph/interpreted-executor/src/node_registry.rs +++ b/node-graph/interpreted-executor/src/node_registry.rs @@ -136,6 +136,7 @@ fn node_registry() -> HashMap, input: &f64, params: [&f64]), register_node!(graphene_core::ops::SomeNode, input: ImageFrame, params: []), register_node!(graphene_std::raster::DownscaleNode, input: ImageFrame, params: []), + register_node!(graphene_std::raster::MaskImageNode<_>, input: ImageFrame, params: [ImageFrame]), #[cfg(feature = "gpu")] register_node!(graphene_std::executor::MapGpuSingleImageNode<_>, input: Image, params: [String]), vec![( From 0a929751f373cf2d7a1db75d5a91f5511950deb6 Mon Sep 17 00:00:00 2001 From: Keavon Chambers Date: Mon, 27 Mar 2023 10:42:48 -0700 Subject: [PATCH 2/3] Rename mask parameter to Stencil --- .../node_graph_message_handler/document_node_types.rs | 2 +- .../node_graph_message_handler/node_properties.rs | 2 +- node-graph/gcore/src/raster.rs | 2 +- node-graph/gstd/src/raster.rs | 8 +++++++- 4 files changed, 10 insertions(+), 4 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 a04fc3006cf..82ac57915b6 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 @@ -292,7 +292,7 @@ fn static_nodes() -> Vec { identifier: NodeImplementation::proto("graphene_std::raster::MaskImageNode<_>"), inputs: vec![ DocumentInputType::value("Image", TaggedValue::ImageFrame(ImageFrame::empty()), true), - DocumentInputType::value("Mask", TaggedValue::ImageFrame(ImageFrame::empty()), true), + DocumentInputType::value("Stencil", TaggedValue::ImageFrame(ImageFrame::empty()), true), ], outputs: vec![DocumentOutputType::new("Image", FrontendGraphDataType::Raster)], properties: node_properties::mask_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 7f94b1db895..fd907e3dacd 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 @@ -478,7 +478,7 @@ pub fn blend_properties(document_node: &DocumentNode, node_id: NodeId, _context: } pub fn mask_properties(document_node: &DocumentNode, node_id: NodeId, _context: &mut NodePropertiesContext) -> Vec { - let mask = color_widget(document_node, node_id, 1, "Backdrop", ColorInput::default(), true); + let mask = color_widget(document_node, node_id, 1, "Stencil", ColorInput::default(), true); vec![mask] } diff --git a/node-graph/gcore/src/raster.rs b/node-graph/gcore/src/raster.rs index 9e888a70b8a..948d0d094a2 100644 --- a/node-graph/gcore/src/raster.rs +++ b/node-graph/gcore/src/raster.rs @@ -441,7 +441,7 @@ mod image { &mut self.image.data[y * (self.image.width as usize) + x] } - /// Clamps the provided point to (0, 0) (ImageSize) and returns the closest pixel + /// Clamps the provided point to ((0, 0), (ImageSize.x, ImageSize.y)) and returns the closest pixel pub fn sample(&self, position: DVec2) -> Color { let x = position.x.clamp(0., self.image.width as f64 - 1.) as usize; let y = position.y.clamp(0., self.image.height as f64 - 1.) as usize; diff --git a/node-graph/gstd/src/raster.rs b/node-graph/gstd/src/raster.rs index 8d8f10827ac..53e4f00db2d 100644 --- a/node-graph/gstd/src/raster.rs +++ b/node-graph/gstd/src/raster.rs @@ -205,7 +205,6 @@ fn mask_image(mut image: ImageFrame, mask: ImageFrame) -> ImageFrame { let mask_size = DVec2::new(mask.image.width as f64, mask.image.height as f64); if mask_size == DVec2::ZERO { - log::debug!("{}", mask.image.data.len()); return image; } @@ -221,8 +220,15 @@ fn mask_image(mut image: ImageFrame, mask: ImageFrame) -> ImageFrame { let image_pixel = image.get_mut(x as usize, y as usize); let mask_pixel = mask.sample(mask_point); + // TODO: Fix crash when `mask_pixel.r()` is negative + // TODO: This can be reproduced when a mask Stencil input is given which is fed by a Levels node where the first slider is dragged to the right let alpha = image_pixel.a() * mask_pixel.r(); + // TODO: Remove this if statement after fixing the crash + if Color::from_rgbaf32(image_pixel.r(), image_pixel.g(), image_pixel.b(), alpha).is_none() { + log::debug!("image_pixel.a(): {}, mask_pixel.r(): {}", image_pixel.a(), mask_pixel.r()); + log::debug!("r: {}, g: {}, b: {}, a: {}", image_pixel.r(), image_pixel.g(), image_pixel.b(), alpha); + } *image_pixel = Color::from_rgbaf32(image_pixel.r(), image_pixel.g(), image_pixel.b(), alpha).unwrap(); } } From 76b7d38169e5a77274c70d4215cdaa6b0bd51985 Mon Sep 17 00:00:00 2001 From: Keavon Chambers Date: Sat, 8 Apr 2023 19:36:30 -0700 Subject: [PATCH 3/3] Polish --- .../node_graph_message_handler/document_node_types.rs | 4 ++-- node-graph/compilation-client/src/main.rs | 2 +- node-graph/gstd/src/executor.rs | 2 +- node-graph/gstd/src/raster.rs | 8 -------- 4 files changed, 4 insertions(+), 12 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 e062e99e3b7..be234a8d609 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 @@ -286,7 +286,7 @@ fn static_nodes() -> Vec { properties: |_document_node, _node_id, _context| node_properties::string_properties("Creates an embedded image with the given transform"), }, DocumentNodeType { - name: "Mask Node", + name: "Mask", category: "Image Adjustments", identifier: NodeImplementation::proto("graphene_std::raster::MaskImageNode<_>"), inputs: vec![ @@ -297,7 +297,7 @@ fn static_nodes() -> Vec { properties: node_properties::mask_properties, }, DocumentNodeType { - name: "Blend Node", + name: "Blend", category: "Image Adjustments", identifier: NodeImplementation::proto("graphene_core::raster::BlendNode<_, _, _, _>"), inputs: vec![ diff --git a/node-graph/compilation-client/src/main.rs b/node-graph/compilation-client/src/main.rs index a6c770f0200..9cf9206c5fa 100644 --- a/node-graph/compilation-client/src/main.rs +++ b/node-graph/compilation-client/src/main.rs @@ -16,7 +16,7 @@ fn main() { nodes: [( 0, DocumentNode { - name: "Inc Node".into(), + name: "Inc".into(), inputs: vec![NodeInput::Network(concrete!(u32))], implementation: DocumentNodeImplementation::Network(add_network()), metadata: DocumentNodeMetadata::default(), diff --git a/node-graph/gstd/src/executor.rs b/node-graph/gstd/src/executor.rs index bb72985e231..c0495909131 100644 --- a/node-graph/gstd/src/executor.rs +++ b/node-graph/gstd/src/executor.rs @@ -44,7 +44,7 @@ fn map_gpu_single_image(input: Image, node: String) -> Image { nodes: [( 0, DocumentNode { - name: "Image filter Node".into(), + name: "Image Filter".into(), inputs: vec![NodeInput::Network(concrete!(Color))], implementation: DocumentNodeImplementation::Unresolved(identifier), metadata: DocumentNodeMetadata::default(), diff --git a/node-graph/gstd/src/raster.rs b/node-graph/gstd/src/raster.rs index f9576a857b2..5a710f47b48 100644 --- a/node-graph/gstd/src/raster.rs +++ b/node-graph/gstd/src/raster.rs @@ -219,16 +219,8 @@ fn mask_image(mut image: ImageFrame, mask: ImageFrame) -> ImageFrame { let image_pixel = image.get_mut(x as usize, y as usize); let mask_pixel = mask.sample(mask_point); - - // TODO: Fix crash when `mask_pixel.r()` is negative - // TODO: This can be reproduced when a mask Stencil input is given which is fed by a Levels node where the first slider is dragged to the right let alpha = image_pixel.a() * mask_pixel.r(); - // TODO: Remove this if statement after fixing the crash - if Color::from_rgbaf32(image_pixel.r(), image_pixel.g(), image_pixel.b(), alpha).is_none() { - log::debug!("image_pixel.a(): {}, mask_pixel.r(): {}", image_pixel.a(), mask_pixel.r()); - log::debug!("r: {}, g: {}, b: {}, a: {}", image_pixel.r(), image_pixel.g(), image_pixel.b(), alpha); - } *image_pixel = Color::from_rgbaf32(image_pixel.r(), image_pixel.g(), image_pixel.b(), alpha).unwrap(); } }