Blender V5.0
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
10
11#include "DNA_scene_types.h"
12#include "DNA_sequence_types.h"
13
14#include "BLI_bounds.hh"
15#include "BLI_listbase.h"
16#include "BLI_math_base.h"
17#include "BLI_math_base.hh"
18#include "BLI_math_matrix.hh"
20#include "BLI_rect.h"
21
22#include "BLF_api.hh"
23
24#include "SEQ_animation.hh"
25#include "SEQ_channels.hh"
26#include "SEQ_edit.hh"
27#include "SEQ_effects.hh"
28#include "SEQ_iterator.hh"
29#include "SEQ_relations.hh"
30#include "SEQ_sequencer.hh"
31#include "SEQ_time.hh"
32#include "SEQ_transform.hh"
33
34#include "effects/effects.hh"
35#include "sequencer.hh"
36#include "strip_time.hh"
37
38namespace blender::seq {
39
41{
42 return (strip->flag & SEQ_SINGLE_FRAME_CONTENT) != 0;
43}
44
46{
47 return !strip->is_effect() || (effect_get_num_inputs(strip->type) == 0);
48}
49
50bool transform_test_overlap(const Scene *scene, Strip *strip1, Strip *strip2)
51{
52 return (
53 strip1 != strip2 && strip1->channel == strip2->channel &&
54 ((time_right_handle_frame_get(scene, strip1) <= time_left_handle_frame_get(scene, strip2)) ||
55 (time_left_handle_frame_get(scene, strip1) >=
56 time_right_handle_frame_get(scene, strip2))) == 0);
57}
58
59bool transform_test_overlap(const Scene *scene, ListBase *seqbasep, Strip *test)
60{
61 Strip *strip;
62
63 strip = static_cast<Strip *>(seqbasep->first);
64 while (strip) {
65 if (transform_test_overlap(scene, test, strip)) {
66 return true;
67 }
68
69 strip = strip->next;
70 }
71 return false;
72}
73
74void transform_translate_strip(Scene *evil_scene, Strip *strip, int delta)
75{
76 if (delta == 0) {
77 return;
78 }
79
80 /* Meta strips requires their content is to be translated, and then frame range of the meta is
81 * updated based on nested strips. This won't work for empty meta-strips,
82 * so they can be treated as normal strip. */
83 if (strip->type == STRIP_TYPE_META && !BLI_listbase_is_empty(&strip->seqbase)) {
84 LISTBASE_FOREACH (Strip *, strip_child, &strip->seqbase) {
85 transform_translate_strip(evil_scene, strip_child, delta);
86 }
87 /* Move meta start/end points. */
88 strip_time_translate_handles(evil_scene, strip, delta);
89 }
90 else if (strip->input1 == nullptr && strip->input2 == nullptr) { /* All other strip types. */
91 strip->start += delta;
92 /* Only to make files usable in older versions. */
93 strip->startdisp = time_left_handle_frame_get(evil_scene, strip);
94 strip->enddisp = time_right_handle_frame_get(evil_scene, strip);
95 }
96
97 offset_animdata(evil_scene, strip, delta);
98 blender::Span<Strip *> effects = SEQ_lookup_effects_by_strip(evil_scene->ed, strip);
99 strip_time_update_effects_strip_range(evil_scene, effects);
100 time_update_meta_strip_range(evil_scene, lookup_meta_by_strip(evil_scene->ed, strip));
101}
102
104 Strip *test,
105 Scene *evil_scene,
106 int channel_delta)
107{
108 const int orig_channel = test->channel;
109 BLI_assert(ELEM(channel_delta, -1, 1));
110
111 strip_channel_set(test, test->channel + channel_delta);
112
113 const ListBase *channels = channels_displayed_get(editing_get(evil_scene));
114 SeqTimelineChannel *channel = channel_get_by_index(channels, test->channel);
115
116 bool use_fallback_translation = false;
117
118 while (transform_test_overlap(evil_scene, seqbasep, test) || channel_is_muted(channel) ||
119 channel_is_locked(channel))
120 {
121 if ((channel_delta > 0) ? (test->channel + channel_delta >= MAX_CHANNELS) :
122 (test->channel + channel_delta < 1))
123 {
124 use_fallback_translation = true;
125 break;
126 }
127
128 strip_channel_set(test, test->channel + channel_delta);
129 channel = channel_get_by_index(channels, test->channel);
130 }
131
132 /* Strip can not be moved to next free channel, translate it instead. */
133 if (use_fallback_translation) {
134 int new_frame = time_right_handle_frame_get(evil_scene, test);
135
136 LISTBASE_FOREACH (Strip *, strip, seqbasep) {
137 if (strip->channel == orig_channel) {
138 new_frame = max_ii(new_frame, time_right_handle_frame_get(evil_scene, strip));
139 }
140 }
141
142 strip_channel_set(test, orig_channel);
143
144 new_frame = new_frame + (test->start - time_left_handle_frame_get(
145 evil_scene, test)); /* adjust by the startdisp */
146 transform_translate_strip(evil_scene, test, new_frame - test->start);
147 return false;
148 }
149
150 return true;
151}
152
153bool transform_seqbase_shuffle(ListBase *seqbasep, Strip *test, Scene *evil_scene)
154{
155 return transform_seqbase_shuffle_ex(seqbasep, test, evil_scene, 1);
156}
157
158static bool shuffle_strip_test_overlap(const Scene *scene,
159 const Strip *strip1,
160 const Strip *strip2,
161 const int offset)
162{
163 BLI_assert(strip1 != strip2);
164 return (strip1->channel == strip2->channel &&
165 ((time_right_handle_frame_get(scene, strip1) + offset <=
166 time_left_handle_frame_get(scene, strip2)) ||
167 (time_left_handle_frame_get(scene, strip1) + offset >=
168 time_right_handle_frame_get(scene, strip2))) == 0);
169}
170
171static int shuffle_strip_time_offset_get(const Scene *scene,
172 blender::Span<Strip *> strips_to_shuffle,
173 ListBase *seqbasep,
174 char dir)
175{
176 int offset = 0;
177 bool all_conflicts_resolved = false;
178
179 while (!all_conflicts_resolved) {
180 all_conflicts_resolved = true;
181 for (Strip *strip : strips_to_shuffle) {
182 LISTBASE_FOREACH (Strip *, strip_other, seqbasep) {
183 if (strips_to_shuffle.contains(strip_other)) {
184 continue;
185 }
186 if (relation_is_effect_of_strip(strip_other, strip)) {
187 continue;
188 }
189 if (!shuffle_strip_test_overlap(scene, strip, strip_other, offset)) {
190 continue;
191 }
192
193 all_conflicts_resolved = false;
194
195 if (dir == 'L') {
196 offset = min_ii(offset,
197 time_left_handle_frame_get(scene, strip_other) -
198 time_right_handle_frame_get(scene, strip));
199 }
200 else {
201 offset = max_ii(offset,
202 time_right_handle_frame_get(scene, strip_other) -
203 time_left_handle_frame_get(scene, strip));
204 }
205 }
206 }
207 }
208
209 return offset;
210}
211
213 ListBase *seqbasep,
214 Scene *evil_scene,
215 ListBase *markers,
216 const bool use_sync_markers)
217{
220 strips_to_shuffle, empty_set, seqbasep, evil_scene, markers, use_sync_markers);
221}
222
224 blender::Span<Strip *> time_dependent_strips,
225 ListBase *seqbasep,
226 Scene *evil_scene,
227 ListBase *markers,
228 const bool use_sync_markers)
229{
230 int offset_l = shuffle_strip_time_offset_get(evil_scene, strips_to_shuffle, seqbasep, 'L');
231 int offset_r = shuffle_strip_time_offset_get(evil_scene, strips_to_shuffle, seqbasep, 'R');
232 int offset = (-offset_l < offset_r) ? offset_l : offset_r;
233
234 if (offset) {
235 for (Strip *strip : strips_to_shuffle) {
236 transform_translate_strip(evil_scene, strip, offset);
237 strip->runtime.flag &= ~STRIP_OVERLAP;
238 }
239
240 if (!time_dependent_strips.is_empty()) {
241 for (Strip *strip : time_dependent_strips) {
242 offset_animdata(evil_scene, strip, offset);
243 }
244 }
245
246 if (use_sync_markers && !(evil_scene->toolsettings->lock_markers) && (markers != nullptr)) {
247 /* affect selected markers - it's unlikely that we will want to affect all in this way? */
248 LISTBASE_FOREACH (TimeMarker *, marker, markers) {
249 if (marker->flag & SELECT) {
250 marker->frame += offset;
251 }
252 }
253 }
254 }
255
256 return offset ? false : true;
257}
258
260 blender::Span<Strip *> transformed_strips)
261{
262 blender::VectorSet<Strip *> standalone_strips;
263
264 for (Strip *strip : transformed_strips) {
265 if (!strip->is_effect() || strip->input1 == nullptr) {
266 standalone_strips.add(strip);
267 }
268 }
269 return standalone_strips;
270}
271
272/* Query strips positioned after left edge of transformed strips bound-box. */
274 const Scene *scene,
275 ListBase *seqbase,
276 blender::Span<Strip *> transformed_strips,
277 blender::Span<Strip *> time_dependent_strips)
278{
279 int minframe = MAXFRAME;
280 {
281 for (Strip *strip : transformed_strips) {
282 minframe = min_ii(minframe, time_left_handle_frame_get(scene, strip));
283 }
284 }
285
286 blender::VectorSet<Strip *> right_side_strips;
287 LISTBASE_FOREACH (Strip *, strip, seqbase) {
288 if (!time_dependent_strips.is_empty() && time_dependent_strips.contains(strip)) {
289 continue;
290 }
291 if (transformed_strips.contains(strip)) {
292 continue;
293 }
294
295 if ((strip->flag & SELECT) == 0 && time_left_handle_frame_get(scene, strip) >= minframe) {
296 right_side_strips.add(strip);
297 }
298 }
299 return right_side_strips;
300}
301
302/* Offset all strips positioned after left edge of transformed strips bound-box by amount equal
303 * to overlap of transformed strips. */
305 ListBase *seqbasep,
306 blender::Span<Strip *> transformed_strips,
307 blender::Span<Strip *> time_dependent_strips,
308 bool use_sync_markers)
309{
310 ListBase *markers = &scene->markers;
311
313 scene, seqbasep, transformed_strips, time_dependent_strips);
314
315 /* Temporarily move right side strips beyond timeline boundary. */
316 for (Strip *strip : right_side_strips) {
317 strip->channel += MAX_CHANNELS * 2;
318 }
319
320 /* Shuffle transformed standalone strips. This is because transformed strips can overlap with
321 * strips on left side. */
322 blender::VectorSet standalone_strips = extract_standalone_strips(transformed_strips);
324 standalone_strips, time_dependent_strips, seqbasep, scene, markers, use_sync_markers);
325
326 /* Move temporarily moved strips back to their original place and tag for shuffling. */
327 for (Strip *strip : right_side_strips) {
328 strip->channel -= MAX_CHANNELS * 2;
329 }
330 /* Shuffle again to displace strips on right side. Final effect shuffling is done in
331 * SEQ_transform_handle_overlap. */
332 transform_seqbase_shuffle_time(right_side_strips, seqbasep, scene, markers, use_sync_markers);
333}
334
336 const Scene *scene, ListBase *seqbasep, blender::Span<Strip *> transformed_strips)
337{
338 blender::VectorSet<Strip *> overwrite_targets = query_unselected_strips(seqbasep);
339
340 /* Effects of transformed strips can be unselected. These must not be included. */
341 overwrite_targets.remove_if([&](Strip *strip) { return transformed_strips.contains(strip); });
342 overwrite_targets.remove_if([&](Strip *strip) {
343 bool does_overlap = false;
344 for (Strip *strip_transformed : transformed_strips) {
345 if (transform_test_overlap(scene, strip, strip_transformed)) {
346 does_overlap = true;
347 }
348 }
349
350 return !does_overlap;
351 });
352
353 return overwrite_targets;
354}
355
357 /* No overlap. */
359 /* Overlapping strip covers overlapped completely. */
361 /* Overlapping strip is inside overlapped. */
363 /* Partial overlap between 2 strips. */
366};
367
369 const Strip *transformed,
370 const Strip *target)
371{
372 if (time_left_handle_frame_get(scene, transformed) <=
373 time_left_handle_frame_get(scene, target) &&
374 time_right_handle_frame_get(scene, transformed) >=
375 time_right_handle_frame_get(scene, target))
376 {
378 }
379 if (time_left_handle_frame_get(scene, transformed) > time_left_handle_frame_get(scene, target) &&
380 time_right_handle_frame_get(scene, transformed) < time_right_handle_frame_get(scene, target))
381 {
383 }
384 if (time_left_handle_frame_get(scene, transformed) <=
385 time_left_handle_frame_get(scene, target) &&
386 time_left_handle_frame_get(scene, target) <= time_right_handle_frame_get(scene, transformed))
387 {
389 }
390 if (time_left_handle_frame_get(scene, transformed) <=
391 time_right_handle_frame_get(scene, target) &&
392 time_right_handle_frame_get(scene, target) <=
393 time_right_handle_frame_get(scene, transformed))
394 {
396 }
397 return STRIP_OVERLAP_NONE;
398}
399
400/* Split strip in 3 parts, remove middle part and fit transformed inside. */
402 ListBase *seqbasep,
403 const Strip *transformed,
404 Strip *target)
405{
406 /* Because we are doing a soft split, bmain is not used in SEQ_edit_strip_split, so we can
407 * pass nullptr here. */
408 Main *bmain = nullptr;
409 const char *error_msg = nullptr;
410 Strip *split_strip = edit_strip_split(bmain,
411 scene,
412 seqbasep,
413 target,
414 time_left_handle_frame_get(scene, transformed),
416 true,
417 &error_msg);
418 if (split_strip == nullptr) {
419 return;
420 }
421
422 error_msg = nullptr;
423 if (edit_strip_split(bmain,
424 scene,
425 seqbasep,
426 split_strip,
427 time_right_handle_frame_get(scene, transformed),
429 true,
430 &error_msg) == nullptr)
431 {
432 return;
433 }
434 edit_flag_for_removal(scene, seqbasep, split_strip);
435 edit_remove_flagged_strips(scene, seqbasep);
436}
437
438/* Trim strips by adjusting handle position.
439 * This is bit more complicated in case overlap happens on effect. */
441 ListBase *seqbasep,
442 const Strip *transformed,
443 Strip *target,
444 const eOvelapDescrition overlap)
445{
447 target, scene, seqbasep, query_strip_effect_chain);
448
449 /* Expand collection by adding all target's children, effects and their children. */
450 if (target->is_effect()) {
451 iterator_set_expand(scene, seqbasep, targets, query_strip_effect_chain);
452 }
453
454 /* Trim all non effects, that have influence on effect length which is overlapping. */
455 for (Strip *strip : targets) {
456 if (strip->is_effect() && effect_get_num_inputs(strip->type) > 0) {
457 continue;
458 }
459 if (overlap == STRIP_OVERLAP_LEFT_SIDE) {
460 time_left_handle_frame_set(scene, strip, time_right_handle_frame_get(scene, transformed));
461 }
462 else {
464 time_right_handle_frame_set(scene, strip, time_left_handle_frame_get(scene, transformed));
465 }
466 }
467}
468
470 ListBase *seqbasep,
471 blender::Span<Strip *> transformed_strips)
472{
473 blender::VectorSet targets = query_overwrite_targets(scene, seqbasep, transformed_strips);
474 blender::VectorSet<Strip *> strips_to_delete;
475
476 ListBase *channels = channels_displayed_get(editing_get(scene));
477 for (Strip *target : targets) {
478 for (Strip *transformed : transformed_strips) {
479 if (transformed->channel != target->channel) {
480 continue;
481 }
482 /* Do not allow overwriting/trimming/deleting locked strips. */
483 if (transform_is_locked(channels, target)) {
484 continue;
485 }
486
487 const eOvelapDescrition overlap = overlap_description_get(scene, transformed, target);
488
489 if (overlap == STRIP_OVERLAP_IS_FULL) {
490 strips_to_delete.add(target);
491 }
492 else if (overlap == STRIP_OVERLAP_IS_INSIDE) {
493 strip_transform_handle_overwrite_split(scene, seqbasep, transformed, target);
494 }
496 strip_transform_handle_overwrite_trim(scene, seqbasep, transformed, target, overlap);
497 }
498 }
499 }
500
501 /* Remove covered strips. This must be done in separate loop, because
502 * `SEQ_edit_strip_split()` also uses `SEQ_edit_remove_flagged_sequences()`. See #91096. */
503 if (!strips_to_delete.is_empty()) {
504 for (Strip *strip : strips_to_delete) {
505 edit_flag_for_removal(scene, seqbasep, strip);
506 }
507 edit_remove_flagged_strips(scene, seqbasep);
508 }
509}
510
512 ListBase *seqbasep,
513 blender::Span<Strip *> transformed_strips,
514 blender::Span<Strip *> time_dependent_strips,
515 bool use_sync_markers)
516{
517 ListBase *markers = &scene->markers;
518
519 /* Shuffle non strips with no effects attached. */
520 blender::VectorSet standalone_strips = extract_standalone_strips(transformed_strips);
522 standalone_strips, time_dependent_strips, seqbasep, scene, markers, use_sync_markers);
523}
524
526 ListBase *seqbasep,
527 blender::Span<Strip *> transformed_strips,
528 bool use_sync_markers)
529{
531 transform_handle_overlap(scene, seqbasep, transformed_strips, empty_set, use_sync_markers);
532}
533
535 ListBase *seqbasep,
536 blender::Span<Strip *> transformed_strips,
537 blender::Span<Strip *> time_dependent_strips,
538 bool use_sync_markers)
539{
540 const eSeqOverlapMode overlap_mode = tool_settings_overlap_mode_get(scene);
541
542 switch (overlap_mode) {
545 scene, seqbasep, transformed_strips, time_dependent_strips, use_sync_markers);
546 break;
548 strip_transform_handle_overwrite(scene, seqbasep, transformed_strips);
549 break;
552 scene, seqbasep, transformed_strips, time_dependent_strips, use_sync_markers);
553 break;
554 }
555
556 /* If any effects still overlap, we need to move them up.
557 * In some cases other strips can be overlapping still, see #90646. */
558 for (Strip *strip : transformed_strips) {
559 if (transform_test_overlap(scene, seqbasep, strip)) {
560 transform_seqbase_shuffle(seqbasep, strip, scene);
561 }
562 strip->runtime.flag &= ~STRIP_OVERLAP;
563 }
564}
565
567 ListBase *seqbase,
568 const int delta,
569 const int timeline_frame)
570{
571 LISTBASE_FOREACH (Strip *, strip, seqbase) {
572 if (time_left_handle_frame_get(scene, strip) >= timeline_frame) {
573 transform_translate_strip(scene, strip, delta);
574 relations_invalidate_cache(scene, strip);
575 }
576 }
577
578 if (!scene->toolsettings->lock_markers) {
579 LISTBASE_FOREACH (TimeMarker *, marker, &scene->markers) {
580 if (marker->frame >= timeline_frame) {
581 marker->frame += delta;
582 }
583 }
584 }
585}
586
587void strip_channel_set(Strip *strip, int channel)
588{
589 strip->channel = math::clamp(channel, 1, MAX_CHANNELS);
590}
591
592bool transform_is_locked(ListBase *channels, const Strip *strip)
593{
594 const SeqTimelineChannel *channel = channel_get_by_index(channels, strip->channel);
595 return strip->flag & SEQ_LOCK ||
596 (channel_is_locked(channel) && ((strip->runtime.flag & STRIP_IGNORE_CHANNEL_LOCK) == 0));
597}
598
600{
601 float2 mirror(1.0f, 1.0f);
602
603 if ((strip->flag & SEQ_FLIPX) != 0) {
604 mirror.x = -1.0f;
605 }
606 if ((strip->flag & SEQ_FLIPY) != 0) {
607 mirror.y = -1.0f;
608 }
609 return mirror;
610}
611
613{
614 float2 scene_render_size(scene->r.xsch, scene->r.ysch);
615
617 const StripElem *selem = strip->data->stripdata;
618 return {float(selem->orig_width), float(selem->orig_height)};
619 }
620
621 if (strip->type == STRIP_TYPE_MOVIECLIP) {
622 const MovieClip *clip = strip->clip;
623 if (clip != nullptr && clip->lastsize[0] != 0 && clip->lastsize[1] != 0) {
624 return {float(clip->lastsize[0]), float(clip->lastsize[1])};
625 }
626 }
627
628 if (strip->type == STRIP_TYPE_TEXT) {
629 const TextVars *data = static_cast<TextVars *>(strip->effectdata);
630 const FontFlags font_flags = ((data->flag & SEQ_TEXT_BOLD) ? BLF_BOLD : BLF_NONE) |
631 ((data->flag & SEQ_TEXT_ITALIC) ? BLF_ITALIC : BLF_NONE);
632 const int font = text_effect_font_init(nullptr, strip, font_flags);
634 strip, font, int2(scene_render_size));
635 BLF_disable(font, font_flags);
636
637 const float2 text_size(float(BLI_rcti_size_x(&runtime->text_boundbox)),
638 float(BLI_rcti_size_y(&runtime->text_boundbox)));
639 MEM_delete(runtime);
640 return text_size;
641 }
642
643 return scene_render_size;
644}
645
646float2 image_transform_origin_get(const Scene *scene, const Strip *strip)
647{
648
649 const StripTransform *transform = strip->data->transform;
650 if (strip->type != STRIP_TYPE_TEXT) {
651 return {transform->origin[0], transform->origin[1]};
652 }
653
654 /* Text image size is different from true image size, so the origin position must be
655 * calculated. */
656 float2 scene_render_size(scene->r.xsch, scene->r.ysch);
657 const float2 text_image_size = transform_image_raw_size_get(scene, strip);
658 const float2 scale = text_image_size / scene_render_size;
659 const float2 origin_rel(transform->origin[0], transform->origin[1]);
660 const float2 origin_center(0.5f, 0.5f);
661 const float2 origin_diff = origin_rel - origin_center;
662
663 const float2 true_origin_relative = origin_center + origin_diff * scale;
664 return true_origin_relative;
665}
666
668{
669 const StripTransform *transform = strip->data->transform;
670 const float2 image_size = transform_image_raw_size_get(scene, strip);
671 const float2 origin_relative(transform->origin[0], transform->origin[1]);
672 const float2 translation(transform->xofs, transform->yofs);
673 const float2 origin_pos_pixels = (image_size * origin_relative) - (image_size * 0.5f) +
674 translation;
675 const float2 viewport_pixel_aspect(scene->r.xasp / scene->r.yasp, 1.0f);
676 const float2 mirror = image_transform_mirror_factor_get(strip);
677 return origin_pos_pixels * mirror * viewport_pixel_aspect;
678}
679
681 const Strip *strip,
682 bool apply_rotation = true)
683{
684 const StripTransform *transform = strip->data->transform;
685 const float2 image_size = transform_image_raw_size_get(scene, strip);
686 const float2 origin_relative(transform->origin[0], transform->origin[1]);
687 const float2 origin_absolute = image_size * origin_relative;
688 const float2 translation(transform->xofs, transform->yofs);
689 const float rotation = apply_rotation ? transform->rotation : 0.0f;
690 const float2 scale(transform->scale_x, transform->scale_y);
691 const float2 pivot = origin_absolute - (image_size / 2);
692
693 const float3x3 matrix = math::from_loc_rot_scale<float3x3>(translation, rotation, scale);
694 return math::from_origin_transform(matrix, pivot);
695}
696
698{
699 return seq_image_transform_matrix_get_ex(scene, strip);
700}
701
703 const Strip *strip,
704 bool apply_rotation)
705{
706 const float2 image_size = transform_image_raw_size_get(scene, strip);
707
708 const StripCrop *crop = strip->data->crop;
709 float2 quad[4]{
710 {(image_size[0] / 2) - crop->right, (image_size[1] / 2) - crop->top},
711 {(image_size[0] / 2) - crop->right, (-image_size[1] / 2) + crop->bottom},
712 {(-image_size[0] / 2) + crop->left, (-image_size[1] / 2) + crop->bottom},
713 {(-image_size[0] / 2) + crop->left, (image_size[1] / 2) - crop->top},
714 };
715
716 const float3x3 matrix = seq_image_transform_matrix_get_ex(scene, strip, apply_rotation);
717 const float2 viewport_pixel_aspect(scene->r.xasp / scene->r.yasp, 1.0f);
718 const float2 mirror = image_transform_mirror_factor_get(strip);
719
720 Array<float2> quad_transformed;
721 quad_transformed.reinitialize(4);
722
723 for (int i = 0; i < 4; i++) {
724 const float2 point = math::transform_point(matrix, quad[i]);
725 quad_transformed[i] = point * mirror * viewport_pixel_aspect;
726 }
727 return quad_transformed;
728}
729
730Array<float2> image_transform_quad_get(const Scene *scene, const Strip *strip, bool apply_rotation)
731{
732 return strip_image_transform_quad_get_ex(scene, strip, apply_rotation);
733}
734
736{
737 return strip_image_transform_quad_get_ex(scene, strip, true);
738}
739
740float2 image_preview_unit_to_px(const Scene *scene, const float2 co_src)
741{
742 return {co_src.x * scene->r.xsch, co_src.y * scene->r.ysch};
743}
744
746{
747 return {co_src.x / scene->r.xsch, co_src.y / scene->r.ysch};
748}
749
751{
752 return {float2(std::numeric_limits<float>::max()), float2(std::numeric_limits<float>::lowest())};
753}
754
757 bool apply_rotation)
758{
760
761 for (Strip *strip : strips) {
762 const Array<float2> quad = image_transform_quad_get(scene, strip, apply_rotation);
763 const Bounds<float2> strip_box = *blender::bounds::min_max(quad.as_span());
764 box = blender::bounds::merge(box, strip_box);
765 }
766
767 return box;
768}
769
770} // namespace blender::seq
void BLF_disable(int fontid, FontFlags flag)
Definition blf.cc:329
FontFlags
Definition BLF_enums.hh:31
@ BLF_ITALIC
Definition BLF_enums.hh:46
@ BLF_NONE
Definition BLF_enums.hh:32
@ BLF_BOLD
Definition BLF_enums.hh:45
#define BLI_assert(a)
Definition BLI_assert.h:46
#define LISTBASE_FOREACH(type, var, list)
BLI_INLINE bool BLI_listbase_is_empty(const ListBase *lb)
MINLINE int min_ii(int a, int b)
MINLINE int max_ii(int a, int b)
BLI_INLINE int BLI_rcti_size_y(const struct rcti *rct)
Definition BLI_rect.h:198
BLI_INLINE int BLI_rcti_size_x(const struct rcti *rct)
Definition BLI_rect.h:194
#define ELEM(...)
eSeqOverlapMode
@ SEQ_OVERLAP_EXPAND
@ SEQ_OVERLAP_SHUFFLE
@ SEQ_OVERLAP_OVERWRITE
#define MAXFRAME
@ SEQ_TEXT_ITALIC
@ SEQ_TEXT_BOLD
@ STRIP_IGNORE_CHANNEL_LOCK
@ STRIP_OVERLAP
@ STRIP_TYPE_MOVIECLIP
@ STRIP_TYPE_TEXT
@ STRIP_TYPE_IMAGE
@ STRIP_TYPE_MOVIE
@ STRIP_TYPE_META
@ SEQ_SINGLE_FRAME_CONTENT
@ SEQ_FLIPX
@ SEQ_FLIPY
BMesh const char void * data
SIMD_FORCE_INLINE btVector3 transform(const btVector3 &point) const
void reinitialize(const int64_t new_size)
Definition BLI_array.hh:419
constexpr bool is_empty() const
Definition BLI_span.hh:260
constexpr bool contains(const T &value) const
Definition BLI_span.hh:277
bool add(const Key &key)
int64_t remove_if(Predicate &&predicate)
nullptr float
#define SELECT
blender::gpu::Batch * quad
Bounds< T > merge(const Bounds< T > &a, const Bounds< T > &b)
Definition BLI_bounds.hh:26
std::optional< Bounds< T > > min_max(const std::optional< Bounds< T > > &a, const T &b)
Definition BLI_bounds.hh:55
T clamp(const T &a, const T &min, const T &max)
MatT from_origin_transform(const MatT &transform, const VectorT origin)
MatT from_loc_rot_scale(const typename MatT::loc_type &location, const RotationT &rotation, const VecBase< typename MatT::base_type, ScaleDim > &scale)
VecBase< T, 3 > transform_point(const CartesianBasis &basis, const VecBase< T, 3 > &v)
Strip * lookup_meta_by_strip(Editing *ed, const Strip *key)
int time_right_handle_frame_get(const Scene *scene, const Strip *strip)
static void strip_transform_handle_overwrite_split(Scene *scene, ListBase *seqbasep, const Strip *transformed, Strip *target)
float2 transform_image_raw_size_get(const Scene *scene, const Strip *strip)
int text_effect_font_init(const RenderData *context, const Strip *strip, FontFlags font_flags)
Array< float2 > image_transform_final_quad_get(const Scene *scene, const Strip *strip)
void relations_invalidate_cache(Scene *scene, Strip *strip)
static void strip_transform_handle_overlap_shuffle(Scene *scene, ListBase *seqbasep, blender::Span< Strip * > transformed_strips, blender::Span< Strip * > time_dependent_strips, bool use_sync_markers)
static void strip_transform_handle_overwrite_trim(Scene *scene, ListBase *seqbasep, const Strip *transformed, Strip *target, const eOvelapDescrition overlap)
SeqTimelineChannel * channel_get_by_index(const ListBase *channels, const int channel_index)
Definition channels.cc:60
ListBase * channels_displayed_get(const Editing *ed)
Definition channels.cc:28
void edit_remove_flagged_strips(Scene *scene, ListBase *seqbase)
static blender::VectorSet< Strip * > query_overwrite_targets(const Scene *scene, ListBase *seqbasep, blender::Span< Strip * > transformed_strips)
Bounds< float2 > image_transform_bounding_box_from_collection(Scene *scene, blender::Span< Strip * > strips, bool apply_rotation)
void edit_flag_for_removal(Scene *scene, ListBase *seqbase, Strip *strip)
float3x3 image_transform_matrix_get(const Scene *scene, const Strip *strip)
void transform_translate_strip(Scene *evil_scene, Strip *strip, int delta)
bool channel_is_locked(const SeqTimelineChannel *channel)
Definition channels.cc:76
VectorSet< Strip * > query_unselected_strips(ListBase *seqbase)
Definition iterator.cc:242
Array< float2 > image_transform_quad_get(const Scene *scene, const Strip *strip, bool apply_rotation)
void strip_channel_set(Strip *strip, int channel)
TextVarsRuntime * text_effect_calc_runtime(const Strip *strip, int font, const int2 image_size)
static void strip_transform_handle_overwrite(Scene *scene, ListBase *seqbasep, blender::Span< Strip * > transformed_strips)
bool transform_test_overlap(const Scene *scene, Strip *strip1, Strip *strip2)
Editing * editing_get(const Scene *scene)
Definition sequencer.cc:286
static blender::VectorSet< Strip * > extract_standalone_strips(blender::Span< Strip * > transformed_strips)
blender::Span< Strip * > SEQ_lookup_effects_by_strip(Editing *ed, const Strip *key)
static eOvelapDescrition overlap_description_get(const Scene *scene, const Strip *transformed, const Strip *target)
int time_left_handle_frame_get(const Scene *, const Strip *strip)
void offset_animdata(const Scene *scene, Strip *strip, float ofs)
Definition animation.cc:42
float2 image_transform_mirror_factor_get(const Strip *strip)
void query_strip_effect_chain(const Scene *scene, Strip *reference_strip, ListBase *seqbase, VectorSet< Strip * > &r_strips)
Definition iterator.cc:254
void strip_time_update_effects_strip_range(const Scene *scene, const blender::Span< Strip * > effects)
bool transform_single_image_check(const Strip *strip)
constexpr int MAX_CHANNELS
void time_update_meta_strip_range(const Scene *scene, Strip *strip_meta)
void iterator_set_expand(const Scene *scene, ListBase *seqbase, VectorSet< Strip * > &strips, void strip_query_func(const Scene *scene, Strip *strip_reference, ListBase *seqbase, VectorSet< Strip * > &strips))
Definition iterator.cc:82
static void strip_transform_handle_expand_to_fit(Scene *scene, ListBase *seqbasep, blender::Span< Strip * > transformed_strips, blender::Span< Strip * > time_dependent_strips, bool use_sync_markers)
void strip_time_translate_handles(const Scene *scene, Strip *strip, const int offset)
bool transform_seqbase_shuffle_ex(ListBase *seqbasep, Strip *test, Scene *evil_scene, int channel_delta)
static bool shuffle_strip_test_overlap(const Scene *scene, const Strip *strip1, const Strip *strip2, const int offset)
void transform_offset_after_frame(Scene *scene, ListBase *seqbase, const int delta, const int timeline_frame)
float2 image_transform_origin_offset_pixelspace_get(const Scene *scene, const Strip *strip)
static Array< float2 > strip_image_transform_quad_get_ex(const Scene *scene, const Strip *strip, bool apply_rotation)
float2 image_transform_origin_get(const Scene *scene, const Strip *strip)
static Bounds< float2 > negative_bounds()
VectorSet< Strip * > query_by_reference(Strip *strip_reference, const Scene *scene, ListBase *seqbase, void strip_query_func(const Scene *scene, Strip *strip_reference, ListBase *seqbase, VectorSet< Strip * > &strips))
Definition iterator.cc:69
bool transform_seqbase_shuffle_time(blender::Span< Strip * > strips_to_shuffle, ListBase *seqbasep, Scene *evil_scene, ListBase *markers, const bool use_sync_markers)
bool transform_is_locked(ListBase *channels, const Strip *strip)
static blender::VectorSet< Strip * > query_right_side_strips(const Scene *scene, ListBase *seqbase, blender::Span< Strip * > transformed_strips, blender::Span< Strip * > time_dependent_strips)
bool relation_is_effect_of_strip(const Strip *effect, const Strip *input)
void time_left_handle_frame_set(const Scene *scene, Strip *strip, int timeline_frame)
static int shuffle_strip_time_offset_get(const Scene *scene, blender::Span< Strip * > strips_to_shuffle, ListBase *seqbasep, char dir)
bool channel_is_muted(const SeqTimelineChannel *channel)
Definition channels.cc:81
void transform_handle_overlap(Scene *scene, ListBase *seqbasep, blender::Span< Strip * > transformed_strips, bool use_sync_markers)
void time_right_handle_frame_set(const Scene *scene, Strip *strip, int timeline_frame)
bool transform_strip_can_be_translated(const Strip *strip)
Strip * edit_strip_split(Main *bmain, Scene *scene, ListBase *seqbase, Strip *strip, const int timeline_frame, const eSplitMethod method, const bool ignore_connections, const char **r_error)
float2 image_preview_unit_to_px(const Scene *scene, const float2 co_src)
bool transform_seqbase_shuffle(ListBase *seqbasep, Strip *test, Scene *evil_scene)
eSeqOverlapMode tool_settings_overlap_mode_get(Scene *scene)
Definition sequencer.cc:421
static float3x3 seq_image_transform_matrix_get_ex(const Scene *scene, const Strip *strip, bool apply_rotation=true)
float2 image_preview_unit_from_px(const Scene *scene, const float2 co_src)
int effect_get_num_inputs(int strip_type)
Definition effects.cc:327
VecBase< int32_t, 2 > int2
VecBase< float, 2 > float2
MatBase< float, 3, 3 > float3x3
void * first
struct ToolSettings * toolsettings
struct Editing * ed
struct RenderData r
ListBase markers
StripCrop * crop
StripTransform * transform
StripElem * stripdata
struct Strip * input1
StripData * data
struct MovieClip * clip
void * effectdata
ListBase seqbase
StripRuntime runtime
struct Strip * next
struct Strip * input2
i
Definition text_draw.cc:230