Files
test/source/blender/sequencer/intern/effects/vse_effect_transform.cc
Bastien Montagne 1d76cbab64 Cleanup: sequencer: Replace 'void' MEM_[cm]allocN with templated, type-safe MEM_[cm]allocN<T>.
The main issue of 'type-less' standard C allocations is that there is no check on
allocated type possible.

This is a serious source of annoyance (and crashes) when making some
low-level structs non-trivial, as tracking down all usages of these
structs in higher-level other structs and their allocation is... really
painful.

MEM_[cm]allocN<T> templates on the other hand do check that the
given type is trivial, at build time (static assert), which makes such issue...
trivial to catch.

NOTE: New code should strive to use MEM_new (i.e. allocation and
construction) as much as possible, even for trivial PoD types.

Pull Request: https://projects.blender.org/blender/blender/pulls/135747
2025-03-10 17:16:43 +01:00

204 lines
5.4 KiB
C++

/* SPDX-FileCopyrightText: 2024 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup sequencer
*/
#include "BLI_math_rotation.h"
#include "DNA_sequence_types.h"
#include "DNA_space_types.h"
#include "IMB_imbuf.hh"
#include "IMB_interp.hh"
#include "SEQ_proxy.hh"
#include "SEQ_render.hh"
#include "effects.hh"
namespace blender::seq {
static void init_transform_effect(Strip *strip)
{
if (strip->effectdata) {
MEM_freeN(strip->effectdata);
}
TransformVars *transform = MEM_callocN<TransformVars>("transformvars");
strip->effectdata = transform;
transform->ScalexIni = 1.0f;
transform->ScaleyIni = 1.0f;
transform->xIni = 0.0f;
transform->yIni = 0.0f;
transform->rotIni = 0.0f;
transform->interpolation = 1;
transform->percent = 1;
transform->uniform_scale = 0;
}
static int num_inputs_transform()
{
return 1;
}
static void free_transform_effect(Strip *strip, const bool /*do_id_user*/)
{
MEM_SAFE_FREE(strip->effectdata);
}
static void copy_transform_effect(Strip *dst, const Strip *src, const int /*flag*/)
{
dst->effectdata = MEM_dupallocN(src->effectdata);
}
static void transform_image(int x,
int y,
int start_line,
int total_lines,
const ImBuf *ibuf,
ImBuf *out,
float scale_x,
float scale_y,
float translate_x,
float translate_y,
float rotate,
int interpolation)
{
/* Rotate */
float s = sinf(rotate);
float c = cosf(rotate);
float4 *dst_fl = reinterpret_cast<float4 *>(out->float_buffer.data);
uchar4 *dst_ch = reinterpret_cast<uchar4 *>(out->byte_buffer.data);
size_t offset = size_t(x) * start_line;
for (int yi = start_line; yi < start_line + total_lines; yi++) {
for (int xi = 0; xi < x; xi++) {
/* Translate point. */
float xt = xi - translate_x;
float yt = yi - translate_y;
/* Rotate point with center ref. */
float xr = c * xt + s * yt;
float yr = -s * xt + c * yt;
/* Scale point with center ref. */
xt = xr / scale_x;
yt = yr / scale_y;
/* Undo reference center point. */
xt += (x / 2.0f);
yt += (y / 2.0f);
/* interpolate */
switch (interpolation) {
case 0:
if (dst_fl) {
dst_fl[offset] = imbuf::interpolate_nearest_border_fl(ibuf, xt, yt);
}
else {
dst_ch[offset] = imbuf::interpolate_nearest_border_byte(ibuf, xt, yt);
}
break;
case 1:
if (dst_fl) {
dst_fl[offset] = imbuf::interpolate_bilinear_border_fl(ibuf, xt, yt);
}
else {
dst_ch[offset] = imbuf::interpolate_bilinear_border_byte(ibuf, xt, yt);
}
break;
case 2:
if (dst_fl) {
dst_fl[offset] = imbuf::interpolate_cubic_bspline_fl(ibuf, xt, yt);
}
else {
dst_ch[offset] = imbuf::interpolate_cubic_bspline_byte(ibuf, xt, yt);
}
break;
}
offset++;
}
}
}
static ImBuf *do_transform_effect(const RenderData *context,
Strip *strip,
float /*timeline_frame*/,
float /*fac*/,
ImBuf *src1,
ImBuf * /*src2*/)
{
ImBuf *dst = prepare_effect_imbufs(context, src1, nullptr);
const TransformVars *transform = (TransformVars *)strip->effectdata;
/* Scale */
float scale_x, scale_y;
if (transform->uniform_scale) {
scale_x = scale_y = transform->ScalexIni;
}
else {
scale_x = transform->ScalexIni;
scale_y = transform->ScaleyIni;
}
const int x = context->rectx;
const int y = context->recty;
/* Translate */
float translate_x, translate_y;
if (!transform->percent) {
/* Compensate text size for preview render size. */
double proxy_size_comp = context->scene->r.size / 100.0;
if (context->preview_render_size != SEQ_RENDER_SIZE_SCENE) {
proxy_size_comp = rendersize_to_scale_factor(context->preview_render_size);
}
translate_x = transform->xIni * proxy_size_comp + (x / 2.0f);
translate_y = transform->yIni * proxy_size_comp + (y / 2.0f);
}
else {
translate_x = x * (transform->xIni / 100.0f) + (x / 2.0f);
translate_y = y * (transform->yIni / 100.0f) + (y / 2.0f);
}
/* Rotate */
float rotate_radians = DEG2RADF(transform->rotIni);
blender::threading::parallel_for(
blender::IndexRange(dst->y), 32, [&](blender::IndexRange y_range) {
transform_image(x,
y,
y_range.first(),
y_range.size(),
src1,
dst,
scale_x,
scale_y,
translate_x,
translate_y,
rotate_radians,
transform->interpolation);
});
return dst;
}
void transform_effect_get_handle(EffectHandle &rval)
{
rval.init = init_transform_effect;
rval.num_inputs = num_inputs_transform;
rval.free = free_transform_effect;
rval.copy = copy_transform_effect;
rval.execute = do_transform_effect;
}
} // namespace blender::seq