Blender V5.0
node_composite_channel_matte.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_base.hh"
6#include "BLI_math_color.h"
8
10
11#include "NOD_multi_function.hh"
13
14#include "BKE_node.hh"
15
16#include "GPU_material.hh"
17
19
21
23 {CMP_NODE_CHANNEL_MATTE_CS_RGB, "RGB", 0, N_("RGB"), N_("RGB (Red, Green, Blue) color space")},
25 "HSV",
26 0,
27 N_("HSV"),
28 N_("HSV (Hue, Saturation, Value) color space")},
30 "YUV",
31 0,
32 N_("YUV"),
33 N_("YUV (Y - luma, U V - chroma) color space")},
35 "YCC",
36 0,
37 N_("YCbCr"),
38 N_("YCbCr (Y - luma, Cb - blue-difference chroma, Cr - red-difference chroma) color space")},
39 {0, nullptr, 0, nullptr, nullptr},
40};
41
42enum class RGBChannel : uint8_t {
43 R = 0,
44 G = 1,
45 B = 2,
46};
47
49 {int(RGBChannel::R), "R", 0, "R", ""},
50 {int(RGBChannel::G), "G", 0, "G", ""},
51 {int(RGBChannel::B), "B", 0, "B", ""},
52 {0, nullptr, 0, nullptr, nullptr},
53};
54
55enum class HSVChannel : uint8_t {
56 H = 0,
57 S = 1,
58 V = 2,
59};
60
62 {int(HSVChannel::H), "H", 0, "H", ""},
63 {int(HSVChannel::S), "S", 0, "S", ""},
64 {int(HSVChannel::V), "V", 0, "V", ""},
65 {0, nullptr, 0, nullptr, nullptr},
66};
67
68enum class YUVChannel : uint8_t {
69 Y = 0,
70 U = 1,
71 V = 2,
72};
73
75 {int(YUVChannel::Y), "Y", 0, "Y", ""},
76 {int(YUVChannel::U), "U", 0, "U", ""},
77 {int(YUVChannel::V), "V", 0, "V", ""},
78 {0, nullptr, 0, nullptr, nullptr},
79};
80
81enum class YCbCrChannel : uint8_t {
82 Y = 0,
83 Cb = 1,
84 Cr = 2,
85};
86
88 {int(YCbCrChannel::Y), "Y", 0, "Y", ""},
89 {int(YCbCrChannel::Cb), "CB", 0, "Cb", ""},
90 {int(YCbCrChannel::Cr), "CR", 0, "Cr", ""},
91 {0, nullptr, 0, nullptr, nullptr},
92};
93
96 "SINGLE",
97 0,
98 "Single",
99 "Limit by single channel"},
101 "MAX",
102 0,
103 "Max",
104 "Limit by maximum of other channels"},
105 {0, nullptr, 0, nullptr, nullptr},
106};
107
109{
110 b.use_custom_socket_order();
111 b.allow_any_socket_order();
112 b.is_function_node();
113 b.add_input<decl::Color>("Image").default_value({1.0f, 1.0f, 1.0f, 1.0f}).hide_value();
114 b.add_output<decl::Color>("Image").align_with_previous();
115 b.add_output<decl::Float>("Matte");
116
117 b.add_input<decl::Float>("Minimum")
118 .default_value(0.0f)
120 .min(0.0f)
121 .max(1.0f)
122 .description("Channel values lower than this minimum are keyed");
123 b.add_input<decl::Float>("Maximum")
124 .default_value(1.0f)
126 .min(0.0f)
127 .max(1.0f)
128 .description("Channel values higher than this maximum are not keyed");
129
130 b.add_input<decl::Menu>("Color Space")
131 .default_value(CMP_NODE_CHANNEL_MATTE_CS_RGB)
132 .static_items(color_space_items)
133 .expanded()
135 b.add_input<decl::Menu>("RGB Key Channel")
136 .default_value(RGBChannel::G)
137 .static_items(rgb_channel_items)
138 .expanded()
140 .usage_by_menu("Color Space", CMP_NODE_CHANNEL_MATTE_CS_RGB)
141 .optional_label();
142 b.add_input<decl::Menu>("HSV Key Channel")
143 .default_value(HSVChannel::H)
144 .static_items(hsv_channel_items)
145 .expanded()
147 .usage_by_menu("Color Space", CMP_NODE_CHANNEL_MATTE_CS_HSV)
148 .optional_label();
149 b.add_input<decl::Menu>("YUV Key Channel")
150 .default_value(YUVChannel::V)
151 .static_items(yuv_channel_items)
152 .expanded()
153 .usage_by_menu("Color Space", CMP_NODE_CHANNEL_MATTE_CS_YUV)
155 b.add_input<decl::Menu>("YCbCr Key Channel")
156 .default_value(YCbCrChannel::Cr)
157 .static_items(ycbcr_channel_items)
158 .expanded()
159 .usage_by_menu("Color Space", CMP_NODE_CHANNEL_MATTE_CS_YCC)
161
162 b.add_input<decl::Menu>("Limit Method")
164 .static_items(limit_method_items)
165 .expanded()
167 b.add_input<decl::Menu>("RGB Limit Channel")
168 .default_value(RGBChannel::R)
169 .static_items(rgb_channel_items)
170 .expanded()
172 .make_available([](bNode &node) {
173 bNodeSocket &limit_method_socket = *blender::bke::node_find_socket(
174 node, SOCK_IN, "Limit Method");
175 limit_method_socket.default_value_typed<bNodeSocketValueMenu>()->value =
177
178 bNodeSocket &color_space_socket = *blender::bke::node_find_socket(
179 node, SOCK_IN, "Color Space");
180 color_space_socket.default_value_typed<bNodeSocketValueMenu>()->value =
182 })
183 .usage_inference(
184 [](const socket_usage_inference::InputSocketUsageParams &params) -> std::optional<bool> {
185 return params.menu_input_may_be("Limit Method",
187 params.menu_input_may_be("Color Space", CMP_NODE_CHANNEL_MATTE_CS_RGB);
188 });
189 b.add_input<decl::Menu>("HSV Limit Channel")
190 .default_value(HSVChannel::S)
191 .static_items(hsv_channel_items)
192 .expanded()
194 .make_available([](bNode &node) {
195 bNodeSocket &limit_method_socket = *blender::bke::node_find_socket(
196 node, SOCK_IN, "Limit Method");
197 limit_method_socket.default_value_typed<bNodeSocketValueMenu>()->value =
199
200 bNodeSocket &color_space_socket = *blender::bke::node_find_socket(
201 node, SOCK_IN, "Color Space");
202 color_space_socket.default_value_typed<bNodeSocketValueMenu>()->value =
204 })
205 .usage_inference(
206 [](const socket_usage_inference::InputSocketUsageParams &params) -> std::optional<bool> {
207 return params.menu_input_may_be("Limit Method",
209 params.menu_input_may_be("Color Space", CMP_NODE_CHANNEL_MATTE_CS_HSV);
210 });
211 b.add_input<decl::Menu>("YUV Limit Channel")
212 .default_value(YUVChannel::U)
213 .static_items(yuv_channel_items)
214 .expanded()
216 .make_available([](bNode &node) {
217 bNodeSocket &limit_method_socket = *blender::bke::node_find_socket(
218 node, SOCK_IN, "Limit Method");
219 limit_method_socket.default_value_typed<bNodeSocketValueMenu>()->value =
221
222 bNodeSocket &color_space_socket = *blender::bke::node_find_socket(
223 node, SOCK_IN, "Color Space");
224 color_space_socket.default_value_typed<bNodeSocketValueMenu>()->value =
226 })
227 .usage_inference(
228 [](const socket_usage_inference::InputSocketUsageParams &params) -> std::optional<bool> {
229 return params.menu_input_may_be("Limit Method",
231 params.menu_input_may_be("Color Space", CMP_NODE_CHANNEL_MATTE_CS_YUV);
232 });
233 b.add_input<decl::Menu>("YCbCr Limit Channel")
234 .default_value(YCbCrChannel::Cb)
235 .static_items(ycbcr_channel_items)
236 .expanded()
238 .make_available([](bNode &node) {
239 bNodeSocket &limit_method_socket = *blender::bke::node_find_socket(
240 node, SOCK_IN, "Limit Method");
241 limit_method_socket.default_value_typed<bNodeSocketValueMenu>()->value =
243
244 bNodeSocket &color_space_socket = *blender::bke::node_find_socket(
245 node, SOCK_IN, "Color Space");
246 color_space_socket.default_value_typed<bNodeSocketValueMenu>()->value =
248 })
249 .usage_inference(
250 [](const socket_usage_inference::InputSocketUsageParams &params) -> std::optional<bool> {
251 return params.menu_input_may_be("Limit Method",
253 params.menu_input_may_be("Color Space", CMP_NODE_CHANNEL_MATTE_CS_YCC);
254 });
255}
256
257static void node_composit_init_channel_matte(bNodeTree * /*ntree*/, bNode *node)
258{
259 /* Unused, but allocated for forward compatibility. */
260 node->storage = MEM_callocN<NodeChroma>(__func__);
261}
262
263using namespace blender::compositor;
264
265static int node_gpu_material(GPUMaterial *material,
266 bNode *node,
267 bNodeExecData * /*execdata*/,
270{
271 return GPU_stack_link(material, node, "node_composite_channel_matte", inputs, outputs);
272}
273
275{
276 switch (color_space) {
278 return color.xyz();
279 }
281 float4 hsv;
282 rgb_to_hsv_v(color, hsv);
283 return hsv.xyz();
284 }
286 float4 yuv;
287 rgb_to_yuv(color.x, color.y, color.z, &yuv.x, &yuv.y, &yuv.z, BLI_YUV_ITU_BT709);
288 return yuv.xyz();
289 }
291 float4 ycc;
292 rgb_to_ycc(color.x, color.y, color.z, &ycc.x, &ycc.y, &ycc.z, BLI_YCC_ITU_BT709);
293 ycc /= 255.0f;
294 return ycc.xyz();
295 }
296 }
297
298 return color.xyz();
299}
300
302 const int rgb_channel,
303 const int hsv_channel,
304 const int yuv_channel,
305 const int ycc_channel)
306{
307 switch (color_space) {
309 return rgb_channel;
311 return hsv_channel;
313 return yuv_channel;
315 return ycc_channel;
316 }
317
318 return 0;
319}
320
321/* Compute the indices of the channels used to compute the limit value. We always assume the limit
322 * algorithm is Max, if it is a single limit channel, store it in both limit channels, because
323 * the maximum of two identical values is the same value. */
325 const int matte_channel,
326 const int limit_channel)
327{
328 /* If the algorithm is Max, store the indices of the other two channels other than the matte
329 * channel. */
330 if (limit_method == CMP_NODE_CHANNEL_MATTE_LIMIT_ALGORITHM_MAX) {
331 return int2((matte_channel + 1) % 3, (matte_channel + 2) % 3);
332 }
333
334 /* If the algorithm is Single, store the index of the limit channel in both channels. */
335 return int2(limit_channel);
336}
337
338static void channel_key(const float4 color,
339 const float minimum,
340 const float maximum,
341 const CMPNodeChannelMatteColorSpace color_space,
342 const int rgb_key_channel,
343 const int hsv_key_channel,
344 const int yuv_key_channel,
345 const int ycc_key_channel,
346 const CMPNodeChannelMatteLimitAlgorithm limit_method,
347 const int rgb_limit_channel,
348 const int hsv_limit_channel,
349 const int yuv_limit_channel,
350 const int ycc_limit_channel,
351 float4 &output_color,
352 float &matte)
353{
354 const float3 channels = compute_channels(color, color_space);
355 const int matte_channel = get_channel_index(
356 color_space, rgb_key_channel, hsv_key_channel, yuv_key_channel, ycc_key_channel);
357 const int limit_channel = get_channel_index(
358 color_space, rgb_limit_channel, hsv_limit_channel, yuv_limit_channel, ycc_limit_channel);
359 const int2 limit_channels = compute_limit_channels(limit_method, matte_channel, limit_channel);
360
361 float matte_value = channels[matte_channel];
362 float limit_value = math::max(channels[limit_channels.x], channels[limit_channels.y]);
363
364 float alpha = 1.0f - (matte_value - limit_value);
365 if (alpha > maximum) {
366 alpha = color.w;
367 }
368 else if (alpha < minimum) {
369 alpha = 0.0f;
370 }
371 else {
372 alpha = (alpha - minimum) / (maximum - minimum);
373 }
374
375 matte = math::min(alpha, color.w);
376 output_color = color * matte;
377}
378
380{
381 static auto function =
382 mf::build::detail::build_multi_function_with_n_inputs_two_outputs<float4, float>(
383 "Channel Key",
384 [=](const float4 &color,
385 const float &minimum,
386 const float &maximum,
387 const MenuValue &color_space,
388 const MenuValue &rgb_key_channel,
389 const MenuValue &hsv_key_channel,
390 const MenuValue &yuv_key_channel,
391 const MenuValue &ycc_key_channel,
392 const MenuValue &limit_method,
393 const MenuValue &rgb_limit_channel,
394 const MenuValue &hsv_limit_channel,
395 const MenuValue &yuv_limit_channel,
396 const MenuValue &ycc_limit_channel,
397 float4 &output_color,
398 float &matte) -> void {
400 minimum,
401 maximum,
403 rgb_key_channel.value,
404 hsv_key_channel.value,
405 yuv_key_channel.value,
406 ycc_key_channel.value,
408 rgb_limit_channel.value,
409 hsv_limit_channel.value,
410 yuv_limit_channel.value,
411 ycc_limit_channel.value,
412 output_color,
413 matte);
414 },
415 mf::build::exec_presets::SomeSpanOrSingle<0>(),
417 float,
418 float,
419 MenuValue,
420 MenuValue,
421 MenuValue,
422 MenuValue,
423 MenuValue,
424 MenuValue,
425 MenuValue,
426 MenuValue,
427 MenuValue,
428 MenuValue>());
429
430 builder.set_matching_fn(function);
431}
432
433} // namespace blender::nodes::node_composite_channel_matte_cc
434
436{
438
439 static blender::bke::bNodeType ntype;
440
441 cmp_node_type_base(&ntype, "CompositorNodeChannelMatte", CMP_NODE_CHANNEL_MATTE);
442 ntype.ui_name = "Channel Key";
443 ntype.ui_description = "Create matte based on differences in color channels";
444 ntype.enum_name_legacy = "CHANNEL_MATTE";
445 ntype.nclass = NODE_CLASS_MATTE;
446 ntype.declare = file_ns::cmp_node_channel_matte_declare;
447 ntype.flag |= NODE_PREVIEW;
448 ntype.initfunc = file_ns::node_composit_init_channel_matte;
451 ntype.gpu_fn = file_ns::node_gpu_material;
452 ntype.build_multi_function = file_ns::node_build_multi_function;
453
455}
#define NODE_CLASS_MATTE
Definition BKE_node.hh:454
#define CMP_NODE_CHANNEL_MATTE
void rgb_to_hsv_v(const float rgb[3], float r_hsv[3])
#define BLI_YUV_ITU_BT709
void rgb_to_ycc(float r, float g, float b, float *r_y, float *r_cb, float *r_cr, int colorspace)
void rgb_to_yuv(float r, float g, float b, float *r_y, float *r_u, float *r_v, int colorspace)
Definition math_color.cc:67
#define BLI_YCC_ITU_BT709
#define BLT_I18NCONTEXT_COLOR
CMPNodeChannelMatteLimitAlgorithm
@ CMP_NODE_CHANNEL_MATTE_LIMIT_ALGORITHM_MAX
@ CMP_NODE_CHANNEL_MATTE_LIMIT_ALGORITHM_SINGLE
@ NODE_PREVIEW
@ SOCK_IN
CMPNodeChannelMatteColorSpace
@ CMP_NODE_CHANNEL_MATTE_CS_YUV
@ CMP_NODE_CHANNEL_MATTE_CS_RGB
@ CMP_NODE_CHANNEL_MATTE_CS_HSV
@ CMP_NODE_CHANNEL_MATTE_CS_YCC
bool GPU_stack_link(GPUMaterial *mat, const bNode *node, const char *name, GPUNodeStack *in, GPUNodeStack *out,...)
#define Y
#define NOD_REGISTER_NODE(REGISTER_FUNC)
@ PROP_FACTOR
Definition RNA_types.hh:251
#define U
void set_matching_fn(const mf::MultiFunction *fn)
std::optional< std::string > translation_context
nullptr float
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
void * MEM_callocN(size_t len, const char *str)
Definition mallocn.cc:118
#define B
#define R
#define G(x, y, z)
#define H(x, y, z)
bNodeSocket * node_find_socket(bNode &node, eNodeSocketInOut in_out, StringRef identifier)
Definition node.cc:2532
void node_register_type(bNodeType &ntype)
Definition node.cc:2416
void node_type_storage(bNodeType &ntype, std::optional< StringRefNull > storagename, void(*freefunc)(bNode *node), void(*copyfunc)(bNodeTree *dest_ntree, bNode *dest_node, const bNode *src_node))
Definition node.cc:5414
T min(const T &a, const T &b)
T max(const T &a, const T &b)
static int node_gpu_material(GPUMaterial *material, bNode *node, bNodeExecData *, GPUNodeStack *inputs, GPUNodeStack *outputs)
static float3 compute_channels(const float4 color, const CMPNodeChannelMatteColorSpace color_space)
static void cmp_node_channel_matte_declare(NodeDeclarationBuilder &b)
static void channel_key(const float4 color, const float minimum, const float maximum, const CMPNodeChannelMatteColorSpace color_space, const int rgb_key_channel, const int hsv_key_channel, const int yuv_key_channel, const int ycc_key_channel, const CMPNodeChannelMatteLimitAlgorithm limit_method, const int rgb_limit_channel, const int hsv_limit_channel, const int yuv_limit_channel, const int ycc_limit_channel, float4 &output_color, float &matte)
static int2 compute_limit_channels(const CMPNodeChannelMatteLimitAlgorithm limit_method, const int matte_channel, const int limit_channel)
static int get_channel_index(const CMPNodeChannelMatteColorSpace color_space, const int rgb_channel, const int hsv_channel, const int yuv_channel, const int ycc_channel)
static void node_composit_init_channel_matte(bNodeTree *, bNode *node)
static void node_build_multi_function(blender::nodes::NodeMultiFunctionBuilder &builder)
VecBase< float, 4 > float4
VecBase< int32_t, 2 > int2
VecBase< float, 3 > float3
static void register_node_type_cmp_channel_matte()
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[]
void node_free_standard_storage(bNode *node)
Definition node_util.cc:42
void node_copy_standard_storage(bNodeTree *, bNode *dest_node, const bNode *src_node)
Definition node_util.cc:54
void * storage
VecBase< T, 3 > xyz() const
Defines a node type.
Definition BKE_node.hh:238
std::string ui_description
Definition BKE_node.hh:244
void(* initfunc)(bNodeTree *ntree, bNode *node)
Definition BKE_node.hh:289
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)
CCL_NAMESPACE_BEGIN struct Window V