Blender V4.3
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
9#include "BLI_math_base.h"
11
12#include "DNA_node_types.h"
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
22#include "COM_node_operation.hh"
23#include "COM_utilities.hh"
24
26
27/* **************** Crop ******************** */
28
30
32
34{
35 b.add_input<decl::Color>("Image")
36 .default_value({1.0f, 1.0f, 1.0f, 1.0f})
37 .compositor_domain_priority(0);
38 b.add_output<decl::Color>("Image");
39}
40
41static void node_composit_init_crop(bNodeTree * /*ntree*/, bNode *node)
42{
43 NodeTwoXYs *nxy = MEM_cnew<NodeTwoXYs>(__func__);
44 node->storage = nxy;
45 nxy->x1 = 0;
46 nxy->x2 = 0;
47 nxy->y1 = 0;
48 nxy->y2 = 0;
49}
50
52{
54
55 uiItemR(layout, ptr, "use_crop_size", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE);
56 uiItemR(layout, ptr, "relative", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE);
57
58 col = uiLayoutColumn(layout, true);
59 if (RNA_boolean_get(ptr, "relative")) {
60 uiItemR(col, ptr, "rel_min_x", UI_ITEM_R_SPLIT_EMPTY_NAME, IFACE_("Left"), ICON_NONE);
61 uiItemR(col, ptr, "rel_max_x", UI_ITEM_R_SPLIT_EMPTY_NAME, IFACE_("Right"), ICON_NONE);
62 uiItemR(col, ptr, "rel_min_y", UI_ITEM_R_SPLIT_EMPTY_NAME, IFACE_("Up"), ICON_NONE);
63 uiItemR(col, ptr, "rel_max_y", UI_ITEM_R_SPLIT_EMPTY_NAME, IFACE_("Down"), ICON_NONE);
64 }
65 else {
66 uiItemR(col, ptr, "min_x", UI_ITEM_R_SPLIT_EMPTY_NAME, IFACE_("Left"), ICON_NONE);
67 uiItemR(col, ptr, "max_x", UI_ITEM_R_SPLIT_EMPTY_NAME, IFACE_("Right"), ICON_NONE);
68 uiItemR(col, ptr, "min_y", UI_ITEM_R_SPLIT_EMPTY_NAME, IFACE_("Up"), ICON_NONE);
69 uiItemR(col, ptr, "max_y", UI_ITEM_R_SPLIT_EMPTY_NAME, IFACE_("Down"), ICON_NONE);
70 }
71}
72
73using namespace blender::realtime_compositor;
74
76 public:
78
79 void execute() override
80 {
81 /* The operation does nothing, so just pass the input through. */
82 if (is_identity()) {
83 get_input("Image").pass_through(get_result("Image"));
84 return;
85 }
86
87 if (get_is_image_crop()) {
89 }
90 else {
92 }
93 }
94
95 /* Crop by replacing areas outside of the cropping bounds with zero alpha. The output have the
96 * same domain as the input image. */
98 {
99 GPUShader *shader = context().get_shader("compositor_alpha_crop");
100 GPU_shader_bind(shader);
101
102 int2 lower_bound, upper_bound;
103 compute_cropping_bounds(lower_bound, upper_bound);
104 GPU_shader_uniform_2iv(shader, "lower_bound", lower_bound);
105 GPU_shader_uniform_2iv(shader, "upper_bound", upper_bound);
106
107 const Result &input_image = get_input("Image");
108 input_image.bind_as_texture(shader, "input_tx");
109
110 const Domain domain = compute_domain();
111
112 Result &output_image = get_result("Image");
113 output_image.allocate_texture(domain);
114 output_image.bind_as_image(shader, "output_img");
115
116 compute_dispatch_threads_at_least(shader, domain.size);
117
118 input_image.unbind_as_texture();
119 output_image.unbind_as_image();
121 }
122
123 /* Crop the image into a new size that matches the cropping bounds. */
125 {
126 int2 lower_bound, upper_bound;
127 compute_cropping_bounds(lower_bound, upper_bound);
128
129 /* The image is cropped into nothing, so just return a single zero value. */
130 if (lower_bound.x == upper_bound.x || lower_bound.y == upper_bound.y) {
131 Result &result = get_result("Image");
132 result.allocate_invalid();
133 return;
134 }
135
136 GPUShader *shader = context().get_shader("compositor_image_crop");
137 GPU_shader_bind(shader);
138
139 GPU_shader_uniform_2iv(shader, "lower_bound", lower_bound);
140
141 const Result &input_image = get_input("Image");
142 input_image.bind_as_texture(shader, "input_tx");
143
144 const int2 size = upper_bound - lower_bound;
145
146 Result &output_image = get_result("Image");
147 output_image.allocate_texture(Domain(size, compute_domain().transformation));
148 output_image.bind_as_image(shader, "output_img");
149
151
152 input_image.unbind_as_texture();
153 output_image.unbind_as_image();
155 }
156
157 /* If true, the image should actually be cropped into a new size. Otherwise, if false, the region
158 * outside of the cropping bounds will be set to a zero alpha value. */
160 {
161 return bnode().custom1;
162 }
163
165 {
166 return bnode().custom2;
167 }
168
169 /* Returns true if the operation does nothing and the input can be passed through. */
171 {
172 const Result &input = get_input("Image");
173 /* Single value inputs can't be cropped and are returned as is. */
174 if (input.is_single_value()) {
175 return true;
176 }
177
178 int2 lower_bound, upper_bound;
179 compute_cropping_bounds(lower_bound, upper_bound);
180 const int2 input_size = input.domain().size;
181 /* The cropping bounds cover the whole image, so no cropping happens. */
182 if (lower_bound == int2(0) && upper_bound == input_size) {
183 return true;
184 }
185
186 return false;
187 }
188
189 void compute_cropping_bounds(int2 &lower_bound, int2 &upper_bound)
190 {
191 const NodeTwoXYs &node_two_xys = node_storage(bnode());
192 const int2 input_size = get_input("Image").domain().size;
193
194 if (get_is_relative()) {
195 /* The cropping bounds are relative to the image size. The factors are in the [0, 1] range,
196 * so it is guaranteed that they won't go over the input image size. */
197 lower_bound.x = input_size.x * node_two_xys.fac_x1;
198 lower_bound.y = input_size.y * node_two_xys.fac_y2;
199 upper_bound.x = input_size.x * node_two_xys.fac_x2;
200 upper_bound.y = input_size.y * node_two_xys.fac_y1;
201 }
202 else {
203 /* Make sure the bounds don't go over the input image size. */
204 lower_bound.x = min_ii(node_two_xys.x1, input_size.x);
205 lower_bound.y = min_ii(node_two_xys.y2, input_size.y);
206 upper_bound.x = min_ii(node_two_xys.x2, input_size.x);
207 upper_bound.y = min_ii(node_two_xys.y1, input_size.y);
208 }
209
210 /* Make sure upper bound is actually higher than the lower bound. */
211 lower_bound.x = min_ii(lower_bound.x, upper_bound.x);
212 lower_bound.y = min_ii(lower_bound.y, upper_bound.y);
213 upper_bound.x = max_ii(lower_bound.x, upper_bound.x);
214 upper_bound.y = max_ii(lower_bound.y, upper_bound.y);
215 }
216};
217
219{
220 return new CropOperation(context, node);
221}
222
223} // namespace blender::nodes::node_composite_crop_cc
224
226{
227 namespace file_ns = blender::nodes::node_composite_crop_cc;
228
229 static blender::bke::bNodeType ntype;
230
231 cmp_node_type_base(&ntype, CMP_NODE_CROP, "Crop", NODE_CLASS_DISTORT);
232 ntype.declare = file_ns::cmp_node_crop_declare;
233 ntype.draw_buttons = file_ns::node_composit_buts_crop;
234 ntype.initfunc = file_ns::node_composit_init_crop;
237 ntype.get_compositor_operation = file_ns::get_compositor_operation;
238
240}
#define NODE_STORAGE_FUNCS(StorageT)
Definition BKE_node.hh:1799
#define NODE_CLASS_DISTORT
Definition BKE_node.hh:412
MINLINE int min_ii(int a, int b)
MINLINE int max_ii(int a, int b)
#define IFACE_(msgid)
void GPU_shader_uniform_2iv(GPUShader *sh, const char *name, const int data[2])
void GPU_shader_bind(GPUShader *shader)
void GPU_shader_unbind()
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
struct GPUShader GPUShader
void compute_cropping_bounds(int2 &lower_bound, int2 &upper_bound)
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
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
static void node_composit_init_crop(bNodeTree *, bNode *node)
static void cmp_node_crop_declare(NodeDeclarationBuilder &b)
static NodeOperation * get_compositor_operation(Context &context, DNode node)
static void node_composit_buts_crop(uiLayout *layout, bContext *, PointerRNA *ptr)
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
void register_node_type_cmp_crop()
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)
int16_t custom1
int16_t custom2
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