Compositor: Turn Channel Key options to inputs

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

Reference #137223.

Pull Request: https://projects.blender.org/blender/blender/pulls/137806
This commit is contained in:
Omar Emara
2025-04-21 14:02:15 +02:00
committed by Omar Emara
parent 8afde59054
commit 89b268bf0c
7 changed files with 170 additions and 56 deletions

View File

@@ -27,7 +27,7 @@
/* Blender file format version. */
#define BLENDER_FILE_VERSION BLENDER_VERSION
#define BLENDER_FILE_SUBVERSION 37
#define BLENDER_FILE_SUBVERSION 38
/* 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

@@ -823,6 +823,12 @@ static void write_compositor_legacy_properties(bNodeTree &node_tree)
const bNodeSocket *input = blender::bke::node_find_socket(*node, SOCK_IN, "Shutter");
storage->fac = input->default_value_typed<bNodeSocketValueFloat>()->value / 2.0f;
}
if (node->type_legacy == CMP_NODE_CHANNEL_MATTE) {
NodeChroma *storage = static_cast<NodeChroma *>(node->storage);
write_input_to_property_float("Minimum", storage->t2);
write_input_to_property_float("Maximum", storage->t1);
}
}
}

View File

@@ -2744,6 +2744,60 @@ static void do_version_vector_blur_node_options_to_inputs_animation(bNodeTree *n
});
}
/* The options were converted into inputs. */
static void do_version_channel_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, "Minimum")) {
bNodeSocket *input = blender::bke::node_add_static_socket(
*node_tree, *node, SOCK_IN, SOCK_FLOAT, PROP_FACTOR, "Minimum", "Minimum");
input->default_value_typed<bNodeSocketValueFloat>()->value = storage->t2;
}
if (!blender::bke::node_find_socket(*node, SOCK_IN, "Maximum")) {
bNodeSocket *input = blender::bke::node_add_static_socket(
*node_tree, *node, SOCK_IN, SOCK_FLOAT, PROP_FACTOR, "Maximum", "Maximum");
input->default_value_typed<bNodeSocketValueFloat>()->value = storage->t1;
}
}
/* The options were converted into inputs. */
static void do_version_channel_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, "limit_min")) {
fcurve->rna_path = BLI_sprintfN("%s.%s", node_rna_path.c_str(), "inputs[1].default_value");
}
else if (BLI_str_endswith(fcurve->rna_path, "limit_max")) {
fcurve->rna_path = BLI_sprintfN("%s.%s", node_rna_path.c_str(), "inputs[2].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) {
@@ -3393,6 +3447,19 @@ void do_versions_after_linking_400(FileData *fd, Main *bmain)
FOREACH_NODETREE_END;
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 405, 38)) {
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_CHANNEL_MATTE) {
do_version_channel_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.
@@ -8160,6 +8227,19 @@ void blo_do_versions_400(FileData *fd, Library * /*lib*/, Main *bmain)
FOREACH_NODETREE_END;
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 405, 38)) {
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_CHANNEL_MATTE) {
do_version_channel_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

@@ -10,11 +10,11 @@
#define CMP_NODE_CHANNEL_MATTE_CS_YCC 4.0f
void node_composite_channel_matte(float4 color,
float min_limit,
float max_limit,
const float color_space,
const float matte_channel,
const float2 limit_channels,
float max_limit,
float min_limit,
out float4 result,
out float matte)
{

View File

@@ -689,6 +689,22 @@ inline auto SI2_SO2(const char *name,
name, element_fn, exec_preset, TypeSequence<In1, In2>());
}
/** Build multi-function with 3 single-input and 2 single-output parameter. */
template<typename In1,
typename In2,
typename In3,
typename Out1,
typename Out2,
typename ElementFn,
typename ExecPreset = exec_presets::Materialized>
inline auto SI3_SO2(const char *name,
const ElementFn element_fn,
const ExecPreset exec_preset = exec_presets::Materialized())
{
return detail::build_multi_function_with_n_inputs_two_outputs<Out1, Out2>(
name, element_fn, exec_preset, TypeSequence<In1, In2, In3>());
}
/** Build multi-function with 1 single-input and 3 single output parameter. */
template<typename In1,
typename Out1,

View File

@@ -3963,6 +3963,10 @@ static const char node_input_corner_rounding[] = "Corner Rounding";
/* Vector Blur node. */
static const char node_input_samples[] = "Samples";
/* Channel Key node. */
static const char node_input_minimum[] = "Minimum";
static const char node_input_maximum[] = "Maximum";
/* --------------------------------------------------------------------
* White Balance Node.
*/
@@ -8064,17 +8068,27 @@ static void def_cmp_channel_matte(BlenderRNA * /*brna*/, StructRNA *srna)
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
prop = RNA_def_property(srna, "limit_max", PROP_FLOAT, PROP_NONE);
RNA_def_property_float_sdna(prop, nullptr, "t1");
RNA_def_property_float_funcs(prop, nullptr, "rna_Matte_t1_set", nullptr);
RNA_def_property_float_funcs(prop,
"rna_node_property_to_input_getter<float, node_input_maximum>",
"rna_node_property_to_input_setter<float, node_input_maximum>",
nullptr);
RNA_def_property_ui_range(prop, 0, 1, 0.1f, 3);
RNA_def_property_ui_text(prop, "High", "Values higher than this setting are 100% opaque");
RNA_def_property_ui_text(
prop,
"High",
"Values higher than this setting are 100% opaque. (Deprecated: Use Maximum input instead.)");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
prop = RNA_def_property(srna, "limit_min", PROP_FLOAT, PROP_NONE);
RNA_def_property_float_sdna(prop, nullptr, "t2");
RNA_def_property_float_funcs(prop, nullptr, "rna_Matte_t2_set", nullptr);
RNA_def_property_float_funcs(prop,
"rna_node_property_to_input_getter<float, node_input_minimum>",
"rna_node_property_to_input_setter<float, node_input_minimum>",
nullptr);
RNA_def_property_ui_range(prop, 0, 1, 0.1f, 3);
RNA_def_property_ui_text(prop, "Low", "Values lower than this setting are 100% keyed");
RNA_def_property_ui_text(
prop,
"Low",
"Values lower than this setting are 100% keyed. (Deprecated: Use Minimum input instead.)");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
}

