Compositor: Re-write Pixelate node for CPU compositor #117223

Merged
Sergey Sharybin merged 3 commits from Sergey/blender:compositor_pixelate into main 2024-01-18 11:31:06 +01:00
6 changed files with 156 additions and 25 deletions

View File

@ -16,18 +16,16 @@ PixelateNode::PixelateNode(bNode *editor_node) : Node(editor_node)
void PixelateNode::convert_to_operations(NodeConverter &converter,
const CompositorContext & /*context*/) const
{
const bNode *editor_node = this->get_bnode();
NodeInput *input_socket = this->get_input_socket(0);
NodeOutput *output_socket = this->get_output_socket(0);
DataType datatype = input_socket->get_data_type();
if (input_socket->is_linked()) {
NodeOutput *link = input_socket->get_link();
datatype = link->get_data_type();
}
PixelateOperation *operation = new PixelateOperation(datatype);
PixelateOperation *operation = new PixelateOperation();
converter.add_operation(operation);
operation->set_pixel_size(editor_node->custom1);
converter.map_input_socket(input_socket, operation->get_input_socket(0));
converter.map_output_socket(output_socket, operation->get_output_socket(0));
}

View File

@ -4,16 +4,20 @@
#include "COM_PixelateOperation.h"
#include <algorithm>
namespace blender::compositor {
PixelateOperation::PixelateOperation(DataType data_type)
PixelateOperation::PixelateOperation()
{
this->add_input_socket(data_type);
this->add_output_socket(data_type);
this->add_input_socket(DataType::Color);
this->add_output_socket(DataType::Color);
this->set_canvas_input_index(0);
input_operation_ = nullptr;
flags_.can_be_constant = true;
pixel_size_ = 1;
}
void PixelateOperation::init_execution()
@ -26,14 +30,97 @@ void PixelateOperation::deinit_execution()
input_operation_ = nullptr;
}
void PixelateOperation::execute_pixel_sampled(float output[4],
float x,
float y,
PixelSampler sampler)
bool PixelateOperation::determine_depending_area_of_interest(rcti *input,
ReadBufferOperation *read_operation,
rcti *output)
{
float nx = round(x);
float ny = round(y);
input_operation_->read_sampled(output, nx, ny, sampler);
rcti new_input;
new_input.xmin = input->xmin;
new_input.xmax = input->xmax + pixel_size_ - 1;
new_input.ymax = input->ymax;
new_input.ymax = input->ymax + pixel_size_ - 1;
return NodeOperation::determine_depending_area_of_interest(&new_input, read_operation, output);
}
void PixelateOperation::execute_pixel_sampled(float output[4],
const float x,
const float y,
const PixelSampler sampler)
{
const int width = this->get_width();
const int height = this->get_height();
const int x_start = (int(x) / pixel_size_) * pixel_size_;
const int y_start = (int(y) / pixel_size_) * pixel_size_;
const int x_end = std::min(x_start + pixel_size_, width);
const int y_end = std::min(y_start + pixel_size_, height);
float4 color_accum(0, 0, 0, 0);
for (int iy = y_start; iy < y_end; ++iy) {
for (int ix = x_start; ix < x_end; ++ix) {
float4 color;
input_operation_->read_sampled(color, ix, iy, sampler);
color_accum += color;
}
}
const int scale = (x_end - x_start) * (y_end - y_start);
copy_v4_v4(output, color_accum / float(scale));
}
void PixelateOperation::get_area_of_interest(const int /*input_idx*/,
const rcti &output_area,
rcti &r_input_area)
{
r_input_area.xmin = output_area.xmin;
r_input_area.ymin = output_area.ymin;
r_input_area.xmax = output_area.xmax + pixel_size_ - 1;
r_input_area.ymax = output_area.ymax + pixel_size_ - 1;
}
void PixelateOperation::update_memory_buffer_partial(MemoryBuffer *output,
const rcti &area,
Span<MemoryBuffer *> inputs)
{
MemoryBuffer *image = inputs[0];
if (image->is_a_single_elem()) {
copy_v4_v4(output->get_elem(0, 0), image->get_elem(0, 0));
return;
}
const int width = image->get_width();
const int height = image->get_height();
for (BuffersIterator<float> it = output->iterate_with(inputs, area); !it.is_end(); ++it) {
const int x_start = (it.x / pixel_size_) * pixel_size_;
const int y_start = (it.y / pixel_size_) * pixel_size_;
const int x_end = std::min(x_start + pixel_size_, width);
const int y_end = std::min(y_start + pixel_size_, height);
float4 color_accum(0, 0, 0, 0);
for (int y = y_start; y < y_end; ++y) {
for (int x = x_start; x < x_end; ++x) {
float4 color;
image->read_elem(x, y, color);
color_accum += color;
}
}
const int scale = (x_end - x_start) * (y_end - y_start);
copy_v4_v4(it.out, color_accum / float(scale));
}
}
} // namespace blender::compositor

