mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-15 22:28:10 +08:00
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 <keavon@keavon.com>
This commit is contained in:
@@ -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,
|
||||
|
||||
@@ -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<LayoutGroup> {
|
||||
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<LayoutGroup> {
|
||||
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<LayoutGroup> {
|
||||
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<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().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<LayoutGroup> {
|
||||
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<LayoutGroup> {
|
||||
@@ -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<LayoutGroup> {
|
||||
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 }]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user