Members of the KDE Community are recommended to subscribe to the kde-community mailing list at https://mail.kde.org/mailman/listinfo/kde-community to allow them to participate in important discussions and receive other important announcements

Fix various keyframe crashes

parent c1bdc728
......@@ -761,6 +761,30 @@ QVariant KeyframeModel::getInterpolatedValue(int p) const
return getInterpolatedValue(pos);
}
QVariant KeyframeModel::getNormalizedValue(double newVal) const
{
if (auto ptr = m_model.lock()) {
double min = ptr->data(m_index, AssetParameterModel::MinRole).toDouble();
double max = ptr->data(m_index, AssetParameterModel::MaxRole).toDouble();
double factor = ptr->data(m_index, AssetParameterModel::FactorRole).toDouble();
double norm = ptr->data(m_index, AssetParameterModel::DefaultRole).toDouble();
int logRole = ptr->data(m_index, AssetParameterModel::ScaleRole).toInt();
double realValue;
if (logRole == -1) {
// Logarythmic scale for lower than norm values
if (newVal >= 0.5) {
realValue = norm + (2 * (newVal - 0.5) * (max / factor - norm));
} else {
realValue = norm - pow(2 * (0.5 - newVal), 10.0 / 6) * (norm - min / factor);
}
} else {
realValue = (newVal * (max - min) + min) / factor;
}
return QVariant(realValue);
}
return QVariant();
}
QVariant KeyframeModel::getInterpolatedValue(const GenTime &pos) const
{
if (m_keyframeList.count(pos) > 0) {
......
......@@ -62,23 +62,23 @@ public:
enum { TypeRole = Qt::UserRole + 1, PosRole, FrameRole, ValueRole, NormalizedValueRole };
friend class KeyframeModelList;
protected:
/** @brief These methods should ONLY be called by keyframemodellist to ensure synchronisation
* with keyframes from other parameters */
/* @brief Adds a keyframe at the given position. If there is already one then we update it.
@param pos defines the position of the keyframe, relative to the clip
@param type is the type of the keyframe.
*/
bool addKeyframe(GenTime pos, KeyframeType type, QVariant value);
Q_INVOKABLE bool addKeyframe(int frame, double normalizedValue);
protected:
bool addKeyframe(int frame, double normalizedValue);
/* @brief Same function but accumulates undo/redo
@param notify: if true, send a signal to model
*/
bool addKeyframe(GenTime pos, KeyframeType type, QVariant value, bool notify, Fun &undo, Fun &redo);
public:
/* @brief Removes the keyframe at the given position. */
Q_INVOKABLE bool removeKeyframe(int frame);
Q_INVOKABLE bool moveKeyframe(int oldPos, int pos, double newVal);
bool removeKeyframe(int frame);
bool moveKeyframe(int oldPos, int pos, double newVal);
bool removeKeyframe(GenTime pos);
/* @brief Delete all the keyframes of the model */
bool removeAllKeyframes();
......@@ -145,6 +145,8 @@ public:
/* @brief Return the interpolated value at given pos */
QVariant getInterpolatedValue(int pos) const;
QVariant getInterpolatedValue(const GenTime &pos) const;
/* @brief Return the real value from a normalized one */
QVariant getNormalizedValue(double newVal) const;
// Mandatory overloads
Q_INVOKABLE QVariant data(const QModelIndex &index, int role) const override;
......
......@@ -87,6 +87,25 @@ bool KeyframeModelList::addKeyframe(GenTime pos, KeyframeType type)
return applyOperation(op, update ? i18n("Change keyframe type") : i18n("Add keyframe"));
}
bool KeyframeModelList::addKeyframe(int frame, double val)
{
QWriteLocker locker(&m_lock);
GenTime pos(frame, pCore->getCurrentFps());
Q_ASSERT(m_parameters.size() > 0);
KeyframeType type = keyframeType(GenTime());
bool update = (m_parameters.begin()->second->hasKeyframe(pos) > 0);
auto op = [this, pos, frame, type, val](std::shared_ptr<KeyframeModel> param, Fun &undo, Fun &redo) {
QVariant value;
if (m_parameters.begin()->second == param) {
value = param->getNormalizedValue(val);
} else {
value = param->getInterpolatedValue(pos);
}
return param->addKeyframe(pos, type, value, true, undo, redo);
};
return applyOperation(op, update ? i18n("Change keyframe type") : i18n("Add keyframe"));
}
bool KeyframeModelList::removeKeyframe(GenTime pos)
{
QWriteLocker locker(&m_lock);
......@@ -111,6 +130,22 @@ bool KeyframeModelList::moveKeyframe(GenTime oldPos, GenTime pos, bool logUndo)
return applyOperation(op, logUndo ? i18n("Move keyframe") : QString());
}
bool KeyframeModelList::updateKeyframe(GenTime oldPos, GenTime pos, double normalizedVal, bool logUndo)
{
QWriteLocker locker(&m_lock);
Q_ASSERT(m_parameters.size() > 0);
auto op = [this, oldPos, pos, normalizedVal](std::shared_ptr<KeyframeModel> param, Fun &undo, Fun &redo) {
double value;
if (m_parameters.begin()->second == param) {
value = normalizedVal;
} else {
value = -1;
}
return param->moveKeyframe(oldPos, pos, value, undo, redo);
};
return applyOperation(op, logUndo ? i18n("Move keyframe") : QString());
}
bool KeyframeModelList::updateKeyframe(GenTime pos, QVariant value, const QPersistentModelIndex &index)
{
QWriteLocker locker(&m_lock);
......
......@@ -61,6 +61,7 @@ public:
@param type is the type of the keyframe.
*/
bool addKeyframe(GenTime pos, KeyframeType type);
bool addKeyframe(int frame, double val);
/* @brief Removes the keyframe at the given position. */
bool removeKeyframe(GenTime pos);
......@@ -81,6 +82,7 @@ public:
*/
bool updateKeyframe(GenTime pos, QVariant value, const QPersistentModelIndex &index);
bool updateKeyframeType(GenTime pos, int type, const QPersistentModelIndex &index);
bool updateKeyframe(GenTime oldPos, GenTime pos, double normalizedVal, bool logUndo = true);
KeyframeType keyframeType(GenTime pos) const;
/* @brief Returns a keyframe data at given pos
ok is a return parameter, set to true if everything went good
......
......@@ -940,3 +940,54 @@ bool EffectStackModel::isStackEnabled() const
{
return m_effectStackEnabled;
}
bool EffectStackModel::addEffectKeyFrame(int frame, double normalisedVal)
{
if (rootItem->childCount() == 0) return false;
int ix = 0;
auto ptr = m_services.front().lock();
if (ptr) {
ix = ptr->get_int("kdenlive:activeeffect");
}
if (ix < 0) {
return false;
}
std::shared_ptr<EffectItemModel> sourceEffect = std::static_pointer_cast<EffectItemModel>(rootItem->child(ix));
std::shared_ptr<KeyframeModelList> listModel = sourceEffect->getKeyframeModel();
return listModel->addKeyframe(frame, normalisedVal);
}
bool EffectStackModel::removeKeyFrame(int frame)
{
if (rootItem->childCount() == 0) return false;
int ix = 0;
auto ptr = m_services.front().lock();
if (ptr) {
ix = ptr->get_int("kdenlive:activeeffect");
}
if (ix < 0) {
return false;
}
std::shared_ptr<EffectItemModel> sourceEffect = std::static_pointer_cast<EffectItemModel>(rootItem->child(ix));
std::shared_ptr<KeyframeModelList> listModel = sourceEffect->getKeyframeModel();
return listModel->removeKeyframe(GenTime(frame, pCore->getCurrentFps()));
}
bool EffectStackModel::updateKeyFrame(int oldFrame, int newFrame, double normalisedVal)
{
if (rootItem->childCount() == 0) return false;
int ix = 0;
auto ptr = m_services.front().lock();
if (ptr) {
ix = ptr->get_int("kdenlive:activeeffect");
}
if (ix < 0) {
return false;
}
std::shared_ptr<EffectItemModel> sourceEffect = std::static_pointer_cast<EffectItemModel>(rootItem->child(ix));
std::shared_ptr<KeyframeModelList> listModel = sourceEffect->getKeyframeModel();
return listModel->updateKeyframe(GenTime(oldFrame, pCore->getCurrentFps()), GenTime(newFrame, pCore->getCurrentFps()), normalisedVal);
}
......@@ -108,6 +108,12 @@ public:
Q_INVOKABLE double getFilterParam(const QString &effectId, const QString &paramName);
/** get the active effect's keyframe model */
Q_INVOKABLE KeyframeModel *getEffectKeyframeModel();
/** Add a keyframe in all model parameters */
bool addEffectKeyFrame(int frame, double normalisedVal);
/** Remove a keyframe in all model parameters */
bool removeKeyFrame(int frame);
/** Update a keyframe in all model parameters (with value updated only in first parameter)*/
bool updateKeyFrame(int oldFrame, int newFrame, double normalisedVal);
/** Remove unwanted fade effects, mostly after a cut operation */
void cleanFadeEffects(bool outEffects, Fun &undo, Fun &redo);
......
......@@ -156,10 +156,10 @@ Rectangle
var newPos = Math.round((keyframe.x + parent.x + root.baseUnit / 2) / timeScale) + inPoint
var newVal = (keyframeContainer.height - (parent.y + mouse.y)) / keyframeContainer.height
if (newVal > 1.5 || newVal < -0.5) {
keyframeModel.removeKeyframe(frame);
timeline.removeClipEffectKeyframe(clipRoot.clipId, frame);
} else if (frame != newPos) {
newVal = newVal < 0 ? 0 : newVal > 1 ? 1 : newVal
keyframeModel.moveKeyframe(frame, newPos, newVal)
timeline.updateClipEffectKeyframe(clipRoot.clipId, frame, newPos, newVal)
}
}
onPositionChanged: {
......@@ -170,7 +170,7 @@ Rectangle
}
}
onDoubleClicked: {
keyframeModel.removeKeyframe(frame);
timeline.removeClipEffectKeyframe(clipRoot.clipId, frame);
}
}
}
......
......@@ -1026,7 +1026,7 @@ Rectangle {
if (dragProxy.masterObject.keyframeModel) {
var newVal = (dragProxy.masterObject.height - mouseY) / dragProxy.masterObject.height
var newPos = Math.round(mouseX / timeScale) + dragProxy.masterObject.inPoint
dragProxy.masterObject.keyframeModel.addKeyframe(newPos, newVal)
timeline.addClipEffectKeyframe(dragProxy.draggedItem, newPos, newVal)
}
}
}
......
......@@ -2276,3 +2276,28 @@ void TimelineController::saveTimelineSelection(QDir targetDir)
{
TimelineFunctions::saveTimelineSelection(m_model, m_selection.selectedItems, targetDir);
}
void TimelineController::addClipEffectKeyframe(int cid, int frame, double val)
{
if (m_model->isClip(cid)) {
std::shared_ptr<EffectStackModel> destStack = m_model->getClipEffectStackModel(cid);
destStack->addEffectKeyFrame(frame, val);
}
}
void TimelineController::removeClipEffectKeyframe(int cid, int frame)
{
if (m_model->isClip(cid)) {
std::shared_ptr<EffectStackModel> destStack = m_model->getClipEffectStackModel(cid);
destStack->removeKeyFrame(frame);
}
}
void TimelineController::updateClipEffectKeyframe(int cid, int oldFrame, int newFrame, double normalizedValue)
{
if (m_model->isClip(cid)) {
std::shared_ptr<EffectStackModel> destStack = m_model->getClipEffectStackModel(cid);
destStack->updateKeyFrame(oldFrame, newFrame, normalizedValue);
}
}
......@@ -305,6 +305,9 @@ public:
*/
Q_INVOKABLE void pasteEffects(int targetId = -1);
Q_INVOKABLE double fps() const;
Q_INVOKABLE void addClipEffectKeyframe(int cid, int frame, double val);
Q_INVOKABLE void removeClipEffectKeyframe(int cid, int frame);
Q_INVOKABLE void updateClipEffectKeyframe(int cid, int oldFrame, int newFrame, double normalizedValue);
void switchTrackLock(bool applyToAll = false);
void switchTargetTrack();
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment