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
This commit is contained in:
parent
a95e56b741
commit
064167fce7
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
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.addLink(angleAttribute->getOutputSocket(), rotateOperation->getInputSocket(1));
|
||||
converter.addOperation(scaleOperation);
|
||||
converter.addOperation(translateOperation);
|
||||
converter.addOperation(rotateOperation);
|
||||
converter.addOperation(psoperation);
|
||||
|
||||
converter.addLink(xAttribute->getOutputSocket(), translateOperation->getInputSocket(1));
|
||||
converter.addLink(yAttribute->getOutputSocket(), translateOperation->getInputSocket(2));
|
||||
converter.addLink(scaleAttribute->getOutputSocket(), scaleOperation->getInputSocket(1));
|
||||
converter.addLink(scaleAttribute->getOutputSocket(), scaleOperation->getInputSocket(2));
|
||||
|
||||
converter.mapOutputSocket(getOutputSocket(), psoperation->getOutputSocket());
|
||||
converter.addLink(angleAttribute->getOutputSocket(), rotateOperation->getInputSocket(1));
|
||||
|
||||
if (invert) {
|
||||
// Translate -> Rotate -> Scale.
|
||||
converter.mapInputSocket(imageInput, translateOperation->getInputSocket(0));
|
||||
converter.addLink(xAttribute->getOutputSocket(), translateOperation->getInputSocket(1));
|
||||
converter.addLink(yAttribute->getOutputSocket(), translateOperation->getInputSocket(2));
|
||||
|
||||
converter.addLink(translateOperation->getOutputSocket(), rotateOperation->getInputSocket(0));
|
||||
converter.addLink(rotateOperation->getOutputSocket(), scaleOperation->getInputSocket(0));
|
||||
converter.mapOutputSocket(getOutputSocket(), psoperation->getOutputSocket());
|
||||
|
||||
converter.addLink(scaleOperation->getOutputSocket(), psoperation->getInputSocket(0));
|
||||
}
|
||||
else {
|
||||
// Scale -> Rotate -> Translate.
|
||||
converter.mapInputSocket(imageInput, scaleOperation->getInputSocket(0));
|
||||
if (invert) {
|
||||
// Translate -> Rotate -> Scale.
|
||||
converter.mapInputSocket(imageInput, translateOperation->getInputSocket(0));
|
||||
|
||||
converter.addLink(scaleOperation->getOutputSocket(), rotateOperation->getInputSocket(0));
|
||||
converter.addLink(rotateOperation->getOutputSocket(), translateOperation->getInputSocket(0));
|
||||
converter.addLink(translateOperation->getOutputSocket(),
|
||||
rotateOperation->getInputSocket(0));
|
||||
converter.addLink(rotateOperation->getOutputSocket(), scaleOperation->getInputSocket(0));
|
||||
|
||||
converter.addLink(translateOperation->getOutputSocket(), psoperation->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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
|
@ -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
|
Loading…
Reference in New Issue