Realtime Compositor: Immediately realize transformations #112332

Merged
Omar Emara merged 6 commits from OmarEmaraDev/blender:realize-transformation into main 2023-10-12 11:04:59 +02:00
16 changed files with 307 additions and 243 deletions

View File

@ -67,17 +67,21 @@ set(SRC
algorithms/intern/morphological_distance.cc
algorithms/intern/morphological_distance_feather.cc
algorithms/intern/parallel_reduction.cc
algorithms/intern/realize_on_domain.cc
algorithms/intern/smaa.cc
algorithms/intern/summed_area_table.cc
algorithms/intern/symmetric_separable_blur.cc
algorithms/intern/transform.cc
algorithms/COM_algorithm_jump_flooding.hh
algorithms/COM_algorithm_morphological_distance.hh
algorithms/COM_algorithm_morphological_distance_feather.hh
algorithms/COM_algorithm_parallel_reduction.hh
algorithms/COM_algorithm_realize_on_domain.hh
algorithms/COM_algorithm_smaa.hh
algorithms/COM_algorithm_summed_area_table.hh
algorithms/COM_algorithm_symmetric_separable_blur.hh
algorithms/COM_algorithm_transform.hh
cached_resources/intern/cached_mask.cc
cached_resources/intern/cached_texture.cc

View File

@ -16,14 +16,6 @@ namespace blender::realtime_compositor {
struct InputRealizationOptions {
/* The input should be realized on the operation domain of the operation. */
bool realize_on_operation_domain : 1;
/* The input should be realized on a domain that is identical to the domain of the input but with
* an identity rotation and an increased size that completely fits the image after rotation. This
* is useful for operations that are not rotation invariant. */
bool realize_rotation : 1;
/* The input should be realized on a domain that is identical to the domain of the input but with
* an identity scale and an increased/decreased size that completely fits the image after
* scaling. This is useful for operations that are not scale invariant. */
bool realize_scale : 1;
};
/* ------------------------------------------------------------------------------------------------

View File

@ -42,40 +42,6 @@ class RealizeOnDomainOperation : public SimpleOperation {
protected:
/* The operation domain is just the target domain. */
Domain compute_domain() override;
private:
/* Get the realization shader of the appropriate type. */
GPUShader *get_realization_shader();
};
/* ------------------------------------------------------------------------------------------------
* Realize Transformation Operation
*
* A simple operation that realizes its input on a domain such that its transformations become
* identity and its size is increased/decreased to adapt to the new domain. The transformations
* that become identity are the ones marked to be realized in the given InputRealizationOptions.
* For instance, if InputRealizationOptions.realize_rotation is true and the input is rotated, the
* output of the operation will be a result of zero rotation but expanded size to account for the
* bounding box of the input after rotation. This is useful for operations that are not rotation
* invariant and thus require an input of zero rotation for correct operation.
*
* Notice that this class is not an actual operation, but constructs a RealizeOnDomainOperation
* with the appreciate domain in its construct_if_needed static constructor. */
class RealizeTransformationOperation {
public:
/* Determine if a realize transformation operation is needed for the input with the given result
* and descriptor. If it is not needed, return a null pointer. If it is needed, return an
* instance of RealizeOnDomainOperation with the appropriate domain. */
static SimpleOperation *construct_if_needed(Context &context,
const Result &input_result,
const InputDescriptor &input_descriptor);
private:
/* Given the domain of an input and its realization options, compute a domain such that the
* appropriate transformations specified in the realization options become identity and the size
* of the domain is increased/reduced to adapt to the new domain. */
static Domain compute_target_domain(const Domain &input_domain,
const InputRealizationOptions &realization_options);
};
} // namespace blender::realtime_compositor

View File

@ -0,0 +1,28 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
#include "BLI_math_matrix_types.hh"
#include "COM_context.hh"
#include "COM_domain.hh"
#include "COM_result.hh"
namespace blender::realtime_compositor {
/* Projects the input on a target domain, copies the area of the input that intersects the target
* domain, and fill the rest with zeros or repetitions of the input depending on the realization
* options. The transformation and realization options of the input are ignored and the given
* input_transformation and realization_options are used instead to allow the caller to change them
* without mutating the input result directly. See the discussion in COM_domain.hh for more
* information on what realization on domain means. */
void realize_on_domain(Context &context,
Result &input,
Result &output,
const Domain &domain,
const float3x3 &input_transformation,
const RealizationOptions &realization_options);
} // namespace blender::realtime_compositor

