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-08-23 16:30:01 +0300
committerManuel Castilla <manzanillawork@gmail.com>2021-08-23 17:36:09 +0300
commit064167fce70e3d7c382c374334a1bd0b520fe9fe (patch)
tree6e6f9a26d8e446a9e6966fa185e9527d720b19ee
parenta95e56b741709f7157a44196091ccad3ec369e5e (diff)
Compositor: Full frame transform nodes
Adds full frame implementation to "Rotate", "Transform" and "Stabilize2D" nodes. To avoid sampling twice when concatenating scale and rotate operations, a `TransformOperation` is implemented with all the functionality. The nodes have no functional changes. Part of T88150. Reviewed By: jbakker Differential Revision: https://developer.blender.org/D12165
-rw-r--r--source/blender/compositor/CMakeLists.txt2
-rw-r--r--source/blender/compositor/COM_defines.h2
-rw-r--r--source/blender/compositor/nodes/COM_RotateNode.cc25
-rw-r--r--source/blender/compositor/nodes/COM_Stabilize2dNode.cc100
-rw-r--r--source/blender/compositor/nodes/COM_TransformNode.cc61
-rw-r--r--source/blender/compositor/operations/COM_RotateOperation.cc86
-rw-r--r--source/blender/compositor/operations/COM_RotateOperation.h37
-rw-r--r--source/blender/compositor/operations/COM_ScaleOperation.cc13
-rw-r--r--source/blender/compositor/operations/COM_ScaleOperation.h9
-rw-r--r--source/blender/compositor/operations/COM_TransformOperation.cc156
-rw-r--r--source/blender/compositor/operations/COM_TransformOperation.h87
11 files changed, 501 insertions, 77 deletions
diff --git a/source/blender/compositor/CMakeLists.txt b/source/blender/compositor/CMakeLists.txt
index ee287c65fe9..dba2d1e1e67 100644
--- a/source/blender/compositor/CMakeLists.txt
+++ b/source/blender/compositor/CMakeLists.txt
@@ -515,6 +515,8 @@ set(SRC
operations/COM_ScaleOperation.h
operations/COM_ScreenLensDistortionOperation.cc
operations/COM_ScreenLensDistortionOperation.h
+ operations/COM_TransformOperation.cc
+ operations/COM_TransformOperation.h
operations/COM_TranslateOperation.cc
operations/COM_TranslateOperation.h
operations/COM_WrapOperation.cc
diff --git a/source/blender/compositor/COM_defines.h b/source/blender/compositor/COM_defines.h
index 40a1e0da2a8..ee9bea7b2c6 100644
--- a/source/blender/compositor/COM_defines.h
+++ b/source/blender/compositor/COM_defines.h
@@ -119,6 +119,8 @@ constexpr float COM_PREVIEW_SIZE = 140.f;
constexpr float COM_RULE_OF_THIRDS_DIVIDER = 100.0f;
constexpr float COM_BLUR_BOKEH_PIXELS = 512;
+constexpr rcti COM_SINGLE_ELEM_AREA = {0, 1, 0, 1};
+
constexpr IndexRange XRange(const rcti &area)
{
return IndexRange(area.xmin, area.xmax - area.xmin);
diff --git a/source/blender/compositor/nodes/COM_RotateNode.cc b/source/blender/compositor/nodes/COM_RotateNode.cc
index af5baa733dc..c2fd8ed5594 100644
--- a/source/blender/compositor/nodes/COM_RotateNode.cc
+++ b/source/blender/compositor/nodes/COM_RotateNode.cc
@@ -30,20 +30,31 @@ RotateNode::RotateNode(bNode *editorNode) : Node(editorNode)
}
void RotateNode::convertToOperations(NodeConverter &converter,
- const CompositorContext & /*context*/) const
+ const CompositorContext &context) const
{
NodeInput *inputSocket = this->getInputSocket(0);
NodeInput *inputDegreeSocket = this->getInputSocket(1);
NodeOutput *outputSocket = this->getOutputSocket(0);
RotateOperation *operation = new RotateOperation();
- SetSamplerOperation *sampler = new SetSamplerOperation();
- sampler->setSampler((PixelSampler)this->getbNode()->custom1);
-
- converter.addOperation(sampler);
converter.addOperation(operation);
- converter.addLink(sampler->getOutputSocket(), operation->getInputSocket(0));
- converter.mapInputSocket(inputSocket, sampler->getInputSocket(0));
+ PixelSampler sampler = (PixelSampler)this->getbNode()->custom1;
+ switch (context.get_execution_model()) {
+ case eExecutionModel::Tiled: {
+ SetSamplerOperation *sampler_op = new SetSamplerOperation();
+ sampler_op->setSampler(sampler);
+ converter.addOperation(sampler_op);
+ converter.addLink(sampler_op->getOutputSocket(), operation->getInputSocket(0));
+ converter.mapInputSocket(inputSocket, sampler_op->getInputSocket(0));
+ break;
+ }
+ case eExecutionModel::FullFrame: {
+ operation->set_sampler(sampler);
+ converter.mapInputSocket(inputSocket, operation->getInputSocket(0));
+ break;
+ }
+ }
+
converter.mapInputSocket(inputDegreeSocket, operation->getInputSocket(1));
converter.mapOutputSocket(outputSocket, operation->getOutputSocket(0));
}
diff --git a/source/blender/compositor/nodes/COM_Stabilize2dNode.cc b/source/blender/compositor/nodes/COM_Stabilize2dNode.cc
index 0262f653d1a..7b2388bebca 100644
--- a/source/blender/compositor/nodes/COM_Stabilize2dNode.cc
+++ b/source/blender/compositor/nodes/COM_Stabilize2dNode.cc
@@ -22,6 +22,7 @@
#include "COM_RotateOperation.h"
#include "COM_ScaleOperation.h"
#include "COM_SetSamplerOperation.h"
+#include "COM_TransformOperation.h"
#include "COM_TranslateOperation.h"
#include "BKE_tracking.h"
@@ -42,18 +43,12 @@ void Stabilize2dNode::convertToOperations(NodeConverter &converter,
NodeInput *imageInput = this->getInputSocket(0);
MovieClip *clip = (MovieClip *)editorNode->id;
bool invert = (editorNode->custom2 & CMP_NODEFLAG_STABILIZE_INVERSE) != 0;
+ const PixelSampler sampler = (PixelSampler)editorNode->custom1;
- ScaleRelativeOperation *scaleOperation = new ScaleRelativeOperation();
- scaleOperation->setSampler((PixelSampler)editorNode->custom1);
- RotateOperation *rotateOperation = new RotateOperation();
- rotateOperation->setDoDegree2RadConversion(false);
- TranslateOperation *translateOperation = new TranslateOperation();
MovieClipAttributeOperation *scaleAttribute = new MovieClipAttributeOperation();
MovieClipAttributeOperation *angleAttribute = new MovieClipAttributeOperation();
MovieClipAttributeOperation *xAttribute = new MovieClipAttributeOperation();
MovieClipAttributeOperation *yAttribute = new MovieClipAttributeOperation();
- SetSamplerOperation *psoperation = new SetSamplerOperation();
- psoperation->setSampler((PixelSampler)editorNode->custom1);
scaleAttribute->setAttribute(MCA_SCALE);
scaleAttribute->setFramenumber(context.getFramenumber());
@@ -79,38 +74,67 @@ void Stabilize2dNode::convertToOperations(NodeConverter &converter,
converter.addOperation(angleAttribute);
converter.addOperation(xAttribute);
converter.addOperation(yAttribute);
- converter.addOperation(scaleOperation);
- converter.addOperation(translateOperation);
- converter.addOperation(rotateOperation);
- converter.addOperation(psoperation);
- converter.addLink(scaleAttribute->getOutputSocket(), scaleOperation->getInputSocket(1));
- converter.addLink(scaleAttribute->getOutputSocket(), scaleOperation->getInputSocket(2));
-
- converter.addLink(angleAttribute->getOutputSocket(), rotateOperation->getInputSocket(1));
-
- converter.addLink(xAttribute->getOutputSocket(), translateOperation->getInputSocket(1));
- converter.addLink(yAttribute->getOutputSocket(), translateOperation->getInputSocket(2));
-
- converter.mapOutputSocket(getOutputSocket(), psoperation->getOutputSocket());
-
- if (invert) {
- // Translate -> Rotate -> Scale.
- converter.mapInputSocket(imageInput, translateOperation->getInputSocket(0));
-
- converter.addLink(translateOperation->getOutputSocket(), rotateOperation->getInputSocket(0));
- converter.addLink(rotateOperation->getOutputSocket(), scaleOperation->getInputSocket(0));
-
- converter.addLink(scaleOperation->getOutputSocket(), psoperation->getInputSocket(0));
- }
- else {
- // Scale -> Rotate -> Translate.
- converter.mapInputSocket(imageInput, scaleOperation->getInputSocket(0));
-
- converter.addLink(scaleOperation->getOutputSocket(), rotateOperation->getInputSocket(0));
- converter.addLink(rotateOperation->getOutputSocket(), translateOperation->getInputSocket(0));
-
- converter.addLink(translateOperation->getOutputSocket(), psoperation->getInputSocket(0));
+ switch (context.get_execution_model()) {
+ case eExecutionModel::Tiled: {
+ ScaleRelativeOperation *scaleOperation = new ScaleRelativeOperation();
+ scaleOperation->setSampler(sampler);
+ RotateOperation *rotateOperation = new RotateOperation();
+ rotateOperation->setDoDegree2RadConversion(false);
+ TranslateOperation *translateOperation = new TranslateOperation();
+ SetSamplerOperation *psoperation = new SetSamplerOperation();
+ psoperation->setSampler(sampler);
+
+ converter.addOperation(scaleOperation);
+ converter.addOperation(translateOperation);
+ converter.addOperation(rotateOperation);
+ converter.addOperation(psoperation);
+
+ converter.addLink(scaleAttribute->getOutputSocket(), scaleOperation->getInputSocket(1));
+ converter.addLink(scaleAttribute->getOutputSocket(), scaleOperation->getInputSocket(2));
+
+ converter.addLink(angleAttribute->getOutputSocket(), rotateOperation->getInputSocket(1));
+
+ converter.addLink(xAttribute->getOutputSocket(), translateOperation->getInputSocket(1));
+ converter.addLink(yAttribute->getOutputSocket(), translateOperation->getInputSocket(2));
+
+ converter.mapOutputSocket(getOutputSocket(), psoperation->getOutputSocket());
+
+ if (invert) {
+ // Translate -> Rotate -> Scale.
+ converter.mapInputSocket(imageInput, translateOperation->getInputSocket(0));
+
+ converter.addLink(translateOperation->getOutputSocket(),
+ rotateOperation->getInputSocket(0));
+ converter.addLink(rotateOperation->getOutputSocket(), scaleOperation->getInputSocket(0));
+
+ converter.addLink(scaleOperation->getOutputSocket(), psoperation->getInputSocket(0));
+ }
+ else {
+ // Scale -> Rotate -> Translate.
+ converter.mapInputSocket(imageInput, scaleOperation->getInputSocket(0));
+
+ converter.addLink(scaleOperation->getOutputSocket(), rotateOperation->getInputSocket(0));
+ converter.addLink(rotateOperation->getOutputSocket(),
+ translateOperation->getInputSocket(0));
+
+ converter.addLink(translateOperation->getOutputSocket(), psoperation->getInputSocket(0));
+ }
+ break;
+ }
+ case eExecutionModel::FullFrame: {
+ TransformOperation *transform_op = new TransformOperation();
+ transform_op->set_sampler(sampler);
+ transform_op->set_convert_rotate_degree_to_rad(false);
+ transform_op->set_invert(invert);
+ converter.addOperation(transform_op);
+ converter.mapInputSocket(imageInput, transform_op->getInputSocket(0));
+ converter.addLink(xAttribute->getOutputSocket(), transform_op->getInputSocket(1));
+ converter.addLink(yAttribute->getOutputSocket(), transform_op->getInputSocket(2));
+ converter.addLink(angleAttribute->getOutputSocket(), transform_op->getInputSocket(3));
+ converter.addLink(scaleAttribute->getOutputSocket(), transform_op->getInputSocket(4));
+ converter.mapOutputSocket(getOutputSocket(), transform_op->getOutputSocket());
+ }
}
}
diff --git a/source/blender/compositor/nodes/COM_TransformNode.cc b/source/blender/compositor/nodes/COM_TransformNode.cc
index e1deaf616a4..d2fb7b54633 100644
--- a/source/blender/compositor/nodes/COM_TransformNode.cc
+++ b/source/blender/compositor/nodes/COM_TransformNode.cc
@@ -22,6 +22,7 @@
#include "COM_ScaleOperation.h"
#include "COM_SetSamplerOperation.h"
#include "COM_SetValueOperation.h"
+#include "COM_TransformOperation.h"
#include "COM_TranslateOperation.h"
namespace blender::compositor {
@@ -32,7 +33,7 @@ TransformNode::TransformNode(bNode *editorNode) : Node(editorNode)
}
void TransformNode::convertToOperations(NodeConverter &converter,
- const CompositorContext & /*context*/) const
+ const CompositorContext &context) const
{
NodeInput *imageInput = this->getInputSocket(0);
NodeInput *xInput = this->getInputSocket(1);
@@ -40,33 +41,51 @@ void TransformNode::convertToOperations(NodeConverter &converter,
NodeInput *angleInput = this->getInputSocket(3);
NodeInput *scaleInput = this->getInputSocket(4);
- ScaleRelativeOperation *scaleOperation = new ScaleRelativeOperation();
- converter.addOperation(scaleOperation);
+ switch (context.get_execution_model()) {
+ case eExecutionModel::Tiled: {
+ ScaleRelativeOperation *scaleOperation = new ScaleRelativeOperation();
+ converter.addOperation(scaleOperation);
- RotateOperation *rotateOperation = new RotateOperation();
- rotateOperation->setDoDegree2RadConversion(false);
- converter.addOperation(rotateOperation);
+ RotateOperation *rotateOperation = new RotateOperation();
+ rotateOperation->setDoDegree2RadConversion(false);
+ converter.addOperation(rotateOperation);
- TranslateOperation *translateOperation = new TranslateOperation();
- converter.addOperation(translateOperation);
+ TranslateOperation *translateOperation = new TranslateOperation();
+ converter.addOperation(translateOperation);
- SetSamplerOperation *sampler = new SetSamplerOperation();
- sampler->setSampler((PixelSampler)this->getbNode()->custom1);
- converter.addOperation(sampler);
+ SetSamplerOperation *sampler = new SetSamplerOperation();
+ sampler->setSampler((PixelSampler)this->getbNode()->custom1);
+ converter.addOperation(sampler);
- converter.mapInputSocket(imageInput, sampler->getInputSocket(0));
- converter.addLink(sampler->getOutputSocket(), scaleOperation->getInputSocket(0));
- converter.mapInputSocket(scaleInput, scaleOperation->getInputSocket(1));
- converter.mapInputSocket(scaleInput, scaleOperation->getInputSocket(2)); // xscale = yscale
+ converter.mapInputSocket(imageInput, sampler->getInputSocket(0));
+ converter.addLink(sampler->getOutputSocket(), scaleOperation->getInputSocket(0));
+ converter.mapInputSocket(scaleInput, scaleOperation->getInputSocket(1));
+ converter.mapInputSocket(scaleInput, scaleOperation->getInputSocket(2)); // xscale = yscale
- converter.addLink(scaleOperation->getOutputSocket(), rotateOperation->getInputSocket(0));
- converter.mapInputSocket(angleInput, rotateOperation->getInputSocket(1));
+ converter.addLink(scaleOperation->getOutputSocket(), rotateOperation->getInputSocket(0));
+ converter.mapInputSocket(angleInput, rotateOperation->getInputSocket(1));
- converter.addLink(rotateOperation->getOutputSocket(), translateOperation->getInputSocket(0));
- converter.mapInputSocket(xInput, translateOperation->getInputSocket(1));
- converter.mapInputSocket(yInput, translateOperation->getInputSocket(2));
+ converter.addLink(rotateOperation->getOutputSocket(), translateOperation->getInputSocket(0));
+ converter.mapInputSocket(xInput, translateOperation->getInputSocket(1));
+ converter.mapInputSocket(yInput, translateOperation->getInputSocket(2));
- converter.mapOutputSocket(getOutputSocket(), translateOperation->getOutputSocket());
+ converter.mapOutputSocket(getOutputSocket(), translateOperation->getOutputSocket());
+ break;
+ }
+ case eExecutionModel::FullFrame: {
+ TransformOperation *op = new TransformOperation();
+ op->set_sampler((PixelSampler)this->getbNode()->custom1);
+ converter.addOperation(op);
+
+ converter.mapInputSocket(imageInput, op->getInputSocket(0));
+ converter.mapInputSocket(xInput, op->getInputSocket(1));
+ converter.mapInputSocket(yInput, op->getInputSocket(2));
+ converter.mapInputSocket(angleInput, op->getInputSocket(3));
+ converter.mapInputSocket(scaleInput, op->getInputSocket(4));
+ converter.mapOutputSocket(getOutputSocket(), op->getOutputSocket());
+ break;
+ }
+ }
}
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_RotateOperation.cc b/source/blender/compositor/operations/COM_RotateOperation.cc
index 4fb3d324992..e3c482c15cb 100644
--- a/source/blender/compositor/operations/COM_RotateOperation.cc
+++ b/source/blender/compositor/operations/COM_RotateOperation.cc
@@ -17,6 +17,8 @@
*/
#include "COM_RotateOperation.h"
+#include "COM_ConstantOperation.h"
+
#include "BLI_math.h"
namespace blender::compositor {
@@ -31,13 +33,50 @@ RotateOperation::RotateOperation()
this->m_degreeSocket = nullptr;
this->m_doDegree2RadConversion = false;
this->m_isDegreeSet = false;
+ sampler_ = PixelSampler::Bilinear;
+}
+
+void RotateOperation::get_area_rotation_bounds(const rcti &area,
+ const float center_x,
+ const float center_y,
+ const float sine,
+ const float cosine,
+ rcti &r_bounds)
+{
+ const float dxmin = area.xmin - center_x;
+ const float dymin = area.ymin - center_y;
+ const float dxmax = area.xmax - center_x;
+ const float dymax = area.ymax - center_y;
+
+ const float x1 = center_x + (cosine * dxmin + sine * dymin);
+ const float x2 = center_x + (cosine * dxmax + sine * dymin);
+ const float x3 = center_x + (cosine * dxmin + sine * dymax);
+ const float x4 = center_x + (cosine * dxmax + sine * dymax);
+ const float y1 = center_y + (-sine * dxmin + cosine * dymin);
+ const float y2 = center_y + (-sine * dxmax + cosine * dymin);
+ const float y3 = center_y + (-sine * dxmin + cosine * dymax);
+ const float y4 = center_y + (-sine * dxmax + cosine * dymax);
+ const float minx = MIN2(x1, MIN2(x2, MIN2(x3, x4)));
+ const float maxx = MAX2(x1, MAX2(x2, MAX2(x3, x4)));
+ const float miny = MIN2(y1, MIN2(y2, MIN2(y3, y4)));
+ const float maxy = MAX2(y1, MAX2(y2, MAX2(y3, y4)));
+
+ r_bounds.xmin = floor(minx);
+ r_bounds.xmax = ceil(maxx);
+ r_bounds.ymin = floor(miny);
+ r_bounds.ymax = ceil(maxy);
+}
+
+void RotateOperation::init_data()
+{
+ this->m_centerX = (getWidth() - 1) / 2.0;
+ this->m_centerY = (getHeight() - 1) / 2.0;
}
+
void RotateOperation::initExecution()
{
this->m_imageSocket = this->getInputSocketReader(0);
this->m_degreeSocket = this->getInputSocketReader(1);
- this->m_centerX = (getWidth() - 1) / 2.0;
- this->m_centerY = (getHeight() - 1) / 2.0;
}
void RotateOperation::deinitExecution()
@@ -50,7 +89,19 @@ inline void RotateOperation::ensureDegree()
{
if (!this->m_isDegreeSet) {
float degree[4];
- this->m_degreeSocket->readSampled(degree, 0, 0, PixelSampler::Nearest);
+ switch (execution_model_) {
+ case eExecutionModel::Tiled:
+ 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;
+ break;
+ }
+
double rad;
if (this->m_doDegree2RadConversion) {
rad = DEG2RAD((double)degree[0]);
@@ -108,4 +159,33 @@ bool RotateOperation::determineDependingAreaOfInterest(rcti *input,
return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output);
}
+void RotateOperation::get_area_of_interest(const int input_idx,
+ const rcti &output_area,
+ rcti &r_input_area)
+{
+ if (input_idx == DEGREE_INPUT_INDEX) {
+ /* Degrees input is always used as constant. */
+ r_input_area = COM_SINGLE_ELEM_AREA;
+ return;
+ }
+
+ ensureDegree();
+ get_area_rotation_bounds(output_area, m_centerX, m_centerY, m_sine, m_cosine, r_input_area);
+ expand_area_for_sampler(r_input_area, sampler_);
+}
+
+void RotateOperation::update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs)
+{
+ ensureDegree();
+ const MemoryBuffer *input_img = inputs[IMAGE_INPUT_INDEX];
+ 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);
+ input_img->read_elem_sampled(x, y, sampler_, it.out);
+ }
+}
+
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_RotateOperation.h b/source/blender/compositor/operations/COM_RotateOperation.h
index d76507f9816..f0de699f9ce 100644
--- a/source/blender/compositor/operations/COM_RotateOperation.h
+++ b/source/blender/compositor/operations/COM_RotateOperation.h
@@ -18,12 +18,15 @@
#pragma once
-#include "COM_NodeOperation.h"
+#include "COM_MultiThreadedOperation.h"
namespace blender::compositor {
-class RotateOperation : public NodeOperation {
+class RotateOperation : public MultiThreadedOperation {
private:
+ constexpr static int IMAGE_INPUT_INDEX = 0;
+ constexpr static int DEGREE_INPUT_INDEX = 1;
+
SocketReader *m_imageSocket;
SocketReader *m_degreeSocket;
float m_centerX;
@@ -32,21 +35,51 @@ class RotateOperation : public NodeOperation {
float m_sine;
bool m_doDegree2RadConversion;
bool m_isDegreeSet;
+ PixelSampler sampler_;
public:
RotateOperation();
+
+ static void rotate_coords(
+ float &x, float &y, float center_x, float center_y, float sine, float cosine)
+ {
+ const float dx = x - center_x;
+ const float dy = y - center_y;
+ x = center_x + (cosine * dx + sine * dy);
+ y = center_y + (-sine * dx + cosine * dy);
+ }
+
+ 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);
+
bool determineDependingAreaOfInterest(rcti *input,
ReadBufferOperation *readOperation,
rcti *output) override;
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
+ void init_data() override;
void initExecution() override;
void deinitExecution() override;
+
void setDoDegree2RadConversion(bool abool)
{
this->m_doDegree2RadConversion = abool;
}
+ void set_sampler(PixelSampler sampler)
+ {
+ sampler_ = sampler;
+ }
+
void ensureDegree();
+
+ 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;
};
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_ScaleOperation.cc b/source/blender/compositor/operations/COM_ScaleOperation.cc
index 5410b2c832a..ef34bc7bee6 100644
--- a/source/blender/compositor/operations/COM_ScaleOperation.cc
+++ b/source/blender/compositor/operations/COM_ScaleOperation.cc
@@ -74,17 +74,18 @@ float ScaleOperation::get_constant_scale_y()
return get_constant_scale(2, get_relative_scale_y_factor());
}
-BLI_INLINE float scale_coord(const int coord, const float center, const float relative_scale)
+void ScaleOperation::scale_area(
+ rcti &rect, float center_x, float center_y, float scale_x, float scale_y)
{
- return center + (coord - center) / relative_scale;
+ 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);
}
void ScaleOperation::scale_area(rcti &rect, float scale_x, float scale_y)
{
- rect.xmin = scale_coord(rect.xmin, m_centerX, scale_x);
- rect.xmax = scale_coord(rect.xmax, m_centerX, scale_x);
- rect.ymin = scale_coord(rect.ymin, m_centerY, scale_y);
- rect.ymax = scale_coord(rect.ymax, m_centerY, scale_y);
+ scale_area(rect, m_centerX, m_centerY, scale_x, scale_y);
}
void ScaleOperation::init_data()
diff --git a/source/blender/compositor/operations/COM_ScaleOperation.h b/source/blender/compositor/operations/COM_ScaleOperation.h
index 62a2cabc8e6..65762d1ce62 100644
--- a/source/blender/compositor/operations/COM_ScaleOperation.h
+++ b/source/blender/compositor/operations/COM_ScaleOperation.h
@@ -46,6 +46,9 @@ class BaseScaleOperation : public MultiThreadedOperation {
};
class ScaleOperation : public BaseScaleOperation {
+ public:
+ static constexpr float MIN_SCALE = 0.0001f;
+
protected:
SocketReader *m_inputOperation;
SocketReader *m_inputXOperation;
@@ -57,6 +60,12 @@ class ScaleOperation : public BaseScaleOperation {
ScaleOperation();
ScaleOperation(DataType data_type);
+ static float scale_coord(const float coord, const float center, const float relative_scale)
+ {
+ return center + (coord - center) / MAX2(relative_scale, MIN_SCALE);
+ }
+ static void scale_area(rcti &rect, float center_x, float center_y, float scale_x, float scale_y);
+
void init_data() override;
void initExecution() override;
void deinitExecution() override;
diff --git a/source/blender/compositor/operations/COM_TransformOperation.cc b/source/blender/compositor/operations/COM_TransformOperation.cc
new file mode 100644
index 00000000000..2feaa0ae16d
--- /dev/null
+++ b/source/blender/compositor/operations/COM_TransformOperation.cc
@@ -0,0 +1,156 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Copyright 2021, Blender Foundation.
+ */
+
+#include "COM_TransformOperation.h"
+#include "COM_ConstantOperation.h"
+#include "COM_RotateOperation.h"
+#include "COM_ScaleOperation.h"
+
+#include "BLI_math.h"
+
+namespace blender::compositor {
+
+TransformOperation::TransformOperation()
+{
+ addInputSocket(DataType::Color);
+ addInputSocket(DataType::Value);
+ addInputSocket(DataType::Value);
+ addInputSocket(DataType::Value);
+ addInputSocket(DataType::Value);
+ addOutputSocket(DataType::Color);
+ translate_factor_x_ = 1.0f;
+ translate_factor_y_ = 1.0f;
+ convert_degree_to_rad_ = false;
+ sampler_ = PixelSampler::Bilinear;
+ invert_ = false;
+}
+
+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];
+ }
+
+ /* 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 double rad = convert_degree_to_rad_ ? DEG2RAD((double)degree) : degree;
+ rotate_cosine_ = cos(rad);
+ rotate_sine_ = sin(rad);
+}
+
+void TransformOperation::get_area_of_interest(const int input_idx,
+ const rcti &output_area,
+ rcti &r_input_area)
+{
+ 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);
+ 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_SINGLE_ELEM_AREA;
+ break;
+ }
+ case SCALE_INPUT_INDEX: {
+ r_input_area = output_area;
+ break;
+ }
+ }
+}
+
+void TransformOperation::update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ 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);
+ if (invert_) {
+ transform_inverted(it, input_img);
+ }
+ else {
+ transform(it, input_img);
+ }
+}
+
+void TransformOperation::transform(BuffersIterator<float> &it, const MemoryBuffer *input_img)
+{
+ for (; !it.is_end(); ++it) {
+ const float scale = *it.in(0);
+ float x = it.x - translate_x_;
+ float y = it.y - translate_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);
+ }
+}
+
+void TransformOperation::transform_inverted(BuffersIterator<float> &it,
+ const MemoryBuffer *input_img)
+{
+ 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);
+ RotateOperation::rotate_coords(
+ x, y, rotate_center_x_, rotate_center_y_, rotate_sine_, rotate_cosine_);
+ x -= translate_x_;
+ y -= translate_y_;
+ input_img->read_elem_sampled(x, y, sampler_, it.out);
+ }
+}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_TransformOperation.h b/source/blender/compositor/operations/COM_TransformOperation.h
new file mode 100644
index 00000000000..480998a0207
--- /dev/null
+++ b/source/blender/compositor/operations/COM_TransformOperation.h
@@ -0,0 +1,87 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Copyright 2021, Blender Foundation.
+ */
+
+#pragma once
+
+#include "COM_MultiThreadedOperation.h"
+
+namespace blender::compositor {
+
+class TransformOperation : public MultiThreadedOperation {
+ private:
+ constexpr static int IMAGE_INPUT_INDEX = 0;
+ constexpr static int X_INPUT_INDEX = 1;
+ constexpr static int Y_INPUT_INDEX = 2;
+ 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_;
+
+ /* Set variables. */
+ PixelSampler sampler_;
+ bool convert_degree_to_rad_;
+ float translate_factor_x_;
+ float translate_factor_y_;
+ bool invert_;
+
+ public:
+ TransformOperation();
+
+ void set_translate_factor_xy(float x, float y)
+ {
+ translate_factor_x_ = x;
+ translate_factor_y_ = y;
+ }
+
+ void set_convert_rotate_degree_to_rad(bool value)
+ {
+ convert_degree_to_rad_ = value;
+ }
+
+ void set_sampler(PixelSampler sampler)
+ {
+ sampler_ = sampler;
+ }
+
+ void set_invert(bool value)
+ {
+ invert_ = value;
+ }
+
+ 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;
+
+ 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);
+};
+
+} // namespace blender::compositor