Compositor: Turn Alpha Over options to inputs

This patch turns the options of the Alpha Over node into inputs.

In the process, Convert Premultiplied option was renamed to Straight
Alpha.

Reference #137223.

Pull Request: https://projects.blender.org/blender/blender/pulls/138441
This commit is contained in:
Omar Emara
2025-05-05 14:16:27 +02:00
committed by Omar Emara
parent 787f7272ae
commit 1f51172692
6 changed files with 118 additions and 93 deletions

View File

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

@@ -1018,6 +1018,10 @@ static void write_compositor_legacy_properties(bNodeTree &node_tree)
storage->sigma_color = threshold_input->default_value_typed<bNodeSocketValueFloat>()->value *
3.0f;
}
if (node->type_legacy == CMP_NODE_ALPHAOVER) {
write_input_to_property_bool_short("Straight Alpha", node->custom1);
}
}
}

View File

@@ -4508,6 +4508,46 @@ static void do_version_alpha_over_remove_premultiply(bNodeTree *node_tree)
}
}
/* The options were converted into inputs. */
static void do_version_alpha_over_node_options_to_inputs(bNodeTree *node_tree, bNode *node)
{
if (!blender::bke::node_find_socket(*node, SOCK_IN, "Straight Alpha")) {
bNodeSocket *input = blender::bke::node_add_static_socket(
*node_tree, *node, SOCK_IN, SOCK_BOOLEAN, PROP_NONE, "Straight Alpha", "Straight Alpha");
input->default_value_typed<bNodeSocketValueBoolean>()->value = bool(node->custom1);
}
}
/* The options were converted into inputs. */
static void do_version_alpha_over_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, "use_premultiply")) {
fcurve->rna_path = BLI_sprintfN("%s.%s", node_rna_path.c_str(), "inputs[3].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) {
@@ -5404,6 +5444,19 @@ void do_versions_after_linking_400(FileData *fd, Main *bmain)
FOREACH_NODETREE_END;
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 405, 64)) {
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_ALPHAOVER) {
do_version_alpha_over_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.
@@ -10550,6 +10603,19 @@ void blo_do_versions_400(FileData *fd, Library * /*lib*/, Main *bmain)
FOREACH_NODETREE_END;
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 405, 64)) {
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_ALPHAOVER) {
do_version_alpha_over_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

@@ -2,36 +2,16 @@
*
* SPDX-License-Identifier: GPL-2.0-or-later */
void node_composite_alpha_over_key(float factor,
float4 color,
float4 over_color,
out float4 result)
/* Computes the Porter and Duff Over compositing operation. If straight_alpha is true, then the
* foreground is in straight alpha form and would need to be premultiplied. */
void node_composite_alpha_over(
float factor, float4 background, float4 foreground, float straight_alpha, out float4 result)
{
if (over_color.a <= 0.0f) {
result = color;
}
else if (factor == 1.0f && over_color.a >= 1.0f) {
result = over_color;
}
else {
result = mix(color, float4(over_color.rgb, 1.0f), factor * over_color.a);
}
}
/* Premultiply the alpha of the foreground if it is straight. */
const float alpha = clamp(foreground.w, 0.0f, 1.0f);
const float4 premultiplied_foreground = float4(foreground.xyz * alpha, alpha);
const float4 foreground_color = straight_alpha != 0.0f ? premultiplied_foreground : foreground;
void node_composite_alpha_over_premultiply(float factor,
float4 color,
float4 over_color,
out float4 result)
{
if (over_color.a < 0.0f) {
result = color;
}
else if (factor == 1.0f && over_color.a >= 1.0f) {
result = over_color;
}
else {
float multiplier = 1.0f - factor * over_color.a;
result = multiplier * color + factor * over_color;
}
const float4 mix_result = background * (1.0f - alpha) + foreground_color;
result = mix(background, mix_result, factor);
}

View File

@@ -4224,6 +4224,9 @@ static const char node_input_center[] = "Center";
static const char node_input_translation_amount[] = "Translation Amount";
static const char node_input_translation_direction[] = "Translation Direction";
/* Alpha Over node. */
static const char node_input_straight_alpha[] = "Straight Alpha";
/* --------------------------------------------------------------------
* White Balance Node.
*/
@@ -7285,10 +7288,13 @@ static void def_cmp_alpha_over(BlenderRNA * /*brna*/, StructRNA *srna)
{
PropertyRNA *prop;
/* XXX: Tooltip */
prop = RNA_def_property(srna, "use_premultiply", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, nullptr, "custom1", 1);
RNA_def_property_ui_text(prop, "Convert Premultiplied", "");
RNA_def_property_boolean_funcs(
prop,
"rna_node_property_to_input_getter<bool, node_input_straight_alpha>",
"rna_node_property_to_input_setter<bool, node_input_straight_alpha>");
RNA_def_property_ui_text(
prop, "Convert Premultiplied", "(Deprecated: Use Straight Alpha input instead.)");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
RNA_def_struct_sdna_from(srna, "NodeTwoFloats", "storage");

View File

@@ -38,6 +38,13 @@ static void cmp_node_alphaover_declare(NodeDeclarationBuilder &b)
b.add_input<decl::Color>("Image", "Image_001")
.default_value({1.0f, 1.0f, 1.0f, 1.0f})
.compositor_domain_priority(1);
b.add_input<decl::Bool>("Straight Alpha")
.default_value(false)
.description(
"Defines whether the foreground is in straight alpha form, which is necessary to know "
"for proper alpha compositing. Images in the compositor are in premultiplied alpha form "
"by default, so this should be false in most cases. But if, and only if, the foreground "
"was converted to straight alpha form for some reason, this should be set to true");
b.add_output<decl::Color>("Image");
}
@@ -47,82 +54,45 @@ static void node_alphaover_init(bNodeTree * /*ntree*/, bNode *node)
node->storage = MEM_callocN<NodeTwoFloats>(__func__);
}
static void node_composit_buts_alphaover(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr)
{
uiItemR(layout, ptr, "use_premultiply", UI_ITEM_R_SPLIT_EMPTY_NAME, std::nullopt, ICON_NONE);
}
using namespace blender::compositor;
static bool get_use_premultiply(const bNode &node)
{
return node.custom1;
}
static int node_gpu_material(GPUMaterial *material,
bNode *node,
bNodeExecData * /*execdata*/,
GPUNodeStack *inputs,
GPUNodeStack *outputs)
{
if (get_use_premultiply(*node)) {
return GPU_stack_link(material, node, "node_composite_alpha_over_key", inputs, outputs);
}
return GPU_stack_link(material, node, "node_composite_alpha_over_premultiply", inputs, outputs);
return GPU_stack_link(material, node, "node_composite_alpha_over", inputs, outputs);
}
static float4 alpha_over_key(const float factor, const float4 &color, const float4 &over_color)
/* Computes the Porter and Duff Over compositing operation. If straight_alpha is true, then the
* foreground is in straight alpha form and would need to be premultiplied. */
static float4 alpha_over(const float factor,
const float4 &background,
const float4 &foreground,
const bool straight_alpha)
{
if (over_color.w <= 0.0f) {
return color;
}
/* Premultiply the alpha of the foreground if it is straight. */
const float alpha = math::clamp(foreground.w, 0.0f, 1.0f);
const float4 premultiplied_foreground = float4(foreground.xyz() * alpha, alpha);
const float4 foreground_color = straight_alpha ? premultiplied_foreground : foreground;
if (factor == 1.0f && over_color.w >= 1.0f) {
return over_color;
}
return math::interpolate(color, float4(over_color.xyz(), 1.0f), factor * over_color.w);
}
static float4 alpha_over_premultiply(const float factor,
const float4 &color,
const float4 &over_color)
{
if (over_color.w < 0.0f) {
return color;
}
if (factor == 1.0f && over_color.w >= 1.0f) {
return over_color;
}
float multiplier = 1.0f - factor * over_color.w;
return multiplier * color + factor * over_color;
const float4 mix_result = background * (1.0f - alpha) + foreground_color;
return math::interpolate(background, mix_result, factor);
}
static void node_build_multi_function(blender::nodes::NodeMultiFunctionBuilder &builder)
{
static auto key_function = mf::build::SI3_SO<float, float4, float4, float4>(
"Alpha Over Key",
[=](const float factor, const float4 &color, const float4 &over_color) -> float4 {
return alpha_over_key(factor, color, over_color);
static auto function = mf::build::SI4_SO<float, float4, float4, bool, float4>(
"Alpha Over",
[=](const float factor,
const float4 &background,
const float4 &foreground,
const bool straight_alpha) -> float4 {
return alpha_over(factor, background, foreground, straight_alpha);
},
mf::build::exec_presets::SomeSpanOrSingle<1, 2>());
static auto premultiply_function = mf::build::SI3_SO<float, float4, float4, float4>(
"Alpha Over Premultiply",
[=](const float factor, const float4 &color, const float4 &over_color) -> float4 {
return alpha_over_premultiply(factor, color, over_color);
},
mf::build::exec_presets::SomeSpanOrSingle<1, 2>());
if (get_use_premultiply(builder.node())) {
builder.set_matching_fn(key_function);
}
else {
builder.set_matching_fn(premultiply_function);
}
builder.set_matching_fn(function);
}
} // namespace blender::nodes::node_composite_alpha_over_cc
@@ -139,7 +109,6 @@ void register_node_type_cmp_alphaover()
ntype.enum_name_legacy = "ALPHAOVER";
ntype.nclass = NODE_CLASS_OP_COLOR;
ntype.declare = file_ns::cmp_node_alphaover_declare;
ntype.draw_buttons = file_ns::node_composit_buts_alphaover;
ntype.initfunc = file_ns::node_alphaover_init;
blender::bke::node_type_storage(
ntype, "NodeTwoFloats", node_free_standard_storage, node_copy_standard_storage);