diff --git a/data/effects/rotoscoping.xml b/data/effects/rotoscoping.xml --- a/data/effects/rotoscoping.xml +++ b/data/effects/rotoscoping.xml @@ -3,7 +3,7 @@ Rotoscoping Keyframable vector based rotoscoping Till Theato - + Alpha,Luma,RGB Mode @@ -29,6 +29,4 @@ Feathering passes - - diff --git a/src/assets/CMakeLists.txt b/src/assets/CMakeLists.txt --- a/src/assets/CMakeLists.txt +++ b/src/assets/CMakeLists.txt @@ -5,6 +5,8 @@ assets/assetlist/model/assetfilter.cpp assets/assetlist/model/assettreemodel.cpp assets/assetpanel.cpp + assets/keyframes/model/rotoscoping/bpoint.cpp + assets/keyframes/model/rotoscoping/rotowidget.cpp assets/keyframes/model/keyframemodel.cpp assets/keyframes/model/keyframemodellist.cpp assets/keyframes/view/keyframeview.cpp diff --git a/src/assets/keyframes/model/keyframemodel.hpp b/src/assets/keyframes/model/keyframemodel.hpp --- a/src/assets/keyframes/model/keyframemodel.hpp +++ b/src/assets/keyframes/model/keyframemodel.hpp @@ -173,9 +173,11 @@ |= represents a discrete keyframe, = a linear one and ~= a Catmull-Rom spline */ QString getAnimProperty() const; + QString getRotoProperty() const; /* @brief this function does the opposite: given a MLT representation of an animation, build the corresponding model */ void parseAnimProperty(const QString &prop); + void parseRotoProperty(const QString &prop); private: std::weak_ptr m_model; diff --git a/src/assets/keyframes/model/keyframemodel.cpp b/src/assets/keyframes/model/keyframemodel.cpp --- a/src/assets/keyframes/model/keyframemodel.cpp +++ b/src/assets/keyframes/model/keyframemodel.cpp @@ -20,11 +20,14 @@ ***************************************************************************/ #include "keyframemodel.hpp" +#include "rotoscoping/bpoint.h" +#include "rotoscoping/rotowidget.hpp" #include "core.h" #include "doc/docundostack.hpp" #include "macros.hpp" #include +#include #include KeyframeModel::KeyframeModel(std::weak_ptr model, const QModelIndex &index, std::weak_ptr undo_stack, QObject *parent) @@ -280,6 +283,7 @@ if (m_paramType == ParamType::KeyframeParam) { if (qFuzzyCompare(oldValue.toDouble(), value.toDouble())) return true; } + auto operation = updateKeyframe_lambda(pos, type, value, true); auto reverse = updateKeyframe_lambda(pos, type, oldValue, true); bool res = operation(); @@ -611,6 +615,21 @@ return prop; } +QString KeyframeModel::getRotoProperty() const +{ + QJsonDocument doc; + if (auto ptr = m_model.lock()) { + int in = ptr->data(m_index, AssetParameterModel::InRole).toInt(); + int out = ptr->data(m_index, AssetParameterModel::ParentDurationRole).toInt(); + QMap map; + for (const auto keyframe : m_keyframeList) { + map.insert(QString::number(in + keyframe.first.frames(pCore->getCurrentFps())).rightJustified(log10((double)out) + 1, '0'), keyframe.second.second); + } + doc = QJsonDocument::fromVariant(QVariant(map)); + } + return doc.toJson(); +} + mlt_keyframe_type convertToMltType(KeyframeType type) { switch (type) { @@ -687,6 +706,25 @@ */ } +void KeyframeModel::parseRotoProperty(const QString &prop) +{ + Fun undo = []() { return true; }; + Fun redo = []() { return true; }; + + QJsonParseError jsonError; + QJsonDocument doc = QJsonDocument::fromJson(prop.toLatin1(), &jsonError); + QVariant data = doc.toVariant(); + if (data.canConvert(QVariant::Map)) { + QList keyframes; + QMap map = data.toMap(); + QMap::const_iterator i = map.constBegin(); + while (i != map.constEnd()) { + addKeyframe(GenTime(i.key().toInt(), pCore->getCurrentFps()), KeyframeType::Linear, i.value(), false, undo, redo); + ++i; + } + } +} + QVariant KeyframeModel::getInterpolatedValue(int p) const { auto pos = GenTime(p, pCore->getCurrentFps()); @@ -695,10 +733,12 @@ QVariant KeyframeModel::getInterpolatedValue(const GenTime &pos) const { - int p = pos.frames(pCore->getCurrentFps()); if (m_keyframeList.count(pos) > 0) { return m_keyframeList.at(pos).second; } + if (m_keyframeList.size() == 0) { + return QVariant(); + } auto next = m_keyframeList.upper_bound(pos); if (next == m_keyframeList.cbegin()) { return (m_keyframeList.cbegin())->second.second; @@ -712,6 +752,7 @@ // We now have surrounding keyframes, we use mlt to compute the value Mlt::Properties prop; QLocale locale; + int p = pos.frames(pCore->getCurrentFps()); if (m_paramType == ParamType::KeyframeParam) { prop.anim_set("keyframe", prev->second.second.toDouble(), prev->first.frames(pCore->getCurrentFps()), next->first.frames(pCore->getCurrentFps()), convertToMltType(prev->second.first)); @@ -751,6 +792,28 @@ rect = prop.anim_get_rect("keyframe", p); const QString res = QStringLiteral("%1 %2 %3 %4 %5").arg((int)rect.x).arg((int)rect.y).arg((int)rect.w).arg((int)rect.h).arg(rect.o); return QVariant(res); + } else if (m_paramType == ParamType::Roto_spline) { + // interpolate + QSize frame = pCore->getCurrentFrameSize(); + QList p1 = RotoWidget::getPoints(prev->second.second, frame); + qreal relPos = (p - prev->first.frames(pCore->getCurrentFps())) / (qreal)(((next->first - prev->first).frames(pCore->getCurrentFps())) + 1); + QList p2 = RotoWidget::getPoints(next->second.second, frame); + int count = qMin(p1.count(), p2.count()); + QList vlist; + for (int i = 0; i < count; ++i) { + BPoint bp; + QList pl; + for (int j = 0; j < 3; ++j) { + if (p1.at(i)[j] != p2.at(i)[j]) { + bp[j] = QLineF(p1.at(i)[j], p2.at(i)[j]).pointAt(relPos); + } else { + bp[j] = p1.at(i)[j]; + } + pl << QVariant(QList() << QVariant(bp[j].x() / frame.width()) << QVariant(bp[j].y() / frame.height())); + } + vlist << QVariant(pl); + } + return vlist; } return QVariant(); } @@ -764,6 +827,9 @@ if (m_paramType == ParamType::KeyframeParam || m_paramType == ParamType::AnimatedRect) { data = getAnimProperty(); ptr->setParameter(name, data); + } else if (m_paramType == ParamType::Roto_spline) { + data = getRotoProperty(); + ptr->setParameter(name, data); } else { Q_ASSERT(false); // Not implemented, TODO } @@ -784,6 +850,8 @@ if (m_paramType == ParamType::KeyframeParam || m_paramType == ParamType::AnimatedRect) { qDebug() << "parsing keyframe" << data; parseAnimProperty(data); + } else if (m_paramType == ParamType::Roto_spline) { + parseRotoProperty(data); } else { // first, try to convert to double bool ok = false; diff --git a/src/assets/keyframes/model/keyframemodellist.hpp b/src/assets/keyframes/model/keyframemodellist.hpp --- a/src/assets/keyframes/model/keyframemodellist.hpp +++ b/src/assets/keyframes/model/keyframemodellist.hpp @@ -90,6 +90,9 @@ /* @brief Returns true if we only have 1 keyframe */ bool singleKeyframe() const; + /* @brief Returns true if we only have no keyframe + */ + bool isEmpty() const; /* @brief Returns the keyframe located after given position. If there is a keyframe at given position it is ignored. diff --git a/src/assets/keyframes/model/keyframemodellist.cpp b/src/assets/keyframes/model/keyframemodellist.cpp --- a/src/assets/keyframes/model/keyframemodellist.cpp +++ b/src/assets/keyframes/model/keyframemodellist.cpp @@ -166,6 +166,12 @@ return m_parameters.begin()->second->singleKeyframe(); } +bool KeyframeModelList::isEmpty() const +{ + READ_LOCK(); + return (m_parameters.size() == 0 || m_parameters.begin()->second->rowCount() == 0); +} + Keyframe KeyframeModelList::getNextKeyframe(const GenTime &pos, bool *ok) const { READ_LOCK(); diff --git a/src/assets/keyframes/model/rotoscoping/bpoint.h b/src/assets/keyframes/model/rotoscoping/bpoint.h new file mode 100644 --- /dev/null +++ b/src/assets/keyframes/model/rotoscoping/bpoint.h @@ -0,0 +1,72 @@ +/*************************************************************************** + * Copyright (C) 2011 by Till Theato (root@ttill.de) * + * This file is part of Kdenlive (www.kdenlive.org). * + * * + * Kdenlive is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 2 of the License, or * + * (at your option) any later version. * + * * + * Kdenlive is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with Kdenlive. If not, see . * + ***************************************************************************/ + +#ifndef BPOINT_H +#define BPOINT_H + +#include + +/** + * @brief Represents a point in a cubic Bézier spline. + */ + +class BPoint +{ +public: + enum class PointType { H1 = 0, P = 1, H2 = 2 }; + /** @brief Sets the point to -1, -1 to mark it as unusable (until point + handles have proper values) */ + BPoint(); + /** @brief Sets up according to the params. Linking detecting is done using autoSetLinked(). */ + BPoint(const QPointF &handle1, const QPointF &point, const QPointF &handle2); + + bool operator==(const BPoint &point) const; + /** @brief Returns h1 if i = 0, p if i = 1, h2 if i = 2. */ + QPointF &operator[](int i); + /** @brief Returns h1 if i = 0, p if i = 1, h2 if i = 2. */ + const QPointF &operator[](int i) const; + + /** @brief Sets p to @param point. + * @param updateHandles (default = true) Whether to make sure the handles keep their position relative to p. */ + void setP(const QPointF &point, bool updateHandles = true); + + /** @brief Sets h1 to @param handle1. + * + * If handlesLinked is true h2 is updated. */ + void setH1(const QPointF &handle1); + + /** @brief Sets h2 to @param handle2. + * If handlesLinked is true h1 is updated. */ + void setH2(const QPointF &handle2); + + /** @brief Sets handlesLinked to true if the handles are in a linked state (line through h1, p, h2) otherwise to false. */ + void autoSetLinked(); + + /** @brief Toggles the link of the handles to @param linked*/ + void setHandlesLinked(bool linked); + + /** handle 1 */ + QPointF h1; + /** point */ + QPointF p; + /** handle 2 */ + QPointF h2; + /** handles are linked to achieve a natural locking spline => PH1 = -r*PH2 ; a line can be drawn through h1, p, h2 */ + bool handlesLinked; +}; + +#endif diff --git a/src/assets/keyframes/model/rotoscoping/bpoint.cpp b/src/assets/keyframes/model/rotoscoping/bpoint.cpp new file mode 100644 --- /dev/null +++ b/src/assets/keyframes/model/rotoscoping/bpoint.cpp @@ -0,0 +1,101 @@ +/*************************************************************************** + * Copyright (C) 2011 by Till Theato (root@ttill.de) * + * This file is part of Kdenlive (www.kdenlive.org). * + * * + * Kdenlive is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 2 of the License, or * + * (at your option) any later version. * + * * + * Kdenlive is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with Kdenlive. If not, see . * + ***************************************************************************/ + +#include "bpoint.h" + +#include + +BPoint::BPoint() + : h1(-1, -1) + , p(-1, -1) + , h2(-1, -1) + , handlesLinked(true) +{ +} + +BPoint::BPoint(const QPointF &handle1, const QPointF &point, const QPointF &handle2) + : h1(handle1) + , p(point) + , h2(handle2) +{ + autoSetLinked(); +} + +QPointF &BPoint::operator[](int i) +{ + return i == 0 ? h1 : (i == 1 ? p : h2); +} + +const QPointF &BPoint::operator[](int i) const +{ + return i == 0 ? h1 : (i == 1 ? p : h2); +} + +bool BPoint::operator==(const BPoint &point) const +{ + return point.h1 == h1 && point.p == p && point.h2 == h2; +} + +void BPoint::setP(const QPointF &point, bool updateHandles) +{ + QPointF offset = point - p; + p = point; + if (updateHandles) { + h1 += offset; + h2 += offset; + } +} + +void BPoint::setH1(const QPointF &handle1) +{ + h1 = handle1; + if (handlesLinked) { + qreal angle = QLineF(h1, p).angle(); + QLineF l = QLineF(p, h2); + l.setAngle(angle); + h2 = l.p2(); + } +} + +void BPoint::setH2(const QPointF &handle2) +{ + h2 = handle2; + if (handlesLinked) { + qreal angle = QLineF(h2, p).angle(); + QLineF l = QLineF(p, h1); + l.setAngle(angle); + h1 = l.p2(); + } +} + +void BPoint::autoSetLinked() +{ + // sometimes the angle is returned as 360° + // due to rounding problems the angle is sometimes not quite 0 + qreal angle = QLineF(h1, p).angleTo(QLineF(p, h2)); + handlesLinked = angle < 1e-3 || qRound(angle) == 360; +} + +void BPoint::setHandlesLinked(bool linked) +{ + handlesLinked = linked; + if (linked) { + // we force recomputing one of the handles + setH1(h1); + } +} diff --git a/src/assets/keyframes/model/rotoscoping/rotowidget.hpp b/src/assets/keyframes/model/rotoscoping/rotowidget.hpp new file mode 100644 --- /dev/null +++ b/src/assets/keyframes/model/rotoscoping/rotowidget.hpp @@ -0,0 +1,72 @@ +/* +Copyright (C) 2018 Jean-Baptiste Mardelle +This file is part of Kdenlive. See www.kdenlive.org. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of +the License or (at your option) version 3 or any later version +accepted by the membership of KDE e.V. (or its successor approved +by the membership of KDE e.V.), which shall act as a proxy +defined in Section 14 of version 3 of the license. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ + +#ifndef ROTOHELPER_H +#define ROTOHELPER_H + +#include "bpoint.h" + +#include +#include +#include + +class Monitor; + +class RotoWidget : public QWidget +{ + Q_OBJECT + +public: + /* @brief Construct a keyframe list bound to the given effect + @param init_value is the value taken by the param at time 0. + @param model is the asset this parameter belong to + @param index is the index of this parameter in its model + */ + explicit RotoWidget(Monitor *monitor, QPersistentModelIndex index, QWidget *parent = nullptr); + /** @brief Send signals to the monitor to update the qml overlay. + @param returns : true if the monitor's connection was changed to active. + */ + bool connectMonitor(bool activate); + /** @brief Returns a spline defined as string, based on its control points and frame size + @param value : the control points + @param frame: the frame size + */ + static QVariant getSpline(QVariant value, const QSize frame); + /** @brief Returns a list of spline control points, based on its string definition and frame size + @param value : the spline's string definition + @param frame: the frame size + */ + static QList getPoints(QVariant value, const QSize frame); + +private: + Monitor *m_monitor; + QPersistentModelIndex m_index; + bool m_active; + +private slots: + void slotUpdateRotoMonitor(const QVariantList &v); + +signals: + void updateRotoKeyframe(QPersistentModelIndex, const QVariantList&); +}; + +#endif + diff --git a/src/assets/keyframes/model/rotoscoping/rotowidget.cpp b/src/assets/keyframes/model/rotoscoping/rotowidget.cpp new file mode 100644 --- /dev/null +++ b/src/assets/keyframes/model/rotoscoping/rotowidget.cpp @@ -0,0 +1,94 @@ +/* +Copyright (C) 2018 Jean-Baptiste Mardelle +This file is part of Kdenlive. See www.kdenlive.org. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of +the License or (at your option) version 3 or any later version +accepted by the membership of KDE e.V. (or its successor approved +by the membership of KDE e.V.), which shall act as a proxy +defined in Section 14 of version 3 of the license. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ + +#include "rotowidget.hpp" +#include "gentime.h" +#include "monitor/monitor.h" + +#include + +RotoWidget::RotoWidget(Monitor *monitor, QPersistentModelIndex index, QWidget *parent) + : QWidget(parent) + , m_monitor(monitor) + , m_index(index) + , m_active(false) +{ +} + +bool RotoWidget::connectMonitor(bool activate) +{ + if (activate == m_active) { + return false; + } + m_active = activate; + if (activate) { + connect(m_monitor, &Monitor::effectPointsChanged, this, &RotoWidget::slotUpdateRotoMonitor, Qt::UniqueConnection); + } else { + disconnect(m_monitor, &Monitor::effectPointsChanged, this, &RotoWidget::slotUpdateRotoMonitor); + } + return m_active; +} + +void RotoWidget::slotUpdateRotoMonitor(const QVariantList &v) +{ + emit updateRotoKeyframe(m_index, v); +} + + +QVariant RotoWidget::getSpline(QVariant value, const QSize frame) +{ + QList bPoints; + const QVariantList points = value.toList(); + for (int i = 0; i < points.size() / 3; i++) { + BPoint b(points.at(3 * i).toPointF(), points.at(3 * i + 1).toPointF(), points.at(3 * i + 2).toPointF()); + bPoints << b; + } + QList vlist; + foreach (const BPoint &point, bPoints) { + QList pl; + for (int i = 0; i < 3; ++i) { + pl << QVariant(QList() << QVariant(point[i].x() / frame.width()) << QVariant(point[i].y() / frame.height())); + } + vlist << QVariant(pl); + } + return vlist; +} + +QList RotoWidget::getPoints(QVariant value, const QSize frame) +{ + QList points; + QList data = value.toList(); + + // skip tracking flag + if (data.count() && data.at(0).canConvert(QVariant::String)) { + data.removeFirst(); + } + + foreach (const QVariant &bpoint, data) { + QList l = bpoint.toList(); + BPoint p; + for (int i = 0; i < 3; ++i) { + p[i] = QPointF(l.at(i).toList().at(0).toDouble() * frame.width(), l.at(i).toList().at(1).toDouble() * frame.height()); + } + points << p; + } + return points; +} diff --git a/src/assets/model/assetparametermodel.hpp b/src/assets/model/assetparametermodel.hpp --- a/src/assets/model/assetparametermodel.hpp +++ b/src/assets/model/assetparametermodel.hpp @@ -91,6 +91,7 @@ InRole, OutRole, ParentInRole, + ParentPositionRole, ParentDurationRole }; diff --git a/src/assets/model/assetparametermodel.cpp b/src/assets/model/assetparametermodel.cpp --- a/src/assets/model/assetparametermodel.cpp +++ b/src/assets/model/assetparametermodel.cpp @@ -117,7 +117,7 @@ if (m_keyframes) return; int ix = 0; for (const auto &name : m_rows) { - if (m_params[name].type == ParamType::KeyframeParam || m_params[name].type == ParamType::AnimatedRect) { + if (m_params[name].type == ParamType::KeyframeParam || m_params[name].type == ParamType::AnimatedRect || m_params[name].type == ParamType::Roto_spline) { addKeyframeParam(index(ix, 0)); } ix++; @@ -260,8 +260,10 @@ return m_asset->get_int("in"); case OutRole: return m_asset->get_int("out"); - case ParentInRole: + case ParentPositionRole: return pCore->getItemPosition(m_ownerId); + case ParentInRole: + return pCore->getItemIn(m_ownerId); case ParentDurationRole: return pCore->getItemDuration(m_ownerId); case MinRole: diff --git a/src/assets/view/assetparameterview.cpp b/src/assets/view/assetparameterview.cpp --- a/src/assets/view/assetparameterview.cpp +++ b/src/assets/view/assetparameterview.cpp @@ -200,6 +200,9 @@ return MonitorSceneGeometry; } } + if (m_model->getAssetId() == QStringLiteral("rotoscoping")) { + return MonitorSceneRoto; + } return MonitorSceneDefault; } diff --git a/src/assets/view/widgets/abstractparamwidget.cpp b/src/assets/view/widgets/abstractparamwidget.cpp --- a/src/assets/view/widgets/abstractparamwidget.cpp +++ b/src/assets/view/widgets/abstractparamwidget.cpp @@ -84,6 +84,7 @@ break; case ParamType::KeyframeParam: case ParamType::AnimatedRect: + case ParamType::Roto_spline: widget = new KeyframeWidget(model, index, parent); break; case ParamType::Geometry: @@ -109,7 +110,6 @@ case ParamType::Addedgeometry: case ParamType::Curve: case ParamType::Bezier_spline: - case ParamType::Roto_spline: case ParamType::Wipe: case ParamType::Url: case ParamType::Keywords: diff --git a/src/assets/view/widgets/keyframewidget.hpp b/src/assets/view/widgets/keyframewidget.hpp --- a/src/assets/view/widgets/keyframewidget.hpp +++ b/src/assets/view/widgets/keyframewidget.hpp @@ -67,6 +67,7 @@ void slotAtKeyframe(bool atKeyframe, bool singleKeyframe); void monitorSeek(int pos); void slotEditKeyframeType(QAction *action); + void slotUpdateRotoMonitor(QPersistentModelIndex index, const QVariantList &v); private: QVBoxLayout *m_lay; @@ -80,7 +81,7 @@ TimecodeDisplay *m_time; void connectMonitor(bool active); std::unordered_map m_parameters; - + QPersistentModelIndex m_rotoIx; }; #endif diff --git a/src/assets/view/widgets/keyframewidget.cpp b/src/assets/view/widgets/keyframewidget.cpp --- a/src/assets/view/widgets/keyframewidget.cpp +++ b/src/assets/view/widgets/keyframewidget.cpp @@ -19,6 +19,7 @@ #include "keyframewidget.hpp" #include "assets/keyframes/model/keyframemodellist.hpp" +#include "assets/keyframes/model/rotoscoping/rotowidget.hpp" #include "assets/keyframes/view/keyframeview.hpp" #include "assets/model/assetparametermodel.hpp" #include "core.h" @@ -36,9 +37,8 @@ KeyframeWidget::KeyframeWidget(std::shared_ptr model, QModelIndex index, QWidget *parent) : AbstractParamWidget(model, index, parent) + , m_keyframes(model->getKeyframeModel()) { - m_keyframes = model->getKeyframeModel(); - setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); m_lay = new QVBoxLayout(this); @@ -126,8 +126,9 @@ m_buttonAddDelete->setEnabled(isInRange); connectMonitor(isInRange); int framePos = qBound(in, pos, out) - in; - m_keyframeview->slotSetPosition(framePos, isInRange); - m_time->setValue(framePos); + if (isInRange) { + slotSetPosition(framePos, false); + } } void KeyframeWidget::slotEditKeyframeType(QAction *action) @@ -158,6 +159,22 @@ } } ((GeometryWidget *)w.second)->setValue(rect, opacity); + } else if (type == ParamType::Roto_spline) { + QVariantList centerPoints; + QVariantList controlPoints; + if (!m_keyframes->isEmpty()) { + QVariant splineData = m_keyframes->getInterpolatedValue(pos, w.first); + QList p = RotoWidget::getPoints(splineData, pCore->getCurrentFrameSize()); + for (int i = 0; i < p.size(); i++) { + centerPoints << QVariant(p.at(i).p); + controlPoints << QVariant(p.at(i).h1); + controlPoints << QVariant(p.at(i).h2); + } + Monitor *monitor = pCore->getMonitor(m_model->monitorId); + if (monitor) { + monitor->setUpEffectGeometry(QRect(), centerPoints, controlPoints); + } + } } } } @@ -170,7 +187,6 @@ m_time->setValue(pos); m_keyframeview->slotSetPosition(pos, true); } - slotRefreshParams(); if (update) { @@ -257,6 +273,13 @@ connect(geomWidget, &GeometryWidget::valueChanged, [this, index](const QString v) { m_keyframes->updateKeyframe(GenTime(getPosition(), pCore->getCurrentFps()), QVariant(v), index); }); paramWidget = geomWidget; + } else if (type == ParamType::Roto_spline) { + m_rotoIx = index; + Monitor *monitor = pCore->getMonitor(m_model->monitorId); + RotoWidget *roto = new RotoWidget(monitor, index, this); + connect(roto, &RotoWidget::updateRotoKeyframe, this, &KeyframeWidget::slotUpdateRotoMonitor, Qt::UniqueConnection); + paramWidget = roto; + paramWidget->setMaximumHeight(1); } else { double value = m_keyframes->getInterpolatedValue(getPosition(), index).toDouble(); double min = m_model->data(index, AssetParameterModel::MinRole).toDouble(); @@ -298,5 +321,23 @@ ((GeometryWidget *)w.second)->connectMonitor(active); break; } + if (type == ParamType::Roto_spline) { + // Rotoscoping widget, trigger refresh + if (((RotoWidget *)w.second)->connectMonitor(active)) { + slotRefreshParams(); + } + break; + } + } +} + +void KeyframeWidget::slotUpdateRotoMonitor(QPersistentModelIndex index, const QVariantList &v) +{ + QVariant res = RotoWidget::getSpline(QVariant(v), pCore->getCurrentFrameSize()); + if (m_keyframes->isEmpty()) { + m_keyframes->addKeyframe(GenTime(getPosition(), pCore->getCurrentFps()), KeyframeType::Linear); + m_keyframes->updateKeyframe(GenTime(getPosition(), pCore->getCurrentFps()), res, index); + } else if (m_keyframes->hasKeyframe(getPosition())) { + m_keyframes->updateKeyframe(GenTime(getPosition(), pCore->getCurrentFps()), res, index); } } diff --git a/src/effects/effectstack/view/effectstackview.cpp b/src/effects/effectstack/view/effectstackview.cpp --- a/src/effects/effectstack/view/effectstackview.cpp +++ b/src/effects/effectstack/view/effectstackview.cpp @@ -233,7 +233,6 @@ view->slotActivateEffect(m_model->getIndexFromItem(activeModel)); view->buttonUp->setEnabled(i > 0); view->buttonDown->setEnabled(i < max - 1); - } updateTreeHeight(); qDebug() << "MUTEX UNLOCK!!!!!!!!!!!! loadEffects"; @@ -307,9 +306,16 @@ // Release ownership of smart pointer if (m_model) { disconnect(m_model.get(), &EffectStackModel::dataChanged, this, &EffectStackView::refresh); - } - if (reset) { - m_model.reset(); + if (reset) { + // Make sure to delete the delegates + for (int i = 0; i < m_model->rowCount(); i++) { + std::shared_ptr item = m_model->getEffectStackRow(i); + std::shared_ptr eff = std::static_pointer_cast(item); + QModelIndex ix = m_model->getIndexFromItem(eff); + m_effectsTree->setIndexWidget(ix, nullptr); + } + m_model.reset(); + } } } diff --git a/src/monitor/view/kdenlivemonitorrotoscene.qml b/src/monitor/view/kdenlivemonitorrotoscene.qml --- a/src/monitor/view/kdenlivemonitorrotoscene.qml +++ b/src/monitor/view/kdenlivemonitorrotoscene.qml @@ -4,14 +4,13 @@ id: root objectName: "rootrotoscene" + SystemPalette { id: activePalette } // default size, but scalable by user height: 300; width: 400 property string comment property string framenum - property rect framesize: Qt.rect(5, 5, 200, 200) property point profile - profile: Qt.point(1920, 1080) - property point center: Qt.point(960, 540) + property point center property double zoom property double scalex : 1 property double scaley : 1 @@ -19,6 +18,13 @@ property double sourcedar : 1 property double offsetx : 0 property double offsety : 0 + property double frameSize: 10 + property int duration: 300 + property double timeScale: 1 + property int rulerHeight: 20 + property bool mouseOverRuler: false + property int mouseRulerPos: 0 + property int consumerPosition: -1 onOffsetxChanged: canvas.requestPaint() onOffsetyChanged: canvas.requestPaint() onScalexChanged: canvas.requestPaint() @@ -42,6 +48,11 @@ effectToolBar.setZoom(root.zoom) } + onIskeyframeChanged: { + console.log('KEYFRAME CHANGED: ', iskeyframe) + canvas.requestPaint() + } + function refreshdar() { canvas.darOffset = root.sourcedar < root.profile.x * root.stretch / root.profile.y ? (root.profile.x * root.stretch - root.profile.y * root.sourcedar) / (2 * root.profile.x * root.stretch) :(root.profile.y - root.profile.x * root.stretch / root.sourcedar) / (2 * root.profile.y); canvas.requestPaint() @@ -52,19 +63,35 @@ canvas.requestPaint() } + onDurationChanged: { + timeScale = width / duration + if (duration < 200) { + frameSize = 5 * timeScale + } else if (duration < 2500) { + frameSize = 25 * timeScale + } else if (duration < 10000) { + frameSize = 50 * timeScale + } else { + frameSize = 100 * timeScale + } + } + Text { id: fontReference property int fontSize fontSize: font.pointSize } + Item { + id: monitorOverlay + height: root.height - root.rulerHeight + width: root.width + Canvas { id: canvas property double handleSize property double darOffset : 0 - width: root.width - height: root.height - anchors.centerIn: root + anchors.fill: parent contextType: "2d"; handleSize: fontReference.fontSize / 2 renderTarget: Canvas.FramebufferObject @@ -165,8 +192,8 @@ property color hoverColor: "#ff0000" width: root.profile.x * root.scalex height: root.profile.y * root.scaley - x: root.center.x - width / 2 - root.offsetx - y: root.center.y - height / 2 - root.offsety + x: root.center.x - width / 2 - root.offsetx; + y: root.center.y - height / 2 - root.offsety; color: "transparent" border.color: "#ffffff00" } @@ -175,9 +202,8 @@ id: global objectName: "global" acceptedButtons: Qt.LeftButton | Qt.RightButton - width: root.width; height: root.height + anchors.fill: parent property bool containsMouse - anchors.centerIn: root hoverEnabled: true cursorShape: containsMouse ? Qt.PointingHandCursor : Qt.ArrowCursor @@ -288,3 +314,13 @@ visible: root.showToolbar } } + MonitorRuler { + id: clipMonitorRuler + anchors { + left: root.left + right: root.right + bottom: root.bottom + } + height: root.rulerHeight + } +}