Add 'Gradient Map' adjustment node
This commit is contained in:
parent
f6ffa45a81
commit
501b562d0f
|
|
@ -2282,7 +2282,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
|
|||
},
|
||||
},
|
||||
category: "Raster",
|
||||
properties: node_properties::adjust_hsl_properties,
|
||||
properties: node_properties::hue_saturation_properties,
|
||||
},
|
||||
DocumentNodeDefinition {
|
||||
identifier: "Brightness/Contrast",
|
||||
|
|
@ -2346,7 +2346,28 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
|
|||
},
|
||||
},
|
||||
category: "Raster",
|
||||
properties: node_properties::adjust_threshold_properties,
|
||||
properties: node_properties::threshold_properties,
|
||||
},
|
||||
DocumentNodeDefinition {
|
||||
identifier: "Gradient Map",
|
||||
node_template: NodeTemplate {
|
||||
document_node: DocumentNode {
|
||||
implementation: DocumentNodeImplementation::proto("graphene_core::raster::GradientMapNode<_, _>"),
|
||||
inputs: vec![
|
||||
NodeInput::value(TaggedValue::ImageFrame(ImageFrame::empty()), true),
|
||||
NodeInput::value(TaggedValue::GradientStops(vector::style::GradientStops::default()), false),
|
||||
NodeInput::value(TaggedValue::Bool(false), false),
|
||||
],
|
||||
..Default::default()
|
||||
},
|
||||
persistent_node_metadata: DocumentNodePersistentMetadata {
|
||||
input_names: vec!["Image".to_string(), "Gradient".to_string()],
|
||||
output_names: vec!["Image".to_string()],
|
||||
..Default::default()
|
||||
},
|
||||
},
|
||||
category: "Raster",
|
||||
properties: node_properties::gradient_map_properties,
|
||||
},
|
||||
DocumentNodeDefinition {
|
||||
identifier: "Vibrance",
|
||||
|
|
@ -2363,7 +2384,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
|
|||
},
|
||||
},
|
||||
category: "Raster",
|
||||
properties: node_properties::adjust_vibrance_properties,
|
||||
properties: node_properties::vibrance_properties,
|
||||
},
|
||||
DocumentNodeDefinition {
|
||||
identifier: "Channel Mixer",
|
||||
|
|
@ -2426,7 +2447,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
|
|||
},
|
||||
},
|
||||
category: "Raster",
|
||||
properties: node_properties::adjust_channel_mixer_properties,
|
||||
properties: node_properties::channel_mixer_properties,
|
||||
},
|
||||
DocumentNodeDefinition {
|
||||
identifier: "Selective Color",
|
||||
|
|
@ -2536,7 +2557,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
|
|||
},
|
||||
},
|
||||
category: "Raster",
|
||||
properties: node_properties::adjust_selective_color_properties,
|
||||
properties: node_properties::selective_color_properties,
|
||||
},
|
||||
DocumentNodeDefinition {
|
||||
identifier: "Opacity",
|
||||
|
|
|
|||
|
|
@ -1091,7 +1091,7 @@ pub fn noise_pattern_properties(document_node: &DocumentNode, node_id: NodeId, _
|
|||
]
|
||||
}
|
||||
|
||||
pub fn adjust_hsl_properties(document_node: &DocumentNode, node_id: NodeId, _context: &mut NodePropertiesContext) -> Vec<LayoutGroup> {
|
||||
pub fn hue_saturation_properties(document_node: &DocumentNode, node_id: NodeId, _context: &mut NodePropertiesContext) -> Vec<LayoutGroup> {
|
||||
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().mode_range().min(-100.).max(100.).unit("%"), true);
|
||||
let lightness_shift = number_widget(document_node, node_id, 3, "Lightness Shift", NumberInput::default().mode_range().min(-100.).max(100.).unit("%"), true);
|
||||
|
|
@ -1128,7 +1128,7 @@ pub fn _blur_image_properties(document_node: &DocumentNode, node_id: NodeId, _co
|
|||
vec![LayoutGroup::Row { widgets: radius }, LayoutGroup::Row { widgets: sigma }]
|
||||
}
|
||||
|
||||
pub fn adjust_threshold_properties(document_node: &DocumentNode, node_id: NodeId, _context: &mut NodePropertiesContext) -> Vec<LayoutGroup> {
|
||||
pub fn threshold_properties(document_node: &DocumentNode, node_id: NodeId, _context: &mut NodePropertiesContext) -> Vec<LayoutGroup> {
|
||||
let thereshold_min = number_widget(document_node, node_id, 1, "Min Luminance", NumberInput::default().mode_range().min(0.).max(100.).unit("%"), true);
|
||||
let thereshold_max = number_widget(document_node, node_id, 2, "Max Luminance", NumberInput::default().mode_range().min(0.).max(100.).unit("%"), true);
|
||||
let luminance_calc = luminance_calculation(document_node, node_id, 3, "Luminance Calc", true);
|
||||
|
|
@ -1136,13 +1136,46 @@ pub fn adjust_threshold_properties(document_node: &DocumentNode, node_id: NodeId
|
|||
vec![LayoutGroup::Row { widgets: thereshold_min }, LayoutGroup::Row { widgets: thereshold_max }, luminance_calc]
|
||||
}
|
||||
|
||||
pub fn adjust_vibrance_properties(document_node: &DocumentNode, node_id: NodeId, _context: &mut NodePropertiesContext) -> Vec<LayoutGroup> {
|
||||
pub fn gradient_map_properties(document_node: &DocumentNode, node_id: NodeId, _context: &mut NodePropertiesContext) -> Vec<LayoutGroup> {
|
||||
let gradient_input = 1;
|
||||
let reverse_input = 2;
|
||||
|
||||
let gradient = if let Some(TaggedValue::GradientStops(gradient)) = &document_node.inputs[gradient_input].as_value() {
|
||||
gradient.clone()
|
||||
} else {
|
||||
return vec![LayoutGroup::Row { widgets: vec![] }];
|
||||
};
|
||||
let mut gradient_row = vec![TextLabel::new("Gradient").widget_holder()];
|
||||
add_blank_assist(&mut gradient_row);
|
||||
gradient_row.extend([
|
||||
Separator::new(SeparatorType::Unrelated).widget_holder(),
|
||||
ColorButton::default()
|
||||
.allow_none(false)
|
||||
.value(FillChoice::Gradient(gradient))
|
||||
.on_update(move |x: &ColorButton| {
|
||||
NodeGraphMessage::SetInputValue {
|
||||
node_id,
|
||||
input_index: gradient_input,
|
||||
value: TaggedValue::GradientStops(x.value.as_gradient().unwrap().clone()),
|
||||
}
|
||||
.into()
|
||||
})
|
||||
.on_commit(commit_value)
|
||||
.widget_holder(),
|
||||
]);
|
||||
|
||||
let reverse_row = bool_widget(document_node, node_id, reverse_input, "Reverse", true);
|
||||
|
||||
vec![LayoutGroup::Row { widgets: gradient_row }, LayoutGroup::Row { widgets: reverse_row }]
|
||||
}
|
||||
|
||||
pub fn vibrance_properties(document_node: &DocumentNode, node_id: NodeId, _context: &mut NodePropertiesContext) -> Vec<LayoutGroup> {
|
||||
let vibrance = number_widget(document_node, node_id, 1, "Vibrance", NumberInput::default().mode_range().min(-100.).max(100.).unit("%"), true);
|
||||
|
||||
vec![LayoutGroup::Row { widgets: vibrance }]
|
||||
}
|
||||
|
||||
pub fn adjust_channel_mixer_properties(document_node: &DocumentNode, node_id: NodeId, _context: &mut NodePropertiesContext) -> Vec<LayoutGroup> {
|
||||
pub fn channel_mixer_properties(document_node: &DocumentNode, node_id: NodeId, _context: &mut NodePropertiesContext) -> Vec<LayoutGroup> {
|
||||
// Monochrome
|
||||
let monochrome_index = 1;
|
||||
let monochrome = bool_widget(document_node, node_id, monochrome_index, "Monochrome", true);
|
||||
|
|
@ -1236,7 +1269,7 @@ pub fn adjust_channel_mixer_properties(document_node: &DocumentNode, node_id: No
|
|||
layout
|
||||
}
|
||||
|
||||
pub fn adjust_selective_color_properties(document_node: &DocumentNode, node_id: NodeId, _context: &mut NodePropertiesContext) -> Vec<LayoutGroup> {
|
||||
pub fn selective_color_properties(document_node: &DocumentNode, node_id: NodeId, _context: &mut NodePropertiesContext) -> Vec<LayoutGroup> {
|
||||
// Colors choice
|
||||
let colors_index = 38;
|
||||
let mut colors = vec![TextLabel::new("Colors").widget_holder(), Separator::new(SeparatorType::Unrelated).widget_holder()];
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ use super::curve::{Curve, CurveManipulatorGroup, ValueMapperNode};
|
|||
#[cfg(feature = "alloc")]
|
||||
use super::ImageFrame;
|
||||
use super::{Channel, Color, Node, RGBMut};
|
||||
use crate::vector::style::GradientStops;
|
||||
use crate::vector::VectorData;
|
||||
use crate::GraphicGroup;
|
||||
|
||||
|
|
@ -554,6 +555,21 @@ pub fn blend_colors(foreground: Color, background: Color, blend_mode: BlendMode,
|
|||
background.alpha_blend(target_color.to_associated_alpha(opacity))
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct GradientMapNode<Gradient, Reverse> {
|
||||
gradient: Gradient,
|
||||
reverse: Reverse,
|
||||
// TODO: Add support for dithering to break up gradient color banding
|
||||
// TODO: Add support for controlling the gradient interpolation method (instead of always `luminance_srgb()`)
|
||||
}
|
||||
|
||||
#[node_macro::node_fn(GradientMapNode)]
|
||||
fn gradient_map_node(color: Color, gradient: GradientStops, reverse: bool) -> Color {
|
||||
let intensity = color.luminance_srgb();
|
||||
let intensity = if reverse { 1. - intensity } else { intensity };
|
||||
gradient.evalute(intensity as f64)
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct VibranceNode<Vibrance> {
|
||||
vibrance: Vibrance,
|
||||
|
|
|
|||
|
|
@ -36,6 +36,32 @@ impl Default for GradientStops {
|
|||
}
|
||||
}
|
||||
|
||||
impl GradientStops {
|
||||
pub fn evalute(&self, t: f64) -> Color {
|
||||
if self.0.is_empty() {
|
||||
return Color::BLACK;
|
||||
}
|
||||
|
||||
if t <= self.0[0].0 {
|
||||
return self.0[0].1;
|
||||
}
|
||||
if t >= self.0[self.0.len() - 1].0 {
|
||||
return self.0[self.0.len() - 1].1;
|
||||
}
|
||||
|
||||
for i in 0..self.0.len() - 1 {
|
||||
let (t1, c1) = self.0[i];
|
||||
let (t2, c2) = self.0[i + 1];
|
||||
if t >= t1 && t <= t2 {
|
||||
let normalized_t = (t - t1) / (t2 - t1);
|
||||
return c1.lerp(&c2, normalized_t as f32);
|
||||
}
|
||||
}
|
||||
|
||||
Color::BLACK
|
||||
}
|
||||
}
|
||||
|
||||
/// A gradient fill.
|
||||
///
|
||||
/// Contains the start and end points, along with the colors at varying points along the length.
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ use graphene_core::{Node, NodeIO, NodeIOTypes};
|
|||
use graphene_std::any::{ComposeTypeErased, DowncastBothNode, DynAnyNode, FutureWrapperNode, IntoTypeErasedNode};
|
||||
use graphene_std::application_io::{RenderConfig, TextureFrame};
|
||||
use graphene_std::raster::*;
|
||||
use graphene_std::vector::style::GradientStops;
|
||||
use graphene_std::wasm_application_io::*;
|
||||
use graphene_std::GraphicElement;
|
||||
#[cfg(feature = "gpu")]
|
||||
|
|
@ -484,6 +485,7 @@ fn node_registry() -> HashMap<ProtoNodeIdentifier, HashMap<NodeIOTypes, NodeCons
|
|||
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, f64, LuminanceCalculation]),
|
||||
raster_node!(graphene_core::raster::GradientMapNode<_, _>, params: [GradientStops, bool]),
|
||||
raster_node!(graphene_core::raster::VibranceNode<_>, params: [f64]),
|
||||
raster_node!(
|
||||
graphene_core::raster::ChannelMixerNode<_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _>,
|
||||
|
|
|
|||
Loading…
Reference in New Issue