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