View File

@ -0,0 +1,33 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
#include "BLI_math_matrix_types.hh"
#include "COM_context.hh"
#include "COM_domain.hh"
#include "COM_result.hh"
namespace blender::realtime_compositor {
/* Transforms the given result based on the given transformation and interpolation, writing the
* transformed result to the given output.
*
* The rotation and scale components of the transformation are realized and the size of the result
* is increased/reduced to adapt to the new transformation. For instance, if the transformation is
* a rotation, the input will be rotated and expanded in size to account for the bounding box of
* the input after rotation. The size of the returned result is bound and clipped by the maximum
* possible GPU texture size to avoid allocations that surpass hardware limits, which is typically
* 16k.
*
* The translation component of the transformation is delayed and only stored in the domain of the
* result to be realized later when needed. */
void transform(Context &context,
Result &input,
Result &output,
float3x3 transformation,
Interpolation interpolation);
} // namespace blender::realtime_compositor

View File

@ -0,0 +1,113 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "BLI_math_angle_types.hh"
#include "BLI_math_matrix.hh"
#include "BLI_math_matrix_types.hh"
#include "BLI_math_vector_types.hh"
#include "BLI_utildefines.h"
#include "GPU_capabilities.h"
#include "GPU_shader.h"
#include "GPU_texture.h"
#include "COM_context.hh"
#include "COM_domain.hh"
#include "COM_result.hh"
#include "COM_utilities.hh"
#include "COM_algorithm_realize_on_domain.hh"
namespace blender::realtime_compositor {
static const char *get_realization_shader(Result &input,
const RealizationOptions &realization_options)
{
if (realization_options.interpolation == Interpolation::Bicubic) {
switch (input.type()) {
case ResultType::Color:
return "compositor_realize_on_domain_bicubic_color";
case ResultType::Vector:
return "compositor_realize_on_domain_bicubic_vector";
case ResultType::Float:
return "compositor_realize_on_domain_bicubic_float";
case ResultType::Int2:
/* Realization does not support integer images. */
break;
}
}
else {
switch (input.type()) {
case ResultType::Color:
return "compositor_realize_on_domain_color";
case ResultType::Vector:
return "compositor_realize_on_domain_vector";
case ResultType::Float:
return "compositor_realize_on_domain_float";
case ResultType::Int2:
/* Realization does not support integer images. */
break;
}
}
BLI_assert_unreachable();
return nullptr;
}
void realize_on_domain(Context &context,
Result &input,
Result &output,
const Domain &domain,
const float3x3 &input_transformation,
const RealizationOptions &realization_options)
{
GPUShader *shader = context.shader_manager().get(
get_realization_shader(input, realization_options));
GPU_shader_bind(shader);
/* Transform the input space into the domain space. */
const float3x3 local_transformation = math::invert(domain.transformation) * input_transformation;
/* Set the origin of the transformation to be the center of the domain. */
const float3x3 transformation = math::from_origin_transform<float3x3>(
local_transformation, float2(domain.size) / 2.0f);
/* Invert the transformation because the shader transforms the domain coordinates instead of the
* input image itself and thus expect the inverse. */
const float3x3 inverse_transformation = math::invert(transformation);
GPU_shader_uniform_mat3_as_mat4(shader, "inverse_transformation", inverse_transformation.ptr());
/* The texture sampler should use bilinear interpolation for both the bilinear and bicubic
* cases, as the logic used by the bicubic realization shader expects textures to use bilinear
* interpolation. */
const bool use_bilinear = ELEM(
realization_options.interpolation, Interpolation::Bilinear, Interpolation::Bicubic);
GPU_texture_filter_mode(input.texture(), use_bilinear);
/* If the input repeats, set a repeating wrap mode for out-of-bound texture access. Otherwise,
* make out-of-bound texture access return zero by setting a clamp to border extend mode. */
GPU_texture_extend_mode_x(input.texture(),
realization_options.repeat_x ?
GPU_SAMPLER_EXTEND_MODE_REPEAT :
GPU_SAMPLER_EXTEND_MODE_CLAMP_TO_BORDER);
GPU_texture_extend_mode_y(input.texture(),
realization_options.repeat_y ?
GPU_SAMPLER_EXTEND_MODE_REPEAT :
GPU_SAMPLER_EXTEND_MODE_CLAMP_TO_BORDER);
input.bind_as_texture(shader, "input_tx");
output.allocate_texture(domain);
output.bind_as_image(shader, "domain_img");
compute_dispatch_threads_at_least(shader, domain.size);
input.unbind_as_texture();
output.unbind_as_image();
GPU_shader_unbind();
}
} // namespace blender::realtime_compositor

