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