Add the Selective Color adjustment node
This commit is contained in:
parent
76c754d38a
commit
161bbc62b4
|
|
@ -7,7 +7,7 @@ use graph_craft::document::value::*;
|
||||||
use graph_craft::document::*;
|
use graph_craft::document::*;
|
||||||
use graph_craft::imaginate_input::ImaginateSamplingMethod;
|
use graph_craft::imaginate_input::ImaginateSamplingMethod;
|
||||||
use graph_craft::NodeIdentifier;
|
use graph_craft::NodeIdentifier;
|
||||||
use graphene_core::raster::{BlendMode, Color, Image, ImageFrame, LuminanceCalculation};
|
use graphene_core::raster::{BlendMode, Color, Image, ImageFrame, LuminanceCalculation, RedGreenBlue, RelativeAbsolute, SelectiveColorChoice};
|
||||||
use graphene_core::vector::VectorData;
|
use graphene_core::vector::VectorData;
|
||||||
use graphene_core::*;
|
use graphene_core::*;
|
||||||
|
|
||||||
|
|
@ -696,11 +696,72 @@ fn static_nodes() -> Vec<DocumentNodeType> {
|
||||||
DocumentInputType::value("(Blue) Blue", TaggedValue::F64(100.), false),
|
DocumentInputType::value("(Blue) Blue", TaggedValue::F64(100.), false),
|
||||||
DocumentInputType::value("(Blue) Constant", TaggedValue::F64(0.), false),
|
DocumentInputType::value("(Blue) Constant", TaggedValue::F64(0.), false),
|
||||||
// Display-only properties (not used within the node)
|
// Display-only properties (not used within the node)
|
||||||
DocumentInputType::value("Output Channel", TaggedValue::U32(0), false),
|
DocumentInputType::value("Output Channel", TaggedValue::RedGreenBlue(RedGreenBlue::Red), false),
|
||||||
],
|
],
|
||||||
outputs: vec![DocumentOutputType::new("Image", FrontendGraphDataType::Raster)],
|
outputs: vec![DocumentOutputType::new("Image", FrontendGraphDataType::Raster)],
|
||||||
properties: node_properties::adjust_channel_mixer_properties,
|
properties: node_properties::adjust_channel_mixer_properties,
|
||||||
},
|
},
|
||||||
|
DocumentNodeType {
|
||||||
|
name: "Selective Color",
|
||||||
|
category: "Image Adjustments",
|
||||||
|
identifier: NodeImplementation::proto(
|
||||||
|
"graphene_core::raster::SelectiveColorNode<_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _>",
|
||||||
|
),
|
||||||
|
inputs: vec![
|
||||||
|
DocumentInputType::value("Image", TaggedValue::ImageFrame(ImageFrame::empty()), true),
|
||||||
|
// Mode
|
||||||
|
DocumentInputType::value("Mode", TaggedValue::RelativeAbsolute(RelativeAbsolute::Relative), false),
|
||||||
|
// Reds
|
||||||
|
DocumentInputType::value("(Reds) Cyan", TaggedValue::F64(0.), false),
|
||||||
|
DocumentInputType::value("(Reds) Magenta", TaggedValue::F64(0.), false),
|
||||||
|
DocumentInputType::value("(Reds) Yellow", TaggedValue::F64(0.), false),
|
||||||
|
DocumentInputType::value("(Reds) Black", TaggedValue::F64(0.), false),
|
||||||
|
// Yellows
|
||||||
|
DocumentInputType::value("(Yellows) Cyan", TaggedValue::F64(0.), false),
|
||||||
|
DocumentInputType::value("(Yellows) Magenta", TaggedValue::F64(0.), false),
|
||||||
|
DocumentInputType::value("(Yellows) Yellow", TaggedValue::F64(0.), false),
|
||||||
|
DocumentInputType::value("(Yellows) Black", TaggedValue::F64(0.), false),
|
||||||
|
// Greens
|
||||||
|
DocumentInputType::value("(Greens) Cyan", TaggedValue::F64(0.), false),
|
||||||
|
DocumentInputType::value("(Greens) Magenta", TaggedValue::F64(0.), false),
|
||||||
|
DocumentInputType::value("(Greens) Yellow", TaggedValue::F64(0.), false),
|
||||||
|
DocumentInputType::value("(Greens) Black", TaggedValue::F64(0.), false),
|
||||||
|
// Cyans
|
||||||
|
DocumentInputType::value("(Cyans) Cyan", TaggedValue::F64(0.), false),
|
||||||
|
DocumentInputType::value("(Cyans) Magenta", TaggedValue::F64(0.), false),
|
||||||
|
DocumentInputType::value("(Cyans) Yellow", TaggedValue::F64(0.), false),
|
||||||
|
DocumentInputType::value("(Cyans) Black", TaggedValue::F64(0.), false),
|
||||||
|
// Blues
|
||||||
|
DocumentInputType::value("(Blues) Cyan", TaggedValue::F64(0.), false),
|
||||||
|
DocumentInputType::value("(Blues) Magenta", TaggedValue::F64(0.), false),
|
||||||
|
DocumentInputType::value("(Blues) Yellow", TaggedValue::F64(0.), false),
|
||||||
|
DocumentInputType::value("(Blues) Black", TaggedValue::F64(0.), false),
|
||||||
|
// Magentas
|
||||||
|
DocumentInputType::value("(Magentas) Cyan", TaggedValue::F64(0.), false),
|
||||||
|
DocumentInputType::value("(Magentas) Magenta", TaggedValue::F64(0.), false),
|
||||||
|
DocumentInputType::value("(Magentas) Yellow", TaggedValue::F64(0.), false),
|
||||||
|
DocumentInputType::value("(Magentas) Black", TaggedValue::F64(0.), false),
|
||||||
|
// Whites
|
||||||
|
DocumentInputType::value("(Whites) Cyan", TaggedValue::F64(0.), false),
|
||||||
|
DocumentInputType::value("(Whites) Magenta", TaggedValue::F64(0.), false),
|
||||||
|
DocumentInputType::value("(Whites) Yellow", TaggedValue::F64(0.), false),
|
||||||
|
DocumentInputType::value("(Whites) Black", TaggedValue::F64(0.), false),
|
||||||
|
// Neutrals
|
||||||
|
DocumentInputType::value("(Neutrals) Cyan", TaggedValue::F64(0.), false),
|
||||||
|
DocumentInputType::value("(Neutrals) Magenta", TaggedValue::F64(0.), false),
|
||||||
|
DocumentInputType::value("(Neutrals) Yellow", TaggedValue::F64(0.), false),
|
||||||
|
DocumentInputType::value("(Neutrals) Black", TaggedValue::F64(0.), false),
|
||||||
|
// Blacks
|
||||||
|
DocumentInputType::value("(Blacks) Cyan", TaggedValue::F64(0.), false),
|
||||||
|
DocumentInputType::value("(Blacks) Magenta", TaggedValue::F64(0.), false),
|
||||||
|
DocumentInputType::value("(Blacks) Yellow", TaggedValue::F64(0.), false),
|
||||||
|
DocumentInputType::value("(Blacks) Black", TaggedValue::F64(0.), false),
|
||||||
|
// Display-only properties (not used within the node)
|
||||||
|
DocumentInputType::value("Colors", TaggedValue::SelectiveColorChoice(SelectiveColorChoice::Reds), false),
|
||||||
|
],
|
||||||
|
outputs: vec![DocumentOutputType::new("Image", FrontendGraphDataType::Raster)],
|
||||||
|
properties: node_properties::adjust_selective_color_properties,
|
||||||
|
},
|
||||||
DocumentNodeType {
|
DocumentNodeType {
|
||||||
name: "Opacity",
|
name: "Opacity",
|
||||||
category: "Image Adjustments",
|
category: "Image Adjustments",
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ use glam::DVec2;
|
||||||
use graph_craft::document::value::TaggedValue;
|
use graph_craft::document::value::TaggedValue;
|
||||||
use graph_craft::document::{DocumentNode, NodeId, NodeInput};
|
use graph_craft::document::{DocumentNode, NodeId, NodeInput};
|
||||||
use graph_craft::imaginate_input::*;
|
use graph_craft::imaginate_input::*;
|
||||||
use graphene_core::raster::{BlendMode, Color, LuminanceCalculation};
|
use graphene_core::raster::{BlendMode, Color, LuminanceCalculation, RedGreenBlue, RelativeAbsolute, SelectiveColorChoice};
|
||||||
use graphene_core::vector::style::{FillType, GradientType, LineCap, LineJoin};
|
use graphene_core::vector::style::{FillType, GradientType, LineCap, LineJoin};
|
||||||
|
|
||||||
use super::document_node_types::NodePropertiesContext;
|
use super::document_node_types::NodePropertiesContext;
|
||||||
|
|
@ -546,6 +546,7 @@ pub fn adjust_vibrance_properties(document_node: &DocumentNode, node_id: NodeId,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn adjust_channel_mixer_properties(document_node: &DocumentNode, node_id: NodeId, _context: &mut NodePropertiesContext) -> Vec<LayoutGroup> {
|
pub fn adjust_channel_mixer_properties(document_node: &DocumentNode, node_id: NodeId, _context: &mut NodePropertiesContext) -> Vec<LayoutGroup> {
|
||||||
|
// Monochrome
|
||||||
let monochrome_index = 1;
|
let monochrome_index = 1;
|
||||||
let monochrome = bool_widget(document_node, node_id, monochrome_index, "Monochrome", true);
|
let monochrome = bool_widget(document_node, node_id, monochrome_index, "Monochrome", true);
|
||||||
let is_monochrome = if let &NodeInput::Value {
|
let is_monochrome = if let &NodeInput::Value {
|
||||||
|
|
@ -558,51 +559,53 @@ pub fn adjust_channel_mixer_properties(document_node: &DocumentNode, node_id: No
|
||||||
false
|
false
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Output channel choice
|
||||||
let output_channel_index = 18;
|
let output_channel_index = 18;
|
||||||
let mut output_channel = vec![WidgetHolder::text_widget("Output Channel"), WidgetHolder::unrelated_separator()];
|
let mut output_channel = vec![WidgetHolder::text_widget("Output Channel"), WidgetHolder::unrelated_separator()];
|
||||||
add_blank_assist(&mut output_channel);
|
add_blank_assist(&mut output_channel);
|
||||||
if let &NodeInput::Value {
|
if let &NodeInput::Value {
|
||||||
tagged_value: TaggedValue::U32(red_green_blue_index),
|
tagged_value: TaggedValue::RedGreenBlue(choice),
|
||||||
exposed: false,
|
exposed: false,
|
||||||
} = &document_node.inputs[output_channel_index]
|
} = &document_node.inputs[output_channel_index]
|
||||||
{
|
{
|
||||||
let entries = [("Red", 0), ("Green", 1), ("Blue", 2)]
|
let entries = vec![
|
||||||
.into_iter()
|
RadioEntryData::new(RedGreenBlue::Red.to_string()).on_update(update_value(|_| TaggedValue::RedGreenBlue(RedGreenBlue::Red), node_id, output_channel_index)),
|
||||||
.map(|(name, val)| RadioEntryData::new(name).on_update(update_value(move |_| TaggedValue::U32(val), node_id, output_channel_index)))
|
RadioEntryData::new(RedGreenBlue::Green.to_string()).on_update(update_value(|_| TaggedValue::RedGreenBlue(RedGreenBlue::Green), node_id, output_channel_index)),
|
||||||
.collect();
|
RadioEntryData::new(RedGreenBlue::Blue.to_string()).on_update(update_value(|_| TaggedValue::RedGreenBlue(RedGreenBlue::Blue), node_id, output_channel_index)),
|
||||||
output_channel.extend([RadioInput::new(entries).selected_index(red_green_blue_index).widget_holder()]);
|
];
|
||||||
|
output_channel.extend([RadioInput::new(entries).selected_index(choice as u32).widget_holder()]);
|
||||||
};
|
};
|
||||||
|
|
||||||
let is_output_channel = if let &NodeInput::Value {
|
let is_output_channel = if let &NodeInput::Value {
|
||||||
tagged_value: TaggedValue::U32(red_green_blue_index),
|
tagged_value: TaggedValue::RedGreenBlue(choice),
|
||||||
..
|
..
|
||||||
} = &document_node.inputs[output_channel_index]
|
} = &document_node.inputs[output_channel_index]
|
||||||
{
|
{
|
||||||
red_green_blue_index
|
choice
|
||||||
} else {
|
} else {
|
||||||
warn!("Channel Mixer node properties panel could not be displayed.");
|
warn!("Channel Mixer node properties panel could not be displayed.");
|
||||||
return vec![];
|
return vec![];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Channel values
|
||||||
let (r, g, b, c) = match (is_monochrome, is_output_channel) {
|
let (r, g, b, c) = match (is_monochrome, is_output_channel) {
|
||||||
(true, _) => ((2, "Red", 40.), (3, "Green", 40.), (4, "Blue", 20.), (5, "Constant", 0.)),
|
(true, _) => ((2, "Red", 40.), (3, "Green", 40.), (4, "Blue", 20.), (5, "Constant", 0.)),
|
||||||
(false, 0) => ((6, "(Red) Red", 100.), (7, "(Red) Green", 0.), (8, "(Red) Blue", 0.), (9, "(Red) Constant", 0.)),
|
(false, RedGreenBlue::Red) => ((6, "(Red) Red", 100.), (7, "(Red) Green", 0.), (8, "(Red) Blue", 0.), (9, "(Red) Constant", 0.)),
|
||||||
(false, 1) => ((10, "(Green) Red", 0.), (11, "(Green) Green", 100.), (12, "(Green) Blue", 0.), (13, "(Green) Constant", 0.)),
|
(false, RedGreenBlue::Green) => ((10, "(Green) Red", 0.), (11, "(Green) Green", 100.), (12, "(Green) Blue", 0.), (13, "(Green) Constant", 0.)),
|
||||||
(false, 2) => ((14, "(Blue) Red", 0.), (15, "(Blue) Green", 0.), (16, "(Blue) Blue", 100.), (17, "(Blue) Constant", 0.)),
|
(false, RedGreenBlue::Blue) => ((14, "(Blue) Red", 0.), (15, "(Blue) Green", 0.), (16, "(Blue) Blue", 100.), (17, "(Blue) Constant", 0.)),
|
||||||
_ => unreachable!(),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let red = number_widget(document_node, node_id, r.0, r.1, NumberInput::default().min(-200.).max(200.).value(Some(r.2)).unit("%"), true);
|
let red = number_widget(document_node, node_id, r.0, r.1, NumberInput::default().min(-200.).max(200.).value(Some(r.2)).unit("%"), true);
|
||||||
let green = number_widget(document_node, node_id, g.0, g.1, NumberInput::default().min(-200.).max(200.).value(Some(g.2)).unit("%"), true);
|
let green = number_widget(document_node, node_id, g.0, g.1, NumberInput::default().min(-200.).max(200.).value(Some(g.2)).unit("%"), true);
|
||||||
let blue = number_widget(document_node, node_id, b.0, b.1, NumberInput::default().min(-200.).max(200.).value(Some(b.2)).unit("%"), true);
|
let blue = number_widget(document_node, node_id, b.0, b.1, NumberInput::default().min(-200.).max(200.).value(Some(b.2)).unit("%"), true);
|
||||||
let constant = number_widget(document_node, node_id, c.0, c.1, NumberInput::default().min(-200.).max(200.).value(Some(c.2)).unit("%"), true);
|
let constant = number_widget(document_node, node_id, c.0, c.1, NumberInput::default().min(-200.).max(200.).value(Some(c.2)).unit("%"), true);
|
||||||
|
|
||||||
|
// Monochrome
|
||||||
let mut layout = vec![LayoutGroup::Row { widgets: monochrome }];
|
let mut layout = vec![LayoutGroup::Row { widgets: monochrome }];
|
||||||
|
// Output channel choice
|
||||||
if !is_monochrome {
|
if !is_monochrome {
|
||||||
layout.push(LayoutGroup::Row { widgets: output_channel });
|
layout.push(LayoutGroup::Row { widgets: output_channel });
|
||||||
};
|
};
|
||||||
|
// Channel values
|
||||||
layout.extend([
|
layout.extend([
|
||||||
// Gray output
|
|
||||||
LayoutGroup::Row { widgets: red },
|
LayoutGroup::Row { widgets: red },
|
||||||
LayoutGroup::Row { widgets: green },
|
LayoutGroup::Row { widgets: green },
|
||||||
LayoutGroup::Row { widgets: blue },
|
LayoutGroup::Row { widgets: blue },
|
||||||
|
|
@ -611,6 +614,85 @@ pub fn adjust_channel_mixer_properties(document_node: &DocumentNode, node_id: No
|
||||||
layout
|
layout
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn adjust_selective_color_properties(document_node: &DocumentNode, node_id: NodeId, _context: &mut NodePropertiesContext) -> Vec<LayoutGroup> {
|
||||||
|
// Colors choice
|
||||||
|
let colors_index = 38;
|
||||||
|
let mut colors = vec![WidgetHolder::text_widget("Colors"), WidgetHolder::unrelated_separator()];
|
||||||
|
add_blank_assist(&mut colors);
|
||||||
|
if let &NodeInput::Value {
|
||||||
|
tagged_value: TaggedValue::SelectiveColorChoice(choice),
|
||||||
|
exposed: false,
|
||||||
|
} = &document_node.inputs[colors_index]
|
||||||
|
{
|
||||||
|
use SelectiveColorChoice::*;
|
||||||
|
let entries = [vec![Reds, Yellows, Greens, Cyans, Blues, Magentas], vec![Whites, Neutrals, Blacks]]
|
||||||
|
.into_iter()
|
||||||
|
.map(|section| {
|
||||||
|
section
|
||||||
|
.into_iter()
|
||||||
|
.map(|choice| DropdownEntryData::new(choice.to_string()).on_update(update_value(move |_| TaggedValue::SelectiveColorChoice(choice), node_id, colors_index)))
|
||||||
|
.collect()
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
colors.extend([DropdownInput::new(entries).selected_index(Some(choice as u32)).widget_holder()]);
|
||||||
|
};
|
||||||
|
let colors_choice_index = if let &NodeInput::Value {
|
||||||
|
tagged_value: TaggedValue::SelectiveColorChoice(choice),
|
||||||
|
..
|
||||||
|
} = &document_node.inputs[colors_index]
|
||||||
|
{
|
||||||
|
choice
|
||||||
|
} else {
|
||||||
|
warn!("Selective Color node properties panel could not be displayed.");
|
||||||
|
return vec![];
|
||||||
|
};
|
||||||
|
|
||||||
|
// CMYK
|
||||||
|
let (c, m, y, k) = match colors_choice_index {
|
||||||
|
SelectiveColorChoice::Reds => ((2, "(Reds) Cyan"), (3, "(Reds) Magenta"), (4, "(Reds) Yellow"), (5, "(Reds) Black")),
|
||||||
|
SelectiveColorChoice::Yellows => ((6, "(Yellows) Cyan"), (7, "(Yellows) Magenta"), (8, "(Yellows) Yellow"), (9, "(Yellows) Black")),
|
||||||
|
SelectiveColorChoice::Greens => ((10, "(Greens) Cyan"), (11, "(Greens) Magenta"), (12, "(Greens) Yellow"), (13, "(Greens) Black")),
|
||||||
|
SelectiveColorChoice::Cyans => ((14, "(Cyans) Cyan"), (15, "(Cyans) Magenta"), (16, "(Cyans) Yellow"), (17, "(Cyans) Black")),
|
||||||
|
SelectiveColorChoice::Blues => ((18, "(Blues) Cyan"), (19, "(Blues) Magenta"), (20, "(Blues) Yellow"), (21, "(Blues) Black")),
|
||||||
|
SelectiveColorChoice::Magentas => ((22, "(Magentas) Cyan"), (23, "(Magentas) Magenta"), (24, "(Magentas) Yellow"), (25, "(Magentas) Black")),
|
||||||
|
SelectiveColorChoice::Whites => ((26, "(Whites) Cyan"), (27, "(Whites) Magenta"), (28, "(Whites) Yellow"), (29, "(Whites) Black")),
|
||||||
|
SelectiveColorChoice::Neutrals => ((30, "(Neutrals) Cyan"), (31, "(Neutrals) Magenta"), (32, "(Neutrals) Yellow"), (33, "(Neutrals) Black")),
|
||||||
|
SelectiveColorChoice::Blacks => ((34, "(Blacks) Cyan"), (35, "(Blacks) Magenta"), (36, "(Blacks) Yellow"), (37, "(Blacks) Black")),
|
||||||
|
};
|
||||||
|
let cyan = number_widget(document_node, node_id, c.0, c.1, NumberInput::default().min(-100.).max(100.).unit("%"), true);
|
||||||
|
let magenta = number_widget(document_node, node_id, m.0, m.1, NumberInput::default().min(-100.).max(100.).unit("%"), true);
|
||||||
|
let yellow = number_widget(document_node, node_id, y.0, y.1, NumberInput::default().min(-100.).max(100.).unit("%"), true);
|
||||||
|
let black = number_widget(document_node, node_id, k.0, k.1, NumberInput::default().min(-100.).max(100.).unit("%"), true);
|
||||||
|
|
||||||
|
// Mode
|
||||||
|
let mode_index = 1;
|
||||||
|
let mut mode = start_widgets(document_node, node_id, mode_index, "Mode", FrontendGraphDataType::General, true);
|
||||||
|
mode.push(WidgetHolder::unrelated_separator());
|
||||||
|
if let &NodeInput::Value {
|
||||||
|
tagged_value: TaggedValue::RelativeAbsolute(relative_or_absolute),
|
||||||
|
exposed: false,
|
||||||
|
} = &document_node.inputs[mode_index]
|
||||||
|
{
|
||||||
|
let entries = vec![
|
||||||
|
RadioEntryData::new("Relative").on_update(update_value(|_| TaggedValue::RelativeAbsolute(RelativeAbsolute::Relative), node_id, mode_index)),
|
||||||
|
RadioEntryData::new("Absolute").on_update(update_value(|_| TaggedValue::RelativeAbsolute(RelativeAbsolute::Absolute), node_id, mode_index)),
|
||||||
|
];
|
||||||
|
mode.push(RadioInput::new(entries).selected_index(relative_or_absolute as u32).widget_holder());
|
||||||
|
};
|
||||||
|
|
||||||
|
vec![
|
||||||
|
// Colors choice
|
||||||
|
LayoutGroup::Row { widgets: colors },
|
||||||
|
// CMYK
|
||||||
|
LayoutGroup::Row { widgets: cyan },
|
||||||
|
LayoutGroup::Row { widgets: magenta },
|
||||||
|
LayoutGroup::Row { widgets: yellow },
|
||||||
|
LayoutGroup::Row { widgets: black },
|
||||||
|
// Mode
|
||||||
|
LayoutGroup::Row { widgets: mode },
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(feature = "gpu")]
|
#[cfg(feature = "gpu")]
|
||||||
pub fn gpu_map_properties(document_node: &DocumentNode, node_id: NodeId, _context: &mut NodePropertiesContext) -> Vec<LayoutGroup> {
|
pub fn gpu_map_properties(document_node: &DocumentNode, node_id: NodeId, _context: &mut NodePropertiesContext) -> Vec<LayoutGroup> {
|
||||||
let map = text_widget(document_node, node_id, 1, "Map", true);
|
let map = text_widget(document_node, node_id, 1, "Map", true);
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ use crate::Node;
|
||||||
|
|
||||||
use core::fmt::Debug;
|
use core::fmt::Debug;
|
||||||
use dyn_any::{DynAny, StaticType};
|
use dyn_any::{DynAny, StaticType};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
#[cfg(target_arch = "spirv")]
|
#[cfg(target_arch = "spirv")]
|
||||||
use spirv_std::num_traits::float::Float;
|
use spirv_std::num_traits::float::Float;
|
||||||
|
|
@ -456,6 +457,24 @@ fn vibrance_node(color: Color, vibrance: f64) -> Color {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Hash, DynAny, specta::Type)]
|
||||||
|
pub enum RedGreenBlue {
|
||||||
|
Red,
|
||||||
|
Green,
|
||||||
|
Blue,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl core::fmt::Display for RedGreenBlue {
|
||||||
|
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||||
|
match self {
|
||||||
|
RedGreenBlue::Red => write!(f, "Red"),
|
||||||
|
RedGreenBlue::Green => write!(f, "Green"),
|
||||||
|
RedGreenBlue::Blue => write!(f, "Blue"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
pub struct ChannelMixerNode<Monochrome, MonochromeR, MonochromeG, MonochromeB, MonochromeC, RedR, RedG, RedB, RedC, GreenR, GreenG, GreenB, GreenC, BlueR, BlueG, BlueB, BlueC> {
|
pub struct ChannelMixerNode<Monochrome, MonochromeR, MonochromeG, MonochromeB, MonochromeC, RedR, RedG, RedB, RedC, GreenR, GreenG, GreenB, GreenC, BlueR, BlueG, BlueB, BlueC> {
|
||||||
monochrome: Monochrome,
|
monochrome: Monochrome,
|
||||||
|
|
@ -523,6 +542,209 @@ fn channel_mixer_node(
|
||||||
color.to_linear_srgb()
|
color.to_linear_srgb()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Hash, DynAny, specta::Type)]
|
||||||
|
pub enum RelativeAbsolute {
|
||||||
|
Relative,
|
||||||
|
Absolute,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl core::fmt::Display for RelativeAbsolute {
|
||||||
|
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||||
|
match self {
|
||||||
|
RelativeAbsolute::Relative => write!(f, "Relative"),
|
||||||
|
RelativeAbsolute::Absolute => write!(f, "Absolute"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Hash, DynAny, specta::Type)]
|
||||||
|
pub enum SelectiveColorChoice {
|
||||||
|
Reds,
|
||||||
|
Yellows,
|
||||||
|
Greens,
|
||||||
|
Cyans,
|
||||||
|
Blues,
|
||||||
|
Magentas,
|
||||||
|
Whites,
|
||||||
|
Neutrals,
|
||||||
|
Blacks,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl core::fmt::Display for SelectiveColorChoice {
|
||||||
|
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||||
|
match self {
|
||||||
|
SelectiveColorChoice::Reds => write!(f, "Reds"),
|
||||||
|
SelectiveColorChoice::Yellows => write!(f, "Yellows"),
|
||||||
|
SelectiveColorChoice::Greens => write!(f, "Greens"),
|
||||||
|
SelectiveColorChoice::Cyans => write!(f, "Cyans"),
|
||||||
|
SelectiveColorChoice::Blues => write!(f, "Blues"),
|
||||||
|
SelectiveColorChoice::Magentas => write!(f, "Magentas"),
|
||||||
|
SelectiveColorChoice::Whites => write!(f, "Whites"),
|
||||||
|
SelectiveColorChoice::Neutrals => write!(f, "Neutrals"),
|
||||||
|
SelectiveColorChoice::Blacks => write!(f, "Blacks"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub struct SelectiveColorNode<Absolute, RC, RM, RY, RK, YC, YM, YY, YK, GC, GM, GY, GK, CC, CM, CY, CK, BC, BM, BY, BK, MC, MM, MY, MK, WC, WM, WY, WK, NC, NM, NY, NK, KC, KM, KY, KK> {
|
||||||
|
mode: Absolute,
|
||||||
|
r_c: RC,
|
||||||
|
r_m: RM,
|
||||||
|
r_y: RY,
|
||||||
|
r_k: RK,
|
||||||
|
y_c: YC,
|
||||||
|
y_m: YM,
|
||||||
|
y_y: YY,
|
||||||
|
y_k: YK,
|
||||||
|
g_c: GC,
|
||||||
|
g_m: GM,
|
||||||
|
g_y: GY,
|
||||||
|
g_k: GK,
|
||||||
|
c_c: CC,
|
||||||
|
c_m: CM,
|
||||||
|
c_y: CY,
|
||||||
|
c_k: CK,
|
||||||
|
b_c: BC,
|
||||||
|
b_m: BM,
|
||||||
|
b_y: BY,
|
||||||
|
b_k: BK,
|
||||||
|
m_c: MC,
|
||||||
|
m_m: MM,
|
||||||
|
m_y: MY,
|
||||||
|
m_k: MK,
|
||||||
|
w_c: WC,
|
||||||
|
w_m: WM,
|
||||||
|
w_y: WY,
|
||||||
|
w_k: WK,
|
||||||
|
n_c: NC,
|
||||||
|
n_m: NM,
|
||||||
|
n_y: NY,
|
||||||
|
n_k: NK,
|
||||||
|
k_c: KC,
|
||||||
|
k_m: KM,
|
||||||
|
k_y: KY,
|
||||||
|
k_k: KK,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Based on https://blog.pkh.me/p/22-understanding-selective-coloring-in-adobe-photoshop.html
|
||||||
|
#[node_macro::node_fn(SelectiveColorNode)]
|
||||||
|
fn selective_color_node(
|
||||||
|
color: Color,
|
||||||
|
mode: RelativeAbsolute,
|
||||||
|
r_c: f64,
|
||||||
|
r_m: f64,
|
||||||
|
r_y: f64,
|
||||||
|
r_k: f64,
|
||||||
|
y_c: f64,
|
||||||
|
y_m: f64,
|
||||||
|
y_y: f64,
|
||||||
|
y_k: f64,
|
||||||
|
g_c: f64,
|
||||||
|
g_m: f64,
|
||||||
|
g_y: f64,
|
||||||
|
g_k: f64,
|
||||||
|
c_c: f64,
|
||||||
|
c_m: f64,
|
||||||
|
c_y: f64,
|
||||||
|
c_k: f64,
|
||||||
|
b_c: f64,
|
||||||
|
b_m: f64,
|
||||||
|
b_y: f64,
|
||||||
|
b_k: f64,
|
||||||
|
m_c: f64,
|
||||||
|
m_m: f64,
|
||||||
|
m_y: f64,
|
||||||
|
m_k: f64,
|
||||||
|
w_c: f64,
|
||||||
|
w_m: f64,
|
||||||
|
w_y: f64,
|
||||||
|
w_k: f64,
|
||||||
|
n_c: f64,
|
||||||
|
n_m: f64,
|
||||||
|
n_y: f64,
|
||||||
|
n_k: f64,
|
||||||
|
k_c: f64,
|
||||||
|
k_m: f64,
|
||||||
|
k_y: f64,
|
||||||
|
k_k: f64,
|
||||||
|
) -> Color {
|
||||||
|
let color = color.to_gamma_srgb();
|
||||||
|
|
||||||
|
let (r, g, b, a) = color.components();
|
||||||
|
|
||||||
|
let min = |a: f32, b: f32, c: f32| a.min(b).min(c);
|
||||||
|
let max = |a: f32, b: f32, c: f32| a.max(b).max(c);
|
||||||
|
let med = |a: f32, b: f32, c: f32| a + b + c - min(a, b, c) - max(a, b, c);
|
||||||
|
|
||||||
|
let max_channel = max(r, g, b);
|
||||||
|
let min_channel = min(r, g, b);
|
||||||
|
|
||||||
|
let pixel_color_range = |choice| match choice {
|
||||||
|
SelectiveColorChoice::Reds => max_channel == r,
|
||||||
|
SelectiveColorChoice::Yellows => min_channel == b,
|
||||||
|
SelectiveColorChoice::Greens => max_channel == g,
|
||||||
|
SelectiveColorChoice::Cyans => min_channel == r,
|
||||||
|
SelectiveColorChoice::Blues => max_channel == b,
|
||||||
|
SelectiveColorChoice::Magentas => min_channel == g,
|
||||||
|
SelectiveColorChoice::Whites => r > 0.5 && g > 0.5 && b > 0.5,
|
||||||
|
SelectiveColorChoice::Neutrals => r > 0. && g > 0. && b > 0. && r < 1. && g < 1. && b < 1.,
|
||||||
|
SelectiveColorChoice::Blacks => r < 0.5 && g < 0.5 && b < 0.5,
|
||||||
|
};
|
||||||
|
|
||||||
|
let color_parameter_group_scale_factor_rgb = max(r, g, b) - med(r, g, b);
|
||||||
|
let color_parameter_group_scale_factor_cmy = med(r, g, b) - min(r, g, b);
|
||||||
|
|
||||||
|
// Used to apply the r, g, or b channel slope (by multiplying it by 1) in relative mode, or no slope (by multiplying it by 0) in absolute mode
|
||||||
|
let (slope_r, slope_g, slope_b) = match mode {
|
||||||
|
RelativeAbsolute::Relative => (r - 1., g - 1., b - 1.),
|
||||||
|
RelativeAbsolute::Absolute => (-1., -1., -1.),
|
||||||
|
};
|
||||||
|
|
||||||
|
let (sum_r, sum_g, sum_b) = [
|
||||||
|
(SelectiveColorChoice::Reds, (r_c, r_m, r_y, r_k)),
|
||||||
|
(SelectiveColorChoice::Yellows, (y_c, y_m, y_y, y_k)),
|
||||||
|
(SelectiveColorChoice::Greens, (g_c, g_m, g_y, g_k)),
|
||||||
|
(SelectiveColorChoice::Cyans, (c_c, c_m, c_y, c_k)),
|
||||||
|
(SelectiveColorChoice::Blues, (b_c, b_m, b_y, b_k)),
|
||||||
|
(SelectiveColorChoice::Magentas, (m_c, m_m, m_y, m_k)),
|
||||||
|
(SelectiveColorChoice::Whites, (w_c, w_m, w_y, w_k)),
|
||||||
|
(SelectiveColorChoice::Neutrals, (n_c, n_m, n_y, n_k)),
|
||||||
|
(SelectiveColorChoice::Blacks, (k_c, k_m, k_y, k_k)),
|
||||||
|
]
|
||||||
|
.into_iter()
|
||||||
|
.fold((0., 0., 0.), |acc, (color_parameter_group, (c, m, y, k))| {
|
||||||
|
// Skip this color parameter group...
|
||||||
|
// ...if it's unchanged from the default of zero offset on all CMYK paramters, or...
|
||||||
|
// ...if this pixel's color isn't in the range affected by this color parameter group
|
||||||
|
if (c < f64::EPSILON && m < f64::EPSILON && y < f64::EPSILON && k < f64::EPSILON) || (!pixel_color_range(color_parameter_group)) {
|
||||||
|
return acc;
|
||||||
|
}
|
||||||
|
|
||||||
|
let (c, m, y, k) = (c as f32 / 100., m as f32 / 100., y as f32 / 100., k as f32 / 100.);
|
||||||
|
|
||||||
|
let color_parameter_group_scale_factor = match color_parameter_group {
|
||||||
|
SelectiveColorChoice::Reds | SelectiveColorChoice::Greens | SelectiveColorChoice::Blues => color_parameter_group_scale_factor_rgb,
|
||||||
|
SelectiveColorChoice::Cyans | SelectiveColorChoice::Magentas | SelectiveColorChoice::Yellows => color_parameter_group_scale_factor_cmy,
|
||||||
|
SelectiveColorChoice::Whites => min(r, g, b) * 2. - 1.,
|
||||||
|
SelectiveColorChoice::Neutrals => 1. - ((max(r, g, b) - 0.5).abs() + (min(r, g, b) - 0.5).abs()),
|
||||||
|
SelectiveColorChoice::Blacks => 1. - max(r, g, b) * 2.,
|
||||||
|
};
|
||||||
|
|
||||||
|
let offset_r = ((c + k * (c + 1.)) * slope_r).clamp(-r, -r + 1.) * color_parameter_group_scale_factor;
|
||||||
|
let offset_g = ((m + k * (m + 1.)) * slope_g).clamp(-g, -g + 1.) * color_parameter_group_scale_factor;
|
||||||
|
let offset_b = ((y + k * (y + 1.)) * slope_b).clamp(-b, -b + 1.) * color_parameter_group_scale_factor;
|
||||||
|
|
||||||
|
(acc.0 + offset_r, acc.1 + offset_g, acc.2 + offset_b)
|
||||||
|
});
|
||||||
|
|
||||||
|
let color = Color::from_rgbaf32_unchecked((r + sum_r).clamp(0., 1.), (g + sum_g).clamp(0., 1.), (b + sum_b).clamp(0., 1.), a);
|
||||||
|
|
||||||
|
color.to_linear_srgb()
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
pub struct OpacityNode<O> {
|
pub struct OpacityNode<O> {
|
||||||
opacity_multiplier: O,
|
opacity_multiplier: O,
|
||||||
|
|
|
||||||
|
|
@ -150,9 +150,9 @@ impl Color {
|
||||||
/// # Examples
|
/// # Examples
|
||||||
/// ```
|
/// ```
|
||||||
/// use graphene_core::raster::color::Color;
|
/// use graphene_core::raster::color::Color;
|
||||||
/// let color = Color::from_rgb8(0x72, 0x67, 0x62);
|
/// let color = Color::from_rgb8_srgb(0x72, 0x67, 0x62);
|
||||||
/// let color2 = Color::from_rgba8(0x72, 0x67, 0x62, 0xFF);
|
/// let color2 = Color::from_rgba8_srgb(0x72, 0x67, 0x62, 0xFF);
|
||||||
/// assert!(color == color2)
|
/// assert_eq!(color, color2)
|
||||||
/// ```
|
/// ```
|
||||||
pub fn from_rgb8_srgb(red: u8, green: u8, blue: u8) -> Color {
|
pub fn from_rgb8_srgb(red: u8, green: u8, blue: u8) -> Color {
|
||||||
Color::from_rgba8_srgb(red, green, blue, 255)
|
Color::from_rgba8_srgb(red, green, blue, 255)
|
||||||
|
|
@ -163,7 +163,7 @@ impl Color {
|
||||||
/// # Examples
|
/// # Examples
|
||||||
/// ```
|
/// ```
|
||||||
/// use graphene_core::raster::color::Color;
|
/// use graphene_core::raster::color::Color;
|
||||||
/// let color = Color::from_rgba8(0x72, 0x67, 0x62, 0x61);
|
/// let color = Color::from_rgba8_srgb(0x72, 0x67, 0x62, 0x61);
|
||||||
/// ```
|
/// ```
|
||||||
pub fn from_rgba8_srgb(red: u8, green: u8, blue: u8, alpha: u8) -> Color {
|
pub fn from_rgba8_srgb(red: u8, green: u8, blue: u8, alpha: u8) -> Color {
|
||||||
let map_range = |int_color| int_color as f32 / 255.0;
|
let map_range = |int_color| int_color as f32 / 255.0;
|
||||||
|
|
@ -503,8 +503,8 @@ impl Color {
|
||||||
/// # Examples
|
/// # Examples
|
||||||
/// ```
|
/// ```
|
||||||
/// use graphene_core::raster::color::Color;
|
/// use graphene_core::raster::color::Color;
|
||||||
/// let color = Color::from_rgba8(0x7C, 0x67, 0xFA, 0x61);
|
/// let color = Color::from_rgba8_srgb(0x52, 0x67, 0xFA, 0x61).to_gamma_srgb();
|
||||||
/// assert!("7C67FA61" == color.rgba_hex())
|
/// assert_eq!("5267FA61", color.rgba_hex())
|
||||||
/// ```
|
/// ```
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
pub fn rgba_hex(&self) -> String {
|
pub fn rgba_hex(&self) -> String {
|
||||||
|
|
@ -520,12 +520,12 @@ impl Color {
|
||||||
/// Return a 6-character RGB hex string (without a # prefix).
|
/// Return a 6-character RGB hex string (without a # prefix).
|
||||||
/// ```
|
/// ```
|
||||||
/// use graphene_core::raster::color::Color;
|
/// use graphene_core::raster::color::Color;
|
||||||
/// let color = Color::from_rgba8(0x7C, 0x67, 0xFA, 0x61);
|
/// let color = Color::from_rgba8_srgb(0x52, 0x67, 0xFA, 0x61).to_gamma_srgb();
|
||||||
/// assert!("7C67FA" == color.rgb_hex())
|
/// assert_eq!("5267FA", color.rgb_hex())
|
||||||
/// ```
|
/// ```
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
pub fn rgb_hex(&self) -> String {
|
pub fn rgb_hex(&self) -> String {
|
||||||
format!("{:02X?}{:02X?}{:02X?}", (self.r() * 255.) as u8, (self.g() * 255.) as u8, (self.b() * 255.) as u8,)
|
format!("{:02X?}{:02X?}{:02X?}", (self.r() * 255.) as u8, (self.g() * 255.) as u8, (self.b() * 255.) as u8)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return the all components as a u8 slice, first component is red, followed by green, followed by blue, followed by alpha.
|
/// Return the all components as a u8 slice, first component is red, followed by green, followed by blue, followed by alpha.
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,7 @@
|
||||||
use core::marker::PhantomData;
|
|
||||||
use dyn_any::{StaticType, StaticTypeSized};
|
|
||||||
|
|
||||||
use crate::Node;
|
use crate::Node;
|
||||||
|
|
||||||
|
use core::marker::PhantomData;
|
||||||
|
|
||||||
#[derive(Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
|
#[derive(Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
|
||||||
pub struct IntNode<const N: u32>;
|
pub struct IntNode<const N: u32>;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -39,6 +39,9 @@ pub enum TaggedValue {
|
||||||
Fill(graphene_core::vector::style::Fill),
|
Fill(graphene_core::vector::style::Fill),
|
||||||
Stroke(graphene_core::vector::style::Stroke),
|
Stroke(graphene_core::vector::style::Stroke),
|
||||||
VecF32(Vec<f32>),
|
VecF32(Vec<f32>),
|
||||||
|
RedGreenBlue(graphene_core::raster::RedGreenBlue),
|
||||||
|
RelativeAbsolute(graphene_core::raster::RelativeAbsolute),
|
||||||
|
SelectiveColorChoice(graphene_core::raster::SelectiveColorChoice),
|
||||||
LineCap(graphene_core::vector::style::LineCap),
|
LineCap(graphene_core::vector::style::LineCap),
|
||||||
LineJoin(graphene_core::vector::style::LineJoin),
|
LineJoin(graphene_core::vector::style::LineJoin),
|
||||||
FillType(graphene_core::vector::style::FillType),
|
FillType(graphene_core::vector::style::FillType),
|
||||||
|
|
@ -92,6 +95,9 @@ impl Hash for TaggedValue {
|
||||||
Self::Fill(fill) => fill.hash(state),
|
Self::Fill(fill) => fill.hash(state),
|
||||||
Self::Stroke(stroke) => stroke.hash(state),
|
Self::Stroke(stroke) => stroke.hash(state),
|
||||||
Self::VecF32(vec_f32) => vec_f32.iter().for_each(|val| val.to_bits().hash(state)),
|
Self::VecF32(vec_f32) => vec_f32.iter().for_each(|val| val.to_bits().hash(state)),
|
||||||
|
Self::RedGreenBlue(red_green_blue) => red_green_blue.hash(state),
|
||||||
|
Self::RelativeAbsolute(relative_absolute) => relative_absolute.hash(state),
|
||||||
|
Self::SelectiveColorChoice(selective_color_choice) => selective_color_choice.hash(state),
|
||||||
Self::LineCap(line_cap) => line_cap.hash(state),
|
Self::LineCap(line_cap) => line_cap.hash(state),
|
||||||
Self::LineJoin(line_join) => line_join.hash(state),
|
Self::LineJoin(line_join) => line_join.hash(state),
|
||||||
Self::FillType(fill_type) => fill_type.hash(state),
|
Self::FillType(fill_type) => fill_type.hash(state),
|
||||||
|
|
@ -151,6 +157,9 @@ impl<'a> TaggedValue {
|
||||||
TaggedValue::Fill(x) => Box::new(x),
|
TaggedValue::Fill(x) => Box::new(x),
|
||||||
TaggedValue::Stroke(x) => Box::new(x),
|
TaggedValue::Stroke(x) => Box::new(x),
|
||||||
TaggedValue::VecF32(x) => Box::new(x),
|
TaggedValue::VecF32(x) => Box::new(x),
|
||||||
|
TaggedValue::RedGreenBlue(x) => Box::new(x),
|
||||||
|
TaggedValue::RelativeAbsolute(x) => Box::new(x),
|
||||||
|
TaggedValue::SelectiveColorChoice(x) => Box::new(x),
|
||||||
TaggedValue::LineCap(x) => Box::new(x),
|
TaggedValue::LineCap(x) => Box::new(x),
|
||||||
TaggedValue::LineJoin(x) => Box::new(x),
|
TaggedValue::LineJoin(x) => Box::new(x),
|
||||||
TaggedValue::FillType(x) => Box::new(x),
|
TaggedValue::FillType(x) => Box::new(x),
|
||||||
|
|
@ -193,6 +202,9 @@ impl<'a> TaggedValue {
|
||||||
TaggedValue::Fill(_) => concrete!(graphene_core::vector::style::Fill),
|
TaggedValue::Fill(_) => concrete!(graphene_core::vector::style::Fill),
|
||||||
TaggedValue::Stroke(_) => concrete!(graphene_core::vector::style::Stroke),
|
TaggedValue::Stroke(_) => concrete!(graphene_core::vector::style::Stroke),
|
||||||
TaggedValue::VecF32(_) => concrete!(Vec<f32>),
|
TaggedValue::VecF32(_) => concrete!(Vec<f32>),
|
||||||
|
TaggedValue::RedGreenBlue(_) => concrete!(graphene_core::raster::RedGreenBlue),
|
||||||
|
TaggedValue::RelativeAbsolute(_) => concrete!(graphene_core::raster::RelativeAbsolute),
|
||||||
|
TaggedValue::SelectiveColorChoice(_) => concrete!(graphene_core::raster::SelectiveColorChoice),
|
||||||
TaggedValue::LineCap(_) => concrete!(graphene_core::vector::style::LineCap),
|
TaggedValue::LineCap(_) => concrete!(graphene_core::vector::style::LineCap),
|
||||||
TaggedValue::LineJoin(_) => concrete!(graphene_core::vector::style::LineJoin),
|
TaggedValue::LineJoin(_) => concrete!(graphene_core::vector::style::LineJoin),
|
||||||
TaggedValue::FillType(_) => concrete!(graphene_core::vector::style::FillType),
|
TaggedValue::FillType(_) => concrete!(graphene_core::vector::style::FillType),
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
use dyn_any::{DynAny, StaticType, StaticTypeSized};
|
use dyn_any::{DynAny, StaticType};
|
||||||
|
|
||||||
use glam::{DAffine2, DVec2};
|
use glam::{DAffine2, DVec2};
|
||||||
use graphene_core::raster::{Alpha, Channel, Image, ImageFrame, Luminance, Pixel, RasterMut, Sample};
|
use graphene_core::raster::{Alpha, Channel, Image, ImageFrame, Luminance, Pixel, RasterMut, Sample};
|
||||||
use graphene_core::transform::Transform;
|
use graphene_core::transform::Transform;
|
||||||
|
|
|
||||||
|
|
@ -262,6 +262,10 @@ fn node_registry() -> HashMap<NodeIdentifier, HashMap<NodeIOTypes, NodeConstruct
|
||||||
graphene_core::raster::ChannelMixerNode<_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _>,
|
graphene_core::raster::ChannelMixerNode<_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _>,
|
||||||
params: [bool, f64, f64, f64, f64, f64, f64, f64, f64, f64, f64, f64, f64, f64, f64, f64, f64]
|
params: [bool, f64, f64, f64, f64, f64, f64, f64, f64, f64, f64, f64, f64, f64, f64, f64, f64]
|
||||||
),
|
),
|
||||||
|
raster_node!(
|
||||||
|
graphene_core::raster::SelectiveColorNode<_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _>,
|
||||||
|
params: [RelativeAbsolute, f64, f64, f64, f64, f64, f64, f64, f64, f64, f64, f64, f64, f64, f64, f64, f64, f64, f64, f64, f64, f64, f64, f64, f64, f64, f64, f64, f64, f64, f64, f64, f64, f64, f64, f64, f64]
|
||||||
|
),
|
||||||
vec![(
|
vec![(
|
||||||
NodeIdentifier::new("graphene_core::raster::BrightnessContrastNode<_, _, _>"),
|
NodeIdentifier::new("graphene_core::raster::BrightnessContrastNode<_, _, _>"),
|
||||||
|args| {
|
|args| {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue