diff options
author | Omar Emara <mail@OmarEmara.dev> | 2022-09-09 15:10:38 +0300 |
---|---|---|
committer | Omar Emara <mail@OmarEmara.dev> | 2022-09-09 15:11:43 +0300 |
commit | 0fd39da3a96adfe30e5260f72ecc8c6d8b68b98f (patch) | |
tree | b52c1c68ccaf998ed3c52a50cf5407003ecb1c02 /source/blender/nodes/composite | |
parent | e254d8867d666846fa8455ac8415763127eb48b6 (diff) |
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
Diffstat (limited to 'source/blender/nodes/composite')
-rw-r--r-- | source/blender/nodes/composite/nodes/node_composite_scale.cc | 149 |
1 files changed, 143 insertions, 6 deletions
diff --git a/source/blender/nodes/composite/nodes/node_composite_scale.cc b/source/blender/nodes/composite/nodes/node_composite_scale.cc index 8b43ae8c9ca..eb2d7162c69 100644 --- a/source/blender/nodes/composite/nodes/node_composite_scale.cc +++ b/source/blender/nodes/composite/nodes/node_composite_scale.cc @@ -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); } }; |