/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved. * SPDX-FileCopyrightText: 2003-2024 Blender Authors * SPDX-FileCopyrightText: 2005-2006 Peter Schlaile * * SPDX-License-Identifier: GPL-2.0-or-later */ /** \file * \ingroup bke */ #include #include "MEM_guardedalloc.h" #include "DNA_sequence_types.h" #include "BLI_listbase.h" #include "SEQ_effects.hh" #include "SEQ_iterator.hh" #include "SEQ_relations.hh" #include "SEQ_render.hh" #include "SEQ_time.hh" using blender::VectorSet; static bool seq_for_each_recursive(ListBase *seqbase, SeqForEachFunc callback, void *user_data) { LISTBASE_FOREACH (Sequence *, seq, seqbase) { if (!callback(seq, user_data)) { /* Callback signaled stop, return. */ return false; } if (seq->type == SEQ_TYPE_META) { if (!seq_for_each_recursive(&seq->seqbase, callback, user_data)) { return false; } } } return true; } void SEQ_for_each_callback(ListBase *seqbase, SeqForEachFunc callback, void *user_data) { seq_for_each_recursive(seqbase, callback, user_data); } VectorSet SEQ_query_by_reference(Sequence *seq_reference, const Scene *scene, ListBase *seqbase, void seq_query_func(const Scene *scene, Sequence *seq_reference, ListBase *seqbase, VectorSet &strips)) { VectorSet strips; seq_query_func(scene, seq_reference, seqbase, strips); return strips; } void SEQ_iterator_set_expand(const Scene *scene, ListBase *seqbase, VectorSet &strips, void seq_query_func(const Scene *scene, Sequence *seq_reference, ListBase *seqbase, VectorSet &strips)) { /* Collect expanded results for each sequence in provided VectorSet. */ VectorSet query_matches; for (Sequence *strip : strips) { query_matches.add_multiple(SEQ_query_by_reference(strip, scene, seqbase, seq_query_func)); } /* Merge all expanded results in provided VectorSet. */ strips.add_multiple(query_matches); } static void query_all_strips_recursive(const ListBase *seqbase, VectorSet &strips) { LISTBASE_FOREACH (Sequence *, seq, seqbase) { if (seq->type == SEQ_TYPE_META) { query_all_strips_recursive(&seq->seqbase, strips); } strips.add(seq); } } static void query_all_meta_strips_recursive(const ListBase *seqbase, VectorSet &strips) { LISTBASE_FOREACH (Sequence *, seq, seqbase) { if (seq->type == SEQ_TYPE_META) { query_all_meta_strips_recursive(&seq->seqbase, strips); strips.add(seq); } } } VectorSet SEQ_query_all_strips_recursive(const ListBase *seqbase) { VectorSet strips; query_all_strips_recursive(seqbase, strips); return strips; } VectorSet SEQ_query_all_meta_strips_recursive(const ListBase *seqbase) { VectorSet strips; query_all_meta_strips_recursive(seqbase, strips); return strips; } VectorSet SEQ_query_all_strips(ListBase *seqbase) { VectorSet strips; LISTBASE_FOREACH (Sequence *, strip, seqbase) { strips.add(strip); } return strips; } VectorSet SEQ_query_selected_strips(ListBase *seqbase) { VectorSet strips; LISTBASE_FOREACH (Sequence *, strip, seqbase) { if ((strip->flag & SELECT) != 0) { strips.add(strip); } } return strips; } static VectorSet query_strips_at_frame(const Scene *scene, ListBase *seqbase, const int timeline_frame) { VectorSet strips; LISTBASE_FOREACH (Sequence *, strip, seqbase) { if (SEQ_time_strip_intersects_frame(scene, strip, timeline_frame)) { strips.add(strip); } } return strips; } static void collection_filter_channel_up_to_incl(VectorSet &strips, const int channel) { strips.remove_if([&](Sequence *strip) { return strip->machine > channel; }); } /* Check if seq must be rendered. This depends on whole stack in some cases, not only seq itself. * Order of applying these conditions is important. */ static bool must_render_strip(const VectorSet &strips, Sequence *strip) { bool seq_have_effect_in_stack = false; for (Sequence *strip_iter : strips) { /* Strips is below another strip with replace blending are not rendered. */ if (strip_iter->blend_mode == SEQ_BLEND_REPLACE && strip->machine < strip_iter->machine) { return false; } if ((strip_iter->type & SEQ_TYPE_EFFECT) != 0 && SEQ_relation_is_effect_of_strip(strip_iter, strip)) { /* Strips in same channel or higher than its effect are rendered. */ if (strip->machine >= strip_iter->machine) { return true; } /* Mark that this strip has effect in stack, that is above the strip. */ seq_have_effect_in_stack = true; } } /* All non-generator effects are rendered (with respect to conditions above). */ if ((strip->type & SEQ_TYPE_EFFECT) != 0 && SEQ_effect_get_num_inputs(strip->type) != 0) { return true; } /* If strip has effects in stack, and all effects are above this strip, it is not rendered. */ if (seq_have_effect_in_stack) { return false; } return true; } /* Remove strips we don't want to render from VectorSet. */ static void collection_filter_rendered_strips(VectorSet &strips, ListBase *channels) { /* Remove sound strips and muted strips from VectorSet, because these are not rendered. * Function #must_render_strip() don't have to check for these strips anymore. */ strips.remove_if([&](Sequence *strip) { return strip->type == SEQ_TYPE_SOUND_RAM || SEQ_render_is_muted(channels, strip); }); strips.remove_if([&](Sequence *strip) { return !must_render_strip(strips, strip); }); } VectorSet SEQ_query_rendered_strips(const Scene *scene, ListBase *channels, ListBase *seqbase, const int timeline_frame, const int displayed_channel) { VectorSet strips = query_strips_at_frame(scene, seqbase, timeline_frame); if (displayed_channel != 0) { collection_filter_channel_up_to_incl(strips, displayed_channel); } collection_filter_rendered_strips(strips, channels); return strips; } VectorSet SEQ_query_unselected_strips(ListBase *seqbase) { VectorSet strips; LISTBASE_FOREACH (Sequence *, seq, seqbase) { if ((seq->flag & SELECT) != 0) { continue; } strips.add(seq); } return strips; } void SEQ_query_strip_effect_chain(const Scene *scene, Sequence *reference_strip, ListBase *seqbase, VectorSet &strips) { if (strips.contains(reference_strip)) { return; /* Strip is already in set, so all effects connected to it are as well. */ } strips.add(reference_strip); /* Find all strips that reference_strip is connected to. */ if (reference_strip->type & SEQ_TYPE_EFFECT) { if (reference_strip->seq1) { SEQ_query_strip_effect_chain(scene, reference_strip->seq1, seqbase, strips); } if (reference_strip->seq2) { SEQ_query_strip_effect_chain(scene, reference_strip->seq2, seqbase, strips); } if (reference_strip->seq3) { SEQ_query_strip_effect_chain(scene, reference_strip->seq3, seqbase, strips); } } /* Find all strips connected to reference_strip. */ LISTBASE_FOREACH (Sequence *, seq_test, seqbase) { if (seq_test->seq1 == reference_strip || seq_test->seq2 == reference_strip || seq_test->seq3 == reference_strip) { SEQ_query_strip_effect_chain(scene, seq_test, seqbase, strips); } } }