Blender V4.3
node_composite_scale.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"
11#include "BLI_math_base.hh"
12#include "BLI_math_matrix.hh"
15#include "BLI_string.h"
16
17#include "RNA_access.hh"
18
19#include "UI_interface.hh"
20#include "UI_resources.hh"
21
22#include "GPU_shader.hh"
23#include "GPU_texture.hh"
24
26#include "COM_node_operation.hh"
27#include "COM_utilities.hh"
28
30
31/* **************** Scale ******************** */
32
34
36{
37 b.add_input<decl::Color>("Image")
38 .default_value({1.0f, 1.0f, 1.0f, 1.0f})
39 .compositor_domain_priority(0);
40 b.add_input<decl::Float>("X")
41 .default_value(1.0f)
42 .min(0.0001f)
43 .max(CMP_SCALE_MAX)
45 b.add_input<decl::Float>("Y")
46 .default_value(1.0f)
47 .min(0.0001f)
48 .max(CMP_SCALE_MAX)
50 b.add_output<decl::Color>("Image");
51}
52
54{
55 bool use_xy_scale = ELEM(node->custom1, CMP_NODE_SCALE_RELATIVE, CMP_NODE_SCALE_ABSOLUTE);
56
57 /* Only show X/Y scale factor inputs for modes using them! */
58 LISTBASE_FOREACH (bNodeSocket *, sock, &node->inputs) {
59 if (STR_ELEM(sock->name, "X", "Y")) {
60 bke::node_set_socket_availability(ntree, sock, use_xy_scale);
61 }
62 }
63}
64
66{
67 uiItemR(layout, ptr, "space", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE);
68
70 uiLayout *row;
71 uiItemR(layout,
72 ptr,
73 "frame_method",
75 nullptr,
76 ICON_NONE);
77 row = uiLayoutRow(layout, true);
78 uiItemR(row, ptr, "offset_x", UI_ITEM_R_SPLIT_EMPTY_NAME, "X", ICON_NONE);
79 uiItemR(row, ptr, "offset_y", UI_ITEM_R_SPLIT_EMPTY_NAME, "Y", ICON_NONE);
80 }
81}
82
83using namespace blender::realtime_compositor;
84
86 public:
88
89 void execute() override
90 {
91 if (is_variable_size()) {
93 }
94 else {
96 }
97 }
98
100 {
101 Result &input = get_input("Image");
102 Result &output = get_result("Image");
103
104 const float2 scale = get_scale();
105 const math::AngleRadian rotation = 0.0f;
106 const float2 translation = get_translation();
107 const float3x3 transformation = math::from_loc_rot_scale<float3x3>(
108 translation, rotation, scale);
109
110 transform(context(), input, output, transformation, input.get_realization_options());
111 }
112
114 {
115 GPUShader *shader = context().get_shader("compositor_scale_variable");
116 GPU_shader_bind(shader);
117
118 Result &input = get_input("Image");
119 GPU_texture_filter_mode(input, true);
121 input.bind_as_texture(shader, "input_tx");
122
123 Result &x_scale = get_input("X");
124 x_scale.bind_as_texture(shader, "x_scale_tx");
125
126 Result &y_scale = get_input("Y");
127 y_scale.bind_as_texture(shader, "y_scale_tx");
128
129 Result &output = get_result("Image");
130 const Domain domain = compute_domain();
131 output.allocate_texture(domain);
132 output.bind_as_image(shader, "output_img");
133
134 compute_dispatch_threads_at_least(shader, domain.size);
135
136 input.unbind_as_texture();
137 x_scale.unbind_as_texture();
138 y_scale.unbind_as_texture();
139 output.unbind_as_image();
141 }
142
144 {
145 switch (get_scale_method()) {
147 return get_scale_relative();
149 return get_scale_absolute();
153 return get_scale_render_size();
154 default:
156 return float2(1.0f);
157 }
158 }
159
160 /* Scale by the input factors. */
162 {
163 return float2(get_input("X").get_float_value_default(1.0f),
164 get_input("Y").get_float_value_default(1.0f));
165 }
166
167 /* Scale such that the new size matches the input absolute size. */
169 {
170 const float2 input_size = float2(get_input("Image").domain().size);
171 const float2 absolute_size = float2(get_input("X").get_float_value_default(1.0f),
172 get_input("Y").get_float_value_default(1.0f));
173 return absolute_size / input_size;
174 }
175
176 /* Scale by the render resolution percentage. */
178 {
179 return float2(context().get_render_percentage());
180 }
181
183 {
184 if (!context().is_valid_compositing_region()) {
185 return float2(1.0f);
186 }
187
195 default:
197 return float2(1.0f);
198 }
199 }
200
201 /* Scale such that the new size matches the render size. Since the input is freely scaled, it is
202 * potentially stretched, hence the name. */
204 {
205 const float2 input_size = float2(get_input("Image").domain().size);
206 const float2 render_size = float2(context().get_compositing_region_size());
207 return render_size / input_size;
208 }
209
210 /* Scale such that the dimension with the smaller scaling factor matches that of the render size
211 * while maintaining the input's aspect ratio. Since the other dimension is guaranteed not to
212 * exceed the render size region due to its larger scaling factor, the image is said to be fit
213 * inside that region, hence the name. */
215 {
216 const float2 input_size = float2(get_input("Image").domain().size);
217 const float2 render_size = float2(context().get_compositing_region_size());
218 const float2 scale = render_size / input_size;
219 return float2(math::min(scale.x, scale.y));
220 }
221
222 /* Scale such that the dimension with the larger scaling factor matches that of the render size
223 * while maintaining the input's aspect ratio. Since the other dimension is guaranteed to exceed
224 * the render size region due to its lower scaling factor, the image will be cropped inside that
225 * region, hence the name. */
227 {
228 const float2 input_size = float2(get_input("Image").domain().size);
229 const float2 render_size = float2(context().get_compositing_region_size());
230 const float2 scale = render_size / input_size;
231 return float2(math::max(scale.x, scale.y));
232 }
233
235 {
236 /* Only the render size option supports offset translation. */
238 return float2(0.0f);
239 }
240
241 /* Translate by the offset factor relative to the new size. */
242 const float2 input_size = float2(get_input("Image").domain().size);
243 return get_offset() * input_size * get_scale();
244 }
245
247 {
248 /* Only relative scaling can be variable. */
250 return false;
251 }
252
253 return !get_input("X").is_single_value() || !get_input("Y").is_single_value();
254 }
255
260
265
267 {
268 return float2(bnode().custom3, bnode().custom4);
269 }
270};
271
273{
274 return new ScaleOperation(context, node);
275}
276
277} // namespace blender::nodes::node_composite_scale_cc
278
280{
281 namespace file_ns = blender::nodes::node_composite_scale_cc;
282
283 static blender::bke::bNodeType ntype;
284
285 cmp_node_type_base(&ntype, CMP_NODE_SCALE, "Scale", NODE_CLASS_DISTORT);
286 ntype.declare = file_ns::cmp_node_scale_declare;
287 ntype.draw_buttons = file_ns::node_composit_buts_scale;
288 ntype.updatefunc = file_ns::node_composite_update_scale;
289 ntype.get_compositor_operation = file_ns::get_compositor_operation;
290
292}
#define NODE_CLASS_DISTORT
Definition BKE_node.hh:412
#define BLI_assert_unreachable()
Definition BLI_assert.h:97
#define LISTBASE_FOREACH(type, var, list)
#define STR_ELEM(...)
Definition BLI_string.h:653
#define ELEM(...)
CMPNodeScaleRenderSizeMethod
@ CMP_NODE_SCALE_RENDER_SIZE_STRETCH
@ CMP_NODE_SCALE_RENDER_SIZE_FIT
@ CMP_NODE_SCALE_RENDER_SIZE_CROP
CMPNodeScaleMethod
@ CMP_NODE_SCALE_RENDER_SIZE
@ CMP_NODE_SCALE_RELATIVE
@ CMP_NODE_SCALE_ABSOLUTE
@ CMP_NODE_SCALE_RENDER_PERCENT
void GPU_shader_bind(GPUShader *shader)
void GPU_shader_unbind()
void GPU_texture_extend_mode(GPUTexture *texture, GPUSamplerExtendMode extend_mode)
@ GPU_SAMPLER_EXTEND_MODE_CLAMP_TO_BORDER
void GPU_texture_filter_mode(GPUTexture *texture, bool use_filter)
uiLayout * uiLayoutRow(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
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_texture(GPUShader *shader, const char *texture_name) const
Definition result.cc:253
local_group_size(16, 16) .push_constant(Type b
void node_set_socket_availability(bNodeTree *ntree, bNodeSocket *sock, bool is_available)
Definition node.cc:3911
void node_register_type(bNodeType *ntype)
Definition node.cc:1708
T min(const T &a, const T &b)
T max(const T &a, const T &b)
MatT from_loc_rot_scale(const typename MatT::loc_type &location, const RotationT &rotation, const VecBase< typename MatT::base_type, ScaleDim > &scale)
static void cmp_node_scale_declare(NodeDeclarationBuilder &b)
static void node_composite_update_scale(bNodeTree *ntree, bNode *node)
static void node_composit_buts_scale(uiLayout *layout, bContext *, PointerRNA *ptr)
static NodeOperation * get_compositor_operation(Context &context, DNode node)
void transform(Context &context, Result &input, Result &output, const float3x3 &transformation, RealizationOptions realization_options)
void compute_dispatch_threads_at_least(GPUShader *shader, int2 threads_range, int2 local_size=int2(16))
Definition utilities.cc:131
VecBase< float, 2 > float2
void register_node_type_cmp_scale()
void cmp_node_type_base(blender::bke::bNodeType *ntype, int type, const char *name, short nclass)
#define CMP_SCALE_MAX
int RNA_enum_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(* draw_buttons)(uiLayout *, bContext *C, PointerRNA *ptr)
Definition BKE_node.hh:238
NodeDeclareFunction declare
Definition BKE_node.hh:347
void(* updatefunc)(bNodeTree *ntree, bNode *node)
Definition BKE_node.hh:257
PointerRNA * ptr
Definition wm_files.cc:4126