Compositor: Add support for canvas compositing

This commit adds functionality for operations that require pixel
translation or resizing on "Full Frame" mode, allowing to adjust
their canvas. It fixes most cropping issues in translate, scale,
rotate and transform nodes by adjusting their canvas to the result,
instead of the input canvas.

Operations output buffer is still always on (0,0) position for
easier image algorithm implementation, even when the
canvas is not.

Current limitations (will be addressed on bcon2):
- Displayed translation in Viewer node is limited to 6000px.
- When scaling up the canvas size is limited to the
 scene resolution size x 1.5 . From that point it crops.

If none of these limitations are hit, the Viewer node displays
the full input with any translation.

Differential Revision: https://developer.blender.org/D12466
This commit is contained in:
Manuel Castilla 2021-09-28 19:33:06 +02:00
parent 76377f0176
commit f84fb12f5d
Notes: blender-bot 2023-02-14 07:25:51 +01:00
Referenced by commit a3610c451a, Fix Compositor stack use after scope
44 changed files with 1066 additions and 368 deletions

View File

@ -18,11 +18,14 @@
#pragma once
#include "BLI_float2.hh"
#include "BLI_index_range.hh"
#include "BLI_rect.h"
namespace blender::compositor {
using Size2f = float2;
enum class eExecutionModel {
/**
* Operations are executed from outputs to inputs grouped in execution groups and rendered

View File

@ -43,6 +43,12 @@ int CompositorContext::getFramenumber() const
return m_rd->cfra;
}
Size2f CompositorContext::get_render_size() const
{
return {getRenderData()->xsch * getRenderPercentageAsFactor(),
getRenderData()->ysch * getRenderPercentageAsFactor()};
}
eExecutionModel CompositorContext::get_execution_model() const
{
if (U.experimental.use_full_frame_compositor) {

View File

@ -288,6 +288,8 @@ class CompositorContext {
return m_rd->size * 0.01f;
}
Size2f get_render_size() const;
/**
* Get active execution model.
*/

View File

@ -467,7 +467,9 @@ void COM_convert_canvas(NodeOperationBuilder &builder,
/* 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();
@ -477,13 +479,12 @@ void COM_convert_canvas(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;
@ -518,63 +519,74 @@ void COM_convert_canvas(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);
float addX = doCenter ? (toWidth - fromWidth) / 2.0f : 0.0f;
float addY = doCenter ? (toHeight - fromHeight) / 2.0f : 0.0f;
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);
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);
rcti scale_canvas = fromOperation->get_canvas();
if (builder.context().get_execution_model() == eExecutionModel::FullFrame) {
ScaleOperation::scale_area(scale_canvas, scaleX, scaleY);
scale_canvas.xmax = scale_canvas.xmin + toOperation->getWidth();
scale_canvas.ymax = scale_canvas.ymin + toOperation->getHeight();
addX = 0;
addY = 0;
}
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);
const rcti &translate_canvas = toOperation->get_canvas();
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);
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;
}
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

View File

@ -74,34 +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 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->get_canvas(), 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. */
@ -187,7 +214,8 @@ void FullFrameExecutionModel::determine_areas_to_render(NodeOperation *output_op
std::pair<NodeOperation *, rcti> pair = stack.pop_last();
NodeOperation *operation = pair.first;
const rcti &render_area = pair.second;
if (active_buffers_.is_area_registered(operation, render_area)) {
if (BLI_rcti_is_empty(&render_area) ||
active_buffers_.is_area_registered(operation, render_area)) {
continue;
}
@ -239,9 +267,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 ||
@ -251,12 +278,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;
}
}

View File

@ -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);

View File

@ -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()

View File

@ -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
@ -166,9 +172,9 @@ class MemoryBuffer {
/**
* Get offset needed to jump from buffer start to given coordinates.
*/
int get_coords_offset(int x, int y) const
intptr_t get_coords_offset(int x, int y) const
{
return (y - m_rect.ymin) * row_stride + (x - m_rect.xmin) * elem_stride;
return ((intptr_t)y - m_rect.ymin) * row_stride + ((intptr_t)x - m_rect.xmin) * elem_stride;
}
/**
@ -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);
}
@ -196,7 +202,7 @@ class MemoryBuffer {
void read_elem_checked(int x, int y, float *out) const
{
if (x < m_rect.xmin || x >= m_rect.xmax || y < m_rect.ymin || y >= m_rect.ymax) {
if (!has_coords(x, y)) {
clear_elem(out);
}
else {
@ -206,12 +212,7 @@ class MemoryBuffer {
void read_elem_checked(float x, float y, float *out) const
{
if (x < m_rect.xmin || x >= m_rect.xmax || y < m_rect.ymin || y >= m_rect.ymax) {
clear_elem(out);
}
else {
read_elem(x, y, out);
}
read_elem_checked(floor_x(x), floor_y(y), out);
}
void read_elem_bilinear(float x, float y, float *out) const
@ -286,8 +287,7 @@ class MemoryBuffer {
*/
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];
}
@ -296,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];
}
@ -306,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));
}
@ -681,6 +680,33 @@ class MemoryBuffer {
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,

View File

@ -40,6 +40,24 @@ NodeOperation::NodeOperation()
this->m_btree = nullptr;
}
/** Get constant value when operation is constant, otherwise return default_value. */
float NodeOperation::get_constant_value_default(float default_value)
{
BLI_assert(m_outputs.size() > 0 && getOutputSocket()->getDataType() == DataType::Value);
return *get_constant_elem_default(&default_value);
}
/** Get constant elem when operation is constant, otherwise return default_elem. */
const float *NodeOperation::get_constant_elem_default(const float *default_elem)
{
BLI_assert(m_outputs.size() > 0);
if (get_flags().is_constant_operation) {
return static_cast<ConstantOperation *>(this)->get_constant_elem();
}
return default_elem;
}
/**
* Generate a hash that identifies the operation result in the current execution.
* Requires `hash_output_params` to be implemented, otherwise `std::nullopt` is returned.
@ -186,6 +204,28 @@ 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_;
}
/**
* Mainly used for re-determining canvas of constant operations in cases where preferred canvas
* depends on the constant element.
*/
void NodeOperation::unset_canvas()
{
BLI_assert(m_inputs.size() == 0);
flags.is_canvas_set = false;
}
SocketReader *NodeOperation::getInputSocketReader(unsigned int inputSocketIndex)
{
return this->getInputSocket(inputSocketIndex)->getReader();

View File

@ -62,9 +62,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 */
@ -381,6 +385,9 @@ class NodeOperation {
return m_id;
}
float get_constant_value_default(float default_value);
const float *get_constant_elem_default(const float *default_elem);
const NodeOperationFlags get_flags() const
{
return flags;
@ -507,18 +514,9 @@ class NodeOperation {
}
virtual void deinitExecution();
void set_canvas(const rcti &canvas_area)
{
if (!this->flags.is_canvas_set) {
canvas_ = canvas_area;
flags.is_canvas_set = true;
}
}
const rcti &get_canvas() const
{
return canvas_;
}
void set_canvas(const rcti &canvas_area);
const rcti &get_canvas() const;
void unset_canvas();
/**
* \brief is this operation the active viewer output
@ -575,12 +573,12 @@ class NodeOperation {
unsigned int getWidth() const
{
return BLI_rcti_size_x(&canvas_);
return BLI_rcti_size_x(&get_canvas());
}
unsigned int getHeight() const
{
return BLI_rcti_size_y(&canvas_);
return BLI_rcti_size_y(&get_canvas());
}
inline void readSampled(float result[4], float x, float y, PixelSampler sampler)
@ -677,6 +675,7 @@ 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)
{
canvas_.xmax = canvas_.xmin + width;
@ -687,6 +686,7 @@ class NodeOperation {
canvas_.ymax = canvas_.ymin + height;
this->flags.is_canvas_set = true;
}
SocketReader *getInputSocketReader(unsigned int inputSocketindex);
NodeOperation *getInputOperation(unsigned int inputSocketindex);

View File

@ -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"
@ -448,9 +449,19 @@ void NodeOperationBuilder::determine_canvases()
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()) {
const rcti &from_canvas = link.from()->getOperation().get_canvas();
const rcti &to_canvas = link.to()->getOperation().get_canvas();
bool needs_conversion;
if (link.to()->getResizeMode() == ResizeMode::Align) {
needs_conversion = from_canvas.xmin != to_canvas.xmin ||
from_canvas.ymin != to_canvas.ymin;
}
else {
needs_conversion = !BLI_rcti_compare(&from_canvas, &to_canvas);
}
if (needs_conversion) {
convert_links.append(link);
}
}

View File

@ -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;
}
/**

View File

@ -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);

View File

@ -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));

View File

@ -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));

View File

@ -52,6 +52,8 @@ void ScaleNode::convertToOperations(NodeConverter &converter,
converter.mapOutputSocket(outputSocket, operation->getOutputSocket(0));
operation->setVariableSize(inputXSocket->isLinked() || inputYSocket->isLinked());
operation->set_scale_canvas_max_size(context.get_render_size() * 1.5f);
break;
}
case CMP_SCALE_SCENEPERCENT: {
@ -68,6 +70,7 @@ void ScaleNode::convertToOperations(NodeConverter &converter,
converter.mapOutputSocket(outputSocket, operation->getOutputSocket(0));
operation->setVariableSize(inputXSocket->isLinked() || inputYSocket->isLinked());
operation->set_scale_canvas_max_size(context.get_render_size() * 1.5f);
break;
}
@ -81,13 +84,13 @@ 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);
converter.addOperation(operation);
converter.mapInputSocket(inputSocket, operation->getInputSocket(0));
converter.mapOutputSocket(outputSocket, operation->getOutputSocket(0));
operation->setVariableSize(inputXSocket->isLinked() || inputYSocket->isLinked());
operation->set_scale_canvas_max_size(context.get_render_size() * 3.0f);
break;
}
@ -102,6 +105,7 @@ void ScaleNode::convertToOperations(NodeConverter &converter,
converter.mapOutputSocket(outputSocket, operation->getOutputSocket(0));
operation->setVariableSize(inputXSocket->isLinked() || inputYSocket->isLinked());
operation->set_scale_canvas_max_size(context.get_render_size() * 1.5f);
break;
}

View File

@ -123,17 +123,54 @@ void Stabilize2dNode::convertToOperations(NodeConverter &converter,
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());
ScaleRelativeOperation *scaleOperation = new ScaleRelativeOperation();
scaleOperation->setSampler(sampler);
RotateOperation *rotateOperation = new RotateOperation();
rotateOperation->setDoDegree2RadConversion(false);
rotateOperation->set_sampler(sampler);
TranslateOperation *translateOperation = new TranslateCanvasOperation();
converter.addOperation(scaleOperation);
converter.addOperation(translateOperation);
converter.addOperation(rotateOperation);
converter.addLink(scaleAttribute->getOutputSocket(), scaleOperation->getInputSocket(1));
converter.addLink(scaleAttribute->getOutputSocket(), scaleOperation->getInputSocket(2));
converter.addLink(angleAttribute->getOutputSocket(), rotateOperation->getInputSocket(1));
converter.addLink(xAttribute->getOutputSocket(), translateOperation->getInputSocket(1));
converter.addLink(yAttribute->getOutputSocket(), translateOperation->getInputSocket(2));
NodeOperationInput *stabilization_socket = nullptr;
if (invert) {
/* Translate -> Rotate -> Scale. */
stabilization_socket = translateOperation->getInputSocket(0);
converter.mapInputSocket(imageInput, translateOperation->getInputSocket(0));
converter.addLink(translateOperation->getOutputSocket(),
rotateOperation->getInputSocket(0));
converter.addLink(rotateOperation->getOutputSocket(), scaleOperation->getInputSocket(0));
converter.mapOutputSocket(getOutputSocket(), scaleOperation->getOutputSocket());
}
else {
/* Scale -> Rotate -> Translate. */
stabilization_socket = scaleOperation->getInputSocket(0);
converter.mapInputSocket(imageInput, scaleOperation->getInputSocket(0));
converter.addLink(scaleOperation->getOutputSocket(), rotateOperation->getInputSocket(0));
converter.addLink(rotateOperation->getOutputSocket(),
translateOperation->getInputSocket(0));
converter.mapOutputSocket(getOutputSocket(), translateOperation->getOutputSocket());
}
xAttribute->set_socket_input_resolution_for_stabilization(stabilization_socket);
yAttribute->set_socket_input_resolution_for_stabilization(stabilization_socket);
scaleAttribute->set_socket_input_resolution_for_stabilization(stabilization_socket);
angleAttribute->set_socket_input_resolution_for_stabilization(stabilization_socket);
break;
}
}
}

