Commit 71e11d7a authored by Jean-Baptiste Mardelle's avatar Jean-Baptiste Mardelle
Browse files

Reintroduce the much requested rotoscoping effect, now ported to qml

parent 891fe441
......@@ -20,10 +20,14 @@ Item {
onScaleyChanged: canvas.requestPaint()
onSourcedarChanged: refreshdar()
property bool iskeyframe : true
property int requestedKeyFrame
property var centerPoints: [Qt.point(120, 100), Qt.point(160, 100), Qt.point(180, 200), Qt.point(120, 200)]
property var centerPointsTypes: [Qt.point(120, 100), Qt.point(160, 100), Qt.point(180, 200), Qt.point(120, 200)]
onCenterPointsChanged: canvas.requestPaint()
property bool isDefined: false
property int requestedKeyFrame : -1
property int requestedSubKeyFrame : -1
// The coordinate points where the bezier curve passes
property var centerPoints : []
// The control points for the bezier curve points (2 controls points for each coordinate)
property var centerPointsTypes : []
onCenterPointsTypesChanged: checkDefined()
signal effectPolygonChanged()
signal addKeyframe()
signal seekToKeyframe()
......@@ -33,6 +37,11 @@ Item {
canvas.requestPaint()
}
function checkDefined() {
root.isDefined = root.centerPointsTypes.length > 0
canvas.requestPaint()
}
Text {
id: fontReference
property int fontSize
......@@ -60,22 +69,73 @@ Item {
ctx.strokeStyle = Qt.rgba(1, 0, 0, 0.5)
ctx.fillStyle = Qt.rgba(1, 0, 0, 0.5)
ctx.lineWidth = 2
if (root.centerPoints.length == 0) {
// no points defined yet
return
}
var p1 = convertPoint(root.centerPoints[0])
var startP = p1;
ctx.moveTo(p1.x, p1.y)
ctx.fillRect(p1.x - handleSize, p1.y - handleSize, 2 * handleSize, 2 * handleSize);
for (var i = 0; i < root.centerPoints.length - 1; i++) {
p1 = convertPoint(root.centerPoints[i + 1])
// Control points
var c1 = convertPoint(root.centerPointsTypes[i])
var c2 = convertPoint(root.centerPointsTypes[i + 1])
ctx.bezierCurveTo(c1.x, c1.y, c2.x, c2.y, p1.x, p1.y);
if (!isDefined) {
ctx.fillRect(p1.x - handleSize, p1.y - handleSize, 2 * handleSize, 2 * handleSize);
}
if (root.centerPoints.length > 2) {
var c1 = convertPoint(root.centerPointsTypes[root.centerPoints.length - 1])
var c2 = convertPoint(root.centerPointsTypes[0])
ctx.bezierCurveTo(c1.x, c1.y, c2.x, c2.y, startP.x, startP.y);
for (var i = 1; i < root.centerPoints.length; i++) {
p1 = convertPoint(root.centerPoints[i])
ctx.lineTo(p1.x, p1.y);
ctx.fillRect(p1.x - handleSize, p1.y - handleSize, 2 * handleSize, 2 * handleSize);
}
} else {
var c1; var c2
for (var i = 0; i < root.centerPoints.length; i++) {
p1 = convertPoint(root.centerPoints[i])
// Control points
var subkf = false
if (i == 0) {
c1 = convertPoint(root.centerPointsTypes[root.centerPointsTypes.length - 1])
if (root.requestedSubKeyFrame == root.centerPointsTypes.length - 1) {
subkf = true
}
} else {
c1 = convertPoint(root.centerPointsTypes[2*i - 1])
if (root.requestedSubKeyFrame == 2*i - 1) {
subkf = true
}
}
c2 = convertPoint(root.centerPointsTypes[2*i])
ctx.bezierCurveTo(c1.x, c1.y, c2.x, c2.y, p1.x, p1.y);
if (iskeyframe) {
if (subkf) {
ctx.fillStyle = Qt.rgba(1, 1, 0, 0.8)
ctx.fillRect(c1.x - handleSize/2, c1.y - handleSize/2, handleSize, handleSize);
ctx.fillStyle = Qt.rgba(1, 0, 0, 0.5)
} else {
ctx.fillRect(c1.x - handleSize/2, c1.y - handleSize/2, handleSize, handleSize);
}
if (root.requestedSubKeyFrame == 2 * i) {
ctx.fillStyle = Qt.rgba(1, 1, 0, 0.8)
ctx.fillRect(c2.x - handleSize/2, c2.y - handleSize/2, handleSize, handleSize);
ctx.fillStyle = Qt.rgba(1, 0, 0, 0.5)
} else {
ctx.fillRect(c2.x - handleSize/2, c2.y - handleSize/2, handleSize, handleSize);
}
c1 = convertPoint(root.centerPointsTypes[2*i + 1])
ctx.lineTo(c1.x, c1.y);
ctx.moveTo(p1.x, p1.y)
ctx.lineTo(c2.x, c2.y);
ctx.moveTo(p1.x, p1.y)
if (i == root.requestedKeyFrame) {
ctx.fillStyle = Qt.rgba(1, 1, 0, 0.8)
ctx.fillRect(p1.x - handleSize, p1.y - handleSize, 2 * handleSize, 2 * handleSize);
ctx.fillStyle = Qt.rgba(1, 0, 0, 0.5)
} else {
ctx.fillRect(p1.x - handleSize, p1.y - handleSize, 2 * handleSize, 2 * handleSize);
}
}
}
if (root.centerPoints.length > 2) {
var c1 = convertPoint(root.centerPointsTypes[root.centerPointsTypes.length - 1])
var c2 = convertPoint(root.centerPointsTypes[0])
ctx.bezierCurveTo(c1.x, c1.y, c2.x, c2.y, startP.x, startP.y);
}
}
ctx.stroke()
......@@ -88,6 +148,7 @@ Item {
return Qt.point(x,y);
}
}
Rectangle {
id: frame
objectName: "referenceframe"
......@@ -102,24 +163,71 @@ Item {
MouseArea {
id: global
objectName: "global"
acceptedButtons: Qt.LeftButton | Qt.RightButton
width: root.width; height: root.height
property bool containsMouse
anchors.centerIn: root
hoverEnabled: true
cursorShape: containsMouse ? Qt.PointingHandCursor : Qt.ArrowCursor
onClicked: {
if (!root.isDefined) {
if (mouse.button == Qt.RightButton) {
// close shape, define control points
var p0; var p1; var p2
for (var i = 0; i < root.centerPoints.length; i++) {
p1 = root.centerPoints[i]
if (i == 0) {
p0 = root.centerPoints[root.centerPoints.length - 1]
} else {
p0 = root.centerPoints[i - 1]
}
if (i == root.centerPoints.length - 1) {
p2 = root.centerPoints[0]
} else {
p2 = root.centerPoints[i + 1]
}
var ctrl1 = Qt.point((p0.x - p1.x) / 5, (p0.y - p1.y) / 5);
var ctrl2 = Qt.point((p2.x - p1.x) / 5, (p2.y - p1.y) / 5);
root.centerPointsTypes.push(Qt.point(p1.x + ctrl1.x, p1.y + ctrl1.y))
root.centerPointsTypes.push(Qt.point(p1.x + ctrl2.x, p1.y + ctrl2.y))
}
root.isDefined = true;
root.effectPolygonChanged()
canvas.requestPaint()
} else {
var newPoint = Qt.point((mouseX - frame.x) / root.scalex, (mouseY - frame.y) / root.scaley);
root.centerPoints.push(newPoint)
canvas.requestPaint()
}
}
}
onDoubleClicked: {
root.addKeyframe()
}
onPositionChanged: {
if (root.iskeyframe == false) return;
if (pressed && root.requestedKeyFrame >= 0) {
root.centerPoints[root.requestedKeyFrame].x = (mouseX - frame.x) / root.scalex;
root.centerPoints[root.requestedKeyFrame].y = (mouseY - frame.y) / root.scaley;
canvas.requestPaint()
root.effectPolygonChanged()
} else {
if (isDefined && pressed) {
if (root.requestedKeyFrame >= 0) {
var xDiff = (mouseX - frame.x) / root.scalex - root.centerPoints[root.requestedKeyFrame].x
var yDiff = (mouseY - frame.y) / root.scaley - root.centerPoints[root.requestedKeyFrame].y
root.centerPoints[root.requestedKeyFrame].x += xDiff
root.centerPoints[root.requestedKeyFrame].y += yDiff
root.centerPointsTypes[root.requestedKeyFrame * 2].x += xDiff
root.centerPointsTypes[root.requestedKeyFrame * 2].y += yDiff
root.centerPointsTypes[root.requestedKeyFrame * 2 + 1].x += xDiff
root.centerPointsTypes[root.requestedKeyFrame * 2 + 1].y += yDiff
canvas.requestPaint()
root.effectPolygonChanged()
} else if (root.requestedSubKeyFrame >= 0) {
root.centerPointsTypes[root.requestedSubKeyFrame].x = (mouseX - frame.x) / root.scalex
root.centerPointsTypes[root.requestedSubKeyFrame].y = (mouseY - frame.y) / root.scaley
canvas.requestPaint()
root.effectPolygonChanged()
}
} else if (root.centerPoints.length > 0) {
for(var i = 0; i < root.centerPoints.length; i++)
{
var p1 = canvas.convertPoint(root.centerPoints[i])
......@@ -134,10 +242,25 @@ Item {
return;
}
}
if (root.requestedKeyFrame == -1) {
for(var i = 0; i < root.centerPointsTypes.length; i++)
{
var p1 = canvas.convertPoint(root.centerPointsTypes[i])
if (Math.abs(p1.x - mouseX) <= canvas.handleSize/2 && Math.abs(p1.y - mouseY) <= canvas.handleSize/2) {
if (i == root.requestedSubKeyFrame) {
containsMouse = true;
return;
}
root.requestedSubKeyFrame = i
canvas.requestPaint()
containsMouse = true;
return;
}
}
if (root.requestedKeyFrame == -1 && root.requestedSubKeyFrame == -1) {
return;
}
root.requestedKeyFrame = -1
root.requestedSubKeyFrame = -1
containsMouse = false;
canvas.requestPaint()
}
......
......@@ -1565,6 +1565,24 @@ QVariantList Monitor::effectPolygon() const
return root->property("centerPoints").toList();
}
QVariantList Monitor::effectRoto() const
{
QQuickItem *root = m_glMonitor->rootObject();
if (!root) {
return QVariantList();
}
QVariantList points = root->property("centerPoints").toList();
QVariantList controlPoints = root->property("centerPointsTypes").toList();
// rotoscoping effect needs a list of
QVariantList mix;
for (int i = 0; i < points.count(); i++) {
mix << controlPoints.at(2 * i);
mix << points.at(i);
mix << controlPoints.at(2 * i + 1);
}
return mix;
}
void Monitor::setEffectKeyframe(bool enable)
{
QQuickItem *root = m_glMonitor->rootObject();
......
......@@ -129,6 +129,7 @@ public:
QSize profileSize() const;
QRect effectRect() const;
QVariantList effectPolygon() const;
QVariantList effectRoto() const;
void setEffectKeyframe(bool enable);
void sendFrameForAnalysis(bool analyse);
void updateAudioForAnalysis();
......
......@@ -88,7 +88,7 @@ void QmlManager::setScene(Kdenlive::MonitorId id, MonitorSceneType type, QSize p
//TODO
m_view->setSource(QUrl(QStringLiteral("qrc:/qml/kdenlivemonitorrotoscene.qml")));
root = m_view->rootObject();
QObject::connect(root, SIGNAL(effectPolygonChanged()), this, SLOT(effectPolygonChanged()), Qt::UniqueConnection);
QObject::connect(root, SIGNAL(effectPolygonChanged()), this, SLOT(effectRotoChanged()), Qt::UniqueConnection);
root->setProperty("profile", QPoint(profile.width(), profile.height()));
root->setProperty("framesize", QRect(0, 0, profile.width(), profile.height()));
root->setProperty("scalex", (double) displayRect.width() / profile.width() * zoom);
......@@ -129,3 +129,18 @@ void QmlManager::effectPolygonChanged()
QVariantList points = m_view->rootObject()->property("centerPoints").toList();
emit effectPointsChanged(points);
}
void QmlManager::effectRotoChanged()
{
if (!m_view->rootObject()) return;
QVariantList points = m_view->rootObject()->property("centerPoints").toList();
QVariantList controlPoints = m_view->rootObject()->property("centerPointsTypes").toList();
// rotoscoping effect needs a list of
QVariantList mix;
for (int i = 0; i < points.count(); i++) {
mix << controlPoints.at(2 * i);
mix << points.at(i);
mix << controlPoints.at(2 * i + 1);
}
emit effectPointsChanged(mix);
}
......@@ -55,6 +55,7 @@ private:
private slots:
void effectRectChanged();
void effectPolygonChanged();
void effectRotoChanged();
signals:
void effectChanged(const QRect);
......
......@@ -53,25 +53,23 @@ RotoWidget::RotoWidget(const QByteArray &data, Monitor *monitor, const ItemInfo
QVBoxLayout *l = new QVBoxLayout(this);
m_keyframeWidget = new SimpleKeyframeWidget(t, m_out - m_in, this);
l->addWidget(m_keyframeWidget);
connect(m_monitor, SIGNAL(effectPointsChanged(QVariantList)), this, SLOT(slotUpdateDataPoints(QVariantList)));
//MonitorEditWidget *edit = NULL; //monitor->getEffectEdit();
//m_scene = NULL;//edit->getScene();
//m_scene->cleanup();
// TODO: port to qml monitor scene
/*m_item = new SplineItem(QList <BPoint>(), NULL, m_scene);
connect(m_item, SIGNAL(changed(bool)), this, SLOT(slotUpdateData(bool)));
connect(m_scene, SIGNAL(addKeyframe()), this, SLOT(slotAddKeyframe()));
*/
connect(m_keyframeWidget, SIGNAL(positionChanged(int)), this, SLOT(slotPositionChanged(int)));
connect(m_keyframeWidget, SIGNAL(keyframeAdded(int)), this, SLOT(slotAddKeyframe(int)));
connect(m_keyframeWidget, SIGNAL(keyframeRemoved(int)), this, SLOT(slotRemoveKeyframe(int)));
connect(m_keyframeWidget, SIGNAL(keyframeMoved(int,int)), this, SLOT(slotMoveKeyframe(int,int)));
connect(m_scene, SIGNAL(addKeyframe()), this, SLOT(slotAddKeyframe()));
setSpline(data, false);
setupTrackingListen(info);
m_scene->centerView();
*/
}
RotoWidget::~RotoWidget()
......@@ -93,18 +91,14 @@ void RotoWidget::slotSyncPosition(int relTimelinePos)
slotPositionChanged(relTimelinePos, false);
}
void RotoWidget::slotUpdateData(int pos, bool editing)
void RotoWidget::slotUpdateData(int pos, QList <BPoint> spline)
{
Q_UNUSED(editing)
int width = m_monitor->render->frameRenderWidth();
int height = m_monitor->render->renderHeight();
/*
* use the position of the on-monitor points to create a storable list
*/
//TODO: get points from monitor qml scene
QList <BPoint> spline; // = m_item->getPoints();
QList <QVariant> vlist;
foreach (const BPoint &point, spline) {
QList <QVariant> pl;
......@@ -131,9 +125,14 @@ void RotoWidget::slotUpdateData(int pos, bool editing)
emit valueChanged();
}
void RotoWidget::slotUpdateData(bool editing)
void RotoWidget::slotUpdateDataPoints(QVariantList points, int pos)
{
slotUpdateData(-1, editing);
QList <BPoint> bPoints;
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;
}
slotUpdateData(pos, bPoints);
}
QByteArray RotoWidget::getSpline()
......@@ -154,6 +153,7 @@ void RotoWidget::slotPositionChanged(int pos, bool seek)
pos += m_in;
QList <BPoint> p;
bool isKeyframe = false;
if (m_data.canConvert(QVariant::Map)) {
QMap <QString, QVariant> map = m_data.toMap();
......@@ -187,13 +187,9 @@ void RotoWidget::slotPositionChanged(int pos, bool seek)
}
p.append(bp);
}
// TODO: port to qml monitor scene
/*m_item->setPoints(p);
m_item->setEnabled(false);*/
//m_scene->setEnabled(false);
} else {
p = getPoints(keyframe2);
isKeyframe = pos == keyframe2;
// only update if necessary to preserve the current point selection
// TODO: port to qml monitor scene
/*
......@@ -204,6 +200,7 @@ void RotoWidget::slotPositionChanged(int pos, bool seek)
}
} else {
p = getPoints(-1);
isKeyframe = true;
// only update if necessary to preserve the current point selection
// TODO: port to qml monitor scene
/*if (p != m_item->getPoints())
......@@ -211,6 +208,15 @@ void RotoWidget::slotPositionChanged(int pos, bool seek)
m_item->setEnabled(true);*/
//m_scene->setEnabled(true);
}
QVariantList centerPoints;
QVariantList controlPoints;
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);
}
m_monitor->setUpEffectGeometry(QRect(), centerPoints, controlPoints);
m_monitor->setEffectKeyframe(isKeyframe);
if (seek)
emit seekToPos(pos - m_in);
......@@ -252,11 +258,7 @@ void RotoWidget::slotAddKeyframe(int pos)
if (pos < 0)
m_keyframeWidget->addKeyframe();
slotUpdateData(pos);
// TODO: port to qml monitor scene
//m_item->setEnabled(true);
//m_scene->setEnabled(true);
slotUpdateDataPoints(m_monitor->effectRoto(), pos);
}
void RotoWidget::slotRemoveKeyframe(int pos)
......
......@@ -87,9 +87,9 @@ private:
private slots:
/** @brief Updates/Creates the spline at @param pos based on the on-monitor items. */
void slotUpdateData(int pos = -1, bool editing = false);
void slotUpdateData(int pos, QList <BPoint> spline);
/** @brief Updates/Creates the spline at the current timeline position based on the on-monitor items. */
void slotUpdateData(bool editing);
void slotUpdateDataPoints(QVariantList points, int pos = -1);
/** @brief Updates the on-monitor items to fit the spline at position @param pos. */
void slotPositionChanged(int pos, bool seek = true);
......
Supports Markdown
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