1 /*
2 SPDX-FileCopyrightText: 2017 Nicolas Carion
3 SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
4 */
5
6 #include "keyframemodel.hpp"
7 #include "core.h"
8 #include "doc/docundostack.hpp"
9 #include "macros.hpp"
10 #include "profiles/profilemodel.hpp"
11 #include "rotoscoping/bpoint.h"
12 #include "rotoscoping/rotohelper.hpp"
13
14 #include <QSize>
15 #include <QLineF>
16 #include <QDebug>
17 #include <QJsonDocument>
18 #include <mlt++/Mlt.h>
19 #include <utility>
20
KeyframeModel(std::weak_ptr<AssetParameterModel> model,const QModelIndex & index,std::weak_ptr<DocUndoStack> undo_stack,QObject * parent)21 KeyframeModel::KeyframeModel(std::weak_ptr<AssetParameterModel> model, const QModelIndex &index, std::weak_ptr<DocUndoStack> undo_stack, QObject *parent)
22 : QAbstractListModel(parent)
23 , m_model(std::move(model))
24 , m_undoStack(std::move(undo_stack))
25 , m_index(index)
26 , m_lastData()
27 , m_lock(QReadWriteLock::Recursive)
28 {
29 qDebug() << "Construct keyframemodel. Checking model:" << m_model.expired();
30 if (auto ptr = m_model.lock()) {
31 m_paramType = ptr->data(m_index, AssetParameterModel::TypeRole).value<ParamType>();
32 }
33 setup();
34 refresh();
35 }
36
setup()37 void KeyframeModel::setup()
38 {
39 // We connect the signals of the abstractitemmodel to a more generic one.
40 connect(this, &KeyframeModel::columnsMoved, this, &KeyframeModel::modelChanged);
41 connect(this, &KeyframeModel::columnsRemoved, this, &KeyframeModel::modelChanged);
42 connect(this, &KeyframeModel::columnsInserted, this, &KeyframeModel::modelChanged);
43 connect(this, &KeyframeModel::rowsMoved, this, &KeyframeModel::modelChanged);
44 connect(this, &KeyframeModel::rowsRemoved, this, &KeyframeModel::modelChanged);
45 connect(this, &KeyframeModel::rowsInserted, this, &KeyframeModel::modelChanged);
46 connect(this, &KeyframeModel::modelReset, this, &KeyframeModel::modelChanged);
47 connect(this, &KeyframeModel::dataChanged, this, &KeyframeModel::modelChanged);
48 connect(this, &KeyframeModel::modelChanged, this, &KeyframeModel::sendModification);
49 }
50
addKeyframe(GenTime pos,KeyframeType type,QVariant value,bool notify,Fun & undo,Fun & redo)51 bool KeyframeModel::addKeyframe(GenTime pos, KeyframeType type, QVariant value, bool notify, Fun &undo, Fun &redo)
52 {
53 qDebug() << "ADD keyframe" << pos.frames(pCore->getCurrentFps()) << value << notify;
54 QWriteLocker locker(&m_lock);
55 Fun local_undo = []() { return true; };
56 Fun local_redo = []() { return true; };
57 if (m_keyframeList.count(pos) > 0) {
58 qDebug() << "already there";
59 if (std::pair<KeyframeType, QVariant>({type, value}) == m_keyframeList.at(pos)) {
60 qDebug() << "nothing to do";
61 return true; // nothing to do
62 }
63 // In this case we simply change the type and value
64 KeyframeType oldType = m_keyframeList[pos].first;
65 QVariant oldValue = m_keyframeList[pos].second;
66 local_undo = updateKeyframe_lambda(pos, oldType, oldValue, notify);
67 local_redo = updateKeyframe_lambda(pos, type, value, notify);
68 if (local_redo()) {
69 UPDATE_UNDO_REDO(local_redo, local_undo, undo, redo);
70 return true;
71 }
72 } else {
73 Fun redo_first = addKeyframe_lambda(pos, type, value, notify);
74 if (redo_first()) {
75 local_redo = addKeyframe_lambda(pos, type, value, true);
76 local_undo = deleteKeyframe_lambda(pos, true);
77 UPDATE_UNDO_REDO(local_redo, local_undo, undo, redo);
78 return true;
79 }
80 }
81 return false;
82 }
83
addKeyframe(int frame,double normalizedValue)84 bool KeyframeModel::addKeyframe(int frame, double normalizedValue)
85 {
86 QVariant result = getNormalizedValue(normalizedValue);
87 if (result.isValid()) {
88 // TODO: Use default configurable kf type
89 return addKeyframe(GenTime(frame, pCore->getCurrentFps()), KeyframeType::Linear, result);
90 }
91 return false;
92 }
93
addKeyframe(GenTime pos,KeyframeType type,QVariant value)94 bool KeyframeModel::addKeyframe(GenTime pos, KeyframeType type, QVariant value)
95 {
96 QWriteLocker locker(&m_lock);
97 Fun undo = []() { return true; };
98 Fun redo = []() { return true; };
99
100 bool update = (m_keyframeList.count(pos) > 0);
101 bool res = addKeyframe(pos, type, std::move(value), true, undo, redo);
102 if (res) {
103 PUSH_UNDO(undo, redo, update ? i18n("Change keyframe type") : i18n("Add keyframe"));
104 }
105 return res;
106 }
107
removeKeyframe(GenTime pos,Fun & undo,Fun & redo,bool notify,bool updateSelection)108 bool KeyframeModel::removeKeyframe(GenTime pos, Fun &undo, Fun &redo, bool notify, bool updateSelection)
109 {
110 qDebug() << "Going to remove keyframe at " << pos.frames(pCore->getCurrentFps()) << " NOTIFY: " << notify;
111 qDebug() << "before" << getAnimProperty();
112 QWriteLocker locker(&m_lock);
113 Q_ASSERT(m_keyframeList.count(pos) > 0);
114 KeyframeType oldType = m_keyframeList[pos].first;
115 QVariant oldValue = m_keyframeList[pos].second;
116 Fun select_undo = []() { return true; };
117 Fun select_redo = []() { return true; };
118 if (updateSelection) {
119 if (auto ptr = m_model.lock()) {
120 if (!ptr->m_selectedKeyframes.isEmpty()) {
121 int ix = getIndexForPos(pos);
122 QVector<int> selection;
123 QVector<int> prevSelection = ptr->m_selectedKeyframes;
124 for (auto &kf : prevSelection) {
125 if (kf == ix) {
126 continue;
127 }
128 if (kf < ix) {
129 selection << kf;
130 } else {
131 selection << (kf - 1);
132 }
133 }
134 setActiveKeyframe(-1);
135 std::sort(selection.begin(), selection.end());
136 select_redo = [this, selection]() {
137 setSelectedKeyframes(selection);
138 return true;
139 };
140 select_undo = [this, prevSelection]() {
141 setSelectedKeyframes(prevSelection);
142 return true;
143 };
144
145 }
146 }
147 }
148 Fun redo_first = deleteKeyframe_lambda(pos, notify);
149 if (redo_first()) {
150 Fun local_undo = addKeyframe_lambda(pos, oldType, oldValue, true);
151 Fun local_redo = deleteKeyframe_lambda(pos, true);
152 select_redo();
153 qDebug() << "after" << getAnimProperty();
154 UPDATE_UNDO_REDO(local_redo, local_undo, undo, redo);
155 UPDATE_UNDO_REDO(select_redo, select_undo, undo, redo);
156 return true;
157 }
158 return false;
159 }
160
duplicateKeyframe(GenTime srcPos,GenTime dstPos,Fun & undo,Fun & redo)161 bool KeyframeModel::duplicateKeyframe(GenTime srcPos, GenTime dstPos, Fun &undo, Fun &redo)
162 {
163 QWriteLocker locker(&m_lock);
164 Q_ASSERT(m_keyframeList.count(srcPos) > 0);
165 KeyframeType oldType = m_keyframeList[srcPos].first;
166 QVariant oldValue = m_keyframeList[srcPos].second;
167 Fun local_redo = addKeyframe_lambda(dstPos, oldType, oldValue, true);
168 Fun local_undo = deleteKeyframe_lambda(dstPos, true);
169 if (local_redo()) {
170 UPDATE_UNDO_REDO(local_redo, local_undo, undo, redo);
171 return true;
172 }
173 return false;
174 }
175
removeKeyframe(int frame)176 bool KeyframeModel::removeKeyframe(int frame)
177 {
178 GenTime pos(frame, pCore->getCurrentFps());
179 return removeKeyframe(pos);
180 }
181
removeKeyframe(GenTime pos)182 bool KeyframeModel::removeKeyframe(GenTime pos)
183 {
184 QWriteLocker locker(&m_lock);
185 Fun undo = []() { return true; };
186 Fun redo = []() { return true; };
187
188 if (m_keyframeList.count(pos) > 0 && m_keyframeList.find(pos) == m_keyframeList.begin()) {
189 return false; // initial point must stay
190 }
191
192 bool res = removeKeyframe(pos, undo, redo);
193 if (res) {
194 PUSH_UNDO(undo, redo, i18n("Delete keyframe"));
195 }
196 return res;
197 }
198
getPosAtIndex(int ix) const199 GenTime KeyframeModel::getPosAtIndex(int ix) const
200 {
201 QList<GenTime> positions = getKeyframePos();
202 std::sort(positions.begin(), positions.end());
203 if (ix < 0 || ix >= positions.count()) {
204 return GenTime();
205 }
206 return positions.at(ix);
207 }
208
moveKeyframe(GenTime oldPos,GenTime pos,QVariant newVal,Fun & undo,Fun & redo,bool updateView)209 bool KeyframeModel::moveKeyframe(GenTime oldPos, GenTime pos, QVariant newVal, Fun &undo, Fun &redo, bool updateView)
210 {
211 qDebug() << "starting to move keyframe" << oldPos.frames(pCore->getCurrentFps()) << pos.frames(pCore->getCurrentFps());
212 QWriteLocker locker(&m_lock);
213 // Check if we have several selected keyframes
214 if (oldPos == pos) {
215 if (!newVal.isValid()) {
216 // no change
217 return true;
218 }
219 }
220 if (auto ptr = m_model.lock()) {
221 if (ptr->m_selectedKeyframes.size() > 1) {
222 // We have several selected keyframes, move them all
223 double offset = 0.;
224 if (newVal.isValid() && newVal.type() == QVariant::Double) {
225 int row = static_cast<int>(std::distance(m_keyframeList.begin(), m_keyframeList.find(oldPos)));
226 double oldVal = data(index(row), NormalizedValueRole).toDouble();
227 offset = newVal.toDouble() - oldVal;
228 }
229 QVector<GenTime> positions;
230 for (auto &kf : ptr->m_selectedKeyframes) {
231 if (kf > 0) {
232 positions << getPosAtIndex(kf);
233 }
234 }
235 GenTime delta = pos - oldPos;
236 if (pos > oldPos) {
237 // Moving right, reverse sort
238 std::sort(positions.rbegin(), positions.rend());
239 // Check max pos
240 bool ok = false;
241 GenTime test = positions.first();
242 auto next = getNextKeyframe(test, &ok);
243 if (ok) {
244 delta = qMin(delta, next.first - GenTime(1, pCore->getCurrentFps()) - test);
245 }
246 } else {
247 // Moving left
248 std::sort(positions.begin(), positions.end());
249 // Check min pos
250 bool ok = false;
251 GenTime test = positions.first();
252 auto next = getPrevKeyframe(test, &ok);
253 if (ok) {
254 delta = qMax(delta, (next.first + GenTime(1, pCore->getCurrentFps())) - test);
255 }
256 }
257 if (delta == GenTime()) {
258 if (!newVal.isValid()) {
259 // no change
260 return true;
261 }
262 }
263 bool res = true;
264 for (auto &p : positions) {
265 if (p == oldPos) {
266 res = res && moveOneKeyframe(oldPos, oldPos + delta, newVal, undo, redo, updateView);
267 } else {
268 if (!qFuzzyIsNull(offset)) {
269 // Calculate new value
270 int row = static_cast<int>(std::distance(m_keyframeList.begin(), m_keyframeList.find(p)));
271 double newVal2 = qBound(0., data(index(row), NormalizedValueRole).toDouble() + offset, 1.);
272 res = res && moveOneKeyframe(p, p + delta, newVal2, undo, redo, updateView);
273 } else {
274 res = res && moveOneKeyframe(p, p + delta, QVariant(), undo, redo, updateView);
275 }
276 }
277 }
278 return res;
279 } else {
280 if (pos > oldPos) {
281 // Moving right
282 bool ok = false;
283 auto next = getNextKeyframe(oldPos, &ok);
284 if (ok) {
285 pos = qMin(pos, next.first - GenTime(1, pCore->getCurrentFps()));
286 }
287 } else {
288 // Moving left
289 bool ok = false;
290 auto next = getPrevKeyframe(oldPos, &ok);
291 if (ok) {
292 pos = qMax(pos, next.first + GenTime(1, pCore->getCurrentFps()));
293 }
294 }
295 return moveOneKeyframe(oldPos, pos, newVal, undo, redo, updateView);
296 }
297 }
298 return false;
299 }
300
moveOneKeyframe(GenTime oldPos,GenTime pos,QVariant newVal,Fun & undo,Fun & redo,bool updateView)301 bool KeyframeModel::moveOneKeyframe(GenTime oldPos, GenTime pos, QVariant newVal, Fun &undo, Fun &redo, bool updateView)
302 {
303 qDebug() << "starting to move keyframe" << oldPos.frames(pCore->getCurrentFps()) << pos.frames(pCore->getCurrentFps());
304 QWriteLocker locker(&m_lock);
305 Q_ASSERT(m_keyframeList.count(oldPos) > 0);
306 if (oldPos == pos) {
307 if (!newVal.isValid()) {
308 // no change
309 return true;
310 }
311 if (m_paramType == ParamType::AnimatedRect) {
312 return updateKeyframe(pos, newVal);
313 }
314 // Calculate real value from normalized
315 QVariant result = getNormalizedValue(newVal.toDouble());
316 return updateKeyframe(pos, result);
317 }
318 if (oldPos != pos && hasKeyframe(pos)) {
319 // Move rejected, another keyframe is here
320 qDebug()<<"==== MOVE REJECTED!!";
321 return false;
322 }
323 KeyframeType oldType = m_keyframeList[oldPos].first;
324 QVariant oldValue = m_keyframeList[oldPos].second;
325 Fun local_undo = []() { return true; };
326 Fun local_redo = []() { return true; };
327 qDebug() << getAnimProperty();
328 // TODO: use the new Animation::key_set_frame to move a keyframe
329 bool res = removeKeyframe(oldPos, local_undo, local_redo, true, false);
330 qDebug() << "Move keyframe finished deletion:" << res;
331 qDebug() << getAnimProperty();
332 if (res) {
333 if (m_paramType == ParamType::AnimatedRect) {
334 if (!newVal.isValid()) {
335 newVal = oldValue;
336 }
337 res = addKeyframe(pos, oldType, newVal, updateView, local_undo, local_redo);
338 } else if (newVal.isValid()) {
339 QVariant result = getNormalizedValue(newVal.toDouble());
340 if (result.isValid()) {
341 res = addKeyframe(pos, oldType, result, updateView, local_undo, local_redo);
342 }
343 } else {
344 res = addKeyframe(pos, oldType, oldValue, updateView, local_undo, local_redo);
345 }
346 qDebug() << "Move keyframe finished insertion:" << res;
347 qDebug() << getAnimProperty();
348 }
349 if (res) {
350 UPDATE_UNDO_REDO(local_redo, local_undo, undo, redo);
351 } else {
352 bool undone = local_undo();
353 Q_ASSERT(undone);
354 }
355 return res;
356 }
357
moveKeyframe(int oldPos,int pos,bool logUndo)358 bool KeyframeModel::moveKeyframe(int oldPos, int pos, bool logUndo)
359 {
360 GenTime oPos(oldPos, pCore->getCurrentFps());
361 GenTime nPos(pos, pCore->getCurrentFps());
362 return moveKeyframe(oPos, nPos, QVariant(), logUndo);
363 }
364
offsetKeyframes(int oldPos,int pos,bool logUndo)365 bool KeyframeModel::offsetKeyframes(int oldPos, int pos, bool logUndo)
366 {
367 if (oldPos == pos) return true;
368 GenTime oldFrame(oldPos, pCore->getCurrentFps());
369 Q_ASSERT(m_keyframeList.count(oldFrame) > 0);
370 GenTime diff(pos - oldPos, pCore->getCurrentFps());
371 QWriteLocker locker(&m_lock);
372 Fun undo = []() { return true; };
373 Fun redo = []() { return true; };
374 QList<GenTime> times;
375 for (const auto &m : m_keyframeList) {
376 if (m.first < oldFrame) continue;
377 times << m.first;
378 }
379 bool res = true;
380 for (const auto &t : qAsConst(times)) {
381 res &= moveKeyframe(t, t + diff, QVariant(), undo, redo);
382 }
383 if (res && logUndo) {
384 PUSH_UNDO(undo, redo, i18nc("@action", "Move keyframes"));
385 }
386 return res;
387 }
388
moveKeyframe(int oldPos,int pos,QVariant newVal)389 bool KeyframeModel::moveKeyframe(int oldPos, int pos, QVariant newVal)
390 {
391 GenTime oPos(oldPos, pCore->getCurrentFps());
392 GenTime nPos(pos, pCore->getCurrentFps());
393 return moveKeyframe(oPos, nPos, std::move(newVal), true);
394 }
395
moveKeyframe(GenTime oldPos,GenTime pos,QVariant newVal,bool logUndo)396 bool KeyframeModel::moveKeyframe(GenTime oldPos, GenTime pos, QVariant newVal, bool logUndo)
397 {
398 QWriteLocker locker(&m_lock);
399 Q_ASSERT(m_keyframeList.count(oldPos) > 0);
400 if (oldPos == pos) return true;
401 Fun undo = []() { return true; };
402 Fun redo = []() { return true; };
403 bool res = moveKeyframe(oldPos, pos, std::move(newVal), undo, redo);
404 if (res && logUndo) {
405 PUSH_UNDO(undo, redo, i18nc("@action", "Move keyframe"));
406 }
407 return res;
408 }
409
directUpdateKeyframe(GenTime pos,QVariant value)410 bool KeyframeModel::directUpdateKeyframe(GenTime pos, QVariant value)
411 {
412 QWriteLocker locker(&m_lock);
413 Q_ASSERT(m_keyframeList.count(pos) > 0);
414 KeyframeType type = m_keyframeList[pos].first;
415 auto operation = updateKeyframe_lambda(pos, type, std::move(value), true);
416 return operation();
417 }
418
updateKeyframe(GenTime pos,const QVariant & value,Fun & undo,Fun & redo,bool update)419 bool KeyframeModel::updateKeyframe(GenTime pos, const QVariant &value, Fun &undo, Fun &redo, bool update)
420 {
421 QWriteLocker locker(&m_lock);
422 Q_ASSERT(m_keyframeList.count(pos) > 0);
423 KeyframeType type = m_keyframeList[pos].first;
424 QVariant oldValue = m_keyframeList[pos].second;
425 // Check if keyframe is different
426 if (m_paramType == ParamType::KeyframeParam || m_paramType == ParamType::ColorWheel) {
427 if (qFuzzyCompare(oldValue.toDouble(), value.toDouble())) return true;
428 }
429 auto operation = updateKeyframe_lambda(pos, type, value, update);
430 auto reverse = updateKeyframe_lambda(pos, type, oldValue, update);
431 bool res = operation();
432 if (res) {
433 UPDATE_UNDO_REDO(operation, reverse, undo, redo);
434 }
435 return res;
436 }
437
updateKeyframe(int pos,double newVal)438 bool KeyframeModel::updateKeyframe(int pos, double newVal)
439 {
440 GenTime Pos(pos, pCore->getCurrentFps());
441 if (auto ptr = m_model.lock()) {
442 double min = ptr->data(m_index, AssetParameterModel::VisualMinRole).toDouble();
443 double max = ptr->data(m_index, AssetParameterModel::VisualMaxRole).toDouble();
444 if (qFuzzyIsNull(min) && qFuzzyIsNull(max)) {
445 min = ptr->data(m_index, AssetParameterModel::MinRole).toDouble();
446 max = ptr->data(m_index, AssetParameterModel::MaxRole).toDouble();
447 }
448 double factor = ptr->data(m_index, AssetParameterModel::FactorRole).toDouble();
449 double norm = ptr->data(m_index, AssetParameterModel::DefaultRole).toDouble();
450 int logRole = ptr->data(m_index, AssetParameterModel::ScaleRole).toInt();
451 double realValue;
452 if (logRole == -1) {
453 // Logarythmic scale
454 if (newVal >= 0.5) {
455 realValue = norm + pow(2 * (newVal - 0.5), 10.0 / 6) * (max / factor - norm);
456 } else {
457 realValue = norm - pow(2 * (0.5 - newVal), 10.0 / 6) * (norm - min / factor);
458 }
459 } else {
460 realValue = (newVal * (max - min) + min) / factor;
461 }
462 return updateKeyframe(Pos, realValue);
463 }
464 return false;
465 }
466
updateKeyframe(GenTime pos,QVariant value)467 bool KeyframeModel::updateKeyframe(GenTime pos, QVariant value)
468 {
469 QWriteLocker locker(&m_lock);
470 Q_ASSERT(m_keyframeList.count(pos) > 0);
471
472 Fun undo = []() { return true; };
473 Fun redo = []() { return true; };
474 bool res = updateKeyframe(pos, std::move(value), undo, redo);
475 if (res) {
476 PUSH_UNDO(undo, redo, i18n("Update keyframe"));
477 }
478 return res;
479 }
480
convertFromMltType(mlt_keyframe_type type)481 KeyframeType convertFromMltType(mlt_keyframe_type type)
482 {
483 switch (type) {
484 case mlt_keyframe_linear:
485 return KeyframeType::Linear;
486 case mlt_keyframe_discrete:
487 return KeyframeType::Discrete;
488 case mlt_keyframe_smooth:
489 return KeyframeType::Curve;
490 }
491 return KeyframeType::Linear;
492 }
493
updateKeyframeType(GenTime pos,int type,Fun & undo,Fun & redo)494 bool KeyframeModel::updateKeyframeType(GenTime pos, int type, Fun &undo, Fun &redo)
495 {
496 QWriteLocker locker(&m_lock);
497 Q_ASSERT(m_keyframeList.count(pos) > 0);
498 KeyframeType oldType = m_keyframeList[pos].first;
499 KeyframeType newType = convertFromMltType(mlt_keyframe_type(type));
500 QVariant value = m_keyframeList[pos].second;
501 // Check if keyframe is different
502 if (m_paramType == ParamType::KeyframeParam || m_paramType == ParamType::ColorWheel) {
503 if (oldType == newType) return true;
504 }
505 auto operation = updateKeyframe_lambda(pos, newType, value, true);
506 auto reverse = updateKeyframe_lambda(pos, oldType, value, true);
507 bool res = operation();
508 if (res) {
509 UPDATE_UNDO_REDO(operation, reverse, undo, redo);
510 }
511 return res;
512 }
513
updateKeyframe_lambda(GenTime pos,KeyframeType type,const QVariant & value,bool notify)514 Fun KeyframeModel::updateKeyframe_lambda(GenTime pos, KeyframeType type, const QVariant &value, bool notify)
515 {
516 QWriteLocker locker(&m_lock);
517 return [this, pos, type, value, notify]() {
518 qDebug() << "update lambda" << pos.frames(pCore->getCurrentFps()) << value << notify;
519 Q_ASSERT(m_keyframeList.count(pos) > 0);
520 int row = static_cast<int>(std::distance(m_keyframeList.begin(), m_keyframeList.find(pos)));
521 m_keyframeList[pos].first = type;
522 m_keyframeList[pos].second = value;
523 if (notify) emit dataChanged(index(row), index(row), {ValueRole, NormalizedValueRole, TypeRole});
524 return true;
525 };
526 }
527
addKeyframe_lambda(GenTime pos,KeyframeType type,const QVariant & value,bool notify)528 Fun KeyframeModel::addKeyframe_lambda(GenTime pos, KeyframeType type, const QVariant &value, bool notify)
529 {
530 QWriteLocker locker(&m_lock);
531 return [this, notify, pos, type, value]() {
532 qDebug() << "add lambda" << pos.frames(pCore->getCurrentFps()) << value << notify;
533 Q_ASSERT(m_keyframeList.count(pos) == 0);
534 // We determine the row of the newly added marker
535 auto insertionIt = m_keyframeList.lower_bound(pos);
536 int insertionRow = static_cast<int>(m_keyframeList.size());
537 if (insertionIt != m_keyframeList.end()) {
538 insertionRow = static_cast<int>(std::distance(m_keyframeList.begin(), insertionIt));
539 }
540 if (notify) beginInsertRows(QModelIndex(), insertionRow, insertionRow);
541 m_keyframeList[pos].first = type;
542 m_keyframeList[pos].second = value;
543 if (notify) endInsertRows();
544 return true;
545 };
546 }
547
deleteKeyframe_lambda(GenTime pos,bool notify)548 Fun KeyframeModel::deleteKeyframe_lambda(GenTime pos, bool notify)
549 {
550 QWriteLocker locker(&m_lock);
551 return [this, pos, notify]() {
552 qDebug() << "delete lambda" << pos.frames(pCore->getCurrentFps()) << notify;
553 qDebug() << "before" << getAnimProperty();
554 Q_ASSERT(m_keyframeList.count(pos) > 0);
555 //Q_ASSERT(pos != GenTime()); // cannot delete initial point
556 int row = static_cast<int>(std::distance(m_keyframeList.begin(), m_keyframeList.find(pos)));
557 if (notify) beginRemoveRows(QModelIndex(), row, row);
558 m_keyframeList.erase(pos);
559 if (notify) endRemoveRows();
560 qDebug() << "after" << getAnimProperty();
561 return true;
562 };
563 }
564
roleNames() const565 QHash<int, QByteArray> KeyframeModel::roleNames() const
566 {
567 QHash<int, QByteArray> roles;
568 roles[PosRole] = "position";
569 roles[FrameRole] = "frame";
570 roles[TypeRole] = "type";
571 roles[ValueRole] = "value";
572 roles[SelectedRole] = "selected";
573 roles[ActiveRole] = "active";
574 roles[NormalizedValueRole] = "normalizedValue";
575 return roles;
576 }
577
data(const QModelIndex & index,int role) const578 QVariant KeyframeModel::data(const QModelIndex &index, int role) const
579 {
580 READ_LOCK();
581 if (index.row() < 0 || index.row() >= static_cast<int>(m_keyframeList.size()) || !index.isValid()) {
582 return QVariant();
583 }
584 auto it = m_keyframeList.begin();
585 std::advance(it, index.row());
586 switch (role) {
587 case Qt::DisplayRole:
588 case Qt::EditRole:
589 case ValueRole:
590 return it->second.second;
591 case NormalizedValueRole: {
592 if (m_paramType == ParamType::AnimatedRect) {
593 const QString &data = it->second.second.toString();
594 bool ok;
595 double converted = data.section(QLatin1Char(' '), -1).toDouble(&ok);
596 if (!ok) {
597 qDebug() << "QLocale: Could not convert animated rect opacity" << data;
598 }
599 return converted;
600 }
601 double val = it->second.second.toDouble();
602 if (auto ptr = m_model.lock()) {
603 Q_ASSERT(m_index.isValid());
604 double min = ptr->data(m_index, AssetParameterModel::VisualMinRole).toDouble();
605 double max = ptr->data(m_index, AssetParameterModel::VisualMaxRole).toDouble();
606 if (qFuzzyIsNull(min) && qFuzzyIsNull(max)) {
607 min = ptr->data(m_index, AssetParameterModel::MinRole).toDouble();
608 max = ptr->data(m_index, AssetParameterModel::MaxRole).toDouble();
609 }
610 double factor = ptr->data(m_index, AssetParameterModel::FactorRole).toDouble();
611 double norm = ptr->data(m_index, AssetParameterModel::DefaultRole).toDouble();
612 int logRole = ptr->data(m_index, AssetParameterModel::ScaleRole).toInt();
613 double linear = val * factor;
614 if (logRole == -1) {
615 // Logarythmic scale
616 // transform current value to 0..1 scale
617 if (linear >= norm) {
618 double scaled = (linear - norm) / (max * factor - norm);
619 return 0.5 + pow(scaled, 0.6) * 0.5;
620 }
621 double scaled = (linear - norm) / (min * factor - norm);
622 // Log scale
623 return 0.5 - pow(scaled, 0.6) * 0.5;
624 }
625 return (linear - min) / (max - min);
626 } else {
627 qDebug() << "// CANNOT LOCK effect MODEL";
628 }
629 return 1;
630 }
631 case PosRole:
632 return it->first.seconds();
633 case FrameRole:
634 case Qt::UserRole:
635 return it->first.frames(pCore->getCurrentFps());
636 case TypeRole:
637 return QVariant::fromValue<KeyframeType>(it->second.first);
638 case SelectedRole:
639 if (auto ptr = m_model.lock()) {
640 return ptr->m_selectedKeyframes.contains(index.row());
641 }
642 break;
643 case ActiveRole:
644 if (auto ptr = m_model.lock()) {
645 return ptr->m_activeKeyframe == index.row();
646 }
647 break;
648 }
649 return QVariant();
650 }
651
rowCount(const QModelIndex & parent) const652 int KeyframeModel::rowCount(const QModelIndex &parent) const
653 {
654 READ_LOCK();
655 if (parent.isValid()) return 0;
656 return static_cast<int>(m_keyframeList.size());
657 }
658
singleKeyframe() const659 bool KeyframeModel::singleKeyframe() const
660 {
661 READ_LOCK();
662 return m_keyframeList.size() <= 1;
663 }
664
getKeyframe(const GenTime & pos,bool * ok) const665 Keyframe KeyframeModel::getKeyframe(const GenTime &pos, bool *ok) const
666 {
667 READ_LOCK();
668 if (m_keyframeList.count(pos) <= 0) {
669 // return empty marker
670 *ok = false;
671 return {GenTime(), KeyframeType::Linear};
672 }
673 *ok = true;
674 return {pos, m_keyframeList.at(pos).first};
675 }
676
getNextKeyframe(const GenTime & pos,bool * ok) const677 Keyframe KeyframeModel::getNextKeyframe(const GenTime &pos, bool *ok) const
678 {
679 auto it = m_keyframeList.upper_bound(pos);
680 if (it == m_keyframeList.end()) {
681 // return empty marker
682 *ok = false;
683 return {GenTime(), KeyframeType::Linear};
684 }
685 *ok = true;
686 return {(*it).first, (*it).second.first};
687 }
688
getPrevKeyframe(const GenTime & pos,bool * ok) const689 Keyframe KeyframeModel::getPrevKeyframe(const GenTime &pos, bool *ok) const
690 {
691 auto it = m_keyframeList.lower_bound(pos);
692 if (it == m_keyframeList.begin()) {
693 // return empty marker
694 *ok = false;
695 return {GenTime(), KeyframeType::Linear};
696 }
697 --it;
698 *ok = true;
699 return {(*it).first, (*it).second.first};
700 }
701
getClosestKeyframe(const GenTime & pos,bool * ok) const702 Keyframe KeyframeModel::getClosestKeyframe(const GenTime &pos, bool *ok) const
703 {
704 if (m_keyframeList.count(pos) > 0) {
705 return getKeyframe(pos, ok);
706 }
707 bool ok1, ok2;
708 auto next = getNextKeyframe(pos, &ok1);
709 auto prev = getPrevKeyframe(pos, &ok2);
710 *ok = ok1 || ok2;
711 if (ok1 && ok2) {
712 double fps = pCore->getCurrentFps();
713 if (qAbs(next.first.frames(fps) - pos.frames(fps)) < qAbs(prev.first.frames(fps) - pos.frames(fps))) {
714 return next;
715 }
716 return prev;
717 } else if (ok1) {
718 return next;
719 } else if (ok2) {
720 return prev;
721 }
722 // return empty marker
723 return {GenTime(), KeyframeType::Linear};
724 }
725
hasKeyframe(int frame) const726 bool KeyframeModel::hasKeyframe(int frame) const
727 {
728 return hasKeyframe(GenTime(frame, pCore->getCurrentFps()));
729 }
hasKeyframe(const GenTime & pos) const730 bool KeyframeModel::hasKeyframe(const GenTime &pos) const
731 {
732 READ_LOCK();
733 return m_keyframeList.count(pos) > 0;
734 }
735
removeAllKeyframes(Fun & undo,Fun & redo)736 bool KeyframeModel::removeAllKeyframes(Fun &undo, Fun &redo)
737 {
738 QWriteLocker locker(&m_lock);
739 std::vector<GenTime> all_pos;
740 Fun local_undo = []() { return true; };
741 Fun local_redo = []() { return true; };
742 int kfrCount = int(m_keyframeList.size()) - 1;
743 // Clear selection
744 if (auto ptr = m_model.lock()) {
745 ptr->m_selectedKeyframes = {};
746 }
747 if (kfrCount <= 0) {
748 // Nothing to do
749 UPDATE_UNDO_REDO(local_redo, local_undo, undo, redo);
750 return true;
751 }
752 // we trigger only one global remove/insertrow event
753 Fun update_redo_start = [this, kfrCount]() {
754 beginRemoveRows(QModelIndex(), 1, kfrCount);
755 return true;
756 };
757 Fun update_redo_end = [this]() {
758 endRemoveRows();
759 return true;
760 };
761 Fun update_undo_start = [this, kfrCount]() {
762 beginInsertRows(QModelIndex(), 1, kfrCount);
763 return true;
764 };
765 Fun update_undo_end = [this]() {
766 endInsertRows();
767 return true;
768 };
769 PUSH_LAMBDA(update_redo_start, local_redo);
770 PUSH_LAMBDA(update_undo_start, local_undo);
771 for (const auto &m : m_keyframeList) {
772 all_pos.push_back(m.first);
773 }
774 update_redo_start();
775 bool res = true;
776 bool first = true;
777 for (const auto &p : all_pos) {
778 if (first) { // skip first point
779 first = false;
780 continue;
781 }
782 res = removeKeyframe(p, local_undo, local_redo, false);
783 if (!res) {
784 bool undone = local_undo();
785 Q_ASSERT(undone);
786 return false;
787 }
788 }
789 update_redo_end();
790 PUSH_LAMBDA(update_redo_end, local_redo);
791 PUSH_LAMBDA(update_undo_end, local_undo);
792 UPDATE_UNDO_REDO(local_redo, local_undo, undo, redo);
793 return true;
794 }
795
removeAllKeyframes()796 bool KeyframeModel::removeAllKeyframes()
797 {
798 QWriteLocker locker(&m_lock);
799 Fun undo = []() { return true; };
800 Fun redo = []() { return true; };
801 bool res = removeAllKeyframes(undo, redo);
802 if (res) {
803 PUSH_UNDO(undo, redo, i18n("Delete all keyframes"));
804 }
805 return res;
806 }
807
convertToMltType(KeyframeType type)808 mlt_keyframe_type convertToMltType(KeyframeType type)
809 {
810 switch (type) {
811 case KeyframeType::Linear:
812 return mlt_keyframe_linear;
813 case KeyframeType::Discrete:
814 return mlt_keyframe_discrete;
815 case KeyframeType::Curve:
816 return mlt_keyframe_smooth;
817 }
818 return mlt_keyframe_linear;
819 }
820
getAnimProperty() const821 QString KeyframeModel::getAnimProperty() const
822 {
823 if (m_paramType == ParamType::Roto_spline) {
824 return getRotoProperty();
825 }
826 Mlt::Properties mlt_prop;
827 if (auto ptr = m_model.lock()) {
828 ptr->passProperties(mlt_prop);
829 }
830 int ix = 0;
831 bool first = true;
832 std::shared_ptr<Mlt::Animation> anim(nullptr);
833 for (const auto &keyframe : m_keyframeList) {
834 if (first) {
835 switch (m_paramType) {
836 case ParamType::AnimatedRect:
837 mlt_prop.anim_set("key", keyframe.second.second.toString().toUtf8().constData(), keyframe.first.frames(pCore->getCurrentFps()));
838 break;
839 default:
840 mlt_prop.anim_set("key", keyframe.second.second.toDouble(), keyframe.first.frames(pCore->getCurrentFps()));
841 break;
842 }
843 anim.reset(mlt_prop.get_anim("key"));
844 anim->key_set_type(ix, convertToMltType(keyframe.second.first));
845 first = false;
846 ix++;
847 continue;
848 }
849 switch (m_paramType) {
850 case ParamType::AnimatedRect:
851 mlt_prop.anim_set("key", keyframe.second.second.toString().toUtf8().constData(), keyframe.first.frames(pCore->getCurrentFps()));
852 break;
853 default:
854 mlt_prop.anim_set("key", keyframe.second.second.toDouble(), keyframe.first.frames(pCore->getCurrentFps()));
855 break;
856 }
857 anim->key_set_type(ix, convertToMltType(keyframe.second.first));
858 ix++;
859 }
860 QString ret;
861 if (anim) {
862 char *cut = anim->serialize_cut();
863 ret = QString(cut);
864 free(cut);
865 }
866 return ret;
867 }
868
getRotoProperty() const869 QString KeyframeModel::getRotoProperty() const
870 {
871 QJsonDocument doc;
872 if (auto ptr = m_model.lock()) {
873 int in = ptr->data(m_index, AssetParameterModel::ParentInRole).toInt();
874 int out = in + ptr->data(m_index, AssetParameterModel::ParentDurationRole).toInt();
875 QVariantMap map;
876 for (const auto &keyframe : m_keyframeList) {
877 map.insert(QString::number(keyframe.first.frames(pCore->getCurrentFps())).rightJustified(int(log10(double(out))) + 1, '0'), keyframe.second.second);
878 }
879 doc = QJsonDocument::fromVariant(map);
880 }
881 return doc.toJson();
882 }
883
parseAnimProperty(const QString & prop)884 void KeyframeModel::parseAnimProperty(const QString &prop)
885 {
886 Fun undo = []() { return true; };
887 Fun redo = []() { return true; };
888 disconnect(this, &KeyframeModel::modelChanged, this, &KeyframeModel::sendModification);
889 removeAllKeyframes(undo, redo);
890 int in = 0;
891 int out = 0;
892 bool useOpacity = true;
893 Mlt::Properties mlt_prop;
894 if (auto ptr = m_model.lock()) {
895 in = ptr->data(m_index, AssetParameterModel::ParentInRole).toInt();
896 out = ptr->data(m_index, AssetParameterModel::ParentDurationRole).toInt();
897 ptr->passProperties(mlt_prop);
898 useOpacity = ptr->data(m_index, AssetParameterModel::OpacityRole).toBool();
899 } else {
900 qDebug()<<"###################\n\n/// ERROR LOCKING MODEL!!! ";
901 }
902 mlt_prop.set("key", prop.toUtf8().constData());
903 // This is a fake query to force the animation to be parsed
904 (void)mlt_prop.anim_get_double("key", 0, out);
905
906 Mlt::Animation anim = mlt_prop.get_animation("key");
907
908 qDebug() << "Found" << anim.key_count() << ", OUT: " << out << ", animation properties: " << prop;
909 bool useDefaultType = !prop.contains(QLatin1Char('='));
910 for (int i = 0; i < anim.key_count(); ++i) {
911 int frame;
912 mlt_keyframe_type type;
913 anim.key_get(i, frame, type);
914 if (useDefaultType) {
915 // TODO: use a default user defined type
916 type = mlt_keyframe_linear;
917 }
918 QVariant value;
919 switch (m_paramType) {
920 case ParamType::AnimatedRect: {
921 mlt_rect rect = mlt_prop.anim_get_rect("key", frame);
922 if (useOpacity) {
923 value = QVariant(QStringLiteral("%1 %2 %3 %4 %5").arg(rect.x).arg(rect.y).arg(rect.w).arg(rect.h).arg(rect.o, 0, 'f'));
924 } else {
925 value = QVariant(QStringLiteral("%1 %2 %3 %4").arg(rect.x).arg(rect.y).arg(rect.w).arg(rect.h));
926 }
927 break;
928 }
929 default:
930 value = QVariant(mlt_prop.anim_get_double("key", frame));
931 break;
932 }
933 if (i == 0 && frame > in) {
934 // Always add a keyframe at start pos
935 addKeyframe(GenTime(in, pCore->getCurrentFps()), convertFromMltType(type), value, true, undo, redo);
936 } else if (frame == in && hasKeyframe(GenTime(in))) {
937 // First keyframe already exists, adjust its value
938 updateKeyframe(GenTime(frame, pCore->getCurrentFps()), value, undo, redo, true);
939 continue;
940 }
941 addKeyframe(GenTime(frame, pCore->getCurrentFps()), convertFromMltType(type), value, true, undo, redo);
942 }
943 connect(this, &KeyframeModel::modelChanged, this, &KeyframeModel::sendModification);
944 }
945
resetAnimProperty(const QString & prop)946 void KeyframeModel::resetAnimProperty(const QString &prop)
947 {
948 Fun undo = []() { return true; };
949 Fun redo = []() { return true; };
950
951 // Delete all existing keyframes
952 disconnect(this, &KeyframeModel::modelChanged, this, &KeyframeModel::sendModification);
953 removeAllKeyframes(undo, redo);
954
955 Mlt::Properties mlt_prop;
956 int in = 0;
957 bool useOpacity = true;
958 if (auto ptr = m_model.lock()) {
959 in = ptr->data(m_index, AssetParameterModel::ParentInRole).toInt();
960 ptr->passProperties(mlt_prop);
961 if (m_paramType == ParamType::AnimatedRect) {
962 useOpacity = ptr->data(m_index, AssetParameterModel::OpacityRole).toBool();
963 }
964 }
965 mlt_prop.set("key", prop.toUtf8().constData());
966 // This is a fake query to force the animation to be parsed
967 (void)mlt_prop.anim_get_int("key", 0, 0);
968
969 Mlt::Animation anim = mlt_prop.get_animation("key");
970
971 qDebug() << "Found" << anim.key_count() << "animation properties";
972 for (int i = 0; i < anim.key_count(); ++i) {
973 int frame;
974 mlt_keyframe_type type;
975 anim.key_get(i, frame, type);
976 if (!prop.contains(QLatin1Char('='))) {
977 // TODO: use a default user defined type
978 type = mlt_keyframe_linear;
979 }
980 QVariant value;
981 switch (m_paramType) {
982 case ParamType::AnimatedRect: {
983 mlt_rect rect = mlt_prop.anim_get_rect("key", frame);
984 if (useOpacity) {
985 value = QVariant(QStringLiteral("%1 %2 %3 %4 %5").arg(rect.x).arg(rect.y).arg(rect.w).arg(rect.h).arg(QString::number(rect.o, 'f')));
986 } else {
987 value = QVariant(QStringLiteral("%1 %2 %3 %4").arg(rect.x).arg(rect.y).arg(rect.w).arg(rect.h));
988 }
989 break;
990 }
991 default:
992 value = QVariant(mlt_prop.anim_get_double("key", frame));
993 break;
994 }
995 if (i == 0 && frame > in) {
996 // Always add a keyframe at start pos
997 addKeyframe(GenTime(in, pCore->getCurrentFps()), convertFromMltType(type), value, false, undo, redo);
998 } else if (frame == in && hasKeyframe(GenTime(in))) {
999 // First keyframe already exists, adjust its value
1000 updateKeyframe(GenTime(frame, pCore->getCurrentFps()), value, undo, redo, false);
1001 continue;
1002 }
1003 addKeyframe(GenTime(frame, pCore->getCurrentFps()), convertFromMltType(type), value, false, undo, redo);
1004 }
1005 QString effectName;
1006 if (auto ptr = m_model.lock()) {
1007 effectName = ptr->data(m_index, Qt::DisplayRole).toString();
1008 } else {
1009 effectName = i18n("effect");
1010 }
1011 Fun update_local = [this]() {
1012 emit dataChanged(index(0), index(int(m_keyframeList.size())), {});
1013 return true;
1014 };
1015 update_local();
1016 PUSH_LAMBDA(update_local, undo);
1017 PUSH_LAMBDA(update_local, redo);
1018 PUSH_UNDO(undo, redo, i18n("Reset %1", effectName));
1019 connect(this, &KeyframeModel::modelChanged, this, &KeyframeModel::sendModification);
1020 }
1021
parseRotoProperty(const QString & prop)1022 void KeyframeModel::parseRotoProperty(const QString &prop)
1023 {
1024 Fun undo = []() { return true; };
1025 Fun redo = []() { return true; };
1026
1027 QJsonParseError jsonError;
1028 QJsonDocument doc = QJsonDocument::fromJson(prop.toLatin1(), &jsonError);
1029 QVariant data = doc.toVariant();
1030 if (data.canConvert(QVariant::Map)) {
1031 QMap<QString, QVariant> map = data.toMap();
1032 QMap<QString, QVariant>::const_iterator i = map.constBegin();
1033 while (i != map.constEnd()) {
1034 addKeyframe(GenTime(i.key().toInt(), pCore->getCurrentFps()), KeyframeType::Linear, i.value(), false, undo, redo);
1035 ++i;
1036 }
1037 }
1038 }
1039
getInterpolatedValue(int p) const1040 QVariant KeyframeModel::getInterpolatedValue(int p) const
1041 {
1042 auto pos = GenTime(p, pCore->getCurrentFps());
1043 return getInterpolatedValue(pos);
1044 }
1045
updateInterpolated(const QVariant & interpValue,double val)1046 QVariant KeyframeModel::updateInterpolated(const QVariant &interpValue, double val)
1047 {
1048 QStringList vals = interpValue.toString().split(QLatin1Char(' '));
1049 if (!vals.isEmpty()) {
1050 vals[vals.size() - 1] = QString::number(val, 'f');
1051 }
1052 return vals.join(QLatin1Char(' '));
1053 }
1054
getNormalizedValue(double newVal) const1055 QVariant KeyframeModel::getNormalizedValue(double newVal) const
1056 {
1057 if (auto ptr = m_model.lock()) {
1058 double min = ptr->data(m_index, AssetParameterModel::VisualMinRole).toDouble();
1059 double max = ptr->data(m_index, AssetParameterModel::VisualMaxRole).toDouble();
1060 if (qFuzzyIsNull(min) && qFuzzyIsNull(max)) {
1061 min = ptr->data(m_index, AssetParameterModel::MinRole).toDouble();
1062 max = ptr->data(m_index, AssetParameterModel::MaxRole).toDouble();
1063 }
1064 if (qFuzzyIsNull(min) && qFuzzyIsNull(max)) {
1065 min = 0.;
1066 max = 1.;
1067 }
1068 double factor = ptr->data(m_index, AssetParameterModel::FactorRole).toDouble();
1069 double norm = ptr->data(m_index, AssetParameterModel::DefaultRole).toDouble();
1070 int logRole = ptr->data(m_index, AssetParameterModel::ScaleRole).toInt();
1071 double realValue;
1072 if (logRole == -1) {
1073 // Logarythmic scale
1074 if (newVal >= 0.5) {
1075 realValue = norm + pow(2 * (newVal - 0.5), 10.0 / 6) * (max / factor - norm);
1076 } else {
1077 realValue = norm - pow(2 * (0.5 - newVal), 10.0 / 6) * (norm - min / factor);
1078 }
1079 } else {
1080 realValue = (newVal * (max - min) + min) / factor;
1081 }
1082 return QVariant(realValue);
1083 }
1084 return QVariant();
1085 }
1086
getInterpolatedValue(const GenTime & pos) const1087 QVariant KeyframeModel::getInterpolatedValue(const GenTime &pos) const
1088 {
1089 if (m_keyframeList.count(pos) > 0) {
1090 return m_keyframeList.at(pos).second;
1091 }
1092 if (m_keyframeList.size() == 0) {
1093 return QVariant();
1094 }
1095 Mlt::Properties mlt_prop;
1096 QString animData;
1097 int out = 0;
1098 bool useOpacity = false;
1099 if (auto ptr = m_model.lock()) {
1100 ptr->passProperties(mlt_prop);
1101 out = ptr->data(m_index, AssetParameterModel::ParentDurationRole).toInt();
1102 useOpacity = ptr->data(m_index, AssetParameterModel::OpacityRole).toBool();
1103 animData = ptr->data(m_index, AssetParameterModel::ValueRole).toString();
1104 }
1105 if (m_paramType == ParamType::KeyframeParam || m_paramType == ParamType::ColorWheel) {
1106 if (!animData.isEmpty()) {
1107 mlt_prop.set("key", animData.toUtf8().constData());
1108 // This is a fake query to force the animation to be parsed
1109 (void)mlt_prop.anim_get_double("key", 0, out);
1110 return QVariant(mlt_prop.anim_get_double("key", pos.frames(pCore->getCurrentFps())));
1111 }
1112 return QVariant();
1113 } else if (m_paramType == ParamType::AnimatedRect) {
1114 if (!animData.isEmpty()) {
1115 mlt_prop.set("key", animData.toUtf8().constData());
1116 // This is a fake query to force the animation to be parsed
1117 (void)mlt_prop.anim_get_double("key", 0, out);
1118 mlt_rect rect = mlt_prop.anim_get_rect("key", pos.frames(pCore->getCurrentFps()));
1119 QString res = QStringLiteral("%1 %2 %3 %4").arg(int(rect.x)).arg(int(rect.y)).arg(int(rect.w)).arg(int(rect.h));
1120 if (useOpacity) {
1121 res.append(QStringLiteral(" %1").arg(QString::number(rect.o, 'f')));
1122 }
1123 return QVariant(res);
1124 }
1125 return QVariant();
1126 } else if (m_paramType == ParamType::Roto_spline) {
1127 // interpolate
1128 auto next = m_keyframeList.upper_bound(pos);
1129 if (next == m_keyframeList.cbegin()) {
1130 return (m_keyframeList.cbegin())->second.second;
1131 } else if (next == m_keyframeList.cend()) {
1132 auto it = m_keyframeList.cend();
1133 --it;
1134 return it->second.second;
1135 }
1136 auto prev = next;
1137 --prev;
1138
1139 QSize frame = pCore->getCurrentFrameSize();
1140 QList<BPoint> p1 = RotoHelper::getPoints(prev->second.second, frame);
1141 QList<BPoint> p2 = RotoHelper::getPoints(next->second.second, frame);
1142 // relPos should be in [0,1]:
1143 // - equal to 0 on prev keyframe
1144 // - equal to 1 on next keyframe
1145 qreal relPos = 0;
1146 if (next->first != prev->first) {
1147 relPos = (pos.frames(pCore->getCurrentFps()) - prev->first.frames(pCore->getCurrentFps())) / qreal
1148 (((next->first - prev->first).frames(pCore->getCurrentFps())));
1149 }
1150 int count = qMin(p1.count(), p2.count());
1151 QList<QVariant> vlist;
1152 for (int i = 0; i < count; ++i) {
1153 BPoint bp;
1154 QList<QVariant> pl;
1155 for (int j = 0; j < 3; ++j) {
1156 if (p1.at(i)[j] != p2.at(i)[j]) {
1157 bp[j] = QLineF(p1.at(i)[j], p2.at(i)[j]).pointAt(relPos);
1158 } else {
1159 bp[j] = p1.at(i)[j];
1160 }
1161 pl << QVariant(QList<QVariant>() << QVariant(bp[j].x() / frame.width()) << QVariant(bp[j].y() / frame.height()));
1162 }
1163 vlist << QVariant(pl);
1164 }
1165 return vlist;
1166 }
1167 return QVariant();
1168 }
1169
sendModification()1170 void KeyframeModel::sendModification()
1171 {
1172 if (auto ptr = m_model.lock()) {
1173 Q_ASSERT(m_index.isValid());
1174 QString name = ptr->data(m_index, AssetParameterModel::NameRole).toString();
1175 if (m_paramType == ParamType::KeyframeParam || m_paramType == ParamType::AnimatedRect || m_paramType == ParamType::Roto_spline || m_paramType == ParamType::ColorWheel) {
1176 m_lastData = getAnimProperty();
1177 ptr->setParameter(name, m_lastData, false, m_index);
1178 } else {
1179 Q_ASSERT(false); // Not implemented, TODO
1180 }
1181 }
1182 }
1183
realValue(double normalizedValue) const1184 QString KeyframeModel::realValue(double normalizedValue) const
1185 {
1186 double value = getNormalizedValue(normalizedValue).toDouble();
1187 if (auto ptr = m_model.lock()) {
1188 int decimals = ptr->data(m_index, AssetParameterModel::DecimalsRole).toInt();
1189 value *= ptr->data(m_index, AssetParameterModel::FactorRole).toDouble();
1190 QString result;
1191 if (decimals == 0) {
1192 if (m_paramType == ParamType::AnimatedRect) {
1193 value = qRound(value * 100.);
1194 }
1195 // Fix rounding erros in double > int conversion
1196 if (value > 0.) {
1197 value += 0.001;
1198 } else {
1199 value -= 0.001;
1200 }
1201 result = QString::number(int(value));
1202 } else {
1203 result = QString::number(value, 'f', decimals);
1204 }
1205 result.append(ptr->data(m_index, AssetParameterModel::SuffixRole).toString());
1206 return result;
1207 }
1208 return QString::number(value);
1209 }
1210
refresh()1211 void KeyframeModel::refresh()
1212 {
1213 Q_ASSERT(m_index.isValid());
1214 QString animData;
1215 if (auto ptr = m_model.lock()) {
1216 animData = ptr->data(m_index, AssetParameterModel::ValueRole).toString();
1217 } else {
1218 qDebug() << "WARNING : unable to access keyframe's model";
1219 return;
1220 }
1221 if (animData == m_lastData) {
1222 // nothing to do
1223 qDebug() << "// DATA WAS ALREADY PARSED, ABORTING REFRESH\n";
1224 return;
1225 }
1226 if (m_paramType == ParamType::KeyframeParam || m_paramType == ParamType::AnimatedRect || m_paramType == ParamType::ColorWheel) {
1227 parseAnimProperty(animData);
1228 } else if (m_paramType == ParamType::Roto_spline) {
1229 parseRotoProperty(animData);
1230 } else {
1231 // first, try to convert to double
1232 bool ok = false;
1233 double value = animData.toDouble(&ok);
1234 if (ok) {
1235 Fun undo = []() { return true; };
1236 Fun redo = []() { return true; };
1237 addKeyframe(GenTime(), KeyframeType::Linear, QVariant(value), false, undo, redo);
1238 } else {
1239 Q_ASSERT(false); // Not implemented, TODO
1240 }
1241 }
1242 m_lastData = animData;
1243 }
1244
reset()1245 void KeyframeModel::reset()
1246 {
1247 Q_ASSERT(m_index.isValid());
1248 QString animData;
1249 if (auto ptr = m_model.lock()) {
1250 animData = ptr->data(m_index, AssetParameterModel::ValueRole).toString();
1251 } else {
1252 qDebug() << "WARNING : unable to access keyframe's model";
1253 return;
1254 }
1255 if (animData == m_lastData) {
1256 // nothing to do
1257 qDebug() << "// DATA WAS ALREADY PARSED, ABORTING\n_________________";
1258 return;
1259 }
1260 if (m_paramType == ParamType::KeyframeParam || m_paramType == ParamType::AnimatedRect || m_paramType == ParamType::ColorWheel) {
1261 qDebug() << "parsing keyframe" << animData;
1262 resetAnimProperty(animData);
1263 } else if (m_paramType == ParamType::Roto_spline) {
1264 // TODO: resetRotoProperty(animData);
1265 } else {
1266 // first, try to convert to double
1267 bool ok = false;
1268 double value = animData.toDouble(&ok);
1269 if (ok) {
1270 Fun undo = []() { return true; };
1271 Fun redo = []() { return true; };
1272 addKeyframe(GenTime(), KeyframeType::Linear, QVariant(value), false, undo, redo);
1273 PUSH_UNDO(undo, redo, i18n("Reset effect"));
1274 qDebug() << "KEYFRAME ADDED" << value;
1275 } else {
1276 Q_ASSERT(false); // Not implemented, TODO
1277 }
1278 }
1279 m_lastData = animData;
1280 }
1281
getRanges(const QString & animData,const std::shared_ptr<AssetParameterModel> & model)1282 QList<QPoint> KeyframeModel::getRanges(const QString &animData, const std::shared_ptr<AssetParameterModel> &model)
1283 {
1284 Mlt::Properties mlt_prop;
1285 model->passProperties(mlt_prop);
1286 mlt_prop.set("key", animData.toUtf8().constData());
1287 // This is a fake query to force the animation to be parsed
1288 (void)mlt_prop.anim_get_int("key", 0, 0);
1289
1290 Mlt::Animation anim = mlt_prop.get_animation("key");
1291 int frame;
1292 mlt_keyframe_type type;
1293 anim.key_get(0, frame, type);
1294 mlt_rect rect = mlt_prop.anim_get_rect("key", frame);
1295 QPoint pX(int(rect.x), int(rect.x));
1296 QPoint pY(int(rect.y), int(rect.y));
1297 QPoint pW(int(rect.w), int(rect.w));
1298 QPoint pH(int(rect.h), int(rect.h));
1299 QPoint pO(int(rect.o), int(rect.o));
1300 for (int i = 1; i < anim.key_count(); ++i) {
1301 anim.key_get(i, frame, type);
1302 if (!animData.contains(QLatin1Char('='))) {
1303 // TODO: use a default user defined type
1304 type = mlt_keyframe_linear;
1305 }
1306 rect = mlt_prop.anim_get_rect("key", frame);
1307 pX.setX(qMin(int(rect.x), pX.x()));
1308 pX.setY(qMax(int(rect.x), pX.y()));
1309 pY.setX(qMin(int(rect.y), pY.x()));
1310 pY.setY(qMax(int(rect.y), pY.y()));
1311 pW.setX(qMin(int(rect.w), pW.x()));
1312 pW.setY(qMax(int(rect.w), pW.y()));
1313 pH.setX(qMin(int(rect.h), pH.x()));
1314 pH.setY(qMax(int(rect.h), pH.y()));
1315 pO.setX(qMin(int(rect.o), pO.x()));
1316 pO.setY(qMax(int(rect.o), pO.y()));
1317 // value = QVariant(QStringLiteral("%1 %2 %3 %4 %5").arg(rect.x).arg(rect.y).arg(rect.w).arg(rect.h).arg(locale.toString(rect.o)));
1318 }
1319 QList<QPoint> result{pX, pY, pW, pH, pO};
1320 return result;
1321 }
1322
getAnimation(std::shared_ptr<AssetParameterModel> model,const QString & animData,int duration)1323 std::shared_ptr<Mlt::Properties> KeyframeModel::getAnimation(std::shared_ptr<AssetParameterModel> model, const QString &animData, int duration)
1324 {
1325 std::shared_ptr<Mlt::Properties> mlt_prop(new Mlt::Properties());
1326 model->passProperties(*mlt_prop.get());
1327 mlt_prop->set("key", animData.toUtf8().constData());
1328 // This is a fake query to force the animation to be parsed
1329 (void)mlt_prop->anim_get_rect("key", 0, duration);
1330 return mlt_prop;
1331 }
1332
getAnimationStringWithOffset(std::shared_ptr<AssetParameterModel> model,const QString & animData,int offset)1333 const QString KeyframeModel::getAnimationStringWithOffset(std::shared_ptr<AssetParameterModel> model, const QString &animData, int offset)
1334 {
1335 Mlt::Properties mlt_prop;
1336 model->passProperties(mlt_prop);
1337 mlt_prop.set("key", animData.toUtf8().constData());
1338 // This is a fake query to force the animation to be parsed
1339 (void)mlt_prop.anim_get_rect("key", 0);
1340 Mlt::Animation anim = mlt_prop.get_animation("key");
1341 if (offset > 0) {
1342 for (int i = anim.key_count() - 1; i >= 0; --i) {
1343 int pos = anim.key_get_frame(i) + offset;
1344 anim.key_set_frame(i, pos);
1345 }
1346 } else {
1347 for (int i = 0; i < anim.key_count(); ++i) {
1348 int pos = anim.key_get_frame(i) + offset;
1349 if (pos > 0) {
1350 anim.key_set_frame(i, pos);
1351 }
1352 }
1353 }
1354 return qstrdup(anim.serialize_cut());
1355 }
1356
getKeyframePos() const1357 QList<GenTime> KeyframeModel::getKeyframePos() const
1358 {
1359 QList<GenTime> all_pos;
1360 for (const auto &m : m_keyframeList) {
1361 all_pos.push_back(m.first);
1362 }
1363 return all_pos;
1364 }
1365
removeNextKeyframes(GenTime pos,Fun & undo,Fun & redo)1366 bool KeyframeModel::removeNextKeyframes(GenTime pos, Fun &undo, Fun &redo)
1367 {
1368 QWriteLocker locker(&m_lock);
1369 std::vector<GenTime> all_pos;
1370 Fun local_undo = []() { return true; };
1371 Fun local_redo = []() { return true; };
1372 int firstPos = 0;
1373 for (const auto &m : m_keyframeList) {
1374 if (m.first <= pos) {
1375 firstPos++;
1376 continue;
1377 }
1378 all_pos.push_back(m.first);
1379 }
1380 int kfrCount = int(all_pos.size());
1381 // Remove deleted keyframes from selection
1382 if (auto ptr = m_model.lock()) {
1383 QVector <int> selection;
1384 for (auto &ix : ptr->m_selectedKeyframes) {
1385 if (ix < kfrCount) {
1386 selection << ix;
1387 }
1388 }
1389 ptr->m_selectedKeyframes = selection;
1390 }
1391 // we trigger only one global remove/insertrow event
1392 Fun update_redo_start = [this, firstPos, kfrCount]() {
1393 beginRemoveRows(QModelIndex(), firstPos, kfrCount);
1394 return true;
1395 };
1396 Fun update_redo_end = [this]() {
1397 endRemoveRows();
1398 return true;
1399 };
1400 Fun update_undo_start = [this, firstPos, kfrCount]() {
1401 beginInsertRows(QModelIndex(), firstPos, kfrCount);
1402 return true;
1403 };
1404 Fun update_undo_end = [this]() {
1405 endInsertRows();
1406 return true;
1407 };
1408 PUSH_LAMBDA(update_redo_start, local_redo);
1409 PUSH_LAMBDA(update_undo_start, local_undo);
1410 update_redo_start();
1411 bool res = true;
1412 for (const auto &p : all_pos) {
1413 res = removeKeyframe(p, local_undo, local_redo, false);
1414 if (!res) {
1415 bool undone = local_undo();
1416 Q_ASSERT(undone);
1417 return false;
1418 }
1419 }
1420 update_redo_end();
1421 PUSH_LAMBDA(update_redo_end, local_redo);
1422 PUSH_LAMBDA(update_undo_end, local_undo);
1423 UPDATE_UNDO_REDO(local_redo, local_undo, undo, redo);
1424 return true;
1425 }
1426
setSelectedKeyframe(int ix,bool add)1427 void KeyframeModel::setSelectedKeyframe(int ix, bool add)
1428 {
1429 QVector<int> previous;
1430 if (auto ptr = m_model.lock()) {
1431 if (add) {
1432 if (ptr->m_selectedKeyframes.contains(ix)) {
1433 // remove from selection
1434 ptr->m_selectedKeyframes.removeAll(ix);
1435 } else {
1436 ptr->m_selectedKeyframes << ix;
1437 }
1438 } else {
1439 previous = ptr->m_selectedKeyframes;
1440 ptr->m_selectedKeyframes = {ix};
1441 }
1442 }
1443 if (!add) {
1444 for (auto &ix2 : previous) {
1445 if (ix2 > -1) {
1446 emit requestModelUpdate(index(ix2), index(ix2), {SelectedRole});
1447 }
1448 }
1449 }
1450 if (ix > -1) {
1451 emit requestModelUpdate(index(ix), index(ix), {SelectedRole});
1452 }
1453 }
1454
setSelectedKeyframes(QVector<int> selection)1455 void KeyframeModel::setSelectedKeyframes(QVector<int> selection)
1456 {
1457 QVector<int> previous;
1458 selection.removeAll(-1);
1459 std::sort(selection.begin(), selection.end());
1460 if (auto ptr = m_model.lock()) {
1461 previous = ptr->m_selectedKeyframes;
1462 ptr->m_selectedKeyframes = selection;
1463 }
1464 if (!selection.isEmpty()) {
1465 emit requestModelUpdate(index(selection.first()), index(selection.last()), {SelectedRole});
1466 }
1467 for (auto &ix : previous) {
1468 if (ix > -1 && !selection.contains(ix)) {
1469 emit requestModelUpdate(index(ix), index(ix), {SelectedRole});
1470 }
1471 }
1472 }
1473
activeKeyframe() const1474 int KeyframeModel::activeKeyframe() const
1475 {
1476 if (auto ptr = m_model.lock()) {
1477 return ptr->m_activeKeyframe;
1478 }
1479 return -1;
1480 }
1481
setActiveKeyframe(int ix)1482 void KeyframeModel::setActiveKeyframe(int ix)
1483 {
1484 int oldActive = -1;
1485 if (auto ptr = m_model.lock()) {
1486 oldActive = ptr->m_activeKeyframe;
1487 if (oldActive == ix) {
1488 // Keyframe already active
1489 return;
1490 }
1491 ptr->m_activeKeyframe = ix;
1492 }
1493 emit requestModelUpdate(index(ix), index(ix), {ActiveRole});
1494 if (oldActive > -1) {
1495 emit requestModelUpdate(index(oldActive), index(oldActive), {ActiveRole});
1496 }
1497 }
1498
getIndexForPos(const GenTime pos) const1499 int KeyframeModel::getIndexForPos(const GenTime pos) const
1500 {
1501 if (m_keyframeList.count(pos) == 0) {
1502 return -1;
1503 }
1504 return static_cast<int>(std::distance(m_keyframeList.begin(), m_keyframeList.find(pos)));
1505 }
1506