Blender V5.0
node_composite_ellipsemask.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 "COM_node_operation.hh"
18#include "COM_utilities.hh"
19
21
23
25 {CMP_NODE_MASKTYPE_ADD, "ADD", 0, N_("Add"), ""},
26 {CMP_NODE_MASKTYPE_SUBTRACT, "SUBTRACT", 0, N_("Subtract"), ""},
27 {CMP_NODE_MASKTYPE_MULTIPLY, "MULTIPLY", 0, N_("Multiply"), ""},
28 {CMP_NODE_MASKTYPE_NOT, "NOT", 0, N_("Not"), ""},
29 {0, nullptr, 0, nullptr, nullptr},
30};
31
33{
34 b.add_input<decl::Menu>("Operation")
35 .default_value(CMP_NODE_MASKTYPE_ADD)
36 .static_items(operation_items)
38 b.add_input<decl::Float>("Mask")
39 .subtype(PROP_FACTOR)
40 .default_value(0.0f)
41 .min(0.0f)
42 .max(1.0f)
43 .structure_type(StructureType::Dynamic);
44 b.add_input<decl::Float>("Value")
45 .subtype(PROP_FACTOR)
46 .default_value(1.0f)
47 .min(0.0f)
48 .max(1.0f)
49 .structure_type(StructureType::Dynamic);
50 b.add_input<decl::Vector>("Position")
51 .subtype(PROP_FACTOR)
52 .dimensions(2)
53 .default_value({0.5f, 0.5f})
54 .min(-0.5f)
55 .max(1.5f);
56 b.add_input<decl::Vector>("Size")
57 .subtype(PROP_FACTOR)
58 .dimensions(2)
59 .default_value({0.2f, 0.1f})
60 .min(0.0f)
61 .max(1.0f);
62 b.add_input<decl::Float>("Rotation").subtype(PROP_ANGLE);
63
64 b.add_output<decl::Float>("Mask").structure_type(StructureType::Dynamic);
65}
66
67using namespace blender::compositor;
68
69template<CMPNodeMaskType MaskType>
70static void ellipse_mask(const Result &base_mask,
71 const Result &value_mask,
72 Result &output_mask,
73 const int2 &texel,
74 const int2 &domain_size,
75 const float2 &location,
76 const float2 &radius,
77 const float cos_angle,
78 const float sin_angle)
79{
80 float2 uv = float2(texel) / float2(domain_size - int2(1));
81 uv -= location;
82 uv.y *= float(domain_size.y) / float(domain_size.x);
83 uv = float2x2(float2(cos_angle, -sin_angle), float2(sin_angle, cos_angle)) * uv;
84 bool is_inside = math::length(uv / radius) < 1.0f;
85
86 float base_mask_value = base_mask.load_pixel<float, true>(texel);
87 float value = value_mask.load_pixel<float, true>(texel);
88
89 float output_mask_value = 0.0f;
90 if constexpr (MaskType == CMP_NODE_MASKTYPE_ADD) {
91 output_mask_value = is_inside ? math::max(base_mask_value, value) : base_mask_value;
92 }
93 else if constexpr (MaskType == CMP_NODE_MASKTYPE_SUBTRACT) {
94 output_mask_value = is_inside ? math::clamp(base_mask_value - value, 0.0f, 1.0f) :
95 base_mask_value;
96 }
97 else if constexpr (MaskType == CMP_NODE_MASKTYPE_MULTIPLY) {
98 output_mask_value = is_inside ? base_mask_value * value : 0.0f;
99 }
100 else if constexpr (MaskType == CMP_NODE_MASKTYPE_NOT) {
101 output_mask_value = is_inside ? (base_mask_value > 0.0f ? 0.0f : value) : base_mask_value;
102 }
103
104 output_mask.store_pixel(texel, output_mask_value);
105}
106
108 public:
110
111 void execute() override
112 {
113 const Result &input_mask = get_input("Mask");
114 Result &output_mask = get_result("Mask");
115 const float2 size = this->get_size();
116 if (math::is_any_zero(size)) {
117 output_mask.share_data(input_mask);
118 return;
119 }
120 /* For single value masks, the output will assume the compositing region, so ensure it is valid
121 * first. See the compute_domain method. */
122 if (input_mask.is_single_value() && !context().is_valid_compositing_region()) {
123 output_mask.allocate_invalid();
124 return;
125 }
126
127 if (this->context().use_gpu()) {
128 this->execute_gpu();
129 }
130 else {
131 this->execute_cpu();
132 }
133 }
134
136 {
138 GPU_shader_bind(shader);
139
140 const Domain domain = compute_domain();
141
142 GPU_shader_uniform_2iv(shader, "domain_size", domain.size);
143
144 GPU_shader_uniform_2fv(shader, "location", get_location());
145 GPU_shader_uniform_2fv(shader, "radius", get_size() / 2.0f);
146 GPU_shader_uniform_1f(shader, "cos_angle", std::cos(get_angle()));
147 GPU_shader_uniform_1f(shader, "sin_angle", std::sin(get_angle()));
148
149 const Result &input_mask = get_input("Mask");
150 input_mask.bind_as_texture(shader, "base_mask_tx");
151
152 const Result &value = get_input("Value");
153 value.bind_as_texture(shader, "mask_value_tx");
154
155 Result &output_mask = get_result("Mask");
156 output_mask.allocate_texture(domain);
157 output_mask.bind_as_image(shader, "output_mask_img");
158
160
161 input_mask.unbind_as_texture();
162 value.unbind_as_texture();
163 output_mask.unbind_as_image();
165 }
166
167 const char *get_shader_name()
168 {
169 switch (this->get_operation()) {
171 return "compositor_ellipse_mask_add";
173 return "compositor_ellipse_mask_subtract";
175 return "compositor_ellipse_mask_multiply";
177 return "compositor_ellipse_mask_not";
178 }
179
180 return "compositor_ellipse_mask_add";
181 }
182
184 {
185 const Result &base_mask = this->get_input("Mask");
186 const Result &value_mask = this->get_input("Value");
187
188 Result &output_mask = get_result("Mask");
189 const Domain domain = this->compute_domain();
190 output_mask.allocate_texture(domain);
191
192 const int2 domain_size = domain.size;
193 const float2 location = this->get_location();
194 const float2 radius = this->get_size() / 2.0f;
195 const float cos_angle = math::cos(this->get_angle());
196 const float sin_angle = math::sin(this->get_angle());
197
198 switch (this->get_operation()) {
200 parallel_for(domain_size, [&](const int2 texel) {
202 value_mask,
203 output_mask,
204 texel,
205 domain_size,
206 location,
207 radius,
208 cos_angle,
209 sin_angle);
210 });
211 return;
213 parallel_for(domain_size, [&](const int2 texel) {
215 value_mask,
216 output_mask,
217 texel,
218 domain_size,
219 location,
220 radius,
221 cos_angle,
222 sin_angle);
223 });
224 return;
226 parallel_for(domain_size, [&](const int2 texel) {
228 value_mask,
229 output_mask,
230 texel,
231 domain_size,
232 location,
233 radius,
234 cos_angle,
235 sin_angle);
236 });
237 return;
239 parallel_for(domain_size, [&](const int2 texel) {
241 value_mask,
242 output_mask,
243 texel,
244 domain_size,
245 location,
246 radius,
247 cos_angle,
248 sin_angle);
249 });
250 return;
251 }
252
253 parallel_for(domain_size, [&](const int2 texel) {
255 value_mask,
256 output_mask,
257 texel,
258 domain_size,
259 location,
260 radius,
261 cos_angle,
262 sin_angle);
263 });
264 }
265
267 {
268 if (get_input("Mask").is_single_value()) {
269 return Domain(context().get_compositing_region_size());
270 }
271 return get_input("Mask").domain();
272 }
273
275 {
276 return this->get_input("Position").get_single_value_default(float2(0.5f));
277 }
278
280 {
281 return math::max(float2(0.0f),
282 this->get_input("Size").get_single_value_default(float2(0.2f, 0.1f)));
283 }
284
285 float get_angle()
286 {
287 return this->get_input("Rotation").get_single_value_default(0.0f);
288 }
289
291 {
292 const Result &input = this->get_input("Operation");
293 const MenuValue default_menu_value = MenuValue(CMP_NODE_MASKTYPE_ADD);
294 const MenuValue menu_value = input.get_single_value_default(default_menu_value);
295 return static_cast<CMPNodeMaskType>(menu_value.value);
296 }
297};
298
300{
301 return new EllipseMaskOperation(context, node);
302}
303
304} // namespace blender::nodes::node_composite_ellipsemask_cc
305
307{
309
310 static blender::bke::bNodeType ntype;
311
312 cmp_node_type_base(&ntype, "CompositorNodeEllipseMask", CMP_NODE_MASK_ELLIPSE);
313 ntype.ui_name = "Ellipse Mask";
314 ntype.ui_description =
315 "Create elliptical mask suitable for use as a simple matte or vignette mask";
316 ntype.enum_name_legacy = "ELLIPSEMASK";
317 ntype.nclass = NODE_CLASS_MATTE;
318 ntype.declare = file_ns::cmp_node_ellipsemask_declare;
319 ntype.get_compositor_operation = file_ns::get_compositor_operation;
320
322}
#define NODE_CLASS_MATTE
Definition BKE_node.hh:454
#define CMP_NODE_MASK_ELLIPSE
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
void share_data(const Result &source)
Definition result.cc:523
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)
bool is_any_zero(const T &a)
T length(const VecBase< T, Size > &a)
T sin(const AngleRadianBase< T > &a)
T max(const T &a, const T &b)
static void cmp_node_ellipsemask_declare(NodeDeclarationBuilder &b)
static NodeOperation * get_compositor_operation(Context &context, DNode node)
static void ellipse_mask(const Result &base_mask, const Result &value_mask, Result &output_mask, const int2 &texel, const int2 &domain_size, const float2 &location, const float2 &radius, const float cos_angle, const float sin_angle)
MatBase< float, 2, 2 > float2x2
VecBase< int32_t, 2 > int2
VecBase< float, 2 > float2
static void register_node_type_cmp_ellipsemask()
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)