View File

@ -4,7 +4,7 @@
#pragma once
#include "COM_NodeOperation.h"
#include "COM_MultiThreadedOperation.h"
namespace blender::compositor {
@ -15,19 +15,26 @@ namespace blender::compositor {
* For some setups you don want this.
* This operation will remove the sub-pixel accuracy
*/
class PixelateOperation : public NodeOperation {
class PixelateOperation : public MultiThreadedOperation {
private:
/**
* \brief cached reference to the input operation
*/
SocketReader *input_operation_;
int pixel_size_;
public:
/**
* \brief PixelateOperation
* \param data_type: the datatype to create this operator for (saves datatype conversions)
*/
PixelateOperation(DataType data_type);
PixelateOperation();
void set_pixel_size(const int pixel_size)
{
if (pixel_size < 1) {
pixel_size_ = 1;
return;
}
pixel_size_ = pixel_size;
}
/**
* \brief initialization of the execution
@ -39,6 +46,10 @@ class PixelateOperation : public NodeOperation {
*/
void deinit_execution() override;
bool determine_depending_area_of_interest(rcti *input,
ReadBufferOperation *read_operation,
rcti *output) override;
/**
* \brief execute_pixel
* \param output: result
@ -47,6 +58,11 @@ class PixelateOperation : public NodeOperation {
* \param sampler: sampler
*/
void execute_pixel_sampled(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

View File

@ -8305,6 +8305,21 @@ static void def_cmp_trackpos(StructRNA *srna)
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
}
static void def_cmp_pixelate(StructRNA *srna)
{
PropertyRNA *prop;
/* The range of the pixel size is chosen so that it is positive and above zero, and also does not
* exceed the underlying int16_t type. The size limit matches the maximum size used by blur

Specify a range to avoid negative input.

Specify a range to avoid negative input.
* nodes. */
prop = RNA_def_property(srna, "pixel_size", PROP_INT, PROP_NONE);
RNA_def_property_int_sdna(prop, nullptr, "custom1");
RNA_def_property_range(prop, 1, 2048);
RNA_def_property_int_default(prop, 1);
RNA_def_property_ui_text(prop, "Pixel Size", "Pixel size of the output image");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
}
static void def_cmp_translate(StructRNA *srna)
{
static const EnumPropertyItem translate_items[] = {

View File

@ -207,7 +207,7 @@ DefNode(CompositorNode, CMP_NODE_MASK, def_cmp_mask, "MASK",
DefNode(CompositorNode, CMP_NODE_KEYINGSCREEN, def_cmp_keyingscreen, "KEYINGSCREEN", KeyingScreen, "Keying Screen", "" )
DefNode(CompositorNode, CMP_NODE_KEYING, def_cmp_keying, "KEYING", Keying, "Keying", "" )
DefNode(CompositorNode, CMP_NODE_TRACKPOS, def_cmp_trackpos, "TRACKPOS", TrackPos, "Track Position", "" )
DefNode(CompositorNode, CMP_NODE_PIXELATE, 0, "PIXELATE", Pixelate, "Pixelate", "" )
DefNode(CompositorNode, CMP_NODE_PIXELATE, def_cmp_pixelate, "PIXELATE", Pixelate, "Pixelate", "" )
DefNode(CompositorNode, CMP_NODE_PLANETRACKDEFORM,def_cmp_planetrackdeform,"PLANETRACKDEFORM",PlaneTrackDeform,"Plane Track Deform","" )
DefNode(CompositorNode, CMP_NODE_CORNERPIN, 0, "CORNERPIN", CornerPin, "Corner Pin", "" )
DefNode(CompositorNode, CMP_NODE_SUNBEAMS, def_cmp_sunbeams, "SUNBEAMS", SunBeams, "Sun Beams", "" )

View File

@ -10,6 +10,9 @@
#include "COM_node_operation.hh"
#include "UI_interface.hh"
#include "UI_resources.hh"
#include "node_composite_util.hh"
/* **************** Pixelate ******************** */
@ -22,6 +25,16 @@ static void cmp_node_pixelate_declare(NodeDeclarationBuilder &b)
b.add_output<decl::Color>("Color");
}
static void node_composit_init_pixelate(bNodeTree * /*ntree*/, bNode *node)
{
node->custom1 = 1;
}
static void node_composit_buts_pixelate(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr)
{
uiItemR(layout, ptr, "pixel_size", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE);
}
using namespace blender::realtime_compositor;
class PixelateOperation : public NodeOperation {
@ -78,6 +91,8 @@ void register_node_type_cmp_pixelate()
cmp_node_type_base(&ntype, CMP_NODE_PIXELATE, "Pixelate", NODE_CLASS_OP_FILTER);
ntype.declare = file_ns::cmp_node_pixelate_declare;
ntype.draw_buttons = file_ns::node_composit_buts_pixelate;
ntype.initfunc = file_ns::node_composit_init_pixelate;
ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);