View File

@ -73,16 +73,33 @@ void TransformNode::convertToOperations(NodeConverter &converter,
break;
}
case eExecutionModel::FullFrame: {
TransformOperation *op = new TransformOperation();
op->set_sampler((PixelSampler)this->getbNode()->custom1);
converter.addOperation(op);
ScaleRelativeOperation *scaleOperation = new ScaleRelativeOperation();
converter.addOperation(scaleOperation);
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());
RotateOperation *rotateOperation = new RotateOperation();
rotateOperation->setDoDegree2RadConversion(false);
converter.addOperation(rotateOperation);
TranslateOperation *translateOperation = new TranslateCanvasOperation();
converter.addOperation(translateOperation);
PixelSampler sampler = (PixelSampler)this->getbNode()->custom1;
scaleOperation->setSampler(sampler);
rotateOperation->set_sampler(sampler);
scaleOperation->set_scale_canvas_max_size(context.get_render_size());
converter.mapInputSocket(imageInput, 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(rotateOperation->getOutputSocket(), translateOperation->getInputSocket(0));
converter.mapInputSocket(xInput, translateOperation->getInputSocket(1));
converter.mapInputSocket(yInput, translateOperation->getInputSocket(2));
converter.mapOutputSocket(getOutputSocket(), translateOperation->getOutputSocket());
break;
}
}

View File

@ -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();

View File

@ -34,7 +34,7 @@ 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);

View File

@ -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;

View File

@ -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;

View File

