Blender V4.3
COM_ScaleOperation.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2011 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
7
8namespace blender::compositor {
9
10#define USE_FORCE_BILINEAR
11/* XXX(@ideasman42): ignore input and use default from old compositor,
12 * could become an option like the transform node.
13 *
14 * NOTE: use bilinear because bicubic makes fuzzy even when not scaling at all (1:1)
15 */
16
18{
19#ifdef USE_FORCE_BILINEAR
21#else
22 sampler_ = -1;
23#endif
24 variable_size_ = false;
25}
26
28
37
38float ScaleOperation::get_constant_scale(const int input_op_idx, const float factor)
39{
40 const bool is_constant = get_input_operation(input_op_idx)->get_flags().is_constant_operation;
41 if (is_constant) {
42 return ((ConstantOperation *)get_input_operation(input_op_idx))->get_constant_elem()[0] *
43 factor;
44 }
45
46 return 1.0f;
47}
48
49float ScaleOperation::get_constant_scale_x(const float width)
50{
51 return get_constant_scale(X_INPUT_INDEX, get_relative_scale_x_factor(width));
52}
53
54float ScaleOperation::get_constant_scale_y(const float height)
55{
56 return get_constant_scale(Y_INPUT_INDEX, get_relative_scale_y_factor(height));
57}
58
59bool ScaleOperation::is_scaling_variable()
60{
63}
64
65void ScaleOperation::scale_area(rcti &area, float relative_scale_x, float relative_scale_y)
66{
67 const rcti src_area = area;
68 const float center_x = BLI_rcti_size_x(&area) / 2.0f;
69 const float center_y = BLI_rcti_size_y(&area) / 2.0f;
70 area.xmin = floorf(scale_coord(area.xmin, center_x, relative_scale_x));
71 area.xmax = ceilf(scale_coord(area.xmax, center_x, relative_scale_x));
72 area.ymin = floorf(scale_coord(area.ymin, center_y, relative_scale_y));
73 area.ymax = ceilf(scale_coord(area.ymax, center_y, relative_scale_y));
74
75 float scale_offset_x, scale_offset_y;
76 ScaleOperation::get_scale_offset(src_area, area, scale_offset_x, scale_offset_y);
77 BLI_rcti_translate(&area, -scale_offset_x, -scale_offset_y);
78}
79
81{
82
83 if (BLI_rcti_size_x(&area) > max_size.x) {
84 area.xmax = area.xmin + max_size.x;
85 }
86 if (BLI_rcti_size_y(&area) > max_size.y) {
87 area.ymax = area.ymin + max_size.y;
88 }
89}
90
96
97void ScaleOperation::get_scale_offset(const rcti &input_canvas,
98 const rcti &scale_canvas,
99 float &r_scale_offset_x,
100 float &r_scale_offset_y)
101{
102 r_scale_offset_x = (BLI_rcti_size_x(&input_canvas) - BLI_rcti_size_x(&scale_canvas)) / 2.0f;
103 r_scale_offset_y = (BLI_rcti_size_y(&input_canvas) - BLI_rcti_size_y(&scale_canvas)) / 2.0f;
104}
105
107 const rcti &scale_canvas,
108 const float relative_scale_x,
109 const float relative_scale_y,
110 const rcti &output_area,
111 rcti &r_input_area)
112{
113 const float scale_center_x = BLI_rcti_size_x(&input_canvas) / 2.0f;
114 const float scale_center_y = BLI_rcti_size_y(&input_canvas) / 2.0f;
115 float scale_offset_x, scale_offset_y;
116 ScaleOperation::get_scale_offset(input_canvas, scale_canvas, scale_offset_x, scale_offset_y);
117
118 r_input_area.xmin = floorf(
119 scale_coord_inverted(output_area.xmin + scale_offset_x, scale_center_x, relative_scale_x));
120 r_input_area.xmax = ceilf(
121 scale_coord_inverted(output_area.xmax + scale_offset_x, scale_center_x, relative_scale_x));
122 r_input_area.ymin = floorf(
123 scale_coord_inverted(output_area.ymin + scale_offset_y, scale_center_y, relative_scale_y));
124 r_input_area.ymax = ceilf(
125 scale_coord_inverted(output_area.ymax + scale_offset_y, scale_center_y, relative_scale_y));
126}
127
129 const rcti &output_area,
130 rcti &r_input_area)
131{
132 r_input_area = output_area;
133 if (input_idx != 0 || is_scaling_variable()) {
134 return;
135 }
136
138 const float scale_x = get_constant_scale_x(image_op->get_width());
139 const float scale_y = get_constant_scale_y(image_op->get_height());
140
142 image_op->get_canvas(), this->get_canvas(), scale_x, scale_y, output_area, r_input_area);
144}
145
147 const rcti &area,
149{
151 const int input_image_width = input_image_op->get_width();
152 const int input_image_height = input_image_op->get_height();
153 const float scale_x_factor = get_relative_scale_x_factor(input_image_width);
154 const float scale_y_factor = get_relative_scale_y_factor(input_image_height);
155 const float scale_center_x = input_image_width / 2.0f;
156 const float scale_center_y = input_image_height / 2.0f;
157 float from_scale_offset_x, from_scale_offset_y;
159 input_image_op->get_canvas(), this->get_canvas(), from_scale_offset_x, from_scale_offset_y);
160
161 const MemoryBuffer *input_image = inputs[IMAGE_INPUT_INDEX];
162 MemoryBuffer *input_x = inputs[X_INPUT_INDEX];
163 MemoryBuffer *input_y = inputs[Y_INPUT_INDEX];
164 BuffersIterator<float> it = output->iterate_with({input_x, input_y}, area);
165 for (; !it.is_end(); ++it) {
166 const float rel_scale_x = *it.in(0) * scale_x_factor;
167 const float rel_scale_y = *it.in(1) * scale_y_factor;
168 const float scaled_x = scale_coord_inverted(from_scale_offset_x + canvas_.xmin + 0.5f + it.x,
169 scale_center_x,
170 rel_scale_x) -
171 0.5f;
172 const float scaled_y = scale_coord_inverted(from_scale_offset_y + canvas_.ymin + 0.5f + it.y,
173 scale_center_y,
174 rel_scale_y) -
175 0.5f;
176
177 input_image->read_elem_sampled(
178 scaled_x - canvas_.xmin, scaled_y - canvas_.ymin, (PixelSampler)sampler_, it.out);
179 }
180}
181
182void ScaleOperation::determine_canvas(const rcti &preferred_area, rcti &r_area)
183{
184 const bool image_determined =
185 get_input_socket(IMAGE_INPUT_INDEX)->determine_canvas(preferred_area, r_area);
186 if (image_determined) {
187 rcti image_canvas = r_area;
188 rcti unused = COM_AREA_NONE;
191 x_socket->determine_canvas(image_canvas, unused);
192 y_socket->determine_canvas(image_canvas, unused);
193 if (is_scaling_variable()) {
194 /* Do not scale canvas. */
195 return;
196 }
197
198 /* Determine scaled canvas. */
199 const float input_width = BLI_rcti_size_x(&r_area);
200 const float input_height = BLI_rcti_size_y(&r_area);
201 const float scale_x = get_constant_scale_x(input_width);
202 const float scale_y = get_constant_scale_y(input_height);
203 scale_area(r_area, scale_x, scale_y);
204
205 /* Re-determine canvases of x and y constant inputs with scaled canvas as preferred. */
208 x_socket->determine_canvas(r_area, unused);
209 y_socket->determine_canvas(r_area, unused);
210 }
211}
212
214
216
224
225void ScaleFixedSizeOperation::init_data(const rcti &input_canvas)
226{
227 const int input_width = BLI_rcti_size_x(&input_canvas);
228 const int input_height = BLI_rcti_size_y(&input_canvas);
229 rel_x_ = input_width / float(new_width_);
230 rel_y_ = input_height / float(new_height_);
231
232 /* *** all the options below are for a fairly special case - camera framing *** */
233 if (offset_x_ != 0.0f || offset_y_ != 0.0f) {
234 is_offset_ = true;
235
236 if (new_width_ > new_height_) {
237 offset_x_ *= new_width_;
238 offset_y_ *= new_width_;
239 }
240 else {
241 offset_x_ *= new_height_;
242 offset_y_ *= new_height_;
243 }
244 }
245
246 if (is_aspect_) {
247 /* apply aspect from clip */
248 const float w_src = input_width;
249 const float h_src = input_height;
250
251 /* destination aspect is already applied from the camera frame */
252 const float w_dst = new_width_;
253 const float h_dst = new_height_;
254
255 const float asp_src = w_src / h_src;
256 const float asp_dst = w_dst / h_dst;
257
258 if (fabsf(asp_src - asp_dst) >= FLT_EPSILON) {
259 if ((asp_src > asp_dst) == (is_crop_ == true)) {
260 /* fit X */
261 const float div = asp_src / asp_dst;
262 rel_x_ /= div;
263 offset_x_ += ((w_src - (w_src * div)) / (w_src / w_dst)) / 2.0f;
264 if (is_crop_) {
265 int fit_width = new_width_ * div;
266
267 const int added_width = fit_width - new_width_;
268 new_width_ += added_width;
269 offset_x_ += added_width / 2.0f;
270 }
271 }
272 else {
273 /* fit Y */
274 const float div = asp_dst / asp_src;
275 rel_y_ /= div;
276 offset_y_ += ((h_src - (h_src * div)) / (h_src / h_dst)) / 2.0f;
277 if (is_crop_) {
278 int fit_height = new_height_ * div;
279
280 const int added_height = fit_height - new_height_;
281 new_height_ += added_height;
282 offset_y_ += added_height / 2.0f;
283 }
284 }
285
286 is_offset_ = true;
287 }
288 }
289 /* *** end framing options *** */
290}
291
292void ScaleFixedSizeOperation::determine_canvas(const rcti &preferred_area, rcti &r_area)
293{
294 rcti local_preferred = preferred_area;
295 local_preferred.xmax = local_preferred.xmin + new_width_;
296 local_preferred.ymax = local_preferred.ymin + new_height_;
297 rcti input_canvas = COM_AREA_NONE;
298 const bool input_determined = get_input_socket(0)->determine_canvas(local_preferred,
299 input_canvas);
300 if (input_determined) {
301 init_data(input_canvas);
302 r_area = input_canvas;
303 r_area.xmin /= rel_x_;
304 r_area.ymin /= rel_y_;
305 r_area.xmin += offset_x_;
306 r_area.ymin += offset_y_;
307
308 r_area.xmax = r_area.xmin + new_width_;
309 r_area.ymax = r_area.ymin + new_height_;
310 }
311}
312
314 const rcti &output_area,
315 rcti &r_input_area)
316{
317 BLI_assert(input_idx == 0);
318 UNUSED_VARS_NDEBUG(input_idx);
319
320 r_input_area.xmax = ceilf((output_area.xmax - offset_x_) * rel_x_);
321 r_input_area.xmin = floorf((output_area.xmin - offset_x_) * rel_x_);
322 r_input_area.ymax = ceilf((output_area.ymax - offset_y_) * rel_y_);
323 r_input_area.ymin = floorf((output_area.ymin - offset_y_) * rel_y_);
325}
326
328 const rcti &area,
330{
331 const MemoryBuffer *input_img = inputs[0];
333 BuffersIterator<float> it = output->iterate_with({}, area);
334 const float add_x = (canvas_.xmin + 0.5f - offset_x_) * rel_x_ - canvas_.xmin - 0.5f;
335 const float add_y = (canvas_.ymin + 0.5f - offset_y_) * rel_y_ - canvas_.ymin - 0.5f;
336 for (; !it.is_end(); ++it) {
337 const float nx = it.x * rel_x_ + add_x;
338 const float ny = it.y * rel_y_ + add_y;
339 input_img->read_elem_sampled(nx, ny, sampler, it.out);
340 }
341}
342
343} // namespace blender::compositor
#define BLI_assert(a)
Definition BLI_assert.h:50
BLI_INLINE int BLI_rcti_size_y(const struct rcti *rct)
Definition BLI_rect.h:193
void BLI_rcti_translate(struct rcti *rect, int x, int y)
Definition rct.c:560
BLI_INLINE int BLI_rcti_size_x(const struct rcti *rct)
Definition BLI_rect.h:189
#define UNUSED_VARS_NDEBUG(...)
a MemoryBuffer contains access to the data
void read_elem_sampled(float x, float y, PixelSampler sampler, float *out) const
BuffersIterator< float > iterate_with(Span< MemoryBuffer * > inputs)
bool determine_canvas(const rcti &preferred_area, rcti &r_area)
NodeOperation contains calculation logic.
void add_output_socket(DataType datatype)
const NodeOperationFlags get_flags() const
NodeOperation * get_input_operation(int index)
NodeOperationInput * get_input_socket(unsigned int index)
void add_input_socket(DataType datatype, ResizeMode resize_mode=ResizeMode::Center)
void set_canvas_input_index(unsigned int index)
set the index of the input socket that will determine the canvas of this operation
void update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, Span< MemoryBuffer * > inputs) override
void determine_canvas(const rcti &preferred_area, rcti &r_area) override
void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override
Get input operation area being read by this operation on rendering given output area.
static void get_scale_offset(const rcti &input_canvas, const rcti &scale_canvas, float &r_scale_offset_x, float &r_scale_offset_y)
static void get_scale_area_of_interest(const rcti &input_canvas, const rcti &scale_canvas, float relative_scale_x, float relative_scale_y, const rcti &output_area, rcti &r_input_area)
void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override
Get input operation area being read by this operation on rendering given output area.
virtual float get_relative_scale_x_factor(float width)=0
static void scale_area(rcti &area, float relative_scale_x, float relative_scale_y)
static float scale_coord_inverted(const float coord, const float center, const float relative_scale)
virtual float get_relative_scale_y_factor(float height)=0
void determine_canvas(const rcti &preferred_area, rcti &r_area) override
void update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, Span< MemoryBuffer * > inputs) override
static void clamp_area_size_max(rcti &area, Size2f max_size)
static float scale_coord(const float coord, const float center, const float relative_scale)
local_group_size(16, 16) .push_constant(Type local_group_size(16, 16) .push_constant(Type input_tx sampler(1, ImageType::FLOAT_2D, "matte_tx") .image(0
#define ceilf(x)
#define floorf(x)
#define fabsf(x)
draw_view in_light_buf[] float
draw_view push_constant(Type::INT, "radiance_src") .push_constant(Type capture_info_buf storage_buf(1, Qualifier::READ, "ObjectBounds", "bounds_buf[]") .push_constant(Type draw_view int
DataType
possible data types for sockets
Definition COM_defines.h:21
void expand_area_for_sampler(rcti &area, PixelSampler sampler)
Definition COM_Enums.cc:9
constexpr rcti COM_AREA_NONE
Definition COM_defines.h:89
typename BuffersIteratorBuilder< T >::Iterator BuffersIterator
int ymin
int ymax
int xmin
int xmax