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-12 21:00:03 +0300
committerManuel Castilla <manzanillawork@gmail.com>2021-09-12 21:00:03 +0300
commit313ea8a28e8a7a95d723fd4c46ab349ed08bea0f (patch)
tree994277f03e4efa6fd802a999c54910e5e2b188e5
parenta0a9499a3a6bc8001c59c7b547ca7a7781415c9a (diff)
Compositor: Add support for canvas compositingtemp-compositor-canvas
This patch adds functionality for operations that require pixels translation or resizing on "Full Frame" mode, allowing to adjust their canvas within the output canvas. It fixes current cropping issues in translate, scale, rotate and transform nodes by adjusting their canvas to the result, instead of the input canvas. Only the scale node canvas is limited to the scene output resolution size for performance reasons as there are many operations that require their whole input to be rendered. Operations output buffer is still always on (0,0) position for easier image algorithm implementation, even when operation canvas is not.
-rw-r--r--source/blender/compositor/intern/COM_CompositorContext.cc10
-rw-r--r--source/blender/compositor/intern/COM_CompositorContext.h3
-rw-r--r--source/blender/compositor/intern/COM_Converter.cc113
-rw-r--r--source/blender/compositor/intern/COM_FullFrameExecutionModel.cc64
-rw-r--r--source/blender/compositor/intern/COM_FullFrameExecutionModel.h6
-rw-r--r--source/blender/compositor/intern/COM_MemoryBuffer.cc2
-rw-r--r--source/blender/compositor/intern/COM_MemoryBuffer.h54
-rw-r--r--source/blender/compositor/intern/COM_NodeOperation.cc40
-rw-r--r--source/blender/compositor/intern/COM_NodeOperation.h32
-rw-r--r--source/blender/compositor/intern/COM_NodeOperationBuilder.cc17
-rw-r--r--source/blender/compositor/intern/COM_SharedOperationBuffers.cc12
-rw-r--r--source/blender/compositor/intern/COM_SharedOperationBuffers.h2
-rw-r--r--source/blender/compositor/nodes/COM_BoxMaskNode.cc2
-rw-r--r--source/blender/compositor/nodes/COM_EllipseMaskNode.cc2
-rw-r--r--source/blender/compositor/nodes/COM_ScaleNode.cc9
-rw-r--r--source/blender/compositor/nodes/COM_Stabilize2dNode.cc3
-rw-r--r--source/blender/compositor/nodes/COM_TransformNode.cc1
-rw-r--r--source/blender/compositor/nodes/COM_TranslateNode.cc4
-rw-r--r--source/blender/compositor/operations/COM_BokehBlurOperation.cc2
-rw-r--r--source/blender/compositor/operations/COM_CalculateMeanOperation.cc2
-rw-r--r--source/blender/compositor/operations/COM_CropOperation.cc2
-rw-r--r--source/blender/compositor/operations/COM_MapUVOperation.cc2
-rw-r--r--source/blender/compositor/operations/COM_PlaneDistortCommonOperation.cc2
-rw-r--r--source/blender/compositor/operations/COM_PreviewOperation.cc2
-rw-r--r--source/blender/compositor/operations/COM_RotateOperation.cc114
-rw-r--r--source/blender/compositor/operations/COM_RotateOperation.h20
-rw-r--r--source/blender/compositor/operations/COM_ScaleOperation.cc207
-rw-r--r--source/blender/compositor/operations/COM_ScaleOperation.h67
-rw-r--r--source/blender/compositor/operations/COM_TonemapOperation.cc2
-rw-r--r--source/blender/compositor/operations/COM_TransformOperation.cc211
-rw-r--r--source/blender/compositor/operations/COM_TransformOperation.h21
-rw-r--r--source/blender/compositor/operations/COM_TranslateOperation.cc36
-rw-r--r--source/blender/compositor/operations/COM_TranslateOperation.h27
-rw-r--r--source/blender/compositor/operations/COM_VariableSizeBokehBlurOperation.cc6
34 files changed, 813 insertions, 286 deletions
diff --git a/source/blender/compositor/intern/COM_CompositorContext.cc b/source/blender/compositor/intern/COM_CompositorContext.cc
index a93820b66dc..0602553ca12 100644
--- a/source/blender/compositor/intern/COM_CompositorContext.cc
+++ b/source/blender/compositor/intern/COM_CompositorContext.cc
@@ -43,6 +43,16 @@ int CompositorContext::getFramenumber() const
return m_rd->cfra;
}
+float CompositorContext::get_render_width() const
+{
+ return getRenderData()->xsch * getRenderPercentageAsFactor();
+}
+
+float CompositorContext::get_render_height() const
+{
+ return getRenderData()->ysch * getRenderPercentageAsFactor();
+}
+
eExecutionModel CompositorContext::get_execution_model() const
{
if (U.experimental.use_full_frame_compositor) {
diff --git a/source/blender/compositor/intern/COM_CompositorContext.h b/source/blender/compositor/intern/COM_CompositorContext.h
index c6e83f93777..088883a2f46 100644
--- a/source/blender/compositor/intern/COM_CompositorContext.h
+++ b/source/blender/compositor/intern/COM_CompositorContext.h
@@ -288,6 +288,9 @@ class CompositorContext {
return m_rd->size * 0.01f;
}
+ float get_render_width() const;
+ float get_render_height() const;
+
/**
* Get active execution model.
*/
diff --git a/source/blender/compositor/intern/COM_Converter.cc b/source/blender/compositor/intern/COM_Converter.cc
index bd05a8e4ef0..f1634f46d2b 100644
--- a/source/blender/compositor/intern/COM_Converter.cc
+++ b/source/blender/compositor/intern/COM_Converter.cc
@@ -467,7 +467,9 @@ void COM_convert_canvas(NodeOperationBuilder &builder,
/* Data type conversions are executed before resolutions to ensure convert operations have
* resolution. This method have to ensure same datatypes are linked for new operations. */
BLI_assert(fromSocket->getDataType() == toSocket->getDataType());
+
ResizeMode mode = toSocket->getResizeMode();
+ BLI_assert(mode != ResizeMode::None);
NodeOperation *toOperation = &toSocket->getOperation();
const float toWidth = toOperation->getWidth();
@@ -477,13 +479,12 @@ void COM_convert_canvas(NodeOperationBuilder &builder,
const float fromHeight = fromOperation->getHeight();
bool doCenter = false;
bool doScale = false;
- float addX = (toWidth - fromWidth) / 2.0f;
- float addY = (toHeight - fromHeight) / 2.0f;
float scaleX = 0;
float scaleY = 0;
switch (mode) {
case ResizeMode::None:
+ case ResizeMode::Align:
break;
case ResizeMode::Center:
doCenter = true;
@@ -518,63 +519,67 @@ void COM_convert_canvas(NodeOperationBuilder &builder,
break;
}
- if (doCenter) {
- NodeOperation *first = nullptr;
- ScaleOperation *scaleOperation = nullptr;
- if (doScale) {
- scaleOperation = new ScaleRelativeOperation(fromSocket->getDataType());
- scaleOperation->getInputSocket(1)->setResizeMode(ResizeMode::None);
- scaleOperation->getInputSocket(2)->setResizeMode(ResizeMode::None);
- first = scaleOperation;
- SetValueOperation *sxop = new SetValueOperation();
- sxop->setValue(scaleX);
- builder.addLink(sxop->getOutputSocket(), scaleOperation->getInputSocket(1));
- SetValueOperation *syop = new SetValueOperation();
- syop->setValue(scaleY);
- builder.addLink(syop->getOutputSocket(), scaleOperation->getInputSocket(2));
- builder.addOperation(sxop);
- builder.addOperation(syop);
-
- const rcti &scale_canvas = fromOperation->get_canvas();
- scaleOperation->set_canvas(scale_canvas);
- sxop->set_canvas(scale_canvas);
- syop->set_canvas(scale_canvas);
- builder.addOperation(scaleOperation);
- }
+ NodeOperation *first = nullptr;
+ ScaleOperation *scaleOperation = nullptr;
+ if (doScale) {
+ scaleOperation = new ScaleRelativeOperation(fromSocket->getDataType());
+ scaleOperation->getInputSocket(1)->setResizeMode(ResizeMode::None);
+ scaleOperation->getInputSocket(2)->setResizeMode(ResizeMode::None);
+ first = scaleOperation;
+ SetValueOperation *sxop = new SetValueOperation();
+ sxop->setValue(scaleX);
+ builder.addLink(sxop->getOutputSocket(), scaleOperation->getInputSocket(1));
+ SetValueOperation *syop = new SetValueOperation();
+ syop->setValue(scaleY);
+ builder.addLink(syop->getOutputSocket(), scaleOperation->getInputSocket(2));
+ builder.addOperation(sxop);
+ builder.addOperation(syop);
- TranslateOperation *translateOperation = new TranslateOperation(toSocket->getDataType());
- translateOperation->getInputSocket(1)->setResizeMode(ResizeMode::None);
- translateOperation->getInputSocket(2)->setResizeMode(ResizeMode::None);
- if (!first) {
- first = translateOperation;
- }
- SetValueOperation *xop = new SetValueOperation();
- xop->setValue(addX);
- builder.addLink(xop->getOutputSocket(), translateOperation->getInputSocket(1));
- SetValueOperation *yop = new SetValueOperation();
- yop->setValue(addY);
- builder.addLink(yop->getOutputSocket(), translateOperation->getInputSocket(2));
- builder.addOperation(xop);
- builder.addOperation(yop);
+ const rcti &scale_canvas = fromOperation->get_canvas();
+ scaleOperation->set_canvas(scale_canvas);
+ sxop->set_canvas(scale_canvas);
+ syop->set_canvas(scale_canvas);
+ builder.addOperation(scaleOperation);
+ }
- const rcti &translate_canvas = toOperation->get_canvas();
- translateOperation->set_canvas(translate_canvas);
- xop->set_canvas(translate_canvas);
- yop->set_canvas(translate_canvas);
- builder.addOperation(translateOperation);
+ TranslateOperation *translateOperation = new TranslateOperation(toSocket->getDataType());
+ translateOperation->getInputSocket(1)->setResizeMode(ResizeMode::None);
+ translateOperation->getInputSocket(2)->setResizeMode(ResizeMode::None);
+ if (!first) {
+ first = translateOperation;
+ }
+ const float addX = doCenter ? (toWidth - fromWidth) / 2.0f : 0.0f;
+ const float addY = doCenter ? (toHeight - fromHeight) / 2.0f : 0.0f;
+ SetValueOperation *xop = new SetValueOperation();
+ xop->setValue(addX);
+ builder.addLink(xop->getOutputSocket(), translateOperation->getInputSocket(1));
+ SetValueOperation *yop = new SetValueOperation();
+ yop->setValue(addY);
+ builder.addLink(yop->getOutputSocket(), translateOperation->getInputSocket(2));
+ builder.addOperation(xop);
+ builder.addOperation(yop);
- if (doScale) {
- translateOperation->getInputSocket(0)->setResizeMode(ResizeMode::None);
- builder.addLink(scaleOperation->getOutputSocket(), translateOperation->getInputSocket(0));
- }
+ rcti translate_canvas = toOperation->get_canvas();
+ if (mode == ResizeMode::Align) {
+ translate_canvas.xmax = translate_canvas.xmin + fromWidth;
+ translate_canvas.ymax = translate_canvas.ymin + fromHeight;
+ }
+ translateOperation->set_canvas(translate_canvas);
+ xop->set_canvas(translate_canvas);
+ yop->set_canvas(translate_canvas);
+ builder.addOperation(translateOperation);
- /* remove previous link and replace */
- builder.removeInputLink(toSocket);
- first->getInputSocket(0)->setResizeMode(ResizeMode::None);
- toSocket->setResizeMode(ResizeMode::None);
- builder.addLink(fromSocket, first->getInputSocket(0));
- builder.addLink(translateOperation->getOutputSocket(), toSocket);
+ if (doScale) {
+ translateOperation->getInputSocket(0)->setResizeMode(ResizeMode::None);
+ builder.addLink(scaleOperation->getOutputSocket(), translateOperation->getInputSocket(0));
}
+
+ /* remove previous link and replace */
+ builder.removeInputLink(toSocket);
+ first->getInputSocket(0)->setResizeMode(ResizeMode::None);
+ toSocket->setResizeMode(ResizeMode::None);
+ builder.addLink(fromSocket, first->getInputSocket(0));
+ builder.addLink(translateOperation->getOutputSocket(), toSocket);
}
} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_FullFrameExecutionModel.cc b/source/blender/compositor/intern/COM_FullFrameExecutionModel.cc
index 957bbe24e5f..c44a168390b 100644
--- a/source/blender/compositor/intern/COM_FullFrameExecutionModel.cc
+++ b/source/blender/compositor/intern/COM_FullFrameExecutionModel.cc
@@ -74,34 +74,61 @@ void FullFrameExecutionModel::determine_areas_to_render_and_reads()
}
}
-Vector<MemoryBuffer *> FullFrameExecutionModel::get_input_buffers(NodeOperation *op)
+/**
+ * Returns input buffers with an offset relative to given output coordinates. Returned memory
+ * buffers must be deleted.
+ */
+Vector<MemoryBuffer *> FullFrameExecutionModel::get_input_buffers(NodeOperation *op,
+ const int output_x,
+ const int output_y)
{
const int num_inputs = op->getNumberOfInputSockets();
Vector<MemoryBuffer *> inputs_buffers(num_inputs);
for (int i = 0; i < num_inputs; i++) {
- NodeOperation *input_op = op->get_input_operation(i);
- inputs_buffers[i] = active_buffers_.get_rendered_buffer(input_op);
+ NodeOperation *input = op->get_input_operation(i);
+ const int offset_x = (input->get_canvas().xmin - op->get_canvas().xmin) + output_x;
+ const int offset_y = (input->get_canvas().ymin - op->get_canvas().ymin) + output_y;
+ MemoryBuffer *buf = active_buffers_.get_rendered_buffer(input);
+
+ rcti rect = buf->get_rect();
+ BLI_rcti_translate(&rect, offset_x, offset_y);
+ inputs_buffers[i] = new MemoryBuffer(
+ buf->getBuffer(), buf->get_num_channels(), rect, buf->is_a_single_elem());
}
return inputs_buffers;
}
-MemoryBuffer *FullFrameExecutionModel::create_operation_buffer(NodeOperation *op)
+MemoryBuffer *FullFrameExecutionModel::create_operation_buffer(NodeOperation *op,
+ const int output_x,
+ const int output_y)
{
+ rcti rect;
+ BLI_rcti_init(&rect, output_x, output_x + op->getWidth(), output_y, output_y + op->getHeight());
+
const DataType data_type = op->getOutputSocket(0)->getDataType();
const bool is_a_single_elem = op->get_flags().is_constant_operation;
- return new MemoryBuffer(data_type, op->get_canvas(), is_a_single_elem);
+ return new MemoryBuffer(data_type, rect, is_a_single_elem);
}
void FullFrameExecutionModel::render_operation(NodeOperation *op)
{
- Vector<MemoryBuffer *> input_bufs = get_input_buffers(op);
+ /* Output has no offset for easier image algorithms implementation on operations. */
+ constexpr int output_x = 0;
+ constexpr int output_y = 0;
const bool has_outputs = op->getNumberOfOutputSockets() > 0;
- MemoryBuffer *op_buf = has_outputs ? create_operation_buffer(op) : nullptr;
+ MemoryBuffer *op_buf = has_outputs ? create_operation_buffer(op, output_x, output_y) : nullptr;
if (op->getWidth() > 0 && op->getHeight() > 0) {
- Span<rcti> areas = active_buffers_.get_areas_to_render(op);
+ Vector<MemoryBuffer *> input_bufs = get_input_buffers(op, output_x, output_y);
+ const int op_offset_x = output_x - op->get_canvas().xmin;
+ const int op_offset_y = output_y - op->get_canvas().ymin;
+ Span<rcti> areas = active_buffers_.get_areas_to_render(op, op_offset_x, op_offset_y);
op->render(op_buf, areas, input_bufs);
DebugInfo::operation_rendered(op, op_buf);
+
+ for (MemoryBuffer *buf : input_bufs) {
+ delete buf;
+ }
}
/* Even if operation has no resolution set the empty buffer. It will be clipped with a
* TranslateOperation from convert resolutions if linked to an operation with resolution. */
@@ -187,7 +214,8 @@ void FullFrameExecutionModel::determine_areas_to_render(NodeOperation *output_op
std::pair<NodeOperation *, rcti> pair = stack.pop_last();
NodeOperation *operation = pair.first;
const rcti &render_area = pair.second;
- if (active_buffers_.is_area_registered(operation, render_area)) {
+ if (BLI_rcti_is_empty(&render_area) ||
+ active_buffers_.is_area_registered(operation, render_area)) {
continue;
}
@@ -239,9 +267,8 @@ void FullFrameExecutionModel::get_output_render_area(NodeOperation *output_op, r
BLI_assert(output_op->isOutputOperation(context_.isRendering()));
/* By default return operation bounds (no border). */
- const int op_width = output_op->getWidth();
- const int op_height = output_op->getHeight();
- BLI_rcti_init(&r_area, 0, op_width, 0, op_height);
+ rcti canvas = output_op->get_canvas();
+ r_area = canvas;
const bool has_viewer_border = border_.use_viewer_border &&
(output_op->get_flags().is_viewer_operation ||
@@ -251,12 +278,13 @@ void FullFrameExecutionModel::get_output_render_area(NodeOperation *output_op, r
/* Get border with normalized coordinates. */
const rctf *norm_border = has_viewer_border ? border_.viewer_border : border_.render_border;
- /* Return de-normalized border. */
- BLI_rcti_init(&r_area,
- norm_border->xmin * op_width,
- norm_border->xmax * op_width,
- norm_border->ymin * op_height,
- norm_border->ymax * op_height);
+ /* Return de-normalized border within canvas. */
+ const int w = output_op->getWidth();
+ const int h = output_op->getHeight();
+ r_area.xmin = canvas.xmin + norm_border->xmin * w;
+ r_area.xmax = canvas.xmin + norm_border->xmax * w;
+ r_area.ymin = canvas.ymin + norm_border->ymin * h;
+ r_area.ymax = canvas.ymin + norm_border->ymax * h;
}
}
diff --git a/source/blender/compositor/intern/COM_FullFrameExecutionModel.h b/source/blender/compositor/intern/COM_FullFrameExecutionModel.h
index f75d4f1afdc..66dfb8f052c 100644
--- a/source/blender/compositor/intern/COM_FullFrameExecutionModel.h
+++ b/source/blender/compositor/intern/COM_FullFrameExecutionModel.h
@@ -61,8 +61,10 @@ class FullFrameExecutionModel : public ExecutionModel {
void determine_areas_to_render_and_reads();
void render_operations();
void render_output_dependencies(NodeOperation *output_op);
- Vector<MemoryBuffer *> get_input_buffers(NodeOperation *op);
- MemoryBuffer *create_operation_buffer(NodeOperation *op);
+ Vector<MemoryBuffer *> get_input_buffers(NodeOperation *op,
+ const int output_x,
+ const int output_y);
+ MemoryBuffer *create_operation_buffer(NodeOperation *op, const int output_x, const int output_y);
void render_operation(NodeOperation *op);
void operation_finished(NodeOperation *operation);
diff --git a/source/blender/compositor/intern/COM_MemoryBuffer.cc b/source/blender/compositor/intern/COM_MemoryBuffer.cc
index 1fbf502fea6..05d3971f8ea 100644
--- a/source/blender/compositor/intern/COM_MemoryBuffer.cc
+++ b/source/blender/compositor/intern/COM_MemoryBuffer.cc
@@ -122,6 +122,8 @@ void MemoryBuffer::set_strides()
this->elem_stride = m_num_channels;
this->row_stride = getWidth() * m_num_channels;
}
+ to_positive_x_stride_ = m_rect.xmin < 0 ? -m_rect.xmin + 1 : (m_rect.xmin == 0 ? 1 : 0);
+ to_positive_y_stride_ = m_rect.ymin < 0 ? -m_rect.ymin + 1 : (m_rect.ymin == 0 ? 1 : 0);
}
void MemoryBuffer::clear()
diff --git a/source/blender/compositor/intern/COM_MemoryBuffer.h b/source/blender/compositor/intern/COM_MemoryBuffer.h
index f730d53acec..a265c7b813e 100644
--- a/source/blender/compositor/intern/COM_MemoryBuffer.h
+++ b/source/blender/compositor/intern/COM_MemoryBuffer.h
@@ -114,6 +114,12 @@ class MemoryBuffer {
*/
bool owns_data_;
+ /** Stride to make any x coordinate within buffer positive (non-zero). */
+ int to_positive_x_stride_;
+
+ /** Stride to make any y coordinate within buffer positive (non-zero). */
+ int to_positive_y_stride_;
+
public:
/**
* \brief construct new temporarily MemoryBuffer for an area
@@ -176,7 +182,7 @@ class MemoryBuffer {
*/
float *get_elem(int x, int y)
{
- BLI_assert(x >= m_rect.xmin && x < m_rect.xmax && y >= m_rect.ymin && y < m_rect.ymax);
+ BLI_assert(has_coords(x, y));
return m_buffer + get_coords_offset(x, y);
}
@@ -185,7 +191,7 @@ class MemoryBuffer {
*/
const float *get_elem(int x, int y) const
{
- BLI_assert(x >= m_rect.xmin && x < m_rect.xmax && y >= m_rect.ymin && y < m_rect.ymax);
+ BLI_assert(has_coords(x, y));
return m_buffer + get_coords_offset(x, y);
}
@@ -196,7 +202,7 @@ class MemoryBuffer {
void read_elem_checked(int x, int y, float *out) const
{
- if (x < m_rect.xmin || x >= m_rect.xmax || y < m_rect.ymin || y >= m_rect.ymax) {
+ if (!has_coords(x, y)) {
clear_elem(out);
}
else {
@@ -206,12 +212,7 @@ class MemoryBuffer {
void read_elem_checked(float x, float y, float *out) const
{
- if (x < m_rect.xmin || x >= m_rect.xmax || y < m_rect.ymin || y >= m_rect.ymax) {
- clear_elem(out);
- }
- else {
- read_elem(x, y, out);
- }
+ read_elem_checked(floor_x(x), floor_y(y), out);
}
void read_elem_bilinear(float x, float y, float *out) const
@@ -286,8 +287,7 @@ class MemoryBuffer {
*/
float &get_value(int x, int y, int channel)
{
- BLI_assert(x >= m_rect.xmin && x < m_rect.xmax && y >= m_rect.ymin && y < m_rect.ymax &&
- channel >= 0 && channel < m_num_channels);
+ BLI_assert(has_coords(x, y) && channel >= 0 && channel < m_num_channels);
return m_buffer[get_coords_offset(x, y) + channel];
}
@@ -296,8 +296,7 @@ class MemoryBuffer {
*/
const float &get_value(int x, int y, int channel) const
{
- BLI_assert(x >= m_rect.xmin && x < m_rect.xmax && y >= m_rect.ymin && y < m_rect.ymax &&
- channel >= 0 && channel < m_num_channels);
+ BLI_assert(has_coords(x, y) && channel >= 0 && channel < m_num_channels);
return m_buffer[get_coords_offset(x, y) + channel];
}
@@ -306,7 +305,7 @@ class MemoryBuffer {
*/
const float *get_row_end(int y) const
{
- BLI_assert(y >= 0 && y < getHeight());
+ BLI_assert(has_y(y));
return m_buffer + (is_a_single_elem() ? m_num_channels : get_coords_offset(getWidth(), y));
}
@@ -681,6 +680,33 @@ class MemoryBuffer {
return y - m_rect.ymin;
}
+ template<typename T> bool has_coords(T x, T y) const
+ {
+ return has_x(x) && has_y(y);
+ }
+
+ template<typename T> bool has_x(T x) const
+ {
+ return x >= m_rect.xmin && x < m_rect.xmax;
+ }
+
+ template<typename T> bool has_y(T y) const
+ {
+ return y >= m_rect.ymin && y < m_rect.ymax;
+ }
+
+ /* Fast floor functions. The caller should check result is within buffer bounds. It ceils in near
+ * cases and when given coordinate is negative and less than buffer rect `min - 1`. */
+ int floor_x(float x) const
+ {
+ return (int)(x + to_positive_x_stride_) - to_positive_x_stride_;
+ }
+
+ int floor_y(float y) const
+ {
+ return (int)(y + to_positive_y_stride_) - to_positive_y_stride_;
+ }
+
void copy_single_elem_from(const MemoryBuffer *src,
int channel_offset,
int elem_size,
diff --git a/source/blender/compositor/intern/COM_NodeOperation.cc b/source/blender/compositor/intern/COM_NodeOperation.cc
index ff232efdb08..a6a395261f2 100644
--- a/source/blender/compositor/intern/COM_NodeOperation.cc
+++ b/source/blender/compositor/intern/COM_NodeOperation.cc
@@ -40,6 +40,24 @@ NodeOperation::NodeOperation()
this->m_btree = nullptr;
}
+/** Get constant value when operation is constant, otherwise return default_value. */
+float NodeOperation::get_constant_value_default(float default_value)
+{
+ BLI_assert(m_outputs.size() > 0 && getOutputSocket()->getDataType() == DataType::Value);
+ return *get_constant_elem_default(&default_value);
+}
+
+/** Get constant elem when operation is constant, otherwise return default_elem. */
+const float *NodeOperation::get_constant_elem_default(const float *default_elem)
+{
+ BLI_assert(m_outputs.size() > 0);
+ if (get_flags().is_constant_operation) {
+ return static_cast<ConstantOperation *>(this)->get_constant_elem();
+ }
+
+ return default_elem;
+}
+
/**
* Generate a hash that identifies the operation result in the current execution.
* Requires `hash_output_params` to be implemented, otherwise `std::nullopt` is returned.
@@ -186,6 +204,28 @@ void NodeOperation::deinitExecution()
{
/* pass */
}
+
+void NodeOperation::set_canvas(const rcti &canvas_area)
+{
+ canvas_ = canvas_area;
+ flags.is_canvas_set = true;
+}
+
+const rcti &NodeOperation::get_canvas() const
+{
+ return canvas_;
+}
+
+/**
+ * Mainly used for re-determining canvas of constant operations in cases where preferred canvas
+ * depends on the constant element.
+ */
+void NodeOperation::unset_canvas()
+{
+ BLI_assert(m_inputs.size() == 0);
+ flags.is_canvas_set = false;
+}
+
SocketReader *NodeOperation::getInputSocketReader(unsigned int inputSocketIndex)
{
return this->getInputSocket(inputSocketIndex)->getReader();
diff --git a/source/blender/compositor/intern/COM_NodeOperation.h b/source/blender/compositor/intern/COM_NodeOperation.h
index 9f113b60345..f507665bee3 100644
--- a/source/blender/compositor/intern/COM_NodeOperation.h
+++ b/source/blender/compositor/intern/COM_NodeOperation.h
@@ -62,9 +62,13 @@ enum class ResizeMode {
/** \brief Center the input image to the center of the working area of the node, no resizing
* occurs */
Center = NS_CR_CENTER,
- /** \brief The bottom left of the input image is the bottom left of the working area of the node,
- * no resizing occurs */
+ /** No resizing or translation. */
None = NS_CR_NONE,
+ /**
+ * Input image is translated so that its bottom left matches the bottom left of the working area
+ * of the node, no resizing occurs.
+ */
+ Align = 100,
/** \brief Fit the width of the input image to the width of the working area of the node */
FitWidth = NS_CR_FIT_WIDTH,
/** \brief Fit the height of the input image to the height of the working area of the node */
@@ -381,6 +385,9 @@ class NodeOperation {
return m_id;
}
+ float get_constant_value_default(float default_value);
+ const float *get_constant_elem_default(const float *default_elem);
+
const NodeOperationFlags get_flags() const
{
return flags;
@@ -507,18 +514,9 @@ class NodeOperation {
}
virtual void deinitExecution();
- void set_canvas(const rcti &canvas_area)
- {
- if (!this->flags.is_canvas_set) {
- canvas_ = canvas_area;
- flags.is_canvas_set = true;
- }
- }
-
- const rcti &get_canvas() const
- {
- return canvas_;
- }
+ void set_canvas(const rcti &canvas_area);
+ const rcti &get_canvas() const;
+ void unset_canvas();
/**
* \brief is this operation the active viewer output
@@ -575,12 +573,12 @@ class NodeOperation {
unsigned int getWidth() const
{
- return BLI_rcti_size_x(&canvas_);
+ return BLI_rcti_size_x(&get_canvas());
}
unsigned int getHeight() const
{
- return BLI_rcti_size_y(&canvas_);
+ return BLI_rcti_size_y(&get_canvas());
}
inline void readSampled(float result[4], float x, float y, PixelSampler sampler)
@@ -677,6 +675,7 @@ class NodeOperation {
void addInputSocket(DataType datatype, ResizeMode resize_mode = ResizeMode::Center);
void addOutputSocket(DataType datatype);
+ /* TODO(manzanilla): to be removed with tiled implementation. */
void setWidth(unsigned int width)
{
canvas_.xmax = canvas_.xmin + width;
@@ -687,6 +686,7 @@ class NodeOperation {
canvas_.ymax = canvas_.ymin + height;
this->flags.is_canvas_set = true;
}
+
SocketReader *getInputSocketReader(unsigned int inputSocketindex);
NodeOperation *getInputOperation(unsigned int inputSocketindex);
diff --git a/source/blender/compositor/intern/COM_NodeOperationBuilder.cc b/source/blender/compositor/intern/COM_NodeOperationBuilder.cc
index 3791d05602d..acb7f61f6dd 100644
--- a/source/blender/compositor/intern/COM_NodeOperationBuilder.cc
+++ b/source/blender/compositor/intern/COM_NodeOperationBuilder.cc
@@ -33,6 +33,7 @@
#include "COM_SetValueOperation.h"
#include "COM_SetVectorOperation.h"
#include "COM_SocketProxyOperation.h"
+#include "COM_TranslateOperation.h"
#include "COM_ViewerOperation.h"
#include "COM_WriteBufferOperation.h"
@@ -448,9 +449,19 @@ void NodeOperationBuilder::determine_canvases()
Vector<Link> convert_links;
for (const Link &link : m_links) {
if (link.to()->getResizeMode() != ResizeMode::None) {
- NodeOperation &from_op = link.from()->getOperation();
- NodeOperation &to_op = link.to()->getOperation();
- if (from_op.getWidth() != to_op.getWidth() || from_op.getHeight() != to_op.getHeight()) {
+ const rcti &from_canvas = link.from()->getOperation().get_canvas();
+ const rcti &to_canvas = link.to()->getOperation().get_canvas();
+
+ bool needs_conversion;
+ if (link.to()->getResizeMode() == ResizeMode::Align) {
+ needs_conversion = from_canvas.xmin != to_canvas.xmin ||
+ from_canvas.ymin != to_canvas.ymin;
+ }
+ else {
+ needs_conversion = !BLI_rcti_compare(&from_canvas, &to_canvas);
+ }
+
+ if (needs_conversion) {
convert_links.append(link);
}
}
diff --git a/source/blender/compositor/intern/COM_SharedOperationBuffers.cc b/source/blender/compositor/intern/COM_SharedOperationBuffers.cc
index 7e0486b0f54..55153bd4f0a 100644
--- a/source/blender/compositor/intern/COM_SharedOperationBuffers.cc
+++ b/source/blender/compositor/intern/COM_SharedOperationBuffers.cc
@@ -76,9 +76,17 @@ void SharedOperationBuffers::register_read(NodeOperation *read_op)
/**
* Get registered areas given operation needs to render.
*/
-blender::Span<rcti> SharedOperationBuffers::get_areas_to_render(NodeOperation *op)
+Vector<rcti> SharedOperationBuffers::get_areas_to_render(NodeOperation *op,
+ const int offset_x,
+ const int offset_y)
{
- return get_buffer_data(op).render_areas.as_span();
+ Span<rcti> render_areas = get_buffer_data(op).render_areas.as_span();
+ Vector<rcti> dst_areas;
+ for (rcti dst : render_areas) {
+ BLI_rcti_translate(&dst, offset_x, offset_y);
+ dst_areas.append(std::move(dst));
+ }
+ return dst_areas;
}
/**
diff --git a/source/blender/compositor/intern/COM_SharedOperationBuffers.h b/source/blender/compositor/intern/COM_SharedOperationBuffers.h
index f7763cd8ae4..4461ba75cbe 100644
--- a/source/blender/compositor/intern/COM_SharedOperationBuffers.h
+++ b/source/blender/compositor/intern/COM_SharedOperationBuffers.h
@@ -53,7 +53,7 @@ class SharedOperationBuffers {
bool has_registered_reads(NodeOperation *op);
void register_read(NodeOperation *read_op);
- blender::Span<rcti> get_areas_to_render(NodeOperation *op);
+ Vector<rcti> get_areas_to_render(NodeOperation *op, int offset_x, int offset_y);
bool is_operation_rendered(NodeOperation *op);
void set_rendered_buffer(NodeOperation *op, std::unique_ptr<MemoryBuffer> buffer);
MemoryBuffer *get_rendered_buffer(NodeOperation *op);
diff --git a/source/blender/compositor/nodes/COM_BoxMaskNode.cc b/source/blender/compositor/nodes/COM_BoxMaskNode.cc
index 14f42cc42f7..8017e063a69 100644
--- a/source/blender/compositor/nodes/COM_BoxMaskNode.cc
+++ b/source/blender/compositor/nodes/COM_BoxMaskNode.cc
@@ -62,7 +62,7 @@ void BoxMaskNode::convertToOperations(NodeConverter &converter,
scaleOperation->setOffset(0.0f, 0.0f);
scaleOperation->setNewWidth(rd->xsch * render_size_factor);
scaleOperation->setNewHeight(rd->ysch * render_size_factor);
- scaleOperation->getInputSocket(0)->setResizeMode(ResizeMode::None);
+ scaleOperation->getInputSocket(0)->setResizeMode(ResizeMode::Align);
converter.addOperation(scaleOperation);
converter.addLink(valueOperation->getOutputSocket(0), scaleOperation->getInputSocket(0));
diff --git a/source/blender/compositor/nodes/COM_EllipseMaskNode.cc b/source/blender/compositor/nodes/COM_EllipseMaskNode.cc
index 3b4f5ca8c94..752597ef937 100644
--- a/source/blender/compositor/nodes/COM_EllipseMaskNode.cc
+++ b/source/blender/compositor/nodes/COM_EllipseMaskNode.cc
@@ -62,7 +62,7 @@ void EllipseMaskNode::convertToOperations(NodeConverter &converter,
scaleOperation->setOffset(0.0f, 0.0f);
scaleOperation->setNewWidth(rd->xsch * render_size_factor);
scaleOperation->setNewHeight(rd->ysch * render_size_factor);
- scaleOperation->getInputSocket(0)->setResizeMode(ResizeMode::None);
+ scaleOperation->getInputSocket(0)->setResizeMode(ResizeMode::Align);
converter.addOperation(scaleOperation);
converter.addLink(valueOperation->getOutputSocket(0), scaleOperation->getInputSocket(0));
diff --git a/source/blender/compositor/nodes/COM_ScaleNode.cc b/source/blender/compositor/nodes/COM_ScaleNode.cc
index 819d2e72f30..e485d26d32d 100644
--- a/source/blender/compositor/nodes/COM_ScaleNode.cc
+++ b/source/blender/compositor/nodes/COM_ScaleNode.cc
@@ -52,6 +52,9 @@ void ScaleNode::convertToOperations(NodeConverter &converter,
converter.mapOutputSocket(outputSocket, operation->getOutputSocket(0));
operation->setVariableSize(inputXSocket->isLinked() || inputYSocket->isLinked());
+ operation->set_scale_canvas_max_size(context.get_render_width(),
+ context.get_render_height());
+
break;
}
case CMP_SCALE_SCENEPERCENT: {
@@ -68,6 +71,8 @@ void ScaleNode::convertToOperations(NodeConverter &converter,
converter.mapOutputSocket(outputSocket, operation->getOutputSocket(0));
operation->setVariableSize(inputXSocket->isLinked() || inputYSocket->isLinked());
+ operation->set_scale_canvas_max_size(context.get_render_width(),
+ context.get_render_height());
break;
}
@@ -81,7 +86,7 @@ void ScaleNode::convertToOperations(NodeConverter &converter,
operation->setOffset(bnode->custom3, bnode->custom4);
operation->setNewWidth(rd->xsch * render_size_factor);
operation->setNewHeight(rd->ysch * render_size_factor);
- operation->getInputSocket(0)->setResizeMode(ResizeMode::None);
+ operation->getInputSocket(0)->setResizeMode(ResizeMode::Align);
converter.addOperation(operation);
converter.mapInputSocket(inputSocket, operation->getInputSocket(0));
@@ -102,6 +107,8 @@ void ScaleNode::convertToOperations(NodeConverter &converter,
converter.mapOutputSocket(outputSocket, operation->getOutputSocket(0));
operation->setVariableSize(inputXSocket->isLinked() || inputYSocket->isLinked());
+ operation->set_scale_canvas_max_size(context.get_render_width(),
+ context.get_render_height());
break;
}
diff --git a/source/blender/compositor/nodes/COM_Stabilize2dNode.cc b/source/blender/compositor/nodes/COM_Stabilize2dNode.cc
index 90f62c6d562..9310e0e964e 100644
--- a/source/blender/compositor/nodes/COM_Stabilize2dNode.cc
+++ b/source/blender/compositor/nodes/COM_Stabilize2dNode.cc
@@ -127,6 +127,8 @@ void Stabilize2dNode::convertToOperations(NodeConverter &converter,
transform_op->set_sampler(sampler);
transform_op->set_convert_rotate_degree_to_rad(false);
transform_op->set_invert(invert);
+ transform_op->set_scale_canvas_max_size(context.get_render_width(),
+ context.get_render_height());
converter.addOperation(transform_op);
converter.mapInputSocket(imageInput, transform_op->getInputSocket(0));
converter.addLink(xAttribute->getOutputSocket(), transform_op->getInputSocket(1));
@@ -134,6 +136,7 @@ void Stabilize2dNode::convertToOperations(NodeConverter &converter,
converter.addLink(angleAttribute->getOutputSocket(), transform_op->getInputSocket(3));
converter.addLink(scaleAttribute->getOutputSocket(), transform_op->getInputSocket(4));
converter.mapOutputSocket(getOutputSocket(), transform_op->getOutputSocket());
+ break;
}
}
}
diff --git a/source/blender/compositor/nodes/COM_TransformNode.cc b/source/blender/compositor/nodes/COM_TransformNode.cc
index d2fb7b54633..aba4d74c2e3 100644
--- a/source/blender/compositor/nodes/COM_TransformNode.cc
+++ b/source/blender/compositor/nodes/COM_TransformNode.cc
@@ -75,6 +75,7 @@ void TransformNode::convertToOperations(NodeConverter &converter,
case eExecutionModel::FullFrame: {
TransformOperation *op = new TransformOperation();
op->set_sampler((PixelSampler)this->getbNode()->custom1);
+ op->set_scale_canvas_max_size(context.get_render_width(), context.get_render_height());
converter.addOperation(op);
converter.mapInputSocket(imageInput, op->getInputSocket(0));
diff --git a/source/blender/compositor/nodes/COM_TranslateNode.cc b/source/blender/compositor/nodes/COM_TranslateNode.cc
index 3a3e98c3472..165a03baf41 100644
--- a/source/blender/compositor/nodes/COM_TranslateNode.cc
+++ b/source/blender/compositor/nodes/COM_TranslateNode.cc
@@ -41,7 +41,9 @@ void TranslateNode::convertToOperations(NodeConverter &converter,
NodeInput *inputYSocket = this->getInputSocket(2);
NodeOutput *outputSocket = this->getOutputSocket(0);
- TranslateOperation *operation = new TranslateOperation();
+ TranslateOperation *operation = context.get_execution_model() == eExecutionModel::Tiled ?
+ new TranslateOperation() :
+ new TranslateCanvasOperation();
operation->set_wrapping(data->wrap_axis);
if (data->relative) {
const RenderData *rd = context.getRenderData();
diff --git a/source/blender/compositor/operations/COM_BokehBlurOperation.cc b/source/blender/compositor/operations/COM_BokehBlurOperation.cc
index 3f61a300849..93482dd2a54 100644
--- a/source/blender/compositor/operations/COM_BokehBlurOperation.cc
+++ b/source/blender/compositor/operations/COM_BokehBlurOperation.cc
@@ -34,7 +34,7 @@ constexpr int SIZE_INPUT_INDEX = 3;
BokehBlurOperation::BokehBlurOperation()
{
this->addInputSocket(DataType::Color);
- this->addInputSocket(DataType::Color, ResizeMode::None);
+ this->addInputSocket(DataType::Color, ResizeMode::Align);
this->addInputSocket(DataType::Value);
this->addInputSocket(DataType::Value);
this->addOutputSocket(DataType::Color);
diff --git a/source/blender/compositor/operations/COM_CalculateMeanOperation.cc b/source/blender/compositor/operations/COM_CalculateMeanOperation.cc
index 5fff4da62ae..a573a9d7eed 100644
--- a/source/blender/compositor/operations/COM_CalculateMeanOperation.cc
+++ b/source/blender/compositor/operations/COM_CalculateMeanOperation.cc
@@ -27,7 +27,7 @@ namespace blender::compositor {
CalculateMeanOperation::CalculateMeanOperation()
{
- this->addInputSocket(DataType::Color, ResizeMode::None);
+ this->addInputSocket(DataType::Color, ResizeMode::Align);
this->addOutputSocket(DataType::Value);
this->m_imageReader = nullptr;
this->m_iscalculated = false;
diff --git a/source/blender/compositor/operations/COM_CropOperation.cc b/source/blender/compositor/operations/COM_CropOperation.cc
index 73805b76864..6ac30c22ad1 100644
--- a/source/blender/compositor/operations/COM_CropOperation.cc
+++ b/source/blender/compositor/operations/COM_CropOperation.cc
@@ -23,7 +23,7 @@ namespace blender::compositor {
CropBaseOperation::CropBaseOperation()
{
- this->addInputSocket(DataType::Color, ResizeMode::None);
+ this->addInputSocket(DataType::Color, ResizeMode::Align);
this->addOutputSocket(DataType::Color);
this->m_inputOperation = nullptr;
this->m_settings = nullptr;
diff --git a/source/blender/compositor/operations/COM_MapUVOperation.cc b/source/blender/compositor/operations/COM_MapUVOperation.cc
index 5062b0c42cb..ba38e583b30 100644
--- a/source/blender/compositor/operations/COM_MapUVOperation.cc
+++ b/source/blender/compositor/operations/COM_MapUVOperation.cc
@@ -23,7 +23,7 @@ namespace blender::compositor {
MapUVOperation::MapUVOperation()
{
- this->addInputSocket(DataType::Color, ResizeMode::None);
+ this->addInputSocket(DataType::Color, ResizeMode::Align);
this->addInputSocket(DataType::Vector);
this->addOutputSocket(DataType::Color);
this->m_alpha = 0.0f;
diff --git a/source/blender/compositor/operations/COM_PlaneDistortCommonOperation.cc b/source/blender/compositor/operations/COM_PlaneDistortCommonOperation.cc
index ab45899b7f5..31ef41789fd 100644
--- a/source/blender/compositor/operations/COM_PlaneDistortCommonOperation.cc
+++ b/source/blender/compositor/operations/COM_PlaneDistortCommonOperation.cc
@@ -73,7 +73,7 @@ BLI_INLINE void warpCoord(float x, float y, float matrix[3][3], float uv[2], flo
PlaneDistortWarpImageOperation::PlaneDistortWarpImageOperation() : PlaneDistortBaseOperation()
{
- this->addInputSocket(DataType::Color, ResizeMode::None);
+ this->addInputSocket(DataType::Color, ResizeMode::Align);
this->addOutputSocket(DataType::Color);
this->m_pixelReader = nullptr;
this->flags.complex = true;
diff --git a/source/blender/compositor/operations/COM_PreviewOperation.cc b/source/blender/compositor/operations/COM_PreviewOperation.cc
index 7b1dd89bd75..b6b04385f43 100644
--- a/source/blender/compositor/operations/COM_PreviewOperation.cc
+++ b/source/blender/compositor/operations/COM_PreviewOperation.cc
@@ -41,7 +41,7 @@ PreviewOperation::PreviewOperation(const ColorManagedViewSettings *viewSettings,
const unsigned int defaultHeight)
{
- this->addInputSocket(DataType::Color, ResizeMode::None);
+ this->addInputSocket(DataType::Color, ResizeMode::Align);
this->m_preview = nullptr;
this->m_outputBuffer = nullptr;
this->m_input = nullptr;
diff --git a/source/blender/compositor/operations/COM_RotateOperation.cc b/source/blender/compositor/operations/COM_RotateOperation.cc
index 8578e5c3269..43b90642bc5 100644
--- a/source/blender/compositor/operations/COM_RotateOperation.cc
+++ b/source/blender/compositor/operations/COM_RotateOperation.cc
@@ -25,8 +25,8 @@ namespace blender::compositor {
RotateOperation::RotateOperation()
{
- this->addInputSocket(DataType::Color);
- this->addInputSocket(DataType::Value);
+ this->addInputSocket(DataType::Color, ResizeMode::Align);
+ this->addInputSocket(DataType::Value, ResizeMode::Align);
this->addOutputSocket(DataType::Color);
this->set_canvas_input_index(0);
this->m_imageSocket = nullptr;
@@ -36,6 +36,30 @@ RotateOperation::RotateOperation()
sampler_ = PixelSampler::Bilinear;
}
+void RotateOperation::get_rotation_center(const rcti &area, float &r_x, float &r_y)
+{
+ r_x = area.xmin + (BLI_rcti_size_x(&area) - 1) / 2.0;
+ r_y = area.ymin + (BLI_rcti_size_y(&area) - 1) / 2.0;
+}
+
+void RotateOperation::get_rotation_center(const float width,
+ const float height,
+ float &r_x,
+ float &r_y)
+{
+ r_x = (width - 1) / 2.0;
+ r_y = (height - 1) / 2.0;
+}
+
+void RotateOperation::get_rotation_offset(const rcti &input_canvas,
+ const rcti &rotate_canvas,
+ float &r_offset_x,
+ float &r_offset_y)
+{
+ r_offset_x = (BLI_rcti_size_x(&input_canvas) - BLI_rcti_size_x(&rotate_canvas)) / 2.0f;
+ r_offset_y = (BLI_rcti_size_y(&input_canvas) - BLI_rcti_size_y(&rotate_canvas)) / 2.0f;
+}
+
void RotateOperation::get_area_rotation_bounds(const rcti &area,
const float center_x,
const float center_y,
@@ -67,10 +91,44 @@ void RotateOperation::get_area_rotation_bounds(const rcti &area,
r_bounds.ymax = ceil(maxy);
}
+void RotateOperation::get_rotation_area_of_interest(const rcti &input_canvas,
+ const rcti &rotate_canvas,
+ const float sine,
+ const float cosine,
+ const rcti &output_area,
+ rcti &r_input_area)
+{
+ float center_x, center_y;
+ get_rotation_center(input_canvas, center_x, center_y);
+
+ float rotate_offset_x, rotate_offset_y;
+ get_rotation_offset(input_canvas, rotate_canvas, rotate_offset_x, rotate_offset_y);
+
+ r_input_area = output_area;
+ BLI_rcti_translate(&r_input_area, rotate_offset_x, rotate_offset_y);
+ get_area_rotation_bounds(r_input_area, center_x, center_y, sine, cosine, r_input_area);
+}
+
+void RotateOperation::get_rotation_canvas(const rcti &input_canvas,
+ const float sine,
+ const float cosine,
+ rcti &r_canvas)
+{
+ float center_x, center_y;
+ get_rotation_center(input_canvas, center_x, center_y);
+
+ rcti rot_bounds;
+ get_area_rotation_bounds(input_canvas, center_x, center_y, sine, cosine, rot_bounds);
+ r_canvas = input_canvas;
+ r_canvas.xmax = input_canvas.xmin + BLI_rcti_size_x(&rot_bounds);
+ r_canvas.ymax = input_canvas.ymin + BLI_rcti_size_y(&rot_bounds);
+}
+
void RotateOperation::init_data()
{
- this->m_centerX = (getWidth() - 1) / 2.0;
- this->m_centerY = (getHeight() - 1) / 2.0;
+ if (execution_model_ == eExecutionModel::Tiled) {
+ get_rotation_center(get_canvas(), m_centerX, m_centerY);
+ }
}
void RotateOperation::initExecution()
@@ -94,11 +152,7 @@ inline void RotateOperation::ensureDegree()
this->m_degreeSocket->readSampled(degree, 0, 0, PixelSampler::Nearest);
break;
case eExecutionModel::FullFrame:
- NodeOperation *degree_op = getInputOperation(DEGREE_INPUT_INDEX);
- const bool is_constant_degree = degree_op->get_flags().is_constant_operation;
- degree[0] = is_constant_degree ?
- static_cast<ConstantOperation *>(degree_op)->get_constant_elem()[0] :
- 0.0f;
+ degree[0] = get_input_operation(DEGREE_INPUT_INDEX)->get_constant_value_default(0.0f);
break;
}
@@ -159,6 +213,25 @@ bool RotateOperation::determineDependingAreaOfInterest(rcti *input,
return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output);
}
+void RotateOperation::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 unused;
+ getInputSocket(DEGREE_INPUT_INDEX)->determine_canvas(r_area, unused);
+
+ ensureDegree();
+
+ get_rotation_canvas(r_area, m_sine, m_cosine, r_area);
+ }
+}
+
void RotateOperation::get_area_of_interest(const int input_idx,
const rcti &output_area,
rcti &r_input_area)
@@ -169,7 +242,10 @@ void RotateOperation::get_area_of_interest(const int input_idx,
}
ensureDegree();
- get_area_rotation_bounds(output_area, m_centerX, m_centerY, m_sine, m_cosine, r_input_area);
+
+ const rcti &input_image_canvas = get_input_operation(IMAGE_INPUT_INDEX)->get_canvas();
+ get_rotation_area_of_interest(
+ input_image_canvas, this->get_canvas(), m_sine, m_cosine, output_area, r_input_area);
expand_area_for_sampler(r_input_area, sampler_);
}
@@ -177,12 +253,22 @@ void RotateOperation::update_memory_buffer_partial(MemoryBuffer *output,
const rcti &area,
Span<MemoryBuffer *> inputs)
{
- ensureDegree();
const MemoryBuffer *input_img = inputs[IMAGE_INPUT_INDEX];
+
+ NodeOperation *image_op = get_input_operation(IMAGE_INPUT_INDEX);
+ const float image_width = image_op->getWidth();
+ const float image_height = image_op->getHeight();
+
+ float center_x, center_y;
+ get_rotation_center(image_width, image_height, center_x, center_y);
+ float rotate_offset_x, rotate_offset_y;
+ get_rotation_offset(
+ image_op->get_canvas(), this->get_canvas(), rotate_offset_x, rotate_offset_y);
+
for (BuffersIterator<float> it = output->iterate_with({}, area); !it.is_end(); ++it) {
- float x = it.x;
- float y = it.y;
- rotate_coords(x, y, m_centerX, m_centerY, m_sine, m_cosine);
+ float x = rotate_offset_x + it.x;
+ float y = rotate_offset_y + it.y;
+ rotate_coords(x, y, center_x, center_y, m_sine, m_cosine);
input_img->read_elem_sampled(x, y, sampler_, it.out);
}
}
diff --git a/source/blender/compositor/operations/COM_RotateOperation.h b/source/blender/compositor/operations/COM_RotateOperation.h
index f0de699f9ce..e395e99c2b2 100644
--- a/source/blender/compositor/operations/COM_RotateOperation.h
+++ b/source/blender/compositor/operations/COM_RotateOperation.h
@@ -29,8 +29,10 @@ class RotateOperation : public MultiThreadedOperation {
SocketReader *m_imageSocket;
SocketReader *m_degreeSocket;
+ /* TODO(manzanilla): to be removed with tiled implementation. */
float m_centerX;
float m_centerY;
+
float m_cosine;
float m_sine;
bool m_doDegree2RadConversion;
@@ -49,12 +51,28 @@ class RotateOperation : public MultiThreadedOperation {
y = center_y + (-sine * dx + cosine * dy);
}
+ static void get_rotation_center(const rcti &area, float &r_x, float &r_y);
+ static void get_rotation_center(const float width, const float height, float &r_x, float &r_y);
+ static void get_rotation_offset(const rcti &input_canvas,
+ const rcti &rotate_canvas,
+ float &r_offset_x,
+ float &r_offset_y);
static void get_area_rotation_bounds(const rcti &area,
const float center_x,
const float center_y,
const float sine,
const float cosine,
rcti &r_bounds);
+ static void get_rotation_area_of_interest(const rcti &input_canvas,
+ const rcti &rotate_canvas,
+ const float sine,
+ const float cosine,
+ const rcti &output_area,
+ rcti &r_input_area);
+ static void get_rotation_canvas(const rcti &input_canvas,
+ const float sine,
+ const float cosine,
+ rcti &r_canvas);
bool determineDependingAreaOfInterest(rcti *input,
ReadBufferOperation *readOperation,
@@ -80,6 +98,8 @@ class RotateOperation : public MultiThreadedOperation {
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;
};
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_ScaleOperation.cc b/source/blender/compositor/operations/COM_ScaleOperation.cc
index f5a423ea8e3..717e04e1952 100644
--- a/source/blender/compositor/operations/COM_ScaleOperation.cc
+++ b/source/blender/compositor/operations/COM_ScaleOperation.cc
@@ -44,11 +44,10 @@ ScaleOperation::ScaleOperation() : ScaleOperation(DataType::Color)
ScaleOperation::ScaleOperation(DataType data_type) : BaseScaleOperation()
{
- this->addInputSocket(data_type);
+ this->addInputSocket(data_type, ResizeMode::Align);
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 +63,60 @@ 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(X_INPUT_INDEX, get_relative_scale_x_factor(width));
+}
+
+float ScaleOperation::get_constant_scale_y(const float height)
{
- return get_constant_scale(1, get_relative_scale_x_factor());
+ return get_constant_scale(Y_INPUT_INDEX, get_relative_scale_y_factor(height));
}
-float ScaleOperation::get_constant_scale_y()
+bool ScaleOperation::is_scaling_variable()
{
- return get_constant_scale(2, get_relative_scale_y_factor());
+ 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 center_x, float center_y, float scale_x, float scale_y)
+void ScaleOperation::scale_area(rcti &area, float relative_scale_x, float relative_scale_y)
{
- 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);
+ const int src_xmin = area.xmin;
+ const int src_ymin = area.ymin;
+
+ /* Scale. */
+ const float center_x = BLI_rcti_cent_x_fl(&area);
+ const float center_y = BLI_rcti_cent_y_fl(&area);
+ 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));
+
+ /* Move area to original position. */
+ BLI_rcti_translate(&area, src_xmin - area.xmin, src_ymin - area.ymin);
}
-void ScaleOperation::scale_area(rcti &rect, float scale_x, float scale_y)
+void ScaleOperation::clamp_area_size_max(rcti &area, int width, int height)
{
- scale_area(rect, m_centerX, m_centerY, scale_x, scale_y);
+
+ if (BLI_rcti_size_x(&area) > width) {
+ area.xmax = area.xmin + width;
+ }
+ if (BLI_rcti_size_y(&area) > height) {
+ area.ymax = area.ymin + height;
+ }
+}
+
+void ScaleOperation::set_scale_canvas_max_size(int width, int height)
+{
+ max_scale_canvas_width_ = width;
+ max_scale_canvas_height_ = height;
}
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 +133,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_cent_x(&input_canvas);
+ const float scale_center_y = BLI_rcti_cent_y(&input_canvas);
+ 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 +186,68 @@ 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 scale_offset_x, scale_offset_y;
+ ScaleOperation::get_scale_offset(
+ input_image_op->get_canvas(), this->get_canvas(), scale_offset_x, 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(
+ it.x + scale_offset_x, scale_center_x, rel_scale_x);
+ const float scaled_y = scale_coord_inverted(
+ it.y + scale_offset_y, scale_center_y, rel_scale_y);
+ input_image->read_elem_sampled(scaled_x, scaled_y, (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);
+ clamp_area_size_max(r_area,
+ MAX2(input_width, max_scale_canvas_width_),
+ MAX2(input_height, max_scale_canvas_height_));
+
+ /* 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 +275,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 +295,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 +331,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 +357,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();
@@ -265,7 +378,7 @@ bool ScaleAbsoluteOperation::determineDependingAreaOfInterest(rcti *input,
/* Absolute fixed size. */
ScaleFixedSizeOperation::ScaleFixedSizeOperation() : BaseScaleOperation()
{
- this->addInputSocket(DataType::Color, ResizeMode::None);
+ this->addInputSocket(DataType::Color, ResizeMode::Align);
this->addOutputSocket(DataType::Color);
this->set_canvas_input_index(0);
this->m_inputOperation = nullptr;
@@ -382,10 +495,14 @@ 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 = output_area;
+ BLI_rcti_translate(&r_input_area, -canvas_.xmin, -canvas_.ymin);
+ r_input_area.xmax = (r_input_area.xmax - m_offsetX) * this->m_relX;
+ r_input_area.xmin = (r_input_area.xmin - m_offsetX) * this->m_relX;
+ r_input_area.ymax = (r_input_area.ymax - m_offsetY) * this->m_relY;
+ r_input_area.ymin = (r_input_area.ymin - m_offsetY) * this->m_relY;
+ BLI_rcti_translate(&r_input_area, canvas_.xmin, canvas_.ymin);
expand_area_for_sampler(r_input_area, (PixelSampler)m_sampler);
}
diff --git a/source/blender/compositor/operations/COM_ScaleOperation.h b/source/blender/compositor/operations/COM_ScaleOperation.h
index 04fa4fe62d1..30f0660dd44 100644
--- a/source/blender/compositor/operations/COM_ScaleOperation.h
+++ b/source/blender/compositor/operations/COM_ScaleOperation.h
@@ -42,19 +42,27 @@ class BaseScaleOperation : public MultiThreadedOperation {
}
int m_sampler;
+ /* TODO(manzanilla): to be removed with tiled implementation. */
bool m_variable_size;
};
class ScaleOperation : public BaseScaleOperation {
public:
- static constexpr float MIN_SCALE = 0.0001f;
+ static constexpr float MIN_RELATIVE_SCALE = 0.0001f;
+ static constexpr float DEFAULT_MAX_SCALE_CANVAS_SIZE = 12000;
protected:
+ static constexpr int IMAGE_INPUT_INDEX = 0;
+ static constexpr int X_INPUT_INDEX = 1;
+ static constexpr int Y_INPUT_INDEX = 2;
+
SocketReader *m_inputOperation;
SocketReader *m_inputXOperation;
SocketReader *m_inputYOperation;
- float m_centerX;
- float m_centerY;
+ float canvas_center_x_;
+ float canvas_center_y_;
+ int max_scale_canvas_width_ = DEFAULT_MAX_SCALE_CANVAS_SIZE;
+ int max_scale_canvas_height_ = DEFAULT_MAX_SCALE_CANVAS_SIZE;
public:
ScaleOperation();
@@ -62,9 +70,30 @@ class ScaleOperation : public BaseScaleOperation {
static float scale_coord(const float coord, const float center, const float relative_scale)
{
- return center + (coord - center) / MAX2(relative_scale, MIN_SCALE);
+ return center + (coord - center) * MAX2(relative_scale, MIN_RELATIVE_SCALE);
+ }
+
+ static float scale_coord_inverted(const float coord,
+ const float center,
+ const float relative_scale)
+ {
+ return center + (coord - center) / MAX2(relative_scale, MIN_RELATIVE_SCALE);
}
- static void scale_area(rcti &rect, float center_x, float center_y, float scale_x, float scale_y);
+
+ 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 scale_area(rcti &area, float relative_scale_x, float relative_scale_y);
+ static void 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);
+ static void clamp_area_size_max(rcti &area, int max_width, int max_height);
+
+ void set_scale_canvas_max_size(int width, int height);
void init_data() override;
void initExecution() override;
@@ -75,15 +104,17 @@ class ScaleOperation : public BaseScaleOperation {
const rcti &area,
Span<MemoryBuffer *> inputs) override;
+ void determine_canvas(const rcti &preferred_area, rcti &r_area) override;
+
protected:
- virtual float get_relative_scale_x_factor() = 0;
- virtual float get_relative_scale_y_factor() = 0;
+ virtual float get_relative_scale_x_factor(float width) = 0;
+ virtual float get_relative_scale_y_factor(float height) = 0;
private:
+ bool is_scaling_variable();
float get_constant_scale(int input_op_idx, float factor);
- float get_constant_scale_x();
- float get_constant_scale_y();
- void scale_area(rcti &rect, float scale_x, float scale_y);
+ float get_constant_scale_x(float width);
+ float get_constant_scale_y(float height);
};
class ScaleRelativeOperation : public ScaleOperation {
@@ -94,11 +125,13 @@ class ScaleRelativeOperation : public ScaleOperation {
ReadBufferOperation *readOperation,
rcti *output) override;
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
- float get_relative_scale_x_factor() override
+
+ float get_relative_scale_x_factor(float UNUSED(width)) override
{
return 1.0f;
}
- float get_relative_scale_y_factor() override
+
+ float get_relative_scale_y_factor(float UNUSED(height)) override
{
return 1.0f;
}
@@ -110,13 +143,15 @@ class ScaleAbsoluteOperation : public ScaleOperation {
ReadBufferOperation *readOperation,
rcti *output) override;
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
- float get_relative_scale_x_factor() override
+
+ float get_relative_scale_x_factor(float width) override
{
- return 1.0f / getWidth();
+ return 1.0f / width;
}
- float get_relative_scale_y_factor() override
+
+ float get_relative_scale_y_factor(float height) override
{
- return 1.0f / getHeight();
+ return 1.0f / height;
}
};
diff --git a/source/blender/compositor/operations/COM_TonemapOperation.cc b/source/blender/compositor/operations/COM_TonemapOperation.cc
index 67eb26993d0..cb671c54abe 100644
--- a/source/blender/compositor/operations/COM_TonemapOperation.cc
+++ b/source/blender/compositor/operations/COM_TonemapOperation.cc
@@ -28,7 +28,7 @@ namespace blender::compositor {
TonemapOperation::TonemapOperation()
{
- this->addInputSocket(DataType::Color, ResizeMode::None);
+ this->addInputSocket(DataType::Color, ResizeMode::Align);
this->addOutputSocket(DataType::Color);
this->m_imageReader = nullptr;
this->m_data = nullptr;
diff --git a/source/blender/compositor/operations/COM_TransformOperation.cc b/source/blender/compositor/operations/COM_TransformOperation.cc
index f73c9e9d956..8a77a4fa012 100644
--- a/source/blender/compositor/operations/COM_TransformOperation.cc
+++ b/source/blender/compositor/operations/COM_TransformOperation.cc
@@ -27,55 +27,41 @@ namespace blender::compositor {
TransformOperation::TransformOperation()
{
- addInputSocket(DataType::Color);
- addInputSocket(DataType::Value);
- addInputSocket(DataType::Value);
- addInputSocket(DataType::Value);
- addInputSocket(DataType::Value);
+ addInputSocket(DataType::Color, ResizeMode::None);
+ addInputSocket(DataType::Value, ResizeMode::None);
+ addInputSocket(DataType::Value, ResizeMode::None);
+ addInputSocket(DataType::Value, ResizeMode::None);
+ addInputSocket(DataType::Value, ResizeMode::None);
addOutputSocket(DataType::Color);
translate_factor_x_ = 1.0f;
translate_factor_y_ = 1.0f;
convert_degree_to_rad_ = false;
sampler_ = PixelSampler::Bilinear;
invert_ = false;
+ max_scale_canvas_width_ = ScaleOperation::DEFAULT_MAX_SCALE_CANVAS_SIZE;
+ max_scale_canvas_height_ = ScaleOperation::DEFAULT_MAX_SCALE_CANVAS_SIZE;
+}
+
+void TransformOperation::set_scale_canvas_max_size(int width, int height)
+{
+ max_scale_canvas_width_ = width;
+ max_scale_canvas_height_ = height;
}
void TransformOperation::init_data()
{
- /* Translation. */
- translate_x_ = 0;
- NodeOperation *x_op = getInputOperation(X_INPUT_INDEX);
- if (x_op->get_flags().is_constant_operation) {
- translate_x_ = static_cast<ConstantOperation *>(x_op)->get_constant_elem()[0] *
- translate_factor_x_;
- }
- translate_y_ = 0;
- NodeOperation *y_op = getInputOperation(Y_INPUT_INDEX);
- if (y_op->get_flags().is_constant_operation) {
- translate_y_ = static_cast<ConstantOperation *>(y_op)->get_constant_elem()[0] *
- translate_factor_y_;
- }
- /* Scaling. */
- scale_center_x_ = getWidth() / 2.0;
- scale_center_y_ = getHeight() / 2.0;
- constant_scale_ = 1.0f;
- NodeOperation *scale_op = getInputOperation(SCALE_INPUT_INDEX);
- if (scale_op->get_flags().is_constant_operation) {
- constant_scale_ = static_cast<ConstantOperation *>(scale_op)->get_constant_elem()[0];
- }
+ translate_x_ = get_input_operation(X_INPUT_INDEX)->get_constant_value_default(0.0f) *
+ translate_factor_x_;
+ translate_y_ = get_input_operation(Y_INPUT_INDEX)->get_constant_value_default(0.0f) *
+ translate_factor_y_;
- /* Rotation. */
- rotate_center_x_ = (getWidth() - 1.0) / 2.0;
- rotate_center_y_ = (getHeight() - 1.0) / 2.0;
- NodeOperation *degree_op = getInputOperation(DEGREE_INPUT_INDEX);
- const bool is_constant_degree = degree_op->get_flags().is_constant_operation;
- const float degree = is_constant_degree ?
- static_cast<ConstantOperation *>(degree_op)->get_constant_elem()[0] :
- 0.0f;
+ const float degree = get_input_operation(DEGREE_INPUT_INDEX)->get_constant_value_default(0.0f);
const double rad = convert_degree_to_rad_ ? DEG2RAD((double)degree) : degree;
rotate_cosine_ = cos(rad);
rotate_sine_ = sin(rad);
+
+ scale_ = get_input_operation(SCALE_INPUT_INDEX)->get_constant_value_default(1.0f);
}
void TransformOperation::get_area_of_interest(const int input_idx,
@@ -84,26 +70,41 @@ void TransformOperation::get_area_of_interest(const int input_idx,
{
switch (input_idx) {
case IMAGE_INPUT_INDEX: {
- BLI_rcti_translate(&r_input_area, translate_x_, translate_y_);
- ScaleOperation::scale_area(
- r_input_area, scale_center_x_, scale_center_y_, constant_scale_, constant_scale_);
- RotateOperation::get_area_rotation_bounds(r_input_area,
- rotate_center_x_,
- rotate_center_y_,
- rotate_sine_,
- rotate_cosine_,
- r_input_area);
+ NodeOperation *image_op = get_input_operation(IMAGE_INPUT_INDEX);
+ const rcti &image_canvas = image_op->get_canvas();
+ if (invert_) {
+ /* Scale -> Rotate -> Translate. */
+ r_input_area = output_area;
+ BLI_rcti_translate(&r_input_area, -translate_x_, -translate_y_);
+ RotateOperation::get_rotation_area_of_interest(scale_canvas_,
+ rotate_canvas_,
+ rotate_sine_,
+ rotate_cosine_,
+ r_input_area,
+ r_input_area);
+ ScaleOperation::get_scale_area_of_interest(
+ image_canvas, scale_canvas_, scale_, scale_, r_input_area, r_input_area);
+ }
+ else {
+ /* Translate -> Rotate -> Scale. */
+ ScaleOperation::get_scale_area_of_interest(
+ rotate_canvas_, scale_canvas_, scale_, scale_, output_area, r_input_area);
+ RotateOperation::get_rotation_area_of_interest(translate_canvas_,
+ rotate_canvas_,
+ rotate_sine_,
+ rotate_cosine_,
+ r_input_area,
+ r_input_area);
+ BLI_rcti_translate(&r_input_area, -translate_x_, -translate_y_);
+ }
expand_area_for_sampler(r_input_area, sampler_);
break;
}
case X_INPUT_INDEX:
case Y_INPUT_INDEX:
- case DEGREE_INPUT_INDEX: {
- r_input_area = COM_CONSTANT_INPUT_AREA_OF_INTEREST;
- break;
- }
+ case DEGREE_INPUT_INDEX:
case SCALE_INPUT_INDEX: {
- r_input_area = output_area;
+ r_input_area = COM_CONSTANT_INPUT_AREA_OF_INTEREST;
break;
}
}
@@ -114,8 +115,7 @@ void TransformOperation::update_memory_buffer_partial(MemoryBuffer *output,
Span<MemoryBuffer *> inputs)
{
const MemoryBuffer *input_img = inputs[IMAGE_INPUT_INDEX];
- MemoryBuffer *input_scale = inputs[SCALE_INPUT_INDEX];
- BuffersIterator<float> it = output->iterate_with({input_scale}, area);
+ BuffersIterator<float> it = output->iterate_with({}, area);
if (invert_) {
transform_inverted(it, input_img);
}
@@ -124,31 +124,116 @@ void TransformOperation::update_memory_buffer_partial(MemoryBuffer *output,
}
}
+void TransformOperation::determine_canvas(const rcti &preferred_area, rcti &r_area)
+{
+ const bool image_determined =
+ getInputSocket(IMAGE_INPUT_INDEX)->determine_canvas(preferred_area, r_area);
+ if (image_determined) {
+ rcti image_canvas = r_area;
+ rcti unused;
+ getInputSocket(X_INPUT_INDEX)->determine_canvas(image_canvas, unused);
+ getInputSocket(Y_INPUT_INDEX)->determine_canvas(image_canvas, unused);
+ getInputSocket(DEGREE_INPUT_INDEX)->determine_canvas(image_canvas, unused);
+ getInputSocket(SCALE_INPUT_INDEX)->determine_canvas(image_canvas, unused);
+
+ init_data();
+ if (invert_) {
+ /* Scale -> Rotate -> Translate. */
+ scale_canvas_ = image_canvas;
+ ScaleOperation::scale_area(scale_canvas_, scale_, scale_);
+ ScaleOperation::clamp_area_size_max(
+ scale_canvas_,
+ MAX2(BLI_rcti_size_x(&image_canvas), max_scale_canvas_width_),
+ MAX2(BLI_rcti_size_y(&image_canvas), max_scale_canvas_height_));
+
+ RotateOperation::get_rotation_canvas(
+ scale_canvas_, rotate_sine_, rotate_cosine_, rotate_canvas_);
+
+ translate_canvas_ = rotate_canvas_;
+ BLI_rcti_translate(&translate_canvas_, translate_x_, translate_y_);
+
+ r_area = translate_canvas_;
+ }
+ else {
+ /* Translate -> Rotate -> Scale. */
+ translate_canvas_ = image_canvas;
+ BLI_rcti_translate(&translate_canvas_, translate_x_, translate_y_);
+
+ RotateOperation::get_rotation_canvas(
+ translate_canvas_, rotate_sine_, rotate_cosine_, rotate_canvas_);
+
+ scale_canvas_ = rotate_canvas_;
+ ScaleOperation::scale_area(scale_canvas_, scale_, scale_);
+ ScaleOperation::clamp_area_size_max(
+ scale_canvas_,
+ MAX2(BLI_rcti_size_x(&rotate_canvas_), max_scale_canvas_width_),
+ MAX2(BLI_rcti_size_y(&rotate_canvas_), max_scale_canvas_height_));
+
+ r_area = scale_canvas_;
+ }
+ }
+}
+
+/** Translate -> Rotate -> Scale. */
void TransformOperation::transform(BuffersIterator<float> &it, const MemoryBuffer *input_img)
{
+ float rotate_center_x, rotate_center_y;
+ RotateOperation::get_rotation_center(BLI_rcti_size_x(&translate_canvas_),
+ BLI_rcti_size_y(&translate_canvas_),
+ rotate_center_x,
+ rotate_center_y);
+ float rotate_offset_x, rotate_offset_y;
+ RotateOperation::get_rotation_offset(
+ translate_canvas_, rotate_canvas_, rotate_offset_x, rotate_offset_y);
+
+ const float scale_center_x = BLI_rcti_size_x(&rotate_canvas_) / 2.0f;
+ const float scale_center_y = BLI_rcti_size_y(&rotate_canvas_) / 2.0f;
+ float scale_offset_x, scale_offset_y;
+ ScaleOperation::get_scale_offset(rotate_canvas_, scale_canvas_, scale_offset_x, scale_offset_y);
+
for (; !it.is_end(); ++it) {
- const float scale = *it.in(0);
- float x = it.x - translate_x_;
- float y = it.y - translate_y_;
+ float x = ScaleOperation::scale_coord_inverted(it.x + scale_offset_x, scale_center_x, scale_);
+ float y = ScaleOperation::scale_coord_inverted(it.y + scale_offset_y, scale_center_y, scale_);
+
+ x = rotate_offset_x + x;
+ y = rotate_offset_y + y;
RotateOperation::rotate_coords(
- x, y, rotate_center_x_, rotate_center_y_, rotate_sine_, rotate_cosine_);
- x = ScaleOperation::scale_coord(x, scale_center_x_, scale);
- y = ScaleOperation::scale_coord(y, scale_center_y_, scale);
- input_img->read_elem_sampled(x, y, sampler_, it.out);
+ x, y, rotate_center_x, rotate_center_y, rotate_sine_, rotate_cosine_);
+
+ input_img->read_elem_sampled(x - translate_x_, y - translate_y_, sampler_, it.out);
}
}
+/** Scale -> Rotate -> Translate. */
void TransformOperation::transform_inverted(BuffersIterator<float> &it,
const MemoryBuffer *input_img)
{
+ const rcti &image_canvas = get_input_operation(IMAGE_INPUT_INDEX)->get_canvas();
+ const float scale_center_x = BLI_rcti_size_x(&image_canvas) / 2.0f - translate_x_;
+ const float scale_center_y = BLI_rcti_size_y(&image_canvas) / 2.0f - translate_y_;
+ float scale_offset_x, scale_offset_y;
+ ScaleOperation::get_scale_offset(image_canvas, scale_canvas_, scale_offset_x, scale_offset_y);
+
+ float rotate_center_x, rotate_center_y;
+ RotateOperation::get_rotation_center(BLI_rcti_size_x(&scale_canvas_),
+ BLI_rcti_size_y(&scale_canvas_),
+ rotate_center_x,
+ rotate_center_y);
+ rotate_center_x -= translate_x_;
+ rotate_center_y -= translate_y_;
+ float rotate_offset_x, rotate_offset_y;
+ RotateOperation::get_rotation_offset(
+ scale_canvas_, rotate_canvas_, rotate_offset_x, rotate_offset_y);
+
for (; !it.is_end(); ++it) {
- const float scale = *it.in(0);
- float x = ScaleOperation::scale_coord(it.x, scale_center_x_, scale);
- float y = ScaleOperation::scale_coord(it.y, scale_center_y_, scale);
+ float x = rotate_offset_x + (it.x - translate_x_);
+ float y = rotate_offset_y + (it.y - translate_y_);
RotateOperation::rotate_coords(
- x, y, rotate_center_x_, rotate_center_y_, rotate_sine_, rotate_cosine_);
- x -= translate_x_;
- y -= translate_y_;
+ x, y, rotate_center_x, rotate_center_y, rotate_sine_, rotate_cosine_);
+
+ x = ScaleOperation::scale_coord_inverted(x + scale_offset_x, scale_center_x, scale_);
+ y = ScaleOperation::scale_coord_inverted(y + scale_offset_y, scale_center_y, scale_);
+
input_img->read_elem_sampled(x, y, sampler_, it.out);
}
}
diff --git a/source/blender/compositor/operations/COM_TransformOperation.h b/source/blender/compositor/operations/COM_TransformOperation.h
index 480998a0207..6c9de5cffdc 100644
--- a/source/blender/compositor/operations/COM_TransformOperation.h
+++ b/source/blender/compositor/operations/COM_TransformOperation.h
@@ -30,15 +30,14 @@ class TransformOperation : public MultiThreadedOperation {
constexpr static int DEGREE_INPUT_INDEX = 3;
constexpr static int SCALE_INPUT_INDEX = 4;
- float scale_center_x_;
- float scale_center_y_;
- float rotate_center_x_;
- float rotate_center_y_;
float rotate_cosine_;
float rotate_sine_;
- float translate_x_;
- float translate_y_;
- float constant_scale_;
+ int translate_x_;
+ int translate_y_;
+ float scale_;
+ rcti scale_canvas_;
+ rcti rotate_canvas_;
+ rcti translate_canvas_;
/* Set variables. */
PixelSampler sampler_;
@@ -46,6 +45,8 @@ class TransformOperation : public MultiThreadedOperation {
float translate_factor_x_;
float translate_factor_y_;
bool invert_;
+ int max_scale_canvas_width_;
+ int max_scale_canvas_height_;
public:
TransformOperation();
@@ -71,16 +72,18 @@ class TransformOperation : public MultiThreadedOperation {
invert_ = value;
}
+ void set_scale_canvas_max_size(int width, int height);
+
void init_data() override;
void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override;
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;
+
private:
- /** Translate -> Rotate -> Scale. */
void transform(BuffersIterator<float> &it, const MemoryBuffer *input_img);
- /** Scale -> Rotate -> Translate. */
void transform_inverted(BuffersIterator<float> &it, const MemoryBuffer *input_img);
};
diff --git a/source/blender/compositor/operations/COM_TranslateOperation.cc b/source/blender/compositor/operations/COM_TranslateOperation.cc
index 266e960037c..cae7808eb59 100644
--- a/source/blender/compositor/operations/COM_TranslateOperation.cc
+++ b/source/blender/compositor/operations/COM_TranslateOperation.cc
@@ -23,11 +23,11 @@ namespace blender::compositor {
TranslateOperation::TranslateOperation() : TranslateOperation(DataType::Color)
{
}
-TranslateOperation::TranslateOperation(DataType data_type)
+TranslateOperation::TranslateOperation(DataType data_type, ResizeMode resize_mode)
{
- this->addInputSocket(data_type);
- this->addInputSocket(DataType::Value);
- this->addInputSocket(DataType::Value);
+ this->addInputSocket(data_type, resize_mode);
+ this->addInputSocket(DataType::Value, ResizeMode::None);
+ this->addInputSocket(DataType::Value, ResizeMode::None);
this->addOutputSocket(data_type);
this->set_canvas_input_index(0);
this->m_inputOperation = nullptr;
@@ -39,6 +39,7 @@ TranslateOperation::TranslateOperation(DataType data_type)
this->x_extend_mode_ = MemoryBufferExtend::Clip;
this->y_extend_mode_ = MemoryBufferExtend::Clip;
}
+
void TranslateOperation::initExecution()
{
this->m_inputOperation = this->getInputSocketReader(0);
@@ -122,6 +123,10 @@ void TranslateOperation::get_area_of_interest(const int input_idx,
BLI_rcti_translate(&r_input_area, 0, -delta_y);
}
}
+ else {
+ r_input_area = output_area;
+ }
+ r_input_area = get_input_operation(input_idx)->get_canvas();
}
void TranslateOperation::update_memory_buffer_partial(MemoryBuffer *output,
@@ -142,4 +147,27 @@ void TranslateOperation::update_memory_buffer_partial(MemoryBuffer *output,
}
}
+TranslateCanvasOperation::TranslateCanvasOperation()
+ : TranslateOperation(DataType::Color, ResizeMode::None)
+{
+}
+
+void TranslateCanvasOperation::determine_canvas(const rcti &preferred_area, rcti &r_area)
+{
+ const bool determined =
+ getInputSocket(IMAGE_INPUT_INDEX)->determine_canvas(preferred_area, r_area);
+ if (determined) {
+ NodeOperationInput *x_socket = getInputSocket(X_INPUT_INDEX);
+ NodeOperationInput *y_socket = getInputSocket(Y_INPUT_INDEX);
+ rcti unused;
+ x_socket->determine_canvas(r_area, unused);
+ y_socket->determine_canvas(r_area, unused);
+
+ ensureDelta();
+ const float delta_x = x_extend_mode_ == MemoryBufferExtend::Clip ? getDeltaX() : 0.0f;
+ const float delta_y = y_extend_mode_ == MemoryBufferExtend::Clip ? getDeltaY() : 0.0f;
+ BLI_rcti_translate(&r_area, delta_x, delta_y);
+ }
+}
+
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_TranslateOperation.h b/source/blender/compositor/operations/COM_TranslateOperation.h
index ce1965cecef..c5a3d1ffd99 100644
--- a/source/blender/compositor/operations/COM_TranslateOperation.h
+++ b/source/blender/compositor/operations/COM_TranslateOperation.h
@@ -24,6 +24,11 @@
namespace blender::compositor {
class TranslateOperation : public MultiThreadedOperation {
+ protected:
+ static constexpr int IMAGE_INPUT_INDEX = 0;
+ static constexpr int X_INPUT_INDEX = 1;
+ static constexpr int Y_INPUT_INDEX = 2;
+
private:
SocketReader *m_inputOperation;
SocketReader *m_inputXOperation;
@@ -33,12 +38,14 @@ class TranslateOperation : public MultiThreadedOperation {
bool m_isDeltaSet;
float m_factorX;
float m_factorY;
+
+ protected:
MemoryBufferExtend x_extend_mode_;
MemoryBufferExtend y_extend_mode_;
public:
TranslateOperation();
- TranslateOperation(DataType data_type);
+ TranslateOperation(DataType data_type, ResizeMode mode = ResizeMode::Center);
bool determineDependingAreaOfInterest(rcti *input,
ReadBufferOperation *readOperation,
rcti *output) override;
@@ -67,16 +74,8 @@ class TranslateOperation : public MultiThreadedOperation {
this->m_deltaY = tempDelta[0];
}
else {
- this->m_deltaX = 0;
- NodeOperation *x_op = getInputOperation(1);
- if (x_op->get_flags().is_constant_operation) {
- this->m_deltaX = ((ConstantOperation *)x_op)->get_constant_elem()[0];
- }
- this->m_deltaY = 0;
- NodeOperation *y_op = getInputOperation(2);
- if (y_op->get_flags().is_constant_operation) {
- this->m_deltaY = ((ConstantOperation *)y_op)->get_constant_elem()[0];
- }
+ m_deltaX = get_input_operation(X_INPUT_INDEX)->get_constant_value_default(0.0f);
+ m_deltaY = get_input_operation(Y_INPUT_INDEX)->get_constant_value_default(0.0f);
}
this->m_isDeltaSet = true;
@@ -93,4 +92,10 @@ class TranslateOperation : public MultiThreadedOperation {
Span<MemoryBuffer *> inputs) override;
};
+class TranslateCanvasOperation : public TranslateOperation {
+ public:
+ TranslateCanvasOperation();
+ void determine_canvas(const rcti &preferred_area, rcti &r_area) override;
+};
+
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_VariableSizeBokehBlurOperation.cc b/source/blender/compositor/operations/COM_VariableSizeBokehBlurOperation.cc
index 0fe44e3a61f..c524447a4fa 100644
--- a/source/blender/compositor/operations/COM_VariableSizeBokehBlurOperation.cc
+++ b/source/blender/compositor/operations/COM_VariableSizeBokehBlurOperation.cc
@@ -28,8 +28,8 @@ namespace blender::compositor {
VariableSizeBokehBlurOperation::VariableSizeBokehBlurOperation()
{
this->addInputSocket(DataType::Color);
- this->addInputSocket(DataType::Color, ResizeMode::None); /* Do not resize the bokeh image. */
- this->addInputSocket(DataType::Value); /* Radius. */
+ this->addInputSocket(DataType::Color, ResizeMode::Align); /* Do not resize the bokeh image. */
+ this->addInputSocket(DataType::Value); /* Radius. */
#ifdef COM_DEFOCUS_SEARCH
/* Inverse search radius optimization structure. */
this->addInputSocket(DataType::Color, ResizeMode::None);
@@ -440,7 +440,7 @@ void VariableSizeBokehBlurOperation::update_memory_buffer_partial(MemoryBuffer *
/* #InverseSearchRadiusOperation. */
InverseSearchRadiusOperation::InverseSearchRadiusOperation()
{
- this->addInputSocket(DataType::Value, ResizeMode::None); /* Radius. */
+ this->addInputSocket(DataType::Value, ResizeMode::Align); /* Radius. */
this->addOutputSocket(DataType::Color);
this->flags.complex = true;
this->m_inputRadius = nullptr;