1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
|
/* SPDX-License-Identifier: GPL-2.0-or-later
* Copyright 2006 Blender Foundation. All rights reserved. */
/** \file
* \ingroup cmpnodes
*/
#include "BLI_assert.h"
#include "BLI_float3x3.hh"
#include "BLI_math_base.hh"
#include "BLI_math_vec_types.hh"
#include "RNA_access.h"
#include "UI_interface.h"
#include "UI_resources.h"
#include "COM_node_operation.hh"
#include "node_composite_util.hh"
/* **************** Scale ******************** */
namespace blender::nodes::node_composite_scale_cc {
static void cmp_node_scale_declare(NodeDeclarationBuilder &b)
{
b.add_input<decl::Color>(N_("Image"))
.default_value({1.0f, 1.0f, 1.0f, 1.0f})
.compositor_domain_priority(0);
b.add_input<decl::Float>(N_("X"))
.default_value(1.0f)
.min(0.0001f)
.max(CMP_SCALE_MAX)
.compositor_expects_single_value();
b.add_input<decl::Float>(N_("Y"))
.default_value(1.0f)
.min(0.0001f)
.max(CMP_SCALE_MAX)
.compositor_expects_single_value();
b.add_output<decl::Color>(N_("Image"));
}
static void node_composite_update_scale(bNodeTree *ntree, bNode *node)
{
bNodeSocket *sock;
bool use_xy_scale = ELEM(node->custom1, CMP_NODE_SCALE_RELATIVE, CMP_NODE_SCALE_ABSOLUTE);
/* Only show X/Y scale factor inputs for modes using them! */
for (sock = (bNodeSocket *)node->inputs.first; sock; sock = sock->next) {
if (STR_ELEM(sock->name, "X", "Y")) {
nodeSetSocketAvailability(ntree, sock, use_xy_scale);
}
}
}
static void node_composit_buts_scale(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr)
{
uiItemR(layout, ptr, "space", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE);
if (RNA_enum_get(ptr, "space") == CMP_NODE_SCALE_RENDER_SIZE) {
uiLayout *row;
uiItemR(layout,
ptr,
"frame_method",
UI_ITEM_R_SPLIT_EMPTY_NAME | UI_ITEM_R_EXPAND,
nullptr,
ICON_NONE);
row = uiLayoutRow(layout, true);
uiItemR(row, ptr, "offset_x", UI_ITEM_R_SPLIT_EMPTY_NAME, "X", ICON_NONE);
uiItemR(row, ptr, "offset_y", UI_ITEM_R_SPLIT_EMPTY_NAME, "Y", ICON_NONE);
}
}
using namespace blender::realtime_compositor;
class ScaleOperation : public NodeOperation {
public:
using NodeOperation::NodeOperation;
void execute() override
{
Result &input = get_input("Image");
Result &result = get_result("Image");
input.pass_through(result);
const float3x3 transformation = float3x3::from_translation_rotation_scale(
get_translation(), 0.0f, get_scale());
result.transform(transformation);
result.get_realization_options().interpolation = Interpolation::Bilinear;
}
float2 get_scale()
{
switch (get_scale_method()) {
case CMP_NODE_SCALE_RELATIVE:
return get_scale_relative();
case CMP_NODE_SCALE_ABSOLUTE:
return get_scale_absolute();
case CMP_NODE_SCALE_RENDER_PERCENT:
return get_scale_render_percent();
case CMP_NODE_SCALE_RENDER_SIZE:
return get_scale_render_size();
default:
BLI_assert_unreachable();
return float2(1.0f);
}
}
/* Scale by the input factors. */
float2 get_scale_relative()
{
return float2(get_input("X").get_float_value_default(1.0f),
get_input("Y").get_float_value_default(1.0f));
}
/* Scale such that the new size matches the input absolute size. */
float2 get_scale_absolute()
{
const float2 input_size = float2(get_input("Image").domain().size);
const float2 absolute_size = float2(get_input("X").get_float_value_default(1.0f),
get_input("Y").get_float_value_default(1.0f));
return absolute_size / input_size;
}
/* Scale by the render resolution percentage. */
float2 get_scale_render_percent()
{
return float2(context().get_scene()->r.size / 100.0f);
}
float2 get_scale_render_size()
{
switch (get_scale_render_size_method()) {
case CMP_NODE_SCALE_RENDER_SIZE_STRETCH:
return get_scale_render_size_stretch();
case CMP_NODE_SCALE_RENDER_SIZE_FIT:
return get_scale_render_size_fit();
case CMP_NODE_SCALE_RENDER_SIZE_CROP:
return get_scale_render_size_crop();
default:
BLI_assert_unreachable();
return float2(1.0f);
}
}
/* Scale such that the new size matches the render size. Since the input is freely scaled, it is
* potentially stretched, hence the name. */
float2 get_scale_render_size_stretch()
{
const float2 input_size = float2(get_input("Image").domain().size);
const float2 render_size = float2(context().get_output_size());
return render_size / input_size;
}
/* Scale such that the dimension with the smaller scaling factor matches that of the render size
* while maintaining the input's aspect ratio. Since the other dimension is guaranteed not to
* exceed the render size region due to its larger scaling factor, the image is said to be fit
* inside that region, hence the name. */
float2 get_scale_render_size_fit()
{
const float2 input_size = float2(get_input("Image").domain().size);
const float2 render_size = float2(context().get_output_size());
const float2 scale = render_size / input_size;
return float2(math::min(scale.x, scale.y));
}
/* Scale such that the dimension with the larger scaling factor matches that of the render size
* while maintaining the input's aspect ratio. Since the other dimension is guaranteed to exceed
* the render size region due to its lower scaling factor, the image will be cropped inside that
* region, hence the name. */
float2 get_scale_render_size_crop()
{
const float2 input_size = float2(get_input("Image").domain().size);
const float2 render_size = float2(context().get_output_size());
const float2 scale = render_size / input_size;
return float2(math::max(scale.x, scale.y));
}
float2 get_translation()
{
/* Only the render size option supports offset translation. */
if (get_scale_method() != CMP_NODE_SCALE_RENDER_SIZE) {
return float2(0.0f);
}
/* Translate by the offset factor relative to the new size. */
const float2 input_size = float2(get_input("Image").domain().size);
return get_offset() * input_size * get_scale();
}
CMPNodeScaleMethod get_scale_method()
{
return (CMPNodeScaleMethod)bnode().custom1;
}
CMPNodeScaleRenderSizeMethod get_scale_render_size_method()
{
return (CMPNodeScaleRenderSizeMethod)bnode().custom2;
}
float2 get_offset()
{
return float2(bnode().custom3, bnode().custom4);
}
};
static NodeOperation *get_compositor_operation(Context &context, DNode node)
{
return new ScaleOperation(context, node);
}
} // namespace blender::nodes::node_composite_scale_cc
void register_node_type_cmp_scale()
{
namespace file_ns = blender::nodes::node_composite_scale_cc;
static bNodeType ntype;
cmp_node_type_base(&ntype, CMP_NODE_SCALE, "Scale", NODE_CLASS_DISTORT);
ntype.declare = file_ns::cmp_node_scale_declare;
ntype.draw_buttons = file_ns::node_composit_buts_scale;
node_type_update(&ntype, file_ns::node_composite_update_scale);
ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
|