X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=src%2Frotoscoping%2Frotowidget.cpp;h=e8d83ec3a8a42d2648c013adc2b91ad0cc714d6a;hb=c3302003093710ee247ad84c0fe2ef3c579d417f;hp=57268f6bdd2bfad35b8a1da5f37840f33824b130;hpb=df68f0f59c160e3b33d6aca6cddd5ecceafbeb1a;p=kdenlive diff --git a/src/rotoscoping/rotowidget.cpp b/src/rotoscoping/rotowidget.cpp index 57268f6b..e8d83ec3 100644 --- a/src/rotoscoping/rotowidget.cpp +++ b/src/rotoscoping/rotowidget.cpp @@ -26,6 +26,8 @@ #include "simplekeyframes/simplekeyframewidget.h" #include "kdenlivesettings.h" +#include + #include #include @@ -33,72 +35,50 @@ #include +/** @brief Listener for "tracking-finished" event in MLT rotoscoping filter. */ +void tracking_finished(mlt_service *owner, RotoWidget *self, char *data) +{ + Q_UNUSED(owner) + + if (self) + self->setSpline(QString(data)); +} -RotoWidget::RotoWidget(QString data, Monitor *monitor, int in, int out, Timecode t, QWidget* parent) : +RotoWidget::RotoWidget(const QString &data, Monitor *monitor, const ItemInfo &info, const Timecode &t, QWidget* parent) : QWidget(parent), m_monitor(monitor), m_showScene(true), - m_in(in), - m_out(out) + m_in(info.cropStart.frames(KdenliveSettings::project_fps())), + m_out((info.cropStart + info.cropDuration).frames(KdenliveSettings::project_fps()) - 1), + m_filter(NULL) { QVBoxLayout *l = new QVBoxLayout(this); m_keyframeWidget = new SimpleKeyframeWidget(t, m_out - m_in, this); l->addWidget(m_keyframeWidget); MonitorEditWidget *edit = monitor->getEffectEdit(); - edit->showVisibilityButton(true); m_scene = edit->getScene(); - - QJson::Parser parser; - bool ok; - m_data = parser.parse(data.toUtf8(), &ok); - if (!ok) { - // :( - } - - - if (m_data.canConvert(QVariant::Map)) { - /* - * pass keyframe data to keyframe timeline - */ - QList keyframes; - QMap map = m_data.toMap(); - QMap ::const_iterator i = map.constBegin(); - while (i != map.constEnd()) { - keyframes.append(i.key().toInt() - m_in); - ++i; - } - m_keyframeWidget->setKeyframes(keyframes); - - for (int j = 0; j < keyframes.count(); ++j) { - // key might already be justified - if (map.contains(QString::number(keyframes.at(j) + m_in))) { - QVariant value = map.take(QString::number(keyframes.at(j) + m_in)); - map[QString::number(keyframes.at(j) + m_in).rightJustified(log10((double)m_out) + 1, '0')] = value; - } - } - m_data = QVariant(map); - } else { - // static (only one keyframe) - m_keyframeWidget->setKeyframes(QList () << 0); - } + m_scene->cleanup(); m_item = new SplineItem(QList (), NULL, m_scene); connect(m_item, SIGNAL(changed(bool)), this, SLOT(slotUpdateData(bool))); - connect(edit, SIGNAL(showEdit(bool)), this, SLOT(slotShowScene(bool))); - connect(m_monitor, SIGNAL(renderPosition(int)), this, SLOT(slotCheckMonitorPosition(int))); 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())); - slotPositionChanged(0, false); + setSpline(data, false); + setupTrackingListen(info); + m_scene->centerView(); } RotoWidget::~RotoWidget() { + if (m_filter) + mlt_events_disconnect(m_filter->get_properties(), this); + delete m_keyframeWidget; m_scene->removeItem(m_item); @@ -106,18 +86,11 @@ RotoWidget::~RotoWidget() if (m_monitor) { MonitorEditWidget *edit = m_monitor->getEffectEdit(); - edit->showVisibilityButton(false); edit->removeCustomControls(); - m_monitor->slotEffectScene(false); + m_monitor->slotShowEffectScene(false); } } -void RotoWidget::slotCheckMonitorPosition(int renderPos) -{ - if (m_showScene) - emit checkMonitorPosition(renderPos); -} - void RotoWidget::slotSyncPosition(int relTimelinePos) { relTimelinePos = qBound(0, relTimelinePos, m_out); @@ -125,15 +98,6 @@ void RotoWidget::slotSyncPosition(int relTimelinePos) slotPositionChanged(relTimelinePos, false); } -void RotoWidget::slotShowScene(bool show) -{ - m_showScene = show; - if (!m_showScene) - m_monitor->slotEffectScene(false); - else - slotCheckMonitorPosition(m_monitor->render->seekFramePosition()); -} - void RotoWidget::slotUpdateData(int pos, bool editing) { Q_UNUSED(editing) @@ -160,7 +124,12 @@ void RotoWidget::slotUpdateData(int pos, bool editing) map[QString::number((pos < 0 ? m_keyframeWidget->getPosition() : pos) + m_in).rightJustified(log10((double)m_out) + 1, '0')] = QVariant(vlist); m_data = QVariant(map); } else { + // timeline update is only required if the first keyframe did not exist yet + bool update = m_data.isNull(); m_data = QVariant(vlist); + if (update) { + keyframeTimelineFullUpdate(); + } } emit valueChanged(); @@ -256,6 +225,11 @@ QList RotoWidget::getPoints(int keyframe) data = m_data.toMap()[QString::number(keyframe).rightJustified(log10((double)m_out) + 1, '0')].toList(); else data = m_data.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; @@ -321,6 +295,79 @@ void RotoWidget::updateTimecodeFormat() m_keyframeWidget->updateTimecodeFormat(); } +void RotoWidget::keyframeTimelineFullUpdate() +{ + if (m_data.canConvert(QVariant::Map)) { + QList keyframes; + QMap map = m_data.toMap(); + QMap ::const_iterator i = map.constBegin(); + while (i != map.constEnd()) { + keyframes.append(i.key().toInt() - m_in); + ++i; + } + m_keyframeWidget->setKeyframes(keyframes); + + /*for (int j = 0; j < keyframes.count(); ++j) { + // key might already be justified + if (map.contains(QString::number(keyframes.at(j) + m_in))) { + QVariant value = map.take(QString::number(keyframes.at(j) + m_in)); + map[QString::number(keyframes.at(j) + m_in).rightJustified(log10((double)m_out) + 1, '0')] = value; + } + } + m_data = QVariant(map);*/ + } else { + // static (only one keyframe) + // make sure the first keyframe was already created + if (m_data.isValid()) { + m_keyframeWidget->setKeyframes(QList () << 0); + } + } +} + +void RotoWidget::setupTrackingListen(const ItemInfo &info) +{ + if (info.startPos < GenTime()) { + // TODO: track effects + return; + } + + Mlt::Service service(m_monitor->render->getProducer()->parent().get_service()); + Mlt::Tractor tractor(service); + Mlt::Producer trackProducer(tractor.track(tractor.count() - info.track - 1)); + Mlt::Playlist trackPlaylist((mlt_playlist) trackProducer.get_service()); + + Mlt::Producer *clip = trackPlaylist.get_clip_at((int)info.startPos.frames(KdenliveSettings::project_fps())); + if (!clip) { + return; + } + + int i = 0; + Mlt::Filter *filter = clip->filter(0); + while (filter) { + if (strcmp(filter->get("kdenlive_id"), "rotoscoping") == 0) { + m_filter = filter; + filter->listen("tracking-finished", this, (mlt_listener)tracking_finished); + break; + } + filter = clip->filter(++i); + } + + delete clip; +} + +void RotoWidget::setSpline(const QString &spline, bool notify) +{ + QJson::Parser parser; + bool ok; + m_data = parser.parse(spline.simplified().toUtf8(), &ok); + if (!ok) { + // :( + } + keyframeTimelineFullUpdate(); + slotPositionChanged(m_keyframeWidget->getPosition(), false); + if (notify) + emit valueChanged(); +} static QVariant interpolate(int position, int in, int out, QVariant *splineIn, QVariant *splineOut) @@ -329,7 +376,12 @@ static QVariant interpolate(int position, int in, int out, QVariant *splineIn, Q QList keyframe1 = splineIn->toList(); QList keyframe2 = splineOut->toList(); QList keyframe; + if (keyframe1.count() && keyframe1.at(0).canConvert(QVariant::String)) + keyframe1.removeFirst(); + if (keyframe2.count() && keyframe2.at(0).canConvert(QVariant::String)) + keyframe2.removeFirst(); int max = qMin(keyframe1.count(), keyframe2.count()); + for (int i = 0; i < max; ++i) { QList p1 = keyframe1.at(i).toList(); QList p2 = keyframe2.at(i).toList(); @@ -344,10 +396,8 @@ static QVariant interpolate(int position, int in, int out, QVariant *splineIn, Q return QVariant(keyframe); } -bool adjustRotoDuration(QString* data, int in, int out, bool cut) +bool adjustRotoDuration(QString* data, int in, int out) { - Q_UNUSED(cut) - QJson::Parser parser; bool ok; QVariant splines = parser.parse(data->toUtf8(), &ok); @@ -359,24 +409,23 @@ bool adjustRotoDuration(QString* data, int in, int out, bool cut) if (!splines.canConvert(QVariant::Map)) return false; + QMap newMap; QMap map = splines.toMap(); QMap::iterator i = map.end(); int lastPos = -1; - QVariant last; + QVariant last = QVariant(); /* * Take care of resize from start */ bool startFound = false; while (i-- != map.begin()) { - if (i.key().toInt() < in) { - if (!startFound) { - startFound = true; - if (lastPos < 0) - map[QString::number(in).rightJustified(log10((double)out) + 1, '0')] = i.value(); - else - map[QString::number(in).rightJustified(log10((double)out) + 1, '0')] = interpolate(in, i.key().toInt(), lastPos, &i.value(), &last); - } + if (!startFound && i.key().toInt() < in) { + startFound = true; + if (lastPos < 0) + newMap[QString::number(in).rightJustified(log10((double)out) + 1, '0')] = i.value(); + else + newMap[QString::number(in).rightJustified(log10((double)out) + 1, '0')] = interpolate(in, i.key().toInt(), lastPos, &i.value(), &last); } lastPos = i.key().toInt(); last = i.value(); @@ -391,14 +440,12 @@ bool adjustRotoDuration(QString* data, int in, int out, bool cut) lastPos = -1; bool endFound = false; while (i != map.end()) { - if (i.key().toInt() > out) { - if (!endFound) { - endFound = true; - if (lastPos < 0) - map[QString::number(out)] = i.value(); - else - map[QString::number(out)] = interpolate(out, lastPos, i.key().toInt(), &last, &i.value()); - } + if (!endFound && i.key().toInt() > out) { + endFound = true; + if (lastPos < 0) + newMap[QString::number(out)] = i.value(); + else + newMap[QString::number(out)] = interpolate(out, lastPos, i.key().toInt(), &last, &i.value()); } lastPos = i.key().toInt(); last = i.value(); @@ -408,8 +455,17 @@ bool adjustRotoDuration(QString* data, int in, int out, bool cut) ++i; } + /* + * Update key lengths to prevent sorting issues + */ + i = map.begin(); + while (i != map.end()) { + newMap[i.key().rightJustified(log10((double)out) + 1, '0', true)] = i.value(); + ++i; + } + QJson::Serializer serializer; - *data = QString(serializer.serialize(QVariant(map))); + *data = QString(serializer.serialize(QVariant(newMap))); if (startFound || endFound) return true;