View File

@ -0,0 +1,88 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "BLI_math_angle_types.hh"
#include "BLI_math_matrix.hh"
#include "BLI_math_matrix_types.hh"
#include "BLI_math_vector_types.hh"
#include "GPU_capabilities.h"
#include "GPU_shader.h"
#include "GPU_texture.h"
#include "COM_algorithm_realize_on_domain.hh"
#include "COM_context.hh"
#include "COM_domain.hh"
#include "COM_result.hh"
#include "COM_algorithm_transform.hh"
namespace blender::realtime_compositor {
/* Given a potentially transformed domain, compute a domain such that its rotation and scale become
* identity and the size of the domain is increased/reduced to adapt to the new transformation. For
* instance, if the domain is rotated, the returned domain will have zero rotation but expanded
* size to account for the bounding box of the domain after rotation. The size of the returned
* domain is bound and clipped by the maximum possible GPU texture size to avoid allocations that
* surpass hardware limits, which is typically 16k. */
static Domain compute_realized_transformation_domain(const Domain &domain)
{
math::AngleRadian rotation;
float2 translation, scale;
float2 size = float2(domain.size);
math::to_loc_rot_scale(domain.transformation, translation, rotation, scale);
/* Set the rotation to zero and expand the domain size to fit the bounding box of the rotated
* result. */
const float sine = math::abs(math::sin(rotation));
const float cosine = math::abs(math::cos(rotation));
size = float2(size.x * sine + size.y * cosine, size.x * cosine + size.y * sine);
rotation = 0.0f;
/* Set the scale to 1 and scale the domain size to adapt to the new domain. */
size *= scale;
scale = float2(1.0f);
const float3x3 transformation = math::from_loc_rot_scale<float3x3>(translation, rotation, scale);
const int2 domain_size = math::clamp(
int2(math::round(size)), int2(1), int2(GPU_max_texture_size()));
return Domain(domain_size, transformation);
}
void transform(Context &context,
Result &input,
Result &output,
float3x3 transformation,
Interpolation interpolation)
{
math::AngleRadian rotation;
float2 translation, scale;
math::to_loc_rot_scale(transformation, translation, rotation, scale);
/* Rotation and scale transformations are immediately realized. */
if (rotation != 0.0f || scale != float2(1.0f)) {
RealizationOptions realization_options = input.get_realization_options();
realization_options.interpolation = interpolation;
Domain input_domain = input.domain();
input_domain.transform(transformation);
const Domain target_domain = compute_realized_transformation_domain(input_domain);
realize_on_domain(
context, input, output, target_domain, input_domain.transformation, realization_options);
}
else {
input.pass_through(output);
}
/* Translation transformations are delayed and are only stored in the result. */
const float3x3 translation_matrix = math::from_location<float3x3>(translation);
output.transform(translation_matrix);
output.get_realization_options().interpolation = interpolation;
}
} // namespace blender::realtime_compositor

View File

