Blender V5.0
node_composite_boxmask.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
8
9#include <cmath>
10
11#include "BLI_math_base.hh"
14
15#include "GPU_shader.hh"
16
17#include "BKE_node.hh"
18
19#include "COM_node_operation.hh"
20#include "COM_utilities.hh"
21
23
25
27 {CMP_NODE_MASKTYPE_ADD, "ADD", 0, N_("Add"), ""},
28 {CMP_NODE_MASKTYPE_SUBTRACT, "SUBTRACT", 0, N_("Subtract"), ""},
29 {CMP_NODE_MASKTYPE_MULTIPLY, "MULTIPLY", 0, N_("Multiply"), ""},
30 {CMP_NODE_MASKTYPE_NOT, "NOT", 0, N_("Not"), ""},
31 {0, nullptr, 0, nullptr, nullptr},
32};
33
35{
36 b.add_input<decl::Menu>("Operation")
37 .default_value(CMP_NODE_MASKTYPE_ADD)
38 .static_items(operation_items)
40 b.add_input<decl::Float>("Mask")
41 .subtype(PROP_FACTOR)
42 .default_value(0.0f)
43 .min(0.0f)
44 .max(1.0f)
45 .structure_type(StructureType::Dynamic);
46 b.add_input<decl::Float>("Value")
47 .subtype(PROP_FACTOR)
48 .default_value(1.0f)
49 .min(0.0f)
50 .max(1.0f)
51 .structure_type(StructureType::Dynamic);
52 b.add_input<decl::Vector>("Position")
53 .subtype(PROP_FACTOR)
54 .dimensions(2)
55 .default_value({0.5f, 0.5f})
56 .min(-0.5f)
57 .max(1.5f);
58 b.add_input<decl::Vector>("Size")
59 .subtype(PROP_FACTOR)
60 .dimensions(2)
61 .default_value({0.2f, 0.1f})
62 .min(0.0f)
63 .max(1.0f);
64 b.add_input<decl::Float>("Rotation").subtype(PROP_ANGLE);
65
66 b.add_output<decl::Float>("Mask").structure_type(StructureType::Dynamic);
67}
68
69using namespace blender::compositor;
70
71template<CMPNodeMaskType MaskType>
72static void box_mask(const Result &base_mask,
73 const Result &value_mask,
74 Result &output_mask,
75 const int2 &texel,
76 const int2 &domain_size,
77 const float2 &location,
78 const float2 &size,
79 const float cos_angle,
80 const float sin_angle)
81{
82 float2 uv = float2(texel) / float2(domain_size - int2(1));
83 uv -= location;
84 uv.y *= float(domain_size.y) / float(domain_size.x);
85 uv = float2x2(float2(cos_angle, -sin_angle), float2(sin_angle, cos_angle)) * uv;
86 bool is_inside = math::abs(uv.x) < size.x && math::abs(uv.y) < size.y;
87
88 float base_mask_value = base_mask.load_pixel<float, true>(texel);
89 float value = value_mask.load_pixel<float, true>(texel);
90
91 float output_mask_value = 0.0f;
92 if constexpr (MaskType == CMP_NODE_MASKTYPE_ADD) {
93 output_mask_value = is_inside ? math::max(base_mask_value, value) : base_mask_value;
94 }
95 else if constexpr (MaskType == CMP_NODE_MASKTYPE_SUBTRACT) {
96 output_mask_value = is_inside ? math::clamp(base_mask_value - value, 0.0f, 1.0f) :
97 base_mask_value;
98 }
99 else if constexpr (MaskType == CMP_NODE_MASKTYPE_MULTIPLY) {
100 output_mask_value = is_inside ? base_mask_value * value : 0.0f;
101 }
102 else if constexpr (MaskType == CMP_NODE_MASKTYPE_NOT) {
103 output_mask_value = is_inside ? (base_mask_value > 0.0f ? 0.0f : value) : base_mask_value;
104 }
105
106 output_mask.store_pixel(texel, output_mask_value);
107}
108
110 public:
112
113 void execute() override
114 {
115 const Result &input_mask = get_input("Mask");
116 Result &output_mask = get_result("Mask");
117 /* For single value masks, the output will assume the compositing region, so ensure it is valid
118 * first. See the compute_domain method. */
119 if (input_mask.is_single_value() && !context().is_valid_compositing_region()) {
120 output_mask.allocate_invalid();
121 return;
122 }
123
124 if (this->context().use_gpu()) {
125 this->execute_gpu();
126 }
127 else {
128 this->execute_cpu();
129 }
130 }
131
133 {
135 GPU_shader_bind(shader);
136
137 const Domain domain = compute_domain();
138
139 GPU_shader_uniform_2iv(shader, "domain_size", domain.size);
140
141 GPU_shader_uniform_2fv(shader, "location", get_location());
142 GPU_shader_uniform_2fv(shader, "size", get_size() / 2.0f);
143 GPU_shader_uniform_1f(shader, "cos_angle", std::cos(get_angle()));
144 GPU_shader_uniform_1f(shader, "sin_angle", std::sin(get_angle()));
145
146 const Result &input_mask = get_input("Mask");
147 input_mask.bind_as_texture(shader, "base_mask_tx");
148
149 const Result &value = get_input("Value");
150 value.bind_as_texture(shader, "mask_value_tx");
151
152 Result &output_mask = get_result("Mask");
153 output_mask.allocate_texture(domain);
154 output_mask.bind_as_image(shader, "output_mask_img");
155
157
158 input_mask.unbind_as_texture();
159 value.unbind_as_texture();
160 output_mask.unbind_as_image();
162 }
163
164 const char *get_shader_name()
165 {
166 switch (this->get_operation()) {
168 return "compositor_box_mask_add";
170 return "compositor_box_mask_subtract";
172 return "compositor_box_mask_multiply";
174 return "compositor_box_mask_not";
175 }
176
177 return "compositor_box_mask_add";
178 }
179
181 {
182 const Result &base_mask = this->get_input("Mask");
183 const Result &value_mask = this->get_input("Value");
184
185 Result &output_mask = get_result("Mask");
186 const Domain domain = this->compute_domain();
187 output_mask.allocate_texture(domain);
188
189 const int2 domain_size = domain.size;
190 const float2 location = this->get_location();
191 const float2 size = this->get_size() / 2.0f;
192 const float cos_angle = math::cos(this->get_angle());
193 const float sin_angle = math::sin(this->get_angle());
194
195 switch (this->get_operation()) {
197 parallel_for(domain_size, [&](const int2 texel) {
199 value_mask,
200 output_mask,
201 texel,
202 domain_size,
203 location,
204 size,
205 cos_angle,
206 sin_angle);
207 });
208 return;
210 parallel_for(domain_size, [&](const int2 texel) {
212 value_mask,
213 output_mask,
214 texel,
215 domain_size,
216 location,
217 size,
218 cos_angle,
219 sin_angle);
220 });
221 return;
223 parallel_for(domain_size, [&](const int2 texel) {
225 value_mask,
226 output_mask,
227 texel,
228 domain_size,
229 location,
230 size,
231 cos_angle,
232 sin_angle);
233 });
234 return;
236 parallel_for(domain_size, [&](const int2 texel) {
238 value_mask,
239 output_mask,
240 texel,
241 domain_size,
242 location,
243 size,
244 cos_angle,
245 sin_angle);
246 });
247 return;
248 }
249
250 parallel_for(domain_size, [&](const int2 texel) {
252 value_mask,
253 output_mask,
254 texel,
255 domain_size,
256 location,
257 size,
258 cos_angle,
259 sin_angle);
260 });
261 }
262
264 {
265 if (get_input("Mask").is_single_value()) {
266 return Domain(context().get_compositing_region_size());
267 }
268 return get_input("Mask").domain();
269 }
270
272 {
273 return this->get_input("Position").get_single_value_default(float2(0.5f));
274 }
275
277 {
278 return math::max(float2(0.0f),
279 this->get_input("Size").get_single_value_default(float2(0.2f, 0.1f)));
280 }
281
282 float get_angle()
283 {
284 return this->get_input("Rotation").get_single_value_default(0.0f);
285 }
286
288 {
289 const Result &input = this->get_input("Operation");
290 const MenuValue default_menu_value = MenuValue(CMP_NODE_MASKTYPE_ADD);
291 const MenuValue menu_value = input.get_single_value_default(default_menu_value);
292 return static_cast<CMPNodeMaskType>(menu_value.value);
293 }
294};
295
297{
298 return new BoxMaskOperation(context, node);
299}
300
301} // namespace blender::nodes::node_composite_boxmask_cc
302
304{
306
307 static blender::bke::bNodeType ntype;
308
309 cmp_node_type_base(&ntype, "CompositorNodeBoxMask", CMP_NODE_MASK_BOX);
310 ntype.ui_name = "Box Mask";
311 ntype.ui_description = "Create rectangular mask suitable for use as a simple matte";
312 ntype.enum_name_legacy = "BOXMASK";
313 ntype.nclass = NODE_CLASS_MATTE;
314 ntype.declare = file_ns::cmp_node_boxmask_declare;
315 ntype.get_compositor_operation = file_ns::get_compositor_operation;
316
318}
#define NODE_CLASS_MATTE
Definition BKE_node.hh:454
#define CMP_NODE_MASK_BOX
CMPNodeMaskType
void GPU_shader_uniform_1f(blender::gpu::Shader *sh, const char *name, float value)
void GPU_shader_bind(blender::gpu::Shader *shader, const blender::gpu::shader::SpecializationConstants *constants_state=nullptr)
void GPU_shader_uniform_2fv(blender::gpu::Shader *sh, const char *name, const float data[2])
void GPU_shader_unbind()
void GPU_shader_uniform_2iv(blender::gpu::Shader *sh, const char *name, const int data[2])
#define NOD_REGISTER_NODE(REGISTER_FUNC)
@ PROP_ANGLE
Definition RNA_types.hh:252
@ PROP_FACTOR
Definition RNA_types.hh:251
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
Definition btDbvt.cpp:52
gpu::Shader * get_shader(const char *info_name, ResultPrecision precision)
NodeOperation(Context &context, DNode node)
Result & get_result(StringRef identifier)
Definition operation.cc:39
Result & get_input(StringRef identifier) const
Definition operation.cc:138
T get_single_value_default(const T &default_value) const
void store_pixel(const int2 &texel, const T &pixel_value)
void allocate_texture(const Domain domain, const bool from_pool=true, const std::optional< ResultStorageType > storage_type=std::nullopt)
Definition result.cc:389
void unbind_as_texture() const
Definition result.cc:511
void bind_as_texture(gpu::Shader *shader, const char *texture_name) const
Definition result.cc:487
const Domain & domain() const
T load_pixel(const int2 &texel) const
void unbind_as_image() const
Definition result.cc:517
void bind_as_image(gpu::Shader *shader, const char *image_name, bool read=false) const
Definition result.cc:498
bool is_single_value() const
Definition result.cc:758
nullptr float
#define CMP_NODE_MASKTYPE_MULTIPLY
#define CMP_NODE_MASKTYPE_ADD
#define CMP_NODE_MASKTYPE_NOT
#define CMP_NODE_MASKTYPE_SUBTRACT
static bool is_inside(int x, int y, int cols, int rows)
Definition filesel.cc:764
#define input
void node_register_type(bNodeType &ntype)
Definition node.cc:2416
void compute_dispatch_threads_at_least(gpu::Shader *shader, int2 threads_range, int2 local_size=int2(16))
Definition utilities.cc:196
void parallel_for(const int2 range, const Function &function)
T cos(const AngleRadianBase< T > &a)
T clamp(const T &a, const T &min, const T &max)
T sin(const AngleRadianBase< T > &a)
T max(const T &a, const T &b)
T abs(const T &a)
static void box_mask(const Result &base_mask, const Result &value_mask, Result &output_mask, const int2 &texel, const int2 &domain_size, const float2 &location, const float2 &size, const float cos_angle, const float sin_angle)
static void cmp_node_boxmask_declare(NodeDeclarationBuilder &b)
static const EnumPropertyItem operation_items[]
static NodeOperation * get_compositor_operation(Context &context, DNode node)
MatBase< float, 2, 2 > float2x2
VecBase< int32_t, 2 > int2
VecBase< float, 2 > float2
static void register_node_type_cmp_boxmask()
void cmp_node_type_base(blender::bke::bNodeType *ntype, std::string idname, const std::optional< int16_t > legacy_type)
#define min(a, b)
Definition sort.cc:36
Defines a node type.
Definition BKE_node.hh:238
std::string ui_description
Definition BKE_node.hh:244
NodeGetCompositorOperationFunction get_compositor_operation
Definition BKE_node.hh:348
const char * enum_name_legacy
Definition BKE_node.hh:247
NodeDeclareFunction declare
Definition BKE_node.hh:362
#define N_(msgid)