Blender V4.3
node_composite_blur.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
9#include "BLI_assert.h"
10#include "BLI_math_base.hh"
11#include "BLI_math_vector.hh"
13
14#include "RNA_access.hh"
15
16#include "UI_interface.hh"
17#include "UI_resources.hh"
18
19#include "GPU_shader.hh"
20#include "GPU_texture.hh"
21
24#include "COM_node_operation.hh"
26#include "COM_utilities.hh"
27
29
30/* **************** BLUR ******************** */
31
33
35
37{
38 b.add_input<decl::Color>("Image")
39 .default_value({1.0f, 1.0f, 1.0f, 1.0f})
40 .compositor_domain_priority(0);
41 b.add_input<decl::Float>("Size")
42 .default_value(1.0f)
43 .min(0.0f)
44 .max(1.0f)
46 b.add_output<decl::Color>("Image");
47}
48
49static void node_composit_init_blur(bNodeTree * /*ntree*/, bNode *node)
50{
51 NodeBlurData *data = MEM_cnew<NodeBlurData>(__func__);
52 data->filtertype = R_FILTER_GAUSS;
53 node->storage = data;
54}
55
57{
58 uiLayout *col, *row;
59
60 col = uiLayoutColumn(layout, false);
61 const int filter = RNA_enum_get(ptr, "filter_type");
62 const int reference = RNA_boolean_get(ptr, "use_variable_size");
63
64 uiItemR(col, ptr, "filter_type", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE);
65 if (filter != R_FILTER_FAST_GAUSS) {
66 uiItemR(col, ptr, "use_variable_size", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE);
67 if (!reference) {
68 uiItemR(col, ptr, "use_bokeh", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE);
69 }
70 uiItemR(col, ptr, "use_gamma_correction", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE);
71 }
72
73 uiItemR(col, ptr, "use_relative", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE);
74
75 if (RNA_boolean_get(ptr, "use_relative")) {
76 uiItemL(col, IFACE_("Aspect Correction"), ICON_NONE);
77 row = uiLayoutRow(layout, true);
78 uiItemR(row,
79 ptr,
80 "aspect_correction",
82 nullptr,
83 ICON_NONE);
84
85 col = uiLayoutColumn(layout, true);
86 uiItemR(col, ptr, "factor_x", UI_ITEM_R_SPLIT_EMPTY_NAME, IFACE_("X"), ICON_NONE);
87 uiItemR(col, ptr, "factor_y", UI_ITEM_R_SPLIT_EMPTY_NAME, IFACE_("Y"), ICON_NONE);
88 }
89 else {
90 col = uiLayoutColumn(layout, true);
91 uiItemR(col, ptr, "size_x", UI_ITEM_R_SPLIT_EMPTY_NAME, IFACE_("X"), ICON_NONE);
92 uiItemR(col, ptr, "size_y", UI_ITEM_R_SPLIT_EMPTY_NAME, IFACE_("Y"), ICON_NONE);
93 }
94 uiItemR(col, ptr, "use_extended_bounds", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE);
95}
96
97using namespace blender::realtime_compositor;
98
100 public:
102
103 void execute() override
104 {
105 if (is_identity()) {
106 get_input("Image").pass_through(get_result("Image"));
107 return;
108 }
109
110 if (node_storage(bnode()).filtertype == R_FILTER_FAST_GAUSS) {
112 context(), get_input("Image"), get_result("Image"), compute_blur_radius());
113 }
114 else if (use_variable_size()) {
116 }
117 else if (use_separable_filter()) {
119 get_input("Image"),
120 get_result("Image"),
122 node_storage(bnode()).filtertype,
124 node_storage(bnode()).gamma);
125 }
126 else {
128 }
129 }
130
132 {
133 GPUShader *shader = context().get_shader("compositor_symmetric_blur");
134 GPU_shader_bind(shader);
135
136 GPU_shader_uniform_1b(shader, "extend_bounds", get_extend_bounds());
137 GPU_shader_uniform_1b(shader, "gamma_correct", node_storage(bnode()).gamma);
138
139 const Result &input_image = get_input("Image");
140 input_image.bind_as_texture(shader, "input_tx");
141
142 const float2 blur_radius = compute_blur_radius();
143
145 context(), node_storage(bnode()).filtertype, blur_radius);
146 weights.bind_as_texture(shader, "weights_tx");
147
148 Domain domain = compute_domain();
149 if (get_extend_bounds()) {
150 /* Add a radius amount of pixels in both sides of the image, hence the multiply by 2. */
151 domain.size += int2(math::ceil(blur_radius)) * 2;
152 }
153
154 Result &output_image = get_result("Image");
155 output_image.allocate_texture(domain);
156 output_image.bind_as_image(shader, "output_img");
157
158 compute_dispatch_threads_at_least(shader, domain.size);
159
161 output_image.unbind_as_image();
162 input_image.unbind_as_texture();
163 weights.unbind_as_texture();
164 }
165
167 {
168 GPUShader *shader = context().get_shader("compositor_symmetric_blur_variable_size");
169 GPU_shader_bind(shader);
170
171 GPU_shader_uniform_1b(shader, "extend_bounds", get_extend_bounds());
172 GPU_shader_uniform_1b(shader, "gamma_correct", node_storage(bnode()).gamma);
173
174 const Result &input_image = get_input("Image");
175 input_image.bind_as_texture(shader, "input_tx");
176
177 const float2 blur_radius = compute_blur_radius();
178
180 context(), node_storage(bnode()).filtertype, blur_radius);
181 weights.bind_as_texture(shader, "weights_tx");
182
183 const Result &input_size = get_input("Size");
184 input_size.bind_as_texture(shader, "size_tx");
185
186 Domain domain = compute_domain();
187 if (get_extend_bounds()) {
188 /* Add a radius amount of pixels in both sides of the image, hence the multiply by 2. */
189 domain.size += int2(math::ceil(blur_radius)) * 2;
190 }
191
192 Result &output_image = get_result("Image");
193 output_image.allocate_texture(domain);
194 output_image.bind_as_image(shader, "output_img");
195
196 compute_dispatch_threads_at_least(shader, domain.size);
197
199 output_image.unbind_as_image();
200 input_image.unbind_as_texture();
201 weights.unbind_as_texture();
202 input_size.unbind_as_texture();
203 }
204
206 {
207 const float size = math::clamp(get_input("Size").get_float_value_default(1.0f), 0.0f, 1.0f);
208
209 if (!node_storage(bnode()).relative) {
210 return float2(node_storage(bnode()).sizex, node_storage(bnode()).sizey) * size;
211 }
212
213 int2 image_size = get_input("Image").domain().size;
214 switch (node_storage(bnode()).aspect) {
216 image_size.y = image_size.x;
217 break;
219 image_size.x = image_size.y;
220 break;
221 default:
222 BLI_assert(node_storage(bnode()).aspect == CMP_NODE_BLUR_ASPECT_NONE);
223 break;
224 }
225
226 return float2(image_size) * get_size_factor() * size;
227 }
228
229 /* Returns true if the operation does nothing and the input can be passed through. */
231 {
232 const Result &input = get_input("Image");
233 /* Single value inputs can't be blurred and are returned as is. */
234 if (input.is_single_value()) {
235 return true;
236 }
237
238 /* Zero blur radius. The operation does nothing and the input can be passed through. */
239 if (compute_blur_radius() == float2(0.0)) {
240 return true;
241 }
242
243 return false;
244 }
245
246 /* The blur node can operate with different filter types, evaluated on the normalized distance to
247 * the center of the filter. Some of those filters are separable and can be computed as such. If
248 * the bokeh member is disabled in the node, then the filter is always computed as separable even
249 * if it is not in fact separable, in which case, the used filter is a cheaper approximation to
250 * the actual filter. If the bokeh member is enabled, then the filter is computed as separable if
251 * it is in fact separable and as a normal 2D filter otherwise. */
253 {
254 if (!node_storage(bnode()).bokeh) {
255 return true;
256 }
257
258 /* Only Gaussian filters are separable. The rest is not. */
259 switch (node_storage(bnode()).filtertype) {
260 case R_FILTER_GAUSS:
262 return true;
263 default:
264 return false;
265 }
266 }
267
269 {
270 return get_variable_size() && !get_input("Size").is_single_value() &&
271 node_storage(bnode()).filtertype != R_FILTER_FAST_GAUSS;
272 }
273
275 {
276 return float2(node_storage(bnode()).percentx, node_storage(bnode()).percenty) / 100.0f;
277 }
278
283
288};
289
291{
292 return new BlurOperation(context, node);
293}
294
295} // namespace blender::nodes::node_composite_blur_cc
296
298{
299 namespace file_ns = blender::nodes::node_composite_blur_cc;
300
301 static blender::bke::bNodeType ntype;
302
303 cmp_node_type_base(&ntype, CMP_NODE_BLUR, "Blur", NODE_CLASS_OP_FILTER);
304 ntype.declare = file_ns::cmp_node_blur_declare;
305 ntype.draw_buttons = file_ns::node_composit_buts_blur;
306 ntype.flag |= NODE_PREVIEW;
307 ntype.initfunc = file_ns::node_composit_init_blur;
310 ntype.get_compositor_operation = file_ns::get_compositor_operation;
311
313}
#define NODE_STORAGE_FUNCS(StorageT)
Definition BKE_node.hh:1799
#define NODE_CLASS_OP_FILTER
Definition BKE_node.hh:408
#define BLI_assert(a)
Definition BLI_assert.h:50
#define IFACE_(msgid)
@ CMP_NODE_BLUR_ASPECT_NONE
@ CMP_NODE_BLUR_ASPECT_X
@ CMP_NODE_BLUR_ASPECT_Y
@ NODE_PREVIEW
@ CMP_NODEFLAG_BLUR_VARIABLE_SIZE
@ CMP_NODEFLAG_BLUR_EXTEND_BOUNDS
@ R_FILTER_GAUSS
@ R_FILTER_FAST_GAUSS
void GPU_shader_bind(GPUShader *shader)
void GPU_shader_uniform_1b(GPUShader *sh, const char *name, bool value)
void GPU_shader_unbind()
void uiItemL(uiLayout *layout, const char *name, int icon)
uiLayout * uiLayoutRow(uiLayout *layout, bool align)
uiLayout * uiLayoutColumn(uiLayout *layout, bool align)
void uiItemR(uiLayout *layout, PointerRNA *ptr, const char *propname, eUI_Item_Flag flag, const char *name, int icon)
@ UI_ITEM_R_SPLIT_EMPTY_NAME
@ UI_ITEM_R_EXPAND
struct GPUShader GPUShader
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_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
void pass_through(Result &target)
Definition result.cc:289
const Domain & domain() const
Definition result.cc:712
void allocate_texture(Domain domain, bool from_pool=true)
Definition result.cc:204
void bind_as_texture(GPUShader *shader, const char *texture_name) const
Definition result.cc:253
SymmetricBlurWeights & get(Context &context, int type, float2 radius)
void bind_as_texture(GPUShader *shader, const char *texture_name) const
local_group_size(16, 16) .push_constant(Type b
uint col
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
T clamp(const T &a, const T &min, const T &max)
T ceil(const T &a)
static void cmp_node_blur_declare(NodeDeclarationBuilder &b)
static void node_composit_init_blur(bNodeTree *, bNode *node)
static void node_composit_buts_blur(uiLayout *layout, bContext *, PointerRNA *ptr)
static NodeOperation * get_compositor_operation(Context &context, DNode node)
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 recursive_gaussian_blur(Context &context, Result &input, Result &output, float2 radius)
void compute_dispatch_threads_at_least(GPUShader *shader, int2 threads_range, int2 local_size=int2(16))
Definition utilities.cc:131
VecBase< int32_t, 2 > int2
VecBase< float, 2 > float2
void register_node_type_cmp_blur()
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
bool RNA_boolean_get(PointerRNA *ptr, const char *name)
int RNA_enum_get(PointerRNA *ptr, const char *name)
int16_t custom1
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