From 709ce44617bf8c41b5bfe9a93aa58e9140323428 Mon Sep 17 00:00:00 2001 From: Richard Antalik Date: Tue, 24 Aug 2021 00:52:24 +0200 Subject: Fix T90407: Split fails on transition strips When splitting strips, first they are duplicated and then offsets adjusted. This can fail on cross transitions, because some strips don't overlap with split frame. All strips, that relate to each other must be duplicated to ensure correct relations after splitting, so solution is to delete non overlapping strips from left or right side respectively. Since cross transition don't have to overlap with source strips, splitting such strips would lead to effect being deleted, which could cause crash when iterating over strips in python. Therefore splitting of such strips is now forbidden and will generate error. Splitting of transition will also generate error solely because such operation is illogical. Reviewed By: sergey Differential Revision: https://developer.blender.org/D12121 --- source/blender/sequencer/SEQ_edit.h | 3 +- source/blender/sequencer/intern/strip_edit.c | 124 +++++++++++++++++++++++---- 2 files changed, 110 insertions(+), 17 deletions(-) (limited to 'source/blender/sequencer') diff --git a/source/blender/sequencer/SEQ_edit.h b/source/blender/sequencer/SEQ_edit.h index 6d043dffe72..fbbf4bc53ea 100644 --- a/source/blender/sequencer/SEQ_edit.h +++ b/source/blender/sequencer/SEQ_edit.h @@ -53,7 +53,8 @@ struct Sequence *SEQ_edit_strip_split(struct Main *bmain, struct ListBase *seqbase, struct Sequence *seq, const int timeline_frame, - const eSeqSplitMethod method); + const eSeqSplitMethod method, + const char **r_error); bool SEQ_edit_remove_gaps(struct Scene *scene, struct ListBase *seqbase, const int initial_frame, diff --git a/source/blender/sequencer/intern/strip_edit.c b/source/blender/sequencer/intern/strip_edit.c index b83ee92f117..17ff1c90be8 100644 --- a/source/blender/sequencer/intern/strip_edit.c +++ b/source/blender/sequencer/intern/strip_edit.c @@ -351,6 +351,11 @@ static void seq_split_set_left_offset(Sequence *seq, int timeline_frame) SEQ_transform_set_left_handle_frame(seq, timeline_frame); } +static bool seq_edit_split_effect_intersect_check(const Sequence *seq, const int timeline_frame) +{ + return timeline_frame > seq->startdisp && timeline_frame < seq->enddisp; +} + static void seq_edit_split_handle_strip_offsets(Main *bmain, Scene *scene, Sequence *left_seq, @@ -358,20 +363,82 @@ static void seq_edit_split_handle_strip_offsets(Main *bmain, const int timeline_frame, const eSeqSplitMethod method) { - switch (method) { - case SEQ_SPLIT_SOFT: - seq_split_set_left_offset(right_seq, timeline_frame); - seq_split_set_right_offset(left_seq, timeline_frame); - break; - case SEQ_SPLIT_HARD: - seq_split_set_right_hold_offset(left_seq, timeline_frame); - seq_split_set_left_hold_offset(right_seq, timeline_frame); - SEQ_add_reload_new_file(bmain, scene, left_seq, false); - SEQ_add_reload_new_file(bmain, scene, right_seq, false); - break; - } - SEQ_time_update_sequence(scene, left_seq); - SEQ_time_update_sequence(scene, right_seq); + if (seq_edit_split_effect_intersect_check(right_seq, timeline_frame)) { + switch (method) { + case SEQ_SPLIT_SOFT: + seq_split_set_left_offset(right_seq, timeline_frame); + break; + case SEQ_SPLIT_HARD: + seq_split_set_left_hold_offset(right_seq, timeline_frame); + SEQ_add_reload_new_file(bmain, scene, right_seq, false); + break; + } + SEQ_time_update_sequence(scene, right_seq); + } + + if (seq_edit_split_effect_intersect_check(left_seq, timeline_frame)) { + switch (method) { + case SEQ_SPLIT_SOFT: + seq_split_set_right_offset(left_seq, timeline_frame); + break; + case SEQ_SPLIT_HARD: + seq_split_set_right_hold_offset(left_seq, timeline_frame); + SEQ_add_reload_new_file(bmain, scene, left_seq, false); + break; + } + SEQ_time_update_sequence(scene, left_seq); + } +} + +static bool seq_edit_split_effect_inputs_intersect(const Sequence *seq, const int timeline_frame) +{ + bool input_does_intersect = false; + if (seq->seq1) { + input_does_intersect |= seq_edit_split_effect_intersect_check(seq->seq1, timeline_frame); + if ((seq->seq1->type & SEQ_TYPE_EFFECT) != 0) { + input_does_intersect |= seq_edit_split_effect_inputs_intersect(seq->seq1, timeline_frame); + } + } + if (seq->seq2) { + input_does_intersect |= seq_edit_split_effect_intersect_check(seq->seq2, timeline_frame); + if ((seq->seq1->type & SEQ_TYPE_EFFECT) != 0) { + input_does_intersect |= seq_edit_split_effect_inputs_intersect(seq->seq2, timeline_frame); + } + } + if (seq->seq3) { + input_does_intersect |= seq_edit_split_effect_intersect_check(seq->seq3, timeline_frame); + if ((seq->seq1->type & SEQ_TYPE_EFFECT) != 0) { + input_does_intersect |= seq_edit_split_effect_inputs_intersect(seq->seq3, timeline_frame); + } + } + return input_does_intersect; +} + +static bool seq_edit_split_operation_permitted_check(SeqCollection *strips, + const int timeline_frame, + const char **r_error) +{ + Sequence *seq; + SEQ_ITERATOR_FOREACH (seq, strips) { + if ((seq->type & SEQ_TYPE_EFFECT) == 0) { + continue; + } + if (!seq_edit_split_effect_intersect_check(seq, timeline_frame)) { + continue; + } + if (SEQ_effect_get_num_inputs(seq->type) <= 1) { + continue; + } + if (ELEM(seq->type, SEQ_TYPE_CROSS, SEQ_TYPE_GAMCROSS, SEQ_TYPE_WIPE)) { + *r_error = "Splitting transition effect is not permitted."; + return false; + } + if (!seq_edit_split_effect_inputs_intersect(seq, timeline_frame)) { + *r_error = "Effect inputs don't overlap. Can not split such effect."; + return false; + } + } + return true; } /** @@ -390,16 +457,23 @@ Sequence *SEQ_edit_strip_split(Main *bmain, ListBase *seqbase, Sequence *seq, const int timeline_frame, - const eSeqSplitMethod method) + const eSeqSplitMethod method, + const char **r_error) { - if (timeline_frame <= seq->startdisp || timeline_frame >= seq->enddisp) { + if (!seq_edit_split_effect_intersect_check(seq, timeline_frame)) { return NULL; } + /* Whole strip chain must be duplicated in order to preserve relationships. */ SeqCollection *collection = SEQ_collection_create(__func__); SEQ_collection_append_strip(seq, collection); SEQ_collection_expand(seqbase, collection, SEQ_query_strip_effect_chain); + if (!seq_edit_split_operation_permitted_check(collection, timeline_frame, r_error)) { + SEQ_collection_free(collection); + return NULL; + } + /* Move strips in collection from seqbase to new ListBase. */ ListBase left_strips = {NULL, NULL}; SEQ_ITERATOR_FOREACH (seq, collection) { @@ -421,7 +495,19 @@ Sequence *SEQ_edit_strip_split(Main *bmain, Sequence *left_seq = left_strips.first; Sequence *right_seq = right_strips.first; Sequence *return_seq = right_strips.first; + + /* Strips can't be tagged while in detached `seqbase`. Collect all strips which needs to be + * deleted and delay tagging until they are moved back to `seqbase` in `Editing`. */ + SeqCollection *strips_to_delete = SEQ_collection_create(__func__); + while (left_seq && right_seq) { + if (left_seq->startdisp >= timeline_frame) { + SEQ_collection_append_strip(left_seq, strips_to_delete); + } + if (right_seq->enddisp <= timeline_frame) { + SEQ_collection_append_strip(right_seq, strips_to_delete); + } + seq_edit_split_handle_strip_offsets(bmain, scene, left_seq, right_seq, timeline_frame, method); left_seq = left_seq->next; right_seq = right_seq->next; @@ -435,6 +521,12 @@ Sequence *SEQ_edit_strip_split(Main *bmain, SEQ_ensure_unique_name(seq, scene); } + Sequence *seq_delete; + SEQ_ITERATOR_FOREACH (seq_delete, strips_to_delete) { + SEQ_edit_flag_for_removal(scene, seqbase, seq_delete); + } + SEQ_edit_remove_flagged_sequences(scene, seqbase); + SEQ_collection_free(strips_to_delete); return return_seq; } -- cgit v1.2.3