Files
test/source/blender/sequencer/intern/effects/effects.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

298 lines
7.2 KiB
C++

/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
* SPDX-FileCopyrightText: 2003-2024 Blender Authors
* SPDX-FileCopyrightText: 2005-2006 Peter Schlaile <peter [at] schlaile [dot] de>
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup sequencer
*/
#include "DNA_scene_types.h"
#include "DNA_sequence_types.h"
#include "IMB_colormanagement.hh"
#include "IMB_imbuf.hh"
#include "IMB_metadata.hh"
#include "RE_pipeline.h"
#include "SEQ_render.hh"
#include "SEQ_time.hh"
#include "effects.hh"
#include "render.hh"
namespace blender::seq {
ImBuf *prepare_effect_imbufs(const RenderData *context,
ImBuf *ibuf1,
ImBuf *ibuf2,
bool uninitialized_pixels)
{
ImBuf *out;
Scene *scene = context->scene;
int x = context->rectx;
int y = context->recty;
int base_flags = uninitialized_pixels ? IB_uninitialized_pixels : 0;
if (!ibuf1 && !ibuf2) {
/* Hmm, global float option? */
out = IMB_allocImBuf(x, y, 32, IB_byte_data | base_flags);
}
else if ((ibuf1 && ibuf1->float_buffer.data) || (ibuf2 && ibuf2->float_buffer.data)) {
/* if any inputs are float, output is float too */
out = IMB_allocImBuf(x, y, 32, IB_float_data | base_flags);
}
else {
out = IMB_allocImBuf(x, y, 32, IB_byte_data | base_flags);
}
if (out->float_buffer.data) {
if (ibuf1 && !ibuf1->float_buffer.data) {
seq_imbuf_to_sequencer_space(scene, ibuf1, true);
}
if (ibuf2 && !ibuf2->float_buffer.data) {
seq_imbuf_to_sequencer_space(scene, ibuf2, true);
}
IMB_colormanagement_assign_float_colorspace(out, scene->sequencer_colorspace_settings.name);
}
else {
if (ibuf1 && !ibuf1->byte_buffer.data) {
IMB_byte_from_float(ibuf1);
}
if (ibuf2 && !ibuf2->byte_buffer.data) {
IMB_byte_from_float(ibuf2);
}
}
/* If effect only affecting a single channel, forward input's metadata to the output. */
if (ibuf1 != nullptr && ibuf1 == ibuf2) {
IMB_metadata_copy(out, ibuf1);
}
return out;
}
Array<float> make_gaussian_blur_kernel(float rad, int size)
{
int n = 2 * size + 1;
Array<float> gaussian(n);
float sum = 0.0f;
float fac = (rad > 0.0f ? 1.0f / rad : 0.0f);
for (int i = -size; i <= size; i++) {
float val = RE_filter_value(R_FILTER_GAUSS, float(i) * fac);
sum += val;
gaussian[i + size] = val;
}
float inv_sum = 1.0f / sum;
for (int i = 0; i < n; i++) {
gaussian[i] *= inv_sum;
}
return gaussian;
}
static void init_noop(Strip * /*strip*/) {}
static void load_noop(Strip * /*strip*/) {}
static void free_noop(Strip * /*strip*/, const bool /*do_id_user*/) {}
static int num_inputs_default()
{
return 2;
}
static StripEarlyOut early_out_noop(const Strip * /*strip*/, float /*fac*/)
{
return StripEarlyOut::DoEffect;
}
StripEarlyOut early_out_fade(const Strip * /*strip*/, float fac)
{
if (fac == 0.0f) {
return StripEarlyOut::UseInput1;
}
if (fac == 1.0f) {
return StripEarlyOut::UseInput2;
}
return StripEarlyOut::DoEffect;
}
StripEarlyOut early_out_mul_input2(const Strip * /*strip*/, float fac)
{
if (fac == 0.0f) {
return StripEarlyOut::UseInput1;
}
return StripEarlyOut::DoEffect;
}
StripEarlyOut early_out_mul_input1(const Strip * /*strip*/, float fac)
{
if (fac == 0.0f) {
return StripEarlyOut::UseInput2;
}
return StripEarlyOut::DoEffect;
}
static void get_default_fac_noop(const Scene * /*scene*/,
const Strip * /*strip*/,
float /*timeline_frame*/,
float *fac)
{
*fac = 1.0f;
}
void get_default_fac_fade(const Scene *scene, const Strip *strip, float timeline_frame, float *fac)
{
*fac = float(timeline_frame - time_left_handle_frame_get(scene, strip));
*fac /= time_strip_length_get(scene, strip);
*fac = math::clamp(*fac, 0.0f, 1.0f);
}
EffectHandle effect_handle_get(int strip_type)
{
EffectHandle rval;
rval.init = init_noop;
rval.num_inputs = num_inputs_default;
rval.load = load_noop;
rval.free = free_noop;
rval.early_out = early_out_noop;
rval.get_default_fac = get_default_fac_noop;
rval.execute = nullptr;
rval.copy = nullptr;
switch (strip_type) {
case STRIP_TYPE_CROSS:
cross_effect_get_handle(rval);
break;
case STRIP_TYPE_GAMCROSS:
gamma_cross_effect_get_handle(rval);
break;
case STRIP_TYPE_ADD:
add_effect_get_handle(rval);
break;
case STRIP_TYPE_SUB:
sub_effect_get_handle(rval);
break;
case STRIP_TYPE_MUL:
mul_effect_get_handle(rval);
break;
case STRIP_TYPE_SCREEN:
case STRIP_TYPE_OVERLAY:
case STRIP_TYPE_COLOR_BURN:
case STRIP_TYPE_LINEAR_BURN:
case STRIP_TYPE_DARKEN:
case STRIP_TYPE_LIGHTEN:
case STRIP_TYPE_DODGE:
case STRIP_TYPE_SOFT_LIGHT:
case STRIP_TYPE_HARD_LIGHT:
case STRIP_TYPE_PIN_LIGHT:
case STRIP_TYPE_LIN_LIGHT:
case STRIP_TYPE_VIVID_LIGHT:
case STRIP_TYPE_BLEND_COLOR:
case STRIP_TYPE_HUE:
case STRIP_TYPE_SATURATION:
case STRIP_TYPE_VALUE:
case STRIP_TYPE_DIFFERENCE:
case STRIP_TYPE_EXCLUSION:
blend_mode_effect_get_handle(rval);
break;
case STRIP_TYPE_COLORMIX:
color_mix_effect_get_handle(rval);
break;
case STRIP_TYPE_ALPHAOVER:
alpha_over_effect_get_handle(rval);
break;
case STRIP_TYPE_ALPHAUNDER:
alpha_under_effect_get_handle(rval);
break;
case STRIP_TYPE_WIPE:
wipe_effect_get_handle(rval);
break;
case STRIP_TYPE_GLOW:
glow_effect_get_handle(rval);
break;
case STRIP_TYPE_TRANSFORM:
transform_effect_get_handle(rval);
break;
case STRIP_TYPE_SPEED:
speed_effect_get_handle(rval);
break;
case STRIP_TYPE_COLOR:
solid_color_effect_get_handle(rval);
break;
case STRIP_TYPE_MULTICAM:
multi_camera_effect_get_handle(rval);
break;
case STRIP_TYPE_ADJUSTMENT:
adjustment_effect_get_handle(rval);
break;
case STRIP_TYPE_GAUSSIAN_BLUR:
gaussian_blur_effect_get_handle(rval);
break;
case STRIP_TYPE_TEXT:
text_effect_get_handle(rval);
break;
}
return rval;
}
EffectHandle strip_effect_handle_get(Strip *strip)
{
EffectHandle rval = {};
if (strip->type & STRIP_TYPE_EFFECT) {
rval = effect_handle_get(strip->type);
if ((strip->flag & SEQ_EFFECT_NOT_LOADED) != 0) {
rval.load(strip);
strip->flag &= ~SEQ_EFFECT_NOT_LOADED;
}
}
return rval;
}
EffectHandle strip_effect_get_sequence_blend(Strip *strip)
{
EffectHandle rval = {};
if (strip->blend_mode != 0) {
if ((strip->flag & SEQ_EFFECT_NOT_LOADED) != 0) {
/* load the effect first */
rval = effect_handle_get(strip->type);
rval.load(strip);
}
rval = effect_handle_get(strip->blend_mode);
if ((strip->flag & SEQ_EFFECT_NOT_LOADED) != 0) {
/* now load the blend and unset unloaded flag */
rval.load(strip);
strip->flag &= ~SEQ_EFFECT_NOT_LOADED;
}
}
return rval;
}
int effect_get_num_inputs(int strip_type)
{
EffectHandle rval = effect_handle_get(strip_type);
int count = rval.num_inputs();
if (rval.execute) {
return count;
}
return 0;
}
} // namespace blender::seq