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
+ }
+}