86 lines
3.4 KiB
C++
86 lines
3.4 KiB
C++
/* 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.hh"
|
|
#include "GPU_shader.hh"
|
|
#include "GPU_texture.hh"
|
|
|
|
#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::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(Context &context, 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 * cosine + size.y * sine, size.x * sine + size.y * cosine);
|
|
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 int max_size = context.use_gpu() ? GPU_max_texture_size() : 65536;
|
|
const int2 domain_size = math::clamp(int2(math::round(size)), int2(1), int2(max_size));
|
|
|
|
return Domain(domain_size, transformation);
|
|
}
|
|
|
|
void transform(Context &context,
|
|
Result &input,
|
|
Result &output,
|
|
const float3x3 &transformation,
|
|
RealizationOptions realization_options)
|
|
{
|
|
/* If we are wrapping, the input is translated but the target domain remains fixed, which results
|
|
* in the input clipping on one side and wrapping on the opposite side. This mask vector can be
|
|
* multiplied to the translation component of the transformation to remove it. */
|
|
const float2 wrap_mask = float2(realization_options.wrap_x ? 0.0f : 1.0f,
|
|
realization_options.wrap_y ? 0.0f : 1.0f);
|
|
|
|
/* Compute a transformed input domain, excluding translations of wrapped axes. */
|
|
Domain input_domain = input.domain();
|
|
float3x3 domain_transformation = transformation;
|
|
domain_transformation.location() *= wrap_mask;
|
|
input_domain.transform(domain_transformation);
|
|
|
|
/* Realize the input on the target domain using the full transformation. */
|
|
const Domain target_domain = compute_realized_transformation_domain(context, input_domain);
|
|
realize_on_domain(context,
|
|
input,
|
|
output,
|
|
target_domain,
|
|
transformation * input.domain().transformation,
|
|
realization_options);
|
|
|
|
output.get_realization_options().interpolation = realization_options.interpolation;
|
|
}
|
|
|
|
} // namespace blender::compositor
|