Compare commits
169 Commits
tmp-eevee-
...
compositor
Author | SHA1 | Date | |
---|---|---|---|
1662bf8ebf | |||
e8f41f1b93 | |||
c8897efa53 | |||
9a0ed67e25 | |||
efafe7474d | |||
31fc76fa5b | |||
f669932c93 | |||
7e840321c7 | |||
c03814f945 | |||
fe53c04c51 | |||
5a13cec6b9 | |||
0b87a846a8 | |||
802abbb0da | |||
e30d5d183c | |||
c97795b43a | |||
ddc2d379d4 | |||
1be813c8e5 | |||
a9ba0e67cc | |||
71eb303ad5 | |||
549df33a67 | |||
eab2443938 | |||
00632dbb2f | |||
c443b0d475 | |||
591e46c335 | |||
d1553b4bbd | |||
70cc988a90 | |||
1747c679ee | |||
eb05b26a69 | |||
561ed5a61f | |||
aed85ac4b6 | |||
bf725bbb92 | |||
ae8e9c6f48 | |||
1bf90d2b21 | |||
f76c2be114 | |||
5c1650aa05 | |||
06c8ebdc7c | |||
f4ea8f5d40 | |||
50b3766075 | |||
1a0b2ef90f | |||
868e0e3062 | |||
c17d04765e | |||
c9fad771e9 | |||
723c45a8e6 | |||
8048479416 | |||
97e667237a | |||
dd512e0d3f | |||
3305e073b4 | |||
e94326b67b | |||
c84a294c66 | |||
13590fba71 | |||
79ffaa4816 | |||
b02069ef4d | |||
70bff51e60 | |||
dbe753d1b3 | |||
9fa4d37061 | |||
d5655a8449 | |||
d017ff9ec1 | |||
28502f2239 | |||
a570ad2e1b | |||
9ef0f7ab27 | |||
f330f43f10 | |||
53d588a9d9 | |||
4908abda5f | |||
9b02754383 | |||
961d38f8fd | |||
4b4be12881 | |||
e783811305 | |||
f690cd36b5 | |||
6acbb08538 | |||
417262e8f8 | |||
4745dfadde | |||
7fe3917f83 | |||
4147e09b44 | |||
499fbfa0a6 | |||
64a1b02bda | |||
93718956fc | |||
30baa0b7ff | |||
9dcac5e859 | |||
7f6a8e4156 | |||
e09bae21e7 | |||
479c87c1d6 | |||
fd04112033 | |||
543172f2f4 | |||
01e5042cd8 | |||
456a989d2e | |||
9062b71d4f | |||
88d72a8f5c | |||
e906650807 | |||
43394dada3 | |||
bf27be8192 | |||
585a891ab2 | |||
89b5731f19 | |||
61b0580cf5 | |||
0e255b2e7a | |||
5f39310323 | |||
dfc9bbd617 | |||
f2a1a6f870 | |||
d81dd5a268 | |||
b17c490043 | |||
89e22a99e7 | |||
f00ee29407 | |||
da1260a5fc | |||
3671d16e29 | |||
157d9f6661 | |||
ce7f952e25 | |||
0642543a1b | |||
a8a957bff0 | |||
67d869cf41 | |||
4a184c28ce | |||
d0f5cb2b42 | |||
ce1dc77f32 | |||
e7838d7b81 | |||
c2ef885a2e | |||
ddf70996b2 | |||
1d33599e64 | |||
783c28918b | |||
c4975c7191 | |||
d6e038d90c | |||
64489ff903 | |||
3a1e527804 | |||
0a62ef0193 | |||
f6f1940c45 | |||
e71ec02181 | |||
76f03636cd | |||
339d87b8f0 | |||
1482e8ab03 | |||
b748b732cd | |||
7e4f53ffd9 | |||
85a38b38e9 | |||
7ddc3ab3ee | |||
502710009d | |||
a5fcacf7b6 | |||
fc6f5bc0aa | |||
acb6e36b75 | |||
0185651831 | |||
cb410346ac | |||
c033475f46 | |||
4d6dacb8dc | |||
e9b1b49e17 | |||
d2860a49f6 | |||
aa1aec663d | |||
633370c0a0 | |||
982d8b19f5 | |||
852fc4b1e9 | |||
b75e5a0677 | |||
51025854b6 | |||
8673d06783 | |||
be3e8c6b27 | |||
a3e5a06bea | |||
6025e482df | |||
af6134046e | |||
9df6486a06 | |||
a26966ccc5 | |||
7a543203db | |||
c7274c5146 | |||
80efbb94a2 | |||
569f24b543 | |||
442520ca09 | |||
9217d073b5 | |||
179901e854 | |||
08e8c3fbbf | |||
fa5391f9c5 | |||
0d96f69510 | |||
ff1fd68d79 | |||
f0d3358e9b | |||
ea623890dd | |||
4347e678fc | |||
7f6db5dde5 | |||
fff5aa5a9f |
Submodule release/datafiles/locale updated: 8a05b618f0...2cef4877ed
Submodule release/scripts/addons updated: 67f1fbca14...27fe7f3a4f
Submodule release/scripts/addons_contrib updated: ef6ef414d2...5a82baad9f
@@ -328,10 +328,14 @@ set(SRC
|
||||
operations/COM_FastGaussianBlurOperation.h
|
||||
operations/COM_GammaCorrectOperation.cc
|
||||
operations/COM_GammaCorrectOperation.h
|
||||
operations/COM_GaussianAlphaBlurBaseOperation.cc
|
||||
operations/COM_GaussianAlphaBlurBaseOperation.h
|
||||
operations/COM_GaussianAlphaXBlurOperation.cc
|
||||
operations/COM_GaussianAlphaXBlurOperation.h
|
||||
operations/COM_GaussianAlphaYBlurOperation.cc
|
||||
operations/COM_GaussianAlphaYBlurOperation.h
|
||||
operations/COM_GaussianBlurBaseOperation.cc
|
||||
operations/COM_GaussianBlurBaseOperation.h
|
||||
operations/COM_GaussianBokehBlurOperation.cc
|
||||
operations/COM_GaussianBokehBlurOperation.h
|
||||
operations/COM_GaussianXBlurOperation.cc
|
||||
@@ -515,6 +519,8 @@ set(SRC
|
||||
operations/COM_ScaleOperation.h
|
||||
operations/COM_ScreenLensDistortionOperation.cc
|
||||
operations/COM_ScreenLensDistortionOperation.h
|
||||
operations/COM_TransformOperation.cc
|
||||
operations/COM_TransformOperation.h
|
||||
operations/COM_TranslateOperation.cc
|
||||
operations/COM_TranslateOperation.h
|
||||
operations/COM_WrapOperation.cc
|
||||
|
@@ -33,6 +33,8 @@ enum class eExecutionModel {
|
||||
FullFrame
|
||||
};
|
||||
|
||||
enum class eDimension { X, Y };
|
||||
|
||||
/**
|
||||
* \brief possible data types for sockets
|
||||
* \ingroup Model
|
||||
@@ -119,6 +121,9 @@ constexpr float COM_PREVIEW_SIZE = 140.f;
|
||||
constexpr float COM_RULE_OF_THIRDS_DIVIDER = 100.0f;
|
||||
constexpr float COM_BLUR_BOKEH_PIXELS = 512;
|
||||
|
||||
constexpr rcti COM_AREA_NONE = {0, 0, 0, 0};
|
||||
constexpr rcti COM_CONSTANT_INPUT_AREA_OF_INTEREST = COM_AREA_NONE;
|
||||
|
||||
constexpr IndexRange XRange(const rcti &area)
|
||||
{
|
||||
return IndexRange(area.xmin, area.xmax - area.xmin);
|
||||
|
@@ -24,12 +24,7 @@ BufferOperation::BufferOperation(MemoryBuffer *buffer, DataType data_type)
|
||||
{
|
||||
buffer_ = buffer;
|
||||
inflated_buffer_ = nullptr;
|
||||
/* TODO: Implement a MemoryBuffer get_size() method returning a Size2d type. Shorten following
|
||||
* code to: set_resolution(buffer.get_size()) */
|
||||
unsigned int resolution[2];
|
||||
resolution[0] = buffer->getWidth();
|
||||
resolution[1] = buffer->getHeight();
|
||||
setResolution(resolution);
|
||||
set_canvas(buffer->get_rect());
|
||||
addOutputSocket(data_type);
|
||||
flags.is_constant_operation = buffer_->is_a_single_elem();
|
||||
flags.is_fullframe_operation = false;
|
||||
|
@@ -456,14 +456,16 @@ NodeOperation *COM_convert_data_type(const NodeOperationOutput &from, const Node
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void COM_convert_resolution(NodeOperationBuilder &builder,
|
||||
NodeOperationOutput *fromSocket,
|
||||
NodeOperationInput *toSocket)
|
||||
void COM_convert_canvas(NodeOperationBuilder &builder,
|
||||
NodeOperationOutput *fromSocket,
|
||||
NodeOperationInput *toSocket)
|
||||
{
|
||||
/* Data type conversions are executed before resolutions to ensure convert operations have
|
||||
* resolution. This method have to ensure same datatypes are linked for new operations. */
|
||||
BLI_assert(fromSocket->getDataType() == toSocket->getDataType());
|
||||
|
||||
ResizeMode mode = toSocket->getResizeMode();
|
||||
BLI_assert(mode != ResizeMode::None);
|
||||
|
||||
NodeOperation *toOperation = &toSocket->getOperation();
|
||||
const float toWidth = toOperation->getWidth();
|
||||
@@ -473,13 +475,12 @@ void COM_convert_resolution(NodeOperationBuilder &builder,
|
||||
const float fromHeight = fromOperation->getHeight();
|
||||
bool doCenter = false;
|
||||
bool doScale = false;
|
||||
float addX = (toWidth - fromWidth) / 2.0f;
|
||||
float addY = (toHeight - fromHeight) / 2.0f;
|
||||
float scaleX = 0;
|
||||
float scaleY = 0;
|
||||
|
||||
switch (mode) {
|
||||
case ResizeMode::None:
|
||||
case ResizeMode::Align:
|
||||
break;
|
||||
case ResizeMode::Center:
|
||||
doCenter = true;
|
||||
@@ -514,63 +515,68 @@ void COM_convert_resolution(NodeOperationBuilder &builder,
|
||||
break;
|
||||
}
|
||||
|
||||
if (doCenter) {
|
||||
NodeOperation *first = nullptr;
|
||||
ScaleOperation *scaleOperation = nullptr;
|
||||
if (doScale) {
|
||||
scaleOperation = new ScaleRelativeOperation(fromSocket->getDataType());
|
||||
scaleOperation->getInputSocket(1)->setResizeMode(ResizeMode::None);
|
||||
scaleOperation->getInputSocket(2)->setResizeMode(ResizeMode::None);
|
||||
first = scaleOperation;
|
||||
SetValueOperation *sxop = new SetValueOperation();
|
||||
sxop->setValue(scaleX);
|
||||
builder.addLink(sxop->getOutputSocket(), scaleOperation->getInputSocket(1));
|
||||
SetValueOperation *syop = new SetValueOperation();
|
||||
syop->setValue(scaleY);
|
||||
builder.addLink(syop->getOutputSocket(), scaleOperation->getInputSocket(2));
|
||||
builder.addOperation(sxop);
|
||||
builder.addOperation(syop);
|
||||
NodeOperation *first = nullptr;
|
||||
ScaleOperation *scaleOperation = nullptr;
|
||||
if (doScale) {
|
||||
scaleOperation = new ScaleRelativeOperation(fromSocket->getDataType());
|
||||
scaleOperation->getInputSocket(1)->setResizeMode(ResizeMode::None);
|
||||
scaleOperation->getInputSocket(2)->setResizeMode(ResizeMode::None);
|
||||
first = scaleOperation;
|
||||
SetValueOperation *sxop = new SetValueOperation();
|
||||
sxop->setValue(scaleX);
|
||||
builder.addLink(sxop->getOutputSocket(), scaleOperation->getInputSocket(1));
|
||||
SetValueOperation *syop = new SetValueOperation();
|
||||
syop->setValue(scaleY);
|
||||
builder.addLink(syop->getOutputSocket(), scaleOperation->getInputSocket(2));
|
||||
builder.addOperation(sxop);
|
||||
builder.addOperation(syop);
|
||||
|
||||
unsigned int resolution[2] = {fromOperation->getWidth(), fromOperation->getHeight()};
|
||||
scaleOperation->setResolution(resolution);
|
||||
sxop->setResolution(resolution);
|
||||
syop->setResolution(resolution);
|
||||
builder.addOperation(scaleOperation);
|
||||
}
|
||||
|
||||
TranslateOperation *translateOperation = new TranslateOperation(toSocket->getDataType());
|
||||
translateOperation->getInputSocket(1)->setResizeMode(ResizeMode::None);
|
||||
translateOperation->getInputSocket(2)->setResizeMode(ResizeMode::None);
|
||||
if (!first) {
|
||||
first = translateOperation;
|
||||
}
|
||||
SetValueOperation *xop = new SetValueOperation();
|
||||
xop->setValue(addX);
|
||||
builder.addLink(xop->getOutputSocket(), translateOperation->getInputSocket(1));
|
||||
SetValueOperation *yop = new SetValueOperation();
|
||||
yop->setValue(addY);
|
||||
builder.addLink(yop->getOutputSocket(), translateOperation->getInputSocket(2));
|
||||
builder.addOperation(xop);
|
||||
builder.addOperation(yop);
|
||||
|
||||
unsigned int resolution[2] = {toOperation->getWidth(), toOperation->getHeight()};
|
||||
translateOperation->setResolution(resolution);
|
||||
xop->setResolution(resolution);
|
||||
yop->setResolution(resolution);
|
||||
builder.addOperation(translateOperation);
|
||||
|
||||
if (doScale) {
|
||||
translateOperation->getInputSocket(0)->setResizeMode(ResizeMode::None);
|
||||
builder.addLink(scaleOperation->getOutputSocket(), translateOperation->getInputSocket(0));
|
||||
}
|
||||
|
||||
/* remove previous link and replace */
|
||||
builder.removeInputLink(toSocket);
|
||||
first->getInputSocket(0)->setResizeMode(ResizeMode::None);
|
||||
toSocket->setResizeMode(ResizeMode::None);
|
||||
builder.addLink(fromSocket, first->getInputSocket(0));
|
||||
builder.addLink(translateOperation->getOutputSocket(), toSocket);
|
||||
unsigned int resolution[2] = {fromOperation->getWidth(), fromOperation->getHeight()};
|
||||
const rcti &scale_canvas = fromOperation->get_canvas();
|
||||
scaleOperation->set_canvas(scale_canvas);
|
||||
sxop->set_canvas(scale_canvas);
|
||||
syop->set_canvas(scale_canvas);
|
||||
builder.addOperation(scaleOperation);
|
||||
}
|
||||
|
||||
TranslateOperation *translateOperation = new TranslateOperation(toSocket->getDataType());
|
||||
translateOperation->getInputSocket(1)->setResizeMode(ResizeMode::None);
|
||||
translateOperation->getInputSocket(2)->setResizeMode(ResizeMode::None);
|
||||
if (!first) {
|
||||
first = translateOperation;
|
||||
}
|
||||
const float addX = doCenter ? (toWidth - fromWidth) / 2.0f : 0.0f;
|
||||
const float addY = doCenter ? (toHeight - fromHeight) / 2.0f : 0.0f;
|
||||
SetValueOperation *xop = new SetValueOperation();
|
||||
xop->setValue(addX);
|
||||
builder.addLink(xop->getOutputSocket(), translateOperation->getInputSocket(1));
|
||||
SetValueOperation *yop = new SetValueOperation();
|
||||
yop->setValue(addY);
|
||||
builder.addLink(yop->getOutputSocket(), translateOperation->getInputSocket(2));
|
||||
builder.addOperation(xop);
|
||||
builder.addOperation(yop);
|
||||
|
||||
rcti translate_canvas = toOperation->get_canvas();
|
||||
if (mode == ResizeMode::Align) {
|
||||
translate_canvas.xmax = translate_canvas.xmin + fromWidth;
|
||||
translate_canvas.ymax = translate_canvas.ymin + fromHeight;
|
||||
}
|
||||
translateOperation->set_canvas(translate_canvas);
|
||||
xop->set_canvas(translate_canvas);
|
||||
yop->set_canvas(translate_canvas);
|
||||
builder.addOperation(translateOperation);
|
||||
|
||||
if (doScale) {
|
||||
translateOperation->getInputSocket(0)->setResizeMode(ResizeMode::None);
|
||||
builder.addLink(scaleOperation->getOutputSocket(), translateOperation->getInputSocket(0));
|
||||
}
|
||||
|
||||
/* remove previous link and replace */
|
||||
builder.removeInputLink(toSocket);
|
||||
first->getInputSocket(0)->setResizeMode(ResizeMode::None);
|
||||
toSocket->setResizeMode(ResizeMode::None);
|
||||
builder.addLink(fromSocket, first->getInputSocket(0));
|
||||
builder.addLink(translateOperation->getOutputSocket(), toSocket);
|
||||
}
|
||||
|
||||
} // namespace blender::compositor
|
||||
|
@@ -65,8 +65,8 @@ NodeOperation *COM_convert_data_type(const NodeOperationOutput &from,
|
||||
* \note Conversion logic is implemented in this function.
|
||||
* \see InputSocketResizeMode for the possible conversions.
|
||||
*/
|
||||
void COM_convert_resolution(NodeOperationBuilder &builder,
|
||||
NodeOperationOutput *fromSocket,
|
||||
NodeOperationInput *toSocket);
|
||||
void COM_convert_canvas(NodeOperationBuilder &builder,
|
||||
NodeOperationOutput *fromSocket,
|
||||
NodeOperationInput *toSocket);
|
||||
|
||||
} // namespace blender::compositor
|
||||
|
@@ -162,8 +162,10 @@ int DebugInfo::graphviz_operation(const ExecutionSystem *system,
|
||||
|
||||
len += snprintf(str + len,
|
||||
maxlen > len ? maxlen - len : 0,
|
||||
"#%d (%u,%u)",
|
||||
"#%d (%i,%i) (%u,%u)",
|
||||
operation->get_id(),
|
||||
operation->get_canvas().xmin,
|
||||
operation->get_canvas().ymin,
|
||||
operation->getWidth(),
|
||||
operation->getHeight());
|
||||
|
||||
|
@@ -74,37 +74,61 @@ void FullFrameExecutionModel::determine_areas_to_render_and_reads()
|
||||
}
|
||||
}
|
||||
|
||||
Vector<MemoryBuffer *> FullFrameExecutionModel::get_input_buffers(NodeOperation *op)
|
||||
/**
|
||||
* Returns input buffers with an offset relative to given output coordinates. Returned memory
|
||||
* buffers must be deleted.
|
||||
*/
|
||||
Vector<MemoryBuffer *> FullFrameExecutionModel::get_input_buffers(NodeOperation *op,
|
||||
const int output_x,
|
||||
const int output_y)
|
||||
{
|
||||
const int num_inputs = op->getNumberOfInputSockets();
|
||||
Vector<MemoryBuffer *> inputs_buffers(num_inputs);
|
||||
for (int i = 0; i < num_inputs; i++) {
|
||||
NodeOperation *input_op = op->get_input_operation(i);
|
||||
inputs_buffers[i] = active_buffers_.get_rendered_buffer(input_op);
|
||||
NodeOperation *input = op->get_input_operation(i);
|
||||
const int offset_x = (input->get_canvas().xmin - op->get_canvas().xmin) + output_x;
|
||||
const int offset_y = (input->get_canvas().ymin - op->get_canvas().ymin) + output_y;
|
||||
MemoryBuffer *buf = active_buffers_.get_rendered_buffer(input);
|
||||
|
||||
rcti rect = buf->get_rect();
|
||||
BLI_rcti_translate(&rect, offset_x, offset_y);
|
||||
inputs_buffers[i] = new MemoryBuffer(
|
||||
buf->getBuffer(), buf->get_num_channels(), rect, buf->is_a_single_elem());
|
||||
}
|
||||
return inputs_buffers;
|
||||
}
|
||||
|
||||
MemoryBuffer *FullFrameExecutionModel::create_operation_buffer(NodeOperation *op)
|
||||
MemoryBuffer *FullFrameExecutionModel::create_operation_buffer(NodeOperation *op,
|
||||
const int output_x,
|
||||
const int output_y)
|
||||
{
|
||||
rcti op_rect;
|
||||
BLI_rcti_init(&op_rect, 0, op->getWidth(), 0, op->getHeight());
|
||||
rcti rect;
|
||||
BLI_rcti_init(&rect, output_x, output_x + op->getWidth(), output_y, output_y + op->getHeight());
|
||||
|
||||
const DataType data_type = op->getOutputSocket(0)->getDataType();
|
||||
const bool is_a_single_elem = op->get_flags().is_constant_operation;
|
||||
return new MemoryBuffer(data_type, op_rect, is_a_single_elem);
|
||||
return new MemoryBuffer(data_type, rect, is_a_single_elem);
|
||||
}
|
||||
|
||||
void FullFrameExecutionModel::render_operation(NodeOperation *op)
|
||||
{
|
||||
Vector<MemoryBuffer *> input_bufs = get_input_buffers(op);
|
||||
/* Output has no offset for easier image algorithms implementation on operations. */
|
||||
constexpr int output_x = 0;
|
||||
constexpr int output_y = 0;
|
||||
|
||||
const bool has_outputs = op->getNumberOfOutputSockets() > 0;
|
||||
MemoryBuffer *op_buf = has_outputs ? create_operation_buffer(op) : nullptr;
|
||||
MemoryBuffer *op_buf = has_outputs ? create_operation_buffer(op, output_x, output_y) : nullptr;
|
||||
if (op->getWidth() > 0 && op->getHeight() > 0) {
|
||||
Span<rcti> areas = active_buffers_.get_areas_to_render(op);
|
||||
Vector<MemoryBuffer *> input_bufs = get_input_buffers(op, output_x, output_y);
|
||||
const int op_offset_x = output_x - op->get_canvas().xmin;
|
||||
const int op_offset_y = output_y - op->get_canvas().ymin;
|
||||
Span<rcti> areas = active_buffers_.get_areas_to_render(op, op_offset_x, op_offset_y);
|
||||
op->render(op_buf, areas, input_bufs);
|
||||
DebugInfo::operation_rendered(op, op_buf);
|
||||
|
||||
for (MemoryBuffer *buf : input_bufs) {
|
||||
delete buf;
|
||||
}
|
||||
}
|
||||
/* Even if operation has no resolution set the empty buffer. It will be clipped with a
|
||||
* TranslateOperation from convert resolutions if linked to an operation with resolution. */
|
||||
@@ -199,14 +223,15 @@ void FullFrameExecutionModel::determine_areas_to_render(NodeOperation *output_op
|
||||
const int num_inputs = operation->getNumberOfInputSockets();
|
||||
for (int i = 0; i < num_inputs; i++) {
|
||||
NodeOperation *input_op = operation->get_input_operation(i);
|
||||
rcti input_op_rect, input_area;
|
||||
BLI_rcti_init(&input_op_rect, 0, input_op->getWidth(), 0, input_op->getHeight());
|
||||
rcti input_area;
|
||||
operation->get_area_of_interest(input_op, render_area, input_area);
|
||||
|
||||
/* Ensure area of interest is within operation bounds, cropping areas outside. */
|
||||
BLI_rcti_isect(&input_area, &input_op_rect, &input_area);
|
||||
BLI_rcti_isect(&input_area, &input_op->get_canvas(), &input_area);
|
||||
|
||||
stack.append({input_op, input_area});
|
||||
if (!BLI_rcti_is_empty(&input_area)) {
|
||||
stack.append({input_op, input_area});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -243,9 +268,8 @@ void FullFrameExecutionModel::get_output_render_area(NodeOperation *output_op, r
|
||||
BLI_assert(output_op->isOutputOperation(context_.isRendering()));
|
||||
|
||||
/* By default return operation bounds (no border). */
|
||||
const int op_width = output_op->getWidth();
|
||||
const int op_height = output_op->getHeight();
|
||||
BLI_rcti_init(&r_area, 0, op_width, 0, op_height);
|
||||
rcti canvas = output_op->get_canvas();
|
||||
r_area = canvas;
|
||||
|
||||
const bool has_viewer_border = border_.use_viewer_border &&
|
||||
(output_op->get_flags().is_viewer_operation ||
|
||||
@@ -255,12 +279,13 @@ void FullFrameExecutionModel::get_output_render_area(NodeOperation *output_op, r
|
||||
/* Get border with normalized coordinates. */
|
||||
const rctf *norm_border = has_viewer_border ? border_.viewer_border : border_.render_border;
|
||||
|
||||
/* Return de-normalized border. */
|
||||
BLI_rcti_init(&r_area,
|
||||
norm_border->xmin * op_width,
|
||||
norm_border->xmax * op_width,
|
||||
norm_border->ymin * op_height,
|
||||
norm_border->ymax * op_height);
|
||||
/* Return de-normalized border within canvas. */
|
||||
const int w = output_op->getWidth();
|
||||
const int h = output_op->getHeight();
|
||||
r_area.xmin = canvas.xmin + norm_border->xmin * w;
|
||||
r_area.xmax = canvas.xmin + norm_border->xmax * w;
|
||||
r_area.ymin = canvas.ymin + norm_border->ymin * h;
|
||||
r_area.ymax = canvas.ymin + norm_border->ymax * h;
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -61,8 +61,10 @@ class FullFrameExecutionModel : public ExecutionModel {
|
||||
void determine_areas_to_render_and_reads();
|
||||
void render_operations();
|
||||
void render_output_dependencies(NodeOperation *output_op);
|
||||
Vector<MemoryBuffer *> get_input_buffers(NodeOperation *op);
|
||||
MemoryBuffer *create_operation_buffer(NodeOperation *op);
|
||||
Vector<MemoryBuffer *> get_input_buffers(NodeOperation *op,
|
||||
const int output_x,
|
||||
const int output_y);
|
||||
MemoryBuffer *create_operation_buffer(NodeOperation *op, const int output_x, const int output_y);
|
||||
void render_operation(NodeOperation *op);
|
||||
|
||||
void operation_finished(NodeOperation *operation);
|
||||
|
@@ -122,6 +122,8 @@ void MemoryBuffer::set_strides()
|
||||
this->elem_stride = m_num_channels;
|
||||
this->row_stride = getWidth() * m_num_channels;
|
||||
}
|
||||
to_positive_x_stride_ = m_rect.xmin < 0 ? -m_rect.xmin + 1 : (m_rect.xmin == 0 ? 1 : 0);
|
||||
to_positive_y_stride_ = m_rect.ymin < 0 ? -m_rect.ymin + 1 : (m_rect.ymin == 0 ? 1 : 0);
|
||||
}
|
||||
|
||||
void MemoryBuffer::clear()
|
||||
@@ -245,7 +247,9 @@ void MemoryBuffer::copy_from(const MemoryBuffer *src,
|
||||
|
||||
void MemoryBuffer::copy_from(const uchar *src, const rcti &area)
|
||||
{
|
||||
copy_from(src, area, 0, this->get_num_channels(), this->get_num_channels(), 0);
|
||||
const int elem_stride = this->get_num_channels();
|
||||
const int row_stride = elem_stride * getWidth();
|
||||
copy_from(src, area, 0, this->get_num_channels(), elem_stride, row_stride, 0);
|
||||
}
|
||||
|
||||
void MemoryBuffer::copy_from(const uchar *src,
|
||||
@@ -253,10 +257,18 @@ void MemoryBuffer::copy_from(const uchar *src,
|
||||
const int channel_offset,
|
||||
const int elem_size,
|
||||
const int elem_stride,
|
||||
const int row_stride,
|
||||
const int to_channel_offset)
|
||||
{
|
||||
copy_from(
|
||||
src, area, channel_offset, elem_size, elem_stride, area.xmin, area.ymin, to_channel_offset);
|
||||
copy_from(src,
|
||||
area,
|
||||
channel_offset,
|
||||
elem_size,
|
||||
elem_stride,
|
||||
row_stride,
|
||||
area.xmin,
|
||||
area.ymin,
|
||||
to_channel_offset);
|
||||
}
|
||||
|
||||
void MemoryBuffer::copy_from(const uchar *src,
|
||||
@@ -264,6 +276,7 @@ void MemoryBuffer::copy_from(const uchar *src,
|
||||
const int channel_offset,
|
||||
const int elem_size,
|
||||
const int elem_stride,
|
||||
const int row_stride,
|
||||
const int to_x,
|
||||
const int to_y,
|
||||
const int to_channel_offset)
|
||||
@@ -273,10 +286,9 @@ void MemoryBuffer::copy_from(const uchar *src,
|
||||
|
||||
const int width = BLI_rcti_size_x(&area);
|
||||
const int height = BLI_rcti_size_y(&area);
|
||||
const int src_row_stride = width * elem_stride;
|
||||
const uchar *const src_start = src + area.ymin * src_row_stride + channel_offset;
|
||||
const uchar *const src_start = src + area.ymin * row_stride + channel_offset;
|
||||
for (int y = 0; y < height; y++) {
|
||||
const uchar *from_elem = src_start + y * src_row_stride;
|
||||
const uchar *from_elem = src_start + y * row_stride + area.xmin * elem_stride;
|
||||
float *to_elem = &this->get_value(to_x, to_y + y, to_channel_offset);
|
||||
const float *row_end = to_elem + width * this->elem_stride;
|
||||
while (to_elem < row_end) {
|
||||
@@ -346,7 +358,16 @@ void MemoryBuffer::copy_from(const ImBuf *src,
|
||||
else if (src->rect) {
|
||||
const uchar *uc_buf = (uchar *)src->rect;
|
||||
const int elem_stride = src->channels;
|
||||
copy_from(uc_buf, area, channel_offset, elem_size, elem_stride, to_x, to_y, to_channel_offset);
|
||||
const int row_stride = elem_stride * src->x;
|
||||
copy_from(uc_buf,
|
||||
area,
|
||||
channel_offset,
|
||||
elem_size,
|
||||
elem_stride,
|
||||
row_stride,
|
||||
to_x,
|
||||
to_y,
|
||||
to_channel_offset);
|
||||
if (ensure_linear_space) {
|
||||
colorspace_to_scene_linear(this, area, src->rect_colorspace);
|
||||
}
|
||||
@@ -405,12 +426,48 @@ void MemoryBuffer::addPixel(int x, int y, const float color[4])
|
||||
}
|
||||
}
|
||||
|
||||
static void read_ewa_elem(void *userdata, int x, int y, float result[4])
|
||||
{
|
||||
const MemoryBuffer *buffer = static_cast<const MemoryBuffer *>(userdata);
|
||||
buffer->read_elem_checked(x, y, result);
|
||||
}
|
||||
|
||||
void MemoryBuffer::read_elem_filtered(
|
||||
const float x, const float y, float dx[2], float dy[2], float *out) const
|
||||
{
|
||||
BLI_assert(this->m_datatype == DataType::Color);
|
||||
|
||||
const float deriv[2][2] = {{dx[0], dx[1]}, {dy[0], dy[1]}};
|
||||
|
||||
float inv_width = 1.0f / (float)this->getWidth(), inv_height = 1.0f / (float)this->getHeight();
|
||||
/* TODO(sergey): Render pipeline uses normalized coordinates and derivatives,
|
||||
* but compositor uses pixel space. For now let's just divide the values and
|
||||
* switch compositor to normalized space for EWA later.
|
||||
*/
|
||||
float uv_normal[2] = {get_relative_x(x) * inv_width, get_relative_y(y) * inv_height};
|
||||
float du_normal[2] = {deriv[0][0] * inv_width, deriv[0][1] * inv_height};
|
||||
float dv_normal[2] = {deriv[1][0] * inv_width, deriv[1][1] * inv_height};
|
||||
|
||||
BLI_ewa_filter(this->getWidth(),
|
||||
this->getHeight(),
|
||||
false,
|
||||
true,
|
||||
uv_normal,
|
||||
du_normal,
|
||||
dv_normal,
|
||||
read_ewa_elem,
|
||||
const_cast<MemoryBuffer *>(this),
|
||||
out);
|
||||
}
|
||||
|
||||
/* TODO(manzanilla): To be removed with tiled implementation. */
|
||||
static void read_ewa_pixel_sampled(void *userdata, int x, int y, float result[4])
|
||||
{
|
||||
MemoryBuffer *buffer = (MemoryBuffer *)userdata;
|
||||
buffer->read(result, x, y);
|
||||
}
|
||||
|
||||
/* TODO(manzanilla): To be removed with tiled implementation. */
|
||||
void MemoryBuffer::readEWA(float *result, const float uv[2], const float derivatives[2][2])
|
||||
{
|
||||
if (m_is_a_single_elem) {
|
||||
|
@@ -114,6 +114,12 @@ class MemoryBuffer {
|
||||
*/
|
||||
bool owns_data_;
|
||||
|
||||
/** Stride to make any x coordinate within buffer positive (non-zero). */
|
||||
int to_positive_x_stride_;
|
||||
|
||||
/** Stride to make any y coordinate within buffer positive (non-zero). */
|
||||
int to_positive_y_stride_;
|
||||
|
||||
public:
|
||||
/**
|
||||
* \brief construct new temporarily MemoryBuffer for an area
|
||||
@@ -176,7 +182,7 @@ class MemoryBuffer {
|
||||
*/
|
||||
float *get_elem(int x, int y)
|
||||
{
|
||||
BLI_assert(x >= m_rect.xmin && x < m_rect.xmax && y >= m_rect.ymin && y < m_rect.ymax);
|
||||
BLI_assert(has_coords(x, y));
|
||||
return m_buffer + get_coords_offset(x, y);
|
||||
}
|
||||
|
||||
@@ -185,7 +191,7 @@ class MemoryBuffer {
|
||||
*/
|
||||
const float *get_elem(int x, int y) const
|
||||
{
|
||||
BLI_assert(x >= m_rect.xmin && x < m_rect.xmax && y >= m_rect.ymin && y < m_rect.ymax);
|
||||
BLI_assert(has_coords(x, y));
|
||||
return m_buffer + get_coords_offset(x, y);
|
||||
}
|
||||
|
||||
@@ -194,27 +200,94 @@ class MemoryBuffer {
|
||||
memcpy(out, get_elem(x, y), m_num_channels * sizeof(float));
|
||||
}
|
||||
|
||||
void read_elem_checked(int x, int y, float *out) const
|
||||
{
|
||||
if (!has_coords(x, y)) {
|
||||
clear_elem(out);
|
||||
}
|
||||
else {
|
||||
read_elem(x, y, out);
|
||||
}
|
||||
}
|
||||
|
||||
void read_elem_checked(float x, float y, float *out) const
|
||||
{
|
||||
read_elem_checked(floor_x(x), floor_y(y), out);
|
||||
}
|
||||
|
||||
void read_elem_bilinear(float x, float y, float *out) const
|
||||
{
|
||||
/* Only clear past +/-1 borders to be able to smooth edges. */
|
||||
if (x <= m_rect.xmin - 1.0f || x >= m_rect.xmax || y <= m_rect.ymin - 1.0f ||
|
||||
y >= m_rect.ymax) {
|
||||
clear_elem(out);
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_is_a_single_elem) {
|
||||
if (x >= m_rect.xmin && x < m_rect.xmax - 1.0f && y >= m_rect.ymin &&
|
||||
y < m_rect.ymax - 1.0f) {
|
||||
memcpy(out, m_buffer, get_elem_bytes_len());
|
||||
return;
|
||||
}
|
||||
|
||||
/* Do sampling at borders to smooth edges. */
|
||||
const float last_x = getWidth() - 1.0f;
|
||||
const float rel_x = get_relative_x(x);
|
||||
float single_x = 0.0f;
|
||||
if (rel_x < 0.0f) {
|
||||
single_x = rel_x;
|
||||
}
|
||||
else if (rel_x > last_x) {
|
||||
single_x = rel_x - last_x;
|
||||
}
|
||||
|
||||
const float last_y = getHeight() - 1.0f;
|
||||
const float rel_y = get_relative_y(y);
|
||||
float single_y = 0.0f;
|
||||
if (rel_y < 0.0f) {
|
||||
single_y = rel_y;
|
||||
}
|
||||
else if (rel_y > last_y) {
|
||||
single_y = rel_y - last_y;
|
||||
}
|
||||
|
||||
BLI_bilinear_interpolation_fl(m_buffer, out, 1, 1, m_num_channels, single_x, single_y);
|
||||
return;
|
||||
}
|
||||
|
||||
BLI_bilinear_interpolation_fl(m_buffer,
|
||||
out,
|
||||
getWidth(),
|
||||
getHeight(),
|
||||
m_num_channels,
|
||||
get_relative_x(x),
|
||||
get_relative_y(y));
|
||||
}
|
||||
|
||||
void read_elem_sampled(float x, float y, PixelSampler sampler, float *out) const
|
||||
{
|
||||
switch (sampler) {
|
||||
case PixelSampler::Nearest:
|
||||
this->read_elem(x, y, out);
|
||||
read_elem_checked(x, y, out);
|
||||
break;
|
||||
case PixelSampler::Bilinear:
|
||||
case PixelSampler::Bicubic:
|
||||
/* No bicubic. Current implementation produces fuzzy results. */
|
||||
this->readBilinear(out, x, y);
|
||||
read_elem_bilinear(x, y, out);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void read_elem_filtered(
|
||||
const float x, const float y, float dx[2], float dy[2], float *out) const;
|
||||
|
||||
/**
|
||||
* Get channel value at given coordinates.
|
||||
*/
|
||||
float &get_value(int x, int y, int channel)
|
||||
{
|
||||
BLI_assert(x >= m_rect.xmin && x < m_rect.xmax && y >= m_rect.ymin && y < m_rect.ymax &&
|
||||
channel >= 0 && channel < m_num_channels);
|
||||
BLI_assert(has_coords(x, y) && channel >= 0 && channel < m_num_channels);
|
||||
return m_buffer[get_coords_offset(x, y) + channel];
|
||||
}
|
||||
|
||||
@@ -223,8 +296,7 @@ class MemoryBuffer {
|
||||
*/
|
||||
const float &get_value(int x, int y, int channel) const
|
||||
{
|
||||
BLI_assert(x >= m_rect.xmin && x < m_rect.xmax && y >= m_rect.ymin && y < m_rect.ymax &&
|
||||
channel >= 0 && channel < m_num_channels);
|
||||
BLI_assert(has_coords(x, y) && channel >= 0 && channel < m_num_channels);
|
||||
return m_buffer[get_coords_offset(x, y) + channel];
|
||||
}
|
||||
|
||||
@@ -233,7 +305,7 @@ class MemoryBuffer {
|
||||
*/
|
||||
const float *get_row_end(int y) const
|
||||
{
|
||||
BLI_assert(y >= 0 && y < getHeight());
|
||||
BLI_assert(has_y(y));
|
||||
return m_buffer + (is_a_single_elem() ? m_num_channels : get_coords_offset(getWidth(), y));
|
||||
}
|
||||
|
||||
@@ -300,6 +372,12 @@ class MemoryBuffer {
|
||||
return this->m_buffer;
|
||||
}
|
||||
|
||||
float *release_buffer()
|
||||
{
|
||||
owns_data_ = false;
|
||||
return this->m_buffer;
|
||||
}
|
||||
|
||||
MemoryBuffer *inflate() const;
|
||||
|
||||
inline void wrap_pixel(int &x, int &y, MemoryBufferExtend extend_x, MemoryBufferExtend extend_y)
|
||||
@@ -403,6 +481,8 @@ class MemoryBuffer {
|
||||
y = y + m_rect.ymin;
|
||||
}
|
||||
|
||||
/* TODO(manzanilla): To be removed with tiled implementation. For applying `MemoryBufferExtend`
|
||||
* use `wrap_pixel`. */
|
||||
inline void read(float *result,
|
||||
int x,
|
||||
int y,
|
||||
@@ -425,6 +505,7 @@ class MemoryBuffer {
|
||||
}
|
||||
}
|
||||
|
||||
/* TODO(manzanilla): To be removed with tiled implementation. */
|
||||
inline void readNoCheck(float *result,
|
||||
int x,
|
||||
int y,
|
||||
@@ -507,12 +588,14 @@ class MemoryBuffer {
|
||||
int channel_offset,
|
||||
int elem_size,
|
||||
int elem_stride,
|
||||
int row_stride,
|
||||
int to_channel_offset);
|
||||
void copy_from(const uchar *src,
|
||||
const rcti &area,
|
||||
int channel_offset,
|
||||
int elem_size,
|
||||
int elem_stride,
|
||||
int row_stride,
|
||||
int to_x,
|
||||
int to_y,
|
||||
int to_channel_offset);
|
||||
@@ -582,6 +665,48 @@ class MemoryBuffer {
|
||||
return get_memory_width() * get_memory_height();
|
||||
}
|
||||
|
||||
void clear_elem(float *out) const
|
||||
{
|
||||
memset(out, 0, this->m_num_channels * sizeof(float));
|
||||
}
|
||||
|
||||
template<typename T> T get_relative_x(T x) const
|
||||
{
|
||||
return x - m_rect.xmin;
|
||||
}
|
||||
|
||||
template<typename T> T get_relative_y(T y) const
|
||||
{
|
||||
return y - m_rect.ymin;
|
||||
}
|
||||
|
||||
template<typename T> bool has_coords(T x, T y) const
|
||||
{
|
||||
return has_x(x) && has_y(y);
|
||||
}
|
||||
|
||||
template<typename T> bool has_x(T x) const
|
||||
{
|
||||
return x >= m_rect.xmin && x < m_rect.xmax;
|
||||
}
|
||||
|
||||
template<typename T> bool has_y(T y) const
|
||||
{
|
||||
return y >= m_rect.ymin && y < m_rect.ymax;
|
||||
}
|
||||
|
||||
/* Fast floor functions. The caller should check result is within buffer bounds. It ceils in near
|
||||
* cases and when given coordinate is negative and less than buffer rect `min - 1`. */
|
||||
int floor_x(float x) const
|
||||
{
|
||||
return (int)(x + to_positive_x_stride_) - to_positive_x_stride_;
|
||||
}
|
||||
|
||||
int floor_y(float y) const
|
||||
{
|
||||
return (int)(y + to_positive_y_stride_) - to_positive_y_stride_;
|
||||
}
|
||||
|
||||
void copy_single_elem_from(const MemoryBuffer *src,
|
||||
int channel_offset,
|
||||
int elem_size,
|
||||
|
@@ -248,7 +248,9 @@ void NodeGraph::add_proxies_group_inputs(bNode *b_node, bNode *b_node_io)
|
||||
}
|
||||
}
|
||||
|
||||
void NodeGraph::add_proxies_group_outputs(bNode *b_node, bNode *b_node_io, bool use_buffer)
|
||||
void NodeGraph::add_proxies_group_outputs(const CompositorContext &context,
|
||||
bNode *b_node,
|
||||
bNode *b_node_io)
|
||||
{
|
||||
bNodeTree *b_group_tree = (bNodeTree *)b_node->id;
|
||||
BLI_assert(b_group_tree); /* should have been checked in advance */
|
||||
@@ -261,7 +263,8 @@ void NodeGraph::add_proxies_group_outputs(bNode *b_node, bNode *b_node_io, bool
|
||||
b_sock_io = b_sock_io->next) {
|
||||
bNodeSocket *b_sock_group = find_b_node_output(b_node, b_sock_io->identifier);
|
||||
if (b_sock_group) {
|
||||
if (use_buffer) {
|
||||
if (context.isGroupnodeBufferEnabled() &&
|
||||
context.get_execution_model() == eExecutionModel::Tiled) {
|
||||
SocketBufferNode *buffer = new SocketBufferNode(b_node_io, b_sock_io, b_sock_group);
|
||||
add_node(buffer, b_group_tree, key, is_active_group);
|
||||
}
|
||||
@@ -297,7 +300,7 @@ void NodeGraph::add_proxies_group(const CompositorContext &context,
|
||||
}
|
||||
|
||||
if (b_node_io->type == NODE_GROUP_OUTPUT && (b_node_io->flag & NODE_DO_OUTPUT)) {
|
||||
add_proxies_group_outputs(b_node, b_node_io, context.isGroupnodeBufferEnabled());
|
||||
add_proxies_group_outputs(context, b_node, b_node_io);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -107,7 +107,9 @@ class NodeGraph {
|
||||
bool is_active_group);
|
||||
|
||||
void add_proxies_group_inputs(bNode *b_node, bNode *b_node_io);
|
||||
void add_proxies_group_outputs(bNode *b_node, bNode *b_node_io, bool use_buffer);
|
||||
void add_proxies_group_outputs(const CompositorContext &context,
|
||||
bNode *b_node,
|
||||
bNode *b_node_io);
|
||||
void add_proxies_group(const CompositorContext &context, bNode *b_node, bNodeInstanceKey key);
|
||||
|
||||
void add_proxies_reroute(bNodeTree *b_ntree,
|
||||
|
@@ -35,9 +35,8 @@ namespace blender::compositor {
|
||||
|
||||
NodeOperation::NodeOperation()
|
||||
{
|
||||
this->m_resolutionInputSocketIndex = 0;
|
||||
this->m_width = 0;
|
||||
this->m_height = 0;
|
||||
canvas_input_index_ = 0;
|
||||
canvas_ = COM_AREA_NONE;
|
||||
this->m_btree = nullptr;
|
||||
}
|
||||
|
||||
@@ -61,44 +60,46 @@ void NodeOperation::addOutputSocket(DataType datatype)
|
||||
m_outputs.append(NodeOperationOutput(this, datatype));
|
||||
}
|
||||
|
||||
void NodeOperation::determineResolution(unsigned int resolution[2],
|
||||
unsigned int preferredResolution[2])
|
||||
void NodeOperation::determine_canvas(const rcti &preferred_area, rcti &r_area)
|
||||
{
|
||||
unsigned int used_resolution_index = 0;
|
||||
if (m_resolutionInputSocketIndex == RESOLUTION_INPUT_ANY) {
|
||||
unsigned int used_canvas_index = 0;
|
||||
if (canvas_input_index_ == RESOLUTION_INPUT_ANY) {
|
||||
for (NodeOperationInput &input : m_inputs) {
|
||||
unsigned int any_resolution[2] = {0, 0};
|
||||
input.determineResolution(any_resolution, preferredResolution);
|
||||
if (any_resolution[0] * any_resolution[1] > 0) {
|
||||
resolution[0] = any_resolution[0];
|
||||
resolution[1] = any_resolution[1];
|
||||
rcti any_area = COM_AREA_NONE;
|
||||
const bool determined = input.determine_canvas(preferred_area, any_area);
|
||||
if (determined) {
|
||||
r_area = any_area;
|
||||
break;
|
||||
}
|
||||
used_resolution_index += 1;
|
||||
used_canvas_index += 1;
|
||||
}
|
||||
}
|
||||
else if (m_resolutionInputSocketIndex < m_inputs.size()) {
|
||||
NodeOperationInput &input = m_inputs[m_resolutionInputSocketIndex];
|
||||
input.determineResolution(resolution, preferredResolution);
|
||||
used_resolution_index = m_resolutionInputSocketIndex;
|
||||
else if (canvas_input_index_ < m_inputs.size()) {
|
||||
NodeOperationInput &input = m_inputs[canvas_input_index_];
|
||||
input.determine_canvas(preferred_area, r_area);
|
||||
used_canvas_index = canvas_input_index_;
|
||||
}
|
||||
unsigned int temp2[2] = {resolution[0], resolution[1]};
|
||||
|
||||
unsigned int temp[2];
|
||||
if (modify_determined_canvas_fn_) {
|
||||
modify_determined_canvas_fn_(r_area);
|
||||
}
|
||||
|
||||
rcti unused_area;
|
||||
const rcti &local_preferred_area = r_area;
|
||||
for (unsigned int index = 0; index < m_inputs.size(); index++) {
|
||||
if (index == used_resolution_index) {
|
||||
if (index == used_canvas_index) {
|
||||
continue;
|
||||
}
|
||||
NodeOperationInput &input = m_inputs[index];
|
||||
if (input.isConnected()) {
|
||||
input.determineResolution(temp, temp2);
|
||||
input.determine_canvas(local_preferred_area, unused_area);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void NodeOperation::setResolutionInputSocketIndex(unsigned int index)
|
||||
void NodeOperation::set_canvas_input_index(unsigned int index)
|
||||
{
|
||||
this->m_resolutionInputSocketIndex = index;
|
||||
this->canvas_input_index_ = index;
|
||||
}
|
||||
|
||||
void NodeOperation::init_data()
|
||||
@@ -134,6 +135,23 @@ void NodeOperation::deinitExecution()
|
||||
{
|
||||
/* pass */
|
||||
}
|
||||
|
||||
void NodeOperation::set_canvas(const rcti &canvas_area)
|
||||
{
|
||||
canvas_ = canvas_area;
|
||||
flags.is_canvas_set = true;
|
||||
}
|
||||
|
||||
const rcti &NodeOperation::get_canvas() const
|
||||
{
|
||||
return canvas_;
|
||||
}
|
||||
|
||||
void NodeOperation::unset_canvas()
|
||||
{
|
||||
flags.is_canvas_set = false;
|
||||
}
|
||||
|
||||
SocketReader *NodeOperation::getInputSocketReader(unsigned int inputSocketIndex)
|
||||
{
|
||||
return this->getInputSocket(inputSocketIndex)->getReader();
|
||||
@@ -209,7 +227,7 @@ void NodeOperation::get_area_of_interest(const int input_idx,
|
||||
/* Non full-frame operations never implement this method. To ensure correctness assume
|
||||
* whole area is used. */
|
||||
NodeOperation *input_op = getInputOperation(input_idx);
|
||||
BLI_rcti_init(&r_input_area, 0, input_op->getWidth(), 0, input_op->getHeight());
|
||||
r_input_area = input_op->get_canvas();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -369,12 +387,16 @@ SocketReader *NodeOperationInput::getReader()
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void NodeOperationInput::determineResolution(unsigned int resolution[2],
|
||||
unsigned int preferredResolution[2])
|
||||
/**
|
||||
* \return Whether canvas area could be determined.
|
||||
*/
|
||||
bool NodeOperationInput::determine_canvas(const rcti &preferred_area, rcti &r_area)
|
||||
{
|
||||
if (m_link) {
|
||||
m_link->determineResolution(resolution, preferredResolution);
|
||||
m_link->determine_canvas(preferred_area, r_area);
|
||||
return !BLI_rcti_is_empty(&r_area);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/******************
|
||||
@@ -386,18 +408,16 @@ NodeOperationOutput::NodeOperationOutput(NodeOperation *op, DataType datatype)
|
||||
{
|
||||
}
|
||||
|
||||
void NodeOperationOutput::determineResolution(unsigned int resolution[2],
|
||||
unsigned int preferredResolution[2])
|
||||
void NodeOperationOutput::determine_canvas(const rcti &preferred_area, rcti &r_area)
|
||||
{
|
||||
NodeOperation &operation = getOperation();
|
||||
if (operation.get_flags().is_resolution_set) {
|
||||
resolution[0] = operation.getWidth();
|
||||
resolution[1] = operation.getHeight();
|
||||
if (operation.get_flags().is_canvas_set) {
|
||||
r_area = operation.get_canvas();
|
||||
}
|
||||
else {
|
||||
operation.determineResolution(resolution, preferredResolution);
|
||||
if (resolution[0] > 0 && resolution[1] > 0) {
|
||||
operation.setResolution(resolution);
|
||||
operation.determine_canvas(preferred_area, r_area);
|
||||
if (!BLI_rcti_is_empty(&r_area)) {
|
||||
operation.set_canvas(r_area);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -419,8 +439,8 @@ std::ostream &operator<<(std::ostream &os, const NodeOperationFlags &node_operat
|
||||
if (node_operation_flags.use_viewer_border) {
|
||||
os << "view_border,";
|
||||
}
|
||||
if (node_operation_flags.is_resolution_set) {
|
||||
os << "resolution_set,";
|
||||
if (node_operation_flags.is_canvas_set) {
|
||||
os << "canvas_set,";
|
||||
}
|
||||
if (node_operation_flags.is_set_operation) {
|
||||
os << "set_operation,";
|
||||
|
@@ -60,9 +60,13 @@ enum class ResizeMode {
|
||||
/** \brief Center the input image to the center of the working area of the node, no resizing
|
||||
* occurs */
|
||||
Center = NS_CR_CENTER,
|
||||
/** \brief The bottom left of the input image is the bottom left of the working area of the node,
|
||||
* no resizing occurs */
|
||||
/** No resizing or translation. */
|
||||
None = NS_CR_NONE,
|
||||
/**
|
||||
* Input image is translated so that its bottom left matches the bottom left of the working area
|
||||
* of the node, no resizing occurs.
|
||||
*/
|
||||
Align = 100,
|
||||
/** \brief Fit the width of the input image to the width of the working area of the node */
|
||||
FitWidth = NS_CR_FIT_WIDTH,
|
||||
/** \brief Fit the height of the input image to the height of the working area of the node */
|
||||
@@ -128,7 +132,7 @@ class NodeOperationInput {
|
||||
|
||||
SocketReader *getReader();
|
||||
|
||||
void determineResolution(unsigned int resolution[2], unsigned int preferredResolution[2]);
|
||||
bool determine_canvas(const rcti &preferred_area, rcti &r_area);
|
||||
|
||||
#ifdef WITH_CXX_GUARDEDALLOC
|
||||
MEM_CXX_CLASS_ALLOC_FUNCS("COM:NodeOperation")
|
||||
@@ -156,12 +160,7 @@ class NodeOperationOutput {
|
||||
return m_datatype;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief determine the resolution of this data going through this socket
|
||||
* \param resolution: the result of this operation
|
||||
* \param preferredResolution: the preferable resolution as no resolution could be determined
|
||||
*/
|
||||
void determineResolution(unsigned int resolution[2], unsigned int preferredResolution[2]);
|
||||
void determine_canvas(const rcti &preferred_area, rcti &r_area);
|
||||
|
||||
#ifdef WITH_CXX_GUARDEDALLOC
|
||||
MEM_CXX_CLASS_ALLOC_FUNCS("COM:NodeOperation")
|
||||
@@ -209,9 +208,9 @@ struct NodeOperationFlags {
|
||||
bool use_viewer_border : 1;
|
||||
|
||||
/**
|
||||
* Is the resolution of the operation set.
|
||||
* Is the canvas of the operation set.
|
||||
*/
|
||||
bool is_resolution_set : 1;
|
||||
bool is_canvas_set : 1;
|
||||
|
||||
/**
|
||||
* Is this a set operation (value, color, vector).
|
||||
@@ -255,7 +254,7 @@ struct NodeOperationFlags {
|
||||
open_cl = false;
|
||||
use_render_border = false;
|
||||
use_viewer_border = false;
|
||||
is_resolution_set = false;
|
||||
is_canvas_set = false;
|
||||
is_set_operation = false;
|
||||
is_read_buffer_operation = false;
|
||||
is_write_buffer_operation = false;
|
||||
@@ -283,9 +282,11 @@ class NodeOperation {
|
||||
Vector<NodeOperationOutput> m_outputs;
|
||||
|
||||
/**
|
||||
* \brief the index of the input socket that will be used to determine the resolution
|
||||
* \brief the index of the input socket that will be used to determine the canvas
|
||||
*/
|
||||
unsigned int m_resolutionInputSocketIndex;
|
||||
unsigned int canvas_input_index_;
|
||||
|
||||
std::function<void(rcti &canvas)> modify_determined_canvas_fn_;
|
||||
|
||||
/**
|
||||
* \brief mutex reference for very special node initializations
|
||||
@@ -309,15 +310,7 @@ class NodeOperation {
|
||||
*/
|
||||
eExecutionModel execution_model_;
|
||||
|
||||
/**
|
||||
* Width of the output of this operation.
|
||||
*/
|
||||
unsigned int m_width;
|
||||
|
||||
/**
|
||||
* Height of the output of this operation.
|
||||
*/
|
||||
unsigned int m_height;
|
||||
rcti canvas_;
|
||||
|
||||
/**
|
||||
* Flags how to evaluate this operation.
|
||||
@@ -331,10 +324,8 @@ class NodeOperation {
|
||||
{
|
||||
}
|
||||
|
||||
void set_execution_model(const eExecutionModel model)
|
||||
{
|
||||
execution_model_ = model;
|
||||
}
|
||||
/* Renturns false when input is not constant */
|
||||
bool get_input_constant(const int input_idx, float *r_constant);
|
||||
|
||||
void set_name(const std::string name)
|
||||
{
|
||||
@@ -379,14 +370,7 @@ class NodeOperation {
|
||||
return getInputOperation(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief determine the resolution of this node
|
||||
* \note this method will not set the resolution, this is the responsibility of the caller
|
||||
* \param resolution: the result of this operation
|
||||
* \param preferredResolution: the preferable resolution as no resolution could be determined
|
||||
*/
|
||||
virtual void determineResolution(unsigned int resolution[2],
|
||||
unsigned int preferredResolution[2]);
|
||||
virtual void determine_canvas(const rcti &preferred_area, rcti &r_area);
|
||||
|
||||
/**
|
||||
* \brief isOutputOperation determines whether this operation is an output of the
|
||||
@@ -408,6 +392,11 @@ class NodeOperation {
|
||||
return false;
|
||||
}
|
||||
|
||||
void set_execution_model(const eExecutionModel model)
|
||||
{
|
||||
execution_model_ = model;
|
||||
}
|
||||
|
||||
void setbNodeTree(const bNodeTree *tree)
|
||||
{
|
||||
this->m_btree = tree;
|
||||
@@ -482,18 +471,9 @@ class NodeOperation {
|
||||
}
|
||||
virtual void deinitExecution();
|
||||
|
||||
/**
|
||||
* \brief set the resolution
|
||||
* \param resolution: the resolution to set
|
||||
*/
|
||||
void setResolution(unsigned int resolution[2])
|
||||
{
|
||||
if (!this->flags.is_resolution_set) {
|
||||
this->m_width = resolution[0];
|
||||
this->m_height = resolution[1];
|
||||
this->flags.is_resolution_set = true;
|
||||
}
|
||||
}
|
||||
void set_canvas(const rcti &canvas_area);
|
||||
const rcti &get_canvas() const;
|
||||
void unset_canvas();
|
||||
|
||||
/**
|
||||
* \brief is this operation the active viewer output
|
||||
@@ -512,10 +492,19 @@ class NodeOperation {
|
||||
rcti *output);
|
||||
|
||||
/**
|
||||
* \brief set the index of the input socket that will determine the resolution of this
|
||||
* \brief set the index of the input socket that will determine the canvas of this
|
||||
* operation \param index: the index to set
|
||||
*/
|
||||
void setResolutionInputSocketIndex(unsigned int index);
|
||||
void set_canvas_input_index(unsigned int index);
|
||||
|
||||
/**
|
||||
* Set a custom function to modify determined canvas from main input just before setting it
|
||||
* as preferred for the other inputs.
|
||||
*/
|
||||
void set_determined_canvas_modifier(std::function<void(rcti &canvas)> fn)
|
||||
{
|
||||
modify_determined_canvas_fn_ = fn;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief get the render priority of this node.
|
||||
@@ -541,12 +530,12 @@ class NodeOperation {
|
||||
|
||||
unsigned int getWidth() const
|
||||
{
|
||||
return m_width;
|
||||
return BLI_rcti_size_x(&get_canvas());
|
||||
}
|
||||
|
||||
unsigned int getHeight() const
|
||||
{
|
||||
return m_height;
|
||||
return BLI_rcti_size_y(&get_canvas());
|
||||
}
|
||||
|
||||
inline void readSampled(float result[4], float x, float y, PixelSampler sampler)
|
||||
@@ -616,16 +605,18 @@ class NodeOperation {
|
||||
void addInputSocket(DataType datatype, ResizeMode resize_mode = ResizeMode::Center);
|
||||
void addOutputSocket(DataType datatype);
|
||||
|
||||
/* TODO(manzanilla): to be removed with tiled implementation. */
|
||||
void setWidth(unsigned int width)
|
||||
{
|
||||
this->m_width = width;
|
||||
this->flags.is_resolution_set = true;
|
||||
canvas_.xmax = canvas_.xmin + width;
|
||||
this->flags.is_canvas_set = true;
|
||||
}
|
||||
void setHeight(unsigned int height)
|
||||
{
|
||||
this->m_height = height;
|
||||
this->flags.is_resolution_set = true;
|
||||
canvas_.ymax = canvas_.ymin + height;
|
||||
this->flags.is_canvas_set = true;
|
||||
}
|
||||
|
||||
SocketReader *getInputSocketReader(unsigned int inputSocketindex);
|
||||
NodeOperation *getInputOperation(unsigned int inputSocketindex);
|
||||
|
||||
|
@@ -33,6 +33,7 @@
|
||||
#include "COM_SetValueOperation.h"
|
||||
#include "COM_SetVectorOperation.h"
|
||||
#include "COM_SocketProxyOperation.h"
|
||||
#include "COM_TranslateOperation.h"
|
||||
#include "COM_ViewerOperation.h"
|
||||
#include "COM_WriteBufferOperation.h"
|
||||
|
||||
@@ -109,7 +110,7 @@ void NodeOperationBuilder::convertToOperations(ExecutionSystem *system)
|
||||
folder.fold_operations();
|
||||
}
|
||||
|
||||
determineResolutions();
|
||||
determine_canvases();
|
||||
|
||||
if (m_context->get_execution_model() == eExecutionModel::Tiled) {
|
||||
/* surround complex ops with read/write buffer */
|
||||
@@ -417,41 +418,40 @@ void NodeOperationBuilder::resolve_proxies()
|
||||
}
|
||||
}
|
||||
|
||||
void NodeOperationBuilder::determineResolutions()
|
||||
void NodeOperationBuilder::determine_canvases()
|
||||
{
|
||||
/* determine all resolutions of the operations (Width/Height) */
|
||||
/* Determine all canvas areas of the operations. */
|
||||
const rcti &preferred_area = COM_AREA_NONE;
|
||||
for (NodeOperation *op : m_operations) {
|
||||
if (op->isOutputOperation(m_context->isRendering()) && !op->get_flags().is_preview_operation) {
|
||||
unsigned int resolution[2] = {0, 0};
|
||||
unsigned int preferredResolution[2] = {0, 0};
|
||||
op->determineResolution(resolution, preferredResolution);
|
||||
op->setResolution(resolution);
|
||||
rcti canvas = COM_AREA_NONE;
|
||||
op->determine_canvas(preferred_area, canvas);
|
||||
op->set_canvas(canvas);
|
||||
}
|
||||
}
|
||||
|
||||
for (NodeOperation *op : m_operations) {
|
||||
if (op->isOutputOperation(m_context->isRendering()) && op->get_flags().is_preview_operation) {
|
||||
unsigned int resolution[2] = {0, 0};
|
||||
unsigned int preferredResolution[2] = {0, 0};
|
||||
op->determineResolution(resolution, preferredResolution);
|
||||
op->setResolution(resolution);
|
||||
rcti canvas = COM_AREA_NONE;
|
||||
op->determine_canvas(preferred_area, canvas);
|
||||
op->set_canvas(canvas);
|
||||
}
|
||||
}
|
||||
|
||||
/* add convert resolution operations when needed */
|
||||
/* Convert operation canvases when needed. */
|
||||
{
|
||||
Vector<Link> convert_links;
|
||||
for (const Link &link : m_links) {
|
||||
if (link.to()->getResizeMode() != ResizeMode::None) {
|
||||
NodeOperation &from_op = link.from()->getOperation();
|
||||
NodeOperation &to_op = link.to()->getOperation();
|
||||
if (from_op.getWidth() != to_op.getWidth() || from_op.getHeight() != to_op.getHeight()) {
|
||||
if (!BLI_rcti_compare(&from_op.get_canvas(), &to_op.get_canvas())) {
|
||||
convert_links.append(link);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (const Link &link : convert_links) {
|
||||
COM_convert_resolution(*this, link.from(), link.to());
|
||||
COM_convert_canvas(*this, link.from(), link.to());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -145,8 +145,8 @@ class NodeOperationBuilder {
|
||||
/** Replace proxy operations with direct links */
|
||||
void resolve_proxies();
|
||||
|
||||
/** Calculate resolution for each operation */
|
||||
void determineResolutions();
|
||||
/** Calculate canvas area for each operation. */
|
||||
void determine_canvases();
|
||||
|
||||
/** Helper function to store connected inputs for replacement */
|
||||
Vector<NodeOperationInput *> cache_output_links(NodeOperationOutput *output) const;
|
||||
|
@@ -76,9 +76,17 @@ void SharedOperationBuffers::register_read(NodeOperation *read_op)
|
||||
/**
|
||||
* Get registered areas given operation needs to render.
|
||||
*/
|
||||
blender::Span<rcti> SharedOperationBuffers::get_areas_to_render(NodeOperation *op)
|
||||
Vector<rcti> SharedOperationBuffers::get_areas_to_render(NodeOperation *op,
|
||||
const int offset_x,
|
||||
const int offset_y)
|
||||
{
|
||||
return get_buffer_data(op).render_areas.as_span();
|
||||
Span<rcti> render_areas = get_buffer_data(op).render_areas.as_span();
|
||||
Vector<rcti> dst_areas;
|
||||
for (rcti dst : render_areas) {
|
||||
BLI_rcti_translate(&dst, offset_x, offset_y);
|
||||
dst_areas.append(std::move(dst));
|
||||
}
|
||||
return dst_areas;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -53,7 +53,7 @@ class SharedOperationBuffers {
|
||||
bool has_registered_reads(NodeOperation *op);
|
||||
void register_read(NodeOperation *read_op);
|
||||
|
||||
blender::Span<rcti> get_areas_to_render(NodeOperation *op);
|
||||
Vector<rcti> get_areas_to_render(NodeOperation *op, int offset_x, int offset_y);
|
||||
bool is_operation_rendered(NodeOperation *op);
|
||||
void set_rendered_buffer(NodeOperation *op, std::unique_ptr<MemoryBuffer> buffer);
|
||||
MemoryBuffer *get_rendered_buffer(NodeOperation *op);
|
||||
|
@@ -51,13 +51,13 @@ void AlphaOverNode::convertToOperations(NodeConverter &converter,
|
||||
|
||||
convertProg->setUseValueAlphaMultiply(false);
|
||||
if (color1Socket->isLinked()) {
|
||||
convertProg->setResolutionInputSocketIndex(1);
|
||||
convertProg->set_canvas_input_index(1);
|
||||
}
|
||||
else if (color2Socket->isLinked()) {
|
||||
convertProg->setResolutionInputSocketIndex(2);
|
||||
convertProg->set_canvas_input_index(2);
|
||||
}
|
||||
else {
|
||||
convertProg->setResolutionInputSocketIndex(0);
|
||||
convertProg->set_canvas_input_index(0);
|
||||
}
|
||||
|
||||
converter.addOperation(convertProg);
|
||||
|
@@ -62,7 +62,7 @@ void BoxMaskNode::convertToOperations(NodeConverter &converter,
|
||||
scaleOperation->setOffset(0.0f, 0.0f);
|
||||
scaleOperation->setNewWidth(rd->xsch * render_size_factor);
|
||||
scaleOperation->setNewHeight(rd->ysch * render_size_factor);
|
||||
scaleOperation->getInputSocket(0)->setResizeMode(ResizeMode::None);
|
||||
scaleOperation->getInputSocket(0)->setResizeMode(ResizeMode::Align);
|
||||
converter.addOperation(scaleOperation);
|
||||
|
||||
converter.addLink(valueOperation->getOutputSocket(0), scaleOperation->getInputSocket(0));
|
||||
|
@@ -37,16 +37,16 @@ void CombineColorNode::convertToOperations(NodeConverter &converter,
|
||||
|
||||
CombineChannelsOperation *operation = new CombineChannelsOperation();
|
||||
if (inputRSocket->isLinked()) {
|
||||
operation->setResolutionInputSocketIndex(0);
|
||||
operation->set_canvas_input_index(0);
|
||||
}
|
||||
else if (inputGSocket->isLinked()) {
|
||||
operation->setResolutionInputSocketIndex(1);
|
||||
operation->set_canvas_input_index(1);
|
||||
}
|
||||
else if (inputBSocket->isLinked()) {
|
||||
operation->setResolutionInputSocketIndex(2);
|
||||
operation->set_canvas_input_index(2);
|
||||
}
|
||||
else {
|
||||
operation->setResolutionInputSocketIndex(3);
|
||||
operation->set_canvas_input_index(3);
|
||||
}
|
||||
converter.addOperation(operation);
|
||||
|
||||
|
@@ -62,7 +62,7 @@ void EllipseMaskNode::convertToOperations(NodeConverter &converter,
|
||||
scaleOperation->setOffset(0.0f, 0.0f);
|
||||
scaleOperation->setNewWidth(rd->xsch * render_size_factor);
|
||||
scaleOperation->setNewHeight(rd->ysch * render_size_factor);
|
||||
scaleOperation->getInputSocket(0)->setResizeMode(ResizeMode::None);
|
||||
scaleOperation->getInputSocket(0)->setResizeMode(ResizeMode::Align);
|
||||
converter.addOperation(scaleOperation);
|
||||
|
||||
converter.addLink(valueOperation->getOutputSocket(0), scaleOperation->getInputSocket(0));
|
||||
|
@@ -66,7 +66,7 @@ void GlareNode::convertToOperations(NodeConverter &converter,
|
||||
mixvalueoperation->setValue(glare->mix);
|
||||
|
||||
MixGlareOperation *mixoperation = new MixGlareOperation();
|
||||
mixoperation->setResolutionInputSocketIndex(1);
|
||||
mixoperation->set_canvas_input_index(1);
|
||||
mixoperation->getInputSocket(2)->setResizeMode(ResizeMode::FitAny);
|
||||
|
||||
converter.addOperation(glareoperation);
|
||||
|
@@ -53,7 +53,7 @@ void HueSaturationValueCorrectNode::convertToOperations(
|
||||
converter.addOperation(changeHSV);
|
||||
|
||||
MixBlendOperation *blend = new MixBlendOperation();
|
||||
blend->setResolutionInputSocketIndex(1);
|
||||
blend->set_canvas_input_index(1);
|
||||
converter.addOperation(blend);
|
||||
|
||||
converter.mapInputSocket(colorSocket, rgbToHSV->getInputSocket(0));
|
||||
|
@@ -56,7 +56,7 @@ void HueSaturationValueNode::convertToOperations(NodeConverter &converter,
|
||||
converter.addOperation(changeHSV);
|
||||
|
||||
MixBlendOperation *blend = new MixBlendOperation();
|
||||
blend->setResolutionInputSocketIndex(1);
|
||||
blend->set_canvas_input_index(1);
|
||||
converter.addOperation(blend);
|
||||
|
||||
converter.mapInputSocket(colorSocket, rgbToHSV->getInputSocket(0));
|
||||
|
@@ -34,7 +34,7 @@ void MapUVNode::convertToOperations(NodeConverter &converter,
|
||||
|
||||
MapUVOperation *operation = new MapUVOperation();
|
||||
operation->setAlpha((float)node->custom1);
|
||||
operation->setResolutionInputSocketIndex(1);
|
||||
operation->set_canvas_input_index(1);
|
||||
converter.addOperation(operation);
|
||||
|
||||
converter.mapInputSocket(getInputSocket(0), operation->getInputSocket(0));
|
||||
|
@@ -30,20 +30,31 @@ RotateNode::RotateNode(bNode *editorNode) : Node(editorNode)
|
||||
}
|
||||
|
||||
void RotateNode::convertToOperations(NodeConverter &converter,
|
||||
const CompositorContext & /*context*/) const
|
||||
const CompositorContext &context) const
|
||||
{
|
||||
NodeInput *inputSocket = this->getInputSocket(0);
|
||||
NodeInput *inputDegreeSocket = this->getInputSocket(1);
|
||||
NodeOutput *outputSocket = this->getOutputSocket(0);
|
||||
RotateOperation *operation = new RotateOperation();
|
||||
SetSamplerOperation *sampler = new SetSamplerOperation();
|
||||
sampler->setSampler((PixelSampler)this->getbNode()->custom1);
|
||||
|
||||
converter.addOperation(sampler);
|
||||
converter.addOperation(operation);
|
||||
|
||||
converter.addLink(sampler->getOutputSocket(), operation->getInputSocket(0));
|
||||
converter.mapInputSocket(inputSocket, sampler->getInputSocket(0));
|
||||
PixelSampler sampler = (PixelSampler)this->getbNode()->custom1;
|
||||
switch (context.get_execution_model()) {
|
||||
case eExecutionModel::Tiled: {
|
||||
SetSamplerOperation *sampler_op = new SetSamplerOperation();
|
||||
sampler_op->setSampler(sampler);
|
||||
converter.addOperation(sampler_op);
|
||||
converter.addLink(sampler_op->getOutputSocket(), operation->getInputSocket(0));
|
||||
converter.mapInputSocket(inputSocket, sampler_op->getInputSocket(0));
|
||||
break;
|
||||
}
|
||||
case eExecutionModel::FullFrame: {
|
||||
operation->set_sampler(sampler);
|
||||
converter.mapInputSocket(inputSocket, operation->getInputSocket(0));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
converter.mapInputSocket(inputDegreeSocket, operation->getInputSocket(1));
|
||||
converter.mapOutputSocket(outputSocket, operation->getOutputSocket(0));
|
||||
}
|
||||
|
@@ -81,7 +81,7 @@ void ScaleNode::convertToOperations(NodeConverter &converter,
|
||||
operation->setOffset(bnode->custom3, bnode->custom4);
|
||||
operation->setNewWidth(rd->xsch * render_size_factor);
|
||||
operation->setNewHeight(rd->ysch * render_size_factor);
|
||||
operation->getInputSocket(0)->setResizeMode(ResizeMode::None);
|
||||
operation->getInputSocket(0)->setResizeMode(ResizeMode::Align);
|
||||
converter.addOperation(operation);
|
||||
|
||||
converter.mapInputSocket(inputSocket, operation->getInputSocket(0));
|
||||
|
@@ -39,7 +39,7 @@ void SetAlphaNode::convertToOperations(NodeConverter &converter,
|
||||
}
|
||||
|
||||
if (!this->getInputSocket(0)->isLinked() && this->getInputSocket(1)->isLinked()) {
|
||||
operation->setResolutionInputSocketIndex(1);
|
||||
operation->set_canvas_input_index(1);
|
||||
}
|
||||
|
||||
converter.addOperation(operation);
|
||||
|
@@ -22,6 +22,7 @@
|
||||
#include "COM_RotateOperation.h"
|
||||
#include "COM_ScaleOperation.h"
|
||||
#include "COM_SetSamplerOperation.h"
|
||||
#include "COM_TransformOperation.h"
|
||||
#include "COM_TranslateOperation.h"
|
||||
|
||||
#include "BKE_tracking.h"
|
||||
@@ -42,18 +43,12 @@ void Stabilize2dNode::convertToOperations(NodeConverter &converter,
|
||||
NodeInput *imageInput = this->getInputSocket(0);
|
||||
MovieClip *clip = (MovieClip *)editorNode->id;
|
||||
bool invert = (editorNode->custom2 & CMP_NODEFLAG_STABILIZE_INVERSE) != 0;
|
||||
const PixelSampler sampler = (PixelSampler)editorNode->custom1;
|
||||
|
||||
ScaleRelativeOperation *scaleOperation = new ScaleRelativeOperation();
|
||||
scaleOperation->setSampler((PixelSampler)editorNode->custom1);
|
||||
RotateOperation *rotateOperation = new RotateOperation();
|
||||
rotateOperation->setDoDegree2RadConversion(false);
|
||||
TranslateOperation *translateOperation = new TranslateOperation();
|
||||
MovieClipAttributeOperation *scaleAttribute = new MovieClipAttributeOperation();
|
||||
MovieClipAttributeOperation *angleAttribute = new MovieClipAttributeOperation();
|
||||
MovieClipAttributeOperation *xAttribute = new MovieClipAttributeOperation();
|
||||
MovieClipAttributeOperation *yAttribute = new MovieClipAttributeOperation();
|
||||
SetSamplerOperation *psoperation = new SetSamplerOperation();
|
||||
psoperation->setSampler((PixelSampler)editorNode->custom1);
|
||||
|
||||
scaleAttribute->setAttribute(MCA_SCALE);
|
||||
scaleAttribute->setFramenumber(context.getFramenumber());
|
||||
@@ -79,38 +74,67 @@ void Stabilize2dNode::convertToOperations(NodeConverter &converter,
|
||||
converter.addOperation(angleAttribute);
|
||||
converter.addOperation(xAttribute);
|
||||
converter.addOperation(yAttribute);
|
||||
converter.addOperation(scaleOperation);
|
||||
converter.addOperation(translateOperation);
|
||||
converter.addOperation(rotateOperation);
|
||||
converter.addOperation(psoperation);
|
||||
|
||||
converter.addLink(scaleAttribute->getOutputSocket(), scaleOperation->getInputSocket(1));
|
||||
converter.addLink(scaleAttribute->getOutputSocket(), scaleOperation->getInputSocket(2));
|
||||
switch (context.get_execution_model()) {
|
||||
case eExecutionModel::Tiled: {
|
||||
ScaleRelativeOperation *scaleOperation = new ScaleRelativeOperation();
|
||||
scaleOperation->setSampler(sampler);
|
||||
RotateOperation *rotateOperation = new RotateOperation();
|
||||
rotateOperation->setDoDegree2RadConversion(false);
|
||||
TranslateOperation *translateOperation = new TranslateOperation();
|
||||
SetSamplerOperation *psoperation = new SetSamplerOperation();
|
||||
psoperation->setSampler(sampler);
|
||||
|
||||
converter.addLink(angleAttribute->getOutputSocket(), rotateOperation->getInputSocket(1));
|
||||
converter.addOperation(scaleOperation);
|
||||
converter.addOperation(translateOperation);
|
||||
converter.addOperation(rotateOperation);
|
||||
converter.addOperation(psoperation);
|
||||
|
||||
converter.addLink(xAttribute->getOutputSocket(), translateOperation->getInputSocket(1));
|
||||
converter.addLink(yAttribute->getOutputSocket(), translateOperation->getInputSocket(2));
|
||||
converter.addLink(scaleAttribute->getOutputSocket(), scaleOperation->getInputSocket(1));
|
||||
converter.addLink(scaleAttribute->getOutputSocket(), scaleOperation->getInputSocket(2));
|
||||
|
||||
converter.mapOutputSocket(getOutputSocket(), psoperation->getOutputSocket());
|
||||
converter.addLink(angleAttribute->getOutputSocket(), rotateOperation->getInputSocket(1));
|
||||
|
||||
if (invert) {
|
||||
// Translate -> Rotate -> Scale.
|
||||
converter.mapInputSocket(imageInput, translateOperation->getInputSocket(0));
|
||||
converter.addLink(xAttribute->getOutputSocket(), translateOperation->getInputSocket(1));
|
||||
converter.addLink(yAttribute->getOutputSocket(), translateOperation->getInputSocket(2));
|
||||
|
||||
converter.addLink(translateOperation->getOutputSocket(), rotateOperation->getInputSocket(0));
|
||||
converter.addLink(rotateOperation->getOutputSocket(), scaleOperation->getInputSocket(0));
|
||||
converter.mapOutputSocket(getOutputSocket(), psoperation->getOutputSocket());
|
||||
|
||||
converter.addLink(scaleOperation->getOutputSocket(), psoperation->getInputSocket(0));
|
||||
}
|
||||
else {
|
||||
// Scale -> Rotate -> Translate.
|
||||
converter.mapInputSocket(imageInput, scaleOperation->getInputSocket(0));
|
||||
if (invert) {
|
||||
// Translate -> Rotate -> Scale.
|
||||
converter.mapInputSocket(imageInput, translateOperation->getInputSocket(0));
|
||||
|
||||
converter.addLink(scaleOperation->getOutputSocket(), rotateOperation->getInputSocket(0));
|
||||
converter.addLink(rotateOperation->getOutputSocket(), translateOperation->getInputSocket(0));
|
||||
converter.addLink(translateOperation->getOutputSocket(),
|
||||
rotateOperation->getInputSocket(0));
|
||||
converter.addLink(rotateOperation->getOutputSocket(), scaleOperation->getInputSocket(0));
|
||||
|
||||
converter.addLink(translateOperation->getOutputSocket(), psoperation->getInputSocket(0));
|
||||
converter.addLink(scaleOperation->getOutputSocket(), psoperation->getInputSocket(0));
|
||||
}
|
||||
else {
|
||||
// Scale -> Rotate -> Translate.
|
||||
converter.mapInputSocket(imageInput, scaleOperation->getInputSocket(0));
|
||||
|
||||
converter.addLink(scaleOperation->getOutputSocket(), rotateOperation->getInputSocket(0));
|
||||
converter.addLink(rotateOperation->getOutputSocket(),
|
||||
translateOperation->getInputSocket(0));
|
||||
|
||||
converter.addLink(translateOperation->getOutputSocket(), psoperation->getInputSocket(0));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case eExecutionModel::FullFrame: {
|
||||
TransformOperation *transform_op = new TransformOperation();
|
||||
transform_op->set_sampler(sampler);
|
||||
transform_op->set_convert_rotate_degree_to_rad(false);
|
||||
transform_op->set_invert(invert);
|
||||
converter.addOperation(transform_op);
|
||||
converter.mapInputSocket(imageInput, transform_op->getInputSocket(0));
|
||||
converter.addLink(xAttribute->getOutputSocket(), transform_op->getInputSocket(1));
|
||||
converter.addLink(yAttribute->getOutputSocket(), transform_op->getInputSocket(2));
|
||||
converter.addLink(angleAttribute->getOutputSocket(), transform_op->getInputSocket(3));
|
||||
converter.addLink(scaleAttribute->getOutputSocket(), transform_op->getInputSocket(4));
|
||||
converter.mapOutputSocket(getOutputSocket(), transform_op->getOutputSocket());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -22,6 +22,7 @@
|
||||
#include "COM_ScaleOperation.h"
|
||||
#include "COM_SetSamplerOperation.h"
|
||||
#include "COM_SetValueOperation.h"
|
||||
#include "COM_TransformOperation.h"
|
||||
#include "COM_TranslateOperation.h"
|
||||
|
||||
namespace blender::compositor {
|
||||
@@ -32,7 +33,7 @@ TransformNode::TransformNode(bNode *editorNode) : Node(editorNode)
|
||||
}
|
||||
|
||||
void TransformNode::convertToOperations(NodeConverter &converter,
|
||||
const CompositorContext & /*context*/) const
|
||||
const CompositorContext &context) const
|
||||
{
|
||||
NodeInput *imageInput = this->getInputSocket(0);
|
||||
NodeInput *xInput = this->getInputSocket(1);
|
||||
@@ -40,33 +41,51 @@ void TransformNode::convertToOperations(NodeConverter &converter,
|
||||
NodeInput *angleInput = this->getInputSocket(3);
|
||||
NodeInput *scaleInput = this->getInputSocket(4);
|
||||
|
||||
ScaleRelativeOperation *scaleOperation = new ScaleRelativeOperation();
|
||||
converter.addOperation(scaleOperation);
|
||||
switch (context.get_execution_model()) {
|
||||
case eExecutionModel::Tiled: {
|
||||
ScaleRelativeOperation *scaleOperation = new ScaleRelativeOperation();
|
||||
converter.addOperation(scaleOperation);
|
||||
|
||||
RotateOperation *rotateOperation = new RotateOperation();
|
||||
rotateOperation->setDoDegree2RadConversion(false);
|
||||
converter.addOperation(rotateOperation);
|
||||
RotateOperation *rotateOperation = new RotateOperation();
|
||||
rotateOperation->setDoDegree2RadConversion(false);
|
||||
converter.addOperation(rotateOperation);
|
||||
|
||||
TranslateOperation *translateOperation = new TranslateOperation();
|
||||
converter.addOperation(translateOperation);
|
||||
TranslateOperation *translateOperation = new TranslateOperation();
|
||||
converter.addOperation(translateOperation);
|
||||
|
||||
SetSamplerOperation *sampler = new SetSamplerOperation();
|
||||
sampler->setSampler((PixelSampler)this->getbNode()->custom1);
|
||||
converter.addOperation(sampler);
|
||||
SetSamplerOperation *sampler = new SetSamplerOperation();
|
||||
sampler->setSampler((PixelSampler)this->getbNode()->custom1);
|
||||
converter.addOperation(sampler);
|
||||
|
||||
converter.mapInputSocket(imageInput, sampler->getInputSocket(0));
|
||||
converter.addLink(sampler->getOutputSocket(), scaleOperation->getInputSocket(0));
|
||||
converter.mapInputSocket(scaleInput, scaleOperation->getInputSocket(1));
|
||||
converter.mapInputSocket(scaleInput, scaleOperation->getInputSocket(2)); // xscale = yscale
|
||||
converter.mapInputSocket(imageInput, sampler->getInputSocket(0));
|
||||
converter.addLink(sampler->getOutputSocket(), scaleOperation->getInputSocket(0));
|
||||
converter.mapInputSocket(scaleInput, scaleOperation->getInputSocket(1));
|
||||
converter.mapInputSocket(scaleInput, scaleOperation->getInputSocket(2)); // xscale = yscale
|
||||
|
||||
converter.addLink(scaleOperation->getOutputSocket(), rotateOperation->getInputSocket(0));
|
||||
converter.mapInputSocket(angleInput, rotateOperation->getInputSocket(1));
|
||||
converter.addLink(scaleOperation->getOutputSocket(), rotateOperation->getInputSocket(0));
|
||||
converter.mapInputSocket(angleInput, rotateOperation->getInputSocket(1));
|
||||
|
||||
converter.addLink(rotateOperation->getOutputSocket(), translateOperation->getInputSocket(0));
|
||||
converter.mapInputSocket(xInput, translateOperation->getInputSocket(1));
|
||||
converter.mapInputSocket(yInput, translateOperation->getInputSocket(2));
|
||||
converter.addLink(rotateOperation->getOutputSocket(), translateOperation->getInputSocket(0));
|
||||
converter.mapInputSocket(xInput, translateOperation->getInputSocket(1));
|
||||
converter.mapInputSocket(yInput, translateOperation->getInputSocket(2));
|
||||
|
||||
converter.mapOutputSocket(getOutputSocket(), translateOperation->getOutputSocket());
|
||||
converter.mapOutputSocket(getOutputSocket(), translateOperation->getOutputSocket());
|
||||
break;
|
||||
}
|
||||
case eExecutionModel::FullFrame: {
|
||||
TransformOperation *op = new TransformOperation();
|
||||
op->set_sampler((PixelSampler)this->getbNode()->custom1);
|
||||
converter.addOperation(op);
|
||||
|
||||
converter.mapInputSocket(imageInput, op->getInputSocket(0));
|
||||
converter.mapInputSocket(xInput, op->getInputSocket(1));
|
||||
converter.mapInputSocket(yInput, op->getInputSocket(2));
|
||||
converter.mapInputSocket(angleInput, op->getInputSocket(3));
|
||||
converter.mapInputSocket(scaleInput, op->getInputSocket(4));
|
||||
converter.mapOutputSocket(getOutputSocket(), op->getOutputSocket());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace blender::compositor
|
||||
|
@@ -41,7 +41,9 @@ void TranslateNode::convertToOperations(NodeConverter &converter,
|
||||
NodeInput *inputYSocket = this->getInputSocket(2);
|
||||
NodeOutput *outputSocket = this->getOutputSocket(0);
|
||||
|
||||
TranslateOperation *operation = new TranslateOperation();
|
||||
TranslateOperation *operation = context.get_execution_model() == eExecutionModel::Tiled ?
|
||||
new TranslateOperation() :
|
||||
new TranslateCanvasOperation();
|
||||
operation->set_wrapping(data->wrap_axis);
|
||||
if (data->relative) {
|
||||
const RenderData *rd = context.getRenderData();
|
||||
|
@@ -60,10 +60,10 @@ void ViewerNode::convertToOperations(NodeConverter &converter,
|
||||
viewerOperation->setViewSettings(context.getViewSettings());
|
||||
viewerOperation->setDisplaySettings(context.getDisplaySettings());
|
||||
|
||||
viewerOperation->setResolutionInputSocketIndex(0);
|
||||
viewerOperation->set_canvas_input_index(0);
|
||||
if (!imageSocket->isLinked()) {
|
||||
if (alphaSocket->isLinked()) {
|
||||
viewerOperation->setResolutionInputSocketIndex(1);
|
||||
viewerOperation->set_canvas_input_index(1);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -17,6 +17,8 @@
|
||||
*/
|
||||
|
||||
#include "COM_BlurBaseOperation.h"
|
||||
#include "COM_ConstantOperation.h"
|
||||
|
||||
#include "BLI_math.h"
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
@@ -36,11 +38,15 @@ BlurBaseOperation::BlurBaseOperation(DataType data_type)
|
||||
this->m_size = 1.0f;
|
||||
this->m_sizeavailable = false;
|
||||
this->m_extend_bounds = false;
|
||||
use_variable_size_ = false;
|
||||
}
|
||||
void BlurBaseOperation::initExecution()
|
||||
|
||||
void BlurBaseOperation::init_data()
|
||||
{
|
||||
this->m_inputProgram = this->getInputSocketReader(0);
|
||||
this->m_inputSize = this->getInputSocketReader(1);
|
||||
if (execution_model_ == eExecutionModel::FullFrame) {
|
||||
updateSize();
|
||||
}
|
||||
|
||||
this->m_data.image_in_width = this->getWidth();
|
||||
this->m_data.image_in_height = this->getHeight();
|
||||
if (this->m_data.relative) {
|
||||
@@ -61,6 +67,12 @@ void BlurBaseOperation::initExecution()
|
||||
this->m_data.sizex = round_fl_to_int(this->m_data.percentx * 0.01f * sizex);
|
||||
this->m_data.sizey = round_fl_to_int(this->m_data.percenty * 0.01f * sizey);
|
||||
}
|
||||
}
|
||||
|
||||
void BlurBaseOperation::initExecution()
|
||||
{
|
||||
this->m_inputProgram = this->getInputSocketReader(0);
|
||||
this->m_inputSize = this->getInputSocketReader(1);
|
||||
|
||||
QualityStepHelper::initExecution(COM_QH_MULTIPLY);
|
||||
}
|
||||
@@ -165,23 +177,86 @@ void BlurBaseOperation::setData(const NodeBlurData *data)
|
||||
memcpy(&m_data, data, sizeof(NodeBlurData));
|
||||
}
|
||||
|
||||
int BlurBaseOperation::get_blur_size(eDimension dim) const
|
||||
{
|
||||
switch (dim) {
|
||||
case eDimension::X:
|
||||
return m_data.sizex;
|
||||
case eDimension::Y:
|
||||
return m_data.sizey;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
void BlurBaseOperation::updateSize()
|
||||
{
|
||||
if (!this->m_sizeavailable) {
|
||||
float result[4];
|
||||
this->getInputSocketReader(1)->readSampled(result, 0, 0, PixelSampler::Nearest);
|
||||
this->m_size = result[0];
|
||||
this->m_sizeavailable = true;
|
||||
if (this->m_sizeavailable || use_variable_size_) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (execution_model_) {
|
||||
case eExecutionModel::Tiled: {
|
||||
float result[4];
|
||||
this->getInputSocketReader(1)->readSampled(result, 0, 0, PixelSampler::Nearest);
|
||||
this->m_size = result[0];
|
||||
break;
|
||||
}
|
||||
case eExecutionModel::FullFrame: {
|
||||
NodeOperation *size_input = get_input_operation(SIZE_INPUT_INDEX);
|
||||
if (size_input->get_flags().is_constant_operation) {
|
||||
m_size = *static_cast<ConstantOperation *>(size_input)->get_constant_elem();
|
||||
} /* Else use default. */
|
||||
break;
|
||||
}
|
||||
}
|
||||
this->m_sizeavailable = true;
|
||||
}
|
||||
|
||||
static int round_to_even(float value)
|
||||
{
|
||||
return ceilf(value * 0.5f) * 2.0f;
|
||||
}
|
||||
|
||||
void BlurBaseOperation::determine_canvas(const rcti &preferred_area, rcti &r_area)
|
||||
{
|
||||
if (!m_extend_bounds) {
|
||||
NodeOperation::determine_canvas(preferred_area, r_area);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (execution_model_) {
|
||||
case eExecutionModel::Tiled: {
|
||||
NodeOperation::determine_canvas(preferred_area, r_area);
|
||||
r_area.xmax += 2 * m_size * m_data.sizex;
|
||||
r_area.ymax += 2 * m_size * m_data.sizey;
|
||||
break;
|
||||
}
|
||||
case eExecutionModel::FullFrame: {
|
||||
/* Setting a modifier ensures all non main inputs have extended bounds as preferred
|
||||
* canvas, avoiding unnecessary canvas convertions that would hide constant
|
||||
* operations. */
|
||||
set_determined_canvas_modifier([=](rcti &canvas) {
|
||||
/* Rounding to even prevents jiggling in backdrop while switching size values. */
|
||||
canvas.xmax += round_to_even(2 * m_size * m_data.sizex);
|
||||
canvas.ymax += round_to_even(2 * m_size * m_data.sizey);
|
||||
});
|
||||
NodeOperation::determine_canvas(preferred_area, r_area);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BlurBaseOperation::determineResolution(unsigned int resolution[2],
|
||||
unsigned int preferredResolution[2])
|
||||
void BlurBaseOperation::get_area_of_interest(const int input_idx,
|
||||
const rcti &output_area,
|
||||
rcti &r_input_area)
|
||||
{
|
||||
NodeOperation::determineResolution(resolution, preferredResolution);
|
||||
if (this->m_extend_bounds) {
|
||||
resolution[0] += 2 * this->m_size * m_data.sizex;
|
||||
resolution[1] += 2 * this->m_size * m_data.sizey;
|
||||
switch (input_idx) {
|
||||
case 0:
|
||||
r_input_area = output_area;
|
||||
break;
|
||||
case 1:
|
||||
r_input_area = use_variable_size_ ? output_area : COM_CONSTANT_INPUT_AREA_OF_INTEREST;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -18,7 +18,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "COM_NodeOperation.h"
|
||||
#include "COM_MultiThreadedOperation.h"
|
||||
#include "COM_QualityStepHelper.h"
|
||||
|
||||
#define MAX_GAUSSTAB_RADIUS 30000
|
||||
@@ -27,10 +27,16 @@
|
||||
|
||||
namespace blender::compositor {
|
||||
|
||||
class BlurBaseOperation : public NodeOperation, public QualityStepHelper {
|
||||
class BlurBaseOperation : public MultiThreadedOperation, public QualityStepHelper {
|
||||
private:
|
||||
bool m_extend_bounds;
|
||||
|
||||
protected:
|
||||
BlurBaseOperation(DataType data_type);
|
||||
static constexpr int IMAGE_INPUT_INDEX = 0;
|
||||
static constexpr int SIZE_INPUT_INDEX = 1;
|
||||
|
||||
protected:
|
||||
BlurBaseOperation(DataType data_type8);
|
||||
float *make_gausstab(float rad, int size);
|
||||
#ifdef BLI_HAVE_SSE2
|
||||
__m128 *convert_gausstab_sse(const float *gausstab, int size);
|
||||
@@ -49,9 +55,11 @@ class BlurBaseOperation : public NodeOperation, public QualityStepHelper {
|
||||
float m_size;
|
||||
bool m_sizeavailable;
|
||||
|
||||
bool m_extend_bounds;
|
||||
/* Flags for inheriting classes. */
|
||||
bool use_variable_size_;
|
||||
|
||||
public:
|
||||
virtual void init_data() override;
|
||||
/**
|
||||
* Initialize the execution
|
||||
*/
|
||||
@@ -75,8 +83,13 @@ class BlurBaseOperation : public NodeOperation, public QualityStepHelper {
|
||||
this->m_extend_bounds = extend_bounds;
|
||||
}
|
||||
|
||||
void determineResolution(unsigned int resolution[2],
|
||||
unsigned int preferredResolution[2]) override;
|
||||
int get_blur_size(eDimension dim) const;
|
||||
|
||||
void determine_canvas(const rcti &preferred_area, rcti &r_area) override;
|
||||
|
||||
virtual void get_area_of_interest(int input_idx,
|
||||
const rcti &output_area,
|
||||
rcti &r_input_area) override;
|
||||
};
|
||||
|
||||
} // namespace blender::compositor
|
||||
|
@@ -17,6 +17,8 @@
|
||||
*/
|
||||
|
||||
#include "COM_BokehBlurOperation.h"
|
||||
#include "COM_ConstantOperation.h"
|
||||
|
||||
#include "BLI_math.h"
|
||||
#include "COM_OpenCLDevice.h"
|
||||
|
||||
@@ -24,10 +26,15 @@
|
||||
|
||||
namespace blender::compositor {
|
||||
|
||||
constexpr int IMAGE_INPUT_INDEX = 0;
|
||||
constexpr int BOKEH_INPUT_INDEX = 1;
|
||||
constexpr int BOUNDING_BOX_INPUT_INDEX = 2;
|
||||
constexpr int SIZE_INPUT_INDEX = 3;
|
||||
|
||||
BokehBlurOperation::BokehBlurOperation()
|
||||
{
|
||||
this->addInputSocket(DataType::Color);
|
||||
this->addInputSocket(DataType::Color, ResizeMode::None);
|
||||
this->addInputSocket(DataType::Color, ResizeMode::Align);
|
||||
this->addInputSocket(DataType::Value);
|
||||
this->addInputSocket(DataType::Value);
|
||||
this->addOutputSocket(DataType::Color);
|
||||
@@ -44,6 +51,23 @@ BokehBlurOperation::BokehBlurOperation()
|
||||
this->m_extend_bounds = false;
|
||||
}
|
||||
|
||||
void BokehBlurOperation::init_data()
|
||||
{
|
||||
if (execution_model_ == eExecutionModel::FullFrame) {
|
||||
updateSize();
|
||||
}
|
||||
|
||||
NodeOperation *bokeh = get_input_operation(BOKEH_INPUT_INDEX);
|
||||
const int width = bokeh->getWidth();
|
||||
const int height = bokeh->getHeight();
|
||||
|
||||
const float dimension = MIN2(width, height);
|
||||
|
||||
m_bokehMidX = width / 2.0f;
|
||||
m_bokehMidY = height / 2.0f;
|
||||
m_bokehDimension = dimension / 2.0f;
|
||||
}
|
||||
|
||||
void *BokehBlurOperation::initializeTileData(rcti * /*rect*/)
|
||||
{
|
||||
lockMutex();
|
||||
@@ -58,18 +82,11 @@ void *BokehBlurOperation::initializeTileData(rcti * /*rect*/)
|
||||
void BokehBlurOperation::initExecution()
|
||||
{
|
||||
initMutex();
|
||||
|
||||
this->m_inputProgram = getInputSocketReader(0);
|
||||
this->m_inputBokehProgram = getInputSocketReader(1);
|
||||
this->m_inputBoundingBoxReader = getInputSocketReader(2);
|
||||
|
||||
int width = this->m_inputBokehProgram->getWidth();
|
||||
int height = this->m_inputBokehProgram->getHeight();
|
||||
|
||||
float dimension = MIN2(width, height);
|
||||
|
||||
this->m_bokehMidX = width / 2.0f;
|
||||
this->m_bokehMidY = height / 2.0f;
|
||||
this->m_bokehDimension = dimension / 2.0f;
|
||||
QualityStepHelper::initExecution(COM_QH_INCREASE);
|
||||
}
|
||||
|
||||
@@ -225,23 +242,147 @@ void BokehBlurOperation::executeOpenCL(OpenCLDevice *device,
|
||||
|
||||
void BokehBlurOperation::updateSize()
|
||||
{
|
||||
if (!this->m_sizeavailable) {
|
||||
float result[4];
|
||||
this->getInputSocketReader(3)->readSampled(result, 0, 0, PixelSampler::Nearest);
|
||||
this->m_size = result[0];
|
||||
CLAMP(this->m_size, 0.0f, 10.0f);
|
||||
this->m_sizeavailable = true;
|
||||
if (this->m_sizeavailable) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (execution_model_) {
|
||||
case eExecutionModel::Tiled: {
|
||||
float result[4];
|
||||
this->getInputSocketReader(3)->readSampled(result, 0, 0, PixelSampler::Nearest);
|
||||
this->m_size = result[0];
|
||||
CLAMP(this->m_size, 0.0f, 10.0f);
|
||||
break;
|
||||
}
|
||||
case eExecutionModel::FullFrame: {
|
||||
NodeOperation *size_input = get_input_operation(SIZE_INPUT_INDEX);
|
||||
if (size_input->get_flags().is_constant_operation) {
|
||||
m_size = *static_cast<ConstantOperation *>(size_input)->get_constant_elem();
|
||||
CLAMP(m_size, 0.0f, 10.0f);
|
||||
} /* Else use default. */
|
||||
break;
|
||||
}
|
||||
}
|
||||
this->m_sizeavailable = true;
|
||||
}
|
||||
|
||||
static float round_to_even(float value)
|
||||
{
|
||||
return roundf(value * 0.5f) * 2.0f;
|
||||
}
|
||||
|
||||
void BokehBlurOperation::determine_canvas(const rcti &preferred_area, rcti &r_area)
|
||||
{
|
||||
if (!m_extend_bounds) {
|
||||
NodeOperation::determine_canvas(preferred_area, r_area);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (execution_model_) {
|
||||
case eExecutionModel::Tiled: {
|
||||
NodeOperation::determine_canvas(preferred_area, r_area);
|
||||
const float max_dim = MAX2(BLI_rcti_size_x(&r_area), BLI_rcti_size_y(&r_area));
|
||||
r_area.xmax += 2 * this->m_size * max_dim / 100.0f;
|
||||
r_area.ymax += 2 * this->m_size * max_dim / 100.0f;
|
||||
break;
|
||||
}
|
||||
case eExecutionModel::FullFrame: {
|
||||
set_determined_canvas_modifier([=](rcti &canvas) {
|
||||
const float max_dim = MAX2(BLI_rcti_size_x(&canvas), BLI_rcti_size_y(&canvas));
|
||||
/* Rounding to even prevents image jiggling in backdrop while switching size values. */
|
||||
float add_size = round_to_even(2 * this->m_size * max_dim / 100.0f);
|
||||
canvas.xmax += add_size;
|
||||
canvas.ymax += add_size;
|
||||
});
|
||||
NodeOperation::determine_canvas(preferred_area, r_area);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BokehBlurOperation::determineResolution(unsigned int resolution[2],
|
||||
unsigned int preferredResolution[2])
|
||||
void BokehBlurOperation::get_area_of_interest(const int input_idx,
|
||||
const rcti &output_area,
|
||||
rcti &r_input_area)
|
||||
{
|
||||
NodeOperation::determineResolution(resolution, preferredResolution);
|
||||
if (this->m_extend_bounds) {
|
||||
const float max_dim = MAX2(resolution[0], resolution[1]);
|
||||
resolution[0] += 2 * this->m_size * max_dim / 100.0f;
|
||||
resolution[1] += 2 * this->m_size * max_dim / 100.0f;
|
||||
switch (input_idx) {
|
||||
case IMAGE_INPUT_INDEX: {
|
||||
const float max_dim = MAX2(this->getWidth(), this->getHeight());
|
||||
const float add_size = m_size * max_dim / 100.0f;
|
||||
r_input_area.xmin = output_area.xmin - add_size;
|
||||
r_input_area.xmax = output_area.xmax + add_size;
|
||||
r_input_area.ymin = output_area.ymin - add_size;
|
||||
r_input_area.ymax = output_area.ymax + add_size;
|
||||
break;
|
||||
}
|
||||
case BOKEH_INPUT_INDEX: {
|
||||
NodeOperation *bokeh_input = getInputOperation(BOKEH_INPUT_INDEX);
|
||||
r_input_area = bokeh_input->get_canvas();
|
||||
break;
|
||||
}
|
||||
case BOUNDING_BOX_INPUT_INDEX:
|
||||
r_input_area = output_area;
|
||||
break;
|
||||
case SIZE_INPUT_INDEX: {
|
||||
r_input_area = COM_CONSTANT_INPUT_AREA_OF_INTEREST;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BokehBlurOperation::update_memory_buffer_partial(MemoryBuffer *output,
|
||||
const rcti &area,
|
||||
Span<MemoryBuffer *> inputs)
|
||||
{
|
||||
const float max_dim = MAX2(this->getWidth(), this->getHeight());
|
||||
const int pixel_size = m_size * max_dim / 100.0f;
|
||||
const float m = m_bokehDimension / pixel_size;
|
||||
|
||||
const MemoryBuffer *image_input = inputs[IMAGE_INPUT_INDEX];
|
||||
const MemoryBuffer *bokeh_input = inputs[BOKEH_INPUT_INDEX];
|
||||
MemoryBuffer *bounding_input = inputs[BOUNDING_BOX_INPUT_INDEX];
|
||||
BuffersIterator<float> it = output->iterate_with({bounding_input}, area);
|
||||
const rcti &image_rect = image_input->get_rect();
|
||||
for (; !it.is_end(); ++it) {
|
||||
const int x = it.x;
|
||||
const int y = it.y;
|
||||
const float bounding_box = *it.in(0);
|
||||
if (bounding_box <= 0.0f) {
|
||||
image_input->read_elem(x, y, it.out);
|
||||
continue;
|
||||
}
|
||||
|
||||
float color_accum[4] = {0};
|
||||
float multiplier_accum[4] = {0};
|
||||
if (pixel_size < 2) {
|
||||
image_input->read_elem(x, y, color_accum);
|
||||
multiplier_accum[0] = 1.0f;
|
||||
multiplier_accum[1] = 1.0f;
|
||||
multiplier_accum[2] = 1.0f;
|
||||
multiplier_accum[3] = 1.0f;
|
||||
}
|
||||
const int miny = MAX2(y - pixel_size, image_rect.ymin);
|
||||
const int maxy = MIN2(y + pixel_size, image_rect.ymax);
|
||||
const int minx = MAX2(x - pixel_size, image_rect.xmin);
|
||||
const int maxx = MIN2(x + pixel_size, image_rect.xmax);
|
||||
const int step = getStep();
|
||||
const int elem_stride = image_input->elem_stride * step;
|
||||
const int row_stride = image_input->row_stride * step;
|
||||
const float *row_color = image_input->get_elem(minx, miny);
|
||||
for (int ny = miny; ny < maxy; ny += step, row_color += row_stride) {
|
||||
const float *color = row_color;
|
||||
const float v = m_bokehMidY - (ny - y) * m;
|
||||
for (int nx = minx; nx < maxx; nx += step, color += elem_stride) {
|
||||
const float u = m_bokehMidX - (nx - x) * m;
|
||||
float bokeh[4];
|
||||
bokeh_input->read_elem_checked(u, v, bokeh);
|
||||
madd_v4_v4v4(color_accum, bokeh, color);
|
||||
add_v4_v4(multiplier_accum, bokeh);
|
||||
}
|
||||
}
|
||||
it.out[0] = color_accum[0] * (1.0f / multiplier_accum[0]);
|
||||
it.out[1] = color_accum[1] * (1.0f / multiplier_accum[1]);
|
||||
it.out[2] = color_accum[2] * (1.0f / multiplier_accum[2]);
|
||||
it.out[3] = color_accum[3] * (1.0f / multiplier_accum[3]);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -18,12 +18,12 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "COM_NodeOperation.h"
|
||||
#include "COM_MultiThreadedOperation.h"
|
||||
#include "COM_QualityStepHelper.h"
|
||||
|
||||
namespace blender::compositor {
|
||||
|
||||
class BokehBlurOperation : public NodeOperation, public QualityStepHelper {
|
||||
class BokehBlurOperation : public MultiThreadedOperation, public QualityStepHelper {
|
||||
private:
|
||||
SocketReader *m_inputProgram;
|
||||
SocketReader *m_inputBokehProgram;
|
||||
@@ -31,6 +31,7 @@ class BokehBlurOperation : public NodeOperation, public QualityStepHelper {
|
||||
void updateSize();
|
||||
float m_size;
|
||||
bool m_sizeavailable;
|
||||
|
||||
float m_bokehMidX;
|
||||
float m_bokehMidY;
|
||||
float m_bokehDimension;
|
||||
@@ -39,6 +40,8 @@ class BokehBlurOperation : public NodeOperation, public QualityStepHelper {
|
||||
public:
|
||||
BokehBlurOperation();
|
||||
|
||||
void init_data() override;
|
||||
|
||||
void *initializeTileData(rcti *rect) override;
|
||||
/**
|
||||
* The inner loop of this operation.
|
||||
@@ -77,8 +80,12 @@ class BokehBlurOperation : public NodeOperation, public QualityStepHelper {
|
||||
this->m_extend_bounds = extend_bounds;
|
||||
}
|
||||
|
||||
void determineResolution(unsigned int resolution[2],
|
||||
unsigned int preferredResolution[2]) override;
|
||||
void determine_canvas(const rcti &preferred_area, rcti &r_area) override;
|
||||
|
||||
void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override;
|
||||
void update_memory_buffer_partial(MemoryBuffer *output,
|
||||
const rcti &area,
|
||||
Span<MemoryBuffer *> inputs) override;
|
||||
};
|
||||
|
||||
} // namespace blender::compositor
|
||||
|
@@ -145,11 +145,13 @@ void BokehImageOperation::deinitExecution()
|
||||
}
|
||||
}
|
||||
|
||||
void BokehImageOperation::determineResolution(unsigned int resolution[2],
|
||||
unsigned int /*preferredResolution*/[2])
|
||||
void BokehImageOperation::determine_canvas(const rcti &preferred_area, rcti &r_area)
|
||||
{
|
||||
resolution[0] = COM_BLUR_BOKEH_PIXELS;
|
||||
resolution[1] = COM_BLUR_BOKEH_PIXELS;
|
||||
BLI_rcti_init(&r_area,
|
||||
preferred_area.xmin,
|
||||
preferred_area.xmin + COM_BLUR_BOKEH_PIXELS,
|
||||
preferred_area.ymin,
|
||||
preferred_area.ymin + COM_BLUR_BOKEH_PIXELS);
|
||||
}
|
||||
|
||||
} // namespace blender::compositor
|
||||
|
@@ -128,8 +128,7 @@ class BokehImageOperation : public MultiThreadedOperation {
|
||||
* \brief determine the resolution of this operation. currently fixed at [COM_BLUR_BOKEH_PIXELS,
|
||||
* COM_BLUR_BOKEH_PIXELS] \param resolution: \param preferredResolution:
|
||||
*/
|
||||
void determineResolution(unsigned int resolution[2],
|
||||
unsigned int preferredResolution[2]) override;
|
||||
void determine_canvas(const rcti &preferred_area, rcti &r_area) override;
|
||||
|
||||
/**
|
||||
* \brief set the node data
|
||||
|
@@ -27,7 +27,7 @@ namespace blender::compositor {
|
||||
|
||||
CalculateMeanOperation::CalculateMeanOperation()
|
||||
{
|
||||
this->addInputSocket(DataType::Color, ResizeMode::None);
|
||||
this->addInputSocket(DataType::Color, ResizeMode::Align);
|
||||
this->addOutputSocket(DataType::Value);
|
||||
this->m_imageReader = nullptr;
|
||||
this->m_iscalculated = false;
|
||||
@@ -165,11 +165,7 @@ void CalculateMeanOperation::get_area_of_interest(int input_idx,
|
||||
rcti &r_input_area)
|
||||
{
|
||||
BLI_assert(input_idx == 0);
|
||||
NodeOperation *operation = getInputOperation(input_idx);
|
||||
r_input_area.xmin = 0;
|
||||
r_input_area.ymin = 0;
|
||||
r_input_area.xmax = operation->getWidth();
|
||||
r_input_area.ymax = operation->getHeight();
|
||||
r_input_area = get_input_operation(input_idx)->get_canvas();
|
||||
}
|
||||
|
||||
void CalculateMeanOperation::update_memory_buffer_started(MemoryBuffer *UNUSED(output),
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
|
@@ -40,7 +40,7 @@ ColorBalanceASCCDLOperation::ColorBalanceASCCDLOperation()
|
||||
this->addOutputSocket(DataType::Color);
|
||||
this->m_inputValueOperation = nullptr;
|
||||
this->m_inputColorOperation = nullptr;
|
||||
this->setResolutionInputSocketIndex(1);
|
||||
this->set_canvas_input_index(1);
|
||||
flags.can_be_constant = true;
|
||||
}
|
||||
|
||||
|
@@ -45,7 +45,7 @@ ColorBalanceLGGOperation::ColorBalanceLGGOperation()
|
||||
this->addOutputSocket(DataType::Color);
|
||||
this->m_inputValueOperation = nullptr;
|
||||
this->m_inputColorOperation = nullptr;
|
||||
this->setResolutionInputSocketIndex(1);
|
||||
this->set_canvas_input_index(1);
|
||||
flags.can_be_constant = true;
|
||||
}
|
||||
|
||||
|
@@ -37,7 +37,7 @@ ColorCurveOperation::ColorCurveOperation()
|
||||
this->m_inputBlackProgram = nullptr;
|
||||
this->m_inputWhiteProgram = nullptr;
|
||||
|
||||
this->setResolutionInputSocketIndex(1);
|
||||
this->set_canvas_input_index(1);
|
||||
}
|
||||
void ColorCurveOperation::initExecution()
|
||||
{
|
||||
@@ -139,7 +139,7 @@ ConstantLevelColorCurveOperation::ConstantLevelColorCurveOperation()
|
||||
this->m_inputFacProgram = nullptr;
|
||||
this->m_inputImageProgram = nullptr;
|
||||
|
||||
this->setResolutionInputSocketIndex(1);
|
||||
this->set_canvas_input_index(1);
|
||||
}
|
||||
void ConstantLevelColorCurveOperation::initExecution()
|
||||
{
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
|
@@ -51,4 +51,13 @@ void ColorRampOperation::deinitExecution()
|
||||
this->m_inputProgram = nullptr;
|
||||
}
|
||||
|
||||
void ColorRampOperation::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) {
|
||||
BKE_colorband_evaluate(m_colorBand, *it.in(0), it.out);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace blender::compositor
|
||||
|
@@ -18,12 +18,12 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "COM_NodeOperation.h"
|
||||
#include "COM_MultiThreadedOperation.h"
|
||||
#include "DNA_texture_types.h"
|
||||
|
||||
namespace blender::compositor {
|
||||
|
||||
class ColorRampOperation : public NodeOperation {
|
||||
class ColorRampOperation : public MultiThreadedOperation {
|
||||
private:
|
||||
/**
|
||||
* Cached reference to the inputProgram
|
||||
@@ -53,6 +53,10 @@ class ColorRampOperation : public NodeOperation {
|
||||
{
|
||||
this->m_colorBand = colorBand;
|
||||
}
|
||||
|
||||
void update_memory_buffer_partial(MemoryBuffer *output,
|
||||
const rcti &area,
|
||||
Span<MemoryBuffer *> inputs) override;
|
||||
};
|
||||
|
||||
} // namespace blender::compositor
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
|
@@ -236,8 +236,7 @@ void CompositorOperation::update_memory_buffer_partial(MemoryBuffer *UNUSED(outp
|
||||
depth_buf.copy_from(inputs[2], area);
|
||||
}
|
||||
|
||||
void CompositorOperation::determineResolution(unsigned int resolution[2],
|
||||
unsigned int preferredResolution[2])
|
||||
void CompositorOperation::determine_canvas(const rcti &preferred_area, rcti &r_area)
|
||||
{
|
||||
int width = this->m_rd->xsch * this->m_rd->size / 100;
|
||||
int height = this->m_rd->ysch * this->m_rd->size / 100;
|
||||
@@ -254,13 +253,11 @@ void CompositorOperation::determineResolution(unsigned int resolution[2],
|
||||
RE_ReleaseResult(re);
|
||||
}
|
||||
|
||||
preferredResolution[0] = width;
|
||||
preferredResolution[1] = height;
|
||||
rcti local_preferred;
|
||||
BLI_rcti_init(&local_preferred, 0, width, 0, height);
|
||||
|
||||
NodeOperation::determineResolution(resolution, preferredResolution);
|
||||
|
||||
resolution[0] = width;
|
||||
resolution[1] = height;
|
||||
NodeOperation::determine_canvas(local_preferred, r_area);
|
||||
r_area = local_preferred;
|
||||
}
|
||||
|
||||
} // namespace blender::compositor
|
||||
|
@@ -115,8 +115,7 @@ class CompositorOperation : public MultiThreadedOperation {
|
||||
{
|
||||
return eCompositorPriority::Medium;
|
||||
}
|
||||
void determineResolution(unsigned int resolution[2],
|
||||
unsigned int preferredResolution[2]) override;
|
||||
void determine_canvas(const rcti &preferred_area, rcti &r_area) override;
|
||||
void setUseAlphaInput(bool value)
|
||||
{
|
||||
this->m_useAlphaInput = value;
|
||||
|
@@ -22,14 +22,14 @@ namespace blender::compositor {
|
||||
|
||||
ConstantOperation::ConstantOperation()
|
||||
{
|
||||
needs_resolution_to_get_constant_ = false;
|
||||
needs_canvas_to_get_constant_ = false;
|
||||
flags.is_constant_operation = true;
|
||||
flags.is_fullframe_operation = true;
|
||||
}
|
||||
|
||||
bool ConstantOperation::can_get_constant_elem() const
|
||||
{
|
||||
return !needs_resolution_to_get_constant_ || this->flags.is_resolution_set;
|
||||
return !needs_canvas_to_get_constant_ || this->flags.is_canvas_set;
|
||||
}
|
||||
|
||||
void ConstantOperation::update_memory_buffer(MemoryBuffer *output,
|
||||
|
@@ -31,7 +31,7 @@ namespace blender::compositor {
|
||||
*/
|
||||
class ConstantOperation : public NodeOperation {
|
||||
protected:
|
||||
bool needs_resolution_to_get_constant_;
|
||||
bool needs_canvas_to_get_constant_;
|
||||
|
||||
public:
|
||||
ConstantOperation();
|
||||
|
@@ -116,4 +116,31 @@ void ConvertDepthToRadiusOperation::deinitExecution()
|
||||
this->m_inputOperation = nullptr;
|
||||
}
|
||||
|
||||
void ConvertDepthToRadiusOperation::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 z = *it.in(0);
|
||||
if (z == 0.0f) {
|
||||
*it.out = 0.0f;
|
||||
continue;
|
||||
}
|
||||
|
||||
const float inv_z = (1.0f / z);
|
||||
|
||||
/* bug T6656 part 2b, do not re-scale. */
|
||||
#if 0
|
||||
bcrad = 0.5f * fabs(aperture * (dof_sp * (cam_invfdist - iZ) - 1.0f));
|
||||
// scale crad back to original maximum and blend
|
||||
crad->rect[px] = bcrad + wts->rect[px] * (scf * crad->rect[px] - bcrad);
|
||||
#endif
|
||||
const float radius = 0.5f *
|
||||
fabsf(m_aperture * (m_dof_sp * (m_inverseFocalDistance - inv_z) - 1.0f));
|
||||
/* 'bug' T6615, limit minimum radius to 1 pixel,
|
||||
* not really a solution, but somewhat mitigates the problem. */
|
||||
*it.out = CLAMPIS(radius, 0.0f, m_maxRadius);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace blender::compositor
|
||||
|
@@ -19,7 +19,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "COM_FastGaussianBlurOperation.h"
|
||||
#include "COM_NodeOperation.h"
|
||||
#include "COM_MultiThreadedOperation.h"
|
||||
#include "DNA_object_types.h"
|
||||
|
||||
namespace blender::compositor {
|
||||
@@ -28,7 +28,7 @@ namespace blender::compositor {
|
||||
* this program converts an input color to an output value.
|
||||
* it assumes we are in sRGB color space.
|
||||
*/
|
||||
class ConvertDepthToRadiusOperation : public NodeOperation {
|
||||
class ConvertDepthToRadiusOperation : public MultiThreadedOperation {
|
||||
private:
|
||||
/**
|
||||
* Cached reference to the inputProgram
|
||||
@@ -83,6 +83,10 @@ class ConvertDepthToRadiusOperation : public NodeOperation {
|
||||
{
|
||||
this->m_blurPostOperation = operation;
|
||||
}
|
||||
|
||||
void update_memory_buffer_partial(MemoryBuffer *output,
|
||||
const rcti &area,
|
||||
Span<MemoryBuffer *> inputs) override;
|
||||
};
|
||||
|
||||
} // namespace blender::compositor
|
||||
|
@@ -39,6 +39,14 @@ void ConvertBaseOperation::deinitExecution()
|
||||
this->m_inputOperation = nullptr;
|
||||
}
|
||||
|
||||
void ConvertBaseOperation::update_memory_buffer_partial(MemoryBuffer *output,
|
||||
const rcti &area,
|
||||
Span<MemoryBuffer *> inputs)
|
||||
{
|
||||
BuffersIterator<float> it = output->iterate_with(inputs, area);
|
||||
update_memory_buffer_partial(it);
|
||||
}
|
||||
|
||||
/* ******** Value to Color ******** */
|
||||
|
||||
ConvertValueToColorOperation::ConvertValueToColorOperation() : ConvertBaseOperation()
|
||||
@@ -58,6 +66,14 @@ void ConvertValueToColorOperation::executePixelSampled(float output[4],
|
||||
output[3] = 1.0f;
|
||||
}
|
||||
|
||||
void ConvertValueToColorOperation::update_memory_buffer_partial(BuffersIterator<float> &it)
|
||||
{
|
||||
for (; !it.is_end(); ++it) {
|
||||
it.out[0] = it.out[1] = it.out[2] = *it.in(0);
|
||||
it.out[3] = 1.0f;
|
||||
}
|
||||
}
|
||||
|
||||
/* ******** Color to Value ******** */
|
||||
|
||||
ConvertColorToValueOperation::ConvertColorToValueOperation() : ConvertBaseOperation()
|
||||
@@ -76,6 +92,14 @@ void ConvertColorToValueOperation::executePixelSampled(float output[4],
|
||||
output[0] = (inputColor[0] + inputColor[1] + inputColor[2]) / 3.0f;
|
||||
}
|
||||
|
||||
void ConvertColorToValueOperation::update_memory_buffer_partial(BuffersIterator<float> &it)
|
||||
{
|
||||
for (; !it.is_end(); ++it) {
|
||||
const float *in = it.in(0);
|
||||
it.out[0] = (in[0] + in[1] + in[2]) / 3.0f;
|
||||
}
|
||||
}
|
||||
|
||||
/* ******** Color to BW ******** */
|
||||
|
||||
ConvertColorToBWOperation::ConvertColorToBWOperation() : ConvertBaseOperation()
|
||||
@@ -94,6 +118,13 @@ void ConvertColorToBWOperation::executePixelSampled(float output[4],
|
||||
output[0] = IMB_colormanagement_get_luminance(inputColor);
|
||||
}
|
||||
|
||||
void ConvertColorToBWOperation::update_memory_buffer_partial(BuffersIterator<float> &it)
|
||||
{
|
||||
for (; !it.is_end(); ++it) {
|
||||
it.out[0] = IMB_colormanagement_get_luminance(it.in(0));
|
||||
}
|
||||
}
|
||||
|
||||
/* ******** Color to Vector ******** */
|
||||
|
||||
ConvertColorToVectorOperation::ConvertColorToVectorOperation() : ConvertBaseOperation()
|
||||
@@ -112,6 +143,13 @@ void ConvertColorToVectorOperation::executePixelSampled(float output[4],
|
||||
copy_v3_v3(output, color);
|
||||
}
|
||||
|
||||
void ConvertColorToVectorOperation::update_memory_buffer_partial(BuffersIterator<float> &it)
|
||||
{
|
||||
for (; !it.is_end(); ++it) {
|
||||
copy_v3_v3(it.out, it.in(0));
|
||||
}
|
||||
}
|
||||
|
||||
/* ******** Value to Vector ******** */
|
||||
|
||||
ConvertValueToVectorOperation::ConvertValueToVectorOperation() : ConvertBaseOperation()
|
||||
@@ -130,6 +168,13 @@ void ConvertValueToVectorOperation::executePixelSampled(float output[4],
|
||||
output[0] = output[1] = output[2] = value;
|
||||
}
|
||||
|
||||
void ConvertValueToVectorOperation::update_memory_buffer_partial(BuffersIterator<float> &it)
|
||||
{
|
||||
for (; !it.is_end(); ++it) {
|
||||
it.out[0] = it.out[1] = it.out[2] = *it.in(0);
|
||||
}
|
||||
}
|
||||
|
||||
/* ******** Vector to Color ******** */
|
||||
|
||||
ConvertVectorToColorOperation::ConvertVectorToColorOperation() : ConvertBaseOperation()
|
||||
@@ -147,6 +192,14 @@ void ConvertVectorToColorOperation::executePixelSampled(float output[4],
|
||||
output[3] = 1.0f;
|
||||
}
|
||||
|
||||
void ConvertVectorToColorOperation::update_memory_buffer_partial(BuffersIterator<float> &it)
|
||||
{
|
||||
for (; !it.is_end(); ++it) {
|
||||
copy_v3_v3(it.out, it.in(0));
|
||||
it.out[3] = 1.0f;
|
||||
}
|
||||
}
|
||||
|
||||
/* ******** Vector to Value ******** */
|
||||
|
||||
ConvertVectorToValueOperation::ConvertVectorToValueOperation() : ConvertBaseOperation()
|
||||
@@ -165,6 +218,14 @@ void ConvertVectorToValueOperation::executePixelSampled(float output[4],
|
||||
output[0] = (input[0] + input[1] + input[2]) / 3.0f;
|
||||
}
|
||||
|
||||
void ConvertVectorToValueOperation::update_memory_buffer_partial(BuffersIterator<float> &it)
|
||||
{
|
||||
for (; !it.is_end(); ++it) {
|
||||
const float *in = it.in(0);
|
||||
it.out[0] = (in[0] + in[1] + in[2]) / 3.0f;
|
||||
}
|
||||
}
|
||||
|
||||
/* ******** RGB to YCC ******** */
|
||||
|
||||
ConvertRGBToYCCOperation::ConvertRGBToYCCOperation() : ConvertBaseOperation()
|
||||
@@ -207,6 +268,18 @@ void ConvertRGBToYCCOperation::executePixelSampled(float output[4],
|
||||
output[3] = inputColor[3];
|
||||
}
|
||||
|
||||
void ConvertRGBToYCCOperation::update_memory_buffer_partial(BuffersIterator<float> &it)
|
||||
{
|
||||
for (; !it.is_end(); ++it) {
|
||||
const float *in = it.in(0);
|
||||
rgb_to_ycc(in[0], in[1], in[2], &it.out[0], &it.out[1], &it.out[2], this->m_mode);
|
||||
|
||||
/* Normalize for viewing (#rgb_to_ycc returns 0-255 values). */
|
||||
mul_v3_fl(it.out, 1.0f / 255.0f);
|
||||
it.out[3] = in[3];
|
||||
}
|
||||
}
|
||||
|
||||
/* ******** YCC to RGB ******** */
|
||||
|
||||
ConvertYCCToRGBOperation::ConvertYCCToRGBOperation() : ConvertBaseOperation()
|
||||
@@ -240,7 +313,6 @@ void ConvertYCCToRGBOperation::executePixelSampled(float output[4],
|
||||
this->m_inputOperation->readSampled(inputColor, x, y, sampler);
|
||||
|
||||
/* need to un-normalize the data */
|
||||
/* R,G,B --> Y,Cb,Cr */
|
||||
mul_v3_fl(inputColor, 255.0f);
|
||||
|
||||
ycc_to_rgb(inputColor[0],
|
||||
@@ -253,6 +325,22 @@ void ConvertYCCToRGBOperation::executePixelSampled(float output[4],
|
||||
output[3] = inputColor[3];
|
||||
}
|
||||
|
||||
void ConvertYCCToRGBOperation::update_memory_buffer_partial(BuffersIterator<float> &it)
|
||||
{
|
||||
for (; !it.is_end(); ++it) {
|
||||
const float *in = it.in(0);
|
||||
/* Multiply by 255 to un-normalize (#ycc_to_rgb needs input values in 0-255 range). */
|
||||
ycc_to_rgb(in[0] * 255.0f,
|
||||
in[1] * 255.0f,
|
||||
in[2] * 255.0f,
|
||||
&it.out[0],
|
||||
&it.out[1],
|
||||
&it.out[2],
|
||||
this->m_mode);
|
||||
it.out[3] = in[3];
|
||||
}
|
||||
}
|
||||
|
||||
/* ******** RGB to YUV ******** */
|
||||
|
||||
ConvertRGBToYUVOperation::ConvertRGBToYUVOperation() : ConvertBaseOperation()
|
||||
@@ -278,6 +366,15 @@ void ConvertRGBToYUVOperation::executePixelSampled(float output[4],
|
||||
output[3] = inputColor[3];
|
||||
}
|
||||
|
||||
void ConvertRGBToYUVOperation::update_memory_buffer_partial(BuffersIterator<float> &it)
|
||||
{
|
||||
for (; !it.is_end(); ++it) {
|
||||
const float *in = it.in(0);
|
||||
rgb_to_yuv(in[0], in[1], in[2], &it.out[0], &it.out[1], &it.out[2], BLI_YUV_ITU_BT709);
|
||||
it.out[3] = in[3];
|
||||
}
|
||||
}
|
||||
|
||||
/* ******** YUV to RGB ******** */
|
||||
|
||||
ConvertYUVToRGBOperation::ConvertYUVToRGBOperation() : ConvertBaseOperation()
|
||||
@@ -303,6 +400,15 @@ void ConvertYUVToRGBOperation::executePixelSampled(float output[4],
|
||||
output[3] = inputColor[3];
|
||||
}
|
||||
|
||||
void ConvertYUVToRGBOperation::update_memory_buffer_partial(BuffersIterator<float> &it)
|
||||
{
|
||||
for (; !it.is_end(); ++it) {
|
||||
const float *in = it.in(0);
|
||||
yuv_to_rgb(in[0], in[1], in[2], &it.out[0], &it.out[1], &it.out[2], BLI_YUV_ITU_BT709);
|
||||
it.out[3] = in[3];
|
||||
}
|
||||
}
|
||||
|
||||
/* ******** RGB to HSV ******** */
|
||||
|
||||
ConvertRGBToHSVOperation::ConvertRGBToHSVOperation() : ConvertBaseOperation()
|
||||
@@ -322,6 +428,15 @@ void ConvertRGBToHSVOperation::executePixelSampled(float output[4],
|
||||
output[3] = inputColor[3];
|
||||
}
|
||||
|
||||
void ConvertRGBToHSVOperation::update_memory_buffer_partial(BuffersIterator<float> &it)
|
||||
{
|
||||
for (; !it.is_end(); ++it) {
|
||||
const float *in = it.in(0);
|
||||
rgb_to_hsv_v(in, it.out);
|
||||
it.out[3] = in[3];
|
||||
}
|
||||
}
|
||||
|
||||
/* ******** HSV to RGB ******** */
|
||||
|
||||
ConvertHSVToRGBOperation::ConvertHSVToRGBOperation() : ConvertBaseOperation()
|
||||
@@ -344,6 +459,18 @@ void ConvertHSVToRGBOperation::executePixelSampled(float output[4],
|
||||
output[3] = inputColor[3];
|
||||
}
|
||||
|
||||
void ConvertHSVToRGBOperation::update_memory_buffer_partial(BuffersIterator<float> &it)
|
||||
{
|
||||
for (; !it.is_end(); ++it) {
|
||||
const float *in = it.in(0);
|
||||
hsv_to_rgb_v(in, it.out);
|
||||
it.out[0] = max_ff(it.out[0], 0.0f);
|
||||
it.out[1] = max_ff(it.out[1], 0.0f);
|
||||
it.out[2] = max_ff(it.out[2], 0.0f);
|
||||
it.out[3] = in[3];
|
||||
}
|
||||
}
|
||||
|
||||
/* ******** Premul to Straight ******** */
|
||||
|
||||
ConvertPremulToStraightOperation::ConvertPremulToStraightOperation() : ConvertBaseOperation()
|
||||
@@ -363,6 +490,13 @@ void ConvertPremulToStraightOperation::executePixelSampled(float output[4],
|
||||
copy_v4_v4(output, converted);
|
||||
}
|
||||
|
||||
void ConvertPremulToStraightOperation::update_memory_buffer_partial(BuffersIterator<float> &it)
|
||||
{
|
||||
for (; !it.is_end(); ++it) {
|
||||
copy_v4_v4(it.out, ColorSceneLinear4f<eAlpha::Premultiplied>(it.in(0)).unpremultiply_alpha());
|
||||
}
|
||||
}
|
||||
|
||||
/* ******** Straight to Premul ******** */
|
||||
|
||||
ConvertStraightToPremulOperation::ConvertStraightToPremulOperation() : ConvertBaseOperation()
|
||||
@@ -382,6 +516,13 @@ void ConvertStraightToPremulOperation::executePixelSampled(float output[4],
|
||||
copy_v4_v4(output, converted);
|
||||
}
|
||||
|
||||
void ConvertStraightToPremulOperation::update_memory_buffer_partial(BuffersIterator<float> &it)
|
||||
{
|
||||
for (; !it.is_end(); ++it) {
|
||||
copy_v4_v4(it.out, ColorSceneLinear4f<eAlpha::Straight>(it.in(0)).premultiply_alpha());
|
||||
}
|
||||
}
|
||||
|
||||
/* ******** Separate Channels ******** */
|
||||
|
||||
SeparateChannelOperation::SeparateChannelOperation()
|
||||
@@ -410,6 +551,15 @@ void SeparateChannelOperation::executePixelSampled(float output[4],
|
||||
output[0] = input[this->m_channel];
|
||||
}
|
||||
|
||||
void SeparateChannelOperation::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) {
|
||||
it.out[0] = it.in(0)[this->m_channel];
|
||||
}
|
||||
}
|
||||
|
||||
/* ******** Combine Channels ******** */
|
||||
|
||||
CombineChannelsOperation::CombineChannelsOperation()
|
||||
@@ -419,7 +569,7 @@ CombineChannelsOperation::CombineChannelsOperation()
|
||||
this->addInputSocket(DataType::Value);
|
||||
this->addInputSocket(DataType::Value);
|
||||
this->addOutputSocket(DataType::Color);
|
||||
this->setResolutionInputSocketIndex(0);
|
||||
this->set_canvas_input_index(0);
|
||||
this->m_inputChannel1Operation = nullptr;
|
||||
this->m_inputChannel2Operation = nullptr;
|
||||
this->m_inputChannel3Operation = nullptr;
|
||||
@@ -466,4 +616,16 @@ void CombineChannelsOperation::executePixelSampled(float output[4],
|
||||
}
|
||||
}
|
||||
|
||||
void CombineChannelsOperation::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) {
|
||||
it.out[0] = *it.in(0);
|
||||
it.out[1] = *it.in(1);
|
||||
it.out[2] = *it.in(2);
|
||||
it.out[3] = *it.in(3);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace blender::compositor
|
||||
|
@@ -18,11 +18,11 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "COM_NodeOperation.h"
|
||||
#include "COM_MultiThreadedOperation.h"
|
||||
|
||||
namespace blender::compositor {
|
||||
|
||||
class ConvertBaseOperation : public NodeOperation {
|
||||
class ConvertBaseOperation : public MultiThreadedOperation {
|
||||
protected:
|
||||
SocketReader *m_inputOperation;
|
||||
|
||||
@@ -31,6 +31,13 @@ class ConvertBaseOperation : public NodeOperation {
|
||||
|
||||
void initExecution() override;
|
||||
void deinitExecution() override;
|
||||
|
||||
void update_memory_buffer_partial(MemoryBuffer *output,
|
||||
const rcti &area,
|
||||
Span<MemoryBuffer *> inputs) final;
|
||||
|
||||
protected:
|
||||
virtual void update_memory_buffer_partial(BuffersIterator<float> &it) = 0;
|
||||
};
|
||||
|
||||
class ConvertValueToColorOperation : public ConvertBaseOperation {
|
||||
@@ -38,6 +45,9 @@ class ConvertValueToColorOperation : public ConvertBaseOperation {
|
||||
ConvertValueToColorOperation();
|
||||
|
||||
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
|
||||
|
||||
protected:
|
||||
void update_memory_buffer_partial(BuffersIterator<float> &it) override;
|
||||
};
|
||||
|
||||
class ConvertColorToValueOperation : public ConvertBaseOperation {
|
||||
@@ -45,6 +55,9 @@ class ConvertColorToValueOperation : public ConvertBaseOperation {
|
||||
ConvertColorToValueOperation();
|
||||
|
||||
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
|
||||
|
||||
protected:
|
||||
void update_memory_buffer_partial(BuffersIterator<float> &it) override;
|
||||
};
|
||||
|
||||
class ConvertColorToBWOperation : public ConvertBaseOperation {
|
||||
@@ -52,6 +65,9 @@ class ConvertColorToBWOperation : public ConvertBaseOperation {
|
||||
ConvertColorToBWOperation();
|
||||
|
||||
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
|
||||
|
||||
protected:
|
||||
void update_memory_buffer_partial(BuffersIterator<float> &it) override;
|
||||
};
|
||||
|
||||
class ConvertColorToVectorOperation : public ConvertBaseOperation {
|
||||
@@ -59,6 +75,9 @@ class ConvertColorToVectorOperation : public ConvertBaseOperation {
|
||||
ConvertColorToVectorOperation();
|
||||
|
||||
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
|
||||
|
||||
protected:
|
||||
void update_memory_buffer_partial(BuffersIterator<float> &it) override;
|
||||
};
|
||||
|
||||
class ConvertValueToVectorOperation : public ConvertBaseOperation {
|
||||
@@ -66,6 +85,9 @@ class ConvertValueToVectorOperation : public ConvertBaseOperation {
|
||||
ConvertValueToVectorOperation();
|
||||
|
||||
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
|
||||
|
||||
protected:
|
||||
void update_memory_buffer_partial(BuffersIterator<float> &it) override;
|
||||
};
|
||||
|
||||
class ConvertVectorToColorOperation : public ConvertBaseOperation {
|
||||
@@ -73,6 +95,9 @@ class ConvertVectorToColorOperation : public ConvertBaseOperation {
|
||||
ConvertVectorToColorOperation();
|
||||
|
||||
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
|
||||
|
||||
protected:
|
||||
void update_memory_buffer_partial(BuffersIterator<float> &it) override;
|
||||
};
|
||||
|
||||
class ConvertVectorToValueOperation : public ConvertBaseOperation {
|
||||
@@ -80,6 +105,9 @@ class ConvertVectorToValueOperation : public ConvertBaseOperation {
|
||||
ConvertVectorToValueOperation();
|
||||
|
||||
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
|
||||
|
||||
protected:
|
||||
void update_memory_buffer_partial(BuffersIterator<float> &it) override;
|
||||
};
|
||||
|
||||
class ConvertRGBToYCCOperation : public ConvertBaseOperation {
|
||||
@@ -94,6 +122,9 @@ class ConvertRGBToYCCOperation : public ConvertBaseOperation {
|
||||
|
||||
/** Set the YCC mode */
|
||||
void setMode(int mode);
|
||||
|
||||
protected:
|
||||
void update_memory_buffer_partial(BuffersIterator<float> &it) override;
|
||||
};
|
||||
|
||||
class ConvertYCCToRGBOperation : public ConvertBaseOperation {
|
||||
@@ -108,6 +139,9 @@ class ConvertYCCToRGBOperation : public ConvertBaseOperation {
|
||||
|
||||
/** Set the YCC mode */
|
||||
void setMode(int mode);
|
||||
|
||||
protected:
|
||||
void update_memory_buffer_partial(BuffersIterator<float> &it) override;
|
||||
};
|
||||
|
||||
class ConvertRGBToYUVOperation : public ConvertBaseOperation {
|
||||
@@ -115,6 +149,9 @@ class ConvertRGBToYUVOperation : public ConvertBaseOperation {
|
||||
ConvertRGBToYUVOperation();
|
||||
|
||||
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
|
||||
|
||||
protected:
|
||||
void update_memory_buffer_partial(BuffersIterator<float> &it) override;
|
||||
};
|
||||
|
||||
class ConvertYUVToRGBOperation : public ConvertBaseOperation {
|
||||
@@ -122,6 +159,9 @@ class ConvertYUVToRGBOperation : public ConvertBaseOperation {
|
||||
ConvertYUVToRGBOperation();
|
||||
|
||||
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
|
||||
|
||||
protected:
|
||||
void update_memory_buffer_partial(BuffersIterator<float> &it) override;
|
||||
};
|
||||
|
||||
class ConvertRGBToHSVOperation : public ConvertBaseOperation {
|
||||
@@ -129,6 +169,9 @@ class ConvertRGBToHSVOperation : public ConvertBaseOperation {
|
||||
ConvertRGBToHSVOperation();
|
||||
|
||||
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
|
||||
|
||||
protected:
|
||||
void update_memory_buffer_partial(BuffersIterator<float> &it) override;
|
||||
};
|
||||
|
||||
class ConvertHSVToRGBOperation : public ConvertBaseOperation {
|
||||
@@ -136,6 +179,9 @@ class ConvertHSVToRGBOperation : public ConvertBaseOperation {
|
||||
ConvertHSVToRGBOperation();
|
||||
|
||||
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
|
||||
|
||||
protected:
|
||||
void update_memory_buffer_partial(BuffersIterator<float> &it) override;
|
||||
};
|
||||
|
||||
class ConvertPremulToStraightOperation : public ConvertBaseOperation {
|
||||
@@ -143,6 +189,9 @@ class ConvertPremulToStraightOperation : public ConvertBaseOperation {
|
||||
ConvertPremulToStraightOperation();
|
||||
|
||||
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
|
||||
|
||||
protected:
|
||||
void update_memory_buffer_partial(BuffersIterator<float> &it) override;
|
||||
};
|
||||
|
||||
class ConvertStraightToPremulOperation : public ConvertBaseOperation {
|
||||
@@ -150,9 +199,12 @@ class ConvertStraightToPremulOperation : public ConvertBaseOperation {
|
||||
ConvertStraightToPremulOperation();
|
||||
|
||||
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
|
||||
|
||||
protected:
|
||||
void update_memory_buffer_partial(BuffersIterator<float> &it) override;
|
||||
};
|
||||
|
||||
class SeparateChannelOperation : public NodeOperation {
|
||||
class SeparateChannelOperation : public MultiThreadedOperation {
|
||||
private:
|
||||
SocketReader *m_inputOperation;
|
||||
int m_channel;
|
||||
@@ -168,9 +220,13 @@ class SeparateChannelOperation : public NodeOperation {
|
||||
{
|
||||
this->m_channel = channel;
|
||||
}
|
||||
|
||||
void update_memory_buffer_partial(MemoryBuffer *output,
|
||||
const rcti &area,
|
||||
Span<MemoryBuffer *> inputs) override;
|
||||
};
|
||||
|
||||
class CombineChannelsOperation : public NodeOperation {
|
||||
class CombineChannelsOperation : public MultiThreadedOperation {
|
||||
private:
|
||||
SocketReader *m_inputChannel1Operation;
|
||||
SocketReader *m_inputChannel2Operation;
|
||||
@@ -183,6 +239,10 @@ class CombineChannelsOperation : public NodeOperation {
|
||||
|
||||
void initExecution() override;
|
||||
void deinitExecution() override;
|
||||
|
||||
void update_memory_buffer_partial(MemoryBuffer *output,
|
||||
const rcti &area,
|
||||
Span<MemoryBuffer *> inputs) override;
|
||||
};
|
||||
|
||||
} // namespace blender::compositor
|
||||
|
@@ -95,4 +95,81 @@ void ConvolutionEdgeFilterOperation::executePixel(float output[4], int x, int y,
|
||||
output[3] = MAX2(output[3], 0.0f);
|
||||
}
|
||||
|
||||
void ConvolutionEdgeFilterOperation::update_memory_buffer_partial(MemoryBuffer *output,
|
||||
const rcti &area,
|
||||
Span<MemoryBuffer *> inputs)
|
||||
{
|
||||
const MemoryBuffer *image = inputs[IMAGE_INPUT_INDEX];
|
||||
const int last_x = getWidth() - 1;
|
||||
const int last_y = getHeight() - 1;
|
||||
for (BuffersIterator<float> it = output->iterate_with(inputs, area); !it.is_end(); ++it) {
|
||||
const int left_offset = (it.x == 0) ? 0 : -image->elem_stride;
|
||||
const int right_offset = (it.x == last_x) ? 0 : image->elem_stride;
|
||||
const int down_offset = (it.y == 0) ? 0 : -image->row_stride;
|
||||
const int up_offset = (it.y == last_y) ? 0 : image->row_stride;
|
||||
|
||||
const float *center_color = it.in(IMAGE_INPUT_INDEX);
|
||||
float res1[4] = {0};
|
||||
float res2[4] = {0};
|
||||
|
||||
const float *color = center_color + down_offset + left_offset;
|
||||
madd_v3_v3fl(res1, color, m_filter[0]);
|
||||
copy_v3_v3(res2, res1);
|
||||
|
||||
color = center_color + down_offset;
|
||||
madd_v3_v3fl(res1, color, m_filter[1]);
|
||||
madd_v3_v3fl(res2, color, m_filter[3]);
|
||||
|
||||
color = center_color + down_offset + right_offset;
|
||||
madd_v3_v3fl(res1, color, m_filter[2]);
|
||||
madd_v3_v3fl(res2, color, m_filter[6]);
|
||||
|
||||
color = center_color + left_offset;
|
||||
madd_v3_v3fl(res1, color, m_filter[3]);
|
||||
madd_v3_v3fl(res2, color, m_filter[1]);
|
||||
|
||||
{
|
||||
float rgb_filtered[3];
|
||||
mul_v3_v3fl(rgb_filtered, center_color, m_filter[4]);
|
||||
add_v3_v3(res1, rgb_filtered);
|
||||
add_v3_v3(res2, rgb_filtered);
|
||||
}
|
||||
|
||||
color = center_color + right_offset;
|
||||
madd_v3_v3fl(res1, color, m_filter[5]);
|
||||
madd_v3_v3fl(res2, color, m_filter[7]);
|
||||
|
||||
color = center_color + up_offset + left_offset;
|
||||
madd_v3_v3fl(res1, color, m_filter[6]);
|
||||
madd_v3_v3fl(res2, color, m_filter[2]);
|
||||
|
||||
color = center_color + up_offset;
|
||||
madd_v3_v3fl(res1, color, m_filter[7]);
|
||||
madd_v3_v3fl(res2, color, m_filter[5]);
|
||||
|
||||
{
|
||||
color = center_color + up_offset + right_offset;
|
||||
float rgb_filtered[3];
|
||||
mul_v3_v3fl(rgb_filtered, color, m_filter[8]);
|
||||
add_v3_v3(res1, rgb_filtered);
|
||||
add_v3_v3(res2, rgb_filtered);
|
||||
}
|
||||
|
||||
it.out[0] = sqrt(res1[0] * res1[0] + res2[0] * res2[0]);
|
||||
it.out[1] = sqrt(res1[1] * res1[1] + res2[1] * res2[1]);
|
||||
it.out[2] = sqrt(res1[2] * res1[2] + res2[2] * res2[2]);
|
||||
|
||||
const float factor = *it.in(FACTOR_INPUT_INDEX);
|
||||
const float m_factor = 1.0f - factor;
|
||||
it.out[0] = it.out[0] * factor + center_color[0] * m_factor;
|
||||
it.out[1] = it.out[1] * factor + center_color[1] * m_factor;
|
||||
it.out[2] = it.out[2] * factor + center_color[2] * m_factor;
|
||||
|
||||
it.out[3] = center_color[3];
|
||||
|
||||
/* Make sure we don't return negative color. */
|
||||
CLAMP4_MIN(it.out, 0.0f);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace blender::compositor
|
||||
|
@@ -25,6 +25,10 @@ namespace blender::compositor {
|
||||
class ConvolutionEdgeFilterOperation : public ConvolutionFilterOperation {
|
||||
public:
|
||||
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;
|
||||
};
|
||||
|
||||
} // namespace blender::compositor
|
||||
|
@@ -29,7 +29,7 @@ ConvolutionFilterOperation::ConvolutionFilterOperation()
|
||||
this->addInputSocket(DataType::Color);
|
||||
this->addInputSocket(DataType::Value);
|
||||
this->addOutputSocket(DataType::Color);
|
||||
this->setResolutionInputSocketIndex(0);
|
||||
this->set_canvas_input_index(0);
|
||||
this->m_inputOperation = nullptr;
|
||||
this->flags.complex = true;
|
||||
}
|
||||
@@ -127,4 +127,62 @@ bool ConvolutionFilterOperation::determineDependingAreaOfInterest(
|
||||
return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output);
|
||||
}
|
||||
|
||||
void ConvolutionFilterOperation::get_area_of_interest(const int input_idx,
|
||||
const rcti &output_area,
|
||||
rcti &r_input_area)
|
||||
{
|
||||
switch (input_idx) {
|
||||
case IMAGE_INPUT_INDEX: {
|
||||
const int add_x = (m_filterWidth - 1) / 2 + 1;
|
||||
const int add_y = (m_filterHeight - 1) / 2 + 1;
|
||||
r_input_area.xmin = output_area.xmin - add_x;
|
||||
r_input_area.xmax = output_area.xmax + add_x;
|
||||
r_input_area.ymin = output_area.ymin - add_y;
|
||||
r_input_area.ymax = output_area.ymax + add_y;
|
||||
break;
|
||||
}
|
||||
case FACTOR_INPUT_INDEX: {
|
||||
r_input_area = output_area;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ConvolutionFilterOperation::update_memory_buffer_partial(MemoryBuffer *output,
|
||||
const rcti &area,
|
||||
Span<MemoryBuffer *> inputs)
|
||||
{
|
||||
const MemoryBuffer *image = inputs[IMAGE_INPUT_INDEX];
|
||||
const int last_x = getWidth() - 1;
|
||||
const int last_y = getHeight() - 1;
|
||||
for (BuffersIterator<float> it = output->iterate_with(inputs, area); !it.is_end(); ++it) {
|
||||
const int left_offset = (it.x == 0) ? 0 : -image->elem_stride;
|
||||
const int right_offset = (it.x == last_x) ? 0 : image->elem_stride;
|
||||
const int down_offset = (it.y == 0) ? 0 : -image->row_stride;
|
||||
const int up_offset = (it.y == last_y) ? 0 : image->row_stride;
|
||||
|
||||
const float *center_color = it.in(IMAGE_INPUT_INDEX);
|
||||
zero_v4(it.out);
|
||||
madd_v4_v4fl(it.out, center_color + down_offset + left_offset, m_filter[0]);
|
||||
madd_v4_v4fl(it.out, center_color + down_offset, m_filter[1]);
|
||||
madd_v4_v4fl(it.out, center_color + down_offset + right_offset, m_filter[2]);
|
||||
madd_v4_v4fl(it.out, center_color + left_offset, m_filter[3]);
|
||||
madd_v4_v4fl(it.out, center_color, m_filter[4]);
|
||||
madd_v4_v4fl(it.out, center_color + right_offset, m_filter[5]);
|
||||
madd_v4_v4fl(it.out, center_color + up_offset + left_offset, m_filter[6]);
|
||||
madd_v4_v4fl(it.out, center_color + up_offset, m_filter[7]);
|
||||
madd_v4_v4fl(it.out, center_color + up_offset + right_offset, m_filter[8]);
|
||||
|
||||
const float factor = *it.in(FACTOR_INPUT_INDEX);
|
||||
const float m_factor = 1.0f - factor;
|
||||
it.out[0] = it.out[0] * factor + center_color[0] * m_factor;
|
||||
it.out[1] = it.out[1] * factor + center_color[1] * m_factor;
|
||||
it.out[2] = it.out[2] * factor + center_color[2] * m_factor;
|
||||
it.out[3] = it.out[3] * factor + center_color[3] * m_factor;
|
||||
|
||||
/* Make sure we don't return negative color. */
|
||||
CLAMP4_MIN(it.out, 0.0f);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace blender::compositor
|
||||
|
@@ -18,11 +18,15 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "COM_NodeOperation.h"
|
||||
#include "COM_MultiThreadedOperation.h"
|
||||
|
||||
namespace blender::compositor {
|
||||
|
||||
class ConvolutionFilterOperation : public NodeOperation {
|
||||
class ConvolutionFilterOperation : public MultiThreadedOperation {
|
||||
protected:
|
||||
static constexpr int IMAGE_INPUT_INDEX = 0;
|
||||
static constexpr int FACTOR_INPUT_INDEX = 1;
|
||||
|
||||
private:
|
||||
int m_filterWidth;
|
||||
int m_filterHeight;
|
||||
@@ -43,6 +47,11 @@ class ConvolutionFilterOperation : public NodeOperation {
|
||||
|
||||
void initExecution() override;
|
||||
void deinitExecution() override;
|
||||
|
||||
void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) final;
|
||||
virtual void update_memory_buffer_partial(MemoryBuffer *output,
|
||||
const rcti &area,
|
||||
Span<MemoryBuffer *> inputs) override;
|
||||
};
|
||||
|
||||
} // namespace blender::compositor
|
||||
|
@@ -23,7 +23,7 @@ namespace blender::compositor {
|
||||
|
||||
CropBaseOperation::CropBaseOperation()
|
||||
{
|
||||
this->addInputSocket(DataType::Color, ResizeMode::None);
|
||||
this->addInputSocket(DataType::Color, ResizeMode::Align);
|
||||
this->addOutputSocket(DataType::Color);
|
||||
this->m_inputOperation = nullptr;
|
||||
this->m_settings = nullptr;
|
||||
@@ -95,6 +95,22 @@ void CropOperation::executePixelSampled(float output[4], float x, float y, Pixel
|
||||
}
|
||||
}
|
||||
|
||||
void CropOperation::update_memory_buffer_partial(MemoryBuffer *output,
|
||||
const rcti &area,
|
||||
Span<MemoryBuffer *> inputs)
|
||||
{
|
||||
rcti crop_area;
|
||||
BLI_rcti_init(&crop_area, m_xmin, m_xmax, m_ymin, m_ymax);
|
||||
for (BuffersIterator<float> it = output->iterate_with(inputs, area); !it.is_end(); ++it) {
|
||||
if (BLI_rcti_isect_pt(&crop_area, it.x, it.y)) {
|
||||
copy_v4_v4(it.out, it.in(0));
|
||||
}
|
||||
else {
|
||||
zero_v4(it.out);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CropImageOperation::CropImageOperation() : CropBaseOperation()
|
||||
{
|
||||
/* pass */
|
||||
@@ -114,13 +130,24 @@ bool CropImageOperation::determineDependingAreaOfInterest(rcti *input,
|
||||
return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output);
|
||||
}
|
||||
|
||||
void CropImageOperation::determineResolution(unsigned int resolution[2],
|
||||
unsigned int preferredResolution[2])
|
||||
void CropImageOperation::get_area_of_interest(const int input_idx,
|
||||
const rcti &output_area,
|
||||
rcti &r_input_area)
|
||||
{
|
||||
NodeOperation::determineResolution(resolution, preferredResolution);
|
||||
BLI_assert(input_idx == 0);
|
||||
UNUSED_VARS_NDEBUG(input_idx);
|
||||
r_input_area.xmax = output_area.xmax + this->m_xmin;
|
||||
r_input_area.xmin = output_area.xmin + this->m_xmin;
|
||||
r_input_area.ymax = output_area.ymax + this->m_ymin;
|
||||
r_input_area.ymin = output_area.ymin + this->m_ymin;
|
||||
}
|
||||
|
||||
void CropImageOperation::determine_canvas(const rcti &preferred_area, rcti &r_area)
|
||||
{
|
||||
NodeOperation::determine_canvas(preferred_area, r_area);
|
||||
updateArea();
|
||||
resolution[0] = this->m_xmax - this->m_xmin;
|
||||
resolution[1] = this->m_ymax - this->m_ymin;
|
||||
r_area.xmax = r_area.xmin + (m_xmax - m_xmin);
|
||||
r_area.ymax = r_area.ymin + (m_ymax - m_ymin);
|
||||
}
|
||||
|
||||
void CropImageOperation::executePixelSampled(float output[4],
|
||||
@@ -136,4 +163,21 @@ void CropImageOperation::executePixelSampled(float output[4],
|
||||
}
|
||||
}
|
||||
|
||||
void CropImageOperation::update_memory_buffer_partial(MemoryBuffer *output,
|
||||
const rcti &area,
|
||||
Span<MemoryBuffer *> inputs)
|
||||
{
|
||||
rcti op_area;
|
||||
BLI_rcti_init(&op_area, 0, getWidth(), 0, getHeight());
|
||||
const MemoryBuffer *input = inputs[0];
|
||||
for (BuffersIterator<float> it = output->iterate_with({}, area); !it.is_end(); ++it) {
|
||||
if (BLI_rcti_isect_pt(&op_area, it.x, it.y)) {
|
||||
input->read_elem_checked(it.x + this->m_xmin, it.y + this->m_ymin, it.out);
|
||||
}
|
||||
else {
|
||||
zero_v4(it.out);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace blender::compositor
|
||||
|
@@ -18,11 +18,11 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "COM_NodeOperation.h"
|
||||
#include "COM_MultiThreadedOperation.h"
|
||||
|
||||
namespace blender::compositor {
|
||||
|
||||
class CropBaseOperation : public NodeOperation {
|
||||
class CropBaseOperation : public MultiThreadedOperation {
|
||||
protected:
|
||||
SocketReader *m_inputOperation;
|
||||
NodeTwoXYs *m_settings;
|
||||
@@ -53,6 +53,10 @@ class CropOperation : public CropBaseOperation {
|
||||
public:
|
||||
CropOperation();
|
||||
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
|
||||
|
||||
void update_memory_buffer_partial(MemoryBuffer *output,
|
||||
const rcti &area,
|
||||
Span<MemoryBuffer *> inputs) override;
|
||||
};
|
||||
|
||||
class CropImageOperation : public CropBaseOperation {
|
||||
@@ -62,9 +66,13 @@ class CropImageOperation : public CropBaseOperation {
|
||||
bool determineDependingAreaOfInterest(rcti *input,
|
||||
ReadBufferOperation *readOperation,
|
||||
rcti *output) override;
|
||||
void determineResolution(unsigned int resolution[2],
|
||||
unsigned int preferredResolution[2]) override;
|
||||
void determine_canvas(const rcti &preferred_area, rcti &r_area) override;
|
||||
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
|
||||
|
||||
void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override;
|
||||
void update_memory_buffer_partial(MemoryBuffer *output,
|
||||
const rcti &area,
|
||||
Span<MemoryBuffer *> inputs) override;
|
||||
};
|
||||
|
||||
} // namespace blender::compositor
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
|
@@ -35,6 +35,8 @@ DenoiseOperation::DenoiseOperation()
|
||||
this->addInputSocket(DataType::Color);
|
||||
this->addOutputSocket(DataType::Color);
|
||||
this->m_settings = nullptr;
|
||||
flags.is_fullframe_operation = true;
|
||||
output_rendered_ = false;
|
||||
}
|
||||
void DenoiseOperation::initExecution()
|
||||
{
|
||||
@@ -63,8 +65,7 @@ MemoryBuffer *DenoiseOperation::createMemoryBuffer(rcti *rect2)
|
||||
rect.xmax = getWidth();
|
||||
rect.ymax = getHeight();
|
||||
MemoryBuffer *result = new MemoryBuffer(DataType::Color, rect);
|
||||
float *data = result->getBuffer();
|
||||
this->generateDenoise(data, tileColor, tileNormal, tileAlbedo, this->m_settings);
|
||||
this->generateDenoise(result, tileColor, tileNormal, tileAlbedo, this->m_settings);
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -84,23 +85,33 @@ bool DenoiseOperation::determineDependingAreaOfInterest(rcti * /*input*/,
|
||||
return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output);
|
||||
}
|
||||
|
||||
void DenoiseOperation::generateDenoise(float *data,
|
||||
MemoryBuffer *inputTileColor,
|
||||
MemoryBuffer *inputTileNormal,
|
||||
MemoryBuffer *inputTileAlbedo,
|
||||
void DenoiseOperation::generateDenoise(MemoryBuffer *output,
|
||||
MemoryBuffer *input_color,
|
||||
MemoryBuffer *input_normal,
|
||||
MemoryBuffer *input_albedo,
|
||||
NodeDenoise *settings)
|
||||
{
|
||||
float *inputBufferColor = inputTileColor->getBuffer();
|
||||
BLI_assert(inputBufferColor);
|
||||
if (!inputBufferColor) {
|
||||
BLI_assert(input_color->getBuffer());
|
||||
if (!input_color->getBuffer()) {
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef WITH_OPENIMAGEDENOISE
|
||||
/* Always supported through Accelerate framework BNNS on macOS. */
|
||||
# ifndef __APPLE__
|
||||
if (BLI_cpu_support_sse41())
|
||||
# endif
|
||||
{
|
||||
/* OpenImageDenoise needs full buffers. */
|
||||
MemoryBuffer *buf_color = input_color->is_a_single_elem() ? input_color->inflate() :
|
||||
input_color;
|
||||
MemoryBuffer *buf_normal = input_normal && input_normal->is_a_single_elem() ?
|
||||
input_normal->inflate() :
|
||||
input_normal;
|
||||
MemoryBuffer *buf_albedo = input_albedo && input_albedo->is_a_single_elem() ?
|
||||
input_albedo->inflate() :
|
||||
input_albedo;
|
||||
|
||||
/* Since it's memory intensive, it's better to run only one instance of OIDN at a time.
|
||||
* OpenImageDenoise is multithreaded internally and should use all available cores nonetheless.
|
||||
*/
|
||||
@@ -111,35 +122,35 @@ void DenoiseOperation::generateDenoise(float *data,
|
||||
|
||||
oidn::FilterRef filter = device.newFilter("RT");
|
||||
filter.setImage("color",
|
||||
inputBufferColor,
|
||||
buf_color->getBuffer(),
|
||||
oidn::Format::Float3,
|
||||
inputTileColor->getWidth(),
|
||||
inputTileColor->getHeight(),
|
||||
buf_color->getWidth(),
|
||||
buf_color->getHeight(),
|
||||
0,
|
||||
sizeof(float[4]));
|
||||
if (inputTileNormal && inputTileNormal->getBuffer()) {
|
||||
if (buf_normal && buf_normal->getBuffer()) {
|
||||
filter.setImage("normal",
|
||||
inputTileNormal->getBuffer(),
|
||||
buf_normal->getBuffer(),
|
||||
oidn::Format::Float3,
|
||||
inputTileNormal->getWidth(),
|
||||
inputTileNormal->getHeight(),
|
||||
buf_normal->getWidth(),
|
||||
buf_normal->getHeight(),
|
||||
0,
|
||||
sizeof(float[3]));
|
||||
}
|
||||
if (inputTileAlbedo && inputTileAlbedo->getBuffer()) {
|
||||
if (buf_albedo && buf_albedo->getBuffer()) {
|
||||
filter.setImage("albedo",
|
||||
inputTileAlbedo->getBuffer(),
|
||||
buf_albedo->getBuffer(),
|
||||
oidn::Format::Float3,
|
||||
inputTileAlbedo->getWidth(),
|
||||
inputTileAlbedo->getHeight(),
|
||||
buf_albedo->getWidth(),
|
||||
buf_albedo->getHeight(),
|
||||
0,
|
||||
sizeof(float[4]));
|
||||
}
|
||||
filter.setImage("output",
|
||||
data,
|
||||
output->getBuffer(),
|
||||
oidn::Format::Float3,
|
||||
inputTileColor->getWidth(),
|
||||
inputTileColor->getHeight(),
|
||||
buf_color->getWidth(),
|
||||
buf_color->getHeight(),
|
||||
0,
|
||||
sizeof(float[4]));
|
||||
|
||||
@@ -153,19 +164,43 @@ void DenoiseOperation::generateDenoise(float *data,
|
||||
filter.execute();
|
||||
BLI_mutex_unlock(&oidn_lock);
|
||||
|
||||
/* copy the alpha channel, OpenImageDenoise currently only supports RGB */
|
||||
size_t numPixels = inputTileColor->getWidth() * inputTileColor->getHeight();
|
||||
for (size_t i = 0; i < numPixels; i++) {
|
||||
data[i * 4 + 3] = inputBufferColor[i * 4 + 3];
|
||||
/* Copy the alpha channel, OpenImageDenoise currently only supports RGB. */
|
||||
output->copy_from(input_color, input_color->get_rect(), 3, COM_DATA_TYPE_VALUE_CHANNELS, 3);
|
||||
|
||||
/* Delete inflated buffers. */
|
||||
if (input_color->is_a_single_elem()) {
|
||||
delete buf_color;
|
||||
}
|
||||
if (input_normal && input_normal->is_a_single_elem()) {
|
||||
delete buf_normal;
|
||||
}
|
||||
if (input_albedo && input_albedo->is_a_single_elem()) {
|
||||
delete buf_albedo;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
/* If built without OIDN or running on an unsupported CPU, just pass through. */
|
||||
UNUSED_VARS(inputTileAlbedo, inputTileNormal, settings);
|
||||
::memcpy(data,
|
||||
inputBufferColor,
|
||||
sizeof(float[4]) * inputTileColor->getWidth() * inputTileColor->getHeight());
|
||||
UNUSED_VARS(input_albedo, input_normal, settings);
|
||||
output->copy_from(input_color, input_color->get_rect());
|
||||
}
|
||||
|
||||
void DenoiseOperation::get_area_of_interest(const int UNUSED(input_idx),
|
||||
const rcti &UNUSED(output_area),
|
||||
rcti &r_input_area)
|
||||
{
|
||||
r_input_area = this->get_canvas();
|
||||
}
|
||||
|
||||
void DenoiseOperation::update_memory_buffer(MemoryBuffer *output,
|
||||
const rcti &UNUSED(area),
|
||||
Span<MemoryBuffer *> inputs)
|
||||
{
|
||||
if (!output_rendered_) {
|
||||
this->generateDenoise(output, inputs[0], inputs[1], inputs[2], m_settings);
|
||||
output_rendered_ = true;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace blender::compositor
|
||||
|
@@ -37,6 +37,8 @@ class DenoiseOperation : public SingleThreadedOperation {
|
||||
*/
|
||||
NodeDenoise *m_settings;
|
||||
|
||||
bool output_rendered_;
|
||||
|
||||
public:
|
||||
DenoiseOperation();
|
||||
/**
|
||||
@@ -57,11 +59,16 @@ class DenoiseOperation : public SingleThreadedOperation {
|
||||
ReadBufferOperation *readOperation,
|
||||
rcti *output) override;
|
||||
|
||||
void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override;
|
||||
void update_memory_buffer(MemoryBuffer *output,
|
||||
const rcti &area,
|
||||
Span<MemoryBuffer *> inputs) override;
|
||||
|
||||
protected:
|
||||
void generateDenoise(float *data,
|
||||
MemoryBuffer *inputTileColor,
|
||||
MemoryBuffer *inputTileNormal,
|
||||
MemoryBuffer *inputTileAlbedo,
|
||||
void generateDenoise(MemoryBuffer *output,
|
||||
MemoryBuffer *input_color,
|
||||
MemoryBuffer *input_normal,
|
||||
MemoryBuffer *input_albedo,
|
||||
NodeDenoise *settings);
|
||||
|
||||
MemoryBuffer *createMemoryBuffer(rcti *rect) override;
|
||||
|
@@ -29,7 +29,7 @@ DespeckleOperation::DespeckleOperation()
|
||||
this->addInputSocket(DataType::Color);
|
||||
this->addInputSocket(DataType::Value);
|
||||
this->addOutputSocket(DataType::Color);
|
||||
this->setResolutionInputSocketIndex(0);
|
||||
this->set_canvas_input_index(0);
|
||||
this->m_inputOperation = nullptr;
|
||||
this->flags.complex = true;
|
||||
}
|
||||
@@ -127,6 +127,11 @@ void DespeckleOperation::executePixel(float output[4], int x, int y, void * /*da
|
||||
else {
|
||||
copy_v4_v4(output, color_org);
|
||||
}
|
||||
|
||||
#undef TOT_DIV_ONE
|
||||
#undef TOT_DIV_CNR
|
||||
#undef WTOT
|
||||
#undef COLOR_ADD
|
||||
}
|
||||
|
||||
bool DespeckleOperation::determineDependingAreaOfInterest(rcti *input,
|
||||
@@ -144,4 +149,106 @@ bool DespeckleOperation::determineDependingAreaOfInterest(rcti *input,
|
||||
return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output);
|
||||
}
|
||||
|
||||
void DespeckleOperation::get_area_of_interest(const int input_idx,
|
||||
const rcti &output_area,
|
||||
rcti &r_input_area)
|
||||
{
|
||||
switch (input_idx) {
|
||||
case IMAGE_INPUT_INDEX: {
|
||||
const int add_x = 2; //(this->m_filterWidth - 1) / 2 + 1;
|
||||
const int add_y = 2; //(this->m_filterHeight - 1) / 2 + 1;
|
||||
r_input_area.xmin = output_area.xmin - add_x;
|
||||
r_input_area.xmax = output_area.xmax + add_x;
|
||||
r_input_area.ymin = output_area.ymin - add_y;
|
||||
r_input_area.ymax = output_area.ymax + add_y;
|
||||
break;
|
||||
}
|
||||
case FACTOR_INPUT_INDEX: {
|
||||
r_input_area = output_area;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DespeckleOperation::update_memory_buffer_partial(MemoryBuffer *output,
|
||||
const rcti &area,
|
||||
Span<MemoryBuffer *> inputs)
|
||||
{
|
||||
const MemoryBuffer *image = inputs[IMAGE_INPUT_INDEX];
|
||||
const int last_x = getWidth() - 1;
|
||||
const int last_y = getHeight() - 1;
|
||||
for (BuffersIterator<float> it = output->iterate_with(inputs, area); !it.is_end(); ++it) {
|
||||
const int x1 = MAX2(it.x - 1, 0);
|
||||
const int x2 = it.x;
|
||||
const int x3 = MIN2(it.x + 1, last_x);
|
||||
const int y1 = MAX2(it.y - 1, 0);
|
||||
const int y2 = it.y;
|
||||
const int y3 = MIN2(it.y + 1, last_y);
|
||||
|
||||
float w = 0.0f;
|
||||
const float *color_org = it.in(IMAGE_INPUT_INDEX);
|
||||
float color_mid[4];
|
||||
float color_mid_ok[4];
|
||||
const float *in1 = nullptr;
|
||||
|
||||
#define TOT_DIV_ONE 1.0f
|
||||
#define TOT_DIV_CNR (float)M_SQRT1_2
|
||||
|
||||
#define WTOT (TOT_DIV_ONE * 4 + TOT_DIV_CNR * 4)
|
||||
|
||||
#define COLOR_ADD(fac) \
|
||||
{ \
|
||||
madd_v4_v4fl(color_mid, in1, fac); \
|
||||
if (color_diff(in1, color_org, m_threshold)) { \
|
||||
w += fac; \
|
||||
madd_v4_v4fl(color_mid_ok, in1, fac); \
|
||||
} \
|
||||
}
|
||||
|
||||
zero_v4(color_mid);
|
||||
zero_v4(color_mid_ok);
|
||||
|
||||
in1 = image->get_elem(x1, y1);
|
||||
COLOR_ADD(TOT_DIV_CNR)
|
||||
in1 = image->get_elem(x2, y1);
|
||||
COLOR_ADD(TOT_DIV_ONE)
|
||||
in1 = image->get_elem(x3, y1);
|
||||
COLOR_ADD(TOT_DIV_CNR)
|
||||
in1 = image->get_elem(x1, y2);
|
||||
COLOR_ADD(TOT_DIV_ONE)
|
||||
|
||||
#if 0
|
||||
const float* in2 = image->get_elem(x2, y2);
|
||||
madd_v4_v4fl(color_mid, in2, this->m_filter[4]);
|
||||
#endif
|
||||
|
||||
in1 = image->get_elem(x3, y2);
|
||||
COLOR_ADD(TOT_DIV_ONE)
|
||||
in1 = image->get_elem(x1, y3);
|
||||
COLOR_ADD(TOT_DIV_CNR)
|
||||
in1 = image->get_elem(x2, y3);
|
||||
COLOR_ADD(TOT_DIV_ONE)
|
||||
in1 = image->get_elem(x3, y3);
|
||||
COLOR_ADD(TOT_DIV_CNR)
|
||||
|
||||
mul_v4_fl(color_mid, 1.0f / (4.0f + (4.0f * (float)M_SQRT1_2)));
|
||||
// mul_v4_fl(color_mid, 1.0f / w);
|
||||
|
||||
if ((w != 0.0f) && ((w / WTOT) > (m_threshold_neighbor)) &&
|
||||
color_diff(color_mid, color_org, m_threshold)) {
|
||||
const float factor = *it.in(FACTOR_INPUT_INDEX);
|
||||
mul_v4_fl(color_mid_ok, 1.0f / w);
|
||||
interp_v4_v4v4(it.out, color_org, color_mid_ok, factor);
|
||||
}
|
||||
else {
|
||||
copy_v4_v4(it.out, color_org);
|
||||
}
|
||||
|
||||
#undef TOT_DIV_ONE
|
||||
#undef TOT_DIV_CNR
|
||||
#undef WTOT
|
||||
#undef COLOR_ADD
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace blender::compositor
|
||||
|
@@ -18,12 +18,15 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "COM_NodeOperation.h"
|
||||
#include "COM_MultiThreadedOperation.h"
|
||||
|
||||
namespace blender::compositor {
|
||||
|
||||
class DespeckleOperation : public NodeOperation {
|
||||
class DespeckleOperation : public MultiThreadedOperation {
|
||||
private:
|
||||
constexpr static int IMAGE_INPUT_INDEX = 0;
|
||||
constexpr static int FACTOR_INPUT_INDEX = 1;
|
||||
|
||||
float m_threshold;
|
||||
float m_threshold_neighbor;
|
||||
|
||||
@@ -52,6 +55,11 @@ class DespeckleOperation : public NodeOperation {
|
||||
|
||||
void initExecution() override;
|
||||
void deinitExecution() override;
|
||||
|
||||
void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override;
|
||||
void update_memory_buffer_partial(MemoryBuffer *output,
|
||||
const rcti &area,
|
||||
Span<MemoryBuffer *> inputs) override;
|
||||
};
|
||||
|
||||
} // namespace blender::compositor
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
|
@@ -35,9 +35,9 @@ DilateErodeThresholdOperation::DilateErodeThresholdOperation()
|
||||
this->m__switch = 0.5f;
|
||||
this->m_distance = 0.0f;
|
||||
}
|
||||
void DilateErodeThresholdOperation::initExecution()
|
||||
|
||||
void DilateErodeThresholdOperation::init_data()
|
||||
{
|
||||
this->m_inputProgram = this->getInputSocketReader(0);
|
||||
if (this->m_distance < 0.0f) {
|
||||
this->m_scope = -this->m_distance + this->m_inset;
|
||||
}
|
||||
@@ -54,6 +54,11 @@ void DilateErodeThresholdOperation::initExecution()
|
||||
}
|
||||
}
|
||||
|
||||
void DilateErodeThresholdOperation::initExecution()
|
||||
{
|
||||
this->m_inputProgram = this->getInputSocketReader(0);
|
||||
}
|
||||
|
||||
void *DilateErodeThresholdOperation::initializeTileData(rcti * /*rect*/)
|
||||
{
|
||||
void *buffer = this->m_inputProgram->initializeTileData(nullptr);
|
||||
@@ -160,6 +165,112 @@ bool DilateErodeThresholdOperation::determineDependingAreaOfInterest(
|
||||
return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output);
|
||||
}
|
||||
|
||||
void DilateErodeThresholdOperation::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_scope;
|
||||
r_input_area.xmax = output_area.xmax + m_scope;
|
||||
r_input_area.ymin = output_area.ymin - m_scope;
|
||||
r_input_area.ymax = output_area.ymax + m_scope;
|
||||
}
|
||||
|
||||
struct DilateErodeThresholdOperation::PixelData {
|
||||
int x;
|
||||
int y;
|
||||
int xmin;
|
||||
int xmax;
|
||||
int ymin;
|
||||
int ymax;
|
||||
const float *elem;
|
||||
float distance;
|
||||
int elem_stride;
|
||||
int row_stride;
|
||||
/** Switch. */
|
||||
float sw;
|
||||
};
|
||||
|
||||
template<template<typename> typename TCompare>
|
||||
static float get_min_distance(DilateErodeThresholdOperation::PixelData &p)
|
||||
{
|
||||
/* TODO(manzanilla): bad performance, generate a table with relative offsets on operation
|
||||
* initialization to loop from less to greater distance and break as soon as #compare is
|
||||
* true. */
|
||||
const TCompare compare;
|
||||
float min_dist = p.distance;
|
||||
const float *row = p.elem + ((intptr_t)p.ymin - p.y) * p.row_stride +
|
||||
((intptr_t)p.xmin - p.x) * p.elem_stride;
|
||||
for (int yi = p.ymin; yi < p.ymax; yi++) {
|
||||
const float dy = yi - p.y;
|
||||
const float dist_y = dy * dy;
|
||||
const float *elem = row;
|
||||
for (int xi = p.xmin; xi < p.xmax; xi++) {
|
||||
if (compare(*elem, p.sw)) {
|
||||
const float dx = xi - p.x;
|
||||
const float dist = dx * dx + dist_y;
|
||||
min_dist = MIN2(min_dist, dist);
|
||||
}
|
||||
elem += p.elem_stride;
|
||||
}
|
||||
row += p.row_stride;
|
||||
}
|
||||
return min_dist;
|
||||
}
|
||||
|
||||
void DilateErodeThresholdOperation::update_memory_buffer_partial(MemoryBuffer *output,
|
||||
const rcti &area,
|
||||
Span<MemoryBuffer *> inputs)
|
||||
{
|
||||
const MemoryBuffer *input = inputs[0];
|
||||
const rcti &input_rect = input->get_rect();
|
||||
const float rd = m_scope * m_scope;
|
||||
const float inset = m_inset;
|
||||
|
||||
PixelData p;
|
||||
p.sw = m__switch;
|
||||
p.distance = rd * 2;
|
||||
p.elem_stride = input->elem_stride;
|
||||
p.row_stride = input->row_stride;
|
||||
for (BuffersIterator<float> it = output->iterate_with(inputs, area); !it.is_end(); ++it) {
|
||||
p.x = it.x;
|
||||
p.y = it.y;
|
||||
p.xmin = MAX2(p.x - m_scope, input_rect.xmin);
|
||||
p.ymin = MAX2(p.y - m_scope, input_rect.ymin);
|
||||
p.xmax = MIN2(p.x + m_scope, input_rect.xmax);
|
||||
p.ymax = MIN2(p.y + m_scope, input_rect.ymax);
|
||||
p.elem = it.in(0);
|
||||
|
||||
float pixel_value;
|
||||
if (*p.elem > p.sw) {
|
||||
pixel_value = -sqrtf(get_min_distance<std::less>(p));
|
||||
}
|
||||
else {
|
||||
pixel_value = sqrtf(get_min_distance<std::greater>(p));
|
||||
}
|
||||
|
||||
if (m_distance > 0.0f) {
|
||||
const float delta = m_distance - pixel_value;
|
||||
if (delta >= 0.0f) {
|
||||
*it.out = delta >= inset ? 1.0f : delta / inset;
|
||||
}
|
||||
else {
|
||||
*it.out = 0.0f;
|
||||
}
|
||||
}
|
||||
else {
|
||||
const float delta = -m_distance + pixel_value;
|
||||
if (delta < 0.0f) {
|
||||
*it.out = delta < -inset ? 1.0f : (-delta) / inset;
|
||||
}
|
||||
else {
|
||||
*it.out = 0.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Dilate Distance
|
||||
DilateDistanceOperation::DilateDistanceOperation()
|
||||
{
|
||||
@@ -170,15 +281,20 @@ DilateDistanceOperation::DilateDistanceOperation()
|
||||
flags.complex = true;
|
||||
flags.open_cl = true;
|
||||
}
|
||||
void DilateDistanceOperation::initExecution()
|
||||
|
||||
void DilateDistanceOperation::init_data()
|
||||
{
|
||||
this->m_inputProgram = this->getInputSocketReader(0);
|
||||
this->m_scope = this->m_distance;
|
||||
if (this->m_scope < 3) {
|
||||
this->m_scope = 3;
|
||||
}
|
||||
}
|
||||
|
||||
void DilateDistanceOperation::initExecution()
|
||||
{
|
||||
this->m_inputProgram = this->getInputSocketReader(0);
|
||||
}
|
||||
|
||||
void *DilateDistanceOperation::initializeTileData(rcti * /*rect*/)
|
||||
{
|
||||
void *buffer = this->m_inputProgram->initializeTileData(nullptr);
|
||||
@@ -258,6 +374,92 @@ void DilateDistanceOperation::executeOpenCL(OpenCLDevice *device,
|
||||
device->COM_clEnqueueRange(dilateKernel, outputMemoryBuffer, 7, this);
|
||||
}
|
||||
|
||||
void DilateDistanceOperation::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_scope;
|
||||
r_input_area.xmax = output_area.xmax + m_scope;
|
||||
r_input_area.ymin = output_area.ymin - m_scope;
|
||||
r_input_area.ymax = output_area.ymax + m_scope;
|
||||
}
|
||||
|
||||
struct DilateDistanceOperation::PixelData {
|
||||
int x;
|
||||
int y;
|
||||
int xmin;
|
||||
int xmax;
|
||||
int ymin;
|
||||
int ymax;
|
||||
const float *elem;
|
||||
float min_distance;
|
||||
int scope;
|
||||
int elem_stride;
|
||||
int row_stride;
|
||||
const rcti &input_rect;
|
||||
|
||||
PixelData(MemoryBuffer *input, const int distance, const int scope)
|
||||
: min_distance(distance * distance),
|
||||
scope(scope),
|
||||
elem_stride(input->elem_stride),
|
||||
row_stride(input->row_stride),
|
||||
input_rect(input->get_rect())
|
||||
{
|
||||
}
|
||||
|
||||
void update(BuffersIterator<float> &it)
|
||||
{
|
||||
x = it.x;
|
||||
y = it.y;
|
||||
xmin = MAX2(x - scope, input_rect.xmin);
|
||||
ymin = MAX2(y - scope, input_rect.ymin);
|
||||
xmax = MIN2(x + scope, input_rect.xmax);
|
||||
ymax = MIN2(y + scope, input_rect.ymax);
|
||||
elem = it.in(0);
|
||||
}
|
||||
};
|
||||
|
||||
template<template<typename> typename TCompare>
|
||||
static float get_distance_value(DilateDistanceOperation::PixelData &p, const float start_value)
|
||||
{
|
||||
/* TODO(manzanilla): bad performance, only loop elements within minimum distance removing
|
||||
* coordinates and conditional if `dist <= min_dist`. May need to generate a table of offsets. */
|
||||
const TCompare compare;
|
||||
const float min_dist = p.min_distance;
|
||||
float value = start_value;
|
||||
const float *row = p.elem + ((intptr_t)p.ymin - p.y) * p.row_stride +
|
||||
((intptr_t)p.xmin - p.x) * p.elem_stride;
|
||||
for (int yi = p.ymin; yi < p.ymax; yi++) {
|
||||
const float dy = yi - p.y;
|
||||
const float dist_y = dy * dy;
|
||||
const float *elem = row;
|
||||
for (int xi = p.xmin; xi < p.xmax; xi++) {
|
||||
const float dx = xi - p.x;
|
||||
const float dist = dx * dx + dist_y;
|
||||
if (dist <= min_dist) {
|
||||
value = compare(*elem, value) ? *elem : value;
|
||||
}
|
||||
elem += p.elem_stride;
|
||||
}
|
||||
row += p.row_stride;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
void DilateDistanceOperation::update_memory_buffer_partial(MemoryBuffer *output,
|
||||
const rcti &area,
|
||||
Span<MemoryBuffer *> inputs)
|
||||
{
|
||||
PixelData p(inputs[0], m_distance, m_scope);
|
||||
for (BuffersIterator<float> it = output->iterate_with(inputs, area); !it.is_end(); ++it) {
|
||||
p.update(it);
|
||||
*it.out = get_distance_value<std::greater>(p, 0.0f);
|
||||
}
|
||||
}
|
||||
|
||||
/* Erode Distance */
|
||||
ErodeDistanceOperation::ErodeDistanceOperation() : DilateDistanceOperation()
|
||||
{
|
||||
@@ -318,6 +520,17 @@ void ErodeDistanceOperation::executeOpenCL(OpenCLDevice *device,
|
||||
device->COM_clEnqueueRange(erodeKernel, outputMemoryBuffer, 7, this);
|
||||
}
|
||||
|
||||
void ErodeDistanceOperation::update_memory_buffer_partial(MemoryBuffer *output,
|
||||
const rcti &area,
|
||||
Span<MemoryBuffer *> inputs)
|
||||
{
|
||||
PixelData p(inputs[0], m_distance, m_scope);
|
||||
for (BuffersIterator<float> it = output->iterate_with(inputs, area); !it.is_end(); ++it) {
|
||||
p.update(it);
|
||||
*it.out = get_distance_value<std::less>(p, 1.0f);
|
||||
}
|
||||
}
|
||||
|
||||
/* Dilate step */
|
||||
DilateStepOperation::DilateStepOperation()
|
||||
{
|
||||
@@ -475,6 +688,124 @@ bool DilateStepOperation::determineDependingAreaOfInterest(rcti *input,
|
||||
return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output);
|
||||
}
|
||||
|
||||
void DilateStepOperation::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_iterations;
|
||||
r_input_area.xmax = output_area.xmax + m_iterations;
|
||||
r_input_area.ymin = output_area.ymin - m_iterations;
|
||||
r_input_area.ymax = output_area.ymax + m_iterations;
|
||||
}
|
||||
|
||||
template<typename TCompareSelector>
|
||||
static void step_update_memory_buffer(MemoryBuffer *output,
|
||||
const MemoryBuffer *input,
|
||||
const rcti &area,
|
||||
const int num_iterations,
|
||||
const float compare_min_value)
|
||||
{
|
||||
TCompareSelector selector;
|
||||
|
||||
const int width = output->getWidth();
|
||||
const int height = output->getHeight();
|
||||
|
||||
const int half_window = num_iterations;
|
||||
const int window = half_window * 2 + 1;
|
||||
|
||||
const int xmin = MAX2(0, area.xmin - half_window);
|
||||
const int ymin = MAX2(0, area.ymin - half_window);
|
||||
const int xmax = MIN2(width, area.xmax + half_window);
|
||||
const int ymax = MIN2(height, area.ymax + half_window);
|
||||
|
||||
const int bwidth = area.xmax - area.xmin;
|
||||
const int bheight = area.ymax - area.ymin;
|
||||
|
||||
/* Create a buffer with the area needed for horizontal and vertical passes. */
|
||||
rcti result_area;
|
||||
BLI_rcti_init(&result_area, xmin, xmax, ymin, ymax);
|
||||
MemoryBuffer result(DataType::Value, result_area);
|
||||
|
||||
/* #temp holds maxima for every step in the algorithm, #buf holds a
|
||||
* single row or column of input values, padded with #limit values to
|
||||
* simplify the logic. */
|
||||
float *temp = (float *)MEM_mallocN(sizeof(float) * (2 * window - 1), "dilate erode temp");
|
||||
float *buf = (float *)MEM_mallocN(sizeof(float) * (MAX2(bwidth, bheight) + 5 * half_window),
|
||||
"dilate erode buf");
|
||||
|
||||
/* The following is based on the van Herk/Gil-Werman algorithm for morphology operations. */
|
||||
/* First pass, horizontal dilate/erode. */
|
||||
for (int y = ymin; y < ymax; y++) {
|
||||
for (int x = 0; x < bwidth + 5 * half_window; x++) {
|
||||
buf[x] = compare_min_value;
|
||||
}
|
||||
for (int x = xmin; x < xmax; x++) {
|
||||
buf[x - area.xmin + window - 1] = input->get_value(x, y, 0);
|
||||
}
|
||||
|
||||
for (int i = 0; i < (bwidth + 3 * half_window) / window; i++) {
|
||||
int start = (i + 1) * window - 1;
|
||||
|
||||
temp[window - 1] = buf[start];
|
||||
for (int x = 1; x < window; x++) {
|
||||
temp[window - 1 - x] = selector(temp[window - x], buf[start - x]);
|
||||
temp[window - 1 + x] = selector(temp[window + x - 2], buf[start + x]);
|
||||
}
|
||||
|
||||
start = half_window + (i - 1) * window + 1;
|
||||
for (int x = -MIN2(0, start); x < window - MAX2(0, start + window - bwidth); x++) {
|
||||
result.get_value(start + x + area.xmin, y, 0) = selector(temp[x], temp[x + window - 1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Second pass, vertical dilate/erode. */
|
||||
for (int x = xmin; x < xmax; x++) {
|
||||
for (int y = 0; y < bheight + 5 * half_window; y++) {
|
||||
buf[y] = compare_min_value;
|
||||
}
|
||||
for (int y = ymin; y < ymax; y++) {
|
||||
buf[y - area.ymin + window - 1] = result.get_value(x, y, 0);
|
||||
}
|
||||
|
||||
for (int i = 0; i < (bheight + 3 * half_window) / window; i++) {
|
||||
int start = (i + 1) * window - 1;
|
||||
|
||||
temp[window - 1] = buf[start];
|
||||
for (int y = 1; y < window; y++) {
|
||||
temp[window - 1 - y] = selector(temp[window - y], buf[start - y]);
|
||||
temp[window - 1 + y] = selector(temp[window + y - 2], buf[start + y]);
|
||||
}
|
||||
|
||||
start = half_window + (i - 1) * window + 1;
|
||||
for (int y = -MIN2(0, start); y < window - MAX2(0, start + window - bheight); y++) {
|
||||
result.get_value(x, y + start + area.ymin, 0) = selector(temp[y], temp[y + window - 1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MEM_freeN(temp);
|
||||
MEM_freeN(buf);
|
||||
|
||||
output->copy_from(&result, area);
|
||||
}
|
||||
|
||||
struct Max2Selector {
|
||||
float operator()(float f1, float f2) const
|
||||
{
|
||||
return MAX2(f1, f2);
|
||||
}
|
||||
};
|
||||
|
||||
void DilateStepOperation::update_memory_buffer_partial(MemoryBuffer *output,
|
||||
const rcti &area,
|
||||
Span<MemoryBuffer *> inputs)
|
||||
{
|
||||
step_update_memory_buffer<Max2Selector>(output, inputs[0], area, m_iterations, -FLT_MAX);
|
||||
}
|
||||
|
||||
/* Erode step */
|
||||
ErodeStepOperation::ErodeStepOperation() : DilateStepOperation()
|
||||
{
|
||||
@@ -571,4 +902,18 @@ void *ErodeStepOperation::initializeTileData(rcti *rect)
|
||||
return result;
|
||||
}
|
||||
|
||||
struct Min2Selector {
|
||||
float operator()(float f1, float f2) const
|
||||
{
|
||||
return MIN2(f1, f2);
|
||||
}
|
||||
};
|
||||
|
||||
void ErodeStepOperation::update_memory_buffer_partial(MemoryBuffer *output,
|
||||
const rcti &area,
|
||||
Span<MemoryBuffer *> inputs)
|
||||
{
|
||||
step_update_memory_buffer<Min2Selector>(output, inputs[0], area, m_iterations, FLT_MAX);
|
||||
}
|
||||
|
||||
} // namespace blender::compositor
|
||||
|
@@ -18,11 +18,14 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "COM_NodeOperation.h"
|
||||
#include "COM_MultiThreadedOperation.h"
|
||||
|
||||
namespace blender::compositor {
|
||||
|
||||
class DilateErodeThresholdOperation : public NodeOperation {
|
||||
class DilateErodeThresholdOperation : public MultiThreadedOperation {
|
||||
public:
|
||||
struct PixelData;
|
||||
|
||||
private:
|
||||
/**
|
||||
* Cached reference to the inputProgram
|
||||
@@ -47,6 +50,7 @@ class DilateErodeThresholdOperation : public NodeOperation {
|
||||
*/
|
||||
void executePixel(float output[4], int x, int y, void *data) override;
|
||||
|
||||
void init_data() override;
|
||||
/**
|
||||
* Initialize the execution
|
||||
*/
|
||||
@@ -74,10 +78,17 @@ class DilateErodeThresholdOperation : public NodeOperation {
|
||||
bool determineDependingAreaOfInterest(rcti *input,
|
||||
ReadBufferOperation *readOperation,
|
||||
rcti *output) override;
|
||||
|
||||
void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override;
|
||||
void update_memory_buffer_partial(MemoryBuffer *output,
|
||||
const rcti &area,
|
||||
Span<MemoryBuffer *> inputs) override;
|
||||
};
|
||||
|
||||
class DilateDistanceOperation : public NodeOperation {
|
||||
private:
|
||||
class DilateDistanceOperation : public MultiThreadedOperation {
|
||||
public:
|
||||
struct PixelData;
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Cached reference to the inputProgram
|
||||
@@ -94,6 +105,7 @@ class DilateDistanceOperation : public NodeOperation {
|
||||
*/
|
||||
void executePixel(float output[4], int x, int y, void *data) override;
|
||||
|
||||
void init_data() override;
|
||||
/**
|
||||
* Initialize the execution
|
||||
*/
|
||||
@@ -119,7 +131,13 @@ class DilateDistanceOperation : public NodeOperation {
|
||||
MemoryBuffer **inputMemoryBuffers,
|
||||
std::list<cl_mem> *clMemToCleanUp,
|
||||
std::list<cl_kernel> *clKernelsToCleanUp) override;
|
||||
|
||||
void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) final;
|
||||
virtual void update_memory_buffer_partial(MemoryBuffer *output,
|
||||
const rcti &area,
|
||||
Span<MemoryBuffer *> inputs) override;
|
||||
};
|
||||
|
||||
class ErodeDistanceOperation : public DilateDistanceOperation {
|
||||
public:
|
||||
ErodeDistanceOperation();
|
||||
@@ -135,9 +153,13 @@ class ErodeDistanceOperation : public DilateDistanceOperation {
|
||||
MemoryBuffer **inputMemoryBuffers,
|
||||
std::list<cl_mem> *clMemToCleanUp,
|
||||
std::list<cl_kernel> *clKernelsToCleanUp) override;
|
||||
|
||||
void update_memory_buffer_partial(MemoryBuffer *output,
|
||||
const rcti &area,
|
||||
Span<MemoryBuffer *> inputs) override;
|
||||
};
|
||||
|
||||
class DilateStepOperation : public NodeOperation {
|
||||
class DilateStepOperation : public MultiThreadedOperation {
|
||||
protected:
|
||||
/**
|
||||
* Cached reference to the inputProgram
|
||||
@@ -174,6 +196,11 @@ class DilateStepOperation : public NodeOperation {
|
||||
bool determineDependingAreaOfInterest(rcti *input,
|
||||
ReadBufferOperation *readOperation,
|
||||
rcti *output) override;
|
||||
|
||||
void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) final;
|
||||
virtual void update_memory_buffer_partial(MemoryBuffer *output,
|
||||
const rcti &area,
|
||||
Span<MemoryBuffer *> inputs) override;
|
||||
};
|
||||
|
||||
class ErodeStepOperation : public DilateStepOperation {
|
||||
@@ -181,6 +208,9 @@ class ErodeStepOperation : public DilateStepOperation {
|
||||
ErodeStepOperation();
|
||||
|
||||
void *initializeTileData(rcti *rect) override;
|
||||
void update_memory_buffer_partial(MemoryBuffer *output,
|
||||
const rcti &area,
|
||||
Span<MemoryBuffer *> inputs) override;
|
||||
};
|
||||
|
||||
} // namespace blender::compositor
|
||||
|
@@ -146,4 +146,55 @@ bool DirectionalBlurOperation::determineDependingAreaOfInterest(rcti * /*input*/
|
||||
return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output);
|
||||
}
|
||||
|
||||
void DirectionalBlurOperation::get_area_of_interest(const int input_idx,
|
||||
const rcti &UNUSED(output_area),
|
||||
rcti &r_input_area)
|
||||
{
|
||||
BLI_assert(input_idx == 0);
|
||||
UNUSED_VARS_NDEBUG(input_idx);
|
||||
r_input_area = this->get_canvas();
|
||||
}
|
||||
|
||||
void DirectionalBlurOperation::update_memory_buffer_partial(MemoryBuffer *output,
|
||||
const rcti &area,
|
||||
Span<MemoryBuffer *> inputs)
|
||||
{
|
||||
const MemoryBuffer *input = inputs[0];
|
||||
const int iterations = pow(2.0f, this->m_data->iter);
|
||||
for (BuffersIterator<float> it = output->iterate_with({}, area); !it.is_end(); ++it) {
|
||||
const int x = it.x;
|
||||
const int y = it.y;
|
||||
float color_accum[4];
|
||||
input->read_elem_bilinear(x, y, color_accum);
|
||||
|
||||
/* Blur pixel. */
|
||||
/* TODO(manzanilla): Many values used on iterations can be calculated beforehand. Create a
|
||||
* table on operation initialization. */
|
||||
float ltx = this->m_tx;
|
||||
float lty = this->m_ty;
|
||||
float lsc = this->m_sc;
|
||||
float lrot = this->m_rot;
|
||||
for (int i = 0; i < iterations; i++) {
|
||||
const float cs = cosf(lrot), ss = sinf(lrot);
|
||||
const float isc = 1.0f / (1.0f + lsc);
|
||||
|
||||
const float v = isc * (y - this->m_center_y_pix) + lty;
|
||||
const float u = isc * (x - this->m_center_x_pix) + ltx;
|
||||
|
||||
float color[4];
|
||||
input->read_elem_bilinear(
|
||||
cs * u + ss * v + this->m_center_x_pix, cs * v - ss * u + this->m_center_y_pix, color);
|
||||
add_v4_v4(color_accum, color);
|
||||
|
||||
/* Double transformations. */
|
||||
ltx += this->m_tx;
|
||||
lty += this->m_ty;
|
||||
lrot += this->m_rot;
|
||||
lsc += this->m_sc;
|
||||
}
|
||||
|
||||
mul_v4_v4fl(it.out, color_accum, 1.0f / (iterations + 1));
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace blender::compositor
|
||||
|
@@ -18,12 +18,12 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "COM_NodeOperation.h"
|
||||
#include "COM_MultiThreadedOperation.h"
|
||||
#include "COM_QualityStepHelper.h"
|
||||
|
||||
namespace blender::compositor {
|
||||
|
||||
class DirectionalBlurOperation : public NodeOperation, public QualityStepHelper {
|
||||
class DirectionalBlurOperation : public MultiThreadedOperation, public QualityStepHelper {
|
||||
private:
|
||||
SocketReader *m_inputProgram;
|
||||
NodeDBlurData *m_data;
|
||||
@@ -65,6 +65,11 @@ class DirectionalBlurOperation : public NodeOperation, public QualityStepHelper
|
||||
MemoryBuffer **inputMemoryBuffers,
|
||||
std::list<cl_mem> *clMemToCleanUp,
|
||||
std::list<cl_kernel> *clKernelsToCleanUp) override;
|
||||
|
||||
void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override;
|
||||
void update_memory_buffer_partial(MemoryBuffer *output,
|
||||
const rcti &area,
|
||||
Span<MemoryBuffer *> inputs) override;
|
||||
};
|
||||
|
||||
} // namespace blender::compositor
|
||||
|
@@ -32,20 +32,30 @@ DisplaceOperation::DisplaceOperation()
|
||||
this->flags.complex = true;
|
||||
|
||||
this->m_inputColorProgram = nullptr;
|
||||
this->m_inputVectorProgram = nullptr;
|
||||
this->m_inputScaleXProgram = nullptr;
|
||||
this->m_inputScaleYProgram = nullptr;
|
||||
}
|
||||
|
||||
void DisplaceOperation::initExecution()
|
||||
{
|
||||
this->m_inputColorProgram = this->getInputSocketReader(0);
|
||||
this->m_inputVectorProgram = this->getInputSocketReader(1);
|
||||
this->m_inputScaleXProgram = this->getInputSocketReader(2);
|
||||
this->m_inputScaleYProgram = this->getInputSocketReader(3);
|
||||
NodeOperation *vector = this->getInputSocketReader(1);
|
||||
NodeOperation *scale_x = this->getInputSocketReader(2);
|
||||
NodeOperation *scale_y = this->getInputSocketReader(3);
|
||||
if (execution_model_ == eExecutionModel::Tiled) {
|
||||
vector_read_fn_ = [=](float x, float y, float *out) {
|
||||
vector->readSampled(out, x, y, PixelSampler::Bilinear);
|
||||
};
|
||||
scale_x_read_fn_ = [=](float x, float y, float *out) {
|
||||
scale_x->readSampled(out, x, y, PixelSampler::Nearest);
|
||||
};
|
||||
scale_y_read_fn_ = [=](float x, float y, float *out) {
|
||||
scale_y->readSampled(out, x, y, PixelSampler::Nearest);
|
||||
};
|
||||
}
|
||||
|
||||
this->m_width_x4 = this->getWidth() * 4;
|
||||
this->m_height_x4 = this->getHeight() * 4;
|
||||
input_vector_width_ = vector->getWidth();
|
||||
input_vector_height_ = vector->getHeight();
|
||||
}
|
||||
|
||||
void DisplaceOperation::executePixelSampled(float output[4],
|
||||
@@ -69,8 +79,8 @@ void DisplaceOperation::executePixelSampled(float output[4],
|
||||
bool DisplaceOperation::read_displacement(
|
||||
float x, float y, float xscale, float yscale, const float origin[2], float &r_u, float &r_v)
|
||||
{
|
||||
float width = m_inputVectorProgram->getWidth();
|
||||
float height = m_inputVectorProgram->getHeight();
|
||||
float width = input_vector_width_;
|
||||
float height = input_vector_height_;
|
||||
if (x < 0.0f || x >= width || y < 0.0f || y >= height) {
|
||||
r_u = 0.0f;
|
||||
r_v = 0.0f;
|
||||
@@ -78,7 +88,7 @@ bool DisplaceOperation::read_displacement(
|
||||
}
|
||||
|
||||
float col[4];
|
||||
m_inputVectorProgram->readSampled(col, x, y, PixelSampler::Bilinear);
|
||||
vector_read_fn_(x, y, col);
|
||||
r_u = origin[0] - col[0] * xscale;
|
||||
r_v = origin[1] - col[1] * yscale;
|
||||
return true;
|
||||
@@ -90,9 +100,9 @@ void DisplaceOperation::pixelTransform(const float xy[2], float r_uv[2], float r
|
||||
float uv[2]; /* temporary variables for derivative estimation */
|
||||
int num;
|
||||
|
||||
m_inputScaleXProgram->readSampled(col, xy[0], xy[1], PixelSampler::Nearest);
|
||||
scale_x_read_fn_(xy[0], xy[1], col);
|
||||
float xs = col[0];
|
||||
m_inputScaleYProgram->readSampled(col, xy[0], xy[1], PixelSampler::Nearest);
|
||||
scale_y_read_fn_(xy[0], xy[1], col);
|
||||
float ys = col[0];
|
||||
/* clamp x and y displacement to triple image resolution -
|
||||
* to prevent hangs from huge values mistakenly plugged in eg. z buffers */
|
||||
@@ -146,9 +156,9 @@ void DisplaceOperation::pixelTransform(const float xy[2], float r_uv[2], float r
|
||||
void DisplaceOperation::deinitExecution()
|
||||
{
|
||||
this->m_inputColorProgram = nullptr;
|
||||
this->m_inputVectorProgram = nullptr;
|
||||
this->m_inputScaleXProgram = nullptr;
|
||||
this->m_inputScaleYProgram = nullptr;
|
||||
vector_read_fn_ = nullptr;
|
||||
scale_x_read_fn_ = nullptr;
|
||||
scale_y_read_fn_ = nullptr;
|
||||
}
|
||||
|
||||
bool DisplaceOperation::determineDependingAreaOfInterest(rcti *input,
|
||||
@@ -195,4 +205,58 @@ bool DisplaceOperation::determineDependingAreaOfInterest(rcti *input,
|
||||
return false;
|
||||
}
|
||||
|
||||
void DisplaceOperation::get_area_of_interest(const int input_idx,
|
||||
const rcti &output_area,
|
||||
rcti &r_input_area)
|
||||
{
|
||||
switch (input_idx) {
|
||||
case 0: {
|
||||
r_input_area = getInputOperation(input_idx)->get_canvas();
|
||||
break;
|
||||
}
|
||||
case 1: {
|
||||
r_input_area = output_area;
|
||||
expand_area_for_sampler(r_input_area, PixelSampler::Bilinear);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
r_input_area = output_area;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DisplaceOperation::update_memory_buffer_started(MemoryBuffer *UNUSED(output),
|
||||
const rcti &UNUSED(area),
|
||||
Span<MemoryBuffer *> inputs)
|
||||
{
|
||||
MemoryBuffer *vector = inputs[1];
|
||||
MemoryBuffer *scale_x = inputs[2];
|
||||
MemoryBuffer *scale_y = inputs[3];
|
||||
vector_read_fn_ = [=](float x, float y, float *out) { vector->read_elem_bilinear(x, y, out); };
|
||||
scale_x_read_fn_ = [=](float x, float y, float *out) { scale_x->read_elem_checked(x, y, out); };
|
||||
scale_y_read_fn_ = [=](float x, float y, float *out) { scale_y->read_elem_checked(x, y, out); };
|
||||
}
|
||||
|
||||
void DisplaceOperation::update_memory_buffer_partial(MemoryBuffer *output,
|
||||
const rcti &area,
|
||||
Span<MemoryBuffer *> inputs)
|
||||
{
|
||||
const MemoryBuffer *input_color = inputs[0];
|
||||
for (BuffersIterator<float> it = output->iterate_with({}, area); !it.is_end(); ++it) {
|
||||
const float xy[2] = {(float)it.x, (float)it.y};
|
||||
float uv[2];
|
||||
float deriv[2][2];
|
||||
|
||||
pixelTransform(xy, uv, deriv);
|
||||
if (is_zero_v2(deriv[0]) && is_zero_v2(deriv[1])) {
|
||||
input_color->read_elem_bilinear(uv[0], uv[1], it.out);
|
||||
}
|
||||
else {
|
||||
/* EWA filtering (without nearest it gets blurry with NO distortion). */
|
||||
input_color->read_elem_filtered(uv[0], uv[1], deriv[0], deriv[1], it.out);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace blender::compositor
|
||||
|
@@ -18,23 +18,27 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "COM_NodeOperation.h"
|
||||
#include "COM_MultiThreadedOperation.h"
|
||||
|
||||
namespace blender::compositor {
|
||||
|
||||
class DisplaceOperation : public NodeOperation {
|
||||
class DisplaceOperation : public MultiThreadedOperation {
|
||||
private:
|
||||
/**
|
||||
* Cached reference to the inputProgram
|
||||
*/
|
||||
SocketReader *m_inputColorProgram;
|
||||
SocketReader *m_inputVectorProgram;
|
||||
SocketReader *m_inputScaleXProgram;
|
||||
SocketReader *m_inputScaleYProgram;
|
||||
|
||||
float m_width_x4;
|
||||
float m_height_x4;
|
||||
|
||||
int input_vector_width_;
|
||||
int input_vector_height_;
|
||||
|
||||
std::function<void(float x, float y, float *out)> vector_read_fn_;
|
||||
std::function<void(float x, float y, float *out)> scale_x_read_fn_;
|
||||
std::function<void(float x, float y, float *out)> scale_y_read_fn_;
|
||||
|
||||
public:
|
||||
DisplaceOperation();
|
||||
|
||||
@@ -62,6 +66,14 @@ class DisplaceOperation : public NodeOperation {
|
||||
*/
|
||||
void deinitExecution() override;
|
||||
|
||||
void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override;
|
||||
void update_memory_buffer_started(MemoryBuffer *output,
|
||||
const rcti &area,
|
||||
Span<MemoryBuffer *> inputs) override;
|
||||
void update_memory_buffer_partial(MemoryBuffer *output,
|
||||
const rcti &area,
|
||||
Span<MemoryBuffer *> inputs) override;
|
||||
|
||||
private:
|
||||
bool read_displacement(
|
||||
float x, float y, float xscale, float yscale, const float origin[2], float &r_u, float &r_v);
|
||||
|
@@ -132,4 +132,53 @@ bool DisplaceSimpleOperation::determineDependingAreaOfInterest(rcti *input,
|
||||
return false;
|
||||
}
|
||||
|
||||
void DisplaceSimpleOperation::get_area_of_interest(const int input_idx,
|
||||
const rcti &output_area,
|
||||
rcti &r_input_area)
|
||||
{
|
||||
switch (input_idx) {
|
||||
case 0: {
|
||||
r_input_area = get_input_operation(input_idx)->get_canvas();
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
r_input_area = output_area;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DisplaceSimpleOperation::update_memory_buffer_partial(MemoryBuffer *output,
|
||||
const rcti &area,
|
||||
Span<MemoryBuffer *> inputs)
|
||||
{
|
||||
const float width = this->getWidth();
|
||||
const float height = this->getHeight();
|
||||
const MemoryBuffer *input_color = inputs[0];
|
||||
for (BuffersIterator<float> it = output->iterate_with(inputs.drop_front(1), area); !it.is_end();
|
||||
++it) {
|
||||
float scale_x = *it.in(1);
|
||||
float scale_y = *it.in(2);
|
||||
|
||||
/* Clamp x and y displacement to triple image resolution -
|
||||
* to prevent hangs from huge values mistakenly plugged in eg. z buffers. */
|
||||
CLAMP(scale_x, -m_width_x4, m_width_x4);
|
||||
CLAMP(scale_y, -m_height_x4, m_height_x4);
|
||||
|
||||
/* Main displacement in pixel space. */
|
||||
const float *vector = it.in(0);
|
||||
const float p_dx = vector[0] * scale_x;
|
||||
const float p_dy = vector[1] * scale_y;
|
||||
|
||||
/* Displaced pixel in uv coords, for image sampling. */
|
||||
/* Clamp nodes to avoid glitches. */
|
||||
float u = it.x - p_dx + 0.5f;
|
||||
float v = it.y - p_dy + 0.5f;
|
||||
CLAMP(u, 0.0f, width - 1.0f);
|
||||
CLAMP(v, 0.0f, height - 1.0f);
|
||||
|
||||
input_color->read_elem_checked(u, v, it.out);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace blender::compositor
|
||||
|
@@ -18,11 +18,11 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "COM_NodeOperation.h"
|
||||
#include "COM_MultiThreadedOperation.h"
|
||||
|
||||
namespace blender::compositor {
|
||||
|
||||
class DisplaceSimpleOperation : public NodeOperation {
|
||||
class DisplaceSimpleOperation : public MultiThreadedOperation {
|
||||
private:
|
||||
/**
|
||||
* Cached reference to the inputProgram
|
||||
@@ -59,6 +59,11 @@ class DisplaceSimpleOperation : public NodeOperation {
|
||||
* Deinitialize the execution
|
||||
*/
|
||||
void deinitExecution() override;
|
||||
|
||||
void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override;
|
||||
void update_memory_buffer_partial(MemoryBuffer *output,
|
||||
const rcti &area,
|
||||
Span<MemoryBuffer *> inputs) override;
|
||||
};
|
||||
|
||||
} // namespace blender::compositor
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
|
@@ -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);
|
||||
|
@@ -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
|
||||
|
@@ -25,9 +25,10 @@ DotproductOperation::DotproductOperation()
|
||||
this->addInputSocket(DataType::Vector);
|
||||
this->addInputSocket(DataType::Vector);
|
||||
this->addOutputSocket(DataType::Value);
|
||||
this->setResolutionInputSocketIndex(0);
|
||||
this->set_canvas_input_index(0);
|
||||
this->m_input1Operation = nullptr;
|
||||
this->m_input2Operation = nullptr;
|
||||
flags.can_be_constant = true;
|
||||
}
|
||||
void DotproductOperation::initExecution()
|
||||
{
|
||||
@@ -55,4 +56,15 @@ void DotproductOperation::executePixelSampled(float output[4],
|
||||
output[0] = -(input1[0] * input2[0] + input1[1] * input2[1] + input1[2] * input2[2]);
|
||||
}
|
||||
|
||||
void DotproductOperation::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 *input1 = it.in(0);
|
||||
const float *input2 = it.in(1);
|
||||
*it.out = -(input1[0] * input2[0] + input1[1] * input2[1] + input1[2] * input2[2]);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace blender::compositor
|
||||
|
@@ -18,11 +18,11 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "COM_NodeOperation.h"
|
||||
#include "COM_MultiThreadedOperation.h"
|
||||
|
||||
namespace blender::compositor {
|
||||
|
||||
class DotproductOperation : public NodeOperation {
|
||||
class DotproductOperation : public MultiThreadedOperation {
|
||||
private:
|
||||
SocketReader *m_input1Operation;
|
||||
SocketReader *m_input2Operation;
|
||||
@@ -33,6 +33,10 @@ class DotproductOperation : public NodeOperation {
|
||||
|
||||
void initExecution() override;
|
||||
void deinitExecution() override;
|
||||
|
||||
void update_memory_buffer_partial(MemoryBuffer *output,
|
||||
const rcti &area,
|
||||
Span<MemoryBuffer *> inputs) override;
|
||||
};
|
||||
|
||||
} // namespace blender::compositor
|
||||
|
@@ -1399,10 +1399,7 @@ 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;
|
||||
r_input_area = this->get_canvas();
|
||||
}
|
||||
|
||||
void DoubleEdgeMaskOperation::update_memory_buffer(MemoryBuffer *output,
|
||||
|
@@ -62,6 +62,13 @@ bool FastGaussianBlurOperation::determineDependingAreaOfInterest(
|
||||
return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output);
|
||||
}
|
||||
|
||||
void FastGaussianBlurOperation::init_data()
|
||||
{
|
||||
BlurBaseOperation::init_data();
|
||||
this->m_sx = this->m_data.sizex * this->m_size / 2.0f;
|
||||
this->m_sy = this->m_data.sizey * this->m_size / 2.0f;
|
||||
}
|
||||
|
||||
void FastGaussianBlurOperation::initExecution()
|
||||
{
|
||||
BlurBaseOperation::initExecution();
|
||||
@@ -117,6 +124,7 @@ void FastGaussianBlurOperation::IIR_gauss(MemoryBuffer *src,
|
||||
unsigned int chan,
|
||||
unsigned int xy)
|
||||
{
|
||||
BLI_assert(!src->is_a_single_elem());
|
||||
double q, q2, sc, cf[4], tsM[9], tsu[3], tsv[3];
|
||||
double *X, *Y, *W;
|
||||
const unsigned int src_width = src->getWidth();
|
||||
@@ -257,6 +265,61 @@ void FastGaussianBlurOperation::IIR_gauss(MemoryBuffer *src,
|
||||
#undef YVV
|
||||
}
|
||||
|
||||
void FastGaussianBlurOperation::get_area_of_interest(const int input_idx,
|
||||
const rcti &output_area,
|
||||
rcti &r_input_area)
|
||||
{
|
||||
switch (input_idx) {
|
||||
case IMAGE_INPUT_INDEX:
|
||||
r_input_area = this->get_canvas();
|
||||
break;
|
||||
default:
|
||||
BlurBaseOperation::get_area_of_interest(input_idx, output_area, r_input_area);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void FastGaussianBlurOperation::update_memory_buffer_started(MemoryBuffer *output,
|
||||
const rcti &area,
|
||||
Span<MemoryBuffer *> inputs)
|
||||
{
|
||||
/* TODO(manzanilla): Add a render test and make #IIR_gauss multi-threaded with support for
|
||||
* an output buffer. */
|
||||
const MemoryBuffer *input = inputs[IMAGE_INPUT_INDEX];
|
||||
MemoryBuffer *image = nullptr;
|
||||
const bool is_full_output = BLI_rcti_compare(&output->get_rect(), &area);
|
||||
if (is_full_output) {
|
||||
image = output;
|
||||
}
|
||||
else {
|
||||
image = new MemoryBuffer(getOutputSocket()->getDataType(), area);
|
||||
}
|
||||
image->copy_from(input, area);
|
||||
|
||||
if ((this->m_sx == this->m_sy) && (this->m_sx > 0.0f)) {
|
||||
for (const int c : IndexRange(COM_DATA_TYPE_COLOR_CHANNELS)) {
|
||||
IIR_gauss(image, this->m_sx, c, 3);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (this->m_sx > 0.0f) {
|
||||
for (const int c : IndexRange(COM_DATA_TYPE_COLOR_CHANNELS)) {
|
||||
IIR_gauss(image, this->m_sx, c, 1);
|
||||
}
|
||||
}
|
||||
if (this->m_sy > 0.0f) {
|
||||
for (const int c : IndexRange(COM_DATA_TYPE_COLOR_CHANNELS)) {
|
||||
IIR_gauss(image, this->m_sy, c, 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!is_full_output) {
|
||||
output->copy_from(image, area);
|
||||
delete image;
|
||||
}
|
||||
}
|
||||
|
||||
FastGaussianBlurValueOperation::FastGaussianBlurValueOperation()
|
||||
{
|
||||
this->addInputSocket(DataType::Value);
|
||||
@@ -341,4 +404,41 @@ void *FastGaussianBlurValueOperation::initializeTileData(rcti *rect)
|
||||
return this->m_iirgaus;
|
||||
}
|
||||
|
||||
void FastGaussianBlurValueOperation::get_area_of_interest(const int UNUSED(input_idx),
|
||||
const rcti &UNUSED(output_area),
|
||||
rcti &r_input_area)
|
||||
{
|
||||
r_input_area = this->get_canvas();
|
||||
}
|
||||
|
||||
void FastGaussianBlurValueOperation::update_memory_buffer_started(MemoryBuffer *UNUSED(output),
|
||||
const rcti &UNUSED(area),
|
||||
Span<MemoryBuffer *> inputs)
|
||||
{
|
||||
if (m_iirgaus == nullptr) {
|
||||
const MemoryBuffer *image = inputs[0];
|
||||
MemoryBuffer *gauss = new MemoryBuffer(*image);
|
||||
FastGaussianBlurOperation::IIR_gauss(gauss, m_sigma, 0, 3);
|
||||
m_iirgaus = gauss;
|
||||
}
|
||||
}
|
||||
|
||||
void FastGaussianBlurValueOperation::update_memory_buffer_partial(MemoryBuffer *output,
|
||||
const rcti &area,
|
||||
Span<MemoryBuffer *> inputs)
|
||||
{
|
||||
MemoryBuffer *image = inputs[0];
|
||||
BuffersIterator<float> it = output->iterate_with({image, m_iirgaus}, area);
|
||||
if (this->m_overlay == FAST_GAUSS_OVERLAY_MIN) {
|
||||
for (; !it.is_end(); ++it) {
|
||||
*it.out = MIN2(*it.in(0), *it.in(1));
|
||||
}
|
||||
}
|
||||
else if (this->m_overlay == FAST_GAUSS_OVERLAY_MAX) {
|
||||
for (; !it.is_end(); ++it) {
|
||||
*it.out = MAX2(*it.in(0), *it.in(1));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace blender::compositor
|
||||
|
@@ -38,8 +38,19 @@ class FastGaussianBlurOperation : public BlurBaseOperation {
|
||||
|
||||
static void IIR_gauss(MemoryBuffer *src, float sigma, unsigned int channel, unsigned int xy);
|
||||
void *initializeTileData(rcti *rect) override;
|
||||
void init_data() override;
|
||||
void deinitExecution() override;
|
||||
void initExecution() override;
|
||||
|
||||
void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override;
|
||||
void update_memory_buffer_started(MemoryBuffer *output,
|
||||
const rcti &area,
|
||||
Span<MemoryBuffer *> inputs) override;
|
||||
void update_memory_buffer_partial(MemoryBuffer *UNUSED(output),
|
||||
const rcti &UNUSED(area),
|
||||
Span<MemoryBuffer *> UNUSED(inputs)) override
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
enum {
|
||||
@@ -48,7 +59,7 @@ enum {
|
||||
FAST_GAUSS_OVERLAY_MAX = 1,
|
||||
};
|
||||
|
||||
class FastGaussianBlurValueOperation : public NodeOperation {
|
||||
class FastGaussianBlurValueOperation : public MultiThreadedOperation {
|
||||
private:
|
||||
float m_sigma;
|
||||
MemoryBuffer *m_iirgaus;
|
||||
@@ -80,6 +91,14 @@ class FastGaussianBlurValueOperation : public NodeOperation {
|
||||
{
|
||||
this->m_overlay = overlay;
|
||||
}
|
||||
|
||||
void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override;
|
||||
void update_memory_buffer_started(MemoryBuffer *output,
|
||||
const rcti &area,
|
||||
Span<MemoryBuffer *> inputs) override;
|
||||
void update_memory_buffer_partial(MemoryBuffer *output,
|
||||
const rcti &area,
|
||||
Span<MemoryBuffer *> inputs) override;
|
||||
};
|
||||
|
||||
} // namespace blender::compositor
|
||||
|
@@ -24,7 +24,7 @@ FlipOperation::FlipOperation()
|
||||
{
|
||||
this->addInputSocket(DataType::Color);
|
||||
this->addOutputSocket(DataType::Color);
|
||||
this->setResolutionInputSocketIndex(0);
|
||||
this->set_canvas_input_index(0);
|
||||
this->m_inputOperation = nullptr;
|
||||
this->m_flipX = true;
|
||||
this->m_flipY = false;
|
||||
@@ -75,4 +75,42 @@ bool FlipOperation::determineDependingAreaOfInterest(rcti *input,
|
||||
return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output);
|
||||
}
|
||||
|
||||
void FlipOperation::get_area_of_interest(const int input_idx,
|
||||
const rcti &output_area,
|
||||
rcti &r_input_area)
|
||||
{
|
||||
BLI_assert(input_idx == 0);
|
||||
UNUSED_VARS_NDEBUG(input_idx);
|
||||
if (this->m_flipX) {
|
||||
const int w = (int)this->getWidth() - 1;
|
||||
r_input_area.xmax = (w - output_area.xmin) + 1;
|
||||
r_input_area.xmin = (w - output_area.xmax) - 1;
|
||||
}
|
||||
else {
|
||||
r_input_area.xmin = output_area.xmin;
|
||||
r_input_area.xmax = output_area.xmax;
|
||||
}
|
||||
if (this->m_flipY) {
|
||||
const int h = (int)this->getHeight() - 1;
|
||||
r_input_area.ymax = (h - output_area.ymin) + 1;
|
||||
r_input_area.ymin = (h - output_area.ymax) - 1;
|
||||
}
|
||||
else {
|
||||
r_input_area.ymin = output_area.ymin;
|
||||
r_input_area.ymax = output_area.ymax;
|
||||
}
|
||||
}
|
||||
|
||||
void FlipOperation::update_memory_buffer_partial(MemoryBuffer *output,
|
||||
const rcti &area,
|
||||
Span<MemoryBuffer *> inputs)
|
||||
{
|
||||
const MemoryBuffer *input_img = inputs[0];
|
||||
for (BuffersIterator<float> it = output->iterate_with({}, area); !it.is_end(); ++it) {
|
||||
const int nx = this->m_flipX ? ((int)this->getWidth() - 1) - it.x : it.x;
|
||||
const int ny = this->m_flipY ? ((int)this->getHeight() - 1) - it.y : it.y;
|
||||
input_img->read_elem(nx, ny, it.out);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace blender::compositor
|
||||
|
@@ -18,11 +18,11 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "COM_NodeOperation.h"
|
||||
#include "COM_MultiThreadedOperation.h"
|
||||
|
||||
namespace blender::compositor {
|
||||
|
||||
class FlipOperation : public NodeOperation {
|
||||
class FlipOperation : public MultiThreadedOperation {
|
||||
private:
|
||||
SocketReader *m_inputOperation;
|
||||
bool m_flipX;
|
||||
@@ -45,6 +45,11 @@ class FlipOperation : public NodeOperation {
|
||||
{
|
||||
this->m_flipY = flipY;
|
||||
}
|
||||
|
||||
void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override;
|
||||
void update_memory_buffer_partial(MemoryBuffer *output,
|
||||
const rcti &area,
|
||||
Span<MemoryBuffer *> inputs) override;
|
||||
};
|
||||
|
||||
} // namespace blender::compositor
|
||||
|
@@ -26,6 +26,7 @@ GammaCorrectOperation::GammaCorrectOperation()
|
||||
this->addInputSocket(DataType::Color);
|
||||
this->addOutputSocket(DataType::Color);
|
||||
this->m_inputProgram = nullptr;
|
||||
flags.can_be_constant = true;
|
||||
}
|
||||
void GammaCorrectOperation::initExecution()
|
||||
{
|
||||
@@ -58,6 +59,34 @@ void GammaCorrectOperation::executePixelSampled(float output[4],
|
||||
}
|
||||
}
|
||||
|
||||
void GammaCorrectOperation::update_memory_buffer_partial(MemoryBuffer *output,
|
||||
const rcti &area,
|
||||
Span<MemoryBuffer *> inputs)
|
||||
{
|
||||
const MemoryBuffer *input = inputs[0];
|
||||
for (BuffersIterator<float> it = output->iterate_with({}, area); !it.is_end(); ++it) {
|
||||
float color[4];
|
||||
input->read_elem(it.x, it.y, color);
|
||||
if (color[3] > 0.0f) {
|
||||
color[0] /= color[3];
|
||||
color[1] /= color[3];
|
||||
color[2] /= color[3];
|
||||
}
|
||||
|
||||
/* Check for negative to avoid nan's. */
|
||||
it.out[0] = color[0] > 0.0f ? color[0] * color[0] : 0.0f;
|
||||
it.out[1] = color[1] > 0.0f ? color[1] * color[1] : 0.0f;
|
||||
it.out[2] = color[2] > 0.0f ? color[2] * color[2] : 0.0f;
|
||||
it.out[3] = color[3];
|
||||
|
||||
if (color[3] > 0.0f) {
|
||||
it.out[0] *= color[3];
|
||||
it.out[1] *= color[3];
|
||||
it.out[2] *= color[3];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GammaCorrectOperation::deinitExecution()
|
||||
{
|
||||
this->m_inputProgram = nullptr;
|
||||
@@ -68,6 +97,7 @@ GammaUncorrectOperation::GammaUncorrectOperation()
|
||||
this->addInputSocket(DataType::Color);
|
||||
this->addOutputSocket(DataType::Color);
|
||||
this->m_inputProgram = nullptr;
|
||||
flags.can_be_constant = true;
|
||||
}
|
||||
void GammaUncorrectOperation::initExecution()
|
||||
{
|
||||
@@ -100,6 +130,33 @@ void GammaUncorrectOperation::executePixelSampled(float output[4],
|
||||
}
|
||||
}
|
||||
|
||||
void GammaUncorrectOperation::update_memory_buffer_partial(MemoryBuffer *output,
|
||||
const rcti &area,
|
||||
Span<MemoryBuffer *> inputs)
|
||||
{
|
||||
const MemoryBuffer *input = inputs[0];
|
||||
for (BuffersIterator<float> it = output->iterate_with({}, area); !it.is_end(); ++it) {
|
||||
float color[4];
|
||||
input->read_elem(it.x, it.y, color);
|
||||
if (color[3] > 0.0f) {
|
||||
color[0] /= color[3];
|
||||
color[1] /= color[3];
|
||||
color[2] /= color[3];
|
||||
}
|
||||
|
||||
it.out[0] = color[0] > 0.0f ? sqrtf(color[0]) : 0.0f;
|
||||
it.out[1] = color[1] > 0.0f ? sqrtf(color[1]) : 0.0f;
|
||||
it.out[2] = color[2] > 0.0f ? sqrtf(color[2]) : 0.0f;
|
||||
it.out[3] = color[3];
|
||||
|
||||
if (color[3] > 0.0f) {
|
||||
it.out[0] *= color[3];
|
||||
it.out[1] *= color[3];
|
||||
it.out[2] *= color[3];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GammaUncorrectOperation::deinitExecution()
|
||||
{
|
||||
this->m_inputProgram = nullptr;
|
||||
|
@@ -18,11 +18,11 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "COM_NodeOperation.h"
|
||||
#include "COM_MultiThreadedOperation.h"
|
||||
|
||||
namespace blender::compositor {
|
||||
|
||||
class GammaCorrectOperation : public NodeOperation {
|
||||
class GammaCorrectOperation : public MultiThreadedOperation {
|
||||
private:
|
||||
/**
|
||||
* Cached reference to the inputProgram
|
||||
@@ -46,9 +46,13 @@ class GammaCorrectOperation : public NodeOperation {
|
||||
* Deinitialize the execution
|
||||
*/
|
||||
void deinitExecution() override;
|
||||
|
||||
void update_memory_buffer_partial(MemoryBuffer *output,
|
||||
const rcti &area,
|
||||
Span<MemoryBuffer *> inputs) override;
|
||||
};
|
||||
|
||||
class GammaUncorrectOperation : public NodeOperation {
|
||||
class GammaUncorrectOperation : public MultiThreadedOperation {
|
||||
private:
|
||||
/**
|
||||
* Cached reference to the inputProgram
|
||||
@@ -72,6 +76,10 @@ class GammaUncorrectOperation : public NodeOperation {
|
||||
* Deinitialize the execution
|
||||
*/
|
||||
void deinitExecution() override;
|
||||
|
||||
void update_memory_buffer_partial(MemoryBuffer *output,
|
||||
const rcti &area,
|
||||
Span<MemoryBuffer *> inputs) override;
|
||||
};
|
||||
|
||||
} // namespace blender::compositor
|
||||
|
@@ -0,0 +1,168 @@
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* Copyright 2021, Blender Foundation.
|
||||
*/
|
||||
|
||||
#include "COM_GaussianAlphaBlurBaseOperation.h"
|
||||
|
||||
namespace blender::compositor {
|
||||
|
||||
GaussianAlphaBlurBaseOperation::GaussianAlphaBlurBaseOperation(eDimension dim)
|
||||
: BlurBaseOperation(DataType::Value)
|
||||
{
|
||||
this->m_gausstab = nullptr;
|
||||
this->m_filtersize = 0;
|
||||
this->m_falloff = -1; /* Intentionally invalid, so we can detect uninitialized values. */
|
||||
dimension_ = dim;
|
||||
}
|
||||
|
||||
void GaussianAlphaBlurBaseOperation::init_data()
|
||||
{
|
||||
BlurBaseOperation::init_data();
|
||||
if (execution_model_ == eExecutionModel::FullFrame) {
|
||||
rad_ = max_ff(m_size * this->get_blur_size(dimension_), 0.0f);
|
||||
rad_ = min_ff(rad_, MAX_GAUSSTAB_RADIUS);
|
||||
m_filtersize = min_ii(ceil(rad_), MAX_GAUSSTAB_RADIUS);
|
||||
}
|
||||
}
|
||||
|
||||
void GaussianAlphaBlurBaseOperation::initExecution()
|
||||
{
|
||||
BlurBaseOperation::initExecution();
|
||||
if (execution_model_ == eExecutionModel::FullFrame) {
|
||||
m_gausstab = BlurBaseOperation::make_gausstab(rad_, m_filtersize);
|
||||
m_distbuf_inv = BlurBaseOperation::make_dist_fac_inverse(rad_, m_filtersize, m_falloff);
|
||||
}
|
||||
}
|
||||
|
||||
void GaussianAlphaBlurBaseOperation::deinitExecution()
|
||||
{
|
||||
BlurBaseOperation::deinitExecution();
|
||||
|
||||
if (this->m_gausstab) {
|
||||
MEM_freeN(this->m_gausstab);
|
||||
this->m_gausstab = nullptr;
|
||||
}
|
||||
|
||||
if (this->m_distbuf_inv) {
|
||||
MEM_freeN(this->m_distbuf_inv);
|
||||
this->m_distbuf_inv = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void GaussianAlphaBlurBaseOperation::get_area_of_interest(const int input_idx,
|
||||
const rcti &output_area,
|
||||
rcti &r_input_area)
|
||||
{
|
||||
if (input_idx != IMAGE_INPUT_INDEX) {
|
||||
BlurBaseOperation::get_area_of_interest(input_idx, output_area, r_input_area);
|
||||
return;
|
||||
}
|
||||
|
||||
r_input_area = output_area;
|
||||
switch (dimension_) {
|
||||
case eDimension::X:
|
||||
r_input_area.xmin = output_area.xmin - m_filtersize - 1;
|
||||
r_input_area.xmax = output_area.xmax + m_filtersize + 1;
|
||||
break;
|
||||
case eDimension::Y:
|
||||
r_input_area.ymin = output_area.ymin - m_filtersize - 1;
|
||||
r_input_area.ymax = output_area.ymax + m_filtersize + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
BLI_INLINE float finv_test(const float f, const bool test)
|
||||
{
|
||||
return (LIKELY(test == false)) ? f : 1.0f - f;
|
||||
}
|
||||
|
||||
void GaussianAlphaBlurBaseOperation::update_memory_buffer_partial(MemoryBuffer *output,
|
||||
const rcti &area,
|
||||
Span<MemoryBuffer *> inputs)
|
||||
{
|
||||
MemoryBuffer *input = inputs[IMAGE_INPUT_INDEX];
|
||||
const rcti &input_rect = input->get_rect();
|
||||
BuffersIterator<float> it = output->iterate_with({input}, area);
|
||||
|
||||
int min_input_coord = -1;
|
||||
int max_input_coord = -1;
|
||||
int elem_stride = -1;
|
||||
std::function<int()> get_current_coord;
|
||||
switch (dimension_) {
|
||||
case eDimension::X:
|
||||
min_input_coord = input_rect.xmin;
|
||||
max_input_coord = input_rect.xmax;
|
||||
get_current_coord = [&] { return it.x; };
|
||||
elem_stride = input->elem_stride;
|
||||
break;
|
||||
case eDimension::Y:
|
||||
min_input_coord = input_rect.ymin;
|
||||
max_input_coord = input_rect.ymax;
|
||||
get_current_coord = [&] { return it.y; };
|
||||
elem_stride = input->row_stride;
|
||||
break;
|
||||
}
|
||||
|
||||
for (; !it.is_end(); ++it) {
|
||||
const int coord = get_current_coord();
|
||||
const int coord_min = max_ii(coord - m_filtersize, min_input_coord);
|
||||
const int coord_max = min_ii(coord + m_filtersize + 1, max_input_coord);
|
||||
|
||||
/* *** This is the main part which is different to #GaussianBlurBaseOperation. *** */
|
||||
/* Gauss. */
|
||||
float alpha_accum = 0.0f;
|
||||
float multiplier_accum = 0.0f;
|
||||
|
||||
/* Dilate. */
|
||||
const bool do_invert = m_do_subtract;
|
||||
/* Init with the current color to avoid unneeded lookups. */
|
||||
float value_max = finv_test(*it.in(0), do_invert);
|
||||
float distfacinv_max = 1.0f; /* 0 to 1 */
|
||||
|
||||
const int step = QualityStepHelper::getStep();
|
||||
const float *in = it.in(0) + ((intptr_t)coord_min - coord) * elem_stride;
|
||||
const int in_stride = elem_stride * step;
|
||||
int index = (coord_min - coord) + m_filtersize;
|
||||
const int index_end = index + (coord_max - coord_min);
|
||||
for (; index < index_end; in += in_stride, index += step) {
|
||||
float value = finv_test(*in, do_invert);
|
||||
|
||||
/* Gauss. */
|
||||
float multiplier = m_gausstab[index];
|
||||
alpha_accum += value * multiplier;
|
||||
multiplier_accum += multiplier;
|
||||
|
||||
/* Dilate - find most extreme color. */
|
||||
if (value > value_max) {
|
||||
multiplier = m_distbuf_inv[index];
|
||||
value *= multiplier;
|
||||
if (value > value_max) {
|
||||
value_max = value;
|
||||
distfacinv_max = multiplier;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Blend between the max value and gauss blue - gives nice feather. */
|
||||
const float value_blur = alpha_accum / multiplier_accum;
|
||||
const float value_final = (value_max * distfacinv_max) +
|
||||
(value_blur * (1.0f - distfacinv_max));
|
||||
*it.out = finv_test(value_final, do_invert);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace blender::compositor
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user