Blender V5.0
node_composite_alpha_over.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2006 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
5#include "BLI_math_vector.hh"
7
9
10#include "NOD_multi_function.hh"
11
12#include "UI_resources.hh"
13
14#include "GPU_material.hh"
15
17
19
20static const EnumPropertyItem type_items[] = {
22 "OVER",
23 ICON_NONE,
24 N_("Over"),
25 N_("The foreground goes over the background according to the alpha of the foreground")},
27 "DISJOINT_OVER",
28 ICON_NONE,
29 N_("Disjoint Over"),
30 N_("The foreground goes over the background according to the alpha of the foreground while "
31 "assuming the background is being held out by the foreground")},
33 "CONJOINT_OVER",
34 ICON_NONE,
35 N_("Conjoint Over"),
36 N_("The foreground goes over the background according to the alpha of the foreground but the "
37 "foreground completely covers the background if it is more opaque")},
38 {0, nullptr, 0, nullptr, nullptr},
39};
40
42{
43 b.is_function_node();
44 b.add_input<decl::Color>("Background").default_value({1.0f, 1.0f, 1.0f, 1.0f});
45 b.add_input<decl::Color>("Foreground").default_value({1.0f, 1.0f, 1.0f, 1.0f});
46 b.add_input<decl::Float>("Factor", "Fac")
47 .default_value(1.0f)
48 .min(0.0f)
49 .max(1.0f)
51 b.add_input<decl::Menu>("Type")
53 .static_items(type_items)
55 b.add_input<decl::Bool>("Straight Alpha")
56 .default_value(false)
58 "Defines whether the foreground is in straight alpha form, which is necessary to know "
59 "for proper alpha compositing. Images in the compositor are in premultiplied alpha form "
60 "by default, so this should be false in most cases. But if, and only if, the foreground "
61 "was converted to straight alpha form for some reason, this should be set to true");
62 b.add_output<decl::Color>("Image");
63}
64
65static int node_gpu_material(GPUMaterial *material,
66 bNode *node,
67 bNodeExecData * /*execdata*/,
70{
71 return GPU_stack_link(material, node, "node_composite_alpha_over", inputs, outputs);
72}
73
74/* If straight_alpha is true, then the foreground is in straight alpha form and would need to be
75 * premultiplied. */
76static float4 preprocess_foreground(const float4 &foreground, const bool straight_alpha)
77{
78 const float alpha = math::clamp(foreground.w, 0.0f, 1.0f);
79 const float4 premultiplied_foreground = float4(foreground.xyz() * alpha, alpha);
80 return straight_alpha ? premultiplied_foreground : foreground;
81}
82
83/* Computes the Porter and Duff Over compositing operation. */
84static float4 alpha_over(const float4 &background,
85 const float4 &foreground,
86 const float factor,
87 const bool straight_alpha)
88{
89 const float4 foreground_color = preprocess_foreground(foreground, straight_alpha);
90
91 const float foreground_alpha = math::clamp(foreground.w, 0.0f, 1.0f);
92 const float4 mix_result = foreground_color + background * (1.0f - foreground_alpha);
93
94 return math::interpolate(background, mix_result, factor);
95}
96
97/* Computes the Porter and Duff Over compositing operation while assuming the background is being
98 * held out by the foreground. See for reference:
99 *
100 * https://benmcewan.com/blog/disjoint-over-and-conjoint-over-explained */
101static float4 alpha_over_disjoint(const float4 &background,
102 const float4 &foreground,
103 const float factor,
104 const bool straight_alpha)
105{
106 const float4 foreground_color = preprocess_foreground(foreground, straight_alpha);
107
108 const float foreground_alpha = math::clamp(foreground.w, 0.0f, 1.0f);
109 const float background_alpha = math::clamp(background.w, 0.0f, 1.0f);
110
111 if (foreground_alpha + background_alpha < 1.0f) {
112 const float4 mix_result = foreground_color + background;
113 return math::interpolate(background, mix_result, factor);
114 }
115
116 const float4 straight_background = math::safe_divide(background, background_alpha);
117 const float4 mix_result = foreground_color + straight_background * (1.0f - foreground_alpha);
118
119 return math::interpolate(background, mix_result, factor);
120}
121
122/* Computes the Porter and Duff Over compositing operation but the foreground completely covers the
123 * background if it is more opaque but not necessary completely opaque. See for reference:
124 *
125 * https://benmcewan.com/blog/disjoint-over-and-conjoint-over-explained */
126static float4 alpha_over_conjoint(const float4 &background,
127 const float4 &foreground,
128 const float factor,
129 const bool straight_alpha)
130{
131 const float4 foreground_color = preprocess_foreground(foreground, straight_alpha);
132
133 const float foreground_alpha = math::clamp(foreground.w, 0.0f, 1.0f);
134 const float background_alpha = math::clamp(background.w, 0.0f, 1.0f);
135
136 if (foreground_alpha > background_alpha) {
137 const float4 mix_result = foreground_color;
138 return math::interpolate(background, mix_result, factor);
139 }
140
141 const float alpha_ratio = math::safe_divide(foreground_alpha, background_alpha);
142 const float4 mix_result = foreground_color + background * (1.0f - alpha_ratio);
143
144 return math::interpolate(background, mix_result, factor);
145}
146
148{
149 static auto function = mf::build::SI5_SO<float4, float4, float, MenuValue, bool, float4>(
150 "Alpha Over",
151 [=](const float4 &background,
152 const float4 &foreground,
153 const float factor,
154 const MenuValue type,
155 const bool straight_alpha) -> float4 {
156 switch (CMPNodeAlphaOverOperationType(type.value)) {
158 return alpha_over(background, foreground, factor, straight_alpha);
160 return alpha_over_disjoint(background, foreground, factor, straight_alpha);
162 return alpha_over_conjoint(background, foreground, factor, straight_alpha);
163 }
164 return background;
165 },
166 mf::build::exec_presets::SomeSpanOrSingle<0, 1>());
167
168 builder.set_matching_fn(function);
169}
170
172{
173 static blender::bke::bNodeType ntype;
174
175 cmp_node_type_base(&ntype, "CompositorNodeAlphaOver", CMP_NODE_ALPHAOVER);
176 ntype.ui_name = "Alpha Over";
177 ntype.ui_description = "Overlay a foreground image onto a background image";
178 ntype.enum_name_legacy = "ALPHAOVER";
180 ntype.declare = node_declare;
183
185}
187
188} // namespace blender::nodes::node_composite_alpha_over_cc
#define NODE_CLASS_OP_COLOR
Definition BKE_node.hh:449
#define CMP_NODE_ALPHAOVER
CMPNodeAlphaOverOperationType
@ CMP_NODE_ALPHA_OVER_OPERATION_TYPE_OVER
@ CMP_NODE_ALPHA_OVER_OPERATION_TYPE_CONJOINT_OVER
@ CMP_NODE_ALPHA_OVER_OPERATION_TYPE_DISJOINT_OVER
bool GPU_stack_link(GPUMaterial *mat, const bNode *node, const char *name, GPUNodeStack *in, GPUNodeStack *out,...)
#define NOD_REGISTER_NODE(REGISTER_FUNC)
@ PROP_FACTOR
Definition RNA_types.hh:251
void set_matching_fn(const mf::MultiFunction *fn)
void node_register_type(bNodeType &ntype)
Definition node.cc:2416
T clamp(const T &a, const T &min, const T &max)
T safe_divide(const T &a, const T &b)
T interpolate(const T &a, const T &b, const FactorT &t)
static float4 preprocess_foreground(const float4 &foreground, const bool straight_alpha)
static void node_build_multi_function(blender::nodes::NodeMultiFunctionBuilder &builder)
static float4 alpha_over_conjoint(const float4 &background, const float4 &foreground, const float factor, const bool straight_alpha)
static int node_gpu_material(GPUMaterial *material, bNode *node, bNodeExecData *, GPUNodeStack *inputs, GPUNodeStack *outputs)
static float4 alpha_over(const float4 &background, const float4 &foreground, const float factor, const bool straight_alpha)
static void node_declare(NodeDeclarationBuilder &b)
static float4 alpha_over_disjoint(const float4 &background, const float4 &foreground, const float factor, const bool straight_alpha)
VecBase< float, 4 > float4
void cmp_node_type_base(blender::bke::bNodeType *ntype, std::string idname, const std::optional< int16_t > legacy_type)
static blender::bke::bNodeSocketTemplate outputs[]
static blender::bke::bNodeSocketTemplate inputs[]
VecBase< T, 3 > xyz() const
Defines a node type.
Definition BKE_node.hh:238
std::string ui_description
Definition BKE_node.hh:244
NodeGPUExecFunction gpu_fn
Definition BKE_node.hh:342
NodeMultiFunctionBuildFunction build_multi_function
Definition BKE_node.hh:351
const char * enum_name_legacy
Definition BKE_node.hh:247
NodeDeclareFunction declare
Definition BKE_node.hh:362
#define N_(msgid)