From c3688f7bb77be0f36c7af33e7883ac731569172a Mon Sep 17 00:00:00 2001 From: Omar Emara Date: Wed, 6 Aug 2025 13:46:34 +0200 Subject: [PATCH] Fix #136939: Translation is ignored in some nodes The compositor ignores translation in certain nodes like Corner Pin. Users find this unexpected as adjusting the translation of the input has no effect on the output. The only alternative logical thing to do if translation exists is to clip the image, which this patch do. This affects the following nodes: - File Output. - Map UV (Image input). - Corner Pin. - Plane Track Deform. - Bokeh Blur (Bokeh Kernel input). Pull Request: https://projects.blender.org/blender/blender/pulls/144049 --- .../COM_realize_on_domain_operation.hh | 12 ++++++++---- .../intern/realize_on_domain_operation.cc | 19 ++++++++++++++----- .../nodes/node_composite_cornerpin.cc | 9 +++++++++ .../nodes/node_composite_file_output.cc | 13 +++++++++++++ 4 files changed, 44 insertions(+), 9 deletions(-) diff --git a/source/blender/compositor/COM_realize_on_domain_operation.hh b/source/blender/compositor/COM_realize_on_domain_operation.hh index e4ae63668bb..e2f9cb9464e 100644 --- a/source/blender/compositor/COM_realize_on_domain_operation.hh +++ b/source/blender/compositor/COM_realize_on_domain_operation.hh @@ -44,10 +44,14 @@ class RealizeOnDomainOperation : public SimpleOperation { /* 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 size to avoid - * allocations that surpass hardware limits. */ - static Domain compute_realized_transformation_domain(Context &context, const Domain &domain); + * rotation but expanded size to account for the bounding box of the domain after rotation. + * Similarly, if the realize_translation argument is true, translation will be set to zero, + * though this will not affect the size of the domain in any way. The size of the returned domain + * is bound and clipped by the maximum possible size to avoid allocations that surpass hardware + * limits. */ + static Domain compute_realized_transformation_domain(Context &context, + const Domain &domain, + const bool realize_translation = false); protected: /* The operation domain is just the target domain. */ diff --git a/source/blender/compositor/intern/realize_on_domain_operation.cc b/source/blender/compositor/intern/realize_on_domain_operation.cc index d7bc5338bac..e5bcd1a55c5 100644 --- a/source/blender/compositor/intern/realize_on_domain_operation.cc +++ b/source/blender/compositor/intern/realize_on_domain_operation.cc @@ -207,16 +207,19 @@ Domain RealizeOnDomainOperation::compute_domain() * realization shouldn't be needed. */ static constexpr float transformation_tolerance = 10e-6f; -Domain RealizeOnDomainOperation::compute_realized_transformation_domain(Context &context, - const Domain &domain) +Domain RealizeOnDomainOperation::compute_realized_transformation_domain( + Context &context, const Domain &domain, const bool realize_translation) { const int2 size = domain.size; /* If the domain is only infinitesimally rotated or scaled, return a domain with just the - * translation component. */ + * translation component if not realizing translation. */ if (math::is_equal( float2x2(domain.transformation), float2x2::identity(), transformation_tolerance)) { + if (realize_translation) { + return Domain(size); + } return Domain(size, math::from_location(domain.transformation.location())); } @@ -263,7 +266,10 @@ Domain RealizeOnDomainOperation::compute_realized_transformation_domain(Context const int2 safe_size = math::clamp(new_size, int2(1), int2(max_size)); /* Create a domain from the new safe size and just the translation component of the - * transformation, */ + * transformation if not realizing translation. */ + if (realize_translation) { + return Domain(safe_size); + } return Domain(safe_size, math::from_location(domain.transformation.location())); } @@ -295,8 +301,11 @@ SimpleOperation *RealizeOnDomainOperation::construct_if_needed( InputRealizationMode::OperationDomain; const Domain target_domain = use_operation_domain ? operation_domain : input_result.domain(); + const bool should_realize_translation = input_descriptor.realization_mode == + InputRealizationMode::Transforms; const Domain realized_target_domain = - RealizeOnDomainOperation::compute_realized_transformation_domain(context, target_domain); + RealizeOnDomainOperation::compute_realized_transformation_domain( + context, target_domain, should_realize_translation); /* The input have an almost identical domain to the realized target domain, so no need to realize * it and the operation is not needed. */ diff --git a/source/blender/nodes/composite/nodes/node_composite_cornerpin.cc b/source/blender/nodes/composite/nodes/node_composite_cornerpin.cc index 773fbf5ebca..52e5681b8a1 100644 --- a/source/blender/nodes/composite/nodes/node_composite_cornerpin.cc +++ b/source/blender/nodes/composite/nodes/node_composite_cornerpin.cc @@ -425,6 +425,15 @@ class CornerPinOperation : public NodeOperation { return is_clipped_x || is_clipped_y || output_needed || use_anisotropic; } + + Domain compute_domain() override + { + Domain domain = this->get_input("Image").domain(); + /* Reset the location of the domain such that translations take effect, this will result in + * clipping but is more expected for the user. */ + domain.transformation.location() = float2(0.0f); + return domain; + } }; static NodeOperation *get_compositor_operation(Context &context, DNode node) diff --git a/source/blender/nodes/composite/nodes/node_composite_file_output.cc b/source/blender/nodes/composite/nodes/node_composite_file_output.cc index 11c626d1fc8..cde891373c0 100644 --- a/source/blender/nodes/composite/nodes/node_composite_file_output.cc +++ b/source/blender/nodes/composite/nodes/node_composite_file_output.cc @@ -1023,6 +1023,19 @@ class FileOutputOperation : public NodeOperation { { return context().get_render_data().scemode & R_MULTIVIEW; } + + Domain compute_domain() override + { + Domain domain = NodeOperation::compute_domain(); + if (!this->is_multi_layer()) { + return domain; + } + + /* Reset the location of the domain in multi-layer case such that translations take effect, + * this will result in clipping but is more expected for the user. */ + domain.transformation.location() = float2(0.0f); + return domain; + } }; static NodeOperation *get_compositor_operation(Context &context, DNode node)