Blender V4.5
node_composite_crop.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 "BLI_bounds.hh"
10#include "BLI_bounds_types.hh"
12
13#include "DNA_node_types.h"
14
15#include "BKE_node.hh"
16
17#include "GPU_shader.hh"
18
19#include "COM_node_operation.hh"
20#include "COM_utilities.hh"
21
23
24/* **************** Crop ******************** */
25
27
29{
30 b.add_input<decl::Color>("Image").default_value({1.0f, 1.0f, 1.0f, 1.0f});
31 b.add_input<decl::Int>("X")
32 .default_value(0)
33 .min(0)
35 .description("The X position of the lower left corner of the crop region");
36 b.add_input<decl::Int>("Y")
37 .default_value(0)
38 .min(0)
40 .description("The Y position of the lower left corner of the crop region");
41 b.add_input<decl::Int>("Width")
42 .default_value(1920)
43 .min(1)
45 .description("The width of the crop region");
46 b.add_input<decl::Int>("Height")
47 .default_value(1080)
48 .min(1)
50 .description("The height of the crop region");
51 b.add_input<decl::Bool>("Alpha Crop")
52 .default_value(false)
54 .description(
55 "Sets the areas outside of the crop region to be transparent instead of actually "
56 "cropping the size of the image");
57
58 b.add_output<decl::Color>("Image");
59}
60
61using namespace blender::compositor;
62
64 public:
66
67 void execute() override
68 {
69 if (this->is_identity()) {
70 const Result &input = this->get_input("Image");
71 Result &output = this->get_result("Image");
72 output.share_data(input);
73 return;
74 }
75
76 if (this->is_alpha_crop()) {
77 this->execute_alpha_crop();
78 }
79 else {
80 this->execute_image_crop();
81 }
82 }
83
84 /* Crop by replacing areas outside of the cropping bounds with zero alpha. The output have the
85 * same domain as the input image. */
87 {
88 if (this->context().use_gpu()) {
90 }
91 else {
93 }
94 }
95
97 {
98 GPUShader *shader = this->context().get_shader("compositor_alpha_crop");
99 GPU_shader_bind(shader);
100
102 GPU_shader_uniform_2iv(shader, "lower_bound", bounds.min);
103 GPU_shader_uniform_2iv(shader, "upper_bound", bounds.max);
104
105 const Result &input_image = this->get_input("Image");
106 input_image.bind_as_texture(shader, "input_tx");
107
108 const Domain domain = this->compute_domain();
109
110 Result &output_image = this->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 output_image.unbind_as_image();
119 }
120
122 {
124
125 const Result &input = this->get_input("Image");
126
127 const Domain domain = this->compute_domain();
128 Result &output = this->get_result("Image");
129 output.allocate_texture(domain);
130
131 parallel_for(domain.size, [&](const int2 texel) {
132 /* The lower bound is inclusive and upper bound is exclusive. */
133 bool is_inside = texel.x >= bounds.min.x && texel.y >= bounds.min.y &&
134 texel.x < bounds.max.x && texel.y < bounds.max.y;
135 /* Write the pixel color if it is inside the cropping region, otherwise, write zero. */
136 float4 color = is_inside ? input.load_pixel<float4>(texel) : float4(0.0f);
137 output.store_pixel(texel, color);
138 });
139 }
140
141 /* Crop the image into a new size that matches the cropping bounds. */
143 {
144 if (this->context().use_gpu()) {
146 }
147 else {
149 }
150 }
151
153 {
155
156 GPUShader *shader = this->context().get_shader("compositor_image_crop");
157 GPU_shader_bind(shader);
158
159 GPU_shader_uniform_2iv(shader, "lower_bound", bounds.min);
160
161 const Result &input_image = this->get_input("Image");
162 input_image.bind_as_texture(shader, "input_tx");
163
164 const int2 size = bounds.size();
165
166 Result &output_image = this->get_result("Image");
167 output_image.allocate_texture(Domain(size, this->compute_domain().transformation));
168 output_image.bind_as_image(shader, "output_img");
169
171
172 input_image.unbind_as_texture();
173 output_image.unbind_as_image();
175 }
176
178 {
180
181 const Result &input = this->get_input("Image");
182
183 const int2 size = bounds.size();
184 Result &output = this->get_result("Image");
185 output.allocate_texture(Domain(size, this->compute_domain().transformation));
186
187 parallel_for(size, [&](const int2 texel) {
188 output.store_pixel(texel, input.load_pixel<float4>(texel + bounds.min));
189 });
190 }
191
192 /* Returns true if the operation does nothing and the input can be passed through. */
194 {
195 const Result &input = this->get_input("Image");
196 /* Single value inputs can't be cropped and are returned as is. */
197 if (input.is_single_value()) {
198 return true;
199 }
200
202 const int2 input_size = input.domain().size;
203 /* The cropping bounds cover the whole image, so no cropping happens. */
204 if (bounds.min == int2(0) && bounds.max == input_size) {
205 return true;
206 }
207
208 return false;
209 }
210
212 {
213 const int2 input_size = this->get_input("Image").domain().size;
214
215 const int x = math::clamp(
216 this->get_input("X").get_single_value_default(0), 0, input_size.x - 1);
217 const int y = math::clamp(
218 this->get_input("Y").get_single_value_default(0), 0, input_size.y - 1);
219 const int width = math::max(1, this->get_input("Width").get_single_value_default(100));
220 const int height = math::max(1, this->get_input("Height").get_single_value_default(100));
221
222 const Bounds<int2> input_bounds = Bounds<int2>(int2(0), input_size);
223
224 const Bounds<int2> crop_bounds = Bounds<int2>(int2(x, y), int2(x + width, y + height));
225 return *bounds::intersect(crop_bounds, input_bounds);
226 }
227
228 /* If true, the region outside of the cropping bounds will be set to a zero alpha value instead
229 * of actually cropping the size of the image. */
231 {
232 return this->get_input("Alpha Crop").get_single_value_default(false);
233 }
234};
235
237{
238 return new CropOperation(context, node);
239}
240
241} // namespace blender::nodes::node_composite_crop_cc
242
244{
245 namespace file_ns = blender::nodes::node_composite_crop_cc;
246
247 static blender::bke::bNodeType ntype;
248
249 cmp_node_type_base(&ntype, "CompositorNodeCrop", CMP_NODE_CROP);
250 ntype.ui_name = "Crop";
251 ntype.ui_description =
252 "Crops image to a smaller region, either making the cropped area transparent or resizing "
253 "the image";
254 ntype.enum_name_legacy = "CROP";
256 ntype.declare = file_ns::cmp_node_crop_declare;
257 ntype.get_compositor_operation = file_ns::get_compositor_operation;
258
260}
#define NODE_CLASS_DISTORT
Definition BKE_node.hh:441
#define CMP_NODE_CROP
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)
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
virtual Domain compute_domain()
Definition operation.cc:56
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 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
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:625
#define input
#define output
void node_register_type(bNodeType &ntype)
Definition node.cc:2748
std::optional< Bounds< T > > intersect(const Bounds< T > &a, const Bounds< T > &b)
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 clamp(const T &a, const T &min, const T &max)
T max(const T &a, const T &b)
static void cmp_node_crop_declare(NodeDeclarationBuilder &b)
static NodeOperation * get_compositor_operation(Context &context, DNode node)
VecBase< float, 4 > float4
VecBase< int32_t, 2 > int2
static void register_node_type_cmp_crop()
void cmp_node_type_base(blender::bke::bNodeType *ntype, std::string idname, const std::optional< int16_t > legacy_type)
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
NodeDeclareFunction declare
Definition BKE_node.hh:355