File indexing completed on 2024-05-05 04:52:42
0001 /* 0002 SPDX-FileCopyrightText: 2017 Nicolas Carion 0003 SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL 0004 */ 0005 0006 #include "keyframemodel.hpp" 0007 #include "../../bpoint.h" 0008 #include "core.h" 0009 #include "doc/docundostack.hpp" 0010 #include "macros.hpp" 0011 #include "profiles/profilemodel.hpp" 0012 #include "rotoscoping/rotohelper.hpp" 0013 #include "utils/qcolorutils.h" 0014 0015 #include <QDebug> 0016 #include <QJsonDocument> 0017 #include <QLineF> 0018 #include <QSize> 0019 #include <mlt++/Mlt.h> 0020 #include <utility> 0021 0022 // std::unordered_map and QHash could not be used here 0023 #ifdef USE_MLT_NEW_KEYFRAMES 0024 extern const QMap<KeyframeType, QString> KeyframeTypeName = { 0025 {KeyframeType::Linear, i18n("Linear")}, 0026 {KeyframeType::Discrete, i18n("Discrete")}, 0027 {KeyframeType::CurveSmooth, i18n("Smooth")}, 0028 {KeyframeType::BounceIn, i18n("Bounce In")}, 0029 {KeyframeType::BounceOut, i18n("Bounce Out")}, 0030 {KeyframeType::CubicIn, i18n("Cubic In")}, 0031 {KeyframeType::CubicOut, i18n("Cubic Out")}, 0032 {KeyframeType::ExponentialIn, i18n("Exponential In")}, 0033 {KeyframeType::ExponentialOut, i18n("Exponential Out")}, 0034 {KeyframeType::CircularIn, i18n("Circular In")}, 0035 {KeyframeType::CircularOut, i18n("Circular Out")}, 0036 {KeyframeType::ElasticIn, i18n("Elastic In")}, 0037 {KeyframeType::ElasticOut, i18n("Elastic Out")}, 0038 {KeyframeType::Curve, i18n("Smooth (deprecated)")}, 0039 }; 0040 #else 0041 extern const QMap<KeyframeType, QString> KeyframeTypeName = { 0042 {KeyframeType::Linear, i18n("Linear")}, {KeyframeType::Discrete, i18n("Discrete")}, {KeyframeType::Curve, i18n("Smooth")}}; 0043 #endif 0044 0045 KeyframeModel::KeyframeModel(std::weak_ptr<AssetParameterModel> model, const QModelIndex &index, std::weak_ptr<DocUndoStack> undo_stack, int in, int out, 0046 QObject *parent) 0047 : QAbstractListModel(parent) 0048 , m_model(std::move(model)) 0049 , m_undoStack(std::move(undo_stack)) 0050 , m_index(index) 0051 , m_lastData() 0052 , m_lock(QReadWriteLock::Recursive) 0053 { 0054 qDebug() << "Construct keyframemodel. Checking model:" << m_model.expired(); 0055 if (auto ptr = m_model.lock()) { 0056 m_paramType = ptr->data(m_index, AssetParameterModel::TypeRole).value<ParamType>(); 0057 } 0058 setup(); 0059 refresh(in, out); 0060 } 0061 0062 void KeyframeModel::setup() 0063 { 0064 // We connect the signals of the abstractitemmodel to a more generic one. 0065 connect(this, &KeyframeModel::columnsMoved, this, &KeyframeModel::modelChanged); 0066 connect(this, &KeyframeModel::columnsRemoved, this, &KeyframeModel::modelChanged); 0067 connect(this, &KeyframeModel::columnsInserted, this, &KeyframeModel::modelChanged); 0068 connect(this, &KeyframeModel::rowsMoved, this, &KeyframeModel::modelChanged); 0069 connect(this, &KeyframeModel::rowsRemoved, this, &KeyframeModel::modelChanged); 0070 connect(this, &KeyframeModel::rowsInserted, this, &KeyframeModel::modelChanged); 0071 connect(this, &KeyframeModel::modelReset, this, &KeyframeModel::modelChanged); 0072 connect(this, &KeyframeModel::dataChanged, this, [this](const QModelIndex &, const QModelIndex &, const QVector<int> &roles) { 0073 QVector<int> notParamRoles = {SelectedRole, ActiveRole}; 0074 if (roles.size() == 1 && notParamRoles.contains(roles.first())) { 0075 // Selection role changed, no need to update the keyframe parameters 0076 return; 0077 } 0078 Q_EMIT modelChanged(); 0079 }); 0080 connect(this, &KeyframeModel::modelChanged, this, &KeyframeModel::sendModification); 0081 } 0082 0083 bool KeyframeModel::addKeyframe(GenTime pos, KeyframeType type, QVariant value, bool notify, Fun &undo, Fun &redo) 0084 { 0085 qDebug() << "ADD keyframe" << pos.frames(pCore->getCurrentFps()) << value << notify; 0086 QWriteLocker locker(&m_lock); 0087 Fun local_undo = []() { return true; }; 0088 Fun local_redo = []() { return true; }; 0089 if (m_keyframeList.count(pos) > 0) { 0090 qDebug() << "already there"; 0091 if (std::pair<KeyframeType, QVariant>({type, value}) == m_keyframeList.at(pos)) { 0092 qDebug() << "nothing to do"; 0093 return true; // nothing to do 0094 } 0095 // In this case we simply change the type and value 0096 KeyframeType oldType = m_keyframeList[pos].first; 0097 QVariant oldValue = m_keyframeList[pos].second; 0098 local_undo = updateKeyframe_lambda(pos, oldType, oldValue, notify); 0099 local_redo = updateKeyframe_lambda(pos, type, value, notify); 0100 if (local_redo()) { 0101 UPDATE_UNDO_REDO(local_redo, local_undo, undo, redo); 0102 return true; 0103 } 0104 } else { 0105 Fun redo_first = addKeyframe_lambda(pos, type, value, notify); 0106 if (redo_first()) { 0107 local_redo = addKeyframe_lambda(pos, type, value, true); 0108 local_undo = deleteKeyframe_lambda(pos, true); 0109 UPDATE_UNDO_REDO(local_redo, local_undo, undo, redo); 0110 return true; 0111 } 0112 } 0113 return false; 0114 } 0115 0116 bool KeyframeModel::addKeyframe(int frame, double normalizedValue) 0117 { 0118 QVariant result = getNormalizedValue(normalizedValue); 0119 if (result.isValid()) { 0120 // TODO: Use default configurable kf type 0121 return addKeyframe(GenTime(frame, pCore->getCurrentFps()), KeyframeType::Linear, result); 0122 } 0123 return false; 0124 } 0125 0126 bool KeyframeModel::addKeyframe(GenTime pos, KeyframeType type, QVariant value) 0127 { 0128 QWriteLocker locker(&m_lock); 0129 Fun undo = []() { return true; }; 0130 Fun redo = []() { return true; }; 0131 0132 bool update = (m_keyframeList.count(pos) > 0); 0133 bool res = addKeyframe(pos, type, std::move(value), true, undo, redo); 0134 if (res) { 0135 PUSH_UNDO(undo, redo, update ? i18n("Change keyframe type") : i18n("Add keyframe")); 0136 } 0137 return res; 0138 } 0139 0140 bool KeyframeModel::removeKeyframe(GenTime pos, Fun &undo, Fun &redo, bool notify, bool updateSelection) 0141 { 0142 qDebug() << "Going to remove keyframe at " << pos.frames(pCore->getCurrentFps()) << " NOTIFY: " << notify; 0143 qDebug() << "before" << getAnimProperty(); 0144 QWriteLocker locker(&m_lock); 0145 Q_ASSERT(m_keyframeList.count(pos) > 0); 0146 KeyframeType oldType = m_keyframeList[pos].first; 0147 QVariant oldValue = m_keyframeList[pos].second; 0148 Fun select_undo = []() { return true; }; 0149 Fun select_redo = []() { return true; }; 0150 if (updateSelection) { 0151 if (auto ptr = m_model.lock()) { 0152 if (!ptr->m_selectedKeyframes.isEmpty()) { 0153 int ix = getIndexForPos(pos); 0154 QVector<int> selection; 0155 QVector<int> prevSelection = ptr->m_selectedKeyframes; 0156 for (auto &kf : prevSelection) { 0157 if (kf == ix) { 0158 continue; 0159 } 0160 if (kf < ix) { 0161 selection << kf; 0162 } else { 0163 selection << (kf - 1); 0164 } 0165 } 0166 setActiveKeyframe(-1); 0167 std::sort(selection.begin(), selection.end()); 0168 select_redo = [this, selection]() { 0169 setSelectedKeyframes(selection); 0170 return true; 0171 }; 0172 select_undo = [this, prevSelection]() { 0173 setSelectedKeyframes(prevSelection); 0174 return true; 0175 }; 0176 } 0177 } 0178 } 0179 Fun redo_first = deleteKeyframe_lambda(pos, notify); 0180 if (redo_first()) { 0181 Fun local_undo = addKeyframe_lambda(pos, oldType, oldValue, notify); 0182 select_redo(); 0183 qDebug() << "after" << getAnimProperty(); 0184 UPDATE_UNDO_REDO(redo_first, local_undo, undo, redo); 0185 UPDATE_UNDO_REDO(select_redo, select_undo, undo, redo); 0186 return true; 0187 } 0188 return false; 0189 } 0190 0191 bool KeyframeModel::duplicateKeyframe(GenTime srcPos, GenTime dstPos, Fun &undo, Fun &redo) 0192 { 0193 QWriteLocker locker(&m_lock); 0194 Q_ASSERT(m_keyframeList.count(srcPos) > 0); 0195 KeyframeType oldType = m_keyframeList[srcPos].first; 0196 QVariant oldValue = m_keyframeList[srcPos].second; 0197 Fun local_redo = addKeyframe_lambda(dstPos, oldType, oldValue, true); 0198 Fun local_undo = deleteKeyframe_lambda(dstPos, true); 0199 if (local_redo()) { 0200 UPDATE_UNDO_REDO(local_redo, local_undo, undo, redo); 0201 return true; 0202 } 0203 return false; 0204 } 0205 0206 bool KeyframeModel::removeKeyframe(int frame) 0207 { 0208 GenTime pos(frame, pCore->getCurrentFps()); 0209 return removeKeyframe(pos); 0210 } 0211 0212 bool KeyframeModel::removeKeyframe(GenTime pos) 0213 { 0214 QWriteLocker locker(&m_lock); 0215 Fun undo = []() { return true; }; 0216 Fun redo = []() { return true; }; 0217 0218 if (m_keyframeList.count(pos) > 0 && m_keyframeList.find(pos) == m_keyframeList.begin()) { 0219 return false; // initial point must stay 0220 } 0221 0222 bool res = removeKeyframe(pos, undo, redo); 0223 if (res) { 0224 PUSH_UNDO(undo, redo, i18n("Delete keyframe")); 0225 } 0226 return res; 0227 } 0228 0229 GenTime KeyframeModel::getPosAtIndex(int ix) const 0230 { 0231 QList<GenTime> positions = getKeyframePos(); 0232 std::sort(positions.begin(), positions.end()); 0233 if (ix < 0 || ix >= positions.count()) { 0234 return GenTime(); 0235 } 0236 return positions.at(ix); 0237 } 0238 0239 bool KeyframeModel::moveKeyframe(GenTime oldPos, GenTime pos, const QVariant &newVal, Fun &undo, Fun &redo, bool updateView) 0240 { 0241 qDebug() << "starting to move keyframe" << oldPos.frames(pCore->getCurrentFps()) << pos.frames(pCore->getCurrentFps()); 0242 QWriteLocker locker(&m_lock); 0243 // Check if we have several selected keyframes 0244 if (oldPos == pos) { 0245 if (!newVal.isValid()) { 0246 // no change 0247 return true; 0248 } 0249 } 0250 if (auto ptr = m_model.lock()) { 0251 if (ptr->m_selectedKeyframes.size() > 1) { 0252 // We have several selected keyframes, move them all 0253 double offset = 0.; 0254 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) 0255 if (newVal.isValid() && newVal.type() == QVariant::Double) { 0256 #else 0257 if (newVal.isValid() && newVal.typeId() == QMetaType::Double) { 0258 #endif 0259 int row = static_cast<int>(std::distance(m_keyframeList.begin(), m_keyframeList.find(oldPos))); 0260 double oldVal = data(index(row), NormalizedValueRole).toDouble(); 0261 offset = newVal.toDouble() - oldVal; 0262 } 0263 QVector<GenTime> positions; 0264 for (auto &kf : ptr->m_selectedKeyframes) { 0265 if (kf > 0) { 0266 positions << getPosAtIndex(kf); 0267 } 0268 } 0269 GenTime delta = pos - oldPos; 0270 if (pos > oldPos) { 0271 // Moving right, reverse sort 0272 std::sort(positions.rbegin(), positions.rend()); 0273 // Check max pos 0274 bool ok = false; 0275 GenTime test = positions.first(); 0276 auto next = getNextKeyframe(test, &ok); 0277 if (ok) { 0278 delta = qMin(delta, next.first - GenTime(1, pCore->getCurrentFps()) - test); 0279 } 0280 } else { 0281 // Moving left 0282 std::sort(positions.begin(), positions.end()); 0283 // Check min pos 0284 bool ok = false; 0285 GenTime test = positions.first(); 0286 auto next = getPrevKeyframe(test, &ok); 0287 if (ok) { 0288 delta = qMax(delta, (next.first + GenTime(1, pCore->getCurrentFps())) - test); 0289 } 0290 } 0291 if (delta == GenTime()) { 0292 if (!newVal.isValid()) { 0293 // no change 0294 return true; 0295 } 0296 } 0297 bool res = true; 0298 for (auto &p : positions) { 0299 if (p == oldPos) { 0300 res = res && moveOneKeyframe(oldPos, oldPos + delta, newVal, undo, redo, updateView); 0301 } else { 0302 if (!qFuzzyIsNull(offset)) { 0303 // Calculate new value 0304 int row = static_cast<int>(std::distance(m_keyframeList.begin(), m_keyframeList.find(p))); 0305 double newVal2 = qBound(0., data(index(row), NormalizedValueRole).toDouble() + offset, 1.); 0306 res = res && moveOneKeyframe(p, p + delta, newVal2, undo, redo, updateView); 0307 } else { 0308 res = res && moveOneKeyframe(p, p + delta, QVariant(), undo, redo, updateView); 0309 } 0310 } 0311 } 0312 return res; 0313 } else { 0314 // We have only one selected keyframe 0315 if (pos > oldPos) { 0316 // Moving right 0317 bool ok = false; 0318 auto next = getNextKeyframe(oldPos, &ok); 0319 if (ok) { 0320 pos = qMin(pos, next.first - GenTime(1, pCore->getCurrentFps())); 0321 } 0322 } else { 0323 // Moving left 0324 bool ok = false; 0325 auto next = getPrevKeyframe(oldPos, &ok); 0326 if (ok) { 0327 pos = qMax(pos, next.first + GenTime(1, pCore->getCurrentFps())); 0328 } 0329 } 0330 return moveOneKeyframe(oldPos, pos, newVal, undo, redo, updateView); 0331 } 0332 } 0333 return false; 0334 } 0335 0336 bool KeyframeModel::moveOneKeyframe(GenTime oldPos, GenTime pos, QVariant newVal, Fun &undo, Fun &redo, bool updateView) 0337 { 0338 qDebug() << "starting to move keyframe" << oldPos.frames(pCore->getCurrentFps()) << pos.frames(pCore->getCurrentFps()); 0339 QWriteLocker locker(&m_lock); 0340 Q_ASSERT(m_keyframeList.count(oldPos) > 0); 0341 if (oldPos == pos) { 0342 if (!newVal.isValid()) { 0343 // no change 0344 return true; 0345 } 0346 if (m_paramType == ParamType::AnimatedRect) { 0347 return updateKeyframe(pos, newVal); 0348 } 0349 // Calculate real value from normalized 0350 QVariant result = getNormalizedValue(newVal.toDouble()); 0351 return updateKeyframe(pos, result); 0352 } 0353 if (oldPos != pos && hasKeyframe(pos)) { 0354 // Move rejected, another keyframe is here 0355 qDebug() << "==== MOVE REJECTED!!"; 0356 return false; 0357 } 0358 KeyframeType oldType = m_keyframeList[oldPos].first; 0359 QVariant oldValue = m_keyframeList[oldPos].second; 0360 Fun local_undo = []() { return true; }; 0361 Fun local_redo = []() { return true; }; 0362 qDebug() << getAnimProperty(); 0363 // TODO: use the new Animation::key_set_frame to move a keyframe 0364 bool res = removeKeyframe(oldPos, local_undo, local_redo, true, false); 0365 qDebug() << "Move keyframe finished deletion:" << res; 0366 qDebug() << getAnimProperty(); 0367 if (res) { 0368 if (m_paramType == ParamType::AnimatedRect) { 0369 if (!newVal.isValid()) { 0370 newVal = oldValue; 0371 } 0372 res = addKeyframe(pos, oldType, newVal, updateView, local_undo, local_redo); 0373 } else if (newVal.isValid()) { 0374 QVariant result = getNormalizedValue(newVal.toDouble()); 0375 if (result.isValid()) { 0376 res = addKeyframe(pos, oldType, result, updateView, local_undo, local_redo); 0377 } 0378 } else { 0379 res = addKeyframe(pos, oldType, oldValue, updateView, local_undo, local_redo); 0380 } 0381 qDebug() << "Move keyframe finished insertion:" << res; 0382 qDebug() << getAnimProperty(); 0383 } 0384 if (res) { 0385 UPDATE_UNDO_REDO(local_redo, local_undo, undo, redo); 0386 } else { 0387 bool undone = local_undo(); 0388 Q_ASSERT(undone); 0389 } 0390 return res; 0391 } 0392 0393 bool KeyframeModel::moveKeyframe(int oldPos, int pos, bool logUndo) 0394 { 0395 GenTime oPos(oldPos, pCore->getCurrentFps()); 0396 GenTime nPos(pos, pCore->getCurrentFps()); 0397 return moveKeyframe(oPos, nPos, QVariant(), logUndo); 0398 } 0399 0400 bool KeyframeModel::offsetKeyframes(int oldPos, int pos, bool logUndo) 0401 { 0402 if (oldPos == pos) return true; 0403 GenTime oldFrame(oldPos, pCore->getCurrentFps()); 0404 Q_ASSERT(m_keyframeList.count(oldFrame) > 0); 0405 GenTime diff(pos - oldPos, pCore->getCurrentFps()); 0406 QWriteLocker locker(&m_lock); 0407 Fun undo = []() { return true; }; 0408 Fun redo = []() { return true; }; 0409 QList<GenTime> times; 0410 for (const auto &m : m_keyframeList) { 0411 if (m.first < oldFrame) continue; 0412 times << m.first; 0413 } 0414 bool res = true; 0415 for (const auto &t : qAsConst(times)) { 0416 res &= moveKeyframe(t, t + diff, QVariant(), undo, redo); 0417 } 0418 if (res && logUndo) { 0419 PUSH_UNDO(undo, redo, i18nc("@action", "Move keyframes")); 0420 } 0421 return res; 0422 } 0423 0424 bool KeyframeModel::moveKeyframe(int oldPos, int pos, QVariant newVal) 0425 { 0426 GenTime oPos(oldPos, pCore->getCurrentFps()); 0427 GenTime nPos(pos, pCore->getCurrentFps()); 0428 return moveKeyframe(oPos, nPos, std::move(newVal), true); 0429 } 0430 0431 bool KeyframeModel::moveKeyframe(GenTime oldPos, GenTime pos, QVariant newVal, bool logUndo) 0432 { 0433 QWriteLocker locker(&m_lock); 0434 Q_ASSERT(m_keyframeList.count(oldPos) > 0); 0435 if (oldPos == pos) return true; 0436 Fun undo = []() { return true; }; 0437 Fun redo = []() { return true; }; 0438 bool res = moveKeyframe(oldPos, pos, std::move(newVal), undo, redo); 0439 if (res && logUndo) { 0440 PUSH_UNDO(undo, redo, i18nc("@action", "Move keyframe")); 0441 } 0442 return res; 0443 } 0444 0445 bool KeyframeModel::directUpdateKeyframe(GenTime pos, QVariant value, bool notify) 0446 { 0447 QWriteLocker locker(&m_lock); 0448 Q_ASSERT(m_keyframeList.count(pos) > 0); 0449 KeyframeType type = m_keyframeList[pos].first; 0450 auto operation = updateKeyframe_lambda(pos, type, std::move(value), notify); 0451 return operation(); 0452 } 0453 0454 bool KeyframeModel::updateKeyframe(GenTime pos, const QVariant &value, Fun &undo, Fun &redo, bool update) 0455 { 0456 QWriteLocker locker(&m_lock); 0457 Q_ASSERT(m_keyframeList.count(pos) > 0); 0458 KeyframeType type = m_keyframeList[pos].first; 0459 QVariant oldValue = m_keyframeList[pos].second; 0460 // Check if keyframe is different 0461 if (m_paramType == ParamType::KeyframeParam || m_paramType == ParamType::ColorWheel) { 0462 if (qFuzzyCompare(oldValue.toDouble(), value.toDouble())) return true; 0463 } 0464 auto operation = updateKeyframe_lambda(pos, type, value, update); 0465 auto reverse = updateKeyframe_lambda(pos, type, oldValue, update); 0466 bool res = operation(); 0467 if (res) { 0468 UPDATE_UNDO_REDO(operation, reverse, undo, redo); 0469 } 0470 return res; 0471 } 0472 0473 bool KeyframeModel::updateKeyframe(int pos, double newVal) 0474 { 0475 GenTime Pos(pos, pCore->getCurrentFps()); 0476 if (auto ptr = m_model.lock()) { 0477 double min = ptr->data(m_index, AssetParameterModel::VisualMinRole).toDouble(); 0478 double max = ptr->data(m_index, AssetParameterModel::VisualMaxRole).toDouble(); 0479 if (qFuzzyIsNull(min) && qFuzzyIsNull(max)) { 0480 min = ptr->data(m_index, AssetParameterModel::MinRole).toDouble(); 0481 max = ptr->data(m_index, AssetParameterModel::MaxRole).toDouble(); 0482 } 0483 double factor = ptr->data(m_index, AssetParameterModel::FactorRole).toDouble(); 0484 double norm = ptr->data(m_index, AssetParameterModel::DefaultRole).toDouble(); 0485 int logRole = ptr->data(m_index, AssetParameterModel::ScaleRole).toInt(); 0486 double realValue; 0487 if (logRole == -1) { 0488 // Logarythmic scale 0489 if (newVal >= 0.5) { 0490 realValue = norm + pow(2 * (newVal - 0.5), 10.0 / 6) * (max / factor - norm); 0491 } else { 0492 realValue = norm - pow(2 * (0.5 - newVal), 10.0 / 6) * (norm - min / factor); 0493 } 0494 } else { 0495 realValue = (newVal * (max - min) + min) / factor; 0496 } 0497 return updateKeyframe(Pos, realValue); 0498 } 0499 return false; 0500 } 0501 0502 bool KeyframeModel::updateKeyframe(GenTime pos, QVariant value) 0503 { 0504 QWriteLocker locker(&m_lock); 0505 Q_ASSERT(m_keyframeList.count(pos) > 0); 0506 0507 Fun undo = []() { return true; }; 0508 Fun redo = []() { return true; }; 0509 bool res = updateKeyframe(pos, value, undo, redo); 0510 if (res) { 0511 PUSH_UNDO(undo, redo, i18n("Update keyframe")); 0512 } 0513 return res; 0514 } 0515 0516 KeyframeType convertFromMltType(mlt_keyframe_type type) 0517 { 0518 switch (type) { 0519 case mlt_keyframe_linear: 0520 return KeyframeType::Linear; 0521 case mlt_keyframe_discrete: 0522 return KeyframeType::Discrete; 0523 #ifdef USE_MLT_NEW_KEYFRAMES 0524 case mlt_keyframe_smooth_natural: 0525 return KeyframeType::CurveSmooth; 0526 case mlt_keyframe_bounce_in: 0527 return KeyframeType::BounceIn; 0528 case mlt_keyframe_bounce_out: 0529 return KeyframeType::BounceOut; 0530 case mlt_keyframe_cubic_in: 0531 return KeyframeType::CubicIn; 0532 case mlt_keyframe_cubic_out: 0533 return KeyframeType::CubicOut; 0534 case mlt_keyframe_exponential_in: 0535 return KeyframeType::ExponentialIn; 0536 case mlt_keyframe_exponential_out: 0537 return KeyframeType::ExponentialOut; 0538 case mlt_keyframe_circular_in: 0539 return KeyframeType::CircularIn; 0540 case mlt_keyframe_circular_out: 0541 return KeyframeType::CircularOut; 0542 case mlt_keyframe_elastic_in: 0543 return KeyframeType::ElasticIn; 0544 case mlt_keyframe_elastic_out: 0545 return KeyframeType::ElasticOut; 0546 #endif 0547 case mlt_keyframe_smooth: 0548 return KeyframeType::Curve; 0549 default: 0550 return KeyframeType::Linear; 0551 } 0552 } 0553 0554 bool KeyframeModel::updateKeyframeType(GenTime pos, int type, Fun &undo, Fun &redo) 0555 { 0556 QWriteLocker locker(&m_lock); 0557 Q_ASSERT(m_keyframeList.count(pos) > 0); 0558 KeyframeType oldType = m_keyframeList[pos].first; 0559 KeyframeType newType = convertFromMltType(mlt_keyframe_type(type)); 0560 QVariant value = m_keyframeList[pos].second; 0561 // Check if keyframe is different 0562 if (m_paramType == ParamType::KeyframeParam || m_paramType == ParamType::ColorWheel) { 0563 if (oldType == newType) return true; 0564 } 0565 auto operation = updateKeyframe_lambda(pos, newType, value, true); 0566 auto reverse = updateKeyframe_lambda(pos, oldType, value, true); 0567 bool res = operation(); 0568 if (res) { 0569 UPDATE_UNDO_REDO(operation, reverse, undo, redo); 0570 } 0571 return res; 0572 } 0573 0574 Fun KeyframeModel::updateKeyframe_lambda(GenTime pos, KeyframeType type, const QVariant &value, bool notify) 0575 { 0576 QWriteLocker locker(&m_lock); 0577 return [this, pos, type, value, notify]() { 0578 // qDebug() << "update lambda" << pos.frames(pCore->getCurrentFps()) << value << notify; 0579 Q_ASSERT(m_keyframeList.count(pos) > 0); 0580 int row = static_cast<int>(std::distance(m_keyframeList.begin(), m_keyframeList.find(pos))); 0581 m_keyframeList[pos].first = type; 0582 m_keyframeList[pos].second = value; 0583 if (notify) Q_EMIT dataChanged(index(row), index(row), {ValueRole, NormalizedValueRole, TypeRole}); 0584 return true; 0585 }; 0586 } 0587 0588 Fun KeyframeModel::addKeyframe_lambda(GenTime pos, KeyframeType type, const QVariant &value, bool notify) 0589 { 0590 QWriteLocker locker(&m_lock); 0591 return [this, notify, pos, type, value]() { 0592 qDebug() << "add lambda" << pos.frames(pCore->getCurrentFps()) << value << notify; 0593 Q_ASSERT(m_keyframeList.count(pos) == 0); 0594 // We determine the row of the newly added marker 0595 auto insertionIt = m_keyframeList.lower_bound(pos); 0596 int insertionRow = static_cast<int>(m_keyframeList.size()); 0597 if (insertionIt != m_keyframeList.end()) { 0598 insertionRow = static_cast<int>(std::distance(m_keyframeList.begin(), insertionIt)); 0599 } 0600 if (notify) beginInsertRows(QModelIndex(), insertionRow, insertionRow); 0601 m_keyframeList[pos].first = type; 0602 m_keyframeList[pos].second = value; 0603 if (notify) endInsertRows(); 0604 return true; 0605 }; 0606 } 0607 0608 Fun KeyframeModel::deleteKeyframe_lambda(GenTime pos, bool notify) 0609 { 0610 QWriteLocker locker(&m_lock); 0611 return [this, pos, notify]() { 0612 qDebug() << "delete lambda" << pos.frames(pCore->getCurrentFps()) << notify; 0613 qDebug() << "before" << getAnimProperty(); 0614 Q_ASSERT(m_keyframeList.count(pos) > 0); 0615 // Q_ASSERT(pos != GenTime()); // cannot delete initial point 0616 int row = static_cast<int>(std::distance(m_keyframeList.begin(), m_keyframeList.find(pos))); 0617 if (notify) beginRemoveRows(QModelIndex(), row, row); 0618 m_keyframeList.erase(pos); 0619 if (notify) endRemoveRows(); 0620 qDebug() << "after" << getAnimProperty(); 0621 return true; 0622 }; 0623 } 0624 0625 QHash<int, QByteArray> KeyframeModel::roleNames() const 0626 { 0627 QHash<int, QByteArray> roles; 0628 roles[PosRole] = "position"; 0629 roles[FrameRole] = "frame"; 0630 roles[TypeRole] = "type"; 0631 roles[ValueRole] = "value"; 0632 roles[SelectedRole] = "selected"; 0633 roles[ActiveRole] = "active"; 0634 roles[NormalizedValueRole] = "normalizedValue"; 0635 roles[MoveOnlyRole] = "moveOnly"; 0636 return roles; 0637 } 0638 0639 QVariant KeyframeModel::data(const QModelIndex &index, int role) const 0640 { 0641 READ_LOCK(); 0642 if (index.row() < 0 || index.row() >= static_cast<int>(m_keyframeList.size()) || !index.isValid()) { 0643 return QVariant(); 0644 } 0645 auto it = m_keyframeList.begin(); 0646 std::advance(it, index.row()); 0647 switch (role) { 0648 case Qt::DisplayRole: 0649 case Qt::EditRole: 0650 case ValueRole: 0651 if (m_paramType == ParamType::Roto_spline) { 0652 return 0; 0653 } 0654 return it->second.second; 0655 case MoveOnlyRole: { 0656 if (m_paramType == ParamType::Roto_spline) { 0657 return true; 0658 } 0659 return false; 0660 } 0661 case NormalizedValueRole: { 0662 if (m_paramType == ParamType::AnimatedRect) { 0663 const QString &data = it->second.second.toString(); 0664 bool ok; 0665 double converted = data.section(QLatin1Char(' '), -1).toDouble(&ok); 0666 if (!ok) { 0667 qDebug() << "QLocale: Could not convert animated rect opacity" << data; 0668 } 0669 return converted; 0670 } 0671 if (m_paramType == ParamType::Roto_spline) { 0672 return 0.5; 0673 } 0674 double val = it->second.second.toDouble(); 0675 if (auto ptr = m_model.lock()) { 0676 Q_ASSERT(m_index.isValid()); 0677 double min = ptr->data(m_index, AssetParameterModel::VisualMinRole).toDouble(); 0678 double max = ptr->data(m_index, AssetParameterModel::VisualMaxRole).toDouble(); 0679 if (qFuzzyIsNull(min) && qFuzzyIsNull(max)) { 0680 min = ptr->data(m_index, AssetParameterModel::MinRole).toDouble(); 0681 max = ptr->data(m_index, AssetParameterModel::MaxRole).toDouble(); 0682 } 0683 double factor = ptr->data(m_index, AssetParameterModel::FactorRole).toDouble(); 0684 double norm = ptr->data(m_index, AssetParameterModel::DefaultRole).toDouble(); 0685 int logRole = ptr->data(m_index, AssetParameterModel::ScaleRole).toInt(); 0686 double linear = val * factor; 0687 if (logRole == -1) { 0688 // Logarythmic scale 0689 // transform current value to 0..1 scale 0690 if (linear >= norm) { 0691 double scaled = (linear - norm) / (max * factor - norm); 0692 return 0.5 + pow(scaled, 0.6) * 0.5; 0693 } 0694 double scaled = (linear - norm) / (min * factor - norm); 0695 // Log scale 0696 return 0.5 - pow(scaled, 0.6) * 0.5; 0697 } 0698 return (linear - min) / (max - min); 0699 } else { 0700 qDebug() << "// CANNOT LOCK effect MODEL"; 0701 } 0702 return 1; 0703 } 0704 case PosRole: 0705 return it->first.seconds(); 0706 case FrameRole: 0707 case Qt::UserRole: 0708 return it->first.frames(pCore->getCurrentFps()); 0709 case TypeRole: 0710 return QVariant::fromValue<KeyframeType>(it->second.first); 0711 case SelectedRole: 0712 if (auto ptr = m_model.lock()) { 0713 return ptr->m_selectedKeyframes.contains(index.row()); 0714 } 0715 break; 0716 case ActiveRole: 0717 if (auto ptr = m_model.lock()) { 0718 return ptr->m_activeKeyframe == index.row(); 0719 } 0720 break; 0721 } 0722 return QVariant(); 0723 } 0724 0725 int KeyframeModel::rowCount(const QModelIndex &parent) const 0726 { 0727 READ_LOCK(); 0728 if (parent.isValid()) return 0; 0729 return static_cast<int>(m_keyframeList.size()); 0730 } 0731 0732 bool KeyframeModel::singleKeyframe() const 0733 { 0734 READ_LOCK(); 0735 return m_keyframeList.size() <= 1; 0736 } 0737 0738 Keyframe KeyframeModel::getKeyframe(const GenTime &pos, bool *ok) const 0739 { 0740 READ_LOCK(); 0741 if (m_keyframeList.count(pos) == 0) { 0742 // return empty marker 0743 *ok = false; 0744 return {GenTime(), KeyframeType::Linear}; 0745 } 0746 *ok = true; 0747 return {pos, m_keyframeList.at(pos).first}; 0748 } 0749 0750 Keyframe KeyframeModel::getNextKeyframe(const GenTime &pos, bool *ok) const 0751 { 0752 auto it = m_keyframeList.upper_bound(pos); 0753 if (it == m_keyframeList.end()) { 0754 // return empty marker 0755 *ok = false; 0756 return {GenTime(), KeyframeType::Linear}; 0757 } 0758 *ok = true; 0759 return {(*it).first, (*it).second.first}; 0760 } 0761 0762 Keyframe KeyframeModel::getPrevKeyframe(const GenTime &pos, bool *ok) const 0763 { 0764 auto it = m_keyframeList.lower_bound(pos); 0765 if (it == m_keyframeList.begin()) { 0766 // return empty marker 0767 *ok = false; 0768 return {GenTime(), KeyframeType::Linear}; 0769 } 0770 --it; 0771 *ok = true; 0772 return {(*it).first, (*it).second.first}; 0773 } 0774 0775 Keyframe KeyframeModel::getClosestKeyframe(const GenTime &pos, bool *ok) const 0776 { 0777 if (m_keyframeList.count(pos) > 0) { 0778 return getKeyframe(pos, ok); 0779 } 0780 bool ok1, ok2; 0781 auto next = getNextKeyframe(pos, &ok1); 0782 auto prev = getPrevKeyframe(pos, &ok2); 0783 *ok = ok1 || ok2; 0784 if (ok1 && ok2) { 0785 double fps = pCore->getCurrentFps(); 0786 if (qAbs(next.first.frames(fps) - pos.frames(fps)) < qAbs(prev.first.frames(fps) - pos.frames(fps))) { 0787 return next; 0788 } 0789 return prev; 0790 } else if (ok1) { 0791 return next; 0792 } else if (ok2) { 0793 return prev; 0794 } 0795 // return empty marker 0796 return {GenTime(), KeyframeType::Linear}; 0797 } 0798 0799 bool KeyframeModel::hasKeyframe(int frame) const 0800 { 0801 return hasKeyframe(GenTime(frame, pCore->getCurrentFps())); 0802 } 0803 bool KeyframeModel::hasKeyframe(const GenTime &pos) const 0804 { 0805 READ_LOCK(); 0806 return m_keyframeList.count(pos) > 0; 0807 } 0808 0809 bool KeyframeModel::removeAllKeyframes(Fun &undo, Fun &redo) 0810 { 0811 QWriteLocker locker(&m_lock); 0812 Fun local_undo = []() { return true; }; 0813 Fun local_redo = []() { return true; }; 0814 int kfrCount = int(m_keyframeList.size()) - 1; 0815 // Clear selection 0816 if (auto ptr = m_model.lock()) { 0817 ptr->m_selectedKeyframes = {}; 0818 } 0819 if (kfrCount <= 0) { 0820 // Nothing to do 0821 UPDATE_UNDO_REDO(local_redo, local_undo, undo, redo); 0822 return true; 0823 } 0824 // we trigger only one global remove/insertrow event 0825 Fun update_redo_start = [this, kfrCount]() { 0826 beginRemoveRows(QModelIndex(), 1, kfrCount); 0827 return true; 0828 }; 0829 Fun update_redo_end = [this]() { 0830 endRemoveRows(); 0831 return true; 0832 }; 0833 Fun update_undo_start = [this, kfrCount]() { 0834 beginInsertRows(QModelIndex(), 1, kfrCount); 0835 return true; 0836 }; 0837 Fun update_undo_end = [this]() { 0838 endInsertRows(); 0839 return true; 0840 }; 0841 PUSH_LAMBDA(update_redo_start, local_redo); 0842 PUSH_LAMBDA(update_undo_start, local_undo); 0843 QList<GenTime> all_pos = getKeyframePos(); 0844 update_redo_start(); 0845 bool res = true; 0846 bool first = true; 0847 for (const auto &p : qAsConst(all_pos)) { 0848 if (first) { // skip first point 0849 first = false; 0850 continue; 0851 } 0852 res = removeKeyframe(p, local_undo, local_redo, false); 0853 if (!res) { 0854 bool undone = local_undo(); 0855 Q_ASSERT(undone); 0856 return false; 0857 } 0858 } 0859 update_redo_end(); 0860 PUSH_LAMBDA(update_redo_end, local_redo); 0861 PUSH_LAMBDA(update_undo_end, local_undo); 0862 UPDATE_UNDO_REDO(local_redo, local_undo, undo, redo); 0863 return true; 0864 } 0865 0866 bool KeyframeModel::removeAllKeyframes() 0867 { 0868 QWriteLocker locker(&m_lock); 0869 Fun undo = []() { return true; }; 0870 Fun redo = []() { return true; }; 0871 bool res = removeAllKeyframes(undo, redo); 0872 if (res) { 0873 PUSH_UNDO(undo, redo, i18n("Delete all keyframes")); 0874 } 0875 return res; 0876 } 0877 0878 mlt_keyframe_type convertToMltType(KeyframeType type) 0879 { 0880 return static_cast<mlt_keyframe_type>(static_cast<int>(type)); 0881 } 0882 0883 QString KeyframeModel::getAnimProperty() const 0884 { 0885 if (m_paramType == ParamType::Roto_spline) { 0886 return getRotoProperty(); 0887 } 0888 Mlt::Properties mlt_prop; 0889 if (auto ptr = m_model.lock()) { 0890 ptr->passProperties(mlt_prop); 0891 } 0892 int ix = 0; 0893 bool first = true; 0894 std::shared_ptr<Mlt::Animation> anim(nullptr); 0895 for (const auto &keyframe : m_keyframeList) { 0896 switch (m_paramType) { 0897 case ParamType::AnimatedRect: 0898 case ParamType::Color: 0899 mlt_prop.anim_set("key", keyframe.second.second.toString().toUtf8().constData(), keyframe.first.frames(pCore->getCurrentFps())); 0900 break; 0901 default: 0902 mlt_prop.anim_set("key", keyframe.second.second.toDouble(), keyframe.first.frames(pCore->getCurrentFps())); 0903 break; 0904 } 0905 if (first) { 0906 anim.reset(mlt_prop.get_anim("key")); 0907 first = false; 0908 } 0909 anim->key_set_type(ix, convertToMltType(keyframe.second.first)); 0910 ix++; 0911 } 0912 QString ret; 0913 if (anim) { 0914 char *cut = anim->serialize_cut(); 0915 ret = QString(cut); 0916 free(cut); 0917 } 0918 return ret; 0919 } 0920 0921 QString KeyframeModel::getRotoProperty() const 0922 { 0923 QJsonDocument doc; 0924 if (auto ptr = m_model.lock()) { 0925 int in = ptr->data(m_index, AssetParameterModel::ParentInRole).toInt(); 0926 int out = in + ptr->data(m_index, AssetParameterModel::ParentDurationRole).toInt(); 0927 QVariantMap map; 0928 for (const auto &keyframe : m_keyframeList) { 0929 map.insert(QString::number(keyframe.first.frames(pCore->getCurrentFps())).rightJustified(int(log10(double(out))) + 1, '0'), keyframe.second.second); 0930 } 0931 doc = QJsonDocument::fromVariant(map); 0932 } 0933 return doc.toJson(); 0934 } 0935 0936 void KeyframeModel::parseAnimProperty(const QString &prop, int in, int out) 0937 { 0938 Fun undo = []() { return true; }; 0939 Fun redo = []() { return true; }; 0940 disconnect(this, &KeyframeModel::modelChanged, this, &KeyframeModel::sendModification); 0941 removeAllKeyframes(undo, redo); 0942 bool useOpacity = true; 0943 Mlt::Properties mlt_prop; 0944 if (auto ptr = m_model.lock()) { 0945 if (out <= in) { 0946 in = ptr->data(m_index, AssetParameterModel::ParentInRole).toInt(); 0947 out = ptr->data(m_index, AssetParameterModel::ParentDurationRole).toInt(); 0948 } 0949 ptr->passProperties(mlt_prop); 0950 useOpacity = ptr->data(m_index, AssetParameterModel::OpacityRole).toBool(); 0951 } else { 0952 qDebug() << "###################\n\n/// ERROR LOCKING MODEL!!! "; 0953 } 0954 mlt_prop.set("key", prop.toUtf8().constData()); 0955 // This is a fake query to force the animation to be parsed 0956 (void)mlt_prop.anim_get_double("key", 0, out); 0957 0958 Mlt::Animation anim = mlt_prop.get_animation("key"); 0959 0960 qDebug() << "Found" << anim.key_count() << ", OUT: " << out << ", animation properties: " << prop; 0961 bool useDefaultType = !prop.contains(QLatin1Char('=')); 0962 for (int i = 0; i < anim.key_count(); ++i) { 0963 int frame; 0964 mlt_keyframe_type type; 0965 anim.key_get(i, frame, type); 0966 if (useDefaultType) { 0967 // TODO: use a default user defined type 0968 type = mlt_keyframe_linear; 0969 } 0970 QVariant value; 0971 switch (m_paramType) { 0972 case ParamType::AnimatedRect: { 0973 mlt_rect rect = mlt_prop.anim_get_rect("key", frame); 0974 if (useOpacity) { 0975 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')); 0976 } else { 0977 value = QVariant(QStringLiteral("%1 %2 %3 %4").arg(rect.x).arg(rect.y).arg(rect.w).arg(rect.h)); 0978 } 0979 break; 0980 } 0981 case ParamType::Color: { 0982 mlt_color mltColor = mlt_prop.anim_get_color("key", frame); 0983 QColor color(mltColor.r, mltColor.g, mltColor.b, mltColor.a); 0984 value = QVariant(QColorUtils::colorToString(color, true)); 0985 break; 0986 } 0987 0988 default: 0989 value = QVariant(mlt_prop.anim_get_double("key", frame)); 0990 break; 0991 } 0992 if (i == 0 && frame > in) { 0993 // Always add a keyframe at start pos 0994 addKeyframe(GenTime(in, pCore->getCurrentFps()), convertFromMltType(type), value, true, undo, redo); 0995 } else if (frame == in && hasKeyframe(GenTime(in))) { 0996 // First keyframe already exists, adjust its value 0997 updateKeyframe(GenTime(frame, pCore->getCurrentFps()), value, undo, redo, true); 0998 continue; 0999 } 1000 addKeyframe(GenTime(frame, pCore->getCurrentFps()), convertFromMltType(type), value, true, undo, redo); 1001 } 1002 connect(this, &KeyframeModel::modelChanged, this, &KeyframeModel::sendModification); 1003 } 1004 1005 void KeyframeModel::resetAnimProperty(const QString &prop) 1006 { 1007 Fun undo = []() { return true; }; 1008 Fun redo = []() { return true; }; 1009 1010 // Delete all existing keyframes 1011 QSignalBlocker bk(this); 1012 removeAllKeyframes(undo, redo); 1013 1014 Mlt::Properties mlt_prop; 1015 int in = 0; 1016 bool useOpacity = true; 1017 if (auto ptr = m_model.lock()) { 1018 in = ptr->data(m_index, AssetParameterModel::ParentInRole).toInt(); 1019 ptr->passProperties(mlt_prop); 1020 if (m_paramType == ParamType::AnimatedRect) { 1021 useOpacity = ptr->data(m_index, AssetParameterModel::OpacityRole).toBool(); 1022 } 1023 } 1024 mlt_prop.set("key", prop.toUtf8().constData()); 1025 // This is a fake query to force the animation to be parsed 1026 (void)mlt_prop.anim_get_int("key", 0, 0); 1027 1028 Mlt::Animation anim = mlt_prop.get_animation("key"); 1029 1030 qDebug() << "Found" << anim.key_count() << "animation properties"; 1031 for (int i = 0; i < anim.key_count(); ++i) { 1032 int frame; 1033 mlt_keyframe_type type; 1034 anim.key_get(i, frame, type); 1035 if (!prop.contains(QLatin1Char('='))) { 1036 // TODO: use a default user defined type 1037 type = mlt_keyframe_linear; 1038 } 1039 QVariant value; 1040 switch (m_paramType) { 1041 case ParamType::AnimatedRect: { 1042 mlt_rect rect = mlt_prop.anim_get_rect("key", frame); 1043 if (useOpacity) { 1044 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'))); 1045 } else { 1046 value = QVariant(QStringLiteral("%1 %2 %3 %4").arg(rect.x).arg(rect.y).arg(rect.w).arg(rect.h)); 1047 } 1048 break; 1049 } 1050 default: 1051 value = QVariant(mlt_prop.anim_get_double("key", frame)); 1052 break; 1053 } 1054 if (i == 0 && frame > in) { 1055 // Always add a keyframe at start pos 1056 addKeyframe(GenTime(in, pCore->getCurrentFps()), convertFromMltType(type), value, false, undo, redo); 1057 } else if (frame == in && hasKeyframe(GenTime(in))) { 1058 // First keyframe already exists, adjust its value 1059 updateKeyframe(GenTime(frame, pCore->getCurrentFps()), value, undo, redo, false); 1060 continue; 1061 } 1062 addKeyframe(GenTime(frame, pCore->getCurrentFps()), convertFromMltType(type), value, false, undo, redo); 1063 } 1064 QString effectName; 1065 if (auto ptr = m_model.lock()) { 1066 effectName = ptr->data(m_index, Qt::DisplayRole).toString(); 1067 } else { 1068 effectName = i18n("effect"); 1069 } 1070 Fun update_local = [this]() { 1071 Q_EMIT dataChanged(index(0), index(int(m_keyframeList.size())), {}); 1072 return true; 1073 }; 1074 update_local(); 1075 PUSH_LAMBDA(update_local, undo); 1076 PUSH_LAMBDA(update_local, redo); 1077 PUSH_UNDO(undo, redo, i18n("Reset %1", effectName)); 1078 } 1079 1080 void KeyframeModel::parseRotoProperty(const QString &prop) 1081 { 1082 Fun undo = []() { return true; }; 1083 Fun redo = []() { return true; }; 1084 1085 QJsonParseError jsonError; 1086 QJsonDocument doc = QJsonDocument::fromJson(prop.toUtf8(), &jsonError); 1087 QVariant data = doc.toVariant(); 1088 if (data.canConvert<QVariantMap>()) { 1089 QMap<QString, QVariant> map = data.toMap(); 1090 QMap<QString, QVariant>::const_iterator i = map.constBegin(); 1091 while (i != map.constEnd()) { 1092 addKeyframe(GenTime(i.key().toInt(), pCore->getCurrentFps()), KeyframeType::Linear, i.value(), false, undo, redo); 1093 ++i; 1094 } 1095 } 1096 } 1097 1098 QVariant KeyframeModel::getInterpolatedValue(int p) const 1099 { 1100 auto pos = GenTime(p, pCore->getCurrentFps()); 1101 return getInterpolatedValue(pos); 1102 } 1103 1104 QVariant KeyframeModel::updateInterpolated(const QVariant &interpValue, double val) 1105 { 1106 QStringList vals = interpValue.toString().split(QLatin1Char(' ')); 1107 if (!vals.isEmpty()) { 1108 vals[vals.size() - 1] = QString::number(val, 'f'); 1109 } 1110 return vals.join(QLatin1Char(' ')); 1111 } 1112 1113 QVariant KeyframeModel::getNormalizedValue(double newVal) const 1114 { 1115 if (auto ptr = m_model.lock()) { 1116 double min = ptr->data(m_index, AssetParameterModel::VisualMinRole).toDouble(); 1117 double max = ptr->data(m_index, AssetParameterModel::VisualMaxRole).toDouble(); 1118 if (qFuzzyIsNull(min) && qFuzzyIsNull(max)) { 1119 min = ptr->data(m_index, AssetParameterModel::MinRole).toDouble(); 1120 max = ptr->data(m_index, AssetParameterModel::MaxRole).toDouble(); 1121 } 1122 if (qFuzzyIsNull(min) && qFuzzyIsNull(max)) { 1123 min = 0.; 1124 max = 1.; 1125 } 1126 double factor = ptr->data(m_index, AssetParameterModel::FactorRole).toDouble(); 1127 double norm = ptr->data(m_index, AssetParameterModel::DefaultRole).toDouble(); 1128 int logRole = ptr->data(m_index, AssetParameterModel::ScaleRole).toInt(); 1129 double realValue; 1130 if (logRole == -1) { 1131 // Logarythmic scale 1132 if (newVal >= 0.5) { 1133 realValue = norm + pow(2 * (newVal - 0.5), 10.0 / 6) * (max / factor - norm); 1134 } else { 1135 realValue = norm - pow(2 * (0.5 - newVal), 10.0 / 6) * (norm - min / factor); 1136 } 1137 } else { 1138 realValue = (newVal * (max - min) + min) / factor; 1139 } 1140 return QVariant(realValue); 1141 } 1142 return QVariant(); 1143 } 1144 1145 QVariant KeyframeModel::getInterpolatedValue(const GenTime &pos) const 1146 { 1147 if (m_keyframeList.count(pos) > 0) { 1148 return m_keyframeList.at(pos).second; 1149 } 1150 if (m_keyframeList.size() == 0) { 1151 return QVariant(); 1152 } 1153 Mlt::Properties mlt_prop; 1154 QString animData; 1155 int out = 0; 1156 bool useOpacity = false; 1157 if (auto ptr = m_model.lock()) { 1158 ptr->passProperties(mlt_prop); 1159 out = ptr->data(m_index, AssetParameterModel::ParentDurationRole).toInt(); 1160 useOpacity = ptr->data(m_index, AssetParameterModel::OpacityRole).toBool(); 1161 animData = ptr->data(m_index, AssetParameterModel::ValueRole).toString(); 1162 } 1163 1164 if (!animData.isEmpty() && (m_paramType == ParamType::KeyframeParam || m_paramType == ParamType::ColorWheel)) { 1165 mlt_prop.set("key", animData.toUtf8().constData()); 1166 // This is a fake query to force the animation to be parsed 1167 (void)mlt_prop.anim_get_double("key", 0, out); 1168 return QVariant(mlt_prop.anim_get_double("key", pos.frames(pCore->getCurrentFps()))); 1169 } 1170 if (!animData.isEmpty() && m_paramType == ParamType::AnimatedRect) { 1171 mlt_prop.set("key", animData.toUtf8().constData()); 1172 // This is a fake query to force the animation to be parsed 1173 (void)mlt_prop.anim_get_double("key", 0, out); 1174 mlt_rect rect = mlt_prop.anim_get_rect("key", pos.frames(pCore->getCurrentFps())); 1175 QString res = QStringLiteral("%1 %2 %3 %4").arg(int(rect.x)).arg(int(rect.y)).arg(int(rect.w)).arg(int(rect.h)); 1176 if (useOpacity) { 1177 res.append(QStringLiteral(" %1").arg(QString::number(rect.o, 'f'))); 1178 } 1179 return QVariant(res); 1180 } 1181 if (!animData.isEmpty() && m_paramType == ParamType::Color) { 1182 mlt_prop.set("key", animData.toUtf8().constData()); 1183 // This is a fake query to force the animation to be parsed 1184 (void)mlt_prop.anim_get_double("key", 0, out); 1185 mlt_color mltColor = mlt_prop.anim_get_color("key", pos.frames(pCore->getCurrentFps())); 1186 QColor color(mltColor.r, mltColor.g, mltColor.b, mltColor.a); 1187 return QVariant(QColorUtils::colorToString(color, true)); 1188 } 1189 if (m_paramType == ParamType::Roto_spline) { 1190 // interpolate 1191 auto next = m_keyframeList.upper_bound(pos); 1192 if (next == m_keyframeList.cbegin()) { 1193 return (m_keyframeList.cbegin())->second.second; 1194 } else if (next == m_keyframeList.cend()) { 1195 auto it = m_keyframeList.cend(); 1196 --it; 1197 return it->second.second; 1198 } 1199 auto prev = next; 1200 --prev; 1201 1202 QSize frame = pCore->getCurrentFrameSize(); 1203 QList<BPoint> p1 = RotoHelper::getPoints(prev->second.second, frame); 1204 QList<BPoint> p2 = RotoHelper::getPoints(next->second.second, frame); 1205 // relPos should be in [0,1]: 1206 // - equal to 0 on prev keyframe 1207 // - equal to 1 on next keyframe 1208 qreal relPos = 0; 1209 if (next->first != prev->first) { 1210 relPos = (pos.frames(pCore->getCurrentFps()) - prev->first.frames(pCore->getCurrentFps())) / 1211 qreal(((next->first - prev->first).frames(pCore->getCurrentFps()))); 1212 } 1213 int count = qMin(p1.count(), p2.count()); 1214 QList<QVariant> vlist; 1215 for (int i = 0; i < count; ++i) { 1216 BPoint bp; 1217 QList<QVariant> pl; 1218 for (int j = 0; j < 3; ++j) { 1219 if (p1.at(i)[j] != p2.at(i)[j]) { 1220 bp[j] = QLineF(p1.at(i)[j], p2.at(i)[j]).pointAt(relPos); 1221 } else { 1222 bp[j] = p1.at(i)[j]; 1223 } 1224 pl << QVariant(QList<QVariant>() << QVariant(bp[j].x() / frame.width()) << QVariant(bp[j].y() / frame.height())); 1225 } 1226 vlist << QVariant(pl); 1227 } 1228 return vlist; 1229 } 1230 return QVariant(); 1231 } 1232 1233 void KeyframeModel::sendModification() 1234 { 1235 if (auto ptr = m_model.lock()) { 1236 Q_ASSERT(m_index.isValid()); 1237 QString name = ptr->data(m_index, AssetParameterModel::NameRole).toString(); 1238 if (AssetParameterModel::isAnimated(m_paramType)) { 1239 m_lastData = getAnimProperty(); 1240 ptr->setParameter(name, m_lastData, false, m_index); 1241 } else { 1242 Q_ASSERT(false); // Not implemented, TODO 1243 } 1244 } 1245 } 1246 1247 QString KeyframeModel::realValue(double normalizedValue) const 1248 { 1249 double value = getNormalizedValue(normalizedValue).toDouble(); 1250 if (auto ptr = m_model.lock()) { 1251 int decimals = ptr->data(m_index, AssetParameterModel::DecimalsRole).toInt(); 1252 value *= ptr->data(m_index, AssetParameterModel::FactorRole).toDouble(); 1253 QString result; 1254 if (decimals == 0) { 1255 if (m_paramType == ParamType::AnimatedRect) { 1256 value = qRound(value * 100.); 1257 } 1258 // Fix rounding erros in double > int conversion 1259 if (value > 0.) { 1260 value += 0.001; 1261 } else { 1262 value -= 0.001; 1263 } 1264 result = QString::number(int(value)); 1265 } else { 1266 result = QString::number(value, 'f', decimals); 1267 } 1268 result.append(ptr->data(m_index, AssetParameterModel::SuffixRole).toString()); 1269 return result; 1270 } 1271 return QString::number(value); 1272 } 1273 1274 void KeyframeModel::refresh(int in, int out) 1275 { 1276 Q_ASSERT(m_index.isValid()); 1277 QString animData; 1278 if (auto ptr = m_model.lock()) { 1279 animData = ptr->data(m_index, AssetParameterModel::ValueRole).toString(); 1280 } else { 1281 qDebug() << "WARNING : unable to access keyframe's model"; 1282 return; 1283 } 1284 if (animData == m_lastData) { 1285 // nothing to do 1286 // qDebug() << "// DATA WAS ALREADY PARSED, ABORTING REFRESH\n"; 1287 return; 1288 } 1289 if (m_paramType == ParamType::Roto_spline) { 1290 parseRotoProperty(animData); 1291 } else if (AssetParameterModel::isAnimated(m_paramType)) { 1292 parseAnimProperty(animData, in, out); 1293 } else { 1294 // first, try to convert to double 1295 bool ok = false; 1296 double value = animData.toDouble(&ok); 1297 if (ok) { 1298 Fun undo = []() { return true; }; 1299 Fun redo = []() { return true; }; 1300 addKeyframe(GenTime(), KeyframeType::Linear, QVariant(value), false, undo, redo); 1301 } else { 1302 Q_ASSERT(false); // Not implemented, TODO 1303 } 1304 } 1305 m_lastData = animData; 1306 } 1307 1308 void KeyframeModel::reset() 1309 { 1310 Q_ASSERT(m_index.isValid()); 1311 QString animData; 1312 if (auto ptr = m_model.lock()) { 1313 animData = ptr->data(m_index, AssetParameterModel::ValueRole).toString(); 1314 } else { 1315 qDebug() << "WARNING : unable to access keyframe's model"; 1316 return; 1317 } 1318 if (animData == m_lastData) { 1319 // nothing to do 1320 qDebug() << "// DATA WAS ALREADY PARSED, ABORTING\n_________________"; 1321 return; 1322 } 1323 if (m_paramType == ParamType::Roto_spline) { 1324 // TODO: resetRotoProperty(animData); 1325 } else if (AssetParameterModel::isAnimated(m_paramType)) { 1326 qDebug() << "parsing keyframe" << animData; 1327 resetAnimProperty(animData); 1328 } else { 1329 // first, try to convert to double 1330 bool ok = false; 1331 double value = animData.toDouble(&ok); 1332 if (ok) { 1333 Fun undo = []() { return true; }; 1334 Fun redo = []() { return true; }; 1335 addKeyframe(GenTime(), KeyframeType::Linear, QVariant(value), false, undo, redo); 1336 PUSH_UNDO(undo, redo, i18n("Reset effect")); 1337 qDebug() << "KEYFRAME ADDED" << value; 1338 } else { 1339 Q_ASSERT(false); // Not implemented, TODO 1340 } 1341 } 1342 m_lastData = animData; 1343 } 1344 1345 QList<QPoint> KeyframeModel::getRanges(const QString &animData, const std::shared_ptr<AssetParameterModel> &model) 1346 { 1347 Mlt::Properties mlt_prop; 1348 model->passProperties(mlt_prop); 1349 mlt_prop.set("key", animData.toUtf8().constData()); 1350 // This is a fake query to force the animation to be parsed 1351 (void)mlt_prop.anim_get_int("key", 0, 0); 1352 1353 Mlt::Animation anim = mlt_prop.get_animation("key"); 1354 int frame; 1355 mlt_keyframe_type type; 1356 anim.key_get(0, frame, type); 1357 mlt_rect rect = mlt_prop.anim_get_rect("key", frame); 1358 QPoint pX(int(rect.x), int(rect.x)); 1359 QPoint pY(int(rect.y), int(rect.y)); 1360 QPoint pW(int(rect.w), int(rect.w)); 1361 QPoint pH(int(rect.h), int(rect.h)); 1362 QPoint pO(int(rect.o), int(rect.o)); 1363 for (int i = 1; i < anim.key_count(); ++i) { 1364 anim.key_get(i, frame, type); 1365 if (!animData.contains(QLatin1Char('='))) { 1366 // TODO: use a default user defined type 1367 type = mlt_keyframe_linear; 1368 } 1369 rect = mlt_prop.anim_get_rect("key", frame); 1370 pX.setX(qMin(int(rect.x), pX.x())); 1371 pX.setY(qMax(int(rect.x), pX.y())); 1372 pY.setX(qMin(int(rect.y), pY.x())); 1373 pY.setY(qMax(int(rect.y), pY.y())); 1374 pW.setX(qMin(int(rect.w), pW.x())); 1375 pW.setY(qMax(int(rect.w), pW.y())); 1376 pH.setX(qMin(int(rect.h), pH.x())); 1377 pH.setY(qMax(int(rect.h), pH.y())); 1378 pO.setX(qMin(int(rect.o), pO.x())); 1379 pO.setY(qMax(int(rect.o), pO.y())); 1380 // 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))); 1381 } 1382 QList<QPoint> result{pX, pY, pW, pH, pO}; 1383 return result; 1384 } 1385 1386 std::shared_ptr<Mlt::Properties> KeyframeModel::getAnimation(std::shared_ptr<AssetParameterModel> model, const QString &animData, int duration) 1387 { 1388 std::shared_ptr<Mlt::Properties> mlt_prop(new Mlt::Properties()); 1389 model->passProperties(*mlt_prop.get()); 1390 mlt_prop->set("key", animData.toUtf8().constData()); 1391 // This is a fake query to force the animation to be parsed 1392 (void)mlt_prop->anim_get_rect("key", 0, duration); 1393 return mlt_prop; 1394 } 1395 1396 const QString KeyframeModel::getAnimationStringWithOffset(std::shared_ptr<AssetParameterModel> model, const QString &animData, int offset, int duration, 1397 ParamType paramType, bool useOpacity) 1398 { 1399 Mlt::Properties mlt_prop; 1400 model->passProperties(mlt_prop); 1401 mlt_prop.set("key", animData.toUtf8().constData()); 1402 // This is a fake query to force the animation to be parsed 1403 (void)mlt_prop.anim_get_rect("key", 0); 1404 Mlt::Animation anim = mlt_prop.get_animation("key"); 1405 if (offset > 0) { 1406 for (int i = anim.key_count() - 1; i >= 0; --i) { 1407 int pos = anim.key_get_frame(i) + offset; 1408 anim.key_set_frame(i, pos); 1409 } 1410 } else if (offset < 0) { 1411 for (int i = 0; i < anim.key_count(); ++i) { 1412 int pos = anim.key_get_frame(i) + offset; 1413 if (pos >= 0) { 1414 anim.key_set_frame(i, pos); 1415 } 1416 } 1417 } 1418 // If last key is beyond duration, add new keyframe at end 1419 int lastPos = anim.key_get_frame(anim.key_count() - 1); 1420 if (lastPos > duration) { 1421 QVariant value; 1422 switch (paramType) { 1423 case ParamType::AnimatedRect: { 1424 mlt_rect rect = mlt_prop.anim_get_rect("key", duration); 1425 QString res = QStringLiteral("%1 %2 %3 %4").arg(int(rect.x)).arg(int(rect.y)).arg(int(rect.w)).arg(int(rect.h)); 1426 if (useOpacity) { 1427 res.append(QStringLiteral(" %1").arg(QString::number(rect.o, 'f'))); 1428 } 1429 value = QVariant(res); 1430 break; 1431 } 1432 case ParamType::Color: { 1433 mlt_color mltColor = mlt_prop.anim_get_color("key", duration); 1434 QColor color(mltColor.r, mltColor.g, mltColor.b, mltColor.a); 1435 value = QVariant(QColorUtils::colorToString(color, true)); 1436 break; 1437 } 1438 default: 1439 value = QVariant(mlt_prop.anim_get_double("key", duration)); 1440 break; 1441 } 1442 mlt_prop.anim_set("key", value.toString().toUtf8().constData(), duration); 1443 // Ensure the added keyframe uses the same type as last one 1444 mlt_keyframe_type lastType = anim.key_get_type(anim.key_count() - 1); 1445 for (int i = 0; i < anim.key_count(); i++) { 1446 if (anim.key_get_frame(i) == duration) { 1447 anim.key_set_type(i, lastType); 1448 break; 1449 } 1450 } 1451 } 1452 return qstrdup(anim.serialize_cut(0, duration)); 1453 } 1454 1455 const QString KeyframeModel::getIconByKeyframeType(KeyframeType type){ 1456 switch (type) { 1457 case KeyframeType::Linear: 1458 return QStringLiteral("linear"); 1459 case KeyframeType::Discrete: 1460 return QStringLiteral("discrete"); 1461 case KeyframeType::Curve: 1462 return QStringLiteral("smooth"); 1463 #ifdef USE_MLT_NEW_KEYFRAMES 1464 case KeyframeType::CurveSmooth: 1465 return QStringLiteral("smooth"); 1466 #endif 1467 default: 1468 return QStringLiteral("favorite"); 1469 } 1470 } 1471 QList<GenTime> KeyframeModel::getKeyframePos() const 1472 { 1473 QList<GenTime> all_pos; 1474 for (const auto &m : m_keyframeList) { 1475 all_pos.push_back(m.first); 1476 } 1477 return all_pos; 1478 } 1479 1480 bool KeyframeModel::removeNextKeyframes(GenTime pos, Fun &undo, Fun &redo) 1481 { 1482 QWriteLocker locker(&m_lock); 1483 std::vector<GenTime> all_pos; 1484 Fun local_undo = []() { return true; }; 1485 Fun local_redo = []() { return true; }; 1486 for (const auto &m : m_keyframeList) { 1487 if (m.first >= pos && m.first != m_keyframeList.begin()->first) { 1488 all_pos.push_back(m.first); 1489 } 1490 } 1491 std::sort(all_pos.begin(), all_pos.end()); 1492 int kfrCount = int(m_keyframeList.size()); 1493 // Remove deleted keyframes from selection 1494 if (auto ptr = m_model.lock()) { 1495 QVector<int> selection; 1496 for (auto &ix : ptr->m_selectedKeyframes) { 1497 if (ix < kfrCount) { 1498 selection << ix; 1499 } 1500 } 1501 ptr->m_selectedKeyframes = selection; 1502 } 1503 // we trigger only one global remove/insertrow event 1504 int row = static_cast<int>(std::distance(m_keyframeList.begin(), m_keyframeList.find(all_pos.front()))); 1505 Fun update_redo_start = [this, row, kfrCount]() { 1506 beginRemoveRows(QModelIndex(), row, kfrCount - 1); 1507 return true; 1508 }; 1509 Fun update_redo_end = [this]() { 1510 endRemoveRows(); 1511 return true; 1512 }; 1513 Fun update_undo_start = [this, row, kfrCount]() { 1514 beginInsertRows(QModelIndex(), row, kfrCount - 1); 1515 return true; 1516 }; 1517 Fun update_undo_end = [this]() { 1518 endInsertRows(); 1519 return true; 1520 }; 1521 PUSH_LAMBDA(update_redo_start, local_redo); 1522 PUSH_LAMBDA(update_undo_start, local_undo); 1523 update_redo_start(); 1524 for (const auto &p : all_pos) { 1525 bool res = removeKeyframe(p, local_undo, local_redo, false); 1526 if (!res) { 1527 bool undone = local_undo(); 1528 Q_ASSERT(undone); 1529 return false; 1530 } 1531 } 1532 update_redo_end(); 1533 PUSH_LAMBDA(update_redo_end, local_redo); 1534 PUSH_LAMBDA(update_undo_end, local_undo); 1535 UPDATE_UNDO_REDO(local_redo, local_undo, undo, redo); 1536 return true; 1537 } 1538 1539 void KeyframeModel::setSelectedKeyframe(int ix, bool add) 1540 { 1541 QVector<int> previous; 1542 if (auto ptr = m_model.lock()) { 1543 if (add) { 1544 if (ptr->m_selectedKeyframes.contains(ix)) { 1545 // remove from selection 1546 ptr->m_selectedKeyframes.removeAll(ix); 1547 } else { 1548 ptr->m_selectedKeyframes << ix; 1549 } 1550 } else { 1551 previous = ptr->m_selectedKeyframes; 1552 ptr->m_selectedKeyframes = {ix}; 1553 } 1554 } 1555 if (!add) { 1556 for (auto &ix2 : previous) { 1557 if (ix2 > -1) { 1558 Q_EMIT requestModelUpdate(index(ix2), index(ix2), {SelectedRole}); 1559 } 1560 } 1561 } 1562 if (ix > -1) { 1563 Q_EMIT requestModelUpdate(index(ix), index(ix), {SelectedRole}); 1564 } 1565 } 1566 1567 void KeyframeModel::setSelectedKeyframes(QVector<int> selection) 1568 { 1569 QVector<int> previous; 1570 selection.removeAll(-1); 1571 std::sort(selection.begin(), selection.end()); 1572 if (auto ptr = m_model.lock()) { 1573 previous = ptr->m_selectedKeyframes; 1574 ptr->m_selectedKeyframes = selection; 1575 } 1576 if (!selection.isEmpty()) { 1577 Q_EMIT requestModelUpdate(index(selection.first()), index(selection.last()), {SelectedRole}); 1578 } 1579 for (auto &ix : previous) { 1580 if (ix > -1 && !selection.contains(ix)) { 1581 Q_EMIT requestModelUpdate(index(ix), index(ix), {SelectedRole}); 1582 } 1583 } 1584 } 1585 1586 int KeyframeModel::activeKeyframe() const 1587 { 1588 if (auto ptr = m_model.lock()) { 1589 return ptr->m_activeKeyframe; 1590 } 1591 return -1; 1592 } 1593 1594 void KeyframeModel::setActiveKeyframe(int ix) 1595 { 1596 int oldActive = -1; 1597 if (auto ptr = m_model.lock()) { 1598 oldActive = ptr->m_activeKeyframe; 1599 if (oldActive == ix) { 1600 // Keyframe already active 1601 return; 1602 } 1603 ptr->m_activeKeyframe = ix; 1604 } 1605 Q_EMIT requestModelUpdate(index(ix), index(ix), {ActiveRole}); 1606 if (oldActive > -1) { 1607 Q_EMIT requestModelUpdate(index(oldActive), index(oldActive), {ActiveRole}); 1608 } 1609 } 1610 1611 int KeyframeModel::getIndexForPos(const GenTime pos) const 1612 { 1613 if (m_keyframeList.count(pos) == 0) { 1614 return -1; 1615 } 1616 return static_cast<int>(std::distance(m_keyframeList.begin(), m_keyframeList.find(pos))); 1617 }