Compositor: implement interpolation methods for Translate node #119603

Merged
Habib Gahbiche merged 10 commits from zazizizou/blender:com-translate-interp into main 2024-05-01 14:44:13 +02:00
9 changed files with 88 additions and 24 deletions

View File

@ -407,7 +407,10 @@ class MemoryBuffer {
*/
MemoryBuffer *inflate() const;
inline void wrap_pixel(int &x, int &y, MemoryBufferExtend extend_x, MemoryBufferExtend extend_y)
inline void wrap_pixel(int &x,
int &y,
MemoryBufferExtend extend_x,
MemoryBufferExtend extend_y) const
{
const int w = get_width();
const int h = get_height();
@ -503,10 +506,11 @@ class MemoryBuffer {
}
inline void read(float *result,
int x,
int y,
float x,
float y,
PixelSampler sampler = PixelSampler::Nearest,
MemoryBufferExtend extend_x = MemoryBufferExtend::Clip,
MemoryBufferExtend extend_y = MemoryBufferExtend::Clip)
MemoryBufferExtend extend_y = MemoryBufferExtend::Clip) const
{
bool clip_x = (extend_x == MemoryBufferExtend::Clip && (x < rect_.xmin || x >= rect_.xmax));
bool clip_y = (extend_y == MemoryBufferExtend::Clip && (y < rect_.ymin || y >= rect_.ymax));
@ -515,12 +519,10 @@ class MemoryBuffer {
memset(result, 0, num_channels_ * sizeof(float));
}
else {
int u = x;
int v = y;
float u = x;
float v = y;
this->wrap_pixel(u, v, extend_x, extend_y);
const int offset = get_coords_offset(u, v);
float *buffer = &buffer_[offset];
memcpy(result, buffer, sizeof(float) * num_channels_);
this->read_elem_sampled(u, v, sampler, result);
}
}
void write_pixel(int x, int y, const float color[4]);

View File

@ -61,6 +61,7 @@ void Stabilize2dNode::convert_to_operations(NodeConverter &converter,
rotate_operation->set_do_degree2_rad_conversion(false);
rotate_operation->set_sampler(sampler);
TranslateOperation *translate_operation = new TranslateCanvasOperation();
translate_operation->set_sampler(sampler);
converter.add_operation(scale_operation);
converter.add_operation(translate_operation);

View File

@ -28,6 +28,18 @@ void TranslateNode::convert_to_operations(NodeConverter &converter,
operation->set_wrapping(data->wrap_axis);
operation->set_is_relative(data->relative);
switch (data->interpolation) {
case CMP_NODE_INTERPOLATION_NEAREST:
operation->set_sampler(PixelSampler::Nearest);
break;
case CMP_NODE_INTERPOLATION_BILINEAR:
operation->set_sampler(PixelSampler::Bilinear);
break;
case CMP_NODE_INTERPOLATION_BICUBIC:
operation->set_sampler(PixelSampler::Bicubic);
break;
}
converter.add_operation(operation);
converter.map_input_socket(input_xsocket, operation->get_input_socket(1));
converter.map_input_socket(input_ysocket, operation->get_input_socket(2));

View File

@ -18,6 +18,7 @@ TranslateOperation::TranslateOperation(DataType data_type, ResizeMode resize_mod
is_relative_ = false;
this->x_extend_mode_ = MemoryBufferExtend::Clip;
this->y_extend_mode_ = MemoryBufferExtend::Clip;
this->sampler_ = PixelSampler::Nearest;
this->flags_.can_be_constant = true;
}
@ -96,14 +97,20 @@ void TranslateOperation::update_memory_buffer_partial(MemoryBuffer *output,
return;
}
const int delta_x = this->get_delta_x();
const int delta_y = this->get_delta_y();
float delta_x = this->get_delta_x();
float delta_y = this->get_delta_y();
if (sampler_ == PixelSampler::Nearest) {
/* Use same rounding convention for GPU compositor. */
delta_x = round(delta_x);
delta_y = round(delta_y);
}
for (int y = area.ymin; y < area.ymax; y++) {
float *out = output->get_elem(area.xmin, y);
for (int x = area.xmin; x < area.xmax; x++) {
const int input_x = x - delta_x;
const int input_y = y - delta_y;
input->read(out, input_x, input_y, x_extend_mode_, y_extend_mode_);
const float input_x = x - delta_x;
const float input_y = y - delta_y;
input->read(out, input_x, input_y, sampler_, x_extend_mode_, y_extend_mode_);
out += output->elem_stride;
}
}

View File

@ -22,6 +22,7 @@ class TranslateOperation : public MultiThreadedOperation {
float delta_y_;
bool is_delta_set_;
bool is_relative_;
PixelSampler sampler_;
std::mutex mutex_;
@ -51,6 +52,15 @@ class TranslateOperation : public MultiThreadedOperation {
return is_relative_;
}
PixelSampler get_sampler()
{
return sampler_;
}
void set_sampler(PixelSampler sampler)
{
sampler_ = sampler;
}
inline void ensure_delta()
{
if (!is_delta_set_) {

View File

@ -1420,6 +1420,7 @@ typedef struct NodeTrackPosData {
typedef struct NodeTranslateData {
char wrap_axis;
char relative;
short interpolation;
} NodeTranslateData;
typedef struct NodePlaneTrackDeformData {
@ -2565,12 +2566,13 @@ typedef enum CMPNodeKuwahara {
CMP_NODE_KUWAHARA_ANISOTROPIC = 1,
} CMPNodeKuwahara;
/* Stabilize 2D node. Stored in custom1. */
typedef enum CMPNodeStabilizeInterpolation {
CMP_NODE_STABILIZE_INTERPOLATION_NEAREST = 0,
CMP_NODE_STABILIZE_INTERPOLATION_BILINEAR = 1,
CMP_NODE_STABILIZE_INTERPOLATION_BICUBIC = 2,
} CMPNodeStabilizeInterpolation;
/* Stabilize 2D node. Stored in custom1 for Stabilize 2D node and in interpolation for Translate
* node. */
typedef enum CMPNodeInterpolation {
CMP_NODE_INTERPOLATION_NEAREST = 0,
CMP_NODE_INTERPOLATION_BILINEAR = 1,
CMP_NODE_INTERPOLATION_BICUBIC = 2,
} CMPNodeInterpolation;
/* Stabilize 2D node. Stored in custom2. */
typedef enum CMPNodeStabilizeInverse {

View File

@ -8478,10 +8478,23 @@ static void def_cmp_translate(StructRNA *srna)
{0, nullptr, 0, nullptr, nullptr},
};
static const EnumPropertyItem interpolation_items[] = {
{CMP_NODE_INTERPOLATION_NEAREST, "Nearest", 0, "Nearest", "Use nearest interpolation"},
{CMP_NODE_INTERPOLATION_BILINEAR, "Bilinear", 0, "Bilinear", "Use bilinear interpolation"},
{CMP_NODE_INTERPOLATION_BICUBIC, "Bicubic", 0, "Bicubic", "Use bicubic interpolation"},
{0, nullptr, 0, nullptr, nullptr},
};
PropertyRNA *prop;
RNA_def_struct_sdna_from(srna, "NodeTranslateData", "storage");
prop = RNA_def_property(srna, "interpolation", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, NULL, "interpolation");
RNA_def_property_enum_items(prop, interpolation_items);
RNA_def_property_ui_text(prop, "", "");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
prop = RNA_def_property(srna, "use_relative", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, nullptr, "relative", 1);
RNA_def_property_ui_text(

View File

@ -116,12 +116,12 @@ class Stabilize2DOperation : public NodeOperation {
Interpolation get_interpolation()
{
switch (static_cast<CMPNodeStabilizeInterpolation>(bnode().custom1)) {
case CMP_NODE_STABILIZE_INTERPOLATION_NEAREST:
switch (static_cast<CMPNodeInterpolation>(bnode().custom1)) {
case CMP_NODE_INTERPOLATION_NEAREST:
return Interpolation::Nearest;
case CMP_NODE_STABILIZE_INTERPOLATION_BILINEAR:
case CMP_NODE_INTERPOLATION_BILINEAR:
return Interpolation::Bilinear;
case CMP_NODE_STABILIZE_INTERPOLATION_BICUBIC:
case CMP_NODE_INTERPOLATION_BICUBIC:
return Interpolation::Bicubic;
}

View File

@ -48,6 +48,7 @@ static void node_composit_init_translate(bNodeTree * /*ntree*/, bNode *node)
static void node_composit_buts_translate(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr)
{
uiItemR(layout, ptr, "interpolation", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE);
uiItemR(layout, ptr, "use_relative", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE);
uiItemR(layout, ptr, "wrap_axis", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE);
}
@ -77,10 +78,26 @@ class TranslateOperation : public NodeOperation {
RealizationOptions realization_options = input.get_realization_options();
realization_options.wrap_x = get_wrap_x();
realization_options.wrap_y = get_wrap_y();
realization_options.interpolation = get_interpolation();
transform(context(), input, result, transformation, realization_options);
}
Interpolation get_interpolation()
{
switch (node_storage(bnode()).interpolation) {
case CMP_NODE_INTERPOLATION_NEAREST:
return Interpolation::Nearest;
case CMP_NODE_INTERPOLATION_BILINEAR:
return Interpolation::Bilinear;
case CMP_NODE_INTERPOLATION_BICUBIC:
return Interpolation::Bicubic;
}
BLI_assert_unreachable();
return Interpolation::Nearest;
}
bool get_use_relative()
{
return node_storage(bnode()).relative;