Blender V5.0
transform_convert_sequencer_image.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2021 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8
9#include "MEM_guardedalloc.h"
10
11#include "DNA_screen_types.h"
12#include "DNA_sequence_types.h"
13#include "DNA_space_types.h"
14
15#include "BKE_context.hh"
16
17#include "BLI_array.hh"
18#include "BLI_math_matrix.h"
19#include "BLI_math_matrix.hh"
20#include "BLI_math_rotation.h"
21#include "BLI_math_vector.h"
22#include "BLI_math_vector.hh"
23
24#include "SEQ_channels.hh"
25#include "SEQ_iterator.hh"
26#include "SEQ_relations.hh"
27#include "SEQ_sequencer.hh"
28#include "SEQ_transform.hh"
29
30#include "ANIM_keyframing.hh"
31
32#include "RNA_access.hh"
33#include "RNA_prototypes.hh"
34
35#include "transform.hh"
36#include "transform_convert.hh"
37
38namespace blender::ed::transform {
39
40namespace {
41
43struct TransDataSeq {
44 Strip *strip;
45 Array<float2> quad_orig;
46 float3x3 orig_matrix;
47
48 float2 orig_origin_relative; /* 0-1 range within image bounds. */
49 float2 orig_origin_pixelspace;
50 float2 orig_translation;
51 float2 orig_scale;
52 float orig_rotation;
53 int orig_flag;
54 float active_seq_orig_rotation;
55 float2 orig_mirror;
56};
57
58} // namespace
59
60static void store_transform_properties(const Scene *scene,
61 Strip *strip,
62 float2 origin,
63 TransData *td)
64{
65 Editing *ed = seq::editing_get(scene);
66 const StripTransform *transform = strip->data->transform;
67 TransDataSeq *tdseq = MEM_new<TransDataSeq>("TransSeq TransDataSeq");
68 tdseq->strip = strip;
69 copy_v2_v2(tdseq->orig_origin_relative, transform->origin);
70 tdseq->orig_origin_pixelspace = origin;
71 tdseq->quad_orig = seq::image_transform_final_quad_get(scene, strip);
72 tdseq->orig_matrix = math::invert(seq::image_transform_matrix_get(scene, strip));
73
74 tdseq->orig_translation[0] = transform->xofs;
75 tdseq->orig_translation[1] = transform->yofs;
76 tdseq->orig_scale[0] = transform->scale_x;
77 tdseq->orig_scale[1] = transform->scale_y;
78 tdseq->orig_rotation = transform->rotation;
79 tdseq->orig_flag = strip->flag;
80 tdseq->orig_mirror = seq::image_transform_mirror_factor_get(strip);
81 tdseq->active_seq_orig_rotation = ed->act_strip && ed->act_strip->data->transform ?
82 ed->act_strip->data->transform->rotation :
83 transform->rotation;
84 tdseq->strip = strip;
85 td->extra = static_cast<void *>(tdseq);
86}
87
89 const Scene *scene, Strip *strip, TransData *td, TransData2D *td2d, int vert_index)
90{
91 const StripTransform *transform = strip->data->transform;
94 float vertex[2] = {origin[0], origin[1]};
95
96 /* Add control vertex, so rotation and scale can be calculated.
97 * All three vertices will form a "L" shape that is aligned to the local strip axis.
98 */
99 if (vert_index == 1) {
100 vertex[0] += cosf(transform->rotation);
101 vertex[1] += sinf(transform->rotation);
102 }
103 else if (vert_index == 2) {
104 vertex[0] -= sinf(transform->rotation);
105 vertex[1] += cosf(transform->rotation);
106 }
107
108 td2d->loc[0] = vertex[0];
109 td2d->loc[1] = vertex[1];
110 td2d->loc2d = nullptr;
111 td->loc = td2d->loc;
112 copy_v3_v3(td->iloc, td->loc);
113
114 td->center[0] = origin[0];
115 td->center[1] = origin[1];
116
117 unit_m3(td->mtx);
118 unit_m3(td->smtx);
119
120 axis_angle_to_mat3_single(td->axismtx, 'Z', transform->rotation * mirror[0] * mirror[1]);
122
123 /* Store properties only once per vertex "triad". */
124 if (vert_index == 0) {
125 store_transform_properties(scene, strip, origin, td);
126 }
127
128 td->flag |= TD_SELECTED;
129 td->dist = 0.0;
130
131 return td;
132}
133
134static void freeSeqData(TransInfo * /*t*/,
136 TransCustomData * /*custom_data*/)
137{
138 TransData *td = tc->data;
139 for (int i = 0; i < tc->data_len; i += 3) {
140 TransDataSeq *tdseq = static_cast<TransDataSeq *>((td + i)->extra);
141 MEM_delete(tdseq);
142 }
143}
144
146{
148 Editing *ed = seq::editing_get(scene);
149 const SpaceSeq *sseq = static_cast<const SpaceSeq *>(t->area->spacedata.first);
150 const ARegion *region = t->region;
151
152 if (ed == nullptr) {
153 return;
154 }
155 if (sseq->mainb != SEQ_DRAW_IMG_IMBUF) {
156 return;
157 }
158 if (region->regiontype == RGN_TYPE_PREVIEW && sseq->view == SEQ_VIEW_SEQUENCE_PREVIEW) {
159 return;
160 }
161
164 VectorSet strips = seq::query_rendered_strips(scene, channels, seqbase, scene->r.cfra, 0);
165 strips.remove_if([&](Strip *strip) { return (strip->flag & SELECT) == 0; });
166
167 if (strips.is_empty()) {
168 return;
169 }
170
173
174 tc->data_len = strips.size() * 3; /* 3 vertices per sequence are needed. */
175 TransData *td = tc->data = MEM_calloc_arrayN<TransData>(tc->data_len, "TransSeq TransData");
177 "TransSeq TransData2D");
178
179 for (Strip *strip : strips) {
180 /* One `Sequence` needs 3 `TransData` entries - center point placed in image origin, then 2
181 * points offset by 1 in X and Y direction respectively, so rotation and scale can be
182 * calculated from these points. */
183 SeqToTransData(scene, strip, td++, td2d++, 0);
184 SeqToTransData(scene, strip, td++, td2d++, 1);
185 SeqToTransData(scene, strip, td++, td2d++, 2);
186 }
187}
188
190 Scene *scene,
192 const int tmode)
193{
194 PropertyRNA *prop;
195 PointerRNA ptr = RNA_pointer_create_discrete(&scene->id, &RNA_StripTransform, transform);
196
197 const bool around_cursor = scene->toolsettings->sequencer_tool_settings->pivot_point ==
199 const bool do_loc = tmode == TFM_TRANSLATION || around_cursor;
200 const bool do_rot = tmode == TFM_ROTATION;
201 const bool do_scale = tmode == TFM_RESIZE;
202 const bool only_when_keyed = animrig::is_keying_flag(scene, AUTOKEY_FLAG_INSERTAVAILABLE);
203
204 bool changed = false;
205 if (do_rot) {
206 prop = RNA_struct_find_property(&ptr, "rotation");
208 C, scene, &ptr, prop, -1, scene->r.cfra, only_when_keyed);
209 }
210 if (do_loc) {
211 prop = RNA_struct_find_property(&ptr, "offset_x");
213 C, scene, &ptr, prop, -1, scene->r.cfra, only_when_keyed);
214 prop = RNA_struct_find_property(&ptr, "offset_y");
216 C, scene, &ptr, prop, -1, scene->r.cfra, only_when_keyed);
217 }
218 if (do_scale) {
219 prop = RNA_struct_find_property(&ptr, "scale_x");
221 C, scene, &ptr, prop, -1, scene->r.cfra, only_when_keyed);
222 prop = RNA_struct_find_property(&ptr, "scale_y");
224 C, scene, &ptr, prop, -1, scene->r.cfra, only_when_keyed);
225 }
226
227 return changed;
228}
229
235
237 TransDataSeq *tdseq,
238 TransData2D *td2d,
239 Strip *strip)
240{
242 float2 handle_origin = {td2d->loc[0], td2d->loc[1]};
243 /* X and Y control points used to read scale and rotation. */
244 float2 handle_x = float2((td2d + 1)->loc) - handle_origin;
245 float2 handle_y = float2((td2d + 2)->loc) - handle_origin;
246 float2 aspect = {scene->r.yasp / scene->r.xasp, 1.0f};
248 float2 orig_strip_origin_pixelspace = tdseq->orig_origin_pixelspace;
249
250 return TransformResult{(orig_strip_origin_pixelspace - handle_origin) * mirror * aspect,
251 {math::length(handle_x), math::length(handle_y)},
252 t->values_final[0] * mirror[0] * mirror[1]};
253}
254
256{
258 TransData *td = nullptr;
259 TransData2D *td2d = nullptr;
261 Editing *ed = seq::editing_get(scene);
262 int i;
263
264 for (i = 0, td = tc->data, td2d = tc->data_2d; i < tc->data_len; i += 3, td += 3, td2d += 3) {
265 TransDataSeq *tdseq = static_cast<TransDataSeq *>(td->extra);
266 Strip *strip = tdseq->strip;
268 TransformResult result = transform_result_get(t, tdseq, td2d, strip);
269
270 /* Round resulting position to integer pixels. Resulting strip
271 * will more often end up using faster interpolation (without bilinear),
272 * and avoids "text edges are too dark" artifacts with light text strips
273 * on light backgrounds. The latter happens because bilinear filtering
274 * does not do full alpha pre-multiplication. */
275 transform->xofs = roundf(tdseq->orig_translation.x - result.translation.x);
276 transform->yofs = roundf(tdseq->orig_translation.y - result.translation.y);
277
278 /* Scale. */
279 transform->scale_x = tdseq->orig_scale.x * result.scale.x;
280 transform->scale_y = tdseq->orig_scale.y * result.scale.y;
281
282 /* Rotation. Scaling can cause negative rotation. */
283 if (t->mode == TFM_ROTATION) {
284 transform->rotation = tdseq->orig_rotation + result.rotation;
285 }
286
287 if (t->mode == TFM_MIRROR) {
288
289 transform->xofs *= t->values_final[0];
290 transform->yofs *= t->values_final[1];
291
292 if (t->orient_curr == O_SET && t->orient_type_mask & (1 << V3D_ORIENT_LOCAL)) {
293 if (strip == ed->act_strip) {
294 transform->rotation = tdseq->orig_rotation;
295 }
296 else {
297 transform->rotation = tdseq->orig_rotation + (2 * tdseq->active_seq_orig_rotation);
298 }
299 }
300 else {
301 transform->rotation = tdseq->orig_rotation;
302 }
303
304 strip->flag = tdseq->orig_flag;
305 if (t->values_final[0] == -1) {
306 strip->flag ^= SEQ_FLIPX;
307 }
308 if (t->values_final[1] == -1) {
309 strip->flag ^= SEQ_FLIPY;
310 }
311 }
312
313 if ((t->animtimer) && animrig::is_autokey_on(scene)) {
314 animrecord_check_state(t, &scene->id);
316 }
317
319 }
320}
321
322static float2 calculate_translation_offset(TransInfo *t, TransDataSeq *tdseq)
323{
325 Strip *strip = tdseq->strip;
327
328 /* During modal operation, transform->*ofs is adjusted. Reset this value to original state, so
329 * that new offset can be calculated. */
330 transform->xofs = tdseq->orig_translation[0];
331 transform->yofs = tdseq->orig_translation[1];
332
333 const float2 viewport_pixel_aspect = {scene->r.xasp / scene->r.yasp, 1.0f};
335
336 Array<float2> quad_new = seq::image_transform_final_quad_get(scene, strip);
337 return (quad_new[0] - tdseq->quad_orig[0]) * mirror / viewport_pixel_aspect;
338}
339
340static float2 calculate_new_origin_position(TransInfo *t, TransDataSeq *tdseq, TransData2D *td2d)
341{
343 Strip *strip = tdseq->strip;
344
345 const float2 image_size = seq::transform_image_raw_size_get(scene, strip);
346
347 const float2 viewport_pixel_aspect = {scene->r.xasp / scene->r.yasp, 1.0f};
349
350 const float2 origin = tdseq->orig_origin_pixelspace;
351 const float2 translation = transform_result_get(t, tdseq, td2d, strip).translation;
352 const float2 origin_pixelspace_unscaled = origin / viewport_pixel_aspect * mirror;
353 const float2 origin_translated = origin_pixelspace_unscaled - translation;
354 const float2 origin_raw_space = math::transform_point(tdseq->orig_matrix, origin_translated);
355 const float2 origin_abs = origin_raw_space + image_size / 2;
356 const float2 origin_rel = origin_abs / image_size;
357 return origin_rel;
358}
359
361{
363 TransData *td = nullptr;
364 TransData2D *td2d = nullptr;
366 int i;
367
368 for (i = 0, td = tc->data, td2d = tc->data_2d; i < tc->data_len; i += 3, td += 3, td2d += 3) {
369 TransDataSeq *tdseq = static_cast<TransDataSeq *>(td->extra);
370 Strip *strip = tdseq->strip;
372
373 const float2 origin_rel = calculate_new_origin_position(t, tdseq, td2d);
374 transform->origin[0] = origin_rel.x;
375 transform->origin[1] = origin_rel.y;
376
377 /* Calculate offset, so image does not change it's position in preview. */
378 float2 delta_translation = calculate_translation_offset(t, tdseq);
379 transform->xofs = tdseq->orig_translation.x - delta_translation.x;
380 transform->yofs = tdseq->orig_translation.y - delta_translation.y;
381
383 }
384}
385
387{
388 if ((t->flag & T_ORIGIN) == 0) {
390 }
391 else {
393 }
394}
395
397{
398
400 TransData *td = nullptr;
401 TransData2D *td2d = nullptr;
403 int i;
404
405 for (i = 0, td = tc->data, td2d = tc->data_2d; i < tc->data_len; i += 3, td += 3, td2d += 3) {
406 TransDataSeq *tdseq = static_cast<TransDataSeq *>(td->extra);
407 Strip *strip = tdseq->strip;
409 if (t->state == TRANS_CANCEL) {
410 transform->xofs = tdseq->orig_translation.x;
411 transform->yofs = tdseq->orig_translation.y;
412 transform->rotation = tdseq->orig_rotation;
413 transform->scale_x = tdseq->orig_scale.x;
414 transform->scale_y = tdseq->orig_scale.y;
415 transform->origin[0] = tdseq->orig_origin_relative.x;
416 transform->origin[1] = tdseq->orig_origin_relative.y;
417 strip->flag = tdseq->orig_flag;
418 continue;
419 }
420
421 if (animrig::is_autokey_on(scene)) {
423 }
424 }
425}
426
428 /*flags*/ (T_POINTS | T_2D_EDIT),
429 /*create_trans_data*/ createTransSeqImageData,
430 /*recalc_data*/ recalcData_sequencer_image,
431 /*special_aftertrans_update*/ special_aftertrans_update__sequencer_image,
432};
433
434} // namespace blender::ed::transform
Functions to insert, delete or modify keyframes.
Scene * CTX_data_sequencer_scene(const bContext *C)
void unit_m3(float m[3][3])
void normalize_m3(float R[3][3]) ATTR_NONNULL()
void axis_angle_to_mat3_single(float R[3][3], char axis, float angle)
MINLINE void copy_v2_v2(float r[2], const float a[2])
MINLINE void copy_v3_v3(float r[3], const float a[3])
@ RGN_TYPE_PREVIEW
@ SEQ_FLIPX
@ SEQ_FLIPY
struct Strip Strip
@ SEQ_VIEW_SEQUENCE_PREVIEW
@ SEQ_DRAW_IMG_IMBUF
@ AUTOKEY_FLAG_INSERTAVAILABLE
@ V3D_AROUND_CURSOR
@ V3D_ORIENT_LOCAL
Read Guarded memory(de)allocation.
#define C
Definition RandGen.cpp:29
int64_t size() const
int64_t remove_if(Predicate &&predicate)
#define SELECT
#define roundf(x)
void * MEM_calloc_arrayN(size_t len, size_t size, const char *str)
Definition mallocn.cc:123
bool is_autokey_on(const Scene *scene)
bool autokeyframe_property(bContext *C, Scene *scene, PointerRNA *ptr, PropertyRNA *prop, int rnaindex, float cfra, bool only_if_property_keyed)
bool is_keying_flag(const Scene *scene, eKeying_Flag flag)
void animrecord_check_state(TransInfo *t, ID *id)
static void createTransSeqImageData(bContext *C, TransInfo *t)
static TransformResult transform_result_get(TransInfo *t, TransDataSeq *tdseq, TransData2D *td2d, Strip *strip)
static float2 calculate_translation_offset(TransInfo *t, TransDataSeq *tdseq)
static TransData * SeqToTransData(Scene *scene, TransData *td, TransData2D *td2d, TransDataSeq *tdsq, Strip *strip, int flag, int sel_flag)
static void image_transform_set(TransInfo *t)
static bool autokeyframe_sequencer_image(bContext *C, Scene *scene, StripTransform *transform, const int tmode)
static void recalcData_sequencer_image(TransInfo *t)
static void freeSeqData(TransInfo *t, TransDataContainer *tc, TransCustomData *custom_data)
static void store_transform_properties(const Scene *scene, Strip *strip, float2 origin, TransData *td)
static float2 calculate_new_origin_position(TransInfo *t, TransDataSeq *tdseq, TransData2D *td2d)
static void special_aftertrans_update__sequencer_image(bContext *C, TransInfo *t)
T length(const VecBase< T, Size > &a)
CartesianBasis invert(const CartesianBasis &basis)
VecBase< T, 3 > transform_point(const CartesianBasis &basis, const VecBase< T, 3 > &v)
float2 transform_image_raw_size_get(const Scene *scene, const Strip *strip)
Array< float2 > image_transform_final_quad_get(const Scene *scene, const Strip *strip)
void relations_invalidate_cache(Scene *scene, Strip *strip)
ListBase * channels_displayed_get(const Editing *ed)
Definition channels.cc:28
float3x3 image_transform_matrix_get(const Scene *scene, const Strip *strip)
Editing * editing_get(const Scene *scene)
Definition sequencer.cc:286
float2 image_transform_mirror_factor_get(const Strip *strip)
float2 image_transform_origin_offset_pixelspace_get(const Scene *scene, const Strip *strip)
VectorSet< Strip * > query_rendered_strips(const Scene *scene, ListBase *channels, ListBase *seqbase, const int timeline_frame, const int displayed_channel)
Definition iterator.cc:228
ListBase * active_seqbase_get(const Editing *ed)
Definition sequencer.cc:433
VecBase< float, 2 > float2
MatBase< float, 3, 3 > float3x3
#define sinf
#define cosf
PropertyRNA * RNA_struct_find_property(PointerRNA *ptr, const char *identifier)
PointerRNA RNA_pointer_create_discrete(ID *id, StructRNA *type, void *data)
void * first
struct ToolSettings * toolsettings
struct RenderData r
ListBase spacedata
StripTransform * transform
StripData * data
struct SequencerToolSettings * sequencer_tool_settings
void(* free_cb)(TransInfo *, TransDataContainer *tc, TransCustomData *custom_data)
Definition transform.hh:633
i
Definition text_draw.cc:230
#define TRANS_DATA_CONTAINER_FIRST_SINGLE(t)
Definition transform.hh:39
conversion and adaptation of different datablocks to a common struct.
PointerRNA * ptr
Definition wm_files.cc:4238