Blender V4.5
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_sequence_types.h"
12#include "DNA_space_types.h"
13
14#include "BLI_array.hh"
15#include "BLI_math_matrix.h"
16#include "BLI_math_matrix.hh"
17#include "BLI_math_rotation.h"
18#include "BLI_math_vector.h"
19#include "BLI_math_vector.hh"
20
21#include "SEQ_channels.hh"
22#include "SEQ_iterator.hh"
23#include "SEQ_relations.hh"
24#include "SEQ_sequencer.hh"
25#include "SEQ_transform.hh"
26
27#include "ANIM_keyframing.hh"
28
29#include "RNA_access.hh"
30#include "RNA_prototypes.hh"
31
32#include "transform.hh"
33#include "transform_convert.hh"
34
35namespace blender::ed::transform {
36
37namespace {
38
40struct TransDataSeq {
41 Strip *strip;
42 Array<float2> quad_orig;
43 float3x3 orig_matrix;
44
45 float2 orig_origin_relative; /* 0-1 range within image bounds. */
46 float2 orig_origin_pixelspace;
47 float2 orig_translation;
48 float2 orig_scale;
49 float orig_rotation;
50 int orig_flag;
51 float active_seq_orig_rotation;
52 float2 orig_mirror;
53};
54
55} // namespace
56
57static void store_transform_properties(const Scene *scene,
58 Strip *strip,
59 float2 origin,
60 TransData *td)
61{
62 Editing *ed = seq::editing_get(scene);
63 const StripTransform *transform = strip->data->transform;
64 TransDataSeq *tdseq = MEM_new<TransDataSeq>("TransSeq TransDataSeq");
65 tdseq->strip = strip;
66 copy_v2_v2(tdseq->orig_origin_relative, transform->origin);
67 tdseq->orig_origin_pixelspace = origin;
68 tdseq->quad_orig = seq::image_transform_final_quad_get(scene, strip);
69 tdseq->orig_matrix = math::invert(seq::image_transform_matrix_get(scene, strip));
70
71 tdseq->orig_translation[0] = transform->xofs;
72 tdseq->orig_translation[1] = transform->yofs;
73 tdseq->orig_scale[0] = transform->scale_x;
74 tdseq->orig_scale[1] = transform->scale_y;
75 tdseq->orig_rotation = transform->rotation;
76 tdseq->orig_flag = strip->flag;
77 tdseq->orig_mirror = seq::image_transform_mirror_factor_get(strip);
78 tdseq->active_seq_orig_rotation = ed->act_strip && ed->act_strip->data->transform ?
79 ed->act_strip->data->transform->rotation :
80 transform->rotation;
81 tdseq->strip = strip;
82 td->extra = static_cast<void *>(tdseq);
83}
84
86 const Scene *scene, Strip *strip, TransData *td, TransData2D *td2d, int vert_index)
87{
88 const StripTransform *transform = strip->data->transform;
91 float vertex[2] = {origin[0], origin[1]};
92
93 /* Add control vertex, so rotation and scale can be calculated.
94 * All three vertices will form a "L" shape that is aligned to the local strip axis.
95 */
96 if (vert_index == 1) {
97 vertex[0] += cosf(transform->rotation);
98 vertex[1] += sinf(transform->rotation);
99 }
100 else if (vert_index == 2) {
101 vertex[0] -= sinf(transform->rotation);
102 vertex[1] += cosf(transform->rotation);
103 }
104
105 td2d->loc[0] = vertex[0];
106 td2d->loc[1] = vertex[1];
107 td2d->loc2d = nullptr;
108 td->loc = td2d->loc;
109 copy_v3_v3(td->iloc, td->loc);
110
111 td->center[0] = origin[0];
112 td->center[1] = origin[1];
113
114 unit_m3(td->mtx);
115 unit_m3(td->smtx);
116
117 axis_angle_to_mat3_single(td->axismtx, 'Z', transform->rotation * mirror[0] * mirror[1]);
119
120 /* Store properties only once per vertex "triad". */
121 if (vert_index == 0) {
122 store_transform_properties(scene, strip, origin, td);
123 }
124
125 td->ext = nullptr;
126 td->flag |= TD_SELECTED;
127 td->dist = 0.0;
128
129 return td;
130}
131
132static void freeSeqData(TransInfo * /*t*/,
134 TransCustomData * /*custom_data*/)
135{
136 TransData *td = tc->data;
137 for (int i = 0; i < tc->data_len; i += 3) {
138 TransDataSeq *tdseq = static_cast<TransDataSeq *>((td + i)->extra);
139 MEM_delete(tdseq);
140 }
141}
142
144{
146 const SpaceSeq *sseq = static_cast<const SpaceSeq *>(t->area->spacedata.first);
147 const ARegion *region = t->region;
148
149 if (ed == nullptr) {
150 return;
151 }
152 if (sseq->mainb != SEQ_DRAW_IMG_IMBUF) {
153 return;
154 }
155 if (region->regiontype == RGN_TYPE_PREVIEW && sseq->view == SEQ_VIEW_SEQUENCE_PREVIEW) {
156 return;
157 }
158
161 VectorSet strips = seq::query_rendered_strips(t->scene, channels, seqbase, t->scene->r.cfra, 0);
162 strips.remove_if([&](Strip *strip) { return (strip->flag & SELECT) == 0; });
163
164 if (strips.is_empty()) {
165 return;
166 }
167
170
171 tc->data_len = strips.size() * 3; /* 3 vertices per sequence are needed. */
172 TransData *td = tc->data = MEM_calloc_arrayN<TransData>(tc->data_len, "TransSeq TransData");
174 "TransSeq TransData2D");
175
176 for (Strip *strip : strips) {
177 /* One `Sequence` needs 3 `TransData` entries - center point placed in image origin, then 2
178 * points offset by 1 in X and Y direction respectively, so rotation and scale can be
179 * calculated from these points. */
180 SeqToTransData(t->scene, strip, td++, td2d++, 0);
181 SeqToTransData(t->scene, strip, td++, td2d++, 1);
182 SeqToTransData(t->scene, strip, td++, td2d++, 2);
183 }
184}
185
187 Scene *scene,
189 const int tmode)
190{
191 PropertyRNA *prop;
192 PointerRNA ptr = RNA_pointer_create_discrete(&scene->id, &RNA_StripTransform, transform);
193
194 const bool around_cursor = scene->toolsettings->sequencer_tool_settings->pivot_point ==
196 const bool do_loc = tmode == TFM_TRANSLATION || around_cursor;
197 const bool do_rot = tmode == TFM_ROTATION;
198 const bool do_scale = tmode == TFM_RESIZE;
199 const bool only_when_keyed = animrig::is_keying_flag(scene, AUTOKEY_FLAG_INSERTAVAILABLE);
200
201 bool changed = false;
202 if (do_rot) {
203 prop = RNA_struct_find_property(&ptr, "rotation");
205 C, scene, &ptr, prop, -1, scene->r.cfra, only_when_keyed);
206 }
207 if (do_loc) {
208 prop = RNA_struct_find_property(&ptr, "offset_x");
210 C, scene, &ptr, prop, -1, scene->r.cfra, only_when_keyed);
211 prop = RNA_struct_find_property(&ptr, "offset_y");
213 C, scene, &ptr, prop, -1, scene->r.cfra, only_when_keyed);
214 }
215 if (do_scale) {
216 prop = RNA_struct_find_property(&ptr, "scale_x");
218 C, scene, &ptr, prop, -1, scene->r.cfra, only_when_keyed);
219 prop = RNA_struct_find_property(&ptr, "scale_y");
221 C, scene, &ptr, prop, -1, scene->r.cfra, only_when_keyed);
222 }
223
224 return changed;
225}
226
232
234 TransDataSeq *tdseq,
235 TransData2D *td2d,
236 Strip *strip)
237{
238 float2 handle_origin = {td2d->loc[0], td2d->loc[1]};
239 /* X and Y control points used to read scale and rotation. */
240 float2 handle_x = float2((td2d + 1)->loc) - handle_origin;
241 float2 handle_y = float2((td2d + 2)->loc) - handle_origin;
242 float2 aspect = {t->scene->r.yasp / t->scene->r.xasp, 1.0f};
244 float2 orig_strip_origin_pixelspace = tdseq->orig_origin_pixelspace;
245
246 return TransformResult{(orig_strip_origin_pixelspace - handle_origin) * mirror * aspect,
247 {math::length(handle_x), math::length(handle_y)},
248 t->values_final[0] * mirror[0] * mirror[1]};
249}
250
252{
254 TransData *td = nullptr;
255 TransData2D *td2d = nullptr;
257 int i;
258
259 for (i = 0, td = tc->data, td2d = tc->data_2d; i < tc->data_len; i += 3, td += 3, td2d += 3) {
260 TransDataSeq *tdseq = static_cast<TransDataSeq *>(td->extra);
261 Strip *strip = tdseq->strip;
263 TransformResult result = transform_result_get(t, tdseq, td2d, strip);
264
265 /* Round resulting position to integer pixels. Resulting strip
266 * will more often end up using faster interpolation (without bilinear),
267 * and avoids "text edges are too dark" artifacts with light text strips
268 * on light backgrounds. The latter happens because bilinear filtering
269 * does not do full alpha pre-multiplication. */
270 transform->xofs = roundf(tdseq->orig_translation.x - result.translation.x);
271 transform->yofs = roundf(tdseq->orig_translation.y - result.translation.y);
272
273 /* Scale. */
274 transform->scale_x = tdseq->orig_scale.x * result.scale.x;
275 transform->scale_y = tdseq->orig_scale.y * result.scale.y;
276
277 /* Rotation. Scaling can cause negative rotation. */
278 if (t->mode == TFM_ROTATION) {
279 transform->rotation = tdseq->orig_rotation - result.rotation;
280 }
281
282 if (t->mode == TFM_MIRROR) {
283
284 transform->xofs *= t->values_final[0];
285 transform->yofs *= t->values_final[1];
286
287 if (t->orient_curr == O_SET) {
288 if (strip == ed->act_strip) {
289 transform->rotation = -tdseq->orig_rotation;
290 }
291 else {
292 transform->rotation = tdseq->orig_rotation + (2 * -tdseq->active_seq_orig_rotation);
293 }
294 }
295 else {
296 strip->flag = tdseq->orig_flag;
297 if (t->values_final[0] == -1) {
298 strip->flag ^= SEQ_FLIPX;
299 }
300 if (t->values_final[1] == -1) {
301 strip->flag ^= SEQ_FLIPY;
302 }
303 transform->rotation = tdseq->orig_rotation;
304 }
305 }
306
307 if ((t->animtimer) && animrig::is_autokey_on(t->scene)) {
310 }
311
313 }
314}
315
316static float2 calculate_translation_offset(TransInfo *t, TransDataSeq *tdseq)
317{
318 Strip *strip = tdseq->strip;
320
321 /* During modal operation, transform->*ofs is adjusted. Reset this value to original state, so
322 * that new offset can be calculated. */
323 transform->xofs = tdseq->orig_translation[0];
324 transform->yofs = tdseq->orig_translation[1];
325
326 const float2 viewport_pixel_aspect = {t->scene->r.xasp / t->scene->r.yasp, 1.0f};
328
330 return (quad_new[0] - tdseq->quad_orig[0]) * mirror / viewport_pixel_aspect;
331}
332
333static float2 calculate_new_origin_position(TransInfo *t, TransDataSeq *tdseq, TransData2D *td2d)
334{
335 Strip *strip = tdseq->strip;
336
337 float2 image_size(float(t->scene->r.xsch), float(t->scene->r.ysch));
339 image_size.x = strip->data->stripdata->orig_width;
340 image_size.y = strip->data->stripdata->orig_height;
341 }
342
343 const float2 viewport_pixel_aspect = {t->scene->r.xasp / t->scene->r.yasp, 1.0f};
345
346 const float2 origin = tdseq->orig_origin_pixelspace;
347 const float2 translation = transform_result_get(t, tdseq, td2d, strip).translation;
348 const float2 origin_pixelspace_unscaled = origin / viewport_pixel_aspect * mirror;
349 const float2 origin_translated = origin_pixelspace_unscaled - translation;
350 const float2 origin_raw_space = math::transform_point(tdseq->orig_matrix, origin_translated);
351 const float2 origin_abs = origin_raw_space + image_size / 2;
352 const float2 origin_rel = origin_abs / image_size;
353 return origin_rel;
354}
355
357{
359 TransData *td = nullptr;
360 TransData2D *td2d = nullptr;
361 int i;
362
363 for (i = 0, td = tc->data, td2d = tc->data_2d; i < tc->data_len; i += 3, td += 3, td2d += 3) {
364 TransDataSeq *tdseq = static_cast<TransDataSeq *>(td->extra);
365 Strip *strip = tdseq->strip;
367
368 const float2 origin_rel = calculate_new_origin_position(t, tdseq, td2d);
369 transform->origin[0] = origin_rel.x;
370 transform->origin[1] = origin_rel.y;
371
372 /* Calculate offset, so image does not change it's position in preview. */
373 float2 delta_translation = calculate_translation_offset(t, tdseq);
374 transform->xofs = tdseq->orig_translation.x - delta_translation.x;
375 transform->yofs = tdseq->orig_translation.y - delta_translation.y;
376
378 }
379}
380
382{
383 if ((t->flag & T_ORIGIN) == 0) {
385 }
386 else {
388 }
389}
390
392{
393
395 TransData *td = nullptr;
396 TransData2D *td2d = nullptr;
397 int i;
398
399 for (i = 0, td = tc->data, td2d = tc->data_2d; i < tc->data_len; i += 3, td += 3, td2d += 3) {
400 TransDataSeq *tdseq = static_cast<TransDataSeq *>(td->extra);
401 Strip *strip = tdseq->strip;
403 if (t->state == TRANS_CANCEL) {
404 transform->xofs = tdseq->orig_translation.x;
405 transform->yofs = tdseq->orig_translation.y;
406 transform->rotation = tdseq->orig_rotation;
407 transform->scale_x = tdseq->orig_scale.x;
408 transform->scale_y = tdseq->orig_scale.y;
409 transform->origin[0] = tdseq->orig_origin_relative.x;
410 transform->origin[1] = tdseq->orig_origin_relative.y;
411 strip->flag = tdseq->orig_flag;
412 continue;
413 }
414
417 }
418 }
419}
420
422 /*flags*/ (T_POINTS | T_2D_EDIT),
423 /*create_trans_data*/ createTransSeqImageData,
424 /*recalc_data*/ recalcData_sequencer_image,
425 /*special_aftertrans_update*/ special_aftertrans_update__sequencer_image,
426};
427
428} // namespace blender::ed::transform
Functions to insert, delete or modify keyframes.
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])
#define ELEM(...)
@ RGN_TYPE_PREVIEW
@ STRIP_TYPE_IMAGE
@ STRIP_TYPE_MOVIE
struct Strip Strip
@ SEQ_FLIPX
@ SEQ_FLIPY
@ SEQ_VIEW_SEQUENCE_PREVIEW
@ SEQ_DRAW_IMG_IMBUF
@ AUTOKEY_FLAG_INSERTAVAILABLE
@ V3D_AROUND_CURSOR
Read Guarded memory(de)allocation.
#define C
Definition RandGen.cpp:29
int64_t size() const
int64_t remove_if(Predicate &&predicate)
#define SELECT
#define sinf(x)
#define cosf(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 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 special_aftertrans_update__sequencer_image(bContext *, TransInfo *t)
static void recalcData_sequencer_image(TransInfo *t)
static void freeSeqData(TransInfo *t, TransDataContainer *tc, TransCustomData *custom_data)
static void createTransSeqImageData(bContext *, TransInfo *t)
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)
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)
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:272
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:205
ListBase * active_seqbase_get(const Editing *ed)
Definition sequencer.cc:420
VecBase< float, 2 > float2
MatBase< float, 3, 3 > float3x3
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
StripElem * stripdata
StripData * data
struct SequencerToolSettings * sequencer_tool_settings
void(* free_cb)(TransInfo *, TransDataContainer *tc, TransCustomData *custom_data)
Definition transform.hh:628
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:4226