Compositor: Add Extension Mode for Scale node

This commit introduces Extension Mode for the Scale node.

Pull Request: https://projects.blender.org/blender/blender/pulls/141485
This commit is contained in:
Benjamin Beilharz
2025-07-08 11:07:28 +02:00
committed by Omar Emara
parent 4b4c9c8ed1
commit 00ed2030cb
5 changed files with 100 additions and 23 deletions

View File

@@ -27,7 +27,7 @@
/* Blender file format version. */
#define BLENDER_FILE_VERSION BLENDER_VERSION
#define BLENDER_FILE_SUBVERSION 33
#define BLENDER_FILE_SUBVERSION 34
/* Minimum Blender version that supports reading file written with the current
* version. Older Blender versions will test this and cancel loading the file, showing a warning to

View File

@@ -1282,6 +1282,27 @@ void blo_do_versions_500(FileData * /*fd*/, Library * /*lib*/, Main *bmain)
}
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 500, 34)) {
FOREACH_NODETREE_BEGIN (bmain, ntree, id) {
if (ntree->type != NTREE_COMPOSIT) {
continue;
}
LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
if (node->type_legacy != CMP_NODE_SCALE) {
continue;
}
if (node->storage == nullptr) {
continue;
}
NodeScaleData *data = static_cast<NodeScaleData *>(node->storage);
data->extension_x = CMP_NODE_EXTENSION_MODE_ZERO;
data->extension_y = CMP_NODE_EXTENSION_MODE_ZERO;
node->storage = data;
}
FOREACH_NODETREE_END;
}
}
/**
* Always bump subversion in BKE_blender_version.h when adding versioning
* code here, and wrap it inside a MAIN_VERSION_FILE_ATLEAST check.

View File

@@ -1581,6 +1581,8 @@ typedef struct NodeTranslateData {
typedef struct NodeScaleData {
short interpolation;
char extension_x;
char extension_y;
} NodeScaleData;
typedef struct NodeDisplaceData {

View File

@@ -6716,6 +6716,18 @@ static void def_cmp_scale(BlenderRNA * /*brna*/, StructRNA *srna)
RNA_def_property_enum_items(prop, cmp_interpolation_items);
RNA_def_property_ui_text(prop, "Interpolation", "Interpolation method");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
prop = RNA_def_property(srna, "extension_x", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, nullptr, "extension_x");
RNA_def_property_enum_items(prop, cmp_extension_mode_items);
RNA_def_property_ui_text(prop, "X Extension Mode", "The extension mode applied to the X axis");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
prop = RNA_def_property(srna, "extension_y", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, nullptr, "extension_y");
RNA_def_property_enum_items(prop, cmp_extension_mode_items);
RNA_def_property_ui_text(prop, "Y Extension Mode", "The extension mode applied to the Y axis");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
}
static void def_cmp_rotate(BlenderRNA * /*brna*/, StructRNA *srna)

View File

