Files
test/source/blender/compositor/operations/COM_TranslateOperation.cc
Habib Gahbiche 483c854612 Compositor: implement interpolation methods for Translate node
Compositor: Expose interpolation methods Nearest, Bilinear and Bicubic to the user for translate node

This is part of #119592.

Pull Request: https://projects.blender.org/blender/blender/pulls/119603
2024-05-01 14:44:01 +02:00

143 lines
5.0 KiB
C++

/* SPDX-FileCopyrightText: 2011 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "COM_TranslateOperation.h"
namespace blender::compositor {
TranslateOperation::TranslateOperation() : TranslateOperation(DataType::Color) {}
TranslateOperation::TranslateOperation(DataType data_type, ResizeMode resize_mode)
{
this->add_input_socket(data_type, resize_mode);
this->add_input_socket(DataType::Value, ResizeMode::None);
this->add_input_socket(DataType::Value, ResizeMode::None);
this->add_output_socket(data_type);
this->set_canvas_input_index(0);
is_delta_set_ = false;
is_relative_ = false;
this->x_extend_mode_ = MemoryBufferExtend::Clip;
this->y_extend_mode_ = MemoryBufferExtend::Clip;
this->sampler_ = PixelSampler::Nearest;
this->flags_.can_be_constant = true;
}
void TranslateOperation::set_wrapping(int wrapping_type)
{
switch (wrapping_type) {
case CMP_NODE_WRAP_X:
x_extend_mode_ = MemoryBufferExtend::Repeat;
break;
case CMP_NODE_WRAP_Y:
y_extend_mode_ = MemoryBufferExtend::Repeat;
break;
case CMP_NODE_WRAP_XY:
x_extend_mode_ = MemoryBufferExtend::Repeat;
y_extend_mode_ = MemoryBufferExtend::Repeat;
break;
default:
break;
}
}
void TranslateOperation::get_area_of_interest(const int input_idx,
const rcti &output_area,
rcti &r_input_area)
{
if (input_idx == 0) {
ensure_delta();
r_input_area = output_area;
if (x_extend_mode_ == MemoryBufferExtend::Clip) {
const int delta_x = this->get_delta_x();
BLI_rcti_translate(&r_input_area, -delta_x, 0);
}
else if (x_extend_mode_ == MemoryBufferExtend::Repeat) {
/* The region of interest should consider the whole input image to avoid cropping effects,
* e.g. by prior scaling or rotating. Note: this is still consistent with immediate
* realization of transform nodes in GPU compositor, where nodes are to be evaluated from
* left to right. */
const int in_width = get_width();
BLI_rcti_resize_x(&r_input_area, in_width);
}
if (y_extend_mode_ == MemoryBufferExtend::Clip) {
const int delta_y = this->get_delta_y();
BLI_rcti_translate(&r_input_area, 0, -delta_y);
}
else if (y_extend_mode_ == MemoryBufferExtend::Repeat) {
const int in_height = get_height();
BLI_rcti_resize_y(&r_input_area, in_height);
}
}
else {
r_input_area = output_area;
}
}
void TranslateOperation::update_memory_buffer_partial(MemoryBuffer *output,
const rcti &area,
Span<MemoryBuffer *> inputs)
{
MemoryBuffer *input = inputs[0];
if (input->is_a_single_elem()) {
copy_v4_v4(output->get_elem(0, 0), input->get_elem(0, 0));
return;
}
/* Some compositor operations produce an empty output buffer by specifying a COM_AREA_NONE canvas
* to indicate an invalid output, for instance, when the Mask operation reference an invalid
* mask. The intention is that this buffer would signal that a fallback value would fill the
* canvas of consumer operations. Since the aforementioned filling is achieved through the
* Translate operation as part of canvas conversion in COM_convert_canvas, we handle the empty
* buffer case here and fill the output using a fallback black color. */
if (BLI_rcti_is_empty(&input->get_rect())) {
const float value[4] = {0.0f, 0.0f, 0.0f, 1.0f};
output->fill(area, value);
return;
}
float delta_x = this->get_delta_x();
float delta_y = this->get_delta_y();
if (sampler_ == PixelSampler::Nearest) {
/* Use same rounding convention for GPU compositor. */
delta_x = round(delta_x);
delta_y = round(delta_y);
}
for (int y = area.ymin; y < area.ymax; y++) {
float *out = output->get_elem(area.xmin, y);
for (int x = area.xmin; x < area.xmax; x++) {
const float input_x = x - delta_x;
const float input_y = y - delta_y;
input->read(out, input_x, input_y, sampler_, x_extend_mode_, y_extend_mode_);
out += output->elem_stride;
}
}
}
TranslateCanvasOperation::TranslateCanvasOperation()
: TranslateOperation(DataType::Color, ResizeMode::None)
{
}
void TranslateCanvasOperation::determine_canvas(const rcti &preferred_area, rcti &r_area)
{
const bool determined =
get_input_socket(IMAGE_INPUT_INDEX)->determine_canvas(preferred_area, r_area);
if (determined) {
NodeOperationInput *x_socket = get_input_socket(X_INPUT_INDEX);
NodeOperationInput *y_socket = get_input_socket(Y_INPUT_INDEX);
rcti unused = COM_AREA_NONE;
x_socket->determine_canvas(r_area, unused);
y_socket->determine_canvas(r_area, unused);
ensure_delta();
const float delta_x = x_extend_mode_ == MemoryBufferExtend::Clip ? get_delta_x() : 0.0f;
const float delta_y = y_extend_mode_ == MemoryBufferExtend::Clip ? get_delta_y() : 0.0f;
BLI_rcti_translate(&r_area, delta_x, delta_y);
}
}
} // namespace blender::compositor