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:46 +0300
committerManuel Castilla <manzanillawork@gmail.com>2021-08-23 18:09:59 +0300
commit153b45037f5d27cf125d56ebb9aa77aca53d0981 (patch)
treebcf590fc895b03efbfb090acd1fecfb1fa4f04a0
parentdaa7c59e38c8fe464004b3becd6956b880c38c92 (diff)
Compositor: Full frame matte nodes
Adds full frame implementation to Channel Key, Chroma Key, Color Key, Color Spill, Cryptomatte, Difference Key, Distance Key, Keying, Keying Screen and Luminance Key nodes. The other nodes in "Matte" sub-menu are submitted separately. No functional changes. Part of T88150. Reviewed By: jbakker Differential Revision: https://developer.blender.org/D12220
-rw-r--r--source/blender/compositor/operations/COM_ChannelMatteOperation.cc34
-rw-r--r--source/blender/compositor/operations/COM_ChannelMatteOperation.h8
-rw-r--r--source/blender/compositor/operations/COM_ChromaMatteOperation.cc55
-rw-r--r--source/blender/compositor/operations/COM_ChromaMatteOperation.h8
-rw-r--r--source/blender/compositor/operations/COM_ColorMatteOperation.cc37
-rw-r--r--source/blender/compositor/operations/COM_ColorMatteOperation.h8
-rw-r--r--source/blender/compositor/operations/COM_ColorSpillOperation.cc33
-rw-r--r--source/blender/compositor/operations/COM_ColorSpillOperation.h8
-rw-r--r--source/blender/compositor/operations/COM_CryptomatteOperation.cc30
-rw-r--r--source/blender/compositor/operations/COM_CryptomatteOperation.h8
-rw-r--r--source/blender/compositor/operations/COM_DifferenceMatteOperation.cc41
-rw-r--r--source/blender/compositor/operations/COM_DifferenceMatteOperation.h8
-rw-r--r--source/blender/compositor/operations/COM_DistanceRGBMatteOperation.cc42
-rw-r--r--source/blender/compositor/operations/COM_DistanceRGBMatteOperation.h10
-rw-r--r--source/blender/compositor/operations/COM_DistanceYCCMatteOperation.cc2
-rw-r--r--source/blender/compositor/operations/COM_DistanceYCCMatteOperation.h2
-rw-r--r--source/blender/compositor/operations/COM_KeyingBlurOperation.cc63
-rw-r--r--source/blender/compositor/operations/COM_KeyingBlurOperation.h11
-rw-r--r--source/blender/compositor/operations/COM_KeyingClipOperation.cc85
-rw-r--r--source/blender/compositor/operations/COM_KeyingClipOperation.h11
-rw-r--r--source/blender/compositor/operations/COM_KeyingDespillOperation.cc29
-rw-r--r--source/blender/compositor/operations/COM_KeyingDespillOperation.h8
-rw-r--r--source/blender/compositor/operations/COM_KeyingOperation.cc45
-rw-r--r--source/blender/compositor/operations/COM_KeyingOperation.h8
-rw-r--r--source/blender/compositor/operations/COM_KeyingScreenOperation.cc95
-rw-r--r--source/blender/compositor/operations/COM_KeyingScreenOperation.h12
-rw-r--r--source/blender/compositor/operations/COM_LuminanceMatteOperation.cc36
-rw-r--r--source/blender/compositor/operations/COM_LuminanceMatteOperation.h8
28 files changed, 701 insertions, 44 deletions
diff --git a/source/blender/compositor/operations/COM_ChannelMatteOperation.cc b/source/blender/compositor/operations/COM_ChannelMatteOperation.cc
index ec4331dc231..65742d0cfcc 100644
--- a/source/blender/compositor/operations/COM_ChannelMatteOperation.cc
+++ b/source/blender/compositor/operations/COM_ChannelMatteOperation.cc
@@ -27,6 +27,7 @@ ChannelMatteOperation::ChannelMatteOperation()
addOutputSocket(DataType::Value);
this->m_inputImageProgram = nullptr;
+ flags.can_be_constant = true;
}
void ChannelMatteOperation::initExecution()
@@ -121,4 +122,37 @@ void ChannelMatteOperation::executePixelSampled(float output[4],
output[0] = MIN2(alpha, inColor[3]);
}
+void ChannelMatteOperation::update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs)
+{
+ for (BuffersIterator<float> it = output->iterate_with(inputs, area); !it.is_end(); ++it) {
+ const float *color = it.in(0);
+
+ /* Matte operation. */
+ float alpha = color[this->m_ids[0]] - MAX2(color[this->m_ids[1]], color[this->m_ids[2]]);
+
+ /* Flip because 0.0 is transparent, not 1.0. */
+ alpha = 1.0f - alpha;
+
+ /* Test range. */
+ if (alpha > m_limit_max) {
+ alpha = color[3]; /* Whatever it was prior. */
+ }
+ else if (alpha < m_limit_min) {
+ alpha = 0.0f;
+ }
+ else { /* Blend. */
+ alpha = (alpha - m_limit_min) / m_limit_range;
+ }
+
+ /* Store matte(alpha) value in [0] to go with
+ * COM_SetAlphaMultiplyOperation and the Value output.
+ */
+
+ /* Don't make something that was more transparent less transparent. */
+ *it.out = MIN2(alpha, color[3]);
+ }
+}
+
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_ChannelMatteOperation.h b/source/blender/compositor/operations/COM_ChannelMatteOperation.h
index 6e9dcccd36e..ba50105dd3b 100644
--- a/source/blender/compositor/operations/COM_ChannelMatteOperation.h
+++ b/source/blender/compositor/operations/COM_ChannelMatteOperation.h
@@ -18,7 +18,7 @@
#pragma once
-#include "COM_MixOperation.h"
+#include "COM_MultiThreadedOperation.h"
namespace blender::compositor {
@@ -26,7 +26,7 @@ namespace blender::compositor {
* this program converts an input color to an output value.
* it assumes we are in sRGB color space.
*/
-class ChannelMatteOperation : public NodeOperation {
+class ChannelMatteOperation : public MultiThreadedOperation {
private:
SocketReader *m_inputImageProgram;
@@ -71,6 +71,10 @@ class ChannelMatteOperation : public NodeOperation {
this->m_limit_channel = nodeChroma->channel;
this->m_matte_channel = custom2;
}
+
+ 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_ChromaMatteOperation.cc b/source/blender/compositor/operations/COM_ChromaMatteOperation.cc
index b7fec5f07e5..0784f266b19 100644
--- a/source/blender/compositor/operations/COM_ChromaMatteOperation.cc
+++ b/source/blender/compositor/operations/COM_ChromaMatteOperation.cc
@@ -29,6 +29,7 @@ ChromaMatteOperation::ChromaMatteOperation()
this->m_inputImageProgram = nullptr;
this->m_inputKeyProgram = nullptr;
+ flags.can_be_constant = true;
}
void ChromaMatteOperation::initExecution()
@@ -110,4 +111,58 @@ void ChromaMatteOperation::executePixelSampled(float output[4],
}
}
+void ChromaMatteOperation::update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs)
+{
+ const float acceptance = this->m_settings->t1; /* In radians. */
+ const float cutoff = this->m_settings->t2; /* In radians. */
+ const float gain = this->m_settings->fstrength;
+ for (BuffersIterator<float> it = output->iterate_with(inputs, area); !it.is_end(); ++it) {
+ const float *in_image = it.in(0);
+ const float *in_key = it.in(1);
+
+ /* Store matte(alpha) value in [0] to go with
+ * #COM_SetAlphaMultiplyOperation and the Value output. */
+
+ /* Algorithm from book "Video Demystified", does not include the spill reduction part. */
+ /* Find theta, the angle that the color space should be rotated based on key. */
+
+ /* Rescale to `-1.0..1.0`. */
+ // const float image_Y = (in_image[0] * 2.0f) - 1.0f; // UNUSED
+ const float image_cb = (in_image[1] * 2.0f) - 1.0f;
+ const float image_cr = (in_image[2] * 2.0f) - 1.0f;
+
+ // const float key_Y = (in_key[0] * 2.0f) - 1.0f; // UNUSED
+ const float key_cb = (in_key[1] * 2.0f) - 1.0f;
+ const float key_cr = (in_key[2] * 2.0f) - 1.0f;
+
+ const float theta = atan2(key_cr, key_cb);
+
+ /* Rotate the cb and cr into x/z space. */
+ const float x_angle = image_cb * cosf(theta) + image_cr * sinf(theta);
+ const float z_angle = image_cr * cosf(theta) - image_cb * sinf(theta);
+
+ /* If within the acceptance angle. */
+ /* If kfg is <0 then the pixel is outside of the key color. */
+ const float kfg = x_angle - (fabsf(z_angle) / tanf(acceptance / 2.0f));
+
+ if (kfg > 0.0f) { /* Found a pixel that is within key color. */
+ const float beta = atan2(z_angle, x_angle);
+ float alpha = 1.0f - (kfg / gain);
+
+ /* Ff beta is within the cutoff angle. */
+ if (fabsf(beta) < (cutoff / 2.0f)) {
+ alpha = 0.0f;
+ }
+
+ /* Don't make something that was more transparent less transparent. */
+ it.out[0] = alpha < in_image[3] ? alpha : in_image[3];
+ }
+ else { /* Pixel is outside key color. */
+ it.out[0] = in_image[3]; /* Make pixel just as transparent as it was before. */
+ }
+ }
+}
+
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_ChromaMatteOperation.h b/source/blender/compositor/operations/COM_ChromaMatteOperation.h
index 48c3a785011..065349910a7 100644
--- a/source/blender/compositor/operations/COM_ChromaMatteOperation.h
+++ b/source/blender/compositor/operations/COM_ChromaMatteOperation.h
@@ -18,7 +18,7 @@
#pragma once
-#include "COM_MixOperation.h"
+#include "COM_MultiThreadedOperation.h"
namespace blender::compositor {
@@ -26,7 +26,7 @@ namespace blender::compositor {
* this program converts an input color to an output value.
* it assumes we are in sRGB color space.
*/
-class ChromaMatteOperation : public NodeOperation {
+class ChromaMatteOperation : public MultiThreadedOperation {
private:
NodeChroma *m_settings;
SocketReader *m_inputImageProgram;
@@ -50,6 +50,10 @@ class ChromaMatteOperation : public NodeOperation {
{
this->m_settings = nodeChroma;
}
+
+ 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_ColorMatteOperation.cc b/source/blender/compositor/operations/COM_ColorMatteOperation.cc
index ddfbf415d9c..dec6571f217 100644
--- a/source/blender/compositor/operations/COM_ColorMatteOperation.cc
+++ b/source/blender/compositor/operations/COM_ColorMatteOperation.cc
@@ -29,6 +29,7 @@ ColorMatteOperation::ColorMatteOperation()
this->m_inputImageProgram = nullptr;
this->m_inputKeyProgram = nullptr;
+ flags.can_be_constant = true;
}
void ColorMatteOperation::initExecution()
@@ -82,4 +83,40 @@ void ColorMatteOperation::executePixelSampled(float output[4],
}
}
+void ColorMatteOperation::update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs)
+{
+ const float hue = m_settings->t1;
+ const float sat = m_settings->t2;
+ const float val = m_settings->t3;
+ for (BuffersIterator<float> it = output->iterate_with(inputs, area); !it.is_end(); ++it) {
+ const float *in_color = it.in(0);
+ const float *in_key = it.in(1);
+
+ /* Store matte(alpha) value in [0] to go with
+ * COM_SetAlphaMultiplyOperation and the Value output.
+ */
+
+ float h_wrap;
+ if (
+ /* Do hue last because it needs to wrap, and does some more checks. */
+
+ /* #sat */ (fabsf(in_color[1] - in_key[1]) < sat) &&
+ /* #val */ (fabsf(in_color[2] - in_key[2]) < val) &&
+
+ /* Multiply by 2 because it wraps on both sides of the hue,
+ * otherwise 0.5 would key all hue's. */
+
+ /* #hue */
+ ((h_wrap = 2.0f * fabsf(in_color[0] - in_key[0])) < hue || (2.0f - h_wrap) < hue)) {
+ it.out[0] = 0.0f; /* Make transparent. */
+ }
+
+ else { /* Pixel is outside key color. */
+ it.out[0] = in_color[3]; /* Make pixel just as transparent as it was before. */
+ }
+ }
+}
+
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_ColorMatteOperation.h b/source/blender/compositor/operations/COM_ColorMatteOperation.h
index 439a3b0741d..49d06e62e65 100644
--- a/source/blender/compositor/operations/COM_ColorMatteOperation.h
+++ b/source/blender/compositor/operations/COM_ColorMatteOperation.h
@@ -18,7 +18,7 @@
#pragma once
-#include "COM_MixOperation.h"
+#include "COM_MultiThreadedOperation.h"
namespace blender::compositor {
@@ -26,7 +26,7 @@ namespace blender::compositor {
* this program converts an input color to an output value.
* it assumes we are in sRGB color space.
*/
-class ColorMatteOperation : public NodeOperation {
+class ColorMatteOperation : public MultiThreadedOperation {
private:
NodeChroma *m_settings;
SocketReader *m_inputImageProgram;
@@ -50,6 +50,10 @@ class ColorMatteOperation : public NodeOperation {
{
this->m_settings = nodeChroma;
}
+
+ 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_ColorSpillOperation.cc b/source/blender/compositor/operations/COM_ColorSpillOperation.cc
index 7dc7e2775fc..4b0e0520b75 100644
--- a/source/blender/compositor/operations/COM_ColorSpillOperation.cc
+++ b/source/blender/compositor/operations/COM_ColorSpillOperation.cc
@@ -32,6 +32,7 @@ ColorSpillOperation::ColorSpillOperation()
this->m_inputFacReader = nullptr;
this->m_spillChannel = 1; // GREEN
this->m_spillMethod = 0;
+ flags.can_be_constant = true;
}
void ColorSpillOperation::initExecution()
@@ -118,4 +119,36 @@ void ColorSpillOperation::executePixelSampled(float output[4],
}
}
+void ColorSpillOperation::update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs)
+{
+ for (BuffersIterator<float> it = output->iterate_with(inputs, area); !it.is_end(); ++it) {
+ const float *color = it.in(0);
+ const float factor = MIN2(1.0f, *it.in(1));
+
+ float map;
+ switch (m_spillMethod) {
+ case 0: /* simple */
+ map = factor *
+ (color[m_spillChannel] - (m_settings->limscale * color[m_settings->limchan]));
+ break;
+ default: /* average */
+ map = factor * (color[m_spillChannel] -
+ (m_settings->limscale * AVG(color[m_channel2], color[m_channel3])));
+ break;
+ }
+
+ if (map > 0.0f) {
+ it.out[0] = color[0] + m_rmut * (m_settings->uspillr * map);
+ it.out[1] = color[1] + m_gmut * (m_settings->uspillg * map);
+ it.out[2] = color[2] + m_bmut * (m_settings->uspillb * map);
+ it.out[3] = color[3];
+ }
+ else {
+ copy_v4_v4(it.out, color);
+ }
+ }
+}
+
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_ColorSpillOperation.h b/source/blender/compositor/operations/COM_ColorSpillOperation.h
index 9b82e720527..6a5e688c160 100644
--- a/source/blender/compositor/operations/COM_ColorSpillOperation.h
+++ b/source/blender/compositor/operations/COM_ColorSpillOperation.h
@@ -18,7 +18,7 @@
#pragma once
-#include "COM_NodeOperation.h"
+#include "COM_MultiThreadedOperation.h"
namespace blender::compositor {
@@ -26,7 +26,7 @@ namespace blender::compositor {
* this program converts an input color to an output value.
* it assumes we are in sRGB color space.
*/
-class ColorSpillOperation : public NodeOperation {
+class ColorSpillOperation : public MultiThreadedOperation {
protected:
NodeColorspill *m_settings;
SocketReader *m_inputImageReader;
@@ -65,6 +65,10 @@ class ColorSpillOperation : public NodeOperation {
}
float calculateMapValue(float fac, float *input);
+
+ 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_CryptomatteOperation.cc b/source/blender/compositor/operations/COM_CryptomatteOperation.cc
index 1a86fadad76..02e7c5607d8 100644
--- a/source/blender/compositor/operations/COM_CryptomatteOperation.cc
+++ b/source/blender/compositor/operations/COM_CryptomatteOperation.cc
@@ -71,4 +71,34 @@ void CryptomatteOperation::executePixel(float output[4], int x, int y, void *dat
}
}
+void CryptomatteOperation::update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs)
+{
+ for (BuffersIterator<float> it = output->iterate_with(inputs, area); !it.is_end(); ++it) {
+ zero_v4(it.out);
+ for (int i = 0; i < it.get_num_inputs(); i++) {
+ const float *input = it.in(i);
+ if (i == 0) {
+ /* Write the front-most object as false color for picking. */
+ it.out[0] = input[0];
+ uint32_t m3hash;
+ ::memcpy(&m3hash, &input[0], sizeof(uint32_t));
+ /* Since the red channel is likely to be out of display range,
+ * setting green and blue gives more meaningful images. */
+ it.out[1] = ((float)(m3hash << 8) / (float)UINT32_MAX);
+ it.out[2] = ((float)(m3hash << 16) / (float)UINT32_MAX);
+ }
+ for (const float hash : m_objectIndex) {
+ if (input[0] == hash) {
+ it.out[3] += input[1];
+ }
+ if (input[2] == hash) {
+ it.out[3] += input[3];
+ }
+ }
+ }
+ }
+}
+
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_CryptomatteOperation.h b/source/blender/compositor/operations/COM_CryptomatteOperation.h
index 1b91358d228..f1bf4cdf624 100644
--- a/source/blender/compositor/operations/COM_CryptomatteOperation.h
+++ b/source/blender/compositor/operations/COM_CryptomatteOperation.h
@@ -18,11 +18,11 @@
#pragma once
-#include "COM_NodeOperation.h"
+#include "COM_MultiThreadedOperation.h"
namespace blender::compositor {
-class CryptomatteOperation : public NodeOperation {
+class CryptomatteOperation : public MultiThreadedOperation {
private:
Vector<float> m_objectIndex;
@@ -35,6 +35,10 @@ class CryptomatteOperation : public NodeOperation {
void executePixel(float output[4], int x, int y, void *data) override;
void addObjectIndex(float objectIndex);
+
+ 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_DifferenceMatteOperation.cc b/source/blender/compositor/operations/COM_DifferenceMatteOperation.cc
index 0acdfc1651f..31714b03b06 100644
--- a/source/blender/compositor/operations/COM_DifferenceMatteOperation.cc
+++ b/source/blender/compositor/operations/COM_DifferenceMatteOperation.cc
@@ -29,6 +29,7 @@ DifferenceMatteOperation::DifferenceMatteOperation()
this->m_inputImage1Program = nullptr;
this->m_inputImage2Program = nullptr;
+ flags.can_be_constant = true;
}
void DifferenceMatteOperation::initExecution()
@@ -86,4 +87,44 @@ void DifferenceMatteOperation::executePixelSampled(float output[4],
}
}
+void DifferenceMatteOperation::update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs)
+{
+ for (BuffersIterator<float> it = output->iterate_with(inputs, area); !it.is_end(); ++it) {
+ const float *color1 = it.in(0);
+ const float *color2 = it.in(1);
+
+ float difference = (fabsf(color2[0] - color1[0]) + fabsf(color2[1] - color1[1]) +
+ fabsf(color2[2] - color1[2]));
+
+ /* Average together the distances. */
+ difference = difference / 3.0f;
+
+ const float tolerance = m_settings->t1;
+ const float falloff = m_settings->t2;
+
+ /* Make 100% transparent. */
+ if (difference <= tolerance) {
+ it.out[0] = 0.0f;
+ }
+ /* In the falloff region, make partially transparent. */
+ else if (difference <= falloff + tolerance) {
+ difference = difference - tolerance;
+ const float alpha = difference / falloff;
+ /* Only change if more transparent than before. */
+ if (alpha < color1[3]) {
+ it.out[0] = alpha;
+ }
+ else { /* Leave as before. */
+ it.out[0] = color1[3];
+ }
+ }
+ else {
+ /* Foreground object. */
+ it.out[0] = color1[3];
+ }
+ }
+}
+
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_DifferenceMatteOperation.h b/source/blender/compositor/operations/COM_DifferenceMatteOperation.h
index d3963fee1c1..0a86535d946 100644
--- a/source/blender/compositor/operations/COM_DifferenceMatteOperation.h
+++ b/source/blender/compositor/operations/COM_DifferenceMatteOperation.h
@@ -18,7 +18,7 @@
#pragma once
-#include "COM_MixOperation.h"
+#include "COM_MultiThreadedOperation.h"
namespace blender::compositor {
@@ -26,7 +26,7 @@ namespace blender::compositor {
* this program converts an input color to an output value.
* it assumes we are in sRGB color space.
*/
-class DifferenceMatteOperation : public NodeOperation {
+class DifferenceMatteOperation : public MultiThreadedOperation {
private:
NodeChroma *m_settings;
SocketReader *m_inputImage1Program;
@@ -50,6 +50,10 @@ class DifferenceMatteOperation : public NodeOperation {
{
this->m_settings = nodeChroma;
}
+
+ 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_DistanceRGBMatteOperation.cc b/source/blender/compositor/operations/COM_DistanceRGBMatteOperation.cc
index 1b3403cbb29..8155ff769a0 100644
--- a/source/blender/compositor/operations/COM_DistanceRGBMatteOperation.cc
+++ b/source/blender/compositor/operations/COM_DistanceRGBMatteOperation.cc
@@ -29,6 +29,7 @@ DistanceRGBMatteOperation::DistanceRGBMatteOperation()
this->m_inputImageProgram = nullptr;
this->m_inputKeyProgram = nullptr;
+ flags.can_be_constant = true;
}
void DistanceRGBMatteOperation::initExecution()
@@ -43,7 +44,7 @@ void DistanceRGBMatteOperation::deinitExecution()
this->m_inputKeyProgram = nullptr;
}
-float DistanceRGBMatteOperation::calculateDistance(float key[4], float image[4])
+float DistanceRGBMatteOperation::calculateDistance(const float key[4], const float image[4])
{
return len_v3v3(key, image);
}
@@ -93,4 +94,43 @@ void DistanceRGBMatteOperation::executePixelSampled(float output[4],
}
}
+void DistanceRGBMatteOperation::update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs)
+{
+ for (BuffersIterator<float> it = output->iterate_with(inputs, area); !it.is_end(); ++it) {
+ const float *in_image = it.in(0);
+ const float *in_key = it.in(1);
+
+ float distance = this->calculateDistance(in_key, in_image);
+ const float tolerance = this->m_settings->t1;
+ const float falloff = this->m_settings->t2;
+
+ /* Store matte(alpha) value in [0] to go with
+ * COM_SetAlphaMultiplyOperation and the Value output.
+ */
+
+ /* Make 100% transparent. */
+ if (distance < tolerance) {
+ it.out[0] = 0.0f;
+ }
+ /* In the falloff region, make partially transparent. */
+ else if (distance < falloff + tolerance) {
+ distance = distance - tolerance;
+ const float alpha = distance / falloff;
+ /* Only change if more transparent than before. */
+ if (alpha < in_image[3]) {
+ it.out[0] = alpha;
+ }
+ else { /* Leave as before. */
+ it.out[0] = in_image[3];
+ }
+ }
+ else {
+ /* Leave as before. */
+ it.out[0] = in_image[3];
+ }
+ }
+}
+
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_DistanceRGBMatteOperation.h b/source/blender/compositor/operations/COM_DistanceRGBMatteOperation.h
index 6fe603233b7..ba6682214ae 100644
--- a/source/blender/compositor/operations/COM_DistanceRGBMatteOperation.h
+++ b/source/blender/compositor/operations/COM_DistanceRGBMatteOperation.h
@@ -18,7 +18,7 @@
#pragma once
-#include "COM_MixOperation.h"
+#include "COM_MultiThreadedOperation.h"
namespace blender::compositor {
@@ -26,13 +26,13 @@ namespace blender::compositor {
* this program converts an input color to an output value.
* it assumes we are in sRGB color space.
*/
-class DistanceRGBMatteOperation : public NodeOperation {
+class DistanceRGBMatteOperation : public MultiThreadedOperation {
protected:
NodeChroma *m_settings;
SocketReader *m_inputImageProgram;
SocketReader *m_inputKeyProgram;
- virtual float calculateDistance(float key[4], float image[4]);
+ virtual float calculateDistance(const float key[4], const float image[4]);
public:
/**
@@ -52,6 +52,10 @@ class DistanceRGBMatteOperation : public NodeOperation {
{
this->m_settings = nodeChroma;
}
+
+ 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_DistanceYCCMatteOperation.cc b/source/blender/compositor/operations/COM_DistanceYCCMatteOperation.cc
index 597545dd706..50e473ea5b3 100644
--- a/source/blender/compositor/operations/COM_DistanceYCCMatteOperation.cc
+++ b/source/blender/compositor/operations/COM_DistanceYCCMatteOperation.cc
@@ -21,7 +21,7 @@
namespace blender::compositor {
-float DistanceYCCMatteOperation::calculateDistance(float key[4], float image[4])
+float DistanceYCCMatteOperation::calculateDistance(const float key[4], const float image[4])
{
/* only measure the second 2 values */
return len_v2v2(key + 1, image + 1);
diff --git a/source/blender/compositor/operations/COM_DistanceYCCMatteOperation.h b/source/blender/compositor/operations/COM_DistanceYCCMatteOperation.h
index a87e885e5d8..0e178fddc39 100644
--- a/source/blender/compositor/operations/COM_DistanceYCCMatteOperation.h
+++ b/source/blender/compositor/operations/COM_DistanceYCCMatteOperation.h
@@ -29,7 +29,7 @@ namespace blender::compositor {
*/
class DistanceYCCMatteOperation : public DistanceRGBMatteOperation {
protected:
- float calculateDistance(float key[4], float image[4]) override;
+ float calculateDistance(const float key[4], const float image[4]) override;
};
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_KeyingBlurOperation.cc b/source/blender/compositor/operations/COM_KeyingBlurOperation.cc
index 994b00cd3f4..d5ebd5e9df7 100644
--- a/source/blender/compositor/operations/COM_KeyingBlurOperation.cc
+++ b/source/blender/compositor/operations/COM_KeyingBlurOperation.cc
@@ -96,4 +96,67 @@ bool KeyingBlurOperation::determineDependingAreaOfInterest(rcti *input,
return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output);
}
+void KeyingBlurOperation::get_area_of_interest(const int UNUSED(input_idx),
+ const rcti &output_area,
+ rcti &r_input_area)
+{
+ switch (m_axis) {
+ case BLUR_AXIS_X:
+ r_input_area.xmin = output_area.xmin - m_size;
+ r_input_area.ymin = output_area.ymin;
+ r_input_area.xmax = output_area.xmax + m_size;
+ r_input_area.ymax = output_area.ymax;
+ break;
+ case BLUR_AXIS_Y:
+ r_input_area.xmin = output_area.xmin;
+ r_input_area.ymin = output_area.ymin - m_size;
+ r_input_area.xmax = output_area.xmax;
+ r_input_area.ymax = output_area.ymax + m_size;
+ break;
+ default:
+ BLI_assert_msg(0, "Unknown axis");
+ break;
+ }
+}
+
+void KeyingBlurOperation::update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs)
+{
+ const MemoryBuffer *input = inputs[0];
+ BuffersIterator<float> it = output->iterate_with(inputs, area);
+
+ int coord_max;
+ int elem_stride;
+ std::function<int()> get_current_coord;
+ switch (m_axis) {
+ case BLUR_AXIS_X:
+ get_current_coord = [&] { return it.x; };
+ coord_max = this->getWidth();
+ elem_stride = input->elem_stride;
+ break;
+ case BLUR_AXIS_Y:
+ get_current_coord = [&] { return it.y; };
+ coord_max = this->getHeight();
+ elem_stride = input->row_stride;
+ break;
+ }
+
+ for (; !it.is_end(); ++it) {
+ const int coord = get_current_coord();
+ const int start_coord = MAX2(0, coord - m_size + 1);
+ const int end_coord = MIN2(coord_max, coord + m_size);
+ const int count = end_coord - start_coord;
+
+ float sum = 0.0f;
+ const float *start = it.in(0) + (start_coord - coord) * elem_stride;
+ const float *end = start + count * elem_stride;
+ for (const float *elem = start; elem < end; elem += elem_stride) {
+ sum += *elem;
+ }
+
+ *it.out = sum / count;
+ }
+}
+
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_KeyingBlurOperation.h b/source/blender/compositor/operations/COM_KeyingBlurOperation.h
index b055d7713f1..b290b905e63 100644
--- a/source/blender/compositor/operations/COM_KeyingBlurOperation.h
+++ b/source/blender/compositor/operations/COM_KeyingBlurOperation.h
@@ -18,14 +18,14 @@
#pragma once
-#include "COM_NodeOperation.h"
+#include "COM_MultiThreadedOperation.h"
namespace blender::compositor {
/**
* Class with implementation of blurring for keying node
*/
-class KeyingBlurOperation : public NodeOperation {
+class KeyingBlurOperation : public MultiThreadedOperation {
protected:
int m_size;
int m_axis;
@@ -54,6 +54,13 @@ class KeyingBlurOperation : public NodeOperation {
bool determineDependingAreaOfInterest(rcti *input,
ReadBufferOperation *readOperation,
rcti *output) override;
+
+ void get_area_of_interest(const 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_KeyingClipOperation.cc b/source/blender/compositor/operations/COM_KeyingClipOperation.cc
index 4029be4e077..817c920ed91 100644
--- a/source/blender/compositor/operations/COM_KeyingClipOperation.cc
+++ b/source/blender/compositor/operations/COM_KeyingClipOperation.cc
@@ -130,4 +130,89 @@ bool KeyingClipOperation::determineDependingAreaOfInterest(rcti *input,
return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output);
}
+void KeyingClipOperation::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_kernelRadius;
+ r_input_area.xmax = output_area.xmax + m_kernelRadius;
+ r_input_area.ymin = output_area.ymin - m_kernelRadius;
+ r_input_area.ymax = output_area.ymax + m_kernelRadius;
+}
+
+void KeyingClipOperation::update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs)
+{
+ const MemoryBuffer *input = inputs[0];
+ BuffersIterator<float> it = output->iterate_with(inputs, area);
+
+ const int delta = m_kernelRadius;
+ const float tolerance = m_kernelTolerance;
+ const int width = this->getWidth();
+ const int height = this->getHeight();
+ const int row_stride = input->row_stride;
+ const int elem_stride = input->elem_stride;
+ for (; !it.is_end(); ++it) {
+ const int x = it.x;
+ const int y = it.y;
+
+ const int start_x = MAX2(0, x - delta + 1);
+ const int start_y = MAX2(0, y - delta + 1);
+ const int end_x = MIN2(x + delta, width);
+ const int end_y = MIN2(y + delta, height);
+ const int x_len = end_x - start_x;
+ const int y_len = end_y - start_y;
+
+ const int total_count = x_len * y_len - 1;
+ const int threshold_count = ceil((float)total_count * 0.9f);
+ bool ok = false;
+ if (delta == 0) {
+ ok = true;
+ }
+
+ const float *main_elem = it.in(0);
+ const float value = *main_elem;
+ const float *row = input->get_elem(start_x, start_y);
+ const float *end_row = row + y_len * row_stride;
+ int count = 0;
+ for (; ok == false && row < end_row; row += row_stride) {
+ const float *end_elem = row + x_len * elem_stride;
+ for (const float *elem = row; ok == false && elem < end_elem; elem += elem_stride) {
+ if (UNLIKELY(elem == main_elem)) {
+ continue;
+ }
+
+ const float current_value = *elem;
+ if (fabsf(current_value - value) < tolerance) {
+ count++;
+ if (count >= threshold_count) {
+ ok = true;
+ }
+ }
+ }
+ }
+
+ if (m_isEdgeMatte) {
+ *it.out = ok ? 0.0f : 1.0f;
+ }
+ else {
+ if (!ok) {
+ *it.out = value;
+ }
+ else if (value < m_clipBlack) {
+ *it.out = 0.0f;
+ }
+ else if (value >= m_clipWhite) {
+ *it.out = 1.0f;
+ }
+ else {
+ *it.out = (value - m_clipBlack) / (m_clipWhite - m_clipBlack);
+ }
+ }
+ }
+}
+
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_KeyingClipOperation.h b/source/blender/compositor/operations/COM_KeyingClipOperation.h
index 0a21fb48c99..1a17d591781 100644
--- a/source/blender/compositor/operations/COM_KeyingClipOperation.h
+++ b/source/blender/compositor/operations/COM_KeyingClipOperation.h
@@ -18,14 +18,14 @@
#pragma once
-#include "COM_NodeOperation.h"
+#include "COM_MultiThreadedOperation.h"
namespace blender::compositor {
/**
* Class with implementation of black/white clipping for keying node
*/
-class KeyingClipOperation : public NodeOperation {
+class KeyingClipOperation : public MultiThreadedOperation {
protected:
float m_clipBlack;
float m_clipWhite;
@@ -68,6 +68,13 @@ class KeyingClipOperation : public NodeOperation {
bool determineDependingAreaOfInterest(rcti *input,
ReadBufferOperation *readOperation,
rcti *output) override;
+
+ void get_area_of_interest(const 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_KeyingDespillOperation.cc b/source/blender/compositor/operations/COM_KeyingDespillOperation.cc
index d31a88cb91e..620b767e584 100644
--- a/source/blender/compositor/operations/COM_KeyingDespillOperation.cc
+++ b/source/blender/compositor/operations/COM_KeyingDespillOperation.cc
@@ -36,6 +36,7 @@ KeyingDespillOperation::KeyingDespillOperation()
this->m_pixelReader = nullptr;
this->m_screenReader = nullptr;
+ flags.can_be_constant = true;
}
void KeyingDespillOperation::initExecution()
@@ -82,4 +83,32 @@ void KeyingDespillOperation::executePixelSampled(float output[4],
}
}
+void KeyingDespillOperation::update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs)
+{
+ for (BuffersIterator<float> it = output->iterate_with(inputs, area); !it.is_end(); ++it) {
+ const float *pixel_color = it.in(0);
+ const float *screen_color = it.in(1);
+
+ const int screen_primary_channel = max_axis_v3(screen_color);
+ const int other_1 = (screen_primary_channel + 1) % 3;
+ const int other_2 = (screen_primary_channel + 2) % 3;
+
+ const int min_channel = MIN2(other_1, other_2);
+ const int max_channel = MAX2(other_1, other_2);
+
+ const float average_value = m_colorBalance * pixel_color[min_channel] +
+ (1.0f - m_colorBalance) * pixel_color[max_channel];
+ const float amount = (pixel_color[screen_primary_channel] - average_value);
+
+ copy_v4_v4(it.out, pixel_color);
+
+ const float amount_despill = m_despillFactor * amount;
+ if (amount_despill > 0.0f) {
+ it.out[screen_primary_channel] = pixel_color[screen_primary_channel] - amount_despill;
+ }
+ }
+}
+
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_KeyingDespillOperation.h b/source/blender/compositor/operations/COM_KeyingDespillOperation.h
index 279ac60e6e9..16bed651d3a 100644
--- a/source/blender/compositor/operations/COM_KeyingDespillOperation.h
+++ b/source/blender/compositor/operations/COM_KeyingDespillOperation.h
@@ -18,14 +18,14 @@
#pragma once
-#include "COM_NodeOperation.h"
+#include "COM_MultiThreadedOperation.h"
namespace blender::compositor {
/**
* Class with implementation of keying despill node
*/
-class KeyingDespillOperation : public NodeOperation {
+class KeyingDespillOperation : public MultiThreadedOperation {
protected:
SocketReader *m_pixelReader;
SocketReader *m_screenReader;
@@ -48,6 +48,10 @@ class KeyingDespillOperation : public NodeOperation {
}
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;
};
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_KeyingOperation.cc b/source/blender/compositor/operations/COM_KeyingOperation.cc
index e786e4b8219..3edb5a5d34e 100644
--- a/source/blender/compositor/operations/COM_KeyingOperation.cc
+++ b/source/blender/compositor/operations/COM_KeyingOperation.cc
@@ -110,4 +110,49 @@ void KeyingOperation::executePixelSampled(float output[4], float x, float y, Pix
}
}
+void KeyingOperation::update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs)
+{
+ for (BuffersIterator<float> it = output->iterate_with(inputs, area); !it.is_end(); ++it) {
+ const float *pixel_color = it.in(0);
+ const float *screen_color = it.in(1);
+
+ const int primary_channel = max_axis_v3(screen_color);
+ const float min_pixel_color = min_fff(pixel_color[0], pixel_color[1], pixel_color[2]);
+
+ if (min_pixel_color > 1.0f) {
+ /* Overexposure doesn't happen on screen itself and usually happens
+ * on light sources in the shot, this need to be checked separately
+ * because saturation and falloff calculation is based on the fact
+ * that pixels are not overexposed.
+ */
+ it.out[0] = 1.0f;
+ }
+ else {
+ const float saturation = get_pixel_saturation(pixel_color, m_screenBalance, primary_channel);
+ const float screen_saturation = get_pixel_saturation(
+ screen_color, m_screenBalance, primary_channel);
+
+ if (saturation < 0) {
+ /* Means main channel of pixel is different from screen,
+ * assume this is completely a foreground.
+ */
+ it.out[0] = 1.0f;
+ }
+ else if (saturation >= screen_saturation) {
+ /* Matched main channels and higher saturation on pixel
+ * is treated as completely background.
+ */
+ it.out[0] = 0.0f;
+ }
+ else {
+ /* Nice alpha falloff on edges. */
+ const float distance = 1.0f - saturation / screen_saturation;
+ it.out[0] = distance;
+ }
+ }
+ }
+}
+
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_KeyingOperation.h b/source/blender/compositor/operations/COM_KeyingOperation.h
index 3d41ecaa0f6..e134ad54896 100644
--- a/source/blender/compositor/operations/COM_KeyingOperation.h
+++ b/source/blender/compositor/operations/COM_KeyingOperation.h
@@ -20,7 +20,7 @@
#include <string.h>
-#include "COM_NodeOperation.h"
+#include "COM_MultiThreadedOperation.h"
#include "BLI_listbase.h"
@@ -29,7 +29,7 @@ namespace blender::compositor {
/**
* Class with implementation of keying node
*/
-class KeyingOperation : public NodeOperation {
+class KeyingOperation : public MultiThreadedOperation {
protected:
SocketReader *m_pixelReader;
SocketReader *m_screenReader;
@@ -48,6 +48,10 @@ class KeyingOperation : public NodeOperation {
}
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;
};
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_KeyingScreenOperation.cc b/source/blender/compositor/operations/COM_KeyingScreenOperation.cc
index 17b613246ad..c00aafc19a2 100644
--- a/source/blender/compositor/operations/COM_KeyingScreenOperation.cc
+++ b/source/blender/compositor/operations/COM_KeyingScreenOperation.cc
@@ -39,12 +39,21 @@ KeyingScreenOperation::KeyingScreenOperation()
this->m_framenumber = 0;
this->m_trackingObject[0] = 0;
flags.complex = true;
+ m_cachedTriangulation = nullptr;
}
void KeyingScreenOperation::initExecution()
{
initMutex();
- this->m_cachedTriangulation = nullptr;
+ if (execution_model_ == eExecutionModel::FullFrame) {
+ BLI_assert(m_cachedTriangulation == nullptr);
+ if (m_movieClip) {
+ m_cachedTriangulation = buildVoronoiTriangulation();
+ }
+ }
+ else {
+ this->m_cachedTriangulation = nullptr;
+ }
}
void KeyingScreenOperation::deinitExecution()
@@ -226,7 +235,7 @@ KeyingScreenOperation::TriangulationData *KeyingScreenOperation::buildVoronoiTri
return triangulation;
}
-void *KeyingScreenOperation::initializeTileData(rcti *rect)
+KeyingScreenOperation::TileData *KeyingScreenOperation::triangulate(const rcti *rect)
{
TileData *tile_data;
TriangulationData *triangulation;
@@ -234,18 +243,6 @@ void *KeyingScreenOperation::initializeTileData(rcti *rect)
int chunk_size = 20;
int i;
- if (this->m_movieClip == nullptr) {
- return nullptr;
- }
-
- if (!this->m_cachedTriangulation) {
- lockMutex();
- if (this->m_cachedTriangulation == nullptr) {
- this->m_cachedTriangulation = buildVoronoiTriangulation();
- }
- unlockMutex();
- }
-
triangulation = this->m_cachedTriangulation;
if (!triangulation) {
@@ -278,6 +275,23 @@ void *KeyingScreenOperation::initializeTileData(rcti *rect)
return tile_data;
}
+void *KeyingScreenOperation::initializeTileData(rcti *rect)
+{
+ if (this->m_movieClip == nullptr) {
+ return nullptr;
+ }
+
+ if (!this->m_cachedTriangulation) {
+ lockMutex();
+ if (this->m_cachedTriangulation == nullptr) {
+ this->m_cachedTriangulation = buildVoronoiTriangulation();
+ }
+ unlockMutex();
+ }
+
+ return triangulate(rect);
+}
+
void KeyingScreenOperation::deinitializeTileData(rcti * /*rect*/, void *data)
{
TileData *tile_data = (TileData *)data;
@@ -347,4 +361,57 @@ void KeyingScreenOperation::executePixel(float output[4], int x, int y, void *da
}
}
+void KeyingScreenOperation::update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs)
+{
+ if (m_movieClip == nullptr) {
+ output->fill(area, COM_COLOR_BLACK);
+ return;
+ }
+
+ TileData *tri_area = this->triangulate(&area);
+ BLI_assert(tri_area != nullptr);
+
+ const int *triangles = tri_area->triangles;
+ const int num_triangles = tri_area->triangles_total;
+ const TriangulationData *triangulation = m_cachedTriangulation;
+ for (BuffersIterator<float> it = output->iterate_with(inputs, area); !it.is_end(); ++it) {
+ copy_v4_v4(it.out, COM_COLOR_BLACK);
+
+ const float co[2] = {(float)it.x, (float)it.y};
+ for (int i = 0; i < num_triangles; i++) {
+ const int triangle_idx = triangles[i];
+ const rcti *rect = &triangulation->triangles_AABB[triangle_idx];
+
+ if (!BLI_rcti_isect_pt(rect, it.x, it.y)) {
+ continue;
+ }
+
+ const int *triangle = triangulation->triangles[triangle_idx];
+ const VoronoiTriangulationPoint &a = triangulation->triangulated_points[triangle[0]];
+ const VoronoiTriangulationPoint &b = triangulation->triangulated_points[triangle[1]];
+ const VoronoiTriangulationPoint &c = triangulation->triangulated_points[triangle[2]];
+
+ float w[3];
+ if (!barycentric_coords_v2(a.co, b.co, c.co, co, w)) {
+ continue;
+ }
+
+ if (barycentric_inside_triangle_v2(w)) {
+ it.out[0] = a.color[0] * w[0] + b.color[0] * w[1] + c.color[0] * w[2];
+ it.out[1] = a.color[1] * w[0] + b.color[1] * w[1] + c.color[1] * w[2];
+ it.out[2] = a.color[2] * w[0] + b.color[2] * w[1] + c.color[2] * w[2];
+ break;
+ }
+ }
+ }
+
+ if (tri_area->triangles) {
+ MEM_freeN(tri_area->triangles);
+ }
+
+ MEM_freeN(tri_area);
+}
+
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_KeyingScreenOperation.h b/source/blender/compositor/operations/COM_KeyingScreenOperation.h
index 4118d229be9..0bc47dbea30 100644
--- a/source/blender/compositor/operations/COM_KeyingScreenOperation.h
+++ b/source/blender/compositor/operations/COM_KeyingScreenOperation.h
@@ -20,7 +20,7 @@
#include <string.h>
-#include "COM_NodeOperation.h"
+#include "COM_MultiThreadedOperation.h"
#include "DNA_movieclip_types.h"
@@ -34,7 +34,7 @@ namespace blender::compositor {
/**
* Class with implementation of green screen gradient rasterization
*/
-class KeyingScreenOperation : public NodeOperation {
+class KeyingScreenOperation : public MultiThreadedOperation {
protected:
typedef struct TriangulationData {
VoronoiTriangulationPoint *triangulated_points;
@@ -43,6 +43,7 @@ class KeyingScreenOperation : public NodeOperation {
rcti *triangles_AABB;
} TriangulationData;
+ /* TODO(manzanilla): rename to #TrianguledArea on removing tiled implementation. */
typedef struct TileData {
int *triangles;
int triangles_total;
@@ -84,6 +85,13 @@ class KeyingScreenOperation : public NodeOperation {
}
void executePixel(float output[4], int x, int y, void *data) override;
+
+ void update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs) override;
+
+ private:
+ TileData *triangulate(const rcti *rect);
};
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_LuminanceMatteOperation.cc b/source/blender/compositor/operations/COM_LuminanceMatteOperation.cc
index 5ca16e40ce3..c642c60b912 100644
--- a/source/blender/compositor/operations/COM_LuminanceMatteOperation.cc
+++ b/source/blender/compositor/operations/COM_LuminanceMatteOperation.cc
@@ -29,6 +29,7 @@ LuminanceMatteOperation::LuminanceMatteOperation()
addOutputSocket(DataType::Value);
this->m_inputImageProgram = nullptr;
+ flags.can_be_constant = true;
}
void LuminanceMatteOperation::initExecution()
@@ -78,4 +79,39 @@ void LuminanceMatteOperation::executePixelSampled(float output[4],
output[0] = min_ff(alpha, inColor[3]);
}
+void LuminanceMatteOperation::update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs)
+{
+ for (BuffersIterator<float> it = output->iterate_with(inputs, area); !it.is_end(); ++it) {
+ const float *color = it.in(0);
+ const float luminance = IMB_colormanagement_get_luminance(color);
+
+ /* One line thread-friend algorithm:
+ * `it.out[0] = MIN2(color[3], MIN2(1.0f, MAX2(0.0f, ((luminance - low) / (high - low))));`
+ */
+
+ /* Test range. */
+ const float high = m_settings->t1;
+ const float low = m_settings->t2;
+ float alpha;
+ if (luminance > high) {
+ alpha = 1.0f;
+ }
+ else if (luminance < low) {
+ alpha = 0.0f;
+ }
+ else { /* Blend. */
+ alpha = (luminance - low) / (high - low);
+ }
+
+ /* Store matte(alpha) value in [0] to go with
+ * COM_SetAlphaMultiplyOperation and the Value output.
+ */
+
+ /* Don't make something that was more transparent less transparent. */
+ it.out[0] = MIN2(alpha, color[3]);
+ }
+}
+
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_LuminanceMatteOperation.h b/source/blender/compositor/operations/COM_LuminanceMatteOperation.h
index 035c68b9d59..aedfc715382 100644
--- a/source/blender/compositor/operations/COM_LuminanceMatteOperation.h
+++ b/source/blender/compositor/operations/COM_LuminanceMatteOperation.h
@@ -18,7 +18,7 @@
#pragma once
-#include "COM_MixOperation.h"
+#include "COM_MultiThreadedOperation.h"
namespace blender::compositor {
@@ -26,7 +26,7 @@ namespace blender::compositor {
* this program converts an input color to an output value.
* it assumes we are in sRGB color space.
*/
-class LuminanceMatteOperation : public NodeOperation {
+class LuminanceMatteOperation : public MultiThreadedOperation {
private:
NodeChroma *m_settings;
SocketReader *m_inputImageProgram;
@@ -49,6 +49,10 @@ class LuminanceMatteOperation : public NodeOperation {
{
this->m_settings = nodeChroma;
}
+
+ void update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs) override;
};
} // namespace blender::compositor