Blender V4.5
node_composite_filter.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
10#include "BLI_math_vector.hh"
12
13#include "UI_interface.hh"
14#include "UI_resources.hh"
15
16#include "COM_node_operation.hh"
17#include "COM_utilities.hh"
18
20
21/* **************** FILTER ******************** */
22
24
26{
27 b.add_input<decl::Float>("Fac")
28 .default_value(1.0f)
29 .min(0.0f)
30 .max(1.0f)
32 .compositor_domain_priority(1);
33 b.add_input<decl::Color>("Image")
34 .default_value({1.0f, 1.0f, 1.0f, 1.0f})
35 .compositor_domain_priority(0);
36 b.add_output<decl::Color>("Image");
37}
38
40{
41 layout->prop(ptr, "filter_type", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE);
42}
43
45 public:
48 {
49 bNode &node = params.add_node("CompositorNodeFilter");
50 node.custom1 = filter_type;
51 params.update_and_connect_available_socket(node, "Image");
52 }
53};
54
56{
57 const eNodeSocketDatatype from_socket_type = eNodeSocketDatatype(params.other_socket().type);
58 if (!params.node_tree().typeinfo->validate_link(from_socket_type, SOCK_RGBA)) {
59 return;
60 }
61
63 params.add_item(IFACE_("Box Sharpen"), SocketSearchOp{CMP_NODE_FILTER_SHARP_BOX});
69 params.add_item(IFACE_("Diamond Sharpen"), SocketSearchOp{CMP_NODE_FILTER_SHARP_DIAMOND});
70}
71
72using namespace blender::compositor;
73
75 public:
77
78 void execute() override
79 {
80 const Result &input_image = this->get_input("Image");
81 if (input_image.is_single_value()) {
82 Result &output_image = this->get_result("Image");
83 output_image.share_data(input_image);
84 return;
85 }
86
87 if (this->context().use_gpu()) {
88 this->execute_gpu();
89 }
90 else {
91 this->execute_cpu();
92 }
93 }
94
96 {
97 GPUShader *shader = context().get_shader(get_shader_name());
98 GPU_shader_bind(shader);
99
101
102 const Result &input_image = get_input("Image");
103 input_image.bind_as_texture(shader, "input_tx");
104
105 const Result &factor = get_input("Fac");
106 factor.bind_as_texture(shader, "factor_tx");
107
108 const Domain domain = compute_domain();
109
110 Result &output_image = get_result("Image");
111 output_image.allocate_texture(domain);
112 output_image.bind_as_image(shader, "output_img");
113
115
116 input_image.unbind_as_texture();
117 factor.unbind_as_texture();
118 output_image.unbind_as_image();
120 }
121
122 const char *get_shader_name()
123 {
124 if (this->is_edge_filter()) {
125 return "compositor_edge_filter";
126 }
127 return "compositor_filter";
128 }
129
131 {
132 const float3x3 kernel = this->get_filter_kernel();
133
134 const Result &input = get_input("Image");
135 const Result &factor = get_input("Fac");
136
137 const Domain domain = compute_domain();
138 Result &output = get_result("Image");
139 output.allocate_texture(domain);
140
141 if (this->is_edge_filter()) {
142 parallel_for(domain.size, [&](const int2 texel) {
143 /* Compute the dot product between the 3x3 window around the pixel and the edge detection
144 * kernel in the X direction and Y direction. The Y direction kernel is computed by
145 * transposing the given X direction kernel. */
146 float3 color_x = float3(0.0f);
147 float3 color_y = float3(0.0f);
148 for (int j = 0; j < 3; j++) {
149 for (int i = 0; i < 3; i++) {
150 float3 color = input.load_pixel_extended<float4>(texel + int2(i - 1, j - 1)).xyz();
151 color_x += color * kernel[j][i];
152 color_y += color * kernel[i][j];
153 }
154 }
155
156 /* Compute the channel-wise magnitude of the 2D vector composed from the X and Y edge
157 * detection filter results. */
158 float3 magnitude = math::sqrt(color_x * color_x + color_y * color_y);
159
160 /* Mix the channel-wise magnitude with the original color at the center of the kernel using
161 * the input factor. */
162 float4 color = input.load_pixel<float4>(texel);
163 magnitude = math::interpolate(
164 color.xyz(), magnitude, factor.load_pixel<float, true>(texel));
165
166 /* Store the channel-wise magnitude with the original alpha of the input. */
167 output.store_pixel(texel, float4(magnitude, color.w));
168 });
169 }
170 else {
171 parallel_for(domain.size, [&](const int2 texel) {
172 /* Compute the dot product between the 3x3 window around the pixel and the kernel. */
173 float4 color = float4(0.0f);
174 for (int j = 0; j < 3; j++) {
175 for (int i = 0; i < 3; i++) {
176 color += input.load_pixel_extended<float4>(texel + int2(i - 1, j - 1)) * kernel[j][i];
177 }
178 }
179
180 /* Mix with the original color at the center of the kernel using the input factor. */
182 input.load_pixel<float4>(texel), color, factor.load_pixel<float, true>(texel));
183
184 /* Store the color making sure it is not negative. */
185 output.store_pixel(texel, math::max(color, float4(0.0f)));
186 });
187 }
188 }
189
191 {
192 switch (this->get_filter_method()) {
197 return true;
202 return false;
203 }
204 return false;
205 }
206
208 {
209 /* Initialize the kernels as arrays of rows with the top row first. Edge detection kernels
210 * return the kernel in the X direction, while the kernel in the Y direction will be computed
211 * inside the shader by transposing the kernel in the X direction. */
212 switch (get_filter_method()) {
214 const float kernel[3][3] = {{1.0f / 16.0f, 2.0f / 16.0f, 1.0f / 16.0f},
215 {2.0f / 16.0f, 4.0f / 16.0f, 2.0f / 16.0f},
216 {1.0f / 16.0f, 2.0f / 16.0f, 1.0f / 16.0f}};
217 return float3x3(kernel);
218 }
220 const float kernel[3][3] = {
221 {-1.0f, -1.0f, -1.0f}, {-1.0f, 9.0f, -1.0f}, {-1.0f, -1.0f, -1.0f}};
222 return float3x3(kernel);
223 }
225 const float kernel[3][3] = {{-1.0f / 8.0f, -1.0f / 8.0f, -1.0f / 8.0f},
226 {-1.0f / 8.0f, 1.0f, -1.0f / 8.0f},
227 {-1.0f / 8.0f, -1.0f / 8.0f, -1.0f / 8.0f}};
228 return float3x3(kernel);
229 }
231 const float kernel[3][3] = {{1.0f, 0.0f, -1.0f}, {2.0f, 0.0f, -2.0f}, {1.0f, 0.0f, -1.0f}};
232 return float3x3(kernel);
233 }
235 const float kernel[3][3] = {{1.0f, 0.0f, -1.0f}, {1.0f, 0.0f, -1.0f}, {1.0f, 0.0f, -1.0f}};
236 return float3x3(kernel);
237 }
239 const float kernel[3][3] = {
240 {5.0f, -3.0f, -2.0f}, {5.0f, -3.0f, -2.0f}, {5.0f, -3.0f, -2.0f}};
241 return float3x3(kernel);
242 }
244 const float kernel[3][3] = {{1.0f, 2.0f, 1.0f}, {0.0f, 1.0f, 0.0f}, {-1.0f, -2.0f, -1.0f}};
245 return float3x3(kernel);
246 }
248 const float kernel[3][3] = {
249 {0.0f, -1.0f, 0.0f}, {-1.0f, 5.0f, -1.0f}, {0.0f, -1.0f, 0.0f}};
250 return float3x3(kernel);
251 }
252 default: {
253 const float kernel[3][3] = {{0.0f, 0.0f, 0.0f}, {0.0f, 1.0f, 0.0f}, {0.0f, 0.0f, 0.0f}};
254 return float3x3(kernel);
255 }
256 }
257 }
258
260 {
261 return static_cast<CMPNodeFilterMethod>(bnode().custom1);
262 }
263};
264
266{
267 return new FilterOperation(context, node);
268}
269
270} // namespace blender::nodes::node_composite_filter_cc
271
273{
275
276 static blender::bke::bNodeType ntype;
277
278 cmp_node_type_base(&ntype, "CompositorNodeFilter", CMP_NODE_FILTER);
279 ntype.ui_name = "Filter";
280 ntype.ui_description = "Apply common image enhancement filters";
281 ntype.enum_name_legacy = "FILTER";
283 ntype.declare = file_ns::cmp_node_filter_declare;
284 ntype.draw_buttons = file_ns::node_composit_buts_filter;
286 ntype.flag |= NODE_PREVIEW;
287 ntype.get_compositor_operation = file_ns::get_compositor_operation;
288 ntype.gather_link_search_ops = file_ns::gather_link_searches;
289
291}
#define NODE_CLASS_OP_FILTER
Definition BKE_node.hh:437
#define CMP_NODE_FILTER
#define IFACE_(msgid)
@ NODE_PREVIEW
eNodeSocketDatatype
@ SOCK_RGBA
CMPNodeFilterMethod
@ CMP_NODE_FILTER_PREWITT
@ CMP_NODE_FILTER_SHARP_BOX
@ CMP_NODE_FILTER_KIRSCH
@ CMP_NODE_FILTER_LAPLACE
@ CMP_NODE_FILTER_SHARP_DIAMOND
@ CMP_NODE_FILTER_SOFT
@ CMP_NODE_FILTER_SOBEL
@ CMP_NODE_FILTER_SHADOW
void GPU_shader_bind(GPUShader *shader, const blender::gpu::shader::SpecializationConstants *constants_state=nullptr)
void GPU_shader_uniform_mat3_as_mat4(GPUShader *sh, const char *name, const float data[3][3])
void GPU_shader_unbind()
#define NOD_REGISTER_NODE(REGISTER_FUNC)
@ PROP_FACTOR
Definition RNA_types.hh:239
@ UI_ITEM_R_SPLIT_EMPTY_NAME
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
virtual Domain compute_domain()
Definition operation.cc:56
void share_data(const Result &source)
Definition result.cc:401
void allocate_texture(Domain domain, bool from_pool=true)
Definition result.cc:309
void unbind_as_texture() const
Definition result.cc:389
void bind_as_texture(GPUShader *shader, const char *texture_name) const
Definition result.cc:365
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 input
MatBase< 3, 3 > float3x3
#define output
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
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 sqrt(const T &a)
T interpolate(const T &a, const T &b, const FactorT &t)
T max(const T &a, const T &b)
static NodeOperation * get_compositor_operation(Context &context, DNode node)
static void gather_link_searches(GatherLinkSearchOpParams &params)
static void node_composit_buts_filter(uiLayout *layout, bContext *, PointerRNA *ptr)
static void cmp_node_filter_declare(NodeDeclarationBuilder &b)
VecBase< float, 4 > float4
VecBase< int32_t, 2 > int2
MatBase< float, 3, 3 > float3x3
VecBase< float, 3 > float3
static void register_node_type_cmp_filter()
void cmp_node_type_base(blender::bke::bNodeType *ntype, std::string idname, const std::optional< int16_t > legacy_type)
void node_filter_label(const bNodeTree *, const bNode *node, char *label, int label_maxncpy)
Definition node_util.cc:227
int16_t custom1
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
void(* labelfunc)(const bNodeTree *ntree, const bNode *node, char *label, int label_maxncpy)
Definition BKE_node.hh:258
const char * enum_name_legacy
Definition BKE_node.hh:235
void(* draw_buttons)(uiLayout *, bContext *C, PointerRNA *ptr)
Definition BKE_node.hh:247
NodeGatherSocketLinkOperationsFunction gather_link_search_ops
Definition BKE_node.hh:371
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)
PointerRNA * ptr
Definition wm_files.cc:4226