@@ -16,6 +16,8 @@
#include "BLI_math_vector_types.hh"
#include "BLI_string.h"
#include "DNA_node_types.h"
#include "RNA_access.hh"
#include "UI_interface_layout.hh"
@@ -24,6 +26,7 @@
#include "GPU_shader.hh"
#include "GPU_texture.hh"
#include "COM_domain.hh"
#include "COM_node_operation.hh"
#include "COM_utilities.hh"
@@ -59,6 +62,8 @@ static void node_composit_init_scale(bNodeTree * /*ntree*/, bNode *node)
{
NodeScaleData *data = MEM_callocN<NodeScaleData>(__func__);
data->interpolation = CMP_NODE_INTERPOLATION_BILINEAR;
data->extension_x = CMP_NODE_EXTENSION_MODE_ZERO;
data->extension_y = CMP_NODE_EXTENSION_MODE_ZERO;
node->storage = data;
}
@@ -76,16 +81,24 @@ static void node_composite_update_scale(bNodeTree *ntree, bNode *node)
static void node_composit_buts_scale(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr)
{
layout->prop(ptr, "interpolation", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE);
layout->prop(ptr, "space", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE);
uiLayout &column = layout->column(true);
column.prop(ptr, "space", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE);
if (RNA_enum_get(ptr, "space") == CMP_NODE_SCALE_RENDER_SIZE) {
layout->prop(ptr,
"frame_method",
UI_ITEM_R_SPLIT_EMPTY_NAME | UI_ITEM_R_EXPAND,
std::nullopt,
ICON_NONE);
column.prop(ptr,
"frame_method",
UI_ITEM_R_SPLIT_EMPTY_NAME | UI_ITEM_R_EXPAND,
std::nullopt,
ICON_NONE);
}
uiLayout &column_interpolation_extension_modes = layout->column(true);
column_interpolation_extension_modes.prop(
ptr, "interpolation", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE);
uiLayout &row = column_interpolation_extension_modes.row(true);
row.prop(ptr, "extension_x", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE);
row.prop(ptr, "extension_y", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE);
}
using namespace blender::compositor;
@@ -115,6 +128,8 @@ class ScaleOperation : public NodeOperation {
output.transform(transformation);
output.get_realization_options().interpolation = this->get_interpolation();
output.get_realization_options().extension_x = this->get_extension_mode_x();
output.get_realization_options().extension_y = this->get_extension_mode_y();
}
void execute_variable_size()
@@ -137,10 +152,14 @@ class ScaleOperation : public NodeOperation {
* cases, as the logic used by the bicubic realization shader expects textures to use bilinear
* interpolation. */
const Interpolation interpolation = this->get_interpolation();
const ExtensionMode extension_mode_x = this->get_extension_mode_x();
const ExtensionMode extension_mode_y = this->get_extension_mode_y();
/* For now the EWA sampling falls back to bicubic interpolation. */
const bool use_bilinear = ELEM(interpolation, Interpolation::Bilinear, Interpolation::Bicubic);
GPU_texture_filter_mode(input, use_bilinear);
GPU_texture_extend_mode(input, GPU_SAMPLER_EXTEND_MODE_CLAMP_TO_BORDER);
GPU_texture_extend_mode_x(input, map_extension_mode_to_extend_mode(extension_mode_x));
GPU_texture_extend_mode_y(input, map_extension_mode_to_extend_mode(extension_mode_y));
input.bind_as_texture(shader, "input_tx");
Result &x_scale = get_input("X");
@@ -171,6 +190,8 @@ class ScaleOperation : public NodeOperation {
Result &output = this->get_result("Image");
const Interpolation interpolation = this->get_interpolation();
const ExtensionMode extension_mode_x = this->get_extension_mode_x();
const ExtensionMode extension_mode_y = this->get_extension_mode_y();
const Domain domain = compute_domain();
const int2 size = domain.size;
output.allocate_texture(domain);
@@ -183,19 +204,10 @@ class ScaleOperation : public NodeOperation {
y_scale.load_pixel<float, true>(texel));
float2 scaled_coordinates = center +
(coordinates - center) / math::max(scale, float2(0.0001f));
switch (interpolation) {
/* For now the EWA sampling falls back to bicubic interpolation. */
case Interpolation::Anisotropic:
case Interpolation::Bicubic:
output.store_pixel(texel, input.sample_cubic_wrap(scaled_coordinates, false, false));
break;
case Interpolation::Bilinear:
output.store_pixel(texel, input.sample_bilinear_wrap(scaled_coordinates, false, false));
break;
case Interpolation::Nearest:
output.store_pixel(texel, input.sample_nearest_wrap(scaled_coordinates, false, false));
break;
}
output.store_pixel(
texel,
input.sample(scaled_coordinates, interpolation, extension_mode_x, extension_mode_y));
});
}
@@ -207,9 +219,39 @@ class ScaleOperation : public NodeOperation {
return "compositor_scale_variable";
}
ExtensionMode get_extension_mode_x()
{
switch (static_cast<CMPExtensionMode>(node_storage(bnode()).extension_x)) {
case CMP_NODE_EXTENSION_MODE_ZERO:
return ExtensionMode::Zero;
case CMP_NODE_EXTENSION_MODE_REPEAT:
return ExtensionMode::Repeat;
case CMP_NODE_EXTENSION_MODE_EXTEND:
return ExtensionMode::Extend;
}
BLI_assert_unreachable();
return ExtensionMode::Zero;
}
ExtensionMode get_extension_mode_y()
{
switch (static_cast<CMPExtensionMode>(node_storage(bnode()).extension_y)) {
case CMP_NODE_EXTENSION_MODE_ZERO:
return ExtensionMode::Zero;
case CMP_NODE_EXTENSION_MODE_REPEAT:
return ExtensionMode::Repeat;
case CMP_NODE_EXTENSION_MODE_EXTEND:
return ExtensionMode::Extend;
}
BLI_assert_unreachable();
return ExtensionMode::Zero;
}
Interpolation get_interpolation() const
{
switch (node_storage(bnode()).interpolation) {
switch (static_cast<CMPNodeInterpolation>(node_storage(bnode()).interpolation)) {
case CMP_NODE_INTERPOLATION_NEAREST:
return Interpolation::Nearest;
case CMP_NODE_INTERPOLATION_BILINEAR: