Compositor: Turn Color Key options to inputs

This patch turns the options of the Color Key node into inputs.

Reference #137223.

Pull Request: https://projects.blender.org/blender/blender/pulls/137815
This commit is contained in:
Omar Emara
2025-04-21 16:40:17 +02:00
committed by Omar Emara
parent 031755b9df
commit fa7d58ab4c
6 changed files with 169 additions and 83 deletions

View File

@@ -27,7 +27,7 @@
/* Blender file format version. */
#define BLENDER_FILE_VERSION BLENDER_VERSION
#define BLENDER_FILE_SUBVERSION 39
#define BLENDER_FILE_SUBVERSION 40
/* Minimum Blender version that supports reading file written with the current
* version. Older Blender versions will test this and cancel loading the file, showing a warning to

View File

@@ -836,6 +836,13 @@ static void write_compositor_legacy_properties(bNodeTree &node_tree)
write_input_to_property_float("Maximum", storage->t1);
write_input_to_property_float("Falloff", storage->fstrength);
}
if (node->type_legacy == CMP_NODE_COLOR_MATTE) {
NodeChroma *storage = static_cast<NodeChroma *>(node->storage);
write_input_to_property_float("Hue", storage->t1);
write_input_to_property_float("Saturation", storage->t2);
write_input_to_property_float("Value", storage->t3);
}
}
}

View File

@@ -2861,6 +2861,69 @@ static void do_version_chroma_matte_node_options_to_inputs_animation(bNodeTree *
});
}
/* The options were converted into inputs. */
static void do_version_color_matte_node_options_to_inputs(bNodeTree *node_tree, bNode *node)
{
NodeChroma *storage = static_cast<NodeChroma *>(node->storage);
if (!storage) {
return;
}
if (!blender::bke::node_find_socket(*node, SOCK_IN, "Hue")) {
bNodeSocket *input = blender::bke::node_add_static_socket(
*node_tree, *node, SOCK_IN, SOCK_FLOAT, PROP_FACTOR, "Hue", "Hue");
input->default_value_typed<bNodeSocketValueFloat>()->value = storage->t1;
}
if (!blender::bke::node_find_socket(*node, SOCK_IN, "Saturation")) {
bNodeSocket *input = blender::bke::node_add_static_socket(
*node_tree, *node, SOCK_IN, SOCK_FLOAT, PROP_FACTOR, "Saturation", "Saturation");
input->default_value_typed<bNodeSocketValueFloat>()->value = storage->t2;
}
if (!blender::bke::node_find_socket(*node, SOCK_IN, "Value")) {
bNodeSocket *input = blender::bke::node_add_static_socket(
*node_tree, *node, SOCK_IN, SOCK_FLOAT, PROP_FACTOR, "Value", "Value");
input->default_value_typed<bNodeSocketValueFloat>()->value = storage->t3;
}
}
/* The options were converted into inputs. */
static void do_version_color_matte_node_options_to_inputs_animation(bNodeTree *node_tree,
bNode *node)
{
/* Compute the RNA path of the node. */
char escaped_node_name[sizeof(node->name) * 2 + 1];
BLI_str_escape(escaped_node_name, node->name, sizeof(escaped_node_name));
const std::string node_rna_path = fmt::format("nodes[\"{}\"]", escaped_node_name);
BKE_fcurves_id_cb(&node_tree->id, [&](ID * /*id*/, FCurve *fcurve) {
/* The FCurve does not belong to the node since its RNA path doesn't start with the node's RNA
* path. */
if (!blender::StringRef(fcurve->rna_path).startswith(node_rna_path)) {
return;
}
/* Change the RNA path of the FCurve from the old properties to the new inputs, adjusting the
* values of the FCurves frames when needed. */
char *old_rna_path = fcurve->rna_path;
if (BLI_str_endswith(fcurve->rna_path, "color_hue")) {
fcurve->rna_path = BLI_sprintfN("%s.%s", node_rna_path.c_str(), "inputs[2].default_value");
}
else if (BLI_str_endswith(fcurve->rna_path, "color_saturation")) {
fcurve->rna_path = BLI_sprintfN("%s.%s", node_rna_path.c_str(), "inputs[3].default_value");
}
else if (BLI_str_endswith(fcurve->rna_path, "color_value")) {
fcurve->rna_path = BLI_sprintfN("%s.%s", node_rna_path.c_str(), "inputs[4].default_value");
}
/* The RNA path was changed, free the old path. */
if (fcurve->rna_path != old_rna_path) {
MEM_freeN(old_rna_path);
}
});
}
static void do_version_viewer_shortcut(bNodeTree *node_tree)
{
LISTBASE_FOREACH_MUTABLE (bNode *, node, &node_tree->nodes) {
@@ -3536,6 +3599,19 @@ void do_versions_after_linking_400(FileData *fd, Main *bmain)
FOREACH_NODETREE_END;
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 405, 40)) {
FOREACH_NODETREE_BEGIN (bmain, node_tree, id) {
if (node_tree->type == NTREE_COMPOSIT) {
LISTBASE_FOREACH (bNode *, node, &node_tree->nodes) {
if (node->type_legacy == CMP_NODE_COLOR_MATTE) {
do_version_color_matte_node_options_to_inputs_animation(node_tree, node);
}
}
}
}
FOREACH_NODETREE_END;
}
/**
* Always bump subversion in BKE_blender_version.h when adding versioning
* code here, and wrap it inside a MAIN_VERSION_FILE_ATLEAST check.
@@ -8329,6 +8405,19 @@ void blo_do_versions_400(FileData *fd, Library * /*lib*/, Main *bmain)
FOREACH_NODETREE_END;
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 405, 40)) {
FOREACH_NODETREE_BEGIN (bmain, node_tree, id) {
if (node_tree->type == NTREE_COMPOSIT) {
LISTBASE_FOREACH (bNode *, node, &node_tree->nodes) {
if (node->type_legacy == CMP_NODE_COLOR_MATTE) {
do_version_color_matte_node_options_to_inputs(node_tree, node);
}
}
}
}
FOREACH_NODETREE_END;
}
/* Always run this versioning (keep at the bottom of the function). Meshes are written with the
* legacy format which always needs to be converted to the new format on file load. To be moved
* to a subversion check in 5.0. */

