Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorManuel Castilla <manzanillawork@gmail.com>2021-09-28 20:33:06 +0300
committerManuel Castilla <manzanillawork@gmail.com>2021-09-28 23:00:17 +0300
commitf84fb12f5d72433780a96c3cc4381399f153cf1a (patch)
tree62ca25765b3b5ccc2bc8ca35449ddd8f3999071a /source/blender/compositor/operations/COM_ScaleOperation.cc
parent76377f0176b9561a7fc8f46b4ed704c631ddd90d (diff)
Compositor: Add support for canvas compositing
This commit adds functionality for operations that require pixel translation or resizing on "Full Frame" mode, allowing to adjust their canvas. It fixes most cropping issues in translate, scale, rotate and transform nodes by adjusting their canvas to the result, instead of the input canvas. Operations output buffer is still always on (0,0) position for easier image algorithm implementation, even when the canvas is not. Current limitations (will be addressed on bcon2): - Displayed translation in Viewer node is limited to 6000px. - When scaling up the canvas size is limited to the scene resolution size x 1.5 . From that point it crops. If none of these limitations are hit, the Viewer node displays the full input with any translation. Differential Revision: https://developer.blender.org/D12466
Diffstat (limited to 'source/blender/compositor/operations/COM_ScaleOperation.cc')
-rw-r--r--source/blender/compositor/operations/COM_ScaleOperation.cc263
1 files changed, 206 insertions, 57 deletions
diff --git a/source/blender/compositor/operations/COM_ScaleOperation.cc b/source/blender/compositor/operations/COM_ScaleOperation.cc
index f5a423ea8e3..dbd8faf0f1d 100644
--- a/source/blender/compositor/operations/COM_ScaleOperation.cc
+++ b/source/blender/compositor/operations/COM_ScaleOperation.cc
@@ -38,17 +38,21 @@ BaseScaleOperation::BaseScaleOperation()
m_variable_size = false;
}
+void BaseScaleOperation::set_scale_canvas_max_size(Size2f size)
+{
+ max_scale_canvas_size_ = size;
+}
+
ScaleOperation::ScaleOperation() : ScaleOperation(DataType::Color)
{
}
ScaleOperation::ScaleOperation(DataType data_type) : BaseScaleOperation()
{
- this->addInputSocket(data_type);
+ this->addInputSocket(data_type, ResizeMode::None);
this->addInputSocket(DataType::Value);
this->addInputSocket(DataType::Value);
this->addOutputSocket(data_type);
- this->set_canvas_input_index(0);
this->m_inputOperation = nullptr;
this->m_inputXOperation = nullptr;
this->m_inputYOperation = nullptr;
@@ -64,34 +68,52 @@ float ScaleOperation::get_constant_scale(const int input_op_idx, const float fac
return 1.0f;
}
-float ScaleOperation::get_constant_scale_x()
+float ScaleOperation::get_constant_scale_x(const float width)
{
- return get_constant_scale(1, get_relative_scale_x_factor());
+ return get_constant_scale(X_INPUT_INDEX, get_relative_scale_x_factor(width));
}
-float ScaleOperation::get_constant_scale_y()
+float ScaleOperation::get_constant_scale_y(const float height)
{
- return get_constant_scale(2, get_relative_scale_y_factor());
+ return get_constant_scale(Y_INPUT_INDEX, get_relative_scale_y_factor(height));
}
-void ScaleOperation::scale_area(
- rcti &rect, float center_x, float center_y, float scale_x, float scale_y)
+bool ScaleOperation::is_scaling_variable()
{
- rect.xmin = scale_coord(rect.xmin, center_x, scale_x);
- rect.xmax = scale_coord(rect.xmax, center_x, scale_x);
- rect.ymin = scale_coord(rect.ymin, center_y, scale_y);
- rect.ymax = scale_coord(rect.ymax, center_y, scale_y);
+ return !get_input_operation(X_INPUT_INDEX)->get_flags().is_constant_operation ||
+ !get_input_operation(Y_INPUT_INDEX)->get_flags().is_constant_operation;
}
-void ScaleOperation::scale_area(rcti &rect, float scale_x, float scale_y)
+void ScaleOperation::scale_area(rcti &area, float relative_scale_x, float relative_scale_y)
{
- scale_area(rect, m_centerX, m_centerY, scale_x, scale_y);
+ const rcti src_area = area;
+ const float center_x = BLI_rcti_size_x(&area) / 2.0f;
+ const float center_y = BLI_rcti_size_y(&area) / 2.0f;
+ area.xmin = floorf(scale_coord(area.xmin, center_x, relative_scale_x));
+ area.xmax = ceilf(scale_coord(area.xmax, center_x, relative_scale_x));
+ area.ymin = floorf(scale_coord(area.ymin, center_y, relative_scale_y));
+ area.ymax = ceilf(scale_coord(area.ymax, center_y, relative_scale_y));
+
+ float scale_offset_x, scale_offset_y;
+ ScaleOperation::get_scale_offset(src_area, area, scale_offset_x, scale_offset_y);
+ BLI_rcti_translate(&area, -scale_offset_x, -scale_offset_y);
+}
+
+void ScaleOperation::clamp_area_size_max(rcti &area, Size2f max_size)
+{
+
+ if (BLI_rcti_size_x(&area) > max_size.x) {
+ area.xmax = area.xmin + max_size.x;
+ }
+ if (BLI_rcti_size_y(&area) > max_size.y) {
+ area.ymax = area.ymin + max_size.y;
+ }
}
void ScaleOperation::init_data()
{
- m_centerX = getWidth() / 2.0f;
- m_centerY = getHeight() / 2.0f;
+ canvas_center_x_ = canvas_.xmin + getWidth() / 2.0f;
+ canvas_center_y_ = canvas_.ymin + getHeight() / 2.0f;
}
void ScaleOperation::initExecution()
@@ -108,18 +130,52 @@ void ScaleOperation::deinitExecution()
this->m_inputYOperation = nullptr;
}
+void ScaleOperation::get_scale_offset(const rcti &input_canvas,
+ const rcti &scale_canvas,
+ float &r_scale_offset_x,
+ float &r_scale_offset_y)
+{
+ r_scale_offset_x = (BLI_rcti_size_x(&input_canvas) - BLI_rcti_size_x(&scale_canvas)) / 2.0f;
+ r_scale_offset_y = (BLI_rcti_size_y(&input_canvas) - BLI_rcti_size_y(&scale_canvas)) / 2.0f;
+}
+
+void ScaleOperation::get_scale_area_of_interest(const rcti &input_canvas,
+ const rcti &scale_canvas,
+ const float relative_scale_x,
+ const float relative_scale_y,
+ const rcti &output_area,
+ rcti &r_input_area)
+{
+ const float scale_center_x = BLI_rcti_size_x(&input_canvas) / 2.0f;
+ const float scale_center_y = BLI_rcti_size_y(&input_canvas) / 2.0f;
+ float scale_offset_x, scale_offset_y;
+ ScaleOperation::get_scale_offset(input_canvas, scale_canvas, scale_offset_x, scale_offset_y);
+
+ r_input_area.xmin = floorf(
+ scale_coord_inverted(output_area.xmin + scale_offset_x, scale_center_x, relative_scale_x));
+ r_input_area.xmax = ceilf(
+ scale_coord_inverted(output_area.xmax + scale_offset_x, scale_center_x, relative_scale_x));
+ r_input_area.ymin = floorf(
+ scale_coord_inverted(output_area.ymin + scale_offset_y, scale_center_y, relative_scale_y));
+ r_input_area.ymax = ceilf(
+ scale_coord_inverted(output_area.ymax + scale_offset_y, scale_center_y, relative_scale_y));
+}
+
void ScaleOperation::get_area_of_interest(const int input_idx,
const rcti &output_area,
rcti &r_input_area)
{
r_input_area = output_area;
- if (input_idx != 0 || m_variable_size) {
+ if (input_idx != 0 || is_scaling_variable()) {
return;
}
- float scale_x = get_constant_scale_x();
- float scale_y = get_constant_scale_y();
- scale_area(r_input_area, scale_x, scale_y);
+ NodeOperation *image_op = get_input_operation(IMAGE_INPUT_INDEX);
+ const float scale_x = get_constant_scale_x(image_op->getWidth());
+ const float scale_y = get_constant_scale_y(image_op->getHeight());
+
+ get_scale_area_of_interest(
+ image_op->get_canvas(), this->get_canvas(), scale_x, scale_y, output_area, r_input_area);
expand_area_for_sampler(r_input_area, (PixelSampler)m_sampler);
}
@@ -127,18 +183,70 @@ void ScaleOperation::update_memory_buffer_partial(MemoryBuffer *output,
const rcti &area,
Span<MemoryBuffer *> inputs)
{
- const MemoryBuffer *input_img = inputs[0];
- MemoryBuffer *input_x = inputs[1];
- MemoryBuffer *input_y = inputs[2];
- const float scale_x_factor = get_relative_scale_x_factor();
- const float scale_y_factor = get_relative_scale_y_factor();
+ NodeOperation *input_image_op = get_input_operation(IMAGE_INPUT_INDEX);
+ const int input_image_width = input_image_op->getWidth();
+ const int input_image_height = input_image_op->getHeight();
+ const float scale_x_factor = get_relative_scale_x_factor(input_image_width);
+ const float scale_y_factor = get_relative_scale_y_factor(input_image_height);
+ const float scale_center_x = input_image_width / 2.0f;
+ const float scale_center_y = input_image_height / 2.0f;
+ float from_scale_offset_x, from_scale_offset_y;
+ ScaleOperation::get_scale_offset(
+ input_image_op->get_canvas(), this->get_canvas(), from_scale_offset_x, from_scale_offset_y);
+
+ const MemoryBuffer *input_image = inputs[IMAGE_INPUT_INDEX];
+ MemoryBuffer *input_x = inputs[X_INPUT_INDEX];
+ MemoryBuffer *input_y = inputs[Y_INPUT_INDEX];
BuffersIterator<float> it = output->iterate_with({input_x, input_y}, area);
for (; !it.is_end(); ++it) {
const float rel_scale_x = *it.in(0) * scale_x_factor;
const float rel_scale_y = *it.in(1) * scale_y_factor;
- const float scaled_x = scale_coord(it.x, m_centerX, rel_scale_x);
- const float scaled_y = scale_coord(it.y, m_centerY, rel_scale_y);
- input_img->read_elem_sampled(scaled_x, scaled_y, (PixelSampler)m_sampler, it.out);
+ const float scaled_x = scale_coord_inverted(
+ from_scale_offset_x + canvas_.xmin + it.x, scale_center_x, rel_scale_x);
+ const float scaled_y = scale_coord_inverted(
+ from_scale_offset_y + canvas_.ymin + it.y, scale_center_y, rel_scale_y);
+
+ input_image->read_elem_sampled(
+ scaled_x - canvas_.xmin, scaled_y - canvas_.ymin, (PixelSampler)m_sampler, it.out);
+ }
+}
+
+void ScaleOperation::determine_canvas(const rcti &preferred_area, rcti &r_area)
+{
+ if (execution_model_ == eExecutionModel::Tiled) {
+ NodeOperation::determine_canvas(preferred_area, r_area);
+ return;
+ }
+
+ const bool image_determined =
+ getInputSocket(IMAGE_INPUT_INDEX)->determine_canvas(preferred_area, r_area);
+ if (image_determined) {
+ rcti image_canvas = r_area;
+ rcti unused;
+ NodeOperationInput *x_socket = getInputSocket(X_INPUT_INDEX);
+ NodeOperationInput *y_socket = getInputSocket(Y_INPUT_INDEX);
+ x_socket->determine_canvas(image_canvas, unused);
+ y_socket->determine_canvas(image_canvas, unused);
+ if (is_scaling_variable()) {
+ /* Do not scale canvas. */
+ return;
+ }
+
+ /* Determine scaled canvas. */
+ const float input_width = BLI_rcti_size_x(&r_area);
+ const float input_height = BLI_rcti_size_y(&r_area);
+ const float scale_x = get_constant_scale_x(input_width);
+ const float scale_y = get_constant_scale_y(input_height);
+ scale_area(r_area, scale_x, scale_y);
+ const Size2f max_scale_size = {MAX2(input_width, max_scale_canvas_size_.x),
+ MAX2(input_height, max_scale_canvas_size_.y)};
+ clamp_area_size_max(r_area, max_scale_size);
+
+ /* Re-determine canvases of x and y constant inputs with scaled canvas as preferred. */
+ get_input_operation(X_INPUT_INDEX)->unset_canvas();
+ get_input_operation(Y_INPUT_INDEX)->unset_canvas();
+ x_socket->determine_canvas(r_area, unused);
+ y_socket->determine_canvas(r_area, unused);
}
}
@@ -166,8 +274,8 @@ void ScaleRelativeOperation::executePixelSampled(float output[4],
const float scx = scaleX[0];
const float scy = scaleY[0];
- float nx = this->m_centerX + (x - this->m_centerX) / scx;
- float ny = this->m_centerY + (y - this->m_centerY) / scy;
+ float nx = this->canvas_center_x_ + (x - this->canvas_center_x_) / scx;
+ float ny = this->canvas_center_y_ + (y - this->canvas_center_y_) / scy;
this->m_inputOperation->readSampled(output, nx, ny, effective_sampler);
}
@@ -186,10 +294,10 @@ bool ScaleRelativeOperation::determineDependingAreaOfInterest(rcti *input,
const float scx = scaleX[0];
const float scy = scaleY[0];
- newInput.xmax = this->m_centerX + (input->xmax - this->m_centerX) / scx + 1;
- newInput.xmin = this->m_centerX + (input->xmin - this->m_centerX) / scx - 1;
- newInput.ymax = this->m_centerY + (input->ymax - this->m_centerY) / scy + 1;
- newInput.ymin = this->m_centerY + (input->ymin - this->m_centerY) / scy - 1;
+ newInput.xmax = this->canvas_center_x_ + (input->xmax - this->canvas_center_x_) / scx + 1;
+ newInput.xmin = this->canvas_center_x_ + (input->xmin - this->canvas_center_x_) / scx - 1;
+ newInput.ymax = this->canvas_center_y_ + (input->ymax - this->canvas_center_y_) / scy + 1;
+ newInput.ymin = this->canvas_center_y_ + (input->ymin - this->canvas_center_y_) / scy - 1;
}
else {
newInput.xmax = this->getWidth();
@@ -222,8 +330,8 @@ void ScaleAbsoluteOperation::executePixelSampled(float output[4],
float relativeXScale = scx / width;
float relativeYScale = scy / height;
- float nx = this->m_centerX + (x - this->m_centerX) / relativeXScale;
- float ny = this->m_centerY + (y - this->m_centerY) / relativeYScale;
+ float nx = this->canvas_center_x_ + (x - this->canvas_center_x_) / relativeXScale;
+ float ny = this->canvas_center_y_ + (y - this->canvas_center_y_) / relativeYScale;
this->m_inputOperation->readSampled(output, nx, ny, effective_sampler);
}
@@ -248,10 +356,14 @@ bool ScaleAbsoluteOperation::determineDependingAreaOfInterest(rcti *input,
float relateveXScale = scx / width;
float relateveYScale = scy / height;
- newInput.xmax = this->m_centerX + (input->xmax - this->m_centerX) / relateveXScale;
- newInput.xmin = this->m_centerX + (input->xmin - this->m_centerX) / relateveXScale;
- newInput.ymax = this->m_centerY + (input->ymax - this->m_centerY) / relateveYScale;
- newInput.ymin = this->m_centerY + (input->ymin - this->m_centerY) / relateveYScale;
+ newInput.xmax = this->canvas_center_x_ +
+ (input->xmax - this->canvas_center_x_) / relateveXScale;
+ newInput.xmin = this->canvas_center_x_ +
+ (input->xmin - this->canvas_center_x_) / relateveXScale;
+ newInput.ymax = this->canvas_center_y_ +
+ (input->ymax - this->canvas_center_y_) / relateveYScale;
+ newInput.ymin = this->canvas_center_y_ +
+ (input->ymin - this->canvas_center_y_) / relateveYScale;
}
else {
newInput.xmax = this->getWidth();
@@ -272,11 +384,12 @@ ScaleFixedSizeOperation::ScaleFixedSizeOperation() : BaseScaleOperation()
this->m_is_offset = false;
}
-void ScaleFixedSizeOperation::init_data()
+void ScaleFixedSizeOperation::init_data(const rcti &input_canvas)
{
- const NodeOperation *input_op = getInputOperation(0);
- this->m_relX = input_op->getWidth() / (float)this->m_newWidth;
- this->m_relY = input_op->getHeight() / (float)this->m_newHeight;
+ const int input_width = BLI_rcti_size_x(&input_canvas);
+ const int input_height = BLI_rcti_size_y(&input_canvas);
+ this->m_relX = input_width / (float)this->m_newWidth;
+ this->m_relY = input_height / (float)this->m_newHeight;
/* *** all the options below are for a fairly special case - camera framing *** */
if (this->m_offsetX != 0.0f || this->m_offsetY != 0.0f) {
@@ -294,8 +407,8 @@ void ScaleFixedSizeOperation::init_data()
if (this->m_is_aspect) {
/* apply aspect from clip */
- const float w_src = input_op->getWidth();
- const float h_src = input_op->getHeight();
+ const float w_src = input_width;
+ const float h_src = input_height;
/* destination aspect is already applied from the camera frame */
const float w_dst = this->m_newWidth;
@@ -310,12 +423,32 @@ void ScaleFixedSizeOperation::init_data()
const float div = asp_src / asp_dst;
this->m_relX /= div;
this->m_offsetX += ((w_src - (w_src * div)) / (w_src / w_dst)) / 2.0f;
+ if (m_is_crop && execution_model_ == eExecutionModel::FullFrame) {
+ int fit_width = m_newWidth * div;
+ if (fit_width > max_scale_canvas_size_.x) {
+ fit_width = max_scale_canvas_size_.x;
+ }
+
+ const int added_width = fit_width - m_newWidth;
+ m_newWidth += added_width;
+ m_offsetX += added_width / 2.0f;
+ }
}
else {
/* fit Y */
const float div = asp_dst / asp_src;
this->m_relY /= div;
this->m_offsetY += ((h_src - (h_src * div)) / (h_src / h_dst)) / 2.0f;
+ if (m_is_crop && execution_model_ == eExecutionModel::FullFrame) {
+ int fit_height = m_newHeight * div;
+ if (fit_height > max_scale_canvas_size_.y) {
+ fit_height = max_scale_canvas_size_.y;
+ }
+
+ const int added_height = fit_height - m_newHeight;
+ m_newHeight += added_height;
+ m_offsetY += added_height / 2.0f;
+ }
}
this->m_is_offset = true;
@@ -371,9 +504,21 @@ void ScaleFixedSizeOperation::determine_canvas(const rcti &preferred_area, rcti
rcti local_preferred = preferred_area;
local_preferred.xmax = local_preferred.xmin + m_newWidth;
local_preferred.ymax = local_preferred.ymin + m_newHeight;
- BaseScaleOperation::determine_canvas(local_preferred, r_area);
- r_area.xmax = r_area.xmin + m_newWidth;
- r_area.ymax = r_area.ymin + m_newHeight;
+ rcti input_canvas;
+ const bool input_determined = getInputSocket(0)->determine_canvas(local_preferred, input_canvas);
+ if (input_determined) {
+ init_data(input_canvas);
+ r_area = input_canvas;
+ if (execution_model_ == eExecutionModel::FullFrame) {
+ r_area.xmin /= m_relX;
+ r_area.ymin /= m_relY;
+ r_area.xmin += m_offsetX;
+ r_area.ymin += m_offsetY;
+ }
+
+ r_area.xmax = r_area.xmin + m_newWidth;
+ r_area.ymax = r_area.ymin + m_newHeight;
+ }
}
void ScaleFixedSizeOperation::get_area_of_interest(const int input_idx,
@@ -382,10 +527,11 @@ void ScaleFixedSizeOperation::get_area_of_interest(const int input_idx,
{
BLI_assert(input_idx == 0);
UNUSED_VARS_NDEBUG(input_idx);
- r_input_area.xmax = (output_area.xmax - m_offsetX) * this->m_relX;
- r_input_area.xmin = (output_area.xmin - m_offsetX) * this->m_relX;
- r_input_area.ymax = (output_area.ymax - m_offsetY) * this->m_relY;
- r_input_area.ymin = (output_area.ymin - m_offsetY) * this->m_relY;
+
+ r_input_area.xmax = ceilf((output_area.xmax - m_offsetX) * this->m_relX);
+ r_input_area.xmin = floorf((output_area.xmin - m_offsetX) * this->m_relX);
+ r_input_area.ymax = ceilf((output_area.ymax - m_offsetY) * this->m_relY);
+ r_input_area.ymin = floorf((output_area.ymin - m_offsetY) * this->m_relY);
expand_area_for_sampler(r_input_area, (PixelSampler)m_sampler);
}
@@ -398,14 +544,17 @@ void ScaleFixedSizeOperation::update_memory_buffer_partial(MemoryBuffer *output,
BuffersIterator<float> it = output->iterate_with({}, area);
if (this->m_is_offset) {
for (; !it.is_end(); ++it) {
- const float nx = (it.x - this->m_offsetX) * this->m_relX;
- const float ny = (it.y - this->m_offsetY) * this->m_relY;
- input_img->read_elem_sampled(nx, ny, sampler, it.out);
+ const float nx = (canvas_.xmin + it.x - this->m_offsetX) * this->m_relX;
+ const float ny = (canvas_.ymin + it.y - this->m_offsetY) * this->m_relY;
+ input_img->read_elem_sampled(nx - canvas_.xmin, ny - canvas_.ymin, sampler, it.out);
}
}
else {
for (; !it.is_end(); ++it) {
- input_img->read_elem_sampled(it.x * this->m_relX, it.y * this->m_relY, sampler, it.out);
+ input_img->read_elem_sampled((canvas_.xmin + it.x) * this->m_relX - canvas_.xmin,
+ (canvas_.ymin + it.y) * this->m_relY - canvas_.ymin,
+ sampler,
+ it.out);
}
}
}