Blender V4.5
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 "UI_interface.hh"
16#include "UI_resources.hh"
17
18#include "GPU_shader.hh"
19
20#include "COM_node_operation.hh"
21#include "COM_utilities.hh"
22
24
25/* **************** SCALAR MATH ******************** */
26
28
30{
31 b.add_input<decl::Float>("Mask").subtype(PROP_FACTOR).default_value(0.0f).min(0.0f).max(1.0f);
32 b.add_input<decl::Float>("Value").subtype(PROP_FACTOR).default_value(1.0f).min(0.0f).max(1.0f);
33 b.add_input<decl::Vector>("Position")
34 .subtype(PROP_FACTOR)
35 .dimensions(2)
36 .default_value({0.5f, 0.5f})
37 .min(-0.5f)
38 .max(1.5f)
39 .compositor_expects_single_value();
40 b.add_input<decl::Vector>("Size")
41 .subtype(PROP_FACTOR)
42 .dimensions(2)
43 .default_value({0.2f, 0.1f})
44 .min(0.0f)
45 .max(1.0f)
46 .compositor_expects_single_value();
47 b.add_input<decl::Float>("Rotation").subtype(PROP_ANGLE).compositor_expects_single_value();
48
49 b.add_output<decl::Float>("Mask");
50}
51
53{
54 layout->prop(ptr, "mask_type", UI_ITEM_R_SPLIT_EMPTY_NAME, std::nullopt, ICON_NONE);
55}
56
57using namespace blender::compositor;
58
59template<CMPNodeMaskType MaskType>
60static void ellipse_mask(const Result &base_mask,
61 const Result &value_mask,
62 Result &output_mask,
63 const int2 &texel,
64 const int2 &domain_size,
65 const float2 &location,
66 const float2 &radius,
67 const float cos_angle,
68 const float sin_angle)
69{
70 float2 uv = float2(texel) / float2(domain_size - int2(1));
71 uv -= location;
72 uv.y *= float(domain_size.y) / float(domain_size.x);
73 uv = float2x2(float2(cos_angle, -sin_angle), float2(sin_angle, cos_angle)) * uv;
74 bool is_inside = math::length(uv / radius) < 1.0f;
75
76 float base_mask_value = base_mask.load_pixel<float, true>(texel);
77 float value = value_mask.load_pixel<float, true>(texel);
78
79 float output_mask_value = 0.0f;
80 if constexpr (MaskType == CMP_NODE_MASKTYPE_ADD) {
81 output_mask_value = is_inside ? math::max(base_mask_value, value) : base_mask_value;
82 }
83 else if constexpr (MaskType == CMP_NODE_MASKTYPE_SUBTRACT) {
84 output_mask_value = is_inside ? math::clamp(base_mask_value - value, 0.0f, 1.0f) :
85 base_mask_value;
86 }
87 else if constexpr (MaskType == CMP_NODE_MASKTYPE_MULTIPLY) {
88 output_mask_value = is_inside ? base_mask_value * value : 0.0f;
89 }
90 else if constexpr (MaskType == CMP_NODE_MASKTYPE_NOT) {
91 output_mask_value = is_inside ? (base_mask_value > 0.0f ? 0.0f : value) : base_mask_value;
92 }
93
94 output_mask.store_pixel(texel, output_mask_value);
95}
96
98 public:
100
101 void execute() override
102 {
103 const Result &input_mask = get_input("Mask");
104 Result &output_mask = get_result("Mask");
105 const float2 size = this->get_size();
106 if (math::is_any_zero(size)) {
107 output_mask.share_data(input_mask);
108 return;
109 }
110 /* For single value masks, the output will assume the compositing region, so ensure it is valid
111 * first. See the compute_domain method. */
112 if (input_mask.is_single_value() && !context().is_valid_compositing_region()) {
113 output_mask.allocate_invalid();
114 return;
115 }
116
117 if (this->context().use_gpu()) {
118 this->execute_gpu();
119 }
120 else {
121 this->execute_cpu();
122 }
123 }
124
126 {
127 GPUShader *shader = context().get_shader(get_shader_name());
128 GPU_shader_bind(shader);
129
130 const Domain domain = compute_domain();
131
132 GPU_shader_uniform_2iv(shader, "domain_size", domain.size);
133
134 GPU_shader_uniform_2fv(shader, "location", get_location());
135 GPU_shader_uniform_2fv(shader, "radius", get_size() / 2.0f);
136 GPU_shader_uniform_1f(shader, "cos_angle", std::cos(get_angle()));
137 GPU_shader_uniform_1f(shader, "sin_angle", std::sin(get_angle()));
138
139 const Result &input_mask = get_input("Mask");
140 input_mask.bind_as_texture(shader, "base_mask_tx");
141
142 const Result &value = get_input("Value");
143 value.bind_as_texture(shader, "mask_value_tx");
144
145 Result &output_mask = get_result("Mask");
146 output_mask.allocate_texture(domain);
147 output_mask.bind_as_image(shader, "output_mask_img");
148
150
151 input_mask.unbind_as_texture();
152 value.unbind_as_texture();
153 output_mask.unbind_as_image();
155 }
156
157 const char *get_shader_name()
158 {
159 switch (get_mask_type()) {
160 default:
162 return "compositor_ellipse_mask_add";
164 return "compositor_ellipse_mask_subtract";
166 return "compositor_ellipse_mask_multiply";
168 return "compositor_ellipse_mask_not";
169 }
170 }
171
173 {
174 const Result &base_mask = this->get_input("Mask");
175 const Result &value_mask = this->get_input("Value");
176
177 Result &output_mask = get_result("Mask");
178 const Domain domain = this->compute_domain();
179 output_mask.allocate_texture(domain);
180
181 const int2 domain_size = domain.size;
182 const float2 location = this->get_location();
183 const float2 radius = this->get_size() / 2.0f;
184 const float cos_angle = math::cos(this->get_angle());
185 const float sin_angle = math::sin(this->get_angle());
186
187 switch (this->get_mask_type()) {
189 parallel_for(domain_size, [&](const int2 texel) {
191 value_mask,
192 output_mask,
193 texel,
194 domain_size,
195 location,
196 radius,
197 cos_angle,
198 sin_angle);
199 });
200 break;
202 parallel_for(domain_size, [&](const int2 texel) {
204 value_mask,
205 output_mask,
206 texel,
207 domain_size,
208 location,
209 radius,
210 cos_angle,
211 sin_angle);
212 });
213 break;
215 parallel_for(domain_size, [&](const int2 texel) {
217 value_mask,
218 output_mask,
219 texel,
220 domain_size,
221 location,
222 radius,
223 cos_angle,
224 sin_angle);
225 });
226 break;
228 parallel_for(domain_size, [&](const int2 texel) {
230 value_mask,
231 output_mask,
232 texel,
233 domain_size,
234 location,
235 radius,
236 cos_angle,
237 sin_angle);
238 });
239 break;
240 }
241 }
242
244 {
245 if (get_input("Mask").is_single_value()) {
246 return Domain(context().get_compositing_region_size());
247 }
248 return get_input("Mask").domain();
249 }
250
252 {
253 return CMPNodeMaskType(bnode().custom1);
254 }
255
257 {
258 return this->get_input("Position").get_single_value_default(float3(0.5f, 0.5f, 0.0f)).xy();
259 }
260
262 {
263 return math::max(
264 float2(0.0f),
265 this->get_input("Size").get_single_value_default(float3(0.2f, 0.1f, 0.0f)).xy());
266 }
267
268 float get_angle()
269 {
270 return this->get_input("Rotation").get_single_value_default(0.0f);
271 }
272};
273
275{
276 return new EllipseMaskOperation(context, node);
277}
278
279} // namespace blender::nodes::node_composite_ellipsemask_cc
280
282{
284
285 static blender::bke::bNodeType ntype;
286
287 cmp_node_type_base(&ntype, "CompositorNodeEllipseMask", CMP_NODE_MASK_ELLIPSE);
288 ntype.ui_name = "Ellipse Mask";
289 ntype.ui_description =
290 "Create elliptical mask suitable for use as a simple matte or vignette mask";
291 ntype.enum_name_legacy = "ELLIPSEMASK";
292 ntype.nclass = NODE_CLASS_MATTE;
293 ntype.declare = file_ns::cmp_node_ellipsemask_declare;
294 ntype.draw_buttons = file_ns::node_composit_buts_ellipsemask;
295 ntype.get_compositor_operation = file_ns::get_compositor_operation;
296
298}
#define NODE_CLASS_MATTE
Definition BKE_node.hh:440
#define CMP_NODE_MASK_ELLIPSE
CMPNodeMaskType
void GPU_shader_uniform_2fv(GPUShader *sh, const char *name, const float data[2])
void GPU_shader_uniform_1f(GPUShader *sh, const char *name, float value)
void GPU_shader_bind(GPUShader *shader, const blender::gpu::shader::SpecializationConstants *constants_state=nullptr)
void GPU_shader_uniform_2iv(GPUShader *sh, const char *name, const int data[2])
void GPU_shader_unbind()
#define NOD_REGISTER_NODE(REGISTER_FUNC)
@ PROP_ANGLE
Definition RNA_types.hh:240
@ PROP_FACTOR
Definition RNA_types.hh:239
@ UI_ITEM_R_SPLIT_EMPTY_NAME
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
Definition btDbvt.cpp:52
GPUShader * 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:401
T get_single_value_default(const T &default_value) const
void allocate_texture(Domain domain, bool from_pool=true)
Definition result.cc:309
void store_pixel(const int2 &texel, const T &pixel_value)
void unbind_as_texture() const
Definition result.cc:389
void bind_as_texture(GPUShader *shader, const char *texture_name) const
Definition result.cc:365
const Domain & domain() const
T load_pixel(const int2 &texel) const
void bind_as_image(GPUShader *shader, const char *image_name, bool read=false) const
Definition result.cc:376
void unbind_as_image() const
Definition result.cc:395
bool is_single_value() const
Definition result.cc:622
#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:772
void node_register_type(bNodeType &ntype)
Definition node.cc:2748
void compute_dispatch_threads_at_least(GPUShader *shader, int2 threads_range, int2 local_size=int2(16))
Definition utilities.cc:170
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 node_composit_buts_ellipsemask(uiLayout *layout, bContext *, PointerRNA *ptr)
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
VecBase< float, 3 > float3
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:226
std::string ui_description
Definition BKE_node.hh:232
NodeGetCompositorOperationFunction get_compositor_operation
Definition BKE_node.hh:336
const char * enum_name_legacy
Definition BKE_node.hh:235
void(* draw_buttons)(uiLayout *, bContext *C, PointerRNA *ptr)
Definition BKE_node.hh:247
NodeDeclareFunction declare
Definition BKE_node.hh:355
void prop(PointerRNA *ptr, PropertyRNA *prop, int index, int value, eUI_Item_Flag flag, std::optional< blender::StringRef > name_opt, int icon, std::optional< blender::StringRef > placeholder=std::nullopt)
int xy[2]
Definition wm_draw.cc:174
PointerRNA * ptr
Definition wm_files.cc:4226