Blender V4.3
strip_transform.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
2 * SPDX-FileCopyrightText: 2003-2009 Blender Authors
3 * SPDX-FileCopyrightText: 2005-2006 Peter Schlaile <peter [at] schlaile [dot] de>
4 *
5 * SPDX-License-Identifier: GPL-2.0-or-later */
6
11#include "DNA_scene_types.h"
12#include "DNA_sequence_types.h"
13
14#include "BLI_listbase.h"
15#include "BLI_math_matrix.h"
16#include "BLI_math_rotation.h"
17#include "BLI_math_vector.h"
19
20#include "SEQ_animation.hh"
21#include "SEQ_channels.hh"
22#include "SEQ_edit.hh"
23#include "SEQ_effects.hh"
24#include "SEQ_iterator.hh"
25#include "SEQ_relations.hh"
26#include "SEQ_sequencer.hh"
27#include "SEQ_time.hh"
28#include "SEQ_transform.hh"
29
30#include "sequencer.hh"
31#include "strip_time.hh"
32
34{
35 return (seq->flag & SEQ_SINGLE_FRAME_CONTENT) != 0;
36}
37
39{
40 /* is there more than 1 select */
41 bool ok = false;
42
43 LISTBASE_FOREACH (Sequence *, seq, seqbase) {
44 if (seq->flag & SELECT) {
45 ok = true;
46 break;
47 }
48 }
49
50 if (ok == false) {
51 return false;
52 }
53
54 /* test relationships */
55 LISTBASE_FOREACH (Sequence *, seq, seqbase) {
56 if ((seq->type & SEQ_TYPE_EFFECT) == 0) {
57 continue;
58 }
59
60 if (seq->flag & SELECT) {
61 if ((seq->seq1 && (seq->seq1->flag & SELECT) == 0) ||
62 (seq->seq2 && (seq->seq2->flag & SELECT) == 0))
63 {
64 return false;
65 }
66 }
67 else {
68 if ((seq->seq1 && (seq->seq1->flag & SELECT)) || (seq->seq2 && (seq->seq2->flag & SELECT))) {
69 return false;
70 }
71 }
72 }
73
74 return true;
75}
76
78{
79 return !(seq->type & SEQ_TYPE_EFFECT) || (SEQ_effect_get_num_inputs(seq->type) == 0);
80}
81
83{
84 return (seq1 != seq2 && seq1->machine == seq2->machine &&
85 ((SEQ_time_right_handle_frame_get(scene, seq1) <=
86 SEQ_time_left_handle_frame_get(scene, seq2)) ||
87 (SEQ_time_left_handle_frame_get(scene, seq1) >=
88 SEQ_time_right_handle_frame_get(scene, seq2))) == 0);
89}
90
91bool SEQ_transform_test_overlap(const Scene *scene, ListBase *seqbasep, Sequence *test)
92{
93 Sequence *seq;
94
95 seq = static_cast<Sequence *>(seqbasep->first);
96 while (seq) {
97 if (SEQ_transform_test_overlap_seq_seq(scene, test, seq)) {
98 return true;
99 }
100
101 seq = seq->next;
102 }
103 return false;
104}
105
106void SEQ_transform_translate_sequence(Scene *evil_scene, Sequence *seq, int delta)
107{
108 if (delta == 0) {
109 return;
110 }
111
112 /* Meta strips requires their content is to be translated, and then frame range of the meta is
113 * updated based on nested strips. This won't work for empty meta-strips,
114 * so they can be treated as normal strip. */
115 if (seq->type == SEQ_TYPE_META && !BLI_listbase_is_empty(&seq->seqbase)) {
116 LISTBASE_FOREACH (Sequence *, seq_child, &seq->seqbase) {
117 SEQ_transform_translate_sequence(evil_scene, seq_child, delta);
118 }
119 /* Move meta start/end points. */
120 seq_time_translate_handles(evil_scene, seq, delta);
121 }
122 else if (seq->seq1 == nullptr && seq->seq2 == nullptr) { /* All other strip types. */
123 seq->start += delta;
124 /* Only to make files usable in older versions. */
125 seq->startdisp = SEQ_time_left_handle_frame_get(evil_scene, seq);
126 seq->enddisp = SEQ_time_right_handle_frame_get(evil_scene, seq);
127 }
128
129 SEQ_offset_animdata(evil_scene, seq, delta);
130 blender::Span effects = seq_sequence_lookup_effects_by_seq(evil_scene, seq);
131 seq_time_update_effects_strip_range(evil_scene, effects);
133}
134
136 Sequence *test,
137 Scene *evil_scene,
138 int channel_delta)
139{
140 const int orig_machine = test->machine;
141 BLI_assert(ELEM(channel_delta, -1, 1));
142
143 test->machine += channel_delta;
144 while (SEQ_transform_test_overlap(evil_scene, seqbasep, test)) {
145 if ((channel_delta > 0) ? (test->machine >= SEQ_MAX_CHANNELS) : (test->machine < 1)) {
146 break;
147 }
148
149 test->machine += channel_delta;
150 }
151
152 if (!SEQ_is_valid_strip_channel(test)) {
153 /* Blender 2.4x would remove the strip.
154 * nicer to move it to the end */
155
156 int new_frame = SEQ_time_right_handle_frame_get(evil_scene, test);
157
158 LISTBASE_FOREACH (Sequence *, seq, seqbasep) {
159 if (seq->machine == orig_machine) {
160 new_frame = max_ii(new_frame, SEQ_time_right_handle_frame_get(evil_scene, seq));
161 }
162 }
163
164 test->machine = orig_machine;
165 new_frame = new_frame + (test->start - SEQ_time_left_handle_frame_get(
166 evil_scene, test)); /* adjust by the startdisp */
167 SEQ_transform_translate_sequence(evil_scene, test, new_frame - test->start);
168 return false;
169 }
170
171 return true;
172}
173
174bool SEQ_transform_seqbase_shuffle(ListBase *seqbasep, Sequence *test, Scene *evil_scene)
175{
176 return SEQ_transform_seqbase_shuffle_ex(seqbasep, test, evil_scene, 1);
177}
178
179static bool shuffle_seq_test_overlap(const Scene *scene,
180 const Sequence *seq1,
181 const Sequence *seq2,
182 const int offset)
183{
184 BLI_assert(seq1 != seq2);
185 return (seq1->machine == seq2->machine &&
186 ((SEQ_time_right_handle_frame_get(scene, seq1) + offset <=
187 SEQ_time_left_handle_frame_get(scene, seq2)) ||
188 (SEQ_time_left_handle_frame_get(scene, seq1) + offset >=
189 SEQ_time_right_handle_frame_get(scene, seq2))) == 0);
190}
191
192static int shuffle_seq_time_offset_get(const Scene *scene,
193 blender::Span<Sequence *> strips_to_shuffle,
194 ListBase *seqbasep,
195 char dir)
196{
197 int offset = 0;
198 bool all_conflicts_resolved = false;
199
200 while (!all_conflicts_resolved) {
201 all_conflicts_resolved = true;
202 for (Sequence *seq : strips_to_shuffle) {
203 LISTBASE_FOREACH (Sequence *, seq_other, seqbasep) {
204 if (strips_to_shuffle.contains(seq_other)) {
205 continue;
206 }
207 if (SEQ_relation_is_effect_of_strip(seq_other, seq)) {
208 continue;
209 }
210 if (!shuffle_seq_test_overlap(scene, seq, seq_other, offset)) {
211 continue;
212 }
213
214 all_conflicts_resolved = false;
215
216 if (dir == 'L') {
217 offset = min_ii(offset,
218 SEQ_time_left_handle_frame_get(scene, seq_other) -
220 }
221 else {
222 offset = max_ii(offset,
223 SEQ_time_right_handle_frame_get(scene, seq_other) -
225 }
226 }
227 }
228 }
229
230 return offset;
231}
232
234 ListBase *seqbasep,
235 Scene *evil_scene,
237 const bool use_sync_markers)
238{
241 strips_to_shuffle, empty_set, seqbasep, evil_scene, markers, use_sync_markers);
242}
243
245 blender::Span<Sequence *> time_dependent_strips,
246 ListBase *seqbasep,
247 Scene *evil_scene,
249 const bool use_sync_markers)
250{
251 int offset_l = shuffle_seq_time_offset_get(evil_scene, strips_to_shuffle, seqbasep, 'L');
252 int offset_r = shuffle_seq_time_offset_get(evil_scene, strips_to_shuffle, seqbasep, 'R');
253 int offset = (-offset_l < offset_r) ? offset_l : offset_r;
254
255 if (offset) {
256 for (Sequence *seq : strips_to_shuffle) {
257 SEQ_transform_translate_sequence(evil_scene, seq, offset);
258 seq->flag &= ~SEQ_OVERLAP;
259 }
260
261 if (!time_dependent_strips.is_empty()) {
262 for (Sequence *seq : time_dependent_strips) {
263 SEQ_offset_animdata(evil_scene, seq, offset);
264 }
265 }
266
267 if (use_sync_markers && !(evil_scene->toolsettings->lock_markers) && (markers != nullptr)) {
268 /* affect selected markers - it's unlikely that we will want to affect all in this way? */
269 LISTBASE_FOREACH (TimeMarker *, marker, markers) {
270 if (marker->flag & SELECT) {
271 marker->frame += offset;
272 }
273 }
274 }
275 }
276
277 return offset ? false : true;
278}
279
281 blender::Span<Sequence *> transformed_strips)
282{
283 blender::VectorSet<Sequence *> standalone_strips;
284
285 for (Sequence *seq : transformed_strips) {
286 if ((seq->type & SEQ_TYPE_EFFECT) == 0 || seq->seq1 == nullptr) {
287 standalone_strips.add(seq);
288 }
289 }
290 return standalone_strips;
291}
292
293/* Query strips positioned after left edge of transformed strips bound-box. */
295 const Scene *scene,
296 ListBase *seqbase,
297 blender::Span<Sequence *> transformed_strips,
298 blender::Span<Sequence *> time_dependent_strips)
299{
300 int minframe = MAXFRAME;
301 {
302 for (Sequence *seq : transformed_strips) {
303 minframe = min_ii(minframe, SEQ_time_left_handle_frame_get(scene, seq));
304 }
305 }
306
307 blender::VectorSet<Sequence *> right_side_strips;
308 LISTBASE_FOREACH (Sequence *, seq, seqbase) {
309 if (!time_dependent_strips.is_empty() && time_dependent_strips.contains(seq)) {
310 continue;
311 }
312 if (transformed_strips.contains(seq)) {
313 continue;
314 }
315
316 if ((seq->flag & SELECT) == 0 && SEQ_time_left_handle_frame_get(scene, seq) >= minframe) {
317 right_side_strips.add(seq);
318 }
319 }
320 return right_side_strips;
321}
322
323/* Offset all strips positioned after left edge of transformed strips bound-box by amount equal
324 * to overlap of transformed strips. */
326 ListBase *seqbasep,
327 blender::Span<Sequence *> transformed_strips,
328 blender::Span<Sequence *> time_dependent_strips,
329 bool use_sync_markers)
330{
331 ListBase *markers = &scene->markers;
332
334 scene, seqbasep, transformed_strips, time_dependent_strips);
335
336 /* Temporarily move right side strips beyond timeline boundary. */
337 for (Sequence *seq : right_side_strips) {
338 seq->machine += SEQ_MAX_CHANNELS * 2;
339 }
340
341 /* Shuffle transformed standalone strips. This is because transformed strips can overlap with
342 * strips on left side. */
343 blender::VectorSet standalone_strips = extract_standalone_strips(transformed_strips);
345 standalone_strips, time_dependent_strips, seqbasep, scene, markers, use_sync_markers);
346
347 /* Move temporarily moved strips back to their original place and tag for shuffling. */
348 for (Sequence *seq : right_side_strips) {
349 seq->machine -= SEQ_MAX_CHANNELS * 2;
350 }
351 /* Shuffle again to displace strips on right side. Final effect shuffling is done in
352 * SEQ_transform_handle_overlap. */
354 right_side_strips, seqbasep, scene, markers, use_sync_markers);
355}
356
358 const Scene *scene, ListBase *seqbasep, blender::Span<Sequence *> transformed_strips)
359{
361
362 /* Effects of transformed strips can be unselected. These must not be included. */
363 overwrite_targets.remove_if([&](Sequence *seq) { return transformed_strips.contains(seq); });
364 overwrite_targets.remove_if([&](Sequence *seq) {
365 bool does_overlap = false;
366 for (Sequence *seq_transformed : transformed_strips) {
367 if (SEQ_transform_test_overlap_seq_seq(scene, seq, seq_transformed)) {
368 does_overlap = true;
369 }
370 }
371
372 return !does_overlap;
373 });
374
375 return overwrite_targets;
376}
377
379 /* No overlap. */
381 /* Overlapping strip covers overlapped completely. */
383 /* Overlapping strip is inside overlapped. */
385 /* Partial overlap between 2 strips. */
388};
389
391 const Sequence *transformed,
392 const Sequence *target)
393{
394 if (SEQ_time_left_handle_frame_get(scene, transformed) <=
395 SEQ_time_left_handle_frame_get(scene, target) &&
396 SEQ_time_right_handle_frame_get(scene, transformed) >=
397 SEQ_time_right_handle_frame_get(scene, target))
398 {
400 }
401 if (SEQ_time_left_handle_frame_get(scene, transformed) >
402 SEQ_time_left_handle_frame_get(scene, target) &&
403 SEQ_time_right_handle_frame_get(scene, transformed) <
404 SEQ_time_right_handle_frame_get(scene, target))
405 {
407 }
408 if (SEQ_time_left_handle_frame_get(scene, transformed) <=
409 SEQ_time_left_handle_frame_get(scene, target) &&
410 SEQ_time_left_handle_frame_get(scene, target) <=
411 SEQ_time_right_handle_frame_get(scene, transformed))
412 {
414 }
415 if (SEQ_time_left_handle_frame_get(scene, transformed) <=
416 SEQ_time_right_handle_frame_get(scene, target) &&
417 SEQ_time_right_handle_frame_get(scene, target) <=
418 SEQ_time_right_handle_frame_get(scene, transformed))
419 {
421 }
422 return STRIP_OVERLAP_NONE;
423}
424
425/* Split strip in 3 parts, remove middle part and fit transformed inside. */
427 ListBase *seqbasep,
428 const Sequence *transformed,
429 Sequence *target)
430{
431 /* Because we are doing a soft split, bmain is not used in SEQ_edit_strip_split, so we can
432 * pass nullptr here. */
433 Main *bmain = nullptr;
434
435 Sequence *split_strip = SEQ_edit_strip_split(bmain,
436 scene,
437 seqbasep,
438 target,
439 SEQ_time_left_handle_frame_get(scene, transformed),
441 nullptr);
443 scene,
444 seqbasep,
445 split_strip,
446 SEQ_time_right_handle_frame_get(scene, transformed),
448 nullptr);
449 SEQ_edit_flag_for_removal(scene, seqbasep, split_strip);
450 SEQ_edit_remove_flagged_sequences(scene, seqbasep);
451}
452
453/* Trim strips by adjusting handle position.
454 * This is bit more complicated in case overlap happens on effect. */
456 ListBase *seqbasep,
457 const Sequence *transformed,
458 Sequence *target,
459 const eOvelapDescrition overlap)
460{
462 target, scene, seqbasep, SEQ_query_strip_effect_chain);
463
464 /* Expand collection by adding all target's children, effects and their children. */
465 if ((target->type & SEQ_TYPE_EFFECT) != 0) {
466 SEQ_iterator_set_expand(scene, seqbasep, targets, SEQ_query_strip_effect_chain);
467 }
468
469 /* Trim all non effects, that have influence on effect length which is overlapping. */
470 for (Sequence *seq : targets) {
471 if ((seq->type & SEQ_TYPE_EFFECT) != 0 && SEQ_effect_get_num_inputs(seq->type) > 0) {
472 continue;
473 }
474 if (overlap == STRIP_OVERLAP_LEFT_SIDE) {
476 scene, seq, SEQ_time_right_handle_frame_get(scene, transformed));
477 }
478 else {
481 scene, seq, SEQ_time_left_handle_frame_get(scene, transformed));
482 }
483 }
484}
485
487 ListBase *seqbasep,
488 blender::Span<Sequence *> transformed_strips)
489{
490 blender::VectorSet targets = query_overwrite_targets(scene, seqbasep, transformed_strips);
491 blender::VectorSet<Sequence *> strips_to_delete;
492
493 for (Sequence *target : targets) {
494 for (Sequence *transformed : transformed_strips) {
495 if (transformed->machine != target->machine) {
496 continue;
497 }
498
499 const eOvelapDescrition overlap = overlap_description_get(scene, transformed, target);
500
501 if (overlap == STRIP_OVERLAP_IS_FULL) {
502 strips_to_delete.add(target);
503 }
504 else if (overlap == STRIP_OVERLAP_IS_INSIDE) {
505 seq_transform_handle_overwrite_split(scene, seqbasep, transformed, target);
506 }
508 seq_transform_handle_overwrite_trim(scene, seqbasep, transformed, target, overlap);
509 }
510 }
511 }
512
513 /* Remove covered strips. This must be done in separate loop, because
514 * `SEQ_edit_strip_split()` also uses `SEQ_edit_remove_flagged_sequences()`. See #91096. */
515 if (!strips_to_delete.is_empty()) {
516 for (Sequence *seq : strips_to_delete) {
517 SEQ_edit_flag_for_removal(scene, seqbasep, seq);
518 }
519 SEQ_edit_remove_flagged_sequences(scene, seqbasep);
520 }
521}
522
524 ListBase *seqbasep,
525 blender::Span<Sequence *> transformed_strips,
526 blender::Span<Sequence *> time_dependent_strips,
527 bool use_sync_markers)
528{
529 ListBase *markers = &scene->markers;
530
531 /* Shuffle non strips with no effects attached. */
532 blender::VectorSet standalone_strips = extract_standalone_strips(transformed_strips);
534 standalone_strips, time_dependent_strips, seqbasep, scene, markers, use_sync_markers);
535}
536
538 ListBase *seqbasep,
539 blender::Span<Sequence *> transformed_strips,
540 bool use_sync_markers)
541{
543 SEQ_transform_handle_overlap(scene, seqbasep, transformed_strips, empty_set, use_sync_markers);
544}
545
547 ListBase *seqbasep,
548 blender::Span<Sequence *> transformed_strips,
549 blender::Span<Sequence *> time_dependent_strips,
550 bool use_sync_markers)
551{
552 const eSeqOverlapMode overlap_mode = SEQ_tool_settings_overlap_mode_get(scene);
553
554 switch (overlap_mode) {
557 scene, seqbasep, transformed_strips, time_dependent_strips, use_sync_markers);
558 break;
560 seq_transform_handle_overwrite(scene, seqbasep, transformed_strips);
561 break;
564 scene, seqbasep, transformed_strips, time_dependent_strips, use_sync_markers);
565 break;
566 }
567
568 /* If any effects still overlap, we need to move them up.
569 * In some cases other strips can be overlapping still, see #90646. */
570 for (Sequence *seq : transformed_strips) {
571 if (SEQ_transform_test_overlap(scene, seqbasep, seq)) {
572 SEQ_transform_seqbase_shuffle(seqbasep, seq, scene);
573 }
574 seq->flag &= ~SEQ_OVERLAP;
575 }
576}
577
579 ListBase *seqbase,
580 const int delta,
581 const int timeline_frame)
582{
583 LISTBASE_FOREACH (Sequence *, seq, seqbase) {
584 if (SEQ_time_left_handle_frame_get(scene, seq) >= timeline_frame) {
585 SEQ_transform_translate_sequence(scene, seq, delta);
587 }
588 }
589
590 if (!scene->toolsettings->lock_markers) {
591 LISTBASE_FOREACH (TimeMarker *, marker, &scene->markers) {
592 if (marker->frame >= timeline_frame) {
593 marker->frame += delta;
594 }
595 }
596 }
597}
598
599bool SEQ_transform_is_locked(ListBase *channels, const Sequence *seq)
600{
601 const SeqTimelineChannel *channel = SEQ_channel_get_by_index(channels, seq->machine);
602 return seq->flag & SEQ_LOCK ||
603 (SEQ_channel_is_locked(channel) && ((seq->flag & SEQ_IGNORE_CHANNEL_LOCK) == 0));
604}
605
606void SEQ_image_transform_mirror_factor_get(const Sequence *seq, float r_mirror[2])
607{
608 r_mirror[0] = 1.0f;
609 r_mirror[1] = 1.0f;
610
611 if ((seq->flag & SEQ_FLIPX) != 0) {
612 r_mirror[0] = -1.0f;
613 }
614 if ((seq->flag & SEQ_FLIPY) != 0) {
615 r_mirror[1] = -1.0f;
616 }
617}
618
620 const Sequence *seq,
621 float r_origin[2])
622{
623 float image_size[2];
624 const StripElem *strip_elem = seq->strip->stripdata;
625 if (strip_elem == nullptr) {
626 image_size[0] = scene->r.xsch;
627 image_size[1] = scene->r.ysch;
628 }
629 else {
630 image_size[0] = strip_elem->orig_width;
631 image_size[1] = strip_elem->orig_height;
632 }
633
634 const StripTransform *transform = seq->strip->transform;
635 r_origin[0] = (image_size[0] * transform->origin[0]) - (image_size[0] * 0.5f) + transform->xofs;
636 r_origin[1] = (image_size[1] * transform->origin[1]) - (image_size[1] * 0.5f) + transform->yofs;
637
638 const float viewport_pixel_aspect[2] = {scene->r.xasp / scene->r.yasp, 1.0f};
639 float mirror[2];
641 mul_v2_v2(r_origin, mirror);
642 mul_v2_v2(r_origin, viewport_pixel_aspect);
643}
644
645static void seq_image_transform_quad_get_ex(const Scene *scene,
646 const Sequence *seq,
647 bool apply_rotation,
648 float r_quad[4][2])
649{
650 StripTransform *transform = seq->strip->transform;
651 const StripCrop *crop = seq->strip->crop;
652
653 float image_size[2] = {float(scene->r.xsch), float(scene->r.ysch)};
655 image_size[0] = seq->strip->stripdata->orig_width;
656 image_size[1] = seq->strip->stripdata->orig_height;
657 }
658
659 float transform_matrix[4][4];
660 float rotation_matrix[3][3];
661 axis_angle_to_mat3_single(rotation_matrix, 'Z', apply_rotation ? transform->rotation : 0.0f);
662 loc_rot_size_to_mat4(transform_matrix,
663 blender::float3{transform->xofs, transform->yofs, 0.0f},
664 rotation_matrix,
665 blender::float3{transform->scale_x, transform->scale_y, 1.0f});
666 const float origin[2] = {image_size[0] * transform->origin[0],
667 image_size[1] * transform->origin[1]};
668 const float pivot[3] = {origin[0] - (image_size[0] / 2), origin[1] - (image_size[1] / 2), 0.0f};
669 transform_pivot_set_m4(transform_matrix, pivot);
670
671 float quad_temp[4][3];
672 for (int i = 0; i < 4; i++) {
673 zero_v3(quad_temp[i]);
674 }
675
676 quad_temp[0][0] = (image_size[0] / 2) - crop->right;
677 quad_temp[0][1] = (image_size[1] / 2) - crop->top;
678 quad_temp[1][0] = (image_size[0] / 2) - crop->right;
679 quad_temp[1][1] = (-image_size[1] / 2) + crop->bottom;
680 quad_temp[2][0] = (-image_size[0] / 2) + crop->left;
681 quad_temp[2][1] = (-image_size[1] / 2) + crop->bottom;
682 quad_temp[3][0] = (-image_size[0] / 2) + crop->left;
683 quad_temp[3][1] = (image_size[1] / 2) - crop->top;
684
685 float mirror[2];
687
688 const float viewport_pixel_aspect[2] = {scene->r.xasp / scene->r.yasp, 1.0f};
689
690 for (int i = 0; i < 4; i++) {
691 mul_m4_v3(transform_matrix, quad_temp[i]);
692 mul_v2_v2(quad_temp[i], mirror);
693 mul_v2_v2(quad_temp[i], viewport_pixel_aspect);
694 copy_v2_v2(r_quad[i], quad_temp[i]);
695 }
696}
697
699 const Sequence *seq,
700 bool apply_rotation,
701 float r_quad[4][2])
702{
703 seq_image_transform_quad_get_ex(scene, seq, apply_rotation, r_quad);
704}
705
707 const Sequence *seq,
708 float r_quad[4][2])
709{
710 seq_image_transform_quad_get_ex(scene, seq, true, r_quad);
711}
712
713void SEQ_image_preview_unit_to_px(const Scene *scene, const float co_src[2], float co_dst[2])
714{
715 co_dst[0] = co_src[0] * scene->r.xsch;
716 co_dst[1] = co_src[1] * scene->r.ysch;
717}
718
719void SEQ_image_preview_unit_from_px(const Scene *scene, const float co_src[2], float co_dst[2])
720{
721 co_dst[0] = co_src[0] / scene->r.xsch;
722 co_dst[1] = co_src[1] / scene->r.ysch;
723}
724
727 bool apply_rotation,
728 float r_min[2],
729 float r_max[2])
730{
731 INIT_MINMAX2(r_min, r_max);
732 for (Sequence *seq : strips) {
733 float quad[4][2];
734 SEQ_image_transform_quad_get(scene, seq, apply_rotation, quad);
735 for (int i = 0; i < 4; i++) {
736 minmax_v2v2_v2(r_min, r_max, quad[i]);
737 }
738 }
739}
#define BLI_assert(a)
Definition BLI_assert.h:50
BLI_INLINE bool BLI_listbase_is_empty(const struct ListBase *lb)
#define LISTBASE_FOREACH(type, var, list)
MINLINE int min_ii(int a, int b)
MINLINE int max_ii(int a, int b)
void loc_rot_size_to_mat4(float R[4][4], const float loc[3], const float rot[3][3], const float size[3])
void transform_pivot_set_m4(float mat[4][4], const float pivot[3])
void mul_m4_v3(const float M[4][4], float r[3])
void axis_angle_to_mat3_single(float R[3][3], char axis, float angle)
MINLINE void mul_v2_v2(float r[2], const float a[2])
MINLINE void copy_v2_v2(float r[2], const float a[2])
void minmax_v2v2_v2(float min[2], float max[2], const float vec[2])
MINLINE void zero_v3(float r[3])
#define INIT_MINMAX2(min, max)
#define ELEM(...)
eSeqOverlapMode
@ SEQ_OVERLAP_EXPAND
@ SEQ_OVERLAP_SHUFFLE
@ SEQ_OVERLAP_OVERWRITE
#define MAXFRAME
@ SEQ_TYPE_META
@ SEQ_TYPE_IMAGE
@ SEQ_TYPE_EFFECT
@ SEQ_TYPE_MOVIE
@ SEQ_SINGLE_FRAME_CONTENT
@ SEQ_FLIPX
@ SEQ_IGNORE_CHANNEL_LOCK
@ SEQ_FLIPY
@ SEQ_SPLIT_SOFT
Definition SEQ_edit.hh:55
constexpr int SEQ_MAX_CHANNELS
void SEQ_offset_animdata(Scene *scene, Sequence *seq, int ofs)
Definition animation.cc:69
SeqTimelineChannel * SEQ_channel_get_by_index(const ListBase *channels, const int channel_index)
Definition channels.cc:61
bool SEQ_channel_is_locked(const SeqTimelineChannel *channel)
Definition channels.cc:77
constexpr bool is_empty() const
Definition BLI_span.hh:261
constexpr bool contains(const T &value) const
Definition BLI_span.hh:278
bool add(const Key &key)
int64_t remove_if(Predicate &&predicate)
#define SELECT
draw_view in_light_buf[] float
int SEQ_effect_get_num_inputs(int seq_type)
Definition effects.cc:3467
blender::gpu::Batch * quad
const vector< Marker > & markers
VectorSet< Sequence * > SEQ_query_unselected_strips(ListBase *seqbase)
Definition iterator.cc:198
void SEQ_iterator_set_expand(const Scene *scene, ListBase *seqbase, VectorSet< Sequence * > &strips, void seq_query_func(const Scene *scene, Sequence *seq_reference, ListBase *seqbase, VectorSet< Sequence * > &strips))
Definition iterator.cc:61
void SEQ_query_strip_effect_chain(const Scene *scene, Sequence *reference_strip, ListBase *seqbase, VectorSet< Sequence * > &strips)
Definition iterator.cc:210
VectorSet< Sequence * > 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< Sequence * > &strips))
Definition iterator.cc:48
Sequence * seq_sequence_lookup_meta_by_seq(const Scene *scene, const Sequence *key)
blender::Span< Sequence * > seq_sequence_lookup_effects_by_seq(const Scene *scene, const Sequence *key)
eSeqOverlapMode SEQ_tool_settings_overlap_mode_get(Scene *scene)
Definition sequencer.cc:404
bool SEQ_is_valid_strip_channel(const Sequence *seq)
Definition sequencer.cc:696
void SEQ_edit_flag_for_removal(Scene *scene, ListBase *seqbase, Sequence *seq)
Sequence * SEQ_edit_strip_split(Main *bmain, Scene *scene, ListBase *seqbase, Sequence *seq, const int timeline_frame, const eSeqSplitMethod method, const char **r_error)
void SEQ_edit_remove_flagged_sequences(Scene *scene, ListBase *seqbase)
bool SEQ_relation_is_effect_of_strip(const Sequence *effect, const Sequence *input)
void SEQ_relations_invalidate_cache_preprocessed(Scene *scene, Sequence *seq)
void SEQ_time_right_handle_frame_set(const Scene *scene, Sequence *seq, int timeline_frame)
void SEQ_time_update_meta_strip_range(const Scene *scene, Sequence *seq_meta)
void SEQ_time_left_handle_frame_set(const Scene *scene, Sequence *seq, int timeline_frame)
int SEQ_time_left_handle_frame_get(const Scene *, const Sequence *seq)
void seq_time_translate_handles(const Scene *scene, Sequence *seq, const int offset)
void seq_time_update_effects_strip_range(const Scene *scene, const blender::Span< Sequence * > effects)
int SEQ_time_right_handle_frame_get(const Scene *scene, const Sequence *seq)
void SEQ_image_transform_quad_get(const Scene *scene, const Sequence *seq, bool apply_rotation, float r_quad[4][2])
bool SEQ_transform_seqbase_isolated_sel_check(ListBase *seqbase)
static void seq_transform_handle_overwrite(Scene *scene, ListBase *seqbasep, blender::Span< Sequence * > transformed_strips)
bool SEQ_transform_seqbase_shuffle_ex(ListBase *seqbasep, Sequence *test, Scene *evil_scene, int channel_delta)
static void seq_image_transform_quad_get_ex(const Scene *scene, const Sequence *seq, bool apply_rotation, float r_quad[4][2])
bool SEQ_transform_seqbase_shuffle(ListBase *seqbasep, Sequence *test, Scene *evil_scene)
static eOvelapDescrition overlap_description_get(const Scene *scene, const Sequence *transformed, const Sequence *target)
void SEQ_image_preview_unit_to_px(const Scene *scene, const float co_src[2], float co_dst[2])
void SEQ_image_transform_bounding_box_from_collection(Scene *scene, blender::Span< Sequence * > strips, bool apply_rotation, float r_min[2], float r_max[2])
bool SEQ_transform_test_overlap(const Scene *scene, ListBase *seqbasep, Sequence *test)
static void seq_transform_handle_overwrite_split(Scene *scene, ListBase *seqbasep, const Sequence *transformed, Sequence *target)
static blender::VectorSet< Sequence * > extract_standalone_strips(blender::Span< Sequence * > transformed_strips)
static void seq_transform_handle_overlap_shuffle(Scene *scene, ListBase *seqbasep, blender::Span< Sequence * > transformed_strips, blender::Span< Sequence * > time_dependent_strips, bool use_sync_markers)
void SEQ_image_transform_origin_offset_pixelspace_get(const Scene *scene, const Sequence *seq, float r_origin[2])
void SEQ_image_transform_final_quad_get(const Scene *scene, const Sequence *seq, float r_quad[4][2])
bool SEQ_transform_seqbase_shuffle_time(blender::Span< Sequence * > strips_to_shuffle, ListBase *seqbasep, Scene *evil_scene, ListBase *markers, const bool use_sync_markers)
static bool shuffle_seq_test_overlap(const Scene *scene, const Sequence *seq1, const Sequence *seq2, const int offset)
static blender::VectorSet< Sequence * > query_right_side_strips(const Scene *scene, ListBase *seqbase, blender::Span< Sequence * > transformed_strips, blender::Span< Sequence * > time_dependent_strips)
static void seq_transform_handle_expand_to_fit(Scene *scene, ListBase *seqbasep, blender::Span< Sequence * > transformed_strips, blender::Span< Sequence * > time_dependent_strips, bool use_sync_markers)
void SEQ_transform_translate_sequence(Scene *evil_scene, Sequence *seq, int delta)
static blender::VectorSet< Sequence * > query_overwrite_targets(const Scene *scene, ListBase *seqbasep, blender::Span< Sequence * > transformed_strips)
void SEQ_image_transform_mirror_factor_get(const Sequence *seq, float r_mirror[2])
bool SEQ_transform_is_locked(ListBase *channels, const Sequence *seq)
eOvelapDescrition
@ STRIP_OVERLAP_RIGHT_SIDE
@ STRIP_OVERLAP_LEFT_SIDE
@ STRIP_OVERLAP_IS_INSIDE
@ STRIP_OVERLAP_IS_FULL
@ STRIP_OVERLAP_NONE
void SEQ_transform_handle_overlap(Scene *scene, ListBase *seqbasep, blender::Span< Sequence * > transformed_strips, bool use_sync_markers)
void SEQ_image_preview_unit_from_px(const Scene *scene, const float co_src[2], float co_dst[2])
bool SEQ_transform_sequence_can_be_translated(const Sequence *seq)
static void seq_transform_handle_overwrite_trim(Scene *scene, ListBase *seqbasep, const Sequence *transformed, Sequence *target, const eOvelapDescrition overlap)
bool SEQ_transform_test_overlap_seq_seq(const Scene *scene, Sequence *seq1, Sequence *seq2)
static int shuffle_seq_time_offset_get(const Scene *scene, blender::Span< Sequence * > strips_to_shuffle, ListBase *seqbasep, char dir)
bool SEQ_transform_single_image_check(const Sequence *seq)
void SEQ_transform_offset_after_frame(Scene *scene, ListBase *seqbase, const int delta, const int timeline_frame)
void * first
struct ToolSettings * toolsettings
struct Sequence * seq1
struct Sequence * seq2
struct Sequence * next
StripTransform * transform
StripElem * stripdata
StripCrop * crop