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:
Diffstat (limited to 'source/blender/compositor')
-rw-r--r--source/blender/compositor/CMakeLists.txt3
-rw-r--r--source/blender/compositor/COM_defines.h23
-rw-r--r--source/blender/compositor/intern/COM_BufferArea.h198
-rw-r--r--source/blender/compositor/intern/COM_BufferRange.h171
-rw-r--r--source/blender/compositor/intern/COM_BuffersIterator.h164
-rw-r--r--source/blender/compositor/intern/COM_Debug.cc45
-rw-r--r--source/blender/compositor/intern/COM_Debug.h17
-rw-r--r--source/blender/compositor/intern/COM_FullFrameExecutionModel.cc1
-rw-r--r--source/blender/compositor/intern/COM_MemoryBuffer.cc14
-rw-r--r--source/blender/compositor/intern/COM_MemoryBuffer.h29
-rw-r--r--source/blender/compositor/operations/COM_BrightnessOperation.cc45
-rw-r--r--source/blender/compositor/operations/COM_BrightnessOperation.h8
-rw-r--r--source/blender/compositor/operations/COM_DoubleEdgeMaskOperation.cc40
-rw-r--r--source/blender/compositor/operations/COM_DoubleEdgeMaskOperation.h12
-rw-r--r--source/blender/compositor/operations/COM_EllipseMaskOperation.cc73
-rw-r--r--source/blender/compositor/operations/COM_EllipseMaskOperation.h16
-rw-r--r--source/blender/compositor/operations/COM_MixOperation.cc555
-rw-r--r--source/blender/compositor/operations/COM_MixOperation.h89
-rw-r--r--source/blender/compositor/operations/COM_SMAAOperation.cc2
-rw-r--r--source/blender/compositor/operations/COM_ViewerOperation.cc32
-rw-r--r--source/blender/compositor/operations/COM_ViewerOperation.h12
21 files changed, 1535 insertions, 14 deletions
diff --git a/source/blender/compositor/CMakeLists.txt b/source/blender/compositor/CMakeLists.txt
index 20b56ceb55f..830792a2a48 100644
--- a/source/blender/compositor/CMakeLists.txt
+++ b/source/blender/compositor/CMakeLists.txt
@@ -49,8 +49,11 @@ set(SRC
COM_compositor.h
COM_defines.h
+ intern/COM_BufferArea.h
intern/COM_BufferOperation.cc
intern/COM_BufferOperation.h
+ intern/COM_BufferRange.h
+ intern/COM_BuffersIterator.h
intern/COM_CPUDevice.cc
intern/COM_CPUDevice.h
intern/COM_ChunkOrder.cc
diff --git a/source/blender/compositor/COM_defines.h b/source/blender/compositor/COM_defines.h
index 9f8e6f10215..0e97bafbb0b 100644
--- a/source/blender/compositor/COM_defines.h
+++ b/source/blender/compositor/COM_defines.h
@@ -18,6 +18,9 @@
#pragma once
+#include "BLI_index_range.hh"
+#include "BLI_rect.h"
+
namespace blender::compositor {
enum class eExecutionModel {
@@ -109,4 +112,24 @@ constexpr float COM_PREVIEW_SIZE = 140.f;
constexpr float COM_RULE_OF_THIRDS_DIVIDER = 100.0f;
constexpr float COM_BLUR_BOKEH_PIXELS = 512;
+constexpr IndexRange XRange(const rcti &area)
+{
+ return IndexRange(area.xmin, area.xmax - area.xmin);
+}
+
+constexpr IndexRange YRange(const rcti &area)
+{
+ return IndexRange(area.ymin, area.ymax - area.ymin);
+}
+
+constexpr IndexRange XRange(const rcti *area)
+{
+ return XRange(*area);
+}
+
+constexpr IndexRange YRange(const rcti *area)
+{
+ return YRange(*area);
+}
+
} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_BufferArea.h b/source/blender/compositor/intern/COM_BufferArea.h
new file mode 100644
index 00000000000..621ffea5bc3
--- /dev/null
+++ b/source/blender/compositor/intern/COM_BufferArea.h
@@ -0,0 +1,198 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Copyright 2021, Blender Foundation.
+ */
+
+#pragma once
+
+#include "BLI_rect.h"
+
+#include <iterator>
+
+namespace blender::compositor {
+
+/* Forward declarations. */
+template<typename T> class BufferAreaIterator;
+
+/**
+ * A rectangle area of buffer elements.
+ */
+template<typename T> class BufferArea : rcti {
+ public:
+ using Iterator = BufferAreaIterator<T>;
+ using ConstIterator = BufferAreaIterator<const T>;
+
+ private:
+ T *buffer_;
+ /* Number of elements in a buffer row. */
+ int buffer_width_;
+ /* Buffer element stride. */
+ int elem_stride_;
+
+ public:
+ constexpr BufferArea() = default;
+
+ /**
+ * Create a buffer area containing given rectangle area.
+ */
+ constexpr BufferArea(T *buffer, int buffer_width, const rcti &area, int elem_stride = 1)
+ : rcti(area), buffer_(buffer), buffer_width_(buffer_width), elem_stride_(elem_stride)
+ {
+ }
+
+ /**
+ * Create a buffer area containing whole buffer with no offsets.
+ */
+ constexpr BufferArea(T *buffer, int buffer_width, int buffer_height, int elem_stride = 1)
+ : buffer_(buffer), buffer_width_(buffer_width), elem_stride_(elem_stride)
+ {
+ BLI_rcti_init(this, 0, buffer_width, 0, buffer_height);
+ }
+
+ constexpr friend bool operator==(const BufferArea &a, const BufferArea &b)
+ {
+ return a.buffer_ == b.buffer_ && BLI_rcti_compare(&a, &b) && a.elem_stride_ == b.elem_stride_;
+ }
+
+ constexpr const rcti &get_rect() const
+ {
+ return *this;
+ }
+
+ /**
+ * Number of elements in a row.
+ */
+ constexpr int width() const
+ {
+ return BLI_rcti_size_x(this);
+ }
+
+ /**
+ * Number of elements in a column.
+ */
+ constexpr int height() const
+ {
+ return BLI_rcti_size_y(this);
+ }
+
+ constexpr Iterator begin()
+ {
+ return begin_iterator<Iterator>();
+ }
+
+ constexpr Iterator end()
+ {
+ return end_iterator<Iterator>();
+ }
+
+ constexpr ConstIterator begin() const
+ {
+ return begin_iterator<ConstIterator>();
+ }
+
+ constexpr ConstIterator end() const
+ {
+ return end_iterator<ConstIterator>();
+ }
+
+ private:
+ template<typename TIterator> constexpr TIterator begin_iterator() const
+ {
+ if (elem_stride_ == 0) {
+ /* Iterate a single element. */
+ return TIterator(buffer_, 1, 1, 1);
+ }
+
+ T *begin_ptr = buffer_ + (intptr_t)this->ymin * buffer_width_ * elem_stride_ +
+ (intptr_t)this->xmin * elem_stride_;
+ return TIterator(begin_ptr, buffer_width_, BLI_rcti_size_x(this), elem_stride_);
+ }
+
+ template<typename TIterator> constexpr TIterator end_iterator() const
+ {
+ if (elem_stride_ == 0) {
+ /* Iterate a single element. */
+ return TIterator(buffer_ + 1, 1, 1, 1);
+ }
+
+ T *end_ptr = buffer_ + (intptr_t)(this->ymax - 1) * buffer_width_ * elem_stride_ +
+ (intptr_t)this->xmax * elem_stride_;
+ return TIterator(end_ptr, buffer_width_, BLI_rcti_size_x(this), elem_stride_);
+ }
+};
+
+template<typename T> class BufferAreaIterator {
+ public:
+ using iterator_category = std::input_iterator_tag;
+ using value_type = T *;
+ using pointer = T *const *;
+ using reference = T *const &;
+ using difference_type = std::ptrdiff_t;
+
+ private:
+ int elem_stride_;
+ int row_stride_;
+ /* Stride between a row end and the next row start. */
+ int rows_gap_;
+ T *current_;
+ const T *row_end_;
+
+ public:
+ constexpr BufferAreaIterator() = default;
+
+ constexpr BufferAreaIterator(T *current, int buffer_width, int area_width, int elem_stride = 1)
+ : elem_stride_(elem_stride),
+ row_stride_(buffer_width * elem_stride),
+ rows_gap_(row_stride_ - area_width * elem_stride),
+ current_(current),
+ row_end_(current + area_width * elem_stride)
+ {
+ }
+
+ constexpr BufferAreaIterator &operator++()
+ {
+ current_ += elem_stride_;
+ if (current_ == row_end_) {
+ current_ += rows_gap_;
+ row_end_ += row_stride_;
+ }
+ return *this;
+ }
+
+ constexpr BufferAreaIterator operator++(int) const
+ {
+ BufferAreaIterator copied_iterator = *this;
+ ++copied_iterator;
+ return copied_iterator;
+ }
+
+ constexpr friend bool operator!=(const BufferAreaIterator &a, const BufferAreaIterator &b)
+ {
+ return a.current_ != b.current_;
+ }
+
+ constexpr friend bool operator==(const BufferAreaIterator &a, const BufferAreaIterator &b)
+ {
+ return a.current_ == b.current_;
+ }
+
+ constexpr T *operator*() const
+ {
+ return current_;
+ }
+};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_BufferRange.h b/source/blender/compositor/intern/COM_BufferRange.h
new file mode 100644
index 00000000000..ffdf1f2f1e5
--- /dev/null
+++ b/source/blender/compositor/intern/COM_BufferRange.h
@@ -0,0 +1,171 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Copyright 2021, Blender Foundation.
+ */
+
+#pragma once
+
+#include "BLI_assert.h"
+#include "BLI_rect.h"
+
+#include <iterator>
+
+namespace blender::compositor {
+
+/* Forward declarations. */
+template<typename T> class BufferRangeIterator;
+
+/**
+ * A range of buffer elements.
+ */
+template<typename T> class BufferRange {
+ public:
+ using Iterator = BufferRangeIterator<T>;
+ using ConstIterator = BufferRangeIterator<const T>;
+
+ private:
+ T *start_;
+ /* Number of elements in the range. */
+ int64_t size_;
+ /* Buffer element stride. */
+ int elem_stride_;
+
+ public:
+ constexpr BufferRange() = default;
+
+ /**
+ * Create a buffer range of elements from a given element index.
+ */
+ constexpr BufferRange(T *buffer, int64_t start_elem_index, int64_t size, int elem_stride = 1)
+ : start_(buffer + start_elem_index * elem_stride), size_(size), elem_stride_(elem_stride)
+ {
+ }
+
+ constexpr friend bool operator==(const BufferRange &a, const BufferRange &b)
+ {
+ return a.start_ == b.start_ && a.size_ == b.size_ && a.elem_stride_ == b.elem_stride_;
+ }
+
+ /**
+ * Access an element in the range. Index is relative to range start.
+ */
+ constexpr T *operator[](int64_t index) const
+ {
+ BLI_assert(index >= 0);
+ BLI_assert(index < this->size());
+ return start_ + index * elem_stride_;
+ }
+
+ /**
+ * Get the number of elements in the range.
+ */
+ constexpr int64_t size() const
+ {
+ return size_;
+ }
+
+ constexpr Iterator begin()
+ {
+ return begin_iterator<Iterator>();
+ }
+
+ constexpr Iterator end()
+ {
+ return end_iterator<Iterator>();
+ }
+
+ constexpr ConstIterator begin() const
+ {
+ return begin_iterator<ConstIterator>();
+ }
+
+ constexpr ConstIterator end() const
+ {
+ return end_iterator<ConstIterator>();
+ }
+
+ private:
+ template<typename TIterator> constexpr TIterator begin_iterator() const
+ {
+ if (elem_stride_ == 0) {
+ /* Iterate a single element. */
+ return TIterator(start_, 1);
+ }
+
+ return TIterator(start_, elem_stride_);
+ }
+
+ template<typename TIterator> constexpr TIterator end_iterator() const
+ {
+ if (elem_stride_ == 0) {
+ /* Iterate a single element. */
+ return TIterator(start_ + 1, 1);
+ }
+
+ return TIterator(start_ + size_ * elem_stride_, elem_stride_);
+ }
+};
+
+template<typename T> class BufferRangeIterator {
+ public:
+ using iterator_category = std::input_iterator_tag;
+ using value_type = T *;
+ using pointer = T *const *;
+ using reference = T *const &;
+ using difference_type = std::ptrdiff_t;
+
+ private:
+ T *current_;
+ int elem_stride_;
+
+ public:
+ constexpr BufferRangeIterator() = default;
+
+ constexpr BufferRangeIterator(T *current, int elem_stride = 1)
+ : current_(current), elem_stride_(elem_stride)
+ {
+ }
+
+ constexpr BufferRangeIterator &operator++()
+ {
+ current_ += elem_stride_;
+ return *this;
+ }
+
+ constexpr BufferRangeIterator operator++(int) const
+ {
+ BufferRangeIterator copied_iterator = *this;
+ ++copied_iterator;
+ return copied_iterator;
+ }
+
+ constexpr friend bool operator!=(const BufferRangeIterator &a, const BufferRangeIterator &b)
+ {
+ return a.current_ != b.current_;
+ }
+
+ constexpr friend bool operator==(const BufferRangeIterator &a, const BufferRangeIterator &b)
+ {
+ return a.current_ == b.current_;
+ }
+
+ constexpr T *operator*() const
+ {
+ return current_;
+ }
+};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_BuffersIterator.h b/source/blender/compositor/intern/COM_BuffersIterator.h
new file mode 100644
index 00000000000..58dbd9c6f59
--- /dev/null
+++ b/source/blender/compositor/intern/COM_BuffersIterator.h
@@ -0,0 +1,164 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Copyright 2021, Blender Foundation.
+ */
+
+#pragma once
+
+#include "BLI_rect.h"
+#include "BLI_vector.hh"
+
+namespace blender::compositor {
+
+/**
+ * Builds an iterator for simultaneously iterating an area of elements in an output buffer and any
+ * number of input buffers. It's not a standard C++ iterator and it does not support neither
+ * deference, equality or postfix increment operators.
+ */
+template<typename T> class BuffersIteratorBuilder {
+ public:
+ class Iterator {
+ const T *out_end_;
+ const T *out_row_end_;
+ int out_elem_stride_;
+ int out_row_stride_;
+ /* Stride between an output row end and the next row start. */
+ int out_rows_gap_;
+
+ struct In {
+ int elem_stride;
+ int rows_gap;
+ const T *in;
+ };
+ Vector<In, 6> ins_;
+
+ friend class BuffersIteratorBuilder;
+
+ public:
+ /**
+ * Current output element.
+ */
+ T *out;
+
+ public:
+ /**
+ * Get current element from an input.
+ */
+ const T *in(int input_index) const
+ {
+ BLI_assert(input_index < ins_.size());
+ return ins_[input_index].in;
+ }
+
+ int get_num_inputs() const
+ {
+ return ins_.size();
+ }
+
+ /**
+ * Has the end of the area been reached.
+ */
+ bool is_end() const
+ {
+ return out >= out_end_;
+ }
+
+ /**
+ * Go to the next element in the area.
+ */
+ void next()
+ {
+ out += out_elem_stride_;
+ for (In &in : ins_) {
+ in.in += in.elem_stride;
+ }
+ if (out == out_row_end_) {
+ out += out_rows_gap_;
+ out_row_end_ += out_row_stride_;
+ for (In &in : ins_) {
+ in.in += in.rows_gap;
+ }
+ }
+ }
+
+ Iterator &operator++()
+ {
+ this->next();
+ return *this;
+ }
+ };
+
+ private:
+ Iterator iterator_;
+ rcti area_;
+ bool is_built_;
+
+ public:
+ /**
+ * Create a buffers iterator builder to iterate given output buffer area.
+ * \param output: Output buffer.
+ * \param buffer_width: Number of elements in an output buffer row.
+ * \param area: Rectangle area to be iterated in all buffers.
+ * \param elem_stride: Output buffer element stride.
+ */
+ BuffersIteratorBuilder(T *output, int buffer_width, const rcti &area, int elem_stride = 1)
+ : area_(area), is_built_(false)
+ {
+ iterator_.out_elem_stride_ = elem_stride;
+ iterator_.out_row_stride_ = buffer_width * elem_stride;
+ iterator_.out_rows_gap_ = iterator_.out_row_stride_ - BLI_rcti_size_x(&area) * elem_stride;
+ iterator_.out = output + (intptr_t)area.ymin * iterator_.out_row_stride_ +
+ (intptr_t)area.xmin * elem_stride;
+ iterator_.out_row_end_ = iterator_.out + (intptr_t)BLI_rcti_size_x(&area) * elem_stride;
+ iterator_.out_end_ = iterator_.out_row_end_ +
+ (intptr_t)iterator_.out_row_stride_ * (BLI_rcti_size_y(&area) - 1);
+ }
+
+ /**
+ * Create a buffers iterator builder to iterate given output buffer with no offsets.
+ */
+ BuffersIteratorBuilder(T *output, int buffer_width, int buffer_height, int elem_stride = 1)
+ : BuffersIteratorBuilder(
+ output, buffer_width, {0, buffer_width, 0, buffer_height}, elem_stride)
+ {
+ }
+
+ /**
+ * Add an input buffer to be iterated. Its coordinates must be correlated with the output.
+ */
+ void add_input(const T *input, int buffer_width, int elem_stride = 1)
+ {
+ BLI_assert(!is_built_);
+ typename Iterator::In in;
+ in.elem_stride = elem_stride;
+ in.rows_gap = buffer_width * elem_stride - BLI_rcti_size_x(&area_) * elem_stride;
+ in.in = input + area_.ymin * buffer_width * elem_stride + area_.xmin * elem_stride;
+ iterator_.ins_.append(std::move(in));
+ }
+
+ /**
+ * Build the iterator.
+ */
+ BuffersIteratorBuilder::Iterator build()
+ {
+ is_built_ = true;
+ return iterator_;
+ }
+};
+
+template<typename T> using BuffersIterator = typename BuffersIteratorBuilder<T>::Iterator;
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_Debug.cc b/source/blender/compositor/intern/COM_Debug.cc
index abef4517b3e..c0460aed4a4 100644
--- a/source/blender/compositor/intern/COM_Debug.cc
+++ b/source/blender/compositor/intern/COM_Debug.cc
@@ -31,6 +31,8 @@ extern "C" {
#include "BKE_appdir.h"
#include "BKE_node.h"
#include "DNA_node_types.h"
+#include "IMB_imbuf.h"
+#include "IMB_imbuf_types.h"
}
#include "COM_ExecutionSystem.h"
@@ -50,7 +52,7 @@ std::string DebugInfo::m_current_node_name;
std::string DebugInfo::m_current_op_name;
DebugInfo::GroupStateMap DebugInfo::m_group_states;
-static std::string operation_class_name(NodeOperation *op)
+static std::string operation_class_name(const NodeOperation *op)
{
std::string full_name = typeid(*op).name();
/* Remove name-spaces. */
@@ -452,4 +454,45 @@ void DebugInfo::graphviz(const ExecutionSystem *system, StringRefNull name)
}
}
+static std::string get_operations_export_dir()
+{
+ return std::string(BKE_tempdir_session()) + "COM_operations" + SEP_STR;
+}
+
+void DebugInfo::export_operation(const NodeOperation *op, MemoryBuffer *render)
+{
+ ImBuf *ibuf = IMB_allocFromBuffer(nullptr,
+ render->getBuffer(),
+ render->getWidth(),
+ render->getHeight(),
+ render->get_num_channels());
+
+ const std::string file_name = operation_class_name(op) + "_" + std::to_string(op->get_id()) +
+ ".png";
+ const std::string path = get_operations_export_dir() + file_name;
+ BLI_make_existing_file(path.c_str());
+ IMB_saveiff(ibuf, path.c_str(), ibuf->flags);
+ IMB_freeImBuf(ibuf);
+}
+
+void DebugInfo::delete_operation_exports()
+{
+ const std::string dir = get_operations_export_dir();
+ if (BLI_exists(dir.c_str())) {
+ struct direntry *file_list;
+ int num_files = BLI_filelist_dir_contents(dir.c_str(), &file_list);
+ for (int i = 0; i < num_files; i++) {
+ direntry *file = &file_list[i];
+ const eFileAttributes file_attrs = BLI_file_attributes(file->path);
+ if (file_attrs & FILE_ATTR_ANY_LINK) {
+ continue;
+ }
+
+ if (BLI_is_file(file->path) && BLI_path_extension_check(file->path, ".png")) {
+ BLI_delete(file->path, false, false);
+ }
+ }
+ }
+}
+
} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_Debug.h b/source/blender/compositor/intern/COM_Debug.h
index 53461e13f48..23d99c7e529 100644
--- a/source/blender/compositor/intern/COM_Debug.h
+++ b/source/blender/compositor/intern/COM_Debug.h
@@ -30,6 +30,9 @@ namespace blender::compositor {
static constexpr bool COM_EXPORT_GRAPHVIZ = false;
static constexpr bool COM_GRAPHVIZ_SHOW_NODE_NAME = false;
+/* Saves operations results to image files. */
+static constexpr bool COM_EXPORT_OPERATION_BUFFERS = false;
+
class Node;
class ExecutionSystem;
class ExecutionGroup;
@@ -75,6 +78,9 @@ class DebugInfo {
m_group_states[execution_group] = EG_WAIT;
}
}
+ if (COM_EXPORT_OPERATION_BUFFERS) {
+ delete_operation_exports();
+ }
};
static void node_added(const Node *node)
@@ -118,6 +124,14 @@ class DebugInfo {
}
};
+ static void operation_rendered(const NodeOperation *op, MemoryBuffer *render)
+ {
+ /* Don't export constant operations as there are too many and it's rarely useful. */
+ if (COM_EXPORT_OPERATION_BUFFERS && render && !render->is_a_single_elem()) {
+ export_operation(op, render);
+ }
+ }
+
static void graphviz(const ExecutionSystem *system, StringRefNull name = "");
protected:
@@ -133,6 +147,9 @@ class DebugInfo {
const char *name, const char *color, const char *style, char *str, int maxlen);
static int graphviz_legend(char *str, int maxlen, bool has_execution_groups);
static bool graphviz_system(const ExecutionSystem *system, char *str, int maxlen);
+
+ static void export_operation(const NodeOperation *op, MemoryBuffer *render);
+ static void delete_operation_exports();
};
} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_FullFrameExecutionModel.cc b/source/blender/compositor/intern/COM_FullFrameExecutionModel.cc
index 3b0a9172871..9f6904bb306 100644
--- a/source/blender/compositor/intern/COM_FullFrameExecutionModel.cc
+++ b/source/blender/compositor/intern/COM_FullFrameExecutionModel.cc
@@ -104,6 +104,7 @@ void FullFrameExecutionModel::render_operation(NodeOperation *op)
op->render(op_buf, areas, input_bufs);
active_buffers_.set_rendered_buffer(op, std::unique_ptr<MemoryBuffer>(op_buf));
+ DebugInfo::operation_rendered(op, op_buf);
operation_finished(op);
}
diff --git a/source/blender/compositor/intern/COM_MemoryBuffer.cc b/source/blender/compositor/intern/COM_MemoryBuffer.cc
index c7bddddd0e6..f4f58146de4 100644
--- a/source/blender/compositor/intern/COM_MemoryBuffer.cc
+++ b/source/blender/compositor/intern/COM_MemoryBuffer.cc
@@ -129,6 +129,20 @@ void MemoryBuffer::clear()
memset(m_buffer, 0, buffer_len() * m_num_channels * sizeof(float));
}
+BuffersIterator<float> MemoryBuffer::iterate_with(Span<MemoryBuffer *> inputs)
+{
+ return iterate_with(inputs, m_rect);
+}
+
+BuffersIterator<float> MemoryBuffer::iterate_with(Span<MemoryBuffer *> inputs, const rcti &area)
+{
+ BuffersIteratorBuilder<float> builder(m_buffer, getWidth(), area, elem_stride);
+ for (MemoryBuffer *input : inputs) {
+ builder.add_input(input->getBuffer(), input->getWidth(), input->elem_stride);
+ }
+ return builder.build();
+}
+
/**
* Converts a single elem buffer to a full size buffer (allocates memory for all
* elements in resolution).
diff --git a/source/blender/compositor/intern/COM_MemoryBuffer.h b/source/blender/compositor/intern/COM_MemoryBuffer.h
index 4ad0872b0b7..048ed4c5d6e 100644
--- a/source/blender/compositor/intern/COM_MemoryBuffer.h
+++ b/source/blender/compositor/intern/COM_MemoryBuffer.h
@@ -18,6 +18,9 @@
#pragma once
+#include "COM_BufferArea.h"
+#include "COM_BufferRange.h"
+#include "COM_BuffersIterator.h"
#include "COM_ExecutionGroup.h"
#include "COM_MemoryProxy.h"
@@ -239,6 +242,32 @@ class MemoryBuffer {
}
/**
+ * Get all buffer elements as a range with no offsets.
+ */
+ BufferRange<float> as_range()
+ {
+ return BufferRange<float>(m_buffer, 0, buffer_len(), elem_stride);
+ }
+
+ BufferRange<const float> as_range() const
+ {
+ return BufferRange<const float>(m_buffer, 0, buffer_len(), elem_stride);
+ }
+
+ BufferArea<float> get_buffer_area(const rcti &area)
+ {
+ return BufferArea<float>(m_buffer, getWidth(), area, elem_stride);
+ }
+
+ BufferArea<const float> get_buffer_area(const rcti &area) const
+ {
+ return BufferArea<const float>(m_buffer, getWidth(), area, elem_stride);
+ }
+
+ BuffersIterator<float> iterate_with(Span<MemoryBuffer *> inputs);
+ BuffersIterator<float> iterate_with(Span<MemoryBuffer *> inputs, const rcti &area);
+
+ /**
* \brief get the data of this MemoryBuffer
* \note buffer should already be available in memory
*/
diff --git a/source/blender/compositor/operations/COM_BrightnessOperation.cc b/source/blender/compositor/operations/COM_BrightnessOperation.cc
index 92cab47318a..7878eca2bbd 100644
--- a/source/blender/compositor/operations/COM_BrightnessOperation.cc
+++ b/source/blender/compositor/operations/COM_BrightnessOperation.cc
@@ -28,6 +28,7 @@ BrightnessOperation::BrightnessOperation()
this->addOutputSocket(DataType::Color);
this->m_inputProgram = nullptr;
this->m_use_premultiply = false;
+ flags.can_be_constant = true;
}
void BrightnessOperation::setUsePremultiply(bool use_premultiply)
@@ -85,6 +86,50 @@ void BrightnessOperation::executePixelSampled(float output[4],
}
}
+void BrightnessOperation::update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs)
+{
+ float tmp_color[4];
+ for (BuffersIterator<float> it = output->iterate_with(inputs, area); !it.is_end(); ++it) {
+ const float *in_color = it.in(0);
+ const float brightness = *it.in(1) / 100.0f;
+ const float contrast = *it.in(2);
+ float delta = contrast / 200.0f;
+ /*
+ * The algorithm is by Werner D. Streidt
+ * (http://visca.com/ffactory/archives/5-99/msg00021.html)
+ * Extracted of OpenCV demhist.c
+ */
+ float a, b;
+ if (contrast > 0) {
+ a = 1.0f - delta * 2.0f;
+ a = 1.0f / max_ff(a, FLT_EPSILON);
+ b = a * (brightness - delta);
+ }
+ else {
+ delta *= -1;
+ a = max_ff(1.0f - delta * 2.0f, 0.0f);
+ b = a * brightness + delta;
+ }
+ const float *color;
+ if (this->m_use_premultiply) {
+ premul_to_straight_v4_v4(tmp_color, in_color);
+ color = tmp_color;
+ }
+ else {
+ color = in_color;
+ }
+ it.out[0] = a * color[0] + b;
+ it.out[1] = a * color[1] + b;
+ it.out[2] = a * color[2] + b;
+ it.out[3] = color[3];
+ if (this->m_use_premultiply) {
+ straight_to_premul_v4(it.out);
+ }
+ }
+}
+
void BrightnessOperation::deinitExecution()
{
this->m_inputProgram = nullptr;
diff --git a/source/blender/compositor/operations/COM_BrightnessOperation.h b/source/blender/compositor/operations/COM_BrightnessOperation.h
index 7c33e0b35ec..64b4fa0dbe2 100644
--- a/source/blender/compositor/operations/COM_BrightnessOperation.h
+++ b/source/blender/compositor/operations/COM_BrightnessOperation.h
@@ -18,11 +18,11 @@
#pragma once
-#include "COM_NodeOperation.h"
+#include "COM_MultiThreadedOperation.h"
namespace blender::compositor {
-class BrightnessOperation : public NodeOperation {
+class BrightnessOperation : public MultiThreadedOperation {
private:
/**
* Cached reference to the inputProgram
@@ -52,6 +52,10 @@ class BrightnessOperation : public NodeOperation {
void deinitExecution() override;
void setUsePremultiply(bool use_premultiply);
+
+ 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_DoubleEdgeMaskOperation.cc b/source/blender/compositor/operations/COM_DoubleEdgeMaskOperation.cc
index c4099a6d33d..1a0d7e52b0f 100644
--- a/source/blender/compositor/operations/COM_DoubleEdgeMaskOperation.cc
+++ b/source/blender/compositor/operations/COM_DoubleEdgeMaskOperation.cc
@@ -1318,6 +1318,7 @@ DoubleEdgeMaskOperation::DoubleEdgeMaskOperation()
this->m_adjacentOnly = false;
this->m_keepInside = false;
this->flags.complex = true;
+ is_output_rendered_ = false;
}
bool DoubleEdgeMaskOperation::determineDependingAreaOfInterest(rcti * /*input*/,
@@ -1382,4 +1383,43 @@ void DoubleEdgeMaskOperation::deinitExecution()
}
}
+void DoubleEdgeMaskOperation::get_area_of_interest(int UNUSED(input_idx),
+ const rcti &UNUSED(output_area),
+ rcti &r_input_area)
+{
+ r_input_area.xmax = this->getWidth();
+ r_input_area.xmin = 0;
+ r_input_area.ymax = this->getHeight();
+ r_input_area.ymin = 0;
+}
+
+void DoubleEdgeMaskOperation::update_memory_buffer(MemoryBuffer *output,
+ const rcti &UNUSED(area),
+ Span<MemoryBuffer *> inputs)
+{
+ if (!is_output_rendered_) {
+ /* Ensure full buffers to work with no strides. */
+ MemoryBuffer *input_inner_mask = inputs[0];
+ MemoryBuffer *inner_mask = input_inner_mask->is_a_single_elem() ? input_inner_mask->inflate() :
+ input_inner_mask;
+ MemoryBuffer *input_outer_mask = inputs[1];
+ MemoryBuffer *outer_mask = input_outer_mask->is_a_single_elem() ? input_outer_mask->inflate() :
+ input_outer_mask;
+
+ BLI_assert(output->getWidth() == this->getWidth());
+ BLI_assert(output->getHeight() == this->getHeight());
+ /* TODO(manzanilla): Once tiled implementation is removed, use execution system to run
+ * multi-threaded where possible. */
+ doDoubleEdgeMask(inner_mask->getBuffer(), outer_mask->getBuffer(), output->getBuffer());
+ is_output_rendered_ = true;
+
+ if (inner_mask != input_inner_mask) {
+ delete inner_mask;
+ }
+ if (outer_mask != input_outer_mask) {
+ delete outer_mask;
+ }
+ }
+}
+
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_DoubleEdgeMaskOperation.h b/source/blender/compositor/operations/COM_DoubleEdgeMaskOperation.h
index e956e8edc3e..45a80bbbbf0 100644
--- a/source/blender/compositor/operations/COM_DoubleEdgeMaskOperation.h
+++ b/source/blender/compositor/operations/COM_DoubleEdgeMaskOperation.h
@@ -18,7 +18,7 @@
#pragma once
-#include "COM_NodeOperation.h"
+#include "COM_MultiThreadedOperation.h"
namespace blender::compositor {
@@ -31,8 +31,12 @@ class DoubleEdgeMaskOperation : public NodeOperation {
SocketReader *m_inputInnerMask;
bool m_adjacentOnly;
bool m_keepInside;
+
+ /* TODO(manzanilla): To be removed with tiled implementation. */
float *m_cachedInstance;
+ bool is_output_rendered_;
+
public:
DoubleEdgeMaskOperation();
@@ -66,6 +70,12 @@ class DoubleEdgeMaskOperation : public NodeOperation {
{
this->m_keepInside = keepInside;
}
+
+ void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override;
+
+ void update_memory_buffer(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs) override;
};
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_EllipseMaskOperation.cc b/source/blender/compositor/operations/COM_EllipseMaskOperation.cc
index 5a4503fecec..eb1fd98a590 100644
--- a/source/blender/compositor/operations/COM_EllipseMaskOperation.cc
+++ b/source/blender/compositor/operations/COM_EllipseMaskOperation.cc
@@ -20,6 +20,8 @@
#include "BLI_math.h"
#include "DNA_node_types.h"
+#include <functional>
+
namespace blender::compositor {
EllipseMaskOperation::EllipseMaskOperation()
@@ -114,6 +116,77 @@ void EllipseMaskOperation::executePixelSampled(float output[4],
}
}
+void EllipseMaskOperation::update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs)
+{
+ MaskFunc mask_func;
+ switch (m_maskType) {
+ case CMP_NODE_MASKTYPE_ADD:
+ mask_func = [](const bool is_inside, const float *mask, const float *value) {
+ return is_inside ? MAX2(mask[0], value[0]) : mask[0];
+ };
+ break;
+ case CMP_NODE_MASKTYPE_SUBTRACT:
+ mask_func = [](const bool is_inside, const float *mask, const float *value) {
+ return is_inside ? CLAMPIS(mask[0] - value[0], 0, 1) : mask[0];
+ };
+ break;
+ case CMP_NODE_MASKTYPE_MULTIPLY:
+ mask_func = [](const bool is_inside, const float *mask, const float *value) {
+ return is_inside ? mask[0] * value[0] : 0;
+ };
+ break;
+ case CMP_NODE_MASKTYPE_NOT:
+ mask_func = [](const bool is_inside, const float *mask, const float *value) {
+ if (is_inside) {
+ return mask[0] > 0.0f ? 0.0f : value[0];
+ }
+ return mask[0];
+ };
+ break;
+ }
+ apply_mask(output, area, inputs, mask_func);
+}
+
+void EllipseMaskOperation::apply_mask(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs,
+ MaskFunc mask_func)
+{
+ const MemoryBuffer *input_mask = inputs[0];
+ const MemoryBuffer *input_value = inputs[1];
+ const float op_w = this->getWidth();
+ const float op_h = this->getHeight();
+ const float half_w = this->m_data->width / 2.0f;
+ const float half_h = this->m_data->height / 2.0f;
+ const float tx = half_w * half_w;
+ const float ty = half_h * half_h;
+ for (const int y : YRange(area)) {
+ const float op_ry = y / op_h;
+ const float dy = (op_ry - this->m_data->y) / m_aspectRatio;
+ float *out = output->get_elem(area.xmin, y);
+ const float *mask = input_mask->get_elem(area.xmin, y);
+ const float *value = input_value->get_elem(area.xmin, y);
+ for (const int x : XRange(area)) {
+ const float op_rx = x / op_w;
+ const float dx = op_rx - this->m_data->x;
+ const float rx = this->m_data->x + (m_cosine * dx + m_sine * dy);
+ const float ry = this->m_data->y + (-m_sine * dx + m_cosine * dy);
+ float sx = rx - this->m_data->x;
+ sx *= sx;
+ float sy = ry - this->m_data->y;
+ sy *= sy;
+ const bool inside = ((sx / tx) + (sy / ty)) < 1.0f;
+ out[0] = mask_func(inside, mask, value);
+
+ mask += input_mask->elem_stride;
+ value += input_value->elem_stride;
+ out += output->elem_stride;
+ }
+ }
+}
+
void EllipseMaskOperation::deinitExecution()
{
this->m_inputMask = nullptr;
diff --git a/source/blender/compositor/operations/COM_EllipseMaskOperation.h b/source/blender/compositor/operations/COM_EllipseMaskOperation.h
index 64afe0145cf..fba3f979d26 100644
--- a/source/blender/compositor/operations/COM_EllipseMaskOperation.h
+++ b/source/blender/compositor/operations/COM_EllipseMaskOperation.h
@@ -18,12 +18,14 @@
#pragma once
-#include "COM_NodeOperation.h"
+#include "COM_MultiThreadedOperation.h"
namespace blender::compositor {
-class EllipseMaskOperation : public NodeOperation {
+class EllipseMaskOperation : public MultiThreadedOperation {
private:
+ using MaskFunc = std::function<float(bool is_inside, const float *mask, const float *value)>;
+
/**
* Cached reference to the inputProgram
*/
@@ -64,6 +66,16 @@ class EllipseMaskOperation : public NodeOperation {
{
this->m_maskType = maskType;
}
+
+ void update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs) override;
+
+ private:
+ void apply_mask(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs,
+ MaskFunc mask_func);
};
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_MixOperation.cc b/source/blender/compositor/operations/COM_MixOperation.cc
index 58fa09fa2a8..77ecbf60356 100644
--- a/source/blender/compositor/operations/COM_MixOperation.cc
+++ b/source/blender/compositor/operations/COM_MixOperation.cc
@@ -35,6 +35,7 @@ MixBaseOperation::MixBaseOperation()
this->m_inputColor2Operation = nullptr;
this->setUseValueAlphaMultiply(false);
this->setUseClamp(false);
+ flags.can_be_constant = true;
}
void MixBaseOperation::initExecution()
@@ -97,6 +98,45 @@ void MixBaseOperation::deinitExecution()
this->m_inputColor2Operation = nullptr;
}
+void MixBaseOperation::update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs)
+{
+ const MemoryBuffer *input_value = inputs[0];
+ const MemoryBuffer *input_color1 = inputs[1];
+ const MemoryBuffer *input_color2 = inputs[2];
+ const int width = BLI_rcti_size_x(&area);
+ PixelCursor p;
+ p.out_stride = output->elem_stride;
+ p.value_stride = input_value->elem_stride;
+ p.color1_stride = input_color1->elem_stride;
+ p.color2_stride = input_color2->elem_stride;
+ for (const int y : YRange(area)) {
+ p.out = output->get_elem(area.xmin, y);
+ p.row_end = p.out + width * output->elem_stride;
+ p.value = input_value->get_elem(area.xmin, y);
+ p.color1 = input_color1->get_elem(area.xmin, y);
+ p.color2 = input_color2->get_elem(area.xmin, y);
+ update_memory_buffer_row(p);
+ }
+}
+
+void MixBaseOperation::update_memory_buffer_row(PixelCursor &p)
+{
+ while (p.out < p.row_end) {
+ float value = p.value[0];
+ if (this->useValueAlphaMultiply()) {
+ value *= p.color2[3];
+ }
+ const float value_m = 1.0f - value;
+ p.out[0] = value_m * p.color1[0] + value * p.color2[0];
+ p.out[1] = value_m * p.color1[1] + value * p.color2[1];
+ p.out[2] = value_m * p.color1[2] + value * p.color2[2];
+ p.out[3] = p.color1[3];
+ p.next();
+ }
+}
+
/* ******** Mix Add Operation ******** */
void MixAddOperation::executePixelSampled(float output[4], float x, float y, PixelSampler sampler)
@@ -121,6 +161,23 @@ void MixAddOperation::executePixelSampled(float output[4], float x, float y, Pix
clampIfNeeded(output);
}
+void MixAddOperation::update_memory_buffer_row(PixelCursor &p)
+{
+ while (p.out < p.row_end) {
+ float value = p.value[0];
+ if (this->useValueAlphaMultiply()) {
+ value *= p.color2[3];
+ }
+ p.out[0] = p.color1[0] + value * p.color2[0];
+ p.out[1] = p.color1[1] + value * p.color2[1];
+ p.out[2] = p.color1[2] + value * p.color2[2];
+ p.out[3] = p.color1[3];
+
+ clampIfNeeded(p.out);
+ p.next();
+ }
+}
+
/* ******** Mix Blend Operation ******** */
void MixBlendOperation::executePixelSampled(float output[4],
@@ -150,6 +207,24 @@ void MixBlendOperation::executePixelSampled(float output[4],
clampIfNeeded(output);
}
+void MixBlendOperation::update_memory_buffer_row(PixelCursor &p)
+{
+ while (p.out < p.row_end) {
+ float value = p.value[0];
+ if (this->useValueAlphaMultiply()) {
+ value *= p.color2[3];
+ }
+ float value_m = 1.0f - value;
+ p.out[0] = value_m * p.color1[0] + value * p.color2[0];
+ p.out[1] = value_m * p.color1[1] + value * p.color2[1];
+ p.out[2] = value_m * p.color1[2] + value * p.color2[2];
+ p.out[3] = p.color1[3];
+
+ clampIfNeeded(p.out);
+ p.next();
+ }
+}
+
/* ******** Mix Burn Operation ******** */
void MixColorBurnOperation::executePixelSampled(float output[4],
@@ -228,6 +303,48 @@ void MixColorBurnOperation::executePixelSampled(float output[4],
clampIfNeeded(output);
}
+void MixColorBurnOperation::update_memory_buffer_row(PixelCursor &p)
+{
+ while (p.out < p.row_end) {
+ float value = p.value[0];
+ if (this->useValueAlphaMultiply()) {
+ value *= p.color2[3];
+ }
+ const float value_m = 1.0f - value;
+
+ float tmp = value_m + value * p.color2[0];
+ if (tmp <= 0.0f) {
+ p.out[0] = 0.0f;
+ }
+ else {
+ tmp = 1.0f - (1.0f - p.color1[0]) / tmp;
+ p.out[0] = CLAMPIS(tmp, 0.0f, 1.0f);
+ }
+
+ tmp = value_m + value * p.color2[1];
+ if (tmp <= 0.0f) {
+ p.out[1] = 0.0f;
+ }
+ else {
+ tmp = 1.0f - (1.0f - p.color1[1]) / tmp;
+ p.out[1] = CLAMPIS(tmp, 0.0f, 1.0f);
+ }
+
+ tmp = value_m + value * p.color2[2];
+ if (tmp <= 0.0f) {
+ p.out[2] = 0.0f;
+ }
+ else {
+ tmp = 1.0f - (1.0f - p.color1[2]) / tmp;
+ p.out[2] = CLAMPIS(tmp, 0.0f, 1.0f);
+ }
+ p.out[3] = p.color1[3];
+
+ clampIfNeeded(p.out);
+ p.next();
+ }
+}
+
/* ******** Mix Color Operation ******** */
void MixColorOperation::executePixelSampled(float output[4],
@@ -268,6 +385,36 @@ void MixColorOperation::executePixelSampled(float output[4],
clampIfNeeded(output);
}
+void MixColorOperation::update_memory_buffer_row(PixelCursor &p)
+{
+ while (p.out < p.row_end) {
+ float value = p.value[0];
+ if (this->useValueAlphaMultiply()) {
+ value *= p.color2[3];
+ }
+ const float value_m = 1.0f - value;
+
+ float colH, colS, colV;
+ rgb_to_hsv(p.color2[0], p.color2[1], p.color2[2], &colH, &colS, &colV);
+ if (colS != 0.0f) {
+ float rH, rS, rV;
+ float tmpr, tmpg, tmpb;
+ rgb_to_hsv(p.color1[0], p.color1[1], p.color1[2], &rH, &rS, &rV);
+ hsv_to_rgb(colH, colS, rV, &tmpr, &tmpg, &tmpb);
+ p.out[0] = (value_m * p.color1[0]) + (value * tmpr);
+ p.out[1] = (value_m * p.color1[1]) + (value * tmpg);
+ p.out[2] = (value_m * p.color1[2]) + (value * tmpb);
+ }
+ else {
+ copy_v3_v3(p.out, p.color1);
+ }
+ p.out[3] = p.color1[3];
+
+ clampIfNeeded(p.out);
+ p.next();
+ }
+}
+
/* ******** Mix Darken Operation ******** */
void MixDarkenOperation::executePixelSampled(float output[4],
@@ -296,6 +443,24 @@ void MixDarkenOperation::executePixelSampled(float output[4],
clampIfNeeded(output);
}
+void MixDarkenOperation::update_memory_buffer_row(PixelCursor &p)
+{
+ while (p.out < p.row_end) {
+ float value = p.value[0];
+ if (this->useValueAlphaMultiply()) {
+ value *= p.color2[3];
+ }
+ float value_m = 1.0f - value;
+ p.out[0] = min_ff(p.color1[0], p.color2[0]) * value + p.color1[0] * value_m;
+ p.out[1] = min_ff(p.color1[1], p.color2[1]) * value + p.color1[1] * value_m;
+ p.out[2] = min_ff(p.color1[2], p.color2[2]) * value + p.color1[2] * value_m;
+ p.out[3] = p.color1[3];
+
+ clampIfNeeded(p.out);
+ p.next();
+ }
+}
+
/* ******** Mix Difference Operation ******** */
void MixDifferenceOperation::executePixelSampled(float output[4],
@@ -324,6 +489,24 @@ void MixDifferenceOperation::executePixelSampled(float output[4],
clampIfNeeded(output);
}
+void MixDifferenceOperation::update_memory_buffer_row(PixelCursor &p)
+{
+ while (p.out < p.row_end) {
+ float value = p.value[0];
+ if (this->useValueAlphaMultiply()) {
+ value *= p.color2[3];
+ }
+ const float value_m = 1.0f - value;
+ p.out[0] = value_m * p.color1[0] + value * fabsf(p.color1[0] - p.color2[0]);
+ p.out[1] = value_m * p.color1[1] + value * fabsf(p.color1[1] - p.color2[1]);
+ p.out[2] = value_m * p.color1[2] + value * fabsf(p.color1[2] - p.color2[2]);
+ p.out[3] = p.color1[3];
+
+ clampIfNeeded(p.out);
+ p.next();
+ }
+}
+
/* ******** Mix Difference Operation ******** */
void MixDivideOperation::executePixelSampled(float output[4],
@@ -369,6 +552,41 @@ void MixDivideOperation::executePixelSampled(float output[4],
clampIfNeeded(output);
}
+void MixDivideOperation::update_memory_buffer_row(PixelCursor &p)
+{
+ while (p.out < p.row_end) {
+ float value = p.value[0];
+ if (this->useValueAlphaMultiply()) {
+ value *= p.color2[3];
+ }
+ const float value_m = 1.0f - value;
+
+ if (p.color2[0] != 0.0f) {
+ p.out[0] = value_m * (p.color1[0]) + value * (p.color1[0]) / p.color2[0];
+ }
+ else {
+ p.out[0] = 0.0f;
+ }
+ if (p.color2[1] != 0.0f) {
+ p.out[1] = value_m * (p.color1[1]) + value * (p.color1[1]) / p.color2[1];
+ }
+ else {
+ p.out[1] = 0.0f;
+ }
+ if (p.color2[2] != 0.0f) {
+ p.out[2] = value_m * (p.color1[2]) + value * (p.color1[2]) / p.color2[2];
+ }
+ else {
+ p.out[2] = 0.0f;
+ }
+
+ p.out[3] = p.color1[3];
+
+ clampIfNeeded(p.out);
+ p.next();
+ }
+}
+
/* ******** Mix Dodge Operation ******** */
void MixDodgeOperation::executePixelSampled(float output[4],
@@ -452,6 +670,64 @@ void MixDodgeOperation::executePixelSampled(float output[4],
clampIfNeeded(output);
}
+void MixDodgeOperation::update_memory_buffer_row(PixelCursor &p)
+{
+ while (p.out < p.row_end) {
+ float value = p.value[0];
+ if (this->useValueAlphaMultiply()) {
+ value *= p.color2[3];
+ }
+
+ float tmp;
+ if (p.color1[0] != 0.0f) {
+ tmp = 1.0f - value * p.color2[0];
+ if (tmp <= 0.0f) {
+ p.out[0] = 1.0f;
+ }
+ else {
+ p.out[0] = p.color1[0] / tmp;
+ CLAMP_MAX(p.out[0], 1.0f);
+ }
+ }
+ else {
+ p.out[0] = 0.0f;
+ }
+
+ if (p.color1[1] != 0.0f) {
+ tmp = 1.0f - value * p.color2[1];
+ if (tmp <= 0.0f) {
+ p.out[1] = 1.0f;
+ }
+ else {
+ p.out[1] = p.color1[1] / tmp;
+ CLAMP_MAX(p.out[1], 1.0f);
+ }
+ }
+ else {
+ p.out[1] = 0.0f;
+ }
+
+ if (p.color1[2] != 0.0f) {
+ tmp = 1.0f - value * p.color2[2];
+ if (tmp <= 0.0f) {
+ p.out[2] = 1.0f;
+ }
+ else {
+ p.out[2] = p.color1[2] / tmp;
+ CLAMP_MAX(p.out[2], 1.0f);
+ }
+ }
+ else {
+ p.out[2] = 0.0f;
+ }
+
+ p.out[3] = p.color1[3];
+
+ clampIfNeeded(p.out);
+ p.next();
+ }
+}
+
/* ******** Mix Glare Operation ******** */
void MixGlareOperation::executePixelSampled(float output[4],
@@ -487,6 +763,33 @@ void MixGlareOperation::executePixelSampled(float output[4],
clampIfNeeded(output);
}
+void MixGlareOperation::update_memory_buffer_row(PixelCursor &p)
+{
+ while (p.out < p.row_end) {
+ const float value = p.value[0];
+ /* Linear interpolation between 3 cases:
+ * value=-1:output=input value=0:output=input+glare value=1:output=glare
+ */
+ float input_weight;
+ float glare_weight;
+ if (value < 0.0f) {
+ input_weight = 1.0f;
+ glare_weight = 1.0f + value;
+ }
+ else {
+ input_weight = 1.0f - value;
+ glare_weight = 1.0f;
+ }
+ p.out[0] = input_weight * MAX2(p.color1[0], 0.0f) + glare_weight * p.color2[0];
+ p.out[1] = input_weight * MAX2(p.color1[1], 0.0f) + glare_weight * p.color2[1];
+ p.out[2] = input_weight * MAX2(p.color1[2], 0.0f) + glare_weight * p.color2[2];
+ p.out[3] = p.color1[3];
+
+ clampIfNeeded(p.out);
+ p.next();
+ }
+}
+
/* ******** Mix Hue Operation ******** */
void MixHueOperation::executePixelSampled(float output[4], float x, float y, PixelSampler sampler)
@@ -524,6 +827,36 @@ void MixHueOperation::executePixelSampled(float output[4], float x, float y, Pix
clampIfNeeded(output);
}
+void MixHueOperation::update_memory_buffer_row(PixelCursor &p)
+{
+ while (p.out < p.row_end) {
+ float value = p.value[0];
+ if (this->useValueAlphaMultiply()) {
+ value *= p.color2[3];
+ }
+ const float value_m = 1.0f - value;
+
+ float colH, colS, colV;
+ rgb_to_hsv(p.color2[0], p.color2[1], p.color2[2], &colH, &colS, &colV);
+ if (colS != 0.0f) {
+ float rH, rS, rV;
+ float tmpr, tmpg, tmpb;
+ rgb_to_hsv(p.color1[0], p.color1[1], p.color1[2], &rH, &rS, &rV);
+ hsv_to_rgb(colH, rS, rV, &tmpr, &tmpg, &tmpb);
+ p.out[0] = value_m * p.color1[0] + value * tmpr;
+ p.out[1] = value_m * p.color1[1] + value * tmpg;
+ p.out[2] = value_m * p.color1[2] + value * tmpb;
+ }
+ else {
+ copy_v3_v3(p.out, p.color1);
+ }
+ p.out[3] = p.color1[3];
+
+ clampIfNeeded(p.out);
+ p.next();
+ }
+}
+
/* ******** Mix Lighten Operation ******** */
void MixLightenOperation::executePixelSampled(float output[4],
@@ -570,6 +903,30 @@ void MixLightenOperation::executePixelSampled(float output[4],
clampIfNeeded(output);
}
+void MixLightenOperation::update_memory_buffer_row(PixelCursor &p)
+{
+ while (p.out < p.row_end) {
+ float value = p.value[0];
+ if (this->useValueAlphaMultiply()) {
+ value *= p.color2[3];
+ }
+
+ float tmp = value * p.color2[0];
+ p.out[0] = MAX2(tmp, p.color1[0]);
+
+ tmp = value * p.color2[1];
+ p.out[1] = MAX2(tmp, p.color1[1]);
+
+ tmp = value * p.color2[2];
+ p.out[2] = MAX2(tmp, p.color1[2]);
+
+ p.out[3] = p.color1[3];
+
+ clampIfNeeded(p.out);
+ p.next();
+ }
+}
+
/* ******** Mix Linear Light Operation ******** */
void MixLinearLightOperation::executePixelSampled(float output[4],
@@ -613,6 +970,39 @@ void MixLinearLightOperation::executePixelSampled(float output[4],
clampIfNeeded(output);
}
+void MixLinearLightOperation::update_memory_buffer_row(PixelCursor &p)
+{
+ while (p.out < p.row_end) {
+ float value = p.value[0];
+ if (this->useValueAlphaMultiply()) {
+ value *= p.color2[3];
+ }
+ if (p.color2[0] > 0.5f) {
+ p.out[0] = p.color1[0] + value * (2.0f * (p.color2[0] - 0.5f));
+ }
+ else {
+ p.out[0] = p.color1[0] + value * (2.0f * (p.color2[0]) - 1.0f);
+ }
+ if (p.color2[1] > 0.5f) {
+ p.out[1] = p.color1[1] + value * (2.0f * (p.color2[1] - 0.5f));
+ }
+ else {
+ p.out[1] = p.color1[1] + value * (2.0f * (p.color2[1]) - 1.0f);
+ }
+ if (p.color2[2] > 0.5f) {
+ p.out[2] = p.color1[2] + value * (2.0f * (p.color2[2] - 0.5f));
+ }
+ else {
+ p.out[2] = p.color1[2] + value * (2.0f * (p.color2[2]) - 1.0f);
+ }
+
+ p.out[3] = p.color1[3];
+
+ clampIfNeeded(p.out);
+ p.next();
+ }
+}
+
/* ******** Mix Multiply Operation ******** */
void MixMultiplyOperation::executePixelSampled(float output[4],
@@ -641,6 +1031,25 @@ void MixMultiplyOperation::executePixelSampled(float output[4],
clampIfNeeded(output);
}
+void MixMultiplyOperation::update_memory_buffer_row(PixelCursor &p)
+{
+ while (p.out < p.row_end) {
+ float value = p.value[0];
+ if (this->useValueAlphaMultiply()) {
+ value *= p.color2[3];
+ }
+ const float value_m = 1.0f - value;
+ p.out[0] = p.color1[0] * (value_m + value * p.color2[0]);
+ p.out[1] = p.color1[1] * (value_m + value * p.color2[1]);
+ p.out[2] = p.color1[2] * (value_m + value * p.color2[2]);
+
+ p.out[3] = p.color1[3];
+
+ clampIfNeeded(p.out);
+ p.next();
+ }
+}
+
/* ******** Mix Overlay Operation ******** */
void MixOverlayOperation::executePixelSampled(float output[4],
@@ -686,6 +1095,40 @@ void MixOverlayOperation::executePixelSampled(float output[4],
clampIfNeeded(output);
}
+void MixOverlayOperation::update_memory_buffer_row(PixelCursor &p)
+{
+ while (p.out < p.row_end) {
+ float value = p.value[0];
+ if (this->useValueAlphaMultiply()) {
+ value *= p.color2[3];
+ }
+ const float value_m = 1.0f - value;
+ if (p.color1[0] < 0.5f) {
+ p.out[0] = p.color1[0] * (value_m + 2.0f * value * p.color2[0]);
+ }
+ else {
+ p.out[0] = 1.0f - (value_m + 2.0f * value * (1.0f - p.color2[0])) * (1.0f - p.color1[0]);
+ }
+ if (p.color1[1] < 0.5f) {
+ p.out[1] = p.color1[1] * (value_m + 2.0f * value * p.color2[1]);
+ }
+ else {
+ p.out[1] = 1.0f - (value_m + 2.0f * value * (1.0f - p.color2[1])) * (1.0f - p.color1[1]);
+ }
+ if (p.color1[2] < 0.5f) {
+ p.out[2] = p.color1[2] * (value_m + 2.0f * value * p.color2[2]);
+ }
+ else {
+ p.out[2] = 1.0f - (value_m + 2.0f * value * (1.0f - p.color2[2])) * (1.0f - p.color1[2]);
+ }
+
+ p.out[3] = p.color1[3];
+
+ clampIfNeeded(p.out);
+ p.next();
+ }
+}
+
/* ******** Mix Saturation Operation ******** */
void MixSaturationOperation::executePixelSampled(float output[4],
@@ -723,6 +1166,33 @@ void MixSaturationOperation::executePixelSampled(float output[4],
clampIfNeeded(output);
}
+void MixSaturationOperation::update_memory_buffer_row(PixelCursor &p)
+{
+ while (p.out < p.row_end) {
+ float value = p.value[0];
+ if (this->useValueAlphaMultiply()) {
+ value *= p.color2[3];
+ }
+ const float value_m = 1.0f - value;
+
+ float rH, rS, rV;
+ rgb_to_hsv(p.color1[0], p.color1[1], p.color1[2], &rH, &rS, &rV);
+ if (rS != 0.0f) {
+ float colH, colS, colV;
+ rgb_to_hsv(p.color2[0], p.color2[1], p.color2[2], &colH, &colS, &colV);
+ hsv_to_rgb(rH, (value_m * rS + value * colS), rV, &p.out[0], &p.out[1], &p.out[2]);
+ }
+ else {
+ copy_v3_v3(p.out, p.color1);
+ }
+
+ p.out[3] = p.color1[3];
+
+ clampIfNeeded(p.out);
+ p.next();
+ }
+}
+
/* ******** Mix Screen Operation ******** */
void MixScreenOperation::executePixelSampled(float output[4],
@@ -752,6 +1222,25 @@ void MixScreenOperation::executePixelSampled(float output[4],
clampIfNeeded(output);
}
+void MixScreenOperation::update_memory_buffer_row(PixelCursor &p)
+{
+ while (p.out < p.row_end) {
+ float value = p.value[0];
+ if (this->useValueAlphaMultiply()) {
+ value *= p.color2[3];
+ }
+ const float value_m = 1.0f - value;
+
+ p.out[0] = 1.0f - (value_m + value * (1.0f - p.color2[0])) * (1.0f - p.color1[0]);
+ p.out[1] = 1.0f - (value_m + value * (1.0f - p.color2[1])) * (1.0f - p.color1[1]);
+ p.out[2] = 1.0f - (value_m + value * (1.0f - p.color2[2])) * (1.0f - p.color1[2]);
+ p.out[3] = p.color1[3];
+
+ clampIfNeeded(p.out);
+ p.next();
+ }
+}
+
/* ******** Mix Soft Light Operation ******** */
void MixSoftLightOperation::executePixelSampled(float output[4],
@@ -793,6 +1282,34 @@ void MixSoftLightOperation::executePixelSampled(float output[4],
clampIfNeeded(output);
}
+void MixSoftLightOperation::update_memory_buffer_row(PixelCursor &p)
+{
+ while (p.out < p.row_end) {
+ float value = p.value[0];
+ if (this->useValueAlphaMultiply()) {
+ value *= p.color2[3];
+ }
+ const float value_m = 1.0f - value;
+ float scr, scg, scb;
+
+ /* First calculate non-fac based Screen mix. */
+ scr = 1.0f - (1.0f - p.color2[0]) * (1.0f - p.color1[0]);
+ scg = 1.0f - (1.0f - p.color2[1]) * (1.0f - p.color1[1]);
+ scb = 1.0f - (1.0f - p.color2[2]) * (1.0f - p.color1[2]);
+
+ p.out[0] = value_m * p.color1[0] +
+ value * ((1.0f - p.color1[0]) * p.color2[0] * p.color1[0] + p.color1[0] * scr);
+ p.out[1] = value_m * p.color1[1] +
+ value * ((1.0f - p.color1[1]) * p.color2[1] * p.color1[1] + p.color1[1] * scg);
+ p.out[2] = value_m * p.color1[2] +
+ value * ((1.0f - p.color1[2]) * p.color2[2] * p.color1[2] + p.color1[2] * scb);
+ p.out[3] = p.color1[3];
+
+ clampIfNeeded(p.out);
+ p.next();
+ }
+}
+
/* ******** Mix Subtract Operation ******** */
void MixSubtractOperation::executePixelSampled(float output[4],
@@ -820,6 +1337,23 @@ void MixSubtractOperation::executePixelSampled(float output[4],
clampIfNeeded(output);
}
+void MixSubtractOperation::update_memory_buffer_row(PixelCursor &p)
+{
+ while (p.out < p.row_end) {
+ float value = p.value[0];
+ if (this->useValueAlphaMultiply()) {
+ value *= p.color2[3];
+ }
+ p.out[0] = p.color1[0] - value * p.color2[0];
+ p.out[1] = p.color1[1] - value * p.color2[1];
+ p.out[2] = p.color1[2] - value * p.color2[2];
+ p.out[3] = p.color1[3];
+
+ clampIfNeeded(p.out);
+ p.next();
+ }
+}
+
/* ******** Mix Value Operation ******** */
void MixValueOperation::executePixelSampled(float output[4],
@@ -851,4 +1385,25 @@ void MixValueOperation::executePixelSampled(float output[4],
clampIfNeeded(output);
}
+void MixValueOperation::update_memory_buffer_row(PixelCursor &p)
+{
+ while (p.out < p.row_end) {
+ float value = p.value[0];
+ if (this->useValueAlphaMultiply()) {
+ value *= p.color2[3];
+ }
+ float value_m = 1.0f - value;
+
+ float rH, rS, rV;
+ float colH, colS, colV;
+ rgb_to_hsv(p.color1[0], p.color1[1], p.color1[2], &rH, &rS, &rV);
+ rgb_to_hsv(p.color2[0], p.color2[1], p.color2[2], &colH, &colS, &colV);
+ hsv_to_rgb(rH, rS, (value_m * rV + value * colV), &p.out[0], &p.out[1], &p.out[2]);
+ p.out[3] = p.color1[3];
+
+ clampIfNeeded(p.out);
+ p.next();
+ }
+}
+
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_MixOperation.h b/source/blender/compositor/operations/COM_MixOperation.h
index 6c241bc5762..7ef9d78d58f 100644
--- a/source/blender/compositor/operations/COM_MixOperation.h
+++ b/source/blender/compositor/operations/COM_MixOperation.h
@@ -18,7 +18,7 @@
#pragma once
-#include "COM_NodeOperation.h"
+#include "COM_MultiThreadedOperation.h"
namespace blender::compositor {
@@ -27,8 +27,29 @@ namespace blender::compositor {
* it assumes we are in sRGB color space.
*/
-class MixBaseOperation : public NodeOperation {
+class MixBaseOperation : public MultiThreadedOperation {
protected:
+ struct PixelCursor {
+ float *out;
+ const float *row_end;
+ const float *value;
+ const float *color1;
+ const float *color2;
+ int out_stride;
+ int value_stride;
+ int color1_stride;
+ int color2_stride;
+
+ void next()
+ {
+ BLI_assert(out < row_end);
+ out += out_stride;
+ value += value_stride;
+ color1 += color1_stride;
+ color2 += color2_stride;
+ }
+ };
+
/**
* Prefetched reference to the inputProgram
*/
@@ -81,101 +102,165 @@ class MixBaseOperation : public NodeOperation {
{
this->m_useClamp = value;
}
+
+ void update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs) final;
+
+ protected:
+ virtual void update_memory_buffer_row(PixelCursor &p);
};
class MixAddOperation : public MixBaseOperation {
public:
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
+
+ protected:
+ void update_memory_buffer_row(PixelCursor &p) override;
};
class MixBlendOperation : public MixBaseOperation {
public:
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
+
+ protected:
+ void update_memory_buffer_row(PixelCursor &p) override;
};
class MixColorBurnOperation : public MixBaseOperation {
public:
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
+
+ protected:
+ void update_memory_buffer_row(PixelCursor &p) override;
};
class MixColorOperation : public MixBaseOperation {
public:
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
+
+ protected:
+ void update_memory_buffer_row(PixelCursor &p) override;
};
class MixDarkenOperation : public MixBaseOperation {
public:
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
+
+ protected:
+ void update_memory_buffer_row(PixelCursor &p) override;
};
class MixDifferenceOperation : public MixBaseOperation {
public:
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
+
+ protected:
+ void update_memory_buffer_row(PixelCursor &p) override;
};
class MixDivideOperation : public MixBaseOperation {
public:
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
+
+ protected:
+ void update_memory_buffer_row(PixelCursor &p) override;
};
class MixDodgeOperation : public MixBaseOperation {
public:
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
+
+ protected:
+ void update_memory_buffer_row(PixelCursor &p) override;
};
class MixGlareOperation : public MixBaseOperation {
public:
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
+
+ protected:
+ void update_memory_buffer_row(PixelCursor &p) override;
};
class MixHueOperation : public MixBaseOperation {
public:
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
+
+ protected:
+ void update_memory_buffer_row(PixelCursor &p) override;
};
class MixLightenOperation : public MixBaseOperation {
public:
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
+
+ protected:
+ void update_memory_buffer_row(PixelCursor &p) override;
};
class MixLinearLightOperation : public MixBaseOperation {
public:
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
+
+ protected:
+ void update_memory_buffer_row(PixelCursor &p) override;
};
class MixMultiplyOperation : public MixBaseOperation {
public:
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
+
+ protected:
+ void update_memory_buffer_row(PixelCursor &p) override;
};
class MixOverlayOperation : public MixBaseOperation {
public:
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
+
+ protected:
+ void update_memory_buffer_row(PixelCursor &p) override;
};
class MixSaturationOperation : public MixBaseOperation {
public:
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
+
+ protected:
+ void update_memory_buffer_row(PixelCursor &p) override;
};
class MixScreenOperation : public MixBaseOperation {
public:
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
+
+ protected:
+ void update_memory_buffer_row(PixelCursor &p) override;
};
class MixSoftLightOperation : public MixBaseOperation {
public:
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
+
+ protected:
+ void update_memory_buffer_row(PixelCursor &p) override;
};
class MixSubtractOperation : public MixBaseOperation {
public:
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
+
+ protected:
+ void update_memory_buffer_row(PixelCursor &p) override;
};
class MixValueOperation : public MixBaseOperation {
public:
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
+
+ protected:
+ void update_memory_buffer_row(PixelCursor &p) override;
};
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_SMAAOperation.cc b/source/blender/compositor/operations/COM_SMAAOperation.cc
index 3c753591ced..b078d85372d 100644
--- a/source/blender/compositor/operations/COM_SMAAOperation.cc
+++ b/source/blender/compositor/operations/COM_SMAAOperation.cc
@@ -19,9 +19,9 @@
*/
#include "COM_SMAAOperation.h"
+#include "BKE_node.h"
#include "BLI_math.h"
#include "COM_SMAAAreaTexture.h"
-#include "BKE_node.h"
extern "C" {
#include "IMB_colormanagement.h"
diff --git a/source/blender/compositor/operations/COM_ViewerOperation.cc b/source/blender/compositor/operations/COM_ViewerOperation.cc
index 860f56e23fa..e47396e14a1 100644
--- a/source/blender/compositor/operations/COM_ViewerOperation.cc
+++ b/source/blender/compositor/operations/COM_ViewerOperation.cc
@@ -191,10 +191,11 @@ void ViewerOperation::initImage()
BLI_thread_unlock(LOCK_DRAW_IMAGE);
}
-void ViewerOperation::updateImage(rcti *rect)
+void ViewerOperation::updateImage(const rcti *rect)
{
+ float *buffer = m_outputBuffer;
IMB_partial_display_buffer_update(this->m_ibuf,
- this->m_outputBuffer,
+ buffer,
nullptr,
getWidth(),
0,
@@ -218,4 +219,31 @@ eCompositorPriority ViewerOperation::getRenderPriority() const
return eCompositorPriority::Low;
}
+void ViewerOperation::update_memory_buffer_partial(MemoryBuffer *UNUSED(output),
+ const rcti &area,
+ Span<MemoryBuffer *> inputs)
+{
+ if (!m_outputBuffer) {
+ return;
+ }
+
+ MemoryBuffer output_buffer(
+ m_outputBuffer, COM_DATA_TYPE_COLOR_CHANNELS, getWidth(), getHeight());
+ const MemoryBuffer *input_image = inputs[0];
+ output_buffer.copy_from(input_image, area);
+ if (this->m_useAlphaInput) {
+ const MemoryBuffer *input_alpha = inputs[1];
+ output_buffer.copy_from(input_alpha, area, 0, COM_DATA_TYPE_VALUE_CHANNELS, 3);
+ }
+
+ if (m_depthBuffer) {
+ MemoryBuffer depth_buffer(
+ m_depthBuffer, COM_DATA_TYPE_VALUE_CHANNELS, getWidth(), getHeight());
+ const MemoryBuffer *input_depth = inputs[2];
+ depth_buffer.copy_from(input_depth, area);
+ }
+
+ updateImage(&area);
+}
+
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_ViewerOperation.h b/source/blender/compositor/operations/COM_ViewerOperation.h
index c0f13ff79fc..ed05a7a282d 100644
--- a/source/blender/compositor/operations/COM_ViewerOperation.h
+++ b/source/blender/compositor/operations/COM_ViewerOperation.h
@@ -20,15 +20,17 @@
#include "BKE_global.h"
#include "BLI_rect.h"
-#include "COM_NodeOperation.h"
+#include "COM_MultiThreadedOperation.h"
#include "DNA_image_types.h"
namespace blender::compositor {
-class ViewerOperation : public NodeOperation {
+class ViewerOperation : public MultiThreadedOperation {
private:
+ /* TODO(manzanilla): To be removed together with tiled implementation. */
float *m_outputBuffer;
float *m_depthBuffer;
+
Image *m_image;
ImageUser *m_imageUser;
bool m_active;
@@ -125,8 +127,12 @@ class ViewerOperation : public NodeOperation {
this->m_displaySettings = displaySettings;
}
+ void update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs) override;
+
private:
- void updateImage(rcti *rect);
+ void updateImage(const rcti *rect);
void initImage();
};