Files
test2/source/blender/sequencer/intern/effects/vse_effect_blend.cc
Aras Pranckevicius 4f0fd32d6f VSE: Over Drop effect was not doing anything useful, map it to Alpha Over
Behavior of it was exactly the same as Alpha Over for the last 18 years (since
327d413eb3 in 2006 March), so just remap it to regular alpha over on file read.

Pull Request: https://projects.blender.org/blender/blender/pulls/134342
2025-02-10 16:10:01 +01:00

401 lines
12 KiB
C++

/* SPDX-FileCopyrightText: 2024 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup sequencer
*/
#include "BLI_math_color_blend.h"
#include "DNA_sequence_types.h"
#include "IMB_imbuf.hh"
#include "SEQ_render.hh"
#include "effects.hh"
using namespace blender;
/* -------------------------------------------------------------------- */
/* Alpha Over Effect */
static void init_alpha_over_or_under(Strip *strip)
{
Strip *seq1 = strip->seq1;
Strip *seq2 = strip->seq2;
strip->seq2 = seq1;
strip->seq1 = seq2;
}
static bool alpha_opaque(uchar alpha)
{
return alpha == 255;
}
static bool alpha_opaque(float alpha)
{
return alpha >= 1.0f;
}
/* dst = src1 over src2 (alpha from src1) */
struct AlphaOverEffectOp {
template<typename T> void apply(const T *src1, const T *src2, T *dst, int64_t size) const
{
const float fac = this->factor;
if (fac <= 0.0f) {
memcpy(dst, src2, sizeof(T) * 4 * size);
return;
}
for (int64_t idx = 0; idx < size; idx++) {
if (src1[3] <= 0.0f) {
/* Alpha of zero. No color addition will happen as the colors are pre-multiplied. */
memcpy(dst, src2, sizeof(T) * 4);
}
else if (fac == 1.0f && alpha_opaque(src1[3])) {
/* No change to `src1` as `fac == 1` and fully opaque. */
memcpy(dst, src1, sizeof(T) * 4);
}
else {
float4 col1 = load_premul_pixel(src1);
float mfac = 1.0f - fac * col1.w;
float4 col2 = load_premul_pixel(src2);
float4 col = fac * col1 + mfac * col2;
store_premul_pixel(col, dst);
}
src1 += 4;
src2 += 4;
dst += 4;
}
}
float factor;
};
static ImBuf *do_alphaover_effect(const SeqRenderData *context,
Strip * /*strip*/,
float /*timeline_frame*/,
float fac,
ImBuf *src1,
ImBuf *src2)
{
ImBuf *dst = prepare_effect_imbufs(context, src1, src2);
AlphaOverEffectOp op;
op.factor = fac;
apply_effect_op(op, src1, src2, dst);
return dst;
}
/* -------------------------------------------------------------------- */
/* Alpha Under Effect */
/* dst = src1 under src2 (alpha from src2) */
struct AlphaUnderEffectOp {
template<typename T> void apply(const T *src1, const T *src2, T *dst, int64_t size) const
{
const float fac = this->factor;
if (fac <= 0.0f) {
memcpy(dst, src2, sizeof(T) * 4 * size);
return;
}
for (int64_t idx = 0; idx < size; idx++) {
if (src2[3] <= 0.0f && fac >= 1.0f) {
memcpy(dst, src1, sizeof(T) * 4);
}
else if (alpha_opaque(src2[3])) {
memcpy(dst, src2, sizeof(T) * 4);
}
else {
float4 col2 = load_premul_pixel(src2);
float mfac = fac * (1.0f - col2.w);
float4 col1 = load_premul_pixel(src1);
float4 col = mfac * col1 + col2;
store_premul_pixel(col, dst);
}
src1 += 4;
src2 += 4;
dst += 4;
}
}
float factor;
};
static ImBuf *do_alphaunder_effect(const SeqRenderData *context,
Strip * /*strip*/,
float /*timeline_frame*/,
float fac,
ImBuf *src1,
ImBuf *src2)
{
ImBuf *dst = prepare_effect_imbufs(context, src1, src2);
AlphaUnderEffectOp op;
op.factor = fac;
apply_effect_op(op, src1, src2, dst);
return dst;
}
/* -------------------------------------------------------------------- */
/* Blend Mode Effect */
/* blend_function has to be: void (T* dst, const T *src1, const T *src2) */
template<typename T, typename Func>
static void apply_blend_function(
float fac, int64_t size, const T *src1, const T *src2, T *dst, Func blend_function)
{
for (int64_t i = 0; i < size; i++) {
T achannel = src2[3];
((T *)src2)[3] = T(achannel * fac);
blend_function(dst, src1, src2);
((T *)src2)[3] = achannel;
dst[3] = src1[3];
src1 += 4;
src2 += 4;
dst += 4;
}
}
static void do_blend_effect_float(
float fac, int64_t size, const float *rect1, const float *rect2, int btype, float *out)
{
switch (btype) {
case STRIP_TYPE_ADD:
apply_blend_function(fac, size, rect1, rect2, out, blend_color_add_float);
break;
case STRIP_TYPE_SUB:
apply_blend_function(fac, size, rect1, rect2, out, blend_color_sub_float);
break;
case STRIP_TYPE_MUL:
apply_blend_function(fac, size, rect1, rect2, out, blend_color_mul_float);
break;
case STRIP_TYPE_DARKEN:
apply_blend_function(fac, size, rect1, rect2, out, blend_color_darken_float);
break;
case STRIP_TYPE_COLOR_BURN:
apply_blend_function(fac, size, rect1, rect2, out, blend_color_burn_float);
break;
case STRIP_TYPE_LINEAR_BURN:
apply_blend_function(fac, size, rect1, rect2, out, blend_color_linearburn_float);
break;
case STRIP_TYPE_SCREEN:
apply_blend_function(fac, size, rect1, rect2, out, blend_color_screen_float);
break;
case STRIP_TYPE_LIGHTEN:
apply_blend_function(fac, size, rect1, rect2, out, blend_color_lighten_float);
break;
case STRIP_TYPE_DODGE:
apply_blend_function(fac, size, rect1, rect2, out, blend_color_dodge_float);
break;
case STRIP_TYPE_OVERLAY:
apply_blend_function(fac, size, rect1, rect2, out, blend_color_overlay_float);
break;
case STRIP_TYPE_SOFT_LIGHT:
apply_blend_function(fac, size, rect1, rect2, out, blend_color_softlight_float);
break;
case STRIP_TYPE_HARD_LIGHT:
apply_blend_function(fac, size, rect1, rect2, out, blend_color_hardlight_float);
break;
case STRIP_TYPE_PIN_LIGHT:
apply_blend_function(fac, size, rect1, rect2, out, blend_color_pinlight_float);
break;
case STRIP_TYPE_LIN_LIGHT:
apply_blend_function(fac, size, rect1, rect2, out, blend_color_linearlight_float);
break;
case STRIP_TYPE_VIVID_LIGHT:
apply_blend_function(fac, size, rect1, rect2, out, blend_color_vividlight_float);
break;
case STRIP_TYPE_BLEND_COLOR:
apply_blend_function(fac, size, rect1, rect2, out, blend_color_color_float);
break;
case STRIP_TYPE_HUE:
apply_blend_function(fac, size, rect1, rect2, out, blend_color_hue_float);
break;
case STRIP_TYPE_SATURATION:
apply_blend_function(fac, size, rect1, rect2, out, blend_color_saturation_float);
break;
case STRIP_TYPE_VALUE:
apply_blend_function(fac, size, rect1, rect2, out, blend_color_luminosity_float);
break;
case STRIP_TYPE_DIFFERENCE:
apply_blend_function(fac, size, rect1, rect2, out, blend_color_difference_float);
break;
case STRIP_TYPE_EXCLUSION:
apply_blend_function(fac, size, rect1, rect2, out, blend_color_exclusion_float);
break;
default:
break;
}
}
static void do_blend_effect_byte(
float fac, int64_t size, const uchar *rect1, const uchar *rect2, int btype, uchar *out)
{
switch (btype) {
case STRIP_TYPE_ADD:
apply_blend_function(fac, size, rect1, rect2, out, blend_color_add_byte);
break;
case STRIP_TYPE_SUB:
apply_blend_function(fac, size, rect1, rect2, out, blend_color_sub_byte);
break;
case STRIP_TYPE_MUL:
apply_blend_function(fac, size, rect1, rect2, out, blend_color_mul_byte);
break;
case STRIP_TYPE_DARKEN:
apply_blend_function(fac, size, rect1, rect2, out, blend_color_darken_byte);
break;
case STRIP_TYPE_COLOR_BURN:
apply_blend_function(fac, size, rect1, rect2, out, blend_color_burn_byte);
break;
case STRIP_TYPE_LINEAR_BURN:
apply_blend_function(fac, size, rect1, rect2, out, blend_color_linearburn_byte);
break;
case STRIP_TYPE_SCREEN:
apply_blend_function(fac, size, rect1, rect2, out, blend_color_screen_byte);
break;
case STRIP_TYPE_LIGHTEN:
apply_blend_function(fac, size, rect1, rect2, out, blend_color_lighten_byte);
break;
case STRIP_TYPE_DODGE:
apply_blend_function(fac, size, rect1, rect2, out, blend_color_dodge_byte);
break;
case STRIP_TYPE_OVERLAY:
apply_blend_function(fac, size, rect1, rect2, out, blend_color_overlay_byte);
break;
case STRIP_TYPE_SOFT_LIGHT:
apply_blend_function(fac, size, rect1, rect2, out, blend_color_softlight_byte);
break;
case STRIP_TYPE_HARD_LIGHT:
apply_blend_function(fac, size, rect1, rect2, out, blend_color_hardlight_byte);
break;
case STRIP_TYPE_PIN_LIGHT:
apply_blend_function(fac, size, rect1, rect2, out, blend_color_pinlight_byte);
break;
case STRIP_TYPE_LIN_LIGHT:
apply_blend_function(fac, size, rect1, rect2, out, blend_color_linearlight_byte);
break;
case STRIP_TYPE_VIVID_LIGHT:
apply_blend_function(fac, size, rect1, rect2, out, blend_color_vividlight_byte);
break;
case STRIP_TYPE_BLEND_COLOR:
apply_blend_function(fac, size, rect1, rect2, out, blend_color_color_byte);
break;
case STRIP_TYPE_HUE:
apply_blend_function(fac, size, rect1, rect2, out, blend_color_hue_byte);
break;
case STRIP_TYPE_SATURATION:
apply_blend_function(fac, size, rect1, rect2, out, blend_color_saturation_byte);
break;
case STRIP_TYPE_VALUE:
apply_blend_function(fac, size, rect1, rect2, out, blend_color_luminosity_byte);
break;
case STRIP_TYPE_DIFFERENCE:
apply_blend_function(fac, size, rect1, rect2, out, blend_color_difference_byte);
break;
case STRIP_TYPE_EXCLUSION:
apply_blend_function(fac, size, rect1, rect2, out, blend_color_exclusion_byte);
break;
default:
break;
}
}
struct BlendModeEffectOp {
template<typename T> void apply(const T *src1, const T *src2, T *dst, int64_t size) const
{
if constexpr (std::is_same_v<T, float>) {
do_blend_effect_float(this->factor, size, src1, src2, this->blend_mode, dst);
}
else {
do_blend_effect_byte(this->factor, size, src1, src2, this->blend_mode, dst);
}
}
int blend_mode; /* STRIP_TYPE_ */
float factor;
};
static ImBuf *do_blend_mode_effect(const SeqRenderData *context,
Strip *strip,
float /*timeline_frame*/,
float fac,
ImBuf *src1,
ImBuf *src2)
{
ImBuf *dst = prepare_effect_imbufs(context, src1, src2);
BlendModeEffectOp op;
op.factor = fac;
op.blend_mode = strip->blend_mode;
apply_effect_op(op, src1, src2, dst);
return dst;
}
/* -------------------------------------------------------------------- */
/* Color Mix Effect */
static void init_colormix_effect(Strip *strip)
{
if (strip->effectdata) {
MEM_freeN(strip->effectdata);
}
strip->effectdata = MEM_callocN(sizeof(ColorMixVars), "colormixvars");
ColorMixVars *data = (ColorMixVars *)strip->effectdata;
data->blend_effect = STRIP_TYPE_OVERLAY;
data->factor = 1.0f;
}
static ImBuf *do_colormix_effect(const SeqRenderData *context,
Strip *strip,
float /*timeline_frame*/,
float /*fac*/,
ImBuf *src1,
ImBuf *src2)
{
ImBuf *dst = prepare_effect_imbufs(context, src1, src2);
const ColorMixVars *data = static_cast<const ColorMixVars *>(strip->effectdata);
BlendModeEffectOp op;
op.blend_mode = data->blend_effect;
op.factor = data->factor;
apply_effect_op(op, src1, src2, dst);
return dst;
}
static void copy_effect_default(Strip *dst, const Strip *src, const int /*flag*/)
{
dst->effectdata = MEM_dupallocN(src->effectdata);
}
static void free_effect_default(Strip *strip, const bool /*do_id_user*/)
{
MEM_SAFE_FREE(strip->effectdata);
}
void blend_mode_effect_get_handle(SeqEffectHandle &rval)
{
rval.execute = do_blend_mode_effect;
rval.early_out = early_out_mul_input2;
}
void color_mix_effect_get_handle(SeqEffectHandle &rval)
{
rval.init = init_colormix_effect;
rval.free = free_effect_default;
rval.copy = copy_effect_default;
rval.execute = do_colormix_effect;
rval.early_out = early_out_mul_input2;
}
void alpha_over_effect_get_handle(SeqEffectHandle &rval)
{
rval.init = init_alpha_over_or_under;
rval.execute = do_alphaover_effect;
rval.early_out = early_out_mul_input1;
}
void alpha_under_effect_get_handle(SeqEffectHandle &rval)
{
rval.init = init_alpha_over_or_under;
rval.execute = do_alphaunder_effect;
}