Blender V4.3
node_composite_keying.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2011 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
9#include "BLI_math_base.h"
11
12#include "DNA_movieclip_types.h"
13#include "DNA_scene_types.h"
14
15#include "UI_interface.hh"
16#include "UI_resources.hh"
17
18#include "GPU_shader.hh"
19#include "GPU_texture.hh"
20
24#include "COM_node_operation.hh"
25#include "COM_utilities.hh"
26
28
29/* **************** Keying ******************** */
30
32
34
36{
37 b.add_input<decl::Color>("Image")
38 .default_value({0.8f, 0.8f, 0.8f, 1.0f})
39 .compositor_domain_priority(0);
40 b.add_input<decl::Color>("Key Color")
41 .default_value({1.0f, 1.0f, 1.0f, 1.0f})
42 .compositor_domain_priority(1);
43 b.add_input<decl::Float>("Garbage Matte").hide_value().compositor_domain_priority(2);
44 b.add_input<decl::Float>("Core Matte").hide_value().compositor_domain_priority(3);
45 b.add_output<decl::Color>("Image");
46 b.add_output<decl::Float>("Matte");
47 b.add_output<decl::Float>("Edges");
48}
49
50static void node_composit_init_keying(bNodeTree * /*ntree*/, bNode *node)
51{
52 NodeKeyingData *data = MEM_cnew<NodeKeyingData>(__func__);
53
54 data->screen_balance = 0.5f;
55 data->despill_balance = 0.5f;
56 data->despill_factor = 1.0f;
57 data->edge_kernel_radius = 3;
58 data->edge_kernel_tolerance = 0.1f;
59 data->clip_black = 0.0f;
60 data->clip_white = 1.0f;
61 node->storage = data;
62}
63
65{
66 // bNode *node = (bNode*)ptr->data; /* UNUSED */
67
68 uiItemR(layout, ptr, "blur_pre", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE);
69 uiItemR(layout, ptr, "screen_balance", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE);
70 uiItemR(layout, ptr, "despill_factor", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE);
71 uiItemR(layout, ptr, "despill_balance", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE);
72 uiItemR(layout, ptr, "edge_kernel_radius", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE);
73 uiItemR(layout, ptr, "edge_kernel_tolerance", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE);
74 uiItemR(layout, ptr, "clip_black", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE);
75 uiItemR(layout, ptr, "clip_white", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE);
76 uiItemR(layout, ptr, "dilate_distance", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE);
77 uiItemR(layout, ptr, "feather_falloff", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE);
78 uiItemR(layout, ptr, "feather_distance", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE);
79 uiItemR(layout, ptr, "blur_post", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE);
80}
81
82using namespace blender::realtime_compositor;
83
85 public:
87
88 void execute() override
89 {
90 Result blurred_input = compute_blurred_input();
91
92 Result matte = compute_matte(blurred_input);
93 blurred_input.release();
94
95 /* This also computes the edges output if needed. */
96 Result tweaked_matte = compute_tweaked_matte(matte);
97 matte.release();
98
99 Result &output_image = get_result("Image");
100 Result &output_matte = get_result("Matte");
101 if (output_image.should_compute() || output_matte.should_compute()) {
102 Result blurred_matte = compute_blurred_matte(tweaked_matte);
103 tweaked_matte.release();
104
105 Result morphed_matte = compute_morphed_matte(blurred_matte);
106 blurred_matte.release();
107
108 Result feathered_matte = compute_feathered_matte(morphed_matte);
109 morphed_matte.release();
110
111 if (output_image.should_compute()) {
112 compute_image(feathered_matte);
113 }
114
115 if (output_matte.should_compute()) {
116 output_matte.steal_data(feathered_matte);
117 }
118 else {
119 feathered_matte.release();
120 }
121 }
122 }
123
125 {
126 /* No blur needed, return the original matte. We also increment the reference count of the
127 * input because the caller will release it after the call, and we want to extend its life
128 * since it is now returned as the output. */
129 const float blur_size = node_storage(bnode()).blur_pre;
130 if (blur_size == 0.0f) {
131 Result output = get_input("Image");
132 output.increment_reference_count();
133 return output;
134 }
135
136 Result chroma = extract_input_chroma();
137
138 Result blurred_chroma = context().create_result(ResultType::Color);
140 context(), chroma, blurred_chroma, float2(blur_size) / 2, R_FILTER_BOX);
141 chroma.release();
142
143 Result blurred_input = replace_input_chroma(blurred_chroma);
144 blurred_chroma.release();
145
146 return blurred_input;
147 }
148
150 {
151 GPUShader *shader = context().get_shader("compositor_keying_extract_chroma");
152 GPU_shader_bind(shader);
153
154 Result &input = get_input("Image");
155 input.bind_as_texture(shader, "input_tx");
156
157 Result output = context().create_result(ResultType::Color);
158 output.allocate_texture(input.domain());
159 output.bind_as_image(shader, "output_img");
160
161 compute_dispatch_threads_at_least(shader, input.domain().size);
162
164 input.unbind_as_texture();
165 output.unbind_as_image();
166
167 return output;
168 }
169
171 {
172 GPUShader *shader = context().get_shader("compositor_keying_replace_chroma");
173 GPU_shader_bind(shader);
174
175 Result &input = get_input("Image");
176 input.bind_as_texture(shader, "input_tx");
177
178 new_chroma.bind_as_texture(shader, "new_chroma_tx");
179
180 Result output = context().create_result(ResultType::Color);
181 output.allocate_texture(input.domain());
182 output.bind_as_image(shader, "output_img");
183
184 compute_dispatch_threads_at_least(shader, input.domain().size);
185
187 input.unbind_as_texture();
188 new_chroma.unbind_as_texture();
189 output.unbind_as_image();
190
191 return output;
192 }
193
195 {
196 GPUShader *shader = context().get_shader("compositor_keying_compute_matte");
197 GPU_shader_bind(shader);
198
199 GPU_shader_uniform_1f(shader, "key_balance", node_storage(bnode()).screen_balance);
200
201 input.bind_as_texture(shader, "input_tx");
202
203 Result &key_color = get_input("Key Color");
204 key_color.bind_as_texture(shader, "key_tx");
205
206 Result output = context().create_result(ResultType::Float);
207 output.allocate_texture(input.domain());
208 output.bind_as_image(shader, "output_img");
209
210 compute_dispatch_threads_at_least(shader, input.domain().size);
211
213 input.unbind_as_texture();
214 key_color.unbind_as_texture();
215 output.unbind_as_image();
216
217 return output;
218 }
219
221 {
222 Result &output_edges = get_result("Edges");
223
224 const float black_level = node_storage(bnode()).clip_black;
225 const float white_level = node_storage(bnode()).clip_white;
226
227 const bool core_matte_exists = node().input_by_identifier("Core Matte")->is_logically_linked();
228 const bool garbage_matte_exists =
229 node().input_by_identifier("Garbage Matte")->is_logically_linked();
230
231 /* The edges output is not needed and the matte is not tweaked, so return the original matte.
232 * We also increment the reference count of the input because the caller will release it after
233 * the call, and we want to extend its life since it is now returned as the output. */
234 if (!output_edges.should_compute() && (black_level == 0.0f && white_level == 1.0f) &&
235 !core_matte_exists && !garbage_matte_exists)
236 {
237 Result output_matte = input_matte;
238 input_matte.increment_reference_count();
239 return output_matte;
240 }
241
242 GPUShader *shader = context().get_shader("compositor_keying_tweak_matte");
243 GPU_shader_bind(shader);
244
245 GPU_shader_uniform_1b(shader, "compute_edges", output_edges.should_compute());
246 GPU_shader_uniform_1b(shader, "apply_core_matte", core_matte_exists);
247 GPU_shader_uniform_1b(shader, "apply_garbage_matte", garbage_matte_exists);
248 GPU_shader_uniform_1i(shader, "edge_search_radius", node_storage(bnode()).edge_kernel_radius);
249 GPU_shader_uniform_1f(shader, "edge_tolerance", node_storage(bnode()).edge_kernel_tolerance);
250 GPU_shader_uniform_1f(shader, "black_level", black_level);
251 GPU_shader_uniform_1f(shader, "white_level", white_level);
252
253 input_matte.bind_as_texture(shader, "input_matte_tx");
254
255 Result &garbage_matte = get_input("Garbage Matte");
256 garbage_matte.bind_as_texture(shader, "garbage_matte_tx");
257
258 Result &core_matte = get_input("Core Matte");
259 core_matte.bind_as_texture(shader, "core_matte_tx");
260
261 Result output_matte = context().create_result(ResultType::Float);
262 output_matte.allocate_texture(input_matte.domain());
263 output_matte.bind_as_image(shader, "output_matte_img");
264
265 output_edges.allocate_texture(input_matte.domain());
266 output_edges.bind_as_image(shader, "output_edges_img");
267
268 compute_dispatch_threads_at_least(shader, input_matte.domain().size);
269
271 input_matte.unbind_as_texture();
272 garbage_matte.unbind_as_texture();
273 core_matte.unbind_as_texture();
274 output_matte.unbind_as_image();
275 output_edges.unbind_as_image();
276
277 return output_matte;
278 }
279
281 {
282 const float blur_size = node_storage(bnode()).blur_post;
283 /* No blur needed, return the original matte. We also increment the reference count of the
284 * input because the caller will release it after the call, and we want to extend its life
285 * since it is now returned as the output. */
286 if (blur_size == 0.0f) {
287 Result output_matte = input_matte;
288 input_matte.increment_reference_count();
289 return output_matte;
290 }
291
292 Result blurred_matte = context().create_result(ResultType::Float);
294 context(), input_matte, blurred_matte, float2(blur_size) / 2, R_FILTER_BOX);
295
296 return blurred_matte;
297 }
298
300 {
301 const int distance = node_storage(bnode()).dilate_distance;
302 /* No morphology needed, return the original matte. We also increment the reference count of
303 * the input because the caller will release it after the call, and we want to extend its life
304 * since it is now returned as the output. */
305 if (distance == 0) {
306 Result output_matte = input_matte;
307 input_matte.increment_reference_count();
308 return output_matte;
309 }
310
311 Result morphed_matte = context().create_result(ResultType::Float);
312 morphological_distance(context(), input_matte, morphed_matte, distance);
313
314 return morphed_matte;
315 }
316
318 {
319 const int distance = node_storage(bnode()).feather_distance;
320 /* No feathering needed, return the original matte. We also increment the reference count of
321 * the input because the caller will release it after the call, and we want to extend its life
322 * since it is now returned as the output. */
323 if (distance == 0) {
324 Result output_matte = input_matte;
325 input_matte.increment_reference_count();
326 return output_matte;
327 }
328
329 Result feathered_matte = context().create_result(ResultType::Float);
331 context(), input_matte, feathered_matte, distance, node_storage(bnode()).feather_falloff);
332
333 return feathered_matte;
334 }
335
337 {
338 GPUShader *shader = context().get_shader("compositor_keying_compute_image");
339 GPU_shader_bind(shader);
340
341 GPU_shader_uniform_1f(shader, "despill_factor", node_storage(bnode()).despill_factor);
342 GPU_shader_uniform_1f(shader, "despill_balance", node_storage(bnode()).despill_balance);
343
344 Result &input = get_input("Image");
345 input.bind_as_texture(shader, "input_tx");
346
347 Result &key = get_input("Key Color");
348 key.bind_as_texture(shader, "key_tx");
349
350 matte.bind_as_texture(shader, "matte_tx");
351
352 Result &output = get_result("Image");
353 output.allocate_texture(matte.domain());
354 output.bind_as_image(shader, "output_img");
355
356 compute_dispatch_threads_at_least(shader, input.domain().size);
357
359 input.unbind_as_texture();
360 key.unbind_as_texture();
361 matte.unbind_as_texture();
362 output.unbind_as_image();
363 }
364};
365
367{
368 return new KeyingOperation(context, node);
369}
370
371} // namespace blender::nodes::node_composite_keying_cc
372
374{
376
377 static blender::bke::bNodeType ntype;
378
379 cmp_node_type_base(&ntype, CMP_NODE_KEYING, "Keying", NODE_CLASS_MATTE);
380 ntype.declare = file_ns::cmp_node_keying_declare;
381 ntype.draw_buttons = file_ns::node_composit_buts_keying;
382 ntype.initfunc = file_ns::node_composit_init_keying;
384 &ntype, "NodeKeyingData", node_free_standard_storage, node_copy_standard_storage);
385 ntype.get_compositor_operation = file_ns::get_compositor_operation;
386
388}
#define NODE_CLASS_MATTE
Definition BKE_node.hh:411
#define NODE_STORAGE_FUNCS(StorageT)
Definition BKE_node.hh:1799
@ R_FILTER_BOX
void GPU_shader_uniform_1i(GPUShader *sh, const char *name, int value)
void GPU_shader_uniform_1f(GPUShader *sh, const char *name, float value)
void GPU_shader_bind(GPUShader *shader)
void GPU_shader_uniform_1b(GPUShader *sh, const char *name, bool value)
void GPU_shader_unbind()
void uiItemR(uiLayout *layout, PointerRNA *ptr, const char *propname, eUI_Item_Flag flag, const char *name, int icon)
@ UI_ITEM_R_SPLIT_EMPTY_NAME
struct GPUShader GPUShader
#define output
DInputSocket input_by_identifier(StringRef identifier) const
GPUShader * get_shader(const char *info_name, ResultPrecision precision)
Result create_result(ResultType type, ResultPrecision precision)
NodeOperation(Context &context, DNode node)
Result & get_input(StringRef identifier) const
Definition operation.cc:144
Result & get_result(StringRef identifier)
Definition operation.cc:46
void bind_as_image(GPUShader *shader, const char *image_name, bool read=false) const
Definition result.cc:264
const Domain & domain() const
Definition result.cc:712
void increment_reference_count(int count=1)
Definition result.cc:600
void allocate_texture(Domain domain, bool from_pool=true)
Definition result.cc:204
void steal_data(Result &source)
Definition result.cc:304
void bind_as_texture(GPUShader *shader, const char *texture_name) const
Definition result.cc:253
local_group_size(16, 16) .push_constant(Type b
void node_type_storage(bNodeType *ntype, const char *storagename, void(*freefunc)(bNode *node), void(*copyfunc)(bNodeTree *dest_ntree, bNode *dest_node, const bNode *src_node))
Definition node.cc:4632
void node_register_type(bNodeType *ntype)
Definition node.cc:1708
static void node_composit_buts_keying(uiLayout *layout, bContext *, PointerRNA *ptr)
static NodeOperation * get_compositor_operation(Context &context, DNode node)
static void node_composit_init_keying(bNodeTree *, bNode *node)
static void cmp_node_keying_declare(NodeDeclarationBuilder &b)
void symmetric_separable_blur(Context &context, Result &input, Result &output, float2 radius, int filter_type=R_FILTER_GAUSS, bool extend_bounds=false, bool gamma_correct=false)
void morphological_distance(Context &context, Result &input, Result &output, int distance)
void compute_dispatch_threads_at_least(GPUShader *shader, int2 threads_range, int2 local_size=int2(16))
Definition utilities.cc:131
void morphological_distance_feather(Context &context, Result &input, Result &output, int distance, int falloff_type=PROP_SMOOTH)
VecBase< float, 2 > float2
void register_node_type_cmp_keying()
void cmp_node_type_base(blender::bke::bNodeType *ntype, int type, const char *name, short nclass)
void node_free_standard_storage(bNode *node)
Definition node_util.cc:46
void node_copy_standard_storage(bNodeTree *, bNode *dest_node, const bNode *src_node)
Definition node_util.cc:58
Defines a node type.
Definition BKE_node.hh:218
NodeGetCompositorOperationFunction get_compositor_operation
Definition BKE_node.hh:324
void(* initfunc)(bNodeTree *ntree, bNode *node)
Definition BKE_node.hh:267
void(* draw_buttons)(uiLayout *, bContext *C, PointerRNA *ptr)
Definition BKE_node.hh:238
NodeDeclareFunction declare
Definition BKE_node.hh:347
PointerRNA * ptr
Definition wm_files.cc:4126