View File

@@ -6,7 +6,7 @@
void node_composite_color_matte(float4 color,
float4 key,
float hue_epsilon,
float hue_threshold,
float saturation_epsilon,
float value_epsilon,
out float4 result,
@@ -18,6 +18,9 @@ void node_composite_color_matte(float4 color,
float4 key_hsva;
rgb_to_hsv(key, key_hsva);
/* Divide by 2 because the hue wraps around. */
float hue_epsilon = hue_threshold / 2.0f;
bool is_within_saturation = distance(color_hsva.y, key_hsva.y) < saturation_epsilon;
bool is_within_value = distance(color_hsva.z, key_hsva.z) < value_epsilon;
bool is_within_hue = distance(color_hsva.x, key_hsva.x) < hue_epsilon;

View File

@@ -3970,6 +3970,11 @@ static const char node_input_maximum[] = "Maximum";
/* Chroma Key node. */
static const char node_input_falloff[] = "Falloff";
/* Color Key node. */
static const char node_input_hue[] = "Hue";
static const char node_input_saturation[] = "Saturation";
static const char node_input_value[] = "Value";
/* --------------------------------------------------------------------
* White Balance Node.
*/
@@ -7789,21 +7794,37 @@ static void def_cmp_color_matte(BlenderRNA * /*brna*/, StructRNA *srna)
RNA_def_struct_sdna_from(srna, "NodeChroma", "storage");
prop = RNA_def_property(srna, "color_hue", PROP_FLOAT, PROP_NONE);
RNA_def_property_float_sdna(prop, nullptr, "t1");
RNA_def_property_float_funcs(prop,
"rna_node_property_to_input_getter<float, node_input_hue>",
"rna_node_property_to_input_setter<float, node_input_hue>",
nullptr);
RNA_def_property_range(prop, 0.0f, 1.0f);
RNA_def_property_ui_text(prop, "H", "Hue tolerance for colors to be considered a keying color");
RNA_def_property_ui_text(prop,
"H",
"Hue tolerance for colors to be considered a keying color. "
"(Deprecated: Use Hue input instead.)");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
prop = RNA_def_property(srna, "color_saturation", PROP_FLOAT, PROP_NONE);
RNA_def_property_float_sdna(prop, nullptr, "t2");
RNA_def_property_float_funcs(prop,
"rna_node_property_to_input_getter<float, node_input_saturation>",
"rna_node_property_to_input_setter<float, node_input_saturation>",
nullptr);
RNA_def_property_range(prop, 0.0f, 1.0f);
RNA_def_property_ui_text(prop, "S", "Saturation tolerance for the color");
RNA_def_property_ui_text(
prop,
"S",
"Saturation tolerance for the color. (Deprecated: Use Saturation input instead.)");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
prop = RNA_def_property(srna, "color_value", PROP_FLOAT, PROP_NONE);
RNA_def_property_float_sdna(prop, nullptr, "t3");
RNA_def_property_float_funcs(prop,
"rna_node_property_to_input_getter<float, node_input_value>",
"rna_node_property_to_input_setter<float, node_input_value>",
nullptr);
RNA_def_property_range(prop, 0.0f, 1.0f);
RNA_def_property_ui_text(prop, "V", "Value tolerance for the color");
RNA_def_property_ui_text(
prop, "V", "Value tolerance for the color. (Deprecated: Use Value input instead.)");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
}

View File

@@ -30,12 +30,33 @@ NODE_STORAGE_FUNCS(NodeChroma)
static void cmp_node_color_matte_declare(NodeDeclarationBuilder &b)
{
b.add_input<decl::Color>("Image")
.default_value({1.0f, 1.0f, 1.0f, 1.0f})
.compositor_domain_priority(0);
b.add_input<decl::Color>("Key Color")
.default_value({1.0f, 1.0f, 1.0f, 1.0f})
.compositor_domain_priority(1);
b.add_input<decl::Color>("Image").default_value({1.0f, 1.0f, 1.0f, 1.0f});
b.add_input<decl::Color>("Key Color").default_value({1.0f, 1.0f, 1.0f, 1.0f});
b.add_input<decl::Float>("Hue")
.default_value(0.01f)
.subtype(PROP_FACTOR)
.min(0.0f)
.max(1.0f)
.description(
"If the difference in hue between the color and key color is less than this threshold, "
"it is keyed");
b.add_input<decl::Float>("Saturation")
.default_value(0.1f)
.subtype(PROP_FACTOR)
.min(0.0f)
.max(1.0f)
.description(
"If the difference in saturation between the color and key color is less than this "
"threshold, it is keyed");
b.add_input<decl::Float>("Value")
.default_value(0.1f)
.subtype(PROP_FACTOR)
.min(0.0f)
.max(1.0f)
.description(
"If the difference in value between the color and key color is less than this "
"threshold, it is keyed");
b.add_output<decl::Color>("Image");
b.add_output<decl::Float>("Matte");
}
@@ -51,72 +72,20 @@ static void node_composit_init_color_matte(bNodeTree * /*ntree*/, bNode *node)
c->fstrength = 1.0f;
}
static void node_composit_buts_color_matte(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr)
{
uiLayout *col;
col = uiLayoutColumn(layout, true);
uiItemR(col,
ptr,
"color_hue",
UI_ITEM_R_SPLIT_EMPTY_NAME | UI_ITEM_R_SLIDER,
std::nullopt,
ICON_NONE);
uiItemR(col,
ptr,
"color_saturation",
UI_ITEM_R_SPLIT_EMPTY_NAME | UI_ITEM_R_SLIDER,
std::nullopt,
ICON_NONE);
uiItemR(col,
ptr,
"color_value",
UI_ITEM_R_SPLIT_EMPTY_NAME | UI_ITEM_R_SLIDER,
std::nullopt,
ICON_NONE);
}
using namespace blender::compositor;
static float get_hue_epsilon(const bNode &node)
{
/* Divide by 2 because the hue wraps around. */
return node_storage(node).t1 / 2.0f;
}
static float get_saturation_epsilon(const bNode &node)
{
return node_storage(node).t2;
}
static float get_value_epsilon(const bNode &node)
{
return node_storage(node).t3;
}
static int node_gpu_material(GPUMaterial *material,
bNode *node,
bNodeExecData * /*execdata*/,
GPUNodeStack *inputs,
GPUNodeStack *outputs)
{
const float hue_epsilon = get_hue_epsilon(*node);
const float saturation_epsilon = get_saturation_epsilon(*node);
const float value_epsilon = get_value_epsilon(*node);
return GPU_stack_link(material,
node,
"node_composite_color_matte",
inputs,
outputs,
GPU_uniform(&hue_epsilon),
GPU_uniform(&saturation_epsilon),
GPU_uniform(&value_epsilon));
return GPU_stack_link(material, node, "node_composite_color_matte", inputs, outputs);
}
static void color_matte(const float4 color,
const float4 key,
const float hue_epsilon,
const float hue_threshold,
const float saturation_epsilon,
const float value_epsilon,
float4 &result,
@@ -127,6 +96,9 @@ static void color_matte(const float4 color,
float3 key_hsva;
rgb_to_hsv_v(key, key_hsva);
/* Divide by 2 because the hue wraps around. */
float hue_epsilon = hue_threshold / 2.0f;
bool is_within_saturation = math::distance(color_hsva.y, key_hsva.y) < saturation_epsilon;
bool is_within_value = math::distance(color_hsva.z, key_hsva.z) < value_epsilon;
bool is_within_hue = math::distance(color_hsva.x, key_hsva.x) < hue_epsilon;
@@ -141,22 +113,17 @@ static void color_matte(const float4 color,
static void node_build_multi_function(blender::nodes::NodeMultiFunctionBuilder &builder)
{
const float hue_epsilon = get_hue_epsilon(builder.node());
const float saturation_epsilon = get_saturation_epsilon(builder.node());
const float value_epsilon = get_value_epsilon(builder.node());
builder.construct_and_set_matching_fn_cb([=]() {
return mf::build::SI2_SO2<float4, float4, float4, float>(
return mf::build::SI5_SO2<float4, float4, float, float, float, float4, float>(
"Color Key",
[=](const float4 &color, const float4 &key_color, float4 &output_color, float &matte)
-> void {
color_matte(color,
key_color,
hue_epsilon,
saturation_epsilon,
value_epsilon,
output_color,
matte);
[=](const float4 &color,
const float4 &key_color,
const float &hue,
const float &saturation,
const float &value,
float4 &output_color,
float &matte) -> void {
color_matte(color, key_color, hue, saturation, value, output_color, matte);
},
mf::build::exec_presets::AllSpanOrSingle());
});
@@ -176,7 +143,6 @@ void register_node_type_cmp_color_matte()
ntype.enum_name_legacy = "COLOR_MATTE";
ntype.nclass = NODE_CLASS_MATTE;
ntype.declare = file_ns::cmp_node_color_matte_declare;
ntype.draw_buttons = file_ns::node_composit_buts_color_matte;
ntype.flag |= NODE_PREVIEW;
ntype.initfunc = file_ns::node_composit_init_color_matte;
blender::bke::node_type_storage(