Rendering with F12 causes some inconsistencies between canvases and areas of interest. This is due to some corrections of composite node canvas to fit to the render size, which are not necessary for the Viewer Node. This patch fixes the issue by considering the input image as a whole before translating. Note: this is still consistent with immediate realization of transform nodes in GPU compositor, where nodes are to be evaluated from left to right. Pull Request: https://projects.blender.org/blender/blender/pulls/119276
136 lines
4.8 KiB
C++
136 lines
4.8 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->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;
|
|
}
|
|
|
|
const int delta_x = this->get_delta_x();
|
|
const int delta_y = this->get_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 int input_x = x - delta_x;
|
|
const int input_y = y - delta_y;
|
|
input->read(out, input_x, input_y, 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
|