@ -106,12 +106,6 @@ void Operation::add_and_evaluate_input_processors()
add_and_evaluate_input_processor(identifier, conversion);
}
for (const StringRef &identifier : results_mapped_to_inputs_.keys()) {
SimpleOperation *realize_transformation = RealizeTransformationOperation::construct_if_needed(
context(), get_input(identifier), get_input_descriptor(identifier));
add_and_evaluate_input_processor(identifier, realize_transformation);
}
for (const StringRef &identifier : results_mapped_to_inputs_.keys()) {
SimpleOperation *realize_on_domain = RealizeOnDomainOperation::construct_if_needed(
context(), get_input(identifier), get_input_descriptor(identifier), compute_domain());

View File

@ -2,22 +2,13 @@
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "BLI_assert.h"
#include "BLI_math_angle_types.hh"
#include "BLI_math_matrix.hh"
#include "BLI_math_matrix_types.hh"
#include "BLI_math_vector_types.hh"
#include "BLI_utildefines.h"
#include "GPU_shader.h"
#include "GPU_texture.h"
#include "COM_algorithm_realize_on_domain.hh"
#include "COM_context.hh"
#include "COM_domain.hh"
#include "COM_input_descriptor.hh"
#include "COM_realize_on_domain_operation.hh"
#include "COM_result.hh"
#include "COM_utilities.hh"
#include "COM_realize_on_domain_operation.hh"
namespace blender::realtime_compositor {
@ -38,90 +29,12 @@ RealizeOnDomainOperation::RealizeOnDomainOperation(Context &context,
void RealizeOnDomainOperation::execute()
{
Result &input = get_input();
Result &result = get_result();
result.allocate_texture(domain_);
GPUShader *shader = get_realization_shader();
GPU_shader_bind(shader);
/* Transform the input space into the domain space. */
const float3x3 local_transformation = math::invert(domain_.transformation) *
input.domain().transformation;
/* Set the origin of the transformation to be the center of the domain. */
const float3x3 transformation = math::from_origin_transform<float3x3>(
local_transformation, float2(domain_.size) / 2.0f);
/* Invert the transformation because the shader transforms the domain coordinates instead of the
* input image itself and thus expect the inverse. */
const float3x3 inverse_transformation = math::invert(transformation);
GPU_shader_uniform_mat3_as_mat4(shader, "inverse_transformation", inverse_transformation.ptr());
/* The texture sampler should use bilinear interpolation for both the bilinear and bicubic
* cases, as the logic used by the bicubic realization shader expects textures to use bilinear
* interpolation. */
const bool use_bilinear = ELEM(input.get_realization_options().interpolation,
Interpolation::Bilinear,
Interpolation::Bicubic);
GPU_texture_filter_mode(input.texture(), use_bilinear);
/* If the input repeats, set a repeating wrap mode for out-of-bound texture access. Otherwise,
* make out-of-bound texture access return zero by setting a clamp to border extend mode. */
GPU_texture_extend_mode_x(input.texture(),
input.get_realization_options().repeat_x ?
GPU_SAMPLER_EXTEND_MODE_REPEAT :
GPU_SAMPLER_EXTEND_MODE_CLAMP_TO_BORDER);
GPU_texture_extend_mode_y(input.texture(),
input.get_realization_options().repeat_y ?
GPU_SAMPLER_EXTEND_MODE_REPEAT :
GPU_SAMPLER_EXTEND_MODE_CLAMP_TO_BORDER);
input.bind_as_texture(shader, "input_tx");
result.bind_as_image(shader, "domain_img");
compute_dispatch_threads_at_least(shader, domain_.size);
input.unbind_as_texture();
result.unbind_as_image();
GPU_shader_unbind();
}
GPUShader *RealizeOnDomainOperation::get_realization_shader()
{
if (get_input().get_realization_options().interpolation == Interpolation::Bicubic) {
switch (get_result().type()) {
case ResultType::Color:
return shader_manager().get("compositor_realize_on_domain_bicubic_color");
case ResultType::Vector:
return shader_manager().get("compositor_realize_on_domain_bicubic_vector");
case ResultType::Float:
return shader_manager().get("compositor_realize_on_domain_bicubic_float");
default:
/* Other types are internal and needn't be handled by operations. */
BLI_assert_unreachable();
return nullptr;
}
}
else {
switch (get_result().type()) {
case ResultType::Color:
return shader_manager().get("compositor_realize_on_domain_color");
case ResultType::Vector:
return shader_manager().get("compositor_realize_on_domain_vector");
case ResultType::Float:
return shader_manager().get("compositor_realize_on_domain_float");
default:
/* Other types are internal and needn't be handled by operations. */
BLI_assert_unreachable();
return nullptr;
}
}
BLI_assert_unreachable();
return nullptr;
realize_on_domain(context(),
get_input(),
get_result(),
domain_,
get_input().domain().transformation,
get_input().get_realization_options());
}
Domain RealizeOnDomainOperation::compute_domain()
@ -161,68 +74,4 @@ SimpleOperation *RealizeOnDomainOperation::construct_if_needed(
return new RealizeOnDomainOperation(context, operation_domain, input_descriptor.type);
}
/* ------------------------------------------------------------------------------------------------
* Realize Transformation Operation
*/
Domain RealizeTransformationOperation::compute_target_domain(
const Domain &input_domain, const InputRealizationOptions &realization_options)
{
if (!realization_options.realize_rotation && !realization_options.realize_scale) {
return input_domain;
}
math::AngleRadian rotation;
float2 translation, scale;
float2 size = float2(input_domain.size);
math::to_loc_rot_scale(input_domain.transformation, translation, rotation, scale);
/* Set the rotation to zero and expand the domain size to fit the bounding box of the rotated
* result. */
if (realization_options.realize_rotation) {
const float sine = math::abs(math::sin(rotation));
const float cosine = math::abs(math::cos(rotation));
size = float2(size.x * sine + size.y * cosine, size.x * cosine + size.y * sine);
rotation = 0.0f;
}
/* Set the scale to 1 and scale the domain size to adapt to the new domain. */
if (realization_options.realize_scale) {
size *= scale;
scale = float2(1.0f);
}
const float3x3 transformation = math::from_loc_rot_scale<float3x3>(translation, rotation, scale);
return Domain(int2(math::ceil(size)), transformation);
}
SimpleOperation *RealizeTransformationOperation::construct_if_needed(
Context &context, const Result &input_result, const InputDescriptor &input_descriptor)
{
/* The input expects a single value and if no single value is provided, it will be ignored and a
* default value will be used, so no need to realize it and the operation is not needed. */
if (input_descriptor.expects_single_value) {
return nullptr;
}
/* Input result is a single value and does not need realization, the operation is not needed. */
if (input_result.is_single_value()) {
return nullptr;
}
const Domain target_domain = compute_target_domain(input_result.domain(),
input_descriptor.realization_options);
/* The input have an identical domain to the target domain, either because the input doesn't need
* to realize its transformations or because it has identity transformations, so no need to
* realize it and the operation is not needed. */
if (target_domain == input_result.domain()) {
return nullptr;
}
/* Otherwise, realization on the target domain is needed. */
return new RealizeOnDomainOperation(context, target_domain, input_descriptor.type);
}
} // namespace blender::realtime_compositor

View File

@ -129,12 +129,6 @@ InputDescriptor input_descriptor_from_input_socket(const bNodeSocket *socket)
input_descriptor.realization_options.realize_on_operation_domain = bool(
socket_declaration->compositor_realization_options() &
CompositorInputRealizationOptions::RealizeOnOperationDomain);
input_descriptor.realization_options.realize_rotation = bool(
socket_declaration->compositor_realization_options() &
CompositorInputRealizationOptions::RealizeRotation);
input_descriptor.realization_options.realize_scale = bool(
socket_declaration->compositor_realization_options() &
CompositorInputRealizationOptions::RealizeScale);
return input_descriptor;
}

View File

@ -52,10 +52,9 @@ enum class OutputSocketFieldType {
enum class CompositorInputRealizationOptions : uint8_t {
None = 0,
RealizeOnOperationDomain = (1 << 0),
RealizeRotation = (1 << 1),
RealizeScale = (1 << 2),
};
ENUM_OPERATORS(CompositorInputRealizationOptions, CompositorInputRealizationOptions::RealizeScale)
ENUM_OPERATORS(CompositorInputRealizationOptions,
CompositorInputRealizationOptions::RealizeOnOperationDomain)
/**
* Contains information about how a node output's field state depends on inputs of the same node.

View File

@ -30,7 +30,7 @@ static void cmp_node_bokehblur_declare(NodeDeclarationBuilder &b)
.compositor_domain_priority(0);
b.add_input<decl::Color>("Bokeh")
.default_value({1.0f, 1.0f, 1.0f, 1.0f})
.compositor_realization_options(CompositorInputRealizationOptions::RealizeRotation);
.compositor_realization_options(CompositorInputRealizationOptions::None);
b.add_input<decl::Float>("Size")
.default_value(1.0f)
.min(0.0f)

View File

@ -7,11 +7,13 @@
*/
#include "BLI_assert.h"
#include "BLI_math_angle_types.hh"
#include "BLI_math_matrix.hh"
#include "UI_interface.hh"
#include "UI_resources.hh"
#include "COM_algorithm_transform.hh"
#include "COM_node_operation.hh"
#include "node_composite_util.hh"
@ -53,15 +55,12 @@ class RotateOperation : public NodeOperation {
void execute() override
{
Result &input = get_input("Image");
Result &result = get_result("Image");
input.pass_through(result);
Result &output = get_result("Image");
const math::AngleRadian rotation = get_input("Degr").get_float_value_default(0.0f);
const float3x3 transformation = math::from_rotation<float3x3>(rotation);
result.transform(transformation);
result.get_realization_options().interpolation = get_interpolation();
transform(context(), input, output, transformation, get_interpolation());
}
Interpolation get_interpolation()

View File

@ -7,8 +7,10 @@
*/
#include "BLI_assert.h"
#include "BLI_math_angle_types.hh"
#include "BLI_math_base.hh"
#include "BLI_math_matrix.hh"
#include "BLI_math_matrix_types.hh"
#include "BLI_math_vector_types.hh"
#include "BLI_string.h"
@ -17,6 +19,7 @@
#include "UI_interface.hh"
#include "UI_resources.hh"
#include "COM_algorithm_transform.hh"
#include "COM_node_operation.hh"
#include "node_composite_util.hh"
@ -82,13 +85,16 @@ class ScaleOperation : public NodeOperation {
void execute() override
{
Result &input = get_input("Image");
Result &result = get_result("Image");
input.pass_through(result);
Result &output = get_result("Image");
const float2 scale = get_scale();
const math::AngleRadian rotation = 0.0f;
const float2 translation = get_translation();
const float3x3 transformation = math::from_loc_rot_scale<float3x3>(
get_translation(), math::AngleRadian(0.0f), get_scale());
translation, rotation, scale);
result.transform(transformation);
const Interpolation interpolation = input.get_realization_options().interpolation;
transform(context(), input, output, transformation, interpolation);
}
float2 get_scale()

View File

@ -23,6 +23,7 @@
#include "BKE_movieclip.h"
#include "BKE_tracking.h"
#include "COM_algorithm_transform.hh"
#include "COM_node_operation.hh"
#include "node_composite_util.hh"
@ -82,17 +83,17 @@ class Stabilize2DOperation : public NodeOperation {
void execute() override
{
Result &input_image = get_input("Image");
Result &output_image = get_result("Image");
input_image.pass_through(output_image);
Result &input = get_input("Image");
Result &output = get_result("Image");
MovieClip *movie_clip = get_movie_clip();
if (input_image.is_single_value() || !movie_clip) {
if (input.is_single_value() || !movie_clip) {
input.pass_through(output);
return;
}
const int width = input_image.domain().size.x;
const int height = input_image.domain().size.y;
const int width = input.domain().size.x;
const int height = input.domain().size.y;
const int frame_number = BKE_movieclip_remap_scene_to_clip_frame(movie_clip,
context().get_frame_number());
@ -107,8 +108,7 @@ class Stabilize2DOperation : public NodeOperation {
transformation = math::invert(transformation);
}
output_image.transform(transformation);
output_image.get_realization_options().interpolation = get_interpolation();
transform(context(), input, output, transformation, get_interpolation());
}
Interpolation get_interpolation()

View File

@ -7,12 +7,14 @@
*/
#include "BLI_assert.h"
#include "BLI_math_angle_types.hh"
#include "BLI_math_matrix.hh"
#include "BLI_math_vector.h"
#include "UI_interface.hh"
#include "UI_resources.hh"
#include "COM_algorithm_transform.hh"
#include "COM_node_operation.hh"
#include "node_composite_util.hh"
@ -56,7 +58,6 @@ static void node_composit_buts_transform(uiLayout *layout, bContext * /*C*/, Poi
}
using namespace blender::realtime_compositor;
using namespace blender::math;
class TransformOperation : public NodeOperation {
public:
@ -65,18 +66,16 @@ class TransformOperation : public NodeOperation {
void execute() override
{
Result &input = get_input("Image");
Result &result = get_result("Image");
input.pass_through(result);
Result &output = get_result("Image");
const float2 translation = float2(get_input("X").get_float_value_default(0.0f),
get_input("Y").get_float_value_default(0.0f));
const AngleRadian rotation = AngleRadian(get_input("Angle").get_float_value_default(0.0f));
const math::AngleRadian rotation = get_input("Angle").get_float_value_default(0.0f);
const float2 scale = float2(get_input("Scale").get_float_value_default(1.0f));
const float3x3 transformation = math::from_loc_rot_scale<float3x3>(
translation, rotation, scale);
const float3x3 transformation = from_loc_rot_scale<float3x3>(translation, rotation, scale);
result.transform(transformation);
result.get_realization_options().interpolation = get_interpolation();
transform(context(), input, output, transformation, get_interpolation());
}
Interpolation get_interpolation()