Add Chromium-only Blender WebEngine parity work
This commit is contained in:
@@ -0,0 +1,181 @@
|
||||
/* SPDX-FileCopyrightText: 2024 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup sequencer
|
||||
*/
|
||||
|
||||
#include "DNA_sequence_types.h"
|
||||
|
||||
#include "IMB_imbuf.hh"
|
||||
|
||||
#include "PRF_profile.hh"
|
||||
|
||||
#include "SEQ_render.hh"
|
||||
|
||||
#include "effects.hh"
|
||||
|
||||
namespace blender::seq {
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/* Color Add Effect */
|
||||
|
||||
struct AddEffectOp {
|
||||
template<typename T> void apply(const T *src1, const T *src2, T *dst, int64_t size) const
|
||||
{
|
||||
const float fac = this->factor;
|
||||
int ifac = int(256.0f * fac);
|
||||
for (int64_t idx = 0; idx < size; idx++) {
|
||||
if constexpr (std::is_same_v<T, uchar>) {
|
||||
const int f = ifac * int(src2[3]);
|
||||
dst[0] = min_ii(src1[0] + ((f * src2[0]) >> 16), 255);
|
||||
dst[1] = min_ii(src1[1] + ((f * src2[1]) >> 16), 255);
|
||||
dst[2] = min_ii(src1[2] + ((f * src2[2]) >> 16), 255);
|
||||
}
|
||||
else {
|
||||
const float f = (1.0f - (src1[3] * (1.0f - fac))) * src2[3];
|
||||
dst[0] = src1[0] + f * src2[0];
|
||||
dst[1] = src1[1] + f * src2[1];
|
||||
dst[2] = src1[2] + f * src2[2];
|
||||
}
|
||||
dst[3] = src1[3];
|
||||
src1 += 4;
|
||||
src2 += 4;
|
||||
dst += 4;
|
||||
}
|
||||
}
|
||||
float factor;
|
||||
};
|
||||
|
||||
static SeqResult do_add_effect(const RenderData *context,
|
||||
SeqRenderState * /*state*/,
|
||||
Strip * /*strip*/,
|
||||
float /*timeline_frame*/,
|
||||
float fac,
|
||||
const SeqResult &src1,
|
||||
const SeqResult &src2)
|
||||
{
|
||||
PRF_scope_with_name("SeqFxAdd", ProfileCategory::Draw);
|
||||
SeqResult dst = prepare_effect_imbufs(context, src1, src2);
|
||||
AddEffectOp op;
|
||||
op.factor = fac;
|
||||
apply_effect_op(op, src1.image, src2.image, dst.image);
|
||||
/* Destination uses alpha from src1 */
|
||||
dst.is_opaque_before_transform = !src1.image->can_contain_alpha();
|
||||
return dst;
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/* Color Subtract Effect */
|
||||
|
||||
struct SubEffectOp {
|
||||
template<typename T> void apply(const T *src1, const T *src2, T *dst, int64_t size) const
|
||||
{
|
||||
const float fac = this->factor;
|
||||
int ifac = int(256.0f * fac);
|
||||
for (int64_t idx = 0; idx < size; idx++) {
|
||||
if constexpr (std::is_same_v<T, uchar>) {
|
||||
const int f = ifac * int(src2[3]);
|
||||
dst[0] = max_ii(src1[0] - ((f * src2[0]) >> 16), 0);
|
||||
dst[1] = max_ii(src1[1] - ((f * src2[1]) >> 16), 0);
|
||||
dst[2] = max_ii(src1[2] - ((f * src2[2]) >> 16), 0);
|
||||
}
|
||||
else {
|
||||
const float f = (1.0f - (src1[3] * (1.0f - fac))) * src2[3];
|
||||
dst[0] = max_ff(src1[0] - f * src2[0], 0.0f);
|
||||
dst[1] = max_ff(src1[1] - f * src2[1], 0.0f);
|
||||
dst[2] = max_ff(src1[2] - f * src2[2], 0.0f);
|
||||
}
|
||||
dst[3] = src1[3];
|
||||
src1 += 4;
|
||||
src2 += 4;
|
||||
dst += 4;
|
||||
}
|
||||
}
|
||||
float factor;
|
||||
};
|
||||
|
||||
static SeqResult do_sub_effect(const RenderData *context,
|
||||
SeqRenderState * /*state*/,
|
||||
Strip * /*strip*/,
|
||||
float /*timeline_frame*/,
|
||||
float fac,
|
||||
const SeqResult &src1,
|
||||
const SeqResult &src2)
|
||||
{
|
||||
PRF_scope_with_name("SeqFxSub", ProfileCategory::Draw);
|
||||
SeqResult dst = prepare_effect_imbufs(context, src1, src2);
|
||||
SubEffectOp op;
|
||||
op.factor = fac;
|
||||
apply_effect_op(op, src1.image, src2.image, dst.image);
|
||||
/* Destination uses alpha from src1 */
|
||||
dst.is_opaque_before_transform = !src1.image->can_contain_alpha();
|
||||
return dst;
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/* Multiply Effect */
|
||||
|
||||
struct MulEffectOp {
|
||||
template<typename T> void apply(const T *src1, const T *src2, T *dst, int64_t size) const
|
||||
{
|
||||
const float fac = this->factor;
|
||||
int ifac = int(256.0f * fac);
|
||||
for (int64_t idx = 0; idx < size; idx++) {
|
||||
/* Formula: `fac * (a * b) + (1-fac) * a => fac * a * (b - 1) + a` */
|
||||
if constexpr (std::is_same_v<T, uchar>) {
|
||||
dst[0] = src1[0] + ((ifac * src1[0] * (src2[0] - 255)) >> 16);
|
||||
dst[1] = src1[1] + ((ifac * src1[1] * (src2[1] - 255)) >> 16);
|
||||
dst[2] = src1[2] + ((ifac * src1[2] * (src2[2] - 255)) >> 16);
|
||||
dst[3] = src1[3] + ((ifac * src1[3] * (src2[3] - 255)) >> 16);
|
||||
}
|
||||
else {
|
||||
dst[0] = src1[0] + fac * src1[0] * (src2[0] - 1.0f);
|
||||
dst[1] = src1[1] + fac * src1[1] * (src2[1] - 1.0f);
|
||||
dst[2] = src1[2] + fac * src1[2] * (src2[2] - 1.0f);
|
||||
dst[3] = src1[3] + fac * src1[3] * (src2[3] - 1.0f);
|
||||
}
|
||||
src1 += 4;
|
||||
src2 += 4;
|
||||
dst += 4;
|
||||
}
|
||||
}
|
||||
float factor;
|
||||
};
|
||||
|
||||
static SeqResult do_mul_effect(const RenderData *context,
|
||||
SeqRenderState * /*state*/,
|
||||
Strip * /*strip*/,
|
||||
float /*timeline_frame*/,
|
||||
float fac,
|
||||
const SeqResult &src1,
|
||||
const SeqResult &src2)
|
||||
{
|
||||
PRF_scope_with_name("SeqFxMul", ProfileCategory::Draw);
|
||||
SeqResult dst = prepare_effect_imbufs(context, src1, src2);
|
||||
MulEffectOp op;
|
||||
op.factor = fac;
|
||||
apply_effect_op(op, src1.image, src2.image, dst.image);
|
||||
return dst;
|
||||
}
|
||||
|
||||
void add_effect_get_handle(EffectHandle &rval)
|
||||
{
|
||||
rval.execute = do_add_effect;
|
||||
rval.early_out = early_out_mul_input2;
|
||||
}
|
||||
|
||||
void sub_effect_get_handle(EffectHandle &rval)
|
||||
{
|
||||
rval.execute = do_sub_effect;
|
||||
rval.early_out = early_out_mul_input2;
|
||||
}
|
||||
|
||||
void mul_effect_get_handle(EffectHandle &rval)
|
||||
{
|
||||
rval.execute = do_mul_effect;
|
||||
rval.early_out = early_out_mul_input2;
|
||||
}
|
||||
|
||||
} // namespace blender::seq
|
||||
@@ -0,0 +1,92 @@
|
||||
/* SPDX-FileCopyrightText: 2024 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup sequencer
|
||||
*/
|
||||
|
||||
#include "PRF_profile.hh"
|
||||
|
||||
#include "DNA_sequence_types.h"
|
||||
|
||||
#include "SEQ_channels.hh"
|
||||
#include "SEQ_render.hh"
|
||||
#include "SEQ_sequencer.hh"
|
||||
#include "SEQ_utils.hh"
|
||||
|
||||
#include "effects.hh"
|
||||
#include "render.hh"
|
||||
|
||||
namespace blender::seq {
|
||||
|
||||
static StripEarlyOut early_out_adjustment(const Strip * /*strip*/, float /*fac*/)
|
||||
{
|
||||
return StripEarlyOut::NoInput;
|
||||
}
|
||||
|
||||
static SeqResult do_adjustment_impl(const RenderData *context,
|
||||
SeqRenderState *state,
|
||||
Strip *strip,
|
||||
float timeline_frame)
|
||||
{
|
||||
SeqResult out;
|
||||
Editing *ed = context->scene->ed;
|
||||
|
||||
ListBaseT<Strip> *seqbasep = get_seqbase_by_strip(context->scene, strip);
|
||||
ListBaseT<SeqTimelineChannel> *channels = get_channels_by_strip(ed, strip);
|
||||
|
||||
/* Clamp timeline_frame to strip range so it behaves as if it had "still frame" offset (last
|
||||
* frame is static after end of strip). This is how most strips behave. This way transition
|
||||
* effects that doesn't overlap or speed effect can't fail rendering outside of strip range. */
|
||||
timeline_frame = clamp_i(
|
||||
timeline_frame, strip->left_handle(), strip->right_handle(context->scene) - 1);
|
||||
|
||||
if (strip->channel > 1) {
|
||||
out = seq_render_give_ibuf_seqbase(
|
||||
context, state, timeline_frame, strip->channel - 1, channels, seqbasep);
|
||||
}
|
||||
|
||||
/* Found nothing? Then work our way up the meta-strip stack, as this adjustment strip might be
|
||||
* inside a nested meta-strip and affect strips below that meta-strip.
|
||||
*
|
||||
* NOTE: we should NOT walk past the stack level that the user is currently tabbed into,
|
||||
* otherwise the adjustment layer can leak content from outside the meta context. */
|
||||
|
||||
if (!out.is_valid()) {
|
||||
Strip *meta = lookup_meta_by_strip(ed, strip);
|
||||
if (meta && meta != ed->current_meta_strip) {
|
||||
out = do_adjustment_impl(context, state, meta, timeline_frame);
|
||||
}
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
static SeqResult do_adjustment(const RenderData *context,
|
||||
SeqRenderState *state,
|
||||
Strip *strip,
|
||||
float timeline_frame,
|
||||
float /*fac*/,
|
||||
const SeqResult & /*ibuf1*/,
|
||||
const SeqResult & /*ibuf2*/)
|
||||
{
|
||||
PRF_scope_with_name("SeqFxAdjustment", ProfileCategory::Draw);
|
||||
Editing *ed = context->scene->ed;
|
||||
if (!ed || state->strips_in_progress.contains(strip)) {
|
||||
return {};
|
||||
}
|
||||
|
||||
state->strips_in_progress.add(strip);
|
||||
SeqResult out = do_adjustment_impl(context, state, strip, timeline_frame);
|
||||
state->strips_in_progress.remove(strip);
|
||||
return out;
|
||||
}
|
||||
|
||||
void adjustment_effect_get_handle(EffectHandle &rval)
|
||||
{
|
||||
rval.early_out = early_out_adjustment;
|
||||
rval.execute = do_adjustment;
|
||||
}
|
||||
|
||||
} // namespace blender::seq
|
||||
@@ -0,0 +1,415 @@
|
||||
/* 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 "PRF_profile.hh"
|
||||
|
||||
#include "SEQ_render.hh"
|
||||
|
||||
#include "effects.hh"
|
||||
|
||||
namespace blender::seq {
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/* Alpha Over Effect */
|
||||
|
||||
static void init_alpha_over_or_under(Strip *strip)
|
||||
{
|
||||
Strip *input1 = strip->input1;
|
||||
Strip *input2 = strip->input2;
|
||||
|
||||
strip->input2 = input1;
|
||||
strip->input1 = input2;
|
||||
}
|
||||
|
||||
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 (std::is_same_v<T, uchar> && src1[3] == 0) {
|
||||
/* Optimization for fully transparent pixels: copy src2. Only do this for byte images;
|
||||
* in floats alpha=0 can still have pure emissive color. */
|
||||
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 SeqResult do_alphaover_effect(const RenderData *context,
|
||||
SeqRenderState * /*state*/,
|
||||
Strip * /*strip*/,
|
||||
float /*timeline_frame*/,
|
||||
float fac,
|
||||
const SeqResult &src1,
|
||||
const SeqResult &src2)
|
||||
{
|
||||
PRF_scope_with_name("SeqFxOver", ProfileCategory::Draw);
|
||||
SeqResult dst = prepare_effect_imbufs(context, src1, src2);
|
||||
AlphaOverEffectOp op;
|
||||
op.factor = fac;
|
||||
apply_effect_op(op, src1.image, src2.image, dst.image);
|
||||
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 SeqResult do_alphaunder_effect(const RenderData *context,
|
||||
SeqRenderState * /*state*/,
|
||||
Strip * /*strip*/,
|
||||
float /*timeline_frame*/,
|
||||
float fac,
|
||||
const SeqResult &src1,
|
||||
const SeqResult &src2)
|
||||
{
|
||||
PRF_scope_with_name("SeqFxUnder", ProfileCategory::Draw);
|
||||
SeqResult dst = prepare_effect_imbufs(context, src1, src2);
|
||||
AlphaUnderEffectOp op;
|
||||
op.factor = fac;
|
||||
apply_effect_op(op, src1.image, src2.image, dst.image);
|
||||
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];
|
||||
(static_cast<T *>(const_cast<T *>(src2)))[3] = T(achannel * fac);
|
||||
blend_function(dst, src1, src2);
|
||||
(static_cast<T *>(const_cast<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,
|
||||
StripBlendMode btype,
|
||||
float *out)
|
||||
{
|
||||
switch (btype) {
|
||||
case STRIP_BLEND_ADD:
|
||||
apply_blend_function(fac, size, rect1, rect2, out, blend_color_add_float);
|
||||
break;
|
||||
case STRIP_BLEND_SUB:
|
||||
apply_blend_function(fac, size, rect1, rect2, out, blend_color_sub_float);
|
||||
break;
|
||||
case STRIP_BLEND_MUL:
|
||||
apply_blend_function(fac, size, rect1, rect2, out, blend_color_mul_float);
|
||||
break;
|
||||
case STRIP_BLEND_DARKEN:
|
||||
apply_blend_function(fac, size, rect1, rect2, out, blend_color_darken_float);
|
||||
break;
|
||||
case STRIP_BLEND_COLOR_BURN:
|
||||
apply_blend_function(fac, size, rect1, rect2, out, blend_color_burn_float);
|
||||
break;
|
||||
case STRIP_BLEND_LINEAR_BURN:
|
||||
apply_blend_function(fac, size, rect1, rect2, out, blend_color_linearburn_float);
|
||||
break;
|
||||
case STRIP_BLEND_SCREEN:
|
||||
apply_blend_function(fac, size, rect1, rect2, out, blend_color_screen_float);
|
||||
break;
|
||||
case STRIP_BLEND_LIGHTEN:
|
||||
apply_blend_function(fac, size, rect1, rect2, out, blend_color_lighten_float);
|
||||
break;
|
||||
case STRIP_BLEND_DODGE:
|
||||
apply_blend_function(fac, size, rect1, rect2, out, blend_color_dodge_float);
|
||||
break;
|
||||
case STRIP_BLEND_OVERLAY:
|
||||
apply_blend_function(fac, size, rect1, rect2, out, blend_color_overlay_float);
|
||||
break;
|
||||
case STRIP_BLEND_SOFT_LIGHT:
|
||||
apply_blend_function(fac, size, rect1, rect2, out, blend_color_softlight_float);
|
||||
break;
|
||||
case STRIP_BLEND_HARD_LIGHT:
|
||||
apply_blend_function(fac, size, rect1, rect2, out, blend_color_hardlight_float);
|
||||
break;
|
||||
case STRIP_BLEND_PIN_LIGHT:
|
||||
apply_blend_function(fac, size, rect1, rect2, out, blend_color_pinlight_float);
|
||||
break;
|
||||
case STRIP_BLEND_LIN_LIGHT:
|
||||
apply_blend_function(fac, size, rect1, rect2, out, blend_color_linearlight_float);
|
||||
break;
|
||||
case STRIP_BLEND_VIVID_LIGHT:
|
||||
apply_blend_function(fac, size, rect1, rect2, out, blend_color_vividlight_float);
|
||||
break;
|
||||
case STRIP_BLEND_BLEND_COLOR:
|
||||
apply_blend_function(fac, size, rect1, rect2, out, blend_color_color_float);
|
||||
break;
|
||||
case STRIP_BLEND_HUE:
|
||||
apply_blend_function(fac, size, rect1, rect2, out, blend_color_hue_float);
|
||||
break;
|
||||
case STRIP_BLEND_SATURATION:
|
||||
apply_blend_function(fac, size, rect1, rect2, out, blend_color_saturation_float);
|
||||
break;
|
||||
case STRIP_BLEND_VALUE:
|
||||
apply_blend_function(fac, size, rect1, rect2, out, blend_color_luminosity_float);
|
||||
break;
|
||||
case STRIP_BLEND_DIFFERENCE:
|
||||
apply_blend_function(fac, size, rect1, rect2, out, blend_color_difference_float);
|
||||
break;
|
||||
case STRIP_BLEND_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,
|
||||
StripBlendMode btype,
|
||||
uchar *out)
|
||||
{
|
||||
switch (btype) {
|
||||
case STRIP_BLEND_ADD:
|
||||
apply_blend_function(fac, size, rect1, rect2, out, blend_color_add_byte);
|
||||
break;
|
||||
case STRIP_BLEND_SUB:
|
||||
apply_blend_function(fac, size, rect1, rect2, out, blend_color_sub_byte);
|
||||
break;
|
||||
case STRIP_BLEND_MUL:
|
||||
apply_blend_function(fac, size, rect1, rect2, out, blend_color_mul_byte);
|
||||
break;
|
||||
case STRIP_BLEND_DARKEN:
|
||||
apply_blend_function(fac, size, rect1, rect2, out, blend_color_darken_byte);
|
||||
break;
|
||||
case STRIP_BLEND_COLOR_BURN:
|
||||
apply_blend_function(fac, size, rect1, rect2, out, blend_color_burn_byte);
|
||||
break;
|
||||
case STRIP_BLEND_LINEAR_BURN:
|
||||
apply_blend_function(fac, size, rect1, rect2, out, blend_color_linearburn_byte);
|
||||
break;
|
||||
case STRIP_BLEND_SCREEN:
|
||||
apply_blend_function(fac, size, rect1, rect2, out, blend_color_screen_byte);
|
||||
break;
|
||||
case STRIP_BLEND_LIGHTEN:
|
||||
apply_blend_function(fac, size, rect1, rect2, out, blend_color_lighten_byte);
|
||||
break;
|
||||
case STRIP_BLEND_DODGE:
|
||||
apply_blend_function(fac, size, rect1, rect2, out, blend_color_dodge_byte);
|
||||
break;
|
||||
case STRIP_BLEND_OVERLAY:
|
||||
apply_blend_function(fac, size, rect1, rect2, out, blend_color_overlay_byte);
|
||||
break;
|
||||
case STRIP_BLEND_SOFT_LIGHT:
|
||||
apply_blend_function(fac, size, rect1, rect2, out, blend_color_softlight_byte);
|
||||
break;
|
||||
case STRIP_BLEND_HARD_LIGHT:
|
||||
apply_blend_function(fac, size, rect1, rect2, out, blend_color_hardlight_byte);
|
||||
break;
|
||||
case STRIP_BLEND_PIN_LIGHT:
|
||||
apply_blend_function(fac, size, rect1, rect2, out, blend_color_pinlight_byte);
|
||||
break;
|
||||
case STRIP_BLEND_LIN_LIGHT:
|
||||
apply_blend_function(fac, size, rect1, rect2, out, blend_color_linearlight_byte);
|
||||
break;
|
||||
case STRIP_BLEND_VIVID_LIGHT:
|
||||
apply_blend_function(fac, size, rect1, rect2, out, blend_color_vividlight_byte);
|
||||
break;
|
||||
case STRIP_BLEND_BLEND_COLOR:
|
||||
apply_blend_function(fac, size, rect1, rect2, out, blend_color_color_byte);
|
||||
break;
|
||||
case STRIP_BLEND_HUE:
|
||||
apply_blend_function(fac, size, rect1, rect2, out, blend_color_hue_byte);
|
||||
break;
|
||||
case STRIP_BLEND_SATURATION:
|
||||
apply_blend_function(fac, size, rect1, rect2, out, blend_color_saturation_byte);
|
||||
break;
|
||||
case STRIP_BLEND_VALUE:
|
||||
apply_blend_function(fac, size, rect1, rect2, out, blend_color_luminosity_byte);
|
||||
break;
|
||||
case STRIP_BLEND_DIFFERENCE:
|
||||
apply_blend_function(fac, size, rect1, rect2, out, blend_color_difference_byte);
|
||||
break;
|
||||
case STRIP_BLEND_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);
|
||||
}
|
||||
}
|
||||
StripBlendMode blend_mode;
|
||||
float factor;
|
||||
};
|
||||
|
||||
static SeqResult do_blend_mode_effect(const RenderData *context,
|
||||
SeqRenderState * /*state*/,
|
||||
Strip *strip,
|
||||
float /*timeline_frame*/,
|
||||
float fac,
|
||||
const SeqResult &src1,
|
||||
const SeqResult &src2)
|
||||
{
|
||||
PRF_scope_with_name("SeqFxBlend", ProfileCategory::Draw);
|
||||
SeqResult dst = prepare_effect_imbufs(context, src1, src2);
|
||||
BlendModeEffectOp op;
|
||||
op.factor = fac;
|
||||
op.blend_mode = strip->blend_mode;
|
||||
apply_effect_op(op, src1.image, src2.image, dst.image);
|
||||
return dst;
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/* Color Mix Effect */
|
||||
|
||||
static void init_colormix_effect(Strip *strip)
|
||||
{
|
||||
ColorMixVars *data = MEM_new<ColorMixVars>("colormixvars");
|
||||
strip->effectdata = data;
|
||||
data->blend_effect = STRIP_BLEND_OVERLAY;
|
||||
data->factor = 1.0f;
|
||||
}
|
||||
|
||||
static void free_colormix_effect(Strip *strip, const bool /*do_id_user*/)
|
||||
{
|
||||
if (strip->effectdata) {
|
||||
MEM_delete(static_cast<ColorMixVars *>(strip->effectdata));
|
||||
strip->effectdata = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
static SeqResult do_colormix_effect(const RenderData *context,
|
||||
SeqRenderState * /*state*/,
|
||||
Strip *strip,
|
||||
float /*timeline_frame*/,
|
||||
float /*fac*/,
|
||||
const SeqResult &src1,
|
||||
const SeqResult &src2)
|
||||
{
|
||||
PRF_scope_with_name("SeqFxColorMix", ProfileCategory::Draw);
|
||||
SeqResult 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.image, src2.image, dst.image);
|
||||
return dst;
|
||||
}
|
||||
|
||||
void blend_mode_effect_get_handle(EffectHandle &rval)
|
||||
{
|
||||
rval.execute = do_blend_mode_effect;
|
||||
rval.early_out = early_out_mul_input2;
|
||||
}
|
||||
|
||||
void color_mix_effect_get_handle(EffectHandle &rval)
|
||||
{
|
||||
rval.init = init_colormix_effect;
|
||||
rval.free = free_colormix_effect;
|
||||
rval.execute = do_colormix_effect;
|
||||
rval.early_out = early_out_mul_input2;
|
||||
}
|
||||
|
||||
void alpha_over_effect_get_handle(EffectHandle &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(EffectHandle &rval)
|
||||
{
|
||||
rval.init = init_alpha_over_or_under;
|
||||
rval.execute = do_alphaunder_effect;
|
||||
}
|
||||
|
||||
} // namespace blender::seq
|
||||
@@ -0,0 +1,204 @@
|
||||
/* SPDX-FileCopyrightText: 2026 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup sequencer
|
||||
*/
|
||||
|
||||
#include "BKE_node_runtime.hh"
|
||||
|
||||
#include "COM_domain.hh"
|
||||
|
||||
#include "DEG_depsgraph_query.hh"
|
||||
|
||||
#include "DNA_sequence_types.h"
|
||||
|
||||
#include "IMB_colormanagement.hh"
|
||||
#include "IMB_imbuf.hh"
|
||||
|
||||
#include "PRF_profile.hh"
|
||||
|
||||
#include "SEQ_sequencer.hh"
|
||||
|
||||
#include "cache/compositor_cache.hh"
|
||||
#include "compositor.hh"
|
||||
#include "effects.hh"
|
||||
|
||||
namespace blender::seq {
|
||||
|
||||
class CompositorEffectContext : public CompositorContext {
|
||||
bNodeTree *node_group_;
|
||||
|
||||
ImBuf *input_1_;
|
||||
ImBuf *input_2_;
|
||||
ImBuf *output_;
|
||||
float factor_;
|
||||
|
||||
public:
|
||||
CompositorEffectContext(compositor::StaticCacheManager &cache_manager,
|
||||
const RenderData &render_data,
|
||||
bNodeTree *node_tree,
|
||||
ImBuf *input_1,
|
||||
ImBuf *input_2,
|
||||
ImBuf *output,
|
||||
float factor,
|
||||
const Strip &strip)
|
||||
: CompositorContext(cache_manager, render_data, strip),
|
||||
node_group_(node_tree),
|
||||
input_1_(input_1),
|
||||
input_2_(input_2),
|
||||
output_(output),
|
||||
factor_(factor)
|
||||
{
|
||||
}
|
||||
|
||||
compositor::Domain get_compositing_domain() const override
|
||||
{
|
||||
return compositor::Domain(int2(this->output_->x, this->output_->y));
|
||||
}
|
||||
|
||||
void write_viewer(compositor::Result &viewer_result) override
|
||||
{
|
||||
write_viewer_impl(viewer_result, *this->output_);
|
||||
}
|
||||
|
||||
void evaluate()
|
||||
{
|
||||
using namespace compositor;
|
||||
const bNodeTree &node_group = *DEG_get_evaluated<bNodeTree>(render_data_.depsgraph,
|
||||
node_group_);
|
||||
const bke::DataBlockComputeContext compute_context(nullptr, this->get_scene().id);
|
||||
NodeGroupOperation node_group_operation(*this,
|
||||
node_group,
|
||||
this->needed_outputs(),
|
||||
node_group.active_viewer_key,
|
||||
bke::NODE_INSTANCE_KEY_BASE,
|
||||
compute_context);
|
||||
set_output_refcount(node_group, node_group_operation);
|
||||
|
||||
/* Map the inputs to the operation. */
|
||||
Vector<std::unique_ptr<Result>> inputs;
|
||||
int float_counter = 0;
|
||||
int color_counter = 0;
|
||||
for (const bNodeTreeInterfaceSocket *input_socket : node_group.interface_inputs()) {
|
||||
const bke::bNodeSocketType *typeinfo = input_socket->socket_typeinfo();
|
||||
Result *input_result = nullptr;
|
||||
if (typeinfo && typeinfo->type == SOCK_FLOAT && float_counter == 0) {
|
||||
/* First float input is factor. */
|
||||
input_result = new Result(this->create_result(ResultType::Float, ResultPrecision::Full));
|
||||
input_result->allocate_single_value();
|
||||
input_result->set_single_value(this->factor_);
|
||||
float_counter++;
|
||||
}
|
||||
else if (color_counter == 0 && this->input_1_) {
|
||||
/* First input image. */
|
||||
input_result = new Result(this->create_result(ResultType::Color, ResultPrecision::Full));
|
||||
create_result_from_input(*input_result, *this->input_1_);
|
||||
color_counter++;
|
||||
}
|
||||
else if (color_counter == 1 && this->input_2_) {
|
||||
/* Second input image. */
|
||||
input_result = new Result(this->create_result(ResultType::Color, ResultPrecision::Full));
|
||||
create_result_from_input(*input_result, *this->input_2_);
|
||||
color_counter++;
|
||||
}
|
||||
else {
|
||||
/* Unsupported sockets. */
|
||||
input_result = new Result(this->create_result(ResultType::Color, ResultPrecision::Full));
|
||||
input_result->allocate_invalid();
|
||||
}
|
||||
|
||||
node_group_operation.map_input_to_result(input_socket->identifier, input_result);
|
||||
inputs.append(std::unique_ptr<Result>(input_result));
|
||||
}
|
||||
|
||||
node_group_operation.evaluate();
|
||||
this->write_outputs(node_group, node_group_operation, *this->output_);
|
||||
}
|
||||
};
|
||||
|
||||
static SeqResult do_compositor_effect(const RenderData *context,
|
||||
SeqRenderState * /*state*/,
|
||||
Strip *strip,
|
||||
float /*timeline_frame*/,
|
||||
float fac,
|
||||
const SeqResult &src1,
|
||||
const SeqResult &src2)
|
||||
{
|
||||
PRF_scope_with_name("SeqFxCompositor", ProfileCategory::Draw);
|
||||
const int x = context->rectx;
|
||||
const int y = context->recty;
|
||||
SeqResult out;
|
||||
out.image = IMB_allocImBuf(x, y, ImBufFlags::FloatData | ImBufFlags::UninitializedPixels);
|
||||
IMB_colormanagement_assign_float_colorspace(
|
||||
out.image, IMB_colormanagement_role_colorspace_name_get(COLOR_ROLE_SCENE_LINEAR));
|
||||
|
||||
CompositorEffectVars *data = static_cast<CompositorEffectVars *>(strip->effectdata);
|
||||
if (!data || !data->node_group) {
|
||||
IMB_rectfill(out.image, float4(0, 0, 0, 1));
|
||||
out.image->color_mode = ImColorMode::RGB;
|
||||
out.is_opaque_before_transform = true;
|
||||
}
|
||||
else {
|
||||
CompositorCache &com_cache = context->scene->ed->runtime->ensure_compositor_cache();
|
||||
CompositorEffectContext com_context(com_cache.get_cache_manager(),
|
||||
*context,
|
||||
data->node_group,
|
||||
src1.image,
|
||||
src2.image,
|
||||
out.image,
|
||||
fac,
|
||||
*strip);
|
||||
|
||||
if (com_context.use_gpu()) {
|
||||
com_context.set_gpu_supported(render_begin_gpu(*context));
|
||||
}
|
||||
com_cache.recreate_if_needed(
|
||||
com_context.use_gpu(), com_context.get_precision(), context->gpu_context);
|
||||
com_context.evaluate();
|
||||
com_context.cache_manager().reset();
|
||||
if (com_context.use_gpu()) {
|
||||
render_end_gpu(*context);
|
||||
}
|
||||
out.translation += com_context.get_result_translation();
|
||||
out.is_opaque_before_transform = !out.image->can_contain_alpha();
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
static void init_compositor_effect(Strip *strip)
|
||||
{
|
||||
CompositorEffectVars *data = MEM_new<CompositorEffectVars>(__func__);
|
||||
strip->effectdata = data;
|
||||
}
|
||||
|
||||
static void free_compositor_effect(Strip *strip, const bool /*do_id_user*/)
|
||||
{
|
||||
if (strip->effectdata) {
|
||||
CompositorEffectVars *data = static_cast<CompositorEffectVars *>(strip->effectdata);
|
||||
MEM_delete(data);
|
||||
strip->effectdata = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
static StripEarlyOut early_out_compositor(const Strip *strip, float /*fac*/)
|
||||
{
|
||||
/* No inputs: compositor generates the result. */
|
||||
if (strip->input1 == nullptr) {
|
||||
return StripEarlyOut::NoInput;
|
||||
}
|
||||
|
||||
/* One or two inputs: do the effect. */
|
||||
return StripEarlyOut::DoEffect;
|
||||
}
|
||||
|
||||
void compositor_effect_get_handle(EffectHandle &rval)
|
||||
{
|
||||
rval.init = init_compositor_effect;
|
||||
rval.free = free_compositor_effect;
|
||||
rval.execute = do_compositor_effect;
|
||||
rval.early_out = early_out_compositor;
|
||||
}
|
||||
|
||||
} // namespace blender::seq
|
||||
@@ -0,0 +1,136 @@
|
||||
/* SPDX-FileCopyrightText: 2024 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup sequencer
|
||||
*/
|
||||
|
||||
#include "DNA_sequence_types.h"
|
||||
|
||||
#include "IMB_imbuf.hh"
|
||||
|
||||
#include "PRF_profile.hh"
|
||||
|
||||
#include "SEQ_render.hh"
|
||||
|
||||
#include "effects.hh"
|
||||
|
||||
namespace blender::seq {
|
||||
|
||||
struct CrossEffectOp {
|
||||
template<typename T> void apply(const T *src1, const T *src2, T *dst, int64_t size) const
|
||||
{
|
||||
const float fac = this->factor;
|
||||
const float mfac = 1.0f - fac;
|
||||
const int ifac = int(256.0f * fac);
|
||||
const int imfac = 256 - ifac;
|
||||
for (int64_t idx = 0; idx < size; idx++) {
|
||||
if constexpr (std::is_same_v<T, uchar>) {
|
||||
dst[0] = (imfac * src1[0] + ifac * src2[0]) >> 8;
|
||||
dst[1] = (imfac * src1[1] + ifac * src2[1]) >> 8;
|
||||
dst[2] = (imfac * src1[2] + ifac * src2[2]) >> 8;
|
||||
dst[3] = (imfac * src1[3] + ifac * src2[3]) >> 8;
|
||||
}
|
||||
else {
|
||||
dst[0] = mfac * src1[0] + fac * src2[0];
|
||||
dst[1] = mfac * src1[1] + fac * src2[1];
|
||||
dst[2] = mfac * src1[2] + fac * src2[2];
|
||||
dst[3] = mfac * src1[3] + fac * src2[3];
|
||||
}
|
||||
src1 += 4;
|
||||
src2 += 4;
|
||||
dst += 4;
|
||||
}
|
||||
}
|
||||
float factor;
|
||||
};
|
||||
|
||||
static SeqResult do_cross_effect(const RenderData *context,
|
||||
SeqRenderState * /*state*/,
|
||||
Strip * /*strip*/,
|
||||
float /*timeline_frame*/,
|
||||
float fac,
|
||||
const SeqResult &src1,
|
||||
const SeqResult &src2)
|
||||
{
|
||||
PRF_scope_with_name("SeqFxCross", ProfileCategory::Draw);
|
||||
SeqResult dst = prepare_effect_imbufs(context, src1, src2);
|
||||
CrossEffectOp op;
|
||||
op.factor = fac;
|
||||
apply_effect_op(op, src1.image, src2.image, dst.image);
|
||||
dst.is_opaque_before_transform = !src1.image->can_contain_alpha() &&
|
||||
!src2.image->can_contain_alpha();
|
||||
return dst;
|
||||
}
|
||||
|
||||
/* One could argue that gamma cross should not be hardcoded to 2.0 gamma,
|
||||
* but instead either do proper input->linear conversion (often sRGB). Or
|
||||
* maybe not even that, but do interpolation in some perceptual color space
|
||||
* like OKLAB. But currently it is fixed to just 2.0 gamma. */
|
||||
|
||||
static float gammaCorrect(float c)
|
||||
{
|
||||
if (UNLIKELY(c < 0)) {
|
||||
return -(c * c);
|
||||
}
|
||||
return c * c;
|
||||
}
|
||||
|
||||
static float invGammaCorrect(float c)
|
||||
{
|
||||
return sqrtf_signed(c);
|
||||
}
|
||||
|
||||
struct GammaCrossEffectOp {
|
||||
template<typename T> void apply(const T *src1, const T *src2, T *dst, int64_t size) const
|
||||
{
|
||||
const float fac = this->factor;
|
||||
const float mfac = 1.0f - fac;
|
||||
for (int64_t idx = 0; idx < size; idx++) {
|
||||
float4 col1 = load_premul_pixel(src1);
|
||||
float4 col2 = load_premul_pixel(src2);
|
||||
float4 col;
|
||||
for (int c = 0; c < 4; ++c) {
|
||||
col[c] = gammaCorrect(mfac * invGammaCorrect(col1[c]) + fac * invGammaCorrect(col2[c]));
|
||||
}
|
||||
store_premul_pixel(col, dst);
|
||||
src1 += 4;
|
||||
src2 += 4;
|
||||
dst += 4;
|
||||
}
|
||||
}
|
||||
float factor;
|
||||
};
|
||||
|
||||
static SeqResult do_gammacross_effect(const RenderData *context,
|
||||
SeqRenderState * /*state*/,
|
||||
Strip * /*strip*/,
|
||||
float /*timeline_frame*/,
|
||||
float fac,
|
||||
const SeqResult &src1,
|
||||
const SeqResult &src2)
|
||||
{
|
||||
PRF_scope_with_name("SeqFxGammaCross", ProfileCategory::Draw);
|
||||
SeqResult dst = prepare_effect_imbufs(context, src1, src2);
|
||||
GammaCrossEffectOp op;
|
||||
op.factor = fac;
|
||||
apply_effect_op(op, src1.image, src2.image, dst.image);
|
||||
dst.is_opaque_before_transform = !src1.image->can_contain_alpha() &&
|
||||
!src2.image->can_contain_alpha();
|
||||
return dst;
|
||||
}
|
||||
|
||||
void cross_effect_get_handle(EffectHandle &rval)
|
||||
{
|
||||
rval.execute = do_cross_effect;
|
||||
rval.early_out = early_out_fade;
|
||||
}
|
||||
|
||||
void gamma_cross_effect_get_handle(EffectHandle &rval)
|
||||
{
|
||||
rval.early_out = early_out_fade;
|
||||
rval.execute = do_gammacross_effect;
|
||||
}
|
||||
|
||||
} // namespace blender::seq
|
||||
@@ -0,0 +1,227 @@
|
||||
/* SPDX-FileCopyrightText: 2024 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup sequencer
|
||||
*/
|
||||
|
||||
#include "BLI_math_base.hh"
|
||||
#include "BLI_task.hh"
|
||||
|
||||
#include "DNA_sequence_types.h"
|
||||
|
||||
#include "IMB_imbuf.hh"
|
||||
|
||||
#include "PRF_profile.hh"
|
||||
|
||||
#include "SEQ_render.hh"
|
||||
|
||||
#include "effects.hh"
|
||||
|
||||
namespace blender::seq {
|
||||
|
||||
static void init_gaussian_blur_effect(Strip *strip)
|
||||
{
|
||||
GaussianBlurVars *data = MEM_new<GaussianBlurVars>("gaussianblurvars");
|
||||
strip->effectdata = data;
|
||||
data->size_x = 9.0f;
|
||||
data->size_y = 9.0f;
|
||||
}
|
||||
|
||||
static void free_gaussian_blur_effect(Strip *strip, const bool /*do_id_user*/)
|
||||
{
|
||||
if (strip->effectdata) {
|
||||
GaussianBlurVars *data = static_cast<GaussianBlurVars *>(strip->effectdata);
|
||||
MEM_delete(data);
|
||||
strip->effectdata = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
static StripEarlyOut early_out_gaussian_blur(const Strip *strip, float /*fac*/)
|
||||
{
|
||||
GaussianBlurVars *data = static_cast<GaussianBlurVars *>(strip->effectdata);
|
||||
if (data->size_x == 0.0f && data->size_y == 0) {
|
||||
return StripEarlyOut::UseInput1;
|
||||
}
|
||||
return StripEarlyOut::DoEffect;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static void gaussian_blur_x(const Span<float> gaussian,
|
||||
int half_size,
|
||||
int start_line,
|
||||
int width,
|
||||
int height,
|
||||
int /*frame_height*/,
|
||||
const T *rect,
|
||||
T *dst)
|
||||
{
|
||||
dst += int64_t(start_line) * width * 4;
|
||||
for (int y = start_line; y < start_line + height; y++) {
|
||||
for (int x = 0; x < width; x++) {
|
||||
float4 accum(0.0f);
|
||||
float accum_weight = 0.0f;
|
||||
|
||||
int xmin = math::max(x - half_size, 0);
|
||||
int xmax = math::min(x + half_size, width - 1);
|
||||
for (int nx = xmin, index = (xmin - x) + half_size; nx <= xmax; nx++, index++) {
|
||||
float weight = gaussian[index];
|
||||
int offset = (y * width + nx) * 4;
|
||||
accum += float4(rect + offset) * weight;
|
||||
accum_weight += weight;
|
||||
}
|
||||
accum *= (1.0f / accum_weight);
|
||||
if constexpr (math::is_math_float_type<T>) {
|
||||
dst[0] = accum[0];
|
||||
dst[1] = accum[1];
|
||||
dst[2] = accum[2];
|
||||
dst[3] = accum[3];
|
||||
}
|
||||
else {
|
||||
dst[0] = accum[0] + 0.5f;
|
||||
dst[1] = accum[1] + 0.5f;
|
||||
dst[2] = accum[2] + 0.5f;
|
||||
dst[3] = accum[3] + 0.5f;
|
||||
}
|
||||
dst += 4;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static void gaussian_blur_y(const Span<float> gaussian,
|
||||
int half_size,
|
||||
int start_line,
|
||||
int width,
|
||||
int height,
|
||||
int frame_height,
|
||||
const T *rect,
|
||||
T *dst)
|
||||
{
|
||||
dst += int64_t(start_line) * width * 4;
|
||||
for (int y = start_line; y < start_line + height; y++) {
|
||||
for (int x = 0; x < width; x++) {
|
||||
float4 accum(0.0f);
|
||||
float accum_weight = 0.0f;
|
||||
int ymin = math::max(y - half_size, 0);
|
||||
int ymax = math::min(y + half_size, frame_height - 1);
|
||||
for (int ny = ymin, index = (ymin - y) + half_size; ny <= ymax; ny++, index++) {
|
||||
float weight = gaussian[index];
|
||||
int offset = (ny * width + x) * 4;
|
||||
accum += float4(rect + offset) * weight;
|
||||
accum_weight += weight;
|
||||
}
|
||||
accum *= (1.0f / accum_weight);
|
||||
if constexpr (math::is_math_float_type<T>) {
|
||||
dst[0] = accum[0];
|
||||
dst[1] = accum[1];
|
||||
dst[2] = accum[2];
|
||||
dst[3] = accum[3];
|
||||
}
|
||||
else {
|
||||
dst[0] = accum[0] + 0.5f;
|
||||
dst[1] = accum[1] + 0.5f;
|
||||
dst[2] = accum[2] + 0.5f;
|
||||
dst[3] = accum[3] + 0.5f;
|
||||
}
|
||||
dst += 4;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static SeqResult do_gaussian_blur_effect(const RenderData *context,
|
||||
SeqRenderState * /*state*/,
|
||||
Strip *strip,
|
||||
float /*timeline_frame*/,
|
||||
float /*fac*/,
|
||||
const SeqResult &ibuf1,
|
||||
const SeqResult & /*ibuf2*/)
|
||||
{
|
||||
PRF_scope_with_name("SeqFxBlur", ProfileCategory::Draw);
|
||||
/* Create blur kernel weights. */
|
||||
const GaussianBlurVars *data = static_cast<const GaussianBlurVars *>(strip->effectdata);
|
||||
|
||||
const float size_scale = seq::get_render_scale_factor(*context);
|
||||
const float size_x = data->size_x * size_scale;
|
||||
const float size_y = data->size_y * size_scale;
|
||||
|
||||
const int half_size_x = int(size_x + 0.5f);
|
||||
const int half_size_y = int(size_y + 0.5f);
|
||||
Array<float> gaussian_x = make_gaussian_blur_kernel(size_x, half_size_x);
|
||||
Array<float> gaussian_y = make_gaussian_blur_kernel(size_y, half_size_y);
|
||||
|
||||
const int width = context->rectx;
|
||||
const int height = context->recty;
|
||||
const bool is_float = ibuf1.image->float_data();
|
||||
|
||||
/* Horizontal blur: create output, blur ibuf1 into it. */
|
||||
SeqResult out = prepare_effect_imbufs(context, ibuf1, {});
|
||||
threading::parallel_for(IndexRange(context->recty), 32, [&](const IndexRange y_range) {
|
||||
const int y_first = y_range.first();
|
||||
const int y_size = y_range.size();
|
||||
if (is_float) {
|
||||
gaussian_blur_x(gaussian_x,
|
||||
half_size_x,
|
||||
y_first,
|
||||
width,
|
||||
y_size,
|
||||
height,
|
||||
ibuf1.image->float_data(),
|
||||
out.image->float_data_for_write());
|
||||
}
|
||||
else {
|
||||
gaussian_blur_x(gaussian_x,
|
||||
half_size_x,
|
||||
y_first,
|
||||
width,
|
||||
y_size,
|
||||
height,
|
||||
ibuf1.image->byte_data(),
|
||||
out.image->byte_data_for_write());
|
||||
}
|
||||
});
|
||||
|
||||
/* Vertical blur: create output, blur previous output into it. */
|
||||
SeqResult vin = out;
|
||||
out = prepare_effect_imbufs(context, vin, {});
|
||||
threading::parallel_for(IndexRange(context->recty), 32, [&](const IndexRange y_range) {
|
||||
const int y_first = y_range.first();
|
||||
const int y_size = y_range.size();
|
||||
if (is_float) {
|
||||
gaussian_blur_y(gaussian_y,
|
||||
half_size_y,
|
||||
y_first,
|
||||
width,
|
||||
y_size,
|
||||
height,
|
||||
vin.image->float_data(),
|
||||
out.image->float_data_for_write());
|
||||
}
|
||||
else {
|
||||
gaussian_blur_y(gaussian_y,
|
||||
half_size_y,
|
||||
y_first,
|
||||
width,
|
||||
y_size,
|
||||
height,
|
||||
vin.image->byte_data(),
|
||||
out.image->byte_data_for_write());
|
||||
}
|
||||
});
|
||||
|
||||
/* Free the first output. */
|
||||
IMB_freeImBuf(vin.image);
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
void gaussian_blur_effect_get_handle(EffectHandle &rval)
|
||||
{
|
||||
rval.init = init_gaussian_blur_effect;
|
||||
rval.free = free_gaussian_blur_effect;
|
||||
rval.early_out = early_out_gaussian_blur;
|
||||
rval.execute = do_gaussian_blur_effect;
|
||||
}
|
||||
|
||||
} // namespace blender::seq
|
||||
@@ -0,0 +1,246 @@
|
||||
/* SPDX-FileCopyrightText: 2024 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup sequencer
|
||||
*/
|
||||
|
||||
#include "BLI_math_vector.hh"
|
||||
#include "BLI_task.hh"
|
||||
|
||||
#include "DNA_scene_types.h"
|
||||
#include "DNA_sequence_types.h"
|
||||
|
||||
#include "IMB_colormanagement.hh"
|
||||
#include "IMB_imbuf.hh"
|
||||
|
||||
#include "PRF_profile.hh"
|
||||
|
||||
#include "SEQ_render.hh"
|
||||
|
||||
#include "effects.hh"
|
||||
|
||||
namespace blender::seq {
|
||||
|
||||
static void glow_blur_bitmap(
|
||||
const float4 *src, float4 *map, int width, int height, float blur, int quality)
|
||||
{
|
||||
/* If we're not really blurring, bail out */
|
||||
if (blur <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* If result would be no blurring, early out. */
|
||||
const int halfWidth = ((quality + 1) * blur);
|
||||
if (halfWidth == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
Array<float4> temp(width * height);
|
||||
|
||||
/* Initialize the gaussian filter.
|
||||
* TODO: use code from #filter_kernel_value. */
|
||||
Array<float> filter(halfWidth * 2);
|
||||
const float k = -1.0f / (2.0f * float(M_PI) * blur * blur);
|
||||
float weight = 0;
|
||||
for (int ix = 0; ix < halfWidth; ix++) {
|
||||
weight = exp(k * (ix * ix));
|
||||
filter[halfWidth - ix] = weight;
|
||||
filter[halfWidth + ix] = weight;
|
||||
}
|
||||
filter[0] = weight;
|
||||
/* Normalize the array */
|
||||
float fval = 0;
|
||||
for (int ix = 0; ix < halfWidth * 2; ix++) {
|
||||
fval += filter[ix];
|
||||
}
|
||||
for (int ix = 0; ix < halfWidth * 2; ix++) {
|
||||
filter[ix] /= fval;
|
||||
}
|
||||
|
||||
/* Blur the rows: read map, write temp */
|
||||
threading::parallel_for(IndexRange(height), 32, [&](const IndexRange y_range) {
|
||||
for (const int y : y_range) {
|
||||
for (int x = 0; x < width; x++) {
|
||||
float4 curColor = float4(0.0f);
|
||||
int xmin = math::max(x - halfWidth, 0);
|
||||
int xmax = math::min(x + halfWidth, width);
|
||||
for (int nx = xmin, index = (xmin - x) + halfWidth; nx < xmax; nx++, index++) {
|
||||
curColor += map[nx + y * width] * filter[index];
|
||||
}
|
||||
temp[x + y * width] = curColor;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
/* Blur the columns: read temp, write map */
|
||||
threading::parallel_for(IndexRange(width), 32, [&](const IndexRange x_range) {
|
||||
const float4 one = float4(1.0f);
|
||||
for (const int x : x_range) {
|
||||
for (int y = 0; y < height; y++) {
|
||||
float4 curColor = float4(0.0f);
|
||||
int ymin = math::max(y - halfWidth, 0);
|
||||
int ymax = math::min(y + halfWidth, height);
|
||||
for (int ny = ymin, index = (ymin - y) + halfWidth; ny < ymax; ny++, index++) {
|
||||
curColor += temp[x + ny * width] * filter[index];
|
||||
}
|
||||
if (src != nullptr) {
|
||||
curColor = math::min(one, src[x + y * width] + curColor);
|
||||
}
|
||||
map[x + y * width] = curColor;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
static void blur_isolate_highlights(const float4 *in,
|
||||
float4 *out,
|
||||
int width,
|
||||
int height,
|
||||
float threshold,
|
||||
float boost,
|
||||
float clamp)
|
||||
{
|
||||
threading::parallel_for(IndexRange(height), 64, [&](const IndexRange y_range) {
|
||||
const float4 clampv = float4(clamp);
|
||||
for (const int y : y_range) {
|
||||
int index = y * width;
|
||||
for (int x = 0; x < width; x++, index++) {
|
||||
|
||||
/* Isolate the intensity */
|
||||
float intensity = (in[index].x + in[index].y + in[index].z - threshold);
|
||||
float4 val;
|
||||
if (intensity > 0) {
|
||||
val = math::min(clampv, in[index] * (boost * intensity));
|
||||
}
|
||||
else {
|
||||
val = float4(0.0f);
|
||||
}
|
||||
out[index] = val;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
static void init_glow_effect(Strip *strip)
|
||||
{
|
||||
GlowVars *data = MEM_new<GlowVars>("glowvars");
|
||||
strip->effectdata = data;
|
||||
data->fMini = 0.25f;
|
||||
data->fClamp = 1.0f;
|
||||
data->fBoost = 0.5f;
|
||||
data->dDist = 3.0f;
|
||||
data->dQuality = 3;
|
||||
data->bNoComp = 0;
|
||||
}
|
||||
|
||||
static void free_glow_effect(Strip *strip, const bool /*do_id_user*/)
|
||||
{
|
||||
if (strip->effectdata) {
|
||||
GlowVars *data = static_cast<GlowVars *>(strip->effectdata);
|
||||
MEM_delete(data);
|
||||
strip->effectdata = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
static void do_glow_effect_byte(Strip *strip,
|
||||
int render_size,
|
||||
float fac,
|
||||
int x,
|
||||
int y,
|
||||
const uchar *rect1,
|
||||
const uchar * /*rect2*/,
|
||||
uchar *out)
|
||||
{
|
||||
GlowVars *glow = static_cast<GlowVars *>(strip->effectdata);
|
||||
|
||||
Array<float4> inbuf(x * y);
|
||||
Array<float4> outbuf(x * y);
|
||||
|
||||
IMB_colormanagement_transform_byte_to_float(*inbuf.data(), rect1, x, y, 4, "sRGB", "sRGB");
|
||||
|
||||
blur_isolate_highlights(
|
||||
inbuf.data(), outbuf.data(), x, y, glow->fMini * 3.0f, glow->fBoost * fac, glow->fClamp);
|
||||
glow_blur_bitmap(glow->bNoComp ? nullptr : inbuf.data(),
|
||||
outbuf.data(),
|
||||
x,
|
||||
y,
|
||||
glow->dDist * (render_size / 100.0f),
|
||||
glow->dQuality);
|
||||
|
||||
threading::parallel_for(IndexRange(y), 64, [&](const IndexRange y_range) {
|
||||
size_t offset = y_range.first() * x;
|
||||
IMB_buffer_byte_from_float(
|
||||
out + offset * 4, *(outbuf.data() + offset), 4, 0.0f, true, x, y_range.size(), x);
|
||||
});
|
||||
}
|
||||
|
||||
static void do_glow_effect_float(Strip *strip,
|
||||
int render_size,
|
||||
float fac,
|
||||
int x,
|
||||
int y,
|
||||
const float *rect1,
|
||||
const float * /*rect2*/,
|
||||
float *out)
|
||||
{
|
||||
float4 *outbuf = reinterpret_cast<float4 *>(out);
|
||||
const float4 *inbuf = reinterpret_cast<const float4 *>(rect1);
|
||||
GlowVars *glow = static_cast<GlowVars *>(strip->effectdata);
|
||||
|
||||
blur_isolate_highlights(
|
||||
inbuf, outbuf, x, y, glow->fMini * 3.0f, glow->fBoost * fac, glow->fClamp);
|
||||
glow_blur_bitmap(glow->bNoComp ? nullptr : inbuf,
|
||||
outbuf,
|
||||
x,
|
||||
y,
|
||||
glow->dDist * (render_size / 100.0f),
|
||||
glow->dQuality);
|
||||
}
|
||||
|
||||
static SeqResult do_glow_effect(const RenderData *context,
|
||||
SeqRenderState * /*state*/,
|
||||
Strip *strip,
|
||||
float /*timeline_frame*/,
|
||||
float fac,
|
||||
const SeqResult &ibuf1,
|
||||
const SeqResult & /*ibuf2*/)
|
||||
{
|
||||
PRF_scope_with_name("SeqFxGlow", ProfileCategory::Draw);
|
||||
SeqResult out = prepare_effect_imbufs(context, ibuf1, {});
|
||||
|
||||
int render_size = 100 * context->rectx / context->scene->r.xsch;
|
||||
|
||||
if (out.image->float_data()) {
|
||||
do_glow_effect_float(strip,
|
||||
render_size,
|
||||
fac,
|
||||
context->rectx,
|
||||
context->recty,
|
||||
ibuf1.image->float_data(),
|
||||
nullptr,
|
||||
out.image->float_data_for_write());
|
||||
}
|
||||
else {
|
||||
do_glow_effect_byte(strip,
|
||||
render_size,
|
||||
fac,
|
||||
context->rectx,
|
||||
context->recty,
|
||||
ibuf1.image->byte_data(),
|
||||
nullptr,
|
||||
out.image->byte_data_for_write());
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
void glow_effect_get_handle(EffectHandle &rval)
|
||||
{
|
||||
rval.init = init_glow_effect;
|
||||
rval.free = free_glow_effect;
|
||||
rval.execute = do_glow_effect;
|
||||
}
|
||||
|
||||
} // namespace blender::seq
|
||||
@@ -0,0 +1,65 @@
|
||||
/* SPDX-FileCopyrightText: 2024 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup sequencer
|
||||
*/
|
||||
|
||||
#include "DNA_scene_types.h"
|
||||
#include "DNA_sequence_types.h"
|
||||
|
||||
#include "PRF_profile.hh"
|
||||
|
||||
#include "SEQ_channels.hh"
|
||||
#include "SEQ_render.hh"
|
||||
#include "SEQ_utils.hh"
|
||||
|
||||
#include "effects.hh"
|
||||
#include "render.hh"
|
||||
|
||||
namespace blender::seq {
|
||||
|
||||
static StripEarlyOut early_out_multicam(const Strip * /*strip*/, float /*fac*/)
|
||||
{
|
||||
return StripEarlyOut::NoInput;
|
||||
}
|
||||
|
||||
static SeqResult do_multicam(const RenderData *context,
|
||||
SeqRenderState *state,
|
||||
Strip *strip,
|
||||
float timeline_frame,
|
||||
float /*fac*/,
|
||||
const SeqResult & /*ibuf1*/,
|
||||
const SeqResult & /*ibuf2*/)
|
||||
{
|
||||
PRF_scope_with_name("SeqFxMultiCam", ProfileCategory::Draw);
|
||||
if (strip->multicam_source == 0 || strip->multicam_source >= strip->channel) {
|
||||
return {};
|
||||
}
|
||||
|
||||
Editing *ed = context->scene->ed;
|
||||
if (!ed || state->strips_in_progress.contains(strip)) {
|
||||
return {};
|
||||
}
|
||||
ListBaseT<Strip> *seqbasep = get_seqbase_by_strip(context->scene, strip);
|
||||
ListBaseT<SeqTimelineChannel> *channels = get_channels_by_strip(ed, strip);
|
||||
if (!seqbasep) {
|
||||
return {};
|
||||
}
|
||||
|
||||
state->strips_in_progress.add(strip);
|
||||
SeqResult out = seq_render_give_ibuf_seqbase(
|
||||
context, state, timeline_frame, strip->multicam_source, channels, seqbasep);
|
||||
state->strips_in_progress.remove(strip);
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
void multi_camera_effect_get_handle(EffectHandle &rval)
|
||||
{
|
||||
rval.early_out = early_out_multicam;
|
||||
rval.execute = do_multicam;
|
||||
}
|
||||
|
||||
} // namespace blender::seq
|
||||
@@ -0,0 +1,85 @@
|
||||
/* SPDX-FileCopyrightText: 2024 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup sequencer
|
||||
*/
|
||||
|
||||
#include "BLI_task.hh"
|
||||
|
||||
#include "DNA_sequence_types.h"
|
||||
|
||||
#include "IMB_imbuf.hh"
|
||||
|
||||
#include "PRF_profile.hh"
|
||||
|
||||
#include "effects.hh"
|
||||
|
||||
namespace blender::seq {
|
||||
|
||||
static void init_solid_color(Strip *strip)
|
||||
{
|
||||
SolidColorVars *data = MEM_new<SolidColorVars>("solidcolor");
|
||||
strip->effectdata = data;
|
||||
data->col[0] = data->col[1] = data->col[2] = 0.5;
|
||||
data->width = data->height = 1;
|
||||
}
|
||||
|
||||
static void free_solid_color(Strip *strip, const bool /*do_id_user*/)
|
||||
{
|
||||
if (strip->effectdata) {
|
||||
SolidColorVars *data = static_cast<SolidColorVars *>(strip->effectdata);
|
||||
MEM_delete(data);
|
||||
strip->effectdata = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
static StripEarlyOut early_out_color(const Strip * /*strip*/, float /*fac*/)
|
||||
{
|
||||
return StripEarlyOut::NoInput;
|
||||
}
|
||||
|
||||
static SeqResult do_solid_color(const RenderData * /*context*/,
|
||||
SeqRenderState * /*state*/,
|
||||
Strip *strip,
|
||||
float /*timeline_frame*/,
|
||||
float /*fac*/,
|
||||
const SeqResult & /*ibuf1*/,
|
||||
const SeqResult & /*ibuf2*/)
|
||||
{
|
||||
PRF_scope_with_name("SeqFxColor", ProfileCategory::Draw);
|
||||
SeqResult out;
|
||||
|
||||
const SolidColorVars *cv = static_cast<const SolidColorVars *>(strip->effectdata);
|
||||
out.image = IMB_allocImBuf(cv->width, cv->height, ImBufFlags::ByteData);
|
||||
|
||||
uchar color[4];
|
||||
rgb_float_to_uchar(color, cv->col);
|
||||
color[3] = 255;
|
||||
|
||||
uchar *byte_data = out.image->byte_data_for_write();
|
||||
threading::parallel_for(IndexRange(out.image->y), 64, [&](const IndexRange y_range) {
|
||||
uchar *dst = byte_data + y_range.first() * out.image->x * 4;
|
||||
uchar *dst_end = dst + y_range.size() * out.image->x * 4;
|
||||
while (dst < dst_end) {
|
||||
memcpy(dst, color, sizeof(color));
|
||||
dst += 4;
|
||||
}
|
||||
});
|
||||
|
||||
out.image->color_mode = ImColorMode::RGB;
|
||||
out.is_opaque_before_transform = true;
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
void solid_color_effect_get_handle(EffectHandle &rval)
|
||||
{
|
||||
rval.init = init_solid_color;
|
||||
rval.free = free_solid_color;
|
||||
rval.early_out = early_out_color;
|
||||
rval.execute = do_solid_color;
|
||||
}
|
||||
|
||||
} // namespace blender::seq
|
||||
@@ -0,0 +1,213 @@
|
||||
/* 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 "PRF_profile.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)
|
||||
{
|
||||
SpeedControlVars *data = MEM_new<SpeedControlVars>("speedcontrolvars");
|
||||
strip->effectdata = data;
|
||||
data->speed_control_type = SEQ_SPEED_STRETCH;
|
||||
data->speed_fader = 1.0f;
|
||||
data->speed_fader_length = 0.0f;
|
||||
data->speed_fader_frame_number = 0.0f;
|
||||
}
|
||||
|
||||
static void free_speed_effect(Strip *strip, const bool /*do_id_user*/)
|
||||
{
|
||||
if (strip->effectdata) {
|
||||
SpeedControlVars *v = static_cast<SpeedControlVars *>(strip->effectdata);
|
||||
if (v->frameMap) {
|
||||
MEM_delete(v->frameMap);
|
||||
}
|
||||
MEM_delete(v);
|
||||
strip->effectdata = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
static void copy_speed_effect(Strip *dst, const Strip *src, const int /*flag*/)
|
||||
{
|
||||
SpeedControlVars *v = MEM_dupalloc(static_cast<SpeedControlVars *>(src->effectdata));
|
||||
v->frameMap = nullptr;
|
||||
dst->effectdata = v;
|
||||
}
|
||||
|
||||
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 = strip->right_handle(scene) - strip->left_handle();
|
||||
|
||||
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 = static_cast<SpeedControlVars *>(strip->effectdata);
|
||||
if (v->frameMap) {
|
||||
MEM_delete(v->frameMap);
|
||||
}
|
||||
|
||||
v->frameMap = MEM_new_array_uninitialized<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, strip->left_handle() + frame_index);
|
||||
const int target_frame_max = strip->input1->length(scene);
|
||||
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 = static_cast<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 = static_cast<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 = source->length(scene) - source->startofs;
|
||||
const float speed_effetct_length = strip_speed->right_handle(scene) -
|
||||
strip_speed->left_handle();
|
||||
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;
|
||||
if (s->speed_fader < 0) {
|
||||
/* Treat `target_frame` as a negative offset from the last frame of the strip. */
|
||||
target_frame += source->length(scene);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SEQ_SPEED_LENGTH:
|
||||
target_frame = source->length(scene) * (s->speed_fader_length / 100.0f);
|
||||
break;
|
||||
case SEQ_SPEED_FRAME_NUMBER:
|
||||
target_frame = s->speed_fader_frame_number;
|
||||
break;
|
||||
}
|
||||
|
||||
CLAMP(target_frame, 0, source->length(scene));
|
||||
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 SeqResult do_speed_effect(const RenderData *context,
|
||||
SeqRenderState *state,
|
||||
Strip *strip,
|
||||
float timeline_frame,
|
||||
float fac,
|
||||
const SeqResult &ibuf1,
|
||||
const SeqResult &ibuf2)
|
||||
{
|
||||
PRF_scope_with_name("SeqFxSpeed", ProfileCategory::Draw);
|
||||
SeqResult out;
|
||||
|
||||
const SpeedControlVars *s = static_cast<SpeedControlVars *>(strip->effectdata);
|
||||
EffectHandle cross_effect = effect_handle_get(STRIP_TYPE_CROSS);
|
||||
|
||||
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, state, nullptr, timeline_frame, fac, ibuf1, ibuf2);
|
||||
return out;
|
||||
}
|
||||
|
||||
/* No interpolation. */
|
||||
out.image = IMB_dupImBuf(ibuf1.image);
|
||||
out.is_opaque_before_transform = !ibuf1.image->can_contain_alpha();
|
||||
return out;
|
||||
}
|
||||
|
||||
void speed_effect_get_handle(EffectHandle &rval)
|
||||
{
|
||||
rval.init = init_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
|
||||
1136
blender-5.2.0/source/blender/sequencer/intern/effects/effect_text.cc
Normal file
1136
blender-5.2.0/source/blender/sequencer/intern/effects/effect_text.cc
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,253 @@
|
||||
/* SPDX-FileCopyrightText: 2024 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup sequencer
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "BLI_math_vector.hh"
|
||||
#include "BLI_task.hh"
|
||||
|
||||
#include "DNA_sequence_types.h"
|
||||
|
||||
#include "IMB_imbuf.hh"
|
||||
|
||||
#include "PRF_profile.hh"
|
||||
|
||||
#include "SEQ_render.hh"
|
||||
|
||||
#include "effects.hh"
|
||||
|
||||
namespace blender::seq {
|
||||
|
||||
struct WipeData {
|
||||
WipeData(const WipeVars *wipe, int width, int height, float fac)
|
||||
{
|
||||
this->type = wipe->wipetype;
|
||||
this->forward = wipe->forward != 0;
|
||||
this->size = float2(width, height);
|
||||
|
||||
if (this->type == SEQ_WIPE_SINGLE) {
|
||||
/* Position that the wipe line goes through: moves along
|
||||
* the image diagonal. The other diagonal when angle is negative. */
|
||||
this->pos = this->size * (this->forward ? fac : (1.0f - fac));
|
||||
if (wipe->angle < 0.0f) {
|
||||
this->pos.x = this->size.x - this->pos.x;
|
||||
}
|
||||
}
|
||||
if (this->type == SEQ_WIPE_DOUBLE) {
|
||||
/* For double blend, position goes from center of screen
|
||||
* along the diagonal. The other blend line position will be
|
||||
* a mirror of it. */
|
||||
float2 offset = this->size * (this->forward ? (1.0f - fac) : fac) * 0.5f;
|
||||
if (wipe->angle < 0.0f) {
|
||||
offset.x = -offset.x;
|
||||
}
|
||||
this->pos = this->size * 0.5f + offset;
|
||||
}
|
||||
|
||||
/* Line direction: (cos(a), sin(a)). Perpendicular: (-sin(a), cos(a)).
|
||||
* Angle is negative to match previous behavior. */
|
||||
this->normal.x = -sinf(-wipe->angle);
|
||||
this->normal.y = cosf(-wipe->angle);
|
||||
|
||||
/* Blend zone width. */
|
||||
float blend_width = wipe->edgeWidth * ((width + height) / 2.0f);
|
||||
if (ELEM(this->type, SEQ_WIPE_DOUBLE, SEQ_WIPE_IRIS)) {
|
||||
blend_width *= 0.5f;
|
||||
}
|
||||
/* For single/double wipes, make sure the blend zone goes to zero at start & end
|
||||
* of transition. */
|
||||
if (ELEM(this->type, SEQ_WIPE_SINGLE, SEQ_WIPE_DOUBLE)) {
|
||||
blend_width = std::min(blend_width, fac * this->size.y);
|
||||
blend_width = std::min(blend_width, this->size.y - fac * this->size.y);
|
||||
}
|
||||
this->blend_width_inv = math::safe_rcp(blend_width);
|
||||
|
||||
if (this->type == SEQ_WIPE_IRIS) {
|
||||
/* Distance to Iris circle at current factor. */
|
||||
float2 iris = this->size * 0.5f * (this->forward ? (1.0f - fac) : fac);
|
||||
this->iris_dist = math::length(iris);
|
||||
}
|
||||
|
||||
if (this->type == SEQ_WIPE_CLOCK) {
|
||||
float angle_cur = 2.0f * float(M_PI) * (this->forward ? (1.0f - fac) : fac);
|
||||
float angle_width = wipe->edgeWidth * float(M_PI);
|
||||
float delta_neg = angle_width * (this->forward ? fac : (1.0f - fac));
|
||||
float delta_pos = angle_width * (this->forward ? (1.0f - fac) : fac);
|
||||
this->clock_angles.x = std::max(angle_cur - delta_neg, 0.0f);
|
||||
this->clock_angles.y = std::min(angle_cur + delta_pos, 2.0f * float(M_PI));
|
||||
this->clock_angle_inv_dif = math::safe_rcp(this->clock_angles.y - this->clock_angles.x);
|
||||
}
|
||||
}
|
||||
|
||||
float2 size; /* Image size. */
|
||||
float2 pos; /* Position that wipe line goes through. */
|
||||
float2 normal; /* Normal vector to single/double wipe line. */
|
||||
float blend_width_inv = 0.0f;
|
||||
float iris_dist = 0.0f;
|
||||
float2 clock_angles; /* Min, max clock angles at current factor. */
|
||||
float clock_angle_inv_dif = 0.0f;
|
||||
eEffectWipeType type;
|
||||
bool forward = false;
|
||||
};
|
||||
|
||||
static float calc_wipe_band(float dist, float inv_width)
|
||||
{
|
||||
if (inv_width == 0.0f) {
|
||||
return dist < 0.0f ? 0.0f : 1.0f;
|
||||
}
|
||||
return dist * inv_width + 0.5f;
|
||||
}
|
||||
|
||||
static float calc_wipe_blend(const WipeData *data, int x, int y)
|
||||
{
|
||||
float output = 0.0f;
|
||||
switch (data->type) {
|
||||
case SEQ_WIPE_SINGLE: {
|
||||
/* Distance to line: dot(pixel_pos - line_pos, line_normal). */
|
||||
float dist = math::dot(float2(x, y) - data->pos, data->normal);
|
||||
output = calc_wipe_band(dist, data->blend_width_inv);
|
||||
} break;
|
||||
|
||||
case SEQ_WIPE_DOUBLE: {
|
||||
/* Distance to line: dot(pixel_pos - line_pos, line_normal).
|
||||
* For double wipe, we have two lines to calculate the distance to. */
|
||||
float2 pos1 = data->pos;
|
||||
float2 pos2 = data->size - data->pos;
|
||||
float dist1 = math::dot(float2(x, y) - pos1, -data->normal);
|
||||
float dist2 = math::dot(float2(x, y) - pos2, data->normal);
|
||||
float dist = std::min(dist1, dist2);
|
||||
output = calc_wipe_band(dist, data->blend_width_inv);
|
||||
} break;
|
||||
|
||||
case SEQ_WIPE_CLOCK: {
|
||||
float2 offset = float2(x, y) - data->size * 0.5f;
|
||||
if (math::length_squared(offset) < 1.0e-3f) {
|
||||
output = 0.0f;
|
||||
}
|
||||
else {
|
||||
float angle;
|
||||
angle = atan2f(offset.y, offset.x);
|
||||
if (angle < 0.0f) {
|
||||
angle += 2.0f * float(M_PI);
|
||||
}
|
||||
if (angle < data->clock_angles.x) {
|
||||
output = 1;
|
||||
}
|
||||
else if (angle > data->clock_angles.y) {
|
||||
output = 0;
|
||||
}
|
||||
else {
|
||||
output = (data->clock_angles.y - angle) * data->clock_angle_inv_dif;
|
||||
}
|
||||
}
|
||||
} break;
|
||||
|
||||
case SEQ_WIPE_IRIS: {
|
||||
float dist = math::distance(float2(x, y), data->size * 0.5f);
|
||||
output = calc_wipe_band(data->iris_dist - dist, data->blend_width_inv);
|
||||
} break;
|
||||
}
|
||||
if (!data->forward) {
|
||||
output = 1.0f - output;
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
static void init_wipe_effect(Strip *strip)
|
||||
{
|
||||
strip->effectdata = MEM_new<WipeVars>("wipevars");
|
||||
}
|
||||
|
||||
static void free_wipe_effect(Strip *strip, const bool /*do_id_user*/)
|
||||
{
|
||||
if (strip->effectdata) {
|
||||
MEM_delete(static_cast<WipeVars *>(strip->effectdata));
|
||||
strip->effectdata = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static void do_wipe_effect(
|
||||
const Strip *strip, float fac, int width, int height, const T *rect1, const T *rect2, T *out)
|
||||
{
|
||||
const WipeVars *wipe = static_cast<const WipeVars *>(strip->effectdata);
|
||||
|
||||
const WipeData data(wipe, width, height, fac);
|
||||
|
||||
threading::parallel_for(IndexRange(height), 64, [&](const IndexRange y_range) {
|
||||
const T *cp1 = rect1 + y_range.first() * width * 4;
|
||||
const T *cp2 = rect2 + y_range.first() * width * 4;
|
||||
T *rt = out + y_range.first() * width * 4;
|
||||
for (const int y : y_range) {
|
||||
for (int x = 0; x < width; x++) {
|
||||
float blend = calc_wipe_blend(&data, x, y);
|
||||
if (blend <= 0.0f) {
|
||||
memcpy(rt, cp2, sizeof(T) * 4);
|
||||
}
|
||||
else if (blend >= 1.0f) {
|
||||
memcpy(rt, cp1, sizeof(T) * 4);
|
||||
}
|
||||
else {
|
||||
float4 col1 = load_premul_pixel(cp1);
|
||||
float4 col2 = load_premul_pixel(cp2);
|
||||
float4 col = col1 * blend + col2 * (1.0f - blend);
|
||||
store_premul_pixel(col, rt);
|
||||
}
|
||||
|
||||
rt += 4;
|
||||
cp1 += 4;
|
||||
cp2 += 4;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
static SeqResult do_wipe_effect(const RenderData *context,
|
||||
SeqRenderState * /*state*/,
|
||||
Strip *strip,
|
||||
float /*timeline_frame*/,
|
||||
float fac,
|
||||
const SeqResult &ibuf1,
|
||||
const SeqResult &ibuf2)
|
||||
{
|
||||
PRF_scope_with_name("SeqFxWipe", ProfileCategory::Draw);
|
||||
SeqResult out = prepare_effect_imbufs(context, ibuf1, ibuf2);
|
||||
|
||||
if (out.image->float_data()) {
|
||||
do_wipe_effect(strip,
|
||||
fac,
|
||||
context->rectx,
|
||||
context->recty,
|
||||
ibuf1.image->float_data(),
|
||||
ibuf2.image->float_data(),
|
||||
out.image->float_data_for_write());
|
||||
}
|
||||
else {
|
||||
do_wipe_effect(strip,
|
||||
fac,
|
||||
context->rectx,
|
||||
context->recty,
|
||||
ibuf1.image->byte_data(),
|
||||
ibuf2.image->byte_data(),
|
||||
out.image->byte_data_for_write());
|
||||
}
|
||||
|
||||
out.is_opaque_before_transform = !ibuf1.image->can_contain_alpha() &&
|
||||
!ibuf2.image->can_contain_alpha();
|
||||
return out;
|
||||
}
|
||||
|
||||
void wipe_effect_get_handle(EffectHandle &rval)
|
||||
{
|
||||
rval.init = init_wipe_effect;
|
||||
rval.free = free_wipe_effect;
|
||||
rval.early_out = early_out_fade;
|
||||
rval.execute = do_wipe_effect;
|
||||
}
|
||||
|
||||
} // namespace blender::seq
|
||||
383
blender-5.2.0/source/blender/sequencer/intern/effects/effects.cc
Normal file
383
blender-5.2.0/source/blender/sequencer/intern/effects/effects.cc
Normal file
@@ -0,0 +1,383 @@
|
||||
/* 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 "BLI_math_filter.hh"
|
||||
|
||||
#include "BKE_fcurve.hh"
|
||||
|
||||
#include "DNA_scene_types.h"
|
||||
#include "DNA_sequence_types.h"
|
||||
|
||||
#include "IMB_colormanagement.hh"
|
||||
#include "IMB_imbuf.hh"
|
||||
#include "IMB_metadata.hh"
|
||||
|
||||
#include "PRF_profile.hh"
|
||||
|
||||
#include "RNA_prototypes.hh"
|
||||
|
||||
#include "SEQ_render.hh"
|
||||
|
||||
#include "effects.hh"
|
||||
#include "render.hh"
|
||||
|
||||
namespace blender::seq {
|
||||
|
||||
SeqResult prepare_effect_imbufs(const RenderData *context,
|
||||
const SeqResult &ibuf1,
|
||||
const SeqResult &ibuf2,
|
||||
bool uninitialized_pixels)
|
||||
{
|
||||
PRF_scope_with_name("SeqFxPrepareImbufs", ProfileCategory::Draw);
|
||||
SeqResult out;
|
||||
Scene *scene = context->scene;
|
||||
int x = context->rectx;
|
||||
int y = context->recty;
|
||||
ImBufFlags base_flags = uninitialized_pixels ? ImBufFlags::UninitializedPixels :
|
||||
ImBufFlags::Zero;
|
||||
|
||||
if (!ibuf1.is_valid() && !ibuf2.is_valid()) {
|
||||
out.image = IMB_allocImBuf(x, y, ImBufFlags::ByteData | base_flags);
|
||||
}
|
||||
else if ((ibuf1.is_valid() && ibuf1.image->float_data()) ||
|
||||
(ibuf2.is_valid() && ibuf2.image->float_data()))
|
||||
{
|
||||
/* if any inputs are float, output is float too */
|
||||
out.image = IMB_allocImBuf(x, y, ImBufFlags::FloatData | base_flags);
|
||||
}
|
||||
else {
|
||||
out.image = IMB_allocImBuf(x, y, ImBufFlags::ByteData | base_flags);
|
||||
}
|
||||
|
||||
if (out.image->float_data()) {
|
||||
if (ibuf1.is_valid()) {
|
||||
ensure_ibuf_is_sequencer_space(scene, ibuf1.image, true);
|
||||
}
|
||||
if (ibuf2.is_valid()) {
|
||||
ensure_ibuf_is_sequencer_space(scene, ibuf2.image, true);
|
||||
}
|
||||
IMB_colormanagement_assign_float_colorspace(out.image,
|
||||
scene->sequencer_colorspace_settings.name);
|
||||
}
|
||||
else {
|
||||
if (ibuf1.is_valid() && !ibuf1.image->byte_data()) {
|
||||
IMB_byte_from_float(ibuf1.image);
|
||||
}
|
||||
if (ibuf2.is_valid() && !ibuf2.image->byte_data()) {
|
||||
IMB_byte_from_float(ibuf2.image);
|
||||
}
|
||||
}
|
||||
|
||||
/* If effect only affecting a single channel, forward input's metadata to the output. */
|
||||
if (ibuf1.is_valid() && ibuf1.image == ibuf2.image) {
|
||||
IMB_metadata_copy(out.image, ibuf1.image);
|
||||
}
|
||||
|
||||
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 = math::filter_kernel_value(math::FilterKernel::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 copy_effect_default(Strip *dst, const Strip *src, const int /*flag*/)
|
||||
{
|
||||
dst->effectdata = MEM_dupalloc_void(src->effectdata);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
void effect_ensure_initialized(Strip *strip)
|
||||
{
|
||||
if (strip->effectdata == nullptr) {
|
||||
EffectHandle h = strip_effect_handle_get(strip);
|
||||
if (h.init != nullptr) {
|
||||
h.init(strip);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void effect_free(Strip *strip)
|
||||
{
|
||||
EffectHandle h = strip_effect_handle_get(strip);
|
||||
if (h.free != nullptr) {
|
||||
h.free(strip, true);
|
||||
BLI_assert(strip->effectdata == nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
EffectHandle effect_handle_get(StripType strip_type)
|
||||
{
|
||||
EffectHandle rval;
|
||||
|
||||
rval.init = init_noop;
|
||||
rval.free = nullptr;
|
||||
rval.early_out = early_out_noop;
|
||||
rval.execute = nullptr;
|
||||
rval.copy = copy_effect_default;
|
||||
|
||||
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_COMPOSITOR:
|
||||
compositor_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_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_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;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return rval;
|
||||
}
|
||||
|
||||
static EffectHandle effect_handle_for_blend_mode_get(StripBlendMode blend)
|
||||
{
|
||||
EffectHandle rval;
|
||||
|
||||
rval.init = init_noop;
|
||||
rval.free = nullptr;
|
||||
rval.early_out = early_out_noop;
|
||||
rval.execute = nullptr;
|
||||
rval.copy = nullptr;
|
||||
|
||||
switch (blend) {
|
||||
case STRIP_BLEND_CROSS:
|
||||
cross_effect_get_handle(rval);
|
||||
break;
|
||||
case STRIP_BLEND_ADD:
|
||||
add_effect_get_handle(rval);
|
||||
break;
|
||||
case STRIP_BLEND_SUB:
|
||||
sub_effect_get_handle(rval);
|
||||
break;
|
||||
case STRIP_BLEND_ALPHAOVER:
|
||||
alpha_over_effect_get_handle(rval);
|
||||
break;
|
||||
case STRIP_BLEND_ALPHAUNDER:
|
||||
alpha_under_effect_get_handle(rval);
|
||||
break;
|
||||
case STRIP_BLEND_GAMCROSS:
|
||||
gamma_cross_effect_get_handle(rval);
|
||||
break;
|
||||
case STRIP_BLEND_MUL:
|
||||
mul_effect_get_handle(rval);
|
||||
break;
|
||||
case STRIP_BLEND_SCREEN:
|
||||
case STRIP_BLEND_LIGHTEN:
|
||||
case STRIP_BLEND_DODGE:
|
||||
case STRIP_BLEND_DARKEN:
|
||||
case STRIP_BLEND_COLOR_BURN:
|
||||
case STRIP_BLEND_LINEAR_BURN:
|
||||
case STRIP_BLEND_OVERLAY:
|
||||
case STRIP_BLEND_HARD_LIGHT:
|
||||
case STRIP_BLEND_SOFT_LIGHT:
|
||||
case STRIP_BLEND_PIN_LIGHT:
|
||||
case STRIP_BLEND_LIN_LIGHT:
|
||||
case STRIP_BLEND_VIVID_LIGHT:
|
||||
case STRIP_BLEND_HUE:
|
||||
case STRIP_BLEND_SATURATION:
|
||||
case STRIP_BLEND_VALUE:
|
||||
case STRIP_BLEND_BLEND_COLOR:
|
||||
case STRIP_BLEND_DIFFERENCE:
|
||||
case STRIP_BLEND_EXCLUSION:
|
||||
blend_mode_effect_get_handle(rval);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return rval;
|
||||
}
|
||||
|
||||
EffectHandle strip_effect_handle_get(Strip *strip)
|
||||
{
|
||||
EffectHandle h = {};
|
||||
if (strip->is_effect()) {
|
||||
h = effect_handle_get(strip->type);
|
||||
}
|
||||
return h;
|
||||
}
|
||||
|
||||
EffectHandle strip_blend_mode_handle_get(Strip *strip)
|
||||
{
|
||||
EffectHandle h = {};
|
||||
if (strip->blend_mode != STRIP_BLEND_REPLACE) {
|
||||
h = effect_handle_for_blend_mode_get(strip->blend_mode);
|
||||
}
|
||||
return h;
|
||||
}
|
||||
|
||||
static float transition_fader_calc(const Scene *scene, const Strip *strip, float timeline_frame)
|
||||
{
|
||||
float fac = float(timeline_frame - strip->left_handle());
|
||||
/* Compositor with no inputs can have strip->len not be updated,
|
||||
* since most of existing editing code assumes no-input effects never need the length.
|
||||
* So for the fader, just calculated it here directly. */
|
||||
if (strip->type == STRIP_TYPE_COMPOSITOR) {
|
||||
fac /= strip->enddisp - strip->startdisp;
|
||||
}
|
||||
else {
|
||||
fac /= strip->length(scene);
|
||||
}
|
||||
fac = math::clamp(fac, 0.0f, 1.0f);
|
||||
return fac;
|
||||
}
|
||||
|
||||
float effect_fader_calc(Scene *scene, Strip *strip, float timeline_frame)
|
||||
{
|
||||
if (strip->flag & SEQ_USE_EFFECT_DEFAULT_FADE) {
|
||||
if (effect_is_transition(strip->type)) {
|
||||
return transition_fader_calc(scene, strip, timeline_frame);
|
||||
}
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
const FCurve *fcu = id_data_find_fcurve(
|
||||
&scene->id, strip, RNA_Strip, "effect_fader", 0, nullptr);
|
||||
if (fcu) {
|
||||
return evaluate_fcurve(fcu, timeline_frame);
|
||||
}
|
||||
return strip->effect_fader;
|
||||
}
|
||||
|
||||
int effect_type_get_min_num_inputs(StripType type)
|
||||
{
|
||||
if (!strip_type_is_effect(type)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Zero input effects. Note: compositor is here too, but it supports
|
||||
* any input count. */
|
||||
if (ELEM(type,
|
||||
STRIP_TYPE_ADJUSTMENT,
|
||||
STRIP_TYPE_MULTICAM,
|
||||
STRIP_TYPE_COLOR,
|
||||
STRIP_TYPE_TEXT,
|
||||
STRIP_TYPE_COMPOSITOR))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* One input effects. */
|
||||
if (ELEM(type, STRIP_TYPE_GAUSSIAN_BLUR, STRIP_TYPE_GLOW, STRIP_TYPE_SPEED)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Others are two inputs. */
|
||||
return 2;
|
||||
}
|
||||
|
||||
bool strip_type_is_effect(StripType type)
|
||||
{
|
||||
return (type >= STRIP_TYPE_CROSS && type <= STRIP_TYPE_COMPOSITOR) ||
|
||||
(type >= STRIP_TYPE_WIPE && type <= STRIP_TYPE_ADJUSTMENT) ||
|
||||
(type >= STRIP_TYPE_GAUSSIAN_BLUR && type <= STRIP_TYPE_COLORMIX);
|
||||
}
|
||||
|
||||
bool effect_is_transition(StripType type)
|
||||
{
|
||||
return ELEM(type, STRIP_TYPE_CROSS, STRIP_TYPE_GAMCROSS, STRIP_TYPE_WIPE, STRIP_TYPE_COMPOSITOR);
|
||||
}
|
||||
|
||||
} // namespace blender::seq
|
||||
173
blender-5.2.0/source/blender/sequencer/intern/effects/effects.hh
Normal file
173
blender-5.2.0/source/blender/sequencer/intern/effects/effects.hh
Normal file
@@ -0,0 +1,173 @@
|
||||
/* SPDX-FileCopyrightText: 2004 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
/** \file
|
||||
* \ingroup sequencer
|
||||
*/
|
||||
|
||||
#include "BLI_array.hh"
|
||||
#include "BLI_math_color.h"
|
||||
#include "BLI_math_vector_types.hh"
|
||||
#include "BLI_task.hh"
|
||||
|
||||
#include "IMB_imbuf_types.hh"
|
||||
#include "SEQ_effects.hh"
|
||||
|
||||
#include "render.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
struct ImBuf;
|
||||
struct Scene;
|
||||
struct Strip;
|
||||
|
||||
namespace seq {
|
||||
|
||||
struct SeqRenderState;
|
||||
struct RenderData;
|
||||
|
||||
enum class StripEarlyOut {
|
||||
NoInput = -1, /* No input needed. */
|
||||
DoEffect = 0, /* No early out (do the effect). */
|
||||
UseInput1 = 1, /* Output = input1. */
|
||||
UseInput2 = 2, /* Output = input2. */
|
||||
};
|
||||
|
||||
struct EffectHandle {
|
||||
/* #init is only called on first creation, or when changing effect type. */
|
||||
void (*init)(Strip *strip);
|
||||
|
||||
/* duplicate */
|
||||
void (*copy)(Strip *dst, const Strip *src, int flag);
|
||||
|
||||
/* destruct */
|
||||
void (*free)(Strip *strip, bool do_id_user);
|
||||
|
||||
StripEarlyOut (*early_out)(const Strip *strip, float fac);
|
||||
|
||||
/* execute the effect */
|
||||
SeqResult (*execute)(const RenderData *context,
|
||||
SeqRenderState *state,
|
||||
Strip *strip,
|
||||
float timeline_frame,
|
||||
float fac,
|
||||
const SeqResult &input1,
|
||||
const SeqResult &input2);
|
||||
};
|
||||
|
||||
/** Get the effect handle for a given strip.
|
||||
* If `strip` is not an effect strip, returns empty `EffectHandle`. */
|
||||
EffectHandle strip_effect_handle_get(Strip *strip);
|
||||
|
||||
EffectHandle strip_blend_mode_handle_get(Strip *strip);
|
||||
/**
|
||||
* Build frame map when speed in mode #SEQ_SPEED_MULTIPLY is animated.
|
||||
* This is, because `target_frame` value is integrated over time.
|
||||
*/
|
||||
void strip_effect_speed_rebuild_map(Scene *scene, Strip *strip);
|
||||
/**
|
||||
* Override timeline_frame when rendering speed effect input.
|
||||
*/
|
||||
float strip_speed_effect_target_frame_get(Scene *scene,
|
||||
Strip *strip_speed,
|
||||
float timeline_frame,
|
||||
int input);
|
||||
|
||||
SeqResult prepare_effect_imbufs(const RenderData *context,
|
||||
const SeqResult &ibuf1,
|
||||
const SeqResult &ibuf2,
|
||||
bool uninitialized_pixels = true);
|
||||
|
||||
Array<float> make_gaussian_blur_kernel(float rad, int size);
|
||||
|
||||
inline float4 load_premul_pixel(const uchar *ptr)
|
||||
{
|
||||
float4 res;
|
||||
straight_uchar_to_premul_float(res, ptr);
|
||||
return res;
|
||||
}
|
||||
|
||||
inline float4 load_premul_pixel(const float *ptr)
|
||||
{
|
||||
return float4(ptr);
|
||||
}
|
||||
|
||||
inline void store_premul_pixel(const float4 &pix, uchar *dst)
|
||||
{
|
||||
premul_float_to_straight_uchar(dst, pix);
|
||||
}
|
||||
|
||||
inline void store_premul_pixel(const float4 &pix, float *dst)
|
||||
{
|
||||
*reinterpret_cast<float4 *>(dst) = pix;
|
||||
}
|
||||
|
||||
StripEarlyOut early_out_mul_input1(const Strip * /*strip*/, float fac);
|
||||
StripEarlyOut early_out_mul_input2(const Strip * /*strip*/, float fac);
|
||||
StripEarlyOut early_out_fade(const Strip * /*strip*/, float fac);
|
||||
|
||||
EffectHandle effect_handle_get(StripType strip_type);
|
||||
|
||||
float effect_fader_calc(Scene *scene, Strip *strip, float timeline_frame);
|
||||
|
||||
void add_effect_get_handle(EffectHandle &rval);
|
||||
void adjustment_effect_get_handle(EffectHandle &rval);
|
||||
void alpha_over_effect_get_handle(EffectHandle &rval);
|
||||
void alpha_under_effect_get_handle(EffectHandle &rval);
|
||||
void blend_mode_effect_get_handle(EffectHandle &rval);
|
||||
void color_mix_effect_get_handle(EffectHandle &rval);
|
||||
void compositor_effect_get_handle(EffectHandle &rval);
|
||||
void cross_effect_get_handle(EffectHandle &rval);
|
||||
void gamma_cross_effect_get_handle(EffectHandle &rval);
|
||||
void gaussian_blur_effect_get_handle(EffectHandle &rval);
|
||||
void glow_effect_get_handle(EffectHandle &rval);
|
||||
void mul_effect_get_handle(EffectHandle &rval);
|
||||
void multi_camera_effect_get_handle(EffectHandle &rval);
|
||||
void solid_color_effect_get_handle(EffectHandle &rval);
|
||||
void speed_effect_get_handle(EffectHandle &rval);
|
||||
void sub_effect_get_handle(EffectHandle &rval);
|
||||
void text_effect_get_handle(EffectHandle &rval);
|
||||
void transform_effect_get_handle(EffectHandle &rval);
|
||||
void wipe_effect_get_handle(EffectHandle &rval);
|
||||
|
||||
/* Given `OpT` that implements an `apply` function:
|
||||
*
|
||||
* template <typename T>
|
||||
* void apply(const T *src1, const T *src2, T *dst, int64_t size) const;
|
||||
*
|
||||
* this function calls the apply() function in parallel
|
||||
* chunks of the image to process, and with uchar or float types
|
||||
* All images are expected to have 4 (RGBA) color channels. */
|
||||
template<typename OpT>
|
||||
static void apply_effect_op(const OpT &op, const ImBuf *src1, const ImBuf *src2, ImBuf *dst)
|
||||
{
|
||||
BLI_assert_msg(src1->channels == 0 || src1->channels == 4,
|
||||
"Sequencer only supports 4 channel images");
|
||||
BLI_assert_msg(src2->channels == 0 || src2->channels == 4,
|
||||
"Sequencer only supports 4 channel images");
|
||||
BLI_assert_msg(dst->channels == 0 || dst->channels == 4,
|
||||
"Sequencer only supports 4 channel images");
|
||||
float *dst_float_data = dst->float_data_for_write();
|
||||
uchar *dst_byte_data = dst->byte_data_for_write();
|
||||
threading::parallel_for(IndexRange(size_t(dst->x) * dst->y), 32 * 1024, [&](IndexRange range) {
|
||||
int64_t offset = range.first() * 4;
|
||||
if (dst_float_data) {
|
||||
const float *src1_ptr = src1->float_data() + offset;
|
||||
const float *src2_ptr = src2->float_data() + offset;
|
||||
float *dst_ptr = dst_float_data + offset;
|
||||
op.apply(src1_ptr, src2_ptr, dst_ptr, range.size());
|
||||
}
|
||||
else {
|
||||
const uchar *src1_ptr = src1->byte_data() + offset;
|
||||
const uchar *src2_ptr = src2->byte_data() + offset;
|
||||
uchar *dst_ptr = dst_byte_data + offset;
|
||||
op.apply(src1_ptr, src2_ptr, dst_ptr, range.size());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace seq
|
||||
} // namespace blender
|
||||
Reference in New Issue
Block a user