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:18 +0300
committerManuel Castilla <manzanillawork@gmail.com>2021-08-23 18:07:37 +0300
commit344aca3b1bf2718904455ea6cef1ffd8bedf51a6 (patch)
treee71d120e19ed64c1475250e226082c4325bfe2c1 /source/blender/compositor
parent064167fce70e3d7c382c374334a1bd0b520fe9fe (diff)
Compositor: Full frame distort nodes
Adds full frame implementation to "Displace", "Crop", "Flip", "Plane Track Deform", "Corner Pin", "Movie Distortion", "Lens Distortion" and "Map UV" nodes. The other nodes in "Distort" sub-menu are implemented separately in other commits. No functional changes. Part of T88150. Reviewed By: jbakker Differential Revision: https://developer.blender.org/D12166
Diffstat (limited to 'source/blender/compositor')
-rw-r--r--source/blender/compositor/operations/COM_CropOperation.cc45
-rw-r--r--source/blender/compositor/operations/COM_CropOperation.h13
-rw-r--r--source/blender/compositor/operations/COM_DisplaceOperation.cc95
-rw-r--r--source/blender/compositor/operations/COM_DisplaceOperation.h22
-rw-r--r--source/blender/compositor/operations/COM_DisplaceSimpleOperation.cc52
-rw-r--r--source/blender/compositor/operations/COM_DisplaceSimpleOperation.h9
-rw-r--r--source/blender/compositor/operations/COM_FlipOperation.cc38
-rw-r--r--source/blender/compositor/operations/COM_FlipOperation.h9
-rw-r--r--source/blender/compositor/operations/COM_MapUVOperation.cc97
-rw-r--r--source/blender/compositor/operations/COM_MapUVOperation.h21
-rw-r--r--source/blender/compositor/operations/COM_MovieDistortionOperation.cc47
-rw-r--r--source/blender/compositor/operations/COM_MovieDistortionOperation.h9
-rw-r--r--source/blender/compositor/operations/COM_PlaneCornerPinOperation.cc99
-rw-r--r--source/blender/compositor/operations/COM_PlaneCornerPinOperation.h7
-rw-r--r--source/blender/compositor/operations/COM_PlaneDistortCommonOperation.cc115
-rw-r--r--source/blender/compositor/operations/COM_PlaneDistortCommonOperation.h16
-rw-r--r--source/blender/compositor/operations/COM_PlaneTrackOperation.cc26
-rw-r--r--source/blender/compositor/operations/COM_PlaneTrackOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_ProjectorLensDistortionOperation.cc54
-rw-r--r--source/blender/compositor/operations/COM_ProjectorLensDistortionOperation.h11
-rw-r--r--source/blender/compositor/operations/COM_ScreenLensDistortionOperation.cc189
-rw-r--r--source/blender/compositor/operations/COM_ScreenLensDistortionOperation.h13
22 files changed, 934 insertions, 57 deletions
diff --git a/source/blender/compositor/operations/COM_CropOperation.cc b/source/blender/compositor/operations/COM_CropOperation.cc
index f12d93bc8d3..12833660fcb 100644
--- a/source/blender/compositor/operations/COM_CropOperation.cc
+++ b/source/blender/compositor/operations/COM_CropOperation.cc
@@ -95,6 +95,22 @@ void CropOperation::executePixelSampled(float output[4], float x, float y, Pixel
}
}
+void CropOperation::update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs)
+{
+ rcti crop_area;
+ BLI_rcti_init(&crop_area, m_xmin, m_xmax, m_ymin, m_ymax);
+ for (BuffersIterator<float> it = output->iterate_with(inputs, area); !it.is_end(); ++it) {
+ if (BLI_rcti_isect_pt(&crop_area, it.x, it.y)) {
+ copy_v4_v4(it.out, it.in(0));
+ }
+ else {
+ zero_v4(it.out);
+ }
+ }
+}
+
CropImageOperation::CropImageOperation() : CropBaseOperation()
{
/* pass */
@@ -114,6 +130,18 @@ bool CropImageOperation::determineDependingAreaOfInterest(rcti *input,
return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output);
}
+void CropImageOperation::get_area_of_interest(const int input_idx,
+ const rcti &output_area,
+ rcti &r_input_area)
+{
+ BLI_assert(input_idx == 0);
+ UNUSED_VARS_NDEBUG(input_idx);
+ r_input_area.xmax = output_area.xmax + this->m_xmin;
+ r_input_area.xmin = output_area.xmin + this->m_xmin;
+ r_input_area.ymax = output_area.ymax + this->m_ymin;
+ r_input_area.ymin = output_area.ymin + this->m_ymin;
+}
+
void CropImageOperation::determineResolution(unsigned int resolution[2],
unsigned int preferredResolution[2])
{
@@ -136,4 +164,21 @@ void CropImageOperation::executePixelSampled(float output[4],
}
}
+void CropImageOperation::update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs)
+{
+ rcti op_area;
+ BLI_rcti_init(&op_area, 0, getWidth(), 0, getHeight());
+ const MemoryBuffer *input = inputs[0];
+ for (BuffersIterator<float> it = output->iterate_with({}, area); !it.is_end(); ++it) {
+ if (BLI_rcti_isect_pt(&op_area, it.x, it.y)) {
+ input->read_elem_checked(it.x + this->m_xmin, it.y + this->m_ymin, it.out);
+ }
+ else {
+ zero_v4(it.out);
+ }
+ }
+}
+
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_CropOperation.h b/source/blender/compositor/operations/COM_CropOperation.h
index acdff79a77c..57caa4e5834 100644
--- a/source/blender/compositor/operations/COM_CropOperation.h
+++ b/source/blender/compositor/operations/COM_CropOperation.h
@@ -18,11 +18,11 @@
#pragma once
-#include "COM_NodeOperation.h"
+#include "COM_MultiThreadedOperation.h"
namespace blender::compositor {
-class CropBaseOperation : public NodeOperation {
+class CropBaseOperation : public MultiThreadedOperation {
protected:
SocketReader *m_inputOperation;
NodeTwoXYs *m_settings;
@@ -53,6 +53,10 @@ class CropOperation : public CropBaseOperation {
public:
CropOperation();
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
+
+ void update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs) override;
};
class CropImageOperation : public CropBaseOperation {
@@ -65,6 +69,11 @@ class CropImageOperation : public CropBaseOperation {
void determineResolution(unsigned int resolution[2],
unsigned int preferredResolution[2]) override;
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) 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;
};
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_DisplaceOperation.cc b/source/blender/compositor/operations/COM_DisplaceOperation.cc
index 9f3f5cfe489..a4c01fda7ca 100644
--- a/source/blender/compositor/operations/COM_DisplaceOperation.cc
+++ b/source/blender/compositor/operations/COM_DisplaceOperation.cc
@@ -32,20 +32,30 @@ DisplaceOperation::DisplaceOperation()
this->flags.complex = true;
this->m_inputColorProgram = nullptr;
- this->m_inputVectorProgram = nullptr;
- this->m_inputScaleXProgram = nullptr;
- this->m_inputScaleYProgram = nullptr;
}
void DisplaceOperation::initExecution()
{
this->m_inputColorProgram = this->getInputSocketReader(0);
- this->m_inputVectorProgram = this->getInputSocketReader(1);
- this->m_inputScaleXProgram = this->getInputSocketReader(2);
- this->m_inputScaleYProgram = this->getInputSocketReader(3);
+ NodeOperation *vector = this->getInputSocketReader(1);
+ NodeOperation *scale_x = this->getInputSocketReader(2);
+ NodeOperation *scale_y = this->getInputSocketReader(3);
+ if (execution_model_ == eExecutionModel::Tiled) {
+ vector_read_fn_ = [=](float x, float y, float *out) {
+ vector->readSampled(out, x, y, PixelSampler::Bilinear);
+ };
+ scale_x_read_fn_ = [=](float x, float y, float *out) {
+ scale_x->readSampled(out, x, y, PixelSampler::Nearest);
+ };
+ scale_y_read_fn_ = [=](float x, float y, float *out) {
+ scale_y->readSampled(out, x, y, PixelSampler::Nearest);
+ };
+ }
this->m_width_x4 = this->getWidth() * 4;
this->m_height_x4 = this->getHeight() * 4;
+ input_vector_width_ = vector->getWidth();
+ input_vector_height_ = vector->getHeight();
}
void DisplaceOperation::executePixelSampled(float output[4],
@@ -69,8 +79,8 @@ void DisplaceOperation::executePixelSampled(float output[4],
bool DisplaceOperation::read_displacement(
float x, float y, float xscale, float yscale, const float origin[2], float &r_u, float &r_v)
{
- float width = m_inputVectorProgram->getWidth();
- float height = m_inputVectorProgram->getHeight();
+ float width = input_vector_width_;
+ float height = input_vector_height_;
if (x < 0.0f || x >= width || y < 0.0f || y >= height) {
r_u = 0.0f;
r_v = 0.0f;
@@ -78,7 +88,7 @@ bool DisplaceOperation::read_displacement(
}
float col[4];
- m_inputVectorProgram->readSampled(col, x, y, PixelSampler::Bilinear);
+ vector_read_fn_(x, y, col);
r_u = origin[0] - col[0] * xscale;
r_v = origin[1] - col[1] * yscale;
return true;
@@ -90,9 +100,9 @@ void DisplaceOperation::pixelTransform(const float xy[2], float r_uv[2], float r
float uv[2]; /* temporary variables for derivative estimation */
int num;
- m_inputScaleXProgram->readSampled(col, xy[0], xy[1], PixelSampler::Nearest);
+ scale_x_read_fn_(xy[0], xy[1], col);
float xs = col[0];
- m_inputScaleYProgram->readSampled(col, xy[0], xy[1], PixelSampler::Nearest);
+ scale_y_read_fn_(xy[0], xy[1], col);
float ys = col[0];
/* clamp x and y displacement to triple image resolution -
* to prevent hangs from huge values mistakenly plugged in eg. z buffers */
@@ -146,9 +156,9 @@ void DisplaceOperation::pixelTransform(const float xy[2], float r_uv[2], float r
void DisplaceOperation::deinitExecution()
{
this->m_inputColorProgram = nullptr;
- this->m_inputVectorProgram = nullptr;
- this->m_inputScaleXProgram = nullptr;
- this->m_inputScaleYProgram = nullptr;
+ vector_read_fn_ = nullptr;
+ scale_x_read_fn_ = nullptr;
+ scale_y_read_fn_ = nullptr;
}
bool DisplaceOperation::determineDependingAreaOfInterest(rcti *input,
@@ -195,4 +205,61 @@ bool DisplaceOperation::determineDependingAreaOfInterest(rcti *input,
return false;
}
+void DisplaceOperation::get_area_of_interest(const int input_idx,
+ const rcti &output_area,
+ rcti &r_input_area)
+{
+ switch (input_idx) {
+ case 0: {
+ r_input_area.xmin = 0;
+ r_input_area.ymin = 0;
+ r_input_area.xmax = getInputOperation(input_idx)->getWidth();
+ r_input_area.ymax = getInputOperation(input_idx)->getHeight();
+ break;
+ }
+ case 1: {
+ r_input_area = output_area;
+ expand_area_for_sampler(r_input_area, PixelSampler::Bilinear);
+ break;
+ }
+ default: {
+ r_input_area = output_area;
+ break;
+ }
+ }
+}
+
+void DisplaceOperation::update_memory_buffer_started(MemoryBuffer *UNUSED(output),
+ const rcti &UNUSED(area),
+ Span<MemoryBuffer *> inputs)
+{
+ MemoryBuffer *vector = inputs[1];
+ MemoryBuffer *scale_x = inputs[2];
+ MemoryBuffer *scale_y = inputs[3];
+ vector_read_fn_ = [=](float x, float y, float *out) { vector->read_elem_bilinear(x, y, out); };
+ scale_x_read_fn_ = [=](float x, float y, float *out) { scale_x->read_elem_checked(x, y, out); };
+ scale_y_read_fn_ = [=](float x, float y, float *out) { scale_y->read_elem_checked(x, y, out); };
+}
+
+void DisplaceOperation::update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs)
+{
+ const MemoryBuffer *input_color = inputs[0];
+ for (BuffersIterator<float> it = output->iterate_with({}, area); !it.is_end(); ++it) {
+ const float xy[2] = {(float)it.x, (float)it.y};
+ float uv[2];
+ float deriv[2][2];
+
+ pixelTransform(xy, uv, deriv);
+ if (is_zero_v2(deriv[0]) && is_zero_v2(deriv[1])) {
+ input_color->read_elem_bilinear(uv[0], uv[1], it.out);
+ }
+ else {
+ /* EWA filtering (without nearest it gets blurry with NO distortion). */
+ input_color->read_elem_filtered(uv[0], uv[1], deriv[0], deriv[1], it.out);
+ }
+ }
+}
+
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_DisplaceOperation.h b/source/blender/compositor/operations/COM_DisplaceOperation.h
index fd82692f687..5be914ab672 100644
--- a/source/blender/compositor/operations/COM_DisplaceOperation.h
+++ b/source/blender/compositor/operations/COM_DisplaceOperation.h
@@ -18,23 +18,27 @@
#pragma once
-#include "COM_NodeOperation.h"
+#include "COM_MultiThreadedOperation.h"
namespace blender::compositor {
-class DisplaceOperation : public NodeOperation {
+class DisplaceOperation : public MultiThreadedOperation {
private:
/**
* Cached reference to the inputProgram
*/
SocketReader *m_inputColorProgram;
- SocketReader *m_inputVectorProgram;
- SocketReader *m_inputScaleXProgram;
- SocketReader *m_inputScaleYProgram;
float m_width_x4;
float m_height_x4;
+ int input_vector_width_;
+ int input_vector_height_;
+
+ std::function<void(float x, float y, float *out)> vector_read_fn_;
+ std::function<void(float x, float y, float *out)> scale_x_read_fn_;
+ std::function<void(float x, float y, float *out)> scale_y_read_fn_;
+
public:
DisplaceOperation();
@@ -62,6 +66,14 @@ class DisplaceOperation : public NodeOperation {
*/
void deinitExecution() override;
+ void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override;
+ void update_memory_buffer_started(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs) override;
+ void update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs) override;
+
private:
bool read_displacement(
float x, float y, float xscale, float yscale, const float origin[2], float &r_u, float &r_v);
diff --git a/source/blender/compositor/operations/COM_DisplaceSimpleOperation.cc b/source/blender/compositor/operations/COM_DisplaceSimpleOperation.cc
index f4b77f5d32c..e1c531bd49e 100644
--- a/source/blender/compositor/operations/COM_DisplaceSimpleOperation.cc
+++ b/source/blender/compositor/operations/COM_DisplaceSimpleOperation.cc
@@ -132,4 +132,56 @@ bool DisplaceSimpleOperation::determineDependingAreaOfInterest(rcti *input,
return false;
}
+void DisplaceSimpleOperation::get_area_of_interest(const int input_idx,
+ const rcti &output_area,
+ rcti &r_input_area)
+{
+ switch (input_idx) {
+ case 0: {
+ r_input_area.xmin = 0;
+ r_input_area.ymin = 0;
+ r_input_area.xmax = getInputOperation(input_idx)->getWidth();
+ r_input_area.ymax = getInputOperation(input_idx)->getHeight();
+ break;
+ }
+ default: {
+ r_input_area = output_area;
+ break;
+ }
+ }
+}
+
+void DisplaceSimpleOperation::update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs)
+{
+ const float width = this->getWidth();
+ const float height = this->getHeight();
+ const MemoryBuffer *input_color = inputs[0];
+ for (BuffersIterator<float> it = output->iterate_with(inputs.drop_front(1), area); !it.is_end();
+ ++it) {
+ float scale_x = *it.in(1);
+ float scale_y = *it.in(2);
+
+ /* Clamp x and y displacement to triple image resolution -
+ * to prevent hangs from huge values mistakenly plugged in eg. z buffers. */
+ CLAMP(scale_x, -m_width_x4, m_width_x4);
+ CLAMP(scale_y, -m_height_x4, m_height_x4);
+
+ /* Main displacement in pixel space. */
+ const float *vector = it.in(0);
+ const float p_dx = vector[0] * scale_x;
+ const float p_dy = vector[1] * scale_y;
+
+ /* Displaced pixel in uv coords, for image sampling. */
+ /* Clamp nodes to avoid glitches. */
+ float u = it.x - p_dx + 0.5f;
+ float v = it.y - p_dy + 0.5f;
+ CLAMP(u, 0.0f, width - 1.0f);
+ CLAMP(v, 0.0f, height - 1.0f);
+
+ input_color->read_elem_checked(u, v, it.out);
+ }
+}
+
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_DisplaceSimpleOperation.h b/source/blender/compositor/operations/COM_DisplaceSimpleOperation.h
index 15e6fcd0523..99f52155466 100644
--- a/source/blender/compositor/operations/COM_DisplaceSimpleOperation.h
+++ b/source/blender/compositor/operations/COM_DisplaceSimpleOperation.h
@@ -18,11 +18,11 @@
#pragma once
-#include "COM_NodeOperation.h"
+#include "COM_MultiThreadedOperation.h"
namespace blender::compositor {
-class DisplaceSimpleOperation : public NodeOperation {
+class DisplaceSimpleOperation : public MultiThreadedOperation {
private:
/**
* Cached reference to the inputProgram
@@ -59,6 +59,11 @@ class DisplaceSimpleOperation : public NodeOperation {
* Deinitialize the execution
*/
void deinitExecution() 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;
};
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_FlipOperation.cc b/source/blender/compositor/operations/COM_FlipOperation.cc
index 8afbec4ddbe..d0dc6c0b570 100644
--- a/source/blender/compositor/operations/COM_FlipOperation.cc
+++ b/source/blender/compositor/operations/COM_FlipOperation.cc
@@ -75,4 +75,42 @@ bool FlipOperation::determineDependingAreaOfInterest(rcti *input,
return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output);
}
+void FlipOperation::get_area_of_interest(const int input_idx,
+ const rcti &output_area,
+ rcti &r_input_area)
+{
+ BLI_assert(input_idx == 0);
+ UNUSED_VARS_NDEBUG(input_idx);
+ if (this->m_flipX) {
+ const int w = (int)this->getWidth() - 1;
+ r_input_area.xmax = (w - output_area.xmin) + 1;
+ r_input_area.xmin = (w - output_area.xmax) - 1;
+ }
+ else {
+ r_input_area.xmin = output_area.xmin;
+ r_input_area.xmax = output_area.xmax;
+ }
+ if (this->m_flipY) {
+ const int h = (int)this->getHeight() - 1;
+ r_input_area.ymax = (h - output_area.ymin) + 1;
+ r_input_area.ymin = (h - output_area.ymax) - 1;
+ }
+ else {
+ r_input_area.ymin = output_area.ymin;
+ r_input_area.ymax = output_area.ymax;
+ }
+}
+
+void FlipOperation::update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs)
+{
+ const MemoryBuffer *input_img = inputs[0];
+ for (BuffersIterator<float> it = output->iterate_with({}, area); !it.is_end(); ++it) {
+ const int nx = this->m_flipX ? ((int)this->getWidth() - 1) - it.x : it.x;
+ const int ny = this->m_flipY ? ((int)this->getHeight() - 1) - it.y : it.y;
+ input_img->read_elem(nx, ny, it.out);
+ }
+}
+
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_FlipOperation.h b/source/blender/compositor/operations/COM_FlipOperation.h
index f26d587fde6..dba7f82c341 100644
--- a/source/blender/compositor/operations/COM_FlipOperation.h
+++ b/source/blender/compositor/operations/COM_FlipOperation.h
@@ -18,11 +18,11 @@
#pragma once
-#include "COM_NodeOperation.h"
+#include "COM_MultiThreadedOperation.h"
namespace blender::compositor {
-class FlipOperation : public NodeOperation {
+class FlipOperation : public MultiThreadedOperation {
private:
SocketReader *m_inputOperation;
bool m_flipX;
@@ -45,6 +45,11 @@ class FlipOperation : public NodeOperation {
{
this->m_flipY = flipY;
}
+
+ 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_MapUVOperation.cc b/source/blender/compositor/operations/COM_MapUVOperation.cc
index 74e3d965d41..ad047c619f8 100644
--- a/source/blender/compositor/operations/COM_MapUVOperation.cc
+++ b/source/blender/compositor/operations/COM_MapUVOperation.cc
@@ -34,10 +34,26 @@ MapUVOperation::MapUVOperation()
this->m_inputColorProgram = nullptr;
}
+void MapUVOperation::init_data()
+{
+ NodeOperation *image_input = get_input_operation(0);
+ image_width_ = image_input->getWidth();
+ image_height_ = image_input->getHeight();
+
+ NodeOperation *uv_input = get_input_operation(1);
+ uv_width_ = uv_input->getWidth();
+ uv_height_ = uv_input->getHeight();
+}
+
void MapUVOperation::initExecution()
{
this->m_inputColorProgram = this->getInputSocketReader(0);
this->m_inputUVProgram = this->getInputSocketReader(1);
+ if (execution_model_ == eExecutionModel::Tiled) {
+ uv_input_read_fn_ = [=](float x, float y, float *out) {
+ this->m_inputUVProgram->readSampled(out, x, y, PixelSampler::Bilinear);
+ };
+ }
}
void MapUVOperation::executePixelSampled(float output[4],
@@ -81,9 +97,7 @@ void MapUVOperation::executePixelSampled(float output[4],
bool MapUVOperation::read_uv(float x, float y, float &r_u, float &r_v, float &r_alpha)
{
- float width = m_inputUVProgram->getWidth();
- float height = m_inputUVProgram->getHeight();
- if (x < 0.0f || x >= width || y < 0.0f || y >= height) {
+ if (x < 0.0f || x >= uv_width_ || y < 0.0f || y >= uv_height_) {
r_u = 0.0f;
r_v = 0.0f;
r_alpha = 0.0f;
@@ -91,9 +105,9 @@ bool MapUVOperation::read_uv(float x, float y, float &r_u, float &r_v, float &r_
}
float vector[3];
- m_inputUVProgram->readSampled(vector, x, y, PixelSampler::Bilinear);
- r_u = vector[0] * m_inputColorProgram->getWidth();
- r_v = vector[1] * m_inputColorProgram->getHeight();
+ uv_input_read_fn_(x, y, vector);
+ r_u = vector[0] * image_width_;
+ r_v = vector[1] * image_height_;
r_alpha = vector[2];
return true;
}
@@ -186,4 +200,75 @@ bool MapUVOperation::determineDependingAreaOfInterest(rcti *input,
return false;
}
+void MapUVOperation::get_area_of_interest(const int input_idx,
+ const rcti &output_area,
+ rcti &r_input_area)
+{
+ switch (input_idx) {
+ case 0: {
+ r_input_area.xmin = 0;
+ r_input_area.xmax = image_width_;
+ r_input_area.ymin = 0;
+ r_input_area.ymax = image_height_;
+ break;
+ }
+ case 1: {
+ r_input_area = output_area;
+ expand_area_for_sampler(r_input_area, PixelSampler::Bilinear);
+ break;
+ }
+ }
+}
+
+void MapUVOperation::update_memory_buffer_started(MemoryBuffer *UNUSED(output),
+ const rcti &UNUSED(area),
+ Span<MemoryBuffer *> inputs)
+{
+ const MemoryBuffer *uv_input = inputs[1];
+ uv_input_read_fn_ = [=](float x, float y, float *out) {
+ uv_input->read_elem_bilinear(x, y, out);
+ };
+}
+
+void MapUVOperation::update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs)
+{
+ const MemoryBuffer *input_image = inputs[0];
+ for (BuffersIterator<float> it = output->iterate_with({}, area); !it.is_end(); ++it) {
+ float xy[2] = {(float)it.x, (float)it.y};
+ float uv[2];
+ float deriv[2][2];
+ float alpha;
+ pixelTransform(xy, uv, deriv, alpha);
+ if (alpha == 0.0f) {
+ zero_v4(it.out);
+ continue;
+ }
+
+ /* EWA filtering. */
+ input_image->read_elem_filtered(uv[0], uv[1], deriv[0], deriv[1], it.out);
+
+ /* UV to alpha threshold. */
+ const float threshold = this->m_alpha * 0.05f;
+ /* XXX alpha threshold is used to fade out pixels on boundaries with invalid derivatives.
+ * this calculation is not very well defined, should be looked into if it becomes a problem ...
+ */
+ const float du = len_v2(deriv[0]);
+ const float dv = len_v2(deriv[1]);
+ const float factor = 1.0f - threshold * (du / image_width_ + dv / image_height_);
+ if (factor < 0.0f) {
+ alpha = 0.0f;
+ }
+ else {
+ alpha *= factor;
+ }
+
+ /* "premul" */
+ if (alpha < 1.0f) {
+ mul_v4_fl(it.out, alpha);
+ }
+ }
+}
+
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_MapUVOperation.h b/source/blender/compositor/operations/COM_MapUVOperation.h
index eb5f7d49122..65fbcb461c9 100644
--- a/source/blender/compositor/operations/COM_MapUVOperation.h
+++ b/source/blender/compositor/operations/COM_MapUVOperation.h
@@ -18,11 +18,11 @@
#pragma once
-#include "COM_NodeOperation.h"
+#include "COM_MultiThreadedOperation.h"
namespace blender::compositor {
-class MapUVOperation : public NodeOperation {
+class MapUVOperation : public MultiThreadedOperation {
private:
/**
* Cached reference to the inputProgram
@@ -30,8 +30,15 @@ class MapUVOperation : public NodeOperation {
SocketReader *m_inputUVProgram;
SocketReader *m_inputColorProgram;
+ int uv_width_;
+ int uv_height_;
+ int image_width_;
+ int image_height_;
+
float m_alpha;
+ std::function<void(float x, float y, float *out)> uv_input_read_fn_;
+
public:
MapUVOperation();
@@ -49,6 +56,8 @@ class MapUVOperation : public NodeOperation {
void pixelTransform(const float xy[2], float r_uv[2], float r_deriv[2][2], float &r_alpha);
+ void init_data() override;
+
/**
* Initialize the execution
*/
@@ -64,6 +73,14 @@ class MapUVOperation : public NodeOperation {
this->m_alpha = alpha;
}
+ void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override;
+ void update_memory_buffer_started(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs) override;
+ void update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs) override;
+
private:
bool read_uv(float x, float y, float &r_u, float &r_v, float &r_alpha);
};
diff --git a/source/blender/compositor/operations/COM_MovieDistortionOperation.cc b/source/blender/compositor/operations/COM_MovieDistortionOperation.cc
index c8e045ea117..d3424959061 100644
--- a/source/blender/compositor/operations/COM_MovieDistortionOperation.cc
+++ b/source/blender/compositor/operations/COM_MovieDistortionOperation.cc
@@ -128,4 +128,51 @@ bool MovieDistortionOperation::determineDependingAreaOfInterest(rcti *input,
return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output);
}
+void MovieDistortionOperation::get_area_of_interest(const int input_idx,
+ const rcti &output_area,
+ rcti &r_input_area)
+{
+ BLI_assert(input_idx == 0);
+ UNUSED_VARS_NDEBUG(input_idx);
+ r_input_area.xmin = output_area.xmin - m_margin[0];
+ r_input_area.ymin = output_area.ymin - m_margin[1];
+ r_input_area.xmax = output_area.xmax + m_margin[0];
+ r_input_area.ymax = output_area.ymax + m_margin[1];
+}
+
+void MovieDistortionOperation::update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs)
+{
+ const MemoryBuffer *input_img = inputs[0];
+ if (this->m_distortion == nullptr) {
+ output->copy_from(input_img, area);
+ return;
+ }
+
+ /* `float overscan = 0.0f;` */
+ const float pixel_aspect = this->m_pixel_aspect;
+ const float w = (float)this->m_width /* `/ (1 + overscan)` */;
+ const float h = (float)this->m_height /* `/ (1 + overscan)` */;
+ const float aspx = w / (float)this->m_calibration_width;
+ const float aspy = h / (float)this->m_calibration_height;
+ float xy[2];
+ float distorted_xy[2];
+ for (BuffersIterator<float> it = output->iterate_with({}, area); !it.is_end(); ++it) {
+ xy[0] = (it.x /* `- 0.5 * overscan * w` */) / aspx;
+ xy[1] = (it.y /* `- 0.5 * overscan * h` */) / aspy / pixel_aspect;
+
+ if (this->m_apply) {
+ BKE_tracking_distortion_undistort_v2(this->m_distortion, xy, distorted_xy);
+ }
+ else {
+ BKE_tracking_distortion_distort_v2(this->m_distortion, xy, distorted_xy);
+ }
+
+ const float u = distorted_xy[0] * aspx /* `+ 0.5 * overscan * w` */;
+ const float v = (distorted_xy[1] * aspy /* `+ 0.5 * overscan * h` */) * pixel_aspect;
+ input_img->read_elem_bilinear(u, v, it.out);
+ }
+}
+
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_MovieDistortionOperation.h b/source/blender/compositor/operations/COM_MovieDistortionOperation.h
index 631a62f7ebf..69c2f9c269c 100644
--- a/source/blender/compositor/operations/COM_MovieDistortionOperation.h
+++ b/source/blender/compositor/operations/COM_MovieDistortionOperation.h
@@ -18,7 +18,7 @@
#pragma once
-#include "COM_NodeOperation.h"
+#include "COM_MultiThreadedOperation.h"
#include "DNA_movieclip_types.h"
#include "MEM_guardedalloc.h"
@@ -26,7 +26,7 @@
namespace blender::compositor {
-class MovieDistortionOperation : public NodeOperation {
+class MovieDistortionOperation : public MultiThreadedOperation {
private:
SocketReader *m_inputOperation;
MovieClip *m_movieClip;
@@ -58,6 +58,11 @@ class MovieDistortionOperation : public NodeOperation {
bool determineDependingAreaOfInterest(rcti *input,
ReadBufferOperation *readOperation,
rcti *output) 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;
};
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_PlaneCornerPinOperation.cc b/source/blender/compositor/operations/COM_PlaneCornerPinOperation.cc
index 3577860b93d..d2a06ddd7c4 100644
--- a/source/blender/compositor/operations/COM_PlaneCornerPinOperation.cc
+++ b/source/blender/compositor/operations/COM_PlaneCornerPinOperation.cc
@@ -16,6 +16,7 @@
*/
#include "COM_PlaneCornerPinOperation.h"
+#include "COM_ConstantOperation.h"
#include "COM_ReadBufferOperation.h"
#include "MEM_guardedalloc.h"
@@ -28,6 +29,11 @@
namespace blender::compositor {
+constexpr int LOWER_LEFT_CORNER_INDEX = 0;
+constexpr int LOWER_RIGHT_CORNER_INDEX = 1;
+constexpr int UPPER_RIGHT_CORNER_INDEX = 2;
+constexpr int UPPER_LEFT_CORNER_INDEX = 3;
+
static bool check_corners(float corners[4][2])
{
int i, next, prev;
@@ -58,6 +64,7 @@ static bool check_corners(float corners[4][2])
return true;
}
+/* TODO(manzanilla): to be removed with tiled implementation. */
static void readCornersFromSockets(rcti *rect, SocketReader *readers[4], float corners[4][2])
{
for (int i = 0; i < 4; i++) {
@@ -87,6 +94,53 @@ static void readCornersFromSockets(rcti *rect, SocketReader *readers[4], float c
}
}
+static void set_default_corner(const int corner_idx, float r_corner[2])
+{
+ BLI_assert(corner_idx >= 0 && corner_idx < 4);
+ switch (corner_idx) {
+ case LOWER_LEFT_CORNER_INDEX:
+ r_corner[0] = 0.0f;
+ r_corner[1] = 0.0f;
+ break;
+ case LOWER_RIGHT_CORNER_INDEX:
+ r_corner[0] = 1.0f;
+ r_corner[1] = 0.0f;
+ break;
+ case UPPER_RIGHT_CORNER_INDEX:
+ r_corner[0] = 1.0f;
+ r_corner[1] = 1.0f;
+ break;
+ case UPPER_LEFT_CORNER_INDEX:
+ r_corner[0] = 0.0f;
+ r_corner[1] = 1.0f;
+ break;
+ }
+}
+
+static void read_input_corners(NodeOperation *op, const int first_input_idx, float r_corners[4][2])
+{
+ for (const int i : IndexRange(4)) {
+ NodeOperation *input = op->get_input_operation(i + first_input_idx);
+ if (input->get_flags().is_constant_operation) {
+ ConstantOperation *corner_input = static_cast<ConstantOperation *>(input);
+ copy_v2_v2(r_corners[i], corner_input->get_constant_elem());
+ }
+ else {
+ set_default_corner(i, r_corners[i]);
+ }
+ }
+
+ /* Convexity check: concave corners need to be prevented, otherwise
+ * #BKE_tracking_homography_between_two_quads will freeze. */
+ if (!check_corners(r_corners)) {
+ /* Revert to default corners. There could be a more elegant solution,
+ * this prevents freezing at least. */
+ for (const int i : IndexRange(4)) {
+ set_default_corner(i, r_corners[i]);
+ }
+ }
+}
+
/* ******** PlaneCornerPinMaskOperation ******** */
PlaneCornerPinMaskOperation::PlaneCornerPinMaskOperation() : m_corners_ready(false)
@@ -103,6 +157,17 @@ PlaneCornerPinMaskOperation::PlaneCornerPinMaskOperation() : m_corners_ready(fal
flags.complex = true;
}
+void PlaneCornerPinMaskOperation::init_data()
+{
+ if (execution_model_ == eExecutionModel::FullFrame) {
+ float corners[4][2];
+ read_input_corners(this, 0, corners);
+ calculateCorners(corners, true, 0);
+ }
+}
+
+/* TODO(manzanilla): to be removed with tiled implementation. Same for #deinitExecution and do the
+ * same on #PlaneCornerPinWarpImageOperation. */
void PlaneCornerPinMaskOperation::initExecution()
{
PlaneDistortMaskOperation::initExecution();
@@ -147,10 +212,22 @@ void *PlaneCornerPinMaskOperation::initializeTileData(rcti *rect)
void PlaneCornerPinMaskOperation::determineResolution(unsigned int resolution[2],
unsigned int preferredResolution[2])
{
+ if (execution_model_ == eExecutionModel::FullFrame) {
+ /* Determine inputs resolution. */
+ PlaneDistortMaskOperation::determineResolution(resolution, preferredResolution);
+ }
resolution[0] = preferredResolution[0];
resolution[1] = preferredResolution[1];
}
+void PlaneCornerPinMaskOperation::get_area_of_interest(const int UNUSED(input_idx),
+ const rcti &UNUSED(output_area),
+ rcti &r_input_area)
+{
+ /* All corner inputs are used as constants. */
+ r_input_area = COM_SINGLE_ELEM_AREA;
+}
+
/* ******** PlaneCornerPinWarpImageOperation ******** */
PlaneCornerPinWarpImageOperation::PlaneCornerPinWarpImageOperation() : m_corners_ready(false)
@@ -161,6 +238,15 @@ PlaneCornerPinWarpImageOperation::PlaneCornerPinWarpImageOperation() : m_corners
addInputSocket(DataType::Vector);
}
+void PlaneCornerPinWarpImageOperation::init_data()
+{
+ if (execution_model_ == eExecutionModel::FullFrame) {
+ float corners[4][2];
+ read_input_corners(this, 1, corners);
+ calculateCorners(corners, true, 0);
+ }
+}
+
void PlaneCornerPinWarpImageOperation::initExecution()
{
PlaneDistortWarpImageOperation::initExecution();
@@ -227,4 +313,17 @@ bool PlaneCornerPinWarpImageOperation::determineDependingAreaOfInterest(
#endif
}
+void PlaneCornerPinWarpImageOperation::get_area_of_interest(const int input_idx,
+ const rcti &output_area,
+ rcti &r_input_area)
+{
+ if (input_idx == 0) {
+ PlaneDistortWarpImageOperation::get_area_of_interest(input_idx, output_area, r_input_area);
+ }
+ else {
+ /* Corner inputs are used as constants. */
+ r_input_area = COM_SINGLE_ELEM_AREA;
+ }
+}
+
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_PlaneCornerPinOperation.h b/source/blender/compositor/operations/COM_PlaneCornerPinOperation.h
index 91c0cd9e16b..2831e937147 100644
--- a/source/blender/compositor/operations/COM_PlaneCornerPinOperation.h
+++ b/source/blender/compositor/operations/COM_PlaneCornerPinOperation.h
@@ -31,11 +31,13 @@ namespace blender::compositor {
class PlaneCornerPinMaskOperation : public PlaneDistortMaskOperation {
private:
+ /* TODO(manzanilla): to be removed with tiled implementation. */
bool m_corners_ready;
public:
PlaneCornerPinMaskOperation();
+ void init_data() override;
void initExecution() override;
void deinitExecution() override;
@@ -43,6 +45,8 @@ class PlaneCornerPinMaskOperation : public PlaneDistortMaskOperation {
void determineResolution(unsigned int resolution[2],
unsigned int preferredResolution[2]) override;
+
+ void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override;
};
class PlaneCornerPinWarpImageOperation : public PlaneDistortWarpImageOperation {
@@ -52,6 +56,7 @@ class PlaneCornerPinWarpImageOperation : public PlaneDistortWarpImageOperation {
public:
PlaneCornerPinWarpImageOperation();
+ void init_data() override;
void initExecution() override;
void deinitExecution() override;
@@ -60,6 +65,8 @@ class PlaneCornerPinWarpImageOperation : public PlaneDistortWarpImageOperation {
bool determineDependingAreaOfInterest(rcti *input,
ReadBufferOperation *readOperation,
rcti *output) override;
+
+ void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override;
};
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_PlaneDistortCommonOperation.cc b/source/blender/compositor/operations/COM_PlaneDistortCommonOperation.cc
index 4edcc206f5b..a80cbbe942a 100644
--- a/source/blender/compositor/operations/COM_PlaneDistortCommonOperation.cc
+++ b/source/blender/compositor/operations/COM_PlaneDistortCommonOperation.cc
@@ -85,8 +85,9 @@ void PlaneDistortWarpImageOperation::calculateCorners(const float corners[4][2],
{
PlaneDistortBaseOperation::calculateCorners(corners, normalized, sample);
- const int width = this->m_pixelReader->getWidth();
- const int height = this->m_pixelReader->getHeight();
+ const NodeOperation *image = get_input_operation(0);
+ const int width = image->getWidth();
+ const int height = image->getHeight();
float frame_corners[4][2] = {
{0.0f, 0.0f}, {(float)width, 0.0f}, {(float)width, (float)height}, {0.0f, (float)height}};
MotionSample *sample_data = &this->m_samples[sample];
@@ -127,6 +128,34 @@ void PlaneDistortWarpImageOperation::executePixelSampled(float output[4],
}
}
+void PlaneDistortWarpImageOperation::update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs)
+{
+ const MemoryBuffer *input_img = inputs[0];
+ float uv[2];
+ float deriv[2][2];
+ BuffersIterator<float> it = output->iterate_with({}, area);
+ if (this->m_motion_blur_samples == 1) {
+ for (; !it.is_end(); ++it) {
+ warpCoord(it.x, it.y, this->m_samples[0].perspectiveMatrix, uv, deriv);
+ input_img->read_elem_filtered(uv[0], uv[1], deriv[0], deriv[1], it.out);
+ }
+ }
+ else {
+ for (; !it.is_end(); ++it) {
+ zero_v4(it.out);
+ for (const int sample : IndexRange(this->m_motion_blur_samples)) {
+ float color[4];
+ warpCoord(it.x, it.y, this->m_samples[sample].perspectiveMatrix, uv, deriv);
+ input_img->read_elem_filtered(uv[0], uv[1], deriv[0], deriv[1], color);
+ add_v4_v4(it.out, color);
+ }
+ mul_v4_fl(it.out, 1.0f / (float)this->m_motion_blur_samples);
+ }
+ }
+}
+
bool PlaneDistortWarpImageOperation::determineDependingAreaOfInterest(
rcti *input, ReadBufferOperation *readOperation, rcti *output)
{
@@ -157,6 +186,51 @@ bool PlaneDistortWarpImageOperation::determineDependingAreaOfInterest(
return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output);
}
+void PlaneDistortWarpImageOperation::get_area_of_interest(const int input_idx,
+ const rcti &output_area,
+ rcti &r_input_area)
+{
+ if (input_idx != 0) {
+ r_input_area = output_area;
+ return;
+ }
+
+ /* TODO: figure out the area needed for warping and EWA filtering. */
+ r_input_area.xmin = 0;
+ r_input_area.ymin = 0;
+ r_input_area.xmax = get_input_operation(0)->getWidth();
+ r_input_area.ymax = get_input_operation(0)->getHeight();
+
+/* Old implemention but resulting coordinates are way out of input operation bounds and in some
+ * cases the area result may incorrectly cause cropping. */
+#if 0
+ float min[2], max[2];
+ INIT_MINMAX2(min, max);
+ for (int sample = 0; sample < this->m_motion_blur_samples; sample++) {
+ float UVs[4][2];
+ float deriv[2][2];
+ MotionSample *sample_data = &this->m_samples[sample];
+ /* TODO(sergey): figure out proper way to do this. */
+ warpCoord(
+ output_area.xmin - 2, output_area.ymin - 2, sample_data->perspectiveMatrix, UVs[0], deriv);
+ warpCoord(
+ output_area.xmax + 2, output_area.ymin - 2, sample_data->perspectiveMatrix, UVs[1], deriv);
+ warpCoord(
+ output_area.xmax + 2, output_area.ymax + 2, sample_data->perspectiveMatrix, UVs[2], deriv);
+ warpCoord(
+ output_area.xmin - 2, output_area.ymax + 2, sample_data->perspectiveMatrix, UVs[3], deriv);
+ for (int i = 0; i < 4; i++) {
+ minmax_v2v2_v2(min, max, UVs[i]);
+ }
+ }
+
+ r_input_area.xmin = min[0] - 1;
+ r_input_area.ymin = min[1] - 1;
+ r_input_area.xmax = max[0] + 1;
+ r_input_area.ymax = max[1] + 1;
+#endif
+}
+
/* ******** PlaneDistort Mask ******** */
PlaneDistortMaskOperation::PlaneDistortMaskOperation() : PlaneDistortBaseOperation()
@@ -219,4 +293,41 @@ void PlaneDistortMaskOperation::executePixelSampled(float output[4],
}
}
+void PlaneDistortMaskOperation::update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> UNUSED(inputs))
+{
+ for (BuffersIterator<float> it = output->iterate_with({}, area); !it.is_end(); ++it) {
+ int inside_count = 0;
+ for (const int motion_sample : IndexRange(this->m_motion_blur_samples)) {
+ MotionSample &sample = this->m_samples[motion_sample];
+ inside_count += get_jitter_samples_inside_count(it.x, it.y, sample);
+ }
+ *it.out = (float)inside_count / (this->m_osa * this->m_motion_blur_samples);
+ }
+}
+
+int PlaneDistortMaskOperation::get_jitter_samples_inside_count(int x,
+ int y,
+ MotionSample &sample_data)
+{
+ float point[2];
+ int inside_count = 0;
+ for (int sample = 0; sample < this->m_osa; sample++) {
+ point[0] = x + this->m_jitter[sample][0];
+ point[1] = y + this->m_jitter[sample][1];
+ if (isect_point_tri_v2(point,
+ sample_data.frameSpaceCorners[0],
+ sample_data.frameSpaceCorners[1],
+ sample_data.frameSpaceCorners[2]) ||
+ isect_point_tri_v2(point,
+ sample_data.frameSpaceCorners[0],
+ sample_data.frameSpaceCorners[2],
+ sample_data.frameSpaceCorners[3])) {
+ inside_count++;
+ }
+ }
+ return inside_count;
+}
+
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_PlaneDistortCommonOperation.h b/source/blender/compositor/operations/COM_PlaneDistortCommonOperation.h
index cc6e4d00d71..3ef9c1dfab8 100644
--- a/source/blender/compositor/operations/COM_PlaneDistortCommonOperation.h
+++ b/source/blender/compositor/operations/COM_PlaneDistortCommonOperation.h
@@ -20,7 +20,7 @@
#include <string.h>
-#include "COM_NodeOperation.h"
+#include "COM_MultiThreadedOperation.h"
#include "DNA_movieclip_types.h"
#include "DNA_tracking_types.h"
@@ -32,7 +32,7 @@ namespace blender::compositor {
#define PLANE_DISTORT_MAX_SAMPLES 64
-class PlaneDistortBaseOperation : public NodeOperation {
+class PlaneDistortBaseOperation : public MultiThreadedOperation {
protected:
struct MotionSample {
float frameSpaceCorners[4][2]; /* Corners coordinates in pixel space. */
@@ -78,6 +78,11 @@ class PlaneDistortWarpImageOperation : public PlaneDistortBaseOperation {
bool determineDependingAreaOfInterest(rcti *input,
ReadBufferOperation *readOperation,
rcti *output) 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;
};
class PlaneDistortMaskOperation : public PlaneDistortBaseOperation {
@@ -91,6 +96,13 @@ class PlaneDistortMaskOperation : public PlaneDistortBaseOperation {
void initExecution() override;
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
+
+ void update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs) override;
+
+ private:
+ int get_jitter_samples_inside_count(int x, int y, MotionSample &sample_data);
};
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_PlaneTrackOperation.cc b/source/blender/compositor/operations/COM_PlaneTrackOperation.cc
index 0884f2ad979..bf24f843ca2 100644
--- a/source/blender/compositor/operations/COM_PlaneTrackOperation.cc
+++ b/source/blender/compositor/operations/COM_PlaneTrackOperation.cc
@@ -101,18 +101,40 @@ void PlaneTrackCommon::determineResolution(unsigned int resolution[2],
/* ******** PlaneTrackMaskOperation ******** */
+void PlaneTrackMaskOperation::init_data()
+{
+ PlaneDistortMaskOperation::init_data();
+ if (execution_model_ == eExecutionModel::FullFrame) {
+ PlaneTrackCommon::read_and_calculate_corners(this);
+ }
+}
+
+/* TODO(manzanilla): to be removed with tiled implementation. */
void PlaneTrackMaskOperation::initExecution()
{
PlaneDistortMaskOperation::initExecution();
- PlaneTrackCommon::read_and_calculate_corners(this);
+ if (execution_model_ == eExecutionModel::Tiled) {
+ PlaneTrackCommon::read_and_calculate_corners(this);
+ }
}
/* ******** PlaneTrackWarpImageOperation ******** */
+void PlaneTrackWarpImageOperation::init_data()
+{
+ PlaneDistortWarpImageOperation::init_data();
+ if (execution_model_ == eExecutionModel::FullFrame) {
+ PlaneTrackCommon::read_and_calculate_corners(this);
+ }
+}
+
+/* TODO(manzanilla): to be removed with tiled implementation. */
void PlaneTrackWarpImageOperation::initExecution()
{
PlaneDistortWarpImageOperation::initExecution();
- PlaneTrackCommon::read_and_calculate_corners(this);
+ if (execution_model_ == eExecutionModel::Tiled) {
+ PlaneTrackCommon::read_and_calculate_corners(this);
+ }
}
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_PlaneTrackOperation.h b/source/blender/compositor/operations/COM_PlaneTrackOperation.h
index 3bae230aa06..d2027755162 100644
--- a/source/blender/compositor/operations/COM_PlaneTrackOperation.h
+++ b/source/blender/compositor/operations/COM_PlaneTrackOperation.h
@@ -73,6 +73,8 @@ class PlaneTrackMaskOperation : public PlaneDistortMaskOperation, public PlaneTr
{
}
+ void init_data() override;
+
void initExecution() override;
void determineResolution(unsigned int resolution[2],
@@ -92,6 +94,8 @@ class PlaneTrackWarpImageOperation : public PlaneDistortWarpImageOperation,
{
}
+ void init_data() override;
+
void initExecution() override;
void determineResolution(unsigned int resolution[2],
diff --git a/source/blender/compositor/operations/COM_ProjectorLensDistortionOperation.cc b/source/blender/compositor/operations/COM_ProjectorLensDistortionOperation.cc
index 93702d3f0cf..fcab5dd5751 100644
--- a/source/blender/compositor/operations/COM_ProjectorLensDistortionOperation.cc
+++ b/source/blender/compositor/operations/COM_ProjectorLensDistortionOperation.cc
@@ -17,6 +17,8 @@
*/
#include "COM_ProjectorLensDistortionOperation.h"
+#include "COM_ConstantOperation.h"
+
#include "BLI_math.h"
#include "BLI_utildefines.h"
@@ -32,6 +34,20 @@ ProjectorLensDistortionOperation::ProjectorLensDistortionOperation()
this->m_dispersionAvailable = false;
this->m_dispersion = 0.0f;
}
+
+void ProjectorLensDistortionOperation::init_data()
+{
+ if (execution_model_ == eExecutionModel::FullFrame) {
+ NodeOperation *dispersion_input = get_input_operation(1);
+ if (dispersion_input->get_flags().is_constant_operation) {
+ this->m_dispersion =
+ static_cast<ConstantOperation *>(dispersion_input)->get_constant_elem()[0];
+ }
+ this->m_kr = 0.25f * max_ff(min_ff(this->m_dispersion, 1.0f), 0.0f);
+ this->m_kr2 = this->m_kr * 20;
+ }
+}
+
void ProjectorLensDistortionOperation::initExecution()
{
this->initMutex();
@@ -97,6 +113,7 @@ bool ProjectorLensDistortionOperation::determineDependingAreaOfInterest(
return false;
}
+/* TODO(manzanilla): to be removed with tiled implementation. */
void ProjectorLensDistortionOperation::updateDispersion()
{
if (this->m_dispersionAvailable) {
@@ -114,4 +131,41 @@ void ProjectorLensDistortionOperation::updateDispersion()
this->unlockMutex();
}
+void ProjectorLensDistortionOperation::get_area_of_interest(const int input_idx,
+ const rcti &output_area,
+ rcti &r_input_area)
+{
+ if (input_idx == 1) {
+ /* Dispersion input is used as constant only. */
+ r_input_area = COM_SINGLE_ELEM_AREA;
+ return;
+ }
+
+ r_input_area.ymax = output_area.ymax;
+ r_input_area.ymin = output_area.ymin;
+ r_input_area.xmin = output_area.xmin - this->m_kr2 - 2;
+ r_input_area.xmax = output_area.xmax + this->m_kr2 + 2;
+}
+
+void ProjectorLensDistortionOperation::update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs)
+{
+ const MemoryBuffer *input_image = inputs[0];
+ const float height = this->getHeight();
+ const float width = this->getWidth();
+ float color[4];
+ for (BuffersIterator<float> it = output->iterate_with({}, area); !it.is_end(); ++it) {
+ const float v = (it.y + 0.5f) / height;
+ const float u = (it.x + 0.5f) / width;
+ input_image->read_elem_bilinear((u * width + this->m_kr2) - 0.5f, v * height - 0.5f, color);
+ it.out[0] = color[0];
+ input_image->read_elem(it.x, it.y, color);
+ it.out[1] = color[1];
+ input_image->read_elem_bilinear((u * width - this->m_kr2) - 0.5f, v * height - 0.5f, color);
+ it.out[2] = color[2];
+ it.out[3] = 1.0f;
+ }
+}
+
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_ProjectorLensDistortionOperation.h b/source/blender/compositor/operations/COM_ProjectorLensDistortionOperation.h
index bce61d3de15..7c7626bf271 100644
--- a/source/blender/compositor/operations/COM_ProjectorLensDistortionOperation.h
+++ b/source/blender/compositor/operations/COM_ProjectorLensDistortionOperation.h
@@ -18,12 +18,12 @@
#pragma once
-#include "COM_NodeOperation.h"
+#include "COM_MultiThreadedOperation.h"
#include "DNA_node_types.h"
namespace blender::compositor {
-class ProjectorLensDistortionOperation : public NodeOperation {
+class ProjectorLensDistortionOperation : public MultiThreadedOperation {
private:
/**
* Cached reference to the inputProgram
@@ -31,6 +31,7 @@ class ProjectorLensDistortionOperation : public NodeOperation {
SocketReader *m_inputProgram;
float m_dispersion;
+ /* TODO(manzanilla): to be removed with tiled implementation. */
bool m_dispersionAvailable;
float m_kr, m_kr2;
@@ -43,6 +44,7 @@ class ProjectorLensDistortionOperation : public NodeOperation {
*/
void executePixel(float output[4], int x, int y, void *data) override;
+ void init_data() override;
/**
* Initialize the execution
*/
@@ -59,6 +61,11 @@ class ProjectorLensDistortionOperation : public NodeOperation {
rcti *output) override;
void updateDispersion();
+
+ 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_ScreenLensDistortionOperation.cc b/source/blender/compositor/operations/COM_ScreenLensDistortionOperation.cc
index 634fe66b0dd..f9ba2ef69ad 100644
--- a/source/blender/compositor/operations/COM_ScreenLensDistortionOperation.cc
+++ b/source/blender/compositor/operations/COM_ScreenLensDistortionOperation.cc
@@ -17,6 +17,7 @@
*/
#include "COM_ScreenLensDistortionOperation.h"
+#include "COM_ConstantOperation.h"
#include "BLI_math.h"
#include "BLI_rand.h"
@@ -53,6 +54,35 @@ void ScreenLensDistortionOperation::setDispersion(float dispersion)
m_dispersion_const = true;
}
+void ScreenLensDistortionOperation::init_data()
+{
+ this->m_cx = 0.5f * (float)getWidth();
+ this->m_cy = 0.5f * (float)getHeight();
+
+ switch (execution_model_) {
+ case eExecutionModel::FullFrame: {
+ NodeOperation *distortion_op = get_input_operation(1);
+ NodeOperation *dispersion_op = get_input_operation(2);
+ if (!m_distortion_const && distortion_op->get_flags().is_constant_operation) {
+ m_distortion = static_cast<ConstantOperation *>(distortion_op)->get_constant_elem()[0];
+ }
+ if (!m_dispersion_const && distortion_op->get_flags().is_constant_operation) {
+ m_dispersion = static_cast<ConstantOperation *>(dispersion_op)->get_constant_elem()[0];
+ }
+ updateVariables(m_distortion, m_dispersion);
+ break;
+ }
+ case eExecutionModel::Tiled: {
+ /* If both are constant, init variables once. */
+ if (m_distortion_const && m_dispersion_const) {
+ updateVariables(m_distortion, m_dispersion);
+ m_variables_ready = true;
+ }
+ break;
+ }
+ }
+}
+
void ScreenLensDistortionOperation::initExecution()
{
this->m_inputProgram = this->getInputSocketReader(0);
@@ -61,15 +91,6 @@ void ScreenLensDistortionOperation::initExecution()
uint rng_seed = (uint)(PIL_check_seconds_timer_i() & UINT_MAX);
rng_seed ^= (uint)POINTER_AS_INT(m_inputProgram);
this->m_rng = BLI_rng_new(rng_seed);
-
- this->m_cx = 0.5f * (float)getWidth();
- this->m_cy = 0.5f * (float)getHeight();
-
- /* if both are constant, init variables once */
- if (m_distortion_const && m_dispersion_const) {
- updateVariables(m_distortion, m_dispersion);
- m_variables_ready = true;
- }
}
void *ScreenLensDistortionOperation::initializeTileData(rcti * /*rect*/)
@@ -130,7 +151,7 @@ bool ScreenLensDistortionOperation::get_delta(float r_sq,
return false;
}
-void ScreenLensDistortionOperation::accumulate(MemoryBuffer *buffer,
+void ScreenLensDistortionOperation::accumulate(const MemoryBuffer *buffer,
int a,
int b,
float r_sq,
@@ -154,7 +175,14 @@ void ScreenLensDistortionOperation::accumulate(MemoryBuffer *buffer,
float xy[2];
distort_uv(uv, t, xy);
- buffer->readBilinear(color, xy[0], xy[1]);
+ switch (execution_model_) {
+ case eExecutionModel::Tiled:
+ buffer->readBilinear(color, xy[0], xy[1]);
+ break;
+ case eExecutionModel::FullFrame:
+ buffer->read_elem_bilinear(xy[0], xy[1], color);
+ break;
+ }
sum[a] += (1.0f - tz) * color[a];
sum[b] += (tz)*color[b];
@@ -354,4 +382,143 @@ void ScreenLensDistortionOperation::updateVariables(float distortion, float disp
mul_v3_v3fl(m_k4, m_k, 4.0f);
}
+void ScreenLensDistortionOperation::get_area_of_interest(const int input_idx,
+ const rcti &UNUSED(output_area),
+ rcti &r_input_area)
+{
+ if (input_idx != 0) {
+ /* Dispersion and distorsion inputs are used as constants only. */
+ r_input_area = COM_SINGLE_ELEM_AREA;
+ }
+
+ /* XXX the original method of estimating the area-of-interest does not work
+ * it assumes a linear increase/decrease of mapped coordinates, which does not
+ * yield correct results for the area and leaves uninitialized buffer areas.
+ * So now just use the full image area, which may not be as efficient but works at least ...
+ */
+#if 1
+ NodeOperation *image = getInputOperation(0);
+ r_input_area.xmax = image->getWidth();
+ r_input_area.xmin = 0;
+ r_input_area.ymax = image->getHeight();
+ r_input_area.ymin = 0;
+
+#else /* Original method in tiled implementation. */
+ rcti newInput;
+ const float margin = 2;
+
+ BLI_rcti_init_minmax(&newInput);
+
+ if (m_dispersion_const && m_distortion_const) {
+ /* update from fixed distortion/dispersion */
+# define UPDATE_INPUT(x, y) \
+ { \
+ float coords[6]; \
+ determineUV(coords, x, y); \
+ newInput.xmin = min_ffff(newInput.xmin, coords[0], coords[2], coords[4]); \
+ newInput.ymin = min_ffff(newInput.ymin, coords[1], coords[3], coords[5]); \
+ newInput.xmax = max_ffff(newInput.xmax, coords[0], coords[2], coords[4]); \
+ newInput.ymax = max_ffff(newInput.ymax, coords[1], coords[3], coords[5]); \
+ } \
+ (void)0
+
+ UPDATE_INPUT(input->xmin, input->xmax);
+ UPDATE_INPUT(input->xmin, input->ymax);
+ UPDATE_INPUT(input->xmax, input->ymax);
+ UPDATE_INPUT(input->xmax, input->ymin);
+
+# undef UPDATE_INPUT
+ }
+ else {
+ /* use maximum dispersion 1.0 if not const */
+ float dispersion = m_dispersion_const ? m_dispersion : 1.0f;
+
+# define UPDATE_INPUT(x, y, distortion) \
+ { \
+ float coords[6]; \
+ updateVariables(distortion, dispersion); \
+ determineUV(coords, x, y); \
+ newInput.xmin = min_ffff(newInput.xmin, coords[0], coords[2], coords[4]); \
+ newInput.ymin = min_ffff(newInput.ymin, coords[1], coords[3], coords[5]); \
+ newInput.xmax = max_ffff(newInput.xmax, coords[0], coords[2], coords[4]); \
+ newInput.ymax = max_ffff(newInput.ymax, coords[1], coords[3], coords[5]); \
+ } \
+ (void)0
+
+ if (m_distortion_const) {
+ /* update from fixed distortion */
+ UPDATE_INPUT(input->xmin, input->xmax, m_distortion);
+ UPDATE_INPUT(input->xmin, input->ymax, m_distortion);
+ UPDATE_INPUT(input->xmax, input->ymax, m_distortion);
+ UPDATE_INPUT(input->xmax, input->ymin, m_distortion);
+ }
+ else {
+ /* update from min/max distortion (-1..1) */
+ UPDATE_INPUT(input->xmin, input->xmax, -1.0f);
+ UPDATE_INPUT(input->xmin, input->ymax, -1.0f);
+ UPDATE_INPUT(input->xmax, input->ymax, -1.0f);
+ UPDATE_INPUT(input->xmax, input->ymin, -1.0f);
+
+ UPDATE_INPUT(input->xmin, input->xmax, 1.0f);
+ UPDATE_INPUT(input->xmin, input->ymax, 1.0f);
+ UPDATE_INPUT(input->xmax, input->ymax, 1.0f);
+ UPDATE_INPUT(input->xmax, input->ymin, 1.0f);
+
+# undef UPDATE_INPUT
+ }
+ }
+
+ newInput.xmin -= margin;
+ newInput.ymin -= margin;
+ newInput.xmax += margin;
+ newInput.ymax += margin;
+
+ operation = getInputOperation(0);
+ if (operation->determineDependingAreaOfInterest(&newInput, readOperation, output)) {
+ return true;
+ }
+ return false;
+#endif
+}
+
+void ScreenLensDistortionOperation::update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs)
+{
+ const MemoryBuffer *input_image = inputs[0];
+ for (BuffersIterator<float> it = output->iterate_with({}, area); !it.is_end(); ++it) {
+ float xy[2] = {(float)it.x, (float)it.y};
+ float uv[2];
+ get_uv(xy, uv);
+ const float uv_dot = len_squared_v2(uv);
+
+ float delta[3][2];
+ const bool valid_r = get_delta(uv_dot, m_k4[0], uv, delta[0]);
+ const bool valid_g = get_delta(uv_dot, m_k4[1], uv, delta[1]);
+ const bool valid_b = get_delta(uv_dot, m_k4[2], uv, delta[2]);
+ if (!(valid_r && valid_g && valid_b)) {
+ zero_v4(it.out);
+ continue;
+ }
+
+ int count[3] = {0, 0, 0};
+ float sum[4] = {0, 0, 0, 0};
+ accumulate(input_image, 0, 1, uv_dot, uv, delta, sum, count);
+ accumulate(input_image, 1, 2, uv_dot, uv, delta, sum, count);
+
+ if (count[0]) {
+ it.out[0] = 2.0f * sum[0] / (float)count[0];
+ }
+ if (count[1]) {
+ it.out[1] = 2.0f * sum[1] / (float)count[1];
+ }
+ if (count[2]) {
+ it.out[2] = 2.0f * sum[2] / (float)count[2];
+ }
+
+ /* Set alpha. */
+ it.out[3] = 1.0f;
+ }
+}
+
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_ScreenLensDistortionOperation.h b/source/blender/compositor/operations/COM_ScreenLensDistortionOperation.h
index 98872bfe142..616fc8883b0 100644
--- a/source/blender/compositor/operations/COM_ScreenLensDistortionOperation.h
+++ b/source/blender/compositor/operations/COM_ScreenLensDistortionOperation.h
@@ -18,14 +18,14 @@
#pragma once
-#include "COM_NodeOperation.h"
+#include "COM_MultiThreadedOperation.h"
#include "DNA_node_types.h"
struct RNG;
namespace blender::compositor {
-class ScreenLensDistortionOperation : public NodeOperation {
+class ScreenLensDistortionOperation : public MultiThreadedOperation {
private:
/**
* Cached reference to the inputProgram
@@ -50,6 +50,8 @@ class ScreenLensDistortionOperation : public NodeOperation {
public:
ScreenLensDistortionOperation();
+ void init_data() override;
+
/**
* The inner loop of this operation.
*/
@@ -84,6 +86,11 @@ class ScreenLensDistortionOperation : public NodeOperation {
ReadBufferOperation *readOperation,
rcti *output) 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:
void determineUV(float result[6], float x, float y) const;
void updateVariables(float distortion, float dispersion);
@@ -91,7 +98,7 @@ class ScreenLensDistortionOperation : public NodeOperation {
void get_uv(const float xy[2], float uv[2]) const;
void distort_uv(const float uv[2], float t, float xy[2]) const;
bool get_delta(float r_sq, float k4, const float uv[2], float delta[2]) const;
- void accumulate(MemoryBuffer *buffer,
+ void accumulate(const MemoryBuffer *buffer,
int a,
int b,
float r_sq,