@ -23,7 +23,7 @@ namespace blender::compositor {
MapUVOperation::MapUVOperation()
{
this->addInputSocket(DataType::Color, ResizeMode::None);
this->addInputSocket(DataType::Color, ResizeMode::Align);
this->addInputSocket(DataType::Vector);
this->addOutputSocket(DataType::Color);
this->m_alpha = 0.0f;

View File

@ -31,6 +31,7 @@ MovieClipAttributeOperation::MovieClipAttributeOperation()
this->m_invert = false;
needs_canvas_to_get_constant_ = true;
is_value_calculated_ = false;
stabilization_resolution_socket_ = nullptr;
}
void MovieClipAttributeOperation::initExecution()
@ -53,8 +54,17 @@ void MovieClipAttributeOperation::calc_value()
scale = 1.0f;
angle = 0.0f;
int clip_framenr = BKE_movieclip_remap_scene_to_clip_frame(this->m_clip, this->m_framenumber);
BKE_tracking_stabilization_data_get(
this->m_clip, clip_framenr, getWidth(), getHeight(), loc, &scale, &angle);
NodeOperation &stabilization_operation =
stabilization_resolution_socket_ ?
stabilization_resolution_socket_->getLink()->getOperation() :
*this;
BKE_tracking_stabilization_data_get(this->m_clip,
clip_framenr,
stabilization_operation.getWidth(),
stabilization_operation.getHeight(),
loc,
&scale,
&angle);
switch (this->m_attribute) {
case MCA_SCALE:
this->m_value = scale;

View File

@ -42,6 +42,7 @@ class MovieClipAttributeOperation : public ConstantOperation {
bool m_invert;
MovieClipAttribute m_attribute;
bool is_value_calculated_;
NodeOperationInput *stabilization_resolution_socket_;
public:
/**
@ -76,6 +77,14 @@ class MovieClipAttributeOperation : public ConstantOperation {
this->m_invert = invert;
}
/**
* Set an operation socket which input will be used to get the resolution for stabilization.
*/
void set_socket_input_resolution_for_stabilization(NodeOperationInput *input_socket)
{
stabilization_resolution_socket_ = input_socket;
}
private:
void calc_value();
};

View File

@ -73,7 +73,7 @@ BLI_INLINE void warpCoord(float x, float y, float matrix[3][3], float uv[2], flo
PlaneDistortWarpImageOperation::PlaneDistortWarpImageOperation() : PlaneDistortBaseOperation()
{
this->addInputSocket(DataType::Color, ResizeMode::None);
this->addInputSocket(DataType::Color, ResizeMode::Align);
this->addOutputSocket(DataType::Color);
this->m_pixelReader = nullptr;
this->flags.complex = true;

View File

@ -41,7 +41,7 @@ PreviewOperation::PreviewOperation(const ColorManagedViewSettings *viewSettings,
const unsigned int defaultHeight)
{
this->addInputSocket(DataType::Color, ResizeMode::None);
this->addInputSocket(DataType::Color, ResizeMode::Align);
this->m_preview = nullptr;
this->m_outputBuffer = nullptr;
this->m_input = nullptr;
@ -162,7 +162,7 @@ void PreviewOperation::determine_canvas(const rcti &UNUSED(preferred_area), rcti
width = width * this->m_divider;
height = height * this->m_divider;
BLI_rcti_init(&r_area, 0, width, 0, height);
BLI_rcti_init(&r_area, r_area.xmin, r_area.xmin + width, r_area.ymin, r_area.ymin + height);
}
eCompositorPriority PreviewOperation::getRenderPriority() const

View File

@ -131,6 +131,23 @@ void ProjectorLensDistortionOperation::updateDispersion()
this->unlockMutex();
}
void ProjectorLensDistortionOperation::determine_canvas(const rcti &preferred_area, rcti &r_area)
{
switch (execution_model_) {
case eExecutionModel::FullFrame: {
set_determined_canvas_modifier([=](rcti &canvas) {
/* Ensure screen space. */
BLI_rcti_translate(&canvas, -canvas.xmin, -canvas.ymin);
});
break;
}
default:
break;
}
NodeOperation::determine_canvas(preferred_area, r_area);
}
void ProjectorLensDistortionOperation::get_area_of_interest(const int input_idx,
const rcti &output_area,
rcti &r_input_area)

View File

@ -62,6 +62,7 @@ class ProjectorLensDistortionOperation : public MultiThreadedOperation {
void updateDispersion();
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,

View File

@ -25,8 +25,8 @@ namespace blender::compositor {
RotateOperation::RotateOperation()
{
this->addInputSocket(DataType::Color);
this->addInputSocket(DataType::Value);
this->addInputSocket(DataType::Color, ResizeMode::None);
this->addInputSocket(DataType::Value, ResizeMode::None);
this->addOutputSocket(DataType::Color);
this->set_canvas_input_index(0);
this->m_imageSocket = nullptr;
@ -36,6 +36,21 @@ RotateOperation::RotateOperation()
sampler_ = PixelSampler::Bilinear;
}
void RotateOperation::get_rotation_center(const rcti &area, float &r_x, float &r_y)
{
r_x = (BLI_rcti_size_x(&area) - 1) / 2.0;
r_y = (BLI_rcti_size_y(&area) - 1) / 2.0;
}
void RotateOperation::get_rotation_offset(const rcti &input_canvas,
const rcti &rotate_canvas,
float &r_offset_x,
float &r_offset_y)
{
r_offset_x = (BLI_rcti_size_x(&input_canvas) - BLI_rcti_size_x(&rotate_canvas)) / 2.0f;
r_offset_y = (BLI_rcti_size_y(&input_canvas) - BLI_rcti_size_y(&rotate_canvas)) / 2.0f;
}
void RotateOperation::get_area_rotation_bounds(const rcti &area,
const float center_x,
const float center_y,
@ -48,14 +63,14 @@ void RotateOperation::get_area_rotation_bounds(const rcti &area,
const float dxmax = area.xmax - center_x;
const float dymax = area.ymax - center_y;
const float x1 = center_x + (cosine * dxmin + sine * dymin);
const float x2 = center_x + (cosine * dxmax + sine * dymin);
const float x3 = center_x + (cosine * dxmin + sine * dymax);
const float x4 = center_x + (cosine * dxmax + sine * dymax);
const float y1 = center_y + (-sine * dxmin + cosine * dymin);
const float y2 = center_y + (-sine * dxmax + cosine * dymin);
const float y3 = center_y + (-sine * dxmin + cosine * dymax);
const float y4 = center_y + (-sine * dxmax + cosine * dymax);
const float x1 = center_x + (cosine * dxmin + (-sine) * dymin);
const float x2 = center_x + (cosine * dxmax + (-sine) * dymin);
const float x3 = center_x + (cosine * dxmin + (-sine) * dymax);
const float x4 = center_x + (cosine * dxmax + (-sine) * dymax);
const float y1 = center_y + (sine * dxmin + cosine * dymin);
const float y2 = center_y + (sine * dxmax + cosine * dymin);
const float y3 = center_y + (sine * dxmin + cosine * dymax);
const float y4 = center_y + (sine * dxmax + cosine * dymax);
const float minx = MIN2(x1, MIN2(x2, MIN2(x3, x4)));
const float maxx = MAX2(x1, MAX2(x2, MAX2(x3, x4)));
const float miny = MIN2(y1, MIN2(y2, MIN2(y3, y4)));
@ -67,10 +82,56 @@ void RotateOperation::get_area_rotation_bounds(const rcti &area,
r_bounds.ymax = ceil(maxy);
}
void RotateOperation::get_area_rotation_bounds_inverted(const rcti &area,
const float center_x,
const float center_y,
const float sine,
const float cosine,
rcti &r_bounds)
{
get_area_rotation_bounds(area, center_x, center_y, -sine, cosine, r_bounds);
}
void RotateOperation::get_rotation_area_of_interest(const rcti &input_canvas,
const rcti &rotate_canvas,
const float sine,
const float cosine,
const rcti &output_area,
rcti &r_input_area)
{
float center_x, center_y;
get_rotation_center(input_canvas, center_x, center_y);
float rotate_offset_x, rotate_offset_y;
get_rotation_offset(input_canvas, rotate_canvas, rotate_offset_x, rotate_offset_y);
r_input_area = output_area;
BLI_rcti_translate(&r_input_area, rotate_offset_x, rotate_offset_y);
get_area_rotation_bounds_inverted(r_input_area, center_x, center_y, sine, cosine, r_input_area);
}
void RotateOperation::get_rotation_canvas(const rcti &input_canvas,
const float sine,
const float cosine,
rcti &r_canvas)
{
float center_x, center_y;
get_rotation_center(input_canvas, center_x, center_y);
rcti rot_bounds;
get_area_rotation_bounds(input_canvas, center_x, center_y, sine, cosine, rot_bounds);
float offset_x, offset_y;
get_rotation_offset(input_canvas, rot_bounds, offset_x, offset_y);
r_canvas = rot_bounds;
BLI_rcti_translate(&r_canvas, -offset_x, -offset_y);
}
void RotateOperation::init_data()
{
this->m_centerX = (getWidth() - 1) / 2.0;
this->m_centerY = (getHeight() - 1) / 2.0;
if (execution_model_ == eExecutionModel::Tiled) {
get_rotation_center(get_canvas(), m_centerX, m_centerY);
}
}
void RotateOperation::initExecution()
@ -94,11 +155,7 @@ inline void RotateOperation::ensureDegree()
this->m_degreeSocket->readSampled(degree, 0, 0, PixelSampler::Nearest);
break;
case eExecutionModel::FullFrame:
NodeOperation *degree_op = getInputOperation(DEGREE_INPUT_INDEX);
const bool is_constant_degree = degree_op->get_flags().is_constant_operation;
degree[0] = is_constant_degree ?
static_cast<ConstantOperation *>(degree_op)->get_constant_elem()[0] :
0.0f;
degree[0] = get_input_operation(DEGREE_INPUT_INDEX)->get_constant_value_default(0.0f);
break;
}
@ -159,6 +216,26 @@ bool RotateOperation::determineDependingAreaOfInterest(rcti *input,
return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output);
}
void RotateOperation::determine_canvas(const rcti &preferred_area, rcti &r_area)
{
if (execution_model_ == eExecutionModel::Tiled) {
NodeOperation::determine_canvas(preferred_area, r_area);
return;
}
const bool image_determined =
getInputSocket(IMAGE_INPUT_INDEX)->determine_canvas(preferred_area, r_area);
if (image_determined) {
rcti input_canvas = r_area;
rcti unused;
getInputSocket(DEGREE_INPUT_INDEX)->determine_canvas(input_canvas, unused);
ensureDegree();
get_rotation_canvas(input_canvas, m_sine, m_cosine, r_area);
}
}
void RotateOperation::get_area_of_interest(const int input_idx,
const rcti &output_area,
rcti &r_input_area)
@ -169,7 +246,10 @@ void RotateOperation::get_area_of_interest(const int input_idx,
}
ensureDegree();
get_area_rotation_bounds(output_area, m_centerX, m_centerY, m_sine, m_cosine, r_input_area);
const rcti &input_image_canvas = get_input_operation(IMAGE_INPUT_INDEX)->get_canvas();
get_rotation_area_of_interest(
input_image_canvas, this->get_canvas(), m_sine, m_cosine, output_area, r_input_area);
expand_area_for_sampler(r_input_area, sampler_);
}
@ -177,13 +257,20 @@ void RotateOperation::update_memory_buffer_partial(MemoryBuffer *output,
const rcti &area,
Span<MemoryBuffer *> inputs)
{
ensureDegree();
const MemoryBuffer *input_img = inputs[IMAGE_INPUT_INDEX];
NodeOperation *image_op = get_input_operation(IMAGE_INPUT_INDEX);
float center_x, center_y;
get_rotation_center(image_op->get_canvas(), center_x, center_y);
float rotate_offset_x, rotate_offset_y;
get_rotation_offset(
image_op->get_canvas(), this->get_canvas(), rotate_offset_x, rotate_offset_y);
for (BuffersIterator<float> it = output->iterate_with({}, area); !it.is_end(); ++it) {
float x = it.x;
float y = it.y;
rotate_coords(x, y, m_centerX, m_centerY, m_sine, m_cosine);
input_img->read_elem_sampled(x, y, sampler_, it.out);
float x = rotate_offset_x + it.x + canvas_.xmin;
float y = rotate_offset_y + it.y + canvas_.ymin;
rotate_coords(x, y, center_x, center_y, m_sine, m_cosine);
input_img->read_elem_sampled(x - canvas_.xmin, y - canvas_.ymin, sampler_, it.out);
}
}

View File

@ -29,8 +29,10 @@ class RotateOperation : public MultiThreadedOperation {
SocketReader *m_imageSocket;
SocketReader *m_degreeSocket;
/* TODO(manzanilla): to be removed with tiled implementation. */
float m_centerX;
float m_centerY;
float m_cosine;
float m_sine;
bool m_doDegree2RadConversion;
@ -49,12 +51,33 @@ class RotateOperation : public MultiThreadedOperation {
y = center_y + (-sine * dx + cosine * dy);
}
static void get_rotation_center(const rcti &area, float &r_x, float &r_y);
static void get_rotation_offset(const rcti &input_canvas,
const rcti &rotate_canvas,
float &r_offset_x,
float &r_offset_y);
static void get_area_rotation_bounds(const rcti &area,
const float center_x,
const float center_y,
const float sine,
const float cosine,
rcti &r_bounds);
static void get_area_rotation_bounds_inverted(const rcti &area,
const float center_x,
const float center_y,
const float sine,
const float cosine,
rcti &r_bounds);
static void get_rotation_area_of_interest(const rcti &input_canvas,
const rcti &rotate_canvas,
const float sine,
const float cosine,
const rcti &output_area,
rcti &r_input_area);
static void get_rotation_canvas(const rcti &input_canvas,
const float sine,
const float cosine,
rcti &r_canvas);
bool determineDependingAreaOfInterest(rcti *input,
ReadBufferOperation *readOperation,
@ -80,6 +103,8 @@ class RotateOperation : public MultiThreadedOperation {
void update_memory_buffer_partial(MemoryBuffer *output,
const rcti &area,
Span<MemoryBuffer *> inputs) override;
void determine_canvas(const rcti &preferred_area, rcti &r_area) override;
};
} // namespace blender::compositor

View File

@ -38,17 +38,21 @@ BaseScaleOperation::BaseScaleOperation()
m_variable_size = false;
}
void BaseScaleOperation::set_scale_canvas_max_size(Size2f size)
{
max_scale_canvas_size_ = size;
}
ScaleOperation::ScaleOperation() : ScaleOperation(DataType::Color)
{
}
ScaleOperation::ScaleOperation(DataType data_type) : BaseScaleOperation()
{
this->addInputSocket(data_type);
this->addInputSocket(data_type, ResizeMode::None);
this->addInputSocket(DataType::Value);
this->addInputSocket(DataType::Value);
this->addOutputSocket(data_type);
this->set_canvas_input_index(0);
this->m_inputOperation = nullptr;
this->m_inputXOperation = nullptr;
this->m_inputYOperation = nullptr;
@ -64,34 +68,52 @@ float ScaleOperation::get_constant_scale(const int input_op_idx, const float fac
return 1.0f;
}
float ScaleOperation::get_constant_scale_x()
float ScaleOperation::get_constant_scale_x(const float width)
{
return get_constant_scale(1, get_relative_scale_x_factor());
return get_constant_scale(X_INPUT_INDEX, get_relative_scale_x_factor(width));
}
float ScaleOperation::get_constant_scale_y()
float ScaleOperation::get_constant_scale_y(const float height)
{
return get_constant_scale(2, get_relative_scale_y_factor());
return get_constant_scale(Y_INPUT_INDEX, get_relative_scale_y_factor(height));
}
void ScaleOperation::scale_area(
rcti &rect, float center_x, float center_y, float scale_x, float scale_y)
bool ScaleOperation::is_scaling_variable()
{
rect.xmin = scale_coord(rect.xmin, center_x, scale_x);
rect.xmax = scale_coord(rect.xmax, center_x, scale_x);
rect.ymin = scale_coord(rect.ymin, center_y, scale_y);
rect.ymax = scale_coord(rect.ymax, center_y, scale_y);
return !get_input_operation(X_INPUT_INDEX)->get_flags().is_constant_operation ||
!get_input_operation(Y_INPUT_INDEX)->get_flags().is_constant_operation;
}
void ScaleOperation::scale_area(rcti &rect, float scale_x, float scale_y)
void ScaleOperation::scale_area(rcti &area, float relative_scale_x, float relative_scale_y)
{
scale_area(rect, m_centerX, m_centerY, scale_x, scale_y);
const rcti src_area = area;
const float center_x = BLI_rcti_size_x(&area) / 2.0f;
const float center_y = BLI_rcti_size_y(&area) / 2.0f;
area.xmin = floorf(scale_coord(area.xmin, center_x, relative_scale_x));
area.xmax = ceilf(scale_coord(area.xmax, center_x, relative_scale_x));
area.ymin = floorf(scale_coord(area.ymin, center_y, relative_scale_y));
area.ymax = ceilf(scale_coord(area.ymax, center_y, relative_scale_y));
float scale_offset_x, scale_offset_y;
ScaleOperation::get_scale_offset(src_area, area, scale_offset_x, scale_offset_y);
BLI_rcti_translate(&area, -scale_offset_x, -scale_offset_y);
}
void ScaleOperation::clamp_area_size_max(rcti &area, Size2f max_size)
{
if (BLI_rcti_size_x(&area) > max_size.x) {
area.xmax = area.xmin + max_size.x;
}
if (BLI_rcti_size_y(&area) > max_size.y) {
area.ymax = area.ymin + max_size.y;
}
}
void ScaleOperation::init_data()
{
m_centerX = getWidth() / 2.0f;
m_centerY = getHeight() / 2.0f;
canvas_center_x_ = canvas_.xmin + getWidth() / 2.0f;
canvas_center_y_ = canvas_.ymin + getHeight() / 2.0f;
}
void ScaleOperation::initExecution()
@ -108,18 +130,52 @@ void ScaleOperation::deinitExecution()
this->m_inputYOperation = nullptr;
}
void ScaleOperation::get_scale_offset(const rcti &input_canvas,
const rcti &scale_canvas,
float &r_scale_offset_x,
float &r_scale_offset_y)
{
r_scale_offset_x = (BLI_rcti_size_x(&input_canvas) - BLI_rcti_size_x(&scale_canvas)) / 2.0f;
r_scale_offset_y = (BLI_rcti_size_y(&input_canvas) - BLI_rcti_size_y(&scale_canvas)) / 2.0f;
}
void ScaleOperation::get_scale_area_of_interest(const rcti &input_canvas,
const rcti &scale_canvas,
const float relative_scale_x,
const float relative_scale_y,
const rcti &output_area,
rcti &r_input_area)
{
const float scale_center_x = BLI_rcti_size_x(&input_canvas) / 2.0f;
const float scale_center_y = BLI_rcti_size_y(&input_canvas) / 2.0f;
float scale_offset_x, scale_offset_y;
ScaleOperation::get_scale_offset(input_canvas, scale_canvas, scale_offset_x, scale_offset_y);
r_input_area.xmin = floorf(
scale_coord_inverted(output_area.xmin + scale_offset_x, scale_center_x, relative_scale_x));
r_input_area.xmax = ceilf(
scale_coord_inverted(output_area.xmax + scale_offset_x, scale_center_x, relative_scale_x));
r_input_area.ymin = floorf(
scale_coord_inverted(output_area.ymin + scale_offset_y, scale_center_y, relative_scale_y));
r_input_area.ymax = ceilf(
scale_coord_inverted(output_area.ymax + scale_offset_y, scale_center_y, relative_scale_y));
}
void ScaleOperation::get_area_of_interest(const int input_idx,
const rcti &output_area,
rcti &r_input_area)
{
r_input_area = output_area;
if (input_idx != 0 || m_variable_size) {
if (input_idx != 0 || is_scaling_variable()) {
return;
}
float scale_x = get_constant_scale_x();
float scale_y = get_constant_scale_y();
scale_area(r_input_area, scale_x, scale_y);
NodeOperation *image_op = get_input_operation(IMAGE_INPUT_INDEX);
const float scale_x = get_constant_scale_x(image_op->getWidth());
const float scale_y = get_constant_scale_y(image_op->getHeight());
get_scale_area_of_interest(
image_op->get_canvas(), this->get_canvas(), scale_x, scale_y, output_area, r_input_area);
expand_area_for_sampler(r_input_area, (PixelSampler)m_sampler);
}
@ -127,18 +183,70 @@ void ScaleOperation::update_memory_buffer_partial(MemoryBuffer *output,
const rcti &area,
Span<MemoryBuffer *> inputs)
{
const MemoryBuffer *input_img = inputs[0];
MemoryBuffer *input_x = inputs[1];
MemoryBuffer *input_y = inputs[2];
const float scale_x_factor = get_relative_scale_x_factor();
const float scale_y_factor = get_relative_scale_y_factor();
NodeOperation *input_image_op = get_input_operation(IMAGE_INPUT_INDEX);
const int input_image_width = input_image_op->getWidth();
const int input_image_height = input_image_op->getHeight();
const float scale_x_factor = get_relative_scale_x_factor(input_image_width);
const float scale_y_factor = get_relative_scale_y_factor(input_image_height);
const float scale_center_x = input_image_width / 2.0f;
const float scale_center_y = input_image_height / 2.0f;
float from_scale_offset_x, from_scale_offset_y;
ScaleOperation::get_scale_offset(
input_image_op->get_canvas(), this->get_canvas(), from_scale_offset_x, from_scale_offset_y);
const MemoryBuffer *input_image = inputs[IMAGE_INPUT_INDEX];
MemoryBuffer *input_x = inputs[X_INPUT_INDEX];
MemoryBuffer *input_y = inputs[Y_INPUT_INDEX];
BuffersIterator<float> it = output->iterate_with({input_x, input_y}, area);
for (; !it.is_end(); ++it) {
const float rel_scale_x = *it.in(0) * scale_x_factor;
const float rel_scale_y = *it.in(1) * scale_y_factor;
const float scaled_x = scale_coord(it.x, m_centerX, rel_scale_x);
const float scaled_y = scale_coord(it.y, m_centerY, rel_scale_y);
input_img->read_elem_sampled(scaled_x, scaled_y, (PixelSampler)m_sampler, it.out);
const float scaled_x = scale_coord_inverted(
from_scale_offset_x + canvas_.xmin + it.x, scale_center_x, rel_scale_x);
const float scaled_y = scale_coord_inverted(
from_scale_offset_y + canvas_.ymin + it.y, scale_center_y, rel_scale_y);
input_image->read_elem_sampled(
scaled_x - canvas_.xmin, scaled_y - canvas_.ymin, (PixelSampler)m_sampler, it.out);
}
}
void ScaleOperation::determine_canvas(const rcti &preferred_area, rcti &r_area)
{
if (execution_model_ == eExecutionModel::Tiled) {
NodeOperation::determine_canvas(preferred_area, r_area);
return;
}
const bool image_determined =
getInputSocket(IMAGE_INPUT_INDEX)->determine_canvas(preferred_area, r_area);
if (image_determined) {
rcti image_canvas = r_area;
rcti unused;
NodeOperationInput *x_socket = getInputSocket(X_INPUT_INDEX);
NodeOperationInput *y_socket = getInputSocket(Y_INPUT_INDEX);
x_socket->determine_canvas(image_canvas, unused);
y_socket->determine_canvas(image_canvas, unused);
if (is_scaling_variable()) {
/* Do not scale canvas. */
return;
}
/* Determine scaled canvas. */
const float input_width = BLI_rcti_size_x(&r_area);
const float input_height = BLI_rcti_size_y(&r_area);
const float scale_x = get_constant_scale_x(input_width);
const float scale_y = get_constant_scale_y(input_height);
scale_area(r_area, scale_x, scale_y);
const Size2f max_scale_size = {MAX2(input_width, max_scale_canvas_size_.x),
MAX2(input_height, max_scale_canvas_size_.y)};
clamp_area_size_max(r_area, max_scale_size);
/* Re-determine canvases of x and y constant inputs with scaled canvas as preferred. */
get_input_operation(X_INPUT_INDEX)->unset_canvas();
get_input_operation(Y_INPUT_INDEX)->unset_canvas();
x_socket->determine_canvas(r_area, unused);
y_socket->determine_canvas(r_area, unused);
}
}
@ -166,8 +274,8 @@ void ScaleRelativeOperation::executePixelSampled(float output[4],
const float scx = scaleX[0];
const float scy = scaleY[0];
float nx = this->m_centerX + (x - this->m_centerX) / scx;
float ny = this->m_centerY + (y - this->m_centerY) / scy;
float nx = this->canvas_center_x_ + (x - this->canvas_center_x_) / scx;
float ny = this->canvas_center_y_ + (y - this->canvas_center_y_) / scy;
this->m_inputOperation->readSampled(output, nx, ny, effective_sampler);
}
@ -186,10 +294,10 @@ bool ScaleRelativeOperation::determineDependingAreaOfInterest(rcti *input,
const float scx = scaleX[0];
const float scy = scaleY[0];
newInput.xmax = this->m_centerX + (input->xmax - this->m_centerX) / scx + 1;
newInput.xmin = this->m_centerX + (input->xmin - this->m_centerX) / scx - 1;
newInput.ymax = this->m_centerY + (input->ymax - this->m_centerY) / scy + 1;
newInput.ymin = this->m_centerY + (input->ymin - this->m_centerY) / scy - 1;
newInput.xmax = this->canvas_center_x_ + (input->xmax - this->canvas_center_x_) / scx + 1;
newInput.xmin = this->canvas_center_x_ + (input->xmin - this->canvas_center_x_) / scx - 1;
newInput.ymax = this->canvas_center_y_ + (input->ymax - this->canvas_center_y_) / scy + 1;
newInput.ymin = this->canvas_center_y_ + (input->ymin - this->canvas_center_y_) / scy - 1;
}
else {
newInput.xmax = this->getWidth();
@ -222,8 +330,8 @@ void ScaleAbsoluteOperation::executePixelSampled(float output[4],
float relativeXScale = scx / width;
float relativeYScale = scy / height;
float nx = this->m_centerX + (x - this->m_centerX) / relativeXScale;
float ny = this->m_centerY + (y - this->m_centerY) / relativeYScale;
float nx = this->canvas_center_x_ + (x - this->canvas_center_x_) / relativeXScale;
float ny = this->canvas_center_y_ + (y - this->canvas_center_y_) / relativeYScale;
this->m_inputOperation->readSampled(output, nx, ny, effective_sampler);
}
@ -248,10 +356,14 @@ bool ScaleAbsoluteOperation::determineDependingAreaOfInterest(rcti *input,
float relateveXScale = scx / width;
float relateveYScale = scy / height;
newInput.xmax = this->m_centerX + (input->xmax - this->m_centerX) / relateveXScale;
newInput.xmin = this->m_centerX + (input->xmin - this->m_centerX) / relateveXScale;
newInput.ymax = this->m_centerY + (input->ymax - this->m_centerY) / relateveYScale;
newInput.ymin = this->m_centerY + (input->ymin - this->m_centerY) / relateveYScale;
newInput.xmax = this->canvas_center_x_ +
(input->xmax - this->canvas_center_x_) / relateveXScale;
newInput.xmin = this->canvas_center_x_ +
(input->xmin - this->canvas_center_x_) / relateveXScale;
newInput.ymax = this->canvas_center_y_ +
(input->ymax - this->canvas_center_y_) / relateveYScale;
newInput.ymin = this->canvas_center_y_ +
(input->ymin - this->canvas_center_y_) / relateveYScale;
}
else {
newInput.xmax = this->getWidth();
@ -272,11 +384,12 @@ ScaleFixedSizeOperation::ScaleFixedSizeOperation() : BaseScaleOperation()
this->m_is_offset = false;
}
void ScaleFixedSizeOperation::init_data()
void ScaleFixedSizeOperation::init_data(const rcti &input_canvas)
{
const NodeOperation *input_op = getInputOperation(0);
this->m_relX = input_op->getWidth() / (float)this->m_newWidth;
this->m_relY = input_op->getHeight() / (float)this->m_newHeight;
const int input_width = BLI_rcti_size_x(&input_canvas);
const int input_height = BLI_rcti_size_y(&input_canvas);
this->m_relX = input_width / (float)this->m_newWidth;
this->m_relY = input_height / (float)this->m_newHeight;
/* *** all the options below are for a fairly special case - camera framing *** */
if (this->m_offsetX != 0.0f || this->m_offsetY != 0.0f) {
@ -294,8 +407,8 @@ void ScaleFixedSizeOperation::init_data()
if (this->m_is_aspect) {
/* apply aspect from clip */
const float w_src = input_op->getWidth();
const float h_src = input_op->getHeight();
const float w_src = input_width;
const float h_src = input_height;
/* destination aspect is already applied from the camera frame */
const float w_dst = this->m_newWidth;
@ -310,12 +423,32 @@ void ScaleFixedSizeOperation::init_data()
const float div = asp_src / asp_dst;
this->m_relX /= div;
this->m_offsetX += ((w_src - (w_src * div)) / (w_src / w_dst)) / 2.0f;
if (m_is_crop && execution_model_ == eExecutionModel::FullFrame) {
int fit_width = m_newWidth * div;
if (fit_width > max_scale_canvas_size_.x) {
fit_width = max_scale_canvas_size_.x;
}
const int added_width = fit_width - m_newWidth;
m_newWidth += added_width;
m_offsetX += added_width / 2.0f;
}
}
else {
/* fit Y */
const float div = asp_dst / asp_src;
this->m_relY /= div;
this->m_offsetY += ((h_src - (h_src * div)) / (h_src / h_dst)) / 2.0f;
if (m_is_crop && execution_model_ == eExecutionModel::FullFrame) {
int fit_height = m_newHeight * div;
if (fit_height > max_scale_canvas_size_.y) {
fit_height = max_scale_canvas_size_.y;
}
const int added_height = fit_height - m_newHeight;
m_newHeight += added_height;
m_offsetY += added_height / 2.0f;
}
}
this->m_is_offset = true;
@ -371,9 +504,21 @@ void ScaleFixedSizeOperation::determine_canvas(const rcti &preferred_area, rcti
rcti local_preferred = preferred_area;
local_preferred.xmax = local_preferred.xmin + m_newWidth;
local_preferred.ymax = local_preferred.ymin + m_newHeight;
BaseScaleOperation::determine_canvas(local_preferred, r_area);
r_area.xmax = r_area.xmin + m_newWidth;
r_area.ymax = r_area.ymin + m_newHeight;
rcti input_canvas;
const bool input_determined = getInputSocket(0)->determine_canvas(local_preferred, input_canvas);
if (input_determined) {
init_data(input_canvas);
r_area = input_canvas;
if (execution_model_ == eExecutionModel::FullFrame) {
r_area.xmin /= m_relX;
r_area.ymin /= m_relY;
r_area.xmin += m_offsetX;
r_area.ymin += m_offsetY;
}
r_area.xmax = r_area.xmin + m_newWidth;
r_area.ymax = r_area.ymin + m_newHeight;
}
}
void ScaleFixedSizeOperation::get_area_of_interest(const int input_idx,
@ -382,10 +527,11 @@ void ScaleFixedSizeOperation::get_area_of_interest(const int input_idx,
{
BLI_assert(input_idx == 0);
UNUSED_VARS_NDEBUG(input_idx);
r_input_area.xmax = (output_area.xmax - m_offsetX) * this->m_relX;
r_input_area.xmin = (output_area.xmin - m_offsetX) * this->m_relX;
r_input_area.ymax = (output_area.ymax - m_offsetY) * this->m_relY;
r_input_area.ymin = (output_area.ymin - m_offsetY) * this->m_relY;
r_input_area.xmax = ceilf((output_area.xmax - m_offsetX) * this->m_relX);
r_input_area.xmin = floorf((output_area.xmin - m_offsetX) * this->m_relX);
r_input_area.ymax = ceilf((output_area.ymax - m_offsetY) * this->m_relY);
r_input_area.ymin = floorf((output_area.ymin - m_offsetY) * this->m_relY);
expand_area_for_sampler(r_input_area, (PixelSampler)m_sampler);
}
@ -398,14 +544,17 @@ void ScaleFixedSizeOperation::update_memory_buffer_partial(MemoryBuffer *output,
BuffersIterator<float> it = output->iterate_with({}, area);
if (this->m_is_offset) {
for (; !it.is_end(); ++it) {
const float nx = (it.x - this->m_offsetX) * this->m_relX;
const float ny = (it.y - this->m_offsetY) * this->m_relY;
input_img->read_elem_sampled(nx, ny, sampler, it.out);
const float nx = (canvas_.xmin + it.x - this->m_offsetX) * this->m_relX;
const float ny = (canvas_.ymin + it.y - this->m_offsetY) * this->m_relY;
input_img->read_elem_sampled(nx - canvas_.xmin, ny - canvas_.ymin, sampler, it.out);
}
}
else {
for (; !it.is_end(); ++it) {
input_img->read_elem_sampled(it.x * this->m_relX, it.y * this->m_relY, sampler, it.out);
input_img->read_elem_sampled((canvas_.xmin + it.x) * this->m_relX - canvas_.xmin,
(canvas_.ymin + it.y) * this->m_relY - canvas_.ymin,
sampler,
it.out);
}
}
}

View File

@ -23,6 +23,9 @@
namespace blender::compositor {
class BaseScaleOperation : public MultiThreadedOperation {
public:
static constexpr float DEFAULT_MAX_SCALE_CANVAS_SIZE = 12000;
public:
void setSampler(PixelSampler sampler)
{
@ -33,6 +36,8 @@ class BaseScaleOperation : public MultiThreadedOperation {
m_variable_size = variable_size;
};
void set_scale_canvas_max_size(Size2f size);
protected:
BaseScaleOperation();
@ -41,20 +46,26 @@ class BaseScaleOperation : public MultiThreadedOperation {
return (m_sampler == -1) ? sampler : (PixelSampler)m_sampler;
}
Size2f max_scale_canvas_size_ = {DEFAULT_MAX_SCALE_CANVAS_SIZE, DEFAULT_MAX_SCALE_CANVAS_SIZE};
int m_sampler;
/* TODO(manzanilla): to be removed with tiled implementation. */
bool m_variable_size;
};
class ScaleOperation : public BaseScaleOperation {
public:
static constexpr float MIN_SCALE = 0.0001f;
static constexpr float MIN_RELATIVE_SCALE = 0.0001f;
protected:
static constexpr int IMAGE_INPUT_INDEX = 0;
static constexpr int X_INPUT_INDEX = 1;
static constexpr int Y_INPUT_INDEX = 2;
SocketReader *m_inputOperation;
SocketReader *m_inputXOperation;
SocketReader *m_inputYOperation;
float m_centerX;
float m_centerY;
float canvas_center_x_;
float canvas_center_y_;
public:
ScaleOperation();
@ -62,9 +73,28 @@ class ScaleOperation : public BaseScaleOperation {
static float scale_coord(const float coord, const float center, const float relative_scale)
{
return center + (coord - center) / MAX2(relative_scale, MIN_SCALE);
return center + (coord - center) * MAX2(relative_scale, MIN_RELATIVE_SCALE);
}
static void scale_area(rcti &rect, float center_x, float center_y, float scale_x, float scale_y);
static float scale_coord_inverted(const float coord,
const float center,
const float relative_scale)
{
return center + (coord - center) / MAX2(relative_scale, MIN_RELATIVE_SCALE);
}
static void get_scale_offset(const rcti &input_canvas,
const rcti &scale_canvas,
float &r_scale_offset_x,
float &r_scale_offset_y);
static void scale_area(rcti &area, float relative_scale_x, float relative_scale_y);
static void get_scale_area_of_interest(const rcti &input_canvas,
const rcti &scale_canvas,
const float relative_scale_x,
const float relative_scale_y,
const rcti &output_area,
rcti &r_input_area);
static void clamp_area_size_max(rcti &area, Size2f max_size);
void init_data() override;
void initExecution() override;
@ -75,15 +105,17 @@ class ScaleOperation : public BaseScaleOperation {
const rcti &area,
Span<MemoryBuffer *> inputs) override;
void determine_canvas(const rcti &preferred_area, rcti &r_area) override;
protected:
virtual float get_relative_scale_x_factor() = 0;
virtual float get_relative_scale_y_factor() = 0;
virtual float get_relative_scale_x_factor(float width) = 0;
virtual float get_relative_scale_y_factor(float height) = 0;
private:
bool is_scaling_variable();
float get_constant_scale(int input_op_idx, float factor);
float get_constant_scale_x();
float get_constant_scale_y();
void scale_area(rcti &rect, float scale_x, float scale_y);
float get_constant_scale_x(float width);
float get_constant_scale_y(float height);
};
class ScaleRelativeOperation : public ScaleOperation {
@ -94,11 +126,13 @@ class ScaleRelativeOperation : public ScaleOperation {
ReadBufferOperation *readOperation,
rcti *output) override;
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
float get_relative_scale_x_factor() override
float get_relative_scale_x_factor(float UNUSED(width)) override
{
return 1.0f;
}
float get_relative_scale_y_factor() override
float get_relative_scale_y_factor(float UNUSED(height)) override
{
return 1.0f;
}
@ -110,13 +144,15 @@ class ScaleAbsoluteOperation : public ScaleOperation {
ReadBufferOperation *readOperation,
rcti *output) override;
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
float get_relative_scale_x_factor() override
float get_relative_scale_x_factor(float width) override
{
return 1.0f / getWidth();
return 1.0f / width;
}
float get_relative_scale_y_factor() override
float get_relative_scale_y_factor(float height) override
{
return 1.0f / getHeight();
return 1.0f / height;
}
};
@ -144,7 +180,6 @@ class ScaleFixedSizeOperation : public BaseScaleOperation {
void determine_canvas(const rcti &preferred_area, rcti &r_area) override;
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
void init_data() override;
void initExecution() override;
void deinitExecution() override;
void setNewWidth(int width)
@ -173,6 +208,9 @@ class ScaleFixedSizeOperation : public BaseScaleOperation {
void update_memory_buffer_partial(MemoryBuffer *output,
const rcti &area,
Span<MemoryBuffer *> inputs) override;
private:
void init_data(const rcti &input_canvas);
};
} // namespace blender::compositor

View File

@ -382,6 +382,23 @@ void ScreenLensDistortionOperation::updateVariables(float distortion, float disp
mul_v3_v3fl(m_k4, m_k, 4.0f);
}
void ScreenLensDistortionOperation::determine_canvas(const rcti &preferred_area, rcti &r_area)
{
switch (execution_model_) {
case eExecutionModel::FullFrame: {
set_determined_canvas_modifier([=](rcti &canvas) {
/* Ensure screen space. */
BLI_rcti_translate(&canvas, -canvas.xmin, -canvas.ymin);
});
break;
}
default:
break;
}
NodeOperation::determine_canvas(preferred_area, r_area);
}
void ScreenLensDistortionOperation::get_area_of_interest(const int input_idx,
const rcti &UNUSED(output_area),
rcti &r_input_area)

View File

@ -86,6 +86,7 @@ class ScreenLensDistortionOperation : public MultiThreadedOperation {
ReadBufferOperation *readOperation,
rcti *output) 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,

View File

@ -74,26 +74,18 @@ void TextureBaseOperation::deinitExecution()
void TextureBaseOperation::determine_canvas(const rcti &preferred_area, rcti &r_area)
{
switch (execution_model_) {
case eExecutionModel::Tiled: {
r_area = preferred_area;
if (BLI_rcti_is_empty(&preferred_area)) {
int width = this->m_rd->xsch * this->m_rd->size / 100;
int height = this->m_rd->ysch * this->m_rd->size / 100;
r_area.xmax = preferred_area.xmin + width;
r_area.ymax = preferred_area.ymin + height;
}
break;
}
case eExecutionModel::FullFrame: {
/* Determine inputs. */
rcti temp;
NodeOperation::determine_canvas(preferred_area, temp);
r_area = preferred_area;
if (BLI_rcti_is_empty(&preferred_area)) {
int width = this->m_rd->xsch * this->m_rd->size / 100;
int height = this->m_rd->ysch * this->m_rd->size / 100;
r_area.xmax = preferred_area.xmin + width;
r_area.ymax = preferred_area.ymin + height;
}
/* Don't use input areas, they are only used as parameters. */
r_area = preferred_area;
break;
}
if (execution_model_ == eExecutionModel::FullFrame) {
/* Determine inputs. */
rcti temp;
NodeOperation::determine_canvas(r_area, temp);
}
}

View File

@ -28,7 +28,7 @@ namespace blender::compositor {
TonemapOperation::TonemapOperation()
{
this->addInputSocket(DataType::Color, ResizeMode::None);
this->addInputSocket(DataType::Color, ResizeMode::Align);
this->addOutputSocket(DataType::Color);
this->m_imageReader = nullptr;
this->m_data = nullptr;

View File

@ -27,55 +27,40 @@ namespace blender::compositor {
TransformOperation::TransformOperation()
{
addInputSocket(DataType::Color);
addInputSocket(DataType::Value);
addInputSocket(DataType::Value);
addInputSocket(DataType::Value);
addInputSocket(DataType::Value);
addInputSocket(DataType::Color, ResizeMode::None);
addInputSocket(DataType::Value, ResizeMode::None);
addInputSocket(DataType::Value, ResizeMode::None);
addInputSocket(DataType::Value, ResizeMode::None);
addInputSocket(DataType::Value, ResizeMode::None);
addOutputSocket(DataType::Color);
translate_factor_x_ = 1.0f;
translate_factor_y_ = 1.0f;
convert_degree_to_rad_ = false;
sampler_ = PixelSampler::Bilinear;
invert_ = false;
max_scale_canvas_size_ = {ScaleOperation::DEFAULT_MAX_SCALE_CANVAS_SIZE,
ScaleOperation::DEFAULT_MAX_SCALE_CANVAS_SIZE};
}
void TransformOperation::set_scale_canvas_max_size(Size2f size)
{
max_scale_canvas_size_ = size;
}
void TransformOperation::init_data()
{
/* Translation. */
translate_x_ = 0;
NodeOperation *x_op = getInputOperation(X_INPUT_INDEX);
if (x_op->get_flags().is_constant_operation) {
translate_x_ = static_cast<ConstantOperation *>(x_op)->get_constant_elem()[0] *
translate_factor_x_;
}
translate_y_ = 0;
NodeOperation *y_op = getInputOperation(Y_INPUT_INDEX);
if (y_op->get_flags().is_constant_operation) {
translate_y_ = static_cast<ConstantOperation *>(y_op)->get_constant_elem()[0] *
translate_factor_y_;
}
/* Scaling. */
scale_center_x_ = getWidth() / 2.0;
scale_center_y_ = getHeight() / 2.0;
constant_scale_ = 1.0f;
NodeOperation *scale_op = getInputOperation(SCALE_INPUT_INDEX);
if (scale_op->get_flags().is_constant_operation) {
constant_scale_ = static_cast<ConstantOperation *>(scale_op)->get_constant_elem()[0];
}
translate_x_ = get_input_operation(X_INPUT_INDEX)->get_constant_value_default(0.0f) *
translate_factor_x_;
translate_y_ = get_input_operation(Y_INPUT_INDEX)->get_constant_value_default(0.0f) *
translate_factor_y_;
/* Rotation. */
rotate_center_x_ = (getWidth() - 1.0) / 2.0;
rotate_center_y_ = (getHeight() - 1.0) / 2.0;
NodeOperation *degree_op = getInputOperation(DEGREE_INPUT_INDEX);
const bool is_constant_degree = degree_op->get_flags().is_constant_operation;
const float degree = is_constant_degree ?
static_cast<ConstantOperation *>(degree_op)->get_constant_elem()[0] :
0.0f;
const float degree = get_input_operation(DEGREE_INPUT_INDEX)->get_constant_value_default(0.0f);
const double rad = convert_degree_to_rad_ ? DEG2RAD((double)degree) : degree;
rotate_cosine_ = cos(rad);
rotate_sine_ = sin(rad);
scale_ = get_input_operation(SCALE_INPUT_INDEX)->get_constant_value_default(1.0f);
}
void TransformOperation::get_area_of_interest(const int input_idx,
@ -84,26 +69,41 @@ void TransformOperation::get_area_of_interest(const int input_idx,
{
switch (input_idx) {
case IMAGE_INPUT_INDEX: {
BLI_rcti_translate(&r_input_area, translate_x_, translate_y_);
ScaleOperation::scale_area(
r_input_area, scale_center_x_, scale_center_y_, constant_scale_, constant_scale_);
RotateOperation::get_area_rotation_bounds(r_input_area,
rotate_center_x_,
rotate_center_y_,
rotate_sine_,
rotate_cosine_,
r_input_area);
NodeOperation *image_op = get_input_operation(IMAGE_INPUT_INDEX);
const rcti &image_canvas = image_op->get_canvas();
if (invert_) {
/* Scale -> Rotate -> Translate. */
r_input_area = output_area;
BLI_rcti_translate(&r_input_area, -translate_x_, -translate_y_);
RotateOperation::get_rotation_area_of_interest(scale_canvas_,
rotate_canvas_,
rotate_sine_,
rotate_cosine_,
r_input_area,
r_input_area);
ScaleOperation::get_scale_area_of_interest(
image_canvas, scale_canvas_, scale_, scale_, r_input_area, r_input_area);
}
else {
/* Translate -> Rotate -> Scale. */
ScaleOperation::get_scale_area_of_interest(
rotate_canvas_, scale_canvas_, scale_, scale_, output_area, r_input_area);
RotateOperation::get_rotation_area_of_interest(translate_canvas_,
rotate_canvas_,
rotate_sine_,
rotate_cosine_,
r_input_area,
r_input_area);
BLI_rcti_translate(&r_input_area, -translate_x_, -translate_y_);
}
expand_area_for_sampler(r_input_area, sampler_);
break;
}
case X_INPUT_INDEX:
case Y_INPUT_INDEX:
case DEGREE_INPUT_INDEX: {
r_input_area = COM_CONSTANT_INPUT_AREA_OF_INTEREST;
break;
}
case DEGREE_INPUT_INDEX:
case SCALE_INPUT_INDEX: {
r_input_area = output_area;
r_input_area = COM_CONSTANT_INPUT_AREA_OF_INTEREST;
break;
}
}
@ -114,8 +114,7 @@ void TransformOperation::update_memory_buffer_partial(MemoryBuffer *output,
Span<MemoryBuffer *> inputs)
{
const MemoryBuffer *input_img = inputs[IMAGE_INPUT_INDEX];
MemoryBuffer *input_scale = inputs[SCALE_INPUT_INDEX];
BuffersIterator<float> it = output->iterate_with({input_scale}, area);
BuffersIterator<float> it = output->iterate_with({}, area);
if (invert_) {
transform_inverted(it, input_img);
}
@ -124,31 +123,111 @@ void TransformOperation::update_memory_buffer_partial(MemoryBuffer *output,
}
}
void TransformOperation::transform(BuffersIterator<float> &it, const MemoryBuffer *input_img)
void TransformOperation::determine_canvas(const rcti &preferred_area, rcti &r_area)
{
for (; !it.is_end(); ++it) {
const float scale = *it.in(0);
float x = it.x - translate_x_;
float y = it.y - translate_y_;
RotateOperation::rotate_coords(
x, y, rotate_center_x_, rotate_center_y_, rotate_sine_, rotate_cosine_);
x = ScaleOperation::scale_coord(x, scale_center_x_, scale);
y = ScaleOperation::scale_coord(y, scale_center_y_, scale);
input_img->read_elem_sampled(x, y, sampler_, it.out);
const bool image_determined =
getInputSocket(IMAGE_INPUT_INDEX)->determine_canvas(preferred_area, r_area);
if (image_determined) {
rcti image_canvas = r_area;
rcti unused;
getInputSocket(X_INPUT_INDEX)->determine_canvas(image_canvas, unused);
getInputSocket(Y_INPUT_INDEX)->determine_canvas(image_canvas, unused);
getInputSocket(DEGREE_INPUT_INDEX)->determine_canvas(image_canvas, unused);
getInputSocket(SCALE_INPUT_INDEX)->determine_canvas(image_canvas, unused);
init_data();
if (invert_) {
/* Scale -> Rotate -> Translate. */
scale_canvas_ = image_canvas;
ScaleOperation::scale_area(scale_canvas_, scale_, scale_);
const Size2f max_scale_size = {
MAX2(BLI_rcti_size_x(&image_canvas), max_scale_canvas_size_.x),
MAX2(BLI_rcti_size_y(&image_canvas), max_scale_canvas_size_.y)};
ScaleOperation::clamp_area_size_max(scale_canvas_, max_scale_size);
RotateOperation::get_rotation_canvas(
scale_canvas_, rotate_sine_, rotate_cosine_, rotate_canvas_);
translate_canvas_ = rotate_canvas_;
BLI_rcti_translate(&translate_canvas_, translate_x_, translate_y_);
r_area = translate_canvas_;
}
else {
/* Translate -> Rotate -> Scale. */
translate_canvas_ = image_canvas;
BLI_rcti_translate(&translate_canvas_, translate_x_, translate_y_);
RotateOperation::get_rotation_canvas(
translate_canvas_, rotate_sine_, rotate_cosine_, rotate_canvas_);
scale_canvas_ = rotate_canvas_;
ScaleOperation::scale_area(scale_canvas_, scale_, scale_);
const Size2f max_scale_size = {
MAX2(BLI_rcti_size_x(&rotate_canvas_), max_scale_canvas_size_.x),
MAX2(BLI_rcti_size_y(&rotate_canvas_), max_scale_canvas_size_.y)};
ScaleOperation::clamp_area_size_max(scale_canvas_, max_scale_size);
r_area = scale_canvas_;
}
}
}
/** Translate -> Rotate -> Scale. */
void TransformOperation::transform(BuffersIterator<float> &it, const MemoryBuffer *input_img)
{
float rotate_center_x, rotate_center_y;
RotateOperation::get_rotation_center(translate_canvas_, rotate_center_x, rotate_center_y);
float rotate_offset_x, rotate_offset_y;
RotateOperation::get_rotation_offset(
translate_canvas_, rotate_canvas_, rotate_offset_x, rotate_offset_y);
const float scale_center_x = BLI_rcti_size_x(&rotate_canvas_) / 2.0f;
const float scale_center_y = BLI_rcti_size_y(&rotate_canvas_) / 2.0f;
float scale_offset_x, scale_offset_y;
ScaleOperation::get_scale_offset(rotate_canvas_, scale_canvas_, scale_offset_x, scale_offset_y);
for (; !it.is_end(); ++it) {
float x = ScaleOperation::scale_coord_inverted(it.x + scale_offset_x, scale_center_x, scale_);
float y = ScaleOperation::scale_coord_inverted(it.y + scale_offset_y, scale_center_y, scale_);
x = rotate_offset_x + x;
y = rotate_offset_y + y;
RotateOperation::rotate_coords(
x, y, rotate_center_x, rotate_center_y, rotate_sine_, rotate_cosine_);
input_img->read_elem_sampled(x - translate_x_, y - translate_y_, sampler_, it.out);
}
}
/** Scale -> Rotate -> Translate. */
void TransformOperation::transform_inverted(BuffersIterator<float> &it,
const MemoryBuffer *input_img)
{
const rcti &image_canvas = get_input_operation(IMAGE_INPUT_INDEX)->get_canvas();
const float scale_center_x = BLI_rcti_size_x(&image_canvas) / 2.0f - translate_x_;
const float scale_center_y = BLI_rcti_size_y(&image_canvas) / 2.0f - translate_y_;
float scale_offset_x, scale_offset_y;
ScaleOperation::get_scale_offset(image_canvas, scale_canvas_, scale_offset_x, scale_offset_y);
float rotate_center_x, rotate_center_y;
RotateOperation::get_rotation_center(translate_canvas_, rotate_center_x, rotate_center_y);
rotate_center_x -= translate_x_;
rotate_center_y -= translate_y_;
float rotate_offset_x, rotate_offset_y;
RotateOperation::get_rotation_offset(
scale_canvas_, rotate_canvas_, rotate_offset_x, rotate_offset_y);
for (; !it.is_end(); ++it) {
const float scale = *it.in(0);
float x = ScaleOperation::scale_coord(it.x, scale_center_x_, scale);
float y = ScaleOperation::scale_coord(it.y, scale_center_y_, scale);
float x = rotate_offset_x + (it.x - translate_x_);
float y = rotate_offset_y + (it.y - translate_y_);
RotateOperation::rotate_coords(
x, y, rotate_center_x_, rotate_center_y_, rotate_sine_, rotate_cosine_);
x -= translate_x_;
y -= translate_y_;
x, y, rotate_center_x, rotate_center_y, rotate_sine_, rotate_cosine_);
x = ScaleOperation::scale_coord_inverted(x + scale_offset_x, scale_center_x, scale_);
y = ScaleOperation::scale_coord_inverted(y + scale_offset_y, scale_center_y, scale_);
input_img->read_elem_sampled(x, y, sampler_, it.out);
}
}

View File

@ -30,15 +30,14 @@ class TransformOperation : public MultiThreadedOperation {
constexpr static int DEGREE_INPUT_INDEX = 3;
constexpr static int SCALE_INPUT_INDEX = 4;
float scale_center_x_;
float scale_center_y_;
float rotate_center_x_;
float rotate_center_y_;
float rotate_cosine_;
float rotate_sine_;
float translate_x_;
float translate_y_;
float constant_scale_;
int translate_x_;
int translate_y_;
float scale_;
rcti scale_canvas_;
rcti rotate_canvas_;
rcti translate_canvas_;
/* Set variables. */
PixelSampler sampler_;
@ -46,6 +45,7 @@ class TransformOperation : public MultiThreadedOperation {
float translate_factor_x_;
float translate_factor_y_;
bool invert_;
Size2f max_scale_canvas_size_;
public:
TransformOperation();
@ -71,16 +71,18 @@ class TransformOperation : public MultiThreadedOperation {
invert_ = value;
}
void set_scale_canvas_max_size(Size2f size);
void init_data() override;
void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override;
void update_memory_buffer_partial(MemoryBuffer *output,
const rcti &area,
Span<MemoryBuffer *> inputs) override;
void determine_canvas(const rcti &preferred_area, rcti &r_area) override;
private:
/** Translate -> Rotate -> Scale. */
void transform(BuffersIterator<float> &it, const MemoryBuffer *input_img);
/** Scale -> Rotate -> Translate. */
void transform_inverted(BuffersIterator<float> &it, const MemoryBuffer *input_img);
};

View File

@ -23,11 +23,11 @@ namespace blender::compositor {
TranslateOperation::TranslateOperation() : TranslateOperation(DataType::Color)
{
}
TranslateOperation::TranslateOperation(DataType data_type)
TranslateOperation::TranslateOperation(DataType data_type, ResizeMode resize_mode)
{
this->addInputSocket(data_type);
this->addInputSocket(DataType::Value);
this->addInputSocket(DataType::Value);
this->addInputSocket(data_type, resize_mode);
this->addInputSocket(DataType::Value, ResizeMode::None);
this->addInputSocket(DataType::Value, ResizeMode::None);
this->addOutputSocket(data_type);
this->set_canvas_input_index(0);
this->m_inputOperation = nullptr;
@ -39,6 +39,7 @@ TranslateOperation::TranslateOperation(DataType data_type)
this->x_extend_mode_ = MemoryBufferExtend::Clip;
this->y_extend_mode_ = MemoryBufferExtend::Clip;
}
void TranslateOperation::initExecution()
{
this->m_inputOperation = this->getInputSocketReader(0);
@ -122,6 +123,9 @@ void TranslateOperation::get_area_of_interest(const int input_idx,
BLI_rcti_translate(&r_input_area, 0, -delta_y);
}
}
else {
r_input_area = output_area;
}
}
void TranslateOperation::update_memory_buffer_partial(MemoryBuffer *output,
@ -142,4 +146,27 @@ void TranslateOperation::update_memory_buffer_partial(MemoryBuffer *output,
}
}
TranslateCanvasOperation::TranslateCanvasOperation()
: TranslateOperation(DataType::Color, ResizeMode::None)
{
}
void TranslateCanvasOperation::determine_canvas(const rcti &preferred_area, rcti &r_area)
{
const bool determined =
getInputSocket(IMAGE_INPUT_INDEX)->determine_canvas(preferred_area, r_area);
if (determined) {
NodeOperationInput *x_socket = getInputSocket(X_INPUT_INDEX);
NodeOperationInput *y_socket = getInputSocket(Y_INPUT_INDEX);
rcti unused;
x_socket->determine_canvas(r_area, unused);
y_socket->determine_canvas(r_area, unused);
ensureDelta();
const float delta_x = x_extend_mode_ == MemoryBufferExtend::Clip ? getDeltaX() : 0.0f;
const float delta_y = y_extend_mode_ == MemoryBufferExtend::Clip ? getDeltaY() : 0.0f;
BLI_rcti_translate(&r_area, delta_x, delta_y);
}
}
} // namespace blender::compositor

View File

@ -24,6 +24,11 @@
namespace blender::compositor {
class TranslateOperation : public MultiThreadedOperation {
protected:
static constexpr int IMAGE_INPUT_INDEX = 0;
static constexpr int X_INPUT_INDEX = 1;
static constexpr int Y_INPUT_INDEX = 2;
private:
SocketReader *m_inputOperation;
SocketReader *m_inputXOperation;
@ -33,12 +38,14 @@ class TranslateOperation : public MultiThreadedOperation {
bool m_isDeltaSet;
float m_factorX;
float m_factorY;
protected:
MemoryBufferExtend x_extend_mode_;
MemoryBufferExtend y_extend_mode_;
public:
TranslateOperation();
TranslateOperation(DataType data_type);
TranslateOperation(DataType data_type, ResizeMode mode = ResizeMode::Center);
bool determineDependingAreaOfInterest(rcti *input,
ReadBufferOperation *readOperation,
rcti *output) override;
@ -67,16 +74,8 @@ class TranslateOperation : public MultiThreadedOperation {
this->m_deltaY = tempDelta[0];
}
else {
this->m_deltaX = 0;
NodeOperation *x_op = getInputOperation(1);
if (x_op->get_flags().is_constant_operation) {
this->m_deltaX = ((ConstantOperation *)x_op)->get_constant_elem()[0];
}
this->m_deltaY = 0;
NodeOperation *y_op = getInputOperation(2);
if (y_op->get_flags().is_constant_operation) {
this->m_deltaY = ((ConstantOperation *)y_op)->get_constant_elem()[0];
}
m_deltaX = get_input_operation(X_INPUT_INDEX)->get_constant_value_default(0.0f);
m_deltaY = get_input_operation(Y_INPUT_INDEX)->get_constant_value_default(0.0f);
}
this->m_isDeltaSet = true;
@ -93,4 +92,10 @@ class TranslateOperation : public MultiThreadedOperation {
Span<MemoryBuffer *> inputs) override;
};
class TranslateCanvasOperation : public TranslateOperation {
public:
TranslateCanvasOperation();
void determine_canvas(const rcti &preferred_area, rcti &r_area) override;
};
} // namespace blender::compositor

View File

@ -28,8 +28,8 @@ namespace blender::compositor {
VariableSizeBokehBlurOperation::VariableSizeBokehBlurOperation()
{
this->addInputSocket(DataType::Color);
this->addInputSocket(DataType::Color, ResizeMode::None); /* Do not resize the bokeh image. */
this->addInputSocket(DataType::Value); /* Radius. */
this->addInputSocket(DataType::Color, ResizeMode::Align); /* Do not resize the bokeh image. */
this->addInputSocket(DataType::Value); /* Radius. */
#ifdef COM_DEFOCUS_SEARCH
/* Inverse search radius optimization structure. */
this->addInputSocket(DataType::Color, ResizeMode::None);
@ -440,7 +440,7 @@ void VariableSizeBokehBlurOperation::update_memory_buffer_partial(MemoryBuffer *
/* #InverseSearchRadiusOperation. */
InverseSearchRadiusOperation::InverseSearchRadiusOperation()
{
this->addInputSocket(DataType::Value, ResizeMode::None); /* Radius. */
this->addInputSocket(DataType::Value, ResizeMode::Align); /* Radius. */
this->addOutputSocket(DataType::Color);
this->flags.complex = true;
this->m_inputRadius = nullptr;

View File

@ -23,6 +23,7 @@
#include "BLI_math_color.h"
#include "BLI_math_vector.h"
#include "BLI_utildefines.h"
#include "COM_ExecutionSystem.h"
#include "MEM_guardedalloc.h"
#include "PIL_time.h"
#include "WM_api.h"
@ -34,6 +35,8 @@
namespace blender::compositor {
static int MAX_VIEWER_TRANSLATION_PADDING = 12000;
ViewerOperation::ViewerOperation()
{
this->setImage(nullptr);
@ -67,7 +70,7 @@ void ViewerOperation::initExecution()
this->m_depthInput = getInputSocketReader(2);
this->m_doDepthBuffer = (this->m_depthInput != nullptr);
if (isActiveViewerOutput()) {
if (isActiveViewerOutput() && !exec_system_->is_breaked()) {
initImage();
}
}
@ -130,6 +133,7 @@ void ViewerOperation::determine_canvas(const rcti &preferred_area, rcti &r_area)
rcti local_preferred = preferred_area;
local_preferred.xmax = local_preferred.xmin + sceneRenderWidth;
local_preferred.ymax = local_preferred.ymin + sceneRenderHeight;
NodeOperation::determine_canvas(local_preferred, r_area);
}
@ -155,13 +159,24 @@ void ViewerOperation::initImage()
BLI_thread_unlock(LOCK_DRAW_IMAGE);
return;
}
if (ibuf->x != (int)getWidth() || ibuf->y != (int)getHeight()) {
int padding_x = abs(canvas_.xmin) * 2;
int padding_y = abs(canvas_.ymin) * 2;
if (padding_x > MAX_VIEWER_TRANSLATION_PADDING) {
padding_x = MAX_VIEWER_TRANSLATION_PADDING;
}
if (padding_y > MAX_VIEWER_TRANSLATION_PADDING) {
padding_y = MAX_VIEWER_TRANSLATION_PADDING;
}
display_width_ = getWidth() + padding_x;
display_height_ = getHeight() + padding_y;
if (ibuf->x != display_width_ || ibuf->y != display_height_) {
imb_freerectImBuf(ibuf);
imb_freerectfloatImBuf(ibuf);
IMB_freezbuffloatImBuf(ibuf);
ibuf->x = getWidth();
ibuf->y = getHeight();
ibuf->x = display_width_;
ibuf->y = display_height_;
/* zero size can happen if no image buffers exist to define a sensible resolution */
if (ibuf->x > 0 && ibuf->y > 0) {
imb_addrectfloatImBuf(ibuf);
@ -193,11 +208,15 @@ void ViewerOperation::initImage()
void ViewerOperation::updateImage(const rcti *rect)
{
if (exec_system_->is_breaked()) {
return;
}
float *buffer = m_outputBuffer;
IMB_partial_display_buffer_update(this->m_ibuf,
buffer,
nullptr,
getWidth(),
display_width_,
0,
0,
this->m_viewSettings,
@ -227,29 +246,46 @@ void ViewerOperation::update_memory_buffer_partial(MemoryBuffer *UNUSED(output),
return;
}
const int offset_x = area.xmin + (canvas_.xmin > 0 ? canvas_.xmin * 2 : 0);
const int offset_y = area.ymin + (canvas_.ymin > 0 ? canvas_.ymin * 2 : 0);
MemoryBuffer output_buffer(
m_outputBuffer, COM_DATA_TYPE_COLOR_CHANNELS, getWidth(), getHeight());
m_outputBuffer, COM_DATA_TYPE_COLOR_CHANNELS, display_width_, display_height_);
const MemoryBuffer *input_image = inputs[0];
output_buffer.copy_from(input_image, area);
output_buffer.copy_from(input_image, area, offset_x, offset_y);
if (this->m_useAlphaInput) {
const MemoryBuffer *input_alpha = inputs[1];
output_buffer.copy_from(input_alpha, area, 0, COM_DATA_TYPE_VALUE_CHANNELS, 3);
output_buffer.copy_from(
input_alpha, area, 0, COM_DATA_TYPE_VALUE_CHANNELS, offset_x, offset_y, 3);
}
if (m_depthBuffer) {
MemoryBuffer depth_buffer(
m_depthBuffer, COM_DATA_TYPE_VALUE_CHANNELS, getWidth(), getHeight());
m_depthBuffer, COM_DATA_TYPE_VALUE_CHANNELS, display_width_, display_height_);
const MemoryBuffer *input_depth = inputs[2];
depth_buffer.copy_from(input_depth, area);
depth_buffer.copy_from(input_depth, area, offset_x, offset_y);
}
updateImage(&area);
rcti display_area;
BLI_rcti_init(&display_area,
offset_x,
offset_x + BLI_rcti_size_x(&area),
offset_y,
offset_y + BLI_rcti_size_y(&area));
updateImage(&display_area);
}
void ViewerOperation::clear_display_buffer()
{
BLI_assert(isActiveViewerOutput());
if (exec_system_->is_breaked()) {
return;
}
initImage();
if (m_outputBuffer == nullptr) {
return;
}
size_t buf_bytes = (size_t)m_ibuf->y * m_ibuf->x * COM_DATA_TYPE_COLOR_CHANNELS * sizeof(float);
if (buf_bytes > 0) {
memset(m_outputBuffer, 0, buf_bytes);

View File

@ -50,6 +50,9 @@ class ViewerOperation : public MultiThreadedOperation {
SocketReader *m_alphaInput;
SocketReader *m_depthInput;
int display_width_;
int display_height_;
public:
ViewerOperation();
void initExecution() override;