Currently when a strip has a transform that does not fill the whole render area, first the image of the strip is transformed, and then any modifiers are applied on that. This is mostly in the new Compositor modifier, where procedural textures, gradients, image coordinates "stick to the screen" instead of following the transformed strip. This changes the behavior so that first the modifiers are applied to the strip image, and then the strip is transformed. This is potentially a visually breaking change: - This can alter visual look of existing strip, especially if they are scaled. Previous behavior was first scale filtering, then modifier; now it is first modifier, then scale filtering. - Most obvious change is Compositor modifier (which is new in 5.0). - Compositor modifier can actually expand the input image (e.g. Blur node with "expand bounds" option set), and that works. - Note that Masks continue to be applied in global/screen space. There can be small look differences with rotated/scaled strips that use masks, due to Mask application now needing to do filtered mask image lookups. - If anyone needs previous behavior (modifier is applied on the "whole screen"), they can put transformed strip into a meta strip, and apply the modifier on the meta strip itself. Compositor modifier examples with images in the PR. Pull Request: https://projects.blender.org/blender/blender/pulls/146181
653 lines
18 KiB
C++
653 lines
18 KiB
C++
/* SPDX-FileCopyrightText: 2012-2024 Blender Authors
|
|
*
|
|
* SPDX-License-Identifier: GPL-2.0-or-later */
|
|
|
|
/** \file
|
|
* \ingroup bke
|
|
*/
|
|
|
|
#include "BLI_array.hh"
|
|
#include "BLI_hash.hh"
|
|
#include "BLI_listbase.h"
|
|
#include "BLI_rand.hh"
|
|
#include "BLI_set.hh"
|
|
#include "BLI_string_utf8.h"
|
|
#include "BLI_string_utils.hh"
|
|
#include "BLI_task.hh"
|
|
|
|
#include "BLT_translation.hh"
|
|
|
|
#include "DNA_mask_types.h"
|
|
#include "DNA_sequence_types.h"
|
|
#include "DNA_space_types.h"
|
|
|
|
#include "BKE_colortools.hh"
|
|
#include "BKE_screen.hh"
|
|
|
|
#include "IMB_colormanagement.hh"
|
|
|
|
#include "RNA_access.hh"
|
|
#include "RNA_prototypes.hh"
|
|
|
|
#include "SEQ_modifier.hh"
|
|
#include "SEQ_modifiertypes.hh"
|
|
#include "SEQ_render.hh"
|
|
#include "SEQ_select.hh"
|
|
#include "SEQ_sequencer.hh"
|
|
#include "SEQ_time.hh"
|
|
#include "SEQ_transform.hh"
|
|
#include "SEQ_utils.hh"
|
|
|
|
#include "UI_interface.hh"
|
|
#include "UI_interface_layout.hh"
|
|
|
|
#include "BLO_read_write.hh"
|
|
|
|
#include "WM_api.hh"
|
|
|
|
#include "modifier.hh"
|
|
#include "render.hh"
|
|
|
|
namespace blender::seq {
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
static bool modifier_has_persistent_uid(const Strip &strip, int uid)
|
|
{
|
|
LISTBASE_FOREACH (StripModifierData *, smd, &strip.modifiers) {
|
|
if (smd->persistent_uid == uid) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void modifier_persistent_uid_init(const Strip &strip, StripModifierData &smd)
|
|
{
|
|
uint64_t hash = blender::get_default_hash(blender::StringRef(smd.name));
|
|
blender::RandomNumberGenerator rng{uint32_t(hash)};
|
|
while (true) {
|
|
const int new_uid = rng.get_int32();
|
|
if (new_uid <= 0) {
|
|
continue;
|
|
}
|
|
if (modifier_has_persistent_uid(strip, new_uid)) {
|
|
continue;
|
|
}
|
|
smd.persistent_uid = new_uid;
|
|
break;
|
|
}
|
|
}
|
|
|
|
bool modifier_persistent_uids_are_valid(const Strip &strip)
|
|
{
|
|
Set<int> uids;
|
|
int modifiers_num = 0;
|
|
LISTBASE_FOREACH (StripModifierData *, smd, &strip.modifiers) {
|
|
if (smd->persistent_uid <= 0) {
|
|
return false;
|
|
}
|
|
uids.add(smd->persistent_uid);
|
|
modifiers_num++;
|
|
}
|
|
if (uids.size() != modifiers_num) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static void modifier_panel_header(const bContext * /*C*/, Panel *panel)
|
|
{
|
|
uiLayout *row, *sub, *name_row;
|
|
uiLayout *layout = panel->layout;
|
|
|
|
/* Don't use #modifier_panel_get_property_pointers, we don't want to lock the header. */
|
|
PointerRNA *ptr = UI_panel_custom_data_get(panel);
|
|
StripModifierData *smd = reinterpret_cast<StripModifierData *>(ptr->data);
|
|
|
|
UI_panel_context_pointer_set(panel, "modifier", ptr);
|
|
|
|
/* Modifier Icon. */
|
|
sub = &layout->row(true);
|
|
sub->emboss_set(blender::ui::EmbossType::None);
|
|
PointerRNA active_op_ptr = sub->op(
|
|
"SEQUENCER_OT_strip_modifier_set_active", "", RNA_struct_ui_icon(ptr->type));
|
|
RNA_string_set(&active_op_ptr, "modifier", smd->name);
|
|
|
|
row = &layout->row(true);
|
|
|
|
/* Modifier Name.
|
|
* Count how many buttons are added to the header to check if there is enough space. */
|
|
int buttons_number = 0;
|
|
name_row = &row->row(true);
|
|
|
|
sub = &row->row(true);
|
|
sub->prop(ptr, "enable", UI_ITEM_NONE, "", ICON_NONE);
|
|
buttons_number++;
|
|
|
|
/* Delete button. */
|
|
sub = &row->row(false);
|
|
sub->emboss_set(blender::ui::EmbossType::None);
|
|
PointerRNA remove_op_ptr = sub->op("SEQUENCER_OT_strip_modifier_remove", "", ICON_X);
|
|
RNA_string_set(&remove_op_ptr, "name", smd->name);
|
|
buttons_number++;
|
|
|
|
bool display_name = (panel->sizex / UI_UNIT_X - buttons_number > 5) || (panel->sizex == 0);
|
|
if (display_name) {
|
|
name_row->prop(ptr, "name", UI_ITEM_NONE, "", ICON_NONE);
|
|
}
|
|
else {
|
|
row->alignment_set(blender::ui::LayoutAlign::Right);
|
|
}
|
|
|
|
/* Extra padding for delete button. */
|
|
layout->separator();
|
|
}
|
|
|
|
void draw_mask_input_type_settings(const bContext *C, uiLayout *layout, PointerRNA *ptr)
|
|
{
|
|
Scene *sequencer_scene = CTX_data_sequencer_scene(C);
|
|
Editing *ed = seq::editing_get(sequencer_scene);
|
|
uiLayout *row, *col;
|
|
|
|
const int input_mask_type = RNA_enum_get(ptr, "input_mask_type");
|
|
|
|
layout->use_property_split_set(true);
|
|
|
|
col = &layout->column(false);
|
|
row = &col->row(true);
|
|
row->prop(ptr, "input_mask_type", UI_ITEM_R_EXPAND, "Type", ICON_NONE);
|
|
|
|
if (input_mask_type == STRIP_MASK_INPUT_STRIP) {
|
|
MetaStack *ms = meta_stack_active_get(ed);
|
|
PointerRNA sequences_object;
|
|
if (ms) {
|
|
sequences_object = RNA_pointer_create_discrete(
|
|
&sequencer_scene->id, &RNA_MetaStrip, ms->parent_strip);
|
|
}
|
|
else {
|
|
sequences_object = RNA_pointer_create_discrete(
|
|
&sequencer_scene->id, &RNA_SequenceEditor, ed);
|
|
}
|
|
col->prop_search(ptr, "input_mask_strip", &sequences_object, "strips", "Mask", ICON_NONE);
|
|
}
|
|
else {
|
|
col->prop(ptr, "input_mask_id", UI_ITEM_NONE, std::nullopt, ICON_NONE);
|
|
row = &col->row(true);
|
|
row->prop(ptr, "mask_time", UI_ITEM_R_EXPAND, std::nullopt, ICON_NONE);
|
|
}
|
|
}
|
|
|
|
bool modifier_ui_poll(const bContext *C, PanelType * /*pt*/)
|
|
{
|
|
Scene *sequencer_scene = CTX_data_sequencer_scene(C);
|
|
if (!sequencer_scene) {
|
|
return false;
|
|
}
|
|
Strip *active_strip = seq::select_active_get(sequencer_scene);
|
|
return active_strip != nullptr;
|
|
}
|
|
|
|
/**
|
|
* Move a modifier to the index it's moved to after a drag and drop.
|
|
*/
|
|
static void modifier_reorder(bContext *C, Panel *panel, const int new_index)
|
|
{
|
|
PointerRNA *smd_ptr = UI_panel_custom_data_get(panel);
|
|
StripModifierData *smd = reinterpret_cast<StripModifierData *>(smd_ptr->data);
|
|
|
|
PointerRNA props_ptr;
|
|
wmOperatorType *ot = WM_operatortype_find("SEQUENCER_OT_strip_modifier_move_to_index", false);
|
|
WM_operator_properties_create_ptr(&props_ptr, ot);
|
|
RNA_string_set(&props_ptr, "modifier", smd->name);
|
|
RNA_int_set(&props_ptr, "index", new_index);
|
|
WM_operator_name_call_ptr(C, ot, blender::wm::OpCallContext::InvokeDefault, &props_ptr, nullptr);
|
|
WM_operator_properties_free(&props_ptr);
|
|
}
|
|
|
|
static short get_strip_modifier_expand_flag(const bContext * /*C*/, Panel *panel)
|
|
{
|
|
PointerRNA *smd_ptr = UI_panel_custom_data_get(panel);
|
|
StripModifierData *smd = reinterpret_cast<StripModifierData *>(smd_ptr->data);
|
|
return smd->layout_panel_open_flag;
|
|
}
|
|
|
|
static void set_strip_modifier_expand_flag(const bContext * /*C*/, Panel *panel, short expand_flag)
|
|
{
|
|
PointerRNA *smd_ptr = UI_panel_custom_data_get(panel);
|
|
StripModifierData *smd = reinterpret_cast<StripModifierData *>(smd_ptr->data);
|
|
smd->layout_panel_open_flag = expand_flag;
|
|
}
|
|
|
|
PanelType *modifier_panel_register(ARegionType *region_type,
|
|
const eStripModifierType type,
|
|
PanelDrawFn draw)
|
|
{
|
|
PanelType *panel_type = MEM_callocN<PanelType>(__func__);
|
|
|
|
modifier_type_panel_id(type, panel_type->idname);
|
|
STRNCPY_UTF8(panel_type->label, "");
|
|
STRNCPY_UTF8(panel_type->translation_context, BLT_I18NCONTEXT_DEFAULT_BPYRNA);
|
|
STRNCPY_UTF8(panel_type->active_property, "is_active");
|
|
STRNCPY_UTF8(panel_type->context, "strip_modifier");
|
|
|
|
panel_type->draw_header = modifier_panel_header;
|
|
panel_type->draw = draw;
|
|
panel_type->poll = modifier_ui_poll;
|
|
|
|
/* Give the panel the special flag that says it was built here and corresponds to a
|
|
* modifier rather than a #PanelType. */
|
|
panel_type->flag = PANEL_TYPE_HEADER_EXPAND | PANEL_TYPE_INSTANCED;
|
|
panel_type->reorder = modifier_reorder;
|
|
panel_type->get_list_data_expand_flag = get_strip_modifier_expand_flag;
|
|
panel_type->set_list_data_expand_flag = set_strip_modifier_expand_flag;
|
|
|
|
BLI_addtail(®ion_type->paneltypes, panel_type);
|
|
|
|
return panel_type;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
float4 load_pixel_premul(const uchar *ptr)
|
|
{
|
|
float4 res;
|
|
straight_uchar_to_premul_float(res, ptr);
|
|
return res;
|
|
}
|
|
|
|
float4 load_pixel_premul(const float *ptr)
|
|
{
|
|
return float4(ptr);
|
|
}
|
|
|
|
void store_pixel_premul(float4 pix, uchar *ptr)
|
|
{
|
|
premul_float_to_straight_uchar(ptr, pix);
|
|
}
|
|
|
|
void store_pixel_premul(float4 pix, float *ptr)
|
|
{
|
|
*reinterpret_cast<float4 *>(ptr) = pix;
|
|
}
|
|
|
|
float4 load_pixel_raw(const uchar *ptr)
|
|
{
|
|
float4 res;
|
|
rgba_uchar_to_float(res, ptr);
|
|
return res;
|
|
}
|
|
|
|
float4 load_pixel_raw(const float *ptr)
|
|
{
|
|
return float4(ptr);
|
|
}
|
|
|
|
void store_pixel_raw(float4 pix, uchar *ptr)
|
|
{
|
|
rgba_float_to_uchar(ptr, pix);
|
|
}
|
|
|
|
void store_pixel_raw(float4 pix, float *ptr)
|
|
{
|
|
*reinterpret_cast<float4 *>(ptr) = pix;
|
|
}
|
|
|
|
/**
|
|
* \a timeline_frame is offset by \a fra_offset only in case we are using a real mask.
|
|
*/
|
|
static ImBuf *modifier_render_mask_input(const RenderData *context,
|
|
SeqRenderState *state,
|
|
int mask_input_type,
|
|
Strip *mask_strip,
|
|
Mask *mask_id,
|
|
int timeline_frame,
|
|
int fra_offset)
|
|
{
|
|
ImBuf *mask_input = nullptr;
|
|
|
|
if (mask_input_type == STRIP_MASK_INPUT_STRIP) {
|
|
if (mask_strip) {
|
|
mask_input = seq_render_strip(context, state, mask_strip, timeline_frame);
|
|
}
|
|
}
|
|
else if (mask_input_type == STRIP_MASK_INPUT_ID) {
|
|
/* Note that we do not request mask to be float image: if it is that is
|
|
* fine, but if it is a byte image then we also just take that without
|
|
* extra memory allocations or conversions. All modifiers are expected
|
|
* to handle mask being either type. */
|
|
mask_input = seq_render_mask(context->depsgraph,
|
|
context->rectx,
|
|
context->recty,
|
|
mask_id,
|
|
timeline_frame - fra_offset,
|
|
false);
|
|
}
|
|
|
|
return mask_input;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/** \name Public Modifier Functions
|
|
* \{ */
|
|
|
|
static StripModifierTypeInfo *modifiersTypes[NUM_STRIP_MODIFIER_TYPES] = {nullptr};
|
|
|
|
static void modifier_types_init(StripModifierTypeInfo *types[])
|
|
{
|
|
#define INIT_TYPE(typeName) (types[eSeqModifierType_##typeName] = &seqModifierType_##typeName)
|
|
INIT_TYPE(None);
|
|
INIT_TYPE(BrightContrast);
|
|
INIT_TYPE(ColorBalance);
|
|
INIT_TYPE(Compositor);
|
|
INIT_TYPE(Curves);
|
|
INIT_TYPE(HueCorrect);
|
|
INIT_TYPE(Mask);
|
|
INIT_TYPE(SoundEqualizer);
|
|
INIT_TYPE(Tonemap);
|
|
INIT_TYPE(WhiteBalance);
|
|
#undef INIT_TYPE
|
|
}
|
|
|
|
void modifiers_init()
|
|
{
|
|
modifier_types_init(modifiersTypes);
|
|
}
|
|
|
|
const StripModifierTypeInfo *modifier_type_info_get(int type)
|
|
{
|
|
if (type <= 0 || type >= NUM_STRIP_MODIFIER_TYPES) {
|
|
return nullptr;
|
|
}
|
|
return modifiersTypes[type];
|
|
}
|
|
|
|
StripModifierData *modifier_new(Strip *strip, const char *name, int type)
|
|
{
|
|
StripModifierData *smd;
|
|
const StripModifierTypeInfo *smti = modifier_type_info_get(type);
|
|
|
|
smd = static_cast<StripModifierData *>(MEM_callocN(smti->struct_size, "sequence modifier"));
|
|
|
|
smd->type = type;
|
|
smd->flag |= STRIP_MODIFIER_FLAG_EXPANDED;
|
|
smd->layout_panel_open_flag |= UI_PANEL_DATA_EXPAND_ROOT;
|
|
|
|
if (!name || !name[0]) {
|
|
STRNCPY_UTF8(smd->name, CTX_DATA_(BLT_I18NCONTEXT_ID_SEQUENCE, smti->name));
|
|
}
|
|
else {
|
|
STRNCPY_UTF8(smd->name, name);
|
|
}
|
|
|
|
BLI_addtail(&strip->modifiers, smd);
|
|
|
|
modifier_unique_name(strip, smd);
|
|
|
|
if (smti->init_data) {
|
|
smti->init_data(smd);
|
|
}
|
|
|
|
modifier_set_active(strip, smd);
|
|
|
|
return smd;
|
|
}
|
|
|
|
bool modifier_remove(Strip *strip, StripModifierData *smd)
|
|
{
|
|
if (BLI_findindex(&strip->modifiers, smd) == -1) {
|
|
return false;
|
|
}
|
|
|
|
BLI_remlink(&strip->modifiers, smd);
|
|
modifier_free(smd);
|
|
|
|
return true;
|
|
}
|
|
|
|
void modifier_clear(Strip *strip)
|
|
{
|
|
StripModifierData *smd, *smd_next;
|
|
|
|
for (smd = static_cast<StripModifierData *>(strip->modifiers.first); smd; smd = smd_next) {
|
|
smd_next = smd->next;
|
|
modifier_free(smd);
|
|
}
|
|
|
|
BLI_listbase_clear(&strip->modifiers);
|
|
}
|
|
|
|
void modifier_free(StripModifierData *smd)
|
|
{
|
|
const StripModifierTypeInfo *smti = modifier_type_info_get(smd->type);
|
|
|
|
if (smti && smti->free_data) {
|
|
smti->free_data(smd);
|
|
}
|
|
|
|
MEM_freeN(smd);
|
|
}
|
|
|
|
void modifier_unique_name(Strip *strip, StripModifierData *smd)
|
|
{
|
|
const StripModifierTypeInfo *smti = modifier_type_info_get(smd->type);
|
|
|
|
BLI_uniquename(&strip->modifiers,
|
|
smd,
|
|
CTX_DATA_(BLT_I18NCONTEXT_ID_SEQUENCE, smti->name),
|
|
'.',
|
|
offsetof(StripModifierData, name),
|
|
sizeof(smd->name));
|
|
}
|
|
|
|
StripModifierData *modifier_find_by_name(Strip *strip, const char *name)
|
|
{
|
|
return static_cast<StripModifierData *>(
|
|
BLI_findstring(&(strip->modifiers), name, offsetof(StripModifierData, name)));
|
|
}
|
|
|
|
static bool skip_modifier(Scene *scene, const StripModifierData *smd, int timeline_frame)
|
|
{
|
|
using namespace blender::seq;
|
|
|
|
if (smd->mask_strip == nullptr) {
|
|
return false;
|
|
}
|
|
const bool strip_has_ended_skip = smd->mask_input_type == STRIP_MASK_INPUT_STRIP &&
|
|
smd->mask_time == STRIP_MASK_TIME_RELATIVE &&
|
|
!time_strip_intersects_frame(
|
|
scene, smd->mask_strip, timeline_frame);
|
|
const bool missing_data_skip = !strip_has_valid_data(smd->mask_strip) ||
|
|
media_presence_is_missing(scene, smd->mask_strip);
|
|
|
|
return strip_has_ended_skip || missing_data_skip;
|
|
}
|
|
|
|
void modifier_apply_stack(const RenderData *context,
|
|
SeqRenderState *state,
|
|
const Strip *strip,
|
|
const float3x3 &transform,
|
|
ImBuf *ibuf,
|
|
int timeline_frame)
|
|
{
|
|
if (strip->modifiers.first && (strip->flag & SEQ_USE_LINEAR_MODIFIERS)) {
|
|
render_imbuf_from_sequencer_space(context->scene, ibuf);
|
|
}
|
|
|
|
LISTBASE_FOREACH (StripModifierData *, smd, &strip->modifiers) {
|
|
const StripModifierTypeInfo *smti = modifier_type_info_get(smd->type);
|
|
|
|
/* could happen if modifier is being removed or not exists in current version of blender */
|
|
if (!smti) {
|
|
continue;
|
|
}
|
|
|
|
/* modifier is muted, do nothing */
|
|
if (smd->flag & STRIP_MODIFIER_FLAG_MUTE) {
|
|
continue;
|
|
}
|
|
|
|
if (smti->apply && !skip_modifier(context->scene, smd, timeline_frame)) {
|
|
int frame_offset;
|
|
if (smd->mask_time == STRIP_MASK_TIME_RELATIVE) {
|
|
frame_offset = strip->start;
|
|
}
|
|
else /* if (smd->mask_time == STRIP_MASK_TIME_ABSOLUTE) */ {
|
|
frame_offset = smd->mask_id ? ((Mask *)smd->mask_id)->sfra : 0;
|
|
}
|
|
|
|
ImBuf *mask = modifier_render_mask_input(context,
|
|
state,
|
|
smd->mask_input_type,
|
|
smd->mask_strip,
|
|
smd->mask_id,
|
|
timeline_frame,
|
|
frame_offset);
|
|
smti->apply(context, strip, transform.ptr(), smd, ibuf, mask);
|
|
if (mask) {
|
|
IMB_freeImBuf(mask);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (strip->modifiers.first && (strip->flag & SEQ_USE_LINEAR_MODIFIERS)) {
|
|
seq_imbuf_to_sequencer_space(context->scene, ibuf, false);
|
|
}
|
|
}
|
|
|
|
StripModifierData *modifier_copy(Strip &strip_dst, StripModifierData *mod_src)
|
|
{
|
|
const StripModifierTypeInfo *smti = modifier_type_info_get(mod_src->type);
|
|
StripModifierData *mod_new = static_cast<StripModifierData *>(MEM_dupallocN(mod_src));
|
|
|
|
if (smti && smti->copy_data) {
|
|
smti->copy_data(mod_new, mod_src);
|
|
}
|
|
|
|
BLI_addtail(&strip_dst.modifiers, mod_new);
|
|
BLI_uniquename(&strip_dst.modifiers,
|
|
mod_new,
|
|
"Strip Modifier",
|
|
'.',
|
|
offsetof(StripModifierData, name),
|
|
sizeof(StripModifierData::name));
|
|
return mod_new;
|
|
}
|
|
|
|
void modifier_list_copy(Strip *strip_new, Strip *strip)
|
|
{
|
|
LISTBASE_FOREACH (StripModifierData *, smd, &strip->modifiers) {
|
|
modifier_copy(*strip_new, smd);
|
|
}
|
|
}
|
|
|
|
int sequence_supports_modifiers(Strip *strip)
|
|
{
|
|
return (strip->type != STRIP_TYPE_SOUND_RAM);
|
|
}
|
|
|
|
bool modifier_move_to_index(Strip *strip, StripModifierData *smd, const int new_index)
|
|
{
|
|
const int current_index = BLI_findindex(&strip->modifiers, smd);
|
|
return BLI_listbase_move_index(&strip->modifiers, current_index, new_index);
|
|
}
|
|
|
|
StripModifierData *modifier_get_active(const Strip *strip)
|
|
{
|
|
/* In debug mode, check for only one active modifier. */
|
|
#ifndef NDEBUG
|
|
int active_count = 0;
|
|
LISTBASE_FOREACH (StripModifierData *, smd, &strip->modifiers) {
|
|
if (smd->flag & STRIP_MODIFIER_FLAG_ACTIVE) {
|
|
active_count++;
|
|
}
|
|
}
|
|
BLI_assert(ELEM(active_count, 0, 1));
|
|
#endif
|
|
|
|
LISTBASE_FOREACH (StripModifierData *, smd, &strip->modifiers) {
|
|
if (smd->flag & STRIP_MODIFIER_FLAG_ACTIVE) {
|
|
return smd;
|
|
}
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
void modifier_set_active(Strip *strip, StripModifierData *smd)
|
|
{
|
|
LISTBASE_FOREACH (StripModifierData *, smd_iter, &strip->modifiers) {
|
|
smd_iter->flag &= ~STRIP_MODIFIER_FLAG_ACTIVE;
|
|
}
|
|
|
|
if (smd != nullptr) {
|
|
BLI_assert(BLI_findindex(&strip->modifiers, smd) != -1);
|
|
smd->flag |= STRIP_MODIFIER_FLAG_ACTIVE;
|
|
}
|
|
}
|
|
|
|
void modifier_type_panel_id(eStripModifierType type, char *r_idname)
|
|
{
|
|
const StripModifierTypeInfo *mti = modifier_type_info_get(type);
|
|
BLI_string_join(
|
|
r_idname, sizeof(PanelType::idname), STRIP_MODIFIER_TYPE_PANEL_PREFIX, mti->idname);
|
|
}
|
|
|
|
void foreach_strip_modifier_id(Strip *strip, const FunctionRef<void(ID *)> fn)
|
|
{
|
|
LISTBASE_FOREACH (StripModifierData *, smd, &strip->modifiers) {
|
|
if (smd->mask_id) {
|
|
fn(reinterpret_cast<ID *>(smd->mask_id));
|
|
}
|
|
if (smd->type == eSeqModifierType_Compositor) {
|
|
auto *modifier_data = reinterpret_cast<SequencerCompositorModifierData *>(smd);
|
|
if (modifier_data->node_group) {
|
|
fn(reinterpret_cast<ID *>(modifier_data->node_group));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/** \} */
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/** \name .blend File I/O
|
|
* \{ */
|
|
|
|
void modifier_blend_write(BlendWriter *writer, ListBase *modbase)
|
|
{
|
|
LISTBASE_FOREACH (StripModifierData *, smd, modbase) {
|
|
const StripModifierTypeInfo *smti = modifier_type_info_get(smd->type);
|
|
|
|
if (smti) {
|
|
BLO_write_struct_by_name(writer, smti->struct_name, smd);
|
|
if (smti->blend_write) {
|
|
smti->blend_write(writer, smd);
|
|
}
|
|
}
|
|
else {
|
|
BLO_write_struct(writer, StripModifierData, smd);
|
|
}
|
|
}
|
|
}
|
|
|
|
void modifier_blend_read_data(BlendDataReader *reader, ListBase *lb)
|
|
{
|
|
BLO_read_struct_list(reader, StripModifierData, lb);
|
|
|
|
LISTBASE_FOREACH (StripModifierData *, smd, lb) {
|
|
if (smd->mask_strip) {
|
|
BLO_read_struct(reader, Strip, &smd->mask_strip);
|
|
}
|
|
|
|
const StripModifierTypeInfo *smti = modifier_type_info_get(smd->type);
|
|
if (smti && smti->blend_read) {
|
|
smti->blend_read(reader, smd);
|
|
}
|
|
}
|
|
}
|
|
|
|
/** \} */
|
|
|
|
} // namespace blender::seq
|