Realtime Compositor: Implement scale node

This patch implements the Scale node for the realtime compositor.

Differential Revision: https://developer.blender.org/D15758

Reviewed By: Clement Foucault
This commit is contained in:
Omar Emara 2022-09-09 14:10:38 +02:00
parent e254d8867d
commit 0fd39da3a9
5 changed files with 172 additions and 28 deletions

View File

@ -1337,15 +1337,6 @@ void BKE_nodetree_remove_layer_n(struct bNodeTree *ntree, struct Scene *scene, i
#define CMP_CHAN_RGB 1
#define CMP_CHAN_A 2
/* scale node type, in custom1 */
#define CMP_SCALE_RELATIVE 0
#define CMP_SCALE_ABSOLUTE 1
#define CMP_SCALE_SCENEPERCENT 2
#define CMP_SCALE_RENDERPERCENT 3
/* custom2 */
#define CMP_SCALE_RENDERSIZE_FRAME_ASPECT (1 << 0)
#define CMP_SCALE_RENDERSIZE_FRAME_CROP (1 << 1)
/* track position node, in custom1 */
#define CMP_TRACKPOS_ABSOLUTE 0
#define CMP_TRACKPOS_RELATIVE_START 1

View File

@ -25,7 +25,7 @@ void ScaleNode::convert_to_operations(NodeConverter &converter,
NodeOutput *output_socket = this->get_output_socket(0);
switch (bnode->custom1) {
case CMP_SCALE_RELATIVE: {
case CMP_NODE_SCALE_RELATIVE: {
ScaleRelativeOperation *operation = new ScaleRelativeOperation();
converter.add_operation(operation);
@ -39,7 +39,7 @@ void ScaleNode::convert_to_operations(NodeConverter &converter,
break;
}
case CMP_SCALE_SCENEPERCENT: {
case CMP_NODE_SCALE_RENDER_PERCENT: {
SetValueOperation *scale_factor_operation = new SetValueOperation();
scale_factor_operation->set_value(context.get_render_percentage_as_factor());
converter.add_operation(scale_factor_operation);
@ -59,13 +59,14 @@ void ScaleNode::convert_to_operations(NodeConverter &converter,
break;
}
case CMP_SCALE_RENDERPERCENT: {
case CMP_NODE_SCALE_RENDER_SIZE: {
const RenderData *rd = context.get_render_data();
const float render_size_factor = context.get_render_percentage_as_factor();
ScaleFixedSizeOperation *operation = new ScaleFixedSizeOperation();
/* framing options */
operation->set_is_aspect((bnode->custom2 & CMP_SCALE_RENDERSIZE_FRAME_ASPECT) != 0);
operation->set_is_crop((bnode->custom2 & CMP_SCALE_RENDERSIZE_FRAME_CROP) != 0);
operation->set_is_aspect(
ELEM(bnode->custom2, CMP_NODE_SCALE_RENDER_SIZE_FIT, CMP_NODE_SCALE_RENDER_SIZE_CROP));
operation->set_is_crop(bnode->custom2 == CMP_NODE_SCALE_RENDER_SIZE_CROP);
operation->set_offset(bnode->custom3, bnode->custom4);
operation->set_new_width(rd->xsch * render_size_factor);
operation->set_new_height(rd->ysch * render_size_factor);
@ -79,7 +80,7 @@ void ScaleNode::convert_to_operations(NodeConverter &converter,
break;
}
case CMP_SCALE_ABSOLUTE: {
case CMP_NODE_SCALE_ABSOLUTE: {
/* TODO: what is the use of this one.... perhaps some issues when the ui was updated... */
ScaleAbsoluteOperation *operation = new ScaleAbsoluteOperation();
converter.add_operation(operation);

View File

@ -2029,6 +2029,21 @@ typedef enum CMPNodeFlipMode {
CMP_NODE_FLIP_X_Y = 2,
} CMPNodeFlipMode;
/* Scale Node. Stored in custom1. */
typedef enum CMPNodeScaleMethod {
CMP_NODE_SCALE_RELATIVE = 0,
CMP_NODE_SCALE_ABSOLUTE = 1,
CMP_NODE_SCALE_RENDER_PERCENT = 2,
CMP_NODE_SCALE_RENDER_SIZE = 3,
} CMPNodeScaleMethod;
/* Scale Node. Stored in custom2. */
typedef enum CMPNodeScaleRenderSizeMethod {
CMP_NODE_SCALE_RENDER_SIZE_STRETCH = 0,
CMP_NODE_SCALE_RENDER_SIZE_FIT = 1,
CMP_NODE_SCALE_RENDER_SIZE_CROP = 2,
} CMPNodeScaleRenderSizeMethod;
/* Filter Node. Stored in custom1. */
typedef enum CMPNodeFilterMethod {
CMP_NODE_FILTER_SOFT = 0,

View File

@ -7190,18 +7190,18 @@ static void def_cmp_scale(StructRNA *srna)
PropertyRNA *prop;
static const EnumPropertyItem space_items[] = {
{CMP_SCALE_RELATIVE, "RELATIVE", 0, "Relative", ""},
{CMP_SCALE_ABSOLUTE, "ABSOLUTE", 0, "Absolute", ""},
{CMP_SCALE_SCENEPERCENT, "SCENE_SIZE", 0, "Scene Size", ""},
{CMP_SCALE_RENDERPERCENT, "RENDER_SIZE", 0, "Render Size", ""},
{CMP_NODE_SCALE_RELATIVE, "RELATIVE", 0, "Relative", ""},
{CMP_NODE_SCALE_ABSOLUTE, "ABSOLUTE", 0, "Absolute", ""},
{CMP_NODE_SCALE_RENDER_PERCENT, "SCENE_SIZE", 0, "Scene Size", ""},
{CMP_NODE_SCALE_RENDER_SIZE, "RENDER_SIZE", 0, "Render Size", ""},
{0, NULL, 0, NULL, NULL},
};
/* matching bgpic_camera_frame_items[] */
static const EnumPropertyItem space_frame_items[] = {
{0, "STRETCH", 0, "Stretch", ""},
{CMP_SCALE_RENDERSIZE_FRAME_ASPECT, "FIT", 0, "Fit", ""},
{CMP_SCALE_RENDERSIZE_FRAME_ASPECT | CMP_SCALE_RENDERSIZE_FRAME_CROP, "CROP", 0, "Crop", ""},
{CMP_NODE_SCALE_RENDER_SIZE_STRETCH, "STRETCH", 0, "Stretch", ""},
{CMP_NODE_SCALE_RENDER_SIZE_FIT, "FIT", 0, "Fit", ""},
{CMP_NODE_SCALE_RENDER_SIZE_CROP, "CROP", 0, "Crop", ""},
{0, NULL, 0, NULL, NULL},
};

View File

@ -5,6 +5,11 @@
* \ingroup cmpnodes
*/
#include "BLI_assert.h"
#include "BLI_float3x3.hh"
#include "BLI_math_base.hh"
#include "BLI_math_vec_types.hh"
#include "RNA_access.h"
#include "UI_interface.h"
@ -20,16 +25,26 @@ namespace blender::nodes::node_composite_scale_cc {
static void cmp_node_scale_declare(NodeDeclarationBuilder &b)
{
b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
b.add_input<decl::Float>(N_("X")).default_value(1.0f).min(0.0001f).max(CMP_SCALE_MAX);
b.add_input<decl::Float>(N_("Y")).default_value(1.0f).min(0.0001f).max(CMP_SCALE_MAX);
b.add_input<decl::Color>(N_("Image"))
.default_value({1.0f, 1.0f, 1.0f, 1.0f})
.compositor_domain_priority(0);
b.add_input<decl::Float>(N_("X"))
.default_value(1.0f)
.min(0.0001f)
.max(CMP_SCALE_MAX)
.compositor_expects_single_value();
b.add_input<decl::Float>(N_("Y"))
.default_value(1.0f)
.min(0.0001f)
.max(CMP_SCALE_MAX)
.compositor_expects_single_value();
b.add_output<decl::Color>(N_("Image"));
}
static void node_composite_update_scale(bNodeTree *ntree, bNode *node)
{
bNodeSocket *sock;
bool use_xy_scale = ELEM(node->custom1, CMP_SCALE_RELATIVE, CMP_SCALE_ABSOLUTE);
bool use_xy_scale = ELEM(node->custom1, CMP_NODE_SCALE_RELATIVE, CMP_NODE_SCALE_ABSOLUTE);
/* Only show X/Y scale factor inputs for modes using them! */
for (sock = (bNodeSocket *)node->inputs.first; sock; sock = sock->next) {
@ -43,7 +58,7 @@ static void node_composit_buts_scale(uiLayout *layout, bContext *UNUSED(C), Poin
{
uiItemR(layout, ptr, "space", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE);
if (RNA_enum_get(ptr, "space") == CMP_SCALE_RENDERPERCENT) {
if (RNA_enum_get(ptr, "space") == CMP_NODE_SCALE_RENDER_SIZE) {
uiLayout *row;
uiItemR(layout,
ptr,
@ -65,7 +80,129 @@ class ScaleOperation : public NodeOperation {
void execute() override
{
get_input("Image").pass_through(get_result("Image"));
Result &input = get_input("Image");
Result &result = get_result("Image");
input.pass_through(result);
const float3x3 transformation = float3x3::from_translation_rotation_scale(
get_translation(), 0.0f, get_scale());
result.transform(transformation);
result.get_realization_options().interpolation = Interpolation::Bilinear;
}
float2 get_scale()
{
switch (get_scale_method()) {
case CMP_NODE_SCALE_RELATIVE:
return get_scale_relative();
case CMP_NODE_SCALE_ABSOLUTE:
return get_scale_absolute();
case CMP_NODE_SCALE_RENDER_PERCENT:
return get_scale_render_percent();
case CMP_NODE_SCALE_RENDER_SIZE:
return get_scale_render_size();
default:
BLI_assert_unreachable();
return float2(1.0f);
}
}
/* Scale by the input factors. */
float2 get_scale_relative()
{
return float2(get_input("X").get_float_value_default(1.0f),
get_input("Y").get_float_value_default(1.0f));
}
/* Scale such that the new size matches the input absolute size. */
float2 get_scale_absolute()
{
const float2 input_size = float2(get_input("Image").domain().size);
const float2 absolute_size = float2(get_input("X").get_float_value_default(1.0f),
get_input("Y").get_float_value_default(1.0f));
return absolute_size / input_size;
}
/* Scale by the render resolution percentage. */
float2 get_scale_render_percent()
{
return float2(context().get_scene()->r.size / 100.0f);
}
float2 get_scale_render_size()
{
switch (get_scale_render_size_method()) {
case CMP_NODE_SCALE_RENDER_SIZE_STRETCH:
return get_scale_render_size_stretch();
case CMP_NODE_SCALE_RENDER_SIZE_FIT:
return get_scale_render_size_fit();
case CMP_NODE_SCALE_RENDER_SIZE_CROP:
return get_scale_render_size_crop();
default:
BLI_assert_unreachable();
return float2(1.0f);
}
}
/* Scale such that the new size matches the render size. Since the input is freely scaled, it is
* potentially stretched, hence the name. */
float2 get_scale_render_size_stretch()
{
const float2 input_size = float2(get_input("Image").domain().size);
const float2 render_size = float2(context().get_output_size());
return render_size / input_size;
}
/* Scale such that the dimension with the smaller scaling factor matches that of the render size
* while maintaining the input's aspect ratio. Since the other dimension is guaranteed not to
* exceed the render size region due to its larger scaling factor, the image is said to be fit
* inside that region, hence the name. */
float2 get_scale_render_size_fit()
{
const float2 input_size = float2(get_input("Image").domain().size);
const float2 render_size = float2(context().get_output_size());
const float2 scale = render_size / input_size;
return float2(math::min(scale.x, scale.y));
}
/* Scale such that the dimension with the larger scaling factor matches that of the render size
* while maintaining the input's aspect ratio. Since the other dimension is guaranteed to exceed
* the render size region due to its lower scaling factor, the image will be cropped inside that
* region, hence the name. */
float2 get_scale_render_size_crop()
{
const float2 input_size = float2(get_input("Image").domain().size);
const float2 render_size = float2(context().get_output_size());
const float2 scale = render_size / input_size;
return float2(math::max(scale.x, scale.y));
}
float2 get_translation()
{
/* Only the render size option supports offset translation. */
if (get_scale_method() != CMP_NODE_SCALE_RENDER_SIZE) {
return float2(0.0f);
}
/* Translate by the offset factor relative to the new size. */
const float2 input_size = float2(get_input("Image").domain().size);
return get_offset() * input_size * get_scale();
}
CMPNodeScaleMethod get_scale_method()
{
return (CMPNodeScaleMethod)bnode().custom1;
}
CMPNodeScaleRenderSizeMethod get_scale_render_size_method()
{
return (CMPNodeScaleRenderSizeMethod)bnode().custom2;
}
float2 get_offset()
{
return float2(bnode().custom3, bnode().custom4);
}
};