View File

@@ -32,9 +32,20 @@ NODE_STORAGE_FUNCS(NodeChroma)
static void cmp_node_channel_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>("Image").default_value({1.0f, 1.0f, 1.0f, 1.0f});
b.add_input<decl::Float>("Minimum")
.default_value(0.0f)
.subtype(PROP_FACTOR)
.min(0.0f)
.max(1.0f)
.description("Channel values lower than this minimum are keyed");
b.add_input<decl::Float>("Maximum")
.default_value(1.0f)
.subtype(PROP_FACTOR)
.min(0.0f)
.max(1.0f)
.description("Channel values higher than this maximum are not keyed");
b.add_output<decl::Color>("Image");
b.add_output<decl::Float>("Matte");
}
@@ -90,19 +101,6 @@ static void node_composit_buts_channel_matte(uiLayout *layout, bContext * /*C*/,
std::nullopt,
ICON_NONE);
}
uiItemR(col,
ptr,
"limit_max",
UI_ITEM_R_SPLIT_EMPTY_NAME | UI_ITEM_R_SLIDER,
std::nullopt,
ICON_NONE);
uiItemR(col,
ptr,
"limit_min",
UI_ITEM_R_SPLIT_EMPTY_NAME | UI_ITEM_R_SLIDER,
std::nullopt,
ICON_NONE);
}
using namespace blender::compositor;
@@ -145,16 +143,6 @@ static int2 get_limit_channels(const bNode &node)
return limit_channels;
}
static float get_max_limit(const bNode &node)
{
return node_storage(node).t1;
}
static float get_min_limit(const bNode &node)
{
return node_storage(node).t2;
}
static int node_gpu_material(GPUMaterial *material,
bNode *node,
bNodeExecData * /*execdata*/,
@@ -164,8 +152,6 @@ static int node_gpu_material(GPUMaterial *material,
const float color_space = int(get_color_space(*node));
const float matte_channel = get_matte_channel(*node);
const float2 limit_channels = float2(get_limit_channels(*node));
const float max_limit = get_max_limit(*node);
const float min_limit = get_min_limit(*node);
return GPU_stack_link(material,
node,
@@ -174,17 +160,15 @@ static int node_gpu_material(GPUMaterial *material,
outputs,
GPU_constant(&color_space),
GPU_constant(&matte_channel),
GPU_constant(limit_channels),
GPU_uniform(&max_limit),
GPU_uniform(&min_limit));
GPU_constant(limit_channels));
}
template<CMPNodeChannelMatteColorSpace ColorSpace>
static void channel_key(const float4 &color,
const int matte_channel,
const int2 limit_channels,
const float min_limit,
const float max_limit,
const int matte_channel,
const int2 limit_channels,
float4 &result,
float &matte)
{
@@ -228,50 +212,64 @@ static void node_build_multi_function(blender::nodes::NodeMultiFunctionBuilder &
const CMPNodeChannelMatteColorSpace color_space = get_color_space(builder.node());
const int matte_channel = get_matte_channel(builder.node());
const int2 limit_channels = get_limit_channels(builder.node());
const float min_limit = get_min_limit(builder.node());
const float max_limit = get_max_limit(builder.node());
switch (color_space) {
case CMP_NODE_CHANNEL_MATTE_CS_RGB:
builder.construct_and_set_matching_fn_cb([=]() {
return mf::build::SI1_SO2<float4, float4, float>(
return mf::build::SI3_SO2<float4, float, float, float4, float>(
"Channel Key RGB",
[=](const float4 &color, float4 &output_color, float &matte) -> void {
[=](const float4 &color,
const float &minimum,
const float &maximum,
float4 &output_color,
float &matte) -> void {
channel_key<CMP_NODE_CHANNEL_MATTE_CS_RGB>(
color, matte_channel, limit_channels, min_limit, max_limit, output_color, matte);
color, minimum, maximum, matte_channel, limit_channels, output_color, matte);
},
mf::build::exec_presets::AllSpanOrSingle());
});
break;
case CMP_NODE_CHANNEL_MATTE_CS_HSV:
builder.construct_and_set_matching_fn_cb([=]() {
return mf::build::SI1_SO2<float4, float4, float>(
return mf::build::SI3_SO2<float4, float, float, float4, float>(
"Channel Key HSV",
[=](const float4 &color, float4 &output_color, float &matte) -> void {
[=](const float4 &color,
const float &minimum,
const float &maximum,
float4 &output_color,
float &matte) -> void {
channel_key<CMP_NODE_CHANNEL_MATTE_CS_HSV>(
color, matte_channel, limit_channels, min_limit, max_limit, output_color, matte);
color, minimum, maximum, matte_channel, limit_channels, output_color, matte);
},
mf::build::exec_presets::AllSpanOrSingle());
});
break;
case CMP_NODE_CHANNEL_MATTE_CS_YUV:
builder.construct_and_set_matching_fn_cb([=]() {
return mf::build::SI1_SO2<float4, float4, float>(
return mf::build::SI3_SO2<float4, float, float, float4, float>(
"Channel Key YUV",
[=](const float4 &color, float4 &output_color, float &matte) -> void {
[=](const float4 &color,
const float &minimum,
const float &maximum,
float4 &output_color,
float &matte) -> void {
channel_key<CMP_NODE_CHANNEL_MATTE_CS_YUV>(
color, matte_channel, limit_channels, min_limit, max_limit, output_color, matte);
color, minimum, maximum, matte_channel, limit_channels, output_color, matte);
},
mf::build::exec_presets::AllSpanOrSingle());
});
break;
case CMP_NODE_CHANNEL_MATTE_CS_YCC:
builder.construct_and_set_matching_fn_cb([=]() {
return mf::build::SI1_SO2<float4, float4, float>(
return mf::build::SI3_SO2<float4, float, float, float4, float>(
"Channel Key YCC",
[=](const float4 &color, float4 &output_color, float &matte) -> void {
[=](const float4 &color,
const float &minimum,
const float &maximum,
float4 &output_color,
float &matte) -> void {
channel_key<CMP_NODE_CHANNEL_MATTE_CS_YCC>(
color, matte_channel, limit_channels, min_limit, max_limit, output_color, matte);
color, minimum, maximum, matte_channel, limit_channels, output_color, matte);
},
mf::build::exec_presets::AllSpanOrSingle());
});