Files
test2/source/blender/sequencer/intern/effects/vse_effect_speed.cc
John Kiril Swenson 1834f9f200 VSE: Improve "Change Effect Type" operator
- Previously, effects could be changed into types that take different
amount of inputs, e.g. from "add" to "glow", which led to undefined
and glitchy behavior (unreported bug). Fix by requiring same input count
for new effect.
- Previously, strip names were not updated when changing effect type,
which tended to be confusing. Now, if the default name was not changed,
update to the new effect type name.
- Remove "effect strip" menu from color and text strips. Although they
are effects internally, to a user, this is not clear, and providing the
option to modify effect inputs in these cases doesn't really make sense.

Pull Request: https://projects.blender.org/blender/blender/pulls/139514
2025-05-29 20:28:20 +02:00

220 lines
6.5 KiB
C++

/* SPDX-FileCopyrightText: 2024 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup sequencer
*/
#include "BKE_fcurve.hh"
#include "DNA_scene_types.h"
#include "DNA_sequence_types.h"
#include "IMB_imbuf.hh"
#include "RNA_prototypes.hh"
#include "SEQ_render.hh"
#include "SEQ_time.hh"
#include "effects.hh"
#include "render.hh"
namespace blender::seq {
static void init_speed_effect(Strip *strip)
{
if (strip->effectdata) {
MEM_freeN(strip->effectdata);
}
SpeedControlVars *v = MEM_callocN<SpeedControlVars>("speedcontrolvars");
strip->effectdata = v;
v->speed_control_type = SEQ_SPEED_STRETCH;
v->speed_fader = 1.0f;
v->speed_fader_length = 0.0f;
v->speed_fader_frame_number = 0.0f;
}
static void load_speed_effect(Strip *strip)
{
SpeedControlVars *v = (SpeedControlVars *)strip->effectdata;
v->frameMap = nullptr;
}
static int num_inputs_speed()
{
return 1;
}
static void free_speed_effect(Strip *strip, const bool /*do_id_user*/)
{
SpeedControlVars *v = (SpeedControlVars *)strip->effectdata;
if (v->frameMap) {
MEM_freeN(v->frameMap);
}
MEM_SAFE_FREE(strip->effectdata);
}
static void copy_speed_effect(Strip *dst, const Strip *src, const int /*flag*/)
{
SpeedControlVars *v;
dst->effectdata = MEM_dupallocN(src->effectdata);
v = (SpeedControlVars *)dst->effectdata;
v->frameMap = nullptr;
}
static StripEarlyOut early_out_speed(const Strip * /*strip*/, float /*fac*/)
{
return StripEarlyOut::DoEffect;
}
static FCurve *strip_effect_speed_speed_factor_curve_get(Scene *scene, Strip *strip)
{
return id_data_find_fcurve(&scene->id, strip, &RNA_Strip, "speed_factor", 0, nullptr);
}
void strip_effect_speed_rebuild_map(Scene *scene, Strip *strip)
{
const int effect_strip_length = time_right_handle_frame_get(scene, strip) -
time_left_handle_frame_get(scene, strip);
if ((strip->input1 == nullptr) || (effect_strip_length < 1)) {
return; /* Make COVERITY happy and check for (CID 598) input strip. */
}
const FCurve *fcu = strip_effect_speed_speed_factor_curve_get(scene, strip);
if (fcu == nullptr) {
return;
}
SpeedControlVars *v = (SpeedControlVars *)strip->effectdata;
if (v->frameMap) {
MEM_freeN(v->frameMap);
}
v->frameMap = MEM_malloc_arrayN<float>(size_t(effect_strip_length), __func__);
v->frameMap[0] = 0.0f;
float target_frame = 0;
for (int frame_index = 1; frame_index < effect_strip_length; frame_index++) {
target_frame += evaluate_fcurve(fcu, time_left_handle_frame_get(scene, strip) + frame_index);
const int target_frame_max = time_strip_length_get(scene, strip->input1);
CLAMP(target_frame, 0, target_frame_max);
v->frameMap[frame_index] = target_frame;
}
}
static void strip_effect_speed_frame_map_ensure(Scene *scene, Strip *strip)
{
const SpeedControlVars *v = (SpeedControlVars *)strip->effectdata;
if (v->frameMap != nullptr) {
return;
}
strip_effect_speed_rebuild_map(scene, strip);
}
float strip_speed_effect_target_frame_get(Scene *scene,
Strip *strip_speed,
float timeline_frame,
int input)
{
if (strip_speed->input1 == nullptr) {
return 0.0f;
}
strip_effect_handle_get(strip_speed); /* Ensure, that data are initialized. */
int frame_index = round_fl_to_int(give_frame_index(scene, strip_speed, timeline_frame));
SpeedControlVars *s = (SpeedControlVars *)strip_speed->effectdata;
const Strip *source = strip_speed->input1;
float target_frame = 0.0f;
switch (s->speed_control_type) {
case SEQ_SPEED_STRETCH: {
/* Only right handle controls effect speed! */
const float target_content_length = time_strip_length_get(scene, source) - source->startofs;
const float speed_effetct_length = time_right_handle_frame_get(scene, strip_speed) -
time_left_handle_frame_get(scene, strip_speed);
const float ratio = frame_index / speed_effetct_length;
target_frame = target_content_length * ratio;
break;
}
case SEQ_SPEED_MULTIPLY: {
const FCurve *fcu = strip_effect_speed_speed_factor_curve_get(scene, strip_speed);
if (fcu != nullptr) {
strip_effect_speed_frame_map_ensure(scene, strip_speed);
target_frame = s->frameMap[frame_index];
}
else {
target_frame = frame_index * s->speed_fader;
}
break;
}
case SEQ_SPEED_LENGTH:
target_frame = time_strip_length_get(scene, source) * (s->speed_fader_length / 100.0f);
break;
case SEQ_SPEED_FRAME_NUMBER:
target_frame = s->speed_fader_frame_number;
break;
}
CLAMP(target_frame, 0, time_strip_length_get(scene, source));
target_frame += strip_speed->start;
/* No interpolation. */
if ((s->flags & SEQ_SPEED_USE_INTERPOLATION) == 0) {
return target_frame;
}
/* Interpolation is used, switch between current and next frame based on which input is
* requested. */
return input == 0 ? target_frame : ceil(target_frame);
}
static float speed_effect_interpolation_ratio_get(Scene *scene,
Strip *strip_speed,
float timeline_frame)
{
const float target_frame = strip_speed_effect_target_frame_get(
scene, strip_speed, timeline_frame, 0);
return target_frame - floor(target_frame);
}
static ImBuf *do_speed_effect(const RenderData *context,
Strip *strip,
float timeline_frame,
float fac,
ImBuf *ibuf1,
ImBuf *ibuf2)
{
const SpeedControlVars *s = (SpeedControlVars *)strip->effectdata;
EffectHandle cross_effect = effect_handle_get(STRIP_TYPE_CROSS);
ImBuf *out;
if (s->flags & SEQ_SPEED_USE_INTERPOLATION) {
fac = speed_effect_interpolation_ratio_get(context->scene, strip, timeline_frame);
/* Current frame is ibuf1, next frame is ibuf2. */
out = cross_effect.execute(context, nullptr, timeline_frame, fac, ibuf1, ibuf2);
return out;
}
/* No interpolation. */
return IMB_dupImBuf(ibuf1);
}
void speed_effect_get_handle(EffectHandle &rval)
{
rval.init = init_speed_effect;
rval.num_inputs = num_inputs_speed;
rval.load = load_speed_effect;
rval.free = free_speed_effect;
rval.copy = copy_speed_effect;
rval.execute = do_speed_effect;
rval.early_out = early_out_speed;
}
} // namespace blender::seq